From 06a3eb1a98119030976d25205aa899c30df85a45 Mon Sep 17 00:00:00 2001 From: Alan Cox Date: Fri, 23 Nov 2007 15:22:09 -0500 Subject: [PATCH] Linux 2.2.18pre2 o Fix the compile problems with microcode.c (Dave Jones, Daniel Roesen) o GDTH driver update (Achim Leubner) o Fix mathsemu miuse of casting with asm (??) o Make msnd_pinnacle driver build on Alpha o Acenic 0.45 fixes (Chip Salzenberg) o Compaq CISS driver (SA 5300) (Charles White, + cleanups Alan Cox) + gcc 2.95 fixup o Modularise pm2fb and atyfb o Upgrade AMI Megaraid driver to 1.09 (AMI) o Add DEC HSG80 and COMPAQ 'logical volume' to scsi multilun list o SK PCI FDDI driver support (Schneider & Koch) o Linux 2.2 USB backport (Vojtech Pavlik) backport 3 + further fixes from the USB list + mm/slab.c fix for cache destroy o AGP driver backport (XFree86, Precision DRM driver backport Insight, XiG, HJ Lu, VA Linux, and others) --- Documentation/Configure.help | 62 + Documentation/cciss.txt | 47 + Documentation/mkdev.cciss | 40 + Documentation/networking/skfp.txt | 224 + {drivers => Documentation}/usb/CREDITS | 21 +- Documentation/usb/URB.txt | 196 + Documentation/usb/acm.txt | 138 + Documentation/usb/dc2xx.txt | 111 + Documentation/usb/error-codes.txt | 108 + Documentation/usb/ibmcam.txt | 288 ++ Documentation/usb/input.txt | 306 ++ .../usb/ohci.txt | 32 +- Documentation/usb/ov511.txt | 225 + Documentation/usb/proc_usb_info.txt | 256 ++ Documentation/usb/rio.txt | 138 + Documentation/usb/scanner-hp-sane.txt | 81 + Documentation/usb/scanner.txt | 362 ++ Documentation/usb/uhci.txt | 165 + Documentation/usb/usb-help.txt | 16 + Documentation/usb/usb-serial.txt | 164 + MAINTAINERS | 7 + Makefile | 7 +- arch/alpha/config.in | 2 +- arch/i386/config.in | 2 +- arch/i386/kernel/microcode.c | 46 +- arch/i386/math-emu/get_address.c | 11 +- arch/i386/mm/ioremap.c | 13 +- arch/sparc64/config.in | 13 +- drivers/block/Config.in | 4 + drivers/block/Makefile | 8 + drivers/block/cciss.c | 1934 +++++++++ drivers/block/cciss.h | 201 + drivers/block/cciss_cmd.h | 254 ++ drivers/block/genhd.c | 10 + drivers/block/ll_rw_blk.c | 14 +- drivers/char/Config.in | 27 +- drivers/char/Makefile | 32 +- drivers/char/agp/Makefile | 22 + drivers/char/agp/agp.h | 279 ++ drivers/char/agp/agp_backend.h | 231 + drivers/char/agp/agpgart.h | 224 + drivers/char/agp/agpgart_be.c | 2483 +++++++++++ drivers/char/agp/agpgart_fe.c | 1098 +++++ drivers/char/cpia.c | 2672 ++++++------ {include/linux => drivers/char}/cpia.h | 230 +- drivers/char/cpia_pp.c | 457 +- drivers/char/cpia_usb.c | 636 +++ drivers/char/drm/Config.in | 15 + drivers/char/drm/Makefile | 88 + drivers/char/drm/README.drm | 41 + drivers/char/drm/agpsupport.c | 329 ++ drivers/char/drm/auth.c | 160 + drivers/char/drm/bufs.c | 538 +++ drivers/char/drm/context.c | 308 ++ drivers/char/drm/ctxbitmap.c | 85 + drivers/char/drm/dma.c | 540 +++ drivers/char/drm/drawable.c | 50 + drivers/char/drm/drm.h | 367 ++ drivers/char/drm/drmP.h | 750 ++++ drivers/char/drm/drm_syms.c | 149 + drivers/char/drm/ffb_context.c | 530 +++ drivers/char/drm/ffb_drv.c | 868 ++++ drivers/char/drm/ffb_drv.h | 276 ++ drivers/char/drm/fops.c | 238 ++ drivers/char/drm/gamma_dma.c | 824 ++++ drivers/char/drm/gamma_drv.c | 575 +++ drivers/char/drm/gamma_drv.h | 58 + drivers/char/drm/i810_bufs.c | 334 ++ drivers/char/drm/i810_context.c | 203 + drivers/char/drm/i810_dma.c | 1417 +++++++ drivers/char/drm/i810_drm.h | 194 + drivers/char/drm/i810_drv.c | 653 +++ drivers/char/drm/i810_drv.h | 225 + drivers/char/drm/init.c | 113 + drivers/char/drm/ioctl.c | 91 + drivers/char/drm/lists.c | 245 ++ drivers/char/drm/lock.c | 225 + drivers/char/drm/memory.c | 449 ++ drivers/char/drm/mga_bufs.c | 639 +++ drivers/char/drm/mga_context.c | 203 + drivers/char/drm/mga_dma.c | 1115 +++++ drivers/char/drm/mga_drm.h | 269 ++ drivers/char/drm/mga_drv.c | 666 +++ drivers/char/drm/mga_drv.h | 445 ++ drivers/char/drm/mga_state.c | 1067 +++++ drivers/char/drm/proc.c | 577 +++ drivers/char/drm/r128_bufs.c | 309 ++ drivers/char/drm/r128_context.c | 207 + drivers/char/drm/r128_dma.c | 922 ++++ drivers/char/drm/r128_drm.h | 111 + drivers/char/drm/r128_drv.c | 731 ++++ drivers/char/drm/r128_drv.h | 224 + drivers/char/drm/tdfx_context.c | 209 + drivers/char/drm/tdfx_drv.c | 690 +++ drivers/char/drm/tdfx_drv.h | 67 + drivers/char/drm/vm.c | 319 ++ drivers/char/keyboard.c | 3 + drivers/char/mem.c | 20 +- drivers/char/pc_keyb.c | 11 +- drivers/net/Config.in | 3 +- drivers/net/Makefile | 12 +- drivers/net/Space.c | 4 + drivers/net/acenic.c | 21 +- drivers/net/sk98lin/skge.c | 1 + drivers/net/skfp/Makefile | 39 + drivers/net/skfp/can.c | 83 + drivers/net/skfp/cfm.c | 642 +++ drivers/net/skfp/drvfbi.c | 1612 +++++++ drivers/net/skfp/ecm.c | 547 +++ drivers/net/skfp/ess.c | 732 ++++ drivers/net/skfp/fplustm.c | 1645 ++++++++ drivers/net/skfp/h/cmtdef.h | 801 ++++ drivers/net/skfp/h/fddi.h | 69 + drivers/net/skfp/h/fddimib.h | 349 ++ drivers/net/skfp/h/fplustm.h | 278 ++ drivers/net/skfp/h/hwmtm.h | 424 ++ drivers/net/skfp/h/lnkstat.h | 84 + drivers/net/skfp/h/mbuf.h | 54 + drivers/net/skfp/h/osdef1st.h | 121 + drivers/net/skfp/h/sba.h | 142 + drivers/net/skfp/h/sba_def.h | 76 + drivers/net/skfp/h/skfbi.h | 1920 +++++++++ drivers/net/skfp/h/skfbiinc.h | 123 + drivers/net/skfp/h/smc.h | 471 +++ drivers/net/skfp/h/smt.h | 882 ++++ drivers/net/skfp/h/smt_p.h | 326 ++ drivers/net/skfp/h/smtstate.h | 100 + drivers/net/skfp/h/supern_2.h | 1059 +++++ drivers/net/skfp/h/targethw.h | 173 + drivers/net/skfp/h/targetos.h | 164 + drivers/net/skfp/h/types.h | 48 + drivers/net/skfp/hwmtm.c | 2241 ++++++++++ drivers/net/skfp/hwt.c | 314 ++ drivers/net/skfp/lnkstat.c | 209 + drivers/net/skfp/pcmplc.c | 2094 +++++++++ drivers/net/skfp/pmf.c | 1701 ++++++++ drivers/net/skfp/queue.c | 185 + drivers/net/skfp/rmt.c | 674 +++ drivers/net/skfp/skfddi.c | 2614 ++++++++++++ drivers/net/skfp/smt.c | 2225 ++++++++++ drivers/net/skfp/smtdef.c | 371 ++ drivers/net/skfp/smtinit.c | 126 + drivers/net/skfp/smtparse.c | 475 +++ drivers/net/skfp/smttimer.c | 173 + drivers/net/skfp/srf.c | 441 ++ drivers/scsi/gdth.c | 864 ++-- drivers/scsi/gdth.h | 96 +- drivers/scsi/gdth_ioctl.h | 10 +- drivers/scsi/gdth_proc.c | 274 +- drivers/scsi/gdth_proc.h | 17 +- drivers/scsi/megaraid.c | 677 ++- drivers/scsi/megaraid.h | 42 +- drivers/scsi/scsi.c | 2 + drivers/sound/msnd_pinnacle.c | 2 + drivers/usb/Config.in | 97 +- drivers/usb/Makefile | 174 +- drivers/usb/README.kbd | 65 - drivers/usb/README.ohci | 26 - drivers/usb/acm.c | 691 +++ drivers/usb/audio.c | 3743 ++++++++++++++++- drivers/usb/audio.h | 116 + drivers/usb/dabfirmware.h | 1408 +++++++ drivers/usb/dabusb.c | 846 ++++ drivers/usb/dabusb.h | 84 + drivers/usb/dc2xx.c | 475 +++ drivers/usb/devices.c | 526 +++ drivers/usb/devio.c | 1201 ++++++ drivers/usb/drivers.c | 119 + drivers/usb/dsbr100.c | 354 ++ drivers/usb/evdev.c | 349 ++ drivers/usb/hid-debug.h | 235 ++ drivers/usb/hid.c | 1526 +++++++ drivers/usb/hid.h | 330 ++ drivers/usb/hub.c | 764 +++- drivers/usb/hub.h | 58 +- drivers/usb/ibmcam.c | 3146 ++++++++++++++ drivers/usb/ibmcam.h | 240 ++ drivers/usb/inits.h | 6 - drivers/usb/inode.c | 712 ++++ drivers/usb/input.c | 408 ++ drivers/usb/joydev.c | 487 +++ drivers/usb/kaweth.c | 1030 +++++ drivers/usb/kawethfw.h | 639 +++ drivers/usb/keybdev.c | 198 + drivers/usb/keyboard.c | 226 - drivers/usb/keymap.c | 50 - drivers/usb/maps/fixup.map | 31 - drivers/usb/maps/serial.map | 370 -- drivers/usb/maps/usb.map | 233 - drivers/usb/mdc800.c | 935 ++++ drivers/usb/microtek.c | 1055 +++++ drivers/usb/microtek.h | 73 + drivers/usb/mkmap | 83 - drivers/usb/mouse.c | 293 -- drivers/usb/mousedev.c | 468 +++ drivers/usb/ohci-debug.c | 178 - drivers/usb/ohci-hcd.c | 1489 ------- drivers/usb/ohci-hcd.h | 404 -- drivers/usb/ohci-root-hub.c | 604 --- drivers/usb/ohci-root-hub.h | 71 - drivers/usb/ohci.c | 1669 -------- drivers/usb/ohci.h | 349 -- drivers/usb/ov511.c | 2757 ++++++++++++ drivers/usb/ov511.h | 354 ++ drivers/usb/pegasus.c | 564 +++ drivers/usb/plusb.c | 621 +++ drivers/usb/plusb.h | 48 + drivers/usb/printer.c | 526 +++ drivers/usb/restart | 38 - drivers/usb/rio500.c | 447 ++ drivers/usb/rio500_usb.h | 37 + drivers/usb/scanner.c | 876 ++++ drivers/usb/scanner.h | 166 + drivers/usb/serial/Makefile | 29 + drivers/usb/serial/Makefile-keyspan_pda_fw | 16 + drivers/usb/serial/digi_acceleport.c | 1710 ++++++++ drivers/usb/serial/ezusb_convert.pl | 50 + drivers/usb/serial/ftdi_sio.c | 723 ++++ drivers/usb/serial/ftdi_sio.h | 413 ++ drivers/usb/serial/keyspan_pda.S | 1124 +++++ drivers/usb/serial/keyspan_pda.c | 700 +++ drivers/usb/serial/keyspan_pda_fw.h | 93 + drivers/usb/serial/omninet.c | 337 ++ drivers/usb/serial/usb-serial.h | 183 + drivers/usb/serial/usbserial.c | 1285 ++++++ drivers/usb/serial/visor.c | 208 + drivers/usb/serial/visor.h | 74 + drivers/usb/serial/whiteheat.c | 408 ++ drivers/usb/serial/whiteheat.h | 169 + drivers/usb/serial/whiteheat_fw.h | 1556 +++++++ drivers/usb/stopusb | 7 - drivers/usb/uhci-debug.c | 168 - drivers/usb/uhci-debug.h | 316 ++ drivers/usb/uhci.c | 2786 ++++++++---- drivers/usb/uhci.h | 375 +- drivers/usb/usb-core.c | 179 + drivers/usb/usb-debug.c | 158 +- drivers/usb/usb-ohci.c | 2137 ++++++++++ drivers/usb/usb-ohci.h | 450 ++ drivers/usb/usb-storage-debug.h | 89 + drivers/usb/usb-storage.c | 2200 ++++++++++ drivers/usb/usb-storage.h | 159 + drivers/usb/usb-uhci-debug.h | 182 + drivers/usb/usb-uhci.c | 2886 +++++++++++++ drivers/usb/usb-uhci.h | 282 ++ drivers/usb/usb.c | 1985 +++++++-- drivers/usb/usb.h | 376 -- drivers/usb/usbkbd.c | 262 ++ drivers/usb/usbmouse.c | 187 + drivers/usb/uss720.c | 663 +++ drivers/usb/wacom.c | 333 ++ drivers/usb/wmforce.c | 190 + drivers/video/Config.in | 6 +- drivers/video/atyfb.c | 64 +- drivers/video/pm2fb.c | 10 +- include/linux/agp_backend.h | 228 + include/linux/agpgart.h | 224 + include/linux/cciss_ioctl.h | 186 + include/linux/devfs_fs_kernel.h | 7 + include/linux/fs.h | 4 + include/linux/input.h | 532 +++ include/linux/joystick.h | 8 + include/linux/kbd_kern.h | 2 + include/linux/kcomp.h | 96 + include/linux/major.h | 9 + include/linux/pci.h | 2 + include/linux/pm.h | 88 + include/linux/sched.h | 2 + include/linux/slab.h | 1 + include/linux/spinlock.h | 4 + include/linux/usb.h | 786 ++++ include/linux/usbdev_fs_i.h | 11 + include/linux/usbdev_fs_sb.h | 10 + include/linux/usbdevice_fs.h | 203 + init/main.c | 11 + kernel/ksyms.c | 2 + kernel/sched.c | 28 + mm/slab.c | 110 +- 278 files changed, 115202 insertions(+), 10419 deletions(-) create mode 100644 Documentation/cciss.txt create mode 100644 Documentation/mkdev.cciss create mode 100644 Documentation/networking/skfp.txt rename {drivers => Documentation}/usb/CREDITS (90%) create mode 100644 Documentation/usb/URB.txt create mode 100644 Documentation/usb/acm.txt create mode 100644 Documentation/usb/dc2xx.txt create mode 100644 Documentation/usb/error-codes.txt create mode 100644 Documentation/usb/ibmcam.txt create mode 100644 Documentation/usb/input.txt rename drivers/usb/README.ohci_hcd => Documentation/usb/ohci.txt (82%) create mode 100644 Documentation/usb/ov511.txt create mode 100644 Documentation/usb/proc_usb_info.txt create mode 100644 Documentation/usb/rio.txt create mode 100644 Documentation/usb/scanner-hp-sane.txt create mode 100644 Documentation/usb/scanner.txt create mode 100644 Documentation/usb/uhci.txt create mode 100644 Documentation/usb/usb-help.txt create mode 100644 Documentation/usb/usb-serial.txt create mode 100644 drivers/block/cciss.c create mode 100644 drivers/block/cciss.h create mode 100644 drivers/block/cciss_cmd.h create mode 100644 drivers/char/agp/Makefile create mode 100644 drivers/char/agp/agp.h create mode 100644 drivers/char/agp/agp_backend.h create mode 100644 drivers/char/agp/agpgart.h create mode 100644 drivers/char/agp/agpgart_be.c create mode 100644 drivers/char/agp/agpgart_fe.c rename {include/linux => drivers/char}/cpia.h (56%) create mode 100644 drivers/char/cpia_usb.c create mode 100644 drivers/char/drm/Config.in create mode 100644 drivers/char/drm/Makefile create mode 100644 drivers/char/drm/README.drm create mode 100644 drivers/char/drm/agpsupport.c create mode 100644 drivers/char/drm/auth.c create mode 100644 drivers/char/drm/bufs.c create mode 100644 drivers/char/drm/context.c create mode 100644 drivers/char/drm/ctxbitmap.c create mode 100644 drivers/char/drm/dma.c create mode 100644 drivers/char/drm/drawable.c create mode 100644 drivers/char/drm/drm.h create mode 100644 drivers/char/drm/drmP.h create mode 100644 drivers/char/drm/drm_syms.c create mode 100644 drivers/char/drm/ffb_context.c create mode 100644 drivers/char/drm/ffb_drv.c create mode 100644 drivers/char/drm/ffb_drv.h create mode 100644 drivers/char/drm/fops.c create mode 100644 drivers/char/drm/gamma_dma.c create mode 100644 drivers/char/drm/gamma_drv.c create mode 100644 drivers/char/drm/gamma_drv.h create mode 100644 drivers/char/drm/i810_bufs.c create mode 100644 drivers/char/drm/i810_context.c create mode 100644 drivers/char/drm/i810_dma.c create mode 100644 drivers/char/drm/i810_drm.h create mode 100644 drivers/char/drm/i810_drv.c create mode 100644 drivers/char/drm/i810_drv.h create mode 100644 drivers/char/drm/init.c create mode 100644 drivers/char/drm/ioctl.c create mode 100644 drivers/char/drm/lists.c create mode 100644 drivers/char/drm/lock.c create mode 100644 drivers/char/drm/memory.c create mode 100644 drivers/char/drm/mga_bufs.c create mode 100644 drivers/char/drm/mga_context.c create mode 100644 drivers/char/drm/mga_dma.c create mode 100644 drivers/char/drm/mga_drm.h create mode 100644 drivers/char/drm/mga_drv.c create mode 100644 drivers/char/drm/mga_drv.h create mode 100644 drivers/char/drm/mga_state.c create mode 100644 drivers/char/drm/proc.c create mode 100644 drivers/char/drm/r128_bufs.c create mode 100644 drivers/char/drm/r128_context.c create mode 100644 drivers/char/drm/r128_dma.c create mode 100644 drivers/char/drm/r128_drm.h create mode 100644 drivers/char/drm/r128_drv.c create mode 100644 drivers/char/drm/r128_drv.h create mode 100644 drivers/char/drm/tdfx_context.c create mode 100644 drivers/char/drm/tdfx_drv.c create mode 100644 drivers/char/drm/tdfx_drv.h create mode 100644 drivers/char/drm/vm.c create mode 100644 drivers/net/skfp/Makefile create mode 100644 drivers/net/skfp/can.c create mode 100644 drivers/net/skfp/cfm.c create mode 100644 drivers/net/skfp/drvfbi.c create mode 100644 drivers/net/skfp/ecm.c create mode 100644 drivers/net/skfp/ess.c create mode 100644 drivers/net/skfp/fplustm.c create mode 100644 drivers/net/skfp/h/cmtdef.h create mode 100644 drivers/net/skfp/h/fddi.h create mode 100644 drivers/net/skfp/h/fddimib.h create mode 100644 drivers/net/skfp/h/fplustm.h create mode 100644 drivers/net/skfp/h/hwmtm.h create mode 100644 drivers/net/skfp/h/lnkstat.h create mode 100644 drivers/net/skfp/h/mbuf.h create mode 100644 drivers/net/skfp/h/osdef1st.h create mode 100644 drivers/net/skfp/h/sba.h create mode 100644 drivers/net/skfp/h/sba_def.h create mode 100644 drivers/net/skfp/h/skfbi.h create mode 100644 drivers/net/skfp/h/skfbiinc.h create mode 100644 drivers/net/skfp/h/smc.h create mode 100644 drivers/net/skfp/h/smt.h create mode 100644 drivers/net/skfp/h/smt_p.h create mode 100644 drivers/net/skfp/h/smtstate.h create mode 100644 drivers/net/skfp/h/supern_2.h create mode 100644 drivers/net/skfp/h/targethw.h create mode 100644 drivers/net/skfp/h/targetos.h create mode 100644 drivers/net/skfp/h/types.h create mode 100644 drivers/net/skfp/hwmtm.c create mode 100644 drivers/net/skfp/hwt.c create mode 100644 drivers/net/skfp/lnkstat.c create mode 100644 drivers/net/skfp/pcmplc.c create mode 100644 drivers/net/skfp/pmf.c create mode 100644 drivers/net/skfp/queue.c create mode 100644 drivers/net/skfp/rmt.c create mode 100644 drivers/net/skfp/skfddi.c create mode 100644 drivers/net/skfp/smt.c create mode 100644 drivers/net/skfp/smtdef.c create mode 100644 drivers/net/skfp/smtinit.c create mode 100644 drivers/net/skfp/smtparse.c create mode 100644 drivers/net/skfp/smttimer.c create mode 100644 drivers/net/skfp/srf.c delete mode 100644 drivers/usb/README.kbd delete mode 100644 drivers/usb/README.ohci create mode 100644 drivers/usb/acm.c create mode 100644 drivers/usb/audio.h create mode 100644 drivers/usb/dabfirmware.h create mode 100644 drivers/usb/dabusb.c create mode 100644 drivers/usb/dabusb.h create mode 100644 drivers/usb/dc2xx.c create mode 100644 drivers/usb/devices.c create mode 100644 drivers/usb/devio.c create mode 100644 drivers/usb/drivers.c create mode 100644 drivers/usb/dsbr100.c create mode 100644 drivers/usb/evdev.c create mode 100644 drivers/usb/hid-debug.h create mode 100644 drivers/usb/hid.c create mode 100644 drivers/usb/hid.h create mode 100644 drivers/usb/ibmcam.c create mode 100644 drivers/usb/ibmcam.h delete mode 100644 drivers/usb/inits.h create mode 100644 drivers/usb/inode.c create mode 100644 drivers/usb/input.c create mode 100644 drivers/usb/joydev.c create mode 100644 drivers/usb/kaweth.c create mode 100644 drivers/usb/kawethfw.h create mode 100644 drivers/usb/keybdev.c delete mode 100644 drivers/usb/keyboard.c delete mode 100644 drivers/usb/keymap.c delete mode 100644 drivers/usb/maps/fixup.map delete mode 100644 drivers/usb/maps/serial.map delete mode 100644 drivers/usb/maps/usb.map create mode 100644 drivers/usb/mdc800.c create mode 100644 drivers/usb/microtek.c create mode 100644 drivers/usb/microtek.h delete mode 100644 drivers/usb/mkmap delete mode 100644 drivers/usb/mouse.c create mode 100644 drivers/usb/mousedev.c delete mode 100644 drivers/usb/ohci-debug.c delete mode 100644 drivers/usb/ohci-hcd.c delete mode 100644 drivers/usb/ohci-hcd.h delete mode 100644 drivers/usb/ohci-root-hub.c delete mode 100644 drivers/usb/ohci-root-hub.h delete mode 100644 drivers/usb/ohci.c delete mode 100644 drivers/usb/ohci.h create mode 100644 drivers/usb/ov511.c create mode 100644 drivers/usb/ov511.h create mode 100644 drivers/usb/pegasus.c create mode 100644 drivers/usb/plusb.c create mode 100644 drivers/usb/plusb.h create mode 100644 drivers/usb/printer.c delete mode 100644 drivers/usb/restart create mode 100644 drivers/usb/rio500.c create mode 100644 drivers/usb/rio500_usb.h create mode 100644 drivers/usb/scanner.c create mode 100644 drivers/usb/scanner.h create mode 100644 drivers/usb/serial/Makefile create mode 100644 drivers/usb/serial/Makefile-keyspan_pda_fw create mode 100644 drivers/usb/serial/digi_acceleport.c create mode 100644 drivers/usb/serial/ezusb_convert.pl create mode 100644 drivers/usb/serial/ftdi_sio.c create mode 100644 drivers/usb/serial/ftdi_sio.h create mode 100644 drivers/usb/serial/keyspan_pda.S create mode 100644 drivers/usb/serial/keyspan_pda.c create mode 100644 drivers/usb/serial/keyspan_pda_fw.h create mode 100644 drivers/usb/serial/omninet.c create mode 100644 drivers/usb/serial/usb-serial.h create mode 100644 drivers/usb/serial/usbserial.c create mode 100644 drivers/usb/serial/visor.c create mode 100644 drivers/usb/serial/visor.h create mode 100644 drivers/usb/serial/whiteheat.c create mode 100644 drivers/usb/serial/whiteheat.h create mode 100644 drivers/usb/serial/whiteheat_fw.h delete mode 100644 drivers/usb/stopusb delete mode 100644 drivers/usb/uhci-debug.c create mode 100644 drivers/usb/uhci-debug.h create mode 100644 drivers/usb/usb-core.c create mode 100644 drivers/usb/usb-ohci.c create mode 100644 drivers/usb/usb-ohci.h create mode 100644 drivers/usb/usb-storage-debug.h create mode 100644 drivers/usb/usb-storage.c create mode 100644 drivers/usb/usb-storage.h create mode 100644 drivers/usb/usb-uhci-debug.h create mode 100644 drivers/usb/usb-uhci.c create mode 100644 drivers/usb/usb-uhci.h delete mode 100644 drivers/usb/usb.h create mode 100644 drivers/usb/usbkbd.c create mode 100644 drivers/usb/usbmouse.c create mode 100644 drivers/usb/uss720.c create mode 100644 drivers/usb/wacom.c create mode 100644 drivers/usb/wmforce.c create mode 100644 include/linux/agp_backend.h create mode 100644 include/linux/agpgart.h create mode 100644 include/linux/cciss_ioctl.h create mode 100644 include/linux/devfs_fs_kernel.h create mode 100644 include/linux/input.h create mode 100644 include/linux/kcomp.h create mode 100644 include/linux/pm.h create mode 100644 include/linux/spinlock.h create mode 100644 include/linux/usb.h create mode 100644 include/linux/usbdev_fs_i.h create mode 100644 include/linux/usbdev_fs_sb.h create mode 100644 include/linux/usbdevice_fs.h diff --git a/Documentation/Configure.help b/Documentation/Configure.help index 3ef2ff63375f..fe6b95c1eb29 100644 --- a/Documentation/Configure.help +++ b/Documentation/Configure.help @@ -6942,6 +6942,35 @@ CONFIG_DEFXX This is support for the DIGITAL series of EISA (DEFEA) and PCI (DEFPA) controllers which can connect you to a local FDDI network. +SysKonnect FDDI PCI support +CONFIG_SKFP + Say Y here if you have a SysKonnect FDDI PCI adapter. + The following adapters are supported by this driver: + - SK-5521 (SK-NET FDDI-UP) + - SK-5522 (SK-NET FDDI-UP DAS) + - SK-5541 (SK-NET FDDI-FP) + - SK-5543 (SK-NET FDDI-LP) + - SK-5544 (SK-NET FDDI-LP DAS) + - SK-5821 (SK-NET FDDI-UP64) + - SK-5822 (SK-NET FDDI-UP64 DAS) + - SK-5841 (SK-NET FDDI-FP64) + - SK-5843 (SK-NET FDDI-LP64) + - SK-5844 (SK-NET FDDI-LP64 DAS) + - Netelligent 100 FDDI DAS Fibre SC + - Netelligent 100 FDDI SAS Fibre SC + - Netelligent 100 FDDI DAS UTP + - Netelligent 100 FDDI SAS UTP + - Netelligent 100 FDDI SAS Fibre MIC + Read Documentation/networking/skfp.txt for information about + the driver. + Questions concerning this driver can be addressed to: + linux@syskonnect.de + + If you want to compile this driver as a module ( = code which can be + inserted in and removed from the running kernel whenever you want), + say M here and read Documentation/modules.txt. This is recommended. + The module will be called skfp.o. + HIgh Performance Parallel Interface support (EXPERIMENTAL) CONFIG_HIPPI HIgh Performance Parallel Interface (HIPPI) is a 800Mbit/sec and @@ -9549,6 +9578,20 @@ CONFIG_RTC sampling), then say Y here, and read Documentation/rtc.txt for details. +AGP/GART support +CONFIG_AGP + This provides a kernel interface (/dev/agpgart) for programming AGP + transfers on motherboards that support them. Primarily, this is used + for hardware-accelerated 3d graphics, though any other AGP device + could take advantage of it. + + If you have a 3d-capable AGP video card say 'M' or 'Y' here. + Otherwise, say 'N'. + + You will also have to indicate support for your specific chipset. + Consult the output of lspci, your motherboard manual, or the inside + of your computer if unsure what to choose. Multiple selections are ok. + Tadpole ANA H8 Support CONFIG_H8 The Hitachi H8/337 is a microcontroller used to deal with the power @@ -12362,6 +12405,14 @@ CONFIG_BLK_CPQ_DA boards supported by this driver, and for further information on the use of this driver. +Compaq Smart Array support +CONFIG_BLK_CPQ_CISS_DA + This is the driver for Compaq Smart Array controllers. + Everyone using these boards should say Y here. + See "linux/Documentation/cciss.txt" for the current list of + boards supported by this driver, and for further information + on the use of this driver. + QuickNet Internet LineJack/PhoneJack support CONFIG_PHONE_IXJ Say M if you have a telephony card manufactured by Quicknet @@ -12380,6 +12431,17 @@ CONFIG_PHONE_IXJ If you do not have any Quicknet telephony cards, you can safely ignore this option. +Kawasaki USB ethernet controller support +CONFIG_USB_KAWETH + Say Y here if you want support for devices based on the Kawasaki + LSI KL5KUSB100 USB to Ethernet 1-Chip Controller, such as the + NetGear EA101 USB to Ethernet Adapter. + + This code is also available as a module ( = code which can be + inserted in and removed from the running kernel whenever you want). + The module will be called kaweth.o. If you want to compile it as a + module, say M here and read Documentation/modules.txt. + Toshiba Laptop support CONFIG_TOSHIBA If you intend to run this the kernel on a Toshiba portable say yes diff --git a/Documentation/cciss.txt b/Documentation/cciss.txt new file mode 100644 index 000000000000..56d4d7a6ae92 --- /dev/null +++ b/Documentation/cciss.txt @@ -0,0 +1,47 @@ +This driver is for Compaq's SMART Array Controllers. + +Supported Cards: +---------------- + +This driver is known to work with the following cards: + + * SA 5300 + +If notes are not already created in the /dev/cciss directory + +# mkdev.cciss [ctlrs] + +Where ctlrs is the number of controllers you have (defaults to 1 if not +specified). + +Device Naming: +-------------- + +You need some entries in /dev for the cciss device. The mkdev.cciss script +can make device nodes for you automatically. Currently the device setup +is as follows: + +Major numbers: + 104 cciss0 + 105 cciss1 + 106 cciss2 + etc... + +Minor numbers: + b7 b6 b5 b4 b3 b2 b1 b0 + |----+----| |----+----| + | | + | +-------- Partition ID (0=wholedev, 1-15 partition) + | + +-------------------- Logical Volume number + +The suggested device naming scheme is: +/dev/cciss/c0d0 Controller 0, disk 0, whole device +/dev/cciss/c0d0p1 Controller 0, disk 0, partition 1 +/dev/cciss/c0d0p2 Controller 0, disk 0, partition 2 +/dev/cciss/c0d0p3 Controller 0, disk 0, partition 3 + +/dev/cciss/c1d1 Controller 1, disk 1, whole device +/dev/cciss/c1d1p1 Controller 1, disk 1, partition 1 +/dev/cciss/c1d1p2 Controller 1, disk 1, partition 2 +/dev/cciss/c1d1p3 Controller 1, disk 1, partition 3 diff --git a/Documentation/mkdev.cciss b/Documentation/mkdev.cciss new file mode 100644 index 000000000000..fbbaf30a7175 --- /dev/null +++ b/Documentation/mkdev.cciss @@ -0,0 +1,40 @@ +#!/bin/sh +# Script to create device nodes for SMART array controllers +# Usage: +# mkdev.cciss [num controllers] [num log volumes] [num partitions] +# +# With no arguments, the script assumes 1 controller, 16 logical volumes, +# and 16 partitions/volume, which is adequate for most configurations. +# +# If you had 5 controllers and were planning on no more than 4 logical volumes +# each, using a maximum of 8 partitions per volume, you could say: +# +# mkdev.cciss 5 4 8 +# +# Of course, this has no real benefit over "mkdev.cciss 5" except that it +# doesn't create so many device nodes in /dev/cciss. + +NR_CTLR=${1-1} +NR_VOL=${2-16} +NR_PART=${3-16} + +if [ ! -d /dev/cciss ]; then + mkdir -p /dev/cciss +fi + +C=0; while [ $C -lt $NR_CTLR ]; do + MAJ=`expr $C + 104` + D=0; while [ $D -lt $NR_VOL ]; do + P=0; while [ $P -lt $NR_PART ]; do + MIN=`expr $D \* 16 + $P` + if [ $P -eq 0 ]; then + mknod /dev/cciss/c${C}d${D} b $MAJ $MIN + else + mknod /dev/cciss/c${C}d${D}p${P} b $MAJ $MIN + fi + P=`expr $P + 1` + done + D=`expr $D + 1` + done + C=`expr $C + 1` +done diff --git a/Documentation/networking/skfp.txt b/Documentation/networking/skfp.txt new file mode 100644 index 000000000000..2fe9c522205e --- /dev/null +++ b/Documentation/networking/skfp.txt @@ -0,0 +1,224 @@ +(C)Copyright 1998-2000 SysKonnect, +=========================================================================== + +skfp.txt created 17-Jul-2000 + +Readme File for skfp.o v2.07 + + +This file contains +(1) OVERVIEW +(2) SUPPORTED ADAPTERS +(3) GENERAL INFORMATION +(4) INSTALLATION +(5) INCLUSION OF THE ADAPTER IN SYSTEM START +(6) TROUBLESHOOTING +(7) FUNCTION OF THE ADAPTER LEDS +(8) HISTORY + +=========================================================================== + + + +(1) OVERVIEW +============ + +This README explains how to use the driver 'skfp' for Linux with your +network adapter. + +Chapter 2: Contains a list of all network adapters that are supported by + this driver. + +Chapter 3: Gives some general information. + +Chapter 4: Describes common problems and solutions. + +Chapter 5: Shows the changed functionality of the adapter LEDs. + +Chapter 6: History of development. + +*** + + +(2) SUPPORTED ADAPTERS +====================== + +The network driver 'skfp' supports the following network adapters: +SysKonnect adapters: + - SK-5521 (SK-NET FDDI-UP) + - SK-5522 (SK-NET FDDI-UP DAS) + - SK-5541 (SK-NET FDDI-FP) + - SK-5543 (SK-NET FDDI-LP) + - SK-5544 (SK-NET FDDI-LP DAS) + - SK-5821 (SK-NET FDDI-UP64) + - SK-5822 (SK-NET FDDI-UP64 DAS) + - SK-5841 (SK-NET FDDI-FP64) + - SK-5843 (SK-NET FDDI-LP64) + - SK-5844 (SK-NET FDDI-LP64 DAS) +Compaq adapters (not tested): + - Netelligent 100 FDDI DAS Fibre SC + - Netelligent 100 FDDI SAS Fibre SC + - Netelligent 100 FDDI DAS UTP + - Netelligent 100 FDDI SAS UTP + - Netelligent 100 FDDI SAS Fibre MIC +*** + + +(3) GENERAL INFORMATION +======================= + +From v2.01 on, the driver is integrated in the linux kernel sources. +Therefor, the installation is the same as for any other adapter +supported by the kernel. +Refer to the manual of your distribution about the installation +of network adapters. +Makes my life much easier :-) +*** + + +(4) TROUBLESHOOTING +=================== + +If you run into problems during installation, check those items: + +Problem: The FDDI adapter can not be found by the driver. +Reason: Look in /proc/pci for the following entry: + 'FDDI network controller: SysKonnect SK-FDDI-PCI ...' + If this entry exists, then the FDDI adapter has been + found by the system and should be able to be used. + If this entry does not exist or if the file '/proc/pci' + is not there, then you may have a hardware problem or PCI + support may not be enabled in your kernel. + The adapter can be checked using the diagnostic program + which is available from the SysKonnect web site: + www.syskonnect.de + Some COMPAQ machines have a problem with PCI under + Linux. This is described in the 'PCI howto' document + (included in some distributions or available from the + www, e.g. at 'www.linux.org') and no workaround is available. + +Problem: You want to use your computer as a router between + multiple IP subnetworks (using multiple adapters), but + you can not reach computers in other subnetworks. +Reason: Either the router's kernel is not configured for IP + forwarding or there is a problem with the routing table + and gateway configuration in at least one of the + computers. + +If your problem is not listed here, please contact our +technical support for help. +You can send email to: + linux@syskonnect.de +When contacting our technical support, +please ensure that the following information is available: +- System Manufacturer and Model +- Boards in your system +- Distribution +- Kernel version + +*** + + +(5) FUNCTION OF THE ADAPTER LEDS +================================ + + The functionality of the LED's on the FDDI network adapters was + changed in SMT version v2.82. With this new SMT version, the yellow + LED works as a ring operational indicator. An active yellow LED + indicates that the ring is down. The green LED on the adapter now + works as a link indicator where an active GREEN LED indicates that + the respective port has a physical connection. + + With versions of SMT prior to v2.82 a ring up was indicated if the + yellow LED was off while the green LED(s) showed the connection + status of the adapter. During a ring down the green LED was off and + the yellow LED was on. + + All implementations indicate that a driver is not loaded if + all LEDs are off. + +*** + + +(6) HISTORY +=========== + +v2.07 (20000717) (In-Kernel version) + New features: + - in kernel 2.2.17 (hopefully !) + +v2.06 (20000511) (In-Kernel version) + New features: + - 64 bit support + - new pci dma interface + - in kernel 2.3.99 + +v2.05 (20000217) (In-Kernel version) + New features: + - Changes for 2.3.45 kernel + +v2.04 (20000207) (Standalone version) + New features: + - Added rx/tx byte counter + +v2.03 (20000111) (Standalone version) + Problems fixed: + - Fixed printk statements from v2.02 + +v2.02 (991215) (Standalone version) + Problems fixed: + - Removed unnecessary output + - Fixed path for "printver.sh" in makefile + +v2.01 (991122) + New features: + - Integration in Linux kernel sources + - Support for memory mapped I/O + +v2.00 (991112) + New features: + - Full source released under GPL + +v1.05 (991023) + Problems fixed: + - Compilation with kernel version 2.2.13 failed + +v1.04 (990427) + Changes: + - New SMT module included, changing LED functionality + Problems fixed: + - Synchronization on SMP machines was buggy + +v1.03 (990325) + Problems fixed: + - Interrupt routing on SMP machines could be incorrect + +v1.02 (990310) + New features: + - Support for kernel versions 2.2.x added + - Kernel patch instead of private duplicate of kernel functions + +v1.01 (980812) + Problems fixed: + Connection hangup with telnet + Slow telnet connection + +v1.00 beta 01 (980507) + New features: + None. + Problems fixed: + None. + Known limitations: + - tar archive instead of standard package format (rpm). + - FDDI statistic is empty. + - not tested with 2.1.xx kernels + - integration in kernel not tested + - not tested simultaneously with FDDI adapters from other vendors. + - only X86 processors supported. + - SBA (Synchronous Bandwidth Allocator) parameters can + not be configured. + - does not work on some COMPAQ machines. See the PCI howto + document for details about this problem. + - data corruption with kernel versions below 2.0.33. + +*** End of information file *** diff --git a/drivers/usb/CREDITS b/Documentation/usb/CREDITS similarity index 90% rename from drivers/usb/CREDITS rename to Documentation/usb/CREDITS index 6cd211b2270c..38bdbaee0d95 100644 --- a/drivers/usb/CREDITS +++ b/Documentation/usb/CREDITS @@ -4,11 +4,20 @@ The following people have contributed to this code (in alphabetical order by last name). I'm sure this list should be longer, its difficult to maintain, add yourself with a patch if desired. + Georg Acher Alan Cox + Randy Dunlap Johannes Erdfelt + Deti Fliegl ham Bradley M Keryan - Vojtech Pavlik + Greg Kroah-Hartman + Pavel Machek + Paul Mackerras + David E. Nelson + Vojtech Pavlik + Bill Ryder + Thomas Sailer Gregory P. Smith Linus Torvalds Roman Weissgaerber @@ -28,7 +37,11 @@ of hardware, support, time and development (this is from the original THANKS file in Inaky's driver): The following corporations have helped us in the development -of Linux USB / UUSBD: + of Linux USB / UUSBD: + + - 3Com GmbH for donating a ISDN Pro TA and supporting me + in technical questions and with test equipment. I'd never + expect such a great help. - USAR Systems provided us with one of their excellent USB Evaluation Kits. It allows us to test the Linux-USB driver @@ -95,6 +108,9 @@ of Linux USB / UUSBD: - Thanks to Entrega for providing PCI to USB cards, hubs and converter products for development. + - Thanks to ConnectTech for providing a WhiteHEAT usb to + serial converter, and the documentation for the device to + allow a driver to be written. And thanks go to (hey! in no particular order :) @@ -151,4 +167,3 @@ of Linux USB / UUSBD: for convincing me USB Standard hubs are not that standard and that's good to allow for vendor specific quirks on the standard hub driver. - diff --git a/Documentation/usb/URB.txt b/Documentation/usb/URB.txt new file mode 100644 index 000000000000..67cb9e31f62f --- /dev/null +++ b/Documentation/usb/URB.txt @@ -0,0 +1,196 @@ +1. Specification of the API + +1.1. Basic concept or 'What is an URB?' + +The basic idea of the new driver is message passing, the message itself is +called USB Request Block, or URB for short. + +- An URB consists of all relevant information to execute any USB transaction +and deliver the data and status back. + +- Execution of an URB is an inherently asynchronous operation, i.e. the +submit_urb(urb) call returns immediately after it has successfully queued +the requested action. + +- Ongoing transfers for one URB (e.g. ISO) can simply be canceled with +unlink_urb(urb) at any time. + +- Each URB has a completion handler, which is called after the action +has been successfully completed or canceled (INT transfers behave a bit +different, see below). The URB also contains a context-pointer for free +usage and information passing to the completion handler. + +- URBs can be linked. After completing one URB, the next one can be +automatically submitted. This is especially useful for ISO transfers: +You only have read/write the data from/to the buffers in the completion +handler, the continous streaming itself is transparently done by the +URB-machinery. + +1.2. The URB structure + +typedef struct urb +{ +// ignore, for host controller/URB machine internal use + void *hcpriv; // private data for host controller + struct list_head urb_list; // list pointer to all active urbs + +// This is used for urb linking + struct urb* next; // pointer to next URB + struct usb_device *dev; // pointer to associated USB device + +// pipe is assembled by the various well known pipe-macros in usb.h + unsigned int pipe; // pipe information + +// status after each completion + int status; // returned status + unsigned int transfer_flags; // ASAP, SP_OK, EARLY_COMPLETE + +// for data stage (CTRL), BULK, INT and ISO + void *transfer_buffer; // associated data buffer + +// expected length + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + +// setup stage for CTRL (always 8 bytes!) + unsigned char* setup_packet; // setup packet (control only) + +// with ASAP, start_frame is set to the determined frame + int start_frame; // start frame (iso/irq) + int number_of_packets; // # of packets (iso/int) + int interval; // polling interval (irq only) + int error_count; // number of errors (iso only) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // +// specification of the requested data offsets and length for ISO + iso_packet_descriptor_t iso_frame_desc[0]; +} urb_t, *purb_t; + +1.3. How to get an URB? + +URBs are allocated with the following call + + purb_t alloc_urb(int isoframes) + +Return value is a pointer to the allocated URB, 0 if allocation failed. +The parameter isoframes specifies the number of isochronous transfer frames +you want to schedule. For CTRL/BULK/INT, use 0. + +To free an URB, use + + void free_urb(purb_t purb) + +This call also may free internal (host controller specific) memory in the +future. + +1.4. What has to be filled in? + +Depending on the type of transaction, there are some macros +(FILL_CONTROL_URB, FILL_BULK_URB, and FILL_INT_URB, defined in uhci.h) +that simplify the URB creation. In general, all macros need the usb +device pointer, the pipe (usual format), the transfer buffer, the +desired transfer length, the completion handler, and its context. +Take a look at the uhci_control_msg-function that convert the old API +into an URB. + +Flags: +For ISO there are two startup behaviors: Specified start_frame or ASAP. +For ASAP set USB_ISO_ASAP in transfer_flags. + +If short packets should NOT be tolerated, set USB_DISABLE_SPD in +transfer_flags. + +Usually, (to reduce restart time) the completion handler is called +AFTER the URB re-submission. You can get the other way by setting +USB_URB_EARLY_COMPLETE in transfer_flags. This is implicite for +INT transfers. + +1.5. How to submit an URB? + +Just call + + int submit_urb(purb_t purb) + +It immediately returns, either with status 0 (request queued) or some +error code, usually caused by the following: + +- Out of memory (-ENOMEM) +- Wrong pipe handle (-ENXIO) +- Unplugged device (-ENODEV) +- Stalled endpoint (-EPIPE) +- Too many queued ISO transfers (-EAGAIN) +- Too many requested ISO frames (-EFBIG) +- Invalid INT interval (-EINVAL) +- More than one packet for INT (-EINVAL) + +After submission, urb->status is USB_ST_URB_PENDING. + +For isochronous endpoints, subsequent submitting of URBs to the same endpoint +with the ASAP flag result in a seamless ISO streaming. Exception: The +execution cannot be scheduled later than 900 frames from the 'now'-time. +The same applies to INT transfers, but here the seamless continuation is +independent of the transfer flags (implicitely ASAP). + +1.6. How to cancel an already running URB? + +Call + int unlink_urb(purb_t purb) + +It removes the urb from the internal list and frees all allocated +HW descriptors. The status is changed to USB_ST_URB_KILLED. After +unlink_urb() returns, you can safely free the URB with free_urb(urb) +and all other possibly associated data (urb->context etc.) + +1.7. What about the completion handler? + +The completion handler is optional, but useful for fast data processing +or wakeup of a sleeping process (as shown in the compatibility wrapper's +completion handler). + +The handler is of the following type: + + typedef void (*usb_complete_t)(struct urb *); + +i.e. it gets just the URB that caused the completion call. +In the completion handler, you should have a look at urb->status to +detect any USB errors. Since the context parameter is included in the URB, +you can pass information to the completion handler. + + +1.8. How to do isochronous (ISO) transfers? + +For ISO transfers you have to append the iso_packet_descriptor_t structure +to the URB for each frame you want to schedule. When using alloc_urb(n) +(recommended), the isoframe-parameter n can be used to allocate the +structures for n frames. + +For each entry you have to specify the data offset for this frame (base is +transfer_buffer), and the length you want to write/expect to read. +After completion, actual_length contains the actual transfered length and +status contains the resulting USB-status for the ISO transfer for this frame. +It is allowed to specify a varying length from frame to frame (e.g. for +audio synchronisation/adaptive transfer rates). You can also use the length +0 to omit one or more frames (striping). + +As can be concluded from above, the UHCI-driver does not care for continous +data in case of short packet ISO reads! There's no fixup_isoc() like in the +old driver. There may be a common routine to do this in the future, but this +has nothing to do with the UHCI-driver! + +For scheduling you can choose your own start frame or ASAP. As written above, +queuing more than one ISO frame with ASAP to the same device&endpoint result +in seamless ISO streaming. For continous streaming you have to use URB +linking. + +1.9. How to start interrupt (INT) transfers? + +INT transfers are currently implemented with 8 different queues for intervals +for 1, 2, 4,... 128ms. Only one TD is allocated for each interrupt. After +calling the completion handler, the TD is recycled. +With the submission of one URB, the interrupt is scheduled until it is +canceled by unlink_urb. + +The submit_urb()-call modifies urb->interval to the rounded value. + diff --git a/Documentation/usb/acm.txt b/Documentation/usb/acm.txt new file mode 100644 index 000000000000..e4fced658923 --- /dev/null +++ b/Documentation/usb/acm.txt @@ -0,0 +1,138 @@ + Linux ACM driver v0.16 + (c) 1999 Vojtech Pavlik + Sponsored by SuSE +---------------------------------------------------------------------------- + +0. Disclaimer +~~~~~~~~~~~~~ + This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + + This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + + You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Should you need to contact me, the author, you can do so either by e-mail +- mail your message to , or by paper mail: Vojtech Pavlik, +Ucitelska 1576, Prague 8, 182 00 Czech Republic + + For your convenience, the GNU General Public License version 2 is included +in the package: See the file COPYING. + +1. Usage +~~~~~~~~ + The drivers/usb/acm.c drivers works with USB modems and USB ISDN terminal +adapters that conform to the Universal Serial Bus Communication Device Class +Abstract Control Model (USB CDC ACM) specification. + + Many modems do, here is a list of those I know of: + + 3Com OfficeConnect 56k + 3Com Voice FaxModem Pro + 3Com Sportster + MultiTech MultiModem 56k + Zoom 2986L FaxModem + Compaq 56k FaxModem + ELSA Microlink 56k + + I know of one ISDN TA that does work with the acm driver: + + 3Com USR ISDN Pro TA + + Unfortunately many modems and most ISDN TAs use proprietary interfaces and +thus won't work with this drivers. Check for ACM compliance before buying. + + The driver (with devfs) creates these devices in /dev/usb/acm: + + crw-r--r-- 1 root root 166, 0 Apr 1 10:49 0 + crw-r--r-- 1 root root 166, 1 Apr 1 10:49 1 + crw-r--r-- 1 root root 166, 2 Apr 1 10:49 2 + + And so on, up to 31, with the limit being possible to change in acm.c to up +to 256, so you can use up to 256 USB modems with one computer (you'll need +three USB cards for that, though). + + If you don't use devfs, then you can create device nodes with the same +minor/major numbers anywhere you want, but either the above location or +/dev/usb/ttyACM0 is preferred. + + To use the modems you need these modules loaded: + + usbcore.o + usb-[uo]hci.o or uhci.o + acm.o + + After that, the modem[s] should be accessible. You should be able to use +minicom, ppp and mgetty with them. + +2. Verifying that it works +~~~~~~~~~~~~~~~~~~~~~~~~~~ + The first step would be to check /proc/bus/usb/devices, it should look +like this: + +T: Bus=01 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +B: Alloc= 0/900 us ( 0%), #Int= 0, #Iso= 0 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0000 ProdID=0000 Rev= 0.00 +S: Product=USB UHCI Root Hub +S: SerialNumber=6800 +C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms +T: Bus=01 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 2 Spd=12 MxCh= 0 +D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 +P: Vendor=04c1 ProdID=008f Rev= 2.07 +S: Manufacturer=3Com Inc. +S: Product=3Com U.S. Robotics Pro ISDN TA +S: SerialNumber=UFT53A49BVT7 +C: #Ifs= 1 Cfg#= 1 Atr=60 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 3 Cls=ff(vend.) Sub=ff Prot=ff Driver=acm +E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms +C:* #Ifs= 2 Cfg#= 2 Atr=60 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm +E: Ad=81(I) Atr=03(Int.) MxPS= 16 Ivl=128ms +I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm +E: Ad=85(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms +E: Ad=04(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + +The presence of these three lines (and the Cls= 'comm' and 'data' classes) +is important, it means it's an ACM device. The Driver=acm means the acm +driver is used for the device. If you see only Cls=ff(vend.) then you're out +of luck, you have a device with vendor specific-interface. + +D: Ver= 1.00 Cls=02(comm.) Sub=00 Prot=00 MxPS= 8 #Cfgs= 2 +I: If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=02 Prot=01 Driver=acm +I: If#= 1 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=acm + +In the system log you should see: + +usb.c: USB new device connect, assigned device number 2 +usb.c: kmalloc IF c7691fa0, numif 1 +usb.c: kmalloc IF c7b5f3e0, numif 2 +usb.c: skipped 4 class/vendor specific interface descriptors +usb.c: new device strings: Mfr=1, Product=2, SerialNumber=3 +usb.c: USB device number 2 default language ID 0x409 +Manufacturer: 3Com Inc. +Product: 3Com U.S. Robotics Pro ISDN TA +SerialNumber: UFT53A49BVT7 +acm.c: probing config 1 +acm.c: probing config 2 +ttyACM0: USB ACM device +acm.c: acm_control_msg: rq: 0x22 val: 0x0 len: 0x0 result: 0 +acm.c: acm_control_msg: rq: 0x20 val: 0x0 len: 0x7 result: 7 +usb.c: acm driver claimed interface c7b5f3e0 +usb.c: acm driver claimed interface c7b5f3f8 +usb.c: acm driver claimed interface c7691fa0 + +If all this seems to be OK, fire up minicom and set it to talk to the ttyACM +device and try typing 'at'. If it responds with 'OK', then everything is +working. diff --git a/Documentation/usb/dc2xx.txt b/Documentation/usb/dc2xx.txt new file mode 100644 index 000000000000..af73377dc585 --- /dev/null +++ b/Documentation/usb/dc2xx.txt @@ -0,0 +1,111 @@ +14 April 2000 +david-b@pacbell.net + +This is an overview of how to use the "dc2xx" USB driver with certain +digital still cameras from Kodak and other vendors. + + +CAMERAS + +This driver will mostly be used with Kodak DC-2xx series digital still +cameras, but it should be trivial to tell it about several non-Kodak +USB-enabled cameras. + +You'll most likely want to hook it up to recent versions of "gPhoto" +(www.gphoto.org), since version 0.4 and later know how to use it to talk +to Kodak DC-240 and DC-280 cameras over USB. + +In addition the DC-220, DC-260, DC-265, and DC-290 are also recognized. +However, like other cameras using the "Digita OS" (from www.flashpoint.com) +there is no gPhoto support for this camera. There is a python script +for accessing these cameras (see archives of the linux-usb mailing list) +and a "Digita Services" library that can also use this driver. + +The HP PhotoSmart C500 should also work, since it's another Digita camera +with USB support. + + +USB HARDWARE + +Recent kernels have had no particular problems using this driver with +either OHCI or UHCI chipsets, and have worked on the PowerMac platform. + +Note that in some cases changes in BIOS settings may be needed before +your USB works. At least one user has reported a need for SMP-related +settings as well, and some old hardware may not handle USB correctly. + + +SETUP + +Configure in the DC2XX USB driver, and have it in your kernel. It works +as a module, or compiled in directly. + +Create at least one device, perhaps like this (both read and write): + + # mknod -m 0660 /dev/usb/dc2xx0 c 180 80 + # mknod -m 0660 /dev/usb/dc2xx1 c 180 81 + ... + +NOTE: you would normally configure PAM so that the user logged in at +the console is granted ownership of these devices. console.perms(5) +explains how to do this. + +The driver supports multiple device nodes. The USB framework supports +a maximum of sixteen device nodes (up to minor device number 96). + +When you plug in one camera, it will use the first device node (dc2xx0 +in the example above). A second camera will use the second device node, +and so on. + + +SANITY TESTING + +First: if you've got /proc support, make sure that the driver has hooked +itself up correctly. + + - You should see an entry in /proc/bus/usb/drivers for "dc2xx", + if you enabled USB /proc support and correctly mounted the + usbdevfs on /proc/bus/usb. + +Second: when you connect your camera to the computer, does it get recognized +by the driver? (Make sure the camera is powered on!) + + - if you've got /proc/bus/usb/devices, you should see an entry + something like this. The "ProdID" may be different if you didn't + plug in a DC-240, as may the strings presented, but "Driver=dc2xx" + had better be there. + + T: Lev=01 Prnt=00 Port=00 Cnt=01 Dev#= 1 Spd=12 MxCh= 0 + D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 + P: Vendor=040a ProdID=0120 Rev= 1.08 + S: Manufacturer=Eastman Kodak Company + S: Product=KODAK DC240 Zoom Digital Camera + C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr=100mA + I: If#= 0 Alt= 0 #EPs= 2 Cls=00(>ifc ) Sub=00 Prot=00 Driver=dc2xx + E: Ad=01(O) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + E: Ad=82(I) Atr=02(Bulk) MxPS= 64 Ivl= 0ms + + - see if "dmesg" output tells you that you plugged in your camera. + + Manufacturer: Eastman Kodak Company + Product: KODAK DC240 Zoom Digital Camera + dc2xx.c: USB Camera #0 connected + +Third: (optional) can you use gPhoto to talk to the camera? + + - When you configure your camera, tell it to use "/dev/usb/dc2xx0" + (or whatever name you used). Right now, gPhoto emits a diagnostic + message (non-GUI) saying that it since it didn't act like a TTY, + it's assuming it's got a USB connection. + + - With the camera turned on, get the "camera summary". It'll + talk to the camera -- and tell you you're using USB. + +If you got that far, you should be able to use everything fine. + + +ADDITIONAL INFORMATION + +You may find that you need more driver-specific information, which is +currently accessible through a link from http://www.linux-usb.org/ +along with other Linux USB resources. diff --git a/Documentation/usb/error-codes.txt b/Documentation/usb/error-codes.txt new file mode 100644 index 000000000000..836661375990 --- /dev/null +++ b/Documentation/usb/error-codes.txt @@ -0,0 +1,108 @@ +$Id: README.error-codes,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This is the documentation of (hopefully) all possible error codes (and +their interpretation) that can be returned from the hostcontroller driver +and from usbcore. + +NOTE: +The USB_ST_* codes are deferred and are only listed for compatibility, new +software should use only -E* instead! + + + +************************************************************************** +* Error codes returned by usb_submit_urb * +************************************************************************** + +Non-USB-specific: + +USB_ST_NOERROR +0 URB submission went fine + +-ENOMEM no memory for allocation of internal structures + +USB-specific: + +-ENODEV specified USB-device or bus doesn't exist + +-ENXIO specified endpoint doesn't exist on the device + +USB_ST_URB_INVALID_ERROR +-EINVAL a) Invalid transfer type specified (or not supported) + b) Invalid interrupt interval (0<=n<256) + c) more than one interrupt packet requested + +-EAGAIN a) specified ISO start frame too early + b) (using ISO-ASAP) too much scheduled for the future + wait some time and try again. + +-EFBIG too much ISO frames requested (currently uhci>900) + +-EPIPE specified pipe-handle is already stalled + +-EMSGSIZE endpoint message size is zero, do interface/alternate setting + + +************************************************************************** +* Error codes returned by in urb->status * +* or in iso_frame_desc[n].status (for ISO) * +************************************************************************** + +USB_ST_NOERROR +0 Transfer completed successfully + +USB_ST_URB_KILLED +-ENOENT URB was canceled by unlink_urb + +USB_ST_URB_PENDING +-EINPROGRESS URB still pending, no results yet + (actually no error until now;-) + +USB_ST_BITSTUFF +USB_ST_INTERNALERROR +-EPROTO a) bitstuff error + b) unknown USB error + +USB_ST_CRC +-EILSEQ CRC mismatch + +-EPIPE a) babble detect + b) endpoint stalled + +USB_ST_BUFFERUNDERRUN +-ENOST buffer error + +USB_ST_NORESPONSE +USB_ST_TIMEOUT +-ETIMEDOUT transfer timed out, NAK + +USB_ST_REMOVED +-ENODEV device was removed + +USB_ST_SHORT_PACKET +-EREMOTEIO short packet detected + +USB_ST_PARTIAL_ERROR +-EXDEV ISO transfer only partially completed + look at individual frame status for details + +USB_ST_URB_INVALID_ERROR +-EINVAL ISO madness, if this happens: Log off and go home + +************************************************************************** +* Error codes returned by usbcore-functions * +* (expect also other submit and transfer status codes) * +************************************************************************** + +usb_register(): +USB_ST_NOTSUPPORTED +-EINVAL error during registering new driver + +usb_terminate_bulk(): +USB_ST_REMOVED +-ENODEV urb already removed + +usb_get_*/usb_set_*(): + All USB errors (submit/status) can occur + + diff --git a/Documentation/usb/ibmcam.txt b/Documentation/usb/ibmcam.txt new file mode 100644 index 000000000000..21d07c0ba7d2 --- /dev/null +++ b/Documentation/usb/ibmcam.txt @@ -0,0 +1,288 @@ +README for Linux device driver for the IBM "C-It" USB video camera + +INTRODUCTION: + +This driver does not use all features known to exist in +the IBM camera. However most of needed features work well. + +This driver was developed using logs of observed USB traffic +which was produced by standard Windows driver (c-it98.sys). +I did not have data sheets from Xirlink. + +Video formats: + 128x96 [model 1] + 176x144 + 320x240 [model 2] + 352x240 [model 2] + 352x288 +Frame rate: 3 - 30 frames per second (FPS) +External interface: USB +Internal interface: Video For Linux (V4L) +Supported controls: +- by V4L: Contrast, Brightness, Color, Hue +- by driver options: frame rate, lighting conditions, video format, + default picture settings, sharpness. + +SUPPORTED CAMERAS: + +IBM "C-It" camera, also known as "Xirlink PC Camera" +The device uses proprietary ASIC (and compression method); +it is manufactured by Xirlink. See http://www.xirlink.com/ +http://www.ibmpccamera.com or http://www.c-itnow.com/ for +details and pictures. + +The Linux driver was developed with camera with following +model number (or FCC ID): KSX-XVP510. This camera has three +interfaces, each with one endpoint (control, iso, iso). This +type of cameras is referred to as "model 1". These cameras are +no longer manufactured. + +Xirlink now manufactures new cameras which are somewhat different. +In particular, following models [FCC ID] belong to that category: + +XVP300 [KSX-X9903] +XVP600 [KSX-X9902] +XVP610 [KSX-X9902] + +(see http://www.xirlink.com/ibmpccamera/ for updates, they refer +to these new cameras by Windows driver dated 12-27-99, v3005 BETA) +These cameras have two interfaces, one endpoint in each (iso, bulk). +Such type of cameras is referred to as "model 2". They are supported +(with exception of 352x288 native mode). + +Quirks of Model 2 cameras: +------------------------- + +Model 2 does not have hardware contrast control. Corresponding V4L +control is not used at the moment. It may be possible to implement +contrast control in software, at cost of extra processor cycles. + +This driver provides 352x288 mode by switching the camera into +quasi-352x288 RGB mode (800 Kbits per frame) essentially limiting +this mode to 10 frames per second or less, in ideal conditions on +the bus (USB is shared, after all). The frame rate +has to be programmed very conservatively. Additional concern is that +frame rate depends on brightness setting; therefore the picture can +be good at one brightness and broken at another! I did not want to fix +the frame rate at slowest setting, but I had to move it pretty much down +the scale (so that framerate option barely matters). I also noticed that +camera after first powering up produces frames slightly faster than during +consecutive uses. All this means that if you use videosize=2 (which is +default), be warned - you may encounter broken picture on first connect; +try to adjust brightness - brighter image is slower, so USB will be able +to send all data. However if you regularly use Model 2 cameras you may +prefer videosize=1 which makes perfectly good I420, with no scaling and +lesser demands on USB (300 Kbits per second, or 26 frames per second). + +The camera that I had also has a hardware quirk: if disconnected, +it needs few minutes to "relax" before it can be plugged in again +(poorly designed USB processor reset circuit?) + +Model 2 camera can be programmed for very high sensitivity (even starlight +may be enough), this makes it convenient for tinkering with. The driver +code has enough comments to help a programmer to tweak the camera +as s/he feels necessary. + +WHAT YOU NEED: + +- A supported IBM PC (C-it) camera (model 1 or 2) + +- A Linux box with USB support (2.3/2.4; 2.2 w/backport may work) + +- A Video4Linux compatible frame grabber program such as xawtv. + +HOW TO COMPILE THE DRIVER: + +You need to compile the driver only if you are a developer +or if you want to make changes to the code. Most distributions +precompile all modules, so you can go directly to the next +section "HOW TO USE THE DRIVER". + +The driver consists of two files in usb/ directory: +ibmcam.c and ibmcam.h These files are included into the +Linux kernel build process if you configure the kernel +for CONFIG_USB_IBMCAM. Run "make xconfig" and in USB section +you will find the IBM camera driver. Select it, save the +configuration and recompile. + +HOW TO USE THE DRIVER: + +I recommend to compile driver as a module. This gives you an +easier access to its configuration. The camera has many more +settings than V4L can operate, so some settings are done using +module options. + +Typically module is installed with command 'modprobe', like this: + +# modprobe ibmcam framerate=1 + +Alternatively you can use 'insmod' in similar fashion: + +# insmod /lib/modules/2.x.y/usb/ibmcam.o framerate=1 + +Module can be inserted with camera connected or disconnected. + +The driver can have options, though some defaults are provided. + +Driver options: (* indicates that option is model-dependent) + +Name Type Range [default] Example +-------------- -------------- -------------- ------------------ +debug Integer 0-9 [0] debug=1 +flags Integer 0-0xFF [0] flags=0x0d +framerate Integer 0-6 [2] framerate=1 +hue_correction Integer 0-255 [128] hue_correction=115 +init_brightness Integer 0-255 [128] init_brightness=100 +init_contrast Integer 0-255 [192] init_contrast=200 +init_color Integer 0-255 [128] init_color=130 +init_hue Integer 0-255 [128] init_hue=115 +lighting Integer 0-2* [1] lighting=2 +sharpness Integer 0-6* [4] sharpness=3 +videosize Integer 0-2* [2] videosize=1 + +Options for Model 2 only: + +Name Type Range [default] Example +-------------- -------------- -------------- ------------------ +init_model2_rg Integer 0..255 [0x70] init_model2_rg=128 +init_model2_rg2 Integer 0..255 [0x2f] init_model2_rg2=50 +init_model2_sat Integer 0..255 [0x34] init_model2_sat=65 +init_model2_yb Integer 0..255 [0xa0] init_model2_yb=200 + +debug You don't need this option unless you are a developer. + If you are a developer then you will see in the code + what values do what. 0=off. + +flags This is a bit mask, and you can combine any number of + bits to produce what you want. Usually you don't want + any of extra features this option provides: + + FLAGS_RETRY_VIDIOCSYNC 1 This bit allows to retry failed + VIDIOCSYNC ioctls without failing. + Will work with xawtv, will not + with xrealproducer. Default is + not set. + FLAGS_MONOCHROME 2 Activates monochrome (b/w) mode. + FLAGS_DISPLAY_HINTS 4 Shows colored pixels which have + magic meaning to developers. + FLAGS_OVERLAY_STATS 8 Shows tiny numbers on screen, + useful only for debugging. + FLAGS_FORCE_TESTPATTERN 16 Shows blue screen with numbers. + FLAGS_SEPARATE_FRAMES 32 Shows each frame separately, as + it was received from the camera. + Default (not set) is to mix the + preceding frame in to compensate + for occasional loss of Isoc data + on high frame rates. + FLAGS_CLEAN_FRAMES 64 Forces "cleanup" of each frame + prior to use; relevant only if + FLAGS_SEPARATE_FRAMES is set. + Default is not to clean frames, + this is a little faster but may + produce flicker if frame rate is + too high and Isoc data gets lost. + +framerate This setting controls frame rate of the camera. This is + an approximate setting (in terms of "worst" ... "best") + because camera changes frame rate depending on amount + of light available. Setting 0 is slowest, 6 is fastest. + Beware - fast settings are very demanding and may not + work well with all video sizes. Be conservative. + +hue_correction This highly optional setting allows to adjust the + hue of the image in a way slightly different from + what usual "hue" control does. Both controls affect + YUV colorspace: regular "hue" control adjusts only + U component, and this "hue_correction" option similarly + adjusts only V component. However usually it is enough + to tweak only U or V to compensate for colored light or + color temperature; this option simply allows more + complicated correction when and if it is necessary. + +init_brightness These settings specify _initial_ values which will be +init_contrast used to set up the camera. If your V4L application has +init_color its own controls to adjust the picture then these +init_hue controls will be used too. These options allow you to + preconfigure the camera when it gets connected, before + any V4L application connects to it. Good for webcams. + +init_model2_rg These initial settings alter color balance of the +init_model2_rg2 camera on hardware level. All four settings may be used +init_model2_sat to tune the camera to specific lighting conditions. These +init_model2_yb settings only apply to Model 2 cameras. + +lighting This option selects one of three hardware-defined + photosensitivity settings of the camera. 0=bright light, + 1=Medium (default), 2=Low light. This setting affects + frame rate: the dimmer the lighting the lower the frame + rate (because longer exposition time is needed). The + Model 2 cameras allow values more than 2 for this option, + thus enabling extremely high sensitivity at cost of frame + rate, color saturation and imaging sensor noise. + +sharpness This option controls smoothing (noise reduction) + made by camera. Setting 0 is most smooth, setting 6 + is most sharp. Be aware that CMOS sensor used in the + camera is pretty noisy, so if you choose 6 you will + be greeted with "snowy" image. Default is 4. Model 2 + cameras do not support this feature. + +videosize This setting chooses one if three image sizes that are + supported by this driver. Camera supports more, but + it's difficult to reverse-engineer all formats. + Following video sizes are supported: + + videosize=0 128x96 (Model 1 only) + videosize=1 176x144 + videosize=2 352x288 + videosize=3 320x240 (Model 2 only) + videosize=4 352x240 (Model 2 only) + + The last one (352x288) is the native size of the sensor + array, so it's the best resolution camera (Model 1) can + yield. The best resolution of Model 2 is 176x144, and + larger images are produced by stretching the bitmap. + Choose the image size you need. The smaller image can + support faster frame rate. Default is 352x288. + +WHAT NEEDS TO BE DONE: + +- The box freezes if camera is unplugged after being used (OHCI). + Workaround: remove usb-ohci module first. +- On occasion camera (model 1) does not start properly (xawtv reports + errors), or camera produces negative image (funny colors.) + Workaround: reload the driver module. Reason: [1]. +- The button on the camera is not used. I don't know how to get to it. + I know now how to read button on Model 2, but what to do with it? + +[1] +- Camera reports its status back to the driver; however I don't know + what returned data means. If camera fails at some initialization + stage then something should be done, and I don't do that because + I don't even know that some command failed. This is mostly Model 1 + concern because Model 2 uses different commands which do not return + status (and seem to complete successfully every time). + +VIDEO SIZE AND IMAGE SIZE + +Camera produces picture X by Y pixels. This is camera-specific and can be +altered by programming the camera accordingly. This image is placed onto +larger (or equal) area W by H, this is V4L image. At this time the driver +uses V4L image size (W by H) 352x288 pixels because many programs (such +as xawtv) expect quite specific sizes and don't want to deal with arbitrary, +camera-specific sizes. However this approach "hides" real image size, and +application always sees the camera as producing only 352x288 image. It is +possible to change the V4L image size to 128x96, and then if camera is +switched to 128x96 mode then xawtv will correctly accept this image size. But +many other popular sizes (such as 176x144) will not be welcomed. This is the +reason why all camera images are at this time placed onto 352x288 "canvas", +and size of that canvas (V4L) is reported to applications. It will be easy +to add options to control the canvas size, but it will be application- +specific because not all applications are ready to work with variety of +camera-specific sizes. + +CREDITS: + +The code is based in no small part on the CPiA driver by Johannes Erdfelt, +Randy Dunlap, and others. Big thanks to them for their pioneering work on that +and the USB stack. diff --git a/Documentation/usb/input.txt b/Documentation/usb/input.txt new file mode 100644 index 000000000000..c60bd900bad0 --- /dev/null +++ b/Documentation/usb/input.txt @@ -0,0 +1,306 @@ + Linux Input drivers v1.0 + (c) 1999-2000 Vojtech Pavlik + Sponsored by SuSE + $Id: input.txt,v 1.4 2000/05/28 17:57:22 vojtech Exp $ +---------------------------------------------------------------------------- + +0. Disclaimer +~~~~~~~~~~~~~ + This program is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License as published by the Free +Software Foundation; either version 2 of the License, or (at your option) +any later version. + + This program is distributed in the hope that it will be useful, but +WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY +or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +more details. + + You should have received a copy of the GNU General Public License along +with this program; if not, write to the Free Software Foundation, Inc., 59 +Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Should you need to contact me, the author, you can do so either by e-mail +- mail your message to , or by paper mail: Vojtech Pavlik, +Ucitelska 1576, Prague 8, 182 00 Czech Republic + + For your convenience, the GNU General Public License version 2 is included +in the package: See the file COPYING. + +1. Introduction +~~~~~~~~~~~~~~~ + This is a collection of drivers that is designed to support all input +devices under Linux. However, in the current kernels, although it's +possibilities are much bigger, it's limited to USB devices only. This is +also why it resides in the drivers/usb subdirectory. + + The center of the input drivers is the input.o module, which must be +loaded before any other of the input modules - it serves as a way of +communication between two groups of modules: + +1.1 Device drivers +~~~~~~~~~~~~~~~~~~ + These modules talk to the hardware (for example via USB), and provide +events (keystrokes, mouse movements) to the input.o module. + +1.2 Event handlers +~~~~~~~~~~~~~~~~~~ + These modules get events from input.o and pass them where needed via +various interfaces - keystrokes to the kernel, mouse movements via a +simulated PS/2 interface to GPM and X and so on. + +2. Simple Usage +~~~~~~~~~~~~~~~ + For the most usual configuration, with one USB mouse and one USB keyboard, +you'll have to load the following modules (or have them built in to the +kernel): + + input.o + mousedev.o + keybdev.o + usbcore.o + usb-[uo]hci.o + hid.o + + After this, the USB keyboard will work straight away, and the USB mouse +will be available as a character device on major 13, minor 63: + + crw-r--r-- 1 root root 13, 63 Mar 28 22:45 mice + + This device, has to be created, unless you use devfs, in which case it's +created automatically. The commands to do that are: + + cd /dev + mkdir input + mknod input/mice c 13 63 + + After that you have to point GPM (the textmode mouse cut&paste tool) and +XFree to this device to use it - GPM should be called like: + + gpm -t ps2 -m /dev/input/mice + + And in X: + + Section "Pointer" + Protocol "ImPS/2" + Device "/dev/input/mice" + ZAxisMapping 4 5 + EndSection + + When you do all of the above, you can use your USB mouse and keyboard. + +3. Detailed Description +~~~~~~~~~~~~~~~~~~~~~~~ +3.1 Device drivers +~~~~~~~~~~~~~~~~~~ + Device drivers are the modules that generate events. The events are +however not useful without being handled, so you also will need to use some +of the modules from section 3.2. + +3.1.1 hid.c +~~~~~~~~~~~ + Hid.c is the largest and most complex driver of the whole suite. It +handles all HID devices, and because there is a very wide variety of them, +and because the USB HID specification isn't simple, it needs to be this big. + + Currently, it handles USB mice, joysticks, gamepads, steering wheels +keyboards, trackballs and digitizers. + + However, USB uses HID also for monitor controls, speaker controls, UPSs, +LCDs and many other purposes. + + The monitor and speaker controls should be easy to add to the hid/input +interface, but for the UPSs and LCDs it doesn't make much sense. The driver +doesn't support these yet, and a new, non-event interface should be designed +for them. If you have any of these devices (I don't), feel free to design +something and if it's good, it'll get into the driver. + + The usage of the hid.o module is very simple, it takes no parameters, +detects everything automatically and when a HID device is inserted, it +detects it appropriately. + + However, because the devices vary wildly, you might happen to have a +device that doesn't work well. In that case #define DEBUG at the beginning +of hid.c and send me the syslog traces. + +3.1.2 usbmouse.c +~~~~~~~~~~~~~~~~ + For embedded systems, for mice with broken HID descriptors and just any +other use when the big hid.c wouldn't be a good choice, there is the +usbmouse.c driver. It handles USB mice only. It uses a simpler HIDBP +protocol. This also means the mice must support this simpler protocol. Not +all do. If you don't have any strong reason to use this module, use hid.c +instead. + +3.1.3 usbkbd.c +~~~~~~~~~~~~~~ + Much like usbmouse.c, this module talks to keyboards with a simpplified +HIDBP protocol. It's smaller, but doesn't support any extra special keys. +Use hid.c instead if there isn't any special reason to use this. + +3.1.4 wacom.c +~~~~~~~~~~~~~ + This is a driver for Wacom Graphire and Intuos tablets. Not for Wacom +PenPartner, that one is handled by the HID driver. Although the Intuos and +Graphire tablets claim that they are HID tablets as well, they are not and +thus need this specific driver. + +3.1.5 wmforce.c +~~~~~~~~~~~~~~~ + A driver for the Logitech WingMan Force joysticks, when connected via the +USB port. It works quite well, but there is no force feedback support yet, +because the interface to do that is a trade secret of Immersion Corp, and +even Logitech can't disclose it. + + Support for Logitech WingMan Force Wheel isn't present in this driver, but +if someone has the device, and is willing to cooperate, it should be a +matter of a couple days to add it. + +3.2 Event handlers +~~~~~~~~~~~~~~~~~~ + Event handlers distrubite the events from the devices to userland and +kernel, as needed. + +3.2.1 keybdev.c +~~~~~~~~~~~~~~~ + Keybdev is currently a rather ugly hack that translates the input events +into architecture-specific keyboard raw mode (Xlated AT Set2 on x86), and +passes them into the handle_scancode function of the keyboard.c module. This +works well enough on all architectures that keybdev can generate rawmode on, +other architectures can be added to it. + + The right way would be to pass the events to keyboard.c directly, best if +keyboard.c would itself be an event handler. This is done in the input +patch, available on the webpage mentioned below. + +3.2.2 mousedev.c +~~~~~~~~~~~~~~~~ + Mousedev is also a hack to make programs that use mouse input work. It +takes events from either mice or digitizers/tablets and makes a PS/2-style +(a la /dev/psaux) mouse device available to the userland. Ideally, the +programs could use a more reasonable interface, for example evdev.c + + Mousedev devices in /dev/input (as shown above) are: + + crw-r--r-- 1 root root 13, 32 Mar 28 22:45 mouse0 + crw-r--r-- 1 root root 13, 33 Mar 29 00:41 mouse1 + crw-r--r-- 1 root root 13, 34 Mar 29 00:41 mouse2 + crw-r--r-- 1 root root 13, 35 Apr 1 10:50 mouse3 + ... + ... + crw-r--r-- 1 root root 13, 62 Apr 1 10:50 mouse30 + crw-r--r-- 1 root root 13, 63 Apr 1 10:50 mice + +Each 'mouse' device is assigned to a single mouse or digitizer, except the last +one - 'mice'. This single character device is shared by all mice and +digitizers, and even if none are connected, the device is present. This is +useful for hotplugging USB mice, so that programs can open the device even when +no mice are present. + + CONFIG_INPUT_MOUSEDEV_SCREEN_[XY] in the kernel configuration are the size +of your screen (in pixels) in XFree86. This is needed if you want to use +your digitizer in X, because it's movement is sent to X via a virtual PS/2 +mouse and thus needs to be scaled accordingly. These values won't be used if +you use a mouse only. + + Mousedev will generate either PS/2, ImPS/2 (Microsoft IntelliMouse) or +GenPS/2 (Genius NetMouse/NetScroll) protocols, depending on what the program +reading the data wishes. You can set GPM and X to any of these. You'll need +ImPS/2 if you want to make use of a wheel on a USB mouse and GenPS/2 if you +want to use extra (up to 5) buttons. I'm not sure how much is GenPS/2 supported +in X, though. + +3.2.3 joydev.c +~~~~~~~~~~~~~~ + Joydev implements v0.x and v1.x Linux joystick api, much like +drivers/char/joystick/joystick.c. See joystick-api.txt in the Documentation +subdirectory for details. Joydev does it on top of the input subsystem, +though. As soon as any USB joystick is connected, it can be accessed in +/dev/input on: + + crw-r--r-- 1 root root 13, 0 Apr 1 10:50 js0 + crw-r--r-- 1 root root 13, 1 Apr 1 10:50 js1 + crw-r--r-- 1 root root 13, 2 Apr 1 10:50 js2 + crw-r--r-- 1 root root 13, 3 Apr 1 10:50 js3 + ... + +And so on up to js31. + +3.2.4 evdev.c +~~~~~~~~~~~~~ + Evdev is the generic input event interface. It passes the events generated +in the kernel straight to the program, with timestamps. The API is still +evolving, but should be useable now. It's described in section 5. + + This should be the way for GPM and X to get keyboard and mouse mouse +events. It allows for multihead in X without any specific multihead kernel +support. The event codes are the same on all architectures and are hardware +independent. + + The devices are in /dev/input: + + crw-r--r-- 1 root root 13, 64 Apr 1 10:49 event0 + crw-r--r-- 1 root root 13, 65 Apr 1 10:50 event1 + crw-r--r-- 1 root root 13, 66 Apr 1 10:50 event2 + crw-r--r-- 1 root root 13, 67 Apr 1 10:50 event3 + ... + +3. Contacts +~~~~~~~~~~~ + This effort has it's home page at: + + http://www.suse.cz/development/input/ + +You'll find both the latest HID driver and the complete Input driver there +as well as information how to access the CVS repository for latest revisions +of the drivers. + + + There is also a mailing list for this: + + majordomo@atrey.karlin.mff.cuni.cz + +Send "subscribe linux-input" to subscribe to it. + +4. Verifying if it works +~~~~~~~~~~~~~~~~~~~~~~~~ + Typing a couple keys on the keyboard should be enough to check that a USB +keyboard works and is correctly connected to the kernel keyboard driver. + + Doing a cat /dev/input/mouse0 (c, 13, 32) will verify that a mouse is also +emulated, characters should appear if you move it. + + You can test the joystick emulation with the 'jstest' utility, available +in the joystick package (see Documentation/joystick.txt). + + You can test the event devics with the 'evtest' utitily available on the +input driver homepage (see the URL above). + +5. Event interface +~~~~~~~~~~~~~~~~~~ + Should you want to add event device support into any application (X, gpm, +svgalib ...) I will be happy to provide you any help I +can. Here goes a description of the current state of things, which is going +to be extended, but not changed incompatibly as time goes: + + You can use blocking and nonblocking reads, also select() on the +/dev/input/eventX devices, and you'll always get a whole number of input +events on a read. Their layout is: + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; + + 'time' is the timestamp, it returns the time at which the event happened. +Type is for example EV_REL for relative momement, REL_KEY for a keypress or +release. More types are defined in include/linux/input.h. + + 'code' is event code, for example REL_X or KEY_BACKSPACE, again a complete +list is in include/linux/input.h. + + 'value' is the value the event carries. Either a relative change for +EV_REL, absolute new value for EV_ABS (joysticks ...), or 0 for EV_KEY for +release, 1 for keypress and 2 for autorepeat. diff --git a/drivers/usb/README.ohci_hcd b/Documentation/usb/ohci.txt similarity index 82% rename from drivers/usb/README.ohci_hcd rename to Documentation/usb/ohci.txt index 53bd35c955b3..39a5ce4827ef 100644 --- a/drivers/usb/README.ohci_hcd +++ b/Documentation/usb/ohci.txt @@ -1,13 +1,13 @@ The OHCI HCD layer is a simple but nearly complete implementation of what the USB people would call a HCD for the OHCI. - (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) + (ISO comming soon, Bulk, INT u. CTRL transfers enabled) It is based on Linus Torvalds UHCI code and Gregory Smith OHCI fragments (0.03 source tree). The layer (functions) on top of it, is for interfacing to the alternate-usb device-drivers. - Roman Weissgaerber - + * v4.0 1999/08/18 removed all dummy eds, unlink unused eds, code cleanup, bulk transfers * v2.1 1999/05/09 ep_addr correction, code cleanup * v0.2.0 1999/05/04 * everything has been moved into 2 files (ohci-hcd.c, ohci-hub-root.c and headers) @@ -21,16 +21,14 @@ The layer (functions) on top of it, is for interfacing to the alternate-usb devi * v0.1.0 1999/04/27 initial release to remove the module try: -killall root-hub -: -rmmod usb-ohci-hcd +rmmod usb-ohci Features: - virtual root hub, all basic hub descriptors and commands (state: complete) this is an option now (v0.2.0) #define CONFIG_USB_OHCI_VROOTHUB includes the virtual hub code, (VROOTHUB) - default is without. - (at the moment: the Virtual Root Hub option is not recommended) + default is with. + (at the moment: the Virtual Root Hub is included automatically) files: ohci-root-hub.c, ohci-root-hub.h @@ -41,6 +39,7 @@ Features: In the HCD layer the EDs has to be allocated manually either by calling a subroutine or by sending a USB root hub vendor specific command to the virtual root hub. At the alternate linux usb stack EDs will be added (allocated) at their first use. + ED will be unlinked from the HC chains if they are not bussy. files: ohci-hcd.c ohci-hcd.h routines: (do not use for drivers, use the top layer alternate usb commands instead) @@ -49,10 +48,10 @@ Features: int interval, int load, f_handler handler, int ep_size, int speed) adds an endpoint, (if the endpoint already exists some parameters will be updated) - int usb_ohci_rm_ep(struct usb_ohci_ed *ed, struct ohci * ohci) + int usb_ohci_rm_ep( ) removes an endpoint and all pending TDs of that EP - usb_ohci_rm_function( struct ohci * ohci, union ep_addr_ ep_addr) + usb_ohci_rm_function( ) removes all Endpoints of a function (device) - Transfer Descriptors (TD): handling and allocation of TDs is transparent to the upper layers @@ -62,7 +61,7 @@ Features: There is one basic command for all types of bus transfers (INT, BULK, ISO, CTRL): - int ohci_trans_req(struct ohci * ohci, int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) + int ohci_trans_req(struct ohci * ohci, hcd_ed, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) CTRL: ctrl, ctrl_len ... cmd buffer data, data_len ... data buffer (in or out) @@ -96,17 +95,4 @@ Features: some error handling, root-hub request dequeuing (files: ohci-done-list.c in ohci-hcd.c now(v0.2.0)) -ep_addr union or int is for addressing devices&endpoints: -__u8 ep_addr.bep.ep ... bit 3..0 endpoint address - bit 4 0 - bit 6,5 type: eg. 10 CTRL, 11 BULK, 01 INT, 00 ISO - bit 7 direction 1 IN, 0 OUT - -__u8 ep_addr.bep.fa ... bit 6..0 function address - bit 7 0 - -(__u8 ep_addr.bep.hc ... host controller nr) not used -(__u8 ep_addr.bep.host ... host nr) not used - - diff --git a/Documentation/usb/ov511.txt b/Documentation/usb/ov511.txt new file mode 100644 index 000000000000..a9245312959f --- /dev/null +++ b/Documentation/usb/ov511.txt @@ -0,0 +1,225 @@ +------------------------------------------------------------------------------- +Readme for Linux device driver for the OmniVision OV511 USB to camera bridge IC +------------------------------------------------------------------------------- + +Author: Mark McClelland +Homepage: http://alpha.dyndns.org/ov511 + +NEW IN THIS VERSION: + o Race conditions and other bugs fixed + +INTRODUCTION: + +This is a driver for the OV511, a USB-only chip used in many "webcam" devices. +Any camera using the OV511/OV511+ and the OV7610/20/20AE CCD should work.It +supports streaming and capture of color or monochrome video via the Video4Linux +API. Most V4L apps are compatible with it, but a few videoconferencing programs +do not work yet. The following resolutions are supported: 640x480, 448x336, +384x288, 352x288, and 320x240. + +WHAT YOU NEED: + +- If you want to help with the development, get the chip's specification docs at + http://www.ovt.com/omniusbp.html + +- A Video4Linux compatible frame grabber program (I recommend vidcat and xawtv) + vidcat is part of the w3cam package: http://www.hdk-berlin.de/~rasca/w3cam/ + xawtv is available at: http://www.in-berlin.de/User/kraxel/xawtv.html + +HOW TO USE IT: + +You must have first compiled USB support, support for your specific USB host +controller (UHCI or OHCI), and Video4Linux support for your kernel (I recommend +making them modules.) + +Next, (as root) from your appropriate modules directory (lib/modules/2.3.XX): + + insmod usb/usbcore.o + insmod usb/usb-uhci.o insmod usb/ohci-hcd.o + insmod misc/videodev.o + insmod usb/ov511.o + +If it is not already there (it usually is), create the video device: + + mknod /dev/video c 81 0 + +Sometimes /dev/video is a symlink to /dev/video0 + +You will have to set permissions on this device to allow you to read/write +from it: + + chmod 666 /dev/video + chmod 666 /dev/video0 (if necessary) + +Now you are ready to run a video app! Both vidcat and xawtv work well for me +at 640x480. + +[Using vidcat:] + + vidcat -s 640x480 > test.jpg + xview test.jpg + +[Using xawtv:] + +You must make some modifications to the source and compile it before you use it. +(Note: this may not be applicable to versions other than 3.06) + +In src/Xawtv.ad, change xawtv.tv.width to 640 and xawtv.tv.height to 480. Next, +in src/grab-v4l.c, change SYNC_TIMEOUT from 1 to 2. Then, from the main xawtv +directory: + + make clean + ./configure + make + make install + +Now you should be able to run xawtv. Right click for the options dialog. If +you get a scrambled image it is likely that you made a mistake in Xawtv.ad. +Try setting the size to 320x240 if all else fails. + +FAQ: +Q: "Why does the picture have noise and look grainy" +A: This is a problem at low light levels, and may be also due to subtle bugs in + the code. The cause is most likely the OV7610 settings we are currently + using. I am looking into this problem. + +Q: "The driver sometimes says `Failed to read OV7610 ID.' What is the deal?" +A: The I2C code that allows the OV511 to communicate with the camera chip is a + bit flaky right now. This message means that the I2C bus never got + initialized properly, and the camera will most likely not work even if you + disable this warning. Try unloading/reloading the driver or unplugging/re- + plugging the camera if this happens. Also try increasing the i2c_detect_tries + parameter (see below). + +Q: "Why do you bother with this phony camera detection crap? It doesn't do + anything useful!" +A: The main purpose of only supporting known camera models is to force people + with new camera models to tell me about them, so I can assemble the list + above, and so the code can know what CCD chip you have. Right now, nearly all + of the cameras use the OV7610 and consequently I have not put support for + other ones in, so the value of the detection code is questionable. Eventually + though, new CCDs might appear and we will be fortunate to have the detection. + +MODULE PARAMETERS: + + You can set these with: insmod ov511 NAME=VALUE + There is currently no way to set these on a per-camera basis. + + NAME: autoadjust + TYPE: integer (boolean) + DEFAULT: 1 + DESC: The camera normally adjusts exposure, gain, and hue automatically. This + can be set to 0 to disable this automatic adjustment. Note that there is + currently no way to set these parameters manually once autoadjust is + disabled. + + NAME: debug + TYPE: integer (0-6) + DEFAULT: 3 + DESC: Sets the threshold for printing debug messages. The higher the value, + the more is printed. The levels are cumulative, and are as follows: + 0=no debug messages + 1=init/detection/unload and other significant messages + 2=some warning messages + 3=config/control function calls + 4=most function calls and data parsing messages + 5=highly repetitive mesgs + + NAME: fix_rgb_offset + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Some people have reported that the blue component of the image is one + or so lines higher than the red component. This is only apparent in + images with white objects on black backgrounds at 640x480. Setting this + to 1 will realign the color planes correctly. NOTE: This is still + experimental and very buggy. You will likely need a fast (500 Mhz) CPU. + + NAME: snapshot + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Set to 1 to enable snapshot mode. read() will block until the snapshot + button is pressed. Note that this does not yet work with most apps, + including xawtv and vidcat. NOTE: See the section "TODO" for more info. + + NAME: sensor + TYPE: integer ([0, 1, 3]) + DEFAULT: [varies] + DESC: If you know that your camera sensor is not detected correctly, set this + parameter. This is a global option for all attached OV511 cameras. You + will probably never need to set this, but if you do, valid values are: + 0 for OV7620 + 1 for OV7620AE + 3 for OV7610 + + NAME: i2c_detect_tries + TYPE: integer (don't set it insanely high!) + DEFAULT: 5 + DESC: This is the number of times the driver will try to sync and detect the + internal i2c bus (which connects the OV511 and sensor). If you are + getting intermittant detection failures ("Failed to read sensor ID...") + you should increase this by a modest amount. If setting it to 20 or so + doesn't fix things, look elsewhere for the cause of the problem. + + NAME: aperture + TYPE: integer (0 - 15) + DEFAULT: [varies by sensor] + DESC: For legal values, see the OV7610/7620 specs under register Common F. + This setting affects the upper nybble of that reg (bits 4-7). This is + for if you want to play with the camera's pixel saturation. + + NAME: force_rgb + TYPE: integer (boolean) + DEFAULT: 0 + DESC: Force image to be read in RGB instead of BGR. This option allow + programs that expect RGB data (e.g. gqcam) to work with this driver. If + your colors look VERY wrong, you may want to change this. + +WORKING FEATURES: + o Color streaming/capture at 640x480, 448x336, 384x288, 352x288, and 320x240 + o YUV420 color + o Monochrome + o Setting/getting of saturation, contrast and brightness (no hue yet; only + works with OV7610, not the OV7620 or OV7620AE) + o proc status reporting + +EXPERIMENTAL FEATURES: + o fix_rgb_offset: Sometimes works, but other times causes errors with xawtv and + corrupted frames. If you have a very fast CPU, you can try it. + o Snapshot mode (only works with some read() based apps; see below for more) + o read() support + +TODO: + o Fix the noise / grainy image problem. + o Get compression working. It would be a nice addition as it improves + frame rate quite a bit. OmniVision wouldn't tell me how the algorithm works, + so we can't really work on that yet. Please kindly inform OmniVision that you + would like them to release their specifications to the Linux community. + o Get 160x120 working + o YUV422 (and other color modes) + o Get snapshot mode working with mmap(). + o Fix fixFrameRGBoffset(). It is not stable yet with streaming video. + o Get hue (red/blue channel balance) adjustment working (in ov511_get_picture() + and ov511_set_picture()) + o Get autoadjust disable working + o V4L2 support (Probably not until it goes into the kernel) + o Fix I2C initialization. Some people are reporting problems with reading the + 7610 registers. This could be due to timing differences, an excessive I2C + clock rate, or a problem with ov511_i2c_read(). + o Get rid of the memory management functions (put them in videodev.c??) + o Setting of contrast and brightness not working with 7620 + o Driver/camera state save/restore for when USB supports suspend/resume + o Multiple cameras reportedly do not work simultaneously + o Problems with OHCI + +HOW TO CONTACT ME: + +You can email me at mmcclelland@delphi.com . Please prefix the subject line +with "OV511: " so that I am certain to notice your message. + +CREDITS: + +The code is based in no small part on the CPiA driver by Johannes Erdfelt, +Randy Dunlap, and others. Big thanks to them for their pioneering work on that +and the USB stack. Thanks to Bret Wallach for getting camera reg IO, ISOC, and +image capture working. Thanks to Orion Sky Lawlor, Kevin Moore, and Claudio +Matsuoka for their work as well. diff --git a/Documentation/usb/proc_usb_info.txt b/Documentation/usb/proc_usb_info.txt new file mode 100644 index 000000000000..84510b9505d2 --- /dev/null +++ b/Documentation/usb/proc_usb_info.txt @@ -0,0 +1,256 @@ +/proc/bus/usb filesystem output +=============================== +(version 2000.03.24) + + +The /proc filesystem for USB devices generates +/proc/bus/usb/drivers and /proc/bus/usb/devices. + +/proc/bus/usb/drivers lists the registered drivers, +one per line, with each driver's USB minor dev node +number range if applicable. + +In /proc/bus/usb/devices, each device's output has multiple +lines of ASCII output. +I made it ASCII instead of binary on purpose, so that someone +can obtain some useful data from it without the use of an +auxiliary program. However, with an auxiliary program, the numbers +in the first 4 columns of each "T:" line (topology info: +Lev, Prnt, Port, Cnt) can be used to build a USB topology diagram. +(I think. I haven't proved this, but I have tested it with 3 +different topo/connections and it looked possible.) + +Each line is tagged with a one-character ID for that line: + +T = Topology (etc.) +B = Bandwidth (applies only to USB host controllers, which are + virtualized as root hubs) +D = Device descriptor info. +P = Product ID info. (from Device descriptor, but they won't fit + together on one line) +S = String descriptors. +C = Configuration descriptor info. (* = active configuration) +I = Interface descriptor info. +E = Endpoint descriptor info. + +======================================================================= + +/proc/bus/usb/devices output format: + +Legend: + d = decimal number (may have leading spaces or 0's) + x = hexadecimal number (may have leading spaces or 0's) + s = string + + +Topology info: + +T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd +| | | | | | | | |__MaxChildren +| | | | | | | |__Device Speed in Mbps +| | | | | | |__DeviceNumber +| | | | | |__Count of devices at this level +| | | | |__Connector/Port on Parent for this device +| | | |__Parent DeviceNumber +| | |__Level in topology for this bus +| |__Bus number +|__Topology info tag + + +Bandwidth info: +B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd +| | | |__Number if isochronous requests +| | |__Number of interrupt requests +| |__Total Bandwidth allocated to this bus +|__Bandwidth info tag + + +Device descriptor info & Product ID info: + +D: Ver=x.xx Cls=xx(s) Sub=xx Prot=xx MxPS=dd #Cfgs=dd +P: Vendor=xxxx ProdID=xxxx Rev=xx.xx + +where +D: Ver=x.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd +| | | | | | |__NumberConfigurations +| | | | | |__MaxPacketSize of Default Endpoint +| | | | |__DeviceProtocol +| | | |__DeviceSubClass +| | |__DeviceClass +| |__Device USB version +|__Device info tag #1 + +where +P: Vendor=xxxx ProdID=xxxx Rev=xx.xx +| | | |__Product revision number +| | |__Product ID code +| |__Vendor ID code +|__Device info tag #2 + + +String descriptor info: + +S: Manufacturer=ssss +| |__Manufacturer of this device as read from the device. +|__String info tag + +S: Product=ssss +| |__Product description of this device as read from the device, +| except that it is a generated string for USB host controllers +| (virtual root hubs), in the form "USB *HCI Root Hub". +|__String info tag + +S: SerialNumber=ssss +| |__Serial Number of this device as read from the device, +| except that it is a generated string for USB host controllers +| (virtual root hubs), and represent's the host controller's +| unique identification in the system (currently I/O or +| memory-mapped base address). +|__String info tag + + +Configuration descriptor info: + +C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA +| | | | |__MaxPower in mA +| | | |__Attributes +| | |__ConfiguratioNumber +| |__NumberOfInterfaces +|__Config info tag + + +Interface descriptor info (can be multiple per Config): + +I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=ssss +| | | | | | | |__Driver name +| | | | | | |__InterfaceProtocol +| | | | | |__InterfaceSubClass +| | | | |__InterfaceClass +| | | |__NumberOfEndpoints +| | |__AlternateSettingNumber +| |__InterfaceNumber +|__Interface info tag + + +Endpoint descriptor info (can be multiple per Interface): + +E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms +E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms +| | | | |__Interval +| | | |__EndpointMaxPacketSize +| | |__Attributes(EndpointType) +| |__EndpointAddress(I=In,O=Out) +|__Endpoint info tag + +======================================================================= + + +If a user or script is interested only in Topology info, for +example, use something like "grep ^T: /proc/bus/usb/devices" +for only the Topology lines. A command like +"grep -i ^[tdp]: /proc/bus/usb/devices" can be used to list +only the lines that begin with the characters in square brackets, +where the valid characters are TDPCIE. With a slightly more able +script, it can display any selected lines (for example, only T, D, +and P lines) and change their output format. (The "procusb" +Perl script is the beginning of this idea. It will list only +selected lines [selected from TBDPSCIE] or "All" lines from +/proc/bus/usb/devices.) + +The Topology lines can be used to generate a graphic/pictorial +of the USB devices on a system's root hub. (See more below +on how to do this.) + +The Interface lines can be used to determine what driver is +being used for each device. + +The Configuration lines could be used to list maximum power +(in milliamps) that a system's USB devices are using. +For example, "grep ^C: /proc/bus/usb/devices". + + +Here's an example, from a system which has a UHCI root hub, +an external hub connected to the root hub, and a mouse and +a serial converter connected to the external hub. + +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +B: Alloc= 28/900 us ( 3%), #Int= 2, #Iso= 0 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0000 ProdID=0000 Rev= 0.00 +S: Product=USB UHCI Root Hub +S: SerialNumber=dce0 +C:* #Ifs= 1 Cfg#= 1 Atr=40 MxPwr= 0mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 8 Ivl=255ms +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +D: Ver= 1.00 Cls=09(hub ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0451 ProdID=1446 Rev= 1.00 +C:* #Ifs= 1 Cfg#= 1 Atr=e0 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +E: Ad=81(I) Atr=03(Int.) MxPS= 1 Ivl=255ms +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=04b4 ProdID=0001 Rev= 0.00 +C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse +E: Ad=81(I) Atr=03(Int.) MxPS= 3 Ivl= 10ms +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +D: Ver= 1.00 Cls=00(>ifc ) Sub=00 Prot=00 MxPS= 8 #Cfgs= 1 +P: Vendor=0565 ProdID=0001 Rev= 1.08 +S: Manufacturer=Peracom Networks, Inc. +S: Product=Peracom USB to Serial Converter +C:* #Ifs= 1 Cfg#= 1 Atr=a0 MxPwr=100mA +I: If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial +E: Ad=81(I) Atr=02(Bulk) MxPS= 64 Ivl= 16ms +E: Ad=01(O) Atr=02(Bulk) MxPS= 16 Ivl= 16ms +E: Ad=82(I) Atr=03(Int.) MxPS= 8 Ivl= 8ms + + +Selecting only the "T:" and "I:" lines from this (for example, by using +"procusb ti"), we have: + +T: Bus=00 Lev=00 Prnt=00 Port=00 Cnt=00 Dev#= 1 Spd=12 MxCh= 2 +T: Bus=00 Lev=01 Prnt=01 Port=00 Cnt=01 Dev#= 2 Spd=12 MxCh= 4 +I: If#= 0 Alt= 0 #EPs= 1 Cls=09(hub ) Sub=00 Prot=00 Driver=hub +T: Bus=00 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#= 3 Spd=1.5 MxCh= 0 +I: If#= 0 Alt= 0 #EPs= 1 Cls=03(HID ) Sub=01 Prot=02 Driver=mouse +T: Bus=00 Lev=02 Prnt=02 Port=02 Cnt=02 Dev#= 4 Spd=12 MxCh= 0 +I: If#= 0 Alt= 0 #EPs= 3 Cls=00(>ifc ) Sub=00 Prot=00 Driver=serial + + +Physically this looks like (or could be converted to): + + +------------------+ + | PC/root_hub (12)| Dev# = 1 + +------------------+ (nn) is Mbps. + Level 0 | CN.0 | CN.1 | [CN = connector/port #] + +------------------+ + / + / + +-----------------------+ + Level 1 | Dev#2: 4-port hub (12)| + +-----------------------+ + |CN.0 |CN.1 |CN.2 |CN.3 | + +-----------------------+ + \ \____________________ + \_____ \ + \ \ + +--------------------+ +--------------------+ + Level 2 | Dev# 3: mouse (1.5)| | Dev# 4: serial (12)| + +--------------------+ +--------------------+ + + + +Or, in a more tree-like structure (ports [Connectors] without +connections could be omitted): + +PC: Dev# 1, root hub, 2 ports, 12 Mbps +|_ CN.0: Dev# 2, hub, 4 ports, 12 Mbps + |_ CN.0: Dev #3, mouse, 1.5 Mbps + |_ CN.1: + |_ CN.2: Dev #4, serial, 12 Mbps + |_ CN.3: +|_ CN.1: + + + ### END ### diff --git a/Documentation/usb/rio.txt b/Documentation/usb/rio.txt new file mode 100644 index 000000000000..0aa79ab0088c --- /dev/null +++ b/Documentation/usb/rio.txt @@ -0,0 +1,138 @@ +Copyright (C) 1999, 2000 Bruce Tenison +Portions Copyright (C) 1999, 2000 David Nelson +Thanks to David Nelson for guidance and the usage of the scanner.txt +and scanner.c files to model our driver and this informative file. + +Mar. 2, 2000 + +CHANGES + +- Initial Revision + + +OVERVIEW + +This README will address issues regarding how to configure the kernel +to access a RIO 500 mp3 player. +Before I explain how to use this to access the Rio500 please be warned: + +W A R N I N G: +-------------- + +Please note that this software is still under development. The authors +are in no way responsible for any damage that may occur, no matter how +inconsequential. + +It seems that the Rio has a problem when sending .mp3 with low batteries. +I suggest when the batteries are low and want to transfer stuff that you +replace it with a fresh one. In my case, what happened is I lost two 16kb +blocks (they are no longer usable to store information to it). But I don't +know if thats normal or not. It could simply be a problem with the flash +memory. + +In an extreme case, I left my Rio playing overnight and the batteries wore +down to nothing and appear to have corrupted the flash memory. My RIO +needed to be replaced as a result. Diamond tech support is aware of the +problem. Do NOT allow your batteries to wear down to nothing before +changing them. It appears RIO 500 firmware does not handle low battery +power well at all. + +On systems with OHCI controllers, the kernel OHCI code appears to have +power on problems with some chipsets. If you are having problems +connecting to your RIO 500, try turning it on first and then plugging it +into the USB cable. + +Contact information: +-------------------- + + The main page for the project is hosted at sourceforge.net in the following + address: http://rio500.sourceforge.net You can also go to the sourceforge + project page at: http://sourceforge.net/project/?group_id=1944 There is + also a mailing list: rio500-users@lists.sourceforge.net + +Authors: +------- + +Most of the code was written by Cesar Miquel . Keith +Clayton is incharge of the PPC port and making sure +things work there. Bruce Tenison is adding support +for .fon files and also does testing. The program will mostly sure be +re-written and Pete Ikusz along with the rest will re-design it. I would +also like to thank Tri Nguyen who provided use +with some important information regarding the communication with the Rio. + +ADDITIONAL INFORMATION and Userspace tools + +http://rio500.sourceforge.net/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. + +A Linux development kernel (2.3.x) with USB support enabled or a +backported version to linux-2.2.x. See http://www.linux-usb.org for +more information on accomplishing this. + +A Linux kernel with RIO 500 support enabled. + +'lspci' which is only needed to determine the type of USB hardware +available in your machine. + +CONFIGURATION + +Using `lspci -v`, determine the type of USB hardware available. + + If you see something like: + + USB Controller: ...... + Flags: ..... + I/O ports at .... + + Then you have a UHCI based controller. + + If you see something like: + + USB Controller: ..... + Flags: .... + Memory at ..... + + Then you have a OHCI based controller. + +Using `make menuconfig` or your preferred method for configuring the +kernel, select 'Support for USB', 'OHCI/UHCI' depending on your +hardware (determined from the steps above), 'USB Diamond Rio500 support', and +'Preliminary USB device filesystem'. Compile and install the modules +(you may need to execute `depmod -a` to update the module +dependencies). + +Add a device for the USB rio500: + `mknod /dev/usb/rio500 c 180 64` + +Set appropriate permissions for /dev/usb/rio500 (don't forget about +group and world permissions). Both read and write permissions are +required for proper operation. + +Load the appropriate modules (if compiled as modules): + + OHCI: + modprobe usbcore + modprobe usb-ohci + modprobe rio500 + + UHCI: + modprobe usbcore + modprobe usb-uhci (or uhci) + modprobe rio500 + +That's it. The Rio500 Utils at: http://rio500.sourceforge.net should +be able to access the rio500. + +BUGS + +If you encounter any problems feel free to drop me an email. + +Bruce Tenison +btenison@dibbs.net + diff --git a/Documentation/usb/scanner-hp-sane.txt b/Documentation/usb/scanner-hp-sane.txt new file mode 100644 index 000000000000..25c36b7fe796 --- /dev/null +++ b/Documentation/usb/scanner-hp-sane.txt @@ -0,0 +1,81 @@ +Copyright (C) 1999, 2000 David E. Nelson + +April 26, 2000 + +CHANGES + +- Amended for Linux-2.3.99-pre6-3 +- Updated for multiple scanner support + +INTRODUCTION + +This document will hopefully provide enough info on how to get SANE +working with a Hewlett Packard USB capable scanner using the USB +interface. The majority of HP Scanners support the Scanner Control +Language (SCL) which is both published by HP and supported by SANE. +The only HP Scanners that I'm aware of that do not support SCL are the +4200C ,3300C, and the PhotoSmart S20. All other HP scanners with USB +interfaces should work (4100C, 5200C, 6200C, and 6300C) as do models +that are derived from the models above. ie the 6350C which is a 6300C +with a transparency adaptor included with the scanner at time of +purchase. Of course as HP releases new scanners this information may +change. + + +REQUIREMENTS + +In order to get this running you'll need USB support in your kernel in +addition to USB Scanner support. Please refer to scanner.txt for +issues pertaining to Linux USB and USB Scanner support. + +An installed version of SANE which is available from +http://www.mostang.com/sane/. Testing has been performed using +version SANE-1.0.1. For instructions on building and installing SANE, +refer to the various README files within the SANE distribution. + +The latest SANE HP backend is available from http://www.kirchgessner.net. +At the time of this writing, version 0.83 was available. + + +OK, I'VE INSTALLED SANE. SO WHAT DO I DO NOW? + +NOTE: $INSTALL_DIR is the location where SANE is installed. It may be +/usr/local, /usr, /opt or somewhere else. If you don't know, ask your +system administrator. + +1) Make sure that you have the libsane-hp.* libraries under the +$INSTALL_DIR/lib/sane/ directory. If you don't, then the HP backend +was either not compiled or installed properly. + +2) Under the directory $INSTALL_DIR/etc/sane.d/ edit the following +files: dll.conf, hp.conf. + + dll.conf: Make sure that the 'hp' entry is present and uncommented. + + hp.conf: This should contain two lines: + + /dev/usbscanner + option connect-device + +NOTE: If you are using multiple scanners, make sure to have the correct +device, ie /dev/usbscanner0. See scanner.txt for more info. + +3) You should now be able to use SANE (xscanimage or scanimage). + +Don't forget to read any relevant man pages regarding the usage of +SANE. If you have other entries uncommented in 'dll.conf', you may +have to specify the device to (x)scanimage. Again, `man` is your +friend. The xscanimage (1) man page has info on how to get 'The Gimp' +to work with xscanimage. Note that Gimp support must be compiled into +SANE for it to work. If you are dealing with a RedHat system, this +means that you'll also need to install the gimp-devel rpm package +prior to compiling SANE. + +NOTE: The issues regarding core dumping by (x)scanimage have (or seem +to be thus far) been resolved with version 0.2+ of the USB scanner +driver which should be available in linux-2.3.23. If you notice +otherwise, please contact me. + +David /\/elson +dnelson@jump.net +http://www.jump.net/~dnelson diff --git a/Documentation/usb/scanner.txt b/Documentation/usb/scanner.txt new file mode 100644 index 000000000000..e800b37e5f7d --- /dev/null +++ b/Documentation/usb/scanner.txt @@ -0,0 +1,362 @@ +Copyright (C) 1999, 2000 David E. Nelson + +April 26, 2000 + +CHANGES + +- Amended for linux-2.3.99-pre6-3 +- Appended hp_scan.c to end of this README +- Removed most references to HP +- Updated uhci/ohci host controller info +- Updated support for multiple scanner support +- Updated supported scanners list +- Updated usbdevfs info +- Spellcheck + +OVERVIEW + +This README addresses issues regarding how to configure the kernel +to access a USB scanner. Although the driver was originally conceived +for USB HP scanners, it's general enough so that it can be used with +other scanners. Also, one can now pass the USB Vendor and Product +ID's using module parameters for unknown scanners. Refer to the +document scanner-hp-sane.txt for guidance on how to configure SANE to +use a USB HP Scanner. + + +ADDITIONAL INFORMATION + +http://www.linux-usb.org/ + + +REQUIREMENTS + +A host with a USB port. Ideally, either a UHCI (Intel) or OHCI +(Compaq and others) hardware port should work. At the time of this +writing, there are two UHCI drivers and one OHCI. + +A Linux development kernel (2.3.x) with USB support enabled or a +backported version to linux-2.2.x. See http://www.linux-usb.org for +more information on accomplishing this. + +A Linux kernel with USB Scanner support enabled. + +'lspci' which is only needed to determine the type of USB hardware +available/installed in your machine. + +CONFIGURATION + +Using `lspci -v`, determine the type of USB hardware available/installed. + + If you see something like: + + USB Controller: ...... + Flags: ..... + I/O ports at .... + + Then you have a UHCI based controller. + + If you see something like: + + USB Controller: ..... + Flags: .... + Memory at ..... + + Then you have a OHCI based controller. + +Using `make menuconfig` or your preferred method for configuring the +kernel, select 'Support for USB', 'OHCI/UHCI' depending on your +hardware (determined from the steps above), 'USB Scanner support', and +'Preliminary USB device filesystem'. Compile and install the modules +(you may need to execute `depmod -a` to update the module +dependencies). If any of the USB sections were compiled into the +kernel, a reboot is necessary. NOTE: Updating the boot disk with +'lilo' may also be required. Testing was performed only as modules, +YMMV. + +Beginning with version 0.4 of the driver, up to 16 scanners can be +connected/used simultaneously. If you intend to use more than +one scanner at a time: + + Add a device for the USB scanner: + `mknod /dev/usbscanner0 c 180 48` + `mknod /dev/usbscanner1 c 180 49` + . + . + `mknod /dev/usb/scanner15 180 63` + + +If you foresee using only one scanner it is best to: + `mknod /dev/usbscanner0 c 180 48` + `ln -s /dev/usbscanner0 /dev/usbscanner` + + +Set appropriate permissions for /dev/usbscanner[0-15] (don't forget +about group and world permissions). Both read and write permissions +are required for proper operation. For example: + `chmod 666 /dev/usbscanner0` + +Load the appropriate modules (if compiled as modules): + + OHCI: + modprobe usb-ohci + modprobe scanner + + UHCI: + modprobe usb-uhci + modprobe scanner + +That's it. SANE should now be able to access the device. + +There is a small test program (hp_scan.c -- appended below) that can +be used to test the scanner device if it's an HP scanner that supports +SCL (Scanner Control Language). Known HP scanner that support SCL are +the 4100, 5200, 6200, the 6300 -- note that the 4200 is *not* +supported since it does not understand SCL; it's also strongly +suspected that the 3300 and the PhotoSmart S20 are not SCL compliant. +Hp_scan.c's purpose is to test the driver without having to +retrieve/configure SANE. Hp_scan.c will scan the entire bed and put +the output into a file called 'out.dat' in the current directory. The +data in the file is raw data so it's not very useful for imaging. + +MESSAGES + +On occassion the message 'usb_control/bulk_msg: timeout' or something +similar will appear in '/var/adm/messages' or on the console or both, +depending on how your system is configured. This is a side effect +that scanners are sometimes very slow at warming up and/or +initialiazing. In most cases, however, only several of these messages +should appear and is generally considered to be normal. If you see +a message of the type 'excessive NAK's received' then this should +be considered abnormal and generally indicates that the USB system is +unable to communicate with the scanner for some particular reason. + +SUPPORTED SCANNERS + +NOTE: Just because a product is listed here does not mean that +applications exist that support the product. It's in the hopes that +this will allow developers a means to produce applications that will +support the listed USB products. + +At the time of this writing, the following scanners were supported by +scanner.c: + + Acer + Prisa Acerscan 620U & 640U (!) + Prisa AcerScan 620U (!) + Agfa + SnapScan 1212U + Another SnapScan 1212U (?) + SnapScan Touch + Colorado -- See Primax/Colorado below + Epson -- See Seiko/Epson below + Genius + ColorPage-Vivid Pro + Hewlett Packard + 3300C + 4100C + 4200C + PhotoSmart S20 + 5200C + 6200C + 6300C + Microtek + ScanMaker X6 - X6U + Phantom 336CX - C3 + Phantom 336CX - C3 #2 + Phantom C6 + ScanMaker V6USL + ScanMaker V6USL #2 + ScanMaker V6UL - SpicyU + Mustek + 1200 CU + Primax/Colorado + G2-300 #1 + G2-600 #1 + G2E-300 #1 + ReadyScan 636i + G2-300 #2 + G2-600 #2 + G2E-300 #2 + G2E-600 + Colorado USB 9600 + Colorado USB 19200 + Colorado 600u + Colorado 1200u + Seiko/Epson Corp. + Perfection 636U and 636Photo + Perfection 610 + Perfection 1200U and 1200Photo + Umax + Astra 1220U + Astra 1236U + Astra 2000U + Astra 2200U + Visioneer + OneTouch 5300 + OneTouch 7600 duplicate ID (!) + 6100 + + +MODULE PARAMETERS + +If you have a device that you wish to experiment with or try using +this driver with, but the Vendor and Product ID's are not coded in, +don't despair. If the driver was compiled as a module, you can pass +options to the driver. Simply add + + options scanner vendor=0x#### product=0x**** + +to the /etc/modules.conf file replacing the #'s and the *'s with the +correct ID's. The ID's can be retrieved from the messages file or +using `cat /proc/bus/usb/devices`. Note that USB /proc support must be +enabled during kernel configuration. If the 'scanner' module is +already loaded into memory, it must be reloaded for the module +parameters to take effect. In essence, `rmmod scanner; modprobe +scanner` must be performed. + +**NOTE**: In later kernels (2.3.38+), a new filesystem was introduced, +usbdevfs. To mount the filesystem, issue the command (as root): + + mount -t usbdevfs /proc/bus/usb /proc/bus/usb + +An alternative and more permanent method would be to add + + none /proc/bus/usb usbdevfs defaults 0 0 + +to /etc/fstab. This will mount usbdevfs at each reboot. You can then +issue `cat /proc/bus/usb/devices` to extract USB device information. + + +BUGS + +Just look at the list of fixes in the source files. So, if you +encounter any problems feel free to drop me an email. + +David /\/elson +dnelson@jump.net +http://www.jump.net/~dnelson + +--------------- snip -- hp_scan.c -- snip --------------- +/* + +This is a really crude attempt at writing a short test program. It's +mostly only to be used to test connectivity with USB HP scanners that +understand SCL. Currently, the supported models are 4100C, 5200C, +6200C, and the 6300C. Note that the 4200C is *NOT* acceptable. + +Copyright (C) David E. Nelson , 1999 + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or (at +your option) any later version. + +*/ + +#include +#include +#include +#include +#include + +/* + Gray Output produces about a 8945400 byte file. + Color Output produces a 26836200 byte file. + + To compile: gcc -o hp_scan hp_scan.c +*/ + +// #define COLOR /* Undef to scan GrayScale */ + +int send_cmd(int, const char *, int); +int read_cmd(int, char *, int); + +int +main(void) { + + ssize_t cnt = 0, total_cnt = 0; + + FILE *fpout; + + int fp; + int data_size = 32768; + + char *data; + + static char reset_cmd[] = {'\x1b','E'}; + +#ifdef COLOR + static char data_type_cmd[] = {'\x1b','*','a','5','T'}; /* Color */ + static char data_width_cmd[] = {'\x1b','*','a','2','4','G'}; /* 24 Bit Color */ +#else + static char data_type_cmd[] = {'\x1b','*','a','4','T'}; /* Gray */ + static char data_width_cmd[] = {'\x1b','*','a','8','G'}; /* 8 Bit Gray */ +#endif + + static char query_cmd[] = {'\x1b', '*', 's', '2', '5', '7', 'E'}; + static char start_scan_cmd[] = {'\x1b','*','f','0','S'}; + + if(!(data=malloc(data_size))) { + perror("malloc failed"); + exit (1); + } + + if((fp=open("/dev/usbscanner", O_RDWR)) < 0) { + perror("Unable to open scanner device"); + exit (1); + } + + if((fpout=fopen("out.dat", "w+")) == NULL) { + perror("Unable to open ouput file"); + exit(1); + } + + send_cmd(fp, reset_cmd, sizeof(reset_cmd)); + send_cmd(fp, data_type_cmd, sizeof(data_type_cmd)); + send_cmd(fp, data_width_cmd, sizeof(data_width_cmd)); + send_cmd(fp, start_scan_cmd, sizeof(start_scan_cmd)); + + while ((cnt = read(fp, data, data_size)) > 0) { + printf("Read: %u\n", cnt); + if(fwrite(data, sizeof(char), cnt, fpout) < 0) { + perror("Write to output file failed"); + exit (1); + } + total_cnt += cnt; + } + if (cnt < 0) { + perror("Read from scanner failed"); + exit (1); + } + + printf("\nRead %lu bytes.\n", total_cnt); + + send_cmd(fp, reset_cmd, sizeof(reset_cmd)); + + close(fp); + fclose(fpout); + return (0); +} + +int +send_cmd(int fp, const char * cmd, int length) { + + int result; + int x; + + if((result = write(fp, cmd, length)) != length) { + printf ("Write warning: %d bytes requested, %d written\n"); + } else if (result < 0) { + perror ("send_cmd failure"); + exit (1); + } + return (result); +} + +int +read_cmd(int fp, char * response, int length) { + + return read(fp, response, length); + +} diff --git a/Documentation/usb/uhci.txt b/Documentation/usb/uhci.txt new file mode 100644 index 000000000000..53aaa11d8fb7 --- /dev/null +++ b/Documentation/usb/uhci.txt @@ -0,0 +1,165 @@ +Specification and Internals for the New UHCI Driver (Whitepaper...) + + brought to you by + + Georg Acher, acher@in.tum.de (executive slave) (base guitar) + Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + + $Id: README.uhci,v 1.1 1999/12/14 14:03:02 fliegl Exp $ + +This document and the new uhci sources can be found on + http://hotswap.in.tum.de/usb + +1. General issues + +1.1 Why a new UHCI driver, we already have one?!? + +Correct, but its internal structure got more and more mixed up by the (still +ongoing) efforts to get isochronous transfers (ISO) to work. +Since there is an increasing need for reliable ISO-transfers (especially +for USB-audio needed by TS and for a DAB-USB-Receiver build by GA and DF), +this state was a bit unsatisfying in our opinion, so we've decided (based +on knowledge and experiences with the old UHCI driver) to start +from scratch with a new approach, much simpler but at the same time more +powerful. +It is inspired by the way Win98/Win2000 handles USB requests via URBs, +but it's definitely 100% free of MS-code and doesn't crash while +unplugging an used ISO-device like Win98 ;-) +Some code for HW setup and root hub management was taken from the +original UHCI driver, but heavily modified to fit into the new code. +The invention of the basic concept, and major coding were completed in two +days (and nights) on the 16th and 17th of October 1999, now known as the +great USB-October-Revolution started by GA, DF, and TS ;-) + +Since the concept is in no way UHCI dependant, we hope that it will also be +transfered to the OHCI-driver, so both drivers share a common API. + +1.2. Advantages and disadvantages + ++ All USB transfer types work now! ++ Asynchronous operation ++ Simple, but powerful interface (only two calls for start and cancel) ++ Easy migration to the new API, simplified by a compatibility API ++ Simple usage of ISO transfers ++ Automatic linking of requests ++ ISO transfers allow variable length for each frame and striping ++ No CPU dependent and non-portable atomic memory access, no asm()-inlines ++ Tested on x86 and Alpha + +- Rewriting for ISO transfers needed + +1.3. Is there some compatibility to the old API? + +Yes, but only for control, bulk and interrupt transfers. We've implemented +some wrapper calls for these transfer types. The usbcore works fine with +these wrappers. For ISO there's no compatibility, because the old ISO-API +and its semantics were unnecessary complicated in our opinion. + +1.4. What's really working? + +As said above, CTRL und BULK already work fine even with the wrappers, +so legacy code wouldn't notice the change. +Regarding to Thomas, ISO transfers now run stable with USB audio. +INT transfers (e.g. mouse driver) work fine, too. + +1.5. Are there any bugs? + +No ;-) +Hm... +Well, of course this implementation needs extensive testing on all available +hardware, but we believe that any fixes shouldn't harm the overall concept. + +1.6. What should be done next? + +A large part of the request handling seems to be identical for UHCI and +OHCI, so it would be a good idea to extract the common parts and have only +the HW specific stuff in uhci.c. Furthermore, all other USB device drivers +should need URBification, if they use isochronous or interrupt transfers. +One thing missing in the current implementation (and the old UHCI driver) +is fair queueing for BULK transfers. Since this would need (in principle) +the alteration of already constructed TD chains (to switch from depth to +breadth execution), another way has to be found. Maybe some simple +heuristics work with the same effect. + +--------------------------------------------------------------------------- + +2. Internal structure and mechanisms + +To get quickly familiar with the internal structures, here's a short +description how the new UHCI driver works. However, the ultimate source of +truth is only uhci.c! + +2.1. Descriptor structure (QHs and TDs) + +During initialization, the following skeleton is allocated in init_skel: + + framespecific | common chain + +framelist[] +[ 0 ]-----> TD --> TD -------\ +[ 1 ]-----> TD --> TD --------> TD ----> QH -------> QH -------> QH ---> NULL + ... TD --> TD -------/ +[1023]-----> TD --> TD ------/ + + ^^ ^^ ^^ ^^ ^^ ^^ + 1024 TDs for 7 TDs for 1 TD for Start of Start of End Chain + ISO INT (2-128ms) 1ms-INT CTRL Chain BULK Chain + +For each CTRL or BULK transfer a new QH is allocated and the containing data +transfers are appended as (vertical) TDs. After building the whole QH with its +dangling TDs, the QH is inserted before the BULK Chain QH (for CTRL) or +before the End Chain QH (for BULK). Since only the QH->next pointers are +affected, no atomic memory operation is required. The three QHs in the +common chain are never equipped with TDs! + +For ISO or INT, the TD for each frame is simply inserted into the apropriate +ISO/INT-TD-chain for the desired frame. The 7 skeleton INT-TDs are scattered +among the 1024 frames similar to the old UHCI driver. + +For CTRL/BULK/ISO, the last TD in the transfer has the IOC-bit set. For INT, +every TD (there is only one...) has the IOC-bit set. + +Besides the data for the UHCI controller (2 or 4 32bit words), the descriptors +are double-linked through the .vertical and .horizontal elements in the +SW data of the descriptor (using the double-linked list structures and +operations), but SW-linking occurs only in closed domains, i.e. for each of +the 1024 ISO-chains and the 8 INT-chains there is a closed cycle. This +simplifies all insertions and unlinking operations and avoids costly +bus_to_virt()-calls. + +2.2. URB structure and linking to QH/TDs + +During assembly of the QH and TDs of the requested action, these descriptors +are stored in urb->urb_list, so the allocated QH/TD descriptors are bound to +this URB. +If the assembly was successful and the descriptors were added to the HW chain, +the corresponding URB is inserted into a global URB list for this controller. +This list stores all pending URBs. + +2.3. Interrupt processing + +Since UHCI provides no means to directly detect completed transactions, the +following is done in each UHCI interrupt (uhci_interrupt()): + +For each URB in the pending queue (process_urb()), the ACTIVE-flag of the +associated TDs are processed (depending on the transfer type +process_{transfer|interrupt|iso}()). If the TDs are not active anymore, +they indicate the completion of the transaction and the status is calculated. +Inactive QH/TDs are removed from the HW chain (since the host controller +already removed the TDs from the QH, no atomic access is needed) and +eventually the URB is marked as completed (OK or errors) and removed from the +pending queue. Then the next linked URB is submitted. After (or immediately +before) that, the completion handler is called. + +2.4. Unlinking URBs + +First, all QH/TDs stored in the URB are unlinked from the HW chain. +To ensure that the host controller really left a vertical TD chain, we +wait for one frame. After that, the TDs are physically destroyed. + +2.5. URB linking and the consequences + +Since URBs can be linked and the corresponding submit_urb is called in +the UHCI-interrupt, all work associated with URB/QH/TD assembly has to be +interrupt save. This forces kmalloc to use GFP_ATOMIC in the interrupt. diff --git a/Documentation/usb/usb-help.txt b/Documentation/usb/usb-help.txt new file mode 100644 index 000000000000..9abd4a08b063 --- /dev/null +++ b/Documentation/usb/usb-help.txt @@ -0,0 +1,16 @@ +usb-help.txt +2000-March-24 + +For USB help other than the readme files that are located in +linux/Documentation/usb/*, see the following: + +Linux-USB project: http://www.linux-usb.org + mirrors at http://www.suse.cz/development/linux-usb/ + and http://usb.in.tum.de/linux-usb/ +Linux USB Guide: http://linuxusbguide.sourceforge.net +Linux-USB device overview (working devices and drivers): + http://www.qbik.ch/usb/devices/ + +The Linux-USB mailing list is linux-usb@suse.com . + +### diff --git a/Documentation/usb/usb-serial.txt b/Documentation/usb/usb-serial.txt new file mode 100644 index 000000000000..00248f36e22b --- /dev/null +++ b/Documentation/usb/usb-serial.txt @@ -0,0 +1,164 @@ +INTRODUCTION + + The USB serial driver currently supports a number of different USB to + serial converter products, as well as some devices that use a serial + interface from userspace to talk to the device. + + See the individual product section below for specific information about + the different devices. + + +CONFIGURATION + + Currently the driver can handle up to 256 different serial interfaces at + one time. + + If you are not using devfs: + The major number that the driver uses is 188 so to use the driver, + create the following nodes: + mknod /dev/ttyUSB0 c 188 0 + mknod /dev/ttyUSB1 c 188 1 + mknod /dev/ttyUSB2 c 188 2 + mknod /dev/ttyUSB3 c 188 3 + . + . + . + mknod /dev/ttyUSB254 c 188 254 + mknod /dev/ttyUSB255 c 188 255 + + If you are using devfs: + The devices supported by this driver will show up as + /dev/usb/tts/{0,1,...} + + When the device is connected and recognized by the driver, the driver + will print to the system log, which node(s) the device has been bound + to. + + +SPECIFIC DEVICES SUPPORTED + + +ConnectTech WhiteHEAT 4 port converter + + ConnectTech has been very forthcoming with information about their + device, including providing a unit to test with. This driver will end up + being fully supported. + +Current status: + The device's firmware is downloaded on connection, the new firmware + runs properly and all four ports are successfuly recognized and connected. + Now data flow needs to be implemented properly. + This driver is not fully operational. + + +HandSpring Visor USB docking station + +Current status: + Only when the Visor tries to connect to the host, does the docking + station show up as a valid USB device. When this happens, the device is + properly enumerated, assigned a port, and then communication _should_ be + possible. The driver cleans up properly when the device is removed, or + the connection is canceled on the Visor. + + NOTE: + This means that in order to talk to the Visor, the sync button must be + pressed BEFORE trying to get any program to communicate to the Visor. + This goes against the current documentation for pilot-xfer and other + packages, but is the only way that it will work due to the hardware + in the Visor. + + When the device is connected, try talking to it on the second port + (this is usually /dev/ttyUSB1 if you do not have any other usb-serial + devices in the system.) The system log should tell you which port is + the port to use for the HotSync transfer. The "Generic" port can be used + for other device communication, such as a PPP link. + + There is a webpage and mailing lists for this portion of the driver at: + http://usbvisor.sourceforge.net/ + + +Keyspan PDA Serial Adapter + + Single port DB-9 serial adapter, pushed as a PDA adapter for iMacs (mostly + sold in Macintosh catalogs, comes in a translucent white/green dongle). + Fairly simple device. Firmware is homebrew. + +Current status: + Things that work: + basic input/output (tested with 'cu') + blocking write when serial line can't keep up + changing baud rates (up to 115200) + getting/setting modem control pins (TIOCM{GET,SET,BIS,BIC}) + sending break (although duration looks suspect) + Things that don't: + device strings (as logged by kernel) have trailing binary garbage + device ID isn't right, might collide with other Keyspan products + changing baud rates ought to flush tx/rx to avoid mangled half characters + Big Things on the todo list: + parity, 7 vs 8 bits per char, 1 or 2 stop bits + HW flow control + not all of the standard USB descriptors are handled: Get_Status, Set_Feature + O_NONBLOCK, select() + + +FTDI Single Port Serial Driver + + This is a single port DB-25 serial adapter. More information about this + device and the Linux driver can be found at: + http://reality.sgi.com/bryder_wellington/ftdi_sio/ + + +ZyXEL omni.net lcd plus ISDN TA + + This is an ISDN TA. Please report both successes and troubles to the + author at omninet@kroah.com + + +Digi AccelePort Driver + + This driver supports the Digi AccelePort USB 4 device, a 4 port + USB serial converter. The driver does NOT yet support the Digi + AccelePort USB 2 or 8. + + The driver supports open, close, read, write, termios settings (baud + rate, word size, parity, stop bits, hardware/software flow control, + CREAD), DTR/RTS, and TIOCMGET/SET/BIS/BIC ioctls. It has not been + thoroughly tested, but it seems to be working reasonable well. There + is more work to do, including flow control, ioctls, and support for + the Digi AccelePort USB 2 and 8. + + Please contact Peter Berger (pberger@brimson.com) or Al Borchers + (alborchers@steinerpoint.com) for questions or problems with this + driver. + + +Generic Serial driver + + If your device is not one of the above listed devices, compatible with + the above models, you can try out the "generic" interface. This + interface does not provide any type of control messages sent to the + device, and does not support any kind of device flow control. All that + is required of your device is that it has at least one bulk in endpoint, + or one bulk out endpoint. + + To enable the generic driver to recognize your device, build the driver + as a module and load it by the following invocation: + insmod usb-serial vendor=0x#### product=0x#### + where the #### is replaced with the hex representation of your device's + vendor id and product id. + + This driver has been successfully used to connect to the NetChip USB + development board, providing a way to develop USB firmware without + having to write a custom driver. + + +CONTACT: + + If anyone has any problems using this driver, with any of the above + specified products, please contact me, or join the Linux-USB mailing + list (information on joining the mailing list, as well as a link to its + searchable archive is at http://www.linux-usb.org/ ) + + +Greg Kroah-Hartman +greg@kroah.com diff --git a/MAINTAINERS b/MAINTAINERS index db15bfc03c35..0c448948f905 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -301,6 +301,13 @@ L: compaqandlinux@cpqlin.van-dijk.net W: ftp.compaq.com/pub/products/drivers/linux S: Supported +COMPAQ SMART CISS RAID DRIVER +P: Charles White +M: Charles White +L: compaqandlinux@cpqlin.van-dijk.net +W: ftp.compaq.com/pub/products/drivers/linux +S: Supported + DAC960 RAID DRIVER P: Leonard N. Zubkoff M: Leonard N. Zubkoff diff --git a/Makefile b/Makefile index cc21c2544d7d..6d895658e2c0 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 2 SUBLEVEL = 18 -EXTRAVERSION = pre1 +EXTRAVERSION = pre2 ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/) @@ -191,7 +191,7 @@ DRIVERS := $(DRIVERS) drivers/tc/tc.a endif ifeq ($(CONFIG_USB),y) -DRIVERS := $(DRIVERS) drivers/usb/usb.a +DRIVERS := $(DRIVERS) drivers/usb/usbdrv.o endif ifeq ($(CONFIG_I2O),y) @@ -348,7 +348,10 @@ modules_install: if [ -f VIDEO_MODULES ]; then inst_mod VIDEO_MODULES video; fi; \ if [ -f FC4_MODULES ]; then inst_mod FC4_MODULES fc4; fi; \ if [ -f IRDA_MODULES ]; then inst_mod IRDA_MODULES net; fi; \ + if [ -f USB_MODULES ]; then inst_mod USB_MODULES usb; fi; \ + if [ -f USB_SERIAL_MODULES ]; then inst_mod USB_SERIAL_MODULES usb; fi; \ if [ -f SK98LIN_MODULES ]; then inst_mod SK98LIN_MODULES net; fi; \ + if [ -f SKFP_MODULES ]; then inst_mod SKFP_MODULES net; fi; \ \ for f in *.o; do [ -r $$f ] && echo $$f; done | sort > $$MODLIB/.allmods; \ echo $$MODULES | tr ' ' '\n' | sort | comm -23 $$MODLIB/.allmods - > $$MODLIB/.misc; \ diff --git a/arch/alpha/config.in b/arch/alpha/config.in index 15c93d422658..7694727f10ef 100644 --- a/arch/alpha/config.in +++ b/arch/alpha/config.in @@ -268,7 +268,7 @@ fi endmenu source drivers/char/Config.in - +source drivers/usb/Config.in source fs/Config.in if [ "$CONFIG_VT" = "y" ]; then diff --git a/arch/i386/config.in b/arch/i386/config.in index 92ddef794f91..9bcdbad0d185 100644 --- a/arch/i386/config.in +++ b/arch/i386/config.in @@ -178,7 +178,7 @@ endmenu source drivers/char/Config.in -# source drivers/usb/Config.in +source drivers/usb/Config.in source fs/Config.in diff --git a/arch/i386/kernel/microcode.c b/arch/i386/kernel/microcode.c index fbebff98237f..ba12cc426b47 100644 --- a/arch/i386/kernel/microcode.c +++ b/arch/i386/kernel/microcode.c @@ -39,24 +39,12 @@ #include #include #include -#include +#include #include #include #include -#include -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) -#ifdef devfs_register -#undef devfs_register -#define devfs_register(a,b,c,d,e,f,g,h) NULL -#endif -#ifndef devfs_set_file_size -#define devfs_set_file_size(a,b) -#endif -#ifndef __exit -#define __exit -#endif #define MICROCODE_MINOR 184 #define MICROCODE_IOCFREE _IO('6',0) struct microcode { @@ -70,7 +58,6 @@ struct microcode { unsigned int reserved[5]; unsigned int bits[500]; }; -#endif #define MICROCODE_VERSION "1.05" @@ -104,9 +91,6 @@ static char *mc_applied; /* holds an array of applied microcode blocks */ static unsigned int mc_fsize; static struct file_operations microcode_fops = { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) - owner: THIS_MODULE, -#endif read: microcode_read, write: microcode_write, ioctl: microcode_ioctl, @@ -120,13 +104,7 @@ static struct miscdevice microcode_dev = { fops: µcode_fops, }; -static devfs_handle_t devfs_handle; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) -static int __init microcode_init(void) -#else int __init microcode_init(void) -#endif { int error = 0; @@ -136,32 +114,20 @@ int __init microcode_init(void) MICROCODE_MINOR); error = 1; } - devfs_handle = devfs_register(NULL, "cpu/microcode", - DEVFS_FL_DEFAULT, 0, 0, S_IFREG | S_IRUSR | S_IWUSR, - µcode_fops, NULL); - if (devfs_handle == NULL && error) { - printk(KERN_ERR "microcode: failed to devfs_register()\n"); - return -EINVAL; - } printk(KERN_INFO "P6 Microcode Update Driver v%s registered\n", MICROCODE_VERSION); return 0; } -static void __exit microcode_exit(void) +static void microcode_exit(void) { misc_deregister(µcode_dev); - devfs_unregister(devfs_handle); if (mc_applied) kfree(mc_applied); printk(KERN_INFO "P6 Microcode Update Driver v%s unregistered\n", MICROCODE_VERSION); } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0) -module_init(microcode_init); -module_exit(microcode_exit); -#else #ifdef MODULE int init_module(void) { @@ -172,7 +138,6 @@ void cleanup_module(void) microcode_exit(); } #endif -#endif /* * We enforce only one user at a time here with open/close. @@ -209,8 +174,13 @@ static int do_microcode_update(void) int i, error = 0, err; struct microcode *m; +#ifdef __SMP__ if (smp_call_function(do_update_one, (void *)update_req, 1, 1) != 0) panic("do_microcode_update(): timed out waiting for other CPUs\n"); +#else + smp_call_function(do_update_one, (void *)update_req, 1, 1); +#endif + do_update_one((void *)update_req); for (i=0; iselector)); + __asm__("mov %%fs,%0":"=r" (selector)); + addr->selector = selector; break; case PREFIX_GS_-1: - /* The cast is needed here to get gcc 2.8.0 to use a 16 bit register + /* The extra variable is needed here to get gcc to use a 16 bit register in the assembler statement. */ - __asm__("mov %%gs,%0":"=r" ((unsigned short)addr->selector)); + __asm__("mov %%gs,%0":"=r" (selector)); + addr->selector = selector; break; default: addr->selector = PM_REG_(segment); diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c index 32f3c33fd476..549e724ce169 100644 --- a/arch/i386/mm/ioremap.c +++ b/arch/i386/mm/ioremap.c @@ -110,7 +110,18 @@ void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flag * Don't allow anybody to remap normal RAM that we're using.. */ if (phys_addr < virt_to_phys(high_memory)) - return NULL; + { + char *temp_addr, *temp_end; + int i; + + temp_addr = __va(phys_addr); + temp_end = temp_addr + (size - 1); + + for(i = MAP_NR(temp_addr); i < MAP_NR(temp_end); i++) { + if(!PageReserved(mem_map + i)) + return NULL; + } + } /* * Mappings have to be page-aligned diff --git a/arch/sparc64/config.in b/arch/sparc64/config.in index 78cb350958e8..a6d8b70464d7 100644 --- a/arch/sparc64/config.in +++ b/arch/sparc64/config.in @@ -247,9 +247,10 @@ if [ "$CONFIG_NET" = "y" ]; then tristate 'EtherExpressPro/100 support' CONFIG_EEXPRESS_PRO100 tristate 'SysKonnect SK-98xx support' CONFIG_SK98LIN fi -# bool 'FDDI driver support' CONFIG_FDDI -# if [ "$CONFIG_FDDI" = "y" ]; then -# fi + bool 'FDDI driver support' CONFIG_FDDI + if [ "$CONFIG_FDDI" = "y" ]; then + tristate ' SysKonnect FDDI PCI support' CONFIG_SKFP + fi fi endmenu fi @@ -273,6 +274,12 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then fi endmenu +mainmenu_option next_comment +comment 'XFree86 DRI support' +bool 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM +dep_tristate ' Creator/Creator3D' CONFIG_DRM_FFB $CONFIG_DRM +endmenu + source fs/Config.in mainmenu_option next_comment diff --git a/drivers/block/Config.in b/drivers/block/Config.in index e3348db1b369..b5951af72379 100644 --- a/drivers/block/Config.in +++ b/drivers/block/Config.in @@ -138,6 +138,10 @@ fi if [ "$CONFIG_PCI" = "y" ]; then tristate 'Compaq SMART2 support' CONFIG_BLK_CPQ_DA fi +if [ "$CONFIG_PCI" = "y" ]; then + tristate 'Compaq CISS Array support' CONFIG_BLK_CPQ_CISS_DA +fi + if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then define_bool CONFIG_BLK_DEV_HD y diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 875c8ebe5aae..cd1ff13f44e0 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -246,6 +246,14 @@ else endif endif +ifeq ($(CONFIG_BLK_CPQ_CISS_DA),y) +L_OBJS += cciss.o +else + ifeq ($(CONFIG_BLK_CPQ_CISS_DA),m) + M_OBJS += cciss.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_DAC960),y) LX_OBJS += DAC960.o else diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c new file mode 100644 index 000000000000..37f2709d40b9 --- /dev/null +++ b/drivers/block/cciss.c @@ -0,0 +1,1934 @@ +/* + * Disk Array driver for Compaq SMART2 Controllers + * Copyright 2000 Compaq Computer Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Questions/Comments/Bugfixes to arrays@compaq.com + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define CCISS_DRIVER_VERSION(maj,min,submin) ((maj<<16)|(min<<8)|(submin)) +#define DRIVER_NAME "Compaq CISS Driver (v 1.0.0)" +#define DRIVER_VERSION CCISS_DRIVER_VERSION(1,0,0) + +/* Embedded module documentation macros - see modules.h */ +MODULE_AUTHOR("Charles M. White III - Compaq Computer Corporation"); +MODULE_DESCRIPTION("Driver for Compaq Smart Array Controller 5300"); + +#include "cciss_cmd.h" +#include "cciss.h" +#include + +#define NR_PRODUCTS (sizeof(products)/sizeof(struct board_type)) + +/* board_id = Subsystem Device ID & Vendor ID + * product = Marketing Name for the board + * access = Address of the struct of function pointers + */ +static struct board_type products[] = { + { 0x40700E11, "Smart Array 5300", &SA5_access }, +}; + +/* How long to wait (in millesconds) for board to go into simple mode */ +#define MAX_CONFIG_WAIT 1000 + +#define READ_AHEAD 128 +#define NR_CMDS 128 /* #commands that can be outstanding */ +#define MAX_CTLR 8 +static int nr_ctlr =0; +static ctlr_info_t *hba[MAX_CTLR] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + +static struct proc_dir_entry *proc_cciss = NULL; + +static void do_cciss_request(int i); +/* + * This is a hack. This driver eats a major number for each controller, and + * sets blkdev[xxx].request_fn to each one of these so the real request + * function knows what controller its working with. + */ +#define DO_CCISS_REQUEST(x) { do_cciss_request(x); } + +static void do_cciss_request0(void) DO_CCISS_REQUEST(0); +static void do_cciss_request1(void) DO_CCISS_REQUEST(1); +static void do_cciss_request2(void) DO_CCISS_REQUEST(2); +static void do_cciss_request3(void) DO_CCISS_REQUEST(3); +static void do_cciss_request4(void) DO_CCISS_REQUEST(4); +static void do_cciss_request5(void) DO_CCISS_REQUEST(5); +static void do_cciss_request6(void) DO_CCISS_REQUEST(6); +static void do_cciss_request7(void) DO_CCISS_REQUEST(7); + +static int cciss_open(struct inode *inode, struct file *filep); +static int cciss_release(struct inode *inode, struct file *filep); +static int cciss_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg); + +static int revalidate_allvol(kdev_t dev); +static int revalidate_logvol(kdev_t dev, int maxusage); +static int frevalidate_logvol(kdev_t dev); + +static void cciss_getgeometry(int cntl_num); + +static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c); +static void start_io( ctlr_info_t *h); + +static struct file_operations cciss_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + cciss_ioctl, /* ioctl */ + NULL, /* mmap */ + cciss_open, /* open code */ + NULL, + cciss_release, /* release */ + block_fsync, /* fsync */ + NULL, /* fasync */ + NULL, /* Disk change */ + frevalidate_logvol, /* revalidate */ +}; + +/* + * Report information about this controller. + */ +static int cciss_proc_get_info(char *buffer, char **start, off_t offset, int length, int *eof, void *data) +{ + off_t pos = 0; + off_t len = 0; + int size, i, ctlr; + ctlr_info_t *h = (ctlr_info_t*)data; + drive_info_struct *drv; + + ctlr = h->ctlr; + size = sprintf(buffer, "%s: Compaq %s Controller\n" + " Board ID: %08lx\n" + " Firmware Version: %c%c%c%c\n" + " Memory Address: %08lx\n" + " IRQ: 0x%x\n" + " Logical drives: %d\n" + " Current Q depth: %d\n" + " Current # commands on controller %d\n" + " Max Q depth since init: %d\n" + " Max # commands on controller since init: %d\n" + " Max SG entries since init: %d\n\n", + h->devname, + h->product_name, + (unsigned long)h->board_id, + h->firm_ver[0], h->firm_ver[1], h->firm_ver[2], h->firm_ver[3], + (unsigned long)h->vaddr, + (unsigned int)h->intr, + h->num_luns, + h->Qdepth, h->commands_outstanding, + h->maxQsinceinit, h->max_outstanding, h->maxSG); + + pos += size; len += size; + for(i=0; inum_luns; i++) { + drv = &h->drv[i]; + size = sprintf(buffer+len, "cciss/c%dd%d: blksz=%d nr_blocks=%d\n", + ctlr, i, drv->block_size, drv->nr_blocks); + pos += size; len += size; + } + + size = sprintf(buffer+len, "nr_allocs = %d\nnr_frees = %d\n", + h->nr_allocs, h->nr_frees); + pos += size; len += size; + + *eof = 1; + *start = buffer+offset; + len -= offset; + if (len>length) + len = length; + return len; +} + +/* + * Get us a file in /proc/cciss that says something about each controller. + * Create /proc/cciss if it doesn't exist yet. + */ +static void cciss_procinit(int i) +{ + struct proc_dir_entry *pd; + + if (proc_cciss == NULL) { + proc_cciss = create_proc_entry("cciss", S_IFDIR|S_IRUGO|S_IXUGO, &proc_root); + if (!proc_cciss) + return; + } + + pd = create_proc_entry(hba[i]->devname, S_IFREG|S_IRUGO, proc_cciss); + if (!pd) + return; + pd->read_proc = cciss_proc_get_info; + pd->data = hba[i]; +} + +/* + * For operations that cannot sleep, a command block is allocated at init, + * and managed by cmd_alloc() and cmd_free() using a simple bitmap to track + * which ones are free or in use. For operations that can wait for kmalloc + * to possible sleep, this routine can be called with a NULL pointer. + * cmd_free() MUST be called with a NULL pointer if cmd_alloc was. + */ +static CommandList_struct * cmd_alloc(ctlr_info_t *h) +{ + CommandList_struct *c; + int i; + u64bit temp64; + + if (h == NULL) + { + c = (CommandList_struct *)kmalloc(sizeof(CommandList_struct), + GFP_KERNEL); + if(c==NULL) + return NULL; + memset(c, 0, sizeof(CommandList_struct)); + + c->err_info = (ErrorInfo_struct *)kmalloc( + sizeof(ErrorInfo_struct), GFP_KERNEL); + + if (c->err_info == NULL) + { + kfree(c); + return NULL; + } + memset(c->err_info, 0, sizeof(ErrorInfo_struct)); + } else /* get it out of the controllers pool */ + { + do { + i = find_first_zero_bit(h->cmd_pool_bits, NR_CMDS); + if (i == NR_CMDS) + return NULL; + } while(test_and_set_bit(i%32, h->cmd_pool_bits+(i/32)) != 0); +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: using command buffer %d\n", i); +#endif + c = h->cmd_pool + i; + memset(c, 0, sizeof(CommandList_struct)); + c->err_info = h->errinfo_pool + i; + memset(c->err_info, 0, sizeof(ErrorInfo_struct)); + h->nr_allocs++; + } + + + temp64.val = (__u64) virt_to_bus(c->err_info); + c->ErrDesc.Addr.lower = temp64.val32.lower; + c->ErrDesc.Addr.upper = temp64.val32.upper; + c->ErrDesc.Len = sizeof(ErrorInfo_struct); + c->busaddr = virt_to_bus(c); + return c; + + +} + +/* + * Frees a command block that was previously allocated with cmd_alloc(). + */ +static void cmd_free(ctlr_info_t *h, CommandList_struct *c) +{ + int i; + + if( h == NULL) + { + kfree(c->err_info); + kfree(c); + } else + { + i = c - h->cmd_pool; + clear_bit(i%32, h->cmd_pool_bits+(i/32)); + h->nr_frees++; + } +} + +/* + * fills in the disk information. + */ +static void cciss_geninit( struct gendisk *g) +{ + int ctlr = -1; + drive_info_struct *drv; + int i,j; + + /* find the controller this gendisk data is from */ + for(i=0; i < nr_ctlr; i++) + { + if ( g == &(hba[i]->gendisk)) + { + ctlr = i; + break; + } + } + if ( ctlr < 0) + { + printk(KERN_WARNING "cciss: gendisk data invalid\n"); + return; + } + /* Loop through each real device */ + g->nr_real = 0; + for(i=0; i< NWD; i++) + { + drv = &(hba[ctlr]->drv[i]); + if( !(drv->nr_blocks)) + continue; + hba[ctlr]->hd[i << NWD_SHIFT].nr_sects = + hba[ctlr]->sizes[i << NWD_SHIFT] = drv->nr_blocks; + + /* for each partition */ + for(j=0; jblocksizes[(i<hardsizes[ (i<block_size; + } + g->nr_real++; + } +} +/* + * Open. Make sure the device is really there. + */ +static int cciss_open(struct inode *inode, struct file *filep) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_open %x (%x:%x)\n", inode->i_rdev, ctlr, dsk); +#endif /* CCISS_DEBUG */ + + if (ctlr > MAX_CTLR || hba[ctlr] == NULL) + return -ENXIO; + + if (!suser() && hba[ctlr]->sizes[ MINOR(inode->i_rdev)] == 0) + return -ENXIO; + + /* + * Root is allowed to open raw volume zero even if its not configured + * so array config can still work. I don't think I really like this, + * but I'm already using way to many device nodes to claim another one + * for "raw controller". + */ + if (suser() + && (hba[ctlr]->sizes[MINOR(inode->i_rdev)] == 0) + && (MINOR(inode->i_rdev)!= 0)) + return -ENXIO; + + hba[ctlr]->drv[dsk].usage_count++; + hba[ctlr]->usage_count++; + MOD_INC_USE_COUNT; + return 0; +} +/* + * Close. Sync first. + */ +static int cciss_release(struct inode *inode, struct file *filep) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_release %x (%x:%x)\n", inode->i_rdev, ctlr, dsk); +#endif /* CCISS_DEBUG */ + + fsync_dev(inode->i_rdev); + + hba[ctlr]->drv[dsk].usage_count--; + hba[ctlr]->usage_count--; + MOD_DEC_USE_COUNT; + return 0; +} + +/* + * ioctl + */ +static int cciss_ioctl(struct inode *inode, struct file *filep, + unsigned int cmd, unsigned long arg) +{ + int ctlr = MAJOR(inode->i_rdev) - MAJOR_NR; + int dsk = MINOR(inode->i_rdev) >> NWD_SHIFT; + int diskinfo[4]; + struct hd_geometry *geo = (struct hd_geometry *)arg; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss_ioctl: Called with cmd=%x %lx\n", cmd, arg); +#endif /* CCISS_DEBUG */ + + switch(cmd) { + case HDIO_GETGEO: + if (hba[ctlr]->drv[dsk].cylinders) { + diskinfo[0] = hba[ctlr]->drv[dsk].heads; + diskinfo[1] = hba[ctlr]->drv[dsk].sectors; + diskinfo[2] = hba[ctlr]->drv[dsk].cylinders; + } else { + diskinfo[0] = 0xff; + diskinfo[1] = 0x3f; + diskinfo[2] = hba[ctlr]->drv[dsk].nr_blocks / (0xff*0x3f); } + put_user(diskinfo[0], &geo->heads); + put_user(diskinfo[1], &geo->sectors); + put_user(diskinfo[2], &geo->cylinders); + put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].start_sect, &geo->start); + return 0; + case BLKGETSIZE: + if (!arg) return -EINVAL; + put_user(hba[ctlr]->hd[MINOR(inode->i_rdev)].nr_sects, (long*)arg); + return 0; + case BLKRASET: + if (!capable(CAP_SYS_ADMIN)) return -EACCES; + if (!(inode->i_rdev)) return -EINVAL; + if (arg>0xff) return -EINVAL; + read_ahead[MAJOR(inode->i_rdev)] = arg; + return 0; + case BLKRAGET: + if (!arg) return -EINVAL; + put_user(read_ahead[MAJOR(inode->i_rdev)], (int*)arg); + return 0; + case BLKRRPART: + return revalidate_logvol(inode->i_rdev, 1); + + case CCISS_GETPCIINFO: + { + cciss_pci_info_struct pciinfo; + + if (!arg) return -EINVAL; + pciinfo.bus = hba[ctlr]->pci_bus; + pciinfo.dev_fn = hba[ctlr]->pci_dev_fn; + pciinfo.board_id = hba[ctlr]->board_id; + copy_to_user_ret((void *) arg, &pciinfo, + sizeof( cciss_pci_info_struct), -EFAULT); + return(0); + } + case CCISS_GETINTINFO: + { + cciss_coalint_struct intinfo; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + intinfo.delay = readl(&c->cfgtable->HostWrite.CoalIntDelay); + intinfo.count = readl(&c->cfgtable->HostWrite.CoalIntCount); + copy_to_user_ret((void *) arg, &intinfo, + sizeof( cciss_coalint_struct), -EFAULT); + return(0); + } + case CCISS_SETINTINFO: + { + cciss_coalint_struct intinfo; + ctlr_info_t *c = hba[ctlr]; + unsigned long flags; + int i; + + if (!arg) return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + copy_from_user_ret(&intinfo, (void *) arg, + sizeof( cciss_coalint_struct), -EFAULT); + if ( (intinfo.delay == 0 ) && (intinfo.count == 0)) + + { +// printk("cciss_ioctl: delay and count cannot be 0\n"); + return( -EINVAL); + } + spin_lock_irqsave(&io_request_lock, flags); + /* Can only safely update if no commands outstanding */ + if (c->commands_outstanding > 0 ) + { +// printk("cciss_ioctl: cannot change coalasing " +// "%d commands outstanding on controller\n", +// c->commands_outstanding); + spin_unlock_irqrestore(&io_request_lock, flags); + return(-EINVAL); + } + /* Update the field, and then ring the doorbell */ + writel( intinfo.delay, + &(c->cfgtable->HostWrite.CoalIntDelay)); + writel( intinfo.count, + &(c->cfgtable->HostWrite.CoalIntCount)); + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) + & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + spin_unlock_irqrestore(&io_request_lock, flags); + if (i >= MAX_CONFIG_WAIT) + return( -EFAULT); + return(0); + } + case CCISS_GETNODENAME: + { + NodeName_type NodeName; + ctlr_info_t *c = hba[ctlr]; + int i; + + if (!arg) return -EINVAL; + for(i=0;i<16;i++) + NodeName[i] = readb(&c->cfgtable->ServerName[i]); + copy_to_user_ret((void *) arg, NodeName, + sizeof( NodeName_type), -EFAULT); + return(0); + } + case CCISS_SETNODENAME: + { + NodeName_type NodeName; + ctlr_info_t *c = hba[ctlr]; + unsigned long flags; + int i; + + if (!arg) return -EINVAL; + if (!capable(CAP_SYS_ADMIN)) return -EPERM; + + copy_from_user_ret(NodeName, (void *) arg, + sizeof( NodeName_type), -EFAULT); + + spin_lock_irqsave(&io_request_lock, flags); + + /* Update the field, and then ring the doorbell */ + for(i=0;i<16;i++) + writeb( NodeName[i], &c->cfgtable->ServerName[i]); + + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) + & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + spin_unlock_irqrestore(&io_request_lock, flags); + if (i >= MAX_CONFIG_WAIT) + return( -EFAULT); + return(0); + } + + case CCISS_GETHEARTBEAT: + { + Heartbeat_type heartbeat; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + heartbeat = readl(&c->cfgtable->HeartBeat); + copy_to_user_ret((void *) arg, &heartbeat, + sizeof( Heartbeat_type), -EFAULT); + return(0); + } + case CCISS_GETBUSTYPES: + { + BusTypes_type BusTypes; + ctlr_info_t *c = hba[ctlr]; + + if (!arg) return -EINVAL; + BusTypes = readl(&c->cfgtable->BusTypes); + copy_to_user_ret((void *) arg, &BusTypes, + sizeof( BusTypes_type), -EFAULT); + return(0); + } + case CCISS_GETFIRMVER: + { + FirmwareVer_type firmware; + + if (!arg) return -EINVAL; + memcpy(firmware, hba[ctlr]->firm_ver, 4); + + copy_to_user_ret((void *) arg, firmware, + sizeof( FirmwareVer_type), -EFAULT); + return(0); + } + case CCISS_GETDRIVVER: + { + DriverVer_type DriverVer = DRIVER_VERSION; + + if (!arg) return -EINVAL; + + copy_to_user_ret((void *) arg, &DriverVer, + sizeof( DriverVer_type), -EFAULT); + return(0); + } + + case CCISS_REVALIDVOLS: + return( revalidate_allvol(inode->i_rdev)); + + case CCISS_PASSTHRU: + { + IOCTL_Command_struct iocommand; + ctlr_info_t *h = hba[ctlr]; + CommandList_struct *c; + char *buff = NULL; + u64bit temp64; + unsigned long flags; + + if (!arg) return -EINVAL; + + if (!capable(CAP_SYS_RAWIO)) return -EPERM; + + copy_from_user_ret(&iocommand, (void *) arg, + sizeof( IOCTL_Command_struct), -EFAULT); + if((iocommand.buf_size < 1) && + (iocommand.Request.Type.Direction != XFER_NONE)) + { + return -EINVAL; + } + /* Check kmalloc limits */ + if(iocommand.buf_size > 128000) + return -EINVAL; + if(iocommand.buf_size > 0) + { + buff = kmalloc(iocommand.buf_size, GFP_KERNEL); + if( buff == NULL) + return -EFAULT; + } + if (iocommand.Request.Type.Direction == XFER_WRITE) + { + /* Copy the data into the buffer we created */ + copy_from_user_ret(buff, iocommand.buf, + iocommand.buf_size, -EFAULT); + } + if ((c = cmd_alloc(NULL)) == NULL) + { + if(buff!=NULL) + kfree(buff); + return -ENOMEM; + } + // Fill in the command type + c->cmd_type = CMD_IOCTL_PEND; + // Fill in Command Header + c->Header.ReplyQueue = 0; // unused in simple mode + if( iocommand.buf_size > 0) // buffer to fill + { + c->Header.SGList = 1; + c->Header.SGTotal= 1; + } else // no buffers to fill + { + c->Header.SGList = 0; + c->Header.SGTotal= 0; + } + c->Header.LUN = iocommand.LUN_info; + c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag + + // Fill in Request block + c->Request = iocommand.Request; + + // Fill in the scatter gather information + if (iocommand.buf_size > 0 ) + { + temp64.val = (__u64) virt_to_bus(buff); + c->SG[0].Addr.lower = temp64.val32.lower; + c->SG[0].Addr.upper = temp64.val32.upper; + c->SG[0].Len = iocommand.buf_size; + c->SG[0].Ext = 0; // we are not chaining + } + /* Put the request on the tail of the request queue */ + spin_lock_irqsave(&io_request_lock, flags); + addQ(&h->reqQ, c); + h->Qdepth++; + start_io(h); + spin_unlock_irqrestore(&io_request_lock, flags); + + /* Wait for completion */ + while(c->cmd_type != CMD_IOCTL_DONE) + { + schedule_timeout(1); + rmb(); + } + + /* Copy the error information out */ + iocommand.error_info = *(c->err_info); + if ( copy_to_user((void *) arg, &iocommand, + sizeof( IOCTL_Command_struct) ) < 0 ) + { + cmd_free(NULL, c); + if (buff != NULL) + kfree(buff); + return( -EFAULT); + } + + if (iocommand.Request.Type.Direction == XFER_READ) + { + /* Copy the data out of the buffer we created */ + if (copy_to_user(iocommand.buf, buff, + iocommand.buf_size) < 0) + { + cmd_free(NULL, c); + kfree(buff); + } + } + cmd_free(NULL, c); + if (buff != NULL) + kfree(buff); + return(0); + } + + RO_IOCTLS(inode->i_rdev, arg); + + default: + return -EBADRQC; + } + +} + +/* Borrowed and adapted from sd.c */ +static int revalidate_logvol(kdev_t dev, int maxusage) +{ + int ctlr, target; + struct gendisk *gdev; + unsigned long flags; + int max_p; + int start; + int i; + + target = MINOR(dev) >> NWD_SHIFT; + ctlr = MAJOR(dev) - MAJOR_NR; + gdev = &(hba[ctlr]->gendisk); + + spin_lock_irqsave(&io_request_lock, flags); + if (hba[ctlr]->drv[target].usage_count > maxusage) { + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_WARNING "cpqarray: Device busy for " + "revalidation (usage=%d)\n", + hba[ctlr]->drv[target].usage_count); + return -EBUSY; + } + hba[ctlr]->drv[target].usage_count++; + spin_unlock_irqrestore(&io_request_lock, flags); + + max_p = gdev->max_p; + start = target << gdev->minor_shift; + + for(i=max_p; i>=0; i--) { + int minor = start+i; + kdev_t devi = MKDEV(MAJOR_NR + ctlr, minor); + struct super_block *sb = get_super(devi); + sync_dev(devi); + if (sb) invalidate_inodes(sb); + invalidate_buffers(devi); + gdev->part[minor].start_sect = 0; + gdev->part[minor].nr_sects = 0; + + /* reset the blocksize so we can read the partition table */ + blksize_size[MAJOR_NR+ctlr][minor] = 1024; + } + gdev->part[start].nr_sects = hba[ctlr]->drv[target].nr_blocks; + resetup_one_dev(gdev, target); + hba[ctlr]->drv[target].usage_count--; + return 0; +} + +static int frevalidate_logvol(kdev_t dev) +{ +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: frevalidate has been called\n"); +#endif /* CCISS_DEBUG */ + return revalidate_logvol(dev, 0); +} + +/* + * revalidate_allvol is for online array config utilities. After a + * utility reconfigures the drives in the array, it can use this function + * (through an ioctl) to make the driver zap any previous disk structs for + * that controller and get new ones. + * + * Right now I'm using the getgeometry() function to do this, but this + * function should probably be finer grained and allow you to revalidate one + * particualar logical volume (instead of all of them on a particular + * controller). + */ +static int revalidate_allvol(kdev_t dev) +{ + int ctlr, i; + unsigned long flags; + + ctlr = MAJOR(dev) - MAJOR_NR; + if (MINOR(dev) != 0) + return -ENXIO; + + spin_lock_irqsave(&io_request_lock, flags); + if (hba[ctlr]->usage_count > 1) { + spin_unlock_irqrestore(&io_request_lock, flags); + printk(KERN_WARNING "cciss: Device busy for volume" + " revalidation (usage=%d)\n", hba[ctlr]->usage_count); + return -EBUSY; + } + spin_unlock_irqrestore(&io_request_lock, flags); + hba[ctlr]->usage_count++; + + /* + * Set the partition and block size structures for all volumes + * on this controller to zero. We will reread all of this data + */ + memset(hba[ctlr]->hd, 0, sizeof(struct hd_struct) * 256); + memset(hba[ctlr]->sizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->blocksizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->hardsizes, 0, sizeof(int) * 256); + memset(hba[ctlr]->drv, 0, sizeof(drive_info_struct) + * CISS_MAX_LUN); + hba[ctlr]->gendisk.nr_real = 0; + + /* + * Tell the array controller not to give us any interupts while + * we check the new geometry. Then turn interrupts back on when + * we're done. + */ + hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_OFF); + cciss_getgeometry(ctlr); + hba[ctlr]->access.set_intr_mask(hba[ctlr], CCISS_INTR_ON); + + cciss_geninit(&(hba[ctlr]->gendisk)); + for(i=0; isizes[ i<usage_count--; + return 0; +} + + + +/* + * Wait polling for a command to complete. + * The memory mapped FIFO is polled for the completion. + * Used only at init time, interrupts disabled. + */ +static unsigned long pollcomplete(int ctlr) +{ + unsigned long done; + int i; + + /* Wait (up to 2 seconds) for a command to complete */ + + for (i = 200000; i > 0; i--) { + done = hba[ctlr]->access.command_completed(hba[ctlr]); + if (done == FIFO_EMPTY) { + udelay(10); /* a short fixed delay */ + } else + return (done); + } + /* Invalid address to tell caller we ran out of time */ + return 1; +} +/* + * Send a command to the controller, and wait for it to complete. + * Only used at init time. + */ +static int sendcmd( + __u8 cmd, + int ctlr, + void *buff, + size_t size, + unsigned int use_unit_num, + unsigned int log_unit, + __u8 page_code ) +{ + CommandList_struct *c; + int i; + unsigned long complete; + ctlr_info_t *info_p= hba[ctlr]; + u64bit temp64; + + c = cmd_alloc(info_p); + if (c == NULL) + { + printk(KERN_WARNING "cciss: unable to get memory"); + return(IO_ERROR); + } + // Fill in Command Header + c->Header.ReplyQueue = 0; // unused in simple mode + if( buff != NULL) // buffer to fill + { + c->Header.SGList = 1; + c->Header.SGTotal= 1; + } else // no buffers to fill + { + c->Header.SGList = 0; + c->Header.SGTotal= 0; + } + c->Header.Tag.lower = c->busaddr; // use the kernel address the cmd block for tag + // Fill in Request block + switch(cmd) + { + case CISS_INQUIRY: + /* If the logical unit number is 0 then, this is going + to controller so It's a physical command + mode = 0 target = 0. + So we have nothing to write. + Otherwise + mode = 1 target = LUNID + */ + if(use_unit_num != 0) + { + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + } + /* are we trying to read a vital product page */ + if(page_code != 0) + { + c->Request.CDB[1] = 0x01; + c->Request.CDB[2] = page_code; + } + c->Request.CDBLen = 6; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_INQUIRY; + c->Request.CDB[4] = size & 0xFF; + break; + case CISS_REPORT_LOG: + /* Talking to controller so It's a physical command + mode = 00 target = 0. + So we have nothing to write. + */ + c->Request.CDBLen = 12; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CISS_REPORT_LOG; + c->Request.CDB[6] = (size >> 24) & 0xFF; //MSB + c->Request.CDB[7] = (size >> 16) & 0xFF; + c->Request.CDB[8] = (size >> 8) & 0xFF; + c->Request.CDB[9] = size & 0xFF; + break; + + case CCISS_READ_CAPACITY: + c->Header.LUN.LogDev.VolId= + hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + c->Request.CDBLen = 10; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = XFER_READ; // Read + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = CCISS_READ_CAPACITY; + break; + default: + printk(KERN_WARNING + "cciss: Unknown Command 0x%c sent attempted\n", + cmd); + cmd_free(info_p, c); + return(IO_ERROR); + }; + // Fill in the scatter gather information + if (size > 0 ) + { + temp64.val = (__u64) virt_to_bus(buff); + c->SG[0].Addr.lower = temp64.val32.lower; + c->SG[0].Addr.upper = temp64.val32.upper; + c->SG[0].Len = size; + c->SG[0].Ext = 0; // we are not chaining + } + /* + * Disable interrupt + */ +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: turning intr off\n"); +#endif /* CCISS_DEBUG */ + info_p->access.set_intr_mask(info_p, CCISS_INTR_OFF); + + /* Make sure there is room in the command FIFO */ + /* Actually it should be completely empty at this time. */ + for (i = 200000; i > 0; i--) + { + /* if fifo isn't full go */ + if (!(info_p->access.fifo_full(info_p))) + { + + break; + } + udelay(10); + printk(KERN_WARNING "cciss cciss%d: SendCmd FIFO full," + " waiting!\n", ctlr); + } + /* + * Send the cmd + */ + info_p->access.submit_command(info_p, c); + complete = pollcomplete(ctlr); + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: command completed\n"); +#endif /* CCISS_DEBUG */ + + if (complete != 1) { + if ( (complete & CISS_ERROR_BIT) + && (complete & ~CISS_ERROR_BIT) == c->busaddr) + { + /* if data overrun or underun on Report command + ignore it + */ + if (((c->Request.CDB[0] == CISS_REPORT_LOG) || + (c->Request.CDB[0] == CISS_INQUIRY)) && + ((c->err_info->CommandStatus == + CMD_DATA_OVERRUN) || + (c->err_info->CommandStatus == + CMD_DATA_UNDERRUN) + )) + { + complete = c->busaddr; + } else + { + printk(KERN_WARNING "ciss ciss%d: sendcmd" + " Error %x \n", ctlr, + c->err_info->CommandStatus); + printk(KERN_WARNING "ciss ciss%d: sendcmd" + " offensive info\n" + " size %x\n num %x value %x\n", ctlr, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_size, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_num, + c->err_info->MoreErrInfo.Invalid_Cmd.offense_value); + cmd_free(info_p,c); + return(IO_ERROR); + } + } + if (complete != c->busaddr) { + printk( KERN_WARNING "cciss cciss%d: SendCmd " + "Invalid command list address returned! (%lx)\n", + ctlr, complete); + cmd_free(info_p, c); + return (IO_ERROR); + } + } else { + printk( KERN_WARNING + "cciss cciss%d: SendCmd Timeout out, " + "No command list address returned!\n", + ctlr); + cmd_free(info_p, c); + return (IO_ERROR); + } + cmd_free(info_p, c); + return (IO_OK); +} +/* + * Map (physical) PCI mem into (virtual) kernel space + */ +static ulong remap_pci_mem(ulong base, ulong size) +{ + ulong page_base = ((ulong) base) & PAGE_MASK; + ulong page_offs = ((ulong) base) - page_base; + ulong page_remapped = (ulong) ioremap(page_base, page_offs+size); + + return (ulong) (page_remapped ? (page_remapped + page_offs) : 0UL); +} + +/* + * Enqueuing and dequeuing functions for cmdlists. + */ +static inline void addQ(CommandList_struct **Qptr, CommandList_struct *c) +{ + if (*Qptr == NULL) { + *Qptr = c; + c->next = c->prev = c; + } else { + c->prev = (*Qptr)->prev; + c->next = (*Qptr); + (*Qptr)->prev->next = c; + (*Qptr)->prev = c; + } +} + +static inline CommandList_struct *removeQ(CommandList_struct **Qptr, + CommandList_struct *c) +{ + if (c && c->next != c) { + if (*Qptr == c) *Qptr = c->next; + c->prev->next = c->next; + c->next->prev = c->prev; + } else { + *Qptr = NULL; + } + return c; +} + +/* + * Takes jobs of the Q and sends them to the hardware, then puts it on + * the Q to wait for completion. + */ +static void start_io( ctlr_info_t *h) +{ + CommandList_struct *c; + + while(( c = h->reqQ) != NULL ) + { + /* can't do anything if fifo is full */ + if ((h->access.fifo_full(h))) + { + printk(KERN_WARNING "cciss: fifo full \n"); + return; + } + /* Get the frist entry from the Request Q */ + removeQ(&(h->reqQ), c); + h->Qdepth--; + + /* Tell the controller execute command */ + h->access.submit_command(h, c); + + /* Put job onto the completed Q */ + addQ (&(h->cmpQ), c); + } +} + +static inline void complete_buffers( struct buffer_head *bh, int status) +{ + struct buffer_head *xbh; + + while(bh) + { + xbh = bh->b_reqnext; + bh->b_reqnext = NULL; + bh->b_end_io(bh, status); + bh = xbh; + } +} +/* checks the status of the job and calls complete buffers to mark all + * buffers for the completed job. + */ +static inline void complete_command( CommandList_struct *cmd, int timeout) +{ + int status = 1; + + if (timeout) + status = 0; + if(cmd->err_info->CommandStatus != 0) + { /* an error has occured */ + switch(cmd->err_info->CommandStatus) + { + case CMD_TARGET_STATUS: + printk(KERN_WARNING "cciss: cmd %p has " + " completed with errors\n", cmd); + if( cmd->err_info->ScsiStatus) + { + printk(KERN_WARNING "cciss: cmd %p " + "has SCSI Status = %x\n", + cmd, + cmd->err_info->ScsiStatus); + } + + break; + case CMD_DATA_UNDERRUN: + printk(KERN_WARNING "cciss: cmd %p has" + " completed with data underrun " + "reported\n", cmd); + break; + case CMD_DATA_OVERRUN: + printk(KERN_WARNING "cciss: cmd %p has" + " completed with data overrun " + "reported\n", cmd); + break; + case CMD_INVALID: + printk(KERN_WARNING "cciss: cmd %p is " + "reported invalid\n", cmd); + status = 0; + break; + case CMD_PROTOCOL_ERR: + printk(KERN_WARNING "cciss: cmd %p has " + "protocol error \n", cmd); + status = 0; + break; + case CMD_HARDWARE_ERR: + printk(KERN_WARNING "cciss: cmd %p had " + " hardware error\n", cmd); + status = 0; + break; + case CMD_CONNECTION_LOST: + printk(KERN_WARNING "cciss: cmd %p had " + "connection lost\n", cmd); + status=0; + break; + case CMD_ABORTED: + printk(KERN_WARNING "cciss: cmd %p was " + "aborted\n", cmd); + status=0; + break; + case CMD_ABORT_FAILED: + printk(KERN_WARNING "cciss: cmd %p reports " + "abort failed\n", cmd); + status=0; + break; + case CMD_UNSOLICITED_ABORT: + printk(KERN_WARNING "cciss: cmd %p aborted " + "do to an unsolicited abort\n", cmd); + status=0; + break; + case CMD_TIMEOUT: + printk(KERN_WARNING "cciss: cmd %p timedout\n", + cmd); + status=0; + break; + default: + printk(KERN_WARNING "cciss: cmd %p returned " + "unknown status %x\n", cmd, + cmd->err_info->CommandStatus); + status=0; + } + } + complete_buffers(cmd->bh, status); +} +/* + * Get a request and submit it to the controller. + * Currently we do one request at a time. Ideally we would like to send + * everything to the controller on the first call, but there is a danger + * of holding the io_request_lock for to long. + */ +static void do_cciss_request(int ctlr) +{ + ctlr_info_t *h= hba[ctlr]; + CommandList_struct *c; + int log_unit, start_blk, seg, sect; + char *lastdataend; + struct buffer_head *bh; + struct request *creq; + u64bit temp64; + + /* loop till creq is null or current_request is inactive */ + creq = blk_dev[MAJOR_NR+ctlr].current_request; + if ((creq == NULL) || (creq->rq_status == RQ_INACTIVE)) + { + /* nothing to do... restart processing and return */ + start_io(h); + return; + } + if ((ctlr != (MAJOR(creq->rq_dev)-MAJOR_NR)) || (ctlr > nr_ctlr) + || (h == NULL)) + { +#ifdef CCISS_DEBUG + printk(KERN_WARNING "cciss: doreq cmd of %d, %x at %p\n", + ctlr, creq->rq_dev, creq); +#endif /* CCISS_DEBUG */ + complete_buffers(creq->bh, 0); + return; + } + if (( c = cmd_alloc(h)) == NULL) + { + start_io(h); + return; + } + c->cmd_type = CMD_RWREQ; + bh = c->bh = creq->bh; + + /* fill in the request */ + log_unit = MINOR(creq->rq_dev) >> NWD_SHIFT; + c->Header.ReplyQueue = 0; // unused in simple mode + c->Header.Tag.lower = c->busaddr; // use the physical address the cmd block for tag + c->Header.LUN.LogDev.VolId= hba[ctlr]->drv[log_unit].LunID; + c->Header.LUN.LogDev.Mode = 1; + c->Request.CDBLen = 10; // 12 byte commands not in FW yet; + c->Request.Type.Type = TYPE_CMD; // It is a command. + c->Request.Type.Attribute = ATTR_SIMPLE; + c->Request.Type.Direction = + (creq->cmd == READ) ? XFER_READ: XFER_WRITE; + c->Request.Timeout = 0; // Don't time out + c->Request.CDB[0] = (creq->cmd == READ) ? CCISS_READ : CCISS_WRITE; + start_blk = hba[ctlr]->hd[MINOR(creq->rq_dev)].start_sect + creq->sector; + if (bh == NULL) + panic("cciss: bh== NULL?"); +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n",(int) creq->sector, + (int) creq->nr_sectors); +#endif /* CCISS_DEBUG */ + seg = 0; + lastdataend = NULL; + sect = 0; + while(bh) + { + sect += bh->b_size/512; + if (bh->b_size % 512) + { + printk(KERN_CRIT "cciss: Oh Man. %d+%d, size=%d\n", + (int) creq->sector, sect, (int) bh->b_size); + panic("b_size 512 != 0\n"); + } + if (bh->b_data == lastdataend) + { // tack it on to the last segment + c->SG[seg-1].Len +=bh->b_size; + lastdataend += bh->b_size; + } else + { + c->SG[seg].Len = bh->b_size; + temp64.val = (__u64) virt_to_bus(bh->b_data); + c->SG[seg].Addr.lower = temp64.val32.lower; + c->SG[seg].Addr.upper = temp64.val32.upper; + c->SG[0].Ext = 0; // we are not chaining + lastdataend = bh->b_data + bh->b_size; + if( ++seg == MAXSGENTRIES) + { + break; + } + } + bh = bh->b_reqnext; + } + /* track how many SG entries we are using */ + if( seg > h->maxSG) + h->maxSG = seg; + + /* adjusting the remaining request, if any */ + creq-> sector+= sect; + creq->nr_sectors -= sect; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: Submitting %d sectors in %d segments\n", sect, seg); +#endif /* CCISS_DEBUG */ + + c->Header.SGList = c->Header.SGTotal = seg; + c->Request.CDB[1]= 0; + c->Request.CDB[2]= (start_blk >> 24) & 0xff; //MSB + c->Request.CDB[3]= (start_blk >> 16) & 0xff; + c->Request.CDB[4]= (start_blk >> 8) & 0xff; + c->Request.CDB[5]= start_blk & 0xff; + c->Request.CDB[6]= 0; // (sect >> 24) & 0xff; MSB + // c->Request.CDB[7]= (sect >> 16) & 0xff; + c->Request.CDB[7]= (sect >> 8) & 0xff; + c->Request.CDB[8]= sect & 0xff; + c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0; + + /* check to see if we going to complete the entire request */ + /* if so, mark this request as Done and ready the next one */ + if (creq->nr_sectors) + { +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: More to do on the same request %p %ld\n", + creq, creq->nr_sectors); +#endif /* CCISS_DEBUG */ + + creq->bh = bh->b_reqnext; + bh->b_reqnext = NULL; + } else + { +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "cciss: Done with %p, queueing %p\n", creq, creq->next); +#endif /* CCISS_DEBUG */ + + creq->rq_status = RQ_INACTIVE; + blk_dev[MAJOR_NR+ctlr].current_request = creq->next; + wake_up(&wait_for_request); + } + addQ(&(h->reqQ),c); + h->Qdepth++; + if(h->Qdepth > h->maxQsinceinit) + h->maxQsinceinit = h->Qdepth; + start_io(h); +} + +static void do_cciss_intr(int irq, void *dev_id, struct pt_regs *regs) +{ + ctlr_info_t *h = dev_id; + CommandList_struct *c; + unsigned long flags; + __u32 a, a1; + + + /* Is this interrupt for us? */ + if ( h->access.intr_pending(h) == 0) + return; + + /* + * If there are completed commands in the completion queue, + * we had better do something about it. + */ + spin_lock_irqsave(&io_request_lock, flags); + while( h->access.intr_pending(h)) + { + while((a = h->access.command_completed(h)) != FIFO_EMPTY) + { + a1 = a; + a &= ~3; + if ((c = h->cmpQ) == NULL) + { + printk(KERN_WARNING "cpqarray: Completion of %08lx ignored\n", (unsigned long)a1); + continue; + } + while(c->busaddr != a) { + c = c->next; + if (c == h->cmpQ) + break; + } + /* + * If we've found the command, take it off the + * completion Q and free it + */ + if (c->busaddr == a) { + removeQ(&h->cmpQ, c); + if (c->cmd_type == CMD_RWREQ) { + complete_command(c, 0); + cmd_free(h, c); + } else if (c->cmd_type == CMD_IOCTL_PEND) { + c->cmd_type = CMD_IOCTL_DONE; + } + continue; + } + } + } + /* + * See if we can queue up some more IO + */ + do_cciss_request(h->ctlr); + spin_unlock_irqrestore(&io_request_lock, flags); +} +/* + * We cannot read the structure directly, for portablity we must use + * the io functions. + * This is for debug only. + */ +#ifdef CCISS_DEBUG +static void print_cfg_table( CfgTable_struct *tb) +{ + int i; + char temp_name[17]; + + printk("Controller Configuration information\n"); + printk("------------------------------------\n"); + for(i=0;i<4;i++) + temp_name[i] = readb(&(tb->Signature[i])); + temp_name[4]='\0'; + printk(" Signature = %s\n", temp_name); + printk(" Spec Number = %d\n", readl(&(tb->SpecValence))); + printk(" Transport methods supported = 0x%x\n", + readl(&(tb-> TransportSupport))); + printk(" Transport methods active = 0x%x\n", + readl(&(tb->TransportActive))); + printk(" Requested transport Method = 0x%x\n", + readl(&(tb->HostWrite.TransportRequest))); + printk(" Coalese Interrupt Delay = 0x%x\n", + readl(&(tb->HostWrite.CoalIntDelay))); + printk(" Coalese Interrupt Count = 0x%x\n", + readl(&(tb->HostWrite.CoalIntCount))); + printk(" Max outstanding commands = 0x%d\n", + readl(&(tb->CmdsOutMax))); + printk(" Bus Types = 0x%x\n", readl(&(tb-> BusTypes))); + for(i=0;i<16;i++) + temp_name[i] = readb(&(tb->ServerName[i])); + temp_name[16] = '\0'; + printk(" Server Name = %s\n", temp_name); + printk(" Heartbeat Counter = 0x%x\n\n\n", + readl(&(tb->HeartBeat))); +} +#endif /* CCISS_DEBUG */ + +static int cciss_pci_init(ctlr_info_t *c, unchar bus, unchar device_fn) +{ + ushort vendor_id, device_id, command; + unchar cache_line_size, latency_timer; + unchar irq, revision; + uint addr[6]; + __u32 board_id; + struct pci_dev *pdev; + + int i; + + pdev = pci_find_slot(bus, device_fn); + vendor_id = pdev->vendor; + device_id = pdev->device; + irq = pdev->irq; + + for(i=0; i<6; i++) + addr[i] = pdev->base_address[i]; + (void) pcibios_read_config_word(bus, device_fn, + PCI_COMMAND,&command); + (void) pcibios_read_config_byte(bus, device_fn, + PCI_CLASS_REVISION,&revision); + (void) pcibios_read_config_byte(bus, device_fn, + PCI_CACHE_LINE_SIZE, &cache_line_size); + (void) pcibios_read_config_byte(bus, device_fn, + PCI_LATENCY_TIMER, &latency_timer); + + (void) pcibios_read_config_dword(bus, device_fn, 0x2c, &board_id); + +#ifdef CCISS_DEBUG + printk("vendor_id = %x\n", vendor_id); + printk("device_id = %x\n", device_id); + printk("command = %x\n", command); + for(i=0; i<6; i++) + printk("addr[%d] = %x\n", i, addr[i]); + printk("revision = %x\n", revision); + printk("irq = %x\n", irq); + printk("cache_line_size = %x\n", cache_line_size); + printk("latency_timer = %x\n", latency_timer); + printk("board_id = %x\n", board_id); +#endif /* CCISS_DEBUG */ + + c->intr = irq; + + /* + * Memory base addr is first addr , the second points to the config + * table + */ + c->paddr = addr[0]; + c->vaddr = remap_pci_mem(c->paddr, 128); + c->cfgtable = (CfgTable_struct *) remap_pci_mem(addr[1], + sizeof(CfgTable_struct)); + c->board_id = board_id; + +#ifdef CCISS_DEBUG + print_cfg_table(c->cfgtable); +#endif /* CCISS_DEBUG */ + for(i=0; iproduct_name = products[i].product_name; + c->access = *(products[i].access); + break; + } + } + if (i == NR_PRODUCTS) { + printk(KERN_WARNING "cciss: Sorry, I don't know how" + " to access the Smart Array controller %08lx\n", + (unsigned long)board_id); + return -1; + } +#ifdef CCISS_DEBUG + printk("Trying to put board into Simple mode\n"); +#endif /* CCISS_DEBUG */ + c->max_commands = readl(&(c->cfgtable->CmdsOutMax)); + /* Update the field, and then ring the doorbell */ + writel( CFGTBL_Trans_Simple, + &(c->cfgtable->HostWrite.TransportRequest)); + writel( CFGTBL_ChangeReq, c->vaddr + SA5_DOORBELL); + + for(i=0;ivaddr + SA5_DOORBELL) & CFGTBL_ChangeReq)) + break; + /* delay and try again */ + udelay(1000); + } + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "I counter got to %d %x\n", i, readl(c->vaddr + SA5_DOORBELL)); +#endif /* CCISS_DEBUG */ +#ifdef CCISS_DEBUG + print_cfg_table(c->cfgtable); +#endif /* CCISS_DEBUG */ + + if (!(readl(&(c->cfgtable->TransportActive)) & CFGTBL_Trans_Simple)) + { + printk(KERN_WARNING "cciss: unable to get board into" + " simple mode\n"); + return -1; + } + return 0; + +} +/* + * Scans PCI space for any controllers that this driver can control. + */ +static int cciss_pci_detect(void) +{ + + int index; + unchar bus=0, dev_fn=0; + + for(index=0; ; index++) { + if (pcibios_find_device(PCI_VENDOR_ID_COMPAQ, + PCI_DEVICE_ID_COMPAQ_CISS, + index, &bus, &dev_fn)) + break; + printk(KERN_DEBUG "cciss: Device %x has been found at %x %x\n", + PCI_DEVICE_ID_COMPAQ_CISS, bus, dev_fn); + if (index == 1000000) break; + if (nr_ctlr == 8) { + printk(KERN_WARNING "cciss: This driver" + " supports a maximum of 8 controllers.\n"); + break; + } + hba[nr_ctlr] = kmalloc(sizeof(ctlr_info_t), GFP_KERNEL); + if(hba[nr_ctlr]==NULL) + { + printk(KERN_ERR "cciss: out of memory.\n"); + continue; + } + memset(hba[nr_ctlr], 0, sizeof(ctlr_info_t)); + if (cciss_pci_init(hba[nr_ctlr], bus, dev_fn) != 0) + { + kfree(hba[nr_ctlr]); + continue; + } + sprintf(hba[nr_ctlr]->devname, "cciss%d", nr_ctlr); + hba[nr_ctlr]->ctlr = nr_ctlr; + hba[nr_ctlr]->pci_bus = bus; + hba[nr_ctlr]->pci_dev_fn = dev_fn; + nr_ctlr++; + + } + return nr_ctlr; + +} + +/* + * Gets information about the local volumes attached to the controller. + */ +static void cciss_getgeometry(int cntl_num) +{ + ReportLunData_struct *ld_buff; + ReadCapdata_struct *size_buff; + InquiryData_struct *inq_buff; + int return_code; + int i; + int listlength = 0; + int lunid = 0; + int block_size; + int total_size; + + ld_buff = kmalloc(sizeof(ReportLunData_struct), GFP_KERNEL); + if (ld_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + return; + } + memset(ld_buff, 0, sizeof(ReportLunData_struct)); + size_buff = kmalloc(sizeof( ReadCapdata_struct), GFP_KERNEL); + if (size_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + return; + } + inq_buff = kmalloc(sizeof( InquiryData_struct), GFP_KERNEL); + if (inq_buff == NULL) + { + printk(KERN_ERR "cciss: out of memory\n"); + kfree(ld_buff); + kfree(size_buff); + return; + } + /* Get the firmware version */ + return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, + sizeof(InquiryData_struct), 0, 0 ,0 ); + if (return_code == IO_OK) + { + hba[cntl_num]->firm_ver[0] = inq_buff->data_byte[32]; + hba[cntl_num]->firm_ver[1] = inq_buff->data_byte[33]; + hba[cntl_num]->firm_ver[2] = inq_buff->data_byte[34]; + hba[cntl_num]->firm_ver[3] = inq_buff->data_byte[35]; + } else /* send command failed */ + { + printk(KERN_WARNING "cciss: unable to determine firmware" + " version of controller\n"); + } + /* Get the number of logical volumes */ + return_code = sendcmd(CISS_REPORT_LOG, cntl_num, ld_buff, + sizeof(ReportLunData_struct), 0, 0, 0 ); + + if( return_code == IO_OK) + { +#ifdef CCISS_DEBUG + printk("LUN Data\n--------------------------\n"); +#endif /* CCISS_DEBUG */ + + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[0])) << 24; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[1])) << 16; + listlength |= (0xff & (unsigned int)(ld_buff->LUNListLength[2])) << 8; + listlength |= 0xff & (unsigned int)(ld_buff->LUNListLength[3]); + } else /* reading number of logical volumes failed */ + { + printk(KERN_WARNING "cciss: report logical volume" + " command failed\n"); + listlength = 0; + } + hba[cntl_num]->num_luns = listlength / 8; // 8 bytes pre entry + if (hba[cntl_num]->num_luns > CISS_MAX_LUN) + { + printk(KERN_ERR "ciss: only %d number of logical volumes supported\n", + CISS_MAX_LUN); + hba[cntl_num]->num_luns = CISS_MAX_LUN; + } +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Length = %x %x %x %x = %d\n", ld_buff->LUNListLength[0], + ld_buff->LUNListLength[1], ld_buff->LUNListLength[2], + ld_buff->LUNListLength[3], hba[cntl_num]->num_luns); +#endif /* CCISS_DEBUG */ + for(i=0; i< hba[cntl_num]->num_luns ; i++) + { + lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3])) << 24; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][2])) << 16; + lunid |= (0xff & (unsigned int)(ld_buff->LUN[i][1])) << 8; + lunid |= 0xff & (unsigned int)(ld_buff->LUN[i][0]); + hba[cntl_num]->drv[i].LunID = lunid; + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "LUN[%d]: %x %x %x %x = %x\n", i, + ld_buff->LUN[i][0], ld_buff->LUN[i][1],ld_buff->LUN[i][2], + ld_buff->LUN[i][3], hba[cntl_num]->drv[i].LunID); +#endif /* CCISS_DEBUG */ + + memset(size_buff, 0, sizeof(ReadCapdata_struct)); + return_code = sendcmd(CCISS_READ_CAPACITY, cntl_num, size_buff, + sizeof( ReadCapdata_struct), 1, i, 0 ); + if (return_code == IO_OK) + { + total_size = (0xff & + (unsigned int)(size_buff->total_size[0])) << 24; + total_size |= (0xff & + (unsigned int)(size_buff->total_size[1])) << 16; + total_size |= (0xff & + (unsigned int)(size_buff->total_size[2])) << 8; + total_size |= (0xff & (unsigned int) + (size_buff->total_size[3])); + total_size++; // command returns highest block address + + block_size = (0xff & + (unsigned int)(size_buff->block_size[0])) << 24; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[1])) << 16; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[2])) << 8; + block_size |= (0xff & + (unsigned int)(size_buff->block_size[3])); + } else /* read capacity command failed */ + { + printk(KERN_WARNING "cciss: read capacity failed\n"); + total_size = block_size = 0; + } + printk(" blocks= %d block_size= %d\n", total_size, + block_size); + + /* Execute the command to read the disk geometry */ + memset(inq_buff, 0, sizeof(InquiryData_struct)); + return_code = sendcmd(CISS_INQUIRY, cntl_num, inq_buff, + sizeof(InquiryData_struct), 1, i ,0xC1 ); + if (return_code == IO_OK) + { + if(inq_buff->data_byte[8] == 0xFF) + { + printk(KERN_WARNING "cciss: reading geometry failed, volume does not support reading geometry\n"); + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = 255; + hba[cntl_num]->drv[i].sectors = 32; // Sectors per track + hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; } else + { + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = + inq_buff->data_byte[6]; + hba[cntl_num]->drv[i].sectors = + inq_buff->data_byte[7]; + hba[cntl_num]->drv[i].cylinders = + (inq_buff->data_byte[4] & 0xff) << 8; + hba[cntl_num]->drv[i].cylinders += + inq_buff->data_byte[5]; + } + } + else /* Get geometry failed */ + { + printk(KERN_WARNING "cciss: reading geometry failed, continuing with default geometry\n"); + + hba[cntl_num]->drv[i].block_size = block_size; + hba[cntl_num]->drv[i].nr_blocks = total_size; + hba[cntl_num]->drv[i].heads = 255; + hba[cntl_num]->drv[i].sectors = 32; // Sectors per track + hba[cntl_num]->drv[i].cylinders = total_size / 255 / 32; + } + printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n", + hba[cntl_num]->drv[i].heads, + hba[cntl_num]->drv[i].sectors, + hba[cntl_num]->drv[i].cylinders); + + } + kfree(ld_buff); + kfree(size_buff); +} + +/* + * This is it. Find all the controllers and register them. I really hate + * stealing all these major device numbers. + * returns the number of block devices registered. + */ +int cciss_init(void) +{ + int num_cntlrs_reg = 0; + int i; + + void (*request_fns[MAX_CTLR])(void) = { + do_cciss_request0, do_cciss_request1, + do_cciss_request2, do_cciss_request3, + do_cciss_request4, do_cciss_request5, + do_cciss_request6, do_cciss_request7, + }; + + /* detect controllers */ + cciss_pci_detect(); + + if (nr_ctlr == 0) + return(num_cntlrs_reg); + + printk(KERN_INFO DRIVER_NAME "\n"); + printk(KERN_INFO "Found %d controller(s)\n", nr_ctlr); + for(i=0;idevname, &cciss_fops)) + { + printk(KERN_ERR "cciss: Unable to get major number " + "%d for %s\n", MAJOR_NR+i, hba[i]->devname); + continue; + } + /* make sure the board interrupts are off */ + hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_OFF); + if( request_irq(hba[i]->intr, do_cciss_intr, SA_INTERRUPT|SA_SHIRQ, hba[i]->devname, hba[i])) + { + printk(KERN_ERR "ciss: Unable to get irq %d for %s\n", + hba[i]->intr, hba[i]->devname); + unregister_blkdev( MAJOR_NR+i, hba[i]->devname); + continue; + } + num_cntlrs_reg++; + hba[i]->cmd_pool_bits = (__u32*)kmalloc( + ((NR_CMDS+31)/32)*sizeof(__u32), GFP_KERNEL); + hba[i]->cmd_pool = (CommandList_struct *)kmalloc( + NR_CMDS * sizeof(CommandList_struct), + GFP_KERNEL); + hba[i]->errinfo_pool = (ErrorInfo_struct *)kmalloc( + NR_CMDS * sizeof( ErrorInfo_struct), + GFP_KERNEL); + if((hba[i]->cmd_pool_bits == NULL) + || (hba[i]->cmd_pool == NULL) + || (hba[i]->errinfo_pool == NULL)) + { + nr_ctlr = i; + if(hba[i]->cmd_pool_bits) + kfree(hba[i]->cmd_pool_bits); + if(hba[i]->cmd_pool) + kfree(hba[i]->cmd_pool); + if(hba[i]->errinfo_pool) + kfree(hba[i]->errinfo_pool); + free_irq(hba[i]->intr, hba[i]); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + num_cntlrs_reg--; + printk( KERN_ERR "cciss: out of memory"); + return(num_cntlrs_reg); + } + + /* command and error info recs zeroed out before + they are used */ + memset(hba[i]->cmd_pool_bits, 0, + ((NR_CMDS+31)/32)*sizeof(__u32)); + +#ifdef CCISS_DEBUG + printk(KERN_DEBUG "Scanning for drives on controller cciss%d\n",i); +#endif /* CCISS_DEBUG */ + + cciss_getgeometry(i); + + /* Turn the interrupts on so we can service requests */ + hba[i]->access.set_intr_mask(hba[i], CCISS_INTR_ON); + + cciss_procinit(i); + /* Fill in the gendisk data */ + hba[i]->gendisk.major = MAJOR_NR + i; + hba[i]->gendisk.major_name = "cciss"; + hba[i]->gendisk.minor_shift = NWD_SHIFT; + hba[i]->gendisk.max_p = MAX_PART; + hba[i]->gendisk.max_nr = NWD; + hba[i]->gendisk.init = cciss_geninit; + hba[i]->gendisk.part = hba[i]->hd; + hba[i]->gendisk.sizes = hba[i]->sizes; + hba[i]->gendisk.nr_real = hba[i]->num_luns; + + /* fill in the other Kernel structs */ + blk_dev[MAJOR_NR+i].request_fn = request_fns[i]; + blksize_size[MAJOR_NR+i] = hba[i]->blocksizes; + hardsect_size[MAJOR_NR+i] = hba[i]->hardsizes; + read_ahead[MAJOR_NR+i] = READ_AHEAD; + + /* Get on the disk list */ + hba[i]->gendisk.next = gendisk_head; + gendisk_head = &(hba[i]->gendisk); + } + return(nr_ctlr); +} + +#ifdef MODULE +EXPORT_NO_SYMBOLS; + + +/* This is a bit of a hack... */ +int init_module(void) +{ + int i,j; + + if (cciss_init() == 0) /* all the block dev numbers already used */ + return -EIO; /* or no controllers were found */ + for(i=0; igendisk)); + for(j=0; jsizes[j<gendisk), j); + } + } + return 0; +} +void cleanup_module(void) +{ + int i; + struct gendisk *g; + + for(i=0; iaccess.set_intr_mask(hba[i], CCISS_INTR_OFF); + free_irq(hba[i]->intr, hba[i]); + iounmap((void*)hba[i]->vaddr); + unregister_blkdev(MAJOR_NR+i, hba[i]->devname); + remove_proc_entry(hba[i]->devname, proc_cciss); + + /* remove it from the disk list */ + if (gendisk_head == &(hba[i]->gendisk)) + { + gendisk_head = hba[i]->gendisk.next; + } else + { + for(g=gendisk_head; g ; g=g->next) + { + if(g->next == &(hba[i]->gendisk)) + { + g->next = hba[i]->gendisk.next; + } + } + } + remove_proc_entry("cciss", &proc_root); + kfree(hba[i]->cmd_pool); + kfree(hba[i]->errinfo_pool); + kfree(hba[i]->cmd_pool_bits); + kfree(hba[i]); + } +} + +#endif /* MODULE */ + diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h new file mode 100644 index 000000000000..1053aefd3ae1 --- /dev/null +++ b/drivers/block/cciss.h @@ -0,0 +1,201 @@ +#ifndef CCISS_H +#define CCISS_H + +#include + +#include "cciss_cmd.h" + + +#define NWD 16 +#define NWD_SHIFT 4 +#define MAX_PART 16 + +#define IO_OK 0 +#define IO_ERROR 1 + +#define MAJOR_NR COMPAQ_CISS_MAJOR + +struct ctlr_info; +typedef struct ctlr_info ctlr_info_t; + +struct access_method { + void (*submit_command)(ctlr_info_t *h, CommandList_struct *c); + void (*set_intr_mask)(ctlr_info_t *h, unsigned long val); + unsigned long (*fifo_full)(ctlr_info_t *h); + unsigned long (*intr_pending)(ctlr_info_t *h); + unsigned long (*command_completed)(ctlr_info_t *h); +}; +typedef struct _drive_info_struct +{ + __u32 LunID; + int usage_count; + int nr_blocks; + int block_size; + int heads; + int sectors; + int cylinders; +} drive_info_struct; + +struct ctlr_info +{ + int ctlr; + char devname[8]; + char *product_name; + char firm_ver[4]; // Firmware version + unchar pci_bus; + unchar pci_dev_fn; + __u32 board_id; + __u32 vaddr; + __u32 paddr; + CfgTable_struct *cfgtable; + int intr; + + int max_commands; + int commands_outstanding; + int max_outstanding; /* Debug */ + int num_luns; + int usage_count; /* number of opens all all minor devices */ + + // information about each logical volume + drive_info_struct drv[CISS_MAX_LUN]; + + struct access_method access; + + /* queue and queue Info */ + CommandList_struct *reqQ; + CommandList_struct *cmpQ; + unsigned int Qdepth; + unsigned int maxQsinceinit; + unsigned int maxSG; + + //* pointers to command and error info pool */ + CommandList_struct *cmd_pool; + ErrorInfo_struct *errinfo_pool; + __u32 *cmd_pool_bits; + int nr_allocs; + int nr_frees; + + // Disk structures we need to pass back + struct gendisk gendisk; + // indexed by minor numbers + struct hd_struct hd[256]; + int sizes[256]; + int blocksizes[256]; + int hardsizes[256]; +}; + +/* Defining the diffent access_menthods */ +/* + * Memory mapped FIFO interface (SMART 53xx cards) + */ +#define SA5_DOORBELL 0x20 +#define SA5_REQUEST_PORT_OFFSET 0x40 +#define SA5_REPLY_INTR_MASK_OFFSET 0x34 +#define SA5_REPLY_PORT_OFFSET 0x44 +#define SA5_INTR_STATUS 0x30 + +#define SA5_INTR_OFF 0x08 +#define SA5_INTR_PENDING 0x08 +#define FIFO_EMPTY 0xffffffff + +#define CISS_ERROR_BIT 0x02 + +#define CCISS_INTR_ON 1 +#define CCISS_INTR_OFF 0 +/* + Send the command to the hardware +*/ +static void SA5_submit_command( ctlr_info_t *h, CommandList_struct *c) +{ +#ifdef CCISS_DEBUG + printk("Sending %x - down to controller\n", c->busaddr ); +#endif /* CCISS_DEBUG */ + writel(c->busaddr, h->vaddr + SA5_REQUEST_PORT_OFFSET); + h->commands_outstanding++; + if ( h->commands_outstanding > h->max_outstanding) + h->max_outstanding = h->commands_outstanding; +} + +/* + * This card is the oposite of the other cards. + * 0 turns interrupts on... + * 0x08 turns them off... + */ +static void SA5_intr_mask(ctlr_info_t *h, unsigned long val) +{ + if (val) + { /* Turn interrupts on */ + writel(0, h->vaddr + SA5_REPLY_INTR_MASK_OFFSET); + } else /* Turn them off */ + { + writel( SA5_INTR_OFF, + h->vaddr + SA5_REPLY_INTR_MASK_OFFSET); + } +} +/* + * Returns true if fifo is full. + * + */ +static unsigned long SA5_fifo_full(ctlr_info_t *h) +{ + if( h->commands_outstanding >= h->max_commands) + return(1); + else + return(0); + +} +/* + * returns value read from hardware. + * returns FIFO_EMPTY if there is nothing to read + */ +static unsigned long SA5_completed(ctlr_info_t *h) +{ + unsigned long register_value + = readl(h->vaddr + SA5_REPLY_PORT_OFFSET); + if(register_value != FIFO_EMPTY) + { + h->commands_outstanding--; +#ifdef CCISS_DEBUG + printk("cciss: Read %lx back from board\n", register_value); +#endif /* CCISS_DEBUG */ + } +#ifdef CCISS_DEBUG + else + { + printk("cciss: FIFO Empty read\n"); + } +#endif + return ( register_value); + +} +/* + * Returns true if an interrupt is pending.. + */ +static unsigned long SA5_intr_pending(ctlr_info_t *h) +{ + unsigned long register_value = + readl(h->vaddr + SA5_INTR_STATUS); +#ifdef CCISS_DEBUG + printk("cciss: intr_pending %lx\n", register_value); +#endif /* CCISS_DEBUG */ + if( register_value & SA5_INTR_PENDING) + return 1; + return 0 ; +} + + +static struct access_method SA5_access = { + SA5_submit_command, + SA5_intr_mask, + SA5_fifo_full, + SA5_intr_pending, + SA5_completed, +}; + +struct board_type { + __u32 board_id; + char *product_name; + struct access_method *access; +}; +#endif /* CCISS_H */ + diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h new file mode 100644 index 000000000000..456a3a021dc8 --- /dev/null +++ b/drivers/block/cciss_cmd.h @@ -0,0 +1,254 @@ +#ifndef CCISS_CMD_H +#define CCISS_CMD_H +//########################################################################### +//DEFINES +//########################################################################### +#define CISS_VERSION "1.00" + +//general boundary defintions +#define SENSEINFOBYTES 32//note that this value may vary between host implementations +#define MAXSGENTRIES 31 +#define MAXREPLYQS 256 + +//Command Status value +#define CMD_SUCCESS 0x0000 +#define CMD_TARGET_STATUS 0x0001 +#define CMD_DATA_UNDERRUN 0x0002 +#define CMD_DATA_OVERRUN 0x0003 +#define CMD_INVALID 0x0004 +#define CMD_PROTOCOL_ERR 0x0005 +#define CMD_HARDWARE_ERR 0x0006 +#define CMD_CONNECTION_LOST 0x0007 +#define CMD_ABORTED 0x0008 +#define CMD_ABORT_FAILED 0x0009 +#define CMD_UNSOLICITED_ABORT 0x000A +#define CMD_TIMEOUT 0x000B +#define CMD_UNABORTABLE 0x000C + +//transfer direction +#define XFER_NONE 0x00 +#define XFER_WRITE 0x01 +#define XFER_READ 0x02 +#define XFER_RSVD 0x03 + +//task attribute +#define ATTR_UNTAGGED 0x00 +#define ATTR_SIMPLE 0x04 +#define ATTR_HEADOFQUEUE 0x05 +#define ATTR_ORDERED 0x06 +#define ATTR_ACA 0x07 + +//cdb type +#define TYPE_CMD 0x00 +#define TYPE_MSG 0x01 + +//config space register offsets +#define CFG_VENDORID 0x00 +#define CFG_DEVICEID 0x02 +#define CFG_I2OBAR 0x10 +#define CFG_MEM1BAR 0x14 + +//i2o space register offsets +#define I2O_IBDB_SET 0x20 +#define I2O_IBDB_CLEAR 0x70 +#define I2O_INT_STATUS 0x30 +#define I2O_INT_MASK 0x34 +#define I2O_IBPOST_Q 0x40 +#define I2O_OBPOST_Q 0x44 + +//Configuration Table +#define CFGTBL_ChangeReq 0x00000001l +#define CFGTBL_AccCmds 0x00000001l + +#define CFGTBL_Trans_Simple 0x00000002l + +#define CFGTBL_BusType_Ultra2 0x00000001l +#define CFGTBL_BusType_Ultra3 0x00000002l +#define CFGTBL_BusType_Fibre1G 0x00000100l +#define CFGTBL_BusType_Fibre2G 0x00000200l +typedef struct _vals32 +{ + __u32 lower; + __u32 upper; +} vals32; + +typedef union _u64bit +{ + vals32 val32; + __u64 val; +} u64bit; + +// Type defs used in the following structs +#define BYTE __u8 +#define WORD __u16 +#define HWORD __u16 +#define DWORD __u32 +#define QWORD vals32 + +//########################################################################### +//STRUCTURES +//########################################################################### +#define CISS_MAX_LUN 16 +// SCSI-3 Cmmands + +#pragma pack(1) + +#define CISS_INQUIRY 0x12 +//Date returned +typedef struct _InquiryData_struct +{ + BYTE data_byte[36]; +} InquiryData_struct; + +#define CISS_REPORT_LOG 0xc2 /* Report Logical LUNs */ +// Data returned +typedef struct _ReportLUNdata_struct +{ + BYTE LUNListLength[4]; + DWORD reserved; + BYTE LUN[CISS_MAX_LUN][8]; +} ReportLunData_struct; + +#define CCISS_READ_CAPACITY 0x25 /* Read Capacity */ +typedef struct _ReadCapdata_struct +{ + BYTE total_size[4]; // Total size in blocks + BYTE block_size[4]; // Size of blocks in bytes +} ReadCapdata_struct; + +// 12 byte commands not implemented in firmware yet. +// #define CCISS_READ 0xa8 // Read(12) +// #define CCISS_WRITE 0xaa // Write(12) + #define CCISS_READ 0x28 // Read(10) + #define CCISS_WRITE 0x2a // Write(10) + +//Command List Structure +typedef union _SCSI3Addr_struct { + struct { + BYTE Bus:6; + BYTE Mode:2; // b00 + BYTE Dev; + } PeripDev; + struct { + BYTE DevMSB:6; + BYTE Mode:2; // b01 + BYTE DevLSB; + } LogDev; + struct { + BYTE Targ:6; + BYTE Mode:2; // b10 + BYTE Dev:5; + BYTE Bus:3; + } LogUnit; +} SCSI3Addr_struct; + +typedef struct _PhysDevAddr_struct { + DWORD TargetId:24; + DWORD Bus:6; + DWORD Mode:2; + SCSI3Addr_struct Target[2]; //2 level target device addr +} PhysDevAddr_struct; + +typedef struct _LogDevAddr_struct { + DWORD VolId:30; + DWORD Mode:2; + BYTE reserved[4]; +} LogDevAddr_struct; + +typedef union _LUNAddr_struct { + BYTE LunAddrBytes[8]; + SCSI3Addr_struct SCSI3Lun[4]; + PhysDevAddr_struct PhysDev; + LogDevAddr_struct LogDev; +} LUNAddr_struct; + +typedef struct _CommandListHeader_struct { + BYTE ReplyQueue; + BYTE SGList; + HWORD SGTotal; + QWORD Tag; + LUNAddr_struct LUN; +} CommandListHeader_struct; +typedef struct _RequestBlock_struct { + BYTE CDBLen; + struct { + BYTE Type:3; + BYTE Attribute:3; + BYTE Direction:2; + } Type; + HWORD Timeout; + BYTE CDB[16]; +} RequestBlock_struct; +typedef struct _ErrDescriptor_struct { + QWORD Addr; + DWORD Len; +} ErrDescriptor_struct; +typedef struct _SGDescriptor_struct { + QWORD Addr; + DWORD Len; + DWORD Ext; +} SGDescriptor_struct; + +typedef union _MoreErrInfo_struct{ + struct { + BYTE Reserved[3]; + BYTE Type; + DWORD ErrorInfo; + }Common_Info; + struct{ + BYTE Reserved[2]; + BYTE offense_size;//size of offending entry + BYTE offense_num; //byte # of offense 0-base + DWORD offense_value; + }Invalid_Cmd; +}MoreErrInfo_struct; +typedef struct _ErrorInfo_struct { + BYTE ScsiStatus; + BYTE SenseLen; + HWORD CommandStatus; + DWORD ResidualCnt; + MoreErrInfo_struct MoreErrInfo; + BYTE SenseInfo[SENSEINFOBYTES]; +} ErrorInfo_struct; + +/* Command types */ +#define CMD_RWREQ 0x00 +#define CMD_IOCTL_PEND 0x01 +#define CMD_IOCTL_DONE 0x02 + +typedef struct _CommandList_struct { + CommandListHeader_struct Header; + RequestBlock_struct Request; + ErrDescriptor_struct ErrDesc; + SGDescriptor_struct SG[MAXSGENTRIES]; + /* information associated with the command */ + __u32 busaddr; /* physical addres of this record */ + ErrorInfo_struct * err_info; /* pointer to the allocated mem */ + int cmd_type; + struct _CommandList_struct *prev; + struct _CommandList_struct *next; + struct buffer_head * bh; +} CommandList_struct; + +//Configuration Table Structure +typedef struct _HostWrite_struct { + DWORD TransportRequest; + DWORD Reserved; + DWORD CoalIntDelay; + DWORD CoalIntCount; +} HostWrite_struct; + +typedef struct _CfgTable_struct { + BYTE Signature[4]; + DWORD SpecValence; + DWORD TransportSupport; + DWORD TransportActive; + HostWrite_struct HostWrite; + DWORD CmdsOutMax; + DWORD BusTypes; + DWORD Reserved; + BYTE ServerName[16]; + DWORD HeartBeat; +} CfgTable_struct; +#pragma pack() +#endif // CCISS_CMD_H diff --git a/drivers/block/genhd.c b/drivers/block/genhd.c index d95c555c4a68..6a13b121dcf9 100644 --- a/drivers/block/genhd.c +++ b/drivers/block/genhd.c @@ -131,6 +131,10 @@ char *disk_name (struct gendisk *hd, int minor, char *buf) return buf; } } + if (hd->major >= COMPAQ_CISS_MAJOR && hd->major <= + COMPAQ_CISS_MAJOR+7) { + return raid_name (hd, minor, COMPAQ_CISS_MAJOR, buf); + } if (hd->major >= COMPAQ_SMART2_MAJOR && hd->major <= COMPAQ_SMART2_MAJOR+7) { return raid_name (hd, minor, COMPAQ_SMART2_MAJOR, buf); @@ -1622,6 +1626,9 @@ __initfunc(void device_setup(void)) { extern void console_map_init(void); extern void cpqarray_init(void); +#ifdef CONFIG_BLK_CPQ_CISS_DA + extern int cciss_init(void); +#endif #ifdef CONFIG_PARPORT extern int parport_init(void); #endif @@ -1655,6 +1662,9 @@ __initfunc(void device_setup(void)) #ifdef CONFIG_BLK_CPQ_DA cpqarray_init(); #endif +#ifdef CONFIG_BLK_CPQ_CISS_DA + cciss_init(); +#endif #ifdef CONFIG_NET net_dev_init(); #endif diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index f918e56ea416..955d59b72368 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -421,7 +421,15 @@ static inline int seek_to_not_starving_chunk(struct request ** req, int * lat) case COMPAQ_SMART2_MAJOR+4: \ case COMPAQ_SMART2_MAJOR+5: \ case COMPAQ_SMART2_MAJOR+6: \ - case COMPAQ_SMART2_MAJOR+7: + case COMPAQ_SMART2_MAJOR+7: \ + case COMPAQ_CISS_MAJOR+0: \ + case COMPAQ_CISS_MAJOR+1: \ + case COMPAQ_CISS_MAJOR+2: \ + case COMPAQ_CISS_MAJOR+3: \ + case COMPAQ_CISS_MAJOR+4: \ + case COMPAQ_CISS_MAJOR+5: \ + case COMPAQ_CISS_MAJOR+6: \ + case COMPAQ_CISS_MAJOR+7: #define elevator_starve_rest_of_queue(req) \ do { \ @@ -568,7 +576,9 @@ void add_request(struct blk_dev_struct * dev, struct request * req) if (scsi_blk_major(major) || (major >= DAC960_MAJOR+0 && major <= DAC960_MAJOR+7) || (major >= COMPAQ_SMART2_MAJOR+0 && - major <= COMPAQ_SMART2_MAJOR+7)) + major <= COMPAQ_SMART2_MAJOR+7) || + (major >= COMPAQ_CISS_MAJOR+0 && + major <= COMPAQ_CISS_MAJOR+7)) queue_new_request = 1; out: diff --git a/drivers/char/Config.in b/drivers/char/Config.in index 407bf8fdd624..ec9012fadbea 100644 --- a/drivers/char/Config.in +++ b/drivers/char/Config.in @@ -66,7 +66,7 @@ if [ "$CONFIG_PARPORT" != "n" ]; then bool ' Support IEEE1284 status readback' CONFIG_PRINTER_READBACK fi fi - + bool 'Mouse Support (not serial mice)' CONFIG_MOUSE if [ "$CONFIG_MOUSE" = "y" ]; then mainmenu_option next_comment @@ -121,6 +121,19 @@ if [ "$CONFIG_ALPHA_BOOK1" = "y" ]; then bool 'Tadpole ANA H8 Support' CONFIG_H8 fi +if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + tristate '/dev/agpgart (AGP Support) (EXPERIMENTAL)' CONFIG_AGP n + if [ "$CONFIG_AGP" != "n" ]; then + bool ' Intel 440LX/BX/GX support' CONFIG_AGP_INTEL + bool ' Intel I810/I810 DC100/I810e support' CONFIG_AGP_I810 + bool ' VIA VP3/MVP3/Apollo Pro support' CONFIG_AGP_VIA + bool ' AMD Irongate support' CONFIG_AGP_AMD + bool ' Generic SiS support' CONFIG_AGP_SIS + bool ' ALI M1541 support' CONFIG_AGP_ALI + fi + source drivers/char/drm/Config.in +fi + mainmenu_option next_comment comment 'Video For Linux' @@ -160,13 +173,13 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - dep_tristate 'CPiA Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV + dep_tristate 'CPiA Video For Linux' CONFIG_VIDEO_CPIA $CONFIG_VIDEO_DEV if [ "$CONFIG_VIDEO_CPIA" != "n" ]; then - if [ "$CONFIG_PARPORT" != "n" ]; then - dep_tristate 'CPiA Parallel Port Lowlevel Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT - if [ "$CONFIG_VIDEO_CPIA_PP" != "n" ]; then - bool 'CPiA Parallel Port DMA Support (EXPERIMENTAL)' CONFIG_VIDEO_CPIA_PP_DMA - fi + if [ "CONFIG_PARPORT_1284" != "n" ]; then + dep_tristate ' CPiA Parallel Port Lowlevel Support' CONFIG_VIDEO_CPIA_PP $CONFIG_VIDEO_CPIA $CONFIG_PARPORT + fi + if [ "$CONFIG_USB" != "n" ]; then + dep_tristate ' CPiA USB Lowlevel Support' CONFIG_VIDEO_CPIA_USB $CONFIG_VIDEO_CPIA $CONFIG_USB fi fi fi diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 7ac1772bc92f..42ddcd4f17d9 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -11,7 +11,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) rio ftape joystick +ALL_SUB_DIRS := $(SUB_DIRS) rio ftape joystick drm # # This file contains the font map for the default (hardware) font @@ -371,6 +371,17 @@ else endif endif +ifeq ($(CONFIG_AGP),y) +L_OBJS += agp/agpgart.o +ALL_SUB_DIRS += agp +SUB_DIRS += agp +else + ifeq ($(CONFIG_AGP),m) + ALL_SUB_DIRS += agp + MOD_SUB_DIRS += agp + endif +endif + ifeq ($(CONFIG_VIDEO_DEV),y) LX_OBJS += videodev.o else @@ -499,6 +510,14 @@ else endif endif +ifeq ($(CONFIG_VIDEO_CPIA_USB),y) +L_OBJS += cpia_usb.o +else + ifeq ($(CONFIG_VIDEO_CPIA_USB),m) + M_OBJS += cpia_usb.o + endif +endif + ifeq ($(CONFIG_RADIO_AZTECH),y) L_OBJS += radio-aztech.o else @@ -603,6 +622,17 @@ ifdef CONFIG_H8 LX_OBJS += h8.o endif + +ifeq ($(CONFIG_DRM),y) +SUB_DIRS += drm +MOD_SUB_DIRS += drm +else + ifeq ($(CONFIG_DRM),m) + MOD_SUB_DIRS += drm + endif +endif + + ifeq ($(L_I2C),y) LX_OBJS += i2c.o else diff --git a/drivers/char/agp/Makefile b/drivers/char/agp/Makefile new file mode 100644 index 000000000000..d20869b0e4b2 --- /dev/null +++ b/drivers/char/agp/Makefile @@ -0,0 +1,22 @@ +# +# Makefile for the agpgart device driver. This driver adds a user +# space ioctl interface to use agp memory. It also adds a kernel interface +# that other drivers could use to manipulate agp memory. + +O_TARGET := agp.o + +ifeq ($(CONFIG_AGP),y) + O_OBJS += agpgart_fe.o + OX_OBJS += agpgart_be.o +else + ifeq ($(CONFIG_AGP), m) + MI_OBJS += agpgart_fe.o + MIX_OBJS += agpgart_be.o + M_OBJS += agpgart.o + endif +endif + +include $(TOPDIR)/Rules.make + +agpgart.o: agpgart_be.o agpgart_fe.o + $(LD) $(LD_RFLAG) -r -o $@ agpgart_be.o agpgart_fe.o diff --git a/drivers/char/agp/agp.h b/drivers/char/agp/agp.h new file mode 100644 index 000000000000..fdc1b4186788 --- /dev/null +++ b/drivers/char/agp/agp.h @@ -0,0 +1,279 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AGP_BACKEND_PRIV_H +#define _AGP_BACKEND_PRIV_H 1 + +enum aper_size_type { + U8_APER_SIZE, + U16_APER_SIZE, + U32_APER_SIZE, + LVL2_APER_SIZE, + FIXED_APER_SIZE +}; + +typedef struct _gatt_mask { + unsigned long mask; + u32 type; + /* totally device specific, for integrated chipsets that + * might have different types of memory masks. For other + * devices this will probably be ignored */ +} gatt_mask; + +typedef struct _aper_size_info_8 { + int size; + int num_entries; + int page_order; + u8 size_value; +} aper_size_info_8; + +typedef struct _aper_size_info_16 { + int size; + int num_entries; + int page_order; + u16 size_value; +} aper_size_info_16; + +typedef struct _aper_size_info_32 { + int size; + int num_entries; + int page_order; + u32 size_value; +} aper_size_info_32; + +typedef struct _aper_size_info_lvl2 { + int size; + int num_entries; + u32 size_value; +} aper_size_info_lvl2; + +typedef struct _aper_size_info_fixed { + int size; + int num_entries; + int page_order; +} aper_size_info_fixed; + +struct agp_bridge_data { + agp_version *version; + void *aperture_sizes; + void *previous_size; + void *current_size; + void *dev_private_data; + struct pci_dev *dev; + gatt_mask *masks; + unsigned long *gatt_table; + unsigned long *gatt_table_real; + unsigned long scratch_page; + unsigned long gart_bus_addr; + unsigned long gatt_bus_addr; + u32 mode; + enum chipset_type type; + enum aper_size_type size_type; + u32 *key_list; + atomic_t current_memory_agp; + atomic_t agp_in_use; + int max_memory_agp; /* in number of pages */ + int needs_scratch_page; + int aperture_size_idx; + int num_aperture_sizes; + int num_of_masks; + int capndx; + + /* Links to driver specific functions */ + + int (*fetch_size) (void); + int (*configure) (void); + void (*agp_enable) (u32); + void (*cleanup) (void); + void (*tlb_flush) (agp_memory *); + unsigned long (*mask_memory) (unsigned long, int); + void (*cache_flush) (void); + int (*create_gatt_table) (void); + int (*free_gatt_table) (void); + int (*insert_memory) (agp_memory *, off_t, int); + int (*remove_memory) (agp_memory *, off_t, int); + agp_memory *(*alloc_by_type) (size_t, int); + void (*free_by_type) (agp_memory *); +}; + +#define OUTREG32(mmap, addr, val) *(volatile u32 *)(mmap + (addr)) = (val) +#define OUTREG16(mmap, addr, val) *(volatile u16 *)(mmap + (addr)) = (val) +#define OUTREG8 (mmap, addr, val) *(volatile u8 *) (mmap + (addr)) = (val) + +#define INREG32(mmap, addr) *(volatile u32 *)(mmap + (addr)) +#define INREG16(mmap, addr) *(volatile u16 *)(mmap + (addr)) +#define INREG8 (mmap, addr) *(volatile u8 *) (mmap + (addr)) + +#define CACHE_FLUSH agp_bridge.cache_flush +#define A_SIZE_8(x) ((aper_size_info_8 *) x) +#define A_SIZE_16(x) ((aper_size_info_16 *) x) +#define A_SIZE_32(x) ((aper_size_info_32 *) x) +#define A_SIZE_LVL2(x) ((aper_size_info_lvl2 *) x) +#define A_SIZE_FIX(x) ((aper_size_info_fixed *) x) +#define A_IDX8() (A_SIZE_8(agp_bridge.aperture_sizes) + i) +#define A_IDX16() (A_SIZE_16(agp_bridge.aperture_sizes) + i) +#define A_IDX32() (A_SIZE_32(agp_bridge.aperture_sizes) + i) +#define A_IDXLVL2() (A_SIZE_LVL2(agp_bridge.aperture_sizes) + i) +#define A_IDXFIX() (A_SIZE_FIX(agp_bridge.aperture_sizes) + i) +#define MAXKEY (4096 * 32) + +#ifndef min +#define min(a,b) (((a)<(b))?(a):(b)) +#endif + +#define arraysize(x) (sizeof(x)/sizeof(*(x))) + +#define AGPGART_MODULE_NAME "agpgart" +#define PFX AGPGART_MODULE_NAME ": " + +#define PGE_EMPTY(p) (!(p) || (p) == (unsigned long) agp_bridge.scratch_page) + +#ifndef PCI_DEVICE_ID_VIA_82C691_0 +#define PCI_DEVICE_ID_VIA_82C691_0 0x0691 +#endif +#ifndef PCI_DEVICE_ID_VIA_82C691_1 +#define PCI_DEVICE_ID_VIA_82C691_1 0x8691 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_0 +#define PCI_DEVICE_ID_INTEL_810_0 0x7120 +#endif +#ifndef PCI_DEVICE_ID_INTEL_840_0 +#define PCI_DEVICE_ID_INTEL_840_0 0x1a21 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_DC100_0 +#define PCI_DEVICE_ID_INTEL_810_DC100_0 0x7122 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_E_0 +#define PCI_DEVICE_ID_INTEL_810_E_0 0x7124 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82443GX_0 +#define PCI_DEVICE_ID_INTEL_82443GX_0 0x71a0 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_1 +#define PCI_DEVICE_ID_INTEL_810_1 0x7121 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_DC100_1 +#define PCI_DEVICE_ID_INTEL_810_DC100_1 0x7123 +#endif +#ifndef PCI_DEVICE_ID_INTEL_810_E_1 +#define PCI_DEVICE_ID_INTEL_810_E_1 0x7125 +#endif +#ifndef PCI_DEVICE_ID_INTEL_815_0 +#define PCI_DEVICE_ID_INTEL_815_0 0x1130 +#endif +#ifndef PCI_DEVICE_ID_INTEL_815_1 +#define PCI_DEVICE_ID_INTEL_815_1 0x1132 +#endif +#ifndef PCI_DEVICE_ID_INTEL_82443GX_1 +#define PCI_DEVICE_ID_INTEL_82443GX_1 0x71a1 +#endif +#ifndef PCI_DEVICE_ID_AMD_IRONGATE_0 +#define PCI_DEVICE_ID_AMD_IRONGATE_0 0x7006 +#endif +#ifndef PCI_VENDOR_ID_AL +#define PCI_VENDOR_ID_AL 0x10b9 +#endif +#ifndef PCI_DEVICE_ID_AL_M1541_0 +#define PCI_DEVICE_ID_AL_M1541_0 0x1541 +#endif +#ifndef PCI_DEVICE_ID_SI_630 +#define PCI_DEVICE_ID_SI_630 0x0630 +#endif +#ifndef PCI_DEVICE_ID_SI_540 +#define PCI_DEVICE_ID_SI_540 0x0540 +#endif +#ifndef PCI_DEVICE_ID_SI_620 +#define PCI_DEVICE_ID_SI_620 0x0620 +#endif +#ifndef PCI_DEVICE_ID_SI_530 +#define PCI_DEVICE_ID_SI_530 0x0530 +#endif +#ifndef PCI_DEVICE_ID_VIA_8501_0 +#define PCI_DEVICE_ID_VIA_8501_0 0x0501 +#endif + + +/* intel register */ +#define INTEL_APBASE 0x10 +#define INTEL_APSIZE 0xb4 +#define INTEL_ATTBASE 0xb8 +#define INTEL_AGPCTRL 0xb0 +#define INTEL_NBXCFG 0x50 +#define INTEL_ERRSTS 0x91 + +/* intel i840 registers */ +#define INTEL_I840_MCHCFG 0x50 +#define INTEL_I840_ERRSTS 0xc8 + +/* intel i810 registers */ +#define I810_GMADDR 0x10 +#define I810_MMADDR 0x14 +#define I810_PTE_BASE 0x10000 +#define I810_PTE_MAIN_UNCACHED 0x00000000 +#define I810_PTE_LOCAL 0x00000002 +#define I810_PTE_VALID 0x00000001 +#define I810_SMRAM_MISCC 0x70 +#define I810_GFX_MEM_WIN_SIZE 0x00010000 +#define I810_GFX_MEM_WIN_32M 0x00010000 +#define I810_GMS 0x000000c0 +#define I810_GMS_DISABLE 0x00000000 +#define I810_PGETBL_CTL 0x2020 +#define I810_PGETBL_ENABLED 0x00000001 +#define I810_DRAM_CTL 0x3000 +#define I810_DRAM_ROW_0 0x00000001 +#define I810_DRAM_ROW_0_SDRAM 0x00000001 + +/* VIA register */ +#define VIA_APBASE 0x10 +#define VIA_GARTCTRL 0x80 +#define VIA_APSIZE 0x84 +#define VIA_ATTBASE 0x88 + +/* SiS registers */ +#define SIS_APBASE 0x10 +#define SIS_ATTBASE 0x90 +#define SIS_APSIZE 0x94 +#define SIS_TLBCNTRL 0x97 +#define SIS_TLBFLUSH 0x98 + +/* AMD registers */ +#define AMD_APBASE 0x10 +#define AMD_MMBASE 0x14 +#define AMD_APSIZE 0xac +#define AMD_MODECNTL 0xb0 +#define AMD_MODECNTL2 0xb2 +#define AMD_GARTENABLE 0x02 /* In mmio region (16-bit register) */ +#define AMD_ATTBASE 0x04 /* In mmio region (32-bit register) */ +#define AMD_TLBFLUSH 0x0c /* In mmio region (32-bit register) */ +#define AMD_CACHEENTRY 0x10 /* In mmio region (32-bit register) */ + +/* ALi registers */ +#define ALI_APBASE 0x10 +#define ALI_AGPCTRL 0xb8 +#define ALI_ATTBASE 0xbc +#define ALI_TLBCTRL 0xc0 + +#endif /* _AGP_BACKEND_PRIV_H */ diff --git a/drivers/char/agp/agp_backend.h b/drivers/char/agp/agp_backend.h new file mode 100644 index 000000000000..750af5e01656 --- /dev/null +++ b/drivers/char/agp/agp_backend.h @@ -0,0 +1,231 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AGP_BACKEND_H +#define _AGP_BACKEND_H 1 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define AGPGART_VERSION_MAJOR 0 +#define AGPGART_VERSION_MINOR 99 + +enum chipset_type { + NOT_SUPPORTED, + INTEL_GENERIC, + INTEL_LX, + INTEL_BX, + INTEL_GX, + INTEL_I810, + INTEL_I840, + VIA_GENERIC, + VIA_VP3, + VIA_MVP3, + VIA_MVP4, + VIA_APOLLO_SUPER, + VIA_APOLLO_PRO, + SIS_GENERIC, + AMD_GENERIC, + AMD_IRONGATE, + ALI_M1541, + ALI_GENERIC +}; + +typedef struct _agp_version { + u16 major; + u16 minor; +} agp_version; + +typedef struct _agp_kern_info { + agp_version version; + struct pci_dev *device; + enum chipset_type chipset; + unsigned long mode; + off_t aper_base; + size_t aper_size; + int max_memory; /* In pages */ + int current_memory; +} agp_kern_info; + +/* + * The agp_memory structure has information + * about the block of agp memory allocated. + * A caller may manipulate the next and prev + * pointers to link each allocated item into + * a list. These pointers are ignored by the + * backend. Everything else should never be + * written to, but the caller may read any of + * the items to detrimine the status of this + * block of agp memory. + * + */ + +typedef struct _agp_memory { + int key; + struct _agp_memory *next; + struct _agp_memory *prev; + size_t page_count; + int num_scratch_pages; + unsigned long *memory; + off_t pg_start; + u32 type; + u32 physical; + u8 is_bound; + u8 is_flushed; +} agp_memory; + +#define AGP_NORMAL_MEMORY 0 + +extern void agp_free_memory(agp_memory *); + +/* + * agp_free_memory : + * + * This function frees memory associated with + * an agp_memory pointer. It is the only function + * that can be called when the backend is not owned + * by the caller. (So it can free memory on client + * death.) + * + * It takes an agp_memory pointer as an argument. + * + */ + +extern agp_memory *agp_allocate_memory(size_t, u32); + +/* + * agp_allocate_memory : + * + * This function allocates a group of pages of + * a certain type. + * + * It takes a size_t argument of the number of pages, and + * an u32 argument of the type of memory to be allocated. + * Every agp bridge device will allow you to allocate + * AGP_NORMAL_MEMORY which maps to physical ram. Any other + * type is device dependant. + * + * It returns NULL whenever memory is unavailable. + * + */ + +extern void agp_copy_info(agp_kern_info *); + +/* + * agp_copy_info : + * + * This function copies information about the + * agp bridge device and the state of the agp + * backend into an agp_kern_info pointer. + * + * It takes an agp_kern_info pointer as an + * argument. The caller should insure that + * this pointer is valid. + * + */ + +extern int agp_bind_memory(agp_memory *, off_t); + +/* + * agp_bind_memory : + * + * This function binds an agp_memory structure + * into the graphics aperture translation table. + * + * It takes an agp_memory pointer and an offset into + * the graphics aperture translation table as arguments + * + * It returns -EINVAL if the pointer == NULL. + * It returns -EBUSY if the area of the table + * requested is already in use. + * + */ + +extern int agp_unbind_memory(agp_memory *); + +/* + * agp_unbind_memory : + * + * This function removes an agp_memory structure + * from the graphics aperture translation table. + * + * It takes an agp_memory pointer as an argument. + * + * It returns -EINVAL if this piece of agp_memory + * is not currently bound to the graphics aperture + * translation table or if the agp_memory + * pointer == NULL + * + */ + +extern void agp_enable(u32); + +/* + * agp_enable : + * + * This function initializes the agp point-to-point + * connection. + * + * It takes an agp mode register as an argument + * + */ + +extern int agp_backend_acquire(void); + +/* + * agp_backend_acquire : + * + * This Function attempts to acquire the agp + * backend. + * + * returns -EBUSY if agp is in use, + * returns 0 if the caller owns the agp backend + */ + +extern void agp_backend_release(void); + +/* + * agp_backend_release : + * + * This Function releases the lock on the agp + * backend. + * + * The caller must insure that the graphics + * aperture translation table is read for use + * by another entity. (Ensure that all memory + * it bound is unbound.) + * + */ + +/* Don't call this directly!! used in initialization */ +extern int agp_init(void); + +#endif /* _AGP_BACKEND_H */ diff --git a/drivers/char/agp/agpgart.h b/drivers/char/agp/agpgart.h new file mode 100644 index 000000000000..7457cd114baf --- /dev/null +++ b/drivers/char/agp/agpgart.h @@ -0,0 +1,224 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AGP_H +#define _AGP_H 1 + +#define AGPIOC_BASE 'A' +#define AGPIOC_INFO _IOR (AGPIOC_BASE, 0, agp_info*) +#define AGPIOC_ACQUIRE _IO (AGPIOC_BASE, 1) +#define AGPIOC_RELEASE _IO (AGPIOC_BASE, 2) +#define AGPIOC_SETUP _IOW (AGPIOC_BASE, 3, agp_setup*) +#define AGPIOC_RESERVE _IOW (AGPIOC_BASE, 4, agp_region*) +#define AGPIOC_PROTECT _IOW (AGPIOC_BASE, 5, agp_region*) +#define AGPIOC_ALLOCATE _IOWR(AGPIOC_BASE, 6, agp_allocate*) +#define AGPIOC_DEALLOCATE _IOW (AGPIOC_BASE, 7, int) +#define AGPIOC_BIND _IOW (AGPIOC_BASE, 8, agp_bind*) +#define AGPIOC_UNBIND _IOW (AGPIOC_BASE, 9, agp_unbind*) + +#define AGP_DEVICE "/dev/agpgart" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef __KERNEL__ +#include +#include + +typedef struct _agp_version { + __u16 major; + __u16 minor; +} agp_version; + +typedef struct _agp_info { + agp_version version; /* version of the driver */ + __u32 bridge_id; /* bridge vendor/device */ + __u32 agp_mode; /* mode info of bridge */ + off_t aper_base; /* base of aperture */ + size_t aper_size; /* size of aperture */ + size_t pg_total; /* max pages (swap + system) */ + size_t pg_system; /* max pages (system) */ + size_t pg_used; /* current pages used */ +} agp_info; + +typedef struct _agp_setup { + __u32 agp_mode; /* mode info of bridge */ +} agp_setup; + +/* + * The "prot" down below needs still a "sleep" flag somehow ... + */ +typedef struct _agp_segment { + off_t pg_start; /* starting page to populate */ + size_t pg_count; /* number of pages */ + int prot; /* prot flags for mmap */ +} agp_segment; + +typedef struct _agp_region { + pid_t pid; /* pid of process */ + size_t seg_count; /* number of segments */ + struct _agp_segment *seg_list; +} agp_region; + +typedef struct _agp_allocate { + int key; /* tag of allocation */ + size_t pg_count; /* number of pages */ + __u32 type; /* 0 == normal, other devspec */ + __u32 physical; /* device specific (some devices + * need a phys address of the + * actual page behind the gatt + * table) */ +} agp_allocate; + +typedef struct _agp_bind { + int key; /* tag of allocation */ + off_t pg_start; /* starting page to populate */ +} agp_bind; + +typedef struct _agp_unbind { + int key; /* tag of allocation */ + __u32 priority; /* priority for paging out */ +} agp_unbind; + +#else /* __KERNEL__ */ + +#define AGPGART_MINOR 175 + +#define AGP_UNLOCK() up(&(agp_fe.agp_mutex)); +#define AGP_LOCK() down(&(agp_fe.agp_mutex)); +#define AGP_LOCK_INIT() sema_init(&(agp_fe.agp_mutex), 1) + +#ifndef _AGP_BACKEND_H +typedef struct _agp_version { + u16 major; + u16 minor; +} agp_version; + +#endif + +typedef struct _agp_info { + agp_version version; /* version of the driver */ + u32 bridge_id; /* bridge vendor/device */ + u32 agp_mode; /* mode info of bridge */ + off_t aper_base; /* base of aperture */ + size_t aper_size; /* size of aperture */ + size_t pg_total; /* max pages (swap + system) */ + size_t pg_system; /* max pages (system) */ + size_t pg_used; /* current pages used */ +} agp_info; + +typedef struct _agp_setup { + u32 agp_mode; /* mode info of bridge */ +} agp_setup; + +/* + * The "prot" down below needs still a "sleep" flag somehow ... + */ +typedef struct _agp_segment { + off_t pg_start; /* starting page to populate */ + size_t pg_count; /* number of pages */ + int prot; /* prot flags for mmap */ +} agp_segment; + +typedef struct _agp_segment_priv { + off_t pg_start; + size_t pg_count; + pgprot_t prot; +} agp_segment_priv; + +typedef struct _agp_region { + pid_t pid; /* pid of process */ + size_t seg_count; /* number of segments */ + struct _agp_segment *seg_list; +} agp_region; + +typedef struct _agp_allocate { + int key; /* tag of allocation */ + size_t pg_count; /* number of pages */ + u32 type; /* 0 == normal, other devspec */ + u32 physical; /* device specific (some devices + * need a phys address of the + * actual page behind the gatt + * table) */ +} agp_allocate; + +typedef struct _agp_bind { + int key; /* tag of allocation */ + off_t pg_start; /* starting page to populate */ +} agp_bind; + +typedef struct _agp_unbind { + int key; /* tag of allocation */ + u32 priority; /* priority for paging out */ +} agp_unbind; + +typedef struct _agp_client { + struct _agp_client *next; + struct _agp_client *prev; + pid_t pid; + int num_segments; + agp_segment_priv **segments; +} agp_client; + +typedef struct _agp_controller { + struct _agp_controller *next; + struct _agp_controller *prev; + pid_t pid; + int num_clients; + agp_memory *pool; + agp_client *clients; +} agp_controller; + +#define AGP_FF_ALLOW_CLIENT 0 +#define AGP_FF_ALLOW_CONTROLLER 1 +#define AGP_FF_IS_CLIENT 2 +#define AGP_FF_IS_CONTROLLER 3 +#define AGP_FF_IS_VALID 4 + +typedef struct _agp_file_private { + struct _agp_file_private *next; + struct _agp_file_private *prev; + pid_t my_pid; + u32 access_flags; +} agp_file_private; + +struct agp_front_data { + struct semaphore agp_mutex; + agp_controller *current_controller; + agp_controller *controllers; + agp_file_private *file_priv_list; + u8 used_by_controller; + u8 backend_acquired; +}; + +#endif /* __KERNEL__ */ + +#endif /* _AGP_H */ diff --git a/drivers/char/agp/agpgart_be.c b/drivers/char/agp/agpgart_be.c new file mode 100644 index 000000000000..d672cf70dc5b --- /dev/null +++ b/drivers/char/agp/agpgart_be.c @@ -0,0 +1,2483 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ +#define EXPORT_SYMTAB 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "agp_backend.h" +#include "agp.h" + +MODULE_AUTHOR("Jeff Hartmann "); +MODULE_PARM(agp_try_unsupported, "1i"); +EXPORT_SYMBOL(agp_free_memory); +EXPORT_SYMBOL(agp_allocate_memory); +EXPORT_SYMBOL(agp_copy_info); +EXPORT_SYMBOL(agp_bind_memory); +EXPORT_SYMBOL(agp_unbind_memory); +EXPORT_SYMBOL(agp_enable); +EXPORT_SYMBOL(agp_backend_acquire); +EXPORT_SYMBOL(agp_backend_release); + +static void flush_cache(void); + +static struct agp_bridge_data agp_bridge; +static int agp_try_unsupported __initdata = 0; + + +static inline void flush_cache(void) +{ +#if defined(__i386__) + asm volatile ("wbinvd":::"memory"); +#elif defined(__alpha__) + /* ??? I wonder if we'll really need to flush caches, or if the + core logic can manage to keep the system coherent. The ARM + speaks only of using `cflush' to get things in memory in + preparation for power failure. + + If we do need to call `cflush', we'll need a target page, + as we can only flush one page at a time. */ + mb(); +#else +#error "Please define flush_cache." +#endif +} + +#ifdef CONFIG_SMP +static atomic_t cpus_waiting; + +static void ipi_handler(void *null) +{ + flush_cache(); + atomic_dec(&cpus_waiting); + while (atomic_read(&cpus_waiting) > 0) + barrier(); +} + +static void smp_flush_cache(void) +{ + atomic_set(&cpus_waiting, smp_num_cpus - 1); + if (smp_call_function(ipi_handler, NULL, 1, 0) != 0) + panic(PFX "timed out waiting for the other CPUs!\n"); + flush_cache(); + while (atomic_read(&cpus_waiting) > 0) + barrier(); +} +#define global_cache_flush smp_flush_cache +#else /* CONFIG_SMP */ +#define global_cache_flush flush_cache +#endif /* CONFIG_SMP */ + +int agp_backend_acquire(void) +{ + atomic_inc(&agp_bridge.agp_in_use); + + if (atomic_read(&agp_bridge.agp_in_use) != 1) { + atomic_dec(&agp_bridge.agp_in_use); + return -EBUSY; + } + MOD_INC_USE_COUNT; + return 0; +} + +void agp_backend_release(void) +{ + atomic_dec(&agp_bridge.agp_in_use); + MOD_DEC_USE_COUNT; +} + +/* + * Basic Page Allocation Routines - + * These routines handle page allocation + * and by default they reserve the allocated + * memory. They also handle incrementing the + * current_memory_agp value, Which is checked + * against a maximum value. + */ + +static unsigned long agp_alloc_page(void) +{ + void *pt; + + pt = (void *) __get_free_page(GFP_KERNEL); + if (pt == NULL) { + return 0; + } + atomic_inc(&mem_map[MAP_NR(pt)].count); + set_bit(PG_locked, &mem_map[MAP_NR(pt)].flags); + atomic_inc(&agp_bridge.current_memory_agp); + return (unsigned long) pt; +} + +static void agp_destroy_page(unsigned long page) +{ + void *pt = (void *) page; + + if (pt == NULL) { + return; + } + atomic_dec(&mem_map[MAP_NR(pt)].count); + clear_bit(PG_locked, &mem_map[MAP_NR(pt)].flags); +#if 0 + wake_up(&mem_map[MAP_NR(pt)].wait); +#endif + free_page((unsigned long) pt); + atomic_dec(&agp_bridge.current_memory_agp); +} + +/* End Basic Page Allocation Routines */ + +/* + * Generic routines for handling agp_memory structures - + * They use the basic page allocation routines to do the + * brunt of the work. + */ + + +static void agp_free_key(int key) +{ + + if (key < 0) { + return; + } + if (key < MAXKEY) { + clear_bit(key, agp_bridge.key_list); + } +} + +static int agp_get_key(void) +{ + int bit; + + bit = find_first_zero_bit(agp_bridge.key_list, MAXKEY); + if (bit < MAXKEY) { + set_bit(bit, agp_bridge.key_list); + return bit; + } + return -1; +} + +static agp_memory *agp_create_memory(int scratch_pages) +{ + agp_memory *new; + + new = kmalloc(sizeof(agp_memory), GFP_KERNEL); + + if (new == NULL) { + return NULL; + } + memset(new, 0, sizeof(agp_memory)); + new->key = agp_get_key(); + + if (new->key < 0) { + kfree(new); + return NULL; + } + new->memory = vmalloc(PAGE_SIZE * scratch_pages); + + if (new->memory == NULL) { + agp_free_key(new->key); + kfree(new); + return NULL; + } + new->num_scratch_pages = scratch_pages; + return new; +} + +void agp_free_memory(agp_memory * curr) +{ + int i; + + if (curr == NULL) { + return; + } + if (curr->is_bound == TRUE) { + agp_unbind_memory(curr); + } + if (curr->type != 0) { + agp_bridge.free_by_type(curr); + return; + } + if (curr->page_count != 0) { + for (i = 0; i < curr->page_count; i++) { + curr->memory[i] &= ~(0x00000fff); + agp_destroy_page((unsigned long) + phys_to_virt(curr->memory[i])); + } + } + agp_free_key(curr->key); + vfree(curr->memory); + kfree(curr); + MOD_DEC_USE_COUNT; +} + +#define ENTRIES_PER_PAGE (PAGE_SIZE / sizeof(unsigned long)) + +agp_memory *agp_allocate_memory(size_t page_count, u32 type) +{ + int scratch_pages; + agp_memory *new; + int i; + + if ((atomic_read(&agp_bridge.current_memory_agp) + page_count) > + agp_bridge.max_memory_agp) { + return NULL; + } + + if (type != 0) { + new = agp_bridge.alloc_by_type(page_count, type); + return new; + } + /* We always increase the module count, since free auto-decrements + * it + */ + + MOD_INC_USE_COUNT; + + scratch_pages = (page_count + ENTRIES_PER_PAGE - 1) / ENTRIES_PER_PAGE; + + new = agp_create_memory(scratch_pages); + + if (new == NULL) { + MOD_DEC_USE_COUNT; + return NULL; + } + for (i = 0; i < page_count; i++) { + new->memory[i] = agp_alloc_page(); + + if (new->memory[i] == 0) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[i] = + agp_bridge.mask_memory( + virt_to_phys((void *) new->memory[i]), + type); + new->page_count++; + } + + return new; +} + +/* End - Generic routines for handling agp_memory structures */ + +static int agp_return_size(void) +{ + int current_size; + void *temp; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + current_size = A_SIZE_8(temp)->size; + break; + case U16_APER_SIZE: + current_size = A_SIZE_16(temp)->size; + break; + case U32_APER_SIZE: + current_size = A_SIZE_32(temp)->size; + break; + case LVL2_APER_SIZE: + current_size = A_SIZE_LVL2(temp)->size; + break; + case FIXED_APER_SIZE: + current_size = A_SIZE_FIX(temp)->size; + break; + default: + current_size = 0; + break; + } + + return current_size; +} + +/* Routine to copy over information structure */ + +void agp_copy_info(agp_kern_info * info) +{ + memset(info, 0, sizeof(agp_kern_info)); + info->version.major = agp_bridge.version->major; + info->version.minor = agp_bridge.version->minor; + info->device = agp_bridge.dev; + info->chipset = agp_bridge.type; + info->mode = agp_bridge.mode; + info->aper_base = agp_bridge.gart_bus_addr; + info->aper_size = agp_return_size(); + info->max_memory = agp_bridge.max_memory_agp; + info->current_memory = atomic_read(&agp_bridge.current_memory_agp); +} + +/* End - Routine to copy over information structure */ + +/* + * Routines for handling swapping of agp_memory into the GATT - + * These routines take agp_memory and insert them into the GATT. + * They call device specific routines to actually write to the GATT. + */ + +int agp_bind_memory(agp_memory * curr, off_t pg_start) +{ + int ret_val; + + if ((curr == NULL) || (curr->is_bound == TRUE)) { + return -EINVAL; + } + ret_val = agp_bridge.insert_memory(curr, pg_start, curr->type); + + if (ret_val != 0) { + return ret_val; + } + curr->is_bound = TRUE; + curr->pg_start = pg_start; + return 0; +} + +int agp_unbind_memory(agp_memory * curr) +{ + int ret_val; + + if (curr == NULL) { + return -EINVAL; + } + if (curr->is_bound != TRUE) { + return -EINVAL; + } + ret_val = agp_bridge.remove_memory(curr, curr->pg_start, curr->type); + + if (ret_val != 0) { + return ret_val; + } + curr->is_bound = FALSE; + curr->pg_start = 0; + return 0; +} + +/* End - Routines for handling swapping of agp_memory into the GATT */ + +/* + * Driver routines - start + * Currently this module supports the + * i810, 440lx, 440bx, 440gx, via vp3, via mvp3, + * amd irongate, ALi M1541 and generic support for the + * SiS chipsets. + */ + +/* Generic Agp routines - Start */ + +static void agp_generic_agp_enable(u32 mode) +{ + struct pci_dev *device = NULL; + u32 command, scratch, cap_id; + u8 cap_ptr; + + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + 4, + &command); + + /* + * PASS1: go throu all devices that claim to be + * AGP devices and collect their data. + */ + + while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, + device)) != NULL) { + pci_read_config_dword(device, 0x04, &scratch); + + if (!(scratch & 0x00100000)) + continue; + + pci_read_config_byte(device, 0x34, &cap_ptr); + + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(device, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr != 0x00) { + /* + * Ok, here we have a AGP device. Disable impossible + * settings, and adjust the readqueue to the minimum. + */ + + pci_read_config_dword(device, cap_ptr + 4, &scratch); + + /* adjust RQ depth */ + command = + ((command & ~0xff000000) | + min((mode & 0xff000000), + min((command & 0xff000000), + (scratch & 0xff000000)))); + + /* disable SBA if it's not supported */ + if (!((command & 0x00000200) && + (scratch & 0x00000200) && + (mode & 0x00000200))) + command &= ~0x00000200; + + /* disable FW if it's not supported */ + if (!((command & 0x00000010) && + (scratch & 0x00000010) && + (mode & 0x00000010))) + command &= ~0x00000010; + + if (!((command & 4) && + (scratch & 4) && + (mode & 4))) + command &= ~0x00000004; + + if (!((command & 2) && + (scratch & 2) && + (mode & 2))) + command &= ~0x00000002; + + if (!((command & 1) && + (scratch & 1) && + (mode & 1))) + command &= ~0x00000001; + } + } + /* + * PASS2: Figure out the 4X/2X/1X setting and enable the + * target (our motherboard chipset). + */ + + if (command & 4) { + command &= ~3; /* 4X */ + } + if (command & 2) { + command &= ~5; /* 2X */ + } + if (command & 1) { + command &= ~6; /* 1X */ + } + command |= 0x00000100; + + pci_write_config_dword(agp_bridge.dev, + agp_bridge.capndx + 8, + command); + + /* + * PASS3: Go throu all AGP devices and update the + * command registers. + */ + + while ((device = pci_find_class(PCI_CLASS_DISPLAY_VGA << 8, + device)) != NULL) { + pci_read_config_dword(device, 0x04, &scratch); + + if (!(scratch & 0x00100000)) + continue; + + pci_read_config_byte(device, 0x34, &cap_ptr); + + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(device, + cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr != 0x00) + pci_write_config_dword(device, cap_ptr + 8, command); + } +} + +static int agp_generic_create_gatt_table(void) +{ + char *table; + char *table_end; + int size; + int page_order; + int num_entries; + int i; + void *temp; + + /* The generic routines can't handle 2 level gatt's */ + if (agp_bridge.size_type == LVL2_APER_SIZE) { + return -EINVAL; + } + + table = NULL; + i = agp_bridge.aperture_size_idx; + temp = agp_bridge.current_size; + size = page_order = num_entries = 0; + + if (agp_bridge.size_type != FIXED_APER_SIZE) { + do { + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + size = A_SIZE_8(temp)->size; + page_order = + A_SIZE_8(temp)->page_order; + num_entries = + A_SIZE_8(temp)->num_entries; + break; + case U16_APER_SIZE: + size = A_SIZE_16(temp)->size; + page_order = A_SIZE_16(temp)->page_order; + num_entries = A_SIZE_16(temp)->num_entries; + break; + case U32_APER_SIZE: + size = A_SIZE_32(temp)->size; + page_order = A_SIZE_32(temp)->page_order; + num_entries = A_SIZE_32(temp)->num_entries; + break; + /* This case will never really happen. */ + case FIXED_APER_SIZE: + case LVL2_APER_SIZE: + default: + size = page_order = num_entries = 0; + break; + } + + table = (char *) __get_free_pages(GFP_KERNEL, + page_order); + + if (table == NULL) { + i++; + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + agp_bridge.current_size = A_IDX8(); + break; + case U16_APER_SIZE: + agp_bridge.current_size = A_IDX16(); + break; + case U32_APER_SIZE: + agp_bridge.current_size = A_IDX32(); + break; + /* This case will never really + * happen. + */ + case FIXED_APER_SIZE: + case LVL2_APER_SIZE: + default: + agp_bridge.current_size = + agp_bridge.current_size; + break; + } + } else { + agp_bridge.aperture_size_idx = i; + } + } while ((table == NULL) && + (i < agp_bridge.num_aperture_sizes)); + } else { + size = ((aper_size_info_fixed *) temp)->size; + page_order = ((aper_size_info_fixed *) temp)->page_order; + num_entries = ((aper_size_info_fixed *) temp)->num_entries; + table = (char *) __get_free_pages(GFP_KERNEL, page_order); + } + + if (table == NULL) { + return -ENOMEM; + } + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) { + set_bit(PG_reserved, &mem_map[i].flags); + } + + agp_bridge.gatt_table = (unsigned long *) table; + agp_bridge.gatt_table_real = (unsigned long *) table; + CACHE_FLUSH(); + + if (agp_bridge.gatt_table == NULL) { + for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) { + clear_bit(PG_reserved, &mem_map[i].flags); + } + + free_pages((unsigned long) table, page_order); + + return -ENOMEM; + } + agp_bridge.gatt_bus_addr = virt_to_phys(agp_bridge.gatt_table_real); + + for (i = 0; i < num_entries; i++) { + agp_bridge.gatt_table[i] = + (unsigned long) agp_bridge.scratch_page; + } + + return 0; +} + +static int agp_generic_free_gatt_table(void) +{ + int i; + int page_order; + char *table, *table_end; + void *temp; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + page_order = A_SIZE_8(temp)->page_order; + break; + case U16_APER_SIZE: + page_order = A_SIZE_16(temp)->page_order; + break; + case U32_APER_SIZE: + page_order = A_SIZE_32(temp)->page_order; + break; + case FIXED_APER_SIZE: + page_order = A_SIZE_FIX(temp)->page_order; + break; + case LVL2_APER_SIZE: + /* The generic routines can't deal with 2 level gatt's */ + return -EINVAL; + break; + default: + page_order = 0; + break; + } + + /* Do not worry about freeing memory, because if this is + * called, then all agp memory is deallocated and removed + * from the table. + */ + + table = (char *) agp_bridge.gatt_table_real; + table_end = table + ((PAGE_SIZE * (1 << page_order)) - 1); + + for (i = MAP_NR(table); i <= MAP_NR(table_end); i++) { + clear_bit(PG_reserved, &mem_map[i].flags); + } + + free_pages((unsigned long) agp_bridge.gatt_table_real, page_order); + return 0; +} + +static int agp_generic_insert_memory(agp_memory * mem, + off_t pg_start, int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge.current_size; + + switch (agp_bridge.size_type) { + case U8_APER_SIZE: + num_entries = A_SIZE_8(temp)->num_entries; + break; + case U16_APER_SIZE: + num_entries = A_SIZE_16(temp)->num_entries; + break; + case U32_APER_SIZE: + num_entries = A_SIZE_32(temp)->num_entries; + break; + case FIXED_APER_SIZE: + num_entries = A_SIZE_FIX(temp)->num_entries; + break; + case LVL2_APER_SIZE: + /* The generic routines can't deal with 2 level gatt's */ + return -EINVAL; + break; + default: + num_entries = 0; + break; + } + + if (type != 0 || mem->type != 0) { + /* The generic routines know nothing of memory types */ + return -EINVAL; + } + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + j = pg_start; + + while (j < (pg_start + mem->page_count)) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + j++; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + agp_bridge.gatt_table[j] = mem->memory[i]; + } + /* Do cache flush all the time since the table is not + * mapped uncached */ + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; +} + +static int agp_generic_remove_memory(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + + if (type != 0 || mem->type != 0) { + /* The generic routines know nothing of memory types */ + return -EINVAL; + } + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + agp_bridge.gatt_table[i] = + (unsigned long) agp_bridge.scratch_page; + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static agp_memory *agp_generic_alloc_by_type(size_t page_count, int type) +{ + return NULL; +} + +static void agp_generic_free_by_type(agp_memory * curr) +{ + if (curr->memory != NULL) { + vfree(curr->memory); + } + agp_free_key(curr->key); + kfree(curr); +} + +void agp_enable(u32 mode) +{ + agp_bridge.agp_enable(mode); +} + +/* End - Generic Agp routines */ + +static aper_size_info_fixed intel_i810_sizes[] = +{ + {64, 16384, 4}, + /* The 32M mode still requires a 64k gatt */ + {32, 8192, 4} +}; + +#define AGP_DCACHE_MEMORY 1 +#define AGP_PHYS_MEMORY 2 + +static gatt_mask intel_i810_masks[] = +{ + {I810_PTE_VALID, 0}, + {(I810_PTE_VALID | I810_PTE_LOCAL), AGP_DCACHE_MEMORY}, + {I810_PTE_VALID, 0} +}; + +static struct _intel_i810_private { + struct pci_dev *i810_dev; /* device one */ + volatile u8 *registers; + int num_dcache_entries; +} intel_i810_private; + +static int intel_i810_fetch_size(void) +{ + u32 smram_miscc; + aper_size_info_fixed *values; + + pci_read_config_dword(agp_bridge.dev, I810_SMRAM_MISCC, &smram_miscc); + values = A_SIZE_FIX(agp_bridge.aperture_sizes); + + if ((smram_miscc & I810_GMS) == I810_GMS_DISABLE) { + printk(KERN_WARNING PFX "i810 is disabled\n"); + return 0; + } + if ((smram_miscc & I810_GFX_MEM_WIN_SIZE) == I810_GFX_MEM_WIN_32M) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + 1); + agp_bridge.aperture_size_idx = 1; + return values[1].size; + } else { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values); + agp_bridge.aperture_size_idx = 0; + return values[0].size; + } + + return 0; +} + +static int intel_i810_configure(void) +{ + aper_size_info_fixed *current_size; + u32 temp; + int i; + + current_size = A_SIZE_FIX(agp_bridge.current_size); + + pci_read_config_dword(intel_i810_private.i810_dev, I810_MMADDR, &temp); + temp &= 0xfff80000; + + intel_i810_private.registers = + (volatile u8 *) ioremap(temp, 128 * 4096); + + if ((INREG32(intel_i810_private.registers, I810_DRAM_CTL) + & I810_DRAM_ROW_0) == I810_DRAM_ROW_0_SDRAM) { + /* This will need to be dynamically assigned */ + printk(KERN_INFO PFX "detected 4MB dedicated video ram.\n"); + intel_i810_private.num_dcache_entries = 1024; + } + pci_read_config_dword(intel_i810_private.i810_dev, I810_GMADDR, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, + agp_bridge.gatt_bus_addr | I810_PGETBL_ENABLED); + CACHE_FLUSH(); + + if (agp_bridge.needs_scratch_page == TRUE) { + for (i = 0; i < current_size->num_entries; i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + agp_bridge.scratch_page); + } + } + return 0; +} + +static void intel_i810_cleanup(void) +{ + OUTREG32(intel_i810_private.registers, I810_PGETBL_CTL, 0); + iounmap((void *) intel_i810_private.registers); +} + +static void intel_i810_tlbflush(agp_memory * mem) +{ + return; +} + +static void intel_i810_agp_enable(u32 mode) +{ + return; +} + +static int intel_i810_insert_entries(agp_memory * mem, off_t pg_start, + int type) +{ + int i, j, num_entries; + void *temp; + + temp = agp_bridge.current_size; + num_entries = A_SIZE_FIX(temp)->num_entries; + + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + for (j = pg_start; j < (pg_start + mem->page_count); j++) { + if (!PGE_EMPTY(agp_bridge.gatt_table[j])) { + return -EBUSY; + } + } + + if (type != 0 || mem->type != 0) { + if ((type == AGP_DCACHE_MEMORY) && + (mem->type == AGP_DCACHE_MEMORY)) { + /* special insert */ + CACHE_FLUSH(); + for (i = pg_start; + i < (pg_start + mem->page_count); i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + (i * 4096) | I810_PTE_LOCAL | + I810_PTE_VALID); + } + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; + } + if((type == AGP_PHYS_MEMORY) && + (mem->type == AGP_PHYS_MEMORY)) { + goto insert; + } + return -EINVAL; + } + +insert: + CACHE_FLUSH(); + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (j * 4), mem->memory[i]); + } + CACHE_FLUSH(); + + agp_bridge.tlb_flush(mem); + return 0; +} + +static int intel_i810_remove_entries(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + OUTREG32(intel_i810_private.registers, + I810_PTE_BASE + (i * 4), + agp_bridge.scratch_page); + } + + agp_bridge.tlb_flush(mem); + return 0; +} + +static agp_memory *intel_i810_alloc_by_type(size_t pg_count, int type) +{ + agp_memory *new; + + if (type == AGP_DCACHE_MEMORY) { + if (pg_count != intel_i810_private.num_dcache_entries) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + new->type = AGP_DCACHE_MEMORY; + new->page_count = pg_count; + new->num_scratch_pages = 0; + vfree(new->memory); + MOD_INC_USE_COUNT; + return new; + } + if(type == AGP_PHYS_MEMORY) { + /* The I810 requires a physical address to program + * it's mouse pointer into hardware. However the + * Xserver still writes to it through the agp + * aperture + */ + if (pg_count != 1) { + return NULL; + } + new = agp_create_memory(1); + + if (new == NULL) { + return NULL; + } + MOD_INC_USE_COUNT; + new->memory[0] = agp_alloc_page(); + + if (new->memory[0] == 0) { + /* Free this structure */ + agp_free_memory(new); + return NULL; + } + new->memory[0] = + agp_bridge.mask_memory( + virt_to_phys((void *) new->memory[0]), + type); + new->page_count = 1; + new->num_scratch_pages = 1; + new->type = AGP_PHYS_MEMORY; + new->physical = virt_to_phys((void *) new->memory[0]); + return new; + } + + return NULL; +} + +static void intel_i810_free_by_type(agp_memory * curr) +{ + agp_free_key(curr->key); + if(curr->type == AGP_PHYS_MEMORY) { + agp_destroy_page((unsigned long) + phys_to_virt(curr->memory[0])); + vfree(curr->memory); + } + kfree(curr); + MOD_DEC_USE_COUNT; +} + +static unsigned long intel_i810_mask_memory(unsigned long addr, int type) +{ + /* Type checking must be done elsewhere */ + return addr | agp_bridge.masks[type].mask; +} + +static int __init intel_i810_setup(struct pci_dev *i810_dev) +{ + intel_i810_private.i810_dev = i810_dev; + + agp_bridge.masks = intel_i810_masks; + agp_bridge.num_of_masks = 2; + agp_bridge.aperture_sizes = (void *) intel_i810_sizes; + agp_bridge.size_type = FIXED_APER_SIZE; + agp_bridge.num_aperture_sizes = 2; + agp_bridge.dev_private_data = (void *) &intel_i810_private; + agp_bridge.needs_scratch_page = TRUE; + agp_bridge.configure = intel_i810_configure; + agp_bridge.fetch_size = intel_i810_fetch_size; + agp_bridge.cleanup = intel_i810_cleanup; + agp_bridge.tlb_flush = intel_i810_tlbflush; + agp_bridge.mask_memory = intel_i810_mask_memory; + agp_bridge.agp_enable = intel_i810_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = intel_i810_insert_entries; + agp_bridge.remove_memory = intel_i810_remove_entries; + agp_bridge.alloc_by_type = intel_i810_alloc_by_type; + agp_bridge.free_by_type = intel_i810_free_by_type; + + return 0; +} + + +#ifdef CONFIG_AGP_INTEL + +static int intel_fetch_size(void) +{ + int i; + u16 temp; + aper_size_info_16 *values; + + pci_read_config_word(agp_bridge.dev, INTEL_APSIZE, &temp); + values = A_SIZE_16(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static void intel_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2200); + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); +} + +static void intel_cleanup(void) +{ + u16 temp; + aper_size_info_16 *previous_size; + + previous_size = A_SIZE_16(agp_bridge.previous_size); + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, temp & ~(1 << 9)); + pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, + previous_size->size_value); +} + +static int intel_configure(void) +{ + u32 temp; + u16 temp2; + aper_size_info_16 *current_size; + + current_size = A_SIZE_16(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_word(agp_bridge.dev, INTEL_APSIZE, + current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x2280); + + /* paccfg/nbxcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_NBXCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_NBXCFG, + (temp2 & ~(1 << 10)) | (1 << 9)); + /* clear any possible error conditions */ + pci_write_config_byte(agp_bridge.dev, INTEL_ERRSTS + 1, 7); + return 0; +} + +static int intel_840_configure(void) +{ + u32 temp; + u16 temp2; + aper_size_info_16 *current_size; + + current_size = A_SIZE_16(agp_bridge.current_size); + + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, INTEL_APSIZE, + (char)current_size->size_value); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, INTEL_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* attbase - aperture base */ + pci_write_config_dword(agp_bridge.dev, INTEL_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* agpctrl */ + pci_write_config_dword(agp_bridge.dev, INTEL_AGPCTRL, 0x0000); + + /* mcgcfg */ + pci_read_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, &temp2); + pci_write_config_word(agp_bridge.dev, INTEL_I840_MCHCFG, + temp2 | (1 << 9)); + /* clear any possible error conditions */ + pci_write_config_word(agp_bridge.dev, INTEL_I840_ERRSTS, 0xc000); + return 0; +} + +static unsigned long intel_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + + +/* Setup function */ +static gatt_mask intel_generic_masks[] = +{ + {0x00000017, 0} +}; + +static aper_size_info_16 intel_generic_sizes[7] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 32}, + {64, 16384, 4, 48}, + {32, 8192, 3, 56}, + {16, 4096, 2, 60}, + {8, 2048, 1, 62}, + {4, 1024, 0, 63} +}; + +static int __init intel_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_generic_sizes; + agp_bridge.size_type = U16_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_configure; + agp_bridge.fetch_size = intel_fetch_size; + agp_bridge.cleanup = intel_cleanup; + agp_bridge.tlb_flush = intel_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; + + (void) pdev; /* unused */ +} + +static int __init intel_840_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = intel_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) intel_generic_sizes; + agp_bridge.size_type = U16_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = intel_840_configure; + agp_bridge.fetch_size = intel_fetch_size; + agp_bridge.cleanup = intel_cleanup; + agp_bridge.tlb_flush = intel_tlbflush; + agp_bridge.mask_memory = intel_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; + + (void) pdev; /* unused */ +} + +#endif /* CONFIG_AGP_INTEL */ + +#ifdef CONFIG_AGP_VIA + +static int via_fetch_size(void) +{ + int i; + u8 temp; + aper_size_info_8 *values; + + values = A_SIZE_8(agp_bridge.aperture_sizes); + pci_read_config_byte(agp_bridge.dev, VIA_APSIZE, &temp); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int via_configure(void) +{ + u32 temp; + aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + /* aperture size */ + pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, + current_size->size_value); + /* address to map too */ + pci_read_config_dword(agp_bridge.dev, VIA_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + + /* GART control register */ + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); + + /* attbase - aperture GATT base */ + pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE, + (agp_bridge.gatt_bus_addr & 0xfffff000) | 3); + return 0; +} + +static void via_cleanup(void) +{ + aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_write_config_dword(agp_bridge.dev, VIA_ATTBASE, 0); + pci_write_config_byte(agp_bridge.dev, VIA_APSIZE, + previous_size->size_value); +} + +static void via_tlbflush(agp_memory * mem) +{ + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000008f); + pci_write_config_dword(agp_bridge.dev, VIA_GARTCTRL, 0x0000000f); +} + +static unsigned long via_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static aper_size_info_8 via_generic_sizes[7] = +{ + {256, 65536, 6, 0}, + {128, 32768, 5, 128}, + {64, 16384, 4, 192}, + {32, 8192, 3, 224}, + {16, 4096, 2, 240}, + {8, 2048, 1, 248}, + {4, 1024, 0, 252} +}; + +static gatt_mask via_generic_masks[] = +{ + {0x00000000, 0} +}; + +static int __init via_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = via_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) via_generic_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = via_configure; + agp_bridge.fetch_size = via_fetch_size; + agp_bridge.cleanup = via_cleanup; + agp_bridge.tlb_flush = via_tlbflush; + agp_bridge.mask_memory = via_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; + + (void) pdev; /* unused */ +} + +#endif /* CONFIG_AGP_VIA */ + +#ifdef CONFIG_AGP_SIS + +static int sis_fetch_size(void) +{ + u8 temp_size; + int i; + aper_size_info_8 *values; + + pci_read_config_byte(agp_bridge.dev, SIS_APSIZE, &temp_size); + values = A_SIZE_8(agp_bridge.aperture_sizes); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if ((temp_size == values[i].size_value) || + ((temp_size & ~(0x03)) == + (values[i].size_value & ~(0x03)))) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + + +static void sis_tlbflush(agp_memory * mem) +{ + pci_write_config_byte(agp_bridge.dev, SIS_TLBFLUSH, 0x02); +} + +static int sis_configure(void) +{ + u32 temp; + aper_size_info_8 *current_size; + + current_size = A_SIZE_8(agp_bridge.current_size); + pci_write_config_byte(agp_bridge.dev, SIS_TLBCNTRL, 0x05); + pci_read_config_dword(agp_bridge.dev, SIS_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + pci_write_config_dword(agp_bridge.dev, SIS_ATTBASE, + agp_bridge.gatt_bus_addr); + pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, + current_size->size_value); + return 0; +} + +static void sis_cleanup(void) +{ + aper_size_info_8 *previous_size; + + previous_size = A_SIZE_8(agp_bridge.previous_size); + pci_write_config_byte(agp_bridge.dev, SIS_APSIZE, + (previous_size->size_value & ~(0x03))); +} + +static unsigned long sis_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + +static aper_size_info_8 sis_generic_sizes[7] = +{ + {256, 65536, 6, 99}, + {128, 32768, 5, 83}, + {64, 16384, 4, 67}, + {32, 8192, 3, 51}, + {16, 4096, 2, 35}, + {8, 2048, 1, 19}, + {4, 1024, 0, 3} +}; + +static gatt_mask sis_generic_masks[] = +{ + {0x00000000, 0} +}; + +static int __init sis_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = sis_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) sis_generic_sizes; + agp_bridge.size_type = U8_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = sis_configure; + agp_bridge.fetch_size = sis_fetch_size; + agp_bridge.cleanup = sis_cleanup; + agp_bridge.tlb_flush = sis_tlbflush; + agp_bridge.mask_memory = sis_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; +} + +#endif /* CONFIG_AGP_SIS */ + +#ifdef CONFIG_AGP_AMD + +typedef struct _amd_page_map { + unsigned long *real; + unsigned long *remapped; +} amd_page_map; + +static struct _amd_irongate_private { + volatile u8 *registers; + amd_page_map **gatt_pages; + int num_tables; +} amd_irongate_private; + +static int amd_create_page_map(amd_page_map *page_map) +{ + int i; + + page_map->real = (unsigned long *) __get_free_page(GFP_KERNEL); + if (page_map->real == NULL) { + return -ENOMEM; + } + set_bit(PG_reserved, &mem_map[MAP_NR(page_map->real)].flags); + page_map->remapped = page_map->real; + + for(i = 0; i < PAGE_SIZE / sizeof(unsigned long); i++) { + page_map->remapped[i] = agp_bridge.scratch_page; + } + CACHE_FLUSH(); + + return 0; +} + +static void amd_free_page_map(amd_page_map *page_map) +{ + clear_bit(PG_reserved, + &mem_map[MAP_NR(page_map->real)].flags); + free_page((unsigned long) page_map->real); +} + +static void amd_free_gatt_pages(void) +{ + int i; + amd_page_map **tables; + amd_page_map *entry; + + tables = amd_irongate_private.gatt_pages; + for(i = 0; i < amd_irongate_private.num_tables; i++) { + entry = tables[i]; + if (entry != NULL) { + if (entry->real != NULL) { + amd_free_page_map(entry); + } + kfree(entry); + } + } + kfree(tables); +} + +static int amd_create_gatt_pages(int nr_tables) +{ + amd_page_map **tables; + amd_page_map *entry; + int retval = 0; + int i; + + tables = kmalloc((nr_tables + 1) * sizeof(amd_page_map *), + GFP_KERNEL); + if (tables == NULL) { + return -ENOMEM; + } + memset(tables, 0, sizeof(amd_page_map *) * (nr_tables + 1)); + for (i = 0; i < nr_tables; i++) { + entry = kmalloc(sizeof(amd_page_map), GFP_KERNEL); + if (entry == NULL) { + retval = -ENOMEM; + break; + } + memset(entry, 0, sizeof(amd_page_map)); + tables[i] = entry; + retval = amd_create_page_map(entry); + if (retval != 0) break; + } + amd_irongate_private.num_tables = nr_tables; + amd_irongate_private.gatt_pages = tables; + + if (retval != 0) amd_free_gatt_pages(); + + return retval; +} + +/* Since we don't need contigious memory we just try + * to get the gatt table once + */ + +#define GET_PAGE_DIR_OFF(addr) (addr >> 22) +#define GET_PAGE_DIR_IDX(addr) (GET_PAGE_DIR_OFF(addr) - \ + GET_PAGE_DIR_OFF(agp_bridge.gart_bus_addr)) +#define GET_GATT_OFF(addr) ((addr & 0x003ff000) >> 12) +#define GET_GATT(addr) (amd_irongate_private.gatt_pages[\ + GET_PAGE_DIR_IDX(addr)]->remapped) + +static int amd_create_gatt_table(void) +{ + aper_size_info_lvl2 *value; + amd_page_map page_dir; + unsigned long addr; + int retval; + u32 temp; + int i; + + value = A_SIZE_LVL2(agp_bridge.current_size); + retval = amd_create_page_map(&page_dir); + if (retval != 0) { + return retval; + } + + retval = amd_create_gatt_pages(value->num_entries / 1024); + if (retval != 0) { + amd_free_page_map(&page_dir); + return retval; + } + + agp_bridge.gatt_table_real = page_dir.real; + agp_bridge.gatt_table = page_dir.remapped; + agp_bridge.gatt_bus_addr = virt_to_bus(page_dir.real); + + /* Get the address for the gart region. + * This is a bus address even on the alpha, b/c its + * used to program the agp master not the cpu + */ + + pci_read_config_dword(agp_bridge.dev, AMD_APBASE, &temp); + addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + agp_bridge.gart_bus_addr = addr; + + /* Calculate the agp offset */ + for(i = 0; i < value->num_entries / 1024; i++, addr += 0x00400000) { + page_dir.remapped[GET_PAGE_DIR_OFF(addr)] = + virt_to_bus(amd_irongate_private.gatt_pages[i]->real); + page_dir.remapped[GET_PAGE_DIR_OFF(addr)] |= 0x00000001; + } + + return 0; +} + +static int amd_free_gatt_table(void) +{ + amd_page_map page_dir; + + page_dir.real = agp_bridge.gatt_table_real; + page_dir.remapped = agp_bridge.gatt_table; + + amd_free_gatt_pages(); + amd_free_page_map(&page_dir); + return 0; +} + +static int amd_irongate_fetch_size(void) +{ + int i; + u32 temp; + aper_size_info_lvl2 *values; + + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = (temp & 0x0000000e); + values = A_SIZE_LVL2(agp_bridge.aperture_sizes); + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static int amd_irongate_configure(void) +{ + aper_size_info_lvl2 *current_size; + u32 temp; + u16 enable_reg; + + current_size = A_SIZE_LVL2(agp_bridge.current_size); + + /* Get the memory mapped registers */ + pci_read_config_dword(agp_bridge.dev, AMD_MMBASE, &temp); + temp = (temp & PCI_BASE_ADDRESS_MEM_MASK); + amd_irongate_private.registers = (volatile u8 *) ioremap(temp, 4096); + + /* Write out the address of the gatt table */ + OUTREG32(amd_irongate_private.registers, AMD_ATTBASE, + agp_bridge.gatt_bus_addr); + + /* Write the Sync register */ + pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL, 0x80); + + /* Set indexing mode */ + pci_write_config_byte(agp_bridge.dev, AMD_MODECNTL2, 0x00); + + /* Write the enable register */ + enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); + enable_reg = (enable_reg | 0x0004); + OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); + + /* Write out the size register */ + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = (((temp & ~(0x0000000e)) | current_size->size_value) + | 0x00000001); + pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); + + /* Flush the tlb */ + OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); + + return 0; +} + +static void amd_irongate_cleanup(void) +{ + aper_size_info_lvl2 *previous_size; + u32 temp; + u16 enable_reg; + + previous_size = A_SIZE_LVL2(agp_bridge.previous_size); + + enable_reg = INREG16(amd_irongate_private.registers, AMD_GARTENABLE); + enable_reg = (enable_reg & ~(0x0004)); + OUTREG16(amd_irongate_private.registers, AMD_GARTENABLE, enable_reg); + + /* Write back the previous size and disable gart translation */ + pci_read_config_dword(agp_bridge.dev, AMD_APSIZE, &temp); + temp = ((temp & ~(0x0000000f)) | previous_size->size_value); + pci_write_config_dword(agp_bridge.dev, AMD_APSIZE, temp); + iounmap((void *) amd_irongate_private.registers); +} + +/* + * This routine could be implemented by taking the addresses + * written to the GATT, and flushing them individually. However + * currently it just flushes the whole table. Which is probably + * more efficent, since agp_memory blocks can be a large number of + * entries. + */ + +static void amd_irongate_tlbflush(agp_memory * temp) +{ + OUTREG32(amd_irongate_private.registers, AMD_TLBFLUSH, 0x00000001); +} + +static unsigned long amd_irongate_mask_memory(unsigned long addr, int type) +{ + /* Only type 0 is supported by the irongate */ + + return addr | agp_bridge.masks[0].mask; +} + +static int amd_insert_memory(agp_memory * mem, + off_t pg_start, int type) +{ + int i, j, num_entries; + unsigned long *cur_gatt; + unsigned long addr; + + num_entries = A_SIZE_LVL2(agp_bridge.current_size)->num_entries; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + if ((pg_start + mem->page_count) > num_entries) { + return -EINVAL; + } + + j = pg_start; + while (j < (pg_start + mem->page_count)) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + if (!PGE_EMPTY(cur_gatt[GET_GATT_OFF(addr)])) { + return -EBUSY; + } + j++; + } + + for (i = 0, j = pg_start; i < mem->page_count; i++, j++) { + addr = (j * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = mem->memory[i]; + } + + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; +} + +static int amd_remove_memory(agp_memory * mem, off_t pg_start, + int type) +{ + int i; + unsigned long *cur_gatt; + unsigned long addr; + + if (type != 0 || mem->type != 0) { + return -EINVAL; + } + for (i = pg_start; i < (mem->page_count + pg_start); i++) { + addr = (i * PAGE_SIZE) + agp_bridge.gart_bus_addr; + cur_gatt = GET_GATT(addr); + cur_gatt[GET_GATT_OFF(addr)] = + (unsigned long) agp_bridge.scratch_page; + } + + CACHE_FLUSH(); + agp_bridge.tlb_flush(mem); + return 0; +} + +static aper_size_info_lvl2 amd_irongate_sizes[7] = +{ + {2048, 524288, 0x0000000c}, + {1024, 262144, 0x0000000a}, + {512, 131072, 0x00000008}, + {256, 65536, 0x00000006}, + {128, 32768, 0x00000004}, + {64, 16384, 0x00000002}, + {32, 8192, 0x00000000} +}; + +static gatt_mask amd_irongate_masks[] = +{ + {0x00000001, 0} +}; + +static int __init amd_irongate_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = amd_irongate_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) amd_irongate_sizes; + agp_bridge.size_type = LVL2_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = (void *) &amd_irongate_private; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = amd_irongate_configure; + agp_bridge.fetch_size = amd_irongate_fetch_size; + agp_bridge.cleanup = amd_irongate_cleanup; + agp_bridge.tlb_flush = amd_irongate_tlbflush; + agp_bridge.mask_memory = amd_irongate_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = amd_create_gatt_table; + agp_bridge.free_gatt_table = amd_free_gatt_table; + agp_bridge.insert_memory = amd_insert_memory; + agp_bridge.remove_memory = amd_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; + + (void) pdev; /* unused */ +} + +#endif /* CONFIG_AGP_AMD */ + +#ifdef CONFIG_AGP_ALI + +static int ali_fetch_size(void) +{ + int i; + u32 temp; + aper_size_info_32 *values; + + pci_read_config_dword(agp_bridge.dev, ALI_ATTBASE, &temp); + temp &= ~(0xfffffff0); + values = A_SIZE_32(agp_bridge.aperture_sizes); + + for (i = 0; i < agp_bridge.num_aperture_sizes; i++) { + if (temp == values[i].size_value) { + agp_bridge.previous_size = + agp_bridge.current_size = (void *) (values + i); + agp_bridge.aperture_size_idx = i; + return values[i].size; + } + } + + return 0; +} + +static void ali_tlbflush(agp_memory * mem) +{ + u32 temp; + + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, + ((temp & 0xffffff00) | 0x00000090)); + pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, + ((temp & 0xffffff00) | 0x00000010)); +} + +static void ali_cleanup(void) +{ + aper_size_info_32 *previous_size; + u32 temp; + + previous_size = A_SIZE_32(agp_bridge.previous_size); + + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, + ((temp & 0xffffff00) | 0x00000090)); + pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, + previous_size->size_value); +} + +static int ali_configure(void) +{ + u32 temp; + aper_size_info_32 *current_size; + + current_size = A_SIZE_32(agp_bridge.current_size); + + /* aperture size and gatt addr */ + pci_write_config_dword(agp_bridge.dev, ALI_ATTBASE, + agp_bridge.gatt_bus_addr | current_size->size_value); + + /* tlb control */ + pci_read_config_dword(agp_bridge.dev, ALI_TLBCTRL, &temp); + pci_write_config_dword(agp_bridge.dev, ALI_TLBCTRL, + ((temp & 0xffffff00) | 0x00000010)); + + /* address to map to */ + pci_read_config_dword(agp_bridge.dev, ALI_APBASE, &temp); + agp_bridge.gart_bus_addr = (temp & PCI_BASE_ADDRESS_MEM_MASK); + return 0; +} + +static unsigned long ali_mask_memory(unsigned long addr, int type) +{ + /* Memory type is ignored */ + + return addr | agp_bridge.masks[0].mask; +} + + +/* Setup function */ +static gatt_mask ali_generic_masks[] = +{ + {0x00000000, 0} +}; + +static aper_size_info_32 ali_generic_sizes[7] = +{ + {256, 65536, 6, 10}, + {128, 32768, 5, 9}, + {64, 16384, 4, 8}, + {32, 8192, 3, 7}, + {16, 4096, 2, 6}, + {8, 2048, 1, 4}, + {4, 1024, 0, 3} +}; + +static int __init ali_generic_setup (struct pci_dev *pdev) +{ + agp_bridge.masks = ali_generic_masks; + agp_bridge.num_of_masks = 1; + agp_bridge.aperture_sizes = (void *) ali_generic_sizes; + agp_bridge.size_type = U32_APER_SIZE; + agp_bridge.num_aperture_sizes = 7; + agp_bridge.dev_private_data = NULL; + agp_bridge.needs_scratch_page = FALSE; + agp_bridge.configure = ali_configure; + agp_bridge.fetch_size = ali_fetch_size; + agp_bridge.cleanup = ali_cleanup; + agp_bridge.tlb_flush = ali_tlbflush; + agp_bridge.mask_memory = ali_mask_memory; + agp_bridge.agp_enable = agp_generic_agp_enable; + agp_bridge.cache_flush = global_cache_flush; + agp_bridge.create_gatt_table = agp_generic_create_gatt_table; + agp_bridge.free_gatt_table = agp_generic_free_gatt_table; + agp_bridge.insert_memory = agp_generic_insert_memory; + agp_bridge.remove_memory = agp_generic_remove_memory; + agp_bridge.alloc_by_type = agp_generic_alloc_by_type; + agp_bridge.free_by_type = agp_generic_free_by_type; + + return 0; + + (void) pdev; /* unused */ +} + +#endif /* CONFIG_AGP_ALI */ + + +/* per-chipset initialization data. + * note -- all chipsets for a single vendor MUST be grouped together + */ +static struct { + unsigned short device_id; /* first, to make table easier to read */ + unsigned short vendor_id; + enum chipset_type chipset; + const char *vendor_name; + const char *chipset_name; + int (*chipset_setup) (struct pci_dev *pdev); +} agp_bridge_info[] __initdata = { + +#ifdef CONFIG_AGP_ALI + { PCI_DEVICE_ID_AL_M1541_0, + PCI_VENDOR_ID_AL, + ALI_M1541, + "Ali", + "M1541", + ali_generic_setup }, + { 0, + PCI_VENDOR_ID_AL, + ALI_GENERIC, + "Ali", + "Generic", + ali_generic_setup }, +#endif /* CONFIG_AGP_ALI */ + +#ifdef CONFIG_AGP_AMD + { PCI_DEVICE_ID_AMD_IRONGATE_0, + PCI_VENDOR_ID_AMD, + AMD_IRONGATE, + "AMD", + "Irongate", + amd_irongate_setup }, + { 0, + PCI_VENDOR_ID_AMD, + AMD_GENERIC, + "AMD", + "Generic", + amd_irongate_setup }, +#endif /* CONFIG_AGP_AMD */ + +#ifdef CONFIG_AGP_INTEL + { PCI_DEVICE_ID_INTEL_82443LX_0, + PCI_VENDOR_ID_INTEL, + INTEL_LX, + "Intel", + "440LX", + intel_generic_setup }, + { PCI_DEVICE_ID_INTEL_82443BX_0, + PCI_VENDOR_ID_INTEL, + INTEL_BX, + "Intel", + "440BX", + intel_generic_setup }, + { PCI_DEVICE_ID_INTEL_82443GX_0, + PCI_VENDOR_ID_INTEL, + INTEL_GX, + "Intel", + "440GX", + intel_generic_setup }, + { PCI_DEVICE_ID_INTEL_840_0, + PCI_VENDOR_ID_INTEL, + INTEL_I840, + "Intel", + "i840", + intel_840_setup }, + { 0, + PCI_VENDOR_ID_INTEL, + INTEL_GENERIC, + "Intel", + "Generic", + intel_generic_setup }, +#endif /* CONFIG_AGP_INTEL */ + +#ifdef CONFIG_AGP_SIS + { PCI_DEVICE_ID_SI_630, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "630", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_540, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "540", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_620, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "620", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_530, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "530", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_630, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "Generic", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_540, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "Generic", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_620, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "Generic", + sis_generic_setup }, + { PCI_DEVICE_ID_SI_530, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "Generic", + sis_generic_setup }, + { 0, + PCI_VENDOR_ID_SI, + SIS_GENERIC, + "SiS", + "Generic", + sis_generic_setup }, +#endif /* CONFIG_AGP_SIS */ + +#ifdef CONFIG_AGP_VIA + { PCI_DEVICE_ID_VIA_8501_0, + PCI_VENDOR_ID_VIA, + VIA_MVP4, + "Via", + "MVP4", + via_generic_setup }, + { PCI_DEVICE_ID_VIA_82C597_0, + PCI_VENDOR_ID_VIA, + VIA_VP3, + "Via", + "VP3", + via_generic_setup }, + { PCI_DEVICE_ID_VIA_82C598_0, + PCI_VENDOR_ID_VIA, + VIA_MVP3, + "Via", + "MVP3", + via_generic_setup }, + { PCI_DEVICE_ID_VIA_8371_0, + PCI_VENDOR_ID_VIA, + VIA_APOLLO_SUPER, + "Via", + "Apollo Super", + via_generic_setup }, + { PCI_DEVICE_ID_VIA_82C691_0, + PCI_VENDOR_ID_VIA, + VIA_APOLLO_PRO, + "Via", + "Apollo Pro", + via_generic_setup }, + { 0, + PCI_VENDOR_ID_VIA, + VIA_GENERIC, + "Via", + "Generic", + via_generic_setup }, +#endif /* CONFIG_AGP_VIA */ + + { 0, }, /* dummy final entry, always present */ +}; + + +/* scan table above for supported devices */ +static int __init agp_lookup_host_bridge (struct pci_dev *pdev) +{ + int i; + + for (i = 0; i < arraysize (agp_bridge_info); i++) + if (pdev->vendor == agp_bridge_info[i].vendor_id) + break; + + if (i >= arraysize (agp_bridge_info)) { + printk (KERN_DEBUG PFX "unsupported bridge\n"); + return -ENODEV; + } + + while ((i < arraysize (agp_bridge_info)) && + (agp_bridge_info[i].vendor_id == pdev->vendor)) { + if (pdev->device == agp_bridge_info[i].device_id) { + printk (KERN_INFO PFX "Detected %s %s chipset\n", + agp_bridge_info[i].vendor_name, + agp_bridge_info[i].chipset_name); + agp_bridge.type = agp_bridge_info[i].chipset; + return agp_bridge_info[i].chipset_setup (pdev); + } + + i++; + } + + i--; /* point to vendor generic entry (device_id == 0) */ + + /* try init anyway, if user requests it AND + * there is a 'generic' bridge entry for this vendor */ + if (agp_try_unsupported && agp_bridge_info[i].device_id == 0) { + printk(KERN_WARNING PFX "Trying generic %s routines" + " for device id: %x\n", + agp_bridge_info[i].vendor_name, pdev->device); + agp_bridge.type = agp_bridge_info[i].chipset; + return agp_bridge_info[i].chipset_setup (pdev); + } + + printk(KERN_ERR PFX "Unsupported %s chipset," + " you might want to try " + "agp_try_unsupported=1.\n", + agp_bridge_info[i].vendor_name); + return -ENODEV; +} + + +/* Supported Device Scanning routine */ + +static int __init agp_find_supported_device(void) +{ + struct pci_dev *dev = NULL; + u8 cap_ptr = 0x00; + u32 cap_id, scratch; + + if ((dev = pci_find_class(PCI_CLASS_BRIDGE_HOST << 8, NULL)) == NULL) + return -ENODEV; + + agp_bridge.dev = dev; + + /* Need to test for I810 here */ + + if (dev->vendor == PCI_VENDOR_ID_INTEL) { + struct pci_dev *i810_dev; + + switch (dev->device) { + case PCI_DEVICE_ID_INTEL_810_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810," + " but could not find the secondary" + " device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel " + "i810 Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup (i810_dev); + + case PCI_DEVICE_ID_INTEL_810_DC100_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_DC100_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810 " + "DC100, but could not find the " + "secondary device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel i810 " + "DC100 Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + + case PCI_DEVICE_ID_INTEL_810_E_0: + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_810_E_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "Detected an Intel i810 E" + ", but could not find the secondary " + "device.\n"); + return -ENODEV; + } + printk(KERN_INFO PFX "Detected an Intel i810 E " + "Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + case PCI_DEVICE_ID_INTEL_815_0: + /* The i815 can operate either as an i810 style + * integrated device, or as an AGP4X motherboard. + * + * This only addresses the first mode: + */ + i810_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_815_1, + NULL); + if (i810_dev == NULL) { + printk(KERN_ERR PFX "agpgart: Detected an " + "Intel i815, but could not find the" + " secondary device.\n"); + agp_bridge.type = NOT_SUPPORTED; + return -ENODEV; + } + printk(KERN_INFO PFX "agpgart: Detected an Intel i815 " + "Chipset.\n"); + agp_bridge.type = INTEL_I810; + return intel_i810_setup(i810_dev); + + default: + break; + } + } + + + /* find capndx */ + pci_read_config_dword(dev, 0x04, &scratch); + if (!(scratch & 0x00100000)) + return -ENODEV; + + pci_read_config_byte(dev, 0x34, &cap_ptr); + if (cap_ptr != 0x00) { + do { + pci_read_config_dword(dev, cap_ptr, &cap_id); + + if ((cap_id & 0xff) != 0x02) + cap_ptr = (cap_id >> 8) & 0xff; + } + while (((cap_id & 0xff) != 0x02) && (cap_ptr != 0x00)); + } + if (cap_ptr == 0x00) + return -ENODEV; + agp_bridge.capndx = cap_ptr; + + /* Fill in the mode register */ + pci_read_config_dword(agp_bridge.dev, + agp_bridge.capndx + 4, + &agp_bridge.mode); + + /* probe for known chipsets */ + return agp_lookup_host_bridge (dev); +} + +struct agp_max_table { + int mem; + int agp; +}; + +static struct agp_max_table maxes_table[9] __initdata = +{ + {0, 0}, + {32, 4}, + {64, 28}, + {128, 96}, + {256, 204}, + {512, 440}, + {1024, 942}, + {2048, 1920}, + {4096, 3932} +}; + +static int __init agp_find_max (void) +{ + long memory, index, result; + + memory = virt_to_phys(high_memory) >> 20; + index = 1; + + while ((memory > maxes_table[index].mem) && + (index < 8)) { + index++; + } + + result = maxes_table[index - 1].agp + + ( (memory - maxes_table[index - 1].mem) * + (maxes_table[index].agp - maxes_table[index - 1].agp)) / + (maxes_table[index].mem - maxes_table[index - 1].mem); + + printk(KERN_INFO PFX "Maximum main memory to use " + "for agp memory: %ldM\n", result); + result = result << (20 - PAGE_SHIFT); + return result; +} + +#define AGPGART_VERSION_MAJOR 0 +#define AGPGART_VERSION_MINOR 99 + +static agp_version agp_current_version = +{ + AGPGART_VERSION_MAJOR, + AGPGART_VERSION_MINOR +}; + +static int __init agp_backend_initialize(void) +{ + int size_value, rc, got_gatt=0, got_keylist=0; + + memset(&agp_bridge, 0, sizeof(struct agp_bridge_data)); + agp_bridge.type = NOT_SUPPORTED; + agp_bridge.max_memory_agp = agp_find_max(); + agp_bridge.version = &agp_current_version; + + rc = agp_find_supported_device(); + if (rc) { + /* not KERN_ERR because error msg should have already printed */ + printk(KERN_DEBUG PFX "no supported devices found.\n"); + return rc; + } + + if (agp_bridge.needs_scratch_page == TRUE) { + agp_bridge.scratch_page = agp_alloc_page(); + + if (agp_bridge.scratch_page == 0) { + printk(KERN_ERR PFX "unable to get memory for " + "scratch page.\n"); + return -ENOMEM; + } + agp_bridge.scratch_page = + virt_to_phys((void *) agp_bridge.scratch_page); + agp_bridge.scratch_page = + agp_bridge.mask_memory(agp_bridge.scratch_page, 0); + } + + size_value = agp_bridge.fetch_size(); + + if (size_value == 0) { + printk(KERN_ERR PFX "unable to detrimine aperture size.\n"); + rc = -EINVAL; + goto err_out; + } + if (agp_bridge.create_gatt_table()) { + printk(KERN_ERR PFX "unable to get memory for graphics " + "translation table.\n"); + rc = -ENOMEM; + goto err_out; + } + got_gatt = 1; + + agp_bridge.key_list = vmalloc(PAGE_SIZE * 4); + if (agp_bridge.key_list == NULL) { + printk(KERN_ERR PFX "error allocating memory for key lists.\n"); + rc = -ENOMEM; + goto err_out; + } + got_keylist = 1; + + /* FIXME vmalloc'd memory not guaranteed contiguous */ + memset(agp_bridge.key_list, 0, PAGE_SIZE * 4); + + if (agp_bridge.configure()) { + printk(KERN_ERR PFX "error configuring host chipset.\n"); + rc = -EINVAL; + goto err_out; + } + + printk(KERN_INFO PFX "AGP aperture is %dM @ 0x%lx\n", + size_value, agp_bridge.gart_bus_addr); + + return 0; + +err_out: + if (agp_bridge.needs_scratch_page == TRUE) { + agp_bridge.scratch_page &= ~(0x00000fff); + agp_destroy_page((unsigned long) + phys_to_virt(agp_bridge.scratch_page)); + } + if (got_gatt) + agp_bridge.free_gatt_table(); + if (got_keylist) + vfree(agp_bridge.key_list); + return rc; +} + + +/* cannot be __exit b/c as it could be called from __init code */ +static void agp_backend_cleanup(void) +{ + agp_bridge.cleanup(); + agp_bridge.free_gatt_table(); + vfree(agp_bridge.key_list); + + if (agp_bridge.needs_scratch_page == TRUE) { + agp_bridge.scratch_page &= ~(0x00000fff); + agp_destroy_page((unsigned long) + phys_to_virt(agp_bridge.scratch_page)); + } +} + +extern int agp_frontend_initialize(void); +extern void agp_frontend_cleanup(void); + +__initfunc(int agp_init(void)) +{ + int ret_val; + + printk(KERN_INFO "Linux agpgart interface v%d.%d (c) Jeff Hartmann\n", + AGPGART_VERSION_MAJOR, AGPGART_VERSION_MINOR); + + ret_val = agp_backend_initialize(); + if (ret_val) + return ret_val; + + ret_val = agp_frontend_initialize(); + if (ret_val) { + agp_backend_cleanup(); + return ret_val; + } + + return 0; +} + +#ifdef MODULE +int init_module(void) +{ + return agp_init(); +} + +void cleanup_module(void) +{ + agp_frontend_cleanup(); + agp_backend_cleanup(); +} +#endif diff --git a/drivers/char/agp/agpgart_fe.c b/drivers/char/agp/agpgart_fe.c new file mode 100644 index 000000000000..9b2c83019834 --- /dev/null +++ b/drivers/char/agp/agpgart_fe.c @@ -0,0 +1,1098 @@ +/* + * AGPGART module frontend version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "agp_backend.h" +#include "agpgart.h" +#include +#include +#include +#include +#include + +#include "agp.h" + +static struct agp_front_data agp_fe; + +static agp_memory *agp_find_mem_by_key(int key) +{ + agp_memory *curr; + + if (agp_fe.current_controller == NULL) { + return NULL; + } + curr = agp_fe.current_controller->pool; + + while (curr != NULL) { + if (curr->key == key) { + return curr; + } + curr = curr->next; + } + + return NULL; +} + +static void agp_remove_from_pool(agp_memory * temp) +{ + agp_memory *prev; + agp_memory *next; + + /* Check to see if this is even in the memory pool */ + + if (agp_find_mem_by_key(temp->key) != NULL) { + next = temp->next; + prev = temp->prev; + + if (prev != NULL) { + prev->next = next; + if (next != NULL) { + next->prev = prev; + } + } else { + /* This is the first item on the list */ + if (next != NULL) { + next->prev = NULL; + } + agp_fe.current_controller->pool = next; + } + } +} + +/* + * Routines for managing each client's segment list - + * These routines handle adding and removing segments + * to each auth'ed client. + */ + +static agp_segment_priv *agp_find_seg_in_client(const agp_client * client, + unsigned long offset, + int size, pgprot_t page_prot) +{ + agp_segment_priv *seg; + int num_segments, pg_start, pg_count, i; + + pg_start = offset / 4096; + pg_count = size / 4096; + seg = *(client->segments); + num_segments = client->num_segments; + + for (i = 0; i < client->num_segments; i++) { + if ((seg[i].pg_start == pg_start) && + (seg[i].pg_count == pg_count) && + (pgprot_val(seg[i].prot) == pgprot_val(page_prot))) { + return seg + i; + } + } + + return NULL; +} + +static void agp_remove_seg_from_client(agp_client * client) +{ + if (client->segments != NULL) { + if (*(client->segments) != NULL) { + kfree(*(client->segments)); + } + kfree(client->segments); + } +} + +static void agp_add_seg_to_client(agp_client * client, + agp_segment_priv ** seg, int num_segments) +{ + agp_segment_priv **prev_seg; + + prev_seg = client->segments; + + if (prev_seg != NULL) { + agp_remove_seg_from_client(client); + } + client->num_segments = num_segments; + client->segments = seg; +} + +/* Originally taken from linux/mm/mmap.c from the array + * protection_map. + * The original really should be exported to modules, or + * some routine which does the conversion for you + */ + +static const pgprot_t my_protect_map[16] = +{ + __P000, __P001, __P010, __P011, __P100, __P101, __P110, __P111, + __S000, __S001, __S010, __S011, __S100, __S101, __S110, __S111 +}; + +static pgprot_t agp_convert_mmap_flags(int prot) +{ +#define _trans(x,bit1,bit2) \ +((bit1==bit2)?(x&bit1):(x&bit1)?bit2:0) + + unsigned long prot_bits; + pgprot_t temp; + + prot_bits = _trans(prot, PROT_READ, VM_READ) | + _trans(prot, PROT_WRITE, VM_WRITE) | + _trans(prot, PROT_EXEC, VM_EXEC); + + prot_bits |= VM_SHARED; + + temp = my_protect_map[prot_bits & 0x0000000f]; + + return temp; +} + +static int agp_create_segment(agp_client * client, agp_region * region) +{ + agp_segment_priv **ret_seg; + agp_segment_priv *seg; + agp_segment *user_seg; + int i; + + seg = kmalloc((sizeof(agp_segment_priv) * region->seg_count), + GFP_KERNEL); + if (seg == NULL) { + kfree(region->seg_list); + return -ENOMEM; + } + memset(seg, 0, (sizeof(agp_segment_priv) * region->seg_count)); + user_seg = region->seg_list; + + for (i = 0; i < region->seg_count; i++) { + seg[i].pg_start = user_seg[i].pg_start; + seg[i].pg_count = user_seg[i].pg_count; + seg[i].prot = agp_convert_mmap_flags(user_seg[i].prot); + } + ret_seg = kmalloc(sizeof(void *), GFP_KERNEL); + if (ret_seg == NULL) { + kfree(region->seg_list); + kfree(seg); + return -ENOMEM; + } + *ret_seg = seg; + kfree(region->seg_list); + agp_add_seg_to_client(client, ret_seg, region->seg_count); + return 0; +} + +/* End - Routines for managing each client's segment list */ + +/* This function must only be called when current_controller != NULL */ +static void agp_insert_into_pool(agp_memory * temp) +{ + agp_memory *prev; + + prev = agp_fe.current_controller->pool; + + if (prev != NULL) { + prev->prev = temp; + temp->next = prev; + } + agp_fe.current_controller->pool = temp; +} + + +/* File private list routines */ + +agp_file_private *agp_find_private(pid_t pid) +{ + agp_file_private *curr; + + curr = agp_fe.file_priv_list; + + while (curr != NULL) { + if (curr->my_pid == pid) { + return curr; + } + curr = curr->next; + } + + return NULL; +} + +void agp_insert_file_private(agp_file_private * priv) +{ + agp_file_private *prev; + + prev = agp_fe.file_priv_list; + + if (prev != NULL) { + prev->prev = priv; + } + priv->next = prev; + agp_fe.file_priv_list = priv; +} + +void agp_remove_file_private(agp_file_private * priv) +{ + agp_file_private *next; + agp_file_private *prev; + + next = priv->next; + prev = priv->prev; + + if (prev != NULL) { + prev->next = next; + + if (next != NULL) { + next->prev = prev; + } + } else { + if (next != NULL) { + next->prev = NULL; + } + agp_fe.file_priv_list = next; + } +} + +/* End - File flag list routines */ + +/* + * Wrappers for agp_free_memory & agp_allocate_memory + * These make sure that internal lists are kept updated. + */ +static void agp_free_memory_wrap(agp_memory * memory) +{ + agp_remove_from_pool(memory); + agp_free_memory(memory); +} + +static agp_memory *agp_allocate_memory_wrap(size_t pg_count, u32 type) +{ + agp_memory *memory; + + memory = agp_allocate_memory(pg_count, type); + printk(KERN_DEBUG "memory : %p\n", memory); + if (memory == NULL) { + return NULL; + } + agp_insert_into_pool(memory); + return memory; +} + +/* Routines for managing the list of controllers - + * These routines manage the current controller, and the list of + * controllers + */ + +static agp_controller *agp_find_controller_by_pid(pid_t id) +{ + agp_controller *controller; + + controller = agp_fe.controllers; + + while (controller != NULL) { + if (controller->pid == id) { + return controller; + } + controller = controller->next; + } + + return NULL; +} + +static agp_controller *agp_create_controller(pid_t id) +{ + agp_controller *controller; + + controller = kmalloc(sizeof(agp_controller), GFP_KERNEL); + + if (controller == NULL) { + return NULL; + } + memset(controller, 0, sizeof(agp_controller)); + controller->pid = id; + + return controller; +} + +static int agp_insert_controller(agp_controller * controller) +{ + agp_controller *prev_controller; + + prev_controller = agp_fe.controllers; + controller->next = prev_controller; + + if (prev_controller != NULL) { + prev_controller->prev = controller; + } + agp_fe.controllers = controller; + + return 0; +} + +static void agp_remove_all_clients(agp_controller * controller) +{ + agp_client *client; + agp_client *temp; + + client = controller->clients; + + while (client) { + agp_file_private *priv; + + temp = client; + agp_remove_seg_from_client(temp); + priv = agp_find_private(temp->pid); + + if (priv != NULL) { + clear_bit(AGP_FF_IS_VALID, &priv->access_flags); + clear_bit(AGP_FF_IS_CLIENT, &priv->access_flags); + } + client = client->next; + kfree(temp); + } +} + +static void agp_remove_all_memory(agp_controller * controller) +{ + agp_memory *memory; + agp_memory *temp; + + memory = controller->pool; + + while (memory) { + temp = memory; + memory = memory->next; + agp_free_memory_wrap(temp); + } +} + +static int agp_remove_controller(agp_controller * controller) +{ + agp_controller *prev_controller; + agp_controller *next_controller; + + prev_controller = controller->prev; + next_controller = controller->next; + + if (prev_controller != NULL) { + prev_controller->next = next_controller; + if (next_controller != NULL) { + next_controller->prev = prev_controller; + } + } else { + if (next_controller != NULL) { + next_controller->prev = NULL; + } + agp_fe.controllers = next_controller; + } + + agp_remove_all_memory(controller); + agp_remove_all_clients(controller); + + if (agp_fe.current_controller == controller) { + agp_fe.current_controller = NULL; + agp_fe.backend_acquired = FALSE; + agp_backend_release(); + } + kfree(controller); + return 0; +} + +static void agp_controller_make_current(agp_controller * controller) +{ + agp_client *clients; + + clients = controller->clients; + + while (clients != NULL) { + agp_file_private *priv; + + priv = agp_find_private(clients->pid); + + if (priv != NULL) { + set_bit(AGP_FF_IS_VALID, &priv->access_flags); + set_bit(AGP_FF_IS_CLIENT, &priv->access_flags); + } + clients = clients->next; + } + + agp_fe.current_controller = controller; +} + +static void agp_controller_release_current(agp_controller * controller, + agp_file_private * controller_priv) +{ + agp_client *clients; + + clear_bit(AGP_FF_IS_VALID, &controller_priv->access_flags); + clients = controller->clients; + + while (clients != NULL) { + agp_file_private *priv; + + priv = agp_find_private(clients->pid); + + if (priv != NULL) { + clear_bit(AGP_FF_IS_VALID, &priv->access_flags); + } + clients = clients->next; + } + + agp_fe.current_controller = NULL; + agp_fe.used_by_controller = FALSE; + agp_backend_release(); +} + +/* + * Routines for managing client lists - + * These routines are for managing the list of auth'ed clients. + */ + +static agp_client *agp_find_client_in_controller(agp_controller * controller, + pid_t id) +{ + agp_client *client; + + if (controller == NULL) { + return NULL; + } + client = controller->clients; + + while (client != NULL) { + if (client->pid == id) { + return client; + } + client = client->next; + } + + return NULL; +} + +static agp_controller *agp_find_controller_for_client(pid_t id) +{ + agp_controller *controller; + + controller = agp_fe.controllers; + + while (controller != NULL) { + if ((agp_find_client_in_controller(controller, id)) != NULL) { + return controller; + } + controller = controller->next; + } + + return NULL; +} + +static agp_client *agp_find_client_by_pid(pid_t id) +{ + agp_client *temp; + + if (agp_fe.current_controller == NULL) { + return NULL; + } + temp = agp_find_client_in_controller(agp_fe.current_controller, id); + return temp; +} + +static void agp_insert_client(agp_client * client) +{ + agp_client *prev_client; + + prev_client = agp_fe.current_controller->clients; + client->next = prev_client; + + if (prev_client != NULL) { + prev_client->prev = client; + } + agp_fe.current_controller->clients = client; + agp_fe.current_controller->num_clients++; +} + +static agp_client *agp_create_client(pid_t id) +{ + agp_client *new_client; + + new_client = kmalloc(sizeof(agp_client), GFP_KERNEL); + + if (new_client == NULL) { + return NULL; + } + memset(new_client, 0, sizeof(agp_client)); + new_client->pid = id; + agp_insert_client(new_client); + return new_client; +} + +static int agp_remove_client(pid_t id) +{ + agp_client *client; + agp_client *prev_client; + agp_client *next_client; + agp_controller *controller; + + controller = agp_find_controller_for_client(id); + + if (controller == NULL) { + return -EINVAL; + } + client = agp_find_client_in_controller(controller, id); + + if (client == NULL) { + return -EINVAL; + } + prev_client = client->prev; + next_client = client->next; + + if (prev_client != NULL) { + prev_client->next = next_client; + if (next_client != NULL) { + next_client->prev = prev_client; + } + } else { + if (next_client != NULL) { + next_client->prev = NULL; + } + controller->clients = next_client; + } + + controller->num_clients--; + agp_remove_seg_from_client(client); + kfree(client); + return 0; +} + +/* End - Routines for managing client lists */ + +/* File Operations */ + +static int agp_mmap(struct file *file, struct vm_area_struct *vma) +{ + int size; + int current_size; + unsigned long offset; + agp_client *client; + agp_file_private *priv = (agp_file_private *) file->private_data; + agp_kern_info kerninfo; + + AGP_LOCK(); + + if (agp_fe.backend_acquired != TRUE) { + AGP_UNLOCK(); + return -EPERM; + } + if (!(test_bit(AGP_FF_IS_VALID, &priv->access_flags))) { + AGP_UNLOCK(); + return -EPERM; + } + agp_copy_info(&kerninfo); + size = vma->vm_end - vma->vm_start; + current_size = kerninfo.aper_size; + current_size = current_size * 0x100000; + offset = vma->vm_offset; + + if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) { + if ((size + offset) > current_size) { + AGP_UNLOCK(); + return -EINVAL; + } + client = agp_find_client_by_pid(current->pid); + + if (client == NULL) { + AGP_UNLOCK(); + return -EPERM; + } + if (!agp_find_seg_in_client(client, offset, + size, vma->vm_page_prot)) { + AGP_UNLOCK(); + return -EINVAL; + } + if (remap_page_range(vma->vm_start, + (kerninfo.aper_base + offset), + size, vma->vm_page_prot)) { + AGP_UNLOCK(); + return -EAGAIN; + } + AGP_UNLOCK(); + return 0; + } + if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) { + if (size != current_size) { + AGP_UNLOCK(); + return -EINVAL; + } + if (remap_page_range(vma->vm_start, kerninfo.aper_base, + size, vma->vm_page_prot)) { + AGP_UNLOCK(); + return -EAGAIN; + } + AGP_UNLOCK(); + return 0; + } + AGP_UNLOCK(); + return -EPERM; +} + +static int agp_release(struct inode *inode, struct file *file) +{ + agp_file_private *priv = (agp_file_private *) file->private_data; + + AGP_LOCK(); + + if (test_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags)) { + agp_controller *controller; + + controller = agp_find_controller_by_pid(priv->my_pid); + + if (controller != NULL) { + if (controller == agp_fe.current_controller) { + agp_controller_release_current(controller, + priv); + } + agp_remove_controller(controller); + } + } + if (test_bit(AGP_FF_IS_CLIENT, &priv->access_flags)) { + agp_remove_client(priv->my_pid); + } + agp_remove_file_private(priv); + kfree(priv); + MOD_DEC_USE_COUNT; + AGP_UNLOCK(); + return 0; +} + +static int agp_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + agp_file_private *priv; + agp_client *client; + int rc = -ENXIO; + + AGP_LOCK(); + MOD_INC_USE_COUNT; + + if (minor != AGPGART_MINOR) + goto err_out; + + priv = kmalloc(sizeof(agp_file_private), GFP_KERNEL); + if (priv == NULL) + goto err_out_nomem; + + memset(priv, 0, sizeof(agp_file_private)); + set_bit(AGP_FF_ALLOW_CLIENT, &priv->access_flags); + priv->my_pid = current->pid; + + if ((current->uid == 0) || (current->suid == 0)) { + /* Root priv, can be controller */ + set_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags); + } + client = agp_find_client_by_pid(current->pid); + + if (client != NULL) { + set_bit(AGP_FF_IS_CLIENT, &priv->access_flags); + set_bit(AGP_FF_IS_VALID, &priv->access_flags); + } + file->private_data = (void *) priv; + agp_insert_file_private(priv); + AGP_UNLOCK(); + return 0; + +err_out_nomem: + rc = -ENOMEM; +err_out: + MOD_DEC_USE_COUNT; + AGP_UNLOCK(); + return rc; +} + + +static long long agp_lseek(struct file *file, long long offset, int origin) +{ + return -ESPIPE; +} + +static ssize_t agp_read(struct file *file, char *buf, + size_t count, loff_t * ppos) +{ + return -EINVAL; +} + +static ssize_t agp_write(struct file *file, const char *buf, + size_t count, loff_t * ppos) +{ + return -EINVAL; +} + +static int agpioc_info_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_info userinfo; + agp_kern_info kerninfo; + + agp_copy_info(&kerninfo); + + userinfo.version.major = kerninfo.version.major; + userinfo.version.minor = kerninfo.version.minor; + userinfo.bridge_id = kerninfo.device->vendor | + (kerninfo.device->device << 16); + userinfo.agp_mode = kerninfo.mode; + userinfo.aper_base = kerninfo.aper_base; + userinfo.aper_size = kerninfo.aper_size; + userinfo.pg_total = userinfo.pg_system = kerninfo.max_memory; + userinfo.pg_used = kerninfo.current_memory; + + if (copy_to_user((void *) arg, &userinfo, sizeof(agp_info))) { + return -EFAULT; + } + return 0; +} + +static int agpioc_acquire_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_controller *controller; + if (!(test_bit(AGP_FF_ALLOW_CONTROLLER, &priv->access_flags))) { + return -EPERM; + } + if (agp_fe.current_controller != NULL) { + return -EBUSY; + } + if ((agp_backend_acquire()) == 0) { + agp_fe.backend_acquired = TRUE; + } else { + return -EBUSY; + } + + controller = agp_find_controller_by_pid(priv->my_pid); + + if (controller != NULL) { + agp_controller_make_current(controller); + } else { + controller = agp_create_controller(priv->my_pid); + + if (controller == NULL) { + agp_fe.backend_acquired = FALSE; + agp_backend_release(); + return -ENOMEM; + } + agp_insert_controller(controller); + agp_controller_make_current(controller); + } + + set_bit(AGP_FF_IS_CONTROLLER, &priv->access_flags); + set_bit(AGP_FF_IS_VALID, &priv->access_flags); + return 0; +} + +static int agpioc_release_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_controller_release_current(agp_fe.current_controller, priv); + return 0; +} + +static int agpioc_setup_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_setup mode; + + if (copy_from_user(&mode, (void *) arg, sizeof(agp_setup))) { + return -EFAULT; + } + agp_enable(mode.agp_mode); + return 0; +} + +static int agpioc_reserve_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_region reserve; + agp_client *client; + agp_file_private *client_priv; + + + if (copy_from_user(&reserve, (void *) arg, sizeof(agp_region))) { + return -EFAULT; + } + client = agp_find_client_by_pid(reserve.pid); + + if (reserve.seg_count == 0) { + /* remove a client */ + client_priv = agp_find_private(reserve.pid); + + if (client_priv != NULL) { + set_bit(AGP_FF_IS_CLIENT, + &client_priv->access_flags); + set_bit(AGP_FF_IS_VALID, + &client_priv->access_flags); + } + if (client == NULL) { + /* client is already removed */ + return 0; + } + return agp_remove_client(reserve.pid); + } else { + agp_segment *segment; + + segment = kmalloc((sizeof(agp_segment) * reserve.seg_count), + GFP_KERNEL); + + if (segment == NULL) { + return -ENOMEM; + } + if (copy_from_user(segment, (void *) reserve.seg_list, + GFP_KERNEL)) { + kfree(segment); + return -EFAULT; + } + reserve.seg_list = segment; + + if (client == NULL) { + /* Create the client and add the segment */ + client = agp_create_client(reserve.pid); + + if (client == NULL) { + kfree(segment); + return -ENOMEM; + } + client_priv = agp_find_private(reserve.pid); + + if (client_priv != NULL) { + set_bit(AGP_FF_IS_CLIENT, + &client_priv->access_flags); + set_bit(AGP_FF_IS_VALID, + &client_priv->access_flags); + } + return agp_create_segment(client, &reserve); + } else { + return agp_create_segment(client, &reserve); + } + } + /* Will never really happen */ + return -EINVAL; +} + +static int agpioc_protect_wrap(agp_file_private * priv, unsigned long arg) +{ + /* This function is not currently implemented */ + return -EINVAL; +} + +static int agpioc_allocate_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_memory *memory; + agp_allocate alloc; + + if (copy_from_user(&alloc, (void *) arg, sizeof(agp_allocate))) { + return -EFAULT; + } + memory = agp_allocate_memory_wrap(alloc.pg_count, alloc.type); + + if (memory == NULL) { + return -ENOMEM; + } + alloc.key = memory->key; + alloc.physical = memory->physical; + + if (copy_to_user((void *) arg, &alloc, sizeof(agp_allocate))) { + agp_free_memory_wrap(memory); + return -EFAULT; + } + return 0; +} + +static int agpioc_deallocate_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_memory *memory; + + memory = agp_find_mem_by_key((int) arg); + + if (memory == NULL) { + return -EINVAL; + } + agp_free_memory_wrap(memory); + return 0; +} + +static int agpioc_bind_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_bind bind_info; + agp_memory *memory; + + if (copy_from_user(&bind_info, (void *) arg, sizeof(agp_bind))) { + return -EFAULT; + } + memory = agp_find_mem_by_key(bind_info.key); + + if (memory == NULL) { + return -EINVAL; + } + return agp_bind_memory(memory, bind_info.pg_start); +} + +static int agpioc_unbind_wrap(agp_file_private * priv, unsigned long arg) +{ + agp_memory *memory; + agp_unbind unbind; + + if (copy_from_user(&unbind, (void *) arg, sizeof(agp_unbind))) { + return -EFAULT; + } + memory = agp_find_mem_by_key(unbind.key); + + if (memory == NULL) { + return -EINVAL; + } + return agp_unbind_memory(memory); +} + +static int agp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + agp_file_private *curr_priv = (agp_file_private *) file->private_data; + int ret_val = -ENOTTY; + + AGP_LOCK(); + + if ((agp_fe.current_controller == NULL) && + (cmd != AGPIOC_ACQUIRE)) { + ret_val = -EINVAL; + goto ioctl_out; + } + if ((agp_fe.backend_acquired != TRUE) && + (cmd != AGPIOC_ACQUIRE)) { + ret_val = -EBUSY; + goto ioctl_out; + } + if (cmd != AGPIOC_ACQUIRE) { + if (!(test_bit(AGP_FF_IS_CONTROLLER, + &curr_priv->access_flags))) { + ret_val = -EPERM; + goto ioctl_out; + } + /* Use the original pid of the controller, + * in case it's threaded */ + + if (agp_fe.current_controller->pid != curr_priv->my_pid) { + ret_val = -EBUSY; + goto ioctl_out; + } + } + switch (cmd) { + case AGPIOC_INFO: + { + ret_val = agpioc_info_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_ACQUIRE: + { + ret_val = agpioc_acquire_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_RELEASE: + { + ret_val = agpioc_release_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_SETUP: + { + ret_val = agpioc_setup_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_RESERVE: + { + ret_val = agpioc_reserve_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_PROTECT: + { + ret_val = agpioc_protect_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_ALLOCATE: + { + ret_val = agpioc_allocate_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_DEALLOCATE: + { + ret_val = agpioc_deallocate_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_BIND: + { + ret_val = agpioc_bind_wrap(curr_priv, arg); + goto ioctl_out; + } + case AGPIOC_UNBIND: + { + ret_val = agpioc_unbind_wrap(curr_priv, arg); + goto ioctl_out; + } + } + +ioctl_out: + AGP_UNLOCK(); + return ret_val; +} + +static struct file_operations agp_fops = +{ + llseek: agp_lseek, + read: agp_read, + write: agp_write, + ioctl: agp_ioctl, + mmap: agp_mmap, + open: agp_open, + release: agp_release, +}; + +static struct miscdevice agp_miscdev = +{ + AGPGART_MINOR, + AGPGART_MODULE_NAME, + &agp_fops +}; + +int __init agp_frontend_initialize(void) +{ + memset(&agp_fe, 0, sizeof(struct agp_front_data)); + AGP_LOCK_INIT(); + + if (misc_register(&agp_miscdev)) { + printk(KERN_ERR PFX "unable to get minor: %d\n", AGPGART_MINOR); + return -EIO; + } + return 0; +} + +void agp_frontend_cleanup(void) +{ + misc_deregister(&agp_miscdev); +} diff --git a/drivers/char/cpia.c b/drivers/char/cpia.c index acc700e2b7bd..7efc0b8ad179 100644 --- a/drivers/char/cpia.c +++ b/drivers/char/cpia.c @@ -3,9 +3,9 @@ * * Supports CPiA based Video Camera's. * - * (C) 1999 Peter Pregler, - * Scott J. Bertin, - * Johannes Erdfelt + * (C) Copyright 1999-2000 Peter Pregler, + * (C) Copyright 1999-2000 Scott J. Bertin, + * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@valinux.com * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -22,28 +22,28 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* #define _CPIA_DEBUG_ define for verbose debug output */ #include #include +#include #include -#include #include #include -#include #include -#include +#include #include #include -#include +#include #include #include +#include #ifdef CONFIG_KMOD #include #endif -#undef _CPIA_DEBUG_ /* define for verbose debug output */ -#include +#include "cpia.h" #ifdef CONFIG_VIDEO_CPIA_PP extern int cpia_pp_init(void); @@ -52,8 +52,12 @@ extern int cpia_pp_init(void); extern int cpia_usb_init(void); #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) +#define init_MUTEX(x) (*(x) = MUTEX) +#endif + #ifdef MODULE -MODULE_AUTHOR("Scott J. Bertin & Peter Pregler "); +MODULE_AUTHOR("Scott J. Bertin & Peter Pregler & Johannes Erdfelt "); MODULE_DESCRIPTION("V4L-driver for Vision CPiA based cameras"); MODULE_SUPPORTED_DEVICE("video"); #endif @@ -97,6 +101,7 @@ MODULE_SUPPORTED_DEVICE("video"); #define CPIA_COMMAND_I2CRead (INPUT | CPIA_MODULE_SYSTEM | 13) #define CPIA_COMMAND_GetVPVersion (INPUT | CPIA_MODULE_VP_CTRL | 1) +#define CPIA_COMMAND_ResetFrameCounter (INPUT | CPIA_MODULE_VP_CTRL | 2) #define CPIA_COMMAND_SetColourParams (OUTPUT | CPIA_MODULE_VP_CTRL | 3) #define CPIA_COMMAND_SetExposure (OUTPUT | CPIA_MODULE_VP_CTRL | 4) #define CPIA_COMMAND_SetColourBalance (OUTPUT | CPIA_MODULE_VP_CTRL | 6) @@ -127,6 +132,7 @@ MODULE_SUPPORTED_DEVICE("video"); #define CPIA_COMMAND_SetYUVThresh (OUTPUT | CPIA_MODULE_CAPTURE | 12) #define CPIA_COMMAND_SetCompressionParams (OUTPUT | CPIA_MODULE_CAPTURE | 13) #define CPIA_COMMAND_DiscardFrame (OUTPUT | CPIA_MODULE_CAPTURE | 14) +#define CPIA_COMMAND_GrabReset (OUTPUT | CPIA_MODULE_CAPTURE | 15) #define CPIA_COMMAND_OutputRS232 (OUTPUT | CPIA_MODULE_DEBUG | 1) #define CPIA_COMMAND_AbortProcess (OUTPUT | CPIA_MODULE_DEBUG | 4) @@ -135,117 +141,7 @@ MODULE_SUPPORTED_DEVICE("video"); #define CPIA_COMMAND_StartDummyDtream (OUTPUT | CPIA_MODULE_DEBUG | 8) #define CPIA_COMMAND_AbortStream (OUTPUT | CPIA_MODULE_DEBUG | 9) #define CPIA_COMMAND_DownloadDRAM (OUTPUT | CPIA_MODULE_DEBUG | 10) - -struct cam_params { - struct { - u8 firmwareVersion; - u8 firmwareRevision; - u8 vcVersion; - u8 vcRevision; - } version; - struct { - u16 vendor; - u16 product; - u16 deviceRevision; - } pnpID; - struct { - u8 vpVersion; - u8 vpRevision; - u16 cameraHeadID; - } vpVersion; - struct { - u8 systemState; - u8 grabState; - u8 streamState; - u8 fatalError; - u8 cmdError; - u8 debugFlags; - u8 vpStatus; - u8 errorCode; - } status; - struct { - u8 brightness; - u8 contrast; - u8 saturation; - } colourParams; - struct { - u8 gainMode; - u8 expMode; - u8 compMode; - u8 centreWeight; - u8 gain; - u8 fineExp; - u8 coarseExpLo; - u8 coarseExpHi; - u8 redComp; - u8 green1Comp; - u8 green2Comp; - u8 blueComp; - } exposure; - struct { - u8 balanceMode; - u8 redGain; - u8 greenGain; - u8 blueGain; - } colourBalance; - struct { - u8 divisor; - u8 baserate; - } sensorFps; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } apcor; - struct { - u8 flickerMode; - u8 coarseJump; - u8 allowableOverExposure; - } flickerControl; - struct { - u8 gain1; - u8 gain2; - u8 gain4; - u8 gain8; - } vlOffset; - struct { - u8 mode; - u8 decimation; - } compression; - struct { - u8 frTargeting; - u8 targetFR; - u8 targetQ; - } compressionTarget; - struct { - u8 yThreshold; - u8 uvThreshold; - } yuvThreshold; - struct { - u8 hysteresis; - u8 threshMax; - u8 smallStep; - u8 largeStep; - u8 decimationHysteresis; - u8 frDiffStepThresh; - u8 qDiffStepThresh; - u8 decimationThreshMod; - } compressionParams; - struct { - u8 videoSize; /* CIF/QCIF */ - u8 subSample; - u8 yuvOrder; - } format; - struct { - u8 colStart; /* skip first 8*colStart pixels */ - u8 colEnd; /* finish at 8*colEnd pixels */ - u8 rowStart; /* skip first 4*rowStart lines */ - u8 rowEnd; /* finish at 4*rowEnd lines */ - } roi; - u8 ecpTiming; - u8 streamStartLine; -}; +#define CPIA_COMMAND_Null (OUTPUT | CPIA_MODULE_DEBUG | 11) enum { FRAME_READY, /* Ready to grab into */ @@ -254,24 +150,6 @@ enum { FRAME_UNUSED, /* Unused (no MCAPTURE) */ }; -#define FRAME_NUM 2 /* double buffering for now */ -struct cpia_frame { - u8 *data; - int count; - int width; - int height; - volatile int state; -}; - -enum v4l_camstates { - CPIA_V4L_IDLE = 0, - CPIA_V4L_ERROR, - CPIA_V4L_COMMAND, - CPIA_V4L_GRABBING, - CPIA_V4L_STREAMING, - CPIA_V4L_STREAMING_PAUSED, -}; - #define COMMAND_NONE 0x0000 #define COMMAND_SETCOMPRESSION 0x0001 #define COMMAND_SETCOMPRESSIONTARGET 0x0002 @@ -288,45 +166,7 @@ enum v4l_camstates { #define COMMAND_SETAPCOR 0x1000 #define COMMAND_SETFLICKERCTRL 0x2000 #define COMMAND_SETVLOFFSET 0x4000 - -struct cam_data { - int index; /* which camera is this */ - struct semaphore busy_lock; /* guard against SMP multithreading */ - struct cpia_camera_ops *ops; /* lowlevel driver operations */ - void *lowlevel_data; /* private data for lowlevel driver */ - u8 *raw_image; /* buffer for raw image data */ - struct cpia_frame decompressed_frame; - /* buffer to hold decompressed frame */ - int image_size; /* sizeof last decompressed image */ - int open_count; /* # of process that have camera open */ - /* camera status */ - int fps; /* actual fps reported by the camera */ - int transfer_rate; /* transfer rate from camera in kB/s */ - u8 mainsFreq; /* for flicker control */ - - /* proc interface */ - struct semaphore param_lock; /* params lock for this camera */ - struct cam_params params; /* camera settings */ - struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ - - /* v4l */ - int video_size; /* VIDEO_SIZE_ */ - volatile enum v4l_camstates camstate; /* v4l layer status */ - struct video_device vdev; /* v4l videodev */ - struct video_picture vp; /* v4l camera settings */ - struct video_window vw; /* v4l capture area */ - - /* mmap interface */ - int curframe; /* the current frame to grab into */ - u8 *frame_buf; /* frame buffer data */ - struct cpia_frame frame[FRAME_NUM]; - /* FRAME_NUM-buffering, so we need a array */ - - int first_frame; - volatile u32 cmd_queue; /* queued commands */ -}; - -static struct cam_data *camera[CPIA_MAXCAMS] ={ [0 ... CPIA_MAXCAMS-1] = NULL }; +#define COMMAND_SETLIGHTS 0x8000 /* GA 04/14/00 */ /* Developer's Guide Table 5 p 3-34 * indexed by [mains][sensorFps.baserate][sensorFps.divisor]*/ @@ -338,6 +178,7 @@ static u8 flicker_jumps[2][2][4] = /* forward declaration of local function */ static void reset_camera_struct(struct cam_data *cam); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) /********************************************************************** * * Memory management @@ -357,64 +198,134 @@ static inline unsigned long uvirt_to_phys(unsigned long adr) pte_t *ptep, pte; pgd = pgd_offset(current->mm, adr); - if (pgd_none(*pgd)) return 0; + if (pgd_none(*pgd)) + return 0; pmd = pmd_offset(pgd, adr); - if (pmd_none(*pmd)) return 0; + if (pmd_none(*pmd)) + return 0; ptep = pte_offset(pmd, adr/*&(~PGDIR_MASK)*/); pte = *ptep; - if(pte_present(pte)) - return - virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); + if (pte_present(pte)) + return virt_to_phys((void *)(pte_page(pte)|(adr&(PAGE_SIZE-1)))); return 0; } -static inline unsigned long kvirt_to_phys(unsigned long adr) +static inline unsigned long kvirt_to_pa(unsigned long adr) { return uvirt_to_phys(VMALLOC_VMADDR(adr)); } -static void * rvmalloc(unsigned long size) +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet - jerdfelt + * + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) { - void * mem; + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | + (adr & (PAGE_SIZE-1)); + } + } + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + +static void *rvmalloc(unsigned long size) +{ + void *mem; unsigned long adr, page; + /* Round it off to PAGE_SIZE */ size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - mem=vmalloc(size); - if (mem) { - /* Clear the ram out, no junk to the user */ - memset(mem, 0, size); - adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_phys(adr); - mem_map_reserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; - } + mem = vmalloc(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + mem_map_reserve(MAP_NR(phys_to_virt(page))); +#else + mem_map_reserve(MAP_NR(__va(page))); +#endif + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } + return mem; } -static void rvfree(void * mem, unsigned long size) +static void rvfree(void *mem, unsigned long size) { unsigned long adr, page; - + + if (!mem) + return; + size += (PAGE_SIZE - 1); size &= ~(PAGE_SIZE - 1); - if (mem) { - adr=(unsigned long) mem; - while (size > 0) { - page = kvirt_to_phys(adr); - mem_map_unreserve(MAP_NR(phys_to_virt(page))); - adr+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; - } - vfree(mem); + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + mem_map_unreserve(MAP_NR(phys_to_virt(page))); +#else + mem_map_unreserve(MAP_NR(__va(page))); +#endif + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } + vfree(mem); } /********************************************************************** @@ -422,6 +333,7 @@ static void rvfree(void * mem, unsigned long size) * /proc interface * **********************************************************************/ +#ifdef CONFIG_PROC_FS static struct proc_dir_entry *cpia_proc_root=NULL; static int cpia_read_proc(char *page, char **start, off_t off, @@ -432,7 +344,7 @@ static int cpia_read_proc(char *page, char **start, off_t off, struct cam_data *cam = data; char tmpstr[20]; - /* IMPORTANT: This output MUST be kept under 4k (FIXME: PAGE_SIZE?) + /* IMPORTANT: This output MUST be kept under PAGE_SIZE * or we need to get more sophisticated. */ out += sprintf(out, "read-only\n-----------------------\n"); @@ -467,22 +379,23 @@ static int cpia_read_proc(char *page, char **start, off_t off, cam->params.status.vpStatus); out += sprintf(out, "error_code: %#04x\n", cam->params.status.errorCode); + /* GA 04/14/00 - QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "button: %4d\n", + cam->params.qx3.button); + out += sprintf(out, "cradled: %4d\n", + cam->params.qx3.cradled); + } out += sprintf(out, "video_size: %s\n", cam->params.format.videoSize == VIDEOSIZE_CIF ? "CIF " : "QCIF"); - out += sprintf(out, "sub_sample: %s\n", - cam->params.format.subSample == SUBSAMPLE_420 ? - "420" : "422"); - out += sprintf(out, "yuv_order: %s\n", - cam->params.format.yuvOrder == YUVORDER_YUYV ? - "YUYV" : "UYVY"); out += sprintf(out, "roi: (%3d, %3d) to (%3d, %3d)\n", cam->params.roi.colStart*8, cam->params.roi.rowStart*4, cam->params.roi.colEnd*8, cam->params.roi.rowEnd*4); - out += sprintf(out, "actual_fps: %d\n", cam->fps); - out += sprintf(out, "transfer_rate: %dkB/s\n", + out += sprintf(out, "actual_fps: %3d\n", cam->fps); + out += sprintf(out, "transfer_rate: %4dkB/s\n", cam->transfer_rate); out += sprintf(out, "\nread-write\n"); @@ -490,13 +403,13 @@ static int cpia_read_proc(char *page, char **start, off_t off, " max default comment\n"); out += sprintf(out, "brightness: %8d %8d %8d %8d\n", cam->params.colourParams.brightness, 0, 100, 50); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits contrast to 80 */ tmp = 80; - } else { + else tmp = 96; - } + out += sprintf(out, "contrast: %8d %8d %8d %8d" " steps of 8\n", cam->params.colourParams.contrast, 0, tmp, 48); @@ -510,21 +423,20 @@ static int cpia_read_proc(char *page, char **start, off_t off, 2*cam->params.streamStartLine, 0, cam->params.format.videoSize == VIDEOSIZE_CIF ? 288:144, cam->params.format.videoSize == VIDEOSIZE_CIF ? 240:120); + out += sprintf(out, "sub_sample: %8s %8s %8s %8s\n", + cam->params.format.subSample == SUBSAMPLE_420 ? + "420" : "422", "420", "422", "422"); + out += sprintf(out, "yuv_order: %8s %8s %8s %8s\n", + cam->params.format.yuvOrder == YUVORDER_YUYV ? + "YUYV" : "UYVY", "YUYV" , "UYVY", "YUYV"); out += sprintf(out, "ecp_timing: %8s %8s %8s %8s\n", cam->params.ecpTiming ? "slow" : "normal", "slow", "normal", "normal"); - switch(cam->params.colourBalance.balanceMode) { - case 1: - // FIXME case 3: - sprintf(tmpstr, "manual"); - break; - case 2: + if (cam->params.colourBalance.balanceModeIsAuto) { sprintf(tmpstr, "auto"); - break; - default: - sprintf(tmpstr, "unknown"); - break; + } else { + sprintf(tmpstr, "manual"); } out += sprintf(out, "color_balance_mode: %8s %8s %8s" " %8s\n", tmpstr, "manual", "auto", "auto"); @@ -535,20 +447,20 @@ static int cpia_read_proc(char *page, char **start, off_t off, out += sprintf(out, "blue_gain: %8d %8d %8d %8d\n", cam->params.colourBalance.blueGain, 0, 212, 92); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits gain to 2 */ sprintf(tmpstr, "%8d %8d", 1, 2); - } else { + else sprintf(tmpstr, "%8d %8d", 1, 8); - } - if(cam->params.exposure.gainMode == 0) { + + if (cam->params.exposure.gainMode == 0) out += sprintf(out, "max_gain: unknown %18s" " %8d powers of 2\n", tmpstr, 2); - } else { + else out += sprintf(out, "max_gain: %8d %18s %8d powers of 2\n", 1<<(cam->params.exposure.gainMode-1), tmpstr, 2); - } + switch(cam->params.exposure.expMode) { case 1: case 3: @@ -568,22 +480,22 @@ static int cpia_read_proc(char *page, char **start, off_t off, "off", "on", "on"); out += sprintf(out, "gain: %8d %8d max_gain %8d 1,2,4,8 possible\n", 1<params.exposure.gain, 1, 1); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits fineExp to 127 */ tmp = 255; - } else { + else tmp = 511; - } + out += sprintf(out, "fine_exp: %8d %8d %8d %8d\n", cam->params.exposure.fineExp*2, 0, tmp, 0); - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2) /* 1-02 firmware limits coarseExpHi to 0 */ tmp = 255; - } else { + else tmp = 65535; - } + out += sprintf(out, "coarse_exp: %8d %8d %8d" " %8d\n", cam->params.exposure.coarseExpLo+ 256*cam->params.exposure.coarseExpHi, 0, tmp, 185); @@ -637,9 +549,9 @@ static int cpia_read_proc(char *page, char **start, off_t off, break; } out += sprintf(out, " none,auto,manual auto\n"); - out += sprintf(out, "decimation: %8s %8s %8s %8s\n", + out += sprintf(out, "decimation_enable: %8s %8s %8s %8s\n", cam->params.compression.decimation == - DECIMATION_ENAB ? "on":"off", "off", "off", + DECIMATION_ENAB ? "on":"off", "off", "on", "off"); out += sprintf(out, "compression_target: %9s %9s %9s %9s\n", cam->params.compressionTarget.frTargeting == @@ -647,13 +559,13 @@ static int cpia_read_proc(char *page, char **start, off_t off, "framerate":"quality", "framerate", "quality", "quality"); out += sprintf(out, "target_framerate: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetFR, 0, 30, 7); + cam->params.compressionTarget.targetFR, 1, 30, 15); out += sprintf(out, "target_quality: %8d %8d %8d %8d\n", - cam->params.compressionTarget.targetQ, 0, 255, 10); + cam->params.compressionTarget.targetQ, 1, 64, 5); out += sprintf(out, "y_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.yThreshold, 0, 31, 15); + cam->params.yuvThreshold.yThreshold, 0, 31, 6); out += sprintf(out, "uv_threshold: %8d %8d %8d %8d\n", - cam->params.yuvThreshold.uvThreshold, 0, 31, 15); + cam->params.yuvThreshold.uvThreshold, 0, 31, 6); out += sprintf(out, "hysteresis: %8d %8d %8d %8d\n", cam->params.compressionParams.hysteresis, 0, 255, 3); out += sprintf(out, "threshold_max: %8d %8d %8d %8d\n", @@ -674,15 +586,25 @@ static int cpia_read_proc(char *page, char **start, off_t off, out += sprintf(out, "decimation_thresh_mod: %8d %8d %8d %8d\n", cam->params.compressionParams.decimationThreshMod, 0, 255, 2); - + + /* GA 04/14/00 - QX3 specific entries */ + if (cam->params.qx3.qx3_detected) { + out += sprintf(out, "toplight: %8s %8s %8s %8s\n", + cam->params.qx3.toplight ? "on" : "off", + "off", "on", "off"); + out += sprintf(out, "bottomlight: %8s %8s %8s %8s\n", + cam->params.qx3.bottomlight ? "on" : "off", + "off", "on", "off"); + } + len = out - page; len -= off; if (len < count) { *eof = 1; if (len <= 0) return 0; - } else { + } else len = count; - } + *start = page + off; return len; } @@ -692,16 +614,14 @@ static int cpia_write_proc(struct file *file, const char *buffer, { struct cam_data *cam = data; struct cam_params new_params; - int retval, len, colon_found; + int retval, find_colon; int size = count; - char *p; unsigned long val; u32 command_flags = 0; u8 new_mains; - if(down_interruptible(&cam->param_lock)) { + if (down_interruptible(&cam->param_lock)) return -ERESTARTSYS; - } /* * Skip over leading whitespace @@ -714,106 +634,112 @@ static int cpia_write_proc(struct file *file, const char *buffer, memcpy(&new_params, &cam->params, sizeof(struct cam_params)); new_mains = cam->mainsFreq; -#define MATCH(x) (len=strlen(x), len <= count && strncmp(buffer, x, len) == 0) +#define MATCH(x) \ + ({ \ + int _len = strlen(x), _ret, _colon_found; \ + _ret = (_len <= count && strncmp(buffer, x, _len) == 0); \ + if (_ret) { \ + buffer += _len; \ + count -= _len; \ + if (find_colon) { \ + _colon_found = 0; \ + while (count && (*buffer == ' ' || *buffer == '\t' || \ + (!_colon_found && *buffer == ':'))) { \ + if (*buffer == ':') \ + _colon_found = 1; \ + --count; \ + ++buffer; \ + } \ + if (!count || !_colon_found) \ + retval = -EINVAL; \ + find_colon = 0; \ + } \ + } \ + _ret; \ + }) #define FIRMWARE_VERSION(x,y) (new_params.version.firmwareVersion == (x) && \ new_params.version.firmwareRevision == (y)) #define VALUE \ - simple_strtoul(buffer, &p, 0); \ - if(p == buffer) { \ - retval = -EINVAL; \ - } else { \ - count -= p-buffer; \ - buffer = p; \ - } -#define FIND_VALUE \ - buffer += len;\ - count -= len; \ - colon_found=0; \ - while(count && (*buffer == ' ' || *buffer == '\t' || \ - (!colon_found && *buffer == ':'))) { \ - if(*buffer == ':') colon_found = 1; \ - --count; \ - ++buffer; \ - } \ - if(!count || !colon_found) { \ - retval = -EINVAL; \ - } -#define FIND_END \ - if(retval == 0) { \ - while(count && isspace(*buffer) && *buffer != '\n'){ \ - --count; \ - ++buffer; \ - } \ - if(count) { \ - if(*buffer != '\n' && *buffer != ';') { \ - retval = -EINVAL; \ - } else { \ - --count; \ - ++buffer; \ - } \ + ({ \ + char *_p; \ + unsigned long int _ret; \ + _ret = simple_strtoul(buffer, &_p, 0); \ + if (_p == buffer) \ + retval = -EINVAL; \ + else { \ + count -= _p - buffer; \ + buffer = _p; \ } \ - } + _ret; \ + }) + retval = 0; - while(count && !retval) { - if(MATCH("brightness")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) - new_params.colourParams.brightness=val; - else retval = -EINVAL; + while (count && !retval) { + find_colon = 1; + if (MATCH("brightness")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.brightness = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("contrast")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) { + } else if (MATCH("contrast")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) { /* contrast is in steps of 8, so round*/ val = ((val + 3) / 8) * 8; /* 1-02 firmware limits contrast to 80*/ - if(FIRMWARE_VERSION(1,2) && val > 80) + if (FIRMWARE_VERSION(1,2) && val > 80) val = 80; + new_params.colourParams.contrast = val; - } else retval = -EINVAL; + } else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("saturation")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 100) - new_params.colourParams.saturation=val; - else retval = -EINVAL; + } else if (MATCH("saturation")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 100) + new_params.colourParams.saturation = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURPARAMS; - FIND_END - } else if(MATCH("sensor_fps")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("sensor_fps")) { + if (!retval) + val = VALUE; + + if (!retval) { /* find values so that sensorFPS is minimized, * but >= val */ - if(val > 30) { + if (val > 30) retval = -EINVAL; - } else if(val > 25) { + else if (val > 25) { new_params.sensorFps.divisor = 0; new_params.sensorFps.baserate = 1; - } else if(val > 15) { + } else if (val > 15) { new_params.sensorFps.divisor = 0; new_params.sensorFps.baserate = 0; - } else if(val > 12) { + } else if (val > 12) { new_params.sensorFps.divisor = 1; new_params.sensorFps.baserate = 1; - } else if(val > 7) { + } else if (val > 7) { new_params.sensorFps.divisor = 1; new_params.sensorFps.baserate = 0; - } else if(val > 6) { + } else if (val > 6) { new_params.sensorFps.divisor = 2; new_params.sensorFps.baserate = 1; - } else if(val > 3) { + } else if (val > 3) { new_params.sensorFps.divisor = 2; new_params.sensorFps.baserate = 0; } else { @@ -825,89 +751,100 @@ static int cpia_write_proc(struct file *file, const char *buffer, flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; } command_flags |= COMMAND_SETSENSORFPS; - FIND_END - } else if(MATCH("stream_start_line")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("stream_start_line")) { + if (!retval) + val = VALUE; + + if (!retval) { int max_line = 288; - if(new_params.format.videoSize==VIDEOSIZE_QCIF) + + if (new_params.format.videoSize == VIDEOSIZE_QCIF) max_line = 144; - if(val <= max_line) + if (val <= max_line) new_params.streamStartLine = val/2; - else retval = -EINVAL; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("ecp_timing")) { - FIND_VALUE - if(!retval && MATCH("normal")) { - buffer += len; - count -= len; + } else if (MATCH("sub_sample")) { + if (!retval && MATCH("420")) + new_params.format.subSample = SUBSAMPLE_420; + else if (!retval && MATCH("422")) + new_params.format.subSample = SUBSAMPLE_422; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("yuv_order")) { + if (!retval && MATCH("YUYV")) + new_params.format.yuvOrder = YUVORDER_YUYV; + else if (!retval && MATCH("UYVY")) + new_params.format.yuvOrder = YUVORDER_UYVY; + else + retval = -EINVAL; + + command_flags |= COMMAND_SETFORMAT; + } else if (MATCH("ecp_timing")) { + if (!retval && MATCH("normal")) new_params.ecpTiming = 0; - } else if(!retval && MATCH("slow")) { - buffer += len; - count -= len; + else if (!retval && MATCH("slow")) new_params.ecpTiming = 1; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETECPTIMING; - FIND_END - } else if(MATCH("color_balance_mode")) { - FIND_VALUE - if(!retval && MATCH("manual")) { - buffer += len; - count -= len; - new_params.colourBalance.balanceMode=1; - } else if(!retval && MATCH("auto")) { - buffer += len; - count -= len; - new_params.colourBalance.balanceMode=2; - } else { + } else if (MATCH("color_balance_mode")) { + if (!retval && MATCH("manual")) + new_params.colourBalance.balanceModeIsAuto = 0; + else if (!retval && MATCH("auto")) + new_params.colourBalance.balanceModeIsAuto = 1; + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("red_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) + } else if (MATCH("red_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) new_params.colourBalance.redGain = val; - else retval = -EINVAL; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("green_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) - new_params.colourBalance.greenGain=val; - else retval = -EINVAL; + } else if (MATCH("green_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) + new_params.colourBalance.greenGain = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("blue_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 212) + } else if (MATCH("blue_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 212) new_params.colourBalance.blueGain = val; - else retval = -EINVAL; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOLOURBALANCE; - FIND_END - } else if(MATCH("max_gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("max_gain")) { + if (!retval) + val = VALUE; + + if (!retval) { /* 1-02 firmware limits gain to 2 */ - if(FIRMWARE_VERSION(1,2) && val > 2) + if (FIRMWARE_VERSION(1,2) && val > 2) val = 2; switch(val) { case 1: @@ -928,44 +865,32 @@ static int cpia_write_proc(struct file *file, const char *buffer, } } command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("exposure_mode")) { - FIND_VALUE - if(!retval && MATCH("auto")) { - buffer += len; - count -= len; + } else if (MATCH("exposure_mode")) { + if (!retval && MATCH("auto")) new_params.exposure.expMode = 2; - } else if(!retval && MATCH("manual")) { - buffer += len; - count -= len; - if(new_params.exposure.expMode == 2) + else if (!retval && MATCH("manual")) { + if (new_params.exposure.expMode == 2) new_params.exposure.expMode = 3; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else { + } else retval = -EINVAL; - } + command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("centre_weight")) { - FIND_VALUE - if(!retval && MATCH("on")) { - buffer += len; - count -= len; + } else if (MATCH("centre_weight")) { + if (!retval && MATCH("on")) new_params.exposure.centreWeight = 1; - } else if(!retval && MATCH("off")) { - buffer += len; - count -= len; + else if (!retval && MATCH("off")) new_params.exposure.centreWeight = 2; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETEXPOSURE; - FIND_END - } else if(MATCH("gain")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("gain")) { + if (!retval) + val = VALUE; + + if (!retval) { switch(val) { case 1: new_params.exposure.gain = 0; @@ -996,35 +921,36 @@ static int cpia_write_proc(struct file *file, const char *buffer, break; } command_flags |= COMMAND_SETEXPOSURE; - if(new_params.exposure.gain > - new_params.exposure.gainMode-1) + if (new_params.exposure.gain > + new_params.exposure.gainMode-1) retval = -EINVAL; } - FIND_END - } else if(MATCH("fine_exp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 256) { + } else if (MATCH("fine_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 256) { /* 1-02 firmware limits fineExp to 127*/ - if(FIRMWARE_VERSION(1,2) && val > 127) + if (FIRMWARE_VERSION(1,2) && val > 127) val = 127; new_params.exposure.fineExp = val; new_params.exposure.expMode = 1; command_flags |= COMMAND_SETEXPOSURE; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("coarse_exp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 65536) { + } else if (MATCH("coarse_exp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 65536) { /* 1-02 firmware limits * coarseExp to 255 */ - if(FIRMWARE_VERSION(1,2) && val > 255) + if (FIRMWARE_VERSION(1,2) && val > 255) val = 255; new_params.exposure.coarseExpLo = val & 0xff; @@ -1034,353 +960,398 @@ static int cpia_write_proc(struct file *file, const char *buffer, command_flags |= COMMAND_SETEXPOSURE; new_params.flickerControl.flickerMode = 0; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("red_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 220 && val <= 255) { + } else if (MATCH("red_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 220 && val <= 255) { new_params.exposure.redComp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("green1_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 214 && val <= 255) { + } else if (MATCH("green1_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { new_params.exposure.green1Comp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("green2_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 214 && val <= 255) { + } else if (MATCH("green2_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 214 && val <= 255) { new_params.exposure.green2Comp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("blue_comp")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val >= 230 && val <= 255) { + } else if (MATCH("blue_comp")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val >= 230 && val <= 255) { new_params.exposure.blueComp = val; command_flags |= COMMAND_SETEXPOSURE; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain1")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain1 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain1 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain2")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain2 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain2 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain4")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain4 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain4 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("apcor_gain8")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { + } else if (MATCH("apcor_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { command_flags |= COMMAND_SETAPCOR; - if(val <= 0xff) new_params.apcor.gain8 = val; - else retval = -EINVAL; + if (val <= 0xff) + new_params.apcor.gain8 = val; + else + retval = -EINVAL; } - FIND_END - } else if(MATCH("vl_offset_gain1")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain1 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain1")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain1 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain2")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain2 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain2")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain2 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain4")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain4 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain4")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain4 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("vl_offset_gain8")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) new_params.vlOffset.gain8 = val; - else retval = -EINVAL; + } else if (MATCH("vl_offset_gain8")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.vlOffset.gain8 = val; + else + retval = -EINVAL; } - FIND_END command_flags |= COMMAND_SETVLOFFSET; - } else if(MATCH("flicker_control")) { - FIND_VALUE - if(!retval && MATCH("on")) { - buffer += len; - count -= len; + } else if (MATCH("flicker_control")) { + if (!retval && MATCH("on")) { new_params.flickerControl.flickerMode = 1; new_params.exposure.expMode = 2; command_flags |= COMMAND_SETEXPOSURE; - } else if(!retval && MATCH("off")) { - buffer += len; - count -= len; + } else if (!retval && MATCH("off")) new_params.flickerControl.flickerMode = 0; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETFLICKERCTRL; - FIND_END - } else if(MATCH("mains_frequency")) { - FIND_VALUE - if(!retval && MATCH("50")) { - buffer += len; - count -= len; + } else if (MATCH("mains_frequency")) { + if (!retval && MATCH("50")) { new_mains = 0; new_params.flickerControl.coarseJump = flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; - } else if(!retval && MATCH("60")) { - buffer += len; - count -= len; + } else if (!retval && MATCH("60")) { new_mains = 1; new_params.flickerControl.coarseJump = flicker_jumps[new_mains] [new_params.sensorFps.baserate] [new_params.sensorFps.divisor]; - if(new_params.flickerControl.flickerMode) + if (new_params.flickerControl.flickerMode) command_flags |= COMMAND_SETFLICKERCTRL; - } else { + } else retval = -EINVAL; - } - FIND_END - } else if(MATCH("allowable_overexposure")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val <= 0xff) { + } else if (MATCH("allowable_overexposure")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) { new_params.flickerControl. allowableOverExposure = val; command_flags |= COMMAND_SETFLICKERCTRL; - } else retval = -EINVAL; + } else + retval = -EINVAL; } - FIND_END - } else if(MATCH("compression_mode")) { - FIND_VALUE - if(!retval && MATCH("none")) { - buffer += len; - count -= len; + } else if (MATCH("compression_mode")) { + if (!retval && MATCH("none")) new_params.compression.mode = CPIA_COMPRESSION_NONE; - } else if(!retval && MATCH("auto")) { - buffer += len; - count -= len; + else if (!retval && MATCH("auto")) new_params.compression.mode = CPIA_COMPRESSION_AUTO; - } else if(!retval && MATCH("manual")) { - buffer += len; - count -= len; + else if (!retval && MATCH("manual")) new_params.compression.mode = CPIA_COMPRESSION_MANUAL; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSION; - FIND_END - } else if(MATCH("decimation")) { - FIND_VALUE - if(!retval && MATCH("off")) { - buffer += len; - count -= len; + } else if (MATCH("decimation_enable")) { + if (!retval && MATCH("off")) new_params.compression.decimation = 0; - } else { + else if (!retval && MATCH("on")) + new_params.compression.decimation = 1; + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSION; - FIND_END - } else if(MATCH("compression_target")) { - FIND_VALUE - if(!retval && MATCH("quality")) { - buffer += len; - count -= len; + } else if (MATCH("compression_target")) { + if (!retval && MATCH("quality")) new_params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_QUALITY; - } else if(!retval && MATCH("framerate")) { - buffer += len; - count -= len; + else if (!retval && MATCH("framerate")) new_params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_FRAMERATE; - } else { + else retval = -EINVAL; - } + command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("target_framerate")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) - new_params.compressionTarget.targetFR = val; + } else if (MATCH("target_framerate")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 30) + new_params.compressionTarget.targetFR = val; + else + retval = -EINVAL; + } command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("target_quality")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) new_params.compressionTarget.targetQ = val; + } else if (MATCH("target_quality")) { + if (!retval) + val = VALUE; + + if (!retval) { + if(val > 0 && val <= 64) + new_params.compressionTarget.targetQ = val; + else + retval = -EINVAL; + } command_flags |= COMMAND_SETCOMPRESSIONTARGET; - FIND_END - } else if(MATCH("y_threshold")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 32) - new_params.yuvThreshold.yThreshold=val; - else retval = -EINVAL; + } else if (MATCH("y_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.yThreshold = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETYUVTHRESH; - FIND_END - } else if(MATCH("uv_threshold")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(val < 32) - new_params.yuvThreshold.uvThreshold=val; - else retval = -EINVAL; + } else if (MATCH("uv_threshold")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val < 32) + new_params.yuvThreshold.uvThreshold = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETYUVTHRESH; - FIND_END - } else if(MATCH("hysteresis")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.hysteresis=val; - else retval = -EINVAL; + } else if (MATCH("hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.hysteresis = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("threshold_max")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.threshMax=val; - else retval = -EINVAL; + } else if (MATCH("threshold_max")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.threshMax = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("small_step")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.smallStep=val; - else retval = -EINVAL; + } else if (MATCH("small_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.smallStep = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("large_step")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams.largeStep=val; - else retval = -EINVAL; + } else if (MATCH("large_step")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.largeStep = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("decimation_hysteresis")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - decimationHysteresis = val; - else retval = -EINVAL; + } else if (MATCH("decimation_hysteresis")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationHysteresis = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("fr_diff_step_thresh")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - frDiffStepThresh = val; - else retval = -EINVAL; + } else if (MATCH("fr_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.frDiffStepThresh = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("q_diff_step_thresh")) { - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - qDiffStepThresh = val; - else retval = -EINVAL; - command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END - } else if(MATCH("decimation_thresh_mod")) { + } else if (MATCH("q_diff_step_thresh")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.qDiffStepThresh = val; + else + retval = -EINVAL; } - FIND_VALUE - if(!retval) { val = VALUE } - if(!retval) { - if(retval <= 0xff) - new_params.compressionParams. - decimationThreshMod = val; - else retval = -EINVAL; + command_flags |= COMMAND_SETCOMPRESSIONPARAMS; + } else if (MATCH("decimation_thresh_mod")) { + if (!retval) + val = VALUE; + + if (!retval) { + if (val <= 0xff) + new_params.compressionParams.decimationThreshMod = val; + else + retval = -EINVAL; } command_flags |= COMMAND_SETCOMPRESSIONPARAMS; - FIND_END + } else if (MATCH("toplight")) { /* GA 4/14/00 */ + if (!retval && MATCH("on")) + new_params.qx3.toplight = 1; + + else if (!retval && MATCH("off")) + new_params.qx3.toplight = 0; + + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + + } else if (MATCH("bottomlight")) { /* GA 4/14/00 */ + if (!retval && MATCH("on")) + new_params.qx3.bottomlight = 1; + + else if (!retval && MATCH("off")) + new_params.qx3.bottomlight = 0; + + else + retval = -EINVAL; + command_flags |= COMMAND_SETLIGHTS; + } else { + DBG("No match found\n"); retval = -EINVAL; } + + if (!retval) { + while (count && isspace(*buffer) && *buffer != '\n') { + --count; + ++buffer; + } + if (count) { + if (*buffer != '\n' && *buffer != ';') + retval = -EINVAL; + else { + --count; + ++buffer; + } + } + } } #undef MATCH #undef FIRMWARE_VERSION #undef VALUE #undef FIND_VALUE #undef FIND_END - if(retval == 0) { - if(command_flags & COMMAND_SETCOLOURPARAMS) { + if (!retval) { + if (command_flags & COMMAND_SETCOLOURPARAMS) { /* Adjust cam->vp to reflect these changes */ cam->vp.brightness = new_params.colourParams.brightness*65535/100; @@ -1394,9 +1365,8 @@ static int cpia_write_proc(struct file *file, const char *buffer, cam->mainsFreq = new_mains; cam->cmd_queue |= command_flags; retval = size; - } else { + } else DBG("error: %d\n", retval); - } up(&cam->param_lock); @@ -1408,17 +1378,21 @@ static void create_proc_cpia_cam(struct cam_data *cam) char name[7]; struct proc_dir_entry *ent; - if(cpia_proc_root == NULL || cam == NULL) { + if (!cpia_proc_root || !cam) return; - } + sprintf(name, "video%d", cam->vdev.minor); ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, cpia_proc_root); - if (!ent) return; + if (!ent) + return; + ent->data = cam; ent->read_proc = cpia_read_proc; ent->write_proc = cpia_write_proc; - ent->size = 3623; + ent->size = 3736; + if (cam->params.qx3.qx3_detected) + ent->size += 188; cam->proc_entry = ent; } @@ -1426,16 +1400,43 @@ static void destroy_proc_cpia_cam(struct cam_data *cam) { char name[7]; - if(cam == NULL || cam->proc_entry == NULL) return; + if (!cam || !cam->proc_entry) + return; sprintf(name, "video%d", cam->vdev.minor); remove_proc_entry(name, cpia_proc_root); cam->proc_entry = NULL; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) +/* + * This is called as the fill_inode function when an inode + * is going into (fill = 1) or out of service (fill = 0). + * We use it here to manage the module use counts. + */ +static void proc_cpia_modcount(struct inode *inode, int fill) +{ +#ifdef MODULE + if (fill) + MOD_INC_USE_COUNT; + else + MOD_DEC_USE_COUNT; +#endif +} +#endif + static void proc_cpia_create(void) { cpia_proc_root = create_proc_entry("cpia", S_IFDIR, 0); + + if (cpia_proc_root) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + cpia_proc_root->fill_inode = &proc_cpia_modcount; +#else + cpia_proc_root->owner = THIS_MODULE; +#endif + } else + LOG("Unable to initialise /proc/cpia\n"); } #ifdef MODULE @@ -1444,6 +1445,7 @@ static void proc_cpia_destroy(void) remove_proc_entry("cpia", 0); } #endif +#endif /* CONFIG_PROC_FS */ /* ----------------------- debug functions ---------------------- */ @@ -1473,39 +1475,41 @@ static int match_videosize( int width, int height ) { /* return the best match, where 'best' is as always * the largest that is not bigger than what is requested. */ - if(width>=352 && height>=288) { + if (width>=352 && height>=288) return VIDEOSIZE_352_288; /* CIF */ - } - if(width>=320 && height>=240) { + + if (width>=320 && height>=240) return VIDEOSIZE_320_240; /* SIF */ - } - if(width>=288 && height>=216) { + + if (width>=288 && height>=216) return VIDEOSIZE_288_216; - } - if(width>=256 && height>=192) { + + if (width>=256 && height>=192) return VIDEOSIZE_256_192; - } - if(width>=224 && height>=168) { + + if (width>=224 && height>=168) return VIDEOSIZE_224_168; - } - if(width>=192 && height>=144) { + + if (width>=192 && height>=144) return VIDEOSIZE_192_144; - } - if(width>=176 && height>=144) { + + if (width>=176 && height>=144) return VIDEOSIZE_176_144; /* QCIF */ - } - if(width>=160 && height>=120) { + + if (width>=160 && height>=120) return VIDEOSIZE_160_120; /* QSIF */ - } - if(width>=128 && height>=96) { + + if (width>=128 && height>=96) return VIDEOSIZE_128_96; - } - if(width>=64 && height>=48) { + + if (width>=88 && height>=72) + return VIDEOSIZE_88_72; + + if (width>=64 && height>=48) return VIDEOSIZE_64_48; - } - if(width>=48 && height>=48) { + + if (width>=48 && height>=48) return VIDEOSIZE_48_48; - } return -1; } @@ -1608,6 +1612,16 @@ static void set_vw_size(struct cam_data *cam) cam->params.roi.rowEnd=30; cam->params.streamStartLine = 60; break; + case VIDEOSIZE_88_72: + cam->vw.width = 88; + cam->vw.height = 72; + cam->params.format.videoSize=VIDEOSIZE_QCIF; + cam->params.roi.colStart=5; + cam->params.roi.colEnd=16; + cam->params.roi.rowStart=9; + cam->params.roi.rowEnd=27; + cam->params.streamStartLine = 60; + break; case VIDEOSIZE_64_48: cam->vw.width = 64; cam->vw.height = 48; @@ -1635,27 +1649,29 @@ static void set_vw_size(struct cam_data *cam) return; } -static int allocate_frame_buf(struct cam_data *cam) { +static int allocate_frame_buf(struct cam_data *cam) +{ int i; - cam->frame_buf = rvmalloc(FRAME_NUM*CPIA_MAX_FRAME_SIZE); - if (!cam->frame_buf) { + cam->frame_buf = rvmalloc(FRAME_NUM * CPIA_MAX_FRAME_SIZE); + if (!cam->frame_buf) return -ENOBUFS; - } - for( i=0; iframe[i].data = cam->frame_buf + i*CPIA_MAX_FRAME_SIZE; - } + + for (i = 0; i < FRAME_NUM; i++) + cam->frame[i].data = cam->frame_buf + i * CPIA_MAX_FRAME_SIZE; + return 0; } -static int free_frame_buf(struct cam_data *cam) { +static int free_frame_buf(struct cam_data *cam) +{ int i; rvfree(cam->frame_buf, FRAME_NUM*CPIA_MAX_FRAME_SIZE); - cam->frame_buf=0; - for( i=0; iframe_buf = 0; + for (i=0; i < FRAME_NUM; i++) cam->frame[i].data = NULL; - } + return 0; } @@ -1663,9 +1679,9 @@ static int free_frame_buf(struct cam_data *cam) { static void inline free_frames(struct cpia_frame frame[FRAME_NUM]) { int i; - for( i=0; iparam_lock); datasize=8; break; + case CPIA_COMMAND_ReadMCPorts: /* GA 4/14/00 */ + case CPIA_COMMAND_ReadVCRegs: + datasize = 4; + break; default: datasize=0; break; @@ -1708,12 +1728,12 @@ static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) cmd[7] = 0; retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if(retval) { - LOG("%x - failed\n", command); + if (retval) { + DBG("%x - failed, retval=%d\n", command, retval); if (command == CPIA_COMMAND_GetColourParams || command == CPIA_COMMAND_GetColourBalance || command == CPIA_COMMAND_GetExposure) - up(&cam->param_lock); + up(&cam->param_lock); } else { switch(command) { case CPIA_COMMAND_GetCPIAVersion: @@ -1765,9 +1785,48 @@ static int do_command(struct cam_data *cam, u16 command, u8 a, u8 b, u8 c, u8 d) cam->params.exposure.green1Comp = data[5]; cam->params.exposure.green2Comp = data[6]; cam->params.exposure.blueComp = data[7]; + /* If the *Comp parameters are wacko, generate + * a warning, and reset them back to default + * values. - rich@annexia.org + */ + if (cam->params.exposure.redComp < 220 || + cam->params.exposure.redComp > 255 || + cam->params.exposure.green1Comp < 214 || + cam->params.exposure.green1Comp > 255 || + cam->params.exposure.green2Comp < 214 || + cam->params.exposure.green2Comp > 255 || + cam->params.exposure.blueComp < 230 || + cam->params.exposure.blueComp > 255) + { + printk (KERN_WARNING "*_comp parameters have gone AWOL (%d/%d/%d/%d) - reseting them\n", + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); + cam->params.exposure.redComp = 220; + cam->params.exposure.green1Comp = 214; + cam->params.exposure.green2Comp = 214; + cam->params.exposure.blueComp = 230; + } up(&cam->param_lock); break; - default: + + case CPIA_COMMAND_ReadMCPorts: /* GA 04/14/00 */ + if (!cam->params.qx3.qx3_detected) break; + + /* test button press */ + cam->params.qx3.button = ((data[1] & 0x02) == 0); + if (cam->params.qx3.button) { + /* button pressed - unlock the latch */ + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xDF,0xDF,0); + do_command(cam,CPIA_COMMAND_WriteMCPort,3,0xFF,0xFF,0); + } + + /* test whether microscope is cradled */ + cam->params.qx3.cradled = ((data[2] & 0x40) == 0); + break; + + default: break; } } @@ -1782,6 +1841,7 @@ static int do_command_extended(struct cam_data *cam, u16 command, { int retval; u8 cmd[8], data[8]; + cmd[0] = command>>8; cmd[1] = command&0xff; cmd[2] = a; @@ -1800,29 +1860,186 @@ static int do_command_extended(struct cam_data *cam, u16 command, data[7] = l; retval = cam->ops->transferCmd(cam->lowlevel_data, cmd, data); - if(retval) { + if (retval) LOG("%x - failed\n", command); + + return retval; +} + +/********************************************************************** + * + * Colorspace conversion + * + **********************************************************************/ +#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) + +static int convert420(unsigned char *yuv, unsigned char *rgb, int out_fmt, + int linesize, int mmap_kludge) +{ + int y, u, v, r, g, b, y1; + + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = ((*(rgb+1-linesize)) & 0x7c) << 1; + g = ((*(rgb-linesize)) & 0xe0) >> 4 | + ((*(rgb+1-linesize)) & 0x03) << 6; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_RGB565: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + r = (*(rgb+1-linesize)) & 0xf8; + g = ((*(rgb-linesize)) & 0xe0) >> 3 | + ((*(rgb+1-linesize)) & 0x07) << 5; + b = ((*(rgb-linesize)) & 0x1f) << 3; + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u - 53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + y = (*yuv++ - 16) * 76310; + y1 = (*yuv - 16) * 76310; + if (mmap_kludge) { + r = *(rgb+2-linesize); + g = *(rgb+1-linesize); + b = *(rgb-linesize); + } else { + r = *(rgb-linesize); + g = *(rgb+1-linesize); + b = *(rgb+2-linesize); + } + u = (-53294 * r - 104635 * g + 157929 * b) / 5756495; + v = (157968 * r - 132278 * g - 25690 * b) / 5366159; + r = 104635 * v; + g = -25690 * u + -53294 * v; + b = 132278 * u; + break; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + y = *yuv++; + u = *(rgb+1-linesize); + y1 = *yuv; + v = *(rgb+3-linesize); + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + case VIDEO_PALETTE_UYVY: + u = *(rgb-linesize); + y = *yuv++; + v = *(rgb+2-linesize); + y1 = *yuv; + /* Just to avoid compiler warnings */ + r = 0; + g = 0; + b = 0; + break; + case VIDEO_PALETTE_GREY: + default: + y = *yuv++; + y1 = *yuv; + /* Just to avoid compiler warnings */ + u = 0; + v = 0; + r = 0; + g = 0; + b = 0; + break; + } + switch(out_fmt) { + case VIDEO_PALETTE_RGB555: + *rgb++ = ((LIMIT(g+y) & 0xf8) << 2) | (LIMIT(b+y) >> 3); + *rgb++ = ((LIMIT(r+y) & 0xf8) >> 1) | (LIMIT(g+y) >> 6); + *rgb++ = ((LIMIT(g+y1) & 0xf8) << 2) | (LIMIT(b+y1) >> 3); + *rgb = ((LIMIT(r+y1) & 0xf8) >> 1) | (LIMIT(g+y1) >> 6); + return 4; + case VIDEO_PALETTE_RGB565: + *rgb++ = ((LIMIT(g+y) & 0xfc) << 3) | (LIMIT(b+y) >> 3); + *rgb++ = (LIMIT(r+y) & 0xf8) | (LIMIT(g+y) >> 5); + *rgb++ = ((LIMIT(g+y1) & 0xfc) << 3) | (LIMIT(b+y1) >> 3); + *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); + return 4; + case VIDEO_PALETTE_RGB24: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 6; + case VIDEO_PALETTE_RGB32: + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } + return 8; + case VIDEO_PALETTE_GREY: + *rgb++ = y; + *rgb = y1; + return 2; + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUYV: + *rgb++ = y; + *rgb++ = u; + *rgb++ = y1; + *rgb = v; + return 4; + case VIDEO_PALETTE_UYVY: + *rgb++ = u; + *rgb++ = y; + *rgb++ = v; + *rgb = y1; + return 4; + default: + DBG("Empty: %d\n", out_fmt); + return 0; } - return retval; } -/********************************************************************** - * - * Colorspace conversion - * - **********************************************************************/ -#define LIMIT(x) ((((x)>0xffffff)?0xff0000:(((x)<=0xffff)?0:(x)&0xff0000))>>16) - static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, - int in_uyvy) + int in_uyvy, int mmap_kludge) { int y, u, v, r, g, b, y1; + switch(out_fmt) { case VIDEO_PALETTE_RGB555: case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_RGB24: case VIDEO_PALETTE_RGB32: - if(in_uyvy) { + if (in_uyvy) { u = *yuv++ - 128; y = (*yuv++ - 16) * 76310; v = *yuv++ - 128; @@ -1862,21 +2079,40 @@ static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, *rgb = (LIMIT(r+y1) & 0xf8) | (LIMIT(g+y1) >> 5); return 4; case VIDEO_PALETTE_RGB24: - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } return 6; case VIDEO_PALETTE_RGB32: - *rgb++ = LIMIT(b+y); - *rgb++ = LIMIT(g+y); - *rgb++ = LIMIT(r+y); - rgb++; - *rgb++ = LIMIT(b+y1); - *rgb++ = LIMIT(g+y1); - *rgb = LIMIT(r+y1); + if (mmap_kludge) { + *rgb++ = LIMIT(b+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(r+y); + rgb++; + *rgb++ = LIMIT(b+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(r+y1); + } else { + *rgb++ = LIMIT(r+y); + *rgb++ = LIMIT(g+y); + *rgb++ = LIMIT(b+y); + rgb++; + *rgb++ = LIMIT(r+y1); + *rgb++ = LIMIT(g+y1); + *rgb = LIMIT(b+y1); + } return 8; case VIDEO_PALETTE_GREY: *rgb++ = y; @@ -1896,11 +2132,13 @@ static int yuvconvert(unsigned char *yuv, unsigned char *rgb, int out_fmt, *rgb = y1; return 4; default: + DBG("Empty: %d\n", out_fmt); return 0; } } -static int skipcount(int count, int fmt) { +static int skipcount(int count, int fmt) +{ switch(fmt) { case VIDEO_PALETTE_GREY: return count; @@ -1922,66 +2160,68 @@ static int skipcount(int count, int fmt) { static int parse_picture(struct cam_data *cam, int size) { u8 *obuf, *ibuf, *end_obuf; - int ll, in_uyvy, compressed, origsize, out_fmt; + int ll, in_uyvy, compressed, decimation, even_line, origsize, out_fmt; + int rows, cols, linesize, subsample_422; /* make sure params don't change while we are decoding */ down(&cam->param_lock); - + obuf = cam->decompressed_frame.data; end_obuf = obuf+CPIA_MAX_FRAME_SIZE; ibuf = cam->raw_image; origsize = size; out_fmt = cam->vp.palette; - - if((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { + + if ((ibuf[0] != MAGIC_0) || (ibuf[1] != MAGIC_1)) { LOG("header not found\n"); up(&cam->param_lock); return -1; } - if((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { + if ((ibuf[16] != VIDEOSIZE_QCIF) && (ibuf[16] != VIDEOSIZE_CIF)) { LOG("wrong video size\n"); up(&cam->param_lock); return -1; } - if(ibuf[17] != SUBSAMPLE_422) { + if (ibuf[17] != SUBSAMPLE_420 && ibuf[17] != SUBSAMPLE_422) { LOG("illegal subtype %d\n",ibuf[17]); up(&cam->param_lock); return -1; } + subsample_422 = ibuf[17] == SUBSAMPLE_422; - if(ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { + if (ibuf[18] != YUVORDER_YUYV && ibuf[18] != YUVORDER_UYVY) { LOG("illegal yuvorder %d\n",ibuf[18]); up(&cam->param_lock); return -1; } in_uyvy = ibuf[18] == YUVORDER_UYVY; -#if 0 - /* FIXME: ROI mismatch occurs when switching capture sizes */ - if((ibuf[24] != cam->params.roi.colStart) || - (ibuf[25] != cam->params.roi.colEnd) || - (ibuf[26] != cam->params.roi.rowStart) || - (ibuf[27] != cam->params.roi.rowEnd)) { + if ((ibuf[24] != cam->params.roi.colStart) || + (ibuf[25] != cam->params.roi.colEnd) || + (ibuf[26] != cam->params.roi.rowStart) || + (ibuf[27] != cam->params.roi.rowEnd)) { LOG("ROI mismatch\n"); up(&cam->param_lock); return -1; } -#endif + cols = 8*(ibuf[25] - ibuf[24]); + rows = 4*(ibuf[27] - ibuf[26]); - if((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { + if ((ibuf[28] != NOT_COMPRESSED) && (ibuf[28] != COMPRESSED)) { LOG("illegal compression %d\n",ibuf[28]); up(&cam->param_lock); return -1; } compressed = (ibuf[28] == COMPRESSED); - if(ibuf[29] != NO_DECIMATION) { - LOG("decimation not supported\n"); + if (ibuf[29] != NO_DECIMATION && ibuf[29] != DECIMATION_ENAB) { + LOG("illegal decimation %d\n",ibuf[29]); up(&cam->param_lock); return -1; } + decimation = (ibuf[29] == DECIMATION_ENAB); cam->params.yuvThreshold.yThreshold = ibuf[30]; cam->params.yuvThreshold.uvThreshold = ibuf[31]; @@ -1994,98 +2234,135 @@ static int parse_picture(struct cam_data *cam, int size) cam->params.status.vpStatus = ibuf[38]; cam->params.status.errorCode = ibuf[39]; cam->fps = ibuf[41]; + up(&cam->param_lock); - ibuf += 64; - size -= 64; + linesize = skipcount(cols, out_fmt); + ibuf += FRAME_HEADER_SIZE; + size -= FRAME_HEADER_SIZE; ll = ibuf[0] | (ibuf[1] << 8); ibuf += 2; - while(size > 0) { + even_line = 1; + + while (size > 0) { size -= (ll+2); - if(size < 0) { + if (size < 0) { LOG("Insufficient data in buffer\n"); - up(&cam->param_lock); return -1; } - while(ll > 1) { - if(!compressed || (compressed && !(*ibuf & 1))) { - obuf += yuvconvert(ibuf,obuf,out_fmt,in_uyvy); - ibuf += 4; - ll -= 4; + + while (ll > 1) { + if (!compressed || (compressed && !(*ibuf & 1))) { + if(subsample_422 || even_line) { + obuf += yuvconvert(ibuf, obuf, out_fmt, + in_uyvy, cam->mmap_kludge); + ibuf += 4; + ll -= 4; + } else { + /* SUBSAMPLE_420 on an odd line */ + obuf += convert420(ibuf, obuf, + out_fmt, linesize, + cam->mmap_kludge); + ibuf += 2; + ll -= 2; + } } else { /*skip compressed interval from previous frame*/ - int skipsize = skipcount(*ibuf >> 1, out_fmt); - obuf += skipsize; - if( obuf > end_obuf ) { - LOG("Insufficient data in buffer\n"); - up(&cam->param_lock); + obuf += skipcount(*ibuf >> 1, out_fmt); + if (obuf > end_obuf) { + LOG("Insufficient buffer size\n"); return -1; } ++ibuf; ll--; } } - if(ll == 1) { - if(*ibuf != EOL) { + if (ll == 1) { + if (*ibuf != EOL) { LOG("EOL not found giving up after %d/%d" " bytes\n", origsize-size, origsize); - up(&cam->param_lock); return -1; } - ibuf++; /* skip over EOL */ + ++ibuf; /* skip over EOL */ - if((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && + if ((size > 3) && (ibuf[0] == EOI) && (ibuf[1] == EOI) && (ibuf[2] == EOI) && (ibuf[3] == EOI)) { size -= 4; break; } - if(size > 1) { + if(decimation) { + /* skip the odd lines for now */ + obuf += linesize; + } + + if (size > 1) { ll = ibuf[0] | (ibuf[1] << 8); ibuf += 2; /* skip over line length */ } + + if(!decimation) + even_line = !even_line; } else { LOG("line length was not 1 but %d after %d/%d bytes\n", ll, origsize-size, origsize); - up(&cam->param_lock); return -1; } } + if(decimation) { + /* interpolate odd rows */ + int i, j; + u8 *prev, *next; + prev = cam->decompressed_frame.data; + obuf = prev+linesize; + next = obuf+linesize; + for(i=1; idecompressed_frame.count = obuf-cam->decompressed_frame.data; - up(&cam->param_lock); - return cam->decompressed_frame.count; } /* InitStreamCap wrapper to select correct start line */ -static inline int init_stream_cap(struct cam_data *cam) { +static inline int init_stream_cap(struct cam_data *cam) +{ return do_command(cam, CPIA_COMMAND_InitStreamCap, 0, cam->params.streamStartLine, 0, 0); } /* update various camera modes and settings */ -static void dispatch_commands(struct cam_data *cam ) { +static void dispatch_commands(struct cam_data *cam) +{ down(&cam->param_lock); - if( cam->cmd_queue==COMMAND_NONE ) { + if (cam->cmd_queue==COMMAND_NONE) { up(&cam->param_lock); return; } DEB_BYTE(cam->cmd_queue); DEB_BYTE(cam->cmd_queue>>8); - if( cam->cmd_queue & COMMAND_SETCOLOURPARAMS ) { + if (cam->cmd_queue & COMMAND_SETCOLOURPARAMS) do_command(cam, CPIA_COMMAND_SetColourParams, cam->params.colourParams.brightness, cam->params.colourParams.contrast, cam->params.colourParams.saturation, 0); - } - if( cam->cmd_queue & COMMAND_SETCOMPRESSION ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSION) do_command(cam, CPIA_COMMAND_SetCompression, cam->params.compression.mode, cam->params.compression.decimation, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETFORMAT ) { + + if (cam->cmd_queue & COMMAND_SETFORMAT) { do_command(cam, CPIA_COMMAND_SetFormat, cam->params.format.videoSize, cam->params.format.subSample, @@ -2095,22 +2372,23 @@ static void dispatch_commands(struct cam_data *cam ) { cam->params.roi.rowStart, cam->params.roi.rowEnd); cam->first_frame = 1; } - if( cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONTARGET) do_command(cam, CPIA_COMMAND_SetCompressionTarget, cam->params.compressionTarget.frTargeting, cam->params.compressionTarget.targetFR, cam->params.compressionTarget.targetQ, 0); - } - if( cam->cmd_queue & COMMAND_SETYUVTHRESH ) { + + if (cam->cmd_queue & COMMAND_SETYUVTHRESH) do_command(cam, CPIA_COMMAND_SetYUVThresh, cam->params.yuvThreshold.yThreshold, cam->params.yuvThreshold.uvThreshold, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETECPTIMING ) { + + if (cam->cmd_queue & COMMAND_SETECPTIMING) do_command(cam, CPIA_COMMAND_SetECPTiming, cam->params.ecpTiming, 0, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS ) { + + if (cam->cmd_queue & COMMAND_SETCOMPRESSIONPARAMS) do_command_extended(cam, CPIA_COMMAND_SetCompressionParams, 0, 0, 0, 0, cam->params.compressionParams.hysteresis, @@ -2121,8 +2399,8 @@ static void dispatch_commands(struct cam_data *cam ) { cam->params.compressionParams.frDiffStepThresh, cam->params.compressionParams.qDiffStepThresh, cam->params.compressionParams.decimationThreshMod); - } - if( cam->cmd_queue & COMMAND_SETEXPOSURE ) { + + if (cam->cmd_queue & COMMAND_SETEXPOSURE) do_command_extended(cam, CPIA_COMMAND_SetExposure, cam->params.exposure.gainMode, cam->params.exposure.expMode, @@ -2136,150 +2414,191 @@ static void dispatch_commands(struct cam_data *cam ) { cam->params.exposure.green1Comp, cam->params.exposure.green2Comp, cam->params.exposure.blueComp); + + if (cam->cmd_queue & COMMAND_SETCOLOURBALANCE) { + if (cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 1, + cam->params.colourBalance.redGain, + cam->params.colourBalance.greenGain, + cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); + } } - if( cam->cmd_queue & COMMAND_SETCOLOURBALANCE ) { - do_command(cam, CPIA_COMMAND_SetColourBalance, - cam->params.colourBalance.balanceMode, - cam->params.colourBalance.redGain, - cam->params.colourBalance.greenGain, - cam->params.colourBalance.blueGain); - } - if( cam->cmd_queue & COMMAND_SETSENSORFPS ) { + + if (cam->cmd_queue & COMMAND_SETSENSORFPS) do_command(cam, CPIA_COMMAND_SetSensorFPS, cam->params.sensorFps.divisor, cam->params.sensorFps.baserate, 0, 0); - } - if( cam->cmd_queue & COMMAND_SETAPCOR ) { + + if (cam->cmd_queue & COMMAND_SETAPCOR) do_command(cam, CPIA_COMMAND_SetApcor, cam->params.apcor.gain1, cam->params.apcor.gain2, cam->params.apcor.gain4, cam->params.apcor.gain8); - } - if( cam->cmd_queue & COMMAND_SETFLICKERCTRL ) { + + if (cam->cmd_queue & COMMAND_SETFLICKERCTRL) do_command(cam, CPIA_COMMAND_SetFlickerCtrl, cam->params.flickerControl.flickerMode, cam->params.flickerControl.coarseJump, cam->params.flickerControl.allowableOverExposure, 0); - } - if( cam->cmd_queue & COMMAND_SETVLOFFSET ) { + + if (cam->cmd_queue & COMMAND_SETVLOFFSET) do_command(cam, CPIA_COMMAND_SetVLOffset, cam->params.vlOffset.gain1, cam->params.vlOffset.gain2, cam->params.vlOffset.gain4, cam->params.vlOffset.gain8); - } - if( cam->cmd_queue & COMMAND_PAUSE ) { + + if (cam->cmd_queue & COMMAND_PAUSE) do_command(cam, CPIA_COMMAND_EndStreamCap, 0, 0, 0, 0); - } - if( cam->cmd_queue & COMMAND_RESUME ) { - init_stream_cap( cam ); - } + + if (cam->cmd_queue & COMMAND_RESUME) + init_stream_cap(cam); + + /* GA 04/14/00 */ + if (cam->cmd_queue & COMMAND_SETLIGHTS && cam->params.qx3.qx3_detected) + { + int p1 = (cam->params.qx3.bottomlight == 0) << 1; + int p2 = (cam->params.qx3.toplight == 0) << 3; + do_command(cam, CPIA_COMMAND_WriteVCReg, 0x90, 0x8F, 0x50, 0); + do_command(cam, CPIA_COMMAND_WriteMCPort, 2, 0, (p1|p2|0xE0), 0); + } + up(&cam->param_lock); - cam->cmd_queue=COMMAND_NONE; + cam->cmd_queue = COMMAND_NONE; return; } /* kernel thread function to read image from camera */ static void fetch_frame(void *data) { - int image_size; + int image_size, retry; struct cam_data *cam = (struct cam_data *)data; unsigned long oldjif, rate, diff; - /* load first frame always uncompressed */ - if( cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { - do_command(cam, CPIA_COMMAND_SetCompression, - CPIA_COMPRESSION_NONE, - NO_DECIMATION, 0, 0); - } - - /* init camera upload */ - if(do_command(cam, CPIA_COMMAND_SetGrabMode, CPIA_GRAB_CONTINUOUS, - 0, 0, 0)) goto end; - if(do_command(cam, CPIA_COMMAND_GrabFrame, 0, - cam->params.streamStartLine, 0, 0)) goto end; - - /* loop until image ready */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - while(cam->params.status.streamState != STREAM_READY) { - if(current->need_resched) {schedule();} - current->state=TASK_INTERRUPTIBLE; - schedule_timeout(10*HZ/1000); /* 10 ms, hopefully ;) */ - if(signal_pending(current)) { - goto end; + /* Allow up to two bad images in a row to be read and + * ignored before an error is reported */ + for (retry = 0; retry < 3; ++retry) { + if (retry) + DBG("retry=%d\n", retry); + + if (!cam->ops) + continue; + + /* load first frame always uncompressed */ + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) + do_command(cam, CPIA_COMMAND_SetCompression, + CPIA_COMPRESSION_NONE, + NO_DECIMATION, 0, 0); + + /* init camera upload */ + if (do_command(cam, CPIA_COMMAND_SetGrabMode, + CPIA_GRAB_CONTINUOUS, 0, 0, 0)) + continue; + + if (do_command(cam, CPIA_COMMAND_GrabFrame, 0, + cam->params.streamStartLine, 0, 0)) + continue; + + if (cam->ops->wait_for_stream_ready) { + /* loop until image ready */ + do_command(cam, CPIA_COMMAND_GetCameraStatus,0,0,0,0); + while (cam->params.status.streamState != STREAM_READY) { + if (current->need_resched) + schedule(); + + current->state = TASK_INTERRUPTIBLE; + + /* sleep for 10 ms, hopefully ;) */ + schedule_timeout(10*HZ/1000); + if (signal_pending(current)) + return; + + do_command(cam, CPIA_COMMAND_GetCameraStatus, + 0, 0, 0, 0); + } } - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - } - /* grab image from camera */ - if(current->need_resched) {schedule();} - oldjif = jiffies; - image_size=cam->ops->streamRead(cam->lowlevel_data, cam->raw_image, 0); - if(image_size<=0) { - DBG("read frame failed\n"); - goto end; - } - rate = image_size * HZ / 1024; - diff = jiffies-oldjif; - rate = diff==0 ? rate : rate/diff; /* unlikely but possible */ - /* Keep track of transfer_rate as a runnung average over 3 frames - * to smooth out any inconsistencies */ - cam->transfer_rate = (2*cam->transfer_rate + rate) / 3; + /* grab image from camera */ + if (current->need_resched) + schedule(); - /* camera idle now so dispatch queued commands */ - dispatch_commands( cam ); + oldjif = jiffies; + image_size = cam->ops->streamRead(cam->lowlevel_data, + cam->raw_image, 0); + if (image_size <= 0) { + DBG("streamRead failed: %d\n", image_size); + continue; + } - /* Update our knowledge of the camera state - FIXME: necessary? */ - do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); - do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); - - /* decompress and convert image to by copying it from - * raw_image to decompressed_frame - */ - if(current->need_resched) {schedule();} - cam->image_size = parse_picture(cam, image_size); - if( cam->image_size <= 0 ) { - DBG("parse frame failed\n"); - goto end; - } - - /* FIXME: this only works for double buffering */ - if( cam->frame[cam->curframe].state == FRAME_READY ) { - memcpy(cam->frame[cam->curframe].data, - cam->decompressed_frame.data, - cam->decompressed_frame.count); - cam->frame[cam->curframe].state = FRAME_DONE; - } else { - cam->decompressed_frame.state = FRAME_DONE; + rate = image_size * HZ / 1024; + diff = jiffies-oldjif; + cam->transfer_rate = diff==0 ? rate : rate/diff; + /* diff==0 ? unlikely but possible */ + + /* camera idle now so dispatch queued commands */ + dispatch_commands(cam); + + /* Update our knowledge of the camera state - FIXME: necessary? */ + do_command(cam, CPIA_COMMAND_GetColourBalance, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_GetExposure, 0, 0, 0, 0); + do_command(cam, CPIA_COMMAND_ReadMCPorts, 0, 0, 0, 0); /* GA */ + + + /* decompress and convert image to by copying it from + * raw_image to decompressed_frame + */ + if (current->need_resched) + schedule(); + + cam->image_size = parse_picture(cam, image_size); + if (cam->image_size <= 0) + DBG("parse_picture failed %d\n", cam->image_size); + else + break; } + if (retry < 3) { + /* FIXME: this only works for double buffering */ + if (cam->frame[cam->curframe].state == FRAME_READY) { + memcpy(cam->frame[cam->curframe].data, + cam->decompressed_frame.data, + cam->decompressed_frame.count); + cam->frame[cam->curframe].state = FRAME_DONE; + } else + cam->decompressed_frame.state = FRAME_DONE; + #if 0 - if( cam->first_frame && - cam->params.compression.mode != CPIA_COMPRESSION_NONE ) { - cam->first_frame = 0; - cam->cmd_queue |= COMMAND_SETCOMPRESSION; - } + if (cam->first_frame && + cam->params.compression.mode != CPIA_COMPRESSION_NONE) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + } #else - if( cam->first_frame ) { - cam->first_frame = 0; - cam->cmd_queue |= COMMAND_SETCOMPRESSION; - cam->cmd_queue |= COMMAND_SETEXPOSURE; - } + if (cam->first_frame) { + cam->first_frame = 0; + cam->cmd_queue |= COMMAND_SETCOMPRESSION; + cam->cmd_queue |= COMMAND_SETEXPOSURE; + } #endif -end: - return; + } } static int capture_frame(struct cam_data *cam, struct video_mmap *vm) { int retval = 0; - if( cam->frame_buf == NULL ) { /* we do lazy allocation */ - if( (retval = allocate_frame_buf(cam)) ) { + if (!cam->frame_buf) { + /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) return retval; - } } /* FIXME: the first frame seems to be captured by the camera @@ -2287,25 +2606,30 @@ static int capture_frame(struct cam_data *cam, struct video_mmap *vm) that one, the next one is generated with our settings (exposure, color balance, ...) */ - if(cam->first_frame) { - cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - fetch_frame(cam); - if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + if (cam->first_frame) { + cam->curframe = vm->frame; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval = -EIO; } cam->curframe = vm->frame; - cam->frame[cam->curframe].state = FRAME_READY; - fetch_frame(cam); - if(cam->frame[cam->curframe].state != FRAME_DONE) retval=-EIO; + cam->frame[cam->curframe].state = FRAME_READY; + fetch_frame(cam); + if (cam->frame[cam->curframe].state != FRAME_DONE) + retval=-EIO; return retval; } static int goto_high_power(struct cam_data *cam) { - if(do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) return -1; - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; - if(cam->params.status.systemState == HI_POWER_STATE) { + if (do_command(cam, CPIA_COMMAND_GotoHiPower, 0, 0, 0, 0)) + return -1; + mdelay(100); /* windows driver does it too */ + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == HI_POWER_STATE) { DBG("camera now in HIGH power state\n"); return 0; } @@ -2315,14 +2639,15 @@ static int goto_high_power(struct cam_data *cam) static int goto_low_power(struct cam_data *cam) { - if(do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) return -1; - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -1; - if(cam->params.status.systemState == LO_POWER_STATE) { + if (do_command(cam, CPIA_COMMAND_GotoLoPower, 0, 0, 0, 0)) + return -1; + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + return -1; + if (cam->params.status.systemState == LO_POWER_STATE) { DBG("camera now in LOW power state\n"); return 0; } printstatus(cam); - mdelay(100); /* windows driver does it too */ return -1; } @@ -2344,21 +2669,23 @@ static void save_camera_state(struct cam_data *cam) cam->params.colourBalance.redGain, cam->params.colourBalance.greenGain, cam->params.colourBalance.blueGain); - - return; } static void set_camera_state(struct cam_data *cam) { - if(cam->params.colourBalance.balanceMode != 1) { + if(cam->params.colourBalance.balanceModeIsAuto) { + do_command(cam, CPIA_COMMAND_SetColourBalance, + 2, 0, 0, 0); + } else { do_command(cam, CPIA_COMMAND_SetColourBalance, 1, cam->params.colourBalance.redGain, cam->params.colourBalance.greenGain, cam->params.colourBalance.blueGain); + do_command(cam, CPIA_COMMAND_SetColourBalance, + 3, 0, 0, 0); } - if(cam->params.colourBalance.balanceMode == 0) - cam->params.colourBalance.balanceMode = 2; + do_command_extended(cam, CPIA_COMMAND_SetExposure, cam->params.exposure.gainMode, 1, 1, @@ -2367,19 +2694,19 @@ static void set_camera_state(struct cam_data *cam) cam->params.exposure.fineExp, cam->params.exposure.coarseExpLo, cam->params.exposure.coarseExpHi, - cam->params.exposure.redComp, - cam->params.exposure.green1Comp, - cam->params.exposure.green2Comp, - cam->params.exposure.blueComp); + cam->params.exposure.redComp, + cam->params.exposure.green1Comp, + cam->params.exposure.green2Comp, + cam->params.exposure.blueComp); do_command_extended(cam, CPIA_COMMAND_SetExposure, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0); - if(cam->params.exposure.gainMode == 0) + if (!cam->params.exposure.gainMode) cam->params.exposure.gainMode = 2; - if(cam->params.exposure.expMode == 0) + if (!cam->params.exposure.expMode) cam->params.exposure.expMode = 2; - if(cam->params.exposure.centreWeight == 0) + if (!cam->params.exposure.centreWeight) cam->params.exposure.centreWeight = 1; cam->cmd_queue = COMMAND_SETCOMPRESSION | @@ -2407,32 +2734,25 @@ static void get_version_information(struct cam_data *cam) { /* GetCPIAVersion */ do_command(cam, CPIA_COMMAND_GetCPIAVersion, 0, 0, 0, 0); - printk(KERN_INFO " CPIA Version: %d.%02d (%d.%d)\n", - cam->params.version.firmwareVersion, - cam->params.version.firmwareRevision, - cam->params.version.vcVersion, - cam->params.version.vcRevision); /* GetPnPID */ do_command(cam, CPIA_COMMAND_GetPnPID, 0, 0, 0, 0); - printk(KERN_INFO " CPIA PnP-ID: %04x:%04x:%04x\n", - cam->params.pnpID.vendor, cam->params.pnpID.product, - cam->params.pnpID.deviceRevision); } /* initialize camera */ static int reset_camera(struct cam_data *cam) { /* Start the camera in low power mode */ - if(goto_low_power(cam)) { - if( cam->params.status.systemState != 0x4 ) { + if (goto_low_power(cam)) { + if (cam->params.status.systemState != WARM_BOOT_STATE) return -ENODEV; - } + /* FIXME: this is just dirty trial and error */ reset_camera_struct(cam); goto_high_power(cam); do_command(cam, CPIA_COMMAND_DiscardFrame, 0, 0, 0, 0); - if(goto_low_power(cam)) return -NODEV; + if (goto_low_power(cam)) + return -NODEV; } /* procedure described in developer's guide p3-28 */ @@ -2440,11 +2760,15 @@ static int reset_camera(struct cam_data *cam) /* Check the firmware version FIXME: should we check PNPID? */ cam->params.version.firmwareVersion = 0; get_version_information(cam); - if(cam->params.version.firmwareVersion != 1) { + if (cam->params.version.firmwareVersion != 1) return -ENODEV; - } + + /* GA 04/14/00 - set QX3 detected flag */ + cam->params.qx3.qx3_detected = (cam->params.pnpID.vendor == 0x0813 && + cam->params.pnpID.product == 0x0001); - /* The fatal error checking should be done after the camera powers up (developer's guide p 3-38) */ + /* The fatal error checking should be done after + * the camera powers up (developer's guide p 3-38) */ /* Set streamState before transition to high power to avoid bug * in firmware 1-02 */ @@ -2452,40 +2776,38 @@ static int reset_camera(struct cam_data *cam) STREAM_NOT_READY, 0); /* GotoHiPower */ - if(goto_high_power(cam)) + if (goto_high_power(cam)) return -ENODEV; - /* Check the camera status */ - if(do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) + if (do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0)) return -EIO; - if(cam->params.status.fatalError) { - DBG("fatal_error: %#04x\n",cam->params.status.fatalError); - DBG("vp_status: %#04x\n",cam->params.status.vpStatus); - if(cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { + + if (cam->params.status.fatalError) { + DBG("fatal_error: %#04x\n", + cam->params.status.fatalError); + DBG("vp_status: %#04x\n", + cam->params.status.vpStatus); + if (cam->params.status.fatalError & ~(COM_FLAG|CPIA_FLAG)) { /* Fatal error in camera */ return -EIO; - } else if(cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)){ + } else if (cam->params.status.fatalError & (COM_FLAG|CPIA_FLAG)) { /* Firmware 1-02 may do this for parallel port cameras, * just clear the flags (developer's guide p 3-38) */ do_command(cam, CPIA_COMMAND_ModifyCameraStatus, FATALERROR, ~(COM_FLAG|CPIA_FLAG), 0, 0); } } - + /* Check the camera status again */ - if(cam->params.status.fatalError) { - if(cam->params.status.fatalError) + if (cam->params.status.fatalError) { + if (cam->params.status.fatalError) return -EIO; } /* VPVersion can't be retrieved before the camera is in HiPower, * so get it here instead of in get_version_information. */ do_command(cam, CPIA_COMMAND_GetVPVersion, 0, 0, 0, 0); - printk(KERN_INFO " VP-Version: %d.%d %04x\n", - cam->params.vpVersion.vpVersion, - cam->params.vpVersion.vpRevision, - cam->params.vpVersion.cameraHeadID); /* set camera to a known state */ set_camera_state(cam); @@ -2499,54 +2821,60 @@ static int cpia_open(struct video_device *dev, int flags) int i; struct cam_data *cam = dev->priv; - DBG("cpia_open\n"); - - if(cam->open_count > 0) { - DBG("Camera[%d] already open\n",cam->index); + if (!cam) { + DBG("Internal error, cam_data not found!\n"); + return -EBUSY; + } + + if (cam->open_count > 0) { + DBG("Camera already open\n"); return -EBUSY; } - if(cam->raw_image == NULL) { - if((cam->raw_image=rvmalloc(CPIA_MAX_IMAGE_SIZE)) == NULL){ + if (!cam->raw_image) { + cam->raw_image = rvmalloc(CPIA_MAX_IMAGE_SIZE); + if (!cam->raw_image) return -ENOMEM; - } } - if(cam->decompressed_frame.data == NULL) { - cam->decompressed_frame.data=rvmalloc(CPIA_MAX_FRAME_SIZE); - if(cam->decompressed_frame.data == NULL){ + + if (!cam->decompressed_frame.data) { + cam->decompressed_frame.data = rvmalloc(CPIA_MAX_FRAME_SIZE); + if (!cam->decompressed_frame.data) { rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return -ENOMEM; } } /* open cpia */ - if (cam->ops->open(cam->index, &cam->lowlevel_data)) { + if (cam->ops->open(cam->lowlevel_data)) { rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; + cam->decompressed_frame.data = NULL; rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return -ENODEV; } /* reset the camera */ - if((i = reset_camera(cam)) != 0) { + if ((i = reset_camera(cam)) != 0) { cam->ops->close(cam->lowlevel_data); rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; + cam->decompressed_frame.data = NULL; rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; + cam->raw_image = NULL; return i; } /* Set ownership of /proc/cpia/videoX to current user */ - if(cam->proc_entry != NULL) + if(cam->proc_entry) cam->proc_entry->uid = current->uid; - + /* set mark for loading first frame uncompressed */ cam->first_frame = 1; - cam->busy_lock = MUTEX; - + + /* init it to something */ + cam->mmap_kludge = 0; + ++cam->open_count; #ifdef MODULE MOD_INC_USE_COUNT; @@ -2554,78 +2882,51 @@ static int cpia_open(struct video_device *dev, int flags) return 0; } -/* FIXME */ static void cpia_close(struct video_device *dev) { struct cam_data *cam; - DBG("cpia_close\n"); cam = dev->priv; - if(cam->ops == NULL) { - if(--cam->open_count == 0) { - int i; - video_unregister_device(dev); - /* Need a lock when adding/removing cameras */ - lock_kernel(); - if(cam->raw_image) { - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=0; - } - if(cam->decompressed_frame.data) { - rvfree(cam->decompressed_frame.data, - CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=0; - } - if(cam->frame_buf) { - free_frame_buf(cam); - } - for(i=0; iproc_entry != NULL) - cam->proc_entry->uid = 0; + if (cam->ops) { + /* Return ownership of /proc/cpia/videoX to root */ + if(cam->proc_entry) + cam->proc_entry->uid = 0; - /* save camera state for later open (developers guide ch 3.5.3) */ - save_camera_state(cam); + /* save camera state for later open (developers guide ch 3.5.3) */ + save_camera_state(cam); - /* GotoLoPower */ - goto_low_power(cam); + /* GotoLoPower */ + goto_low_power(cam); - /* cleanup internal state stuff */ - free_frames(cam->frame); + /* Update the camera ststus */ + do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); - /* Update the camera ststus */ - do_command(cam, CPIA_COMMAND_GetCameraStatus, 0, 0, 0, 0); + /* cleanup internal state stuff */ + free_frames(cam->frame); - /* close cpia */ - cam->ops->close(cam->lowlevel_data); + /* close cpia */ + cam->ops->close(cam->lowlevel_data); + } - if(--cam->open_count == 0) { + if (--cam->open_count == 0) { /* clean up capture-buffers */ - if(cam->raw_image) { + if (cam->raw_image) { rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image = 0; + cam->raw_image = NULL; } - if(cam->decompressed_frame.data) { + + if (cam->decompressed_frame.data) { rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data = 0; + cam->decompressed_frame.data = NULL; } - if(cam->frame_buf) { + + if (cam->frame_buf) free_frame_buf(cam); + + if (!cam->ops) { + video_unregister_device(dev); + kfree(cam); } } @@ -2637,26 +2938,27 @@ static void cpia_close(struct video_device *dev) } static long cpia_read(struct video_device *dev, char *buf, - unsigned long count, int noblock) + unsigned long count, int noblock) { struct cam_data *cam = dev->priv; /* make this _really_ smp and multithredi-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } if (!buf) { DBG("buf NULL\n"); up(&cam->busy_lock); return -EINVAL; } - if(count == 0) { + + if (!count) { DBG("count 0\n"); up(&cam->busy_lock); return 0; } - if(!cam->ops) { + + if (!cam->ops) { DBG("ops NULL\n"); up(&cam->busy_lock); return -ENODEV; @@ -2664,8 +2966,9 @@ static long cpia_read(struct video_device *dev, char *buf, /* upload frame */ cam->decompressed_frame.state = FRAME_READY; + cam->mmap_kludge=0; fetch_frame(cam); - if( cam->decompressed_frame.state != FRAME_DONE ) { + if (cam->decompressed_frame.state != FRAME_DONE) { DBG("upload failed %d/%d\n", cam->decompressed_frame.count, cam->decompressed_frame.state); up(&cam->busy_lock); @@ -2674,12 +2977,13 @@ static long cpia_read(struct video_device *dev, char *buf, cam->decompressed_frame.state = FRAME_UNUSED; /* copy data to user space */ - if(cam->decompressed_frame.count > count) { - DBG("count wrong\n"); + if (cam->decompressed_frame.count > count) { + DBG("count wrong: %d, %lu\n", cam->decompressed_frame.count, + count); up(&cam->busy_lock); return -EFAULT; } - if(copy_to_user(buf, cam->decompressed_frame.data, + if (copy_to_user(buf, cam->decompressed_frame.data, cam->decompressed_frame.count)) { DBG("copy_to_user failed\n"); up(&cam->busy_lock); @@ -2695,31 +2999,34 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) struct cam_data *cam = dev->priv; int retval = 0; + if (!cam || !cam->ops) + return -ENODEV; + /* make this _really_ smp-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } + //DBG("cpia_ioctl: %u\n", ioctlnr); switch (ioctlnr) { - /* query capabilites */ case VIDIOCGCAP: { struct video_capability b; DBG("VIDIOCGCAP\n"); - strcpy(b.name, "CPiA Parport Camera"); + strcpy(b.name, "CPiA Camera"); b.type = VID_TYPE_CAPTURE; b.channels = 1; b.audios = 0; - b.maxwidth = 352; /* VIDEOSIZE_CIF */ + b.maxwidth = 352; /* VIDEOSIZE_CIF */ b.maxheight = 288; - b.minwidth = 48; /* VIDEOSIZE_48_48 */ + b.minwidth = 48; /* VIDEOSIZE_48_48 */ b.minheight = 48; if (copy_to_user(arg, &b, sizeof(b))) retval = -EFAULT; + break; } @@ -2782,13 +3089,15 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) retval = -EFAULT; break; } + /* check validity */ DBG("palette: %d\n", vp.palette); DBG("depth: %d\n", vp.depth); - if ( !valid_mode(vp.palette, vp.depth) ) { + if (!valid_mode(vp.palette, vp.depth)) { retval = -EINVAL; break; } + down(&cam->param_lock); /* brightness, colour, contrast need no check 0-65535 */ memcpy( &cam->vp, &vp, sizeof(vp) ); @@ -2799,12 +3108,13 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) /* contrast is in steps of 8, so round */ cam->params.colourParams.contrast = ((cam->params.colourParams.contrast + 3) / 8) * 8; - if(cam->params.version.firmwareVersion == 1 && - cam->params.version.firmwareRevision == 2 && - cam->params.colourParams.contrast > 80) { + if (cam->params.version.firmwareVersion == 1 && + cam->params.version.firmwareRevision == 2 && + cam->params.colourParams.contrast > 80) { /* 1-02 firmware limits contrast to 80 */ cam->params.colourParams.contrast = 80; } + /* queue command to update camera */ cam->cmd_queue |= COMMAND_SETCOLOURPARAMS; up(&cam->param_lock); @@ -2845,9 +3155,10 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) * is requested by the user??? */ down(&cam->param_lock); - if(vw.width!=cam->vw.width || vw.height!=cam->vw.height) { + if (vw.width != cam->vw.width || vw.height != cam->vw.height) { int video_size = match_videosize(vw.width, vw.height); - if(video_size < 0) { + + if (video_size < 0) { retval = -EINVAL; up(&cam->param_lock); break; @@ -2863,7 +3174,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) /* setformat ignored by camera during streaming, * so stop/dispatch/start */ - if(cam->cmd_queue & COMMAND_SETFORMAT) { + if (cam->cmd_queue & COMMAND_SETFORMAT) { DBG("\n"); dispatch_commands(cam); } @@ -2882,10 +3193,10 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) memset(&vm, 0, sizeof(vm)); vm.size = CPIA_MAX_FRAME_SIZE*FRAME_NUM; vm.frames = FRAME_NUM; - for( i=0; ivp.palette = vm.format; switch(vm.format) { case VIDEO_PALETTE_GREY: + cam->vp.depth = 8; + break; case VIDEO_PALETTE_RGB555: case VIDEO_PALETTE_RGB565: case VIDEO_PALETTE_YUV422: @@ -2930,15 +3243,16 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) retval = -EINVAL; break; } - if(retval != 0) break; + if (retval) + break; /* set video size */ video_size = match_videosize(vm.width, vm.height); - if(cam->video_size<0) { + if (cam->video_size < 0) { retval = -EINVAL; break; } - if( video_size != cam->video_size ) { + if (video_size != cam->video_size) { cam->video_size = video_size; set_vw_size(cam); cam->cmd_queue |= COMMAND_SETFORMAT; @@ -2949,6 +3263,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) cam->vw.width, cam->vw.height); #endif /* according to v4l-spec we must start streaming here */ + cam->mmap_kludge = 1; retval = capture_frame(cam, &vm); break; @@ -2964,7 +3279,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) } //DBG("VIDIOCSYNC: %d\n", frame); - if (frame<0||frame>=FRAME_NUM) { + if (frame<0 || frame >= FRAME_NUM) { retval = -EINVAL; break; } @@ -2982,7 +3297,7 @@ static int cpia_ioctl(struct video_device *dev, unsigned int ioctlnr, void *arg) //DBG("VIDIOCSYNC: %d synced\n", frame); break; } - if(retval == -EINTR) { + if (retval == -EINTR) { /* FIXME - xawtv does not handle this nice */ retval = 0; } @@ -3040,49 +3355,57 @@ static int cpia_mmap(struct video_device *dev, const char *adr, { unsigned long start = (unsigned long)adr; unsigned long page, pos; - struct cam_data *cam; + struct cam_data *cam = dev->priv; int retval; + if (!cam || !cam->ops) + return -ENODEV; + DBG("cpia_mmap: %ld\n", size); - if(size > FRAME_NUM*CPIA_MAX_FRAME_SIZE){ + if (size > FRAME_NUM*CPIA_MAX_FRAME_SIZE) return -EINVAL; - } - - cam = dev->priv; + if (!cam || !cam->ops) + return -ENODEV; + /* make this _really_ smp-safe */ - if( down_interruptible(&cam->busy_lock) ) { + if (down_interruptible(&cam->busy_lock)) return -EINTR; - } - if( cam->frame_buf == NULL ) { /* we do lazy allocation */ - if( (retval = allocate_frame_buf(cam)) ) { + if (!cam->frame_buf) { /* we do lazy allocation */ + if ((retval = allocate_frame_buf(cam))) { up(&cam->busy_lock); return retval; } } + pos = (unsigned long)(cam->frame_buf); while (size > 0) { - page = kvirt_to_phys(pos); + page = kvirt_to_pa(pos); if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) { up(&cam->busy_lock); return -EAGAIN; } - start+=PAGE_SIZE; - pos+=PAGE_SIZE; - if (size > PAGE_SIZE) size-=PAGE_SIZE; - else size=0; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; } DBG("cpia_mmap: %ld\n", size); up(&cam->busy_lock); + return 0; } int cpia_video_init(struct video_device *vdev) { +#ifdef CONFIG_PROC_FS create_proc_cpia_cam(vdev->priv); +#endif return 0; } @@ -3124,7 +3447,7 @@ static void reset_camera_struct(struct cam_data *cam) cam->params.exposure.green1Comp = 214; cam->params.exposure.green2Comp = 214; cam->params.exposure.blueComp = 230; - cam->params.colourBalance.balanceMode = 2; /* auto color balance */ + cam->params.colourBalance.balanceModeIsAuto = 1; cam->params.colourBalance.redGain = 32; cam->params.colourBalance.greenGain = 6; cam->params.colourBalance.blueGain = 92; @@ -3151,16 +3474,15 @@ static void reset_camera_struct(struct cam_data *cam) cam->params.compressionParams.decimationThreshMod = 2; /* End of default values from Software Developer's Guide */ - /* Start with a reasonable transfer rate */ - cam->transfer_rate = 500; + cam->transfer_rate = 0; /* Set Sensor FPS to 15fps. This seems better than 30fps * for indoor lighting. */ cam->params.sensorFps.divisor = 1; cam->params.sensorFps.baserate = 1; - cam->params.yuvThreshold.yThreshold = 15; /* FIXME? */ - cam->params.yuvThreshold.uvThreshold = 15; /* FIXME? */ + cam->params.yuvThreshold.yThreshold = 6; /* From windows driver */ + cam->params.yuvThreshold.uvThreshold = 6; /* From windows driver */ cam->params.format.subSample = SUBSAMPLE_422; cam->params.format.yuvOrder = YUVORDER_YUYV; @@ -3168,8 +3490,14 @@ static void reset_camera_struct(struct cam_data *cam) cam->params.compression.mode = CPIA_COMPRESSION_AUTO; cam->params.compressionTarget.frTargeting = CPIA_COMPRESSION_TARGET_QUALITY; - cam->params.compressionTarget.targetFR = 7; /* FIXME? */ - cam->params.compressionTarget.targetQ = 10; /* FIXME? */ + cam->params.compressionTarget.targetFR = 15; /* From windows driver */ + cam->params.compressionTarget.targetQ = 5; /* From windows driver */ + + cam->params.qx3.qx3_detected = 0; + cam->params.qx3.toplight = 0; + cam->params.qx3.bottomlight = 0; + cam->params.qx3.button = 0; + cam->params.qx3.cradled = 0; cam->video_size = VIDEOSIZE_CIF; @@ -3179,7 +3507,7 @@ static void reset_camera_struct(struct cam_data *cam) cam->vp.contrast = 32768; /* 50% */ cam->vp.whiteness = 0; /* not used -> grayscale only */ cam->vp.depth = 0; /* FIXME: to be set by user? */ - cam->vp.palette = 0; /* FIXME: to be set by user? */ + cam->vp.palette = VIDEO_PALETTE_RGB24; /* FIXME: to be set by user? */ cam->vw.x = 0; cam->vw.y = 0; @@ -3201,11 +3529,13 @@ static void init_camera_struct(struct cam_data *cam, struct cpia_camera_ops *ops ) { int i; + /* Default everything to 0 */ memset(cam, 0, sizeof(struct cam_data)); cam->ops = ops; - cam->param_lock = MUTEX; + init_MUTEX(&cam->param_lock); + init_MUTEX(&cam->busy_lock); reset_camera_struct(cam); @@ -3215,7 +3545,7 @@ static void init_camera_struct(struct cam_data *cam, cam->vdev.priv = cam; cam->curframe = 0; - for( i=0; iframe[i].width = 0; cam->frame[i].height = 0; cam->frame[i].state = FRAME_UNUSED; @@ -3225,95 +3555,84 @@ static void init_camera_struct(struct cam_data *cam, cam->decompressed_frame.height = 0; cam->decompressed_frame.state = FRAME_UNUSED; cam->decompressed_frame.data = NULL; - - return; } -int cpia_register_camera(struct cpia_camera_ops *ops) +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel) { - int i; - struct cam_data *cam; + struct cam_data *camera; + /* Need a lock when adding/removing cameras. This doesn't happen * often and doesn't take very long, so grabbing the kernel lock * should be OK. */ - lock_kernel(); - for(i=0; i < CPIA_MAXCAMS && camera[i] != NULL; ++i); /* no loop body */ - if(i == CPIA_MAXCAMS) { - unlock_kernel(); - return -ENODEV; - } - if((camera[i] = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { + if ((camera = kmalloc(sizeof(struct cam_data), GFP_KERNEL)) == NULL) { unlock_kernel(); - return -ENOMEM; + return NULL; } - init_camera_struct( camera[i], ops ); - camera[i]->index = i; - + init_camera_struct( camera, ops ); + camera->lowlevel_data = lowlevel; + /* register v4l device */ - if (video_register_device(&camera[i]->vdev, VFL_TYPE_GRABBER) == -1) { - kfree(camera[i]); - camera[i] = NULL; + if (video_register_device(&camera->vdev, VFL_TYPE_GRABBER) == -1) { + kfree(camera); unlock_kernel(); printk(KERN_DEBUG "video_register_device failed\n"); - return -ENODEV; + return NULL; } - unlock_kernel(); - - /* FIXME */ -#if 0 - cam = camera[i]; + /* get version information from camera: open/reset/close */ + /* open cpia */ - if (cam->ops->open(cam->index, &cam->lowlevel_data)) { - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; - return -ENODEV; - } + if (camera->ops->open(camera->lowlevel_data)) + return camera; /* reset the camera */ - if((i = reset_camera(cam)) != 0) { - cam->ops->close(cam->lowlevel_data); - rvfree(cam->decompressed_frame.data, CPIA_MAX_FRAME_SIZE); - cam->decompressed_frame.data=NULL; - rvfree(cam->raw_image, CPIA_MAX_IMAGE_SIZE); - cam->raw_image=NULL; - return i; + if (reset_camera(camera) != 0) { + camera->ops->close(camera->lowlevel_data); + return camera; } -#endif - return i; + + /* close cpia */ + camera->ops->close(camera->lowlevel_data); + + printk(KERN_INFO " CPiA Version: %d.%02d (%d.%d)\n", + camera->params.version.firmwareVersion, + camera->params.version.firmwareRevision, + camera->params.version.vcVersion, + camera->params.version.vcRevision); + printk(KERN_INFO " CPiA PnP-ID: %04x:%04x:%04x\n", + camera->params.pnpID.vendor, + camera->params.pnpID.product, + camera->params.pnpID.deviceRevision); + printk(KERN_INFO " VP-Version: %d.%d %04x\n", + camera->params.vpVersion.vpVersion, + camera->params.vpVersion.vpRevision, + camera->params.vpVersion.cameraHeadID); + + return camera; } -void cpia_unregister_camera(int camnr) +void cpia_unregister_camera(struct cam_data *cam) { - struct cam_data *cam; - if(camnr >= CPIA_MAXCAMS || camera[camnr] == NULL) return; - cam = camera[camnr]; - - DBG("unregistering video\n"); - /* FIXME: Is this safe if the device is open? */ - video_unregister_device(&cam->vdev); - - /* Need a lock when adding/removing cameras. This doesn't happen - * often and doesn't take very long, so grabbing the kernel lock - * should be OK. */ - lock_kernel(); + if (!cam->open_count) { + DBG("unregistering video\n"); + video_unregister_device(&cam->vdev); + } else { + LOG("/dev/video%d removed while open, " + "deferring video_unregister_device\n", cam->vdev.minor); + DBG("camera open -- setting ops to NULL\n"); + cam->ops = NULL; + } +#ifdef CONFIG_PROC_FS DBG("destroying /proc/cpia/video%d\n", cam->vdev.minor); destroy_proc_cpia_cam(cam); - - if(cam->open_count == 0) { +#endif + if (!cam->open_count) { DBG("freeing camera\n"); - kfree(camera[camnr]); - camera[camnr] = NULL; - } else { - DBG("camera open -- setting ops to NULL\n"); - cam->ops = NULL; + kfree(cam); } - unlock_kernel(); } /**************************************************************************** @@ -3327,7 +3646,9 @@ int init_module(void) { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS proc_cpia_create(); +#endif #ifdef CONFIG_KMOD #ifdef CONFIG_VIDEO_CPIA_PP_MODULE request_module("cpia_pp"); @@ -3341,14 +3662,9 @@ return 0; void cleanup_module(void) { - int i; - for(i=0; ivdev); - } - } - +#ifdef CONFIG_PROC_FS proc_cpia_destroy(); +#endif } #else @@ -3357,25 +3673,25 @@ int cpia_init(struct video_init *unused) { printk(KERN_INFO "%s v%d.%d.%d\n", ABOUT, CPIA_MAJ_VER, CPIA_MIN_VER, CPIA_PATCH_VER); +#ifdef CONFIG_PROC_FS proc_cpia_create(); +#endif -#ifdef MODULE +#ifdef CONFIG_VIDEO_CPIA_PP + cpia_pp_init(); +#endif #ifdef CONFIG_KMOD #ifdef CONFIG_VIDEO_CPIA_PP_MODULE request_module("cpia_pp"); #endif + #ifdef CONFIG_VIDEO_CPIA_USB_MODULE request_module("cpia_usb"); #endif #endif /* CONFIG_KMOD */ -#else /* !MODULE */ -#ifdef CONFIG_VIDEO_CPIA_PP - cpia_pp_init(); -#endif #ifdef CONFIG_VIDEO_CPIA_USB cpia_usb_init(); #endif -#endif /* !MODULE */ return 0; } diff --git a/include/linux/cpia.h b/drivers/char/cpia.h similarity index 56% rename from include/linux/cpia.h rename to drivers/char/cpia.h index c7fbfc07c4b1..391f81a3deb7 100644 --- a/include/linux/cpia.h +++ b/drivers/char/cpia.h @@ -26,12 +26,12 @@ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#define CPIA_MAJ_VER 0 -#define CPIA_MIN_VER 5 +#define CPIA_MAJ_VER 1 +#define CPIA_MIN_VER 0 #define CPIA_PATCH_VER 0 -#define CPIA_PP_MAJ_VER 0 -#define CPIA_PP_MIN_VER 5 +#define CPIA_PP_MAJ_VER 1 +#define CPIA_PP_MIN_VER 0 #define CPIA_PP_PATCH_VER 0 #define CPIA_MAX_FRAME_SIZE_UNALIGNED (352 * 288 * 4) /* CIF at RGB32 */ @@ -40,13 +40,15 @@ #ifdef __KERNEL__ #include +#include +#include struct cpia_camera_ops { /* open sets privdata to point to structure for this camera. * Returns negative value on error, otherwise 0. */ - int (*open)(int camnr, void **privdata); + int (*open)(void *privdata); /* Registers callback function cb to be called with cbdata * when an image is ready. If cb is NULL, only single image grabs @@ -86,17 +88,200 @@ struct cpia_camera_ops * Returns negative value on error, otherwise 0. */ int (*close)(void *privdata); + + /* If wait_for_stream_ready is non-zero, wait until the streamState + * is STREAM_READY before calling streamRead. + */ + int wait_for_stream_ready; +}; + +struct cpia_frame { + u8 *data; + int count; + int width; + int height; + volatile int state; +}; + +struct cam_params { + struct { + u8 firmwareVersion; + u8 firmwareRevision; + u8 vcVersion; + u8 vcRevision; + } version; + struct { + u16 vendor; + u16 product; + u16 deviceRevision; + } pnpID; + struct { + u8 vpVersion; + u8 vpRevision; + u16 cameraHeadID; + } vpVersion; + struct { + u8 systemState; + u8 grabState; + u8 streamState; + u8 fatalError; + u8 cmdError; + u8 debugFlags; + u8 vpStatus; + u8 errorCode; + } status; + struct { + u8 brightness; + u8 contrast; + u8 saturation; + } colourParams; + struct { + u8 gainMode; + u8 expMode; + u8 compMode; + u8 centreWeight; + u8 gain; + u8 fineExp; + u8 coarseExpLo; + u8 coarseExpHi; + u8 redComp; + u8 green1Comp; + u8 green2Comp; + u8 blueComp; + } exposure; + struct { + u8 balanceModeIsAuto; + u8 redGain; + u8 greenGain; + u8 blueGain; + } colourBalance; + struct { + u8 divisor; + u8 baserate; + } sensorFps; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } apcor; + struct { + u8 flickerMode; + u8 coarseJump; + u8 allowableOverExposure; + } flickerControl; + struct { + u8 gain1; + u8 gain2; + u8 gain4; + u8 gain8; + } vlOffset; + struct { + u8 mode; + u8 decimation; + } compression; + struct { + u8 frTargeting; + u8 targetFR; + u8 targetQ; + } compressionTarget; + struct { + u8 yThreshold; + u8 uvThreshold; + } yuvThreshold; + struct { + u8 hysteresis; + u8 threshMax; + u8 smallStep; + u8 largeStep; + u8 decimationHysteresis; + u8 frDiffStepThresh; + u8 qDiffStepThresh; + u8 decimationThreshMod; + } compressionParams; + struct { + u8 videoSize; /* CIF/QCIF */ + u8 subSample; + u8 yuvOrder; + } format; + + struct { /* GA 04/14/00 - Intel QX3 specific data */ + u8 qx3_detected; /* a QX3 is present */ + u8 toplight; /* top light lit , R/W */ + u8 bottomlight; /* bottom light lit, R/W */ + u8 button; /* snapshot button pressed (R/O) */ + u8 cradled; /* microscope is in cradle (R/O) */ + } qx3; + + struct { + u8 colStart; /* skip first 8*colStart pixels */ + u8 colEnd; /* finish at 8*colEnd pixels */ + u8 rowStart; /* skip first 4*rowStart lines */ + u8 rowEnd; /* finish at 4*rowEnd lines */ + } roi; + u8 ecpTiming; + u8 streamStartLine; +}; + +enum v4l_camstates { + CPIA_V4L_IDLE = 0, + CPIA_V4L_ERROR, + CPIA_V4L_COMMAND, + CPIA_V4L_GRABBING, + CPIA_V4L_STREAMING, + CPIA_V4L_STREAMING_PAUSED, +}; + +#define FRAME_NUM 2 /* double buffering for now */ + +struct cam_data { + struct cam_data **previous; + struct cam_data *next; + + struct semaphore busy_lock; /* guard against SMP multithreading */ + struct cpia_camera_ops *ops; /* lowlevel driver operations */ + void *lowlevel_data; /* private data for lowlevel driver */ + u8 *raw_image; /* buffer for raw image data */ + struct cpia_frame decompressed_frame; + /* buffer to hold decompressed frame */ + int image_size; /* sizeof last decompressed image */ + int open_count; /* # of process that have camera open */ + + /* camera status */ + int fps; /* actual fps reported by the camera */ + int transfer_rate; /* transfer rate from camera in kB/s */ + u8 mainsFreq; /* for flicker control */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct cam_params params; /* camera settings */ + struct proc_dir_entry *proc_entry; /* /proc/cpia/videoX */ + + /* v4l */ + int video_size; /* VIDEO_SIZE_ */ + volatile enum v4l_camstates camstate; /* v4l layer status */ + struct video_device vdev; /* v4l videodev */ + struct video_picture vp; /* v4l camera settings */ + struct video_window vw; /* v4l capture area */ + + /* mmap interface */ + int curframe; /* the current frame to grab into */ + u8 *frame_buf; /* frame buffer data */ + struct cpia_frame frame[FRAME_NUM]; + /* FRAME_NUM-buffering, so we need a array */ + + int first_frame; + int mmap_kludge; /* 'wrong' byte order for mmap */ + volatile u32 cmd_queue; /* queued commands */ }; /* cpia_register_camera is called by low level driver for each camera. * A unique camera number is returned, or a negative value on error */ -int cpia_register_camera(struct cpia_camera_ops *ops); +struct cam_data *cpia_register_camera(struct cpia_camera_ops *ops, void *lowlevel); /* cpia_unregister_camera is called by low level driver when a camera * is removed. This must not fail. */ -void cpia_unregister_camera(int camnr); - -#define CPIA_MAXCAMS 4 +void cpia_unregister_camera(struct cam_data *cam); /* raw CIF + 64 byte header + (2 bytes line_length + EOL) per line + 4*EOI + * one byte 16bit DMA alignment @@ -123,6 +308,7 @@ void cpia_unregister_camera(int camnr); #define VIDEOSIZE_288_216 10 #define VIDEOSIZE_320_240 VIDEOSIZE_SIF #define VIDEOSIZE_352_288 VIDEOSIZE_CIF +#define VIDEOSIZE_88_72 11 /* quarter CIF */ #define SUBSAMPLE_420 0 #define SUBSAMPLE_422 1 #define YUVORDER_YUYV 0 @@ -133,6 +319,7 @@ void cpia_unregister_camera(int camnr); #define DECIMATION_ENAB 1 #define EOI 0xff /* End Of Image */ #define EOL 0xfd /* End Of Line */ +#define FRAME_HEADER_SIZE 64 /* Image grab modes */ #define CPIA_GRAB_SINGLE 0 @@ -207,7 +394,7 @@ void cpia_unregister_camera(int camnr); #define ADBG(lineno,fmt,args...) printk(fmt, jiffies, lineno, ##args) #define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):"__FUNCTION__"(%d):"fmt,##args) #else -#define DBG(fmn,args...) {} +#define DBG(fmn,args...) do {} while(0) #endif #define DEB_BYTE(p)\ @@ -215,6 +402,29 @@ void cpia_unregister_camera(int camnr); (p)&0x80?1:0, (p)&0x40?1:0, (p)&0x20?1:0, (p)&0x10?1:0,\ (p)&0x08?1:0, (p)&0x04?1:0, (p)&0x02?1:0, (p)&0x01?1:0); +#define ADD_TO_LIST(l, drv) \ + {\ + lock_kernel();\ + (drv)->next = l;\ + (drv)->previous = &(l);\ + (l) = drv;\ + unlock_kernel();\ + } while(0) + +#define REMOVE_FROM_LIST(drv) \ + {\ + if ((drv)->previous != NULL) {\ + lock_kernel();\ + if ((drv)->next != NULL)\ + (drv)->next->previous = (drv)->previous;\ + *((drv)->previous) = (drv)->next;\ + (drv)->previous = NULL;\ + (drv)->next = NULL;\ + unlock_kernel();\ + }\ + } while (0) + + #endif /* __KERNEL__ */ #endif /* cpia_h */ diff --git a/drivers/char/cpia_pp.c b/drivers/char/cpia_pp.c index 3e35028a53a5..d40388fb14b8 100644 --- a/drivers/char/cpia_pp.c +++ b/drivers/char/cpia_pp.c @@ -3,10 +3,9 @@ * * Supports CPiA based parallel port Video Camera's. * - * (C) 1999 Bas Huisman - * Scott J. Bertin , - * Peter Pregler - * Johannes Erdfelt + * (C) Copyright 1999 Bas Huisman + * (C) Copyright 1999-2000 Scott J. Bertin , + * (C) Copyright 1999-2000 Peter Pregler * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +23,11 @@ */ #include +#include + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +#undef CONFIG_VIDEO_CPIA_PP_DMA +#endif #include #include @@ -32,6 +36,8 @@ #include #include #include +#include + #ifdef CONFIG_VIDEO_CPIA_PP_DMA #include #endif @@ -41,6 +47,7 @@ #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) /* If this is a module and parport_pc is not, some parport_pc_* functions * are not directly availbale. parport.h messes this up. * This fixes what we need. @@ -67,11 +74,12 @@ #define parport_disable_irq(p) parport_pc_disable_irq(p) #endif /* parport_enable_irq */ #endif /* !PARPORT_NEED_GENERIC_OPS */ +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ -#undef _CPIA_DEBUG_ /* define for verbose debug output */ -#include +/* #define _CPIA_DEBUG_ define for verbose debug output */ +#include "cpia.h" -static int cpia_pp_open(int camnr, void **privdata); +static int cpia_pp_open(void *privdata); static int cpia_pp_registerCallback(void *privdata, void (*cb) (void *cbdata), void *cbdata); static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data); @@ -126,6 +134,8 @@ static int cpia_pp_close(void *privdata); #define ECP_FIFO_SIZE 16 #define DMA_BUFFER_SIZE PAGE_SIZE /* for 16bit DMA make sure DMA_BUFFER_SIZE is 16 bit aligned */ +#define PARPORT_CHUNK_SIZE PAGE_SIZE/* >=2.3.x */ + /* we read this many bytes at once */ #define GetECRMasked(port,mask) (parport_read_econtrol(port) & (mask)) #define GetStatus(port) ((parport_read_status(port)^STATUS_INVERSION_MASK)&(0xf8)) @@ -139,7 +149,6 @@ static int cpia_pp_close(void *privdata); #define ClearControlMasked(port,mask) SetControl(port,GetControl(port)&~(mask)); #define FrobControlBit(port,mask,value) SetControl(port,(GetControl(port)&~(mask))|((value)&(mask))); -#define PP_MAXCAMS (PARPORT_MAX & Peter Pregler "); MODULE_DESCRIPTION("Parallel port driver for Vision CPiA based cameras"); -MODULE_PARM(parport, "1-" __MODULE_STRING(PP_MAXCAMS) "s"); +MODULE_PARM(parport, "1-" __MODULE_STRING(PARPORT_MAX) "s"); MODULE_PARM_DESC(parport, "'auto' or a list of parallel port numbers. Just like lp."); #else -static int parport_nr[PP_MAXCAMS] __initdata = - {[0 ... PP_MAXCAMS - 1] = PPCPIA_PARPORT_UNSPEC}; +static int parport_nr[PARPORT_MAX] __initdata = + {[0 ... PARPORT_MAX - 1] = PPCPIA_PARPORT_UNSPEC}; static int parport_ptr = 0; #endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) enum comstates { CPIA_FORWARD, CPIA_REVERSE }; //fixme enum camstates { CPIA_PHASE_idle = 0, @@ -187,16 +198,22 @@ static char camstatesstr[12][40] = "CPIA_PHASE_secpwrite", }; #endif +#endif + struct pp_cam_entry { struct pardevice *pdev; struct parport *port; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) enum comstates state; enum camstates camstate; +#endif struct tq_struct cb_task; int open_count; - int camnr; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) struct wait_queue *wq_stream; - +#else + wait_queue_head_t wq_stream; +#endif /* image state flags */ int image_ready; /* we got an interrupt */ int image_complete; /* we have seen 4 EOI */ @@ -220,8 +237,6 @@ struct pp_cam_entry { #endif }; -static struct pp_cam_entry *camera[PP_MAXCAMS] = {[0 ... PP_MAXCAMS - 1] = NULL}; - static struct cpia_camera_ops cpia_pp_ops = { cpia_pp_open, @@ -230,9 +245,12 @@ static struct cpia_camera_ops cpia_pp_ops = cpia_pp_streamStart, cpia_pp_streamStop, cpia_pp_streamRead, - cpia_pp_close + cpia_pp_close, + 1 }; +static struct cam_data *cam_list; + #ifdef _CPIA_DEBUG_ #define DEB_PORT(port) { \ u8 controll = GetControl(port); \ @@ -258,17 +276,18 @@ DBG("nsel %c per %c naut %c nstrob %c nak %c busy %c nfaul %c sel %c init %c dir /* FIXME */ static void cpia_parport_enable_irq( struct parport *port ) { - parport_enable_irq(port); - mdelay(10); - return; + parport_enable_irq(port); + mdelay(10); + return; } static void cpia_parport_disable_irq( struct parport *port ) { - parport_disable_irq(port); - mdelay(10); - return; + parport_disable_irq(port); + mdelay(10); + return; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) static int while_out(struct pp_cam_entry *cam) { struct parport *port = cam->port; @@ -709,7 +728,7 @@ int ECPReadBuffer_DMA(struct pp_cam_entry *cam, u8 *buf, int bytes) release_dma_lock(flags); return cam->readbytes; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ /**************************************************************************** * @@ -779,6 +798,7 @@ static int SimECPWriteBuffer(struct pp_cam_entry *cam, const u8 *buf, int size) return written; } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ /**************************************************************************** * @@ -787,10 +807,13 @@ static int SimECPWriteBuffer(struct pp_cam_entry *cam, const u8 *buf, int size) ***************************************************************************/ static void EndTransferMode(struct pp_cam_entry *cam) { - if (!cam) return; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (cam->state == CPIA_REVERSE) Reverse2Forward(cam); Valid1284Termination(cam); if(cam->stream_irq) cpia_parport_enable_irq(cam->port); +#else + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); +#endif } /**************************************************************************** @@ -800,6 +823,7 @@ static void EndTransferMode(struct pp_cam_entry *cam) ***************************************************************************/ static int ForwardSetup(struct pp_cam_entry *cam) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if(cam->stream_irq) cpia_parport_disable_irq(cam->port); if (!Negotiate2SetupPhase(cam, 0)) { if (!ECPSetupPhase(cam)) { @@ -812,6 +836,22 @@ static int ForwardSetup(struct pp_cam_entry *cam) } EndTransferMode(cam); return -1; +#else + int retry; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, IEEE1284_MODE_ECP)) { + break; + } + } + if(retry == 3) { + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + return 0; +#endif } /**************************************************************************** @@ -821,6 +861,7 @@ static int ForwardSetup(struct pp_cam_entry *cam) ***************************************************************************/ static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if(cam->stream_irq) cpia_parport_disable_irq(cam->port); if (!Negotiate2SetupPhase(cam, extensibility)) { if (!ECPSetupPhase(cam)) { @@ -837,6 +878,28 @@ static int ReverseSetup(struct pp_cam_entry *cam, int extensibility) } EndTransferMode(cam); return -1; +#else + int retry; + int mode = IEEE1284_MODE_ECP; + if(extensibility) mode = 8|3|IEEE1284_EXT_LINK; + + /* After some commands the camera needs extra time before + * it will respond again, so we try up to 3 times */ + for(retry=0; retry<3; ++retry) { + if(!parport_negotiate(cam->port, mode)) { + break; + } + } + if(retry == 3) { + if(extensibility) + DBG("Unable to negotiate extensibility mode\n"); + else + DBG("Unable to negotiate ECP mode\n"); + return -1; + } + if(extensibility) cam->port->ieee1284.mode = IEEE1284_MODE_ECP; + return 0; +#endif } #ifdef CONFIG_VIDEO_CPIA_PP_DMA @@ -967,7 +1030,7 @@ static void dma_handler(void *handle) } return; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ /**************************************************************************** * @@ -975,6 +1038,7 @@ static void dma_handler(void *handle) * ***************************************************************************/ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) { struct pp_cam_entry *cam = handle; @@ -992,7 +1056,7 @@ static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) } return; } -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ if (cam->camstate == CPIA_PHASE_ecpread) return; if( cam->camstate != CPIA_PHASE_idle ) DBG("got IRQ(%d) when in %s\n", @@ -1012,6 +1076,7 @@ static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) } return; } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ /**************************************************************************** * @@ -1021,15 +1086,27 @@ static void cpia_pp_irq_handler(int irq, void *handle, struct pt_regs *b) static int WritePacket(struct pp_cam_entry *cam, const u8 *packet, size_t size) { int retval=0; +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) + int size_written; +#endif if (packet == NULL) { return -EINVAL; } if (ForwardSetup(cam)) { + DBG("Write failed in setup\n"); return -EIO; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (SimECPWriteBuffer(cam, packet, size) != size) { retval = -EIO; } +#else + size_written = parport_write(cam->port, packet, size); + if(size_written != size) { + DBG("Write failed, wrote %d/%d\n", size_written, size); + retval = -EIO; + } +#endif EndTransferMode(cam); return retval; } @@ -1048,9 +1125,15 @@ static int ReadPacket(struct pp_cam_entry *cam, u8 *packet, size_t size) if (ReverseSetup(cam, 0)) { return -EIO; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (SimECPReadBuffer(cam, packet, size) != size) { retval = -EIO; } +#else + if(parport_read(cam->port, packet, size) != size) { + retval = -EIO; + } +#endif EndTransferMode(cam); return retval; } @@ -1068,7 +1151,6 @@ static int cpia_pp_streamStart(void *privdata) cam->image_ready=0; //if (ReverseSetup(cam,1)) return -EIO; if(cam->stream_irq) cpia_parport_enable_irq(cam->port); - return 0; } @@ -1089,6 +1171,20 @@ static int cpia_pp_streamStop(void *privdata) return 0; } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +static int cpia_pp_read(struct parport *port, u8 *buffer, int len) +{ + int bytes_read, new_bytes; + for(bytes_read=0; bytes_read= KERNEL_VERSION(2,3,0)) + int i, endseen, block_size, new_bytes; +#endif + if(cam == NULL) { DBG("Internal driver error: cam is NULL\n"); return -EINVAL; @@ -1120,8 +1220,12 @@ static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock) DBG("%d\n", cam->image_ready); } } else { - if (ReverseSetup(cam, 1)) return -EIO; + if (ReverseSetup(cam, 1)) { + DBG("unable to ReverseSetup\n"); + return -EIO; + } } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) read_bytes = ECPReadBuffer(cam, buffer, CPIA_MAX_IMAGE_SIZE); if( 1/*!cam->streaming*/) { EndTransferMode(cam); @@ -1131,6 +1235,36 @@ static int cpia_pp_streamRead(void *privdata, u8 *buffer, int noblock) DBG("incomplete image: %ld / %d / %d\n", jiffies, cam->image_complete, read_bytes); } +#else /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ + endseen = 0; + block_size = PARPORT_CHUNK_SIZE; + while( !cam->image_complete ) { + if(current->need_resched) schedule(); + + new_bytes = cpia_pp_read(cam->port, buffer, block_size ); + if( new_bytes <= 0 ) { + break; + } + i=-1; + while(++iimage_complete=1; + break; + } + if( CPIA_MAX_IMAGE_SIZE-read_bytes <= PARPORT_CHUNK_SIZE ) { + block_size=CPIA_MAX_IMAGE_SIZE-read_bytes; + } + } + EndTransferMode(cam); +#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) */ return cam->image_complete ? read_bytes : -EIO; } @@ -1145,6 +1279,7 @@ static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) int retval=0; int databytes; struct pp_cam_entry *cam = privdata; + if(cam == NULL) { DBG("Internal driver error: cam is NULL\n"); return -EINVAL; @@ -1155,6 +1290,7 @@ static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) } databytes = (((int)command[7])<<8) | command[6]; if ((err = WritePacket(cam, command, PACKET_LENGTH)) < 0) { + DBG("Error writing command\n"); return err; } if(command[0] == DATA_IN) { @@ -1163,9 +1299,9 @@ static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) DBG("Internal driver error: data is NULL\n"); return -EINVAL; } - err = ReadPacket(cam, buffer, 8); - if(err < 0) { + if((err = ReadPacket(cam, buffer, 8)) < 0) { return err; + DBG("Error reading command result\n"); } memcpy(data, buffer, databytes); } else if(command[0] == DATA_OUT) { @@ -1174,7 +1310,10 @@ static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) DBG("Internal driver error: data is NULL\n"); retval = -EINVAL; } else { - WritePacket(cam, data, databytes); + if((err=WritePacket(cam, data, databytes)) < 0){ + DBG("Error writing command data\n"); + return err; + } } } } else { @@ -1189,29 +1328,19 @@ static int cpia_pp_transferCmd(void *privdata, u8 *command, u8 *data) * cpia_pp_open * ***************************************************************************/ -static int cpia_pp_open(int camnr, void **privdata) +static int cpia_pp_open(void *privdata) { - struct pp_cam_entry *cam=NULL; - int i; - for(i=0; icamnr == camnr) { - DBG("Found camera[%d]\n", camnr); - break; - } - } + struct pp_cam_entry *cam = (struct pp_cam_entry *)privdata; - *privdata = cam; + if (cam == NULL) + return -EINVAL; if(cam->open_count == 0) { if (parport_claim(cam->pdev)) { DBG("failed to claim the port\n"); return -EBUSY; } +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) parport_write_econtrol(cam->port, PARPORT_MODE_PCECR); parport_frob_econtrol(cam->port, ECR_serviceIntr|ECR_dmaEn|ECR_nErrIntrEn, @@ -1222,6 +1351,15 @@ static int cpia_pp_open(int camnr, void **privdata) #endif cam->stream_irq=0; cpia_parport_disable_irq(cam->port); +#else + parport_negotiate(cam->port, IEEE1284_MODE_COMPAT); + parport_data_forward(cam->port); + parport_write_control(cam->port, PARPORT_CONTROL_SELECT); + udelay(50); + parport_write_control(cam->port, + PARPORT_CONTROL_SELECT + | PARPORT_CONTROL_INIT); +#endif } ++cam->open_count; @@ -1266,6 +1404,7 @@ static int cpia_pp_close(void *privdata) MOD_DEC_USE_COUNT; #endif if (--cam->open_count == 0) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (cam->port->irq != PARPORT_IRQ_NONE) { cpia_parport_disable_irq(cam->port); } @@ -1278,11 +1417,12 @@ static int cpia_pp_close(void *privdata) interruptible_sleep_on(&cam->wq_dma); } restore_flags(flags); -#endif +#endif /* CONFIG_VIDEO_CPIA_PP_DMA */ if (waitqueue_active(&cam->wq_stream)) { /* FIXME */ wake_up(&cam->wq_stream); } +#endif /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) */ parport_release(cam->pdev); } return 0; @@ -1293,47 +1433,55 @@ static int cpia_pp_close(void *privdata) * cpia_pp_register * ***************************************************************************/ -static int cpia_pp_register(int nr, struct parport *port) +static int cpia_pp_register(struct parport *port) { struct pardevice *pdev = NULL; - int camnr; + struct pp_cam_entry *cam; + struct cam_data *cpia; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) if (!(port->modes & PARPORT_MODE_PCECP)) { +#else + if (!(port->modes & PARPORT_MODE_ECP) && + !(port->modes & PARPORT_MODE_TRISTATE)) { +#endif LOG("port is not ECP capable\n"); return -ENXIO; } - if((camnr = cpia_register_camera(&cpia_pp_ops)) < 0) { - LOG("failed to cpia_register_camera\n"); - return -ENXIO; - } - camera[nr] = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); - if (camera[nr] == NULL) { + cam = kmalloc(sizeof(struct pp_cam_entry), GFP_KERNEL); + if (cam == NULL) { LOG("failed to allocate camera structure\n"); - cpia_unregister_camera(camnr); return -ENOMEM; } - memset(camera[nr],0,sizeof(struct pp_cam_entry)); + memset(cam,0,sizeof(struct pp_cam_entry)); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) pdev = parport_register_device(port, "cpia_pp", NULL, NULL, - cpia_pp_irq_handler, 0, camera[nr]); + cpia_pp_irq_handler, 0, cam); +#else + pdev = parport_register_device(port, "cpia_pp", NULL, NULL, + NULL, 0, cam); +#endif if (!pdev) { LOG("failed to parport_register_device\n"); - cpia_unregister_camera(camnr); - kfree(camera[nr]); + kfree(cam); return -ENXIO; } - camera[nr]->pdev = pdev; - camera[nr]->port = port; - camera[nr]->state = CPIA_FORWARD; - camera[nr]->camstate = CPIA_PHASE_idle; - camera[nr]->camnr = camnr; - init_waitqueue(&camera[nr]->wq_stream); + cam->pdev = pdev; + cam->port = port; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + cam->state = CPIA_FORWARD; + cam->camstate = CPIA_PHASE_idle; + init_waitqueue(&cam->wq_stream); +#else + init_waitqueue_head(&cam->wq_stream); +#endif - camera[nr]->streaming = 0; - camera[nr]->stream_irq = 0; + cam->streaming = 0; + cam->stream_irq = 0; #ifdef CONFIG_VIDEO_CPIA_PP_DMA if (pdev->port->irq != PARPORT_IRQ_NONE && @@ -1341,66 +1489,151 @@ static int cpia_pp_register(int nr, struct parport *port) if(request_dma(pdev->port->dma, "cpia_pp")) { LOG("failed to register dma %d\n", pdev->port->dma); parport_unregister_device(pdev); - cpia_unregister_camera(camnr); - kfree(camera[nr]); + kfree(cam); return -ENXIO; } - camera[nr]->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA); - if(camera[nr]->dma_buf == NULL) { - free_dma(camera[nr]->pdev->port->dma); - LOG("failed to allocate dma buffer, using FIFO mode\n"); + cam->dma_buf=kmalloc(DMA_BUFFER_SIZE, GFP_DMA); + if(cam->dma_buf == NULL) { + free_dma(cam->pdev->port->dma); + LOG("failed to allocate dma buffer, using PIO mode\n"); } else { - init_waitqueue(&camera[nr]->wq_dma); + init_waitqueue(&cam->wq_dma); printk(KERN_INFO " using DMA mode (irq %d, DMA %d)\n", pdev->port->irq, pdev->port->dma); } - memset(camera[nr]->dma_buf, 0, DMA_BUFFER_SIZE); + memset(cam->dma_buf, 0, DMA_BUFFER_SIZE); } else { printk(KERN_INFO " using PIO mode\n"); } #endif + if((cpia = cpia_register_camera(&cpia_pp_ops, cam)) == NULL) { + LOG("failed to cpia_register_camera\n"); +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (cam->dma_buf) { + free_dma(cam->pdev->port->dma); + kfree(cam->dma_buf); + } +#endif + parport_unregister_device(pdev); + kfree(cam); + return -ENXIO; + } + ADD_TO_LIST(cam_list, cpia); + return 0; } +static void cpia_pp_detach (struct parport *port) +{ + struct cam_data *cpia; + + for(cpia = cam_list; cpia != NULL; cpia = cpia->next) { + struct pp_cam_entry *cam = cpia->lowlevel_data; + if (cam && cam->port->number == port->number) { + REMOVE_FROM_LIST(cpia); + +#ifdef CONFIG_VIDEO_CPIA_PP_DMA + if (cam->dma_buf) { + free_dma(cam->pdev->port->dma); + kfree(cam->dma_buf); + } +#endif + + cpia_unregister_camera(cpia); + + if(cam->open_count > 0) { + cpia_pp_close(cam); + } + + parport_unregister_device(cam->pdev); + + kfree(cam); + cpia->lowlevel_data = NULL; + break; + } + } +} + +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +static void cpia_pp_attach (struct parport *port) +{ + unsigned int i; + + switch (parport_nr[0]) + { + case PPCPIA_PARPORT_UNSPEC: + case PPCPIA_PARPORT_AUTO: + if (port->probe_info[0].class != PARPORT_CLASS_MEDIA || + port->probe_info[0].cmdset == NULL || + strncmp(port->probe_info[0].cmdset, "CPIA_1", 6) != 0) + return; + + cpia_pp_register(port); + + break; + + default: + for (i = 0; i < PARPORT_MAX; ++i) { + if (port->number == parport_nr[i]) { + cpia_pp_register(port); + break; + } + } + break; + } +} + +static struct parport_driver cpia_pp_driver = { + "cpia_pp", + cpia_pp_attach, + cpia_pp_detach, + NULL +}; +#endif + int cpia_pp_init(void) { +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) struct parport *port; int i, count = 0; +#endif printk(KERN_INFO "%s v%d.%d.%d\n",ABOUT, CPIA_PP_MAJ_VER,CPIA_PP_MIN_VER,CPIA_PP_PATCH_VER); - switch (parport_nr[0]) { - case PPCPIA_PARPORT_OFF: + if(parport_nr[0] == PPCPIA_PARPORT_OFF) { printk(" disabled\n"); return 0; - + } + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)) + switch (parport_nr[0]) { case PPCPIA_PARPORT_UNSPEC: case PPCPIA_PARPORT_AUTO: for (port = parport_enumerate(); port; port = port->next) { #if defined(CONFIG_PNP_PARPORT) || \ (defined(MODULE) && defined(CONFIG_PNP_PARPORT_MODULE)) - if (port->probe_info.class != PARPORT_CLASS_MEDIA || - port->probe_info.cmdset == NULL || - strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){ - continue; + if(port->probe_info.model) { + if (port->probe_info.class != PARPORT_CLASS_MEDIA || + port->probe_info.cmdset == NULL || + strncmp(port->probe_info.cmdset, "CPIA_1", 6) != 0){ + continue; + } } #endif - - if (cpia_pp_register(count, port) == 0 && - ++count == PP_MAXCAMS) { - break; + if (!cpia_pp_register(port)) { + ++count; } } break; default: - for (i = 0; i < PP_MAXCAMS; i++) { + for (i = 0; i < PARPORT_MAX; i++) { for (port = parport_enumerate(); port; port = port->next) { if (port->number == parport_nr[i]) { - if (!cpia_pp_register(i, port)) { + if (!cpia_pp_register(port)) { count++; } break; @@ -1414,6 +1647,13 @@ int cpia_pp_init(void) if (count == 0) { return -ENODEV; } +#else /* kernel version >= 2.3.0 */ + if (parport_register_driver (&cpia_pp_driver)) { + LOG ("unable to register with parport\n"); + return -EIO; + } +#endif /* kernel version >= 2.3.0 */ + return 0; } @@ -1426,7 +1666,7 @@ int init_module(void) parport_nr[0] = PPCPIA_PARPORT_AUTO; } else { int n; - for (n = 0; n < PP_MAXCAMS && parport[n]; n++) { + for (n = 0; n < PARPORT_MAX && parport[n]; n++) { if (!strncmp(parport[n], "none", 4)) { parport_nr[n] = PPCPIA_PARPORT_NONE; } else { @@ -1443,31 +1683,23 @@ int init_module(void) } } #if defined(CONFIG_KMOD) && defined(CONFIG_PNP_PARPORT_MODULE) - request_module("parport_probe"); + if(parport_enumerate() && !parport_enumerate()->probe_info.model) { + request_module("parport_probe"); + } #endif return cpia_pp_init(); } void cleanup_module(void) { - int i; - for (i = 0; camera[i] != NULL && i < PP_MAXCAMS; i++) { - while(camera[i]->open_count > 0) { - LOG("You forgot to close camera %d, will do it for you\n", camera[i]->camnr); - cpia_pp_close(camera[i]); - } - -#ifdef CONFIG_VIDEO_CPIA_PP_DMA - if (camera[i]->dma_buf) { - free_dma(camera[i]->pdev->port->dma); - kfree(camera[i]->dma_buf); - } -#endif - - cpia_unregister_camera(camera[i]->camnr); - parport_unregister_device(camera[i]->pdev); - kfree(camera[i]); +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) + parport_unregister_driver (&cpia_pp_driver); +#else + while(cam_list != NULL) { + struct pp_cam_entry *cam = cam_list->lowlevel_data; + cpia_pp_detach(cam->port); } +#endif return; } @@ -1482,7 +1714,7 @@ __initfunc(void cpia_pp_setup(char *str, int *ints)) } } else if (!strncmp(str, "parport", 7)) { int n = simple_strtoul(str + 7, NULL, 10); - if (parport_ptr < PP_MAXCAMS) { + if (parport_ptr < PARPORT_MAX) { parport_nr[parport_ptr++] = n; } else { LOG("too many ports, %s ignored.\n", str); @@ -1493,5 +1725,8 @@ __initfunc(void cpia_pp_setup(char *str, int *ints)) parport_nr[parport_ptr++] = PPCPIA_PARPORT_NONE; } } +#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,3,0)) +__setup("cpia_pp=", cpia_pp_setup); +#endif #endif /* !MODULE */ diff --git a/drivers/char/cpia_usb.c b/drivers/char/cpia_usb.c new file mode 100644 index 000000000000..76dc353d7937 --- /dev/null +++ b/drivers/char/cpia_usb.c @@ -0,0 +1,636 @@ +/* + * cpia_usb CPiA USB driver + * + * Supports CPiA based parallel port Video Camera's. + * + * Copyright (C) 1999 Jochen Scharrlach + * Copyright (C) 1999, 2000 Johannes Erdfelt + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpia.h" + +#define USB_REQ_CPIA_GRAB_FRAME 0xC1 +#define USB_REQ_CPIA_UPLOAD_FRAME 0xC2 +#define WAIT_FOR_NEXT_FRAME 0 +#define FORCE_FRAME_UPLOAD 1 + +#define FRAMES_PER_DESC 10 +#define FRAME_SIZE_PER_DESC 960 /* Shouldn't be hardcoded */ +#define CPIA_NUMSBUF 2 +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) +#define SCRATCH_BUF_SIZE (STREAM_BUF_SIZE * 2) + +struct cpia_sbuf { + char *data; + urb_t *urb; +}; + +#define FRAMEBUF_LEN (CPIA_MAX_FRAME_SIZE+100) +enum framebuf_status { + FRAME_EMPTY, + FRAME_READING, + FRAME_READY, + FRAME_ERROR, +}; + +struct framebuf { + int length; + enum framebuf_status status; + u8 data[FRAMEBUF_LEN]; + struct framebuf *next; +}; + +struct usb_cpia { + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + wait_queue_head_t wq_stream; + + int cursbuf; /* Current receiving sbuf */ + struct cpia_sbuf sbuf[CPIA_NUMSBUF]; /* Double buffering */ + + int streaming; + int open; + int present; + struct framebuf *buffers[3]; + struct framebuf *curbuff, *workbuff; +}; + +static int cpia_usb_open(void *privdata); +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata); +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data); +static int cpia_usb_streamStart(void *privdata); +static int cpia_usb_streamStop(void *privdata); +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock); +static int cpia_usb_close(void *privdata); + +#define ABOUT "USB driver for Vision CPiA based cameras" + +static struct cpia_camera_ops cpia_usb_ops = { + cpia_usb_open, + cpia_usb_registerCallback, + cpia_usb_transferCmd, + cpia_usb_streamStart, + cpia_usb_streamStop, + cpia_usb_streamRead, + cpia_usb_close, + 0 +}; + +static struct cam_data *cam_list; + +static void cpia_usb_complete(struct urb *urb) +{ + int i; + char *cdata; + struct usb_cpia *ucpia; + + if (!urb || !urb->context) + return; + + ucpia = (struct usb_cpia *) urb->context; + + if (!ucpia->dev || !ucpia->streaming || !ucpia->present || !ucpia->open) + return; + + if (ucpia->workbuff->status == FRAME_EMPTY) { + ucpia->workbuff->status = FRAME_READING; + ucpia->workbuff->length = 0; + } + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (st) + printk(KERN_DEBUG "cpia data error: [%d] len=%d, status=%X\n", i, n, st); + + if (FRAMEBUF_LEN < ucpia->workbuff->length + n) { + printk(KERN_DEBUG "cpia: scratch buf overflow!scr_len: %d, n: %d\n", ucpia->workbuff->length, n); + return; + } + + if (n) { + if ((ucpia->workbuff->length > 0) || + (0x19 == cdata[0] && 0x68 == cdata[1])) { + memcpy(ucpia->workbuff->data + ucpia->workbuff->length, cdata, n); + ucpia->workbuff->length += n; + } else + DBG("Ignoring packet!\n"); + } else { + if (ucpia->workbuff->length > 4 && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-1] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-2] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-3] && + 0xff == ucpia->workbuff->data[ucpia->workbuff->length-4]) { + ucpia->workbuff->status = FRAME_READY; + ucpia->curbuff = ucpia->workbuff; + ucpia->workbuff = ucpia->workbuff->next; + ucpia->workbuff->status = FRAME_EMPTY; + ucpia->workbuff->length = 0; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + } + } + } +} + +static int cpia_usb_open(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + urb_t *urb; + int ret, retval = 0, fx, err; + + if (!ucpia) + return -EINVAL; + + ucpia->sbuf[0].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[0].data) + return -EINVAL; + + ucpia->sbuf[1].data = kmalloc(FRAMES_PER_DESC * FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ucpia->sbuf[1].data) { + retval = -EINVAL; + goto error_0; + } + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 3); + if (ret < 0) { + printk(KERN_ERR "cpia_usb_open: usb_set_interface error (ret = %d)\n", ret); + retval = -EBUSY; + goto error_all; + } + + ucpia->buffers[0]->status = FRAME_EMPTY; + ucpia->buffers[0]->length = 0; + ucpia->buffers[1]->status = FRAME_EMPTY; + ucpia->buffers[1]->length = 0; + ucpia->buffers[2]->status = FRAME_EMPTY; + ucpia->buffers[2]->length = 0; + ucpia->curbuff = ucpia->buffers[0]; + ucpia->workbuff = ucpia->buffers[1]; + + /* We double buffer the Iso lists */ + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[0].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[0].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + printk(KERN_ERR "cpia_init_isoc: usb_alloc_urb 0\n"); + retval = -ENOMEM; + goto error_all; + } + + ucpia->sbuf[1].urb = urb; + urb->dev = ucpia->dev; + urb->context = ucpia; + urb->pipe = usb_rcvisocpipe(ucpia->dev, 1); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ucpia->sbuf[1].data; + urb->complete = cpia_usb_complete; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = FRAME_SIZE_PER_DESC * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = FRAME_SIZE_PER_DESC * fx; + urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC; + } + + ucpia->sbuf[1].urb->next = ucpia->sbuf[0].urb; + ucpia->sbuf[0].urb->next = ucpia->sbuf[1].urb; + + err = usb_submit_urb(ucpia->sbuf[0].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 0 ret %d\n", + err); + err = usb_submit_urb(ucpia->sbuf[1].urb); + if (err) + printk(KERN_ERR "cpia_init_isoc: usb_submit_urb 1 ret %d\n", + err); + + ucpia->streaming = 1; + ucpia->open = 1; + + return 0; + +error_all: + kfree (ucpia->sbuf[1].data); +error_0: + kfree (ucpia->sbuf[0].data); + + return retval; +} + +// +// convenience functions +// + +/**************************************************************************** + * + * WritePacket + * + ***************************************************************************/ +static int WritePacket(struct usb_device *udev, const u8 *packet, u8 *buf, size_t size) +{ + if (!packet) + return -EINVAL; + + return usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +/**************************************************************************** + * + * ReadPacket + * + ***************************************************************************/ +static int ReadPacket(struct usb_device *udev, u8 *packet, u8 *buf, size_t size) +{ + if (!packet || size <= 0) + return -EINVAL; + + return usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + packet[1] + (packet[0] << 8), + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + packet[2] + (packet[3] << 8), + packet[4] + (packet[5] << 8), buf, size, HZ); +} + +static int cpia_usb_transferCmd(void *privdata, u8 *command, u8 *data) +{ + int err = 0; + int databytes; + struct usb_cpia *ucpia = (struct usb_cpia *)privdata; + struct usb_device *udev = ucpia->dev; + + if (!udev) { + DBG("Internal driver error: udev is NULL\n"); + return -EINVAL; + } + + if (!command) { + DBG("Internal driver error: command is NULL\n"); + return -EINVAL; + } + + databytes = (((int)command[7])<<8) | command[6]; + + if (command[0] == DATA_IN) { + u8 buffer[8]; + + if (!data) { + DBG("Internal driver error: data is NULL\n"); + return -EINVAL; + } + + err = ReadPacket(udev, command, buffer, 8); + if (err < 0) + return err; + + memcpy(data, buffer, databytes); + } else if(command[0] == DATA_OUT) + WritePacket(udev, command, data, databytes); + else { + DBG("Unexpected first byte of command: %x\n", command[0]); + err = -EINVAL; + } + + return 0; +} + +static int cpia_usb_registerCallback(void *privdata, void (*cb) (void *cbdata), + void *cbdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStart(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamStop(void *privdata) +{ + return -ENODEV; +} + +static int cpia_usb_streamRead(void *privdata, u8 *frame, int noblock) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + struct framebuf *mybuff; + + if (!ucpia || !ucpia->present) + return -1; + + if (ucpia->curbuff->status != FRAME_READY) + interruptible_sleep_on(&ucpia->wq_stream); + else + DBG("Frame already waiting!\n"); + + mybuff = ucpia->curbuff; + + if (!mybuff) + return -1; + + if (mybuff->status != FRAME_READY || mybuff->length < 4) { + DBG("Something went wrong!\n"); + return -1; + } + + memcpy(frame, mybuff->data, mybuff->length); + mybuff->status = FRAME_EMPTY; + +/* DBG("read done, %d bytes, Header: %x/%x, Footer: %x%x%x%x\n", */ +/* mybuff->length, frame[0], frame[1], */ +/* frame[mybuff->length-4], frame[mybuff->length-3], */ +/* frame[mybuff->length-2], frame[mybuff->length-1]); */ + + return mybuff->length; +} + +static void cpia_usb_free_resources(struct usb_cpia *ucpia, int try) +{ + if (!ucpia->streaming) + return; + + ucpia->streaming = 0; + + /* Set packet size to 0 */ + if (try) { + int ret; + + ret = usb_set_interface(ucpia->dev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "usb_set_interface error (ret = %d)\n", ret); + return; + } + } + + /* Unschedule all of the iso td's */ + if (ucpia->sbuf[1].urb) { + usb_unlink_urb(ucpia->sbuf[1].urb); + usb_free_urb(ucpia->sbuf[1].urb); + ucpia->sbuf[1].urb = NULL; + } + + if (ucpia->sbuf[0].urb) { + usb_unlink_urb(ucpia->sbuf[0].urb); + usb_free_urb(ucpia->sbuf[0].urb); + ucpia->sbuf[0].urb = NULL; + } +} + +static int cpia_usb_close(void *privdata) +{ + struct usb_cpia *ucpia = (struct usb_cpia *) privdata; + + ucpia->open = 0; + + cpia_usb_free_resources(ucpia, 1); + + if (!ucpia->present) + kfree(ucpia); + + return 0; +} + +int cpia_usb_init(void) +{ + /* return -ENODEV; */ + return 0; +} + +/* Probing and initializing */ + +static void *cpia_probe(struct usb_device *udev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_cpia *ucpia; + struct cam_data *cam; + int ret; + + /* A multi-config CPiA camera? */ + if (udev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &udev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it a CPiA? */ + if (!((udev->descriptor.idVendor == 0x0553 && + udev->descriptor.idProduct == 0x0002) || + (udev->descriptor.idVendor == 0x0813 && + udev->descriptor.idProduct == 0x0001))) /* GA 04/14/00 */ + return NULL; + + /* We found a CPiA */ + printk(KERN_INFO "USB CPiA camera found\n"); + + ucpia = kmalloc(sizeof(*ucpia), GFP_KERNEL); + if (!ucpia) { + printk(KERN_ERR "couldn't kmalloc cpia struct\n"); + return NULL; + } + + memset(ucpia, 0, sizeof(*ucpia)); + + ucpia->dev = udev; + ucpia->iface = interface->bInterfaceNumber; + init_waitqueue_head(&ucpia->wq_stream); + + ucpia->buffers[0] = vmalloc(sizeof(*ucpia->buffers[0])); + if (!ucpia->buffers[0]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 0\n"); + goto fail_alloc_0; + } + + ucpia->buffers[1] = vmalloc(sizeof(*ucpia->buffers[1])); + if (!ucpia->buffers[1]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 1\n"); + goto fail_alloc_1; + } + + ucpia->buffers[2] = vmalloc(sizeof(*ucpia->buffers[2])); + if (!ucpia->buffers[2]) { + printk(KERN_ERR "couldn't vmalloc frame buffer 2\n"); + goto fail_alloc_2; + } + + ucpia->buffers[0]->next = ucpia->buffers[1]; + ucpia->buffers[1]->next = ucpia->buffers[2]; + ucpia->buffers[2]->next = ucpia->buffers[0]; + + ret = usb_set_interface(udev, ucpia->iface, 0); + if (ret < 0) { + printk(KERN_ERR "cpia_probe: usb_set_interface error (ret = %d)\n", ret); + /* goto fail_all; */ + } + + /* Before register_camera, important */ + ucpia->present = 1; + + cam = cpia_register_camera(&cpia_usb_ops, ucpia); + if (!cam) { + LOG("failed to cpia_register_camera\n"); + goto fail_all; + } + + ADD_TO_LIST(cam_list, cam); + + return cam; + +fail_all: + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; +fail_alloc_2: + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; +fail_alloc_1: + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; +fail_alloc_0: + + return NULL; +} + +static void cpia_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver cpia_driver = { + "cpia", + cpia_probe, + cpia_disconnect, + { NULL, NULL } +}; + +/* don't use dev, it may be NULL! (see usb_cpia_cleanup) */ +/* _disconnect from usb_cpia_cleanup is not necessary since usb_deregister */ +/* will do it for us as well as passing a udev structure - jerdfelt */ +static void cpia_disconnect(struct usb_device *udev, void *ptr) +{ + struct cam_data *cam = (struct cam_data *) ptr; + struct usb_cpia *ucpia = (struct usb_cpia *) cam->lowlevel_data; + + REMOVE_FROM_LIST(cam); + + /* Don't even try to reset the altsetting if we're disconnected */ + cpia_usb_free_resources(ucpia, 0); + + ucpia->present = 0; + + cpia_unregister_camera(cam); + + ucpia->curbuff->status = FRAME_ERROR; + + if (waitqueue_active(&ucpia->wq_stream)) + wake_up_interruptible(&ucpia->wq_stream); + + usb_driver_release_interface(&cpia_driver, + &udev->actconfig->interface[0]); + + ucpia->curbuff = ucpia->workbuff = NULL; + + if (ucpia->buffers[2]) { + vfree(ucpia->buffers[2]); + ucpia->buffers[2] = NULL; + } + + if (ucpia->buffers[1]) { + vfree(ucpia->buffers[1]); + ucpia->buffers[1] = NULL; + } + + if (ucpia->buffers[0]) { + vfree(ucpia->buffers[0]); + ucpia->buffers[0] = NULL; + } + + if (!ucpia->open) + kfree(ucpia); +} + +int usb_cpia_init(void) +{ + cam_list = NULL; + + return usb_register(&cpia_driver); +} + +void usb_cpia_cleanup(void) +{ +/* + struct cam_data *cam; + + while ((cam = cam_list) != NULL) + cpia_disconnect(NULL, cam); +*/ + + usb_deregister(&cpia_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_cpia_init(); +} + +void cleanup_module(void) +{ + usb_cpia_cleanup(); +} + +#else /* !MODULE */ + +__initfunc(void cpia_usb_setup(char *str, int *ints)) +{ +} + +#endif /* !MODULE */ diff --git a/drivers/char/drm/Config.in b/drivers/char/drm/Config.in new file mode 100644 index 000000000000..d24d6597004d --- /dev/null +++ b/drivers/char/drm/Config.in @@ -0,0 +1,15 @@ +# +# drm device configuration +# +# This driver provides support for the +# Direct Rendering Infrastructure (DRI) in XFree86 4.x. +# + +tristate 'Direct Rendering Manager (XFree86 DRI support)' CONFIG_DRM +if [ "$CONFIG_DRM" != "n" ]; then + tristate ' 3dfx Banshee/Voodoo3+' CONFIG_DRM_TDFX + tristate ' 3dlabs GMX 2000' CONFIG_DRM_GAMMA + tristate ' ATI Rage 128' CONFIG_DRM_R128 + dep_tristate ' Intel I810' CONFIG_DRM_I810 $CONFIG_AGP + dep_tristate ' Matrox g200/g400' CONFIG_DRM_MGA $CONFIG_AGP +fi diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile new file mode 100644 index 000000000000..b1b8d9768ee1 --- /dev/null +++ b/drivers/char/drm/Makefile @@ -0,0 +1,88 @@ +# +# Makefile for the drm device driver. This driver provides support for +# the Direct Rendering Infrastructure (DRI) in XFree86 4.x. +# + +# drm.o is a fake target -- it is never built +# The real targets are in the module-list +O_TARGET := drm.o +module-list := gamma.o tdfx.o r128.o ffb.o mga.o i810.o +export-objs := $(patsubst %.o,%_drv.o,$(module-list)) + +# libs-objs are included in every module so that radical changes to the +# architecture of the DRM support library can be made at a later time. +# +# The downside is that each module is larger, and a system that uses +# more than one module (i.e., a dual-head system) will use more memory +# (but a system that uses exactly one module will use the same amount of +# memory). +# +# The upside is that if the DRM support library ever becomes insufficient +# for new families of cards, a new library can be implemented for those new +# cards without impacting the drivers for the old cards. This is significant, +# because testing architectural changes to old cards may be impossible, and +# may delay the implementation of a better architecture. We've traded slight +# memory waste (in the dual-head case) for greatly improved long-term +# maintainability. +# +lib-objs := init.o memory.o proc.o auth.o context.o drawable.o bufs.o +lib-objs += lists.o lock.o ioctl.o fops.o vm.o dma.o ctxbitmap.o + +ifeq ($(CONFIG_AGP),y) + lib-objs += agpsupport.o +else + ifeq ($(CONFIG_AGP),m) + lib-objs += agpsupport.o + endif +endif + +gamma-objs := $(lib-objs) gamma_drv.o gamma_dma.o +tdfx-objs := $(lib-objs) tdfx_drv.o tdfx_context.o +r128-objs := $(lib-objs) r128_drv.o r128_dma.o r128_context.o r128_bufs.o +ffb-objs := $(lib-objs) ffb_drv.o ffb_context.o +mga-objs := $(lib-objs) mga_drv.o mga_dma.o mga_context.o mga_bufs.o \ + mga_state.o +i810-objs := $(lib-objs) i810_drv.o i810_dma.o i810_context.o i810_bufs.o + +obj-$(CONFIG_DRM_GAMMA) += gamma.o $(gamma-objs) +obj-$(CONFIG_DRM_TDFX) += tdfx.o $(tdfx-objs) +obj-$(CONFIG_DRM_R128) += r128.o $(r128-objs) +obj-$(CONFIG_DRM_FFB) += ffb.o $(ffb-objs) + +ifneq ($CONFIG_AGP),) +obj-$(CONFIG_DRM_MGA) += mga.o $(mga-objs) +obj-$(CONFIG_DRM_I810) += i810.o $(i810-objs) +endif + +# Take module names out of obj-y and int-m + +obj-y := $(filter-out $(module-list), $(obj-y)) +int-m := $(filter-out $(module-list), $(obj-m)) + +# Translate to Rules.make lists. + +O_OBJS := $(filter-out $(export-objs), $(obj-y)) +OX_OBJS := $(filter $(export-objs), $(obj-y)) +M_OBJS := $(sort $(filter $(module-list), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +include $(TOPDIR)/Rules.make + +gamma.o: $(gamma-objs) + $(LD) -r -o $@ $(gamma-objs) + +tdfx.o: $(tdfx-objs) + $(LD) -r -o $@ $(tdfx-objs) + +mga.o: $(mga-objs) + $(LD) -r -o $@ $(mga-objs) + +i810.o: $(i810-objs) + $(LD) -r -o $@ $(i810-objs) + +r128.o: $(r128-objs) + $(LD) -r -o $@ $(r128-objs) + +ffb.o: $(ffb-objs) + $(LD) -r -o $@ $(ffb-objs) diff --git a/drivers/char/drm/README.drm b/drivers/char/drm/README.drm new file mode 100644 index 000000000000..1cc4c277033c --- /dev/null +++ b/drivers/char/drm/README.drm @@ -0,0 +1,41 @@ + +The Direct Rendering Manager (drm) is a device-independent kernel-level +device driver that provides support for the XFree86 Direct Rendering +Infrastructure (DRI). + +The DRM supports the Direct Rendering Infrastructure (DRI) in four major +ways: + + 1. The DRM provides synchronized access to the graphics hardware via + the use of an optimized two-tiered lock. + + 2. The DRM enforces the DRI security policy for access to the graphics + hardware by only allowing authenticated X11 clients access to + restricted regions of memory. + + 3. The DRM provides a generic DMA engine, complete with multiple + queues and the ability to detect the need for an OpenGL context + switch. + + 4. The DRM is extensible via the use of small device-specific modules + that rely extensively on the API exported by the DRM module. + + +Documentation on the DRI is available from: + http://precisioninsight.com/piinsights.html + +For specific information about kernel-level support, see: + + The Direct Rendering Manager, Kernel Support for the Direct Rendering + Infrastructure + http://precisioninsight.com/dr/drm.html + + Hardware Locking for the Direct Rendering Infrastructure + http://precisioninsight.com/dr/locking.html + + A Security Analysis of the Direct Rendering Infrastructure + http://precisioninsight.com/dr/security.html + + + +$XFree86: xc/programs/Xserver/hw/xfree86/os-support/linux/drm/kernel/README.drm,v 1.2 1999/09/27 14:59:24 dawes Exp $ diff --git a/drivers/char/drm/agpsupport.c b/drivers/char/drm/agpsupport.c new file mode 100644 index 000000000000..e1e1ccedddf0 --- /dev/null +++ b/drivers/char/drm/agpsupport.c @@ -0,0 +1,329 @@ +/* agpsupport.c -- DRM support for AGP/GART backend -*- linux-c -*- + * Created: Mon Dec 13 09:56:45 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include + +drm_agp_func_t drm_agp = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; + +/* The C standard says that 'void *' is not guaranteed to hold a function + pointer, so we use this union to define a generic pointer that is + guaranteed to hold any of the function pointers we care about. */ +typedef union { + void (*free_memory)(agp_memory *); + agp_memory *(*allocate_memory)(size_t, u32); + int (*bind_memory)(agp_memory *, off_t); + int (*unbind_memory)(agp_memory *); + void (*enable)(u32); + int (*acquire)(void); + void (*release)(void); + void (*copy_info)(agp_kern_info *); + unsigned long address; +} drm_agp_func_u; + +typedef struct drm_agp_fill { + const char *name; + drm_agp_func_u *f; +} drm_agp_fill_t; + +static drm_agp_fill_t drm_agp_fill[] = { + { __MODULE_STRING(agp_free_memory), + (drm_agp_func_u *)&drm_agp.free_memory }, + { __MODULE_STRING(agp_allocate_memory), + (drm_agp_func_u *)&drm_agp.allocate_memory }, + { __MODULE_STRING(agp_bind_memory), + (drm_agp_func_u *)&drm_agp.bind_memory }, + { __MODULE_STRING(agp_unbind_memory), + (drm_agp_func_u *)&drm_agp.unbind_memory }, + { __MODULE_STRING(agp_enable), + (drm_agp_func_u *)&drm_agp.enable }, + { __MODULE_STRING(agp_backend_acquire), + (drm_agp_func_u *)&drm_agp.acquire }, + { __MODULE_STRING(agp_backend_release), + (drm_agp_func_u *)&drm_agp.release }, + { __MODULE_STRING(agp_copy_info), + (drm_agp_func_u *)&drm_agp.copy_info }, + { NULL, NULL } +}; + +int drm_agp_info(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + agp_kern_info *kern; + drm_agp_info_t info; + + if (!dev->agp->acquired || !drm_agp.copy_info) return -EINVAL; + + kern = &dev->agp->agp_info; + info.agp_version_major = kern->version.major; + info.agp_version_minor = kern->version.minor; + info.mode = kern->mode; + info.aperture_base = kern->aper_base; + info.aperture_size = kern->aper_size * 1024 * 1024; + info.memory_allowed = kern->max_memory << PAGE_SHIFT; + info.memory_used = kern->current_memory << PAGE_SHIFT; + info.id_vendor = kern->device->vendor; + info.id_device = kern->device->device; + + copy_to_user_ret((drm_agp_info_t *)arg, &info, sizeof(info), -EFAULT); + return 0; +} + +int drm_agp_acquire(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + if (dev->agp->acquired || !drm_agp.acquire) return -EINVAL; + if ((retcode = (*drm_agp.acquire)())) return retcode; + dev->agp->acquired = 1; + return 0; +} + +int drm_agp_release(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!dev->agp->acquired || !drm_agp.release) return -EINVAL; + (*drm_agp.release)(); + dev->agp->acquired = 0; + return 0; + +} + +int drm_agp_enable(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_mode_t mode; + + if (!dev->agp->acquired || !drm_agp.enable) return -EINVAL; + + copy_from_user_ret(&mode, (drm_agp_mode_t *)arg, sizeof(mode), + -EFAULT); + + dev->agp->mode = mode.mode; + (*drm_agp.enable)(mode.mode); + dev->agp->base = dev->agp->agp_info.aper_base; + dev->agp->enabled = 1; + return 0; +} + +int drm_agp_alloc(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + agp_memory *memory; + unsigned long pages; + u32 type; + if (!dev->agp->acquired) return -EINVAL; + copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request), + -EFAULT); + if (!(entry = drm_alloc(sizeof(*entry), DRM_MEM_AGPLISTS))) + return -ENOMEM; + + memset(entry, 0, sizeof(*entry)); + + pages = (request.size + PAGE_SIZE - 1) / PAGE_SIZE; + type = (u32) request.type; + + if (!(memory = drm_alloc_agp(pages, type))) { + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -ENOMEM; + } + + entry->handle = (unsigned long)memory->memory; + entry->memory = memory; + entry->bound = 0; + entry->pages = pages; + entry->prev = NULL; + entry->next = dev->agp->memory; + if (dev->agp->memory) dev->agp->memory->prev = entry; + dev->agp->memory = entry; + + request.handle = entry->handle; + request.physical = memory->physical; + + if (copy_to_user((drm_agp_buffer_t *)arg, &request, sizeof(request))) { + dev->agp->memory = entry->next; + dev->agp->memory->prev = NULL; + drm_free_agp(memory, pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return -EFAULT; + } + return 0; +} + +static drm_agp_mem_t *drm_agp_lookup_entry(drm_device_t *dev, + unsigned long handle) +{ + drm_agp_mem_t *entry; + + for (entry = dev->agp->memory; entry; entry = entry->next) { + if (entry->handle == handle) return entry; + } + return NULL; +} + +int drm_agp_unbind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request), + -EFAULT); + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (!entry->bound) return -EINVAL; + return drm_unbind_agp(entry->memory); +} + +int drm_agp_bind(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_binding_t request; + drm_agp_mem_t *entry; + int retcode; + int page; + + if (!dev->agp->acquired || !drm_agp.bind_memory) return -EINVAL; + copy_from_user_ret(&request, (drm_agp_binding_t *)arg, sizeof(request), + -EFAULT); + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) return -EINVAL; + page = (request.offset + PAGE_SIZE - 1) / PAGE_SIZE; + if ((retcode = drm_bind_agp(entry->memory, page))) return retcode; + entry->bound = dev->agp->base + (page << PAGE_SHIFT); + DRM_DEBUG("base = 0x%lx entry->bound = 0x%lx\n", + dev->agp->base, entry->bound); + return 0; +} + +int drm_agp_free(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_agp_buffer_t request; + drm_agp_mem_t *entry; + + if (!dev->agp->acquired) return -EINVAL; + copy_from_user_ret(&request, (drm_agp_buffer_t *)arg, sizeof(request), + -EFAULT); + if (!(entry = drm_agp_lookup_entry(dev, request.handle))) + return -EINVAL; + if (entry->bound) drm_unbind_agp(entry->memory); + + if (entry->prev) entry->prev->next = entry->next; + else dev->agp->memory = entry->next; + if (entry->next) entry->next->prev = entry->prev; + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + return 0; +} + +drm_agp_head_t *drm_agp_init(void) +{ + drm_agp_fill_t *fill; + drm_agp_head_t *head = NULL; + int agp_available = 1; + + for (fill = &drm_agp_fill[0]; fill->name; fill++) { + char *n = (char *)fill->name; + *fill->f = (drm_agp_func_u)get_module_symbol(NULL, n); + DRM_DEBUG("%s resolves to 0x%08lx\n", n, (*fill->f).address); + if (!(*fill->f).address) agp_available = 0; + } + + DRM_DEBUG("agp_available = %d\n", agp_available); + + if (agp_available) { + if (!(head = drm_alloc(sizeof(*head), DRM_MEM_AGPLISTS))) + return NULL; + memset((void *)head, 0, sizeof(*head)); + (*drm_agp.copy_info)(&head->agp_info); + head->memory = NULL; + switch (head->agp_info.chipset) { + case INTEL_GENERIC: head->chipset = "Intel"; break; + case INTEL_LX: head->chipset = "Intel 440LX"; break; + case INTEL_BX: head->chipset = "Intel 440BX"; break; + case INTEL_GX: head->chipset = "Intel 440GX"; break; + case INTEL_I810: head->chipset = "Intel i810"; break; + case VIA_GENERIC: head->chipset = "VIA"; break; + case VIA_VP3: head->chipset = "VIA VP3"; break; + case VIA_MVP3: head->chipset = "VIA MVP3"; break; + case VIA_APOLLO_PRO: head->chipset = "VIA Apollo Pro"; break; + case VIA_APOLLO_SUPER: head->chipset = "VIA Apollo Super"; break; + case SIS_GENERIC: head->chipset = "SiS"; break; + case AMD_GENERIC: head->chipset = "AMD"; break; + case AMD_IRONGATE: head->chipset = "AMD Irongate"; break; + case ALI_GENERIC: head->chipset = "ALi"; break; + case ALI_M1541: head->chipset = "ALi M1541"; break; + default: + } + DRM_INFO("AGP %d.%d on %s @ 0x%08lx %dMB\n", + head->agp_info.version.major, + head->agp_info.version.minor, + head->chipset, + head->agp_info.aper_base, + head->agp_info.aper_size); + } + return head; +} + +void drm_agp_uninit(void) +{ + drm_agp_fill_t *fill; + + for (fill = &drm_agp_fill[0]; fill->name; fill++) { +#if LINUX_VERSION_CODE >= 0x020400 + if ((*fill->f).address) put_module_symbol((*fill->f).address); +#endif + (*fill->f).address = 0; + } +} diff --git a/drivers/char/drm/auth.c b/drivers/char/drm/auth.c new file mode 100644 index 000000000000..9f81c5391dde --- /dev/null +++ b/drivers/char/drm/auth.c @@ -0,0 +1,160 @@ +/* auth.c -- IOCTLs for authentication -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_hash_magic(drm_magic_t magic) +{ + return magic & (DRM_HASH_SIZE-1); +} + +static drm_file_t *drm_find_file(drm_device_t *dev, drm_magic_t magic) +{ + drm_file_t *retval = NULL; + drm_magic_entry_t *pt; + int hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; pt = pt->next) { + if (pt->magic == magic) { + retval = pt->priv; + break; + } + } + up(&dev->struct_sem); + return retval; +} + +int drm_add_magic(drm_device_t *dev, drm_file_t *priv, drm_magic_t magic) +{ + int hash; + drm_magic_entry_t *entry; + + DRM_DEBUG("%d\n", magic); + + hash = drm_hash_magic(magic); + entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC); + if (!entry) return -ENOMEM; + entry->magic = magic; + entry->priv = priv; + entry->next = NULL; + + down(&dev->struct_sem); + if (dev->magiclist[hash].tail) { + dev->magiclist[hash].tail->next = entry; + dev->magiclist[hash].tail = entry; + } else { + dev->magiclist[hash].head = entry; + dev->magiclist[hash].tail = entry; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_remove_magic(drm_device_t *dev, drm_magic_t magic) +{ + drm_magic_entry_t *prev = NULL; + drm_magic_entry_t *pt; + int hash; + + DRM_DEBUG("%d\n", magic); + hash = drm_hash_magic(magic); + + down(&dev->struct_sem); + for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) { + if (pt->magic == magic) { + if (dev->magiclist[hash].head == pt) { + dev->magiclist[hash].head = pt->next; + } + if (dev->magiclist[hash].tail == pt) { + dev->magiclist[hash].tail = prev; + } + if (prev) { + prev->next = pt->next; + } + up(&dev->struct_sem); + return 0; + } + } + up(&dev->struct_sem); + + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + + return -EINVAL; +} + +int drm_getmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + static drm_magic_t sequence = 0; + static spinlock_t lock = SPIN_LOCK_UNLOCKED; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + + /* Find unique magic */ + if (priv->magic) { + auth.magic = priv->magic; + } else { + spin_lock(&lock); + do { + if (!sequence) ++sequence; /* reserve 0 */ + auth.magic = sequence++; + } while (drm_find_file(dev, auth.magic)); + spin_unlock(&lock); + priv->magic = auth.magic; + drm_add_magic(dev, priv, auth.magic); + } + + DRM_DEBUG("%u\n", auth.magic); + copy_to_user_ret((drm_auth_t *)arg, &auth, sizeof(auth), -EFAULT); + return 0; +} + +int drm_authmagic(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_auth_t auth; + drm_file_t *file; + + copy_from_user_ret(&auth, (drm_auth_t *)arg, sizeof(auth), -EFAULT); + DRM_DEBUG("%u\n", auth.magic); + if ((file = drm_find_file(dev, auth.magic))) { + file->authenticated = 1; + drm_remove_magic(dev, auth.magic); + return 0; + } + return -EINVAL; +} diff --git a/drivers/char/drm/bufs.c b/drivers/char/drm/bufs.c new file mode 100644 index 000000000000..c00f051a8409 --- /dev/null +++ b/drivers/char/drm/bufs.c @@ -0,0 +1,538 @@ +/* bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "linux/un.h" + + /* Compute order. Can be made faster. */ +int drm_order(unsigned long size) +{ + int order; + unsigned long tmp; + + for (order = 0, tmp = size; tmp >>= 1; ++order); + if (size & ~(1 << order)) ++order; + return order; +} + +int drm_addmap(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map; + + if (!(filp->f_mode & 3)) return -EACCES; /* Require read/write */ + + map = drm_alloc(sizeof(*map), DRM_MEM_MAPS); + if (!map) return -ENOMEM; + if (copy_from_user(map, (drm_map_t *)arg, sizeof(*map))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EFAULT; + } + + DRM_DEBUG("offset = 0x%08lx, size = 0x%08lx, type = %d\n", + map->offset, map->size, map->type); + if ((map->offset & (~PAGE_MASK)) || (map->size & (~PAGE_MASK))) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + map->mtrr = -1; + map->handle = 0; + + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifndef __sparc__ + if (map->offset + map->size < map->offset + || map->offset < virt_to_phys(high_memory)) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } +#endif +#ifdef CONFIG_MTRR + if (map->type == _DRM_FRAME_BUFFER + || (map->flags & _DRM_WRITE_COMBINING)) { + map->mtrr = mtrr_add(map->offset, map->size, + MTRR_TYPE_WRCOMB, 1); + } +#endif + map->handle = drm_ioremap(map->offset, map->size); + break; + + + case _DRM_SHM: + map->handle = (void *)drm_alloc_pages(drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + DRM_DEBUG("%ld %d %p\n", map->size, drm_order(map->size), + map->handle); + if (!map->handle) { + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -ENOMEM; + } + map->offset = (unsigned long)map->handle; + if (map->flags & _DRM_CONTAINS_LOCK) { + dev->lock.hw_lock = map->handle; /* Pointer to lock */ + } + break; +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + case _DRM_AGP: + map->offset = map->offset + dev->agp->base; + break; +#endif + default: + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + return -EINVAL; + } + + down(&dev->struct_sem); + if (dev->maplist) { + ++dev->map_count; + dev->maplist = drm_realloc(dev->maplist, + (dev->map_count-1) + * sizeof(*dev->maplist), + dev->map_count + * sizeof(*dev->maplist), + DRM_MEM_MAPS); + } else { + dev->map_count = 1; + dev->maplist = drm_alloc(dev->map_count*sizeof(*dev->maplist), + DRM_MEM_MAPS); + } + dev->maplist[dev->map_count-1] = map; + up(&dev->struct_sem); + + copy_to_user_ret((drm_map_t *)arg, map, sizeof(*map), -EFAULT); + if (map->type != _DRM_SHM) { + copy_to_user_ret(&((drm_map_t *)arg)->handle, + &map->offset, + sizeof(map->offset), + -EFAULT); + } + return 0; +} + +int drm_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int drm_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_info_t *)arg, + sizeof(request), + -EFAULT); + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + copy_to_user_ret(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count), + -EFAULT); + copy_to_user_ret(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size), + -EFAULT); + copy_to_user_ret(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark), + -EFAULT); + copy_to_user_ret(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark), + -EFAULT); + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + copy_to_user_ret((drm_buf_info_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return 0; +} + +int drm_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int drm_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_free_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + copy_from_user_ret(&idx, + &request.list[i], + sizeof(idx), + -EFAULT); + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int drm_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_map_t *)arg, + sizeof(request), + -EFAULT); + + if (request.count >= dma->buf_count) { + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->mm->mmap_sem); + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } +done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + copy_to_user_ret((drm_buf_map_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return retcode; +} diff --git a/drivers/char/drm/context.c b/drivers/char/drm/context.c new file mode 100644 index 000000000000..ca491094eb36 --- /dev/null +++ b/drivers/char/drm/context.c @@ -0,0 +1,308 @@ +/* context.c -- IOCTLs for contexts and DMA queues -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static int drm_init_queue(drm_device_t *dev, drm_queue_t *q, drm_ctx_t *ctx) +{ + DRM_DEBUG("\n"); + + if (atomic_read(&q->use_count) != 1 + || atomic_read(&q->finalization) + || atomic_read(&q->block_count)) { + DRM_ERROR("New queue is already in use: u%d f%d b%d\n", + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count)); + } + + atomic_set(&q->finalization, 0); + atomic_set(&q->block_count, 0); + atomic_set(&q->block_read, 0); + atomic_set(&q->block_write, 0); + atomic_set(&q->total_queued, 0); + atomic_set(&q->total_flushed, 0); + atomic_set(&q->total_locks, 0); + + init_waitqueue_head(&q->write_queue); + init_waitqueue_head(&q->read_queue); + init_waitqueue_head(&q->flush_queue); + + q->flags = ctx->flags; + + drm_waitlist_create(&q->waitlist, dev->dma->buf_count); + + return 0; +} + + +/* drm_alloc_queue: +PRE: 1) dev->queuelist[0..dev->queue_count] is allocated and will not + disappear (so all deallocation must be done after IOCTLs are off) + 2) dev->queue_count < dev->queue_slots + 3) dev->queuelist[i].use_count == 0 and + dev->queuelist[i].finalization == 0 if i not in use +POST: 1) dev->queuelist[i].use_count == 1 + 2) dev->queue_count < dev->queue_slots */ + +static int drm_alloc_queue(drm_device_t *dev) +{ + int i; + drm_queue_t *queue; + int oldslots; + int newslots; + /* Check for a free queue */ + for (i = 0; i < dev->queue_count; i++) { + atomic_inc(&dev->queuelist[i]->use_count); + if (atomic_read(&dev->queuelist[i]->use_count) == 1 + && !atomic_read(&dev->queuelist[i]->finalization)) { + DRM_DEBUG("%d (free)\n", i); + return i; + } + atomic_dec(&dev->queuelist[i]->use_count); + } + /* Allocate a new queue */ + down(&dev->struct_sem); + + queue = drm_alloc(sizeof(*queue), DRM_MEM_QUEUES); + memset(queue, 0, sizeof(*queue)); + atomic_set(&queue->use_count, 1); + + ++dev->queue_count; + if (dev->queue_count >= dev->queue_slots) { + oldslots = dev->queue_slots * sizeof(*dev->queuelist); + if (!dev->queue_slots) dev->queue_slots = 1; + dev->queue_slots *= 2; + newslots = dev->queue_slots * sizeof(*dev->queuelist); + + dev->queuelist = drm_realloc(dev->queuelist, + oldslots, + newslots, + DRM_MEM_QUEUES); + if (!dev->queuelist) { + up(&dev->struct_sem); + DRM_DEBUG("out of memory\n"); + return -ENOMEM; + } + } + dev->queuelist[dev->queue_count-1] = queue; + + up(&dev->struct_sem); + DRM_DEBUG("%d (new)\n", dev->queue_count - 1); + return dev->queue_count - 1; +} + +int drm_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int drm_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = drm_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Init kernel's context and get a new one. */ + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + ctx.handle = drm_alloc_queue(dev); + } + drm_init_queue(dev, dev->queuelist[ctx.handle], &ctx); + DRM_DEBUG("%d\n", ctx.handle); + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int drm_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle < 0 || ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + if (DRM_BUFCOUNT(&q->waitlist)) { + atomic_dec(&q->use_count); + return -EBUSY; + } + + q->flags = ctx.flags; + + atomic_dec(&q->use_count); + return 0; +} + +int drm_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + ctx.flags = q->flags; + atomic_dec(&q->use_count); + + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + + return 0; +} + +int drm_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return drm_context_switch(dev, dev->last_context, ctx.handle); +} + +int drm_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + drm_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int drm_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + drm_queue_t *q; + drm_buf_t *buf; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + if (ctx.handle >= dev->queue_count) return -EINVAL; + q = dev->queuelist[ctx.handle]; + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + /* No longer in use */ + atomic_dec(&q->use_count); + return -EINVAL; + } + + atomic_inc(&q->finalization); /* Mark queue in finalization state */ + atomic_sub(2, &q->use_count); /* Mark queue as unused (pending + finalization) */ + + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + /* Remove queued buffers */ + while ((buf = drm_waitlist_get(&q->waitlist))) { + drm_free_buffer(dev, buf); + } + clear_bit(0, &dev->interrupt_flag); + + /* Wakeup blocked processes */ + wake_up_interruptible(&q->read_queue); + wake_up_interruptible(&q->write_queue); + wake_up_interruptible(&q->flush_queue); + + /* Finalization over. Queue is made + available when both use_count and + finalization become 0, which won't + happen until all the waiting processes + stop waiting. */ + atomic_dec(&q->finalization); + return 0; +} diff --git a/drivers/char/drm/ctxbitmap.c b/drivers/char/drm/ctxbitmap.c new file mode 100644 index 000000000000..615505979a6b --- /dev/null +++ b/drivers/char/drm/ctxbitmap.c @@ -0,0 +1,85 @@ +/* ctxbitmap.c -- Context bitmap management -*- linux-c -*- + * Created: Thu Jan 6 03:56:42 2000 by jhartmann@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle) +{ + if (ctx_handle < 0) goto failed; + + if (ctx_handle < DRM_MAX_CTXBITMAP) { + clear_bit(ctx_handle, dev->ctx_bitmap); + return; + } +failed: + DRM_ERROR("Attempt to free invalid context handle: %d\n", + ctx_handle); + return; +} + +int drm_ctxbitmap_next(drm_device_t *dev) +{ + int bit; + + bit = find_first_zero_bit(dev->ctx_bitmap, DRM_MAX_CTXBITMAP); + if (bit < DRM_MAX_CTXBITMAP) { + set_bit(bit, dev->ctx_bitmap); + DRM_DEBUG("drm_ctxbitmap_next bit : %d\n", bit); + return bit; + } + return -1; +} + +int drm_ctxbitmap_init(drm_device_t *dev) +{ + int i; + int temp; + + dev->ctx_bitmap = (unsigned long *) drm_alloc(PAGE_SIZE, + DRM_MEM_CTXBITMAP); + if(dev->ctx_bitmap == NULL) { + return -ENOMEM; + } + memset((void *) dev->ctx_bitmap, 0, PAGE_SIZE); + for(i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("drm_ctxbitmap_init : %d\n", temp); + } + + return 0; +} + +void drm_ctxbitmap_cleanup(drm_device_t *dev) +{ + drm_free((void *)dev->ctx_bitmap, PAGE_SIZE, + DRM_MEM_CTXBITMAP); +} + diff --git a/drivers/char/drm/dma.c b/drivers/char/drm/dma.c new file mode 100644 index 000000000000..ac2d1bc5aac7 --- /dev/null +++ b/drivers/char/drm/dma.c @@ -0,0 +1,540 @@ +/* dma.c -- DMA IOCTL and function support -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +#include /* For task queue support */ + +void drm_dma_setup(drm_device_t *dev) +{ + int i; + + dev->dma = drm_alloc(sizeof(*dev->dma), DRM_MEM_DRIVER); + memset(dev->dma, 0, sizeof(*dev->dma)); + for (i = 0; i <= DRM_MAX_ORDER; i++) + memset(&dev->dma->bufs[i], 0, sizeof(dev->dma->bufs[0])); +} + +void drm_dma_takedown(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i, j; + + if (!dma) return; + + /* Clear dma buffers */ + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].seg_count) { + DRM_DEBUG("order %d: buf_count = %d," + " seg_count = %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].seg_count); + for (j = 0; j < dma->bufs[i].seg_count; j++) { + drm_free_pages(dma->bufs[i].seglist[j], + dma->bufs[i].page_order, + DRM_MEM_DMA); + } + drm_free(dma->bufs[i].seglist, + dma->bufs[i].seg_count + * sizeof(*dma->bufs[0].seglist), + DRM_MEM_SEGS); + } + if(dma->bufs[i].buf_count) { + for(j = 0; j < dma->bufs[i].buf_count; j++) { + if(dma->bufs[i].buflist[j].dev_private) { + drm_free(dma->bufs[i].buflist[j].dev_private, + dma->bufs[i].buflist[j].dev_priv_size, + DRM_MEM_BUFS); + } + } + drm_free(dma->bufs[i].buflist, + dma->bufs[i].buf_count * + sizeof(*dma->bufs[0].buflist), + DRM_MEM_BUFS); + drm_freelist_destroy(&dma->bufs[i].freelist); + } + } + + if (dma->buflist) { + drm_free(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + DRM_MEM_BUFS); + } + + if (dma->pagelist) { + drm_free(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + } + drm_free(dev->dma, sizeof(*dev->dma), DRM_MEM_DRIVER); + dev->dma = NULL; +} + +#if DRM_DMA_HISTOGRAM +/* This is slow, but is useful for debugging. */ +int drm_histogram_slot(unsigned long count) +{ + int value = DRM_DMA_HISTOGRAM_INITIAL; + int slot; + + for (slot = 0; + slot < DRM_DMA_HISTOGRAM_SLOTS; + ++slot, value = DRM_DMA_HISTOGRAM_NEXT(value)) { + if (count < value) return slot; + } + return DRM_DMA_HISTOGRAM_SLOTS - 1; +} + +void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf) +{ + cycles_t queued_to_dispatched; + cycles_t dispatched_to_completed; + cycles_t completed_to_freed; + int q2d, d2c, c2f, q2c, q2f; + + if (buf->time_queued) { + queued_to_dispatched = (buf->time_dispatched + - buf->time_queued); + dispatched_to_completed = (buf->time_completed + - buf->time_dispatched); + completed_to_freed = (buf->time_freed + - buf->time_completed); + + q2d = drm_histogram_slot(queued_to_dispatched); + d2c = drm_histogram_slot(dispatched_to_completed); + c2f = drm_histogram_slot(completed_to_freed); + + q2c = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed); + q2f = drm_histogram_slot(queued_to_dispatched + + dispatched_to_completed + + completed_to_freed); + + atomic_inc(&dev->histo.total); + atomic_inc(&dev->histo.queued_to_dispatched[q2d]); + atomic_inc(&dev->histo.dispatched_to_completed[d2c]); + atomic_inc(&dev->histo.completed_to_freed[c2f]); + + atomic_inc(&dev->histo.queued_to_completed[q2c]); + atomic_inc(&dev->histo.queued_to_freed[q2f]); + + } + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +} +#endif + +void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf) +{ + drm_device_dma_t *dma = dev->dma; + + if (!buf) return; + + buf->waiting = 0; + buf->pending = 0; + buf->pid = 0; + buf->used = 0; +#if DRM_DMA_HISTOGRAM + buf->time_completed = get_cycles(); +#endif + if (waitqueue_active(&buf->dma_wait)) { + wake_up_interruptible(&buf->dma_wait); + } else { + /* If processes are waiting, the last one + to wake will put the buffer on the free + list. If no processes are waiting, we + put the buffer on the freelist here. */ + drm_freelist_put(dev, &dma->bufs[buf->order].freelist, buf); + } +} + +void drm_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + for (i = 0; i < dma->buf_count; i++) { + if (dma->buflist[i]->pid == pid) { + switch (dma->buflist[i]->list) { + case DRM_LIST_NONE: + drm_free_buffer(dev, dma->buflist[i]); + break; + case DRM_LIST_WAIT: + dma->buflist[i]->list = DRM_LIST_RECLAIM; + break; + default: + /* Buffer already on hardware. */ + break; + } + } + } +} + +int drm_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + drm_queue_t *q; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new >= dev->queue_count) { + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + q = dev->queuelist[new]; + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) == 1) { + atomic_dec(&q->use_count); + clear_bit(0, &dev->context_flag); + return -EINVAL; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + drm_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + atomic_dec(&q->use_count); + + return 0; +} + +int drm_context_switch_complete(drm_device_t *dev, int new) +{ + drm_device_dma_t *dma = dev->dma; + + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + if (!dma || !(dma->next_buffer && dma->next_buffer->while_locked)) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("Cannot free lock\n"); + } + } + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up_interruptible(&dev->context_wait); + + return 0; +} + +void drm_clear_next_buffer(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + dma->next_buffer = NULL; + if (dma->next_queue && !DRM_BUFCOUNT(&dma->next_queue->waitlist)) { + wake_up_interruptible(&dma->next_queue->flush_queue); + } + dma->next_queue = NULL; +} + + +int drm_select_queue(drm_device_t *dev, void (*wrapper)(unsigned long)) +{ + int i; + int candidate = -1; + int j = jiffies; + + if (!dev) { + DRM_ERROR("No device\n"); + return -1; + } + if (!dev->queuelist || !dev->queuelist[DRM_KERNEL_CONTEXT]) { + /* This only happens between the time the + interrupt is initialized and the time + the queues are initialized. */ + return -1; + } + + /* Doing "while locked" DMA? */ + if (DRM_WAITCOUNT(dev, DRM_KERNEL_CONTEXT)) { + return DRM_KERNEL_CONTEXT; + } + + /* If there are buffers on the last_context + queue, and we have not been executing + this context very long, continue to + execute this context. */ + if (dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j + && DRM_WAITCOUNT(dev, dev->last_context)) { + return dev->last_context; + } + + /* Otherwise, find a candidate */ + for (i = dev->last_checked + 1; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + + if (candidate < 0) { + for (i = 0; i < dev->queue_count; i++) { + if (DRM_WAITCOUNT(dev, i)) { + candidate = dev->last_checked = i; + break; + } + } + } + + if (wrapper + && candidate >= 0 + && candidate != dev->last_context + && dev->last_switch <= j + && dev->last_switch + DRM_TIME_SLICE > j) { + if (dev->timer.expires != dev->last_switch + DRM_TIME_SLICE) { + del_timer(&dev->timer); + dev->timer.function = wrapper; + dev->timer.data = (unsigned long)dev; + dev->timer.expires = dev->last_switch+DRM_TIME_SLICE; + add_timer(&dev->timer); + } + return -1; + } + + return candidate; +} + + +int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *d) +{ + int i; + drm_queue_t *q; + drm_buf_t *buf; + int idx; + int while_locked = 0; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + DRM_DEBUG("%d\n", d->send_count); + + if (d->flags & _DRM_DMA_WHILE_LOCKED) { + int context = dev->lock.hw_lock->lock; + + if (!_DRM_LOCK_IS_HELD(context)) { + DRM_ERROR("No lock held during \"while locked\"" + " request\n"); + return -EINVAL; + } + if (d->context != _DRM_LOCKING_CONTEXT(context) + && _DRM_LOCKING_CONTEXT(context) != DRM_KERNEL_CONTEXT) { + DRM_ERROR("Lock held by %d while %d makes" + " \"while locked\" request\n", + _DRM_LOCKING_CONTEXT(context), + d->context); + return -EINVAL; + } + q = dev->queuelist[DRM_KERNEL_CONTEXT]; + while_locked = 1; + } else { + q = dev->queuelist[d->context]; + } + + + atomic_inc(&q->use_count); + if (atomic_read(&q->block_write)) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&q->write_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + if (!atomic_read(&q->block_write)) break; + schedule(); + if (signal_pending(current)) { + atomic_dec(&q->use_count); + return -EINTR; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->write_queue, &entry); + } + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + atomic_dec(&q->use_count); + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->list != DRM_LIST_NONE) { + atomic_dec(&q->use_count); + DRM_ERROR("Process %d using buffer %d on list %d\n", + current->pid, buf->idx, buf->list); + } + buf->used = d->send_sizes[i]; + buf->while_locked = while_locked; + buf->context = d->context; + if (!buf->used) { + DRM_ERROR("Queueing 0 length buffer\n"); + } + if (buf->pending) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + if (buf->waiting) { + atomic_dec(&q->use_count); + DRM_ERROR("Queueing waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + return -EINVAL; + } + buf->waiting = 1; + if (atomic_read(&q->use_count) == 1 + || atomic_read(&q->finalization)) { + drm_free_buffer(dev, buf); + } else { + drm_waitlist_put(&q->waitlist, buf); + atomic_inc(&q->total_queued); + } + } + atomic_dec(&q->use_count); + + return 0; +} + +static int drm_dma_get_buffers_of_order(drm_device_t *dev, drm_dma_t *d, + int order) +{ + int i; + drm_buf_t *buf; + drm_device_dma_t *dma = dev->dma; + + for (i = d->granted_count; i < d->request_count; i++) { + buf = drm_freelist_get(&dma->bufs[order].freelist, + d->flags & _DRM_DMA_WAIT); + if (!buf) break; + if (buf->pending || buf->waiting) { + DRM_ERROR("Free buffer %d in use by %d (w%d, p%d)\n", + buf->idx, + buf->pid, + buf->waiting, + buf->pending); + } + buf->pid = current->pid; + copy_to_user_ret(&d->request_indices[i], + &buf->idx, + sizeof(buf->idx), + -EFAULT); + copy_to_user_ret(&d->request_sizes[i], + &buf->total, + sizeof(buf->total), + -EFAULT); + ++d->granted_count; + } + return 0; +} + + +int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma) +{ + int order; + int retcode = 0; + int tmp_order; + + order = drm_order(dma->request_size); + + dma->granted_count = 0; + retcode = drm_dma_get_buffers_of_order(dev, dma, order); + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_SMALLER_OK)) { + for (tmp_order = order - 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order >= DRM_MIN_ORDER; + --tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + + if (dma->granted_count < dma->request_count + && (dma->flags & _DRM_DMA_LARGER_OK)) { + for (tmp_order = order + 1; + !retcode + && dma->granted_count < dma->request_count + && tmp_order <= DRM_MAX_ORDER; + ++tmp_order) { + + retcode = drm_dma_get_buffers_of_order(dev, dma, + tmp_order); + } + } + return 0; +} diff --git a/drivers/char/drm/drawable.c b/drivers/char/drm/drawable.c new file mode 100644 index 000000000000..03839f5be544 --- /dev/null +++ b/drivers/char/drm/drawable.c @@ -0,0 +1,50 @@ +/* drawable.c -- IOCTLs for drawables -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_adddraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_draw_t draw; + + draw.handle = 0; /* NOOP */ + DRM_DEBUG("%d\n", draw.handle); + copy_to_user_ret((drm_draw_t *)arg, &draw, sizeof(draw), -EFAULT); + return 0; +} + +int drm_rmdraw(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + return 0; /* NOOP */ +} diff --git a/drivers/char/drm/drm.h b/drivers/char/drm/drm.h new file mode 100644 index 000000000000..5a979e1cbf96 --- /dev/null +++ b/drivers/char/drm/drm.h @@ -0,0 +1,367 @@ +/* drm.h -- Header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + * Acknowledgements: + * Dec 1999, Richard Henderson , move to generic cmpxchg. + * + */ + +#ifndef _DRM_H_ +#define _DRM_H_ + +#include /* For _IO* macros */ + +#define DRM_PROC_DEVICES "/proc/devices" +#define DRM_PROC_MISC "/proc/misc" +#define DRM_PROC_DRM "/proc/drm" +#define DRM_DEV_DRM "/dev/drm" +#define DRM_DEV_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP) +#define DRM_DEV_UID 0 +#define DRM_DEV_GID 0 + + +#define DRM_NAME "drm" /* Name in kernel, /dev, and /proc */ +#define DRM_MIN_ORDER 5 /* At least 2^5 bytes = 32 bytes */ +#define DRM_MAX_ORDER 22 /* Up to 2^22 bytes = 4MB */ +#define DRM_RAM_PERCENT 10 /* How much system ram can we lock? */ + +#define _DRM_LOCK_HELD 0x80000000 /* Hardware lock is held */ +#define _DRM_LOCK_CONT 0x40000000 /* Hardware lock is contended */ +#define _DRM_LOCK_IS_HELD(lock) ((lock) & _DRM_LOCK_HELD) +#define _DRM_LOCK_IS_CONT(lock) ((lock) & _DRM_LOCK_CONT) +#define _DRM_LOCKING_CONTEXT(lock) ((lock) & ~(_DRM_LOCK_HELD|_DRM_LOCK_CONT)) + +typedef unsigned long drm_handle_t; +typedef unsigned int drm_context_t; +typedef unsigned int drm_drawable_t; +typedef unsigned int drm_magic_t; + +/* Warning: If you change this structure, make sure you change + * XF86DRIClipRectRec in the server as well */ + +typedef struct drm_clip_rect { + unsigned short x1; + unsigned short y1; + unsigned short x2; + unsigned short y2; +} drm_clip_rect_t; + +/* Seperate include files for the i810/mga/r128 specific structures */ +#include "mga_drm.h" +#include "i810_drm.h" +#include "r128_drm.h" + +typedef struct drm_version { + int version_major; /* Major version */ + int version_minor; /* Minor version */ + int version_patchlevel;/* Patch level */ + size_t name_len; /* Length of name buffer */ + char *name; /* Name of driver */ + size_t date_len; /* Length of date buffer */ + char *date; /* User-space buffer to hold date */ + size_t desc_len; /* Length of desc buffer */ + char *desc; /* User-space buffer to hold desc */ +} drm_version_t; + +typedef struct drm_unique { + size_t unique_len; /* Length of unique */ + char *unique; /* Unique name for driver instantiation */ +} drm_unique_t; + +typedef struct drm_list { + int count; /* Length of user-space structures */ + drm_version_t *version; +} drm_list_t; + +typedef struct drm_block { + int unused; +} drm_block_t; + +typedef struct drm_control { + enum { + DRM_ADD_COMMAND, + DRM_RM_COMMAND, + DRM_INST_HANDLER, + DRM_UNINST_HANDLER + } func; + int irq; +} drm_control_t; + +typedef enum drm_map_type { + _DRM_FRAME_BUFFER = 0, /* WC (no caching), no core dump */ + _DRM_REGISTERS = 1, /* no caching, no core dump */ + _DRM_SHM = 2, /* shared, cached */ + _DRM_AGP = 3 /* AGP/GART */ +} drm_map_type_t; + +typedef enum drm_map_flags { + _DRM_RESTRICTED = 0x01, /* Cannot be mapped to user-virtual */ + _DRM_READ_ONLY = 0x02, + _DRM_LOCKED = 0x04, /* shared, cached, locked */ + _DRM_KERNEL = 0x08, /* kernel requires access */ + _DRM_WRITE_COMBINING = 0x10, /* use write-combining if available */ + _DRM_CONTAINS_LOCK = 0x20 /* SHM page that contains lock */ +} drm_map_flags_t; + +typedef struct drm_map { + unsigned long offset; /* Requested physical address (0 for SAREA)*/ + unsigned long size; /* Requested physical size (bytes) */ + drm_map_type_t type; /* Type of memory to map */ + drm_map_flags_t flags; /* Flags */ + void *handle; /* User-space: "Handle" to pass to mmap */ + /* Kernel-space: kernel-virtual address */ + int mtrr; /* MTRR slot used */ + /* Private data */ +} drm_map_t; + +typedef enum drm_lock_flags { + _DRM_LOCK_READY = 0x01, /* Wait until hardware is ready for DMA */ + _DRM_LOCK_QUIESCENT = 0x02, /* Wait until hardware quiescent */ + _DRM_LOCK_FLUSH = 0x04, /* Flush this context's DMA queue first */ + _DRM_LOCK_FLUSH_ALL = 0x08, /* Flush all DMA queues first */ + /* These *HALT* flags aren't supported yet + -- they will be used to support the + full-screen DGA-like mode. */ + _DRM_HALT_ALL_QUEUES = 0x10, /* Halt all current and future queues */ + _DRM_HALT_CUR_QUEUES = 0x20 /* Halt all current queues */ +} drm_lock_flags_t; + +typedef struct drm_lock { + int context; + drm_lock_flags_t flags; +} drm_lock_t; + +typedef enum drm_dma_flags { /* These values *MUST* match xf86drm.h */ + /* Flags for DMA buffer dispatch */ + _DRM_DMA_BLOCK = 0x01, /* Block until buffer dispatched. + Note, the buffer may not yet have + been processed by the hardware -- + getting a hardware lock with the + hardware quiescent will ensure + that the buffer has been + processed. */ + _DRM_DMA_WHILE_LOCKED = 0x02, /* Dispatch while lock held */ + _DRM_DMA_PRIORITY = 0x04, /* High priority dispatch */ + + /* Flags for DMA buffer request */ + _DRM_DMA_WAIT = 0x10, /* Wait for free buffers */ + _DRM_DMA_SMALLER_OK = 0x20, /* Smaller-than-requested buffers ok */ + _DRM_DMA_LARGER_OK = 0x40 /* Larger-than-requested buffers ok */ +} drm_dma_flags_t; + +typedef struct drm_buf_desc { + int count; /* Number of buffers of this size */ + int size; /* Size in bytes */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + enum { + _DRM_PAGE_ALIGN = 0x01, /* Align on page boundaries for DMA */ + _DRM_AGP_BUFFER = 0x02 /* Buffer is in agp space */ + } flags; + unsigned long agp_start; /* Start address of where the agp buffers + * are in the agp aperture */ +} drm_buf_desc_t; + +typedef struct drm_buf_info { + int count; /* Entries in list */ + drm_buf_desc_t *list; +} drm_buf_info_t; + +typedef struct drm_buf_free { + int count; + int *list; +} drm_buf_free_t; + +typedef struct drm_buf_pub { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int used; /* Amount of buffer in use (for DMA) */ + void *address; /* Address of buffer */ +} drm_buf_pub_t; + +typedef struct drm_buf_map { + int count; /* Length of buflist */ + void *virtual; /* Mmaped area in user-virtual */ + drm_buf_pub_t *list; /* Buffer information */ +} drm_buf_map_t; + +typedef struct drm_dma { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int context; /* Context handle */ + int send_count; /* Number of buffers to send */ + int *send_indices; /* List of handles to buffers */ + int *send_sizes; /* Lengths of data to send */ + drm_dma_flags_t flags; /* Flags */ + int request_count; /* Number of buffers requested */ + int request_size; /* Desired size for buffers */ + int *request_indices; /* Buffer information */ + int *request_sizes; + int granted_count; /* Number of buffers granted */ +} drm_dma_t; + +typedef enum { + _DRM_CONTEXT_PRESERVED = 0x01, + _DRM_CONTEXT_2DONLY = 0x02 +} drm_ctx_flags_t; + +typedef struct drm_ctx { + drm_context_t handle; + drm_ctx_flags_t flags; +} drm_ctx_t; + +typedef struct drm_ctx_res { + int count; + drm_ctx_t *contexts; +} drm_ctx_res_t; + +typedef struct drm_draw { + drm_drawable_t handle; +} drm_draw_t; + +typedef struct drm_auth { + drm_magic_t magic; +} drm_auth_t; + +typedef struct drm_irq_busid { + int irq; + int busnum; + int devnum; + int funcnum; +} drm_irq_busid_t; + +typedef struct drm_agp_mode { + unsigned long mode; +} drm_agp_mode_t; + + /* For drm_agp_alloc -- allocated a buffer */ +typedef struct drm_agp_buffer { + unsigned long size; /* In bytes -- will round to page boundary */ + unsigned long handle; /* Used for BIND/UNBIND ioctls */ + unsigned long type; /* Type of memory to allocate */ + unsigned long physical; /* Physical used by i810 */ +} drm_agp_buffer_t; + + /* For drm_agp_bind */ +typedef struct drm_agp_binding { + unsigned long handle; /* From drm_agp_buffer */ + unsigned long offset; /* In bytes -- will round to page boundary */ +} drm_agp_binding_t; + +typedef struct drm_agp_info { + int agp_version_major; + int agp_version_minor; + unsigned long mode; + unsigned long aperture_base; /* physical address */ + unsigned long aperture_size; /* bytes */ + unsigned long memory_allowed; /* bytes */ + unsigned long memory_used; + + /* PCI information */ + unsigned short id_vendor; + unsigned short id_device; +} drm_agp_info_t; + +#define DRM_IOCTL_BASE 'd' +#define DRM_IOCTL_NR(n) _IOC_NR(n) +#define DRM_IO(nr) _IO(DRM_IOCTL_BASE,nr) +#define DRM_IOR(nr,size) _IOR(DRM_IOCTL_BASE,nr,size) +#define DRM_IOW(nr,size) _IOW(DRM_IOCTL_BASE,nr,size) +#define DRM_IOWR(nr,size) _IOWR(DRM_IOCTL_BASE,nr,size) + + +#define DRM_IOCTL_VERSION DRM_IOWR(0x00, drm_version_t) +#define DRM_IOCTL_GET_UNIQUE DRM_IOWR(0x01, drm_unique_t) +#define DRM_IOCTL_GET_MAGIC DRM_IOR( 0x02, drm_auth_t) +#define DRM_IOCTL_IRQ_BUSID DRM_IOWR(0x03, drm_irq_busid_t) + +#define DRM_IOCTL_SET_UNIQUE DRM_IOW( 0x10, drm_unique_t) +#define DRM_IOCTL_AUTH_MAGIC DRM_IOW( 0x11, drm_auth_t) +#define DRM_IOCTL_BLOCK DRM_IOWR(0x12, drm_block_t) +#define DRM_IOCTL_UNBLOCK DRM_IOWR(0x13, drm_block_t) +#define DRM_IOCTL_CONTROL DRM_IOW( 0x14, drm_control_t) +#define DRM_IOCTL_ADD_MAP DRM_IOWR(0x15, drm_map_t) +#define DRM_IOCTL_ADD_BUFS DRM_IOWR(0x16, drm_buf_desc_t) +#define DRM_IOCTL_MARK_BUFS DRM_IOW( 0x17, drm_buf_desc_t) +#define DRM_IOCTL_INFO_BUFS DRM_IOWR(0x18, drm_buf_info_t) +#define DRM_IOCTL_MAP_BUFS DRM_IOWR(0x19, drm_buf_map_t) +#define DRM_IOCTL_FREE_BUFS DRM_IOW( 0x1a, drm_buf_free_t) + +#define DRM_IOCTL_ADD_CTX DRM_IOWR(0x20, drm_ctx_t) +#define DRM_IOCTL_RM_CTX DRM_IOWR(0x21, drm_ctx_t) +#define DRM_IOCTL_MOD_CTX DRM_IOW( 0x22, drm_ctx_t) +#define DRM_IOCTL_GET_CTX DRM_IOWR(0x23, drm_ctx_t) +#define DRM_IOCTL_SWITCH_CTX DRM_IOW( 0x24, drm_ctx_t) +#define DRM_IOCTL_NEW_CTX DRM_IOW( 0x25, drm_ctx_t) +#define DRM_IOCTL_RES_CTX DRM_IOWR(0x26, drm_ctx_res_t) +#define DRM_IOCTL_ADD_DRAW DRM_IOWR(0x27, drm_draw_t) +#define DRM_IOCTL_RM_DRAW DRM_IOWR(0x28, drm_draw_t) +#define DRM_IOCTL_DMA DRM_IOWR(0x29, drm_dma_t) +#define DRM_IOCTL_LOCK DRM_IOW( 0x2a, drm_lock_t) +#define DRM_IOCTL_UNLOCK DRM_IOW( 0x2b, drm_lock_t) +#define DRM_IOCTL_FINISH DRM_IOW( 0x2c, drm_lock_t) + +#define DRM_IOCTL_AGP_ACQUIRE DRM_IO( 0x30) +#define DRM_IOCTL_AGP_RELEASE DRM_IO( 0x31) +#define DRM_IOCTL_AGP_ENABLE DRM_IOW( 0x32, drm_agp_mode_t) +#define DRM_IOCTL_AGP_INFO DRM_IOR( 0x33, drm_agp_info_t) +#define DRM_IOCTL_AGP_ALLOC DRM_IOWR(0x34, drm_agp_buffer_t) +#define DRM_IOCTL_AGP_FREE DRM_IOW( 0x35, drm_agp_buffer_t) +#define DRM_IOCTL_AGP_BIND DRM_IOW( 0x36, drm_agp_binding_t) +#define DRM_IOCTL_AGP_UNBIND DRM_IOW( 0x37, drm_agp_binding_t) + +/* Mga specific ioctls */ +#define DRM_IOCTL_MGA_INIT DRM_IOW( 0x40, drm_mga_init_t) +#define DRM_IOCTL_MGA_SWAP DRM_IOW( 0x41, drm_mga_swap_t) +#define DRM_IOCTL_MGA_CLEAR DRM_IOW( 0x42, drm_mga_clear_t) +#define DRM_IOCTL_MGA_ILOAD DRM_IOW( 0x43, drm_mga_iload_t) +#define DRM_IOCTL_MGA_VERTEX DRM_IOW( 0x44, drm_mga_vertex_t) +#define DRM_IOCTL_MGA_FLUSH DRM_IOW( 0x45, drm_lock_t ) +#define DRM_IOCTL_MGA_INDICES DRM_IOW( 0x46, drm_mga_indices_t) + +/* I810 specific ioctls */ +#define DRM_IOCTL_I810_INIT DRM_IOW( 0x40, drm_i810_init_t) +#define DRM_IOCTL_I810_VERTEX DRM_IOW( 0x41, drm_i810_vertex_t) +#define DRM_IOCTL_I810_CLEAR DRM_IOW( 0x42, drm_i810_clear_t) +#define DRM_IOCTL_I810_FLUSH DRM_IO ( 0x43) +#define DRM_IOCTL_I810_GETAGE DRM_IO ( 0x44) +#define DRM_IOCTL_I810_GETBUF DRM_IOWR(0x45, drm_i810_dma_t) +#define DRM_IOCTL_I810_SWAP DRM_IO ( 0x46) +#define DRM_IOCTL_I810_COPY DRM_IOW( 0x47, drm_i810_copy_t) +#define DRM_IOCTL_I810_DOCOPY DRM_IO ( 0x48) + +/* Rage 128 specific ioctls */ +#define DRM_IOCTL_R128_INIT DRM_IOW( 0x40, drm_r128_init_t) +#define DRM_IOCTL_R128_RESET DRM_IO( 0x41) +#define DRM_IOCTL_R128_FLUSH DRM_IO( 0x42) +#define DRM_IOCTL_R128_IDLE DRM_IO( 0x43) +#define DRM_IOCTL_R128_PACKET DRM_IOW( 0x44, drm_r128_packet_t) +#define DRM_IOCTL_R128_VERTEX DRM_IOW( 0x45, drm_r128_vertex_t) + +#endif diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h new file mode 100644 index 000000000000..e4783c8db90d --- /dev/null +++ b/drivers/char/drm/drmP.h @@ -0,0 +1,750 @@ +/* drmP.h -- Private header for Direct Rendering Manager -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#ifndef _DRM_P_H_ +#define _DRM_P_H_ + +#ifdef __KERNEL__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* For (un)lock_kernel */ +#include +#include +#include +#ifdef CONFIG_MTRR +#include +#endif +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +#include +#include +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +#include +#include +#endif +#include "drm.h" + +#define DRM_DEBUG_CODE 2 /* Include debugging code (if > 1, then + also include looping detection. */ +#define DRM_DMA_HISTOGRAM 1 /* Make histogram of DMA latency. */ + +#define DRM_HASH_SIZE 16 /* Size of key hash table */ +#define DRM_KERNEL_CONTEXT 0 /* Change drm_resctx if changed */ +#define DRM_RESERVED_CONTEXTS 1 /* Change drm_resctx if changed */ +#define DRM_LOOPING_LIMIT 5000000 +#define DRM_BSZ 1024 /* Buffer size for /dev/drm? output */ +#define DRM_TIME_SLICE (HZ/20) /* Time slice for GLXContexts */ +#define DRM_LOCK_SLICE 1 /* Time slice for lock, in jiffies */ + +#define DRM_FLAG_DEBUG 0x01 +#define DRM_FLAG_NOCTX 0x02 + +#define DRM_MEM_DMA 0 +#define DRM_MEM_SAREA 1 +#define DRM_MEM_DRIVER 2 +#define DRM_MEM_MAGIC 3 +#define DRM_MEM_IOCTLS 4 +#define DRM_MEM_MAPS 5 +#define DRM_MEM_VMAS 6 +#define DRM_MEM_BUFS 7 +#define DRM_MEM_SEGS 8 +#define DRM_MEM_PAGES 9 +#define DRM_MEM_FILES 10 +#define DRM_MEM_QUEUES 11 +#define DRM_MEM_CMDS 12 +#define DRM_MEM_MAPPINGS 13 +#define DRM_MEM_BUFLISTS 14 +#define DRM_MEM_AGPLISTS 15 +#define DRM_MEM_TOTALAGP 16 +#define DRM_MEM_BOUNDAGP 17 +#define DRM_MEM_CTXBITMAP 18 + +#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8) + + /* Backward compatibility section */ + /* _PAGE_WT changed to _PAGE_PWT in 2.2.6 */ +#ifndef _PAGE_PWT +#define _PAGE_PWT _PAGE_WT +#endif + /* Wait queue declarations changed in 2.3.1 */ +#ifndef DECLARE_WAITQUEUE +#define DECLARE_WAITQUEUE(w,c) struct wait_queue w = { c, NULL } +typedef struct wait_queue *wait_queue_head_t; +#define init_waitqueue_head(q) *q = NULL; +#endif + + /* _PAGE_4M changed to _PAGE_PSE in 2.3.23 */ +#ifndef _PAGE_PSE +#define _PAGE_PSE _PAGE_4M +#endif + + /* vm_offset changed to vm_pgoff in 2.3.25 */ +#if LINUX_VERSION_CODE < 0x020319 +#define VM_OFFSET(vma) ((vma)->vm_offset) +#else +#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT) +#endif + + /* *_nopage return values defined in 2.3.26 */ +#ifndef NOPAGE_SIGBUS +#define NOPAGE_SIGBUS 0 +#endif +#ifndef NOPAGE_OOM +#define NOPAGE_OOM 0 +#endif + + /* module_init/module_exit added in 2.3.13 */ +#ifndef module_init +#define module_init(x) int init_module(void) { return x(); } +#endif +#ifndef module_exit +#define module_exit(x) void cleanup_module(void) { x(); } +#endif + + /* Generic cmpxchg added in 2.3.x */ +#ifndef __HAVE_ARCH_CMPXCHG + /* Include this here so that driver can be + used with older kernels. */ +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, + unsigned long new, int size) +{ + unsigned long prev; + switch (size) { + case 1: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgb %b1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 2: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgw %w1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + case 4: + __asm__ __volatile__(LOCK_PREFIX "cmpxchgl %1,%2" + : "=a"(prev) + : "q"(new), "m"(*__xg(ptr)), "0"(old) + : "memory"); + return prev; + } + return old; +} + +#define cmpxchg(ptr,o,n) \ + ((__typeof__(*(ptr)))__cmpxchg((ptr),(unsigned long)(o), \ + (unsigned long)(n),sizeof(*(ptr)))) +#endif + + /* Macros to make printk easier */ +#define DRM_ERROR(fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ "] *ERROR* " fmt , ##arg) +#define DRM_MEM_ERROR(area, fmt, arg...) \ + printk(KERN_ERR "[" DRM_NAME ":" __FUNCTION__ ":%s] *ERROR* " fmt , \ + drm_mem_stats[area].name , ##arg) +#define DRM_INFO(fmt, arg...) printk(KERN_INFO "[" DRM_NAME "] " fmt , ##arg) + +#if DRM_DEBUG_CODE +#define DRM_DEBUG(fmt, arg...) \ + do { \ + if (drm_flags&DRM_FLAG_DEBUG) \ + printk(KERN_DEBUG \ + "[" DRM_NAME ":" __FUNCTION__ "] " fmt , \ + ##arg); \ + } while (0) +#else +#define DRM_DEBUG(fmt, arg...) do { } while (0) +#endif + +#define DRM_PROC_LIMIT (PAGE_SIZE-80) + +#define DRM_PROC_PRINT(fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) return len; + +#define DRM_PROC_PRINT_RET(ret, fmt, arg...) \ + len += sprintf(&buf[len], fmt , ##arg); \ + if (len > DRM_PROC_LIMIT) { ret; return len; } + + /* Internal types and structures */ +#define DRM_ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) +#define DRM_MIN(a,b) ((a)<(b)?(a):(b)) +#define DRM_MAX(a,b) ((a)>(b)?(a):(b)) + +#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1)) +#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x)) +#define DRM_WAITCOUNT(dev,idx) DRM_BUFCOUNT(&dev->queuelist[idx]->waitlist) + +typedef int drm_ioctl_t(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +typedef struct drm_ioctl_desc { + drm_ioctl_t *func; + int auth_needed; + int root_only; +} drm_ioctl_desc_t; + +typedef struct drm_devstate { + pid_t owner; /* X server pid holding x_lock */ + +} drm_devstate_t; + +typedef struct drm_magic_entry { + drm_magic_t magic; + struct drm_file *priv; + struct drm_magic_entry *next; +} drm_magic_entry_t; + +typedef struct drm_magic_head { + struct drm_magic_entry *head; + struct drm_magic_entry *tail; +} drm_magic_head_t; + +typedef struct drm_vma_entry { + struct vm_area_struct *vma; + struct drm_vma_entry *next; + pid_t pid; +} drm_vma_entry_t; + +typedef struct drm_buf { + int idx; /* Index into master buflist */ + int total; /* Buffer size */ + int order; /* log-base-2(total) */ + int used; /* Amount of buffer in use (for DMA) */ + unsigned long offset; /* Byte offset (used internally) */ + void *address; /* Address of buffer */ + unsigned long bus_address; /* Bus address of buffer */ + struct drm_buf *next; /* Kernel-only: used for free list */ + __volatile__ int waiting; /* On kernel DMA queue */ + __volatile__ int pending; /* On hardware DMA queue */ + wait_queue_head_t dma_wait; /* Processes waiting */ + pid_t pid; /* PID of holding process */ + int context; /* Kernel queue for this buffer */ + int while_locked;/* Dispatch this buffer while locked */ + enum { + DRM_LIST_NONE = 0, + DRM_LIST_FREE = 1, + DRM_LIST_WAIT = 2, + DRM_LIST_PEND = 3, + DRM_LIST_PRIO = 4, + DRM_LIST_RECLAIM = 5 + } list; /* Which list we're on */ + +#if DRM_DMA_HISTOGRAM + cycles_t time_queued; /* Queued to kernel DMA queue */ + cycles_t time_dispatched; /* Dispatched to hardware */ + cycles_t time_completed; /* Completed by hardware */ + cycles_t time_freed; /* Back on freelist */ +#endif + + int dev_priv_size; /* Size of buffer private stoarge */ + void *dev_private; /* Per-buffer private storage */ +} drm_buf_t; + +#if DRM_DMA_HISTOGRAM +#define DRM_DMA_HISTOGRAM_SLOTS 9 +#define DRM_DMA_HISTOGRAM_INITIAL 10 +#define DRM_DMA_HISTOGRAM_NEXT(current) ((current)*10) +typedef struct drm_histogram { + atomic_t total; + + atomic_t queued_to_dispatched[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t dispatched_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t completed_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t queued_to_completed[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t queued_to_freed[DRM_DMA_HISTOGRAM_SLOTS]; + + atomic_t dma[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t schedule[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t ctx[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lacq[DRM_DMA_HISTOGRAM_SLOTS]; + atomic_t lhld[DRM_DMA_HISTOGRAM_SLOTS]; +} drm_histogram_t; +#endif + + /* bufs is one longer than it has to be */ +typedef struct drm_waitlist { + int count; /* Number of possible buffers */ + drm_buf_t **bufs; /* List of pointers to buffers */ + drm_buf_t **rp; /* Read pointer */ + drm_buf_t **wp; /* Write pointer */ + drm_buf_t **end; /* End pointer */ + spinlock_t read_lock; + spinlock_t write_lock; +} drm_waitlist_t; + +typedef struct drm_freelist { + int initialized; /* Freelist in use */ + atomic_t count; /* Number of free buffers */ + drm_buf_t *next; /* End pointer */ + + wait_queue_head_t waiting; /* Processes waiting on free bufs */ + int low_mark; /* Low water mark */ + int high_mark; /* High water mark */ + atomic_t wfh; /* If waiting for high mark */ +} drm_freelist_t; + +typedef struct drm_buf_entry { + int buf_size; + int buf_count; + drm_buf_t *buflist; + int seg_count; + int page_order; + unsigned long *seglist; + + drm_freelist_t freelist; +} drm_buf_entry_t; + +typedef struct drm_hw_lock { + __volatile__ unsigned int lock; + char padding[60]; /* Pad to cache line */ +} drm_hw_lock_t; + +typedef struct drm_file { + int authenticated; + int minor; + pid_t pid; + uid_t uid; + drm_magic_t magic; + unsigned long ioctl_count; + struct drm_file *next; + struct drm_file *prev; + struct drm_device *dev; +} drm_file_t; + + +typedef struct drm_queue { + atomic_t use_count; /* Outstanding uses (+1) */ + atomic_t finalization; /* Finalization in progress */ + atomic_t block_count; /* Count of processes waiting */ + atomic_t block_read; /* Queue blocked for reads */ + wait_queue_head_t read_queue; /* Processes waiting on block_read */ + atomic_t block_write; /* Queue blocked for writes */ + wait_queue_head_t write_queue; /* Processes waiting on block_write */ + atomic_t total_queued; /* Total queued statistic */ + atomic_t total_flushed;/* Total flushes statistic */ + atomic_t total_locks; /* Total locks statistics */ + drm_ctx_flags_t flags; /* Context preserving and 2D-only */ + drm_waitlist_t waitlist; /* Pending buffers */ + wait_queue_head_t flush_queue; /* Processes waiting until flush */ +} drm_queue_t; + +typedef struct drm_lock_data { + drm_hw_lock_t *hw_lock; /* Hardware lock */ + pid_t pid; /* PID of lock holder (0=kernel) */ + wait_queue_head_t lock_queue; /* Queue of blocked processes */ + unsigned long lock_time; /* Time of last lock in jiffies */ +} drm_lock_data_t; + +typedef struct drm_device_dma { + /* Performance Counters */ + atomic_t total_prio; /* Total DRM_DMA_PRIORITY */ + atomic_t total_bytes; /* Total bytes DMA'd */ + atomic_t total_dmas; /* Total DMA buffers dispatched */ + + atomic_t total_missed_dma; /* Missed drm_do_dma */ + atomic_t total_missed_lock; /* Missed lock in drm_do_dma */ + atomic_t total_missed_free; /* Missed drm_free_this_buffer */ + atomic_t total_missed_sched;/* Missed drm_dma_schedule */ + + atomic_t total_tried; /* Tried next_buffer */ + atomic_t total_hit; /* Sent next_buffer */ + atomic_t total_lost; /* Lost interrupt */ + + drm_buf_entry_t bufs[DRM_MAX_ORDER+1]; + int buf_count; + drm_buf_t **buflist; /* Vector of pointers info bufs */ + int seg_count; + int page_count; + unsigned long *pagelist; + unsigned long byte_count; + enum { + _DRM_DMA_USE_AGP = 0x01 + } flags; + + /* DMA support */ + drm_buf_t *this_buffer; /* Buffer being sent */ + drm_buf_t *next_buffer; /* Selected buffer to send */ + drm_queue_t *next_queue; /* Queue from which buffer selected*/ + wait_queue_head_t waiting; /* Processes waiting on free bufs */ +} drm_device_dma_t; + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +typedef struct drm_agp_mem { + unsigned long handle; + agp_memory *memory; + unsigned long bound; /* address */ + int pages; + struct drm_agp_mem *prev; + struct drm_agp_mem *next; +} drm_agp_mem_t; + +typedef struct drm_agp_head { + agp_kern_info agp_info; + const char *chipset; + drm_agp_mem_t *memory; + unsigned long mode; + int enabled; + int acquired; + unsigned long base; + int agp_mtrr; +} drm_agp_head_t; + +typedef struct { + void (*free_memory)(agp_memory *); + agp_memory *(*allocate_memory)(size_t, u32); + int (*bind_memory)(agp_memory *, off_t); + int (*unbind_memory)(agp_memory *); + void (*enable)(u32); + int (*acquire)(void); + void (*release)(void); + void (*copy_info)(agp_kern_info *); +} drm_agp_func_t; + +extern drm_agp_func_t drm_agp; +#endif + +typedef struct drm_device { + const char *name; /* Simple driver name */ + char *unique; /* Unique identifier: e.g., busid */ + int unique_len; /* Length of unique field */ + dev_t device; /* Device number for mknod */ + char *devname; /* For /proc/interrupts */ + + int blocked; /* Blocked due to VC switch? */ + struct proc_dir_entry *root; /* Root for this device's entries */ + + /* Locks */ + spinlock_t count_lock; /* For inuse, open_count, buf_use */ + struct semaphore struct_sem; /* For others */ + + /* Usage Counters */ + int open_count; /* Outstanding files open */ + atomic_t ioctl_count; /* Outstanding IOCTLs pending */ + atomic_t vma_count; /* Outstanding vma areas open */ + int buf_use; /* Buffers in use -- cannot alloc */ + atomic_t buf_alloc; /* Buffer allocation in progress */ + + /* Performance Counters */ + atomic_t total_open; + atomic_t total_close; + atomic_t total_ioctl; + atomic_t total_irq; /* Total interruptions */ + atomic_t total_ctx; /* Total context switches */ + + atomic_t total_locks; + atomic_t total_unlocks; + atomic_t total_contends; + atomic_t total_sleeps; + + /* Authentication */ + drm_file_t *file_first; + drm_file_t *file_last; + drm_magic_head_t magiclist[DRM_HASH_SIZE]; + + /* Memory management */ + drm_map_t **maplist; /* Vector of pointers to regions */ + int map_count; /* Number of mappable regions */ + + drm_vma_entry_t *vmalist; /* List of vmas (for debugging) */ + drm_lock_data_t lock; /* Information on hardware lock */ + + /* DMA queues (contexts) */ + int queue_count; /* Number of active DMA queues */ + int queue_reserved; /* Number of reserved DMA queues */ + int queue_slots; /* Actual length of queuelist */ + drm_queue_t **queuelist; /* Vector of pointers to DMA queues */ + drm_device_dma_t *dma; /* Optional pointer for DMA support */ + + /* Context support */ + int irq; /* Interrupt used by board */ + __volatile__ long context_flag; /* Context swapping flag */ + __volatile__ long interrupt_flag; /* Interruption handler flag */ + __volatile__ long dma_flag; /* DMA dispatch flag */ + struct timer_list timer; /* Timer for delaying ctx switch */ + wait_queue_head_t context_wait; /* Processes waiting on ctx switch */ + int last_checked; /* Last context checked for DMA */ + int last_context; /* Last current context */ + unsigned long last_switch; /* jiffies at last context switch */ + struct tq_struct tq; + cycles_t ctx_start; + cycles_t lck_start; +#if DRM_DMA_HISTOGRAM + drm_histogram_t histo; +#endif + + /* Callback to X server for context switch + and for heavy-handed reset. */ + char buf[DRM_BSZ]; /* Output buffer */ + char *buf_rp; /* Read pointer */ + char *buf_wp; /* Write pointer */ + char *buf_end; /* End pointer */ + struct fasync_struct *buf_async;/* Processes waiting for SIGIO */ + wait_queue_head_t buf_readers; /* Processes waiting to read */ + wait_queue_head_t buf_writers; /* Processes waiting to ctx switch */ + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + drm_agp_head_t *agp; +#endif + unsigned long *ctx_bitmap; + void *dev_private; +} drm_device_t; + + + /* Internal function definitions */ + + /* Misc. support (init.c) */ +extern int drm_flags; +extern void drm_parse_options(char *s); +extern int drm_cpu_valid(void); + + + /* Device support (fops.c) */ +extern int drm_open_helper(struct inode *inode, struct file *filp, + drm_device_t *dev); +extern int drm_flush(struct file *filp); +extern int drm_release(struct inode *inode, struct file *filp); +extern int drm_fasync(int fd, struct file *filp, int on); +extern ssize_t drm_read(struct file *filp, char *buf, size_t count, + loff_t *off); +extern int drm_write_string(drm_device_t *dev, const char *s); +extern unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait); + + /* Mapping support (vm.c) */ +#if LINUX_VERSION_CODE < 0x020317 +extern unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +#else + /* Return type changed in 2.3.23 */ +extern struct page *drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern struct page *drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +extern struct page *drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access); +#endif +extern void drm_vm_open(struct vm_area_struct *vma); +extern void drm_vm_close(struct vm_area_struct *vma); +extern int drm_mmap_dma(struct file *filp, + struct vm_area_struct *vma); +extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); +extern struct vm_operations_struct drm_vm_ops; +extern struct vm_operations_struct drm_vm_shm_ops; +extern struct vm_operations_struct drm_vm_dma_ops; + + /* Proc support (proc.c) */ +extern int drm_proc_init(drm_device_t *dev); +extern int drm_proc_cleanup(void); + + /* Memory management support (memory.c) */ +extern void drm_mem_init(void); +extern int drm_mem_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +extern void *drm_alloc(size_t size, int area); +extern void *drm_realloc(void *oldpt, size_t oldsize, size_t size, + int area); +extern char *drm_strdup(const char *s, int area); +extern void drm_strfree(const char *s, int area); +extern void drm_free(void *pt, size_t size, int area); +extern unsigned long drm_alloc_pages(int order, int area); +extern void drm_free_pages(unsigned long address, int order, + int area); +extern void *drm_ioremap(unsigned long offset, unsigned long size); +extern void drm_ioremapfree(void *pt, unsigned long size); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +extern agp_memory *drm_alloc_agp(int pages, u32 type); +extern int drm_free_agp(agp_memory *handle, int pages); +extern int drm_bind_agp(agp_memory *handle, unsigned int start); +extern int drm_unbind_agp(agp_memory *handle); +#endif + + + /* Buffer management support (bufs.c) */ +extern int drm_order(unsigned long size); +extern int drm_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Buffer list management support (lists.c) */ +extern int drm_waitlist_create(drm_waitlist_t *bl, int count); +extern int drm_waitlist_destroy(drm_waitlist_t *bl); +extern int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf); +extern drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl); + +extern int drm_freelist_create(drm_freelist_t *bl, int count); +extern int drm_freelist_destroy(drm_freelist_t *bl); +extern int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, + drm_buf_t *buf); +extern drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block); + + /* DMA support (gen_dma.c) */ +extern void drm_dma_setup(drm_device_t *dev); +extern void drm_dma_takedown(drm_device_t *dev); +extern void drm_free_buffer(drm_device_t *dev, drm_buf_t *buf); +extern void drm_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int drm_context_switch(drm_device_t *dev, int old, int new); +extern int drm_context_switch_complete(drm_device_t *dev, int new); +extern void drm_clear_next_buffer(drm_device_t *dev); +extern int drm_select_queue(drm_device_t *dev, + void (*wrapper)(unsigned long)); +extern int drm_dma_enqueue(drm_device_t *dev, drm_dma_t *dma); +extern int drm_dma_get_buffers(drm_device_t *dev, drm_dma_t *dma); +#if DRM_DMA_HISTOGRAM +extern int drm_histogram_slot(unsigned long count); +extern void drm_histogram_compute(drm_device_t *dev, drm_buf_t *buf); +#endif + + + /* Misc. IOCTL support (ioctl.c) */ +extern int drm_irq_busid(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_setunique(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Context IOCTL support (context.c) */ +extern int drm_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Drawable IOCTL support (drawable.c) */ +extern int drm_adddraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_rmdraw(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Authentication IOCTL support (auth.c) */ +extern int drm_add_magic(drm_device_t *dev, drm_file_t *priv, + drm_magic_t magic); +extern int drm_remove_magic(drm_device_t *dev, drm_magic_t magic); +extern int drm_getmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_authmagic(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + + /* Locking IOCTL support (lock.c) */ +extern int drm_block(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_unblock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_lock_take(__volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_transfer(drm_device_t *dev, + __volatile__ unsigned int *lock, + unsigned int context); +extern int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, + unsigned int context); +extern int drm_finish(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_flush_unblock(drm_device_t *dev, int context, + drm_lock_flags_t flags); +extern int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags); + + /* Context Bitmap support (ctxbitmap.c) */ +extern int drm_ctxbitmap_init(drm_device_t *dev); +extern void drm_ctxbitmap_cleanup(drm_device_t *dev); +extern int drm_ctxbitmap_next(drm_device_t *dev); +extern void drm_ctxbitmap_free(drm_device_t *dev, int ctx_handle); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* AGP/GART support (agpsupport.c) */ +extern drm_agp_head_t *drm_agp_init(void); +extern void drm_agp_uninit(void); +extern int drm_agp_acquire(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_release(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_enable(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_info(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_alloc(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_free(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_unbind(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int drm_agp_bind(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +#endif +#endif +#endif diff --git a/drivers/char/drm/drm_syms.c b/drivers/char/drm/drm_syms.c new file mode 100644 index 000000000000..661ac48161aa --- /dev/null +++ b/drivers/char/drm/drm_syms.c @@ -0,0 +1,149 @@ +#include +#include "drmP.h" + +/* Misc. support (init.c) */ +EXPORT_SYMBOL(drm_flags); +EXPORT_SYMBOL(drm_parse_options); +EXPORT_SYMBOL(drm_cpu_valid); + +/* Device support (fops.c) */ +EXPORT_SYMBOL(drm_open_helper); +EXPORT_SYMBOL(drm_flush); +EXPORT_SYMBOL(drm_release); +EXPORT_SYMBOL(drm_fasync); +EXPORT_SYMBOL(drm_read); +EXPORT_SYMBOL(drm_write_string); +EXPORT_SYMBOL(drm_poll); + +/* Mapping support (vm.c) */ +#if LINUX_VERSION_CODE < 0x020317 +EXPORT_SYMBOL(drm_vm_nopage); +EXPORT_SYMBOL(drm_vm_shm_nopage); +EXPORT_SYMBOL(drm_vm_dma_nopage); +#else +/* Return type changed in 2.3.23 */ +EXPORT_SYMBOL(drm_vm_nopage); +EXPORT_SYMBOL(drm_vm_shm_nopage); +EXPORT_SYMBOL(drm_vm_dma_nopage); +#endif + +EXPORT_SYMBOL(drm_vm_open); +EXPORT_SYMBOL(drm_vm_close); +EXPORT_SYMBOL(drm_mmap_dma); +EXPORT_SYMBOL(drm_mmap); +EXPORT_SYMBOL(drm_vm_ops); +EXPORT_SYMBOL(drm_vm_shm_ops); +EXPORT_SYMBOL(drm_vm_dma_ops); + +/* Proc support (proc.c) */ +EXPORT_SYMBOL(drm_proc_init); +EXPORT_SYMBOL(drm_proc_cleanup); + +/* Memory management support (memory.c) */ +EXPORT_SYMBOL(drm_mem_init); +EXPORT_SYMBOL(drm_mem_info); +EXPORT_SYMBOL(drm_alloc); +EXPORT_SYMBOL(drm_realloc); +EXPORT_SYMBOL(drm_strdup); +EXPORT_SYMBOL(drm_strfree); +EXPORT_SYMBOL(drm_free); +EXPORT_SYMBOL(drm_alloc_pages); +EXPORT_SYMBOL(drm_free_pages); +EXPORT_SYMBOL(drm_ioremap); +EXPORT_SYMBOL(drm_ioremapfree); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +EXPORT_SYMBOL(drm_alloc_agp); +EXPORT_SYMBOL(drm_free_agp); +EXPORT_SYMBOL(drm_bind_agp); +EXPORT_SYMBOL(drm_unbind_agp); +#endif + +/* Buffer management support (bufs.c) */ +EXPORT_SYMBOL(drm_order); +EXPORT_SYMBOL(drm_addmap); +EXPORT_SYMBOL(drm_addbufs); +EXPORT_SYMBOL(drm_infobufs); +EXPORT_SYMBOL(drm_markbufs); +EXPORT_SYMBOL(drm_freebufs); +EXPORT_SYMBOL(drm_mapbufs); + +/* Buffer list management support (lists.c) */ +EXPORT_SYMBOL(drm_waitlist_create); +EXPORT_SYMBOL(drm_waitlist_destroy); +EXPORT_SYMBOL(drm_waitlist_put); +EXPORT_SYMBOL(drm_waitlist_get); +EXPORT_SYMBOL(drm_freelist_create); +EXPORT_SYMBOL(drm_freelist_destroy); +EXPORT_SYMBOL(drm_freelist_put); +EXPORT_SYMBOL(drm_freelist_get); + +/* DMA support (gen_dma.c) */ +EXPORT_SYMBOL(drm_dma_setup); +EXPORT_SYMBOL(drm_dma_takedown); +EXPORT_SYMBOL(drm_free_buffer); +EXPORT_SYMBOL(drm_reclaim_buffers); +EXPORT_SYMBOL(drm_context_switch); +EXPORT_SYMBOL(drm_context_switch_complete); +EXPORT_SYMBOL(drm_clear_next_buffer); +EXPORT_SYMBOL(drm_select_queue); +EXPORT_SYMBOL(drm_dma_enqueue); +EXPORT_SYMBOL(drm_dma_get_buffers); +#if DRM_DMA_HISTOGRAM +EXPORT_SYMBOL(drm_histogram_slot); +EXPORT_SYMBOL(drm_histogram_compute); +#endif + +/* Misc. IOCTL support (ioctl.c) */ +EXPORT_SYMBOL(drm_irq_busid); +EXPORT_SYMBOL(drm_getunique); +EXPORT_SYMBOL(drm_setunique); + +/* Context IOCTL support (context.c) */ +EXPORT_SYMBOL(drm_resctx); +EXPORT_SYMBOL(drm_addctx); +EXPORT_SYMBOL(drm_modctx); +EXPORT_SYMBOL(drm_getctx); +EXPORT_SYMBOL(drm_switchctx); +EXPORT_SYMBOL(drm_newctx); +EXPORT_SYMBOL(drm_rmctx); + +/* Drawable IOCTL support (drawable.c) */ +EXPORT_SYMBOL(drm_adddraw); +EXPORT_SYMBOL(drm_rmdraw); + +/* Authentication IOCTL support (auth.c) */ +EXPORT_SYMBOL(drm_add_magic); +EXPORT_SYMBOL(drm_remove_magic); +EXPORT_SYMBOL(drm_getmagic); +EXPORT_SYMBOL(drm_authmagic); + +/* Locking IOCTL support (lock.c) */ +EXPORT_SYMBOL(drm_block); +EXPORT_SYMBOL(drm_unblock); +EXPORT_SYMBOL(drm_lock_take); +EXPORT_SYMBOL(drm_lock_transfer); +EXPORT_SYMBOL(drm_lock_free); +EXPORT_SYMBOL(drm_finish); +EXPORT_SYMBOL(drm_flush_unblock); +EXPORT_SYMBOL(drm_flush_block_and_flush); + +/* Context Bitmap support (ctxbitmap.c) */ +EXPORT_SYMBOL(drm_ctxbitmap_init); +EXPORT_SYMBOL(drm_ctxbitmap_cleanup); +EXPORT_SYMBOL(drm_ctxbitmap_next); +EXPORT_SYMBOL(drm_ctxbitmap_free); + +/* AGP/GART support (agpsupport.c) */ +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +EXPORT_SYMBOL(drm_agp); +EXPORT_SYMBOL(drm_agp_init); +EXPORT_SYMBOL(drm_agp_uninit); +EXPORT_SYMBOL(drm_agp_acquire); +EXPORT_SYMBOL(drm_agp_release); +EXPORT_SYMBOL(drm_agp_enable); +EXPORT_SYMBOL(drm_agp_info); +EXPORT_SYMBOL(drm_agp_alloc); +EXPORT_SYMBOL(drm_agp_free); +EXPORT_SYMBOL(drm_agp_unbind); +EXPORT_SYMBOL(drm_agp_bind); +#endif diff --git a/drivers/char/drm/ffb_context.c b/drivers/char/drm/ffb_context.c new file mode 100644 index 000000000000..2edd550b12b4 --- /dev/null +++ b/drivers/char/drm/ffb_context.c @@ -0,0 +1,530 @@ +/* $Id: drm-2.4.0-test6-pre5.patch,v 1.1 2000/08/06 05:24:31 notting Exp $ + * ffb_context.c: Creator/Creator3D DRI/DRM context switching. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + * + * Almost entirely stolen from tdfx_context.c, see there + * for authors. + */ + +#include +#include + +#include "drmP.h" + +#include "ffb_drv.h" + +static int ffb_alloc_queue(drm_device_t *dev, int is_2d_only) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int i; + + for (i = 0; i < FFB_MAX_CTXS; i++) { + if (fpriv->hw_state[i] == NULL) + break; + } + if (i == FFB_MAX_CTXS) + return -1; + + fpriv->hw_state[i] = kmalloc(sizeof(struct ffb_hw_context), GFP_KERNEL); + if (fpriv->hw_state[i] == NULL) + return -1; + + fpriv->hw_state[i]->is_2d_only = is_2d_only; + + /* Plus one because 0 is the special DRM_KERNEL_CONTEXT. */ + return i + 1; +} + +static void ffb_save_context(ffb_dev_priv_t *fpriv, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + ctx->drawop = upa_readl(&ffb->drawop); + ctx->ppc = upa_readl(&ffb->ppc); + ctx->wid = upa_readl(&ffb->wid); + ctx->fg = upa_readl(&ffb->fg); + ctx->bg = upa_readl(&ffb->bg); + ctx->xclip = upa_readl(&ffb->xclip); + ctx->fbc = upa_readl(&ffb->fbc); + ctx->rop = upa_readl(&ffb->rop); + ctx->cmp = upa_readl(&ffb->cmp); + ctx->matchab = upa_readl(&ffb->matchab); + ctx->magnab = upa_readl(&ffb->magnab); + ctx->pmask = upa_readl(&ffb->pmask); + ctx->xpmask = upa_readl(&ffb->xpmask); + ctx->lpat = upa_readl(&ffb->lpat); + ctx->fontxy = upa_readl(&ffb->fontxy); + ctx->fontw = upa_readl(&ffb->fontw); + ctx->fontinc = upa_readl(&ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + ctx->ucsr = upa_readl(&ffb->ucsr); + return; + } + + /* Fetch drawop. */ + ctx->drawop = upa_readl(&ffb->drawop); + + /* If we were saving the vertex registers, this is where + * we would do it. We would save 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Capture rendering attributes. */ + + ctx->ppc = upa_readl(&ffb->ppc); /* Pixel Processor Control */ + ctx->wid = upa_readl(&ffb->wid); /* Current WID */ + ctx->fg = upa_readl(&ffb->fg); /* Constant FG color */ + ctx->bg = upa_readl(&ffb->bg); /* Constant BG color */ + ctx->consty = upa_readl(&ffb->consty); /* Constant Y */ + ctx->constz = upa_readl(&ffb->constz); /* Constant Z */ + ctx->xclip = upa_readl(&ffb->xclip); /* X plane clip */ + ctx->dcss = upa_readl(&ffb->dcss); /* Depth Cue Scale Slope */ + ctx->vclipmin = upa_readl(&ffb->vclipmin); /* Primary XY clip, minimum */ + ctx->vclipmax = upa_readl(&ffb->vclipmax); /* Primary XY clip, maximum */ + ctx->vclipzmin = upa_readl(&ffb->vclipzmin); /* Primary Z clip, minimum */ + ctx->vclipzmax = upa_readl(&ffb->vclipzmax); /* Primary Z clip, maximum */ + ctx->dcsf = upa_readl(&ffb->dcsf); /* Depth Cue Scale Front Bound */ + ctx->dcsb = upa_readl(&ffb->dcsb); /* Depth Cue Scale Back Bound */ + ctx->dczf = upa_readl(&ffb->dczf); /* Depth Cue Scale Z Front */ + ctx->dczb = upa_readl(&ffb->dczb); /* Depth Cue Scale Z Back */ + ctx->blendc = upa_readl(&ffb->blendc); /* Alpha Blend Control */ + ctx->blendc1 = upa_readl(&ffb->blendc1); /* Alpha Blend Color 1 */ + ctx->blendc2 = upa_readl(&ffb->blendc2); /* Alpha Blend Color 2 */ + ctx->fbc = upa_readl(&ffb->fbc); /* Frame Buffer Control */ + ctx->rop = upa_readl(&ffb->rop); /* Raster Operation */ + ctx->cmp = upa_readl(&ffb->cmp); /* Compare Controls */ + ctx->matchab = upa_readl(&ffb->matchab); /* Buffer A/B Match Ops */ + ctx->matchc = upa_readl(&ffb->matchc); /* Buffer C Match Ops */ + ctx->magnab = upa_readl(&ffb->magnab); /* Buffer A/B Magnitude Ops */ + ctx->magnc = upa_readl(&ffb->magnc); /* Buffer C Magnitude Ops */ + ctx->pmask = upa_readl(&ffb->pmask); /* RGB Plane Mask */ + ctx->xpmask = upa_readl(&ffb->xpmask); /* X Plane Mask */ + ctx->ypmask = upa_readl(&ffb->ypmask); /* Y Plane Mask */ + ctx->zpmask = upa_readl(&ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + ctx->auxclip0min = upa_readl(&ffb->auxclip[0].min); + ctx->auxclip0max = upa_readl(&ffb->auxclip[0].max); + ctx->auxclip1min = upa_readl(&ffb->auxclip[1].min); + ctx->auxclip1max = upa_readl(&ffb->auxclip[1].max); + ctx->auxclip2min = upa_readl(&ffb->auxclip[2].min); + ctx->auxclip2max = upa_readl(&ffb->auxclip[2].max); + ctx->auxclip3min = upa_readl(&ffb->auxclip[3].min); + ctx->auxclip3max = upa_readl(&ffb->auxclip[3].max); + + ctx->lpat = upa_readl(&ffb->lpat); /* Line Pattern */ + ctx->fontxy = upa_readl(&ffb->fontxy); /* XY Font Coordinate */ + ctx->fontw = upa_readl(&ffb->fontw); /* Font Width */ + ctx->fontinc = upa_readl(&ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + ctx->dcss1 = upa_readl(&ffb->dcss1); /* Depth Cue Scale Slope 1 */ + ctx->dcss2 = upa_readl(&ffb->dcss2); /* Depth Cue Scale Slope 2 */ + ctx->dcss2 = upa_readl(&ffb->dcss3); /* Depth Cue Scale Slope 3 */ + ctx->dcs2 = upa_readl(&ffb->dcs2); /* Depth Cue Scale 2 */ + ctx->dcs3 = upa_readl(&ffb->dcs3); /* Depth Cue Scale 3 */ + ctx->dcs4 = upa_readl(&ffb->dcs4); /* Depth Cue Scale 4 */ + ctx->dcd2 = upa_readl(&ffb->dcd2); /* Depth Cue Depth 2 */ + ctx->dcd3 = upa_readl(&ffb->dcd3); /* Depth Cue Depth 3 */ + ctx->dcd4 = upa_readl(&ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + ctx->stencil = upa_readl(&ffb->stencil); + ctx->stencilctl = upa_readl(&ffb->stencilctl); + } + } + + /* Save the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + ctx->area_pattern[i] = upa_readl(&ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. */ + ctx->ucsr = upa_readl(&ffb->ucsr); +} + +static void ffb_restore_context(ffb_dev_priv_t *fpriv, int old, int idx) +{ + ffb_fbcPtr ffb = fpriv->regs; + struct ffb_hw_context *ctx; + int i; + + ctx = fpriv->hw_state[idx - 1]; + if (idx == 0 || ctx == NULL) + return; + + if (ctx->is_2d_only) { + /* 2D applications only care about certain pieces + * of state. + */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + upa_writel(ctx->ppc, &ffb->ppc); + upa_writel(ctx->wid, &ffb->wid); + upa_writel(ctx->fg, &ffb->fg); + upa_writel(ctx->bg, &ffb->bg); + upa_writel(ctx->xclip, &ffb->xclip); + upa_writel(ctx->fbc, &ffb->fbc); + upa_writel(ctx->rop, &ffb->rop); + upa_writel(ctx->cmp, &ffb->cmp); + upa_writel(ctx->matchab, &ffb->matchab); + upa_writel(ctx->magnab, &ffb->magnab); + upa_writel(ctx->pmask, &ffb->pmask); + upa_writel(ctx->xpmask, &ffb->xpmask); + upa_writel(ctx->lpat, &ffb->lpat); + upa_writel(ctx->fontxy, &ffb->fontxy); + upa_writel(ctx->fontw, &ffb->fontw); + upa_writel(ctx->fontinc, &ffb->fontinc); + + /* stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + upa_writel(ctx->fbc, &ffb->fbc); + } + + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); + return; + } + + /* Restore drawop. */ + upa_writel(ctx->drawop, &ffb->drawop); + + /* If we were restoring the vertex registers, this is where + * we would do it. We would restore 32 32-bit words starting + * at ffb->suvtx. + */ + + /* Restore rendering attributes. */ + + upa_writel(ctx->ppc, &ffb->ppc); /* Pixel Processor Control */ + upa_writel(ctx->wid, &ffb->wid); /* Current WID */ + upa_writel(ctx->fg, &ffb->fg); /* Constant FG color */ + upa_writel(ctx->bg, &ffb->bg); /* Constant BG color */ + upa_writel(ctx->consty, &ffb->consty); /* Constant Y */ + upa_writel(ctx->constz, &ffb->constz); /* Constant Z */ + upa_writel(ctx->xclip, &ffb->xclip); /* X plane clip */ + upa_writel(ctx->dcss, &ffb->dcss); /* Depth Cue Scale Slope */ + upa_writel(ctx->vclipmin, &ffb->vclipmin); /* Primary XY clip, minimum */ + upa_writel(ctx->vclipmax, &ffb->vclipmax); /* Primary XY clip, maximum */ + upa_writel(ctx->vclipzmin, &ffb->vclipzmin); /* Primary Z clip, minimum */ + upa_writel(ctx->vclipzmax, &ffb->vclipzmax); /* Primary Z clip, maximum */ + upa_writel(ctx->dcsf, &ffb->dcsf); /* Depth Cue Scale Front Bound */ + upa_writel(ctx->dcsb, &ffb->dcsb); /* Depth Cue Scale Back Bound */ + upa_writel(ctx->dczf, &ffb->dczf); /* Depth Cue Scale Z Front */ + upa_writel(ctx->dczb, &ffb->dczb); /* Depth Cue Scale Z Back */ + upa_writel(ctx->blendc, &ffb->blendc); /* Alpha Blend Control */ + upa_writel(ctx->blendc1, &ffb->blendc1); /* Alpha Blend Color 1 */ + upa_writel(ctx->blendc2, &ffb->blendc2); /* Alpha Blend Color 2 */ + upa_writel(ctx->fbc, &ffb->fbc); /* Frame Buffer Control */ + upa_writel(ctx->rop, &ffb->rop); /* Raster Operation */ + upa_writel(ctx->cmp, &ffb->cmp); /* Compare Controls */ + upa_writel(ctx->matchab, &ffb->matchab); /* Buffer A/B Match Ops */ + upa_writel(ctx->matchc, &ffb->matchc); /* Buffer C Match Ops */ + upa_writel(ctx->magnab, &ffb->magnab); /* Buffer A/B Magnitude Ops */ + upa_writel(ctx->magnc, &ffb->magnc); /* Buffer C Magnitude Ops */ + upa_writel(ctx->pmask, &ffb->pmask); /* RGB Plane Mask */ + upa_writel(ctx->xpmask, &ffb->xpmask); /* X Plane Mask */ + upa_writel(ctx->ypmask, &ffb->ypmask); /* Y Plane Mask */ + upa_writel(ctx->zpmask, &ffb->zpmask); /* Z Plane Mask */ + + /* Auxiliary Clips. */ + upa_writel(ctx->auxclip0min, &ffb->auxclip[0].min); + upa_writel(ctx->auxclip0max, &ffb->auxclip[0].max); + upa_writel(ctx->auxclip1min, &ffb->auxclip[1].min); + upa_writel(ctx->auxclip1max, &ffb->auxclip[1].max); + upa_writel(ctx->auxclip2min, &ffb->auxclip[2].min); + upa_writel(ctx->auxclip2max, &ffb->auxclip[2].max); + upa_writel(ctx->auxclip3min, &ffb->auxclip[3].min); + upa_writel(ctx->auxclip3max, &ffb->auxclip[3].max); + + upa_writel(ctx->lpat, &ffb->lpat); /* Line Pattern */ + upa_writel(ctx->fontxy, &ffb->fontxy); /* XY Font Coordinate */ + upa_writel(ctx->fontw, &ffb->fontw); /* Font Width */ + upa_writel(ctx->fontinc, &ffb->fontinc); /* Font X/Y Increment */ + + /* These registers/features only exist on FFB2 and later chips. */ + if (fpriv->ffb_type >= ffb2_prototype) { + upa_writel(ctx->dcss1, &ffb->dcss1); /* Depth Cue Scale Slope 1 */ + upa_writel(ctx->dcss2, &ffb->dcss2); /* Depth Cue Scale Slope 2 */ + upa_writel(ctx->dcss3, &ffb->dcss2); /* Depth Cue Scale Slope 3 */ + upa_writel(ctx->dcs2, &ffb->dcs2); /* Depth Cue Scale 2 */ + upa_writel(ctx->dcs3, &ffb->dcs3); /* Depth Cue Scale 3 */ + upa_writel(ctx->dcs4, &ffb->dcs4); /* Depth Cue Scale 4 */ + upa_writel(ctx->dcd2, &ffb->dcd2); /* Depth Cue Depth 2 */ + upa_writel(ctx->dcd3, &ffb->dcd3); /* Depth Cue Depth 3 */ + upa_writel(ctx->dcd4, &ffb->dcd4); /* Depth Cue Depth 4 */ + + /* And stencil/stencilctl only exists on FFB2+ and later + * due to the introduction of 3DRAM-III. + */ + if (fpriv->ffb_type == ffb2_vertical_plus || + fpriv->ffb_type == ffb2_horizontal_plus) { + /* Unfortunately, there is a hardware bug on + * the FFB2+ chips which prevents a normal write + * to the stencil control register from working + * as it should. + * + * The state controlled by the FFB stencilctl register + * really gets transferred to the per-buffer instances + * of the stencilctl register in the 3DRAM chips. + * + * The bug is that FFB does not update buffer C correctly, + * so we have to do it by hand for them. + */ + + /* This will update buffers A and B. */ + upa_writel(ctx->stencil, &ffb->stencil); + upa_writel(ctx->stencilctl, &ffb->stencilctl); + + /* Force FFB to use buffer C 3dram regs. */ + upa_writel(0x80000000, &ffb->fbc); + upa_writel((ctx->stencilctl | 0x80000), + &ffb->rawstencilctl); + + /* Now restore the correct FBC controls. */ + upa_writel(ctx->fbc, &ffb->fbc); + } + } + + /* Restore the 32x32 area pattern. */ + for (i = 0; i < 32; i++) + upa_writel(ctx->area_pattern[i], &ffb->pattern[i]); + + /* Finally, stash away the User Constol/Status Register. + * The only state we really preserve here is the picking + * control. + */ + upa_writel((ctx->ucsr & 0xf0000), &ffb->ucsr); +} + +#define FFB_UCSR_FB_BUSY 0x01000000 +#define FFB_UCSR_RP_BUSY 0x02000000 +#define FFB_UCSR_ALL_BUSY (FFB_UCSR_RP_BUSY|FFB_UCSR_FB_BUSY) + +static void FFBWait(ffb_fbcPtr ffb) +{ + int limit = 100000; + + do { + u32 regval = upa_readl(&ffb->ucsr); + + if ((regval & FFB_UCSR_ALL_BUSY) == 0) + break; + } while (--limit); +} + +int ffb_context_switch(drm_device_t *dev, int old, int new) +{ + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + + atomic_inc(&dev->total_ctx); + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context || + dev->last_context == 0) { + dev->last_context = new; + return 0; + } + + FFBWait(fpriv->regs); + ffb_save_context(fpriv, old); + ffb_restore_context(fpriv, old, new); + FFBWait(fpriv->regs); + + dev->last_context = new; + + return 0; +} + +int ffb_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int ffb_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + idx = ffb_alloc_queue(dev, (ctx.flags & _DRM_CONTEXT_2DONLY)); + if (idx < 0) + return -ENFILE; + + DRM_DEBUG("%d\n", ctx.handle); + ctx.handle = idx; + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int ffb_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if ((ctx.flags & _DRM_CONTEXT_2DONLY) == 0) + hwctx->is_2d_only = 0; + else + hwctx->is_2d_only = 1; + + return 0; +} + +int ffb_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + struct ffb_hw_context *hwctx; + drm_ctx_t ctx; + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + + idx = ctx.handle; + if (idx <= 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + hwctx = fpriv->hw_state[idx - 1]; + if (hwctx == NULL) + return -EINVAL; + + if (hwctx->is_2d_only != 0) + ctx.flags = _DRM_CONTEXT_2DONLY; + else + ctx.flags = 0; + + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + + return 0; +} + +int ffb_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return ffb_context_switch(dev, dev->last_context, ctx.handle); +} + +int ffb_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + return 0; +} + +int ffb_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int idx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + + idx = ctx.handle - 1; + if (idx < 0 || idx >= FFB_MAX_CTXS) + return -EINVAL; + + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + return 0; +} diff --git a/drivers/char/drm/ffb_drv.c b/drivers/char/drm/ffb_drv.c new file mode 100644 index 000000000000..109db988b042 --- /dev/null +++ b/drivers/char/drm/ffb_drv.c @@ -0,0 +1,868 @@ +/* $Id: drm-2.4.0-test6-pre5.patch,v 1.1 2000/08/06 05:24:31 notting Exp $ + * ffb_drv.c: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +#include "drmP.h" + +#include +#include +#include +#include + +#include "ffb_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#define FFB_NAME "ffb" +#define FFB_DESC "Creator/Creator3D" +#define FFB_DATE "20000517" +#define FFB_MAJOR 0 +#define FFB_MINOR 0 +#define FFB_PATCHLEVEL 1 + +/* Forward declarations. */ +int ffb_init(void); +void ffb_cleanup(void); +static int ffb_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_open(struct inode *inode, struct file *filp); +static int ffb_release(struct inode *inode, struct file *filp); +static int ffb_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma); + +/* From ffb_context.c */ +extern int ffb_resctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_addctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_modctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_getctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_switchctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_newctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_rmctx(struct inode *, struct file *, unsigned int, unsigned long); +extern int ffb_context_switch(drm_device_t *, int, int); + +static struct file_operations ffb_fops = { + owner: THIS_MODULE, + open: ffb_open, + flush: drm_flush, + release: ffb_release, + ioctl: ffb_ioctl, + mmap: ffb_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +/* This is just a template, we make a new copy for each FFB + * we discover at init time so that each one gets a unique + * misc device minor number. + */ +static struct miscdevice ffb_misc = { + minor: MISC_DYNAMIC_MINOR, + name: FFB_NAME, + fops: &ffb_fops, +}; + +static drm_ioctl_desc_t ffb_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { ffb_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, /* XXX */ + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + + /* The implementation is currently a nop just like on tdfx. + * Later we can do something more clever. -DaveM + */ + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { ffb_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { ffb_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { ffb_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { ffb_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { ffb_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { ffb_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { ffb_resctx, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { ffb_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { ffb_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define FFB_IOCTL_COUNT DRM_ARRAY_SIZE(ffb_ioctls) + +#ifdef MODULE +static char *ffb = NULL; +#endif + +MODULE_AUTHOR("David S. Miller (davem@redhat.com)"); +MODULE_DESCRIPTION("Sun Creator/Creator3D DRI"); + +static int ffb_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: + drm_ioremapfree(map->handle, map->size); + break; + + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + + default: + break; + }; + + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +drm_device_t **ffb_dev_table; +static int ffb_dev_table_size; + +static void get_ffb_type(ffb_dev_priv_t *ffb_priv, int instance) +{ + volatile unsigned char *strap_bits; + unsigned char val; + + strap_bits = (volatile unsigned char *) + (ffb_priv->card_phys_base + 0x00200000UL); + + /* Don't ask, you have to read the value twice for whatever + * reason to get correct contents. + */ + val = upa_readb(strap_bits); + val = upa_readb(strap_bits); + switch (val & 0x78) { + case (0x0 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb1_prototype; + printk("ffb%d: Detected FFB1 pre-FCS prototype\n", instance); + break; + case (0x0 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb1_standard; + printk("ffb%d: Detected FFB1\n", instance); + break; + case (0x0 << 5) | (0x3 << 3): + ffb_priv->ffb_type = ffb1_speedsort; + printk("ffb%d: Detected FFB1-SpeedSort\n", instance); + break; + case (0x1 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_prototype; + printk("ffb%d: Detected FFB2/vertical pre-FCS prototype\n", instance); + break; + case (0x1 << 5) | (0x1 << 3): + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Detected FFB2/vertical\n", instance); + break; + case (0x1 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_vertical_plus; + printk("ffb%d: Detected FFB2+/vertical\n", instance); + break; + case (0x2 << 5) | (0x0 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2/horizontal\n", instance); + break; + case (0x2 << 5) | (0x2 << 3): + ffb_priv->ffb_type = ffb2_horizontal; + printk("ffb%d: Detected FFB2+/horizontal\n", instance); + break; + default: + ffb_priv->ffb_type = ffb2_vertical; + printk("ffb%d: Unknown boardID[%08x], assuming FFB2\n", instance, val); + break; + }; +} + +static int ffb_init_one(int prom_node, int instance) +{ + struct linux_prom64_registers regs[2*PROMREG_MAX]; + drm_device_t *dev; + ffb_dev_priv_t *ffb_priv; + int ret, i; + + dev = kmalloc(sizeof(drm_device_t) + sizeof(ffb_dev_priv_t), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset(dev, 0, sizeof(*dev)); + spin_lock_init(&dev->count_lock); + sema_init(&dev->struct_sem, 1); + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + ffb_priv->prom_node = prom_node; + if (prom_getproperty(ffb_priv->prom_node, "reg", + (void *)regs, sizeof(regs)) <= 0) { + kfree(dev); + return -EINVAL; + } + ffb_priv->card_phys_base = regs[0].phys_addr; + ffb_priv->regs = (ffb_fbcPtr) + (regs[0].phys_addr + 0x00600000UL); + get_ffb_type(ffb_priv, instance); + for (i = 0; i < FFB_MAX_CTXS; i++) + ffb_priv->hw_state[i] = NULL; + + ffb_dev_table[instance] = dev; + +#ifdef MODULE + drm_parse_options(ffb); +#endif + + memcpy(&ffb_priv->miscdev, &ffb_misc, sizeof(ffb_misc)); + ret = misc_register(&ffb_priv->miscdev); + if (ret) { + ffb_dev_table[instance] = NULL; + kfree(dev); + return ret; + } + + dev->device = MKDEV(MISC_MAJOR, ffb_priv->miscdev.minor); + dev->name = FFB_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d at %016lx\n", + FFB_NAME, + FFB_MAJOR, + FFB_MINOR, + FFB_PATCHLEVEL, + FFB_DATE, + ffb_priv->miscdev.minor, + ffb_priv->card_phys_base); + + return 0; +} + +static int ffb_init_dev_table(void) +{ + int root, node; + int total = 0; + + root = prom_getchild(prom_root_node); + for (node = prom_searchsiblings(root, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) + total++; + + ffb_dev_table = kmalloc(sizeof(drm_device_t *) * total, GFP_KERNEL); + if (!ffb_dev_table) + return -ENOMEM; + + ffb_dev_table_size = total; + + return 0; +} + +int ffb_init(void) +{ + int root, node, instance, ret; + + ret = ffb_init_dev_table(); + if (ret) + return ret; + + instance = 0; + root = prom_getchild(prom_root_node); + for (node = prom_searchsiblings(root, "SUNW,ffb"); node; + node = prom_searchsiblings(prom_getsibling(node), "SUNW,ffb")) { + ret = ffb_init_one(node, instance); + if (ret) + return ret; + instance++; + } + + return 0; +} + +void ffb_cleanup(void) +{ + int instance; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + for (instance = 0; instance < ffb_dev_table_size; instance++) { + drm_device_t *dev = ffb_dev_table[instance]; + ffb_dev_priv_t *ffb_priv; + + if (!dev) + continue; + + ffb_priv = (ffb_dev_priv_t *) (dev + 1); + if (misc_deregister(&ffb_priv->miscdev)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + ffb_takedown(dev); + kfree(dev); + ffb_dev_table[instance] = NULL; + } + kfree(ffb_dev_table); + ffb_dev_table = NULL; + ffb_dev_table_size = 0; +} + +static int ffb_version(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_version_t version; + int len, ret; + + ret = copy_from_user(&version, (drm_version_t *)arg, sizeof(version)); + if (ret) + return -EFAULT; + + version.version_major = FFB_MAJOR; + version.version_minor = FFB_MINOR; + version.version_patchlevel = FFB_PATCHLEVEL; + + len = strlen(FFB_NAME); + if (len > version.name_len) + len = version.name_len; + version.name_len = len; + if (len && version.name) { + ret = copy_to_user(version.name, FFB_NAME, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DATE); + if (len > version.date_len) + len = version.date_len; + version.date_len = len; + if (len && version.date) { + ret = copy_to_user(version.date, FFB_DATE, len); + if (ret) + return -EFAULT; + } + + len = strlen(FFB_DESC); + if (len > version.desc_len) + len = version.desc_len; + version.desc_len = len; + if (len && version.desc) { + ret = copy_to_user(version.desc, FFB_DESC, len); + if (ret) + return -EFAULT; + } + + ret = copy_to_user((drm_version_t *) arg, &version, sizeof(version)); + if (ret) + ret = -EFAULT; + + return ret; +} + +static int ffb_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + return 0; +} + +static int ffb_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev; + int minor, i; + int ret = 0; + + minor = MINOR(inode->i_rdev); + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_dev_priv_t *ffb_priv; + + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + + if (ffb_priv->miscdev.minor == minor) + break; + } + + if (i >= ffb_dev_table_size) + return -EINVAL; + + dev = ffb_dev_table[i]; + if (!dev) + return -EINVAL; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + ret = drm_open_helper(inode, filp, dev); + if (!ret) { + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return ffb_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return ret; +} + +static int ffb_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int ret = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (dev->lock.hw_lock != NULL + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + ffb_dev_priv_t *fpriv = (ffb_dev_priv_t *) (dev + 1); + int context = _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock); + int idx; + + /* We have to free up the rogue hw context state + * holding error or else we will leak it. + */ + idx = context - 1; + if (fpriv->hw_state[idx] != NULL) { + kfree(fpriv->hw_state[idx]); + fpriv->hw_state[idx] = NULL; + } + } + + ret = drm_release(inode, filp); + + if (!ret) { + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + ret = ffb_takedown(dev); + unlock_kernel(); + return ret; + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return ret; +} + +static int ffb_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + int ret; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= FFB_IOCTL_COUNT) { + ret = -EINVAL; + } else { + ioctl = &ffb_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + ret = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + ret = -EACCES; + } else { + ret = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + + return ret; +} + +static int ffb_lock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + + if (!ret && + (dev->last_context != lock.context)) + ffb_context_switch(dev, dev->last_context, lock.context); + + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + + return ret; +} + +int ffb_unlock(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + unsigned int old, new, prev, ctx; + int ret; + + ret = copy_from_user(&lock, (drm_lock_t *)arg, sizeof(lock)); + if (ret) + return -EFAULT; + + if ((ctx = lock.context) == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + + /* We no longer really hold it, but if we are the next + * agent to request it then we should just be able to + * take it immediately and not eat the ioctl. + */ + dev->lock.pid = 0; + { + __volatile__ unsigned int *plock = &dev->lock.hw_lock->lock; + + do { + old = *plock; + new = ctx; + prev = cmpxchg(plock, old, new); + } while (prev != old); + } + + wake_up_interruptible(&dev->lock.lock_queue); + + return 0; +} + +static void align_fb_mapping(struct vm_area_struct *vma) +{ + unsigned long j, alignment; + + j = vma->vm_end - vma->vm_start; + for (alignment = (4 * 1024 * 1024); alignment > PAGE_SIZE; alignment >>= 3) + if (j >= alignment) + break; + if (alignment > PAGE_SIZE) { + j = alignment; + alignment = j - (vma->vm_start & (j - 1)); + if (alignment != j) { + struct vm_area_struct *vmm = find_vma(current->mm,vma->vm_start); + + if (!vmm || vmm->vm_start >= vma->vm_end + alignment) { + vma->vm_start += alignment; + vma->vm_end += alignment; + } + } + } +} + +/* The problem here is, due to virtual cache aliasing, + * we must make sure the shared memory area lands in the + * same dcache line for both the kernel and all drm clients. + */ +static void align_shm_mapping(struct vm_area_struct *vma, unsigned long kvirt) +{ + kvirt &= PAGE_SIZE; + if ((vma->vm_start & PAGE_SIZE) != kvirt) { + struct vm_area_struct *vmm = find_vma(current->mm, vma->vm_start); + + if (!vmm || vmm->vm_start >= vma->vm_end + PAGE_SIZE) { + vma->vm_start += PAGE_SIZE; + vma->vm_end += PAGE_SIZE; + } + } +} + +extern struct vm_operations_struct drm_vm_ops; +extern struct vm_operations_struct drm_vm_shm_ops; + +static int ffb_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + ffb_dev_priv_t *ffb_priv; + int i, minor; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + lock_kernel(); + minor = MINOR(filp->f_dentry->d_inode->i_rdev); + ffb_priv = NULL; + for (i = 0; i < ffb_dev_table_size; i++) { + ffb_priv = (ffb_dev_priv_t *) (ffb_dev_table[i] + 1); + if (ffb_priv->miscdev.minor == minor) + break; + } + if (i >= ffb_dev_table_size) { + unlock_kernel(); + return -EINVAL; + } + /* We don't support/need dma mappings, so... */ + if (!VM_OFFSET(vma)) { + unlock_kernel(); + return -EINVAL; + } + for (i = 0; i < dev->map_count; i++) { + unsigned long off; + + map = dev->maplist[i]; + + /* Ok, a little hack to make 32-bit apps work. */ + off = (map->offset & 0xffffffff); + if (off == VM_OFFSET(vma)) + break; + } + + if (i >= dev->map_count) { + unlock_kernel(); + return -EINVAL; + } + if (!map || + ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) { + unlock_kernel(); + return -EPERM; + } + if (map->size != (vma->vm_end - vma->vm_start)) { + unlock_kernel(); + return -EINVAL; + } + /* Set read-only attribute before mappings are created + * so it works for fb/reg maps too. + */ + if (map->flags & _DRM_READ_ONLY) + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); + + switch (map->type) { + case _DRM_FRAME_BUFFER: + align_fb_mapping(vma); + /* FALLTHROUGH */ + + case _DRM_REGISTERS: + /* In order to handle 32-bit drm apps/xserver we + * play a trick. The mappings only really specify + * the 32-bit offset from the cards 64-bit base + * address, and we just add in the base here. + */ + vma->vm_flags |= VM_IO; + if (io_remap_page_range(vma->vm_start, + ffb_priv->card_phys_base + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot, 0)) { + unlock_kernel(); + return -EAGAIN; + } + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + align_shm_mapping(vma, (unsigned long)dev->lock.hw_lock); + vma->vm_ops = &drm_vm_shm_ops; + + /* Don't let this area swap. Change when + * DRM_KERNEL advisory is supported. + */ + vma->vm_flags |= VM_LOCKED; + break; + default: + unlock_kernel(); + return -EINVAL; /* This should never happen. */ + }; + unlock_kernel(); + + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +module_init(ffb_init); +module_exit(ffb_cleanup); diff --git a/drivers/char/drm/ffb_drv.h b/drivers/char/drm/ffb_drv.h new file mode 100644 index 000000000000..d1623e9ed5ae --- /dev/null +++ b/drivers/char/drm/ffb_drv.h @@ -0,0 +1,276 @@ +/* $Id: drm-2.4.0-test6-pre5.patch,v 1.1 2000/08/06 05:24:31 notting Exp $ + * ffb_drv.h: Creator/Creator3D direct rendering driver. + * + * Copyright (C) 2000 David S. Miller (davem@redhat.com) + */ + +/* Auxilliary clips. */ +typedef struct { + volatile unsigned int min; + volatile unsigned int max; +} ffb_auxclip, *ffb_auxclipPtr; + +/* FFB register set. */ +typedef struct _ffb_fbc { + /* Next vertex registers, on the right we list which drawops + * use said register and the logical name the register has in + * that context. + */ /* DESCRIPTION DRAWOP(NAME) */ +/*0x00*/unsigned int pad1[3]; /* Reserved */ +/*0x0c*/volatile unsigned int alpha; /* ALPHA Transparency */ +/*0x10*/volatile unsigned int red; /* RED */ +/*0x14*/volatile unsigned int green; /* GREEN */ +/*0x18*/volatile unsigned int blue; /* BLUE */ +/*0x1c*/volatile unsigned int z; /* DEPTH */ +/*0x20*/volatile unsigned int y; /* Y triangle(DOYF) */ + /* aadot(DYF) */ + /* ddline(DYF) */ + /* aaline(DYF) */ +/*0x24*/volatile unsigned int x; /* X triangle(DOXF) */ + /* aadot(DXF) */ + /* ddline(DXF) */ + /* aaline(DXF) */ +/*0x28*/unsigned int pad2[2]; /* Reserved */ +/*0x30*/volatile unsigned int ryf; /* Y (alias to DOYF) ddline(RYF) */ + /* aaline(RYF) */ + /* triangle(RYF) */ +/*0x34*/volatile unsigned int rxf; /* X ddline(RXF) */ + /* aaline(RXF) */ + /* triangle(RXF) */ +/*0x38*/unsigned int pad3[2]; /* Reserved */ +/*0x40*/volatile unsigned int dmyf; /* Y (alias to DOYF) triangle(DMYF) */ +/*0x44*/volatile unsigned int dmxf; /* X triangle(DMXF) */ +/*0x48*/unsigned int pad4[2]; /* Reserved */ +/*0x50*/volatile unsigned int ebyi; /* Y (alias to RYI) polygon(EBYI) */ +/*0x54*/volatile unsigned int ebxi; /* X polygon(EBXI) */ +/*0x58*/unsigned int pad5[2]; /* Reserved */ +/*0x60*/volatile unsigned int by; /* Y brline(RYI) */ + /* fastfill(OP) */ + /* polygon(YI) */ + /* rectangle(YI) */ + /* bcopy(SRCY) */ + /* vscroll(SRCY) */ +/*0x64*/volatile unsigned int bx; /* X brline(RXI) */ + /* polygon(XI) */ + /* rectangle(XI) */ + /* bcopy(SRCX) */ + /* vscroll(SRCX) */ + /* fastfill(GO) */ +/*0x68*/volatile unsigned int dy; /* destination Y fastfill(DSTY) */ + /* bcopy(DSRY) */ + /* vscroll(DSRY) */ +/*0x6c*/volatile unsigned int dx; /* destination X fastfill(DSTX) */ + /* bcopy(DSTX) */ + /* vscroll(DSTX) */ +/*0x70*/volatile unsigned int bh; /* Y (alias to RYI) brline(DYI) */ + /* dot(DYI) */ + /* polygon(ETYI) */ + /* Height fastfill(H) */ + /* bcopy(H) */ + /* vscroll(H) */ + /* Y count fastfill(NY) */ +/*0x74*/volatile unsigned int bw; /* X dot(DXI) */ + /* brline(DXI) */ + /* polygon(ETXI) */ + /* fastfill(W) */ + /* bcopy(W) */ + /* vscroll(W) */ + /* fastfill(NX) */ +/*0x78*/unsigned int pad6[2]; /* Reserved */ +/*0x80*/unsigned int pad7[32]; /* Reserved */ + + /* Setup Unit's vertex state register */ +/*100*/ volatile unsigned int suvtx; +/*104*/ unsigned int pad8[63]; /* Reserved */ + + /* Frame Buffer Control Registers */ +/*200*/ volatile unsigned int ppc; /* Pixel Processor Control */ +/*204*/ volatile unsigned int wid; /* Current WID */ +/*208*/ volatile unsigned int fg; /* FG data */ +/*20c*/ volatile unsigned int bg; /* BG data */ +/*210*/ volatile unsigned int consty; /* Constant Y */ +/*214*/ volatile unsigned int constz; /* Constant Z */ +/*218*/ volatile unsigned int xclip; /* X Clip */ +/*21c*/ volatile unsigned int dcss; /* Depth Cue Scale Slope */ +/*220*/ volatile unsigned int vclipmin; /* Viewclip XY Min Bounds */ +/*224*/ volatile unsigned int vclipmax; /* Viewclip XY Max Bounds */ +/*228*/ volatile unsigned int vclipzmin; /* Viewclip Z Min Bounds */ +/*22c*/ volatile unsigned int vclipzmax; /* Viewclip Z Max Bounds */ +/*230*/ volatile unsigned int dcsf; /* Depth Cue Scale Front Bound */ +/*234*/ volatile unsigned int dcsb; /* Depth Cue Scale Back Bound */ +/*238*/ volatile unsigned int dczf; /* Depth Cue Z Front */ +/*23c*/ volatile unsigned int dczb; /* Depth Cue Z Back */ +/*240*/ unsigned int pad9; /* Reserved */ +/*244*/ volatile unsigned int blendc; /* Alpha Blend Control */ +/*248*/ volatile unsigned int blendc1; /* Alpha Blend Color 1 */ +/*24c*/ volatile unsigned int blendc2; /* Alpha Blend Color 2 */ +/*250*/ volatile unsigned int fbramitc; /* FB RAM Interleave Test Control */ +/*254*/ volatile unsigned int fbc; /* Frame Buffer Control */ +/*258*/ volatile unsigned int rop; /* Raster OPeration */ +/*25c*/ volatile unsigned int cmp; /* Frame Buffer Compare */ +/*260*/ volatile unsigned int matchab; /* Buffer AB Match Mask */ +/*264*/ volatile unsigned int matchc; /* Buffer C(YZ) Match Mask */ +/*268*/ volatile unsigned int magnab; /* Buffer AB Magnitude Mask */ +/*26c*/ volatile unsigned int magnc; /* Buffer C(YZ) Magnitude Mask */ +/*270*/ volatile unsigned int fbcfg0; /* Frame Buffer Config 0 */ +/*274*/ volatile unsigned int fbcfg1; /* Frame Buffer Config 1 */ +/*278*/ volatile unsigned int fbcfg2; /* Frame Buffer Config 2 */ +/*27c*/ volatile unsigned int fbcfg3; /* Frame Buffer Config 3 */ +/*280*/ volatile unsigned int ppcfg; /* Pixel Processor Config */ +/*284*/ volatile unsigned int pick; /* Picking Control */ +/*288*/ volatile unsigned int fillmode; /* FillMode */ +/*28c*/ volatile unsigned int fbramwac; /* FB RAM Write Address Control */ +/*290*/ volatile unsigned int pmask; /* RGB PlaneMask */ +/*294*/ volatile unsigned int xpmask; /* X PlaneMask */ +/*298*/ volatile unsigned int ypmask; /* Y PlaneMask */ +/*29c*/ volatile unsigned int zpmask; /* Z PlaneMask */ +/*2a0*/ ffb_auxclip auxclip[4]; /* Auxilliary Viewport Clip */ + + /* New 3dRAM III support regs */ +/*2c0*/ volatile unsigned int rawblend2; +/*2c4*/ volatile unsigned int rawpreblend; +/*2c8*/ volatile unsigned int rawstencil; +/*2cc*/ volatile unsigned int rawstencilctl; +/*2d0*/ volatile unsigned int threedram1; +/*2d4*/ volatile unsigned int threedram2; +/*2d8*/ volatile unsigned int passin; +/*2dc*/ volatile unsigned int rawclrdepth; +/*2e0*/ volatile unsigned int rawpmask; +/*2e4*/ volatile unsigned int rawcsrc; +/*2e8*/ volatile unsigned int rawmatch; +/*2ec*/ volatile unsigned int rawmagn; +/*2f0*/ volatile unsigned int rawropblend; +/*2f4*/ volatile unsigned int rawcmp; +/*2f8*/ volatile unsigned int rawwac; +/*2fc*/ volatile unsigned int fbramid; + +/*300*/ volatile unsigned int drawop; /* Draw OPeration */ +/*304*/ unsigned int pad10[2]; /* Reserved */ +/*30c*/ volatile unsigned int lpat; /* Line Pattern control */ +/*310*/ unsigned int pad11; /* Reserved */ +/*314*/ volatile unsigned int fontxy; /* XY Font coordinate */ +/*318*/ volatile unsigned int fontw; /* Font Width */ +/*31c*/ volatile unsigned int fontinc; /* Font Increment */ +/*320*/ volatile unsigned int font; /* Font bits */ +/*324*/ unsigned int pad12[3]; /* Reserved */ +/*330*/ volatile unsigned int blend2; +/*334*/ volatile unsigned int preblend; +/*338*/ volatile unsigned int stencil; +/*33c*/ volatile unsigned int stencilctl; + +/*340*/ unsigned int pad13[4]; /* Reserved */ +/*350*/ volatile unsigned int dcss1; /* Depth Cue Scale Slope 1 */ +/*354*/ volatile unsigned int dcss2; /* Depth Cue Scale Slope 2 */ +/*358*/ volatile unsigned int dcss3; /* Depth Cue Scale Slope 3 */ +/*35c*/ volatile unsigned int widpmask; +/*360*/ volatile unsigned int dcs2; +/*364*/ volatile unsigned int dcs3; +/*368*/ volatile unsigned int dcs4; +/*36c*/ unsigned int pad14; /* Reserved */ +/*370*/ volatile unsigned int dcd2; +/*374*/ volatile unsigned int dcd3; +/*378*/ volatile unsigned int dcd4; +/*37c*/ unsigned int pad15; /* Reserved */ +/*380*/ volatile unsigned int pattern[32]; /* area Pattern */ +/*400*/ unsigned int pad16[8]; /* Reserved */ +/*420*/ volatile unsigned int reset; /* chip RESET */ +/*424*/ unsigned int pad17[247]; /* Reserved */ +/*800*/ volatile unsigned int devid; /* Device ID */ +/*804*/ unsigned int pad18[63]; /* Reserved */ +/*900*/ volatile unsigned int ucsr; /* User Control & Status Register */ +/*904*/ unsigned int pad19[31]; /* Reserved */ +/*980*/ volatile unsigned int mer; /* Mode Enable Register */ +/*984*/ unsigned int pad20[1439]; /* Reserved */ +} ffb_fbc, *ffb_fbcPtr; + +struct ffb_hw_context { + int is_2d_only; + + unsigned int ppc; + unsigned int wid; + unsigned int fg; + unsigned int bg; + unsigned int consty; + unsigned int constz; + unsigned int xclip; + unsigned int dcss; + unsigned int vclipmin; + unsigned int vclipmax; + unsigned int vclipzmin; + unsigned int vclipzmax; + unsigned int dcsf; + unsigned int dcsb; + unsigned int dczf; + unsigned int dczb; + unsigned int blendc; + unsigned int blendc1; + unsigned int blendc2; + unsigned int fbc; + unsigned int rop; + unsigned int cmp; + unsigned int matchab; + unsigned int matchc; + unsigned int magnab; + unsigned int magnc; + unsigned int pmask; + unsigned int xpmask; + unsigned int ypmask; + unsigned int zpmask; + unsigned int auxclip0min; + unsigned int auxclip0max; + unsigned int auxclip1min; + unsigned int auxclip1max; + unsigned int auxclip2min; + unsigned int auxclip2max; + unsigned int auxclip3min; + unsigned int auxclip3max; + unsigned int drawop; + unsigned int lpat; + unsigned int fontxy; + unsigned int fontw; + unsigned int fontinc; + unsigned int area_pattern[32]; + unsigned int ucsr; + unsigned int stencil; + unsigned int stencilctl; + unsigned int dcss1; + unsigned int dcss2; + unsigned int dcss3; + unsigned int dcs2; + unsigned int dcs3; + unsigned int dcs4; + unsigned int dcd2; + unsigned int dcd3; + unsigned int dcd4; + unsigned int mer; +}; + +#define FFB_MAX_CTXS 32 + +enum ffb_chip_type { + ffb1_prototype = 0, /* Early pre-FCS FFB */ + ffb1_standard, /* First FCS FFB, 100Mhz UPA, 66MHz gclk */ + ffb1_speedsort, /* Second FCS FFB, 100Mhz UPA, 75MHz gclk */ + ffb2_prototype, /* Early pre-FCS vertical FFB2 */ + ffb2_vertical, /* First FCS FFB2/vertical, 100Mhz UPA, 100MHZ gclk, + 75(SingleBuffer)/83(DoubleBuffer) MHz fclk */ + ffb2_vertical_plus, /* Second FCS FFB2/vertical, same timings */ + ffb2_horizontal, /* First FCS FFB2/horizontal, same timings as FFB2/vert */ + ffb2_horizontal_plus, /* Second FCS FFB2/horizontal, same timings */ + afb_m3, /* FCS Elite3D, 3 float chips */ + afb_m6 /* FCS Elite3D, 6 float chips */ +}; + +typedef struct ffb_dev_priv { + /* Misc software state. */ + int prom_node; + enum ffb_chip_type ffb_type; + u64 card_phys_base; + struct miscdevice miscdev; + + /* Controller registers. */ + ffb_fbcPtr regs; + + /* Context table. */ + struct ffb_hw_context *hw_state[FFB_MAX_CTXS]; +} ffb_dev_priv_t; diff --git a/drivers/char/drm/fops.c b/drivers/char/drm/fops.c new file mode 100644 index 000000000000..fb56a7113421 --- /dev/null +++ b/drivers/char/drm/fops.c @@ -0,0 +1,238 @@ +/* fops.c -- File operations for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include + +/* drm_open is called whenever a process opens /dev/drm. */ + +int drm_open_helper(struct inode *inode, struct file *filp, drm_device_t *dev) +{ + kdev_t minor = MINOR(inode->i_rdev); + drm_file_t *priv; + + if (filp->f_flags & O_EXCL) return -EBUSY; /* No exclusive opens */ + if (!drm_cpu_valid()) return -EINVAL; + + DRM_DEBUG("pid = %d, minor = %d\n", current->pid, minor); + + priv = drm_alloc(sizeof(*priv), DRM_MEM_FILES); + memset(priv, 0, sizeof(*priv)); + filp->private_data = priv; + priv->uid = current->euid; + priv->pid = current->pid; + priv->minor = minor; + priv->dev = dev; + priv->ioctl_count = 0; + priv->authenticated = capable(CAP_SYS_ADMIN); + + down(&dev->struct_sem); + if (!dev->file_last) { + priv->next = NULL; + priv->prev = NULL; + dev->file_first = priv; + dev->file_last = priv; + } else { + priv->next = NULL; + priv->prev = dev->file_last; + dev->file_last->next = priv; + dev->file_last = priv; + } + up(&dev->struct_sem); + + return 0; +} + +int drm_flush(struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + return 0; +} + +/* drm_release is called whenever a process closes /dev/drm*. Linux calls + this only if any mappings have been closed. */ + +int drm_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock + && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } + drm_reclaim_buffers(dev, priv->pid); + + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); + + return 0; +} + +int drm_fasync(int fd, struct file *filp, int on) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode; + + DRM_DEBUG("fd = %d, device = 0x%x\n", fd, dev->device); + retcode = fasync_helper(fd, filp, on, &dev->buf_async); + if (retcode < 0) return retcode; + return 0; +} + + +/* The drm_read and drm_write_string code (especially that which manages + the circular buffer), is based on Alessandro Rubini's LINUX DEVICE + DRIVERS (Cambridge: O'Reilly, 1998), pages 111-113. */ + +ssize_t drm_read(struct file *filp, char *buf, size_t count, loff_t *off) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int left; + int avail; + int send; + int cur; + + DRM_DEBUG("%p, %p\n", dev->buf_rp, dev->buf_wp); + + while (dev->buf_rp == dev->buf_wp) { + DRM_DEBUG(" sleeping\n"); + if (filp->f_flags & O_NONBLOCK) { + return -EAGAIN; + } + interruptible_sleep_on(&dev->buf_readers); + if (signal_pending(current)) { + DRM_DEBUG(" interrupted\n"); + return -ERESTARTSYS; + } + DRM_DEBUG(" awake\n"); + } + + left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + avail = DRM_BSZ - left; + send = DRM_MIN(avail, count); + + while (send) { + if (dev->buf_wp > dev->buf_rp) { + cur = DRM_MIN(send, dev->buf_wp - dev->buf_rp); + } else { + cur = DRM_MIN(send, dev->buf_end - dev->buf_rp); + } + copy_to_user_ret(buf, dev->buf_rp, cur, -EINVAL); + dev->buf_rp += cur; + if (dev->buf_rp == dev->buf_end) dev->buf_rp = dev->buf; + send -= cur; + } + + wake_up_interruptible(&dev->buf_writers); + return DRM_MIN(avail, count);; +} + +int drm_write_string(drm_device_t *dev, const char *s) +{ + int left = (dev->buf_rp + DRM_BSZ - dev->buf_wp) % DRM_BSZ; + int send = strlen(s); + int count; + + DRM_DEBUG("%d left, %d to send (%p, %p)\n", + left, send, dev->buf_rp, dev->buf_wp); + + if (left == 1 || dev->buf_wp != dev->buf_rp) { + DRM_ERROR("Buffer not empty (%d left, wp = %p, rp = %p)\n", + left, + dev->buf_wp, + dev->buf_rp); + } + + while (send) { + if (dev->buf_wp >= dev->buf_rp) { + count = DRM_MIN(send, dev->buf_end - dev->buf_wp); + if (count == left) --count; /* Leave a hole */ + } else { + count = DRM_MIN(send, dev->buf_rp - dev->buf_wp - 1); + } + strncpy(dev->buf_wp, s, count); + dev->buf_wp += count; + if (dev->buf_wp == dev->buf_end) dev->buf_wp = dev->buf; + send -= count; + } + +#if LINUX_VERSION_CODE < 0x020400 + if (dev->buf_async) kill_fasync(dev->buf_async, SIGIO); +#else + /* Type of first parameter changed in + Linux 2.4.0-test2... */ + if (dev->buf_async) kill_fasync(&dev->buf_async, SIGIO, POLL_IN); +#endif + DRM_DEBUG("waking\n"); + wake_up_interruptible(&dev->buf_readers); + return 0; +} + +unsigned int drm_poll(struct file *filp, struct poll_table_struct *wait) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + poll_wait(filp, &dev->buf_readers, wait); + if (dev->buf_wp != dev->buf_rp) return POLLIN | POLLRDNORM; + return 0; +} diff --git a/drivers/char/drm/gamma_dma.c b/drivers/char/drm/gamma_dma.c new file mode 100644 index 000000000000..a99f24caeef1 --- /dev/null +++ b/drivers/char/drm/gamma_dma.c @@ -0,0 +1,824 @@ +/* gamma_dma.c -- DMA support for GMX 2000 -*- linux-c -*- + * Created: Fri Mar 19 14:30:16 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "gamma_drv.h" + +#include /* For task queue support */ + + +/* WARNING!!! MAGIC NUMBER!!! The number of regions already added to the + kernel must be specified here. Currently, the number is 2. This must + match the order the X server uses for instantiating register regions , + or must be passed in a new ioctl. */ +#define GAMMA_REG(reg) \ + (2 \ + + ((reg < 0x1000) \ + ? 0 \ + : ((reg < 0x10000) ? 1 : ((reg < 0x11000) ? 2 : 3)))) + +#define GAMMA_OFF(reg) \ + ((reg < 0x1000) \ + ? reg \ + : ((reg < 0x10000) \ + ? (reg - 0x1000) \ + : ((reg < 0x11000) \ + ? (reg - 0x10000) \ + : (reg - 0x11000)))) + +#define GAMMA_BASE(reg) ((unsigned long)dev->maplist[GAMMA_REG(reg)]->handle) +#define GAMMA_ADDR(reg) (GAMMA_BASE(reg) + GAMMA_OFF(reg)) +#define GAMMA_DEREF(reg) *(__volatile__ int *)GAMMA_ADDR(reg) +#define GAMMA_READ(reg) GAMMA_DEREF(reg) +#define GAMMA_WRITE(reg,val) do { GAMMA_DEREF(reg) = val; } while (0) + +#define GAMMA_BROADCASTMASK 0x9378 +#define GAMMA_COMMANDINTENABLE 0x0c48 +#define GAMMA_DMAADDRESS 0x0028 +#define GAMMA_DMACOUNT 0x0030 +#define GAMMA_FILTERMODE 0x8c00 +#define GAMMA_GCOMMANDINTFLAGS 0x0c50 +#define GAMMA_GCOMMANDMODE 0x0c40 +#define GAMMA_GCOMMANDSTATUS 0x0c60 +#define GAMMA_GDELAYTIMER 0x0c38 +#define GAMMA_GDMACONTROL 0x0060 +#define GAMMA_GINTENABLE 0x0808 +#define GAMMA_GINTFLAGS 0x0810 +#define GAMMA_INFIFOSPACE 0x0018 +#define GAMMA_OUTFIFOWORDS 0x0020 +#define GAMMA_OUTPUTFIFO 0x2000 +#define GAMMA_SYNC 0x8c40 +#define GAMMA_SYNC_TAG 0x0188 + +static inline void gamma_dma_dispatch(drm_device_t *dev, unsigned long address, + unsigned long length) +{ + GAMMA_WRITE(GAMMA_DMAADDRESS, virt_to_phys((void *)address)); + while (GAMMA_READ(GAMMA_GCOMMANDSTATUS) != 4) + ; + GAMMA_WRITE(GAMMA_DMACOUNT, length / 4); +} + +static inline void gamma_dma_quiescent_single(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) + ; + + GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); + GAMMA_WRITE(GAMMA_SYNC, 0); + + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); +} + +static inline void gamma_dma_quiescent_dual(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; + while (GAMMA_READ(GAMMA_INFIFOSPACE) < 3) + ; + + GAMMA_WRITE(GAMMA_BROADCASTMASK, 3); + + GAMMA_WRITE(GAMMA_FILTERMODE, 1 << 10); + GAMMA_WRITE(GAMMA_SYNC, 0); + + /* Read from first MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO) != GAMMA_SYNC_TAG); + + /* Read from second MX */ + do { + while (!GAMMA_READ(GAMMA_OUTFIFOWORDS + 0x10000)) + ; + } while (GAMMA_READ(GAMMA_OUTPUTFIFO + 0x10000) != GAMMA_SYNC_TAG); +} + +static inline void gamma_dma_ready(drm_device_t *dev) +{ + while (GAMMA_READ(GAMMA_DMACOUNT)) + ; +} + +static inline int gamma_dma_is_ready(drm_device_t *dev) +{ + return !GAMMA_READ(GAMMA_DMACOUNT); +} + +static void gamma_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_device_dma_t *dma = dev->dma; + + atomic_inc(&dev->total_irq); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0xc350/2); /* 0x05S */ + GAMMA_WRITE(GAMMA_GCOMMANDINTFLAGS, 8); + GAMMA_WRITE(GAMMA_GINTFLAGS, 0x2001); + if (gamma_dma_is_ready(dev)) { + /* Free previous buffer */ + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_free); + return; + } + if (dma->this_buffer) { + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = NULL; + } + clear_bit(0, &dev->dma_flag); + + /* Dispatch new buffer */ + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); + } +} + +/* Only called by gamma_dma_schedule. */ +static int gamma_do_dma(drm_device_t *dev, int locked) +{ + unsigned long address; + unsigned long length; + drm_buf_t *buf; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t dma_start, dma_stop; +#endif + + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_dma); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dma_start = get_cycles(); +#endif + + if (!dma->next_buffer) { + DRM_ERROR("No next_buffer\n"); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + buf = dma->next_buffer; + address = (unsigned long)buf->address; + length = buf->used; + + DRM_DEBUG("context %d, buffer %d (%ld bytes)\n", + buf->context, buf->idx, length); + + if (buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return -EINVAL; + } + + if (!length) { + DRM_ERROR("0 length buffer\n"); + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + clear_bit(0, &dev->dma_flag); + return 0; + } + + if (!gamma_dma_is_ready(dev)) { + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + + if (buf->while_locked) { + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Dispatching buffer %d from pid %d" + " \"while locked\", but no lock held\n", + buf->idx, buf->pid); + } + } else { + if (!locked && !drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + atomic_inc(&dma->total_missed_lock); + clear_bit(0, &dev->dma_flag); + return -EBUSY; + } + } + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + /* PRE: dev->last_context != buf->context */ + if (drm_context_switch(dev, dev->last_context, buf->context)) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + retcode = -EBUSY; + goto cleanup; + + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + } + + drm_clear_next_buffer(dev); + buf->pending = 1; + buf->waiting = 0; + buf->list = DRM_LIST_PEND; +#if DRM_DMA_HISTOGRAM + buf->time_dispatched = get_cycles(); +#endif + + gamma_dma_dispatch(dev, address, length); + drm_free_buffer(dev, dma->this_buffer); + dma->this_buffer = buf; + + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (!buf->while_locked && !dev->context_flag && !locked) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +cleanup: + + clear_bit(0, &dev->dma_flag); + +#if DRM_DMA_HISTOGRAM + dma_stop = get_cycles(); + atomic_inc(&dev->histo.dma[drm_histogram_slot(dma_stop - dma_start)]); +#endif + + return retcode; +} + +static void gamma_dma_schedule_timer_wrapper(unsigned long dev) +{ + gamma_dma_schedule((drm_device_t *)dev, 0); +} + +static void gamma_dma_schedule_tq_wrapper(void *dev) +{ + gamma_dma_schedule(dev, 0); +} + +int gamma_dma_schedule(drm_device_t *dev, int locked) +{ + int next; + drm_queue_t *q; + drm_buf_t *buf; + int retcode = 0; + int processed = 0; + int missed; + int expire = 20; + drm_device_dma_t *dma = dev->dma; +#if DRM_DMA_HISTOGRAM + cycles_t schedule_start; +#endif + + if (test_and_set_bit(0, &dev->interrupt_flag)) { + /* Not reentrant */ + atomic_inc(&dma->total_missed_sched); + return -EBUSY; + } + missed = atomic_read(&dma->total_missed_sched); + +#if DRM_DMA_HISTOGRAM + schedule_start = get_cycles(); +#endif + +again: + if (dev->context_flag) { + clear_bit(0, &dev->interrupt_flag); + return -EBUSY; + } + if (dma->next_buffer) { + /* Unsent buffer that was previously + selected, but that couldn't be sent + because the lock could not be obtained + or the DMA engine wasn't ready. Try + again. */ + atomic_inc(&dma->total_tried); + if (!(retcode = gamma_do_dma(dev, locked))) { + atomic_inc(&dma->total_hit); + ++processed; + } + } else { + do { + next = drm_select_queue(dev, + gamma_dma_schedule_timer_wrapper); + if (next >= 0) { + q = dev->queuelist[next]; + buf = drm_waitlist_get(&q->waitlist); + dma->next_buffer = buf; + dma->next_queue = q; + if (buf && buf->list == DRM_LIST_RECLAIM) { + drm_clear_next_buffer(dev); + drm_free_buffer(dev, buf); + } + } + } while (next >= 0 && !dma->next_buffer); + if (dma->next_buffer) { + if (!(retcode = gamma_do_dma(dev, locked))) { + ++processed; + } + } + } + + if (--expire) { + if (missed != atomic_read(&dma->total_missed_sched)) { + atomic_inc(&dma->total_lost); + if (gamma_dma_is_ready(dev)) goto again; + } + if (processed && gamma_dma_is_ready(dev)) { + atomic_inc(&dma->total_lost); + processed = 0; + goto again; + } + } + + clear_bit(0, &dev->interrupt_flag); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.schedule[drm_histogram_slot(get_cycles() + - schedule_start)]); +#endif + return retcode; +} + +static int gamma_dma_priority(drm_device_t *dev, drm_dma_t *d) +{ + unsigned long address; + unsigned long length; + int must_free = 0; + int retcode = 0; + int i; + int idx; + drm_buf_t *buf; + drm_buf_t *last_buf = NULL; + drm_device_dma_t *dma = dev->dma; + DECLARE_WAITQUEUE(entry, current); + + /* Turn off interrupt handling */ + while (test_and_set_bit(0, &dev->interrupt_flag)) { + schedule(); + if (signal_pending(current)) return -EINTR; + } + if (!(d->flags & _DRM_DMA_WHILE_LOCKED)) { + while (!drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + schedule(); + if (signal_pending(current)) { + clear_bit(0, &dev->interrupt_flag); + return -EINTR; + } + } + ++must_free; + } + atomic_inc(&dma->total_prio); + + for (i = 0; i < d->send_count; i++) { + idx = d->send_indices[i]; + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + d->send_indices[i], dma->buf_count - 1); + continue; + } + buf = dma->buflist[ idx ]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + retcode = -EINVAL; + goto cleanup; + } + if (buf->list != DRM_LIST_NONE) { + DRM_ERROR("Process %d using %d's buffer on list %d\n", + current->pid, buf->pid, buf->list); + retcode = -EINVAL; + goto cleanup; + } + /* This isn't a race condition on + buf->list, since our concern is the + buffer reclaim during the time the + process closes the /dev/drm? handle, so + it can't also be doing DMA. */ + buf->list = DRM_LIST_PRIO; + buf->used = d->send_sizes[i]; + buf->context = d->context; + buf->while_locked = d->flags & _DRM_DMA_WHILE_LOCKED; + address = (unsigned long)buf->address; + length = buf->used; + if (!length) { + DRM_ERROR("0 length buffer\n"); + } + if (buf->pending) { + DRM_ERROR("Sending pending buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + if (buf->waiting) { + DRM_ERROR("Sending waiting buffer:" + " buffer %d, offset %d\n", + d->send_indices[i], i); + retcode = -EINVAL; + goto cleanup; + } + buf->pending = 1; + + if (dev->last_context != buf->context + && !(dev->queuelist[buf->context]->flags + & _DRM_CONTEXT_PRESERVED)) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != buf->context */ + drm_context_switch(dev, dev->last_context, + buf->context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == buf->context. + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + retcode = -EINTR; + goto cleanup; + } + if (dev->last_context != buf->context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, + buf->context); + } + } + +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); + buf->time_dispatched = buf->time_queued; +#endif + gamma_dma_dispatch(dev, address, length); + atomic_add(length, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + + if (last_buf) { + drm_free_buffer(dev, last_buf); + } + last_buf = buf; + } + + +cleanup: + if (last_buf) { + gamma_dma_ready(dev); + drm_free_buffer(dev, last_buf); + } + + if (must_free && !dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + clear_bit(0, &dev->interrupt_flag); + return retcode; +} + +static int gamma_dma_send_buffers(drm_device_t *dev, drm_dma_t *d) +{ + DECLARE_WAITQUEUE(entry, current); + drm_buf_t *last_buf = NULL; + int retcode = 0; + drm_device_dma_t *dma = dev->dma; + + if (d->flags & _DRM_DMA_BLOCK) { + last_buf = dma->buflist[d->send_indices[d->send_count-1]]; + add_wait_queue(&last_buf->dma_wait, &entry); + } + + if ((retcode = drm_dma_enqueue(dev, d))) { + if (d->flags & _DRM_DMA_BLOCK) + remove_wait_queue(&last_buf->dma_wait, &entry); + return retcode; + } + + gamma_dma_schedule(dev, 0); + + if (d->flags & _DRM_DMA_BLOCK) { + DRM_DEBUG("%d waiting\n", current->pid); + current->state = TASK_INTERRUPTIBLE; + for (;;) { + if (!last_buf->waiting + && !last_buf->pending) + break; /* finished */ + schedule(); + if (signal_pending(current)) { + retcode = -EINTR; /* Can't restart */ + break; + } + } + current->state = TASK_RUNNING; + DRM_DEBUG("%d running\n", current->pid); + remove_wait_queue(&last_buf->dma_wait, &entry); + if (!retcode + || (last_buf->list==DRM_LIST_PEND && !last_buf->pending)) { + if (!waitqueue_active(&last_buf->dma_wait)) { + drm_free_buffer(dev, last_buf); + } + } + if (retcode) { + DRM_ERROR("ctx%d w%d p%d c%d i%d l%d %d/%d\n", + d->context, + last_buf->waiting, + last_buf->pending, + DRM_WAITCOUNT(dev, d->context), + last_buf->idx, + last_buf->list, + last_buf->pid, + current->pid); + } + } + return retcode; +} + +int gamma_dma(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_dma_t d; + + copy_from_user_ret(&d, (drm_dma_t *)arg, sizeof(d), -EFAULT); + DRM_DEBUG("%d %d: %d send, %d req\n", + current->pid, d.context, d.send_count, d.request_count); + + if (d.context == DRM_KERNEL_CONTEXT || d.context >= dev->queue_slots) { + DRM_ERROR("Process %d using context %d\n", + current->pid, d.context); + return -EINVAL; + } + if (d.send_count < 0 || d.send_count > dma->buf_count) { + DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", + current->pid, d.send_count, dma->buf_count); + return -EINVAL; + } + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count); + return -EINVAL; + } + + if (d.send_count) { + if (d.flags & _DRM_DMA_PRIORITY) + retcode = gamma_dma_priority(dev, &d); + else + retcode = gamma_dma_send_buffers(dev, &d); + } + + d.granted_count = 0; + + if (!retcode && d.request_count) { + retcode = drm_dma_get_buffers(dev, &d); + } + + DRM_DEBUG("%d returning, granted = %d\n", + current->pid, d.granted_count); + copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT); + + return retcode; +} + +int gamma_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG("%d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + + dev->tq.next = NULL; + dev->tq.sync = 0; + dev->tq.routine = gamma_dma_schedule_tq_wrapper; + dev->tq.data = dev; + + + /* Before installing handler */ + GAMMA_WRITE(GAMMA_GCOMMANDMODE, 0); + GAMMA_WRITE(GAMMA_GDMACONTROL, 0); + + /* Install handler */ + if ((retcode = request_irq(dev->irq, + gamma_dma_service, + 0, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + + /* After installing handler */ + GAMMA_WRITE(GAMMA_GINTENABLE, 0x2001); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0x0008); + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0x39090); + + return 0; +} + +int gamma_irq_uninstall(drm_device_t *dev) +{ + int irq; + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + + DRM_DEBUG("%d\n", irq); + + GAMMA_WRITE(GAMMA_GDELAYTIMER, 0); + GAMMA_WRITE(GAMMA_COMMANDINTENABLE, 0); + GAMMA_WRITE(GAMMA_GINTENABLE, 0); + free_irq(irq, dev); + + return 0; +} + + +int gamma_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + int retcode; + + copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); + + switch (ctl.func) { + case DRM_INST_HANDLER: + if ((retcode = gamma_irq_install(dev, ctl.irq))) + return retcode; + break; + case DRM_UNINST_HANDLER: + if ((retcode = gamma_irq_uninstall(dev))) + return retcode; + break; + default: + return -EINVAL; + } + return 0; +} + +int gamma_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + drm_queue_t *q; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; + q = dev->queuelist[lock.context]; + + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + + if (!ret) { + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (j > 0 && j <= DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(j); + } + } + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + atomic_inc(&q->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + drm_flush_unblock(dev, lock.context, lock.flags); /* cleanup phase */ + + if (!ret) { + if (lock.flags & _DRM_LOCK_READY) + gamma_dma_ready(dev); + if (lock.flags & _DRM_LOCK_QUIESCENT) { + if (gamma_found() == 1) { + gamma_dma_quiescent_single(dev); + } else { + gamma_dma_quiescent_dual(dev); + } + } + } + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} diff --git a/drivers/char/drm/gamma_drv.c b/drivers/char/drm/gamma_drv.c new file mode 100644 index 000000000000..12ed9f5e425c --- /dev/null +++ b/drivers/char/drm/gamma_drv.c @@ -0,0 +1,575 @@ +/* gamma.c -- 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#include +#include "drmP.h" +#include "gamma_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#ifndef PCI_DEVICE_ID_3DLABS_GAMMA +#define PCI_DEVICE_ID_3DLABS_GAMMA 0x0008 +#endif +#ifndef PCI_DEVICE_ID_3DLABS_MX +#define PCI_DEVICE_ID_3DLABS_MX 0x0006 +#endif + +#define GAMMA_NAME "gamma" +#define GAMMA_DESC "3dlabs GMX 2000" +#define GAMMA_DATE "20000719" +#define GAMMA_MAJOR 1 +#define GAMMA_MINOR 0 +#define GAMMA_PATCHLEVEL 0 + +static drm_device_t gamma_device; + +static struct file_operations gamma_fops = { +#if LINUX_VERSION_CODE >= 0x020322 + /* This started being used approx. 2.3.34 */ + owner: THIS_MODULE, +#endif + open: gamma_open, + flush: drm_flush, + release: gamma_release, + ioctl: gamma_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice gamma_misc = { + minor: MISC_DYNAMIC_MINOR, + name: GAMMA_NAME, + fops: &gamma_fops, +}; + +static drm_ioctl_desc_t gamma_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { gamma_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { gamma_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { drm_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { drm_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { drm_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { drm_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { drm_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { drm_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { drm_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { drm_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { drm_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { gamma_dma, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { gamma_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { gamma_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +}; +#define GAMMA_IOCTL_COUNT DRM_ARRAY_SIZE(gamma_ioctls) + +#ifdef MODULE +static char *gamma = NULL; +#endif +static int devices = 0; + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("3dlabs GMX 2000"); +MODULE_PARM(gamma, "s"); +MODULE_PARM(devices, "i"); +MODULE_PARM_DESC(devices, + "devices=x, where x is the number of MX chips on card\n"); +#ifndef MODULE +/* gamma_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_options. + */ + + +static int __init gamma_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("gamma=", gamma_options); +#endif + +static int gamma_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); +#if DRM_DMA_HISTO + memset(&dev->histo, 0, sizeof(dev->histo)); +#endif + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int gamma_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) gamma_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +int gamma_found(void) +{ + return devices; +} + +int gamma_find_devices(void) +{ + struct pci_dev *d = NULL, *one = NULL, *two = NULL; + + d = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_GAMMA,d); + if (!d) return 0; + + one = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,d); + if (!one) return 0; + + /* Make sure it's on the same card, if not - no MX's found */ + if (PCI_SLOT(d->devfn) != PCI_SLOT(one->devfn)) return 0; + + two = pci_find_device(PCI_VENDOR_ID_3DLABS,PCI_DEVICE_ID_3DLABS_MX,one); + if (!two) return 1; + + /* Make sure it's on the same card, if not - only 1 MX found */ + if (PCI_SLOT(d->devfn) != PCI_SLOT(two->devfn)) return 1; + + /* Two MX's found - we don't currently support more than 2 */ + return 2; +} + +/* gamma_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int gamma_init(void) +{ + int retcode; + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(gamma); +#endif + devices = gamma_find_devices(); + if (devices == 0) return -1; + + if ((retcode = misc_register(&gamma_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", GAMMA_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, gamma_misc.minor); + dev->name = GAMMA_NAME; + + drm_mem_init(); + drm_proc_init(dev); + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d with %d MX devices\n", + GAMMA_NAME, + GAMMA_MAJOR, + GAMMA_MINOR, + GAMMA_PATCHLEVEL, + GAMMA_DATE, + gamma_misc.minor, + devices); + + return 0; +} + +/* gamma_cleanup is called via cleanup_module at module unload time. */ + +static void gamma_cleanup(void) +{ + drm_device_t *dev = &gamma_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&gamma_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + gamma_takedown(dev); +} + +module_init(gamma_init); +module_exit(gamma_cleanup); + + +int gamma_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = GAMMA_MAJOR; + version.version_minor = GAMMA_MINOR; + version.version_patchlevel = GAMMA_PATCHLEVEL; + + DRM_COPY(version.name, GAMMA_NAME); + DRM_COPY(version.date, GAMMA_DATE); + DRM_COPY(version.desc, GAMMA_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int gamma_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &gamma_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return gamma_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int gamma_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return gamma_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + unlock_kernel(); + return retcode; +} + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int gamma_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= GAMMA_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &gamma_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + + +int gamma_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + gamma_dma_schedule(dev, 1); + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles() + - dev->lck_start)]); +#endif + + return 0; +} diff --git a/drivers/char/drm/gamma_drv.h b/drivers/char/drm/gamma_drv.h new file mode 100644 index 000000000000..e9ce39589707 --- /dev/null +++ b/drivers/char/drm/gamma_drv.h @@ -0,0 +1,58 @@ +/* gamma_drv.h -- Private header for 3dlabs GMX 2000 driver -*- linux-c -*- + * Created: Mon Jan 4 10:05:05 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#ifndef _GAMMA_DRV_H_ +#define _GAMMA_DRV_H_ + + /* gamma_drv.c */ +extern int gamma_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_open(struct inode *inode, struct file *filp); +extern int gamma_release(struct inode *inode, struct file *filp); +extern int gamma_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* gamma_dma.c */ +extern int gamma_dma_schedule(drm_device_t *dev, int locked); +extern int gamma_dma(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_irq_install(drm_device_t *dev, int irq); +extern int gamma_irq_uninstall(drm_device_t *dev); +extern int gamma_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int gamma_find_devices(void); +extern int gamma_found(void); + +#endif diff --git a/drivers/char/drm/i810_bufs.c b/drivers/char/drm/i810_bufs.c new file mode 100644 index 000000000000..fa1f84dcdf5d --- /dev/null +++ b/drivers/char/drm/i810_bufs.c @@ -0,0 +1,334 @@ +/* i810_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" +#include "linux/un.h" + +int i810_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + agp_offset = request.agp_start; + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + byte_count = 0; + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + while(entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = offset; + buf->bus_address = dev->agp->base + agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->agp->base); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_private = drm_alloc(sizeof(drm_i810_buf_priv_t), + DRM_MEM_BUFS); + buf->dev_priv_size = sizeof(drm_i810_buf_priv_t); + memset(buf->dev_private, 0, sizeof(drm_i810_buf_priv_t)); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + offset = offset + alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + atomic_dec(&dev->buf_alloc); + dma->flags = _DRM_DMA_USE_AGP; + return 0; +} + +int i810_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_buf_desc_t request; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + if(request.flags & _DRM_AGP_BUFFER) + return i810_addbufs_agp(inode, filp, cmd, arg); + else + return -EINVAL; +} + +int i810_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_info_t *)arg, + sizeof(request), + -EFAULT); + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + copy_to_user_ret(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count), + -EFAULT); + copy_to_user_ret(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size), + -EFAULT); + copy_to_user_ret(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark), + -EFAULT); + copy_to_user_ret(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark), + -EFAULT); + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + copy_to_user_ret((drm_buf_info_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return 0; +} + +int i810_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int i810_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_free_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + copy_from_user_ret(&idx, + &request.list[i], + sizeof(idx), + -EFAULT); + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + diff --git a/drivers/char/drm/i810_context.c b/drivers/char/drm/i810_context.c new file mode 100644 index 000000000000..85c0877b6bfe --- /dev/null +++ b/drivers/char/drm/i810_context.c @@ -0,0 +1,203 @@ +/* i810_context.c -- IOCTLs for i810 contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" + +static int i810_alloc_queue(drm_device_t *dev) +{ + int temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("i810_alloc_queue: %d\n", temp); + return temp; +} + +int i810_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + i810_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int i810_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + +int i810_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + +int i810_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = i810_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = i810_alloc_queue(dev); + } + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + DRM_DEBUG("%d\n", ctx.handle); + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int i810_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + /* This does nothing for the i810 */ + return 0; +} + +int i810_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int i810_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return i810_context_switch(dev, dev->last_context, ctx.handle); +} + +int i810_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + i810_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int i810_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + if(ctx.handle != DRM_KERNEL_CONTEXT) { + drm_ctxbitmap_free(dev, ctx.handle); + } + + return 0; +} diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c new file mode 100644 index 000000000000..f3d9db43ee0e --- /dev/null +++ b/drivers/char/drm/i810_dma.c @@ -0,0 +1,1417 @@ +/* i810_dma.c -- DMA support for the i810 -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "i810_drv.h" +#include /* For task queue support */ + +/* in case we don't have a 2.3.99-pre6 kernel or later: */ +#ifndef VM_DONTCOPY +#define VM_DONTCOPY 0 +#endif + +#define I810_BUF_FREE 2 +#define I810_BUF_CLIENT 1 +#define I810_BUF_HARDWARE 0 + +#define I810_BUF_UNMAPPED 0 +#define I810_BUF_MAPPED 1 + +#define I810_REG(reg) 2 +#define I810_BASE(reg) ((unsigned long) \ + dev->maplist[I810_REG(reg)]->handle) +#define I810_ADDR(reg) (I810_BASE(reg) + reg) +#define I810_DEREF(reg) *(__volatile__ int *)I810_ADDR(reg) +#define I810_READ(reg) I810_DEREF(reg) +#define I810_WRITE(reg,val) do { I810_DEREF(reg) = val; } while (0) +#define I810_DEREF16(reg) *(__volatile__ u16 *)I810_ADDR(reg) +#define I810_READ16(reg) I810_DEREF16(reg) +#define I810_WRITE16(reg,val) do { I810_DEREF16(reg) = val; } while (0) + +#define RING_LOCALS unsigned int outring, ringmask; volatile char *virt; + +#define BEGIN_LP_RING(n) do { \ + if (I810_VERBOSE) \ + DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \ + n, __FUNCTION__); \ + if (dev_priv->ring.space < n*4) \ + i810_wait_ring(dev, n*4); \ + dev_priv->ring.space -= n*4; \ + outring = dev_priv->ring.tail; \ + ringmask = dev_priv->ring.tail_mask; \ + virt = dev_priv->ring.virtual_start; \ +} while (0) + +#define ADVANCE_LP_RING() do { \ + if (I810_VERBOSE) DRM_DEBUG("ADVANCE_LP_RING\n"); \ + dev_priv->ring.tail = outring; \ + I810_WRITE(LP_RING + RING_TAIL, outring); \ +} while(0) + +#define OUT_RING(n) do { \ + if (I810_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \ + *(volatile unsigned int *)(virt + outring) = n; \ + outring += 4; \ + outring &= ringmask; \ +} while (0); + +static inline void i810_print_status_page(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = dev->dev_private; + u32 *temp = (u32 *)dev_priv->hw_status_page; + int i; + + DRM_DEBUG( "hw_status: Interrupt Status : %x\n", temp[0]); + DRM_DEBUG( "hw_status: LpRing Head ptr : %x\n", temp[1]); + DRM_DEBUG( "hw_status: IRing Head ptr : %x\n", temp[2]); + DRM_DEBUG( "hw_status: Reserved : %x\n", temp[3]); + DRM_DEBUG( "hw_status: Driver Counter : %d\n", temp[5]); + for(i = 6; i < dma->buf_count + 6; i++) { + DRM_DEBUG( "buffer status idx : %d used: %d\n", i - 6, temp[i]); + } +} + +static drm_buf_t *i810_freelist_get(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i; + int used; + + /* Linear search might not be the best solution */ + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I810_BUF_FREE, + I810_BUF_CLIENT); + if(used == I810_BUF_FREE) { + return buf; + } + } + return NULL; +} + +/* This should only be called if the buffer is not sent to the hardware + * yet, the hardware updates in use for us once its on the ring buffer. + */ + +static int i810_freelist_put(drm_device_t *dev, drm_buf_t *buf) +{ + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + int used; + + /* In use is already a pointer */ + used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, I810_BUF_FREE); + if(used != I810_BUF_CLIENT) { + DRM_ERROR("Freeing buffer thats not in use : %d\n", buf->idx); + return -EINVAL; + } + + return 0; +} + +static struct file_operations i810_buffer_fops = { + open: i810_open, + flush: drm_flush, + release: i810_release, + ioctl: i810_ioctl, + mmap: i810_mmap_buffers, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_i810_private_t *dev_priv; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + + lock_kernel(); + dev = priv->dev; + dev_priv = dev->dev_private; + buf = dev_priv->mmap_buffer; + buf_priv = buf->dev_private; + + vma->vm_flags |= (VM_IO | VM_DONTCOPY); + vma->vm_file = filp; + + buf_priv->currently_mapped = I810_BUF_MAPPED; + unlock_kernel(); + + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) return -EAGAIN; + return 0; +} + +static int i810_map_buffer(drm_buf_t *buf, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_i810_private_t *dev_priv = dev->dev_private; + struct file_operations *old_fops; + int retcode = 0; + + if(buf_priv->currently_mapped == I810_BUF_MAPPED) return -EINVAL; + + if(VM_DONTCOPY != 0) { + down(¤t->mm->mmap_sem); + old_fops = filp->f_op; + filp->f_op = &i810_buffer_fops; + dev_priv->mmap_buffer = buf; + buf_priv->virtual = (void *)do_mmap(filp, 0, buf->total, + PROT_READ|PROT_WRITE, + MAP_SHARED, + buf->bus_address); + dev_priv->mmap_buffer = NULL; + filp->f_op = old_fops; + if ((unsigned long)buf_priv->virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed int)buf_priv->virtual; + buf_priv->virtual = 0; + } + up(¤t->mm->mmap_sem); + } else { + buf_priv->virtual = buf_priv->kernel_virtual; + buf_priv->currently_mapped = I810_BUF_MAPPED; + } + return retcode; +} + +static int i810_unmap_buffer(drm_buf_t *buf) +{ + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + int retcode = 0; + + if(VM_DONTCOPY != 0) { + if(buf_priv->currently_mapped != I810_BUF_MAPPED) + return -EINVAL; + down(¤t->mm->mmap_sem); +#if LINUX_VERSION_CODE < 0x020399 + retcode = do_munmap((unsigned long)buf_priv->virtual, + (size_t) buf->total); +#else + retcode = do_munmap(current->mm, + (unsigned long)buf_priv->virtual, + (size_t) buf->total); +#endif + up(¤t->mm->mmap_sem); + } + buf_priv->currently_mapped = I810_BUF_UNMAPPED; + buf_priv->virtual = 0; + + return retcode; +} + +static int i810_dma_get_buffer(drm_device_t *dev, drm_i810_dma_t *d, + struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + int retcode = 0; + + buf = i810_freelist_get(dev); + if (!buf) { + retcode = -ENOMEM; + DRM_DEBUG("%s retcode %d\n", __FUNCTION__, retcode); + goto out_get_buf; + } + + retcode = i810_map_buffer(buf, filp); + if(retcode) { + i810_freelist_put(dev, buf); + DRM_DEBUG("mapbuf failed in %s retcode %d\n", + __FUNCTION__, retcode); + goto out_get_buf; + } + buf->pid = priv->pid; + buf_priv = buf->dev_private; + d->granted = 1; + d->request_idx = buf->idx; + d->request_size = buf->total; + d->virtual = buf_priv->virtual; + +out_get_buf: + return retcode; +} + +static unsigned long i810_alloc_page(drm_device_t *dev) +{ + unsigned long address; + + address = __get_free_page(GFP_KERNEL); + if(address == 0UL) + return 0; + + atomic_inc(&mem_map[MAP_NR((void *) address)].count); + set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags); + + return address; +} + +static void i810_free_page(drm_device_t *dev, unsigned long page) +{ + if(page == 0UL) + return; + + atomic_dec(&mem_map[MAP_NR((void *) page)].count); + clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags); + wake_up(&mem_map[MAP_NR((void *) page)].wait); + free_page(page); + return; +} + +static int i810_dma_cleanup(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + + if(dev->dev_private) { + int i; + drm_i810_private_t *dev_priv = + (drm_i810_private_t *) dev->dev_private; + + if(dev_priv->ring.virtual_start) { + drm_ioremapfree((void *) dev_priv->ring.virtual_start, + dev_priv->ring.Size); + } + if(dev_priv->hw_status_page != 0UL) { + i810_free_page(dev, dev_priv->hw_status_page); + /* Need to rewrite hardware status page */ + I810_WRITE(0x02080, 0x1ffff000); + } + drm_free(dev->dev_private, sizeof(drm_i810_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_ioremapfree(buf_priv->kernel_virtual, buf->total); + } + } + return 0; +} + +static int i810_wait_ring(drm_device_t *dev, int n) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_ring_buffer_t *ring = &(dev_priv->ring); + int iters = 0; + unsigned long end; + unsigned int last_head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + + end = jiffies + (HZ*3); + while (ring->space < n) { + int i; + + ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; + + if (ring->head != last_head) + end = jiffies + (HZ*3); + + iters++; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("space: %d wanted %d\n", ring->space, n); + DRM_ERROR("lockup\n"); + goto out_wait_ring; + } + + for (i = 0 ; i < 2000 ; i++) ; + } + +out_wait_ring: + return iters; +} + +static void i810_kernel_lost_context(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_ring_buffer_t *ring = &(dev_priv->ring); + + ring->head = I810_READ(LP_RING + RING_HEAD) & HEAD_ADDR; + ring->tail = I810_READ(LP_RING + RING_TAIL); + ring->space = ring->head - (ring->tail+8); + if (ring->space < 0) ring->space += ring->Size; +} + +static int i810_freelist_init(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + int my_idx = 24; + u32 *hw_status = (u32 *)(dev_priv->hw_status_page + my_idx); + int i; + + if(dma->buf_count > 1019) { + /* Not enough space in the status page for the freelist */ + return -EINVAL; + } + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + buf_priv->in_use = hw_status++; + buf_priv->my_use_idx = my_idx; + my_idx += 4; + + *buf_priv->in_use = I810_BUF_FREE; + + buf_priv->kernel_virtual = drm_ioremap(buf->bus_address, + buf->total); + } + return 0; +} + +static int i810_dma_initialize(drm_device_t *dev, + drm_i810_private_t *dev_priv, + drm_i810_init_t *init) +{ + drm_map_t *sarea_map; + + dev->dev_private = (void *) dev_priv; + memset(dev_priv, 0, sizeof(drm_i810_private_t)); + + if (init->ring_map_idx >= dev->map_count || + init->buffer_map_idx >= dev->map_count) { + i810_dma_cleanup(dev); + DRM_ERROR("ring_map or buffer_map are invalid\n"); + return -EINVAL; + } + + dev_priv->ring_map_idx = init->ring_map_idx; + dev_priv->buffer_map_idx = init->buffer_map_idx; + sarea_map = dev->maplist[0]; + dev_priv->sarea_priv = (drm_i810_sarea_t *) + ((u8 *)sarea_map->handle + + init->sarea_priv_offset); + + atomic_set(&dev_priv->flush_done, 0); + init_waitqueue_head(&dev_priv->flush_queue); + + dev_priv->ring.Start = init->ring_start; + dev_priv->ring.End = init->ring_end; + dev_priv->ring.Size = init->ring_size; + + dev_priv->ring.virtual_start = drm_ioremap(dev->agp->base + + init->ring_start, + init->ring_size); + + dev_priv->ring.tail_mask = dev_priv->ring.Size - 1; + + if (dev_priv->ring.virtual_start == NULL) { + i810_dma_cleanup(dev); + DRM_ERROR("can not ioremap virtual address for" + " ring buffer\n"); + return -ENOMEM; + } + + dev_priv->w = init->w; + dev_priv->h = init->h; + dev_priv->pitch = init->pitch; + dev_priv->back_offset = init->back_offset; + dev_priv->depth_offset = init->depth_offset; + + dev_priv->front_di1 = init->front_offset | init->pitch_bits; + dev_priv->back_di1 = init->back_offset | init->pitch_bits; + dev_priv->zi1 = init->depth_offset | init->pitch_bits; + + + /* Program Hardware Status Page */ + dev_priv->hw_status_page = i810_alloc_page(dev); + memset((void *) dev_priv->hw_status_page, 0, PAGE_SIZE); + if(dev_priv->hw_status_page == 0UL) { + i810_dma_cleanup(dev); + DRM_ERROR("Can not allocate hardware status page\n"); + return -ENOMEM; + } + DRM_DEBUG("hw status page @ %lx\n", dev_priv->hw_status_page); + + I810_WRITE(0x02080, virt_to_bus((void *)dev_priv->hw_status_page)); + DRM_DEBUG("Enabled hardware status page\n"); + + /* Now we need to init our freelist */ + if(i810_freelist_init(dev) != 0) { + i810_dma_cleanup(dev); + DRM_ERROR("Not enough space in the status page for" + " the freelist\n"); + return -ENOMEM; + } + return 0; +} + +int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv; + drm_i810_init_t init; + int retcode = 0; + + copy_from_user_ret(&init, (drm_i810_init_t *)arg, + sizeof(init), -EFAULT); + + switch(init.func) { + case I810_INIT_DMA: + dev_priv = drm_alloc(sizeof(drm_i810_private_t), + DRM_MEM_DRIVER); + if(dev_priv == NULL) return -ENOMEM; + retcode = i810_dma_initialize(dev, dev_priv, &init); + break; + case I810_CLEANUP_DMA: + retcode = i810_dma_cleanup(dev); + break; + default: + retcode = -EINVAL; + break; + } + + return retcode; +} + + + +/* Most efficient way to verify state for the i810 is as it is + * emitted. Non-conformant state is silently dropped. + * + * Use 'volatile' & local var tmp to force the emitted values to be + * identical to the verified ones. + */ +static void i810EmitContextVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_CTX_SETUP_SIZE ); + + OUT_RING( GFX_OP_COLOR_FACTOR ); + OUT_RING( code[I810_CTXREG_CF1] ); + + OUT_RING( GFX_OP_STIPPLE ); + OUT_RING( code[I810_CTXREG_ST1] ); + + for ( i = 4 ; i < I810_CTX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + + if ((tmp & (7<<29)) == (3<<29) && + (tmp & (0x1f<<24)) < (0x1d<<24)) + { + OUT_RING( tmp ); + j++; + } + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + +static void i810EmitTexVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + int i, j = 0; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_TEX_SETUP_SIZE ); + + OUT_RING( GFX_OP_MAP_INFO ); + OUT_RING( code[I810_TEXREG_MI1] ); + OUT_RING( code[I810_TEXREG_MI2] ); + OUT_RING( code[I810_TEXREG_MI3] ); + + for ( i = 4 ; i < I810_TEX_SETUP_SIZE ; i++ ) { + tmp = code[i]; + + if ((tmp & (7<<29)) == (3<<29) && + (tmp & (0x1f<<24)) < (0x1d<<24)) + { + OUT_RING( tmp ); + j++; + } + } + + if (j & 1) + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + + +/* Need to do some additional checking when setting the dest buffer. + */ +static void i810EmitDestVerified( drm_device_t *dev, + volatile unsigned int *code ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + unsigned int tmp; + RING_LOCALS; + + BEGIN_LP_RING( I810_DEST_SETUP_SIZE + 2 ); + + tmp = code[I810_DESTREG_DI1]; + if (tmp == dev_priv->front_di1 || tmp == dev_priv->back_di1) { + OUT_RING( CMD_OP_DESTBUFFER_INFO ); + OUT_RING( tmp ); + } else + DRM_DEBUG("bad di1 %x (allow %x or %x)\n", + tmp, dev_priv->front_di1, dev_priv->back_di1); + + /* invarient: + */ + OUT_RING( CMD_OP_Z_BUFFER_INFO ); + OUT_RING( dev_priv->zi1 ); + + OUT_RING( GFX_OP_DESTBUFFER_VARS ); + OUT_RING( code[I810_DESTREG_DV1] ); + + OUT_RING( GFX_OP_DRAWRECT_INFO ); + OUT_RING( code[I810_DESTREG_DR1] ); + OUT_RING( code[I810_DESTREG_DR2] ); + OUT_RING( code[I810_DESTREG_DR3] ); + OUT_RING( code[I810_DESTREG_DR4] ); + OUT_RING( 0 ); + + ADVANCE_LP_RING(); +} + + + +static void i810EmitState( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + + if (dirty & I810_UPLOAD_BUFFERS) { + i810EmitDestVerified( dev, sarea_priv->BufferState ); + sarea_priv->dirty &= ~I810_UPLOAD_BUFFERS; + } + + if (dirty & I810_UPLOAD_CTX) { + i810EmitContextVerified( dev, sarea_priv->ContextState ); + sarea_priv->dirty &= ~I810_UPLOAD_CTX; + } + + if (dirty & I810_UPLOAD_TEX0) { + i810EmitTexVerified( dev, sarea_priv->TexState[0] ); + sarea_priv->dirty &= ~I810_UPLOAD_TEX0; + } + + if (dirty & I810_UPLOAD_TEX1) { + i810EmitTexVerified( dev, sarea_priv->TexState[1] ); + sarea_priv->dirty &= ~I810_UPLOAD_TEX1; + } +} + + + +/* need to verify + */ +static void i810_dma_dispatch_clear( drm_device_t *dev, int flags, + unsigned int clear_color, + unsigned int clear_zval ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = 2; + int i; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox ; i++, pbox++) { + unsigned int x = pbox->x1; + unsigned int y = pbox->y1; + unsigned int width = (pbox->x2 - x) * cpp; + unsigned int height = pbox->y2 - y; + unsigned int start = y * pitch + x * cpp; + + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + if ( flags & I810_FRONT ) { + DRM_DEBUG("clear front\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( start ); + OUT_RING( clear_color ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + + if ( flags & I810_BACK ) { + DRM_DEBUG("clear back\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( dev_priv->back_offset + start ); + OUT_RING( clear_color ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + + if ( flags & I810_DEPTH ) { + DRM_DEBUG("clear depth\n"); + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | + BR00_OP_COLOR_BLT | 0x3 ); + OUT_RING( BR13_SOLID_PATTERN | (0xF0 << 16) | pitch ); + OUT_RING( (height << 16) | width ); + OUT_RING( dev_priv->depth_offset + start ); + OUT_RING( clear_zval ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + } + } +} + +static void i810_dma_dispatch_swap( drm_device_t *dev ) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int pitch = dev_priv->pitch; + int cpp = 2; + int ofs = dev_priv->back_offset; + int i; + RING_LOCALS; + + DRM_DEBUG("swapbuffers\n"); + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + for (i = 0 ; i < nbox; i++, pbox++) + { + unsigned int w = pbox->x2 - pbox->x1; + unsigned int h = pbox->y2 - pbox->y1; + unsigned int dst = pbox->x1*cpp + pbox->y1*pitch; + unsigned int start = ofs + dst; + + if (pbox->x1 > pbox->x2 || + pbox->y1 > pbox->y2 || + pbox->x2 > dev_priv->w || + pbox->y2 > dev_priv->h) + continue; + + DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", + pbox[i].x1, pbox[i].y1, + pbox[i].x2, pbox[i].y2); + + BEGIN_LP_RING( 6 ); + OUT_RING( BR00_BITBLT_CLIENT | BR00_OP_SRC_COPY_BLT | 0x4 ); + OUT_RING( pitch | (0xCC << 16)); + OUT_RING( (h << 16) | (w * cpp)); + OUT_RING( dst ); + OUT_RING( pitch ); + OUT_RING( start ); + ADVANCE_LP_RING(); + } +} + + +static void i810_dma_dispatch_vertex(drm_device_t *dev, + drm_buf_t *buf, + int discard, + int used) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + drm_i810_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_clip_rect_t *box = sarea_priv->boxes; + int nbox = sarea_priv->nbox; + unsigned long address = (unsigned long)buf->bus_address; + unsigned long start = address - dev->agp->base; + int i = 0, u; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + if (nbox > I810_NR_SAREA_CLIPRECTS) + nbox = I810_NR_SAREA_CLIPRECTS; + + if (discard) { + u = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_HARDWARE); + if(u != I810_BUF_CLIENT) { + DRM_DEBUG("xxxx 2\n"); + } + } + + if (used > 4*1024) + used = 0; + + if (sarea_priv->dirty) + i810EmitState( dev ); + + DRM_DEBUG("dispatch vertex addr 0x%lx, used 0x%x nbox %d\n", + address, used, nbox); + + dev_priv->counter++; + DRM_DEBUG( "dispatch counter : %ld\n", dev_priv->counter); + DRM_DEBUG( "i810_dma_dispatch\n"); + DRM_DEBUG( "start : %lx\n", start); + DRM_DEBUG( "used : %d\n", used); + DRM_DEBUG( "start + used - 4 : %ld\n", start + used - 4); + + if (buf_priv->currently_mapped == I810_BUF_MAPPED) { + *(u32 *)buf_priv->virtual = (GFX_OP_PRIMITIVE | + sarea_priv->vertex_prim | + ((used/4)-2)); + + if (used & 4) { + *(u32 *)((u32)buf_priv->virtual + used) = 0; + used += 4; + } + + i810_unmap_buffer(buf); + } + + if (used) { + do { + if (i < nbox) { + BEGIN_LP_RING(4); + OUT_RING( GFX_OP_SCISSOR | SC_UPDATE_SCISSOR | + SC_ENABLE ); + OUT_RING( GFX_OP_SCISSOR_INFO ); + OUT_RING( box[i].x1 | (box[i].y1<<16) ); + OUT_RING( (box[i].x2-1) | ((box[i].y2-1)<<16) ); + ADVANCE_LP_RING(); + } + + BEGIN_LP_RING(4); + OUT_RING( CMD_OP_BATCH_BUFFER ); + OUT_RING( start | BB1_PROTECTED ); + OUT_RING( start + used - 4 ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); + + } while (++i < nbox); + } + + BEGIN_LP_RING(10); + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( 20 ); + OUT_RING( dev_priv->counter ); + OUT_RING( 0 ); + + if (discard) { + OUT_RING( CMD_STORE_DWORD_IDX ); + OUT_RING( buf_priv->my_use_idx ); + OUT_RING( I810_BUF_FREE ); + OUT_RING( 0 ); + } + + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + ADVANCE_LP_RING(); +} + + +/* Interrupts are only for flushing */ +static void i810_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + u16 temp; + + atomic_inc(&dev->total_irq); + temp = I810_READ16(I810REG_INT_IDENTITY_R); + temp = temp & ~(0x6000); + if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, + temp); /* Clear all interrupts */ + else + return; + + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void i810_dma_task_queue(void *device) +{ + drm_device_t *dev = (drm_device_t *) device; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + + atomic_set(&dev_priv->flush_done, 1); + wake_up_interruptible(&dev_priv->flush_queue); +} + +int i810_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + u16 temp; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG( "Interrupt Install : %d\n", irq); + DRM_DEBUG("%d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + + dev->tq.next = NULL; + dev->tq.sync = 0; + dev->tq.routine = i810_dma_task_queue; + dev->tq.data = dev; + + /* Before installing handler */ + temp = I810_READ16(I810REG_HWSTAM); + temp = temp & 0x6000; + I810_WRITE16(I810REG_HWSTAM, temp); + + temp = I810_READ16(I810REG_INT_MASK_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_MASK_R, temp); /* Unmask interrupts */ + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_ENABLE_R, temp); /* Disable all interrupts */ + + /* Install handler */ + if ((retcode = request_irq(dev->irq, + i810_dma_service, + SA_SHIRQ, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + temp = temp | 0x0003; + I810_WRITE16(I810REG_INT_ENABLE_R, + temp); /* Enable bp & user interrupts */ + return 0; +} + +int i810_irq_uninstall(drm_device_t *dev) +{ + int irq; + u16 temp; + + +/* return 0; */ + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + + DRM_DEBUG( "Interrupt UnInstall: %d\n", irq); + DRM_DEBUG("%d\n", irq); + + temp = I810_READ16(I810REG_INT_IDENTITY_R); + temp = temp & ~(0x6000); + if(temp != 0) I810_WRITE16(I810REG_INT_IDENTITY_R, + temp); /* Clear all interrupts */ + + temp = I810_READ16(I810REG_INT_ENABLE_R); + temp = temp & 0x6000; + I810_WRITE16(I810REG_INT_ENABLE_R, + temp); /* Disable all interrupts */ + + free_irq(irq, dev); + + return 0; +} + +int i810_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + int retcode; + + DRM_DEBUG( "i810_control\n"); + + copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); + + switch (ctl.func) { + case DRM_INST_HANDLER: + if ((retcode = i810_irq_install(dev, ctl.irq))) + return retcode; + break; + case DRM_UNINST_HANDLER: + if ((retcode = i810_irq_uninstall(dev))) + return retcode; + break; + default: + return -EINVAL; + } + return 0; +} + +static inline void i810_dma_emit_flush(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + BEGIN_LP_RING(2); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + +/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ +/* atomic_set(&dev_priv->flush_done, 1); */ +/* wake_up_interruptible(&dev_priv->flush_queue); */ +} + +static inline void i810_dma_quiescent_emit(drm_device_t *dev) +{ + drm_i810_private_t *dev_priv = dev->dev_private; + RING_LOCALS; + + i810_kernel_lost_context(dev); + + BEGIN_LP_RING(4); + OUT_RING( INST_PARSER_CLIENT | INST_OP_FLUSH | INST_FLUSH_MAP_CACHE ); + OUT_RING( CMD_REPORT_HEAD ); + OUT_RING( 0 ); + OUT_RING( GFX_OP_USER_INTERRUPT ); + ADVANCE_LP_RING(); + +/* i810_wait_ring( dev, dev_priv->ring.Size - 8 ); */ +/* atomic_set(&dev_priv->flush_done, 1); */ +/* wake_up_interruptible(&dev_priv->flush_queue); */ +} + +static void i810_dma_quiescent(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + unsigned long end; + + if(dev_priv == NULL) { + return; + } + atomic_set(&dev_priv->flush_done, 0); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + + for (;;) { + i810_dma_quiescent_emit(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + return; +} + +static int i810_flush_queue(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + drm_device_dma_t *dma = dev->dma; + unsigned long end; + int i, ret = 0; + + if(dev_priv == NULL) { + return 0; + } + atomic_set(&dev_priv->flush_done, 0); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&dev_priv->flush_queue, &entry); + end = jiffies + (HZ*3); + for (;;) { + i810_dma_emit_flush(dev); + if (atomic_read(&dev_priv->flush_done) == 1) break; + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("lockup\n"); + break; + } + schedule_timeout(HZ*3); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + int used = cmpxchg(buf_priv->in_use, I810_BUF_HARDWARE, + I810_BUF_FREE); + + if (used == I810_BUF_HARDWARE) + DRM_DEBUG("reclaimed from HARDWARE\n"); + if (used == I810_BUF_CLIENT) + DRM_DEBUG("still on client HARDWARE\n"); + } + + return ret; +} + +/* Must be called with the lock held */ +void i810_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + if (!dev->dev_private) return; + if (!dma->buflist) return; + + i810_flush_queue(dev); + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_i810_buf_priv_t *buf_priv = buf->dev_private; + + if (buf->pid == pid && buf_priv) { + int used = cmpxchg(buf_priv->in_use, I810_BUF_CLIENT, + I810_BUF_FREE); + + if (used == I810_BUF_CLIENT) + DRM_DEBUG("reclaimed from client\n"); + if(buf_priv->currently_mapped == I810_BUF_MAPPED) + buf_priv->currently_mapped = I810_BUF_UNMAPPED; + } + } +} + +int i810_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0) { + return -EINVAL; + } + /* Only one queue: + */ + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + DRM_DEBUG("Calling lock schedule\n"); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + if (lock.flags & _DRM_LOCK_QUIESCENT) { + DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); + DRM_DEBUG("fred\n"); + i810_dma_quiescent(dev); + } + } + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + return ret; +} + +int i810_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i810_flush_ioctl\n"); + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_flush_ioctl called without lock held\n"); + return -EINVAL; + } + + i810_flush_queue(dev); + return 0; +} + + +int i810_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + drm_i810_vertex_t vertex; + + copy_from_user_ret(&vertex, (drm_i810_vertex_t *)arg, sizeof(vertex), + -EFAULT); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma_vertex called without lock held\n"); + return -EINVAL; + } + + DRM_DEBUG("i810 dma vertex, idx %d used %d discard %d\n", + vertex.idx, vertex.used, vertex.discard); + + i810_dma_dispatch_vertex( dev, + dma->buflist[ vertex.idx ], + vertex.discard, vertex.used ); + + atomic_add(vertex.used, &dma->total_bytes); + atomic_inc(&dma->total_dmas); + sarea_priv->last_enqueue = dev_priv->counter-1; + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + + + +int i810_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_clear_t clear; + + copy_from_user_ret(&clear, (drm_i810_clear_t *)arg, sizeof(clear), + -EFAULT); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_clear_bufs called without lock held\n"); + return -EINVAL; + } + + i810_dma_dispatch_clear( dev, clear.flags, + clear.clear_color, + clear.clear_depth ); + return 0; +} + +int i810_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + DRM_DEBUG("i810_swap_bufs\n"); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_swap_buf called without lock held\n"); + return -EINVAL; + } + + i810_dma_dispatch_swap( dev ); + return 0; +} + +int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + + sarea_priv->last_dispatch = (int) hw_status[5]; + return 0; +} + +int i810_getbuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_i810_dma_t d; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + + DRM_DEBUG("getbuf\n"); + copy_from_user_ret(&d, (drm_i810_dma_t *)arg, sizeof(d), -EFAULT); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma called without lock held\n"); + return -EINVAL; + } + + d.granted = 0; + + retcode = i810_dma_get_buffer(dev, &d, filp); + + DRM_DEBUG("i810_dma: %d returning %d, granted = %d\n", + current->pid, retcode, d.granted); + + copy_to_user_ret((drm_dma_t *)arg, &d, sizeof(d), -EFAULT); + sarea_priv->last_dispatch = (int) hw_status[5]; + + return retcode; +} + +int i810_copybuf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_i810_copy_t d; + drm_i810_private_t *dev_priv = (drm_i810_private_t *)dev->dev_private; + u32 *hw_status = (u32 *)dev_priv->hw_status_page; + drm_i810_sarea_t *sarea_priv = (drm_i810_sarea_t *) + dev_priv->sarea_priv; + drm_buf_t *buf; + drm_i810_buf_priv_t *buf_priv; + drm_device_dma_t *dma = dev->dma; + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("i810_dma called without lock held\n"); + return -EINVAL; + } + + copy_from_user_ret(&d, (drm_i810_copy_t *)arg, sizeof(d), -EFAULT); + + if(d.idx > dma->buf_count) return -EINVAL; + buf = dma->buflist[ d.idx ]; + buf_priv = buf->dev_private; + if (buf_priv->currently_mapped != I810_BUF_MAPPED) return -EPERM; + + copy_from_user_ret(buf_priv->virtual, d.address, d.used, -EFAULT); + + sarea_priv->last_dispatch = (int) hw_status[5]; + + return 0; +} + +int i810_docopy(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + if(VM_DONTCOPY == 0) return 1; + return 0; +} diff --git a/drivers/char/drm/i810_drm.h b/drivers/char/drm/i810_drm.h new file mode 100644 index 000000000000..c5f51c9ad127 --- /dev/null +++ b/drivers/char/drm/i810_drm.h @@ -0,0 +1,194 @@ +#ifndef _I810_DRM_H_ +#define _I810_DRM_H_ + +/* WARNING: These defines must be the same as what the Xserver uses. + * if you change them, you must change the defines in the Xserver. + */ + +#ifndef _I810_DEFINES_ +#define _I810_DEFINES_ + +#define I810_DMA_BUF_ORDER 12 +#define I810_DMA_BUF_SZ (1< + * Jeff Hartmann + * + */ + +#include +#include "drmP.h" +#include "i810_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#define I810_NAME "i810" +#define I810_DESC "Intel I810" +#define I810_DATE "20000719" +#define I810_MAJOR 1 +#define I810_MINOR 1 +#define I810_PATCHLEVEL 0 + +static drm_device_t i810_device; +drm_ctx_t i810_res_ctx; + +static struct file_operations i810_fops = { +#if LINUX_VERSION_CODE >= 0x020322 + /* This started being used approx. 2.3.34 */ + owner: THIS_MODULE, +#endif + open: i810_open, + flush: drm_flush, + release: i810_release, + ioctl: i810_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice i810_misc = { + minor: MISC_DYNAMIC_MINOR, + name: I810_NAME, + fops: &i810_fops, +}; + +static drm_ioctl_desc_t i810_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { i810_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { i810_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { i810_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { i810_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { i810_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { i810_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { i810_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { i810_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { i810_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { i810_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { i810_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { i810_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { i810_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { i810_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { i810_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_I810_INIT)] = { i810_dma_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_VERTEX)] = { i810_dma_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_CLEAR)] = { i810_clear_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_FLUSH)] = { i810_flush_ioctl,1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETAGE)] = { i810_getage, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_GETBUF)] = { i810_getbuf, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_SWAP)] = { i810_swap_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_COPY)] = { i810_copybuf, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_I810_DOCOPY)] = { i810_docopy, 1, 0 }, +}; + +#define I810_IOCTL_COUNT DRM_ARRAY_SIZE(i810_ioctls) + +#ifdef MODULE +static char *i810 = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("Intel I810"); +MODULE_PARM(i810, "s"); + +#ifndef MODULE +/* i810_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init i810_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("i810=", i810_options); +#endif + +static int i810_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); +#if DRM_DMA_HISTO + memset(&dev->histo, 0, sizeof(dev->histo)); +#endif + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int i810_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) i810_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until r128_cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired && drm_agp.release) + (*drm_agp.release)(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* i810_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int i810_init(void) +{ + int retcode; + drm_device_t *dev = &i810_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(i810); +#endif + DRM_DEBUG("doing misc_register\n"); + if ((retcode = misc_register(&i810_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", I810_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, i810_misc.minor); + dev->name = I810_NAME; + + DRM_DEBUG("doing mem init\n"); + drm_mem_init(); + DRM_DEBUG("doing proc init\n"); + drm_proc_init(dev); + DRM_DEBUG("doing agp init\n"); + dev->agp = drm_agp_init(); + if(dev->agp == NULL) { + DRM_INFO("The i810 drm module requires the agpgart module" + " to function correctly\nPlease load the agpgart" + " module before you load the i810 module\n"); + drm_proc_cleanup(); + misc_deregister(&i810_misc); + i810_takedown(dev); + return -ENOMEM; + } + DRM_DEBUG("doing ctxbitmap init\n"); + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&i810_misc); + i810_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + I810_NAME, + I810_MAJOR, + I810_MINOR, + I810_PATCHLEVEL, + I810_DATE, + i810_misc.minor); + + return 0; +} + +/* i810_cleanup is called via cleanup_module at module unload time. */ + +static void i810_cleanup(void) +{ + drm_device_t *dev = &i810_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&i810_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + i810_takedown(dev); + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +} + +module_init(i810_init); +module_exit(i810_cleanup); + + +int i810_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = I810_MAJOR; + version.version_minor = I810_MINOR; + version.version_patchlevel = I810_PATCHLEVEL; + + DRM_COPY(version.name, I810_NAME); + DRM_COPY(version.date, I810_DATE); + DRM_COPY(version.desc, I810_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int i810_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &i810_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return i810_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int i810_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + i810_reclaim_buffers(dev, priv->pid); + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } else if (dev->lock.hw_lock) { + /* The lock is required to reclaim buffers */ + DECLARE_WAITQUEUE(entry, current); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + retcode = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + dev->lock.pid = priv->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + retcode = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + if(!retcode) { + i810_reclaim_buffers(dev, priv->pid); + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); + } + } + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return i810_takedown(dev); + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return retcode; +} + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int i810_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= I810_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &i810_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int i810_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lhld[drm_histogram_slot(get_cycles() + - dev->lck_start)]); +#endif + + return 0; +} diff --git a/drivers/char/drm/i810_drv.h b/drivers/char/drm/i810_drv.h new file mode 100644 index 000000000000..1c957401717a --- /dev/null +++ b/drivers/char/drm/i810_drv.h @@ -0,0 +1,225 @@ +/* i810_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#ifndef _I810_DRV_H_ +#define _I810_DRV_H_ + +typedef struct drm_i810_buf_priv { + u32 *in_use; + int my_use_idx; + int currently_mapped; + void *virtual; + void *kernel_virtual; + int map_count; + struct vm_area_struct *vma; +} drm_i810_buf_priv_t; + +typedef struct _drm_i810_ring_buffer{ + int tail_mask; + unsigned long Start; + unsigned long End; + unsigned long Size; + u8 *virtual_start; + int head; + int tail; + int space; +} drm_i810_ring_buffer_t; + +typedef struct drm_i810_private { + int ring_map_idx; + int buffer_map_idx; + + drm_i810_ring_buffer_t ring; + drm_i810_sarea_t *sarea_priv; + + unsigned long hw_status_page; + unsigned long counter; + + atomic_t flush_done; + wait_queue_head_t flush_queue; /* Processes waiting until flush */ + drm_buf_t *mmap_buffer; + + + u32 front_di1, back_di1, zi1; + + int back_offset; + int depth_offset; + int w, h; + int pitch; +} drm_i810_private_t; + + /* i810_drv.c */ +extern int i810_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_open(struct inode *inode, struct file *filp); +extern int i810_release(struct inode *inode, struct file *filp); +extern int i810_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_dma.c */ +extern int i810_dma_schedule(drm_device_t *dev, int locked); +extern int i810_getbuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_irq_install(drm_device_t *dev, int irq); +extern int i810_irq_uninstall(drm_device_t *dev); +extern int i810_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern void i810_reclaim_buffers(drm_device_t *dev, pid_t pid); +extern int i810_getage(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg); +extern int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma); +extern int i810_copybuf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_docopy(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_bufs.c */ +extern int i810_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* i810_context.c */ +extern int i810_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int i810_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int i810_context_switch(drm_device_t *dev, int old, int new); +extern int i810_context_switch_complete(drm_device_t *dev, int new); + +#define I810_VERBOSE 0 + + +int i810_dma_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +int i810_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +int i810_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +#define GFX_OP_USER_INTERRUPT ((0<<29)|(2<<23)) +#define GFX_OP_BREAKPOINT_INTERRUPT ((0<<29)|(1<<23)) +#define CMD_REPORT_HEAD (7<<23) +#define CMD_STORE_DWORD_IDX ((0x21<<23) | 0x1) +#define CMD_OP_BATCH_BUFFER ((0x0<<29)|(0x30<<23)|0x1) + +#define INST_PARSER_CLIENT 0x00000000 +#define INST_OP_FLUSH 0x02000000 +#define INST_FLUSH_MAP_CACHE 0x00000001 + + +#define BB1_START_ADDR_MASK (~0x7) +#define BB1_PROTECTED (1<<0) +#define BB1_UNPROTECTED (0<<0) +#define BB2_END_ADDR_MASK (~0x7) + +#define I810REG_HWSTAM 0x02098 +#define I810REG_INT_IDENTITY_R 0x020a4 +#define I810REG_INT_MASK_R 0x020a8 +#define I810REG_INT_ENABLE_R 0x020a0 + +#define LP_RING 0x2030 +#define HP_RING 0x2040 +#define RING_TAIL 0x00 +#define TAIL_ADDR 0x000FFFF8 +#define RING_HEAD 0x04 +#define HEAD_WRAP_COUNT 0xFFE00000 +#define HEAD_WRAP_ONE 0x00200000 +#define HEAD_ADDR 0x001FFFFC +#define RING_START 0x08 +#define START_ADDR 0x00FFFFF8 +#define RING_LEN 0x0C +#define RING_NR_PAGES 0x000FF000 +#define RING_REPORT_MASK 0x00000006 +#define RING_REPORT_64K 0x00000002 +#define RING_REPORT_128K 0x00000004 +#define RING_NO_REPORT 0x00000000 +#define RING_VALID_MASK 0x00000001 +#define RING_VALID 0x00000001 +#define RING_INVALID 0x00000000 + +#define GFX_OP_SCISSOR ((0x3<<29)|(0x1c<<24)|(0x10<<19)) +#define SC_UPDATE_SCISSOR (0x1<<1) +#define SC_ENABLE_MASK (0x1<<0) +#define SC_ENABLE (0x1<<0) + +#define GFX_OP_SCISSOR_INFO ((0x3<<29)|(0x1d<<24)|(0x81<<16)|(0x1)) +#define SCI_YMIN_MASK (0xffff<<16) +#define SCI_XMIN_MASK (0xffff<<0) +#define SCI_YMAX_MASK (0xffff<<16) +#define SCI_XMAX_MASK (0xffff<<0) + +#define GFX_OP_COLOR_FACTOR ((0x3<<29)|(0x1d<<24)|(0x1<<16)|0x0) +#define GFX_OP_STIPPLE ((0x3<<29)|(0x1d<<24)|(0x83<<16)) +#define GFX_OP_MAP_INFO ((0x3<<29)|(0x1d<<24)|0x2) +#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0) +#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3)) +#define GFX_OP_PRIMITIVE ((0x3<<29)|(0x1f<<24)) + +#define CMD_OP_Z_BUFFER_INFO ((0x0<<29)|(0x16<<23)) +#define CMD_OP_DESTBUFFER_INFO ((0x0<<29)|(0x15<<23)) + +#define BR00_BITBLT_CLIENT 0x40000000 +#define BR00_OP_COLOR_BLT 0x10000000 +#define BR00_OP_SRC_COPY_BLT 0x10C00000 +#define BR13_SOLID_PATTERN 0x80000000 + + + +#endif + diff --git a/drivers/char/drm/init.c b/drivers/char/drm/init.c new file mode 100644 index 000000000000..8de3dac9be25 --- /dev/null +++ b/drivers/char/drm/init.c @@ -0,0 +1,113 @@ +/* init.c -- Setup/Cleanup for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_flags = 0; + +/* drm_parse_option parses a single option. See description for + drm_parse_options for details. */ + +static void drm_parse_option(char *s) +{ + char *c, *r; + + DRM_DEBUG("\"%s\"\n", s); + if (!s || !*s) return; + for (c = s; *c && *c != ':'; c++); /* find : or \0 */ + if (*c) r = c + 1; else r = NULL; /* remember remainder */ + *c = '\0'; /* terminate */ + if (!strcmp(s, "noctx")) { + drm_flags |= DRM_FLAG_NOCTX; + DRM_INFO("Server-mediated context switching OFF\n"); + return; + } + if (!strcmp(s, "debug")) { + drm_flags |= DRM_FLAG_DEBUG; + DRM_INFO("Debug messages ON\n"); + return; + } + DRM_ERROR("\"%s\" is not a valid option\n", s); + return; +} + +/* drm_parse_options parse the insmod "drm=" options, or the command-line + * options passed to the kernel via LILO. The grammar of the format is as + * follows: + * + * drm ::= 'drm=' option_list + * option_list ::= option [ ';' option_list ] + * option ::= 'device:' major + * | 'debug' + * | 'noctx' + * major ::= INTEGER + * + * Note that 's' contains option_list without the 'drm=' part. + * + * device=major,minor specifies the device number used for /dev/drm + * if major == 0 then the misc device is used + * if major == 0 and minor == 0 then dynamic misc allocation is used + * debug=on specifies that debugging messages will be printk'd + * debug=trace specifies that each function call will be logged via printk + * debug=off turns off all debugging options + * + */ + +void drm_parse_options(char *s) +{ + char *h, *t, *n; + + DRM_DEBUG("\"%s\"\n", s ?: ""); + if (!s || !*s) return; + + for (h = t = n = s; h && *h; h = n) { + for (; *t && *t != ';'; t++); /* find ; or \0 */ + if (*t) n = t + 1; else n = NULL; /* remember next */ + *t = '\0'; /* terminate */ + drm_parse_option(h); /* parse */ + } +} + +/* drm_cpu_valid returns non-zero if the DRI will run on this CPU, and 0 + * otherwise. */ + +int drm_cpu_valid(void) +{ +#if defined(__i386__) + if (boot_cpu_data.x86 == 3) return 0; /* No cmpxchg on a 386 */ +#endif +#if defined(__sparc__) && !defined(__sparc_v9__) + if (1) + return 0; /* No cmpxchg before v9 sparc. */ +#endif + return 1; +} diff --git a/drivers/char/drm/ioctl.c b/drivers/char/drm/ioctl.c new file mode 100644 index 000000000000..b246f76e5980 --- /dev/null +++ b/drivers/char/drm/ioctl.c @@ -0,0 +1,91 @@ +/* ioctl.c -- IOCTL processing for DRM -*- linux-c -*- + * Created: Fri Jan 8 09:01:26 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_irq_busid(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_irq_busid_t p; + struct pci_dev *dev; + + copy_from_user_ret(&p, (drm_irq_busid_t *)arg, sizeof(p), -EFAULT); + dev = pci_find_slot(p.busnum, PCI_DEVFN(p.devnum, p.funcnum)); + if (dev) p.irq = dev->irq; + else p.irq = 0; + DRM_DEBUG("%d:%d:%d => IRQ %d\n", + p.busnum, p.devnum, p.funcnum, p.irq); + copy_to_user_ret((drm_irq_busid_t *)arg, &p, sizeof(p), -EFAULT); + return 0; +} + +int drm_getunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT); + if (u.unique_len >= dev->unique_len) { + copy_to_user_ret(u.unique, dev->unique, dev->unique_len, + -EFAULT); + } + u.unique_len = dev->unique_len; + copy_to_user_ret((drm_unique_t *)arg, &u, sizeof(u), -EFAULT); + return 0; +} + +int drm_setunique(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_unique_t u; + + if (dev->unique_len || dev->unique) return -EBUSY; + + copy_from_user_ret(&u, (drm_unique_t *)arg, sizeof(u), -EFAULT); + if (!u.unique_len) return -EINVAL; + + dev->unique_len = u.unique_len; + dev->unique = drm_alloc(u.unique_len + 1, DRM_MEM_DRIVER); + copy_from_user_ret(dev->unique, u.unique, dev->unique_len, + -EFAULT); + dev->unique[dev->unique_len] = '\0'; + + dev->devname = drm_alloc(strlen(dev->name) + strlen(dev->unique) + 2, + DRM_MEM_DRIVER); + sprintf(dev->devname, "%s@%s", dev->name, dev->unique); + + return 0; +} diff --git a/drivers/char/drm/lists.c b/drivers/char/drm/lists.c new file mode 100644 index 000000000000..f62495aa21d2 --- /dev/null +++ b/drivers/char/drm/lists.c @@ -0,0 +1,245 @@ +/* lists.c -- Buffer list handling routines -*- linux-c -*- + * Created: Mon Apr 19 20:54:22 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_waitlist_create(drm_waitlist_t *bl, int count) +{ + DRM_DEBUG("%d\n", count); + if (bl->count) return -EINVAL; + + bl->count = count; + bl->bufs = drm_alloc((bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->rp = bl->bufs; + bl->wp = bl->bufs; + bl->end = &bl->bufs[bl->count+1]; + bl->write_lock = SPIN_LOCK_UNLOCKED; + bl->read_lock = SPIN_LOCK_UNLOCKED; + return 0; +} + +int drm_waitlist_destroy(drm_waitlist_t *bl) +{ + DRM_DEBUG("\n"); + if (bl->rp != bl->wp) return -EINVAL; + if (bl->bufs) drm_free(bl->bufs, + (bl->count + 2) * sizeof(*bl->bufs), + DRM_MEM_BUFLISTS); + bl->count = 0; + bl->bufs = NULL; + bl->rp = NULL; + bl->wp = NULL; + bl->end = NULL; + return 0; +} + +int drm_waitlist_put(drm_waitlist_t *bl, drm_buf_t *buf) +{ + int left; + unsigned long flags; + + left = DRM_LEFTCOUNT(bl); + DRM_DEBUG("put %d (%d left, rp = %p, wp = %p)\n", + buf->idx, left, bl->rp, bl->wp); + if (!left) { + DRM_ERROR("Overflow while adding buffer %d from pid %d\n", + buf->idx, buf->pid); + return -EINVAL; + } +#if DRM_DMA_HISTOGRAM + buf->time_queued = get_cycles(); +#endif + buf->list = DRM_LIST_WAIT; + + spin_lock_irqsave(&bl->write_lock, flags); + *bl->wp = buf; + if (++bl->wp >= bl->end) bl->wp = bl->bufs; + spin_unlock_irqrestore(&bl->write_lock, flags); + + return 0; +} + +drm_buf_t *drm_waitlist_get(drm_waitlist_t *bl) +{ + drm_buf_t *buf; + unsigned long flags; + + spin_lock_irqsave(&bl->read_lock, flags); + buf = *bl->rp; + if (bl->rp == bl->wp) { + spin_unlock_irqrestore(&bl->read_lock, flags); + return NULL; + } + if (++bl->rp >= bl->end) bl->rp = bl->bufs; + spin_unlock_irqrestore(&bl->read_lock, flags); + + DRM_DEBUG("get %d\n", buf->idx); + return buf; +} + +int drm_freelist_create(drm_freelist_t *bl, int count) +{ + DRM_DEBUG("\n"); + atomic_set(&bl->count, 0); + bl->next = NULL; + init_waitqueue_head(&bl->waiting); + bl->low_mark = 0; + bl->high_mark = 0; + atomic_set(&bl->wfh, 0); + ++bl->initialized; + return 0; +} + +int drm_freelist_destroy(drm_freelist_t *bl) +{ + DRM_DEBUG("\n"); + atomic_set(&bl->count, 0); + bl->next = NULL; + return 0; +} + +int drm_freelist_put(drm_device_t *dev, drm_freelist_t *bl, drm_buf_t *buf) +{ + drm_buf_t *old, *prev; + int count = 0; + drm_device_dma_t *dma = dev->dma; + + if (!dma) { + DRM_ERROR("No DMA support\n"); + return 1; + } + + if (buf->waiting || buf->pending || buf->list == DRM_LIST_FREE) { + DRM_ERROR("Freed buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n", + buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh), + buf->waiting, buf->pending); + if (!bl) return 1; +#if DRM_DMA_HISTOGRAM + buf->time_freed = get_cycles(); + drm_histogram_compute(dev, buf); +#endif + buf->list = DRM_LIST_FREE; + do { + old = bl->next; + buf->next = old; + prev = cmpxchg(&bl->next, old, buf); + if (++count > DRM_LOOPING_LIMIT) { + DRM_ERROR("Looping\n"); + return 1; + } + } while (prev != old); + atomic_inc(&bl->count); + if (atomic_read(&bl->count) > dma->buf_count) { + DRM_ERROR("%d of %d buffers free after addition of %d\n", + atomic_read(&bl->count), dma->buf_count, buf->idx); + return 1; + } + /* Check for high water mark */ + if (atomic_read(&bl->wfh) && atomic_read(&bl->count)>=bl->high_mark) { + atomic_set(&bl->wfh, 0); + wake_up_interruptible(&bl->waiting); + } + return 0; +} + +static drm_buf_t *drm_freelist_try(drm_freelist_t *bl) +{ + drm_buf_t *old, *new, *prev; + drm_buf_t *buf; + int count = 0; + + if (!bl) return NULL; + + /* Get buffer */ + do { + old = bl->next; + if (!old) return NULL; + new = bl->next->next; + prev = cmpxchg(&bl->next, old, new); + if (++count > DRM_LOOPING_LIMIT) { + DRM_ERROR("Looping\n"); + return NULL; + } + } while (prev != old); + atomic_dec(&bl->count); + + buf = old; + buf->next = NULL; + buf->list = DRM_LIST_NONE; + DRM_DEBUG("%d, count = %d, wfh = %d, w%d, p%d\n", + buf->idx, atomic_read(&bl->count), atomic_read(&bl->wfh), + buf->waiting, buf->pending); + if (buf->waiting || buf->pending) { + DRM_ERROR("Free buffer %d: w%d, p%d, l%d\n", + buf->idx, buf->waiting, buf->pending, buf->list); + } + + return buf; +} + +drm_buf_t *drm_freelist_get(drm_freelist_t *bl, int block) +{ + drm_buf_t *buf = NULL; + DECLARE_WAITQUEUE(entry, current); + + if (!bl || !bl->initialized) return NULL; + + /* Check for low water mark */ + if (atomic_read(&bl->count) <= bl->low_mark) /* Became low */ + atomic_set(&bl->wfh, 1); + if (atomic_read(&bl->wfh)) { + DRM_DEBUG("Block = %d, count = %d, wfh = %d\n", + block, atomic_read(&bl->count), + atomic_read(&bl->wfh)); + if (block) { + add_wait_queue(&bl->waiting, &entry); + current->state = TASK_INTERRUPTIBLE; + for (;;) { + if (!atomic_read(&bl->wfh) + && (buf = drm_freelist_try(bl))) break; + schedule(); + if (signal_pending(current)) break; + } + current->state = TASK_RUNNING; + remove_wait_queue(&bl->waiting, &entry); + } + return buf; + } + + DRM_DEBUG("Count = %d, wfh = %d\n", + atomic_read(&bl->count), atomic_read(&bl->wfh)); + return drm_freelist_try(bl); +} diff --git a/drivers/char/drm/lock.c b/drivers/char/drm/lock.c new file mode 100644 index 000000000000..550827278084 --- /dev/null +++ b/drivers/char/drm/lock.c @@ -0,0 +1,225 @@ +/* lock.c -- IOCTLs for locking -*- linux-c -*- + * Created: Tue Feb 2 08:37:54 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +int drm_block(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_unblock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + DRM_DEBUG("\n"); + return 0; +} + +int drm_lock_take(__volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + DRM_DEBUG("%d attempts\n", context); + do { + old = *lock; + if (old & _DRM_LOCK_HELD) new = old | _DRM_LOCK_CONT; + else new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCKING_CONTEXT(old) == context) { + if (old & _DRM_LOCK_HELD) { + if (context != DRM_KERNEL_CONTEXT) { + DRM_ERROR("%d holds heavyweight lock\n", + context); + } + return 0; + } + } + if (new == (context | _DRM_LOCK_HELD)) { + /* Have lock */ + DRM_DEBUG("%d\n", context); + return 1; + } + DRM_DEBUG("%d unable to get lock held by %d\n", + context, _DRM_LOCKING_CONTEXT(old)); + return 0; +} + +/* This takes a lock forcibly and hands it to context. Should ONLY be used + inside *_unlock to give lock to kernel before calling *_dma_schedule. */ +int drm_lock_transfer(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + + dev->lock.pid = 0; + do { + old = *lock; + new = context | _DRM_LOCK_HELD; + prev = cmpxchg(lock, old, new); + } while (prev != old); + DRM_DEBUG("%d => %d\n", _DRM_LOCKING_CONTEXT(old), context); + return 1; +} + +int drm_lock_free(drm_device_t *dev, + __volatile__ unsigned int *lock, unsigned int context) +{ + unsigned int old, new, prev; + pid_t pid = dev->lock.pid; + + DRM_DEBUG("%d\n", context); + dev->lock.pid = 0; + do { + old = *lock; + new = 0; + prev = cmpxchg(lock, old, new); + } while (prev != old); + if (_DRM_LOCK_IS_HELD(old) && _DRM_LOCKING_CONTEXT(old) != context) { + DRM_ERROR("%d freed heavyweight lock held by %d (pid %d)\n", + context, + _DRM_LOCKING_CONTEXT(old), + pid); + return 1; + } + wake_up_interruptible(&dev->lock.lock_queue); + return 0; +} + +static int drm_flush_queue(drm_device_t *dev, int context) +{ + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + atomic_inc(&q->block_write); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&q->flush_queue, &entry); + atomic_inc(&q->block_count); + for (;;) { + if (!DRM_BUFCOUNT(&q->waitlist)) break; + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + break; + } + } + atomic_dec(&q->block_count); + current->state = TASK_RUNNING; + remove_wait_queue(&q->flush_queue, &entry); + } + atomic_dec(&q->use_count); + atomic_inc(&q->total_flushed); + + /* NOTE: block_write is still incremented! + Use drm_flush_unlock_queue to decrement. */ + return ret; +} + +static int drm_flush_unblock_queue(drm_device_t *dev, int context) +{ + drm_queue_t *q = dev->queuelist[context]; + + DRM_DEBUG("\n"); + + atomic_inc(&q->use_count); + if (atomic_read(&q->use_count) > 1) { + if (atomic_read(&q->block_write)) { + atomic_dec(&q->block_write); + wake_up_interruptible(&q->write_queue); + } + } + atomic_dec(&q->use_count); + return 0; +} + +int drm_flush_block_and_flush(drm_device_t *dev, int context, + drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_queue(dev, i); + } + } + return ret; +} + +int drm_flush_unblock(drm_device_t *dev, int context, drm_lock_flags_t flags) +{ + int ret = 0; + int i; + + DRM_DEBUG("\n"); + + if (flags & _DRM_LOCK_FLUSH) { + ret = drm_flush_unblock_queue(dev, DRM_KERNEL_CONTEXT); + if (!ret) ret = drm_flush_unblock_queue(dev, context); + } + if (flags & _DRM_LOCK_FLUSH_ALL) { + for (i = 0; !ret && i < dev->queue_count; i++) { + ret = drm_flush_unblock_queue(dev, i); + } + } + + return ret; +} + +int drm_finish(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("\n"); + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + ret = drm_flush_block_and_flush(dev, lock.context, lock.flags); + drm_flush_unblock(dev, lock.context, lock.flags); + return ret; +} diff --git a/drivers/char/drm/memory.c b/drivers/char/drm/memory.c new file mode 100644 index 000000000000..5023de808920 --- /dev/null +++ b/drivers/char/drm/memory.c @@ -0,0 +1,449 @@ +/* memory.c -- Memory management wrappers for DRM -*- linux-c -*- + * Created: Thu Feb 4 14:00:34 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" + +typedef struct drm_mem_stats { + const char *name; + int succeed_count; + int free_count; + int fail_count; + unsigned long bytes_allocated; + unsigned long bytes_freed; +} drm_mem_stats_t; + +static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED; +static unsigned long drm_ram_available = 0; /* In pages */ +static unsigned long drm_ram_used = 0; +static drm_mem_stats_t drm_mem_stats[] = { + [DRM_MEM_DMA] = { "dmabufs" }, + [DRM_MEM_SAREA] = { "sareas" }, + [DRM_MEM_DRIVER] = { "driver" }, + [DRM_MEM_MAGIC] = { "magic" }, + [DRM_MEM_IOCTLS] = { "ioctltab" }, + [DRM_MEM_MAPS] = { "maplist" }, + [DRM_MEM_VMAS] = { "vmalist" }, + [DRM_MEM_BUFS] = { "buflist" }, + [DRM_MEM_SEGS] = { "seglist" }, + [DRM_MEM_PAGES] = { "pagelist" }, + [DRM_MEM_FILES] = { "files" }, + [DRM_MEM_QUEUES] = { "queues" }, + [DRM_MEM_CMDS] = { "commands" }, + [DRM_MEM_MAPPINGS] = { "mappings" }, + [DRM_MEM_BUFLISTS] = { "buflists" }, + [DRM_MEM_AGPLISTS] = { "agplist" }, + [DRM_MEM_TOTALAGP] = { "totalagp" }, + [DRM_MEM_BOUNDAGP] = { "boundagp" }, + [DRM_MEM_CTXBITMAP] = { "ctxbitmap"}, + { NULL, 0, } /* Last entry must be null */ +}; + +void drm_mem_init(void) +{ + drm_mem_stats_t *mem; + struct sysinfo si; + + for (mem = drm_mem_stats; mem->name; ++mem) { + mem->succeed_count = 0; + mem->free_count = 0; + mem->fail_count = 0; + mem->bytes_allocated = 0; + mem->bytes_freed = 0; + } + + si_meminfo(&si); +#if LINUX_VERSION_CODE < 0x020317 + /* Changed to page count in 2.3.23 */ + drm_ram_available = si.totalram >> PAGE_SHIFT; +#else + drm_ram_available = si.totalram; +#endif + drm_ram_used = 0; +} + +/* drm_mem_info is called whenever a process reads /dev/drm/mem. */ + +static int _drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_mem_stats_t *pt; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" total counts " + " | outstanding \n"); + DRM_PROC_PRINT("type alloc freed fail bytes freed" + " | allocs bytes\n\n"); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "system", 0, 0, 0, + drm_ram_available << (PAGE_SHIFT - 10)); + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu kB |\n", + "locked", 0, 0, 0, drm_ram_used >> 10); + DRM_PROC_PRINT("\n"); + for (pt = drm_mem_stats; pt->name; pt++) { + DRM_PROC_PRINT("%-9.9s %5d %5d %4d %10lu %10lu | %6d %10ld\n", + pt->name, + pt->succeed_count, + pt->free_count, + pt->fail_count, + pt->bytes_allocated, + pt->bytes_freed, + pt->succeed_count - pt->free_count, + (long)pt->bytes_allocated + - (long)pt->bytes_freed); + } + + return len; +} + +int drm_mem_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + int ret; + + spin_lock(&drm_mem_lock); + ret = _drm_mem_info(buf, start, offset, len, eof, data); + spin_unlock(&drm_mem_lock); + return ret; +} + +void *drm_alloc(size_t size, int area) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(area, "Allocating 0 bytes\n"); + return NULL; + } + + if (!(pt = kmalloc(size, GFP_KERNEL))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void *drm_realloc(void *oldpt, size_t oldsize, size_t size, int area) +{ + void *pt; + + if (!(pt = drm_alloc(size, area))) return NULL; + if (oldpt && oldsize) { + memcpy(pt, oldpt, oldsize); + drm_free(oldpt, oldsize, area); + } + return pt; +} + +char *drm_strdup(const char *s, int area) +{ + char *pt; + int length = s ? strlen(s) : 0; + + if (!(pt = drm_alloc(length+1, area))) return NULL; + strcpy(pt, s); + return pt; +} + +void drm_strfree(const char *s, int area) +{ + unsigned int size; + + if (!s) return; + + size = 1 + (s ? strlen(s) : 0); + drm_free((void *)s, size, area); +} + +void drm_free(void *pt, size_t size, int area) +{ + int alloc_count; + int free_count; + + if (!pt) DRM_MEM_ERROR(area, "Attempt to free NULL pointer\n"); + else kfree(pt); + spin_lock(&drm_mem_lock); + drm_mem_stats[area].bytes_freed += size; + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +unsigned long drm_alloc_pages(int order, int area) +{ + unsigned long address; + unsigned long bytes = PAGE_SIZE << order; + unsigned long addr; + unsigned int sz; + + spin_lock(&drm_mem_lock); + if ((drm_ram_used >> PAGE_SHIFT) + > (DRM_RAM_PERCENT * drm_ram_available) / 100) { + spin_unlock(&drm_mem_lock); + return 0; + } + spin_unlock(&drm_mem_lock); + + address = __get_free_pages(GFP_KERNEL, order); + if (!address) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].fail_count; + spin_unlock(&drm_mem_lock); + return 0; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_allocated += bytes; + drm_ram_used += bytes; + spin_unlock(&drm_mem_lock); + + + /* Zero outside the lock */ + memset((void *)address, 0, bytes); + + /* Reserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { + mem_map_reserve(MAP_NR(addr)); + } + + return address; +} + +void drm_free_pages(unsigned long address, int order, int area) +{ + unsigned long bytes = PAGE_SIZE << order; + int alloc_count; + int free_count; + unsigned long addr; + unsigned int sz; + + if (!address) { + DRM_MEM_ERROR(area, "Attempt to free address 0\n"); + } else { + /* Unreserve */ + for (addr = address, sz = bytes; + sz > 0; + addr += PAGE_SIZE, sz -= PAGE_SIZE) { + mem_map_unreserve(MAP_NR(addr)); + } + free_pages(address, order); + } + + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[area].free_count; + alloc_count = drm_mem_stats[area].succeed_count; + drm_mem_stats[area].bytes_freed += bytes; + drm_ram_used -= bytes; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(area, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +void *drm_ioremap(unsigned long offset, unsigned long size) +{ + void *pt; + + if (!size) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Mapping 0 bytes at 0x%08lx\n", offset); + return NULL; + } + + if (!(pt = ioremap(offset, size))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_allocated += size; + spin_unlock(&drm_mem_lock); + return pt; +} + +void drm_ioremapfree(void *pt, unsigned long size) +{ + int alloc_count; + int free_count; + + if (!pt) + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Attempt to free NULL pointer\n"); + else + iounmap(pt); + + spin_lock(&drm_mem_lock); + drm_mem_stats[DRM_MEM_MAPPINGS].bytes_freed += size; + free_count = ++drm_mem_stats[DRM_MEM_MAPPINGS].free_count; + alloc_count = drm_mem_stats[DRM_MEM_MAPPINGS].succeed_count; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_MAPPINGS, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } +} + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +agp_memory *drm_alloc_agp(int pages, u32 type) +{ + agp_memory *handle; + + if (!pages) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, "Allocating 0 pages\n"); + return NULL; + } + + if (drm_agp.allocate_memory) { + if ((handle = (*drm_agp.allocate_memory)(pages, + type))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_allocated + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + return handle; + } + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_TOTALAGP].fail_count; + spin_unlock(&drm_mem_lock); + return NULL; +} + +int drm_free_agp(agp_memory *handle, int pages) +{ + int alloc_count; + int free_count; + int retval = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Attempt to free NULL AGP handle\n"); + return retval;; + } + + if (drm_agp.free_memory) { + (*drm_agp.free_memory)(handle); + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_TOTALAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_TOTALAGP].succeed_count; + drm_mem_stats[DRM_MEM_TOTALAGP].bytes_freed + += pages << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_TOTALAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + return 0; + } + return retval; +} + +int drm_bind_agp(agp_memory *handle, unsigned int start) +{ + int retcode = -EINVAL; + + DRM_DEBUG("drm_bind_agp called\n"); + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to bind NULL AGP handle\n"); + return retcode; + } + + DRM_DEBUG("drm_agp.bind_memory : %p\n", drm_agp.bind_memory); + if (drm_agp.bind_memory) { + if (!(retcode = (*drm_agp.bind_memory)(handle, start))) { + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_allocated + += handle->page_count << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + DRM_DEBUG("drm_agp.bind_memory: retcode %d\n", retcode); + return retcode; + } + } + spin_lock(&drm_mem_lock); + ++drm_mem_stats[DRM_MEM_BOUNDAGP].fail_count; + spin_unlock(&drm_mem_lock); + return retcode; +} + +int drm_unbind_agp(agp_memory *handle) +{ + int alloc_count; + int free_count; + int retcode = -EINVAL; + + if (!handle) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Attempt to unbind NULL AGP handle\n"); + return retcode; + } + + if (drm_agp.unbind_memory) { + int c = handle->page_count; + if ((retcode = (*drm_agp.unbind_memory)(handle))) + return retcode; + spin_lock(&drm_mem_lock); + free_count = ++drm_mem_stats[DRM_MEM_BOUNDAGP].free_count; + alloc_count = drm_mem_stats[DRM_MEM_BOUNDAGP].succeed_count; + drm_mem_stats[DRM_MEM_BOUNDAGP].bytes_freed += c << PAGE_SHIFT; + spin_unlock(&drm_mem_lock); + if (free_count > alloc_count) { + DRM_MEM_ERROR(DRM_MEM_BOUNDAGP, + "Excess frees: %d frees, %d allocs\n", + free_count, alloc_count); + } + } + return retcode; +} +#endif diff --git a/drivers/char/drm/mga_bufs.c b/drivers/char/drm/mga_bufs.c new file mode 100644 index 000000000000..b97eb4959a0c --- /dev/null +++ b/drivers/char/drm/mga_bufs.c @@ -0,0 +1,639 @@ +/* mga_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Thu Jan 6 01:47:26 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" +#include "linux/un.h" + + +int mga_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + agp_offset = request.agp_start; + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + byte_count = 0; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + DRM_DEBUG("byte_count: %d\n", byte_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + + while(entry->buf_count < count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + + DRM_DEBUG("offset : %ld\n", offset); + + buf->offset = offset; /* Hrm */ + buf->bus_address = dev->agp->base + agp_offset + offset; + buf->address = (void *)(agp_offset + offset + dev->agp->base); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_private = drm_alloc(sizeof(drm_mga_buf_priv_t), DRM_MEM_BUFS); + buf->dev_priv_size = sizeof(drm_mga_buf_priv_t); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + offset = offset + alignment; + entry->buf_count++; + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + + DRM_DEBUG("dma->buf_count : %d\n", dma->buf_count); + + dma->byte_count += byte_count; + + DRM_DEBUG("entry->buf_count : %d\n", entry->buf_count); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + atomic_dec(&dev->buf_alloc); + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + DRM_DEBUG("byte_count: %d\n", byte_count); + + dma->flags = _DRM_DMA_USE_AGP; + + DRM_DEBUG("dma->flags : %x\n", dma->flags); + + return 0; +} + +int mga_addbufs_pci(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int count; + int order; + int size; + int total; + int page_order; + drm_buf_entry_t *entry; + unsigned long page; + drm_buf_t *buf; + int alignment; + unsigned long offset; + int i; + int byte_count; + int page_count; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + DRM_DEBUG("count = %d, size = %d (%d), order = %d, queue_count = %d\n", + request.count, request.size, size, order, dev->queue_count); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size) :size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->seglist = drm_alloc(count * sizeof(*entry->seglist), + DRM_MEM_SEGS); + if (!entry->seglist) { + drm_free(entry->buflist, + count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->seglist, 0, count * sizeof(*entry->seglist)); + + dma->pagelist = drm_realloc(dma->pagelist, + dma->page_count * sizeof(*dma->pagelist), + (dma->page_count + (count << page_order)) + * sizeof(*dma->pagelist), + DRM_MEM_PAGES); + DRM_DEBUG("pagelist: %d entries\n", + dma->page_count + (count << page_order)); + + + entry->buf_size = size; + entry->page_order = page_order; + byte_count = 0; + page_count = 0; + while (entry->buf_count < count) { + if (!(page = drm_alloc_pages(page_order, DRM_MEM_DMA))) break; + entry->seglist[entry->seg_count++] = page; + for (i = 0; i < (1 << page_order); i++) { + DRM_DEBUG("page %d @ 0x%08lx\n", + dma->page_count + page_count, + page + PAGE_SIZE * i); + dma->pagelist[dma->page_count + page_count++] + = page + PAGE_SIZE * i; + } + for (offset = 0; + offset + size <= total && entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + byte_count + offset); + buf->address = (void *)(page + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + byte_count += PAGE_SIZE << page_order; + } + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->seg_count += entry->seg_count; + dma->page_count += entry->seg_count << page_order; + dma->byte_count += PAGE_SIZE * (entry->seg_count << page_order); + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + atomic_dec(&dev->buf_alloc); + return 0; +} + +int mga_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_buf_desc_t request; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + if(request.flags & _DRM_AGP_BUFFER) + return mga_addbufs_agp(inode, filp, cmd, arg); + else + return mga_addbufs_pci(inode, filp, cmd, arg); +} + +int mga_infobufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_info_t request; + int i; + int count; + + if (!dma) return -EINVAL; + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_info_t *)arg, + sizeof(request), + -EFAULT); + + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) ++count; + } + + DRM_DEBUG("count = %d\n", count); + + if (request.count >= count) { + for (i = 0, count = 0; i < DRM_MAX_ORDER+1; i++) { + if (dma->bufs[i].buf_count) { + copy_to_user_ret(&request.list[count].count, + &dma->bufs[i].buf_count, + sizeof(dma->bufs[0] + .buf_count), + -EFAULT); + copy_to_user_ret(&request.list[count].size, + &dma->bufs[i].buf_size, + sizeof(dma->bufs[0].buf_size), + -EFAULT); + copy_to_user_ret(&request.list[count].low_mark, + &dma->bufs[i] + .freelist.low_mark, + sizeof(dma->bufs[0] + .freelist.low_mark), + -EFAULT); + copy_to_user_ret(&request.list[count] + .high_mark, + &dma->bufs[i] + .freelist.high_mark, + sizeof(dma->bufs[0] + .freelist.high_mark), + -EFAULT); + DRM_DEBUG("%d %d %d %d %d\n", + i, + dma->bufs[i].buf_count, + dma->bufs[i].buf_size, + dma->bufs[i].freelist.low_mark, + dma->bufs[i].freelist.high_mark); + ++count; + } + } + } + request.count = count; + + copy_to_user_ret((drm_buf_info_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return 0; +} + +int mga_markbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + int order; + drm_buf_entry_t *entry; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d, %d, %d\n", + request.size, request.low_mark, request.high_mark); + order = drm_order(request.size); + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + entry = &dma->bufs[order]; + + if (request.low_mark < 0 || request.low_mark > entry->buf_count) + return -EINVAL; + if (request.high_mark < 0 || request.high_mark > entry->buf_count) + return -EINVAL; + + entry->freelist.low_mark = request.low_mark; + entry->freelist.high_mark = request.high_mark; + + return 0; +} + +int mga_freebufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_free_t request; + int i; + int idx; + drm_buf_t *buf; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_free_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("%d\n", request.count); + for (i = 0; i < request.count; i++) { + copy_from_user_ret(&idx, + &request.list[i], + sizeof(idx), + -EFAULT); + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d freeing buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + drm_free_buffer(dev, buf); + } + + return 0; +} + +int mga_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + DRM_DEBUG("Busy\n"); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_map_t *)arg, + sizeof(request), + -EFAULT); + + DRM_DEBUG("mga_mapbufs\n"); + DRM_DEBUG("dma->flags : %x\n", dma->flags); + + if (request.count >= dma->buf_count) { + if(dma->flags & _DRM_DMA_USE_AGP) { + drm_mga_private_t *dev_priv = dev->dev_private; + drm_map_t *map = NULL; + + map = dev->maplist[dev_priv->buffer_map_idx]; + if (!map) { + DRM_DEBUG("map is null\n"); + retcode = -EINVAL; + goto done; + } + + DRM_DEBUG("map->offset : %lx\n", map->offset); + DRM_DEBUG("map->size : %lx\n", map->size); + DRM_DEBUG("map->type : %d\n", map->type); + DRM_DEBUG("map->flags : %x\n", map->flags); + DRM_DEBUG("map->handle : %p\n", map->handle); + DRM_DEBUG("map->mtrr : %d\n", map->mtrr); + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up(¤t->mm->mmap_sem); + } else { + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->mm->mmap_sem); + } + if (virtual > -1024UL) { + /* Real error */ + DRM_DEBUG("mmap error\n"); + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + copy_to_user_ret((drm_buf_map_t *)arg, + &request, + sizeof(request), + -EFAULT); + + DRM_DEBUG("retcode : %d\n", retcode); + + return retcode; +} diff --git a/drivers/char/drm/mga_context.c b/drivers/char/drm/mga_context.c new file mode 100644 index 000000000000..9a73e6c14f58 --- /dev/null +++ b/drivers/char/drm/mga_context.c @@ -0,0 +1,203 @@ +/* mga_context.c -- IOCTLs for mga contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" + +static int mga_alloc_queue(drm_device_t *dev) +{ + int temp = drm_ctxbitmap_next(dev); + DRM_DEBUG("mga_alloc_queue: %d\n", temp); + return temp; +} + +int mga_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + mga_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int mga_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + +int mga_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + +int mga_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = mga_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = mga_alloc_queue(dev); + } + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + DRM_DEBUG("%d\n", ctx.handle); + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int mga_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + /* This does nothing for the mga */ + return 0; +} + +int mga_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int mga_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return mga_context_switch(dev, dev->last_context, ctx.handle); +} + +int mga_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + mga_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int mga_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + if(ctx.handle != DRM_KERNEL_CONTEXT) { + drm_ctxbitmap_free(dev, ctx.handle); + } + + return 0; +} diff --git a/drivers/char/drm/mga_dma.c b/drivers/char/drm/mga_dma.c new file mode 100644 index 000000000000..28e8811c8694 --- /dev/null +++ b/drivers/char/drm/mga_dma.c @@ -0,0 +1,1115 @@ +/* mga_dma.c -- DMA support for mga g200/g400 -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" + +#include /* For task queue support */ + +#define MGA_REG(reg) 2 +#define MGA_BASE(reg) ((unsigned long) \ + ((drm_device_t *)dev)->maplist[MGA_REG(reg)]->handle) +#define MGA_ADDR(reg) (MGA_BASE(reg) + reg) +#define MGA_DEREF(reg) *(__volatile__ int *)MGA_ADDR(reg) +#define MGA_READ(reg) MGA_DEREF(reg) +#define MGA_WRITE(reg,val) do { MGA_DEREF(reg) = val; } while (0) + +#define PDEA_pagpxfer_enable 0x2 + +static int mga_flush_queue(drm_device_t *dev); + +static unsigned long mga_alloc_page(drm_device_t *dev) +{ + unsigned long address; + + DRM_DEBUG("%s\n", __FUNCTION__); + address = __get_free_page(GFP_KERNEL); + if(address == 0UL) { + return 0; + } + atomic_inc(&mem_map[MAP_NR((void *) address)].count); + set_bit(PG_locked, &mem_map[MAP_NR((void *) address)].flags); + + return address; +} + +static void mga_free_page(drm_device_t *dev, unsigned long page) +{ + DRM_DEBUG("%s\n", __FUNCTION__); + + if(page == 0UL) { + return; + } + atomic_dec(&mem_map[MAP_NR((void *) page)].count); + clear_bit(PG_locked, &mem_map[MAP_NR((void *) page)].flags); + wake_up(&mem_map[MAP_NR((void *) page)].wait); + free_page(page); + return; +} + +static void mga_delay(void) +{ + return; +} + +void mga_flush_write_combine(void) +{ + int xchangeDummy; + DRM_DEBUG("%s\n", __FUNCTION__); + + __asm__ volatile(" push %%eax ; xchg %%eax, %0 ; pop %%eax" : : "m" (xchangeDummy)); + __asm__ volatile(" push %%eax ; push %%ebx ; push %%ecx ; push %%edx ;" + " movl $0,%%eax ; cpuid ; pop %%edx ; pop %%ecx ; pop %%ebx ;" + " pop %%eax" : /* no outputs */ : /* no inputs */ ); +} + +/* These are two age tags that will never be sent to + * the hardware */ +#define MGA_BUF_USED 0xffffffff +#define MGA_BUF_FREE 0 + +static int mga_freelist_init(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_freelist_t *item; + int i; + + DRM_DEBUG("%s\n", __FUNCTION__); + + dev_priv->head = drm_alloc(sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + if(dev_priv->head == NULL) return -ENOMEM; + memset(dev_priv->head, 0, sizeof(drm_mga_freelist_t)); + dev_priv->head->age = MGA_BUF_USED; + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[ i ]; + buf_priv = buf->dev_private; + item = drm_alloc(sizeof(drm_mga_freelist_t), + DRM_MEM_DRIVER); + if(item == NULL) return -ENOMEM; + memset(item, 0, sizeof(drm_mga_freelist_t)); + item->age = MGA_BUF_FREE; + item->prev = dev_priv->head; + item->next = dev_priv->head->next; + if(dev_priv->head->next != NULL) + dev_priv->head->next->prev = item; + if(item->next == NULL) dev_priv->tail = item; + item->buf = buf; + buf_priv->my_freelist = item; + buf_priv->discard = 0; + buf_priv->dispatched = 0; + dev_priv->head->next = item; + } + + return 0; +} + +static void mga_freelist_cleanup(drm_device_t *dev) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_freelist_t *item; + drm_mga_freelist_t *prev; + + DRM_DEBUG("%s\n", __FUNCTION__); + + item = dev_priv->head; + while(item) { + prev = item; + item = item->next; + drm_free(prev, sizeof(drm_mga_freelist_t), DRM_MEM_DRIVER); + } + + dev_priv->head = dev_priv->tail = NULL; +} + +/* Frees dispatch lock */ +static inline void mga_dma_quiescent(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned long end; + int i; + + DRM_DEBUG("%s\n", __FUNCTION__); + end = jiffies + (HZ*3); + while(1) { + if(!test_and_set_bit(MGA_IN_DISPATCH, + &dev_priv->dispatch_status)) { + break; + } + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup\n"); + goto out_nolock; + } + for (i = 0 ; i < 2000 ; i++) mga_delay(); + } + end = jiffies + (HZ*3); + DRM_DEBUG("quiescent status : %x\n", MGA_READ(MGAREG_STATUS)); + while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup\n"); + goto out_status; + } + for (i = 0 ; i < 2000 ; i++) mga_delay(); + } + sarea_priv->dirty |= MGA_DMA_FLUSH; + +out_status: + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); +out_nolock: +} + +static void mga_reset_freelist(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + int i; + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[ i ]; + buf_priv = buf->dev_private; + buf_priv->my_freelist->age = MGA_BUF_FREE; + } +} + +/* Least recently used : + * These operations are not atomic b/c they are protected by the + * hardware lock */ + +drm_buf_t *mga_freelist_get(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_freelist_t *prev; + drm_mga_freelist_t *next; + static int failed = 0; + + DRM_DEBUG("%s : tail->age : %d last_prim_age : %d\n", __FUNCTION__, + dev_priv->tail->age, dev_priv->last_prim_age); + + if(failed >= 1000 && dev_priv->tail->age >= dev_priv->last_prim_age) { + DRM_DEBUG("I'm waiting on the freelist!!! %d\n", + dev_priv->last_prim_age); + set_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&dev_priv->buf_queue, &entry); + for (;;) { + mga_dma_schedule(dev, 0); + if(!test_bit(MGA_IN_GETBUF, + &dev_priv->dispatch_status)) + break; + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + clear_bit(MGA_IN_GETBUF, + &dev_priv->dispatch_status); + goto failed_getbuf; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->buf_queue, &entry); + } + + if(dev_priv->tail->age < dev_priv->last_prim_age) { + prev = dev_priv->tail->prev; + next = dev_priv->tail; + prev->next = NULL; + next->prev = next->next = NULL; + dev_priv->tail = prev; + next->age = MGA_BUF_USED; + failed = 0; + return next->buf; + } + +failed_getbuf: + failed++; + return NULL; +} + +int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf) +{ + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_freelist_t *prev; + drm_mga_freelist_t *head; + drm_mga_freelist_t *next; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(buf_priv->my_freelist->age == MGA_BUF_USED) { + /* Discarded buffer, put it on the tail */ + next = buf_priv->my_freelist; + next->age = MGA_BUF_FREE; + prev = dev_priv->tail; + prev->next = next; + next->prev = prev; + next->next = NULL; + dev_priv->tail = next; + DRM_DEBUG("Discarded\n"); + } else { + /* Normally aged buffer, put it on the head + 1, + * as the real head is a sentinal element + */ + next = buf_priv->my_freelist; + head = dev_priv->head; + prev = head->next; + head->next = next; + prev->prev = next; + next->prev = head; + next->next = prev; + } + + return 0; +} + +static int mga_init_primary_bufs(drm_device_t *dev, drm_mga_init_t *init) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_prim_buf_t *prim_buffer; + int i, temp, size_of_buf; + int offset = init->reserved_map_agpstart; + + DRM_DEBUG("%s\n", __FUNCTION__); + dev_priv->primary_size = ((init->primary_size + PAGE_SIZE - 1) / + PAGE_SIZE) * PAGE_SIZE; + size_of_buf = dev_priv->primary_size / MGA_NUM_PRIM_BUFS; + dev_priv->warp_ucode_size = init->warp_ucode_size; + dev_priv->prim_bufs = drm_alloc(sizeof(drm_mga_prim_buf_t *) * + (MGA_NUM_PRIM_BUFS + 1), + DRM_MEM_DRIVER); + if(dev_priv->prim_bufs == NULL) { + DRM_ERROR("Unable to allocate memory for prim_buf\n"); + return -ENOMEM; + } + memset(dev_priv->prim_bufs, + 0, sizeof(drm_mga_prim_buf_t *) * (MGA_NUM_PRIM_BUFS + 1)); + + temp = init->warp_ucode_size + dev_priv->primary_size; + temp = ((temp + PAGE_SIZE - 1) / PAGE_SIZE) * PAGE_SIZE; + + dev_priv->ioremap = drm_ioremap(dev->agp->base + offset, + temp); + if(dev_priv->ioremap == NULL) { + DRM_DEBUG("Ioremap failed\n"); + return -ENOMEM; + } + init_waitqueue_head(&dev_priv->wait_queue); + + for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { + prim_buffer = drm_alloc(sizeof(drm_mga_prim_buf_t), + DRM_MEM_DRIVER); + if(prim_buffer == NULL) return -ENOMEM; + memset(prim_buffer, 0, sizeof(drm_mga_prim_buf_t)); + prim_buffer->phys_head = offset + dev->agp->base; + prim_buffer->current_dma_ptr = + prim_buffer->head = + (u32 *) (dev_priv->ioremap + + offset - + init->reserved_map_agpstart); + prim_buffer->num_dwords = 0; + prim_buffer->max_dwords = size_of_buf / sizeof(u32); + prim_buffer->max_dwords -= 5; /* Leave room for the softrap */ + prim_buffer->sec_used = 0; + prim_buffer->idx = i; + prim_buffer->prim_age = i + 1; + offset = offset + size_of_buf; + dev_priv->prim_bufs[i] = prim_buffer; + } + dev_priv->current_prim_idx = 0; + dev_priv->next_prim = + dev_priv->last_prim = + dev_priv->current_prim = + dev_priv->prim_bufs[0]; + dev_priv->next_prim_age = 2; + dev_priv->last_prim_age = 1; + set_bit(MGA_BUF_IN_USE, &dev_priv->current_prim->buffer_status); + return 0; +} + +void mga_fire_primary(drm_device_t *dev, drm_mga_prim_buf_t *prim) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + int use_agp = PDEA_pagpxfer_enable; + unsigned long end; + int i; + int next_idx; + PRIMLOCALS; + + DRM_DEBUG("%s\n", __FUNCTION__); + dev_priv->last_prim = prim; + + /* We never check for overflow, b/c there is always room */ + PRIMPTR(prim); + if(num_dwords <= 0) { + DRM_DEBUG("num_dwords == 0 when dispatched\n"); + goto out_prim_wait; + } + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_DMAPAD, 0); + PRIMOUTREG( MGAREG_SOFTRAP, 0); + PRIMFINISH(prim); + + end = jiffies + (HZ*3); + if(sarea_priv->dirty & MGA_DMA_FLUSH) { + DRM_DEBUG("Dma top flush\n"); + while((MGA_READ(MGAREG_STATUS) & 0x00030001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup in fire primary " + "(Dma Top Flush)\n"); + goto out_prim_wait; + } + + for (i = 0 ; i < 4096 ; i++) mga_delay(); + } + sarea_priv->dirty &= ~(MGA_DMA_FLUSH); + } else { + DRM_DEBUG("Status wait\n"); + while((MGA_READ(MGAREG_STATUS) & 0x00020001) != 0x00020000) { + if((signed)(end - jiffies) <= 0) { + DRM_ERROR("irqs: %d wanted %d\n", + atomic_read(&dev->total_irq), + atomic_read(&dma->total_lost)); + DRM_ERROR("lockup in fire primary " + "(Status Wait)\n"); + goto out_prim_wait; + } + + for (i = 0 ; i < 4096 ; i++) mga_delay(); + } + } + + mga_flush_write_combine(); + atomic_inc(&dev_priv->pending_bufs); + MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); + MGA_WRITE(MGAREG_PRIMEND, (phys_head + num_dwords * 4) | use_agp); + prim->num_dwords = 0; + sarea_priv->last_enqueue = prim->prim_age; + + next_idx = prim->idx + 1; + if(next_idx >= MGA_NUM_PRIM_BUFS) + next_idx = 0; + + dev_priv->next_prim = dev_priv->prim_bufs[next_idx]; + return; + + out_prim_wait: + prim->num_dwords = 0; + prim->sec_used = 0; + clear_bit(MGA_BUF_IN_USE, &prim->buffer_status); + wake_up_interruptible(&dev_priv->wait_queue); + clear_bit(MGA_BUF_SWAP_PENDING, &prim->buffer_status); + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); +} + +int mga_advance_primary(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_prim_buf_t *prim_buffer; + drm_device_dma_t *dma = dev->dma; + int next_prim_idx; + int ret = 0; + + /* This needs to reset the primary buffer if available, + * we should collect stats on how many times it bites + * it's tail */ + DRM_DEBUG("%s\n", __FUNCTION__); + + next_prim_idx = dev_priv->current_prim_idx + 1; + if(next_prim_idx >= MGA_NUM_PRIM_BUFS) + next_prim_idx = 0; + prim_buffer = dev_priv->prim_bufs[next_prim_idx]; + set_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); + + /* In use is cleared in interrupt handler */ + + if(test_and_set_bit(MGA_BUF_IN_USE, &prim_buffer->buffer_status)) { + add_wait_queue(&dev_priv->wait_queue, &entry); + current->state = TASK_INTERRUPTIBLE; + + for (;;) { + mga_dma_schedule(dev, 0); + if(!test_and_set_bit(MGA_BUF_IN_USE, + &prim_buffer->buffer_status)) + break; + atomic_inc(&dev->total_sleeps); + atomic_inc(&dma->total_missed_sched); + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->wait_queue, &entry); + if(ret) return ret; + } + clear_bit(MGA_IN_WAIT, &dev_priv->dispatch_status); + + /* This primary buffer is now free to use */ + prim_buffer->current_dma_ptr = prim_buffer->head; + prim_buffer->num_dwords = 0; + prim_buffer->sec_used = 0; + prim_buffer->prim_age = dev_priv->next_prim_age++; + if(prim_buffer->prim_age == 0 || prim_buffer->prim_age == 0xffffffff) { + mga_flush_queue(dev); + mga_dma_quiescent(dev); + mga_reset_freelist(dev); + prim_buffer->prim_age = (dev_priv->next_prim_age += 2); + } + + /* Reset all buffer status stuff */ + clear_bit(MGA_BUF_NEEDS_OVERFLOW, &prim_buffer->buffer_status); + clear_bit(MGA_BUF_FORCE_FIRE, &prim_buffer->buffer_status); + clear_bit(MGA_BUF_SWAP_PENDING, &prim_buffer->buffer_status); + + dev_priv->current_prim = prim_buffer; + dev_priv->current_prim_idx = next_prim_idx; + return 0; +} + +/* More dynamic performance decisions */ +static inline int mga_decide_to_fire(drm_device_t *dev) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_device_dma_t *dma = dev->dma; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(test_bit(MGA_BUF_FORCE_FIRE, &dev_priv->next_prim->buffer_status)) { + atomic_inc(&dma->total_prio); + return 1; + } + + if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && + dev_priv->next_prim->num_dwords) { + atomic_inc(&dma->total_prio); + return 1; + } + + if (test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && + dev_priv->next_prim->num_dwords) { + atomic_inc(&dma->total_prio); + return 1; + } + + if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS - 1) { + if(test_bit(MGA_BUF_SWAP_PENDING, + &dev_priv->next_prim->buffer_status)) { + atomic_inc(&dma->total_dmas); + return 1; + } + } + + if(atomic_read(&dev_priv->pending_bufs) <= MGA_NUM_PRIM_BUFS / 2) { + if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 8) { + atomic_inc(&dma->total_hit); + return 1; + } + } + + if(atomic_read(&dev_priv->pending_bufs) >= MGA_NUM_PRIM_BUFS / 2) { + if(dev_priv->next_prim->sec_used >= MGA_DMA_BUF_NR / 4) { + atomic_inc(&dma->total_missed_free); + return 1; + } + } + + atomic_inc(&dma->total_tried); + return 0; +} + +int mga_dma_schedule(drm_device_t *dev, int locked) +{ + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_device_dma_t *dma = dev->dma; + int retval = 0; + + if (test_and_set_bit(0, &dev->dma_flag)) { + atomic_inc(&dma->total_missed_dma); + retval = -EBUSY; + goto sch_out_wakeup; + } + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) || + test_bit(MGA_IN_WAIT, &dev_priv->dispatch_status) || + test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) { + locked = 1; + } + + if (!locked && + !drm_lock_take(&dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT)) { + atomic_inc(&dma->total_missed_lock); + clear_bit(0, &dev->dma_flag); + DRM_DEBUG("Not locked\n"); + retval = -EBUSY; + goto sch_out_wakeup; + } + DRM_DEBUG("I'm locked\n"); + + if(!test_and_set_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status)) { + /* Fire dma buffer */ + if(mga_decide_to_fire(dev)) { + DRM_DEBUG("idx :%d\n", dev_priv->next_prim->idx); + clear_bit(MGA_BUF_FORCE_FIRE, + &dev_priv->next_prim->buffer_status); + if(dev_priv->current_prim == dev_priv->next_prim) { + /* Schedule overflow for a later time */ + set_bit(MGA_BUF_NEEDS_OVERFLOW, + &dev_priv->next_prim->buffer_status); + } + mga_fire_primary(dev, dev_priv->next_prim); + } else { + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + } + } else { + DRM_DEBUG("I can't get the dispatch lock\n"); + } + + if (!locked) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +sch_out_wakeup: + if(test_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status) && + atomic_read(&dev_priv->pending_bufs) == 0) { + /* Everything has been processed by the hardware */ + clear_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); + wake_up_interruptible(&dev_priv->flush_queue); + } + + if(test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status) && + dev_priv->tail->age < dev_priv->last_prim_age) { + clear_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status); + DRM_DEBUG("Waking up buf queue\n"); + wake_up_interruptible(&dev_priv->buf_queue); + } else if (test_bit(MGA_IN_GETBUF, &dev_priv->dispatch_status)) { + DRM_DEBUG("Not waking buf_queue on %d %d\n", + atomic_read(&dev->total_irq), + dev_priv->last_prim_age); + } + + clear_bit(0, &dev->dma_flag); + return retval; +} + +static void mga_dma_service(int irq, void *device, struct pt_regs *regs) +{ + drm_device_t *dev = (drm_device_t *)device; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + drm_mga_prim_buf_t *last_prim_buffer; + + DRM_DEBUG("%s\n", __FUNCTION__); + atomic_inc(&dev->total_irq); + if((MGA_READ(MGAREG_STATUS) & 0x00000001) != 0x00000001) return; + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + last_prim_buffer = dev_priv->last_prim; + last_prim_buffer->num_dwords = 0; + last_prim_buffer->sec_used = 0; + dev_priv->sarea_priv->last_dispatch = + dev_priv->last_prim_age = last_prim_buffer->prim_age; + clear_bit(MGA_BUF_IN_USE, &last_prim_buffer->buffer_status); + wake_up_interruptible(&dev_priv->wait_queue); + clear_bit(MGA_BUF_SWAP_PENDING, &last_prim_buffer->buffer_status); + clear_bit(MGA_IN_DISPATCH, &dev_priv->dispatch_status); + atomic_dec(&dev_priv->pending_bufs); + queue_task(&dev->tq, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void mga_dma_task_queue(void *device) +{ + DRM_DEBUG("%s\n", __FUNCTION__); + mga_dma_schedule((drm_device_t *)device, 0); +} + +int mga_dma_cleanup(drm_device_t *dev) +{ + DRM_DEBUG("%s\n", __FUNCTION__); + + if(dev->dev_private) { + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + + if(dev_priv->ioremap) { + int temp = (dev_priv->warp_ucode_size + + dev_priv->primary_size + + PAGE_SIZE - 1) / PAGE_SIZE * PAGE_SIZE; + + drm_ioremapfree((void *) dev_priv->ioremap, temp); + } + if(dev_priv->status_page != NULL) { + iounmap(dev_priv->status_page); + } + if(dev_priv->real_status_page != 0UL) { + mga_free_page(dev, dev_priv->real_status_page); + } + if(dev_priv->prim_bufs != NULL) { + int i; + for(i = 0; i < MGA_NUM_PRIM_BUFS; i++) { + if(dev_priv->prim_bufs[i] != NULL) { + drm_free(dev_priv->prim_bufs[i], + sizeof(drm_mga_prim_buf_t), + DRM_MEM_DRIVER); + } + } + drm_free(dev_priv->prim_bufs, sizeof(void *) * + (MGA_NUM_PRIM_BUFS + 1), + DRM_MEM_DRIVER); + } + if(dev_priv->head != NULL) { + mga_freelist_cleanup(dev); + } + + + drm_free(dev->dev_private, sizeof(drm_mga_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + } + + return 0; +} + +static int mga_dma_initialize(drm_device_t *dev, drm_mga_init_t *init) { + drm_mga_private_t *dev_priv; + drm_map_t *sarea_map = NULL; + int i; + + DRM_DEBUG("%s\n", __FUNCTION__); + + dev_priv = drm_alloc(sizeof(drm_mga_private_t), DRM_MEM_DRIVER); + if(dev_priv == NULL) return -ENOMEM; + dev->dev_private = (void *) dev_priv; + + memset(dev_priv, 0, sizeof(drm_mga_private_t)); + + if((init->reserved_map_idx >= dev->map_count) || + (init->buffer_map_idx >= dev->map_count)) { + mga_dma_cleanup(dev); + DRM_DEBUG("reserved_map or buffer_map are invalid\n"); + return -EINVAL; + } + + dev_priv->reserved_map_idx = init->reserved_map_idx; + dev_priv->buffer_map_idx = init->buffer_map_idx; + sarea_map = dev->maplist[0]; + dev_priv->sarea_priv = (drm_mga_sarea_t *) + ((u8 *)sarea_map->handle + + init->sarea_priv_offset); + + /* Scale primary size to the next page */ + dev_priv->chipset = init->chipset; + dev_priv->frontOffset = init->frontOffset; + dev_priv->backOffset = init->backOffset; + dev_priv->depthOffset = init->depthOffset; + dev_priv->textureOffset = init->textureOffset; + dev_priv->textureSize = init->textureSize; + dev_priv->cpp = init->cpp; + dev_priv->sgram = init->sgram; + dev_priv->stride = init->stride; + + dev_priv->mAccess = init->mAccess; + init_waitqueue_head(&dev_priv->flush_queue); + init_waitqueue_head(&dev_priv->buf_queue); + dev_priv->WarpPipe = -1; + + DRM_DEBUG("chipset: %d ucode_size: %d backOffset: %x depthOffset: %x\n", + dev_priv->chipset, dev_priv->warp_ucode_size, + dev_priv->backOffset, dev_priv->depthOffset); + DRM_DEBUG("cpp: %d sgram: %d stride: %d maccess: %x\n", + dev_priv->cpp, dev_priv->sgram, dev_priv->stride, + dev_priv->mAccess); + + memcpy(&dev_priv->WarpIndex, &init->WarpIndex, + sizeof(drm_mga_warp_index_t) * MGA_MAX_WARP_PIPES); + + for (i = 0 ; i < MGA_MAX_WARP_PIPES ; i++) + DRM_DEBUG("warp pipe %d: installed: %d phys: %lx size: %x\n", + i, + dev_priv->WarpIndex[i].installed, + dev_priv->WarpIndex[i].phys_addr, + dev_priv->WarpIndex[i].size); + + if(mga_init_primary_bufs(dev, init) != 0) { + DRM_ERROR("Can not initialize primary buffers\n"); + mga_dma_cleanup(dev); + return -ENOMEM; + } + dev_priv->real_status_page = mga_alloc_page(dev); + if(dev_priv->real_status_page == 0UL) { + mga_dma_cleanup(dev); + DRM_ERROR("Can not allocate status page\n"); + return -ENOMEM; + } + + dev_priv->status_page = + ioremap_nocache(virt_to_bus((void *)dev_priv->real_status_page), + PAGE_SIZE); + + if(dev_priv->status_page == NULL) { + mga_dma_cleanup(dev); + DRM_ERROR("Can not remap status page\n"); + return -ENOMEM; + } + + /* Write status page when secend or softrap occurs */ + MGA_WRITE(MGAREG_PRIMPTR, + virt_to_bus((void *)dev_priv->real_status_page) | 0x00000003); + + + /* Private is now filled in, initialize the hardware */ + { + PRIMLOCALS; + PRIMGETPTR( dev_priv ); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0x0100); + PRIMOUTREG(MGAREG_SOFTRAP, 0); + /* Poll for the first buffer to insure that + * the status register will be correct + */ + + mga_flush_write_combine(); + MGA_WRITE(MGAREG_PRIMADDRESS, phys_head | TT_GENERAL); + + MGA_WRITE(MGAREG_PRIMEND, ((phys_head + num_dwords * 4) | + PDEA_pagpxfer_enable)); + + while(MGA_READ(MGAREG_DWGSYNC) != 0x0100) ; + } + + if(mga_freelist_init(dev) != 0) { + DRM_ERROR("Could not initialize freelist\n"); + mga_dma_cleanup(dev); + return -ENOMEM; + } + return 0; +} + +int mga_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_init_t init; + + DRM_DEBUG("%s\n", __FUNCTION__); + + copy_from_user_ret(&init, (drm_mga_init_t *)arg, sizeof(init), -EFAULT); + + switch(init.func) { + case MGA_INIT_DMA: + return mga_dma_initialize(dev, &init); + case MGA_CLEANUP_DMA: + return mga_dma_cleanup(dev); + } + + return -EINVAL; +} + +int mga_irq_install(drm_device_t *dev, int irq) +{ + int retcode; + + if (!irq) return -EINVAL; + + down(&dev->struct_sem); + if (dev->irq) { + up(&dev->struct_sem); + return -EBUSY; + } + dev->irq = irq; + up(&dev->struct_sem); + + DRM_DEBUG("install irq handler %d\n", irq); + + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->dma->next_buffer = NULL; + dev->dma->next_queue = NULL; + dev->dma->this_buffer = NULL; + dev->tq.next = NULL; + dev->tq.sync = 0; + dev->tq.routine = mga_dma_task_queue; + dev->tq.data = dev; + + /* Before installing handler */ + MGA_WRITE(MGAREG_IEN, 0); + /* Install handler */ + if ((retcode = request_irq(dev->irq, + mga_dma_service, + SA_SHIRQ, + dev->devname, + dev))) { + down(&dev->struct_sem); + dev->irq = 0; + up(&dev->struct_sem); + return retcode; + } + /* After installing handler */ + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + MGA_WRITE(MGAREG_IEN, 0x00000001); + return 0; +} + +int mga_irq_uninstall(drm_device_t *dev) +{ + int irq; + + down(&dev->struct_sem); + irq = dev->irq; + dev->irq = 0; + up(&dev->struct_sem); + + if (!irq) return -EINVAL; + DRM_DEBUG("remove irq handler %d\n", irq); + MGA_WRITE(MGAREG_ICLEAR, 0x00000001); + MGA_WRITE(MGAREG_IEN, 0); + free_irq(irq, dev); + return 0; +} + +int mga_control(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_control_t ctl; + + copy_from_user_ret(&ctl, (drm_control_t *)arg, sizeof(ctl), -EFAULT); + + DRM_DEBUG("%s\n", __FUNCTION__); + + switch (ctl.func) { + case DRM_INST_HANDLER: + return mga_irq_install(dev, ctl.irq); + case DRM_UNINST_HANDLER: + return mga_irq_uninstall(dev); + default: + return -EINVAL; + } +} + +static int mga_flush_queue(drm_device_t *dev) +{ + DECLARE_WAITQUEUE(entry, current); + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + int ret = 0; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if(dev_priv == NULL) { + return 0; + } + + if(dev_priv->next_prim->num_dwords != 0) { + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&dev_priv->flush_queue, &entry); + set_bit(MGA_IN_FLUSH, &dev_priv->dispatch_status); + mga_dma_schedule(dev, 0); + for (;;) { + if (!test_bit(MGA_IN_FLUSH, + &dev_priv->dispatch_status)) + break; + atomic_inc(&dev->total_sleeps); + schedule(); + if (signal_pending(current)) { + ret = -EINTR; /* Can't restart */ + clear_bit(MGA_IN_FLUSH, + &dev_priv->dispatch_status); + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev_priv->flush_queue, &entry); + } + return ret; +} + +/* Must be called with the lock held */ +void mga_reclaim_buffers(drm_device_t *dev, pid_t pid) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return; + if(dev->dev_private == NULL) return; + if(dma->buflist == NULL) return; + + DRM_DEBUG("%s\n", __FUNCTION__); + mga_flush_queue(dev); + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[ i ]; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + + /* Only buffers that need to get reclaimed ever + * get set to free + */ + if (buf->pid == pid && buf_priv) { + if(buf_priv->my_freelist->age == MGA_BUF_USED) + buf_priv->my_freelist->age = MGA_BUF_FREE; + } + } +} + +int mga_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; + + DRM_DEBUG("%s\n", __FUNCTION__); + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + + if (lock.context < 0) { + return -EINVAL; + } + + /* Only one queue: + */ + + if (!ret) { + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + + if (!ret) { + if (lock.flags & _DRM_LOCK_QUIESCENT) { + DRM_DEBUG("_DRM_LOCK_QUIESCENT\n"); + mga_flush_queue(dev); + mga_dma_quiescent(dev); + } + } + + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + return ret; +} + +int mga_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + drm_mga_private_t *dev_priv = (drm_mga_private_t *)dev->dev_private; + + DRM_DEBUG("%s\n", __FUNCTION__); + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if(!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_flush_ioctl called without lock held\n"); + return -EINVAL; + } + + if(lock.flags & _DRM_LOCK_FLUSH || lock.flags & _DRM_LOCK_FLUSH_ALL) { + drm_mga_prim_buf_t *temp_buf; + + temp_buf = dev_priv->current_prim; + + if(temp_buf && temp_buf->num_dwords) { + set_bit(MGA_BUF_FORCE_FIRE, &temp_buf->buffer_status); + mga_advance_primary(dev); + } + mga_dma_schedule(dev, 1); + } + if(lock.flags & _DRM_LOCK_QUIESCENT) { + mga_flush_queue(dev); + mga_dma_quiescent(dev); + } + + return 0; +} diff --git a/drivers/char/drm/mga_drm.h b/drivers/char/drm/mga_drm.h new file mode 100644 index 000000000000..e75e91a4f501 --- /dev/null +++ b/drivers/char/drm/mga_drm.h @@ -0,0 +1,269 @@ +/* mga_drm.h -- Public header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Tue Jan 25 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Jeff Hartmann + * Keith Whitwell + * + */ + +#ifndef _MGA_DRM_H_ +#define _MGA_DRM_H_ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmMga.h) + */ +#ifndef _MGA_DEFINES_ +#define _MGA_DEFINES_ + +#define MGA_F 0x1 /* fog */ +#define MGA_A 0x2 /* alpha */ +#define MGA_S 0x4 /* specular */ +#define MGA_T2 0x8 /* multitexture */ + +#define MGA_WARP_TGZ 0 +#define MGA_WARP_TGZF (MGA_F) +#define MGA_WARP_TGZA (MGA_A) +#define MGA_WARP_TGZAF (MGA_F|MGA_A) +#define MGA_WARP_TGZS (MGA_S) +#define MGA_WARP_TGZSF (MGA_S|MGA_F) +#define MGA_WARP_TGZSA (MGA_S|MGA_A) +#define MGA_WARP_TGZSAF (MGA_S|MGA_F|MGA_A) +#define MGA_WARP_T2GZ (MGA_T2) +#define MGA_WARP_T2GZF (MGA_T2|MGA_F) +#define MGA_WARP_T2GZA (MGA_T2|MGA_A) +#define MGA_WARP_T2GZAF (MGA_T2|MGA_A|MGA_F) +#define MGA_WARP_T2GZS (MGA_T2|MGA_S) +#define MGA_WARP_T2GZSF (MGA_T2|MGA_S|MGA_F) +#define MGA_WARP_T2GZSA (MGA_T2|MGA_S|MGA_A) +#define MGA_WARP_T2GZSAF (MGA_T2|MGA_S|MGA_F|MGA_A) + +#define MGA_MAX_G400_PIPES 16 +#define MGA_MAX_G200_PIPES 8 /* no multitex */ +#define MGA_MAX_WARP_PIPES MGA_MAX_G400_PIPES + +#define MGA_CARD_TYPE_G200 1 +#define MGA_CARD_TYPE_G400 2 + +#define MGA_FRONT 0x1 +#define MGA_BACK 0x2 +#define MGA_DEPTH 0x4 + +/* 3d state excluding texture units: + */ +#define MGA_CTXREG_DSTORG 0 /* validated */ +#define MGA_CTXREG_MACCESS 1 +#define MGA_CTXREG_PLNWT 2 +#define MGA_CTXREG_DWGCTL 3 +#define MGA_CTXREG_ALPHACTRL 4 +#define MGA_CTXREG_FOGCOLOR 5 +#define MGA_CTXREG_WFLAG 6 +#define MGA_CTXREG_TDUAL0 7 +#define MGA_CTXREG_TDUAL1 8 +#define MGA_CTXREG_FCOL 9 +#define MGA_CTX_SETUP_SIZE 10 + +/* 2d state + */ +#define MGA_2DREG_PITCH 0 +#define MGA_2D_SETUP_SIZE 1 + +/* Each texture unit has a state: + */ +#define MGA_TEXREG_CTL 0 +#define MGA_TEXREG_CTL2 1 +#define MGA_TEXREG_FILTER 2 +#define MGA_TEXREG_BORDERCOL 3 +#define MGA_TEXREG_ORG 4 /* validated */ +#define MGA_TEXREG_ORG1 5 +#define MGA_TEXREG_ORG2 6 +#define MGA_TEXREG_ORG3 7 +#define MGA_TEXREG_ORG4 8 +#define MGA_TEXREG_WIDTH 9 +#define MGA_TEXREG_HEIGHT 10 +#define MGA_TEX_SETUP_SIZE 11 + +/* What needs to be changed for the current vertex dma buffer? + */ +#define MGA_UPLOAD_CTX 0x1 +#define MGA_UPLOAD_TEX0 0x2 +#define MGA_UPLOAD_TEX1 0x4 +#define MGA_UPLOAD_PIPE 0x8 +#define MGA_UPLOAD_TEX0IMAGE 0x10 /* handled client-side */ +#define MGA_UPLOAD_TEX1IMAGE 0x20 /* handled client-side */ +#define MGA_UPLOAD_2D 0x40 +#define MGA_WAIT_AGE 0x80 /* handled client-side */ +#define MGA_UPLOAD_CLIPRECTS 0x100 /* handled client-side */ +#define MGA_DMA_FLUSH 0x200 /* set when someone gets the lock + quiescent */ + +/* 32 buffers of 64k each, total 2 meg. + */ +#define MGA_DMA_BUF_ORDER 16 +#define MGA_DMA_BUF_SZ (1< + * Jeff Hartmann + * + * + */ + +#include +#include "drmP.h" +#include "mga_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#define MGA_NAME "mga" +#define MGA_DESC "Matrox g200/g400" +#define MGA_DATE "20000719" +#define MGA_MAJOR 1 +#define MGA_MINOR 0 +#define MGA_PATCHLEVEL 0 + +static drm_device_t mga_device; +drm_ctx_t mga_res_ctx; + +static struct file_operations mga_fops = { +#if LINUX_VERSION_CODE >= 0x020322 + /* This started being used approx. 2.3.34 */ + owner: THIS_MODULE, +#endif + open: mga_open, + flush: drm_flush, + release: mga_release, + ioctl: mga_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice mga_misc = { + minor: MISC_DYNAMIC_MINOR, + name: MGA_NAME, + fops: &mga_fops, +}; + +static drm_ioctl_desc_t mga_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { mga_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_CONTROL)] = { mga_control, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { mga_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { mga_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { mga_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { mga_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { mga_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { mga_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { mga_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { mga_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { mga_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { mga_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { mga_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { mga_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_DMA)] = { mga_dma, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { mga_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { mga_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INIT)] = { mga_dma_init, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_SWAP)] = { mga_swap_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_CLEAR)] = { mga_clear_bufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_ILOAD)] = { mga_iload, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_VERTEX)] = { mga_vertex, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_FLUSH)] = { mga_flush_ioctl, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MGA_INDICES)] = { mga_indices, 1, 0 }, +}; + +#define MGA_IOCTL_COUNT DRM_ARRAY_SIZE(mga_ioctls) + +#ifdef MODULE +static char *mga = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("Matrox g200/g400"); +MODULE_PARM(mga, "s"); + +#ifndef MODULE +/* mga_options is called by the kernel to parse command-line options passed + * via the boot-loader (e.g., LILO). It calls the insmod option routine, + * drm_parse_drm. + */ + +static int __init mga_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("mga=", mga_options); +#endif + +static int mga_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int mga_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + if (dev->irq) mga_irq_uninstall(dev); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired && drm_agp.release) + (*drm_agp.release)(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->queuelist) { + for (i = 0; i < dev->queue_count; i++) { + drm_waitlist_destroy(&dev->queuelist[i]->waitlist); + if (dev->queuelist[i]) { + drm_free(dev->queuelist[i], + sizeof(*dev->queuelist[0]), + DRM_MEM_QUEUES); + dev->queuelist[i] = NULL; + } + } + drm_free(dev->queuelist, + dev->queue_slots * sizeof(*dev->queuelist), + DRM_MEM_QUEUES); + dev->queuelist = NULL; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* mga_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int mga_init(void) +{ + int retcode; + drm_device_t *dev = &mga_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(mga); +#endif + DRM_DEBUG("doing misc_register\n"); + if ((retcode = misc_register(&mga_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", MGA_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, mga_misc.minor); + dev->name = MGA_NAME; + + DRM_DEBUG("doing mem init\n"); + drm_mem_init(); + DRM_DEBUG("doing proc init\n"); + drm_proc_init(dev); + DRM_DEBUG("doing agp init\n"); + dev->agp = drm_agp_init(); + if(dev->agp == NULL) { + DRM_INFO("The mga drm module requires the agpgart module" + " to function correctly\nPlease load the agpgart" + " module before you load the mga module\n"); + drm_proc_cleanup(); + misc_deregister(&mga_misc); + mga_takedown(dev); + return -ENOMEM; + } +#ifdef CONFIG_MTRR + dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * 1024 * 1024, + MTRR_TYPE_WRCOMB, + 1); +#endif + DRM_DEBUG("doing ctxbitmap init\n"); + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&mga_misc); + mga_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + MGA_NAME, + MGA_MAJOR, + MGA_MINOR, + MGA_PATCHLEVEL, + MGA_DATE, + mga_misc.minor); + + return 0; +} + +/* mga_cleanup is called via cleanup_module at module unload time. */ + +static void mga_cleanup(void) +{ + drm_device_t *dev = &mga_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&mga_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + mga_dma_cleanup(dev); +#ifdef CONFIG_MTRR + if(dev->agp && dev->agp->agp_mtrr) { + int retval; + retval = mtrr_del(dev->agp->agp_mtrr, + dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size * 1024*1024); + DRM_DEBUG("mtrr_del = %d\n", retval); + } +#endif + + mga_takedown(dev); + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +} + +module_init(mga_init); +module_exit(mga_cleanup); + + +int mga_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = MGA_MAJOR; + version.version_minor = MGA_MINOR; + version.version_patchlevel = MGA_PATCHLEVEL; + + DRM_COPY(version.name, MGA_NAME); + DRM_COPY(version.date, MGA_DATE); + DRM_COPY(version.desc, MGA_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int mga_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &mga_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return mga_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int mga_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("pid = %d, device = 0x%x, open_count = %d\n", + current->pid, dev->device, dev->open_count); + + if (dev->lock.hw_lock && _DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) + && dev->lock.pid == current->pid) { + mga_reclaim_buffers(dev, priv->pid); + DRM_ERROR("Process %d dead, freeing lock for context %d\n", + current->pid, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + drm_lock_free(dev, + &dev->lock.hw_lock->lock, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + + /* FIXME: may require heavy-handed reset of + hardware at this point, possibly + processed via a callback to the X + server. */ + } else if (dev->lock.hw_lock) { + /* The lock is required to reclaim buffers */ + DECLARE_WAITQUEUE(entry, current); + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + retcode = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + dev->lock.pid = priv->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; + schedule(); + if (signal_pending(current)) { + retcode = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + if(!retcode) { + mga_reclaim_buffers(dev, priv->pid); + drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT); + } + } + drm_fasync(-1, filp, 0); + + down(&dev->struct_sem); + if (priv->prev) priv->prev->next = priv->next; + else dev->file_first = priv->next; + if (priv->next) priv->next->prev = priv->prev; + else dev->file_last = priv->prev; + up(&dev->struct_sem); + + drm_free(priv, sizeof(*priv), DRM_MEM_FILES); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return mga_takedown(dev); + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return retcode; +} + + +/* drm_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int mga_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= MGA_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &mga_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int mga_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + mga_dma_schedule(dev, 1); + + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + + return 0; +} diff --git a/drivers/char/drm/mga_drv.h b/drivers/char/drm/mga_drv.h new file mode 100644 index 000000000000..fe9e3dbe9bb7 --- /dev/null +++ b/drivers/char/drm/mga_drv.h @@ -0,0 +1,445 @@ +/* mga_drv.h -- Private header for the Matrox g200/g400 driver -*- linux-c -*- + * Created: Mon Dec 13 01:50:01 1999 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#ifndef _MGA_DRV_H_ +#define _MGA_DRV_H_ + +#define MGA_BUF_IN_USE 0 +#define MGA_BUF_SWAP_PENDING 1 +#define MGA_BUF_FORCE_FIRE 2 +#define MGA_BUF_NEEDS_OVERFLOW 3 + +typedef struct { + u32 buffer_status; + unsigned int num_dwords; + unsigned int max_dwords; + u32 *current_dma_ptr; + u32 *head; + u32 phys_head; + unsigned int prim_age; + int sec_used; + int idx; +} drm_mga_prim_buf_t; + +typedef struct _drm_mga_freelist { + unsigned int age; + drm_buf_t *buf; + struct _drm_mga_freelist *next; + struct _drm_mga_freelist *prev; +} drm_mga_freelist_t; + +#define MGA_IN_DISPATCH 0 +#define MGA_IN_FLUSH 1 +#define MGA_IN_WAIT 2 +#define MGA_IN_GETBUF 3 + +typedef struct _drm_mga_private { + u32 dispatch_status; + unsigned int next_prim_age; + __volatile__ unsigned int last_prim_age; + int reserved_map_idx; + int buffer_map_idx; + drm_mga_sarea_t *sarea_priv; + int primary_size; + int warp_ucode_size; + int chipset; + unsigned int frontOffset; + unsigned int backOffset; + unsigned int depthOffset; + unsigned int textureOffset; + unsigned int textureSize; + int cpp; + unsigned int stride; + int sgram; + int use_agp; + drm_mga_warp_index_t WarpIndex[MGA_MAX_G400_PIPES]; + unsigned int WarpPipe; + atomic_t pending_bufs; + void *status_page; + unsigned long real_status_page; + u8 *ioremap; + drm_mga_prim_buf_t **prim_bufs; + drm_mga_prim_buf_t *next_prim; + drm_mga_prim_buf_t *last_prim; + drm_mga_prim_buf_t *current_prim; + int current_prim_idx; + drm_mga_freelist_t *head; + drm_mga_freelist_t *tail; + wait_queue_head_t flush_queue; /* Processes waiting until flush */ + wait_queue_head_t wait_queue; /* Processes waiting until interrupt */ + wait_queue_head_t buf_queue; /* Processes waiting for a free buf */ + /* Some validated register values: + */ + u32 mAccess; +} drm_mga_private_t; + + /* mga_drv.c */ +extern int mga_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_open(struct inode *inode, struct file *filp); +extern int mga_release(struct inode *inode, struct file *filp); +extern int mga_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* mga_dma.c */ +extern int mga_dma_schedule(drm_device_t *dev, int locked); +extern int mga_dma(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_irq_install(drm_device_t *dev, int irq); +extern int mga_irq_uninstall(drm_device_t *dev); +extern int mga_control(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +/* mga_dma_init does init and release */ +extern int mga_dma_init(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_dma_cleanup(drm_device_t *dev); +extern int mga_flush_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern void mga_flush_write_combine(void); +extern unsigned int mga_create_sync_tag(drm_device_t *dev); +extern drm_buf_t *mga_freelist_get(drm_device_t *dev); +extern int mga_freelist_put(drm_device_t *dev, drm_buf_t *buf); +extern int mga_advance_primary(drm_device_t *dev); +extern void mga_reclaim_buffers(drm_device_t *dev, pid_t pid); + + + /* mga_bufs.c */ +extern int mga_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_infobufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_markbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_freebufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_addmap(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + /* mga_state.c */ +extern int mga_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_iload(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_indices(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + /* mga_context.c */ +extern int mga_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int mga_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int mga_context_switch(drm_device_t *dev, int old, int new); +extern int mga_context_switch_complete(drm_device_t *dev, int new); + + +typedef enum { + TT_GENERAL, + TT_BLIT, + TT_VECTOR, + TT_VERTEX +} transferType_t; + +typedef struct { + drm_mga_freelist_t *my_freelist; + int discard; + int dispatched; +} drm_mga_buf_priv_t; + +#define DWGREG0 0x1c00 +#define DWGREG0_END 0x1dff +#define DWGREG1 0x2c00 +#define DWGREG1_END 0x2dff + +#define ISREG0(r) (r >= DWGREG0 && r <= DWGREG0_END) +#define ADRINDEX0(r) (u8)((r - DWGREG0) >> 2) +#define ADRINDEX1(r) (u8)(((r - DWGREG1) >> 2) | 0x80) +#define ADRINDEX(r) (ISREG0(r) ? ADRINDEX0(r) : ADRINDEX1(r)) + +#define MGA_VERBOSE 0 +#define MGA_NUM_PRIM_BUFS 8 + +#define PRIMLOCALS u8 tempIndex[4]; u32 *dma_ptr; u32 phys_head; \ + int outcount, num_dwords + +#define PRIM_OVERFLOW(dev, dev_priv, length) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if( test_bit(MGA_BUF_NEEDS_OVERFLOW, \ + &tmp_buf->buffer_status)) { \ + mga_advance_primary(dev); \ + mga_dma_schedule(dev, 1); \ + } else if( tmp_buf->max_dwords - tmp_buf->num_dwords < length ||\ + tmp_buf->sec_used > MGA_DMA_BUF_NR/2) { \ + set_bit(MGA_BUF_FORCE_FIRE, &tmp_buf->buffer_status); \ + mga_advance_primary(dev); \ + mga_dma_schedule(dev, 1); \ + } \ +} while(0) + +#define PRIMGETPTR(dev_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if(MGA_VERBOSE) \ + DRM_DEBUG("PRIMGETPTR in %s\n", __FUNCTION__); \ + dma_ptr = tmp_buf->current_dma_ptr; \ + num_dwords = tmp_buf->num_dwords; \ + phys_head = tmp_buf->phys_head; \ + outcount = 0; \ +} while(0) + +#define PRIMPTR(prim_buf) do { \ + if(MGA_VERBOSE) \ + DRM_DEBUG("PRIMPTR in %s\n", __FUNCTION__); \ + dma_ptr = prim_buf->current_dma_ptr; \ + num_dwords = prim_buf->num_dwords; \ + phys_head = prim_buf->phys_head; \ + outcount = 0; \ +} while(0) + +#define PRIMFINISH(prim_buf) do { \ + if (MGA_VERBOSE) { \ + DRM_DEBUG( "PRIMFINISH in %s\n", __FUNCTION__); \ + if (outcount & 3) \ + DRM_DEBUG(" --- truncation\n"); \ + } \ + prim_buf->num_dwords = num_dwords; \ + prim_buf->current_dma_ptr = dma_ptr; \ +} while(0) + +#define PRIMADVANCE(dev_priv) do { \ +drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + if (MGA_VERBOSE) { \ + DRM_DEBUG("PRIMADVANCE in %s\n", __FUNCTION__); \ + if (outcount & 3) \ + DRM_DEBUG(" --- truncation\n"); \ + } \ + tmp_buf->num_dwords = num_dwords; \ + tmp_buf->current_dma_ptr = dma_ptr; \ +} while (0) + +#define PRIMUPDATE(dev_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + tmp_buf->sec_used++; \ +} while (0) + +#define AGEBUF(dev_priv, buf_priv) do { \ + drm_mga_prim_buf_t *tmp_buf = \ + dev_priv->prim_bufs[dev_priv->current_prim_idx]; \ + buf_priv->my_freelist->age = tmp_buf->prim_age; \ +} while (0) + + +#define PRIMOUTREG(reg, val) do { \ + tempIndex[outcount]=ADRINDEX(reg); \ + dma_ptr[1+outcount] = val; \ + if (MGA_VERBOSE) \ + DRM_DEBUG(" PRIMOUT %d: 0x%x -- 0x%x\n", \ + num_dwords + 1 + outcount, ADRINDEX(reg), val); \ + if( ++outcount == 4) { \ + outcount = 0; \ + dma_ptr[0] = *(u32 *)tempIndex; \ + dma_ptr+=5; \ + num_dwords += 5; \ + } \ +}while (0) + +/* A reduced set of the mga registers. + */ + +#define MGAREG_MGA_EXEC 0x0100 +#define MGAREG_ALPHACTRL 0x2c7c +#define MGAREG_AR0 0x1c60 +#define MGAREG_AR1 0x1c64 +#define MGAREG_AR2 0x1c68 +#define MGAREG_AR3 0x1c6c +#define MGAREG_AR4 0x1c70 +#define MGAREG_AR5 0x1c74 +#define MGAREG_AR6 0x1c78 +#define MGAREG_CXBNDRY 0x1c80 +#define MGAREG_CXLEFT 0x1ca0 +#define MGAREG_CXRIGHT 0x1ca4 +#define MGAREG_DMAPAD 0x1c54 +#define MGAREG_DSTORG 0x2cb8 +#define MGAREG_DWGCTL 0x1c00 +#define MGAREG_DWGSYNC 0x2c4c +#define MGAREG_FCOL 0x1c24 +#define MGAREG_FIFOSTATUS 0x1e10 +#define MGAREG_FOGCOL 0x1cf4 +#define MGAREG_FXBNDRY 0x1c84 +#define MGAREG_FXLEFT 0x1ca8 +#define MGAREG_FXRIGHT 0x1cac +#define MGAREG_ICLEAR 0x1e18 +#define MGAREG_IEN 0x1e1c +#define MGAREG_LEN 0x1c5c +#define MGAREG_MACCESS 0x1c04 +#define MGAREG_PITCH 0x1c8c +#define MGAREG_PLNWT 0x1c1c +#define MGAREG_PRIMADDRESS 0x1e58 +#define MGAREG_PRIMEND 0x1e5c +#define MGAREG_PRIMPTR 0x1e50 +#define MGAREG_SECADDRESS 0x2c40 +#define MGAREG_SECEND 0x2c44 +#define MGAREG_SETUPADDRESS 0x2cd0 +#define MGAREG_SETUPEND 0x2cd4 +#define MGAREG_SOFTRAP 0x2c48 +#define MGAREG_SRCORG 0x2cb4 +#define MGAREG_STATUS 0x1e14 +#define MGAREG_STENCIL 0x2cc8 +#define MGAREG_STENCILCTL 0x2ccc +#define MGAREG_TDUALSTAGE0 0x2cf8 +#define MGAREG_TDUALSTAGE1 0x2cfc +#define MGAREG_TEXBORDERCOL 0x2c5c +#define MGAREG_TEXCTL 0x2c30 +#define MGAREG_TEXCTL2 0x2c3c +#define MGAREG_TEXFILTER 0x2c58 +#define MGAREG_TEXHEIGHT 0x2c2c +#define MGAREG_TEXORG 0x2c24 +#define MGAREG_TEXORG1 0x2ca4 +#define MGAREG_TEXORG2 0x2ca8 +#define MGAREG_TEXORG3 0x2cac +#define MGAREG_TEXORG4 0x2cb0 +#define MGAREG_TEXTRANS 0x2c34 +#define MGAREG_TEXTRANSHIGH 0x2c38 +#define MGAREG_TEXWIDTH 0x2c28 +#define MGAREG_WACCEPTSEQ 0x1dd4 +#define MGAREG_WCODEADDR 0x1e6c +#define MGAREG_WFLAG 0x1dc4 +#define MGAREG_WFLAG1 0x1de0 +#define MGAREG_WFLAGNB 0x1e64 +#define MGAREG_WFLAGNB1 0x1e08 +#define MGAREG_WGETMSB 0x1dc8 +#define MGAREG_WIADDR 0x1dc0 +#define MGAREG_WIADDR2 0x1dd8 +#define MGAREG_WMISC 0x1e70 +#define MGAREG_WVRTXSZ 0x1dcc +#define MGAREG_YBOT 0x1c9c +#define MGAREG_YDST 0x1c90 +#define MGAREG_YDSTLEN 0x1c88 +#define MGAREG_YDSTORG 0x1c94 +#define MGAREG_YTOP 0x1c98 +#define MGAREG_ZORG 0x1c0c + +#define PDEA_pagpxfer_enable 0x2 + +#define WIA_wmode_suspend 0x0 +#define WIA_wmode_start 0x3 +#define WIA_wagp_agp 0x4 + +#define DC_opcod_line_open 0x0 +#define DC_opcod_autoline_open 0x1 +#define DC_opcod_line_close 0x2 +#define DC_opcod_autoline_close 0x3 +#define DC_opcod_trap 0x4 +#define DC_opcod_texture_trap 0x6 +#define DC_opcod_bitblt 0x8 +#define DC_opcod_iload 0x9 +#define DC_atype_rpl 0x0 +#define DC_atype_rstr 0x10 +#define DC_atype_zi 0x30 +#define DC_atype_blk 0x40 +#define DC_atype_i 0x70 +#define DC_linear_xy 0x0 +#define DC_linear_linear 0x80 +#define DC_zmode_nozcmp 0x0 +#define DC_zmode_ze 0x200 +#define DC_zmode_zne 0x300 +#define DC_zmode_zlt 0x400 +#define DC_zmode_zlte 0x500 +#define DC_zmode_zgt 0x600 +#define DC_zmode_zgte 0x700 +#define DC_solid_disable 0x0 +#define DC_solid_enable 0x800 +#define DC_arzero_disable 0x0 +#define DC_arzero_enable 0x1000 +#define DC_sgnzero_disable 0x0 +#define DC_sgnzero_enable 0x2000 +#define DC_shftzero_disable 0x0 +#define DC_shftzero_enable 0x4000 +#define DC_bop_SHIFT 16 +#define DC_trans_SHIFT 20 +#define DC_bltmod_bmonolef 0x0 +#define DC_bltmod_bmonowf 0x8000000 +#define DC_bltmod_bplan 0x2000000 +#define DC_bltmod_bfcol 0x4000000 +#define DC_bltmod_bu32bgr 0x6000000 +#define DC_bltmod_bu32rgb 0xe000000 +#define DC_bltmod_bu24bgr 0x16000000 +#define DC_bltmod_bu24rgb 0x1e000000 +#define DC_pattern_disable 0x0 +#define DC_pattern_enable 0x20000000 +#define DC_transc_disable 0x0 +#define DC_transc_enable 0x40000000 +#define DC_clipdis_disable 0x0 +#define DC_clipdis_enable 0x80000000 + +#define SETADD_mode_vertlist 0x0 + + +#define MGA_CLEAR_CMD (DC_opcod_trap | DC_arzero_enable | \ + DC_sgnzero_enable | DC_shftzero_enable | \ + (0xC << DC_bop_SHIFT) | DC_clipdis_enable | \ + DC_solid_enable | DC_transc_enable) + + +#define MGA_COPY_CMD (DC_opcod_bitblt | DC_atype_rpl | DC_linear_xy | \ + DC_solid_disable | DC_arzero_disable | \ + DC_sgnzero_enable | DC_shftzero_enable | \ + (0xC << DC_bop_SHIFT) | DC_bltmod_bfcol | \ + DC_pattern_disable | DC_transc_disable | \ + DC_clipdis_enable) \ + +#define MGA_FLUSH_CMD (DC_opcod_texture_trap | (0xF << DC_trans_SHIFT) |\ + DC_arzero_enable | DC_sgnzero_enable | \ + DC_atype_i) + +#endif diff --git a/drivers/char/drm/mga_state.c b/drivers/char/drm/mga_state.c new file mode 100644 index 000000000000..723ccc539e04 --- /dev/null +++ b/drivers/char/drm/mga_state.c @@ -0,0 +1,1067 @@ +/* mga_state.c -- State support for mga g200/g400 -*- linux-c -*- + * Created: Thu Jan 27 02:53:43 2000 by jhartmann@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Jeff Hartmann + * Keith Whitwell + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "mga_drv.h" +#include "drm.h" + +static void mgaEmitClipRect(drm_mga_private_t * dev_priv, + drm_clip_rect_t * box) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + /* This takes 10 dwords */ + PRIMGETPTR(dev_priv); + + /* Force reset of dwgctl (eliminates clip disable) */ +#if 0 + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); +#else + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0x80000000); +#endif + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_CXBNDRY, ((box->x2) << 16) | (box->x1)); + PRIMOUTREG(MGAREG_YTOP, box->y1 * dev_priv->stride / 2); + PRIMOUTREG(MGAREG_YBOT, box->y2 * dev_priv->stride / 2); + + PRIMADVANCE(dev_priv); +} + +static void mgaEmitContext(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + /* This takes a max of 15 dwords */ + PRIMGETPTR(dev_priv); + + PRIMOUTREG(MGAREG_DSTORG, regs[MGA_CTXREG_DSTORG]); + PRIMOUTREG(MGAREG_MACCESS, regs[MGA_CTXREG_MACCESS]); + PRIMOUTREG(MGAREG_PLNWT, regs[MGA_CTXREG_PLNWT]); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + + PRIMOUTREG(MGAREG_ALPHACTRL, regs[MGA_CTXREG_ALPHACTRL]); + PRIMOUTREG(MGAREG_FOGCOL, regs[MGA_CTXREG_FOGCOLOR]); + PRIMOUTREG(MGAREG_WFLAG, regs[MGA_CTXREG_WFLAG]); + PRIMOUTREG(MGAREG_ZORG, dev_priv->depthOffset); /* invarient */ + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + PRIMOUTREG(MGAREG_WFLAG1, regs[MGA_CTXREG_WFLAG]); + PRIMOUTREG(MGAREG_TDUALSTAGE0, regs[MGA_CTXREG_TDUAL0]); + PRIMOUTREG(MGAREG_TDUALSTAGE1, regs[MGA_CTXREG_TDUAL1]); + PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]); + } else { + PRIMOUTREG(MGAREG_FCOL, regs[MGA_CTXREG_FCOL]); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + } + + PRIMADVANCE(dev_priv); +} + +static void mgaG200EmitTex(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[0]; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + PRIMGETPTR(dev_priv); + + /* This takes 20 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2]); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(0x2d00 + 24 * 4, regs[MGA_TEXREG_WIDTH]); + + PRIMOUTREG(0x2d00 + 34 * 4, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMADVANCE(dev_priv); +} + +static void mgaG400EmitTex0(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[0]; + int multitex = sarea_priv->WarpPipe & MGA_T2; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + PRIMGETPTR(dev_priv); + + /* This takes a max of 30 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(0x2d00 + 49 * 4, 0); + + PRIMOUTREG(0x2d00 + 57 * 4, 0); + PRIMOUTREG(0x2d00 + 53 * 4, 0); + PRIMOUTREG(0x2d00 + 61 * 4, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + if (!multitex) { + PRIMOUTREG(0x2d00 + 52 * 4, 0x40); + PRIMOUTREG(0x2d00 + 60 * 4, 0x40); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + } + + PRIMOUTREG(0x2d00 + 54 * 4, regs[MGA_TEXREG_WIDTH] | 0x40); + PRIMOUTREG(0x2d00 + 62 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + + PRIMADVANCE(dev_priv); +} + +#define TMC_map1_enable 0x80000000 + +static void mgaG400EmitTex1(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->TexState[1]; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + PRIMGETPTR(dev_priv); + + /* This takes 25 dwords */ + + PRIMOUTREG(MGAREG_TEXCTL2, + regs[MGA_TEXREG_CTL2] | TMC_map1_enable | 0x00008000); + PRIMOUTREG(MGAREG_TEXCTL, regs[MGA_TEXREG_CTL]); + PRIMOUTREG(MGAREG_TEXFILTER, regs[MGA_TEXREG_FILTER]); + PRIMOUTREG(MGAREG_TEXBORDERCOL, regs[MGA_TEXREG_BORDERCOL]); + + PRIMOUTREG(MGAREG_TEXORG, regs[MGA_TEXREG_ORG]); + PRIMOUTREG(MGAREG_TEXORG1, regs[MGA_TEXREG_ORG1]); + PRIMOUTREG(MGAREG_TEXORG2, regs[MGA_TEXREG_ORG2]); + PRIMOUTREG(MGAREG_TEXORG3, regs[MGA_TEXREG_ORG3]); + + PRIMOUTREG(MGAREG_TEXORG4, regs[MGA_TEXREG_ORG4]); + PRIMOUTREG(MGAREG_TEXWIDTH, regs[MGA_TEXREG_WIDTH]); + PRIMOUTREG(MGAREG_TEXHEIGHT, regs[MGA_TEXREG_HEIGHT]); + PRIMOUTREG(0x2d00 + 49 * 4, 0); + + PRIMOUTREG(0x2d00 + 57 * 4, 0); + PRIMOUTREG(0x2d00 + 53 * 4, 0); + PRIMOUTREG(0x2d00 + 61 * 4, 0); + PRIMOUTREG(0x2d00 + 52 * 4, regs[MGA_TEXREG_WIDTH] | 0x40); + + PRIMOUTREG(0x2d00 + 60 * 4, regs[MGA_TEXREG_HEIGHT] | 0x40); + PRIMOUTREG(MGAREG_TEXTRANS, 0xffff); + PRIMOUTREG(MGAREG_TEXTRANSHIGH, 0xffff); + PRIMOUTREG(MGAREG_TEXCTL2, regs[MGA_TEXREG_CTL2] | 0x00008000); + + PRIMADVANCE(dev_priv); +} + +#define EMIT_PIPE 50 +static void mgaG400EmitPipe(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int pipe = sarea_priv->WarpPipe; + float fParam = 12800.0f; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + PRIMGETPTR(dev_priv); + + /* This takes 50 dwords */ + + /* Establish vertex size. + */ + PRIMOUTREG(MGAREG_WIADDR2, WIA_wmode_suspend); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + if (pipe & MGA_T2) { + PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001e09); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x1e000000); + } else { + if (dev_priv->WarpPipe & MGA_T2) { + /* Flush the WARP pipe */ + PRIMOUTREG(MGAREG_YDST, 0); + PRIMOUTREG(MGAREG_FXLEFT, 0); + PRIMOUTREG(MGAREG_FXRIGHT, 1); + PRIMOUTREG(MGAREG_DWGCTL, MGA_FLUSH_CMD); + + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 1); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7000); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_TEXCTL2, 0 | 0x00008000); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0); + PRIMOUTREG(MGAREG_TEXCTL2, 0x80 | 0x00008000); + PRIMOUTREG(MGAREG_LEN + MGAREG_MGA_EXEC, 0); + } + + PRIMOUTREG(MGAREG_WVRTXSZ, 0x00001807); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0); + PRIMOUTREG(MGAREG_WACCEPTSEQ, 0x18000000); + } + + PRIMOUTREG(MGAREG_WFLAG, 0); + PRIMOUTREG(MGAREG_WFLAG1, 0); + PRIMOUTREG(0x2d00 + 56 * 4, *((u32 *) (&fParam))); + PRIMOUTREG(MGAREG_DMAPAD, 0); + + PRIMOUTREG(0x2d00 + 49 * 4, 0); /* Tex stage 0 */ + PRIMOUTREG(0x2d00 + 57 * 4, 0); /* Tex stage 0 */ + PRIMOUTREG(0x2d00 + 53 * 4, 0); /* Tex stage 1 */ + PRIMOUTREG(0x2d00 + 61 * 4, 0); /* Tex stage 1 */ + + PRIMOUTREG(0x2d00 + 54 * 4, 0x40); /* Tex stage 0 : w */ + PRIMOUTREG(0x2d00 + 62 * 4, 0x40); /* Tex stage 0 : h */ + PRIMOUTREG(0x2d00 + 52 * 4, 0x40); /* Tex stage 1 : w */ + PRIMOUTREG(0x2d00 + 60 * 4, 0x40); /* Tex stage 1 : h */ + + /* Dma pading required due to hw bug */ + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_WIADDR2, + (u32) (dev_priv->WarpIndex[pipe]. + phys_addr | WIA_wmode_start | WIA_wagp_agp)); + PRIMADVANCE(dev_priv); +} + +static void mgaG200EmitPipe(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int pipe = sarea_priv->WarpPipe; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + PRIMGETPTR(dev_priv); + + /* This takes 15 dwords */ + + PRIMOUTREG(MGAREG_WIADDR, WIA_wmode_suspend); + PRIMOUTREG(MGAREG_WVRTXSZ, 7); + PRIMOUTREG(MGAREG_WFLAG, 0); + PRIMOUTREG(0x2d00 + 24 * 4, 0); /* tex w/h */ + + PRIMOUTREG(0x2d00 + 25 * 4, 0x100); + PRIMOUTREG(0x2d00 + 34 * 4, 0); /* tex w/h */ + PRIMOUTREG(0x2d00 + 42 * 4, 0xFFFF); + PRIMOUTREG(0x2d00 + 60 * 4, 0xFFFF); + + /* Dma pading required due to hw bug */ + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_DMAPAD, 0xffffffff); + PRIMOUTREG(MGAREG_WIADDR, + (u32) (dev_priv->WarpIndex[pipe]. + phys_addr | WIA_wmode_start | WIA_wagp_agp)); + + PRIMADVANCE( dev_priv ); +} + +static void mgaEmitState(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + DRM_DEBUG("%s\n", __FUNCTION__); + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + int multitex = sarea_priv->WarpPipe & MGA_T2; + + if (sarea_priv->WarpPipe != dev_priv->WarpPipe) { + mgaG400EmitPipe(dev_priv); + dev_priv->WarpPipe = sarea_priv->WarpPipe; + } + + if (dirty & MGA_UPLOAD_CTX) { + mgaEmitContext(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_CTX; + } + + if (dirty & MGA_UPLOAD_TEX0) { + mgaG400EmitTex0(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX0; + } + + if ((dirty & MGA_UPLOAD_TEX1) && multitex) { + mgaG400EmitTex1(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX1; + } + } else { + if (sarea_priv->WarpPipe != dev_priv->WarpPipe) { + mgaG200EmitPipe(dev_priv); + dev_priv->WarpPipe = sarea_priv->WarpPipe; + } + + if (dirty & MGA_UPLOAD_CTX) { + mgaEmitContext(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_CTX; + } + + if (dirty & MGA_UPLOAD_TEX0) { + mgaG200EmitTex(dev_priv); + sarea_priv->dirty &= ~MGA_UPLOAD_TEX0; + } + } +} + + +/* Disallow all write destinations except the front and backbuffer. + */ +static int mgaVerifyContext(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (regs[MGA_CTXREG_DSTORG] != dev_priv->frontOffset && + regs[MGA_CTXREG_DSTORG] != dev_priv->backOffset) { + DRM_DEBUG("BAD DSTORG: %x (front %x, back %x)\n\n", + regs[MGA_CTXREG_DSTORG], dev_priv->frontOffset, + dev_priv->backOffset); + regs[MGA_CTXREG_DSTORG] = 0; + return -1; + } + + return 0; +} + +/* Disallow texture reads from PCI space. + */ +static int mgaVerifyTex(drm_mga_private_t * dev_priv, int unit) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if ((sarea_priv->TexState[unit][MGA_TEXREG_ORG] & 0x3) == 0x1) { + DRM_DEBUG("BAD TEXREG_ORG: %x, unit %d\n", + sarea_priv->TexState[unit][MGA_TEXREG_ORG], + unit); + sarea_priv->TexState[unit][MGA_TEXREG_ORG] = 0; + return -1; + } + + return 0; +} + +static int mgaVerifyState(drm_mga_private_t * dev_priv) +{ + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int dirty = sarea_priv->dirty; + int rv = 0; + + DRM_DEBUG("%s\n", __FUNCTION__); + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + if (dirty & MGA_UPLOAD_CTX) + rv |= mgaVerifyContext(dev_priv); + + if (dirty & MGA_UPLOAD_TEX0) + rv |= mgaVerifyTex(dev_priv, 0); + + if (dev_priv->chipset == MGA_CARD_TYPE_G400) { + if (dirty & MGA_UPLOAD_TEX1) + rv |= mgaVerifyTex(dev_priv, 1); + + if (dirty & MGA_UPLOAD_PIPE) + rv |= (sarea_priv->WarpPipe > MGA_MAX_G400_PIPES); + } else { + if (dirty & MGA_UPLOAD_PIPE) + rv |= (sarea_priv->WarpPipe > MGA_MAX_G200_PIPES); + } + + return rv == 0; +} + +static int mgaVerifyIload(drm_mga_private_t * dev_priv, + unsigned long bus_address, + unsigned int dstOrg, int length) +{ + DRM_DEBUG("%s\n", __FUNCTION__); + + if (dstOrg < dev_priv->textureOffset || + dstOrg + length > + (dev_priv->textureOffset + dev_priv->textureSize)) { + return -EINVAL; + } + if (length % 64) { + return -EINVAL; + } + return 0; +} + +/* This copies a 64 byte aligned agp region to the frambuffer + * with a standard blit, the ioctl needs to do checking */ + +static void mga_dma_dispatch_tex_blit(drm_device_t * dev, + unsigned long bus_address, + int length, unsigned int destOrg) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + int use_agp = PDEA_pagpxfer_enable | 0x00000001; + u16 y2; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + y2 = length / 64; + + PRIM_OVERFLOW(dev, dev_priv, 30); + PRIMGETPTR(dev_priv); + + PRIMOUTREG(MGAREG_DSTORG, destOrg); + PRIMOUTREG(MGAREG_MACCESS, 0x00000000); + DRM_DEBUG("srcorg : %lx\n", bus_address | use_agp); + PRIMOUTREG(MGAREG_SRCORG, (u32) bus_address | use_agp); + PRIMOUTREG(MGAREG_AR5, 64); + + PRIMOUTREG(MGAREG_PITCH, 64); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD); + + PRIMOUTREG(MGAREG_AR0, 63); + PRIMOUTREG(MGAREG_AR3, 0); + PRIMOUTREG(MGAREG_FXBNDRY, (63 << 16)); + PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, y2); + + PRIMOUTREG(MGAREG_SRCORG, 0); + PRIMOUTREG(MGAREG_PITCH, dev_priv->stride / dev_priv->cpp); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMADVANCE(dev_priv); +} + +static void mga_dma_dispatch_vertex(drm_device_t * dev, drm_buf_t * buf) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned long address = (unsigned long) buf->bus_address; + int length = buf->used; + int use_agp = PDEA_pagpxfer_enable; + int i = 0; + int primary_needed; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + DRM_DEBUG("dispatch vertex %d addr 0x%lx, " + "length 0x%x nbox %d dirty %x\n", + buf->idx, address, length, + sarea_priv->nbox, sarea_priv->dirty); + + DRM_DEBUG("used : %d, total : %d\n", buf->used, buf->total); + + if (buf->used) { + /* WARNING: if you change any of the state functions verify + * these numbers (Overestimating this doesn't hurt). + */ + buf_priv->dispatched = 1; + primary_needed = (50 + 15 + 15 + 30 + 25 + + 10 + 15 * MGA_NR_SAREA_CLIPRECTS); + PRIM_OVERFLOW(dev, dev_priv, primary_needed); + mgaEmitState(dev_priv); + do { + if (i < sarea_priv->nbox) { + DRM_DEBUG("idx %d Emit box %d/%d:" + "%d,%d - %d,%d\n", + buf->idx, + i, sarea_priv->nbox, + sarea_priv->boxes[i].x1, + sarea_priv->boxes[i].y1, + sarea_priv->boxes[i].x2, + sarea_priv->boxes[i].y2); + + mgaEmitClipRect(dev_priv, + &sarea_priv->boxes[i]); + } + + PRIMGETPTR(dev_priv); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SECADDRESS, + ((u32) address) | TT_VERTEX); + PRIMOUTREG(MGAREG_SECEND, + (((u32) (address + length)) | use_agp)); + PRIMADVANCE(dev_priv); + } while (++i < sarea_priv->nbox); + } + if (buf_priv->discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + + +} + + +static void mga_dma_dispatch_indices(drm_device_t * dev, + drm_buf_t * buf, + unsigned int start, unsigned int end) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_buf_priv_t *buf_priv = buf->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int address = (unsigned int) buf->bus_address; + int use_agp = PDEA_pagpxfer_enable; + int i = 0; + int primary_needed; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + DRM_DEBUG("dispatch indices %d addr 0x%x, " + "start 0x%x end 0x%x nbox %d dirty %x\n", + buf->idx, address, start, end, + sarea_priv->nbox, sarea_priv->dirty); + + if (start != end) { + /* WARNING: if you change any of the state functions verify + * these numbers (Overestimating this doesn't hurt). + */ + buf_priv->dispatched = 1; + primary_needed = (50 + 15 + 15 + 30 + 25 + + 10 + 15 * MGA_NR_SAREA_CLIPRECTS); + PRIM_OVERFLOW(dev, dev_priv, primary_needed); + mgaEmitState(dev_priv); + + do { + if (i < sarea_priv->nbox) { + DRM_DEBUG("idx %d Emit box %d/%d:" + "%d,%d - %d,%d\n", + buf->idx, + i, sarea_priv->nbox, + sarea_priv->boxes[i].x1, + sarea_priv->boxes[i].y1, + sarea_priv->boxes[i].x2, + sarea_priv->boxes[i].y2); + + mgaEmitClipRect(dev_priv, + &sarea_priv->boxes[i]); + } + + PRIMGETPTR(dev_priv); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SETUPADDRESS, + ((address + start) | + SETADD_mode_vertlist)); + PRIMOUTREG(MGAREG_SETUPEND, + ((address + end) | use_agp)); + PRIMADVANCE(dev_priv); + } while (++i < sarea_priv->nbox); + } + if (buf_priv->discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } +} + + +static void mga_dma_dispatch_clear(drm_device_t * dev, int flags, + unsigned int clear_color, + unsigned int clear_zval) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + unsigned int cmd; + int i; + int primary_needed; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + if (dev_priv->sgram) + cmd = MGA_CLEAR_CMD | DC_atype_blk; + else + cmd = MGA_CLEAR_CMD | DC_atype_rstr; + + primary_needed = nbox * 70; + if (primary_needed == 0) + primary_needed = 70; + PRIM_OVERFLOW(dev, dev_priv, primary_needed); + PRIMGETPTR(dev_priv); + + for (i = 0; i < nbox; i++) { + unsigned int height = pbox[i].y2 - pbox[i].y1; + + DRM_DEBUG("dispatch clear %d,%d-%d,%d flags %x!\n", + pbox[i].x1, pbox[i].y1, pbox[i].x2, + pbox[i].y2, flags); + + if (flags & MGA_FRONT) { + DRM_DEBUG("clear front\n"); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_color); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + + if (flags & MGA_BACK) { + DRM_DEBUG("clear back\n"); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_color); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->backOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + + if (flags & MGA_DEPTH) { + DRM_DEBUG("clear depth\n"); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_YDSTLEN, + (pbox[i].y1 << 16) | height); + PRIMOUTREG(MGAREG_FXBNDRY, + (pbox[i].x2 << 16) | pbox[i].x1); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_FCOL, clear_zval); + PRIMOUTREG(MGAREG_DSTORG, dev_priv->depthOffset); + PRIMOUTREG(MGAREG_DWGCTL + MGAREG_MGA_EXEC, cmd); + } + } + + /* Force reset of DWGCTL */ + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + PRIMADVANCE(dev_priv); +} + +static void mga_dma_dispatch_swap(drm_device_t * dev) +{ + drm_mga_private_t *dev_priv = dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + unsigned int *regs = sarea_priv->ContextState; + int nbox = sarea_priv->nbox; + drm_clip_rect_t *pbox = sarea_priv->boxes; + int i; + int primary_needed; + PRIMLOCALS; + DRM_DEBUG("%s\n", __FUNCTION__); + + primary_needed = nbox * 5; + primary_needed += 65; + PRIM_OVERFLOW(dev, dev_priv, primary_needed); + PRIMGETPTR(dev_priv); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7100); + PRIMOUTREG(MGAREG_DWGSYNC, 0x7000); + + PRIMOUTREG(MGAREG_DSTORG, dev_priv->frontOffset); + PRIMOUTREG(MGAREG_MACCESS, dev_priv->mAccess); + PRIMOUTREG(MGAREG_SRCORG, dev_priv->backOffset); + PRIMOUTREG(MGAREG_AR5, dev_priv->stride / 2); + + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DWGCTL, MGA_COPY_CMD); + + for (i = 0; i < nbox; i++) { + unsigned int h = pbox[i].y2 - pbox[i].y1; + unsigned int start = pbox[i].y1 * dev_priv->stride / 2; + + DRM_DEBUG("dispatch swap %d,%d-%d,%d!\n", + pbox[i].x1, pbox[i].y1, pbox[i].x2, pbox[i].y2); + + PRIMOUTREG(MGAREG_AR0, start + pbox[i].x2 - 1); + PRIMOUTREG(MGAREG_AR3, start + pbox[i].x1); + PRIMOUTREG(MGAREG_FXBNDRY, + pbox[i].x1 | ((pbox[i].x2 - 1) << 16)); + PRIMOUTREG(MGAREG_YDSTLEN + MGAREG_MGA_EXEC, + (pbox[i].y1 << 16) | h); + } + + /* Force reset of DWGCTL */ + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_DMAPAD, 0); + PRIMOUTREG(MGAREG_SRCORG, 0); + PRIMOUTREG(MGAREG_DWGCTL, regs[MGA_CTXREG_DWGCTL]); + + PRIMADVANCE(dev_priv); +} + +int mga_clear_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_mga_clear_t clear; + + copy_from_user_ret(&clear, (drm_mga_clear_t *) arg, sizeof(clear), + -EFAULT); + DRM_DEBUG("%s\n", __FUNCTION__); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_clear_bufs called without lock held\n"); + return -EINVAL; + } + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX; + mga_dma_dispatch_clear(dev, clear.flags, + clear.clear_color, clear.clear_depth); + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_swap_bufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + DRM_DEBUG("%s\n", __FUNCTION__); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_swap_bufs called without lock held\n"); + return -EINVAL; + } + + if (sarea_priv->nbox > MGA_NR_SAREA_CLIPRECTS) + sarea_priv->nbox = MGA_NR_SAREA_CLIPRECTS; + + /* Make sure we restore the 3D state next time. + */ + dev_priv->sarea_priv->dirty |= MGA_UPLOAD_CTX; + mga_dma_dispatch_swap(dev); + PRIMUPDATE(dev_priv); + set_bit(MGA_BUF_SWAP_PENDING, + &dev_priv->current_prim->buffer_status); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_iload(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_mga_sarea_t *sarea_priv = dev_priv->sarea_priv; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_iload_t iload; + unsigned long bus_address; + DRM_DEBUG("%s\n", __FUNCTION__); + + DRM_DEBUG("Starting Iload\n"); + copy_from_user_ret(&iload, (drm_mga_iload_t *) arg, sizeof(iload), + -EFAULT); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_iload called without lock held\n"); + return -EINVAL; + } + + buf = dma->buflist[iload.idx]; + buf_priv = buf->dev_private; + bus_address = buf->bus_address; + DRM_DEBUG("bus_address %lx, length %d, destorg : %x\n", + bus_address, iload.length, iload.destOrg); + + if (mgaVerifyIload(dev_priv, + bus_address, iload.destOrg, iload.length)) { + mga_freelist_put(dev, buf); + return -EINVAL; + } + + sarea_priv->dirty |= MGA_UPLOAD_CTX; + + mga_dma_dispatch_tex_blit(dev, bus_address, iload.length, + iload.destOrg); + AGEBUF(dev_priv, buf_priv); + buf_priv->discard = 1; + mga_freelist_put(dev, buf); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + +int mga_vertex(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_vertex_t vertex; + DRM_DEBUG("%s\n", __FUNCTION__); + + copy_from_user_ret(&vertex, (drm_mga_vertex_t *) arg, + sizeof(vertex), -EFAULT); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_vertex called without lock held\n"); + return -EINVAL; + } + + DRM_DEBUG("mga_vertex\n"); + + buf = dma->buflist[vertex.idx]; + buf_priv = buf->dev_private; + + buf->used = vertex.used; + buf_priv->discard = vertex.discard; + + if (!mgaVerifyState(dev_priv)) { + if (vertex.discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + DRM_DEBUG("bad state\n"); + return -EINVAL; + } + + mga_dma_dispatch_vertex(dev, buf); + + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + + +int mga_indices(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_mga_private_t *dev_priv = + (drm_mga_private_t *) dev->dev_private; + drm_device_dma_t *dma = dev->dma; + drm_buf_t *buf; + drm_mga_buf_priv_t *buf_priv; + drm_mga_indices_t indices; + DRM_DEBUG("%s\n", __FUNCTION__); + + copy_from_user_ret(&indices, (drm_mga_indices_t *) arg, + sizeof(indices), -EFAULT); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_indices called without lock held\n"); + return -EINVAL; + } + + DRM_DEBUG("mga_indices\n"); + + buf = dma->buflist[indices.idx]; + buf_priv = buf->dev_private; + + buf_priv->discard = indices.discard; + + if (!mgaVerifyState(dev_priv)) { + if (indices.discard) { + if (buf_priv->dispatched == 1) + AGEBUF(dev_priv, buf_priv); + buf_priv->dispatched = 0; + mga_freelist_put(dev, buf); + } + return -EINVAL; + } + + mga_dma_dispatch_indices(dev, buf, indices.start, indices.end); + + PRIMUPDATE(dev_priv); + mga_flush_write_combine(); + mga_dma_schedule(dev, 1); + return 0; +} + + + +static int mga_dma_get_buffers(drm_device_t * dev, drm_dma_t * d) +{ + int i; + drm_buf_t *buf; + DRM_DEBUG("%s\n", __FUNCTION__); + + for (i = d->granted_count; i < d->request_count; i++) { + buf = mga_freelist_get(dev); + if (!buf) + break; + buf->pid = current->pid; + copy_to_user_ret(&d->request_indices[i], + &buf->idx, sizeof(buf->idx), -EFAULT); + copy_to_user_ret(&d->request_sizes[i], + &buf->total, sizeof(buf->total), -EFAULT); + ++d->granted_count; + } + return 0; +} + +int mga_dma(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_dma_t d; + DRM_DEBUG("%s\n", __FUNCTION__); + + copy_from_user_ret(&d, (drm_dma_t *) arg, sizeof(d), -EFAULT); + DRM_DEBUG("%d %d: %d send, %d req\n", + current->pid, d.context, d.send_count, d.request_count); + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("mga_dma called without lock held\n"); + return -EINVAL; + } + + /* Please don't send us buffers. + */ + if (d.send_count != 0) { + DRM_ERROR + ("Process %d trying to send %d buffers via drmDMA\n", + current->pid, d.send_count); + return -EINVAL; + } + + /* We'll send you buffers. + */ + if (d.request_count < 0 || d.request_count > dma->buf_count) { + DRM_ERROR + ("Process %d trying to get %d buffers (of %d max)\n", + current->pid, d.request_count, dma->buf_count); + return -EINVAL; + } + + d.granted_count = 0; + + if (d.request_count) { + retcode = mga_dma_get_buffers(dev, &d); + } + + DRM_DEBUG("%d returning, granted = %d\n", + current->pid, d.granted_count); + copy_to_user_ret((drm_dma_t *) arg, &d, sizeof(d), -EFAULT); + return retcode; +} diff --git a/drivers/char/drm/proc.c b/drivers/char/drm/proc.c new file mode 100644 index 000000000000..ca062a09bd0d --- /dev/null +++ b/drivers/char/drm/proc.c @@ -0,0 +1,577 @@ +/* proc.c -- /proc support for DRM -*- linux-c -*- + * Created: Mon Jan 11 09:48:47 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + */ + +#define __NO_VERSION__ +#include "drmP.h" + +static struct proc_dir_entry *drm_root = NULL; +static struct proc_dir_entry *drm_dev_root = NULL; +static char drm_slot_name[64]; + +static int drm_name_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_vm_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_clients_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_queues_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +static int drm_bufs_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#if DRM_DEBUG_CODE +static int drm_vma_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif +#if DRM_DMA_HISTOGRAM +static int drm_histo_info(char *buf, char **start, off_t offset, + int len, int *eof, void *data); +#endif + +struct drm_proc_list { + const char *name; + int (*f)(char *, char **, off_t, int, int *, void *); +} drm_proc_list[] = { + { "name", drm_name_info }, + { "mem", drm_mem_info }, + { "vm", drm_vm_info }, + { "clients", drm_clients_info }, + { "queues", drm_queues_info }, + { "bufs", drm_bufs_info }, +#if DRM_DEBUG_CODE + { "vma", drm_vma_info }, +#endif +#if DRM_DMA_HISTOGRAM + { "histo", drm_histo_info }, +#endif +}; +#define DRM_PROC_ENTRIES (sizeof(drm_proc_list)/sizeof(drm_proc_list[0])) + +int drm_proc_init(drm_device_t *dev) +{ + struct proc_dir_entry *ent; + int i, j; + + drm_root = create_proc_entry("dri", S_IFDIR, NULL); + if (!drm_root) { + DRM_ERROR("Cannot create /proc/dri\n"); + return -1; + } + + /* Instead of doing this search, we should + add some global support for /proc/dri. */ + for (i = 0; i < 8; i++) { + sprintf(drm_slot_name, "dri/%d", i); + drm_dev_root = create_proc_entry(drm_slot_name, S_IFDIR, NULL); + if (!drm_dev_root) { + DRM_ERROR("Cannot create /proc/%s\n", drm_slot_name); + remove_proc_entry("dri", NULL); + } + if (drm_dev_root->nlink == 2) break; + drm_dev_root = NULL; + } + if (!drm_dev_root) { + DRM_ERROR("Cannot find slot in /proc/dri\n"); + return -1; + } + + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + ent = create_proc_entry(drm_proc_list[i].name, + S_IFREG|S_IRUGO, drm_dev_root); + if (!ent) { + DRM_ERROR("Cannot create /proc/%s/%s\n", + drm_slot_name, drm_proc_list[i].name); + for (j = 0; j < i; j++) + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + remove_proc_entry(drm_slot_name, NULL); + remove_proc_entry("dri", NULL); + return -1; + } + ent->read_proc = drm_proc_list[i].f; + ent->data = dev; + } + + return 0; +} + + +int drm_proc_cleanup(void) +{ + int i; + + if (drm_root) { + if (drm_dev_root) { + for (i = 0; i < DRM_PROC_ENTRIES; i++) { + remove_proc_entry(drm_proc_list[i].name, + drm_dev_root); + } + remove_proc_entry(drm_slot_name, NULL); + } + remove_proc_entry("dri", NULL); + remove_proc_entry(DRM_NAME, NULL); + } + drm_root = drm_dev_root = NULL; + return 0; +} + +static int drm_name_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + if (dev->unique) { + DRM_PROC_PRINT("%s 0x%x %s\n", + dev->name, dev->device, dev->unique); + } else { + DRM_PROC_PRINT("%s 0x%x\n", dev->name, dev->device); + } + return len; +} + +static int _drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_map_t *map; + /* Hardcoded from _DRM_FRAME_BUFFER, + _DRM_REGISTERS, _DRM_SHM, and + _DRM_AGP. */ + const char *types[] = { "FB", "REG", "SHM", "AGP" }; + const char *type; + int i; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("slot offset size type flags " + "address mtrr\n\n"); + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->type < 0 || map->type > 3) type = "??"; + else type = types[map->type]; + DRM_PROC_PRINT("%4d 0x%08lx 0x%08lx %4.4s 0x%02x 0x%08lx ", + i, + map->offset, + map->size, + type, + map->flags, + (unsigned long)map->handle); + if (map->mtrr < 0) { + DRM_PROC_PRINT("none\n"); + } else { + DRM_PROC_PRINT("%4d\n", map->mtrr); + } + } + + return len; +} + +static int drm_vm_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vm_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int i; + drm_queue_t *q; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" ctx/flags use fin" + " blk/rw/rwf wait flushed queued" + " locks\n\n"); + for (i = 0; i < dev->queue_count; i++) { + q = dev->queuelist[i]; + atomic_inc(&q->use_count); + DRM_PROC_PRINT_RET(atomic_dec(&q->use_count), + "%5d/0x%03x %5d %5d" + " %5d/%c%c/%c%c%c %5Zd %10d %10d %10d\n", + i, + q->flags, + atomic_read(&q->use_count), + atomic_read(&q->finalization), + atomic_read(&q->block_count), + atomic_read(&q->block_read) ? 'r' : '-', + atomic_read(&q->block_write) ? 'w' : '-', + waitqueue_active(&q->read_queue) ? 'r':'-', + waitqueue_active(&q->write_queue) ? 'w':'-', + waitqueue_active(&q->flush_queue) ? 'f':'-', + DRM_BUFCOUNT(&q->waitlist), + atomic_read(&q->total_flushed), + atomic_read(&q->total_queued), + atomic_read(&q->total_locks)); + atomic_dec(&q->use_count); + } + + return len; +} + +static int drm_queues_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_queues_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +/* drm_bufs_info is called whenever a process reads + /dev/drm//bufs. */ + +static int _drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + + if (!dma) return 0; + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT(" o size count free segs pages kB\n\n"); + for (i = 0; i <= DRM_MAX_ORDER; i++) { + if (dma->bufs[i].buf_count) + DRM_PROC_PRINT("%2d %8d %5d %5d %5d %5d %5ld\n", + i, + dma->bufs[i].buf_size, + dma->bufs[i].buf_count, + atomic_read(&dma->bufs[i] + .freelist.count), + dma->bufs[i].seg_count, + dma->bufs[i].seg_count + *(1 << dma->bufs[i].page_order), + (dma->bufs[i].seg_count + * (1 << dma->bufs[i].page_order)) + * PAGE_SIZE / 1024); + } + DRM_PROC_PRINT("\n"); + for (i = 0; i < dma->buf_count; i++) { + if (i && !(i%32)) DRM_PROC_PRINT("\n"); + DRM_PROC_PRINT(" %d", dma->buflist[i]->list); + } + DRM_PROC_PRINT("\n"); + + return len; +} + +static int drm_bufs_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_bufs_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + + +static int _drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_file_t *priv; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("a dev pid uid magic ioctls\n\n"); + for (priv = dev->file_first; priv; priv = priv->next) { + DRM_PROC_PRINT("%c %3d %5d %5d %10u %10lu\n", + priv->authenticated ? 'y' : 'n', + priv->minor, + priv->pid, + priv->uid, + priv->magic, + priv->ioctl_count); + } + + return len; +} + +static int drm_clients_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_clients_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} + +#if DRM_DEBUG_CODE + +#define DRM_VMA_VERBOSE 0 + +static int _drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_vma_entry_t *pt; + struct vm_area_struct *vma; +#if DRM_VMA_VERBOSE + unsigned long i; + unsigned long address; + pgd_t *pgd; + pmd_t *pmd; + pte_t *pte; +#endif +#if defined(__i386__) + unsigned int pgprot; +#endif + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + DRM_PROC_PRINT("vma use count: %d, high_memory = %p, 0x%08lx\n", + atomic_read(&dev->vma_count), + high_memory, virt_to_phys(high_memory)); + for (pt = dev->vmalist; pt; pt = pt->next) { + if (!(vma = pt->vma)) continue; + DRM_PROC_PRINT("\n%5d 0x%08lx-0x%08lx %c%c%c%c%c%c 0x%08lx", + pt->pid, + vma->vm_start, + vma->vm_end, + vma->vm_flags & VM_READ ? 'r' : '-', + vma->vm_flags & VM_WRITE ? 'w' : '-', + vma->vm_flags & VM_EXEC ? 'x' : '-', + vma->vm_flags & VM_MAYSHARE ? 's' : 'p', + vma->vm_flags & VM_LOCKED ? 'l' : '-', + vma->vm_flags & VM_IO ? 'i' : '-', + VM_OFFSET(vma)); + +#if defined(__i386__) + pgprot = pgprot_val(vma->vm_page_prot); + DRM_PROC_PRINT(" %c%c%c%c%c%c%c%c%c", + pgprot & _PAGE_PRESENT ? 'p' : '-', + pgprot & _PAGE_RW ? 'w' : 'r', + pgprot & _PAGE_USER ? 'u' : 's', + pgprot & _PAGE_PWT ? 't' : 'b', + pgprot & _PAGE_PCD ? 'u' : 'c', + pgprot & _PAGE_ACCESSED ? 'a' : '-', + pgprot & _PAGE_DIRTY ? 'd' : '-', + pgprot & _PAGE_PSE ? 'm' : 'k', + pgprot & _PAGE_GLOBAL ? 'g' : 'l' ); +#endif + DRM_PROC_PRINT("\n"); +#if 0 + for (i = vma->vm_start; i < vma->vm_end; i += PAGE_SIZE) { + pgd = pgd_offset(vma->vm_mm, i); + pmd = pmd_offset(pgd, i); + pte = pte_offset(pmd, i); + if (pte_present(*pte)) { + address = __pa(pte_page(*pte)) + + (i & (PAGE_SIZE-1)); + DRM_PROC_PRINT(" 0x%08lx -> 0x%08lx" + " %c%c%c%c%c\n", + i, + address, + pte_read(*pte) ? 'r' : '-', + pte_write(*pte) ? 'w' : '-', + pte_exec(*pte) ? 'x' : '-', + pte_dirty(*pte) ? 'd' : '-', + pte_young(*pte) ? 'a' : '-' ); + } else { + DRM_PROC_PRINT(" 0x%08lx\n", i); + } + } +#endif + } + + return len; +} + +static int drm_vma_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_vma_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif + + +#if DRM_DMA_HISTOGRAM +static int _drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + drm_device_dma_t *dma = dev->dma; + int i; + unsigned long slot_value = DRM_DMA_HISTOGRAM_INITIAL; + unsigned long prev_value = 0; + drm_buf_t *buffer; + + if (offset > 0) return 0; /* no partial requests */ + len = 0; + *eof = 1; + + DRM_PROC_PRINT("general statistics:\n"); + DRM_PROC_PRINT("total %10u\n", atomic_read(&dev->histo.total)); + DRM_PROC_PRINT("open %10u\n", atomic_read(&dev->total_open)); + DRM_PROC_PRINT("close %10u\n", atomic_read(&dev->total_close)); + DRM_PROC_PRINT("ioctl %10u\n", atomic_read(&dev->total_ioctl)); + DRM_PROC_PRINT("irq %10u\n", atomic_read(&dev->total_irq)); + DRM_PROC_PRINT("ctx %10u\n", atomic_read(&dev->total_ctx)); + + DRM_PROC_PRINT("\nlock statistics:\n"); + DRM_PROC_PRINT("locks %10u\n", atomic_read(&dev->total_locks)); + DRM_PROC_PRINT("unlocks %10u\n", atomic_read(&dev->total_unlocks)); + DRM_PROC_PRINT("contends %10u\n", atomic_read(&dev->total_contends)); + DRM_PROC_PRINT("sleeps %10u\n", atomic_read(&dev->total_sleeps)); + + + if (dma) { + DRM_PROC_PRINT("\ndma statistics:\n"); + DRM_PROC_PRINT("prio %10u\n", + atomic_read(&dma->total_prio)); + DRM_PROC_PRINT("bytes %10u\n", + atomic_read(&dma->total_bytes)); + DRM_PROC_PRINT("dmas %10u\n", + atomic_read(&dma->total_dmas)); + DRM_PROC_PRINT("missed:\n"); + DRM_PROC_PRINT(" dma %10u\n", + atomic_read(&dma->total_missed_dma)); + DRM_PROC_PRINT(" lock %10u\n", + atomic_read(&dma->total_missed_lock)); + DRM_PROC_PRINT(" free %10u\n", + atomic_read(&dma->total_missed_free)); + DRM_PROC_PRINT(" sched %10u\n", + atomic_read(&dma->total_missed_sched)); + DRM_PROC_PRINT("tried %10u\n", + atomic_read(&dma->total_tried)); + DRM_PROC_PRINT("hit %10u\n", + atomic_read(&dma->total_hit)); + DRM_PROC_PRINT("lost %10u\n", + atomic_read(&dma->total_lost)); + + buffer = dma->next_buffer; + if (buffer) { + DRM_PROC_PRINT("next_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("next_buffer none\n"); + } + buffer = dma->this_buffer; + if (buffer) { + DRM_PROC_PRINT("this_buffer %7d\n", buffer->idx); + } else { + DRM_PROC_PRINT("this_buffer none\n"); + } + } + + + DRM_PROC_PRINT("\nvalues:\n"); + if (dev->lock.hw_lock) { + DRM_PROC_PRINT("lock 0x%08x\n", + dev->lock.hw_lock->lock); + } else { + DRM_PROC_PRINT("lock none\n"); + } + DRM_PROC_PRINT("context_flag 0x%08lx\n", dev->context_flag); + DRM_PROC_PRINT("interrupt_flag 0x%08lx\n", dev->interrupt_flag); + DRM_PROC_PRINT("dma_flag 0x%08lx\n", dev->dma_flag); + + DRM_PROC_PRINT("queue_count %10d\n", dev->queue_count); + DRM_PROC_PRINT("last_context %10d\n", dev->last_context); + DRM_PROC_PRINT("last_switch %10lu\n", dev->last_switch); + DRM_PROC_PRINT("last_checked %10d\n", dev->last_checked); + + + DRM_PROC_PRINT("\n q2d d2c c2f" + " q2c q2f dma sch" + " ctx lacq lhld\n\n"); + for (i = 0; i < DRM_DMA_HISTOGRAM_SLOTS; i++) { + DRM_PROC_PRINT("%s %10lu %10u %10u %10u %10u %10u" + " %10u %10u %10u %10u %10u\n", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 ? ">=" : "< ", + i == DRM_DMA_HISTOGRAM_SLOTS - 1 + ? prev_value : slot_value , + + atomic_read(&dev->histo + .queued_to_dispatched[i]), + atomic_read(&dev->histo + .dispatched_to_completed[i]), + atomic_read(&dev->histo + .completed_to_freed[i]), + + atomic_read(&dev->histo + .queued_to_completed[i]), + atomic_read(&dev->histo + .queued_to_freed[i]), + atomic_read(&dev->histo.dma[i]), + atomic_read(&dev->histo.schedule[i]), + atomic_read(&dev->histo.ctx[i]), + atomic_read(&dev->histo.lacq[i]), + atomic_read(&dev->histo.lhld[i])); + prev_value = slot_value; + slot_value = DRM_DMA_HISTOGRAM_NEXT(slot_value); + } + return len; +} + +static int drm_histo_info(char *buf, char **start, off_t offset, int len, + int *eof, void *data) +{ + drm_device_t *dev = (drm_device_t *)data; + int ret; + + down(&dev->struct_sem); + ret = _drm_histo_info(buf, start, offset, len, eof, data); + up(&dev->struct_sem); + return ret; +} +#endif diff --git a/drivers/char/drm/r128_bufs.c b/drivers/char/drm/r128_bufs.c new file mode 100644 index 000000000000..bd81dcdc04c7 --- /dev/null +++ b/drivers/char/drm/r128_bufs.c @@ -0,0 +1,309 @@ +/* r128_bufs.c -- IOCTLs to manage buffers -*- linux-c -*- + * Created: Wed Apr 12 16:19:08 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * Rickard E. (Rik) Faith + * Jeff Hartmann + * + */ + +#define __NO_VERSION__ +#include +#include "drmP.h" +#include "r128_drv.h" +#include "linux/un.h" + + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) +int r128_addbufs_agp(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + drm_buf_desc_t request; + drm_buf_entry_t *entry; + drm_buf_t *buf; + unsigned long offset; + unsigned long agp_offset; + int count; + int order; + int size; + int alignment; + int page_order; + int total; + int byte_count; + int i; + + if (!dma) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + + count = request.count; + order = drm_order(request.size); + size = 1 << order; + + alignment = (request.flags & _DRM_PAGE_ALIGN) ? PAGE_ALIGN(size):size; + page_order = order - PAGE_SHIFT > 0 ? order - PAGE_SHIFT : 0; + total = PAGE_SIZE << page_order; + + byte_count = 0; + agp_offset = dev->agp->base + request.agp_start; + + DRM_DEBUG("count: %d\n", count); + DRM_DEBUG("order: %d\n", order); + DRM_DEBUG("size: %d\n", size); + DRM_DEBUG("agp_offset: %ld\n", agp_offset); + DRM_DEBUG("alignment: %d\n", alignment); + DRM_DEBUG("page_order: %d\n", page_order); + DRM_DEBUG("total: %d\n", total); + + if (order < DRM_MIN_ORDER || order > DRM_MAX_ORDER) return -EINVAL; + if (dev->queue_count) return -EBUSY; /* Not while in use */ + + spin_lock(&dev->count_lock); + if (dev->buf_use) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + atomic_inc(&dev->buf_alloc); + spin_unlock(&dev->count_lock); + + down(&dev->struct_sem); + entry = &dma->bufs[order]; + if (entry->buf_count) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; /* May only call once for each order */ + } + + entry->buflist = drm_alloc(count * sizeof(*entry->buflist), + DRM_MEM_BUFS); + if (!entry->buflist) { + up(&dev->struct_sem); + atomic_dec(&dev->buf_alloc); + return -ENOMEM; + } + memset(entry->buflist, 0, count * sizeof(*entry->buflist)); + + entry->buf_size = size; + entry->page_order = page_order; + offset = 0; + + for (offset = 0; + entry->buf_count < count; + offset += alignment, ++entry->buf_count) { + buf = &entry->buflist[entry->buf_count]; + buf->idx = dma->buf_count + entry->buf_count; + buf->total = alignment; + buf->order = order; + buf->used = 0; + buf->offset = (dma->byte_count + offset); + buf->address = (void *)(agp_offset + offset); + buf->next = NULL; + buf->waiting = 0; + buf->pending = 0; + init_waitqueue_head(&buf->dma_wait); + buf->pid = 0; + + buf->dev_priv_size = sizeof(drm_r128_buf_priv_t); + buf->dev_private = drm_alloc(sizeof(drm_r128_buf_priv_t), + DRM_MEM_BUFS); + memset(buf->dev_private, 0, buf->dev_priv_size); + +#if DRM_DMA_HISTOGRAM + buf->time_queued = 0; + buf->time_dispatched = 0; + buf->time_completed = 0; + buf->time_freed = 0; +#endif + + byte_count += PAGE_SIZE << page_order; + + DRM_DEBUG("buffer %d @ %p\n", + entry->buf_count, buf->address); + } + + DRM_DEBUG("byte_count: %d\n", byte_count); + + dma->buflist = drm_realloc(dma->buflist, + dma->buf_count * sizeof(*dma->buflist), + (dma->buf_count + entry->buf_count) + * sizeof(*dma->buflist), + DRM_MEM_BUFS); + for (i = dma->buf_count; i < dma->buf_count + entry->buf_count; i++) + dma->buflist[i] = &entry->buflist[i - dma->buf_count]; + + dma->buf_count += entry->buf_count; + dma->byte_count += byte_count; + + drm_freelist_create(&entry->freelist, entry->buf_count); + for (i = 0; i < entry->buf_count; i++) { + drm_freelist_put(dev, &entry->freelist, &entry->buflist[i]); + } + + up(&dev->struct_sem); + + request.count = entry->buf_count; + request.size = size; + + copy_to_user_ret((drm_buf_desc_t *)arg, + &request, + sizeof(request), + -EFAULT); + + dma->flags = _DRM_DMA_USE_AGP; + + atomic_dec(&dev->buf_alloc); + return 0; +} +#endif + +int r128_addbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_buf_desc_t request; + + if (!dev_priv || dev_priv->is_pci) return -EINVAL; + + copy_from_user_ret(&request, + (drm_buf_desc_t *)arg, + sizeof(request), + -EFAULT); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (request.flags & _DRM_AGP_BUFFER) + return r128_addbufs_agp(inode, filp, cmd, arg); + else +#endif + return -EINVAL; +} + +int r128_mapbufs(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + const int zero = 0; + unsigned long virtual; + unsigned long address; + drm_buf_map_t request; + int i; + + if (!dma || !dev_priv || dev_priv->is_pci) return -EINVAL; + + DRM_DEBUG("\n"); + + spin_lock(&dev->count_lock); + if (atomic_read(&dev->buf_alloc)) { + spin_unlock(&dev->count_lock); + return -EBUSY; + } + ++dev->buf_use; /* Can't allocate more after this call */ + spin_unlock(&dev->count_lock); + + copy_from_user_ret(&request, + (drm_buf_map_t *)arg, + sizeof(request), + -EFAULT); + + if (request.count >= dma->buf_count) { + if (dma->flags & _DRM_DMA_USE_AGP) { + drm_map_t *map; + + map = dev_priv->agp_vertbufs; + if (!map) { + retcode = -EINVAL; + goto done; + } + + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, map->size, + PROT_READ|PROT_WRITE, + MAP_SHARED, + (unsigned long)map->offset); + up(¤t->mm->mmap_sem); + } else { + down(¤t->mm->mmap_sem); + virtual = do_mmap(filp, 0, dma->byte_count, + PROT_READ|PROT_WRITE, MAP_SHARED, 0); + up(¤t->mm->mmap_sem); + } + if (virtual > -1024UL) { + /* Real error */ + retcode = (signed long)virtual; + goto done; + } + request.virtual = (void *)virtual; + + for (i = 0; i < dma->buf_count; i++) { + if (copy_to_user(&request.list[i].idx, + &dma->buflist[i]->idx, + sizeof(request.list[0].idx))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].total, + &dma->buflist[i]->total, + sizeof(request.list[0].total))) { + retcode = -EFAULT; + goto done; + } + if (copy_to_user(&request.list[i].used, + &zero, + sizeof(zero))) { + retcode = -EFAULT; + goto done; + } + address = virtual + dma->buflist[i]->offset; + if (copy_to_user(&request.list[i].address, + &address, + sizeof(address))) { + retcode = -EFAULT; + goto done; + } + } + } + done: + request.count = dma->buf_count; + DRM_DEBUG("%d buffers, retcode = %d\n", request.count, retcode); + + copy_to_user_ret((drm_buf_map_t *)arg, + &request, + sizeof(request), + -EFAULT); + + return retcode; +} diff --git a/drivers/char/drm/r128_context.c b/drivers/char/drm/r128_context.c new file mode 100644 index 000000000000..2dd716d4b9a8 --- /dev/null +++ b/drivers/char/drm/r128_context.c @@ -0,0 +1,207 @@ +/* r128_context.c -- IOCTLs for r128 contexts -*- linux-c -*- + * Created: Mon Dec 13 09:51:35 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Author: Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "r128_drv.h" + +extern drm_ctx_t r128_res_ctx; + +static int r128_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int r128_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + r128_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int r128_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + + +int r128_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int r128_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = r128_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = r128_alloc_queue(dev); + } + DRM_DEBUG("%d\n", ctx.handle); + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int r128_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + if (ctx.flags==_DRM_CONTEXT_PRESERVED) + r128_res_ctx.handle=ctx.handle; + return 0; +} + +int r128_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int r128_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return r128_context_switch(dev, dev->last_context, ctx.handle); +} + +int r128_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + r128_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int r128_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + drm_ctxbitmap_free(dev, ctx.handle); + + return 0; +} diff --git a/drivers/char/drm/r128_dma.c b/drivers/char/drm/r128_dma.c new file mode 100644 index 000000000000..16f79c1f6840 --- /dev/null +++ b/drivers/char/drm/r128_dma.c @@ -0,0 +1,922 @@ +/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "r128_drv.h" + +#include /* For task queue support */ +#include + + + +#define DO_REMAP(_m) (_m)->handle = drm_ioremap((_m)->offset, (_m)->size) + +#define DO_REMAPFREE(_m) \ + do { \ + if ((_m)->handle && (_m)->size) \ + drm_ioremapfree((_m)->handle, (_m)->size); \ + } while (0) + +#define DO_FIND_MAP(_m, _o) \ + do { \ + int _i; \ + for (_i = 0; _i < dev->map_count; _i++) { \ + if (dev->maplist[_i]->offset == _o) { \ + _m = dev->maplist[_i]; \ + break; \ + } \ + } \ + } while (0) + + +#define R128_MAX_VBUF_AGE 0x10000000 +#define R128_VB_AGE_REG R128_GUI_SCRATCH_REG0 + +int R128_READ_PLL(drm_device_t *dev, int addr) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + + R128_WRITE8(R128_CLOCK_CNTL_INDEX, addr & 0x1f); + return R128_READ(R128_CLOCK_CNTL_DATA); +} + +static void r128_flush_write_combine(void) +{ + int xchangeDummy; + + __asm__ volatile("push %%eax ;" + "xchg %%eax, %0 ;" + "pop %%eax" : : "m" (xchangeDummy)); + __asm__ volatile("push %%eax ;" + "push %%ebx ;" + "push %%ecx ;" + "push %%edx ;" + "movl $0,%%eax ;" + "cpuid ;" + "pop %%edx ;" + "pop %%ecx ;" + "pop %%ebx ;" + "pop %%eax" : /* no outputs */ : /* no inputs */ ); +} + +static void r128_status(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + + printk("GUI_STAT = 0x%08x\n", + (unsigned int)R128_READ(R128_GUI_STAT)); + printk("PM4_STAT = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_STAT)); + printk("PM4_BUFFER_DL_WPTR = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_BUFFER_DL_WPTR)); + printk("PM4_BUFFER_DL_RPTR = 0x%08x\n", + (unsigned int)R128_READ(R128_PM4_BUFFER_DL_RPTR)); +} + +static int r128_do_cleanup_cce(drm_device_t *dev) +{ + if (dev->dev_private) { + drm_r128_private_t *dev_priv = dev->dev_private; + + if (!dev_priv->is_pci) { + DO_REMAPFREE(dev_priv->agp_ring); + DO_REMAPFREE(dev_priv->agp_read_ptr); + DO_REMAPFREE(dev_priv->agp_vertbufs); + DO_REMAPFREE(dev_priv->agp_indbufs); + DO_REMAPFREE(dev_priv->agp_textures); + } + + drm_free(dev->dev_private, sizeof(drm_r128_private_t), + DRM_MEM_DRIVER); + dev->dev_private = NULL; + } + + return 0; +} + +static int r128_do_init_cce(drm_device_t *dev, drm_r128_init_t *init) +{ + drm_r128_private_t *dev_priv; + int i; + + dev_priv = drm_alloc(sizeof(drm_r128_private_t), DRM_MEM_DRIVER); + if (dev_priv == NULL) return -ENOMEM; + dev->dev_private = (void *)dev_priv; + + memset(dev_priv, 0, sizeof(drm_r128_private_t)); + + dev_priv->is_pci = init->is_pci; + + dev_priv->usec_timeout = init->usec_timeout; + if (dev_priv->usec_timeout < 1 || + dev_priv->usec_timeout > R128_MAX_USEC_TIMEOUT) { + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + dev->dev_private = NULL; + return -EINVAL; + } + + dev_priv->cce_mode = init->cce_mode; + dev_priv->cce_fifo_size = init->cce_fifo_size; + dev_priv->cce_is_bm_mode = + ((init->cce_mode == R128_PM4_192BM) || + (init->cce_mode == R128_PM4_128BM_64INDBM) || + (init->cce_mode == R128_PM4_64BM_128INDBM) || + (init->cce_mode == R128_PM4_64BM_64VCBM_64INDBM)); + dev_priv->cce_secure = init->cce_secure; + + if (dev_priv->cce_is_bm_mode && dev_priv->is_pci) { + drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER); + dev->dev_private = NULL; + return -EINVAL; + } + + for (i = 0; i < dev->map_count; i++) { + if (dev->maplist[i]->type == _DRM_SHM) { + dev_priv->sarea = dev->maplist[i]; + break; + } + } + + DO_FIND_MAP(dev_priv->fb, init->fb_offset); + if (!dev_priv->is_pci) { + DO_FIND_MAP(dev_priv->agp_ring, init->agp_ring_offset); + DO_FIND_MAP(dev_priv->agp_read_ptr, init->agp_read_ptr_offset); + DO_FIND_MAP(dev_priv->agp_vertbufs, init->agp_vertbufs_offset); + DO_FIND_MAP(dev_priv->agp_indbufs, init->agp_indbufs_offset); + DO_FIND_MAP(dev_priv->agp_textures, init->agp_textures_offset); + } + DO_FIND_MAP(dev_priv->mmio, init->mmio_offset); + + dev_priv->sarea_priv = + (drm_r128_sarea_t *)((u8 *)dev_priv->sarea->handle + + init->sarea_priv_offset); + + if (!dev_priv->is_pci) { + DO_REMAP(dev_priv->agp_ring); + DO_REMAP(dev_priv->agp_read_ptr); + DO_REMAP(dev_priv->agp_vertbufs); +#if 0 + DO_REMAP(dev_priv->agp_indirectbufs); + DO_REMAP(dev_priv->agp_textures); +#endif + + dev_priv->ring_size = init->ring_size; + dev_priv->ring_sizel2qw = drm_order(init->ring_size/8); + dev_priv->ring_entries = init->ring_size/sizeof(u32); + dev_priv->ring_read_ptr = ((__volatile__ u32 *) + dev_priv->agp_read_ptr->handle); + dev_priv->ring_start = (u32 *)dev_priv->agp_ring->handle; + dev_priv->ring_end = ((u32 *)dev_priv->agp_ring->handle + + dev_priv->ring_entries); + } + + dev_priv->submit_age = 0; + R128_WRITE(R128_VB_AGE_REG, dev_priv->submit_age); + + return 0; +} + +int r128_init_cce(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_init_t init; + + copy_from_user_ret(&init, (drm_r128_init_t *)arg, sizeof(init), + -EFAULT); + + switch (init.func) { + case R128_INIT_CCE: + return r128_do_init_cce(dev, &init); + case R128_CLEANUP_CCE: + return r128_do_cleanup_cce(dev); + } + + return -EINVAL; +} + +static void r128_mark_vertbufs_done(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + int i; + + for (i = 0; i < dma->buf_count; i++) { + drm_buf_t *buf = dma->buflist[i]; + drm_r128_buf_priv_t *buf_priv = buf->dev_private; + buf_priv->age = 0; + } +} + +static int r128_do_pixcache_flush(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 tmp; + int i; + + tmp = R128_READ(R128_PC_NGUI_CTLSTAT) | R128_PC_FLUSH_ALL; + R128_WRITE(R128_PC_NGUI_CTLSTAT, tmp); + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(R128_READ(R128_PC_NGUI_CTLSTAT) & R128_PC_BUSY)) + return 0; + udelay(1); + } + + return -EBUSY; +} + +static int r128_do_wait_for_fifo(drm_device_t *dev, int entries) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int slots = R128_READ(R128_GUI_STAT) & R128_GUI_FIFOCNT_MASK; + if (slots >= entries) return 0; + udelay(1); + } + return -EBUSY; +} + +static int r128_do_wait_for_idle(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i, ret; + + if (!(ret = r128_do_wait_for_fifo(dev, 64))) return ret; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (!(R128_READ(R128_GUI_STAT) & R128_GUI_ACTIVE)) { + (void)r128_do_pixcache_flush(dev); + return 0; + } + udelay(1); + } + return -EBUSY; +} + +int r128_do_engine_reset(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 clock_cntl_index, mclk_cntl, gen_reset_cntl; + + (void)r128_do_pixcache_flush(dev); + + clock_cntl_index = R128_READ(R128_CLOCK_CNTL_INDEX); + mclk_cntl = R128_READ_PLL(dev, R128_MCLK_CNTL); + + R128_WRITE_PLL(R128_MCLK_CNTL, + mclk_cntl | R128_FORCE_GCP | R128_FORCE_PIPE3D_CP); + + gen_reset_cntl = R128_READ(R128_GEN_RESET_CNTL); + + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl | R128_SOFT_RESET_GUI); + (void)R128_READ(R128_GEN_RESET_CNTL); + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl & ~R128_SOFT_RESET_GUI); + (void)R128_READ(R128_GEN_RESET_CNTL); + + R128_WRITE_PLL(R128_MCLK_CNTL, mclk_cntl); + R128_WRITE(R128_CLOCK_CNTL_INDEX, clock_cntl_index); + R128_WRITE(R128_GEN_RESET_CNTL, gen_reset_cntl); + + /* For CCE ring buffer only */ + if (dev_priv->cce_is_bm_mode) { + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, 0); + R128_WRITE(R128_PM4_BUFFER_DL_RPTR, 0); + *dev_priv->ring_read_ptr = 0; + dev_priv->sarea_priv->ring_write = 0; + } + + /* Reset the CCE mode */ + (void)r128_do_wait_for_idle(dev); + R128_WRITE(R128_PM4_BUFFER_CNTL, + dev_priv->cce_mode | dev_priv->ring_sizel2qw); + (void)R128_READ(R128_PM4_BUFFER_ADDR); /* as per the sample code */ + R128_WRITE(R128_PM4_MICRO_CNTL, R128_PM4_MICRO_FREERUN); + + r128_mark_vertbufs_done(dev); + return 0; +} + +int r128_eng_reset(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + dev->lock.pid != current->pid) { + DRM_ERROR("r128_eng_reset called without holding the lock\n"); + return -EINVAL; + } + + return r128_do_engine_reset(dev); +} + +static int r128_do_engine_flush(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 tmp; + + tmp = R128_READ(R128_PM4_BUFFER_DL_WPTR); + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, tmp | R128_PM4_BUFFER_DL_DONE); + + return 0; +} + +int r128_eng_flush(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + dev->lock.pid != current->pid) { + DRM_ERROR("r128_eng_flush called without holding the lock\n"); + return -EINVAL; + } + + return r128_do_engine_flush(dev); +} + +static int r128_do_cce_wait_for_fifo(drm_device_t *dev, int entries) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int slots = R128_READ(R128_PM4_STAT) & R128_PM4_FIFOCNT_MASK; + if (slots >= entries) return 0; + udelay(1); + } + return -EBUSY; +} + +int r128_do_cce_wait_for_idle(drm_device_t *dev) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int i; + + if (dev_priv->cce_is_bm_mode) { + for (i = 0; i < dev_priv->usec_timeout; i++) { + if (*dev_priv->ring_read_ptr == dev_priv->sarea_priv->ring_write) { + int pm4stat = R128_READ(R128_PM4_STAT); + if ((pm4stat & R128_PM4_FIFOCNT_MASK) >= dev_priv->cce_fifo_size && + !(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) { + return r128_do_pixcache_flush(dev); + } + } + udelay(1); + } + return -EBUSY; + } else { + int ret = r128_do_cce_wait_for_fifo(dev, dev_priv->cce_fifo_size); + if (ret < 0) return ret; + + for (i = 0; i < dev_priv->usec_timeout; i++) { + int pm4stat = R128_READ(R128_PM4_STAT); + if (!(pm4stat & (R128_PM4_BUSY | R128_PM4_GUI_ACTIVE))) { + return r128_do_pixcache_flush(dev); + } + udelay(1); + } + return -EBUSY; + } +} + +int r128_cce_idle(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + dev->lock.pid != current->pid) { + DRM_ERROR("r128_wait_idle called without holding the lock\n"); + return -EINVAL; + } + + return r128_do_cce_wait_for_idle(dev); +} + +static int r128_submit_packets_ring_secure(drm_device_t *dev, + u32 *commands, int *count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int write = dev_priv->sarea_priv->ring_write; + int *write_ptr = dev_priv->ring_start + write; + int c = *count; + u32 tmp = 0; + int psize = 0; + int writing = 1; + int timeout; + + while (c > 0) { + tmp = *commands++; + if (!psize) { + writing = 1; + + if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) { + if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) { + if ((tmp & R128_CCE_PACKET0_REG_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2)) { + writing = 0; + } + } + psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + } else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) { + if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) { + if ((tmp & R128_CCE_PACKET1_REG0_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2)) { + writing = 0; + } + } else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <= + (0x1004 << 9)) { + if ((tmp & R128_CCE_PACKET1_REG1_MASK) != + (R128_PM4_VC_FPU_SETUP << 9)) { + writing = 0; + } + } + psize = 3; + } else { + psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + } + } + psize--; + + if (writing) { + write++; + *write_ptr++ = tmp; + } + if (write >= dev_priv->ring_entries) { + write = 0; + write_ptr = dev_priv->ring_start; + } + timeout = 0; + while (write == *dev_priv->ring_read_ptr) { + (void)R128_READ(R128_PM4_BUFFER_DL_RPTR); + if (timeout++ >= dev_priv->usec_timeout) + return -EBUSY; + udelay(1); + } + c--; + } + + if (write < 32) + memcpy(dev_priv->ring_end, + dev_priv->ring_start, + write * sizeof(u32)); + + /* Make sure WC cache has been flushed */ + r128_flush_write_combine(); + + dev_priv->sarea_priv->ring_write = write; + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write); + + *count = 0; + + return 0; +} + +static int r128_submit_packets_pio_secure(drm_device_t *dev, + u32 *commands, int *count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + u32 tmp = 0; + int psize = 0; + int writing = 1; + int addr = R128_PM4_FIFO_DATA_EVEN; + int ret; + + while (*count > 0) { + tmp = *commands++; + if (!psize) { + writing = 1; + + if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET0) { + if ((tmp & R128_CCE_PACKET0_REG_MASK) <= (0x1004 >> 2)) { + if ((tmp & R128_CCE_PACKET0_REG_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2)) { + writing = 0; + } + } + psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + } else if ((tmp & R128_CCE_PACKET_MASK) == R128_CCE_PACKET1) { + if ((tmp & R128_CCE_PACKET1_REG0_MASK) <= (0x1004 >> 2)) { + if ((tmp & R128_CCE_PACKET1_REG0_MASK) != + (R128_PM4_VC_FPU_SETUP >> 2)) { + writing = 0; + } + } else if ((tmp & R128_CCE_PACKET1_REG1_MASK) <= + (0x1004 << 9)) { + if ((tmp & R128_CCE_PACKET1_REG1_MASK) != + (R128_PM4_VC_FPU_SETUP << 9)) { + writing = 0; + } + } + psize = 3; + } else { + psize = ((tmp & R128_CCE_PACKET_COUNT_MASK) >> 16) + 2; + } + } + psize--; + + if (writing) { + if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0) + return ret; + R128_WRITE(addr, tmp); + addr ^= 0x0004; + } + + *count -= 1; + } + + if (addr == R128_PM4_FIFO_DATA_ODD) { + if ((ret = r128_do_cce_wait_for_fifo(dev, 1)) < 0) return ret; + R128_WRITE(addr, R128_CCE_PACKET2); + } + + return 0; +} + +static int r128_submit_packets_ring(drm_device_t *dev, + u32 *commands, int *count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int write = dev_priv->sarea_priv->ring_write; + int *write_ptr = dev_priv->ring_start + write; + int c = *count; + int timeout; + + while (c > 0) { + write++; + *write_ptr++ = *commands++; + if (write >= dev_priv->ring_entries) { + write = 0; + write_ptr = dev_priv->ring_start; + } + timeout = 0; + while (write == *dev_priv->ring_read_ptr) { + (void)R128_READ(R128_PM4_BUFFER_DL_RPTR); + if (timeout++ >= dev_priv->usec_timeout) + return -EBUSY; + udelay(1); + } + c--; + } + + if (write < 32) + memcpy(dev_priv->ring_end, + dev_priv->ring_start, + write * sizeof(u32)); + + /* Make sure WC cache has been flushed */ + r128_flush_write_combine(); + + dev_priv->sarea_priv->ring_write = write; + R128_WRITE(R128_PM4_BUFFER_DL_WPTR, write); + + *count = 0; + + return 0; +} + +static int r128_submit_packets_pio(drm_device_t *dev, + u32 *commands, int *count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int ret; + + while (*count > 1) { + if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret; + R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++); + R128_WRITE(R128_PM4_FIFO_DATA_ODD, *commands++); + *count -= 2; + } + + if (*count) { + if ((ret = r128_do_cce_wait_for_fifo(dev, 2)) < 0) return ret; + R128_WRITE(R128_PM4_FIFO_DATA_EVEN, *commands++); + R128_WRITE(R128_PM4_FIFO_DATA_ODD, R128_CCE_PACKET2); + *count = 0; + } + + return 0; +} + +static int r128_do_submit_packets(drm_device_t *dev, u32 *buffer, int count) +{ + drm_r128_private_t *dev_priv = dev->dev_private; + int c = count; + int ret; + + if (dev_priv->cce_is_bm_mode) { + int left = 0; + + if (c >= dev_priv->ring_entries) { + c = dev_priv->ring_entries-1; + left = count - c; + } + + /* Since this is only used by the kernel we can use the + insecure ring buffer submit packet routine */ + ret = r128_submit_packets_ring(dev, buffer, &c); + + c += left; + } else { + /* Since this is only used by the kernel we can use the + insecure PIO submit packet routine */ + ret = r128_submit_packets_pio(dev, buffer, &c); + } + + if (ret < 0) return ret; + else return c; +} + +int r128_submit_pkt(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_packet_t packet; + u32 *buffer; + int c; + int size; + int ret = 0; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + dev->lock.pid != current->pid) { + DRM_ERROR("r128_submit_pkt called without holding the lock\n"); + return -EINVAL; + } + + copy_from_user_ret(&packet, (drm_r128_packet_t *)arg, sizeof(packet), + -EFAULT); + + c = packet.count; + size = c * sizeof(*buffer); + + if (dev_priv->cce_is_bm_mode) { + int left = 0; + + if (c >= dev_priv->ring_entries) { + c = dev_priv->ring_entries-1; + size = c * sizeof(*buffer); + left = packet.count - c; + } + + if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM; + copy_from_user_ret(buffer, packet.buffer, size, -EFAULT); + + if (dev_priv->cce_secure) + ret = r128_submit_packets_ring_secure(dev, buffer, &c); + else + ret = r128_submit_packets_ring(dev, buffer, &c); + + c += left; + } else { + if ((buffer = kmalloc(size, 0)) == NULL) return -ENOMEM; + copy_from_user_ret(buffer, packet.buffer, size, -EFAULT); + + if (dev_priv->cce_secure) + ret = r128_submit_packets_pio_secure(dev, buffer, &c); + else + ret = r128_submit_packets_pio(dev, buffer, &c); + } + + kfree(buffer); + + packet.count = c; + copy_to_user_ret((drm_r128_packet_t *)arg, &packet, sizeof(packet), + -EFAULT); + + if (ret) return ret; + else if (c > 0) return -EAGAIN; + + return 0; +} + +static int r128_send_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v) +{ + drm_device_dma_t *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv; + drm_buf_t *buf; + int i, ret; + u32 cce[2]; + + /* Make sure we have valid data */ + for (i = 0; i < v->send_count; i++) { + int idx = v->send_indices[i]; + + if (idx < 0 || idx >= dma->buf_count) { + DRM_ERROR("Index %d (of %d max)\n", + idx, dma->buf_count - 1); + return -EINVAL; + } + buf = dma->buflist[idx]; + if (buf->pid != current->pid) { + DRM_ERROR("Process %d using buffer owned by %d\n", + current->pid, buf->pid); + return -EINVAL; + } + if (buf->pending) { + DRM_ERROR("Sending pending buffer:" + " buffer %d, offset %d\n", + v->send_indices[i], i); + return -EINVAL; + } + } + + /* Wait for idle, if we've wrapped to make sure that all pending + buffers have been processed */ + if (dev_priv->submit_age == R128_MAX_VBUF_AGE) { + if ((ret = r128_do_cce_wait_for_idle(dev)) < 0) return ret; + dev_priv->submit_age = 0; + r128_mark_vertbufs_done(dev); + } + + /* Make sure WC cache has been flushed (if in PIO mode) */ + if (!dev_priv->cce_is_bm_mode) r128_flush_write_combine(); + + /* FIXME: Add support for sending vertex buffer to the CCE here + instead of in client code. The v->prim holds the primitive + type that should be drawn. Loop over the list buffers in + send_indices[] and submit a packet for each VB. + + This will require us to loop over the clip rects here as + well, which implies that we extend the kernel driver to allow + cliprects to be stored here. Note that the cliprects could + possibly come from the X server instead of the client, but + this will require additional changes to the DRI to allow for + this optimization. */ + + /* Submit a CCE packet that writes submit_age to R128_VB_AGE_REG */ + cce[0] = R128CCE0(R128_CCE_PACKET0, R128_VB_AGE_REG, 0); + cce[1] = dev_priv->submit_age; + if ((ret = r128_do_submit_packets(dev, cce, 2)) < 0) { + /* Until we add support for sending VBs to the CCE in + this routine, we can recover from this error. After + we add that support, we won't be able to easily + recover, so we will probably have to implement + another mechanism for handling timeouts from packets + submitted directly by the kernel. */ + return ret; + } + + /* Now that the submit packet request has succeeded, we can mark + the buffers as pending */ + for (i = 0; i < v->send_count; i++) { + buf = dma->buflist[v->send_indices[i]]; + buf->pending = 1; + + buf_priv = buf->dev_private; + buf_priv->age = dev_priv->submit_age; + } + + dev_priv->submit_age++; + + return 0; +} + +static drm_buf_t *r128_freelist_get(drm_device_t *dev) +{ + drm_device_dma_t *dma = dev->dma; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_r128_buf_priv_t *buf_priv; + drm_buf_t *buf; + int i, t; + + /* FIXME: Optimize -- use freelist code */ + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if (buf->pid == 0) return buf; + } + + for (t = 0; t < dev_priv->usec_timeout; t++) { + u32 done_age = R128_READ(R128_VB_AGE_REG); + + for (i = 0; i < dma->buf_count; i++) { + buf = dma->buflist[i]; + buf_priv = buf->dev_private; + if (buf->pending && buf_priv->age <= done_age) { + /* The buffer has been processed, so it + can now be used */ + buf->pending = 0; + return buf; + } + } + udelay(1); + } + + r128_status(dev); + return NULL; +} + + +static int r128_get_vertbufs(drm_device_t *dev, drm_r128_vertex_t *v) +{ + drm_buf_t *buf; + int i; + + for (i = v->granted_count; i < v->request_count; i++) { + buf = r128_freelist_get(dev); + if (!buf) break; + buf->pid = current->pid; + copy_to_user_ret(&v->request_indices[i], + &buf->idx, + sizeof(buf->idx), + -EFAULT); + copy_to_user_ret(&v->request_sizes[i], + &buf->total, + sizeof(buf->total), + -EFAULT); + ++v->granted_count; + } + return 0; +} + +int r128_vertex_buf(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_r128_private_t *dev_priv = dev->dev_private; + drm_device_dma_t *dma = dev->dma; + int retcode = 0; + drm_r128_vertex_t v; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock) || + dev->lock.pid != current->pid) { + DRM_ERROR("r128_vertex_buf called without holding the lock\n"); + return -EINVAL; + } + + if (!dev_priv || dev_priv->is_pci) { + DRM_ERROR("r128_vertex_buf called with a PCI card\n"); + return -EINVAL; + } + + copy_from_user_ret(&v, (drm_r128_vertex_t *)arg, sizeof(v), -EFAULT); + DRM_DEBUG("%d: %d send, %d req\n", + current->pid, v.send_count, v.request_count); + + if (v.send_count < 0 || v.send_count > dma->buf_count) { + DRM_ERROR("Process %d trying to send %d buffers (of %d max)\n", + current->pid, v.send_count, dma->buf_count); + return -EINVAL; + } + if (v.request_count < 0 || v.request_count > dma->buf_count) { + DRM_ERROR("Process %d trying to get %d buffers (of %d max)\n", + current->pid, v.request_count, dma->buf_count); + return -EINVAL; + } + + if (v.send_count) { + retcode = r128_send_vertbufs(dev, &v); + } + + v.granted_count = 0; + + if (!retcode && v.request_count) { + retcode = r128_get_vertbufs(dev, &v); + } + + DRM_DEBUG("%d returning, granted = %d\n", + current->pid, v.granted_count); + copy_to_user_ret((drm_r128_vertex_t *)arg, &v, sizeof(v), -EFAULT); + + return retcode; +} diff --git a/drivers/char/drm/r128_drm.h b/drivers/char/drm/r128_drm.h new file mode 100644 index 000000000000..ac6f73bcde0c --- /dev/null +++ b/drivers/char/drm/r128_drm.h @@ -0,0 +1,111 @@ +/* r128_drm.h -- Public header for the r128 driver -*- linux-c -*- + * Created: Wed Apr 5 19:24:19 2000 by kevin@precisioninsight.com + * + * Copyright 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Kevin E. Martin + * + */ + +#ifndef _R128_DRM_H_ +#define _R128_DRM_H_ + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (xf86drmR128.h) + */ +typedef struct drm_r128_init { + enum { + R128_INIT_CCE = 0x01, + R128_CLEANUP_CCE = 0x02 + } func; + int sarea_priv_offset; + int is_pci; + int cce_mode; + int cce_fifo_size; + int cce_secure; + int ring_size; + int usec_timeout; + + int fb_offset; + int agp_ring_offset; + int agp_read_ptr_offset; + int agp_vertbufs_offset; + int agp_indbufs_offset; + int agp_textures_offset; + int mmio_offset; +} drm_r128_init_t; + +typedef struct drm_r128_packet { + unsigned long *buffer; + int count; + int flags; +} drm_r128_packet_t; + +typedef enum drm_r128_prim { + _DRM_R128_PRIM_NONE = 0x0001, + _DRM_R128_PRIM_POINT = 0x0002, + _DRM_R128_PRIM_LINE = 0x0004, + _DRM_R128_PRIM_POLY_LINE = 0x0008, + _DRM_R128_PRIM_TRI_LIST = 0x0010, + _DRM_R128_PRIM_TRI_FAN = 0x0020, + _DRM_R128_PRIM_TRI_STRIP = 0x0040, + _DRM_R128_PRIM_TRI_TYPE2 = 0x0080 +} drm_r128_prim_t; + +typedef struct drm_r128_vertex { + /* Indices here refer to the offset into + buflist in drm_buf_get_t. */ + int send_count; /* Number of buffers to send */ + int *send_indices; /* List of handles to buffers */ + int *send_sizes; /* Lengths of data to send */ + drm_r128_prim_t prim; /* Primitive type */ + int request_count; /* Number of buffers requested */ + int *request_indices; /* Buffer information */ + int *request_sizes; + int granted_count; /* Number of buffers granted */ +} drm_r128_vertex_t; + +/* WARNING: If you change any of these defines, make sure to change the + * defines in the Xserver file (r128_sarea.h) + */ +#define R128_LOCAL_TEX_HEAP 0 +#define R128_AGP_TEX_HEAP 1 +#define R128_NR_TEX_HEAPS 2 +#define R128_NR_TEX_REGIONS 64 +#define R128_LOG_TEX_GRANULARITY 16 + +typedef struct drm_tex_region { + unsigned char next, prev; + unsigned char in_use; + int age; +} drm_tex_region_t; + +typedef struct drm_r128_sarea { + drm_tex_region_t tex_list[R128_NR_TEX_HEAPS][R128_NR_TEX_REGIONS+1]; + int tex_age[R128_NR_TEX_HEAPS]; + int ctx_owner; + int ring_write; +} drm_r128_sarea_t; + +#endif diff --git a/drivers/char/drm/r128_drv.c b/drivers/char/drm/r128_drv.c new file mode 100644 index 000000000000..a431b1a99203 --- /dev/null +++ b/drivers/char/drm/r128_drv.c @@ -0,0 +1,731 @@ +/* r128_drv.c -- ATI Rage 128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:47:27 1999 by faith@precisioninsight.com + * + * Copyright 1999, 2000 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Kevin E. Martin + * + */ + +#include +#include "drmP.h" +#include "r128_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#define R128_NAME "r128" +#define R128_DESC "ATI Rage 128" +#define R128_DATE "20000719" +#define R128_MAJOR 1 +#define R128_MINOR 0 +#define R128_PATCHLEVEL 0 + +static drm_device_t r128_device; +drm_ctx_t r128_res_ctx; + +static struct file_operations r128_fops = { +#if LINUX_VERSION_CODE >= 0x020322 + /* This started being used approx. 2.3.34 */ + owner: THIS_MODULE, +#endif + open: r128_open, + flush: drm_flush, + release: r128_release, + ioctl: r128_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice r128_misc = { + minor: MISC_DYNAMIC_MINOR, + name: R128_NAME, + fops: &r128_fops, +}; + +static drm_ioctl_desc_t r128_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { r128_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_BUFS)] = { r128_addbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MARK_BUFS)] = { drm_markbufs, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_INFO_BUFS)] = { drm_infobufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_MAP_BUFS)] = { r128_mapbufs, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FREE_BUFS)] = { drm_freebufs, 1, 0 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { r128_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { r128_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { r128_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { r128_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { r128_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { r128_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { r128_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { r128_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { r128_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = { drm_agp_acquire, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = { drm_agp_release, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = { drm_agp_enable, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = { drm_agp_info, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = { drm_agp_alloc, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = { drm_agp_free, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = { drm_agp_bind, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = { drm_agp_unbind, 1, 1 }, +#endif + + [DRM_IOCTL_NR(DRM_IOCTL_R128_INIT)] = { r128_init_cce, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_RESET)] = { r128_eng_reset, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_FLUSH)] = { r128_eng_flush, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_PACKET)] = { r128_submit_pkt, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_IDLE)] = { r128_cce_idle, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_R128_VERTEX)] = { r128_vertex_buf, 1, 0 }, +}; +#define R128_IOCTL_COUNT DRM_ARRAY_SIZE(r128_ioctls) + +#ifdef MODULE +static char *r128 = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("r128"); +MODULE_PARM(r128, "s"); + +#ifndef MODULE +/* r128_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init r128_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("r128=", r128_options); +#endif + +static int r128_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + drm_dma_setup(dev); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + r128_res_ctx.handle=-1; + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int r128_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *entry; + drm_agp_mem_t *nexte; + + /* Remove AGP resources, but leave dev->agp + intact until r128_cleanup is called. */ + for (entry = dev->agp->memory; entry; entry = nexte) { + nexte = entry->next; + if (entry->bound) drm_unbind_agp(entry->memory); + drm_free_agp(entry->memory, entry->pages); + drm_free(entry, sizeof(*entry), DRM_MEM_AGPLISTS); + } + dev->agp->memory = NULL; + + if (dev->agp->acquired && drm_agp.release) + (*drm_agp.release)(); + + dev->agp->acquired = 0; + dev->agp->enabled = 0; + } +#endif + + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + drm_dma_takedown(dev); + + dev->queue_count = 0; + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* r128_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int r128_init(void) +{ + int retcode; + drm_device_t *dev = &r128_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(r128); +#endif + + if ((retcode = misc_register(&r128_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", R128_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, r128_misc.minor); + dev->name = R128_NAME; + + drm_mem_init(); + drm_proc_init(dev); + +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + dev->agp = drm_agp_init(); + if (dev->agp == NULL) { + DRM_ERROR("Cannot initialize agpgart module.\n"); + drm_proc_cleanup(); + misc_deregister(&r128_misc); + r128_takedown(dev); + return -ENOMEM; + } + +#ifdef CONFIG_MTRR + dev->agp->agp_mtrr = mtrr_add(dev->agp->agp_info.aper_base, + dev->agp->agp_info.aper_size*1024*1024, + MTRR_TYPE_WRCOMB, + 1); +#endif +#endif + + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&r128_misc); + r128_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + R128_NAME, + R128_MAJOR, + R128_MINOR, + R128_PATCHLEVEL, + R128_DATE, + r128_misc.minor); + + return 0; +} + +/* r128_cleanup is called via cleanup_module at module unload time. */ + +static void r128_cleanup(void) +{ + drm_device_t *dev = &r128_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&r128_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + r128_takedown(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +#endif +} + +module_init(r128_init); +module_exit(r128_cleanup); + + +int r128_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = R128_MAJOR; + version.version_minor = R128_MINOR; + version.version_patchlevel = R128_PATCHLEVEL; + + DRM_COPY(version.name, R128_NAME); + DRM_COPY(version.date, R128_DATE); + DRM_COPY(version.desc, R128_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int r128_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &r128_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return r128_setup(dev); + } + spin_unlock(&dev->count_lock); + } + + return retcode; +} + +int r128_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return r128_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return retcode; +} + +/* r128_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int r128_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= R128_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &r128_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int r128_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + +#if 0 + /* dev->queue_count == 0 right now for + r128. FIXME? */ + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; +#endif + + if (!ret) { +#if 0 + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (lock.context == r128_res_ctx.handle && + j >= 0 && j < DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n", + lock.context, current->pid, j, + dev->lock.lock_time, jiffies); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule_timeout(DRM_LOCK_SLICE-j); + DRM_DEBUG("jiffies=%d\n", jiffies); + } + } +#endif + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; +#if 1 + current->policy |= SCHED_YIELD; +#endif + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + +#if 0 + if (!ret && dev->last_context != lock.context && + lock.context != r128_res_ctx.handle && + dev->last_context != r128_res_ctx.handle) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != lock.context */ + r128_context_switch(dev, dev->last_context, lock.context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == lock.context + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + current->policy |= SCHED_YIELD; + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + ret = -EINTR; + } else if (dev->last_context != lock.context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, lock.context); + } + } +#endif + + if (!ret) { + if (lock.flags & _DRM_LOCK_READY) { + /* Wait for space in DMA/FIFO */ + } + if (lock.flags & _DRM_LOCK_QUIESCENT) { + /* Make hardware quiescent */ +#if 0 + r128_quiescent(dev); +#endif + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != r128_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY/4; + } +#endif + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} + + +int r128_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + /* FIXME: Try to send data to card here */ + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != r128_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY; + } +#endif + + return 0; +} diff --git a/drivers/char/drm/r128_drv.h b/drivers/char/drm/r128_drv.h new file mode 100644 index 000000000000..5b15dddfbe93 --- /dev/null +++ b/drivers/char/drm/r128_drv.h @@ -0,0 +1,224 @@ +/* r128_drv.h -- Private header for r128 driver -*- linux-c -*- + * Created: Mon Dec 13 09:51:11 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: Rickard E. (Rik) Faith + * Kevin E. Martin + * + */ + +#ifndef _R128_DRV_H_ +#define _R128_DRV_H_ + +typedef struct drm_r128_private { + int is_pci; + + int cce_mode; + int cce_fifo_size; + int cce_is_bm_mode; + int cce_secure; + + drm_r128_sarea_t *sarea_priv; + + __volatile__ u32 *ring_read_ptr; + + u32 *ring_start; + u32 *ring_end; + int ring_size; + int ring_sizel2qw; + int ring_entries; + + int submit_age; + + int usec_timeout; + + drm_map_t *sarea; + drm_map_t *fb; + drm_map_t *agp_ring; + drm_map_t *agp_read_ptr; + drm_map_t *agp_vertbufs; + drm_map_t *agp_indbufs; + drm_map_t *agp_textures; + drm_map_t *mmio; +} drm_r128_private_t; + +typedef struct drm_r128_buf_priv { + u32 age; +} drm_r128_buf_priv_t; + + /* r128_drv.c */ +extern int r128_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_open(struct inode *inode, struct file *filp); +extern int r128_release(struct inode *inode, struct file *filp); +extern int r128_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* r128_dma.c */ +extern int r128_init_cce(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_eng_reset(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_eng_flush(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_submit_pkt(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_cce_idle(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_vertex_buf(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* r128_bufs.c */ +extern int r128_addbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_mapbufs(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* r128_context.c */ +extern int r128_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int r128_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int r128_context_switch(drm_device_t *dev, int old, int new); +extern int r128_context_switch_complete(drm_device_t *dev, int new); + + +/* Register definitions, register access macros and drmAddMap constants + * for Rage 128 kernel driver. + */ + +#define R128_PC_NGUI_CTLSTAT 0x0184 +# define R128_PC_FLUSH_ALL 0x00ff +# define R128_PC_BUSY (1 << 31) + +#define R128_CLOCK_CNTL_INDEX 0x0008 +#define R128_CLOCK_CNTL_DATA 0x000c +# define R128_PLL_WR_EN (1 << 7) + +#define R128_MCLK_CNTL 0x000f +# define R128_FORCE_GCP (1 << 16) +# define R128_FORCE_PIPE3D_CP (1 << 17) +# define R128_FORCE_RCP (1 << 18) + +#define R128_GEN_RESET_CNTL 0x00f0 +# define R128_SOFT_RESET_GUI (1 << 0) + +#define R128_PM4_BUFFER_CNTL 0x0704 +# define R128_PM4_NONPM4 (0 << 28) +# define R128_PM4_192PIO (1 << 28) +# define R128_PM4_192BM (2 << 28) +# define R128_PM4_128PIO_64INDBM (3 << 28) +# define R128_PM4_128BM_64INDBM (4 << 28) +# define R128_PM4_64PIO_128INDBM (5 << 28) +# define R128_PM4_64BM_128INDBM (6 << 28) +# define R128_PM4_64PIO_64VCBM_64INDBM (7 << 28) +# define R128_PM4_64BM_64VCBM_64INDBM (8 << 28) +# define R128_PM4_64PIO_64VCPIO_64INDPIO (15 << 28) + + +#define R128_PM4_BUFFER_DL_RPTR 0x0710 +#define R128_PM4_BUFFER_DL_WPTR 0x0714 +# define R128_PM4_BUFFER_DL_DONE (1 << 31) + +#define R128_PM4_VC_FPU_SETUP 0x071c + +#define R128_PM4_STAT 0x07b8 +# define R128_PM4_FIFOCNT_MASK 0x0fff +# define R128_PM4_BUSY (1 << 16) +# define R128_PM4_GUI_ACTIVE (1 << 31) + +#define R128_PM4_BUFFER_ADDR 0x07f0 +#define R128_PM4_MICRO_CNTL 0x07fc +# define R128_PM4_MICRO_FREERUN (1 << 30) + +#define R128_PM4_FIFO_DATA_EVEN 0x1000 +#define R128_PM4_FIFO_DATA_ODD 0x1004 + +#define R128_GUI_SCRATCH_REG0 0x15e0 +#define R128_GUI_SCRATCH_REG1 0x15e4 +#define R128_GUI_SCRATCH_REG2 0x15e8 +#define R128_GUI_SCRATCH_REG3 0x15ec +#define R128_GUI_SCRATCH_REG4 0x15f0 +#define R128_GUI_SCRATCH_REG5 0x15f4 + +#define R128_GUI_STAT 0x1740 +# define R128_GUI_FIFOCNT_MASK 0x0fff +# define R128_GUI_ACTIVE (1 << 31) + + +/* CCE command packets */ +#define R128_CCE_PACKET0 0x00000000 +#define R128_CCE_PACKET1 0x40000000 +#define R128_CCE_PACKET2 0x80000000 +# define R128_CCE_PACKET_MASK 0xC0000000 +# define R128_CCE_PACKET_COUNT_MASK 0x3fff0000 +# define R128_CCE_PACKET0_REG_MASK 0x000007ff +# define R128_CCE_PACKET1_REG0_MASK 0x000007ff +# define R128_CCE_PACKET1_REG1_MASK 0x003ff800 + + +#define R128_MAX_USEC_TIMEOUT 100000 /* 100 ms */ + + +#define R128_BASE(reg) ((u32)(dev_priv->mmio->handle)) +#define R128_ADDR(reg) (R128_BASE(reg) + reg) + +#define R128_DEREF(reg) *(__volatile__ int *)R128_ADDR(reg) +#define R128_READ(reg) R128_DEREF(reg) +#define R128_WRITE(reg,val) do { R128_DEREF(reg) = val; } while (0) + +#define R128_DEREF8(reg) *(__volatile__ char *)R128_ADDR(reg) +#define R128_READ8(reg) R128_DEREF8(reg) +#define R128_WRITE8(reg,val) do { R128_DEREF8(reg) = val; } while (0) + +#define R128_WRITE_PLL(addr,val) \ +do { \ + R128_WRITE8(R128_CLOCK_CNTL_INDEX, ((addr) & 0x1f) | R128_PLL_WR_EN); \ + R128_WRITE(R128_CLOCK_CNTL_DATA, (val)); \ +} while (0) + +extern int R128_READ_PLL(drm_device_t *dev, int addr); + +#define R128CCE0(p,r,n) ((p) | ((n) << 16) | ((r) >> 2)) +#define R128CCE1(p,r1,r2) ((p) | (((r2) >> 2) << 11) | ((r1) >> 2)) +#define R128CCE2(p) ((p)) +#define R128CCE3(p,n) ((p) | ((n) << 16)) + +#endif diff --git a/drivers/char/drm/tdfx_context.c b/drivers/char/drm/tdfx_context.c new file mode 100644 index 000000000000..d6903c0ae9e0 --- /dev/null +++ b/drivers/char/drm/tdfx_context.c @@ -0,0 +1,209 @@ +/* tdfx_context.c -- IOCTLs for tdfx contexts -*- linux-c -*- + * Created: Thu Oct 7 10:50:22 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#define __NO_VERSION__ +#include "drmP.h" +#include "tdfx_drv.h" + +extern drm_ctx_t tdfx_res_ctx; + +static int tdfx_alloc_queue(drm_device_t *dev) +{ + return drm_ctxbitmap_next(dev); +} + +int tdfx_context_switch(drm_device_t *dev, int old, int new) +{ + char buf[64]; + + atomic_inc(&dev->total_ctx); + + if (test_and_set_bit(0, &dev->context_flag)) { + DRM_ERROR("Reentering -- FIXME\n"); + return -EBUSY; + } + +#if DRM_DMA_HISTOGRAM + dev->ctx_start = get_cycles(); +#endif + + DRM_DEBUG("Context switch from %d to %d\n", old, new); + + if (new == dev->last_context) { + clear_bit(0, &dev->context_flag); + return 0; + } + + if (drm_flags & DRM_FLAG_NOCTX) { + tdfx_context_switch_complete(dev, new); + } else { + sprintf(buf, "C %d %d\n", old, new); + drm_write_string(dev, buf); + } + + return 0; +} + +int tdfx_context_switch_complete(drm_device_t *dev, int new) +{ + dev->last_context = new; /* PRE/POST: This is the _only_ writer. */ + dev->last_switch = jiffies; + + if (!_DRM_LOCK_IS_HELD(dev->lock.hw_lock->lock)) { + DRM_ERROR("Lock isn't held after context switch\n"); + } + + /* If a context switch is ever initiated + when the kernel holds the lock, release + that lock here. */ +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.ctx[drm_histogram_slot(get_cycles() + - dev->ctx_start)]); + +#endif + clear_bit(0, &dev->context_flag); + wake_up(&dev->context_wait); + + return 0; +} + + +int tdfx_resctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_res_t res; + drm_ctx_t ctx; + int i; + + DRM_DEBUG("%d\n", DRM_RESERVED_CONTEXTS); + copy_from_user_ret(&res, (drm_ctx_res_t *)arg, sizeof(res), -EFAULT); + if (res.count >= DRM_RESERVED_CONTEXTS) { + memset(&ctx, 0, sizeof(ctx)); + for (i = 0; i < DRM_RESERVED_CONTEXTS; i++) { + ctx.handle = i; + copy_to_user_ret(&res.contexts[i], + &i, + sizeof(i), + -EFAULT); + } + } + res.count = DRM_RESERVED_CONTEXTS; + copy_to_user_ret((drm_ctx_res_t *)arg, &res, sizeof(res), -EFAULT); + return 0; +} + + +int tdfx_addctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + if ((ctx.handle = tdfx_alloc_queue(dev)) == DRM_KERNEL_CONTEXT) { + /* Skip kernel's context and get a new one. */ + ctx.handle = tdfx_alloc_queue(dev); + } + DRM_DEBUG("%d\n", ctx.handle); + if (ctx.handle == -1) { + DRM_DEBUG("Not enough free contexts.\n"); + /* Should this return -EBUSY instead? */ + return -ENOMEM; + } + + copy_to_user_ret((drm_ctx_t *)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int tdfx_modctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + if (ctx.flags==_DRM_CONTEXT_PRESERVED) + tdfx_res_ctx.handle=ctx.handle; + return 0; +} + +int tdfx_getctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t*)arg, sizeof(ctx), -EFAULT); + /* This is 0, because we don't hanlde any context flags */ + ctx.flags = 0; + copy_to_user_ret((drm_ctx_t*)arg, &ctx, sizeof(ctx), -EFAULT); + return 0; +} + +int tdfx_switchctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + return tdfx_context_switch(dev, dev->last_context, ctx.handle); +} + +int tdfx_newctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + tdfx_context_switch_complete(dev, ctx.handle); + + return 0; +} + +int tdfx_rmctx(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_ctx_t ctx; + + copy_from_user_ret(&ctx, (drm_ctx_t *)arg, sizeof(ctx), -EFAULT); + DRM_DEBUG("%d\n", ctx.handle); + drm_ctxbitmap_free(dev, ctx.handle); + + return 0; +} diff --git a/drivers/char/drm/tdfx_drv.c b/drivers/char/drm/tdfx_drv.c new file mode 100644 index 000000000000..bd384fe7ff32 --- /dev/null +++ b/drivers/char/drm/tdfx_drv.c @@ -0,0 +1,690 @@ +/* tdfx.c -- tdfx driver -*- linux-c -*- + * Created: Thu Oct 7 10:38:32 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#include +#include "drmP.h" +#include "tdfx_drv.h" + +#include + +static __attribute__((unused)) void unused(void) +{ + agp_enable(0); +} + +#define TDFX_NAME "tdfx" +#define TDFX_DESC "3dfx Banshee/Voodoo3+" +#define TDFX_DATE "20000719" +#define TDFX_MAJOR 1 +#define TDFX_MINOR 0 +#define TDFX_PATCHLEVEL 0 + +static drm_device_t tdfx_device; +drm_ctx_t tdfx_res_ctx; + +static struct file_operations tdfx_fops = { +#if LINUX_VERSION_CODE >= 0x020322 + /* This started being used approx. 2.3.34 */ + owner: THIS_MODULE, +#endif + open: tdfx_open, + flush: drm_flush, + release: tdfx_release, + ioctl: tdfx_ioctl, + mmap: drm_mmap, + read: drm_read, + fasync: drm_fasync, + poll: drm_poll, +}; + +static struct miscdevice tdfx_misc = { + minor: MISC_DYNAMIC_MINOR, + name: TDFX_NAME, + fops: &tdfx_fops, +}; + +static drm_ioctl_desc_t tdfx_ioctls[] = { + [DRM_IOCTL_NR(DRM_IOCTL_VERSION)] = { tdfx_version, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_UNIQUE)] = { drm_getunique, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_MAGIC)] = { drm_getmagic, 0, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_IRQ_BUSID)] = { drm_irq_busid, 0, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_SET_UNIQUE)] = { drm_setunique, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_BLOCK)] = { drm_block, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNBLOCK)] = { drm_unblock, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_AUTH_MAGIC)] = { drm_authmagic, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_MAP)] = { drm_addmap, 1, 1 }, + + [DRM_IOCTL_NR(DRM_IOCTL_ADD_CTX)] = { tdfx_addctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_CTX)] = { tdfx_rmctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_MOD_CTX)] = { tdfx_modctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_GET_CTX)] = { tdfx_getctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_SWITCH_CTX)] = { tdfx_switchctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_NEW_CTX)] = { tdfx_newctx, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RES_CTX)] = { tdfx_resctx, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_ADD_DRAW)] = { drm_adddraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_RM_DRAW)] = { drm_rmdraw, 1, 1 }, + [DRM_IOCTL_NR(DRM_IOCTL_LOCK)] = { tdfx_lock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_UNLOCK)] = { tdfx_unlock, 1, 0 }, + [DRM_IOCTL_NR(DRM_IOCTL_FINISH)] = { drm_finish, 1, 0 }, +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ACQUIRE)] = {drm_agp_acquire, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_RELEASE)] = {drm_agp_release, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ENABLE)] = {drm_agp_enable, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_INFO)] = {drm_agp_info, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_ALLOC)] = {drm_agp_alloc, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_FREE)] = {drm_agp_free, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_BIND)] = {drm_agp_unbind, 1, 1}, + [DRM_IOCTL_NR(DRM_IOCTL_AGP_UNBIND)] = {drm_agp_bind, 1, 1}, +#endif +}; +#define TDFX_IOCTL_COUNT DRM_ARRAY_SIZE(tdfx_ioctls) + +#ifdef MODULE +static char *tdfx = NULL; +#endif + +MODULE_AUTHOR("VA Linux Systems, Inc."); +MODULE_DESCRIPTION("tdfx"); +MODULE_PARM(tdfx, "s"); + +#ifndef MODULE +/* tdfx_options is called by the kernel to parse command-line options + * passed via the boot-loader (e.g., LILO). It calls the insmod option + * routine, drm_parse_drm. + */ + +static int __init tdfx_options(char *str) +{ + drm_parse_options(str); + return 1; +} + +__setup("tdfx=", tdfx_options); +#endif + +static int tdfx_setup(drm_device_t *dev) +{ + int i; + + atomic_set(&dev->ioctl_count, 0); + atomic_set(&dev->vma_count, 0); + dev->buf_use = 0; + atomic_set(&dev->buf_alloc, 0); + + atomic_set(&dev->total_open, 0); + atomic_set(&dev->total_close, 0); + atomic_set(&dev->total_ioctl, 0); + atomic_set(&dev->total_irq, 0); + atomic_set(&dev->total_ctx, 0); + atomic_set(&dev->total_locks, 0); + atomic_set(&dev->total_unlocks, 0); + atomic_set(&dev->total_contends, 0); + atomic_set(&dev->total_sleeps, 0); + + for (i = 0; i < DRM_HASH_SIZE; i++) { + dev->magiclist[i].head = NULL; + dev->magiclist[i].tail = NULL; + } + dev->maplist = NULL; + dev->map_count = 0; + dev->vmalist = NULL; + dev->lock.hw_lock = NULL; + init_waitqueue_head(&dev->lock.lock_queue); + dev->queue_count = 0; + dev->queue_reserved = 0; + dev->queue_slots = 0; + dev->queuelist = NULL; + dev->irq = 0; + dev->context_flag = 0; + dev->interrupt_flag = 0; + dev->dma = 0; + dev->dma_flag = 0; + dev->last_context = 0; + dev->last_switch = 0; + dev->last_checked = 0; + init_timer(&dev->timer); + init_waitqueue_head(&dev->context_wait); + + dev->ctx_start = 0; + dev->lck_start = 0; + + dev->buf_rp = dev->buf; + dev->buf_wp = dev->buf; + dev->buf_end = dev->buf + DRM_BSZ; + dev->buf_async = NULL; + init_waitqueue_head(&dev->buf_readers); + init_waitqueue_head(&dev->buf_writers); + + tdfx_res_ctx.handle=-1; + + DRM_DEBUG("\n"); + + /* The kernel's context could be created here, but is now created + in drm_dma_enqueue. This is more resource-efficient for + hardware that does not do DMA, but may mean that + drm_select_queue fails between the time the interrupt is + initialized and the time the queues are initialized. */ + + return 0; +} + + +static int tdfx_takedown(drm_device_t *dev) +{ + int i; + drm_magic_entry_t *pt, *next; + drm_map_t *map; + drm_vma_entry_t *vma, *vma_next; + + DRM_DEBUG("\n"); + + down(&dev->struct_sem); + del_timer(&dev->timer); + + if (dev->devname) { + drm_free(dev->devname, strlen(dev->devname)+1, DRM_MEM_DRIVER); + dev->devname = NULL; + } + + if (dev->unique) { + drm_free(dev->unique, strlen(dev->unique)+1, DRM_MEM_DRIVER); + dev->unique = NULL; + dev->unique_len = 0; + } + /* Clear pid list */ + for (i = 0; i < DRM_HASH_SIZE; i++) { + for (pt = dev->magiclist[i].head; pt; pt = next) { + next = pt->next; + drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC); + } + dev->magiclist[i].head = dev->magiclist[i].tail = NULL; + } +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + /* Clear AGP information */ + if (dev->agp) { + drm_agp_mem_t *temp; + drm_agp_mem_t *temp_next; + + temp = dev->agp->memory; + while(temp != NULL) { + temp_next = temp->next; + drm_free_agp(temp->memory, temp->pages); + drm_free(temp, sizeof(*temp), DRM_MEM_AGPLISTS); + temp = temp_next; + } + if (dev->agp->acquired) (*drm_agp.release)(); + } +#endif + /* Clear vma list (only built for debugging) */ + if (dev->vmalist) { + for (vma = dev->vmalist; vma; vma = vma_next) { + vma_next = vma->next; + drm_free(vma, sizeof(*vma), DRM_MEM_VMAS); + } + dev->vmalist = NULL; + } + + /* Clear map area and mtrr information */ + if (dev->maplist) { + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + switch (map->type) { + case _DRM_REGISTERS: + case _DRM_FRAME_BUFFER: +#ifdef CONFIG_MTRR + if (map->mtrr >= 0) { + int retcode; + retcode = mtrr_del(map->mtrr, + map->offset, + map->size); + DRM_DEBUG("mtrr_del = %d\n", retcode); + } +#endif + drm_ioremapfree(map->handle, map->size); + break; + case _DRM_SHM: + drm_free_pages((unsigned long)map->handle, + drm_order(map->size) + - PAGE_SHIFT, + DRM_MEM_SAREA); + break; + case _DRM_AGP: + /* Do nothing here, because this is all + handled in the AGP/GART driver. */ + break; + } + drm_free(map, sizeof(*map), DRM_MEM_MAPS); + } + drm_free(dev->maplist, + dev->map_count * sizeof(*dev->maplist), + DRM_MEM_MAPS); + dev->maplist = NULL; + dev->map_count = 0; + } + + if (dev->lock.hw_lock) { + dev->lock.hw_lock = NULL; /* SHM removed */ + dev->lock.pid = 0; + wake_up_interruptible(&dev->lock.lock_queue); + } + up(&dev->struct_sem); + + return 0; +} + +/* tdfx_init is called via init_module at module load time, or via + * linux/init/main.c (this is not currently supported). */ + +static int tdfx_init(void) +{ + int retcode; + drm_device_t *dev = &tdfx_device; + + DRM_DEBUG("\n"); + + memset((void *)dev, 0, sizeof(*dev)); + dev->count_lock = SPIN_LOCK_UNLOCKED; + sema_init(&dev->struct_sem, 1); + +#ifdef MODULE + drm_parse_options(tdfx); +#endif + + if ((retcode = misc_register(&tdfx_misc))) { + DRM_ERROR("Cannot register \"%s\"\n", TDFX_NAME); + return retcode; + } + dev->device = MKDEV(MISC_MAJOR, tdfx_misc.minor); + dev->name = TDFX_NAME; + + drm_mem_init(); + drm_proc_init(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + dev->agp = drm_agp_init(); +#endif + if((retcode = drm_ctxbitmap_init(dev))) { + DRM_ERROR("Cannot allocate memory for context bitmap.\n"); + drm_proc_cleanup(); + misc_deregister(&tdfx_misc); + tdfx_takedown(dev); + return retcode; + } + + DRM_INFO("Initialized %s %d.%d.%d %s on minor %d\n", + TDFX_NAME, + TDFX_MAJOR, + TDFX_MINOR, + TDFX_PATCHLEVEL, + TDFX_DATE, + tdfx_misc.minor); + + return 0; +} + +/* tdfx_cleanup is called via cleanup_module at module unload time. */ + +static void tdfx_cleanup(void) +{ + drm_device_t *dev = &tdfx_device; + + DRM_DEBUG("\n"); + + drm_proc_cleanup(); + if (misc_deregister(&tdfx_misc)) { + DRM_ERROR("Cannot unload module\n"); + } else { + DRM_INFO("Module unloaded\n"); + } + drm_ctxbitmap_cleanup(dev); + tdfx_takedown(dev); +#if defined(CONFIG_AGP) || defined(CONFIG_AGP_MODULE) + if (dev->agp) { + drm_agp_uninit(); + drm_free(dev->agp, sizeof(*dev->agp), DRM_MEM_AGPLISTS); + dev->agp = NULL; + } +#endif +} + +module_init(tdfx_init); +module_exit(tdfx_cleanup); + + +int tdfx_version(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_version_t version; + int len; + + copy_from_user_ret(&version, + (drm_version_t *)arg, + sizeof(version), + -EFAULT); + +#define DRM_COPY(name,value) \ + len = strlen(value); \ + if (len > name##_len) len = name##_len; \ + name##_len = strlen(value); \ + if (len && name) { \ + copy_to_user_ret(name, value, len, -EFAULT); \ + } + + version.version_major = TDFX_MAJOR; + version.version_minor = TDFX_MINOR; + version.version_patchlevel = TDFX_PATCHLEVEL; + + DRM_COPY(version.name, TDFX_NAME); + DRM_COPY(version.date, TDFX_DATE); + DRM_COPY(version.desc, TDFX_DESC); + + copy_to_user_ret((drm_version_t *)arg, + &version, + sizeof(version), + -EFAULT); + return 0; +} + +int tdfx_open(struct inode *inode, struct file *filp) +{ + drm_device_t *dev = &tdfx_device; + int retcode = 0; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_open_helper(inode, filp, dev))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_open); + spin_lock(&dev->count_lock); + if (!dev->open_count++) { + spin_unlock(&dev->count_lock); + return tdfx_setup(dev); + } + spin_unlock(&dev->count_lock); + } + return retcode; +} + +int tdfx_release(struct inode *inode, struct file *filp) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + int retcode = 0; + + lock_kernel(); + dev = priv->dev; + + DRM_DEBUG("open_count = %d\n", dev->open_count); + if (!(retcode = drm_release(inode, filp))) { +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_inc(&dev->total_close); + spin_lock(&dev->count_lock); + if (!--dev->open_count) { + if (atomic_read(&dev->ioctl_count) || dev->blocked) { + DRM_ERROR("Device busy: %d %d\n", + atomic_read(&dev->ioctl_count), + dev->blocked); + spin_unlock(&dev->count_lock); + unlock_kernel(); + return -EBUSY; + } + spin_unlock(&dev->count_lock); + unlock_kernel(); + return tdfx_takedown(dev); + } + spin_unlock(&dev->count_lock); + } + + unlock_kernel(); + return retcode; +} + +/* tdfx_ioctl is called whenever a process performs an ioctl on /dev/drm. */ + +int tdfx_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + int nr = DRM_IOCTL_NR(cmd); + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + int retcode = 0; + drm_ioctl_desc_t *ioctl; + drm_ioctl_t *func; + + atomic_inc(&dev->ioctl_count); + atomic_inc(&dev->total_ioctl); + ++priv->ioctl_count; + + DRM_DEBUG("pid = %d, cmd = 0x%02x, nr = 0x%02x, dev 0x%x, auth = %d\n", + current->pid, cmd, nr, dev->device, priv->authenticated); + + if (nr >= TDFX_IOCTL_COUNT) { + retcode = -EINVAL; + } else { + ioctl = &tdfx_ioctls[nr]; + func = ioctl->func; + + if (!func) { + DRM_DEBUG("no function\n"); + retcode = -EINVAL; + } else if ((ioctl->root_only && !capable(CAP_SYS_ADMIN)) + || (ioctl->auth_needed && !priv->authenticated)) { + retcode = -EACCES; + } else { + retcode = (func)(inode, filp, cmd, arg); + } + } + + atomic_dec(&dev->ioctl_count); + return retcode; +} + +int tdfx_lock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + DECLARE_WAITQUEUE(entry, current); + int ret = 0; + drm_lock_t lock; +#if DRM_DMA_HISTOGRAM + cycles_t start; + + dev->lck_start = start = get_cycles(); +#endif + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d (pid %d) requests lock (0x%08x), flags = 0x%08x\n", + lock.context, current->pid, dev->lock.hw_lock->lock, + lock.flags); + +#if 0 + /* dev->queue_count == 0 right now for + tdfx. FIXME? */ + if (lock.context < 0 || lock.context >= dev->queue_count) + return -EINVAL; +#endif + + if (!ret) { +#if 0 + if (_DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock) + != lock.context) { + long j = jiffies - dev->lock.lock_time; + + if (lock.context == tdfx_res_ctx.handle && + j >= 0 && j < DRM_LOCK_SLICE) { + /* Can't take lock if we just had it and + there is contention. */ + DRM_DEBUG("%d (pid %d) delayed j=%d dev=%d jiffies=%d\n", + lock.context, current->pid, j, + dev->lock.lock_time, jiffies); + current->state = TASK_INTERRUPTIBLE; + current->policy |= SCHED_YIELD; + schedule_timeout(DRM_LOCK_SLICE-j); + DRM_DEBUG("jiffies=%d\n", jiffies); + } + } +#endif + add_wait_queue(&dev->lock.lock_queue, &entry); + for (;;) { + if (!dev->lock.hw_lock) { + /* Device has been unregistered */ + ret = -EINTR; + break; + } + if (drm_lock_take(&dev->lock.hw_lock->lock, + lock.context)) { + dev->lock.pid = current->pid; + dev->lock.lock_time = jiffies; + atomic_inc(&dev->total_locks); + break; /* Got lock */ + } + + /* Contention */ + atomic_inc(&dev->total_sleeps); + current->state = TASK_INTERRUPTIBLE; +#if 1 + current->policy |= SCHED_YIELD; +#endif + schedule(); + if (signal_pending(current)) { + ret = -ERESTARTSYS; + break; + } + } + current->state = TASK_RUNNING; + remove_wait_queue(&dev->lock.lock_queue, &entry); + } + +#if 0 + if (!ret && dev->last_context != lock.context && + lock.context != tdfx_res_ctx.handle && + dev->last_context != tdfx_res_ctx.handle) { + add_wait_queue(&dev->context_wait, &entry); + current->state = TASK_INTERRUPTIBLE; + /* PRE: dev->last_context != lock.context */ + tdfx_context_switch(dev, dev->last_context, lock.context); + /* POST: we will wait for the context + switch and will dispatch on a later call + when dev->last_context == lock.context + NOTE WE HOLD THE LOCK THROUGHOUT THIS + TIME! */ + current->policy |= SCHED_YIELD; + schedule(); + current->state = TASK_RUNNING; + remove_wait_queue(&dev->context_wait, &entry); + if (signal_pending(current)) { + ret = -EINTR; + } else if (dev->last_context != lock.context) { + DRM_ERROR("Context mismatch: %d %d\n", + dev->last_context, lock.context); + } + } +#endif + + if (!ret) { + if (lock.flags & _DRM_LOCK_READY) { + /* Wait for space in DMA/FIFO */ + } + if (lock.flags & _DRM_LOCK_QUIESCENT) { + /* Make hardware quiescent */ +#if 0 + tdfx_quiescent(dev); +#endif + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != tdfx_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY/4; + } +#endif + DRM_DEBUG("%d %s\n", lock.context, ret ? "interrupted" : "has lock"); + +#if DRM_DMA_HISTOGRAM + atomic_inc(&dev->histo.lacq[drm_histogram_slot(get_cycles() - start)]); +#endif + + return ret; +} + + +int tdfx_unlock(struct inode *inode, struct file *filp, unsigned int cmd, + unsigned long arg) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_lock_t lock; + + copy_from_user_ret(&lock, (drm_lock_t *)arg, sizeof(lock), -EFAULT); + + if (lock.context == DRM_KERNEL_CONTEXT) { + DRM_ERROR("Process %d using kernel context %d\n", + current->pid, lock.context); + return -EINVAL; + } + + DRM_DEBUG("%d frees lock (%d holds)\n", + lock.context, + _DRM_LOCKING_CONTEXT(dev->lock.hw_lock->lock)); + atomic_inc(&dev->total_unlocks); + if (_DRM_LOCK_IS_CONT(dev->lock.hw_lock->lock)) + atomic_inc(&dev->total_contends); + drm_lock_transfer(dev, &dev->lock.hw_lock->lock, DRM_KERNEL_CONTEXT); + /* FIXME: Try to send data to card here */ + if (!dev->context_flag) { + if (drm_lock_free(dev, &dev->lock.hw_lock->lock, + DRM_KERNEL_CONTEXT)) { + DRM_ERROR("\n"); + } + } + +#if LINUX_VERSION_CODE < 0x020400 + if (lock.context != tdfx_res_ctx.handle) { + current->counter = 5; + current->priority = DEF_PRIORITY; + } +#endif + + return 0; +} diff --git a/drivers/char/drm/tdfx_drv.h b/drivers/char/drm/tdfx_drv.h new file mode 100644 index 000000000000..bee840e19228 --- /dev/null +++ b/drivers/char/drm/tdfx_drv.h @@ -0,0 +1,67 @@ +/* tdfx_drv.h -- Private header for tdfx driver -*- linux-c -*- + * Created: Thu Oct 7 10:40:04 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * Daryll Strauss + * + */ + +#ifndef _TDFX_DRV_H_ +#define _TDFX_DRV_H_ + + /* tdfx_drv.c */ +extern int tdfx_version(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_open(struct inode *inode, struct file *filp); +extern int tdfx_release(struct inode *inode, struct file *filp); +extern int tdfx_ioctl(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_lock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_unlock(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + + /* tdfx_context.c */ + +extern int tdfx_resctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_addctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_modctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_getctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_switchctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_newctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); +extern int tdfx_rmctx(struct inode *inode, struct file *filp, + unsigned int cmd, unsigned long arg); + +extern int tdfx_context_switch(drm_device_t *dev, int old, int new); +extern int tdfx_context_switch_complete(drm_device_t *dev, int new); +#endif diff --git a/drivers/char/drm/vm.c b/drivers/char/drm/vm.c new file mode 100644 index 000000000000..5ee9e3242f8e --- /dev/null +++ b/drivers/char/drm/vm.c @@ -0,0 +1,319 @@ +/* vm.c -- Memory mapping for DRM -*- linux-c -*- + * Created: Mon Jan 4 08:58:31 1999 by faith@precisioninsight.com + * + * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas. + * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California. + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice (including the next + * paragraph) shall be included in all copies or substantial portions of the + * Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR + * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, + * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + * DEALINGS IN THE SOFTWARE. + * + * Authors: + * Rickard E. (Rik) Faith + * + */ + +#define __NO_VERSION__ +#include "drmP.h" + +struct vm_operations_struct drm_vm_ops = { + nopage: drm_vm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_shm_ops = { + nopage: drm_vm_shm_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +struct vm_operations_struct drm_vm_dma_ops = { + nopage: drm_vm_dma_nopage, + open: drm_vm_open, + close: drm_vm_close, +}; + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + DRM_DEBUG("0x%08lx, %d\n", address, write_access); + + return NOPAGE_SIGBUS; /* Disallow mremap */ +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_shm_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dev->lock.hw_lock) return NOPAGE_OOM; /* Nothing allocated */ + + offset = address - vma->vm_start; + page = offset >> PAGE_SHIFT; + physical = (unsigned long)dev->lock.hw_lock + offset; + atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return mem_map + MAP_NR(physical); +#endif +} + +#if LINUX_VERSION_CODE < 0x020317 +unsigned long drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#else + /* Return type changed in 2.3.23 */ +struct page *drm_vm_dma_nopage(struct vm_area_struct *vma, + unsigned long address, + int write_access) +#endif +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; + drm_device_dma_t *dma = dev->dma; + unsigned long physical; + unsigned long offset; + unsigned long page; + + if (!dma) return NOPAGE_SIGBUS; /* Error */ + if (address > vma->vm_end) return NOPAGE_SIGBUS; /* Disallow mremap */ + if (!dma->pagelist) return NOPAGE_OOM ; /* Nothing allocated */ + + offset = address - vma->vm_start; /* vm_[pg]off[set] should be 0 */ + page = offset >> PAGE_SHIFT; + physical = dma->pagelist[page] + (offset & (~PAGE_MASK)); + atomic_inc(&mem_map[MAP_NR(physical)].count); /* Dec. by kernel */ + + DRM_DEBUG("0x%08lx (page %lu) => 0x%08lx\n", address, page, physical); +#if LINUX_VERSION_CODE < 0x020317 + return physical; +#else + return mem_map + MAP_NR(physical); +#endif +} + +void drm_vm_open(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *vma_entry; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); + atomic_inc(&dev->vma_count); +#if LINUX_VERSION_CODE < 0x020333 + /* The map can exist after the fd is closed. */ + MOD_INC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + + +#if DRM_DEBUG_CODE + vma_entry = drm_alloc(sizeof(*vma_entry), DRM_MEM_VMAS); + if (vma_entry) { + down(&dev->struct_sem); + vma_entry->vma = vma; + vma_entry->next = dev->vmalist; + vma_entry->pid = current->pid; + dev->vmalist = vma_entry; + up(&dev->struct_sem); + } +#endif +} + +void drm_vm_close(struct vm_area_struct *vma) +{ + drm_file_t *priv = vma->vm_file->private_data; + drm_device_t *dev = priv->dev; +#if DRM_DEBUG_CODE + drm_vma_entry_t *pt, *prev; +#endif + + DRM_DEBUG("0x%08lx,0x%08lx\n", + vma->vm_start, vma->vm_end - vma->vm_start); +#if LINUX_VERSION_CODE < 0x020333 + MOD_DEC_USE_COUNT; /* Needed before Linux 2.3.51 */ +#endif + atomic_dec(&dev->vma_count); + +#if DRM_DEBUG_CODE + down(&dev->struct_sem); + for (pt = dev->vmalist, prev = NULL; pt; prev = pt, pt = pt->next) { + if (pt->vma == vma) { + if (prev) { + prev->next = pt->next; + } else { + dev->vmalist = pt->next; + } + drm_free(pt, sizeof(*pt), DRM_MEM_VMAS); + break; + } + } + up(&dev->struct_sem); +#endif +} + +int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev; + drm_device_dma_t *dma; + unsigned long length = vma->vm_end - vma->vm_start; + + lock_kernel(); + dev = priv->dev; + dma = dev->dma; + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + /* Length must match exact page count */ + if ((length >> PAGE_SHIFT) != dma->page_count) { + unlock_kernel(); + return -EINVAL; + } + unlock_kernel(); + + vma->vm_ops = &drm_vm_dma_ops; + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} + +int drm_mmap(struct file *filp, struct vm_area_struct *vma) +{ + drm_file_t *priv = filp->private_data; + drm_device_t *dev = priv->dev; + drm_map_t *map = NULL; + int i; + + DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n", + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + + if (!VM_OFFSET(vma)) return drm_mmap_dma(filp, vma); + + /* A sequential search of a linked list is + fine here because: 1) there will only be + about 5-10 entries in the list and, 2) a + DRI client only has to do this mapping + once, so it doesn't have to be optimized + for performance, even if the list was a + bit longer. */ + for (i = 0; i < dev->map_count; i++) { + map = dev->maplist[i]; + if (map->offset == VM_OFFSET(vma)) break; + } + + if (i >= dev->map_count) return -EINVAL; + if (!map || ((map->flags&_DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN))) + return -EPERM; + + /* Check for valid size. */ + if (map->size != vma->vm_end - vma->vm_start) return -EINVAL; + + if (!capable(CAP_SYS_ADMIN) && (map->flags & _DRM_READ_ONLY)) { + vma->vm_flags &= VM_MAYWRITE; +#if defined(__i386__) + pgprot_val(vma->vm_page_prot) &= ~_PAGE_RW; +#else + /* Ye gads this is ugly. With more thought + we could move this up higher and use + `protection_map' instead. */ + vma->vm_page_prot = __pgprot(pte_val(pte_wrprotect( + __pte(pgprot_val(vma->vm_page_prot))))); +#endif + } + + switch (map->type) { + case _DRM_FRAME_BUFFER: + case _DRM_REGISTERS: + case _DRM_AGP: + if (VM_OFFSET(vma) >= __pa(high_memory)) { +#if defined(__i386__) + if (boot_cpu_data.x86 > 3 && map->type != _DRM_AGP) { + pgprot_val(vma->vm_page_prot) |= _PAGE_PCD; + pgprot_val(vma->vm_page_prot) &= ~_PAGE_PWT; + } +#endif + vma->vm_flags |= VM_IO; /* not in core dump */ + } + if (remap_page_range(vma->vm_start, + VM_OFFSET(vma), + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + DRM_DEBUG(" Type = %d; start = 0x%lx, end = 0x%lx," + " offset = 0x%lx\n", + map->type, + vma->vm_start, vma->vm_end, VM_OFFSET(vma)); + vma->vm_ops = &drm_vm_ops; + break; + case _DRM_SHM: + vma->vm_ops = &drm_vm_shm_ops; + /* Don't let this area swap. Change when + DRM_KERNEL advisory is supported. */ + vma->vm_flags |= VM_LOCKED; + break; + default: + return -EINVAL; /* This should never happen. */ + } + vma->vm_flags |= VM_LOCKED | VM_SHM; /* Don't swap */ + +#if LINUX_VERSION_CODE < 0x020203 /* KERNEL_VERSION(2,2,3) */ + /* In Linux 2.2.3 and above, this is + handled in do_mmap() in mm/mmap.c. */ + ++filp->f_count; +#endif + vma->vm_file = filp; /* Needed for drm_vm_open() */ + drm_vm_open(vma); + return 0; +} diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c index e5377cea8e8d..1c17a987fea7 100644 --- a/drivers/char/keyboard.c +++ b/drivers/char/keyboard.c @@ -61,7 +61,9 @@ #define KBD_DEFLOCK 0 #endif +void (*kbd_ledfunc)(unsigned int led) = NULL; EXPORT_SYMBOL(handle_scancode); +EXPORT_SYMBOL(kbd_ledfunc); extern void ctrl_alt_del(void); @@ -905,6 +907,7 @@ static void kbd_bh(void) if (leds != ledstate) { ledstate = leds; kbd_leds(leds); + if (kbd_ledfunc) kbd_ledfunc(leds); } } diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 4e6bce1b9f29..2a83f6d68c0a 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -55,14 +55,8 @@ extern void mda_console_init(void); #if defined(CONFIG_PPC) || defined(CONFIG_MAC) extern void adbdev_init(void); #endif -#ifdef CONFIG_USB_UHCI -int uhci_init(void); -#endif -#ifdef CONFIG_USB_OHCI -int ohci_init(void); -#endif -#ifdef CONFIG_USB_OHCI_HCD -int ohci_hcd_init(void); +#ifdef CONFIG_USB +int usb_init(void); #endif static ssize_t do_write_mem(struct file * file, void *p, unsigned long realp, @@ -621,15 +615,7 @@ __initfunc(int chr_dev_init(void)) printk("unable to get major %d for memory devs\n", MEM_MAJOR); rand_initialize(); #ifdef CONFIG_USB -#ifdef CONFIG_USB_UHCI - uhci_init(); -#endif -#ifdef CONFIG_USB_OHCI - ohci_init(); -#endif -#ifdef CONFIG_USB_OHCI_HCD - ohci_hcd_init(); -#endif + usb_init(); #endif #if defined (CONFIG_FB) fbmem_init(); diff --git a/drivers/char/pc_keyb.c b/drivers/char/pc_keyb.c index 1a033c5845fb..16cd4833b425 100644 --- a/drivers/char/pc_keyb.c +++ b/drivers/char/pc_keyb.c @@ -428,6 +428,8 @@ static inline void handle_mouse_event(unsigned char scancode) #endif } +static unsigned char kbd_exists = 1; + /* * This reads the keyboard status port, and does the * appropriate action. @@ -450,6 +452,7 @@ static unsigned char handle_kbd_event(void) if (status & KBD_STAT_MOUSE_OBF) { handle_mouse_event(scancode); } else { + kbd_exists = 1; if (do_acknowledge(scancode)) handle_scancode(scancode, !(scancode & 0x80)); mark_bh(KEYBOARD_BH); @@ -506,7 +509,7 @@ static int send_data(unsigned char data) mdelay(1); if (!--timeout) { #ifdef KBD_REPORT_TIMEOUTS - printk(KERN_WARNING "Keyboard timeout[2]\n"); + printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?\n"); #endif return 0; } @@ -520,8 +523,10 @@ static int send_data(unsigned char data) void pckbd_leds(unsigned char leds) { - if (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds)) - send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + kbd_exists = 0; + } } /* diff --git a/drivers/net/Config.in b/drivers/net/Config.in index f849cc13dc78..3e3703a60e8b 100644 --- a/drivers/net/Config.in +++ b/drivers/net/Config.in @@ -184,7 +184,8 @@ endmenu bool 'FDDI driver support' CONFIG_FDDI if [ "$CONFIG_FDDI" = "y" ]; then - bool 'Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX + bool ' Digital DEFEA and DEFPA adapter support' CONFIG_DEFXX + tristate ' SysKonnect FDDI PCI support' CONFIG_SKFP fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 307608ede011..f9eb80c98947 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -5,7 +5,7 @@ SUB_DIRS := MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc sk98lin lmc +ALL_SUB_DIRS := $(SUB_DIRS) hamradio irda fc sk98lin lmc skfp L_TARGET := net.a L_OBJS := @@ -623,6 +623,16 @@ else endif endif +ifeq ($(CONFIG_SKFP),y) + SUB_DIRS += skfp + MOD_IN_SUB_DIRS += skfp + L_OBJS += skfp/skfp.o +else + ifeq ($(CONFIG_SKFP),m) + MOD_IN_SUB_DIRS += skfp + endif +endif + ifeq ($(CONFIG_WAVELAN),y) L_OBJS += wavelan.o else diff --git a/drivers/net/Space.c b/drivers/net/Space.c index 1dcff8a11ed2..e50fda92afc5 100644 --- a/drivers/net/Space.c +++ b/drivers/net/Space.c @@ -140,6 +140,7 @@ extern int de620_probe(struct device *); /* FDDI adapters */ extern int dfx_probe(struct device *dev); extern int apfddi_init(struct device *dev); +extern int skfp_probe(struct device *dev); /* HIPPI boards */ extern int rr_hippi_probe(struct device *); @@ -583,6 +584,9 @@ __initfunc(static int fddiif_probe(struct device *dev)) #endif #ifdef CONFIG_APFDDI && apfddi_init(dev) +#endif +#ifdef CONFIG_SKFP + && skfp_probe(dev) #endif && 1 ) { return 1; /* -ENODEV or -EAGAIN would be more accurate. */ diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c index 2a19339b1a94..97df972e13dd 100644 --- a/drivers/net/acenic.c +++ b/drivers/net/acenic.c @@ -30,6 +30,8 @@ * Pierrick Pinasseau (CERN): For lending me an Ultra 5 to test the * driver under Linux/Sparc64 * Matt Domsch : Detect 1000baseT cards + * Chip Salzenberg : Fix race condition between tx + * handler and close() cleanup. */ #include @@ -389,7 +391,7 @@ static int tx_ratio[ACE_MAX_MOD_PARMS] = {0, }; static int dis_pci_mem_inval[ACE_MAX_MOD_PARMS] = {1, 1, 1, 1, 1, 1, 1, 1}; static const char __initdata *version = - "acenic.c: v0.44 05/11/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n" + "acenic.c: v0.45 05/31/2000 Jes Sorensen, linux-acenic@SunSITE.auc.dk\n" " http://home.cern.ch/~jes/gige/acenic.html\n"; static struct net_device *root_dev = NULL; @@ -606,7 +608,6 @@ MODULE_PARM(tx_coal_tick, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(max_tx_desc, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(rx_coal_tick, "1-" __MODULE_STRING(8) "i"); MODULE_PARM(max_rx_desc, "1-" __MODULE_STRING(8) "i"); -#endif void __exit ace_module_cleanup(void) @@ -696,6 +697,7 @@ void __exit ace_module_cleanup(void) root_dev = next; } } +#endif int __init ace_module_init(void) @@ -1010,7 +1012,7 @@ static int __init ace_init(struct net_device *dev) pci_read_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, &cache); if ((cache << 2) != SMP_CACHE_BYTES) { printk(KERN_INFO " PCI cache line size set incorrectly " - "(%i bytes) by BIOS/FW, corring to %i\n", + "(%i bytes) by BIOS/FW, correcting to %i\n", (cache << 2), SMP_CACHE_BYTES); pci_write_config_byte(ap->pdev, PCI_CACHE_LINE_SIZE, SMP_CACHE_BYTES >> 2); @@ -1996,6 +1998,19 @@ static void ace_interrupt(int irq, void *dev_id, struct pt_regs *ptregs) struct sk_buff *skb; skb = ap->skb->tx_skbuff[idx].skb; + /* + * Race condition between the code cleaning + * the tx queue in the interrupt handler and the + * interface close, + * + * This is a kludge that really should be fixed + * by preventing the driver from generating a tx + * interrupt when the packet has already been + * removed from the tx queue. + * + * Nailed by Don Dugger and Chip Salzenberg of + * VA Linux. + */ if (skb) { dma_addr_t mapping; diff --git a/drivers/net/sk98lin/skge.c b/drivers/net/sk98lin/skge.c index 6c12fc49aa4e..ed2edd3aecfa 100644 --- a/drivers/net/sk98lin/skge.c +++ b/drivers/net/sk98lin/skge.c @@ -431,6 +431,7 @@ unsigned short tmp; pci_set_master(pdev); base_address = pdev->base_address[0]; + base_address &= PCI_BASE_ADDRESS_MEM_MASK; #ifdef __sparc_v9__ /* SPARC machines do not initialize the chache line size */ diff --git a/drivers/net/skfp/Makefile b/drivers/net/skfp/Makefile new file mode 100644 index 000000000000..82aa26e4cdf5 --- /dev/null +++ b/drivers/net/skfp/Makefile @@ -0,0 +1,39 @@ +# +# Makefile for the SysKonnect FDDI PCI adapter driver +# + +ifeq ($(CONFIG_SKFP),y) + O_TARGET := skfp.o + O_OBJS = skfddi.o hwmtm.o fplustm.o smt.o cfm.o \ + ecm.o pcmplc.o pmf.o queue.o rmt.o \ + smtdef.o smtinit.o smttimer.o srf.o lnkstat.o \ + smtparse.o hwt.o drvfbi.o ess.o +else + ifeq ($(CONFIG_SKFP),m) + MOD_LIST_NAME := SKFP_MODULES + M_OBJS := skfp.o + O_TARGET := skfp.o + O_OBJS = skfddi.o hwmtm.o fplustm.o smt.o cfm.o \ + ecm.o pcmplc.o pmf.o queue.o rmt.o \ + smtdef.o smtinit.o smttimer.o srf.o lnkstat.o \ + smtparse.o hwt.o drvfbi.o ess.o + endif +endif + +# NOTE: +# Compiling this driver produces some warnings (and some more are +# switched off below), but I did not fix this, because the Hardware +# Module source (see skfddi.c for details) is used for different +# drivers, and fixing it for Linux might bring problems on other +# projects. To keep the source common for all those drivers (and +# thus simplify fixes to it), please do not clean it up! + +EXTRA_CFLAGS += -I. -DPCI -DMEM_MAPPED_IO -Wno-strict-prototypes + +include $(TOPDIR)/Rules.make + +clean: + rm -f core *.o *.a *.s + + + diff --git a/drivers/net/skfp/can.c b/drivers/net/skfp/can.c new file mode 100644 index 000000000000..8a49abce7961 --- /dev/null +++ b/drivers/net/skfp/can.c @@ -0,0 +1,83 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef lint +static const char xID_sccs[] = "@(#)can.c 1.5 97/04/07 (C) SK " ; +#endif + +/* + * canonical bit order + */ +const u_char canonical[256] = { + 0x00,0x80,0x40,0xc0,0x20,0xa0,0x60,0xe0, + 0x10,0x90,0x50,0xd0,0x30,0xb0,0x70,0xf0, + 0x08,0x88,0x48,0xc8,0x28,0xa8,0x68,0xe8, + 0x18,0x98,0x58,0xd8,0x38,0xb8,0x78,0xf8, + 0x04,0x84,0x44,0xc4,0x24,0xa4,0x64,0xe4, + 0x14,0x94,0x54,0xd4,0x34,0xb4,0x74,0xf4, + 0x0c,0x8c,0x4c,0xcc,0x2c,0xac,0x6c,0xec, + 0x1c,0x9c,0x5c,0xdc,0x3c,0xbc,0x7c,0xfc, + 0x02,0x82,0x42,0xc2,0x22,0xa2,0x62,0xe2, + 0x12,0x92,0x52,0xd2,0x32,0xb2,0x72,0xf2, + 0x0a,0x8a,0x4a,0xca,0x2a,0xaa,0x6a,0xea, + 0x1a,0x9a,0x5a,0xda,0x3a,0xba,0x7a,0xfa, + 0x06,0x86,0x46,0xc6,0x26,0xa6,0x66,0xe6, + 0x16,0x96,0x56,0xd6,0x36,0xb6,0x76,0xf6, + 0x0e,0x8e,0x4e,0xce,0x2e,0xae,0x6e,0xee, + 0x1e,0x9e,0x5e,0xde,0x3e,0xbe,0x7e,0xfe, + 0x01,0x81,0x41,0xc1,0x21,0xa1,0x61,0xe1, + 0x11,0x91,0x51,0xd1,0x31,0xb1,0x71,0xf1, + 0x09,0x89,0x49,0xc9,0x29,0xa9,0x69,0xe9, + 0x19,0x99,0x59,0xd9,0x39,0xb9,0x79,0xf9, + 0x05,0x85,0x45,0xc5,0x25,0xa5,0x65,0xe5, + 0x15,0x95,0x55,0xd5,0x35,0xb5,0x75,0xf5, + 0x0d,0x8d,0x4d,0xcd,0x2d,0xad,0x6d,0xed, + 0x1d,0x9d,0x5d,0xdd,0x3d,0xbd,0x7d,0xfd, + 0x03,0x83,0x43,0xc3,0x23,0xa3,0x63,0xe3, + 0x13,0x93,0x53,0xd3,0x33,0xb3,0x73,0xf3, + 0x0b,0x8b,0x4b,0xcb,0x2b,0xab,0x6b,0xeb, + 0x1b,0x9b,0x5b,0xdb,0x3b,0xbb,0x7b,0xfb, + 0x07,0x87,0x47,0xc7,0x27,0xa7,0x67,0xe7, + 0x17,0x97,0x57,0xd7,0x37,0xb7,0x77,0xf7, + 0x0f,0x8f,0x4f,0xcf,0x2f,0xaf,0x6f,0xef, + 0x1f,0x9f,0x5f,0xdf,0x3f,0xbf,0x7f,0xff +} ; + +#ifdef MAKE_TABLE +int byte_reverse(x) +int x ; +{ + int y = 0 ; + + if (x & 0x01) + y |= 0x80 ; + if (x & 0x02) + y |= 0x40 ; + if (x & 0x04) + y |= 0x20 ; + if (x & 0x08) + y |= 0x10 ; + if (x & 0x10) + y |= 0x08 ; + if (x & 0x20) + y |= 0x04 ; + if (x & 0x40) + y |= 0x02 ; + if (x & 0x80) + y |= 0x01 ; + return(y) ; +} +#endif diff --git a/drivers/net/skfp/cfm.c b/drivers/net/skfp/cfm.c new file mode 100644 index 000000000000..7a51b765b4a5 --- /dev/null +++ b/drivers/net/skfp/cfm.c @@ -0,0 +1,642 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT CFM + Configuration Management + DAS with single MAC +*/ + +/* + * Hardware independant state machine implemantation + * The following external SMT functions are referenced : + * + * queue_event() + * + * The following external HW dependant functions are referenced : + * config_mux() + * + * The following HW dependant events are required : + * NONE + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)cfm.c 2.18 98/10/06 (C) SK " ; +#endif + +/* + * FSM Macros + */ +#define AFLAG 0x10 +#define GO_STATE(x) (smc->mib.fddiSMTCF_State = (x)|AFLAG) +#define ACTIONS_DONE() (smc->mib.fddiSMTCF_State &= ~AFLAG) +#define ACTIONS(x) (x|AFLAG) + +#ifdef DEBUG +/* + * symbolic state names + */ +static const char * const cfm_states[] = { + "SC0_ISOLATED","CF1","CF2","CF3","CF4", + "SC1_WRAP_A","SC2_WRAP_B","SC5_TRHU_B","SC7_WRAP_S", + "SC9_C_WRAP_A","SC10_C_WRAP_B","SC11_C_WRAP_S","SC4_THRU_A" +} ; + +/* + * symbolic event names + */ +static const char * const cfm_events[] = { + "NONE","CF_LOOP_A","CF_LOOP_B","CF_JOIN_A","CF_JOIN_B" +} ; +#endif + +/* + * map from state to downstream port type + */ +static const u_char cf_to_ptype[] = { + TNONE,TNONE,TNONE,TNONE,TNONE, + TNONE,TB,TB,TS, + TA,TB,TS,TB +} ; + +/* + * CEM port states + */ +#define CEM_PST_DOWN 0 +#define CEM_PST_UP 1 +#define CEM_PST_HOLD 2 +/* define portstate array only for A and B port */ +/* Do this within the smc structure (use in multiple cards) */ + +/* + * all Globals are defined in smc.h + * struct s_cfm + */ + +/* + * function declarations + */ +static void cfm_fsm() ; + +/* + init CFM state machine + clear all CFM vars and flags +*/ +void cfm_init(smc) +struct s_smc *smc ; +{ + smc->mib.fddiSMTCF_State = ACTIONS(SC0_ISOLATED) ; + smc->r.rm_join = 0 ; + smc->r.rm_loop = 0 ; + smc->y[PA].scrub = 0 ; + smc->y[PB].scrub = 0 ; + smc->y[PA].cem_pst = CEM_PST_DOWN ; + smc->y[PB].cem_pst = CEM_PST_DOWN ; +} + +/* Some terms conditions used by the selection criteria */ +#define THRU_ENABLED(smc) (smc->y[PA].pc_mode != PM_TREE && \ + smc->y[PB].pc_mode != PM_TREE) +/* Selection criteria for the ports */ +static void selection_criteria (smc,phy) +struct s_smc *smc ; +struct s_phy *phy ; +{ + + switch (phy->mib->fddiPORTMy_Type) { + case TA: + if ( !THRU_ENABLED(smc) && smc->y[PB].cf_join ) { + phy->wc_flag = TRUE ; + } else { + phy->wc_flag = FALSE ; + } + + break; + case TB: + /* take precedence over PA */ + phy->wc_flag = FALSE ; + break; + case TS: + phy->wc_flag = FALSE ; + break; + case TM: + phy->wc_flag = FALSE ; + break; + } + +} + +void all_selection_criteria (smc) +struct s_smc *smc ; +{ + struct s_phy *phy ; + int p ; + + for ( p = 0,phy = smc->y ; p < NUMPHYS; p++, phy++ ) { + /* Do the selection criteria */ + selection_criteria (smc,phy); + } +} + +static void cem_priv_state (smc, event) +struct s_smc *smc ; +int event ; +/* State machine for private PORT states: used to optimize dual homing */ +{ + int np; /* Number of the port */ + int i; + + /* Do this only in a DAS */ + if (smc->s.sas != SMT_DAS ) + return ; + + np = event - CF_JOIN; + + if (np != PA && np != PB) { + return ; + } + /* Change the port state according to the event (portnumber) */ + if (smc->y[np].cf_join) { + smc->y[np].cem_pst = CEM_PST_UP ; + } else if (!smc->y[np].wc_flag) { + /* set the port to done only if it is not withheld */ + smc->y[np].cem_pst = CEM_PST_DOWN ; + } + + /* Don't set an hold port to down */ + + /* Check all ports of restart conditions */ + for (i = 0 ; i < 2 ; i ++ ) { + /* Check all port for PORT is on hold and no withhold is done */ + if ( smc->y[i].cem_pst == CEM_PST_HOLD && !smc->y[i].wc_flag ) { + smc->y[i].cem_pst = CEM_PST_DOWN; + queue_event(smc,(int)(EVENT_PCM+i),PC_START) ; + } + if ( smc->y[i].cem_pst == CEM_PST_UP && smc->y[i].wc_flag ) { + smc->y[i].cem_pst = CEM_PST_HOLD; + queue_event(smc,(int)(EVENT_PCM+i),PC_START) ; + } + if ( smc->y[i].cem_pst == CEM_PST_DOWN && smc->y[i].wc_flag ) { + /* + * The port must be restarted when the wc_flag + * will be reset. So set the port on hold. + */ + smc->y[i].cem_pst = CEM_PST_HOLD; + } + } + return ; +} + +/* + CFM state machine + called by dispatcher + + do + display state change + process event + until SM is stable +*/ +void cfm(smc,event) +struct s_smc *smc ; +int event ; +{ + int state ; /* remember last state */ + int cond ; + int oldstate ; + + /* We will do the following: */ + /* - compute the variable WC_Flag for every port (This is where */ + /* we can extend the requested path checking !!) */ + /* - do the old (SMT 6.2 like) state machine */ + /* - do the resulting station states */ + + all_selection_criteria (smc); + + /* We will check now whether a state transition is allowed or not */ + /* - change the portstates */ + cem_priv_state (smc, event); + + oldstate = smc->mib.fddiSMTCF_State ; + do { + DB_CFM("CFM : state %s%s", + (smc->mib.fddiSMTCF_State & AFLAG) ? "ACTIONS " : "", + cfm_states[smc->mib.fddiSMTCF_State & ~AFLAG]) ; + DB_CFM(" event %s\n",cfm_events[event],0) ; + state = smc->mib.fddiSMTCF_State ; + cfm_fsm(smc,event) ; + event = 0 ; + } while (state != smc->mib.fddiSMTCF_State) ; + +#ifndef SLIM_SMT + /* + * check peer wrap condition + */ + cond = FALSE ; + if ( (smc->mib.fddiSMTCF_State == SC9_C_WRAP_A && + smc->y[PA].pc_mode == PM_PEER) || + (smc->mib.fddiSMTCF_State == SC10_C_WRAP_B && + smc->y[PB].pc_mode == PM_PEER) || + (smc->mib.fddiSMTCF_State == SC11_C_WRAP_S && + smc->y[PS].pc_mode == PM_PEER && + smc->y[PS].mib->fddiPORTNeighborType != TS ) ) { + cond = TRUE ; + } + if (cond != smc->mib.fddiSMTPeerWrapFlag) + smt_srf_event(smc,SMT_COND_SMT_PEER_WRAP,0,cond) ; + +#if 0 + /* + * Don't send ever MAC_PATH_CHANGE events. Our MAC is hard-wired + * to the primary path. + */ + /* + * path change + */ + if (smc->mib.fddiSMTCF_State != oldstate) { + smt_srf_event(smc,SMT_EVENT_MAC_PATH_CHANGE,INDEX_MAC,0) ; + } +#endif +#endif /* no SLIM_SMT */ + + /* + * set MAC port type + */ + smc->mib.m[MAC0].fddiMACDownstreamPORTType = + cf_to_ptype[smc->mib.fddiSMTCF_State] ; + cfm_state_change(smc,(int)smc->mib.fddiSMTCF_State) ; +} + +/* + process CFM event +*/ +/*ARGSUSED1*/ +static void cfm_fsm(smc,cmd) +struct s_smc *smc ; +int cmd ; +{ + switch(smc->mib.fddiSMTCF_State) { + case ACTIONS(SC0_ISOLATED) : + smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ; + smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ; + smc->mib.p[PA].fddiPORTMACPlacement = 0 ; + smc->mib.p[PB].fddiPORTMACPlacement = 0 ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ; + config_mux(smc,MUX_ISOLATE) ; /* configure PHY Mux */ + smc->r.rm_loop = FALSE ; + smc->r.rm_join = FALSE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + /* Don't do the WC-Flag changing here */ + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break; + case SC0_ISOLATED : + /*SC07*/ + /*SAS port can be PA or PB ! */ + if (smc->s.sas && (smc->y[PA].cf_join || smc->y[PA].cf_loop || + smc->y[PB].cf_join || smc->y[PB].cf_loop)) { + GO_STATE(SC11_C_WRAP_S) ; + break ; + } + /*SC01*/ + if ((smc->y[PA].cem_pst == CEM_PST_UP && smc->y[PA].cf_join && + !smc->y[PA].wc_flag) || smc->y[PA].cf_loop) { + GO_STATE(SC9_C_WRAP_A) ; + break ; + } + /*SC02*/ + if ((smc->y[PB].cem_pst == CEM_PST_UP && smc->y[PB].cf_join && + !smc->y[PB].wc_flag) || smc->y[PB].cf_loop) { + GO_STATE(SC10_C_WRAP_B) ; + break ; + } + break ; + case ACTIONS(SC9_C_WRAP_A) : + smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ; + smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_ISOLATED ; + smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ; + smc->mib.p[PB].fddiPORTMACPlacement = 0 ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ; + config_mux(smc,MUX_WRAPA) ; /* configure PHY mux */ + if (smc->y[PA].cf_loop) { + smc->r.rm_join = FALSE ; + smc->r.rm_loop = TRUE ; + queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */ + } + if (smc->y[PA].cf_join) { + smc->r.rm_loop = FALSE ; + smc->r.rm_join = TRUE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + } + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break ; + case SC9_C_WRAP_A : + /*SC10*/ + if ( (smc->y[PA].wc_flag || !smc->y[PA].cf_join) && + !smc->y[PA].cf_loop ) { + GO_STATE(SC0_ISOLATED) ; + break ; + } + /*SC12*/ + else if ( (smc->y[PB].cf_loop && smc->y[PA].cf_join && + smc->y[PA].cem_pst == CEM_PST_UP) || + ((smc->y[PB].cf_loop || + (smc->y[PB].cf_join && + smc->y[PB].cem_pst == CEM_PST_UP)) && + (smc->y[PA].pc_mode == PM_TREE || + smc->y[PB].pc_mode == PM_TREE))) { + smc->y[PA].scrub = TRUE ; + GO_STATE(SC10_C_WRAP_B) ; + break ; + } + /*SC14*/ + else if (!smc->s.attach_s && + smc->y[PA].cf_join && + smc->y[PA].cem_pst == CEM_PST_UP && + smc->y[PA].pc_mode == PM_PEER && smc->y[PB].cf_join && + smc->y[PB].cem_pst == CEM_PST_UP && + smc->y[PB].pc_mode == PM_PEER) { + smc->y[PA].scrub = TRUE ; + smc->y[PB].scrub = TRUE ; + GO_STATE(SC4_THRU_A) ; + break ; + } + /*SC15*/ + else if ( smc->s.attach_s && + smc->y[PA].cf_join && + smc->y[PA].cem_pst == CEM_PST_UP && + smc->y[PA].pc_mode == PM_PEER && + smc->y[PB].cf_join && + smc->y[PB].cem_pst == CEM_PST_UP && + smc->y[PB].pc_mode == PM_PEER) { + smc->y[PA].scrub = TRUE ; + smc->y[PB].scrub = TRUE ; + GO_STATE(SC5_THRU_B) ; + break ; + } + break ; + case ACTIONS(SC10_C_WRAP_B) : + smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_ISOLATED ; + smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ; + smc->mib.p[PA].fddiPORTMACPlacement = 0 ; + smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ; + config_mux(smc,MUX_WRAPB) ; /* configure PHY mux */ + if (smc->y[PB].cf_loop) { + smc->r.rm_join = FALSE ; + smc->r.rm_loop = TRUE ; + queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */ + } + if (smc->y[PB].cf_join) { + smc->r.rm_loop = FALSE ; + smc->r.rm_join = TRUE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + } + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break ; + case SC10_C_WRAP_B : + /*SC20*/ + if ( !smc->y[PB].cf_join && !smc->y[PB].cf_loop ) { + GO_STATE(SC0_ISOLATED) ; + break ; + } + /*SC21*/ + else if ( smc->y[PA].cf_loop && smc->y[PA].pc_mode == PM_PEER && + smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) { + smc->y[PB].scrub = TRUE ; + GO_STATE(SC9_C_WRAP_A) ; + break ; + } + /*SC24*/ + else if (!smc->s.attach_s && + smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER && + smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) { + smc->y[PA].scrub = TRUE ; + smc->y[PB].scrub = TRUE ; + GO_STATE(SC4_THRU_A) ; + break ; + } + /*SC25*/ + else if ( smc->s.attach_s && + smc->y[PA].cf_join && smc->y[PA].pc_mode == PM_PEER && + smc->y[PB].cf_join && smc->y[PB].pc_mode == PM_PEER) { + smc->y[PA].scrub = TRUE ; + smc->y[PB].scrub = TRUE ; + GO_STATE(SC5_THRU_B) ; + break ; + } + break ; + case ACTIONS(SC4_THRU_A) : + smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ; + smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ; + smc->mib.p[PA].fddiPORTMACPlacement = 0 ; + smc->mib.p[PB].fddiPORTMACPlacement = INDEX_MAC ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ; + config_mux(smc,MUX_THRUA) ; /* configure PHY mux */ + smc->r.rm_loop = FALSE ; + smc->r.rm_join = TRUE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break ; + case SC4_THRU_A : + /*SC41*/ + if (smc->y[PB].wc_flag || !smc->y[PB].cf_join) { + smc->y[PA].scrub = TRUE ; + GO_STATE(SC9_C_WRAP_A) ; + break ; + } + /*SC42*/ + else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) { + smc->y[PB].scrub = TRUE ; + GO_STATE(SC10_C_WRAP_B) ; + break ; + } + /*SC45*/ + else if (smc->s.attach_s) { + smc->y[PB].scrub = TRUE ; + GO_STATE(SC5_THRU_B) ; + break ; + } + break ; + case ACTIONS(SC5_THRU_B) : + smc->mib.p[PA].fddiPORTCurrentPath = MIB_PATH_THRU ; + smc->mib.p[PB].fddiPORTCurrentPath = MIB_PATH_THRU ; + smc->mib.p[PA].fddiPORTMACPlacement = INDEX_MAC ; + smc->mib.p[PB].fddiPORTMACPlacement = 0 ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_THRU ; + config_mux(smc,MUX_THRUB) ; /* configure PHY mux */ + smc->r.rm_loop = FALSE ; + smc->r.rm_join = TRUE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break ; + case SC5_THRU_B : + /*SC51*/ + if (!smc->y[PB].cf_join || smc->y[PB].wc_flag) { + smc->y[PA].scrub = TRUE ; + GO_STATE(SC9_C_WRAP_A) ; + break ; + } + /*SC52*/ + else if (!smc->y[PA].cf_join || smc->y[PA].wc_flag) { + smc->y[PB].scrub = TRUE ; + GO_STATE(SC10_C_WRAP_B) ; + break ; + } + /*SC54*/ + else if (!smc->s.attach_s) { + smc->y[PA].scrub = TRUE ; + GO_STATE(SC4_THRU_A) ; + break ; + } + break ; + case ACTIONS(SC11_C_WRAP_S) : + smc->mib.p[PS].fddiPORTCurrentPath = MIB_PATH_CONCATENATED ; + smc->mib.p[PS].fddiPORTMACPlacement = INDEX_MAC ; + smc->mib.fddiSMTStationStatus = MIB_SMT_STASTA_CON ; + config_mux(smc,MUX_WRAPS) ; /* configure PHY mux */ + if (smc->y[PA].cf_loop || smc->y[PB].cf_loop) { + smc->r.rm_join = FALSE ; + smc->r.rm_loop = TRUE ; + queue_event(smc,EVENT_RMT,RM_LOOP) ;/* signal RMT */ + } + if (smc->y[PA].cf_join || smc->y[PB].cf_join) { + smc->r.rm_loop = FALSE ; + smc->r.rm_join = TRUE ; + queue_event(smc,EVENT_RMT,RM_JOIN) ;/* signal RMT */ + } + ACTIONS_DONE() ; + DB_CFMN(1,"CFM : %s\n",cfm_states[smc->mib.fddiSMTCF_State],0) ; + break ; + case SC11_C_WRAP_S : + /*SC70*/ + if ( !smc->y[PA].cf_join && !smc->y[PA].cf_loop && + !smc->y[PB].cf_join && !smc->y[PB].cf_loop) { + GO_STATE(SC0_ISOLATED) ; + break ; + } + break ; + default: + SMT_PANIC(smc,SMT_E0106, SMT_E0106_MSG) ; + break; + } +} + +/* + * get MAC's input Port + * return : + * PA or PB + */ +int cfm_get_mac_input(smc) +struct s_smc *smc ; +{ + return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B || + smc->mib.fddiSMTCF_State == SC5_THRU_B) ? PB : PA) ; +} + +/* + * get MAC's output Port + * return : + * PA or PB + */ +int cfm_get_mac_output(smc) +struct s_smc *smc ; +{ + return((smc->mib.fddiSMTCF_State == SC10_C_WRAP_B || + smc->mib.fddiSMTCF_State == SC4_THRU_A) ? PB : PA) ; +} + +static char path_iso[] = { + 0,0, 0,RES_PORT, 0,PA + INDEX_PORT, 0,PATH_ISO, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_ISO, + 0,0, 0,RES_PORT, 0,PB + INDEX_PORT, 0,PATH_ISO +} ; + +static char path_wrap_a[] = { + 0,0, 0,RES_PORT, 0,PA + INDEX_PORT, 0,PATH_PRIM, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_PRIM, + 0,0, 0,RES_PORT, 0,PB + INDEX_PORT, 0,PATH_ISO +} ; + +static char path_wrap_b[] = { + 0,0, 0,RES_PORT, 0,PB + INDEX_PORT, 0,PATH_PRIM, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_PRIM, + 0,0, 0,RES_PORT, 0,PA + INDEX_PORT, 0,PATH_ISO +} ; + +static char path_thru[] = { + 0,0, 0,RES_PORT, 0,PA + INDEX_PORT, 0,PATH_PRIM, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_PRIM, + 0,0, 0,RES_PORT, 0,PB + INDEX_PORT, 0,PATH_PRIM +} ; + +static char path_wrap_s[] = { + 0,0, 0,RES_PORT, 0,PS + INDEX_PORT, 0,PATH_PRIM, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_PRIM, +} ; + +static char path_iso_s[] = { + 0,0, 0,RES_PORT, 0,PS + INDEX_PORT, 0,PATH_ISO, + 0,0, 0,RES_MAC, 0,INDEX_MAC, 0,PATH_ISO, +} ; + +int cem_build_path(smc,to,path_index) +struct s_smc *smc ; +char *to ; +int path_index ; +{ + char *path ; + int len ; + + switch (smc->mib.fddiSMTCF_State) { + default : + case SC0_ISOLATED : + path = smc->s.sas ? path_iso_s : path_iso ; + len = smc->s.sas ? sizeof(path_iso_s) : sizeof(path_iso) ; + break ; + case SC9_C_WRAP_A : + path = path_wrap_a ; + len = sizeof(path_wrap_a) ; + break ; + case SC10_C_WRAP_B : + path = path_wrap_b ; + len = sizeof(path_wrap_b) ; + break ; + case SC4_THRU_A : + path = path_thru ; + len = sizeof(path_thru) ; + break ; + case SC11_C_WRAP_S : + path = path_wrap_s ; + len = sizeof(path_wrap_s) ; + break ; + } + memcpy(to,path,len) ; + + LINT_USE(path_index); + + return(len) ; +} diff --git a/drivers/net/skfp/drvfbi.c b/drivers/net/skfp/drvfbi.c new file mode 100644 index 000000000000..93533c5ef31e --- /dev/null +++ b/drivers/net/skfp/drvfbi.c @@ -0,0 +1,1612 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * FBI board dependent Driver for SMT and LLC + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/supern_2.h" +#include "h/skfbiinc.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)drvfbi.c 1.63 99/02/11 (C) SK " ; +#endif + +/* + * PCM active state + */ +#define PC8_ACTIVE 8 + +#define LED_Y_ON 0x11 /* Used for ring up/down indication */ +#define LED_Y_OFF 0x10 + + +#define MS2BCLK(x) ((x)*12500L) + +/* + * valid configuration values are: + */ +#ifdef ISA +const int opt_ints[] = {8, 3, 4, 5, 9, 10, 11, 12, 15} ; +const int opt_iops[] = {8, + 0x100, 0x120, 0x180, 0x1a0, 0x220, 0x240, 0x320, 0x340}; +const int opt_dmas[] = {4, 3, 5, 6, 7} ; +const int opt_eproms[] = {15, 0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, + 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc} ; +#endif +#ifdef EISA +const int opt_ints[] = {5, 9, 10, 11} ; +const int opt_dmas[] = {0, 5, 6, 7} ; +const int opt_eproms[] = {0xc0, 0xc2, 0xc4, 0xc6, 0xc8, 0xca, 0xcc, 0xce, + 0xd0, 0xd2, 0xd4, 0xd6, 0xd8, 0xda, 0xdc} ; +#endif + +#ifdef MCA +int opt_ints[] = {3, 11, 10, 9} ; /* FM1 */ +int opt_eproms[] = {0, 0xc4, 0xc8, 0xcc, 0xd0, 0xd4, 0xd8, 0xdc} ; +#endif /* MCA */ + +/* + * xPOS_ID:xxxx + * | \ / + * | \/ + * | --------------------- the patched POS_ID of the Adapter + * | xxxx = (Vendor ID low byte, + * | Vendor ID high byte, + * | Device ID low byte, + * | Device ID high byte) + * +------------------------------ the patched oem_id must be + * 'S' for SK or 'I' for IBM + * this is a short id for the driver. + */ +#ifndef MULT_OEM +#ifndef OEM_CONCEPT +#ifndef MCA +const u_char oem_id[] = "xPOS_ID:xxxx" ; +#else +const u_char oem_id[] = "xPOSID1:xxxx" ; /* FM1 card id. */ +#endif +#else /* OEM_CONCEPT */ +#ifndef MCA +const u_char oem_id[] = OEM_ID ; +#else +const u_char oem_id[] = OEM_ID1 ; /* FM1 card id. */ +#endif /* MCA */ +#endif /* OEM_CONCEPT */ +#define ID_BYTE0 8 +#define OEMID(smc,i) oem_id[ID_BYTE0 + i] +#else /* MULT_OEM */ +const struct s_oem_ids oem_ids[] = { +#include "oemids.h" +{0} +}; +#define OEMID(smc,i) smc->hw.oem_id->oi_id[i] +#endif /* MULT_OEM */ + +/* Prototypes of external functions */ +extern void hwt_restart() ; +#ifdef AIX +extern int AIX_vpdReadByte() ; +#endif + + +/* Prototypes of local functions. */ +void smt_stop_watchdog() ; + +#ifdef MCA +static int read_card_id() ; +static void DisableSlotAccess() ; +static void EnableSlotAccess() ; +#ifdef AIX +extern int attach_POS_addr() ; +extern int detach_POS_addr() ; +extern u_char read_POS() ; +extern void write_POS() ; +extern int AIX_vpdReadByte() ; +#else +#define read_POS(smc,a1,a2) ((u_char) inp(a1)) +#define write_POS(smc,a1,a2,a3) outp((a1),(a3)) +#endif +#endif /* MCA */ + + +/* + * FDDI card reset + */ +static void card_start(smc) +struct s_smc *smc ; +{ + int i ; +#ifdef PCI + u_char rev_id ; + u_short word; +#endif + + smt_stop_watchdog(smc) ; + +#ifdef ISA + outpw(CSR_A,0) ; /* reset for all chips */ + for (i = 10 ; i ; i--) /* delay for PLC's */ + (void)inpw(ISR_A) ; + OUT_82c54_TIMER(3,COUNT(2) | RW_OP(3) | TMODE(2)) ; + /* counter 2, mode 2 */ + OUT_82c54_TIMER(2,97) ; /* LSB */ + OUT_82c54_TIMER(2,0) ; /* MSB ( 15.6 us ) */ + outpw(CSR_A,CS_CRESET) ; +#endif +#ifdef EISA + outpw(CSR_A,0) ; /* reset for all chips */ + for (i = 10 ; i ; i--) /* delay for PLC's */ + (void)inpw(ISR_A) ; + outpw(CSR_A,CS_CRESET) ; + smc->hw.led = (2<<6) ; + outpw(CSR_A,CS_CRESET | smc->hw.led) ; +#endif +#ifdef MCA + outp(ADDR(CARD_DIS),0) ; /* reset for all chips */ + for (i = 10 ; i ; i--) /* delay for PLC's */ + (void)inpw(ISR_A) ; + outp(ADDR(CARD_EN),0) ; + /* first I/O after reset must not be a access to FORMAC or PLC */ + + /* + * bus timeout (MCA) + */ + OUT_82c54_TIMER(3,COUNT(2) | RW_OP(3) | TMODE(3)) ; + /* counter 2, mode 3 */ + OUT_82c54_TIMER(2,(2*24)) ; /* 3.9 us * 2 square wave */ + OUT_82c54_TIMER(2,0) ; /* MSB */ + + /* POS 102 indicated an activ Check Line or Buss Error monitoring */ + if (inpw(CSA_A) & (POS_EN_CHKINT | POS_EN_BUS_ERR)) { + outp(ADDR(IRQ_CHCK_EN),0) ; + } + + if (!((i = inpw(CSR_A)) & CS_SAS)) { + if (!(i & CS_BYSTAT)) { + outp(ADDR(BYPASS(STAT_INS)),0) ;/* insert station */ + } + } + outpw(LEDR_A,LED_1) ; /* yellow */ +#endif /* MCA */ +#ifdef PCI + /* + * make sure no transfer activity is pending + */ + outpw(FM_A(FM_MDREG1),FM_MINIT) ; + outp(ADDR(B0_CTRL), CTRL_HPI_SET) ; + hwt_wait_time(smc,hwt_quick_read(smc),MS2BCLK(10)) ; + /* + * now reset everything + */ + outp(ADDR(B0_CTRL),CTRL_RST_SET) ; /* reset for all chips */ + i = (int) inp(ADDR(B0_CTRL)) ; /* do dummy read */ + SK_UNUSED(i) ; /* Make LINT happy. */ + outp(ADDR(B0_CTRL), CTRL_RST_CLR) ; + + /* + * Reset all bits in the PCI STATUS register + */ + outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_ON) ; /* enable for writes */ + word = inpw(PCI_C(PCI_STATUS)) ; + outpw(PCI_C(PCI_STATUS), word | PCI_ERRBITS) ; + outp(ADDR(B0_TST_CTRL), TST_CFG_WRITE_OFF) ; /* disable writes */ + + /* + * Release the reset of all the State machines + * Release Master_Reset + * Release HPI_SM_Reset + */ + outp(ADDR(B0_CTRL), CTRL_MRST_CLR|CTRL_HPI_CLR) ; + + /* + * determine the adapter type + * Note: Do it here, because some drivers may call card_start() once + * at very first before any other initialization functions is + * executed. + */ + rev_id = inp(PCI_C(PCI_REV_ID)) ; + if ((rev_id & 0xf0) == SK_ML_ID_1 || (rev_id & 0xf0) == SK_ML_ID_2) { + smc->hw.hw_is_64bit = TRUE ; + } else { + smc->hw.hw_is_64bit = FALSE ; + } + + /* + * Watermark initialization + */ + if (!smc->hw.hw_is_64bit) { + outpd(ADDR(B4_R1_F), RX_WATERMARK) ; + outpd(ADDR(B5_XA_F), TX_WATERMARK) ; + outpd(ADDR(B5_XS_F), TX_WATERMARK) ; + } + + outp(ADDR(B0_CTRL),CTRL_RST_CLR) ; /* clear the reset chips */ + outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_ON|LED_GB_OFF) ; /* ye LED on */ + + /* init the timer value for the watch dog 2,5 minutes */ + outpd(ADDR(B2_WDOG_INI),0x6FC23AC0) ; + + /* initialize the ISR mask */ + smc->hw.is_imask = ISR_MASK ; + smc->hw.hw_state = STOPPED ; +#endif + GET_PAGE(0) ; /* necessary for BOOT */ +} + +void card_stop(smc) +struct s_smc *smc ; +{ + smt_stop_watchdog(smc) ; + smc->hw.mac_ring_is_up = 0 ; /* ring down */ +#ifdef ISA + outpw(CSR_A,0) ; /* reset for all chips */ +#endif +#ifdef EISA + outpw(CSR_A,0) ; /* reset for all chips */ +#endif +#ifdef MCA + outp(ADDR(CARD_DIS),0) ; /* reset for all chips */ +#endif +#ifdef PCI + /* + * make sure no transfer activity is pending + */ + outpw(FM_A(FM_MDREG1),FM_MINIT) ; + outp(ADDR(B0_CTRL), CTRL_HPI_SET) ; + hwt_wait_time(smc,hwt_quick_read(smc),MS2BCLK(10)) ; + /* + * now reset everything + */ + outp(ADDR(B0_CTRL),CTRL_RST_SET) ; /* reset for all chips */ + outp(ADDR(B0_CTRL),CTRL_RST_CLR) ; /* reset for all chips */ + outp(ADDR(B0_LED),LED_GA_OFF|LED_MY_OFF|LED_GB_OFF) ; /* all LEDs off */ + smc->hw.hw_state = STOPPED ; +#endif +} +/*--------------------------- ISR handling ----------------------------------*/ + +#ifndef PCI +void mac1_irq(smc,stu, stl) +struct s_smc *smc ; +u_short stu; +u_short stl; +{ + int restart_tx = 0 ; +again: +#ifndef ISA +/* + * FORMAC+ bug modified the queue pointer if many read/write accesses happens!? + */ + if (stl & (FM_SPCEPDS | /* parit/coding err. syn.q.*/ + FM_SPCEPDA0 | /* parit/coding err. a.q.0 */ + FM_SPCEPDA1 | /* parit/coding err. a.q.1 */ + FM_SPCEPDA2)) { /* parit/coding err. a.q.2 */ + SMT_PANIC(smc,SMT_E0132, SMT_E0132_MSG) ; + } + if (stl & (FM_STBURS | /* tx buffer underrun syn.q.*/ + FM_STBURA0 | /* tx buffer underrun a.q.0 */ + FM_STBURA1 | /* tx buffer underrun a.q.1 */ + FM_STBURA2)) { /* tx buffer underrun a.q.2 */ + SMT_PANIC(smc,SMT_E0133, SMT_E0133_MSG) ; + } +#endif + if ( (stu & (FM_SXMTABT | /* transmit abort */ +#ifdef SYNC + FM_STXABRS | /* syn. tx abort */ +#endif /* SYNC */ + FM_STXABRA0)) || /* asyn. tx abort */ + (stl & (FM_SQLCKS | /* lock for syn. q. */ + FM_SQLCKA0)) ) { /* lock for asyn. q. */ + formac_tx_restart(smc) ; /* init tx */ + restart_tx = 1 ; + stu = inpw(FM_A(FM_ST1U)) ; + stl = inpw(FM_A(FM_ST1L)) ; + stu &= ~ (FM_STECFRMA0 | FM_STEFRMA0 | FM_STEFRMS) ; + if (stu || stl) + goto again ; + } + +#ifndef SYNC + if (stu & (FM_STECFRMA0 | /* end of chain asyn tx */ + FM_STEFRMA0)) { /* end of frame asyn tx */ + /* free tx_queue */ + smc->hw.n_a_send = 0 ; + if (++smc->hw.fp.tx_free < smc->hw.fp.tx_max) { + start_next_send(smc); + } + restart_tx = 1 ; + } +#else /* SYNC */ + if (stu & (FM_STEFRMA0 | /* end of asyn tx */ + FM_STEFRMS)) { /* end of sync tx */ + restart_tx = 1 ; + } +#endif /* SYNC */ + if (restart_tx) + llc_restart_tx(smc) ; +} +#else /* PCI */ + +void mac1_irq(smc,stu, stl) +struct s_smc *smc ; +u_short stu; +u_short stl; +{ + int restart_tx = 0 ; +again: + + /* + * parity error: note encoding error is not possible in tag mode + */ + if (stl & (FM_SPCEPDS | /* parity err. syn.q.*/ + FM_SPCEPDA0 | /* parity err. a.q.0 */ + FM_SPCEPDA1)) { /* parity err. a.q.1 */ + SMT_PANIC(smc,SMT_E0134, SMT_E0134_MSG) ; + } + /* + * buffer underrun: can only occur if a tx threshold is specified + */ + if (stl & (FM_STBURS | /* tx buffer underrun syn.q.*/ + FM_STBURA0 | /* tx buffer underrun a.q.0 */ + FM_STBURA1)) { /* tx buffer underrun a.q.2 */ + SMT_PANIC(smc,SMT_E0133, SMT_E0133_MSG) ; + } + + if ( (stu & (FM_SXMTABT | /* transmit abort */ + FM_STXABRS | /* syn. tx abort */ + FM_STXABRA0)) || /* asyn. tx abort */ + (stl & (FM_SQLCKS | /* lock for syn. q. */ + FM_SQLCKA0)) ) { /* lock for asyn. q. */ + formac_tx_restart(smc) ; /* init tx */ + restart_tx = 1 ; + stu = inpw(FM_A(FM_ST1U)) ; + stl = inpw(FM_A(FM_ST1L)) ; + stu &= ~ (FM_STECFRMA0 | FM_STEFRMA0 | FM_STEFRMS) ; + if (stu || stl) + goto again ; + } + + if (stu & (FM_STEFRMA0 | /* end of asyn tx */ + FM_STEFRMS)) { /* end of sync tx */ + restart_tx = 1 ; + } + + if (restart_tx) + llc_restart_tx(smc) ; +} +#endif /* PCI */ +/* + * interrupt source= plc1 + * this function is called in nwfbisr.asm + */ +void plc1_irq(smc) +struct s_smc *smc ; +{ + u_short st = inpw(PLC(PB,PL_INTR_EVENT)) ; + +#if (defined(ISA) || defined(EISA)) + /* reset PLC Int. bits */ + outpw(PLC1_I,inpw(PLC1_I)) ; +#endif + plc_irq(smc,PB,st) ; +} + +/* + * interrupt source= plc2 + * this function is called in nwfbisr.asm + */ +void plc2_irq(smc) +struct s_smc *smc ; +{ + u_short st = inpw(PLC(PA,PL_INTR_EVENT)) ; + +#if (defined(ISA) || defined(EISA)) + /* reset PLC Int. bits */ + outpw(PLC2_I,inpw(PLC2_I)) ; +#endif + plc_irq(smc,PA,st) ; +} + + +/* + * interrupt source= timer + */ +void timer_irq(smc) +struct s_smc *smc ; +{ + hwt_restart(smc); + smc->hw.t_stop = smc->hw.t_start; + smt_timer_done(smc) ; +} + +/* + * return S-port (PA or PB) + */ +int pcm_get_s_port(smc) +struct s_smc *smc ; +{ + SK_UNUSED(smc) ; + return(PS) ; +} + +/* + * Station Label = "FDDI-XYZ" where + * + * X = connector type + * Y = PMD type + * Z = port type + */ +#define STATION_LABEL_CONNECTOR_OFFSET 5 +#define STATION_LABEL_PMD_OFFSET 6 +#define STATION_LABEL_PORT_OFFSET 7 + +void read_address(smc,mac_addr) +struct s_smc *smc ; +u_char *mac_addr ; +{ + char ConnectorType ; + char PmdType ; + int i ; + + extern const u_char canonical[256] ; + +#if (defined(ISA) || defined(MCA)) + for (i = 0; i < 4 ;i++) { /* read mac address from board */ + smc->hw.fddi_phys_addr.a[i] = + canonical[(inpw(PR_A(i+SA_MAC))&0xff)] ; + } + for (i = 4; i < 6; i++) { + smc->hw.fddi_phys_addr.a[i] = + canonical[(inpw(PR_A(i+SA_MAC+PRA_OFF))&0xff)] ; + } +#endif +#ifdef EISA + /* + * Note: We get trouble on an Alpha machine if we make a inpw() + * instead of inp() + */ + for (i = 0; i < 4 ;i++) { /* read mac address from board */ + smc->hw.fddi_phys_addr.a[i] = + canonical[inp(PR_A(i+SA_MAC))] ; + } + for (i = 4; i < 6; i++) { + smc->hw.fddi_phys_addr.a[i] = + canonical[inp(PR_A(i+SA_MAC+PRA_OFF))] ; + } +#endif +#ifdef PCI + for (i = 0; i < 6; i++) { /* read mac address from board */ + smc->hw.fddi_phys_addr.a[i] = + canonical[inp(ADDR(B2_MAC_0+i))] ; + } +#endif +#ifndef PCI + ConnectorType = inpw(PR_A(SA_PMD_TYPE)) & 0xff ; + PmdType = inpw(PR_A(SA_PMD_TYPE+1)) & 0xff ; +#else + ConnectorType = inp(ADDR(B2_CONN_TYP)) ; + PmdType = inp(ADDR(B2_PMD_TYP)) ; +#endif + + smc->y[PA].pmd_type[PMD_SK_CONN] = + smc->y[PB].pmd_type[PMD_SK_CONN] = ConnectorType ; + smc->y[PA].pmd_type[PMD_SK_PMD ] = + smc->y[PB].pmd_type[PMD_SK_PMD ] = PmdType ; + + if (mac_addr) { + for (i = 0; i < 6 ;i++) { + smc->hw.fddi_canon_addr.a[i] = mac_addr[i] ; + smc->hw.fddi_home_addr.a[i] = canonical[mac_addr[i]] ; + } + return ; + } + smc->hw.fddi_home_addr = smc->hw.fddi_phys_addr ; + + for (i = 0; i < 6 ;i++) { + smc->hw.fddi_canon_addr.a[i] = + canonical[smc->hw.fddi_phys_addr.a[i]] ; + } +} + +/* + * FDDI card soft reset + */ +void init_board(smc,mac_addr) +struct s_smc *smc ; +u_char *mac_addr ; +{ + card_start(smc) ; + read_address(smc,mac_addr) ; + +#ifndef PCI + if (inpw(CSR_A) & CS_SAS) +#else + if (!(inp(ADDR(B0_DAS)) & DAS_AVAIL)) +#endif + smc->s.sas = SMT_SAS ; /* Single att. station */ + else + smc->s.sas = SMT_DAS ; /* Dual att. station */ + +#ifndef PCI + if (inpw(CSR_A) & CS_BYSTAT) +#else + if (!(inp(ADDR(B0_DAS)) & DAS_BYP_ST)) +#endif + smc->mib.fddiSMTBypassPresent = 0 ; + /* without opt. bypass */ + else + smc->mib.fddiSMTBypassPresent = 1 ; + /* with opt. bypass */ +} + +/* + * insert or deinsert optical bypass (called by ECM) + */ +void sm_pm_bypass_req(smc,mode) +struct s_smc *smc ; +int mode; +{ +#if (defined(ISA) || defined(EISA)) + int csra_v ; +#endif + + DB_ECMN(1,"ECM : sm_pm_bypass_req(%s)\n",(mode == BP_INSERT) ? + "BP_INSERT" : "BP_DEINSERT",0) ; + + if (smc->s.sas != SMT_DAS) + return ; + +#if (defined(ISA) || defined(EISA)) + + csra_v = inpw(CSR_A) & ~CS_BYPASS ; +#ifdef EISA + csra_v |= smc->hw.led ; +#endif + + switch(mode) { + case BP_INSERT : + outpw(CSR_A,csra_v | CS_BYPASS) ; + break ; + case BP_DEINSERT : + outpw(CSR_A,csra_v) ; + break ; + } +#endif /* ISA / EISA */ +#ifdef MCA + switch(mode) { + case BP_INSERT : + outp(ADDR(BYPASS(STAT_INS)),0) ;/* insert station */ + break ; + case BP_DEINSERT : + outp(ADDR(BYPASS(STAT_BYP)),0) ; /* bypass station */ + break ; + } +#endif +#ifdef PCI + switch(mode) { + case BP_INSERT : + outp(ADDR(B0_DAS),DAS_BYP_INS) ; /* insert station */ + break ; + case BP_DEINSERT : + outp(ADDR(B0_DAS),DAS_BYP_RMV) ; /* bypass station */ + break ; + } +#endif +} + +/* + * check if bypass connected + */ +int sm_pm_bypass_present(smc) +struct s_smc *smc ; +{ +#ifndef PCI + return( (inpw(CSR_A) & CS_BYSTAT) ? FALSE : TRUE ) ; +#else + return( (inp(ADDR(B0_DAS)) & DAS_BYP_ST) ? TRUE: FALSE) ; +#endif +} + +void plc_clear_irq(smc,p) +struct s_smc *smc ; +int p ; +{ + SK_UNUSED(p) ; + +#if (defined(ISA) || defined(EISA)) + switch (p) { + case PA : + /* reset PLC Int. bits */ + outpw(PLC2_I,inpw(PLC2_I)) ; + break ; + case PB : + /* reset PLC Int. bits */ + outpw(PLC1_I,inpw(PLC1_I)) ; + break ; + } +#else + SK_UNUSED(smc) ; +#endif +} + + +/* + * led_indication called by rmt_indication() and + * pcm_state_change() + * + * Input: + * smc: SMT context + * led_event: + * 0 Only switch green LEDs according to their respective PCM state + * LED_Y_OFF just switch yellow LED off + * LED_Y_ON just switch yello LED on + */ +void led_indication(smc,led_event) +struct s_smc *smc ; +int led_event; +{ + /* use smc->hw.mac_ring_is_up == TRUE + * as indication for Ring Operational + */ + u_short led_state ; + struct s_phy *phy ; + struct fddi_mib_p *mib_a ; + struct fddi_mib_p *mib_b ; + + phy = &smc->y[PA] ; + mib_a = phy->mib ; + phy = &smc->y[PB] ; + mib_b = phy->mib ; + +#ifdef EISA + /* Ring up = yellow led OFF*/ + if (led_event == LED_Y_ON) { + smc->hw.led |= CS_LED_1 ; + } + else if (led_event == LED_Y_OFF) { + smc->hw.led &= ~CS_LED_1 ; + } + else { + /* Link at Port A or B = green led ON */ + if (mib_a->fddiPORTPCMState == PC8_ACTIVE || + mib_b->fddiPORTPCMState == PC8_ACTIVE) { + smc->hw.led |= CS_LED_0 ; + } + else { + smc->hw.led &= ~CS_LED_0 ; + } + } +#endif +#ifdef MCA + led_state = inpw(LEDR_A) ; + + /* Ring up = yellow led OFF*/ + if (led_event == LED_Y_ON) { + led_state |= LED_1 ; + } + else if (led_event == LED_Y_OFF) { + led_state &= ~LED_1 ; + } + else { + led_state &= ~(LED_2|LED_0) ; + + /* Link at Port A = green led A ON */ + if (mib_a->fddiPORTPCMState == PC8_ACTIVE) { + led_state |= LED_2 ; + } + + /* Link at Port B/S = green led B ON */ + if (mib_b->fddiPORTPCMState == PC8_ACTIVE) { + led_state |= LED_0 ; + } + } + + outpw(LEDR_A, led_state) ; +#endif /* MCA */ +#ifdef PCI + led_state = 0 ; + + /* Ring up = yellow led OFF*/ + if (led_event == LED_Y_ON) { + led_state |= LED_MY_ON ; + } + else if (led_event == LED_Y_OFF) { + led_state |= LED_MY_OFF ; + } + else { /* PCM state changed */ + /* Link at Port A/S = green led A ON */ + if (mib_a->fddiPORTPCMState == PC8_ACTIVE) { + led_state |= LED_GA_ON ; + } + else { + led_state |= LED_GA_OFF ; + } + + /* Link at Port B = green led B ON */ + if (mib_b->fddiPORTPCMState == PC8_ACTIVE) { + led_state |= LED_GB_ON ; + } + else { + led_state |= LED_GB_OFF ; + } + } + + outp(ADDR(B0_LED), led_state) ; +#endif /* PCI */ + +} + + +void pcm_state_change(smc,plc,p_state) +struct s_smc *smc; +int plc; +int p_state; +{ + /* + * the current implementation of pcm_state_change() in the driver + * parts must be renamed to drv_pcm_state_change() which will be called + * now after led_indication. + */ + DRV_PCM_STATE_CHANGE(smc,plc,p_state) ; + + led_indication(smc,0) ; +} + + +void rmt_indication(smc,i) +struct s_smc *smc ; +int i; +{ + /* Call a driver special function if defined */ + DRV_RMT_INDICATION(smc,i) ; + + led_indication(smc, i ? LED_Y_OFF : LED_Y_ON) ; +} + + +/* + * llc_recover_tx called by init_tx (fplus.c) + */ +void llc_recover_tx(smc) +struct s_smc *smc ; +{ +#ifdef LOAD_GEN + extern int load_gen_flag ; + + load_gen_flag = 0 ; +#endif +#ifndef SYNC + smc->hw.n_a_send= 0 ; +#else + SK_UNUSED(smc) ; +#endif +} + +/*--------------------------- DMA init ----------------------------*/ +#ifdef ISA + +/* + * init DMA + */ +void init_dma(smc,dma) +struct s_smc *smc; +int dma; +{ + SK_UNUSED(smc) ; + + /* + * set cascade mode, + * clear mask bit (enable DMA cannal) + */ + if (dma > 3) { + outp(0xd6,(dma & 0x03) | 0xc0) ; + outp(0xd4, dma & 0x03) ; + } + else { + outp(0x0b,(dma & 0x03) | 0xc0) ; + outp(0x0a,dma & 0x03) ; + } +} + +/* + * disable DMA + */ +void dis_dma(smc,dma) +struct s_smc *smc ; +int dma; +{ + SK_UNUSED(smc) ; + + /* + * set mask bit (disable DMA cannal) + */ + if (dma > 3) { + outp(0xd4,(dma & 0x03) | 0x04) ; + } + else { + outp(0x0a,(dma & 0x03) | 0x04) ; + } +} + +#endif /* ISA */ + +#ifdef EISA + +/*arrays with io adresses of dma controller length and adress registers*/ +static const int cntr[8] = { 0x001,0x003,0x005,0x007,0,0x0c6,0x0ca,0x0ce } ; +static const int base[8] = { 0x000,0x002,0x004,0x006,0,0x0c4,0x0c8,0x0cc } ; +static const int page[8] = { 0x087,0x083,0x081,0x082,0,0x08b,0x089,0x08a } ; + +void init_dma(smc,dma) +struct s_smc *smc ; +int dma; +{ + /* + * extended mode register + * 32 bit IO + * type c + * TC output + * disable stop + */ + + /* mode read (write) demand */ + smc->hw.dma_rmode = (dma & 3) | 0x08 | 0x0 ; + smc->hw.dma_wmode = (dma & 3) | 0x04 | 0x0 ; + + /* 32 bit IO's, burst DMA mode (type "C") */ + smc->hw.dma_emode = (dma & 3) | 0x08 | 0x30 ; + + outp((dma < 4) ? 0x40b : 0x4d6,smc->hw.dma_emode) ; + + /* disable chaining */ + outp((dma < 4) ? 0x40a : 0x4d4,(dma&3)) ; + + /*load dma controller addresses for fast access during set dma*/ + smc->hw.dma_base_word_count = cntr[smc->hw.dma]; + smc->hw.dma_base_address = base[smc->hw.dma]; + smc->hw.dma_base_address_page = page[smc->hw.dma]; + +} + +void dis_dma(smc,dma) +struct s_smc *smc ; +int dma; +{ + SK_UNUSED(smc) ; + + outp((dma < 4) ? 0x0a : 0xd4,(dma&3)|4) ;/* mask bit */ +} +#endif /* EISA */ + +#ifdef MCA +void init_dma(smc,dma) +struct s_smc *smc; +int dma; +{ + SK_UNUSED(smc) ; + SK_UNUSED(dma) ; +} +void dis_dma(smc,dma) +struct s_smc *smc; +int dma; +{ + SK_UNUSED(smc) ; + SK_UNUSED(dma) ; +} +#endif + +#ifdef PCI +void init_dma(smc,dma) +struct s_smc *smc; +int dma; +{ + SK_UNUSED(smc) ; + SK_UNUSED(dma) ; +} +void dis_dma(smc,dma) +struct s_smc *smc; +int dma; +{ + SK_UNUSED(smc) ; + SK_UNUSED(dma) ; +} +#endif + +#ifdef MULT_OEM +static int is_equal_num(comp1,comp2,num) +char comp1[] ; +char comp2[] ; +int num ; +{ + int i ; + + for (i = 0 ; i < num ; i++) { + if (comp1[i] != comp2[i]) + return (0) ; + } + return (1) ; +} /* is_equal_num */ + + +/* + * set the OEM ID defaults, and test the contents of the OEM data base + * The default OEM is the first ACTIVE entry in the OEM data base + * + * returns: 0 success + * 1 error in data base + * 2 data base empty + * 3 no active entry + */ +int set_oi_id_def(smc) +struct s_smc *smc ; +{ + int sel_id ; + int i ; + int act_entries ; + + i = 0 ; + sel_id = -1 ; + act_entries = FALSE ; + smc->hw.oem_id = 0 ; + smc->hw.oem_min_status = OI_STAT_ACTIVE ; + + /* check OEM data base */ + while (oem_ids[i].oi_status) { + switch (oem_ids[i].oi_status) { + case OI_STAT_ACTIVE: + act_entries = TRUE ; /* we have active IDs */ + if (sel_id == -1) + sel_id = i ; /* save the first active ID */ + case OI_STAT_VALID: + case OI_STAT_PRESENT: + i++ ; + break ; /* entry ok */ + default: + return (1) ; /* invalid oi_status */ + } + } + + if (i == 0) + return (2) ; + if (!act_entries) + return (3) ; + + /* ok, we have a valid OEM data base with an active entry */ + smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[sel_id] ; + return (0) ; +} +#endif /* MULT_OEM */ + + +#ifdef MCA +/************************ + * + * BEGIN_MANUAL_ENTRY() + * + * exist_board + * + * Check if an MCA board is present in the specified slot. + * + * int exist_board( + * struct s_smc *smc, + * int slot) ; + * In + * smc - A pointer to the SMT Context struct. + * + * slot - The number of the slot to inspect. + * Out + * 0 = No adapter present. + * 1 = Found FM1 adapter. + * + * Pseudo + * Read MCA ID + * for all valid OEM_IDs + * compare with ID read + * if equal, return 1 + * return(0 + * + * Note + * The smc pointer must be valid now. + * + * END_MANUAL_ENTRY() + * + ************************/ +#define LONG_CARD_ID(lo, hi) ((((hi) & 0xff) << 8) | ((lo) & 0xff)) +int exist_board(smc,slot) +struct s_smc *smc ; +int slot ; +{ +#ifdef MULT_OEM + SK_LOC_DECL(u_char,id[2]) ; + int idi ; +#endif /* MULT_OEM */ + + /* No longer valid. */ + if (smc == NULL) + return(0) ; + +#ifndef MULT_OEM + if (read_card_id(smc, slot) + == LONG_CARD_ID(OEMID(smc,0), OEMID(smc,1))) + return (1) ; /* Found FM adapter. */ + +#else /* MULT_OEM */ + idi = read_card_id(smc, slot) ; + id[0] = idi & 0xff ; + id[1] = idi >> 8 ; + + smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ; + for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) { + if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status) + continue ; + + if (is_equal_num(&id[0],&OEMID(smc,0),2)) + return (1) ; + } +#endif /* MULT_OEM */ + return (0) ; /* No adapter found. */ +} + +/************************ + * + * read_card_id + * + * Read the MCA card id from the specified slot. + * In + * smc - A pointer to the SMT Context struct. + * CAVEAT: This pointer may be NULL and *must not* be used within this + * function. It's only purpose is for drivers that need some information + * for the inp() and outp() macros. + * + * slot - The number of the slot for which the card id is returned. + * Out + * Returns the card id read from the specified slot. If an illegal slot + * number is specified, the function returns zero. + * + ************************/ +static int read_card_id(smc,slot) +struct s_smc *smc ; /* Do not use. */ +int slot ; +{ + int card_id ; + + SK_UNUSED(smc) ; /* Make LINT happy. */ + if ((slot < 1) || (slot > 15)) /* max 16 slots, 0 = motherboard */ + return (0) ; /* Illegal slot number specified. */ + + EnableSlotAccess(smc, slot) ; + + card_id = ((read_POS(smc,POS_ID_HIGH,slot - 1) & 0xff) << 8) | + (read_POS(smc,POS_ID_LOW,slot - 1) & 0xff) ; + + DisableSlotAccess(smc) ; + + return (card_id) ; +} + +/************************ + * + * BEGIN_MANUAL_ENTRY() + * + * get_board_para + * + * Get adapter configuration information. Fill all board specific + * parameters within the 'smc' structure. + * + * int get_board_para( + * struct s_smc *smc, + * int slot) ; + * In + * smc - A pointer to the SMT Context struct, to which this function will + * write some adapter configuration data. + * + * slot - The number of the slot, in which the adapter is installed. + * Out + * 0 = No adapter present. + * 1 = Ok. + * 2 = Adapter present, but card enable bit not set. + * + * END_MANUAL_ENTRY() + * + ************************/ +int get_board_para(smc,slot) +struct s_smc *smc ; +int slot ; +{ + int val ; + int i ; + + /* Check if adapter present & get type of adapter. */ + switch (exist_board(smc, slot)) { + case 0: /* Adapter not present. */ + return (0) ; + case 1: /* FM Rev. 1 */ + smc->hw.rev = FM1_REV ; + smc->hw.VFullRead = 0x0a ; + smc->hw.VFullWrite = 0x05 ; + smc->hw.DmaWriteExtraBytes = 8 ; /* 2 extra words. */ + break ; + } + smc->hw.slot = slot ; + + EnableSlotAccess(smc, slot) ; + + if (!(read_POS(smc,POS_102, slot - 1) & POS_CARD_EN)) { + DisableSlotAccess(smc) ; + return (2) ; /* Card enable bit not set. */ + } + + val = read_POS(smc,POS_104, slot - 1) ; /* I/O, IRQ */ + +#ifndef MEM_MAPPED_IO /* is defined by the operating system */ + i = val & POS_IOSEL ; /* I/O base addr. (0x0200 .. 0xfe00) */ + smc->hw.iop = (i + 1) * 0x0400 - 0x200 ; +#endif + i = ((val & POS_IRQSEL) >> 6) & 0x03 ; /* IRQ <0, 1> */ + smc->hw.irq = opt_ints[i] ; + + /* FPROM base addr. */ + i = ((read_POS(smc,POS_103, slot - 1) & POS_MSEL) >> 4) & 0x07 ; + smc->hw.eprom = opt_eproms[i] ; + + DisableSlotAccess(smc) ; + + /* before this, the smc->hw.iop must be set !!! */ + smc->hw.slot_32 = inpw(CSF_A) & SLOT_32 ; + + return (1) ; +} + +/* Enable access to specified MCA slot. */ +static void EnableSlotAccess(smc,slot) +struct s_smc *smc ; +int slot ; +{ + SK_UNUSED(slot) ; + +#ifndef AIX + SK_UNUSED(smc) ; + + /* System mode. */ + outp(POS_SYS_SETUP, POS_SYSTEM) ; + + /* Select slot. */ + outp(POS_CHANNEL_POS, POS_CHANNEL_BIT | (slot-1)) ; +#else + attach_POS_addr (smc) ; +#endif +} + +/* Disable access to MCA slot formerly enabled via EnableSlotAccess(). */ +static void DisableSlotAccess(smc) +struct s_smc *smc ; +{ +#ifndef AIX + SK_UNUSED(smc) ; + + outp(POS_CHANNEL_POS, 0) ; +#else + detach_POS_addr (smc) ; +#endif +} +#endif /* MCA */ + +#ifdef EISA +#ifndef MEM_MAPPED_IO +#define SADDR(slot) (((slot)<<12)&0xf000) +#else /* MEM_MAPPED_IO */ +#define SADDR(slot) (smc->hw.iop) +#endif /* MEM_MAPPED_IO */ + +/************************ + * + * BEGIN_MANUAL_ENTRY() + * + * exist_board + * + * Check if an EISA board is present in the specified slot. + * + * int exist_board( + * struct s_smc *smc, + * int slot) ; + * In + * smc - A pointer to the SMT Context struct. + * + * slot - The number of the slot to inspect. + * Out + * 0 = No adapter present. + * 1 = Found adapter. + * + * Pseudo + * Read EISA ID + * for all valid OEM_IDs + * compare with ID read + * if equal, return 1 + * return(0 + * + * Note + * The smc pointer must be valid now. + * + ************************/ +int exist_board(smc,slot) +struct s_smc *smc ; +int slot ; +{ + int i ; +#ifdef MULT_OEM + SK_LOC_DECL(u_char,id[4]) ; +#endif /* MULT_OEM */ + + /* No longer valid. */ + if (smc == NULL) + return(0); + + SK_UNUSED(slot) ; + +#ifndef MULT_OEM + for (i = 0 ; i < 4 ; i++) { + if (inp(SADDR(slot)+PRA(i)) != OEMID(smc,i)) + return(0) ; + } + return(1) ; +#else /* MULT_OEM */ + for (i = 0 ; i < 4 ; i++) + id[i] = inp(SADDR(slot)+PRA(i)) ; + + smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ; + + for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) { + if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status) + continue ; + + if (is_equal_num(&id[0],&OEMID(smc,0),4)) + return (1) ; + } + return (0) ; /* No adapter found. */ +#endif /* MULT_OEM */ +} + + +int get_board_para(smc,slot) +struct s_smc *smc ; +int slot ; +{ + int i ; + + if (!exist_board(smc,slot)) + return(0) ; + + smc->hw.slot = slot ; +#ifndef MEM_MAPPED_IO /* if defined by the operating system */ + smc->hw.iop = SADDR(slot) ; +#endif + + if (!(inp(C0_A(0))&CFG_CARD_EN)) { + return(2) ; /* CFG_CARD_EN bit not set! */ + } + + smc->hw.irq = opt_ints[(inp(C1_A(0)) & CFG_IRQ_SEL)] ; + smc->hw.dma = opt_dmas[((inp(C1_A(0)) & CFG_DRQ_SEL)>>3)] ; + + if ((i = inp(C2_A(0)) & CFG_EPROM_SEL) != 0x0f) + smc->hw.eprom = opt_eproms[i] ; + else + smc->hw.eprom = 0 ; + + smc->hw.DmaWriteExtraBytes = 8 ; + + return(1) ; +} +#endif /* EISA */ + +#ifdef ISA +#ifndef MULT_OEM +const u_char sklogo[6] = SKLOGO_STR ; +#define SIZE_SKLOGO(smc) sizeof(sklogo) +#define SKLOGO(smc,i) sklogo[i] +#else /* MULT_OEM */ +#define SIZE_SKLOGO(smc) smc->hw.oem_id->oi_logo_len +#define SKLOGO(smc,i) smc->hw.oem_id->oi_logo[i] +#endif /* MULT_OEM */ + + +int exist_board(smc,port) +struct s_smc *smc ; +HW_PTR port ; +{ + int i ; +#ifdef MULT_OEM + int bytes_read ; + u_char board_logo[15] ; + SK_LOC_DECL(u_char,id[4]) ; +#endif /* MULT_OEM */ + + /* No longer valid. */ + if (smc == NULL) + return(0); + + SK_UNUSED(smc) ; +#ifndef MULT_OEM + for (i = SADDRL ; i < (signed) (SADDRL+SIZE_SKLOGO(smc)) ; i++) { + if ((u_char)inpw((PRA(i)+port)) != SKLOGO(smc,i-SADDRL)) { + return(0) ; + } + } + + /* check MAC address (S&K or other) */ + for (i = 0 ; i < 3 ; i++) { + if ((u_char)inpw((PRA(i)+port)) != OEMID(smc,i)) + return(0) ; + } + return(1) ; +#else /* MULT_OEM */ + smc->hw.oem_id = (struct s_oem_ids *) &oem_ids[0] ; + board_logo[0] = (u_char)inpw((PRA(SADDRL)+port)) ; + bytes_read = 1 ; + + for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) { + if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status) + continue ; + + /* Test all read bytes with current OEM_entry */ + /* for (i=0; (ihw.oem_id = (struct s_oem_ids *) &oem_ids[0] ; + for (; smc->hw.oem_id->oi_status != OI_STAT_LAST; smc->hw.oem_id++) { + if (smc->hw.oem_id->oi_status < smc->hw.oem_min_status) + continue ; +#endif + ven_id = OEMID(smc,0) + (OEMID(smc,1) << 8) ; + dev_id = OEMID(smc,2) + (OEMID(smc,3) << 8) ; + for (i = 0; i < slot; i++) { + if (pci_find_device(i,&smc->hw.pci_handle, + dev_id,ven_id) != 0) { + + found = FALSE ; + } else { + found = TRUE ; + } + } + if (found) { + return(1) ; /* adapter was found */ + } +#ifdef MULT_OEM + } +#endif + return(0) ; /* adapter was not found */ +} +#endif /* PCI */ +#endif /* USE_BIOS_FUNC */ + +void driver_get_bia(smc, bia_addr) +struct s_smc *smc ; +struct fddi_addr *bia_addr ; +{ + int i ; + + extern const u_char canonical[256] ; + + for (i = 0 ; i < 6 ; i++) { + bia_addr->a[i] = canonical[smc->hw.fddi_phys_addr.a[i]] ; + } +} + +void smt_start_watchdog(smc) +struct s_smc *smc ; +{ + SK_UNUSED(smc) ; /* Make LINT happy. */ + +#ifndef DEBUG + +#ifdef PCI + if (smc->hw.wdog_used) { + outpw(ADDR(B2_WDOG_CRTL),TIM_START) ; /* Start timer. */ + } +#endif + +#endif /* DEBUG */ +} + +void smt_stop_watchdog(smc) +struct s_smc *smc ; +{ + SK_UNUSED(smc) ; /* Make LINT happy. */ +#ifndef DEBUG + +#ifdef PCI + if (smc->hw.wdog_used) { + outpw(ADDR(B2_WDOG_CRTL),TIM_STOP) ; /* Stop timer. */ + } +#endif + +#endif /* DEBUG */ +} + +#ifdef PCI +static char get_rom_byte(smc,addr) +struct s_smc *smc ; +u_short addr ; +{ + GET_PAGE(addr) ; + return (READ_PROM(ADDR(B2_FDP))) ; +} + +/* + * ROM image defines + */ +#define ROM_SIG_1 0 +#define ROM_SIG_2 1 +#define PCI_DATA_1 0x18 +#define PCI_DATA_2 0x19 + +/* + * PCI data structure defines + */ +#define VPD_DATA_1 0x08 +#define VPD_DATA_2 0x09 +#define IMAGE_LEN_1 0x10 +#define IMAGE_LEN_2 0x11 +#define CODE_TYPE 0x14 +#define INDICATOR 0x15 + +/* + * BEGIN_MANUAL_ENTRY(mac_drv_vpd_read) + * mac_drv_vpd_read(smc,buf,size,image) + * + * function DOWNCALL (FDDIWARE) + * reads the VPD data of the FPROM and writes it into the + * buffer + * + * para buf points to the buffer for the VPD data + * size size of the VPD data buffer + * image boot image; code type of the boot image + * image = 0 Intel x86, PC-AT compatible + * 1 OPENBOOT standard for PCI + * 2-FF reserved + * + * returns len number of VPD data bytes read form the FPROM + * <0 number of read bytes + * >0 error: data invalid + * + * END_MANUAL_ENTRY + */ +int mac_drv_vpd_read(smc,buf,size,image) +struct s_smc *smc ; +char *buf ; +int size ; +char image ; +{ + u_short ibase ; + u_short pci_base ; + u_short vpd ; + int len ; + + len = 0 ; + ibase = 0 ; + /* + * as long images defined + */ + while (get_rom_byte(smc,ibase+ROM_SIG_1) == 0x55 && + (u_char) get_rom_byte(smc,ibase+ROM_SIG_2) == 0xaa) { + /* + * get the pointer to the PCI data structure + */ + pci_base = ibase + get_rom_byte(smc,ibase+PCI_DATA_1) + + (get_rom_byte(smc,ibase+PCI_DATA_2) << 8) ; + + if (image == get_rom_byte(smc,pci_base+CODE_TYPE)) { + /* + * we have the right image, read the VPD data + */ + vpd = ibase + get_rom_byte(smc,pci_base+VPD_DATA_1) + + (get_rom_byte(smc,pci_base+VPD_DATA_2) << 8) ; + if (vpd == ibase) { + break ; /* no VPD data */ + } + for (len = 0; len < size; len++,buf++,vpd++) { + *buf = get_rom_byte(smc,vpd) ; + } + break ; + } + else { + /* + * try the next image + */ + if (get_rom_byte(smc,pci_base+INDICATOR) & 0x80) { + break ; /* this was the last image */ + } + ibase = ibase + get_rom_byte(smc,ibase+IMAGE_LEN_1) + + (get_rom_byte(smc,ibase+IMAGE_LEN_2) << 8) ; + } + } + + return(len) ; +} + +void mac_drv_pci_fix(smc,fix_value) +struct s_smc *smc ; +u_long fix_value ; +{ + smc->hw.pci_fix_value = fix_value ; +} + +void mac_do_pci_fix(smc) +struct s_smc *smc ; +{ + SK_UNUSED(smc) ; +} +#endif /* PCI */ diff --git a/drivers/net/skfp/ecm.c b/drivers/net/skfp/ecm.c new file mode 100644 index 000000000000..9dcca7f5581e --- /dev/null +++ b/drivers/net/skfp/ecm.c @@ -0,0 +1,547 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT ECM + Entity Coordination Management + Hardware independant state machine +*/ + +/* + * Hardware independant state machine implemantation + * The following external SMT functions are referenced : + * + * queue_event() + * smt_timer_start() + * smt_timer_stop() + * + * The following external HW dependant functions are referenced : + * sm_pm_bypass_req() + * sm_pm_ls_latch() + * sm_pm_get_ls() + * + * The following HW dependant events are required : + * NONE + * + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)ecm.c 2.7 99/08/05 (C) SK " ; +#endif + +/* + * FSM Macros + */ +#define AFLAG 0x10 +#define GO_STATE(x) (smc->mib.fddiSMTECMState = (x)|AFLAG) +#define ACTIONS_DONE() (smc->mib.fddiSMTECMState &= ~AFLAG) +#define ACTIONS(x) (x|AFLAG) + +#define EC0_OUT 0 /* not inserted */ +#define EC1_IN 1 /* inserted */ +#define EC2_TRACE 2 /* tracing */ +#define EC3_LEAVE 3 /* leaving the ring */ +#define EC4_PATH_TEST 4 /* performing path test */ +#define EC5_INSERT 5 /* bypass being turned on */ +#define EC6_CHECK 6 /* checking bypass */ +#define EC7_DEINSERT 7 /* bypass being turnde off */ + +#ifdef DEBUG +/* + * symbolic state names + */ +static const char * const ecm_states[] = { + "EC0_OUT","EC1_IN","EC2_TRACE","EC3_LEAVE","EC4_PATH_TEST", + "EC5_INSERT","EC6_CHECK","EC7_DEINSERT" +} ; + +/* + * symbolic event names + */ +static const char * const ecm_events[] = { + "NONE","EC_CONNECT","EC_DISCONNECT","EC_TRACE_PROP","EC_PATH_TEST", + "EC_TIMEOUT_TD","EC_TIMEOUT_TMAX", + "EC_TIMEOUT_IMAX","EC_TIMEOUT_INMAX","EC_TEST_DONE" +} ; +#endif + +/* + * all Globals are defined in smc.h + * struct s_ecm + */ + +/* + * function declarations + */ + +static void ecm_fsm() ; +static void start_ecm_timer() ; +static void stop_ecm_timer() ; +static void prop_actions() ; + +/* + init ECM state machine + clear all ECM vars and flags +*/ +void ecm_init(smc) +struct s_smc *smc ; +{ + smc->e.path_test = PT_PASSED ; + smc->e.trace_prop = 0 ; + smc->e.sb_flag = 0 ; + smc->mib.fddiSMTECMState = ACTIONS(EC0_OUT) ; + smc->e.ecm_line_state = FALSE ; +} + +/* + ECM state machine + called by dispatcher + + do + display state change + process event + until SM is stable +*/ +void ecm(smc,event) +struct s_smc *smc ; +int event ; +{ + int state ; + + do { + DB_ECM("ECM : state %s%s", + (smc->mib.fddiSMTECMState & AFLAG) ? "ACTIONS " : "", + ecm_states[smc->mib.fddiSMTECMState & ~AFLAG]) ; + DB_ECM(" event %s\n",ecm_events[event],0) ; + state = smc->mib.fddiSMTECMState ; + ecm_fsm(smc,event) ; + event = 0 ; + } while (state != smc->mib.fddiSMTECMState) ; + ecm_state_change(smc,(int)smc->mib.fddiSMTECMState) ; +} + +/* + process ECM event +*/ +static void ecm_fsm(smc,cmd) +struct s_smc *smc ; +int cmd ; +{ + int ls_a ; /* current line state PHY A */ + int ls_b ; /* current line state PHY B */ + int p ; /* ports */ + + + smc->mib.fddiSMTBypassPresent = sm_pm_bypass_present(smc) ; + if (cmd == EC_CONNECT) + smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ; + + /* For AIX event notification: */ + /* Is a disconnect command remotely issued ? */ + if (cmd == EC_DISCONNECT && + smc->mib.fddiSMTRemoteDisconnectFlag == TRUE) + AIX_EVENT (smc, (u_long) CIO_HARD_FAIL, (u_long) + FDDI_REMOTE_DISCONNECT, smt_get_event_word(smc), + smt_get_error_word(smc) ); + + /*jd 05-Aug-1999 Bug #10419 "Port Disconnect fails at Dup MAc Cond."*/ + if (cmd == EC_CONNECT) { + smc->e.DisconnectFlag = FALSE ; + } + else if (cmd == EC_DISCONNECT) { + smc->e.DisconnectFlag = TRUE ; + } + + switch(smc->mib.fddiSMTECMState) { + case ACTIONS(EC0_OUT) : + /* + * We do not perform a path test + */ + smc->e.path_test = PT_PASSED ; + smc->e.ecm_line_state = FALSE ; + stop_ecm_timer(smc) ; + ACTIONS_DONE() ; + break ; + case EC0_OUT: + /*EC01*/ + if (cmd == EC_CONNECT && !smc->mib.fddiSMTBypassPresent + && smc->e.path_test==PT_PASSED) { + GO_STATE(EC1_IN) ; + break ; + } + /*EC05*/ + else if (cmd == EC_CONNECT && (smc->e.path_test==PT_PASSED) && + smc->mib.fddiSMTBypassPresent && + (smc->s.sas == SMT_DAS)) { + GO_STATE(EC5_INSERT) ; + break ; + } + break; + case ACTIONS(EC1_IN) : + stop_ecm_timer(smc) ; + smc->e.trace_prop = 0 ; + sm_ma_control(smc,MA_TREQ) ; + for (p = 0 ; p < NUMPHYS ; p++) + if (smc->mib.p[p].fddiPORTHardwarePresent) + queue_event(smc,EVENT_PCMA+p,PC_START) ; + ACTIONS_DONE() ; + break ; + case EC1_IN: + /*EC12*/ + if (cmd == EC_TRACE_PROP) { + prop_actions(smc) ; + GO_STATE(EC2_TRACE) ; + break ; + } + /*EC13*/ + else if (cmd == EC_DISCONNECT) { + GO_STATE(EC3_LEAVE) ; + break ; + } + break; + case ACTIONS(EC2_TRACE) : + start_ecm_timer(smc,MIB2US(smc->mib.fddiSMTTrace_MaxExpiration), + EC_TIMEOUT_TMAX) ; + ACTIONS_DONE() ; + break ; + case EC2_TRACE : + /*EC22*/ + if (cmd == EC_TRACE_PROP) { + prop_actions(smc) ; + GO_STATE(EC2_TRACE) ; + break ; + } + /*EC23a*/ + else if (cmd == EC_DISCONNECT) { + smc->e.path_test = PT_EXITING ; + GO_STATE(EC3_LEAVE) ; + break ; + } + /*EC23b*/ + else if (smc->e.path_test == PT_PENDING) { + GO_STATE(EC3_LEAVE) ; + break ; + } + /*EC23c*/ + else if (cmd == EC_TIMEOUT_TMAX) { + /* Trace_Max is expired */ + /* -> send AIX_EVENT */ + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, + (u_long) FDDI_SMT_ERROR, (u_long) + FDDI_TRACE_MAX, smt_get_error_word(smc)); + smc->e.path_test = PT_PENDING ; + GO_STATE(EC3_LEAVE) ; + break ; + } + break ; + case ACTIONS(EC3_LEAVE) : + start_ecm_timer(smc,smc->s.ecm_td_min,EC_TIMEOUT_TD) ; + for (p = 0 ; p < NUMPHYS ; p++) + queue_event(smc,EVENT_PCMA+p,PC_STOP) ; + ACTIONS_DONE() ; + break ; + case EC3_LEAVE: + /*EC30*/ + if (cmd == EC_TIMEOUT_TD && !smc->mib.fddiSMTBypassPresent && + (smc->e.path_test != PT_PENDING)) { + GO_STATE(EC0_OUT) ; + break ; + } + /*EC34*/ + else if (cmd == EC_TIMEOUT_TD && + (smc->e.path_test == PT_PENDING)) { + GO_STATE(EC4_PATH_TEST) ; + break ; + } + /*EC31*/ + else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) { + GO_STATE(EC1_IN) ; + break ; + } + /*EC33*/ + else if (cmd == EC_DISCONNECT && + smc->e.path_test == PT_PENDING) { + smc->e.path_test = PT_EXITING ; + /* + * stay in state - state will be left via timeout + */ + } + /*EC37*/ + else if (cmd == EC_TIMEOUT_TD && + smc->mib.fddiSMTBypassPresent && + smc->e.path_test != PT_PENDING) { + GO_STATE(EC7_DEINSERT) ; + break ; + } + break ; + case ACTIONS(EC4_PATH_TEST) : + stop_ecm_timer(smc) ; + smc->e.path_test = PT_TESTING ; + start_ecm_timer(smc,smc->s.ecm_test_done,EC_TEST_DONE) ; + /* now perform path test ... just a simulation */ + ACTIONS_DONE() ; + break ; + case EC4_PATH_TEST : + /* path test done delay */ + if (cmd == EC_TEST_DONE) + smc->e.path_test = PT_PASSED ; + + if (smc->e.path_test == PT_FAILED) + RS_SET(smc,RS_PATHTEST) ; + + /*EC40a*/ + if (smc->e.path_test == PT_FAILED && + !smc->mib.fddiSMTBypassPresent) { + GO_STATE(EC0_OUT) ; + break ; + } + /*EC40b*/ + else if (cmd == EC_DISCONNECT && + !smc->mib.fddiSMTBypassPresent) { + GO_STATE(EC0_OUT) ; + break ; + } + /*EC41*/ + else if (smc->e.path_test == PT_PASSED) { + GO_STATE(EC1_IN) ; + break ; + } + /*EC47a*/ + else if (smc->e.path_test == PT_FAILED && + smc->mib.fddiSMTBypassPresent) { + GO_STATE(EC7_DEINSERT) ; + break ; + } + /*EC47b*/ + else if (cmd == EC_DISCONNECT && + smc->mib.fddiSMTBypassPresent) { + GO_STATE(EC7_DEINSERT) ; + break ; + } + break ; + case ACTIONS(EC5_INSERT) : + sm_pm_bypass_req(smc,BP_INSERT); + start_ecm_timer(smc,smc->s.ecm_in_max,EC_TIMEOUT_INMAX) ; + ACTIONS_DONE() ; + break ; + case EC5_INSERT : + /*EC56*/ + if (cmd == EC_TIMEOUT_INMAX) { + GO_STATE(EC6_CHECK) ; + break ; + } + /*EC57*/ + else if (cmd == EC_DISCONNECT) { + GO_STATE(EC7_DEINSERT) ; + break ; + } + break ; + case ACTIONS(EC6_CHECK) : + /* + * in EC6_CHECK, we *POLL* the line state ! + * check whether both bypass switches have switched. + */ + start_ecm_timer(smc,smc->s.ecm_check_poll,0) ; + smc->e.ecm_line_state = TRUE ; /* flag to pcm: report Q/HLS */ + (void) sm_pm_ls_latch(smc,PA,1) ; /* enable line state latch */ + (void) sm_pm_ls_latch(smc,PB,1) ; /* enable line state latch */ + ACTIONS_DONE() ; + break ; + case EC6_CHECK : + ls_a = sm_pm_get_ls(smc,PA) ; + ls_b = sm_pm_get_ls(smc,PB) ; + + /*EC61*/ + if (((ls_a == PC_QLS) || (ls_a == PC_HLS)) && + ((ls_b == PC_QLS) || (ls_b == PC_HLS)) ) { + smc->e.sb_flag = FALSE ; + smc->e.ecm_line_state = FALSE ; + GO_STATE(EC1_IN) ; + break ; + } + /*EC66*/ + else if (!smc->e.sb_flag && + (((ls_a == PC_ILS) && (ls_b == PC_QLS)) || + ((ls_a == PC_QLS) && (ls_b == PC_ILS)))){ + smc->e.sb_flag = TRUE ; + DB_ECMN(1,"ECM : EC6_CHECK - stuck bypass\n",0,0) ; + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_SMT_ERROR, (u_long) FDDI_BYPASS_STUCK, + smt_get_error_word(smc)); + } + /*EC67*/ + else if (cmd == EC_DISCONNECT) { + smc->e.ecm_line_state = FALSE ; + GO_STATE(EC7_DEINSERT) ; + break ; + } + else { + /* + * restart poll + */ + start_ecm_timer(smc,smc->s.ecm_check_poll,0) ; + } + break ; + case ACTIONS(EC7_DEINSERT) : + sm_pm_bypass_req(smc,BP_DEINSERT); + start_ecm_timer(smc,smc->s.ecm_i_max,EC_TIMEOUT_IMAX) ; + ACTIONS_DONE() ; + break ; + case EC7_DEINSERT: + /*EC70*/ + if (cmd == EC_TIMEOUT_IMAX) { + GO_STATE(EC0_OUT) ; + break ; + } + /*EC75*/ + else if (cmd == EC_CONNECT && smc->e.path_test == PT_PASSED) { + GO_STATE(EC5_INSERT) ; + break ; + } + break; + default: + SMT_PANIC(smc,SMT_E0107, SMT_E0107_MSG) ; + break; + } +} + +#ifndef CONCENTRATOR +/* + * trace propagation actions for SAS & DAS + */ +static void prop_actions(smc) +struct s_smc *smc ; +{ + int port_in = 0 ; + int port_out = 0 ; + + RS_SET(smc,RS_EVENT) ; + switch (smc->s.sas) { + case SMT_SAS : + port_in = port_out = pcm_get_s_port(smc) ; + break ; + case SMT_DAS : + port_in = cfm_get_mac_input(smc) ; /* PA or PB */ + port_out = cfm_get_mac_output(smc) ; /* PA or PB */ + break ; + case SMT_NAC : + SMT_PANIC(smc,SMT_E0108, SMT_E0108_MSG) ; + return ; + } + + DB_ECM("ECM : prop_actions - trace_prop %d\n", smc->e.trace_prop,0) ; + DB_ECM("ECM : prop_actions - in %d out %d\n", port_in,port_out) ; + + if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) { + /* trace initiatior */ + DB_ECM("ECM : initiate TRACE on PHY %c\n",'A'+port_in-PA,0) ; + queue_event(smc,EVENT_PCM+port_in,PC_TRACE) ; + } + else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PA))) && + port_out != PA) { + /* trace propagate upstream */ + DB_ECM("ECM : propagate TRACE on PHY B\n",0,0) ; + queue_event(smc,EVENT_PCMB,PC_TRACE) ; + } + else if ((smc->e.trace_prop & ENTITY_BIT(ENTITY_PHY(PB))) && + port_out != PB) { + /* trace propagate upstream */ + DB_ECM("ECM : propagate TRACE on PHY A\n",0,0) ; + queue_event(smc,EVENT_PCMA,PC_TRACE) ; + } + else { + /* signal trace termination */ + DB_ECM("ECM : TRACE terminated\n",0,0) ; + smc->e.path_test = PT_PENDING ; + } + smc->e.trace_prop = 0 ; +} +#else +/* + * trace propagation actions for Concentrator + */ +static void prop_actions(smc) +struct s_smc *smc ; +{ + int initiator ; + int upstream ; + int p ; + + RS_SET(smc,RS_EVENT) ; + while (smc->e.trace_prop) { + DB_ECM("ECM : prop_actions - trace_prop %d\n", + smc->e.trace_prop,0) ; + + if (smc->e.trace_prop & ENTITY_BIT(ENTITY_MAC)) { + initiator = ENTITY_MAC ; + smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_MAC) ; + DB_ECM("ECM: MAC initiates trace\n",0,0) ; + } + else { + for (p = NUMPHYS-1 ; p >= 0 ; p--) { + if (smc->e.trace_prop & + ENTITY_BIT(ENTITY_PHY(p))) + break ; + } + initiator = ENTITY_PHY(p) ; + smc->e.trace_prop &= ~ENTITY_BIT(ENTITY_PHY(p)) ; + } + upstream = cem_get_upstream(smc,initiator) ; + + if (upstream == ENTITY_MAC) { + /* signal trace termination */ + DB_ECM("ECM : TRACE terminated\n",0,0) ; + smc->e.path_test = PT_PENDING ; + } + else { + /* trace propagate upstream */ + DB_ECM("ECM : propagate TRACE on PHY %d\n",upstream,0) ; + queue_event(smc,EVENT_PCM+upstream,PC_TRACE) ; + } + } +} +#endif + + +/* + * SMT timer interface + * start ECM timer + */ +static void start_ecm_timer(smc,value,event) +struct s_smc *smc ; +u_long value; +int event ; +{ + smt_timer_start(smc,&smc->e.ecm_timer,value,EV_TOKEN(EVENT_ECM,event)); +} + +/* + * SMT timer interface + * stop ECM timer + */ +static void stop_ecm_timer(smc) +struct s_smc *smc ; +{ + if (smc->e.ecm_timer.tm_active) + smt_timer_stop(smc,&smc->e.ecm_timer) ; +} diff --git a/drivers/net/skfp/ess.c b/drivers/net/skfp/ess.c new file mode 100644 index 000000000000..a4c6409b4e34 --- /dev/null +++ b/drivers/net/skfp/ess.c @@ -0,0 +1,732 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * ******************************************************************* + * This SBA code implements the Synchronous Bandwidth Allocation + * functions described in the "FDDI Synchronous Forum Implementer's + * Agreement" dated December 1th, 1993. + * ******************************************************************* + * + * PURPOSE: The purpose of this function is to control + * synchronous allocations on a single FDDI segment. + * Allocations are limited to the primary FDDI ring. + * The SBM provides recovery mechanisms to recover + * unused bandwidth also resolves T_Neg and + * reconfiguration changes. Many of the SBM state + * machine inputs are sourced by the underlying + * FDDI sub-system supporting the SBA application. + * + * ******************************************************************* + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smt_p.h" + + +#ifndef SLIM_SMT + +#ifdef ESS + +#ifndef lint +static const char ID_sccs[] = "@(#)ess.c 1.10 96/02/23 (C) SK" ; +#define LINT_USE(x) +#else +#define LINT_USE(x) (x)=(x) +#endif +#define MS2BCLK(x) ((x)*12500L) + +/* + ------------------------------------------------------------- + LOCAL VARIABLES: + ------------------------------------------------------------- +*/ + +static const u_short plist_raf_alc_res[] = { SMT_P0012, SMT_P320B, SMT_P320F, + SMT_P3210, SMT_P0019, SMT_P001A, + SMT_P001D, 0 } ; + +static const u_short plist_raf_chg_req[] = { SMT_P320B, SMT_P320F, SMT_P3210, + SMT_P001A, 0 } ; + +static const struct fddi_addr smt_sba_da = {{0x80,0x01,0x43,0x00,0x80,0x0C}} ; +static const struct fddi_addr null_addr = {{0,0,0,0,0,0}} ; + +/* + ------------------------------------------------------------- + GLOBAL VARIABLES: + ------------------------------------------------------------- +*/ + + +/* + ------------------------------------------------------------- + LOCAL FUNCTIONS: + ------------------------------------------------------------- +*/ + +static void ess_send_response(), ess_config_fifo(), + ess_send_alc_req(), ess_send_frame() ; + +/* + ------------------------------------------------------------- + EXTERNAL FUNCTIONS: + ------------------------------------------------------------- +*/ + +extern void *sm_to_para() ; + +extern void smt_send_frame(), smt_free_mbuf(), + set_formac_tsync(), formac_reinit_tx() ; + +extern int smt_check_para() ; + +extern SMbuf *smt_get_mbuf(), *smt_build_frame() ; + +extern u_long smt_get_tid() ; + +/* + ------------------------------------------------------------- + PUBLIC FUNCTIONS: + ------------------------------------------------------------- +*/ + + void ess_timer_poll(), ess_para_change() ; + + int ess_raf_received_pack(), process_bw_alloc() ; + + +/* + * -------------------------------------------------------------------------- + * End Station Support (ESS) + * -------------------------------------------------------------------------- + */ + +/* + * evaluate the RAF frame + */ +int ess_raf_received_pack(smc,mb,sm,fs) +struct s_smc *smc ; +SMbuf *mb ; +struct smt_header *sm ; +int fs ; +{ + void *p ; /* universal pointer */ + struct smt_p_0016 *cmd ; /* para: command for the ESS */ + SMbuf *db ; + u_long msg_res_type ; /* recource type */ + u_long payload, overhead ; + int local ; + int i ; + + /* + * Message Processing Code + */ + local = ((fs & L_INDICATOR) != 0) ; + + /* + * get the resource type + */ + if (!(p = (void *) sm_to_para(smc,sm,SMT_P0015))) { + DB_ESS("ESS: RAF frame error, parameter type not found\n",0,0) ; + return(fs) ; + } + msg_res_type = ((struct smt_p_0015 *)p)->res_type ; + + /* + * get the pointer to the ESS command + */ + if (!(cmd = (struct smt_p_0016 *) sm_to_para(smc,sm,SMT_P0016))) { + /* + * error in frame: para ESS command was not found + */ + DB_ESS("ESS: RAF frame error, parameter command not found\n",0,0); + return(fs) ; + } + + DB_ESSN(2,"fc %x ft %x\n",sm->smt_class,sm->smt_type) ; + DB_ESSN(2,"ver %x tran %lx\n",sm->smt_version,sm->smt_tid) ; + DB_ESSN(2,"stn_id %s\n",addr_to_string(&sm->smt_source),0) ; + + DB_ESSN(2,"infolen %x res %x\n",sm->smt_len, msg_res_type) ; + DB_ESSN(2,"sbacmd %x\n",cmd->sba_cmd,0) ; + + /* + * evaluate the ESS command + */ + switch (cmd->sba_cmd) { + + /* + * Process an ESS Allocation Request + */ + case REQUEST_ALLOCATION : + /* + * check for an RAF Request (Allocation Request) + */ + if (sm->smt_type == SMT_REQUEST) { + /* + * process the Allocation request only if the frame is + * local and no static allocation is used + */ + if (!local || smc->mib.fddiESSPayload) + return(fs) ; + + p = (void *) sm_to_para(smc,sm,SMT_P0019) ; + for (i = 0; i < 5; i++) { + if (((struct smt_p_0019 *)p)->alloc_addr.a[i]) { + return(fs) ; + } + } + + /* + * Note: The Application should send a LAN_LOC_FRAME. + * The ESS do not send the Frame to the network! + */ + smc->ess.alloc_trans_id = sm->smt_tid ; + DB_ESS("ESS: save Alloc Req Trans ID %lx\n",sm->smt_tid,0); + p = (void *) sm_to_para(smc,sm,SMT_P320F) ; + ((struct smt_p_320f *)p)->mib_payload = + smc->mib.a[PATH0].fddiPATHSbaPayload ; + p = (void *) sm_to_para(smc,sm,SMT_P3210) ; + ((struct smt_p_3210 *)p)->mib_overhead = + smc->mib.a[PATH0].fddiPATHSbaOverhead ; + sm->smt_dest = smt_sba_da ; + + if (smc->ess.local_sba_active) + return(fs | I_INDICATOR) ; + + if (!(db = smt_get_mbuf(smc))) + return(fs) ; + + db->sm_len = mb->sm_len ; + db->sm_off = mb->sm_off ; + memcpy(((char *)(db->sm_data+db->sm_off)),(char *)sm, + (int)db->sm_len) ; + dump_smt(smc, + (struct smt_header *)(db->sm_data+db->sm_off), + "RAF") ; + smt_send_frame(smc,db,FC_SMT_INFO,0) ; + return(fs) ; + } + + /* + * The RAF frame is an Allocation Response ! + * check the parameters + */ + if (smt_check_para(smc,sm,plist_raf_alc_res)) { + DB_ESS("ESS: RAF with para problem, ignoring\n",0,0) ; + return(fs) ; + } + + /* + * VERIFY THE FRAME IS WELL BUILT: + * + * 1. path index = primary ring only + * 2. resource type = sync bw only + * 3. trans action id = alloc_trans_id + * 4. reason code = success + * + * If any are violated, discard the RAF frame + */ + if ((((struct smt_p_320b *)sm_to_para(smc,sm,SMT_P320B))->path_index + != PRIMARY_RING) || + (msg_res_type != SYNC_BW) || + (((struct smt_p_reason *)sm_to_para(smc,sm,SMT_P0012))->rdf_reason + != SMT_RDF_SUCCESS) || + (sm->smt_tid != smc->ess.alloc_trans_id)) { + + DB_ESS("ESS: Allocation Responce not accepted\n",0,0) ; + return(fs) ; + } + + /* + * Extract message parameters + */ + p = (void *) sm_to_para(smc,sm,SMT_P320F) ; + payload = ((struct smt_p_320f *)p)->mib_payload ; + p = (void *) sm_to_para(smc,sm,SMT_P3210) ; + overhead = ((struct smt_p_3210 *)p)->mib_overhead ; + + DB_ESSN(2,"payload= %lx overhead= %lx\n",payload,overhead) ; + + /* + * process the bandwidth allocation + */ + (void)process_bw_alloc(smc,(long)payload,(long)overhead) ; + + return(fs) ; + /* end of Process Allocation Request */ + + /* + * Process an ESS Change Request + */ + case CHANGE_ALLOCATION : + /* + * except only replies + */ + if (sm->smt_type != SMT_REQUEST) { + DB_ESS("ESS: Do not process Change Responses\n",0,0) ; + return(fs) ; + } + + /* + * check the para for the Change Request + */ + if (smt_check_para(smc,sm,plist_raf_chg_req)) { + DB_ESS("ESS: RAF with para problem, ignoring\n",0,0) ; + return(fs) ; + } + + /* + * Verify the path index and resource + * type are correct. If any of + * these are false, don't process this + * change request frame. + */ + if ((((struct smt_p_320b *)sm_to_para(smc,sm,SMT_P320B))->path_index + != PRIMARY_RING) || (msg_res_type != SYNC_BW)) { + DB_ESS("ESS: RAF frame with para problem, ignoring\n",0,0) ; + return(fs) ; + } + + /* + * Extract message queue parameters + */ + p = (void *) sm_to_para(smc,sm,SMT_P320F) ; + payload = ((struct smt_p_320f *)p)->mib_payload ; + p = (void *) sm_to_para(smc,sm,SMT_P3210) ; + overhead = ((struct smt_p_3210 *)p)->mib_overhead ; + + DB_ESSN(2,"ESS: Change Request from %s\n", + addr_to_string(&sm->smt_source),0) ; + DB_ESSN(2,"payload= %lx overhead= %lx\n",payload,overhead) ; + + /* + * process the bandwidth allocation + */ + if(!process_bw_alloc(smc,(long)payload,(long)overhead)) + return(fs) ; + + /* + * send an RAF Change Reply + */ + ess_send_response(smc,sm,CHANGE_ALLOCATION) ; + + return(fs) ; + /* end of Process Change Request */ + + /* + * Process Report Response + */ + case REPORT_ALLOCATION : + /* + * except only requests + */ + if (sm->smt_type != SMT_REQUEST) { + DB_ESS("ESS: Do not process a Report Reply\n",0,0) ; + return(fs) ; + } + + DB_ESSN(2,"ESS: Report Request from %s\n", + addr_to_string(&(sm->smt_source)),0) ; + + /* + * verify that the resource type is sync bw only + */ + if (msg_res_type != SYNC_BW) { + DB_ESS("ESS: ignoring RAF with para problem\n",0,0) ; + return(fs) ; + } + + /* + * send an RAF Change Reply + */ + ess_send_response(smc,sm,REPORT_ALLOCATION) ; + + return(fs) ; + /* end of Process Report Request */ + + default: + /* + * error in frame + */ + DB_ESS("ESS: ignoring RAF with bad sba_cmd\n",0,0) ; + break ; + } + + return(fs) ; +} + +/* + * determines the synchronous bandwidth, set the TSYNC register and the + * mib variables SBAPayload, SBAOverhead and fddiMACT-NEG. + */ +int process_bw_alloc(smc,payload,overhead) +struct s_smc *smc ; +long payload ; +long overhead ; +{ + /* + * determine the synchronous bandwidth (sync_bw) in bytes per T-NEG, + * if the payload is greater than zero. + * For the SBAPayload and the SBAOverhead we have the following + * unite quations + * _ _ + * | bytes | + * SBAPayload = | 8000 ------ | + * | s | + * - - + * _ _ + * | bytes | + * SBAOverhead = | ------ | + * | T-NEG | + * - - + * + * T-NEG is discribed by the equation: + * + * (-) fddiMACT-NEG + * T-NEG = ------------------- + * 12500000 1/s + * + * The number of bytes we are able to send is the payload + * plus the overhead. + * + * bytes T-NEG SBAPayload 8000 bytes/s + * sync_bw = SBAOverhead ------ + ----------------------------- + * T-NEG T-NEG + * + * + * 1 + * sync_bw = SBAOverhead + ---- (-)fddiMACT-NEG * SBAPayload + * 1562 + * + */ + + /* + * set the mib attributes fddiPATHSbaOverhead, fddiPATHSbaPayload + */ +/* if (smt_set_obj(smc,SMT_P320F,payload,S_SET)) { + DB_ESS("ESS: SMT does not accept the payload value\n",0,0) ; + return(FALSE) ; + } + if (smt_set_obj(smc,SMT_P3210,overhead,S_SET)) { + DB_ESS("ESS: SMT does not accept the overhead value\n",0,0) ; + return(FALSE) ; + } */ + + /* premliminary */ + if (payload > MAX_PAYLOAD || overhead > 5000) { + DB_ESS("ESS: payload / overhead not accepted\n",0,0) ; + return(FALSE) ; + } + + /* + * start the iterative allocation process if the payload or the overhead + * are smaller than the parsed values + */ + if (smc->mib.fddiESSPayload && + ((u_long)payload != smc->mib.fddiESSPayload || + (u_long)overhead != smc->mib.fddiESSOverhead)) { + smc->ess.raf_act_timer_poll = TRUE ; + smc->ess.timer_count = 0 ; + } + + /* + * evulate the Payload + */ + if (payload) { + DB_ESSN(2,"ESS: turn SMT_ST_SYNC_SERVICE bit on\n",0,0) ; + smc->ess.sync_bw_available = TRUE ; + + smc->ess.sync_bw = overhead - + (long)smc->mib.m[MAC0].fddiMACT_Neg * + payload / 1562 ; + } + else { + DB_ESSN(2,"ESS: turn SMT_ST_SYNC_SERVICE bit off\n",0,0) ; + smc->ess.sync_bw_available = FALSE ; + smc->ess.sync_bw = 0 ; + overhead = 0 ; + } + + smc->mib.a[PATH0].fddiPATHSbaPayload = payload ; + smc->mib.a[PATH0].fddiPATHSbaOverhead = overhead ; + + + DB_ESSN(2,"tsync = %lx\n",smc->ess.sync_bw,0) ; + + ess_config_fifo(smc) ; + set_formac_tsync(smc,smc->ess.sync_bw) ; + return(TRUE) ; +} + +static void ess_send_response(smc,sm,sba_cmd) +struct s_smc *smc ; +struct smt_header *sm ; +int sba_cmd ; +{ + struct smt_sba_chg *chg ; + SMbuf *mb ; + void *p ; + + /* + * get and initialize the responce frame + */ + if (sba_cmd == CHANGE_ALLOCATION) { + if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REPLY, + sizeof(struct smt_sba_chg)))) + return ; + } + else { + if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REPLY, + sizeof(struct smt_sba_rep_res)))) + return ; + } + + chg = smtod(mb,struct smt_sba_chg *) ; + chg->smt.smt_tid = sm->smt_tid ; + chg->smt.smt_dest = sm->smt_source ; + + /* set P15 */ + chg->s_type.para.p_type = SMT_P0015 ; + chg->s_type.para.p_len = sizeof(struct smt_p_0015) - PARA_LEN ; + chg->s_type.res_type = SYNC_BW ; + + /* set P16 */ + chg->cmd.para.p_type = SMT_P0016 ; + chg->cmd.para.p_len = sizeof(struct smt_p_0016) - PARA_LEN ; + chg->cmd.sba_cmd = sba_cmd ; + + /* set P320B */ + chg->path.para.p_type = SMT_P320B ; + chg->path.para.p_len = sizeof(struct smt_p_320b) - PARA_LEN ; + chg->path.mib_index = SBAPATHINDEX ; + chg->path.path_pad = (u_short)NULL ; + chg->path.path_index = PRIMARY_RING ; + + /* set P320F */ + chg->payload.para.p_type = SMT_P320F ; + chg->payload.para.p_len = sizeof(struct smt_p_320f) - PARA_LEN ; + chg->payload.mib_index = SBAPATHINDEX ; + chg->payload.mib_payload = smc->mib.a[PATH0].fddiPATHSbaPayload ; + + /* set P3210 */ + chg->overhead.para.p_type = SMT_P3210 ; + chg->overhead.para.p_len = sizeof(struct smt_p_3210) - PARA_LEN ; + chg->overhead.mib_index = SBAPATHINDEX ; + chg->overhead.mib_overhead = smc->mib.a[PATH0].fddiPATHSbaOverhead ; + + if (sba_cmd == CHANGE_ALLOCATION) { + /* set P1A */ + chg->cat.para.p_type = SMT_P001A ; + chg->cat.para.p_len = sizeof(struct smt_p_001a) - PARA_LEN ; + p = (void *) sm_to_para(smc,sm,SMT_P001A) ; + chg->cat.category = ((struct smt_p_001a *)p)->category ; + } + dump_smt(smc,(struct smt_header *)chg,"RAF") ; + ess_send_frame(smc,mb) ; +} + + +void ess_timer_poll(smc) +struct s_smc *smc ; +{ + if (!smc->ess.raf_act_timer_poll) + return ; + + DB_ESSN(2,"ESS: timer_poll\n",0,0) ; + + smc->ess.timer_count++ ; + if (smc->ess.timer_count == 10) { + smc->ess.timer_count = 0 ; + ess_send_alc_req(smc) ; + } +} + +static void ess_send_alc_req(smc) +struct s_smc *smc ; +{ + struct smt_sba_alc_req *req ; + SMbuf *mb ; + + /* + * send never allocation request where the requested payload and + * overhead is zero or deallocate bandwidht when no bandwidth is + * parsed + */ + if (!smc->mib.fddiESSPayload) { + smc->mib.fddiESSOverhead = 0 ; + } + else { + if (!smc->mib.fddiESSOverhead) + smc->mib.fddiESSOverhead = DEFAULT_OV ; + } + + if (smc->mib.fddiESSOverhead == + smc->mib.a[PATH0].fddiPATHSbaOverhead && + smc->mib.fddiESSPayload == + smc->mib.a[PATH0].fddiPATHSbaPayload){ + smc->ess.raf_act_timer_poll = FALSE ; + smc->ess.timer_count = 7 ; /* next RAF alc req after 3 s */ + return ; + } + + /* + * get and initialize the responce frame + */ + if (!(mb=smt_build_frame(smc,SMT_RAF,SMT_REQUEST, + sizeof(struct smt_sba_alc_req)))) + return ; + req = smtod(mb,struct smt_sba_alc_req *) ; + req->smt.smt_tid = smc->ess.alloc_trans_id = smt_get_tid(smc) ; + req->smt.smt_dest = smt_sba_da ; + + /* set P15 */ + req->s_type.para.p_type = SMT_P0015 ; + req->s_type.para.p_len = sizeof(struct smt_p_0015) - PARA_LEN ; + req->s_type.res_type = SYNC_BW ; + + /* set P16 */ + req->cmd.para.p_type = SMT_P0016 ; + req->cmd.para.p_len = sizeof(struct smt_p_0016) - PARA_LEN ; + req->cmd.sba_cmd = REQUEST_ALLOCATION ; + + /* + * set the parameter type and parameter lenght of all used + * parameters + */ + + /* set P320B */ + req->path.para.p_type = SMT_P320B ; + req->path.para.p_len = sizeof(struct smt_p_320b) - PARA_LEN ; + req->path.mib_index = SBAPATHINDEX ; + req->path.path_pad = (u_short)NULL ; + req->path.path_index = PRIMARY_RING ; + + /* set P0017 */ + req->pl_req.para.p_type = SMT_P0017 ; + req->pl_req.para.p_len = sizeof(struct smt_p_0017) - PARA_LEN ; + req->pl_req.sba_pl_req = smc->mib.fddiESSPayload - + smc->mib.a[PATH0].fddiPATHSbaPayload ; + + /* set P0018 */ + req->ov_req.para.p_type = SMT_P0018 ; + req->ov_req.para.p_len = sizeof(struct smt_p_0018) - PARA_LEN ; + req->ov_req.sba_ov_req = smc->mib.fddiESSOverhead - + smc->mib.a[PATH0].fddiPATHSbaOverhead ; + + /* set P320F */ + req->payload.para.p_type = SMT_P320F ; + req->payload.para.p_len = sizeof(struct smt_p_320f) - PARA_LEN ; + req->payload.mib_index = SBAPATHINDEX ; + req->payload.mib_payload = smc->mib.a[PATH0].fddiPATHSbaPayload ; + + /* set P3210 */ + req->overhead.para.p_type = SMT_P3210 ; + req->overhead.para.p_len = sizeof(struct smt_p_3210) - PARA_LEN ; + req->overhead.mib_index = SBAPATHINDEX ; + req->overhead.mib_overhead = smc->mib.a[PATH0].fddiPATHSbaOverhead ; + + /* set P19 */ + req->a_addr.para.p_type = SMT_P0019 ; + req->a_addr.para.p_len = sizeof(struct smt_p_0019) - PARA_LEN ; + req->a_addr.sba_pad = (u_short)NULL ; + req->a_addr.alloc_addr = null_addr ; + + /* set P1A */ + req->cat.para.p_type = SMT_P001A ; + req->cat.para.p_len = sizeof(struct smt_p_001a) - PARA_LEN ; + req->cat.category = smc->mib.fddiESSCategory ; + + /* set P1B */ + req->tneg.para.p_type = SMT_P001B ; + req->tneg.para.p_len = sizeof(struct smt_p_001b) - PARA_LEN ; + req->tneg.max_t_neg = smc->mib.fddiESSMaxTNeg ; + + /* set P1C */ + req->segm.para.p_type = SMT_P001C ; + req->segm.para.p_len = sizeof(struct smt_p_001c) - PARA_LEN ; + req->segm.min_seg_siz = smc->mib.fddiESSMinSegmentSize ; + + dump_smt(smc,(struct smt_header *)req,"RAF") ; + ess_send_frame(smc,mb) ; +} + +static void ess_send_frame(smc,mb) +struct s_smc *smc ; +SMbuf *mb ; +{ + /* + * check if the frame must be send to the own ESS + */ + if (smc->ess.local_sba_active) { + /* + * Send the Change Reply to the local SBA + */ + DB_ESS("ESS:Send to the local SBA\n",0,0) ; + if (!smc->ess.sba_reply_pend) + smc->ess.sba_reply_pend = mb ; + else { + DB_ESS("Frame is lost - another frame was pending\n",0,0); + smt_free_mbuf(smc,mb) ; + } + } + else { + /* + * Send the SBA RAF Change Reply to the network + */ + DB_ESS("ESS:Send to the network\n",0,0) ; + smt_send_frame(smc,mb,FC_SMT_INFO,0) ; + } +} + +void ess_para_change(smc) +struct s_smc *smc ; +{ + (void)process_bw_alloc(smc,(long)smc->mib.a[PATH0].fddiPATHSbaPayload, + (long)smc->mib.a[PATH0].fddiPATHSbaOverhead) ; +} + +static void ess_config_fifo(smc) +struct s_smc *smc ; +{ + /* + * if nothing to do exit + */ + if (smc->mib.a[PATH0].fddiPATHSbaPayload) { + if (smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON && + (smc->hw.fp.fifo.fifo_config_mode&SEND_ASYNC_AS_SYNC) == + smc->mib.fddiESSSynchTxMode) { + return ; + } + } + else { + if (!(smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON)) { + return ; + } + } + + /* + * split up the FIFO and reinitialize the queues + */ + formac_reinit_tx(smc) ; +} + +#endif /* ESS */ + +#endif /* no SLIM_SMT */ diff --git a/drivers/net/skfp/fplustm.c b/drivers/net/skfp/fplustm.c new file mode 100644 index 000000000000..4d816c58483e --- /dev/null +++ b/drivers/net/skfp/fplustm.c @@ -0,0 +1,1645 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * FORMAC+ Driver for tag mode + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/supern_2.h" +#include "can.c" + +#ifndef lint +static const char ID_sccs[] = "@(#)fplustm.c 1.32 99/02/23 (C) SK " ; +#endif + +#ifndef UNUSED +#ifdef lint +#define UNUSED(x) (x) = (x) +#else +#define UNUSED(x) +#endif +#endif + +#define FM_ADDRX (FM_ADDET|FM_EXGPA0|FM_EXGPA1) +#define MS2BCLK(x) ((x)*12500L) +#define US2BCLK(x) ((x)*1250L) + +/* + * prototypes for static function + */ +static void build_claim_beacon() ; +static int init_mac() ; +static void rtm_init() ; +static void smt_split_up_fifo() ; + +#if (!defined(NO_SMT_PANIC) || defined(DEBUG)) +static char write_mdr_warning [] = "E350 write_mdr() FM_SNPPND is set\n"; +static char cam_warning [] = "E_SMT_004: CAM still busy\n"; +#endif + +#define DUMMY_READ() smc->hw.mc_dummy = (u_short) inp(ADDR(B0_RAP)) + +#define CHECK_NPP() { unsigned k = 10000 ;\ + while ((inpw(FM_A(FM_STMCHN)) & FM_SNPPND) && k) k--;\ + if (!k) { \ + SMT_PANIC(smc,SMT_E0130, SMT_E0130_MSG) ; \ + } \ + } + +#define CHECK_CAM() { unsigned k = 10 ;\ + while (!(inpw(FM_A(FM_AFSTAT)) & FM_DONE) && k) k--;\ + if (!k) { \ + SMT_PANIC(smc,SMT_E0131, SMT_E0131_MSG) ; \ + } \ + } + +const struct fddi_addr fddi_broadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}}; +static const struct fddi_addr null_addr = {{0,0,0,0,0,0}}; +static const struct fddi_addr dbeacon_multi = {{0x01,0x80,0xc2,0x00,0x01,0x00}}; + +static const u_short my_said = 0xffff ; /* short address (n.u.) */ +static const u_short my_sagp = 0xffff ; /* short group address (n.u.) */ + +/* + * define my address + */ +#ifdef USE_CAN_ADDR +#define MA smc->hw.fddi_canon_addr +#else +#define MA smc->hw.fddi_home_addr +#endif + + +/* + * usefull interrupt bits + */ +static int mac_imsk1u = FM_STXABRS | FM_STXABRA0 | FM_SXMTABT ; +static int mac_imsk1l = FM_SQLCKS | FM_SQLCKA0 | FM_SPCEPDS | FM_SPCEPDA0| + FM_STBURS | FM_STBURA0 ; + + /* delete FM_SRBFL after tests */ +static int mac_imsk2u = FM_SERRSF | FM_SNFSLD | FM_SRCVOVR | FM_SRBFL | + FM_SMYCLM ; +static int mac_imsk2l = FM_STRTEXR | FM_SDUPCLM | FM_SFRMCTR | + FM_SERRCTR | FM_SLSTCTR | + FM_STRTEXP | FM_SMULTDA | FM_SRNGOP ; + +static int mac_imsk3u = FM_SRCVOVR2 | FM_SRBFL2 ; +static int mac_imsk3l = FM_SRPERRQ2 | FM_SRPERRQ1 ; + +static int mac_beacon_imsk2u = FM_SOTRBEC | FM_SMYBEC | FM_SBEC | + FM_SLOCLM | FM_SHICLM | FM_SMYCLM | FM_SCLM ; + + +static u_long mac_get_tneg(smc) +struct s_smc *smc ; +{ + u_long tneg ; + + tneg = (u_long)((long)inpw(FM_A(FM_TNEG))<<5) ; + return((u_long)((tneg + ((inpw(FM_A(FM_TMRS))>>10)&0x1f)) | + 0xffe00000L)) ; +} + +void mac_update_counter(smc) +struct s_smc *smc ; +{ + smc->mib.m[MAC0].fddiMACFrame_Ct = + (smc->mib.m[MAC0].fddiMACFrame_Ct & 0xffff0000L) + + (u_short) inpw(FM_A(FM_FCNTR)) ; + smc->mib.m[MAC0].fddiMACLost_Ct = + (smc->mib.m[MAC0].fddiMACLost_Ct & 0xffff0000L) + + (u_short) inpw(FM_A(FM_LCNTR)) ; + smc->mib.m[MAC0].fddiMACError_Ct = + (smc->mib.m[MAC0].fddiMACError_Ct & 0xffff0000L) + + (u_short) inpw(FM_A(FM_ECNTR)) ; + smc->mib.m[MAC0].fddiMACT_Neg = mac_get_tneg(smc) ; +#ifdef SMT_REAL_TOKEN_CT + /* + * If the token counter is emulated it is updated in smt_event. + */ + TBD +#else + smt_emulate_token_ct( smc, MAC0 ); +#endif +} + +/* + * write long value into buffer memory over memory data register (MDR), + */ +void write_mdr(smc,val) +struct s_smc *smc ; +u_long val; +{ + CHECK_NPP() ; + MDRW(val) ; +} + +/* + * read long value from buffer memory over memory data register (MDR), + */ +u_long read_mdr(smc,addr) +struct s_smc *smc ; +unsigned int addr; +{ + long p ; + CHECK_NPP() ; + MARR(addr) ; + outpw(FM_A(FM_CMDREG1),FM_IRMEMWO) ; + CHECK_NPP() ; /* needed for PCI to prevent from timeing violations */ +/* p = MDRR() ; */ /* bad read values if the workaround */ + /* smc->hw.mc_dummy = *((short volatile far *)(addr)))*/ + /* is used */ + p = (u_long)inpw(FM_A(FM_MDRU))<<16 ; + p += (u_long)inpw(FM_A(FM_MDRL)) ; + return(p) ; +} +/* + * clear buffer memory + */ +static void init_ram(smc) +struct s_smc *smc ; +{ + u_short i ; + + smc->hw.fp.fifo.rbc_ram_start = 0 ; + smc->hw.fp.fifo.rbc_ram_end = + smc->hw.fp.fifo.rbc_ram_start + RBC_MEM_SIZE ; + CHECK_NPP() ; + MARW(smc->hw.fp.fifo.rbc_ram_start) ; + for (i = smc->hw.fp.fifo.rbc_ram_start; + i < (u_short) (smc->hw.fp.fifo.rbc_ram_end-1); i++) + write_mdr(smc,0L) ; + /* Erase the last byte too */ + write_mdr(smc,0L) ; +} + +/* + * set receive FIFO pointer + */ +static void set_recvptr(smc) +struct s_smc *smc ; +{ + /* + * initialize the pointer for receive queue 1 + */ + outpw(FM_A(FM_RPR1),smc->hw.fp.fifo.rx1_fifo_start) ; /* RPR1 */ + outpw(FM_A(FM_SWPR1),smc->hw.fp.fifo.rx1_fifo_start) ; /* SWPR1 */ + outpw(FM_A(FM_WPR1),smc->hw.fp.fifo.rx1_fifo_start) ; /* WPR1 */ + outpw(FM_A(FM_EARV1),smc->hw.fp.fifo.tx_s_start-1) ; /* EARV1 */ + + /* + * initialize the pointer for receive queue 2 + */ + if (smc->hw.fp.fifo.rx2_fifo_size) { + outpw(FM_A(FM_RPR2),smc->hw.fp.fifo.rx2_fifo_start) ; + outpw(FM_A(FM_SWPR2),smc->hw.fp.fifo.rx2_fifo_start) ; + outpw(FM_A(FM_WPR2),smc->hw.fp.fifo.rx2_fifo_start) ; + outpw(FM_A(FM_EARV2),smc->hw.fp.fifo.rbc_ram_end-1) ; + } + else { + outpw(FM_A(FM_RPR2),smc->hw.fp.fifo.rbc_ram_end-1) ; + outpw(FM_A(FM_SWPR2),smc->hw.fp.fifo.rbc_ram_end-1) ; + outpw(FM_A(FM_WPR2),smc->hw.fp.fifo.rbc_ram_end-1) ; + outpw(FM_A(FM_EARV2),smc->hw.fp.fifo.rbc_ram_end-1) ; + } +} + +/* + * set transmit FIFO pointer + */ +static void set_txptr(smc) +struct s_smc *smc ; +{ + outpw(FM_A(FM_CMDREG2),FM_IRSTQ) ; /* reset transmit queues */ + + /* + * initialize the pointer for asynchronous transmit queue + */ + outpw(FM_A(FM_RPXA0),smc->hw.fp.fifo.tx_a0_start) ; /* RPXA0 */ + outpw(FM_A(FM_SWPXA0),smc->hw.fp.fifo.tx_a0_start) ; /* SWPXA0 */ + outpw(FM_A(FM_WPXA0),smc->hw.fp.fifo.tx_a0_start) ; /* WPXA0 */ + outpw(FM_A(FM_EAA0),smc->hw.fp.fifo.rx2_fifo_start-1) ; /* EAA0 */ + + /* + * initialize the pointer for synchronous transmit queue + */ + if (smc->hw.fp.fifo.tx_s_size) { + outpw(FM_A(FM_RPXS),smc->hw.fp.fifo.tx_s_start) ; + outpw(FM_A(FM_SWPXS),smc->hw.fp.fifo.tx_s_start) ; + outpw(FM_A(FM_WPXS),smc->hw.fp.fifo.tx_s_start) ; + outpw(FM_A(FM_EAS),smc->hw.fp.fifo.tx_a0_start-1) ; + } + else { + outpw(FM_A(FM_RPXS),smc->hw.fp.fifo.tx_a0_start-1) ; + outpw(FM_A(FM_SWPXS),smc->hw.fp.fifo.tx_a0_start-1) ; + outpw(FM_A(FM_WPXS),smc->hw.fp.fifo.tx_a0_start-1) ; + outpw(FM_A(FM_EAS),smc->hw.fp.fifo.tx_a0_start-1) ; + } +} + +/* + * init memory buffer management registers + */ +static void init_rbc(smc) +struct s_smc *smc ; +{ + u_short rbc_ram_addr ; + + /* + * set unused pointers or permanent pointers + */ + rbc_ram_addr = smc->hw.fp.fifo.rx2_fifo_start - 1 ; + + outpw(FM_A(FM_RPXA1),rbc_ram_addr) ; /* a1-send pointer */ + outpw(FM_A(FM_WPXA1),rbc_ram_addr) ; + outpw(FM_A(FM_SWPXA1),rbc_ram_addr) ; + outpw(FM_A(FM_EAA1),rbc_ram_addr) ; + + set_recvptr(smc) ; + set_txptr(smc) ; +} + +/* + * init rx pointer + */ +static void init_rx(smc) +struct s_smc *smc ; +{ + struct s_smt_rx_queue *queue ; + + /* + * init all tx data structures for receive queue 1 + */ + smc->hw.fp.rx[QUEUE_R1] = queue = &smc->hw.fp.rx_q[QUEUE_R1] ; + queue->rx_bmu_ctl = (HW_PTR) ADDR(B0_R1_CSR) ; + queue->rx_bmu_dsc = (HW_PTR) ADDR(B4_R1_DA) ; + + /* + * init all tx data structures for receive queue 2 + */ + smc->hw.fp.rx[QUEUE_R2] = queue = &smc->hw.fp.rx_q[QUEUE_R2] ; + queue->rx_bmu_ctl = (HW_PTR) ADDR(B0_R2_CSR) ; + queue->rx_bmu_dsc = (HW_PTR) ADDR(B4_R2_DA) ; +} + +/* + * set the TSYNC register of the FORMAC to regulate synchronous transmission + */ +void set_formac_tsync(smc,sync_bw) +struct s_smc *smc ; +long sync_bw ; +{ + outpw(FM_A(FM_TSYNC),(unsigned int) (((-sync_bw) >> 5) & 0xffff) ) ; +} + +/* + * init all tx data structures + */ +static void init_tx(smc) +struct s_smc *smc ; +{ + struct s_smt_tx_queue *queue ; + + /* + * init all tx data structures for the synchronous queue + */ + smc->hw.fp.tx[QUEUE_S] = queue = &smc->hw.fp.tx_q[QUEUE_S] ; + queue->tx_bmu_ctl = (HW_PTR) ADDR(B0_XS_CSR) ; + queue->tx_bmu_dsc = (HW_PTR) ADDR(B5_XS_DA) ; + +#ifdef ESS + set_formac_tsync(smc,smc->ess.sync_bw) ; +#endif + + /* + * init all tx data structures for the asynchronous queue 0 + */ + smc->hw.fp.tx[QUEUE_A0] = queue = &smc->hw.fp.tx_q[QUEUE_A0] ; + queue->tx_bmu_ctl = (HW_PTR) ADDR(B0_XA_CSR) ; + queue->tx_bmu_dsc = (HW_PTR) ADDR(B5_XA_DA) ; + + + llc_recover_tx(smc) ; +} + +static void mac_counter_init(smc) +struct s_smc *smc ; +{ + int i ; + u_long *ec ; + + /* + * clear FORMAC+ frame-, lost- and error counter + */ + outpw(FM_A(FM_FCNTR),0) ; + outpw(FM_A(FM_LCNTR),0) ; + outpw(FM_A(FM_ECNTR),0) ; + /* + * clear internal error counter stucture + */ + ec = (u_long *)&smc->hw.fp.err_stats ; + for (i = (sizeof(struct err_st)/sizeof(long)) ; i ; i--) + *ec++ = 0L ; + smc->mib.m[MAC0].fddiMACRingOp_Ct = 0 ; +} + +/* + * set FORMAC address, and t_request + */ +static void set_formac_addr(smc) +struct s_smc *smc ; +{ + long t_requ = smc->mib.m[MAC0].fddiMACT_Req ; + + outpw(FM_A(FM_SAID),my_said) ; /* set short address */ + outpw(FM_A(FM_LAIL),(unsigned)((smc->hw.fddi_home_addr.a[4]<<8) + + smc->hw.fddi_home_addr.a[5])) ; + outpw(FM_A(FM_LAIC),(unsigned)((smc->hw.fddi_home_addr.a[2]<<8) + + smc->hw.fddi_home_addr.a[3])) ; + outpw(FM_A(FM_LAIM),(unsigned)((smc->hw.fddi_home_addr.a[0]<<8) + + smc->hw.fddi_home_addr.a[1])) ; + + outpw(FM_A(FM_SAGP),my_sagp) ; /* set short group address */ + + outpw(FM_A(FM_LAGL),(unsigned)((smc->hw.fp.group_addr.a[4]<<8) + + smc->hw.fp.group_addr.a[5])) ; + outpw(FM_A(FM_LAGC),(unsigned)((smc->hw.fp.group_addr.a[2]<<8) + + smc->hw.fp.group_addr.a[3])) ; + outpw(FM_A(FM_LAGM),(unsigned)((smc->hw.fp.group_addr.a[0]<<8) + + smc->hw.fp.group_addr.a[1])) ; + + /* set r_request regs. (MSW & LSW of TRT ) */ + outpw(FM_A(FM_TREQ1),(unsigned)(t_requ>>16)) ; + outpw(FM_A(FM_TREQ0),(unsigned)t_requ) ; +} + +void set_int(p,l) +char *p; +int l; +{ + p[0] = (char)(l >> 24) ; + p[1] = (char)(l >> 16) ; + p[2] = (char)(l >> 8) ; + p[3] = (char)(l >> 0) ; +} + +/* + * copy TX descriptor to buffer mem + * append FC field and MAC frame + * if more bit is set in descr + * append pointer to descriptor (endless loop) + * else + * append 'end of chain' pointer + */ +static void copy_tx_mac(smc,td,mac,off,len) +struct s_smc *smc ; +u_long td; /* transmit descriptor */ +struct fddi_mac *mac; /* mac frame pointer */ +unsigned off; /* start address within buffer memory */ +int len ; /* lenght of the frame including the FC */ +{ + int i ; + u_int *p ; + + CHECK_NPP() ; + MARW(off) ; /* set memory address reg for writes */ + + p = (u_int *) mac ; + for (i = (len + 3)/4 ; i ; i--) { + if (i == 1) { + /* last word, set the tag bit */ + outpw(FM_A(FM_CMDREG2),FM_ISTTB) ; + } + write_mdr(smc,MDR_REVERSE(*p)) ; + p++ ; + } + + outpw(FM_A(FM_CMDREG2),FM_ISTTB) ; /* set the tag bit */ + write_mdr(smc,td) ; /* write over memory data reg to buffer */ +} + +/* + BEGIN_MANUAL_ENTRY(module;tests;3) + How to test directed beacon frames + ---------------------------------------------------------------- + + o Insert a break point in the function build_claim_beacon() + before calling copy_tx_mac() for building the claim frame. + o Modify the RM3_DETECT case so that the RM6_DETECT state + will always entered from the RM3_DETECT state (function rmt_fsm(), + rmt.c) + o Compile the driver. + o Set the parameter TREQ in the protocol.ini or net.cfg to a + small value to make sure your station will win the claim + process. + o Start the driver. + o When you reach the break point, modify the SA and DA address + of the claim frame (e.g. SA = DA = 10005affffff). + o When you see RM3_DETECT and RM6_DETECT, observe the direct + beacon frames on the UPPSLANA. + + END_MANUAL_ENTRY + */ +static void directed_beacon(smc) +struct s_smc *smc ; +{ + SK_LOC_DECL(u_int,a[2]) ; + + /* + * set UNA in frame + * enable FORMAC to send endless queue of directed beacon + * important: the UNA starts at byte 1 (not at byte 0) + */ + * (char *) a = (char) ((long)DBEACON_INFO<<24L) ; + a[1] = 0 ; + memcpy((char *)a+1,(char *) &smc->mib.m[MAC0].fddiMACUpstreamNbr,6) ; + + CHECK_NPP() ; + /* set memory address reg for writes */ + MARW(smc->hw.fp.fifo.rbc_ram_start+DBEACON_FRAME_OFF+4) ; + write_mdr(smc,MDR_REVERSE(a[0])) ; + outpw(FM_A(FM_CMDREG2),FM_ISTTB) ; /* set the tag bit */ + write_mdr(smc,MDR_REVERSE(a[1])) ; + + outpw(FM_A(FM_SABC),smc->hw.fp.fifo.rbc_ram_start + DBEACON_FRAME_OFF) ; +} + +/* + setup claim & beacon pointer + NOTE : + special frame packets end with a pointer to their own + descriptor, and the MORE bit is set in the descriptor +*/ +static void build_claim_beacon(smc,t_request) +struct s_smc *smc ; +u_long t_request; +{ + u_int td ; + int len ; + struct fddi_mac_sf *mac ; + + /* + * build claim packet + */ + len = 17 ; + td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ; + mac = &smc->hw.fp.mac_sfb ; + mac->mac_fc = FC_CLAIM ; + /* DA == SA in claim frame */ + mac->mac_source = mac->mac_dest = MA ; + /* 2's complement */ + set_int((char *)mac->mac_info,(int)t_request) ; + + copy_tx_mac(smc,td,(struct fddi_mac *)mac, + smc->hw.fp.fifo.rbc_ram_start + CLAIM_FRAME_OFF,len) ; + /* set CLAIM start pointer */ + outpw(FM_A(FM_SACL),smc->hw.fp.fifo.rbc_ram_start + CLAIM_FRAME_OFF) ; + + /* + * build beacon packet + */ + len = 17 ; + td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ; + mac->mac_fc = FC_BEACON ; + mac->mac_source = MA ; + mac->mac_dest = null_addr ; /* DA == 0 in beacon frame */ + set_int((char *) mac->mac_info,((int)BEACON_INFO<<24) + 0 ) ; + + copy_tx_mac(smc,td,(struct fddi_mac *)mac, + smc->hw.fp.fifo.rbc_ram_start + BEACON_FRAME_OFF,len) ; + /* set beacon start pointer */ + outpw(FM_A(FM_SABC),smc->hw.fp.fifo.rbc_ram_start + BEACON_FRAME_OFF) ; + + /* + * build directed beacon packet + * contains optional UNA + */ + len = 23 ; + td = TX_DESCRIPTOR | ((((u_int)len-1)&3)<<27) ; + mac->mac_fc = FC_BEACON ; + mac->mac_source = MA ; + mac->mac_dest = dbeacon_multi ; /* multicast */ + set_int((char *) mac->mac_info,((int)DBEACON_INFO<<24) + 0 ) ; + set_int((char *) mac->mac_info+4,0) ; + set_int((char *) mac->mac_info+8,0) ; + + copy_tx_mac(smc,td,(struct fddi_mac *)mac, + smc->hw.fp.fifo.rbc_ram_start + DBEACON_FRAME_OFF,len) ; + + /* end of claim/beacon queue */ + outpw(FM_A(FM_EACB),smc->hw.fp.fifo.rx1_fifo_start-1) ; + + outpw(FM_A(FM_WPXSF),0) ; + outpw(FM_A(FM_RPXSF),0) ; +} + +void formac_rcv_restart(smc) +struct s_smc *smc ; +{ + /* enable receive function */ + SETMASK(FM_A(FM_MDREG1),smc->hw.fp.rx_mode,FM_ADDRX) ; + + outpw(FM_A(FM_CMDREG1),FM_ICLLR) ; /* clear receive lock */ +} + +void formac_tx_restart(smc) +struct s_smc *smc ; +{ + outpw(FM_A(FM_CMDREG1),FM_ICLLS) ; /* clear s-frame lock */ + outpw(FM_A(FM_CMDREG1),FM_ICLLA0) ; /* clear a-frame lock */ +} + +static void enable_formac(smc) +struct s_smc *smc ; +{ + /* set formac IMSK : 0 enables irq */ + outpw(FM_A(FM_IMSK1U),~mac_imsk1u) ; + outpw(FM_A(FM_IMSK1L),~mac_imsk1l) ; + outpw(FM_A(FM_IMSK2U),~mac_imsk2u) ; + outpw(FM_A(FM_IMSK2L),~mac_imsk2l) ; + outpw(FM_A(FM_IMSK3U),~mac_imsk3u) ; + outpw(FM_A(FM_IMSK3L),~mac_imsk3l) ; +} + +#if 0 /* Removed because the driver should use the ASICs TX complete IRQ. */ + /* The FORMACs tx complete IRQ should be used any longer */ + +/* + BEGIN_MANUAL_ENTRY(if,func;others;4) + + void enable_tx_irq(smc, queue) + struct s_smc *smc ; + u_short queue ; + +Function DOWNCALL (SMT, fplustm.c) + enable_tx_irq() enables the FORMACs transmit complete + interrupt of the queue. + +Para queue = QUEUE_S: synchronous queue + = QUEUE_A0: asynchronous queue + +Note After any ring operational change the transmit complete + interrupts are disabled. + The operating system dependent module must enable + the transmit complete interrupt of a queue, + - when it queues the first frame, + because of no transmit resources are beeing + available and + - when it escapes from the function llc_restart_tx + while some frames are still queued. + + END_MANUAL_ENTRY + */ +void enable_tx_irq(smc, queue) +struct s_smc *smc ; +u_short queue ; /* 0 = synchronous queue, 1 = asynchronous queue 0 */ +{ + u_short imask ; + + imask = ~(inpw(FM_A(FM_IMSK1U))) ; + + if (queue == 0) { + outpw(FM_A(FM_IMSK1U),~(imask|FM_STEFRMS)) ; + } + if (queue == 1) { + outpw(FM_A(FM_IMSK1U),~(imask|FM_STEFRMA0)) ; + } +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;4) + + void disable_tx_irq(smc, queue) + struct s_smc *smc ; + u_short queue ; + +Function DOWNCALL (SMT, fplustm.c) + disable_tx_irq disables the FORMACs transmit complete + interrupt of the queue + +Para queue = QUEUE_S: synchronous queue + = QUEUE_A0: asynchronous queue + +Note The operating system dependent module should disable + the transmit complete interrupts if it escapes from the + function llc_restart_tx and no frames are queued. + + END_MANUAL_ENTRY + */ +void disable_tx_irq(smc, queue) +struct s_smc *smc ; +u_short queue ; /* 0 = synchronous queue, 1 = asynchronous queue 0 */ +{ + u_short imask ; + + imask = ~(inpw(FM_A(FM_IMSK1U))) ; + + if (queue == 0) { + outpw(FM_A(FM_IMSK1U),~(imask&~FM_STEFRMS)) ; + } + if (queue == 1) { + outpw(FM_A(FM_IMSK1U),~(imask&~FM_STEFRMA0)) ; + } +} +#endif + +static void disable_formac(smc) +struct s_smc *smc ; +{ + /* clear formac IMSK : 1 disables irq */ + outpw(FM_A(FM_IMSK1U),MW) ; + outpw(FM_A(FM_IMSK1L),MW) ; + outpw(FM_A(FM_IMSK2U),MW) ; + outpw(FM_A(FM_IMSK2L),MW) ; + outpw(FM_A(FM_IMSK3U),MW) ; + outpw(FM_A(FM_IMSK3L),MW) ; +} + + +static void mac_ring_up(smc,up) +struct s_smc *smc ; +int up; +{ + if (up) { + formac_rcv_restart(smc) ; /* enable receive function */ + smc->hw.mac_ring_is_up = TRUE ; + llc_restart_tx(smc) ; /* TX queue */ + } + else { + /* disable receive function */ + SETMASK(FM_A(FM_MDREG1),FM_MDISRCV,FM_ADDET) ; + + /* abort current transmit activity */ + outpw(FM_A(FM_CMDREG2),FM_IACTR) ; + + smc->hw.mac_ring_is_up = FALSE ; + } +} + +/*--------------------------- ISR handling ----------------------------------*/ +/* + * mac1_irq is in drvfbi.c + */ + +/* + * mac2_irq: status bits for the receive queue 1, and ring status + * ring status indication bits + */ +void mac2_irq(smc,code_s2u,code_s2l) +struct s_smc *smc ; +u_short code_s2u ; +u_short code_s2l ; +{ + u_short change_s2l ; + u_short change_s2u ; + + /* (jd) 22-Feb-1999 + * Restart 2_DMax Timer after end of claiming or beaconing + */ + if (code_s2u & (FM_SCLM|FM_SHICLM|FM_SBEC|FM_SOTRBEC)) { + queue_event(smc,EVENT_RMT,RM_TX_STATE_CHANGE) ; + } + else if (code_s2l & (FM_STKISS)) { + queue_event(smc,EVENT_RMT,RM_TX_STATE_CHANGE) ; + } + + /* + * XOR current st bits with the last to avoid useless RMT event queuing + */ + change_s2l = smc->hw.fp.s2l ^ code_s2l ; + change_s2u = smc->hw.fp.s2u ^ code_s2u ; + + if ((change_s2l & FM_SRNGOP) || + (!smc->hw.mac_ring_is_up && ((code_s2l & FM_SRNGOP)))) { + if (code_s2l & FM_SRNGOP) { + mac_ring_up(smc,1) ; + queue_event(smc,EVENT_RMT,RM_RING_OP) ; + smc->mib.m[MAC0].fddiMACRingOp_Ct++ ; + } + else { + mac_ring_up(smc,0) ; + queue_event(smc,EVENT_RMT,RM_RING_NON_OP) ; + } + goto mac2_end ; + } + if (code_s2l & FM_SMISFRM) { /* missed frame */ + smc->mib.m[MAC0].fddiMACNotCopied_Ct++ ; + } + if (code_s2u & (FM_SRCVOVR | /* recv. FIFO overflow */ + FM_SRBFL)) { /* recv. buffer full */ + smc->hw.mac_ct.mac_r_restart_counter++ ; +/* formac_rcv_restart(smc) ; */ + smt_stat_counter(smc,1) ; +/* goto mac2_end ; */ + } + if (code_s2u & FM_SOTRBEC) + queue_event(smc,EVENT_RMT,RM_OTHER_BEACON) ; + if (code_s2u & FM_SMYBEC) + queue_event(smc,EVENT_RMT,RM_MY_BEACON) ; + if (change_s2u & code_s2u & FM_SLOCLM) { + DB_RMTN(2,"RMT : lower claim received\n",0,0) ; + } + if ((code_s2u & FM_SMYCLM) && !(code_s2l & FM_SDUPCLM)) { + /* + * This is my claim and that claim is not detected as a + * duplicate one. + */ + queue_event(smc,EVENT_RMT,RM_MY_CLAIM) ; + } + if (code_s2l & FM_SDUPCLM) { + /* + * If a duplicate claim frame (same SA but T_Bid != T_Req) + * this flag will be set. + * In the RMT state machine we need a RM_VALID_CLAIM event + * to do the appropriate state change. + * RM(34c) + */ + queue_event(smc,EVENT_RMT,RM_VALID_CLAIM) ; + } + if (change_s2u & code_s2u & FM_SHICLM) { + DB_RMTN(2,"RMT : higher claim received\n",0,0) ; + } + if ( (code_s2l & FM_STRTEXP) || + (code_s2l & FM_STRTEXR) ) + queue_event(smc,EVENT_RMT,RM_TRT_EXP) ; + if (code_s2l & FM_SMULTDA) { + /* + * The MAC has found a 2. MAC with the same address. + * Signal dup_addr_test = failed to RMT state machine. + * RM(25) + */ + smc->r.dup_addr_test = DA_FAILED ; + queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ; + } + if (code_s2u & FM_SBEC) + smc->hw.fp.err_stats.err_bec_stat++ ; + if (code_s2u & FM_SCLM) + smc->hw.fp.err_stats.err_clm_stat++ ; + if (code_s2l & FM_STVXEXP) + smc->mib.m[MAC0].fddiMACTvxExpired_Ct++ ; + if ((code_s2u & (FM_SBEC|FM_SCLM))) { + if (!(change_s2l & FM_SRNGOP) && (smc->hw.fp.s2l & FM_SRNGOP)) { + mac_ring_up(smc,0) ; + queue_event(smc,EVENT_RMT,RM_RING_NON_OP) ; + + mac_ring_up(smc,1) ; + queue_event(smc,EVENT_RMT,RM_RING_OP) ; + smc->mib.m[MAC0].fddiMACRingOp_Ct++ ; + } + } + if (code_s2l & FM_SPHINV) + smc->hw.fp.err_stats.err_phinv++ ; + if (code_s2l & FM_SSIFG) + smc->hw.fp.err_stats.err_sifg_det++ ; + if (code_s2l & FM_STKISS) + smc->hw.fp.err_stats.err_tkiss++ ; + if (code_s2l & FM_STKERR) + smc->hw.fp.err_stats.err_tkerr++ ; + if (code_s2l & FM_SFRMCTR) + smc->mib.m[MAC0].fddiMACFrame_Ct += 0x10000L ; + if (code_s2l & FM_SERRCTR) + smc->mib.m[MAC0].fddiMACError_Ct += 0x10000L ; + if (code_s2l & FM_SLSTCTR) + smc->mib.m[MAC0].fddiMACLost_Ct += 0x10000L ; + if (code_s2u & FM_SERRSF) { + SMT_PANIC(smc,SMT_E0114, SMT_E0114_MSG) ; + } +mac2_end: + /* notice old status */ + smc->hw.fp.s2l = code_s2l ; + smc->hw.fp.s2u = code_s2u ; + outpw(FM_A(FM_IMSK2U),~mac_imsk2u) ; +} + +/* + * mac3_irq: receive queue 2 bits and address detection bits + */ +void mac3_irq(smc,code_s3u,code_s3l) +struct s_smc *smc ; +u_short code_s3u ; +u_short code_s3l ; +{ + UNUSED(code_s3l) ; + + if (code_s3u & (FM_SRCVOVR2 | /* recv. FIFO overflow */ + FM_SRBFL2)) { /* recv. buffer full */ + smc->hw.mac_ct.mac_r_restart_counter++ ; + smt_stat_counter(smc,1); + } + + + if (code_s3u & FM_SRPERRQ2) { /* parity error receive queue 2 */ + SMT_PANIC(smc,SMT_E0115, SMT_E0115_MSG) ; + } + if (code_s3u & FM_SRPERRQ1) { /* parity error receive queue 2 */ + SMT_PANIC(smc,SMT_E0116, SMT_E0116_MSG) ; + } +} + + +/* + * take formac offline + */ +static void formac_offline(smc) +struct s_smc *smc ; +{ + outpw(FM_A(FM_CMDREG2),FM_IACTR) ;/* abort current transmit activity */ + + /* disable receive function */ + SETMASK(FM_A(FM_MDREG1),FM_MDISRCV,FM_ADDET) ; + + /* FORMAC+ 'Initialize Mode' */ + SETMASK(FM_A(FM_MDREG1),FM_MINIT,FM_MMODE) ; + + disable_formac(smc) ; + smc->hw.mac_ring_is_up = FALSE ; + smc->hw.hw_state = STOPPED ; +} + +/* + * bring formac online + */ +static void formac_online(smc) +struct s_smc *smc ; +{ + enable_formac(smc) ; + SETMASK(FM_A(FM_MDREG1),FM_MONLINE | FM_SELRA | MDR1INIT | + smc->hw.fp.rx_mode, FM_MMODE | FM_SELRA | FM_ADDRX) ; +} + +/* + * FORMAC+ full init. (tx, rx, timer, counter, claim & beacon) + */ +int init_fplus(smc) +struct s_smc *smc ; +{ + smc->hw.fp.nsa_mode = FM_MRNNSAFNMA ; + smc->hw.fp.rx_mode = FM_MDAMA ; + smc->hw.fp.group_addr = fddi_broadcast ; + smc->hw.fp.func_addr = 0 ; + smc->hw.fp.frselreg_init = 0 ; + + init_driver_fplus(smc) ; + if (smc->s.sas == SMT_DAS) + smc->hw.fp.mdr3init |= FM_MENDAS ; + + smc->hw.mac_ct.mac_nobuf_counter = 0 ; + smc->hw.mac_ct.mac_r_restart_counter = 0 ; + + smc->hw.fp.fm_st1u = (HW_PTR) ADDR(B0_ST1U) ; + smc->hw.fp.fm_st1l = (HW_PTR) ADDR(B0_ST1L) ; + smc->hw.fp.fm_st2u = (HW_PTR) ADDR(B0_ST2U) ; + smc->hw.fp.fm_st2l = (HW_PTR) ADDR(B0_ST2L) ; + smc->hw.fp.fm_st3u = (HW_PTR) ADDR(B0_ST3U) ; + smc->hw.fp.fm_st3l = (HW_PTR) ADDR(B0_ST3L) ; + + smc->hw.fp.s2l = smc->hw.fp.s2u = 0 ; + smc->hw.mac_ring_is_up = 0 ; + + mac_counter_init(smc) ; + + /* convert BCKL units to symbol time */ + smc->hw.mac_pa.t_neg = (u_long)0 ; + smc->hw.mac_pa.t_pri = (u_long)0 ; + + /* make sure all PCI settings are correct */ + mac_do_pci_fix(smc) ; + + return(init_mac(smc,1)) ; + /* enable_formac(smc) ; */ +} + +static int init_mac(smc,all) +struct s_smc *smc ; +int all ; +{ + u_short t_max,x ; + u_long time=0 ; + + /* + * clear memory + */ + outpw(FM_A(FM_MDREG1),FM_MINIT) ; /* FORMAC+ init mode */ + set_formac_addr(smc) ; + outpw(FM_A(FM_MDREG1),FM_MMEMACT) ; /* FORMAC+ memory activ mode */ + /* Note: Mode register 2 is set here, incase parity is enabled. */ + outpw(FM_A(FM_MDREG2),smc->hw.fp.mdr2init) ; + + if (all) { + init_ram(smc) ; + } + else { + /* + * reset the HPI, the Master and the BMUs + */ + outp(ADDR(B0_CTRL), CTRL_HPI_SET) ; + time = hwt_quick_read(smc) ; + } + + /* + * set all pointers, frames etc + */ + smt_split_up_fifo(smc) ; + + init_tx(smc) ; + init_rx(smc) ; + init_rbc(smc) ; + + build_claim_beacon(smc,smc->mib.m[MAC0].fddiMACT_Req) ; + + /* set RX threshold */ + /* see Errata #SN2 Phantom receive overflow */ + outpw(FM_A(FM_FRMTHR),14<<12) ; /* switch on */ + + /* set formac work mode */ + outpw(FM_A(FM_MDREG1),MDR1INIT | FM_SELRA | smc->hw.fp.rx_mode) ; + outpw(FM_A(FM_MDREG2),smc->hw.fp.mdr2init) ; + outpw(FM_A(FM_MDREG3),smc->hw.fp.mdr3init) ; + outpw(FM_A(FM_FRSELREG),smc->hw.fp.frselreg_init) ; + + /* set timer */ + /* + * errata #22 fplus: + * T_MAX must not be FFFE + * or one of FFDF, FFB8, FF91 (-0x27 etc..) + */ + t_max = (u_short)(smc->mib.m[MAC0].fddiMACT_Max/32) ; + x = t_max/0x27 ; + x *= 0x27 ; + if ((t_max == 0xfffe) || (t_max - x == 0x16)) + t_max-- ; + outpw(FM_A(FM_TMAX),(u_short)t_max) ; + + /* BugFix for report #10204 */ + if (smc->mib.m[MAC0].fddiMACTvxValue < (u_long) (- US2BCLK(52))) { + outpw(FM_A(FM_TVX), (u_short) (- US2BCLK(52))/255 & MB) ; + } else { + outpw(FM_A(FM_TVX), + (u_short)((smc->mib.m[MAC0].fddiMACTvxValue/255) & MB)) ; + } + + outpw(FM_A(FM_CMDREG1),FM_ICLLS) ; /* clear s-frame lock */ + outpw(FM_A(FM_CMDREG1),FM_ICLLA0) ; /* clear a-frame lock */ + outpw(FM_A(FM_CMDREG1),FM_ICLLR); /* clear receive lock */ + + /* Auto unlock receice threshold for receive queue 1 and 2 */ + outpw(FM_A(FM_UNLCKDLY),(0xff|(0xff<<8))) ; + + rtm_init(smc) ; /* RT-Monitor */ + + if (!all) { + /* + * after 10ms, reset the BMUs and repair the rings + */ + hwt_wait_time(smc,time,MS2BCLK(10)) ; + outpd(ADDR(B0_R1_CSR),CSR_SET_RESET) ; + outpd(ADDR(B0_XA_CSR),CSR_SET_RESET) ; + outpd(ADDR(B0_XS_CSR),CSR_SET_RESET) ; + outp(ADDR(B0_CTRL), CTRL_HPI_CLR) ; + outpd(ADDR(B0_R1_CSR),CSR_CLR_RESET) ; + outpd(ADDR(B0_XA_CSR),CSR_CLR_RESET) ; + outpd(ADDR(B0_XS_CSR),CSR_CLR_RESET) ; + if (!smc->hw.hw_is_64bit) { + outpd(ADDR(B4_R1_F), RX_WATERMARK) ; + outpd(ADDR(B5_XA_F), TX_WATERMARK) ; + outpd(ADDR(B5_XS_F), TX_WATERMARK) ; + } + smc->hw.hw_state = STOPPED ; + mac_drv_repair_descr(smc) ; + } + smc->hw.hw_state = STARTED ; + + return(0) ; +} + + +/* + * called by CFM + */ +void config_mux(smc,mux) +struct s_smc *smc ; +int mux; +{ + plc_config_mux(smc,mux) ; + + SETMASK(FM_A(FM_MDREG1),FM_SELRA,FM_SELRA) ; +} + +/* + * called by RMT + * enable CLAIM/BEACON interrupts + * (only called if these events are of interest, e.g. in DETECT state + * the interrupt must not be permanently enabled + * RMT calls this function periodically (timer driven polling) + */ +void sm_mac_check_beacon_claim(smc) +struct s_smc *smc ; +{ + /* set formac IMSK : 0 enables irq */ + outpw(FM_A(FM_IMSK2U),~(mac_imsk2u | mac_beacon_imsk2u)) ; + /* the driver must receive the directed beacons */ + formac_rcv_restart(smc) ; + process_receive(smc) ; +} + +/*-------------------------- interface functions ----------------------------*/ +/* + * control ODL output + */ +void sm_pm_control(smc,mode) +struct s_smc *smc ; +int mode; +{ + SK_UNUSED(smc) ; + + /* + * if PCM logic has set LS_REQUEST = Transmit QUIET Line State + * /FOTOFF signal turn activ -> ODL disable + */ + switch(mode) { + case PM_TRANSMIT_DISABLE : + break ; + case PM_TRANSMIT_ENABLE : + break ; + } +} + +/* + * control MAC layer (called by RMT) + */ +void sm_ma_control(smc,mode) +struct s_smc *smc ; +int mode; +{ + switch(mode) { + case MA_OFFLINE : + /* Add to make the MAC offline in RM0_ISOLATED state */ + formac_offline(smc) ; + break ; + case MA_RESET : + (void)init_mac(smc,0) ; + break ; + case MA_BEACON : + formac_online(smc) ; + break ; + case MA_DIRECTED : + directed_beacon(smc) ; + break ; + case MA_TREQ : + /* + * no actions necessary, TREQ is already set + */ + break ; + } +} + +int sm_mac_get_tx_state(smc) +struct s_smc *smc ; +{ + return((inpw(FM_A(FM_STMCHN))>>4)&7) ; +} + +/* + * multicast functions + */ + +static struct s_fpmc *mac_get_mc_table(smc,user,own,del,can) +struct s_smc *smc ; +struct fddi_addr *user ; +struct fddi_addr *own ; +int del ; +int can ; +{ + struct s_fpmc *tb ; + struct s_fpmc *slot ; + u_char *p ; + int i ; + + /* + * set own = can(user) + */ + *own = *user ; + if (can) { + p = own->a ; + for (i = 0 ; i < 6 ; i++, p++) + *p = canonical[*p] ; + } + slot = 0 ; + for (i = 0, tb = smc->hw.fp.mc.table ; i < FPMAX_MULTICAST ; i++, tb++){ + if (!tb->n) { /* not used */ + if (!del && !slot) /* if !del save first free */ + slot = tb ; + continue ; + } + if (memcmp((char *)&tb->a,(char *)own,6)) + continue ; + return(tb) ; + } + return(slot) ; /* return first free or NULL */ +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;2) + + void mac_clear_multicast(smc) + struct s_smc *smc ; + +Function DOWNCALL (SMT, fplustm.c) + Clear all multicast entries + + END_MANUAL_ENTRY() + */ +void mac_clear_multicast(smc) +struct s_smc *smc ; +{ + struct s_fpmc *tb ; + int i ; + + smc->hw.fp.os_slots_used = 0 ; /* note the SMT addresses */ + /* will not be deleted */ + for (i = 0, tb = smc->hw.fp.mc.table ; i < FPMAX_MULTICAST ; i++, tb++){ + if (!tb->perm) { + tb->n = 0 ; + } + } +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;2) + + int mac_set_func_addr(smc,f_addr) + struct s_smc *smc ; + u_long f_addr ; + +Function DOWNCALL (SMT, fplustm.c) + Set a Token-Ring functional address, the address will + be activated after calling mac_update_multicast() + +Para f_addr functional bits in non-canonical format + +Returns 0: always success + + END_MANUAL_ENTRY() + */ +int mac_set_func_addr(smc,f_addr) +struct s_smc *smc ; +u_long f_addr ; +{ + smc->hw.fp.func_addr = f_addr ; + return(0) ; +} + + +/* + BEGIN_MANUAL_ENTRY(if,func;others;2) + + int mac_add_multicast(smc,addr,can) + struct s_smc *smc ; + struct fddi_addr *addr ; + int can ; + +Function DOWNCALL (SMC, fplustm.c) + Add an entry to the multicast table + +Para addr pointer to a multicast address + can = 0: the multicast address has the physical format + = 1: the multicast address has the canonical format + | 0x80 permanent + +Returns 0: success + 1: address table full + +Note After a 'driver reset' or a 'station set address' all + entries of the multicast table are cleared. + In this case the driver has to fill the multicast table again. + After the operating system dependent module filled + the multicast table it must call mac_update_multicast + to activate the new multicast addresses! + + END_MANUAL_ENTRY() + */ +int mac_add_multicast(smc,addr,can) +struct s_smc *smc ; +struct fddi_addr *addr ; +int can ; +{ + SK_LOC_DECL(struct fddi_addr,own) ; + struct s_fpmc *tb ; + + /* + * check if there are free table entries + */ + if (can & 0x80) { + if (smc->hw.fp.smt_slots_used >= SMT_MAX_MULTI) { + return(1) ; + } + } + else { + if (smc->hw.fp.os_slots_used >= FPMAX_MULTICAST-SMT_MAX_MULTI) { + return(1) ; + } + } + + /* + * find empty slot + */ + if (!(tb = mac_get_mc_table(smc,addr,&own,0,can & ~0x80))) + return(1) ; + tb->n++ ; + tb->a = own ; + tb->perm = (can & 0x80) ? 1 : 0 ; + + if (can & 0x80) + smc->hw.fp.smt_slots_used++ ; + else + smc->hw.fp.os_slots_used++ ; + + return(0) ; +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;2) + + void mac_del_multicast(smc,addr,can) + struct s_smc *smc ; + struct fddi_addr *addr ; + int can ; + +Function DOWNCALL (SMT, fplustm.c) + Delete an entry from the multicast table + +Para addr pointer to a multicast address + can = 0: the multicast address has the physical format + = 1: the multicast address has the canonical format + | 0x80 permanent + + END_MANUAL_ENTRY() + */ +void mac_del_multicast(smc,addr,can) +struct s_smc *smc ; +struct fddi_addr *addr ; +int can ; +{ + SK_LOC_DECL(struct fddi_addr,own) ; + struct s_fpmc *tb ; + + if (!(tb = mac_get_mc_table(smc,addr,&own,1,can & ~0x80))) + return ; + /* + * permanent addresses must be deleted with perm bit + * and vice versa + */ + if (( tb->perm && (can & 0x80)) || + (!tb->perm && !(can & 0x80))) { + /* + * delete it + */ + if (tb->n) { + tb->n-- ; + if (tb->perm) { + smc->hw.fp.smt_slots_used-- ; + } + else { + smc->hw.fp.os_slots_used-- ; + } + } + } +} + +/* + * mode + */ + +#define RX_MODE_PROM 0x1 +#define RX_MODE_ALL_MULTI 0x2 + +/* + BEGIN_MANUAL_ENTRY(if,func;others;2) + + void mac_update_multicast(smc) + struct s_smc *smc ; + +Function DOWNCALL (SMT, fplustm.c) + Update FORMAC multicast registers + + END_MANUAL_ENTRY() + */ +void mac_update_multicast(smc) +struct s_smc *smc ; +{ + struct s_fpmc *tb ; + u_char *fu ; + int i ; + + /* + * invalidate the CAM + */ + outpw(FM_A(FM_AFCMD),FM_IINV_CAM) ; + + /* + * set the functional address + */ + if (smc->hw.fp.func_addr) { + fu = (u_char *) &smc->hw.fp.func_addr ; + outpw(FM_A(FM_AFMASK2),0xffff) ; + outpw(FM_A(FM_AFMASK1),(u_short) ~((fu[0] << 8) + fu[1])) ; + outpw(FM_A(FM_AFMASK0),(u_short) ~((fu[2] << 8) + fu[3])) ; + outpw(FM_A(FM_AFPERS),FM_VALID|FM_DA) ; + outpw(FM_A(FM_AFCOMP2), 0xc000) ; + outpw(FM_A(FM_AFCOMP1), 0x0000) ; + outpw(FM_A(FM_AFCOMP0), 0x0000) ; + outpw(FM_A(FM_AFCMD),FM_IWRITE_CAM) ; + } + + /* + * set the mask and the personality register(s) + */ + outpw(FM_A(FM_AFMASK0),0xffff) ; + outpw(FM_A(FM_AFMASK1),0xffff) ; + outpw(FM_A(FM_AFMASK2),0xffff) ; + outpw(FM_A(FM_AFPERS),FM_VALID|FM_DA) ; + + for (i = 0, tb = smc->hw.fp.mc.table; i < FPMAX_MULTICAST; i++, tb++) { + if (tb->n) { + CHECK_CAM() ; + + /* + * wirte the multicast addres into the CAM + */ + outpw(FM_A(FM_AFCOMP2), + (u_short)((tb->a.a[0]<<8)+tb->a.a[1])) ; + outpw(FM_A(FM_AFCOMP1), + (u_short)((tb->a.a[2]<<8)+tb->a.a[3])) ; + outpw(FM_A(FM_AFCOMP0), + (u_short)((tb->a.a[4]<<8)+tb->a.a[5])) ; + outpw(FM_A(FM_AFCMD),FM_IWRITE_CAM) ; + } + } +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;3) + + void mac_set_rx_mode(smc,mode) + struct s_smc *smc ; + int mode ; + +Function DOWNCALL/INTERN (SMT, fplustm.c) + This function enables / disables the selected receive. + Don't call this function if the hardware module is + used -- use mac_drv_rx_mode() instead of. + +Para mode = 1 RX_ENABLE_ALLMULTI enable all multicasts + 2 RX_DISABLE_ALLMULTI disable "enable all multicasts" + 3 RX_ENABLE_PROMISC enable promiscous + 4 RX_DISABLE_PROMISC disable promiscous + 5 RX_ENABLE_NSA enable reception of NSA frames + 6 RX_DISABLE_NSA disable reception of NSA frames + +Note The selected receive modes will be lost after 'driver reset' + or 'set station address' + + END_MANUAL_ENTRY + */ +void mac_set_rx_mode(smc,mode) +struct s_smc *smc ; +int mode ; +{ + switch (mode) { + case RX_ENABLE_ALLMULTI : + smc->hw.fp.rx_prom |= RX_MODE_ALL_MULTI ; + break ; + case RX_DISABLE_ALLMULTI : + smc->hw.fp.rx_prom &= ~RX_MODE_ALL_MULTI ; + break ; + case RX_ENABLE_PROMISC : + smc->hw.fp.rx_prom |= RX_MODE_PROM ; + break ; + case RX_DISABLE_PROMISC : + smc->hw.fp.rx_prom &= ~RX_MODE_PROM ; + break ; + case RX_ENABLE_NSA : + smc->hw.fp.nsa_mode = FM_MDAMA ; + smc->hw.fp.rx_mode = (smc->hw.fp.rx_mode & ~FM_ADDET) | + smc->hw.fp.nsa_mode ; + break ; + case RX_DISABLE_NSA : + smc->hw.fp.nsa_mode = FM_MRNNSAFNMA ; + smc->hw.fp.rx_mode = (smc->hw.fp.rx_mode & ~FM_ADDET) | + smc->hw.fp.nsa_mode ; + break ; + } + if (smc->hw.fp.rx_prom & RX_MODE_PROM) { + smc->hw.fp.rx_mode = FM_MLIMPROM ; + } + else if (smc->hw.fp.rx_prom & RX_MODE_ALL_MULTI) { + smc->hw.fp.rx_mode = smc->hw.fp.nsa_mode | FM_EXGPA0 ; + } + else + smc->hw.fp.rx_mode = smc->hw.fp.nsa_mode ; + SETMASK(FM_A(FM_MDREG1),smc->hw.fp.rx_mode,FM_ADDRX) ; + mac_update_multicast(smc) ; +} + +/* + BEGIN_MANUAL_ENTRY(module;tests;3) + How to test the Restricted Token Monitor + ---------------------------------------------------------------- + + o Insert a break point in the function rtm_irq() + o Remove all stations with a restricted token monitor from the + network. + o Connect a UPPS ISA or EISA station to the network. + o Give the FORMAC of UPPS station the command to send + restricted tokens until the ring becomes instable. + o Now connect your test test client. + o The restricted token monitor should detect the restricted token, + and your break point will be reached. + o You can ovserve how the station will clean the ring. + + END_MANUAL_ENTRY + */ +void rtm_irq(smc) +struct s_smc *smc ; +{ + outpw(ADDR(B2_RTM_CRTL),TIM_CL_IRQ) ; /* clear IRQ */ + if (inpw(ADDR(B2_RTM_CRTL)) & TIM_RES_TOK) { + outpw(FM_A(FM_CMDREG1),FM_ICL) ; /* force claim */ + DB_RMT("RMT: fddiPATHT_Rmode expired\n",0,0) ; + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, + (u_long) FDDI_SMT_EVENT, + (u_long) FDDI_RTT, smt_get_event_word(smc)); + } + outpw(ADDR(B2_RTM_CRTL),TIM_START) ; /* enable RTM monitoring */ +} + +static void rtm_init(smc) +struct s_smc *smc ; +{ + outpd(ADDR(B2_RTM_INI),0) ; /* timer = 0 */ + outpw(ADDR(B2_RTM_CRTL),TIM_START) ; /* enable IRQ */ +} + +void rtm_set_timer(smc) +struct s_smc *smc ; +{ + /* + * MIB timer and hardware timer have the same resolution of 80nS + */ + DB_RMT("RMT: setting new fddiPATHT_Rmode, t = %d ns \n", + (int) smc->mib.a[PATH0].fddiPATHT_Rmode,0) ; + outpd(ADDR(B2_RTM_INI),smc->mib.a[PATH0].fddiPATHT_Rmode) ; +} + +static void smt_split_up_fifo(smc) +struct s_smc *smc ; +{ + +/* + BEGIN_MANUAL_ENTRY(module;mem;1) + ------------------------------------------------------------- + RECEIVE BUFFER MEMORY DIVERSION + ------------------------------------------------------------- + + R1_RxD == SMT_R1_RXD_COUNT + R2_RxD == SMT_R2_RXD_COUNT + + SMT_R1_RXD_COUNT must be unequal zero + + | R1_RxD R2_RxD |R1_RxD R2_RxD | R1_RxD R2_RxD + | x 0 | x 1-3 | x < 3 + ---------------------------------------------------------------------- + | 63,75 kB | 54,75 | R1_RxD + rx queue 1 | RX_FIFO_SPACE | RX_LARGE_FIFO| ------------- * 63,75 kB + | | | R1_RxD+R2_RxD + ---------------------------------------------------------------------- + | | 9 kB | R2_RxD + rx queue 2 | 0 kB | RX_SMALL_FIFO| ------------- * 63,75 kB + | (not used) | | R1_RxD+R2_RxD + + END_MANUAL_ENTRY +*/ + + if (SMT_R1_RXD_COUNT == 0) { + SMT_PANIC(smc,SMT_E0117, SMT_E0117_MSG) ; + } + + switch(SMT_R2_RXD_COUNT) { + case 0: + smc->hw.fp.fifo.rx1_fifo_size = RX_FIFO_SPACE ; + smc->hw.fp.fifo.rx2_fifo_size = 0 ; + break ; + case 1: + case 2: + case 3: + smc->hw.fp.fifo.rx1_fifo_size = RX_LARGE_FIFO ; + smc->hw.fp.fifo.rx2_fifo_size = RX_SMALL_FIFO ; + break ; + default: /* this is not the real defaule */ + smc->hw.fp.fifo.rx1_fifo_size = RX_FIFO_SPACE * + SMT_R1_RXD_COUNT/(SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT) ; + smc->hw.fp.fifo.rx2_fifo_size = RX_FIFO_SPACE * + SMT_R2_RXD_COUNT/(SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT) ; + break ; + } + +/* + BEGIN_MANUAL_ENTRY(module;mem;1) + ------------------------------------------------------------- + TRANSMIT BUFFER MEMORY DIVERSION + ------------------------------------------------------------- + + + | no sync bw | sync bw available and | sync bw available and + | available | SynchTxMode = SPLIT | SynchTxMode = ALL + ----------------------------------------------------------------------- + sync tx | 0 kB | 32 kB | 55 kB + queue | | TX_MEDIUM_FIFO | TX_LARGE_FIFO + ----------------------------------------------------------------------- + async tx | 64 kB | 32 kB | 9 k + queue | TX_FIFO_SPACE| TX_MEDIUM_FIFO | TX_SMALL_FIFO + + END_MANUAL_ENTRY +*/ + + /* + * set the tx mode bits + */ + if (smc->mib.a[PATH0].fddiPATHSbaPayload) { +#ifdef ESS + smc->hw.fp.fifo.fifo_config_mode |= + smc->mib.fddiESSSynchTxMode | SYNC_TRAFFIC_ON ; +#endif + } + else { + smc->hw.fp.fifo.fifo_config_mode &= + ~(SEND_ASYNC_AS_SYNC|SYNC_TRAFFIC_ON) ; + } + + /* + * split up the FIFO + */ + if (smc->hw.fp.fifo.fifo_config_mode & SYNC_TRAFFIC_ON) { + if (smc->hw.fp.fifo.fifo_config_mode & SEND_ASYNC_AS_SYNC) { + smc->hw.fp.fifo.tx_s_size = TX_LARGE_FIFO ; + smc->hw.fp.fifo.tx_a0_size = TX_SMALL_FIFO ; + } + else { + smc->hw.fp.fifo.tx_s_size = TX_MEDIUM_FIFO ; + smc->hw.fp.fifo.tx_a0_size = TX_MEDIUM_FIFO ; + } + } + else { + smc->hw.fp.fifo.tx_s_size = 0 ; + smc->hw.fp.fifo.tx_a0_size = TX_FIFO_SPACE ; + } + + smc->hw.fp.fifo.rx1_fifo_start = smc->hw.fp.fifo.rbc_ram_start + + RX_FIFO_OFF ; + smc->hw.fp.fifo.tx_s_start = smc->hw.fp.fifo.rx1_fifo_start + + smc->hw.fp.fifo.rx1_fifo_size ; + smc->hw.fp.fifo.tx_a0_start = smc->hw.fp.fifo.tx_s_start + + smc->hw.fp.fifo.tx_s_size ; + smc->hw.fp.fifo.rx2_fifo_start = smc->hw.fp.fifo.tx_a0_start + + smc->hw.fp.fifo.tx_a0_size ; + + DB_SMT("FIFO split: mode = %x\n",smc->hw.fp.fifo.fifo_config_mode,0) ; + DB_SMT("rbc_ram_start = %x rbc_ram_end = %x\n", + smc->hw.fp.fifo.rbc_ram_start, smc->hw.fp.fifo.rbc_ram_end) ; + DB_SMT("rx1_fifo_start = %x tx_s_start = %x\n", + smc->hw.fp.fifo.rx1_fifo_start, smc->hw.fp.fifo.tx_s_start) ; + DB_SMT("tx_a0_start = %x rx2_fifo_start = %x\n", + smc->hw.fp.fifo.tx_a0_start, smc->hw.fp.fifo.rx2_fifo_start) ; +} + +void formac_reinit_tx(smc) +struct s_smc *smc ; +{ + /* + * Split up the FIFO and reinitialize the MAC if synchronous + * bandwidth becomes available but no synchronous queue is + * configured. + */ + if (!smc->hw.fp.fifo.tx_s_size && smc->mib.a[PATH0].fddiPATHSbaPayload){ + (void)init_mac(smc,0) ; + } +} + + diff --git a/drivers/net/skfp/h/cmtdef.h b/drivers/net/skfp/h/cmtdef.h new file mode 100644 index 000000000000..0b54c57ca13c --- /dev/null +++ b/drivers/net/skfp/h/cmtdef.h @@ -0,0 +1,801 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _CMTDEF_ +#define _CMTDEF_ + +/* **************************************************************** */ + +/* + * implementation specific constants + * MODIIFY THE FOLLWOING THREE DEFINES + */ +#define AMDPLC /* if Amd PLC chip used */ +#ifdef CONC +#define NUMPHYS 12 /* 2 for SAS or DAS, more for Concentrator */ +#else +#ifdef CONC_II +#define NUMPHYS 24 /* 2 for SAS or DAS, more for Concentrator */ +#else +#define NUMPHYS 2 /* 2 for SAS or DAS, more for Concentrator */ +#endif +#endif +#define NUMMACS 1 /* only 1 supported at the moment */ +#define NUMPATHS 2 /* primary and secondary path supported */ + +/* + * DO NOT MODIFY BEYOND THIS POINT + */ + +/* **************************************************************** */ + +#if NUMPHYS > 2 +#define CONCENTRATOR +#endif + +/* + * Definitions for comfortable LINT usage + */ +#ifdef lint +#define LINT_USE(x) (x)=(x) +#else +#define LINT_USE(x) +#endif + +#ifdef DEBUG +#define DB_PR(flag,a,b,c) { if (flag) printf(a,b,c) ; } +#else +#define DB_PR(flag,a,b,c) +#endif + +#ifdef DEBUG_BRD +#define DB_ECM(a,b,c) DB_PR((smc->debug.d_smt&1),a,b,c) +#define DB_ECMN(n,a,b,c) DB_PR((smc->debug.d_ecm >=(n)),a,b,c) +#define DB_RMT(a,b,c) DB_PR((smc->debug.d_smt&2),a,b,c) +#define DB_RMTN(n,a,b,c) DB_PR((smc->debug.d_rmt >=(n)),a,b,c) +#define DB_CFM(a,b,c) DB_PR((smc->debug.d_smt&4),a,b,c) +#define DB_CFMN(n,a,b,c) DB_PR((smc->debug.d_cfm >=(n)),a,b,c) +#define DB_PCM(a,b,c) DB_PR((smc->debug.d_smt&8),a,b,c) +#define DB_PCMN(n,a,b,c) DB_PR((smc->debug.d_pcm >=(n)),a,b,c) +#define DB_SMT(a,b,c) DB_PR((smc->debug.d_smtf),a,b,c) +#define DB_SMTN(n,a,b,c) DB_PR((smc->debug.d_smtf >=(n)),a,b,c) +#define DB_SBA(a,b,c) DB_PR((smc->debug.d_sba),a,b,c) +#define DB_SBAN(n,a,b,c) DB_PR((smc->debug.d_sba >=(n)),a,b,c) +#define DB_ESS(a,b,c) DB_PR((smc->debug.d_ess),a,b,c) +#define DB_ESSN(n,a,b,c) DB_PR((smc->debug.d_ess >=(n)),a,b,c) +#else +#define DB_ECM(a,b,c) DB_PR((debug.d_smt&1),a,b,c) +#define DB_ECMN(n,a,b,c) DB_PR((debug.d_ecm >=(n)),a,b,c) +#define DB_RMT(a,b,c) DB_PR((debug.d_smt&2),a,b,c) +#define DB_RMTN(n,a,b,c) DB_PR((debug.d_rmt >=(n)),a,b,c) +#define DB_CFM(a,b,c) DB_PR((debug.d_smt&4),a,b,c) +#define DB_CFMN(n,a,b,c) DB_PR((debug.d_cfm >=(n)),a,b,c) +#define DB_PCM(a,b,c) DB_PR((debug.d_smt&8),a,b,c) +#define DB_PCMN(n,a,b,c) DB_PR((debug.d_pcm >=(n)),a,b,c) +#define DB_SMT(a,b,c) DB_PR((debug.d_smtf),a,b,c) +#define DB_SMTN(n,a,b,c) DB_PR((debug.d_smtf >=(n)),a,b,c) +#define DB_SBA(a,b,c) DB_PR((debug.d_sba),a,b,c) +#define DB_SBAN(n,a,b,c) DB_PR((debug.d_sba >=(n)),a,b,c) +#define DB_ESS(a,b,c) DB_PR((debug.d_ess),a,b,c) +#define DB_ESSN(n,a,b,c) DB_PR((debug.d_ess >=(n)),a,b,c) +#endif + +#ifndef SS_NOT_DS +#define SK_LOC_DECL(type,var) type var +#else +#define SK_LOC_DECL(type,var) static type var +#endif +/* + * PHYs and PORTS + * Note: Don't touch the definition of PA and PB. Those might be used + * by some "for" loops. + */ +#define PA 0 +#define PB 1 +#if defined(SUPERNET_3) || defined(CONC_II) +/* + * The port indices have to be different, + * because the MAC output goes through the 2. PLC + * Conc II: It has to be the first port in the row. + */ +#define PS 0 /* Internal PLC which is the same as PA */ +#else +#define PS 1 +#endif +#define PM 2 /* PM .. PA+NUM_PHYS-1 */ + +/* + * PHY types - as in path descriptor 'fddiPHYType' + */ +#define TA 0 /* A port */ +#define TB 1 /* B port */ +#define TS 2 /* S port */ +#define TM 3 /* M port */ +#define TNONE 4 + + +/* + * indexes in MIB + */ +#define INDEX_MAC 1 +#define INDEX_PATH 1 +#define INDEX_PORT 1 + + +/* + * policies + */ +#define POLICY_AA (1<<0) /* reject AA */ +#define POLICY_AB (1<<1) /* reject AB */ +#define POLICY_AS (1<<2) /* reject AS */ +#define POLICY_AM (1<<3) /* reject AM */ +#define POLICY_BA (1<<4) /* reject BA */ +#define POLICY_BB (1<<5) /* reject BB */ +#define POLICY_BS (1<<6) /* reject BS */ +#define POLICY_BM (1<<7) /* reject BM */ +#define POLICY_SA (1<<8) /* reject SA */ +#define POLICY_SB (1<<9) /* reject SB */ +#define POLICY_SS (1<<10) /* reject SS */ +#define POLICY_SM (1<<11) /* reject SM */ +#define POLICY_MA (1<<12) /* reject MA */ +#define POLICY_MB (1<<13) /* reject MB */ +#define POLICY_MS (1<<14) /* reject MS */ +#define POLICY_MM (1<<15) /* reject MM */ + +/* + * commands + */ + +/* + * EVENTS + * event classes + */ +#define EVENT_ECM 1 /* event class ECM */ +#define EVENT_CFM 2 /* event class CFM */ +#define EVENT_RMT 3 /* event class RMT */ +#define EVENT_SMT 4 /* event class SMT */ +#define EVENT_PCM 5 /* event class PCM */ +#define EVENT_PCMA 5 /* event class PCMA */ +#define EVENT_PCMB 6 /* event class PCMB */ + +/* WARNING : + * EVENT_PCM* must be last in the above list + * if more then two ports are used, EVENT_PCM .. EVENT_PCMA+NUM_PHYS-1 + * are used ! + */ + +#define EV_TOKEN(class,event) (((u_long)(class)<<16L)|((u_long)(event))) +#define EV_T_CLASS(token) ((int)((token)>>16)&0xffff) +#define EV_T_EVENT(token) ((int)(token)&0xffff) + +/* + * ECM events + */ +#define EC_CONNECT 1 /* connect request */ +#define EC_DISCONNECT 2 /* disconnect request */ +#define EC_TRACE_PROP 3 /* trace propagation */ +#define EC_PATH_TEST 4 /* path test */ +#define EC_TIMEOUT_TD 5 /* timer TD_min */ +#define EC_TIMEOUT_TMAX 6 /* timer trace_max */ +#define EC_TIMEOUT_IMAX 7 /* timer I_max */ +#define EC_TIMEOUT_INMAX 8 /* timer IN_max */ +#define EC_TEST_DONE 9 /* path test done */ + +/* + * CFM events + */ +#define CF_LOOP 1 /* cf_loop flag from PCM */ +#define CF_LOOP_A 1 /* cf_loop flag from PCM */ +#define CF_LOOP_B 2 /* cf_loop flag from PCM */ +#define CF_JOIN 3 /* cf_join flag from PCM */ +#define CF_JOIN_A 3 /* cf_join flag from PCM */ +#define CF_JOIN_B 4 /* cf_join flag from PCM */ + +/* + * PCM events + */ +#define PC_START 1 +#define PC_STOP 2 +#define PC_LOOP 3 +#define PC_JOIN 4 +#define PC_SIGNAL 5 +#define PC_REJECT 6 +#define PC_MAINT 7 +#define PC_TRACE 8 +#define PC_PDR 9 +#define PC_ENABLE 10 +#define PC_DISABLE 11 + +/* + * must be ordered as in LineStateType + */ +#define PC_QLS 12 +#define PC_ILS 13 +#define PC_MLS 14 +#define PC_HLS 15 +#define PC_LS_PDR 16 +#define PC_LS_NONE 17 +#define LS2MIB(x) ((x)-PC_QLS) +#define MIB2LS(x) ((x)+PC_QLS) + +#define PC_TIMEOUT_TB_MAX 18 /* timer TB_max */ +#define PC_TIMEOUT_TB_MIN 19 /* timer TB_min */ +#define PC_TIMEOUT_C_MIN 20 /* timer C_Min */ +#define PC_TIMEOUT_T_OUT 21 /* timer T_Out */ +#define PC_TIMEOUT_TL_MIN 22 /* timer TL_Min */ +#define PC_TIMEOUT_T_NEXT 23 /* timer t_next[] */ +#define PC_TIMEOUT_LCT 24 +#define PC_NSE 25 /* NOISE hardware timer */ +#define PC_LEM 26 /* LEM done */ + +/* + * RMT events meaning from + */ +#define RM_RING_OP 1 /* ring operational MAC */ +#define RM_RING_NON_OP 2 /* ring not operational MAC */ +#define RM_MY_BEACON 3 /* recvd my beacon MAC */ +#define RM_OTHER_BEACON 4 /* recvd other beacon MAC */ +#define RM_MY_CLAIM 5 /* recvd my claim MAC */ +#define RM_TRT_EXP 6 /* TRT exp MAC */ +#define RM_VALID_CLAIM 7 /* claim from dup addr MAC */ +#define RM_JOIN 8 /* signal rm_join CFM */ +#define RM_LOOP 9 /* signal rm_loop CFM */ +#define RM_DUP_ADDR 10 /* dup_addr_test hange SMT-NIF */ +#define RM_ENABLE_FLAG 11 /* enable flag */ + +#define RM_TIMEOUT_NON_OP 12 /* timeout T_Non_OP */ +#define RM_TIMEOUT_T_STUCK 13 /* timeout T_Stuck */ +#define RM_TIMEOUT_ANNOUNCE 14 /* timeout T_Announce */ +#define RM_TIMEOUT_T_DIRECT 15 /* timeout T_Direct */ +#define RM_TIMEOUT_D_MAX 16 /* timeout D_Max */ +#define RM_TIMEOUT_POLL 17 /* claim/beacon poller */ +#define RM_TX_STATE_CHANGE 18 /* To restart timer for D_Max */ + +/* + * SMT events + */ +#define SM_TIMER 1 /* timer */ +#define SM_FAST 2 /* smt_force_irq */ + +/* PC modes */ +#define PM_NONE 0 +#define PM_PEER 1 +#define PM_TREE 2 + +/* + * PCM withhold codes + * MIB PC-WithholdType ENUM + */ +#define PC_WH_NONE 0 /* ok */ +#define PC_WH_M_M 1 /* M to M */ +#define PC_WH_OTHER 2 /* other incompatible phys */ +#define PC_WH_PATH 3 /* path not available */ +/* + * LCT duration + */ +#define LC_SHORT 1 /* short LCT */ +#define LC_MEDIUM 2 /* medium LCT */ +#define LC_LONG 3 /* long LCT */ +#define LC_EXTENDED 4 /* extended LCT */ + +/* + * path_test values + */ +#define PT_NONE 0 +#define PT_TESTING 1 /* test is running */ +#define PT_PASSED 2 /* test passed */ +#define PT_FAILED 3 /* test failed */ +#define PT_PENDING 4 /* path test follows */ +#define PT_EXITING 5 /* disconnected while in trace/leave */ + +/* + * duplicate address test + * MIB DupAddressTest ENUM + */ +#define DA_NONE 0 /* */ +#define DA_PASSED 1 /* test passed */ +#define DA_FAILED 2 /* test failed */ + + +/* + * optical bypass + */ +#define BP_DEINSERT 0 /* disable bypass */ +#define BP_INSERT 1 /* enable bypass */ + +/* + * ODL enable/disable + */ +#define PM_TRANSMIT_DISABLE 0 /* disable xmit */ +#define PM_TRANSMIT_ENABLE 1 /* enable xmit */ + +/* + * parameter for config_mux + * note : number is index in config_endec table ! + */ +#define MUX_THRUA 0 /* through A */ +#define MUX_THRUB 1 /* through B */ +#define MUX_WRAPA 2 /* wrap A */ +#define MUX_WRAPB 3 /* wrap B */ +#define MUX_ISOLATE 4 /* isolated */ +#define MUX_WRAPS 5 /* SAS */ + +/* + * MAC control + */ +#define MA_RESET 0 +#define MA_BEACON 1 +#define MA_CLAIM 2 +#define MA_DIRECTED 3 /* directed beacon */ +#define MA_TREQ 4 /* change T_Req */ +#define MA_OFFLINE 5 /* switch MAC to offline */ + + +/* + * trace prop + * bit map for trace propagation + */ +#define ENTITY_MAC (NUMPHYS) +#define ENTITY_PHY(p) (p) +#define ENTITY_BIT(m) (1<<(m)) + +/* + * Resource Tag Types + */ +#define PATH_ISO 0 /* isolated */ +#define PATH_PRIM 3 /* primary path */ +#define PATH_THRU 5 /* through path */ + +#define RES_MAC 2 /* resource type MAC */ +#define RES_PORT 4 /* resource type PORT */ + + +/* + * CFM state + * oops: MUST MATCH CF-StateType in SMT7.2 ! + */ +#define SC0_ISOLATED 0 /* isolated */ +#define SC1_WRAP_A 5 /* wrap A (not used) */ +#define SC2_WRAP_B 6 /* wrap B (not used) */ +#define SC4_THRU_A 12 /* through A */ +#define SC5_THRU_B 7 /* through B (used in SMT 6.2) */ +#define SC7_WRAP_S 8 /* SAS (not used) */ +#define SC9_C_WRAP_A 9 /* c wrap A */ +#define SC10_C_WRAP_B 10 /* c wrap B */ +#define SC11_C_WRAP_S 11 /* c wrap S */ + +/* + * convert MIB time in units of 80nS to uS + */ +#define MIB2US(t) ((t)/12) +#define SEC2MIB(s) ((s)*12500000L) +/* + * SMT timer + */ +struct smt_timer { + struct smt_timer *tm_next ; /* linked list */ + struct s_smc *tm_smc ; /* pointer to context */ + u_long tm_delta ; /* delta time */ + u_long tm_token ; /* token value */ + u_short tm_active ; /* flag : active/inactive */ + u_short tm_pad ; /* pad field */ +} ; + +/* + * communication structures + */ +struct mac_parameter { + u_long t_neg ; /* T_Neg parameter */ + u_long t_pri ; /* T_Pri register in MAC */ +} ; + +/* + * MAC counters + */ +struct mac_counter { + u_long mac_nobuf_counter ; /* MAC SW counter: no buffer */ + u_long mac_r_restart_counter ; /* MAC SW counter: rx restarted */ +} ; + +/* + * para struct context for SMT parameters + */ +struct s_pcon { + int pc_len ; + int pc_err ; + int pc_badset ; + void *pc_p ; +} ; + + +/* + * link error monitor + */ +#define LEM_AVG 5 +struct lem_counter { +#ifdef AM29K + int lem_on ; + u_long lem_errors ; + u_long lem_symbols ; + u_long lem_tsymbols ; + int lem_s_count ; + int lem_n_s ; + int lem_values ; + int lem_index ; + int lem_avg_ber[LEM_AVG] ; + int lem_sum ; +#else + u_short lem_float_ber ; /* 10E-nn bit error rate */ + u_long lem_errors ; /* accumulated error count */ + u_short lem_on ; +#endif +} ; + +#define NUMBITS 10 + + +#ifdef AMDPLC + +/* + * PLC state table + */ +struct s_plc { + u_short p_state ; /* current state */ + u_short p_bits ; /* number of bits to send */ + u_short p_start ; /* first bit pos */ + u_short p_pad ; /* padding for alignment */ + u_long soft_err ; /* error counter */ + u_long parity_err ; /* error counter */ + u_long ebuf_err ; /* error counter */ + u_long ebuf_cont ; /* continous error counter */ + u_long phyinv ; /* error counter */ + u_long vsym_ctr ; /* error counter */ + u_long mini_ctr ; /* error counter */ + u_long tpc_exp ; /* error counter */ + u_long np_err ; /* error counter */ + u_long b_pcs ; /* error counter */ + u_long b_tpc ; /* error counter */ + u_long b_tne ; /* error counter */ + u_long b_qls ; /* error counter */ + u_long b_ils ; /* error counter */ + u_long b_hls ; /* error counter */ +} ; +#endif + +#ifdef PROTOTYP_INC +#include "fddi/driver.pro" +#else /* PROTOTYP_INC */ +/* + * function prototypes + */ +#include "h/mbuf.h" /* Type definitions for MBUFs */ +void hwt_restart( /* hwt.c */ +#ifdef ANSIC + struct s_smc *smc +#endif + ) ; + +SMbuf *smt_build_frame( /* smt.c */ +#ifdef ANSIC + struct s_smc *smc, + int class, + int type, + int length +#endif + ) ; + +SMbuf *smt_get_mbuf( /* drvsr.c */ +#ifdef ANSIC + struct s_smc *smc +#endif + ) ; + +void *sm_to_para( /* smt.c */ +#ifdef ANSIC + struct s_smc *smc, + struct smt_header *sm, + int para +#endif + ) ; + +#ifndef SK_UNUSED +#define SK_UNUSED(var) (void)(var) +#endif + +void queue_event() ; +void ecm() ; +void ecm_init() ; +void rmt() ; +void rmt_init() ; +void pcm() ; +void pcm_init() ; +void cfm() ; +void cfm_init() ; +void smt_timer_start() ; +void smt_timer_stop() ; +void pcm_status_state() ; +void plc_config_mux() ; +void sm_lem_evaluate() ; +void smt_clear_una_dna() ; +void mac_status_para() ; +void mac_update_counter() ; +void sm_pm_ls_latch() ; +void sm_ma_control() ; +void sm_mac_check_beacon_claim() ; +void config_mux() ; +void smt_agent_init() ; +void smt_timer_init() ; +void smt_received_pack() ; +void smt_add_para() ; +void smt_swap_para() ; +void ev_init() ; +void hwt_init() ; +u_long hwt_read() ; +void hwt_stop() ; +void hwt_start() ; +void smt_send_mbuf() ; +void smt_free_mbuf() ; +void sm_pm_bypass_req() ; +void rmt_indication() ; +void cfm_state_change() ; +void rx_indication() ; +void tx_indication() ; +#ifndef NO_SMT_PANIC +void smt_panic() ; +#else +#ifdef DEBUG +void smt_panic() ; +#else +#define smt_panic(smc,text) +#endif /* DEBUG */ +#endif /* NO_SMT_PANIC */ +void smt_stat_counter() ; +void smt_timer_poll() ; +u_long smt_get_time() ; +u_long smt_get_tid() ; +void smt_timer_done() ; +void smt_set_defaults() ; +void smt_fixup_mib() ; +void smt_reset_defaults() ; +void smt_agent_task() ; +void smt_please_reconnect() ; +int smt_check_para() ; +void driver_get_bia() ; +#ifdef SUPERNET_3 +void drv_reset_indication() ; +#endif /* SUPERNET_3 */ +void smt_start_watchdog() ; + +void smt_event() ; +void pcm_event() ; +void rmt_event() ; +void cfm_event() ; +void timer_event() ; +void ev_dispatcher() ; + +void smt_get_state() ; +void ecm_get_state() ; +void pcm_get_state() ; +void rmt_get_state() ; + +void ecm_state_change() ; +int sm_pm_bypass_present() ; +void pcm_state_change() ; +void rmt_state_change() ; +int sm_pm_get_ls() ; +int pcm_get_s_port() ; +int pcm_rooted_station() ; +int cfm_get_mac_input() ; +int cfm_get_mac_output() ; +int port_to_mib() ; +int cem_build_path() ; +int sm_mac_get_tx_state() ; +int is_individual() ; +int is_my_addr() ; +int is_broadcast() ; +int is_equal() ; +char *get_pcmstate() ; + +int smt_action() ; +u_short smt_online() ; +void smt_force_irq() ; +void smt_pmf_received_pack() ; +void smt_send_frame() ; +void smt_set_timestamp() ; +void mac_set_rx_mode() ; +int mac_add_multicast() ; +int mac_set_func_addr() ; +void mac_del_multicast() ; +void mac_update_multicast() ; +void mac_clear_multicast() ; +void mac_rx_directed_beacon() ; +void set_formac_tsync() ; +void formac_reinit_tx() ; +void formac_tx_restart() ; +void process_receive() ; +void init_driver_fplus() ; + +void rtm_irq() ; +void rtm_set_timer() ; +void ring_status_indication() ; +void llc_recover_tx() ; +void llc_restart_tx() ; +void plc_clear_irq() ; +void plc_irq() ; +int smt_set_mac_opvalues() ; +#ifdef TAG_MODE +void mac_drv_pci_fix() ; +void mac_do_pci_fix() ; +void mac_drv_clear_tx_queue() ; +void mac_drv_repair_descr() ; +u_long hwt_quick_read() ; +void hwt_wait_time() ; +#endif + +#ifdef SMT_PNMI +#ifdef ANSIC +int pnmi_init (struct s_smc* smc); +int pnmi_process_ndis_id (struct s_smc* smc, u_long ndis_oid, void* buf, + int len, int* BytesAccessed, int* BytesNeeded, u_char action); +#else +int pnmi_init (); +int pnmi_process_ndis_id (); +#endif +#endif + +#ifdef SBA +#ifndef _H2INC +void sba() ; +#endif +void sba_raf_received_pack() ; +void sba_timer_poll() ; +void smt_init_sba() ; +#endif +#ifdef ESS +int ess_raf_received_pack() ; +void ess_timer_poll() ; +void ess_para_change() ; +#endif + +#ifdef BOOT +#define smt_srf_event(a,b,c,d) +#define smt_init_evc(a) +#else +void smt_init_evc() ; +void smt_srf_event() ; +#endif + +#ifndef SMT_REAL_TOKEN_CT +void smt_emulate_token_ct(); +#endif + +#if defined(DEBUG) && !defined(BOOT) +void dump_smt() ; +#else +#define dump_smt(smc,sm,text) +#endif + +#ifdef DEBUG +char *addr_to_string() ; +void dump_hex() ; +#endif +#endif /* PROTOTYP_INC */ + +/* PNMI default defines */ +#ifndef PNMI_INIT +#define PNMI_INIT(smc) /* Nothing */ +#endif +#ifndef PNMI_GET_ID +#define PNMI_GET_ID( smc, ndis_oid, buf, len, BytesWritten, BytesNeeded ) \ + ( 1 ? (-1) : (-1) ) +#endif +#ifndef PNMI_SET_ID +#define PNMI_SET_ID( smc, ndis_oid, buf, len, BytesRead, BytesNeeded, \ + set_type) ( 1 ? (-1) : (-1) ) +#endif + +/* + * SMT_PANIC defines + */ +#ifndef SMT_PANIC +#define SMT_PANIC(smc,nr,msg) smt_panic (smc, msg) +#endif + +#ifndef SMT_ERR_LOG +#define SMT_ERR_LOG(smc,nr,msg) SMT_PANIC (smc, nr, msg) +#endif + +#ifndef SMT_EBASE +#define SMT_EBASE 100 +#endif + +#define SMT_E0100 SMT_EBASE + 0 +#define SMT_E0100_MSG "cfm FSM: illegal ce_type" +#define SMT_E0101 SMT_EBASE + 1 +#define SMT_E0101_MSG "CEM: case ???" +#define SMT_E0102 SMT_EBASE + 2 +#define SMT_E0102_MSG "CEM A: illegal state" +#define SMT_E0103 SMT_EBASE + 3 +#define SMT_E0103_MSG "CEM B: illegal state" +#define SMT_E0104 SMT_EBASE + 4 +#define SMT_E0104_MSG "CEM M: illegal state" +#define SMT_E0105 SMT_EBASE + 5 +#define SMT_E0105_MSG "CEM S: illegal state" +#define SMT_E0106 SMT_EBASE + 6 +#define SMT_E0106_MSG "CFM : illegal state" +#define SMT_E0107 SMT_EBASE + 7 +#define SMT_E0107_MSG "ECM : illegal state" +#define SMT_E0108 SMT_EBASE + 8 +#define SMT_E0108_MSG "prop_actions : NAC in DAS CFM" +#define SMT_E0109 SMT_EBASE + 9 +#define SMT_E0109_MSG "ST2U.FM_SERRSF error in special frame" +#define SMT_E0110 SMT_EBASE + 10 +#define SMT_E0110_MSG "ST2U.FM_SRFRCTOV recv. count. overflow" +#define SMT_E0111 SMT_EBASE + 11 +#define SMT_E0111_MSG "ST2U.FM_SNFSLD NP & FORMAC simult. load" +#define SMT_E0112 SMT_EBASE + 12 +#define SMT_E0112_MSG "ST2U.FM_SRCVFRM single-frame recv.-mode" +#define SMT_E0113 SMT_EBASE + 13 +#define SMT_E0113_MSG "FPLUS: Buffer Memory Error" +#define SMT_E0114 SMT_EBASE + 14 +#define SMT_E0114_MSG "ST2U.FM_SERRSF error in special frame" +#define SMT_E0115 SMT_EBASE + 15 +#define SMT_E0115_MSG "ST3L: parity error in receive queue 2" +#define SMT_E0116 SMT_EBASE + 16 +#define SMT_E0116_MSG "ST3L: parity error in receive queue 1" +#define SMT_E0117 SMT_EBASE + 17 +#define SMT_E0117_MSG "E_SMT_001: RxD count for receive queue 1 = 0" +#define SMT_E0118 SMT_EBASE + 18 +#define SMT_E0118_MSG "PCM : illegal state" +#define SMT_E0119 SMT_EBASE + 19 +#define SMT_E0119_MSG "smt_add_para" +#define SMT_E0120 SMT_EBASE + 20 +#define SMT_E0120_MSG "smt_set_para" +#define SMT_E0121 SMT_EBASE + 21 +#define SMT_E0121_MSG "illegal event in dispatcher" +#define SMT_E0122 SMT_EBASE + 22 +#define SMT_E0122_MSG "RMT : illegal state" +#define SMT_E0123 SMT_EBASE + 23 +#define SMT_E0123_MSG "SBA: state machine has illegal state" +#define SMT_E0124 SMT_EBASE + 24 +#define SMT_E0124_MSG "sba_free_session() called with NULL pointer" +#define SMT_E0125 SMT_EBASE + 25 +#define SMT_E0125_MSG "SBA : illegal session pointer" +#define SMT_E0126 SMT_EBASE + 26 +#define SMT_E0126_MSG "smt_free_mbuf() called with NULL pointer\n" +#define SMT_E0127 SMT_EBASE + 27 +#define SMT_E0127_MSG "sizeof evcs" +#define SMT_E0128 SMT_EBASE + 28 +#define SMT_E0128_MSG "evc->evc_cond_state = 0" +#define SMT_E0129 SMT_EBASE + 29 +#define SMT_E0129_MSG "evc->evc_multiple = 0" +#define SMT_E0130 SMT_EBASE + 30 +#define SMT_E0130_MSG write_mdr_warning +#define SMT_E0131 SMT_EBASE + 31 +#define SMT_E0131_MSG cam_warning +#define SMT_E0132 SMT_EBASE + 32 +#define SMT_E0132_MSG "ST1L.FM_SPCEPDx parity/coding error" +#define SMT_E0133 SMT_EBASE + 33 +#define SMT_E0133_MSG "ST1L.FM_STBURx tx buffer underrun" +#define SMT_E0134 SMT_EBASE + 34 +#define SMT_E0134_MSG "ST1L.FM_SPCEPDx parity error" +#define SMT_E0135 SMT_EBASE + 35 +#define SMT_E0135_MSG "RMT: duplicate MAC address detected. Ring left!" +#define SMT_E0136 SMT_EBASE + 36 +#define SMT_E0136_MSG "Elasticity Buffer hang-up" +#define SMT_E0137 SMT_EBASE + 37 +#define SMT_E0137_MSG "SMT: queue overrun" +#define SMT_E0138 SMT_EBASE + 38 +#define SMT_E0138_MSG "RMT: duplicate MAC address detected. Ring NOT left!" +#endif /* _CMTDEF_ */ diff --git a/drivers/net/skfp/h/fddi.h b/drivers/net/skfp/h/fddi.h new file mode 100644 index 000000000000..c9a28a8a383b --- /dev/null +++ b/drivers/net/skfp/h/fddi.h @@ -0,0 +1,69 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _FDDI_ +#define _FDDI_ + +struct fddi_addr { + u_char a[6] ; +} ; + +#define GROUP_ADDR 0x80 /* MSB in a[0] */ + +struct fddi_mac { + struct fddi_addr mac_dest ; + struct fddi_addr mac_source ; + u_char mac_info[4478] ; +} ; + +#define FDDI_MAC_SIZE (12) +#define FDDI_RAW_MTU (4500-5) /* exl. Pr,SD, ED/FS */ +#define FDDI_RAW (4500) + +/* + * FC values + */ +#define FC_VOID 0x40 /* void frame */ +#define FC_TOKEN 0x80 /* token */ +#define FC_RES_TOKEN 0xc0 /* restricted token */ +#define FC_SMT_INFO 0x41 /* SMT Info frame */ +/* + * FC_SMT_LAN_LOC && FC_SMT_LOC are SK specific ! + */ +#define FC_SMT_LAN_LOC 0x42 /* local SMT Info frame */ +#define FC_SMT_LOC 0x43 /* local SMT Info frame */ +#define FC_SMT_NSA 0x4f /* SMT NSA frame */ +#define FC_MAC 0xc0 /* MAC frame */ +#define FC_BEACON 0xc2 /* MAC beacon frame */ +#define FC_CLAIM 0xc3 /* MAC claim frame */ +#define FC_SYNC_LLC 0xd0 /* sync. LLC frame */ +#define FC_ASYNC_LLC 0x50 /* async. LLC frame */ +#define FC_SYNC_BIT 0x80 /* sync. bit in FC */ + +#define FC_LLC_PRIOR 0x07 /* priority bits */ + +#define BEACON_INFO 0 /* beacon type */ +#define DBEACON_INFO 1 /* beacon type DIRECTED */ + + +/* + * indicator bits + */ +#define C_INDICATOR (1<<0) +#define A_INDICATOR (1<<1) +#define E_INDICATOR (1<<2) +#define I_INDICATOR (1<<6) /* SK specific */ +#define L_INDICATOR (1<<7) /* SK specific */ + +#endif /* _FDDI_ */ diff --git a/drivers/net/skfp/h/fddimib.h b/drivers/net/skfp/h/fddimib.h new file mode 100644 index 000000000000..d1acdc773950 --- /dev/null +++ b/drivers/net/skfp/h/fddimib.h @@ -0,0 +1,349 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * FDDI MIB + */ + +/* + * typedefs + */ + +typedef u_long Counter ; +typedef u_char TimeStamp[8] ; +typedef struct fddi_addr LongAddr ; +typedef u_long Timer_2 ; +typedef u_long Timer ; +typedef u_short ResId ; +typedef u_short SMTEnum ; +typedef u_char SMTFlag ; + +typedef struct { + Counter count ; + TimeStamp timestamp ; +} SetCountType ; + +/* + * bits for bit string "available_path" + */ +#define MIB_PATH_P (1<<0) +#define MIB_PATH_S (1<<1) +#define MIB_PATH_L (1<<2) + +/* + * bits for bit string PermittedPaths & RequestedPaths (SIZE(8)) + */ +#define MIB_P_PATH_LOCAL (1<<0) +#define MIB_P_PATH_SEC_ALTER (1<<1) +#define MIB_P_PATH_PRIM_ALTER (1<<2) +#define MIB_P_PATH_CON_ALTER (1<<3) +#define MIB_P_PATH_SEC_PREFER (1<<4) +#define MIB_P_PATH_PRIM_PREFER (1<<5) +#define MIB_P_PATH_CON_PREFER (1<<6) +#define MIB_P_PATH_THRU (1<<7) + +/* + * enum current path + */ +#define MIB_PATH_ISOLATED 0 +#define MIB_PATH_LOCAL 1 +#define MIB_PATH_SECONDARY 2 +#define MIB_PATH_PRIMARY 3 +#define MIB_PATH_CONCATENATED 4 +#define MIB_PATH_THRU 5 + +/* + * enum PMDClass + */ +#define MIB_PMDCLASS_MULTI 0 +#define MIB_PMDCLASS_SINGLE1 1 +#define MIB_PMDCLASS_SINGLE2 2 +#define MIB_PMDCLASS_SONET 3 +#define MIB_PMDCLASS_LCF 4 +#define MIB_PMDCLASS_TP 5 +#define MIB_PMDCLASS_UNKNOWN 6 +#define MIB_PMDCLASS_UNSPEC 7 + +/* + * enum SMTStationStatus + */ +#define MIB_SMT_STASTA_CON 0 +#define MIB_SMT_STASTA_SEPA 1 +#define MIB_SMT_STASTA_THRU 2 + + +struct fddi_mib { + /* + * private + */ + u_char fddiPRPMFPasswd[8] ; + struct smt_sid fddiPRPMFStation ; + +#ifdef ESS + /* + * private variables for static allocation of the + * End Station Support + */ + u_long fddiESSPayload ; /* payload for static alloc */ + u_long fddiESSOverhead ; /* frame ov for static alloc */ + u_long fddiESSMaxTNeg ; /* maximum of T-NEG */ + u_long fddiESSMinSegmentSize ; /* min size of the sync frames */ + u_long fddiESSCategory ; /* category for the Alloc req */ + short fddiESSSynchTxMode ; /* send all LLC frames as sync */ +#endif /* ESS */ +#ifdef SBA + /* + * private variables for the Synchronous Bandwidth Allocator + */ + char fddiSBACommand ; /* holds the parsed SBA cmd */ + u_char fddiSBAAvailable ; /* SBA allocatable value */ +#endif /* SBA */ + + /* + * SMT standard mib + */ + struct smt_sid fddiSMTStationId ; + u_short fddiSMTOpVersionId ; + u_short fddiSMTHiVersionId ; + u_short fddiSMTLoVersionId ; + u_char fddiSMTManufacturerData[32] ; + u_char fddiSMTUserData[32] ; + u_short fddiSMTMIBVersionId ; + + /* + * ConfigGrp + */ + u_char fddiSMTMac_Ct ; + u_char fddiSMTNonMaster_Ct ; + u_char fddiSMTMaster_Ct ; + u_char fddiSMTAvailablePaths ; + u_short fddiSMTConfigCapabilities ; + u_short fddiSMTConfigPolicy ; + u_short fddiSMTConnectionPolicy ; + u_short fddiSMTTT_Notify ; + u_char fddiSMTStatRptPolicy ; + u_long fddiSMTTrace_MaxExpiration ; + u_short fddiSMTPORTIndexes[NUMPHYS] ; + u_short fddiSMTMACIndexes ; + u_char fddiSMTBypassPresent ; + + /* + * StatusGrp + */ + SMTEnum fddiSMTECMState ; + SMTEnum fddiSMTCF_State ; + SMTEnum fddiSMTStationStatus ; + u_char fddiSMTRemoteDisconnectFlag ; + u_char fddiSMTPeerWrapFlag ; + + /* + * MIBOperationGrp + */ + TimeStamp fddiSMTTimeStamp ; + TimeStamp fddiSMTTransitionTimeStamp ; + SetCountType fddiSMTSetCount ; + struct smt_sid fddiSMTLastSetStationId ; + + struct fddi_mib_m { + u_short fddiMACFrameStatusFunctions ; + Timer_2 fddiMACT_MaxCapabilitiy ; + Timer_2 fddiMACTVXCapabilitiy ; + + /* ConfigGrp */ + u_char fddiMACMultiple_N ; /* private */ + u_char fddiMACMultiple_P ; /* private */ + u_char fddiMACDuplicateAddressCond ;/* private */ + u_char fddiMACAvailablePaths ; + u_short fddiMACCurrentPath ; + LongAddr fddiMACUpstreamNbr ; + LongAddr fddiMACDownstreamNbr ; + LongAddr fddiMACOldUpstreamNbr ; + LongAddr fddiMACOldDownstreamNbr ; + SMTEnum fddiMACDupAddressTest ; + u_short fddiMACRequestedPaths ; + SMTEnum fddiMACDownstreamPORTType ; + ResId fddiMACIndex ; + + /* AddressGrp */ + LongAddr fddiMACSMTAddress ; + + /* OperationGrp */ + Timer_2 fddiMACT_Min ; /* private */ + Timer_2 fddiMACT_ReqMIB ; + Timer_2 fddiMACT_Req ; /* private */ + Timer_2 fddiMACT_Neg ; + Timer_2 fddiMACT_MaxMIB ; + Timer_2 fddiMACT_Max ; /* private */ + Timer_2 fddiMACTvxValueMIB ; + Timer_2 fddiMACTvxValue ; /* private */ + Timer_2 fddiMACT_Pri0 ; + Timer_2 fddiMACT_Pri1 ; + Timer_2 fddiMACT_Pri2 ; + Timer_2 fddiMACT_Pri3 ; + Timer_2 fddiMACT_Pri4 ; + Timer_2 fddiMACT_Pri5 ; + Timer_2 fddiMACT_Pri6 ; + + /* CountersGrp */ + Counter fddiMACFrame_Ct ; + Counter fddiMACCopied_Ct ; + Counter fddiMACTransmit_Ct ; + Counter fddiMACToken_Ct ; + Counter fddiMACError_Ct ; + Counter fddiMACLost_Ct ; + Counter fddiMACTvxExpired_Ct ; + Counter fddiMACNotCopied_Ct ; + Counter fddiMACRingOp_Ct ; + + Counter fddiMACSMTCopied_Ct ; /* private */ + Counter fddiMACSMTTransmit_Ct ; /* private */ + + /* private for delta ratio */ + Counter fddiMACOld_Frame_Ct ; + Counter fddiMACOld_Copied_Ct ; + Counter fddiMACOld_Error_Ct ; + Counter fddiMACOld_Lost_Ct ; + Counter fddiMACOld_NotCopied_Ct ; + + /* FrameErrorConditionGrp */ + u_short fddiMACFrameErrorThreshold ; + u_short fddiMACFrameErrorRatio ; + + /* NotCopiedConditionGrp */ + u_short fddiMACNotCopiedThreshold ; + u_short fddiMACNotCopiedRatio ; + + /* StatusGrp */ + SMTEnum fddiMACRMTState ; + SMTFlag fddiMACDA_Flag ; + SMTFlag fddiMACUNDA_Flag ; + SMTFlag fddiMACFrameErrorFlag ; + SMTFlag fddiMACNotCopiedFlag ; + SMTFlag fddiMACMA_UnitdataAvailable ; + SMTFlag fddiMACHardwarePresent ; + SMTFlag fddiMACMA_UnitdataEnable ; + + } m[NUMMACS] ; +#define MAC0 0 + + struct fddi_mib_a { + ResId fddiPATHIndex ; + u_long fddiPATHSbaPayload ; + u_long fddiPATHSbaOverhead ; + /* fddiPATHConfiguration is built on demand */ + /* u_long fddiPATHConfiguration ; */ + Timer fddiPATHT_Rmode ; + u_long fddiPATHSbaAvailable ; + Timer_2 fddiPATHTVXLowerBound ; + Timer_2 fddiPATHT_MaxLowerBound ; + Timer_2 fddiPATHMaxT_Req ; + } a[NUMPATHS] ; +#define PATH0 0 + + struct fddi_mib_p { + /* ConfigGrp */ + SMTEnum fddiPORTMy_Type ; + SMTEnum fddiPORTNeighborType ; + u_char fddiPORTConnectionPolicies ; + struct { + u_char T_val ; + u_char R_val ; + } fddiPORTMacIndicated ; + SMTEnum fddiPORTCurrentPath ; + /* must be 4: is 32 bit in SMT format + * indices : + * 1 none + * 2 tree + * 3 peer + */ + u_char fddiPORTRequestedPaths[4] ; + u_short fddiPORTMACPlacement ; + u_char fddiPORTAvailablePaths ; + u_char fddiPORTConnectionCapabilities ; + SMTEnum fddiPORTPMDClass ; + ResId fddiPORTIndex ; + + /* OperationGrp */ + SMTEnum fddiPORTMaint_LS ; + SMTEnum fddiPORTPC_LS ; + u_char fddiPORTBS_Flag ; + + /* ErrorCtrsGrp */ + Counter fddiPORTLCTFail_Ct ; + Counter fddiPORTEBError_Ct ; + Counter fddiPORTOldEBError_Ct ; + + /* LerGrp */ + Counter fddiPORTLem_Reject_Ct ; + Counter fddiPORTLem_Ct ; + u_char fddiPORTLer_Estimate ; + u_char fddiPORTLer_Cutoff ; + u_char fddiPORTLer_Alarm ; + + /* StatusGrp */ + SMTEnum fddiPORTConnectState ; + SMTEnum fddiPORTPCMState ; /* real value */ + SMTEnum fddiPORTPCMStateX ; /* value for MIB */ + SMTEnum fddiPORTPC_Withhold ; + SMTFlag fddiPORTHardwarePresent ; + u_char fddiPORTLerFlag ; + + u_char fddiPORTMultiple_U ; /* private */ + u_char fddiPORTMultiple_P ; /* private */ + u_char fddiPORTEB_Condition ; /* private */ + } p[NUMPHYS] ; + struct { + Counter fddiPRIVECF_Req_Rx ; /* ECF req received */ + Counter fddiPRIVECF_Reply_Rx ; /* ECF repl received */ + Counter fddiPRIVECF_Req_Tx ; /* ECF req transm */ + Counter fddiPRIVECF_Reply_Tx ; /* ECF repl transm */ + Counter fddiPRIVPMF_Get_Rx ; /* PMF Get rec */ + Counter fddiPRIVPMF_Set_Rx ; /* PMF Set rec */ + Counter fddiPRIVRDF_Rx ; /* RDF received */ + Counter fddiPRIVRDF_Tx ; /* RDF transmitted */ + } priv ; +} ; + +/* + * OIDs for statistics + */ +#define SMT_OID_CF_STATE 1 /* fddiSMTCF_State */ +#define SMT_OID_PCM_STATE_A 2 /* fddiPORTPCMState port A */ +#define SMT_OID_PCM_STATE_B 17 /* fddiPORTPCMState port B */ +#define SMT_OID_RMT_STATE 3 /* fddiMACRMTState */ +#define SMT_OID_UNA 4 /* fddiMACUpstreamNbr */ +#define SMT_OID_DNA 5 /* fddiMACOldDownstreamNbr */ +#define SMT_OID_ERROR_CT 6 /* fddiMACError_Ct */ +#define SMT_OID_LOST_CT 7 /* fddiMACLost_Ct */ +#define SMT_OID_LEM_CT 8 /* fddiPORTLem_Ct */ +#define SMT_OID_LEM_CT_A 11 /* fddiPORTLem_Ct port A */ +#define SMT_OID_LEM_CT_B 12 /* fddiPORTLem_Ct port B */ +#define SMT_OID_LCT_FAIL_CT 9 /* fddiPORTLCTFail_Ct */ +#define SMT_OID_LCT_FAIL_CT_A 13 /* fddiPORTLCTFail_Ct port A */ +#define SMT_OID_LCT_FAIL_CT_B 14 /* fddiPORTLCTFail_Ct port B */ +#define SMT_OID_LEM_REJECT_CT 10 /* fddiPORTLem_Reject_Ct */ +#define SMT_OID_LEM_REJECT_CT_A 15 /* fddiPORTLem_Reject_Ct port A */ +#define SMT_OID_LEM_REJECT_CT_B 16 /* fddiPORTLem_Reject_Ct port B */ + +/* + * SK MIB + */ +#define SMT_OID_ECF_REQ_RX 20 /* ECF requests received */ +#define SMT_OID_ECF_REPLY_RX 21 /* ECF replies received */ +#define SMT_OID_ECF_REQ_TX 22 /* ECF requests transmitted */ +#define SMT_OID_ECF_REPLY_TX 23 /* ECF replies transmitted */ +#define SMT_OID_PMF_GET_RX 24 /* PMF get requests received */ +#define SMT_OID_PMF_SET_RX 25 /* PMF set requests received */ +#define SMT_OID_RDF_RX 26 /* RDF received */ +#define SMT_OID_RDF_TX 27 /* RDF transmitted */ diff --git a/drivers/net/skfp/h/fplustm.h b/drivers/net/skfp/h/fplustm.h new file mode 100644 index 000000000000..bb9f3b2ac465 --- /dev/null +++ b/drivers/net/skfp/h/fplustm.h @@ -0,0 +1,278 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * AMD Fplus in tag mode data structs + * defs for fplustm.c + */ + +#ifndef _FPLUS_ +#define _FPLUS_ + +#ifndef HW_PTR +#ifdef MEM_MAPPED_IO +#define HW_PTR u_long +#else +#define HW_PTR u_short +#endif +#endif + +/* + * fplus error statistic structure + */ +struct err_st { + u_long err_valid ; /* memory status valid */ + u_long err_abort ; /* memory status receive abort */ + u_long err_e_indicator ; /* error indicator */ + u_long err_crc ; /* error detected (CRC or length) */ + u_long err_llc_frame ; /* LLC frame */ + u_long err_mac_frame ; /* MAC frame */ + u_long err_smt_frame ; /* SMT frame */ + u_long err_imp_frame ; /* implementer frame */ + u_long err_no_buf ; /* no buffer available */ + u_long err_too_long ; /* longer than max. buffer */ + u_long err_bec_stat ; /* beacon state entered */ + u_long err_clm_stat ; /* claim state entered */ + u_long err_sifg_det ; /* short interframe gap detect */ + u_long err_phinv ; /* PHY invalid */ + u_long err_tkiss ; /* token issued */ + u_long err_tkerr ; /* token error */ +} ; + +/* + * Transmit Descriptor struct + */ +struct s_smt_fp_txd { + u_int txd_tbctrl ; /* transmit buffer control */ + u_int txd_txdscr ; /* transmit frame status word */ + u_int txd_tbadr ; /* physical tx buffer address */ + u_int txd_ntdadr ; /* physical pointer to the next TxD */ +#ifdef ENA_64BIT_SUP + u_int txd_tbadr_hi ; /* physical tx buffer addr (high dword)*/ +#endif + char far *txd_virt ; /* virtual pointer to the data frag */ + /* virt pointer to the next TxD */ + struct s_smt_fp_txd volatile far *txd_next ; + struct s_txd_os txd_os ; /* OS - specific struct */ +} ; + +/* + * Receive Descriptor struct + */ +struct s_smt_fp_rxd { + u_int rxd_rbctrl ; /* receive buffer control */ + u_int rxd_rfsw ; /* receive frame status word */ + u_int rxd_rbadr ; /* physical rx buffer address */ + u_int rxd_nrdadr ; /* physical pointer to the next RxD */ +#ifdef ENA_64BIT_SUP + u_int rxd_rbadr_hi ; /* physical tx buffer addr (high dword)*/ +#endif + char far *rxd_virt ; /* virtual pointer to the data frag */ + /* virt pointer to the next RxD */ + struct s_smt_fp_rxd volatile far *rxd_next ; + struct s_rxd_os rxd_os ; /* OS - specific struct */ +} ; + +/* + * Descriptor Union Definition + */ +union s_fp_descr { + struct s_smt_fp_txd t ; /* pointer to the TxD */ + struct s_smt_fp_rxd r ; /* pointer to the RxD */ +} ; + +/* + * TxD Ring Control struct + */ +struct s_smt_tx_queue { + struct s_smt_fp_txd volatile *tx_curr_put ; /* next free TxD */ + struct s_smt_fp_txd volatile *tx_prev_put ; /* shadow put pointer */ + struct s_smt_fp_txd volatile *tx_curr_get ; /* next TxD to release*/ + u_short tx_free ; /* count of free TxD's */ + u_short tx_used ; /* count of used TxD's */ + HW_PTR tx_bmu_ctl ; /* BMU addr for tx start */ + HW_PTR tx_bmu_dsc ; /* BMU addr for curr dsc. */ +} ; + +/* + * RxD Ring Control struct + */ +struct s_smt_rx_queue { + struct s_smt_fp_rxd volatile *rx_curr_put ; /* next RxD to queue into */ + struct s_smt_fp_rxd volatile *rx_prev_put ; /* shadow put pointer */ + struct s_smt_fp_rxd volatile *rx_curr_get ; /* next RxD to fill */ + u_short rx_free ; /* count of free RxD's */ + u_short rx_used ; /* count of used RxD's */ + HW_PTR rx_bmu_ctl ; /* BMU addr for rx start */ + HW_PTR rx_bmu_dsc ; /* BMU addr for curr dsc. */ +} ; + +#define VOID_FRAME_OFF 0x00 +#define CLAIM_FRAME_OFF 0x08 +#define BEACON_FRAME_OFF 0x10 +#define DBEACON_FRAME_OFF 0x18 +#define RX_FIFO_OFF 0x21 /* to get a prime number for */ + /* the RX_FIFO_SPACE */ + +#define RBC_MEM_SIZE 0x8000 +#define SEND_ASYNC_AS_SYNC 0x1 +#define SYNC_TRAFFIC_ON 0x2 + +/* big FIFO memory */ +#define RX_FIFO_SPACE 0x4000 - RX_FIFO_OFF +#define TX_FIFO_SPACE 0x4000 + +#define TX_SMALL_FIFO 0x0900 +#define TX_MEDIUM_FIFO TX_FIFO_SPACE / 2 +#define TX_LARGE_FIFO TX_FIFO_SPACE - TX_SMALL_FIFO + +#define RX_SMALL_FIFO 0x0900 +#define RX_LARGE_FIFO RX_FIFO_SPACE - RX_SMALL_FIFO + +struct s_smt_fifo_conf { + u_short rbc_ram_start ; /* FIFO start address */ + u_short rbc_ram_end ; /* FIFO size */ + u_short rx1_fifo_start ; /* rx queue start address */ + u_short rx1_fifo_size ; /* rx queue size */ + u_short rx2_fifo_start ; /* rx queue start address */ + u_short rx2_fifo_size ; /* rx queue size */ + u_short tx_s_start ; /* sync queue start address */ + u_short tx_s_size ; /* sync queue size */ + u_short tx_a0_start ; /* async queue A0 start address */ + u_short tx_a0_size ; /* async queue A0 size */ + u_short fifo_config_mode ; /* FIFO configuration mode */ +} ; + +#define FM_ADDRX (FM_ADDET|FM_EXGPA0|FM_EXGPA1) + +struct s_smt_fp { + u_short mdr2init ; /* mode register 2 init value */ + u_short mdr3init ; /* mode register 3 init value */ + u_short frselreg_init ; /* frame selection register init val */ + u_short rx_mode ; /* address mode broad/multi/promisc */ + u_short nsa_mode ; + u_short rx_prom ; + u_short exgpa ; + + struct err_st err_stats ; /* error statistics */ + + /* + * MAC buffers + */ + struct fddi_mac_sf { /* special frame build buffer */ + u_char mac_fc ; + struct fddi_addr mac_dest ; + struct fddi_addr mac_source ; + u_char mac_info[0x20] ; + } mac_sfb ; + + + /* + * queues + */ +#define QUEUE_S 0 +#define QUEUE_A0 1 +#define QUEUE_R1 0 +#define QUEUE_R2 1 +#define USED_QUEUES 2 + + /* + * queue pointers; points to the queue dependent variables + */ + struct s_smt_tx_queue *tx[USED_QUEUES] ; + struct s_smt_rx_queue *rx[USED_QUEUES] ; + + /* + * queue dependent variables + */ + struct s_smt_tx_queue tx_q[USED_QUEUES] ; + struct s_smt_rx_queue rx_q[USED_QUEUES] ; + + /* + * FIFO configuration struct + */ + struct s_smt_fifo_conf fifo ; + + /* last formac status */ + u_short s2u ; + u_short s2l ; + + /* calculated FORMAC+ reg.addr. */ + HW_PTR fm_st1u ; + HW_PTR fm_st1l ; + HW_PTR fm_st2u ; + HW_PTR fm_st2l ; + HW_PTR fm_st3u ; + HW_PTR fm_st3l ; + + + /* + * multicast table + */ +#define FPMAX_MULTICAST 32 +#define SMT_MAX_MULTI 4 + struct { + struct s_fpmc { + struct fddi_addr a ; /* mc address */ + u_char n ; /* usage counter */ + u_char perm ; /* flag: permanent */ + } table[FPMAX_MULTICAST] ; + } mc ; + struct fddi_addr group_addr ; + u_long func_addr ; /* functional address */ + int smt_slots_used ; /* count of table entries for the SMT */ + int os_slots_used ; /* count of table entries */ + /* used by the os-specific module */ +} ; + +/* + * modes for mac_set_rx_mode() + */ +#define RX_ENABLE_ALLMULTI 1 /* enable all multicasts */ +#define RX_DISABLE_ALLMULTI 2 /* disable "enable all multicasts" */ +#define RX_ENABLE_PROMISC 3 /* enable promiscous */ +#define RX_DISABLE_PROMISC 4 /* disable promiscous */ +#define RX_ENABLE_NSA 5 /* enable reception of NSA frames */ +#define RX_DISABLE_NSA 6 /* disable reception of NSA frames */ + + +/* + * support for byte reversal in AIX + * (descriptors and pointers must be byte reversed in memory + * CPU is big endian; M-Channel is little endian) + */ +#ifdef AIX +#define MDR_REV +#define AIX_REVERSE(x) ((((x)<<24L)&0xff000000L) + \ + (((x)<< 8L)&0x00ff0000L) + \ + (((x)>> 8L)&0x0000ff00L) + \ + (((x)>>24L)&0x000000ffL)) +#else +#ifndef AIX_REVERSE +#define AIX_REVERSE(x) (x) +#endif +#endif + +#ifdef MDR_REV +#define MDR_REVERSE(x) ((((x)<<24L)&0xff000000L) + \ + (((x)<< 8L)&0x00ff0000L) + \ + (((x)>> 8L)&0x0000ff00L) + \ + (((x)>>24L)&0x000000ffL)) +#else +#ifndef MDR_REVERSE +#define MDR_REVERSE(x) (x) +#endif +#endif + +#endif diff --git a/drivers/net/skfp/h/hwmtm.h b/drivers/net/skfp/h/hwmtm.h new file mode 100644 index 000000000000..4e360af07d77 --- /dev/null +++ b/drivers/net/skfp/h/hwmtm.h @@ -0,0 +1,424 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _HWM_ +#define _HWM_ + +#include "h/mbuf.h" + +/* + * MACRO for DMA synchronization: + * The descriptor 'desc' is flushed for the device 'flag'. + * Devices are the CPU (DDI_DMA_SYNC_FORCPU) and the + * adapter (DDI_DMA_SYNC_FORDEV). + * + * 'desc' Pointer to a Rx or Tx descriptor. + * 'flag' Flag for direction (view for CPU or DEVICE) that + * should be synchronized. + * + * Empty macros and defines are specified here. The real macro + * is os-specific and should be defined in osdef1st.h. + */ +#ifndef DRV_BUF_FLUSH +#define DRV_BUF_FLUSH(desc,flag) +#define DDI_DMA_SYNC_FORCPU +#define DDI_DMA_SYNC_FORDEV +#endif + + /* + * hardware modul dependent receive modes + */ +#define RX_ENABLE_PASS_SMT 21 +#define RX_DISABLE_PASS_SMT 22 +#define RX_ENABLE_PASS_NSA 23 +#define RX_DISABLE_PASS_NSA 24 +#define RX_ENABLE_PASS_DB 25 +#define RX_DISABLE_PASS_DB 26 +#define RX_DISABLE_PASS_ALL 27 +#define RX_DISABLE_LLC_PROMISC 28 +#define RX_ENABLE_LLC_PROMISC 29 + + +#ifndef DMA_RD +#define DMA_RD 1 /* memory -> hw */ +#endif +#ifndef DMA_WR +#define DMA_WR 2 /* hw -> memory */ +#endif +#define SMT_BUF 0x80 + + /* + * bits of the frame status byte + */ +#define EN_IRQ_EOF 0x02 /* get IRQ after end of frame transmission */ +#define LOC_TX 0x04 /* send frame to the local SMT */ +#define LAST_FRAG 0x08 /* last TxD of the frame */ +#define FIRST_FRAG 0x10 /* first TxD of the frame */ +#define LAN_TX 0x20 /* send frame to network if set */ +#define RING_DOWN 0x40 /* error: unable to send, ring down */ +#define OUT_OF_TXD 0x80 /* error: not enough TxDs available */ + + +#ifndef NULL +#define NULL 0 +#endif + +#ifdef LITTLE_ENDIAN +#define HWM_REVERSE(x) (x) +#else +#define HWM_REVERSE(x) ((((x)<<24L)&0xff000000L) + \ + (((x)<< 8L)&0x00ff0000L) + \ + (((x)>> 8L)&0x0000ff00L) + \ + (((x)>>24L)&0x000000ffL)) +#endif + +#define C_INDIC (1L<<25) +#define A_INDIC (1L<<26) +#define RD_FS_LOCAL 0x80 + + /* + * DEBUG FLAGS + */ +#define DEBUG_SMTF 1 +#define DEBUG_SMT 2 +#define DEBUG_ECM 3 +#define DEBUG_RMT 4 +#define DEBUG_CFM 5 +#define DEBUG_PCM 6 +#define DEBUG_SBA 7 +#define DEBUG_ESS 8 + +#define DB_HWM_RX 10 +#define DB_HWM_TX 11 +#define DB_HWM_GEN 12 + +struct s_mbuf_pool { +#ifndef MB_OUTSIDE_SMC + SMbuf mb[MAX_MBUF] ; /* mbuf pool */ +#endif + SMbuf *mb_start ; /* points to the first mb */ + SMbuf *mb_free ; /* free queue */ +} ; + +struct hwm_r { + /* + * hardware modul specific receive variables + */ + u_int len ; /* length of the whole frame */ + char *mb_pos ; /* SMbuf receive position */ +} ; + +struct hw_modul { + /* + * All hardware modul specific variables + */ + struct s_mbuf_pool mbuf_pool ; + struct hwm_r r ; + + union s_fp_descr volatile *descr_p ; /* points to the desriptor area */ + + u_short pass_SMT ; /* pass SMT frames */ + u_short pass_NSA ; /* pass all NSA frames */ + u_short pass_DB ; /* pass Direct Beacon Frames */ + u_short pass_llc_promisc ; /* pass all llc frames (default ON) */ + + SMbuf *llc_rx_pipe ; /* points to the first queued llc fr */ + SMbuf *llc_rx_tail ; /* points to the last queued llc fr */ + int queued_rx_frames ; /* number of queued frames */ + + SMbuf *txd_tx_pipe ; /* points to first mb in the txd ring */ + SMbuf *txd_tx_tail ; /* points to last mb in the txd ring */ + int queued_txd_mb ; /* number of SMT MBufs in txd ring */ + + int rx_break ; /* rev. was breaked because ind. off */ + int leave_isr ; /* leave fddi_isr immedeately if set */ + int isr_flag ; /* set, when HWM is entered from isr */ + /* + * varaibles for the current transmit frame + */ + struct s_smt_tx_queue *tx_p ; /* pointer to the transmit queue */ + u_long tx_descr ; /* tx descriptor for FORMAC+ */ + int tx_len ; /* tx frame length */ + SMbuf *tx_mb ; /* SMT tx MBuf pointer */ + char *tx_data ; /* data pointer to the SMT tx Mbuf */ + + int detec_count ; /* counter for out of RxD condition */ + u_long rx_len_error ; /* rx len FORMAC != sum of fragments */ +} ; + + +/* + * DEBUG structs and macros + */ + +#ifdef DEBUG +struct os_debug { + int hwm_rx ; + int hwm_tx ; + int hwm_gen ; +} ; +#endif + +#ifdef DEBUG +#ifdef DEBUG_BRD +#define DB_P smc->debug +#else +#define DB_P debug +#endif + +#define DB_RX(a,b,c,lev) if (DB_P.d_os.hwm_rx >= (lev)) printf(a,b,c) +#define DB_TX(a,b,c,lev) if (DB_P.d_os.hwm_tx >= (lev)) printf(a,b,c) +#define DB_GEN(a,b,c,lev) if (DB_P.d_os.hwm_gen >= (lev)) printf(a,b,c) +#else /* DEBUG */ +#define DB_RX(a,b,c,lev) +#define DB_TX(a,b,c,lev) +#define DB_GEN(a,b,c,lev) +#endif /* DEBUG */ + +#ifndef SK_BREAK +#define SK_BREAK() +#endif + + +/* + * HWM Macros + */ + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_TX_PHYS) + * u_long HWM_GET_TX_PHYS(txd) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to read + * the physical address of the specified TxD. + * + * para txd pointer to the TxD + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_TX_PHYS(txd) (u_long)AIX_REVERSE((txd)->txd_tbadr) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_TX_LEN) + * int HWM_GET_TX_LEN(txd) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to read + * the fragment length of the specified TxD + * + * para rxd pointer to the TxD + * + * return the length of the fragment in bytes + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_TX_LEN(txd) ((int)AIX_REVERSE((txd)->txd_tbctrl)& RD_LENGTH) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_TX_USED) + * txd *HWM_GET_TX_USED(smc,queue) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to get the + * number of used TxDs for the queue, specified by the index. + * + * para queue the number of the send queue: Can be specified by + * QUEUE_A0, QUEUE_S or (frame_status & QUEUE_A0) + * + * return number of used TxDs for this send queue + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_TX_USED(smc,queue) (int) (smc)->hw.fp.tx_q[queue].tx_used + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_CURR_TXD) + * txd *HWM_GET_CURR_TXD(smc,queue) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to get the + * pointer to the TxD which points to the current queue put + * position. + * + * para queue the number of the send queue: Can be specified by + * QUEUE_A0, QUEUE_S or (frame_status & QUEUE_A0) + * + * return pointer to the current TxD + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_CURR_TXD(smc,queue) (struct s_smt_fp_txd volatile *)\ + (smc)->hw.fp.tx_q[queue].tx_curr_put + +/* + * BEGIN_MANUAL_ENTRY(HWM_TX_CHECK) + * void HWM_TX_CHECK(smc,frame_status,low_water) + * + * function MACRO (hardware module, hwmtm.h) + * This macro is invoked by the OS-specific before it left it's + * driver_send function. This macro calls mac_drv_clear_txd + * if the free TxDs of the current transmit queue is equal or + * lower than the given low water mark. + * + * para frame_status status of the frame, see design description + * low_water low water mark of free TxD's + * + * END_MANUAL_ENTRY + */ +#ifndef HWM_NO_FLOW_CTL +#define HWM_TX_CHECK(smc,frame_status,low_water) {\ + if ((low_water)>=(smc)->hw.fp.tx_q[(frame_status)&QUEUE_A0].tx_free) {\ + mac_drv_clear_txd(smc) ;\ + }\ +} +#else +#define HWM_TX_CHECK(smc,frame_status,low_water) mac_drv_clear_txd(smc) +#endif + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_RX_FRAG_LEN) + * int HWM_GET_RX_FRAG_LEN(rxd) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to read + * the fragment length of the specified RxD + * + * para rxd pointer to the RxD + * + * return the length of the fragment in bytes + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_RX_FRAG_LEN(rxd) ((int)AIX_REVERSE((rxd)->rxd_rbctrl)& \ + RD_LENGTH) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_RX_PHYS) + * u_long HWM_GET_RX_PHYS(rxd) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to read + * the physical address of the specified RxD. + * + * para rxd pointer to the RxD + * + * return the RxD's physical pointer to the data fragment + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_RX_PHYS(rxd) (u_long)AIX_REVERSE((rxd)->rxd_rbadr) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_RX_USED) + * int HWM_GET_RX_USED(smc) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to get + * the count of used RXDs in receive queue 1. + * + * return the used RXD count of receive queue 1 + * + * NOTE: Remember, because of an ASIC bug at least one RXD should be unused + * in the descriptor ring ! + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_RX_USED(smc) ((int)(smc)->hw.fp.rx_q[QUEUE_R1].rx_used) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_RX_FREE) + * int HWM_GET_RX_FREE(smc) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to get + * the rxd_free count of receive queue 1. + * + * return the rxd_free count of receive queue 1 + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_RX_FREE(smc) ((int)(smc)->hw.fp.rx_q[QUEUE_R1].rx_free-1) + +/* + * BEGIN_MANUAL_ENTRY(HWM_GET_CURR_RXD) + * rxd *HWM_GET_CURR_RXD(smc) + * + * function MACRO (hardware module, hwmtm.h) + * This macro may be invoked by the OS-specific module to get the + * pointer to the RxD which points to the current queue put + * position. + * + * return pointer to the current RxD + * + * END_MANUAL_ENTRY + */ +#define HWM_GET_CURR_RXD(smc) (struct s_smt_fp_rxd volatile *)\ + (smc)->hw.fp.rx_q[QUEUE_R1].rx_curr_put + +/* + * BEGIN_MANUAL_ENTRY(HWM_RX_CHECK) + * void HWM_RX_CHECK(smc,low_water) + * + * function MACRO (hardware module, hwmtm.h) + * This macro is invoked by the OS-specific before it left the + * function mac_drv_rx_complete. This macro calls mac_drv_fill_rxd + * if the number of used RxDs is equal or lower than the + * the given low water mark. + * + * para low_water low water mark of used RxD's + * + * END_MANUAL_ENTRY + */ +#ifndef HWM_NO_FLOW_CTL +#define HWM_RX_CHECK(smc,low_water) {\ + if ((low_water) >= (smc)->hw.fp.rx_q[QUEUE_R1].rx_used) {\ + mac_drv_fill_rxd(smc) ;\ + }\ +} +#else +#define HWM_RX_CHECK(smc,low_water) mac_drv_fill_rxd(smc) +#endif + +#ifndef HWM_EBASE +#define HWM_EBASE 500 +#endif + +#define HWM_E0001 HWM_EBASE + 1 +#define HWM_E0001_MSG "HWM: Wrong size of s_rxd_os struct" +#define HWM_E0002 HWM_EBASE + 2 +#define HWM_E0002_MSG "HWM: Wrong size of s_txd_os struct" +#define HWM_E0003 HWM_EBASE + 3 +#define HWM_E0003_MSG "HWM: smt_free_mbuf() called with NULL pointer" +#define HWM_E0004 HWM_EBASE + 4 +#define HWM_E0004_MSG "HWM: Parity error rx queue 1" +#define HWM_E0005 HWM_EBASE + 5 +#define HWM_E0005_MSG "HWM: Encoding error rx queue 1" +#define HWM_E0006 HWM_EBASE + 6 +#define HWM_E0006_MSG "HWM: Encoding error async tx queue" +#define HWM_E0007 HWM_EBASE + 7 +#define HWM_E0007_MSG "HWM: Encoding error sync tx queue" +#define HWM_E0008 HWM_EBASE + 8 +#define HWM_E0008_MSG "" +#define HWM_E0009 HWM_EBASE + 9 +#define HWM_E0009_MSG "HWM: Out of RxD condition detected" +#define HWM_E0010 HWM_EBASE + 10 +#define HWM_E0010_MSG "HWM: A protocol layer has tried to send a frame with an invalid frame control" +#define HWM_E0011 HWM_EBASE + 11 +#define HWM_E0011_MSG "HWM: mac_drv_clear_tx_queue was called although the hardware wasn't stopped" +#define HWM_E0012 HWM_EBASE + 12 +#define HWM_E0012_MSG "HWM: mac_drv_clear_rx_queue was called although the hardware wasn't stopped" +#define HWM_E0013 HWM_EBASE + 13 +#define HWM_E0013_MSG "HWM: mac_drv_repair_descr was called although the hardware wasn't stopped" + +#endif diff --git a/drivers/net/skfp/h/lnkstat.h b/drivers/net/skfp/h/lnkstat.h new file mode 100644 index 000000000000..c73dcd96a40f --- /dev/null +++ b/drivers/net/skfp/h/lnkstat.h @@ -0,0 +1,84 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * Definition of the Error Log Structure + * This structure will be copied into the Error Log buffer + * during the NDIS General Request ReadErrorLog by the MAC Driver + */ + +struct s_error_log { + + /* + * place holder for token ring adapter error log (zeros) + */ + u_char reserved_0 ; /* byte 0 inside Error Log */ + u_char reserved_1 ; /* byte 1 */ + u_char reserved_2 ; /* byte 2 */ + u_char reserved_3 ; /* byte 3 */ + u_char reserved_4 ; /* byte 4 */ + u_char reserved_5 ; /* byte 5 */ + u_char reserved_6 ; /* byte 6 */ + u_char reserved_7 ; /* byte 7 */ + u_char reserved_8 ; /* byte 8 */ + u_char reserved_9 ; /* byte 9 */ + u_char reserved_10 ; /* byte 10 */ + u_char reserved_11 ; /* byte 11 */ + u_char reserved_12 ; /* byte 12 */ + u_char reserved_13 ; /* byte 13 */ + + /* + * FDDI link statistics + */ +/* + * smt error low + */ +#define SMT_ERL_AEB (1<<15) /* A elast. buffer */ +#define SMT_ERL_BLC (1<<14) /* B link error condition */ +#define SMT_ERL_ALC (1<<13) /* A link error condition */ +#define SMT_ERL_NCC (1<<12) /* not copied condition */ +#define SMT_ERL_FEC (1<<11) /* frame error condition */ + +/* + * smt event low + */ +#define SMT_EVL_NCE (1<<5) + + u_short smt_error_low ; /* byte 14/15 */ + u_short smt_error_high ; /* byte 16/17 */ + u_short smt_event_low ; /* byte 18/19 */ + u_short smt_event_high ; /* byte 20/21 */ + u_short connection_policy_violation ; /* byte 22/23 */ + u_short port_event ; /* byte 24/25 */ + u_short set_count_low ; /* byte 26/27 */ + u_short set_count_high ; /* byte 28/29 */ + u_short aci_id_code ; /* byte 30/31 */ + u_short purge_frame_counter ; /* byte 32/33 */ + + /* + * CMT and RMT state machines + */ + u_short ecm_state ; /* byte 34/35 */ + u_short pcm_a_state ; /* byte 36/37 */ + u_short pcm_b_state ; /* byte 38/39 */ + u_short cfm_state ; /* byte 40/41 */ + u_short rmt_state ; /* byte 42/43 */ + + u_short not_used[30] ; /* byte 44-103 */ + + u_short ucode_version_level ; /* byte 104/105 */ + + u_short not_used_1 ; /* byte 106/107 */ + u_short not_used_2 ; /* byte 108/109 */ +} ; diff --git a/drivers/net/skfp/h/mbuf.h b/drivers/net/skfp/h/mbuf.h new file mode 100644 index 000000000000..b339d1f2e0e5 --- /dev/null +++ b/drivers/net/skfp/h/mbuf.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _MBUF_ +#define _MBUF_ + +#ifndef PCI +#define M_SIZE 4550 +#else +#define M_SIZE 4504 +#endif + +#ifndef MAX_MBUF +#define MAX_MBUF 4 +#endif + +#ifndef NO_STD_MBUF +#define sm_next m_next +#define sm_off m_off +#define sm_len m_len +#define sm_data m_data +#define SMbuf Mbuf +#define mtod smtod +#define mtodoff smtodoff +#endif + +struct s_mbuf { + struct s_mbuf *sm_next ; /* low level linked list */ + short sm_off ; /* offset in m_data */ + u_int sm_len ; /* len of data */ +#ifdef PCI + int sm_use_count ; +#endif + char sm_data[M_SIZE] ; +} ; + +typedef struct s_mbuf SMbuf ; + +/* mbuf head, to typed data */ +#define smtod(x,t) ((t)((x)->sm_data + (x)->sm_off)) +#define smtodoff(x,t,o) ((t)((x)->sm_data + (o))) + +#endif /* _MBUF_ */ diff --git a/drivers/net/skfp/h/osdef1st.h b/drivers/net/skfp/h/osdef1st.h new file mode 100644 index 000000000000..9c089682efed --- /dev/null +++ b/drivers/net/skfp/h/osdef1st.h @@ -0,0 +1,121 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * Operating system-dependant definitions that have to be defined + * before any other header files are included. + */ + +// HWM (HardWare Module) Definitions +// ----------------------- + +#ifdef __LITTLE_ENDIAN +#define LITTLE_ENDIAN +#else +#define BIG_ENDIAN +#endif + +// this is set in the makefile +// #define PCI /* only PCI adapters supported by this driver */ +// #define MEM_MAPPED_IO /* use memory mapped I/O */ + + +#define USE_CAN_ADDR /* DA and SA in MAC header are canonical. */ + +#define MB_OUTSIDE_SMC /* SMT Mbufs outside of smc struct. */ + +// ----------------------- + + +// SMT Definitions +// ----------------------- +#define SYNC /* allow synchronous frames */ + +// #define SBA /* Synchronous Bandwidth Allocator support */ + /* not available as free source */ + +#define ESS /* SBA End Station Support */ + +#define SMT_PANIC(smc, nr, msg) printk(KERN_INFO "SMT PANIC: code: %d, msg: %s\n",nr,msg) + + +#ifdef DEBUG +#define printf(s,args...) printk(KERN_INFO s, ## args) +#endif + +// #define HW_PTR u_long +// ----------------------- + + + +// HWM and OS-specific buffer definitions +// ----------------------- + +// default number of receive buffers. +#define NUM_RECEIVE_BUFFERS 10 + +// default number of transmit buffers. +#define NUM_TRANSMIT_BUFFERS 10 + +// Number of SMT buffers (Mbufs). +#define NUM_SMT_BUF 4 + +// Number of TXDs for asynchronous transmit queue. +#define HWM_ASYNC_TXD_COUNT (NUM_TRANSMIT_BUFFERS + NUM_SMT_BUF) + +// Number of TXDs for synchronous transmit queue. +#define HWM_SYNC_TXD_COUNT HWM_ASYNC_TXD_COUNT + + +// Number of RXDs for receive queue #1. +// Note: Workaround for ASIC Errata #7: One extra RXD is required. +#if (NUM_RECEIVE_BUFFERS > 100) +#define SMT_R1_RXD_COUNT (1 + 100) +#else +#define SMT_R1_RXD_COUNT (1 + NUM_RECEIVE_BUFFERS) +#endif + +// Number of RXDs for receive queue #2. +#define SMT_R2_RXD_COUNT 0 // Not used. +// ----------------------- + + + +/* + * OS-specific part of the transmit/receive descriptor structure (TXD/RXD). + * + * Note: The size of these structures must follow this rule: + * + * sizeof(struct) + 2*sizeof(void*) == n * 16, n >= 1 + * + */ + +struct s_txd_os { // os-specific part of transmit descriptor + struct sk_buff *skb; + long padding; +} ; + +struct s_rxd_os { // os-specific part of receive descriptor + struct sk_buff *skb; + long padding; +} ; + + +/* + * So we do not need to make too many modifications to the generic driver + * parts, we take advantage of the AIX byte swapping macro interface. + */ + +#define AIX_REVERSE(x) ((u32)le32_to_cpu((u32)(x))) +#define MDR_REVERSE(x) ((u32)le32_to_cpu((u32)(x))) diff --git a/drivers/net/skfp/h/sba.h b/drivers/net/skfp/h/sba.h new file mode 100644 index 000000000000..df716cd5784a --- /dev/null +++ b/drivers/net/skfp/h/sba.h @@ -0,0 +1,142 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * Synchronous Bandwith Allocation (SBA) structs + */ + +#ifndef _SBA_ +#define _SBA_ + +#include "h/mbuf.h" +#include "h/sba_def.h" + +#ifdef SBA + +/* Timer Cell Template */ +struct timer_cell { + struct timer_cell *next_ptr ; + struct timer_cell *prev_ptr ; + u_long start_time ; + struct s_sba_node_vars *node_var ; +} ; + +/* + * Node variables + */ +struct s_sba_node_vars { + u_char change_resp_flag ; + u_char report_resp_flag ; + u_char change_req_flag ; + u_char report_req_flag ; + long change_amount ; + long node_overhead ; + long node_payload ; + u_long node_status ; + u_char deallocate_status ; + u_char timer_state ; + u_short report_cnt ; + long lastrep_req_tranid ; + struct fddi_addr mac_address ; + struct s_sba_sessions *node_sessions ; + struct timer_cell timer ; +} ; + +/* + * Session variables + */ +struct s_sba_sessions { + u_long deallocate_status ; + long session_overhead ; + u_long min_segment_size ; + long session_payload ; + u_long session_status ; + u_long sba_category ; + long lastchg_req_tranid ; + u_short session_id ; + u_char class ; + u_char fddi2 ; + u_long max_t_neg ; + struct s_sba_sessions *next_session ; +} ; + +struct s_sba { + + struct s_sba_node_vars node[MAX_NODES] ; + struct s_sba_sessions session[MAX_SESSIONS] ; + + struct s_sba_sessions *free_session ; /* points to the first */ + /* free session */ + + struct timer_cell *tail_timer ; /* points to the last timer cell */ + + /* + * variables for allocation actions + */ + long total_payload ; /* Total Payload */ + long total_overhead ; /* Total Overhead */ + long sba_allocatable ; /* allocatable sync bandwidth */ + + /* + * RAF message receive parameters + */ + long msg_path_index ; /* Path Type */ + long msg_sba_pl_req ; /* Payload Request */ + long msg_sba_ov_req ; /* Overhead Request */ + long msg_mib_pl ; /* Current Payload for this Path */ + long msg_mib_ov ; /* Current Overhead for this Path*/ + long msg_category ; /* Category of the Allocation */ + u_long msg_max_t_neg ; /* longest T_Neg acceptable */ + u_long msg_min_seg_siz ; /* minimum segement size */ + struct smt_header *sm ; /* points to the rec message */ + struct fddi_addr *msg_alloc_addr ; /* Allocation Address */ + + /* + * SBA variables + */ + u_long sba_t_neg ; /* holds the last T_NEG */ + long sba_max_alloc ; /* the parsed value of SBAAvailable */ + + /* + * SBA state machine variables + */ + short sba_next_state ; /* the next state of the SBA */ + char sba_command ; /* holds the execuded SBA cmd */ + u_char sba_available ; /* parsed value after possible check */ +} ; + +#endif /* SBA */ + + /* + * variables for the End Station Support + */ +struct s_ess { + + /* + * flags and counters + */ + u_char sync_bw_available ; /* is set if sync bw is allocated */ + u_char local_sba_active ; /* set when a local sba is available */ + char raf_act_timer_poll ; /* activate the timer to send allc req */ + char timer_count ; /* counts every timer function call */ + + SMbuf *sba_reply_pend ; /* local reply for the sba is pending */ + + /* + * variables for the ess bandwidth control + */ + long sync_bw ; /* holds the allocaed sync bw */ + u_long alloc_trans_id ; /* trans id of the last alloc req */ +} ; +#endif diff --git a/drivers/net/skfp/h/sba_def.h b/drivers/net/skfp/h/sba_def.h new file mode 100644 index 000000000000..0459a095d0cd --- /dev/null +++ b/drivers/net/skfp/h/sba_def.h @@ -0,0 +1,76 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#define PHYS 0 /* physical addr */ +#define PERM_ADDR 0x80 /* permanet address */ +#define SB_STATIC 0x00000001 +#define MAX_PAYLOAD 1562 +#define PRIMARY_RING 0x00000001 +#ifndef NULL +#define NULL 0x00 +#endif + +/*********************** SB_Input Variable Values ***********************/ +/* may be needed when ever the SBA state machine is called */ + +#define UNKNOWN_SYNC_SOURCE 0x0001 +#define REQ_ALLOCATION 0x0002 +#define REPORT_RESP 0x0003 +#define CHANGE_RESP 0x0004 +#define TNEG 0x0005 +#define NIF 0x0006 +#define SB_STOP 0x0007 +#define SB_START 0x0008 +#define REPORT_TIMER 0x0009 +#define CHANGE_REQUIRED 0x000A + +#define DEFAULT_OV 50 + +#ifdef SBA +/**************************** SBA STATES *****************************/ + +#define SBA_STANDBY 0x00000000 +#define SBA_ACTIVE 0x00000001 +#define SBA_RECOVERY 0x00000002 +#define SBA_REPORT 0x00000003 +#define SBA_CHANGE 0x00000004 + +/**************************** OTHERS *********************************/ + +#define FIFTY_PERCENT 50 /* bytes per second */ +#define MAX_SESSIONS 150 +#define TWO_MINUTES 13079 /* 9.175 ms/tick */ +#define FIFTY_BYTES 50 +#define SBA_DENIED 0x0000000D +#define I_NEED_ONE 0x00000000 +#define MAX_NODES 50 +/*#define T_REPORT 0x59682F00L*/ /* 120s/80ns in Hex */ +#define TWO_MIN 120 /* seconds */ +#define SBA_ST_UNKNOWN 0x00000002 +#define SBA_ST_ACTIVE 0x00000001 +#define S_CLEAR 0x00000000L +#define ZERO 0x00000000 +#define FULL 0x00000000 /* old: 0xFFFFFFFFF */ +#define S_SET 0x00000001L +#define LOW_PRIO 0x02 /* ??????? */ +#define OK 0x01 /* ??????? */ +#define NOT_OK 0x00 /* ??????? */ + +/****************************************/ +/* deallocate_status[ni][si] values */ +/****************************************/ +#define TX_CHANGE 0X00000001L +#define PENDING 0x00000002L +#define NONE 0X00000000L +#endif diff --git a/drivers/net/skfp/h/skfbi.h b/drivers/net/skfp/h/skfbi.h new file mode 100644 index 000000000000..f127bddb8be6 --- /dev/null +++ b/drivers/net/skfp/h/skfbi.h @@ -0,0 +1,1920 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _SKFBI_H_ +#define _SKFBI_H_ + +#ifdef SYNC +#define exist_board_far exist_board +#define get_board_para_far get_board_para +#endif + +/* + * physical address offset + IO-Port base address + */ +#ifndef PCI +#define ADDR(a) ((a)+smc->hw.iop) +#define ADDRS(smc,a) ((a)+(smc)->hw.iop) +#endif + +/* + * FDDI-Fx (x := {I(SA), E(ISA), M(CA), P(CI)}) + * address calculation & function defines + */ + +#ifdef EISA + +/* + * Configuration PROM: !! all 8-Bit IO's !! + * |<- MAC-Address ->| + * /-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-/ + * val: |PROD_ID0..3| | free | |00|00|5A|40| |nn|mm|00|00| + * /-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-//-+--+--+--+--+-/ + * IO- ^ ^ ^ ^ ^ + * port 0C80 0C83 0C88 0C90 0C98 + * | \ + * | \ + * | \______________________________________________ + * EISA Expansion Board Product ID: \ + * BIT: |7 6 5 4 3 2 1 0| \ + * | PROD_ID0 | PROD_ID1 | PROD_ID2 | PROD_ID3 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * |0| MAN_C0 | MAN_C1 | MAN_C2 | PROD1 | PROD0 | REV1 | REV0 | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * ^=reserved | product numb. | revision numb | + * MAN_Cx = compressed manufacterer code (x:=0..2) + * ASCII : 'A'..'Z' : 0x41..0x5A -> compr.(c-0x40) : 0x01..0x1A (5Bits!) + */ + +#ifndef MULT_OEM +#ifndef OEM_CONCEPT +#define MAN_C0 ('S'-0x40) +#define MAN_C1 ('K'-0x40) +#define MAN_C2 ('D'-0x40) +#define PROD_ID0 (u_char)((MAN_C0<<2) | (MAN_C1>>3)) +#define PROD_ID1 (u_char)(((MAN_C1<<5) & 0xff) | MAN_C2) +#define PROD_ID2 (u_char)(1) /* prod. nr. */ +#define PROD_ID3 (u_char)(0) /* rev. nr. */ + +#ifndef OEM_USER_DATA +#define OEM_USER_DATA "SK-NET FDDI V2.0 Userdata" +#endif +#else /* OEM_CONCEPT */ + +/* MAN_C(0|1|2) no longer present (ra). */ +#define PROD_ID0 (u_char)OEM_PROD_ID0 +#define PROD_ID1 (u_char)OEM_PROD_ID1 +#define PROD_ID2 (u_char)OEM_PROD_ID2 +#define PROD_ID3 (u_char)OEM_PROD_ID3 +#endif /* OEM_CONCEPT */ + +#define SKLOGO PROD_ID0, PROD_ID1, PROD_ID2, PROD_ID3 +#endif /* MULT_OEM */ + +#define SADDRL (0) /* start address SKLOGO */ +#define SA_MAC (0x10) /* start addr. MAC_AD within the PROM */ +#define PRA_OFF (4) +#define SA_PMD_TYPE (8) /* start addr. PMD-Type */ + +#define SKFDDI_PSZ 32 /* address PROM size */ + +/* + * address transmision from logical to physical offset address on board + */ +#define FMA(a) (0x0400|((a)<<1)) /* FORMAC+ (r/w) */ +#define P1A(a) (0x0800|((a)<<1)) /* PLC1 (r/w) */ +#define P2A(a) (0x0840|((a)<<1)) /* PLC2 (r/w) */ +#define TIA(a) (0x0880|((a)<<1)) /* Timer (r/w) */ +#define PRA(a) (0x0c80| (a)) /* configuration PROM */ +#define C0A(a) (0x0c84| (a)) /* config. RAM */ +#define C1A(a) (0x0ca0| (a)) /* IRQ-, DMA-nr., EPROM type */ +#define C2A(a) (0x0ca4| (a)) /* EPROM and PAGE selector */ + +#define CONF C0A(0) /* config RAM (card enable bit port) */ +#define PGRA C2A(0) /* Flash page register */ +#define CDID PRA(0) /* Card ID I/O port addr. offset */ + + +/* + * physical address offset + slot specific IO-Port base address + */ +#define FM_A(a) (FMA(a)+smc->hw.iop) /* FORMAC Plus physical addr */ +#define P1_A(a) (P1A(a)+smc->hw.iop) /* PLC1 (r/w) */ +#define P2_A(a) (P2A(a)+smc->hw.iop) /* PLC2 (r/w) */ +#define TI_A(a) (TIA(a)+smc->hw.iop) /* Timer (r/w) */ +#define PR_A(a) (PRA(a)+smc->hw.iop) /* config. PROM */ +#define C0_A(a) (C0A(a)+smc->hw.iop) /* config. RAM */ +#define C1_A(a) (C1A(a)+smc->hw.iop) /* config. RAM */ +#define C2_A(a) (C2A(a)+smc->hw.iop) /* config. RAM */ + + +#define CSRA 0x0008 /* control/status register address (r/w) */ +#define ISRA 0x0008 /* int. source register address (upper 8Bits) */ +#define PLC1I 0x001a /* clear PLC1 interrupt (write only) */ +#define PLC2I 0x0020 /* clear PLC2 interrupt (write only) */ +#define CSFA 0x001c /* control/status FIFO BUSY flags (read only) */ +#define RQAA 0x001c /* Request reg. (write only) */ +#define WCTA 0x001e /* word counter (r/w) */ +#define FFLAG 0x005e /* FLAG/V_FULL (FIFO almost full, write only)*/ + +#define CSR_A (CSRA+smc->hw.iop) /* control/status register address (r/w) */ +#ifdef UNIX +#define CSR_AS(smc) (CSRA+(smc)->hw.iop) /* control/status register address (r/w) */ +#endif +#define ISR_A (ISRA+smc->hw.iop) /* int. source register address (upper 8Bits) */ +#define PLC1_I (PLC1I+smc->hw.iop) /* clear PLC1 internupt (write only) */ +#define PLC2_I (PLC2I+smc->hw.iop) /* clear PLC2 interrupt (write only) */ +#define CSF_A (CSFA+smc->hw.iop) /* control/status FIFO BUSY flags (r/w) */ +#define RQA_A (RQAA+smc->hw.iop) /* Request reg. (write only) */ +#define WCT_A (WCTA+smc->hw.iop) /* word counter (r/w) */ +#define FFLAG_A (FFLAG+smc->hw.iop) /* FLAG/V_FULL (FIFO almost full, write only)*/ + +/* + * control/status register CSRA bits + */ +/* write */ +#define CS_CRESET 0x01 /* Card reset (0=reset) */ +#define CS_RESET_FIFO 0x02 /* FIFO reset (0=reset) */ +#define CS_IMSK 0x04 /* enable IRQ (1=enable, 0=disable) */ +#define CS_EN_IRQ_TC 0x08 /* enable IRQ from transfer counter */ +#define CS_BYPASS 0x20 /* bypass switch (0=remove, 1=insert)*/ +#define CS_LED_0 0x40 /* switch LED 0 */ +#define CS_LED_1 0x80 /* switch LED 1 */ +/* read */ +#define CS_BYSTAT 0x40 /* 0=Bypass exist, 1= ..not */ +#define CS_SAS 0x80 /* single attachement station (=1) */ + +/* + * control/status register CSFA bits (FIFO) + */ +#define CSF_MUX0 0x01 +#define CSF_MUX1 0x02 +#define CSF_HSREQ0 0x04 +#define CSF_HSREQ1 0x08 +#define CSF_HSREQ2 0x10 +#define CSF_BUSY_DMA 0x40 +#define CSF_BUSY_FIFO 0x80 + +/* + * Interrupt source register ISRA (upper 8 data bits) read only & low activ. + */ +#define IS_MINTR1 0x0100 /* FORMAC ST1U/L & ~IMSK1U/L*/ +#define IS_MINTR2 0x0200 /* FORMAC ST2U/L & ~IMSK2U/L*/ +#define IS_PLINT1 0x0400 /* PLC1 */ +#define IS_PLINT2 0x0800 /* PLC2 */ +#define IS_TIMINT 0x1000 /* Timer 82C54-2 */ +#define IS_TC 0x2000 /* transf. counter */ + +#define ALL_IRSR (IS_MINTR1|IS_MINTR2|IS_PLINT1|IS_PLINT2|IS_TIMINT|IS_TC) + +/* + * CONFIG<0> RAM (C0_A()) + */ +#define CFG_CARD_EN 0x01 /* card enable */ + +/* + * CONFIG<1> RAM (C1_A()) + */ +#define CFG_IRQ_SEL 0x03 /* IRQ select (4 nr.) */ +#define CFG_IRQ_TT 0x04 /* IRQ trigger type (LEVEL/EDGE) */ +#define CFG_DRQ_SEL 0x18 /* DMA requ. (4 nr.) */ +#define CFG_BOOT_EN 0x20 /* 0=BOOT-, 1=Application Software */ +#define CFG_PROG_EN 0x40 /* V_Prog for FLASH_PROM (1=on) */ + +/* + * CONFIG<2> RAM (C2_A()) + */ +#define CFG_EPROM_SEL 0x0f /* FPROM start address selection */ +#define CFG_PAGE 0xf0 /* FPROM page selection */ + + +#define READ_PROM(a) ((u_char)inp(a)) +#define GET_PAGE(i) outp(C2_A(0),((int)(i)<<4) | (inp(C2_A(0)) & ~CFG_PAGE)) +#define FPROM_SW() (inp(C1_A(0)) & CFG_BOOT_EN) + +#define MAX_PAGES 16 /* 16 pages */ +#define MAX_FADDR 0x2000 /* 8K per page */ +#define VPP_ON() outp(C1_A(0),inp(C1_A(0)) | CFG_PROG_EN) +#define VPP_OFF() outp(C1_A(0),inp(C1_A(0)) & ~CFG_PROG_EN) + +#define DMA_BUSY() (inpw(CSF_A) & CSF_BUSY_DMA) +#define FIFO_BUSY() (inpw(CSF_A) & CSF_BUSY_FIFO) +#define DMA_FIFO_BUSY() (inpw(CSF_A) & (CSF_BUSY_DMA | CSF_BUSY_FIFO)) +#define BUS_CHECK() + +#ifdef UNISYS +/* For UNISYS use another macro with drv_usecewait function */ +#define CHECK_DMA() {u_long k = 1000000; \ + while (k && (DMA_BUSY())) { k--; drv_usecwait(20); } \ + if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; } +#else +#define CHECK_DMA() {u_long k = 1000000 ;\ + while (k && (DMA_BUSY())) k-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; } +#endif + +#define CHECK_FIFO() {u_long k = 1000000 ;\ + while (k && (FIFO_BUSY())) k-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0019,HWM_E0019_MSG) ; } + +#define CHECK_DMA_FIFO() {u_long k = 1000000 ;\ + while (k && (DMA_FIFO_BUSY())) k-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ; } + +#define GET_ISR() ~inpw(ISR_A) +#define CHECK_ISR() ~inpw(ISR_A) + +#ifndef UNIX +#ifndef WINNT +#define CLI_FBI() outpw(CSR_A,(inpw(CSR_A)&\ + (CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|smc->hw.led) +#else /* WINNT */ +#define CLI_FBI() outpw(CSR_A,(l_inpw(CSR_A)&\ + (CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|smc->hw.led) +#endif /* WINNT */ +#else /* UNIX */ +#define CLI_FBI(smc) outpw(CSR_AS(smc),(inpw(CSR_AS(smc))&\ + (CS_CRESET|CS_BYPASS))|CS_RESET_FIFO|(smc)->hw.led) +#endif + +#ifndef UNIX +#define STI_FBI() outpw(CSR_A,(inpw(CSR_A)&\ + (CS_CRESET|CS_BYPASS|CS_RESET_FIFO))|CS_IMSK|smc->hw.led) +#else +#define STI_FBI(smc) outpw(CSR_AS(smc),(inpw(CSR_AS(smc))&\ + (CS_CRESET|CS_BYPASS|CS_RESET_FIFO))|CS_IMSK|(smc)->hw.led) +#endif + +/* EISA DMA Controller */ +#define DMA_WRITE_SINGLE_MASK_BIT_M 0x0a /* Master DMA Controller */ +#define DMA_WRITE_SINGLE_MASK_BIT_S 0xd4 /* Slave DMA Controller */ +#define DMA_CLEAR_BYTE_POINTER_M 0x0c +#define DMA_CLEAR_BYTE_POINTER_S 0xd8 + +#endif /* EISA */ + +#ifdef MCA + +/* + * POS Register: !! all I/O's are 8-Bit !! + */ +#define POS_SYS_SETUP 0x94 /* system setup register */ +#define POS_SYSTEM 0xff /* system mode */ + +#define POS_CHANNEL_POS 0x96 /* register slot ID */ +#define POS_CHANNEL_BIT 0x08 /* mask for -"- */ + +#define POS_BASE 0x100 /* POS base address */ +#define POS_ID_LOW POS_BASE /* card ID low */ +#define POS_ID_HIGH (POS_BASE+1) /* card ID high */ +#define POS_102 (POS_BASE+2) /* card en., arbitration level .. */ +#define POS_103 (POS_BASE+3) /* FPROM addr, page */ +#define POS_104 (POS_BASE+4) /* I/O, IRQ */ +#define POS_105 (POS_BASE+5) /* POS_CHCK */ +#define POS_106 (POS_BASE+6) /* to read VPD */ +#define POS_107 (POS_BASE+7) /* added without function */ + +/* FM1 card IDs */ +#define FM1_CARD_ID0 0x83 +#define FM1_CARD_ID1 0 + +#define FM1_IBM_ID0 0x9c +#define FM1_IBM_ID1 0x8f + + +/* FM2 card IDs */ +#define FM2_CARD_ID0 0xab +#define FM2_CARD_ID1 0 + +#define FM2_IBM_ID0 0x7e +#define FM2_IBM_ID1 0x8f + +/* Board revision. */ +#define FM1_REV 0 +#define FM2_REV 1 + +#define MAX_SLOT 8 + +/* + * POS_102 + */ +#define POS_CARD_EN 0x01 /* card enable =1 */ +#define POS_SDAT_EN 0x02 /* enable 32-bit streaming data mode */ +#define POS_EN_CHKINT 0x04 /* enable int. from check line asserted */ +#define POS_EN_BUS_ERR 0x08 /* enable int. on invalid busmaster transf. */ +#define POS_FAIRNESS 0x10 /* fairnes on =1 */ +/* attention: arbitration level used with bit 0 POS 105 */ +#define POS_LARBIT 0xe0 /* arbitration level (0,0,0)->level = 0x8 + (1,1,1)->level = 0xf */ +/* + * POS_103 + */ +#define POS_PAGE 0x07 /* FPROM page selection */ +#define POS_BOOT_EN 0x08 /* boot PROM enable =1 */ +#define POS_MSEL 0x70 /* memory start address for FPROM mapping */ +#define PROG_EN 0x80 /* FM1: Vpp prog on/off */ +#define POS_SDR 0x80 /* FM2: Streaming data bit */ + +/* + * POS_104 + */ +#define POS_IOSEL 0x3f /* selected I/O base address */ +#define POS_IRQSEL 0xc0 /* selected interrupt */ + +/* + * POS_105 + */ +#define POS_CHCK 0x80 +#define POS_SYNC_ERR 0x20 /* FM2: synchronous error reporting */ +#define POS_PAR_DATA 0x10 /* FM2: data parity enable bit */ +#define POS_PAR_ADDR 0x08 /* FM2: address parity enable bit */ +#define POS_IRQHSEL 0x02 /* FM2: Highest bit for IRQ_selection */ +#define POS_HARBIT 0x01 /* Highest bit in Bus arbitration selection */ + +#define SA_MAC (0) /* start addr. MAC_AD within the PROM */ +#define PRA_OFF (0) +#define SA_PMD_TYPE (8) /* start addr. PMD-Type */ + +/* + * address transmision from logical to physical offset address on board + */ +#define FMA(a) (0x0100|((a)<<1)) /* FORMAC+ (r/w) */ +#define P2(a) (0x00c0|((a)<<1)) /* PLC2 (r/w) (DAS) */ +#define P1(a) (0x0080|((a)<<1)) /* PLC1 (r/w) */ +#define TI(a) (0x0060|((a)<<1)) /* Timer (r/w) */ +#define PR(a) (0x0040|((a)<<1)) /* configuration PROM */ +#define CS(a) (0x0020| (a)) /* control/status */ +#define FF(a) (0x0010|((a)<<1)) /* FIFO ASIC */ +#define CT(a) (0x0000|((a)<<1)) /* counter */ + +/* + * counter + */ +#define ACLA CT(0) /* address counter low */ +#define ACHA CT(1) /* address counter high */ +#define BCN CT(2) /* byte counter */ +#define MUX CT(3) /* MUX-register */ +#define WCN CT(0x08) /* word counter */ +#define FFLG CT(0x09) /* FIFO Flags */ + +/* + * test/control register (FM2 only) + */ +#define CNT_TST 0x018 /* Counter test control register */ +#define CNT_STP 0x01a /* Counter test step reg. (8 Bit) */ + +/* + * CS register (read only) + */ +#define CSRA CS(0) /* control/status register address */ +#define CSFA CS(2) /* control/status FIFO BUSY ... */ +#define ISRA CS(4) /* first int. source register address */ +#define ISR2 CS(6) /* second int. source register address */ +#define LEDR CS(0x0c) /* LED register r/w */ +#define CSIL CS(0x10) /* I/O mapped POS_ID_low (100) */ +#define CSIH CS(0x12) /* - " - POS_ID_HIGH (101) */ +#define CSA CS(0x14) /* - " - POS_102 */ +#define CSM CS(0x0e) /* - " - POS_103 */ +#define CSM_FM1 CS(0x16) /* - " - POS_103 (copy in FM1) */ +#define CSI CS(0x18) /* - " - POS_104 */ +#define CSS CS(0x1a) /* - " - POS_105 */ +#define CSP_06 CS(0x1c) /* - " - POS_106 */ +#define WDOG_ST 0x1c /* Watchdog status (FM2 only) */ +#define WDOG_EN 0x1c /* Watchdog enabling (FM2 only, 8Bit) */ +#define WDOG_DIS 0x1e /* Watchdog disabling (FM2 only, 8Bit) */ + +#define PGRA CSM /* Flash page register */ + + +#define WCTA FF(0) /* word counter */ +#define FFLAG FF(1) /* FLAG/V_FULL (FIFO almost full, write only)*/ + +/* + * Timer register (FM2 only) + */ +#define RTM_CNT 0x28 /* RTM Counter */ +#define TI_DIV 0x60 /* Timer Prescaler */ +#define TI_CH1 0x62 /* Timer channel 1 counter */ +#define TI_STOP 0x64 /* Stop timer on channel 1 */ +#define TI_STRT 0x66 /* Start timer on channel 1 */ +#define TI_INI2 0x68 /* Timer: Bus master preemption */ +#define TI_CNT2 0x6a /* Timer */ +#define TI_INI3 0x6c /* Timer: Streaming data */ +#define TI_CNT3 0x6e /* Timer */ +#define WDOG_LO 0x70 /* Watchdog counter low */ +#define WDOG_HI 0x72 /* Watchdog counter high */ +#define RTM_PRE 0x74 /* restr. token prescaler */ +#define RTM_TIM 0x76 /* restr. token timer */ + +/* + * Recommended Timeout values (for FM2 timer only) + */ +#define TOUT_BM_PRE 188 /* 3.76 usec */ +#define TOUT_S_DAT 374 /* 7.48 usec */ + +/* + * CS register (write only) + */ +#define HSR(p) CS(0x18|(p)) /* Host request register */ + +#define RTM_PUT 0x36 /* restr. token counter write */ +#define RTM_GET 0x28 /* - " - clear */ +#define RTM_CLEAR 0x34 /* - " - read */ + +/* + * BCN Bit definitions + */ +#define BCN_BUSY 0x8000 /* DMA Busy flag */ +#define BCN_AZERO 0x4000 /* Almost zero flag (BCN < 4) */ +#define BCN_STREAM 0x2000 /* Allow streaming data (BCN >= 8) */ + +/* + * WCN Bit definitions + */ +#define WCN_ZERO 0x2000 /* Zero flag (counted to zero) */ +#define WCN_AZERO 0x1000 /* Almost zero flag (BCN < 4) */ + +/* + * CNT_TST Bit definitions + */ +#define CNT_MODE 0x01 /* Go into test mode */ +#define CNT_D32 0x02 /* 16/32 BIT test mode */ + +/* + * FIFO Flag FIFO Flags/Vfull register + */ +#define FF_VFULL 0x003f /* V_full value mask */ +#define FFLG_FULL 0x2000 /* FULL flag */ +#define FFLG_A_FULL 0x1000 /* Almost full flag */ +#define FFLG_VFULL 0x0800 /* V_full Flag */ +#define FFLG_A_EMP 0x0400 /* almost empty flag */ +#define FFLG_EMP 0x0200 /* empty flag */ +#define FFLG_T_EMP 0x0100 /* totally empty flag */ + +/* + * WDOG Watchdog status register + */ +#define WDOG_ALM 0x01 /* Watchdog alarm Bit */ +#define WDOG_ACT 0x02 /* Watchdog active Bit */ + +/* + * CS(0) CONTROLS + */ +#define CS_CRESET 0x0001 +#define FIFO_RST 0x0002 +#define CS_IMSK 0x0004 +#define EN_IRQ_CHCK 0x0008 +#define EN_IRQ_TOKEN 0x0010 +#define EN_IRQ_TC 0x0020 +#define TOKEN_STATUS 0x0040 +#define RTM_CHANGE 0x0080 + +#define CS_SAS 0x0100 +#define CS_BYSTAT 0x0200 /* bypass connected (0=conn.) */ +#define CS_BYPASS 0x0400 /* bypass on/off indication */ + +/* + * CS(2) FIFOSTAT + */ +#define HSREQ 0x0007 +#define BIGDIR 0x0008 +#define CSF_BUSY_FIFO 0x0010 +#define CSF_BUSY_DMA 0x0020 +#define SLOT_32 0x0040 + +#define LED_0 0x0001 +#define LED_1 0x0002 +#define LED_2 0x0100 + +#define MAX_PAGES 8 /* pages */ +#define MAX_FADDR 0x4000 /* 16K per page */ + +/* + * IRQ = ISRA || ISR2 ; + * + * ISRA = IRQ_OTH_EN && (IS_LAN | IS_BUS) ; + * ISR2 = IRQ_TC_EN && IS_TC ; + * + * IS_LAN = (IS_MINTR1 | IS_MINTR2 | IS_PLINT1 | IS_PLINT2 | IS_TIMINT) || + * (IRQ_EN_TOKEN && IS_TOKEN) ; + * IS_BUS = IRQ_CHCK_EN && (IS_BUSERR | IS_CHCK_L) ; + */ +/* + * ISRA !!! activ high !!! + */ +#define IS_MINTR1 0x0001 /* FORMAC ST1U/L & ~IMSK1U/L*/ +#define IS_MINTR2 0x0002 /* FORMAC ST2U/L & ~IMSK2U/L*/ +#define IS_PLINT1 0x0004 /* PLC1 */ +#define IS_PLINT2 0x0008 /* PLC2 */ +#define IS_TIMINT 0x0010 /* Timer 82C54-2 */ +#define IS_TOKEN 0x0020 /* restrictet token monitoring */ +#define IS_CHCK_L 0x0040 /* check line asserted */ +#define IS_BUSERR 0x0080 /* bus error */ +/* + * ISR2 + */ +#define IS_TC 0x0001 /* terminal count irq */ +#define IS_SFDBKRTN 0x0002 /* selected feedback return */ +#define IS_D16 0x0004 /* DS16 */ +#define IS_D32 0x0008 /* DS32 */ +#define IS_DPEI 0x0010 /* Data Parity Indication */ + +#define ALL_IRSR 0x00ff + +#define FM_A(a) ADDR(FMA(a)) /* FORMAC Plus physical addr */ +#define P1_A(a) ADDR(P1(a)) /* PLC1 (r/w) */ +#define P2_A(a) ADDR(P2(a)) /* PLC2 (r/w) (DAS) */ +#define TI_A(a) ADDR(TI(a)) /* Timer (r/w) FM1 only! */ +#define PR_A(a) ADDR(PR(a)) /* config. PROM */ +#define CS_A(a) ADDR(CS(a)) /* control/status */ + +#define ISR1_A ADDR(ISRA) /* first int. source register address */ +#define ISR2_A ADDR(ISR2) /* second -"- */ +#define CSR_A ADDR(CSRA) /* control/status register address */ +#define CSF_A ADDR(CSFA) /* control/status FIFO BUSY flags (r/w) */ + +#define CSIL_A ADDR(CSIL) /* I/O mapped POS_ID_low (102) */ +#define CSIH_A ADDR(CSIH) /* - " - POS_ID_HIGH (101) */ +#define CSA_A ADDR(CSA) /* - " - POS_102 */ +#define CSI_A ADDR(CSI) /* - " - POS_104 */ +#define CSM_A ADDR(CSM) /* - " - POS_103 */ +#define CSM_FM1_A ADDR(CSM_FM1) /* - " - POS_103 (2nd copy, FM1) */ +#define CSP_06_A ADDR(CSP_06) /* - " - POS_106 */ + +#define WCT_A ADDR(WCTA) /* word counter (r/w) */ +#define FFLAG_A ADDR(FFLAG) /* FLAG/V_FULL (FIFO almost full, write only)*/ + +#define ACL_A ADDR(ACLA) /* address counter low */ +#define ACH_A ADDR(ACHA) /* address counter high */ +#define BCN_A ADDR(BCN) /* byte counter */ +#define MUX_A ADDR(MUX) /* MUX-register */ + +#define ISR_A ADDR(ISRA) /* Interrupt Source Register */ +#define FIFO_RESET_A ADDR(FIFO_RESET) /* reset the FIFO */ +#define FIFO_EN_A ADDR(FIFO_EN) /* enable the FIFO */ + +#define WDOG_EN_A ADDR(WDOG_EN) /* reset and start the WDOG */ +#define WDOG_DIS_A ADDR(WDOG_DIS) /* disable the WDOG */ +/* + * all control reg. (read!) are 8 bit (except PAGE_RG_A and LEDR_A) + */ +#define HSR_A(p) ADDR(HSR(p)) /* Host request register */ + +#define STAT_BYP 0 /* bypass station */ +#define STAT_INS 2 /* insert station */ +#define BYPASS(o) CS(0x10|(o)) /* o=STAT_BYP || STAT_INS */ + +#define IRQ_TC_EN CS(0x0b) /* enable/disable IRQ on TC */ +#define IRQ_TC_DIS CS(0x0a) +#define IRQ_TOKEN_EN CS(9) /* enable/disable IRQ on restr. Token */ +#define IRQ_TOKEN_DIS CS(8) +#define IRQ_CHCK_EN CS(7) /* -"- IRQ after CHCK line */ +#define IRQ_CHCK_DIS CS(6) +#define IRQ_OTH_EN CS(5) /* -"- other IRQ's */ +#define IRQ_OTH_DIS CS(4) +#define FIFO_EN CS(3) /* disable (reset), enable FIFO */ +#define FIFO_RESET CS(2) +#define CARD_EN CS(1) /* disable (reset), enable card */ +#define CARD_DIS CS(0) + +#define LEDR_A ADDR(LEDR) /* D0=green, D1=yellow, D8=L2 */ +#define PAGE_RG_A ADDR(CSM) /* D<2..0> */ +#define IRQ_CHCK_EN_A ADDR(IRQ_CHCK_EN) +#define IRQ_CHCK_DIS_A ADDR(IRQ_CHCK_DIS) + +#define GET_PAGE(bank) outpw(PAGE_RG_A,(inpw(PAGE_RG_A) &\ + (~POS_PAGE)) |(int) (bank)) +#define VPP_ON() if (smc->hw.rev == FM1_REV) { \ + outpw(PAGE_RG_A, \ + (inpw(PAGE_RG_A) & POS_PAGE) | PROG_EN); \ + } +#define VPP_OFF() if (smc->hw.rev == FM1_REV) { \ + outpw(PAGE_RG_A,(inpw(PAGE_RG_A) & POS_PAGE)); \ + } + +#define SKFDDI_PSZ 16 /* address PROM size */ + +#define READ_PROM(a) ((u_char)inp(a)) + +#define GET_ISR() ~inpw(ISR1_A) +#ifndef TCI +#define CHECK_ISR() ~inpw(ISR1_A) +#define CHECK_ISR_SMP(iop) ~inpw((iop)+ISRA) +#else +#define CHECK_ISR() (~inpw(ISR1_A) | ~inpw(ISR2_A)) +#define CHECK_ISR_SMP(iop) (~inpw((iop)+ISRA) | ~inpw((iop)+ISR2)) +#endif + +#define DMA_BUSY() (inpw(CSF_A) & CSF_BUSY_DMA) +#define FIFO_BUSY() (inpw(CSF_A) & CSF_BUSY_FIFO) +#define DMA_FIFO_BUSY() (inpw(CSF_A) & (CSF_BUSY_DMA | CSF_BUSY_FIFO)) +#define BUS_CHECK() { int i ; \ + if ((i = GET_ISR()) & IS_BUSERR) \ + SMT_PANIC(smc,HWM_E0020,HWM_E0020_MSG) ; \ + if (i & IS_CHCK_L) \ + SMT_PANIC(smc,HWM_E0014,HWM_E0014_MSG) ; \ + } + +#define CHECK_DMA() { u_long k = 10000 ; \ + while (k && (DMA_BUSY())) { \ + k-- ; \ + BUS_CHECK() ; \ + } \ + if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; } + +#define CHECK_FIFO() {u_long k = 1000000 ;\ + while (k && (FIFO_BUSY())) k-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0019,HWM_E0019_MSG) ; } + +#define CHECK_DMA_FIFO() {u_long k = 1000000 ;\ + while (k && (DMA_FIFO_BUSY())) { \ + k-- ;\ + BUS_CHECK() ; \ + } \ + if (!k) SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ; } + +#ifndef UNIX +#define CLI_FBI() outp(ADDR(IRQ_OTH_DIS),0) +#else +#define CLI_FBI(smc) outp(ADDRS((smc),IRQ_OTH_DIS),0) +#endif + +#ifndef TCI +#define CLI_FBI_SMP(iop) outp((iop)+IRQ_OTH_DIS,0) +#else +#define CLI_FBI_SMP(iop) outp((iop)+IRQ_OTH_DIS,0) ;\ + outp((iop)+IRQ_TC_DIS,0) +#endif + +#ifndef UNIX +#define STI_FBI() outp(ADDR(IRQ_OTH_EN),0) +#else +#define STI_FBI(smc) outp(ADDRS((smc),IRQ_OTH_EN),0) +#endif + +/* + * Terminal count primitives + */ +#define CLI_TCI(smc) outp(ADDRS((smc),IRQ_TC_DIS),0) +#define STI_TCI(smc) outp(ADDRS((smc),IRQ_TC_EN),0) +#define CHECK_TC(smc,k) {(k) = 10000 ;\ + while ((k) && (~inpw(ISR2_A) & IS_TC)) (k)-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0018,HWM_E0018_MSG) ; } + +#endif /* MCA */ + +#ifdef ISA + +/* + * address transmision from logic NPADDR6-0 to physical offset address on board + */ +#define FMA(a) (0x8000|(((a)&0x07)<<1)|(((a)&0x78)<<7)) /* FORMAC+ (r/w) */ +#define PRA(a) (0x1000|(((a)&0x07)<<1)|(((a)&0x18)<<7)) /* PROM (read only)*/ +#define P1A(a) (0x4000|(((a)&0x07)<<1)|(((a)&0x18)<<7)) /* PLC1 (r/w) */ +#define P2A(a) (0x5000|(((a)&0x07)<<1)|(((a)&0x18)<<7)) /* PLC2 (r/w) */ +#define TIA(a) (0x6000|(((a)&0x03)<<1)) /* Timer (r/w) */ + +#define ISRA 0x0000 /* int. source register address (read only) */ +#define ACLA 0x0000 /* address counter low address (write only) */ +#define ACHA 0x0002 /* address counter high address (write only) */ +#define TRCA 0x0004 /* transfer counter address (write only) */ +#define PGRA 0x0006 /* page register address (write only) */ +#define RQAA 0x2000 /* Request reg. (write only) */ +#define CSRA 0x3000 /* control/status register address (r/w) */ + +/* + * physical address offset + IO-Port base address + */ +#define FM_A(a) (FMA(a)+smc->hw.iop) /* FORMAC Plus physical addr */ +#define PR_A(a) (PRA(a)+smc->hw.iop) /* PROM (read only)*/ +#define P1_A(a) (P1A(a)+smc->hw.iop) /* PLC1 (r/w) */ +#define P2_A(a) (P2A(a)+smc->hw.iop) /* PLC2 (r/w) */ +#define TI_A(a) (TIA(a)+smc->hw.iop) /* Timer (r/w) */ + +#define ISR_A (0x0000+smc->hw.iop) /* int. source register address (read only) */ +#define ACL_A (0x0000+smc->hw.iop) /* address counter low address (write only) */ +#define ACH_A (0x0002+smc->hw.iop) /* address counter high address (write only)*/ +#define TRC_A (0x0004+smc->hw.iop) /* transfer counter address (write only) */ +#define PGR_A (0x0006+smc->hw.iop) /* page register address (write only) */ +#define RQA_A (0x2000+smc->hw.iop) /* Request reg. (write only) */ +#define CSR_A (0x3000+smc->hw.iop) /* control/status register address (r/w) */ +#ifdef UNIX +#define CSR_AS(smc) (0x3000+(smc)->hw.iop) /* control/status register address */ +#endif +#define PLC1_I (0x3400+smc->hw.iop) /* clear PLC1 interrupt bit */ +#define PLC2_I (0x3800+smc->hw.iop) /* clear PLC2 interrupt bit */ + +#ifndef MULT_OEM +#ifndef OEM_CONCEPT +#define SKLOGO_STR "SKFDDI" +#else /* OEM_CONCEPT */ +#define SKLOGO_STR OEM_FDDI_LOGO +#endif /* OEM_CONCEPT */ +#endif /* MULT_OEM */ +#define SADDRL (24) /* start address SKLOGO */ +#define SA_MAC (0) /* start addr. MAC_AD within the PROM */ +#define PRA_OFF (0) +#define SA_PMD_TYPE (8) /* start addr. PMD-Type */ + +#define CDID (PRA(SADDRL)) /* Card ID int/O port addr. offset */ +#define NEXT_CDID ((PRA(SADDRL+1)) - CDID) + +#define SKFDDI_PSZ 32 /* address PROM size */ + +#define READ_PROM(a) ((u_char)inpw(a)) +#define GET_PAGE(i) outpw(PGR_A,(int)(i)) + +#define MAX_PAGES 16 /* 16 pages */ +#define MAX_FADDR 0x2000 /* 8K per page */ +#define VPP_OFF() outpw(CSR_A,(inpw(CSR_A) & (CS_CRESET|CS_BYPASS))) +#define VPP_ON() outpw(CSR_A,(inpw(CSR_A) & (CS_CRESET|CS_BYPASS)) | \ + CS_VPPSW) + +/* + * control/status register CSRA bits (log. addr: 0x3000) + */ +/* write */ +#define CS_CRESET 0x01 /* Card reset (0=reset) */ +#define CS_IMSK 0x02 /* enable IRQ (1=enable, 0=disable) */ +#define CS_RESINT1 0x04 /* PLINT1 reset */ +#define CS_VPPSW 0x10 /* 12V power switch (0=off, 1=on) */ +#define CS_BYPASS 0x20 /* bypass switch (0=remove, 1=insert)*/ +#define CS_RESINT2 0x40 /* PLINT2 reset */ +/* read */ +#define CS_BUSY 0x04 /* master transfer activ (=1) */ +#define CS_SW_EPROM 0x08 /* 0=Application Soft. 1=BOOT-EPROM */ +#define CS_BYSTAT 0x40 /* 0=Bypass exist, 1= ..not */ +#define CS_SAS 0x80 /* single attachement station (=1) */ + +/* + * Interrupt source register ISRA (log. addr: 0x0000) read only & low activ. + */ +#define IS_MINTR1 0x01 /* FORMAC ST1U/L && ~IMSK1U/L*/ +#define IS_MINTR2 0x02 /* FORMAC ST2U/L && ~IMSK2U/L*/ +#define IS_PLINT1 0x04 /* PLC1 */ +#define IS_PLINT2 0x08 /* PLC2 */ +#define IS_TIMINT 0x10 /* Timer 82C54-2 */ + +#define ALL_IRSR (IS_MINTR1|IS_MINTR2|IS_PLINT1|IS_PLINT2|IS_TIMINT) + +#define FPROM_SW() (inpw(CSR_A)&CS_SW_EPROM) +#define DMA_BUSY() (inpw(CSR_A)&CS_BUSY) +#define CHECK_FIFO() +#define BUS_CHECK() + +/* + * set Host Request register (wr.) + */ +#define SET_HRQ(qup) outpw(RQA_A+((qup)<<1),0) + +#ifndef UNIX +#ifndef WINNT +#define CLI_FBI() outpw(CSR_A,(inpw(CSR_A)&(CS_CRESET|CS_BYPASS|CS_VPPSW))) +#else +#define CLI_FBI() outpw(CSR_A,(l_inpw(CSR_A) & \ + (CS_CRESET|CS_BYPASS|CS_VPPSW))) +#endif +#else +#define CLI_FBI(smc) outpw(CSR_AS(smc),(inpw(CSR_AS(smc))& \ + (CS_CRESET|CS_BYPASS|CS_VPPSW))) +#endif + +#ifndef UNIX +#define STI_FBI() outpw(CSR_A,(inpw(CSR_A) & \ + (CS_CRESET|CS_BYPASS|CS_VPPSW)) | CS_IMSK) +#else +#define STI_FBI(smc) outpw(CSR_AS(smc),(inpw(CSR_AS(smc)) & \ + (CS_CRESET|CS_BYPASS|CS_VPPSW)) | CS_IMSK) +#endif + +#define CHECK_DMA() {unsigned k = 10000 ;\ + while (k && (DMA_BUSY())) k-- ;\ + if (!k) SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; } + +#define GET_ISR() ~inpw(ISR_A) + +#endif /* ISA */ + +/*--------------------------------------------------------------------------*/ +#ifdef PCI + +/* + * (DV) = only defined for Da Vinci + * (ML) = only defined for Monalisa + */ + +/* + * Configuration Space header + */ +#define PCI_VENDOR_ID 0x00 /* 16 bit Vendor ID */ +#define PCI_DEVICE_ID 0x02 /* 16 bit Device ID */ +#define PCI_COMMAND 0x04 /* 16 bit Command */ +#define PCI_STATUS 0x06 /* 16 bit Status */ +#define PCI_REV_ID 0x08 /* 8 bit Revision ID */ +#define PCI_CLASS_CODE 0x09 /* 24 bit Class Code */ +#define PCI_CACHE_LSZ 0x0c /* 8 bit Cache Line Size */ +#define PCI_LAT_TIM 0x0d /* 8 bit Latency Timer */ +#define PCI_HEADER_T 0x0e /* 8 bit Header Type */ +#define PCI_BIST 0x0f /* 8 bit Built-in selftest */ +#define PCI_BASE_1ST 0x10 /* 32 bit 1st Base address */ +#define PCI_BASE_2ND 0x14 /* 32 bit 2nd Base address */ +/* Byte 18..2b: Reserved */ +#define PCI_SUB_VID 0x2c /* 16 bit Subsystem Vendor ID */ +#define PCI_SUB_ID 0x2e /* 16 bit Subsystem ID */ +#define PCI_BASE_ROM 0x30 /* 32 bit Expansion ROM Base Address */ +/* Byte 34..33: Reserved */ +#define PCI_CAP_PTR 0x34 /* 8 bit (ML) Capabilities Ptr */ +/* Byte 35..3b: Reserved */ +#define PCI_IRQ_LINE 0x3c /* 8 bit Interrupt Line */ +#define PCI_IRQ_PIN 0x3d /* 8 bit Interrupt Pin */ +#define PCI_MIN_GNT 0x3e /* 8 bit Min_Gnt */ +#define PCI_MAX_LAT 0x3f /* 8 bit Max_Lat */ +/* Device Dependent Region */ +#define PCI_OUR_REG 0x40 /* 32 bit (DV) Our Register */ +#define PCI_OUR_REG_1 0x40 /* 32 bit (ML) Our Register 1 */ +#define PCI_OUR_REG_2 0x44 /* 32 bit (ML) Our Register 2 */ +/* Power Management Region */ +#define PCI_PM_CAP_ID 0x48 /* 8 bit (ML) Power Management Cap. ID */ +#define PCI_PM_NITEM 0x49 /* 8 bit (ML) Next Item Ptr */ +#define PCI_PM_CAP_REG 0x4a /* 16 bit (ML) Power Management Capabilities */ +#define PCI_PM_CTL_STS 0x4c /* 16 bit (ML) Power Manag. Control/Status */ +/* Byte 0x4e: Reserved */ +#define PCI_PM_DAT_REG 0x4f /* 8 bit (ML) Power Manag. Data Register */ +/* VPD Region */ +#define PCI_VPD_CAP_ID 0x50 /* 8 bit (ML) VPD Cap. ID */ +#define PCI_VPD_NITEM 0x51 /* 8 bit (ML) Next Item Ptr */ +#define PCI_VPD_ADR_REG 0x52 /* 16 bit (ML) VPD Address Register */ +#define PCI_VPD_DAT_REG 0x54 /* 32 bit (ML) VPD Data Register */ +/* Byte 58..ff: Reserved */ + +/* + * I2C Address (PCI Config) + * + * Note: The temperature and voltage sensors are relocated on a different + * I2C bus. + */ +#define I2C_ADDR_VPD 0xA0 /* I2C address for the VPD EEPROM */ + +/* + * Define Bits and Values of the registers + */ +/* PCI_VENDOR_ID 16 bit Vendor ID */ +/* PCI_DEVICE_ID 16 bit Device ID */ +/* Values for Vendor ID and Device ID shall be patched into the code */ +/* PCI_COMMAND 16 bit Command */ +#define PCI_FBTEN 0x0200 /* Bit 9: Fast Back-To-Back enable */ +#define PCI_SERREN 0x0100 /* Bit 8: SERR enable */ +#define PCI_ADSTEP 0x0080 /* Bit 7: Address Stepping */ +#define PCI_PERREN 0x0040 /* Bit 6: Parity Report Response enable */ +#define PCI_VGA_SNOOP 0x0020 /* Bit 5: VGA palette snoop */ +#define PCI_MWIEN 0x0010 /* Bit 4: Memory write an inv cycl ena */ +#define PCI_SCYCEN 0x0008 /* Bit 3: Special Cycle enable */ +#define PCI_BMEN 0x0004 /* Bit 2: Bus Master enable */ +#define PCI_MEMEN 0x0002 /* Bit 1: Memory Space Access enable */ +#define PCI_IOEN 0x0001 /* Bit 0: IO Space Access enable */ + +/* PCI_STATUS 16 bit Status */ +#define PCI_PERR 0x8000 /* Bit 15: Parity Error */ +#define PCI_SERR 0x4000 /* Bit 14: Signaled SERR */ +#define PCI_RMABORT 0x2000 /* Bit 13: Received Master Abort */ +#define PCI_RTABORT 0x1000 /* Bit 12: Received Target Abort */ +#define PCI_STABORT 0x0800 /* Bit 11: Sent Target Abort */ +#define PCI_DEVSEL 0x0600 /* Bit 10..9: DEVSEL Timing */ +#define PCI_DEV_FAST (0<<9) /* fast */ +#define PCI_DEV_MEDIUM (1<<9) /* medium */ +#define PCI_DEV_SLOW (2<<9) /* slow */ +#define PCI_DATAPERR 0x0100 /* Bit 8: DATA Parity error detected */ +#define PCI_FB2BCAP 0x0080 /* Bit 7: Fast Back-to-Back Capability */ +#define PCI_UDF 0x0040 /* Bit 6: User Defined Features */ +#define PCI_66MHZCAP 0x0020 /* Bit 5: 66 MHz PCI bus clock capable */ +#define PCI_NEWCAP 0x0010 /* Bit 4: New cap. list implemented */ + +#define PCI_ERRBITS (PCI_PERR|PCI_SERR|PCI_RMABORT|PCI_STABORT|PCI_DATAPERR) + +/* PCI_REV_ID 8 bit Revision ID */ +/* PCI_CLASS_CODE 24 bit Class Code */ +/* Byte 2: Base Class (02) */ +/* Byte 1: SubClass (02) */ +/* Byte 0: Programming Interface (00) */ + +/* PCI_CACHE_LSZ 8 bit Cache Line Size */ +/* Possible values: 0,2,4,8,16 */ + +/* PCI_LAT_TIM 8 bit Latency Timer */ + +/* PCI_HEADER_T 8 bit Header Type */ +#define PCI_HD_MF_DEV 0x80 /* Bit 7: 0= single, 1= multi-func dev */ +#define PCI_HD_TYPE 0x7f /* Bit 6..0: Header Layout 0= normal */ + +/* PCI_BIST 8 bit Built-in selftest */ +#define PCI_BIST_CAP 0x80 /* Bit 7: BIST Capable */ +#define PCI_BIST_ST 0x40 /* Bit 6: Start BIST */ +#define PCI_BIST_RET 0x0f /* Bit 3..0: Completion Code */ + +/* PCI_BASE_1ST 32 bit 1st Base address */ +#define PCI_MEMSIZE 0x800L /* use 2 kB Memory Base */ +#define PCI_MEMBASE_BITS 0xfffff800L /* Bit 31..11: Memory Base Address */ +#define PCI_MEMSIZE_BIIS 0x000007f0L /* Bit 10..4: Memory Size Req. */ +#define PCI_PREFEN 0x00000008L /* Bit 3: Prefetchable */ +#define PCI_MEM_TYP 0x00000006L /* Bit 2..1: Memory Type */ +#define PCI_MEM32BIT (0<<1) /* Base addr anywhere in 32 Bit range */ +#define PCI_MEM1M (1<<1) /* Base addr below 1 MegaByte */ +#define PCI_MEM64BIT (2<<1) /* Base addr anywhere in 64 Bit range */ +#define PCI_MEMSPACE 0x00000001L /* Bit 0: Memory Space Indic. */ + +/* PCI_BASE_2ND 32 bit 2nd Base address */ +#define PCI_IOBASE 0xffffff00L /* Bit 31..8: I/O Base address */ +#define PCI_IOSIZE 0x000000fcL /* Bit 7..2: I/O Size Requirements */ +#define PCI_IOSPACE 0x00000001L /* Bit 0: I/O Space Indicator */ + +/* PCI_SUB_VID 16 bit Subsystem Vendor ID */ +/* PCI_SUB_ID 16 bit Subsystem ID */ + +/* PCI_BASE_ROM 32 bit Expansion ROM Base Address */ +#define PCI_ROMBASE 0xfffe0000L /* Bit 31..17: ROM BASE addres (1st) */ +#define PCI_ROMBASZ 0x0001c000L /* Bit 16..14: Treat as BASE or SIZE */ +#define PCI_ROMSIZE 0x00003800L /* Bit 13..11: ROM Size Requirements */ +#define PCI_ROMEN 0x00000001L /* Bit 0: Address Decode enable */ + +/* PCI_CAP_PTR 8 bit New Capabilities Pointers */ +/* PCI_IRQ_LINE 8 bit Interrupt Line */ +/* PCI_IRQ_PIN 8 bit Interrupt Pin */ +/* PCI_MIN_GNT 8 bit Min_Gnt */ +/* PCI_MAX_LAT 8 bit Max_Lat */ +/* Device Dependent Region */ +/* PCI_OUR_REG (DV) 32 bit Our Register */ +/* PCI_OUR_REG_1 (ML) 32 bit Our Register 1 */ + /* Bit 31..29: reserved */ +#define PCI_PATCH_DIR (3L<<27) /*(DV) Bit 28..27: Ext Patchs direction */ +#define PCI_PATCH_DIR_0 (1L<<27) /*(DV) Type of the pins EXT_PATCHS<1..0> */ +#define PCI_PATCH_DIR_1 (1L<<28) /* 0 = input */ + /* 1 = output */ +#define PCI_EXT_PATCHS (3L<<25) /*(DV) Bit 26..25: Extended Patches */ +#define PCI_EXT_PATCH_0 (1L<<25) /*(DV) */ +#define PCI_EXT_PATCH_1 (1L<<26) /* CLK for MicroWire (ML) */ +#define PCI_VIO (1L<<25) /*(ML) */ +#define PCI_EN_BOOT (1L<<24) /* Bit 24: Enable BOOT via ROM */ + /* 1 = Don't boot with ROM */ + /* 0 = Boot with ROM */ +#define PCI_EN_IO (1L<<23) /* Bit 23: Mapping to IO space */ +#define PCI_EN_FPROM (1L<<22) /* Bit 22: FLASH mapped to mem? */ + /* 1 = Map Flash to Memory */ + /* 0 = Disable all addr. decoding */ +#define PCI_PAGESIZE (3L<<20) /* Bit 21..20: FLASH Page Size */ +#define PCI_PAGE_16 (0L<<20) /* 16 k pages */ +#define PCI_PAGE_32K (1L<<20) /* 32 k pages */ +#define PCI_PAGE_64K (2L<<20) /* 64 k pages */ +#define PCI_PAGE_128K (3L<<20) /* 128 k pages */ + /* Bit 19: reserved (ML) and (DV) */ +#define PCI_PAGEREG (7L<<16) /* Bit 18..16: Page Register */ + /* Bit 15: reserved */ +#define PCI_FORCE_BE (1L<<14) /* Bit 14: Assert all BEs on MR */ +#define PCI_DIS_MRL (1L<<13) /* Bit 13: Disable Mem R Line */ +#define PCI_DIS_MRM (1L<<12) /* Bit 12: Disable Mem R multip */ +#define PCI_DIS_MWI (1L<<11) /* Bit 11: Disable Mem W & inv */ +#define PCI_DISC_CLS (1L<<10) /* Bit 10: Disc: cacheLsz bound */ +#define PCI_BURST_DIS (1L<<9) /* Bit 9: Burst Disable */ +#define PCI_BYTE_SWAP (1L<<8) /*(DV) Bit 8: Byte Swap in DATA */ +#define PCI_SKEW_DAS (0xfL<<4) /* Bit 7..4: Skew Ctrl, DAS Ext */ +#define PCI_SKEW_BASE (0xfL<<0) /* Bit 3..0: Skew Ctrl, Base */ + +/* PCI_OUR_REG_2 (ML) 32 bit Our Register 2 (Monalisa only) */ +#define PCI_VPD_WR_TH (0xffL<<24) /* Bit 24..31 VPD Write Threshold */ +#define PCI_DEV_SEL (0x7fL<<17) /* Bit 17..23 EEPROM Device Select */ +#define PCI_VPD_ROM_SZ (7L<<14) /* Bit 14..16 VPD ROM Size */ + /* Bit 12..13 reserved */ +#define PCI_PATCH_DIR2 (0xfL<<8) /* Bit 8..11 Ext Patchs dir 2..5 */ +#define PCI_PATCH_DIR_2 (1L<<8) /* Bit 8 CS for MicroWire */ +#define PCI_PATCH_DIR_3 (1L<<9) +#define PCI_PATCH_DIR_4 (1L<<10) +#define PCI_PATCH_DIR_5 (1L<<11) +#define PCI_EXT_PATCHS2 (0xfL<<4) /* Bit 4..7 Extended Patches */ +#define PCI_EXT_PATCH_2 (1L<<4) /* Bit 4 CS for MicroWire */ +#define PCI_EXT_PATCH_3 (1L<<5) +#define PCI_EXT_PATCH_4 (1L<<6) +#define PCI_EXT_PATCH_5 (1L<<7) +#define PCI_EN_DUMMY_RD (1L<<3) /* Bit 3 Enable Dummy Read */ +#define PCI_REV_DESC (1L<<2) /* Bit 2 Reverse Desc. Bytes */ +#define PCI_USEADDR64 (1L<<1) /* Bit 1 Use 64 Bit Addresse */ +#define PCI_USEDATA64 (1L<<0) /* Bit 0 Use 64 Bit Data bus ext*/ + +/* Power Management Region */ +/* PCI_PM_CAP_ID 8 bit (ML) Power Management Cap. ID */ +/* PCI_PM_NITEM 8 bit (ML) Next Item Ptr */ +/* PCI_PM_CAP_REG 16 bit (ML) Power Management Capabilities*/ +#define PCI_PME_SUP (0x1f<<11) /* Bit 11..15 PM Manag. Event Support*/ +#define PCI_PM_D2_SUB (1<<10) /* Bit 10 D2 Support Bit */ +#define PCI_PM_D1_SUB (1<<9) /* Bit 9 D1 Support Bit */ + /* Bit 6..8 reserved */ +#define PCI_PM_DSI (1<<5) /* Bit 5 Device Specific Init.*/ +#define PCI_PM_APS (1<<4) /* Bit 4 Auxialiary Power Src */ +#define PCI_PME_CLOCK (1<<3) /* Bit 3 PM Event Clock */ +#define PCI_PM_VER (7<<0) /* Bit 0..2 PM PCI Spec. version */ + +/* PCI_PM_CTL_STS 16 bit (ML) Power Manag. Control/Status */ +#define PCI_PME_STATUS (1<<15) /* Bit 15 PFA doesn't sup. PME#*/ +#define PCI_PM_DAT_SCL (3<<13) /* Bit 13..14 dat reg Scaling factor */ +#define PCI_PM_DAT_SEL (0xf<<9) /* Bit 9..12 PM data selector field */ + /* Bit 7.. 2 reserved */ +#define PCI_PM_STATE (3<<0) /* Bit 0.. 1 Power Management State */ +#define PCI_PM_STATE_D0 (0<<0) /* D0: Operational (default) */ +#define PCI_PM_STATE_D1 (1<<0) /* D1: not supported */ +#define PCI_PM_STATE_D2 (2<<0) /* D2: not supported */ +#define PCI_PM_STATE_D3 (3<<0) /* D3: HOT, Power Down and Reset */ + +/* PCI_PM_DAT_REG 8 bit (ML) Power Manag. Data Register */ +/* VPD Region */ +/* PCI_VPD_CAP_ID 8 bit (ML) VPD Cap. ID */ +/* PCI_VPD_NITEM 8 bit (ML) Next Item Ptr */ +/* PCI_VPD_ADR_REG 16 bit (ML) VPD Address Register */ +#define PCI_VPD_FLAG (1<<15) /* Bit 15 starts VPD rd/wd cycle*/ +#define PCI_VPD_ADDR (0x3fff<<0) /* Bit 0..14 VPD address */ + +/* PCI_VPD_DAT_REG 32 bit (ML) VPD Data Register */ + +/* + * Control Register File: + * Bank 0 + */ +#define B0_RAP 0x0000 /* 8 bit register address port */ + /* 0x0001 - 0x0003: reserved */ +#define B0_CTRL 0x0004 /* 8 bit control register */ +#define B0_DAS 0x0005 /* 8 Bit control register (DAS) */ +#define B0_LED 0x0006 /* 8 Bit LED register */ +#define B0_TST_CTRL 0x0007 /* 8 bit test control register */ +#define B0_ISRC 0x0008 /* 32 bit Interrupt source register */ +#define B0_IMSK 0x000c /* 32 bit Interrupt mask register */ + +/* 0x0010 - 0x006b: formac+ (supernet_3) fequently used registers */ +#define B0_CMDREG1 0x0010 /* write command reg 1 instruction */ +#define B0_CMDREG2 0x0014 /* write command reg 2 instruction */ +#define B0_ST1U 0x0010 /* read upper 16-bit of status reg 1 */ +#define B0_ST1L 0x0014 /* read lower 16-bit of status reg 1 */ +#define B0_ST2U 0x0018 /* read upper 16-bit of status reg 2 */ +#define B0_ST2L 0x001c /* read lower 16-bit of status reg 2 */ + +#define B0_MARR 0x0020 /* r/w the memory read addr register */ +#define B0_MARW 0x0024 /* r/w the memory write addr register*/ +#define B0_MDRU 0x0028 /* r/w upper 16-bit of mem. data reg */ +#define B0_MDRL 0x002c /* r/w lower 16-bit of mem. data reg */ + +#define B0_MDREG3 0x0030 /* r/w Mode Register 3 */ +#define B0_ST3U 0x0034 /* read upper 16-bit of status reg 3 */ +#define B0_ST3L 0x0038 /* read lower 16-bit of status reg 3 */ +#define B0_IMSK3U 0x003c /* r/w upper 16-bit of IMSK reg 3 */ +#define B0_IMSK3L 0x0040 /* r/w lower 16-bit of IMSK reg 3 */ +#define B0_IVR 0x0044 /* read Interrupt Vector register */ +#define B0_IMR 0x0048 /* r/w Interrupt mask register */ +/* 0x4c Hidden */ + +#define B0_CNTRL_A 0x0050 /* control register A (r/w) */ +#define B0_CNTRL_B 0x0054 /* control register B (r/w) */ +#define B0_INTR_MASK 0x0058 /* interrupt mask (r/w) */ +#define B0_XMIT_VECTOR 0x005c /* transmit vector register (r/w) */ + +#define B0_STATUS_A 0x0060 /* status register A (read only) */ +#define B0_STATUS_B 0x0064 /* status register B (read only) */ +#define B0_CNTRL_C 0x0068 /* control register C (r/w) */ +#define B0_MDREG1 0x006c /* r/w Mode Register 1 */ + +#define B0_R1_CSR 0x0070 /* 32 bit BMU control/status reg (rec q 1) */ +#define B0_R2_CSR 0x0074 /* 32 bit BMU control/status reg (rec q 2)(DV)*/ +#define B0_XA_CSR 0x0078 /* 32 bit BMU control/status reg (a xmit q) */ +#define B0_XS_CSR 0x007c /* 32 bit BMU control/status reg (s xmit q) */ + +/* + * Bank 1 + * - completely empty (this is the RAP Block window) + * Note: if RAP = 1 this page is reserved + */ + +/* + * Bank 2 + */ +#define B2_MAC_0 0x0100 /* 8 bit MAC address Byte 0 */ +#define B2_MAC_1 0x0101 /* 8 bit MAC address Byte 1 */ +#define B2_MAC_2 0x0102 /* 8 bit MAC address Byte 2 */ +#define B2_MAC_3 0x0103 /* 8 bit MAC address Byte 3 */ +#define B2_MAC_4 0x0104 /* 8 bit MAC address Byte 4 */ +#define B2_MAC_5 0x0105 /* 8 bit MAC address Byte 5 */ +#define B2_MAC_6 0x0106 /* 8 bit MAC address Byte 6 (== 0) (DV) */ +#define B2_MAC_7 0x0107 /* 8 bit MAC address Byte 7 (== 0) (DV) */ + +#define B2_CONN_TYP 0x0108 /* 8 bit Connector type */ +#define B2_PMD_TYP 0x0109 /* 8 bit PMD type */ + /* 0x010a - 0x010b: reserved */ + /* Eprom registers are currently of no use */ +#define B2_E_0 0x010c /* 8 bit EPROM Byte 0 */ +#define B2_E_1 0x010d /* 8 bit EPROM Byte 1 */ +#define B2_E_2 0x010e /* 8 bit EPROM Byte 2 */ +#define B2_E_3 0x010f /* 8 bit EPROM Byte 3 */ +#define B2_FAR 0x0110 /* 32 bit Flash-Prom Address Register/Counter */ +#define B2_FDP 0x0114 /* 8 bit Flash-Prom Data Port */ + /* 0x0115 - 0x0117: reserved */ +#define B2_LD_CRTL 0x0118 /* 8 bit loader control */ +#define B2_LD_TEST 0x0119 /* 8 bit loader test */ + /* 0x011a - 0x011f: reserved */ +#define B2_TI_INI 0x0120 /* 32 bit Timer init value */ +#define B2_TI_VAL 0x0124 /* 32 bit Timer value */ +#define B2_TI_CRTL 0x0128 /* 8 bit Timer control */ +#define B2_TI_TEST 0x0129 /* 8 Bit Timer Test */ + /* 0x012a - 0x012f: reserved */ +#define B2_WDOG_INI 0x0130 /* 32 bit Watchdog init value */ +#define B2_WDOG_VAL 0x0134 /* 32 bit Watchdog value */ +#define B2_WDOG_CRTL 0x0138 /* 8 bit Watchdog control */ +#define B2_WDOG_TEST 0x0139 /* 8 Bit Watchdog Test */ + /* 0x013a - 0x013f: reserved */ +#define B2_RTM_INI 0x0140 /* 32 bit RTM init value */ +#define B2_RTM_VAL 0x0144 /* 32 bit RTM value */ +#define B2_RTM_CRTL 0x0148 /* 8 bit RTM control */ +#define B2_RTM_TEST 0x0149 /* 8 Bit RTM Test */ + +#define B2_TOK_COUNT 0x014c /* (ML) 32 bit Token Counter */ +#define B2_DESC_ADDR_H 0x0150 /* (ML) 32 bit Desciptor Base Addr Reg High */ +#define B2_CTRL_2 0x0154 /* (ML) 8 bit Control Register 2 */ +#define B2_IFACE_REG 0x0155 /* (ML) 8 bit Interface Register */ + /* 0x0156: reserved */ +#define B2_TST_CTRL_2 0x0157 /* (ML) 8 bit Test Control Register 2 */ +#define B2_I2C_CTRL 0x0158 /* (ML) 32 bit I2C Control Register */ +#define B2_I2C_DATA 0x015c /* (ML) 32 bit I2C Data Register */ + +#define B2_IRQ_MOD_INI 0x0160 /* (ML) 32 bit IRQ Moderation Timer Init Reg. */ +#define B2_IRQ_MOD_VAL 0x0164 /* (ML) 32 bit IRQ Moderation Timer Value */ +#define B2_IRQ_MOD_CTRL 0x0168 /* (ML) 8 bit IRQ Moderation Timer Control */ +#define B2_IRQ_MOD_TEST 0x0169 /* (ML) 8 bit IRQ Moderation Timer Test */ + /* 0x016a - 0x017f: reserved */ + +/* + * Bank 3 + */ +/* + * This is a copy of the Configuration register file (lower half) + */ +#define B3_CFG_SPC 0x180 + +/* + * Bank 4 + */ +#define B4_R1_D 0x0200 /* 4*32 bit current receive Descriptor */ +#define B4_R1_DA 0x0210 /* 32 bit current rec desc address */ +#define B4_R1_AC 0x0214 /* 32 bit current receive Address Count */ +#define B4_R1_BC 0x0218 /* 32 bit current receive Byte Counter */ +#define B4_R1_CSR 0x021c /* 32 bit BMU Control/Status Register */ +#define B4_R1_F 0x0220 /* 32 bit flag register */ +#define B4_R1_T1 0x0224 /* 32 bit Test Register 1 */ +#define B4_R1_T1_TR 0x0224 /* 8 bit Test Register 1 TR */ +#define B4_R1_T1_WR 0x0225 /* 8 bit Test Register 1 WR */ +#define B4_R1_T1_RD 0x0226 /* 8 bit Test Register 1 RD */ +#define B4_R1_T1_SV 0x0227 /* 8 bit Test Register 1 SV */ +#define B4_R1_T2 0x0228 /* 32 bit Test Register 2 */ +#define B4_R1_T3 0x022c /* 32 bit Test Register 3 */ +#define B4_R1_DA_H 0x0230 /* (ML) 32 bit Curr Rx Desc Address High */ +#define B4_R1_AC_H 0x0234 /* (ML) 32 bit Curr Addr Counter High dword */ + /* 0x0238 - 0x023f: reserved */ + /* Receive queue 2 is removed on Monalisa */ +#define B4_R2_D 0x0240 /* 4*32 bit current receive Descriptor (q2) */ +#define B4_R2_DA 0x0250 /* 32 bit current rec desc address (q2) */ +#define B4_R2_AC 0x0254 /* 32 bit current receive Address Count (q2) */ +#define B4_R2_BC 0x0258 /* 32 bit current receive Byte Counter (q2) */ +#define B4_R2_CSR 0x025c /* 32 bit BMU Control/Status Register (q2) */ +#define B4_R2_F 0x0260 /* 32 bit flag register (q2) */ +#define B4_R2_T1 0x0264 /* 32 bit Test Register 1 (q2) */ +#define B4_R2_T1_TR 0x0264 /* 8 bit Test Register 1 TR (q2) */ +#define B4_R2_T1_WR 0x0265 /* 8 bit Test Register 1 WR (q2) */ +#define B4_R2_T1_RD 0x0266 /* 8 bit Test Register 1 RD (q2) */ +#define B4_R2_T1_SV 0x0267 /* 8 bit Test Register 1 SV (q2) */ +#define B4_R2_T2 0x0268 /* 32 bit Test Register 2 (q2) */ +#define B4_R2_T3 0x026c /* 32 bit Test Register 3 (q2) */ + /* 0x0270 - 0x027c: reserved */ + +/* + * Bank 5 + */ +#define B5_XA_D 0x0280 /* 4*32 bit current transmit Descriptor (xa) */ +#define B5_XA_DA 0x0290 /* 32 bit current tx desc address (xa) */ +#define B5_XA_AC 0x0294 /* 32 bit current tx Address Count (xa) */ +#define B5_XA_BC 0x0298 /* 32 bit current tx Byte Counter (xa) */ +#define B5_XA_CSR 0x029c /* 32 bit BMU Control/Status Register (xa) */ +#define B5_XA_F 0x02a0 /* 32 bit flag register (xa) */ +#define B5_XA_T1 0x02a4 /* 32 bit Test Register 1 (xa) */ +#define B5_XA_T1_TR 0x02a4 /* 8 bit Test Register 1 TR (xa) */ +#define B5_XA_T1_WR 0x02a5 /* 8 bit Test Register 1 WR (xa) */ +#define B5_XA_T1_RD 0x02a6 /* 8 bit Test Register 1 RD (xa) */ +#define B5_XA_T1_SV 0x02a7 /* 8 bit Test Register 1 SV (xa) */ +#define B5_XA_T2 0x02a8 /* 32 bit Test Register 2 (xa) */ +#define B5_XA_T3 0x02ac /* 32 bit Test Register 3 (xa) */ +#define B5_XA_DA_H 0x02b0 /* (ML) 32 bit Curr Tx Desc Address High */ +#define B5_XA_AC_H 0x02b4 /* (ML) 32 bit Curr Addr Counter High dword */ + /* 0x02b8 - 0x02bc: reserved */ +#define B5_XS_D 0x02c0 /* 4*32 bit current transmit Descriptor (xs) */ +#define B5_XS_DA 0x02d0 /* 32 bit current tx desc address (xs) */ +#define B5_XS_AC 0x02d4 /* 32 bit current transmit Address Count(xs) */ +#define B5_XS_BC 0x02d8 /* 32 bit current transmit Byte Counter (xs) */ +#define B5_XS_CSR 0x02dc /* 32 bit BMU Control/Status Register (xs) */ +#define B5_XS_F 0x02e0 /* 32 bit flag register (xs) */ +#define B5_XS_T1 0x02e4 /* 32 bit Test Register 1 (xs) */ +#define B5_XS_T1_TR 0x02e4 /* 8 bit Test Register 1 TR (xs) */ +#define B5_XS_T1_WR 0x02e5 /* 8 bit Test Register 1 WR (xs) */ +#define B5_XS_T1_RD 0x02e6 /* 8 bit Test Register 1 RD (xs) */ +#define B5_XS_T1_SV 0x02e7 /* 8 bit Test Register 1 SV (xs) */ +#define B5_XS_T2 0x02e8 /* 32 bit Test Register 2 (xs) */ +#define B5_XS_T3 0x02ec /* 32 bit Test Register 3 (xs) */ +#define B5_XS_DA_H 0x02f0 /* (ML) 32 bit Curr Tx Desc Address High */ +#define B5_XS_AC_H 0x02f4 /* (ML) 32 bit Curr Addr Counter High dword */ + /* 0x02f8 - 0x02fc: reserved */ + +/* + * Bank 6 + */ +/* External PLC-S registers (SN2 compatibility for DV) */ +/* External registers (ML) */ +#define B6_EXT_REG 0x300 + +/* + * Bank 7 + */ +/* DAS PLC-S Registers */ + +/* + * Bank 8 - 15 + */ +/* IFCP registers */ + +/*---------------------------------------------------------------------------*/ +/* Definitions of the Bits in the registers */ + +/* B0_RAP 16 bit register address port */ +#define RAP_RAP 0x0f /* Bit 3..0: 0 = block0, .., f = block15 */ + +/* B0_CTRL 8 bit control register */ +#define CTRL_FDDI_CLR (1<<7) /* Bit 7: (ML) Clear FDDI Reset */ +#define CTRL_FDDI_SET (1<<6) /* Bit 6: (ML) Set FDDI Reset */ +#define CTRL_HPI_CLR (1<<5) /* Bit 5: Clear HPI SM reset */ +#define CTRL_HPI_SET (1<<4) /* Bit 4: Set HPI SM reset */ +#define CTRL_MRST_CLR (1<<3) /* Bit 3: Clear Master reset */ +#define CTRL_MRST_SET (1<<2) /* Bit 2: Set Master reset */ +#define CTRL_RST_CLR (1<<1) /* Bit 1: Clear Software reset */ +#define CTRL_RST_SET (1<<0) /* Bit 0: Set Software reset */ + +/* B0_DAS 8 Bit control register (DAS) */ +#define BUS_CLOCK (1<<7) /* Bit 7: (ML) Bus Clock 0/1 = 33/66MHz */ +#define BUS_SLOT_SZ (1<<6) /* Bit 6: (ML) Slot Size 0/1 = 32/64 bit slot*/ + /* Bit 5..4: reserved */ +#define DAS_AVAIL (1<<3) /* Bit 3: 1 = DAS, 0 = SAS */ +#define DAS_BYP_ST (1<<2) /* Bit 2: 1 = avail,SAS, 0 = not avail */ +#define DAS_BYP_INS (1<<1) /* Bit 1: 1 = insert Bypass */ +#define DAS_BYP_RMV (1<<0) /* Bit 0: 1 = remove Bypass */ + +/* B0_LED 8 Bit LED register */ + /* Bit 7..6: reserved */ +#define LED_2_ON (1<<5) /* Bit 5: 1 = switch LED_2 on (left,gn)*/ +#define LED_2_OFF (1<<4) /* Bit 4: 1 = switch LED_2 off */ +#define LED_1_ON (1<<3) /* Bit 3: 1 = switch LED_1 on (mid,yel)*/ +#define LED_1_OFF (1<<2) /* Bit 2: 1 = switch LED_1 off */ +#define LED_0_ON (1<<1) /* Bit 1: 1 = switch LED_0 on (rght,gn)*/ +#define LED_0_OFF (1<<0) /* Bit 0: 1 = switch LED_0 off */ +/* This hardware defines are very ugly therefore we define some others */ + +#define LED_GA_ON LED_2_ON /* S port = A port */ +#define LED_GA_OFF LED_2_OFF /* S port = A port */ +#define LED_MY_ON LED_1_ON +#define LED_MY_OFF LED_1_OFF +#define LED_GB_ON LED_0_ON +#define LED_GB_OFF LED_0_OFF + +/* B0_TST_CTRL 8 bit test control register */ +#define TST_FRC_DPERR_MR (1<<7) /* Bit 7: force DATAPERR on MST RE. */ +#define TST_FRC_DPERR_MW (1<<6) /* Bit 6: force DATAPERR on MST WR. */ +#define TST_FRC_DPERR_TR (1<<5) /* Bit 5: force DATAPERR on TRG RE. */ +#define TST_FRC_DPERR_TW (1<<4) /* Bit 4: force DATAPERR on TRG WR. */ +#define TST_FRC_APERR_M (1<<3) /* Bit 3: force ADDRPERR on MST */ +#define TST_FRC_APERR_T (1<<2) /* Bit 2: force ADDRPERR on TRG */ +#define TST_CFG_WRITE_ON (1<<1) /* Bit 1: ena configuration reg. WR */ +#define TST_CFG_WRITE_OFF (1<<0) /* Bit 0: dis configuration reg. WR */ + +/* B0_ISRC 32 bit Interrupt source register */ + /* Bit 31..28: reserved */ +#define IS_I2C_READY (1L<<27) /* Bit 27: (ML) IRQ on end of I2C tx */ +#define IS_IRQ_SW (1L<<26) /* Bit 26: (ML) SW forced IRQ */ +#define IS_EXT_REG (1L<<25) /* Bit 25: (ML) IRQ from external reg*/ +#define IS_IRQ_STAT (1L<<24) /* Bit 24: IRQ status execption */ + /* PERR, RMABORT, RTABORT DATAPERR */ +#define IS_IRQ_MST_ERR (1L<<23) /* Bit 23: IRQ master error */ + /* RMABORT, RTABORT, DATAPERR */ +#define IS_TIMINT (1L<<22) /* Bit 22: IRQ_TIMER */ +#define IS_TOKEN (1L<<21) /* Bit 21: IRQ_RTM */ +/* + * Note: The DAS is our First Port (!=PA) + */ +#define IS_PLINT1 (1L<<20) /* Bit 20: IRQ_PHY_DAS */ +#define IS_PLINT2 (1L<<19) /* Bit 19: IRQ_IFCP_4 */ +#define IS_MINTR3 (1L<<18) /* Bit 18: IRQ_IFCP_3/IRQ_PHY */ +#define IS_MINTR2 (1L<<17) /* Bit 17: IRQ_IFCP_2/IRQ_MAC_2 */ +#define IS_MINTR1 (1L<<16) /* Bit 16: IRQ_IFCP_1/IRQ_MAC_1 */ +/* Receive Queue 1 */ +#define IS_R1_P (1L<<15) /* Bit 15: Parity Error (q1) */ +#define IS_R1_B (1L<<14) /* Bit 14: End of Buffer (q1) */ +#define IS_R1_F (1L<<13) /* Bit 13: End of Frame (q1) */ +#define IS_R1_C (1L<<12) /* Bit 12: Encoding Error (q1) */ +/* Receive Queue 2 */ +#define IS_R2_P (1L<<11) /* Bit 11: (DV) Parity Error (q2) */ +#define IS_R2_B (1L<<10) /* Bit 10: (DV) End of Buffer (q2) */ +#define IS_R2_F (1L<<9) /* Bit 9: (DV) End of Frame (q2) */ +#define IS_R2_C (1L<<8) /* Bit 8: (DV) Encoding Error (q2) */ +/* Asynchronous Transmit queue */ + /* Bit 7: reserved */ +#define IS_XA_B (1L<<6) /* Bit 6: End of Buffer (xa) */ +#define IS_XA_F (1L<<5) /* Bit 5: End of Frame (xa) */ +#define IS_XA_C (1L<<4) /* Bit 4: Encoding Error (xa) */ +/* Synchronous Transmit queue */ + /* Bit 3: reserved */ +#define IS_XS_B (1L<<2) /* Bit 2: End of Buffer (xs) */ +#define IS_XS_F (1L<<1) /* Bit 1: End of Frame (xs) */ +#define IS_XS_C (1L<<0) /* Bit 0: Encoding Error (xs) */ + +/* + * Define all valid interrupt source Bits from GET_ISR () + */ +#define ALL_IRSR 0x01ffff77L /* (DV) */ +#define ALL_IRSR_ML 0x0ffff077L /* (ML) */ + + +/* B0_IMSK 32 bit Interrupt mask register */ +/* + * The Bit definnition of this register are the same as of the interrupt + * source register. These definition are directly derived from the Hardware + * spec. + */ + /* Bit 31..28: reserved */ +#define IRQ_I2C_READY (1L<<27) /* Bit 27: (ML) IRQ on end of I2C tx */ +#define IRQ_SW (1L<<26) /* Bit 26: (ML) SW forced IRQ */ +#define IRQ_EXT_REG (1L<<25) /* Bit 25: (ML) IRQ from external reg*/ +#define IRQ_STAT (1L<<24) /* Bit 24: IRQ status execption */ + /* PERR, RMABORT, RTABORT DATAPERR */ +#define IRQ_MST_ERR (1L<<23) /* Bit 23: IRQ master error */ + /* RMABORT, RTABORT, DATAPERR */ +#define IRQ_TIMER (1L<<22) /* Bit 22: IRQ_TIMER */ +#define IRQ_RTM (1L<<21) /* Bit 21: IRQ_RTM */ +#define IRQ_DAS (1L<<20) /* Bit 20: IRQ_PHY_DAS */ +#define IRQ_IFCP_4 (1L<<19) /* Bit 19: IRQ_IFCP_4 */ +#define IRQ_IFCP_3 (1L<<18) /* Bit 18: IRQ_IFCP_3/IRQ_PHY */ +#define IRQ_IFCP_2 (1L<<17) /* Bit 17: IRQ_IFCP_2/IRQ_MAC_2 */ +#define IRQ_IFCP_1 (1L<<16) /* Bit 16: IRQ_IFCP_1/IRQ_MAC_1 */ +/* Receive Queue 1 */ +#define IRQ_R1_P (1L<<15) /* Bit 15: Parity Error (q1) */ +#define IRQ_R1_B (1L<<14) /* Bit 14: End of Buffer (q1) */ +#define IRQ_R1_F (1L<<13) /* Bit 13: End of Frame (q1) */ +#define IRQ_R1_C (1L<<12) /* Bit 12: Encoding Error (q1) */ +/* Receive Queue 2 */ +#define IRQ_R2_P (1L<<11) /* Bit 11: (DV) Parity Error (q2) */ +#define IRQ_R2_B (1L<<10) /* Bit 10: (DV) End of Buffer (q2) */ +#define IRQ_R2_F (1L<<9) /* Bit 9: (DV) End of Frame (q2) */ +#define IRQ_R2_C (1L<<8) /* Bit 8: (DV) Encoding Error (q2) */ +/* Asynchronous Transmit queue */ + /* Bit 7: reserved */ +#define IRQ_XA_B (1L<<6) /* Bit 6: End of Buffer (xa) */ +#define IRQ_XA_F (1L<<5) /* Bit 5: End of Frame (xa) */ +#define IRQ_XA_C (1L<<4) /* Bit 4: Encoding Error (xa) */ +/* Synchronous Transmit queue */ + /* Bit 3: reserved */ +#define IRQ_XS_B (1L<<2) /* Bit 2: End of Buffer (xs) */ +#define IRQ_XS_F (1L<<1) /* Bit 1: End of Frame (xs) */ +#define IRQ_XS_C (1L<<0) /* Bit 0: Encoding Error (xs) */ + +/* 0x0010 - 0x006b: formac+ (supernet_3) fequently used registers */ +/* B0_R1_CSR 32 bit BMU control/status reg (rec q 1 ) */ +/* B0_R2_CSR 32 bit BMU control/status reg (rec q 2 ) */ +/* B0_XA_CSR 32 bit BMU control/status reg (a xmit q ) */ +/* B0_XS_CSR 32 bit BMU control/status reg (s xmit q ) */ +/* The registers are the same as B4_R1_CSR, B4_R2_CSR, B5_Xa_CSR, B5_XS_CSR */ + +/* B2_MAC_0 8 bit MAC address Byte 0 */ +/* B2_MAC_1 8 bit MAC address Byte 1 */ +/* B2_MAC_2 8 bit MAC address Byte 2 */ +/* B2_MAC_3 8 bit MAC address Byte 3 */ +/* B2_MAC_4 8 bit MAC address Byte 4 */ +/* B2_MAC_5 8 bit MAC address Byte 5 */ +/* B2_MAC_6 8 bit MAC address Byte 6 (== 0) (DV) */ +/* B2_MAC_7 8 bit MAC address Byte 7 (== 0) (DV) */ + +/* B2_CONN_TYP 8 bit Connector type */ +/* B2_PMD_TYP 8 bit PMD type */ +/* Values of connector and PMD type comply to SysKonnect internal std */ + +/* The EPROM register are currently of no use */ +/* B2_E_0 8 bit EPROM Byte 0 */ +/* B2_E_1 8 bit EPROM Byte 1 */ +/* B2_E_2 8 bit EPROM Byte 2 */ +/* B2_E_3 8 bit EPROM Byte 3 */ + +/* B2_FAR 32 bit Flash-Prom Address Register/Counter */ +#define FAR_ADDR 0x1ffffL /* Bit 16..0: FPROM Address mask */ + +/* B2_FDP 8 bit Flash-Prom Data Port */ + +/* B2_LD_CRTL 8 bit loader control */ +/* Bits are currently reserved */ + +/* B2_LD_TEST 8 bit loader test */ +#define LD_T_ON (1<<3) /* Bit 3: Loader Testmode on */ +#define LD_T_OFF (1<<2) /* Bit 2: Loader Testmode off */ +#define LD_T_STEP (1<<1) /* Bit 1: Decrement FPROM addr. Counter */ +#define LD_START (1<<0) /* Bit 0: Start loading FPROM */ + +/* B2_TI_INI 32 bit Timer init value */ +/* B2_TI_VAL 32 bit Timer value */ +/* B2_TI_CRTL 8 bit Timer control */ +/* B2_TI_TEST 8 Bit Timer Test */ +/* B2_WDOG_INI 32 bit Watchdog init value */ +/* B2_WDOG_VAL 32 bit Watchdog value */ +/* B2_WDOG_CRTL 8 bit Watchdog control */ +/* B2_WDOG_TEST 8 Bit Watchdog Test */ +/* B2_RTM_INI 32 bit RTM init value */ +/* B2_RTM_VAL 32 bit RTM value */ +/* B2_RTM_CRTL 8 bit RTM control */ +/* B2_RTM_TEST 8 Bit RTM Test */ +/* B2__CRTL 8 bit control */ +/* B2_IRQ_MOD_INI 32 bit IRQ Moderation Timer Init Reg. (ML) */ +/* B2_IRQ_MOD_VAL 32 bit IRQ Moderation Timer Value (ML) */ +/* B2_IRQ_MOD_CTRL 8 bit IRQ Moderation Timer Control (ML) */ +/* B2_IRQ_MOD_TEST 8 bit IRQ Moderation Timer Test (ML) */ +#define GET_TOK_CT (1<<4) /* Bit 4: Get the Token Counter (RTM) */ +#define TIM_RES_TOK (1<<3) /* Bit 3: RTM Status: 1 == restricted */ +#define TIM_ALARM (1<<3) /* Bit 3: Timer Alarm (WDOG) */ +#define TIM_START (1<<2) /* Bit 2: Start Timer (TI,WDOG,RTM,IRQ_MOD)*/ +#define TIM_STOP (1<<1) /* Bit 1: Stop Timer (TI,WDOG,RTM,IRQ_MOD) */ +#define TIM_CL_IRQ (1<<0) /* Bit 0: Clear Timer IRQ (TI,WDOG,RTM) */ +/* B2__TEST 8 Bit Test */ +#define TIM_T_ON (1<<2) /* Bit 2: Test mode on (TI,WDOG,RTM,IRQ_MOD) */ +#define TIM_T_OFF (1<<1) /* Bit 1: Test mode off (TI,WDOG,RTM,IRQ_MOD) */ +#define TIM_T_STEP (1<<0) /* Bit 0: Test step (TI,WDOG,RTM,IRQ_MOD) */ + +/* B2_TOK_COUNT 0x014c (ML) 32 bit Token Counter */ +/* B2_DESC_ADDR_H 0x0150 (ML) 32 bit Desciptor Base Addr Reg High */ +/* B2_CTRL_2 0x0154 (ML) 8 bit Control Register 2 */ + /* Bit 7..5: reserved */ +#define CTRL_CL_I2C_IRQ (1<<4) /* Bit 4: Clear I2C IRQ */ +#define CTRL_ST_SW_IRQ (1<<3) /* Bit 3: Set IRQ SW Request */ +#define CTRL_CL_SW_IRQ (1<<2) /* Bit 2: Clear IRQ SW Request */ +#define CTRL_STOP_DONE (1<<1) /* Bit 1: Stop Master is finished */ +#define CTRL_STOP_MAST (1<<0) /* Bit 0: Command Bit to stop the master*/ + +/* B2_IFACE_REG 0x0155 (ML) 8 bit Interface Register */ + /* Bit 7..3: reserved */ +#define IF_I2C_DATA_DIR (1<<2) /* Bit 2: direction of IF_I2C_DATA*/ +#define IF_I2C_DATA (1<<1) /* Bit 1: I2C Data Port */ +#define IF_I2C_CLK (1<<0) /* Bit 0: I2C Clock Port */ + + /* 0x0156: reserved */ +/* B2_TST_CTRL_2 0x0157 (ML) 8 bit Test Control Register 2 */ + /* Bit 7..4: reserved */ + /* force the following error on */ + /* the next master read/write */ +#define TST_FRC_DPERR_MR64 (1<<3) /* Bit 3: DataPERR RD 64 */ +#define TST_FRC_DPERR_MW64 (1<<2) /* Bit 2: DataPERR WR 64 */ +#define TST_FRC_APERR_1M64 (1<<1) /* Bit 1: AddrPERR on 1. phase */ +#define TST_FRC_APERR_2M64 (1<<0) /* Bit 0: AddrPERR on 2. phase */ + +/* B2_I2C_CTRL 0x0158 (ML) 32 bit I2C Control Register */ +#define I2C_FLAG (1L<<31) /* Bit 31: Start read/write if WR */ +#define I2C_ADDR (0x7fffL<<16) /* Bit 30..16: Addr to be read/written*/ +#define I2C_DEV_SEL (0x7fL<<9) /* Bit 9..15: I2C Device Select */ + /* Bit 5.. 8: reserved */ +#define I2C_BURST_LEN (1L<<4) /* Bit 4 Burst Len, 1/4 bytes */ +#define I2C_DEV_SIZE (7L<<1) /* Bit 1.. 3: I2C Device Size */ +#define I2C_025K_DEV (0L<<1) /* 0: 256 Bytes or smaller*/ +#define I2C_05K_DEV (1L<<1) /* 1: 512 Bytes */ +#define I2C_1K_DEV (2L<<1) /* 2: 1024 Bytes */ +#define I2C_2K_DEV (3L<<1) /* 3: 2048 Bytes */ +#define I2C_4K_DEV (4L<<1) /* 4: 4096 Bytes */ +#define I2C_8K_DEV (5L<<1) /* 5: 8192 Bytes */ +#define I2C_16K_DEV (6L<<1) /* 6: 16384 Bytes */ +#define I2C_32K_DEV (7L<<1) /* 7: 32768 Bytes */ +#define I2C_STOP_BIT (1<<0) /* Bit 0: Interrupt I2C transfer */ + +/* + * I2C Addresses + * + * The temperature sensor and the voltage sensor are on the same I2C bus. + * Note: The voltage sensor (Micorwire) will be selected by PCI_EXT_PATCH_1 + * in PCI_OUR_REG 1. + */ +#define I2C_ADDR_TEMP 0x90 /* I2C Address Temperature Sensor */ + +/* B2_I2C_DATA 0x015c (ML) 32 bit I2C Data Register */ + +/* B4_R1_D 4*32 bit current receive Descriptor (q1) */ +/* B4_R1_DA 32 bit current rec desc address (q1) */ +/* B4_R1_AC 32 bit current receive Address Count (q1) */ +/* B4_R1_BC 32 bit current receive Byte Counter (q1) */ +/* B4_R1_CSR 32 bit BMU Control/Status Register (q1) */ +/* B4_R1_F 32 bit flag register (q1) */ +/* B4_R1_T1 32 bit Test Register 1 (q1) */ +/* B4_R1_T2 32 bit Test Register 2 (q1) */ +/* B4_R1_T3 32 bit Test Register 3 (q1) */ +/* B4_R2_D 4*32 bit current receive Descriptor (q2) */ +/* B4_R2_DA 32 bit current rec desc address (q2) */ +/* B4_R2_AC 32 bit current receive Address Count (q2) */ +/* B4_R2_BC 32 bit current receive Byte Counter (q2) */ +/* B4_R2_CSR 32 bit BMU Control/Status Register (q2) */ +/* B4_R2_F 32 bit flag register (q2) */ +/* B4_R2_T1 32 bit Test Register 1 (q2) */ +/* B4_R2_T2 32 bit Test Register 2 (q2) */ +/* B4_R2_T3 32 bit Test Register 3 (q2) */ +/* B5_XA_D 4*32 bit current receive Descriptor (xa) */ +/* B5_XA_DA 32 bit current rec desc address (xa) */ +/* B5_XA_AC 32 bit current receive Address Count (xa) */ +/* B5_XA_BC 32 bit current receive Byte Counter (xa) */ +/* B5_XA_CSR 32 bit BMU Control/Status Register (xa) */ +/* B5_XA_F 32 bit flag register (xa) */ +/* B5_XA_T1 32 bit Test Register 1 (xa) */ +/* B5_XA_T2 32 bit Test Register 2 (xa) */ +/* B5_XA_T3 32 bit Test Register 3 (xa) */ +/* B5_XS_D 4*32 bit current receive Descriptor (xs) */ +/* B5_XS_DA 32 bit current rec desc address (xs) */ +/* B5_XS_AC 32 bit current receive Address Count (xs) */ +/* B5_XS_BC 32 bit current receive Byte Counter (xs) */ +/* B5_XS_CSR 32 bit BMU Control/Status Register (xs) */ +/* B5_XS_F 32 bit flag register (xs) */ +/* B5_XS_T1 32 bit Test Register 1 (xs) */ +/* B5_XS_T2 32 bit Test Register 2 (xs) */ +/* B5_XS_T3 32 bit Test Register 3 (xs) */ +/* B5__CSR 32 bit BMU Control/Status Register (xx) */ +#define CSR_DESC_CLEAR (1L<<21) /* Bit 21: Clear Reset for Descr */ +#define CSR_DESC_SET (1L<<20) /* Bit 20: Set Reset for Descr */ +#define CSR_FIFO_CLEAR (1L<<19) /* Bit 19: Clear Reset for FIFO */ +#define CSR_FIFO_SET (1L<<18) /* Bit 18: Set Reset for FIFO */ +#define CSR_HPI_RUN (1L<<17) /* Bit 17: Release HPI SM */ +#define CSR_HPI_RST (1L<<16) /* Bit 16: Reset HPI SM to Idle */ +#define CSR_SV_RUN (1L<<15) /* Bit 15: Release Supervisor SM */ +#define CSR_SV_RST (1L<<14) /* Bit 14: Reset Supervisor SM */ +#define CSR_DREAD_RUN (1L<<13) /* Bit 13: Release Descr Read SM */ +#define CSR_DREAD_RST (1L<<12) /* Bit 12: Reset Descr Read SM */ +#define CSR_DWRITE_RUN (1L<<11) /* Bit 11: Rel. Descr Write SM */ +#define CSR_DWRITE_RST (1L<<10) /* Bit 10: Reset Descr Write SM */ +#define CSR_TRANS_RUN (1L<<9) /* Bit 9: Release Transfer SM */ +#define CSR_TRANS_RST (1L<<8) /* Bit 8: Reset Transfer SM */ + /* Bit 7..5: reserved */ +#define CSR_START (1L<<4) /* Bit 4: Start Rec/Xmit Queue */ +#define CSR_IRQ_CL_P (1L<<3) /* Bit 3: Clear Parity IRQ, Rcv */ +#define CSR_IRQ_CL_B (1L<<2) /* Bit 2: Clear EOB IRQ */ +#define CSR_IRQ_CL_F (1L<<1) /* Bit 1: Clear EOF IRQ */ +#define CSR_IRQ_CL_C (1L<<0) /* Bit 0: Clear ERR IRQ */ + +#define CSR_SET_RESET (CSR_DESC_SET|CSR_FIFO_SET|CSR_HPI_RST|CSR_SV_RST|\ + CSR_DREAD_RST|CSR_DWRITE_RST|CSR_TRANS_RST) +#define CSR_CLR_RESET (CSR_DESC_CLEAR|CSR_FIFO_CLEAR|CSR_HPI_RUN|CSR_SV_RUN|\ + CSR_DREAD_RUN|CSR_DWRITE_RUN|CSR_TRANS_RUN) + + +/* B5__F 32 bit flag register (xx) */ + /* Bit 28..31: reserved */ +#define F_ALM_FULL (1L<<27) /* Bit 27: (ML) FIFO almost full */ +#define F_FIFO_EOF (1L<<26) /* Bit 26: (ML) Fag bit in FIFO */ +#define F_WM_REACHED (1L<<25) /* Bit 25: (ML) Watermark reached */ +#define F_UP_DW_USED (1L<<24) /* Bit 24: (ML) Upper Dword used (bug)*/ + /* Bit 23: reserved */ +#define F_FIFO_LEVEL (0x1fL<<16) /* Bit 16..22:(ML) # of Qwords in FIFO*/ + /* Bit 8..15: reserved */ +#define F_ML_WATER_M 0x0000ffL /* Bit 0.. 7:(ML) Watermark */ +#define FLAG_WATER 0x00001fL /* Bit 4..0:(DV) Level of req data tr.*/ + +/* B5__T1 32 bit Test Register 1 (xx) */ +/* Holds four State Machine control Bytes */ +#define SM_CRTL_SV (0xffL<<24) /* Bit 31..24: Control Supervisor SM */ +#define SM_CRTL_RD (0xffL<<16) /* Bit 23..16: Control Read Desc SM */ +#define SM_CRTL_WR (0xffL<<8) /* Bit 15..8: Control Write Desc SM */ +#define SM_CRTL_TR (0xffL<<0) /* Bit 7..0: Control Transfer SM */ + +/* B4__T1_TR 8 bit Test Register 1 TR (xx) */ +/* B4__T1_WR 8 bit Test Register 1 WR (xx) */ +/* B4__T1_RD 8 bit Test Register 1 RD (xx) */ +/* B4__T1_SV 8 bit Test Register 1 SV (xx) */ +/* The control status byte of each machine looks like ... */ +#define SM_STATE 0xf0 /* Bit 7..4: State which shall be loaded */ +#define SM_LOAD 0x08 /* Bit 3: Load the SM with SM_STATE */ +#define SM_TEST_ON 0x04 /* Bit 2: Switch on SM Test Mode */ +#define SM_TEST_OFF 0x02 /* Bit 1: Go off the Test Mode */ +#define SM_STEP 0x01 /* Bit 0: Step the State Machine */ + +/* The coding of the states */ +#define SM_SV_IDLE 0x0 /* Supervisor Idle Tr/Re */ +#define SM_SV_RES_START 0x1 /* Supervisor Res_Start Tr/Re */ +#define SM_SV_GET_DESC 0x3 /* Supervisor Get_Desc Tr/Re */ +#define SM_SV_CHECK 0x2 /* Supervisor Check Tr/Re */ +#define SM_SV_MOV_DATA 0x6 /* Supervisor Move_Data Tr/Re */ +#define SM_SV_PUT_DESC 0x7 /* Supervisor Put_Desc Tr/Re */ +#define SM_SV_SET_IRQ 0x5 /* Supervisor Set_Irq Tr/Re */ + +#define SM_RD_IDLE 0x0 /* Read Desc. Idle Tr/Re */ +#define SM_RD_LOAD 0x1 /* Read Desc. Load Tr/Re */ +#define SM_RD_WAIT_TC 0x3 /* Read Desc. Wait_TC Tr/Re */ +#define SM_RD_RST_EOF 0x6 /* Read Desc. Reset_EOF Re */ +#define SM_RD_WDONE_R 0x2 /* Read Desc. Wait_Done Re */ +#define SM_RD_WDONE_T 0x4 /* Read Desc. Wait_Done Tr */ + +#define SM_TR_IDLE 0x0 /* Trans. Data Idle Tr/Re */ +#define SM_TR_LOAD 0x3 /* Trans. Data Load Tr/Re */ +#define SM_TR_LOAD_R_ML 0x1 /* Trans. Data Load /Re (ML) */ +#define SM_TR_WAIT_TC 0x2 /* Trans. Data Wait_TC Tr/Re */ +#define SM_TR_WDONE 0x4 /* Trans. Data Wait_Done Tr/Re */ + +#define SM_WR_IDLE 0x0 /* Write Desc. Idle Tr/Re */ +#define SM_WR_ABLEN 0x1 /* Write Desc. Act_Buf_Length Tr/Re */ +#define SM_WR_LD_A4 0x2 /* Write Desc. Load_A4 Re */ +#define SM_WR_RES_OWN 0x2 /* Write Desc. Res_OWN Tr */ +#define SM_WR_WAIT_EOF 0x3 /* Write Desc. Wait_EOF Re */ +#define SM_WR_LD_N2C_R 0x4 /* Write Desc. Load_N2C Re */ +#define SM_WR_WAIT_TC_R 0x5 /* Write Desc. Wait_TC Re */ +#define SM_WR_WAIT_TC4 0x6 /* Write Desc. Wait_TC4 Re */ +#define SM_WR_LD_A_T 0x6 /* Write Desc. Load_A Tr */ +#define SM_WR_LD_A_R 0x7 /* Write Desc. Load_A Re */ +#define SM_WR_WAIT_TC_T 0x7 /* Write Desc. Wait_TC Tr */ +#define SM_WR_LD_N2C_T 0xc /* Write Desc. Load_N2C Tr */ +#define SM_WR_WDONE_T 0x9 /* Write Desc. Wait_Done Tr */ +#define SM_WR_WDONE_R 0xc /* Write Desc. Wait_Done Re */ +#define SM_WR_LD_D_AD 0xe /* Write Desc. Load_Dumr_A Re (ML) */ +#define SM_WR_WAIT_D_TC 0xf /* Write Desc. Wait_Dumr_TC Re (ML) */ + +/* B5__T2 32 bit Test Register 2 (xx) */ +/* Note: This register is only defined for the transmit queues */ + /* Bit 31..8: reserved */ +#define AC_TEST_ON (1<<7) /* Bit 7: Address Counter Test Mode on */ +#define AC_TEST_OFF (1<<6) /* Bit 6: Address Counter Test Mode off*/ +#define BC_TEST_ON (1<<5) /* Bit 5: Byte Counter Test Mode on */ +#define BC_TEST_OFF (1<<4) /* Bit 4: Byte Counter Test Mode off */ +#define TEST_STEP04 (1<<3) /* Bit 3: Inc AC/Dec BC by 4 */ +#define TEST_STEP03 (1<<2) /* Bit 2: Inc AC/Dec BC by 3 */ +#define TEST_STEP02 (1<<1) /* Bit 1: Inc AC/Dec BC by 2 */ +#define TEST_STEP01 (1<<0) /* Bit 0: Inc AC/Dec BC by 1 */ + +/* B5__T3 32 bit Test Register 3 (xx) */ +/* Note: This register is only defined for the transmit queues */ + /* Bit 31..8: reserved */ +#define T3_MUX_2 (1<<7) /* Bit 7: (ML) Mux position MSB */ +#define T3_VRAM_2 (1<<6) /* Bit 6: (ML) Virtual RAM buffer addr MSB */ +#define T3_LOOP (1<<5) /* Bit 5: Set Loopback (Xmit) */ +#define T3_UNLOOP (1<<4) /* Bit 4: Unset Loopback (Xmit) */ +#define T3_MUX (3<<2) /* Bit 3..2: Mux position */ +#define T3_VRAM (3<<0) /* Bit 1..0: Virtual RAM buffer Address */ + +/* PCI card IDs */ +/* + * Note: The following 4 byte definitions shall not be used! Use OEM Concept! + */ +#define PCI_VEND_ID0 0x48 /* PCI vendor ID (SysKonnect) */ +#define PCI_VEND_ID1 0x11 /* PCI vendor ID (SysKonnect) */ + /* (High byte) */ +#define PCI_DEV_ID0 0x00 /* PCI device ID */ +#define PCI_DEV_ID1 0x40 /* PCI device ID (High byte) */ + +/*#define PCI_CLASS 0x02*/ /* PCI class code: network device */ +#define PCI_NW_CLASS 0x02 /* PCI class code: network device */ +#define PCI_SUB_CLASS 0x02 /* PCI subclass ID: FDDI device */ +#define PCI_PROG_INTFC 0x00 /* PCI programming Interface (=0) */ + +/* + * address transmision from logical to physical offset address on board + */ +#define FMA(a) (0x0400|((a)<<2)) /* FORMAC+ (r/w) (SN3) */ +#define P1(a) (0x0380|((a)<<2)) /* PLC1 (r/w) (DAS) */ +#define P2(a) (0x0600|((a)<<2)) /* PLC2 (r/w) (covered by the SN3) */ +#define PRA(a) (B2_MAC_0 + (a)) /* configuration PROM (MAC address) */ + +/* + * FlashProm specification + */ +#define MAX_PAGES 0x20000L /* Every byte has a single page */ +#define MAX_FADDR 1 /* 1 byte per page */ + +/* + * Receive / Transmit Buffer Control word + */ +#define BMU_OWN (1UL<<31) /* OWN bit: 0 == host, 1 == adapter */ +#define BMU_STF (1L<<30) /* Start of Frame ? */ +#define BMU_EOF (1L<<29) /* End of Frame ? */ +#define BMU_EN_IRQ_EOB (1L<<28) /* Enable "End of Buffer" IRQ */ +#define BMU_EN_IRQ_EOF (1L<<27) /* Enable "End of Frame" IRQ */ +#define BMU_DEV_0 (1L<<26) /* RX: don't transfer to system mem */ +#define BMU_SMT_TX (1L<<25) /* TX: if set, buffer type SMT_MBuf */ +#define BMU_ST_BUF (1L<<25) /* RX: copy of start of frame */ +#define BMU_UNUSED (1L<<24) /* Set if the Descr is curr unused */ +#define BMU_SW (3L<<24) /* 2 Bits reserved for SW usage */ +#define BMU_CHECK 0x00550000L /* To identify the control word */ +#define BMU_BBC 0x0000FFFFL /* R/T Buffer Byte Count */ + +/* + * physical address offset + IO-Port base address + */ +#ifdef MEM_MAPPED_IO +#define ADDR(a) (char far *) smc->hw.iop+(a) +#define ADDRS(smc,a) (char far *) (smc)->hw.iop+(a) +#else +#define ADDR(a) (((a)>>7) ? (outp(smc->hw.iop+B0_RAP,(a)>>7), \ + (smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) : \ + (smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) +#define ADDRS(smc,a) (((a)>>7) ? (outp((smc)->hw.iop+B0_RAP,(a)>>7), \ + ((smc)->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) : \ + ((smc)->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) +#endif + +/* + * Define a macro to access the configuration space + */ +#define PCI_C(a) ADDR(B3_CFG_SPC + (a)) /* PCI Config Space */ + +#define EXT_R(a) ADDR(B6_EXT_REG + (a)) /* External Registers */ + +/* + * Define some values needed for the MAC address (PROM) + */ +#define SA_MAC (0) /* start addr. MAC_AD within the PROM */ +#define PRA_OFF (0) /* offset correction when 4th byte reading */ + +#define SKFDDI_PSZ 8 /* address PROM size */ + +#define FM_A(a) ADDR(FMA(a)) /* FORMAC Plus physical addr */ +#define P1_A(a) ADDR(P1(a)) /* PLC1 (r/w) */ +#define P2_A(a) ADDR(P2(a)) /* PLC2 (r/w) (DAS) */ +#define PR_A(a) ADDR(PRA(a)) /* config. PROM (MAC address) */ + +/* + * Macro to read the PROM + */ +#define READ_PROM(a) ((u_char)inp(a)) + +#define GET_PAGE(bank) outpd(ADDR(B2_FAR),bank) +#define VPP_ON() +#define VPP_OFF() + +/* + * Note: Values of the Interrupt Source Register are defined above + */ +#define ISR_A ADDR(B0_ISRC) +#define GET_ISR() inpd(ISR_A) +#define GET_ISR_SMP(iop) inpd((iop)+B0_ISRC) +#define CHECK_ISR() (inpd(ISR_A) & inpd(ADDR(B0_IMSK))) +#define CHECK_ISR_SMP(iop) (inpd((iop)+B0_ISRC) & inpd((iop)+B0_IMSK)) + +#define BUS_CHECK() + +/* + * CLI_FBI: Disable Board Interrupts + * STI_FBI: Enable Board Interrupts + */ +#ifndef UNIX +#define CLI_FBI() outpd(ADDR(B0_IMSK),0) +#else +#define CLI_FBI(smc) outpd(ADDRS((smc),B0_IMSK),0) +#endif + +#ifndef UNIX +#define STI_FBI() outpd(ADDR(B0_IMSK),smc->hw.is_imask) +#else +#define STI_FBI(smc) outpd(ADDRS((smc),B0_IMSK),(smc)->hw.is_imask) +#endif + +#define CLI_FBI_SMP(iop) outpd((iop)+B0_IMSK,0) +#define STI_FBI_SMP(smc,iop) outpd((iop)+B0_IMSK,(smc)->hw.is_imask) + +#endif /* PCI */ +/*--------------------------------------------------------------------------*/ + +/* + * 12 bit transfer (dword) counter: + * (ISA: 2*trc = number of byte) + * (EISA: 4*trc = number of byte) + * (MCA: 4*trc = number of byte) + */ +#define MAX_TRANS (0x0fff) + +/* + * PC PIC + */ +#define MST_8259 (0x20) +#define SLV_8259 (0xA0) + +#define TPS (18) /* ticks per second */ + +/* + * error timer defs + */ +#define TN (4) /* number of supported timer = TN+1 */ +#define SNPPND_TIME (5) /* buffer memory access over mem. data reg. */ + +#define MAC_AD 0x405a0000 + +#define MODR1 FM_A(FM_MDREG1) /* mode register 1 */ +#define MODR2 FM_A(FM_MDREG2) /* mode register 2 */ + +#define CMDR1 FM_A(FM_CMDREG1) /* command register 1 */ +#define CMDR2 FM_A(FM_CMDREG2) /* command register 2 */ + + +/* + * function defines + */ +#define CLEAR(io,mask) outpw((io),inpw(io)&(~(mask))) +#define SET(io,mask) outpw((io),inpw(io)|(mask)) +#define GET(io,mask) (inpw(io)&(mask)) +#define SETMASK(io,val,mask) outpw((io),(inpw(io) & ~(mask)) | (val)) + +/* + * PHY Port A (PA) = PLC 1 + * With SuperNet 3 PHY-A and PHY S are identical. + */ +#define PLC(np,reg) (((np) == PA) ? P2_A(reg) : P1_A(reg)) + +/* + * set memory address register for write and read + */ +#define MARW(ma) outpw(FM_A(FM_MARW),(unsigned int)(ma)) +#define MARR(ma) outpw(FM_A(FM_MARR),(unsigned int)(ma)) + +/* + * read/write from/to memory data register + */ +/* write double word */ +#define MDRW(dd) outpw(FM_A(FM_MDRU),(unsigned int)((dd)>>16)) ;\ + outpw(FM_A(FM_MDRL),(unsigned int)(dd)) + +#ifndef WINNT +/* read double word */ +#define MDRR() (((long)inpw(FM_A(FM_MDRU))<<16) + inpw(FM_A(FM_MDRL))) + +/* read FORMAC+ 32-bit status register */ +#define GET_ST1() (((long)inpw(FM_A(FM_ST1U))<<16) + inpw(FM_A(FM_ST1L))) +#define GET_ST2() (((long)inpw(FM_A(FM_ST2U))<<16) + inpw(FM_A(FM_ST2L))) +#ifdef SUPERNET_3 +#define GET_ST3() (((long)inpw(FM_A(FM_ST3U))<<16) + inpw(FM_A(FM_ST3L))) +#endif +#else +/* read double word */ +#define MDRR() inp2w((FM_A(FM_MDRU)),(FM_A(FM_MDRL))) + +/* read FORMAC+ 32-bit status register */ +#define GET_ST1() inp2w((FM_A(FM_ST1U)),(FM_A(FM_ST1L))) +#define GET_ST2() inp2w((FM_A(FM_ST2U)),(FM_A(FM_ST2L))) +#ifdef SUPERNET_3 +#define GET_ST3() inp2w((FM_A(FM_ST3U)),(FM_A(FM_ST3L))) +#endif +#endif + +/* Special timer macro for 82c54 */ + /* timer access over data bus bit 8..15 */ +#define OUT_82c54_TIMER(port,val) outpw(TI_A(port),(val)<<8) +#define IN_82c54_TIMER(port) ((inpw(TI_A(port))>>8) & 0xff) + + +#ifdef DEBUG +#define DB_MAC(mac,st) {if (debug_mac & 0x1)\ + printf("M") ;\ + if (debug_mac & 0x2)\ + printf("\tMAC %d status 0x%08lx\n",mac,st) ;\ + if (debug_mac & 0x4)\ + dp_mac(mac,st) ;\ +} + +#define DB_PLC(p,iev) { if (debug_plc & 0x1)\ + printf("P") ;\ + if (debug_plc & 0x2)\ + printf("\tPLC %s Int 0x%04x\n", \ + (p == PA) ? "A" : "B", iev) ;\ + if (debug_plc & 0x4)\ + dp_plc(p,iev) ;\ +} + +#define DB_TIMER() { if (debug_timer & 0x1)\ + printf("T") ;\ + if (debug_timer & 0x2)\ + printf("\tTimer ISR\n") ;\ +} + +#else /* no DEBUG */ + +#define DB_MAC(mac,st) +#define DB_PLC(p,iev) +#define DB_TIMER() + +#endif /* no DEBUG */ + +#define INC_PTR(sp,cp,ep) if (++cp == ep) cp = sp +/* + * timer defs + */ +#define COUNT(t) ((t)<<6) /* counter */ +#define RW_OP(o) ((o)<<4) /* read/write operation */ +#define TMODE(m) ((m)<<1) /* timer mode */ + +#endif diff --git a/drivers/net/skfp/h/skfbiinc.h b/drivers/net/skfp/h/skfbiinc.h new file mode 100644 index 000000000000..79d55ad2cd2a --- /dev/null +++ b/drivers/net/skfp/h/skfbiinc.h @@ -0,0 +1,123 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _SKFBIINC_ +#define _SKFBIINC_ + +#include "h/supern_2.h" + +/* + * special defines for use into .asm files + */ +#define ERR_FLAGS (FS_MSRABT | FS_SEAC2 | FS_SFRMERR | FS_SFRMTY1) + +#ifdef ISA +#define DMA_BUSY_CHECK CSRA +#define IMASK_FAST (IS_PLINT1 | IS_PLINT2 | IS_TIMINT) +#define HRQR (RQAA+(RQ_RRQ<<1)) +#define HRQW (RQAA+(RQ_WA2<<1)) +#define HRQA0 (RQAA+(RQ_WA0<<1)) +#define HRQSQ (RQAA+(RQ_WSQ<<1)) +#endif + +#ifdef EISA +#define DMA_BUSY_CHECK CSRA +#define DMA_HIGH_WORD 0x0400 +#define DMA_MASK_M 0x0a +#define DMA_MODE_M 0x0b +#define DMA_BYTE_PTR_M 0x0c +#define DMA_MASK_S 0x0d4 +#define DMA_MODE_S 0x0d6 +#define DMA_BYTE_PTR_S 0x0d8 +#define IMASK_FAST (IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TC) +#endif /* EISA */ + +#ifdef MCA +#define IMASK_FAST (IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \ + IS_CHCK_L | IS_BUSERR) +#endif + +#ifdef PCI +#define IMASK_FAST (IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \ + IS_MINTR2 | IS_MINTR3 | IS_R1_P | \ + IS_R1_C | IS_XA_C | IS_XS_C) +#endif + +#ifdef PCI +#define ISR_MASK (IS_MINTR1 | IS_R1_F | IS_XS_F| IS_XA_F | IMASK_FAST) +#else +#define ISR_MASK (IS_MINTR1 | IS_MINTR2 | IMASK_FAST) +#endif + +#define FMA_FM_CMDREG1 FMA(FM_CMDREG1) +#define FMA_FM_CMDREG2 FMA(FM_CMDREG2) +#define FMA_FM_STMCHN FMA(FM_STMCHN) +#define FMA_FM_RPR FMA(FM_RPR) +#define FMA_FM_WPXA0 FMA(FM_WPXA0) +#define FMA_FM_WPXA2 FMA(FM_WPXA2) +#define FMA_FM_MARR FMA(FM_MARR) +#define FMA_FM_MARW FMA(FM_MARW) +#define FMA_FM_MDRU FMA(FM_MDRU) +#define FMA_FM_MDRL FMA(FM_MDRL) +#define FMA_ST1L FMA(FM_ST1L) +#define FMA_ST1U FMA(FM_ST1U) +#define FMA_ST2L FMA(FM_ST2L) +#define FMA_ST2U FMA(FM_ST2U) +#ifdef SUPERNET_3 +#define FMA_ST3L FMA(FM_ST3L) +#define FMA_ST3U FMA(FM_ST3U) +#endif + +#define TMODE_RRQ RQ_RRQ +#define TMODE_WAQ2 RQ_WA2 +#define HSRA HSR(0) + + +#define FMA_FM_ST1L FMA_ST1L +#define FMA_FM_ST1U FMA_ST1U +#define FMA_FM_ST2L FMA_ST2L +#define FMA_FM_ST2U FMA_ST2U +#ifdef SUPERNET_3 +#define FMA_FM_ST3L FMA_ST3L +#define FMA_FM_ST3U FMA_ST3U +#endif + +#define FMA_FM_SWPR FMA(FM_SWPR) + +#define FMA_FM_RPXA0 FMA(FM_RPXA0) + +#define FMA_FM_RPXS FMA(FM_RPXS) +#define FMA_FM_WPXS FMA(FM_WPXS) + +#define FMA_FM_IMSK1U FMA(FM_IMSK1U) +#define FMA_FM_IMSK1L FMA(FM_IMSK1L) + +#define FMA_FM_EAS FMA(FM_EAS) +#define FMA_FM_EAA0 FMA(FM_EAA0) + +#define TMODE_WAQ0 RQ_WA0 +#define TMODE_WSQ RQ_WSQ + +/* Define default for DRV_PCM_STATE_CHANGE */ +#ifndef DRV_PCM_STATE_CHANGE +#define DRV_PCM_STATE_CHANGE(smc,plc,p_state) /* nothing */ +#endif + +/* Define default for DRV_RMT_INDICATION */ +#ifndef DRV_RMT_INDICATION +#define DRV_RMT_INDICATION(smc,i) /* nothing */ +#endif + +#endif /* n_SKFBIINC_ */ + diff --git a/drivers/net/skfp/h/smc.h b/drivers/net/skfp/h/smc.h new file mode 100644 index 000000000000..b909586bca21 --- /dev/null +++ b/drivers/net/skfp/h/smc.h @@ -0,0 +1,471 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _SCMECM_ +#define _SCMECM_ + +#if defined(PCI) && !defined(OSDEF) +/* + * In the case of the PCI bus the file osdef1st.h must be present + */ +#define OSDEF +#endif + +#ifdef PCI +#ifndef SUPERNET_3 +#define SUPERNET_3 +#endif +#ifndef TAG_MODE +#define TAG_MODE +#endif +#endif + +/* + * include all other files in required order + * the following files must have been included before: + * types.h + * fddi.h + */ +#ifdef OSDEF +#include "h/osdef1st.h" +#endif /* OSDEF */ +#ifdef OEM_CONCEPT +#include "oemdef.h" +#endif /* OEM_CONCEPT */ +#include "h/smt.h" +#include "h/cmtdef.h" +#include "h/fddimib.h" +#include "h/targethw.h" /* all target hw dependencies */ +#include "h/targetos.h" /* all target os dependencies */ +#ifdef ESS +#include "h/sba.h" +#endif + +/* + * Event Queue + * queue.c + * events are class/value pairs + * class is addressee, e.g. RMT, PCM etc. + * value is command, e.g. line state change, ring op change etc. + */ +struct event_queue { + u_short class ; /* event class */ + u_short event ; /* event value */ +} ; + +/* + * define event queue as circular buffer + */ +#ifdef CONCENTRATOR +#define MAX_EVENT 128 +#else /* nCONCENTRATOR */ +#define MAX_EVENT 64 +#endif /* nCONCENTRATOR */ + +struct s_queue { + + struct event_queue ev_queue[MAX_EVENT]; + struct event_queue *ev_put ; + struct event_queue *ev_get ; +} ; + +/* + * ECM - Entity Coordination Management + * ecm.c + */ +struct s_ecm { + u_char path_test ; /* ECM path test variable */ + u_char sb_flag ; /* ECM stuck bypass */ + u_char DisconnectFlag ; /* jd 05-Aug-1999 Bug #10419 + * ECM disconnected */ + u_char ecm_line_state ; /* flag to dispatcher : line states */ + u_long trace_prop ; /* ECM Trace_Prop flag >= 16 bits !! */ + /* NUMPHYS note: + * this variable must have enough bits to hold all entiies in + * the station. So NUMPHYS may not be greater than 31. + */ + char ec_pad[2] ; + struct smt_timer ecm_timer ; /* timer */ +} ; + + +/* + * RMT - Ring Management + * rmt.c + */ +struct s_rmt { + u_char dup_addr_test ; /* state of dupl. addr. test */ + u_char da_flag ; /* flag : duplicate address det. */ + u_char loop_avail ; /* flag : MAC available for loopback */ + u_char sm_ma_avail ; /* flag : MAC available for SMT */ + u_char no_flag ; /* flag : ring not operational */ + u_char bn_flag ; /* flag : MAC reached beacon state */ + u_char jm_flag ; /* flag : jamming in NON_OP_DUP */ + u_char rm_join ; /* CFM flag RM_Join */ + u_char rm_loop ; /* CFM flag RM_Loop */ + + long fast_rm_join ; /* bit mask of active ports */ + /* + * timer and flags + */ + struct smt_timer rmt_timer0 ; /* timer 0 */ + struct smt_timer rmt_timer1 ; /* timer 1 */ + struct smt_timer rmt_timer2 ; /* timer 2 */ + u_char timer0_exp ; /* flag : timer 0 expired */ + u_char timer1_exp ; /* flag : timer 1 expired */ + u_char timer2_exp ; /* flag : timer 2 expired */ + + u_char rm_pad1[1] ; +} ; + +/* + * CFM - Configuration Management + * cfm.c + * used for SAS and DAS + */ +struct s_cfm { + u_char cf_state; /* CFM state machine current state */ + u_char cf_pad[3] ; +} ; + +/* + * CEM - Configuration Element Management + * cem.c + * used for Concentrator + */ +#ifdef CONCENTRATOR +struct s_cem { + int ce_state ; /* CEM state */ + int ce_port ; /* PA PB PM PM+1 .. */ + int ce_type ; /* TA TB TS TM */ +} ; + +/* + * linked list of CCEs in current token path + */ +struct s_c_ring { + struct s_c_ring *c_next ; + char c_entity ; +} ; + +struct mib_path_config { + u_long fddimibPATHConfigSMTIndex; + u_long fddimibPATHConfigPATHIndex; + u_long fddimibPATHConfigTokenOrder; + u_long fddimibPATHConfigResourceType; +#define SNMP_RES_TYPE_MAC 2 /* Resource is a MAC */ +#define SNMP_RES_TYPE_PORT 4 /* Resource is a PORT */ + u_long fddimibPATHConfigResourceIndex; + u_long fddimibPATHConfigCurrentPath; +#define SNMP_PATH_ISOLATED 1 /* Current path is isolated */ +#define SNMP_PATH_LOCAL 2 /* Current path is local */ +#define SNMP_PATH_SECONDARY 3 /* Current path is secondary */ +#define SNMP_PATH_PRIMARY 4 /* Current path is primary */ +#define SNMP_PATH_CONCATENATED 5 /* Current path is concatenated */ +#define SNMP_PATH_THRU 6 /* Current path is thru */ +}; + + +#endif + +/* + * PCM connect states + */ +#define PCM_DISABLED 0 +#define PCM_CONNECTING 1 +#define PCM_STANDBY 2 +#define PCM_ACTIVE 3 + +struct s_pcm { + u_char pcm_pad[3] ; +} ; + +/* + * PHY struct + * one per physical port + */ +struct s_phy { + /* Inter Module Globals */ + struct fddi_mib_p *mib ; + + u_char np ; /* index 0 .. NUMPHYS */ + u_char cf_join ; + u_char cf_loop ; + u_char wc_flag ; /* withhold connection flag */ + u_char pc_mode ; /* Holds the negotiated mode of the PCM */ + u_char pc_lem_fail ; /* flag : LCT failed */ + u_char lc_test ; + u_char scrub ; /* CFM flag Scrub -> PCM */ + char phy_name ; + u_char pmd_type[2] ; /* SK connector/transceiver type codes */ +#define PMD_SK_CONN 0 /* pmd_type[PMD_SK_CONN] = Connector */ +#define PMD_SK_PMD 1 /* pmd_type[PMD_SK_PMD] = Xver */ + u_char pmd_scramble ; /* scrambler on/off */ + + /* inner Module Globals */ + u_char curr_ls ; /* current line state */ + u_char ls_flag ; + u_char rc_flag ; + u_char tc_flag ; + u_char td_flag ; + u_char bitn ; + u_char tr_flag ; /* trace recvd while in active */ + u_char twisted ; /* flag to indicate an A-A or B-B connection */ + u_char t_val[NUMBITS] ; /* transmit bits for signaling */ + u_char r_val[NUMBITS] ; /* receive bits for signaling */ + u_long t_next[NUMBITS] ; + struct smt_timer pcm_timer0 ; + struct smt_timer pcm_timer1 ; + struct smt_timer pcm_timer2 ; + u_char timer0_exp ; + u_char timer1_exp ; + u_char timer2_exp ; + u_char pcm_pad1[1] ; + int cem_pst ; /* CEM privae state; used for dual homing */ + struct lem_counter lem ; +#ifdef AMDPLC + struct s_plc plc ; +#endif +} ; + +/* + * timer package + * smttimer.c + */ +struct s_timer { + struct smt_timer *st_queue ; + struct smt_timer st_fast ; +} ; + +/* + * SRF types and data + */ +#define SMT_EVENT_BASE 1 +#define SMT_EVENT_MAC_PATH_CHANGE (SMT_EVENT_BASE+0) +#define SMT_EVENT_MAC_NEIGHBOR_CHANGE (SMT_EVENT_BASE+1) +#define SMT_EVENT_PORT_PATH_CHANGE (SMT_EVENT_BASE+2) +#define SMT_EVENT_PORT_CONNECTION (SMT_EVENT_BASE+3) + +#define SMT_IS_CONDITION(x) ((x)>=SMT_COND_BASE) + +#define SMT_COND_BASE (SMT_EVENT_PORT_CONNECTION+1) +#define SMT_COND_SMT_PEER_WRAP (SMT_COND_BASE+0) +#define SMT_COND_SMT_HOLD (SMT_COND_BASE+1) +#define SMT_COND_MAC_FRAME_ERROR (SMT_COND_BASE+2) +#define SMT_COND_MAC_DUP_ADDR (SMT_COND_BASE+3) +#define SMT_COND_MAC_NOT_COPIED (SMT_COND_BASE+4) +#define SMT_COND_PORT_EB_ERROR (SMT_COND_BASE+5) +#define SMT_COND_PORT_LER (SMT_COND_BASE+6) + +#define SR0_WAIT 0 +#define SR1_HOLDOFF 1 +#define SR2_DISABLED 2 + +struct s_srf { + u_long SRThreshold ; /* threshold value */ + u_char RT_Flag ; /* report transmitted flag */ + u_char sr_state ; /* state-machine */ + u_char any_report ; /* any report required */ + u_long TSR ; /* timer */ + u_short ring_status ; /* IBM ring status */ +} ; + +/* + * IBM token ring status + */ +#define RS_RES15 (1<<15) /* reserved */ +#define RS_HARDERROR (1<<14) /* ring down */ +#define RS_SOFTERROR (1<<13) /* sent SRF */ +#define RS_BEACON (1<<12) /* transmitted beacon */ +#define RS_PATHTEST (1<<11) /* path test failed */ +#define RS_SELFTEST (1<<10) /* selftest required */ +#define RS_RES9 (1<< 9) /* reserved */ +#define RS_DISCONNECT (1<< 8) /* remote disconnect */ +#define RS_RES7 (1<< 7) /* reserved */ +#define RS_DUPADDR (1<< 6) /* duplicate address */ +#define RS_NORINGOP (1<< 5) /* no ring op */ +#define RS_VERSION (1<< 4) /* SMT version mismatch */ +#define RS_STUCKBYPASSS (1<< 3) /* stuck bypass */ +#define RS_EVENT (1<< 2) /* FDDI event occured */ +#define RS_RINGOPCHANGE (1<< 1) /* ring op changed */ +#define RS_RES0 (1<< 0) /* reserved */ + +#define RS_SET(smc,bit) \ + ring_status_indication(smc,smc->srf.ring_status |= bit) +#define RS_CLEAR(smc,bit) \ + ring_status_indication(smc,smc->srf.ring_status &= ~bit) + +#define RS_CLEAR_EVENT (0xffff & ~(RS_NORINGOP)) + +/* Define the AIX-event-Notification as null function if it isn't defined */ +/* in the targetos.h file */ +#ifndef AIX_EVENT +#define AIX_EVENT(smc,opt0,opt1,opt2,opt3) /* nothing */ +#endif + +struct s_srf_evc { + u_char evc_code ; /* event code type */ + u_char evc_index ; /* index for mult. instances */ + u_char evc_rep_required ; /* report required */ + u_short evc_para ; /* SMT Para Number */ + u_char *evc_cond_state ; /* condition state */ + u_char *evc_multiple ; /* multiple occurence */ +} ; + +/* + * Values used by frame based services + * smt.c + */ +#define SMT_MAX_TEST 5 +#define SMT_TID_NIF 0 /* pending NIF request */ +#define SMT_TID_NIF_TEST 1 /* pending NIF test */ +#define SMT_TID_ECF_UNA 2 /* pending ECF UNA test */ +#define SMT_TID_ECF_DNA 3 /* pending ECF DNA test */ +#define SMT_TID_ECF 4 /* pending ECF test */ + +struct smt_values { + u_long smt_tvu ; /* timer valid una */ + u_long smt_tvd ; /* timer valid dna */ + u_long smt_tid ; /* transaction id */ + u_long pend[SMT_MAX_TEST] ; /* TID of requests */ + u_long uniq_time ; /* unique time stamp */ + u_short uniq_ticks ; /* unique time stamp */ + u_short please_reconnect ; /* flag : reconnect */ + u_long smt_last_lem ; + u_long smt_last_notify ; + struct smt_timer smt_timer ; /* SMT NIF timer */ + u_long last_tok_time[NUMMACS]; /* token cnt emulation */ +} ; + +/* + * SMT/CMT configurable parameters + */ +#define SMT_DAS 0 /* dual attach */ +#define SMT_SAS 1 /* single attach */ +#define SMT_NAC 2 /* null attach concentrator */ + +struct smt_config { + u_char attach_s ; /* CFM attach to secondary path */ + u_char sas ; /* SMT_DAS/SAS/NAC */ + u_char build_ring_map ; /* build ringmap if TRUE */ + u_char numphys ; /* number of active phys */ + u_char sc_pad[1] ; + + u_long pcm_tb_min ; /* PCM : TB_Min timer value */ + u_long pcm_tb_max ; /* PCM : TB_Max timer value */ + u_long pcm_c_min ; /* PCM : C_Min timer value */ + u_long pcm_t_out ; /* PCM : T_Out timer value */ + u_long pcm_tl_min ; /* PCM : TL_min timer value */ + u_long pcm_lc_short ; /* PCM : LC_Short timer value */ + u_long pcm_lc_medium ; /* PCM : LC_Medium timer value */ + u_long pcm_lc_long ; /* PCM : LC_Long timer value */ + u_long pcm_lc_extended ; /* PCM : LC_Extended timer value */ + u_long pcm_t_next_9 ; /* PCM : T_Next[9] timer value */ + u_long pcm_ns_max ; /* PCM : NS_Max timer value */ + + u_long ecm_i_max ; /* ECM : I_Max timer value */ + u_long ecm_in_max ; /* ECM : IN_Max timer value */ + u_long ecm_td_min ; /* ECM : TD_Min timer */ + u_long ecm_test_done ; /* ECM : path test done timer */ + u_long ecm_check_poll ; /* ECM : check bypass poller */ + + u_long rmt_t_non_op ; /* RMT : T_Non_OP timer value */ + u_long rmt_t_stuck ; /* RMT : T_Stuck timer value */ + u_long rmt_t_direct ; /* RMT : T_Direct timer value */ + u_long rmt_t_jam ; /* RMT : T_Jam timer value */ + u_long rmt_t_announce ; /* RMT : T_Announce timer value */ + u_long rmt_t_poll ; /* RMT : claim/beacon poller */ + u_long rmt_dup_mac_behavior ; /* Flag for the beavior of SMT if + * a Duplicate MAC Address was detected. + * FALSE: SMT will leave finaly the ring + * TRUE: SMT will reinstert into the ring + */ + u_long mac_d_max ; /* MAC : D_Max timer value */ + + u_long lct_short ; /* LCT : error threshhold */ + u_long lct_medium ; /* LCT : error threshhold */ + u_long lct_long ; /* LCT : error threshhold */ + u_long lct_extended ; /* LCT : error threshhold */ +} ; + +#ifdef DEBUG +/* + * Debugging struct sometimes used in smc + */ +struct smt_debug { + int d_smtf ; + int d_smt ; + int d_ecm ; + int d_rmt ; + int d_cfm ; + int d_pcm ; + int d_plc ; +#ifdef ESS + int d_ess ; +#endif +#ifdef SBA + int d_sba ; +#endif + struct os_debug d_os; /* Include specific OS DEBUG struct */ +} ; + +#ifndef DEBUG_BRD +/* all boards shall be debugged with one debug struct */ +extern struct smt_debug debug; /* Declaration of debug struct */ +#endif /* DEBUG_BRD */ + +#endif /* DEBUG */ + +/* + * the SMT Context Struct SMC + * this struct contains ALL global variables of SMT + */ +struct s_smc { + struct s_smt_os os ; /* os specific */ + struct s_smt_hw hw ; /* hardware */ + +/* + * NOTE: os and hw MUST BE the first two structs + * anything beyond hw WILL BE SET TO ZERO in smt_set_defaults() + */ + struct smt_config s ; /* smt constants */ + struct smt_values sm ; /* smt variables */ + struct s_ecm e ; /* ecm */ + struct s_rmt r ; /* rmt */ + struct s_cfm cf ; /* cfm/cem */ +#ifdef CONCENTRATOR + struct s_cem ce[NUMPHYS] ; /* cem */ + struct s_c_ring cr[NUMPHYS+NUMMACS] ; +#endif + struct s_pcm p ; /* pcm */ + struct s_phy y[NUMPHYS] ; /* phy */ + struct s_queue q ; /* queue */ + struct s_timer t ; /* timer */ + struct s_srf srf ; /* SRF */ + struct s_srf_evc evcs[6+NUMPHYS*4] ; + struct fddi_mib mib ; /* __THE_MIB__ */ +#ifdef SBA + struct s_sba sba ; /* SBA variables */ +#endif +#ifdef ESS + struct s_ess ess ; /* Ess variables */ +#endif +#if defined(DEBUG) && defined(DEBUG_BRD) + /* If you want all single board to be debugged separately */ + struct smt_debug debug; /* Declaration of debug struct */ +#endif /* DEBUG_BRD && DEBUG */ +} ; + +#endif /* _SCMECM_ */ + diff --git a/drivers/net/skfp/h/smt.h b/drivers/net/skfp/h/smt.h new file mode 100644 index 000000000000..5a2606d16e1c --- /dev/null +++ b/drivers/net/skfp/h/smt.h @@ -0,0 +1,882 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * SMT 7.2 frame definitions + */ + +#ifndef _SMT_ +#define _SMT_ + +/* #define SMT5_10 */ +#define SMT6_10 +#define SMT7_20 + +#define OPT_PMF /* if parameter management is supported */ +#define OPT_SRF /* if status report is supported */ + +/* + * SMT frame version 5.1 + */ + +#define SMT_VID 0x0001 /* V 5.1 .. 6.1 */ +#define SMT_VID_2 0x0002 /* V 7.2 */ + +struct smt_sid { + u_char sid_oem[2] ; /* implementation spec. */ + struct fddi_addr sid_node ; /* node address */ +} ; + +typedef u_char t_station_id[8] ; + +/* + * note on alignment : + * sizeof(struct smt_header) = 32 + * all parameters are long aligned + * if struct smt_header starts at offset 0, all longs are aligned correctly + * (FC starts at offset 3) + */ +_packed struct smt_header { + struct fddi_addr smt_dest ; /* destination address */ + struct fddi_addr smt_source ; /* source address */ + u_char smt_class ; /* NIF, SIF ... */ + u_char smt_type ; /* req., response .. */ + u_short smt_version ; /* version id */ + u_int smt_tid ; /* transaction ID */ + struct smt_sid smt_sid ; /* station ID */ + u_short smt_pad ; /* pad with 0 */ + u_short smt_len ; /* length of info field */ +} ; +#define SWAP_SMTHEADER "662sl8ss" + +#if 0 +/* + * MAC FC values + */ +#define FC_SMT_INFO 0x41 /* SMT info */ +#define FC_SMT_NSA 0x4f /* SMT Next Station Addressing */ +#endif + + +/* + * type codes + */ +#define SMT_ANNOUNCE 0x01 /* announcement */ +#define SMT_REQUEST 0x02 /* request */ +#define SMT_REPLY 0x03 /* reply */ + +/* + * class codes + */ +#define SMT_NIF 0x01 /* neighbor information frames */ +#define SMT_SIF_CONFIG 0x02 /* station information configuration */ +#define SMT_SIF_OPER 0x03 /* station information operation */ +#define SMT_ECF 0x04 /* echo frames */ +#define SMT_RAF 0x05 /* resource allocation */ +#define SMT_RDF 0x06 /* request denied */ +#define SMT_SRF 0x07 /* status report */ +#define SMT_PMF_GET 0x08 /* parameter management get */ +#define SMT_PMF_SET 0x09 /* parameter management set */ +#define SMT_ESF 0xff /* extended service */ + +#define SMT_MAX_ECHO_LEN 4458 /* max length of SMT Echo */ +#if defined(CONC) || defined(CONC_II) +#define SMT_TEST_ECHO_LEN 50 /* test length of SMT Echo */ +#else +#define SMT_TEST_ECHO_LEN SMT_MAX_ECHO_LEN /* test length */ +#endif + +#define SMT_MAX_INFO_LEN (4352-20) /* max length for SMT info */ + + +/* + * parameter types + */ + +struct smt_para { + u_short p_type ; /* type */ + u_short p_len ; /* length of parameter */ +} ; + +#define PARA_LEN (sizeof(struct smt_para)) + +#define SMTSETPARA(p,t) (p)->para.p_type = (t),\ + (p)->para.p_len = sizeof(*(p)) - PARA_LEN + +/* + * P01 : Upstream Neighbor Address, UNA + */ +#define SMT_P_UNA 0x0001 /* upstream neighbor address */ +#define SWAP_SMT_P_UNA "s6" + +struct smt_p_una { + struct smt_para para ; /* generic parameter header */ + u_short una_pad ; + struct fddi_addr una_node ; /* node address, zero if unknown */ +} ; + +/* + * P02 : Station Descriptor + */ +#define SMT_P_SDE 0x0002 /* station descriptor */ +#define SWAP_SMT_P_SDE "1111" + +#define SMT_SDE_STATION 0 /* end node */ +#define SMT_SDE_CONCENTRATOR 1 /* concentrator */ + +struct smt_p_sde { + struct smt_para para ; /* generic parameter header */ + u_char sde_type ; /* station type */ + u_char sde_mac_count ; /* number of MACs */ + u_char sde_non_master ; /* number of A,B or S ports */ + u_char sde_master ; /* number of S ports on conc. */ +} ; + +/* + * P03 : Station State + */ +#define SMT_P_STATE 0x0003 /* station state */ +#define SWAP_SMT_P_STATE "scc" + +struct smt_p_state { + struct smt_para para ; /* generic parameter header */ + u_short st_pad ; + u_char st_topology ; /* topology */ + u_char st_dupl_addr ; /* duplicate address detected */ +} ; +#define SMT_ST_WRAPPED (1<<0) /* station wrapped */ +#define SMT_ST_UNATTACHED (1<<1) /* unattached concentrator */ +#define SMT_ST_TWISTED_A (1<<2) /* A-A connection, twisted ring */ +#define SMT_ST_TWISTED_B (1<<3) /* B-B connection, twisted ring */ +#define SMT_ST_ROOTED_S (1<<4) /* rooted station */ +#define SMT_ST_SRF (1<<5) /* SRF protocol supported */ +#define SMT_ST_SYNC_SERVICE (1<<6) /* use synchronous bandwidth */ + +#define SMT_ST_MY_DUPA (1<<0) /* my station detected dupl. */ +#define SMT_ST_UNA_DUPA (1<<1) /* my UNA detected duplicate */ + +/* + * P04 : timestamp + */ +#define SMT_P_TIMESTAMP 0x0004 /* time stamp */ +#define SWAP_SMT_P_TIMESTAMP "8" +struct smt_p_timestamp { + struct smt_para para ; /* generic parameter header */ + u_char ts_time[8] ; /* time, resolution 80nS, unique */ +} ; + +/* + * P05 : station policies + */ +#define SMT_P_POLICY 0x0005 /* station policies */ +#define SWAP_SMT_P_POLICY "ss" + +struct smt_p_policy { + struct smt_para para ; /* generic parameter header */ + u_short pl_config ; + u_short pl_connect ; /* bit string POLICY_AA ... */ +} ; +#define SMT_PL_HOLD 1 /* hold policy supported (Dual MAC) */ + +/* + * P06 : latency equivalent + */ +#define SMT_P_LATENCY 0x0006 /* latency */ +#define SWAP_SMT_P_LATENCY "ssss" + +/* + * note: latency has two phy entries by definition + * for a SAS, the 2nd one is null + */ +struct smt_p_latency { + struct smt_para para ; /* generic parameter header */ + u_short lt_phyout_idx1 ; /* index */ + u_short lt_latency1 ; /* latency , unit : byte clock */ + u_short lt_phyout_idx2 ; /* 0 if SAS */ + u_short lt_latency2 ; /* 0 if SAS */ +} ; + +/* + * P07 : MAC neighbors + */ +#define SMT_P_NEIGHBORS 0x0007 /* MAC neighbor description */ +#define SWAP_SMT_P_NEIGHBORS "ss66" + +struct smt_p_neighbor { + struct smt_para para ; /* generic parameter header */ + u_short nb_mib_index ; /* MIB index */ + u_short nb_mac_index ; /* n+1 .. n+m, m = #MACs, n = #PHYs */ + struct fddi_addr nb_una ; /* UNA , 0 for unknown */ + struct fddi_addr nb_dna ; /* DNA , 0 for unknown */ +} ; + +/* + * PHY record + */ +#define SMT_PHY_A 0 /* A port */ +#define SMT_PHY_B 1 /* B port */ +#define SMT_PHY_S 2 /* slave port */ +#define SMT_PHY_M 3 /* master port */ + +#define SMT_CS_DISABLED 0 /* connect state : disabled */ +#define SMT_CS_CONNECTING 1 /* connect state : connecting */ +#define SMT_CS_STANDBY 2 /* connect state : stand by */ +#define SMT_CS_ACTIVE 3 /* connect state : active */ + +#define SMT_RM_NONE 0 +#define SMT_RM_MAC 1 + +struct smt_phy_rec { + u_short phy_mib_index ; /* MIB index */ + u_char phy_type ; /* A/B/S/M */ + u_char phy_connect_state ; /* disabled/connecting/active */ + u_char phy_remote_type ; /* A/B/S/M */ + u_char phy_remote_mac ; /* none/remote */ + u_short phy_resource_idx ; /* 1 .. n */ +} ; + +/* + * MAC record + */ +struct smt_mac_rec { + struct fddi_addr mac_addr ; /* MAC address */ + u_short mac_resource_idx ; /* n+1 .. n+m */ +} ; + +/* + * P08 : path descriptors + * should be really an array ; however our environment has a fixed number of + * PHYs and MACs + */ +#define SMT_P_PATH 0x0008 /* path descriptor */ +#define SWAP_SMT_P_PATH "[6s]" + +struct smt_p_path { + struct smt_para para ; /* generic parameter header */ + struct smt_phy_rec pd_phy[2] ; /* PHY A */ + struct smt_mac_rec pd_mac ; /* MAC record */ +} ; + +/* + * P09 : MAC status + */ +#define SMT_P_MAC_STATUS 0x0009 /* MAC status */ +#define SWAP_SMT_P_MAC_STATUS "sslllllllll" + +struct smt_p_mac_status { + struct smt_para para ; /* generic parameter header */ + u_short st_mib_index ; /* MIB index */ + u_short st_mac_index ; /* n+1 .. n+m */ + u_int st_t_req ; /* T_Req */ + u_int st_t_neg ; /* T_Neg */ + u_int st_t_max ; /* T_Max */ + u_int st_tvx_value ; /* TVX_Value */ + u_int st_t_min ; /* T_Min */ + u_int st_sba ; /* synchr. bandwidth alloc */ + u_int st_frame_ct ; /* frame counter */ + u_int st_error_ct ; /* error counter */ + u_int st_lost_ct ; /* lost frames counter */ +} ; + +/* + * P0A : PHY link error rate monitoring + */ +#define SMT_P_LEM 0x000a /* link error monitor */ +#define SWAP_SMT_P_LEM "ssccccll" +/* + * units of lem_cutoff,lem_alarm,lem_estimate : 10**-x + */ +struct smt_p_lem { + struct smt_para para ; /* generic parameter header */ + u_short lem_mib_index ; /* MIB index */ + u_short lem_phy_index ; /* 1 .. n */ + u_char lem_pad2 ; /* be nice and make it even . */ + u_char lem_cutoff ; /* 0x4 .. 0xf, default 0x7 */ + u_char lem_alarm ; /* 0x4 .. 0xf, default 0x8 */ + u_char lem_estimate ; /* 0x0 .. 0xff */ + u_int lem_reject_ct ; /* 0x00000000 .. 0xffffffff */ + u_int lem_ct ; /* 0x00000000 .. 0xffffffff */ +} ; + +/* + * P0B : MAC frame counters + */ +#define SMT_P_MAC_COUNTER 0x000b /* MAC frame counters */ +#define SWAP_SMT_P_MAC_COUNTER "ssll" + +struct smt_p_mac_counter { + struct smt_para para ; /* generic parameter header */ + u_short mc_mib_index ; /* MIB index */ + u_short mc_index ; /* mac index */ + u_int mc_receive_ct ; /* receive counter */ + u_int mc_transmit_ct ; /* transmit counter */ +} ; + +/* + * P0C : MAC frame not copied counter + */ +#define SMT_P_MAC_FNC 0x000c /* MAC frame not copied counter */ +#define SWAP_SMT_P_MAC_FNC "ssl" + +struct smt_p_mac_fnc { + struct smt_para para ; /* generic parameter header */ + u_short nc_mib_index ; /* MIB index */ + u_short nc_index ; /* mac index */ + u_int nc_counter ; /* not copied counter */ +} ; + + +/* + * P0D : MAC priority values + */ +#define SMT_P_PRIORITY 0x000d /* MAC priority values */ +#define SWAP_SMT_P_PRIORITY "ssl" + +struct smt_p_priority { + struct smt_para para ; /* generic parameter header */ + u_short pr_mib_index ; /* MIB index */ + u_short pr_index ; /* mac index */ + u_int pr_priority[7] ; /* priority values */ +} ; + +/* + * P0E : PHY elasticity buffer status + */ +#define SMT_P_EB 0x000e /* PHY EB status */ +#define SWAP_SMT_P_EB "ssl" + +struct smt_p_eb { + struct smt_para para ; /* generic parameter header */ + u_short eb_mib_index ; /* MIB index */ + u_short eb_index ; /* phy index */ + u_int eb_error_ct ; /* # of eb overflows */ +} ; + +/* + * P0F : manufacturer field + */ +#define SMT_P_MANUFACTURER 0x000f /* manufacturer field */ +#define SWAP_SMT_P_MANUFACTURER "" + +struct smp_p_manufacturer { + struct smt_para para ; /* generic parameter header */ + u_char mf_data[32] ; /* OUI + arbitrary data */ +} ; + +/* + * P10 : user field + */ +#define SMT_P_USER 0x0010 /* manufacturer field */ +#define SWAP_SMT_P_USER "" + +struct smp_p_user { + struct smt_para para ; /* generic parameter header */ + u_char us_data[32] ; /* arbitrary data */ +} ; + + + +/* + * P11 : echo data + */ +#define SMT_P_ECHODATA 0x0011 /* echo data */ +#define SWAP_SMT_P_ECHODATA "" + +struct smt_p_echo { + struct smt_para para ; /* generic parameter header */ + u_char ec_data[SMT_MAX_ECHO_LEN-4] ; /* echo data */ +} ; + +/* + * P12 : reason code + */ +#define SMT_P_REASON 0x0012 /* reason code */ +#define SWAP_SMT_P_REASON "l" + +struct smt_p_reason { + struct smt_para para ; /* generic parameter header */ + u_int rdf_reason ; /* CLASS/VERSION */ +} ; +#define SMT_RDF_CLASS 0x00000001 /* class not supported */ +#define SMT_RDF_VERSION 0x00000002 /* version not supported */ +#define SMT_RDF_SUCCESS 0x00000003 /* success (PMF) */ +#define SMT_RDF_BADSET 0x00000004 /* bad set count (PMF) */ +#define SMT_RDF_ILLEGAL 0x00000005 /* read only (PMF) */ +#define SMT_RDF_NOPARAM 0x6 /* paramter not supported (PMF) */ +#define SMT_RDF_RANGE 0x8 /* out of range */ +#define SMT_RDF_AUTHOR 0x9 /* not autohorized */ +#define SMT_RDF_LENGTH 0x0a /* length error */ +#define SMT_RDF_TOOLONG 0x0b /* length error */ +#define SMT_RDF_SBA 0x0d /* SBA denied */ + +/* + * P13 : refused frame beginning + */ +#define SMT_P_REFUSED 0x0013 /* refused frame beginning */ +#define SWAP_SMT_P_REFUSED "l" + +struct smt_p_refused { + struct smt_para para ; /* generic parameter header */ + u_int ref_fc ; /* 3 bytes 0 + FC */ + struct smt_header ref_header ; /* refused header */ +} ; + +/* + * P14 : supported SMT versions + */ +#define SMT_P_VERSION 0x0014 /* SMT supported versions */ +#define SWAP_SMT_P_VERSION "sccss" + +struct smt_p_version { + struct smt_para para ; /* generic parameter header */ + u_short v_pad ; + u_char v_n ; /* 1 .. 0xff, #versions */ + u_char v_index ; /* 1 .. 0xff, index of op. v. */ + u_short v_version[1] ; /* list of min. 1 version */ + u_short v_pad2 ; /* pad if necessary */ +} ; + +/* + * P15 : Resource Type + */ +#define SWAP_SMT_P0015 "l" + +struct smt_p_0015 { + struct smt_para para ; /* generic parameter header */ + u_int res_type ; /* recsource type */ +} ; + +#define SYNC_BW 0x00000001L /* Synchronous Bandwidth */ + +/* + * P16 : SBA Command + */ +#define SWAP_SMT_P0016 "l" + +struct smt_p_0016 { + struct smt_para para ; /* generic parameter header */ + u_int sba_cmd ; /* command for the SBA */ +} ; + +#define REQUEST_ALLOCATION 0x1 /* req allocation of sync bandwidth */ +#define REPORT_ALLOCATION 0x2 /* rep of sync bandwidth allocation */ +#define CHANGE_ALLOCATION 0x3 /* forces a station using sync band-*/ + /* width to change its current allo-*/ + /* cation */ + +/* + * P17 : SBA Payload Request + */ +#define SWAP_SMT_P0017 "l" + +struct smt_p_0017 { + struct smt_para para ; /* generic parameter header */ + int sba_pl_req ; /* total sync bandwidth measured in */ +} ; /* bytes per 125 us */ + +/* + * P18 : SBA Overhead Request + */ +#define SWAP_SMT_P0018 "l" + +struct smt_p_0018 { + struct smt_para para ; /* generic parameter header */ + int sba_ov_req ; /* total sync bandwidth req for overhead*/ +} ; /* measuered in bytes per T_Neg */ + +/* + * P19 : SBA Allocation Address + */ +#define SWAP_SMT_P0019 "s6" + +struct smt_p_0019 { + struct smt_para para ; /* generic parameter header */ + u_short sba_pad ; + struct fddi_addr alloc_addr ; /* Allocation Address */ +} ; + +/* + * P1A : SBA Category + */ +#define SWAP_SMT_P001A "l" + +struct smt_p_001a { + struct smt_para para ; /* generic parameter header */ + u_int category ; /* Allocator defined classification */ +} ; + +/* + * P1B : Maximum T_Neg + */ +#define SWAP_SMT_P001B "l" + +struct smt_p_001b { + struct smt_para para ; /* generic parameter header */ + u_int max_t_neg ; /* longest T_NEG for the sync service*/ +} ; + +/* + * P1C : Minimum SBA Segment Size + */ +#define SWAP_SMT_P001C "l" + +struct smt_p_001c { + struct smt_para para ; /* generic parameter header */ + u_int min_seg_siz ; /* smallest number of bytes per frame*/ +} ; + +/* + * P1D : SBA Allocatable + */ +#define SWAP_SMT_P001D "l" + +struct smt_p_001d { + struct smt_para para ; /* generic parameter header */ + u_int allocatable ; /* total sync bw availabel for alloc */ +} ; + +/* + * P20 0B : frame status capabilities + * NOTE: not in swap table, is used by smt.c AND PMF table + */ +#define SMT_P_FSC 0x200b +/* #define SWAP_SMT_P_FSC "ssss" */ + +struct smt_p_fsc { + struct smt_para para ; /* generic parameter header */ + u_short fsc_pad0 ; + u_short fsc_mac_index ; /* mac index 1 .. ff */ + u_short fsc_pad1 ; + u_short fsc_value ; /* FSC_TYPE[0-2] */ +} ; + +#define FSC_TYPE0 0 /* "normal" node (A/C handling) */ +#define FSC_TYPE1 1 /* Special A/C indicator forwarding */ +#define FSC_TYPE2 2 /* Special A/C indicator forwarding */ + +/* + * P00 21 : user defined authoriziation (see pmf.c) + */ +#define SMT_P_AUTHOR 0x0021 + +/* + * notification parameters + */ +#define SWAP_SMT_P1048 "ll" +struct smt_p_1048 { + u_int p1048_flag ; + u_int p1048_cf_state ; +} ; + +/* + * NOTE: all 2xxx 3xxx and 4xxx must include the INDEX in the swap string, + * even so the INDEX is NOT part of the struct. + * INDEX is already swapped in pmf.c, format in string is '4' + */ +#define SWAP_SMT_P208C "4lss66" +struct smt_p_208c { + u_int p208c_flag ; + u_short p208c_pad ; + u_short p208c_dupcondition ; + struct fddi_addr p208c_fddilong ; + struct fddi_addr p208c_fddiunalong ; +} ; + +#define SWAP_SMT_P208D "4lllll" +struct smt_p_208d { + u_int p208d_flag ; + u_int p208d_frame_ct ; + u_int p208d_error_ct ; + u_int p208d_lost_ct ; + u_int p208d_ratio ; +} ; + +#define SWAP_SMT_P208E "4llll" +struct smt_p_208e { + u_int p208e_flag ; + u_int p208e_not_copied ; + u_int p208e_copied ; + u_int p208e_not_copied_ratio ; +} ; + +#define SWAP_SMT_P208F "4ll6666s6" + +struct smt_p_208f { + u_int p208f_multiple ; + u_int p208f_nacondition ; + struct fddi_addr p208f_old_una ; + struct fddi_addr p208f_new_una ; + struct fddi_addr p208f_old_dna ; + struct fddi_addr p208f_new_dna ; + u_short p208f_curren_path ; + struct fddi_addr p208f_smt_address ; +} ; + +#define SWAP_SMT_P2090 "4lssl" + +struct smt_p_2090 { + u_int p2090_multiple ; + u_short p2090_availablepaths ; + u_short p2090_currentpath ; + u_int p2090_requestedpaths ; +} ; + +/* + * NOTE: + * special kludge for parameters 320b,320f,3210 + * these parameters are part of RAF frames + * RAF frames are parsed in SBA.C and must be swapped + * PMF.C has special code to avoid double swapping + */ +#ifdef LITTLE_ENDIAN +#define SBAPATHINDEX (0x01000000L) +#else +#define SBAPATHINDEX (0x01L) +#endif + +#define SWAP_SMT_P320B "42s" + +struct smt_p_320b { + struct smt_para para ; /* generic parameter header */ + u_int mib_index ; + u_short path_pad ; + u_short path_index ; +} ; + +#define SWAP_SMT_P320F "4l" + +struct smt_p_320f { + struct smt_para para ; /* generic parameter header */ + u_int mib_index ; + u_int mib_payload ; +} ; + +#define SWAP_SMT_P3210 "4l" + +struct smt_p_3210 { + struct smt_para para ; /* generic parameter header */ + u_int mib_index ; + u_int mib_overhead ; +} ; + +#define SWAP_SMT_P4050 "4l1111ll" + +struct smt_p_4050 { + u_int p4050_flag ; + u_char p4050_pad ; + u_char p4050_cutoff ; + u_char p4050_alarm ; + u_char p4050_estimate ; + u_int p4050_reject_ct ; + u_int p4050_ct ; +} ; + +#define SWAP_SMT_P4051 "4lssss" +struct smt_p_4051 { + u_int p4051_multiple ; + u_short p4051_porttype ; + u_short p4051_connectstate ; + u_short p4051_pc_neighbor ; + u_short p4051_pc_withhold ; +} ; + +#define SWAP_SMT_P4052 "4ll" +struct smt_p_4052 { + u_int p4052_flag ; + u_int p4052_eberrorcount ; +} ; + +#define SWAP_SMT_P4053 "4lsslss" + +struct smt_p_4053 { + u_int p4053_multiple ; + u_short p4053_availablepaths ; + u_short p4053_currentpath ; + u_int p4053_requestedpaths ; + u_short p4053_mytype ; + u_short p4053_neighbortype ; +} ; + + +#define SMT_P_SETCOUNT 0x1035 +#define SWAP_SMT_P_SETCOUNT "l8" + +struct smt_p_setcount { + struct smt_para para ; /* generic parameter header */ + u_int count ; + u_char timestamp[8] ; +} ; + +/* + * SMT FRAMES + */ + +/* + * NIF : neighbor information frames + */ +struct smt_nif { + struct smt_header smt ; /* generic header */ + struct smt_p_una una ; /* UNA */ + struct smt_p_sde sde ; /* station descriptor */ + struct smt_p_state state ; /* station state */ +#ifdef SMT6_10 + struct smt_p_fsc fsc ; /* frame status cap. */ +#endif +} ; + +/* + * SIF : station information frames + */ +struct smt_sif_config { + struct smt_header smt ; /* generic header */ + struct smt_p_timestamp ts ; /* time stamp */ + struct smt_p_sde sde ; /* station descriptor */ + struct smt_p_version version ; /* supported versions */ + struct smt_p_state state ; /* station state */ + struct smt_p_policy policy ; /* station policy */ + struct smt_p_latency latency ; /* path latency */ + struct smt_p_neighbor neighbor ; /* neighbors, we have only one*/ +#ifdef OPT_PMF + struct smt_p_setcount setcount ; /* Set Count mandatory */ +#endif + /* WARNING : path MUST BE LAST FIELD !!! (see smt.c:smt_fill_path) */ + struct smt_p_path path ; /* path descriptor */ +} ; +#define SIZEOF_SMT_SIF_CONFIG (sizeof(struct smt_sif_config)- \ + sizeof(struct smt_p_path)) + +struct smt_sif_operation { + struct smt_header smt ; /* generic header */ + struct smt_p_timestamp ts ; /* time stamp */ + struct smt_p_mac_status status ; /* mac status */ + struct smt_p_mac_counter mc ; /* MAC counter */ + struct smt_p_mac_fnc fnc ; /* MAC frame not copied */ + struct smp_p_manufacturer man ; /* manufacturer field */ + struct smp_p_user user ; /* user field */ +#ifdef OPT_PMF + struct smt_p_setcount setcount ; /* Set Count mandatory */ +#endif + /* must be last */ + struct smt_p_lem lem[1] ; /* phy lem status */ +} ; +#define SIZEOF_SMT_SIF_OPERATION (sizeof(struct smt_sif_operation)- \ + sizeof(struct smt_p_lem)) + +/* + * ECF : echo frame + */ +struct smt_ecf { + struct smt_header smt ; /* generic header */ + struct smt_p_echo ec_echo ; /* echo parameter */ +} ; +#define SMT_ECF_LEN (sizeof(struct smt_header)+sizeof(struct smt_para)) + +/* + * RDF : request denied frame + */ +struct smt_rdf { + struct smt_header smt ; /* generic header */ + struct smt_p_reason reason ; /* reason code */ + struct smt_p_version version ; /* supported versions */ + struct smt_p_refused refused ; /* refused frame fragment */ +} ; + +/* + * SBA Request Allocation Responce Frame + */ +struct smt_sba_alc_res { + struct smt_header smt ; /* generic header */ + struct smt_p_0015 s_type ; /* resource type */ + struct smt_p_0016 cmd ; /* SBA command */ + struct smt_p_reason reason ; /* reason code */ + struct smt_p_320b path ; /* path type */ + struct smt_p_320f payload ; /* current SBA payload */ + struct smt_p_3210 overhead ; /* current SBA overhead */ + struct smt_p_0019 a_addr ; /* Allocation Address */ + struct smt_p_001a cat ; /* Category - from the request */ + struct smt_p_001d alloc ; /* SBA Allocatable */ +} ; + +/* + * SBA Request Allocation Request Frame + */ +struct smt_sba_alc_req { + struct smt_header smt ; /* generic header */ + struct smt_p_0015 s_type ; /* resource type */ + struct smt_p_0016 cmd ; /* SBA command */ + struct smt_p_320b path ; /* path type */ + struct smt_p_0017 pl_req ; /* requested payload */ + struct smt_p_0018 ov_req ; /* requested SBA overhead */ + struct smt_p_320f payload ; /* current SBA payload */ + struct smt_p_3210 overhead ; /* current SBA overhead */ + struct smt_p_0019 a_addr ; /* Allocation Address */ + struct smt_p_001a cat ; /* Category - from the request */ + struct smt_p_001b tneg ; /* max T-NEG */ + struct smt_p_001c segm ; /* minimum segment size */ +} ; + +/* + * SBA Change Allocation Request Frame + */ +struct smt_sba_chg { + struct smt_header smt ; /* generic header */ + struct smt_p_0015 s_type ; /* resource type */ + struct smt_p_0016 cmd ; /* SBA command */ + struct smt_p_320b path ; /* path type */ + struct smt_p_320f payload ; /* current SBA payload */ + struct smt_p_3210 overhead ; /* current SBA overhead */ + struct smt_p_001a cat ; /* Category - from the request */ +} ; + +/* + * SBA Report Allocation Request Frame + */ +struct smt_sba_rep_req { + struct smt_header smt ; /* generic header */ + struct smt_p_0015 s_type ; /* resource type */ + struct smt_p_0016 cmd ; /* SBA command */ +} ; + +/* + * SBA Report Allocation Response Frame + */ +struct smt_sba_rep_res { + struct smt_header smt ; /* generic header */ + struct smt_p_0015 s_type ; /* resource type */ + struct smt_p_0016 cmd ; /* SBA command */ + struct smt_p_320b path ; /* path type */ + struct smt_p_320f payload ; /* current SBA payload */ + struct smt_p_3210 overhead ; /* current SBA overhead */ +} ; + +/* + * actions + */ +#define SMT_STATION_ACTION 1 +#define SMT_STATION_ACTION_CONNECT 0 +#define SMT_STATION_ACTION_DISCONNECT 1 +#define SMT_STATION_ACTION_PATHTEST 2 +#define SMT_STATION_ACTION_SELFTEST 3 +#define SMT_STATION_ACTION_DISABLE_A 4 +#define SMT_STATION_ACTION_DISABLE_B 5 +#define SMT_STATION_ACTION_DISABLE_M 6 + +#define SMT_PORT_ACTION 2 +#define SMT_PORT_ACTION_MAINT 0 +#define SMT_PORT_ACTION_ENABLE 1 +#define SMT_PORT_ACTION_DISABLE 2 +#define SMT_PORT_ACTION_START 3 +#define SMT_PORT_ACTION_STOP 4 + +#endif /* _SMT_ */ diff --git a/drivers/net/skfp/h/smt_p.h b/drivers/net/skfp/h/smt_p.h new file mode 100644 index 000000000000..99f9be9552bb --- /dev/null +++ b/drivers/net/skfp/h/smt_p.h @@ -0,0 +1,326 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * defines for all SMT attributes + */ + +/* + * this boring file was produced by perl + * thanks Larry ! + */ +#define SMT_P0012 0x0012 + +#define SMT_P0015 0x0015 +#define SMT_P0016 0x0016 +#define SMT_P0017 0x0017 +#define SMT_P0018 0x0018 +#define SMT_P0019 0x0019 + +#define SMT_P001A 0x001a +#define SMT_P001B 0x001b +#define SMT_P001C 0x001c +#define SMT_P001D 0x001d + +#define SMT_P100A 0x100a +#define SMT_P100B 0x100b +#define SMT_P100C 0x100c +#define SMT_P100D 0x100d +#define SMT_P100E 0x100e +#define SMT_P100F 0x100f +#define SMT_P1010 0x1010 +#define SMT_P1011 0x1011 +#define SMT_P1012 0x1012 +#define SMT_P1013 0x1013 +#define SMT_P1014 0x1014 +#define SMT_P1015 0x1015 +#define SMT_P1016 0x1016 +#define SMT_P1017 0x1017 +#define SMT_P1018 0x1018 +#define SMT_P1019 0x1019 +#define SMT_P101A 0x101a +#define SMT_P101B 0x101b +#define SMT_P101C 0x101c +#define SMT_P101D 0x101d +#define SMT_P101E 0x101e +#define SMT_P101F 0x101f +#define SMT_P1020 0x1020 +#define SMT_P1021 0x1021 +#define SMT_P1022 0x1022 +#define SMT_P1023 0x1023 +#define SMT_P1024 0x1024 +#define SMT_P1025 0x1025 +#define SMT_P1026 0x1026 +#define SMT_P1027 0x1027 +#define SMT_P1028 0x1028 +#define SMT_P1029 0x1029 +#define SMT_P102A 0x102a +#define SMT_P102B 0x102b +#define SMT_P102C 0x102c +#define SMT_P102D 0x102d +#define SMT_P102E 0x102e +#define SMT_P102F 0x102f +#define SMT_P1030 0x1030 +#define SMT_P1031 0x1031 +#define SMT_P1032 0x1032 +#define SMT_P1033 0x1033 +#define SMT_P1034 0x1034 +#define SMT_P1035 0x1035 +#define SMT_P1036 0x1036 +#define SMT_P1037 0x1037 +#define SMT_P1038 0x1038 +#define SMT_P1039 0x1039 +#define SMT_P103A 0x103a +#define SMT_P103B 0x103b +#define SMT_P103C 0x103c +#define SMT_P103D 0x103d +#define SMT_P103E 0x103e +#define SMT_P103F 0x103f +#define SMT_P1040 0x1040 +#define SMT_P1041 0x1041 +#define SMT_P1042 0x1042 +#define SMT_P1043 0x1043 +#define SMT_P1044 0x1044 +#define SMT_P1045 0x1045 +#define SMT_P1046 0x1046 +#define SMT_P1047 0x1047 +#define SMT_P1048 0x1048 +#define SMT_P1049 0x1049 +#define SMT_P104A 0x104a +#define SMT_P104B 0x104b +#define SMT_P104C 0x104c +#define SMT_P104D 0x104d +#define SMT_P104E 0x104e +#define SMT_P104F 0x104f +#define SMT_P1050 0x1050 +#define SMT_P1051 0x1051 +#define SMT_P1052 0x1052 +#define SMT_P1053 0x1053 +#define SMT_P1054 0x1054 + +#define SMT_P10F0 0x10f0 +#define SMT_P10F1 0x10f1 +#ifdef ESS +#define SMT_P10F2 0x10f2 +#define SMT_P10F3 0x10f3 +#define SMT_P10F4 0x10f4 +#define SMT_P10F5 0x10f5 +#define SMT_P10F6 0x10f6 +#define SMT_P10F7 0x10f7 +#endif +#ifdef SBA +#define SMT_P10F8 0x10f8 +#define SMT_P10F9 0x10f9 +#endif + +#define SMT_P200A 0x200a +#define SMT_P200B 0x200b +#define SMT_P200C 0x200c +#define SMT_P200D 0x200d +#define SMT_P200E 0x200e +#define SMT_P200F 0x200f +#define SMT_P2010 0x2010 +#define SMT_P2011 0x2011 +#define SMT_P2012 0x2012 +#define SMT_P2013 0x2013 +#define SMT_P2014 0x2014 +#define SMT_P2015 0x2015 +#define SMT_P2016 0x2016 +#define SMT_P2017 0x2017 +#define SMT_P2018 0x2018 +#define SMT_P2019 0x2019 +#define SMT_P201A 0x201a +#define SMT_P201B 0x201b +#define SMT_P201C 0x201c +#define SMT_P201D 0x201d +#define SMT_P201E 0x201e +#define SMT_P201F 0x201f +#define SMT_P2020 0x2020 +#define SMT_P2021 0x2021 +#define SMT_P2022 0x2022 +#define SMT_P2023 0x2023 +#define SMT_P2024 0x2024 +#define SMT_P2025 0x2025 +#define SMT_P2026 0x2026 +#define SMT_P2027 0x2027 +#define SMT_P2028 0x2028 +#define SMT_P2029 0x2029 +#define SMT_P202A 0x202a +#define SMT_P202B 0x202b +#define SMT_P202C 0x202c +#define SMT_P202D 0x202d +#define SMT_P202E 0x202e +#define SMT_P202F 0x202f +#define SMT_P2030 0x2030 +#define SMT_P2031 0x2031 +#define SMT_P2032 0x2032 +#define SMT_P2033 0x2033 +#define SMT_P2034 0x2034 +#define SMT_P2035 0x2035 +#define SMT_P2036 0x2036 +#define SMT_P2037 0x2037 +#define SMT_P2038 0x2038 +#define SMT_P2039 0x2039 +#define SMT_P203A 0x203a +#define SMT_P203B 0x203b +#define SMT_P203C 0x203c +#define SMT_P203D 0x203d +#define SMT_P203E 0x203e +#define SMT_P203F 0x203f +#define SMT_P2040 0x2040 +#define SMT_P2041 0x2041 +#define SMT_P2042 0x2042 +#define SMT_P2043 0x2043 +#define SMT_P2044 0x2044 +#define SMT_P2045 0x2045 +#define SMT_P2046 0x2046 +#define SMT_P2047 0x2047 +#define SMT_P2048 0x2048 +#define SMT_P2049 0x2049 +#define SMT_P204A 0x204a +#define SMT_P204B 0x204b +#define SMT_P204C 0x204c +#define SMT_P204D 0x204d +#define SMT_P204E 0x204e +#define SMT_P204F 0x204f +#define SMT_P2050 0x2050 +#define SMT_P2051 0x2051 +#define SMT_P2052 0x2052 +#define SMT_P2053 0x2053 +#define SMT_P2054 0x2054 +#define SMT_P2055 0x2055 +#define SMT_P2056 0x2056 +#define SMT_P2057 0x2057 +#define SMT_P2058 0x2058 +#define SMT_P2059 0x2059 +#define SMT_P205A 0x205a +#define SMT_P205B 0x205b +#define SMT_P205C 0x205c +#define SMT_P205D 0x205d +#define SMT_P205E 0x205e +#define SMT_P205F 0x205f +#define SMT_P2060 0x2060 +#define SMT_P2061 0x2061 +#define SMT_P2062 0x2062 +#define SMT_P2063 0x2063 +#define SMT_P2064 0x2064 +#define SMT_P2065 0x2065 +#define SMT_P2066 0x2066 +#define SMT_P2067 0x2067 +#define SMT_P2068 0x2068 +#define SMT_P2069 0x2069 +#define SMT_P206A 0x206a +#define SMT_P206B 0x206b +#define SMT_P206C 0x206c +#define SMT_P206D 0x206d +#define SMT_P206E 0x206e +#define SMT_P206F 0x206f +#define SMT_P2070 0x2070 +#define SMT_P2071 0x2071 +#define SMT_P2072 0x2072 +#define SMT_P2073 0x2073 +#define SMT_P2074 0x2074 +#define SMT_P2075 0x2075 +#define SMT_P2076 0x2076 + +#define SMT_P208C 0x208c +#define SMT_P208D 0x208d +#define SMT_P208E 0x208e +#define SMT_P208F 0x208f +#define SMT_P2090 0x2090 + +#define SMT_P20F0 0x20F0 +#define SMT_P20F1 0x20F1 + +#define SMT_P320A 0x320a +#define SMT_P320B 0x320b +#define SMT_P320C 0x320c +#define SMT_P320D 0x320d +#define SMT_P320E 0x320e +#define SMT_P320F 0x320f +#define SMT_P3210 0x3210 +#define SMT_P3211 0x3211 +#define SMT_P3212 0x3212 +#define SMT_P3213 0x3213 +#define SMT_P3214 0x3214 +#define SMT_P3215 0x3215 +#define SMT_P3216 0x3216 +#define SMT_P3217 0x3217 + +#define SMT_P400A 0x400a +#define SMT_P400B 0x400b +#define SMT_P400C 0x400c +#define SMT_P400D 0x400d +#define SMT_P400E 0x400e +#define SMT_P400F 0x400f +#define SMT_P4010 0x4010 +#define SMT_P4011 0x4011 +#define SMT_P4012 0x4012 +#define SMT_P4013 0x4013 +#define SMT_P4014 0x4014 +#define SMT_P4015 0x4015 +#define SMT_P4016 0x4016 +#define SMT_P4017 0x4017 +#define SMT_P4018 0x4018 +#define SMT_P4019 0x4019 +#define SMT_P401A 0x401a +#define SMT_P401B 0x401b +#define SMT_P401C 0x401c +#define SMT_P401D 0x401d +#define SMT_P401E 0x401e +#define SMT_P401F 0x401f +#define SMT_P4020 0x4020 +#define SMT_P4021 0x4021 +#define SMT_P4022 0x4022 +#define SMT_P4023 0x4023 +#define SMT_P4024 0x4024 +#define SMT_P4025 0x4025 +#define SMT_P4026 0x4026 +#define SMT_P4027 0x4027 +#define SMT_P4028 0x4028 +#define SMT_P4029 0x4029 +#define SMT_P402A 0x402a +#define SMT_P402B 0x402b +#define SMT_P402C 0x402c +#define SMT_P402D 0x402d +#define SMT_P402E 0x402e +#define SMT_P402F 0x402f +#define SMT_P4030 0x4030 +#define SMT_P4031 0x4031 +#define SMT_P4032 0x4032 +#define SMT_P4033 0x4033 +#define SMT_P4034 0x4034 +#define SMT_P4035 0x4035 +#define SMT_P4036 0x4036 +#define SMT_P4037 0x4037 +#define SMT_P4038 0x4038 +#define SMT_P4039 0x4039 +#define SMT_P403A 0x403a +#define SMT_P403B 0x403b +#define SMT_P403C 0x403c +#define SMT_P403D 0x403d +#define SMT_P403E 0x403e +#define SMT_P403F 0x403f +#define SMT_P4040 0x4040 +#define SMT_P4041 0x4041 +#define SMT_P4042 0x4042 +#define SMT_P4043 0x4043 +#define SMT_P4044 0x4044 +#define SMT_P4045 0x4045 +#define SMT_P4046 0x4046 + +#define SMT_P4050 0x4050 +#define SMT_P4051 0x4051 +#define SMT_P4052 0x4052 +#define SMT_P4053 0x4053 diff --git a/drivers/net/skfp/h/smtstate.h b/drivers/net/skfp/h/smtstate.h new file mode 100644 index 000000000000..689fa25dc044 --- /dev/null +++ b/drivers/net/skfp/h/smtstate.h @@ -0,0 +1,100 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * SMT state definitions + */ + +#ifndef KERNEL +/* + * PCM states + */ +#define PC0_OFF 0 +#define PC1_BREAK 1 +#define PC2_TRACE 2 +#define PC3_CONNECT 3 +#define PC4_NEXT 4 +#define PC5_SIGNAL 5 +#define PC6_JOIN 6 +#define PC7_VERIFY 7 +#define PC8_ACTIVE 8 +#define PC9_MAINT 9 + +/* + * PCM modes + */ +#define PM_NONE 0 +#define PM_PEER 1 +#define PM_TREE 2 + +/* + * PCM type + */ +#define TA 0 +#define TB 1 +#define TS 2 +#define TM 3 +#define TNONE 4 + +/* + * CFM states + */ +#define SC0_ISOLATED 0 /* isolated */ +#define SC1_WRAP_A 5 /* wrap A */ +#define SC2_WRAP_B 6 /* wrap B */ +#define SC4_THRU_A 12 /* through A */ +#define SC5_THRU_B 7 /* through B (SMt 6.2) */ +#define SC7_WRAP_S 8 /* SAS */ + +/* + * ECM states + */ +#define EC0_OUT 0 +#define EC1_IN 1 +#define EC2_TRACE 2 +#define EC3_LEAVE 3 +#define EC4_PATH_TEST 4 +#define EC5_INSERT 5 +#define EC6_CHECK 6 +#define EC7_DEINSERT 7 + +/* + * RMT states + */ +#define RM0_ISOLATED 0 +#define RM1_NON_OP 1 /* not operational */ +#define RM2_RING_OP 2 /* ring operational */ +#define RM3_DETECT 3 /* detect dupl addresses */ +#define RM4_NON_OP_DUP 4 /* dupl. addr detected */ +#define RM5_RING_OP_DUP 5 /* ring oper. with dupl. addr */ +#define RM6_DIRECTED 6 /* sending directed beacons */ +#define RM7_TRACE 7 /* trace initiated */ +#endif + +struct pcm_state { + unsigned char pcm_type ; /* TA TB TS TM */ + unsigned char pcm_state ; /* state PC[0-9]_* */ + unsigned char pcm_mode ; /* PM_{NONE,PEER,TREE} */ + unsigned char pcm_neighbor ; /* TA TB TS TM */ + unsigned char pcm_bsf ; /* flag bs : TRUE/FALSE */ + unsigned char pcm_lsf ; /* flag ls : TRUE/FALSE */ + unsigned char pcm_lct_fail ; /* counter lct_fail */ + unsigned char pcm_ls_rx ; /* rx line state */ + short pcm_r_val ; /* signaling bits */ + short pcm_t_val ; /* signaling bits */ +} ; + +struct smt_state { + struct pcm_state pcm_state[NUMPHYS] ; /* port A & port B */ +} ; diff --git a/drivers/net/skfp/h/supern_2.h b/drivers/net/skfp/h/supern_2.h new file mode 100644 index 000000000000..ea564b337c90 --- /dev/null +++ b/drivers/net/skfp/h/supern_2.h @@ -0,0 +1,1059 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + defines for AMD Supernet II chip set + the chips are refered to as + FPLUS Formac Plus + PLC Physical Layer + + added defines for AMD Supernet III chip set + added comments on differences between Supernet II and Supernet III + added defines for the Motorola ELM (MOT_ELM) +*/ + +#ifndef _SUPERNET_ +#define _SUPERNET_ + +/* + * Define Supernet 3 when used + */ +#ifdef PCI +#ifndef SUPERNET_3 +#define SUPERNET_3 +#endif +#define TAG +#endif + +#define MB 0xff +#define MW 0xffff +#define MD 0xffffffff + +/* + * FORMAC frame status (rx_msext) + */ +#define FS_EI (1<<2) +#define FS_AI (1<<1) +#define FS_CI (1<<0) + +#define FS_MSVALID (1<<15) /* end of queue */ +#define FS_MSRABT (1<<14) /* frame was aborted during reception*/ +#define FS_SSRCRTG (1<<12) /* if SA has set MSB (source-routing)*/ +#define FS_SEAC2 (FS_EI<<9) /* error indicator */ +#define FS_SEAC1 (FS_AI<<9) /* address indicator */ +#define FS_SEAC0 (FS_CI<<9) /* copy indicator */ +#define FS_SFRMERR (1<<8) /* error detected (CRC or length) */ +#define FS_SADRRG (1<<7) /* address recognized */ +#define FS_SFRMTY2 (1<<6) /* frame-class bit */ +#define FS_SFRMTY1 (1<<5) /* frame-type bit (impementor) */ +#define FS_SFRMTY0 (1<<4) /* frame-type bit (LLC) */ +#define FS_ERFBB1 (1<<1) /* byte offset (depends on LSB bit) */ +#define FS_ERFBB0 (1<<0) /* - " - */ + +/* + * status frame type + */ +#define FRM_SMT (0) /* asynchr. frames */ +#define FRM_LLCA (1) +#define FRM_IMPA (2) +#define FRM_MAC (4) /* synchr. frames */ +#define FRM_LLCS (5) +#define FRM_IMPS (6) + +/* + * bits in rx_descr.i (receive frame status word) + */ +#define RX_MSVALID ((long)1<<31) /* memory status valid */ +#define RX_MSRABT ((long)1<<30) /* memory status receive abort */ +#define RX_FS_E ((long)FS_SEAC2<<16) /* error indicator */ +#define RX_FS_A ((long)FS_SEAC1<<16) /* address indicator */ +#define RX_FS_C ((long)FS_SEAC0<<16) /* copy indicator */ +#define RX_FS_CRC ((long)FS_SFRMERR<<16)/* error detected */ +#define RX_FS_ADDRESS ((long)FS_SADRRG<<16) /* address recognized */ +#define RX_FS_MAC ((long)FS_SFRMTY2<<16)/* MAC frame */ +#define RX_FS_SMT ((long)0<<16) /* SMT frame */ +#define RX_FS_IMPL ((long)FS_SFRMTY1<<16)/* implementer frame */ +#define RX_FS_LLC ((long)FS_SFRMTY0<<16)/* LLC frame */ + +/* + * receive frame descriptor + */ +union rx_descr { + struct { +#ifdef LITTLE_ENDIAN + unsigned rx_length :16 ; /* frame length lower/upper byte */ + unsigned rx_erfbb :2 ; /* received frame byte boundary */ + unsigned rx_reserv2:2 ; /* reserved */ + unsigned rx_sfrmty :3 ; /* frame type bits */ + unsigned rx_sadrrg :1 ; /* DA == MA or broad-/multicast */ + unsigned rx_sfrmerr:1 ; /* received frame not valid */ + unsigned rx_seac0 :1 ; /* frame-copied C-indicator */ + unsigned rx_seac1 :1 ; /* address-match A-indicator */ + unsigned rx_seac2 :1 ; /* frame-error E-indicator */ + unsigned rx_ssrcrtg:1 ; /* == 1 SA has MSB set */ + unsigned rx_reserv1:1 ; /* reserved */ + unsigned rx_msrabt :1 ; /* memory status receive abort */ + unsigned rx_msvalid:1 ; /* memory status valid */ +#else + unsigned rx_msvalid:1 ; /* memory status valid */ + unsigned rx_msrabt :1 ; /* memory status receive abort */ + unsigned rx_reserv1:1 ; /* reserved */ + unsigned rx_ssrcrtg:1 ; /* == 1 SA has MSB set */ + unsigned rx_seac2 :1 ; /* frame-error E-indicator */ + unsigned rx_seac1 :1 ; /* address-match A-indicator */ + unsigned rx_seac0 :1 ; /* frame-copied C-indicator */ + unsigned rx_sfrmerr:1 ; /* received frame not valid */ + unsigned rx_sadrrg :1 ; /* DA == MA or broad-/multicast */ + unsigned rx_sfrmty :3 ; /* frame type bits */ + unsigned rx_erfbb :2 ; /* received frame byte boundary */ + unsigned rx_reserv2:2 ; /* reserved */ + unsigned rx_length :16 ; /* frame length lower/upper byte */ +#endif + } r ; + long i ; +} ; + +/* defines for Receive Frame Descriptor access */ +#define RD_S_ERFBB 0x00030000L /* received frame byte boundary */ +#define RD_S_RES2 0x000c0000L /* reserved */ +#define RD_S_SFRMTY 0x00700000L /* frame type bits */ +#define RD_S_SADRRG 0x00800000L /* DA == MA or broad-/multicast */ +#define RD_S_SFRMERR 0x01000000L /* received frame not valid */ +#define RD_S_SEAC 0x0e000000L /* frame status indicators */ +#define RD_S_SEAC0 0x02000000L /* frame-copied case-indicator */ +#define RD_S_SEAC1 0x04000000L /* address-match A-indicator */ +#define RD_S_SEAC2 0x08000000L /* frame-error E-indicator */ +#define RD_S_SSRCRTG 0x10000000L /* == 1 SA has MSB set */ +#define RD_S_RES1 0x20000000L /* reserved */ +#define RD_S_MSRABT 0x40000000L /* memory status receive abort */ +#define RD_S_MSVALID 0x80000000L /* memory status valid */ + +#define RD_STATUS 0xffff0000L +#define RD_LENGTH 0x0000ffffL + +/* defines for Receive Frames Status Word values */ +/*RD_S_SFRMTY*/ +#define RD_FRM_SMT (unsigned long)(0<<20) /* asynchr. frames */ +#define RD_FRM_LLCA (unsigned long)(1<<20) +#define RD_FRM_IMPA (unsigned long)(2<<20) +#define RD_FRM_MAC (unsigned long)(4<<20) /* synchr. frames */ +#define RD_FRM_LLCS (unsigned long)(5<<20) +#define RD_FRM_IMPS (unsigned long)(6<<20) + +#define TX_DESCRIPTOR 0x40000000L +#define TX_OFFSET_3 0x18000000L + +#define TXP1 2 + +/* + * transmit frame descriptor + */ +union tx_descr { + struct { +#ifdef LITTLE_ENDIAN + unsigned tx_length:16 ; /* frame length lower/upper byte */ + unsigned tx_res :8 ; /* reserved (bit 16..23) */ + unsigned tx_xmtabt:1 ; /* transmit abort */ + unsigned tx_nfcs :1 ; /* no frame check sequence */ + unsigned tx_xdone :1 ; /* give up token */ + unsigned tx_rpxm :2 ; /* byte offset */ + unsigned tx_pat1 :2 ; /* must be TXP1 */ + unsigned tx_more :1 ; /* more frame in chain */ +#else + unsigned tx_more :1 ; /* more frame in chain */ + unsigned tx_pat1 :2 ; /* must be TXP1 */ + unsigned tx_rpxm :2 ; /* byte offset */ + unsigned tx_xdone :1 ; /* give up token */ + unsigned tx_nfcs :1 ; /* no frame check sequence */ + unsigned tx_xmtabt:1 ; /* transmit abort */ + unsigned tx_res :8 ; /* reserved (bit 16..23) */ + unsigned tx_length:16 ; /* frame length lower/upper byte */ +#endif + } t ; + long i ; +} ; + +/* defines for Transmit Descriptor access */ +#define TD_C_MORE 0x80000000L /* more frame in chain */ +#define TD_C_DESCR 0x60000000L /* must be TXP1 */ +#define TD_C_TXFBB 0x18000000L /* byte offset */ +#define TD_C_XDONE 0x04000000L /* give up token */ +#define TD_C_NFCS 0x02000000L /* no frame check sequence */ +#define TD_C_XMTABT 0x01000000L /* transmit abort */ + +#define TD_C_LNCNU 0x0000ff00L +#define TD_C_LNCNL 0x000000ffL +#define TD_C_LNCN 0x0000ffffL /* frame length lower/upper byte */ + +/* + * transmit pointer + */ +union tx_pointer { + struct t { +#ifdef LITTLE_ENDIAN + unsigned tp_pointer:16 ; /* pointer to tx_descr (low/high) */ + unsigned tp_res :8 ; /* reserved (bit 16..23) */ + unsigned tp_pattern:8 ; /* fixed pattern (bit 24..31) */ +#else + unsigned tp_pattern:8 ; /* fixed pattern (bit 24..31) */ + unsigned tp_res :8 ; /* reserved (bit 16..23) */ + unsigned tp_pointer:16 ; /* pointer to tx_descr (low/high) */ +#endif + } t ; + long i ; +} ; + +/* defines for Nontag Mode Pointer access */ +#define TD_P_CNTRL 0xff000000L +#define TD_P_RPXU 0x0000ff00L +#define TD_P_RPXL 0x000000ffL +#define TD_P_RPX 0x0000ffffL + + +#define TX_PATTERN 0xa0 +#define TX_POINTER_END 0xa0000000L +#define TX_INT_PATTERN 0xa0000000L + +struct tx_queue { + struct tx_queue *tq_next ; + u_short tq_pack_offset ; /* offset buffer memory */ + u_char tq_pad[2] ; +} ; + +/* + defines for FORMAC Plus (Am79C830) +*/ + +/* + * FORMAC+ read/write (r/w) registers + */ +#define FM_CMDREG1 0x00 /* write command reg 1 instruction */ +#define FM_CMDREG2 0x01 /* write command reg 2 instruction */ +#define FM_ST1U 0x00 /* read upper 16-bit of status reg 1 */ +#define FM_ST1L 0x01 /* read lower 16-bit of status reg 1 */ +#define FM_ST2U 0x02 /* read upper 16-bit of status reg 2 */ +#define FM_ST2L 0x03 /* read lower 16-bit of status reg 2 */ +#define FM_IMSK1U 0x04 /* r/w upper 16-bit of IMSK 1 */ +#define FM_IMSK1L 0x05 /* r/w lower 16-bit of IMSK 1 */ +#define FM_IMSK2U 0x06 /* r/w upper 16-bit of IMSK 2 */ +#define FM_IMSK2L 0x07 /* r/w lower 16-bit of IMSK 2 */ +#define FM_SAID 0x08 /* r/w short addr.-individual */ +#define FM_LAIM 0x09 /* r/w long addr.-ind. (MSW of LAID) */ +#define FM_LAIC 0x0a /* r/w long addr.-ind. (middle)*/ +#define FM_LAIL 0x0b /* r/w long addr.-ind. (LSW) */ +#define FM_SAGP 0x0c /* r/w short address-group */ +#define FM_LAGM 0x0d /* r/w long addr.-gr. (MSW of LAGP) */ +#define FM_LAGC 0x0e /* r/w long addr.-gr. (middle) */ +#define FM_LAGL 0x0f /* r/w long addr.-gr. (LSW) */ +#define FM_MDREG1 0x10 /* r/w 16-bit mode reg 1 */ +#define FM_STMCHN 0x11 /* read state-machine reg */ +#define FM_MIR1 0x12 /* read upper 16-bit of MAC Info Reg */ +#define FM_MIR0 0x13 /* read lower 16-bit of MAC Info Reg */ +#define FM_TMAX 0x14 /* r/w 16-bit TMAX reg */ +#define FM_TVX 0x15 /* write 8-bit TVX reg with NP7-0 + read TVX on NP7-0, timer on NP15-8*/ +#define FM_TRT 0x16 /* r/w upper 16-bit of TRT timer */ +#define FM_THT 0x17 /* r/w upper 16-bit of THT timer */ +#define FM_TNEG 0x18 /* read upper 16-bit of TNEG (TTRT) */ +#define FM_TMRS 0x19 /* read lower 5-bit of TNEG,TRT,THT */ + /* F E D C B A 9 8 7 6 5 4 3 2 1 0 + x |-TNEG4-0| |-TRT4-0-| |-THT4-0-| (x-late count) */ +#define FM_TREQ0 0x1a /* r/w 16-bit TREQ0 reg (LSW of TRT) */ +#define FM_TREQ1 0x1b /* r/w 16-bit TREQ1 reg (MSW of TRT) */ +#define FM_PRI0 0x1c /* r/w priority r. for asyn.-queue 0 */ +#define FM_PRI1 0x1d /* r/w priority r. for asyn.-queue 1 */ +#define FM_PRI2 0x1e /* r/w priority r. for asyn.-queue 2 */ +#define FM_TSYNC 0x1f /* r/w 16-bit of the TSYNC register */ +#define FM_MDREG2 0x20 /* r/w 16-bit mode reg 2 */ +#define FM_FRMTHR 0x21 /* r/w the frame threshold register */ +#define FM_EACB 0x22 /* r/w end addr of claim/beacon area */ +#define FM_EARV 0x23 /* r/w end addr of receive queue */ +/* Supernet 3 */ +#define FM_EARV1 FM_EARV + +#define FM_EAS 0x24 /* r/w end addr of synchr. queue */ +#define FM_EAA0 0x25 /* r/w end addr of asyn. queue 0 */ +#define FM_EAA1 0x26 /* r/w end addr of asyn. queue 1 */ +#define FM_EAA2 0x27 /* r/w end addr of asyn. queue 2 */ +#define FM_SACL 0x28 /* r/w start addr of claim frame */ +#define FM_SABC 0x29 /* r/w start addr of beacon frame */ +#define FM_WPXSF 0x2a /* r/w the write ptr. for special fr.*/ +#define FM_RPXSF 0x2b /* r/w the read ptr. for special fr. */ +#define FM_RPR 0x2d /* r/w the read ptr. for receive qu. */ +#define FM_WPR 0x2e /* r/w the write ptr. for receive qu.*/ +#define FM_SWPR 0x2f /* r/w the shadow wr.-ptr. for rec.q.*/ +/* Supernet 3 */ +#define FM_RPR1 FM_RPR +#define FM_WPR1 FM_WPR +#define FM_SWPR1 FM_SWPR + +#define FM_WPXS 0x30 /* r/w the write ptr. for synchr. qu.*/ +#define FM_WPXA0 0x31 /* r/w the write ptr. for asyn. qu.0 */ +#define FM_WPXA1 0x32 /* r/w the write ptr. for asyn. qu.1 */ +#define FM_WPXA2 0x33 /* r/w the write ptr. for asyn. qu.2 */ +#define FM_SWPXS 0x34 /* r/w the shadow wr.-ptr. for syn.q.*/ +#define FM_SWPXA0 0x35 /* r/w the shad. wr.-ptr. for asyn.q0*/ +#define FM_SWPXA1 0x36 /* r/w the shad. wr.-ptr. for asyn.q1*/ +#define FM_SWPXA2 0x37 /* r/w the shad. wr.-ptr. for asyn.q2*/ +#define FM_RPXS 0x38 /* r/w the read ptr. for synchr. qu. */ +#define FM_RPXA0 0x39 /* r/w the read ptr. for asyn. qu. 0 */ +#define FM_RPXA1 0x3a /* r/w the read ptr. for asyn. qu. 1 */ +#define FM_RPXA2 0x3b /* r/w the read ptr. for asyn. qu. 2 */ +#define FM_MARR 0x3c /* r/w the memory read addr register */ +#define FM_MARW 0x3d /* r/w the memory write addr register*/ +#define FM_MDRU 0x3e /* r/w upper 16-bit of mem. data reg */ +#define FM_MDRL 0x3f /* r/w lower 16-bit of mem. data reg */ + +/* following instructions relate to MAC counters and timer */ +#define FM_TMSYNC 0x40 /* r/w upper 16 bits of TMSYNC timer */ +#define FM_FCNTR 0x41 /* r/w the 16-bit frame counter */ +#define FM_LCNTR 0x42 /* r/w the 16-bit lost counter */ +#define FM_ECNTR 0x43 /* r/w the 16-bit error counter */ + +/* Supernet 3: extensions to old register block */ +#define FM_FSCNTR 0x44 /* r/? Frame Strip Counter */ +#define FM_FRSELREG 0x45 /* r/w Frame Selection Register */ + +/* Supernet 3: extensions for 2. receive queue etc. */ +#define FM_MDREG3 0x60 /* r/w Mode Register 3 */ +#define FM_ST3U 0x61 /* read upper 16-bit of status reg 3 */ +#define FM_ST3L 0x62 /* read lower 16-bit of status reg 3 */ +#define FM_IMSK3U 0x63 /* r/w upper 16-bit of IMSK reg 3 */ +#define FM_IMSK3L 0x64 /* r/w lower 16-bit of IMSK reg 3 */ +#define FM_IVR 0x65 /* read Interrupt Vector register */ +#define FM_IMR 0x66 /* r/w Interrupt mask register */ +/* 0x67 Hidden */ +#define FM_RPR2 0x68 /* r/w the read ptr. for rec. qu. 2 */ +#define FM_WPR2 0x69 /* r/w the write ptr. for rec. qu. 2 */ +#define FM_SWPR2 0x6a /* r/w the shadow wptr. for rec. q. 2 */ +#define FM_EARV2 0x6b /* r/w end addr of rec. qu. 2 */ +#define FM_UNLCKDLY 0x6c /* r/w Auto Unlock Delay register */ + /* Bit 15-8: RECV2 unlock threshold */ + /* Bit 7-0: RECV1 unlock threshold */ +/* 0x6f-0x73 Hidden */ +#define FM_LTDPA1 0x79 /* r/w Last Trans desc ptr for A1 qu. */ +/* 0x80-0x9a PLCS registers of built-in PLCS (Supernet 3 only) */ + +/* Supernet 3: Adderss Filter Registers */ +#define FM_AFCMD 0xb0 /* r/w Address Filter Command Reg */ +#define FM_AFSTAT 0xb2 /* r/w Address Filter Status Reg */ +#define FM_AFBIST 0xb4 /* r/w Address Filter BIST signature */ +#define FM_AFCOMP2 0xb6 /* r/w Address Filter Comparand 2 */ +#define FM_AFCOMP1 0xb8 /* r/w Address Filter Comparand 1 */ +#define FM_AFCOMP0 0xba /* r/w Address Filter Comparand 0 */ +#define FM_AFMASK2 0xbc /* r/w Address Filter Mask 2 */ +#define FM_AFMASK1 0xbe /* r/w Address Filter Mask 1 */ +#define FM_AFMASK0 0xc0 /* r/w Address Filter Mask 0 */ +#define FM_AFPERS 0xc2 /* r/w Address Filter Personality Reg */ + +/* Supernet 3: Orion (PDX?) Registers */ +#define FM_ORBIST 0xd0 /* r/w Orion BIST signature */ +#define FM_ORSTAT 0xd2 /* r/w Orion Status Register */ + + +/* + * Mode Register 1 (MDREG1) + */ +#define FM_RES0 0x0001 /* reserved */ + /* SN3: other definition */ +#define FM_XMTINH_HOLD 0x0002 /* transmit-inhibit/hold bit */ + /* SN3: other definition */ +#define FM_HOFLXI 0x0003 /* SN3: Hold / Flush / Inhibit */ +#define FM_FULL_HALF 0x0004 /* full-duplex/half-duplex bit */ +#define FM_LOCKTX 0x0008 /* lock-transmit-asynchr.-queues bit */ +#define FM_EXGPA0 0x0010 /* extended-group-addressing bit 0 */ +#define FM_EXGPA1 0x0020 /* extended-group-addressing bit 1 */ +#define FM_DISCRY 0x0040 /* disable-carry bit */ + /* SN3: reserved */ +#define FM_SELRA 0x0080 /* select input from PHY (1=RA,0=RB) */ + +#define FM_ADDET 0x0700 /* address detection */ +#define FM_MDAMA (0<<8) /* address detection : DA = MA */ +#define FM_MDASAMA (1<<8) /* address detection : DA=MA||SA=MA */ +#define FM_MRNNSAFNMA (2<<8) /* rec. non-NSA frames DA=MA&&SA!=MA */ +#define FM_MRNNSAF (3<<8) /* rec. non-NSA frames DA = MA */ +#define FM_MDISRCV (4<<8) /* disable receive function */ +#define FM_MRES0 (5<<8) /* reserve */ +#define FM_MLIMPROM (6<<8) /* limited-promiscuous mode */ +#define FM_MPROMISCOUS (7<<8) /* address detection : promiscous */ + +#define FM_SELSA 0x0800 /* select-short-address bit */ + +#define FM_MMODE 0x7000 /* mode select */ +#define FM_MINIT (0<<12) /* initialize */ +#define FM_MMEMACT (1<<12) /* memory activate */ +#define FM_MONLINESP (2<<12) /* on-line special */ +#define FM_MONLINE (3<<12) /* on-line (FDDI operational mode) */ +#define FM_MILOOP (4<<12) /* internal loopback */ +#define FM_MRES1 (5<<12) /* reserved */ +#define FM_MRES2 (6<<12) /* reserved */ +#define FM_MELOOP (7<<12) /* external loopback */ + +#define FM_SNGLFRM 0x8000 /* single-frame-receive mode */ + /* SN3: reserved */ + +#define MDR1INIT (FM_MINIT | FM_MDAMA) + +/* + * Mode Register 2 (MDREG2) + */ +#define FM_AFULL 0x000f /* 4-bit value (empty loc.in txqueue)*/ +#define FM_RCVERR 0x0010 /* rec.-errored-frames bit */ +#define FM_SYMCTL 0x0020 /* sysmbol-control bit */ + /* SN3: reserved */ +#define FM_SYNPRQ 0x0040 /* synchron.-NP-DMA-request bit */ +#define FM_ENNPRQ 0x0080 /* enable-NP-DMA-request bit */ +#define FM_ENHSRQ 0x0100 /* enable-host-request bit */ +#define FM_RXFBB01 0x0600 /* rec. frame byte boundary bit0 & 1 */ +#define FM_LSB 0x0800 /* determ. ordering of bytes in buffer*/ +#define FM_PARITY 0x1000 /* 1 = even, 0 = odd */ +#define FM_CHKPAR 0x2000 /* 1 = parity of 32-bit buffer BD-bus*/ +#define FM_STRPFCS 0x4000 /* 1 = strips FCS field of rec.frame */ +#define FM_BMMODE 0x8000 /* Buffer-Memory-Mode (1 = tag mode) */ + /* SN3: 1 = tag, 0 = modified tag */ + +/* + * Status Register 1, Upper 16 Bits (ST1U) + */ +#define FM_STEFRMS 0x0001 /* transmit end of frame: synchr. qu.*/ +#define FM_STEFRMA0 0x0002 /* transmit end of frame: asyn. qu.0 */ +#define FM_STEFRMA1 0x0004 /* transmit end of frame: asyn. qu.1 */ +#define FM_STEFRMA2 0x0008 /* transmit end of frame: asyn. qu.2 */ + /* SN3: reserved */ +#define FM_STECFRMS 0x0010 /* transmit end of chain of syn. qu. */ + /* SN3: reserved */ +#define FM_STECFRMA0 0x0020 /* transmit end of chain of asyn. q0 */ + /* SN3: reserved */ +#define FM_STECFRMA1 0x0040 /* transmit end of chain of asyn. q1 */ + /* SN3: STECMDA1 */ +#define FM_STECMDA1 0x0040 /* SN3: 'no description' */ +#define FM_STECFRMA2 0x0080 /* transmit end of chain of asyn. q2 */ + /* SN3: reserved */ +#define FM_STEXDONS 0x0100 /* transmit until XDONE in syn. qu. */ +#define FM_STBFLA 0x0200 /* asynchr.-queue trans. buffer full */ +#define FM_STBFLS 0x0400 /* synchr.-queue transm. buffer full */ +#define FM_STXABRS 0x0800 /* synchr. queue transmit-abort */ +#define FM_STXABRA0 0x1000 /* asynchr. queue 0 transmit-abort */ +#define FM_STXABRA1 0x2000 /* asynchr. queue 1 transmit-abort */ +#define FM_STXABRA2 0x4000 /* asynchr. queue 2 transmit-abort */ + /* SN3: reserved */ +#define FM_SXMTABT 0x8000 /* transmit abort */ + +/* + * Status Register 1, Lower 16 Bits (ST1L) + */ +#define FM_SQLCKS 0x0001 /* queue lock for synchr. queue */ +#define FM_SQLCKA0 0x0002 /* queue lock for asynchr. queue 0 */ +#define FM_SQLCKA1 0x0004 /* queue lock for asynchr. queue 1 */ +#define FM_SQLCKA2 0x0008 /* queue lock for asynchr. queue 2 */ + /* SN3: reserved */ +#define FM_STXINFLS 0x0010 /* transmit instruction full: syn. */ + /* SN3: reserved */ +#define FM_STXINFLA0 0x0020 /* transmit instruction full: asyn.0 */ + /* SN3: reserved */ +#define FM_STXINFLA1 0x0040 /* transmit instruction full: asyn.1 */ + /* SN3: reserved */ +#define FM_STXINFLA2 0x0080 /* transmit instruction full: asyn.2 */ + /* SN3: reserved */ +#define FM_SPCEPDS 0x0100 /* parity/coding error: syn. queue */ +#define FM_SPCEPDA0 0x0200 /* parity/coding error: asyn. queue0 */ +#define FM_SPCEPDA1 0x0400 /* parity/coding error: asyn. queue1 */ +#define FM_SPCEPDA2 0x0800 /* parity/coding error: asyn. queue2 */ + /* SN3: reserved */ +#define FM_STBURS 0x1000 /* transmit buffer underrun: syn. q. */ +#define FM_STBURA0 0x2000 /* transmit buffer underrun: asyn.0 */ +#define FM_STBURA1 0x4000 /* transmit buffer underrun: asyn.1 */ +#define FM_STBURA2 0x8000 /* transmit buffer underrun: asyn.2 */ + /* SN3: reserved */ + +/* + * Status Register 2, Upper 16 Bits (ST2U) + */ +#define FM_SOTRBEC 0x0001 /* other beacon received */ +#define FM_SMYBEC 0x0002 /* my beacon received */ +#define FM_SBEC 0x0004 /* beacon state entered */ +#define FM_SLOCLM 0x0008 /* low claim received */ +#define FM_SHICLM 0x0010 /* high claim received */ +#define FM_SMYCLM 0x0020 /* my claim received */ +#define FM_SCLM 0x0040 /* claim state entered */ +#define FM_SERRSF 0x0080 /* error in special frame */ +#define FM_SNFSLD 0x0100 /* NP and FORMAC+ simultaneous load */ +#define FM_SRFRCTOV 0x0200 /* receive frame counter overflow */ + /* SN3: reserved */ +#define FM_SRCVFRM 0x0400 /* receive frame */ + /* SN3: reserved */ +#define FM_SRCVOVR 0x0800 /* receive FIFO overflow */ +#define FM_SRBFL 0x1000 /* receive buffer full */ +#define FM_SRABT 0x2000 /* receive abort */ +#define FM_SRBMT 0x4000 /* receive buffer empty */ +#define FM_SRCOMP 0x8000 /* receive complete. Nontag mode */ + +/* + * Status Register 2, Lower 16 Bits (ST2L) + * Attention: SN3 docu shows these bits the other way around + */ +#define FM_SRES0 0x0001 /* reserved */ +#define FM_SESTRIPTK 0x0001 /* SN3: 'no description' */ +#define FM_STRTEXR 0x0002 /* TRT expired in claim | beacon st. */ +#define FM_SDUPCLM 0x0004 /* duplicate claim received */ +#define FM_SSIFG 0x0008 /* short interframe gap */ +#define FM_SFRMCTR 0x0010 /* frame counter overflow */ +#define FM_SERRCTR 0x0020 /* error counter overflow */ +#define FM_SLSTCTR 0x0040 /* lost counter overflow */ +#define FM_SPHINV 0x0080 /* PHY invalid */ +#define FM_SADET 0x0100 /* address detect */ +#define FM_SMISFRM 0x0200 /* missed frame */ +#define FM_STRTEXP 0x0400 /* TRT expired and late count > 0 */ +#define FM_STVXEXP 0x0800 /* TVX expired */ +#define FM_STKISS 0x1000 /* token issued */ +#define FM_STKERR 0x2000 /* token error */ +#define FM_SMULTDA 0x4000 /* multiple destination address */ +#define FM_SRNGOP 0x8000 /* ring operational */ + +/* + * Supernet 3: + * Status Register 3, Upper 16 Bits (ST3U) + */ +#define FM_SRQUNLCK1 0x0001 /* receive queue unlocked queue 1 */ +#define FM_SRQUNLCK2 0x0002 /* receive queue unlocked queue 2 */ +#define FM_SRPERRQ1 0x0004 /* receive parity error rx queue 1 */ +#define FM_SRPERRQ2 0x0008 /* receive parity error rx queue 2 */ + /* Bit 4-10: reserved */ +#define FM_SRCVOVR2 0x0800 /* receive FIFO overfull rx queue 2 */ +#define FM_SRBFL2 0x1000 /* receive buffer full rx queue 2 */ +#define FM_SRABT2 0x2000 /* receive abort rx queue 2 */ +#define FM_SRBMT2 0x4000 /* receive buf empty rx queue 2 */ +#define FM_SRCOMP2 0x8000 /* receive comp rx queue 2 */ + +/* + * Supernet 3: + * Status Register 3, Lower 16 Bits (ST3L) + */ +#define FM_AF_BIST_DONE 0x0001 /* Address Filter BIST is done */ +#define FM_PLC_BIST_DONE 0x0002 /* internal PLC Bist is done */ +#define FM_PDX_BIST_DONE 0x0004 /* PDX BIST is done */ + /* Bit 3: reserved */ +#define FM_SICAMDAMAT 0x0010 /* Status internal CAM DA match */ +#define FM_SICAMDAXACT 0x0020 /* Status internal CAM DA exact match */ +#define FM_SICAMSAMAT 0x0040 /* Status internal CAM SA match */ +#define FM_SICAMSAXACT 0x0080 /* Status internal CAM SA exact match */ + +/* + * MAC State-Machine Register FM_STMCHN + */ +#define FM_MDRTAG 0x0004 /* tag bit of long word read */ +#define FM_SNPPND 0x0008 /* r/w from buffer mem. is pending */ +#define FM_TXSTAT 0x0070 /* transmitter state machine state */ +#define FM_RCSTAT 0x0380 /* receiver state machine state */ +#define FM_TM01 0x0c00 /* indicate token mode */ +#define FM_SIM 0x1000 /* indicate send immediate-mode */ +#define FM_REV 0xe000 /* FORMAC Plus revision number */ + +/* + * Supernet 3 + * Mode Register 3 + */ +#define FM_MENRS 0x0001 /* Ena enhanced rec status encoding */ +#define FM_MENXS 0x0002 /* Ena enhanced xmit status encoding */ +#define FM_MENXCT 0x0004 /* Ena EXACT/INEXACT matching */ +#define FM_MENAFULL 0x0008 /* Ena enh QCTRL encoding for AFULL */ +#define FM_MEIND 0x0030 /* Ena enh A,C indicator settings */ +#define FM_MENQCTRL 0x0040 /* Ena enh QCTRL encoding */ +#define FM_MENRQAUNLCK 0x0080 /* Ena rec q auto unlock */ +#define FM_MENDAS 0x0100 /* Ena DAS connections by cntr MUX */ +#define FM_MENPLCCST 0x0200 /* Ena Counter Segm test in PLC blck */ +#define FM_MENSGLINT 0x0400 /* Ena Vectored Interrupt reading */ +#define FM_MENDRCV 0x0800 /* Ena dual receive queue operation */ +#define FM_MENFCLOC 0x3000 /* Ena FC location within frm data */ +#define FM_MENTRCMD 0x4000 /* Ena ASYNC1 xmit only after command */ +#define FM_MENTDLPBK 0x8000 /* Ena TDAT to RDAT lkoopback */ + +/* + * Supernet 3 + * Frame Selection Register + */ +#define FM_RECV1 0x000f /* options for receive queue 1 */ +#define FM_RCV1_ALL (0<<0) /* receive all frames */ +#define FM_RCV1_LLC (1<<0) /* rec all LLC frames */ +#define FM_RCV1_SMT (2<<0) /* rec all SMT frames */ +#define FM_RCV1_NSMT (3<<0) /* rec non-SMT frames */ +#define FM_RCV1_IMP (4<<0) /* rec Implementor frames */ +#define FM_RCV1_MAC (5<<0) /* rec all MAC frames */ +#define FM_RCV1_SLLC (6<<0) /* rec all sync LLC frames */ +#define FM_RCV1_ALLC (7<<0) /* rec all async LLC frames */ +#define FM_RCV1_VOID (8<<0) /* rec all void frames */ +#define FM_RCV1_ALSMT (9<<0) /* rec all async LLC & SMT frames */ +#define FM_RECV2 0x00f0 /* options for receive queue 2 */ +#define FM_RCV2_ALL (0<<4) /* receive all other frames */ +#define FM_RCV2_LLC (1<<4) /* rec all LLC frames */ +#define FM_RCV2_SMT (2<<4) /* rec all SMT frames */ +#define FM_RCV2_NSMT (3<<4) /* rec non-SMT frames */ +#define FM_RCV2_IMP (4<<4) /* rec Implementor frames */ +#define FM_RCV2_MAC (5<<4) /* rec all MAC frames */ +#define FM_RCV2_SLLC (6<<4) /* rec all sync LLC frames */ +#define FM_RCV2_ALLC (7<<4) /* rec all async LLC frames */ +#define FM_RCV2_VOID (8<<4) /* rec all void frames */ +#define FM_RCV2_ALSMT (9<<4) /* rec all async LLC & SMT frames */ +#define FM_ENXMTADSWAP 0x4000 /* enh rec addr swap (phys -> can) */ +#define FM_ENRCVADSWAP 0x8000 /* enh tx addr swap (can -> phys) */ + +/* + * Supernet 3: + * Address Filter Command Register (AFCMD) + */ +#define FM_INST 0x0007 /* Address Filter Operation */ +#define FM_IINV_CAM (0<<0) /* Invalidate CAM */ +#define FM_IWRITE_CAM (1<<0) /* Write CAM */ +#define FM_IREAD_CAM (2<<0) /* Read CAM */ +#define FM_IRUN_BIST (3<<0) /* Run BIST */ +#define FM_IFIND (4<<0) /* Find */ +#define FM_IINV (5<<0) /* Invalidate */ +#define FM_ISKIP (6<<0) /* Skip */ +#define FM_ICL_SKIP (7<<0) /* Clear all SKIP bits */ + +/* + * Supernet 3: + * Address Filter Status Register (AFSTAT) + */ + /* Bit 0-4: reserved */ +#define FM_REV_NO 0x00e0 /* Revision Number of Address Filter */ +#define FM_BIST_DONE 0x0100 /* BIST complete */ +#define FM_EMPTY 0x0200 /* CAM empty */ +#define FM_ERROR 0x0400 /* Error (improper operation) */ +#define FM_MULT 0x0800 /* Multiple Match */ +#define FM_EXACT 0x1000 /* Exact Match */ +#define FM_FOUND 0x2000 /* Comparand found in CAM */ +#define FM_FULL 0x4000 /* CAM full */ +#define FM_DONE 0x8000 /* DONE indicator */ + +/* + * Supernet 3: + * BIST Signature Register (AFBIST) + */ +#define AF_BIST_SIGNAT 0x0553 /* Address Filter BIST Signature */ + +/* + * Supernet 3: + * Personality Register (AFPERS) + */ +#define FM_VALID 0x0001 /* CAM Entry Valid */ +#define FM_DA 0x0002 /* Destination Address */ +#define FM_DAX 0x0004 /* Destination Address Exact */ +#define FM_SA 0x0008 /* Source Address */ +#define FM_SAX 0x0010 /* Source Address Exact */ +#define FM_SKIP 0x0020 /* Skip this entry */ + +/* + * instruction set for command register 1 (NPADDR6-0 = 0x00) + */ +#define FM_IRESET 0x01 /* software reset */ +#define FM_IRMEMWI 0x02 /* load Memory Data Reg., inc MARR */ +#define FM_IRMEMWO 0x03 /* load MDR from buffer memory, n.i. */ +#define FM_IIL 0x04 /* idle/listen */ +#define FM_ICL 0x05 /* claim/listen */ +#define FM_IBL 0x06 /* beacon/listen */ +#define FM_ILTVX 0x07 /* load TVX timer from TVX reg */ +#define FM_INRTM 0x08 /* nonrestricted token mode */ +#define FM_IENTM 0x09 /* enter nonrestricted token mode */ +#define FM_IERTM 0x0a /* enter restricted token mode */ +#define FM_IRTM 0x0b /* restricted token mode */ +#define FM_ISURT 0x0c /* send unrestricted token */ +#define FM_ISRT 0x0d /* send restricted token */ +#define FM_ISIM 0x0e /* enter send-immediate mode */ +#define FM_IESIM 0x0f /* exit send-immediate mode */ +#define FM_ICLLS 0x11 /* clear synchronous queue lock */ +#define FM_ICLLA0 0x12 /* clear asynchronous queue 0 lock */ +#define FM_ICLLA1 0x14 /* clear asynchronous queue 1 lock */ +#define FM_ICLLA2 0x18 /* clear asynchronous queue 2 lock */ + /* SN3: reserved */ +#define FM_ICLLR 0x20 /* clear receive queue (SN3:1) lock */ +#define FM_ICLLR2 0x21 /* SN3: clear receive queue 2 lock */ +#define FM_ITRXBUS 0x22 /* SN3: Tristate X-Bus (SAS only) */ +#define FM_IDRXBUS 0x23 /* SN3: drive X-Bus */ +#define FM_ICLLAL 0x3f /* clear all queue locks */ + +/* + * instruction set for command register 2 (NPADDR6-0 = 0x01) + */ +#define FM_ITRS 0x01 /* transmit synchronous queue */ + /* SN3: reserved */ +#define FM_ITRA0 0x02 /* transmit asynchronous queue 0 */ + /* SN3: reserved */ +#define FM_ITRA1 0x04 /* transmit asynchronous queue 1 */ + /* SN3: reserved */ +#define FM_ITRA2 0x08 /* transmit asynchronous queue 2 */ + /* SN3: reserved */ +#define FM_IACTR 0x10 /* abort current transmit activity */ +#define FM_IRSTQ 0x20 /* reset transmit queues */ +#define FM_ISTTB 0x30 /* set tag bit */ +#define FM_IERSF 0x40 /* enable receive single frame */ + /* SN3: reserved */ +#define FM_ITR 0x50 /* SN3: Transmit Command */ + + +/* + * defines for PLC (Am79C864) + */ + +/* + * PLC read/write (r/w) registers + */ +#define PL_CNTRL_A 0x00 /* control register A (r/w) */ +#define PL_CNTRL_B 0x01 /* control register B (r/w) */ +#define PL_INTR_MASK 0x02 /* interrupt mask (r/w) */ +#define PL_XMIT_VECTOR 0x03 /* transmit vector register (r/w) */ +#define PL_VECTOR_LEN 0x04 /* transmit vector length (r/w) */ +#define PL_LE_THRESHOLD 0x05 /* link error event threshold (r/w) */ +#define PL_C_MIN 0x06 /* minimum connect state time (r/w) */ +#define PL_TL_MIN 0x07 /* min. line state transmit t. (r/w) */ +#define PL_TB_MIN 0x08 /* minimum break time (r/w) */ +#define PL_T_OUT 0x09 /* signal timeout (r/w) */ +#define PL_CNTRL_C 0x0a /* control register C (r/w) */ +#define PL_LC_LENGTH 0x0b /* link confidence test time (r/w) */ +#define PL_T_SCRUB 0x0c /* scrub time = MAC TVX (r/w) */ +#define PL_NS_MAX 0x0d /* max. noise time before break (r/w)*/ +#define PL_TPC_LOAD_V 0x0e /* TPC timer load value (write only) */ +#define PL_TNE_LOAD_V 0x0f /* TNE timer load value (write only) */ +#define PL_STATUS_A 0x10 /* status register A (read only) */ +#define PL_STATUS_B 0x11 /* status register B (read only) */ +#define PL_TPC 0x12 /* timer for PCM (ro) [20.48 us] */ +#define PL_TNE 0x13 /* time of noise event [0.32 us] */ +#define PL_CLK_DIV 0x14 /* TNE clock divider (read only) */ +#define PL_BIST_SIGNAT 0x15 /* built in self test signature (ro)*/ +#define PL_RCV_VECTOR 0x16 /* receive vector reg. (read only) */ +#define PL_INTR_EVENT 0x17 /* interrupt event reg. (read only) */ +#define PL_VIOL_SYM_CTR 0x18 /* violation symbol count. (read o) */ +#define PL_MIN_IDLE_CTR 0x19 /* minimum idle counter (read only) */ +#define PL_LINK_ERR_CTR 0x1a /* link error event ctr.(read only) */ +#ifdef MOT_ELM +#define PL_T_FOT_ASS 0x1e /* FOTOFF Assert Timer */ +#define PL_T_FOT_DEASS 0x1f /* FOTOFF Deassert Timer */ +#endif /* MOT_ELM */ + +#ifdef MOT_ELM +/* + * Special Quad-Elm Registers. + * A Quad-ELM consists of for ELMs and these additional registers. + */ +#define QELM_XBAR_W 0x80 /* Crossbar Control ELM W */ +#define QELM_XBAR_X 0x81 /* Crossbar Control ELM X */ +#define QELM_XBAR_Y 0x82 /* Crossbar Control ELM Y */ +#define QELM_XBAR_Z 0x83 /* Crossbar Control ELM Z */ +#define QELM_XBAR_P 0x84 /* Crossbar Control Bus P */ +#define QELM_XBAR_S 0x85 /* Crossbar Control Bus S */ +#define QELM_XBAR_R 0x86 /* Crossbar Control Bus R */ +#define QELM_WR_XBAR 0x87 /* Write the Crossbar now (write) */ +#define QELM_CTR_W 0x88 /* Counter W */ +#define QELM_CTR_X 0x89 /* Counter X */ +#define QELM_CTR_Y 0x8a /* Counter Y */ +#define QELM_CTR_Z 0x8b /* Counter Z */ +#define QELM_INT_MASK 0x8c /* Interrupt mask register */ +#define QELM_INT_DATA 0x8d /* Interrupt data (event) register */ +#define QELM_ELMB 0x00 /* Elm base */ +#define QELM_ELM_SIZE 0x20 /* ELM size */ +#endif /* MOT_ELM */ +/* + * PLC control register A (PL_CNTRL_A: log. addr. 0x00) + * It is used for timer configuration, specification of PCM MAINT state option, + * counter interrupt frequency, PLC data path config. and Built In Self Test. + */ +#define PL_RUN_BIST 0x0001 /* begin running its Built In Self T.*/ +#define PL_RF_DISABLE 0x0002 /* disable the Repeat Filter state m.*/ +#define PL_SC_REM_LOOP 0x0004 /* remote loopback path */ +#define PL_SC_BYPASS 0x0008 /* by providing a physical bypass */ +#define PL_LM_LOC_LOOP 0x0010 /* loop path just after elastic buff.*/ +#define PL_EB_LOC_LOOP 0x0020 /* loop path just prior to PDT/PDR IF*/ +#define PL_FOT_OFF 0x0040 /* assertion of /FOTOFF pin of PLC */ +#define PL_LOOPBACK 0x0080 /* it cause the /LPBCK pin ass. low */ +#define PL_MINI_CTR_INT 0x0100 /* partially contr. when bit is ass. */ +#define PL_VSYM_CTR_INT 0x0200 /* controls when int bit is asserted */ +#define PL_ENA_PAR_CHK 0x0400 /* enable parity check */ +#define PL_REQ_SCRUB 0x0800 /* limited access to scrub capability*/ +#define PL_TPC_16BIT 0x1000 /* causes the TPC as a 16 bit timer */ +#define PL_TNE_16BIT 0x2000 /* causes the TNE as a 16 bit timer */ +#define PL_NOISE_TIMER 0x4000 /* allows the noise timing function */ + +/* + * PLC control register B (PL_CNTRL_B: log. addr. 0x01) + * It contains signals and requeste to direct the process of PCM and it is also + * used to control the Line State Match interrupt. + */ +#define PL_PCM_CNTRL 0x0003 /* control PCM state machine */ +#define PL_PCM_NAF (0) /* state is not affected */ +#define PL_PCM_START (1) /* goes to the BREAK state */ +#define PL_PCM_TRACE (2) /* goes to the TRACE state */ +#define PL_PCM_STOP (3) /* goes to the OFF state */ + +#define PL_MAINT 0x0004 /* if OFF state --> MAINT state */ +#define PL_LONG 0x0008 /* perf. a long Link Confid.Test(LCT)*/ +#define PL_PC_JOIN 0x0010 /* if NEXT state --> JOIN state */ + +#define PL_PC_LOOP 0x0060 /* loopback used in the LCT */ +#define PL_NOLCT (0<<5) /* no LCT is performed */ +#define PL_TPDR (1<<5) /* PCM asserts transmit PDR */ +#define PL_TIDLE (2<<5) /* PCM asserts transmit idle */ +#define PL_RLBP (3<<5) /* trans. PDR & remote loopb. path */ + +#define PL_CLASS_S 0x0080 /* signif. that single att. station */ + +#define PL_MAINT_LS 0x0700 /* line state while in the MAINT st. */ +#define PL_M_QUI0 (0<<8) /* transmit QUIET line state */ +#define PL_M_IDLE (1<<8) /* transmit IDLE line state */ +#define PL_M_HALT (2<<8) /* transmit HALT line state */ +#define PL_M_MASTR (3<<8) /* transmit MASTER line state */ +#define PL_M_QUI1 (4<<8) /* transmit QUIET line state */ +#define PL_M_QUI2 (5<<8) /* transmit QUIET line state */ +#define PL_M_TPDR (6<<8) /* tr. PHY_DATA requ.-symbol is tr.ed*/ +#define PL_M_QUI3 (7<<8) /* transmit QUIET line state */ + +#define PL_MATCH_LS 0x7800 /* line state to be comp. with curr.*/ +#define PL_I_ANY (0<<11) /* Int. on any change in *_LINE_ST */ +#define PL_I_IDLE (1<<11) /* Interrupt on IDLE line state */ +#define PL_I_HALT (2<<11) /* Interrupt on HALT line state */ +#define PL_I_MASTR (4<<11) /* Interrupt on MASTER line state */ +#define PL_I_QUIET (8<<11) /* Interrupt on QUIET line state */ + +#define PL_CONFIG_CNTRL 0x8000 /* control over scrub, byp. & loopb.*/ + +/* + * PLC control register C (PL_CNTRL_C: log. addr. 0x0a) + * It contains the scrambling control registers (PLC-S only) + */ +#define PL_C_CIPHER_ENABLE (1<<0) /* enable scrambler */ +#define PL_C_CIPHER_LPBCK (1<<1) /* loopback scrambler */ +#define PL_C_SDOFF_ENABLE (1<<6) /* enable SDOFF timer */ +#define PL_C_SDON_ENABLE (1<<7) /* enable SDON timer */ +#ifdef MOT_ELM +#define PL_C_FOTOFF_CTRL (3<<2) /* FOTOFF timer control */ +#define PL_C_FOTOFF_TIM (0<<2) /* FOTOFF use timer for (de)-assert */ +#define PL_C_FOTOFF_INA (2<<2) /* FOTOFF forced inactive */ +#define PL_C_FOTOFF_ACT (3<<2) /* FOTOFF forced active */ +#define PL_C_FOTOFF_SRCE (1<<4) /* FOTOFF source is PCM state != OFF */ +#define PL_C_RXDATA_EN (1<<5) /* Rec scr data forced to 0 */ +#define PL_C_SDNRZEN (1<<8) /* Monitor rec descr. data for act */ +#else /* nMOT_ELM */ +#define PL_C_FOTOFF_CTRL (3<<8) /* FOTOFF timer control */ +#define PL_C_FOTOFF_0 (0<<8) /* timer off */ +#define PL_C_FOTOFF_30 (1<<8) /* 30uS */ +#define PL_C_FOTOFF_50 (2<<8) /* 50uS */ +#define PL_C_FOTOFF_NEVER (3<<8) /* never */ +#define PL_C_SDON_TIMER (3<<10) /* SDON timer control */ +#define PL_C_SDON_084 (0<<10) /* 0.84 uS */ +#define PL_C_SDON_132 (1<<10) /* 1.32 uS */ +#define PL_C_SDON_252 (2<<10) /* 2.52 uS */ +#define PL_C_SDON_512 (3<<10) /* 5.12 uS */ +#define PL_C_SOFF_TIMER (3<<12) /* SDOFF timer control */ +#define PL_C_SOFF_076 (0<<12) /* 0.76 uS */ +#define PL_C_SOFF_132 (1<<12) /* 1.32 uS */ +#define PL_C_SOFF_252 (2<<12) /* 2.52 uS */ +#define PL_C_SOFF_512 (3<<12) /* 5.12 uS */ +#define PL_C_TSEL (3<<14) /* scrambler path select */ +#endif /* nMOT_ELM */ + +/* + * PLC status register A (PL_STATUS_A: log. addr. 0x10) + * It is used to report status information to the Node Processor about the + * Line State Machine (LSM). + */ +#ifdef MOT_ELM +#define PLC_INT_MASK 0xc000 /* ELM integration bits in status A */ +#define PLC_INT_C 0x0000 /* ELM Revision Band C */ +#define PLC_INT_CAMEL 0x4000 /* ELM integrated into CAMEL */ +#define PLC_INT_QE 0x8000 /* ELM integrated into Quad ELM */ +#define PLC_REV_MASK 0x3800 /* revision bits in status A */ +#define PLC_REVISION_B 0x0000 /* rev bits for ELM Rev B */ +#define PLC_REVISION_QA 0x0800 /* rev bits for ELM core in QELM-A */ +#else /* nMOT_ELM */ +#define PLC_REV_MASK 0xf800 /* revision bits in status A */ +#define PLC_REVISION_A 0x0000 /* revision bits for PLC */ +#define PLC_REVISION_S 0xf800 /* revision bits for PLC-S */ +#define PLC_REV_SN3 0x7800 /* revision bits for PLC-S in IFCP */ +#endif /* nMOT_ELM */ +#define PL_SYM_PR_CTR 0x0007 /* contains the LSM symbol pair Ctr. */ +#define PL_UNKN_LINE_ST 0x0008 /* unknown line state bit from LSM */ +#define PL_LSM_STATE 0x0010 /* state bit of LSM */ + +#define PL_LINE_ST 0x00e0 /* contains recogn. line state of LSM*/ +#define PL_L_NLS (0<<5) /* noise line state */ +#define PL_L_ALS (1<<5) /* activ line state */ +#define PL_L_UND (2<<5) /* undefined */ +#define PL_L_ILS4 (3<<5) /* idle l. s. (after 4 idle symbols) */ +#define PL_L_QLS (4<<5) /* quiet line state */ +#define PL_L_MLS (5<<5) /* master line state */ +#define PL_L_HLS (6<<5) /* halt line state */ +#define PL_L_ILS16 (7<<5) /* idle line state (after 16 idle s.)*/ + +#define PL_PREV_LINE_ST 0x0300 /* value of previous line state */ +#define PL_P_QLS (0<<8) /* quiet line state */ +#define PL_P_MLS (1<<8) /* master line state */ +#define PL_P_HLS (2<<8) /* halt line state */ +#define PL_P_ILS16 (3<<8) /* idle line state (after 16 idle s.)*/ + +#define PL_SIGNAL_DET 0x0400 /* 1=that signal detect is deasserted*/ + + +/* + * PLC status register B (PL_STATUS_B: log. addr. 0x11) + * It contains signals and status from the repeat filter and PCM state machine. + */ +#define PL_BREAK_REASON 0x0007 /* reason for PCM state mach.s to br.*/ +#define PL_B_NOT (0) /* PCM SM has not gone to BREAK state*/ +#define PL_B_PCS (1) /* PC_Start issued */ +#define PL_B_TPC (2) /* TPC timer expired after T_OUT */ +#define PL_B_TNE (3) /* TNE timer expired after NS_MAX */ +#define PL_B_QLS (4) /* quit line state detected */ +#define PL_B_ILS (5) /* idle line state detected */ +#define PL_B_HLS (6) /* halt line state detected */ + +#define PL_TCF 0x0008 /* transmit code flag (start exec.) */ +#define PL_RCF 0x0010 /* receive code flag (start exec.) */ +#define PL_LSF 0x0020 /* line state flag (l.s. has been r.)*/ +#define PL_PCM_SIGNAL 0x0040 /* indic. that XMIT_VECTOR hb.written*/ + +#define PL_PCM_STATE 0x0780 /* state bits of PCM state machine */ +#define PL_PC0 (0<<7) /* OFF - when /RST or PCM_CNTRL */ +#define PL_PC1 (1<<7) /* BREAK - entry point in start PCM*/ +#define PL_PC2 (2<<7) /* TRACE - to localize stuck Beacon*/ +#define PL_PC3 (3<<7) /* CONNECT - synchronize ends of conn*/ +#define PL_PC4 (4<<7) /* NEXT - to seperate the signalng*/ +#define PL_PC5 (5<<7) /* SIGNAL - PCM trans/rec. bit infos*/ +#define PL_PC6 (6<<7) /* JOIN - 1. state to activ conn. */ +#define PL_PC7 (7<<7) /* VERIFY - 2. - " - (3. ACTIVE) */ +#define PL_PC8 (8<<7) /* ACTIVE - PHY has been incorporated*/ +#define PL_PC9 (9<<7) /* MAINT - for test purposes or so + that PCM op. completely in softw. */ + +#define PL_PCI_SCRUB 0x0800 /* scrubbing function is being exec. */ + +#define PL_PCI_STATE 0x3000 /* Physical Connect. Insertion SM */ +#define PL_CI_REMV (0<<12) /* REMOVED */ +#define PL_CI_ISCR (1<<12) /* INSERT_SCRUB */ +#define PL_CI_RSCR (2<<12) /* REMOVE_SCRUB */ +#define PL_CI_INS (3<<12) /* INSERTED */ + +#define PL_RF_STATE 0xc000 /* state bit of repeate filter SM */ +#define PL_RF_REPT (0<<14) /* REPEAT */ +#define PL_RF_IDLE (1<<14) /* IDLE */ +#define PL_RF_HALT1 (2<<14) /* HALT1 */ +#define PL_RF_HALT2 (3<<14) /* HALT2 */ + + +/* + * PLC interrupt event register (PL_INTR_EVENT: log. addr. 0x17) + * It is read only and is clearde whenever it is read! + * It is used by the PLC to report events to the node processor. + */ +#define PL_PARITY_ERR 0x0001 /* p. error h.b.detected on TX9-0 inp*/ +#define PL_LS_MATCH 0x0002 /* l.s.== l.s. PLC_CNTRL_B's MATCH_LS*/ +#define PL_PCM_CODE 0x0004 /* transmit&receive | LCT complete */ +#define PL_TRACE_PROP 0x0008 /* master l.s. while PCM ACTIV|TRACE */ +#define PL_SELF_TEST 0x0010 /* QUIET|HALT while PCM in TRACE st. */ +#define PL_PCM_BREAK 0x0020 /* PCM has entered the BREAK state */ +#define PL_PCM_ENABLED 0x0040 /* asserted SC_JOIN, scrub. & ACTIV */ +#define PL_TPC_EXPIRED 0x0080 /* TPC timer reached zero */ +#define PL_TNE_EXPIRED 0x0100 /* TNE timer reached zero */ +#define PL_EBUF_ERR 0x0200 /* elastic buff. det. over-|underflow*/ +#define PL_PHYINV 0x0400 /* physical layer invalid signal */ +#define PL_VSYM_CTR 0x0800 /* violation symbol counter has incr.*/ +#define PL_MINI_CTR 0x1000 /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/ +#define PL_LE_CTR 0x2000 /* link error event counter */ +#define PL_LSDO 0x4000 /* SDO input pin changed to a 1 */ +#define PL_NP_ERR 0x8000 /* NP has requested to r/w an inv. r.*/ + +/* + * The PLC interrupt mask register (PL_INTR_MASK: log. addr. 0x02) constr. is + * equal PL_INTR_EVENT register. + * For each set bit, the setting of corresponding bit generate an int to NP. + */ + +#ifdef MOT_ELM +/* + * Quad ELM Crosbar Control register values (QELM_XBAR_?) + */ +#define QELM_XOUT_IDLE 0x0000 /* Idles/Passthrough */ +#define QELM_XOUT_P 0x0001 /* Output to: Bus P */ +#define QELM_XOUT_S 0x0002 /* Output to: Bus S */ +#define QELM_XOUT_R 0x0003 /* Output to: Bus R */ +#define QELM_XOUT_W 0x0004 /* Output to: ELM W */ +#define QELM_XOUT_X 0x0005 /* Output to: ELM X */ +#define QELM_XOUT_Y 0x0006 /* Output to: ELM Y */ +#define QELM_XOUT_Z 0x0007 /* Output to: ELM Z */ + +/* + * Quad ELM Interrupt data and event registers. + */ +#define QELM_NP_ERR (1<<15) /* Node Processor Error */ +#define QELM_COUNT_Z (1<<7) /* Counter Z Interrupt */ +#define QELM_COUNT_Y (1<<6) /* Counter Y Interrupt */ +#define QELM_COUNT_X (1<<5) /* Counter X Interrupt */ +#define QELM_COUNT_W (1<<4) /* Counter W Interrupt */ +#define QELM_ELM_Z (1<<3) /* ELM Z Interrupt */ +#define QELM_ELM_Y (1<<2) /* ELM Y Interrupt */ +#define QELM_ELM_X (1<<1) /* ELM X Interrupt */ +#define QELM_ELM_W (1<<0) /* ELM W Interrupt */ +#endif /* MOT_ELM */ +/* + * PLC Timing Parameters + */ +#define TP_C_MIN 0xff9c /* 2 ms */ +#define TP_TL_MIN 0xfff0 /* 0.3 ms */ +#define TP_TB_MIN 0xff10 /* 5 ms */ +#define TP_T_OUT 0xd9db /* 200 ms */ +#define TP_LC_LENGTH 0xf676 /* 50 ms */ +#define TP_LC_LONGLN 0xa0a2 /* 500 ms */ +#define TP_T_SCRUB 0xff6d /* 3.5 ms */ +#define TP_NS_MAX 0xf021 /* 1.3 ms */ + +/* + * BIST values + */ +#define PLC_BIST 0x6ecd /* BIST signature for PLC */ +#define PLCS_BIST 0x5b6b /* BIST signature for PLC-S */ +#define PLC_ELM_B_BIST 0x6ecd /* BIST signature of ELM Rev. B */ +#define PLC_ELM_D_BIST 0x5b6b /* BIST signature of ELM Rev. D */ +#define PLC_CAM_A_BIST 0x9e75 /* BIST signature of CAMEL Rev. A */ +#define PLC_CAM_B_BIST 0x5b6b /* BIST signature of CAMEL Rev. B */ +#define PLC_IFD_A_BIST 0x9e75 /* BIST signature of IFDDI Rev. A */ +#define PLC_IFD_B_BIST 0x5b6b /* BIST signature of IFDDI Rev. B */ +#define PLC_QELM_A_BIST 0x5b6b /* BIST signature of QELM Rev. A */ + +/* + FDDI board recources + */ + +/* + * request register array (log. addr: RQA_A + a<<1 {a=0..7}) write only. + * It specifies to FORMAC+ the type of buffer memory access the host requires. + */ +#define RQ_NOT 0 /* not request */ +#define RQ_RES 1 /* reserved */ +#define RQ_SFW 2 /* special frame write */ +#define RQ_RRQ 3 /* read request: receive queue */ +#define RQ_WSQ 4 /* write request: synchronous queue */ +#define RQ_WA0 5 /* write requ.: asynchronous queue 0 */ +#define RQ_WA1 6 /* write requ.: asynchronous queue 1 */ +#define RQ_WA2 7 /* write requ.: asynchronous queue 2 */ + +#define SZ_LONG (sizeof(long)) + +/* + * FDDI defaults + * NOTE : In the ANSI docs, times are specified in units of "symbol time". + * AMD chips use BCLK as unit. 1 BCKL == 2 symbols + */ +#define COMPLREF ((u_long)32*256*256) /* two's complement 21 bit */ +#define MSTOBCLK(x) ((u_long)(x)*12500L) +#define MSTOTVX(x) (((u_long)(x)*1000L)/80/255) + +#endif /* _SUPERNET_ */ diff --git a/drivers/net/skfp/h/targethw.h b/drivers/net/skfp/h/targethw.h new file mode 100644 index 000000000000..5e6f3a050cea --- /dev/null +++ b/drivers/net/skfp/h/targethw.h @@ -0,0 +1,173 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef _TARGETHW_ +#define _TARGETHW_ + + /* + * PCI Watermark definition + */ +#ifdef PCI +#define RX_WATERMARK 24 +#define TX_WATERMARK 24 +#define SK_ML_ID_1 0x20 +#define SK_ML_ID_2 0x30 +#endif + +#include "h/skfbi.h" +#ifndef TAG_MODE +#include "h/fplus.h" +#else +#include "h/fplustm.h" +#endif + +#ifndef HW_PTR +#ifdef MEM_MAPPED_IO +#define HW_PTR u_long +#else +#define HW_PTR u_short +#endif +#endif + +#ifdef MULT_OEM +#define OI_STAT_LAST 0 /* end of OEM data base */ +#define OI_STAT_PRESENT 1 /* entry present but not empty */ +#define OI_STAT_VALID 2 /* holds valid ID, but is not active */ +#define OI_STAT_ACTIVE 3 /* holds valid ID, entry is active */ + /* active = adapter is supported */ + +/* Memory representation of IDs must match representation in adapter. */ +struct s_oem_ids { + u_char oi_status ; /* Stat: last, present, valid, active */ + u_char oi_mark[5] ; /* "PID00" .. "PID07" .. */ + u_char oi_id[4] ; /* id bytes, representation as */ + /* defined by hardware, */ +#ifdef PCI + u_char oi_sub_id[4] ; /* sub id bytes, representation as */ + /* defined by hardware, */ +#endif +#ifdef ISA + u_char oi_logo_len ; /* the length of the adapter logo */ + u_char oi_logo[6] ; /* the adapter logo */ + u_char oi_reserved1 ; +#endif /* ISA */ +} ; +#endif /* MULT_OEM */ + + +struct s_smt_hw { + /* + * global + */ + HW_PTR iop ; /* IO base address */ + short dma ; /* DMA channel */ + short irq ; /* IRQ level */ + short eprom ; /* FLASH prom */ +#ifndef PCI + short DmaWriteExtraBytes ; /* add bytes for DMA write */ +#endif + +#ifndef SYNC + u_short n_a_send ; /* pending send requests */ +#endif + +#if (defined(EISA) || defined(MCA) || defined(PCI)) + short slot ; /* slot number */ + short max_slots ; /* maximum number of slots */ +#endif + +#if (defined(PCI) || defined(MCA)) + short wdog_used ; /* TRUE if the watch dog is used */ +#endif + +#ifdef MCA + short slot_32 ; /* 32bit slot (1) or 16bit slot (0) */ + short rev ; /* Board revision (FMx_REV). */ + short VFullRead ; /* V_full value for DMA read */ + short VFullWrite ; /* V_full value for DMA write */ +#endif + +#ifdef EISA + short led ; /* LED for FE card */ + + short dma_rmode ; /* read mode */ + short dma_wmode ; /* write mode */ + short dma_emode ; /* extend mode */ + + /* DMA controller channel dependent io addresses */ + u_short dma_base_word_count ; + u_short dma_base_address ; + u_short dma_base_address_page ; +#endif + +#ifdef PCI + u_short pci_handle ; /* handle to access the BIOS func */ + u_long is_imask ; /* int maske for the int source reg */ + u_long phys_mem_addr ; /* physical memory address */ + u_short mc_dummy ; /* work around for MC compiler bug */ + /* + * state of the hardware + */ + u_short hw_state ; /* started or stopped */ + +#define STARTED 1 +#define STOPPED 0 + + int hw_is_64bit ; /* does we have a 64 bit adapter */ +#endif + +#ifdef TAG_MODE + u_long pci_fix_value ; /* value parsed by PCIFIX */ +#endif + + /* + * hwt.c + */ + u_long t_start ; /* HWT start */ + u_long t_stop ; /* HWT stop */ + u_short timer_activ ; /* HWT timer active */ + + /* + * PIC + */ + u_char pic_a1 ; + u_char pic_21 ; + + /* + * GENERIC ; do not modify beyond this line + */ + + /* + * physical and canonical address + */ + struct fddi_addr fddi_home_addr ; + struct fddi_addr fddi_canon_addr ; + struct fddi_addr fddi_phys_addr ; + + /* + * mac variables + */ + struct mac_parameter mac_pa ; /* tmin, tmax, tvx, treq .. */ + struct mac_counter mac_ct ; /* recv., lost, error */ + u_short mac_ring_is_up ; /* ring is up flag */ + + struct s_smt_fp fp ; /* formac+ */ + +#ifdef MULT_OEM + struct s_oem_ids *oem_id ; /* pointer to selected id */ + int oem_min_status ; /* IDs to take care of */ +#endif /* MULT_OEM */ + +} ; +#endif diff --git a/drivers/net/skfp/h/targetos.h b/drivers/net/skfp/h/targetos.h new file mode 100644 index 000000000000..15b41cdd6eff --- /dev/null +++ b/drivers/net/skfp/h/targetos.h @@ -0,0 +1,164 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * Operating system specific definitions for driver and + * hardware module. + */ + +#ifndef TARGETOS_H +#define TARGETOS_H + + +//-------- those should go into include/linux/pci.h +#define PCI_VENDOR_ID_SK 0x1148 +#define PCI_DEVICE_ID_SK_FP 0x4000 +//-------- + + + +//-------- those should go into include/linux/if_fddi.h +#define FDDI_MAC_HDR_LEN 13 + +#define FDDI_RII 0x01 /* routing information bit */ +#define FDDI_RCF_DIR_BIT 0x80 +#define FDDI_RCF_LEN_MASK 0x1f +#define FDDI_RCF_BROADCAST 0x8000 +#define FDDI_RCF_LIMITED_BROADCAST 0xA000 +#define FDDI_RCF_FRAME2K 0x20 +#define FDDI_RCF_FRAME4K 0x30 +//-------- + + +#undef ADDR + +#include +#include +#include +#include +#include +#include +#include + +// is redefined by linux, but we need our definition +#undef ADDR +#ifdef MEM_MAPPED_IO +#define ADDR(a) (char far *) smc->hw.iop+(a) +#else +#define ADDR(a) (((a)>>7) ? (outp(smc->hw.iop+B0_RAP,(a)>>7), (smc->hw.iop+( ((a)&0x7F) | ((a)>>7 ? 0x80:0)) )) : (smc->hw.iop+(((a)&0x7F)|((a)>>7 ? 0x80:0)))) +#endif + +#include "h/hwmtm.h" + +#define TRUE 1 +#define FALSE 0 + +// HWM Definitions +// ----------------------- +#define FDDI_TRACE(string, arg1, arg2, arg3) // Performance analysis. +#ifdef PCI +#define NDD_TRACE(string, arg1, arg2, arg3) // Performance analysis. +#endif // PCI +#define SMT_PAGESIZE PAGE_SIZE // Size of a memory page (power of 2). +// ----------------------- + + +// SMT Definitions +// ----------------------- +#define TICKS_PER_SECOND HZ +#define SMC_VERSION 1 +// ----------------------- + + +// OS-Driver Definitions +// ----------------------- +#define NO_ADDRESS 0xffe0 /* No Device (I/O) Address */ +#define SKFP_MAX_NUM_BOARDS 8 /* maximum number of PCI boards */ + +#define SK_BUS_TYPE_PCI 0 +#define SK_BUS_TYPE_EISA 1 + +#define FP_IO_LEN 256 /* length of IO area used */ + +#define u8 unsigned char +#define u16 unsigned short +#define u32 unsigned int + +#define MAX_TX_QUEUE_LEN 20 // number of packets queued by driver +#define MAX_FRAME_SIZE 4550 + +#define RX_LOW_WATERMARK NUM_RECEIVE_BUFFERS / 2 +#define TX_LOW_WATERMARK NUM_TRANSMIT_BUFFERS - 2 + +/* +** Include the IOCTL stuff +*/ +#include + +#define SKFPIOCTL SIOCDEVPRIVATE + +struct s_skfp_ioctl { + unsigned short cmd; /* Command to run */ + unsigned short len; /* Length of the data buffer */ + unsigned char *data; /* Pointer to the data buffer */ +}; + +/* +** Recognised ioctl commands for the driver +*/ +#define SKFP_GET_STATS 0x05 /* Get the driver statistics */ +#define SKFP_CLR_STATS 0x06 /* Zero out the driver statistics */ + +// The per-adapter driver structure +struct s_smt_os { + struct device *dev; + struct device *next_module; + u32 bus_type;/* bus type (0 == PCI, 1 == EISA) */ + struct pci_dev pdev; /* PCI device structure */ + + unsigned long base_addr; + unsigned char factory_mac_addr[8]; + ulong SharedMemSize; + ulong SharedMemHeap; + void* SharedMemAddr; + + ulong QueueSkb; + struct sk_buff_head SendSkbQueue; + + ulong MaxFrameSize; + u8 ResetRequested; + + // MAC statistics structure + struct fddi_statistics MacStat; + + // receive into this local buffer if no skb available + // data will be not valid, because multiple RxDs can + // point here at the same time, it must be at least + // MAX_FRAME_SIZE bytes in size + unsigned char *LocalRxBuffer; + + // Version (required by SMT module). + u_long smc_version ; + + // Required by Hardware Module (HWM). + struct hw_modul hwm ; + + // For SMP-savety + spinlock_t DriverLock; + +}; + +typedef struct s_smt_os skfddi_priv; + +#endif // _TARGETOS_ diff --git a/drivers/net/skfp/h/types.h b/drivers/net/skfp/h/types.h new file mode 100644 index 000000000000..43b48ff3ba2f --- /dev/null +++ b/drivers/net/skfp/h/types.h @@ -0,0 +1,48 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#include +/* + ---------------------- + Basic SMT system types + ---------------------- +*/ +#ifndef _TYPES_ +#define _TYPES_ + +#define _packed +#ifndef far +#define far +#endif +#ifndef _far +#define _far +#endif + +#ifndef MEM_MAPPED_IO // "normal" IO +#define inp(p) inb(p) +#define inpw(p) inw(p) +#define inpd(p) inl(p) +#define outp(p,c) outb(c,p) +#define outpw(p,s) outw(s,p) +#define outpd(p,l) outl(l,p) +#else // memory mapped io +#define inp(a) readb(a) +#define inpw(a) readw(a) +#define inpd(a) readl(a) +#define outp(a,v) writeb(v, a) +#define outpw(a,v) writew(v, a) +#define outpd(a,v) writel(v, a) +#endif + +#endif /* _TYPES_ */ diff --git a/drivers/net/skfp/hwmtm.c b/drivers/net/skfp/hwmtm.c new file mode 100644 index 000000000000..ece539646180 --- /dev/null +++ b/drivers/net/skfp/hwmtm.c @@ -0,0 +1,2241 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#ifndef lint +static char const ID_sccs[] = "@(#)hwmtm.c 1.40 99/05/31 (C) SK" ; +#endif + +#define HWMTM + +#ifndef FDDI +#define FDDI +#endif + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/supern_2.h" +#include "h/skfbiinc.h" + +/* + ------------------------------------------------------------- + DOCUMENTATION + ------------------------------------------------------------- + BEGIN_MANUAL_ENTRY(DOCUMENTATION) + + T B D + + END_MANUAL_ENTRY +*/ +/* + ------------------------------------------------------------- + LOCAL VARIABLES: + ------------------------------------------------------------- +*/ +#ifdef COMMON_MB_POOL +static SMbuf *mb_start = 0 ; +static SMbuf *mb_free = 0 ; +static int mb_init = FALSE ; +static int call_count = 0 ; +#endif + +/* + ------------------------------------------------------------- + EXTERNE VARIABLES: + ------------------------------------------------------------- +*/ + +#ifdef DEBUG +#ifndef DEBUG_BRD +extern struct smt_debug debug ; +#endif +#endif + +#ifdef NDIS_OS2 +extern u_char offDepth ; +extern u_char force_irq_pending ; +#endif + +/* + ------------------------------------------------------------- + LOCAL FUNCTIONS: + ------------------------------------------------------------- +*/ + +static void queue_llc_rx(), smt_to_llc(), + init_txd_ring(), init_rxd_ring(), + queue_txd_mb() ; + +static u_long init_descr_ring(), repair_txd_ring(), + repair_rxd_ring() ; + +static SMbuf *get_llc_rx(), *get_txd_mb() ; + + +/* + ------------------------------------------------------------- + EXTERNAL FUNCTIONS: + ------------------------------------------------------------- +*/ +/* The external SMT functions are listed in cmtdef.h */ + +extern void *mac_drv_get_space(), *mac_drv_get_desc_mem(), + init_board(), mac_drv_fill_rxd(), + plc1_irq(), mac_drv_tx_complete(), + plc2_irq(), mac1_irq(), + mac2_irq(), mac3_irq(), + timer_irq(), mac_drv_rx_complete(), + mac_drv_requeue_rxd(), init_plc(), + mac_drv_clear_rxd(), llc_restart_tx(), + ev_dispatcher(), smt_force_irq() ; + +#ifdef USE_OS_CPY +extern void hwm_cpy_rxd2mb(), hwm_cpy_txd2mb() ; +#endif +#ifdef ALL_RX_COMPLETE +extern void mac_drv_all_receives_complete() ; +#endif + +extern u_long mac_drv_virt2phys(), dma_master() ; + +#ifdef NDIS_OS2 +extern void post_proc() ; +#else +extern void dma_complete() ; +#endif + +extern int init_fplus(), mac_drv_rx_init() ; + +/* + ------------------------------------------------------------- + PUBLIC FUNCTIONS: + ------------------------------------------------------------- +*/ + void process_receive(), smt_send_mbuf(), + fddi_isr(), mac_drv_clear_txd(), + smt_free_mbuf(), init_driver_fplus(), + mac_drv_rx_mode(), init_fddi_driver(), + mac_drv_clear_tx_queue(), + mac_drv_clear_rx_queue(), + hwm_tx_frag(), hwm_rx_frag() ; + + int mac_drv_rx_frag(), mac_drv_init(), + hwm_tx_init() ; + + u_int mac_drv_check_space() ; + + SMbuf *smt_get_mbuf() ; + +#ifdef DEBUG + void mac_drv_debug_lev() ; +#endif + +/* + ------------------------------------------------------------- + MACROS: + ------------------------------------------------------------- +*/ +#ifndef UNUSED +#ifdef lint +#define UNUSED(x) (x) = (x) +#else +#define UNUSED(x) +#endif +#endif + +#ifdef USE_CAN_ADDR +#define MA smc->hw.fddi_canon_addr.a +#define GROUP_ADDR_BIT 0x01 +#else +#define MA smc->hw.fddi_home_addr.a +#define GROUP_ADDR_BIT 0x80 +#endif + +#define RXD_TXD_COUNT (HWM_ASYNC_TXD_COUNT+HWM_SYNC_TXD_COUNT+\ + SMT_R1_RXD_COUNT+SMT_R2_RXD_COUNT) + +#ifdef MB_OUTSIDE_SMC +#define EXT_VIRT_MEM ((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd) +\ + MAX_MBUF*sizeof(SMbuf)) +#define EXT_VIRT_MEM_2 ((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd)) +#else +#define EXT_VIRT_MEM ((RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd)) +#endif + + /* + * define critical read for 16 Bit drivers + */ +#if defined(NDIS_OS2) || defined(ODI2) +#define CR_READ(var) ((var) & 0xffff0000 | ((var) & 0xffff)) +#else +#define CR_READ(var) (u_long)(var) +#endif + +#define IMASK_SLOW (IS_PLINT1 | IS_PLINT2 | IS_TIMINT | IS_TOKEN | \ + IS_MINTR1 | IS_MINTR2 | IS_MINTR3 | IS_R1_P | \ + IS_R1_C | IS_XA_C | IS_XS_C) + +/* + ------------------------------------------------------------- + INIT- AND SMT FUNCTIONS: + ------------------------------------------------------------- +*/ + + +/* + * BEGIN_MANUAL_ENTRY(mac_drv_check_space) + * u_int mac_drv_check_space() + * + * function DOWNCALL (drvsr.c) + * This function calculates the needed non virtual + * memory for MBufs, RxD and TxD descriptors etc. + * needed by the driver. + * + * return u_int memory in bytes + * + * END_MANUAL_ENTRY + */ +u_int mac_drv_check_space() +{ +#ifdef MB_OUTSIDE_SMC +#ifdef COMMON_MB_POOL + call_count++ ; + if (call_count == 1) { + return(EXT_VIRT_MEM) ; + } + else { + return(EXT_VIRT_MEM_2) ; + } +#else + return (EXT_VIRT_MEM) ; +#endif +#else + return (0) ; +#endif +} + +/* + * BEGIN_MANUAL_ENTRY(mac_drv_init) + * void mac_drv_init(smc) + * + * function DOWNCALL (drvsr.c) + * In this function the hardware module allocates it's + * memory. + * The operating system dependent module should call + * mac_drv_init once, after the adatper is detected. + * END_MANUAL_ENTRY + */ +int mac_drv_init(smc) +struct s_smc *smc ; +{ + if (sizeof(struct s_smt_fp_rxd) % 16) { + SMT_PANIC(smc,HWM_E0001,HWM_E0001_MSG) ; + } + if (sizeof(struct s_smt_fp_txd) % 16) { + SMT_PANIC(smc,HWM_E0002,HWM_E0002_MSG) ; + } + + /* + * get the required memory for the RxDs and TxDs + */ + if (!(smc->os.hwm.descr_p = (union s_fp_descr volatile *) + mac_drv_get_desc_mem(smc,(u_int) + (RXD_TXD_COUNT+1)*sizeof(struct s_smt_fp_txd)))) { + return(1) ; /* no space the hwm modul can't work */ + } + + /* + * get the memory for the SMT MBufs + */ +#ifndef MB_OUTSIDE_SMC + smc->os.hwm.mbuf_pool.mb_start=(SMbuf *)(&smc->os.hwm.mbuf_pool.mb[0]) ; +#else +#ifndef COMMON_MB_POOL + if (!(smc->os.hwm.mbuf_pool.mb_start = (SMbuf *) mac_drv_get_space(smc, + MAX_MBUF*sizeof(SMbuf)))) { + return(1) ; /* no space the hwm modul can't work */ + } +#else + if (!mb_start) { + if (!(mb_start = (SMbuf *) mac_drv_get_space(smc, + MAX_MBUF*sizeof(SMbuf)))) { + return(1) ; /* no space the hwm modul can't work */ + } + } +#endif +#endif + return (0) ; +} + +/* + * BEGIN_MANUAL_ENTRY(init_driver_fplus) + * init_driver_fplus(smc) + * + * Sets hardware modul specific values for the mode register 2 + * (e.g. the byte alignment for the received frames, the position of the + * least significant byte etc.) + * END_MANUAL_ENTRY + */ +void init_driver_fplus(smc) +struct s_smc *smc ; +{ + smc->hw.fp.mdr2init = FM_LSB | FM_BMMODE | FM_ENNPRQ | FM_ENHSRQ | 3 ; + +#ifdef PCI + smc->hw.fp.mdr2init |= FM_CHKPAR | FM_PARITY ; +#endif + smc->hw.fp.mdr3init = FM_MENRQAUNLCK | FM_MENRS ; + +#ifdef USE_CAN_ADDR + /* enable address bit swapping */ + smc->hw.fp.frselreg_init = FM_ENXMTADSWAP | FM_ENRCVADSWAP ; +#endif +} + +static u_long init_descr_ring(smc,start,count) +struct s_smc *smc ; +union s_fp_descr volatile *start; +int count ; +{ + int i ; + union s_fp_descr volatile *d1 ; + union s_fp_descr volatile *d2 ; + u_long phys ; + + DB_GEN("descr ring starts at = %x ",(void *)start,0,3) ; + for (i=count-1, d1=start; i ; i--) { + d2 = d1 ; + d1++ ; /* descr is owned by the host */ + d2->r.rxd_rbctrl = AIX_REVERSE(BMU_CHECK) ; + d2->r.rxd_next = &d1->r ; + phys = mac_drv_virt2phys(smc,(void *)d1) ; + d2->r.rxd_nrdadr = AIX_REVERSE(phys) ; + } + DB_GEN("descr ring ends at = %x ",(void *)d1,0,3) ; + d1->r.rxd_rbctrl = AIX_REVERSE(BMU_CHECK) ; + d1->r.rxd_next = &start->r ; + phys = mac_drv_virt2phys(smc,(void *)start) ; + d1->r.rxd_nrdadr = AIX_REVERSE(phys) ; + + for (i=count, d1=start; i ; i--) { + DRV_BUF_FLUSH(&d1->r,DDI_DMA_SYNC_FORDEV) ; + d1++; + } + return(phys) ; +} + +static void init_txd_ring(smc) +struct s_smc *smc ; +{ + struct s_smt_fp_txd volatile *ds ; + struct s_smt_tx_queue *queue ; + u_long phys ; + + /* + * initialize the transmit descriptors + */ + ds = (struct s_smt_fp_txd volatile *) ((char *)smc->os.hwm.descr_p + + SMT_R1_RXD_COUNT*sizeof(struct s_smt_fp_rxd)) ; + queue = smc->hw.fp.tx[QUEUE_A0] ; + DB_GEN("Init async TxD ring, %d TxDs ",HWM_ASYNC_TXD_COUNT,0,3) ; + (void)init_descr_ring(smc,(union s_fp_descr volatile *)ds, + HWM_ASYNC_TXD_COUNT) ; + phys = AIX_REVERSE(ds->txd_ntdadr) ; + ds++ ; + queue->tx_curr_put = queue->tx_curr_get = ds ; + ds-- ; + queue->tx_free = HWM_ASYNC_TXD_COUNT ; + queue->tx_used = 0 ; + outpd(ADDR(B5_XA_DA),phys) ; + + ds = (struct s_smt_fp_txd volatile *) ((char *)ds + + HWM_ASYNC_TXD_COUNT*sizeof(struct s_smt_fp_txd)) ; + queue = smc->hw.fp.tx[QUEUE_S] ; + DB_GEN("Init sync TxD ring, %d TxDs ",HWM_SYNC_TXD_COUNT,0,3) ; + (void)init_descr_ring(smc,(union s_fp_descr volatile *)ds, + HWM_SYNC_TXD_COUNT) ; + phys = AIX_REVERSE(ds->txd_ntdadr) ; + ds++ ; + queue->tx_curr_put = queue->tx_curr_get = ds ; + queue->tx_free = HWM_SYNC_TXD_COUNT ; + queue->tx_used = 0 ; + outpd(ADDR(B5_XS_DA),phys) ; +} + +static void init_rxd_ring(smc) +struct s_smc *smc ; +{ + struct s_smt_fp_rxd volatile *ds ; + struct s_smt_rx_queue *queue ; + u_long phys ; + + /* + * initialize the receive descriptors + */ + ds = (struct s_smt_fp_rxd volatile *) smc->os.hwm.descr_p ; + queue = smc->hw.fp.rx[QUEUE_R1] ; + DB_GEN("Init RxD ring, %d RxDs ",SMT_R1_RXD_COUNT,0,3) ; + (void)init_descr_ring(smc,(union s_fp_descr volatile *)ds, + SMT_R1_RXD_COUNT) ; + phys = AIX_REVERSE(ds->rxd_nrdadr) ; + ds++ ; + queue->rx_curr_put = queue->rx_curr_get = ds ; + queue->rx_free = SMT_R1_RXD_COUNT ; + queue->rx_used = 0 ; + outpd(ADDR(B4_R1_DA),phys) ; +} + +/* + * BEGIN_MANUAL_ENTRY(init_fddi_driver) + * void init_fddi_driver(smc,mac_addr) + * + * initializes the driver and it's variables + * + * END_MANUAL_ENTRY + */ +void init_fddi_driver(smc,mac_addr) +struct s_smc *smc ; +u_char *mac_addr ; /* canonical address */ +{ + SMbuf *mb ; + int i ; + + init_board(smc,mac_addr) ; + (void)init_fplus(smc) ; + + /* + * initialize the SMbufs for the SMT + */ +#ifndef COMMON_MB_POOL + mb = smc->os.hwm.mbuf_pool.mb_start ; + smc->os.hwm.mbuf_pool.mb_free = (SMbuf *)NULL ; + for (i = 0; i < MAX_MBUF; i++) { + mb->sm_use_count = 1 ; + smt_free_mbuf(smc,mb) ; + mb++ ; + } +#else + mb = mb_start ; + if (!mb_init) { + mb_free = 0 ; + for (i = 0; i < MAX_MBUF; i++) { + mb->sm_use_count = 1 ; + smt_free_mbuf(smc,mb) ; + mb++ ; + } + mb_init = TRUE ; + } +#endif + + /* + * initialize the other variables + */ + smc->os.hwm.llc_rx_pipe = smc->os.hwm.llc_rx_tail = (SMbuf *)NULL ; + smc->os.hwm.txd_tx_pipe = smc->os.hwm.txd_tx_tail = NULL ; + smc->os.hwm.pass_SMT = smc->os.hwm.pass_NSA = smc->os.hwm.pass_DB = 0 ; + smc->os.hwm.pass_llc_promisc = TRUE ; + smc->os.hwm.queued_rx_frames = smc->os.hwm.queued_txd_mb = 0 ; + smc->os.hwm.detec_count = 0 ; + smc->os.hwm.rx_break = 0 ; + smc->os.hwm.rx_len_error = 0 ; + smc->os.hwm.isr_flag = FALSE ; + + /* + * make sure that the start pointer is 16 byte aligned + */ + i = 16 - ((long)smc->os.hwm.descr_p & 0xf) ; + if (i != 16) { + DB_GEN("i = %d",i,0,3) ; + smc->os.hwm.descr_p = (union s_fp_descr volatile *) + ((char *)smc->os.hwm.descr_p+i) ; + } + DB_GEN("pt to descr area = %x",(void *)smc->os.hwm.descr_p,0,3) ; + + init_txd_ring(smc) ; + init_rxd_ring(smc) ; + mac_drv_fill_rxd(smc) ; + + init_plc(smc) ; +} + + +SMbuf *smt_get_mbuf(smc) +struct s_smc *smc ; +{ + register SMbuf *mb ; + +#ifndef COMMON_MB_POOL + mb = smc->os.hwm.mbuf_pool.mb_free ; +#else + mb = mb_free ; +#endif + if (mb) { +#ifndef COMMON_MB_POOL + smc->os.hwm.mbuf_pool.mb_free = mb->sm_next ; +#else + mb_free = mb->sm_next ; +#endif + mb->sm_off = 8 ; + mb->sm_use_count = 1 ; + } + DB_GEN("get SMbuf: mb = %x",(void *)mb,0,3) ; + return (mb) ; /* May be NULL */ +} + +void smt_free_mbuf(smc, mb) +struct s_smc *smc ; +SMbuf *mb; +{ + + if (mb) { + mb->sm_use_count-- ; + DB_GEN("free_mbuf: sm_use_count = %d",mb->sm_use_count,0,3) ; + /* + * If the use_count is != zero the MBuf is queued + * more than once and must not queued into the + * free MBuf queue + */ + if (!mb->sm_use_count) { + DB_GEN("free SMbuf: mb = %x",(void *)mb,0,3) ; +#ifndef COMMON_MB_POOL + mb->sm_next = smc->os.hwm.mbuf_pool.mb_free ; + smc->os.hwm.mbuf_pool.mb_free = mb ; +#else + mb->sm_next = mb_free ; + mb_free = mb ; +#endif + } + } + else + SMT_PANIC(smc,HWM_E0003,HWM_E0003_MSG) ; +} + + +/* + * BEGIN_MANUAL_ENTRY(mac_drv_repair_descr) + * void mac_drv_repair_descr(smc) + * + * function called from SMT (HWM / hwmtm.c) + * The BMU is idle when this function is called. + * Mac_drv_repair_descr sets up the physical address + * for all receive and transmit queues where the BMU + * should continue. + * It may be that the BMU was reseted during a fragmented + * transfer. In this case there are some fragments which will + * never completed by the BMU. The OWN bit of this fragments + * must be switched to be owned by the host. + * + * Give a start command to the receive BMU. + * Start the transmit BMUs if transmit frames pending. + * + * END_MANUAL_ENTRY + */ +void mac_drv_repair_descr(smc) +struct s_smc *smc ; +{ + u_long phys ; + + if (smc->hw.hw_state != STOPPED) { + SK_BREAK() ; + SMT_PANIC(smc,HWM_E0013,HWM_E0013_MSG) ; + return ; + } + + /* + * repair tx queues: don't start + */ + phys = repair_txd_ring(smc,smc->hw.fp.tx[QUEUE_A0]) ; + outpd(ADDR(B5_XA_DA),phys) ; + if (smc->hw.fp.tx_q[QUEUE_A0].tx_used) { + outpd(ADDR(B0_XA_CSR),CSR_START) ; + } + phys = repair_txd_ring(smc,smc->hw.fp.tx[QUEUE_S]) ; + outpd(ADDR(B5_XS_DA),phys) ; + if (smc->hw.fp.tx_q[QUEUE_S].tx_used) { + outpd(ADDR(B0_XS_CSR),CSR_START) ; + } + + /* + * repair rx queues + */ + phys = repair_rxd_ring(smc,smc->hw.fp.rx[QUEUE_R1]) ; + outpd(ADDR(B4_R1_DA),phys) ; + outpd(ADDR(B0_R1_CSR),CSR_START) ; +} + +static u_long repair_txd_ring(smc,queue) +struct s_smc *smc ; +struct s_smt_tx_queue *queue ; +{ + int i ; + int tx_used ; + u_long phys ; + u_long tbctrl ; + struct s_smt_fp_txd volatile *t ; + + SK_UNUSED(smc) ; + + t = queue->tx_curr_get ; + tx_used = queue->tx_used ; + for (i = tx_used+queue->tx_free-1 ; i ; i-- ) { + t = t->txd_next ; + } + phys = AIX_REVERSE(t->txd_ntdadr) ; + + t = queue->tx_curr_get ; + while (tx_used) { + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORCPU) ; + tbctrl = AIX_REVERSE(t->txd_tbctrl) ; + + if (tbctrl & BMU_OWN) { + if (tbctrl & BMU_STF) { + break ; /* exit the loop */ + } + else { + /* + * repair the descriptor + */ + t->txd_tbctrl &= AIX_REVERSE(~BMU_OWN) ; + } + } + phys = AIX_REVERSE(t->txd_ntdadr) ; + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + t = t->txd_next ; + tx_used-- ; + } + return(phys) ; +} + +/* + * Repairs the receive descriptor ring and returns the physical address + * where the BMU should continue working. + * + * o The physical address where the BMU was stopped has to be + * determined. This is the next RxD after rx_curr_get with an OWN + * bit set. + * o The BMU should start working at beginning of the next frame. + * RxDs with an OWN bit set but with a reset STF bit should be + * skipped and owned by the driver (OWN = 0). + */ +static u_long repair_rxd_ring(smc,queue) +struct s_smc *smc ; +struct s_smt_rx_queue *queue ; +{ + int i ; + int rx_used ; + u_long phys ; + u_long rbctrl ; + struct s_smt_fp_rxd volatile *r ; + + SK_UNUSED(smc) ; + + r = queue->rx_curr_get ; + rx_used = queue->rx_used ; + for (i = SMT_R1_RXD_COUNT-1 ; i ; i-- ) { + r = r->rxd_next ; + } + phys = AIX_REVERSE(r->rxd_nrdadr) ; + + r = queue->rx_curr_get ; + while (rx_used) { + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + rbctrl = AIX_REVERSE(r->rxd_rbctrl) ; + + if (rbctrl & BMU_OWN) { + if (rbctrl & BMU_STF) { + break ; /* exit the loop */ + } + else { + /* + * repair the descriptor + */ + r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ; + } + } + phys = AIX_REVERSE(r->rxd_nrdadr) ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ; + r = r->rxd_next ; + rx_used-- ; + } + return(phys) ; +} + + +/* + ------------------------------------------------------------- + INTERRUPT SERVICE ROUTINE: + ------------------------------------------------------------- +*/ + +/* + * BEGIN_MANUAL_ENTRY(fddi_isr) + * void fddi_isr(smc) + * + * function DOWNCALL (drvsr.c) + * interrupt service routine, handles the interrupt requests + * generated by the FDDI adapter. + * + * NOTE: The operating system dependent module must garantee that the + * interrupts of the adapter are disabled when it calls fddi_isr. + * + * About the USE_BREAK_ISR mechanismn: + * + * The main requirement of this mechanismn is to force an timer IRQ when + * leaving process_receive() with leave_isr set. process_receive() may + * be called at any time from anywhere! + * To be sure we don't miss such event we set 'force_irq' per default. + * We have to force and Timer IRQ if 'smc->os.hwm.leave_isr' AND + * 'force_irq' are set. 'force_irq' may be reset if a receive complete + * IRQ is pending. + * + * END_MANUAL_ENTRY + */ +void fddi_isr(smc) +struct s_smc *smc ; +{ + u_long is ; /* ISR source */ + u_short stu, stl ; + SMbuf *mb ; + +#ifdef USE_BREAK_ISR + int force_irq ; +#endif + +#ifdef ODI2 + if (smc->os.hwm.rx_break) { + mac_drv_fill_rxd(smc) ; + if (smc->hw.fp.rx_q[QUEUE_R1].rx_used > 0) { + smc->os.hwm.rx_break = 0 ; + process_receive(smc) ; + } + else { + smc->os.hwm.detec_count = 0 ; + smt_force_irq(smc) ; + } + } +#endif + smc->os.hwm.isr_flag = TRUE ; + +#ifdef USE_BREAK_ISR + force_irq = TRUE ; + if (smc->os.hwm.leave_isr) { + smc->os.hwm.leave_isr = FALSE ; + process_receive(smc) ; + } +#endif + + while ((is = GET_ISR() & ISR_MASK)) { + NDD_TRACE("CH0B",is,0,0) ; + DB_GEN("ISA = 0x%x",is,0,7) ; + + if (is & IMASK_SLOW) { + NDD_TRACE("CH1b",is,0,0) ; + if (is & IS_PLINT1) { /* PLC1 */ + plc1_irq(smc) ; + } + if (is & IS_PLINT2) { /* PLC2 */ + plc2_irq(smc) ; + } + if (is & IS_MINTR1) { /* FORMAC+ STU1(U/L) */ + stu = inpw(FM_A(FM_ST1U)) ; + stl = inpw(FM_A(FM_ST1L)) ; + DB_GEN("Slow transmit complete",0,0,6) ; + mac1_irq(smc,stu,stl) ; + } + if (is & IS_MINTR2) { /* FORMAC+ STU2(U/L) */ + stu= inpw(FM_A(FM_ST2U)) ; + stl= inpw(FM_A(FM_ST2L)) ; + DB_GEN("Slow receive complete",0,0,6) ; + DB_GEN("stl = %x : stu = %x",stl,stu,7) ; + mac2_irq(smc,stu,stl) ; + } + if (is & IS_MINTR3) { /* FORMAC+ STU3(U/L) */ + stu= inpw(FM_A(FM_ST3U)) ; + stl= inpw(FM_A(FM_ST3L)) ; + DB_GEN("FORMAC Mode Register 3",0,0,6) ; + mac3_irq(smc,stu,stl) ; + } + if (is & IS_TIMINT) { /* Timer 82C54-2 */ + timer_irq(smc) ; +#ifdef NDIS_OS2 + force_irq_pending = 0 ; +#endif + /* + * out of RxD detection + */ + if (++smc->os.hwm.detec_count > 4) { + /* + * check out of RxD condition + */ + process_receive(smc) ; + } + } + if (is & IS_TOKEN) { /* Restricted Token Monitor */ + rtm_irq(smc) ; + } + if (is & IS_R1_P) { /* Parity error rx queue 1 */ + /* clear IRQ */ + outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_P) ; + SMT_PANIC(smc,HWM_E0004,HWM_E0004_MSG) ; + } + if (is & IS_R1_C) { /* Encoding error rx queue 1 */ + /* clear IRQ */ + outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_C) ; + SMT_PANIC(smc,HWM_E0005,HWM_E0005_MSG) ; + } + if (is & IS_XA_C) { /* Encoding error async tx q */ + /* clear IRQ */ + outpd(ADDR(B5_XA_CSR),CSR_IRQ_CL_C) ; + SMT_PANIC(smc,HWM_E0006,HWM_E0006_MSG) ; + } + if (is & IS_XS_C) { /* Encoding error sync tx q */ + /* clear IRQ */ + outpd(ADDR(B5_XS_CSR),CSR_IRQ_CL_C) ; + SMT_PANIC(smc,HWM_E0007,HWM_E0007_MSG) ; + } + } + + /* + * Fast Tx complete Async/Sync Queue (BMU service) + */ + if (is & (IS_XS_F|IS_XA_F)) { + DB_GEN("Fast tx complete queue",0,0,6) ; + /* + * clear IRQ, Note: no IRQ is lost, because + * we always service both queues + */ + outpd(ADDR(B5_XS_CSR),CSR_IRQ_CL_F) ; + outpd(ADDR(B5_XA_CSR),CSR_IRQ_CL_F) ; + mac_drv_clear_txd(smc) ; + llc_restart_tx(smc) ; + } + + /* + * Fast Rx Complete (BMU service) + */ + if (is & IS_R1_F) { + DB_GEN("Fast receive complete",0,0,6) ; + /* clear IRQ */ +#ifndef USE_BREAK_ISR + outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_F) ; + process_receive(smc) ; +#else + process_receive(smc) ; + if (smc->os.hwm.leave_isr) { + force_irq = FALSE ; + } else { + outpd(ADDR(B4_R1_CSR),CSR_IRQ_CL_F) ; + process_receive(smc) ; + } +#endif + } + +#ifndef NDIS_OS2 + while ((mb = get_llc_rx(smc))) { + smt_to_llc(smc,mb) ; + } +#else + if (offDepth) + post_proc() ; + + while (!offDepth && (mb = get_llc_rx(smc))) { + smt_to_llc(smc,mb) ; + } + + if (!offDepth && smc->os.hwm.rx_break) { + process_receive(smc) ; + } +#endif + if (smc->q.ev_get != smc->q.ev_put) { + NDD_TRACE("CH2a",0,0,0) ; + ev_dispatcher(smc) ; + } +#ifdef NDIS_OS2 + post_proc() ; + if (offDepth) { /* leave fddi_isr because */ + break ; /* indications not allowed */ + } +#endif +#ifdef USE_BREAK_ISR + if (smc->os.hwm.leave_isr) { + break ; /* leave fddi_isr */ + } +#endif + + /* NOTE: when the isr is left, no rx is pending */ + } /* end of interrupt source polling loop */ + +#ifdef USE_BREAK_ISR + if (smc->os.hwm.leave_isr && force_irq) { + smt_force_irq(smc) ; + } +#endif + smc->os.hwm.isr_flag = FALSE ; + NDD_TRACE("CH0E",0,0,0) ; +} + + +/* + ------------------------------------------------------------- + RECEIVE FUNCTIONS: + ------------------------------------------------------------- +*/ + +#ifndef NDIS_OS2 +/* + * BEGIN_MANUAL_ENTRY(mac_drv_rx_mode) + * void mac_drv_rx_mode(smc,mode) + * + * function DOWNCALL (fplus.c) + * Corresponding to the parameter mode, the operating system + * dependent module can activate several receive modes. + * + * para mode = 1: RX_ENABLE_ALLMULTI enable all multicasts + * = 2: RX_DISABLE_ALLMULTI disable "enable all multicasts" + * = 3: RX_ENABLE_PROMISC enable promiscuous + * = 4: RX_DISABLE_PROMISC disable promiscuous + * = 5: RX_ENABLE_NSA enable rec. of all NSA frames + * (disabled after 'driver reset' & 'set station address') + * = 6: RX_DISABLE_NSA disable rec. of all NSA frames + * + * = 21: RX_ENABLE_PASS_SMT ( see description ) + * = 22: RX_DISABLE_PASS_SMT ( " " ) + * = 23: RX_ENABLE_PASS_NSA ( " " ) + * = 24: RX_DISABLE_PASS_NSA ( " " ) + * = 25: RX_ENABLE_PASS_DB ( " " ) + * = 26: RX_DISABLE_PASS_DB ( " " ) + * = 27: RX_DISABLE_PASS_ALL ( " " ) + * = 28: RX_DISABLE_LLC_PROMISC ( " " ) + * = 29: RX_ENABLE_LLC_PROMISC ( " " ) + * + * + * RX_ENABLE_PASS_SMT / RX_DISABLE_PASS_SMT + * + * If the operating system dependent module activates the + * mode RX_ENABLE_PASS_SMT, the hardware module + * duplicates all SMT frames with the frame control + * FC_SMT_INFO and passes them to the LLC receive channel + * by calling mac_drv_rx_init. + * The SMT Frames which are sent by the local SMT and the NSA + * frames whose A- and C-Indicator is not set are also duplicated + * and passed. + * The receive mode RX_DISABLE_PASS_SMT disables the passing + * of SMT frames. + * + * RX_ENABLE_PASS_NSA / RX_DISABLE_PASS_NSA + * + * If the operating system dependent module activates the + * mode RX_ENABLE_PASS_NSA, the hardware module + * duplicates all NSA frames with frame control FC_SMT_NSA + * and a set A-Indicator and passed them to the LLC + * receive channel by calling mac_drv_rx_init. + * All NSA Frames which are sent by the local SMT + * are also duplicated and passed. + * The receive mode RX_DISABLE_PASS_NSA disables the passing + * of NSA frames with the A- or C-Indicator set. + * + * NOTE: For fear that the hardware module receives NSA frames with + * a reset A-Indicator, the operating system dependent module + * has to call mac_drv_rx_mode with the mode RX_ENABLE_NSA + * before activate the RX_ENABLE_PASS_NSA mode and after every + * 'driver reset' and 'set station address'. + * + * RX_ENABLE_PASS_DB / RX_DISABLE_PASS_DB + * + * If the operating system dependent module activates the + * mode RX_ENABLE_PASS_DB, direct BEACON frames + * (FC_BEACON frame control) are passed to the LLC receive + * channel by mac_drv_rx_init. + * The receive mode RX_DISABLE_PASS_DB disables the passing + * of direct BEACON frames. + * + * RX_DISABLE_PASS_ALL + * + * Disables all special receives modes. It is equal to + * call mac_drv_set_rx_mode successively with the + * parameters RX_DISABLE_NSA, RX_DISABLE_PASS_SMT, + * RX_DISABLE_PASS_NSA and RX_DISABLE_PASS_DB. + * + * RX_ENABLE_LLC_PROMISC + * + * (default) all received LLC frames and all SMT/NSA/DBEACON + * frames depending on the attitude of the flags + * PASS_SMT/PASS_NSA/PASS_DBEACON will be delivered to the + * LLC layer + * + * RX_DISABLE_LLC_PROMISC + * + * all received SMT/NSA/DBEACON frames depending on the + * attitude of the flags PASS_SMT/PASS_NSA/PASS_DBEACON + * will be delivered to the LLC layer. + * all received LLC frames with a directed address, Multicast + * or Broadcast address will be delivered to the LLC + * layer too. + * + * END_MANUAL_ENTRY + */ +void mac_drv_rx_mode(smc,mode) +struct s_smc *smc ; +int mode ; +{ + switch(mode) { + case RX_ENABLE_PASS_SMT: + smc->os.hwm.pass_SMT = TRUE ; + break ; + case RX_DISABLE_PASS_SMT: + smc->os.hwm.pass_SMT = FALSE ; + break ; + case RX_ENABLE_PASS_NSA: + smc->os.hwm.pass_NSA = TRUE ; + break ; + case RX_DISABLE_PASS_NSA: + smc->os.hwm.pass_NSA = FALSE ; + break ; + case RX_ENABLE_PASS_DB: + smc->os.hwm.pass_DB = TRUE ; + break ; + case RX_DISABLE_PASS_DB: + smc->os.hwm.pass_DB = FALSE ; + break ; + case RX_DISABLE_PASS_ALL: + smc->os.hwm.pass_SMT = smc->os.hwm.pass_NSA = FALSE ; + smc->os.hwm.pass_DB = FALSE ; + smc->os.hwm.pass_llc_promisc = TRUE ; + mac_set_rx_mode(smc,RX_DISABLE_NSA) ; + break ; + case RX_DISABLE_LLC_PROMISC: + smc->os.hwm.pass_llc_promisc = FALSE ; + break ; + case RX_ENABLE_LLC_PROMISC: + smc->os.hwm.pass_llc_promisc = TRUE ; + break ; + case RX_ENABLE_ALLMULTI: + case RX_DISABLE_ALLMULTI: + case RX_ENABLE_PROMISC: + case RX_DISABLE_PROMISC: + case RX_ENABLE_NSA: + case RX_DISABLE_NSA: + default: + mac_set_rx_mode(smc,mode) ; + break ; + } +} +#endif /* ifndef NDIS_OS2 */ + +/* + * process receive queue + */ +void process_receive(smc) +struct s_smc *smc ; +{ + int i ; + int n ; + int frag_count ; /* number of RxDs of the curr rx buf */ + int used_frags ; /* number of RxDs of the curr frame */ + struct s_smt_rx_queue *queue ; /* points to the queue ctl struct */ + struct s_smt_fp_rxd volatile *r ; /* rxd pointer */ + struct s_smt_fp_rxd volatile *rxd ; /* first rxd of rx frame */ + u_long rbctrl ; /* receive buffer control word */ + u_long rfsw ; /* receive frame status word */ + u_short rx_used ; + u_char far *virt ; + char far *data ; + SMbuf *mb ; + u_char fc ; /* Frame control */ + int len ; /* Frame length */ + + smc->os.hwm.detec_count = 0 ; + queue = smc->hw.fp.rx[QUEUE_R1] ; + NDD_TRACE("RHxB",0,0,0) ; + for ( ; ; ) { + r = queue->rx_curr_get ; + rx_used = queue->rx_used ; + frag_count = 0 ; + +#ifdef USE_BREAK_ISR + if (smc->os.hwm.leave_isr) { + goto rx_end ; + } +#endif +#ifdef NDIS_OS2 + if (offDepth) { + smc->os.hwm.rx_break = 1 ; + goto rx_end ; + } + smc->os.hwm.rx_break = 0 ; +#endif +#ifdef ODI2 + if (smc->os.hwm.rx_break) { + goto rx_end ; + } +#endif + n = 0 ; + do { + DB_RX("Check RxD %x for OWN and EOF",(void *)r,0,5) ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + rbctrl = CR_READ(r->rxd_rbctrl) ; + rbctrl = AIX_REVERSE(rbctrl) ; + + if (rbctrl & BMU_OWN) { + NDD_TRACE("RHxE",r,rfsw,rbctrl) ; + DB_RX("End of RxDs",0,0,4) ; + goto rx_end ; + } + /* + * out of RxD detection + */ + if (!rx_used) { + SK_BREAK() ; + SMT_PANIC(smc,HWM_E0009,HWM_E0009_MSG) ; + /* Either we don't have an RxD or all + * RxDs are filled. Therefore it's allowed + * for to set the STOPPED flag */ + smc->hw.hw_state = STOPPED ; + mac_drv_clear_rx_queue(smc) ; + smc->hw.hw_state = STARTED ; + mac_drv_fill_rxd(smc) ; + smc->os.hwm.detec_count = 0 ; + goto rx_end ; + } + rfsw = AIX_REVERSE(r->rxd_rfsw) ; + if ((rbctrl & BMU_STF) != ((rbctrl & BMU_ST_BUF) <<5)) { + /* + * The BMU_STF bit is deleted, 1 frame is + * placed into more than 1 rx buffer + * + * skip frame by setting the rx len to 0 + * + * if fragment count == 0 + * The missing STF bit belongs to the + * current frame, search for the + * EOF bit to complete the frame + * else + * the fragment belongs to the next frame, + * exit the loop and process the frame + */ + SK_BREAK() ; + rfsw = 0 ; + if (frag_count) { + break ; + } + } + n += rbctrl & 0xffff ; + r = r->rxd_next ; + frag_count++ ; + rx_used-- ; + } while (!(rbctrl & BMU_EOF)) ; + used_frags = frag_count ; + DB_RX("EOF set in RxD, used_frags = %d ",used_frags,0,5) ; + + /* may be next 2 DRV_BUF_FLUSH() can be skipped, because */ + /* BMU_ST_BUF will not be changed by the ASIC */ + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + while (rx_used && !(r->rxd_rbctrl & AIX_REVERSE(BMU_ST_BUF))) { + DB_RX("Check STF bit in %x",(void *)r,0,5) ; + r = r->rxd_next ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + frag_count++ ; + rx_used-- ; + } + DB_RX("STF bit found",0,0,5) ; + + /* + * The received frame is finished for the process receive + */ + rxd = queue->rx_curr_get ; + queue->rx_curr_get = r ; + queue->rx_free += frag_count ; + queue->rx_used = rx_used ; + + /* + * ASIC Errata no. 7 (STF - Bit Bug) + */ + rxd->rxd_rbctrl &= AIX_REVERSE(~BMU_STF) ; + + for (r=rxd, i=frag_count ; i ; r=r->rxd_next, i--){ + DB_RX("dma_complete for RxD %x",(void *)r,0,5) ; + dma_complete(smc,(union s_fp_descr volatile *)r,DMA_WR); + } + smc->hw.fp.err_stats.err_valid++ ; + smc->mib.m[MAC0].fddiMACCopied_Ct++ ; + + /* the length of the data including the FC */ + len = (rfsw & RD_LENGTH) - 4 ; + + DB_RX("frame length = %d",len,0,4) ; + /* + * check the frame_lenght and all error flags + */ + if (rfsw & (RX_MSRABT|RX_FS_E|RX_FS_CRC|RX_FS_IMPL)){ + if (rfsw & RD_S_MSRABT) { + DB_RX("Frame aborted by the FORMAC",0,0,2) ; + smc->hw.fp.err_stats.err_abort++ ; + } + /* + * check frame status + */ + if (rfsw & RD_S_SEAC2) { + DB_RX("E-Indicator set",0,0,2) ; + smc->hw.fp.err_stats.err_e_indicator++ ; + } + if (rfsw & RD_S_SFRMERR) { + DB_RX("CRC error",0,0,2) ; + smc->hw.fp.err_stats.err_crc++ ; + } + if (rfsw & RX_FS_IMPL) { + DB_RX("Implementer frame",0,0,2) ; + smc->hw.fp.err_stats.err_imp_frame++ ; + } + goto abort_frame ; + } + if (len > FDDI_RAW_MTU-4) { + DB_RX("Frame to long error",0,0,2) ; + smc->hw.fp.err_stats.err_too_long++ ; + goto abort_frame ; + } + /* + * SUPERNET 3 Bug: FORMAC delivers status words + * of aborded frames to the BMU + */ + if (len <= 4) { + DB_RX("Frame length = 0",0,0,2) ; + goto abort_frame ; + } + + if (len != (n-4)) { + DB_RX("BMU: rx len differs: [%d:%d]",len,n,4); + smc->os.hwm.rx_len_error++ ; + goto abort_frame ; + } + + /* + * Check SA == MA + */ + virt = (u_char far *) rxd->rxd_virt ; + DB_RX("FC = %x",*virt,0,2) ; + if (virt[12] == MA[5] && + virt[11] == MA[4] && + virt[10] == MA[3] && + virt[9] == MA[2] && + virt[8] == MA[1] && + (virt[7] & ~GROUP_ADDR_BIT) == MA[0]) { + goto abort_frame ; + } + + /* + * test if LLC frame + */ + if (rfsw & RX_FS_LLC) { + /* + * if pass_llc_promisc is disable + * if DA != Multicast or Broadcast or DA!=MA + * abort the frame + */ + if (!smc->os.hwm.pass_llc_promisc) { + if(!(virt[1] & GROUP_ADDR_BIT)) { + if (virt[6] != MA[5] || + virt[5] != MA[4] || + virt[4] != MA[3] || + virt[3] != MA[2] || + virt[2] != MA[1] || + virt[1] != MA[0]) { + DB_RX("DA != MA and not multi- or broadcast",0,0,2) ; + goto abort_frame ; + } + } + } + + /* + * LLC frame received + */ + DB_RX("LLC - receive",0,0,4) ; + mac_drv_rx_complete(smc,rxd,frag_count,len) ; + } + else { + if (!(mb = smt_get_mbuf(smc))) { + smc->hw.fp.err_stats.err_no_buf++ ; + DB_RX("No SMbuf; receive terminated",0,0,4) ; + goto abort_frame ; + } + data = smtod(mb,char *) - 1 ; + + /* + * copy the frame into a SMT_MBuf + */ +#ifdef USE_OS_CPY + hwm_cpy_rxd2mb(rxd,data,len) ; +#else + for (r=rxd, i=used_frags ; i ; r=r->rxd_next, i--){ + n = AIX_REVERSE(r->rxd_rbctrl) & RD_LENGTH ; + DB_RX("cp SMT frame to mb: len = %d",n,0,6) ; + memcpy(data,r->rxd_virt,n) ; + data += n ; + } + data = smtod(mb,char *) - 1 ; +#endif + fc = *(char *)mb->sm_data = *data ; + mb->sm_len = len - 1 ; /* len - fc */ + data++ ; + + /* + * SMT frame received + */ + switch(fc) { + case FC_SMT_INFO : + smc->hw.fp.err_stats.err_smt_frame++ ; + DB_RX("SMT frame received ",0,0,5) ; + + if (smc->os.hwm.pass_SMT) { + DB_RX("pass SMT frame ",0,0,5) ; + mac_drv_rx_complete(smc, rxd, + frag_count,len) ; + } + else { + DB_RX("requeue RxD",0,0,5) ; + mac_drv_requeue_rxd(smc,rxd,frag_count); + } + + smt_received_pack(smc,mb,(int)(rfsw>>25)) ; + break ; + case FC_SMT_NSA : + smc->hw.fp.err_stats.err_smt_frame++ ; + DB_RX("SMT frame received ",0,0,5) ; + + /* if pass_NSA set pass the NSA frame or */ + /* pass_SMT set and the A-Indicator */ + /* is not set, pass the NSA frame */ + if (smc->os.hwm.pass_NSA || + (smc->os.hwm.pass_SMT && + !(rfsw & A_INDIC))) { + DB_RX("pass SMT frame ",0,0,5) ; + mac_drv_rx_complete(smc, rxd, + frag_count,len) ; + } + else { + DB_RX("requeue RxD",0,0,5) ; + mac_drv_requeue_rxd(smc,rxd,frag_count); + } + + smt_received_pack(smc,mb,(int)(rfsw>>25)) ; + break ; + case FC_BEACON : + if (smc->os.hwm.pass_DB) { + DB_RX("pass DB frame ",0,0,5) ; + mac_drv_rx_complete(smc, rxd, + frag_count,len) ; + } + else { + DB_RX("requeue RxD",0,0,5) ; + mac_drv_requeue_rxd(smc,rxd,frag_count); + } + smt_free_mbuf(smc,mb) ; + break ; + default : + /* + * unknown FC abord the frame + */ + DB_RX("unknown FC error",0,0,2) ; + smt_free_mbuf(smc,mb) ; + DB_RX("requeue RxD",0,0,5) ; + mac_drv_requeue_rxd(smc,rxd,frag_count) ; + if ((fc & 0xf0) == FC_MAC) + smc->hw.fp.err_stats.err_mac_frame++ ; + else + smc->hw.fp.err_stats.err_imp_frame++ ; + + break ; + } + } + + DB_RX("next RxD is %x ",queue->rx_curr_get,0,3) ; + NDD_TRACE("RHx1",queue->rx_curr_get,0,0) ; + + continue ; + /*--------------------------------------------------------------------*/ +abort_frame: + DB_RX("requeue RxD",0,0,5) ; + mac_drv_requeue_rxd(smc,rxd,frag_count) ; + + DB_RX("next RxD is %x ",queue->rx_curr_get,0,3) ; + NDD_TRACE("RHx2",queue->rx_curr_get,0,0) ; + } +rx_end: +#ifdef ALL_RX_COMPLETE + mac_drv_all_receives_complete(smc) ; +#endif + return ; /* lint bug: needs return detect end of function */ +} + +static void smt_to_llc(smc,mb) +struct s_smc *smc ; +SMbuf *mb ; +{ + u_char fc ; + + DB_RX("send a queued frame to the llc layer",0,0,4) ; + smc->os.hwm.r.len = mb->sm_len ; + smc->os.hwm.r.mb_pos = smtod(mb,char *) ; + fc = *smc->os.hwm.r.mb_pos ; + (void)mac_drv_rx_init(smc,(int)mb->sm_len,(int)fc, + smc->os.hwm.r.mb_pos,(int)mb->sm_len) ; + smt_free_mbuf(smc,mb) ; +} + +/* + * BEGIN_MANUAL_ENTRY(hwm_rx_frag) + * void hwm_rx_frag(smc,virt,phys,len,frame_status) + * + * function MACRO (hardware module, hwmtm.h) + * This function calls dma_master for preparing the + * system hardware for the DMA transfer and initializes + * the current RxD with the length and the physical and + * virtual address of the fragment. Furthermore, it sets the + * STF and EOF bits depending on the frame status byte, + * switches the OWN flag of the RxD, so that it is owned by the + * adapter and issues an rx_start. + * + * para virt virtual pointer to the fragment + * len the length of the fragment + * frame_status status of the frame, see design description + * + * NOTE: It is possible to call this function with a fragment length + * of zero. + * + * END_MANUAL_ENTRY + */ +void hwm_rx_frag(smc,virt,phys,len,frame_status) +struct s_smc *smc ; +char far *virt ; +u_long phys ; +int len ; +int frame_status ; +{ + struct s_smt_fp_rxd volatile *r ; + u_int rbctrl ; + + NDD_TRACE("RHfB",virt,len,frame_status) ; + DB_RX("hwm_rx_frag: len = %d, frame_status = %x\n",len,frame_status,2) ; + r = smc->hw.fp.rx_q[QUEUE_R1].rx_curr_put ; + r->rxd_virt = virt ; + r->rxd_rbadr = AIX_REVERSE(phys) ; + rbctrl = AIX_REVERSE( (((u_long)frame_status & + (FIRST_FRAG|LAST_FRAG))<<26) | + (((u_long) frame_status & FIRST_FRAG) << 21) | + BMU_OWN | BMU_CHECK | BMU_EN_IRQ_EOF | len) ; + r->rxd_rbctrl = rbctrl ; + + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ; + outpd(ADDR(B0_R1_CSR),CSR_START) ; + smc->hw.fp.rx_q[QUEUE_R1].rx_free-- ; + smc->hw.fp.rx_q[QUEUE_R1].rx_used++ ; + smc->hw.fp.rx_q[QUEUE_R1].rx_curr_put = r->rxd_next ; + NDD_TRACE("RHfE",r,AIX_REVERSE(r->rxd_rbadr),0) ; +} + +#ifndef NDIS_OS2 +/* + * BEGIN_MANUAL_ENTRY(mac_drv_rx_frag) + * int mac_drv_rx_frag(smc,virt,len) + * + * function DOWNCALL (hwmtm.c) + * mac_drv_rx_frag fills the fragment with a part of the frame. + * + * para virt the virtual address of the fragment + * len the length in bytes of the fragment + * + * return 0: success code, no errors possible + * + * END_MANUAL_ENTRY + */ +int mac_drv_rx_frag(smc,virt,len) +struct s_smc *smc ; +void far *virt ; +int len ; +{ + NDD_TRACE("RHSB",virt,len,smc->os.hwm.r.mb_pos) ; + + DB_RX("receive from queue: len/virt: = %d/%x",len,virt,4) ; + memcpy((char far *)virt,smc->os.hwm.r.mb_pos,len) ; + smc->os.hwm.r.mb_pos += len ; + + NDD_TRACE("RHSE",smc->os.hwm.r.mb_pos,0,0) ; + return(0) ; +} +#endif + + +/* + * BEGINN_MANUAL_ENTRY(mac_drv_clear_rx_queue) + * + * void mac_drv_clear_rx_queue(smc) + * struct s_smc *smc ; + * + * function DOWNCALL (hardware module, hwmtm.c) + * mac_drv_clear_rx_queue is called by the OS-specific module + * after it has issued a card_stop. + * In this case, the frames in the receive queue are obsolete and + * should be removed. For removing mac_drv_clear_rx_queue + * calls dma_master for each RxD and mac_drv_clear_rxd for each + * receive buffer. + * + * NOTE: calling sequence card_stop: + * CLI_FBI(), card_stop(), + * mac_drv_clear_tx_queue(), mac_drv_clear_rx_queue(), + * + * NOTE: The caller is responsible that the BMUs are idle + * when this function is called. + * + * END_MANUAL_ENTRY + */ +void mac_drv_clear_rx_queue(smc) +struct s_smc *smc ; +{ + struct s_smt_fp_rxd volatile *r ; + struct s_smt_fp_rxd volatile *next_rxd ; + struct s_smt_rx_queue *queue ; + int frag_count ; + int i ; + + if (smc->hw.hw_state != STOPPED) { + SK_BREAK() ; + SMT_PANIC(smc,HWM_E0012,HWM_E0012_MSG) ; + return ; + } + + queue = smc->hw.fp.rx[QUEUE_R1] ; + DB_RX("clear_rx_queue",0,0,5) ; + + /* + * dma_complete and mac_drv_clear_rxd for all RxDs / receive buffers + */ + r = queue->rx_curr_get ; + while (queue->rx_used) { + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + DB_RX("switch OWN bit of RxD 0x%x ",r,0,5) ; + r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ; + frag_count = 1 ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ; + r = r->rxd_next ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + while (r != queue->rx_curr_put && + !(r->rxd_rbctrl & AIX_REVERSE(BMU_ST_BUF))) { + DB_RX("Check STF bit in %x",(void *)r,0,5) ; + r->rxd_rbctrl &= AIX_REVERSE(~BMU_OWN) ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORDEV) ; + r = r->rxd_next ; + DRV_BUF_FLUSH(r,DDI_DMA_SYNC_FORCPU) ; + frag_count++ ; + } + DB_RX("STF bit found",0,0,5) ; + next_rxd = r ; + + for (r=queue->rx_curr_get,i=frag_count; i ; r=r->rxd_next,i--){ + DB_RX("dma_complete for RxD %x",(void *)r,0,5) ; + dma_complete(smc,(union s_fp_descr volatile *)r,DMA_WR); + } + + DB_RX("mac_drv_clear_rxd: RxD %x frag_count %d ", + (void *)queue->rx_curr_get,frag_count,5) ; + mac_drv_clear_rxd(smc,queue->rx_curr_get,frag_count) ; + + queue->rx_curr_get = next_rxd ; + queue->rx_used -= frag_count ; + queue->rx_free += frag_count ; + } +} + + +/* + ------------------------------------------------------------- + SEND FUNCTIONS: + ------------------------------------------------------------- +*/ + +/* + * BEGIN_MANUAL_ENTRY(hwm_tx_init) + * int hwm_tx_init(smc,fc,frag_count,frame_len,frame_status) + * + * function DOWN_CALL (hardware module, hwmtm.c) + * hwm_tx_init checks if the frame can be sent through the + * corresponding send queue. + * + * para fc the frame control. To determine through which + * send queue the frame should be transmitted. + * 0x50 - 0x57: asynchronous LLC frame + * 0xD0 - 0xD7: synchronous LLC frame + * 0x41, 0x4F: SMT frame to the network + * 0x42: SMT frame to the network and to the local SMT + * 0x43: SMT frame to the local SMT + * frag_count count of the fragments for this frame + * frame_len length of the frame + * frame_status status of the frame, the send queue bit is already + * specified + * + * return frame_status + * + * END_MANUAL_ENTRY + */ +int hwm_tx_init(smc,fc,frag_count,frame_len,frame_status) +struct s_smc *smc ; +u_char fc ; +int frag_count ; +int frame_len ; +int frame_status ; +{ + NDD_TRACE("THiB",fc,frag_count,frame_len) ; + smc->os.hwm.tx_p = smc->hw.fp.tx[frame_status & QUEUE_A0] ; + smc->os.hwm.tx_descr = TX_DESCRIPTOR | (((u_long)(frame_len-1)&3)<<27) ; + smc->os.hwm.tx_len = frame_len ; + DB_TX("hwm_tx_init: fc = %x, len = %d",fc,frame_len,3) ; + if ((fc & ~(FC_SYNC_BIT|FC_LLC_PRIOR)) == FC_ASYNC_LLC) { + frame_status |= LAN_TX ; + } + else { + switch (fc) { + case FC_SMT_INFO : + case FC_SMT_NSA : + frame_status |= LAN_TX ; + break ; + case FC_SMT_LOC : + frame_status |= LOC_TX ; + break ; + case FC_SMT_LAN_LOC : + frame_status |= LAN_TX | LOC_TX ; + break ; + default : + SMT_PANIC(smc,HWM_E0010,HWM_E0010_MSG) ; + } + } + if (!smc->hw.mac_ring_is_up) { + frame_status &= ~LAN_TX ; + frame_status |= RING_DOWN ; + DB_TX("Ring is down: terminate LAN_TX",0,0,2) ; + } + if (frag_count > smc->os.hwm.tx_p->tx_free) { +#ifndef NDIS_OS2 + mac_drv_clear_txd(smc) ; + if (frag_count > smc->os.hwm.tx_p->tx_free) { + DB_TX("Out of TxDs, terminate LAN_TX",0,0,2) ; + frame_status &= ~LAN_TX ; + frame_status |= OUT_OF_TXD ; + } +#else + DB_TX("Out of TxDs, terminate LAN_TX",0,0,2) ; + frame_status &= ~LAN_TX ; + frame_status |= OUT_OF_TXD ; +#endif + } + DB_TX("frame_status = %x",frame_status,0,3) ; + NDD_TRACE("THiE",frame_status,smc->os.hwm.tx_p->tx_free,0) ; + return(frame_status) ; +} + +/* + * BEGIN_MANUAL_ENTRY(hwm_tx_frag) + * void hwm_tx_frag(smc,virt,phys,len,frame_status) + * + * function DOWNCALL (hardware module, hwmtm.c) + * If the frame should be sent to the LAN, this function calls + * dma_master, fills the current TxD with the virtual and the + * physical address, sets the STF and EOF bits dependent on + * the frame status, and requests the BMU to start the + * transmit. + * If the frame should be sent to the local SMT, an SMT_MBuf + * is allocated if the FIRST_FRAG bit is set in the frame_status. + * The fragment of the frame is copied into the SMT MBuf. + * The function smt_received_pack is called if the LAST_FRAG + * bit is set in the frame_status word. + * + * para virt virtual pointer to the fragment + * len the length of the fragment + * frame_status status of the frame, see design description + * + * return nothing returned, no parameter is modified + * + * NOTE: It is possible to invoke this macro with a fragment length + * of zero. + * + * END_MANUAL_ENTRY + */ +void hwm_tx_frag(smc,virt,phys,len,frame_status) +struct s_smc *smc ; +char far *virt ; +u_long phys ; +int len ; +int frame_status ; +{ + struct s_smt_fp_txd volatile *t ; + struct s_smt_tx_queue *queue ; + u_int tbctrl ; + + queue = smc->os.hwm.tx_p ; + + NDD_TRACE("THfB",virt,len,frame_status) ; + /* Bug fix: AF / May 31 1999 (#missing) + * snmpinfo problem reported by IBM is caused by invalid + * t-pointer (txd) if LAN_TX is not set but LOC_TX only. + * Set: t = queue->tx_curr_put here ! + */ + t = queue->tx_curr_put ; + + DB_TX("hwm_tx_frag: len = %d, frame_status = %x ",len,frame_status,2) ; + if (frame_status & LAN_TX) { + /* '*t' is already defined */ + DB_TX("LAN_TX: TxD = %x, virt = %x ",t,virt,3) ; + t->txd_virt = virt ; + t->txd_txdscr = AIX_REVERSE(smc->os.hwm.tx_descr) ; + t->txd_tbadr = AIX_REVERSE(phys) ; + tbctrl = AIX_REVERSE((((u_long)frame_status & + (FIRST_FRAG|LAST_FRAG|EN_IRQ_EOF))<< 26) | + BMU_OWN|BMU_CHECK |len) ; + t->txd_tbctrl = tbctrl ; + +#ifndef AIX + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + outpd(queue->tx_bmu_ctl,CSR_START) ; +#else /* ifndef AIX */ + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + if (frame_status & QUEUE_A0) { + outpd(ADDR(B0_XA_CSR),CSR_START) ; + } + else { + outpd(ADDR(B0_XS_CSR),CSR_START) ; + } +#endif + queue->tx_free-- ; + queue->tx_used++ ; + queue->tx_curr_put = t->txd_next ; + if (frame_status & LAST_FRAG) { + smc->mib.m[MAC0].fddiMACTransmit_Ct++ ; + } + } + if (frame_status & LOC_TX) { + DB_TX("LOC_TX: ",0,0,3) ; + if (frame_status & FIRST_FRAG) { + if(!(smc->os.hwm.tx_mb = smt_get_mbuf(smc))) { + smc->hw.fp.err_stats.err_no_buf++ ; + DB_TX("No SMbuf; transmit terminated",0,0,4) ; + } + else { + smc->os.hwm.tx_data = + smtod(smc->os.hwm.tx_mb,char *) - 1 ; +#ifdef USE_OS_CPY +#ifdef PASS_1ST_TXD_2_TX_COMP + hwm_cpy_txd2mb(t,smc->os.hwm.tx_data, + smc->os.hwm.tx_len) ; +#endif +#endif + } + } + if (smc->os.hwm.tx_mb) { +#ifndef USE_OS_CPY + DB_TX("copy fragment into MBuf ",0,0,3) ; + memcpy(smc->os.hwm.tx_data,virt,len) ; + smc->os.hwm.tx_data += len ; +#endif + if (frame_status & LAST_FRAG) { +#ifdef USE_OS_CPY +#ifndef PASS_1ST_TXD_2_TX_COMP + /* + * hwm_cpy_txd2mb(txd,data,len) copies 'len' + * bytes from the virtual pointer in 'rxd' + * to 'data'. The virtual pointer of the + * os-specific tx-buffer should be written + * in the LAST txd. + */ + hwm_cpy_txd2mb(t,smc->os.hwm.tx_data, + smc->os.hwm.tx_len) ; +#endif /* nPASS_1ST_TXD_2_TX_COMP */ +#endif /* USE_OS_CPY */ + smc->os.hwm.tx_data = + smtod(smc->os.hwm.tx_mb,char *) - 1 ; + *(char *)smc->os.hwm.tx_mb->sm_data = + *smc->os.hwm.tx_data ; + smc->os.hwm.tx_data++ ; + smc->os.hwm.tx_mb->sm_len = + smc->os.hwm.tx_len - 1 ; + DB_TX("pass LLC frame to SMT ",0,0,3) ; + smt_received_pack(smc,smc->os.hwm.tx_mb, + RD_FS_LOCAL) ; + } + } + } + NDD_TRACE("THfE",t,queue->tx_free,0) ; +} + + +/* + * queues a receive for later send + */ +static void queue_llc_rx(smc,mb) +struct s_smc *smc ; +SMbuf *mb ; +{ + DB_GEN("queue_llc_rx: mb = %x",(void *)mb,0,4) ; + smc->os.hwm.queued_rx_frames++ ; + mb->sm_next = (SMbuf *)NULL ; + if (smc->os.hwm.llc_rx_pipe == 0) { + smc->os.hwm.llc_rx_pipe = mb ; + } + else { + smc->os.hwm.llc_rx_tail->sm_next = mb ; + } + smc->os.hwm.llc_rx_tail = mb ; + + /* + * force an timer IRQ to receive the data + */ + if (!smc->os.hwm.isr_flag) { + smt_force_irq(smc) ; + } +} + +/* + * get a SMbuf from the llc_rx_queue + */ +static SMbuf *get_llc_rx(smc) +struct s_smc *smc ; +{ + SMbuf *mb ; + + if ((mb = smc->os.hwm.llc_rx_pipe)) { + smc->os.hwm.queued_rx_frames-- ; + smc->os.hwm.llc_rx_pipe = mb->sm_next ; + } + DB_GEN("get_llc_rx: mb = 0x%x",(void *)mb,0,4) ; + return(mb) ; +} + +/* + * queues a transmit SMT MBuf during the time were the MBuf is + * queued the TxD ring + */ +static void queue_txd_mb(smc,mb) +struct s_smc *smc ; +SMbuf *mb ; +{ + DB_GEN("_rx: queue_txd_mb = %x",(void *)mb,0,4) ; + smc->os.hwm.queued_txd_mb++ ; + mb->sm_next = (SMbuf *)NULL ; + if (smc->os.hwm.txd_tx_pipe == 0) { + smc->os.hwm.txd_tx_pipe = mb ; + } + else { + smc->os.hwm.txd_tx_tail->sm_next = mb ; + } + smc->os.hwm.txd_tx_tail = mb ; +} + +/* + * get a SMbuf from the txd_tx_queue + */ +static SMbuf *get_txd_mb(smc) +struct s_smc *smc ; +{ + SMbuf *mb ; + + if ((mb = smc->os.hwm.txd_tx_pipe)) { + smc->os.hwm.queued_txd_mb-- ; + smc->os.hwm.txd_tx_pipe = mb->sm_next ; + } + DB_GEN("get_txd_mb: mb = 0x%x",(void *)mb,0,4) ; + return(mb) ; +} + +/* + * SMT Send function + */ +void smt_send_mbuf(smc,mb,fc) +struct s_smc *smc; +SMbuf *mb; +int fc; +{ + char far *data ; + int len ; + int n ; + int i ; + int frag_count ; + int frame_status ; + SK_LOC_DECL(char far,*virt[3]) ; + int frag_len[3] ; + struct s_smt_tx_queue *queue ; + struct s_smt_fp_txd volatile *t ; + u_long phys ; + u_int tbctrl ; + + NDD_TRACE("THSB",mb,fc,0) ; + DB_TX("smt_send_mbuf: mb = 0x%x, fc = 0x%x",mb,fc,4) ; + + mb->sm_off-- ; /* set to fc */ + mb->sm_len++ ; /* + fc */ + data = smtod(mb,char *) ; + *data = fc ; + if (fc == FC_SMT_LOC) + *data = FC_SMT_INFO ; + + /* + * determine the frag count and the virt addresses of the frags + */ + frag_count = 0 ; + len = mb->sm_len ; + while (len) { + n = SMT_PAGESIZE - ((long)data & (SMT_PAGESIZE-1)) ; + if (n >= len) { + n = len ; + } + DB_TX("frag: virt/len = 0x%x/%d ",(void *)data,n,5) ; + virt[frag_count] = data ; + frag_len[frag_count] = n ; + frag_count++ ; + len -= n ; + data += n ; + } + + /* + * determine the frame status + */ + queue = smc->hw.fp.tx[QUEUE_A0] ; + if (fc == FC_BEACON || fc == FC_SMT_LOC) { + frame_status = LOC_TX ; + } + else { + frame_status = LAN_TX ; + if ((smc->os.hwm.pass_NSA &&(fc == FC_SMT_NSA)) || + (smc->os.hwm.pass_SMT &&(fc == FC_SMT_INFO))) + frame_status |= LOC_TX ; + } + + if (!smc->hw.mac_ring_is_up || frag_count > queue->tx_free) { + if (frame_status &= ~LAN_TX) { + DB_TX("Ring is down: terminate LAN_TX",0,0,2) ; + } + else { + DB_TX("Ring is down: terminate transmission",0,0,2) ; + smt_free_mbuf(smc,mb) ; + return ; + } + } + DB_TX("frame_status = 0x%x ",frame_status,0,5) ; + + if ((frame_status & LAN_TX) && (frame_status & LOC_TX)) { + mb->sm_use_count = 2 ; + } + + if (frame_status & LAN_TX) { + t = queue->tx_curr_put ; + frame_status |= FIRST_FRAG ; + for (i = 0; i < frag_count; i++) { + DB_TX("init TxD = 0x%x",(void *)t,0,5) ; + if (i == frag_count-1) { + frame_status |= LAST_FRAG ; + t->txd_txdscr = AIX_REVERSE(TX_DESCRIPTOR | + (((u_long)(mb->sm_len-1)&3) << 27)) ; + } + t->txd_virt = virt[i] ; + phys = dma_master(smc, (void far *)virt[i], + frag_len[i], DMA_RD|SMT_BUF) ; + t->txd_tbadr = AIX_REVERSE(phys) ; + tbctrl = AIX_REVERSE((((u_long) frame_status & + (FIRST_FRAG|LAST_FRAG)) << 26) | + BMU_OWN | BMU_CHECK | BMU_SMT_TX |frag_len[i]) ; + t->txd_tbctrl = tbctrl ; +#ifndef AIX + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + outpd(queue->tx_bmu_ctl,CSR_START) ; +#else + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + outpd(ADDR(B0_XA_CSR),CSR_START) ; +#endif + frame_status &= ~FIRST_FRAG ; + queue->tx_curr_put = t = t->txd_next ; + queue->tx_free-- ; + queue->tx_used++ ; + } + smc->mib.m[MAC0].fddiMACTransmit_Ct++ ; + queue_txd_mb(smc,mb) ; + } + + if (frame_status & LOC_TX) { + DB_TX("pass Mbuf to LLC queue",0,0,5) ; + queue_llc_rx(smc,mb) ; + } + + /* + * We need to unqueue the free SMT_MBUFs here, because it may + * be that the SMT want's to send more than 1 frame for one down call + */ + mac_drv_clear_txd(smc) ; + NDD_TRACE("THSE",t,queue->tx_free,frag_count) ; +} + +/* BEGIN_MANUAL_ENTRY(mac_drv_clear_txd) + * void mac_drv_clear_txd(smc) + * + * function DOWNCALL (hardware module, hwmtm.c) + * mac_drv_clear_txd searches in both send queues for TxD's + * which were finished by the adapter. It calls dma_complete + * for each TxD. If the last fragment of an LLC frame is + * reached, it calls mac_drv_tx_complete to release the + * send buffer. + * + * return nothing + * + * END_MANUAL_ENTRY + */ +void mac_drv_clear_txd(smc) +struct s_smc *smc ; +{ + struct s_smt_tx_queue *queue ; + struct s_smt_fp_txd volatile *t1 ; + struct s_smt_fp_txd volatile *t2=0 ; + SMbuf *mb ; + u_long tbctrl ; + int i ; + int frag_count ; + int n ; + + NDD_TRACE("THcB",0,0,0) ; + for (i = QUEUE_S; i <= QUEUE_A0; i++) { + queue = smc->hw.fp.tx[i] ; + t1 = queue->tx_curr_get ; + DB_TX("clear_txd: QUEUE = %d (0=sync/1=async)",i,0,5) ; + + for ( ; ; ) { + frag_count = 0 ; + + do { + DRV_BUF_FLUSH(t1,DDI_DMA_SYNC_FORCPU) ; + DB_TX("check OWN/EOF bit of TxD 0x%x",t1,0,5) ; + tbctrl = CR_READ(t1->txd_tbctrl) ; + tbctrl = AIX_REVERSE(tbctrl) ; + + if (tbctrl & BMU_OWN || !queue->tx_used){ + DB_TX("End of TxDs queue %d",i,0,4) ; + goto free_next_queue ; /* next queue */ + } + t1 = t1->txd_next ; + frag_count++ ; + } while (!(tbctrl & BMU_EOF)) ; + + t1 = queue->tx_curr_get ; + for (n = frag_count; n; n--) { + tbctrl = AIX_REVERSE(t1->txd_tbctrl) ; + dma_complete(smc, + (union s_fp_descr volatile *) t1, + (int) (DMA_RD | + ((tbctrl & BMU_SMT_TX) >> 18))) ; + t2 = t1 ; + t1 = t1->txd_next ; + } + + if (tbctrl & BMU_SMT_TX) { + mb = get_txd_mb(smc) ; + smt_free_mbuf(smc,mb) ; + } + else { +#ifndef PASS_1ST_TXD_2_TX_COMP + DB_TX("mac_drv_tx_comp for TxD 0x%x",t2,0,4) ; + mac_drv_tx_complete(smc,t2) ; +#else + DB_TX("mac_drv_tx_comp for TxD 0x%x", + queue->tx_curr_get,0,4) ; + mac_drv_tx_complete(smc,queue->tx_curr_get) ; +#endif + } + queue->tx_curr_get = t1 ; + queue->tx_free += frag_count ; + queue->tx_used -= frag_count ; + } +free_next_queue: ; + } + NDD_TRACE("THcE",0,0,0) ; +} + +/* + * BEGINN_MANUAL_ENTRY(mac_drv_clear_tx_queue) + * + * void mac_drv_clear_tx_queue(smc) + * struct s_smc *smc ; + * + * function DOWNCALL (hardware module, hwmtm.c) + * mac_drv_clear_tx_queue is called from the SMT when + * the RMT state machine has entered the ISOLATE state. + * This function is also called by the os-specific module + * after it has called the function card_stop(). + * In this case, the frames in the send queues are obsolete and + * should be removed. + * + * note calling sequence: + * CLI_FBI(), card_stop(), + * mac_drv_clear_tx_queue(), mac_drv_clear_rx_queue(), + * + * NOTE: The caller is responsible that the BMUs are idle + * when this function is called. + * + * END_MANUAL_ENTRY + */ +void mac_drv_clear_tx_queue(smc) +struct s_smc *smc ; +{ + struct s_smt_fp_txd volatile *t ; + struct s_smt_tx_queue *queue ; + int tx_used ; + int i ; + + if (smc->hw.hw_state != STOPPED) { + SK_BREAK() ; + SMT_PANIC(smc,HWM_E0011,HWM_E0011_MSG) ; + return ; + } + + for (i = QUEUE_S; i <= QUEUE_A0; i++) { + queue = smc->hw.fp.tx[i] ; + DB_TX("clear_tx_queue: QUEUE = %d (0=sync/1=async)",i,0,5) ; + + /* + * switch the OWN bit of all pending frames to the host + */ + t = queue->tx_curr_get ; + tx_used = queue->tx_used ; + while (tx_used) { + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORCPU) ; + DB_TX("switch OWN bit of TxD 0x%x ",t,0,5) ; + t->txd_tbctrl &= AIX_REVERSE(~BMU_OWN) ; + DRV_BUF_FLUSH(t,DDI_DMA_SYNC_FORDEV) ; + t = t->txd_next ; + tx_used-- ; + } + } + + /* + * release all TxD's for both send queues + */ + mac_drv_clear_txd(smc) ; + + for (i = QUEUE_S; i <= QUEUE_A0; i++) { + queue = smc->hw.fp.tx[i] ; + t = queue->tx_curr_get ; + + /* + * write the phys pointer of the NEXT descriptor into the + * BMU's current address descriptor pointer and set + * tx_curr_get and tx_curr_put to this position + */ + if (i == QUEUE_S) { + outpd(ADDR(B5_XS_DA),AIX_REVERSE(t->txd_ntdadr)) ; + } + else { + outpd(ADDR(B5_XA_DA),AIX_REVERSE(t->txd_ntdadr)) ; + } + + queue->tx_curr_put = queue->tx_curr_get->txd_next ; + queue->tx_curr_get = queue->tx_curr_put ; + } +} + + +/* + ------------------------------------------------------------- + TEST FUNCTIONS: + ------------------------------------------------------------- +*/ + +#ifdef DEBUG +/* + * BEGIN_MANUAL_ENTRY(mac_drv_debug_lev) + * void mac_drv_debug_lev(smc,flag,lev) + * + * function DOWNCALL (drvsr.c) + * To get a special debug info the user can assign a debug level + * to any debug flag. + * + * para flag debug flag, possible values are: + * = 0: reset all debug flags (the defined level is + * ignored) + * = 1: debug.d_smtf + * = 2: debug.d_smt + * = 3: debug.d_ecm + * = 4: debug.d_rmt + * = 5: debug.d_cfm + * = 6: debug.d_pcm + * + * = 10: debug.d_os.hwm_rx (hardware module receive path) + * = 11: debug.d_os.hwm_tx(hardware module transmit path) + * = 12: debug.d_os.hwm_gen(hardware module general flag) + * + * lev debug level + * + * END_MANUAL_ENTRY + */ +void mac_drv_debug_lev(smc,flag,lev) +struct s_smc *smc ; +int flag ; +int lev ; +{ + switch(flag) { + case (int)NULL: + DB_P.d_smtf = DB_P.d_smt = DB_P.d_ecm = DB_P.d_rmt = 0 ; + DB_P.d_cfm = 0 ; + DB_P.d_os.hwm_rx = DB_P.d_os.hwm_tx = DB_P.d_os.hwm_gen = 0 ; +#ifdef SBA + DB_P.d_sba = 0 ; +#endif +#ifdef ESS + DB_P.d_ess = 0 ; +#endif + break ; + case DEBUG_SMTF: + DB_P.d_smtf = lev ; + break ; + case DEBUG_SMT: + DB_P.d_smt = lev ; + break ; + case DEBUG_ECM: + DB_P.d_ecm = lev ; + break ; + case DEBUG_RMT: + DB_P.d_rmt = lev ; + break ; + case DEBUG_CFM: + DB_P.d_cfm = lev ; + break ; + case DEBUG_PCM: + DB_P.d_pcm = lev ; + break ; + case DEBUG_SBA: +#ifdef SBA + DB_P.d_sba = lev ; +#endif + break ; + case DEBUG_ESS: +#ifdef ESS + DB_P.d_ess = lev ; +#endif + break ; + case DB_HWM_RX: + DB_P.d_os.hwm_rx = lev ; + break ; + case DB_HWM_TX: + DB_P.d_os.hwm_tx = lev ; + break ; + case DB_HWM_GEN: + DB_P.d_os.hwm_gen = lev ; + break ; + default: + break ; + } +} +#endif diff --git a/drivers/net/skfp/hwt.c b/drivers/net/skfp/hwt.c new file mode 100644 index 000000000000..7d51b8b8bb3b --- /dev/null +++ b/drivers/net/skfp/hwt.c @@ -0,0 +1,314 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + * Timer Driver for FBI board (timer chip 82C54) + */ + +/* + * Modifications: + * + * 28-Jun-1994 sw Edit v1.6. + * MCA: Added support for the SK-NET FDDI-FM2 adapter. The + * following functions have been added(+) or modified(*): + * hwt_start(*), hwt_stop(*), hwt_restart(*), hwt_read(*) + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)hwt.c 1.13 97/04/23 (C) SK " ; +#endif + +/* + * Prototypes of local functions. + */ +/* 28-Jun-1994 sw - Note: hwt_restart() is also used in module 'drvfbi.c'. */ +/*static*/ void hwt_restart() ; + +/************************ + * + * hwt_start + * + * Start hardware timer (clock ticks are 16us). + * + * void hwt_start( + * struct s_smc *smc, + * u_long time) ; + * In + * smc - A pointer to the SMT Context structure. + * + * time - The time in units of 16us to load the timer with. + * Out + * Nothing. + * + ************************/ +#define HWT_MAX (65000) + +void hwt_start(smc, time) +struct s_smc *smc ; +u_long time ; +{ + u_short cnt ; + + if (time > HWT_MAX) + time = HWT_MAX ; + + smc->hw.t_start = time ; + smc->hw.t_stop = 0L ; + + cnt = (u_short)time ; + /* + * if time < 16 us + * time = 16 us + */ + if (!cnt) + cnt++ ; +#ifndef PCI + /* + * 6.25MHz -> CLK0 : T0 (cnt0 = 16us) -> OUT0 + * OUT0 -> CLK1 : T1 (cnt1) OUT1 -> ISRA(IS_TIMINT) + */ + OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ; /* counter 1, mode 0 */ + OUT_82c54_TIMER(1,cnt & 0xff) ; /* LSB */ + OUT_82c54_TIMER(1,(cnt>>8) & 0xff) ; /* MSB */ + /* + * start timer by switching counter 0 to mode 3 + * T0 resolution 16 us (CLK0=0.16us) + */ + OUT_82c54_TIMER(3,0<<6 | 3<<4 | 3<<1) ; /* counter 0, mode 3 */ + OUT_82c54_TIMER(0,100) ; /* LSB */ + OUT_82c54_TIMER(0,0) ; /* MSB */ +#else /* PCI */ + outpd(ADDR(B2_TI_INI), (u_long) cnt * 200) ; /* Load timer value. */ + outpw(ADDR(B2_TI_CRTL), TIM_START) ; /* Start timer. */ +#endif /* PCI */ + smc->hw.timer_activ = TRUE ; +} + +/************************ + * + * hwt_stop + * + * Stop hardware timer. + * + * void hwt_stop( + * struct s_smc *smc) ; + * In + * smc - A pointer to the SMT Context structure. + * Out + * Nothing. + * + ************************/ +void hwt_stop(smc) +struct s_smc *smc ; +{ +#ifndef PCI + /* stop counter 0 by switching to mode 0 */ + OUT_82c54_TIMER(3,0<<6 | 3<<4 | 0<<1) ; /* counter 0, mode 0 */ + OUT_82c54_TIMER(0,0) ; /* LSB */ + OUT_82c54_TIMER(0,0) ; /* MSB */ +#else /* PCI */ + outpw(ADDR(B2_TI_CRTL), TIM_STOP) ; + outpw(ADDR(B2_TI_CRTL), TIM_CL_IRQ) ; +#endif /* PCI */ + + smc->hw.timer_activ = FALSE ; +} + +/************************ + * + * hwt_init + * + * Initialize hardware timer. + * + * void hwt_init( + * struct s_smc *smc) ; + * In + * smc - A pointer to the SMT Context structure. + * Out + * Nothing. + * + ************************/ +void hwt_init(smc) +struct s_smc *smc ; +{ + smc->hw.t_start = 0 ; + smc->hw.t_stop = 0 ; + smc->hw.timer_activ = FALSE ; + + hwt_restart(smc) ; +} + +/************************ + * + * hwt_restart + * + * Clear timer interrupt. + * + * void hwt_restart( + * struct s_smc *smc) ; + * In + * smc - A pointer to the SMT Context structure. + * Out + * Nothing. + * + ************************/ +void hwt_restart(smc) +struct s_smc *smc ; +{ + hwt_stop(smc) ; +#ifndef PCI + OUT_82c54_TIMER(3,1<<6 | 3<<4 | 0<<1) ; /* counter 1, mode 0 */ + OUT_82c54_TIMER(1,1 ) ; /* LSB */ + OUT_82c54_TIMER(1,0 ) ; /* MSB */ +#endif +} + +/************************ + * + * hwt_read + * + * Stop hardware timer and read time elapsed since last start. + * + * u_long hwt_read(smc) ; + * In + * smc - A pointer to the SMT Context structure. + * Out + * The elapsed time since last start in units of 16us. + * + ************************/ +u_long hwt_read(smc) +struct s_smc *smc ; +{ + u_short tr ; +#ifndef PCI + u_short is ; +#else + u_long is ; +#endif + + if (smc->hw.timer_activ) { + hwt_stop(smc) ; +#ifndef PCI + OUT_82c54_TIMER(3,1<<6) ; /* latch command */ + tr = IN_82c54_TIMER(1) & 0xff ; + tr += (IN_82c54_TIMER(1) & 0xff)<<8 ; +#else /* PCI */ + tr = (u_short)((inpd(ADDR(B2_TI_VAL))/200) & 0xffff) ; +#endif /* PCI */ + is = GET_ISR() ; + /* Check if timer expired (or wraparound). */ + if ((tr > smc->hw.t_start) || (is & IS_TIMINT)) { + hwt_restart(smc) ; + smc->hw.t_stop = smc->hw.t_start ; + } + else + smc->hw.t_stop = smc->hw.t_start - tr ; + } + return (smc->hw.t_stop) ; +} + +#ifdef PCI +/************************ + * + * hwt_quick_read + * + * Stop hardware timer and read timer value and start the timer again. + * + * u_long hwt_read(smc) ; + * In + * smc - A pointer to the SMT Context structure. + * Out + * current timer value in units of 80ns. + * + ************************/ +u_long hwt_quick_read(smc) +struct s_smc *smc ; +{ + u_long interval ; + u_long time ; + + interval = inpd(ADDR(B2_TI_INI)) ; + outpw(ADDR(B2_TI_CRTL), TIM_STOP) ; + time = inpd(ADDR(B2_TI_VAL)) ; + outpd(ADDR(B2_TI_INI),time) ; + outpw(ADDR(B2_TI_CRTL), TIM_START) ; + outpd(ADDR(B2_TI_INI),interval) ; + + return(time) ; +} + +/************************ + * + * hwt_wait_time(smc,start,duration) + * + * This function returnes after the amount of time is elapsed + * since the start time. + * + * para start start time + * duration time to wait + * + * NOTE: The fuction will return immediatly, if the timer is not + * started + ************************/ +void hwt_wait_time(smc,start,duration) +struct s_smc *smc ; +u_long start ; +long duration ; +{ + long diff ; + long interval ; + int wrapped ; + + /* + * check if timer is running + */ + if (smc->hw.timer_activ == FALSE || + hwt_quick_read(smc) == hwt_quick_read(smc)) { + return ; + } + + interval = inpd(ADDR(B2_TI_INI)) ; + if (interval > duration) { + do { + diff = (long)(start - hwt_quick_read(smc)) ; + if (diff < 0) { + diff += interval ; + } + } while (diff <= duration) ; + } + else { + diff = interval ; + wrapped = 0 ; + do { + if (!wrapped) { + if (hwt_quick_read(smc) >= start) { + diff += interval ; + wrapped = 1 ; + } + } + else { + if (hwt_quick_read(smc) < start) { + wrapped = 0 ; + } + } + } while (diff <= duration) ; + } +} +#endif diff --git a/drivers/net/skfp/lnkstat.c b/drivers/net/skfp/lnkstat.c new file mode 100644 index 000000000000..7d2d94eaee0d --- /dev/null +++ b/drivers/net/skfp/lnkstat.c @@ -0,0 +1,209 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + IBM FDDI read error log function +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/lnkstat.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)lnkstat.c 1.8 97/04/11 (C) SK " ; +#endif + +#ifdef sun +#define _far +#endif + +#define EL_IS_OK(x,l) ((((int)&(((struct s_error_log *)0)->x)) + \ + sizeof(er->x)) <= l) + +/* + BEGIN_MANUAL_ENTRY(if,func;others;11) + + u_long smt_get_error_word(smc) + struct s_smc *smc ; + +Function DOWNCALL (SMT, lnkstat.c) + This functions returns the SMT error work for AIX events. + +Return smt_error_word These bits are supported: + + SMT_ERL_ALC == [PS/PA].fddiPORTLerFlag + SMT_ERL_BLC == [PB].fddiPORTLerFlag + SMT_ERL_NCC == fddiMACNotCopiedFlag + SMT_ERL_FEC == fddiMACFrameErrorFlag + + END_MANUAL_ENTRY() + */ +u_long smt_get_error_word(smc) +struct s_smc *smc ; +{ + u_long st; + + /* + * smt error word low + */ + st = 0 ; + if (smc->s.sas == SMT_SAS) { + if (smc->mib.p[PS].fddiPORTLerFlag) + st |= SMT_ERL_ALC ; + } + else { + if (smc->mib.p[PA].fddiPORTLerFlag) + st |= SMT_ERL_ALC ; + if (smc->mib.p[PB].fddiPORTLerFlag) + st |= SMT_ERL_BLC ; + } + if (smc->mib.m[MAC0].fddiMACNotCopiedFlag) + st |= SMT_ERL_NCC ; /* not copied condition */ + if (smc->mib.m[MAC0].fddiMACFrameErrorFlag) + st |= SMT_ERL_FEC ; /* frame error condition */ + + return st; +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;11) + + u_long smt_get_event_word(smc) + struct s_smc *smc ; + +Function DOWNCALL (SMT, lnkstat.c) + This functions returns the SMT event work for AIX events. + +Return smt_event_word always 0 + + END_MANUAL_ENTRY() + */ +u_long smt_get_event_word(smc) +struct s_smc *smc ; +{ + return (u_long) 0; +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;11) + + u_long smt_get_port_event_word(smc) + struct s_smc *smc ; + +Function DOWNCALL (SMT, lnkstat.c) + This functions returns the SMT port event work for AIX events. + +Return smt_port_event_word always 0 + + END_MANUAL_ENTRY() + */ +u_long smt_get_port_event_word(smc) +struct s_smc *smc ; +{ + return (u_long) 0; +} + +/* + BEGIN_MANUAL_ENTRY(if,func;others;11) + + u_long smt_read_errorlog(smc,p,len) + struct s_smc *smc ; + char _far *p ; + int len ; + +Function DOWNCALL (SMT, lnkstat.c) + This functions returns the SMT error log field for AIX events. + +Para p pointer to the error log field + len len of the error log field + +Return len used len of the error log field + + END_MANUAL_ENTRY() + */ +int smt_read_errorlog(smc,p,len) +struct s_smc *smc ; +char _far *p ; +int len ; +{ + int i ; + int st ; + struct s_error_log _far *er ; + + er = (struct s_error_log _far *) p ; + if (len > sizeof(struct s_error_log)) + len = sizeof(struct s_error_log) ; + for (i = 0 ; i < len ; i++) + *p++ = 0 ; + /* + * set count + */ + if (EL_IS_OK(set_count_high,len)) { + er->set_count_low = (u_short)smc->mib.fddiSMTSetCount.count ; + er->set_count_high = + (u_short)(smc->mib.fddiSMTSetCount.count >> 16L) ; + } + /* + * aci + */ + if (EL_IS_OK(aci_id_code,len)) { + er->aci_id_code = 0 ; + } + /* + * purge counter is missed frames; 16 bits only + */ + if (EL_IS_OK(purge_frame_counter,len)) { + if (smc->mib.m[MAC0].fddiMACCopied_Ct > 0xffff) + er->purge_frame_counter = 0xffff ; + else + er->purge_frame_counter = + (u_short)smc->mib.m[MAC0].fddiMACCopied_Ct ; + } + /* + * CMT and RMT state machines + */ + if (EL_IS_OK(ecm_state,len)) + er->ecm_state = smc->mib.fddiSMTECMState ; + + if (EL_IS_OK(pcm_b_state,len)) { + if (smc->s.sas == SMT_SAS) { + er->pcm_a_state = smc->y[PS].mib->fddiPORTPCMState ; + er->pcm_b_state = 0 ; + } + else { + er->pcm_a_state = smc->y[PA].mib->fddiPORTPCMState ; + er->pcm_b_state = smc->y[PB].mib->fddiPORTPCMState ; + } + } + if (EL_IS_OK(cfm_state,len)) + er->cfm_state = smc->mib.fddiSMTCF_State ; + if (EL_IS_OK(rmt_state,len)) + er->rmt_state = smc->mib.m[MAC0].fddiMACRMTState ; + + /* + * smt error word low (we only need the low order 16 bits.) + */ + + st = smt_get_error_word(smc) & 0xffff ; + + if (EL_IS_OK(smt_error_low,len)) + er->smt_error_low = st ; + + if (EL_IS_OK(ucode_version_level,len)) + er->ucode_version_level = 0x0101 ; + return(len) ; +} diff --git a/drivers/net/skfp/pcmplc.c b/drivers/net/skfp/pcmplc.c new file mode 100644 index 000000000000..5479b27ea0fa --- /dev/null +++ b/drivers/net/skfp/pcmplc.c @@ -0,0 +1,2094 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + PCM + Physical Connection Management +*/ + +/* + * Hardware independant state machine implemantation + * The following external SMT functions are referenced : + * + * queue_event() + * smt_timer_start() + * smt_timer_stop() + * + * The following external HW dependant functions are referenced : + * sm_pm_control() + * sm_ph_linestate() + * sm_pm_ls_latch() + * + * The following HW dependant events are required : + * PC_QLS + * PC_ILS + * PC_HLS + * PC_MLS + * PC_NSE + * PC_LEM + * + */ + + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/supern_2.h" +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)pcmplc.c 2.55 99/08/05 (C) SK " ; +#endif + +#ifdef FDDI_MIB +extern int snmp_fddi_trap( +#ifdef ANSIC +struct s_smc * smc, int type, int index +#endif +); +#endif +#ifdef CONCENTRATOR +extern int plc_is_installed( +#ifdef ANSIC +struct s_smc *smc , +int p +#endif +) ; +#endif +/* + * FSM Macros + */ +#define AFLAG (0x20) +#define GO_STATE(x) (mib->fddiPORTPCMState = (x)|AFLAG) +#define ACTIONS_DONE() (mib->fddiPORTPCMState &= ~AFLAG) +#define ACTIONS(x) (x|AFLAG) + +/* + * PCM states + */ +#define PC0_OFF 0 +#define PC1_BREAK 1 +#define PC2_TRACE 2 +#define PC3_CONNECT 3 +#define PC4_NEXT 4 +#define PC5_SIGNAL 5 +#define PC6_JOIN 6 +#define PC7_VERIFY 7 +#define PC8_ACTIVE 8 +#define PC9_MAINT 9 + +#ifdef DEBUG +/* + * symbolic state names + */ +static const char * const pcm_states[] = { + "PC0_OFF","PC1_BREAK","PC2_TRACE","PC3_CONNECT","PC4_NEXT", + "PC5_SIGNAL","PC6_JOIN","PC7_VERIFY","PC8_ACTIVE","PC9_MAINT" +} ; + +/* + * symbolic event names + */ +static const char * const pcm_events[] = { + "NONE","PC_START","PC_STOP","PC_LOOP","PC_JOIN","PC_SIGNAL", + "PC_REJECT","PC_MAINT","PC_TRACE","PC_PDR", + "PC_ENABLE","PC_DISABLE", + "PC_QLS","PC_ILS","PC_MLS","PC_HLS","PC_LS_PDR","PC_LS_NONE", + "PC_TIMEOUT_TB_MAX","PC_TIMEOUT_TB_MIN", + "PC_TIMEOUT_C_MIN","PC_TIMEOUT_T_OUT", + "PC_TIMEOUT_TL_MIN","PC_TIMEOUT_T_NEXT","PC_TIMEOUT_LCT", + "PC_NSE","PC_LEM" +} ; +#endif + +#ifdef MOT_ELM +/* + * PCL-S control register + * this register in the PLC-S controls the scrambling parameters + */ +#define PLCS_CONTROL_C_U 0 +#define PLCS_CONTROL_C_S (PL_C_SDOFF_ENABLE | PL_C_SDON_ENABLE | \ + PL_C_CIPHER_ENABLE) +#define PLCS_FASSERT_U 0 +#define PLCS_FASSERT_S 0xFd76 /* 52.0 us */ +#define PLCS_FDEASSERT_U 0 +#define PLCS_FDEASSERT_S 0 +#else /* nMOT_ELM */ +/* + * PCL-S control register + * this register in the PLC-S controls the scrambling parameters + * can be patched for ANSI compliance if standard changes + */ +static const u_char plcs_control_c_u[17] = "PLC_CNTRL_C_U=\0\0" ; +static const u_char plcs_control_c_s[17] = "PLC_CNTRL_C_S=\01\02" ; + +#define PLCS_CONTROL_C_U (plcs_control_c_u[14] | (plcs_control_c_u[15]<<8)) +#define PLCS_CONTROL_C_S (plcs_control_c_s[14] | (plcs_control_c_s[15]<<8)) +#endif /* nMOT_ELM */ + +/* + * external vars + */ +/* struct definition see 'cmtdef.h' (also used by CFM) */ + +#define PS_OFF 0 +#define PS_BIT3 1 +#define PS_BIT4 2 +#define PS_BIT7 3 +#define PS_LCT 4 +#define PS_BIT8 5 +#define PS_JOIN 6 +#define PS_ACTIVE 7 + +#define LCT_LEM_MAX 255 + +/* + * PLC timing parameter + */ + +#define PLC_MS(m) ((int)((0x10000L-(m*100000L/2048)))) +#define SLOW_TL_MIN PLC_MS(6) +#define SLOW_C_MIN PLC_MS(10) + +static const struct plt { + int timer ; /* relative plc timer address */ + int para ; /* default timing parameters */ +} pltm[] = { + { PL_C_MIN, SLOW_C_MIN }, /* min t. to remain Connect State */ + { PL_TL_MIN, SLOW_TL_MIN }, /* min t. to transmit a Line State */ + { PL_TB_MIN, TP_TB_MIN }, /* min break time */ + { PL_T_OUT, TP_T_OUT }, /* Signaling timeout */ + { PL_LC_LENGTH, TP_LC_LENGTH }, /* Link Confidence Test Time */ + { PL_T_SCRUB, TP_T_SCRUB }, /* Scrub Time == MAC TVX time ! */ + { PL_NS_MAX, TP_NS_MAX }, /* max t. that noise is tolerated */ + { 0,0 } +} ; + +/* + * interrupt mask + */ +#ifdef SUPERNET_3 +/* + * Do we need the EBUF error during signaling, too, to detect SUPERNET_3 + * PLL bug? + */ +static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK | + PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR; +#else /* SUPERNET_3 */ +/* + * We do NOT need the elasticity buffer error during signaling. + */ +static int plc_imsk_na = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK | + PL_PCM_ENABLED | PL_SELF_TEST ; +#endif /* SUPERNET_3 */ +static int plc_imsk_act = PL_PCM_CODE | PL_TRACE_PROP | PL_PCM_BREAK | + PL_PCM_ENABLED | PL_SELF_TEST | PL_EBUF_ERR; + +/* external functions */ +void all_selection_criteria (); + +/* internal functions */ +static void pcm_fsm() ; +static void pc_rcode_actions() ; +static void pc_tcode_actions() ; +static void reset_lem_struct() ; +static void plc_init() ; +static void sm_ph_lem_start() ; +static void sm_ph_lem_stop() ; +static void sm_ph_linestate() ; +static void real_init_plc() ; + +/* + * SMT timer interface + * start PCM timer 0 + */ +static void start_pcm_timer0(smc,value,event,phy) +struct s_smc *smc ; +u_long value; +int event; +struct s_phy *phy; +{ + phy->timer0_exp = FALSE ; /* clear timer event flag */ + smt_timer_start(smc,&phy->pcm_timer0,value, + EV_TOKEN(EVENT_PCM+phy->np,event)) ; +} +/* + * SMT timer interface + * stop PCM timer 0 + */ +static void stop_pcm_timer0(smc,phy) +struct s_smc *smc ; +struct s_phy *phy; +{ + if (phy->pcm_timer0.tm_active) + smt_timer_stop(smc,&phy->pcm_timer0) ; +} + +/* + init PCM state machine (called by driver) + clear all PCM vars and flags +*/ +void pcm_init(smc) +struct s_smc *smc ; +{ + int i ; + int np ; + struct s_phy *phy ; + struct fddi_mib_p *mib ; + + for (np = 0,phy = smc->y ; np < NUMPHYS ; np++,phy++) { + /* Indicates the type of PHY being used */ + mib = phy->mib ; + mib->fddiPORTPCMState = ACTIONS(PC0_OFF) ; + phy->np = np ; + switch (smc->s.sas) { +#ifdef CONCENTRATOR + case SMT_SAS : + mib->fddiPORTMy_Type = (np == PS) ? TS : TM ; + break ; + case SMT_DAS : + mib->fddiPORTMy_Type = (np == PA) ? TA : + (np == PB) ? TB : TM ; + break ; + case SMT_NAC : + mib->fddiPORTMy_Type = TM ; + break; +#else + case SMT_SAS : + mib->fddiPORTMy_Type = (np == PS) ? TS : TNONE ; + mib->fddiPORTHardwarePresent = (np == PS) ? TRUE : + FALSE ; +#ifndef SUPERNET_3 + smc->y[PA].mib->fddiPORTPCMState = PC0_OFF ; +#else + smc->y[PB].mib->fddiPORTPCMState = PC0_OFF ; +#endif + break ; + case SMT_DAS : + mib->fddiPORTMy_Type = (np == PB) ? TB : TA ; + break ; +#endif + } + /* + * set PMD-type + */ + phy->pmd_scramble = 0 ; + switch (phy->pmd_type[PMD_SK_PMD]) { + case 'P' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ; + break ; + case 'L' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_LCF ; + break ; + case 'D' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ; + break ; + case 'S' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ; + phy->pmd_scramble = TRUE ; + break ; + case 'U' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ; + phy->pmd_scramble = TRUE ; + break ; + case '1' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ; + break ; + case '2' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ; + break ; + case '3' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE2 ; + break ; + case '4' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_SINGLE1 ; + break ; + case 'H' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ; + break ; + case 'I' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ; + break ; + case 'G' : + mib->fddiPORTPMDClass = MIB_PMDCLASS_TP ; + break ; + default: + mib->fddiPORTPMDClass = MIB_PMDCLASS_UNKNOWN ; + break ; + } + /* + * A and B port can be on primary and secondary path + */ + switch (mib->fddiPORTMy_Type) { + case TA : + mib->fddiPORTAvailablePaths |= MIB_PATH_S ; + mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ; + mib->fddiPORTRequestedPaths[2] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_CON_ALTER | + MIB_P_PATH_SEC_PREFER ; + mib->fddiPORTRequestedPaths[3] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_CON_ALTER | + MIB_P_PATH_SEC_PREFER | + MIB_P_PATH_THRU ; + break ; + case TB : + mib->fddiPORTAvailablePaths |= MIB_PATH_S ; + mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ; + mib->fddiPORTRequestedPaths[2] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_PRIM_PREFER ; + mib->fddiPORTRequestedPaths[3] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_PRIM_PREFER | + MIB_P_PATH_CON_PREFER | + MIB_P_PATH_THRU ; + break ; + case TS : + mib->fddiPORTAvailablePaths |= MIB_PATH_S ; + mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ; + mib->fddiPORTRequestedPaths[2] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_CON_ALTER | + MIB_P_PATH_PRIM_PREFER ; + mib->fddiPORTRequestedPaths[3] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_CON_ALTER | + MIB_P_PATH_PRIM_PREFER ; + break ; + case TM : + mib->fddiPORTRequestedPaths[1] = MIB_P_PATH_LOCAL ; + mib->fddiPORTRequestedPaths[2] = + MIB_P_PATH_LOCAL | + MIB_P_PATH_SEC_ALTER | + MIB_P_PATH_PRIM_ALTER ; + mib->fddiPORTRequestedPaths[3] = 0 ; + break ; + } + + phy->pc_lem_fail = FALSE ; + mib->fddiPORTPCMStateX = mib->fddiPORTPCMState ; + mib->fddiPORTLCTFail_Ct = 0 ; + mib->fddiPORTBS_Flag = 0 ; + mib->fddiPORTCurrentPath = MIB_PATH_ISOLATED ; + mib->fddiPORTNeighborType = TNONE ; + phy->ls_flag = 0 ; + phy->rc_flag = 0 ; + phy->tc_flag = 0 ; + phy->td_flag = 0 ; + if (np >= PM) + phy->phy_name = '0' + np - PM ; + else + phy->phy_name = 'A' + np ; + phy->wc_flag = FALSE ; /* set by SMT */ + memset((char *)&phy->lem,0,sizeof(struct lem_counter)) ; + reset_lem_struct(phy) ; + memset((char *)&phy->plc,0,sizeof(struct s_plc)) ; + phy->plc.p_state = PS_OFF ; + for (i = 0 ; i < NUMBITS ; i++) { + phy->t_next[i] = 0 ; + } + } + real_init_plc(smc) ; +} + +void init_plc(smc) +struct s_smc *smc ; +{ + SK_UNUSED(smc) ; + + /* + * dummy + * this is an obsolete public entry point that has to remain + * for compat. It is used by various drivers. + * the work is now done in real_init_plc() + * which is called from pcm_init() ; + */ +} + +static void real_init_plc(smc) +struct s_smc *smc ; +{ + int p ; + + for (p = 0 ; p < NUMPHYS ; p++) + plc_init(smc,p) ; +} + +static void plc_init(smc,p) +struct s_smc *smc ; +int p; +{ + int i ; +#ifndef MOT_ELM + int rev ; /* Revision of PLC-x */ +#endif /* MOT_ELM */ + + /* transit PCM state machine to MAINT state */ + outpw(PLC(p,PL_CNTRL_B),0) ; + outpw(PLC(p,PL_CNTRL_B),PL_PCM_STOP) ; + outpw(PLC(p,PL_CNTRL_A),0) ; + + /* + * if PLC-S then set control register C + */ +#ifndef MOT_ELM + rev = inpw(PLC(p,PL_STATUS_A)) & PLC_REV_MASK ; + if (rev != PLC_REVISION_A) +#endif /* MOT_ELM */ + { + if (smc->y[p].pmd_scramble) { + outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_S) ; +#ifdef MOT_ELM + outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_S) ; + outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_S) ; +#endif /* MOT_ELM */ + } + else { + outpw(PLC(p,PL_CNTRL_C),PLCS_CONTROL_C_U) ; +#ifdef MOT_ELM + outpw(PLC(p,PL_T_FOT_ASS),PLCS_FASSERT_U) ; + outpw(PLC(p,PL_T_FOT_DEASS),PLCS_FDEASSERT_U) ; +#endif /* MOT_ELM */ + } + } + + /* + * set timer register + */ + for ( i = 0 ; pltm[i].timer; i++) /* set timer parameter reg */ + outpw(PLC(p,pltm[i].timer),pltm[i].para) ; + + (void)inpw(PLC(p,PL_INTR_EVENT)) ; /* clear interrupt event reg */ + plc_clear_irq(smc,p) ; + outpw(PLC(p,PL_INTR_MASK),plc_imsk_na); /* enable non active irq's */ + + /* + * if PCM is configured for class s, it will NOT go to the + * REMOVE state if offline (page 3-36;) + * in the concentrator, all inactive PHYS always must be in + * the remove state + * there's no real need to use this feature at all .. + */ +#ifndef CONCENTRATOR + if ((smc->s.sas == SMT_SAS) && (p == PS)) { + outpw(PLC(p,PL_CNTRL_B),PL_CLASS_S) ; + } +#endif +} + +/* + * control PCM state machine + */ +static void plc_go_state(smc,p,state) +struct s_smc *smc ; +int p; +int state; +{ + HW_PTR port ; + int val ; + + SK_UNUSED(smc) ; + + port = (HW_PTR) (PLC(p,PL_CNTRL_B)) ; + val = inpw(port) & ~(PL_PCM_CNTRL | PL_MAINT) ; + outpw(port,val) ; + outpw(port,val | state) ; +} + +/* + * read current line state (called by ECM & PCM) + */ +int sm_pm_get_ls(smc,phy) +struct s_smc *smc ; +int phy; +{ + int state ; + +#ifdef CONCENTRATOR + if (!plc_is_installed(smc,phy)) + return(PC_QLS) ; +#endif + + state = inpw(PLC(phy,PL_STATUS_A)) & PL_LINE_ST ; + switch(state) { + case PL_L_QLS: + state = PC_QLS ; + break ; + case PL_L_MLS: + state = PC_MLS ; + break ; + case PL_L_HLS: + state = PC_HLS ; + break ; + case PL_L_ILS4: + case PL_L_ILS16: + state = PC_ILS ; + break ; + case PL_L_ALS: + state = PC_LS_PDR ; + break ; + default : + state = PC_LS_NONE ; + } + return(state) ; +} + +static int plc_send_bits(smc,phy,len) +struct s_smc *smc ; +struct s_phy *phy; +int len; +{ + int np = phy->np ; /* PHY index */ + int n ; + int i ; + + SK_UNUSED(smc) ; + + /* create bit vector */ + for (i = len-1,n = 0 ; i >= 0 ; i--) { + n = (n<<1) | phy->t_val[phy->bitn+i] ; + } + if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) { +#if 0 + printf("PL_PCM_SIGNAL is set\n") ; +#endif + return(1) ; + } + /* write bit[n] & length = 1 to regs */ + outpw(PLC(np,PL_VECTOR_LEN),len-1) ; /* len=nr-1 */ + outpw(PLC(np,PL_XMIT_VECTOR),n) ; +#ifdef DEBUG +#if 1 +#ifdef DEBUG_BRD + if (smc->debug.d_plc & 0x80) +#else + if (debug.d_plc & 0x80) +#endif + printf("SIGNALING bit %d .. %d\n",phy->bitn,phy->bitn+len-1) ; +#endif +#endif + return(0) ; +} + +/* + * config plc muxes + */ +void plc_config_mux(smc,mux) +struct s_smc *smc ; +int mux ; +{ + if (smc->s.sas != SMT_DAS) + return ; + if (mux == MUX_WRAPB) { + SETMASK(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ; + SETMASK(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP,PL_SC_REM_LOOP) ; + } + else { + CLEAR(PLC(PA,PL_CNTRL_B),PL_CONFIG_CNTRL) ; + CLEAR(PLC(PA,PL_CNTRL_A),PL_SC_REM_LOOP) ; + } + CLEAR(PLC(PB,PL_CNTRL_B),PL_CONFIG_CNTRL) ; + CLEAR(PLC(PB,PL_CNTRL_A),PL_SC_REM_LOOP) ; +} + +/* + PCM state machine + called by dispatcher & fddi_init() (driver) + do + display state change + process event + until SM is stable +*/ +void pcm(smc,np,event) +struct s_smc *smc ; +const int np; +int event; +{ + int state ; + int oldstate ; + struct s_phy *phy ; + struct fddi_mib_p *mib ; + +#ifndef CONCENTRATOR + /* + * ignore 2nd PHY if SAS + */ + if ((np != PS) && (smc->s.sas == SMT_SAS)) + return ; +#endif + phy = &smc->y[np] ; + mib = phy->mib ; + oldstate = mib->fddiPORTPCMState ; + do { + DB_PCM("PCM %c: state %s", + phy->phy_name, + (mib->fddiPORTPCMState & AFLAG) ? "ACTIONS " : "") ; + DB_PCM("%s, event %s\n", + pcm_states[mib->fddiPORTPCMState & ~AFLAG], + pcm_events[event]) ; + state = mib->fddiPORTPCMState ; + pcm_fsm(smc,phy,event) ; + event = 0 ; + } while (state != mib->fddiPORTPCMState) ; + /* + * because the PLC does the bit signaling for us, + * we're always in SIGNAL state + * the MIB want's to see CONNECT + * we therefore fake an entry in the MIB + */ + if (state == PC5_SIGNAL) + mib->fddiPORTPCMStateX = PC3_CONNECT ; + else + mib->fddiPORTPCMStateX = state ; + +#ifndef SLIM_SMT + /* + * path change + */ + if ( mib->fddiPORTPCMState != oldstate && + ((oldstate == PC8_ACTIVE) || (mib->fddiPORTPCMState == PC8_ACTIVE))) { + smt_srf_event(smc,SMT_EVENT_PORT_PATH_CHANGE, + (int) (INDEX_PORT+ phy->np),0) ; + } +#endif + +#ifdef FDDI_MIB + /* check whether a snmp-trap has to be sent */ + + if ( mib->fddiPORTPCMState != oldstate ) { + /* a real state change took place */ + DB_SNMP ("PCM from %d to %d\n", oldstate, mib->fddiPORTPCMState); + if ( mib->fddiPORTPCMState == PC0_OFF ) { + /* send first trap */ + snmp_fddi_trap (smc, 1, (int) mib->fddiPORTIndex ); + } else if ( oldstate == PC0_OFF ) { + /* send second trap */ + snmp_fddi_trap (smc, 2, (int) mib->fddiPORTIndex ); + } else if ( mib->fddiPORTPCMState != PC2_TRACE && + oldstate == PC8_ACTIVE ) { + /* send third trap */ + snmp_fddi_trap (smc, 3, (int) mib->fddiPORTIndex ); + } else if ( mib->fddiPORTPCMState == PC8_ACTIVE ) { + /* send fourth trap */ + snmp_fddi_trap (smc, 4, (int) mib->fddiPORTIndex ); + } + } +#endif + + pcm_state_change(smc,np,state) ; +} + +/* + * PCM state machine + */ +static void pcm_fsm(smc,phy,cmd) +struct s_smc *smc ; +struct s_phy *phy; +int cmd; +{ + int i ; + int np = phy->np ; /* PHY index */ + struct s_plc *plc ; + struct fddi_mib_p *mib ; +#ifndef MOT_ELM + u_short plc_rev ; /* Revision of the plc */ +#endif /* nMOT_ELM */ + + plc = &phy->plc ; + mib = phy->mib ; + + /* + * general transitions independant of state + */ + switch (cmd) { + case PC_STOP : + /*PC00-PC80*/ + if (mib->fddiPORTPCMState != PC9_MAINT) { + GO_STATE(PC0_OFF) ; + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_PORT_EVENT, (u_long) FDDI_PORT_STOP, + smt_get_port_event_word(smc)); + } + return ; + case PC_START : + /*PC01-PC81*/ + if (mib->fddiPORTPCMState != PC9_MAINT) + GO_STATE(PC1_BREAK) ; + return ; + case PC_DISABLE : + /* PC09-PC99 */ + GO_STATE(PC9_MAINT) ; + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_PORT_EVENT, (u_long) FDDI_PORT_DISABLED, + smt_get_port_event_word(smc)); + return ; + case PC_TIMEOUT_LCT : + /* if long or extended LCT */ + stop_pcm_timer0(smc,phy) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ; + /* end of LCT is indicate by PCM_CODE (initiate PCM event) */ + return ; + } + + switch(mib->fddiPORTPCMState) { + case ACTIONS(PC0_OFF) : + stop_pcm_timer0(smc,phy) ; + outpw(PLC(np,PL_CNTRL_A),0) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ; + sm_ph_lem_stop(smc,np) ; /* disable LEM */ + phy->cf_loop = FALSE ; + phy->cf_join = FALSE ; + queue_event(smc,EVENT_CFM,CF_JOIN+np) ; + plc_go_state(smc,np,PL_PCM_STOP) ; + mib->fddiPORTConnectState = PCM_DISABLED ; + ACTIONS_DONE() ; + break ; + case PC0_OFF: + /*PC09*/ + if (cmd == PC_MAINT) { + GO_STATE(PC9_MAINT) ; + break ; + } + break ; + case ACTIONS(PC1_BREAK) : + /* Stop the LCT timer if we came from Signal state */ + stop_pcm_timer0(smc,phy) ; + ACTIONS_DONE() ; + plc_go_state(smc,np,0) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ; + sm_ph_lem_stop(smc,np) ; /* disable LEM */ + /* + * if vector is already loaded, go to OFF to clear PCM_SIGNAL + */ +#if 0 + if (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL) { + plc_go_state(smc,np,PL_PCM_STOP) ; + /* TB_MIN ? */ + } +#endif + /* + * Go to OFF state in any case. + */ + plc_go_state(smc,np,PL_PCM_STOP) ; + + if (mib->fddiPORTPC_Withhold == PC_WH_NONE) + mib->fddiPORTConnectState = PCM_CONNECTING ; + phy->cf_loop = FALSE ; + phy->cf_join = FALSE ; + queue_event(smc,EVENT_CFM,CF_JOIN+np) ; + phy->ls_flag = FALSE ; + phy->pc_mode = PM_NONE ; /* needed by CFM */ + phy->bitn = 0 ; /* bit signaling start bit */ + for (i = 0 ; i < 3 ; i++) + pc_tcode_actions(smc,i,phy) ; + + /* Set the non-active interrupt mask register */ + outpw(PLC(np,PL_INTR_MASK),plc_imsk_na) ; + + /* + * If the LCT was stopped. There might be a + * PCM_CODE interrupt event present. + * This must be cleared. + */ + (void)inpw(PLC(np,PL_INTR_EVENT)) ; +#ifndef MOT_ELM + /* Get the plc revision for revision dependent code */ + plc_rev = inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK ; + + if (plc_rev != PLC_REV_SN3) +#endif /* MOT_ELM */ + { + /* + * No supernet III PLC, so set Xmit verctor and + * length BEFORE starting the state machine. + */ + if (plc_send_bits(smc,phy,3)) { + return ; + } + } + + /* + * Now give the Start command. + * - The start command shall be done before setting the bits + * to be signaled. (In PLC-S description and PLCS in SN3. + * - The start command shall be issued AFTER setting the + * XMIT vector and the XMIT length register. + * + * We do it exactly according this specs for the old PLC and + * the new PLCS inside the SN3. + * For the usual PLCS we try it the way it is done for the + * old PLC and set the XMIT registers again, if the PLC is + * not in SIGNAL state. This is done according to an PLCS + * errata workaround. + */ + + plc_go_state(smc,np,PL_PCM_START) ; + + /* + * workaround for PLC-S eng. sample errata + */ +#ifdef MOT_ELM + if (!(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL)) +#else /* nMOT_ELM */ + if (((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) != + PLC_REVISION_A) && + !(inpw(PLC(np,PL_STATUS_B)) & PL_PCM_SIGNAL)) +#endif /* nMOT_ELM */ + { + /* + * Set register again (PLCS errata) or the first time + * (new SN3 PLCS). + */ + (void) plc_send_bits(smc,phy,3) ; + } + /* + * end of workaround + */ + + GO_STATE(PC5_SIGNAL) ; + plc->p_state = PS_BIT3 ; + plc->p_bits = 3 ; + plc->p_start = 0 ; + + break ; + case PC1_BREAK : + break ; + case ACTIONS(PC2_TRACE) : + plc_go_state(smc,np,PL_PCM_TRACE) ; + ACTIONS_DONE() ; + break ; + case PC2_TRACE : + break ; + + case PC3_CONNECT : /* these states are done by hardware */ + case PC4_NEXT : + break ; + + case ACTIONS(PC5_SIGNAL) : + ACTIONS_DONE() ; + case PC5_SIGNAL : + if ((cmd != PC_SIGNAL) && (cmd != PC_TIMEOUT_LCT)) + break ; + switch (plc->p_state) { + case PS_BIT3 : + for (i = 0 ; i <= 2 ; i++) + pc_rcode_actions(smc,i,phy) ; + pc_tcode_actions(smc,3,phy) ; + plc->p_state = PS_BIT4 ; + plc->p_bits = 1 ; + plc->p_start = 3 ; + phy->bitn = 3 ; + if (plc_send_bits(smc,phy,1)) { + return ; + } + break ; + case PS_BIT4 : + pc_rcode_actions(smc,3,phy) ; + for (i = 4 ; i <= 6 ; i++) + pc_tcode_actions(smc,i,phy) ; + plc->p_state = PS_BIT7 ; + plc->p_bits = 3 ; + plc->p_start = 4 ; + phy->bitn = 4 ; + if (plc_send_bits(smc,phy,3)) { + return ; + } + break ; + case PS_BIT7 : + for (i = 3 ; i <= 6 ; i++) + pc_rcode_actions(smc,i,phy) ; + plc->p_state = PS_LCT ; + plc->p_bits = 0 ; + plc->p_start = 7 ; + phy->bitn = 7 ; + sm_ph_lem_start(smc,np,(int)smc->s.lct_short) ; /* enable LEM */ + /* start LCT */ + i = inpw(PLC(np,PL_CNTRL_B)) & ~PL_PC_LOOP ; + outpw(PLC(np,PL_CNTRL_B),i) ; /* must be cleared */ + outpw(PLC(np,PL_CNTRL_B),i | PL_RLBP) ; + break ; + case PS_LCT : + /* check for local LCT failure */ + pc_tcode_actions(smc,7,phy) ; + /* + * set tval[7] + */ + plc->p_state = PS_BIT8 ; + plc->p_bits = 1 ; + plc->p_start = 7 ; + phy->bitn = 7 ; + if (plc_send_bits(smc,phy,1)) { + return ; + } + break ; + case PS_BIT8 : + /* check for remote LCT failure */ + pc_rcode_actions(smc,7,phy) ; + if (phy->t_val[7] || phy->r_val[7]) { + plc_go_state(smc,np,PL_PCM_STOP) ; + GO_STATE(PC1_BREAK) ; + break ; + } + for (i = 8 ; i <= 9 ; i++) + pc_tcode_actions(smc,i,phy) ; + plc->p_state = PS_JOIN ; + plc->p_bits = 2 ; + plc->p_start = 8 ; + phy->bitn = 8 ; + if (plc_send_bits(smc,phy,2)) { + return ; + } + break ; + case PS_JOIN : + for (i = 8 ; i <= 9 ; i++) + pc_rcode_actions(smc,i,phy) ; + plc->p_state = PS_ACTIVE ; + GO_STATE(PC6_JOIN) ; + break ; + } + break ; + + case ACTIONS(PC6_JOIN) : + /* + * prevent mux error when going from WRAP_A to WRAP_B + */ + if (smc->s.sas == SMT_DAS && np == PB && + (smc->y[PA].pc_mode == PM_TREE || + smc->y[PB].pc_mode == PM_TREE)) { + SETMASK(PLC(np,PL_CNTRL_A), + PL_SC_REM_LOOP,PL_SC_REM_LOOP) ; + SETMASK(PLC(np,PL_CNTRL_B), + PL_CONFIG_CNTRL,PL_CONFIG_CNTRL) ; + } + SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ; + SETMASK(PLC(np,PL_CNTRL_B),PL_PC_JOIN,PL_PC_JOIN) ; + ACTIONS_DONE() ; + cmd = 0 ; + /* fall thru */ + case PC6_JOIN : + switch (plc->p_state) { + case PS_ACTIVE: + /*PC88b*/ + if (!phy->cf_join) { + phy->cf_join = TRUE ; + queue_event(smc,EVENT_CFM,CF_JOIN+np) ; ; + } + if (cmd == PC_JOIN) + GO_STATE(PC8_ACTIVE) ; + /*PC82*/ + if (cmd == PC_TRACE) { + GO_STATE(PC2_TRACE) ; + break ; + } + break ; + } + break ; + + case PC7_VERIFY : + break ; + + case ACTIONS(PC8_ACTIVE) : + /* + * start LEM for SMT + */ + sm_ph_lem_start(smc,(int)phy->np,LCT_LEM_MAX) ; + + phy->tr_flag = FALSE ; + mib->fddiPORTConnectState = PCM_ACTIVE ; + + /* Set the active interrupt mask register */ + outpw(PLC(np,PL_INTR_MASK),plc_imsk_act) ; + + ACTIONS_DONE() ; + break ; + case PC8_ACTIVE : + /*PC81 is done by PL_TNE_EXPIRED irq */ + /*PC82*/ + if (cmd == PC_TRACE) { + GO_STATE(PC2_TRACE) ; + break ; + } + /*PC88c: is done by TRACE_PROP irq */ + + break ; + case ACTIONS(PC9_MAINT) : + stop_pcm_timer0(smc,phy) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_PC_JOIN) ; + CLEAR(PLC(np,PL_CNTRL_B),PL_LONG) ; + CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ; /* disable LEM int. */ + sm_ph_lem_stop(smc,np) ; /* disable LEM */ + phy->cf_loop = FALSE ; + phy->cf_join = FALSE ; + queue_event(smc,EVENT_CFM,CF_JOIN+np) ; + plc_go_state(smc,np,PL_PCM_STOP) ; + mib->fddiPORTConnectState = PCM_DISABLED ; + SETMASK(PLC(np,PL_CNTRL_B),PL_MAINT,PL_MAINT) ; + sm_ph_linestate(smc,np,(int) MIB2LS(mib->fddiPORTMaint_LS)) ; + outpw(PLC(np,PL_CNTRL_A),PL_SC_BYPASS) ; + ACTIONS_DONE() ; + break ; + case PC9_MAINT : + DB_PCMN(1,"PCM %c : MAINT\n",phy->phy_name,0) ; + /*PC90*/ + if (cmd == PC_ENABLE) { + GO_STATE(PC0_OFF) ; + break ; + } + break ; + + default: + SMT_PANIC(smc,SMT_E0118, SMT_E0118_MSG) ; + break ; + } +} + +/* + * force line state on a PHY output (only in MAINT state) + */ +static void sm_ph_linestate(smc,phy,ls) +struct s_smc *smc ; +int phy; +int ls; +{ + int cntrl ; + + SK_UNUSED(smc) ; + + cntrl = (inpw(PLC(phy,PL_CNTRL_B)) & ~PL_MAINT_LS) | + PL_PCM_STOP | PL_MAINT ; + switch(ls) { + case PC_QLS: /* Force Quiet */ + cntrl |= PL_M_QUI0 ; + break ; + case PC_MLS: /* Force Master */ + cntrl |= PL_M_MASTR ; + break ; + case PC_HLS: /* Force Halt */ + cntrl |= PL_M_HALT ; + break ; + default : + case PC_ILS: /* Force Idle */ + cntrl |= PL_M_IDLE ; + break ; + case PC_LS_PDR: /* Enable repeat filter */ + cntrl |= PL_M_TPDR ; + break ; + } + outpw(PLC(phy,PL_CNTRL_B),cntrl) ; +} + + +static void reset_lem_struct(phy) +struct s_phy *phy; +{ + struct lem_counter *lem = &phy->lem ; + + phy->mib->fddiPORTLer_Estimate = 15 ; + lem->lem_float_ber = 15 * 100 ; +} + +/* + * link error monitor + */ +static void lem_evaluate(smc,phy) +struct s_smc *smc ; +struct s_phy *phy; +{ + int ber ; + u_long errors ; + struct lem_counter *lem = &phy->lem ; + struct fddi_mib_p *mib ; + int cond ; + + mib = phy->mib ; + + if (!lem->lem_on) + return ; + + errors = inpw(PLC(((int) phy->np),PL_LINK_ERR_CTR)) ; + lem->lem_errors += errors ; + mib->fddiPORTLem_Ct += errors ; + + errors = lem->lem_errors ; + /* + * calculation is called on a intervall of 8 seconds + * -> this means, that one error in 8 sec. is one of 8*125*10E6 + * the same as BER = 10E-9 + * Please note: + * -> 9 errors in 8 seconds mean: + * BER = 9 * 10E-9 and this is + * < 10E-8, so the limit of 10E-8 is not reached! + */ + + if (!errors) ber = 15 ; + else if (errors <= 9) ber = 9 ; + else if (errors <= 99) ber = 8 ; + else if (errors <= 999) ber = 7 ; + else if (errors <= 9999) ber = 6 ; + else if (errors <= 99999) ber = 5 ; + else if (errors <= 999999) ber = 4 ; + else if (errors <= 9999999) ber = 3 ; + else if (errors <= 99999999) ber = 2 ; + else if (errors <= 999999999) ber = 1 ; + else ber = 0 ; + + /* + * weighted average + */ + ber *= 100 ; + lem->lem_float_ber = lem->lem_float_ber * 7 + ber * 3 ; + lem->lem_float_ber /= 10 ; + mib->fddiPORTLer_Estimate = lem->lem_float_ber / 100 ; + if (mib->fddiPORTLer_Estimate < 4) { + mib->fddiPORTLer_Estimate = 4 ; + } + + if (lem->lem_errors) { + DB_PCMN(1,"LEM %c :\n",phy->np == PB? 'B' : 'A',0) ; + DB_PCMN(1,"errors : %ld\n",lem->lem_errors,0) ; + DB_PCMN(1,"sum_errors : %ld\n",mib->fddiPORTLem_Ct,0) ; + DB_PCMN(1,"current BER : 10E-%d\n",ber/100,0) ; + DB_PCMN(1,"float BER : 10E-(%d/100)\n",lem->lem_float_ber,0) ; + DB_PCMN(1,"avg. BER : 10E-%d\n", + mib->fddiPORTLer_Estimate,0) ; + } + + lem->lem_errors = 0L ; + +#ifndef SLIM_SMT + cond = (mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Alarm) ? + TRUE : FALSE ; +#ifdef SMT_EXT_CUTOFF + smt_ler_alarm_check(smc,phy,cond) ; +#endif /* nSMT_EXT_CUTOFF */ + if (cond != mib->fddiPORTLerFlag) { + smt_srf_event(smc,SMT_COND_PORT_LER, + (int) (INDEX_PORT+ phy->np) ,cond) ; + } +#endif + + if ( mib->fddiPORTLer_Estimate <= mib->fddiPORTLer_Cutoff) { + phy->pc_lem_fail = TRUE ; /* flag */ + mib->fddiPORTLem_Reject_Ct++ ; + /* + * "forgive 10e-2" if we cutoff so we can come + * up again .. + */ + lem->lem_float_ber += 2*100 ; + + /*PC81b*/ +#ifdef CONCENTRATOR + DB_PCMN(1,"PCM: LER cutoff on port %d cutoff %d\n", + phy->np, mib->fddiPORTLer_Cutoff) ; +#endif +#ifdef SMT_EXT_CUTOFF + smt_port_off_event(smc,phy->np); +#else /* nSMT_EXT_CUTOFF */ + queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ; +#endif /* nSMT_EXT_CUTOFF */ + } +} + +/* + * called by SMT to calculate LEM bit error rate + */ +void sm_lem_evaluate(smc) +struct s_smc *smc ; +{ + int np ; + + for (np = 0 ; np < NUMPHYS ; np++) + lem_evaluate(smc,&smc->y[np]) ; +} + +static void lem_check_lct(smc,phy) +struct s_smc *smc ; +struct s_phy *phy ; +{ + struct lem_counter *lem = &phy->lem ; + struct fddi_mib_p *mib ; + int errors ; + + mib = phy->mib ; + + phy->pc_lem_fail = FALSE ; /* flag */ + errors = inpw(PLC(((int)phy->np),PL_LINK_ERR_CTR)) ; + lem->lem_errors += errors ; + mib->fddiPORTLem_Ct += errors ; + if (lem->lem_errors) { + switch(phy->lc_test) { + case LC_SHORT: + if (lem->lem_errors >= smc->s.lct_short) + phy->pc_lem_fail = TRUE ; + break ; + case LC_MEDIUM: + if (lem->lem_errors >= smc->s.lct_medium) + phy->pc_lem_fail = TRUE ; + break ; + case LC_LONG: + if (lem->lem_errors >= smc->s.lct_long) + phy->pc_lem_fail = TRUE ; + break ; + case LC_EXTENDED: + if (lem->lem_errors >= smc->s.lct_extended) + phy->pc_lem_fail = TRUE ; + break ; + } + DB_PCMN(1," >>errors : %d\n",lem->lem_errors,0) ; + } + if (phy->pc_lem_fail) { + mib->fddiPORTLCTFail_Ct++ ; + mib->fddiPORTLem_Reject_Ct++ ; + } + else + mib->fddiPORTLCTFail_Ct = 0 ; +} + +/* + * LEM functions + */ +static void sm_ph_lem_start(smc,np,threshold) +struct s_smc *smc ; +int np; +int threshold; +{ + struct lem_counter *lem = &smc->y[np].lem ; + + lem->lem_on = 1 ; + lem->lem_errors = 0L ; + + /* Do NOT reset mib->fddiPORTLer_Estimate here. It is called too + * often. + */ + + outpw(PLC(np,PL_LE_THRESHOLD),threshold) ; + (void)inpw(PLC(np,PL_LINK_ERR_CTR)) ; /* clear error counter */ + + /* enable LE INT */ + SETMASK(PLC(np,PL_INTR_MASK),PL_LE_CTR,PL_LE_CTR) ; +} + +static void sm_ph_lem_stop(smc,np) +struct s_smc *smc ; +int np; +{ + struct lem_counter *lem = &smc->y[np].lem ; + + lem->lem_on = 0 ; + CLEAR(PLC(np,PL_INTR_MASK),PL_LE_CTR) ; +} + +/* ARGSUSED */ +void sm_pm_ls_latch(smc,phy,on_off) +struct s_smc *smc ; +int phy; +int on_off; /* en- or disable ident. ls */ +{ + SK_UNUSED(smc) ; + + phy = phy ; on_off = on_off ; +} + + +/* + * PCM pseudo code + * receive actions are called AFTER the bit n is received, + * i.e. if pc_rcode_actions(5) is called, bit 6 is the next bit to be received + */ + +/* + * PCM pseudo code 5.1 .. 6.1 + */ +static void pc_rcode_actions(smc,bit,phy) +struct s_smc *smc ; +int bit; +struct s_phy *phy; +{ + struct fddi_mib_p *mib ; + + mib = phy->mib ; + + DB_PCMN(1,"SIG rec %x %x: \n", bit,phy->r_val[bit] ) ; + bit++ ; + + switch(bit) { + case 0: + case 1: + case 2: + break ; + case 3 : + if (phy->r_val[1] == 0 && phy->r_val[2] == 0) + mib->fddiPORTNeighborType = TA ; + else if (phy->r_val[1] == 0 && phy->r_val[2] == 1) + mib->fddiPORTNeighborType = TB ; + else if (phy->r_val[1] == 1 && phy->r_val[2] == 0) + mib->fddiPORTNeighborType = TS ; + else if (phy->r_val[1] == 1 && phy->r_val[2] == 1) + mib->fddiPORTNeighborType = TM ; + break ; + case 4: + if (mib->fddiPORTMy_Type == TM && + mib->fddiPORTNeighborType == TM) { + DB_PCMN(1,"PCM %c : E100 withhold M-M\n", + phy->phy_name,0) ; + mib->fddiPORTPC_Withhold = PC_WH_M_M ; + RS_SET(smc,RS_EVENT) ; + } + else if (phy->t_val[3] || phy->r_val[3]) { + mib->fddiPORTPC_Withhold = PC_WH_NONE ; + if (mib->fddiPORTMy_Type == TM || + mib->fddiPORTNeighborType == TM) + phy->pc_mode = PM_TREE ; + else + phy->pc_mode = PM_PEER ; + + /* reevaluate the selection criteria (wc_flag) */ + all_selection_criteria (smc); + + if (phy->wc_flag) { + mib->fddiPORTPC_Withhold = PC_WH_PATH ; + } + } + else { + mib->fddiPORTPC_Withhold = PC_WH_OTHER ; + RS_SET(smc,RS_EVENT) ; + DB_PCMN(1,"PCM %c : E101 withhold other\n", + phy->phy_name,0) ; + } + phy->twisted = ((mib->fddiPORTMy_Type != TS) && + (mib->fddiPORTMy_Type != TM) && + (mib->fddiPORTNeighborType == + mib->fddiPORTMy_Type)) ; + if (phy->twisted) { + DB_PCMN(1,"PCM %c : E102 !!! TWISTED !!!\n", + phy->phy_name,0) ; + } + break ; + case 5 : + break ; + case 6: + if (phy->t_val[4] || phy->r_val[4]) { + if ((phy->t_val[4] && phy->t_val[5]) || + (phy->r_val[4] && phy->r_val[5]) ) + phy->lc_test = LC_EXTENDED ; + else + phy->lc_test = LC_LONG ; + } + else if (phy->t_val[5] || phy->r_val[5]) + phy->lc_test = LC_MEDIUM ; + else + phy->lc_test = LC_SHORT ; + switch (phy->lc_test) { + case LC_SHORT : /* 50ms */ + outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LENGTH ) ; + phy->t_next[7] = smc->s.pcm_lc_short ; + break ; + case LC_MEDIUM : /* 500ms */ + outpw(PLC((int)phy->np,PL_LC_LENGTH), TP_LC_LONGLN ) ; + phy->t_next[7] = smc->s.pcm_lc_medium ; + break ; + case LC_LONG : + SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ; + phy->t_next[7] = smc->s.pcm_lc_long ; + break ; + case LC_EXTENDED : + SETMASK(PLC((int)phy->np,PL_CNTRL_B),PL_LONG,PL_LONG) ; + phy->t_next[7] = smc->s.pcm_lc_extended ; + break ; + } + if (phy->t_next[7] > smc->s.pcm_lc_medium) { + start_pcm_timer0(smc,phy->t_next[7],PC_TIMEOUT_LCT,phy); + } + DB_PCMN(1,"LCT timer = %ld us\n", phy->t_next[7], 0) ; + phy->t_next[9] = smc->s.pcm_t_next_9 ; + break ; + case 7: + if (phy->t_val[6]) { + phy->cf_loop = TRUE ; + } + phy->td_flag = TRUE ; + break ; + case 8: + if (phy->t_val[7] || phy->r_val[7]) { + DB_PCMN(1,"PCM %c : E103 LCT fail %s\n", + phy->phy_name,phy->t_val[7]? "local":"remote") ; + queue_event(smc,(int)(EVENT_PCM+phy->np),PC_START) ; + } + break ; + case 9: + if (phy->t_val[8] || phy->r_val[8]) { + if (phy->t_val[8]) + phy->cf_loop = TRUE ; + phy->td_flag = TRUE ; + } + break ; + case 10: + if (phy->r_val[9]) { + /* neighbor intends to have MAC on output */ ; + mib->fddiPORTMacIndicated.R_val = TRUE ; + } + else { + /* neighbor does not intend to have MAC on output */ ; + mib->fddiPORTMacIndicated.R_val = FALSE ; + } + break ; + } +} + +/* + * PCM pseudo code 5.1 .. 6.1 + */ +static void pc_tcode_actions(smc,bit,phy) +struct s_smc *smc ; +const int bit; +struct s_phy *phy; +{ + int np = phy->np ; + struct fddi_mib_p *mib ; + + mib = phy->mib ; + + switch(bit) { + case 0: + phy->t_val[0] = 0 ; /* no escape used */ + break ; + case 1: + if (mib->fddiPORTMy_Type == TS || mib->fddiPORTMy_Type == TM) + phy->t_val[1] = 1 ; + else + phy->t_val[1] = 0 ; + break ; + case 2 : + if (mib->fddiPORTMy_Type == TB || mib->fddiPORTMy_Type == TM) + phy->t_val[2] = 1 ; + else + phy->t_val[2] = 0 ; + break ; + case 3: + { + int type,ne ; + int policy ; + + type = mib->fddiPORTMy_Type ; + ne = mib->fddiPORTNeighborType ; + policy = smc->mib.fddiSMTConnectionPolicy ; + + phy->t_val[3] = 1 ; /* Accept connection */ + switch (type) { + case TA : + if ( + ((policy & POLICY_AA) && ne == TA) || + ((policy & POLICY_AB) && ne == TB) || + ((policy & POLICY_AS) && ne == TS) || + ((policy & POLICY_AM) && ne == TM) ) + phy->t_val[3] = 0 ; /* Reject */ + break ; + case TB : + if ( + ((policy & POLICY_BA) && ne == TA) || + ((policy & POLICY_BB) && ne == TB) || + ((policy & POLICY_BS) && ne == TS) || + ((policy & POLICY_BM) && ne == TM) ) + phy->t_val[3] = 0 ; /* Reject */ + break ; + case TS : + if ( + ((policy & POLICY_SA) && ne == TA) || + ((policy & POLICY_SB) && ne == TB) || + ((policy & POLICY_SS) && ne == TS) || + ((policy & POLICY_SM) && ne == TM) ) + phy->t_val[3] = 0 ; /* Reject */ + break ; + case TM : + if ( ne == TM || + ((policy & POLICY_MA) && ne == TA) || + ((policy & POLICY_MB) && ne == TB) || + ((policy & POLICY_MS) && ne == TS) || + ((policy & POLICY_MM) && ne == TM) ) + phy->t_val[3] = 0 ; /* Reject */ + break ; + } +#ifndef SLIM_SMT + /* + * detect undesirable connection attempt event + */ + if ( (type == TA && ne == TA ) || + (type == TA && ne == TS ) || + (type == TB && ne == TB ) || + (type == TB && ne == TS ) || + (type == TS && ne == TA ) || + (type == TS && ne == TB ) ) { + smt_srf_event(smc,SMT_EVENT_PORT_CONNECTION, + (int) (INDEX_PORT+ phy->np) ,0) ; + } +#endif + } + break ; + case 4: + if (mib->fddiPORTPC_Withhold == PC_WH_NONE) { + if (phy->pc_lem_fail) { + phy->t_val[4] = 1 ; /* long */ + phy->t_val[5] = 0 ; + } + else { + phy->t_val[4] = 0 ; + if (mib->fddiPORTLCTFail_Ct > 0) + phy->t_val[5] = 1 ; /* medium */ + else + phy->t_val[5] = 0 ; /* short */ + + /* + * Implementers choice: use medium + * instead of short when undesired + * connection attempt is made. + */ + if (phy->wc_flag) + phy->t_val[5] = 1 ; /* medium */ + } + mib->fddiPORTConnectState = PCM_CONNECTING ; + } + else { + mib->fddiPORTConnectState = PCM_STANDBY ; + phy->t_val[4] = 1 ; /* extended */ + phy->t_val[5] = 1 ; + } + break ; + case 5: + break ; + case 6: + /* we do NOT have a MAC for LCT */ + phy->t_val[6] = 0 ; + break ; + case 7: + phy->cf_loop = FALSE ; + lem_check_lct(smc,phy) ; + if (phy->pc_lem_fail) { + DB_PCMN(1,"PCM %c : E104 LCT failed\n", + phy->phy_name,0) ; + phy->t_val[7] = 1 ; + } + else + phy->t_val[7] = 0 ; + break ; + case 8: + phy->t_val[8] = 0 ; /* Don't request MAC loopback */ + break ; + case 9: + phy->cf_loop = 0 ; + if ((mib->fddiPORTPC_Withhold != PC_WH_NONE) || + ((smc->s.sas == SMT_DAS) && (phy->wc_flag))) { + queue_event(smc,EVENT_PCM+np,PC_START) ; + break ; + } + phy->t_val[9] = FALSE ; + switch (smc->s.sas) { + case SMT_DAS : + /* + * MAC intended on output + */ + if (phy->pc_mode == PM_TREE) { + if ((np == PB) || ((np == PA) && + (smc->y[PB].mib->fddiPORTConnectState != + PCM_ACTIVE))) + phy->t_val[9] = TRUE ; + } + else { + if (np == PB) + phy->t_val[9] = TRUE ; + } + break ; + case SMT_SAS : + if (np == PS) + phy->t_val[9] = TRUE ; + break ; +#ifdef CONCENTRATOR + case SMT_NAC : + /* + * MAC intended on output + */ + if (np == PB) + phy->t_val[9] = TRUE ; + break ; +#endif + } + mib->fddiPORTMacIndicated.T_val = phy->t_val[9] ; + break ; + } + DB_PCMN(1,"SIG snd %x %x: \n", bit,phy->t_val[bit] ) ; +} + +/* + * return status twisted (called by SMT) + */ +int pcm_status_twisted(smc) +struct s_smc *smc ; +{ + int twist = 0 ; + if (smc->s.sas != SMT_DAS) + return(0) ; + if (smc->y[PA].twisted && (smc->y[PA].mib->fddiPORTPCMState == PC8_ACTIVE)) + twist |= 1 ; + if (smc->y[PB].twisted && (smc->y[PB].mib->fddiPORTPCMState == PC8_ACTIVE)) + twist |= 2 ; + return(twist) ; +} + +/* + * return status (called by SMT) + * type + * state + * remote phy type + * remote mac yes/no + */ +void pcm_status_state(smc,np,type,state,remote,mac) +struct s_smc *smc ; +int np; +int *type; +int *state; +int *remote; +int *mac; +{ + struct s_phy *phy = &smc->y[np] ; + struct fddi_mib_p *mib ; + + mib = phy->mib ; + + /* remote PHY type and MAC - set only if active */ + *mac = 0 ; + *type = mib->fddiPORTMy_Type ; /* our PHY type */ + *state = mib->fddiPORTConnectState ; + *remote = mib->fddiPORTNeighborType ; + + switch(mib->fddiPORTPCMState) { + case PC8_ACTIVE : + *mac = mib->fddiPORTMacIndicated.R_val ; + break ; + } +} + +/* + * return rooted station status (called by SMT) + */ +int pcm_rooted_station(smc) +struct s_smc *smc ; +{ + int n ; + + for (n = 0 ; n < NUMPHYS ; n++) { + if (smc->y[n].mib->fddiPORTPCMState == PC8_ACTIVE && + smc->y[n].mib->fddiPORTNeighborType == TM) + return(0) ; + } + return(1) ; +} + +/* + * Interrupt actions for PLC & PCM events + */ +void plc_irq(smc,np,cmd) +struct s_smc *smc ; +int np; /* PHY index */ +unsigned int cmd; +{ + struct s_phy *phy = &smc->y[np] ; + struct s_plc *plc = &phy->plc ; + int n ; +#ifdef SUPERNET_3 + int corr_mask ; +#endif /* SUPERNET_3 */ + int i ; + + if (np >= smc->s.numphys) { + plc->soft_err++ ; + return ; + } + if (cmd & PL_EBUF_ERR) { /* elastic buff. det. over-|underflow*/ + /* + * Check whether the SRF Condition occured. + */ + if (!plc->ebuf_cont && phy->mib->fddiPORTPCMState == PC8_ACTIVE){ + /* + * This is the real Elasticity Error. + * More than one in a row are treated as a + * single one. + * Only count this in the active state. + */ + phy->mib->fddiPORTEBError_Ct ++ ; + + } + + plc->ebuf_err++ ; + if (plc->ebuf_cont <= 1000) { + /* + * Prevent counter from being wrapped after + * hanging years in that interrupt. + */ + plc->ebuf_cont++ ; /* Ebuf continous error */ + } + +#ifdef SUPERNET_3 + if (plc->ebuf_cont == 1000 && + ((inpw(PLC(np,PL_STATUS_A)) & PLC_REV_MASK) == + PLC_REV_SN3)) { + /* + * This interrupt remeained high for at least + * 1000 consecutive interrupt calls. + * + * This is caused by a hardware error of the + * ORION part of the Supernet III chipset. + * + * Disable this bit from the mask. + */ + corr_mask = (plc_imsk_na & ~PL_EBUF_ERR) ; + outpw(PLC(np,PL_INTR_MASK),corr_mask); + + /* + * Disconnect from the ring. + * Call the driver with the reset indication. + */ + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + + /* + * Make an error log entry. + */ + SMT_ERR_LOG(smc,SMT_E0136, SMT_E0136_MSG) ; + + /* + * Indicate the Reset. + */ + drv_reset_indication(smc) ; + } +#endif /* SUPERNET_3 */ + } else { + /* Reset the continous error variable */ + plc->ebuf_cont = 0 ; /* reset Ebuf continous error */ + } + if (cmd & PL_PHYINV) { /* physical layer invalid signal */ + plc->phyinv++ ; + } + if (cmd & PL_VSYM_CTR) { /* violation symbol counter has incr.*/ + plc->vsym_ctr++ ; + } + if (cmd & PL_MINI_CTR) { /* dep. on PLC_CNTRL_A's MINI_CTR_INT*/ + plc->mini_ctr++ ; + } + if (cmd & PL_LE_CTR) { /* link error event counter */ + int j ; + + /* + * note: PL_LINK_ERR_CTR MUST be read to clear it + */ + j = inpw(PLC(np,PL_LE_THRESHOLD)) ; + i = inpw(PLC(np,PL_LINK_ERR_CTR)) ; + + if (i < j) { + /* wrapped around */ + i += 256 ; + } + + if (phy->lem.lem_on) { + /* Note: Lem errors shall only be counted when + * link is ACTIVE or LCT is active. + */ + phy->lem.lem_errors += i ; + phy->mib->fddiPORTLem_Ct += i ; + } + } + if (cmd & PL_TPC_EXPIRED) { /* TPC timer reached zero */ + if (plc->p_state == PS_LCT) { + /* + * end of LCT + */ + ; + } + plc->tpc_exp++ ; + } + if (cmd & PL_LS_MATCH) { /* LS == LS in PLC_CNTRL_B's MATCH_LS*/ + switch (inpw(PLC(np,PL_CNTRL_B)) & PL_MATCH_LS) { + case PL_I_IDLE : phy->curr_ls = PC_ILS ; break ; + case PL_I_HALT : phy->curr_ls = PC_HLS ; break ; + case PL_I_MASTR : phy->curr_ls = PC_MLS ; break ; + case PL_I_QUIET : phy->curr_ls = PC_QLS ; break ; + } + } + if (cmd & PL_PCM_BREAK) { /* PCM has entered the BREAK state */ + int reason; + + reason = inpw(PLC(np,PL_STATUS_B)) & PL_BREAK_REASON ; + + switch (reason) { + case PL_B_PCS : plc->b_pcs++ ; break ; + case PL_B_TPC : plc->b_tpc++ ; break ; + case PL_B_TNE : plc->b_tne++ ; break ; + case PL_B_QLS : plc->b_qls++ ; break ; + case PL_B_ILS : plc->b_ils++ ; break ; + case PL_B_HLS : plc->b_hls++ ; break ; + } + + /*jd 05-Aug-1999 changed: Bug #10419 */ + DB_PCMN(1,"PLC %d: MDcF = %x\n", np, smc->e.DisconnectFlag); + if (smc->e.DisconnectFlag == FALSE) { + DB_PCMN(1,"PLC %d: restart (reason %x)\n", np, reason); + queue_event(smc,EVENT_PCM+np,PC_START) ; + } + else { + DB_PCMN(1,"PLC %d: NO!! restart (reason %x)\n", np, reason); + } + return ; + } + /* + * If both CODE & ENABLE are set ignore enable + */ + if (cmd & PL_PCM_CODE) { /* receive last sign.-bit | LCT complete */ + queue_event(smc,EVENT_PCM+np,PC_SIGNAL) ; + n = inpw(PLC(np,PL_RCV_VECTOR)) ; + for (i = 0 ; i < plc->p_bits ; i++) { + phy->r_val[plc->p_start+i] = n & 1 ; + n >>= 1 ; + } + } + else if (cmd & PL_PCM_ENABLED) { /* asserted SC_JOIN, scrub.completed*/ + queue_event(smc,EVENT_PCM+np,PC_JOIN) ; + } + if (cmd & PL_TRACE_PROP) { /* MLS while PC8_ACTIV || PC2_TRACE */ + /*PC22b*/ + if (!phy->tr_flag) { + DB_PCMN(1,"PCM : irq TRACE_PROP %d %d\n", + np,smc->mib.fddiSMTECMState) ; + phy->tr_flag = TRUE ; + smc->e.trace_prop |= ENTITY_BIT(ENTITY_PHY(np)) ; + queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ; + } + } + /* + * filter PLC glitch ??? + * QLS || HLS only while in PC2_TRACE state + */ + if ((cmd & PL_SELF_TEST) && (phy->mib->fddiPORTPCMState == PC2_TRACE)) { + /*PC22a*/ + if (smc->e.path_test == PT_PASSED) { + DB_PCMN(1,"PCM : state = %s %d\n", get_pcmstate(smc,np), + phy->mib->fddiPORTPCMState) ; + + smc->e.path_test = PT_PENDING ; + queue_event(smc,EVENT_ECM,EC_PATH_TEST) ; + } + } + if (cmd & PL_TNE_EXPIRED) { /* TNE: length of noise events */ + /* break_required (TNE > NS_Max) */ + if (phy->mib->fddiPORTPCMState == PC8_ACTIVE) { + if (!phy->tr_flag) { + DB_PCMN(1,"PCM %c : PC81 %s\n",phy->phy_name,"NSE"); + queue_event(smc,EVENT_PCM+np,PC_START) ; + return ; + } + } + } +#if 0 + if (cmd & PL_NP_ERR) { /* NP has requested to r/w an inv reg*/ + /* + * It's a bug by AMD + */ + plc->np_err++ ; + } + /* pin inactiv (GND) */ + if (cmd & PL_PARITY_ERR) { /* p. error dedected on TX9-0 inp */ + plc->parity_err++ ; + } + if (cmd & PL_LSDO) { /* carrier detected */ + ; + } +#endif +} + +void pcm_set_lct_short(smc,n) +struct s_smc *smc ; +int n ; +{ + if (n <= 0 || n > 1000) + return ; + smc->s.lct_short = n ; +} + +#ifdef DEBUG +/* + * fill state struct + */ +void pcm_get_state(smc,state) +struct s_smc *smc ; +struct smt_state *state ; +{ + struct s_phy *phy ; + struct pcm_state *pcs ; + int i ; + int ii ; + short rbits ; + short tbits ; + struct fddi_mib_p *mib ; + + for (i = 0, phy = smc->y, pcs = state->pcm_state ; i < NUMPHYS ; + i++ , phy++, pcs++ ) { + mib = phy->mib ; + pcs->pcm_type = (u_char) mib->fddiPORTMy_Type ; + pcs->pcm_state = (u_char) mib->fddiPORTPCMState ; + pcs->pcm_mode = phy->pc_mode ; + pcs->pcm_neighbor = (u_char) mib->fddiPORTNeighborType ; + pcs->pcm_bsf = mib->fddiPORTBS_Flag ; + pcs->pcm_lsf = phy->ls_flag ; + pcs->pcm_lct_fail = (u_char) mib->fddiPORTLCTFail_Ct ; + pcs->pcm_ls_rx = LS2MIB(sm_pm_get_ls(smc,i)) ; + for (ii = 0, rbits = tbits = 0 ; ii < NUMBITS ; ii++) { + rbits <<= 1 ; + tbits <<= 1 ; + if (phy->r_val[NUMBITS-1-ii]) + rbits |= 1 ; + if (phy->t_val[NUMBITS-1-ii]) + tbits |= 1 ; + } + pcs->pcm_r_val = rbits ; + pcs->pcm_t_val = tbits ; + } +} + +int get_pcm_state(smc,np) +struct s_smc *smc ; +int np; +{ + int pcs ; + + SK_UNUSED(smc) ; + + switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) { + case PL_PC0 : pcs = PC_STOP ; break ; + case PL_PC1 : pcs = PC_START ; break ; + case PL_PC2 : pcs = PC_TRACE ; break ; + case PL_PC3 : pcs = PC_SIGNAL ; break ; + case PL_PC4 : pcs = PC_SIGNAL ; break ; + case PL_PC5 : pcs = PC_SIGNAL ; break ; + case PL_PC6 : pcs = PC_JOIN ; break ; + case PL_PC7 : pcs = PC_JOIN ; break ; + case PL_PC8 : pcs = PC_ENABLE ; break ; + case PL_PC9 : pcs = PC_MAINT ; break ; + default : pcs = PC_DISABLE ; break ; + } + return(pcs) ; +} + +char *get_linestate(smc,np) +struct s_smc *smc ; +int np; +{ + char *ls = "" ; + + SK_UNUSED(smc) ; + + switch (inpw(PLC(np,PL_STATUS_A)) & PL_LINE_ST) { + case PL_L_NLS : ls = "NOISE" ; break ; + case PL_L_ALS : ls = "ACTIV" ; break ; + case PL_L_UND : ls = "UNDEF" ; break ; + case PL_L_ILS4: ls = "ILS 4" ; break ; + case PL_L_QLS : ls = "QLS" ; break ; + case PL_L_MLS : ls = "MLS" ; break ; + case PL_L_HLS : ls = "HLS" ; break ; + case PL_L_ILS16:ls = "ILS16" ; break ; +#ifdef lint + default: ls = "unknown" ; break ; +#endif + } + return(ls) ; +} + +char *get_pcmstate(smc,np) +struct s_smc *smc ; +int np; +{ + char *pcs ; + + SK_UNUSED(smc) ; + + switch (inpw(PLC(np,PL_STATUS_B)) & PL_PCM_STATE) { + case PL_PC0 : pcs = "OFF" ; break ; + case PL_PC1 : pcs = "BREAK" ; break ; + case PL_PC2 : pcs = "TRACE" ; break ; + case PL_PC3 : pcs = "CONNECT"; break ; + case PL_PC4 : pcs = "NEXT" ; break ; + case PL_PC5 : pcs = "SIGNAL" ; break ; + case PL_PC6 : pcs = "JOIN" ; break ; + case PL_PC7 : pcs = "VERIFY" ; break ; + case PL_PC8 : pcs = "ACTIV" ; break ; + case PL_PC9 : pcs = "MAINT" ; break ; + default : pcs = "UNKNOWN" ; break ; + } + return(pcs) ; +} + +void list_phy(smc) +struct s_smc *smc ; +{ + struct s_plc *plc ; + int np ; + + for (np = 0 ; np < NUMPHYS ; np++) { + plc = &smc->y[np].plc ; + printf("PHY %d:\tERRORS\t\t\tBREAK_REASONS\t\tSTATES:\n",np) ; + printf("\tsoft_error: %ld \t\tPC_Start : %ld\n", + plc->soft_err,plc->b_pcs); + printf("\tparity_err: %ld \t\tTPC exp. : %ld\t\tLine: %s\n", + plc->parity_err,plc->b_tpc,get_linestate(smc,np)) ; + printf("\tebuf_error: %ld \t\tTNE exp. : %ld\n", + plc->ebuf_err,plc->b_tne) ; + printf("\tphyinvalid: %ld \t\tQLS det. : %ld\t\tPCM : %s\n", + plc->phyinv,plc->b_qls,get_pcmstate(smc,np)) ; + printf("\tviosym_ctr: %ld \t\tILS det. : %ld\n", + plc->vsym_ctr,plc->b_ils) ; + printf("\tmingap_ctr: %ld \t\tHLS det. : %ld\n", + plc->mini_ctr,plc->b_hls) ; + printf("\tnodepr_err: %ld\n",plc->np_err) ; + printf("\tTPC_exp : %ld\n",plc->tpc_exp) ; + printf("\tLEM_err : %ld\n",smc->y[np].lem.lem_errors) ; + } +} + + +#ifdef CONCENTRATOR +void pcm_lem_dump(smc) +struct s_smc *smc ; +{ + int i ; + struct s_phy *phy ; + struct fddi_mib_p *mib ; + + char *entostring() ; + + printf("PHY errors BER\n") ; + printf("----------------------\n") ; + for (i = 0,phy = smc->y ; i < NUMPHYS ; i++,phy++) { + if (!plc_is_installed(smc,i)) + continue ; + mib = phy->mib ; + printf("%s\t%ld\t10E-%d\n", + entostring(smc,ENTITY_PHY(i)), + mib->fddiPORTLem_Ct, + mib->fddiPORTLer_Estimate) ; + } +} +#endif +#endif diff --git a/drivers/net/skfp/pmf.c b/drivers/net/skfp/pmf.c new file mode 100644 index 000000000000..d53355bac5aa --- /dev/null +++ b/drivers/net/skfp/pmf.c @@ -0,0 +1,1701 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + Parameter Management Frame processing for SMT 7.2 +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smt_p.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef SLIM_SMT + +#ifndef lint +static const char ID_sccs[] = "@(#)pmf.c 1.37 97/08/04 (C) SK " ; +#endif + +static int smt_authorize() ; +static int smt_check_set_count() ; +static const struct s_p_tab *smt_get_ptab() ; +static int smt_mib_phys() ; +int smt_set_para() ; +void smt_add_para() ; + +#define MOFFSS(e) ((int)&(((struct fddi_mib *)0)->e)) +#define MOFFSA(e) ((int) (((struct fddi_mib *)0)->e)) + +#define MOFFMS(e) ((int)&(((struct fddi_mib_m *)0)->e)) +#define MOFFMA(e) ((int) (((struct fddi_mib_m *)0)->e)) + +#define MOFFAS(e) ((int)&(((struct fddi_mib_a *)0)->e)) +#define MOFFAA(e) ((int) (((struct fddi_mib_a *)0)->e)) + +#define MOFFPS(e) ((int)&(((struct fddi_mib_p *)0)->e)) +#define MOFFPA(e) ((int) (((struct fddi_mib_p *)0)->e)) + + +#define AC_G 0x01 /* Get */ +#define AC_GR 0x02 /* Get/Set */ +#define AC_S 0x04 /* Set */ +#define AC_NA 0x08 +#define AC_GROUP 0x10 /* Group */ +#define MS2BCLK(x) ((x)*12500L) +/* + F LFag (byte) + B byte + S u_short 16 bit + C Counter 32 bit + L Long 32 bit + T Timer_2 32 bit + P TimeStamp ; + A LongAddress (6 byte) + E Enum 16 bit + R ResId 16 Bit +*/ +static const struct s_p_tab { + u_short p_num ; /* parameter code */ + u_char p_access ; /* access rights */ + u_short p_offset ; /* offset in mib */ + char p_swap[3] ; /* format string */ +} p_tab[] = { + /* StationIdGrp */ + { SMT_P100A,AC_GROUP } , + { SMT_P100B,AC_G, MOFFSS(fddiSMTStationId), "8" } , + { SMT_P100D,AC_G, MOFFSS(fddiSMTOpVersionId), "S" } , + { SMT_P100E,AC_G, MOFFSS(fddiSMTHiVersionId), "S" } , + { SMT_P100F,AC_G, MOFFSS(fddiSMTLoVersionId), "S" } , + { SMT_P1010,AC_G, MOFFSA(fddiSMTManufacturerData), "D" } , + { SMT_P1011,AC_GR, MOFFSA(fddiSMTUserData), "D" } , + { SMT_P1012,AC_G, MOFFSS(fddiSMTMIBVersionId), "S" } , + + /* StationConfigGrp */ + { SMT_P1014,AC_GROUP } , + { SMT_P1015,AC_G, MOFFSS(fddiSMTMac_Ct), "B" } , + { SMT_P1016,AC_G, MOFFSS(fddiSMTNonMaster_Ct), "B" } , + { SMT_P1017,AC_G, MOFFSS(fddiSMTMaster_Ct), "B" } , + { SMT_P1018,AC_G, MOFFSS(fddiSMTAvailablePaths), "B" } , + { SMT_P1019,AC_G, MOFFSS(fddiSMTConfigCapabilities),"S" } , + { SMT_P101A,AC_GR, MOFFSS(fddiSMTConfigPolicy), "wS" } , + { SMT_P101B,AC_GR, MOFFSS(fddiSMTConnectionPolicy),"wS" } , + { SMT_P101D,AC_GR, MOFFSS(fddiSMTTT_Notify), "wS" } , + { SMT_P101E,AC_GR, MOFFSS(fddiSMTStatRptPolicy), "bB" } , + { SMT_P101F,AC_GR, MOFFSS(fddiSMTTrace_MaxExpiration),"lL" } , + { SMT_P1020,AC_G, MOFFSA(fddiSMTPORTIndexes), "II" } , + { SMT_P1021,AC_G, MOFFSS(fddiSMTMACIndexes), "I" } , + { SMT_P1022,AC_G, MOFFSS(fddiSMTBypassPresent), "F" } , + + /* StatusGrp */ + { SMT_P1028,AC_GROUP } , + { SMT_P1029,AC_G, MOFFSS(fddiSMTECMState), "E" } , + { SMT_P102A,AC_G, MOFFSS(fddiSMTCF_State), "E" } , + { SMT_P102C,AC_G, MOFFSS(fddiSMTRemoteDisconnectFlag),"F" } , + { SMT_P102D,AC_G, MOFFSS(fddiSMTStationStatus), "E" } , + { SMT_P102E,AC_G, MOFFSS(fddiSMTPeerWrapFlag), "F" } , + + /* MIBOperationGrp */ + { SMT_P1032,AC_GROUP } , + { SMT_P1033,AC_G, MOFFSA(fddiSMTTimeStamp),"P" } , + { SMT_P1034,AC_G, MOFFSA(fddiSMTTransitionTimeStamp),"P" } , + /* NOTE : SMT_P1035 is already swapped ! SMT_P_SETCOUNT */ + { SMT_P1035,AC_G, MOFFSS(fddiSMTSetCount),"4P" } , + { SMT_P1036,AC_G, MOFFSS(fddiSMTLastSetStationId),"8" } , + + { SMT_P103C,AC_S, 0, "wS" } , + + /* + * PRIVATE EXTENSIONS + * only accessable locally to get/set passwd + */ + { SMT_P10F0,AC_GR, MOFFSA(fddiPRPMFPasswd), "8" } , + { SMT_P10F1,AC_GR, MOFFSS(fddiPRPMFStation), "8" } , +#ifdef ESS + { SMT_P10F2,AC_GR, MOFFSS(fddiESSPayload), "lL" } , + { SMT_P10F3,AC_GR, MOFFSS(fddiESSOverhead), "lL" } , + { SMT_P10F4,AC_GR, MOFFSS(fddiESSMaxTNeg), "lL" } , + { SMT_P10F5,AC_GR, MOFFSS(fddiESSMinSegmentSize), "lL" } , + { SMT_P10F6,AC_GR, MOFFSS(fddiESSCategory), "lL" } , + { SMT_P10F7,AC_GR, MOFFSS(fddiESSSynchTxMode), "wS" } , +#endif +#ifdef SBA + { SMT_P10F8,AC_GR, MOFFSS(fddiSBACommand), "bF" } , + { SMT_P10F9,AC_GR, MOFFSS(fddiSBAAvailable), "bF" } , +#endif + /* MAC Attributes */ + { SMT_P200A,AC_GROUP } , + { SMT_P200B,AC_G, MOFFMS(fddiMACFrameStatusFunctions),"S" } , + { SMT_P200D,AC_G, MOFFMS(fddiMACT_MaxCapabilitiy),"T" } , + { SMT_P200E,AC_G, MOFFMS(fddiMACTVXCapabilitiy),"T" } , + + /* ConfigGrp */ + { SMT_P2014,AC_GROUP } , + { SMT_P2016,AC_G, MOFFMS(fddiMACAvailablePaths), "B" } , + { SMT_P2017,AC_G, MOFFMS(fddiMACCurrentPath), "S" } , + { SMT_P2018,AC_G, MOFFMS(fddiMACUpstreamNbr), "A" } , + { SMT_P2019,AC_G, MOFFMS(fddiMACDownstreamNbr), "A" } , + { SMT_P201A,AC_G, MOFFMS(fddiMACOldUpstreamNbr), "A" } , + { SMT_P201B,AC_G, MOFFMS(fddiMACOldDownstreamNbr),"A" } , + { SMT_P201D,AC_G, MOFFMS(fddiMACDupAddressTest), "E" } , + { SMT_P2020,AC_GR, MOFFMS(fddiMACRequestedPaths), "wS" } , + { SMT_P2021,AC_G, MOFFMS(fddiMACDownstreamPORTType),"E" } , + { SMT_P2022,AC_G, MOFFMS(fddiMACIndex), "S" } , + + /* AddressGrp */ + { SMT_P2028,AC_GROUP } , + { SMT_P2029,AC_G, MOFFMS(fddiMACSMTAddress), "A" } , + + /* OperationGrp */ + { SMT_P2032,AC_GROUP } , + { SMT_P2033,AC_G, MOFFMS(fddiMACT_Req), "T" } , + { SMT_P2034,AC_G, MOFFMS(fddiMACT_Neg), "T" } , + { SMT_P2035,AC_G, MOFFMS(fddiMACT_Max), "T" } , + { SMT_P2036,AC_G, MOFFMS(fddiMACTvxValue), "T" } , + { SMT_P2038,AC_G, MOFFMS(fddiMACT_Pri0), "T" } , + { SMT_P2039,AC_G, MOFFMS(fddiMACT_Pri1), "T" } , + { SMT_P203A,AC_G, MOFFMS(fddiMACT_Pri2), "T" } , + { SMT_P203B,AC_G, MOFFMS(fddiMACT_Pri3), "T" } , + { SMT_P203C,AC_G, MOFFMS(fddiMACT_Pri4), "T" } , + { SMT_P203D,AC_G, MOFFMS(fddiMACT_Pri5), "T" } , + { SMT_P203E,AC_G, MOFFMS(fddiMACT_Pri6), "T" } , + + + /* CountersGrp */ + { SMT_P2046,AC_GROUP } , + { SMT_P2047,AC_G, MOFFMS(fddiMACFrame_Ct), "C" } , + { SMT_P2048,AC_G, MOFFMS(fddiMACCopied_Ct), "C" } , + { SMT_P2049,AC_G, MOFFMS(fddiMACTransmit_Ct), "C" } , + { SMT_P204A,AC_G, MOFFMS(fddiMACToken_Ct), "C" } , + { SMT_P2051,AC_G, MOFFMS(fddiMACError_Ct), "C" } , + { SMT_P2052,AC_G, MOFFMS(fddiMACLost_Ct), "C" } , + { SMT_P2053,AC_G, MOFFMS(fddiMACTvxExpired_Ct), "C" } , + { SMT_P2054,AC_G, MOFFMS(fddiMACNotCopied_Ct), "C" } , + { SMT_P2056,AC_G, MOFFMS(fddiMACRingOp_Ct), "C" } , + + /* FrameErrorConditionGrp */ + { SMT_P205A,AC_GROUP } , + { SMT_P205F,AC_GR, MOFFMS(fddiMACFrameErrorThreshold),"wS" } , + { SMT_P2060,AC_G, MOFFMS(fddiMACFrameErrorRatio), "S" } , + + /* NotCopiedConditionGrp */ + { SMT_P2064,AC_GROUP } , + { SMT_P2067,AC_GR, MOFFMS(fddiMACNotCopiedThreshold),"wS" } , + { SMT_P2069,AC_G, MOFFMS(fddiMACNotCopiedRatio), "S" } , + + /* StatusGrp */ + { SMT_P206E,AC_GROUP } , + { SMT_P206F,AC_G, MOFFMS(fddiMACRMTState), "S" } , + { SMT_P2070,AC_G, MOFFMS(fddiMACDA_Flag), "F" } , + { SMT_P2071,AC_G, MOFFMS(fddiMACUNDA_Flag), "F" } , + { SMT_P2072,AC_G, MOFFMS(fddiMACFrameErrorFlag), "F" } , + { SMT_P2073,AC_G, MOFFMS(fddiMACNotCopiedFlag), "F" } , + { SMT_P2074,AC_G, MOFFMS(fddiMACMA_UnitdataAvailable),"F" } , + { SMT_P2075,AC_G, MOFFMS(fddiMACHardwarePresent), "F" } , + { SMT_P2076,AC_GR, MOFFMS(fddiMACMA_UnitdataEnable),"bF" } , + + /* + * PRIVATE EXTENSIONS + * only accessable locally to get/set TMIN + */ + { SMT_P20F0,AC_NA } , + { SMT_P20F1,AC_GR, MOFFMS(fddiMACT_Min), "lT" } , + + /* Path Attributes */ + /* + * DON't swap 320B,320F,3210: they are already swapped in swap_para() + */ + { SMT_P320A,AC_GROUP } , + { SMT_P320B,AC_G, MOFFAS(fddiPATHIndex), "r" } , + { SMT_P320F,AC_GR, MOFFAS(fddiPATHSbaPayload), "l4" } , + { SMT_P3210,AC_GR, MOFFAS(fddiPATHSbaOverhead), "l4" } , + /* fddiPATHConfiguration */ + { SMT_P3212,AC_G, 0, "" } , + { SMT_P3213,AC_GR, MOFFAS(fddiPATHT_Rmode), "lT" } , + { SMT_P3214,AC_GR, MOFFAS(fddiPATHSbaAvailable), "lL" } , + { SMT_P3215,AC_GR, MOFFAS(fddiPATHTVXLowerBound), "lT" } , + { SMT_P3216,AC_GR, MOFFAS(fddiPATHT_MaxLowerBound),"lT" } , + { SMT_P3217,AC_GR, MOFFAS(fddiPATHMaxT_Req), "lT" } , + + /* Port Attributes */ + /* ConfigGrp */ + { SMT_P400A,AC_GROUP } , + { SMT_P400C,AC_G, MOFFPS(fddiPORTMy_Type), "E" } , + { SMT_P400D,AC_G, MOFFPS(fddiPORTNeighborType), "E" } , + { SMT_P400E,AC_GR, MOFFPS(fddiPORTConnectionPolicies),"bB" } , + { SMT_P400F,AC_G, MOFFPS(fddiPORTMacIndicated), "2" } , + { SMT_P4010,AC_G, MOFFPS(fddiPORTCurrentPath), "E" } , + { SMT_P4011,AC_GR, MOFFPA(fddiPORTRequestedPaths), "l4" } , + { SMT_P4012,AC_G, MOFFPS(fddiPORTMACPlacement), "S" } , + { SMT_P4013,AC_G, MOFFPS(fddiPORTAvailablePaths), "B" } , + { SMT_P4016,AC_G, MOFFPS(fddiPORTPMDClass), "E" } , + { SMT_P4017,AC_G, MOFFPS(fddiPORTConnectionCapabilities), "B"} , + { SMT_P401D,AC_G, MOFFPS(fddiPORTIndex), "R" } , + + /* OperationGrp */ + { SMT_P401E,AC_GROUP } , + { SMT_P401F,AC_GR, MOFFPS(fddiPORTMaint_LS), "wE" } , + { SMT_P4021,AC_G, MOFFPS(fddiPORTBS_Flag), "F" } , + { SMT_P4022,AC_G, MOFFPS(fddiPORTPC_LS), "E" } , + + /* ErrorCtrsGrp */ + { SMT_P4028,AC_GROUP } , + { SMT_P4029,AC_G, MOFFPS(fddiPORTEBError_Ct), "C" } , + { SMT_P402A,AC_G, MOFFPS(fddiPORTLCTFail_Ct), "C" } , + + /* LerGrp */ + { SMT_P4032,AC_GROUP } , + { SMT_P4033,AC_G, MOFFPS(fddiPORTLer_Estimate), "F" } , + { SMT_P4034,AC_G, MOFFPS(fddiPORTLem_Reject_Ct), "C" } , + { SMT_P4035,AC_G, MOFFPS(fddiPORTLem_Ct), "C" } , + { SMT_P403A,AC_GR, MOFFPS(fddiPORTLer_Cutoff), "bB" } , + { SMT_P403B,AC_GR, MOFFPS(fddiPORTLer_Alarm), "bB" } , + + /* StatusGrp */ + { SMT_P403C,AC_GROUP } , + { SMT_P403D,AC_G, MOFFPS(fddiPORTConnectState), "E" } , + { SMT_P403E,AC_G, MOFFPS(fddiPORTPCMStateX), "E" } , + { SMT_P403F,AC_G, MOFFPS(fddiPORTPC_Withhold), "E" } , + { SMT_P4040,AC_G, MOFFPS(fddiPORTLerFlag), "F" } , + { SMT_P4041,AC_G, MOFFPS(fddiPORTHardwarePresent),"F" } , + + { SMT_P4046,AC_S, 0, "wS" } , + + { 0, AC_GROUP } , + { 0 } +} ; + + +static SMbuf *smt_build_pmf_response() ; + +void smt_pmf_received_pack(smc,mb,local) +struct s_smc *smc ; +SMbuf *mb ; +int local ; +{ + struct smt_header *sm ; + SMbuf *reply ; + + sm = smtod(mb,struct smt_header *) ; + DB_SMT("SMT: processing PMF frame at %x len %d\n",sm,mb->sm_len) ; +#ifdef DEBUG + dump_smt(smc,sm,"PMF Received") ; +#endif + /* + * Start the watchdog: It may be a long, long packet and + * maybe the watchdog occurs ... + */ + smt_start_watchdog(smc) ; + + if (sm->smt_class == SMT_PMF_GET || + sm->smt_class == SMT_PMF_SET) { + reply = smt_build_pmf_response(smc,sm, + sm->smt_class == SMT_PMF_SET,local) ; + if (reply) { + sm = smtod(reply,struct smt_header *) ; +#ifdef DEBUG + dump_smt(smc,sm,"PMF Reply") ; +#endif + smt_send_frame(smc,reply,FC_SMT_INFO,local) ; + } + } +} + +extern SMbuf *smt_get_mbuf() ; + +static SMbuf *smt_build_pmf_response(smc,req,set,local) +struct s_smc *smc ; +struct smt_header *req ; +int set ; +int local ; +{ + SMbuf *mb ; + struct smt_header *smt ; + struct smt_para *pa ; + struct smt_p_reason *res ; + const struct s_p_tab *pt ; + int len ; + int index ; + int idx_end ; + int error ; + int range ; + SK_LOC_DECL(struct s_pcon,pcon) ; + SK_LOC_DECL(struct s_pcon,set_pcon) ; + + /* + * build SMT header + */ + if (!(mb = smt_get_mbuf(smc))) + return(mb) ; + + smt = smtod(mb, struct smt_header *) ; + smt->smt_dest = req->smt_source ; /* DA == source of request */ + smt->smt_class = req->smt_class ; /* same class (GET/SET) */ + smt->smt_type = SMT_REPLY ; + smt->smt_version = SMT_VID_2 ; + smt->smt_tid = req->smt_tid ; /* same TID */ + smt->smt_pad = 0 ; + smt->smt_len = 0 ; + + /* + * setup parameter status + */ + pcon.pc_len = SMT_MAX_INFO_LEN ; /* max para length */ + pcon.pc_err = 0 ; /* no error */ + pcon.pc_badset = 0 ; /* no bad set count */ + pcon.pc_p = (void *) (smt + 1) ; /* paras start here */ + + /* + * check authoriziation and set count + */ + error = 0 ; + if (set) { + if (!local && smt_authorize(smc,req)) + error = SMT_RDF_AUTHOR ; + else if (smt_check_set_count(smc,req)) + pcon.pc_badset = SMT_RDF_BADSET ; + } + /* + * add reason code and all mandatory parameters + */ + res = (struct smt_p_reason *) pcon.pc_p ; + smt_add_para(smc,&pcon,(u_short) SMT_P_REASON,0,0) ; + smt_add_para(smc,&pcon,(u_short) SMT_P1033,0,0) ; + /* update 1035 and 1036 later if set */ + set_pcon = pcon ; + smt_add_para(smc,&pcon,(u_short) SMT_P1035,0,0) ; + smt_add_para(smc,&pcon,(u_short) SMT_P1036,0,0) ; + + pcon.pc_err = error ; + len = req->smt_len ; + pa = (struct smt_para *) (req + 1) ; + /* + * process list of paras + */ + while (!pcon.pc_err && len > 0 ) { + if (((u_short)len < pa->p_len + PARA_LEN) || (pa->p_len & 3)) { + pcon.pc_err = SMT_RDF_LENGTH ; + break ; + } + + if (((range = (pa->p_type & 0xf000)) == 0x2000) || + range == 0x3000 || range == 0x4000) { + /* + * get index for PART,MAC ad PATH group + */ + index = *((u_char *)pa + PARA_LEN + 3) ;/* index */ + idx_end = index ; + if (!set && (pa->p_len != 4)) { + pcon.pc_err = SMT_RDF_LENGTH ; + break ; + } + if (!index && !set) { + switch (range) { + case 0x2000 : + index = INDEX_MAC ; + idx_end = index - 1 + NUMMACS ; + break ; + case 0x3000 : + index = INDEX_PATH ; + idx_end = index - 1 + NUMPATHS ; + break ; + case 0x4000 : + index = INDEX_PORT ; + idx_end = index - 1 + NUMPHYS ; +#ifndef CONCENTRATOR + if (smc->s.sas == SMT_SAS) + idx_end = INDEX_PORT ; +#endif + break ; + } + } + } + else { + /* + * smt group has no index + */ + if (!set && (pa->p_len != 0)) { + pcon.pc_err = SMT_RDF_LENGTH ; + break ; + } + index = 0 ; + idx_end = 0 ; + } + while (index <= idx_end) { + /* + * if group + * add all paras of group + */ + pt = smt_get_ptab(pa->p_type) ; + if (pt && pt->p_access == AC_GROUP && !set) { + pt++ ; + while (pt->p_access == AC_G || + pt->p_access == AC_GR) { + smt_add_para(smc,&pcon,pt->p_num, + index,local); + pt++ ; + } + } + /* + * ignore + * AUTHORIZATION in get/set + * SET COUNT in set + */ + else if (pa->p_type != SMT_P_AUTHOR && + (!set || (pa->p_type != SMT_P1035))) { + int st ; + if (pcon.pc_badset) { + smt_add_para(smc,&pcon,pa->p_type, + index,local) ; + } + else if (set) { + st = smt_set_para(smc,pa,index,local,1); + /* + * return para even if error + */ + smt_add_para(smc,&pcon,pa->p_type, + index,local) ; + pcon.pc_err = st ; + } + else { + if (pt && pt->p_access == AC_S) { + pcon.pc_err = + SMT_RDF_ILLEGAL ; + } + smt_add_para(smc,&pcon,pa->p_type, + index,local) ; + } + } + if (pcon.pc_err) + break ; + index++ ; + } + len -= pa->p_len + PARA_LEN ; + pa = (struct smt_para *) ((char *)pa + pa->p_len + PARA_LEN) ; + } + smt->smt_len = SMT_MAX_INFO_LEN - pcon.pc_len ; + mb->sm_len = smt->smt_len + sizeof(struct smt_header) ; + + /* update reason code */ + res->rdf_reason = pcon.pc_badset ? pcon.pc_badset : + pcon.pc_err ? pcon.pc_err : SMT_RDF_SUCCESS ; + if (set && (res->rdf_reason == SMT_RDF_SUCCESS)) { + /* + * increment set count + * set time stamp + * store station id of last set + */ + smc->mib.fddiSMTSetCount.count++ ; + smt_set_timestamp(smc,smc->mib.fddiSMTSetCount.timestamp) ; + smc->mib.fddiSMTLastSetStationId = req->smt_sid ; + smt_add_para(smc,&set_pcon,(u_short) SMT_P1035,0,0) ; + smt_add_para(smc,&set_pcon,(u_short) SMT_P1036,0,0) ; + } + return(mb) ; +} + +extern void *sm_to_para() ; + +static int smt_authorize(smc,sm) +struct s_smc *smc ; +struct smt_header *sm ; +{ + struct smt_para *pa ; + int i ; + char *p ; + + /* + * check source station id if not zero + */ + p = (char *) &smc->mib.fddiPRPMFStation ; + for (i = 0 ; i < 8 && !p[i] ; i++) + ; + if (i != 8) { + if (memcmp((char *) &sm->smt_sid, + (char *) &smc->mib.fddiPRPMFStation,8)) + return(1) ; + } + /* + * check authoriziation parameter if passwd not zero + */ + p = (char *) smc->mib.fddiPRPMFPasswd ; + for (i = 0 ; i < 8 && !p[i] ; i++) + ; + if (i != 8) { + pa = (struct smt_para *) sm_to_para(smc,sm,SMT_P_AUTHOR) ; + if (!pa) + return(1) ; + if (pa->p_len != 8) + return(1) ; + if (memcmp((char *)(pa+1),(char *)smc->mib.fddiPRPMFPasswd,8)) + return(1) ; + } + return(0) ; +} + +static int smt_check_set_count(smc,sm) +struct s_smc *smc ; +struct smt_header *sm ; +{ + struct smt_para *pa ; + struct smt_p_setcount *sc ; + + pa = (struct smt_para *) sm_to_para(smc,sm,SMT_P1035) ; + if (pa) { + sc = (struct smt_p_setcount *) pa ; + if ((smc->mib.fddiSMTSetCount.count != sc->count) || + memcmp((char *) smc->mib.fddiSMTSetCount.timestamp, + (char *)sc->timestamp,8)) + return(1) ; + } + return(0) ; +} + +void smt_add_para(smc,pcon,para,index,local) +struct s_smc *smc ; +struct s_pcon *pcon ; +u_short para ; +int index ; +int local ; +{ + struct smt_para *pa ; + const struct s_p_tab *pt ; + struct fddi_mib_m *mib_m = 0 ; + struct fddi_mib_p *mib_p = 0 ; + int len ; + int plen ; + char *from ; + char *to ; + const char *swap ; + char c ; + int range ; + char *mib_addr ; + int mac ; + int path ; + int port ; + int sp_len ; + + /* + * skip if errror + */ + if (pcon->pc_err) + return ; + + /* + * actions don't have a value + */ + pt = smt_get_ptab(para) ; + if (pt && pt->p_access == AC_S) + return ; + + to = (char *) (pcon->pc_p) ; /* destination pointer */ + len = pcon->pc_len ; /* free space */ + plen = len ; /* remember start length */ + pa = (struct smt_para *) to ; /* type/length pointer */ + to += PARA_LEN ; /* skip smt_para */ + len -= PARA_LEN ; + /* + * set index if required + */ + if (((range = (para & 0xf000)) == 0x2000) || + range == 0x3000 || range == 0x4000) { + if (len < 4) + goto wrong_error ; + to[0] = 0 ; + to[1] = 0 ; + to[2] = 0 ; + to[3] = index ; + len -= 4 ; + to += 4 ; + } + mac = index - INDEX_MAC ; + path = index - INDEX_PATH ; + port = index - INDEX_PORT ; + /* + * get pointer to mib + */ + switch (range) { + case 0x1000 : + default : + mib_addr = (char *) (&smc->mib) ; + break ; + case 0x2000 : + if (mac < 0 || mac >= NUMMACS) { + pcon->pc_err = SMT_RDF_NOPARAM ; + return ; + } + mib_addr = (char *) (&smc->mib.m[mac]) ; + mib_m = (struct fddi_mib_m *) mib_addr ; + break ; + case 0x3000 : + if (path < 0 || path >= NUMPATHS) { + pcon->pc_err = SMT_RDF_NOPARAM ; + return ; + } + mib_addr = (char *) (&smc->mib.a[path]) ; + break ; + case 0x4000 : + if (port < 0 || port >= smt_mib_phys(smc)) { + pcon->pc_err = SMT_RDF_NOPARAM ; + return ; + } + mib_addr = (char *) (&smc->mib.p[port_to_mib(smc,port)]) ; + mib_p = (struct fddi_mib_p *) mib_addr ; + break ; + } + /* + * check special paras + */ + swap = 0 ; + switch (para) { + case SMT_P10F0 : + case SMT_P10F1 : +#ifdef ESS + case SMT_P10F2 : + case SMT_P10F3 : + case SMT_P10F4 : + case SMT_P10F5 : + case SMT_P10F6 : + case SMT_P10F7 : +#endif +#ifdef SBA + case SMT_P10F8 : + case SMT_P10F9 : +#endif + case SMT_P20F1 : + if (!local) { + pcon->pc_err = SMT_RDF_NOPARAM ; + return ; + } + break ; + case SMT_P2034 : + case SMT_P2046 : + case SMT_P2047 : + case SMT_P204A : + case SMT_P2051 : + case SMT_P2052 : + mac_update_counter(smc) ; + break ; + case SMT_P4022: + mib_p->fddiPORTPC_LS = LS2MIB( + sm_pm_get_ls(smc,port_to_mib(smc,port))) ; + break ; + case SMT_P_REASON : + * (u_long *) to = 0 ; + sp_len = 4 ; + goto sp_done ; + case SMT_P1033 : /* time stamp */ + smt_set_timestamp(smc,smc->mib.fddiSMTTimeStamp) ; + break ; + + case SMT_P1020: /* port indexes */ +#if NUMPHYS == 12 + swap = "IIIIIIIIIIII" ; +#else +#if NUMPHYS == 2 + if (smc->s.sas == SMT_SAS) + swap = "I" ; + else + swap = "II" ; +#else +#if NUMPHYS == 24 + swap = "IIIIIIIIIIIIIIIIIIIIIIII" ; +#else + ???? +#endif +#endif +#endif + break ; + case SMT_P3212 : + { + sp_len = cem_build_path(smc,to,path) ; + goto sp_done ; + } + case SMT_P1048 : /* peer wrap condition */ + { + struct smt_p_1048 *sp ; + sp = (struct smt_p_1048 *) to ; + sp->p1048_flag = smc->mib.fddiSMTPeerWrapFlag ; + sp->p1048_cf_state = smc->mib.fddiSMTCF_State ; + sp_len = sizeof(struct smt_p_1048) ; + goto sp_done ; + } + case SMT_P208C : + { + struct smt_p_208c *sp ; + sp = (struct smt_p_208c *) to ; + sp->p208c_flag = + smc->mib.m[MAC0].fddiMACDuplicateAddressCond ; + sp->p208c_dupcondition = + (mib_m->fddiMACDA_Flag ? SMT_ST_MY_DUPA : 0) | + (mib_m->fddiMACUNDA_Flag ? SMT_ST_UNA_DUPA : 0); + sp->p208c_fddilong = + mib_m->fddiMACSMTAddress ; + sp->p208c_fddiunalong = + mib_m->fddiMACUpstreamNbr ; + sp->p208c_pad = 0 ; + sp_len = sizeof(struct smt_p_208c) ; + goto sp_done ; + } + case SMT_P208D : /* frame error condition */ + { + struct smt_p_208d *sp ; + sp = (struct smt_p_208d *) to ; + sp->p208d_flag = + mib_m->fddiMACFrameErrorFlag ; + sp->p208d_frame_ct = + mib_m->fddiMACFrame_Ct ; + sp->p208d_error_ct = + mib_m->fddiMACError_Ct ; + sp->p208d_lost_ct = + mib_m->fddiMACLost_Ct ; + sp->p208d_ratio = + mib_m->fddiMACFrameErrorRatio ; + sp_len = sizeof(struct smt_p_208d) ; + goto sp_done ; + } + case SMT_P208E : /* not copied condition */ + { + struct smt_p_208e *sp ; + sp = (struct smt_p_208e *) to ; + sp->p208e_flag = + mib_m->fddiMACNotCopiedFlag ; + sp->p208e_not_copied = + mib_m->fddiMACNotCopied_Ct ; + sp->p208e_copied = + mib_m->fddiMACCopied_Ct ; + sp->p208e_not_copied_ratio = + mib_m->fddiMACNotCopiedRatio ; + sp_len = sizeof(struct smt_p_208e) ; + goto sp_done ; + } + case SMT_P208F : /* neighbor change event */ + { + struct smt_p_208f *sp ; + sp = (struct smt_p_208f *) to ; + sp->p208f_multiple = + mib_m->fddiMACMultiple_N ; + sp->p208f_nacondition = + mib_m->fddiMACDuplicateAddressCond ; + sp->p208f_old_una = + mib_m->fddiMACOldUpstreamNbr ; + sp->p208f_new_una = + mib_m->fddiMACUpstreamNbr ; + sp->p208f_old_dna = + mib_m->fddiMACOldDownstreamNbr ; + sp->p208f_new_dna = + mib_m->fddiMACDownstreamNbr ; + sp->p208f_curren_path = + mib_m->fddiMACCurrentPath ; + sp->p208f_smt_address = + mib_m->fddiMACSMTAddress ; + sp_len = sizeof(struct smt_p_208f) ; + goto sp_done ; + } + case SMT_P2090 : + { + struct smt_p_2090 *sp ; + sp = (struct smt_p_2090 *) to ; + sp->p2090_multiple = + mib_m->fddiMACMultiple_P ; + sp->p2090_availablepaths = + mib_m->fddiMACAvailablePaths ; + sp->p2090_currentpath = + mib_m->fddiMACCurrentPath ; + sp->p2090_requestedpaths = + mib_m->fddiMACRequestedPaths ; + sp_len = sizeof(struct smt_p_2090) ; + goto sp_done ; + } + case SMT_P4050 : + { + struct smt_p_4050 *sp ; + sp = (struct smt_p_4050 *) to ; + sp->p4050_flag = + mib_p->fddiPORTLerFlag ; + sp->p4050_pad = 0 ; + sp->p4050_cutoff = + mib_p->fddiPORTLer_Cutoff ; ; + sp->p4050_alarm = + mib_p->fddiPORTLer_Alarm ; ; + sp->p4050_estimate = + mib_p->fddiPORTLer_Estimate ; + sp->p4050_reject_ct = + mib_p->fddiPORTLem_Reject_Ct ; + sp->p4050_ct = + mib_p->fddiPORTLem_Ct ; + sp_len = sizeof(struct smt_p_4050) ; + goto sp_done ; + } + + case SMT_P4051 : + { + struct smt_p_4051 *sp ; + sp = (struct smt_p_4051 *) to ; + sp->p4051_multiple = + mib_p->fddiPORTMultiple_U ; + sp->p4051_porttype = + mib_p->fddiPORTMy_Type ; + sp->p4051_connectstate = + mib_p->fddiPORTConnectState ; ; + sp->p4051_pc_neighbor = + mib_p->fddiPORTNeighborType ; + sp->p4051_pc_withhold = + mib_p->fddiPORTPC_Withhold ; + sp_len = sizeof(struct smt_p_4051) ; + goto sp_done ; + } + case SMT_P4052 : + { + struct smt_p_4052 *sp ; + sp = (struct smt_p_4052 *) to ; + sp->p4052_flag = + mib_p->fddiPORTEB_Condition ; + sp->p4052_eberrorcount = + mib_p->fddiPORTEBError_Ct ; + sp_len = sizeof(struct smt_p_4052) ; + goto sp_done ; + } + case SMT_P4053 : + { + struct smt_p_4053 *sp ; + sp = (struct smt_p_4053 *) to ; + sp->p4053_multiple = + mib_p->fddiPORTMultiple_P ; ; + sp->p4053_availablepaths = + mib_p->fddiPORTAvailablePaths ; + sp->p4053_currentpath = + mib_p->fddiPORTCurrentPath ; + memcpy( (char *) &sp->p4053_requestedpaths, + (char *) mib_p->fddiPORTRequestedPaths,4) ; + sp->p4053_mytype = + mib_p->fddiPORTMy_Type ; + sp->p4053_neighbortype = + mib_p->fddiPORTNeighborType ; + sp_len = sizeof(struct smt_p_4053) ; + goto sp_done ; + } + default : + break ; + } + /* + * in table ? + */ + if (!pt) { + pcon->pc_err = (para & 0xff00) ? SMT_RDF_NOPARAM : + SMT_RDF_ILLEGAL ; + return ; + } + /* + * check access rights + */ + switch (pt->p_access) { + case AC_G : + case AC_GR : + break ; + default : + pcon->pc_err = SMT_RDF_ILLEGAL ; + return ; + } + from = mib_addr + pt->p_offset ; + if (!swap) + swap = pt->p_swap ; /* pointer to swap string */ + + /* + * copy values + */ + while ((c = *swap++)) { + switch(c) { + case 'b' : + case 'w' : + case 'l' : + break ; + case 'S' : + case 'E' : + case 'R' : + case 'r' : + if (len < 4) + goto len_error ; + to[0] = 0 ; + to[1] = 0 ; +#ifdef LITTLE_ENDIAN + if (c == 'r') { + to[2] = *from++ ; + to[3] = *from++ ; + } + else { + to[3] = *from++ ; + to[2] = *from++ ; + } +#else + to[2] = *from++ ; + to[3] = *from++ ; +#endif + to += 4 ; + len -= 4 ; + break ; + case 'I' : /* for SET of port indexes */ + if (len < 2) + goto len_error ; +#ifdef LITTLE_ENDIAN + to[1] = *from++ ; + to[0] = *from++ ; +#else + to[0] = *from++ ; + to[1] = *from++ ; +#endif + to += 2 ; + len -= 2 ; + break ; + case 'F' : + case 'B' : + if (len < 4) + goto len_error ; + len -= 4 ; + to[0] = 0 ; + to[1] = 0 ; + to[2] = 0 ; + to[3] = *from++ ; + to += 4 ; + break ; + case 'C' : + case 'T' : + case 'L' : + if (len < 4) + goto len_error ; +#ifdef LITTLE_ENDIAN + to[3] = *from++ ; + to[2] = *from++ ; + to[1] = *from++ ; + to[0] = *from++ ; +#else + to[0] = *from++ ; + to[1] = *from++ ; + to[2] = *from++ ; + to[3] = *from++ ; +#endif + len -= 4 ; + to += 4 ; + break ; + case '2' : /* PortMacIndicated */ + if (len < 4) + goto len_error ; + to[0] = 0 ; + to[1] = 0 ; + to[2] = *from++ ; + to[3] = *from++ ; + len -= 4 ; + to += 4 ; + break ; + case '4' : + if (len < 4) + goto len_error ; + to[0] = *from++ ; + to[1] = *from++ ; + to[2] = *from++ ; + to[3] = *from++ ; + len -= 4 ; + to += 4 ; + break ; + case 'A' : + if (len < 8) + goto len_error ; + to[0] = 0 ; + to[1] = 0 ; + memcpy((char *) to+2,(char *) from,6) ; + to += 8 ; + from += 8 ; + len -= 8 ; + break ; + case '8' : + if (len < 8) + goto len_error ; + memcpy((char *) to,(char *) from,8) ; + to += 8 ; + from += 8 ; + len -= 8 ; + break ; + case 'D' : + if (len < 32) + goto len_error ; + memcpy((char *) to,(char *) from,32) ; + to += 32 ; + from += 32 ; + len -= 32 ; + break ; + case 'P' : /* timestamp is NOT swapped */ + if (len < 8) + goto len_error ; + to[0] = *from++ ; + to[1] = *from++ ; + to[2] = *from++ ; + to[3] = *from++ ; + to[4] = *from++ ; + to[5] = *from++ ; + to[6] = *from++ ; + to[7] = *from++ ; + to += 8 ; + len -= 8 ; + break ; + default : + SMT_PANIC(smc,SMT_E0119, SMT_E0119_MSG) ; + break ; + } + } + +done: + /* + * make it even (in case of 'I' encoding) + * note: len is DECREMENTED + */ + if (len & 3) { + to[0] = 0 ; + to[1] = 0 ; + to += 4 - (len & 3 ) ; + len = len & ~ 3 ; + } + + /* set type and length */ + pa->p_type = para ; + pa->p_len = plen - len - PARA_LEN ; + /* return values */ + pcon->pc_p = (void *) to ; + pcon->pc_len = len ; + return ; + +sp_done: + len -= sp_len ; + to += sp_len ; + goto done ; + +len_error: + /* parameter does not fit in frame */ + pcon->pc_err = SMT_RDF_TOOLONG ; + return ; + +wrong_error: + pcon->pc_err = SMT_RDF_LENGTH ; +} + +/* + * set parameter + */ +int smt_set_para(smc,pa,index,local,set) +struct s_smc *smc ; +struct smt_para *pa ; +int index ; +int local ; +int set ; +{ +#define IFSET(x) if (set) (x) + + const struct s_p_tab *pt ; + int len ; + char *from ; + char *to ; + const char *swap ; + char c ; + char *mib_addr ; + struct fddi_mib *mib ; + struct fddi_mib_m *mib_m = 0 ; + struct fddi_mib_a *mib_a = 0 ; + struct fddi_mib_p *mib_p = 0 ; + int mac ; + int path ; + int port ; + SK_LOC_DECL(u_char,byte_val) ; + SK_LOC_DECL(u_short,word_val) ; + SK_LOC_DECL(u_long,long_val) ; + + mac = index - INDEX_MAC ; + path = index - INDEX_PATH ; + port = index - INDEX_PORT ; + len = pa->p_len ; + from = (char *) (pa + 1 ) ; + + mib = &smc->mib ; + switch (pa->p_type & 0xf000) { + case 0x1000 : + default : + mib_addr = (char *) mib ; + break ; + case 0x2000 : + if (mac < 0 || mac >= NUMMACS) { + return(SMT_RDF_NOPARAM) ; + } + mib_m = &smc->mib.m[mac] ; + mib_addr = (char *) mib_m ; + from += 4 ; /* skip index */ + len -= 4 ; + break ; + case 0x3000 : + if (path < 0 || path >= NUMPATHS) { + return(SMT_RDF_NOPARAM) ; + } + mib_a = &smc->mib.a[path] ; + mib_addr = (char *) mib_a ; + from += 4 ; /* skip index */ + len -= 4 ; + break ; + case 0x4000 : + if (port < 0 || port >= smt_mib_phys(smc)) { + return(SMT_RDF_NOPARAM) ; + } + mib_p = &smc->mib.p[port_to_mib(smc,port)] ; + mib_addr = (char *) mib_p ; + from += 4 ; /* skip index */ + len -= 4 ; + break ; + } + switch (pa->p_type) { + case SMT_P10F0 : + case SMT_P10F1 : +#ifdef ESS + case SMT_P10F2 : + case SMT_P10F3 : + case SMT_P10F4 : + case SMT_P10F5 : + case SMT_P10F6 : + case SMT_P10F7 : +#endif +#ifdef SBA + case SMT_P10F8 : + case SMT_P10F9 : +#endif + case SMT_P20F1 : + if (!local) { + return(SMT_RDF_NOPARAM) ; + } + break ; + } + pt = smt_get_ptab(pa->p_type) ; + if (!pt) { + return( (pa->p_type & 0xff00) ? SMT_RDF_NOPARAM : + SMT_RDF_ILLEGAL ) ; + } + switch (pt->p_access) { + case AC_GR : + case AC_S : + break ; + default : + return(SMT_RDF_ILLEGAL) ; + } + to = mib_addr + pt->p_offset ; + swap = pt->p_swap ; /* pointer to swap string */ + + while (swap && (c = *swap++)) { + switch(c) { + case 'b' : + to = (char *) &byte_val ; + break ; + case 'w' : + to = (char *) &word_val ; + break ; + case 'l' : + to = (char *) &long_val ; + break ; + case 'S' : + case 'E' : + case 'R' : + case 'r' : + if (len < 4) { + goto len_error ; + } + if (from[0] | from[1]) + goto val_error ; +#ifdef LITTLE_ENDIAN + if (c == 'r') { + to[0] = from[2] ; + to[1] = from[3] ; + } + else { + to[1] = from[2] ; + to[0] = from[3] ; + } +#else + to[0] = from[2] ; + to[1] = from[3] ; +#endif + from += 4 ; + to += 2 ; + len -= 4 ; + break ; + case 'F' : + case 'B' : + if (len < 4) { + goto len_error ; + } + if (from[0] | from[1] | from[2]) + goto val_error ; + to[0] = from[3] ; + len -= 4 ; + from += 4 ; + to += 4 ; + break ; + case 'C' : + case 'T' : + case 'L' : + if (len < 4) { + goto len_error ; + } +#ifdef LITTLE_ENDIAN + to[3] = *from++ ; + to[2] = *from++ ; + to[1] = *from++ ; + to[0] = *from++ ; +#else + to[0] = *from++ ; + to[1] = *from++ ; + to[2] = *from++ ; + to[3] = *from++ ; +#endif + len -= 4 ; + to += 4 ; + break ; + case 'A' : + if (len < 8) + goto len_error ; + if (set) + memcpy((char *) to,(char *) from+2,6) ; + to += 8 ; + from += 8 ; + len -= 8 ; + break ; + case '4' : + if (len < 4) + goto len_error ; + if (set) + memcpy((char *) to,(char *) from,4) ; + to += 4 ; + from += 4 ; + len -= 4 ; + break ; + case '8' : + if (len < 8) + goto len_error ; + if (set) + memcpy((char *) to,(char *) from,8) ; + to += 8 ; + from += 8 ; + len -= 8 ; + break ; + case 'D' : + if (len < 32) + goto len_error ; + if (set) + memcpy((char *) to,(char *) from,32) ; + to += 32 ; + from += 32 ; + len -= 32 ; + break ; + case 'P' : /* timestamp is NOT swapped */ + if (set) { + to[0] = *from++ ; + to[1] = *from++ ; + to[2] = *from++ ; + to[3] = *from++ ; + to[4] = *from++ ; + to[5] = *from++ ; + to[6] = *from++ ; + to[7] = *from++ ; + } + to += 8 ; + len -= 8 ; + break ; + default : + SMT_PANIC(smc,SMT_E0120, SMT_E0120_MSG) ; + return(SMT_RDF_ILLEGAL) ; + } + } + /* + * actions and internal updates + */ + switch (pa->p_type) { + case SMT_P101A: /* fddiSMTConfigPolicy */ + if (word_val & ~1) + goto val_error ; + IFSET(mib->fddiSMTConfigPolicy = word_val) ; + break ; + case SMT_P101B : /* fddiSMTConnectionPolicy */ + if (!(word_val & POLICY_MM)) + goto val_error ; + IFSET(mib->fddiSMTConnectionPolicy = word_val) ; + break ; + case SMT_P101D : /* fddiSMTTT_Notify */ + if (word_val < 2 || word_val > 30) + goto val_error ; + IFSET(mib->fddiSMTTT_Notify = word_val) ; + break ; + case SMT_P101E : /* fddiSMTStatRptPolicy */ + if (byte_val & ~1) + goto val_error ; + IFSET(mib->fddiSMTStatRptPolicy = byte_val) ; + break ; + case SMT_P101F : /* fddiSMTTrace_MaxExpiration */ + /* + * note: lower limit trace_max = 6.001773... s + * NO upper limit + */ + if (long_val < (long)0x478bf51L) + goto val_error ; + IFSET(mib->fddiSMTTrace_MaxExpiration = long_val) ; + break ; +#ifdef ESS + case SMT_P10F2 : /* fddiESSPayload */ + if (long_val > 1562) + goto val_error ; + if (set && smc->mib.fddiESSPayload != long_val) { + smc->ess.raf_act_timer_poll = TRUE ; + smc->mib.fddiESSPayload = long_val ; + } + break ; + case SMT_P10F3 : /* fddiESSOverhead */ + if (long_val < 50 || long_val > 5000) + goto val_error ; + if (set && smc->mib.fddiESSPayload && + smc->mib.fddiESSOverhead != long_val) { + smc->ess.raf_act_timer_poll = TRUE ; + smc->mib.fddiESSOverhead = long_val ; + } + break ; + case SMT_P10F4 : /* fddiESSMaxTNeg */ + if (long_val > -MS2BCLK(5) || long_val < -MS2BCLK(165)) + goto val_error ; + IFSET(mib->fddiESSMaxTNeg = long_val) ; + break ; + case SMT_P10F5 : /* fddiESSMinSegmentSize */ + if (long_val < 1 || long_val > 4478) + goto val_error ; + IFSET(mib->fddiESSMinSegmentSize = long_val) ; + break ; + case SMT_P10F6 : /* fddiESSCategory */ + if ((long_val & 0xffff) != 1) + goto val_error ; + IFSET(mib->fddiESSCategory = long_val) ; + break ; + case SMT_P10F7 : /* fddiESSSyncTxMode */ + if (word_val > 1) + goto val_error ; + IFSET(mib->fddiESSSynchTxMode = word_val) ; + break ; +#endif +#ifdef SBA + case SMT_P10F8 : /* fddiSBACommand */ + if (byte_val != SB_STOP && byte_val != SB_START) + goto val_error ; + IFSET(mib->fddiSBACommand = byte_val) ; + break ; + case SMT_P10F9 : /* fddiSBAAvailable */ + if (byte_val > 100) + goto val_error ; + IFSET(mib->fddiSBAAvailable = byte_val) ; + break ; +#endif + case SMT_P2020 : /* fddiMACRequestedPaths */ + if ((word_val & (MIB_P_PATH_PRIM_PREFER | + MIB_P_PATH_PRIM_ALTER)) == 0 ) + goto val_error ; + IFSET(mib_m->fddiMACRequestedPaths = word_val) ; + break ; + case SMT_P205F : /* fddiMACFrameErrorThreshold */ + /* 0 .. ffff acceptable */ + IFSET(mib_m->fddiMACFrameErrorThreshold = word_val) ; + break ; + case SMT_P2067 : /* fddiMACNotCopiedThreshold */ + /* 0 .. ffff acceptable */ + IFSET(mib_m->fddiMACNotCopiedThreshold = word_val) ; + break ; + case SMT_P2076: /* fddiMACMA_UnitdataEnable */ + if (byte_val & ~1) + goto val_error ; + if (set) { + mib_m->fddiMACMA_UnitdataEnable = byte_val ; + queue_event(smc,EVENT_RMT,RM_ENABLE_FLAG) ; + } + break ; + case SMT_P20F1 : /* fddiMACT_Min */ + IFSET(mib_m->fddiMACT_Min = long_val) ; + break ; + case SMT_P320F : + if (long_val > 1562) + goto val_error ; + IFSET(mib_a->fddiPATHSbaPayload = long_val) ; +#ifdef ESS + if (set) + ess_para_change(smc) ; +#endif + break ; + case SMT_P3210 : + if (long_val > 5000) + goto val_error ; + + if (long_val != 0 && mib_a->fddiPATHSbaPayload == 0) + goto val_error ; + + IFSET(mib_a->fddiPATHSbaOverhead = long_val) ; +#ifdef ESS + if (set) + ess_para_change(smc) ; +#endif + break ; + case SMT_P3213: /* fddiPATHT_Rmode */ + /* no limit : + * 0 .. 343.597 => 0 .. 2e32 * 80nS + */ + if (set) { + mib_a->fddiPATHT_Rmode = long_val ; + rtm_set_timer(smc) ; + } + break ; + case SMT_P3214 : /* fddiPATHSbaAvailable */ + if (long_val > 0x00BEBC20L) + goto val_error ; +#ifdef SBA + if (set && mib->fddiSBACommand == SB_STOP) + goto val_error ; +#endif + IFSET(mib_a->fddiPATHSbaAvailable = long_val) ; + break ; + case SMT_P3215 : /* fddiPATHTVXLowerBound */ + IFSET(mib_a->fddiPATHTVXLowerBound = long_val) ; + goto change_mac_para ; + case SMT_P3216 : /* fddiPATHT_MaxLowerBound */ + IFSET(mib_a->fddiPATHT_MaxLowerBound = long_val) ; + goto change_mac_para ; + case SMT_P3217 : /* fddiPATHMaxT_Req */ + IFSET(mib_a->fddiPATHMaxT_Req = long_val) ; + +change_mac_para: + if (set && smt_set_mac_opvalues(smc)) { + RS_SET(smc,RS_EVENT) ; + smc->sm.please_reconnect = 1 ; + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + } + break ; + case SMT_P400E : /* fddiPORTConnectionPolicies */ + if (byte_val > 1) + goto val_error ; + IFSET(mib_p->fddiPORTConnectionPolicies = byte_val) ; + break ; + case SMT_P4011 : /* fddiPORTRequestedPaths */ + /* all 3*8 bits allowed */ + IFSET(memcpy((char *)mib_p->fddiPORTRequestedPaths, + (char *)&long_val,4)) ; + break ; + case SMT_P401F: /* fddiPORTMaint_LS */ + if (word_val > 4) + goto val_error ; + IFSET(mib_p->fddiPORTMaint_LS = word_val) ; + break ; + case SMT_P403A : /* fddiPORTLer_Cutoff */ + if (byte_val < 4 || byte_val > 15) + goto val_error ; + IFSET(mib_p->fddiPORTLer_Cutoff = byte_val) ; + break ; + case SMT_P403B : /* fddiPORTLer_Alarm */ + if (byte_val < 4 || byte_val > 15) + goto val_error ; + IFSET(mib_p->fddiPORTLer_Alarm = byte_val) ; + break ; + + /* + * Actions + */ + case SMT_P103C : /* fddiSMTStationAction */ + if (smt_action(smc,SMT_STATION_ACTION, (int) word_val, 0)) + goto val_error ; + break ; + case SMT_P4046: /* fddiPORTAction */ + if (smt_action(smc,SMT_PORT_ACTION, (int) word_val, + port_to_mib(smc,port))) + goto val_error ; + break ; + default : + break ; + } + return(0) ; + +val_error: + /* parameter value in frame is out of range */ + return(SMT_RDF_RANGE) ; + +len_error: + /* parameter value in frame is too short */ + return(SMT_RDF_LENGTH) ; + +#if 0 +no_author_error: + /* parameter not setable, because the SBA is not active + * Please note: we give the return code 'not authorizeed + * because SBA denied is not a valid return code in the + * PMF protocol. + */ + return(SMT_RDF_AUTHOR) ; +#endif +} + +static const struct s_p_tab *smt_get_ptab(para) +u_short para ; +{ + const struct s_p_tab *pt ; + for (pt = p_tab ; pt->p_num && pt->p_num != para ; pt++) + ; + return(pt->p_num ? pt : 0) ; +} + +static int smt_mib_phys(smc) +struct s_smc *smc ; +{ +#ifdef CONCENTRATOR + SK_UNUSED(smc) ; + + return(NUMPHYS) ; +#else + if (smc->s.sas == SMT_SAS) + return(1) ; + return(NUMPHYS) ; +#endif +} + +int port_to_mib(smc,p) +struct s_smc *smc ; +int p ; +{ +#ifdef CONCENTRATOR + SK_UNUSED(smc) ; + + return(p) ; +#else + if (smc->s.sas == SMT_SAS) + return(PS) ; + return(p) ; +#endif +} + + +#ifdef DEBUG +#ifndef BOOT +void dump_smt(smc,sm,text) +struct s_smc *smc ; +struct smt_header *sm ; +char *text ; +{ + int len ; + struct smt_para *pa ; + char *c ; + int n ; + int nn ; +#ifdef LITTLE_ENDIAN + int smtlen ; +#endif + + SK_UNUSED(smc) ; + +#ifdef DEBUG_BRD + if (smc->debug.d_smtf < 2) +#else + if (debug.d_smtf < 2) +#endif + return ; +#ifdef LITTLE_ENDIAN + smtlen = sm->smt_len + sizeof(struct smt_header) ; +#endif + printf("SMT Frame [%s]:\nDA ",text) ; + dump_hex((char *) &sm->smt_dest,6) ; + printf("\tSA ") ; + dump_hex((char *) &sm->smt_source,6) ; + printf(" Class %x Type %x Version %x\n", + sm->smt_class,sm->smt_type,sm->smt_version) ; + printf("TID %lx\t\tSID ",sm->smt_tid) ; + dump_hex((char *) &sm->smt_sid,8) ; + printf(" LEN %x\n",sm->smt_len) ; + + len = sm->smt_len ; + pa = (struct smt_para *) (sm + 1) ; + while (len > 0 ) { + int plen ; +#ifdef UNIX + printf("TYPE %x LEN %x VALUE\t",pa->p_type,pa->p_len) ; +#else + printf("TYPE %04x LEN %2x VALUE\t",pa->p_type,pa->p_len) ; +#endif + n = pa->p_len ; + if ( (n < 0 ) || (n > (int)(len - PARA_LEN))) { + n = len - PARA_LEN ; + printf(" BAD LENGTH\n") ; + break ; + } +#ifdef LITTLE_ENDIAN + smt_swap_para(sm,smtlen,0) ; +#endif + if (n < 24) { + dump_hex((char *)(pa+1),(int) n) ; + printf("\n") ; + } + else { + int first = 0 ; + c = (char *)(pa+1) ; + dump_hex(c,16) ; + printf("\n") ; + n -= 16 ; + c += 16 ; + while (n > 0) { + nn = (n > 16) ? 16 : n ; + if (n > 64) { + if (first == 0) + printf("\t\t\t...\n") ; + first = 1 ; + } + else { + printf("\t\t\t") ; + dump_hex(c,nn) ; + printf("\n") ; + } + n -= nn ; + c += 16 ; + } + } +#ifdef LITTLE_ENDIAN + smt_swap_para(sm,smtlen,1) ; +#endif + plen = (pa->p_len + PARA_LEN + 3) & ~3 ; + len -= plen ; + pa = (struct smt_para *)((char *)pa + plen) ; + } + printf("-------------------------------------------------\n\n") ; +} + +void dump_hex(p,len) +char *p ; +int len ; +{ + int n = 0 ; + while (len--) { + n++ ; +#ifdef UNIX + printf("%x%s",*p++ & 0xff,len ? ( (n & 7) ? " " : "-") : "") ; +#else + printf("%02x%s",*p++ & 0xff,len ? ( (n & 7) ? " " : "-") : "") ; +#endif + } +} +#endif /* no BOOT */ +#endif /* DEBUG */ + + +#endif /* no SLIM_SMT */ diff --git a/drivers/net/skfp/queue.c b/drivers/net/skfp/queue.c new file mode 100644 index 000000000000..e54b544ff273 --- /dev/null +++ b/drivers/net/skfp/queue.c @@ -0,0 +1,185 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT Event Queue Management +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)queue.c 2.9 97/08/04 (C) SK " ; +#endif + +#define PRINTF(a,b,c) + +/* + * init event queue management + */ +void ev_init(smc) +struct s_smc *smc ; +{ + smc->q.ev_put = smc->q.ev_get = smc->q.ev_queue ; +} + +/* + * add event to queue + */ +void queue_event(smc,class,event) +struct s_smc *smc ; +int class ; +int event ; +{ + PRINTF("queue class %d event %d\n",class,event) ; + smc->q.ev_put->class = class ; + smc->q.ev_put->event = event ; + if (++smc->q.ev_put == &smc->q.ev_queue[MAX_EVENT]) + smc->q.ev_put = smc->q.ev_queue ; + + if (smc->q.ev_put == smc->q.ev_get) { + SMT_ERR_LOG(smc,SMT_E0137, SMT_E0137_MSG) ; + } +} + +/* + * timer_event is called from HW timer package. + */ +void timer_event(smc,token) +struct s_smc *smc ; +u_long token ; +{ + PRINTF("timer event class %d token %d\n", + EV_T_CLASS(token), + EV_T_EVENT(token)) ; + queue_event(smc,EV_T_CLASS(token),EV_T_EVENT(token)); +} + +/* + * event dispatcher + * while event queue is not empty + * get event from queue + * send command to state machine + * end + */ +void ev_dispatcher(smc) +struct s_smc *smc ; +{ + struct event_queue *ev ; /* pointer into queue */ + int class ; + + ev = smc->q.ev_get ; + PRINTF("dispatch get %x put %x\n",ev,smc->q.ev_put) ; + while (ev != smc->q.ev_put) { + PRINTF("dispatch class %d event %d\n",ev->class,ev->event) ; + switch(class = ev->class) { + case EVENT_ECM : /* Entity Corordination Man. */ + ecm(smc,(int)ev->event) ; + break ; + case EVENT_CFM : /* Configuration Man. */ + cfm(smc,(int)ev->event) ; + break ; + case EVENT_RMT : /* Ring Man. */ + rmt(smc,(int)ev->event) ; + break ; + case EVENT_SMT : + smt_event(smc,(int)ev->event) ; + break ; +#ifdef CONCENTRATOR + case 99 : + timer_test_event(smc,(int)ev->event) ; + break ; +#endif + case EVENT_PCMA : /* PHY A */ + case EVENT_PCMB : /* PHY B */ + default : + if (class >= EVENT_PCMA && + class < EVENT_PCMA + NUMPHYS) { + pcm(smc,class - EVENT_PCMA,(int)ev->event) ; + break ; + } + SMT_PANIC(smc,SMT_E0121, SMT_E0121_MSG) ; + return ; + } + + if (++ev == &smc->q.ev_queue[MAX_EVENT]) + ev = smc->q.ev_queue ; + + /* Renew get: it is used in queue_events to detect overruns */ + smc->q.ev_get = ev; + } +} + +/* + * smt_online connects to or disconnects from the ring + * MUST be called to initiate connection establishment + * + * on 0 disconnect + * on 1 connect + */ +u_short smt_online(smc,on) +struct s_smc *smc ; +int on ; +{ + queue_event(smc,EVENT_ECM,on ? EC_CONNECT : EC_DISCONNECT) ; + ev_dispatcher(smc) ; + return(smc->mib.fddiSMTCF_State) ; +} + +/* + * set SMT flag to value + * flag flag name + * value flag value + * dump current flag setting + */ +#ifdef CONCENTRATOR +void do_smt_flag(smc,flag,value) +struct s_smc *smc ; +char *flag ; +int value ; +{ +#ifdef DEBUG + struct smt_debug *deb; + + SK_UNUSED(smc) ; + +#ifdef DEBUG_BRD + deb = &smc->debug; +#else + deb = &debug; +#endif + if (!strcmp(flag,"smt")) + deb->d_smt = value ; + else if (!strcmp(flag,"smtf")) + deb->d_smtf = value ; + else if (!strcmp(flag,"pcm")) + deb->d_pcm = value ; + else if (!strcmp(flag,"rmt")) + deb->d_rmt = value ; + else if (!strcmp(flag,"cfm")) + deb->d_cfm = value ; + else if (!strcmp(flag,"ecm")) + deb->d_ecm = value ; + printf("smt %d\n",deb->d_smt) ; + printf("smtf %d\n",deb->d_smtf) ; + printf("pcm %d\n",deb->d_pcm) ; + printf("rmt %d\n",deb->d_rmt) ; + printf("cfm %d\n",deb->d_cfm) ; + printf("ecm %d\n",deb->d_ecm) ; +#endif /* DEBUG */ +} +#endif diff --git a/drivers/net/skfp/rmt.c b/drivers/net/skfp/rmt.c new file mode 100644 index 000000000000..473eb0c9cdfe --- /dev/null +++ b/drivers/net/skfp/rmt.c @@ -0,0 +1,674 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT RMT + Ring Management +*/ + +/* + * Hardware independant state machine implemantation + * The following external SMT functions are referenced : + * + * queue_event() + * smt_timer_start() + * smt_timer_stop() + * + * The following external HW dependant functions are referenced : + * sm_ma_control() + * sm_mac_check_beacon_claim() + * + * The following HW dependant events are required : + * RM_RING_OP + * RM_RING_NON_OP + * RM_MY_BEACON + * RM_OTHER_BEACON + * RM_MY_CLAIM + * RM_TRT_EXP + * RM_VALID_CLAIM + * + */ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)rmt.c 2.13 99/07/02 (C) SK " ; +#endif + +/* + * FSM Macros + */ +#define AFLAG 0x10 +#define GO_STATE(x) (smc->mib.m[MAC0].fddiMACRMTState = (x)|AFLAG) +#define ACTIONS_DONE() (smc->mib.m[MAC0].fddiMACRMTState &= ~AFLAG) +#define ACTIONS(x) (x|AFLAG) + +#define RM0_ISOLATED 0 +#define RM1_NON_OP 1 /* not operational */ +#define RM2_RING_OP 2 /* ring operational */ +#define RM3_DETECT 3 /* detect dupl addresses */ +#define RM4_NON_OP_DUP 4 /* dupl. addr detected */ +#define RM5_RING_OP_DUP 5 /* ring oper. with dupl. addr */ +#define RM6_DIRECTED 6 /* sending directed beacons */ +#define RM7_TRACE 7 /* trace initiated */ + +#ifdef DEBUG +/* + * symbolic state names + */ +static const char * const rmt_states[] = { + "RM0_ISOLATED","RM1_NON_OP","RM2_RING_OP","RM3_DETECT", + "RM4_NON_OP_DUP","RM5_RING_OP_DUP","RM6_DIRECTED", + "RM7_TRACE" +} ; + +/* + * symbolic event names + */ +static const char * const rmt_events[] = { + "NONE","RM_RING_OP","RM_RING_NON_OP","RM_MY_BEACON", + "RM_OTHER_BEACON","RM_MY_CLAIM","RM_TRT_EXP","RM_VALID_CLAIM", + "RM_JOIN","RM_LOOP","RM_DUP_ADDR","RM_ENABLE_FLAG", + "RM_TIMEOUT_NON_OP","RM_TIMEOUT_T_STUCK", + "RM_TIMEOUT_ANNOUNCE","RM_TIMEOUT_T_DIRECT", + "RM_TIMEOUT_D_MAX","RM_TIMEOUT_POLL","RM_TX_STATE_CHANGE" +} ; +#endif + +/* + * Globals + * in struct s_rmt + */ + + +/* + * function declarations + */ +static void rmt_fsm() ; +static void start_rmt_timer0() ; +static void start_rmt_timer1() ; +static void start_rmt_timer2() ; +static void stop_rmt_timer0() ; +static void stop_rmt_timer1() ; +static void stop_rmt_timer2() ; +static void rmt_dup_actions() ; +static void rmt_reinsert_actions() ; +static void rmt_leave_actions() ; +static void rmt_new_dup_actions() ; + +#ifndef SUPERNET_3 +extern void restart_trt_for_dbcn() ; +#endif /*SUPERNET_3*/ + +/* + init RMT state machine + clear all RMT vars and flags +*/ +void rmt_init(smc) +struct s_smc *smc ; +{ + smc->mib.m[MAC0].fddiMACRMTState = ACTIONS(RM0_ISOLATED) ; + smc->r.dup_addr_test = DA_NONE ; + smc->r.da_flag = 0 ; + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + smc->r.sm_ma_avail = FALSE ; + smc->r.loop_avail = 0 ; + smc->r.bn_flag = 0 ; + smc->r.jm_flag = 0 ; + smc->r.no_flag = TRUE ; +} + +/* + RMT state machine + called by dispatcher + + do + display state change + process event + until SM is stable +*/ +void rmt(smc,event) +struct s_smc *smc ; +int event ; +{ + int state ; + + do { + DB_RMT("RMT : state %s%s", + (smc->mib.m[MAC0].fddiMACRMTState & AFLAG) ? "ACTIONS " : "", + rmt_states[smc->mib.m[MAC0].fddiMACRMTState & ~AFLAG]) ; + DB_RMT(" event %s\n",rmt_events[event],0) ; + state = smc->mib.m[MAC0].fddiMACRMTState ; + rmt_fsm(smc,event) ; + event = 0 ; + } while (state != smc->mib.m[MAC0].fddiMACRMTState) ; + rmt_state_change(smc,(int)smc->mib.m[MAC0].fddiMACRMTState) ; +} + +/* + process RMT event +*/ +static void rmt_fsm(smc,cmd) +struct s_smc *smc ; +int cmd ; +{ + /* + * RM00-RM70 : from all states + */ + if (!smc->r.rm_join && !smc->r.rm_loop && + smc->mib.m[MAC0].fddiMACRMTState != ACTIONS(RM0_ISOLATED) && + smc->mib.m[MAC0].fddiMACRMTState != RM0_ISOLATED) { + RS_SET(smc,RS_NORINGOP) ; + rmt_indication(smc,0) ; + GO_STATE(RM0_ISOLATED) ; + return ; + } + + switch(smc->mib.m[MAC0].fddiMACRMTState) { + case ACTIONS(RM0_ISOLATED) : + stop_rmt_timer0(smc) ; + stop_rmt_timer1(smc) ; + stop_rmt_timer2(smc) ; + + /* + * Disable MAC. + */ + sm_ma_control(smc,MA_OFFLINE) ; + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + smc->r.loop_avail = FALSE ; + smc->r.sm_ma_avail = FALSE ; + smc->r.no_flag = TRUE ; + DB_RMTN(1,"RMT : ISOLATED\n",0,0) ; + ACTIONS_DONE() ; + break ; + case RM0_ISOLATED : + /*RM01*/ + if (smc->r.rm_join || smc->r.rm_loop) { + /* + * According to the standard the MAC must be reset + * here. The FORMAC will be initialized and Claim + * and Beacon Frames will be uploaded to the MAC. + * So any change of Treq will take effect NOW. + */ + sm_ma_control(smc,MA_RESET) ; + GO_STATE(RM1_NON_OP) ; + break ; + } + break ; + case ACTIONS(RM1_NON_OP) : + start_rmt_timer0(smc,smc->s.rmt_t_non_op,RM_TIMEOUT_NON_OP) ; + stop_rmt_timer1(smc) ; + stop_rmt_timer2(smc) ; + sm_ma_control(smc,MA_BEACON) ; + DB_RMTN(1,"RMT : RING DOWN\n",0,0) ; + RS_SET(smc,RS_NORINGOP) ; + smc->r.sm_ma_avail = FALSE ; + rmt_indication(smc,0) ; + ACTIONS_DONE() ; + break ; + case RM1_NON_OP : + /*RM12*/ + if (cmd == RM_RING_OP) { + RS_SET(smc,RS_RINGOPCHANGE) ; + GO_STATE(RM2_RING_OP) ; + break ; + } + /*RM13*/ + else if (cmd == RM_TIMEOUT_NON_OP) { + smc->r.bn_flag = FALSE ; + smc->r.no_flag = TRUE ; + GO_STATE(RM3_DETECT) ; + break ; + } + break ; + case ACTIONS(RM2_RING_OP) : + stop_rmt_timer0(smc) ; + stop_rmt_timer1(smc) ; + stop_rmt_timer2(smc) ; + smc->r.no_flag = FALSE ; + if (smc->r.rm_loop) + smc->r.loop_avail = TRUE ; + if (smc->r.rm_join) { + smc->r.sm_ma_avail = TRUE ; + if (smc->mib.m[MAC0].fddiMACMA_UnitdataEnable) + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = TRUE ; + else + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + } + DB_RMTN(1,"RMT : RING UP\n",0,0) ; + RS_CLEAR(smc,RS_NORINGOP) ; + RS_SET(smc,RS_RINGOPCHANGE) ; + rmt_indication(smc,1) ; + smt_stat_counter(smc,0) ; + ACTIONS_DONE() ; + break ; + case RM2_RING_OP : + /*RM21*/ + if (cmd == RM_RING_NON_OP) { + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + smc->r.loop_avail = FALSE ; + RS_SET(smc,RS_RINGOPCHANGE) ; + GO_STATE(RM1_NON_OP) ; + break ; + } + /*RM22a*/ + else if (cmd == RM_ENABLE_FLAG) { + if (smc->mib.m[MAC0].fddiMACMA_UnitdataEnable) + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = TRUE ; + else + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + } + /*RM25*/ + else if (smc->r.dup_addr_test == DA_FAILED) { + smc->mib.m[MAC0].fddiMACMA_UnitdataAvailable = FALSE ; + smc->r.loop_avail = FALSE ; + smc->r.da_flag = TRUE ; + GO_STATE(RM5_RING_OP_DUP) ; + break ; + } + break ; + case ACTIONS(RM3_DETECT) : + start_rmt_timer0(smc,smc->s.mac_d_max*2,RM_TIMEOUT_D_MAX) ; + start_rmt_timer1(smc,smc->s.rmt_t_stuck,RM_TIMEOUT_T_STUCK) ; + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ; + sm_mac_check_beacon_claim(smc) ; + DB_RMTN(1,"RMT : RM3_DETECT\n",0,0) ; + ACTIONS_DONE() ; + break ; + case RM3_DETECT : + if (cmd == RM_TIMEOUT_POLL) { + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL); + sm_mac_check_beacon_claim(smc) ; + break ; + } + if (cmd == RM_TIMEOUT_D_MAX) { + smc->r.timer0_exp = TRUE ; + } + /* + *jd(22-Feb-1999) + * We need a time ">= 2*mac_d_max" since we had finished + * Claim or Beacon state. So we will restart timer0 at + * every state change. + */ + if (cmd == RM_TX_STATE_CHANGE) { + start_rmt_timer0(smc, + smc->s.mac_d_max*2, + RM_TIMEOUT_D_MAX) ; + } + /*RM32*/ + if (cmd == RM_RING_OP) { + GO_STATE(RM2_RING_OP) ; + break ; + } + /*RM33a*/ + else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) + && smc->r.bn_flag) { + smc->r.bn_flag = FALSE ; + } + /*RM33b*/ + else if (cmd == RM_TRT_EXP && !smc->r.bn_flag) { + int tx ; + /* + * set bn_flag only if in state T4 or T5: + * only if we're the beaconer should we start the + * trace ! + */ + if ((tx = sm_mac_get_tx_state(smc)) == 4 || tx == 5) { + DB_RMTN(2,"RMT : DETECT && TRT_EXPIRED && T4/T5\n",0,0); + smc->r.bn_flag = TRUE ; + /* + * If one of the upstream stations beaconed + * and the link to the upstream neighbor is + * lost we need to restart the stuck timer to + * check the "stuck beacon" condition. + */ + start_rmt_timer1(smc,smc->s.rmt_t_stuck, + RM_TIMEOUT_T_STUCK) ; + } + /* + * We do NOT need to clear smc->r.bn_flag in case of + * not being in state T4 or T5, because the flag + * must be cleared in order to get in this condition. + */ + + DB_RMTN(2, + "RMT : sm_mac_get_tx_state() = %d (bn_flag = %d)\n", + tx,smc->r.bn_flag) ; + } + /*RM34a*/ + else if (cmd == RM_MY_CLAIM && smc->r.timer0_exp) { + rmt_new_dup_actions(smc) ; + GO_STATE(RM4_NON_OP_DUP) ; + break ; + } + /*RM34b*/ + else if (cmd == RM_MY_BEACON && smc->r.timer0_exp) { + rmt_new_dup_actions(smc) ; + GO_STATE(RM4_NON_OP_DUP) ; + break ; + } + /*RM34c*/ + else if (cmd == RM_VALID_CLAIM) { + rmt_new_dup_actions(smc) ; + GO_STATE(RM4_NON_OP_DUP) ; + break ; + } + /*RM36*/ + else if (cmd == RM_TIMEOUT_T_STUCK && + smc->r.rm_join && smc->r.bn_flag) { + GO_STATE(RM6_DIRECTED) ; + break ; + } + break ; + case ACTIONS(RM4_NON_OP_DUP) : + start_rmt_timer0(smc,smc->s.rmt_t_announce,RM_TIMEOUT_ANNOUNCE); + start_rmt_timer1(smc,smc->s.rmt_t_stuck,RM_TIMEOUT_T_STUCK) ; + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ; + sm_mac_check_beacon_claim(smc) ; + DB_RMTN(1,"RMT : RM4_NON_OP_DUP\n",0,0) ; + ACTIONS_DONE() ; + break ; + case RM4_NON_OP_DUP : + if (cmd == RM_TIMEOUT_POLL) { + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL); + sm_mac_check_beacon_claim(smc) ; + break ; + } + /*RM41*/ + if (!smc->r.da_flag) { + GO_STATE(RM1_NON_OP) ; + break ; + } + /*RM44a*/ + else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) && + smc->r.bn_flag) { + smc->r.bn_flag = FALSE ; + } + /*RM44b*/ + else if (cmd == RM_TRT_EXP && !smc->r.bn_flag) { + int tx ; + /* + * set bn_flag only if in state T4 or T5: + * only if we're the beaconer should we start the + * trace ! + */ + if ((tx = sm_mac_get_tx_state(smc)) == 4 || tx == 5) { + DB_RMTN(2,"RMT : NOPDUP && TRT_EXPIRED && T4/T5\n",0,0); + smc->r.bn_flag = TRUE ; + /* + * If one of the upstream stations beaconed + * and the link to the upstream neighbor is + * lost we need to restart the stuck timer to + * check the "stuck beacon" condition. + */ + start_rmt_timer1(smc,smc->s.rmt_t_stuck, + RM_TIMEOUT_T_STUCK) ; + } + /* + * We do NOT need to clear smc->r.bn_flag in case of + * not being in state T4 or T5, because the flag + * must be cleared in order to get in this condition. + */ + + DB_RMTN(2, + "RMT : sm_mac_get_tx_state() = %d (bn_flag = %d)\n", + tx,smc->r.bn_flag) ; + } + /*RM44c*/ + else if (cmd == RM_TIMEOUT_ANNOUNCE && !smc->r.bn_flag) { + rmt_dup_actions(smc) ; + } + /*RM45*/ + else if (cmd == RM_RING_OP) { + smc->r.no_flag = FALSE ; + GO_STATE(RM5_RING_OP_DUP) ; + break ; + } + /*RM46*/ + else if (cmd == RM_TIMEOUT_T_STUCK && + smc->r.rm_join && smc->r.bn_flag) { + GO_STATE(RM6_DIRECTED) ; + break ; + } + break ; + case ACTIONS(RM5_RING_OP_DUP) : + stop_rmt_timer0(smc) ; + stop_rmt_timer1(smc) ; + stop_rmt_timer2(smc) ; + DB_RMTN(1,"RMT : RM5_RING_OP_DUP\n",0,0) ; + ACTIONS_DONE() ; + break; + case RM5_RING_OP_DUP : + /*RM52*/ + if (smc->r.dup_addr_test == DA_PASSED) { + smc->r.da_flag = FALSE ; + GO_STATE(RM2_RING_OP) ; + break ; + } + /*RM54*/ + else if (cmd == RM_RING_NON_OP) { + smc->r.jm_flag = FALSE ; + smc->r.bn_flag = FALSE ; + GO_STATE(RM4_NON_OP_DUP) ; + break ; + } + break ; + case ACTIONS(RM6_DIRECTED) : + start_rmt_timer0(smc,smc->s.rmt_t_direct,RM_TIMEOUT_T_DIRECT) ; + stop_rmt_timer1(smc) ; + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL) ; + sm_ma_control(smc,MA_DIRECTED) ; + RS_SET(smc,RS_BEACON) ; + DB_RMTN(1,"RMT : RM6_DIRECTED\n",0,0) ; + ACTIONS_DONE() ; + break ; + case RM6_DIRECTED : + /*RM63*/ + if (cmd == RM_TIMEOUT_POLL) { + start_rmt_timer2(smc,smc->s.rmt_t_poll,RM_TIMEOUT_POLL); + sm_mac_check_beacon_claim(smc) ; +#ifndef SUPERNET_3 + /* Because of problems with the Supernet II chip set + * sending of Directed Beacon will stop after 165ms + * therefore restart_trt_for_dbcn(smc) will be called + * to prevent this. + */ + restart_trt_for_dbcn(smc) ; +#endif /*SUPERNET_3*/ + break ; + } + if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) && + !smc->r.da_flag) { + smc->r.bn_flag = FALSE ; + GO_STATE(RM3_DETECT) ; + break ; + } + /*RM64*/ + else if ((cmd == RM_MY_BEACON || cmd == RM_OTHER_BEACON) && + smc->r.da_flag) { + smc->r.bn_flag = FALSE ; + GO_STATE(RM4_NON_OP_DUP) ; + break ; + } + /*RM67*/ + else if (cmd == RM_TIMEOUT_T_DIRECT) { + GO_STATE(RM7_TRACE) ; + break ; + } + break ; + case ACTIONS(RM7_TRACE) : + stop_rmt_timer0(smc) ; + stop_rmt_timer1(smc) ; + stop_rmt_timer2(smc) ; + smc->e.trace_prop |= ENTITY_BIT(ENTITY_MAC) ; + queue_event(smc,EVENT_ECM,EC_TRACE_PROP) ; + DB_RMTN(1,"RMT : RM7_TRACE\n",0,0) ; + ACTIONS_DONE() ; + break ; + case RM7_TRACE : + break ; + default: + SMT_PANIC(smc,SMT_E0122, SMT_E0122_MSG) ; + break; + } +} + +/* + * (jd) RMT duplicate address actions + * leave the ring or reinsert just as configured + */ +static void rmt_dup_actions(smc) +struct s_smc *smc ; +{ + if (smc->r.jm_flag) { + } + else { + if (smc->s.rmt_dup_mac_behavior) { + SMT_ERR_LOG(smc,SMT_E0138, SMT_E0138_MSG) ; + rmt_reinsert_actions(smc) ; + } + else { + SMT_ERR_LOG(smc,SMT_E0135, SMT_E0135_MSG) ; + rmt_leave_actions(smc) ; + } + } +} + +/* + * Reconnect to the Ring + */ +static void rmt_reinsert_actions(smc) +struct s_smc *smc ; +{ + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + queue_event(smc,EVENT_ECM,EC_CONNECT) ; +} + +/* + * duplicate address detected + */ +static void rmt_new_dup_actions(smc) +struct s_smc *smc ; +{ + smc->r.da_flag = TRUE ; + smc->r.bn_flag = FALSE ; + smc->r.jm_flag = FALSE ; + /* + * we have three options : change address, jam or leave + * we leave the ring as default + * Optionally it's possible to reinsert after leaving the Ring + * but this will not conform with SMT Spec. + */ + if (smc->s.rmt_dup_mac_behavior) { + SMT_ERR_LOG(smc,SMT_E0138, SMT_E0138_MSG) ; + rmt_reinsert_actions(smc) ; + } + else { + SMT_ERR_LOG(smc,SMT_E0135, SMT_E0135_MSG) ; + rmt_leave_actions(smc) ; + } +} + + +/* + * leave the ring + */ +static void rmt_leave_actions(smc) +struct s_smc *smc ; +{ + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + /* + * Note: Do NOT try again later. (with please reconnect) + * The station must be left from the ring! + */ +} + +/* + * SMT timer interface + * start RMT timer 0 + */ +static void start_rmt_timer0(smc,value,event) +struct s_smc *smc ; +u_long value ; +int event ; +{ + smc->r.timer0_exp = FALSE ; /* clear timer event flag */ + smt_timer_start(smc,&smc->r.rmt_timer0,value,EV_TOKEN(EVENT_RMT,event)); +} + +/* + * SMT timer interface + * start RMT timer 1 + */ +static void start_rmt_timer1(smc,value,event) +struct s_smc *smc ; +u_long value ; +int event ; +{ + smc->r.timer1_exp = FALSE ; /* clear timer event flag */ + smt_timer_start(smc,&smc->r.rmt_timer1,value,EV_TOKEN(EVENT_RMT,event)); +} + +/* + * SMT timer interface + * start RMT timer 2 + */ +static void start_rmt_timer2(smc,value,event) +struct s_smc *smc ; +u_long value ; +int event ; +{ + smc->r.timer2_exp = FALSE ; /* clear timer event flag */ + smt_timer_start(smc,&smc->r.rmt_timer2,value,EV_TOKEN(EVENT_RMT,event)); +} + +/* + * SMT timer interface + * stop RMT timer 0 + */ +static void stop_rmt_timer0(smc) +struct s_smc *smc ; +{ + if (smc->r.rmt_timer0.tm_active) + smt_timer_stop(smc,&smc->r.rmt_timer0) ; +} + +/* + * SMT timer interface + * stop RMT timer 1 + */ +static void stop_rmt_timer1(smc) +struct s_smc *smc ; +{ + if (smc->r.rmt_timer1.tm_active) + smt_timer_stop(smc,&smc->r.rmt_timer1) ; +} + +/* + * SMT timer interface + * stop RMT timer 2 + */ +static void stop_rmt_timer2(smc) +struct s_smc *smc ; +{ + if (smc->r.rmt_timer2.tm_active) + smt_timer_stop(smc,&smc->r.rmt_timer2) ; +} diff --git a/drivers/net/skfp/skfddi.c b/drivers/net/skfp/skfddi.c new file mode 100644 index 000000000000..45882720d4d4 --- /dev/null +++ b/drivers/net/skfp/skfddi.c @@ -0,0 +1,2614 @@ +/* + * File Name: + * skfddi.c + * + * Copyright Information: + * Copyright SysKonnect 1998,1999. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + * Abstract: + * A Linux device driver supporting the SysKonnect FDDI PCI controller + * familie. + * + * Maintainers: + * CG Christoph Goos (cgoos@syskonnect.de) + * + * Contributors: + * DM David S. Miller + * OT Oliver Teuber + * + * Address all questions to: + * linux@syskonnect.de + * + * The technical manual for the adapters is available from SysKonnect's + * web pages: www.syskonnect.com + * Goto "Support" and search Knowledge Base for "manual". + * + * Driver Architecture: + * The driver architecture is based on the DEC FDDI driver by + * Lawrence V. Stefani and several ethernet drivers. + * I also used an existing Windows NT miniport driver. + * All hardware dependant fuctions are handled by the SysKonnect + * Hardware Module. + * The only headerfiles that are directly related to this source + * are skfddi.c, h/types.h, h/osdef1st.h, h/targetos.h. + * The others belong to the SysKonnect FDDI Hardware Module and + * should better not be changed. + * NOTE: + * Compiling this driver produces some warnings, but I did not fix + * this, because the Hardware Module source is used for different + * drivers, and fixing it for Linux might bring problems on other + * projects. To keep the source common for all those drivers (and + * thus simplify fixes to it), please do not clean it up! + * + * Modification History: + * Date Name Description + * 02-Mar-98 CG Created. + * + * 10-Mar-99 CG Support for 2.2.x added. + * 25-Mar-99 CG Corrected IRQ routing for SMP (APIC) + * 26-Oct-99 CG Fixed compilation error on 2.2.13 + * 12-Nov-99 CG Source code release + * 15-Dec-99 CG Removed some printk statements + * 11-Jan-2000 CG Fixed printk statements + * 21-Jan-2000 OT Added byte counter + * 07-May-2000 DM 64 bit fixes, new dma interface + * 18-Jul-2000 CG Submitted to 2.2.17 kernel source tree. + * + * Compilation options (-Dxxx): + * DRIVERDEBUG print lots of messages to log file + * DUMPPACKETS print received/transmitted packets to logfile + * + * Tested cpu architectures: + * - i386 + * - sparc64 + */ + +/* Version information string - should be updated prior to */ +/* each new release!!! */ +#define VERSION "2.07" + +static const char *boot_msg = + "SysKonnect FDDI PCI Adapter driver v" VERSION " for\n" + " SK-55xx/SK-58xx adapters (SK-NET FDDI-FP/UP/LP)"; + +/* Include files */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // isdigit + +#include +#include +#include + +#include "h/types.h" +#undef ADDR // undo Linux definition +#include "h/skfbi.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smtstate.h" + + +// Define global routines +int skfp_probe(struct device *dev); + + +// Define module-wide (static) routines +static struct device *alloc_device(struct device *dev, u_long iobase); +static struct device *insert_device(struct device *dev, + int (*init) (struct device *)); +static int fddi_dev_index(unsigned char *s); +static void init_dev(struct device *dev, u_long iobase); +static void link_modules(struct device *dev, struct device *tmp); +static int skfp_driver_init(struct device *dev); +static int skfp_open(struct device *dev); +static int skfp_close(struct device *dev); +static void skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs); +static struct enet_statistics *skfp_ctl_get_stats(struct device *dev); +static void skfp_ctl_set_multicast_list(struct device *dev); +static void skfp_ctl_set_multicast_list_wo_lock(struct device *dev); +static int skfp_ctl_set_mac_address(struct device *dev, void *addr); +static int skfp_ioctl(struct device *dev, struct ifreq *rq, int cmd); +static int skfp_send_pkt(struct sk_buff *skb, struct device *dev); +static void send_queued_packets(struct s_smc *smc); +static void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr); +static void ResetAdapter(struct s_smc *smc); + + +// Functions needed by the hardware module +void *mac_drv_get_space(struct s_smc *smc, u_int size); +void *mac_drv_get_desc_mem(struct s_smc *smc, u_int size); +unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt); +unsigned long dma_master(struct s_smc *smc, void *virt, int len, int flag); +void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, + int flag); +void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd); +void llc_restart_tx(struct s_smc *smc); +void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count, int len); +void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count); +void mac_drv_fill_rxd(struct s_smc *smc); +void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count); +int mac_drv_rx_init(struct s_smc *smc, int len, int fc, char *look_ahead, + int la_len); +void smt_timer_poll(struct s_smc *smc); +void ring_status_indication(struct s_smc *smc, u_long status); +unsigned long smt_get_time(void); +void smt_stat_counter(struct s_smc *smc, int stat); +void cfm_state_change(struct s_smc *smc, int c_state); +void ecm_state_change(struct s_smc *smc, int e_state); +void pcm_state_change(struct s_smc *smc, int plc, int p_state); +void rmt_state_change(struct s_smc *smc, int r_state); +void drv_reset_indication(struct s_smc *smc); +void dump_data(unsigned char *Data, int length); + + +// External functions from the hardware module +extern u_int mac_drv_check_space(); +extern void read_address(struct s_smc *smc, u_char * mac_addr); +extern void card_stop(struct s_smc *smc); +extern int mac_drv_init(struct s_smc *smc); +extern void hwm_tx_frag(struct s_smc *smc, char far * virt, u_long phys, + int len, int frame_status); +extern int hwm_tx_init(struct s_smc *smc, u_char fc, int frag_count, + int frame_len, int frame_status); +extern int init_smt(struct s_smc *smc, u_char * mac_addr); +extern void fddi_isr(struct s_smc *smc); +extern void hwm_rx_frag(struct s_smc *smc, char far * virt, u_long phys, + int len, int frame_status); +extern void mac_drv_rx_mode(struct s_smc *smc, int mode); +extern void mac_drv_clear_tx_queue(struct s_smc *smc); +extern void mac_drv_clear_rx_queue(struct s_smc *smc); +extern void mac_clear_multicast(struct s_smc *smc); +extern void enable_tx_irq(struct s_smc *smc, u_short queue); +extern void mac_drv_clear_txd(struct s_smc *smc); + + +// Define module-wide (static) variables + +static int num_boards = 0; /* total number of adapters configured */ +static int num_fddi = 0; +static int autoprobed = 0; + +#ifdef MODULE +int init_module(void); +void cleanup_module(void); +static struct device *unlink_modules(struct device *p); +static int loading_module = 1; +#else +static int loading_module = 0; +#endif // MODULE + +#ifdef DRIVERDEBUG +#define PRINTK(s, args...) printk(s, ## args) +#else +#define PRINTK(s, args...) +#endif // DRIVERDEBUG + +#define PRIV(dev) (&(((struct s_smc *)dev->priv)->os)) + +/* + * ============== + * = skfp_probe = + * ============== + * + * Overview: + * Probes for supported FDDI PCI controllers + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This routine is called by the OS for each FDDI device name (fddi0, + * fddi1,...,fddi6, fddi7) specified in drivers/net/Space.c. + * If loaded as a module, it will detect and initialize all + * adapters the first time it is called. + * + * Let's say that skfp_probe() is getting called to initialize fddi0. + * Furthermore, let's say there are three supported controllers in the + * system. Before skfp_probe() leaves, devices fddi0, fddi1, and fddi2 + * will be initialized and a global flag will be set to indicate that + * skfp_probe() has already been called. + * + * However...the OS doesn't know that we've already initialized + * devices fddi1 and fddi2 so skfp_probe() gets called again and again + * until it reaches the end of the device list for FDDI (presently, + * fddi7). It's important that the driver "pretend" to probe for + * devices fddi1 and fddi2 and return success. Devices fddi3 + * through fddi7 will return failure since they weren't initialized. + * + * This algorithm seems to work for the time being. As other FDDI + * drivers are written for Linux, a more generic approach (perhaps + * similar to the Ethernet card approach) may need to be implemented. + * + * Return Codes: + * 0 - This device (fddi0, fddi1, etc) configured successfully + * -ENODEV - No devices present, or no SysKonnect FDDI PCI device + * present for this device name + * + * + * Side Effects: + * Device structures for FDDI adapters (fddi0, fddi1, etc) are + * initialized and the board resources are read and stored in + * the device structure. + */ +int skfp_probe(struct device *dev) +{ + int i; /* used in for loops */ + struct pci_dev *pdev = NULL; /* PCI device structure */ +#ifndef MEM_MAPPED_IO + u16 port; /* temporary I/O (port) address */ + int port_len; /* length of port address range (in bytes) */ +#else + unsigned long port; +#endif + u16 command; /* PCI Configuration space Command register val */ + struct s_smc *smc; /* board pointer */ + struct device *tmp = dev; + u8 first_dev_used = 0; + u16 SubSysId; +#ifdef __sparc_v9__ + unsigned short pci_tmp; +#endif + + PRINTK(KERN_INFO "entering skfp_probe\n"); + + /* + * Verify whether we're going through skfp_probe() again + * + * If so, see if we're going through for a subsequent fddi device that + * we've already initialized. If we are, return success (0). If not, + * return failure (-ENODEV). + */ + + if (autoprobed) { + PRINTK(KERN_INFO "Already entered skfp_probe\n"); + if (dev != NULL) { + if ((strncmp(dev->name, "fddi", 4) == 0) && + (dev->base_addr != 0)) { + return (0); + } + return (-ENODEV); + } + } + autoprobed = 1; /* set global flag */ + + printk("%s\n", boot_msg); + + /* Scan for Syskonnect FDDI PCI controllers */ + if (!pci_present()) { /* is PCI BIOS even present? */ + printk("no PCI BIOS present\n"); + return (-ENODEV); + } + for (i = 0; i < SKFP_MAX_NUM_BOARDS; i++) { // scan for PCI cards + PRINTK(KERN_INFO "Check device %d\n", i); + if ((pdev=pci_find_device(PCI_VENDOR_ID_SK, PCI_DEVICE_ID_SK_FP, + pdev)) == 0) { + break; + } + +#ifndef MEM_MAPPED_IO + /* Verify that I/O enable bit is set (PCI slot is enabled) */ + pci_read_config_word(pdev, PCI_COMMAND, &command); + if ((command & PCI_COMMAND_IO) == 0) { + PRINTK("I/O enable bit not set!"); + PRINTK(" Verify that slot is enabled\n"); + continue; + } + + /* Turn off memory mapped space and enable mastering */ + + PRINTK(KERN_INFO "Command Reg: %04x\n", command); + command |= PCI_COMMAND_MASTER; + command &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(pdev, PCI_COMMAND, command); + + /* Read I/O base address from PCI Configuration Space */ + + pci_read_config_word(pdev, PCI_BASE_ADDRESS_1, &port); + port &= PCI_BASE_ADDRESS_IO_MASK; // clear I/O bit (bit 0) + + /* Verify port address range is not already being used */ + + port_len = FP_IO_LEN; + if (check_region(port, port_len) != 0) { + printk("I/O range allocated to adapter"); + printk(" (0x%X-0x%X) is already being used!\n", port, + (port + port_len - 1)); + continue; + } +#else + /* Verify that MEM enable bit is set (PCI slot is enabled) */ + pci_read_config_word(pdev, PCI_COMMAND, &command); + if ((command & PCI_COMMAND_MEMORY) == 0) { + PRINTK("MEMORY-I/O enable bit not set!"); + PRINTK(" Verify that slot is enabled\n"); + continue; + } + + pci_set_master(pdev); + +#ifdef __sparc_v9__ + /* SPARC machines do not initialize the cache line size */ + pci_write_config_byte(pdev, PCI_CACHE_LINE_SIZE, 64); + /* SPARC machines do not set "Memory write and invalidate" */ + pci_read_config_word(pdev, PCI_COMMAND, &pci_tmp); + pci_tmp |= PCI_COMMAND_INVALIDATE; + pci_write_config_word(pdev, PCI_COMMAND, pci_tmp); +#endif + + port = pdev->base_address[0]; + port &= PCI_BASE_ADDRESS_MEM_MASK; + +#ifndef __sparc_v9__ + port = (unsigned long)ioremap(port, 0x4000); +#else + /* workaround for bug in 2.2 kernel for sparcv9 */ + /* use port as is */ +#endif + if (!port){ + printk("skfp: Unable to map MEMORY register, " + "FDDI adapter will be disabled.\n"); + break; + } +#endif + + if ((!loading_module) || first_dev_used) { + /* Allocate a device structure for this adapter */ + tmp = alloc_device(dev, port); + } + first_dev_used = 1; // only significant first time + + pci_read_config_word(pdev, PCI_SUBSYSTEM_ID, &SubSysId); + + if (tmp != NULL) { + if (loading_module) + link_modules(dev, tmp); + dev = tmp; + init_dev(dev, port); + dev->irq = pdev->irq; + + /* Initialize board structure with bus-specific info */ + + smc = (struct s_smc *) dev->priv; + smc->os.dev = dev; + smc->os.bus_type = SK_BUS_TYPE_PCI; + smc->os.pdev = *pdev; + smc->os.QueueSkb = MAX_TX_QUEUE_LEN; + smc->os.MaxFrameSize = MAX_FRAME_SIZE; + smc->os.dev = dev; + smc->hw.slot = -1; + smc->os.ResetRequested = FALSE; + skb_queue_head_init(&smc->os.SendSkbQueue); + + if (skfp_driver_init(dev) == 0) { + // only increment global board + // count on success + num_boards++; + request_region(dev->base_addr, + FP_IO_LEN, dev->name); + if ((SubSysId & 0xff00) == 0x5500 || + (SubSysId & 0xff00) == 0x5800) { + printk("%s: SysKonnect FDDI PCI adapter" + " found (SK-%04X)\n", dev->name, + SubSysId); + } else { + printk("%s: FDDI PCI adapter found\n", + dev->name); + } + } else { + kfree(dev); + i = SKFP_MAX_NUM_BOARDS; // stop search + + } + + } // if (dev != NULL) + + } // for SKFP_MAX_NUM_BOARDS + + /* + * If we're at this point we're going through skfp_probe() for the + * first time. Return success (0) if we've initialized 1 or more + * boards. Otherwise, return failure (-ENODEV). + */ + + if (num_boards > 0) + return (0); + else { + printk("no SysKonnect FDDI adapter found\n"); + return (-ENODEV); + } +} // skfp_probe + + +/************************ + * + * Search the entire 'fddi' device list for a fixed probe. If a match isn't + * found then check for an autoprobe or unused device location. If they + * are not available then insert a new device structure at the end of + * the current list. + * + ************************/ +static struct device *alloc_device(struct device *dev, u_long iobase) +{ + struct device *adev = NULL; + int fixed = 0, new_dev = 0; + + PRINTK(KERN_INFO "entering alloc_device\n"); + if (!dev) + return dev; + + num_fddi = fddi_dev_index(dev->name); + if (loading_module) { + num_fddi++; + dev = insert_device(dev, skfp_probe); + return dev; + } + while (1) { + if (((dev->base_addr == NO_ADDRESS) || + (dev->base_addr == 0)) && !adev) { + adev = dev; + } else if ((dev->priv == NULL) && (dev->base_addr == iobase)) { + fixed = 1; + } else { + if (dev->next == NULL) { + new_dev = 1; + } else if (strncmp(dev->next->name, "fddi", 4) != 0) { + new_dev = 1; + } + } + if ((dev->next == NULL) || new_dev || fixed) + break; + dev = dev->next; + num_fddi++; + } // while (1) + + if (adev && !fixed) { + dev = adev; + num_fddi = fddi_dev_index(dev->name); + new_dev = 0; + } + if (((dev->next == NULL) && ((dev->base_addr != NO_ADDRESS) && + (dev->base_addr != 0)) && !fixed) || + new_dev) { + num_fddi++; /* New device */ + dev = insert_device(dev, skfp_probe); + } + if (dev) { + if (!dev->priv) { + /* Allocate space for private board structure */ + dev->priv = (void *) kmalloc(sizeof(struct s_smc), + GFP_KERNEL); + if (dev->priv == NULL) { + printk("%s: Could not allocate memory for", + dev->name); + printk(" private board structure!\n"); + return (NULL); + } + /* clear structure */ + memset(dev->priv, 0, sizeof(struct s_smc)); + } + } + return dev; +} // alloc_device + + + +/************************ + * + * Initialize device structure + * + ************************/ +static void init_dev(struct device *dev, u_long iobase) +{ + /* Initialize new device structure */ + + dev->rmem_end = 0; /* shared memory isn't used */ + dev->rmem_start = 0; /* shared memory isn't used */ + dev->mem_end = 0; /* shared memory isn't used */ + dev->mem_start = 0; /* shared memory isn't used */ + dev->base_addr = iobase; /* save port (I/O) base address */ + dev->irq = 0; /* set in skfp_driver_init() */ + dev->if_port = 0; /* not applicable to FDDI adapters */ + dev->dma = 0; /* Bus Master DMA doesn't require channel */ + dev->interrupt = 0; + + dev->get_stats = &skfp_ctl_get_stats; + dev->open = &skfp_open; + dev->stop = &skfp_close; + dev->hard_start_xmit = &skfp_send_pkt; + dev->hard_header = NULL; /* set in fddi_setup() */ + dev->rebuild_header = NULL; /* set in fddi_setup() */ + dev->set_multicast_list = &skfp_ctl_set_multicast_list; + dev->set_mac_address = &skfp_ctl_set_mac_address; + dev->do_ioctl = &skfp_ioctl; + dev->set_config = NULL; /* not supported for now &&& */ + dev->header_cache_update = NULL; /* not supported */ + dev->change_mtu = NULL; /* set in fddi_setup() */ + + /* Initialize remaining device structure information */ + fddi_setup(dev); +} // init_device + + +/************************ + * + * If at end of fddi device list and can't use current entry, malloc + * one up. If memory could not be allocated, print an error message. + * +************************/ +static struct device *insert_device(struct device *dev, + int (*init) (struct device *)) +{ + struct device *new; + int len; + + PRINTK(KERN_INFO "entering insert_device\n"); + len = sizeof(struct device) + sizeof(struct s_smc); + new = (struct device *) kmalloc(len, GFP_KERNEL); + if (new == NULL) { + printk("fddi%d: Device not initialised, insufficient memory\n", + num_fddi); + return NULL; + } else { + memset((char *) new, 0, len); + new->name = (char *) (new + 1); + new->priv = (struct s_smc *) (new->name + 8); + new->init = init; /* initialisation routine */ + if (!loading_module) { + new->next = dev->next; + dev->next = new; + } + /* create new device name */ + if (num_fddi > 999) { + sprintf(new->name, "fddi????"); + } else { + sprintf(new->name, "fddi%d", num_fddi); + } + } + return new; +} // insert_device + + +/************************ + * + * Get the number of a "fddiX" string + * + ************************/ +static int fddi_dev_index(unsigned char *s) +{ + int i = 0, j = 0; + + for (; *s; s++) { + if (isdigit(*s)) { + j = 1; + i = (i * 10) + (*s - '0'); + } else if (j) + break; + } + return i; +} // fddi_dev_index + + +/************************ + * + * Used if loaded as module only. Link the device structures + * together. Needed to release them all at unload. + * +************************/ +static void link_modules(struct device *dev, struct device *tmp) +{ + struct device *p = dev; + + if (p) { + while (((struct s_smc *) (p->priv))->os.next_module) { + p = ((struct s_smc *) (p->priv))->os.next_module; + } + + if (dev != tmp) { + ((struct s_smc *) (p->priv))->os.next_module = tmp; + } else { + ((struct s_smc *) (p->priv))->os.next_module = NULL; + } + } + return; +} // link_modules + + + +/* + * ==================== + * = skfp_driver_init = + * ==================== + * + * Overview: + * Initializes remaining adapter board structure information + * and makes sure adapter is in a safe state prior to skfp_open(). + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This function allocates additional resources such as the host memory + * blocks needed by the adapter. + * The adapter is also reset. The OS must call skfp_open() to open + * the adapter and bring it on-line. + * + * Return Codes: + * 0 - initialization succeeded + * -1 - initialization failed + */ +static int skfp_driver_init(struct device *dev) +{ + struct s_smc *smc = (struct s_smc *) dev->priv; + skfddi_priv *bp = PRIV(dev); + u8 val; /* used for I/O read/writes */ + + PRINTK(KERN_INFO "entering skfp_driver_init\n"); + + // set the io address in private structures + bp->base_addr = dev->base_addr; + smc->hw.iop = dev->base_addr; + + // Get the interrupt level from the PCI Configuration Table + val = dev->irq; + + smc->hw.irq = val; + + spin_lock_init(&bp->DriverLock); + + // Determine the required size of the 'shared' memory area. + bp->SharedMemSize = mac_drv_check_space(); + PRINTK(KERN_INFO "Memory for HWM: %ld\n", bp->SharedMemSize); + if (bp->SharedMemSize > 0) { + bp->SharedMemSize += 16; // for descriptor alignment + + bp->SharedMemAddr = kmalloc(bp->SharedMemSize, GFP_KERNEL); + if (!bp->SharedMemSize) { + printk("could not allocate mem for "); + printk("hardware module: %ld byte\n", + bp->SharedMemSize); + return (-1); + } + bp->SharedMemHeap = 0; // Nothing used yet. + + } else { + bp->SharedMemAddr = NULL; + bp->SharedMemHeap = 0; + } // SharedMemSize > 0 + + memset(bp->SharedMemAddr, 0, bp->SharedMemSize); + + card_stop(smc); // Reset adapter. + + PRINTK(KERN_INFO "mac_drv_init()..\n"); + if (mac_drv_init(smc) != 0) { + PRINTK(KERN_INFO "mac_drv_init() failed.\n"); + return (-1); + } + read_address(smc, NULL); + PRINTK(KERN_INFO "HW-Addr: %02x %02x %02x %02x %02x %02x\n", + smc->hw.fddi_canon_addr.a[0], + smc->hw.fddi_canon_addr.a[1], + smc->hw.fddi_canon_addr.a[2], + smc->hw.fddi_canon_addr.a[3], + smc->hw.fddi_canon_addr.a[4], + smc->hw.fddi_canon_addr.a[5]); + memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6); + + smt_reset_defaults(smc, 0); + + return (0); +} // skfp_driver_init + + +/* + * ============= + * = skfp_open = + * ============= + * + * Overview: + * Opens the adapter + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This function brings the adapter to an operational state. + * + * Return Codes: + * 0 - Adapter was successfully opened + * -EAGAIN - Could not register IRQ + */ +static int skfp_open(struct device *dev) +{ + struct s_smc *smc = (struct s_smc *) dev->priv; + + PRINTK(KERN_INFO "entering skfp_open\n"); + /* Register IRQ - support shared interrupts by passing device ptr */ + if (request_irq(dev->irq, (void *) skfp_interrupt, SA_SHIRQ, + dev->name, dev)) { + printk("%s: Requested IRQ %d is busy\n", dev->name, dev->irq); + return (-EAGAIN); + } + /* + * Set current address to factory MAC address + * + * Note: We've already done this step in skfp_driver_init. + * However, it's possible that a user has set a node + * address override, then closed and reopened the + * adapter. Unless we reset the device address field + * now, we'll continue to use the existing modified + * address. + */ + read_address(smc, NULL); + memcpy(dev->dev_addr, smc->hw.fddi_canon_addr.a, 6); + + init_smt(smc, NULL); + smt_online(smc, 1); + STI_FBI(); + + MOD_INC_USE_COUNT; + + /* Set device structure info */ + dev->tbusy = 0; + dev->interrupt = 0; + dev->start = 1; + + /* Clear local multicast address tables */ + mac_clear_multicast(smc); + + /* Disable promiscuous filter settings */ + mac_drv_rx_mode(smc, RX_DISABLE_PROMISC); + + return (0); +} // skfp_open + + +/* + * ============== + * = skfp_close = + * ============== + * + * Overview: + * Closes the device/module. + * + * Returns: + * Condition code + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This routine closes the adapter and brings it to a safe state. + * The interrupt service routine is deregistered with the OS. + * The adapter can be opened again with another call to skfp_open(). + * + * Return Codes: + * Always return 0. + * + * Assumptions: + * No further requests for this adapter are made after this routine is + * called. skfp_open() can be called to reset and reinitialize the + * adapter. + */ +static int skfp_close(struct device *dev) +{ + struct s_smc *smc = (struct s_smc *) dev->priv; + struct sk_buff *skb; + skfddi_priv *bp = PRIV(dev); + + CLI_FBI(); + smt_reset_defaults(smc, 1); + card_stop(smc); + mac_drv_clear_tx_queue(smc); + mac_drv_clear_rx_queue(smc); + + /* Clear device structure flags */ + dev->start = 0; + dev->tbusy = 1; + + /* Deregister (free) IRQ */ + free_irq(dev->irq, dev); + + for (;;) { + skb = skb_dequeue(&bp->SendSkbQueue); + if (skb == NULL) + break; + bp->QueueSkb++; + dev_kfree_skb(skb); + } + + MOD_DEC_USE_COUNT; + + return (0); +} // skfp_close + + +/* + * ================== + * = skfp_interrupt = + * ================== + * + * Overview: + * Interrupt processing routine + * + * Returns: + * None + * + * Arguments: + * irq - interrupt vector + * dev_id - pointer to device information + * regs - pointer to registers structure + * + * Functional Description: + * This routine calls the interrupt processing routine for this adapter. It + * disables and reenables adapter interrupts, as appropriate. We can support + * shared interrupts since the incoming dev_id pointer provides our device + * structure context. All the real work is done in the hardware module. + * + * Return Codes: + * None + * + * Assumptions: + * The interrupt acknowledgement at the hardware level (eg. ACKing the PIC + * on Intel-based systems) is done by the operating system outside this + * routine. + * + * System interrupts are enabled through this call. + * + * Side Effects: + * Interrupts are disabled, then reenabled at the adapter. + */ + +void skfp_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + struct device *dev = (struct device *) dev_id; + struct s_smc *smc; /* private board structure pointer */ + skfddi_priv *bp = PRIV(dev); + + + if (dev == NULL) { + printk("%s: ERROR: irq %d for unknown device\n", dev->name, irq); + return; + } + if (dev->interrupt) { + printk("%s: ERROR: reentering interrupt handler\n", dev->name); + } + dev->interrupt = 1; + + smc = (struct s_smc *) dev->priv; + + // IRQs enabled or disabled ? + if (inpd(ADDR(B0_IMSK)) == 0) { + // IRQs are disabled. + // printk(KERN_INFO "Foreign IRQ (B).\n"); + dev->interrupt = 0; + return; + } + // Note: At this point, IRQs are enabled. + if ((inpd(ISR_A) & smc->hw.is_imask) == 0) { // IRQ? + // Adapter did not issue an IRQ. + // printk(KERN_INFO "Foreign IRQ (C).\n"); + dev->interrupt = 0; + return; + } + CLI_FBI(); // Disable IRQs from our adapter. + spin_lock(&bp->DriverLock); + + // Call interrupt handler in hardware module (HWM). + fddi_isr(smc); + + if (smc->os.ResetRequested) { + ResetAdapter(smc); + smc->os.ResetRequested = FALSE; + } + dev->interrupt = 0; + spin_unlock(&bp->DriverLock); + STI_FBI(); // Enable IRQs from our adapter. + + return; +} // skfp_interrupt + + +/* + * ====================== + * = skfp_ctl_get_stats = + * ====================== + * + * Overview: + * Get statistics for FDDI adapter + * + * Returns: + * Pointer to FDDI statistics structure + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * Gets current MIB objects from adapter, then + * returns FDDI statistics structure as defined + * in if_fddi.h. + * + * Note: Since the FDDI statistics structure is + * still new and the device structure doesn't + * have an FDDI-specific get statistics handler, + * we'll return the FDDI statistics structure as + * a pointer to an Ethernet statistics structure. + * That way, at least the first part of the statistics + * structure can be decoded properly. + * We'll have to pay attention to this routine as the + * device structure becomes more mature and LAN media + * independent. + * + */ +struct enet_statistics *skfp_ctl_get_stats(struct device *dev) +{ + struct s_smc *bp = (struct s_smc *) dev->priv; + + /* Fill the bp->stats structure with driver-maintained counters */ + + bp->os.MacStat.port_bs_flag[0] = 0x1234; + bp->os.MacStat.port_bs_flag[1] = 0x5678; +// goos: need to fill out fddi statistic +#if 0 + /* Get FDDI SMT MIB objects */ + +/* Fill the bp->stats structure with the SMT MIB object values */ + + memcpy(bp->stats.smt_station_id, &bp->cmd_rsp_virt->smt_mib_get.smt_station_id, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_station_id)); + bp->stats.smt_op_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_op_version_id; + bp->stats.smt_hi_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_hi_version_id; + bp->stats.smt_lo_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_lo_version_id; + memcpy(bp->stats.smt_user_data, &bp->cmd_rsp_virt->smt_mib_get.smt_user_data, sizeof(bp->cmd_rsp_virt->smt_mib_get.smt_user_data)); + bp->stats.smt_mib_version_id = bp->cmd_rsp_virt->smt_mib_get.smt_mib_version_id; + bp->stats.smt_mac_cts = bp->cmd_rsp_virt->smt_mib_get.smt_mac_ct; + bp->stats.smt_non_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_non_master_ct; + bp->stats.smt_master_cts = bp->cmd_rsp_virt->smt_mib_get.smt_master_ct; + bp->stats.smt_available_paths = bp->cmd_rsp_virt->smt_mib_get.smt_available_paths; + bp->stats.smt_config_capabilities = bp->cmd_rsp_virt->smt_mib_get.smt_config_capabilities; + bp->stats.smt_config_policy = bp->cmd_rsp_virt->smt_mib_get.smt_config_policy; + bp->stats.smt_connection_policy = bp->cmd_rsp_virt->smt_mib_get.smt_connection_policy; + bp->stats.smt_t_notify = bp->cmd_rsp_virt->smt_mib_get.smt_t_notify; + bp->stats.smt_stat_rpt_policy = bp->cmd_rsp_virt->smt_mib_get.smt_stat_rpt_policy; + bp->stats.smt_trace_max_expiration = bp->cmd_rsp_virt->smt_mib_get.smt_trace_max_expiration; + bp->stats.smt_bypass_present = bp->cmd_rsp_virt->smt_mib_get.smt_bypass_present; + bp->stats.smt_ecm_state = bp->cmd_rsp_virt->smt_mib_get.smt_ecm_state; + bp->stats.smt_cf_state = bp->cmd_rsp_virt->smt_mib_get.smt_cf_state; + bp->stats.smt_remote_disconnect_flag = bp->cmd_rsp_virt->smt_mib_get.smt_remote_disconnect_flag; + bp->stats.smt_station_status = bp->cmd_rsp_virt->smt_mib_get.smt_station_status; + bp->stats.smt_peer_wrap_flag = bp->cmd_rsp_virt->smt_mib_get.smt_peer_wrap_flag; + bp->stats.smt_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_msg_time_stamp.ls; + bp->stats.smt_transition_time_stamp = bp->cmd_rsp_virt->smt_mib_get.smt_transition_time_stamp.ls; + bp->stats.mac_frame_status_functions = bp->cmd_rsp_virt->smt_mib_get.mac_frame_status_functions; + bp->stats.mac_t_max_capability = bp->cmd_rsp_virt->smt_mib_get.mac_t_max_capability; + bp->stats.mac_tvx_capability = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_capability; + bp->stats.mac_available_paths = bp->cmd_rsp_virt->smt_mib_get.mac_available_paths; + bp->stats.mac_current_path = bp->cmd_rsp_virt->smt_mib_get.mac_current_path; + memcpy(bp->stats.mac_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_upstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_downstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_old_upstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_upstream_nbr, FDDI_K_ALEN); + memcpy(bp->stats.mac_old_downstream_nbr, &bp->cmd_rsp_virt->smt_mib_get.mac_old_downstream_nbr, FDDI_K_ALEN); + bp->stats.mac_dup_address_test = bp->cmd_rsp_virt->smt_mib_get.mac_dup_address_test; + bp->stats.mac_requested_paths = bp->cmd_rsp_virt->smt_mib_get.mac_requested_paths; + bp->stats.mac_downstream_port_type = bp->cmd_rsp_virt->smt_mib_get.mac_downstream_port_type; + memcpy(bp->stats.mac_smt_address, &bp->cmd_rsp_virt->smt_mib_get.mac_smt_address, FDDI_K_ALEN); + bp->stats.mac_t_req = bp->cmd_rsp_virt->smt_mib_get.mac_t_req; + bp->stats.mac_t_neg = bp->cmd_rsp_virt->smt_mib_get.mac_t_neg; + bp->stats.mac_t_max = bp->cmd_rsp_virt->smt_mib_get.mac_t_max; + bp->stats.mac_tvx_value = bp->cmd_rsp_virt->smt_mib_get.mac_tvx_value; + bp->stats.mac_frame_error_threshold = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_threshold; + bp->stats.mac_frame_error_ratio = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_ratio; + bp->stats.mac_rmt_state = bp->cmd_rsp_virt->smt_mib_get.mac_rmt_state; + bp->stats.mac_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_da_flag; + bp->stats.mac_una_da_flag = bp->cmd_rsp_virt->smt_mib_get.mac_unda_flag; + bp->stats.mac_frame_error_flag = bp->cmd_rsp_virt->smt_mib_get.mac_frame_error_flag; + bp->stats.mac_ma_unitdata_available = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_available; + bp->stats.mac_hardware_present = bp->cmd_rsp_virt->smt_mib_get.mac_hardware_present; + bp->stats.mac_ma_unitdata_enable = bp->cmd_rsp_virt->smt_mib_get.mac_ma_unitdata_enable; + bp->stats.path_tvx_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_tvx_lower_bound; + bp->stats.path_t_max_lower_bound = bp->cmd_rsp_virt->smt_mib_get.path_t_max_lower_bound; + bp->stats.path_max_t_req = bp->cmd_rsp_virt->smt_mib_get.path_max_t_req; + memcpy(bp->stats.path_configuration, &bp->cmd_rsp_virt->smt_mib_get.path_configuration, sizeof(bp->cmd_rsp_virt->smt_mib_get.path_configuration)); + bp->stats.port_my_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[0]; + bp->stats.port_my_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_my_type[1]; + bp->stats.port_neighbor_type[0] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[0]; + bp->stats.port_neighbor_type[1] = bp->cmd_rsp_virt->smt_mib_get.port_neighbor_type[1]; + bp->stats.port_connection_policies[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[0]; + bp->stats.port_connection_policies[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_policies[1]; + bp->stats.port_mac_indicated[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[0]; + bp->stats.port_mac_indicated[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_indicated[1]; + bp->stats.port_current_path[0] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[0]; + bp->stats.port_current_path[1] = bp->cmd_rsp_virt->smt_mib_get.port_current_path[1]; + memcpy(&bp->stats.port_requested_paths[0 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[0], 3); + memcpy(&bp->stats.port_requested_paths[1 * 3], &bp->cmd_rsp_virt->smt_mib_get.port_requested_paths[1], 3); + bp->stats.port_mac_placement[0] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[0]; + bp->stats.port_mac_placement[1] = bp->cmd_rsp_virt->smt_mib_get.port_mac_placement[1]; + bp->stats.port_available_paths[0] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[0]; + bp->stats.port_available_paths[1] = bp->cmd_rsp_virt->smt_mib_get.port_available_paths[1]; + bp->stats.port_pmd_class[0] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[0]; + bp->stats.port_pmd_class[1] = bp->cmd_rsp_virt->smt_mib_get.port_pmd_class[1]; + bp->stats.port_connection_capabilities[0] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[0]; + bp->stats.port_connection_capabilities[1] = bp->cmd_rsp_virt->smt_mib_get.port_connection_capabilities[1]; + bp->stats.port_bs_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[0]; + bp->stats.port_bs_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_bs_flag[1]; + bp->stats.port_ler_estimate[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[0]; + bp->stats.port_ler_estimate[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_estimate[1]; + bp->stats.port_ler_cutoff[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[0]; + bp->stats.port_ler_cutoff[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_cutoff[1]; + bp->stats.port_ler_alarm[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[0]; + bp->stats.port_ler_alarm[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_alarm[1]; + bp->stats.port_connect_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[0]; + bp->stats.port_connect_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_connect_state[1]; + bp->stats.port_pcm_state[0] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[0]; + bp->stats.port_pcm_state[1] = bp->cmd_rsp_virt->smt_mib_get.port_pcm_state[1]; + bp->stats.port_pc_withhold[0] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[0]; + bp->stats.port_pc_withhold[1] = bp->cmd_rsp_virt->smt_mib_get.port_pc_withhold[1]; + bp->stats.port_ler_flag[0] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[0]; + bp->stats.port_ler_flag[1] = bp->cmd_rsp_virt->smt_mib_get.port_ler_flag[1]; + bp->stats.port_hardware_present[0] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[0]; + bp->stats.port_hardware_present[1] = bp->cmd_rsp_virt->smt_mib_get.port_hardware_present[1]; + + + /* Fill the bp->stats structure with the FDDI counter values */ + + bp->stats.mac_frame_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.frame_cnt.ls; + bp->stats.mac_copied_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.copied_cnt.ls; + bp->stats.mac_transmit_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.transmit_cnt.ls; + bp->stats.mac_error_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.error_cnt.ls; + bp->stats.mac_lost_cts = bp->cmd_rsp_virt->cntrs_get.cntrs.lost_cnt.ls; + bp->stats.port_lct_fail_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[0].ls; + bp->stats.port_lct_fail_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lct_rejects[1].ls; + bp->stats.port_lem_reject_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[0].ls; + bp->stats.port_lem_reject_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.lem_rejects[1].ls; + bp->stats.port_lem_cts[0] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[0].ls; + bp->stats.port_lem_cts[1] = bp->cmd_rsp_virt->cntrs_get.cntrs.link_errors[1].ls; + +#endif + return ((struct enet_statistics *) &bp->os.MacStat); +} // ctl_get_stat + + +/* + * ============================== + * = skfp_ctl_set_multicast_list = + * ============================== + * + * Overview: + * Enable/Disable LLC frame promiscuous mode reception + * on the adapter and/or update multicast address table. + * + * Returns: + * None + * + * Arguments: + * dev - pointer to device information + * + * Functional Description: + * This function aquires the driver lock and only calls + * skfp_ctl_set_multicast_list_wo_lock then. + * This routine follows a fairly simple algorithm for setting the + * adapter filters and CAM: + * + * if IFF_PROMISC flag is set + * enable promiscuous mode + * else + * disable promiscuous mode + * if number of multicast addresses <= max. multicast number + * add mc addresses to adapter table + * else + * enable promiscuous mode + * update adapter filters + * + * Assumptions: + * Multicast addresses are presented in canonical (LSB) format. + * + * Side Effects: + * On-board adapter filters are updated. + */ +static void skfp_ctl_set_multicast_list(struct device *dev) +{ +#ifdef __SMP__ + skfddi_priv *bp = PRIV(dev); +#endif + unsigned long Flags; + + spin_lock_irqsave(&bp->DriverLock, Flags); + skfp_ctl_set_multicast_list_wo_lock(dev); + spin_unlock_irqrestore(&bp->DriverLock, Flags); + return; +} // skfp_ctl_set_multicast_list + + + +static void skfp_ctl_set_multicast_list_wo_lock(struct device *dev) +{ + struct s_smc *smc = (struct s_smc *) dev->priv; + struct dev_mc_list *dmi; /* ptr to multicast addr entry */ + int i; + + /* Enable promiscuous mode, if necessary */ + if (dev->flags & IFF_PROMISC) { + mac_drv_rx_mode(smc, RX_ENABLE_PROMISC); + PRINTK(KERN_INFO "PROMISCUOUS MODE ENABLED\n"); + } + /* Else, update multicast address table */ + else { + mac_drv_rx_mode(smc, RX_DISABLE_PROMISC); + PRINTK(KERN_INFO "PROMISCUOUS MODE DISABLED\n"); + + // Reset all MC addresses + mac_clear_multicast(smc); + mac_drv_rx_mode(smc, RX_DISABLE_ALLMULTI); + + if (dev->flags & IFF_ALLMULTI) { + mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI); + PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n"); + } else if (dev->mc_count > 0) { + if (dev->mc_count <= FPMAX_MULTICAST) { + /* use exact filtering */ + + // point to first multicast addr + dmi = dev->mc_list; + + for (i = 0; i < dev->mc_count; i++) { + mac_add_multicast(smc, + dmi->dmi_addr, 1); + PRINTK(KERN_INFO "ENABLE MC ADDRESS:"); + PRINTK(" %02x %02x %02x ", + dmi->dmi_addr[0], + dmi->dmi_addr[1], + dmi->dmi_addr[2]); + PRINTK("%02x %02x %02x\n", + dmi->dmi_addr[3], + dmi->dmi_addr[4], + dmi->dmi_addr[5]); + dmi = dmi->next; + } // for + + } else { // more MC addresses than HW supports + + mac_drv_rx_mode(smc, RX_ENABLE_ALLMULTI); + PRINTK(KERN_INFO "ENABLE ALL MC ADDRESSES\n"); + } + } else { // no MC addresses + + PRINTK(KERN_INFO "DISABLE ALL MC ADDRESSES\n"); + } + + /* Update adapter filters */ + mac_update_multicast(smc); + } + return; +} // skfp_ctl_set_multicast_list_wo_lock + + +/* + * =========================== + * = skfp_ctl_set_mac_address = + * =========================== + * + * Overview: + * set new mac address on adapter and update dev_addr field in device table. + * + * Returns: + * None + * + * Arguments: + * dev - pointer to device information + * addr - pointer to sockaddr structure containing unicast address to set + * + * Assumptions: + * The address pointed to by addr->sa_data is a valid unicast + * address and is presented in canonical (LSB) format. + */ +static int skfp_ctl_set_mac_address(struct device *dev, void *addr) +{ + struct s_smc *smc = (struct s_smc *) dev->priv; + struct sockaddr *p_sockaddr = (struct sockaddr *) addr; +#ifdef __SMP__ + skfddi_priv *bp = (skfddi_priv *) & smc->os; +#endif + unsigned long Flags; + + + memcpy(dev->dev_addr, p_sockaddr->sa_data, FDDI_K_ALEN); + spin_lock_irqsave(&bp->DriverLock, Flags); + ResetAdapter(smc); + spin_unlock_irqrestore(&bp->DriverLock, Flags); + + return (0); /* always return zero */ +} // skfp_ctl_set_mac_address + + +/* + * ============== + * = skfp_ioctl = + * ============== + * + * Overview: + * + * Perform IOCTL call functions here. Some are privileged operations and the + * effective uid is checked in those cases. + * + * Returns: + * status value + * 0 - success + * other - failure + * + * Arguments: + * dev - pointer to device information + * rq - pointer to ioctl request structure + * cmd - ? + * + */ + + +static int skfp_ioctl(struct device *dev, struct ifreq *rq, int cmd) +{ + skfddi_priv *lp = PRIV(dev); + struct s_skfp_ioctl ioc; + int status = 0; + + copy_from_user(&ioc, rq->ifr_data, sizeof(struct s_skfp_ioctl)); + switch (ioc.cmd) { + case SKFP_GET_STATS: /* Get the driver statistics */ + ioc.len = sizeof(lp->MacStat); + copy_to_user(ioc.data, skfp_ctl_get_stats(dev), ioc.len); + break; + case SKFP_CLR_STATS: /* Zero out the driver statistics */ + if (suser()) { + memset(&lp->MacStat, 0, sizeof(lp->MacStat)); + } else { + status = -EPERM; + } + break; + default: + printk("ioctl for %s: unknow cmd: %04x\n", dev->name, ioc.cmd); + } // switch + + return status; +} // skfp_ioctl + + +/* + * ===================== + * = skfp_send_pkt = + * ===================== + * + * Overview: + * Queues a packet for transmission and try to transmit it. + * + * Returns: + * Condition code + * + * Arguments: + * skb - pointer to sk_buff to queue for transmission + * dev - pointer to device information + * + * Functional Description: + * Here we assume that an incoming skb transmit request + * is contained in a single physically contiguous buffer + * in which the virtual address of the start of packet + * (skb->data) can be converted to a physical address + * by using virt_to_bus(). + * + * We have an internal queue for packets we can not send + * immediately. Packets in this queue can be given to the + * adapter if transmit buffers are freed. + * + * We can't free the skb until after it's been DMA'd + * out by the adapter, so we'll keep it in the driver and + * return it in mac_drv_tx_complete. + * + * Return Codes: + * 0 - driver has queued and/or sent packet + * 1 - caller should requeue the sk_buff for later transmission + * + * Assumptions: + * The entire packet is stored in one physically + * contiguous buffer which is not cached and whose + * 32-bit physical address can be determined. + * + * It's vital that this routine is NOT reentered for the + * same board and that the OS is not in another section of + * code (eg. skfp_interrupt) for the same board on a + * different thread. + * + * Side Effects: + * None + */ +static int skfp_send_pkt(struct sk_buff *skb, struct device *dev) +{ + skfddi_priv *bp = PRIV(dev); + unsigned long Flags; + + PRINTK(KERN_INFO "skfp_send_pkt\n"); + if (dev->interrupt) + return 1; + + if (skb == NULL) { + mark_bh(NET_BH); + return 0; + } + if (dev->tbusy) { + // printk(KERN_INFO "%s transmit timeout\n", dev->name); + spin_lock_irqsave(&bp->DriverLock, Flags); + ResetAdapter((struct s_smc *) dev->priv); + spin_unlock_irqrestore(&bp->DriverLock, Flags); + return 1; + } + /* + * Verify that incoming transmit request is OK + * + * Note: The packet size check is consistent with other + * Linux device drivers, although the correct packet + * size should be verified before calling the + * transmit routine. + */ + + if (!(skb->len >= FDDI_K_LLC_ZLEN && skb->len <= FDDI_K_LLC_LEN)) { + bp->MacStat.tx_errors++; /* bump error counter */ + // dequeue packets from xmt queue and send them + mark_bh(NET_BH); + dev_kfree_skb(skb); + return (0); /* return "success" */ + } + if (test_and_set_bit(0, (void *) &dev->tbusy) != 0) { + printk(KERN_INFO "%s: ERROR: Transmitter access conflict.\n", + dev->name); + return 1; + } + if (bp->QueueSkb == 0) { // return with tbusy set: queue full + + // printk(KERN_INFO "%s: queue full\n", dev->name); + return 1; + } + bp->QueueSkb--; + skb_queue_tail(&bp->SendSkbQueue, skb); + send_queued_packets((struct s_smc *) dev->priv); + if (bp->QueueSkb > 0) { + dev->tbusy = 0; + } + dev->trans_start = jiffies; + return 0; + +} // skfp_send_pkt + + +/* + * ======================= + * = send_queued_packets = + * ======================= + * + * Overview: + * Send packets from the driver queue as long as there are some and + * transmit resources are available. + * + * Returns: + * None + * + * Arguments: + * smc - pointer to smc (adapter) structure + * + * Functional Description: + * Take a packet from queue if there is any. If not, then we are done. + * Check if there are resources to send the packet. If not, requeue it + * and exit. + * Set packet descriptor flags and give packet to adapter. + * Check if any send resources can be freed (we do not use the + * transmit complete interrupt). + */ +static void send_queued_packets(struct s_smc *smc) +{ + skfddi_priv *bp = (skfddi_priv *) & smc->os; + struct sk_buff *skb; + unsigned char fc; + int queue; + struct s_smt_fp_txd *txd; // Current TxD. + static int log_counter=0; // to avoid to much logging + unsigned long Flags; + + int frame_status; // HWM tx frame status. + + PRINTK(KERN_INFO "send queued packets\n"); + for (;;) { + // send first buffer from queue + skb = skb_dequeue(&bp->SendSkbQueue); + + if (!skb) { + PRINTK(KERN_INFO "queue empty\n"); + return; + } // queue empty ! + + spin_lock_irqsave(&bp->DriverLock, Flags); + fc = skb->data[0]; + queue = (fc & FC_SYNC_BIT) ? QUEUE_S : QUEUE_A0; +#ifdef ESS + // Check if the frame may/must be sent as a synchronous frame. + + if ((fc & ~(FC_SYNC_BIT | FC_LLC_PRIOR)) == FC_ASYNC_LLC) { + // It's an LLC frame. + if (!smc->ess.sync_bw_available) + fc &= ~FC_SYNC_BIT; // No bandwidth available. + + else { // Bandwidth is available. + + if (smc->mib.fddiESSSynchTxMode) { + // Send as sync. frame. + fc |= FC_SYNC_BIT; + } + } + } +#endif // ESS + frame_status = hwm_tx_init(smc, fc, 1, skb->len, queue); + + if ((frame_status & (LOC_TX | LAN_TX)) == 0) { + // Unable to send the frame. + + if ((frame_status & RING_DOWN) != 0) { + // Ring is down. + PRINTK("Tx attempt while ring down.\n"); + } else if ((frame_status & OUT_OF_TXD) != 0) { + if(log_counter == 0) { + // printk(KERN_INFO "%s: out of TXDs.\n", + // bp->dev->name); + } + log_counter++; + if (log_counter >= 100000) + log_counter = 0; + } else { + if(log_counter == 0) { + // printk(KERN_INFO "%s: out of transmit" + // "resources\n", + // bp->dev->name); + } + log_counter++; + if (log_counter >= 100000) + log_counter = 0; + } + + // Note: We will retry the operation as soon as + // transmit resources become available. + skb_queue_head(&bp->SendSkbQueue, skb); + spin_unlock_irqrestore(&bp->DriverLock, Flags); + return; // Packet has been queued. + + } // if (unable to send frame) + + bp->QueueSkb++; // one packet less in local queue + + // source address in packet ? + CheckSourceAddress(skb->data, smc->hw.fddi_canon_addr.a); + + txd = (struct s_smt_fp_txd *) HWM_GET_CURR_TXD(smc, queue); + + if (frame_status & LAN_TX) { + txd->txd_os.skb = skb; // save skb + + } + hwm_tx_frag(smc, skb->data, virt_to_bus(skb->data), skb->len, + frame_status | FIRST_FRAG | LAST_FRAG | EN_IRQ_EOF); + + if (!(frame_status & LAN_TX)) { // local only frame + dev_kfree_skb(skb); + } + spin_unlock_irqrestore(&bp->DriverLock, Flags); + } // for + + return; // never reached + +} // send_queued_packets + + +/************************ + * + * CheckSourceAddress + * + * Verify if the source address is set. Insert it if necessary. + * + ************************/ +void CheckSourceAddress(unsigned char *frame, unsigned char *hw_addr) +{ + unsigned char SRBit; + + if ((((unsigned long) frame[1 + 6]) & ~0x01) != 0) // source routing bit + + return; + if ((unsigned short) frame[1 + 10] != 0) + return; + SRBit = frame[1 + 6] & 0x01; + memcpy(&frame[1 + 6], hw_addr, 6); + frame[8] |= SRBit; +} // CheckSourceAddress + + +/************************ + * + * ResetAdapter + * + * Reset the adapter and bring it back to operational mode. + * Args + * smc - A pointer to the SMT context struct. + * Out + * Nothing. + * + ************************/ +static void ResetAdapter(struct s_smc *smc) +{ + + PRINTK(KERN_INFO "[fddi: ResetAdapter]\n"); + + // Stop the adapter. + + card_stop(smc); // Stop all activity. + + // Clear the transmit and receive descriptor queues. + mac_drv_clear_tx_queue(smc); + mac_drv_clear_rx_queue(smc); + + // Restart the adapter. + + smt_reset_defaults(smc, 1); // Initialize the SMT module. + + init_smt(smc, (smc->os.dev)->dev_addr); // Initialize the hardware. + + smt_online(smc, 1); // Insert into the ring again. + STI_FBI(); + + // Restore original receive mode (multicasts, promiscuous, etc.). + skfp_ctl_set_multicast_list_wo_lock(smc->os.dev); +} // ResetAdapter + + +//--------------- functions called by hardware module ---------------- + +/************************ + * + * llc_restart_tx + * + * The hardware driver calls this routine when the transmit complete + * interrupt bits (end of frame) for the synchronous or asynchronous + * queue is set. + * + * NOTE The hardware driver calls this function also if no packets are queued. + * The routine must be able to handle this case. + * Args + * smc - A pointer to the SMT context struct. + * Out + * Nothing. + * + ************************/ +void llc_restart_tx(struct s_smc *smc) +{ + skfddi_priv *bp = (skfddi_priv *) & smc->os; + + PRINTK(KERN_INFO "[llc_restart_tx]\n"); + + // Try to send queued packets + spin_unlock(&bp->DriverLock); + send_queued_packets(smc); + spin_lock(&bp->DriverLock); + bp->dev->tbusy = 0; // system may send again if it was blocked + +} // llc_restart_tx + + +/************************ + * + * mac_drv_get_space + * + * The hardware module calls this function to allocate the memory + * for the SMT MBufs if the define MB_OUTSIDE_SMC is specified. + * Args + * smc - A pointer to the SMT context struct. + * + * size - Size of memory in bytes to allocate. + * Out + * != 0 A pointer to the virtual address of the allocated memory. + * == 0 Allocation error. + * + ************************/ +void *mac_drv_get_space(struct s_smc *smc, unsigned int size) +{ + void *virt; + + PRINTK(KERN_INFO "mac_drv_get_space\n"); + virt = (void *) (smc->os.SharedMemAddr + smc->os.SharedMemHeap); + + if ((smc->os.SharedMemHeap + size) > smc->os.SharedMemSize) { + printk("Unexpected SMT memory size requested: %d\n", size); + return (NULL); + } + smc->os.SharedMemHeap += size; // Move heap pointer. + + PRINTK(KERN_INFO "mac_drv_get_space end\n"); + PRINTK(KERN_INFO "virt addr: %lx\n", (ulong) virt); + PRINTK(KERN_INFO "bus addr: %lx\n", (ulong) virt_to_bus(virt)); + return (virt); +} // mac_drv_get_space + + +/************************ + * + * mac_drv_get_desc_mem + * + * This function is called by the hardware dependent module. + * It allocates the memory for the RxD and TxD descriptors. + * + * This memory must be non-cached, non-movable and non-swapable. + * This memory should start at a physical page boundary. + * Args + * smc - A pointer to the SMT context struct. + * + * size - Size of memory in bytes to allocate. + * Out + * != 0 A pointer to the virtual address of the allocated memory. + * == 0 Allocation error. + * + ************************/ +void *mac_drv_get_desc_mem(struct s_smc *smc, unsigned int size) +{ + + char *virt; + + PRINTK(KERN_INFO "mac_drv_get_desc_mem\n"); + + // Descriptor memory must be aligned on 16-byte boundary. + + virt = mac_drv_get_space(smc, size); + + size = (u_int) ((0 - (unsigned long) virt) & 15); + + PRINTK("Allocate %u bytes alignment gap ", size); + PRINTK("for descriptor memory.\n"); + + if (!mac_drv_get_space(smc, size)) { + printk("fddi: Unable to align descriptor memory.\n"); + return (NULL); + } + return (virt + size); +} // mac_drv_get_desc_mem + + +/************************ + * + * mac_drv_virt2phys + * + * Get the physical address of a given virtual address. + * Args + * smc - A pointer to the SMT context struct. + * + * virt - A (virtual) pointer into our 'shared' memory area. + * Out + * Physical address of the given virtual address. + * + ************************/ +unsigned long mac_drv_virt2phys(struct s_smc *smc, void *virt) +{ + return virt_to_bus(virt); +} // mac_drv_virt2phys + + +/************************ + * + * dma_master + * + * The HWM calls this function, when the driver leads through a DMA + * transfer. If the OS-specific module must prepare the system hardware + * for the DMA transfer, it should do it in this function. + * + * The hardware module calls this dma_master if it wants to send an SMT + * frame. This means that the virt address passed in here is part of + * the 'shared' memory area. + * Args + * smc - A pointer to the SMT context struct. + * + * virt - The virtual address of the data. + * + * len - The length in bytes of the data. + * + * flag - Indicates the transmit direction and the buffer type: + * DMA_RD (0x01) system RAM ==> adapter buffer memory + * DMA_WR (0x02) adapter buffer memory ==> system RAM + * SMT_BUF (0x80) SMT buffer + * + * >> NOTE: SMT_BUF and DMA_RD are always set for PCI. << + * Out + * Returns the pyhsical address for the DMA transfer. + * + ************************/ +u_long dma_master(struct s_smc * smc, void *virt, int len, int flag) +{ + return (virt_to_bus(virt)); +} // dma_master + + +/************************ + * + * dma_complete + * + * The hardware module calls this routine when it has completed a DMA + * transfer. If the operating system dependant module has set up the DMA + * channel via dma_master() (e.g. Windows NT or AIX) it should clean up + * the DMA channel. + * Args + * smc - A pointer to the SMT context struct. + * + * descr - A pointer to a TxD or RxD, respectively. + * + * flag - Indicates the DMA transfer direction / SMT buffer: + * DMA_RD (0x01) system RAM ==> adapter buffer memory + * DMA_WR (0x02) adapter buffer memory ==> system RAM + * SMT_BUF (0x80) SMT buffer (managed by HWM) + * Out + * Nothing. + * + ************************/ +void dma_complete(struct s_smc *smc, volatile union s_fp_descr *descr, int flag) +{ + return; +} // dma_complete + + +/************************ + * + * mac_drv_tx_complete + * + * Transmit of a packet is complete. Release the tx staging buffer. + * + * Args + * smc - A pointer to the SMT context struct. + * + * txd - A pointer to the last TxD which is used by the frame. + * Out + * Returns nothing. + * + ************************/ +void mac_drv_tx_complete(struct s_smc *smc, volatile struct s_smt_fp_txd *txd) +{ + struct sk_buff *skb; + + PRINTK(KERN_INFO "entering mac_drv_tx_complete\n"); + // Check if this TxD points to a skb + + if (!(skb = txd->txd_os.skb)) { + PRINTK("TXD with no skb assigned.\n"); + return; + } + txd->txd_os.skb = NULL; + + smc->os.MacStat.tx_packets++; // Count transmitted packets. + smc->os.MacStat.tx_bytes+=skb->len; // Count bytes + + // free the skb + dev_kfree_skb(skb); + + PRINTK(KERN_INFO "leaving mac_drv_tx_complete\n"); +} // mac_drv_tx_complete + + +/************************ + * + * dump packets to logfile + * + ************************/ +#ifdef DUMPPACKETS +void dump_data(unsigned char *Data, int length) +{ + int i, j; + unsigned char s[255], sh[10]; + if (length > 64) { + length = 64; + } + printk(KERN_INFO "---Packet start---\n"); + for (i = 0, j = 0; i < length / 8; i++, j += 8) + printk(KERN_INFO "%02x %02x %02x %02x %02x %02x %02x %02x\n", + Data[j + 0], Data[j + 1], Data[j + 2], Data[j + 3], + Data[j + 4], Data[j + 5], Data[j + 6], Data[j + 7]); + strcpy(s, ""); + for (i = 0; i < length % 8; i++) { + sprintf(sh, "%02x ", Data[j + i]); + strcat(s, sh); + } + printk(KERN_INFO "%s\n", s); + printk(KERN_INFO "------------------\n"); +} // dump_data +#else +#define dump_data(data,len) +#endif // DUMPPACKETS + +/************************ + * + * mac_drv_rx_complete + * + * The hardware module calls this function if an LLC frame is received + * in a receive buffer. Also the SMT, NSA, and directed beacon frames + * from the network will be passed to the LLC layer by this function + * if passing is enabled. + * + * mac_drv_rx_complete forwards the frame to the LLC layer if it should + * be received. It also fills the RxD ring with new receive buffers if + * some can be queued. + * Args + * smc - A pointer to the SMT context struct. + * + * rxd - A pointer to the first RxD which is used by the receive frame. + * + * frag_count - Count of RxDs used by the received frame. + * + * len - Frame length. + * Out + * Nothing. + * + ************************/ +void mac_drv_rx_complete(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count, int len) +{ + skfddi_priv *bp = (skfddi_priv *) & smc->os; + struct sk_buff *skb; + unsigned char *virt, *cp; + unsigned short ri; + u_int RifLength; + + PRINTK(KERN_INFO "entering mac_drv_rx_complete (len=%d)\n", len); + if (frag_count != 1) { // This is not allowed to happen. + + printk("fddi: ERROR: Multi-fragment receive!\n"); + goto RequeueRxd; // Re-use the given RXD(s). + + } + skb = rxd->rxd_os.skb; + if (!skb) { + PRINTK(KERN_INFO "No skb in rxd\n"); + smc->os.MacStat.rx_errors++; + goto RequeueRxd; + } + virt = skb->data; + + dump_data(skb->data, len); + + /* + * FDDI Frame format: + * +-------+-------+-------+------------+--------+------------+ + * | FC[1] | DA[6] | SA[6] | RIF[0..18] | LLC[3] | Data[0..n] | + * +-------+-------+-------+------------+--------+------------+ + * + * FC = Frame Control + * DA = Destination Address + * SA = Source Address + * RIF = Routing Information Field + * LLC = Logical Link Control + */ + + // Remove Routing Information Field (RIF), if present. + + if ((virt[1 + 6] & FDDI_RII) == 0) + RifLength = 0; + else { + int n; +// goos: RIF removal has still to be tested + PRINTK(KERN_INFO "RIF found\n"); + // Get RIF length from Routing Control (RC) field. + cp = virt + FDDI_MAC_HDR_LEN; // Point behind MAC header. + + ri = ntohs(*((unsigned short *) cp)); + RifLength = ri & FDDI_RCF_LEN_MASK; + if (len < (int) (FDDI_MAC_HDR_LEN + RifLength)) { + printk("fddi: Invalid RIF.\n"); + goto RequeueRxd; // Discard the frame. + + } + virt[1 + 6] &= ~FDDI_RII; // Clear RII bit. + // regions overlap + + virt = cp + RifLength; + for (n = FDDI_MAC_HDR_LEN; n; n--) + *--virt = *--cp; + // adjust sbd->data pointer + skb_pull(skb, RifLength); + len -= RifLength; + RifLength = 0; + } + + // Count statistics. + smc->os.MacStat.rx_packets++; // Count indicated receive packets. + smc->os.MacStat.rx_bytes+=len; // Count bytes + + // virt points to header again + if (virt[1] & 0x01) { // Check group (multicast) bit. + + smc->os.MacStat.multicast++; + } + + // deliver frame to system + rxd->rxd_os.skb = NULL; + skb_trim(skb, len); + skb->protocol = fddi_type_trans(skb, bp->dev); + skb->dev = bp->dev; /* pass up device pointer */ + + netif_rx(skb); + + HWM_RX_CHECK(smc, RX_LOW_WATERMARK); + return; + + RequeueRxd: + PRINTK(KERN_INFO "Rx: re-queue RXD.\n"); + mac_drv_requeue_rxd(smc, rxd, frag_count); + smc->os.MacStat.rx_errors++; // Count receive packets not indicated. + +} // mac_drv_rx_complete + + +/************************ + * + * mac_drv_requeue_rxd + * + * The hardware module calls this function to request the OS-specific + * module to queue the receive buffer(s) represented by the pointer + * to the RxD and the frag_count into the receive queue again. This + * buffer was filled with an invalid frame or an SMT frame. + * Args + * smc - A pointer to the SMT context struct. + * + * rxd - A pointer to the first RxD which is used by the receive frame. + * + * frag_count - Count of RxDs used by the received frame. + * Out + * Nothing. + * + ************************/ +void mac_drv_requeue_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count) +{ + volatile struct s_smt_fp_rxd *next_rxd; + volatile struct s_smt_fp_rxd *src_rxd; + struct sk_buff *skb; + int MaxFrameSize; + unsigned char *v_addr; + unsigned long b_addr; + + if (frag_count != 1) // This is not allowed to happen. + + printk("fddi: Multi-fragment requeue!\n"); + + MaxFrameSize = ((skfddi_priv *) & smc->os)->MaxFrameSize; + src_rxd = rxd; + for (; frag_count > 0; frag_count--) { + next_rxd = src_rxd->rxd_next; + rxd = HWM_GET_CURR_RXD(smc); + + skb = src_rxd->rxd_os.skb; + if (skb == NULL) { // this should not happen + + PRINTK("Requeue with no skb in rxd!\n"); + skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC); + if (skb) { + // we got a skb + rxd->rxd_os.skb = skb; + skb_reserve(skb, 3); + skb_put(skb, MaxFrameSize); + v_addr = skb->data; + b_addr = virt_to_bus(v_addr); + } else { + // no skb available, use local buffer + PRINTK("Queueing invalid buffer!\n"); + rxd->rxd_os.skb = NULL; + v_addr = smc->os.LocalRxBuffer; + b_addr = virt_to_bus(v_addr); + } + } else { + // we use skb from old rxd + rxd->rxd_os.skb = skb; + v_addr = skb->data; + b_addr = virt_to_bus(v_addr); + } + hwm_rx_frag(smc, v_addr, b_addr, MaxFrameSize, + FIRST_FRAG | LAST_FRAG); + + src_rxd = next_rxd; + } +} // mac_drv_requeue_rxd + + +/************************ + * + * mac_drv_fill_rxd + * + * The hardware module calls this function at initialization time + * to fill the RxD ring with receive buffers. It is also called by + * mac_drv_rx_complete if rx_free is large enough to queue some new + * receive buffers into the RxD ring. mac_drv_fill_rxd queues new + * receive buffers as long as enough RxDs and receive buffers are + * available. + * Args + * smc - A pointer to the SMT context struct. + * Out + * Nothing. + * + ************************/ +void mac_drv_fill_rxd(struct s_smc *smc) +{ + int MaxFrameSize; + unsigned char *v_addr; + unsigned long b_addr; + struct sk_buff *skb; + volatile struct s_smt_fp_rxd *rxd; + + PRINTK(KERN_INFO "entering mac_drv_fill_rxd\n"); + + // Walk through the list of free receive buffers, passing receive + // buffers to the HWM as long as RXDs are available. + + MaxFrameSize = ((skfddi_priv *) & smc->os)->MaxFrameSize; + // Check if there is any RXD left. + while (HWM_GET_RX_FREE(smc) > 0) { + PRINTK(KERN_INFO ".\n"); + + rxd = HWM_GET_CURR_RXD(smc); + skb = alloc_skb(MaxFrameSize + 3, GFP_ATOMIC); + if (skb) { + // we got a skb + skb_reserve(skb, 3); + skb_put(skb, MaxFrameSize); + v_addr = skb->data; + b_addr = virt_to_bus(v_addr); + } else { + // no skb available, use local buffer + // System has run out of buffer memory, but we want to + // keep the receiver running in hope of better times. + // Multiple descriptors may point to this local buffer, + // so data in it must be considered invalid. + PRINTK("Queueing invalid buffer!\n"); + v_addr = smc->os.LocalRxBuffer; + b_addr = virt_to_bus(v_addr); + } + + rxd->rxd_os.skb = skb; + + // Pass receive buffer to HWM. + hwm_rx_frag(smc, v_addr, b_addr, MaxFrameSize, + FIRST_FRAG | LAST_FRAG); + } + PRINTK(KERN_INFO "leaving mac_drv_fill_rxd\n"); +} // mac_drv_fill_rxd + + +/************************ + * + * mac_drv_clear_rxd + * + * The hardware module calls this function to release unused + * receive buffers. + * Args + * smc - A pointer to the SMT context struct. + * + * rxd - A pointer to the first RxD which is used by the receive buffer. + * + * frag_count - Count of RxDs used by the receive buffer. + * Out + * Nothing. + * + ************************/ +void mac_drv_clear_rxd(struct s_smc *smc, volatile struct s_smt_fp_rxd *rxd, + int frag_count) +{ + + struct sk_buff *skb; + + PRINTK("entering mac_drv_clear_rxd\n"); + + if (frag_count != 1) // This is not allowed to happen. + + printk("fddi: Multi-fragment clear!\n"); + + for (; frag_count > 0; frag_count--) { + skb = rxd->rxd_os.skb; + if (skb != NULL) { + dev_kfree_skb(skb); + rxd->rxd_os.skb = NULL; + } + rxd = rxd->rxd_next; // Next RXD. + + } +} // mac_drv_clear_rxd + + +/************************ + * + * mac_drv_rx_init + * + * The hardware module calls this routine when an SMT or NSA frame of the + * local SMT should be delivered to the LLC layer. + * + * It is necessary to have this function, because there is no other way to + * copy the contents of SMT MBufs into receive buffers. + * + * mac_drv_rx_init allocates the required target memory for this frame, + * and receives the frame fragment by fragment by calling mac_drv_rx_frag. + * Args + * smc - A pointer to the SMT context struct. + * + * len - The length (in bytes) of the received frame (FC, DA, SA, Data). + * + * fc - The Frame Control field of the received frame. + * + * look_ahead - A pointer to the lookahead data buffer (may be NULL). + * + * la_len - The length of the lookahead data stored in the lookahead + * buffer (may be zero). + * Out + * Always returns zero (0). + * + ************************/ +int mac_drv_rx_init(struct s_smc *smc, int len, int fc, + char *look_ahead, int la_len) +{ + struct sk_buff *skb; + + PRINTK("entering mac_drv_rx_init(len=%d)\n", len); + + // "Received" a SMT or NSA frame of the local SMT. + + if (len != la_len || len < FDDI_MAC_HDR_LEN || !look_ahead) { + PRINTK("fddi: Discard invalid local SMT frame\n"); + PRINTK(" len=%d, la_len=%d, (ULONG) look_ahead=%08lXh.\n", + len, la_len, (unsigned long) look_ahead); + return (0); + } + skb = alloc_skb(len + 3, GFP_ATOMIC); + if (!skb) { + PRINTK("fddi: Local SMT: skb memory exhausted.\n"); + return (0); + } + skb_reserve(skb, 3); + skb_put(skb, len); + memcpy(skb->data, look_ahead, len); + + // deliver frame to system + skb->protocol = fddi_type_trans(skb, ((skfddi_priv *) & smc->os)->dev); + netif_rx(skb); + + return (0); +} // mac_drv_rx_init + + +/************************ + * + * smt_timer_poll + * + * This routine is called periodically by the SMT module to clean up the + * driver. + * + * Return any queued frames back to the upper protocol layers if the ring + * is down. + * Args + * smc - A pointer to the SMT context struct. + * Out + * Nothing. + * + ************************/ +void smt_timer_poll(struct s_smc *smc) +{ +} // smt_timer_poll + + +/************************ + * + * ring_status_indication + * + * This function indicates a change of the ring state. + * Args + * smc - A pointer to the SMT context struct. + * + * status - The current ring status. + * Out + * Nothing. + * + ************************/ +void ring_status_indication(struct s_smc *smc, u_long status) +{ + PRINTK("ring_status_indication( "); + if (status & RS_RES15) + PRINTK("RS_RES15 "); + if (status & RS_HARDERROR) + PRINTK("RS_HARDERROR "); + if (status & RS_SOFTERROR) + PRINTK("RS_SOFTERROR "); + if (status & RS_BEACON) + PRINTK("RS_BEACON "); + if (status & RS_PATHTEST) + PRINTK("RS_PATHTEST "); + if (status & RS_SELFTEST) + PRINTK("RS_SELFTEST "); + if (status & RS_RES9) + PRINTK("RS_RES9 "); + if (status & RS_DISCONNECT) + PRINTK("RS_DISCONNECT "); + if (status & RS_RES7) + PRINTK("RS_RES7 "); + if (status & RS_DUPADDR) + PRINTK("RS_DUPADDR "); + if (status & RS_NORINGOP) + PRINTK("RS_NORINGOP "); + if (status & RS_VERSION) + PRINTK("RS_VERSION "); + if (status & RS_STUCKBYPASSS) + PRINTK("RS_STUCKBYPASSS "); + if (status & RS_EVENT) + PRINTK("RS_EVENT "); + if (status & RS_RINGOPCHANGE) + PRINTK("RS_RINGOPCHANGE "); + if (status & RS_RES0) + PRINTK("RS_RES0 "); + PRINTK("]\n"); +} // ring_status_indication + + +/************************ + * + * smt_get_time + * + * Gets the current time from the system. + * Args + * None. + * Out + * The current time in TICKS_PER_SECOND. + * + * TICKS_PER_SECOND has the unit 'count of timer ticks per second'. It is + * defined in "targetos.h". The definition of TICKS_PER_SECOND must comply + * to the time returned by smt_get_time(). + * + ************************/ +unsigned long smt_get_time(void) +{ + return jiffies; +} // smt_get_time + + +/************************ + * + * smt_stat_counter + * + * Status counter update (ring_op, fifo full). + * Args + * smc - A pointer to the SMT context struct. + * + * stat - = 0: A ring operational change occurred. + * = 1: The FORMAC FIFO buffer is full / FIFO overflow. + * Out + * Nothing. + * + ************************/ +void smt_stat_counter(struct s_smc *smc, int stat) +{ +// BOOLEAN RingIsUp ; + + PRINTK(KERN_INFO "smt_stat_counter\n"); + switch (stat) { + case 0: + PRINTK(KERN_INFO "Ring operational change.\n"); + break; + case 1: + PRINTK(KERN_INFO "Receive fifo overflow.\n"); + smc->os.MacStat.rx_errors++; + break; + default: + PRINTK(KERN_INFO "Unknown status (%d).\n", stat); + break; + } +} // smt_stat_counter + + +/************************ + * + * cfm_state_change + * + * Sets CFM state in custom statistics. + * Args + * smc - A pointer to the SMT context struct. + * + * c_state - Possible values are: + * + * EC0_OUT, EC1_IN, EC2_TRACE, EC3_LEAVE, EC4_PATH_TEST, + * EC5_INSERT, EC6_CHECK, EC7_DEINSERT + * Out + * Nothing. + * + ************************/ +void cfm_state_change(struct s_smc *smc, int c_state) +{ +#ifdef DRIVERDEBUG + char *s; + + switch (c_state) { + case SC0_ISOLATED: + s = "SC0_ISOLATED"; + break; + case SC1_WRAP_A: + s = "SC1_WRAP_A"; + break; + case SC2_WRAP_B: + s = "SC2_WRAP_B"; + break; + case SC4_THRU_A: + s = "SC4_THRU_A"; + break; + case SC5_THRU_B: + s = "SC5_THRU_B"; + break; + case SC7_WRAP_S: + s = "SC7_WRAP_S"; + break; + case SC9_C_WRAP_A: + s = "SC9_C_WRAP_A"; + break; + case SC10_C_WRAP_B: + s = "SC10_C_WRAP_B"; + break; + case SC11_C_WRAP_S: + s = "SC11_C_WRAP_S"; + break; + default: + PRINTK(KERN_INFO "cfm_state_change: unknown %d\n", c_state); + return; + } + PRINTK(KERN_INFO "cfm_state_change: %s\n", s); +#endif // DRIVERDEBUG +} // cfm_state_change + + +/************************ + * + * ecm_state_change + * + * Sets ECM state in custom statistics. + * Args + * smc - A pointer to the SMT context struct. + * + * e_state - Possible values are: + * + * SC0_ISOLATED, SC1_WRAP_A (5), SC2_WRAP_B (6), SC4_THRU_A (12), + * SC5_THRU_B (7), SC7_WRAP_S (8) + * Out + * Nothing. + * + ************************/ +void ecm_state_change(struct s_smc *smc, int e_state) +{ +#ifdef DRIVERDEBUG + char *s; + + switch (e_state) { + case EC0_OUT: + s = "EC0_OUT"; + break; + case EC1_IN: + s = "EC1_IN"; + break; + case EC2_TRACE: + s = "EC2_TRACE"; + break; + case EC3_LEAVE: + s = "EC3_LEAVE"; + break; + case EC4_PATH_TEST: + s = "EC4_PATH_TEST"; + break; + case EC5_INSERT: + s = "EC5_INSERT"; + break; + case EC6_CHECK: + s = "EC6_CHECK"; + break; + case EC7_DEINSERT: + s = "EC7_DEINSERT"; + break; + default: + s = "unknown"; + break; + } + PRINTK(KERN_INFO "ecm_state_change: %s\n", s); +#endif //DRIVERDEBUG +} // ecm_state_change + + +/************************ + * + * rmt_state_change + * + * Sets RMT state in custom statistics. + * Args + * smc - A pointer to the SMT context struct. + * + * r_state - Possible values are: + * + * RM0_ISOLATED, RM1_NON_OP, RM2_RING_OP, RM3_DETECT, + * RM4_NON_OP_DUP, RM5_RING_OP_DUP, RM6_DIRECTED, RM7_TRACE + * Out + * Nothing. + * + ************************/ +void rmt_state_change(struct s_smc *smc, int r_state) +{ +#ifdef DRIVERDEBUG + char *s; + + switch (r_state) { + case RM0_ISOLATED: + s = "RM0_ISOLATED"; + break; + case RM1_NON_OP: + s = "RM1_NON_OP - not operational"; + break; + case RM2_RING_OP: + s = "RM2_RING_OP - ring operational"; + break; + case RM3_DETECT: + s = "RM3_DETECT - detect dupl addresses"; + break; + case RM4_NON_OP_DUP: + s = "RM4_NON_OP_DUP - dupl. addr detected"; + break; + case RM5_RING_OP_DUP: + s = "RM5_RING_OP_DUP - ring oper. with dupl. addr"; + break; + case RM6_DIRECTED: + s = "RM6_DIRECTED - sending directed beacons"; + break; + case RM7_TRACE: + s = "RM7_TRACE - trace initiated"; + break; + default: + s = "unknown"; + break; + } + PRINTK(KERN_INFO "[rmt_state_change: %s]\n", s); +#endif // DRIVERDEBUG +} // rmt_state_change + + +/************************ + * + * drv_reset_indication + * + * This function is called by the SMT when it has detected a severe + * hardware problem. The driver should perform a reset on the adapter + * as soon as possible, but not from within this function. + * Args + * smc - A pointer to the SMT context struct. + * Out + * Nothing. + * + ************************/ +void drv_reset_indication(struct s_smc *smc) +{ + PRINTK(KERN_INFO "entering drv_reset_indication\n"); + + smc->os.ResetRequested = TRUE; // Set flag. + +} // drv_reset_indication + + + +//--------------- functions for use as a module ---------------- + +#ifdef MODULE +/************************ + * + * Note now that module autoprobing is allowed under PCI. The + * IRQ lines will not be auto-detected; instead I'll rely on the BIOSes + * to "do the right thing". + * + ************************/ +#define LP(a) ((struct s_smc*)(a)) +static struct device *mdev = NULL; + +/************************ + * + * init_module + * + * If compiled as a module, find + * adapters and initialize them. + * + ************************/ +int init_module(void) +{ + struct device *p; + + PRINTK(KERN_INFO "FDDI init module\n"); + if ((mdev = insert_device(NULL, skfp_probe)) == NULL) + return -ENOMEM; + + for (p = mdev; p != NULL; p = LP(p->priv)->os.next_module) { + PRINTK(KERN_INFO "device to register: %s\n", p->name); + if (register_netdev(p) != 0) { + printk("skfddi init_module failed\n"); + return -EIO; + } + } + + PRINTK(KERN_INFO "+++++ exit with success +++++\n"); + return 0; +} // init_module + +/************************ + * + * cleanup_module + * + * Release all resources claimed by this module. + * + ************************/ +void cleanup_module(void) +{ + PRINTK(KERN_INFO "cleanup_module\n"); + while (mdev != NULL) { + mdev = unlink_modules(mdev); + } + return; +} // cleanup_module + + +/************************ + * + * unlink_modules + * + * Unregister devices and release their memory. + * + ************************/ +static struct device *unlink_modules(struct device *p) +{ + struct device *next = NULL; + + if (p->priv) { /* Private areas allocated? */ + struct s_smc *lp = (struct s_smc *) p->priv; + + next = lp->os.next_module; + + if (lp->os.SharedMemAddr) { + kfree(lp->os.SharedMemAddr); + } + release_region(p->base_addr, + (lp->os.bus_type == SK_BUS_TYPE_PCI ? FP_IO_LEN : 0)); + } + unregister_netdev(p); + printk("%s: unloaded\n", p->name); + kfree(p); /* Free the device structure */ + + return next; +} // unlink_modules + + +#endif /* MODULE */ diff --git a/drivers/net/skfp/smt.c b/drivers/net/skfp/smt.c new file mode 100644 index 000000000000..b02ab9a2d020 --- /dev/null +++ b/drivers/net/skfp/smt.c @@ -0,0 +1,2225 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smt_p.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)smt.c 2.43 98/11/23 (C) SK " ; +#endif + +extern const u_char canonical[256] ; + +/* + * FC in SMbuf + */ +#define m_fc(mb) ((mb)->sm_data[0]) + +#define SMT_TID_MAGIC 0x1f0a7b3c + +#ifdef DEBUG +static const char *const smt_type_name[] = { + "SMT_00??", "SMT_INFO", "SMT_02??", "SMT_03??", + "SMT_04??", "SMT_05??", "SMT_06??", "SMT_07??", + "SMT_08??", "SMT_09??", "SMT_0A??", "SMT_0B??", + "SMT_0C??", "SMT_0D??", "SMT_0E??", "SMT_NSA" +} ; + +static const char *const smt_class_name[] = { + "UNKNOWN","NIF","SIF_CONFIG","SIF_OPER","ECF","RAF","RDF", + "SRF","PMF_GET","PMF_SET","ESF" +} ; +#endif +#define LAST_CLASS (SMT_PMF_SET) + +static const struct fddi_addr SMT_Unknown = { + { 0,0,0x1f,0,0,0 } +} ; + +/* + * external variables + */ +extern const struct fddi_addr fddi_broadcast ; + +/* + * external functions + */ +int pcm_status_twisted() ; +void pcm_status_state() ; +int pcm_status_type() ; + +extern SMbuf *smt_get_mbuf() ; + +#define EXPORT_PMF +/* + * function prototypes + */ +u_long smt_get_tid() ; +EXPORT_PMF SMbuf *smt_build_frame() ; +EXPORT_PMF void *sm_to_para() ; +#ifdef LITTLE_ENDIAN +static int smt_swap_short() ; +#endif +static int mac_index() ; +static int phy_index() ; +static int mac_con_resource_index() ; +static int phy_con_resource_index() ; +EXPORT_PMF void smt_send_frame() ; +EXPORT_PMF void smt_set_timestamp() ; +static void smt_send_rdf() ; +static void smt_send_nif() ; +static void smt_send_ecf() ; +static void smt_echo_test() ; +static void smt_send_sif_config() ; +static void smt_send_sif_operation() ; +EXPORT_PMF void smt_swap_para() ; +#ifdef LITTLE_ENDIAN +static void smt_string_swap() ; +#endif +static void smt_add_frame_len() ; +static void smt_fill_una() ; +static void smt_fill_sde() ; +static void smt_fill_state() ; +static void smt_fill_timestamp() ; +static void smt_fill_policy() ; +static void smt_fill_latency() ; +static void smt_fill_neighbor() ; +static int smt_fill_path() ; +static void smt_fill_mac_status() ; +static void smt_fill_lem() ; +static void smt_fill_version() ; +static void smt_fill_fsc() ; +static void smt_fill_mac_counter() ; +static void smt_fill_mac_fnc() ; +static void smt_fill_manufacturer() ; +static void smt_fill_user() ; +static void smt_fill_setcount() ; +static void smt_fill_echo() ; +int smt_check_para() ; + +void smt_clear_una_dna() ; +static void smt_clear_old_una_dna() ; +#ifdef CONCENTRATOR +static int entity_to_index() ; +#endif +static void update_dac() ; +static int div_ratio() ; +#ifdef USE_CAN_ADDR +void hwm_conv_can() ; +#else +#define hwm_conv_can(smc,data,len) +#endif + +/* + * list of mandatory paras in frames + */ +static const u_short plist_nif[] = { SMT_P_UNA,SMT_P_SDE,SMT_P_STATE,0 } ; + +/* + * init SMT agent + */ +void smt_agent_init(smc) +struct s_smc *smc ; +{ + int i ; + + /* + * get MAC address + */ + smc->mib.m[MAC0].fddiMACSMTAddress = smc->hw.fddi_home_addr ; + + /* + * get OUI address from driver (bia == built-in-address) + */ + smc->mib.fddiSMTStationId.sid_oem[0] = 0 ; + smc->mib.fddiSMTStationId.sid_oem[1] = 0 ; + driver_get_bia(smc,&smc->mib.fddiSMTStationId.sid_node) ; + for (i = 0 ; i < 6 ; i ++) { + smc->mib.fddiSMTStationId.sid_node.a[i] = + canonical[smc->mib.fddiSMTStationId.sid_node.a[i]] ; + } + smc->mib.fddiSMTManufacturerData[0] = + smc->mib.fddiSMTStationId.sid_node.a[0] ; + smc->mib.fddiSMTManufacturerData[1] = + smc->mib.fddiSMTStationId.sid_node.a[1] ; + smc->mib.fddiSMTManufacturerData[2] = + smc->mib.fddiSMTStationId.sid_node.a[2] ; + smc->sm.smt_tid = 0 ; + smc->mib.m[MAC0].fddiMACDupAddressTest = DA_NONE ; + smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ; +#ifndef SLIM_SMT + smt_clear_una_dna(smc) ; + smt_clear_old_una_dna(smc) ; +#endif + for (i = 0 ; i < SMT_MAX_TEST ; i++) + smc->sm.pend[i] = 0 ; + smc->sm.please_reconnect = 0 ; + smc->sm.uniq_ticks = 0 ; +} + +/* + * SMT task + * forever + * delay 30 seconds + * send NIF + * check tvu & tvd + * end + */ +void smt_agent_task(smc) +struct s_smc *smc ; +{ + smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L, + EV_TOKEN(EVENT_SMT,SM_TIMER)) ; + DB_SMT("SMT agent task\n",0,0) ; +} + +void smt_please_reconnect(smc,reconn_time) +struct s_smc *smc ; /* Pointer to SMT context */ +int reconn_time ; /* Wait for reconnect time in seconds */ +{ + /* + * The please reconnect variable is used as a timer. + * It is decremented each time smt_event is called. + * This happens every second or when smt_force_irq is called. + * Note: smt_force_irq () is called on some packet receives and + * when a multicast address is changed. Since nothing + * is received during the disconnect and the multicast + * address changes can be viewed as not very often and + * the timer runs out close to its given value + * (reconn_time). + */ + smc->sm.please_reconnect = reconn_time ; +} + +#ifndef SMT_REAL_TOKEN_CT +void smt_emulate_token_ct(smc, mac_index) +struct s_smc *smc; +int mac_index; +{ + u_long count; + u_long time; + + + time = smt_get_time(); + count = ((time - smc->sm.last_tok_time[mac_index]) * + 100)/TICKS_PER_SECOND; + + /* + * Only when ring is up we will have a token count. The + * flag is unfortunatly a single instance value. This + * doesn't matter now, because we currently have only + * one MAC instance. + */ + if (smc->hw.mac_ring_is_up){ + smc->mib.m[mac_index].fddiMACToken_Ct += count; + } + + /* Remember current time */ + smc->sm.last_tok_time[mac_index] = time; + +} +#endif + +/*ARGSUSED1*/ +void smt_event(smc,event) +struct s_smc *smc ; +int event ; +{ + u_long time ; +#ifndef SMT_REAL_TOKEN_CT + int i ; +#endif + + + if (smc->sm.please_reconnect) { + smc->sm.please_reconnect -- ; + if (smc->sm.please_reconnect == 0) { + /* Counted down */ + queue_event(smc,EVENT_ECM,EC_CONNECT) ; + } + } + + if (event == SM_FAST) + return ; + + /* + * timer for periodic cleanup in driver + * reset and start the watchdog (FM2) + * ESS timer + * SBA timer + */ + smt_timer_poll(smc) ; + smt_start_watchdog(smc) ; +#ifndef SLIM_SMT +#ifndef BOOT +#ifdef ESS + ess_timer_poll(smc) ; +#endif +#endif +#ifdef SBA + sba_timer_poll(smc) ; +#endif + + smt_srf_event(smc,0,0,0) ; + +#endif /* no SLIM_SMT */ + + time = smt_get_time() ; + + if (time - smc->sm.smt_last_lem >= TICKS_PER_SECOND*8) { + /* + * Use 8 sec. for the time intervall, it simplifies the + * LER estimation. + */ + struct fddi_mib_m *mib ; + u_long upper ; + u_long lower ; + int cond ; + int port; + struct s_phy *phy ; + /* + * calculate LEM bit error rate + */ + sm_lem_evaluate(smc) ; + smc->sm.smt_last_lem = time ; + + /* + * check conditions + */ +#ifndef SLIM_SMT + mac_update_counter(smc) ; + mib = smc->mib.m ; + upper = + (mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) + + (mib->fddiMACError_Ct - mib->fddiMACOld_Error_Ct) ; + lower = + (mib->fddiMACFrame_Ct - mib->fddiMACOld_Frame_Ct) + + (mib->fddiMACLost_Ct - mib->fddiMACOld_Lost_Ct) ; + mib->fddiMACFrameErrorRatio = div_ratio(upper,lower) ; + + cond = + ((!mib->fddiMACFrameErrorThreshold && + mib->fddiMACError_Ct != mib->fddiMACOld_Error_Ct) || + (mib->fddiMACFrameErrorRatio > + mib->fddiMACFrameErrorThreshold)) ; + + if (cond != mib->fddiMACFrameErrorFlag) + smt_srf_event(smc,SMT_COND_MAC_FRAME_ERROR, + INDEX_MAC,cond) ; + + upper = + (mib->fddiMACNotCopied_Ct - mib->fddiMACOld_NotCopied_Ct) ; + lower = + upper + + (mib->fddiMACCopied_Ct - mib->fddiMACOld_Copied_Ct) ; + mib->fddiMACNotCopiedRatio = div_ratio(upper,lower) ; + + cond = + ((!mib->fddiMACNotCopiedThreshold && + mib->fddiMACNotCopied_Ct != + mib->fddiMACOld_NotCopied_Ct)|| + (mib->fddiMACNotCopiedRatio > + mib->fddiMACNotCopiedThreshold)) ; + + if (cond != mib->fddiMACNotCopiedFlag) + smt_srf_event(smc,SMT_COND_MAC_NOT_COPIED, + INDEX_MAC,cond) ; + + /* + * set old values + */ + mib->fddiMACOld_Frame_Ct = mib->fddiMACFrame_Ct ; + mib->fddiMACOld_Copied_Ct = mib->fddiMACCopied_Ct ; + mib->fddiMACOld_Error_Ct = mib->fddiMACError_Ct ; + mib->fddiMACOld_Lost_Ct = mib->fddiMACLost_Ct ; + mib->fddiMACOld_NotCopied_Ct = mib->fddiMACNotCopied_Ct ; + + /* + * Check port EBError Condition + */ + for (port = 0; port < NUMPHYS; port ++) { + phy = &smc->y[port] ; + + if (!phy->mib->fddiPORTHardwarePresent) { + continue; + } + + cond = (phy->mib->fddiPORTEBError_Ct - + phy->mib->fddiPORTOldEBError_Ct > 5) ; + + /* If ratio is more than 5 in 8 seconds + * Set the condition. + */ + smt_srf_event(smc,SMT_COND_PORT_EB_ERROR, + (int) (INDEX_PORT+ phy->np) ,cond) ; + + /* + * set old values + */ + phy->mib->fddiPORTOldEBError_Ct = + phy->mib->fddiPORTEBError_Ct ; + } + +#endif /* no SLIM_SMT */ + } + +#ifndef SLIM_SMT + + if (time - smc->sm.smt_last_notify >= (u_long) + (smc->mib.fddiSMTTT_Notify * TICKS_PER_SECOND) ) { + /* + * we can either send an announcement or a request + * a request will trigger a reply so that we can update + * our dna + * note: same tid must be used until reply is received + */ + if (!smc->sm.pend[SMT_TID_NIF]) + smc->sm.pend[SMT_TID_NIF] = smt_get_tid(smc) ; + smt_send_nif(smc,&fddi_broadcast,FC_SMT_NSA, + smc->sm.pend[SMT_TID_NIF], SMT_REQUEST,0) ; + smc->sm.smt_last_notify = time ; + } + + /* + * check timer + */ + if (smc->sm.smt_tvu && + time - smc->sm.smt_tvu > 228*TICKS_PER_SECOND) { + DB_SMT("SMT : UNA expired\n",0,0) ; + smc->sm.smt_tvu = 0 ; + + if (!is_equal(&smc->mib.m[MAC0].fddiMACUpstreamNbr, + &SMT_Unknown)){ + /* Do not update unknown address */ + smc->mib.m[MAC0].fddiMACOldUpstreamNbr= + smc->mib.m[MAC0].fddiMACUpstreamNbr ; + } + smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ; + smc->mib.m[MAC0].fddiMACUNDA_Flag = FALSE ; + /* + * Make sure the fddiMACUNDA_Flag = FALSE is + * included in the SRF so we don't generate + * a seperate SRF for the deassertion of this + * condition + */ + update_dac(smc,0) ; + smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE, + INDEX_MAC,0) ; + } + if (smc->sm.smt_tvd && + time - smc->sm.smt_tvd > 228*TICKS_PER_SECOND) { + DB_SMT("SMT : DNA expired\n",0,0) ; + smc->sm.smt_tvd = 0 ; + if (!is_equal(&smc->mib.m[MAC0].fddiMACDownstreamNbr, + &SMT_Unknown)){ + /* Do not update unknown address */ + smc->mib.m[MAC0].fddiMACOldDownstreamNbr= + smc->mib.m[MAC0].fddiMACDownstreamNbr ; + } + smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ; + smt_srf_event(smc, SMT_EVENT_MAC_NEIGHBOR_CHANGE, + INDEX_MAC,0) ; + } + +#endif /* no SLIM_SMT */ + +#ifndef SMT_REAL_TOKEN_CT + /* + * Token counter emulation section. If hardware supports the token + * count, the token counter will be updated in mac_update_counter. + */ + for (i = MAC0; i < NUMMACS; i++ ){ + if (time - smc->sm.last_tok_time[i] > 2*TICKS_PER_SECOND ){ + smt_emulate_token_ct( smc, i ); + } + } +#endif + + smt_timer_start(smc,&smc->sm.smt_timer, (u_long)1000000L, + EV_TOKEN(EVENT_SMT,SM_TIMER)) ; +} + +static int div_ratio(upper,lower) +u_long upper ; +u_long lower ; +{ + if ((upper<<16L) < upper) + upper = 0xffff0000L ; + else + upper <<= 16L ; + if (!lower) + return(0) ; + return((int)(upper/lower)) ; +} + +#ifndef SLIM_SMT + +/* + * receive packet handler + */ +void smt_received_pack(smc,mb,fs) +struct s_smc *smc ; +SMbuf *mb ; +int fs ; /* frame status */ +{ + struct smt_header *sm ; + int local ; + + int illegal = 0 ; + + switch (m_fc(mb)) { + case FC_SMT_INFO : + case FC_SMT_LAN_LOC : + case FC_SMT_LOC : + case FC_SMT_NSA : + break ; + default : + smt_free_mbuf(smc,mb) ; + return ; + } + + smc->mib.m[MAC0].fddiMACSMTCopied_Ct++ ; + sm = smtod(mb,struct smt_header *) ; + local = ((fs & L_INDICATOR) != 0) ; + hwm_conv_can(smc,(char *)sm,12) ; + + /* check destination address */ + if (is_individual(&sm->smt_dest) && !is_my_addr(smc,&sm->smt_dest)) { + smt_free_mbuf(smc,mb) ; + return ; + } +#if 0 /* for DUP recognition, do NOT filter them */ + /* ignore loop back packets */ + if (is_my_addr(smc,&sm->smt_source) && !local) { + smt_free_mbuf(smc,mb) ; + return ; + } +#endif + + smt_swap_para(sm,(int) mb->sm_len,1) ; + DB_SMT("SMT : received packet [%s] at 0x%x\n", + smt_type_name[m_fc(mb) & 0xf],sm) ; + DB_SMT("SMT : version %d, class %s\n",sm->smt_version, + smt_class_name[(sm->smt_class>LAST_CLASS)?0 : sm->smt_class]) ; + +#ifdef SBA + /* + * check if NSA frame + */ + if (m_fc(mb) == FC_SMT_NSA && sm->smt_class == SMT_NIF && + (sm->smt_type == SMT_ANNOUNCE || sm->smt_type == SMT_REQUEST)) { + smc->sba.sm = sm ; + sba(smc,NIF) ; + } +#endif + + /* + * ignore any packet with NSA and A-indicator set + */ + if ( (fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) { + DB_SMT("SMT : ignoring NSA with A-indicator set from %s\n", + addr_to_string(&sm->smt_source),0) ; + smt_free_mbuf(smc,mb) ; + return ; + } + + /* + * ignore frames with illegal length + */ + if (((sm->smt_class == SMT_ECF) && (sm->smt_len > SMT_MAX_ECHO_LEN)) || + ((sm->smt_class != SMT_ECF) && (sm->smt_len > SMT_MAX_INFO_LEN))) { + smt_free_mbuf(smc,mb) ; + return ; + } + + /* + * check SMT version + */ + switch (sm->smt_class) { + case SMT_NIF : + case SMT_SIF_CONFIG : + case SMT_SIF_OPER : + case SMT_ECF : + if (sm->smt_version != SMT_VID) + illegal = 1; + break ; + default : + if (sm->smt_version != SMT_VID_2) + illegal = 1; + break ; + } + if (illegal) { + DB_SMT("SMT : version = %d, dest = %s\n", + sm->smt_version,addr_to_string(&sm->smt_source)) ; + smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_VERSION,local) ; + smt_free_mbuf(smc,mb) ; + return ; + } + if ((sm->smt_len > mb->sm_len - sizeof(struct smt_header)) || + ((sm->smt_len & 3) && (sm->smt_class != SMT_ECF))) { + DB_SMT("SMT: info length error, len = %d\n",sm->smt_len,0) ; + smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH,local) ; + smt_free_mbuf(smc,mb) ; + return ; + } + switch (sm->smt_class) { + case SMT_NIF : + if (smt_check_para(smc,sm,plist_nif)) { + DB_SMT("SMT: NIF with para problem, ignoring\n",0,0) ; + break ; + } ; + switch (sm->smt_type) { + case SMT_ANNOUNCE : + case SMT_REQUEST : + if (!(fs & C_INDICATOR) && m_fc(mb) == FC_SMT_NSA + && is_broadcast(&sm->smt_dest)) { + struct smt_p_state *st ; + + /* set my UNA */ + if (!is_equal( + &smc->mib.m[MAC0].fddiMACUpstreamNbr, + &sm->smt_source)) { + DB_SMT("SMT : updated my UNA = %s\n", + addr_to_string(&sm->smt_source),0) ; + if (!is_equal(&smc->mib.m[MAC0]. + fddiMACUpstreamNbr,&SMT_Unknown)){ + /* Do not update unknown address */ + smc->mib.m[MAC0].fddiMACOldUpstreamNbr= + smc->mib.m[MAC0].fddiMACUpstreamNbr ; + } + + smc->mib.m[MAC0].fddiMACUpstreamNbr = + sm->smt_source ; + smt_srf_event(smc, + SMT_EVENT_MAC_NEIGHBOR_CHANGE, + INDEX_MAC,0) ; + smt_echo_test(smc,0) ; + } + smc->sm.smt_tvu = smt_get_time() ; + st = (struct smt_p_state *) + sm_to_para(smc,sm,SMT_P_STATE) ; + if (st) { + smc->mib.m[MAC0].fddiMACUNDA_Flag = + (st->st_dupl_addr & SMT_ST_MY_DUPA) ? + TRUE : FALSE ; + update_dac(smc,1) ; + } + } + if ((sm->smt_type == SMT_REQUEST) && + is_individual(&sm->smt_source) && + ((!(fs & A_INDICATOR) && m_fc(mb) == FC_SMT_NSA) || + (m_fc(mb) != FC_SMT_NSA))) { + DB_SMT("SMT : replying to NIF request %s\n", + addr_to_string(&sm->smt_source),0) ; + smt_send_nif(smc,&sm->smt_source, + FC_SMT_INFO, + sm->smt_tid, + SMT_REPLY,local) ; + } + break ; + case SMT_REPLY : + DB_SMT("SMT : received NIF response from %s\n", + addr_to_string(&sm->smt_source),0) ; + if (fs & A_INDICATOR) { + smc->sm.pend[SMT_TID_NIF] = 0 ; + DB_SMT("SMT : duplicate address\n",0,0) ; + smc->mib.m[MAC0].fddiMACDupAddressTest = + DA_FAILED ; + smc->r.dup_addr_test = DA_FAILED ; + queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ; + smc->mib.m[MAC0].fddiMACDA_Flag = TRUE ; + update_dac(smc,1) ; + break ; + } + if (sm->smt_tid == smc->sm.pend[SMT_TID_NIF]) { + smc->sm.pend[SMT_TID_NIF] = 0 ; + /* set my DNA */ + if (!is_equal( + &smc->mib.m[MAC0].fddiMACDownstreamNbr, + &sm->smt_source)) { + DB_SMT("SMT : updated my DNA\n",0,0) ; + if (!is_equal(&smc->mib.m[MAC0]. + fddiMACDownstreamNbr, &SMT_Unknown)){ + /* Do not update unknown address */ + smc->mib.m[MAC0].fddiMACOldDownstreamNbr = + smc->mib.m[MAC0].fddiMACDownstreamNbr ; + } + + smc->mib.m[MAC0].fddiMACDownstreamNbr = + sm->smt_source ; + smt_srf_event(smc, + SMT_EVENT_MAC_NEIGHBOR_CHANGE, + INDEX_MAC,0) ; + smt_echo_test(smc,1) ; + } + smc->mib.m[MAC0].fddiMACDA_Flag = FALSE ; + update_dac(smc,1) ; + smc->sm.smt_tvd = smt_get_time() ; + smc->mib.m[MAC0].fddiMACDupAddressTest = + DA_PASSED ; + if (smc->r.dup_addr_test != DA_PASSED) { + smc->r.dup_addr_test = DA_PASSED ; + queue_event(smc,EVENT_RMT,RM_DUP_ADDR) ; + } + } + else if (sm->smt_tid == + smc->sm.pend[SMT_TID_NIF_TEST]) { + DB_SMT("SMT : NIF test TID ok\n",0,0) ; + } + else { + DB_SMT("SMT : expected TID %lx, got %lx\n", + smc->sm.pend[SMT_TID_NIF],sm->smt_tid) ; + } + break ; + default : + illegal = 2 ; + break ; + } + break ; + case SMT_SIF_CONFIG : /* station information */ + if (sm->smt_type != SMT_REQUEST) + break ; + DB_SMT("SMT : replying to SIF Config request from %s\n", + addr_to_string(&sm->smt_source),0) ; + smt_send_sif_config(smc,&sm->smt_source,sm->smt_tid,local) ; + break ; + case SMT_SIF_OPER : /* station information */ + if (sm->smt_type != SMT_REQUEST) + break ; + DB_SMT("SMT : replying to SIF Operation request from %s\n", + addr_to_string(&sm->smt_source),0) ; + smt_send_sif_operation(smc,&sm->smt_source,sm->smt_tid,local) ; + break ; + case SMT_ECF : /* echo frame */ + switch (sm->smt_type) { + case SMT_REPLY : + smc->mib.priv.fddiPRIVECF_Reply_Rx++ ; + DB_SMT("SMT: received ECF reply from %s\n", + addr_to_string(&sm->smt_source),0) ; + if (sm_to_para(smc,sm,SMT_P_ECHODATA) == 0) { + DB_SMT("SMT: ECHODATA missing\n",0,0) ; + break ; + } + if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF]) { + DB_SMT("SMT : ECF test TID ok\n",0,0) ; + } + else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_UNA]) { + DB_SMT("SMT : ECF test UNA ok\n",0,0) ; + } + else if (sm->smt_tid == smc->sm.pend[SMT_TID_ECF_DNA]) { + DB_SMT("SMT : ECF test DNA ok\n",0,0) ; + } + else { + DB_SMT("SMT : expected TID %lx, got %lx\n", + smc->sm.pend[SMT_TID_ECF], + sm->smt_tid) ; + } + break ; + case SMT_REQUEST : + smc->mib.priv.fddiPRIVECF_Req_Rx++ ; + { + if (sm->smt_len && !sm_to_para(smc,sm,SMT_P_ECHODATA)) { + DB_SMT("SMT: ECF with para problem,sending RDF\n",0,0) ; + smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_LENGTH, + local) ; + break ; + } + DB_SMT("SMT - sending ECF reply to %s\n", + addr_to_string(&sm->smt_source),0) ; + + /* set destination addr. & reply */ + sm->smt_dest = sm->smt_source ; + sm->smt_type = SMT_REPLY ; + dump_smt(smc,sm,"ECF REPLY") ; + smc->mib.priv.fddiPRIVECF_Reply_Tx++ ; + smt_send_frame(smc,mb,FC_SMT_INFO,local) ; + return ; /* DON'T free mbuf */ + } + default : + illegal = 1 ; + break ; + } + break ; +#ifndef BOOT + case SMT_RAF : /* resource allocation */ +#ifdef ESS + DB_ESSN(2,"ESS: RAF frame received\n",0,0) ; + fs = ess_raf_received_pack(smc,mb,sm,fs) ; +#endif + +#ifdef SBA + DB_SBAN(2,"SBA: RAF frame received\n",0,0) ; + sba_raf_received_pack(smc,sm,fs) ; +#endif + break ; + case SMT_RDF : /* request denied */ + smc->mib.priv.fddiPRIVRDF_Rx++ ; + break ; + case SMT_ESF : /* extended service - not supported */ + if (sm->smt_type == SMT_REQUEST) { + DB_SMT("SMT - received ESF, sending RDF\n",0,0) ; + smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ; + } + break ; + case SMT_PMF_GET : + case SMT_PMF_SET : + if (sm->smt_type != SMT_REQUEST) + break ; + /* update statistics */ + if (sm->smt_class == SMT_PMF_GET) + smc->mib.priv.fddiPRIVPMF_Get_Rx++ ; + else + smc->mib.priv.fddiPRIVPMF_Set_Rx++ ; + /* + * ignore PMF SET with I/G set + */ + if ((sm->smt_class == SMT_PMF_SET) && + !is_individual(&sm->smt_dest)) { + DB_SMT("SMT: ignoring PMF-SET with I/G set\n",0,0) ; + break ; + } + smt_pmf_received_pack(smc,mb, local) ; + break ; + case SMT_SRF : + dump_smt(smc,sm,"SRF received") ; + break ; + default : + if (sm->smt_type != SMT_REQUEST) + break ; + /* + * For frames with unknown class: + * we need to send a RDF frame according to 8.1.3.1.1, + * only if it is a REQUEST. + */ + DB_SMT("SMT : class = %d, send RDF to %s\n", + sm->smt_class, addr_to_string(&sm->smt_source)) ; + + smt_send_rdf(smc,mb,m_fc(mb),SMT_RDF_CLASS,local) ; + break ; +#endif + } + if (illegal) { + DB_SMT("SMT: discarding illegal frame, reason = %d\n", + illegal,0) ; + } + smt_free_mbuf(smc,mb) ; +} + +static void update_dac(smc,report) +struct s_smc *smc ; +int report ; +{ + int cond ; + + cond = ( smc->mib.m[MAC0].fddiMACUNDA_Flag | + smc->mib.m[MAC0].fddiMACDA_Flag) != 0 ; + if (report && (cond != smc->mib.m[MAC0].fddiMACDuplicateAddressCond)) + smt_srf_event(smc, SMT_COND_MAC_DUP_ADDR,INDEX_MAC,cond) ; + else + smc->mib.m[MAC0].fddiMACDuplicateAddressCond = cond ; +} + +/* + * send SMT frame + * set source address + * set station ID + * send frame + */ +EXPORT_PMF void smt_send_frame(smc,mb,fc,local) +struct s_smc *smc ; +SMbuf *mb ; /* buffer to send */ +int fc ; /* FC value */ +int local ; +{ + struct smt_header *sm ; + + if (!smc->r.sm_ma_avail && !local) { + smt_free_mbuf(smc,mb) ; + return ; + } + sm = smtod(mb,struct smt_header *) ; + sm->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ; + sm->smt_sid = smc->mib.fddiSMTStationId ; + + smt_swap_para(sm,(int) mb->sm_len,0) ; /* swap para & header */ + hwm_conv_can(smc,(char *)sm,12) ; /* convert SA and DA */ + smc->mib.m[MAC0].fddiMACSMTTransmit_Ct++ ; + smt_send_mbuf(smc,mb,local ? FC_SMT_LOC : fc) ; +} + +/* + * generate and send RDF + */ +static void smt_send_rdf(smc,rej,fc,reason,local) +struct s_smc *smc ; +SMbuf *rej ; /* mbuf of offending frame */ +int fc ; /* FC of denied frame */ +int reason ; /* reason code */ +int local ; +{ + SMbuf *mb ; + struct smt_header *sm ; /* header of offending frame */ + struct smt_rdf *rdf ; + int len ; + int frame_len ; + + sm = smtod(rej,struct smt_header *) ; + if (sm->smt_type != SMT_REQUEST) + return ; + + DB_SMT("SMT: sending RDF to %s,reason = 0x%x\n", + addr_to_string(&sm->smt_source),reason) ; + + + /* + * note: get framelength from MAC length, NOT from SMT header + * smt header length is included in sm_len + */ + frame_len = rej->sm_len ; + + if (!(mb=smt_build_frame(smc,SMT_RDF,SMT_REPLY,sizeof(struct smt_rdf)))) + return ; + rdf = smtod(mb,struct smt_rdf *) ; + rdf->smt.smt_tid = sm->smt_tid ; /* use TID from sm */ + rdf->smt.smt_dest = sm->smt_source ; /* set dest = source */ + + /* set P12 */ + rdf->reason.para.p_type = SMT_P_REASON ; + rdf->reason.para.p_len = sizeof(struct smt_p_reason) - PARA_LEN ; + rdf->reason.rdf_reason = reason ; + + /* set P14 */ + rdf->version.para.p_type = SMT_P_VERSION ; + rdf->version.para.p_len = sizeof(struct smt_p_version) - PARA_LEN ; + rdf->version.v_pad = 0 ; + rdf->version.v_n = 1 ; + rdf->version.v_index = 1 ; + rdf->version.v_version[0] = SMT_VID_2 ; + rdf->version.v_pad2 = 0 ; + + /* set P13 */ + if ((unsigned) frame_len <= SMT_MAX_INFO_LEN - sizeof(*rdf) + + 2*sizeof(struct smt_header)) + len = frame_len ; + else + len = SMT_MAX_INFO_LEN - sizeof(*rdf) + + 2*sizeof(struct smt_header) ; + /* make length multiple of 4 */ + len &= ~3 ; + rdf->refused.para.p_type = SMT_P_REFUSED ; + /* length of para is smt_frame + ref_fc */ + rdf->refused.para.p_len = len + 4 ; + rdf->refused.ref_fc = fc ; + + /* swap it back */ + smt_swap_para(sm,frame_len,0) ; + + memcpy((char *) &rdf->refused.ref_header,(char *) sm,len) ; + + len -= sizeof(struct smt_header) ; + mb->sm_len += len ; + rdf->smt.smt_len += len ; + + dump_smt(smc,(struct smt_header *)rdf,"RDF") ; + smc->mib.priv.fddiPRIVRDF_Tx++ ; + smt_send_frame(smc,mb,FC_SMT_INFO,local) ; +} + +/* + * generate and send NIF + */ +static void smt_send_nif(smc,dest,fc,tid,type,local) +struct s_smc *smc ; +struct fddi_addr *dest ; /* dest address */ +int fc ; /* frame control */ +u_long tid ; /* transaction id */ +int type ; /* frame type */ +int local ; +{ + struct smt_nif *nif ; + SMbuf *mb ; + + if (!(mb = smt_build_frame(smc,SMT_NIF,type,sizeof(struct smt_nif)))) + return ; + nif = smtod(mb, struct smt_nif *) ; + smt_fill_una(smc,&nif->una) ; /* set UNA */ + smt_fill_sde(smc,&nif->sde) ; /* set station descriptor */ + smt_fill_state(smc,&nif->state) ; /* set state information */ +#ifdef SMT6_10 + smt_fill_fsc(smc,&nif->fsc) ; /* set frame status cap. */ +#endif + nif->smt.smt_dest = *dest ; /* destination address */ + nif->smt.smt_tid = tid ; /* transaction ID */ + dump_smt(smc,(struct smt_header *)nif,"NIF") ; + smt_send_frame(smc,mb,fc,local) ; +} + +#ifdef DEBUG +/* + * send NIF request (test purpose) + */ +static void smt_send_nif_request(smc,dest) +struct s_smc *smc ; +struct fddi_addr *dest ; +{ + smc->sm.pend[SMT_TID_NIF_TEST] = smt_get_tid(smc) ; + smt_send_nif(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_NIF_TEST], + SMT_REQUEST,0) ; +} + +/* + * send ECF request (test purpose) + */ +static void smt_send_ecf_request(smc,dest,len) +struct s_smc *smc ; +struct fddi_addr *dest ; +int len ; +{ + smc->sm.pend[SMT_TID_ECF] = smt_get_tid(smc) ; + smt_send_ecf(smc,dest, FC_SMT_INFO, smc->sm.pend[SMT_TID_ECF], + SMT_REQUEST,len) ; +} +#endif + +/* + * echo test + */ +static void smt_echo_test(smc,dna) +struct s_smc *smc ; +int dna ; +{ + u_long tid ; + + smc->sm.pend[dna ? SMT_TID_ECF_DNA : SMT_TID_ECF_UNA] = + tid = smt_get_tid(smc) ; + smt_send_ecf(smc, dna ? + &smc->mib.m[MAC0].fddiMACDownstreamNbr : + &smc->mib.m[MAC0].fddiMACUpstreamNbr, + FC_SMT_INFO,tid, SMT_REQUEST, (SMT_TEST_ECHO_LEN & ~3)-8) ; +} + +/* + * generate and send ECF + */ +static void smt_send_ecf(smc,dest,fc,tid,type,len) +struct s_smc *smc ; +struct fddi_addr *dest ; /* dest address */ +int fc ; /* frame control */ +u_long tid ; /* transaction id */ +int type ; /* frame type */ +int len ; /* frame length */ +{ + struct smt_ecf *ecf ; + SMbuf *mb ; + + if (!(mb = smt_build_frame(smc,SMT_ECF,type,SMT_ECF_LEN + len))) + return ; + ecf = smtod(mb, struct smt_ecf *) ; + + smt_fill_echo(smc,&ecf->ec_echo,tid,len) ; /* set ECHO */ + ecf->smt.smt_dest = *dest ; /* destination address */ + ecf->smt.smt_tid = tid ; /* transaction ID */ + smc->mib.priv.fddiPRIVECF_Req_Tx++ ; + smt_send_frame(smc,mb,fc,0) ; +} + +/* + * generate and send SIF config response + */ + +static void smt_send_sif_config(smc,dest,tid,local) +struct s_smc *smc ; +struct fddi_addr *dest ; /* dest address */ +u_long tid ; /* transaction id */ +int local ; +{ + struct smt_sif_config *sif ; + SMbuf *mb ; + int len ; + if (!(mb = smt_build_frame(smc,SMT_SIF_CONFIG,SMT_REPLY, + SIZEOF_SMT_SIF_CONFIG))) + return ; + + sif = smtod(mb, struct smt_sif_config *) ; + smt_fill_timestamp(smc,&sif->ts) ; /* set time stamp */ + smt_fill_sde(smc,&sif->sde) ; /* set station descriptor */ + smt_fill_version(smc,&sif->version) ; /* set version information */ + smt_fill_state(smc,&sif->state) ; /* set state information */ + smt_fill_policy(smc,&sif->policy) ; /* set station policy */ + smt_fill_latency(smc,&sif->latency); /* set station latency */ + smt_fill_neighbor(smc,&sif->neighbor); /* set station neighbor */ + smt_fill_setcount(smc,&sif->setcount) ; /* set count */ + len = smt_fill_path(smc,&sif->path); /* set station path descriptor*/ + sif->smt.smt_dest = *dest ; /* destination address */ + sif->smt.smt_tid = tid ; /* transaction ID */ + smt_add_frame_len(mb,len) ; /* adjust length fields */ + dump_smt(smc,(struct smt_header *)sif,"SIF Configuration Reply") ; + smt_send_frame(smc,mb,FC_SMT_INFO,local) ; +} + +/* + * generate and send SIF operation response + */ + +static void smt_send_sif_operation(smc,dest,tid,local) +struct s_smc *smc ; +struct fddi_addr *dest ; /* dest address */ +u_long tid ; /* transaction id */ +int local ; +{ + struct smt_sif_operation *sif ; + SMbuf *mb ; + int ports ; + int i ; + + ports = NUMPHYS ; +#ifndef CONCENTRATOR + if (smc->s.sas == SMT_SAS) + ports = 1 ; +#endif + + if (!(mb = smt_build_frame(smc,SMT_SIF_OPER,SMT_REPLY, + SIZEOF_SMT_SIF_OPERATION+ports*sizeof(struct smt_p_lem)))) + return ; + sif = smtod(mb, struct smt_sif_operation *) ; + smt_fill_timestamp(smc,&sif->ts) ; /* set time stamp */ + smt_fill_mac_status(smc,&sif->status) ; /* set mac status */ + smt_fill_mac_counter(smc,&sif->mc) ; /* set mac counter field */ + smt_fill_mac_fnc(smc,&sif->fnc) ; /* set frame not copied counter */ + smt_fill_manufacturer(smc,&sif->man) ; /* set manufacturer field */ + smt_fill_user(smc,&sif->user) ; /* set user field */ + smt_fill_setcount(smc,&sif->setcount) ; /* set count */ + /* + * set link error mon information + */ + if (ports == 1) { + smt_fill_lem(smc,sif->lem,PS) ; + } + else { + for (i = 0 ; i < ports ; i++) { + smt_fill_lem(smc,&sif->lem[i],i) ; + } + } + + sif->smt.smt_dest = *dest ; /* destination address */ + sif->smt.smt_tid = tid ; /* transaction ID */ + dump_smt(smc,(struct smt_header *)sif,"SIF Operation Reply") ; + smt_send_frame(smc,mb,FC_SMT_INFO,local) ; +} + +/* + * get and initialize SMT frame + */ +EXPORT_PMF SMbuf *smt_build_frame(smc,class,type,length) +struct s_smc *smc ; +int class ; +int type ; +int length ; +{ + SMbuf *mb ; + struct smt_header *smt ; + +#if 0 + if (!smc->r.sm_ma_avail) { + return(0) ; + } +#endif + if (!(mb = smt_get_mbuf(smc))) + return(mb) ; + + mb->sm_len = length ; + smt = smtod(mb, struct smt_header *) ; + smt->smt_dest = fddi_broadcast ; /* set dest = broadcast */ + smt->smt_class = class ; + smt->smt_type = type ; + switch (class) { + case SMT_NIF : + case SMT_SIF_CONFIG : + case SMT_SIF_OPER : + case SMT_ECF : + smt->smt_version = SMT_VID ; + break ; + default : + smt->smt_version = SMT_VID_2 ; + break ; + } + smt->smt_tid = smt_get_tid(smc) ; /* set transaction ID */ + smt->smt_pad = 0 ; + smt->smt_len = length - sizeof(struct smt_header) ; + return(mb) ; +} + +static void smt_add_frame_len(mb,len) +SMbuf *mb ; +int len ; +{ + struct smt_header *smt ; + + smt = smtod(mb, struct smt_header *) ; + smt->smt_len += len ; + mb->sm_len += len ; +} + + + +/* + * fill values in UNA parameter + */ +static void smt_fill_una(smc,una) +struct s_smc *smc ; +struct smt_p_una *una ; +{ + SMTSETPARA(una,SMT_P_UNA) ; + una->una_pad = 0 ; + una->una_node = smc->mib.m[MAC0].fddiMACUpstreamNbr ; +} + +/* + * fill values in SDE parameter + */ +static void smt_fill_sde(smc,sde) +struct s_smc *smc ; +struct smt_p_sde *sde ; +{ + SMTSETPARA(sde,SMT_P_SDE) ; + sde->sde_non_master = smc->mib.fddiSMTNonMaster_Ct ; + sde->sde_master = smc->mib.fddiSMTMaster_Ct ; + sde->sde_mac_count = NUMMACS ; /* only 1 MAC */ +#ifdef CONCENTRATOR + sde->sde_type = SMT_SDE_CONCENTRATOR ; +#else + sde->sde_type = SMT_SDE_STATION ; +#endif +} + +/* + * fill in values in station state parameter + */ +static void smt_fill_state(smc,state) +struct s_smc *smc ; +struct smt_p_state *state ; +{ + int top ; + int twist ; + + SMTSETPARA(state,SMT_P_STATE) ; + state->st_pad = 0 ; + + /* determine topology */ + top = 0 ; + if (smc->mib.fddiSMTPeerWrapFlag) { + top |= SMT_ST_WRAPPED ; /* state wrapped */ + } +#ifdef CONCENTRATOR + if (cfm_status_unattached(smc)) { + top |= SMT_ST_UNATTACHED ; /* unattached concentrator */ + } +#endif + if ((twist = pcm_status_twisted(smc)) & 1) { + top |= SMT_ST_TWISTED_A ; /* twisted cable */ + } + if (twist & 2) { + top |= SMT_ST_TWISTED_B ; /* twisted cable */ + } +#ifdef OPT_SRF + top |= SMT_ST_SRF ; +#endif + if (pcm_rooted_station(smc)) + top |= SMT_ST_ROOTED_S ; + if (smc->mib.a[0].fddiPATHSbaPayload != 0) + top |= SMT_ST_SYNC_SERVICE ; + state->st_topology = top ; + state->st_dupl_addr = + ((smc->mib.m[MAC0].fddiMACDA_Flag ? SMT_ST_MY_DUPA : 0 ) | + (smc->mib.m[MAC0].fddiMACUNDA_Flag ? SMT_ST_UNA_DUPA : 0)) ; +} + +/* + * fill values in timestamp parameter + */ +static void smt_fill_timestamp(smc,ts) +struct s_smc *smc ; +struct smt_p_timestamp *ts ; +{ + + SMTSETPARA(ts,SMT_P_TIMESTAMP) ; + smt_set_timestamp(smc,ts->ts_time) ; +} + +EXPORT_PMF void smt_set_timestamp(smc,p) +struct s_smc *smc ; +u_char *p ; +{ + u_long time ; + u_long utime ; + + /* + * timestamp is 64 bits long ; resolution is 80 nS + * our clock resolution is 10mS + * 10mS/80ns = 125000 ~ 2^17 = 131072 + */ + utime = smt_get_time() ; + time = utime * 100 ; + time /= TICKS_PER_SECOND ; + p[0] = 0 ; + p[1] = (u_char)((time>>(8+8+8+8-1)) & 1) ; + p[2] = (u_char)(time>>(8+8+8-1)) ; + p[3] = (u_char)(time>>(8+8-1)) ; + p[4] = (u_char)(time>>(8-1)) ; + p[5] = (u_char)(time<<1) ; + p[6] = (u_char)(smc->sm.uniq_ticks>>8) ; + p[7] = (u_char)smc->sm.uniq_ticks ; + /* + * make sure we don't wrap: restart whenever the upper digits change + */ + if (utime != smc->sm.uniq_time) { + smc->sm.uniq_ticks = 0 ; + } + smc->sm.uniq_ticks++ ; + smc->sm.uniq_time = utime ; +} + +/* + * fill values in station policy parameter + */ +static void smt_fill_policy(smc,policy) +struct s_smc *smc ; +struct smt_p_policy *policy ; +{ + int i ; + u_char *map ; + u_short in ; + u_short out ; + + /* + * MIB para 101b (fddiSMTConnectionPolicy) coding + * is different from 0005 coding + */ + static u_char ansi_weirdness[16] = { + 0,7,5,3,8,1,6,4,9,10,2,11,12,13,14,15 + } ; + SMTSETPARA(policy,SMT_P_POLICY) ; + + out = 0 ; + in = smc->mib.fddiSMTConnectionPolicy ; + for (i = 0, map = ansi_weirdness ; i < 16 ; i++) { + if (in & 1) + out |= (1<<*map) ; + in >>= 1 ; + map++ ; + } + policy->pl_config = smc->mib.fddiSMTConfigPolicy ; + policy->pl_connect = out ; +} + +/* + * fill values in latency equivalent parameter + */ +static void smt_fill_latency(smc,latency) +struct s_smc *smc ; +struct smt_p_latency *latency ; +{ + SMTSETPARA(latency,SMT_P_LATENCY) ; + + latency->lt_phyout_idx1 = phy_index(smc,0) ; + latency->lt_latency1 = 10 ; /* in octets (byte clock) */ + /* + * note: latency has two phy entries by definition + * for a SAS, the 2nd one is null + */ + if (smc->s.sas == SMT_DAS) { + latency->lt_phyout_idx2 = phy_index(smc,1) ; + latency->lt_latency2 = 10 ; /* in octets (byte clock) */ + } + else { + latency->lt_phyout_idx2 = 0 ; + latency->lt_latency2 = 0 ; + } +} + +/* + * fill values in MAC neighbors parameter + */ +static void smt_fill_neighbor(smc,neighbor) +struct s_smc *smc ; +struct smt_p_neighbor *neighbor ; +{ + SMTSETPARA(neighbor,SMT_P_NEIGHBORS) ; + + neighbor->nb_mib_index = INDEX_MAC ; + neighbor->nb_mac_index = mac_index(smc,1) ; + neighbor->nb_una = smc->mib.m[MAC0].fddiMACUpstreamNbr ; + neighbor->nb_dna = smc->mib.m[MAC0].fddiMACDownstreamNbr ; +} + +/* + * fill values in path descriptor + */ +#ifdef CONCENTRATOR +#define ALLPHYS NUMPHYS +#else +#define ALLPHYS ((smc->s.sas == SMT_SAS) ? 1 : 2) +#endif + +static int smt_fill_path(smc,path) +struct s_smc *smc ; +struct smt_p_path *path ; +{ + SK_LOC_DECL(int,type) ; + SK_LOC_DECL(int,state) ; + SK_LOC_DECL(int,remote) ; + SK_LOC_DECL(int,mac) ; + int len ; + int p ; + int physp ; + struct smt_phy_rec *phy ; + struct smt_mac_rec *pd_mac ; + + len = PARA_LEN + + sizeof(struct smt_mac_rec) * NUMMACS + + sizeof(struct smt_phy_rec) * ALLPHYS ; + path->para.p_type = SMT_P_PATH ; + path->para.p_len = len - PARA_LEN ; + + /* PHYs */ + for (p = 0,phy = path->pd_phy ; p < ALLPHYS ; p++, phy++) { + physp = p ; +#ifndef CONCENTRATOR + if (smc->s.sas == SMT_SAS) + physp = PS ; +#endif + pcm_status_state(smc,physp,&type,&state,&remote,&mac) ; +#ifdef LITTLE_ENDIAN + phy->phy_mib_index = smt_swap_short((u_short)p+INDEX_PORT) ; +#else + phy->phy_mib_index = p+INDEX_PORT ; +#endif + phy->phy_type = type ; + phy->phy_connect_state = state ; + phy->phy_remote_type = remote ; + phy->phy_remote_mac = mac ; + phy->phy_resource_idx = phy_con_resource_index(smc,p) ; + } + + /* MAC */ + pd_mac = (struct smt_mac_rec *) phy ; + pd_mac->mac_addr = smc->mib.m[MAC0].fddiMACSMTAddress ; + pd_mac->mac_resource_idx = mac_con_resource_index(smc,1) ; + return(len) ; +} + +/* + * fill values in mac status + */ +static void smt_fill_mac_status(smc,st) +struct s_smc *smc ; +struct smt_p_mac_status *st ; +{ + SMTSETPARA(st,SMT_P_MAC_STATUS) ; + + st->st_mib_index = INDEX_MAC ; + st->st_mac_index = mac_index(smc,1) ; + + mac_update_counter(smc) ; + /* + * timer values are represented in SMT as 2's complement numbers + * units : internal : 2's complement BCLK + */ + st->st_t_req = smc->mib.m[MAC0].fddiMACT_Req ; + st->st_t_neg = smc->mib.m[MAC0].fddiMACT_Neg ; + st->st_t_max = smc->mib.m[MAC0].fddiMACT_Max ; + st->st_tvx_value = smc->mib.m[MAC0].fddiMACTvxValue ; + st->st_t_min = smc->mib.m[MAC0].fddiMACT_Min ; + + st->st_sba = smc->mib.a[PATH0].fddiPATHSbaPayload ; + st->st_frame_ct = smc->mib.m[MAC0].fddiMACFrame_Ct ; + st->st_error_ct = smc->mib.m[MAC0].fddiMACError_Ct ; + st->st_lost_ct = smc->mib.m[MAC0].fddiMACLost_Ct ; +} + +/* + * fill values in LEM status + */ + +static void smt_fill_lem(smc,lem,phy) +struct s_smc *smc ; +struct smt_p_lem *lem ; +int phy ; +{ + struct fddi_mib_p *mib ; + + mib = smc->y[phy].mib ; + + SMTSETPARA(lem,SMT_P_LEM) ; + lem->lem_mib_index = phy+INDEX_PORT ; + lem->lem_phy_index = phy_index(smc,phy) ; + lem->lem_pad2 = 0 ; + lem->lem_cutoff = mib->fddiPORTLer_Cutoff ; + lem->lem_alarm = mib->fddiPORTLer_Alarm ; + /* long term bit error rate */ + lem->lem_estimate = mib->fddiPORTLer_Estimate ; + /* # of rejected connections */ + lem->lem_reject_ct = mib->fddiPORTLem_Reject_Ct ; + lem->lem_ct = mib->fddiPORTLem_Ct ; /* total number of errors */ +} + +/* + * fill version parameter + */ +static void smt_fill_version(smc,vers) +struct s_smc *smc ; +struct smt_p_version *vers ; +{ + SK_UNUSED(smc) ; + SMTSETPARA(vers,SMT_P_VERSION) ; + vers->v_pad = 0 ; + vers->v_n = 1 ; /* one version is enough .. */ + vers->v_index = 1 ; + vers->v_version[0] = SMT_VID_2 ; + vers->v_pad2 = 0 ; +} + +#ifdef SMT6_10 +/* + * fill frame status capabilities + */ +/* + * note: this para 200B is NOT in swap table, because it's also set in + * PMF add_para + */ +static void smt_fill_fsc(smc,fsc) +struct s_smc *smc ; +struct smt_p_fsc *fsc ; +{ + SK_UNUSED(smc) ; + SMTSETPARA(fsc,SMT_P_FSC) ; + fsc->fsc_pad0 = 0 ; + fsc->fsc_mac_index = INDEX_MAC ; /* this is MIB ; MIB is NOT + * mac_index ()i ! + */ + fsc->fsc_pad1 = 0 ; + fsc->fsc_value = FSC_TYPE0 ; /* "normal" node */ +#ifdef LITTLE_ENDIAN + fsc->fsc_mac_index = smt_swap_short(INDEX_MAC) ; + fsc->fsc_value = smt_swap_short(FSC_TYPE0) ; +#endif +} +#endif + +/* + * fill mac counter field + */ +static void smt_fill_mac_counter(smc,mc) +struct s_smc *smc ; +struct smt_p_mac_counter *mc ; +{ + SMTSETPARA(mc,SMT_P_MAC_COUNTER) ; + mc->mc_mib_index = INDEX_MAC ; + mc->mc_index = mac_index(smc,1) ; + mc->mc_receive_ct = smc->mib.m[MAC0].fddiMACCopied_Ct ; + mc->mc_transmit_ct = smc->mib.m[MAC0].fddiMACTransmit_Ct ; +} + +/* + * fill mac frame not copied counter + */ +static void smt_fill_mac_fnc(smc,fnc) +struct s_smc *smc ; +struct smt_p_mac_fnc *fnc ; +{ + SMTSETPARA(fnc,SMT_P_MAC_FNC) ; + fnc->nc_mib_index = INDEX_MAC ; + fnc->nc_index = mac_index(smc,1) ; + fnc->nc_counter = smc->mib.m[MAC0].fddiMACNotCopied_Ct ; +} + + +/* + * fill manufacturer field + */ +static void smt_fill_manufacturer(smc,man) +struct s_smc *smc ; +struct smp_p_manufacturer *man ; +{ + SMTSETPARA(man,SMT_P_MANUFACTURER) ; + memcpy((char *) man->mf_data, + (char *) smc->mib.fddiSMTManufacturerData, + sizeof(man->mf_data)) ; +} + +/* + * fill user field + */ +static void smt_fill_user(smc,user) +struct s_smc *smc ; +struct smp_p_user *user ; +{ + SMTSETPARA(user,SMT_P_USER) ; + memcpy((char *) user->us_data, + (char *) smc->mib.fddiSMTUserData, + sizeof(user->us_data)) ; +} + + + +/* + * fill set count + */ +static void smt_fill_setcount(smc,setcount) +struct s_smc *smc ; +struct smt_p_setcount *setcount ; +{ + SK_UNUSED(smc) ; + SMTSETPARA(setcount,SMT_P_SETCOUNT) ; + setcount->count = smc->mib.fddiSMTSetCount.count ; + memcpy((char *)setcount->timestamp, + (char *)smc->mib.fddiSMTSetCount.timestamp,8) ; +} + +/* + * fill echo data + */ +static void smt_fill_echo(smc,echo,seed,len) +struct s_smc *smc ; +struct smt_p_echo *echo ; +u_long seed ; +int len ; +{ + + u_char *p ; + + SK_UNUSED(smc) ; + SMTSETPARA(echo,SMT_P_ECHODATA) ; + echo->para.p_len = len ; + for (p = echo->ec_data ; len ; len--) { + *p++ = (u_char) seed ; + seed += 13 ; + } +} + +/* + * clear DNA and UNA + * called from CFM if configuration changes + */ +void smt_clear_una_dna(smc) +struct s_smc *smc ; +{ + smc->mib.m[MAC0].fddiMACUpstreamNbr = SMT_Unknown ; + smc->mib.m[MAC0].fddiMACDownstreamNbr = SMT_Unknown ; +} + +static void smt_clear_old_una_dna(smc) +struct s_smc *smc ; +{ + smc->mib.m[MAC0].fddiMACOldUpstreamNbr = SMT_Unknown ; + smc->mib.m[MAC0].fddiMACOldDownstreamNbr = SMT_Unknown ; +} + +u_long smt_get_tid(smc) +struct s_smc *smc ; +{ + u_long tid ; + while ((tid = ++(smc->sm.smt_tid) ^ SMT_TID_MAGIC) == 0) + ; + return(tid & 0x3fffffffL) ; +} + + +/* + * table of parameter lengths + */ +static const struct smt_pdef { + int ptype ; + int plen ; + const char *pswap ; +} smt_pdef[] = { + { SMT_P_UNA, sizeof(struct smt_p_una) , + SWAP_SMT_P_UNA } , + { SMT_P_SDE, sizeof(struct smt_p_sde) , + SWAP_SMT_P_SDE } , + { SMT_P_STATE, sizeof(struct smt_p_state) , + SWAP_SMT_P_STATE } , + { SMT_P_TIMESTAMP,sizeof(struct smt_p_timestamp) , + SWAP_SMT_P_TIMESTAMP } , + { SMT_P_POLICY, sizeof(struct smt_p_policy) , + SWAP_SMT_P_POLICY } , + { SMT_P_LATENCY, sizeof(struct smt_p_latency) , + SWAP_SMT_P_LATENCY } , + { SMT_P_NEIGHBORS,sizeof(struct smt_p_neighbor) , + SWAP_SMT_P_NEIGHBORS } , + { SMT_P_PATH, sizeof(struct smt_p_path) , + SWAP_SMT_P_PATH } , + { SMT_P_MAC_STATUS,sizeof(struct smt_p_mac_status) , + SWAP_SMT_P_MAC_STATUS } , + { SMT_P_LEM, sizeof(struct smt_p_lem) , + SWAP_SMT_P_LEM } , + { SMT_P_MAC_COUNTER,sizeof(struct smt_p_mac_counter) , + SWAP_SMT_P_MAC_COUNTER } , + { SMT_P_MAC_FNC,sizeof(struct smt_p_mac_fnc) , + SWAP_SMT_P_MAC_FNC } , + { SMT_P_PRIORITY,sizeof(struct smt_p_priority) , + SWAP_SMT_P_PRIORITY } , + { SMT_P_EB,sizeof(struct smt_p_eb) , + SWAP_SMT_P_EB } , + { SMT_P_MANUFACTURER,sizeof(struct smp_p_manufacturer) , + SWAP_SMT_P_MANUFACTURER } , + { SMT_P_REASON, sizeof(struct smt_p_reason) , + SWAP_SMT_P_REASON } , + { SMT_P_REFUSED, sizeof(struct smt_p_refused) , + SWAP_SMT_P_REFUSED } , + { SMT_P_VERSION, sizeof(struct smt_p_version) , + SWAP_SMT_P_VERSION } , +#ifdef ESS + { SMT_P0015, sizeof(struct smt_p_0015) , SWAP_SMT_P0015 } , + { SMT_P0016, sizeof(struct smt_p_0016) , SWAP_SMT_P0016 } , + { SMT_P0017, sizeof(struct smt_p_0017) , SWAP_SMT_P0017 } , + { SMT_P0018, sizeof(struct smt_p_0018) , SWAP_SMT_P0018 } , + { SMT_P0019, sizeof(struct smt_p_0019) , SWAP_SMT_P0019 } , + { SMT_P001A, sizeof(struct smt_p_001a) , SWAP_SMT_P001A } , + { SMT_P001B, sizeof(struct smt_p_001b) , SWAP_SMT_P001B } , + { SMT_P001C, sizeof(struct smt_p_001c) , SWAP_SMT_P001C } , + { SMT_P001D, sizeof(struct smt_p_001d) , SWAP_SMT_P001D } , +#endif +#if 0 + { SMT_P_FSC, sizeof(struct smt_p_fsc) , + SWAP_SMT_P_FSC } , +#endif + + { SMT_P_SETCOUNT,0, SWAP_SMT_P_SETCOUNT } , + { SMT_P1048, 0, SWAP_SMT_P1048 } , + { SMT_P208C, 0, SWAP_SMT_P208C } , + { SMT_P208D, 0, SWAP_SMT_P208D } , + { SMT_P208E, 0, SWAP_SMT_P208E } , + { SMT_P208F, 0, SWAP_SMT_P208F } , + { SMT_P2090, 0, SWAP_SMT_P2090 } , +#ifdef ESS + { SMT_P320B, sizeof(struct smt_p_320b) , SWAP_SMT_P320B } , + { SMT_P320F, sizeof(struct smt_p_320f) , SWAP_SMT_P320F } , + { SMT_P3210, sizeof(struct smt_p_3210) , SWAP_SMT_P3210 } , +#endif + { SMT_P4050, 0, SWAP_SMT_P4050 } , + { SMT_P4051, 0, SWAP_SMT_P4051 } , + { SMT_P4052, 0, SWAP_SMT_P4052 } , + { SMT_P4053, 0, SWAP_SMT_P4053 } , +} ; + +#define N_SMT_PLEN (sizeof(smt_pdef)/sizeof(smt_pdef[0])) + +int smt_check_para(smc,sm,list) +struct s_smc *smc ; +struct smt_header *sm ; +const u_short list[] ; +{ + const u_short *p = list ; + while (*p) { + if (!sm_to_para(smc,sm,(int) *p)) { + DB_SMT("SMT: smt_check_para - missing para %x\n",*p,0); + return(-1) ; + } + p++ ; + } + return(0) ; +} + +EXPORT_PMF void *sm_to_para(smc,sm,para) +struct s_smc *smc ; +struct smt_header *sm ; +int para ; +{ + char *p ; + int len ; + int plen ; + void *found = 0 ; + + SK_UNUSED(smc) ; + + len = sm->smt_len ; + p = (char *)(sm+1) ; /* pointer to info */ + while (len > 0 ) { + if (((struct smt_para *)p)->p_type == para) + found = (void *) p ; + plen = ((struct smt_para *)p)->p_len + PARA_LEN ; + p += plen ; + len -= plen ; + if (len < 0) { + DB_SMT("SMT : sm_to_para - length error %d\n",plen,0) ; + return(0) ; + } + if ((plen & 3) && (para != SMT_P_ECHODATA)) { + DB_SMT("SMT : sm_to_para - odd length %d\n",plen,0) ; + return(0) ; + } + if (found) + return(found) ; + } + return(0) ; +} + +int is_my_addr(smc,addr) +struct s_smc *smc ; +struct fddi_addr *addr ; +{ + return(*(short *)(&addr->a[0]) == + *(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[0]) + && *(short *)(&addr->a[2]) == + *(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[2]) + && *(short *)(&addr->a[4]) == + *(short *)(&smc->mib.m[MAC0].fddiMACSMTAddress.a[4])) ; +} + +int is_zero(addr) +struct fddi_addr *addr ; +{ + return(*(short *)(&addr->a[0]) == 0 && + *(short *)(&addr->a[2]) == 0 && + *(short *)(&addr->a[4]) == 0 ) ; +} + +int is_broadcast(addr) +struct fddi_addr *addr ; +{ + return(*(u_short *)(&addr->a[0]) == 0xffff && + *(u_short *)(&addr->a[2]) == 0xffff && + *(u_short *)(&addr->a[4]) == 0xffff ) ; +} + +int is_individual(addr) +struct fddi_addr *addr ; +{ + return(!(addr->a[0] & GROUP_ADDR)) ; +} + +int is_equal(addr1,addr2) +struct fddi_addr *addr1 ; +struct fddi_addr *addr2 ; +{ + return(*(u_short *)(&addr1->a[0]) == *(u_short *)(&addr2->a[0]) && + *(u_short *)(&addr1->a[2]) == *(u_short *)(&addr2->a[2]) && + *(u_short *)(&addr1->a[4]) == *(u_short *)(&addr2->a[4]) ) ; +} + + +#if 0 +/* + * send ANTC data test frame + */ +void fddi_send_antc(smc,dest) +struct s_smc *smc ; +struct fddi_addr *dest ; +{ + SK_UNUSED(smc) ; + SK_UNUSED(dest) ; +#if 0 + SMbuf *mb ; + struct smt_header *smt ; + int i ; + char *p ; + + mb = smt_get_mbuf() ; + mb->sm_len = 3000+12 ; + p = smtod(mb, char *) + 12 ; + for (i = 0 ; i < 3000 ; i++) + *p++ = 1 << (i&7) ; + + smt = smtod(mb, struct smt_header *) ; + smt->smt_dest = *dest ; + smt->smt_source = smc->mib.m[MAC0].fddiMACSMTAddress ; + smt_send_mbuf(smc,mb,FC_ASYNC_LLC) ; +#endif +} +#endif + +#ifdef DEBUG +#define hextoasc(x) "0123456789abcdef"[x] + +char *addr_to_string(addr) +struct fddi_addr *addr ; +{ + int i ; + static char string[6*3] = "****" ; + + for (i = 0 ; i < 6 ; i++) { + string[i*3] = hextoasc((addr->a[i]>>4)&0xf) ; + string[i*3+1] = hextoasc((addr->a[i])&0xf) ; + string[i*3+2] = ':' ; + } + string[5*3+2] = 0 ; + return(string) ; +} +#endif + +#ifdef AM29K +smt_ifconfig(argc,argv) +int argc ; +char *argv[] ; +{ + if (argc >= 2 && !strcmp(argv[0],"opt_bypass") && + !strcmp(argv[1],"yes")) { + smc->mib.fddiSMTBypassPresent = 1 ; + return(0) ; + } + return(amdfddi_config(0,argc,argv)) ; +} +#endif + +/* + * return static mac index + */ +static int mac_index(smc,mac) +struct s_smc *smc ; +int mac ; +{ + SK_UNUSED(mac) ; +#ifdef CONCENTRATOR + SK_UNUSED(smc) ; + return(NUMPHYS+1) ; +#else + return((smc->s.sas == SMT_SAS) ? 2 : 3) ; +#endif +} + +/* + * return static phy index + */ +static int phy_index(smc,phy) +struct s_smc *smc ; +int phy ; +{ + SK_UNUSED(smc) ; + return(phy+1); +} + +/* + * return dynamic mac connection resource index + */ +static int mac_con_resource_index(smc,mac) +struct s_smc *smc ; +int mac ; +{ +#ifdef CONCENTRATOR + SK_UNUSED(smc) ; + SK_UNUSED(mac) ; + return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_MAC))) ; +#else + SK_UNUSED(mac) ; + switch (smc->mib.fddiSMTCF_State) { + case SC9_C_WRAP_A : + case SC5_THRU_B : + case SC11_C_WRAP_S : + return(1) ; + case SC10_C_WRAP_B : + case SC4_THRU_A : + return(2) ; + } + return(smc->s.sas == SMT_SAS ? 2 : 3) ; +#endif +} + +/* + * return dynamic phy connection resource index + */ +static int phy_con_resource_index(smc,phy) +struct s_smc *smc ; +int phy ; +{ +#ifdef CONCENTRATOR + return(entity_to_index(smc,cem_get_downstream(smc,ENTITY_PHY(phy)))) ; +#else + switch (smc->mib.fddiSMTCF_State) { + case SC9_C_WRAP_A : + return(phy == PA ? 3 : 2) ; + case SC10_C_WRAP_B : + return(phy == PA ? 1 : 3) ; + case SC4_THRU_A : + return(phy == PA ? 3 : 1) ; + case SC5_THRU_B : + return(phy == PA ? 2 : 3) ; + case SC11_C_WRAP_S : + return(2) ; + } + return(phy) ; +#endif +} + +#ifdef CONCENTRATOR +static int entity_to_index(smc,e) +struct s_smc *smc ; +int e ; +{ + if (e == ENTITY_MAC) + return(mac_index(smc,1)) ; + else + return(phy_index(smc,e - ENTITY_PHY(0))) ; +} +#endif + +#ifdef LITTLE_ENDIAN +static int smt_swap_short(s) +u_short s ; +{ + return(((s>>8)&0xff)|((s&0xff)<<8)) ; +} + +void smt_swap_para(sm,len,direction) +struct smt_header *sm ; +int len ; +int direction ; /* 0 encode 1 decode */ +{ + struct smt_para *pa ; + const struct smt_pdef *pd ; + char *p ; + int plen ; + int type ; + int i ; + +/* printf("smt_swap_para sm %x len %d dir %d\n", + sm,len,direction) ; + */ + smt_string_swap((char *)sm,SWAP_SMTHEADER,len) ; + + /* swap args */ + len -= sizeof(struct smt_header) ; + + p = (char *) (sm + 1) ; + while (len > 0) { + pa = (struct smt_para *) p ; + plen = pa->p_len ; + type = pa->p_type ; + pa->p_type = smt_swap_short(pa->p_type) ; + pa->p_len = smt_swap_short(pa->p_len) ; + if (direction) { + plen = pa->p_len ; + type = pa->p_type ; + } + /* + * note: paras can have 0 length ! + */ + if (plen < 0) + break ; + plen += PARA_LEN ; + for (i = N_SMT_PLEN, pd = smt_pdef; i ; i--,pd++) { + if (pd->ptype == type) + break ; + } + if (i && pd->pswap) { + smt_string_swap(p+PARA_LEN,pd->pswap,len) ; + } + len -= plen ; + p += plen ; + } +} + +static void smt_string_swap(data,format,len) +char *data ; +const char *format ; +int len ; +{ + const char *open_paren = 0 ; + int x ; + + while (len > 0 && *format) { + switch (*format) { + case '[' : + open_paren = format ; + break ; + case ']' : + format = open_paren ; + break ; + case '1' : + case '2' : + case '3' : + case '4' : + case '5' : + case '6' : + case '7' : + case '8' : + case '9' : + data += *format - '0' ; + len -= *format - '0' ; + break ; + case 'c': + data++ ; + len-- ; + break ; + case 's' : + x = data[0] ; + data[0] = data[1] ; + data[1] = x ; + data += 2 ; + len -= 2 ; + break ; + case 'l' : + x = data[0] ; + data[0] = data[3] ; + data[3] = x ; + x = data[1] ; + data[1] = data[2] ; + data[2] = x ; + data += 4 ; + len -= 4 ; + break ; + } + format++ ; + } +} +#else +void smt_swap_para(sm,len,direction) +struct smt_header *sm ; +int len ; +int direction ; /* 0 encode 1 decode */ +{ + SK_UNUSED(sm) ; + SK_UNUSED(len) ; + SK_UNUSED(direction) ; +} +#endif + +/* + * PMF actions + */ +int smt_action(smc,class,code,index) +struct s_smc *smc ; +int class ; +int code ; +int index ; +{ + int event ; + int port ; + DB_SMT("SMT: action %d code %d\n",class,code) ; + switch(class) { + case SMT_STATION_ACTION : + switch(code) { + case SMT_STATION_ACTION_CONNECT : + smc->mib.fddiSMTRemoteDisconnectFlag = FALSE ; + queue_event(smc,EVENT_ECM,EC_CONNECT) ; + break ; + case SMT_STATION_ACTION_DISCONNECT : + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + smc->mib.fddiSMTRemoteDisconnectFlag = TRUE ; + RS_SET(smc,RS_DISCONNECT) ; + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_DISCONNECT, + smt_get_event_word(smc)); + break ; + case SMT_STATION_ACTION_PATHTEST : + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_SMT_EVENT, (u_long) FDDI_PATH_TEST, + smt_get_event_word(smc)); + break ; + case SMT_STATION_ACTION_SELFTEST : + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_SELF_TEST, + smt_get_event_word(smc)); + break ; + case SMT_STATION_ACTION_DISABLE_A : + if (smc->y[PA].pc_mode == PM_PEER) { + RS_SET(smc,RS_EVENT) ; + queue_event(smc,EVENT_PCM+PA,PC_DISABLE) ; + } + break ; + case SMT_STATION_ACTION_DISABLE_B : + if (smc->y[PB].pc_mode == PM_PEER) { + RS_SET(smc,RS_EVENT) ; + queue_event(smc,EVENT_PCM+PB,PC_DISABLE) ; + } + break ; + case SMT_STATION_ACTION_DISABLE_M : + for (port = 0 ; port < NUMPHYS ; port++) { + if (smc->mib.p[port].fddiPORTMy_Type != TM) + continue ; + RS_SET(smc,RS_EVENT) ; + queue_event(smc,EVENT_PCM+port,PC_DISABLE) ; + } + break ; + default : + return(1) ; + } + break ; + case SMT_PORT_ACTION : + switch(code) { + case SMT_PORT_ACTION_ENABLE : + event = PC_ENABLE ; + break ; + case SMT_PORT_ACTION_DISABLE : + event = PC_DISABLE ; + break ; + case SMT_PORT_ACTION_MAINT : + event = PC_MAINT ; + break ; + case SMT_PORT_ACTION_START : + event = PC_START ; + break ; + case SMT_PORT_ACTION_STOP : + event = PC_STOP ; + break ; + default : + return(1) ; + } + queue_event(smc,EVENT_PCM+index,event) ; + break ; + default : + return(1) ; + } + return(0) ; +} + +/* + * change tneg + * set T_Req in MIB (Path Attribute) + * calculate new values for MAC + * if change required + * disconnect + * set reconnect + * end + */ +void smt_change_t_neg(smc,tneg) +struct s_smc *smc ; +u_long tneg ; +{ + smc->mib.a[PATH0].fddiPATHMaxT_Req = tneg ; + + if (smt_set_mac_opvalues(smc)) { + RS_SET(smc,RS_EVENT) ; + smc->sm.please_reconnect = 1 ; + queue_event(smc,EVENT_ECM,EC_DISCONNECT) ; + } +} + +/* + * canonical conversion of bytes beginning form *data + */ +#ifdef USE_CAN_ADDR +void hwm_conv_can(smc,data,len) +struct s_smc *smc ; +char *data ; +int len ; +{ + int i ; + + SK_UNUSED(smc) ; + + for (i = len; i ; i--, data++) { + *data = canonical[*(u_char *)data] ; + } +} +#endif + +#endif /* no SLIM_SMT */ diff --git a/drivers/net/skfp/smtdef.c b/drivers/net/skfp/smtdef.c new file mode 100644 index 000000000000..221b0334ce70 --- /dev/null +++ b/drivers/net/skfp/smtdef.c @@ -0,0 +1,371 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT/CMT defaults +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#ifndef OEM_USER_DATA +#define OEM_USER_DATA "SK-NET FDDI V2.0 Userdata" +#endif + +#ifndef lint +static const char ID_sccs[] = "@(#)smtdef.c 2.53 99/08/11 (C) SK " ; +#endif + +/* + * defaults + */ +#define TTMS(x) ((u_long)(x)*1000L) +#define TTS(x) ((u_long)(x)*1000000L) +#define TTUS(x) ((u_long)(x)) + +#define DEFAULT_TB_MIN TTMS(5) +#define DEFAULT_TB_MAX TTMS(50) +#define DEFAULT_C_MIN TTUS(1600) +#define DEFAULT_T_OUT TTMS(100+5) +#define DEFAULT_TL_MIN TTUS(30) +#define DEFAULT_LC_SHORT TTMS(50+5) +#define DEFAULT_LC_MEDIUM TTMS(500+20) +#define DEFAULT_LC_LONG TTS(5)+TTMS(50) +#define DEFAULT_LC_EXTENDED TTS(50)+TTMS(50) +#define DEFAULT_T_NEXT_9 TTMS(200+10) +#define DEFAULT_NS_MAX TTUS(1310) +#define DEFAULT_I_MAX TTMS(25) +#define DEFAULT_IN_MAX TTMS(40) +#define DEFAULT_TD_MIN TTMS(5) +#define DEFAULT_T_NON_OP TTS(1) +#define DEFAULT_T_STUCK TTS(8) +#define DEFAULT_T_DIRECT TTMS(370) +#define DEFAULT_T_JAM TTMS(370) +#define DEFAULT_T_ANNOUNCE TTMS(2500) +#define DEFAULT_D_MAX TTUS(1617) +#define DEFAULT_LEM_ALARM (8) +#define DEFAULT_LEM_CUTOFF (7) +#define DEFAULT_TEST_DONE TTS(1) +#define DEFAULT_CHECK_POLL TTS(1) +#define DEFAULT_POLL TTMS(50) + +/* + * LCT errors threshold + */ +#define DEFAULT_LCT_SHORT 1 +#define DEFAULT_LCT_MEDIUM 3 +#define DEFAULT_LCT_LONG 5 +#define DEFAULT_LCT_EXTEND 50 + +/* Forward declarations */ +extern void smt_reset_defaults (); +static void smt_init_mib (); + +static int set_min_max() ; + +void smt_set_defaults(smc) +struct s_smc *smc ; +{ + smt_reset_defaults(smc,0) ; +} + +#define MS2BCLK(x) ((x)*12500L) +#define US2BCLK(x) ((x)*1250L) + +void smt_reset_defaults(smc,level) +struct s_smc *smc ; +int level ; +{ + struct smt_config *smt ; + int i ; + u_long smt_boot_time; + + + smt_init_mib(smc,level) ; + + smc->os.smc_version = SMC_VERSION ; + smt_boot_time = smt_get_time(); + for( i = 0; i < NUMMACS; i++ ) + smc->sm.last_tok_time[i] = smt_boot_time ; + smt = &smc->s ; + smt->attach_s = 0 ; + smt->build_ring_map = 1 ; + smt->sas = SMT_DAS ; + smt->numphys = NUMPHYS ; + smt->pcm_tb_min = DEFAULT_TB_MIN ; + smt->pcm_tb_max = DEFAULT_TB_MAX ; + smt->pcm_c_min = DEFAULT_C_MIN ; + smt->pcm_t_out = DEFAULT_T_OUT ; + smt->pcm_tl_min = DEFAULT_TL_MIN ; + smt->pcm_lc_short = DEFAULT_LC_SHORT ; + smt->pcm_lc_medium = DEFAULT_LC_MEDIUM ; + smt->pcm_lc_long = DEFAULT_LC_LONG ; + smt->pcm_lc_extended = DEFAULT_LC_EXTENDED ; + smt->pcm_t_next_9 = DEFAULT_T_NEXT_9 ; + smt->pcm_ns_max = DEFAULT_NS_MAX ; + smt->ecm_i_max = DEFAULT_I_MAX ; + smt->ecm_in_max = DEFAULT_IN_MAX ; + smt->ecm_td_min = DEFAULT_TD_MIN ; + smt->ecm_test_done = DEFAULT_TEST_DONE ; + smt->ecm_check_poll = DEFAULT_CHECK_POLL ; + smt->rmt_t_non_op = DEFAULT_T_NON_OP ; + smt->rmt_t_stuck = DEFAULT_T_STUCK ; + smt->rmt_t_direct = DEFAULT_T_DIRECT ; + smt->rmt_t_jam = DEFAULT_T_JAM ; + smt->rmt_t_announce = DEFAULT_T_ANNOUNCE ; + smt->rmt_t_poll = DEFAULT_POLL ; + smt->rmt_dup_mac_behavior = FALSE ; /* See Struct smt_config */ + smt->mac_d_max = DEFAULT_D_MAX ; + + smt->lct_short = DEFAULT_LCT_SHORT ; + smt->lct_medium = DEFAULT_LCT_MEDIUM ; + smt->lct_long = DEFAULT_LCT_LONG ; + smt->lct_extended = DEFAULT_LCT_EXTEND ; + +#ifndef SLIM_SMT +#ifdef ESS + if (level == 0) { + smc->ess.sync_bw_available = FALSE ; + smc->mib.fddiESSPayload = 0 ; + smc->mib.fddiESSOverhead = 0 ; + smc->mib.fddiESSMaxTNeg = (u_long)(- MS2BCLK(25)) ; + smc->mib.fddiESSMinSegmentSize = 1 ; + smc->mib.fddiESSCategory = SB_STATIC ; + smc->mib.fddiESSSynchTxMode = FALSE ; + smc->ess.raf_act_timer_poll = FALSE ; + smc->ess.timer_count = 7 ; /* first RAF alc req after 3s */ + } + smc->ess.local_sba_active = FALSE ; + smc->ess.sba_reply_pend = NULL ; +#endif +#ifdef SBA + smt_init_sba(smc,level) ; +#endif +#endif /* no SLIM_SMT */ +#ifdef TAG_MODE + if (level == 0) { + smc->hw.pci_fix_value = 0 ; + } +#endif +} + +/* + * manufacturer data + */ +static const char man_data[32] = +/* 01234567890123456789012345678901 */ + "xxxSK-NET FDDI SMT 7.3 - V2.8.8" ; + +static void smt_init_mib(smc,level) +struct s_smc *smc ; +int level ; +{ + struct fddi_mib *mib ; + struct fddi_mib_p *pm ; + int port ; + int path ; + + mib = &smc->mib ; + if (level == 0) { + /* + * set EVERYTHING to ZERO + * EXCEPT hw and os + */ + memset(((char *)smc)+ + sizeof(struct s_smt_os)+sizeof(struct s_smt_hw), 0, + sizeof(struct s_smc) - + sizeof(struct s_smt_os) - sizeof(struct s_smt_hw)) ; + } + else { + mib->fddiSMTRemoteDisconnectFlag = 0 ; + mib->fddiSMTPeerWrapFlag = 0 ; + } + + mib->fddiSMTOpVersionId = 2 ; + mib->fddiSMTHiVersionId = 2 ; + mib->fddiSMTLoVersionId = 2 ; + memcpy((char *) mib->fddiSMTManufacturerData,man_data,32) ; + if (level == 0) { + strcpy(mib->fddiSMTUserData,OEM_USER_DATA) ; + } + mib->fddiSMTMIBVersionId = 1 ; + mib->fddiSMTMac_Ct = NUMMACS ; + mib->fddiSMTConnectionPolicy = POLICY_MM | POLICY_AA | POLICY_BB ; + + /* + * fddiSMTNonMaster_Ct and fddiSMTMaster_Ct are set in smt_fixup_mib + * s.sas is not set yet (is set in init driver) + */ + mib->fddiSMTAvailablePaths = MIB_PATH_P | MIB_PATH_S ; + + mib->fddiSMTConfigCapabilities = 0 ; /* no hold,no wrap_ab*/ + mib->fddiSMTTT_Notify = 10 ; + mib->fddiSMTStatRptPolicy = TRUE ; + mib->fddiSMTTrace_MaxExpiration = SEC2MIB(7) ; + mib->fddiSMTMACIndexes = INDEX_MAC ; + mib->fddiSMTStationStatus = MIB_SMT_STASTA_SEPA ; /* seperated */ + + mib->m[MAC0].fddiMACIndex = INDEX_MAC ; + mib->m[MAC0].fddiMACFrameStatusFunctions = FSC_TYPE0 ; + mib->m[MAC0].fddiMACRequestedPaths = + MIB_P_PATH_LOCAL | + MIB_P_PATH_SEC_ALTER | + MIB_P_PATH_PRIM_ALTER ; + mib->m[MAC0].fddiMACAvailablePaths = MIB_PATH_P ; + mib->m[MAC0].fddiMACCurrentPath = MIB_PATH_PRIMARY ; + mib->m[MAC0].fddiMACT_MaxCapabilitiy = (u_long)(- MS2BCLK(165)) ; + mib->m[MAC0].fddiMACTVXCapabilitiy = (u_long)(- US2BCLK(52)) ; + if (level == 0) { + mib->m[MAC0].fddiMACTvxValue = (u_long)(- US2BCLK(27)) ; + mib->m[MAC0].fddiMACTvxValueMIB = (u_long)(- US2BCLK(27)) ; + mib->m[MAC0].fddiMACT_Req = (u_long)(- MS2BCLK(165)) ; + mib->m[MAC0].fddiMACT_ReqMIB = (u_long)(- MS2BCLK(165)) ; + mib->m[MAC0].fddiMACT_Max = (u_long)(- MS2BCLK(165)) ; + mib->m[MAC0].fddiMACT_MaxMIB = (u_long)(- MS2BCLK(165)) ; + mib->m[MAC0].fddiMACT_Min = (u_long)(- MS2BCLK(4)) ; + } + mib->m[MAC0].fddiMACHardwarePresent = TRUE ; + mib->m[MAC0].fddiMACMA_UnitdataEnable = TRUE ; + mib->m[MAC0].fddiMACFrameErrorThreshold = 1 ; + mib->m[MAC0].fddiMACNotCopiedThreshold = 1 ; + /* + * Path attributes + */ + for (path = 0 ; path < NUMPATHS ; path++) { + mib->a[path].fddiPATHIndex = INDEX_PATH + path ; + if (level == 0) { + mib->a[path].fddiPATHTVXLowerBound = + (u_long)(- US2BCLK(27)) ; + mib->a[path].fddiPATHT_MaxLowerBound = + (u_long)(- MS2BCLK(165)) ; + mib->a[path].fddiPATHMaxT_Req = + (u_long)(- MS2BCLK(165)) ; + } + } + + + /* + * Port attributes + */ + pm = mib->p ; + for (port = 0 ; port < NUMPHYS ; port++) { + /* + * set MIB pointer in phy + */ + /* Attention: don't initialize mib pointer here! */ + /* It must be initialized during phase 2 */ + smc->y[port].mib = 0 ; + mib->fddiSMTPORTIndexes[port] = port+INDEX_PORT ; + + pm->fddiPORTIndex = port+INDEX_PORT ; + pm->fddiPORTHardwarePresent = TRUE ; + if (level == 0) { + pm->fddiPORTLer_Alarm = DEFAULT_LEM_ALARM ; + pm->fddiPORTLer_Cutoff = DEFAULT_LEM_CUTOFF ; + } + /* + * fddiPORTRequestedPaths are set in pcmplc.c + * we don't know the port type yet ! + */ + pm->fddiPORTRequestedPaths[1] = 0 ; + pm->fddiPORTRequestedPaths[2] = 0 ; + pm->fddiPORTRequestedPaths[3] = 0 ; + pm->fddiPORTAvailablePaths = MIB_PATH_P ; + pm->fddiPORTPMDClass = MIB_PMDCLASS_MULTI ; + pm++ ; + } + + (void) smt_set_mac_opvalues(smc) ; +} + +int smt_set_mac_opvalues(smc) +struct s_smc *smc ; +{ + int st ; + int st2 ; + + st = set_min_max(1,smc->mib.m[MAC0].fddiMACTvxValueMIB, + smc->mib.a[PATH0].fddiPATHTVXLowerBound, + &smc->mib.m[MAC0].fddiMACTvxValue) ; + st |= set_min_max(0,smc->mib.m[MAC0].fddiMACT_MaxMIB, + smc->mib.a[PATH0].fddiPATHT_MaxLowerBound, + &smc->mib.m[MAC0].fddiMACT_Max) ; + st |= (st2 = set_min_max(0,smc->mib.m[MAC0].fddiMACT_ReqMIB, + smc->mib.a[PATH0].fddiPATHMaxT_Req, + &smc->mib.m[MAC0].fddiMACT_Req)) ; + if (st2) { + /* Treq attribute changed remotely. So send an AIX_EVENT to the + * user + */ + AIX_EVENT(smc, (u_long) FDDI_RING_STATUS, (u_long) + FDDI_SMT_EVENT, (u_long) FDDI_REMOTE_T_REQ, + smt_get_event_word(smc)); + } + return(st) ; +} + +void smt_fixup_mib(smc) +struct s_smc *smc ; +{ +#ifdef CONCENTRATOR + switch (smc->s.sas) { + case SMT_SAS : + smc->mib.fddiSMTNonMaster_Ct = 1 ; + break ; + case SMT_DAS : + smc->mib.fddiSMTNonMaster_Ct = 2 ; + break ; + case SMT_NAC : + smc->mib.fddiSMTNonMaster_Ct = 0 ; + break ; + } + smc->mib.fddiSMTMaster_Ct = NUMPHYS - smc->mib.fddiSMTNonMaster_Ct ; +#else + switch (smc->s.sas) { + case SMT_SAS : + smc->mib.fddiSMTNonMaster_Ct = 1 ; + break ; + case SMT_DAS : + smc->mib.fddiSMTNonMaster_Ct = 2 ; + break ; + } + smc->mib.fddiSMTMaster_Ct = 0 ; +#endif +} + +/* + * determine new setting for operational value + * if limit is lower than mib + * use limit + * else + * use mib + * NOTE : numbers are negative, negate comparison ! + */ +static int set_min_max(maxflag,mib,limit,oper) +int maxflag ; +u_long mib ; +u_long limit ; +u_long *oper ; +{ + u_long old ; + old = *oper ; + if ((limit > mib) ^ maxflag) + *oper = limit ; + else + *oper = mib ; + return(old != *oper) ; +} diff --git a/drivers/net/skfp/smtinit.c b/drivers/net/skfp/smtinit.c new file mode 100644 index 000000000000..c4b5af9b502b --- /dev/null +++ b/drivers/net/skfp/smtinit.c @@ -0,0 +1,126 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + Init SMT + call all module level initialization routines +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)smtinit.c 1.15 97/05/06 (C) SK " ; +#endif + +extern void init_fddi_driver() ; + +/* define global debug variable */ +#if defined(DEBUG) && !defined(DEBUG_BRD) +struct smt_debug debug; +#endif + +#ifndef MULT_OEM +#define OEMID(smc,i) oem_id[i] + extern u_char oem_id[] ; +#else /* MULT_OEM */ +#define OEMID(smc,i) smc->hw.oem_id->oi_mark[i] + extern struct s_oem_ids oem_ids[] ; +#endif /* MULT_OEM */ + +/* + * Set OEM specific values + * + * Can not be called in smt_reset_defaults, because it is not sure that + * the OEM ID is already defined. + */ +static void set_oem_spec_val(smc) +struct s_smc *smc ; +{ + struct fddi_mib *mib ; + + mib = &smc->mib ; + + /* + * set IBM specific values + */ + if (OEMID(smc,0) == 'I') { + mib->fddiSMTConnectionPolicy = POLICY_MM ; + } +} + +/* + * Init SMT + */ +int init_smt(smc,mac_addr) +struct s_smc *smc ; +u_char *mac_addr ; /* canonical address or NULL */ +{ + int p ; + +#if defined(DEBUG) && !defined(DEBUG_BRD) + debug.d_smt = 0 ; + debug.d_smtf = 0 ; + debug.d_rmt = 0 ; + debug.d_ecm = 0 ; + debug.d_pcm = 0 ; + debug.d_cfm = 0 ; + + debug.d_plc = 0 ; +#ifdef ESS + debug.d_ess = 0 ; +#endif +#ifdef SBA + debug.d_sba = 0 ; +#endif +#endif /* DEBUG && !DEBUG_BRD */ + + /* First initialize the ports mib->pointers */ + for ( p = 0; p < NUMPHYS; p ++ ) { + smc->y[p].mib = & smc->mib.p[p] ; + } + + set_oem_spec_val(smc) ; + (void) smt_set_mac_opvalues(smc) ; + init_fddi_driver(smc,mac_addr) ; /* HW driver */ + smt_fixup_mib(smc) ; /* update values that depend on s.sas */ + + ev_init(smc) ; /* event queue */ +#ifndef SLIM_SMT + smt_init_evc(smc) ; /* evcs in MIB */ +#endif /* no SLIM_SMT */ + smt_timer_init(smc) ; /* timer package */ + smt_agent_init(smc) ; /* SMT frame manager */ + + pcm_init(smc) ; /* PCM state machine */ + ecm_init(smc) ; /* ECM state machine */ + cfm_init(smc) ; /* CFM state machine */ + rmt_init(smc) ; /* RMT state machine */ + + for (p = 0 ; p < NUMPHYS ; p++) { + pcm(smc,p,0) ; /* PCM A state machine */ + } + ecm(smc,0) ; /* ECM state machine */ + cfm(smc,0) ; /* CFM state machine */ + rmt(smc,0) ; /* RMT state machine */ + + smt_agent_task(smc) ; /* NIF FSM etc */ + + PNMI_INIT(smc) ; /* PNMI initialization */ + + return(0) ; +} diff --git a/drivers/net/skfp/smtparse.c b/drivers/net/skfp/smtparse.c new file mode 100644 index 000000000000..0b4a2274d055 --- /dev/null +++ b/drivers/net/skfp/smtparse.c @@ -0,0 +1,475 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + + +/* + parser for SMT parameters +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smt_p.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)smtparse.c 1.12 98/10/06 (C) SK " ; +#endif + +#ifdef sun +#define _far +#endif + +/* + * convert to BCLK units + */ +#define MS2BCLK(x) ((x)*12500L) +#define US2BCLK(x) ((x/10)*125L) + +/* + * parameter table + */ +static struct s_ptab { + char *pt_name ; + u_short pt_num ; + u_short pt_type ; + u_long pt_min ; + u_long pt_max ; +} ptab[] = { + { "PMFPASSWD",0, 0 } , + { "USERDATA",1, 0 } , + { "LERCUTOFFA",2, 1, 4, 15 } , + { "LERCUTOFFB",3, 1, 4, 15 } , + { "LERALARMA",4, 1, 4, 15 } , + { "LERALARMB",5, 1, 4, 15 } , + { "TMAX",6, 1, 5, 165 } , + { "TMIN",7, 1, 5, 165 } , + { "TREQ",8, 1, 5, 165 } , + { "TVX",9, 1, 2500, 10000 } , +#ifdef ESS + { "SBAPAYLOAD",10, 1, 0, 1562 } , + { "SBAOVERHEAD",11, 1, 50, 5000 } , + { "MAXTNEG",12, 1, 5, 165 } , + { "MINSEGMENTSIZE",13, 1, 0, 4478 } , + { "SBACATEGORY",14, 1, 0, 0xffff } , + { "SYNCHTXMODE",15, 0 } , +#endif +#ifdef SBA + { "SBACOMMAND",16, 0 } , + { "SBAAVAILABLE",17, 1, 0, 100 } , +#endif + { 0 } +} ; + +/* Define maximum string size for values and keybuffer */ +#define MAX_VAL 40 + +/* + * local function declarations + */ +static u_long parse_num() ; +static int parse_word() ; + +#ifdef SIM +#define DB_MAIN(a,b,c) printf(a,b,c) +#else +#define DB_MAIN(a,b,c) +#endif + +/* + * BEGIN_MANUAL_ENTRY() + * + * int smt_parse_arg(struct s_smc *,char _far *keyword,int type, + char _far *value) + * + * parse SMT parameter + * *keyword + * pointer to keyword, must be \0, \n or \r terminated + * *value pointer to value, either char * or u_long * + * if char * + * pointer to value, must be \0, \n or \r terminated + * if u_long * + * contains binary value + * + * type 0: integer + * 1: string + * return + * 0 parameter parsed ok + * != 0 error + * NOTE: + * function can be called with DS != SS + * + * + * END_MANUAL_ENTRY() + */ +int smt_parse_arg(smc,keyword,type,value) +struct s_smc *smc ; +char _far *keyword ; +int type ; +char _far *value ; +{ + char keybuf[MAX_VAL+1]; + char valbuf[MAX_VAL+1]; + char c ; + char *p ; + char *v ; + char *d ; + u_long val = 0 ; + struct s_ptab *pt ; + int st ; + int i ; + + /* + * parse keyword + */ + if ((st = parse_word(keybuf,keyword))) + return(st) ; + /* + * parse value if given as string + */ + if (type == 1) { + if ((st = parse_word(valbuf,value))) + return(st) ; + } + /* + * search in table + */ + st = 0 ; + for (pt = ptab ; (v = pt->pt_name) ; pt++) { + for (p = keybuf ; (c = *p) ; p++,v++) { + if (c != *v) + break ; + } + if (!c && !*v) + break ; + } + if (!v) + return(-1) ; +#if 0 + printf("=>%s<==>%s<=\n",pt->pt_name,valbuf) ; +#endif + /* + * set value in MIB + */ + if (pt->pt_type) + val = parse_num(type,value,valbuf,pt->pt_min,pt->pt_max,1) ; + switch (pt->pt_num) { + case 0 : + v = valbuf ; + d = (char *) smc->mib.fddiPRPMFPasswd ; + for (i = 0 ; i < (signed)sizeof(smc->mib.fddiPRPMFPasswd) ; i++) + *d++ = *v++ ; + DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiPRPMFPasswd) ; + break ; + case 1 : + v = valbuf ; + d = (char *) smc->mib.fddiSMTUserData ; + for (i = 0 ; i < (signed)sizeof(smc->mib.fddiSMTUserData) ; i++) + *d++ = *v++ ; + DB_MAIN("SET %s = %s\n",pt->pt_name,smc->mib.fddiSMTUserData) ; + break ; + case 2 : + smc->mib.p[PA].fddiPORTLer_Cutoff = (u_char) val ; + DB_MAIN("SET %s = %d\n", + pt->pt_name,smc->mib.p[PA].fddiPORTLer_Cutoff) ; + break ; + case 3 : + smc->mib.p[PB].fddiPORTLer_Cutoff = (u_char) val ; + DB_MAIN("SET %s = %d\n", + pt->pt_name,smc->mib.p[PB].fddiPORTLer_Cutoff) ; + break ; + case 4 : + smc->mib.p[PA].fddiPORTLer_Alarm = (u_char) val ; + DB_MAIN("SET %s = %d\n", + pt->pt_name,smc->mib.p[PA].fddiPORTLer_Alarm) ; + break ; + case 5 : + smc->mib.p[PB].fddiPORTLer_Alarm = (u_char) val ; + DB_MAIN("SET %s = %d\n", + pt->pt_name,smc->mib.p[PB].fddiPORTLer_Alarm) ; + break ; + case 6 : /* TMAX */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.a[PATH0].fddiPATHT_MaxLowerBound = + (u_long) -MS2BCLK((long)val) ; + break ; + case 7 : /* TMIN */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.m[MAC0].fddiMACT_Min = + (u_long) -MS2BCLK((long)val) ; + break ; + case 8 : /* TREQ */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.a[PATH0].fddiPATHMaxT_Req = + (u_long) -MS2BCLK((long)val) ; + break ; + case 9 : /* TVX */ + DB_MAIN("SET %s = %d \n",pt->pt_name,val) ; + smc->mib.a[PATH0].fddiPATHTVXLowerBound = + (u_long) -US2BCLK((long)val) ; + break ; +#ifdef ESS + case 10 : /* SBAPAYLOAD */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + if (smc->mib.fddiESSPayload != val) { + smc->ess.raf_act_timer_poll = TRUE ; + smc->mib.fddiESSPayload = val ; + } + break ; + case 11 : /* SBAOVERHEAD */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.fddiESSOverhead = val ; + break ; + case 12 : /* MAXTNEG */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.fddiESSMaxTNeg = (u_long) -MS2BCLK((long)val) ; + break ; + case 13 : /* MINSEGMENTSIZE */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.fddiESSMinSegmentSize = val ; + break ; + case 14 : /* SBACATEGORY */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.fddiESSCategory = + (smc->mib.fddiESSCategory & 0xffff) | + ((u_long)(val << 16)) ; + break ; + case 15 : /* SYNCHTXMODE */ + /* do not use memcmp(valbuf,"ALL",3) because DS != SS */ + if (valbuf[0] == 'A' && valbuf[1] == 'L' && valbuf[2] == 'L') { + smc->mib.fddiESSSynchTxMode = TRUE ; + DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; + } + /* if (!memcmp(valbuf,"SPLIT",5)) { */ + if (valbuf[0] == 'S' && valbuf[1] == 'P' && valbuf[2] == 'L' && + valbuf[3] == 'I' && valbuf[4] == 'T') { + DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; + smc->mib.fddiESSSynchTxMode = FALSE ; + } + break ; +#endif +#ifdef SBA + case 16 : /* SBACOMMAND */ + /* if (!memcmp(valbuf,"START",5)) { */ + if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'A' && + valbuf[3] == 'R' && valbuf[4] == 'T') { + DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; + smc->mib.fddiSBACommand = SB_START ; + } + /* if (!memcmp(valbuf,"STOP",4)) { */ + if (valbuf[0] == 'S' && valbuf[1] == 'T' && valbuf[2] == 'O' && + valbuf[3] == 'P') { + DB_MAIN("SET %s = %s\n",pt->pt_name,valbuf) ; + smc->mib.fddiSBACommand = SB_STOP ; + } + break ; + case 17 : /* SBAAVAILABLE */ + DB_MAIN("SET %s = %d\n",pt->pt_name,val) ; + smc->mib.fddiSBAAvailable = (u_char) val ; + break ; +#endif + } + return(0) ; +} + +static int parse_word(buf,text) +char *buf ; +char _far *text ; +{ + char c ; + char *p ; + int p_len ; + int quote ; + int i ; + int ok ; + + /* + * skip leading white space + */ + p = buf ; + for (i = 0 ; i < MAX_VAL ; i++) + *p++ = 0 ; + p = buf ; + p_len = 0 ; + ok = 0 ; + while ( (c = *text++) && (c != '\n') && (c != '\r')) { + if ((c != ' ') && (c != '\t')) { + ok = 1 ; + break ; + } + } + if (!ok) + return(-1) ; + if (c == '"') { + quote = 1 ; + } + else { + quote = 0 ; + text-- ; + } + /* + * parse valbuf + */ + ok = 0 ; + while (!ok && p_len < MAX_VAL-1 && (c = *text++) && (c != '\n') + && (c != '\r')) { + switch (quote) { + case 0 : + if ((c == ' ') || (c == '\t') || (c == '=')) { + ok = 1 ; + break ; + } + *p++ = c ; + p_len++ ; + break ; + case 2 : + *p++ = c ; + p_len++ ; + quote = 1 ; + break ; + case 1 : + switch (c) { + case '"' : + ok = 1 ; + break ; + case '\\' : + quote = 2 ; + break ; + default : + *p++ = c ; + p_len++ ; + } + } + } + *p++ = 0 ; + for (p = buf ; (c = *p) ; p++) { + if (c >= 'a' && c <= 'z') + *p = c + 'A' - 'a' ; + } + return(0) ; +} + +static u_long parse_num(type,value,v,mn,mx,scale) +int type ; +char _far *value ; +char *v ; +u_long mn ; +u_long mx ; +int scale ; +{ + u_long x = 0 ; + char c ; + + if (type == 0) { /* integer */ + u_long _far *l ; + u_long u1 ; + + l = (u_long _far *) value ; + u1 = *l ; + /* + * if the value is negative take the lower limit + */ + if ((long)u1 < 0) { + if (- ((long)u1) > (long) mx) { + u1 = 0 ; + } + else { + u1 = (u_long) - ((long)u1) ; + } + } + x = u1 ; + } + else { /* string */ + int sign = 0 ; + + if (*v == '-') { + sign = 1 ; + } + while ((c = *v++) && (c >= '0') && (c <= '9')) { + x = x * 10 + c - '0' ; + } + if (scale == 10) { + x *= 10 ; + if (c == '.') { + if ((c = *v++) && (c >= '0') && (c <= '9')) { + x += c - '0' ; + } + } + } + if (sign) + x = (u_long) - ((long)x) ; + } + /* + * if the value is negative + * and the absolute value is outside the limits + * take the lower limit + * else + * take the absoute value + */ + if ((long)x < 0) { + if (- ((long)x) > (long) mx) { + x = 0 ; + } + else { + x = (u_long) - ((long)x) ; + } + } + if (x < mn) + return(mn) ; + else if (x > mx) + return(mx) ; + return(x) ; +} + +#if 0 +struct s_smc SMC ; +main() +{ + char *p ; + char *v ; + char buf[100] ; + int toggle = 0 ; + + while (gets(buf)) { + p = buf ; + while (*p && ((*p == ' ') || (*p == '\t'))) + p++ ; + + while (*p && ((*p != ' ') && (*p != '\t'))) + p++ ; + + v = p ; + while (*v && ((*v == ' ') || (*v == '\t'))) + v++ ; + if ((*v >= '0') && (*v <= '9')) { + toggle = !toggle ; + if (toggle) { + u_long l ; + l = atol(v) ; + smt_parse_arg(&SMC,buf,0,(char _far *)&l) ; + } + else + smt_parse_arg(&SMC,buf,1,(char _far *)p) ; + } + else { + smt_parse_arg(&SMC,buf,1,(char _far *)p) ; + } + } + exit(0) ; +} +#endif diff --git a/drivers/net/skfp/smttimer.c b/drivers/net/skfp/smttimer.c new file mode 100644 index 000000000000..9d7b71e265c3 --- /dev/null +++ b/drivers/net/skfp/smttimer.c @@ -0,0 +1,173 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT timer +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" + +#ifndef lint +static const char ID_sccs[] = "@(#)smttimer.c 2.4 97/08/04 (C) SK " ; +#endif + +/* + * external function declarations + */ +extern u_long hwt_read() ; +extern void hwt_stop() ; +extern void hwt_start() ; + +static void timer_done() ; + + +void smt_timer_init(smc) +struct s_smc *smc ; +{ + smc->t.st_queue = 0 ; + smc->t.st_fast.tm_active = FALSE ; + smc->t.st_fast.tm_next = 0 ; + hwt_init(smc) ; +} + +void smt_timer_stop(smc,timer) +struct s_smc *smc ; +struct smt_timer *timer ; +{ + struct smt_timer **prev ; + struct smt_timer *tm ; + + /* + * remove timer from queue + */ + timer->tm_active = FALSE ; + if (smc->t.st_queue == timer && !timer->tm_next) { + hwt_stop(smc) ; + } + for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { + if (tm == timer) { + *prev = tm->tm_next ; + if (tm->tm_next) { + tm->tm_next->tm_delta += tm->tm_delta ; + } + return ; + } + } +} + +void smt_timer_start(smc,timer,time,token) +struct s_smc *smc ; +struct smt_timer *timer ; +u_long time ; +u_long token ; +{ + struct smt_timer **prev ; + struct smt_timer *tm ; + u_long delta = 0 ; + + time /= 16 ; /* input is uS, clock ticks are 16uS */ + if (!time) + time = 1 ; + smt_timer_stop(smc,timer) ; + timer->tm_smc = smc ; + timer->tm_token = token ; + timer->tm_active = TRUE ; + if (!smc->t.st_queue) { + smc->t.st_queue = timer ; + timer->tm_next = 0 ; + timer->tm_delta = time ; + hwt_start(smc,time) ; + return ; + } + /* + * timer correction + */ + timer_done(smc,0) ; + + /* + * find position in queue + */ + delta = 0 ; + for (prev = &smc->t.st_queue ; (tm = *prev) ; prev = &tm->tm_next ) { + if (delta + tm->tm_delta > time) { + break ; + } + delta += tm->tm_delta ; + } + /* insert in queue */ + *prev = timer ; + timer->tm_next = tm ; + timer->tm_delta = time - delta ; + if (tm) + tm->tm_delta -= timer->tm_delta ; + /* + * start new with first + */ + hwt_start(smc,smc->t.st_queue->tm_delta) ; +} + +void smt_force_irq(smc) +struct s_smc *smc ; +{ + smt_timer_start(smc,&smc->t.st_fast,32L, EV_TOKEN(EVENT_SMT,SM_FAST)); +} + +void smt_timer_done(smc) +struct s_smc *smc ; +{ + timer_done(smc,1) ; +} + +static void timer_done(smc,restart) +struct s_smc *smc ; +int restart ; +{ + u_long delta ; + struct smt_timer *tm ; + struct smt_timer *next ; + struct smt_timer **last ; + int done = 0 ; + + delta = hwt_read(smc) ; + last = &smc->t.st_queue ; + tm = smc->t.st_queue ; + while (tm && !done) { + if (delta >= tm->tm_delta) { + tm->tm_active = FALSE ; + delta -= tm->tm_delta ; + last = &tm->tm_next ; + tm = tm->tm_next ; + } + else { + tm->tm_delta -= delta ; + delta = 0 ; + done = 1 ; + } + } + *last = 0 ; + next = smc->t.st_queue ; + smc->t.st_queue = tm ; + + for ( tm = next ; tm ; tm = next) { + next = tm->tm_next ; + timer_event(smc,tm->tm_token) ; + } + + if (restart && smc->t.st_queue) + hwt_start(smc,smc->t.st_queue->tm_delta) ; +} diff --git a/drivers/net/skfp/srf.c b/drivers/net/skfp/srf.c new file mode 100644 index 000000000000..d5f091c54d08 --- /dev/null +++ b/drivers/net/skfp/srf.c @@ -0,0 +1,441 @@ +/****************************************************************************** + * + * (C)Copyright 1998,1999 SysKonnect, + * a business unit of Schneider & Koch & Co. Datensysteme GmbH. + * + * See the file "skfddi.c" for further information. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * The information in this file is provided "AS IS" without warranty. + * + ******************************************************************************/ + +/* + SMT 7.2 Status Response Frame Implementation + SRF state machine and frame generation +*/ + +#include "h/types.h" +#include "h/fddi.h" +#include "h/smc.h" +#include "h/smt_p.h" + +#define KERNEL +#include "h/smtstate.h" + +#ifndef SLIM_SMT +#ifndef BOOT + +#ifndef lint +static const char ID_sccs[] = "@(#)srf.c 1.18 97/08/04 (C) SK " ; +#endif + + +/* + * function declarations + */ +static void clear_all_rep() ; +static void clear_reported() ; +static void smt_send_srf() ; +static struct s_srf_evc *smt_get_evc() ; + +#define MAX_EVCS (sizeof(smc->evcs)/sizeof(smc->evcs[0])) + +struct evc_init { + u_char code ; + u_char index ; + u_char n ; + u_short para ; +} ; + +static const struct evc_init evc_inits[] = { + { SMT_COND_SMT_PEER_WRAP, 0,1,SMT_P1048 } , + + { SMT_COND_MAC_DUP_ADDR, INDEX_MAC, NUMMACS,SMT_P208C } , + { SMT_COND_MAC_FRAME_ERROR, INDEX_MAC, NUMMACS,SMT_P208D } , + { SMT_COND_MAC_NOT_COPIED, INDEX_MAC, NUMMACS,SMT_P208E } , + { SMT_EVENT_MAC_NEIGHBOR_CHANGE, INDEX_MAC, NUMMACS,SMT_P208F } , + { SMT_EVENT_MAC_PATH_CHANGE, INDEX_MAC, NUMMACS,SMT_P2090 } , + + { SMT_COND_PORT_LER, INDEX_PORT,NUMPHYS,SMT_P4050 } , + { SMT_COND_PORT_EB_ERROR, INDEX_PORT,NUMPHYS,SMT_P4052 } , + { SMT_EVENT_PORT_CONNECTION, INDEX_PORT,NUMPHYS,SMT_P4051 } , + { SMT_EVENT_PORT_PATH_CHANGE, INDEX_PORT,NUMPHYS,SMT_P4053 } , +} ; + +#define MAX_INIT_EVC (sizeof(evc_inits)/sizeof(evc_inits[0])) + +void smt_init_evc(smc) +struct s_smc *smc ; +{ + struct s_srf_evc *evc ; + const struct evc_init *init ; + int i ; + int index ; + int offset ; + + static u_char fail_safe = FALSE ; + + memset((char *)smc->evcs,0,sizeof(smc->evcs)) ; + + evc = smc->evcs ; + init = evc_inits ; + + for (i = 0 ; (unsigned) i < MAX_INIT_EVC ; i++) { + for (index = 0 ; index < init->n ; index++) { + evc->evc_code = init->code ; + evc->evc_para = init->para ; + evc->evc_index = init->index + index ; +#ifndef DEBUG + evc->evc_multiple = &fail_safe ; + evc->evc_cond_state = &fail_safe ; +#endif + evc++ ; + } + init++ ; + } + + if ((unsigned) (evc - smc->evcs) > MAX_EVCS) { + SMT_PANIC(smc,SMT_E0127, SMT_E0127_MSG) ; + } + + /* + * conditions + */ + smc->evcs[0].evc_cond_state = &smc->mib.fddiSMTPeerWrapFlag ; + smc->evcs[1].evc_cond_state = + &smc->mib.m[MAC0].fddiMACDuplicateAddressCond ; + smc->evcs[2].evc_cond_state = + &smc->mib.m[MAC0].fddiMACFrameErrorFlag ; + smc->evcs[3].evc_cond_state = + &smc->mib.m[MAC0].fddiMACNotCopiedFlag ; + + /* + * events + */ + smc->evcs[4].evc_multiple = &smc->mib.m[MAC0].fddiMACMultiple_N ; + smc->evcs[5].evc_multiple = &smc->mib.m[MAC0].fddiMACMultiple_P ; + + offset = 6 ; + for (i = 0 ; i < NUMPHYS ; i++) { + /* + * conditions + */ + smc->evcs[offset + 0*NUMPHYS].evc_cond_state = + &smc->mib.p[i].fddiPORTLerFlag ; + smc->evcs[offset + 1*NUMPHYS].evc_cond_state = + &smc->mib.p[i].fddiPORTEB_Condition ; + + /* + * events + */ + smc->evcs[offset + 2*NUMPHYS].evc_multiple = + &smc->mib.p[i].fddiPORTMultiple_U ; + smc->evcs[offset + 3*NUMPHYS].evc_multiple = + &smc->mib.p[i].fddiPORTMultiple_P ; + offset++ ; + } +#ifdef DEBUG + for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) { + if (SMT_IS_CONDITION(evc->evc_code)) { + if (!evc->evc_cond_state) { + SMT_PANIC(smc,SMT_E0128, SMT_E0128_MSG) ; + } + evc->evc_multiple = &fail_safe ; + } + else { + if (!evc->evc_multiple) { + SMT_PANIC(smc,SMT_E0129, SMT_E0129_MSG) ; + } + evc->evc_cond_state = &fail_safe ; + } + } +#endif + smc->srf.TSR = smt_get_time() ; + smc->srf.sr_state = SR0_WAIT ; +} + +static struct s_srf_evc *smt_get_evc(smc,code,index) +struct s_smc *smc ; +int code ; +int index ; +{ + int i ; + struct s_srf_evc *evc ; + + for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) { + if (evc->evc_code == code && evc->evc_index == index) + return(evc) ; + } + return(0) ; +} + +#define THRESHOLD_2 (2*TICKS_PER_SECOND) +#define THRESHOLD_32 (32*TICKS_PER_SECOND) + +#ifdef DEBUG +static const char * const srf_names[] = { + "None","MACPathChangeEvent", "MACNeighborChangeEvent", + "PORTPathChangeEvent", "PORTUndesiredConnectionAttemptEvent", + "SMTPeerWrapCondition", "SMTHoldCondition", + "MACFrameErrorCondition", "MACDuplicateAddressCondition", + "MACNotCopiedCondition", "PORTEBErrorCondition", + "PORTLerCondition" +} ; +#endif + +void smt_srf_event(smc,code,index,cond) +struct s_smc *smc ; +int code ; +int index ; +int cond ; +{ + struct s_srf_evc *evc ; + int cond_asserted = 0 ; + int cond_deasserted = 0 ; + int event_occured = 0 ; + int tsr ; + int T_Limit = 2*TICKS_PER_SECOND ; + + if (code == SMT_COND_MAC_DUP_ADDR && cond) { + RS_SET(smc,RS_DUPADDR) ; + } + + if (code) { + DB_SMT("SRF: %s index %d\n",srf_names[code],index) ; + + if (!(evc = smt_get_evc(smc,code,index))) { + DB_SMT("SRF : smt_get_evc() failed\n",0,0) ; + return ; + } + /* + * ignore condition if no change + */ + if (SMT_IS_CONDITION(code)) { + if (*evc->evc_cond_state == cond) + return ; + } + + /* + * set transition time stamp + */ + smt_set_timestamp(smc,smc->mib.fddiSMTTransitionTimeStamp) ; + if (SMT_IS_CONDITION(code)) { + DB_SMT("SRF: condition is %s\n",cond ? "ON":"OFF",0) ; + if (cond) { + *evc->evc_cond_state = TRUE ; + evc->evc_rep_required = TRUE ; + smc->srf.any_report = TRUE ; + cond_asserted = TRUE ; + } + else { + *evc->evc_cond_state = FALSE ; + cond_deasserted = TRUE ; + } + } + else { + if (evc->evc_rep_required) { + *evc->evc_multiple = TRUE ; + } + else { + evc->evc_rep_required = TRUE ; + *evc->evc_multiple = FALSE ; + } + smc->srf.any_report = TRUE ; + event_occured = TRUE ; + } +#ifdef FDDI_MIB + snmp_srf_event(smc,evc) ; +#endif /* FDDI_MIB */ + } + tsr = smt_get_time() - smc->srf.TSR ; + + switch (smc->srf.sr_state) { + case SR0_WAIT : + /* SR01a */ + if (cond_asserted && tsr < T_Limit) { + smc->srf.SRThreshold = THRESHOLD_2 ; + smc->srf.sr_state = SR1_HOLDOFF ; + break ; + } + /* SR01b */ + if (cond_deasserted && tsr < T_Limit) { + smc->srf.sr_state = SR1_HOLDOFF ; + break ; + } + /* SR01c */ + if (event_occured && tsr < T_Limit) { + smc->srf.sr_state = SR1_HOLDOFF ; + break ; + } + /* SR00b */ + if (cond_asserted && tsr >= T_Limit) { + smc->srf.SRThreshold = THRESHOLD_2 ; + smc->srf.TSR = smt_get_time() ; + smt_send_srf(smc) ; + break ; + } + /* SR00c */ + if (cond_deasserted && tsr >= T_Limit) { + smc->srf.TSR = smt_get_time() ; + smt_send_srf(smc) ; + break ; + } + /* SR00d */ + if (event_occured && tsr >= T_Limit) { + smc->srf.TSR = smt_get_time() ; + smt_send_srf(smc) ; + break ; + } + /* SR00e */ + if (smc->srf.any_report && (u_long) tsr >= + smc->srf.SRThreshold) { + smc->srf.SRThreshold *= 2 ; + if (smc->srf.SRThreshold > THRESHOLD_32) + smc->srf.SRThreshold = THRESHOLD_32 ; + smc->srf.TSR = smt_get_time() ; + smt_send_srf(smc) ; + break ; + } + /* SR02 */ + if (!smc->mib.fddiSMTStatRptPolicy) { + smc->srf.sr_state = SR2_DISABLED ; + break ; + } + break ; + case SR1_HOLDOFF : + /* SR10b */ + if (tsr >= T_Limit) { + smc->srf.sr_state = SR0_WAIT ; + smc->srf.TSR = smt_get_time() ; + smt_send_srf(smc) ; + break ; + } + /* SR11a */ + if (cond_asserted) { + smc->srf.SRThreshold = THRESHOLD_2 ; + } + /* SR11b */ + /* SR11c */ + /* handled above */ + /* SR12 */ + if (!smc->mib.fddiSMTStatRptPolicy) { + smc->srf.sr_state = SR2_DISABLED ; + break ; + } + break ; + case SR2_DISABLED : + if (smc->mib.fddiSMTStatRptPolicy) { + smc->srf.sr_state = SR0_WAIT ; + smc->srf.TSR = smt_get_time() ; + smc->srf.SRThreshold = THRESHOLD_2 ; + clear_all_rep(smc) ; + break ; + } + break ; + } +} + +static void clear_all_rep(smc) +struct s_smc *smc ; +{ + struct s_srf_evc *evc ; + int i ; + + for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) { + evc->evc_rep_required = FALSE ; + if (SMT_IS_CONDITION(evc->evc_code)) + *evc->evc_cond_state = FALSE ; + } + smc->srf.any_report = FALSE ; +} + +static void clear_reported(smc) +struct s_smc *smc ; +{ + struct s_srf_evc *evc ; + int i ; + + smc->srf.any_report = FALSE ; + for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) { + if (SMT_IS_CONDITION(evc->evc_code)) { + if (*evc->evc_cond_state == FALSE) + evc->evc_rep_required = FALSE ; + else + smc->srf.any_report = TRUE ; + } + else { + evc->evc_rep_required = FALSE ; + *evc->evc_multiple = FALSE ; + } + } +} + +extern SMbuf *smt_build_frame() ; + +/* + * build and send SMT SRF frame + */ +static void smt_send_srf(smc) +struct s_smc *smc ; +{ + + struct smt_header *smt ; + struct s_srf_evc *evc ; + SK_LOC_DECL(struct s_pcon,pcon) ; + SMbuf *mb ; + int i ; + + static const struct fddi_addr SMT_SRF_DA = { + { 0x80, 0x01, 0x43, 0x00, 0x80, 0x08 } + } ; + + /* + * build SMT header + */ + if (!smc->r.sm_ma_avail) + return ; + if (!(mb = smt_build_frame(smc,SMT_SRF,SMT_ANNOUNCE,0))) + return ; + + RS_SET(smc,RS_SOFTERROR) ; + + smt = smtod(mb, struct smt_header *) ; + smt->smt_dest = SMT_SRF_DA ; /* DA == SRF multicast */ + + /* + * setup parameter status + */ + pcon.pc_len = SMT_MAX_INFO_LEN ; /* max para length */ + pcon.pc_err = 0 ; /* no error */ + pcon.pc_badset = 0 ; /* no bad set count */ + pcon.pc_p = (void *) (smt + 1) ; /* paras start here */ + + smt_add_para(smc,&pcon,(u_short) SMT_P1033,0,0) ; + smt_add_para(smc,&pcon,(u_short) SMT_P1034,0,0) ; + + for (i = 0, evc = smc->evcs ; (unsigned) i < MAX_EVCS ; i++, evc++) { + if (evc->evc_rep_required) { + smt_add_para(smc,&pcon,evc->evc_para, + (int)evc->evc_index,0) ; + } + } + smt->smt_len = SMT_MAX_INFO_LEN - pcon.pc_len ; + mb->sm_len = smt->smt_len + sizeof(struct smt_header) ; + + DB_SMT("SRF: sending SRF at %x, len %d \n",smt,mb->sm_len) ; + DB_SMT("SRF: state SR%d Threshold %d\n", + smc->srf.sr_state,smc->srf.SRThreshold/TICKS_PER_SECOND) ; +#ifdef DEBUG + dump_smt(smc,smt,"SRF Send") ; +#endif + smt_send_frame(smc,mb,FC_SMT_INFO,0) ; + clear_reported(smc) ; +} + +#endif /* no BOOT */ +#endif /* no SLIM_SMT */ diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c index c710f4cc600d..db67ff042284 100644 --- a/drivers/scsi/gdth.c +++ b/drivers/scsi/gdth.c @@ -20,9 +20,46 @@ * along with this kernel; if not, write to the Free Software * * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * * - * Tested with Linux 1.2.13, ..., 2.2.12 * + * Tested with Linux 1.2.13, ..., 2.2.16, ..., 2.4.0-test4 * * * * $Log: gdth.c,v $ + * Revision 1.42 2000/07/20 09:04:50 achim + * Small changes for kernel 2.4 + * + * Revision 1.41 2000/07/04 14:11:11 achim + * gdth_analyse_hdrive() added to rescan drives after online expansion + * + * Revision 1.40 2000/06/27 11:24:16 achim + * Changes Clustering, Screenservice + * + * Revision 1.39 2000/06/15 13:09:04 achim + * Changes for gdth_do_cmd() + * + * Revision 1.38 2000/06/15 12:08:43 achim + * Bugfix gdth_sync_event(), service SCREENSERVICE + * Data direction for command 0xc2 changed to DOU + * + * Revision 1.37 2000/05/25 13:50:10 achim + * New driver parameter virt_ctr added + * + * Revision 1.36 2000/05/04 08:50:46 achim + * Event buffer now in gdth_ha_str + * + * Revision 1.35 2000/03/03 10:44:08 achim + * New event_string only valid for the RP controller family + * + * Revision 1.34 2000/03/02 14:55:29 achim + * New mechanism for async. event handling implemented + * + * Revision 1.33 2000/02/21 15:37:37 achim + * Bugfix Alpha platform + DPMEM above 4GB + * + * Revision 1.32 2000/02/14 16:17:37 achim + * Bugfix sense_buffer[] + raw devices + * + * Revision 1.31 2000/02/10 10:29:00 achim + * Delete sense_buffer[0], if command OK + * * Revision 1.30 1999/11/02 13:42:39 achim * ARRAY_DRV_LIST2 implemented * Now 255 log. and 100 host drives supported @@ -143,7 +180,7 @@ * Initial revision * ************************************************************************/ -#ident "$Id: gdth.c,v 1.30 1999/11/02 13:42:39 achim Exp $" +#ident "$Id: gdth.c,v 1.42 2000/07/20 09:04:50 achim Exp $" /* All GDT Disk Array Controllers are fully supported by this driver. * This includes the PCI/EISA/ISA SCSI Disk Array Controllers and the @@ -170,10 +207,12 @@ * max_ids:x x - target ID count per channel (1..MAXID) * rescan:Y rescan all channels/IDs * rescan:N use all devices found until now + * virt_ctr:Y map every channel to a virtual controller + * virt_ctr:N use multi channel support * hdr_channel:x x - number of virtual bus for host drives * - * The default value is: "gdth=disable:N,reserve_mode:1,reverse_scan:N, - * max_ids:127,rescan:N,hdr_channel:0". + * The default values are: "gdth=disable:N,reserve_mode:1,reverse_scan:N, + * max_ids:127,rescan:N,virt_ctr:N,hdr_channel:0". * Here is another example: "gdth=reserve_list:0,1,2,0,0,1,3,0,rescan:Y". * * When loading the gdth driver as a module, the same options are available. @@ -183,7 +222,7 @@ * '1' in place of 'Y' and '0' in place of 'N'. * * Default: "modprobe gdth disable=0 reserve_mode=1 reverse_scan=0 - * max_ids=127 rescan=0 hdr_channel=0" + * max_ids=127 rescan=0 virt_ctr=0 hdr_channel=0" * The other example: "modprobe gdth reserve_list=0,1,2,0,0,1,3,0 rescan=1". */ @@ -204,6 +243,9 @@ #include #include #include +#ifdef GDTH_RTC +#include +#endif #if LINUX_VERSION_CODE >= 0x020100 #include #else @@ -213,7 +255,9 @@ #include #include #include -#if LINUX_VERSION_CODE >= 0x02015F +#if LINUX_VERSION_CODE >= 0x020322 +#include +#elif LINUX_VERSION_CODE >= 0x02015F #include #endif @@ -236,7 +280,7 @@ static void gdth_interrupt(int irq,void *dev_id,struct pt_regs *regs); static void gdth_interrupt(int irq,struct pt_regs *regs); #endif static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp); -static int gdth_async_event(int hanum,int service); +static int gdth_async_event(int hanum); static void gdth_log_event(gdth_evt_data *dvr, char *buffer); static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority); @@ -271,6 +315,7 @@ static int gdth_wait(int hanum,int index,ulong32 time); static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1, ulong32 p2,ulong32 p3); static int gdth_search_drives(int hanum); +static int gdth_analyse_hdrive(int hanum, ushort hdrive); static void *gdth_mmap(ulong paddr, ulong size); static void gdth_munmap(void *addr); @@ -289,8 +334,6 @@ void gdth_halt(void); #ifdef DEBUG_GDTH static unchar DebugState = DEBUG_GDTH; -extern int sys_syslog(int,char*,int); -#define LOGEN sys_syslog(7,NULL,0) #ifdef __SERIAL__ #define MAX_SERBUF 160 @@ -359,9 +402,9 @@ static int ser_printk(const char *fmt, ...) #define TRACE3(a) {if (DebugState!=0) {ser_printk a;}} #else /* !__SERIAL__ */ -#define TRACE(a) {if (DebugState==1) {LOGEN;printk a;}} -#define TRACE2(a) {if (DebugState==1 || DebugState==2) {LOGEN;printk a;}} -#define TRACE3(a) {if (DebugState!=0) {LOGEN;printk a;}} +#define TRACE(a) {if (DebugState==1) {printk a;}} +#define TRACE2(a) {if (DebugState==1 || DebugState==2) {printk a;}} +#define TRACE3(a) {if (DebugState!=0) {printk a;}} #endif #else /* !DEBUG */ @@ -441,7 +484,7 @@ static void gdth_munmap(void *addr) #define gdth_readl(addr) (ulong32)readl((ulong)(addr)) #define gdth_writeb(b,addr) writeb((b),(ulong)(addr)) #define gdth_writew(b,addr) writew((b),(ulong)(addr)) -#define gdth_writel(b,addr) writel((b),(ulong)(addr)) +#define gdth_writel(b,addr) writel((ulong32)(b),(ulong)(addr)) #else static void *gdth_mmap(ulong paddr, ulong size) @@ -457,7 +500,7 @@ static void gdth_munmap(void *addr) #define gdth_readl(addr) (ulong32)readl((ulong)(addr)) #define gdth_writeb(b,addr) writeb((b),(ulong)(addr)) #define gdth_writew(b,addr) writew((b),(ulong)(addr)) -#define gdth_writel(b,addr) writel((b),(ulong)(addr)) +#define gdth_writel(b,addr) writel((ulong32)(b),(ulong)(addr)) #endif @@ -493,17 +536,21 @@ static unchar gdth_direction_tab[0x100] = { DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, DUN,DUN,DUN,DUN,DUN,DNO,DNO,DUN,DIN,DNO,DOU,DUN,DNO,DUN,DOU,DOU, DOU,DOU,DOU,DNO,DUN,DIN,DOU,DIN,DIN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, - DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, + DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN, DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DOU,DUN,DUN,DUN,DUN,DUN, DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN,DUN }; /* __initfunc, __initdata macros */ -#if LINUX_VERSION_CODE >= 0x020126 +#if LINUX_VERSION_CODE >= 0x020322 +#define GDTH_INITFUNC(type, func) type __init func +#include +#elif LINUX_VERSION_CODE >= 0x020126 +#define GDTH_INITFUNC(type, func) __initfunc(type func) #include #else -#define __initfunc(A) A +#define GDTH_INITFUNC(type, func) type func #define __initdata #define __init #endif @@ -550,6 +597,8 @@ static int hdr_channel = 0; static int max_ids = MAXID; /* rescan all IDs */ static int rescan = 0; +/* map channels to virtual controllers */ +static int virt_ctr = 0; #ifdef MODULE #if LINUX_VERSION_CODE >= 0x02011A @@ -562,6 +611,7 @@ MODULE_PARM(reverse_scan, "i"); MODULE_PARM(hdr_channel, "i"); MODULE_PARM(max_ids, "i"); MODULE_PARM(rescan, "i"); +MODULE_PARM(virt_ctr, "i"); MODULE_AUTHOR("Achim Leubner"); #endif #endif @@ -569,10 +619,12 @@ MODULE_AUTHOR("Achim Leubner"); /* /proc support */ #if LINUX_VERSION_CODE >= 0x010300 #include +#if LINUX_VERSION_CODE < 0x020322 struct proc_dir_entry proc_scsi_gdth = { PROC_SCSI_GDTH, 4, "gdth", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; +#endif #include "gdth_proc.h" #include "gdth_proc.c" #endif @@ -621,7 +673,7 @@ static void gdth_eval_mapping(ulong32 size, int *cyls, int *heads, int *secs) /* controller search and initialization functions */ -__initfunc (static int gdth_search_eisa(ushort eisa_adr)) +GDTH_INITFUNC(static int, gdth_search_eisa(ushort eisa_adr)) { ulong32 id; @@ -639,7 +691,7 @@ __initfunc (static int gdth_search_eisa(ushort eisa_adr)) } -__initfunc (static int gdth_search_isa(ulong32 bios_adr)) +GDTH_INITFUNC(static int, gdth_search_isa(ulong32 bios_adr)) { void *addr; ulong32 id; @@ -655,9 +707,9 @@ __initfunc (static int gdth_search_isa(ulong32 bios_adr)) } -__initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) +GDTH_INITFUNC(static int, gdth_search_pci(gdth_pci_str *pcistr)) { - ulong32 base0, base1, base2; + ulong base0, base1, base2; ushort device_id, cnt; #if LINUX_VERSION_CODE >= 0x2015C struct pci_dev *pdev; @@ -674,7 +726,43 @@ __initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) if (device_id > PCI_DEVICE_ID_VORTEX_GDT6555 && device_id < PCI_DEVICE_ID_VORTEX_GDT6x17RP) continue; -#if LINUX_VERSION_CODE >= 0x2015C +#if LINUX_VERSION_CODE >= 0x20363 + pdev = NULL; + while ((pdev = pci_find_device(PCI_VENDOR_ID_VORTEX,device_id,pdev)) + != NULL) { + if (pci_enable_device(pdev)) + continue; + if (cnt >= MAXHA) + return cnt; + /* GDT PCI controller found, resources are already in pdev */ + pcistr[cnt].pdev = pdev; + pcistr[cnt].device_id = device_id; + pcistr[cnt].bus = pdev->bus->number; + pcistr[cnt].device_fn = pdev->devfn; + pcistr[cnt].irq = pdev->irq; + base0 = pci_resource_flags(pdev, 0); + base1 = pci_resource_flags(pdev, 1); + base2 = pci_resource_flags(pdev, 2); + if (device_id <= PCI_DEVICE_ID_VORTEX_GDT6000B || /* GDT6000/B */ + device_id >= PCI_DEVICE_ID_VORTEX_GDT6x17RP) { /* MPR */ + if (!(base0 & IORESOURCE_MEM)) + continue; + pcistr[cnt].dpmem = pci_resource_start(pdev, 0); + } else { /* GDT6110, GDT6120, .. */ + if (!(base0 & IORESOURCE_MEM) || + !(base2 & IORESOURCE_MEM) || + !(base1 & IORESOURCE_IO)) + continue; + pcistr[cnt].dpmem = pci_resource_start(pdev, 2); + pcistr[cnt].io_mm = pci_resource_start(pdev, 0); + pcistr[cnt].io = pci_resource_start(pdev, 1); + } + TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n", + pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), + pcistr[cnt].irq, pcistr[cnt].dpmem)); + cnt++; + } +#elif LINUX_VERSION_CODE >= 0x2015C pdev = NULL; while ((pdev = pci_find_device(PCI_VENDOR_ID_VORTEX,device_id,pdev)) != NULL) { @@ -695,7 +783,7 @@ __initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) PCI_BASE_ADDRESS_SPACE_MEMORY) continue; pcistr[cnt].dpmem = base0 & PCI_BASE_ADDRESS_MEM_MASK; - } else { /* GDT6110, GDT6120, .. */ + } else { /* GDT6110, GDT6120, .. */ if ((base0 & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY || (base2 & PCI_BASE_ADDRESS_SPACE) != @@ -707,9 +795,9 @@ __initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) pcistr[cnt].io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK; pcistr[cnt].io = base1 & PCI_BASE_ADDRESS_IO_MASK; } - TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n", + TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n", pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), - pcistr[cnt].irq, (int)pcistr[cnt].dpmem)); + pcistr[cnt].irq, pcistr[cnt].dpmem)); cnt++; } #else @@ -762,7 +850,7 @@ __initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) pcistr[cnt].io_mm = base0 & PCI_BASE_ADDRESS_MEM_MASK; pcistr[cnt].io = base1 & PCI_BASE_ADDRESS_IO_MASK; } - TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%x\n", + TRACE2(("Controller found at %d/%d, irq %d, dpmem 0x%lx\n", pcistr[cnt].bus, PCI_SLOT(pcistr[cnt].device_fn), pcistr[cnt].irq, pcistr[cnt].dpmem)); cnt++; @@ -773,7 +861,7 @@ __initfunc (static int gdth_search_pci(gdth_pci_str *pcistr)) } -__initfunc (static void gdth_sort_pci(gdth_pci_str *pcistr, int cnt)) +GDTH_INITFUNC(static void, gdth_sort_pci(gdth_pci_str *pcistr, int cnt)) { gdth_pci_str temp; int i, changed; @@ -811,7 +899,7 @@ __initfunc (static void gdth_sort_pci(gdth_pci_str *pcistr, int cnt)) } -__initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)) +GDTH_INITFUNC(static int, gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)) { ulong32 retries,id; unchar prot_ver,eisacf,i,irq_found; @@ -900,7 +988,7 @@ __initfunc (static int gdth_init_eisa(ushort eisa_adr,gdth_ha_str *ha)) } -__initfunc (static int gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)) +GDTH_INITFUNC(static int, gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)) { register gdt2_dpram_str *dp2_ptr; int i; @@ -997,7 +1085,7 @@ __initfunc (static int gdth_init_isa(ulong32 bios_adr,gdth_ha_str *ha)) } -__initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) +GDTH_INITFUNC(static int, gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) { register gdt6_dpram_str *dp6_ptr; register gdt6c_dpram_str *dp6c_ptr; @@ -1165,7 +1253,18 @@ __initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) } /* manipulate config. space to enable DPMEM, start RP controller */ -#if LINUX_VERSION_CODE >= 0x2015C +#if LINUX_VERSION_CODE >= 0x20363 + pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command); + command |= 6; + pci_write_config_word(pcistr->pdev, PCI_COMMAND, command); + if (pci_resource_start(pcistr->pdev, 8) == 1UL) + pci_resource_start(pcistr->pdev, 8) = 0UL; + i = 0xFEFF0001UL; + pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, i); + gdth_delay(1); + pci_write_config_dword(pcistr->pdev, PCI_ROM_ADDRESS, + pci_resource_start(pcistr->pdev, 8)); +#elif LINUX_VERSION_CODE >= 0x2015C pci_read_config_word(pcistr->pdev, PCI_COMMAND, &command); command |= 6; pci_write_config_word(pcistr->pdev, PCI_COMMAND, command); @@ -1193,13 +1292,13 @@ __initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) pcibios_write_config_dword(pcistr->bus, pcistr->device_fn, PCI_ROM_ADDRESS, rom_addr); #endif - + /* check and reset interface area */ dp6m_ptr = (gdt6m_dpram_str *)ha->brd; gdth_writel(DPMEM_MAGIC, &dp6m_ptr->u); if (gdth_readl(&dp6m_ptr->u) != DPMEM_MAGIC) { - printk("GDT-PCI: Cannot access DPMEM at 0x%x (shadowed?)\n", - (int)pcistr->dpmem); + printk("GDT-PCI: Cannot access DPMEM at 0x%lx (shadowed?)\n", + pcistr->dpmem); found = FALSE; for (i = 0xC8000; i < 0xE8000; i += 0x4000) { gdth_munmap(ha->brd); @@ -1298,7 +1397,7 @@ __initfunc (static int gdth_init_pci(gdth_pci_str *pcistr,gdth_ha_str *ha)) /* controller protocol functions */ -__initfunc (static void gdth_enable_int(int hanum)) +GDTH_INITFUNC(static void, gdth_enable_int(int hanum)) { gdth_ha_str *ha; ulong flags; @@ -1609,6 +1708,12 @@ static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1, cmd_ptr->u.raw.bus = (unchar)p2; cmd_ptr->u.raw.target = (unchar)p3; cmd_ptr->u.raw.lun = (unchar)(p3 >> 8); + } else if (service == SCREENSERVICE) { + if (opcode == GDT_REALTIME) { + *(ulong32 *)&cmd_ptr->u.screen.su.data[0] = p1; + *(ulong32 *)&cmd_ptr->u.screen.su.data[4] = p2; + *(ulong32 *)&cmd_ptr->u.screen.su.data[8] = p3; + } } ha->cmd_len = sizeof(gdth_cmd_str); ha->cmd_offs_dpmem = 0; @@ -1631,20 +1736,21 @@ static int gdth_internal_cmd(int hanum,unchar service,ushort opcode,ulong32 p1, /* search for devices */ -__initfunc (static int gdth_search_drives(int hanum)) +GDTH_INITFUNC(static int, gdth_search_drives(int hanum)) { register gdth_ha_str *ha; ushort cdev_cnt, i; - int drv_cyls, drv_hds, drv_secs; - ulong32 bus_no; - ulong32 drv_cnt, drv_no, j; + ulong32 bus_no, drv_cnt, drv_no, j; gdth_getch_str *chn; gdth_drlist_str *drl; gdth_iochan_str *ioc; gdth_raw_iochan_str *iocr; gdth_arcdl_str *alst; gdth_alist_str *alst2; - +#ifdef GDTH_RTC + unchar rtc[12]; +#endif + TRACE(("gdth_search_drives() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -1655,7 +1761,28 @@ __initfunc (static int gdth_search_drives(int hanum)) return 0; } TRACE2(("gdth_search_drives(): SCREENSERVICE initialized\n")); - + +#ifdef GDTH_RTC + /* read realtime clock info, send to controller */ + /* 1. wait for the falling edge of update flag */ + for (j = 0; j < 1000000; ++j) + if (CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP) + break; + for (j = 0; j < 1000000; ++j) + if (!(CMOS_READ(RTC_FREQ_SELECT) & RTC_UIP)) + break; + /* 2. read info */ + do { + for (j = 0; j < 12; ++j) + rtc[j] = CMOS_READ(j); + } while (rtc[0] != CMOS_READ(0)); + TRACE2(("gdth_search_drives(): RTC: %x/%x/%x\n",*(ulong32 *)&rtc[0], + *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8])); + /* 3. send to controller firmware */ + gdth_internal_cmd(hanum,SCREENSERVICE,GDT_REALTIME, *(ulong32 *)&rtc[0], + *(ulong32 *)&rtc[4], *(ulong32 *)&rtc[8]); +#endif + /* initialize cache service */ if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0)) { printk("GDT: Initialization error cache service (code %d)\n", @@ -1664,6 +1791,7 @@ __initfunc (static int gdth_search_drives(int hanum)) } TRACE2(("gdth_search_drives(): CACHESERVICE initialized\n")); cdev_cnt = (ushort)ha->info; + ha->fw_vers = ha->service; /* detect number of buses - try new IOCTL */ iocr = (gdth_raw_iochan_str *)ha->pscratch; @@ -1723,7 +1851,8 @@ __initfunc (static int gdth_search_drives(int hanum)) ha->more_proc = FALSE; if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_INFO, INVALID_CHANNEL,sizeof(gdth_binfo_str))) { - ha->binfo = *(gdth_binfo_str *)ha->pscratch; + memcpy(&ha->binfo, (gdth_binfo_str *)ha->pscratch, + sizeof(gdth_binfo_str)); if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL,BOARD_FEATURES, INVALID_CHANNEL,sizeof(gdth_bfeat_str))) { TRACE2(("BOARD_INFO/BOARD_FEATURES supported\n")); @@ -1798,14 +1927,14 @@ __initfunc (static int gdth_search_drives(int hanum)) } } } - alst = (gdth_arcdl_str *)ha->pscratch; - alst->entries_avail = MAX_LDRIVES; - alst->first_entry = 0; - alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]); + alst = (gdth_arcdl_str *)ha->pscratch; + alst->entries_avail = MAX_LDRIVES; + alst->first_entry = 0; + alst->list_offset = GDTOFFSOF(gdth_arcdl_str, list[0]); if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, ARRAY_DRV_LIST2 | LA_CTRL_PATTERN, INVALID_CHANNEL, sizeof(gdth_arcdl_str) + - (alst->entries_avail-1) * sizeof(gdth_alist_str))) { + (alst->entries_avail-1) * sizeof(gdth_alist_str))) { for (j = 0; j < alst->entries_init; ++j) { ha->hdr[j].is_arraydrv = alst->list[j].is_arrayd; ha->hdr[j].is_master = alst->list[j].is_master; @@ -1813,9 +1942,9 @@ __initfunc (static int gdth_search_drives(int hanum)) ha->hdr[j].is_hotfix = alst->list[j].is_hotfix; ha->hdr[j].master_no = alst->list[j].cd_handle; } - } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, - ARRAY_DRV_LIST | LA_CTRL_PATTERN, - 0, 35 * sizeof(gdth_alist_str))) { + } else if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_IOCTL, + ARRAY_DRV_LIST | LA_CTRL_PATTERN, + 0, 35 * sizeof(gdth_alist_str))) { for (j = 0; j < 35; ++j) { alst2 = &((gdth_alist_str *)ha->pscratch)[j]; ha->hdr[j].is_arraydrv = alst2->is_arrayd; @@ -1881,60 +2010,70 @@ __initfunc (static int gdth_search_drives(int hanum)) } } - /* scanning for cache devices */ - for (i=0; ihdr[i].present = TRUE; - ha->hdr[i].size = ha->info; - - /* evaluate mapping (sectors per head, heads per cylinder) */ - ha->hdr[i].size &= ~SECS32; - if (ha->info2 == 0) { - gdth_eval_mapping(ha->hdr[i].size,&drv_cyls,&drv_hds,&drv_secs); - } else { - drv_hds = ha->info2 & 0xff; - drv_secs = (ha->info2 >> 8) & 0xff; - drv_cyls = ha->hdr[i].size /drv_hds/drv_secs; - } - ha->hdr[i].heads = (unchar)drv_hds; - ha->hdr[i].secs = (unchar)drv_secs; - /* round size */ - ha->hdr[i].size = drv_cyls * drv_hds * drv_secs; - TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n", - i,ha->hdr[i].size,drv_hds,drv_secs)); - - /* get informations about device */ - if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,i, - 0,0)) { - TRACE2(("gdth_search_dr() cache drive %d devtype %d\n", - i,ha->info)); - ha->hdr[i].devtype = (ushort)ha->info; - } + /* scanning for host drives */ + for (i = 0; i < cdev_cnt; ++i) + gdth_analyse_hdrive(hanum,i); + + TRACE(("gdth_search_drives() OK\n")); + return 1; +} - /* cluster info */ - if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,i, - 0,0)) { - TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n", - i,ha->info)); - ha->hdr[i].cluster_type = (unchar)ha->info; - } else { - ha->hdr[i].cluster_type = 0; - } - - /* R/W attributes */ - if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,i, - 0,0)) { - TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n", - i,ha->info)); - ha->hdr[i].rw_attribs = (unchar)ha->info; - } else { - ha->hdr[i].rw_attribs = 0; - } - } +static int gdth_analyse_hdrive(int hanum,ushort hdrive) +{ + register gdth_ha_str *ha; + int drv_cyls, drv_hds, drv_secs; + + TRACE(("gdth_analyse_hdrive() hanum %d drive %d\n",hanum,hdrive)); + if (hdrive >= MAX_HDRIVES) + return 0; + ha = HADATA(gdth_ctr_tab[hanum]); + + if (!gdth_internal_cmd(hanum,CACHESERVICE,GDT_INFO,hdrive,0,0)) + return 0; + ha->hdr[hdrive].present = TRUE; + ha->hdr[hdrive].size = ha->info; + + /* evaluate mapping (sectors per head, heads per cylinder) */ + ha->hdr[hdrive].size &= ~SECS32; + if (ha->info2 == 0) { + gdth_eval_mapping(ha->hdr[hdrive].size,&drv_cyls,&drv_hds,&drv_secs); + } else { + drv_hds = ha->info2 & 0xff; + drv_secs = (ha->info2 >> 8) & 0xff; + drv_cyls = ha->hdr[hdrive].size /drv_hds/drv_secs; + } + ha->hdr[hdrive].heads = (unchar)drv_hds; + ha->hdr[hdrive].secs = (unchar)drv_secs; + /* round size */ + ha->hdr[hdrive].size = drv_cyls * drv_hds * drv_secs; + TRACE2(("gdth_search_dr() cdr. %d size %d hds %d scs %d\n", + hdrive,ha->hdr[hdrive].size,drv_hds,drv_secs)); + + /* get informations about device */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_DEVTYPE,hdrive,0,0)) { + TRACE2(("gdth_search_dr() cache drive %d devtype %d\n", + hdrive,ha->info)); + ha->hdr[hdrive].devtype = (ushort)ha->info; } - TRACE(("gdth_search_drives() OK\n")); + /* cluster info */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_CLUST_INFO,hdrive,0,0)) { + TRACE2(("gdth_search_dr() cache drive %d cluster info %d\n", + hdrive,ha->info)); + ha->hdr[hdrive].cluster_type = (unchar)ha->info; + } else { + ha->hdr[hdrive].cluster_type = 0; + } + + /* R/W attributes */ + if (gdth_internal_cmd(hanum,CACHESERVICE,GDT_RW_ATTRIBS,hdrive,0,0)) { + TRACE2(("gdth_search_dr() cache drive %d r/w attrib. %d\n", + hdrive,ha->info)); + ha->hdr[hdrive].rw_attribs = (unchar)ha->info; + } else { + ha->hdr[hdrive].rw_attribs = 0; + } + return 1; } @@ -1954,11 +2093,7 @@ static void gdth_putq(int hanum,Scsi_Cmnd *scp,unchar priority) GDTH_LOCK_HA(ha, flags); scp->SCp.this_residual = (int)priority; -#if LINUX_VERSION_CODE >= 0x020000 - b = scp->channel; -#else - b = NUMDATA(nscp->host)->busnum; -#endif + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; t = scp->target; #if LINUX_VERSION_CODE >= 0x010300 if (priority >= DEFAULT_PRI) { @@ -2019,11 +2154,7 @@ static void gdth_next(int hanum) for (nscp = pscp = ha->req_first; nscp; nscp = (Scsi_Cmnd *)nscp->SCp.ptr) { if (nscp != pscp && nscp != (Scsi_Cmnd *)pscp->SCp.ptr) pscp = (Scsi_Cmnd *)pscp->SCp.ptr; -#if LINUX_VERSION_CODE >= 0x020000 - b = nscp->channel; -#else - b = NUMDATA(nscp->host)->busnum; -#endif + b = virt_ctr ? NUMDATA(nscp->host)->busnum : nscp->channel; t = nscp->target; if (nscp->SCp.this_residual >= DEFAULT_PRI) { if ((b != ha->virt_bus && ha->raw[BUS_L2P(ha,b)].lock) || @@ -2085,21 +2216,8 @@ static void gdth_next(int hanum) if (b == ha->virt_bus && nscp->cmnd[0] != INQUIRY && nscp->cmnd[0] != READ_CAPACITY && nscp->cmnd[0] != MODE_SENSE && (ha->hdr[t].cluster_type & CLUSTER_DRIVE)) { - if (!(ha->hdr[t].cluster_type & CLUSTER_MOUNTED)) { - /* cluster drive NOT MOUNTED */ - if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) { - /* cluster drive NOT RESERVED */ - nscp->SCp.Status = GDT_MOUNT; - } else { - /* cluster drive RESERVED (on the other node) */ - nscp->SCp.Status = GDT_CLUST_INFO; - } - } else { - if (!(ha->hdr[t].cluster_type & CLUSTER_RESERVED)) { - /* cluster drive MOUNTED and not RESERVED */ - nscp->SCp.Status = GDT_CLUST_INFO; - } - } + /* always GDT_CLUST_INFO! */ + nscp->SCp.Status = GDT_CLUST_INFO; } } } @@ -2177,6 +2295,7 @@ static void gdth_next(int hanum) if ( (nscp->cmnd[4]&1) && !(ha->hdr[t].devtype&1) ) { TRACE(("Prevent r. nonremov. drive->do nothing\n")); nscp->result = DID_OK << 16; + nscp->sense_buffer[0] = 0; if (!nscp->SCp.have_data_in) nscp->SCp.have_data_in++; else { @@ -2353,6 +2472,7 @@ static int gdth_internal_cache_cmd(int hanum,Scsi_Cmnd *scp) break; } scp->result = DID_OK << 16; + scp->sense_buffer[0] = 0; if (!scp->SCp.have_data_in) scp->SCp.have_data_in++; @@ -2545,7 +2665,7 @@ static int gdth_fill_raw_cmd(int hanum,Scsi_Cmnd *scp,unchar b) cmdp->u.raw.sense_len = 16; cmdp->u.raw.sense_data = virt_to_bus(scp->sense_buffer); cmdp->u.raw.direction = - gdth_direction_tab[scp->cmnd[0]]==DOU ? DATA_OUT : DATA_IN; + gdth_direction_tab[scp->cmnd[0]]==DOU ? GDTH_DATA_OUT:GDTH_DATA_IN; memcpy(cmdp->u.raw.cmd,scp->cmnd,12); if (scp->use_sg) { @@ -2677,8 +2797,12 @@ static gdth_evt_str *gdth_store_event(gdth_ha_str *ha, ushort source, if (ebuffer[elastidx].event_source == source && ebuffer[elastidx].event_idx == idx && - !memcmp((char *)&ebuffer[elastidx].event_data.eu, - (char *)&evt->eu, evt->size)) { + ((evt->size != 0 && ebuffer[elastidx].event_data.size != 0 && + !memcmp((char *)&ebuffer[elastidx].event_data.eu, + (char *)&evt->eu, evt->size)) || + (evt->size == 0 && ebuffer[elastidx].event_data.size == 0 && + !strcmp((char *)&ebuffer[elastidx].event_data.event_string, + (char *)&evt->event_string)))) { e = &ebuffer[elastidx]; do_gettimeofday(&tv); e->last_stamp = tv.tv_sec; @@ -2791,11 +2915,9 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) gdt6_dpram_str *dp6_ptr; gdt2_dpram_str *dp2_ptr; Scsi_Cmnd *scp; - int hanum, rval; + int hanum, rval, i; unchar IStatus; - ushort CmdStatus, Service = 0; - ulong32 InfoBytes, InfoBytes2 = 0; - gdth_evt_data dvr; + ushort Service; ulong flags = 0; TRACE(("gdth_interrupt() IRQ %d\n",irq)); @@ -2813,9 +2935,7 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) /* search controller */ if ((hanum = gdth_get_status(&IStatus,irq)) == -1) { - /* - TRACE2(("gdth_interrupt(): Spurious interrupt received\n")); - */ + /* spurious interrupt */ if (!gdth_polling) GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags); return; @@ -2829,34 +2949,28 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) if (ha->type == GDT_EISA) { if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = inw(ha->bmic + MAILBOXREG+8); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); - if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = inw(ha->bmic + MAILBOXREG+10); - InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); - } + ha->status = inw(ha->bmic + MAILBOXREG+8); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else /* no error */ - CmdStatus = S_OK; - InfoBytes = inl(ha->bmic + MAILBOXREG+12); - if (gdth_polling) /* init. -> more info */ - InfoBytes2 = inl(ha->bmic + MAILBOXREG+4); + ha->status = S_OK; + ha->info = inl(ha->bmic + MAILBOXREG+12); + ha->service = inw(ha->bmic + MAILBOXREG+10); + ha->info2 = inl(ha->bmic + MAILBOXREG+4); + outb(0xff, ha->bmic + EDOORREG); /* acknowledge interrupt */ outb(0x00, ha->bmic + SEMA1REG); /* reset status semaphore */ } else if (ha->type == GDT_ISA) { dp2_ptr = (gdt2_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = gdth_readw(&dp2_ptr->u.ic.Status); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); - if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = gdth_readw(&dp2_ptr->u.ic.Service); - InfoBytes2 = gdth_readl(&dp2_ptr->u.ic.Info[1]); - } + ha->status = gdth_readw(&dp2_ptr->u.ic.Status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else /* no error */ - CmdStatus = S_OK; - InfoBytes = gdth_readl(&dp2_ptr->u.ic.Info[0]); - if (gdth_polling) /* init. -> more info */ - InfoBytes2 = gdth_readl(&dp2_ptr->u.ic.Info[1]); + ha->status = S_OK; + ha->info = gdth_readl(&dp2_ptr->u.ic.Info[0]); + ha->service = gdth_readw(&dp2_ptr->u.ic.Service); + ha->info2 = gdth_readl(&dp2_ptr->u.ic.Info[1]); + gdth_writeb(0xff, &dp2_ptr->io.irqdel); /* acknowledge interrupt */ gdth_writeb(0, &dp2_ptr->u.ic.Cmd_Index); /* reset command index */ gdth_writeb(0, &dp2_ptr->io.Sema1); /* reset status semaphore */ @@ -2864,52 +2978,56 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) dp6_ptr = (gdt6_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = gdth_readw(&dp6_ptr->u.ic.Status); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); - if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = gdth_readw(&dp6_ptr->u.ic.Service); - InfoBytes2 = gdth_readl(&dp6_ptr->u.ic.Info[1]); - } + ha->status = gdth_readw(&dp6_ptr->u.ic.Status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else /* no error */ - CmdStatus = S_OK; - InfoBytes = gdth_readl(&dp6_ptr->u.ic.Info[0]); - if (gdth_polling) /* init. -> more info */ - InfoBytes2 = gdth_readl(&dp6_ptr->u.ic.Info[1]); + ha->status = S_OK; + ha->info = gdth_readl(&dp6_ptr->u.ic.Info[0]); + ha->service = gdth_readw(&dp6_ptr->u.ic.Service); + ha->info2 = gdth_readl(&dp6_ptr->u.ic.Info[1]); + gdth_writeb(0xff, &dp6_ptr->io.irqdel); /* acknowledge interrupt */ gdth_writeb(0, &dp6_ptr->u.ic.Cmd_Index); /* reset command index */ gdth_writeb(0, &dp6_ptr->io.Sema1); /* reset status semaphore */ } else if (ha->type == GDT_PCINEW) { if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = inw(PTR2USHORT(&ha->plx->status)); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); - if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = inw(PTR2USHORT(&ha->plx->service)); - InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); - } + ha->status = inw(PTR2USHORT(&ha->plx->status)); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else - CmdStatus = S_OK; + ha->status = S_OK; + ha->info = inl(PTR2USHORT(&ha->plx->info[0])); + ha->service = inw(PTR2USHORT(&ha->plx->service)); + ha->info2 = inl(PTR2USHORT(&ha->plx->info[1])); - InfoBytes = inl(PTR2USHORT(&ha->plx->info[0])); - if (gdth_polling) /* init. -> more info */ - InfoBytes2 = inl(PTR2USHORT(&ha->plx->info[1])); outb(0xff, PTR2USHORT(&ha->plx->edoor_reg)); outb(0x00, PTR2USHORT(&ha->plx->sema1_reg)); } else if (ha->type == GDT_PCIMPR) { dp6m_ptr = (gdt6m_dpram_str *)ha->brd; if (IStatus & 0x80) { /* error flag */ IStatus &= ~0x80; - CmdStatus = gdth_readw(&dp6m_ptr->i960r.status); - TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,CmdStatus)); - if (IStatus == ASYNCINDEX) { /* async. event ? */ - Service = gdth_readw(&dp6m_ptr->i960r.service); - InfoBytes2 = gdth_readl(&dp6m_ptr->i960r.info[1]); - } + ha->status = gdth_readw(&dp6m_ptr->i960r.status); + TRACE2(("gdth_interrupt() error %d/%d\n",IStatus,ha->status)); } else /* no error */ - CmdStatus = S_OK; - InfoBytes = gdth_readl(&dp6m_ptr->i960r.info[0]); - if (gdth_polling) /* init. -> more info */ - InfoBytes2 = gdth_readl(&dp6m_ptr->i960r.info[1]); + ha->status = S_OK; + ha->info = gdth_readl(&dp6m_ptr->i960r.info[0]); + ha->service = gdth_readw(&dp6m_ptr->i960r.service); + ha->info2 = gdth_readl(&dp6m_ptr->i960r.info[1]); + + /* event string */ + if (IStatus == ASYNCINDEX) { + if (ha->service != SCREENSERVICE && + (ha->fw_vers & 0xff) >= 0x1a) { + ha->dvr.severity = + gdth_readb(&((gdt6m_dpram_str *)ha->brd)->i960r.severity); + for (i = 0; i < 256; ++i) { + ha->dvr.event_string[i] = gdth_readb + (&((gdt6m_dpram_str *)ha->brd)->i960r.evt_str[i]); + if (ha->dvr.event_string[i] == 0) + break; + } + } + } gdth_writeb(0xff, &dp6m_ptr->i960r.edoor_reg); gdth_writeb(0, &dp6m_ptr->i960r.sema1_reg); } else { @@ -2920,10 +3038,7 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) } TRACE(("gdth_interrupt() index %d stat %d info %d\n", - IStatus,CmdStatus,InfoBytes)); - ha->status = CmdStatus; - ha->info = InfoBytes; - ha->info2 = InfoBytes2; + IStatus,ha->status,ha->info)); if (gdth_from_wait) { wait_hanum = hanum; @@ -2932,7 +3047,7 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) if (IStatus == ASYNCINDEX) { TRACE2(("gdth_interrupt() async. event\n")); - gdth_async_event(hanum,Service); + gdth_async_event(hanum); if (!gdth_polling) GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags); gdth_next(hanum); @@ -2941,9 +3056,9 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) if (IStatus == SPEZINDEX) { TRACE2(("Service unknown or not initialized !\n")); - dvr.size = sizeof(dvr.eu.driver); - dvr.eu.driver.ionode = hanum; - gdth_store_event(ha, ES_DRIVER, 4, &dvr); + ha->dvr.size = sizeof(ha->dvr.eu.driver); + ha->dvr.eu.driver.ionode = hanum; + gdth_store_event(ha, ES_DRIVER, 4, &ha->dvr); if (!gdth_polling) GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags); return; @@ -2953,10 +3068,10 @@ static void gdth_interrupt(int irq,struct pt_regs *regs) ha->cmd_tab[IStatus-2].cmnd = UNUSED_CMND; if (scp == UNUSED_CMND) { TRACE2(("gdth_interrupt() index to unused command (%d)\n",IStatus)); - dvr.size = sizeof(dvr.eu.driver); - dvr.eu.driver.ionode = hanum; - dvr.eu.driver.index = IStatus; - gdth_store_event(ha, ES_DRIVER, 1, &dvr); + ha->dvr.size = sizeof(ha->dvr.eu.driver); + ha->dvr.eu.driver.ionode = hanum; + ha->dvr.eu.driver.index = IStatus; + gdth_store_event(ha, ES_DRIVER, 1, &ha->dvr); if (!gdth_polling) GDTH_UNLOCK_HA((gdth_ha_str *)dev_id,flags); return; @@ -2987,9 +3102,7 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) register gdth_ha_str *ha; gdth_msg_str *msg; gdth_cmd_str *cmdp; - char c='\r'; - ushort i; - gdth_evt_data dvr; + unchar b; ha = HADATA(gdth_ctr_tab[hanum]); cmdp = ha->pccb; @@ -3017,11 +3130,11 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) cmdp->OpCode = GDT_READ; cmdp->BoardNode = LOCALBOARD; cmdp->u.screen.reserved = 0; - cmdp->u.screen.msg_handle= msg->msg_handle; - cmdp->u.screen.msg_addr = virt_to_bus(msg); + cmdp->u.screen.su.msg.msg_handle= msg->msg_handle; + cmdp->u.screen.su.msg.msg_addr = virt_to_bus(msg); ha->scratch_busy = TRUE; ha->cmd_offs_dpmem = 0; - ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) + sizeof(ulong32); ha->cmd_cnt = 0; gdth_copy_command(hanum); @@ -3030,22 +3143,19 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) } if (msg->msg_answer && msg->msg_alen) { - for (i=0; imsg_alen && imsg_text[i] = c; - } - msg->msg_alen -= i; - if (c!='\r' && msg->msg_alen!=0) { - msg->msg_answer = 1; - msg->msg_ext = 1; + /* default answers (getchar() not possible) */ + if (msg->msg_alen == 1) { + msg->msg_alen = 0; + msg->msg_len = 1; + msg->msg_text[0] = 0; } else { - msg->msg_ext = 0; - msg->msg_answer = 0; + msg->msg_alen -= 2; + msg->msg_len = 2; + msg->msg_text[0] = 1; + msg->msg_text[1] = 0; } - msg->msg_len = i; + msg->msg_ext = 0; + msg->msg_answer = 0; while (gdth_test_busy(hanum)) gdth_delay(0); cmdp->Service = SCREENSERVICE; @@ -3055,11 +3165,11 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) cmdp->OpCode = GDT_WRITE; cmdp->BoardNode = LOCALBOARD; cmdp->u.screen.reserved = 0; - cmdp->u.screen.msg_handle= msg->msg_handle; - cmdp->u.screen.msg_addr = virt_to_bus(msg); + cmdp->u.screen.su.msg.msg_handle= msg->msg_handle; + cmdp->u.screen.su.msg.msg_addr = virt_to_bus(msg); ha->scratch_busy = TRUE; ha->cmd_offs_dpmem = 0; - ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) + sizeof(ulong32); ha->cmd_cnt = 0; gdth_copy_command(hanum); @@ -3069,8 +3179,9 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) printk("\n"); } else { - if (scp->SCp.Status == -1 && scp->channel != ha->virt_bus) { - ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[scp->target]--; + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; + if (scp->SCp.Status == -1 && b != ha->virt_bus) { + ha->raw[BUS_L2P(ha,b)].io_cnt[scp->target]--; } /* cache or raw service */ if (ha->status == S_OK) { @@ -3107,6 +3218,7 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) scp->sense_buffer[0] = 0x70; scp->sense_buffer[2] = UNIT_ATTENTION; scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); + scp->SCp.Message = (int)(ha->info<<16|S_BSY); } else { scp->SCp.Status = -1; /* retry */ @@ -3121,6 +3233,7 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED; } scp->result = DID_OK << 16; + scp->sense_buffer[0] = 0; } } else if (ha->status == S_BSY) { TRACE2(("Controller busy -> retry !\n")); @@ -3131,9 +3244,6 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) return 2; } else { scp->SCp.Message = (int)((ha->info<<16)|ha->status); - memset((char*)scp->sense_buffer,0,16); - scp->sense_buffer[0] = 0x70; - scp->sense_buffer[2] = NOT_READY; if (scp->SCp.Status != -1) { TRACE2(("gdth_sync_event(): special cmd 0x%x error 0x%x\n", @@ -3145,34 +3255,44 @@ static int gdth_sync_event(int hanum,int service,unchar index,Scsi_Cmnd *scp) scp->SCp.this_residual = HIGH_PRI; return 2; } + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); } else if (scp->cmnd[0] == RESERVE || scp->cmnd[0] == RELEASE) { + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); } else if (service == CACHESERVICE) { - if (ha->status == S_CACHE_UNKNOWN && - (ha->hdr[scp->target].cluster_type & - CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) { - /* bus reset -> force GDT_CLUST_INFO */ - ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED; - } + if (ha->status == S_CACHE_UNKNOWN && + (ha->hdr[scp->target].cluster_type & + CLUSTER_RESERVE_STATE) == CLUSTER_RESERVE_STATE) { + /* bus reset -> force GDT_CLUST_INFO */ + ha->hdr[scp->target].cluster_type &= ~CLUSTER_RESERVED; + } + memset((char*)scp->sense_buffer,0,16); + scp->sense_buffer[0] = 0x70; + scp->sense_buffer[2] = NOT_READY; scp->result = (DID_OK << 16) | (CHECK_CONDITION << 1); #if LINUX_VERSION_CODE >= 0x010300 if (scp->done != gdth_scsi_done) #endif { - dvr.size = sizeof(dvr.eu.sync); - dvr.eu.sync.ionode = hanum; - dvr.eu.sync.service = service; - dvr.eu.sync.status = ha->status; - dvr.eu.sync.info = ha->info; - dvr.eu.sync.hostdrive = scp->target; + ha->dvr.size = sizeof(ha->dvr.eu.sync); + ha->dvr.eu.sync.ionode = hanum; + ha->dvr.eu.sync.service = service; + ha->dvr.eu.sync.status = ha->status; + ha->dvr.eu.sync.info = ha->info; + ha->dvr.eu.sync.hostdrive = scp->target; if (ha->status >= 0x8000) - gdth_store_event(ha, ES_SYNC, 0, &dvr); + gdth_store_event(ha, ES_SYNC, 0, &ha->dvr); else - gdth_store_event(ha, ES_SYNC, service, &dvr); + gdth_store_event(ha, ES_SYNC, service, &ha->dvr); } } else { + /* sense buffer filled from controller firmware (DMA) */ if (ha->status!=S_RAW_SCSI || ha->info>=0x100) { scp->result = DID_BAD_TARGET << 16; } else { @@ -3336,12 +3456,17 @@ static char *async_cache_tab[] = { "GDT HA %u, Uncorrectable DRAM error detected with ECC", /*72*/ "\011\000\002\012\001\013\001\014\001" "GDT HA %u, SCSI bus %u, ID %u, LUN %u: reassigning block", +/*73*/ "\005\000\002\006\002" + "GDT HA %u, Host drive %u resetted locally", +/*74*/ "\005\000\002\006\002" + "GDT HA %u, Host drive %u resetted remotely", +/*75*/ "\003\000\002" + "GDT HA %u, async. status 75 unknown", }; -static int gdth_async_event(int hanum,int service) +static int gdth_async_event(int hanum) { - gdth_evt_data dvr; gdth_ha_str *ha; gdth_msg_str *msg; gdth_cmd_str *cmdp; @@ -3351,9 +3476,9 @@ static int gdth_async_event(int hanum,int service) cmdp= ha->pccb; msg = (gdth_msg_str *)ha->pscratch; TRACE2(("gdth_async_event() ha %d serv %d\n", - hanum,service)); + hanum,ha->service)); - if (service == SCREENSERVICE) { + if (ha->service == SCREENSERVICE) { if (ha->status == MSG_REQUEST) { while (gdth_test_busy(hanum)) gdth_delay(0); @@ -3364,11 +3489,11 @@ static int gdth_async_event(int hanum,int service) cmdp->OpCode = GDT_READ; cmdp->BoardNode = LOCALBOARD; cmdp->u.screen.reserved = 0; - cmdp->u.screen.msg_handle= MSG_INV_HANDLE; - cmdp->u.screen.msg_addr = virt_to_bus(msg); + cmdp->u.screen.su.msg.msg_handle= MSG_INV_HANDLE; + cmdp->u.screen.su.msg.msg_addr = virt_to_bus(msg); ha->scratch_busy = TRUE; ha->cmd_offs_dpmem = 0; - ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.msg_addr) + ha->cmd_len = GDTOFFSOF(gdth_cmd_str,u.screen.su.msg.msg_addr) + sizeof(ulong32); ha->cmd_cnt = 0; gdth_copy_command(hanum); @@ -3383,14 +3508,29 @@ static int gdth_async_event(int hanum,int service) } } else { - dvr.size = sizeof(dvr.eu.async); - dvr.eu.async.ionode = hanum; - dvr.eu.async.service = service; - dvr.eu.async.status = ha->status; - dvr.eu.async.info = ha->info; - *(ulong32 *)dvr.eu.async.scsi_coord = ha->info2; - gdth_store_event(ha, ES_ASYNC, service, &dvr); - gdth_log_event( &dvr, NULL ); + if (ha->type == GDT_PCIMPR && + (ha->fw_vers & 0xff) >= 0x1a) { + ha->dvr.size = 0; + ha->dvr.eu.async.ionode = hanum; + ha->dvr.eu.async.status = ha->status; + /* severity and event_string already set! */ + } else { + ha->dvr.size = sizeof(ha->dvr.eu.async); + ha->dvr.eu.async.ionode = hanum; + ha->dvr.eu.async.service = ha->service; + ha->dvr.eu.async.status = ha->status; + ha->dvr.eu.async.info = ha->info; + *(ulong32 *)ha->dvr.eu.async.scsi_coord = ha->info2; + } + gdth_store_event( ha, ES_ASYNC, ha->service, &ha->dvr ); + gdth_log_event( &ha->dvr, NULL ); + + /* new host drive from expand? */ + if (ha->service == CACHESERVICE && ha->status == 56) { + TRACE2(("gdth_async_event(): new host drive %d created\n", + (ushort)ha->info)); + gdth_analyse_hdrive(hanum, (ushort)ha->info); + } } return 1; } @@ -3402,7 +3542,14 @@ static void gdth_log_event(gdth_evt_data *dvr, char *buffer) int i,j; TRACE2(("gdth_log_event()\n")); - if (dvr->eu.async.service == CACHESERVICE && + if (dvr->size == 0) { + if (buffer == NULL) { + printk("Adapter %d: %s\n",dvr->eu.async.ionode,dvr->event_string); + } else { + sprintf(buffer,"Adapter %d: %s\n", + dvr->eu.async.ionode,dvr->event_string); + } + } else if (dvr->eu.async.service == CACHESERVICE && INDEX_OK(dvr->eu.async.status, async_cache_tab)) { TRACE2(("GDT: Async. event cache service, event no.: %d\n", dvr->eu.async.status)); @@ -3474,7 +3621,7 @@ void gdth_timeout(ulong data) #endif -__initfunc (int gdth_detect(Scsi_Host_Template *shtp)) +GDTH_INITFUNC(int, gdth_detect(Scsi_Host_Template *shtp)) { struct Scsi_Host *shp; gdth_ha_str *ha; @@ -3522,6 +3669,9 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) scsi_unregister(shp); continue; } +#ifdef __ia64__ + break; +#else /* controller found and initialized */ printk("Configuring GDT-ISA HA at BIOS 0x%05X IRQ %u DRQ %u\n", isa_bios,ha->irq,ha->drq); @@ -3559,7 +3709,12 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) NUMDATA(shp)->busnum= 0; ha->pccb = CMDDATA(shp); +#if LINUX_VERSION_CODE >= 0x020322 + ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, + GDTH_SCRATCH_ORD); +#else ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA); +#endif ha->scratch_busy = FALSE; ha->req_first = NULL; ha->tid_cnt = MAX_HDRIVES; @@ -3574,7 +3729,11 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) --gdth_ctr_count; --gdth_ctr_vcount; if (ha->pscratch != NULL) +#if LINUX_VERSION_CODE >= 0x020322 + free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD); +#else scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH); +#endif #if LINUX_VERSION_CODE >= 0x010346 free_irq(ha->irq,NULL); #else @@ -3590,21 +3749,26 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) #if LINUX_VERSION_CODE >= 0x020000 shp->max_id = ha->tid_cnt; shp->max_lun = MAXLUN; - shp->max_channel = ha->bus_cnt; -#else - /* register addit. SCSI channels as virtual controllers */ - for (b=1; bbus_cnt+1; ++b) { - shp = scsi_register(shtp,sizeof(gdth_num_str)); - shp->unchecked_isa_dma = 1; - shp->irq = ha->irq; - shp->dma_channel = ha->drq; - gdth_ctr_vtab[gdth_ctr_vcount++] = shp; - NUMDATA(shp)->hanum = (ushort)hanum; - NUMDATA(shp)->busnum = b; - } + shp->max_channel = virt_ctr ? 0 : ha->bus_cnt; + if (virt_ctr) #endif + { + virt_ctr = 1; + /* register addit. SCSI channels as virtual controllers */ + for (b = 1; b < ha->bus_cnt + 1; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 1; + shp->irq = ha->irq; + shp->dma_channel = ha->drq; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } + } + GDTH_INIT_LOCK_HA(ha); gdth_enable_int(hanum); +#endif /* !__ia64__ */ } } @@ -3646,7 +3810,12 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) NUMDATA(shp)->hanum)); ha->pccb = CMDDATA(shp); +#if LINUX_VERSION_CODE >= 0x020322 + ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, + GDTH_SCRATCH_ORD); +#else ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA); +#endif ha->scratch_busy = FALSE; ha->req_first = NULL; ha->tid_cnt = MAX_HDRIVES; @@ -3661,7 +3830,11 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) --gdth_ctr_count; --gdth_ctr_vcount; if (ha->pscratch != NULL) +#if LINUX_VERSION_CODE >= 0x020322 + free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD); +#else scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH); +#endif #if LINUX_VERSION_CODE >= 0x010346 free_irq(ha->irq,NULL); #else @@ -3677,22 +3850,23 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) #if LINUX_VERSION_CODE >= 0x020000 shp->max_id = ha->tid_cnt; shp->max_lun = MAXLUN; - shp->max_channel = ha->bus_cnt; -#else - /* register addit. SCSI channels as virtual controllers */ - for (b=1; bbus_cnt+1; ++b) { - shp = scsi_register(shtp,sizeof(gdth_num_str)); - shp->unchecked_isa_dma = 0; - shp->irq = ha->irq; - shp->dma_channel = 0xff; - gdth_ctr_vtab[gdth_ctr_vcount++] = shp; - NUMDATA(shp)->hanum = (ushort)hanum; - NUMDATA(shp)->busnum = b; - TRACE2(("EISA detect Bus %d: shp %x hanum %d\n", - NUMDATA(shp)->busnum,(ulong32)shp, - NUMDATA(shp)->hanum)); - } + shp->max_channel = virt_ctr ? 0 : ha->bus_cnt; + if (virt_ctr) #endif + { + virt_ctr = 1; + /* register addit. SCSI channels as virtual controllers */ + for (b = 1; b < ha->bus_cnt + 1; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } + } + GDTH_INIT_LOCK_HA(ha); gdth_enable_int(hanum); } @@ -3745,7 +3919,12 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) NUMDATA(shp)->busnum= 0; ha->pccb = CMDDATA(shp); +#if LINUX_VERSION_CODE >= 0x020322 + ha->pscratch = (void *) __get_free_pages(GFP_ATOMIC | GFP_DMA, + GDTH_SCRATCH_ORD); +#else ha->pscratch = scsi_init_malloc(GDTH_SCRATCH, GFP_ATOMIC | GFP_DMA); +#endif ha->scratch_busy = FALSE; ha->req_first = NULL; ha->tid_cnt = pcistr[ctr].device_id >= 0x200 ? MAXID : MAX_HDRIVES; @@ -3760,7 +3939,11 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) --gdth_ctr_count; --gdth_ctr_vcount; if (ha->pscratch != NULL) +#if LINUX_VERSION_CODE >= 0x020322 + free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD); +#else scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH); +#endif #if LINUX_VERSION_CODE >= 0x010346 free_irq(ha->irq,NULL); #else @@ -3776,19 +3959,23 @@ __initfunc (int gdth_detect(Scsi_Host_Template *shtp)) #if LINUX_VERSION_CODE >= 0x020000 shp->max_id = ha->tid_cnt; shp->max_lun = MAXLUN; - shp->max_channel = ha->bus_cnt; -#else - /* register addit. SCSI channels as virtual controllers */ - for (b=1; bbus_cnt+1; ++b) { - shp = scsi_register(shtp,sizeof(gdth_num_str)); - shp->unchecked_isa_dma = 0; - shp->irq = ha->irq; - shp->dma_channel = 0xff; - gdth_ctr_vtab[gdth_ctr_vcount++] = shp; - NUMDATA(shp)->hanum = (ushort)hanum; - NUMDATA(shp)->busnum = b; - } + shp->max_channel = virt_ctr ? 0 : ha->bus_cnt; + if (virt_ctr) #endif + { + virt_ctr = 1; + /* register addit. SCSI channels as virtual controllers */ + for (b = 1; b < ha->bus_cnt + 1; ++b) { + shp = scsi_register(shtp,sizeof(gdth_num_str)); + shp->unchecked_isa_dma = 0; + shp->irq = ha->irq; + shp->dma_channel = 0xff; + gdth_ctr_vtab[gdth_ctr_vcount++] = shp; + NUMDATA(shp)->hanum = (ushort)hanum; + NUMDATA(shp)->busnum = b; + } + } + GDTH_INIT_LOCK_HA(ha); gdth_enable_int(hanum); } @@ -3833,10 +4020,16 @@ int gdth_release(struct Scsi_Host *shp) free_irq(shp->irq); #endif } +#ifndef __ia64__ if (shp->dma_channel != 0xff) { free_dma(shp->dma_channel); } +#endif +#if LINUX_VERSION_CODE >= 0x020322 + free_pages((unsigned long)ha->pscratch, GDTH_SCRATCH_ORD); +#else scsi_init_free((void *)ha->pscratch, GDTH_SCRATCH); +#endif gdth_ctr_released++; TRACE2(("gdth_release(): HA %d of %d\n", gdth_ctr_released, gdth_ctr_count)); @@ -3937,26 +4130,26 @@ int gdth_eh_bus_reset(Scsi_Cmnd *scp) gdth_ha_str *ha; ulong flags; Scsi_Cmnd *cmnd; + unchar b; TRACE2(("gdth_eh_bus_reset()\n")); hanum = NUMDATA(scp->host)->hanum; + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; ha = HADATA(gdth_ctr_tab[hanum]); /* clear command tab */ GDTH_LOCK_HA(ha, flags); for (i = 0; i < GDTH_MAXCMDS; ++i) { cmnd = ha->cmd_tab[i].cmnd; - if (!SPECIAL_SCP(cmnd) && cmnd->channel == scp->channel) + if (!SPECIAL_SCP(cmnd) && cmnd->channel == b) ha->cmd_tab[i].cmnd = UNUSED_CMND; } GDTH_UNLOCK_HA(ha, flags); - if (scp->channel == ha->virt_bus) { + if (b == ha->virt_bus) { /* host drives */ for (i = 0; i < MAX_HDRIVES; ++i) { - if (ha->hdr[i].present && - (ha->hdr[i].cluster_type & CLUSTER_RESERVED) - == CLUSTER_RESERVED) { + if (ha->hdr[i].present) { GDTH_LOCK_HA(ha, flags); gdth_polling = TRUE; while (gdth_test_busy(hanum)) @@ -3972,12 +4165,12 @@ int gdth_eh_bus_reset(Scsi_Cmnd *scp) /* raw devices */ GDTH_LOCK_HA(ha, flags); for (i = 0; i < MAXID; ++i) - ha->raw[BUS_L2P(ha,scp->channel)].io_cnt[i] = 0; + ha->raw[BUS_L2P(ha,b)].io_cnt[i] = 0; gdth_polling = TRUE; while (gdth_test_busy(hanum)) gdth_delay(0); gdth_internal_cmd(hanum, SCSIRAWSERVICE, GDT_RESET_BUS, - BUS_L2P(ha,scp->channel), 0, 0); + BUS_L2P(ha,b), 0, 0); gdth_polling = FALSE; GDTH_UNLOCK_HA(ha, flags); } @@ -3997,17 +4190,17 @@ int gdth_bios_param(Disk *disk,kdev_t dev,int *ip) int gdth_bios_param(Disk *disk,int dev,int *ip) #endif { - unchar t; + unchar b, t; int hanum; gdth_ha_str *ha; hanum = NUMDATA(disk->device->host)->hanum; + b = virt_ctr ? NUMDATA(disk->device->host)->busnum : disk->device->channel; t = disk->device->id; - TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", - hanum, disk->device->channel, t)); + TRACE2(("gdth_bios_param() ha %d bus %d target %d\n", hanum, b, t)); ha = HADATA(gdth_ctr_tab[hanum]); - if (disk->device->channel != ha->virt_bus || ha->hdr[t].heads == 0) { + if (b != ha->virt_bus || ha->hdr[t].heads == 0) { /* raw device or host drive without mapping information */ TRACE2(("Evaluate mapping\n")); gdth_eval_mapping(disk->capacity,&ip[2],&ip[0],&ip[1]); @@ -4075,19 +4268,32 @@ static void gdth_flush(int hanum) { int i; gdth_ha_str *ha; + gdth_cmd_str gdtcmd; +#if LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else Scsi_Cmnd scp; Scsi_Device sdev; - gdth_cmd_str gdtcmd; - char cmnd[12]; +#endif + char cmnd[MAX_COMMAND_SIZE]; + memset(cmnd, 0xff, 12); TRACE2(("gdth_flush() hanum %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); + +#if LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + scp = scsi_allocate_device(sdev, 1, FALSE); + scp->cmd_len = 12; + scp->use_sg = 0; +#else memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - memset(cmnd, 0xff, 12); sdev.host = scp.host = gdth_ctr_tab[hanum]; sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; +#endif for (i = 0; i < MAX_HDRIVES; ++i) { if (ha->hdr[i].present) { @@ -4098,9 +4304,17 @@ static void gdth_flush(int hanum) gdtcmd.u.cache.BlockNo = 1; gdtcmd.u.cache.sg_canz = 0; TRACE2(("gdth_flush(): flush ha %d drive %d\n", hanum, i)); +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); +#endif } } +#if LINUX_VERSION_CODE >= 0x020322 + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#endif } /* shutdown routine */ @@ -4112,10 +4326,15 @@ void gdth_halt(void) { int hanum; #ifndef __alpha__ + gdth_cmd_str gdtcmd; +#if LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else Scsi_Cmnd scp; Scsi_Device sdev; - gdth_cmd_str gdtcmd; - char cmnd[12]; +#endif + char cmnd[MAX_COMMAND_SIZE]; #endif #if LINUX_VERSION_CODE >= 0x020100 @@ -4136,18 +4355,31 @@ void gdth_halt(void) #ifndef __alpha__ /* controller reset */ + memset(cmnd, 0xff, 12); +#if LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_tab[hanum]); + scp = scsi_allocate_device(sdev, 1, FALSE); + scp->cmd_len = 12; + scp->use_sg = 0; +#else memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - memset(cmnd, 0xff, 12); sdev.host = scp.host = gdth_ctr_tab[hanum]; sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; +#endif gdtcmd.BoardNode = LOCALBOARD; gdtcmd.Service = CACHESERVICE; gdtcmd.OpCode = GDT_RESET; TRACE2(("gdth_halt(): reset controller %d\n", hanum)); +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 10); + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 10); +#endif #endif } printk("Done.\n"); @@ -4156,7 +4388,9 @@ void gdth_halt(void) del_timer(&gdth_timer); #endif #if LINUX_VERSION_CODE >= 0x020100 +#if LINUX_VERSION_CODE < 0x020322 unregister_reboot_notifier(&gdth_notifier); +#endif return NOTIFY_OK; #endif } @@ -4164,7 +4398,7 @@ void gdth_halt(void) /* called from init/main.c */ -__initfunc (void gdth_setup(char *str,int *ints)) +GDTH_INITFUNC(void, gdth_setup(char *str,int *ints)) { int i, argc; char *cur_str, *argv; @@ -4207,6 +4441,8 @@ __initfunc (void gdth_setup(char *str,int *ints)) max_ids = val; else if (!strncmp(argv, "rescan:", 7)) rescan = val; + else if (!strncmp(argv, "virt_ctr:", 9)) + virt_ctr = val; else if (!strncmp(argv, "reserve_list:", 13)) { reserve_list[0] = val; for (i = 1; i < MAX_RES_ARGS; i++) { diff --git a/drivers/scsi/gdth.h b/drivers/scsi/gdth.h index 02aae111348c..df26a9be8476 100644 --- a/drivers/scsi/gdth.h +++ b/drivers/scsi/gdth.h @@ -10,7 +10,7 @@ * * * - * $Id: gdth.h,v 1.24 1999/11/02 13:43:49 achim Exp $ + * $Id: gdth.h,v 1.33 2000/07/24 09:29:25 achim Exp $ */ #include @@ -29,9 +29,9 @@ /* defines, macros */ /* driver version */ -#define GDTH_VERSION_STR "1.17" +#define GDTH_VERSION_STR "1.23" #define GDTH_VERSION 1 -#define GDTH_SUBVERSION 17 +#define GDTH_SUBVERSION 23 /* protocol version */ #define PROTOCOL_VERSION 1 @@ -126,7 +126,8 @@ #endif /* limits */ -#define GDTH_SCRATCH 4096 /* 4KB scratch buffer */ +#define GDTH_SCRATCH PAGE_SIZE /* 4KB scratch buffer */ +#define GDTH_SCRATCH_ORD 0 /* order 0 means 1 page */ #define GDTH_MAXCMDS 124 #define GDTH_MAXC_P_L 16 /* max. cmds per lun */ #define GDTH_MAX_RAW 2 /* max. cmds per raw device */ @@ -180,7 +181,7 @@ #define CLUSTER_RESERVED 4 #define CLUSTER_RESERVE_STATE (CLUSTER_DRIVE|CLUSTER_MOUNTED|CLUSTER_RESERVED) -/* cache/raw service commands */ +/* commands for all services, cache service */ #define GDT_INIT 0 /* service initialization */ #define GDT_READ 1 /* read command */ #define GDT_WRITE 2 /* write command */ @@ -202,7 +203,7 @@ #define GDT_RW_ATTRIBS 23 /* R/W attribs (write thru,..)*/ #define GDT_CLUST_RESET 24 /* releases the cluster drives*/ -/* additional raw service commands */ +/* raw service commands */ #define GDT_RESERVE 14 /* reserve dev. to raw serv. */ #define GDT_RELEASE 15 /* release device */ #define GDT_RESERVE_ALL 16 /* reserve all devices */ @@ -211,6 +212,9 @@ #define GDT_SCAN_START 19 /* start device scan */ #define GDT_SCAN_END 20 /* stop device scan */ +/* screen service commands */ +#define GDT_REALTIME 3 /* realtime clock to screens. */ + /* IOCTL command defines */ #define SCSI_DR_INFO 0x00 /* SCSI drive info */ #define SCSI_CHAN_CNT 0x05 /* SCSI channel count */ @@ -235,18 +239,9 @@ #define IO_CHANNEL 0x00020000L /* default IO channel */ #define INVALID_CHANNEL 0x0000ffffL /* invalid channel */ -/* IOCTLs */ -#define GDTIOCTL_MASK ('J'<<8) -#define GDTIOCTL_GENERAL (GDTIOCTL_MASK | 0) /* general IOCTL */ -#define GDTIOCTL_DRVERS (GDTIOCTL_MASK | 1) /* get driver version */ -#define GDTIOCTL_CTRTYPE (GDTIOCTL_MASK | 2) /* get controller type */ -#define GDTIOCTL_CTRCNT (GDTIOCTL_MASK | 5) /* get controller count */ -#define GDTIOCTL_LOCKDRV (GDTIOCTL_MASK | 6) /* lock host drive */ -#define GDTIOCTL_LOCKCHN (GDTIOCTL_MASK | 7) /* lock channel */ -#define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ - /* service errors */ #define S_OK 1 /* no error */ +#define S_GENERR 6 /* general error */ #define S_BSY 7 /* controller busy */ #define S_CACHE_UNKNOWN 12 /* cache serv.: drive unknown */ #define S_RAW_SCSI 12 /* raw serv.: target error */ @@ -263,10 +258,8 @@ #define HIGH_PRI 0x08 /* data directions */ -#ifndef HOSTS_C -#define DATA_IN 0x01000000L /* data from target */ -#define DATA_OUT 0x00000000L /* data to target */ -#endif +#define GDTH_DATA_IN 0x01000000L /* data from target */ +#define GDTH_DATA_OUT 0x00000000L /* data to target */ /* BMIC registers (EISA controllers) */ #define ID0REG 0x0c80 /* board ID */ @@ -472,11 +465,11 @@ typedef struct { } PACKED gdth_alist_str; typedef struct { - ulong32 entries_avail; /* allocated entries */ - ulong32 entries_init; /* returned entries */ - ulong32 first_entry; /* first entry number */ - ulong32 list_offset; /* offset of following list */ - gdth_alist_str list[1]; /* list */ + ulong32 entries_avail; /* allocated entries */ + ulong32 entries_init; /* returned entries */ + ulong32 first_entry; /* first entry number */ + ulong32 list_offset; /* offset of following list */ + gdth_alist_str list[1]; /* list */ } PACKED gdth_arcdl_str; /* cache info/config IOCTL */ @@ -609,8 +602,13 @@ typedef struct { } PACKED ioctl; /* IOCTL command structure */ struct { ushort reserved; - ulong32 msg_handle; /* message handle */ - ulong32 msg_addr; /* message buffer address */ + union { + struct { + ulong32 msg_handle; /* message handle */ + ulong32 msg_addr; /* message buffer address */ + } PACKED msg; + unchar data[12]; /* buffer for rtc data, ... */ + } su; } PACKED screen; /* screen service cmd. str. */ struct { ushort reserved; @@ -673,6 +671,8 @@ typedef struct { ulong32 l1, l2, l3, l4; } PACKED test; } eu; + ulong32 severity; + unchar event_string[256]; } PACKED gdth_evt_data; typedef struct { @@ -822,7 +822,9 @@ typedef struct { unchar unused5[7]; unchar edoor_en_reg; /* board interrupts enable */ unchar unused6[27]; - ulong32 unused7[1004]; /* size: 4 KB */ + ulong32 unused7[939]; + ulong32 severity; + char evt_str[256]; /* event string */ } PACKED gdt6m_i960_regs; /* DPRAM PCI MPR controllers */ @@ -856,6 +858,7 @@ typedef struct { ushort type; /* controller class */ ushort raw_feat; /* feat. raw service (s/g,..) */ ulong32 stype; /* controller subtype */ + ushort fw_vers; /* firmware version */ ushort cache_feat; /* feat. cache serv. (s/g,..) */ ushort bmic; /* BMIC address (EISA) */ void *brd; /* DPRAM address */ @@ -868,6 +871,7 @@ typedef struct { unchar irq; /* IRQ */ unchar drq; /* DRQ (ISA controllers) */ ushort status; /* command status */ + ushort service; /* service/firmware ver./.. */ ulong32 info; ulong32 info2; /* additional info */ Scsi_Cmnd *req_first; /* top of request queue */ @@ -914,6 +918,7 @@ typedef struct { gdth_cpar_str cpar; /* controller cache par. */ gdth_bfeat_str bfeat; /* controller features */ gdth_binfo_str binfo; /* controller info */ + gdth_evt_data dvr; /* event structure */ #if LINUX_VERSION_CODE >= 0x02015F spinlock_t smp_lock; #endif @@ -1004,7 +1009,38 @@ int gdth_reset(Scsi_Cmnd *); #endif const char *gdth_info(struct Scsi_Host *); -#if LINUX_VERSION_CODE >= 0x02015F +#if LINUX_VERSION_CODE >= 0x020322 +int gdth_bios_param(Disk *,kdev_t,int *); +int gdth_proc_info(char *,char **,off_t,int,int,int); +int gdth_eh_abort(Scsi_Cmnd *scp); +int gdth_eh_device_reset(Scsi_Cmnd *scp); +int gdth_eh_bus_reset(Scsi_Cmnd *scp); +int gdth_eh_host_reset(Scsi_Cmnd *scp); +#define GDTH { proc_name: "gdth", \ + proc_info: gdth_proc_info, \ + name: "GDT SCSI Disk Array Controller",\ + detect: gdth_detect, \ + release: gdth_release, \ + info: gdth_info, \ + command: gdth_command, \ + queuecommand: gdth_queuecommand, \ + eh_abort_handler: gdth_eh_abort, \ + eh_device_reset_handler: gdth_eh_device_reset, \ + eh_bus_reset_handler: gdth_eh_bus_reset, \ + eh_host_reset_handler: gdth_eh_host_reset, \ + abort: gdth_abort, \ + reset: gdth_reset, \ + bios_param: gdth_bios_param, \ + can_queue: GDTH_MAXCMDS, \ + this_id: -1, \ + sg_tablesize: GDTH_MAXSG, \ + cmd_per_lun: GDTH_MAXC_P_L, \ + present: 0, \ + unchecked_isa_dma: 1, \ + use_clustering: ENABLE_CLUSTERING, \ + use_new_eh_code: 1 /* use new error code */ } + +#elif LINUX_VERSION_CODE >= 0x02015F int gdth_bios_param(Disk *,kdev_t,int *); extern struct proc_dir_entry proc_scsi_gdth; int gdth_proc_info(char *,char **,off_t,int,int,int); @@ -1035,6 +1071,7 @@ int gdth_eh_host_reset(Scsi_Cmnd *scp); unchecked_isa_dma: 1, \ use_clustering: ENABLE_CLUSTERING, \ use_new_eh_code: 1 /* use new error code */ } + #elif LINUX_VERSION_CODE >= 0x010300 int gdth_bios_param(Disk *,kdev_t,int *); extern struct proc_dir_entry proc_scsi_gdth; @@ -1059,6 +1096,7 @@ int gdth_proc_info(char *,char **,off_t,int,int,int); 0, \ 1, \ ENABLE_CLUSTERING} + #else int gdth_bios_param(Disk *,int,int *); #define GDTH { NULL, NULL, \ diff --git a/drivers/scsi/gdth_ioctl.h b/drivers/scsi/gdth_ioctl.h index 64a00c5900ff..34715246fbb1 100644 --- a/drivers/scsi/gdth_ioctl.h +++ b/drivers/scsi/gdth_ioctl.h @@ -2,7 +2,7 @@ #define _GDTH_IOCTL_H /* gdth_ioctl.h - * $Id: gdth_ioctl.h,v 1.3 1999/05/26 11:49:57 achim Exp $ + * $Id: gdth_ioctl.h,v 1.6 2000/07/24 09:29:43 achim Exp $ */ /* IOCTLs */ @@ -18,8 +18,10 @@ #define GDTIOCTL_EVENT (GDTIOCTL_MASK | 8) /* read controller events */ #define GDTIOCTL_SCSI (GDTIOCTL_MASK | 9) /* SCSI command */ #define GDTIOCTL_RESET_BUS (GDTIOCTL_MASK |10) /* reset SCSI bus */ +#define GDTIOCTL_RESCAN (GDTIOCTL_MASK |11) /* rescan host drives */ -#define GDTIOCTL_MAGIC 0xaffe0001UL +#define GDTIOCTL_MAGIC 0xaffe0003UL +#define EVENT_SIZE 294 /* IOCTL structure (write) */ @@ -46,7 +48,7 @@ typedef struct { struct { int erase; /* erase event ? */ int handle; - unchar evt[34]; /* event structure */ + unchar evt[EVENT_SIZE]; /* event structure */ } event; struct { unchar bus; /* SCSI bus */ @@ -87,7 +89,7 @@ typedef struct { } ctrcnt; struct { int handle; - unchar evt[34]; /* event structure */ + unchar evt[EVENT_SIZE]; /* event structure */ } event; struct { unchar bus; /* SCSI bus, 0xff: invalid */ diff --git a/drivers/scsi/gdth_proc.c b/drivers/scsi/gdth_proc.c index aa10802c48c3..dbbb43513050 100644 --- a/drivers/scsi/gdth_proc.c +++ b/drivers/scsi/gdth_proc.c @@ -1,5 +1,5 @@ /* gdth_proc.c - * $Id: gdth_proc.c,v 1.16 1999/11/02 13:44:11 achim Exp $ + * $Id: gdth_proc.c,v 1.22 2000/07/24 09:30:28 achim Exp $ */ #include "gdth_ioctl.h" @@ -31,18 +31,30 @@ int gdth_proc_info(char *buffer,char **start,off_t offset,int length, static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum) { int ret_val; +#if LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else Scsi_Cmnd scp; Scsi_Device sdev; +#endif gdth_iowr_str *piowr; TRACE2(("gdth_set_info() ha %d bus %d\n",hanum,busnum)); piowr = (gdth_iowr_str *)buffer; +#if LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]); + scp = scsi_allocate_device(sdev, 1, FALSE); + scp->cmd_len = 12; + scp->use_sg = 0; +#else memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); sdev.host = scp.host = gdth_ctr_vtab[vh]; sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; +#endif if (length >= 4) { if (strncmp(buffer,"gdth",4) == 0) { @@ -52,23 +64,33 @@ static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum) } else if (piowr->magic == GDTIOCTL_MAGIC) { ret_val = gdth_set_bin_info( buffer, length, hanum, scp ); } else { - printk("GDT: Wrong signature: %6s\n",buffer); + printk("GDT: Wrong signature: %x\n",piowr->magic); ret_val = -EINVAL; } } else { ret_val = -EINVAL; } +#if LINUX_VERSION_CODE >= 0x020322 + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#endif return ret_val; } +#if LINUX_VERSION_CODE >= 0x020322 +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp) +#else static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +#endif { int orig_length, drive, wb_mode; int i, found; gdth_ha_str *ha; gdth_cmd_str gdtcmd; gdth_cpar_str *pcpar; - char cmnd[12]; + + char cmnd[MAX_COMMAND_SIZE]; + memset(cmnd, 0xff, 12); TRACE2(("gdth_set_asc_info() ha %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); @@ -76,7 +98,6 @@ static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) drive = -1; wb_mode = 0; found = FALSE; - memset(cmnd, 0xff, 12); if (length >= 5 && strncmp(buffer,"flush",5)==0) { buffer += 6; @@ -103,7 +124,11 @@ static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) gdtcmd.u.cache.DeviceNo = i; gdtcmd.u.cache.BlockNo = 1; gdtcmd.u.cache.sg_canz = 0; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); +#endif } } if (!found) @@ -156,7 +181,11 @@ static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) gdtcmd.u.ioctl.subfunc = CACHE_CONFIG; gdtcmd.u.ioctl.channel = INVALID_CHANNEL; pcpar->write_back = wb_mode==1 ? 0:1; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); +#endif gdth_ioctl_free(hanum); printk("Done.\n"); return(orig_length); @@ -166,9 +195,14 @@ static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) return(-EINVAL); } +#if LINUX_VERSION_CODE >= 0x020322 +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp) +#else static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) +#endif { unchar i, j; + ushort k, hdr_cnt; gdth_ha_str *ha; gdth_iowr_str *piowr; gdth_iord_str *piord; @@ -177,9 +211,11 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) ulong32 *ppadd, add_size; ulong32 *ppadd2, add_size2; ulong flags; - char cmnd[12]; gdth_cmd_str gdtcmd; + char cmnd[MAX_COMMAND_SIZE]; + memset(cmnd, 0xff, 12); + TRACE2(("gdth_set_bin_info() ha %d\n",hanum)); ha = HADATA(gdth_ctr_tab[hanum]); piowr = (gdth_iowr_str *)buffer; @@ -187,7 +223,6 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) pcmd = NULL; ppadd = ppadd2 = NULL; add_size = add_size2 = 0; - memset(cmnd, 0xff, 12); if (length < GDTOFFSOF(gdth_iowr_str,iu)) return(-EINVAL); @@ -242,8 +277,13 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) *ppadd2 = virt_to_bus(piord->iu.general.data+add_size); } /* do IOCTL */ +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, pcmd, cmnd, piowr->timeout); + piord->status = (ulong32)scp->SCp.Message; +#else gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout); piord->status = (ulong32)scp.SCp.Message; +#endif break; case GDTIOCTL_DRVERS: @@ -387,12 +427,20 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) return(-EBUSY); piord = (gdth_iord_str *)ha->pscratch; piord->size = sizeof(gdth_iord_str); - scp.target = scp.device->id = piowr->iu.scsi.target; - scp.channel = scp.device->channel = piowr->iu.scsi.bus; memcpy(cmnd, piowr->iu.scsi.cmd, 12); +#if LINUX_VERSION_CODE >= 0x020322 + scp->target = scp->device->id = piowr->iu.scsi.target; + scp->channel = scp->device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus; + scp->cmd_len = piowr->iu.scsi.cmd_len; + gdth_do_cmd(scp, pcmd, cmnd, piowr->timeout); + piord->status = (ulong32)scp->SCp.Message; +#else + scp.target = scp.device->id = piowr->iu.scsi.target; + scp.channel = scp.device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus; scp.cmd_len = piowr->iu.scsi.cmd_len; gdth_do_cmd(&scp, pcmd, cmnd, piowr->timeout); - piord->status = (ulong32)scp.result; + piord->status = (ulong32)scp.SCp.Message; +#endif break; case GDTIOCTL_RESET_BUS: @@ -400,8 +448,23 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) return(-EBUSY); piord = (gdth_iord_str *)ha->pscratch; piord->size = sizeof(gdth_iord_str); - scp.channel = scp.device->channel = piowr->iu.scsi.bus; +#if LINUX_VERSION_CODE >= 0x020322 + scp->channel = scp->device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus; + piord->status = (ulong32)gdth_eh_bus_reset( scp ); + if (piord->status == SUCCESS) + piord->status = S_OK; + else + piord->status = S_GENERR; +#elif LINUX_VERSION_CODE >= 0x02015F + scp.channel = scp.device->channel = virt_ctr ? 0 : piowr->iu.scsi.bus; piord->status = (ulong32)gdth_eh_bus_reset( &scp ); + if (piord->status == SUCCESS) + piord->status = S_OK; + else + piord->status = S_GENERR; +#else + piord->status = S_OK; +#endif break; case GDTIOCTL_HDRLIST: @@ -423,22 +486,50 @@ static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp) gdtcmd.u.cache.DeviceNo = i; gdtcmd.u.cache.BlockNo = 0; gdtcmd.u.cache.sg_canz = 0; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) == S_OK) + piord->iu.hdr_list[i].cluster_type = + (unchar)(scp->SCp.Message>>16); +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); if ((scp.SCp.Message & 0xffff) == S_OK) piord->iu.hdr_list[i].cluster_type = (unchar)(scp.SCp.Message>>16); - } +#endif + } } else { piord->iu.hdr_list[i].bus = 0xff; } } break; + case GDTIOCTL_RESCAN: + if (!gdth_ioctl_alloc( hanum, sizeof(gdth_iord_str) )) + return(-EBUSY); + piord = (gdth_iord_str *)ha->pscratch; + piord->size = sizeof(gdth_iord_str); + piord->status = S_OK; + GDTH_LOCK_HA(ha, flags); + gdth_polling = TRUE; + while (gdth_test_busy(hanum)) + gdth_delay(0); + /* initialize cache service */ + gdth_internal_cmd(hanum,CACHESERVICE,GDT_INIT,LINUX_OS,0,0); + hdr_cnt = (ushort)ha->info; + /* scanning for host drives */ + for (k = 0; k < MAX_HDRIVES; ++k) + ha->hdr[k].present = FALSE; + for (k = 0; k < hdr_cnt; ++k) + gdth_analyse_hdrive(hanum, k); + gdth_polling = FALSE; + GDTH_UNLOCK_HA(ha, flags); + break; + default: return(-EINVAL); } - /* we return a buffer ID to detect the right buffer during READ-IOCTL */ - return 1; + return length; } static int gdth_get_info(char *buffer,char **start,off_t offset, @@ -454,9 +545,13 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdth_cmd_str gdtcmd; gdth_evt_str estr; +#if LINUX_VERSION_CODE >= 0x020322 + Scsi_Cmnd *scp; + Scsi_Device *sdev; +#else Scsi_Cmnd scp; Scsi_Device sdev; - char cmnd[12]; +#endif char hrec[161]; struct timeval tv; @@ -467,19 +562,27 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdth_cdrinfo_str *pcdi; gdth_hget_str *phg; + char cmnd[MAX_COMMAND_SIZE]; + memset(cmnd, 0xff, 12); + TRACE2(("gdth_get_info() ha %d bus %d\n",hanum,busnum)); ha = HADATA(gdth_ctr_tab[hanum]); - id = length; +#if LINUX_VERSION_CODE >= 0x020322 + sdev = scsi_get_host_dev(gdth_ctr_vtab[vh]); + scp = scsi_allocate_device(sdev, 1, FALSE); + scp->cmd_len = 12; + scp->use_sg = 0; +#else memset(&sdev,0,sizeof(Scsi_Device)); memset(&scp, 0,sizeof(Scsi_Cmnd)); - memset(cmnd, 0xff, 12); sdev.host = scp.host = gdth_ctr_vtab[vh]; sdev.id = scp.target = sdev.host->this_id; scp.device = &sdev; +#endif - /* look for buffer ID in length */ - if (id > 1) { + /* ioctl from tool? */ + if (!gdth_ioctl_check_bin(hanum, (ushort)length)) { /* request is i.e. "cat /proc/scsi/gdth/0" */ /* format: %-15s\t%-10s\t%-15s\t%s */ /* driver parameters */ @@ -514,11 +617,10 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, /* controller information */ size = sprintf(buffer+len,"\nDisk Array Controller Information:\n"); len += size; pos = begin + len; -#if LINUX_VERSION_CODE >= 0x020000 - strcpy(hrec, ha->binfo.type_string); -#else - sprintf(hrec, "%s (Bus %d)", ha->binfo.type_string, busnum); -#endif + if (virt_ctr) + sprintf(hrec, "%s (Bus %d)", ha->binfo.type_string, busnum); + else + strcpy(hrec, ha->binfo.type_string); size = sprintf(buffer+len, " Number: \t%d \tName: \t%s\n", hanum, hrec); @@ -586,11 +688,16 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, sizeof(pds->list[0]); if (pds->entries > cnt) pds->entries = cnt; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) != S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); if ((scp.SCp.Message & 0xffff) != S_OK) +#endif + { pds->count = 0; - TRACE2(("pdr_statistics() entries %d status %d\n", - pds->count, scp.SCp.Message)); + } /* other IOCTLs must fit into area GDTH_SCRATCH/4 */ for (j = 0; j < ha->raw[i].pdev_cnt; ++j) { @@ -606,8 +713,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdtcmd.u.ioctl.subfunc = SCSI_DR_INFO | L_CTRL_PATTERN; gdtcmd.u.ioctl.channel = ha->raw[i].address | ha->raw[i].id_list[j]; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) == S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); - if ((scp.SCp.Message & 0xffff) == S_OK) { + if ((scp.SCp.Message & 0xffff) == S_OK) +#endif + { strncpy(hrec,pdi->vendor,8); strncpy(hrec+8,pdi->product,16); strncpy(hrec+24,pdi->revision,4); @@ -657,8 +770,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdtcmd.u.ioctl.channel = ha->raw[i].address | ha->raw[i].id_list[j]; pdef->sddc_type = 0x08; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) == S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); - if ((scp.SCp.Message & 0xffff) == S_OK) { + if ((scp.SCp.Message & 0xffff) == S_OK) +#endif + { size = sprintf(buffer+len, " Grown Defects:\t%d\n", pdef->sddc_cnt); @@ -704,9 +823,16 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdtcmd.u.ioctl.param_size = sizeof(gdth_cdrinfo_str); gdtcmd.u.ioctl.subfunc = CACHE_DRV_INFO; gdtcmd.u.ioctl.channel = drv_no; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) != S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); if ((scp.SCp.Message & 0xffff) != S_OK) +#endif + { break; + } pcdi->ld_dtype >>= 16; j++; if (pcdi->ld_dtype > 2) { @@ -801,8 +927,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdtcmd.u.ioctl.param_size = sizeof(gdth_arrayinf_str); gdtcmd.u.ioctl.subfunc = ARRAY_INFO | LA_CTRL_PATTERN; gdtcmd.u.ioctl.channel = i; +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) == S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); - if ((scp.SCp.Message & 0xffff) == S_OK) { + if ((scp.SCp.Message & 0xffff) == S_OK) +#endif + { if (pai->ai_state == 0) strcpy(hrec, "idle"); else if (pai->ai_state == 2) @@ -876,8 +1008,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, gdtcmd.u.ioctl.channel = i; phg->entries = MAX_HDRIVES; phg->offset = GDTOFFSOF(gdth_hget_str, entry[0]); +#if LINUX_VERSION_CODE >= 0x020322 + gdth_do_cmd(scp, &gdtcmd, cmnd, 30); + if ((scp->SCp.Message & 0xffff) != S_OK) +#else gdth_do_cmd(&scp, &gdtcmd, cmnd, 30); - if ((scp.SCp.Message & 0xffff) != S_OK) { + if ((scp.SCp.Message & 0xffff) != S_OK) +#endif + { ha->hdr[i].ldr_no = i; ha->hdr[i].rw_attribs = 0; ha->hdr[i].start_sec = 0; @@ -891,8 +1029,6 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, ha->hdr[k].start_sec = phg->entry[j].start_sec; } } - TRACE2(("host_get entries %d status %d\n", - phg->entries, scp.SCp.Message)); } gdth_ioctl_free(hanum); @@ -959,17 +1095,14 @@ static int gdth_get_info(char *buffer,char **start,off_t offset, length = piord->size; memcpy(buffer+len, (char *)piord, length); gdth_ioctl_free(hanum); - len += length; pos = begin + len; - - if (pos < offset) { - len = 0; - begin = pos; - } - if (pos > offset + length) - goto stop_output; + len = length; } stop_output: +#if LINUX_VERSION_CODE >= 0x020322 + scsi_release_command(scp); + scsi_free_host_dev(sdev); +#endif *start = buffer +(offset-begin); len -= (offset-begin); if (len > length) @@ -982,8 +1115,12 @@ stop_output: static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd, char *cmnd, int timeout) { - struct semaphore sem = MUTEX_LOCKED; unsigned bufflen; +#if LINUX_VERSION_CODE >= 0x020322 + DECLARE_MUTEX_LOCKED(sem); +#else + struct semaphore sem = MUTEX_LOCKED; +#endif TRACE2(("gdth_do_cmd()\n")); if (gdtcmd != NULL) { @@ -995,9 +1132,13 @@ static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *gdtcmd, } scp->request.rq_status = RQ_SCSI_BUSY; scp->request.sem = &sem; +#if LINUX_VERSION_CODE >= 0x020322 + scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1); +#else GDTH_LOCK_SCSI_DOCMD(); scsi_do_cmd(scp, cmnd, gdtcmd, bufflen, gdth_scsi_done, timeout*HZ, 1); GDTH_UNLOCK_SCSI_DOCMD(); +#endif down(&sem); } @@ -1046,26 +1187,41 @@ static void gdth_ioctl_free(int hanum) GDTH_UNLOCK_HA(ha, flags); } +static int gdth_ioctl_check_bin(int hanum, ushort size) +{ + gdth_ha_str *ha; + ulong flags; + int ret_val; + + ha = HADATA(gdth_ctr_tab[hanum]); + GDTH_LOCK_HA(ha, flags); + + ret_val = FALSE; + if (ha->scratch_busy) { + if (((gdth_iord_str *)ha->pscratch)->size == (ulong32)size) + ret_val = TRUE; + } + GDTH_UNLOCK_HA(ha, flags); + return ret_val; +} + + static void gdth_wait_completion(int hanum, int busnum, int id) { gdth_ha_str *ha; ulong flags; int i; Scsi_Cmnd *scp; + unchar b; ha = HADATA(gdth_ctr_tab[hanum]); GDTH_LOCK_HA(ha, flags); for (i = 0; i < GDTH_MAXCMDS; ++i) { scp = ha->cmd_tab[i].cmnd; -#if LINUX_VERSION_CODE >= 0x020000 - if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && - scp->channel == (unchar)busnum) -#else - if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && - NUMDATA(scp->host)->busnum == (unchar)busnum) -#endif - { + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; + if (!SPECIAL_SCP(scp) && scp->target == (unchar)id && + b == (unchar)busnum) { scp->SCp.have_data_in = 0; GDTH_UNLOCK_HA(ha, flags); while (!scp->SCp.have_data_in) @@ -1084,19 +1240,14 @@ static void gdth_stop_timeout(int hanum, int busnum, int id) gdth_ha_str *ha; ulong flags; Scsi_Cmnd *scp; + unchar b; ha = HADATA(gdth_ctr_tab[hanum]); GDTH_LOCK_HA(ha, flags); for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { -#if LINUX_VERSION_CODE >= 0x020000 - if (scp->target == (unchar)id && - scp->channel == (unchar)busnum) -#else - if (scp->target == (unchar)id && - NUMDATA(scp->host)->busnum == (unchar)busnum) -#endif - { + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; + if (scp->target == (unchar)id && b == (unchar)busnum) { TRACE2(("gdth_stop_timeout(): update_timeout()\n")); scp->SCp.buffers_residual = gdth_update_timeout(hanum, scp, 0); } @@ -1109,19 +1260,14 @@ static void gdth_start_timeout(int hanum, int busnum, int id) gdth_ha_str *ha; ulong flags; Scsi_Cmnd *scp; + unchar b; ha = HADATA(gdth_ctr_tab[hanum]); GDTH_LOCK_HA(ha, flags); for (scp = ha->req_first; scp; scp = (Scsi_Cmnd *)scp->SCp.ptr) { -#if LINUX_VERSION_CODE >= 0x020000 - if (scp->target == (unchar)id && - scp->channel == (unchar)busnum) -#else - if (scp->target == (unchar)id && - NUMDATA(scp->host)->busnum == (unchar)busnum) -#endif - { + b = virt_ctr ? NUMDATA(scp->host)->busnum : scp->channel; + if (scp->target == (unchar)id && b == (unchar)busnum) { TRACE2(("gdth_start_timeout(): update_timeout()\n")); gdth_update_timeout(hanum, scp, scp->SCp.buffers_residual); } diff --git a/drivers/scsi/gdth_proc.h b/drivers/scsi/gdth_proc.h index d559389b7427..0ce4d032a4cd 100644 --- a/drivers/scsi/gdth_proc.h +++ b/drivers/scsi/gdth_proc.h @@ -2,24 +2,31 @@ #define _GDTH_PROC_H /* gdth_proc.h - * $Id: gdth_proc.h,v 1.7 1999/05/26 11:49:32 achim Exp $ + * $Id: gdth_proc.h,v 1.10 2000/07/24 09:30:01 achim Exp $ */ static int gdth_set_info(char *buffer,int length,int vh,int hanum,int busnum); -static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); -static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); static int gdth_get_info(char *buffer,char **start,off_t offset, int length,int vh,int hanum,int busnum); +#if LINUX_VERSION_CODE >= 0x020322 +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp); +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd *scp); +#else +static int gdth_set_asc_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +static int gdth_set_bin_info(char *buffer,int length,int hanum,Scsi_Cmnd scp); +#endif +static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd, + char *cmnd, int timeout); + static int gdth_ioctl_alloc(int hanum, ushort size); static void gdth_ioctl_free(int hanum); +static int gdth_ioctl_check_bin(int hanum, ushort size); static void gdth_wait_completion(int hanum, int busnum, int id); static void gdth_stop_timeout(int hanum, int busnum, int id); static void gdth_start_timeout(int hanum, int busnum, int id); static int gdth_update_timeout(int hanum, Scsi_Cmnd *scp, int timeout); -static void gdth_do_cmd(Scsi_Cmnd *scp, gdth_cmd_str *cmd, - char *cmnd, int timeout); void gdth_scsi_done(Scsi_Cmnd *scp); #endif diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 9210e0bfcb87..269655ea1bdf 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -2,19 +2,19 @@ * * Linux MegaRAID device driver * - * Copyright 1999 American Megatrends Inc. + * Copyright 2000 American Megatrends Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. * - * Version : 1.07b + * Version : 1.09 * * Description: Linux device driver for AMI MegaRAID controller * - * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 490 - * + * Supported controllers: MegaRAID 418, 428, 438, 466, 762, 467, 471, 490 + * 493. * History: * * Version 0.90: @@ -119,15 +119,37 @@ * Fixed the problem of unnecessary aborts in the abort entry point, which * also enables the driver to handle large amount of I/O requests for * long duration of time. - * + * Version 1.06 + * Intel Release * Version 1.07 * Removed the usage of uaccess.h file for kernel versions less than * 2.0.36, as this file is not present in those versions. * - * Version 1.07b - * The MegaRAID 466 cards with 3.00 firmware lockup and seem to very - * occasionally hang. We check such cards and report them. You can - * get firmware upgrades to flash the board to 3.10 for free. + * Version 108 + * Modified mega_ioctl so that 40LD megamanager would run + * Made some changes for 2.3.XX compilation , esp wait structures + * Code merge between 1.05 and 1.06 . + * Bug fixed problem with ioctl interface for concurrency between + * 8ld and 40ld firwmare + * Removed the flawed semaphore logic for handling new config command + * Added support for building own scatter / gather list for big user + * mode buffers + * Added /proc file system support ,so that information is available in + * human readable format + * + * Version 1a08 + * Changes for IA64 kernels. Checked for CONFIG_PROC_FS flag + * + * Version 1b08 + * Include file changes. + * Version 1b08b + * Change PCI ID value for the 471 card, use #defines when searching + * for megaraid cards. + * + * Version 1.09 + * Fixes by dledford@redhat.com and Manoj @ AMI for IOCTL_CMD_NEW handling of + * IOCTLs > 4K + * * * BUGS: * Some older 2.1 kernels (eg. 2.1.90) have a bug in pci.c that @@ -143,8 +165,8 @@ #define CRLFSTR "\n" #define IOCTL_CMD_NEW 0x81 -#define MEGARAID_VERSION "v107 (December 22, 1999)" - +#define MEGARAID_VERSION "v1.09 (August 7, 2000)" +#define MEGARAID_IOCTL_VERSION 108 #include #include @@ -163,6 +185,7 @@ MODULE_DESCRIPTION ("AMI MegaRAID driver"); #include #include +#include #include #include #include @@ -173,6 +196,8 @@ MODULE_DESCRIPTION ("AMI MegaRAID driver"); #include #include #include +#include +#include #include #include @@ -181,7 +206,11 @@ MODULE_DESCRIPTION ("AMI MegaRAID driver"); #if LINUX_VERSION_CODE < 0x20100 #include #else -#include +#if LINUX_VERSION_CODE < 0x20300 +# include +#else +# include +#endif #endif #include @@ -229,22 +258,22 @@ typedef struct { #define COM_BASE 0x2f8 -u32 RDINDOOR (mega_host_config * megaCfg) +ulong RDINDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x20); } -void WRINDOOR (mega_host_config * megaCfg, u32 value) +void WRINDOOR (mega_host_config * megaCfg, ulong value) { writel (value, megaCfg->base + 0x20); } -u32 RDOUTDOOR (mega_host_config * megaCfg) +ulong RDOUTDOOR (mega_host_config * megaCfg) { return readl (megaCfg->base + 0x2C); } -void WROUTDOOR (mega_host_config * megaCfg, u32 value) +void WROUTDOOR (mega_host_config * megaCfg, ulong value) { writel (value, megaCfg->base + 0x2C); } @@ -276,7 +305,12 @@ static void mega_Convert8ldTo40ld( mega_RAIDINQ *inquiry, #if LINUX_VERSION_CODE > 0x020100 +#if LINUX_VERSION_CODE < 0x20300 # include +# else +# include +#endif + # include # define cpuid smp_processor_id() # if LINUX_VERSION_CODE < 0x020195 @@ -329,6 +363,19 @@ static void ser_putc (char c); static int ser_printk (const char *fmt,...); #endif +#ifdef CONFIG_PROC_FS +#define COPY_BACK if (offset > megaCfg->procidx) { \ + *eof = TRUE; \ + megaCfg->procidx = 0; \ + megaCfg->procbuf[0] = 0; \ + return 0;} \ + if ((count + offset) > megaCfg->procidx) { \ + count = megaCfg->procidx - offset; \ + *eof = TRUE; } \ + memcpy(page, &megaCfg->procbuf[offset], count); \ + megaCfg->procidx = 0; \ + megaCfg->procbuf[0] = 0; +#endif /*================================================================ * * Global variables @@ -340,6 +387,8 @@ static int ser_printk (const char *fmt,...); XX scsi id on each channel. Used for Madrona motherboard, where SAF_TE processor id cannot be scanned */ + + static char *megaraid; #if LINUX_VERSION_CODE > 0x20100 #ifdef MODULE @@ -350,6 +399,7 @@ static int skip_id; static int numCtlrs = 0; static mega_host_config *megaCtlrs[FC_MAX_CHANNELS] = {0}; +struct proc_dir_entry *mega_proc_dir_entry; #if DEBUG static u32 maxCmdTime = 0; @@ -362,11 +412,20 @@ static mega_scb *pLastScb = NULL; volatile static spinlock_t serial_lock; #endif + +#if LINUX_VERSION_CODE < 0x20300 struct proc_dir_entry proc_scsi_megaraid = { PROC_SCSI_MEGARAID, 8, "megaraid", S_IFDIR | S_IRUGO | S_IXUGO, 2 }; +#else +struct proc_dir_entry *proc_scsi_megaraid; +#endif + +#ifdef CONFIG_PROC_FS +extern struct proc_dir_entry proc_root; +#endif #if SERDEBUG static char strbuf[MAX_SERBUF + 1]; @@ -554,7 +613,7 @@ printk("megaraid:ctlr max cmds = %x : MAX_CMDS = %x", megaCfg->max_cmds, MAX_COM return 0; } -/* Run through the list of completed requests */ +/* Run through the list of completed requests and finish it*/ static void mega_rundoneq (mega_host_config *megaCfg) { Scsi_Cmnd *SCpnt; @@ -597,6 +656,8 @@ mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, mega_passthru *pthru; mega_mailbox *mbox; + + if (pScb == NULL) { TRACE(("NULL pScb in mega_cmd_done!")); printk("NULL pScb in mega_cmd_done!"); @@ -628,6 +689,7 @@ mega_cmd_done (mega_host_config * megaCfg, mega_scb * pScb, if ((SCpnt->cmnd[0] & 0x80) ) {/* i.e. ioctl cmd such as 0x80, 0x81 of megamgr*/ switch (status) { + case 2: case 0xF0: case 0xF4: SCpnt->result=(DID_BAD_TARGET<<16)|status; @@ -662,12 +724,7 @@ else{ break; } } - if ( SCpnt->cmnd[0]!=IOCTL_CMD_NEW ) - /* not IOCTL_CMD_NEW SCB, freeSCB()*/ - /* For IOCTL_CMD_NEW SCB, delay freeSCB() in megaraid_queue() - * after copy data back to user space*/ mega_freeSCB(megaCfg, pScb); - /* Add Scsi_Command to end of completed queue */ if( megaCfg->qCompletedH == NULL ) { megaCfg->qCompletedH = megaCfg->qCompletedT = SCpnt; @@ -698,8 +755,8 @@ static mega_scb * mega_build_cmd (mega_host_config * megaCfg, char islogical; char lun = SCpnt->lun; - if ((SCpnt->cmnd[0] == 0x80) || (SCpnt->cmnd[0] == IOCTL_CMD_NEW) ) /* ioctl */ - return mega_ioctl (megaCfg, SCpnt); + if ((SCpnt->cmnd[0] == 0x80) || (SCpnt->cmnd[0] == IOCTL_CMD_NEW) ) + return mega_ioctl (megaCfg, SCpnt); /* Handle IOCTL command */ islogical = (SCpnt->channel == megaCfg->host->max_channel); @@ -715,7 +772,7 @@ static mega_scb * mega_build_cmd (mega_host_config * megaCfg, return NULL; } - if ( islogical ) { + if (islogical) { lun = (SCpnt->target * 8) + lun; if ( lun > FC_MAX_LOGICAL_DRIVES ){ SCpnt->result = (DID_BAD_TARGET << 16); @@ -797,6 +854,15 @@ static mega_scb * mega_build_cmd (mega_host_config * megaCfg, ((u32) SCpnt->cmnd[2] << 8) | (u32) SCpnt->cmnd[3]; mbox->lba &= 0x1FFFFF; + + if (*SCpnt->cmnd == READ_6) { + megaCfg->nReads[(int)lun]++; + megaCfg->nReadBlocks[(int)lun] += mbox->numsectors; + } + else { + megaCfg->nWrites[(int)lun]++; + megaCfg->nWriteBlocks[(int)lun] += mbox->numsectors; + } } /* 10-byte */ @@ -809,6 +875,15 @@ static mega_scb * mega_build_cmd (mega_host_config * megaCfg, ((u32) SCpnt->cmnd[3] << 16) | ((u32) SCpnt->cmnd[4] << 8) | (u32) SCpnt->cmnd[5]; + + if (*SCpnt->cmnd == READ_10) { + megaCfg->nReads[(int)lun]++; + megaCfg->nReadBlocks[(int)lun] += mbox->numsectors; + } + else { + megaCfg->nWrites[(int)lun]++; + megaCfg->nWriteBlocks[(int)lun] += mbox->numsectors; + } } /* Calculate Scatter-Gather info */ @@ -865,6 +940,42 @@ static mega_scb * mega_build_cmd (mega_host_config * megaCfg, return NULL; } +/* Handle Driver Level IOCTLs + * Return value of 0 indicates this function could not handle , so continue + * processing +*/ + +static int mega_driver_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) +{ + unsigned char *data = (unsigned char *)SCpnt->request_buffer; + mega_driver_info driver_info; + + /* If this is not our command dont do anything */ + if (SCpnt->cmnd[0] != DRIVER_IOCTL_INTERFACE) + return 0; + + switch(SCpnt->cmnd[1]) { + case GET_DRIVER_INFO: + if (SCpnt->request_bufflen < sizeof(driver_info)) { + SCpnt->result = DID_BAD_TARGET << 16 ; + callDone(SCpnt); + return 1; + } + + driver_info.size = sizeof(driver_info) - sizeof(int); + driver_info.version = MEGARAID_IOCTL_VERSION; + memcpy( data, &driver_info, sizeof(driver_info)); + break; + default: + SCpnt->result = DID_BAD_TARGET << 16; + } + + callDone(SCpnt); + return 1; +} + + + /*-------------------------------------------------------------------- * build RAID commands for controller, passed down through ioctl() *--------------------------------------------------------------------*/ @@ -875,9 +986,9 @@ static mega_scb * mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) mega_mailbox *mailbox; mega_passthru *pthru; u8 *mboxdata; - long seg; + long seg, i; unsigned char *data = (unsigned char *)SCpnt->request_buffer; - int i; + if ((pScb = mega_allocateSCB (megaCfg, SCpnt)) == NULL) { SCpnt->result = (DID_ERROR << 16); @@ -911,8 +1022,8 @@ static mega_scb * mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) (u32 *) & pthru->dataxferaddr, (u32 *) & pthru->dataxferlen); - for (i=0;i<(SCpnt->request_bufflen-cdblen-7);i++) { - data[i] = data[i+cdblen+7]; + for (i = 0 ; i < (SCpnt->request_bufflen - cdblen - 7) ; i++) { + data[i] = data[i + cdblen + 7]; } return pScb; @@ -933,24 +1044,17 @@ static mega_scb * mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) * cmnd[4..7] = external user buffer * * cmnd[8..11] = length of buffer * * */ - char *kern_area; - char *user_area = *((char **)&SCpnt->cmnd[4]); u32 xfer_size = *((u32 *)&SCpnt->cmnd[8]); - if (verify_area(VERIFY_READ, user_area, xfer_size)) { - printk("megaraid: Got bad user address.\n"); + if(!(pScb->buff_ptr = kmalloc(xfer_size, GFP_KERNEL))) { + printk("megaraid: Insufficient mem for IOCTL_CMD_NEW.\n"); SCpnt->result = (DID_ERROR << 16); callDone (SCpnt); return NULL; } - kern_area = kmalloc(xfer_size, GFP_ATOMIC | GFP_DMA); - if (kern_area == NULL) { - printk("megaraid: Couldn't allocate kernel mem.\n"); - SCpnt->result = (DID_ERROR << 16); - callDone (SCpnt); - return NULL; - } - copy_from_user(kern_area,user_area,xfer_size); - pScb->kern_area = kern_area; + pScb->sgList[0].address = virt_to_bus(pScb->buff_ptr); + pScb->sgList[0].length = xfer_size; + mbox->xferaddr = virt_to_bus(pScb->sgList); + mbox->numsgelements = 1; } #endif @@ -961,16 +1065,10 @@ static mega_scb * mega_ioctl (mega_host_config * megaCfg, Scsi_Cmnd * SCpnt) mbox->logdrv = data[4]; if(SCpnt->cmnd[0] == IOCTL_CMD_NEW) { - if(data[0]==DCMD_FC_CMD){ /*i.e. 0xA1, then override some mbox data */ + if (data[0] == DCMD_FC_CMD){ /*i.e. 0xA1, then override some mbox data */ *(mboxdata+0) = data[0]; /*mailbox byte 0: DCMD_FC_CMD*/ - *(mboxdata+2) = data[2]; /*sub command*/ - *(mboxdata+3) = 0; /*number of elements in SG list*/ - mbox->xferaddr /*i.e. mboxdata byte 0x8 to 0xb*/ - = virt_to_bus(pScb->kern_area); - } - else{ - mbox->xferaddr = virt_to_bus(pScb->kern_area); - mbox->numsgelements = 0; + *(mboxdata+2) = data[1]; /*sub command*/ + *(mboxdata+3) = mbox->numsgelements; /*number of elements in SG list*/ } } else { @@ -1065,7 +1163,7 @@ static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) #if LINUX_VERSION_CODE >= 0x20100 IO_LOCK; #endif - + megaCfg->nInterrupts++; qCnt = 0xff; while ((qCnt = megaCfg->mbox->numstatus) == 0xFF) ; @@ -1135,17 +1233,21 @@ static void megaraid_isr (int irq, void *devp, struct pt_regs *regs) continue; } - if (*(pScb->SCpnt->cmnd)==IOCTL_CMD_NEW) - { /* external user buffer */ - up(&pScb->sem); - } - /* Mark command as completed */ - mega_cmd_done(megaCfg, pScb, qStatus); - + /* We don't want the ISR routine to touch IOCTL_CMD_NEW commands, so + * don't mark them as complete, instead we pop their semaphore so + * that the queue routine can finish them off + */ + if(pScb->SCpnt->cmnd[0] == IOCTL_CMD_NEW) { + /* save the status byte for the queue routine to use */ + pScb->SCpnt->result = qStatus; + up(&pScb->ioctl_sem); + } else { + /* Mark command as completed */ + mega_cmd_done(megaCfg, pScb, qStatus); + } } else { printk("megaraid: wrong cmd id completed from firmware:id=%x\n",sIdx); - for(;;); } } @@ -1200,19 +1302,23 @@ static int megaIssueCmd (mega_host_config * megaCfg, mega_scb * pScb, int intr) { - mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; + volatile mega_mailbox *mbox = (mega_mailbox *) megaCfg->mbox; u_char byte; - u32 cmdDone; + +#ifdef __LP64__ + u64 phys_mbox; +#else u32 phys_mbox; +#endif u8 retval=-1; - mboxData[0x1] = (pScb ? pScb->idx + 1: 0x0); /* Set cmdid */ + mboxData[0x1] = (pScb ? pScb->idx + 1: 0xFE); /* Set cmdid */ mboxData[0xF] = 1; /* Set busy */ phys_mbox = virt_to_bus (megaCfg->mbox); #if DEBUG - showMbox(pScb); + ShowMbox(pScb); #endif /* Wait until mailbox is free */ @@ -1237,7 +1343,7 @@ static int megaIssueCmd (mega_host_config * megaCfg, /* Copy mailbox data into host structure */ megaCfg->mbox64->xferSegment = 0; - memcpy (mbox, mboxData, 16); + memcpy ((char *)mbox, mboxData, 16); /* Kick IO */ if (intr) { @@ -1261,10 +1367,18 @@ static int megaIssueCmd (mega_host_config * megaCfg, if (megaCfg->flag & BOARD_QUARTZ) { mbox->mraid_poll = 0; mbox->mraid_ack = 0; + mbox->numstatus = 0xFF; + mbox->status = 0xFF; WRINDOOR (megaCfg, phys_mbox | 0x1); - while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); - WROUTDOOR (megaCfg, cmdDone); + while (mbox->numstatus == 0xFF); + while (mbox->status == 0xFF); + while (mbox->mraid_poll != 0x77); + mbox->mraid_poll = 0; + mbox->mraid_ack = 0x77; + + /* while ((cmdDone = RDOUTDOOR (megaCfg)) != 0x10001234); + WROUTDOOR (megaCfg, cmdDone);*/ if (pScb) { mega_cmd_done (megaCfg, pScb, mbox->status); @@ -1361,9 +1475,15 @@ static int mega_register_mailbox (mega_host_config * megaCfg, u32 paddr) { /* align on 16-byte boundry */ megaCfg->mbox = &megaCfg->mailbox64.mailbox; - megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xfffffff0); +#ifdef __LP64__ + megaCfg->mbox = (mega_mailbox *) ((((u64) megaCfg->mbox) + 16) & ( (ulong)(-1) ^ 0x0F) ); megaCfg->mbox64 = (mega_mailbox64 *) (megaCfg->mbox - 4); - paddr = (paddr + 4 + 16) & 0xfffffff0; + paddr = (paddr + 4 + 16) & ( (u64)(-1) ^ 0x0F ); +#else + megaCfg->mbox = (mega_mailbox *) ((((u32) megaCfg->mbox) + 16) & 0xFFFFFFF0); + megaCfg->mbox64 = (mega_mailbox64 *) (megaCfg->mbox - 4); + paddr = (paddr + 4 + 16) & 0xFFFFFFF0; +#endif /* Register mailbox area with the firmware */ if (!(megaCfg->flag & BOARD_QUARTZ)) { @@ -1509,10 +1629,10 @@ static int mega_i_query_adapter (mega_host_config * megaCfg) megaCfg->productInfo.BiosVer[2] >> 8, megaCfg->productInfo.BiosVer[2] & 0x0f); #else - memcpy (megaCfg->fwVer, megaCfg->productInfo.FwVer, 4); + memcpy (megaCfg->fwVer, (char *)megaCfg->productInfo.FwVer, 4); megaCfg->fwVer[4] = 0; - memcpy (megaCfg->biosVer, megaCfg->productInfo.BiosVer, 4); + memcpy (megaCfg->biosVer, (char *)megaCfg->productInfo.BiosVer, 4); megaCfg->biosVer[4] = 0; #endif @@ -1537,6 +1657,7 @@ int megaraid_proc_info (char *buffer, char **start, off_t offset, int length, int host_no, int inout) { *start = buffer; + return 0; } @@ -1544,18 +1665,27 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, u16 pciVendor, u16 pciDev, long flag) { - mega_host_config *megaCfg; - struct Scsi_Host *host; + mega_host_config *megaCfg = NULL; + struct Scsi_Host *host = NULL; u_char pciBus, pciDevFun, megaIrq; + +#ifdef __LP64__ + u64 megaBase; +#else u32 megaBase; +#endif + u16 pciIdx = 0; u16 numFound = 0; #if LINUX_VERSION_CODE < 0x20100 while (!pcibios_find_device (pciVendor, pciDev, pciIdx, &pciBus, &pciDevFun)) { #else - +#if LINUX_VERSION_CODE > 0x20300 + struct pci_dev *pdev = NULL; +#else struct pci_dev *pdev = pci_devices; +#endif while ((pdev = pci_find_device (pciVendor, pciDev, pdev))) { pciBus = pdev->bus->number; @@ -1563,18 +1693,50 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, #endif if ((flag & BOARD_QUARTZ) && (skip_id == -1)) { u16 magic; -#if LINUX_VERSION_CODE < 0x20100 pcibios_read_config_word (pciBus, pciDevFun, PCI_CONF_AMISIG, &magic); -#else - pci_read_config_word (pdev, PCI_CONF_AMISIG, &magic); -#endif if ((magic != AMI_SIGNATURE) && (magic != AMI_SIGNATURE_471) ){ pciIdx++; continue; /* not an AMI board */ } } + +/* Hmmm...Should we not make this more modularized so that in future we dont add + for each firmware */ + + if (flag & BOARD_QUARTZ) { + /* Check to see if this is a Dell PERC RAID controller model 466 */ + u16 subsysid, subsysvid; +#if LINUX_VERSION_CODE < 0x20100 + pcibios_read_config_word (pciBus, pciDevFun, + PCI_SUBSYSTEM_VENDOR_ID, + &subsysvid); + pcibios_read_config_word (pciBus, pciDevFun, + PCI_SUBSYSTEM_ID, + &subsysid); +#else + pci_read_config_word (pdev, + PCI_SUBSYSTEM_VENDOR_ID, + &subsysvid); + pci_read_config_word (pdev, + PCI_SUBSYSTEM_ID, + &subsysid); +#endif + if ( (subsysid == 0x1111) && (subsysvid == 0x1111) && + (!strcmp(megaCfg->fwVer,"3.00") || !strcmp(megaCfg->fwVer,"3.01"))) { + printk(KERN_WARNING + "megaraid: Your card is a Dell PERC 2/SC RAID controller with firmware\n" + "megaraid: 3.00 or 3.01. This driver is known to have corruption issues\n" + "megaraid: with those firmware versions on this specific card. In order\n" + "megaraid: to protect your data, please upgrade your firmware to version\n" + "megaraid: 3.10 or later, available from the Dell Technical Support web\n" + "megaraid: site at\n" + "http://support.dell.com/us/en/filelib/download/index.asp?fileid=2489\n"); + continue; + } + } + printk (KERN_INFO "megaraid: found 0x%4.04x:0x%4.04x:idx %d:bus %d:slot %d:func %d\n", pciVendor, pciDev, @@ -1590,10 +1752,14 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, pcibios_read_config_byte (pciBus, pciDevFun, PCI_INTERRUPT_LINE, &megaIrq); -#else +#elif LINUX_VERSION_CODE < 0x20300 megaBase = pdev->base_address[0]; megaIrq = pdev->irq; +#else + megaBase = pdev->resource[0].start; + megaIrq = pdev->irq; #endif + pciIdx++; if (flag & BOARD_QUARTZ) { @@ -1632,6 +1798,7 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, megaCfg->host->n_io_port = 16; megaCfg->host->unique_id = (pciBus << 8) | pciDevFun; megaCtlrs[numCtlrs++] = megaCfg; + if (flag != BOARD_QUARTZ) { /* Request our IO Range */ if (check_region (megaBase, 16)) { @@ -1654,42 +1821,9 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, mega_register_mailbox (megaCfg, virt_to_bus ((void *) &megaCfg->mailbox64)); mega_i_query_adapter (megaCfg); - if (flag == BOARD_QUARTZ) { - /* Check to see if this is a Dell PERC RAID controller model 466 */ - u16 subsysid, subsysvid; -#if LINUX_VERSION_CODE < 0x20100 - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_VENDOR_ID, - &subsysvid); - pcibios_read_config_word (pciBus, pciDevFun, - PCI_SUBSYSTEM_ID, - &subsysid); -#else - pci_read_config_word (pdev, PCI_SUBSYSTEM_VENDOR_ID, &subsysvid); - pci_read_config_word (pdev, PCI_SUBSYSTEM_ID, &subsysid); -#endif - if ( (subsysid == 0x1111) && (subsysvid == 0x1111) && - (!strcmp(megaCfg->fwVer,"3.00") || !strcmp(megaCfg->fwVer,"3.01"))) { - printk(KERN_WARNING -"megaraid: Your card is a Dell PERC 2/SC RAID controller with firmware\n" -"megaraid: 3.00 or 3.01. This driver is known to have corruption issues\n" -"megaraid: with those firmware versions on this specific card. In order\n" -"megaraid: to protect your data, please upgrade your firmware to version\n" -"megaraid: 3.10 or later, available from the Dell Technical Support web\n" -"megaraid: site at\n" -"http://support.dell.com/us/en/filelib/download/index.asp?fileid=2940\n"); - megaraid_release (host); -#ifdef MODULE - continue; -#else - while(1) schedule_timeout(1 * HZ); -#endif - } - } - /* Initialize SCBs */ if (mega_initSCB (megaCfg)) { - megaraid_release (host); + scsi_unregister (host); continue; } @@ -1703,9 +1837,13 @@ int mega_findCard (Scsi_Host_Template * pHostTmpl, *---------------------------------------------------------*/ int megaraid_detect (Scsi_Host_Template * pHostTmpl) { - int count = 0; + int ctlridx = 0, count = 0; +#if LINUX_VERSION_CODE < 0x20300 pHostTmpl->proc_dir = &proc_scsi_megaraid; +#else + pHostTmpl->proc_name = "megaraid"; +#endif #if LINUX_VERSION_CODE < 0x20100 if (!pcibios_present ()) { @@ -1726,9 +1864,31 @@ int megaraid_detect (Scsi_Host_Template * pHostTmpl) printk ("megaraid: " MEGARAID_VERSION CRLFSTR); - count += mega_findCard (pHostTmpl, 0x101E, 0x9010, 0); - count += mega_findCard (pHostTmpl, 0x101E, 0x9060, 0); - count += mega_findCard (pHostTmpl, 0x8086, 0x1960, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID, 0); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID2, 0); + count += mega_findCard (pHostTmpl, 0x8086, + PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + count += mega_findCard (pHostTmpl, PCI_VENDOR_ID_AMI, + PCI_DEVICE_ID_AMI_MEGARAID3, BOARD_QUARTZ); + +#ifdef CONFIG_PROC_FS + if (count) { +#if LINUX_VERSION_CODE > 0x20300 + mega_proc_dir_entry = proc_mkdir("megaraid", &proc_root); +#else + mega_proc_dir_entry = create_proc_entry("megaraid", + S_IFDIR | S_IRUGO | S_IXUGO, + &proc_root); +#endif + if (!mega_proc_dir_entry) + printk("megaraid: failed to create megaraid root\n"); + else + for (ctlridx = 0; ctlridx < count ; ctlridx++) + mega_create_proc_entry(ctlridx, mega_proc_dir_entry); + } +#endif return count; } @@ -1741,6 +1901,7 @@ int megaraid_release (struct Scsi_Host *pSHost) mega_host_config *megaCfg; mega_mailbox *mbox; u_char mboxData[16]; + int i; megaCfg = (mega_host_config *) pSHost->hostdata; mbox = (mega_mailbox *) mboxData; @@ -1766,6 +1927,21 @@ int megaraid_release (struct Scsi_Host *pSHost) mega_freeSgList(megaCfg); scsi_unregister (pSHost); +#ifdef CONFIG_PROC_FS + if (megaCfg->controller_proc_dir_entry) { + remove_proc_entry("stat",megaCfg->controller_proc_dir_entry); + remove_proc_entry("status", megaCfg->controller_proc_dir_entry); + remove_proc_entry("config", megaCfg->controller_proc_dir_entry); + remove_proc_entry("mailbox", megaCfg->controller_proc_dir_entry); + for (i = 0; i < numCtlrs; i++) { + char buf[12] ={0}; + sprintf(buf,"%d",i); + remove_proc_entry(buf,mega_proc_dir_entry); + } + remove_proc_entry("megaraid", &proc_root); + } +#endif + return 0; } @@ -1818,6 +1994,7 @@ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) DRIVER_LOCK_T mega_host_config *megaCfg; mega_scb *pScb; + char *user_area = NULL; megaCfg = (mega_host_config *) SCpnt->host->hostdata; DRIVER_LOCK(megaCfg); @@ -1835,6 +2012,9 @@ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) SCpnt->scsi_done = pktComp; + if (mega_driver_ioctl(megaCfg, SCpnt)) + return 0; + /* If driver in abort or reset.. cancel this command */ if (megaCfg->flag & IN_ABORT) { SCpnt->result = (DID_ABORT << 16); @@ -1886,31 +2066,31 @@ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) megaCfg->qPcnt++; mega_runpendq(megaCfg); - -#if LINUX_VERSION_CODE > 0x020024 - if ( SCpnt->cmnd[0]==IOCTL_CMD_NEW ) - { /* user data from external user buffer */ - char *user_area; - u32 xfer_size; - - pScb->sem=MUTEX_LOCKED; - down(&pScb->sem); - - user_area = *((char **)&pScb->SCpnt->cmnd[4]); - xfer_size = *((u32 *)&pScb->SCpnt->cmnd[8]); - - copy_to_user(user_area,pScb->kern_area,xfer_size); - - kfree(pScb->kern_area); - - mega_freeSCB(megaCfg, pScb); + if(pScb->SCpnt->cmnd[0] == IOCTL_CMD_NEW) { + pScb->ioctl_sem = MUTEX_LOCKED; + spin_unlock_irq(&io_request_lock); + down(&pScb->ioctl_sem); + user_area = *((char **)&pScb->SCpnt->cmnd[4]); + if(copy_to_user(user_area,pScb->buff_ptr,pScb->sgList[0].length)) { + printk("megaraid: Error copying ioctl return value to user buffer.\n"); + pScb->SCpnt->result = (DID_ERROR << 16); } -#endif + spin_lock_irq(&io_request_lock); + DRIVER_LOCK(megaCfg); + kfree(pScb->buff_ptr); + pScb->buff_ptr = NULL; + mega_cmd_done(megaCfg, pScb, pScb->SCpnt->result); + mega_rundoneq(megaCfg); + mega_runpendq(megaCfg); + DRIVER_UNLOCK(megaCfg); } megaCfg->flag &= ~IN_QUEUE; - DRIVER_UNLOCK(megaCfg); + } + + + DRIVER_UNLOCK(megaCfg); return 0; } @@ -1919,7 +2099,13 @@ int megaraid_queue (Scsi_Cmnd * SCpnt, void (*pktComp) (Scsi_Cmnd *)) *----------------------------------------------------------------------*/ volatile static int internal_done_flag = 0; volatile static int internal_done_errcode = 0; -static struct wait_queue *internal_wait = NULL; + +#if LINUX_VERSION_CODE < 0x20300 + static struct wait_queue *internal_wait = NULL; +#else + static DECLARE_WAIT_QUEUE_HEAD(internal_wait); +#endif + static void internal_done (Scsi_Cmnd * SCpnt) { @@ -2080,6 +2266,221 @@ int megaraid_reset (Scsi_Cmnd * SCpnt, unsigned int rstflags) return rc; } + +#ifdef CONFIG_PROC_FS +/* Following code handles /proc fs */ +static int proc_printf (mega_host_config *megaCfg, const char *fmt,...) +{ + va_list args; + int i; + + if (megaCfg->procidx > PROCBUFSIZE) + return 0; + + va_start (args, fmt); + i = vsprintf ((megaCfg->procbuf + megaCfg->procidx), fmt, args); + va_end (args); + + megaCfg->procidx += i; + return i; +} + + +static int proc_read_config(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + + mega_host_config *megaCfg = (mega_host_config *)data; + + *start = page; + + if (megaCfg->productInfo.ProductName[0] != 0) + proc_printf(megaCfg,"%s\n",megaCfg->productInfo.ProductName); + + proc_printf(megaCfg,"Controller Type: "); + + if (megaCfg->flag & BOARD_QUARTZ) + proc_printf(megaCfg,"438/466/467/471/493\n"); + else + proc_printf(megaCfg,"418/428/434\n"); + + if (megaCfg->flag & BOARD_40LD) + proc_printf(megaCfg,"Controller Supports 40 Logical Drives\n"); + + proc_printf(megaCfg,"Base = %08x, Irq = %d, ",megaCfg->base, + megaCfg->host->irq); + + proc_printf(megaCfg, "Logical Drives = %d, Channels = %d\n", + megaCfg->numldrv, megaCfg->productInfo.SCSIChanPresent); + + proc_printf(megaCfg, "Version =%s:%s, DRAM = %dMb\n", + megaCfg->fwVer,megaCfg->biosVer, + megaCfg->productInfo.DramSize); + + proc_printf(megaCfg,"Controller Queue Depth = %d, Driver Queue Depth = %d\n", + megaCfg->productInfo.MaxConcCmds, megaCfg->max_cmds); + COPY_BACK; + return count; +} + + + +static int proc_read_stat(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + int i; + mega_host_config *megaCfg = (mega_host_config *)data; + + *start = page; + + proc_printf(megaCfg,"Statistical Information for this controller\n"); + proc_printf(megaCfg,"Interrupts Collected = %d\n", megaCfg->nInterrupts); + + for (i = 0; i < megaCfg->numldrv; i++) { + proc_printf(megaCfg,"Logical Drive %d:\n", i); + proc_printf(megaCfg,"\tReads Issued = %10d, Writes Issued = %10d\n", + megaCfg->nReads[i], megaCfg->nWrites[i]); + + proc_printf(megaCfg,"\tSectors Read = %10d, Sectors Written = %10d\n\n", megaCfg->nReadBlocks[i], + megaCfg->nWriteBlocks[i]); + } + + COPY_BACK; + return count; +} + + + +static int proc_read_status(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ + mega_host_config *megaCfg = (mega_host_config *)data; + *start = page; + + proc_printf(megaCfg,"TBD\n"); + COPY_BACK; + return count; +} + + +static int proc_read_mbox(char *page, char **start, off_t offset, + int count, int *eof, void *data) +{ +// int i; +// char *dta = NULL; + mega_host_config *megaCfg = (mega_host_config *)data; + volatile mega_mailbox *mbox = megaCfg->mbox; + + *start = page; + + proc_printf(megaCfg,"Contents of Mail Box Structure\n"); + proc_printf(megaCfg," Fw Command = 0x%02x\n", mbox->cmd); + proc_printf(megaCfg," Cmd Sequence = 0x%02x\n", mbox->cmdid); + proc_printf(megaCfg," No of Sectors= %04d\n", mbox->numsectors); + proc_printf(megaCfg," LBA = 0x%02x\n", mbox->lba); + proc_printf(megaCfg," DTA = 0x%08x\n", mbox->xferaddr); + proc_printf(megaCfg," Logical Drive= 0x%02x\n", mbox->logdrv); + proc_printf(megaCfg," No of SG Elmt= 0x%02x\n", mbox->numsgelements); + proc_printf(megaCfg," Busy = %01x\n", mbox->busy); + proc_printf(megaCfg," Status = 0x%02x\n", mbox->status); + +/* proc_printf(megaCfg, "Dump of MailBox\n"); + for (i = 0; i < 16; i++) + proc_printf(megaCfg, "%02x ",*(mbox + i)); + +proc_printf(megaCfg, "\n\nNumber of Status = %02d\n",mbox->numstatus); + + for (i = 0; i < 46; i++) { + proc_printf(megaCfg,"%02d ",*(mbox + 16 + i)); + if (i%16) + proc_printf(megaCfg,"\n"); +} + +if (!mbox->numsgelements) { + dta = phys_to_virt(mbox->xferaddr); + for (i = 0; i < mbox->numsgelements; i++) + if (dta) { + proc_printf(megaCfg,"Addr = %08x\n", (ulong)*(dta + i)); proc_printf(megaCfg,"Length = %08x\n", + (ulong)*(dta + i + 4)); + } +}*/ + COPY_BACK; + return count; +} + + +#if LINUX_VERSION_CODE > 0x20300 +#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string, \ + S_IRUSR | S_IFREG,\ + controller_proc_dir_entry,\ + fxn, megaCfg) +#else +#define CREATE_READ_PROC(string, fxn) create_proc_read_entry(string,S_IRUSR | S_IFREG, controller_proc_dir_entry, fxn, megaCfg) + +struct proc_dir_entry *create_proc_read_entry(const char *string, + int mode, + struct proc_dir_entry *parent, + read_proc_t *fxn, + mega_host_config *megaCfg) +{ + struct proc_dir_entry *temp = NULL; + + temp = kmalloc(sizeof(struct proc_dir_entry), + GFP_KERNEL); + if (!temp) + return NULL; + memset(temp, 0, sizeof(struct proc_dir_entry)); + + if ( (temp->name = kmalloc(strlen(string) + 1, GFP_KERNEL)) == NULL) { + kfree(temp); + return NULL; + } + + strcpy((char *)temp->name, string); + temp->namelen = strlen(string); + temp->mode = mode ; /*S_IFREG | S_IRUSR*/; + temp->data = (void *)megaCfg; + temp->read_proc = fxn; + proc_register(parent, temp); + return temp; +} +#endif + + + +void mega_create_proc_entry(int index, struct proc_dir_entry *parent) +{ + u_char string[64] = {0}; + mega_host_config *megaCfg = megaCtlrs[index]; + struct proc_dir_entry *controller_proc_dir_entry = NULL; + + sprintf(string,"%d", index); + +#if LINUX_VERSION_CODE > 0x20300 + controller_proc_dir_entry = + megaCfg->controller_proc_dir_entry = + proc_mkdir(string, parent); +#else + controller_proc_dir_entry = + megaCfg->controller_proc_dir_entry = + create_proc_entry(string,S_IFDIR | S_IRUGO | S_IXUGO, parent); +#endif + + if (!controller_proc_dir_entry) + printk("\nmegaraid: proc_mkdir failed\n"); + else + { + megaCfg->proc_read = CREATE_READ_PROC("config", proc_read_config); + megaCfg->proc_status = CREATE_READ_PROC("status", proc_read_status); + megaCfg->proc_stat = CREATE_READ_PROC("stat", proc_read_stat); + megaCfg->proc_mbox = CREATE_READ_PROC("mailbox", proc_read_mbox); + } + +} +#endif /* CONFIG_PROC_FS */ + + + /*------------------------------------------------------------- * Return the disk geometry for a particular disk * Input: diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index 5fd1744d8c9f..20f9e8962f8c 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -20,6 +20,11 @@ #define SCB_ABORTED 0x5 #define SCB_RESET 0x6 +/* IOCTL DRIVER INFO */ +#define DRIVER_IOCTL_INTERFACE 0x82 +/* Methods */ +#define GET_DRIVER_INFO 1 + #define MEGA_CMD_TIMEOUT 10 /* Feel free to fiddle with these.. max values are: @@ -97,10 +102,14 @@ /* Define AMI's PCI codes */ #undef PCI_VENDOR_ID_AMI #undef PCI_DEVICE_ID_AMI_MEGARAID +#undef PCI_DEVICE_ID_AMI_MEGARAID2 +#undef PCI_DEVICE_ID_AMI_MEGARAID3 #ifndef PCI_VENDOR_ID_AMI #define PCI_VENDOR_ID_AMI 0x101E #define PCI_DEVICE_ID_AMI_MEGARAID 0x9010 +#define PCI_DEVICE_ID_AMI_MEGARAID2 0x9060 +#define PCI_DEVICE_ID_AMI_MEGARAID3 0x1960 #endif #define PCI_CONF_BASE_ADDR_OFFSET 0x10 @@ -526,8 +535,8 @@ struct _mega_mailbox { /* 0x10 */ u8 numstatus; /* 0x11 */ u8 status; /* 0x12 */ u8 completed[46]; - u8 mraid_poll; - u8 mraid_ack; + volatile u8 mraid_poll; + volatile u8 mraid_ack; u8 pad[16]; /* for alignment purposes */ }__attribute__((packed)); typedef struct _mega_mailbox mega_mailbox; @@ -572,9 +581,8 @@ struct _mega_scb { mega_passthru pthru; Scsi_Cmnd *SCpnt; mega_sglist *sgList; - char *kern_area; /* Only used for large ioctl xfers */ - struct wait_queue *ioctl_wait; - struct semaphore sem; + struct semaphore ioctl_sem; + void *buff_ptr; mega_scb *next; }; @@ -582,7 +590,12 @@ struct _mega_scb { typedef struct _mega_host_config { u8 numldrv; u32 flag; + +#ifdef __LP64__ + u64 base; +#else u32 base; +#endif mega_scb *qFreeH; mega_scb *qFreeT; @@ -596,8 +609,10 @@ typedef struct _mega_host_config { u32 qCcnt; u32 nReads[FC_MAX_LOGICAL_DRIVES]; + u32 nReadBlocks[FC_MAX_LOGICAL_DRIVES]; u32 nWrites[FC_MAX_LOGICAL_DRIVES]; - + u32 nWriteBlocks[FC_MAX_LOGICAL_DRIVES]; + u32 nInterrupts; /* Host adapter parameters */ u8 fwVer[7]; u8 biosVer[7]; @@ -620,9 +635,22 @@ typedef struct _mega_host_config { u8 max_cmds; mega_scb scbList[MAX_COMMANDS]; +#define PROCBUFSIZE 4096 + char procbuf[PROCBUFSIZE]; + int procidx; + struct proc_dir_entry *controller_proc_dir_entry; + struct proc_dir_entry *proc_read, *proc_stat, *proc_status, *proc_mbox; } mega_host_config; +typedef struct _driver_info { + int size; + ulong version; +} mega_driver_info; + + +#if LINUX_VERSION_CODE < 0x20300 extern struct proc_dir_entry proc_scsi_megaraid; +#endif const char *megaraid_info(struct Scsi_Host *); int megaraid_detect(Scsi_Host_Template *); @@ -635,4 +663,6 @@ int megaraid_biosparam(Disk *, kdev_t, int *); int megaraid_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout); +void mega_build_user_sg(char *, ulong , mega_scb *, mega_ioctl_mbox *); +void mega_create_proc_entry(int index, struct proc_dir_entry *); #endif diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index 789e792c44e0..68db2d08cff7 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -303,6 +303,8 @@ static struct dev_info device_list[] = {"EMULEX","MD21/S2 ESDI","*", BLIST_SINGLELUN}, {"CANON","IPUBJD","*", BLIST_SPARSELUN}, {"nCipher","Fastness Crypto","*", BLIST_FORCELUN}, +{"DEC","HSG80","*", BLIST_FORCELUN}, +{"COMPAQ","LOGICAL VOLUME","*", BLIST_FORCELUN}, {"NEC","PD-1 ODX654P","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"MATSHITA","PD-1","*", BLIST_FORCELUN | BLIST_SINGLELUN}, {"iomega","jaz 1GB","J.86", BLIST_NOTQ | BLIST_NOLUN}, diff --git a/drivers/sound/msnd_pinnacle.c b/drivers/sound/msnd_pinnacle.c index 962f20e02a00..095e2354d83c 100644 --- a/drivers/sound/msnd_pinnacle.c +++ b/drivers/sound/msnd_pinnacle.c @@ -50,7 +50,9 @@ #include "sound_config.h" #include "sound_firmware.h" #ifdef MSND_CLASSIC +# ifndef __alpha__ # define SLOWIO +# endif #endif #include "msnd.h" #ifdef MSND_CLASSIC diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in index 632e3d08a5a8..2e307ed6a880 100644 --- a/drivers/usb/Config.in +++ b/drivers/usb/Config.in @@ -1,30 +1,85 @@ # # USB device configuration # -# NOTE NOTE NOTE! This is still considered extremely experimental. -# Right now hubs, mice and keyboards work - at least with UHCI. -# But that may be more a lucky coincidence than anything else.. -# -# This was all developed modularly, but I've been lazy in cleaning -# it up, so right now they are all bools. -# mainmenu_option next_comment -comment 'USB drivers - not for the faint of heart' +comment 'USB support' + +tristate 'Support for USB' CONFIG_USB +if [ ! "$CONFIG_USB" = "n" ]; then + bool ' USB verbose debug messages' CONFIG_USB_DEBUG + +comment 'Miscellaneous USB options' + bool ' Preliminary USB device filesystem' CONFIG_USB_DEVICEFS + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + bool ' Enforce USB bandwidth allocation (EXPERIMENTAL)' CONFIG_USB_BANDWIDTH + else + define_bool CONFIG_USB_BANDWIDTH n + fi + +comment 'USB Controllers' + if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then + dep_tristate ' UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB + fi + if [ "$CONFIG_USB_UHCI" != "y" ]; then + dep_tristate ' UHCI Alternate Driver (JE) support' CONFIG_USB_UHCI_ALT $CONFIG_USB + fi + dep_tristate ' OHCI (Compaq, iMacs, OPTi, SiS, ALi, ...) support' CONFIG_USB_OHCI $CONFIG_USB -if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - tristate 'Support for USB (EXPERIMENTAL!)' CONFIG_USB - if [ ! "$CONFIG_USB" = "n" ]; then - bool 'UHCI (intel PIIX4 and others) support?' CONFIG_USB_UHCI - bool 'OHCI (compaq and some others) support?' CONFIG_USB_OHCI - bool 'OHCI-HCD (other OHCI opt. Virt. Root Hub) support?' CONFIG_USB_OHCI_HCD - if [ "$CONFIG_USB_OHCI_HCD" = "y" ]; then - bool 'OHCI-HCD Virtual Root Hub' CONFIG_USB_OHCI_VROOTHUB - fi +comment 'USB Devices' + dep_tristate ' USB Printer support' CONFIG_USB_PRINTER $CONFIG_USB + dep_tristate ' USB Scanner support' CONFIG_USB_SCANNER $CONFIG_USB + dep_tristate ' USB Audio support' CONFIG_USB_AUDIO $CONFIG_USB $CONFIG_SOUND + dep_tristate ' USB Modem (CDC ACM) support' CONFIG_USB_ACM $CONFIG_USB + dep_tristate ' USB Serial Converter support' CONFIG_USB_SERIAL $CONFIG_USB + if [ "$CONFIG_USB_SERIAL" != "n" ]; then + bool ' USB Generic Serial Driver' CONFIG_USB_SERIAL_GENERIC + bool ' USB Handspring Visor Driver' CONFIG_USB_SERIAL_VISOR + if [ "$CONFIG_EXPERIMENTAL" != "n" ]; then + bool ' USB ConnectTech WhiteHEAT Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_WHITEHEAT + bool ' USB FTDI Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_FTDI_SIO + bool ' USB Keyspan PDA Single Port Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_KEYSPAN_PDA + bool ' USB Digi International AccelePort USB Serial Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_DIGI_ACCELEPORT + bool ' USB ZyXEL omni.net LCD Plus Driver (EXPERIMENTAL)' CONFIG_USB_SERIAL_OMNINET + fi + bool ' USB Serial Converter verbose debug' CONFIG_USB_SERIAL_DEBUG + fi + dep_tristate ' USB IBM (Xirlink) C-it Camera support' CONFIG_USB_IBMCAM $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB OV511 Camera support' CONFIG_USB_OV511 $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' USB Kodak DC-2xx Camera support' CONFIG_USB_DC2XX $CONFIG_USB + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' USB Mustek MDC800 Digital Camera support (EXPERIMENTAL)' CONFIG_USB_MDC800 $CONFIG_USB + dep_tristate ' USB Mass Storage support (EXPERIMENTAL)' CONFIG_USB_STORAGE $CONFIG_USB $CONFIG_SCSI + if [ "$CONFIG_USB_STORAGE" != "n" ]; then + bool ' USB Mass Storage verbose debug' CONFIG_USB_STORAGE_DEBUG + fi + fi +# dep_tristate ' USS720 parport driver' CONFIG_USB_USS720 $CONFIG_USB $CONFIG_PARPORT + dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB + if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then + dep_tristate ' PLUSB Prolific USB-Network driver (EXPERIMENTAL)' CONFIG_USB_PLUSB $CONFIG_USB $CONFIG_NET + dep_tristate ' USB ADMtek Pegasus-based device support (EXPERIMENTAL)' CONFIG_USB_PEGASUS $CONFIG_USB $CONFIG_NET + dep_tristate ' USB Diamond Rio500 support (EXPERIMENTAL)' CONFIG_USB_RIO500 $CONFIG_USB + dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV + dep_tristate ' Microtek X6USB scanner support (EXPERIMENTAL)' CONFIG_USB_MICROTEK $CONFIG_SCSI + fi + dep_tristate ' Kawasaki USB-ethernet controller' CONFIG_USB_KAWETH $CONFIG_USB - bool 'USB mouse support' CONFIG_USB_MOUSE - bool 'USB keyboard support' CONFIG_USB_KBD - bool 'USB audio parsing support' CONFIG_USB_AUDIO - fi +comment 'USB HID' + dep_tristate ' USB Human Interface Device (HID) support' CONFIG_USB_HID $CONFIG_USB + if [ "$CONFIG_USB_HID" != "y" ]; then + dep_tristate ' USB HIDBP Keyboard support' CONFIG_USB_KBD $CONFIG_USB + dep_tristate ' USB HIDBP Mouse support' CONFIG_USB_MOUSE $CONFIG_USB + fi + dep_tristate ' Wacom Intuos/Graphire tablet support' CONFIG_USB_WACOM $CONFIG_USB + dep_tristate ' Logitech WingMan Force joystick support' CONFIG_USB_WMFORCE $CONFIG_USB + dep_tristate ' Keyboard support' CONFIG_INPUT_KEYBDEV $CONFIG_USB + dep_tristate ' Mouse support' CONFIG_INPUT_MOUSEDEV $CONFIG_USB + if [ "$CONFIG_INPUT_MOUSEDEV" != "n" ]; then + int ' Horizontal screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 + int ' Vertical screen resolution' CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 + fi + dep_tristate ' Joystick support' CONFIG_INPUT_JOYDEV $CONFIG_USB + dep_tristate ' Event interface support' CONFIG_INPUT_EVDEV $CONFIG_USB fi endmenu diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 6c6e7b091cc0..a729900d00e1 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,88 +1,122 @@ # -# Makefile for the kernel usb device drivers. +# Makefile for the kernel USB device drivers. # -# Note! Dependencies are done automagically by 'make dep', which also -# removes any old dependencies. DON'T put your own dependencies here -# unless it's something special (ie not a .c file). -# -# Note 2! The CFLAGS definitions are now inherited from the -# parent makes.. -# -# This isn't actually supported yet. Don't try to use it. -SUB_DIRS := -MOD_SUB_DIRS := $(SUB_DIRS) -ALL_SUB_DIRS := $(SUB_DIRS) +# Subdirs. -L_TARGET := usb.a -M_OBJS := -L_OBJS := -LX_OBJS := -USBX_OBJS := usb.o hub.o usb-debug.o +SUB_DIRS := +MOD_SUB_DIRS := $(SUB_DIRS) +MOD_IN_SUB_DIRS := $(SUB_DIRS) +ALL_SUB_DIRS := $(SUB_DIRS) serial -ifeq ($(CONFIG_USB_MOUSE),y) - USBX_OBJS += mouse.o -endif +# The target object and module list name. -ifeq ($(CONFIG_USB_KBD),y) - USBX_OBJS += keyboard.o keymap.o -endif +O_TARGET := usbdrv.o +M_OBJS := +O_OBJS := +MOD_LIST_NAME := USB_MODULES -ifeq ($(CONFIG_USB_AUDIO),y) - USBX_OBJS += audio.o -endif +# Objects that export symbols. -ifeq ($(CONFIG_USB), y) - L_OBJS += $(USBX_OBJS) -endif +export-objs := usb.o input.o -ifeq ($(CONFIG_USB_UHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += uhci.o uhci-debug.o - else - ifeq ($(CONFIG_USB),m) - M_OBJS += usb-uhci.o - MIX_OBJS += $(USBX_OBJS) - endif - endif -endif +# Multipart objects. -ifeq ($(CONFIG_USB_OHCI),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci.o ohci-debug.o - else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci.o ohci-debug.o - M_OBJS += usb-ohci.o - MIX_OBJS += $(USBX_OBJS) - endif - endif +list-multi := usbcore.o +usbcore-objs := usb.o usb-debug.o usb-core.o hub.o + +# Optional parts of multipart objects. + +ifeq ($(CONFIG_USB_DEVICEFS),y) + usbcore-objs += devio.o inode.o drivers.o devices.o endif -ifeq ($(CONFIG_USB_OHCI_HCD),y) - ifeq ($(CONFIG_USB), y) - L_OBJS += ohci-hcd.o ohci-root-hub.o - else - ifeq ($(CONFIG_USB),m) - USBO_OBJS += ohci-hcd.o ohci-root-hub.o - M_OBJS += usb-ohci-hcd.o - MIX_OBJS += $(USBX_OBJS) - endif - endif +# Object file lists. + +obj-y := +obj-m := +obj-n := +obj- := + +# Object files in subdirectories + +ifeq ($(CONFIG_USB_SERIAL),y) + SUB_DIRS += serial + obj-y += serial/usb-serial.o +else + ifeq ($(CONFIG_USB_SERIAL),m) + MOD_IN_SUB_DIRS += serial + endif endif -include $(TOPDIR)/Rules.make -keymap.o: keymap.c -keymap.c: maps/serial.map maps/usb.map maps/fixup.map - ./mkmap > $@ +# Each configuration option enables a list of files. + +obj-$(CONFIG_USB) += usbcore.o +obj-$(CONFIG_USB_UHCI) += usb-uhci.o +obj-$(CONFIG_USB_UHCI_ALT) += uhci.o +obj-$(CONFIG_USB_OHCI) += usb-ohci.o + +obj-$(CONFIG_USB_MOUSE) += usbmouse.o input.o +obj-$(CONFIG_USB_HID) += hid.o input.o +obj-$(CONFIG_USB_KBD) += usbkbd.o input.o +obj-$(CONFIG_USB_WACOM) += wacom.o input.o +obj-$(CONFIG_USB_WMFORCE) += wmforce.o input.o +obj-$(CONFIG_INPUT_KEYBDEV) += keybdev.o input.o +obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o input.o +obj-$(CONFIG_INPUT_JOYDEV) += joydev.o input.o +obj-$(CONFIG_INPUT_EVDEV) += evdev.o input.o + +obj-$(CONFIG_USB_SCANNER) += scanner.o +obj-$(CONFIG_USB_ACM) += acm.o +obj-$(CONFIG_USB_PRINTER) += printer.o +obj-$(CONFIG_USB_AUDIO) += audio.o +obj-$(CONFIG_USB_IBMCAM) += ibmcam.o +obj-$(CONFIG_USB_DC2XX) += dc2xx.o +obj-$(CONFIG_USB_MDC800) += mdc800.o +obj-$(CONFIG_USB_STORAGE) += usb-storage.o +obj-$(CONFIG_USB_USS720) += uss720.o +obj-$(CONFIG_USB_DABUSB) += dabusb.o +obj-$(CONFIG_USB_PLUSB) += plusb.o +obj-$(CONFIG_USB_OV511) += ov511.o +obj-$(CONFIG_USB_PEGASUS) += pegasus.o +obj-$(CONFIG_USB_RIO500) += rio500.o +obj-$(CONFIG_USB_DSBR) += dsbr100.o +obj-$(CONFIG_USB_MICROTEK) += microtek.o +obj-$(CONFIG_USB_KAWETH) += kaweth.o + +# Extract lists of the multi-part drivers. +# The 'int-*' lists are the intermediate files used to build the multi's. + +multi-y := $(filter $(list-multi), $(obj-y)) +multi-m := $(filter $(list-multi), $(obj-m)) +int-y := $(sort $(foreach m, $(multi-y), $($(basename $(m))-objs))) +int-m := $(sort $(foreach m, $(multi-m), $($(basename $(m))-objs))) + +# Files that are both resident and modular: remove from modular. + +obj-m := $(filter-out $(obj-y), $(obj-m)) +int-m := $(filter-out $(int-y), $(int-m)) + +# Take multi-part drivers out of obj-y and put components in. + +obj-y := $(filter-out $(list-multi), $(obj-y)) $(int-y) + +# Translate to Rules.make lists. + +O_OBJS := $(sort $(filter-out $(export-objs), $(obj-y))) +OX_OBJS := $(sort $(filter $(export-objs), $(obj-y))) +M_OBJS := $(sort $(filter-out $(export-objs), $(obj-m))) +MX_OBJS := $(sort $(filter $(export-objs), $(obj-m))) +MI_OBJS := $(sort $(filter-out $(export-objs), $(int-m))) +MIX_OBJS := $(sort $(filter $(export-objs), $(int-m))) + +# The global Rules.make. + +include $(TOPDIR)/Rules.make -usb-uhci.o: uhci.o uhci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ uhci.o uhci-debug.o $(USBX_OBJS) +# Link rules for multi-part drivers. -usb-ohci.o: ohci.o ohci-debug.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci.o ohci-debug.o $(USBX_OBJS) +usbcore.o: $(usbcore-objs) + $(LD) -r -o $@ $(usbcore-objs) -usb-ohci-hcd.o: ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) - $(LD) $(LD_RFLAG) -r -o $@ ohci-hcd.o ohci-root-hub.o $(USBX_OBJS) - diff --git a/drivers/usb/README.kbd b/drivers/usb/README.kbd deleted file mode 100644 index 84a77d90f044..000000000000 --- a/drivers/usb/README.kbd +++ /dev/null @@ -1,65 +0,0 @@ -This is a simple USB keyboard driver written from Linus' -USB driver (started with Greg's usb-0.03b.tar.gz source -tree) - -It works fine with my BTC keyboard but I'm still investigating -trouble with my MS keyboard (trouble starts with an inability -to set into boot protocol mode, though, this very well could -be all due to crappy hardware). - -Anyway, I would appreciate you taking a look if you have -any USB keyboards lying around. Oh also, I'm doing this on -UHCI so sorry if it breaks with OHCI. - --ham - - - -Keyboard patch --------------- - -Instead of using the multiple keyboard patch and then running into all -of the kernel version problems that the current Linux-USB project has -had, I'm just mapping the USB keycodes to the standard AT-101 keycodes -and sending them directly to "handle_scancode". - -This may or may not be considered a hack. Anyway it is effective, and -I think safe, and allows USB keyboards to coexist with a serial -keyboard (oh yeah, one side effect is that you can for example hold -down the control key on the serial keyboard and press "a" on the USB -keyboard and you get Control-a like with Windows USB) and works -fine for console and X. - -You do need to make a *tiny* patch the kernel source tree so that the -function "handle_scancode" is exported from keyboard.c though. - - $ cd /usr/src/linux - $ patch -p0 < kbd.patch - -And, of course, then, you need to rebuild and install the kernel. - -** [Vojtech]: Alternately, just 'insmod kbd-stub', if you don't want -to use the keyboard and are too lazy to patch the kernel. - -Keyboard map ------------- - -I'm including a stupid utility "mkmap" which generates the USB->serial -keymap. It takes in maps/serial.map (the current serial keymap, -generated by "dumpkeys"), maps/usb.map (the USB keymap), and -maps/fixup.map (fixes for e0 keys and misc.) and spits out keymap.c -Anyway, it is not beautiful but should serve its purpose for the -moment. - -Other changes -------------- -uhci.c: - * added a context value to the irq callback function - (this is exactly like the "dev_id" field to request_irq) - * played with uhci_reset_port to get better hot-plug results - (eg. do a wait_ms(200) before calling uhci_reset_port) -usb.c: - * disconnect all devices after uhci-control thread is killed - * skip over the HID descriptor - * disconnect the high-level driver in usb_disconnect - diff --git a/drivers/usb/README.ohci b/drivers/usb/README.ohci deleted file mode 100644 index 7a544eb3353a..000000000000 --- a/drivers/usb/README.ohci +++ /dev/null @@ -1,26 +0,0 @@ -May 09, 1999 16:25:58 - -Cool, things are working "well" now. (I'm not getting oops's from the -OHCI code anyways.. ;). I can attach a usb hub and mouse in any -possible arrangement of the two and they get configured properly. - -You can see that the mouse Interrupt transfers are occuring and being -acknowledged because /proc/interrupts usb-ohci goes up accordingly with -mouse movements/events. That means the TD at least returns some data -and requeues itself. - -Device attach/detach from the root hub is not working well. Currently -every interrupt checks for root hub status changes and frame number -overflow interrupts are enabled. This means you shouldn't have to -wait more than 32-33 seconds for the change to occur, less if there is -other activity. (due to checking in the WDH caused interrupts) -My OHCI controller [SiS 5598 motherboard] doesn't seem to play well -with the RHSC interrupt so it has been disabled. The ohci_timer -should be polling but it not currently working, I haven't had time to -look into that problem. - -However, when I tried telling X to use /dev/psaux for the mouse my -machine locked up... - -- greg@electricrain.com - diff --git a/drivers/usb/acm.c b/drivers/usb/acm.c new file mode 100644 index 000000000000..7951c5895bea --- /dev/null +++ b/drivers/usb/acm.c @@ -0,0 +1,691 @@ +/* + * acm.c Version 0.16 + * + * Copyright (c) 1999 Armin Fuerst + * Copyright (c) 1999 Pavel Machek + * Copyright (c) 1999 Johannes Erdfelt + * Copyright (c) 2000 Vojtech Pavlik + * + * USB Abstract Control Model driver for USB modems and ISDN adapters + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.9 - thorough cleaning, URBification, almost a rewrite + * v0.10 - some more cleanups + * v0.11 - fixed flow control, read error doesn't stop reads + * v0.12 - added TIOCM ioctls, added break handling, made struct acm kmalloced + * v0.13 - added termios, added hangup + * v0.14 - sized down struct acm + * v0.15 - fixed flow control again - characters could be lost + * v0.16 - added code for modems with swapped data and control interfaces + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include + +/* + * CMSPAR, some architectures can't have space and mark parity. + */ + +#ifndef CMSPAR +#define CMSPAR 0 +#endif + +/* + * Major and minor numbers. + */ + +#define ACM_TTY_MAJOR 166 +#define ACM_TTY_MINORS 32 + +/* + * Requests. + */ + +#define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) + +#define ACM_REQ_COMMAND 0x00 +#define ACM_REQ_RESPONSE 0x01 +#define ACM_REQ_SET_FEATURE 0x02 +#define ACM_REQ_GET_FEATURE 0x03 +#define ACM_REQ_CLEAR_FEATURE 0x04 + +#define ACM_REQ_SET_LINE 0x20 +#define ACM_REQ_GET_LINE 0x21 +#define ACM_REQ_SET_CONTROL 0x22 +#define ACM_REQ_SEND_BREAK 0x23 + +/* + * IRQs. + */ + +#define ACM_IRQ_NETWORK 0x00 +#define ACM_IRQ_LINE_STATE 0x20 + +/* + * Output control lines. + */ + +#define ACM_CTRL_DTR 0x01 +#define ACM_CTRL_RTS 0x02 + +/* + * Input control lines and line errors. + */ + +#define ACM_CTRL_DCD 0x01 +#define ACM_CTRL_DSR 0x02 +#define ACM_CTRL_BRK 0x04 +#define ACM_CTRL_RI 0x08 + +#define ACM_CTRL_FRAMING 0x10 +#define ACM_CTRL_PARITY 0x20 +#define ACM_CTRL_OVERRUN 0x40 + +/* + * Line speed and caracter encoding. + */ + +struct acm_line { + __u32 speed; + __u8 stopbits; + __u8 parity; + __u8 databits; +} __attribute__ ((packed)); + +/* + * Internal driver structures. + */ + +struct acm { + struct usb_device *dev; /* the coresponding usb device */ + struct usb_interface *iface; /* the interfaces - +0 control +1 data */ + struct tty_struct *tty; /* the coresponding tty */ + struct urb ctrlurb, readurb, writeurb; /* urbs */ + struct acm_line line; /* line coding (bits, stop, parity) */ + struct tq_struct tqueue; /* task queue for line discipline waking up */ + unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ + unsigned int ctrlout; /* output control lines (DTR, RTS) */ + unsigned int writesize; /* max packet size for the output bulk endpoint */ + unsigned int used; /* someone has this acm's device open */ + unsigned int minor; /* acm minor number */ + unsigned char throttle; /* throttled by tty layer */ + unsigned char clocal; /* termios CLOCAL */ +}; + +static struct usb_driver acm_driver; +static struct tty_driver acm_tty_driver; +static struct acm *acm_table[ACM_TTY_MINORS] = { NULL, /* .... */ }; + +#define ACM_READY(acm) (acm && acm->dev && acm->used) + +/* + * Functions for ACM control messages. + */ + +static int acm_ctrl_msg(struct acm *acm, int request, int value, void *buf, int len) +{ + int retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), + request, USB_RT_ACM, value, acm->iface[0].altsetting[0].bInterfaceNumber, buf, len, HZ * 5); + dbg("acm_control_msg: rq: 0x%02x val: %#x len: %#x result: %d", request, value, len, retval); + return retval < 0 ? retval : 0; +} + +#define acm_set_control(acm, control) acm_ctrl_msg(acm, ACM_REQ_SET_CONTROL, control, NULL, 0) +#define acm_set_line(acm, line) acm_ctrl_msg(acm, ACM_REQ_SET_LINE, 0, line, sizeof(struct acm_line)) +#define acm_send_break(acm, ms) acm_ctrl_msg(acm, ACM_REQ_SEND_BREAK, ms, NULL, 0) + +/* + * Interrupt handler for various ACM control events + */ + +static void acm_ctrl_irq(struct urb *urb) +{ + struct acm *acm = urb->context; + devrequest *dr = urb->transfer_buffer; + unsigned char *data = (unsigned char *)(dr + 1); + int newctrl; + + if (!ACM_READY(acm)) return; + + if (urb->status < 0) { + dbg("nonzero ctrl irq status received: %d", urb->status); + return; + } + + switch (dr->request) { + + case ACM_IRQ_NETWORK: + + dbg("%s network", data[0] ? "connected to" : "disconnected from"); + return; + + case ACM_IRQ_LINE_STATE: + + newctrl = le16_to_cpup((__u16 *) data); + +#if 0 + /* Please someone tell me how to do this properly to kill pppd and not kill minicom */ + if (acm->tty && !acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + dbg("calling hangup"); + tty_hangup(acm->tty); + } +#endif + + acm->ctrlin = newctrl; + + dbg("input control lines: dcd%c dsr%c break%c ring%c framing%c parity%c overrun%c", + acm->ctrlin & ACM_CTRL_DCD ? '+' : '-', acm->ctrlin & ACM_CTRL_DSR ? '+' : '-', + acm->ctrlin & ACM_CTRL_BRK ? '+' : '-', acm->ctrlin & ACM_CTRL_RI ? '+' : '-', + acm->ctrlin & ACM_CTRL_FRAMING ? '+' : '-', acm->ctrlin & ACM_CTRL_PARITY ? '+' : '-', + acm->ctrlin & ACM_CTRL_OVERRUN ? '+' : '-'); + + return; + + default: + dbg("unknown control event received: request %d index %d len %d data0 %d data1 %d", + dr->request, dr->index, dr->length, data[0], data[1]); + return; + } +} + +static void acm_read_bulk(struct urb *urb) +{ + struct acm *acm = urb->context; + struct tty_struct *tty = acm->tty; + unsigned char *data = urb->transfer_buffer; + int i = 0; + + if (!ACM_READY(acm)) return; + + if (urb->status) + dbg("nonzero read bulk status received: %d", urb->status); + + if (!urb->status & !acm->throttle) { + for (i = 0; i < urb->actual_length && !acm->throttle; i++) + tty_insert_flip_char(tty, data[i], 0); + tty_flip_buffer_push(tty); + } + + if (acm->throttle) { + memmove(data, data + i, urb->actual_length - i); + urb->actual_length -= i; + return; + } + + urb->actual_length = 0; + + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); +} + +static void acm_write_bulk(struct urb *urb) +{ + struct acm *acm = (struct acm *)urb->context; + + if (!ACM_READY(acm)) return; + + if (urb->status) + dbg("nonzero write bulk status received: %d", urb->status); + + queue_task(&acm->tqueue, &tq_immediate); + mark_bh(IMMEDIATE_BH); +} + +static void acm_softint(void *private) +{ + struct acm *acm = private; + struct tty_struct *tty = acm->tty; + + if (!ACM_READY(acm)) return; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); +} + +/* + * TTY handlers + */ + +static int acm_tty_open(struct tty_struct *tty, struct file *filp) +{ + struct acm *acm = acm_table[MINOR(tty->device)]; + + if (!acm || !acm->dev) return -EINVAL; + + tty->driver_data = acm; + acm->tty = tty; + + MOD_INC_USE_COUNT; + + if (acm->used++) return 0; + + if (usb_submit_urb(&acm->ctrlurb)) + dbg("usb_submit_urb(ctrl irq) failed"); + + if (usb_submit_urb(&acm->readurb)) + dbg("usb_submit_urb(read bulk) failed"); + + acm_set_control(acm, acm->ctrlout = ACM_CTRL_DTR | ACM_CTRL_RTS); + + return 0; +} + +static void acm_tty_close(struct tty_struct *tty, struct file *filp) +{ + struct acm *acm = tty->driver_data; + + if (!acm || !acm->used) return; + + if (!--acm->used) { + if (acm->dev) { + acm_set_control(acm, acm->ctrlout = 0); + usb_unlink_urb(&acm->ctrlurb); + usb_unlink_urb(&acm->writeurb); + usb_unlink_urb(&acm->readurb); + } else { + tty_unregister_devfs(&acm_tty_driver, acm->minor); + acm_table[acm->minor] = NULL; + kfree(acm); + } + } + MOD_DEC_USE_COUNT; +} + +static int acm_tty_write(struct tty_struct *tty, int from_user, const unsigned char *buf, int count) +{ + struct acm *acm = tty->driver_data; + + if (!ACM_READY(acm)) return -EINVAL; + if (acm->writeurb.status == -EINPROGRESS) return 0; + if (!count) return 0; + + count = (count > acm->writesize) ? acm->writesize : count; + + if (from_user) + copy_from_user(acm->writeurb.transfer_buffer, buf, count); + else + memcpy(acm->writeurb.transfer_buffer, buf, count); + + acm->writeurb.transfer_buffer_length = count; + + if (usb_submit_urb(&acm->writeurb)) + dbg("usb_submit_urb(write bulk) failed"); + + return count; +} + +static int acm_tty_write_room(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return -EINVAL; + return acm->writeurb.status == -EINPROGRESS ? 0 : acm->writesize; +} + +static int acm_tty_chars_in_buffer(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return -EINVAL; + return acm->writeurb.status == -EINPROGRESS ? acm->writeurb.transfer_buffer_length : 0; +} + +static void acm_tty_throttle(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + acm->throttle = 1; +} + +static void acm_tty_unthrottle(struct tty_struct *tty) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + acm->throttle = 0; + if (acm->readurb.status != -EINPROGRESS) + acm_read_bulk(&acm->readurb); +} + +static void acm_tty_break_ctl(struct tty_struct *tty, int state) +{ + struct acm *acm = tty->driver_data; + if (!ACM_READY(acm)) return; + if (acm_send_break(acm, state ? 0xffff : 0)) + dbg("send break failed"); +} + +static int acm_tty_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct acm *acm = tty->driver_data; + unsigned int retval, mask, newctrl; + + if (!ACM_READY(acm)) return -EINVAL; + + switch (cmd) { + + case TIOCMGET: + + return put_user((acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | + (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | + (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | + (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | + (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | + TIOCM_CTS, (unsigned long *) arg); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + + if ((retval = get_user(mask, (unsigned long *) arg))) return retval; + + newctrl = acm->ctrlout; + mask = (mask & TIOCM_DTR ? ACM_CTRL_DTR : 0) | (mask & TIOCM_RTS ? ACM_CTRL_RTS : 0); + + switch (cmd) { + case TIOCMSET: newctrl = mask; break; + case TIOCMBIS: newctrl |= mask; break; + case TIOCMBIC: newctrl &= ~mask; break; + } + + if (acm->ctrlout == newctrl) return 0; + return acm_set_control(acm, acm->ctrlout = newctrl); + } + + return -ENOIOCTLCMD; +} + +static __u32 acm_tty_speed[] = { + 0, 50, 75, 110, 134, 150, 200, 300, 600, + 1200, 1800, 2400, 4800, 9600, 19200, 38400, + 57600, 115200, 230400, 460800, 500000, 576000, + 921600, 1000000, 1152000, 1500000, 2000000, + 2500000, 3000000, 3500000, 4000000 +}; + +static __u8 acm_tty_size[] = { + 5, 6, 7, 8 +}; + +static void acm_tty_set_termios(struct tty_struct *tty, struct termios *termios_old) +{ + struct acm *acm = tty->driver_data; + struct termios *termios = tty->termios; + struct acm_line newline; + int newctrl = acm->ctrlout; + + if (!ACM_READY(acm)) return; + + newline.speed = cpu_to_le32p(acm_tty_speed + + (termios->c_cflag & CBAUD & ~CBAUDEX) + (termios->c_cflag & CBAUDEX ? 15 : 0)); + newline.stopbits = termios->c_cflag & CSTOPB ? 2 : 0; + newline.parity = termios->c_cflag & PARENB ? + (termios->c_cflag & PARODD ? 1 : 2) + (termios->c_cflag & CMSPAR ? 2 : 0) : 0; + newline.databits = acm_tty_size[(termios->c_cflag & CSIZE) >> 4]; + + acm->clocal = termios->c_cflag & CLOCAL; + + if (!newline.speed) { + newline.speed = acm->line.speed; + newctrl &= ~ACM_CTRL_DTR; + } else newctrl |= ACM_CTRL_DTR; + + if (newctrl != acm->ctrlout) + acm_set_control(acm, acm->ctrlout = newctrl); + + if (memcmp(&acm->line, &newline, sizeof(struct acm_line))) { + memcpy(&acm->line, &newline, sizeof(struct acm_line)); + dbg("set line: %d %d %d %d", newline.speed, newline.stopbits, newline.parity, newline.databits); + acm_set_line(acm, &acm->line); + } +} + +/* + * USB probe and disconnect routines. + */ + +static void *acm_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct acm *acm; + struct usb_config_descriptor *cfacm; + struct usb_interface_descriptor *ifcom, *ifdata; + struct usb_endpoint_descriptor *epctrl, *epread, *epwrite; + int readsize, ctrlsize, minor, i; + unsigned char *buf; + + if (dev->descriptor.bDeviceClass != 2 || dev->descriptor.bDeviceSubClass != 0 + || dev->descriptor.bDeviceProtocol != 0) return NULL; + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + + cfacm = dev->config + i; + + dbg("probing config %d", cfacm->bConfigurationValue); + + if (cfacm->bNumInterfaces != 2 || + usb_interface_claimed(cfacm->interface + 0) || + usb_interface_claimed(cfacm->interface + 1)) + continue; + + ifcom = cfacm->interface[0].altsetting + 0; + ifdata = cfacm->interface[1].altsetting + 0; + + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) { + ifcom = cfacm->interface[1].altsetting + 0; + ifdata = cfacm->interface[0].altsetting + 0; + if (ifdata->bInterfaceClass != 10 || ifdata->bNumEndpoints != 2) + continue; + } + + if (ifcom->bInterfaceClass != 2 || ifcom->bInterfaceSubClass != 2 || + ifcom->bInterfaceProtocol != 1 || ifcom->bNumEndpoints != 1) + continue; + + epctrl = ifcom->endpoint + 0; + epread = ifdata->endpoint + 0; + epwrite = ifdata->endpoint + 1; + + if ((epctrl->bEndpointAddress & 0x80) != 0x80 || (epctrl->bmAttributes & 3) != 3 || + (epread->bmAttributes & 3) != 2 || (epwrite->bmAttributes & 3) != 2 || + ((epread->bEndpointAddress & 0x80) ^ (epwrite->bEndpointAddress & 0x80)) != 0x80) + continue; + + if ((epread->bEndpointAddress & 0x80) != 0x80) { + epread = ifdata->endpoint + 1; + epwrite = ifdata->endpoint + 0; + } + + usb_set_configuration(dev, cfacm->bConfigurationValue); + + for (minor = 0; minor < ACM_TTY_MINORS && acm_table[minor]; minor++); + if (acm_table[minor]) { + err("no more free acm devices"); + return NULL; + } + + if (!(acm = kmalloc(sizeof(struct acm), GFP_KERNEL))) { + err("out of memory"); + return NULL; + } + memset(acm, 0, sizeof(struct acm)); + + ctrlsize = epctrl->wMaxPacketSize; + readsize = epread->wMaxPacketSize; + acm->writesize = epwrite->wMaxPacketSize; + acm->iface = cfacm->interface; + acm->minor = minor; + acm->dev = dev; + + acm->tqueue.routine = acm_softint; + acm->tqueue.data = acm; + + if (!(buf = kmalloc(ctrlsize + readsize + acm->writesize, GFP_KERNEL))) { + err("out of memory"); + kfree(acm); + return NULL; + } + + FILL_INT_URB(&acm->ctrlurb, dev, usb_rcvintpipe(dev, epctrl->bEndpointAddress), + buf, ctrlsize, acm_ctrl_irq, acm, epctrl->bInterval); + + FILL_BULK_URB(&acm->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf += ctrlsize, readsize, acm_read_bulk, acm); + + FILL_BULK_URB(&acm->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf += readsize, acm->writesize, acm_write_bulk, acm); + + printk(KERN_INFO "ttyACM%d: USB ACM device\n", minor); + + acm_set_control(acm, acm->ctrlout); + + acm->line.speed = cpu_to_le32(9600); + acm->line.databits = 8; + acm_set_line(acm, &acm->line); + + usb_driver_claim_interface(&acm_driver, acm->iface + 0, acm); + usb_driver_claim_interface(&acm_driver, acm->iface + 1, acm); + + tty_register_devfs(&acm_tty_driver, 0, minor); + return acm_table[minor] = acm; + } + + return NULL; +} + +static void acm_disconnect(struct usb_device *dev, void *ptr) +{ + struct acm *acm = ptr; + + if (!acm || !acm->dev) { + dbg("disconnect on nonexisting interface"); + return; + } + + acm->dev = NULL; + + usb_unlink_urb(&acm->ctrlurb); + usb_unlink_urb(&acm->readurb); + usb_unlink_urb(&acm->writeurb); + + kfree(acm->ctrlurb.transfer_buffer); + + usb_driver_release_interface(&acm_driver, acm->iface + 0); + usb_driver_release_interface(&acm_driver, acm->iface + 1); + + if (!acm->used) { + tty_unregister_devfs(&acm_tty_driver, acm->minor); + acm_table[acm->minor] = NULL; + kfree(acm); + return; + } + + if (acm->tty) + tty_hangup(acm->tty); +} + +/* + * USB driver structure. + */ + +static struct usb_driver acm_driver = { + name: "acm", + probe: acm_probe, + disconnect: acm_disconnect +}; + +/* + * TTY driver structures. + */ + +static int acm_tty_refcount; + +static struct tty_struct *acm_tty_table[ACM_TTY_MINORS]; +static struct termios *acm_tty_termios[ACM_TTY_MINORS]; +static struct termios *acm_tty_termios_locked[ACM_TTY_MINORS]; + +static struct tty_driver acm_tty_driver = { + magic: TTY_DRIVER_MAGIC, + driver_name: "acm", + name: "usb/acm/%d", + major: ACM_TTY_MAJOR, + minor_start: 0, + num: ACM_TTY_MINORS, + type: TTY_DRIVER_TYPE_SERIAL, + subtype: SERIAL_TYPE_NORMAL, + flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + refcount: &acm_tty_refcount, + + table: acm_tty_table, + termios: acm_tty_termios, + termios_locked: acm_tty_termios_locked, + + open: acm_tty_open, + close: acm_tty_close, + write: acm_tty_write, + write_room: acm_tty_write_room, + ioctl: acm_tty_ioctl, + throttle: acm_tty_throttle, + unthrottle: acm_tty_unthrottle, + chars_in_buffer: acm_tty_chars_in_buffer, + break_ctl: acm_tty_break_ctl, + set_termios: acm_tty_set_termios +}; + +/* + * Init / exit. + */ + +static int __init acm_init(void) +{ + acm_tty_driver.init_termios = tty_std_termios; + acm_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + + if (tty_register_driver(&acm_tty_driver)) + return -1; + + if (usb_register(&acm_driver) < 0) { + tty_unregister_driver(&acm_tty_driver); + return -1; + } + + return 0; +} + +static void __exit acm_exit(void) +{ + usb_deregister(&acm_driver); + tty_unregister_driver(&acm_tty_driver); +} + +module_init(acm_init); +module_exit(acm_exit); diff --git a/drivers/usb/audio.c b/drivers/usb/audio.c index 8b0c9d15c748..51d8562f4314 100644 --- a/drivers/usb/audio.c +++ b/drivers/usb/audio.c @@ -1,126 +1,3717 @@ +/*****************************************************************************/ + +/* + * audio.c -- USB Audio Class driver + * + * Copyright (C) 1999, 2000 + * Alan Cox (alan@lxorguk.ukuu.org.uk) + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * + * 1999-09-07: Alan Cox + * Parsing Audio descriptor patch + * 1999-09-08: Thomas Sailer + * Added OSS compatible data io functions; both parts of the + * driver remain to be glued together + * 1999-09-10: Thomas Sailer + * Beautified the driver. Added sample format conversions. + * Still not properly glued with the parsing code. + * The parsing code seems to have its problems btw, + * Since it parses all available configs but doesn't + * store which iface/altsetting belongs to which config. + * 1999-09-20: Thomas Sailer + * Threw out Alan's parsing code and implemented my own one. + * You cannot reasonnably linearly parse audio descriptors, + * especially the AudioClass descriptors have to be considered + * pointer lists. Mixer parsing untested, due to lack of device. + * First stab at synch pipe implementation, the Dallas USB DAC + * wants to use an Asynch out pipe. usb_audio_state now basically + * only contains lists of mixer and wave devices. We can therefore + * now have multiple mixer/wave devices per USB device. + * 1999-10-28: Thomas Sailer + * Converted to URB API. Fixed a taskstate/wakeup semantics mistake + * that made the driver consume all available CPU cycles. + * Now runs stable on UHCI-Acher/Fliegl/Sailer. + * 1999-10-31: Thomas Sailer + * Audio can now be unloaded if it is not in use by any mixer + * or dsp client (formerly you had to disconnect the audio devices + * from the USB port) + * Finally, about three months after ordering, my "Maxxtro SPK222" + * speakers arrived, isn't disdata a great mail order company 8-) + * Parse class specific endpoint descriptor of the audiostreaming + * interfaces and take the endpoint attributes from there. + * Unbelievably, the Philips USB DAC has a sampling rate range + * of over a decade, yet does not support the sampling rate control! + * No wonder it sounds so bad, has very audible sampling rate + * conversion distortion. Don't try to listen to it using + * decent headphones! + * "Let's make things better" -> but please Philips start with your + * own stuff!!!! + * 1999-11-02: Thomas Sailer + * It takes the Philips boxes several seconds to acquire synchronisation + * that means they won't play short sounds. Should probably maintain + * the ISO datastream even if there's nothing to play. + * Fix counting the total_bytes counter, RealPlayer G2 depends on it. + * 1999-12-20: Thomas Sailer + * Fix bad bug in conversion to per interface probing. + * disconnect was called multiple times for the audio device, + * leading to a premature freeing of the audio structures + * 2000-05-13: Thomas Sailer + * I don't remember who changed the find_format routine, + * but the change was completely broken for the Dallas + * chip. Anyway taking sampling rate into account in find_format + * is bad and should not be done unless there are devices with + * completely broken audio descriptors. Unless someone shows + * me such a descriptor, I will not allow find_format to + * take the sampling rate into account. + * Also, the former find_format made: + * - mpg123 play mono instead of stereo + * - sox completely fail for wav's with sample rates < 44.1kHz + * for the Dallas chip. + * Also fix a rather long standing problem with applications that + * use "small" writes producing no sound at all. + * 2000-05-15: Thomas Sailer + * My fears came true, the Philips camera indeed has pretty stupid + * audio descriptors. + * 2000-05-17: Thomas Sailer + * Nemsoft spotted my stupid last minute change, thanks + * 2000-05-19: Thomas Sailer + * Fixed FEATURE_UNIT thinkos found thanks to the KC Technology + * Xtend device. Basically the driver treated FEATURE_UNIT's sourced + * by mono terminals as stereo. + * 2000-05-20: Thomas Sailer + * SELECTOR support (and thus selecting record channels from the mixer). + * Somewhat peculiar due to OSS interface limitations. Only works + * for channels where a "slider" is already in front of it (i.e. + * a MIXER unit or a FEATURE unit with volume capability). + * + */ + +/* + * Strategy: + * + * Alan Cox and Thomas Sailer are starting to dig at opposite ends and + * are hoping to meet in the middle, just like tunnel diggers :) + * Alan tackles the descriptor parsing, Thomas the actual data IO and the + * OSS compatible interface. + * + * Data IO implementation issues + * + * A mmap'able ring buffer per direction is implemented, because + * almost every OSS app expects it. It is however impractical to + * transmit/receive USB data directly into and out of the ring buffer, + * due to alignment and synchronisation issues. Instead, the ring buffer + * feeds a constant time delay line that handles the USB issues. + * + * Now we first try to find an alternate setting that exactly matches + * the sample format requested by the user. If we find one, we do not + * need to perform any sample rate conversions. If there is no matching + * altsetting, we choose the closest one and perform sample format + * conversions. We never do sample rate conversion; these are too + * expensive to be performed in the kernel. + * + * Current status: + * - Pretty stable on UHCI-Acher/Fliegl/Sailer + * - Does not work on OHCI due to lack of OHCI driver supporting URB's + * + * Generally: Due to the brokenness of the Audio Class spec + * it seems generally impossible to write a generic Audio Class driver, + * so a reasonable driver should implement the features that are actually + * used. + * + * Parsing implementation issues + * + * One cannot reasonably parse the AudioClass descriptors linearly. + * Therefore the current implementation features routines to look + * for a specific descriptor in the descriptor list. + * + * How does the parsing work? First, all interfaces are searched + * for an AudioControl class interface. If found, the config descriptor + * that belongs to the current configuration is fetched from the device. + * Then the HEADER descriptor is fetched. It contains a list of + * all AudioStreaming and MIDIStreaming devices. This list is then walked, + * and all AudioStreaming interfaces are classified into input and output + * interfaces (according to the endpoint0 direction in altsetting1) (MIDIStreaming + * is currently not supported). The input & output list is then used + * to group inputs and outputs together and issued pairwise to the + * AudioStreaming class parser. Finally, all OUTPUT_TERMINAL descriptors + * are walked and issued to the mixer construction routine. + * + * The AudioStreaming parser simply enumerates all altsettings belonging + * to the specified interface. It looks for AS_GENERAL and FORMAT_TYPE + * class specific descriptors to extract the sample format/sample rate + * data. Only sample format types PCM and PCM8 are supported right now, and + * only FORMAT_TYPE_I is handled. The isochronous data endpoint needs to + * be the first endpoint of the interface, and the optional synchronisation + * isochronous endpoint the second one. + * + * Mixer construction works as follows: The various TERMINAL and UNIT + * descriptors span a tree from the root (OUTPUT_TERMINAL) through the + * intermediate nodes (UNITs) to the leaves (INPUT_TERMINAL). We walk + * that tree in a depth first manner. FEATURE_UNITs may contribute volume, + * bass and treble sliders to the mixer, MIXER_UNITs volume sliders. + * The terminal type encoded in the INPUT_TERMINALs feeds a heuristic + * to determine "meaningful" OSS slider numbers, however we will see + * how well this works in practice. Other features are not used at the + * moment, they seem less often used. Also, it seems difficult at least + * to construct recording source switches from SELECTOR_UNITs, but + * since there are not many USB ADC's available, we leave that for later. + */ + +/*****************************************************************************/ + +#include #include #include #include #include #include -#include "usb.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -static int usb_audio_probe(struct usb_device *dev); -static void usb_audio_disconnect(struct usb_device *dev); -static LIST_HEAD(usb_audio_list); +#include "audio.h" -struct usb_audio -{ - struct usb_device *dev; +#define AUDIO_DEBUG 1 + +#define SND_DEV_DSP16 5 + +#define dprintk(x) + +/* --------------------------------------------------------------------- */ + +/* + * Linked list of all audio devices... + */ +static struct list_head audiodevs = LIST_HEAD_INIT(audiodevs); +static DECLARE_MUTEX(open_sem); + +/* + * wait queue for processes wanting to open an USB audio device + */ +static DECLARE_WAIT_QUEUE_HEAD(open_wait); + + +#define MAXFORMATS MAX_ALT +#define DMABUFSHIFT 17 /* 128k worth of DMA buffer */ +#define NRSGBUF (1U<<(DMABUFSHIFT-PAGE_SHIFT)) + +/* + * This influences: + * - Latency + * - Interrupt rate + * - Synchronisation behaviour + * Don't touch this if you don't understand all of the above. + */ +#define DESCFRAMES 5 +#define SYNCFRAMES DESCFRAMES + +#define MIXFLG_STEREOIN 1 +#define MIXFLG_STEREOOUT 2 + +struct mixerchannel { + __u16 value; + __u16 osschannel; /* number of the OSS channel */ + __s16 minval, maxval; + __u16 slctunitid; + __u8 unitid; + __u8 selector; + __u8 chnum; + __u8 flags; +}; + +struct audioformat { + unsigned int format; + unsigned int sratelo; + unsigned int sratehi; + unsigned char altsetting; + unsigned char attributes; +}; + +struct dmabuf { + /* buffer data format */ + unsigned int format; + unsigned int srate; + /* physical buffer */ + unsigned char *sgbuf[NRSGBUF]; + unsigned bufsize; + unsigned numfrag; + unsigned fragshift; + unsigned wrptr, rdptr; + unsigned total_bytes; + int count; + unsigned error; /* over/underrun */ + wait_queue_head_t wait; + /* redundant, but makes calculations easier */ + unsigned fragsize; + unsigned dmasize; + /* OSS stuff */ + unsigned mapped:1; + unsigned ready:1; + unsigned ossfragshift; + int ossmaxfrags; + unsigned subdivision; +}; + +struct usb_audio_state; + +#define FLG_URB0RUNNING 1 +#define FLG_URB1RUNNING 2 +#define FLG_SYNC0RUNNING 4 +#define FLG_SYNC1RUNNING 8 +#define FLG_RUNNING 16 +#define FLG_CONNECTED 32 + +struct my_data_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[DESCFRAMES]; +}; + +struct my_sync_urb { + urb_t urb; + iso_packet_descriptor_t isoframe[SYNCFRAMES]; +}; + + +struct usb_audiodev { + struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_audio; + + /* wave stuff */ + mode_t open_mode; + spinlock_t lock; /* DMA buffer access spinlock */ + + struct usbin { + int interface; /* Interface number, -1 means not used */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but adaptive IN mode */ + unsigned int syncinterval; /* P for adaptive IN mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + + struct dmabuf dma; + } usbin; + + struct usbout { + int interface; /* Interface number, -1 means not used */ + unsigned int format; /* USB data format */ + unsigned int datapipe; /* the data input pipe */ + unsigned int syncpipe; /* the synchronisation pipe - 0 for anything but asynchronous OUT mode */ + unsigned int syncinterval; /* P for asynchronous OUT mode, 0 otherwise */ + unsigned int freqn; /* nominal sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqm; /* momentary sampling rate in USB format, i.e. fs/1000 in Q10.14 */ + unsigned int freqmax; /* maximum sampling rate, used for buffer management */ + unsigned int phase; /* phase accumulator */ + unsigned int flags; /* see FLG_ defines */ + + struct my_data_urb durb[2]; /* ISO descriptors for the data endpoint */ + struct my_sync_urb surb[2]; /* ISO sync pipe descriptor if needed */ + + struct dmabuf dma; + } usbout; + + + unsigned int numfmtin, numfmtout; + struct audioformat fmtin[MAXFORMATS]; + struct audioformat fmtout[MAXFORMATS]; +}; + +struct usb_mixerdev { struct list_head list; + struct usb_audio_state *state; + + /* soundcore stuff */ + int dev_mixer; + + unsigned char iface; /* interface number of the AudioControl interface */ + + /* USB format descriptions */ + unsigned int numch, modcnt; + + /* mixch is last and gets allocated dynamically */ + struct mixerchannel ch[0]; }; -static struct usb_driver usb_audio_driver = -{ - "audio", - usb_audio_probe, - usb_audio_disconnect, - {NULL, NULL} +struct usb_audio_state { + struct list_head audiodev; + + /* USB device */ + struct usb_device *usbdev; + + struct list_head audiolist; + struct list_head mixerlist; + + unsigned count; /* usage counter; NOTE: the usb stack is also considered a user */ }; +/* private audio format extensions */ +#define AFMT_STEREO 0x80000000 +#define AFMT_ISSTEREO(x) ((x) & AFMT_STEREO) +#define AFMT_IS16BIT(x) ((x) & (AFMT_S16_LE|AFMT_S16_BE|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_ISUNSIGNED(x) ((x) & (AFMT_U8|AFMT_U16_LE|AFMT_U16_BE)) +#define AFMT_BYTESSHIFT(x) ((AFMT_ISSTEREO(x) ? 1 : 0) + (AFMT_IS16BIT(x) ? 1 : 0)) +#define AFMT_BYTES(x) (1<= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* --------------------------------------------------------------------- */ + +/* + * OSS compatible ring buffer management. The ring buffer may be mmap'ed into + * an application address space. + * + * I first used the rvmalloc stuff copied from bttv. Alan Cox did not like it, so + * we now use an array of pointers to a single page each. This saves us the + * kernel page table manipulations, but we have to do a page table alike mechanism + * (though only one indirection) in software. + */ + +static void dmabuf_release(struct dmabuf *db) +{ + unsigned int nr; + void *p; + + for(nr = 0; nr < NRSGBUF; nr++) { + if (!(p = db->sgbuf[nr])) + continue; + mem_map_unreserve(MAP_NR(p)); + free_page((unsigned long)p); + db->sgbuf[nr] = NULL; + } + db->mapped = db->ready = 0; } -static int usb_audio_probe(struct usb_device *dev) +static int dmabuf_init(struct dmabuf *db) { - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_audio *aud; + unsigned int nr, bytepersec, bufs; + void *p; - int i; - int na=0; - - interface = &dev->config[0].interface[0]; + /* initialize some fields */ + db->rdptr = db->wrptr = db->total_bytes = db->count = db->error = 0; + /* calculate required buffer size */ + bytepersec = db->srate << AFMT_BYTESSHIFT(db->format); + bufs = 1U << DMABUFSHIFT; + if (db->ossfragshift) { + if ((1000 << db->ossfragshift) < bytepersec) + db->fragshift = ld2(bytepersec/1000); + else + db->fragshift = db->ossfragshift; + } else { + db->fragshift = ld2(bytepersec/100/(db->subdivision ? db->subdivision : 1)); + if (db->fragshift < 3) + db->fragshift = 3; + } + db->numfrag = bufs >> db->fragshift; + while (db->numfrag < 4 && db->fragshift > 3) { + db->fragshift--; + db->numfrag = bufs >> db->fragshift; + } + db->fragsize = 1 << db->fragshift; + if (db->ossmaxfrags >= 4 && db->ossmaxfrags < db->numfrag) + db->numfrag = db->ossmaxfrags; + db->dmasize = db->numfrag << db->fragshift; + for(nr = 0; nr < NRSGBUF; nr++) { + if (!db->sgbuf[nr]) { + p = (void *)get_free_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + db->sgbuf[nr] = p; + mem_map_reserve(MAP_NR(p)); + } + memset(db->sgbuf[nr], AFMT_ISUNSIGNED(db->format) ? 0x80 : 0, PAGE_SIZE); + if ((nr << PAGE_SHIFT) >= db->dmasize) + break; + } + db->bufsize = nr << PAGE_SHIFT; + db->ready = 1; + dprintk((KERN_DEBUG "dmabuf_init: bytepersec %d bufs %d ossfragshift %d ossmaxfrags %d " + "fragshift %d fragsize %d numfrag %d dmasize %d bufsize %d fmt 0x%x\n", + bytepersec, bufs, db->ossfragshift, db->ossmaxfrags, db->fragshift, db->fragsize, + db->numfrag, db->dmasize, db->bufsize, db->format)); + return 0; +} + +static int dmabuf_mmap(struct dmabuf *db, unsigned long start, unsigned long size, pgprot_t prot) +{ + unsigned int nr; + + if (!db->ready || db->mapped || (start | size) & (PAGE_SIZE-1) || size > db->bufsize) + return -EINVAL; + size >>= PAGE_SHIFT; + for(nr = 0; nr < size; nr++) + if (!db->sgbuf[nr]) + return -EINVAL; + db->mapped = 1; + for(nr = 0; nr < size; nr++) { + if (remap_page_range(start, virt_to_phys(db->sgbuf[nr]), PAGE_SIZE, prot)) + return -EAGAIN; + start += PAGE_SIZE; + } + return 0; +} + +static void dmabuf_copyin(struct dmabuf *db, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + db->total_bytes += size; + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->wrptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->wrptr; + if (pgrem > rem) + pgrem = rem; + memcpy((db->sgbuf[db->wrptr >> PAGE_SHIFT]) + (db->wrptr & (PAGE_SIZE-1)), buffer, pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->wrptr += pgrem; + if (db->wrptr >= db->dmasize) + db->wrptr = 0; + } +} + +static void dmabuf_copyout(struct dmabuf *db, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + db->total_bytes += size; + for (;;) { + if (size <= 0) + return; + pgrem = ((~db->rdptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - db->rdptr; + if (pgrem > rem) + pgrem = rem; + memcpy(buffer, (db->sgbuf[db->rdptr >> PAGE_SHIFT]) + (db->rdptr & (PAGE_SIZE-1)), pgrem); + size -= pgrem; + (char *)buffer += pgrem; + db->rdptr += pgrem; + if (db->rdptr >= db->dmasize) + db->rdptr = 0; + } +} + +static int dmabuf_copyin_user(struct dmabuf *db, unsigned int ptr, const void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_from_user_ret((db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), buffer, pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} + +static int dmabuf_copyout_user(struct dmabuf *db, unsigned int ptr, void *buffer, unsigned int size) +{ + unsigned int pgrem, rem; + + if (!db->ready || db->mapped) + return -EINVAL; + for (;;) { + if (size <= 0) + return 0; + pgrem = ((~ptr) & (PAGE_SIZE-1)) + 1; + if (pgrem > size) + pgrem = size; + rem = db->dmasize - ptr; + if (pgrem > rem) + pgrem = rem; + copy_to_user_ret(buffer, (db->sgbuf[ptr >> PAGE_SHIFT]) + (ptr & (PAGE_SIZE-1)), pgrem, -EFAULT); + size -= pgrem; + (char *)buffer += pgrem; + ptr += pgrem; + if (ptr >= db->dmasize) + ptr = 0; + } +} + +/* --------------------------------------------------------------------- */ +/* + * USB I/O code. We do sample format conversion if necessary + */ + +static void usbin_stop(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int i, notkilled = 1; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + if (notkilled && signal_pending(current)) { + if (i & FLG_URB0RUNNING) + usb_unlink_urb(&u->durb[0].urb); + if (i & FLG_URB1RUNNING) + usb_unlink_urb(&u->durb[1].urb); + if (i & FLG_SYNC0RUNNING) + usb_unlink_urb(&u->surb[0].urb); + if (i & FLG_SYNC1RUNNING) + usb_unlink_urb(&u->surb[1].urb); + notkilled = 0; + } + } + set_current_state(TASK_RUNNING); + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = + u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL; +} + +static inline void usbin_release(struct usb_audiodev *as) +{ + usbin_stop(as); +} + +static void usbin_disc(struct usb_audiodev *as) +{ + struct usbin *u = &as->usbin; + + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); + spin_unlock_irqrestore(&as->lock, flags); + usbin_stop(as); +} + +static void conversion(const void *ibuf, unsigned int ifmt, void *obuf, unsigned int ofmt, void *tmp, unsigned int scnt) +{ + unsigned int cnt, i; + __s16 *sp, *sp2, s; + unsigned char *bp; - for(i=0;iconfig[0].bNumInterfaces;i++) - { - int x; + cnt = scnt; + if (AFMT_ISSTEREO(ifmt)) + cnt <<= 1; + sp = ((__s16 *)tmp) + cnt; + switch (ifmt & ~AFMT_STEREO) { + case AFMT_U8: + for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = (*bp ^ 0x80) << 8; + } + break; + + case AFMT_S8: + for (bp = ((unsigned char *)ibuf)+cnt, i = 0; i < cnt; i++) { + bp--; + sp--; + *sp = *bp << 8; + } + break; - endpoint = &interface->endpoint[i]; + case AFMT_U16_LE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[0] | (bp[1] << 8)) ^ 0x8000; + } + break; + + case AFMT_U16_BE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = (bp[1] | (bp[0] << 8)) ^ 0x8000; + } + break; + + case AFMT_S16_LE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[0] | (bp[1] << 8); + } + break; + + case AFMT_S16_BE: + for (bp = ((unsigned char *)ibuf)+2*cnt, i = 0; i < cnt; i++) { + bp -= 2; + sp--; + *sp = bp[1] | (bp[0] << 8); + } + break; + } + if (!AFMT_ISSTEREO(ifmt) && AFMT_ISSTEREO(ofmt)) { + /* expand from mono to stereo */ + for (sp = ((__s16 *)tmp)+scnt, sp2 = ((__s16 *)tmp)+2*scnt, i = 0; i < scnt; i++) { + sp--; + sp2 -= 2; + sp2[0] = sp2[1] = sp[0]; + } + } + if (AFMT_ISSTEREO(ifmt) && !AFMT_ISSTEREO(ofmt)) { + /* contract from stereo to mono */ + for (sp = sp2 = ((__s16 *)tmp), i = 0; i < scnt; i++, sp++, sp2 += 2) + sp[0] = (sp2[0] + sp2[1]) >> 1; + } + cnt = scnt; + if (AFMT_ISSTEREO(ofmt)) + cnt <<= 1; + sp = ((__s16 *)tmp); + bp = ((unsigned char *)obuf); + switch (ofmt & ~AFMT_STEREO) { + case AFMT_U8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = (*sp >> 8) ^ 0x80; + break; + + case AFMT_S8: + for (i = 0; i < cnt; i++, sp++, bp++) + *bp = *sp >> 8; + break; + + case AFMT_U16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = (s >> 8) ^ 0x80; + } + break; + + case AFMT_U16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = (s >> 8) ^ 0x80; + } + break; - if(interface->bInterfaceClass != 1) - continue; + case AFMT_S16_LE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[0] = s; + bp[1] = s >> 8; + } + break; - printk(KERN_INFO "USB audio device detected.\n"); + case AFMT_S16_BE: + for (i = 0; i < cnt; i++, sp++, bp += 2) { + s = *sp; + bp[1] = s; + bp[0] = s >> 8; + } + break; + } - switch(interface->bInterfaceSubClass) - { - case 0x01: - printk(KERN_INFO "audio: Control device.\n"); - break; - case 0x02: - printk(KERN_INFO "audio: streaming.\n"); - break; - case 0x03: - printk(KERN_INFO "audio: nonstreaming.\n"); - break; +} + +static void usbin_convert(struct usbin *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + conversion(buffer, u->format, tmp.b, u->dma.format, tmp.b, scnt); + dmabuf_copyin(&u->dma, tmp.b, scnt << dfmtsh); + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbin_prepare_desc(struct usbin *u, purb_t urb) +{ + unsigned int i, maxsize, offs; + + maxsize = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + //printk(KERN_DEBUG "usbin_prepare_desc: maxsize %d freq 0x%x format 0x%x\n", maxsize, u->freqn, u->format); + for (i = offs = 0; i < DESCFRAMES; i++, offs += maxsize) { + urb->iso_frame_desc[i].length = maxsize; + urb->iso_frame_desc[i].offset = offs; + } + return 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + * convert sample format on the fly if necessary + */ +static int usbin_retire_desc(struct usbin *u, purb_t urb) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, dmafree; + unsigned char *cp; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = 0; i < DESCFRAMES; i++) { + cp = ((unsigned char *)urb->transfer_buffer) + urb->iso_frame_desc[i].offset; + if (urb->iso_frame_desc[i].status) { + dprintk((KERN_DEBUG "usbin_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); + continue; + } + scnt = urb->iso_frame_desc[i].actual_length >> ufmtsh; + if (!scnt) + continue; + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + dmafree = u->dma.dmasize - u->dma.count; + if (cnt > dmafree) { + scnt = dmafree >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } } - na++; + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyin(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbin_convert(u, cp, scnt); + } + } + if (err) + u->dma.error++; + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + return err ? -1 : 0; +} + +static void usbin_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbin_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->durb[0].urb) + mask = FLG_URB0RUNNING; + else if (urb == &u->durb[1].urb) + mask = FLG_URB1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbin_completed: panic: unknown URB\n"); } + spin_lock_irqsave(&as->lock, flags); + if (!usbin_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbin_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + printk(KERN_DEBUG "usbin_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret); + } + spin_unlock_irqrestore(&as->lock, flags); +} + +/* + * we output sync data + */ +static int usbin_sync_prepare_desc(struct usbin *u, purb_t urb) +{ + unsigned char *cp = urb->transfer_buffer; + unsigned int i, offs; - if(na==0) - return -1; + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3, cp += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + cp[0] = u->freqn; + cp[1] = u->freqn >> 8; + cp[2] = u->freqn >> 16; + } + return 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbin_sync_retire_desc(struct usbin *u, purb_t urb) +{ + unsigned int i; + + for (i = 0; i < SYNCFRAMES; i++) + if (urb->iso_frame_desc[0].status) + dprintk((KERN_DEBUG "usbin_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); + return 0; +} - aud = kmalloc(sizeof(struct usb_audio), GFP_KERNEL); - if(aud) - { - memset(aud, 0, sizeof(*aud)); - aud->dev = dev; - dev->private = aud; +static void usbin_sync_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbin *u = &as->usbin; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; - endpoint = &interface->endpoint[0]; +#if 0 + printk(KERN_DEBUG "usbin_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->surb[0].urb) + mask = FLG_SYNC0RUNNING; + else if (urb == &u->surb[1].urb) + mask = FLG_SYNC1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbin_sync_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbin_sync_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbin_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + dprintk((KERN_DEBUG "usbin_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); + } + spin_unlock_irqrestore(&as->lock, flags); +} -// usb_set_configuration(dev, dev->config[0].bConfigurationValue); -// usb_set_protocol(dev, 0); -// usb_set_idle(dev, 0, 0); - - usb_request_irq(dev, - usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), - usb_audio_irq, - endpoint->bInterval, - aud); +static int usbin_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbin *u = &as->usbin; + purb_t urb; + unsigned long flags; + unsigned int maxsze, bufsz; - list_add(&aud->list, &usb_audio_list); +#if 0 + printk(KERN_DEBUG "usbin_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } + if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); + u->freqn = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); + u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); + } + if (u->dma.count >= u->dma.dmasize && !u->dma.mapped) + return 0; + u->flags |= FLG_RUNNING; + if (!(u->flags & FLG_URB0RUNNING)) { + urb = &u->durb[0].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbin_completed; + if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { + urb = &u->durb[1].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbin_completed; + if (!usbin_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { + urb = &u->surb[0].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbin_sync_completed; + /* stride: u->syncinterval */ + if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { + urb = &u->surb[1].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbin_sync_completed; + /* stride: u->syncinterval */ + if (!usbin_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } } + spin_unlock_irqrestore(&as->lock, flags); return 0; } -static void usb_audio_disconnect(struct usb_device *dev) +static void usbout_stop(struct usb_audiodev *as) { - struct usb_audio *aud = (struct usb_audio*) dev->private; - if(aud) - { - dev->private = NULL; - list_del(&aud->list); - kfree(aud); + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int i, notkilled = 1; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~FLG_RUNNING; + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + while (i & (FLG_URB0RUNNING|FLG_URB1RUNNING|FLG_SYNC0RUNNING|FLG_SYNC1RUNNING)) { + set_current_state(notkilled ? TASK_INTERRUPTIBLE : TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock_irqsave(&as->lock, flags); + i = u->flags; + spin_unlock_irqrestore(&as->lock, flags); + if (notkilled && signal_pending(current)) { + if (i & FLG_URB0RUNNING) + usb_unlink_urb(&u->durb[0].urb); + if (i & FLG_URB1RUNNING) + usb_unlink_urb(&u->durb[1].urb); + if (i & FLG_SYNC0RUNNING) + usb_unlink_urb(&u->surb[0].urb); + if (i & FLG_SYNC1RUNNING) + usb_unlink_urb(&u->surb[1].urb); + notkilled = 0; + } } - printk(KERN_INFO "USB audio driver removed.\n"); + set_current_state(TASK_RUNNING); + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = u->durb[1].urb.transfer_buffer = + u->surb[0].urb.transfer_buffer = u->surb[1].urb.transfer_buffer = NULL; } -int usb_audio_init(void) +static inline void usbout_release(struct usb_audiodev *as) { - usb_register(&usb_audio_driver); + usbout_stop(as); +} + +static void usbout_disc(struct usb_audiodev *as) +{ + struct usbout *u = &as->usbout; + unsigned long flags; + + spin_lock_irqsave(&as->lock, flags); + u->flags &= ~(FLG_RUNNING | FLG_CONNECTED); + spin_unlock_irqrestore(&as->lock, flags); + usbout_stop(as); +} + +static void usbout_convert(struct usbout *u, unsigned char *buffer, unsigned int samples) +{ + union { + __s16 s[64]; + unsigned char b[0]; + } tmp; + unsigned int scnt, maxs, ufmtsh, dfmtsh; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + maxs = (AFMT_ISSTEREO(u->dma.format | u->format)) ? 32 : 64; + while (samples > 0) { + scnt = samples; + if (scnt > maxs) + scnt = maxs; + dmabuf_copyout(&u->dma, tmp.b, scnt << dfmtsh); + conversion(tmp.b, u->dma.format, buffer, u->format, tmp.b, scnt); + buffer += scnt << ufmtsh; + samples -= scnt; + } +} + +static int usbout_prepare_desc(struct usbout *u, purb_t urb) +{ + unsigned int i, ufmtsh, dfmtsh, err = 0, cnt, scnt, offs; + unsigned char *cp = urb->transfer_buffer; + + ufmtsh = AFMT_BYTESSHIFT(u->format); + dfmtsh = AFMT_BYTESSHIFT(u->dma.format); + for (i = offs = 0; i < DESCFRAMES; i++) { + urb->iso_frame_desc[i].offset = offs; + u->phase = (u->phase & 0x3fff) + u->freqm; + scnt = u->phase >> 14; + if (!scnt) { + urb->iso_frame_desc[i].length = 0; + continue; + } + cnt = scnt << dfmtsh; + if (!u->dma.mapped) { + if (cnt > u->dma.count) { + scnt = u->dma.count >> dfmtsh; + cnt = scnt << dfmtsh; + err++; + } + u->dma.count -= cnt; + } else + u->dma.count += cnt; + if (u->format == u->dma.format) { + /* we do not need format conversion */ + dmabuf_copyout(&u->dma, cp, cnt); + } else { + /* we need sampling format conversion */ + usbout_convert(u, cp, scnt); + } + cnt = scnt << ufmtsh; + urb->iso_frame_desc[i].length = cnt; + offs += cnt; + cp += cnt; + } + if (err) + u->dma.error++; + if (u->dma.mapped) { + if (u->dma.count >= (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } else { + if ((signed)u->dma.dmasize >= u->dma.count + (signed)u->dma.fragsize) + wake_up(&u->dma.wait); + } + return err ? -1 : 0; +} + +/* + * return value: 0 if descriptor should be restarted, -1 otherwise + */ +static int usbout_retire_desc(struct usbout *u, purb_t urb) +{ + unsigned int i; + + for (i = 0; i < DESCFRAMES; i++) { + if (urb->iso_frame_desc[i].status) { + dprintk((KERN_DEBUG "usbout_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); + continue; + } + } + return 0; +} + +static void usbout_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbout_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->durb[0].urb) + mask = FLG_URB0RUNNING; + else if (urb == &u->durb[1].urb) + mask = FLG_URB1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbout_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbout_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbout_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + dprintk((KERN_DEBUG "usbout_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); + } + spin_unlock_irqrestore(&as->lock, flags); +} + +static int usbout_sync_prepare_desc(struct usbout *u, purb_t urb) +{ + unsigned int i, offs; + + for (i = offs = 0; i < SYNCFRAMES; i++, offs += 3) { + urb->iso_frame_desc[i].length = 3; + urb->iso_frame_desc[i].offset = offs; + } return 0; } /* - * Support functions for parsing + * return value: 0 if descriptor should be restarted, -1 otherwise */ - -void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) +static int usbout_sync_retire_desc(struct usbout *u, purb_t urb) +{ + unsigned char *cp = urb->transfer_buffer; + unsigned int f, i; + + for (i = 0; i < SYNCFRAMES; i++, cp += 3) { + if (urb->iso_frame_desc[i].status) { + dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u status %d\n", i, urb->iso_frame_desc[i].status)); + continue; + } + if (urb->iso_frame_desc[i].actual_length < 3) { + dprintk((KERN_DEBUG "usbout_sync_retire_desc: frame %u length %d\n", i, urb->iso_frame_desc[i].actual_length)); + continue; + } + f = cp[0] | (cp[1] << 8) | (cp[2] << 16); + if (abs(f - u->freqn) > (u->freqn >> 3) || f > u->freqmax) { + printk(KERN_WARNING "usbout_sync_retire_desc: requested frequency %u (nominal %u) out of range!\n", f, u->freqn); + continue; + } + u->freqm = f; + } + return 0; +} + +static void usbout_sync_completed(struct urb *urb) +{ + struct usb_audiodev *as = (struct usb_audiodev *)urb->context; + struct usbout *u = &as->usbout; + unsigned long flags; + unsigned int mask; + int suret = USB_ST_NOERROR; + +#if 0 + printk(KERN_DEBUG "usbout_sync_completed: status %d errcnt %d flags 0x%x\n", urb->status, urb->error_count, u->flags); +#endif + if (urb == &u->surb[0].urb) + mask = FLG_SYNC0RUNNING; + else if (urb == &u->surb[1].urb) + mask = FLG_SYNC1RUNNING; + else { + mask = 0; + printk(KERN_ERR "usbout_sync_completed: panic: unknown URB\n"); + } + spin_lock_irqsave(&as->lock, flags); + if (!usbout_sync_retire_desc(u, urb) && + u->flags & FLG_RUNNING && + !usbout_sync_prepare_desc(u, urb) && + (suret = usb_submit_urb(urb)) == USB_ST_NOERROR) { + u->flags |= mask; + } else { + u->flags &= ~(mask | FLG_RUNNING); + wake_up(&u->dma.wait); + dprintk((KERN_DEBUG "usbout_sync_completed: descriptor not restarted (usb_submit_urb: %d)\n", suret)); + } + spin_unlock_irqrestore(&as->lock, flags); +} + +static int usbout_start(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usbout *u = &as->usbout; + purb_t urb; + unsigned long flags; + unsigned int maxsze, bufsz; + +#if 0 + printk(KERN_DEBUG "usbout_start: device %d ufmt 0x%08x dfmt 0x%08x srate %d\n", + dev->devnum, u->format, u->dma.format, u->dma.srate); +#endif + /* allocate USB storage if not already done */ + spin_lock_irqsave(&as->lock, flags); + if (!(u->flags & FLG_CONNECTED)) { + spin_unlock_irqrestore(&as->lock, flags); + return -EIO; + } + if (!(u->flags & FLG_RUNNING)) { + spin_unlock_irqrestore(&as->lock, flags); + u->freqn = u->freqm = ((u->dma.srate << 11) + 62) / 125; /* this will overflow at approx 2MSPS */ + u->freqmax = u->freqn + (u->freqn >> 2); + u->phase = 0; + maxsze = (u->freqmax + 0x3fff) >> (14 - AFMT_BYTESSHIFT(u->format)); + bufsz = DESCFRAMES * maxsze; + if (u->durb[0].urb.transfer_buffer) + kfree(u->durb[0].urb.transfer_buffer); + u->durb[0].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[0].urb.transfer_buffer_length = bufsz; + if (u->durb[1].urb.transfer_buffer) + kfree(u->durb[1].urb.transfer_buffer); + u->durb[1].urb.transfer_buffer = kmalloc(bufsz, GFP_KERNEL); + u->durb[1].urb.transfer_buffer_length = bufsz; + if (u->syncpipe) { + if (u->surb[0].urb.transfer_buffer) + kfree(u->surb[0].urb.transfer_buffer); + u->surb[0].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[0].urb.transfer_buffer_length = 3*SYNCFRAMES; + if (u->surb[1].urb.transfer_buffer) + kfree(u->surb[1].urb.transfer_buffer); + u->surb[1].urb.transfer_buffer = kmalloc(3*SYNCFRAMES, GFP_KERNEL); + u->surb[1].urb.transfer_buffer_length = 3*SYNCFRAMES; + } + if (!u->durb[0].urb.transfer_buffer || !u->durb[1].urb.transfer_buffer || + (u->syncpipe && (!u->surb[0].urb.transfer_buffer || !u->surb[1].urb.transfer_buffer))) { + printk(KERN_ERR "usbaudio: cannot start playback device %d\n", dev->devnum); + return 0; + } + spin_lock_irqsave(&as->lock, flags); + } + if (u->dma.count <= 0 && !u->dma.mapped) + return 0; + u->flags |= FLG_RUNNING; + if (!(u->flags & FLG_URB0RUNNING)) { + urb = &u->durb[0].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbout_completed; + if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_URB1RUNNING)) { + urb = &u->durb[1].urb; + urb->dev = dev; + urb->pipe = u->datapipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = DESCFRAMES; + urb->context = as; + urb->complete = usbout_completed; + if (!usbout_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_URB1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->syncpipe) { + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC0RUNNING)) { + urb = &u->surb[0].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbout_sync_completed; + /* stride: u->syncinterval */ + if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC0RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + if (u->flags & FLG_RUNNING && !(u->flags & FLG_SYNC1RUNNING)) { + urb = &u->surb[1].urb; + urb->dev = dev; + urb->pipe = u->syncpipe; + urb->transfer_flags = USB_ISO_ASAP; + urb->number_of_packets = SYNCFRAMES; + urb->context = as; + urb->complete = usbout_sync_completed; + /* stride: u->syncinterval */ + if (!usbout_sync_prepare_desc(u, urb) && !usb_submit_urb(urb)) + u->flags |= FLG_SYNC1RUNNING; + else + u->flags &= ~FLG_RUNNING; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static unsigned int format_goodness(struct audioformat *afp, unsigned int fmt, unsigned int srate) +{ + unsigned int g = 0; + + if (srate < afp->sratelo) + g += afp->sratelo - srate; + if (srate > afp->sratehi) + g += srate - afp->sratehi; + if (AFMT_ISSTEREO(afp->format) && !AFMT_ISSTEREO(fmt)) + g += 0x100000; + if (!AFMT_ISSTEREO(afp->format) && AFMT_ISSTEREO(fmt)) + g += 0x400000; + if (AFMT_IS16BIT(afp->format) && !AFMT_IS16BIT(fmt)) + g += 0x100000; + if (!AFMT_IS16BIT(afp->format) && AFMT_IS16BIT(fmt)) + g += 0x400000; + return g; +} + +static int find_format(struct audioformat *afp, unsigned int nr, unsigned int fmt, unsigned int srate) +{ + unsigned int i, g, gb = ~0; + int j = -1; /* default to failure */ + + /* find "best" format (according to format_goodness) */ + for (i = 0; i < nr; i++) { + g = format_goodness(&afp[i], fmt, srate); + if (g >= gb) + continue; + j = i; + gb = g; + } + return j; +} + +static int set_format_in(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbin *u = &as->usbin; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int ep; + unsigned char data[3]; + int fmtnr, ret; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + + fmtnr = find_format(as->fmtin, as->numfmtin, d->format, d->srate); + if (fmtnr < 0) { + printk(KERN_ERR "usbaudio: set_format_in(): failed to find desired format/speed combination.\n"); + return -1; + } + + fmt = as->fmtin + fmtnr; + alts = &iface->altsetting[fmt->altsetting]; + u->format = fmt->format; + u->datapipe = usb_rcvisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x08) { + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress & 0x7f)) { + printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_sndisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + dprintk((KERN_DEBUG "usb_audio: set_format_in: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting)); + + if (usb_set_interface(dev, alts->bInterfaceNumber, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & 0x02) { + data[0] = 1; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + } + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & 0x01) { + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set input sampling frequency device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to get input sampling frequency device %d interface %u endpoint 0x%x\n", + ret, dev->devnum, u->interface, ep); + return -1; + } + dprintk((KERN_DEBUG "usbaudio: set_format_in: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16))); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + } + return 0; +} + +static int set_format_out(struct usb_audiodev *as) +{ + struct usb_device *dev = as->state->usbdev; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct usbout *u = &as->usbout; + struct dmabuf *d = &u->dma; + struct audioformat *fmt; + unsigned int ep; + unsigned char data[3]; + int fmtnr, ret; + + if (u->interface < 0 || u->interface >= config->bNumInterfaces) + return 0; + iface = &config->interface[u->interface]; + + fmtnr = find_format(as->fmtout, as->numfmtout, d->format, d->srate); + if (fmtnr < 0) { + printk(KERN_ERR "usbaudio: set_format_out(): failed to find desired format/speed combination.\n"); + return -1; + } + + fmt = as->fmtout + fmtnr; + u->format = fmt->format; + alts = &iface->altsetting[fmt->altsetting]; + u->datapipe = usb_sndisocpipe(dev, alts->endpoint[0].bEndpointAddress & 0xf); + u->syncpipe = u->syncinterval = 0; + if ((alts->endpoint[0].bmAttributes & 0x0c) == 0x04) { +#if 0 + printk(KERN_DEBUG "bNumEndpoints 0x%02x endpoint[1].bmAttributes 0x%02x\n" + KERN_DEBUG "endpoint[1].bSynchAddress 0x%02x endpoint[1].bEndpointAddress 0x%02x\n" + KERN_DEBUG "endpoint[0].bSynchAddress 0x%02x\n", alts->bNumEndpoints, + alts->endpoint[1].bmAttributes, alts->endpoint[1].bSynchAddress, + alts->endpoint[1].bEndpointAddress, alts->endpoint[0].bSynchAddress); +#endif + if (alts->bNumEndpoints < 2 || + alts->endpoint[1].bmAttributes != 0x01 || + alts->endpoint[1].bSynchAddress != 0 || + alts->endpoint[1].bEndpointAddress != (alts->endpoint[0].bSynchAddress | 0x80)) { + printk(KERN_ERR "usbaudio: device %d interface %d altsetting %d invalid synch pipe\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + u->syncpipe = usb_rcvisocpipe(dev, alts->endpoint[1].bEndpointAddress & 0xf); + u->syncinterval = alts->endpoint[1].bRefresh; + } + if (d->srate < fmt->sratelo) + d->srate = fmt->sratelo; + if (d->srate > fmt->sratehi) + d->srate = fmt->sratehi; + + dprintk((KERN_DEBUG "usb_audio: set_format_out: usb_set_interface %u %u\n", alts->bInterfaceNumber, fmt->altsetting)); + + if (usb_set_interface(dev, u->interface, fmt->altsetting) < 0) { + printk(KERN_WARNING "usbaudio: usb_set_interface failed, device %d interface %d altsetting %d\n", + dev->devnum, u->interface, fmt->altsetting); + return -1; + } + if (fmt->sratelo == fmt->sratehi) + return 0; + ep = usb_pipeendpoint(u->datapipe) | (u->datapipe & USB_DIR_IN); + /* if endpoint has pitch control, enable it */ + if (fmt->attributes & 0x02) { + data[0] = 1; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + PITCH_CONTROL << 8, ep, data, 1, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output pitch control device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + } + /* if endpoint has sampling rate control, set it */ + if (fmt->attributes & 0x01) { + data[0] = d->srate; + data[1] = d->srate >> 8; + data[2] = d->srate >> 16; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_OUT, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to set output sampling frequency device %d interface %u endpoint 0x%x to %u\n", + ret, dev->devnum, u->interface, ep, d->srate); + return -1; + } + if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_TYPE_CLASS|USB_RECIP_ENDPOINT|USB_DIR_IN, + SAMPLING_FREQ_CONTROL << 8, ep, data, 3, HZ)) < 0) { + printk(KERN_ERR "usbaudio: failure (error %d) to get output sampling frequency device %d interface %u endpoint 0x%x\n", + ret, dev->devnum, u->interface, ep); + return -1; + } + dprintk((KERN_DEBUG "usbaudio: set_format_out: device %d interface %d altsetting %d srate req: %u real %u\n", + dev->devnum, u->interface, fmt->altsetting, d->srate, data[0] | (data[1] << 8) | (data[2] << 16))); + d->srate = data[0] | (data[1] << 8) | (data[2] << 16); + } + return 0; +} + +static int set_format(struct usb_audiodev *s, unsigned int fmode, unsigned int fmt, unsigned int srate) +{ + int ret1 = 0, ret2 = 0; + + if (!(fmode & (FMODE_READ|FMODE_WRITE))) + return -EINVAL; + if (fmode & FMODE_READ) { + usbin_stop(s); + s->usbin.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbin.dma.format; + else + s->usbin.dma.format = fmt; + if (!srate) + srate = s->usbin.dma.srate; + else + s->usbin.dma.srate = srate; + } + if (fmode & FMODE_WRITE) { + usbout_stop(s); + s->usbout.dma.ready = 0; + if (fmt == AFMT_QUERY) + fmt = s->usbout.dma.format; + else + s->usbout.dma.format = fmt; + if (!srate) + srate = s->usbout.dma.srate; + else + s->usbout.dma.srate = srate; + } + if (fmode & FMODE_READ) + ret1 = set_format_in(s); + if (fmode & FMODE_WRITE) + ret2 = set_format_out(s); + return ret1 ? ret1 : ret2; +} + +/* --------------------------------------------------------------------- */ + +static int wrmixer(struct usb_mixerdev *ms, unsigned mixch, unsigned value) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned char data[2]; + struct mixerchannel *ch; + int v1, v2, v3; + + if (mixch >= ms->numch) + return -1; + ch = &ms->ch[mixch]; + v3 = ch->maxval - ch->minval; + v1 = value & 0xff; + v2 = (value >> 8) & 0xff; + if (v1 > 100) + v1 = 100; + if (v2 > 100) + v2 = 100; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + v2 = v1; + ch->value = v1 | (v2 << 8); + v1 = (v1 * v3) / 100 + ch->minval; + v2 = (v2 * v3) / 100 + ch->minval; + switch (ch->selector) { + case 0: /* mixer unit request */ + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->chnum << 8) | 1, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + /* various feature unit controls */ + case VOLUME_CONTROL: + data[0] = v1; + data[1] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2; + data[1] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 2, HZ) < 0) + goto err; + return 0; + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + data[0] = v1 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | ch->chnum, ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + if (!(ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 0; + data[0] = v2 >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (ch->selector << 8) | (ch->chnum + 1), ms->iface | (ch->unitid << 8), data, 1, HZ) < 0) + goto err; + return 0; + + default: + return -1; + } + return 0; + + err: + printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, ms->iface, ch->unitid, ch->chnum, ch->selector); + return -1; +} + +static int get_rec_src(struct usb_mixerdev *ms) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, retmask = 0; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + mask |= 1 << j; + if (buf == (ms->ch[j].slctunitid >> 8)) + retmask |= 1 << ms->ch[j].osschannel; + } + } + if (err) + return -EIO; + return retmask; +} + +static int set_rec_src(struct usb_mixerdev *ms, int srcmask) +{ + struct usb_device *dev = ms->state->usbdev; + unsigned int mask = 0, smask, bmask; + unsigned int i, j; + unsigned char buf; + int err = 0; + + for (i = 0; i < ms->numch; i++) { + if (!ms->ch[i].slctunitid || (mask & (1 << i))) + continue; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + 0, ms->iface | (ms->ch[i].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector read request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[i].slctunitid & 0xff); + continue; + } + /* first generate smask */ + smask = bmask = 0; + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + smask |= 1 << ms->ch[j].osschannel; + if (buf == (ms->ch[j].slctunitid >> 8)) + bmask |= 1 << ms->ch[j].osschannel; + mask |= 1 << j; + } + /* check for multiple set sources */ + j = hweight32(srcmask & smask); + if (j == 0) + continue; + if (j > 1) + srcmask &= ~bmask; + for (j = i; j < ms->numch; j++) { + if ((ms->ch[i].slctunitid ^ ms->ch[j].slctunitid) & 0xff) + continue; + if (!(srcmask & (1 << ms->ch[j].osschannel))) + continue; + buf = ms->ch[j].slctunitid >> 8; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + 0, ms->iface | (ms->ch[j].slctunitid << 8), &buf, 1, HZ) < 0) { + err = -EIO; + printk(KERN_ERR "usbaudio: selector write request device %u if %u unit %u failed\n", + dev->devnum, ms->iface, ms->ch[j].slctunitid & 0xff); + continue; + } + } + } + return err ? -EIO : 0; +} + +/* --------------------------------------------------------------------- */ + +/* + * should be called with open_sem hold, so that no new processes + * look at the audio device to be destroyed + */ + +static void release(struct usb_audio_state *s) +{ + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + s->count--; + if (s->count) { + up(&open_sem); + return; + } + up(&open_sem); + wake_up(&open_wait); + while (!list_empty(&s->audiolist)) { + as = list_entry(s->audiolist.next, struct usb_audiodev, list); + list_del(&as->list); + usbin_release(as); + usbout_release(as); + dmabuf_release(&as->usbin.dma); + dmabuf_release(&as->usbout.dma); + kfree(as); + } + while (!list_empty(&s->mixerlist)) { + ms = list_entry(s->mixerlist.next, struct usb_mixerdev, list); + list_del(&ms->list); + kfree(ms); + } + kfree(s); +} + +extern inline int prog_dmabuf_in(struct usb_audiodev *as) +{ + usbin_stop(as); + return dmabuf_init(&as->usbin.dma); +} + +extern inline int prog_dmabuf_out(struct usb_audiodev *as) +{ + usbout_stop(as); + return dmabuf_init(&as->usbout.dma); +} + +/* --------------------------------------------------------------------- */ + +static loff_t usb_audio_llseek(struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +/* --------------------------------------------------------------------- */ + +static int usb_audio_open_mixdev(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + struct list_head *devs, *mdevs; + struct usb_mixerdev *ms; + struct usb_audio_state *s; + + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (mdevs = s->mixerlist.next; mdevs != &s->mixerlist; mdevs = mdevs->next) { + ms = list_entry(mdevs, struct usb_mixerdev, list); + if (ms->dev_mixer == minor) + goto mixer_found; + } + } + up(&open_sem); + return -ENODEV; + + mixer_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + file->private_data = ms; + s->count++; + + up(&open_sem); + return 0; +} + +static int usb_audio_release_mixdev(struct inode *inode, struct file *file) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + struct usb_audio_state *s = ms->state; + + down(&open_sem); + release(s); + return 0; +} + +static int usb_audio_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_mixerdev *ms = (struct usb_mixerdev *)file->private_data; + int i, j, val; + + if (!ms->state->usbdev) + return -ENODEV; + + if (cmd == SOUND_MIXER_INFO) { + mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + info.modify_counter = ms->modcnt; + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == SOUND_OLD_MIXER_INFO) { + _old_mixer_info info; + strncpy(info.id, "USB_AUDIO", sizeof(info.id)); + strncpy(info.name, "USB Audio Class Driver", sizeof(info.name)); + if (copy_to_user((void *)arg, &info, sizeof(info))) + return -EFAULT; + return 0; + } + if (cmd == OSS_GETVERSION) + return put_user(SOUND_VERSION, (int *)arg); + if (_IOC_TYPE(cmd) != 'M' || _IOC_SIZE(cmd) != sizeof(int)) + return -EINVAL; + if (_IOC_DIR(cmd) == _IOC_READ) { + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + val = get_rec_src(ms); + if (val < 0) + return val; + return put_user(val, (int *)arg); + + case SOUND_MIXER_DEVMASK: /* Arg contains a bit for each supported device */ + for (val = i = 0; i < ms->numch; i++) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_RECMASK: /* Arg contains a bit for each supported recording source */ + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].slctunitid) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_STEREODEVS: /* Mixer channels supporting stereo */ + for (val = i = 0; i < ms->numch; i++) + if (ms->ch[i].flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) + val |= 1 << ms->ch[i].osschannel; + return put_user(val, (int *)arg); + + case SOUND_MIXER_CAPS: + return put_user(SOUND_CAP_EXCL_INPUT, (int *)arg); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch; j++) { + if (ms->ch[j].osschannel == i) { + return put_user(ms->ch[j].value, (int *)arg); + } + } + return -EINVAL; + } + } + if (_IOC_DIR(cmd) != (_IOC_READ|_IOC_WRITE)) + return -EINVAL; + ms->modcnt++; + switch (_IOC_NR(cmd)) { + case SOUND_MIXER_RECSRC: /* Arg contains a bit for each recording source */ + get_user_ret(val, (int *)arg, -EFAULT); + return set_rec_src(ms, val); + + default: + i = _IOC_NR(cmd); + if (i >= SOUND_MIXER_NRDEVICES) + return -EINVAL; + for (j = 0; j < ms->numch && ms->ch[j].osschannel != i; j++); + if (j >= ms->numch) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (wrmixer(ms, j, val)) + return -EIO; + return put_user(ms->ch[j].value, (int *)arg); + } +} + +static /*const*/ struct file_operations usb_mixer_fops = { + llseek: usb_audio_llseek, + ioctl: usb_audio_ioctl_mixdev, + open: usb_audio_open_mixdev, + release: usb_audio_release_mixdev, +}; + +/* --------------------------------------------------------------------- */ + +static int drain_out(struct usb_audiodev *as, int nonblock) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long flags; + int count, tmo; + + if (as->usbout.dma.mapped || !as->usbout.dma.ready) + return 0; + usbout_start(as); + add_wait_queue(&as->usbout.dma.wait, &wait); + for (;;) { + __set_current_state(TASK_INTERRUPTIBLE); + spin_lock_irqsave(&as->lock, flags); + count = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + if (count <= 0) + break; + if (signal_pending(current)) + break; + if (nonblock) { + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + return -EBUSY; + } + tmo = 3 * HZ * count / as->usbout.dma.srate; + tmo >>= AFMT_BYTESSHIFT(as->usbout.dma.format); + if (!schedule_timeout(tmo + 1)) { + printk(KERN_DEBUG "usbaudio: dma timed out??\n"); + break; + } + } + remove_wait_queue(&as->usbout.dma.wait, &wait); + set_current_state(TASK_RUNNING); + if (signal_pending(current)) + return -ERESTARTSYS; + return 0; +} + +/* --------------------------------------------------------------------- */ + +static ssize_t usb_audio_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbin.dma.mapped) + return -ENXIO; + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + if (!access_ok(VERIFY_WRITE, buffer, count)) + return -EFAULT; + add_wait_queue(&as->usbin.dma.wait, &wait); + while (count > 0) { + spin_lock_irqsave(&as->lock, flags); + ptr = as->usbin.dma.rdptr; + cnt = as->usbin.dma.count; + /* set task state early to avoid wakeup races */ + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (usbin_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyout_user(&as->usbin.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbin.dma.dmasize) + ptr -= as->usbin.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbin.dma.rdptr = ptr; + as->usbin.dma.count -= cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbin.dma.wait, &wait); + return ret; +} + +static ssize_t usb_audio_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + DECLARE_WAITQUEUE(wait, current); + ssize_t ret = 0; + unsigned long flags; + unsigned int ptr; + unsigned int start_thr; + int cnt, err; + + if (ppos != &file->f_pos) + return -ESPIPE; + if (as->usbout.dma.mapped) + return -ENXIO; + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + if (!access_ok(VERIFY_READ, buffer, count)) + return -EFAULT; + start_thr = (as->usbout.dma.srate << AFMT_BYTESSHIFT(as->usbout.dma.format)) / (1000 / (3 * DESCFRAMES)); + add_wait_queue(&as->usbout.dma.wait, &wait); + while (count > 0) { +#if 0 + printk(KERN_DEBUG "usb_audio_write: count %u dma: count %u rdptr %u wrptr %u dmasize %u fragsize %u flags 0x%02x taskst 0x%lx\n", + count, as->usbout.dma.count, as->usbout.dma.rdptr, as->usbout.dma.wrptr, as->usbout.dma.dmasize, as->usbout.dma.fragsize, + as->usbout.flags, current->state); +#endif + spin_lock_irqsave(&as->lock, flags); + if (as->usbout.dma.count < 0) { + as->usbout.dma.count = 0; + as->usbout.dma.rdptr = as->usbout.dma.wrptr; + } + ptr = as->usbout.dma.wrptr; + cnt = as->usbout.dma.dmasize - as->usbout.dma.count; + /* set task state early to avoid wakeup races */ + if (cnt <= 0) + __set_current_state(TASK_INTERRUPTIBLE); + spin_unlock_irqrestore(&as->lock, flags); + if (cnt > count) + cnt = count; + if (cnt <= 0) { + if (usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + break; + } + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + break; + } + continue; + } + if ((err = dmabuf_copyin_user(&as->usbout.dma, ptr, buffer, cnt))) { + if (!ret) + ret = err; + break; + } + ptr += cnt; + if (ptr >= as->usbout.dma.dmasize) + ptr -= as->usbout.dma.dmasize; + spin_lock_irqsave(&as->lock, flags); + as->usbout.dma.wrptr = ptr; + as->usbout.dma.count += cnt; + spin_unlock_irqrestore(&as->lock, flags); + count -= cnt; + buffer += cnt; + ret += cnt; + if (as->usbout.dma.count >= start_thr && usbout_start(as)) { + if (!ret) + ret = -ENODEV; + break; + } + } + __set_current_state(TASK_RUNNING); + remove_wait_queue(&as->usbout.dma.wait, &wait); + return ret; +} + +/* Called without the kernel lock - fine */ +static unsigned int usb_audio_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + unsigned long flags; + unsigned int mask = 0; + + if (file->f_mode & FMODE_WRITE) { + if (!as->usbout.dma.ready) + prog_dmabuf_out(as); + poll_wait(file, &as->usbout.dma.wait, wait); + } + if (file->f_mode & FMODE_READ) { + if (!as->usbin.dma.ready) + prog_dmabuf_in(as); + poll_wait(file, &as->usbin.dma.wait, wait); + } + spin_lock_irqsave(&as->lock, flags); + if (file->f_mode & FMODE_READ) { + if (as->usbin.dma.count >= (signed)as->usbin.dma.fragsize) + mask |= POLLIN | POLLRDNORM; + } + if (file->f_mode & FMODE_WRITE) { + if (as->usbout.dma.mapped) { + if (as->usbout.dma.count >= (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } else { + if ((signed)as->usbout.dma.dmasize >= as->usbout.dma.count + (signed)as->usbout.dma.fragsize) + mask |= POLLOUT | POLLWRNORM; + } + } + spin_unlock_irqrestore(&as->lock, flags); + return mask; +} + +static int usb_audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct dmabuf *db; + int ret; + + if (vma->vm_flags & VM_WRITE) { + if ((ret = prog_dmabuf_out(as)) != 0) + return ret; + db = &as->usbout.dma; + } else if (vma->vm_flags & VM_READ) { + if ((ret = prog_dmabuf_in(as)) != 0) + return ret; + db = &as->usbin.dma; + } else + return -EINVAL; + + return dmabuf_mmap(db, vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot); +} + +static int usb_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + unsigned long flags; + audio_buf_info abinfo; + count_info cinfo; + int val, val2, mapped, ret; + + if (!s->usbdev) + return -EIO; + mapped = ((file->f_mode & FMODE_WRITE) && as->usbout.dma.mapped) || + ((file->f_mode & FMODE_READ) && as->usbin.dma.mapped); + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_SYNC: + if (file->f_mode & FMODE_WRITE) + return drain_out(as, 0/*file->f_flags & O_NONBLOCK*/); + return 0; + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_GETCAPS: + return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | + DSP_CAP_MMAP | DSP_CAP_BATCH, (int *)arg); + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + as->usbout.dma.rdptr = as->usbout.dma.wrptr = as->usbout.dma.count = as->usbout.dma.total_bytes = 0; + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + as->usbin.dma.rdptr = as->usbin.dma.wrptr = as->usbin.dma.count = as->usbin.dma.total_bytes = 0; + } + return 0; + + case SNDCTL_DSP_SPEED: + get_user_ret(val, (int *)arg, -EFAULT); + if (val >= 0) { + if (val < 4000) + val = 4000; + if (val > 100000) + val = 100000; + if (set_format(as, file->f_mode, AFMT_QUERY, val)) + return -EIO; + } + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SNDCTL_DSP_STEREO: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (set_format(as, file->f_mode, val2 | AFMT_STEREO, 0)) + return -EIO; + return 0; + + case SNDCTL_DSP_CHANNELS: + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 0) { + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + if (val == 1) + val2 &= ~AFMT_STEREO; + else + val2 |= AFMT_STEREO; + if (set_format(as, file->f_mode, val2, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SNDCTL_DSP_GETFMTS: /* Returns a mask */ + return put_user(AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE, (int *)arg); + + case SNDCTL_DSP_SETFMT: /* Selects ONE fmt*/ + get_user_ret(val, (int *)arg, -EFAULT); + if (val != AFMT_QUERY) { + if (hweight32(val) != 1) + return -EINVAL; + if (!(val & (AFMT_U8 | AFMT_U16_LE | AFMT_U16_BE | + AFMT_S8 | AFMT_S16_LE | AFMT_S16_BE))) + return -EINVAL; + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + val |= val2 & AFMT_STEREO; + if (set_format(as, file->f_mode, val, 0)) + return -EIO; + } + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(val2 & ~AFMT_STEREO, (int *)arg); + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && as->usbin.flags & FLG_RUNNING) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && as->usbout.flags & FLG_RUNNING) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!as->usbin.dma.ready && (ret = prog_dmabuf_in(as))) + return ret; + if (usbin_start(as)) + return -ENODEV; + } else + usbin_stop(as); + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!as->usbout.dma.ready && (ret = prog_dmabuf_out(as))) + return ret; + if (usbout_start(as)) + return -ENODEV; + } else + usbout_stop(as); + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + if (!(as->usbout.flags & FLG_RUNNING) && (val = prog_dmabuf_out(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbout.dma.fragsize; + abinfo.bytes = as->usbout.dma.dmasize - as->usbout.dma.count; + abinfo.fragstotal = as->usbout.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbout.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_GETISPACE: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + if (!(as->usbin.flags & FLG_RUNNING) && (val = prog_dmabuf_in(as)) != 0) + return val; + spin_lock_irqsave(&as->lock, flags); + abinfo.fragsize = as->usbin.dma.fragsize; + abinfo.bytes = as->usbin.dma.count; + abinfo.fragstotal = as->usbin.dma.numfrag; + abinfo.fragments = abinfo.bytes >> as->usbin.dma.fragshift; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_GETODELAY: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + val = as->usbout.dma.count; + spin_unlock_irqrestore(&as->lock, flags); + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETIPTR: + if (!(file->f_mode & FMODE_READ)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbin.dma.total_bytes; + cinfo.blocks = as->usbin.dma.count >> as->usbin.dma.fragshift; + cinfo.ptr = as->usbin.dma.wrptr; + if (as->usbin.dma.mapped) + as->usbin.dma.count &= as->usbin.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETOPTR: + if (!(file->f_mode & FMODE_WRITE)) + return -EINVAL; + spin_lock_irqsave(&as->lock, flags); + cinfo.bytes = as->usbout.dma.total_bytes; + cinfo.blocks = as->usbout.dma.count >> as->usbout.dma.fragshift; + cinfo.ptr = as->usbout.dma.rdptr; + if (as->usbout.dma.mapped) + as->usbout.dma.count &= as->usbout.dma.fragsize-1; + spin_unlock_irqrestore(&as->lock, flags); + return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) { + if ((val = prog_dmabuf_out(as))) + return val; + return put_user(as->usbout.dma.fragsize, (int *)arg); + } + if ((val = prog_dmabuf_in(as))) + return val; + return put_user(as->usbin.dma.fragsize, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + get_user_ret(val, (int *)arg, -EFAULT); + if (file->f_mode & FMODE_READ) { + as->usbin.dma.ossfragshift = val & 0xffff; + as->usbin.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbin.dma.ossfragshift < 4) + as->usbin.dma.ossfragshift = 4; + if (as->usbin.dma.ossfragshift > 15) + as->usbin.dma.ossfragshift = 15; + if (as->usbin.dma.ossmaxfrags < 4) + as->usbin.dma.ossmaxfrags = 4; + } + if (file->f_mode & FMODE_WRITE) { + as->usbout.dma.ossfragshift = val & 0xffff; + as->usbout.dma.ossmaxfrags = (val >> 16) & 0xffff; + if (as->usbout.dma.ossfragshift < 4) + as->usbout.dma.ossfragshift = 4; + if (as->usbout.dma.ossfragshift > 15) + as->usbout.dma.ossfragshift = 15; + if (as->usbout.dma.ossmaxfrags < 4) + as->usbout.dma.ossmaxfrags = 4; + } + return 0; + + case SNDCTL_DSP_SUBDIVIDE: + if ((file->f_mode & FMODE_READ && as->usbin.dma.subdivision) || + (file->f_mode & FMODE_WRITE && as->usbout.dma.subdivision)) + return -EINVAL; + get_user_ret(val, (int *)arg, -EFAULT); + if (val != 1 && val != 2 && val != 4) + return -EINVAL; + if (file->f_mode & FMODE_READ) + as->usbin.dma.subdivision = val; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.subdivision = val; + return 0; + + case SOUND_PCM_READ_RATE: + return put_user((file->f_mode & FMODE_READ) ? as->usbin.dma.srate : as->usbout.dma.srate, (int *)arg); + + case SOUND_PCM_READ_CHANNELS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_ISSTEREO(val2) ? 2 : 1, (int *)arg); + + case SOUND_PCM_READ_BITS: + val2 = (file->f_mode & FMODE_READ) ? as->usbin.dma.format : as->usbout.dma.format; + return put_user(AFMT_IS16BIT(val2) ? 16 : 8, (int *)arg); + + case SOUND_PCM_WRITE_FILTER: + case SNDCTL_DSP_SETSYNCRO: + case SOUND_PCM_READ_FILTER: + return -EINVAL; + } + return -ENOIOCTLCMD; +} + +static int usb_audio_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + DECLARE_WAITQUEUE(wait, current); + struct list_head *devs, *adevs; + struct usb_audiodev *as; + struct usb_audio_state *s; + + for (;;) { + down(&open_sem); + for (devs = audiodevs.next; devs != &audiodevs; devs = devs->next) { + s = list_entry(devs, struct usb_audio_state, audiodev); + for (adevs = s->audiolist.next; adevs != &s->audiolist; adevs = adevs->next) { + as = list_entry(adevs, struct usb_audiodev, list); + if (!((as->dev_audio ^ minor) & ~0xf)) + goto device_found; + } + } + up(&open_sem); + return -ENODEV; + + device_found: + if (!s->usbdev) { + up(&open_sem); + return -EIO; + } + /* wait for device to become free */ + if (!(as->open_mode & file->f_mode)) + break; + if (file->f_flags & O_NONBLOCK) { + up(&open_sem); + return -EBUSY; + } + __set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&open_wait, &wait); + up(&open_sem); + schedule(); + __set_current_state(TASK_RUNNING); + remove_wait_queue(&open_wait, &wait); + if (signal_pending(current)) + return -ERESTARTSYS; + } + if (file->f_mode & FMODE_READ) + as->usbin.dma.ossfragshift = as->usbin.dma.ossmaxfrags = as->usbin.dma.subdivision = 0; + if (file->f_mode & FMODE_WRITE) + as->usbout.dma.ossfragshift = as->usbout.dma.ossmaxfrags = as->usbout.dma.subdivision = 0; + if (set_format(as, file->f_mode, ((minor & 0xf) == SND_DEV_DSP16) ? AFMT_S16_LE : AFMT_U8 /* AFMT_ULAW */, 8000)) { + up(&open_sem); + return -EIO; + } + file->private_data = as; + as->open_mode |= file->f_mode & (FMODE_READ | FMODE_WRITE); + s->count++; + up(&open_sem); + return 0; +} + +static int usb_audio_release(struct inode *inode, struct file *file) +{ + struct usb_audiodev *as = (struct usb_audiodev *)file->private_data; + struct usb_audio_state *s = as->state; + struct usb_device *dev = s->usbdev; + struct usb_interface *iface; + + if (file->f_mode & FMODE_WRITE) + drain_out(as, file->f_flags & O_NONBLOCK); + down(&open_sem); + if (file->f_mode & FMODE_WRITE) { + usbout_stop(as); + if (dev && as->usbout.interface >= 0) { + iface = &dev->actconfig->interface[as->usbout.interface]; + usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0); + } + dmabuf_release(&as->usbout.dma); + usbout_release(as); + } + if (file->f_mode & FMODE_READ) { + usbin_stop(as); + if (dev && as->usbin.interface >= 0) { + iface = &dev->actconfig->interface[as->usbin.interface]; + usb_set_interface(dev, iface->altsetting->bInterfaceNumber, 0); + } + dmabuf_release(&as->usbin.dma); + usbin_release(as); + } + as->open_mode &= (~file->f_mode) & (FMODE_READ|FMODE_WRITE); + release(s); + wake_up(&open_wait); + return 0; +} + +static /*const*/ struct file_operations usb_audio_fops = { + llseek: usb_audio_llseek, + read: usb_audio_read, + write: usb_audio_write, + poll: usb_audio_poll, + ioctl: usb_audio_ioctl, + mmap: usb_audio_mmap, + open: usb_audio_open, + release: usb_audio_release, +}; + +/* --------------------------------------------------------------------- */ + +static void * usb_audio_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_audio_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver usb_audio_driver = { + "audio", + usb_audio_probe, + usb_audio_disconnect, + LIST_HEAD_INIT(usb_audio_driver.driver_list), + NULL, + 0 +}; + +static void *find_descriptor(void *descstart, unsigned int desclen, void *after, + u8 dtype, int iface, int altsetting) +{ + u8 *p, *end, *next; + int ifc = -1, as = -1; + + p = descstart; + end = p + desclen; + for (; p < end;) { + if (p[0] < 2) + return NULL; + next = p + p[0]; + if (next > end) + return NULL; + if (p[1] == USB_DT_INTERFACE) { + /* minimum length of interface descriptor */ + if (p[0] < 9) + return NULL; + ifc = p[2]; + as = p[3]; + } + if (p[1] == dtype && (!after || (void *)p > after) && + (iface == -1 || iface == ifc) && (altsetting == -1 || altsetting == as)) { + return p; + } + p = next; + } + return NULL; +} + +static void *find_csinterface_descriptor(void *descstart, unsigned int desclen, void *after, u8 dsubtype, int iface, int altsetting) +{ + unsigned char *p; + + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, altsetting); + while (p) { + if (p[0] >= 3 && p[2] == dsubtype) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, altsetting); + } + return NULL; +} + +static void *find_audiocontrol_unit(void *descstart, unsigned int desclen, void *after, u8 unit, int iface) +{ + unsigned char *p; + + p = find_descriptor(descstart, desclen, after, USB_DT_CS_INTERFACE, iface, -1); + while (p) { + if (p[0] >= 4 && p[2] >= INPUT_TERMINAL && p[2] <= EXTENSION_UNIT && p[3] == unit) + return p; + p = find_descriptor(descstart, desclen, p, USB_DT_CS_INTERFACE, iface, -1); + } + return NULL; +} + +static void usb_audio_parsestreaming(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, int asifin, int asifout) +{ + struct usb_device *dev = s->usbdev; + struct usb_audiodev *as; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface_descriptor *alts; + struct usb_interface *iface; + struct audioformat *fp; + unsigned char *fmt, *csep; + unsigned int i, j, k, format; + + if (!(as = kmalloc(sizeof(struct usb_audiodev), GFP_KERNEL))) + return; + memset(as, 0, sizeof(struct usb_audiodev)); + init_waitqueue_head(&as->usbin.dma.wait); + init_waitqueue_head(&as->usbout.dma.wait); + spin_lock_init(&as->lock); + as->state = s; + as->usbin.interface = asifin; + as->usbout.interface = asifout; + /* search for input formats */ + if (asifin >= 0) { + as->usbin.flags = FLG_CONNECTED; + iface = &config->interface[asifin]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifin, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + !(alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous in\n", + dev->devnum, asifin, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifin, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifin, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifin, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifin, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifin, i, fmt[4], fmt[5]); + continue; + } + csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifin, i); + if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", + dev->devnum, asifin, i); + continue; + } + if (as->numfmtin >= MAXFORMATS) + continue; + fp = &as->fmtin[as->numfmtin++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + fp->attributes = csep[3]; + printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", + dev->devnum, asifin, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); + } + } + /* search for output formats */ + if (asifout >= 0) { + as->usbout.flags = FLG_CONNECTED; + iface = &config->interface[asifout]; + for (i = 0; i < iface->num_altsetting; i++) { + alts = &iface->altsetting[i]; + if (alts->bInterfaceClass != USB_CLASS_AUDIO || alts->bInterfaceSubClass != 2) + continue; + if (alts->bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u does not have an endpoint\n", + dev->devnum, asifout, i); + continue; + } + if ((alts->endpoint[0].bmAttributes & 0x03) != 0x01 || + (alts->endpoint[0].bEndpointAddress & 0x80)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u first endpoint not isochronous out\n", + dev->devnum, asifout, i); + continue; + } + fmt = find_csinterface_descriptor(buffer, buflen, NULL, AS_GENERAL, asifout, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 7 || fmt[6] != 0 || (fmt[5] != 1 && fmt[5] != 2)) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u format not supported\n", + dev->devnum, asifout, i); + continue; + } + format = (fmt[5] == 2) ? (AFMT_U16_LE | AFMT_U8) : (AFMT_S16_LE | AFMT_S8); + fmt = find_csinterface_descriptor(buffer, buflen, NULL, FORMAT_TYPE, asifout, i); + if (!fmt) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not found\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[0] < 8+3*(fmt[7] ? fmt[7] : 2) || fmt[3] != 1) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u FORMAT_TYPE descriptor not supported\n", + dev->devnum, asifout, i); + continue; + } + if (fmt[4] < 1 || fmt[4] > 2 || fmt[5] < 1 || fmt[5] > 2) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u unsupported channels %u framesize %u\n", + dev->devnum, asifout, i, fmt[4], fmt[5]); + continue; + } + csep = find_descriptor(buffer, buflen, NULL, USB_DT_CS_ENDPOINT, asifout, i); + if (!csep || csep[0] < 7 || csep[2] != EP_GENERAL) { + printk(KERN_ERR "usbaudio: device %u interface %u altsetting %u no or invalid class specific endpoint descriptor\n", + dev->devnum, asifout, i); + continue; + } + if (as->numfmtout >= MAXFORMATS) + continue; + fp = &as->fmtout[as->numfmtout++]; + if (fmt[5] == 2) + format &= (AFMT_U16_LE | AFMT_S16_LE); + else + format &= (AFMT_U8 | AFMT_S8); + if (fmt[4] == 2) + format |= AFMT_STEREO; + fp->format = format; + fp->altsetting = i; + fp->sratelo = fp->sratehi = fmt[8] | (fmt[9] << 8) | (fmt[10] << 16); + for (j = fmt[7] ? (fmt[7]-1) : 1; j > 0; j--) { + k = fmt[8+3*j] | (fmt[9+3*j] << 8) | (fmt[10+3*j] << 16); + if (k > fp->sratehi) + fp->sratehi = k; + if (k < fp->sratelo) + fp->sratelo = k; + } + fp->attributes = csep[3]; + printk(KERN_INFO "usbaudio: device %u interface %u altsetting %u: format 0x%08x sratelo %u sratehi %u attributes 0x%02x\n", + dev->devnum, asifout, i, fp->format, fp->sratelo, fp->sratehi, fp->attributes); + } + } + if (as->numfmtin == 0 && as->numfmtout == 0) { + kfree(as); + return; + } + if ((as->dev_audio = register_sound_dsp(&usb_audio_fops, -1)) < 0) { + printk(KERN_ERR "usbaudio: cannot register dsp\n"); + kfree(as); + return; + } + /* everything successful */ + list_add_tail(&as->list, &s->audiolist); +} + +struct consmixstate { + struct usb_audio_state *s; + unsigned char *buffer; + unsigned int buflen; + unsigned int ctrlif; + struct mixerchannel mixch[SOUND_MIXER_NRDEVICES]; + unsigned int nrmixch; + unsigned int mixchmask; + unsigned long unitbitmap[32/sizeof(unsigned long)]; + /* return values */ + unsigned int nrchannels; + unsigned int termtype; + unsigned int chconfig; +}; + +static struct mixerchannel *getmixchannel(struct consmixstate *state, unsigned int nr) +{ + struct mixerchannel *c; + + if (nr >= SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usbaudio: invalid OSS mixer channel %u\n", nr); + return NULL; + } + if (!(state->mixchmask & (1 << nr))) { + printk(KERN_WARNING "usbaudio: OSS mixer channel %u already in use\n", nr); + return NULL; + } + c = &state->mixch[state->nrmixch++]; + c->osschannel = nr; + state->mixchmask &= ~(1 << nr); + return c; +} + +static unsigned int getvolchannel(struct consmixstate *state) +{ + unsigned int u; + + if ((state->termtype & 0xff00) == 0x0000 && (state->mixchmask & SOUND_MASK_VOLUME)) + return SOUND_MIXER_VOLUME; + if ((state->termtype & 0xff00) == 0x0100) { + if (state->mixchmask & SOUND_MASK_PCM) + return SOUND_MIXER_PCM; + if (state->mixchmask & SOUND_MASK_ALTPCM) + return SOUND_MIXER_ALTPCM; + } + if ((state->termtype & 0xff00) == 0x0200 && (state->mixchmask & SOUND_MASK_MIC)) + return SOUND_MIXER_MIC; + if ((state->termtype & 0xff00) == 0x0300 && (state->mixchmask & SOUND_MASK_SPEAKER)) + return SOUND_MIXER_SPEAKER; + if ((state->termtype & 0xff00) == 0x0500) { + if (state->mixchmask & SOUND_MASK_PHONEIN) + return SOUND_MIXER_PHONEIN; + if (state->mixchmask & SOUND_MASK_PHONEOUT) + return SOUND_MIXER_PHONEOUT; + } + if (state->termtype >= 0x710 && state->termtype <= 0x711 && (state->mixchmask & SOUND_MASK_RADIO)) + return SOUND_MIXER_RADIO; + if (state->termtype >= 0x709 && state->termtype <= 0x70f && (state->mixchmask & SOUND_MASK_VIDEO)) + return SOUND_MIXER_VIDEO; + u = ffs(state->mixchmask & (SOUND_MASK_LINE | SOUND_MASK_CD | SOUND_MASK_LINE1 | SOUND_MASK_LINE2 | SOUND_MASK_LINE3 | + SOUND_MASK_DIGITAL1 | SOUND_MASK_DIGITAL2 | SOUND_MASK_DIGITAL3)); + return u-1; +} + +static void prepmixch(struct consmixstate *state) +{ + struct usb_device *dev = state->s->usbdev; + struct mixerchannel *ch; + unsigned char buf[2]; + __s16 v1; + unsigned int v2, v3; + + if (!state->nrmixch || state->nrmixch > SOUND_MIXER_NRDEVICES) + return; + ch = &state->mixch[state->nrmixch-1]; + switch (ch->selector) { + case 0: /* mixer unit request */ + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + if (!v2) + v2 = 1; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->chnum << 8) | 1, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + ((ch->chnum + !!(ch->flags & MIXFLG_STEREOIN)) << 8) | (1 + !!(ch->flags & MIXFLG_STEREOOUT)), + state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + /* various feature unit controls */ + case VOLUME_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->minval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + ch->maxval = buf[0] | (buf[1] << 8); + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 2, HZ) < 0) + goto err; + v1 = buf[0] | (buf[1] << 8); + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + case BASS_CONTROL: + case MID_CONTROL: + case TREBLE_CONTROL: + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MIN, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->minval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_MAX, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + ch->maxval = buf[0] << 8; + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | ch->chnum, state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v2 = ch->maxval - ch->minval; + v3 = v1 - ch->minval; + if (!v2) + v2 = 1; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + ch->value = v3; + if (ch->flags & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT)) { + if (usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), GET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_IN, + (ch->selector << 8) | (ch->chnum + 1), state->ctrlif | (ch->unitid << 8), buf, 1, HZ) < 0) + goto err; + v1 = buf[0] << 8; + v3 = v1 - ch->minval; + v3 = 100 * v3 / v2; + if (v3 > 100) + v3 = 100; + } + ch->value |= v3 << 8; + break; + + default: + goto err; + } + return; + + err: + printk(KERN_ERR "usbaudio: mixer request device %u if %u unit %u ch %u selector %u failed\n", + dev->devnum, state->ctrlif, ch->unitid, ch->chnum, ch->selector); + if (state->nrmixch) + state->nrmixch--; +} + + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid); + +extern inline int checkmixbmap(unsigned char *bmap, unsigned char flg, unsigned int inidx, unsigned int numoch) +{ + unsigned int idx; + + idx = inidx*numoch; + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + if (!(flg & (MIXFLG_STEREOIN | MIXFLG_STEREOOUT))) + return 1; + idx = (inidx+!!(flg & MIXFLG_STEREOIN))*numoch+!!(flg & MIXFLG_STEREOOUT); + if (!(bmap[-(idx >> 3)] & (0x80 >> (idx & 7)))) + return 0; + return 1; +} + +static void usb_audio_mixerunit(struct consmixstate *state, unsigned char *mixer) +{ + unsigned int nroutch = mixer[5+mixer[4]]; + unsigned int chidx[SOUND_MIXER_NRDEVICES+1]; + unsigned int termt[SOUND_MIXER_NRDEVICES]; + unsigned char flg = (nroutch >= 2) ? MIXFLG_STEREOOUT : 0; + unsigned char *bmap = &mixer[9+mixer[4]]; + unsigned int bmapsize; + struct mixerchannel *ch; + unsigned int i; + + if (!mixer[4]) { + printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor\n", mixer[3]); + return; + } + if (mixer[4] > SOUND_MIXER_NRDEVICES) { + printk(KERN_ERR "usbaudio: mixer unit %u: too many input pins\n", mixer[3]); + return; + } + chidx[0] = 0; + for (i = 0; i < mixer[4]; i++) { + usb_audio_recurseunit(state, mixer[5+i]); + chidx[i+1] = chidx[i] + state->nrchannels; + termt[i] = state->termtype; + } + state->termtype = 0; + state->chconfig = mixer[6+mixer[4]] | (mixer[7+mixer[4]] << 8); + bmapsize = (nroutch * chidx[mixer[4]] + 7) >> 3; + bmap += bmapsize - 1; + if (mixer[0] < 10+mixer[4]+bmapsize) { + printk(KERN_ERR "usbaudio: unit %u invalid MIXER_UNIT descriptor (bitmap too small)\n", mixer[3]); + return; + } + for (i = 0; i < mixer[4]; i++) { + state->termtype = termt[i]; + if (chidx[i+1]-chidx[i] >= 2) { + flg |= MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + continue; + } + } + flg &= ~MIXFLG_STEREOIN; + if (checkmixbmap(bmap, flg, chidx[i], nroutch)) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = mixer[3]; + ch->selector = 0; + ch->chnum = chidx[i]+1; + ch->flags = flg; + prepmixch(state); + } + } + } + state->termtype = 0; +} + +static struct mixerchannel *slctsrc_findunit(struct consmixstate *state, __u8 unitid) +{ + unsigned int i; + + for (i = 0; i < state->nrmixch; i++) + if (state->mixch[i].unitid == unitid) + return &state->mixch[i]; + return NULL; +} + +static void usb_audio_selectorunit(struct consmixstate *state, unsigned char *selector) +{ + unsigned int chnum, i, mixch; + struct mixerchannel *mch; + + if (!selector[4]) { + printk(KERN_ERR "usbaudio: unit %u invalid SELECTOR_UNIT descriptor\n", selector[3]); + return; + } + mixch = state->nrmixch; + usb_audio_recurseunit(state, selector[5]); + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[3] | (1 << 8); + } else if ((mch = slctsrc_findunit(state, selector[5]))) { + mch->slctunitid = selector[3] | (1 << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel 1\n", selector[3]); + } + chnum = state->nrchannels; + for (i = 1; i < selector[4]; i++) { + mixch = state->nrmixch; + usb_audio_recurseunit(state, selector[5+i]); + if (chnum != state->nrchannels) { + printk(KERN_ERR "usbaudio: selector unit %u: input pins with varying channel numbers\n", selector[3]); + state->termtype = 0; + state->chconfig = 0; + state->nrchannels = 0; + return; + } + if (state->nrmixch != mixch) { + mch = &state->mixch[state->nrmixch-1]; + mch->slctunitid = selector[3] | ((i + 1) << 8); + } else if ((mch = slctsrc_findunit(state, selector[5+i]))) { + mch->slctunitid = selector[3] | ((i + 1) << 8); + } else { + printk(KERN_INFO "usbaudio: selector unit %u: ignoring channel %u\n", selector[3], i+1); + } + } + state->termtype = 0; + state->chconfig = 0; +} + +/* in the future we might try to handle 3D etc. effect units */ + +static void usb_audio_processingunit(struct consmixstate *state, unsigned char *proc) +{ + unsigned int i; + + for (i = 0; i < proc[6]; i++) + usb_audio_recurseunit(state, proc[7+i]); + state->nrchannels = proc[7+proc[6]]; + state->termtype = 0; + state->chconfig = proc[8+proc[6]] | (proc[9+proc[6]] << 8); +} + +static void usb_audio_featureunit(struct consmixstate *state, unsigned char *ftr) +{ + struct mixerchannel *ch; + unsigned short chftr, mchftr; +#if 0 + struct usb_device *dev = state->s->usbdev; + unsigned char data[1]; +#endif + + usb_audio_recurseunit(state, ftr[4]); + if (state->nrchannels == 0) { + printk(KERN_ERR "usbaudio: feature unit %u source has no channels\n", ftr[3]); + return; + } + if (state->nrchannels > 2) + printk(KERN_WARNING "usbaudio: feature unit %u: OSS mixer interface does not support more than 2 channels\n", ftr[3]); + if (state->nrchannels == 1 && ftr[0] == 7+ftr[5]) { + printk(KERN_WARNING "usbaudio: workaround for broken Philips Camera Microphone descriptor enabled\n"); + mchftr = ftr[6]; + chftr = 0; + } else { + if (ftr[0] < 7+ftr[5]*(1+state->nrchannels)) { + printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", ftr[3]); + return; + } + mchftr = ftr[6]; + chftr = ftr[6+ftr[5]]; + if (state->nrchannels > 1) + chftr &= ftr[6+2*ftr[5]]; + } + /* volume control */ + if (chftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 2) { + ch = getmixchannel(state, getvolchannel(state)); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = VOLUME_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* bass control */ + if (chftr & 4) { + ch = getmixchannel(state, SOUND_MIXER_BASS); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 4) { + ch = getmixchannel(state, SOUND_MIXER_BASS); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = BASS_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } + /* treble control */ + if (chftr & 16) { + ch = getmixchannel(state, SOUND_MIXER_TREBLE); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 1; + ch->flags = (state->nrchannels > 1) ? (MIXFLG_STEREOIN | MIXFLG_STEREOOUT) : 0; + prepmixch(state); + } + } else if (mchftr & 16) { + ch = getmixchannel(state, SOUND_MIXER_TREBLE); + if (ch) { + ch->unitid = ftr[3]; + ch->selector = TREBLE_CONTROL; + ch->chnum = 0; + ch->flags = 0; + prepmixch(state); + } + } +#if 0 + /* if there are mute controls, unmute them */ + /* does not seem to be necessary, and the Dallas chip does not seem to support the "all" channel (255) */ + if ((chftr & 1) || (mchftr & 1)) { + printk(KERN_DEBUG "usbaudio: unmuting feature unit %u interface %u\n", ftr[3], state->ctrlif); + data[0] = 0; + if (usb_control_msg(dev, usb_sndctrlpipe(dev, 0), SET_CUR, USB_RECIP_INTERFACE | USB_TYPE_CLASS | USB_DIR_OUT, + (MUTE_CONTROL << 8) | 0xff, state->ctrlif | (ftr[3] << 8), data, 1, HZ) < 0) + printk(KERN_WARNING "usbaudio: failure to unmute feature unit %u interface %u\n", ftr[3], state->ctrlif); + } +#endif +} + +static void usb_audio_recurseunit(struct consmixstate *state, unsigned char unitid) +{ + unsigned char *p1; + unsigned int i, j; + + if (test_and_set_bit(unitid, &state->unitbitmap)) { + printk(KERN_INFO "usbaudio: mixer path revisits unit %d\n", unitid); + return; + } + p1 = find_audiocontrol_unit(state->buffer, state->buflen, NULL, unitid, state->ctrlif); + if (!p1) { + printk(KERN_ERR "usbaudio: unit %d not found!\n", unitid); + return; + } + state->nrchannels = 0; + state->termtype = 0; + state->chconfig = 0; + switch (p1[2]) { + case INPUT_TERMINAL: + if (p1[0] < 12) { + printk(KERN_ERR "usbaudio: unit %u: invalid INPUT_TERMINAL descriptor\n", unitid); + return; + } + state->nrchannels = p1[7]; + state->termtype = p1[4] | (p1[5] << 8); + state->chconfig = p1[8] | (p1[9] << 8); + return; + + case MIXER_UNIT: + if (p1[0] < 10 || p1[0] < 10+p1[4]) { + printk(KERN_ERR "usbaudio: unit %u: invalid MIXER_UNIT descriptor\n", unitid); + return; + } + usb_audio_mixerunit(state, p1); + return; + + case SELECTOR_UNIT: + if (p1[0] < 6 || p1[0] < 6+p1[4]) { + printk(KERN_ERR "usbaudio: unit %u: invalid SELECTOR_UNIT descriptor\n", unitid); + return; + } + usb_audio_selectorunit(state, p1); + return; + + case FEATURE_UNIT: + if (p1[0] < 7 || p1[0] < 7+p1[5]) { + printk(KERN_ERR "usbaudio: unit %u: invalid FEATURE_UNIT descriptor\n", unitid); + return; + } + usb_audio_featureunit(state, p1); + return; + + case PROCESSING_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]] || p1[0] < 13+p1[6]+p1[11+p1[6]]+p1[13+p1[6]+p1[11+p1[6]]]) { + printk(KERN_ERR "usbaudio: unit %u: invalid PROCESSING_UNIT descriptor\n", unitid); + return; + } + usb_audio_processingunit(state, p1); + return; + + case EXTENSION_UNIT: + if (p1[0] < 13 || p1[0] < 13+p1[6] || p1[0] < 13+p1[6]+p1[11+p1[6]]) { + printk(KERN_ERR "usbaudio: unit %u: invalid EXTENSION_UNIT descriptor\n", unitid); + return; + } + for (j = i = 0; i < p1[6]; i++) { + usb_audio_recurseunit(state, p1[7+i]); + if (!i) + j = state->termtype; + else if (j != state->termtype) + j = 0; + } + state->nrchannels = p1[7+p1[6]]; + state->chconfig = p1[8+p1[6]] | (p1[9+p1[6]] << 8); + state->termtype = j; + return; + + default: + printk(KERN_ERR "usbaudio: unit %u: unexpected type 0x%02x\n", unitid, p1[2]); + return; + } +} + +static void usb_audio_constructmixer(struct usb_audio_state *s, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif, unsigned char *oterm) +{ + struct usb_mixerdev *ms; + struct consmixstate state; + + memset(&state, 0, sizeof(state)); + state.s = s; + state.nrmixch = 0; + state.mixchmask = ~0; + state.buffer = buffer; + state.buflen = buflen; + state.ctrlif = ctrlif; + set_bit(oterm[3], &state.unitbitmap); /* mark terminal ID as visited */ + printk(KERN_INFO "usbaudio: constructing mixer for Terminal %u type 0x%04x\n", + oterm[3], oterm[4] | (oterm[5] << 8)); + usb_audio_recurseunit(&state, oterm[7]); + if (!state.nrmixch) { + printk(KERN_INFO "usbaudio: no mixer controls found for Terminal %u\n", oterm[3]); + return; + } + if (!(ms = kmalloc(sizeof(struct usb_mixerdev)+state.nrmixch*sizeof(struct mixerchannel), GFP_KERNEL))) + return; + memset(ms, 0, sizeof(struct usb_mixerdev)); + memcpy(&ms->ch, &state.mixch, state.nrmixch*sizeof(struct mixerchannel)); + ms->state = s; + ms->iface = ctrlif; + ms->numch = state.nrmixch; + if ((ms->dev_mixer = register_sound_mixer(&usb_mixer_fops, -1)) < 0) { + printk(KERN_ERR "usbaudio: cannot register mixer\n"); + kfree(ms); + return; + } + list_add_tail(&ms->list, &s->mixerlist); +} + +static void *usb_audio_parsecontrol(struct usb_device *dev, unsigned char *buffer, unsigned int buflen, unsigned int ctrlif) +{ + struct usb_audio_state *s; + struct usb_config_descriptor *config = dev->actconfig; + struct usb_interface *iface; + unsigned char ifin[USB_MAXINTERFACES], ifout[USB_MAXINTERFACES]; + unsigned char *p1; + unsigned int i, j, k, numifin = 0, numifout = 0; + + if (!(s = kmalloc(sizeof(struct usb_audio_state), GFP_KERNEL))) + return NULL; + memset(s, 0, sizeof(struct usb_audio_state)); + INIT_LIST_HEAD(&s->audiolist); + INIT_LIST_HEAD(&s->mixerlist); + s->usbdev = dev; + s->count = 1; + + /* find audiocontrol interface */ + if (!(p1 = find_csinterface_descriptor(buffer, buflen, NULL, HEADER, ctrlif, -1))) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u no HEADER found\n", + dev->devnum, ctrlif); + goto ret; + } + if (p1[0] < 8 + p1[7]) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u HEADER error\n", + dev->devnum, ctrlif); + goto ret; + } + if (!p1[7]) + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has no AudioStreaming and MidiStreaming interfaces\n", + dev->devnum, ctrlif); + for (i = 0; i < p1[7]; i++) { + j = p1[8+i]; + if (j >= config->bNumInterfaces) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u does not exist\n", + dev->devnum, ctrlif, j); + continue; + } + iface = &config->interface[j]; + if (iface->altsetting[0].bInterfaceClass != USB_CLASS_AUDIO) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u is not an AudioClass interface\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass == 3) { + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u interface %u MIDIStreaming not supported\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->altsetting[0].bInterfaceSubClass != 2) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u invalid AudioClass subtype\n", + dev->devnum, ctrlif, j); + continue; + } + if (iface->num_altsetting == 0) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has no working interface.\n", dev->devnum, ctrlif); + continue; + } + if (iface->num_altsetting == 1) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u has only 1 altsetting.\n", dev->devnum, ctrlif); + continue; + } + if (iface->altsetting[0].bNumEndpoints > 0) { + /* Check all endpoints; should they all have a bandwidth of 0 ? */ + for (k = 0; k < iface->altsetting[0].bNumEndpoints; k++) { + if (iface->altsetting[0].endpoint[k].wMaxPacketSize > 0) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u endpoint %d does not have 0 bandwidth at alt[0]\n", dev->devnum, ctrlif, k); + break; + } + } + if (k < iface->altsetting[0].bNumEndpoints) + continue; + } + if (iface->altsetting[1].bNumEndpoints < 1) { + printk(KERN_ERR "usbaudio: device %d audiocontrol interface %u interface %u has no endpoint\n", + dev->devnum, ctrlif, j); + continue; + } + /* note: this requires the data endpoint to be ep0 and the optional sync + ep to be ep1, which seems to be the case */ + if (iface->altsetting[1].endpoint[0].bEndpointAddress & USB_DIR_IN) { + if (numifin < USB_MAXINTERFACES) { + ifin[numifin++] = j; + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } + } else { + if (numifout < USB_MAXINTERFACES) { + ifout[numifout++] = j; + usb_driver_claim_interface(&usb_audio_driver, iface, (void *)-1); + } + } + } + printk(KERN_INFO "usbaudio: device %d audiocontrol interface %u has %u input and %u output AudioStreaming interfaces\n", + dev->devnum, ctrlif, numifin, numifout); + for (i = 0; i < numifin && i < numifout; i++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], ifout[i]); + for (j = i; j < numifin; j++) + usb_audio_parsestreaming(s, buffer, buflen, ifin[i], -1); + for (j = i; j < numifout; j++) + usb_audio_parsestreaming(s, buffer, buflen, -1, ifout[i]); + /* now walk through all OUTPUT_TERMINAL descriptors to search for mixers */ + p1 = find_csinterface_descriptor(buffer, buflen, NULL, OUTPUT_TERMINAL, ctrlif, -1); + while (p1) { + if (p1[0] >= 9) + usb_audio_constructmixer(s, buffer, buflen, ctrlif, p1); + p1 = find_csinterface_descriptor(buffer, buflen, p1, OUTPUT_TERMINAL, ctrlif, -1); + } + +ret: + if (list_empty(&s->audiolist) && list_empty(&s->mixerlist)) { + kfree(s); + return NULL; + } + /* everything successful */ + down(&open_sem); + list_add_tail(&s->audiodev, &audiodevs); + up(&open_sem); + printk(KERN_DEBUG "usb_audio_parsecontrol: usb_audio_state at %p\n", s); + return s; +} + +/* we only care for the currently active configuration */ + +static void *usb_audio_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_config_descriptor *config = dev->actconfig; + unsigned char *buffer; + unsigned char buf[8]; + unsigned int i, buflen; + int ret; + +#if 0 + printk(KERN_DEBUG "usbaudio: Probing if %i: IC %x, ISC %x\n", ifnum, + config->interface[ifnum].altsetting[0].bInterfaceClass, + config->interface[ifnum].altsetting[0].bInterfaceSubClass); +#endif + if (config->interface[ifnum].altsetting[0].bInterfaceClass != USB_CLASS_AUDIO || + config->interface[ifnum].altsetting[0].bInterfaceSubClass != 1) { +#if 0 + printk(KERN_DEBUG "usbaudio: vendor id 0x%04x, product id 0x%04x contains no AudioControl interface\n", + dev->descriptor.idVendor, dev->descriptor.idProduct); +#endif + return NULL; + } + /* + * audiocontrol interface found + * find which configuration number is active + */ + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + if (dev->config+i == config) + goto configfound; + printk(KERN_ERR "usbaudio: cannot find active configuration number of device %d\n", dev->devnum); + return NULL; + + configfound: + if (usb_set_configuration(dev, config->bConfigurationValue) < 0) { + printk(KERN_ERR "usbaudio: set_configuration failed (ConfigValue 0x%x)\n", config->bConfigurationValue); + return NULL; + } + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buf, 8); + if (ret<0) { + printk(KERN_ERR "usbaudio: cannot get first 8 bytes of config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + if (buf[1] != USB_DT_CONFIG || buf[0] < 9) { + printk(KERN_ERR "usbaudio: invalid config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + buflen = buf[2] | (buf[3] << 8); + if (!(buffer = kmalloc(buflen, GFP_KERNEL))) + return NULL; + ret = usb_get_descriptor(dev, USB_DT_CONFIG, i, buffer, buflen); + if (ret < 0) { + kfree(buffer); + printk(KERN_ERR "usbaudio: cannot get config descriptor %d of device %d\n", i, dev->devnum); + return NULL; + } + return usb_audio_parsecontrol(dev, buffer, buflen, ifnum); +} + + +/* a revoke facility would make things simpler */ + +static void usb_audio_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_audio_state *s = (struct usb_audio_state *)ptr; + struct list_head *list; + struct usb_audiodev *as; + struct usb_mixerdev *ms; + + /* we get called with -1 for every audiostreaming interface registered */ + if (s == (struct usb_audio_state *)-1) { + dprintk((KERN_DEBUG "usb_audio_disconnect: called with -1\n")); + return; + } + if (!s->usbdev) { + dprintk((KERN_DEBUG "usb_audio_disconnect: already called for %p!\n", s)); + return; + } + down(&open_sem); + list_del(&s->audiodev); + INIT_LIST_HEAD(&s->audiodev); + s->usbdev = NULL; + /* deregister all audio and mixer devices, so no new processes can open this device */ + for(list = s->audiolist.next; list != &s->audiolist; list = list->next) { + as = list_entry(list, struct usb_audiodev, list); + usbin_disc(as); + usbout_disc(as); + wake_up(&as->usbin.dma.wait); + wake_up(&as->usbout.dma.wait); + if (as->dev_audio >= 0) + unregister_sound_dsp(as->dev_audio); + as->dev_audio = -1; + } + for(list = s->mixerlist.next; list != &s->mixerlist; list = list->next) { + ms = list_entry(list, struct usb_mixerdev, list); + if (ms->dev_mixer >= 0) + unregister_sound_mixer(ms->dev_mixer); + ms->dev_mixer = -1; + } + release(s); + wake_up(&open_wait); +} + +int usb_audio_init(void) +{ + usb_register(&usb_audio_driver); + return 0; +} + +#ifdef MODULE +int init_module(void) { + return usb_audio_init(); } -void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) +void cleanup_module(void) { + usb_deregister(&usb_audio_driver); } +#endif diff --git a/drivers/usb/audio.h b/drivers/usb/audio.h new file mode 100644 index 000000000000..bb51e468bac6 --- /dev/null +++ b/drivers/usb/audio.h @@ -0,0 +1,116 @@ +#define USB_DT_CS_DEVICE 0x21 +#define USB_DT_CS_CONFIG 0x22 +#define USB_DT_CS_STRING 0x23 +#define USB_DT_CS_INTERFACE 0x24 +#define USB_DT_CS_ENDPOINT 0x25 + +#define CS_AUDIO_UNDEFINED 0x20 +#define CS_AUDIO_DEVICE 0x21 +#define CS_AUDIO_CONFIGURATION 0x22 +#define CS_AUDIO_STRING 0x23 +#define CS_AUDIO_INTERFACE 0x24 +#define CS_AUDIO_ENDPOINT 0x25 + +#define HEADER 0x01 +#define INPUT_TERMINAL 0x02 +#define OUTPUT_TERMINAL 0x03 +#define MIXER_UNIT 0x04 +#define SELECTOR_UNIT 0x05 +#define FEATURE_UNIT 0x06 +#define PROCESSING_UNIT 0x07 +#define EXTENSION_UNIT 0x08 + +#define AS_GENERAL 0x01 +#define FORMAT_TYPE 0x02 +#define FORMAT_SPECIFIC 0x03 + +#define EP_GENERAL 0x01 + +#define MAX_CHAN 9 +#define MAX_FREQ 16 +#define MAX_IFACE 8 +#define MAX_FORMAT 8 +#define MAX_ALT 32 /* Sorry, we need quite a few for the Philips webcams */ + +struct usb_audio_terminal +{ + u8 flags; + u8 assoc; + u16 type; /* Mic etc */ + u8 channels; + u8 source; + u16 chancfg; +}; + +struct usb_audio_format +{ + u8 type; + u8 channels; + u8 num_freq; + u8 sfz; + u8 bits; + u16 freq[MAX_FREQ]; +}; + +struct usb_audio_interface +{ + u8 terminal; + u8 delay; + u16 num_formats; + u16 format_type; + u8 flags; + u8 idleconf; /* Idle config */ +#define AU_IFACE_FOUND 1 + struct usb_audio_format format[MAX_FORMAT]; +}; + +struct usb_audio_device +{ + struct list_head list; + u8 mixer; + u8 selector; + void *irq_handle; + u8 num_channels; + u8 num_dsp_iface; + u8 channel_map[MAX_CHAN]; + struct usb_audio_terminal terminal[MAX_CHAN]; + struct usb_audio_interface interface[MAX_IFACE][MAX_ALT]; +}; + + + +/* Audio Class specific Request Codes */ + +#define SET_CUR 0x01 +#define GET_CUR 0x81 +#define SET_MIN 0x02 +#define GET_MIN 0x82 +#define SET_MAX 0x03 +#define GET_MAX 0x83 +#define SET_RES 0x04 +#define GET_RES 0x84 +#define SET_MEM 0x05 +#define GET_MEM 0x85 +#define GET_STAT 0xff + +/* Terminal Control Selectors */ + +#define COPY_PROTECT_CONTROL 0x01 + +/* Feature Unit Control Selectors */ + +#define MUTE_CONTROL 0x01 +#define VOLUME_CONTROL 0x02 +#define BASS_CONTROL 0x03 +#define MID_CONTROL 0x04 +#define TREBLE_CONTROL 0x05 +#define GRAPHIC_EQUALIZER_CONTROL 0x06 +#define AUTOMATIC_GAIN_CONTROL 0x07 +#define DELAY_CONTROL 0x08 +#define BASS_BOOST_CONTROL 0x09 +#define LOUDNESS_CONTROL 0x0a + +/* Endpoint Control Selectors */ + +#define SAMPLING_FREQ_CONTROL 0x01 +#define PITCH_CONTROL 0x02 diff --git a/drivers/usb/dabfirmware.h b/drivers/usb/dabfirmware.h new file mode 100644 index 000000000000..d14d803566a3 --- /dev/null +++ b/drivers/usb/dabfirmware.h @@ -0,0 +1,1408 @@ +/* + * dabdata.h - dab usb firmware and bitstream data + */ + +static INTEL_HEX_RECORD firmware[] = { + +{ 2, 0x0000, 0, {0x21,0x57} }, +{ 3, 0x0003, 0, {0x02,0x01,0x66} }, +{ 3, 0x000b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0013, 0, {0x02,0x01,0x66} }, +{ 3, 0x001b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0023, 0, {0x02,0x01,0x66} }, +{ 3, 0x002b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0033, 0, {0x02,0x03,0x0f} }, +{ 3, 0x003b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0043, 0, {0x02,0x01,0x00} }, +{ 3, 0x004b, 0, {0x02,0x01,0x66} }, +{ 3, 0x0053, 0, {0x02,0x01,0x66} }, +{ 3, 0x005b, 0, {0x02,0x04,0xbd} }, +{ 3, 0x0063, 0, {0x02,0x01,0x67} }, +{ 3, 0x0100, 0, {0x02,0x0c,0x5a} }, +{ 3, 0x0104, 0, {0x02,0x01,0xed} }, +{ 3, 0x0108, 0, {0x02,0x02,0x51} }, +{ 3, 0x010c, 0, {0x02,0x02,0x7c} }, +{ 3, 0x0110, 0, {0x02,0x02,0xe4} }, +{ 1, 0x0114, 0, {0x32} }, +{ 1, 0x0118, 0, {0x32} }, +{ 3, 0x011c, 0, {0x02,0x05,0xfd} }, +{ 3, 0x0120, 0, {0x02,0x00,0x00} }, +{ 3, 0x0124, 0, {0x02,0x00,0x00} }, +{ 3, 0x0128, 0, {0x02,0x04,0x3c} }, +{ 3, 0x012c, 0, {0x02,0x04,0x6a} }, +{ 3, 0x0130, 0, {0x02,0x00,0x00} }, +{ 3, 0x0134, 0, {0x02,0x00,0x00} }, +{ 3, 0x0138, 0, {0x02,0x00,0x00} }, +{ 3, 0x013c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0140, 0, {0x02,0x00,0x00} }, +{ 3, 0x0144, 0, {0x02,0x00,0x00} }, +{ 3, 0x0148, 0, {0x02,0x00,0x00} }, +{ 3, 0x014c, 0, {0x02,0x00,0x00} }, +{ 3, 0x0150, 0, {0x02,0x00,0x00} }, +{ 3, 0x0154, 0, {0x02,0x00,0x00} }, +{ 10, 0x0157, 0, {0x75,0x81,0x7f,0xe5,0x82,0x60,0x03,0x02,0x01,0x61} }, +{ 5, 0x0161, 0, {0x12,0x07,0x6f,0x21,0x64} }, +{ 1, 0x0166, 0, {0x32} }, +{ 14, 0x0167, 0, {0xc0,0xd0,0xc0,0x86,0xc0,0x82,0xc0,0x83,0xc0,0xe0,0x90,0x7f,0x97,0xe0} }, +{ 14, 0x0175, 0, {0x44,0x80,0xf0,0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0183, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x0191, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x90,0x7f,0x97,0xe0} }, +{ 3, 0x019f, 0, {0x55,0x7f,0xf0} }, +{ 14, 0x01a2, 0, {0x90,0x7f,0x9a,0xe0,0x30,0xe4,0x23,0x90,0x7f,0x68,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01b0, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01be, 0, {0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0} }, +{ 14, 0x01cc, 0, {0xe5,0xd8,0xc2,0xe3,0xf5,0xd8,0xd0,0xe0,0xd0,0x83,0xd0,0x82,0xd0,0x86} }, +{ 3, 0x01da, 0, {0xd0,0xd0,0x32} }, +{ 8, 0x01dd, 0, {0x75,0x86,0x00,0x90,0xff,0xc3,0x7c,0x05} }, +{ 7, 0x01e5, 0, {0xa3,0xe5,0x82,0x45,0x83,0x70,0xf9} }, +{ 1, 0x01ec, 0, {0x22} }, +{ 14, 0x01ed, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0xd0} }, +{ 14, 0x01fb, 0, {0x75,0xd0,0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91} }, +{ 13, 0x0209, 0, {0x90,0x88,0x00,0xe0,0xf5,0x41,0x90,0x7f,0xab,0x74,0x02,0xf0,0x90} }, +{ 9, 0x0216, 0, {0x7f,0xab,0x74,0x02,0xf0,0xe5,0x32,0x60,0x21} }, +{ 4, 0x021f, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x0223, 0, {0xc3,0xea,0x94,0x18,0xeb,0x64,0x80,0x94,0x80,0x50,0x12} }, +{ 14, 0x022e, 0, {0x90,0x7f,0x69,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0x0a,0xba,0x00} }, +{ 2, 0x023c, 0, {0x01,0x0b} }, +{ 2, 0x023e, 0, {0x80,0xe3} }, +{ 2, 0x0240, 0, {0xd0,0x86} }, +{ 14, 0x0242, 0, {0xd0,0xd0,0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0} }, +{ 1, 0x0250, 0, {0x32} }, +{ 14, 0x0251, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x025f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x026d, 0, {0x04,0xf0,0xd0,0x86} }, +{ 11, 0x0271, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x027c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x028a, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0298, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 12, 0x02a5, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0x6e,0x00,0x75,0x6f,0x02,0x12} }, +{ 6, 0x02b1, 0, {0x11,0x44,0x75,0x70,0x39,0x75} }, +{ 6, 0x02b7, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 12, 0x02bd, 0, {0x11,0x75,0x90,0x7f,0xd6,0xe4,0xf0,0x75,0xd8,0x20,0xd0,0x86} }, +{ 14, 0x02c9, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x02d7, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x02e4, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x02f2, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xab,0x74} }, +{ 4, 0x0300, 0, {0x10,0xf0,0xd0,0x86} }, +{ 11, 0x0304, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x030f, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x031d, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 12, 0x032b, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0x75,0x6e,0x00,0x75,0x6f,0x02} }, +{ 7, 0x0337, 0, {0x12,0x11,0x44,0x75,0x70,0x40,0x75} }, +{ 6, 0x033e, 0, {0x71,0x0c,0x75,0x72,0x02,0x12} }, +{ 14, 0x0344, 0, {0x11,0x75,0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0} }, +{ 5, 0x0352, 0, {0x75,0xd8,0x10,0xd0,0x86} }, +{ 14, 0x0357, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0365, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 13, 0x0372, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x037f, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x42,0xf0,0x12,0x10,0x1b,0x90} }, +{ 13, 0x038b, 0, {0x7f,0xa6,0xe5,0x43,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40} }, +{ 1, 0x0398, 0, {0xf0} }, +{ 1, 0x0399, 0, {0x22} }, +{ 13, 0x039a, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x90,0x7f,0xa6,0x74,0x9a,0xf0,0x12} }, +{ 12, 0x03a7, 0, {0x10,0x1b,0x90,0x7f,0xa6,0xe5,0x44,0xf0,0x12,0x10,0x1b,0x90} }, +{ 12, 0x03b3, 0, {0x7f,0xa6,0xe5,0x45,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0xe5} }, +{ 11, 0x03bf, 0, {0x46,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x03ca, 0, {0x22} }, +{ 10, 0x03cb, 0, {0x75,0x44,0x02,0x75,0x45,0x00,0x75,0x46,0x00,0x12} }, +{ 9, 0x03d5, 0, {0x03,0x9a,0x75,0x42,0x03,0x75,0x43,0x00,0x12} }, +{ 2, 0x03de, 0, {0x03,0x72} }, +{ 1, 0x03e0, 0, {0x22} }, +{ 12, 0x03e1, 0, {0x90,0x88,0x00,0xe5,0x36,0xf0,0x90,0x88,0x00,0x74,0x10,0x25} }, +{ 9, 0x03ed, 0, {0x36,0xf0,0x12,0x01,0xdd,0x75,0x42,0x01,0x75} }, +{ 9, 0x03f6, 0, {0x43,0x18,0x12,0x03,0x72,0x75,0x44,0x02,0x75} }, +{ 9, 0x03ff, 0,{0x45,0x00,0x75,0x46,0x00,0x12,0x03,0x9a,0x75} }, +{ 8, 0x0408, 0,{0x42,0x03,0x75,0x43,0x44,0x12,0x03,0x72} }, +{ 1, 0x0410, 0,{0x22} }, +{ 14, 0x0411, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x041f, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 4, 0x042d, 0, {0x02,0xf0,0xd0,0x86} }, +{ 11, 0x0431, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x043c, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x044a, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xa9,0x74} }, +{ 7, 0x0458, 0, {0x04,0xf0,0x75,0x30,0x01,0xd0,0x86} }, +{ 11, 0x045f, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x046a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 14, 0x0478, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90,0x7f,0xaa,0x74} }, +{ 7, 0x0486, 0, {0x04,0xf0,0x75,0x31,0x01,0xd0,0x86} }, +{ 11, 0x048d, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x0498, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04a6, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe5,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04b2, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x04bd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0xd0,0x75,0xd0,0x00,0xc0} }, +{ 12, 0x04cb, 0, {0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe7,0xf5,0x91,0xd0,0x86} }, +{ 11, 0x04d7, 0, {0xd0,0xd0,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 12, 0x04e2, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x20,0x90,0x7f,0x96,0xe4,0xf0} }, +{ 1, 0x04ee, 0, {0x22} }, +{ 7, 0x04ef, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x8a,0x21} }, +{ 1, 0x04f6, 0, {0x22} }, +{ 14, 0x04f7, 0, {0x90,0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xe0,0xfb,0x74,0x80,0x2a,0xfa} }, +{ 14, 0x0505, 0, {0x74,0x80,0x2b,0xfb,0xea,0x03,0x03,0x54,0x3f,0xfc,0xea,0xc4,0x23,0x54} }, +{ 14, 0x0513, 0, {0x1f,0xfa,0x2c,0xfa,0xeb,0x03,0x03,0x54,0x3f,0xfc,0xeb,0xc4,0x23,0x54} }, +{ 11, 0x0521, 0, {0x1f,0xfb,0x2c,0xfb,0x90,0x17,0x0a,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x052c, 0, {0x7a,0x00} }, +{ 7, 0x052e, 0, {0x90,0x17,0x0c,0xe0,0xfc,0x60,0x02} }, +{ 2, 0x0535, 0, {0x7b,0x00} }, +{ 11, 0x0537, 0, {0xea,0x2b,0xfc,0xc3,0x13,0xf5,0x3a,0x75,0x44,0x02,0x8b} }, +{ 7, 0x0542, 0, {0x45,0x8a,0x46,0x12,0x03,0x9a,0x75} }, +{ 9, 0x0549, 0, {0x6e,0x08,0x75,0x6f,0x00,0x12,0x11,0x44,0x75} }, +{ 4, 0x0552, 0, {0x70,0x47,0x75,0x71} }, +{ 8, 0x0556, 0, {0x0c,0x75,0x72,0x02,0x12,0x11,0x75,0x85} }, +{ 5, 0x055e, 0, {0x3a,0x73,0x12,0x11,0xa0} }, +{ 1, 0x0563, 0, {0x22} }, +{ 14, 0x0564, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 14, 0x0572, 0, {0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xef,0xe0,0xfc} }, +{ 14, 0x0580, 0, {0x33,0x95,0xe0,0xfd,0x8c,0x05,0x7c,0x00,0x90,0x7f,0xee,0xe0,0xfe,0x33} }, +{ 14, 0x058e, 0, {0x95,0xe0,0xff,0xec,0x2e,0xfc,0xed,0x3f,0xfd,0x90,0x7f,0xe9,0xe0,0xfe} }, +{ 5, 0x059c, 0, {0xbe,0x01,0x02,0x80,0x03} }, +{ 3, 0x05a1, 0, {0x02,0x05,0xf9} }, +{ 6, 0x05a4, 0, {0xbc,0x01,0x21,0xbd,0x00,0x1e} }, +{ 14, 0x05aa, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfc,0xeb,0x25,0xe0,0xfd,0x2c,0x24,0x00,0xfc} }, +{ 14, 0x05b8, 0, {0xe4,0x34,0x17,0xfd,0x90,0x7e,0xc0,0xe0,0xfe,0x8c,0x82,0x8d,0x83,0xf0} }, +{ 2, 0x05c6, 0, {0x80,0x31} }, +{ 14, 0x05c8, 0, {0xea,0xc4,0x03,0x54,0xf8,0xfa,0xeb,0x25,0xe0,0xfb,0x2a,0xfa,0x24,0x00} }, +{ 14, 0x05d6, 0, {0xfb,0xe4,0x34,0x17,0xfc,0x90,0x7e,0xc0,0xe0,0xfd,0x8b,0x82,0x8c,0x83} }, +{ 14, 0x05e4, 0, {0xf0,0x74,0x01,0x2a,0x24,0x00,0xfa,0xe4,0x34,0x17,0xfb,0x90,0x7e,0xc1} }, +{ 7, 0x05f2, 0, {0xe0,0xfc,0x8a,0x82,0x8b,0x83,0xf0} }, +{ 3, 0x05f9, 0, {0x75,0x38,0x01} }, +{ 1, 0x05fc, 0, {0x22} }, +{ 14, 0x05fd, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x060b, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0619, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 13, 0x0626, 0, {0x7f,0xaa,0x74,0x01,0xf0,0x12,0x05,0x64,0x75,0x37,0x00,0xd0,0x86} }, +{ 14, 0x0633, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0641, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 14, 0x064e, 0, {0x90,0x7f,0xeb,0xe0,0xfa,0x90,0x7f,0xea,0xe0,0xfb,0x90,0x7f,0xee,0xe0} }, +{ 14, 0x065c, 0, {0xfc,0x33,0x95,0xe0,0xfd,0x90,0x7f,0x96,0xe0,0xfe,0x90,0x7f,0x96,0x74} }, +{ 14, 0x066a, 0, {0x80,0x65,0x06,0xf0,0x90,0x7f,0x00,0x74,0x01,0xf0,0xea,0xc4,0x03,0x54} }, +{ 14, 0x0678, 0, {0xf8,0xfe,0xeb,0x25,0xe0,0xfb,0x2e,0xfe,0x24,0x00,0xfb,0xe4,0x34,0x17} }, +{ 14, 0x0686, 0, {0xff,0x8b,0x82,0x8f,0x83,0xe0,0xfb,0x74,0x01,0x2e,0x24,0x00,0xfe,0xe4} }, +{ 14, 0x0694, 0, {0x34,0x17,0xff,0x8e,0x82,0x8f,0x83,0xe0,0xfe,0x90,0x7f,0xe9,0xe0,0xff} }, +{ 3, 0x06a2, 0, {0xbf,0x81,0x0a} }, +{ 10, 0x06a5, 0, {0x90,0x7f,0x00,0xeb,0xf0,0x90,0x7f,0x01,0xee,0xf0} }, +{ 8, 0x06af, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x82,0x1a} }, +{ 3, 0x06b7, 0, {0xba,0x01,0x0c} }, +{ 12, 0x06ba, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06c6, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0xb5,0xf0} }, +{ 8, 0x06d1, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x83,0x1b} }, +{ 3, 0x06d9, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06dc, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0b} }, +{ 11, 0x06e9, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0x74,0x12,0xf0} }, +{ 8, 0x06f4, 0, {0x90,0x7f,0xe9,0xe0,0xfb,0xbb,0x84,0x1c} }, +{ 3, 0x06fc, 0, {0xba,0x01,0x0d} }, +{ 13, 0x06ff, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x80,0x0c} }, +{ 12, 0x070c, 0, {0x90,0x7f,0x00,0x74,0x80,0xf0,0x90,0x7f,0x01,0x74,0x01,0xf0} }, +{ 5, 0x0718, 0, {0x90,0x7f,0xb5,0xec,0xf0} }, +{ 1, 0x071d, 0, {0x22} }, +{ 12, 0x071e, 0, {0x75,0x36,0x0d,0x90,0x88,0x00,0x74,0x1d,0xf0,0x75,0x6b,0x80} }, +{ 10, 0x072a, 0, {0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x0734, 0, {0x6c,0x0f,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 9, 0x073d, 0, {0x6c,0x06,0x12,0x10,0xe2,0x75,0x6b,0x80,0x75} }, +{ 7, 0x0746, 0, {0x6c,0x01,0x12,0x10,0xe2,0x7a,0x00} }, +{ 3, 0x074d, 0, {0xba,0xff,0x00} }, +{ 2, 0x0750, 0, {0x50,0x0a} }, +{ 10, 0x0752, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 10, 0x075c, 0, {0x75,0x6b,0x80,0x75,0x6c,0x3c,0x12,0x10,0xe2,0x75} }, +{ 8, 0x0766, 0, {0x6b,0x80,0x75,0x6c,0x0f,0x12,0x10,0xe2} }, +{ 1, 0x076e, 0, {0x22} }, +{ 14, 0x076f, 0, {0x90,0x7f,0xa1,0xe4,0xf0,0x90,0x7f,0xaf,0x74,0x01,0xf0,0x90,0x7f,0x92} }, +{ 14, 0x077d, 0, {0x74,0x02,0xf0,0x75,0x8e,0x31,0x75,0x89,0x21,0x75,0x88,0x00,0x75,0xc8} }, +{ 14, 0x078b, 0, {0x00,0x75,0x8d,0x40,0x75,0x98,0x40,0x75,0xc0,0x40,0x75,0x87,0x00,0x75} }, +{ 9, 0x0799, 0, {0x20,0x00,0x75,0x21,0x00,0x75,0x22,0x00,0x75} }, +{ 5, 0x07a2, 0, {0x23,0x00,0x75,0x47,0x00} }, +{ 7, 0x07a7, 0, {0xc3,0xe5,0x47,0x94,0x20,0x50,0x11} }, +{ 13, 0x07ae, 0, {0xe5,0x47,0x24,0x00,0xf5,0x82,0xe4,0x34,0x17,0xf5,0x83,0xe4,0xf0} }, +{ 4, 0x07bb, 0, {0x05,0x47,0x80,0xe8} }, +{ 9, 0x07bf, 0, {0xe4,0xf5,0x40,0xf5,0x3f,0xe4,0xf5,0x3c,0xf5} }, +{ 7, 0x07c8, 0, {0x3b,0xe4,0xf5,0x3e,0xf5,0x3d,0x75} }, +{ 11, 0x07cf, 0, {0x32,0x00,0x75,0x37,0x00,0x75,0x39,0x00,0x90,0x7f,0x93} }, +{ 14, 0x07da, 0, {0x74,0x3c,0xf0,0x90,0x7f,0x9c,0x74,0xff,0xf0,0x90,0x7f,0x96,0x74,0x80} }, +{ 14, 0x07e8, 0, {0xf0,0x90,0x7f,0x94,0x74,0x70,0xf0,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 14, 0x07f6, 0, {0x7f,0x97,0xe4,0xf0,0x90,0x7f,0x95,0x74,0xc2,0xf0,0x90,0x7f,0x98,0x74} }, +{ 14, 0x0804, 0, {0x28,0xf0,0x90,0x7f,0x9e,0x74,0x28,0xf0,0x90,0x7f,0xf0,0xe4,0xf0,0x90} }, +{ 14, 0x0812, 0, {0x7f,0xf1,0xe4,0xf0,0x90,0x7f,0xf2,0xe4,0xf0,0x90,0x7f,0xf3,0xe4,0xf0} }, +{ 14, 0x0820, 0, {0x90,0x7f,0xf4,0xe4,0xf0,0x90,0x7f,0xf5,0xe4,0xf0,0x90,0x7f,0xf6,0xe4} }, +{ 14, 0x082e, 0, {0xf0,0x90,0x7f,0xf7,0xe4,0xf0,0x90,0x7f,0xf8,0xe4,0xf0,0x90,0x7f,0xf9} }, +{ 14, 0x083c, 0, {0x74,0x38,0xf0,0x90,0x7f,0xfa,0x74,0xa0,0xf0,0x90,0x7f,0xfb,0x74,0xa0} }, +{ 14, 0x084a, 0, {0xf0,0x90,0x7f,0xfc,0x74,0xa0,0xf0,0x90,0x7f,0xfd,0x74,0xa0,0xf0,0x90} }, +{ 14, 0x0858, 0, {0x7f,0xfe,0x74,0xa0,0xf0,0x90,0x7f,0xff,0x74,0xa0,0xf0,0x90,0x7f,0xe0} }, +{ 14, 0x0866, 0, {0x74,0x03,0xf0,0x90,0x7f,0xe1,0x74,0x01,0xf0,0x90,0x7f,0xdd,0x74,0x80} }, +{ 11, 0x0874, 0, {0xf0,0x12,0x12,0x43,0x12,0x07,0x1e,0x7a,0x00,0x7b,0x00} }, +{ 9, 0x087f, 0, {0xc3,0xea,0x94,0x1e,0xeb,0x94,0x00,0x50,0x17} }, +{ 12, 0x0888, 0, {0x90,0x88,0x00,0xe0,0xf5,0x47,0x90,0x88,0x0b,0xe0,0xf5,0x47} }, +{ 9, 0x0894, 0, {0x90,0x7f,0x68,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x089d, 0, {0x80,0xe0} }, +{ 12, 0x089f, 0, {0x12,0x03,0xe1,0x90,0x7f,0xd6,0xe4,0xf0,0x7a,0x00,0x7b,0x00} }, +{ 13, 0x08ab, 0, {0x8a,0x04,0x8b,0x05,0xc3,0xea,0x94,0xe0,0xeb,0x94,0x2e,0x50,0x1a} }, +{ 14, 0x08b8, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 10, 0x08c6, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x08d0, 0, {0x80,0xd9} }, +{ 13, 0x08d2, 0, {0x90,0x7f,0xd6,0x74,0x02,0xf0,0x90,0x7f,0xd6,0x74,0x06,0xf0,0x90} }, +{ 14, 0x08df, 0, {0x7f,0xde,0x74,0x05,0xf0,0x90,0x7f,0xdf,0x74,0x05,0xf0,0x90,0x7f,0xac} }, +{ 14, 0x08ed, 0, {0xe4,0xf0,0x90,0x7f,0xad,0x74,0x05,0xf0,0x75,0xa8,0x80,0x75,0xf8,0x10} }, +{ 13, 0x08fb, 0, {0x90,0x7f,0xae,0x74,0x0b,0xf0,0x90,0x7f,0xe2,0x74,0x88,0xf0,0x90} }, +{ 12, 0x0908, 0, {0x7f,0xab,0x74,0x08,0xf0,0x75,0xe8,0x11,0x75,0x32,0x01,0x75} }, +{ 12, 0x0914, 0, {0x31,0x00,0x75,0x30,0x00,0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7} }, +{ 10, 0x0920, 0, {0xd0,0x05,0xd0,0x04,0x75,0x34,0x00,0x75,0x35,0x01} }, +{ 13, 0x092a, 0, {0x90,0x7f,0xae,0x74,0x03,0xf0,0x8c,0x02,0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0937, 0, {0x02,0x0a,0x3f} }, +{ 12, 0x093a, 0, {0x85,0x33,0x34,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90,0x7f,0x97} }, +{ 14, 0x0946, 0, {0x74,0x08,0xf0,0x90,0x7f,0x9d,0x74,0x88,0xf0,0x90,0x7f,0x9a,0xe0,0xfa} }, +{ 12, 0x0954, 0, {0x74,0x05,0x5a,0xf5,0x33,0x90,0x7f,0x9d,0x74,0x8f,0xf0,0x90} }, +{ 13, 0x0960, 0, {0x7f,0x97,0x74,0x02,0xf0,0x90,0x7f,0x9d,0x74,0x82,0xf0,0xe5,0x33} }, +{ 13, 0x096d, 0, {0x25,0xe0,0xfa,0x90,0x7f,0x9a,0xe0,0x54,0x05,0xfb,0x4a,0xf5,0x33} }, +{ 2, 0x097a, 0, {0x60,0x0c} }, +{ 12, 0x097c, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x4a,0xf0} }, +{ 11, 0x0988, 0, {0x75,0x6e,0x00,0x75,0x6f,0x00,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0993, 0, {0x11,0x44,0xd0,0x05,0xd0,0x04,0x90,0x17,0x13,0xe0,0xfa,0x74,0x80,0x2a} }, +{ 6, 0x09a1, 0, {0xfa,0xe5,0x33,0xb4,0x04,0x29} }, +{ 3, 0x09a7, 0, {0xba,0xa0,0x00} }, +{ 2, 0x09aa, 0, {0x50,0x24} }, +{ 13, 0x09ac, 0, {0x90,0x17,0x13,0xe0,0x04,0xfb,0x0b,0x90,0x17,0x13,0xeb,0xf0,0x90} }, +{ 14, 0x09b9, 0, {0x17,0x13,0xe0,0xfb,0x90,0x17,0x15,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05} }, +{ 9, 0x09c7, 0, {0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 5, 0x09d0, 0, {0xe5,0x33,0xb4,0x02,0x26} }, +{ 6, 0x09d5, 0, {0xc3,0x74,0x04,0x9a,0x50,0x20} }, +{ 13, 0x09db, 0, {0x90,0x17,0x13,0xe0,0xfa,0x1a,0x1a,0x90,0x17,0x13,0xea,0xf0,0x90} }, +{ 13, 0x09e8, 0, {0x17,0x13,0xe0,0xfa,0x90,0x17,0x15,0xf0,0xc0,0x04,0xc0,0x05,0x12} }, +{ 6, 0x09f5, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04} }, +{ 5, 0x09fb, 0, {0xe5,0x33,0xb4,0x08,0x1d} }, +{ 4, 0x0a00, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a04, 0, {0x74,0x01,0x25,0x35,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a0e, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a1a, 0, {0x05,0xd0,0x04} }, +{ 5, 0x0a1d, 0, {0xe5,0x33,0xb4,0x01,0x1d} }, +{ 4, 0x0a22, 0, {0xe5,0x34,0x70,0x19} }, +{ 10, 0x0a26, 0, {0xe5,0x35,0x24,0xff,0x54,0x0f,0xf5,0x35,0x85,0x35} }, +{ 12, 0x0a30, 0, {0x75,0x75,0x76,0x00,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0} }, +{ 3, 0x0a3c, 0, {0x05,0xd0,0x04} }, +{ 14, 0x0a3f, 0, {0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0,0x04,0x90,0x7f,0x96} }, +{ 14, 0x0a4d, 0, {0xe0,0xfa,0x90,0x7f,0x96,0x74,0x7f,0x5a,0xf0,0x90,0x7f,0x97,0x74,0x08} }, +{ 10, 0x0a5b, 0, {0xf0,0xc3,0xec,0x94,0x00,0xed,0x94,0x02,0x40,0x08} }, +{ 8, 0x0a65, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x20,0xe6,0x08} }, +{ 8, 0x0a6d, 0, {0xc3,0xe4,0x9c,0x74,0x08,0x9d,0x50,0x13} }, +{ 14, 0x0a75, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x40,0x65,0x02,0xf0,0x7c} }, +{ 5, 0x0a83, 0, {0x00,0x7d,0x00,0x80,0x05} }, +{ 5, 0x0a88, 0, {0x0c,0xbc,0x00,0x01,0x0d} }, +{ 5, 0x0a8d, 0, {0xe5,0x38,0xb4,0x01,0x0e} }, +{ 13, 0x0a92, 0, {0xc0,0x04,0xc0,0x05,0x12,0x04,0xf7,0xd0,0x05,0xd0,0x04,0x75,0x38} }, +{ 1, 0x0a9f, 0, {0x00} }, +{ 7, 0x0aa0, 0, {0xe5,0x31,0x70,0x03,0x02,0x09,0x2a} }, +{ 10, 0x0aa7, 0, {0x90,0x7f,0xc9,0xe0,0xfa,0x70,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0ab1, 0, {0x90,0x7f,0x96,0xe0,0xfa,0x90,0x7f,0x96,0x74,0x80,0x65,0x02,0xf0,0x90} }, +{ 9, 0x0abf, 0, {0x7d,0xc0,0xe0,0xfa,0xba,0x2c,0x02,0x80,0x03} }, +{ 3, 0x0ac8, 0, {0x02,0x0b,0x36} }, +{ 5, 0x0acb, 0, {0x75,0x32,0x00,0x7b,0x00} }, +{ 3, 0x0ad0, 0, {0xbb,0x64,0x00} }, +{ 2, 0x0ad3, 0, {0x50,0x1c} }, +{ 14, 0x0ad5, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 13, 0x0ae3, 0, {0x04,0xd0,0x03,0xd0,0x02,0x90,0x88,0x0f,0xe0,0xf5,0x47,0x0b,0x80} }, +{ 1, 0x0af0, 0, {0xdf} }, +{ 13, 0x0af1, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x07,0x1e,0x12,0x03,0xe1,0x12} }, +{ 12, 0x0afe, 0, {0x04,0xf7,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x75,0x6e,0x00,0x75} }, +{ 13, 0x0b0a, 0, {0x6f,0x01,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0x44,0xd0,0x05} }, +{ 9, 0x0b17, 0, {0xd0,0x04,0xd0,0x02,0x75,0x70,0x4d,0x75,0x71} }, +{ 11, 0x0b20, 0, {0x0c,0x75,0x72,0x02,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 11, 0x0b2b, 0, {0x11,0x75,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b36, 0, {0xba,0x2a,0x3b} }, +{ 13, 0x0b39, 0, {0x90,0x7f,0x98,0x74,0x20,0xf0,0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12} }, +{ 14, 0x0b46, 0, {0x01,0xdd,0xd0,0x05,0xd0,0x04,0xd0,0x02,0x90,0x7f,0x98,0x74,0x28,0xf0} }, +{ 2, 0x0b54, 0, {0x7b,0x00} }, +{ 3, 0x0b56, 0, {0xbb,0x0a,0x00} }, +{ 5, 0x0b59, 0, {0x40,0x03,0x02,0x0c,0x2d} }, +{ 14, 0x0b5e, 0, {0xc0,0x02,0xc0,0x03,0xc0,0x04,0xc0,0x05,0x12,0x01,0xdd,0xd0,0x05,0xd0} }, +{ 8, 0x0b6c, 0, {0x04,0xd0,0x03,0xd0,0x02,0x0b,0x80,0xe2} }, +{ 3, 0x0b74, 0, {0xba,0x2b,0x1a} }, +{ 8, 0x0b77, 0, {0x90,0x7f,0xc9,0xe0,0xfb,0xbb,0x40,0x12} }, +{ 14, 0x0b7f, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x12,0x05,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0b8d, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0b91, 0, {0xba,0x10,0x1f} }, +{ 14, 0x0b94, 0, {0x90,0x7f,0x96,0xe0,0xfb,0x90,0x7f,0x96,0x74,0x80,0x65,0x03,0xf0,0xc0} }, +{ 14, 0x0ba2, 0, {0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x3d,0xd0,0x05,0xd0,0x04,0xd0,0x02} }, +{ 3, 0x0bb0, 0, {0x02,0x0c,0x2d} }, +{ 3, 0x0bb3, 0, {0xba,0x11,0x12} }, +{ 14, 0x0bb6, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x6a,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bc4, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bc8, 0, {0xba,0x12,0x12} }, +{ 14, 0x0bcb, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x10,0x8f,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 4, 0x0bd9, 0, {0x02,0x02,0x0c,0x2d} }, +{ 3, 0x0bdd, 0, {0xba,0x13,0x0b} }, +{ 11, 0x0be0, 0, {0x90,0x7d,0xc1,0xe0,0xfb,0x90,0x88,0x00,0xf0,0x80,0x42} }, +{ 3, 0x0beb, 0, {0xba,0x14,0x11} }, +{ 14, 0x0bee, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x11,0xdd,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0bfc, 0, {0x02,0x80,0x2e} }, +{ 3, 0x0bff, 0, {0xba,0x15,0x1d} }, +{ 12, 0x0c02, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x75,0x90,0x7d,0xc2,0xe0,0xf5,0x76} }, +{ 14, 0x0c0e, 0, {0xc0,0x02,0xc0,0x04,0xc0,0x05,0x12,0x13,0xfe,0xd0,0x05,0xd0,0x04,0xd0} }, +{ 3, 0x0c1c, 0, {0x02,0x80,0x0e} }, +{ 3, 0x0c1f, 0, {0xba,0x16,0x0b} }, +{ 11, 0x0c22, 0, {0xc0,0x04,0xc0,0x05,0x12,0x13,0xa3,0xd0,0x05,0xd0,0x04} }, +{ 11, 0x0c2d, 0, {0x90,0x7f,0xc9,0xe4,0xf0,0x75,0x31,0x00,0x02,0x09,0x2a} }, +{ 1, 0x0c38, 0, {0x22} }, +{ 7, 0x0c39, 0, {0x53,0x55,0x50,0x45,0x4e,0x44,0x00} }, +{ 7, 0x0c40, 0, {0x52,0x45,0x53,0x55,0x4d,0x45,0x00} }, +{ 6, 0x0c47, 0, {0x20,0x56,0x6f,0x6c,0x20,0x00} }, +{ 13, 0x0c4d, 0, {0x44,0x41,0x42,0x55,0x53,0x42,0x20,0x76,0x31,0x2e,0x30,0x30,0x00} }, +{ 14, 0x0c5a, 0, {0xc0,0xe0,0xc0,0xf0,0xc0,0x82,0xc0,0x83,0xc0,0x02,0xc0,0x03,0xc0,0x04} }, +{ 14, 0x0c68, 0, {0xc0,0x05,0xc0,0x06,0xc0,0x07,0xc0,0x00,0xc0,0x01,0xc0,0xd0,0x75,0xd0} }, +{ 13, 0x0c76, 0, {0x00,0xc0,0x86,0x75,0x86,0x00,0xe5,0x91,0xc2,0xe4,0xf5,0x91,0x90} }, +{ 14, 0x0c83, 0, {0x7f,0xab,0x74,0x01,0xf0,0x90,0x7f,0xe8,0xe0,0xfa,0x90,0x7f,0xe9,0xe0} }, +{ 6, 0x0c91, 0, {0xfb,0xbb,0x00,0x02,0x80,0x03} }, +{ 3, 0x0c97, 0, {0x02,0x0d,0x38} }, +{ 3, 0x0c9a, 0, {0xba,0x80,0x14} }, +{ 14, 0x0c9d, 0, {0x90,0x7f,0x00,0x74,0x01,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5} }, +{ 6, 0x0cab, 0, {0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0cb1, 0, {0xba,0x82,0x02,0x80,0x03} }, +{ 3, 0x0cb6, 0, {0x02,0x0d,0x1d} }, +{ 8, 0x0cb9, 0, {0x90,0x7f,0xec,0xe0,0xfc,0xbc,0x01,0x00} }, +{ 2, 0x0cc1, 0, {0x40,0x21} }, +{ 6, 0x0cc3, 0, {0xc3,0x74,0x07,0x9c,0x40,0x1b} }, +{ 14, 0x0cc9, 0, {0xec,0x24,0xff,0x25,0xe0,0xfd,0x24,0xc6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cd7, 0, {0x83,0xe0,0xfd,0x53,0x05,0x01,0x90,0x7f,0x00,0xed,0xf0,0x80,0x2b} }, +{ 3, 0x0ce4, 0, {0xbc,0x81,0x00} }, +{ 2, 0x0ce7, 0, {0x40,0x21} }, +{ 6, 0x0ce9, 0, {0xc3,0x74,0x87,0x9c,0x40,0x1b} }, +{ 14, 0x0cef, 0, {0xec,0x24,0x7f,0x25,0xe0,0xfc,0x24,0xb6,0xf5,0x82,0xe4,0x34,0x7f,0xf5} }, +{ 13, 0x0cfd, 0, {0x83,0xe0,0xfc,0x53,0x04,0x01,0x90,0x7f,0x00,0xec,0xf0,0x80,0x05} }, +{ 5, 0x0d0a, 0, {0x90,0x7f,0x00,0xe4,0xf0} }, +{ 14, 0x0d0f, 0, {0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74,0x02,0xf0,0x02,0x0e,0xcd} }, +{ 5, 0x0d1d, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0d22, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0d25, 0, {0x90,0x7f,0x00,0xe4,0xf0,0x90,0x7f,0x01,0xe4,0xf0,0x90,0x7f,0xb5,0x74} }, +{ 5, 0x0d33, 0, {0x02,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d38, 0, {0xbb,0x01,0x2d} }, +{ 6, 0x0d3b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 3, 0x0d41, 0, {0xba,0x02,0x11} }, +{ 13, 0x0d44, 0, {0x75,0x59,0x00,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d51, 0, {0x02,0x02,0x0e,0xcd} }, +{ 5, 0x0d55, 0, {0xba,0x21,0x02,0x80,0x03} }, +{ 3, 0x0d5a, 0, {0x02,0x0e,0xcd} }, +{ 11, 0x0d5d, 0, {0x75,0x37,0x01,0x90,0x7f,0xc5,0xe4,0xf0,0x02,0x0e,0xcd} }, +{ 3, 0x0d68, 0, {0xbb,0x03,0x1f} }, +{ 6, 0x0d6b, 0, {0xba,0x00,0x03,0x02,0x0e,0xcd} }, +{ 5, 0x0d71, 0, {0xba,0x02,0x02,0x80,0x03} }, +{ 3, 0x0d76, 0, {0x02,0x0e,0xcd} }, +{ 13, 0x0d79, 0, {0x75,0x59,0x01,0xc0,0x02,0xc0,0x03,0x12,0x0e,0xf0,0xd0,0x03,0xd0} }, +{ 4, 0x0d86, 0, {0x02,0x02,0x0e,0xcd} }, +{ 3, 0x0d8a, 0, {0xbb,0x06,0x54} }, +{ 5, 0x0d8d, 0, {0xba,0x80,0x02,0x80,0x03} }, +{ 3, 0x0d92, 0, {0x02,0x0e,0xc5} }, +{ 8, 0x0d95, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x01,0x15} }, +{ 12, 0x0d9d, 0, {0x7c,0xfb,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0da9, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 10, 0x0db2, 0, {0x90,0x7f,0xeb,0xe0,0xfc,0xbc,0x02,0x02,0x80,0x03} }, +{ 3, 0x0dbc, 0, {0x02,0x0e,0xc5} }, +{ 10, 0x0dbf, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x02,0x80,0x03} }, +{ 3, 0x0dc9, 0, {0x02,0x0e,0xc5} }, +{ 12, 0x0dcc, 0, {0x7c,0x3b,0x7d,0x0f,0x8d,0x06,0x7f,0x00,0x90,0x7f,0xd4,0xee} }, +{ 9, 0x0dd8, 0, {0xf0,0x90,0x7f,0xd5,0xec,0xf0,0x02,0x0e,0xcd} }, +{ 6, 0x0de1, 0, {0xbb,0x07,0x03,0x02,0x0e,0xc5} }, +{ 3, 0x0de7, 0, {0xbb,0x08,0x10} }, +{ 13, 0x0dea, 0, {0xac,0x48,0x90,0x7f,0x00,0xec,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0df7, 0, {0x02,0x0e,0xcd} }, +{ 3, 0x0dfa, 0, {0xbb,0x09,0x31} }, +{ 5, 0x0dfd, 0, {0xba,0x00,0x02,0x80,0x03} }, +{ 3, 0x0e02, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e05, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xc3,0x74,0x01,0x9c,0x50,0x03,0x02,0x0e,0xc5} }, +{ 8, 0x0e13, 0, {0x90,0x7f,0xea,0xe0,0xfc,0xbc,0x00,0x0a} }, +{ 10, 0x0e1b, 0, {0x90,0x17,0x21,0xe4,0xf0,0x90,0x17,0x22,0xe4,0xf0} }, +{ 9, 0x0e25, 0, {0x90,0x7f,0xea,0xe0,0xf5,0x48,0x02,0x0e,0xcd} }, +{ 3, 0x0e2e, 0, {0xbb,0x0a,0x27} }, +{ 5, 0x0e31, 0, {0xba,0x81,0x02,0x80,0x03} }, +{ 3, 0x0e36, 0, {0x02,0x0e,0xc5} }, +{ 14, 0x0e39, 0, {0x90,0x7f,0xec,0xe0,0xfa,0x24,0x20,0xfa,0xe4,0x34,0x17,0xfc,0x8a,0x82} }, +{ 14, 0x0e47, 0, {0x8c,0x83,0xe0,0xfa,0x90,0x7f,0x00,0xf0,0x90,0x7f,0xb5,0x74,0x01,0xf0} }, +{ 3, 0x0e55, 0, {0x02,0x0e,0xcd} }, +{ 5, 0x0e58, 0, {0xbb,0x0b,0x02,0x80,0x03} }, +{ 3, 0x0e5d, 0, {0x02,0x0e,0xa9} }, +{ 13, 0x0e60, 0, {0x90,0x17,0x20,0xe4,0xf0,0x90,0x7f,0xec,0xe0,0xfa,0xba,0x01,0x1a} }, +{ 8, 0x0e6d, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x12} }, +{ 14, 0x0e75, 0, {0x90,0x7f,0xea,0xe0,0xfa,0x90,0x17,0x21,0xf0,0xc0,0x03,0x12,0x04,0xe2} }, +{ 4, 0x0e83, 0, {0xd0,0x03,0x80,0x46} }, +{ 8, 0x0e87, 0, {0x90,0x7f,0xec,0xe0,0xfa,0xba,0x02,0x3e} }, +{ 8, 0x0e8f, 0, {0x90,0x7f,0xed,0xe0,0xfa,0xba,0x00,0x36} }, +{ 13, 0x0e97, 0, {0xc0,0x03,0x12,0x04,0xef,0xd0,0x03,0x90,0x7f,0xea,0xe0,0xfa,0x90} }, +{ 5, 0x0ea4, 0, {0x17,0x22,0xf0,0x80,0x24} }, +{ 5, 0x0ea9, 0, {0xbb,0x12,0x02,0x80,0x17} }, +{ 5, 0x0eae, 0, {0xbb,0x81,0x02,0x80,0x0d} }, +{ 5, 0x0eb3, 0, {0xbb,0x83,0x02,0x80,0x08} }, +{ 5, 0x0eb8, 0, {0xbb,0x82,0x02,0x80,0x03} }, +{ 3, 0x0ebd, 0, {0xbb,0x84,0x05} }, +{ 5, 0x0ec0, 0, {0x12,0x06,0x4e,0x80,0x08} }, +{ 8, 0x0ec5, 0, {0x90,0x7f,0xb4,0x74,0x03,0xf0,0x80,0x06} }, +{ 6, 0x0ecd, 0, {0x90,0x7f,0xb4,0x74,0x02,0xf0} }, +{ 2, 0x0ed3, 0, {0xd0,0x86} }, +{ 14, 0x0ed5, 0, {0xd0,0xd0,0xd0,0x01,0xd0,0x00,0xd0,0x07,0xd0,0x06,0xd0,0x05,0xd0,0x04} }, +{ 13, 0x0ee3, 0, {0xd0,0x03,0xd0,0x02,0xd0,0x83,0xd0,0x82,0xd0,0xf0,0xd0,0xe0,0x32} }, +{ 11, 0x0ef0, 0, {0x90,0x7f,0xec,0xe0,0xf5,0x5a,0xc3,0x94,0x01,0x40,0x1d} }, +{ 7, 0x0efb, 0, {0xc3,0x74,0x07,0x95,0x5a,0x40,0x16} }, +{ 13, 0x0f02, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xc6,0xf5,0x82,0xe4,0x34} }, +{ 9, 0x0f0f, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0,0x80,0x22} }, +{ 7, 0x0f18, 0, {0xc3,0xe5,0x5a,0x94,0x81,0x40,0x1b} }, +{ 7, 0x0f1f, 0, {0xc3,0x74,0x87,0x95,0x5a,0x40,0x14} }, +{ 13, 0x0f26, 0, {0xe5,0x5a,0x24,0xff,0x25,0xe0,0xfa,0x24,0xb6,0xf5,0x82,0xe4,0x34} }, +{ 7, 0x0f33, 0, {0x7f,0xf5,0x83,0xaa,0x59,0xea,0xf0} }, +{ 1, 0x0f3a, 0, {0x22} }, +{ 14, 0x0f3b, 0, {0x09,0x02,0xba,0x00,0x03,0x01,0x00,0x40,0x00,0x09,0x04,0x00,0x00,0x00} }, +{ 14, 0x0f49, 0, {0x01,0x01,0x00,0x00,0x09,0x24,0x01,0x00,0x01,0x3d,0x00,0x01,0x01,0x0c} }, +{ 14, 0x0f57, 0, {0x24,0x02,0x01,0x10,0x07,0x00,0x02,0x03,0x00,0x00,0x00,0x0d,0x24,0x06} }, +{ 14, 0x0f65, 0, {0x03,0x01,0x02,0x15,0x00,0x03,0x00,0x03,0x00,0x00,0x09,0x24,0x03,0x02} }, +{ 14, 0x0f73, 0, {0x01,0x01,0x00,0x01,0x00,0x09,0x24,0x03,0x04,0x02,0x03,0x00,0x03,0x00} }, +{ 14, 0x0f81, 0, {0x09,0x24,0x03,0x05,0x03,0x06,0x00,0x01,0x00,0x09,0x04,0x01,0x00,0x00} }, +{ 14, 0x0f8f, 0, {0x01,0x02,0x00,0x00,0x09,0x04,0x01,0x01,0x01,0x01,0x02,0x00,0x00,0x07} }, +{ 14, 0x0f9d, 0, {0x24,0x01,0x02,0x01,0x01,0x00,0x0b,0x24,0x02,0x01,0x02,0x02,0x10,0x01} }, +{ 14, 0x0fab, 0, {0x80,0xbb,0x00,0x09,0x05,0x88,0x05,0x00,0x01,0x01,0x00,0x00,0x07,0x25} }, +{ 14, 0x0fb9, 0, {0x01,0x00,0x00,0x00,0x00,0x09,0x04,0x02,0x00,0x02,0x00,0x00,0x00,0x00} }, +{ 14, 0x0fc7, 0, {0x07,0x05,0x82,0x02,0x40,0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00} }, +{ 14, 0x0fd5, 0, {0x09,0x04,0x02,0x01,0x03,0x00,0x00,0x00,0x00,0x07,0x05,0x82,0x02,0x40} }, +{ 14, 0x0fe3, 0, {0x00,0x00,0x07,0x05,0x02,0x02,0x40,0x00,0x00,0x09,0x05,0x89,0x05,0xa0} }, +{ 10, 0x0ff1, 0, {0x01,0x01,0x00,0x00,0x00,0xff,0xff,0xff,0xff,0x00} }, +{ 14, 0x0ffb, 0, {0x12,0x01,0x00,0x01,0x00,0x00,0x00,0x40,0x47,0x05,0x99,0x99,0x00,0x01} }, +{ 14, 0x1009, 0, {0x00,0x00,0x00,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x09,0x02,0xba} }, +{ 4, 0x1017, 0, {0x00,0x03,0x01,0x00} }, +{ 2, 0x101b, 0, {0x7a,0x00} }, +{ 3, 0x101d, 0, {0xba,0x05,0x00} }, +{ 2, 0x1020, 0, {0x50,0x17} }, +{ 8, 0x1022, 0, {0x90,0x7f,0xa5,0xe0,0xfb,0x30,0xe0,0x05} }, +{ 5, 0x102a, 0, {0x90,0x00,0x01,0x80,0x0d} }, +{ 10, 0x102f, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xe4} }, +{ 3, 0x1039, 0, {0x90,0x00,0x01} }, +{ 1, 0x103c, 0, {0x22} }, +{ 14, 0x103d, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0x00,0x7d} }, +{ 4, 0x104b, 0, {0x7e,0xeb,0x60,0x12} }, +{ 14, 0x104f, 0, {0x89,0x82,0x8a,0x83,0xe0,0xa3,0xa9,0x82,0xaa,0x83,0x8c,0x82,0x8d,0x83} }, +{ 4, 0x105d, 0, {0xf0,0x0c,0xdb,0xee} }, +{ 8, 0x1061, 0, {0x90,0x7d,0xc3,0xe0,0x90,0x7f,0xb9,0xf0} }, +{ 1, 0x1069, 0, {0x22} }, +{ 14, 0x106a, 0, {0x90,0x7d,0xc1,0xe0,0xf9,0xa3,0xe0,0xfa,0xa3,0xe0,0xfb,0x7c,0xc4,0x7d} }, +{ 4, 0x1078, 0, {0x7d,0xeb,0x60,0xe5} }, +{ 14, 0x107c, 0, {0x8c,0x82,0x8d,0x83,0xe0,0x0c,0x89,0x82,0x8a,0x83,0xf0,0xa3,0xa9,0x82} }, +{ 4, 0x108a, 0, {0xaa,0x83,0xdb,0xee} }, +{ 1, 0x108e, 0, {0x22} }, +{ 14, 0x108f, 0, {0x90,0x7f,0xa5,0x74,0x80,0xf0,0x05,0x86,0x90,0x7d,0xc1,0xe0,0x05,0x86} }, +{ 14, 0x109d, 0, {0xa3,0xf0,0x12,0x10,0x1b,0x90,0x7f,0xa6,0x05,0x86,0xa3,0xa3,0xe0,0xf9} }, +{ 5, 0x10ab, 0, {0x60,0x16,0xa3,0x05,0x86} }, +{ 13, 0x10b0, 0, {0x90,0x7f,0xa6,0x05,0x86,0xe0,0xa3,0x05,0x86,0xf0,0xc0,0x01,0x12} }, +{ 6, 0x10bd, 0, {0x10,0x1b,0xd0,0x01,0xd9,0xed} }, +{ 6, 0x10c3, 0, {0x90,0x7f,0xa5,0x74,0x40,0xf0} }, +{ 1, 0x10c9, 0, {0x22} }, +{ 8, 0x10ca, 0, {0x90,0x88,0x02,0x74,0x01,0xf0,0x7a,0x00} }, +{ 3, 0x10d2, 0, {0xba,0xff,0x00} }, +{ 2, 0x10d5, 0, {0x50,0x0a} }, +{ 10, 0x10d7, 0, {0xc0,0x02,0x12,0x01,0xdd,0xd0,0x02,0x0a,0x80,0xf1} }, +{ 1, 0x10e1, 0, {0x22} }, +{ 5, 0x10e2, 0, {0xe5,0x6b,0xb4,0xc0,0x08} }, +{ 8, 0x10e7, 0, {0x90,0x88,0x03,0xe5,0x6c,0xf0,0x80,0x06} }, +{ 6, 0x10ef, 0, {0x90,0x88,0x02,0xe5,0x6c,0xf0} }, +{ 4, 0x10f5, 0, {0x7a,0x00,0x7b,0x00} }, +{ 11, 0x10f9, 0, {0xc3,0xea,0x94,0x32,0xeb,0x64,0x80,0x94,0x80,0x50,0x07} }, +{ 5, 0x1104, 0, {0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x1109, 0, {0x80,0xee} }, +{ 1, 0x110b, 0, {0x22} }, +{ 10, 0x110c, 0, {0x90,0x88,0x03,0xe5,0x6d,0xf0,0x05,0x39,0x7a,0x00} }, +{ 3, 0x1116, 0, {0xba,0x28,0x00} }, +{ 2, 0x1119, 0, {0x50,0x03} }, +{ 3, 0x111b, 0, {0x0a,0x80,0xf8} }, +{ 5, 0x111e, 0, {0xe5,0x39,0xb4,0x10,0x08} }, +{ 8, 0x1123, 0, {0x90,0x88,0x02,0x74,0xc0,0xf0,0x80,0x0e} }, +{ 5, 0x112b, 0, {0xe5,0x39,0xb4,0x20,0x09} }, +{ 9, 0x1130, 0, {0x90,0x88,0x02,0x74,0x80,0xf0,0x75,0x39,0x00} }, +{ 2, 0x1139, 0, {0x7a,0x00} }, +{ 3, 0x113b, 0, {0xba,0x28,0x00} }, +{ 2, 0x113e, 0, {0x50,0x03} }, +{ 3, 0x1140, 0, {0x0a,0x80,0xf8} }, +{ 1, 0x1143, 0, {0x22} }, +{ 4, 0x1144, 0, {0xe5,0x6f,0x60,0x02} }, +{ 2, 0x1148, 0, {0x80,0x07} }, +{ 7, 0x114a, 0, {0x7a,0x00,0x75,0x39,0x00,0x80,0x05} }, +{ 5, 0x1151, 0, {0x7a,0x40,0x75,0x39,0x10} }, +{ 9, 0x1156, 0, {0xe5,0x6e,0x2a,0xfa,0xe5,0x6e,0x25,0x39,0xf5} }, +{ 10, 0x115f, 0, {0x39,0x90,0x88,0x02,0x74,0x80,0x2a,0xf0,0x7a,0x00} }, +{ 8, 0x1169, 0, {0xc3,0xea,0x64,0x80,0x94,0xa8,0x50,0x03} }, +{ 3, 0x1171, 0, {0x0a,0x80,0xf5} }, +{ 1, 0x1174, 0, {0x22} }, +{ 6, 0x1175, 0, {0xaa,0x70,0xab,0x71,0xac,0x72} }, +{ 12, 0x117b, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x12,0x14,0xee,0xfd,0x60,0x18} }, +{ 13, 0x1187, 0, {0x8d,0x6d,0xc0,0x02,0xc0,0x03,0xc0,0x04,0x12,0x11,0x0c,0xd0,0x04} }, +{ 9, 0x1194, 0, {0xd0,0x03,0xd0,0x02,0x0a,0xba,0x00,0x01,0x0b} }, +{ 2, 0x119d, 0, {0x80,0xdc} }, +{ 1, 0x119f, 0, {0x22} }, +{ 13, 0x11a0, 0, {0xe5,0x73,0xc4,0x54,0x0f,0xfa,0x53,0x02,0x0f,0xc3,0x74,0x09,0x9a} }, +{ 2, 0x11ad, 0, {0x50,0x06} }, +{ 6, 0x11af, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11b5, 0, {0x74,0x30,0x2a,0xfb} }, +{ 12, 0x11b9, 0, {0x8b,0x6d,0xc0,0x03,0x12,0x11,0x0c,0xd0,0x03,0xaa,0x73,0x53} }, +{ 8, 0x11c5, 0, {0x02,0x0f,0xc3,0x74,0x09,0x9a,0x50,0x06} }, +{ 6, 0x11cd, 0, {0x74,0x37,0x2a,0xfb,0x80,0x04} }, +{ 4, 0x11d3, 0, {0x74,0x30,0x2a,0xfb} }, +{ 5, 0x11d7, 0, {0x8b,0x6d,0x12,0x11,0x0c} }, +{ 1, 0x11dc, 0, {0x22} }, +{ 7, 0x11dd, 0, {0x90,0x7d,0xc3,0xe0,0xfa,0x60,0x0f} }, +{ 12, 0x11e4, 0, {0x90,0x7d,0xc1,0xe0,0xf5,0x6e,0x90,0x7d,0xc2,0xe0,0xf5,0x6f} }, +{ 3, 0x11f0, 0, {0x12,0x11,0x44} }, +{ 12, 0x11f3, 0, {0x90,0x7d,0xff,0xe4,0xf0,0x75,0x70,0xc4,0x75,0x71,0x7d,0x75} }, +{ 5, 0x11ff, 0, {0x72,0x01,0x12,0x11,0x75} }, +{ 1, 0x1204, 0, {0x22} }, +{ 2, 0x1205, 0, {0x7a,0x04} }, +{ 3, 0x1207, 0, {0xba,0x40,0x00} }, +{ 2, 0x120a, 0, {0x50,0x36} }, +{ 14, 0x120c, 0, {0xea,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfb,0x7c,0x00} }, +{ 3, 0x121a, 0, {0xbc,0x08,0x00} }, +{ 2, 0x121d, 0, {0x50,0x20} }, +{ 6, 0x121f, 0, {0x8b,0x05,0xed,0x30,0xe7,0x0b} }, +{ 11, 0x1225, 0, {0x90,0x7f,0x96,0x74,0x42,0xf0,0x74,0xc3,0xf0,0x80,0x08} }, +{ 8, 0x1230, 0, {0x90,0x7f,0x96,0xe4,0xf0,0x74,0x81,0xf0} }, +{ 7, 0x1238, 0, {0xeb,0x25,0xe0,0xfb,0x0c,0x80,0xdb} }, +{ 3, 0x123f, 0, {0x0a,0x80,0xc5} }, +{ 1, 0x1242, 0, {0x22} }, +{ 4, 0x1243, 0, {0x7a,0x00,0x7b,0xef} }, +{ 3, 0x1247, 0, {0xba,0x10,0x00} }, +{ 2, 0x124a, 0, {0x50,0x20} }, +{ 14, 0x124c, 0, {0x74,0x11,0x2b,0xfb,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0x8c,0x82,0x8d} }, +{ 14, 0x125a, 0, {0x83,0xe4,0xf0,0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe4} }, +{ 4, 0x1268, 0, {0xf0,0x0a,0x80,0xdb} }, +{ 1, 0x126c, 0, {0x22} }, +{ 14, 0x126d, 0, {0x74,0xf8,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x127b, 0, {0x74,0xf9,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1289, 0, {0x74,0xfa,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x1297, 0, {0x74,0xfb,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 14, 0x12a5, 0, {0x74,0xff,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x12b3, 0, {0x22} }, +{ 14, 0x12b4, 0, {0x12,0x03,0xcb,0x12,0x12,0x6d,0x7a,0xc0,0x7b,0x87,0x7c,0x01,0x74,0x01} }, +{ 14, 0x12c2, 0, {0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74} }, +{ 14, 0x12d0, 0, {0x01,0x12,0x14,0xbf,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e} }, +{ 14, 0x12de, 0, {0x83,0x8f,0xf0,0x74,0x06,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b} }, +{ 14, 0x12ec, 0, {0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74} }, +{ 14, 0x12fa, 0, {0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0} }, +{ 14, 0x1308, 0, {0x74,0x0b,0x12,0x14,0xbf,0x74,0x01,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07} }, +{ 14, 0x1316, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x74,0x08,0x12,0x14,0xbf,0x74,0x01,0x2d} }, +{ 14, 0x1324, 0, {0xfa,0xe4,0x3e,0xfb,0x8f,0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x01} }, +{ 14, 0x1332, 0, {0x12,0x14,0xbf,0x2a,0xfd,0xe4,0x3b,0xfe,0x8c,0x07,0x8a,0x82,0x8b,0x83} }, +{ 14, 0x1340, 0, {0x8c,0xf0,0xe4,0x12,0x14,0xbf,0x74,0x01,0x2d,0xfa,0xe4,0x3e,0xfb,0x8f} }, +{ 14, 0x134e, 0, {0x04,0x8d,0x82,0x8e,0x83,0x8f,0xf0,0x74,0x03,0x12,0x14,0xbf,0x7d,0x00} }, +{ 3, 0x135c, 0, {0xbd,0x06,0x00} }, +{ 2, 0x135f, 0, {0x50,0x12} }, +{ 11, 0x1361, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0x0a,0xba,0x00,0x01,0x0b} }, +{ 7, 0x136c, 0, {0xe4,0x12,0x14,0xbf,0x0d,0x80,0xe9} }, +{ 13, 0x1373, 0, {0x8a,0x82,0x8b,0x83,0x8c,0xf0,0xe5,0x74,0x12,0x14,0xbf,0x74,0xf9} }, +{ 14, 0x1380, 0, {0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x0f,0xf0,0x74} }, +{ 14, 0x138e, 0, {0xfe,0x24,0x00,0xf5,0x82,0x74,0x03,0x34,0x84,0xf5,0x83,0x74,0x01,0xf0} }, +{ 6, 0x139c, 0, {0x12,0x03,0xe1,0x12,0x04,0xf7} }, +{ 1, 0x13a2, 0, {0x22} }, +{ 13, 0x13a3, 0, {0x90,0x7d,0xc1,0xe0,0xfa,0x24,0x00,0xfb,0xe4,0x34,0x19,0xfc,0x90} }, +{ 14, 0x13b0, 0, {0x7d,0xc2,0xe0,0xfd,0x8b,0x82,0x8c,0x83,0xf0,0x75,0xf0,0x11,0xea,0xa4} }, +{ 3, 0x13be, 0, {0xfa,0x7b,0x00} }, +{ 3, 0x13c1, 0, {0xbb,0x10,0x00} }, +{ 2, 0x13c4, 0, {0x50,0x24} }, +{ 14, 0x13c6, 0, {0xea,0x24,0x00,0xfc,0xe4,0x34,0x18,0xfd,0xeb,0x2c,0xfc,0xe4,0x3d,0xfd} }, +{ 14, 0x13d4, 0, {0x74,0x04,0x2b,0x24,0xc0,0xf5,0x82,0xe4,0x34,0x7d,0xf5,0x83,0xe0,0xfe} }, +{ 8, 0x13e2, 0, {0x8c,0x82,0x8d,0x83,0xf0,0x0b,0x80,0xd7} }, +{ 14, 0x13ea, 0, {0xea,0x24,0x00,0xfa,0xe4,0x34,0x18,0xfb,0x74,0x10,0x2a,0xf5,0x82,0xe4} }, +{ 5, 0x13f8, 0, {0x3b,0xf5,0x83,0xe4,0xf0} }, +{ 1, 0x13fd, 0, {0x22} }, +{ 4, 0x13fe, 0, {0xe5,0x76,0x60,0x02} }, +{ 2, 0x1402, 0, {0x80,0x16} }, +{ 12, 0x1404, 0, {0x74,0x0f,0x55,0x75,0xfa,0x8a,0x75,0x24,0x00,0xf5,0x82,0xe4} }, +{ 10, 0x1410, 0, {0x34,0x19,0xf5,0x83,0xe0,0xf5,0x74,0x12,0x12,0xb4} }, +{ 10, 0x141a, 0, {0x12,0x10,0xca,0x75,0x6e,0x00,0x75,0x6f,0x00,0x12} }, +{ 6, 0x1424, 0, {0x11,0x44,0x75,0x70,0xb9,0x75} }, +{ 6, 0x142a, 0, {0x71,0x14,0x75,0x72,0x02,0x12} }, +{ 11, 0x1430, 0, {0x11,0x75,0xe5,0x76,0xb4,0x02,0x04,0x74,0x01,0x80,0x01} }, +{ 1, 0x143b, 0, {0xe4} }, +{ 3, 0x143c, 0, {0xfa,0x70,0x0f} }, +{ 12, 0x143f, 0, {0x74,0x01,0x25,0x75,0xf5,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0} }, +{ 3, 0x144b, 0, {0x02,0x80,0x0a} }, +{ 10, 0x144e, 0, {0x85,0x75,0x73,0xc0,0x02,0x12,0x11,0xa0,0xd0,0x02} }, +{ 12, 0x1458, 0, {0x75,0x6e,0x00,0x75,0x6f,0x01,0xc0,0x02,0x12,0x11,0x44,0xd0} }, +{ 4, 0x1464, 0, {0x02,0xea,0x70,0x1a} }, +{ 13, 0x1468, 0, {0x75,0xf0,0x11,0xe5,0x75,0xa4,0xfa,0x24,0x00,0xfa,0xe4,0x34,0x18} }, +{ 9, 0x1475, 0, {0xfb,0x8a,0x70,0x8b,0x71,0x75,0x72,0x01,0x12} }, +{ 4, 0x147e, 0, {0x11,0x75,0x80,0x36} }, +{ 2, 0x1482, 0, {0x7a,0x00} }, +{ 3, 0x1484, 0, {0xba,0x10,0x00} }, +{ 2, 0x1487, 0, {0x50,0x2f} }, +{ 13, 0x1489, 0, {0xea,0x24,0x00,0xf5,0x82,0xe4,0x34,0x19,0xf5,0x83,0xe0,0xfb,0xe5} }, +{ 4, 0x1496, 0, {0x75,0xb5,0x03,0x1b} }, +{ 14, 0x149a, 0, {0x75,0xf0,0x11,0xea,0xa4,0xfb,0x24,0x00,0xfb,0xe4,0x34,0x18,0xfc,0x8b} }, +{ 9, 0x14a8, 0, {0x70,0x8c,0x71,0x75,0x72,0x01,0xc0,0x02,0x12} }, +{ 4, 0x14b1, 0, {0x11,0x75,0xd0,0x02} }, +{ 3, 0x14b5, 0, {0x0a,0x80,0xcc} }, +{ 1, 0x14b8, 0, {0x22} }, +{ 6, 0x14b9, 0, {0x50,0x72,0x6f,0x67,0x20,0x00} }, +{ 14, 0x14bf, 0, {0xc8,0xc0,0xe0,0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0b,0x14,0x60,0x0f,0x14} }, +{ 7, 0x14cd, 0, {0x60,0x11,0x14,0x60,0x12,0x80,0x15} }, +{ 7, 0x14d4, 0, {0xd0,0xe0,0xa8,0x82,0xf6,0x80,0x0e} }, +{ 5, 0x14db, 0, {0xd0,0xe0,0xf0,0x80,0x09} }, +{ 4, 0x14e0, 0, {0xd0,0xe0,0x80,0x05} }, +{ 5, 0x14e4, 0, {0xd0,0xe0,0xa8,0x82,0xf2} }, +{ 4, 0x14e9, 0, {0xc8,0xd0,0xe0,0xc8} }, +{ 1, 0x14ed, 0, {0x22} }, +{ 14, 0x14ee, 0, {0xc8,0xc0,0xe0,0xe5,0xf0,0x60,0x0d,0x14,0x60,0x0f,0x14,0x60,0x0f,0x14} }, +{ 6, 0x14fc, 0, {0x60,0x10,0x74,0xff,0x80,0x0f} }, +{ 5, 0x1502, 0, {0xa8,0x82,0xe6,0x80,0x0a} }, +{ 3, 0x1507, 0, {0xe0,0x80,0x07} }, +{ 4, 0x150a, 0, {0xe4,0x93,0x80,0x03} }, +{ 3, 0x150e, 0, {0xa8,0x82,0xe2} }, +{ 4, 0x1511, 0, {0xf8,0xd0,0xe0,0xc8} }, +{ 1, 0x1515, 0, {0x22} }, +{ 0, 0x0000, 1, {0} } + +}; + +static unsigned char bitstream[] = { + +0x00,0x09,0x0F,0xF0,0x0F,0xF0,0x0F,0xF0, 0x0F,0xF0,0x00,0x00,0x01,0x61,0x00,0x0D, +0x64,0x61,0x62,0x75,0x73,0x62,0x74,0x72, 0x2E,0x6E,0x63,0x64,0x00,0x62,0x00,0x0B, +0x73,0x31,0x30,0x78,0x6C,0x76,0x71,0x31, 0x30,0x30,0x00,0x63,0x00,0x0B,0x31,0x39, +0x39,0x39,0x2F,0x30,0x39,0x2F,0x32,0x34, 0x00,0x64,0x00,0x09,0x31,0x30,0x3A,0x34, +0x32,0x3A,0x34,0x36,0x00,0x65,0x00,0x00, 0x2E,0xC0,0xFF,0x20,0x17,0x5F,0x9F,0x5B, +0xFE,0xFB,0xBB,0xB7,0xBB,0xBB,0xFB,0xBF, 0xAF,0xEF,0xFB,0xDF,0xB7,0xFB,0xFB,0x7F, +0xBF,0xB7,0xEF,0xF2,0xFF,0xFB,0xFE,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xBF,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xFA,0xFF,0xFF,0xFF, 0xC9,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xA3,0xFF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xE3,0xFE,0xFF,0xBF, 0xE3,0xFE,0xFF,0xBF,0x6F,0xFB,0xF6,0xFF, +0xBF,0xFF,0x47,0xFF,0xFF,0x9F,0xEE,0xF9, 0xFE,0xCF,0x9F,0xEF,0xFB,0xCF,0x9B,0xEE, +0xF8,0xFE,0xEF,0x8F,0xEE,0xFB,0xFE,0x0B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFB,0xFF,0xFF, 0xBF,0xFF,0xFF,0xFC,0x17,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFB,0xFF,0xFF,0x7F,0xFF,0xFF, +0xFC,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0x5F,0xFF, 0xFF,0xFD,0xFF,0xFF,0xDB,0xFF,0xFD,0xFF, +0x77,0xFF,0xFD,0xFF,0xFF,0xDF,0xFE,0xFD, 0xFF,0xFF,0xF2,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE1, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xE3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x67,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7F,0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xDF,0xFF,0xFF,0xFF,0x2F,0xFF, +0xF3,0xFD,0xFF,0x7F,0xDE,0xF7,0xFD,0xFF, 0x7F,0xF7,0x7D,0xFF,0x7F,0xDF,0xF7,0xBD, +0xFF,0x7F,0xFF,0x1F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, 0x3F,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F, +0x9F,0xE7,0xFA,0x7F,0x9F,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xFF,0xFC,0x7F,0xBF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xB7, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xE0,0xFD,0xF9,0xFE,0x7F,0x9F,0xE7, 0xF9,0xFE,0x7F,0x9D,0xF9,0xFE,0x7D,0x9D, +0xE7,0xF9,0xFE,0x7F,0x9F,0xED,0xED,0xFF, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF, 0x7F,0xDF,0xFF,0x9B,0xFF,0xEF,0xFB,0xFE, +0xFB,0xBF,0xEF,0xBB,0xFE,0xFF,0xAF,0xBB, 0xBE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFF, +0xB7,0xBF,0xDB,0xF6,0xBD,0xBF,0x6B,0xDB, 0xF6,0xF9,0xBF,0x5B,0xD6,0xF9,0xBF,0x6F, +0xDB,0xF6,0xFD,0xBF,0xFF,0x0E,0xFF,0xFF, 0xFF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0x7F, +0xF7,0xBD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xDF,0x9F,0xFF,0xFF,0xFF,0xFE,0xFF, +0xFF,0xEF,0xFE,0xFE,0xFF,0xFF,0x77,0xFF, 0xFB,0xFB,0xFF,0xFF,0xFF,0xFF,0xF8,0x3F, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xF4,0x7F,0xFF,0xFE,0xFD, 0xBE,0xFF,0xDF,0xFE,0xFF,0xFF,0xEF,0x7F, +0xFF,0xCF,0xFF,0xCF,0xFF,0xFF,0xFF,0xDF, 0xE6,0xFF,0xFF,0x7F,0xDF,0xF7,0xDD,0x7F, +0x7F,0xDF,0xF7,0xFF,0x7F,0xDF,0xD7,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFF,0xCD,0xFF,0xF2, +0xFF,0xFF,0x4F,0x7F,0xF4,0xFF,0xFF,0xFF, 0xE7,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBB,0xFF,0xEF,0xFF,0xFE,0xFF, 0xFF,0xFF,0xEF,0xFF,0xFF,0xEF,0xFF,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x65, 0xEF,0xFF,0xFF,0x7F,0xFF,0xFD,0xEF,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xCF,0xDF,0xFE,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xF3,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xEB,0xFF,0xFE,0xBF,0xFF, 0xEB,0xFF,0xFC,0x7F,0xFF,0xFF,0xFF,0xEE, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xD6,0xFF,0xFD,0xBF,0xFF,0xFB,0xFF,0xFE, +0xFD,0xFF,0xFF,0xFD,0xEF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xDE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0x7F,0xBF, 0xFF,0x5F,0xDF,0xFF,0xFF,0xBF,0x77,0xFF, +0xFF,0xFF,0x7F,0xD7,0xFF,0xFF,0xFF,0xFF, 0xFF,0xC3,0xFF,0xFF,0xFF,0xFF,0xDF,0xEF, +0xFF,0xFF,0xFE,0xFB,0xFF,0xFF,0xDF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xB7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xAF,0x7F,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xBF,0xDF,0xF3,0xFD,0xFB,0xFF,0x5B, +0xFD,0xFF,0xBF,0xEF,0xF7,0xFF,0xFF,0x7D, 0xFF,0xFF,0xFF,0xFF,0xF8,0x3B,0xFF,0xBF, +0x6F,0xFF,0xFE,0xFF,0xBF,0xFF,0xEB,0x7D, 0xFF,0xEF,0xFB,0xFE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF2,0x7F,0xFC,0xFF,0x3F,0xDF,0xED, 0xFE,0xFF,0xFF,0xFF,0xFF,0xEF,0x5F,0xF7, +0xB5,0xFF,0xEF,0xFF,0xFF,0xFF,0xE0,0x3F, 0x9F,0x9E,0xFF,0xFF,0xEF,0xFF,0xDF,0xFF, +0xBF,0x5F,0xBF,0xCF,0xF3,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x69,0xAF,0x33,0xFD,0xFF, +0xFB,0xFF,0xFF,0xFF,0xFF,0xFC,0xFF,0x7F, 0xD9,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xF5, +0xA3,0xDF,0x6E,0xDE,0xFF,0xFF,0xBD,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFE,0xE7,0xFD, +0xFF,0xFF,0xFF,0xF9,0xEF,0xC6,0xFE,0xB7, 0xAD,0xE5,0xF9,0xFF,0xFF,0xFF,0xCF,0xFF, +0xFF,0xFF,0xCD,0xFB,0x7F,0xFF,0xFF,0xFF, 0xF9,0xF6,0x0F,0xDF,0xEC,0xCF,0x7F,0xFF, +0xFB,0x7F,0xFF,0xFF,0xFF,0xFD,0xFF,0xFE, 0xF9,0xFD,0x7F,0xFF,0x7F,0xFF,0xF9,0x5B, +0xFF,0x73,0xDC,0xFD,0x7B,0xDF,0xFF,0xFF, 0xFF,0x7B,0xFF,0xFF,0xF7,0x53,0xD6,0xFF, +0xFF,0xFF,0xFF,0xD8,0x9F,0xFE,0xFF,0xEF, 0x7F,0xEE,0xFF,0xFF,0xFF,0xFB,0xED,0xED, +0xFD,0xFF,0xFE,0xFF,0xFF,0xFB,0x7F,0xFF, 0xE2,0x7F,0xFF,0x6F,0xD8,0x57,0xF7,0xFF, +0xFF,0xFF,0xDF,0xFF,0xE8,0xFF,0xFF,0xFD, 0xFF,0xFF,0xFC,0x7F,0xFF,0xE4,0xFF,0xFB, +0xEF,0xFB,0xFE,0xDF,0xB7,0xED,0xFF,0xFE, 0xDF,0x7F,0xFF,0xFE,0x7F,0xB7,0xFF,0xFF, +0xFF,0xFF,0x89,0xFF,0xFF,0xCF,0xF3,0xFE, 0x7F,0xFF,0xEF,0xFF,0xFE,0x7E,0x7F,0xFB, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF1, 0xFF,0xEB,0x7A,0xD5,0xBF,0x6F,0xDB,0xBE, +0xFD,0xB7,0xD8,0xF6,0xE5,0xBF,0x6F,0xFB, 0xFE,0xF5,0xBD,0x7E,0x06,0xFF,0xDF,0xF7, +0xFB,0xF6,0xFF,0x3F,0xFF,0xDB,0xFF,0xFF, 0x6F,0xFB,0xF7,0xFF,0xFF,0xFF,0xFB,0xFE, +0xF7,0xAF,0xFF,0xB7,0xED,0xEF,0xF7,0xFE, 0xFF,0xFF,0xDF,0xFF,0xFE,0xFF,0xEF,0xFF, +0xFF,0xFF,0xFF,0xBF,0xF7,0xFC,0x1F,0xEE, 0xFB,0xFE,0xBD,0xFF,0x7F,0x5F,0xD7,0xFD, +0xFB,0x43,0xFF,0xFF,0xFD,0xFF,0x5F,0xFF, 0xF7,0xFF,0xF9,0x3F,0xFF,0xCF,0xF3,0xFD, +0xF7,0x7E,0xEF,0xA7,0xF9,0xFE,0x8F,0xA7, 0xE9,0xF3,0x7E,0x9F,0xFB,0xF8,0xFF,0xFF, +0x3F,0xFD,0x7F,0x5F,0xDF,0xFD,0xFF,0xFF, 0x5F,0xFF,0xFD,0x5F,0xFF,0xFF,0x7F,0xFD, +0x7F,0xFD,0x9F,0xFF,0xE0,0xFF,0xFA,0xF8, 0xBE,0x6F,0x9F,0xE6,0xF8,0xBE,0x3F,0x9A, +0xF9,0xBE,0x6F,0x9F,0xE2,0xF9,0xFE,0x6F, 0x9F,0xF9,0xFF,0xF5,0xFD,0x7F,0xCF,0xDF, +0xFD,0xFD,0x7F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xF7,0xF5,0xFD,0x0F,0xDB,0xFF,0xD3,0xFF, +0xEB,0xFA,0xFF,0xFF,0xBF,0xFF,0xFA,0xFF, 0xFF,0xCB,0xFB,0xFE,0xFF,0xFF,0xEB,0xFA, +0xFE,0xFF,0xFF,0xB7,0xFF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xDF,0xF5,0xFF,0xFF,0xD7,0xFF, +0xFF,0xFF,0xDF,0xD7,0xF5,0xFF,0x7F,0xFE, 0x4F,0xFF,0xFD,0xFF,0x7F,0x7F,0xFF,0xAD, +0xEB,0xFB,0xFF,0xAD,0xFF,0xFF,0xFF,0xFF, 0xAF,0xEB,0xFB,0xFF,0xFC,0x0D,0xFF,0xFF, +0xDF,0xD2,0xFD,0xFF,0xFF,0xFD,0xF6,0xFF, 0xFF,0x7F,0xFF,0xFF,0x1F,0xFF,0xFF,0xFF, +0xFF,0xFB,0x3F,0x7D,0xEB,0x32,0xFE,0xBF, 0x2F,0xEB,0xFA,0xAE,0xBD,0xE0,0xFA,0x7E, +0xBF,0xAD,0xEB,0xFA,0xFE,0xBF,0xF5,0x7F, 0xFF,0xDE,0xFE,0xE3,0xFB,0xFF,0xFF,0xFF, +0xDF,0xEF,0x4F,0xDF,0xFF,0x7F,0xDF,0xFF, 0xF7,0xFF,0xFF,0xF8,0x7F,0xFF,0xFF,0xEF, +0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xFF,0xDF, 0xED,0xFB,0xDF,0xFF,0xBF,0xFF,0xFF,0xFF, +0x81,0xFF,0xFF,0xFF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFF,0xFE,0xDD,0xFE,0xEF,0xFD,0xFF, +0xFF,0xFB,0xFE,0xF7,0xFF,0x93,0xFD,0xFB, 0x7E,0xFF,0xFE,0x87,0xE9,0xFF,0x7F,0xB3, +0x9F,0xFE,0xFE,0xFF,0xAF,0xFD,0xFE,0x7E, 0x3F,0xFE,0x67,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFC,0xF7,0xDF,0xFD,0xFF,0x7F,0xFF,0xFF, 0x7F,0x6D,0xFF,0xFF,0xFE,0xFF,0xFF,0x2F, +0xFF,0xBF,0xFF,0xFF,0xEE,0xFF,0xBE,0xFF, 0xFF,0xFE,0xFF,0xEF,0xFF,0xFF,0xFE,0xFF, +0xEF,0xFF,0xFF,0xFA,0x5F,0xFF,0xFF,0xFB, 0xFF,0xFF,0xEF,0xFF,0xFB,0xFE,0xFD,0xFF, +0xFE,0xFF,0xFB,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFE,0xBF,0xDF,0xFF,0xFB,0xFF,0xFF,0xF7, +0xFC,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF2,0x7F,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xF3,0xFF,0xFF,0xFF,0xEF,0xFB,0xFF,0xFF, +0xFF,0xDF,0xE2,0xFF,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xE7,0xFF,0xFD, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xED, 0xEF,0xFD,0xFF,0xFF,0xDF,0xD7,0xF5,0xFD, +0x7F,0x5D,0xFD,0xFF,0x7F,0xDF,0x97,0xF4, 0xFD,0x7B,0x5F,0xFF,0xC9,0xFF,0xFB,0xFE, +0xFF,0xBF,0xFF,0x5F,0xFF,0xFF,0xF7,0xFF, 0xEF,0xFD,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0xFF,0xD7,0xFD,0x7D,0x7F,0xFF, 0xFF,0xFF,0xFF,0xEF,0xDF,0xF7,0xFD,0xFF, +0xBB,0xFF,0xFF,0x7F,0xFF,0xFE,0xE3,0xFF, 0xF9,0xFE,0x7F,0xBF,0xEF,0xFB,0xFE,0xFF, +0xBF,0xF9,0xFE,0xFF,0x9F,0xEF,0xF9,0xFE, 0xFF,0xBF,0xF3,0xDA,0xFF,0x37,0xCD,0xF3, +0x7C,0xDF,0x37,0xCD,0xF3,0x7F,0x37,0xCD, 0xF3,0x7C,0xDF,0x37,0xCC,0xF3,0x7F,0x5A, +0xBD,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFD, 0xBF,0x6F,0xDE,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFE,0xF1,0x6F,0xEB,0x7A, 0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7,0xAF, +0x7A,0xDE,0xB7,0xAD,0xEB,0x7A,0xDE,0xB7, 0xFF,0x7E,0xFF,0xFE,0xCD,0xB3,0x6C,0xDB, +0x36,0xCD,0xB3,0x6C,0xDE,0xCD,0xB3,0x6C, 0xDB,0x36,0xCD,0xB3,0x6C,0xDF,0xC9,0xBF, +0xF7,0xBD,0xEF,0x7A,0x9E,0xA7,0xA9,0xEA, 0x7A,0xB7,0xBD,0xEA,0x7B,0xDE,0xA7,0xBD, +0xCA,0x72,0x8D,0x91,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFE,0xF7,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFE, 0x87,0xFF,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6, +0xFD,0xBF,0x6F,0xF6,0xFD,0xBF,0x6F,0xDB, 0xF6,0xFD,0xBF,0x6F,0xFE,0x4F,0xFF,0xBF, +0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB,0xEF, 0xBE,0xEF,0xBB,0xEE,0xFB,0xBE,0xEF,0xBB, +0xEF,0xFC,0x5F,0xFF,0xFF,0xFF,0x3F,0xCF, 0xF3,0xFC,0xFF,0x3F,0xCF,0xFC,0xFF,0x3F, +0xCF,0xF3,0xFC,0xFF,0x3F,0xCF,0xFD,0x9F, 0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFE,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFF,0xE1,0x6F,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFD,0xFF, 0x7F,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x7A,0xBF,0xFB,0xFE,0xDF,0xB7,0xED,0xFB, 0x7E,0xDF,0xB7,0xFB,0x7E,0xDF,0xB7,0xED, +0xFB,0x7E,0xDF,0xB7,0xFF,0xC9,0xFF,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEE, 0xFB,0xFE,0xBB,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0x3F,0xCF,0xFF,0xE7, +0xFE,0xFF,0xF5,0xFD,0x77,0x5D,0xD7,0x35, 0xDD,0x77,0xD7,0xF5,0xCD,0x7B,0x5D,0xD7, +0xF5,0xDD,0x77,0xFE,0x27,0xFF,0xFF,0x8B, 0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9,0xAF, +0x8B,0xE2,0xF8,0xBE,0x2F,0x8B,0xE2,0xF9, 0xFE,0x1F,0xFF,0x5F,0xD7,0xF5,0xFD,0x7F, +0x5F,0xD7,0xF5,0xFF,0x5F,0xD7,0xF5,0xFD, 0x7F,0x5F,0xD7,0xF5,0xFF,0xFA,0x3F,0xFE, +0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xEB, 0xEC,0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF, +0xEB,0xFF,0xFE,0x7F,0xFD,0x7F,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE6, 0xFF,0xFA,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xF7,0xFC,0xFF,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFD,0xFF,0xF5,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x02,0xFF,0xFE,0xBF,0xAB,0xEB,0xFA, 0xBE,0xBF,0x23,0xEB,0xDE,0x1F,0xAF,0xEA, +0xFA,0xFE,0xAF,0xAF,0xEB,0xFD,0x97,0xFF, 0xF3,0xFC,0x7B,0x1F,0xCF,0xF1,0xFC,0x7F, +0x1F,0xF1,0xFC,0x77,0x1F,0xCD,0xF1,0xFC, 0xFF,0x1F,0xFE,0x87,0xFF,0xAF,0xEF,0xFA, +0xFE,0xFF,0xAF,0xEF,0xFA,0xFD,0xBF,0x2B, 0xFB,0x7E,0xBF,0xBF,0xEB,0xFB,0xFB,0xFB, +0xDF,0xFF,0xFB,0xF7,0xFF,0xFF,0x7F,0xF7, 0xF7,0xFF,0xFD,0xDF,0xFE,0xFC,0xDF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xDA,0xBF,0xFF,0xBB, 0xEF,0xFB,0xF9,0xFF,0xBE,0xEF,0xFB,0xFB, +0xBF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xF7,0x7F,0xFD,0xD7,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFE,0xF7,0xFF,0xFE,0xFF, 0xF7,0xFF,0xFF,0x7F,0xFF,0xFF,0xEC,0xFF, +0xFF,0xFE,0xDF,0xBF,0xFF,0xFB,0xFE,0xFF, 0xBB,0x68,0xAE,0x1F,0xAE,0xFB,0xFB,0xFF, +0xFF,0xBF,0xFF,0xD5,0xFF,0x7F,0xFF,0xFF, 0xF7,0xFE,0xFE,0xFF,0xBF,0xEF,0x9F,0xFD, +0x7F,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF,0xFF, 0xBB,0xF7,0xBF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xBF,0xFB,0xFF,0xFF,0xFF,0xDE,0x3F, 0xFF,0xFF,0xFF,0xFF,0xFF,0xA7,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFF,0x7F,0xFB,0xFD,0xFB, 0x7F,0xFF,0xFF,0xFF,0xFF,0xCF,0xF3,0x7C, +0xFF,0x7F,0x8D,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFB,0xFF,0xF7,0xFB,0xFE,0xFD,0xFF,0xFF, +0xFF,0xFF,0xF7,0xFD,0xFF,0x7F,0xFD,0x1F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xBF,0xDF,0xFF, +0xFF,0xFE,0x5C,0xFF,0x6D,0xFF,0x7F,0xAB, 0xE7,0xF1,0xFF,0xFD,0x9F,0xFF,0xFF,0xAD, +0xEB,0x7A,0x3F,0x1F,0xFF,0xFF,0xFE,0xBF, 0xAF,0xF3,0xDE,0xF5,0xFF,0x8F,0xFB,0xDF, +0xE6,0x7F,0xFF,0xDF,0xF3,0xFD,0xFF,0x7E, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xF7,0xF3, +0x7F,0xDF,0xF7,0xEF,0xFF,0xF6,0x3F,0x9F, 0xDF,0xFF,0xFF,0xEE,0xFF,0xFF,0xEF,0xFB, +0xFF,0xFF,0xF9,0xFB,0xFE,0x4F,0xBF,0xEF, 0xBB,0xFF,0x69,0xAF,0xAF,0xFC,0xFF,0x3F, +0xDD,0xFF,0xFC,0xBF,0x8F,0xFF,0xFD,0xF3, 0xBF,0xED,0x9E,0xFC,0xBF,0x6F,0xF5,0xD3, +0xDF,0xFF,0xDB,0xD6,0xF5,0xEF,0xFD,0xFE, 0xFF,0xB9,0xFF,0x1F,0xD2,0xA9,0xAF,0xFF, +0xDB,0xF7,0xBF,0xEF,0x46,0xFF,0xFF,0xAD, 0xEB,0x7A,0xDF,0xEF,0xF7,0xFF,0x7F,0xF7, +0x9F,0xED,0xFF,0x7F,0xFF,0xAD,0xEB,0x7F, 0xF5,0x6F,0xFF,0xFD,0xFB,0xD6,0xF4,0xF7, +0xFB,0xF9,0x7E,0x7F,0xFF,0x5F,0xC2,0xFE, 0xBF,0xFD,0xFB,0x33,0xDF,0xF9,0x5B,0xFF, +0xFF,0xDD,0x67,0x7D,0xCF,0xEF,0xDB,0xEC, 0xFF,0x77,0xDD,0xF7,0xFD,0xFF,0xFF,0xDE, +0xA7,0xBF,0xD4,0x9F,0xFF,0xFF,0xBF,0xEF, 0xFE,0xFF,0xDF,0xEF,0xBB,0xFF,0xFF,0xEF, +0xEB,0xFA,0xFF,0xEF,0xBD,0xFB,0xFF,0xE2, 0x7F,0xFF,0xDF,0xDF,0xF7,0xFD,0xBF,0xBB, +0x73,0xF7,0xFD,0x7F,0xDF,0xDE,0xF7,0xBF, 0xEA,0xDB,0xF6,0xFF,0xD6,0xFF,0xFF,0x66, +0xFF,0xBE,0xFF,0xBF,0x6B,0xD9,0xF6,0xDF, 0xFF,0xFB,0x7E,0x7F,0xB7,0x7E,0xFF,0xFE, +0xFF,0xCD,0xFF,0xFE,0x7F,0xFF,0xFC,0xFD, 0x3F,0xFB,0xFB,0xF7,0xFF,0xFF,0xFB,0xF6, +0x7D,0xFE,0x7F,0xFF,0xFC,0xFF,0xB9,0xFF, 0xF9,0xFA,0xFE,0xBF,0xAF,0x5B,0xD6,0xED, +0xAD,0x7B,0xF6,0xF9,0xBF,0xEF,0xF8,0xFA, 0xFE,0xBF,0xFE,0xE6,0xFF,0xFF,0xF7,0xFD, +0xFF,0x7F,0xBF,0xEF,0xF3,0xFF,0xFF,0x6F, 0xF7,0xFE,0xFF,0xFF,0xF7,0xFD,0xFE,0xF7, +0xEF,0xFF,0xFB,0xEF,0xFB,0x7E,0xDE,0xFE, 0xFF,0xBF,0xFF,0xFE,0xFF,0xFF,0xFB,0xFF, +0xFF,0xEF,0xFB,0x6F,0xFC,0x1F,0xFE,0xE7, 0xFF,0xFF,0xFF,0xEF,0xFF,0xD3,0xB4,0xBB, +0xFF,0xFF,0xFD,0xBF,0x6F,0xE3,0xFE,0xFF, 0xBF,0xFC,0xBF,0xF7,0xCF,0xF7,0xFD,0xFF, +0x2F,0xDF,0xAB,0xEA,0xFF,0xDF,0xE7,0xEA, 0x9A,0xAF,0xEF,0xFB,0xFE,0xFF,0xF5,0x3F, +0xFD,0x7E,0xFF,0xD7,0xF5,0xFB,0xFF,0xFD, 0xF7,0xFF,0x7F,0xFE,0xF7,0xFD,0xFF,0xD7, +0xFF,0xD7,0x7F,0xEE,0x7F,0xFA,0x79,0xFE, 0x2F,0x8B,0xE6,0xF9,0xFE,0x3F,0x9E,0xF9, +0xBE,0x2F,0x0B,0xE7,0xF9,0xFE,0x2F,0x9F, 0xFD,0xFF,0xFE,0x7D,0x7F,0x5F,0xD7,0xFF, +0xFF,0x7F,0xFF,0xFD,0xFF,0x7F,0x5F,0x97, 0xFF,0xFD,0x7F,0x5F,0xFF,0xE3,0xFF,0xFF, +0xFA,0xFE,0xBF,0xAF,0xFB,0xFB,0xFF,0xFF, 0xCF,0xEB,0xFE,0xBF,0xAF,0xFF,0xFA,0xFE, +0xBF,0xFF,0x87,0xFF,0xFF,0xF5,0xFF,0xFF, 0xFF,0xFF,0xFD,0xFF,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFF,0xF5,0xFF,0xFF,0xFE,0x0F, 0xFF,0xFD,0xEB,0xFF,0xFF,0xF7,0xFF,0xEF, +0x7B,0xDF,0xFE,0xFF,0xFF,0xDF,0xF7,0xFD, 0xEB,0x7F,0xDF,0xFF,0x5F,0xFF,0xFF,0xFF, +0xFF,0xFD,0xBF,0xFF,0x7E,0xFA,0xBF,0xC7, 0xDB,0xF7,0xBD,0x3F,0xFB,0xFF,0xF6,0xFF, +0xFA,0xAF,0xFF,0xEB,0xFA,0xFE,0x3F,0x2F, 0xEA,0xFA,0x3E,0xAD,0xC9,0xBA,0xF6,0xAD, +0xAF,0xEB,0xFA,0xF6,0xBF,0xFE,0x7F,0xFF, 0xFF,0xFD,0xFF,0xF1,0x7F,0x3F,0xCF,0xF1, +0xEF,0xFF,0x7F,0xFF,0xBC,0xDF,0xDF,0xF7, 0xDD,0xFF,0xE0,0x7F,0xFF,0xFF,0xFE,0xFF, +0xFA,0xEC,0xBB,0x7F,0x5F,0xFF,0xFB,0xEC, 0xFF,0xEF,0xB7,0xFF,0xF7,0xFF,0xFF,0xB5, +0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xEE,0xDF, 0x5F,0xDF,0xDE,0xFF,0xAE,0xE7,0x77,0xFF, +0xFF,0xDF,0xF7,0xFF,0xE3,0xFF,0xFA,0xBB, 0xFE,0xFF,0xAF,0xFD,0xFB,0xFE,0xBF,0xAB, +0xF9,0xFE,0xFF,0xBF,0x7F,0xBF,0xFE,0xBD, 0xFE,0xD7,0xFF,0x9F,0xFD,0xFF,0xBE,0xEF, +0xFF,0xEE,0xFD,0xBB,0x5B,0xEF,0xFF,0x7F, 0xEF,0xFF,0xEF,0xFF,0x7F,0xFF,0x4F,0xFF, +0xEF,0xFB,0xBC,0xFC,0xFF,0xFF,0xFF,0xFE, 0xFE,0xFD,0xFA,0xFE,0xFB,0xFF,0xFD,0xF3, +0xFB,0xFF,0xF8,0x5F,0xFF,0xFF,0xD7,0xF5, 0xFD,0xDF,0xEF,0xFF,0xF3,0xDC,0x5F,0xCE, +0xF5,0xBD,0xFF,0xFF,0xD7,0xFF,0xFF,0xF9, 0x3F,0xFF,0xDF,0xF7,0xFF,0xFE,0xFF,0xFD, +0xFF,0xFB,0xFF,0xF7,0xB9,0x7D,0xFE,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF9,0x7F,0xFF,0xFE, +0xFF,0xFF,0x7F,0xFF,0xFE,0xFF,0xFF,0xF7, 0xF6,0xFF,0xBF,0xF1,0xF8,0xFF,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xFF,0xFF,0xF9,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEF,0xEF,0xFF,0xFF, +0x9B,0xFB,0x7F,0xFF,0xFF,0xFF,0xC1,0xFF, 0xDF,0xFF,0x3F,0x5F,0xD7,0xBF,0xEF,0xBB, +0xDE,0xEE,0xFF,0x7F,0xDF,0xFF,0xFE,0xF5, 0x7F,0xDF,0xFF,0x99,0xFF,0xFF,0xFA,0xFF, +0xBF,0xFD,0xEB,0x7A,0xFF,0xB7,0xFE,0xFE, 0xFF,0xFF,0xEF,0xFF,0xFF,0xFD,0xBF,0xFF, +0x97,0xFF,0xFD,0xF7,0xFF,0x7F,0xF7,0xFF, 0xFF,0xFD,0x5F,0xFE,0xF3,0xF9,0xDF,0xDF, +0xFF,0xFF,0xFC,0xFF,0xFF,0x83,0xFF,0xFF, 0xFE,0xFF,0x9E,0xEC,0xFB,0xEE,0xFF,0x9F, +0xBF,0xEF,0xFF,0xFE,0xED,0x7B,0xFF,0xFF, 0xFF,0xF1,0x5A,0xFF,0xFF,0xFD,0xFF,0x7C, +0x69,0x3B,0xDF,0xFF,0x7F,0x1F,0xDF,0xFF, 0xFD,0xBA,0xFF,0xFF,0xFB,0xFF,0x5B,0xBD, +0xFF,0xFF,0xFF,0xFF,0xD7,0xB6,0xED,0xE9, 0xFF,0xD6,0xBD,0x6F,0x5F,0xFB,0xFF,0xEF, +0xFF,0x5F,0xFE,0xF6,0x6F,0xFF,0xFF,0xFF, 0xFF,0xF7,0xEB,0x7A,0xDF,0xFF,0x9F,0x7F, +0x7F,0xFF,0xB7,0xFF,0xFF,0xFE,0xDF,0xFF, 0x6C,0xFF,0xFB,0xFF,0xBB,0x6F,0xEB,0xFE, +0xCC,0xF7,0xA5,0xFA,0x5C,0xF5,0x75,0xBB, 0xB7,0xDF,0xFE,0x6F,0x5F,0xC5,0xBF,0xFD, +0x7B,0xFE,0xFF,0x95,0xE7,0x29,0xCF,0x4F, 0xF5,0x91,0xEE,0x6B,0xDF,0xEF,0xFD,0x54, +0xF5,0xBD,0xB1,0xFF,0xEF,0xEE,0xFB,0xBE, 0xBF,0xAF,0xFE,0xDE,0xBD,0x6F,0xDA,0xF2, +0xFF,0xAF,0xBE,0xFF,0xFF,0xFD,0x7E,0xA7, 0xFF,0xF7,0xFF,0xBF,0xEF,0x7B,0xF6,0xFD, +0xBD,0x4A,0xF2,0x85,0x85,0xBF,0x5B,0xFE, 0xB5,0xFD,0xFA,0xFF,0x4F,0xFF,0xFE,0xDF, +0xFF,0xED,0xFF,0xBF,0xFF,0xBF,0x7F,0xFE, 0xFF,0xB7,0x6D,0xFF,0xF7,0xBF,0xBF,0xEF, +0xFD,0x1F,0xFF,0xFE,0x7D,0xFF,0x67,0xFF, 0xFF,0xFF,0x3F,0x7F,0xFE,0xBF,0xFF,0xE7, +0xDF,0xE7,0xFF,0xEF,0x6B,0xFC,0x1F,0xFF, 0xBF,0xEF,0xFB,0xFE,0xDE,0xBF,0xAF,0xFA, +0xFF,0xB6,0xEF,0xF9,0xFE,0xFF,0x8F,0xEF, 0xDB,0xEF,0xAB,0x6F,0xFB,0xFE,0xFF,0xFF, +0xEF,0xFD,0xFF,0x7F,0xFF,0xFF,0xDE,0xFF, 0xFF,0xEF,0xFF,0xFF,0xFF,0x3F,0xFF,0x6C, +0xFF,0xBF,0xFB,0xFF,0xFE,0xFF,0xFB,0xFE, 0xDF,0xFF,0xFF,0xEF,0xFF,0xFF,0xBF,0xFF, +0xFF,0xFE,0xFB,0xFF,0xD5,0x7F,0xFF,0xFF, 0xEF,0xFB,0xFF,0xFF,0xBF,0xEF,0x43,0xB5, +0xFD,0x6F,0xCF,0xD6,0xBE,0x3F,0x7F,0xDB, 0xFE,0xC3,0xFF,0xFD,0xFF,0xAF,0xEB,0xFB, +0xFC,0xFF,0x3E,0xEF,0xE8,0xFA,0xBD,0xCD, 0xAA,0xFE,0xFE,0x7D,0xCF,0xFF,0xB7,0xFF, +0xF7,0xFF,0xFF,0xFF,0xFD,0xFF,0x75,0xCD, 0x52,0xD7,0xFD,0xFB,0xF7,0xDD,0xFB,0xEF, +0xEB,0xFF,0xFF,0x4F,0xFF,0xBF,0x9F,0xE7, 0xF9,0xFC,0x7F,0x8B,0xC3,0xF9,0xAF,0x8F, +0xE7,0xE9,0xBE,0x7F,0x9F,0xE6,0xF9,0xFC, 0x5F,0xFF,0xFF,0xF7,0xFD,0xFF,0x7A,0x5F, +0xD7,0xED,0xFF,0xFF,0xD7,0xFF,0xDD,0x7F, 0xE7,0xFF,0xFC,0xFF,0xFC,0x3F,0xFF,0xFF, +0xFF,0xFB,0xFF,0xFE,0xBF,0xAF,0xFF,0xFD, 0xFF,0xEF,0xFF,0xEB,0xFF,0xFF,0xFF,0xFF, +0xFF,0xF7,0x7F,0xFF,0x7F,0xDF,0xFF,0xFD, 0xFD,0x7F,0xFE,0xF7,0xFD,0x7F,0xDF,0xFF, +0xFD,0xFF,0xFF,0xDF,0xFB,0xFF,0xEE,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7A,0xDF,0xF5, +0xFD,0xFA,0xDF,0xF7,0xFC,0xFF,0x7F,0xDF, 0xBF,0xED,0xFF,0xC9,0xFF,0xDF,0xFF,0xBF, +0x2F,0xFB,0xFF,0xBC,0xAD,0xFF,0xF7,0xFF, 0xFF,0xEF,0xD3,0xFF,0x7D,0xBF,0x6F,0xFF, +0xFA,0xFF,0xFE,0xBF,0xAE,0xEA,0xFA,0xBE, 0xAD,0xA5,0xEB,0xCE,0xBF,0xA7,0xEB,0x5A, +0xDE,0xBD,0xAF,0x6B,0xFD,0x57,0xFF,0xFF, 0xF4,0x7F,0x1F,0x7F,0xFD,0xFF,0x7F,0x36, +0xF0,0xDF,0x79,0xFF,0xFF,0xFF,0xF7,0xFD, 0xBF,0xFF,0x87,0xFF,0xFB,0xF3,0xFC,0xFF, +0xFF,0xFF,0xFF,0x7E,0xFF,0xBF,0xDF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xF8,0x9F, +0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFD, 0xF7,0xFC,0xBD,0xFF,0xFE,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFB,0xF9,0xBF,0xFF,0xFF,0xEB, 0xE2,0xFE,0xFF,0xBF,0xEF,0xA9,0xBA,0x2F, +0xEB,0xF9,0xFE,0x77,0xDF,0xF7,0xFF,0xFF, 0xF9,0x7F,0xFF,0xFF,0x7F,0xEF,0xD7,0xFF, +0xFD,0xFF,0xFB,0xF5,0xFF,0xBF,0x6F,0xDF, 0xFF,0xFF,0xFD,0xFF,0xFF,0xF0,0xFF,0xFF, +0xFF,0x3F,0xCF,0xFF,0xBA,0xEE,0x9B,0xBF, 0xEE,0xD7,0xFE,0xCD,0xEF,0xFF,0xDF,0xBF, +0xFF,0xFF,0xC5,0xFF,0xFF,0xFD,0x7F,0x4F, 0xFD,0xF6,0xD9,0xFF,0x4F,0xD6,0xFD,0xBF, +0x6E,0xFF,0xFF,0xF4,0x7F,0xFF,0x7F,0x8B, 0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xF9,0xFE, +0x37,0xFF,0xD9,0xFB,0xF5,0xAF,0xFD,0xFF, 0xFF,0xFB,0xFF,0xFF,0x07,0xFF,0xFF,0xFF, +0xFB,0xF7,0xFF,0xFD,0xFF,0x7C,0xFA,0x7E, 0x4F,0xFC,0xDF,0x1D,0xC7,0xFF,0xFF,0xFF, +0xFF,0xAE,0xFF,0xFF,0xFF,0xFF,0xFD,0xFB, 0xFF,0xFF,0xFE,0xFE,0xFC,0xFF,0x7F,0x7F, +0xBF,0xEF,0xFE,0xFF,0xFF,0xFF,0x5F,0xFD, 0xFF,0xFF,0xFF,0xFD,0x6F,0x5A,0xD7,0x7B, +0xBE,0x5F,0xFE,0x39,0xFF,0xF7,0xFF,0xF7, 0xFD,0xFE,0xAA,0x1F,0xFF,0xFF,0xFF,0xFF, +0xFE,0xFE,0xAB,0xAF,0xFD,0xFE,0xBF,0xFF, 0xF7,0xFF,0x7F,0xFE,0x8F,0xE3,0xFB,0xEE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xEB,0xFB,0xFF, 0xFD,0xBF,0xEF,0xDF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFB,0xE4,0x3F,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xF3,0xEF,0xBB,0xFB, +0xBF,0xEF,0xBB,0xFF,0xD7,0xBF,0xFF,0xFF, 0xFF,0x29,0xAF,0xF7,0xFF,0xFF,0xFB,0xFF, +0xFB,0xE6,0xFF,0x0F,0xFB,0x3F,0xDF,0x0F, 0xFF,0xAF,0xFF,0xFF,0xFF,0xF5,0xC3,0xDF, +0x5F,0xFF,0xFF,0xFF,0xFE,0x6B,0xCA,0xBE, 0xBC,0xFF,0x9F,0xF2,0xBF,0xFF,0xFE,0xFA, +0xFF,0xFF,0xEF,0x16,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFC,0xDF,0x97,0xFD,0x79,0xFF,0x37, +0xE7,0x7F,0xFF,0xFF,0xB5,0xFF,0xFF,0xF6, 0x2F,0xFF,0xFD,0xFB,0xFE,0xFF,0xFF,0xFD, +0x5F,0x57,0x5F,0xFF,0xDB,0x52,0xDF,0xFF, 0xFD,0xBF,0xFF,0xFF,0xFC,0xDB,0xFF,0x7B, +0xB5,0xFD,0x7F,0xFF,0x71,0x9C,0x6E,0xFF, 0xF6,0x35,0xA5,0x9B,0xFF,0xFF,0xFD,0xFF, +0xFF,0xDB,0x9E,0x7F,0xFE,0xEF,0xFB,0xFF, 0xFF,0xBD,0xEF,0xFF,0xDE,0xB7,0xF9,0x4B, +0xFF,0xF5,0xEF,0xFF,0xFF,0xFF,0xE8,0x7E, 0xFF,0xEA,0xDF,0xF7,0xFF,0xFD,0x69,0x5B, +0xFC,0x9F,0xEF,0x78,0xD6,0xFF,0xEB,0xEF, 0xFF,0xFF,0xFF,0xE8,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xE3,0xF9,0xF6,0xBF,0xFF, 0xFF,0xFE,0xDF,0xFF,0x7F,0xFF,0xFF,0xFF, +0xD1,0xFF,0xFF,0xE7,0xFF,0xFF,0xFF,0xFF, 0xE7,0xF9,0xFF,0xBF,0x7F,0xD9,0xFF,0xFD, +0xFE,0x7F,0xFF,0xFE,0xFF,0xF9,0xFF,0xFB, 0xD6,0xDF,0xBF,0xEF,0x5B,0xD6,0xFF,0xBF, +0xFB,0xF6,0xFF,0xBF,0xEF,0xF8,0xF6,0xDD, 0xBE,0xFE,0x16,0xFF,0xBF,0xEF,0xFF,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0x6F,0xFB, 0xFF,0xFF,0xFF,0x6F,0xF3,0xFF,0xF7,0xEF, +0xFB,0xFF,0xBF,0xFF,0xEF,0xFE,0xFF,0xBF, 0xFF,0xFF,0xFF,0xBE,0xBF,0xFF,0xEF,0xFF, +0x7F,0xEF,0xFF,0xFD,0x17,0xFB,0x7B,0xFF, 0xFF,0xFD,0x7F,0xDB,0xF6,0xF4,0x7F,0xFA, +0xFE,0xF5,0xBF,0xEB,0xE3,0xF7,0xFF,0xFF, 0xE9,0xBF,0xFF,0xAF,0xF7,0xFD,0xF3,0x7E, +0x8F,0xA3,0xEA,0xFF,0xCB,0xF3,0xEE,0xFF, 0xBF,0xEF,0xF7,0xF9,0xFF,0xFE,0x7F,0xFF, +0xFF,0xFF,0xFF,0xF5,0xFB,0xF6,0xFF,0xF5, 0x2F,0xFE,0xFB,0xD7,0xBF,0xFF,0xBE,0xDF, +0x9F,0xFF,0xF0,0xFF,0xFF,0xF9,0xFE,0x7F, 0x8F,0xA3,0xF8,0xFE,0x6F,0x9F,0xF9,0xF6, +0x2F,0x9F,0xE7,0xF9,0xFE,0x2F,0x9F,0xE1, 0xFF,0xFF,0xFF,0x7F,0xDF,0xF7,0xF5,0xFD, +0x7F,0x7F,0xF5,0xFF,0x9F,0x5F,0xFB,0xFE, 0xFF,0x7F,0xFF,0xFF,0xCB,0xFF,0xFF,0xFB, +0xFE,0xFF,0xBF,0xAF,0xFB,0xFE,0xFF,0xDF, 0xFE,0xFE,0xBF,0xF7,0xFF,0xFF,0xFF,0xFF, +0xFF,0xC7,0xFF,0xFF,0xFD,0xFF,0x7F,0xDD, 0xF7,0xFD,0xFF,0xFF,0xD7,0xFF,0xFD,0x7F, +0xFF,0xFB,0xFD,0xFF,0xFF,0xFE,0xEF,0x7F, 0xFD,0xEF,0xFB,0xFE,0xFB,0xFD,0xFF,0x7F, +0xDF,0xFD,0xFF,0x7A,0xDF,0xF7,0xFD,0xFF, 0xFF,0xFF,0xFF,0x1F,0xFF,0xFF,0xD3,0xF7, +0xFF,0xFF,0x6F,0xDB,0xFF,0xFF,0xEF,0xCB, 0xF4,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFE, +0x29,0xFF,0xE8,0xDA,0x76,0x9F,0xAF,0x6A, 0xDA,0xFE,0x35,0xEB,0xDA,0xD6,0xBF,0xAB, +0xEB,0x7A,0xDE,0xBF,0xD7,0x7F,0xFF,0xFE, 0xFF,0xBF,0xEF,0xFD,0xDF,0x77,0xBF,0xFD, +0x37,0xEF,0xFF,0xEF,0xFF,0x3F,0xFF,0xFF, 0xFF,0xFE,0x7F,0xFF,0xFF,0xFF,0xF7,0x7E, +0xDF,0xFF,0xFF,0xFF,0xFA,0xB7,0x7F,0xFF, 0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0x89,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0x9F,0xFB,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xAA,0xFF,0xAB,0xFB,0xFA, 0xEF,0xBF,0xFF,0xDF,0xFA,0x7B,0xB9,0xFE, +0xFE,0xFF,0xFD,0xFF,0xF7,0xFE,0x3F,0xFF, 0xB7,0xFF,0xF7,0xEE,0xFF,0x7F,0xEF,0xFF, +0xFF,0x7F,0xFF,0x1F,0xFB,0xFF,0xBF,0xFB, 0xFE,0xFF,0xBD,0xFF,0xFF,0x2F,0xFF,0xBF, +0xFF,0x7F,0xDF,0xFA,0xFF,0xFF,0xFC,0xEE, 0xF5,0xF3,0xBE,0xFB,0x0F,0xEF,0xF3,0xBE, +0xEF,0xFC,0x5F,0xFF,0x5A,0xFF,0xF7,0xDF, 0xFF,0xFF,0xFE,0xD5,0xFC,0x5F,0xFB,0xF2, +0xFF,0xFF,0x2F,0xBB,0xF3,0xFF,0xFF,0xBF, 0xFF,0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xBF,0xFF,0xFF,0xFD,0x7B,0xFF,0xDF,0xB9, 0xFF,0xFB,0xFF,0xD8,0x7F,0xFF,0xFF,0xFF, +0xFB,0xFF,0xFC,0x7F,0x1F,0xBF,0xE0,0xDF, 0xF7,0xEF,0xFF,0xFD,0x7F,0xFE,0xDF,0xFF, +0xE0,0xFF,0xFF,0xFD,0xEF,0xFB,0xFF,0xFE, 0xF7,0xDF,0xFF,0xEB,0x5F,0xFF,0xF7,0xFF, +0xFF,0xFF,0xFF,0xBF,0xFF,0xFD,0xFF,0xFD, 0xFF,0xFF,0xFF,0xF7,0xFD,0xFF,0x3B,0xDC, +0xFD,0x6D,0x7B,0x5F,0x57,0xF5,0xFD,0x7F, 0x5F,0xFF,0xB1,0xFF,0xEB,0xFF,0xFF,0xFF, +0xFB,0xFB,0xFE,0xFF,0xBF,0xFB,0xBE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFF,0xAF,0xFE,0xF7, +0xDF,0xDF,0xFF,0xFF,0xFF,0x7F,0xCF,0xF3, 0xF8,0xFF,0xD7,0xFB,0xFF,0x5F,0xBF,0xF7, +0xFB,0xFF,0x7F,0xFE,0x23,0xFF,0xFF,0xFE, 0x7F,0xF3,0xFF,0xFB,0xFE,0xFF,0xFF,0xF3, +0xFF,0xFF,0xF5,0xF9,0xFF,0x3F,0xFF,0xFF, 0xF0,0x9A,0xFF,0xBE,0x7F,0xFF,0xFC,0xF9, +0xFF,0xFD,0xAF,0xEB,0xFE,0xBF,0xFF,0xCF, 0xF3,0xFE,0x7F,0xFF,0xFF,0x5B,0xBD,0xFF, +0xBC,0xEB,0xFF,0xD7,0xD4,0xAF,0xAF,0xFD, 0xFF,0xCF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, +0xFD,0xFE,0xFF,0x6F,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFD,0x7F,0x5E,0xFD,0xBF,0xDB,0xF6, +0xFD,0xBF,0x6F,0xFB,0xEE,0xFD,0xFF,0x7A, 0xFF,0xFA,0xFB,0xFF,0x3F,0xFB,0xB7,0x5F, +0xD6,0xF7,0x1F,0x71,0xDC,0x77,0x1D,0xC7, 0x31,0xDC,0x77,0xDF,0xF9,0xBF,0xF5,0x5B, +0xF4,0xD7,0x9D,0xAE,0xFF,0xBF,0xFD,0xBF, 0xDB,0xF6,0xFD,0xBF,0x6F,0xDB,0xF6,0xFE, +0x3D,0x81,0xFF,0xEB,0xFE,0xFE,0xFE,0xFF, 0xEB,0x7A,0xDF,0x7D,0x77,0x7D,0xF5,0x79, +0xDF,0x57,0xDD,0xF5,0x7D,0x7E,0xE6,0xFF, 0xD6,0x3F,0xBF,0x7F,0xFF,0xD4,0xF5,0x3F, +0xBF,0xFB,0xBE,0xEF,0xB3,0xEE,0xFB,0x9E, 0xEF,0xBB,0xFE,0x8B,0xFF,0xFE,0xDF,0xB7, +0xED,0xFF,0xF7,0xFD,0xFE,0xFF,0xEF,0xBB, 0xEE,0xFF,0xBE,0xEF,0xBB,0xEE,0xEB,0xFC, +0x1F,0xFF,0xFF,0xFD,0xFF,0xE7,0xFF,0xF7, 0xFD,0xFF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, +0xFE,0xFF,0xBF,0xEB,0xFA,0x1F,0xFF,0xB7, 0xEF,0x5B,0xFE,0xFF,0xAF,0xEB,0xDD,0xE7, +0xDE,0x77,0x9D,0xE7,0x79,0xDE,0x77,0x9D, 0xBF,0xE6,0x6F,0xFF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFD,0xBF,0x6F,0xF6,0xFD,0xBF, 0x6F,0xDB,0xF6,0xFD,0xBF,0xFF,0x7E,0xFF, +0xFF,0xFB,0xFE,0xFE,0xFF,0xEF,0xFB,0xFD, 0xEF,0x7E,0xF7,0xBD,0xEF,0x7B,0xDE,0xF7, +0xBD,0xEF,0xFF,0xD5,0xFF,0xBF,0xFF,0xEF, 0xFE,0xFF,0xFC,0x3F,0x0F,0xE7,0xFE,0x7F, +0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFE, 0xF3,0xFF,0xFE,0xDF,0xAD,0xDF,0x67,0xEE, +0xFB,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFF,0x23,0xFF,0xFF, +0xFF,0xFF,0x7F,0xFF,0xF3,0xBC,0xDB,0xFE, 0xFB,0xFF,0xFB,0xBE,0xF7,0xFB,0xFF,0x7F, +0xDF,0xFF,0xCF,0xFB,0xFF,0x9F,0xE3,0xF9, 0xBE,0x3F,0x8F,0xE7,0x79,0xFF,0x9D,0xE7, +0xF9,0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x5F, 0xFF,0xCF,0xF7,0xFF,0xFF,0xFF,0xDF,0xF7, +0xFE,0x7F,0xE7,0xF9,0xFE,0x7F,0xFF,0xFF, 0xFB,0xFE,0xFF,0xFF,0xBF,0xFF,0xBF,0xBF, +0xFF,0xFE,0xFF,0xBF,0xEF,0xFF,0xFD,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFD,0xFF, +0xFF,0x3F,0xFF,0xBF,0xFF,0xF7,0xFF,0xFF, 0x7F,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xE8,0xEF,0xFF, 0x5F,0xF7,0xBF,0xF9,0xFE,0xDF,0xB7,0xFD, +0xFF,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xDD,0xFF,0xF2,0xFF,0xBF,0xFF, +0xFF,0xBF,0xFF,0xFF,0x2F,0xF2,0xFF,0xBF, 0x2F,0x7B,0xD2,0xF7,0xBF,0x2F,0xFF,0xBB, +0xFF,0xEE,0x8F,0xAF,0xEB,0xFA,0xFE,0x3F, 0xA7,0x69,0xCE,0x8F,0xA4,0xEA,0xFA,0xEE, +0xB7,0xAE,0xEB,0xFD,0xC7,0xFF,0xF7,0xF7, 0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0x3E,0xF3, +0x74,0xFF,0x3F,0x4F,0xFF,0xE7,0xFF,0x3F, 0xFE,0xA7,0xFF,0xFF,0xDF,0xF7,0xB7,0xFF, +0xF7,0xFF,0xBA,0xEF,0x37,0xEB,0xFB,0xFE, 0xBF,0xFB,0xFE,0xF3,0xFF,0xF9,0xDF,0xFF, +0xBF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFD,0xDF,0xFF,0xFD,0xFF,0xFF,0xFB,0xFE, +0xFD,0xFF,0xFB,0xBF,0xFE,0x3F,0xED,0xFF, 0xDF,0xBE,0x3D,0xA7,0xFB,0xFA,0x3F,0xE6, +0xE1,0xFE,0xFE,0x3F,0xEF,0xE3,0xDF,0xF5, 0x7F,0xFE,0xFF,0x7E,0xFF,0xFF,0xFF,0xFF, +0xEF,0x6F,0xF6,0xFF,0x7D,0xEF,0xD7,0xDE, 0xFF,0x7D,0xEF,0xFF,0xF2,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7B,0xDE,0xFB,0xE6,0xEE, 0xEF,0x37,0x6E,0xF3,0x7E,0xEB,0x37,0xEF, +0xFF,0xC1,0xFF,0xFE,0xFF,0xF7,0xEF,0xFF, 0xFF,0xFF,0xBF,0x3F,0xD2,0xDF,0xBF,0x2F, +0x7B,0xE2,0xFF,0xFE,0x3B,0xBD,0xDB,0xFF, 0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFE, +0xFF,0xFB,0xFF,0xFF,0xBF,0xFF,0xFB,0xDF, 0xFF,0xBF,0xFF,0xB7,0xFF,0xFF,0xBF,0xEF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, 0x7F,0xFF,0x1F,0xEF,0xF1,0xFD,0xFF,0xF6, +0xAF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF, 0xFF,0xFF,0xFE,0x9F,0xFF,0xFF,0xFF,0x77, +0xEF,0xF7,0xFB,0xFF,0xFE,0x5F,0xFF,0xFF, 0xBF,0xCF,0xFB,0xF7,0xDD,0xF7,0xF5,0xFF, +0x5F,0xD5,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5, 0xFF,0xFB,0x0F,0xFF,0xFF,0xA9,0xEA,0x7A, +0xFF,0xAF,0x8F,0xFE,0xDF,0xAF,0xEF,0xFB, 0xFE,0xFF,0xBF,0xEF,0xFB,0xDF,0xE5,0x5F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xBD,0x57,0xFF, 0xFF,0x6F,0x77,0xBF,0xF7,0xFB,0xFF,0x7F, +0xBF,0xF7,0xFF,0xFC,0xBF,0xFF,0x9F,0xFF, 0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF,0x1F, +0xCF,0xFF,0xFC,0xFF,0xFF,0xFF,0xFF,0xFB, 0x65,0xAF,0xF3,0x7C,0xFF,0x3F,0xDF,0xFF, +0xFD,0xE9,0xFE,0x7F,0xE7,0xFF,0xFE,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFD,0xE3,0xDF,0xFB, +0xDB,0xF6,0xFD,0xEF,0x5B,0xFB,0xFF,0xDF, 0xFC,0xFF,0x3F,0xDF,0xF3,0xFD,0xFF,0x7F, +0xDF,0xEF,0x66,0xFF,0xDF,0xAD,0xEB,0x7A, 0xDE,0xF7,0xF7,0xE7,0xD9,0xFD,0x9F,0x67, +0xD9,0xF6,0x7D,0x9F,0xE7,0xDF,0xF5,0x47, 0xFD,0x65,0x5B,0xD6,0xF4,0xFE,0xFF,0xEF, +0xFF,0x6D,0xF6,0xDD,0xB7,0x6D,0xDB,0x76, 0xDC,0xB7,0x7D,0xFA,0x9B,0xF6,0x6D,0x9D, +0x67,0x59,0xDF,0xF7,0xDD,0xFF,0xEB,0xFE, 0xBF,0xAF,0xEB,0xFA,0xFE,0xBF,0xAF,0xE3, +0xD1,0x9F,0xFF,0xBD,0xBF,0xEF,0xFE,0xF7, 0xBF,0xBF,0xF7,0xD7,0x7F,0xDD,0xF7,0x9D, +0xDF,0x7F,0xDF,0xF7,0xFF,0xE0,0x7F,0xFD, 0xC1,0xDF,0xF7,0xFD,0xC7,0x7F,0x7F,0xFB, +0xFF,0xBB,0xEC,0xFB,0x3E,0xFF,0xBF,0xEC, 0xFB,0xFF,0xD8,0x7F,0xBF,0x6C,0xFF,0xBE, +0xFF,0xBF,0xED,0xFF,0xEF,0xFE,0xFB,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEE,0xFF,0xC5, +0xFF,0xAF,0x6F,0xFF,0xFC,0xFD,0x3F,0xE7, 0xFF,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xBF,0x89,0xFE,0xFA,0xBA, 0xFE,0xBF,0xAF,0xFB,0xF6,0xF5,0xD9,0x7D, +0x97,0x65,0xD9,0x74,0x5D,0x97,0x65,0xD3, 0xFE,0xD6,0xFF,0xBF,0xF7,0xFD,0xFF,0x7F, +0xBF,0xCF,0xFB,0xFE,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xEF,0xFB,0xFF,0xF6,0x8F,0xFB, +0xFF,0xEF,0xFB,0x7E,0xDB,0xFE,0xFF,0xBE, 0xEF,0xEE,0xFB,0xBE,0xEF,0xBB,0xEE,0xFB, +0xBE,0xFF,0xFF,0xDF,0xFF,0x43,0xFF,0xFF, 0xFB,0xEF,0x5F,0xB7,0xFE,0x7F,0xE7,0xF9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xF9, 0xBF,0xFE,0xAF,0x77,0xFD,0xFF,0x2F,0xAF, +0xA7,0xFE,0xFF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xF1,0x7F,0xEF,0xDF, +0xFF,0x97,0xF5,0xEF,0xFF,0xDF,0xFF,0xFF, 0xBF,0xFF,0xBF,0xFF,0xFF,0xFE,0xFF,0xFF, +0xFF,0xE0,0xFF,0xFF,0xF9,0xFE,0x2F,0x8B, 0xE3,0xF8,0xBE,0x77,0x9F,0xF9,0xDA,0x77, +0x9D,0xE7,0x79,0xDE,0x77,0x9F,0xDD,0xFF, 0xFD,0xFD,0x7F,0x5F,0xD7,0xFD,0xFF,0x7F, +0xE7,0xFE,0x7F,0x97,0xE7,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFF,0xAB,0xFF,0xEF,0xFA,0xFE, +0xBF,0xAF,0xFF,0xFA,0xFF,0xFF,0xDF,0xFF, 0xFB,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF, +0x67,0xFF,0xF7,0xF5,0xFF,0xFF,0xFF,0xDF, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEF,0xFF,0xBD, 0xEB,0xFF,0xFF,0xF7,0xAD,0xEB,0xFF,0xDF, +0xFD,0xFF,0x3F,0xDF,0xF7,0xFD,0xFF,0x7F, 0xDF,0xFF,0x5F,0xFF,0xF7,0xFF,0xFF,0xFD, +0xBF,0xFF,0xCB,0xF4,0xFF,0x7F,0xD3,0xF7, 0xFD,0x3F,0x7F,0xD3,0xF7,0xFF,0xFC,0x3F, +0xFF,0xEA,0xFA,0xBE,0xAF,0xAB,0xEB,0xBA, 0xF4,0x95,0x6B,0x52,0xD4,0xAD,0x2F,0x4A, +0xD2,0xF6,0xBF,0xD2,0x7F,0xF7,0x3F,0xFF, 0xFF,0xF3,0x7F,0xFF,0xFF,0xF7,0xFF,0xBA, +0xDF,0xFB,0xFD,0xFF,0xBF,0xFF,0xFB,0xFF, 0xF8,0x7F,0xEA,0xFF,0xFE,0xFE,0xDF,0xFF, +0xF7,0xFF,0x7F,0xBB,0xFF,0xFF,0xBF,0xDF, 0xFB,0xFF,0xFF,0xBF,0xFF,0xB1,0x7F,0xFF, +0xFB,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xBF, 0xCF,0xFE,0xFF,0xFF,0xEF,0xFF,0xF7,0xFF, +0xFF,0xFF,0xF1,0xFF,0x69,0xBE,0xFA,0xBF, 0xAF,0xE2,0xFF,0xFE,0xFD,0xAF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFC,0xFF,0xFF,0x07, 0xFD,0x95,0xDB,0xDF,0x7F,0xDF,0xAF,0xFF, +0xF7,0xAF,0x36,0xFE,0xBF,0x65,0xEB,0xF6, 0xFE,0x9F,0x6F,0xFE,0x07,0xFF,0xCF,0xFF, +0xF8,0xFE,0xFF,0xCF,0xFF,0xF6,0xFA,0xE7, 0xFB,0xFE,0xFF,0xBB,0xED,0xF9,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0x75,0xFF,0xEF, 0x7E,0xFD,0xE0,0xE8,0x5E,0xD3,0xE5,0xF9, +0x3E,0x5F,0xD7,0xF7,0xFF,0xFA,0x2F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0x7F, +0x7F,0xD7,0xF5,0x7D,0x5F,0x57,0xD5,0xF5, 0xEF,0xFF,0xF3,0x7F,0xFC,0x7F,0xFF,0xC7, +0xF1,0xFF,0xFF,0x1F,0xCF,0xB0,0xFF,0x3F, 0xCF,0xF3,0xFC,0xFF,0x3F,0xCE,0xFF,0xE4, +0xFF,0xDF,0x7F,0xFE,0xF7,0xBB,0xFF,0xFF, 0xDF,0xEF,0xEE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xBF,0xBF,0xEF,0xFF,0xD1,0xFF,0xFF,0xFF, 0xFD,0xFB,0xFF,0xFD,0xFF,0xFB,0x9F,0xE9, +0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0xBF, 0xFF,0xB3,0xFF,0xFF,0xF7,0xFF,0xFF,0xAF, +0xF7,0xFF,0xB6,0x3F,0xEB,0xFA,0xFE,0xBF, 0xAF,0xEB,0xFA,0xFE,0xBF,0xFE,0xA7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF, 0xFE,0x9F,0xF7,0xF9,0xFF,0x7F,0x9F,0xE7, +0xFF,0xFF,0xFE,0xAF,0x6F,0xFF,0xFF,0xFF, 0x9F,0xFF,0xDF,0xFF,0x7D,0x5F,0xDD,0xFF, +0xFB,0xBF,0xE7,0xBB,0xFF,0xFB,0xDF,0x6D, 0x5F,0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xEB,0xF7,0xFF,0xE7,0xEF,0xF7,0xFF,0xFF, 0x7F,0xFF,0xF7,0xFF,0xFC,0x8F,0xFF,0xEF, +0xFD,0xFE,0xFF,0xBE,0xF4,0xF2,0x7D,0xD7, 0xCF,0xFF,0x3F,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xCF,0x6B,0xFF,0xBF,0x3F,0xFB,0xF2, 0xFC,0x7F,0xEB,0xFF,0x9F,0xFA,0xFF,0xFF, +0x3F,0xFF,0xF3,0xFF,0xFF,0xFD,0x70,0xF7, 0xFF,0xFF,0xBF,0xFF,0xFB,0xD7,0xFE,0xF5, +0x77,0xFF,0x15,0xDD,0x77,0xFD,0xFF,0x7F, 0xDF,0xF7,0xFB,0xCD,0xBF,0xFF,0xFD,0xFF, +0xFF,0xDF,0x37,0xCD,0xF9,0xEC,0xFE,0xEF, 0xBB,0xF4,0xFB,0x3F,0x4F,0xB3,0xFF,0xFD, +0xCB,0xFF,0xE9,0x7E,0x54,0x9F,0xE5,0x4B, 0xB7,0xFF,0xDD,0x7D,0xC7,0x71,0xDD,0x77, +0x5D,0xD7,0x75,0xCD,0x7F,0xD6,0xFF,0xD3, 0xF6,0xF9,0x3F,0x6D,0x95,0xAF,0x7F,0xFE, +0xFF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFE,0xF6,0xC7,0xFF,0xAD,0x7B,0xCA,0xFF, +0xBF,0xBF,0xEF,0xFD,0xE3,0xDF,0xB7,0xED, 0xFB,0x7E,0xDF,0x37,0xED,0xE3,0xFB,0xDF, +0xFF,0x52,0x5C,0x15,0xFD,0xCF,0x7F,0xDF, 0xFE,0xEF,0xEF,0xFB,0xFE,0xFF,0xBF,0xEC, +0x7B,0xFE,0xFF,0xFE,0x3E,0x7F,0xDA,0xF7, 0xFD,0xFF,0x7F,0xFF,0xFF,0xFB,0xEF,0xBB, +0x6F,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, 0xF7,0x7D,0xFF,0xD8,0xFF,0xFD,0xBF,0x7F, +0xFB,0xFF,0xFF,0x9F,0xFB,0xFE,0x7F,0x9F, 0xE7,0xF9,0xFE,0x7F,0x9F,0xEA,0x7F,0xF6, +0xBF,0xBD,0x6A,0x5A,0xF6,0xE5,0xBF,0x77, 0x5F,0x6D,0xDD,0x77,0x5D,0xD7,0x75,0xDD, +0x77,0xFF,0xA5,0xBF,0xCF,0xFB,0xFF,0xFF, 0xBF,0xCF,0xFB,0xFD,0xFF,0xBF,0xF3,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD,0xAB, 0xFF,0xBF,0xBF,0xFF,0xFB,0xFF,0x7F,0xEF, +0xFF,0xBE,0xFB,0xEE,0xFB,0xBE,0xEF,0xBB, 0xEE,0xFB,0xBF,0xFF,0xB5,0xFF,0xD0,0xBC, +0xFD,0x2F,0x4B,0xF7,0xFF,0xFF,0x9F,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x9F, +0xFA,0x8F,0xFD,0xAB,0xFA,0xDA,0xBF,0xAF, 0xB3,0xFD,0xFF,0xBF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFE,0xF7,0xBF,0xFF,0x9F,0xFF, 0x77,0xF7,0xBD,0xFD,0x77,0xDF,0xFF,0x7E, +0xDF,0xED,0xBB,0xFE,0xFF,0xBE,0xEF,0xFB, 0xFE,0xFF,0xFA,0x3F,0xFF,0xBE,0x6F,0x8F, +0xE6,0xF9,0xFE,0x7F,0x9F,0xC7,0xFE,0x7F, 0x9F,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7,0xFB, +0x7F,0xFF,0x7F,0xCF,0xFF,0xFD,0xFF,0xFF, 0xDF,0xFB,0xAF,0xBF,0xEF,0xFF,0xFE,0xFF, +0x9F,0xEF,0xFB,0xFF,0xFC,0xFF,0xFB,0xFE, 0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF5,0xFF,0xFF,0xFF,0x3F,0xDF,0xF7, +0xFF,0xFF,0x7F,0xEF,0xFE,0xFF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xEF,0xFF,0xB3,0x7F, +0xFF,0x7B,0x5E,0xF7,0xFD,0xFF,0x7B,0x7F, 0xF7,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xDF,0xF7,0xFF,0x17,0xFF,0xFF,0xFF,0x7F, 0xFF,0xFF,0xDD,0xF6,0xFC,0xBF,0xCB,0xF2, +0xBC,0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xFE, 0x8F,0xFF,0xFA,0x7E,0xBF,0xA7,0xEB,0xDA, +0xFC,0xBF,0xAF,0x7A,0xFE,0xBF,0xAF,0xEA, 0xFA,0xFE,0xBF,0xAF,0xF4,0xDF,0xFE,0xFF, +0xF3,0x3C,0x7F,0x3E,0xFF,0xCF,0xF8,0xBF, 0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xE7,0xE8, +0xFF,0xFC,0x9F,0xFF,0xFF,0xCF,0xEB,0xB3, 0xE7,0xFB,0x7B,0xF3,0xFE,0xFF,0xCF,0xDB, +0xFB,0xFB,0xBF,0x6F,0x6F,0xDF,0xEC,0x7F, 0xFF,0xFF,0xF7,0xFD,0xFD,0xFF,0xFF,0xFF, +0xFF,0xB2,0xBF,0xFF,0xDE,0xFD,0xBD,0xEF, 0xFB,0xF6,0xDF,0xEA,0xE7,0xDB,0xFE,0xBB, +0xFF,0xEB,0xFB,0xBF,0x9F,0x8F,0xE8,0xFE, 0x3F,0x8F,0xA3,0xF8,0xFE,0x3F,0x8F,0xFF, +0xF8,0x7E,0xFD,0xFD,0x7F,0xFF,0xFB,0xCD, 0xFF,0xFD,0xFF,0x5F,0xEF,0xFD,0xFF,0xFF, +0xDF,0xF7,0xFD,0xFF,0xBE,0x90,0xFF,0xFF, 0xEE,0xFF,0x3F,0xBF,0xF3,0xBB,0xFE,0xB7, +0xAB,0xFA,0xFE,0xAF,0xAD,0xEA,0xFA,0xDE, 0xAB,0xFF,0x63,0xFF,0xFE,0xF2,0xFF,0xB3, +0xFF,0xDF,0xEE,0x7D,0xFF,0x03,0xF1,0xF4, 0x3F,0x1F,0xC3,0xF1,0xEC,0x7F,0xFE,0x6F, +0xFF,0xFB,0xFB,0xFF,0x9F,0xFF,0xBF,0xFF, 0x7B,0x5F,0xFD,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0x7F,0xDF,0xFE,0xCF,0xFB,0xFF,0xFF, 0xAF,0xFB,0xFF,0x1F,0xEF,0xA5,0xFD,0xBF, +0xDF,0xFB,0x7D,0xFF,0xBF,0xDF,0xFB,0xFF, 0xFD,0x3B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xAF,0xF3,0xFF,0xFB,0x7F,0xBF,0xD7,0xFB, 0xBF,0x7F,0xBB,0xF7,0xFF,0xF8,0x7F,0xFF, +0xFA,0x5F,0xD7,0xFF,0xDF,0x7F,0xEF,0xFF, 0xFF,0x7F,0xDB,0xF7,0xFD,0xFF,0x7F,0xDF, +0xB7,0xFB,0xEC,0xFF,0xFF,0xF7,0xBF,0xEF, 0xFD,0xFC,0xFB,0xFF,0xEF,0xF0,0xFE,0x3F, +0x8F,0xE3,0xF8,0xFE,0x3F,0x8F,0xEF,0x8D, 0xFF,0xFF,0xEF,0x7F,0xBF,0xFF,0xFB,0xFF, +0xDB,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0xD8,0xFF,0x2E,0x7F, +0xBE,0xEF,0xFE,0x6E,0xFF,0xBF,0xF9,0xFF, 0xFF,0xF3,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFC,0x66,0xBE,0x47,0xF3,0x7F,0xDF,0xFE, 0x87,0x9F,0xFF,0xFF,0xFF,0xFF,0xE7,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xD6,0x6F,0x7C, 0xFB,0x4F,0xD2,0xFF,0xFD,0x2B,0xFE,0xFF, +0xFF,0xFD,0x5F,0xD7,0xD5,0xF5,0x7D,0xFF, 0xFF,0xFF,0xBF,0x9B,0xFF,0xFF,0xDF,0xB7, +0xFF,0xFF,0xDF,0xFF,0x3F,0xCF,0xFE,0x7F, 0xBF,0xEF,0xFB,0xFC,0xFF,0x3F,0xFF,0xD9, +0xBF,0xFE,0x97,0xEC,0x8F,0xB7,0xFE,0x9B, 0x7D,0xFD,0xB7,0xDD,0x77,0x1D,0xC7,0x71, +0xDD,0x77,0x5D,0xD7,0xF3,0x6F,0xFD,0x3F, 0x73,0xDD,0xAF,0xFD,0x7A,0xFF,0xFF,0xAF, +0xFE,0xFD,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, 0xEF,0x66,0x7F,0xFF,0xFF,0xBF,0xBF,0xFF, +0xFB,0xFF,0xF7,0xDF,0xFD,0xFB,0x7D,0xDF, 0xB7,0xCD,0xF3,0x7C,0x5F,0x3F,0x91,0x3F, +0xFF,0x3D,0xEF,0x7B,0xFF,0xFC,0xFF,0xCA, 0xEF,0xFE,0xFF,0xBD,0xEF,0xFB,0x1E,0xE7, +0xBB,0xEC,0x7F,0xB3,0xFF,0xFD,0x9F,0xFF, 0xFF,0xFE,0xFF,0xFF,0x7F,0xBF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xEE,0xFB,0xBF,0xDF, 0x67,0xFF,0xFF,0xBF,0xEF,0xDB,0xFF,0xBC, +0xFE,0x7F,0xFB,0xFF,0x9F,0xEF,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x87,0xFF,0xEE, +0xFB,0xBE,0xE5,0xBF,0xEF,0xF9,0xD7,0x65, 0xF7,0xDD,0xE7,0x7D,0xDF,0x77,0x5D,0xD7, +0x7F,0xF8,0x9B,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xBF,0xEF,0xFB,0xFF,0x7F,0xCF, +0xF3,0xFC,0xFF,0xBF,0xEF,0xFF,0xDB,0x3F, 0xEF,0xFB,0xFE,0xFF,0xDF,0xFF,0xFE,0xFB, +0xBB,0xEF,0xBF,0xEF,0xBB,0xEE,0xFB,0xBE, 0xEF,0xBB,0xFF,0xFC,0x7F,0xFD,0x3B,0x5B, +0xD6,0xE5,0xFD,0x4F,0xC3,0xFB,0xFF,0xBF, 0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFF, +0xB4,0xFF,0xFA,0xBC,0x8F,0xB2,0xE9,0xD2, 0x2E,0xCF,0xFB,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFB,0xFF,0xEC,0xFF,0xFD, 0xFD,0x7F,0xDF,0xF7,0xE4,0xDF,0x5F,0xFF, +0xFF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xC3,0xFF,0xEF,0xE6,0xF8,0xFE, +0x3F,0x8B,0x83,0xF9,0xFE,0x7F,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x17, +0xFD,0xFF,0xFF,0xFF,0x7F,0x5F,0xF7,0x2C, 0xFF,0xFF,0xFF,0xFE,0x7F,0xFF,0xE7,0xF9, +0xFE,0x7F,0x9F,0xFE,0x2F,0xFF,0xFF,0xEF, 0xFF,0xFE,0xBF,0xEF,0xAD,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFE,0xDF,0xFF,0xDF,0xFF,0xFD,0xFD,0x7F, +0xDF,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0x3F,0xFE, +0xF7,0xFD,0xEF,0x7A,0xFF,0xB1,0xBD,0xFF, 0x7F,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7,0xFD, +0xFF,0x7F,0xF3,0x27,0xFF,0xDF,0xFF,0xDD, 0xFF,0xFC,0x9B,0xFF,0xCB,0xFC,0xBF,0x2F, +0xCB,0xF2,0xFC,0xBF,0x2F,0xC9,0xFF,0xDE, 0xFF,0xDF,0xAF,0xEB,0xDA,0xFE,0xBB,0xAF, +0xEB,0xF8,0xF7,0xAF,0xE8,0xFA,0xFE,0xBF, 0xAF,0xEB,0xF2,0xFF,0xFD,0xFF,0xFF,0xEF, +0xBD,0xD7,0xBF,0xFF,0xFF,0xDE,0x8F,0xB8, 0xDE,0x37,0x8D,0xA3,0x78,0xDA,0x3F,0x8F, +0xFF,0xA1,0xFF,0xFF,0xFB,0xFB,0xFF,0xFF, 0xFF,0xFF,0xA7,0xBD,0xFB,0x76,0xFD,0xBF, +0xEF,0xDB,0xFE,0xBB,0xBF,0xFE,0x27,0x7F, 0xFF,0xFE,0xFE,0xFD,0xF5,0xFF,0xEF,0xF5, +0xDF,0x1F,0xE7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0xFF,0xCD,0xFD,0xAE,0xFF,0xFA, +0x3E,0x3F,0xAB,0xFD,0xF8,0x7E,0x8F,0xE3, 0xF8,0xFE,0x3E,0x8F,0xE3,0xF8,0xFF,0xFE, +0x1F,0xEF,0xDF,0xBF,0xFE,0xDE,0xDF,0xD9, 0xFF,0xDF,0xBC,0xFF,0xFF,0x7F,0xFF,0xEF, +0xFD,0x7F,0xDF,0xF7,0xF9,0x3F,0xFE,0xFF, 0xFF,0x6F,0xFE,0xDE,0xBF,0xF7,0xED,0xEA, +0xFD,0x8F,0x83,0xF8,0xEA,0x3F,0x8F,0xEF, 0xFF,0xF4,0x7F,0xFF,0xEF,0xEF,0x7B,0xF3, +0xF1,0x5F,0xFF,0xFF,0xF1,0x3B,0x7F,0xDF, 0xF7,0xFD,0xFF,0xFF,0xFF,0xFF,0xE0,0xFF, +0xFF,0xFF,0xF7,0xFF,0x6F,0xFF,0x7F,0xFF, 0xFF,0xF7,0xDE,0xF7,0xBF,0xEF,0xFB,0xF7, +0xFD,0xFF,0xFF,0xF5,0xFA,0xFF,0xFF,0xFB, 0xE7,0xFF,0xF3,0xF8,0x7F,0xF3,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x1F,0xEF, 0xBB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, +0xFF,0x7F,0xFF,0x9F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xCF,0xFF,0x37,0xFF,0xFF, +0x7F,0xDF,0x77,0x5D,0xE7,0xFC,0xFF,0xBF, 0xF7,0xF5,0xFB,0xFF,0xFF,0xD7,0xF5,0xFB, +0xFF,0xFF,0x45,0xFD,0x7F,0xEA,0xFD,0xBE, 0xBF,0xDF,0xF7,0xFF,0xFF,0xDB,0xFB,0xFE, +0xFF,0xBF,0xEF,0xFF,0xFF,0xFF,0xFB,0x5F, 0x7F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFE,0xFF,0xEF,0xFD,0xFF,0x7F,0xDF, 0xFF,0xEF,0xFB,0xF8,0x0F,0xF3,0xFF,0xF9, +0x2E,0xFB,0xFE,0xFC,0xF3,0xEF,0xFF,0xFF, 0xBF,0xFF,0xFB,0xE7,0xFF,0xFE,0x7E,0xFF, +0xC0,0x6B,0xCF,0xFF,0x34,0xDF,0xF1,0xFD, 0xFF,0xEF,0xFF,0xFF,0xFF,0xDF,0xF7,0xFD, +0xCF,0x7F,0x9C,0xFD,0xFD,0x6C,0xF7,0xFF, 0xF6,0xFD,0xEB,0x2B,0x9F,0xFF,0xFC,0xFE, +0x7E,0xFF,0xFF,0xFF,0xFF,0xD7,0xF3,0xF7, 0xFF,0xFB,0xE1,0xBF,0xFF,0xEB,0x7A,0xDE, +0xD7,0xFB,0xFF,0xF9,0xFE,0xFF,0xFF,0xF3, 0xDE,0x7F,0xFD,0xE7,0x7F,0xFF,0xFD,0xBB, +0xFF,0xFF,0x7E,0xCC,0xF6,0xAF,0x5F,0x7F, 0xFE,0xF4,0x7D,0xF7,0xFD,0xBB,0x6E,0xDB, +0xB7,0xFF,0xF7,0xDF,0x66,0xFF,0xFF,0xF7, 0x3D,0xCF,0xDE,0xBD,0xFF,0xFF,0xDE,0xDB, +0x8D,0xF7,0x7E,0xDF,0xB7,0xEF,0x7F,0xFF, 0xF6,0x87,0xFF,0xFF,0xEF,0xFE,0xDE,0xBF, +0xFF,0xFF,0xFF,0xBB,0xEF,0xFD,0xFF,0x7B, 0xDE,0xF7,0x3F,0xFF,0xBF,0xFB,0xDB,0xFF, +0xF2,0xB6,0xFD,0xBD,0x7F,0xE7,0xFF,0xFF, 0xFF,0x6F,0xF7,0xFF,0xFF,0xFF,0xFE,0x77, +0xFF,0xBF,0xF8,0xAF,0xFF,0xDF,0xBF,0xFF, 0xBF,0x7F,0xFB,0xFF,0xFF,0xFF,0xDB,0xFE, +0xFF,0xBF,0xFF,0xFA,0xFF,0xFD,0xFF,0xF6, 0x7F,0xFF,0x9F,0xFF,0xFF,0x3F,0xEF,0xF8, +0xEE,0x7E,0x9F,0xBA,0xFE,0xBF,0x8F,0xEF, 0xFE,0xFE,0xF9,0xFF,0xFA,0x7F,0xFE,0x7E, +0xBF,0xAF,0xFB,0x96,0xFD,0x9F,0xEF,0x5E, 0x65,0xBE,0xEF,0x5B,0xB6,0xFF,0xBE,0xE3, +0xFF,0xB5,0xBF,0xFF,0xFD,0xFF,0x7F,0xFF, 0xEF,0xDF,0xFE,0xFF,0xBF,0xFB,0xFE,0xFF, +0xBF,0xCF,0xFF,0xFF,0xFF,0xFD,0x9B,0xFF, 0xFE,0xFB,0xFE,0xDF,0xFF,0x7F,0xFF,0xF7, +0xFE,0xFF,0xDF,0xFB,0xFB,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xB7,0xFE,0xFA,0xFF,0xAB, +0xEF,0xFF,0xFD,0xB5,0x7B,0x7F,0xFB,0xF7, 0xFD,0xFF,0xFF,0xDD,0xFF,0xEF,0x8F,0xFF, +0x2F,0xFF,0xFB,0x7C,0xFF,0x3F,0xDF,0x73, 0xEB,0xFE,0x3F,0xFF,0xEF,0xFB,0xFE,0xFF, +0xEF,0xFD,0xFF,0xBF,0xFD,0x0F,0xFF,0xFF, 0xFF,0xF5,0xF9,0xFF,0x7F,0xD7,0xFD,0xFF, +0xDF,0xFF,0xF7,0xFB,0xFF,0x7F,0xBF,0xFF, 0xFF,0xF0,0x9F,0xFF,0xFE,0x7F,0x8B,0xE3, +0xF9,0xDE,0x27,0x9B,0xE6,0xBE,0x7F,0x9B, 0xC3,0xF8,0xDE,0x7F,0x9D,0xE7,0xFE,0x7F, +0xFF,0xFF,0x5F,0xD7,0xFF,0xFF,0xFF,0x4F, 0xFB,0xFF,0xFF,0x7F,0xFF,0xAF,0xFF,0x9F, +0x7F,0xFB,0xFF,0xE8,0xFF,0xFF,0xFE,0xBF, 0xAF,0xFF,0xFF,0xFE,0xBF,0xEF,0xF7,0xFF, +0xBF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFC,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, +0xFD,0x3F,0xCF,0xFF,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFD,0x7F,0xFF,0xFF,0x93,0xFF,0xFF, +0x7A,0xDF,0xF7,0xFF,0xFF,0x7B,0x7F,0xB7, 0xEF,0xFF,0xFF,0xFD,0xBF,0xFD,0xFB,0xFF, +0xF7,0xFF,0xD7,0xFF,0xFF,0xFF,0xFC,0x9F, 0x6F,0xCB,0xFF,0xF4,0xBB,0xDF,0xD6,0xFD, +0xBF,0x2F,0xD3,0xF7,0xFF,0xDF,0xFF,0xCF, 0xFF,0xFA,0xBE,0xBD,0xAF,0x6A,0xDA,0xBE, +0xBB,0xAB,0x3A,0xBE,0x2D,0xAE,0xEB,0xDA, 0xF6,0x3F,0xAD,0xF5,0xDD,0xFF,0xCF,0xF1, +0xFF,0xF9,0x7F,0xFF,0x73,0xFE,0xFF,0xCF, 0xC3,0xF4,0xF7,0x2F,0xF3,0xFF,0xFC,0xFF, +0x7C,0x1F,0xFF,0x3F,0x4F,0xFF,0x7E,0xFF, 0xEF,0xBD,0xF6,0xFE,0xFF,0x2B,0xEF,0xDC, +0xFB,0xFD,0xFF,0xFB,0xFF,0xEA,0x7B,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFB,0xF7,0xDF,0xFF, +0xE3,0x7D,0xFF,0xB7,0xFF,0xBF,0xFF,0xFF, 0xDF,0xFF,0xF8,0xFF,0xBF,0xFF,0xBF,0xEB, +0xE7,0xFA,0xFE,0x3D,0xBF,0xE9,0xFC,0xBF, 0xFF,0xFA,0xFB,0xFE,0xFF,0xFF,0xFF,0xD9, +0xFF,0xFF,0xFF,0xF6,0x7F,0xFF,0xF6,0x7D, 0xFF,0xDF,0xCF,0xFD,0xBF,0xFB,0xEF,0x7E, +0xFF,0x7F,0xFF,0xFF,0xD3,0xFF,0xFD,0xFB, 0xFF,0xFB,0xFF,0xFF,0xFF,0xEF,0xFF,0xBF, +0xFE,0xFF,0xF7,0xEF,0xFF,0xFF,0xFF,0xFB, 0xFF,0x87,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF, +0x7B,0xFE,0xFF,0xFE,0x3B,0xF7,0xF7,0xFF, 0x3F,0xFF,0xFF,0xFF,0xFF,0xFF,0x0F,0xFF, +0xFF,0xFF,0xFF,0xFB,0xFF,0xFF,0xFF,0xF7, 0xFF,0xFF,0xAD,0xFF,0xFE,0xF7,0xFF,0xFF, +0x5F,0xFF,0xFF,0xDF,0xFF,0xFD,0xFF,0xF5, 0xFF,0xDF,0xFF,0xBD,0xFF,0xE9,0xFF,0xC7, +0xF3,0xFF,0xFF,0xF7,0xFF,0xF3,0xFF,0xF8, 0x3B,0xFF,0xFF,0x7B,0xDF,0xBF,0xFB,0xEF, +0xFB,0xFF,0xFB,0xF7,0xF7,0xBB,0xFF,0xFF, 0xFF,0xFF,0xFB,0xFF,0xFE,0x7F,0xF3,0x7F, +0x5E,0xB7,0xBF,0xFD,0x7F,0xFF,0xF9,0x7F, 0xFB,0xFF,0xEB,0xFD,0x7F,0x7F,0xFF,0xEF, +0xFB,0xE0,0x3F,0xFE,0xBF,0xBF,0xDF,0xFF, 0x7E,0xFF,0xF7,0xFF,0xFF,0xFE,0xBF,0xFF, +0xDB,0x78,0xFF,0xFF,0xFF,0xEE,0xA1,0xBF, 0xF5,0xDE,0xFB,0xF7,0xFF,0xFB,0xFF,0xFF, +0xFF,0xFF,0xFB,0xFF,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xEF,0xF0,0xFF,0xFF,0xFF,0xF3, +0xF7,0xFF,0xEF,0xFF,0xE7,0xCF,0xFF,0xFB, 0xFF,0xEF,0xFF,0xFF,0x9F,0x9F,0xEF,0xFC, +0x16,0xBF,0xFE,0xF3,0xE4,0xFF,0xFF,0xC6, 0xFF,0xE7,0xFF,0xFF,0xFD,0xFF,0xBF,0xFF, +0xFF,0x3F,0xFF,0xBF,0xD6,0xAF,0x7F,0xFE, 0x6B,0x7E,0x7F,0xFF,0xAF,0xFF,0xFF,0xBF, +0xFF,0x5F,0xFF,0xFE,0xFF,0xFF,0xFE,0xFF, 0xFF,0xBD,0xDB,0xFF,0xFE,0x5F,0xF2,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xEF,0x7F,0xFF,0xFF,0xFF,0xFF,0xDE,0xBF, +0xFF,0xFF,0xEF,0xFB,0x77,0xFE,0xBD,0x7F, 0x5F,0xFF,0xFF,0xFF,0xDF,0x6F,0xED,0xFF, +0xFD,0xFF,0x7F,0xFD,0x6F,0xFF,0xFF,0x77, 0xDA,0xCF,0xFD,0x5F,0xFF,0xBF,0xFF,0xFF, +0xDF,0x7F,0xFF,0xFB,0xFF,0xFF,0xFF,0xFF, 0x66,0x7F,0xFF,0xFE,0xBF,0xE7,0xBF,0xFA, +0xFF,0xFE,0xFF,0xFF,0xFF,0xDF,0xFF,0x59, 0xEF,0xFF,0xEF,0xFB,0x7F,0x89,0xFF,0xFF, +0xE9,0xFF,0x6F,0xFF,0xF5,0xFF,0xFF,0xFF, 0xFF,0xFF,0x7F,0xF2,0xF7,0xFF,0xFF,0xEF, +0xF8,0x7F,0xFB,0xFF,0xFD,0xFF,0xFF,0xD9, 0xFF,0xEF,0xBB,0xFF,0xFF,0xFF,0xBF,0xEF, +0xDE,0xFF,0xFF,0x9F,0x7F,0xDF,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF,0xAF, +0xFF,0xFF,0xF7,0x3F,0xEB,0x9F,0xFE,0x7F, 0x9E,0x7F,0x9F,0xFE,0x87,0xFF,0xED,0xDB, +0x56,0xFF,0xBF,0xAF,0x0B,0xD2,0xFF,0xEF, 0xDB,0x6E,0x7D,0xBD,0x6F,0xF8,0xFE,0x3F, +0xFA,0x5B,0xFF,0xFD,0xBF,0xEF,0xFF,0xBF, 0x6F,0xDB,0xE6,0xFF,0xFF,0x3F,0xFF,0xDF, +0xFE,0xFF,0xFF,0xFF,0xFF,0xDA,0x3F,0xFF, 0xFB,0xFE,0xFE,0xFF,0xFF,0xDF,0xF7,0xBD, +0xFF,0xFD,0xFF,0xFE,0xFF,0xFB,0xFF,0xFF, 0xFF,0xFF,0xF1,0x5F,0xFD,0x9F,0xDF,0xFD, +0xFF,0xFD,0x7F,0xFF,0xFF,0xFF,0xFF,0x76, 0xFA,0xFF,0xFF,0x7F,0xE3,0xF8,0xFF,0xAE, +0xFF,0xFB,0x7E,0x9D,0x73,0xFF,0xFA,0x7F, 0xDF,0xFF,0xFF,0x7F,0xFF,0xFB,0xCD,0xFF, +0x7F,0xEF,0xFB,0xFF,0xFD,0xFF,0xF7,0x7F, 0x7F,0xEF,0xFF,0xED,0xFF,0xFF,0xFF,0xB5, +0xFF,0xBF,0xFF,0xBF,0xFD,0xEF,0xDB,0xF7, 0xFF,0x93,0xFF,0xEF,0xE2,0xF9,0xBE,0x7F, +0x8B,0xE7,0xF9,0xFE,0x6B,0xE7,0xF9,0xFE, 0x7F,0x9F,0xE7,0xF9,0xFE,0x7F,0x47,0xFF, +0xFF,0xFD,0xFF,0x9F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xF5,0xFF,0x9F,0xFF,0xF7,0xFE, +0xFF,0xBF,0xFE,0x6F,0xFF,0xFF,0xFB,0xFF, 0xFF,0xFF,0xAF,0xFF,0xFF,0xFF,0x7F,0xFB, +0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xDF,0xFF,0xFF,0xF7,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0xFF,0x5F,0xFF,0xFF,0xFF,0xFF, 0x5F,0xFB,0xFE,0xFF,0xF8,0x37,0xFF,0xFF, +0xEF,0xFF,0x7F,0xFE,0xBF,0xFF,0xFF,0xFE, 0xBF,0xFF,0xFF,0x7F,0xFF,0xBF,0xFD,0xFF, +0x7F,0xFA,0x7F,0xFF,0xFF,0x6F,0xFF,0xFF, 0x7D,0xFF,0xCF,0xFF,0xFF,0xFF,0x4F,0xFF, +0xF2,0xFF,0xFF,0xFF,0xFF,0xFF,0xFA,0xBF, 0xFF,0xAE,0xEB,0xFA,0xFE,0xBB,0xAD,0xEB, +0xFA,0xF7,0xAF,0x6B,0xFA,0xF6,0xBF,0x25, 0xE9,0xF2,0x7F,0x45,0xFF,0xFF,0xFD,0xF7, +0xF7,0xBF,0xFF,0xDF,0xFF,0xFF,0xBF,0xFB, 0xFF,0xDF,0xF3,0xFF,0xF7,0x3F,0xCF,0xFF, +0xA1,0xFF,0xFF,0xBF,0xE7,0xFF,0xFF,0x7F, 0xFF,0x3D,0xFF,0xFF,0xFF,0xF7,0xFF,0x2F, +0xFF,0xFB,0xF5,0x7F,0xFE,0x57,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0x3F,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD,0xFE, 0xF7,0xEE,0xAF,0xFE,0xEE,0xE7,0xFA,0xFF, +0xFE,0x9D,0xF9,0x5E,0xFE,0xFF,0xEB,0xFF, 0xFF,0xDF,0xA7,0xFF,0xFF,0xFF,0xFC,0xDB, +0xFF,0xFF,0xFF,0x7E,0xFB,0xFF,0xFF,0xEF, 0xFB,0xFD,0xFF,0xDB,0xFF,0xFF,0xFF,0xEF, +0xFF,0xFF,0xFF,0xFD,0xBF,0xFE,0xBF,0xFF, 0x6F,0x7F,0xFF,0xF7,0xFF,0xFF,0xF9,0xFF, +0xF7,0xFF,0xBF,0xDE,0xF7,0xFF,0xFF,0xFF, 0xFA,0x7F,0xFD,0xBF,0x5F,0xFF,0xFF,0xBF, +0xFF,0xED,0xFF,0xF7,0xBF,0xFF,0xFF,0xEF, 0xFF,0xDF,0xFF,0xFF,0xFF,0xE6,0xFF,0xFB, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEB,0xFF, +0xFD,0xFF,0xF5,0xFF,0xF6,0x7F,0xDF,0xBD, 0xCF,0xFF,0xFF,0xFF,0xFF,0xDF,0xFF,0xFF, +0xFF,0xF9,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3, 0xFF,0xEE,0xBF,0xFF,0x7D,0xEF,0xFE,0xFF, +0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFE, 0xFF,0xFF,0xFF,0xFF,0xE7,0xFF,0xB5,0xAE, +0xFF,0xFF,0xB6,0xFE,0xBF,0xFF,0xFF,0xBF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0x27,0xFF,0xEF,0xFE,0x7F,0xDF,0xFF, 0x7E,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFD,0xFF,0xF7,0xF9,0x9F,0xFF, 0x5F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F, +0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0x0F,0xFF,0xE7,0xBF,0xFE, +0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFC,0xBF, 0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xC4, +0x6B,0xFF,0x29,0x1F,0xFB,0xAF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xEF,0x1B,0xFE,0xFF,0xFC, +0x6F,0xFF,0xFF,0xFD,0x6A,0xF7,0xD7,0xF5, 0xBF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFE,0xBF,0xFF,0xFF,0xFA,0xFF,0xFF,0xF7, 0xFB,0xDD,0xBF,0xFF,0xE7,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0x7F,0xFF, 0xFF,0xF5,0xFF,0xFF,0xF7,0xFD,0xB3,0xEF, +0xFD,0x7E,0x5D,0xFF,0xFD,0xFF,0xFF,0xFF, 0xFD,0x7F,0xD2,0xF5,0xFB,0x7E,0xCB,0xB7, +0xFF,0xFF,0xFF,0xC6,0xFF,0xFD,0xEE,0x63, 0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xFD,0x65, +0x5B,0xDF,0xFF,0xD5,0xFF,0xFF,0xFF,0xF6, 0xE7,0xBF,0xF7,0xA9,0xFF,0xFF,0xED,0xFF, +0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFF,0xFF, 0xAF,0xFF,0xFF,0xFF,0xF8,0x1B,0xFF,0xE3, +0xD0,0xBF,0xFF,0xE1,0xFF,0xFF,0xFF,0xFF, 0xFF,0xD7,0xFF,0xFF,0xFF,0x5F,0xFF,0xFF, +0xFF,0xFF,0xAF,0xFF,0xDB,0x76,0xBF,0xFF, 0x7F,0xFF,0xBF,0xEF,0xFE,0xFF,0xBF,0xEF, +0xFB,0xFE,0xFF,0xFF,0xFF,0xBF,0xF2,0x7F, 0xFF,0x9F,0xFE,0xBD,0xFE,0x7F,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xF7,0x3F,0xEC,0x7F,0xF6,0x95,0xBB, +0xEF,0xF8,0xFE,0xFC,0xBF,0x2F,0xDA,0xFC, 0xBF,0x2F,0xCB,0xF2,0xFC,0xBF,0xEF,0xFF, +0xA9,0xBF,0xCF,0xFB,0xFF,0xFF,0xFF,0xFE, 0xDD,0xB7,0x6D,0xF6,0xD9,0xB6,0x6D,0x9B, +0x76,0xD9,0xBF,0xFB,0xFD,0xA3,0xFF,0xBF, 0xEF,0xFF,0xEF,0xFF,0xFF,0xFF,0x7F,0xDF, +0xFD,0xEF,0x7B,0xDE,0xF7,0xFD,0xEF,0x7F, 0xFF,0xFF,0x05,0xFF,0xFA,0xFE,0x7F,0xEF, +0xE3,0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xFF, 0xFF,0x5F,0xFF,0xFF,0xFD,0x7F,0xFB,0xAF, +0xFF,0x63,0xC8,0xFF,0xBF,0xEF,0xFF,0xFF, 0xFA,0x7F,0xFF,0xFF,0xFF,0xFE,0x9F,0xF7, +0xFF,0xFA,0xBF,0xFE,0x9F,0xFB,0x7F,0xFF, 0xFF,0xEF,0xD7,0xFF,0xFF,0xF5,0xFF,0xFF, +0xFF,0xFF,0xFD,0x7F,0xFF,0xFF,0xBF,0xFF, 0xF9,0xBF,0xFF,0xBE,0x27,0x9F,0xE7,0xF9, +0xFE,0x7F,0x8B,0xE7,0xFE,0x7F,0x9F,0xE2, 0xF9,0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF, +0xFF,0xFF,0xFB,0xFE,0xFF,0xFF,0xFF,0xD7, 0xFF,0xFF,0xFF,0xFF,0xF5,0xFF,0xFF,0xFF, +0xD7,0xFF,0xFA,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFD,0xFF,0xFF,0xFF,0xAF,0xF7,0xFF,0xFF, +0xFF,0xEB,0xFF,0xFF,0xFF,0xAF,0xFF,0xC4, 0xFF,0xF7,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, +0xFF,0x5F,0xFF,0xFF,0xFF,0xFF,0xD7,0xFF, 0xFF,0xFF,0xFF,0xFF,0xEB,0xFF,0xFB,0x7A, +0xDF,0xF7,0xFD,0xFF,0xFF,0xFE,0xBF,0xFF, 0xFF,0x7F,0xFF,0xAF,0xFF,0xFF,0xFF,0xF7, +0xEF,0xE3,0xFF,0xDD,0xD2,0xFF,0xDF,0xFF, 0xFF,0xF2,0xFC,0xBF,0xCB,0xF6,0xFD,0xBF, +0x2F,0xCB,0xFF,0x7F,0xDF,0xDE,0xAF,0xFF, 0xDA,0xEE,0xBF,0xAF,0xE9,0xFA,0xF4,0xBD, +0xAF,0x5A,0xAE,0xBB,0xAB,0x6B,0xDA,0xDE, 0xBF,0xAD,0xD7,0x5E,0xFF,0xFF,0xBF,0xFC, +0xFF,0xDF,0xFD,0xFF,0xFF,0xFF,0xFF,0xDF, 0xF7,0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFA, +0x1F,0xFF,0xFE,0xFB,0xEF,0xBF,0xFD,0xFF, 0xFD,0xBD,0x77,0xFF,0xFF,0xFF,0xFF,0x9D, +0xEF,0xFF,0xFF,0xFF,0xEF,0x7D,0xFF,0xFB, 0xFE,0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xEE, 0xBF,0xE4,0xFB,0xFF,0xFE,0x3F,0xFE,0xFF, +0xFF,0xFF,0xFF,0xAF,0xEA,0xFE,0xBF,0xAF, 0xEB,0xFA,0xFE,0xFF,0xFF,0xFF,0x55,0xF6, +0xFF,0xFE,0xF7,0xFF,0x7F,0xFF,0xEB,0xF7, 0x5F,0xC5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0x6F,0xFB,0xFF,0x8A,0xFF,0xFF,0xFF,0xFF, 0xEB,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0xBF, +0xEF,0xFB,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF, 0x77,0xDF,0xFB,0xFF,0xFD,0x7F,0xEF,0xFF, +0xFF,0xFF,0xBF,0x7F,0xFF,0xDF,0xBF,0xFF, 0xFB,0xFF,0xFF,0xFF,0xFE,0xEF,0xDF,0xFF, +0xFE,0xFF,0x9F,0xEF,0x7D,0xFF,0xF7,0xFF, 0x7F,0xFF,0xFF,0xDF,0xF7,0xFD,0xFF,0xEF, +0xDF,0xFF,0xDF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0xFB, +0xFD,0xFF,0xBF,0xDF,0xD1,0xFF,0xF8,0x3B, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0x7E,0xDB,0xFD,0xFF,0x77,0xDB,0xB7,0x7D, 0xBF,0xFB,0xFF,0xF8,0x7F,0xED,0x7B,0x5E, +0xFF,0xFE,0xFF,0xFF,0x4F,0xD7,0xFD,0x7F, 0xDF,0xD7,0xF5,0xFF,0x7F,0xFF,0xFF,0xFF, +0xF2,0x3F,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, 0xFF,0xBF,0xEF,0xFE,0xFF,0x3B,0xEE,0xFF, +0xFC,0xEF,0xFF,0xFF,0xFF,0x85,0xFF,0xFD, 0xFE,0xFF,0xF5,0xFF,0xFF,0xFE,0xFF,0xDF, +0xFB,0xFF,0x5F,0xBF,0xFF,0xFD,0xFF,0xFF, 0xFF,0xFF,0xA8,0xFF,0xFF,0x9F,0x9E,0xFF, +0xFF,0xFF,0x7F,0xF3,0xFF,0xFF,0xCF,0xFF, 0xF7,0xFD,0xFF,0x7F,0xFF,0xFF,0xFC,0x16, +0xBF,0xCF,0xA3,0xE5,0xEF,0x7F,0xFF,0xF3, 0xE4,0xFF,0xCF,0x93,0xFC,0xFF,0x3F,0xCF, +0xFF,0xFF,0xFF,0xD6,0x0F,0x7D,0xBF,0x6E, 0xFB,0xF4,0xFC,0xAF,0x6D,0xDB,0x77,0xB7, +0x6D,0xDB,0xF6,0xFD,0xBF,0xFF,0xFF,0xFF, 0xBF,0x9B,0xFA,0xDE,0xB7,0xB7,0xED,0xF9, +0x7E,0xB7,0xAC,0xEB,0xD6,0xB3,0xAD,0xEB, 0x7A,0xDF,0xFF,0xFF,0xFF,0xD8,0xBF,0xFF, +0xB7,0xED,0x9F,0x6F,0xDD,0xF7,0x68,0xDB, 0x37,0xB3,0x6C,0xDB,0x36,0xCD,0xB3,0x7F, +0xFF,0x7F,0xF5,0x6F,0xFD,0xEF,0x79,0x3D, 0xF7,0x93,0xE4,0x7A,0x9E,0xAD,0xEA,0x7A, +0x9E,0xF7,0xBD,0xEF,0xFF,0xFF,0xFF,0x76, 0x7F,0xFB,0xC6,0xFF,0xBB,0xEF,0xDA,0xFE, +0xFD,0xBF,0xFB,0xFE,0xFF,0xBF,0xEF,0xFB, 0xFF,0xFF,0xFB,0xFF,0xA5,0xFF,0xFD,0xAB, +0x6F,0x78,0xDE,0x17,0x8F,0x79,0xDF,0xFD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xFF,0xFB, +0xFF,0xFB,0xFF,0xEF,0xFB,0xEF,0xFB,0xFE, 0xFF,0xBB,0xDA,0xF3,0xEF,0x3B,0xCE,0xF3, +0xBC,0xEF,0x3F,0xCF,0xDF,0xFF,0xB7,0xFF, 0xFF,0xFF,0xCF,0x73,0xFF,0xBF,0xEF,0xFF, +0xF3,0xFF,0x3F,0xCF,0xF3,0xFC,0xFF,0x3D, 0xCF,0x9F,0xFE,0x07,0xFF,0xAF,0xEB,0xFE, +0xFD,0xBF,0xEF,0xEB,0xFA,0xFF,0xAF,0xEB, 0xFA,0xFE,0xBF,0xAF,0xFB,0xFE,0x3F,0xFB, +0x9B,0xFF,0x7F,0xDF,0xFF,0xF3,0xFE,0xFF, 0xDE,0xF7,0xBF,0x7B,0xDE,0xF7,0xBD,0xEF, +0x7B,0xFE,0xFF,0xFF,0xDF,0x3F,0xFE,0xFF, 0xB7,0xFF,0xEF,0xF7,0xFF,0xBF,0xED,0xFE, +0xDF,0xB7,0xED,0xFB,0x7E,0xDF,0xFF,0xFF, 0xFF,0xFD,0x5F,0xEF,0xEB,0xFA,0xFE,0xF5, +0xBF,0x6F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xF8,0xFF,0xA8,0xFF, +0xFF,0xBF,0xEF,0xFB,0x6A,0xFB,0xB7,0xEF, 0xFB,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xBF, +0xEF,0xFB,0xFF,0xE0,0xFF,0xFF,0xFD,0x7F, 0x5C,0xD7,0x7D,0xDF,0xF3,0x5C,0xF5,0xCD, +0x73,0x5E,0xD7,0xB5,0xFD,0x7F,0xEF,0xFF, 0xDB,0xFF,0xFF,0xE2,0xF8,0xBE,0x2F,0x8F, +0xE7,0xF8,0xBE,0x6B,0xE2,0xF8,0xBE,0x2F, 0x8B,0xE2,0xF9,0xFE,0x7F,0xE7,0xFF,0xD7, +0xF5,0xFD,0x7F,0xFF,0xF7,0xF5,0xFD,0x7F, 0xD7,0xF5,0xFD,0x7F,0x5F,0xD7,0xF5,0xFF, +0xFF,0xFF,0x8F,0xFF,0xAF,0xEB,0xFA,0xFF, 0xFF,0xBF,0xEB,0xFA,0xFF,0x2F,0xEB,0xFA, +0xFE,0xBF,0xAF,0xEB,0xFF,0xFF,0xFE,0x5F, 0xFF,0x5F,0xFF,0xFF,0xFD,0xFF,0xFF,0xD7, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFE,0xB7,0xFD, +0xFF,0x7E,0xDF,0xF7,0xAD,0xFF,0x7F,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0x7F, +0xF6,0x7F,0xFF,0xFF,0xFF,0xDB,0xF6,0xFC, 0xAF,0xFF,0xFF,0xFF,0xFF,0xF7,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xEC,0xBF,0xFF, 0xAF,0xEB,0xFA,0xF6,0xAB,0x8F,0xEB,0xFA, +0xF7,0xA5,0xEB,0xFA,0xBE,0xBF,0xAF,0xEB, 0xFA,0xFF,0x6D,0xFF,0xFF,0x7F,0xDF,0x33, +0xDD,0xFF,0x7F,0xFE,0xF7,0xFC,0x7F,0xFB, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xA9, +0xFF,0xFD,0xFF,0xFF,0xFE,0xFF,0xFF,0xDF, 0xFF,0xFF,0xEF,0xEF,0xFD,0xFF,0x7F,0xFF, +0xFF,0xFF,0xFF,0xFE,0xA7,0xFF,0xFF,0xFF, 0x77,0xDF,0xF7,0xFD,0x9F,0x7F,0xFE,0x77, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xAF,0xBF,0xAF,0xFF,0xF9,0xBE,0xBF, +0x8F,0xFB,0xFE,0xFE,0xEF,0xFB,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFF,0xFF,0xFD,0xDF,0x6F, +0xEF,0xFF,0x7F,0xFF,0xBF,0xBF,0xDF,0xFF, 0xFC,0xFF,0xDF,0xF7,0xFD,0xEF,0x7F,0xDF, +0xFF,0xFF,0xFF,0x3F,0xF6,0xFF,0xCF,0xFF, 0xDB,0xFB,0xF7,0xFF,0xEB,0x7A,0xFF,0xFF, +0xFF,0xBF,0xEF,0xFB,0xFF,0xFF,0xFF,0xFE, 0x6D,0xFD,0xFF,0x5F,0xFB,0xFF,0xFF,0xF7, +0xFF,0x5F,0xF5,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xF8,0xFF,0xFB,0xFF, +0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xE7,0xF6, 0xBF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xFF, +0xFF,0xC9,0xFF,0xFF,0xFF,0xBD,0xFF,0xBF, 0xAF,0xEF,0xEF,0x3F,0xD1,0xFC,0x7F,0xFB, +0xC7,0xFF,0xFF,0xFF,0xFF,0xFF,0xE3,0xFF, 0xFF,0xFF,0xFF,0xFD,0xFF,0xFF,0x77,0xFF, +0xDF,0xB7,0xFD,0xF7,0xFD,0xF7,0xFF,0xFF, 0xFF,0xFF,0xFF,0x57,0xFF,0xF7,0xA5,0xFD, +0x3F,0xDF,0xBF,0xBF,0xFE,0x7F,0xFF,0xFF, 0xFF,0xDF,0xFA,0xFD,0xFF,0xFF,0xFF,0xFE, +0x87,0xFF,0xE9,0xFF,0xFE,0xEF,0xBF,0xEF, 0xFE,0xFE,0xFF,0xEF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFA,0x9F,0xFF,0x3F, 0xFF,0xFD,0xFD,0x57,0xDF,0xFD,0xF3,0xFF, +0xDF,0xFD,0xFF,0x5F,0xDF,0xF5,0xFD,0xFF, 0xFF,0xF9,0x8F,0xFF,0xFF,0xFF,0xEE,0x7F, +0xFF,0xFF,0xBF,0x5E,0xFE,0xEC,0xFB,0x3F, 0x7F,0x9F,0xEF,0xF9,0xFF,0xFF,0xCD,0x6B, +0xFF,0xFF,0xFF,0xC5,0xF3,0xFC,0xFA,0x38, 0xFF,0xAF,0x3F,0xEE,0x7F,0x9F,0xFF,0xD9, +0xFF,0xFF,0xFD,0x7A,0xF7,0xFF,0xF3,0xFF, 0xAF,0x6F,0xDB,0xF2,0xB9,0xE9,0xFB,0xFF, +0xFF,0xFF,0xFE,0xFF,0xFF,0xEF,0xFF,0xFB, 0xC5,0xBF,0xFF,0xEF,0xFF,0x5E,0xB7,0xAD, +0xCD,0x79,0x7C,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0x93,0xFF,0xEF, +0xEA,0xFE,0xBF,0xEF,0x5B,0xD2,0xCD,0xF5, 0x6D,0x77,0xDF,0xF7,0xFD,0xFF,0x7F,0xDF, +0xFF,0xFF,0x66,0xFF,0xD5,0x65,0x7D,0x5F, 0x75,0x9D,0x65,0x7F,0xD6,0xFB,0x4F,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF6,0xC7, 0xFF,0xBF,0xEF,0xFA,0xFE,0xFF,0xBF,0xEB, +0xFF,0xDF,0xFF,0x7E,0xFF,0xFF,0xEF,0xFD, 0x7E,0xD7,0xFF,0x78,0xDF,0xFF,0x5F,0xDF, +0xF5,0xBF,0x7F,0xDF,0xC5,0xFF,0x3F,0xF6, 0x7E,0xFF,0x0F,0xEF,0xF2,0x3E,0xBF,0xFF, +0xFB,0x3F,0xFF,0xFB,0x7F,0xFF,0xB3,0xFE, 0xFB,0xF6,0xFD,0xFF,0xDA,0xF7,0xFD,0xFF, +0x7F,0xDF,0xF7,0xBF,0xFF,0xFA,0x7F,0xFF, 0xFF,0xFF,0xFF,0x9F,0xFF,0xF3,0xDC,0xF9, +0xBF,0xCE,0xE7,0xF9,0xFE,0x7F,0x9F,0xE7, 0xFF,0xFF,0xE2,0x7F,0xFE,0xFF,0xBF,0xEF, +0xEB,0xFA,0xFF,0x9F,0x67,0x1E,0xFF,0x8F, 0xE7,0xF8,0xFE,0x7F,0x8F,0xEF,0xFF,0xBD, +0xBF,0xFF,0xFB,0xFF,0xFF,0xDF,0xF7,0xFF, 0xFC,0xFF,0xBF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0xB3,0xFF,0xFF,0xEF, 0xFF,0xFF,0xBF,0xED,0xFF,0xFB,0xEE,0xFE, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFF,0xFF,0xFF, 0xFF,0xB5,0xFF,0xB7,0xFD,0xFD,0x6E,0xFF, +0xFF,0xFE,0xFD,0x2F,0xD8,0xFE,0xBF,0x8F, 0xEB,0xF9,0xFE,0x3F,0xFF,0xFA,0xCF,0xFF, +0xE7,0xD9,0xFA,0xBF,0xDF,0x77,0xFC,0xFB, 0x3F,0xAB,0xFE,0xFF,0xBF,0xEF,0xFB,0xFE, +0xFF,0xFF,0xEE,0x1F,0xFF,0xDF,0xF7,0xFF, 0xFF,0xFF,0x5F,0x97,0x35,0xBF,0x5E,0xFE, +0xBF,0xEF,0xFF,0xF7,0xFD,0xFF,0xFF,0xFA, 0xBF,0xFF,0xBE,0x6F,0x9F,0xE7,0xF8,0xBE, +0x2F,0x8B,0x66,0x94,0x7D,0x9D,0xE7,0xF9, 0xFE,0x7F,0x9F,0xE7,0xF1,0x7F,0xFF,0xFF, +0xFF,0xF7,0xF5,0xFD,0x7F,0x5F,0xFB,0xFD, 0x9E,0xFF,0xFB,0xFE,0xFF,0xFF,0xEF,0xFF, +0xFF,0xA0,0xFF,0xFF,0xFF,0xBF,0xEF,0xEB, 0xFA,0xFE,0xBF,0xB7,0xF7,0xF7,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xDD,0xFF, 0xFD,0xFF,0xFF,0xFF,0xD7,0xFF,0xFF,0xFF, +0x7F,0xF5,0xFF,0xFF,0xEF,0xFF,0xFF,0xFF, 0xBF,0xFF,0xFF,0xAB,0xFE,0xFB,0xFE,0xFF, +0xF7,0xAF,0xFF,0xFF,0xDE,0xF7,0xEB,0x5F, 0xDF,0xF7,0xFD,0xFF,0x7F,0xDF,0xFF,0xFF, +0xB3,0xFF,0xC9,0xFE,0xFF,0xFF,0xFF,0xFF, 0xD6,0xFF,0xFF,0xCB,0xFF,0xFF,0xDF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFC,0x8F,0xFF,0xBA, 0xBE,0xBF,0xAF,0xEB,0x78,0xFE,0xB7,0xAD, +0x3A,0xFE,0xB7,0xAF,0xEB,0x7A,0xFE,0xBF, 0xAF,0xFF,0x9F,0xFF,0xFF,0xDF,0xFC,0xFF, +0xFF,0xFE,0xC3,0xFE,0xFF,0xFF,0x33,0xFC, 0xFF,0xBF,0xDF,0xF3,0xFF,0xFF,0xBB,0x9F, +0xFF,0xFF,0xFF,0xEB,0xDF,0xFF,0xFF,0xAF, 0xF7,0x6F,0xF9,0xBF,0xEF,0xFD,0xFF,0xFF, +0xFF,0xFF,0xFF,0xE3,0x7F,0xFF,0xFF,0xFF, 0xFB,0xFF,0xFF,0xBF,0xFD,0xFB,0xF7,0xFF, +0xDF,0xF7,0xFF,0xFE,0xEF,0x5F,0xBD,0xFF, 0xFA,0xFF,0xF8,0xFF,0xBF,0xAF,0xFB,0xFE, +0xFE,0x3F,0xEF,0xE8,0xFF,0xDF,0xF3,0xFD, 0xFF,0xFF,0xFF,0xFF,0xFF,0xED,0xFF,0xFB, +0xFD,0xFF,0xAF,0xFF,0xFF,0xFE,0xFE,0xBF, 0xDB,0xFF,0xFF,0xFF,0xBF,0xFF,0xDF,0xFF, +0xFD,0xFF,0xCB,0xFF,0xFF,0xFF,0xFF,0xFF, 0xBF,0x6F,0xFF,0x7F,0xB7,0xB3,0xFF,0xFF, +0xDF,0xFF,0xFB,0xEF,0xFF,0xFF,0xFF,0x07, 0xFF,0xFB,0xFF,0xFF,0xFF,0xED,0xFF,0xF5, +0x7C,0xFF,0x7F,0xFE,0xFF,0xFF,0xEF,0xCF, 0xFF,0xFB,0xFF,0xFF,0x2F,0xFF,0xFF,0xFF, +0xFF,0xF3,0xFF,0xFB,0xFF,0xFE,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFD,0x1B,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFE,0x7C,0xFF,0xFF,0xFF,0xFF, +0xEF,0xFF,0xFF,0xFF,0xFF,0xFB,0xBF,0x7F, 0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xDB,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFD, 0xFF,0xFF,0xF0,0x7F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFB,0xFF,0xDF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFD,0xBF,0xFE, +0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xEF,0xFE,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xEF,0xFA,0xB5,0xFF,0xFF,0xFF, 0xF7,0xF7,0xFF,0xFF,0xFF,0xFF,0xDF,0xFB, +0xFC,0xFF,0xFF,0xFE,0xFF,0x7F,0xDF,0xBF, 0xFF,0xCB,0xBF,0xF9,0xFE,0x7F,0x9F,0xE7, +0xF9,0xFE,0x7F,0x97,0xE1,0xFE,0x79,0x9F, 0xE7,0xFD,0xFE,0x7F,0xDF,0xFE,0x37,0xFF, +0xFB,0xDE,0xDE,0xBD,0xEF,0xF3,0xFE,0xFB, 0xAF,0xEB,0xFE,0xFF,0xFF,0xCF,0xFF,0xFE, +0xFF,0xBF,0xFF,0x8F,0xFF,0xEF,0xFB,0xFE, 0xFF,0xBF,0xE7,0xF9,0x5E,0x7F,0xEF,0xFB, +0xDA,0xFF,0xBF,0xEF,0xFB,0xFE,0xFF,0xFD, 0x1F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xDF, +0xFF,0xFF,0x7F,0xFF,0xFF,0xF7,0xFB,0x7F, 0xFF,0xFF,0xFF,0xFF,0xFC,0x3F,0xFF,0xBF, +0xEF,0xFB,0xFE,0xFF,0xBF,0xEF,0x7B,0x7F, 0xBF,0xEF,0xFB,0xFE,0xFF,0xB5,0xEF,0xFB, +0xBF,0xFA,0x7F,0xFC,0xFF,0x3F,0xCF,0xF3, 0xFC,0xFF,0x3F,0xCF,0xBC,0xFF,0x3F,0xEF, +0xF3,0xFC,0xFE,0x3F,0xCF,0xFF,0xEE,0xEF, 0xFB,0xFE,0xFF,0xBF,0xEF,0xFB,0x6A,0xD7, +0xB7,0xFB,0xF8,0xFF,0xB7,0xEF,0xBA,0xFE, 0xFF,0xBF,0x7F,0xE9,0xFF,0xF9,0x7E,0x5F, +0x97,0xE5,0xF9,0xFE,0x7F,0xBF,0xF9,0x7E, 0x5F,0x9F,0xE5,0xFB,0xFE,0x5F,0xB7,0xFF, +0xA3,0xFF,0xF7,0xFD,0xFF,0x7F,0xDF,0xF7, 0xFD,0xFF,0x5E,0xF7,0x7D,0xFF,0x77,0xDF, +0xF7,0xFD,0xFF,0x7F,0xFF,0xD7,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFD,0xDF,0xFB,0x7F, +0xFF,0xFF,0xEF,0xFF,0xFE,0xFB,0xFF,0xFF, 0xBF,0xFE,0x8F,0xFF,0xDF,0xF7,0xFD,0xFD, +0x7F,0xDF,0xF7,0xFD,0x3E,0xDF,0xF5,0xBD, 0xFF,0x7F,0xDF,0xF7,0xFD,0xF7,0xFF,0x9F, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xBE,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFD,0x3F,0xFF,0xDF,0xF7, 0xFD,0xFF,0x7F,0xDF,0xF7,0xFD,0xFF,0xCF, +0x77,0xFC,0xFF,0x5F,0xDF,0xF7,0xFD,0xFF, 0xF4,0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFD,0xFF,0xFF,0xFF,0xEE,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xED,0xFB,0xFF,0xFF,0xBF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xE9,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFB,0xFF,0xFF,0xFF,0xD3,0xFF,0xFF, +0xBF,0x3F,0xFB,0xFF,0xFF,0xFF,0xFB,0xF3, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0xF7, 0xFF,0xFF,0xFF,0xFF,0x17,0xFF,0xFF,0xFF, +0xDF,0xFF,0xFD,0xFF,0xFF,0xFF,0xFF,0xFF, 0xDF,0xDF,0xFF,0xFD,0xFF,0xFF,0xDF,0xF7, +0xFF,0x4F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFE,0xFF,0xFF,0xFF,0xFD, +0xFF,0xFF,0xFF,0xFF,0xFE,0xFF,0x9F,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, +0xFD,0xFF,0xFF,0xFF,0xFF,0xFF,0x7F,0xFF, 0xFF,0xFF,0x7A,0x3F,0xFF,0xFF,0xFF,0xFF, +0xFF,0xFF,0xFF,0x7F,0xFF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xF2, +0x7F,0xFF,0xFB,0xFE,0xFF,0xBF,0xEF,0xF8, 0xFE,0xFF,0xBF,0xFB,0xFE,0xFF,0x8F,0xEC, +0xFB,0xFE,0xFF,0xBF,0xF8,0xF7,0xFE,0xFF, 0xBF,0xEF,0xFB,0xFE,0xFD,0xBF,0xCF,0xEC, +0xFF,0x3F,0xEF,0xDB,0xF8,0xFF,0xBF,0xCF, 0xFF,0xF9,0xFF,0xFF,0xBF,0xFF,0xFB,0xFF, +0xFF,0xFF,0xEF,0xFB,0xDF,0xFF,0xFF,0xFF, 0xFF,0xFF,0xBF,0xFF,0xFF,0xFF,0xBB,0xFF, +0xEF,0xFB,0xFE,0xEF,0xBF,0xEE,0xEB,0xFB, 0xFE,0xFF,0xEF,0xFE,0xEE,0xBF,0xFE,0xEB, +0xFF,0xEF,0xFF,0x17,0xFF,0x7E,0xEB,0xBB, 0xFE,0xBF,0xBE,0xFB,0xEF,0x5B,0xF7,0xBD, +0xFB,0xCF,0xBF,0xBF,0xBB,0xFB,0x7E,0xCC, 0xEF,0xFF + +}; diff --git a/drivers/usb/dabusb.c b/drivers/usb/dabusb.c new file mode 100644 index 000000000000..472f96b6d054 --- /dev/null +++ b/drivers/usb/dabusb.c @@ -0,0 +1,846 @@ +/*****************************************************************************/ + +/* + * dabusb.c -- dab usb driver. + * + * Copyright (C) 1999 Deti Fliegl (deti@fliegl.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * $Id: dabusb.c,v 1.45 2000/01/31 10:23:44 fliegl Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dabusb.h" +#include "dabfirmware.h" + +/* --------------------------------------------------------------------- */ + +#define NRDABUSB 4 + +/*-------------------------------------------------------------------*/ + +static dabusb_t dabusb[NRDABUSB]; +static int buffers = 256; + +/*-------------------------------------------------------------------*/ + +static int dabusb_add_buf_tail (pdabusb_t s, struct list_head *dst, struct list_head *src) +{ + unsigned long flags; + struct list_head *tmp; + int ret = 0; + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (src)) { + // no elements in source buffer + ret = -1; + goto err; + } + tmp = src->next; + list_del (tmp); + list_add_tail (tmp, dst); + + err: spin_unlock_irqrestore (&s->lock, flags); + return ret; +} +/*-------------------------------------------------------------------*/ +#ifdef DEBUG +static void dump_urb (purb_t purb) +{ + dbg("urb :%p", purb); + dbg("next :%p", purb->next); + dbg("dev :%p", purb->dev); + dbg("pipe :%08X", purb->pipe); + dbg("status :%d", purb->status); + dbg("transfer_flags :%08X", purb->transfer_flags); + dbg("transfer_buffer :%p", purb->transfer_buffer); + dbg("transfer_buffer_length:%d", purb->transfer_buffer_length); + dbg("actual_length :%d", purb->actual_length); + dbg("setup_packet :%p", purb->setup_packet); + dbg("start_frame :%d", purb->start_frame); + dbg("number_of_packets :%d", purb->number_of_packets); + dbg("interval :%d", purb->interval); + dbg("error_count :%d", purb->error_count); + dbg("context :%p", purb->context); + dbg("complete :%p", purb->complete); +} +#endif +/*-------------------------------------------------------------------*/ +static int dabusb_cancel_queue (pdabusb_t s, struct list_head *q) +{ + unsigned long flags; + struct list_head *p; + pbuff_t b; + + dbg("dabusb_cancel_queue"); + + spin_lock_irqsave (&s->lock, flags); + + for (p = q->next; p != q; p = p->next) { + b = list_entry (p, buff_t, buff_list); + +#ifdef DEBUG + dump_urb(b->purb); +#endif + usb_unlink_urb (b->purb); + } + spin_unlock_irqrestore (&s->lock, flags); + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_queue (struct list_head *q) +{ + struct list_head *tmp; + struct list_head *p; + pbuff_t b; + + dbg("dabusb_free_queue"); + for (p = q->next; p != q;) { + b = list_entry (p, buff_t, buff_list); + +#ifdef DEBUG + dump_urb(b->purb); +#endif + if (b->purb->transfer_buffer) + kfree (b->purb->transfer_buffer); + usb_free_urb(b->purb); + tmp = p->next; + list_del (p); + kfree (b); + p = tmp; + } + + return 0; +} +/*-------------------------------------------------------------------*/ +static int dabusb_free_buffers (pdabusb_t s) +{ + unsigned long flags; + dbg("dabusb_free_buffers"); + + spin_lock_irqsave(&s->lock, flags); + + dabusb_free_queue (&s->free_buff_list); + dabusb_free_queue (&s->rec_buff_list); + + spin_unlock_irqrestore(&s->lock, flags); + + s->got_mem = 0; + return 0; +} +/*-------------------------------------------------------------------*/ +static void dabusb_iso_complete (purb_t purb) +{ + pbuff_t b = purb->context; + pdabusb_t s = b->s; + int i; + int len; + int dst = 0; + void *buf = purb->transfer_buffer; + + dbg("dabusb_iso_complete"); + + // process if URB was not killed + if (purb->status != -ENOENT) { + unsigned int pipe = usb_rcvisocpipe (purb->dev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (purb->dev, pipe, usb_pipeout (pipe)); + for (i = 0; i < purb->number_of_packets; i++) + if (!purb->iso_frame_desc[i].status) { + len = purb->iso_frame_desc[i].actual_length; + if (len <= pipesize) { + memcpy (buf + dst, buf + purb->iso_frame_desc[i].offset, len); + dst += len; + } + else + err("dabusb_iso_complete: invalid len %d", len); + } + else + warn("dabusb_iso_complete: corrupted packet status: %d", purb->iso_frame_desc[i].status); + if (dst != purb->actual_length) + err("dst!=purb->actual_length:%d!=%d", dst, purb->actual_length); + } + + if (atomic_dec_and_test (&s->pending_io) && !s->remove_pending && s->state != _stopped) { + s->overruns++; + err("overrun (%d)", s->overruns); + } + wake_up (&s->wait); +} +/*-------------------------------------------------------------------*/ +static int dabusb_alloc_buffers (pdabusb_t s) +{ + int buffers = 0; + pbuff_t b; + unsigned int pipe = usb_rcvisocpipe (s->usbdev, _DABUSB_ISOPIPE); + int pipesize = usb_maxpacket (s->usbdev, pipe, usb_pipeout (pipe)); + int packets = _ISOPIPESIZE / pipesize; + int transfer_buffer_length = packets * pipesize; + int i; + + dbg("dabusb_alloc_buffers pipesize:%d packets:%d transfer_buffer_len:%d", + pipesize, packets, transfer_buffer_length); + + while (buffers < (s->total_buffer_size << 10)) { + b = (pbuff_t) kmalloc (sizeof (buff_t), GFP_KERNEL); + if (!b) { + err("kmalloc(sizeof(buff_t))==NULL"); + goto err; + } + memset (b, sizeof (buff_t), 0); + b->s = s; + b->purb = usb_alloc_urb(packets); + if (!b->purb) { + err("usb_alloc_urb == NULL"); + kfree (b); + goto err; + } + + b->purb->transfer_buffer = kmalloc (transfer_buffer_length, GFP_KERNEL); + if (!b->purb->transfer_buffer) { + kfree (b->purb); + kfree (b); + err("kmalloc(%d)==NULL", transfer_buffer_length); + goto err; + } + + b->purb->transfer_buffer_length = transfer_buffer_length; + b->purb->number_of_packets = packets; + b->purb->complete = dabusb_iso_complete; + b->purb->context = b; + b->purb->dev = s->usbdev; + b->purb->pipe = pipe; + b->purb->transfer_flags = USB_ISO_ASAP; + + for (i = 0; i < packets; i++) { + b->purb->iso_frame_desc[i].offset = i * pipesize; + b->purb->iso_frame_desc[i].length = pipesize; + } + + buffers += transfer_buffer_length; + list_add_tail (&b->buff_list, &s->free_buff_list); + } + s->got_mem = buffers; + + return 0; + + err: + dabusb_free_buffers (s); + return -ENOMEM; +} +/*-------------------------------------------------------------------*/ +static int dabusb_bulk (pdabusb_t s, pbulk_transfer_t pb) +{ + int ret; + unsigned int pipe; + int actual_length; + + dbg("dabusb_bulk"); + + if (!pb->pipe) + pipe = usb_rcvbulkpipe (s->usbdev, 2); + else + pipe = usb_sndbulkpipe (s->usbdev, 2); + + ret=usb_bulk_msg(s->usbdev, pipe, pb->data, pb->size, &actual_length, 1000); + if(ret<0) { + err("dabusb: usb_bulk_msg failed(%d)",ret); + } + + if( ret == -EPIPE ) { + warn("CLEAR_FEATURE request to remove STALL condition."); + if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) + err("request failed"); + } + + pb->size = actual_length; + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_writemem (pdabusb_t s, int pos, unsigned char *data, int len) +{ + int ret; + unsigned char *transfer_buffer = kmalloc (len, GFP_KERNEL); + + if (!transfer_buffer) { + err("dabusb_writemem: kmalloc(%d) failed.", len); + return -ENOMEM; + } + + memcpy (transfer_buffer, data, len); + + ret=usb_control_msg(s->usbdev, usb_sndctrlpipe( s->usbdev, 0 ), 0xa0, 0x40, pos, 0, transfer_buffer, len, 300); + + kfree (transfer_buffer); + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_8051_reset (pdabusb_t s, unsigned char reset_bit) +{ + dbg("dabusb_8051_reset: %d",reset_bit); + return dabusb_writemem (s, CPUCS_REG, &reset_bit, 1); +} +/* --------------------------------------------------------------------- */ +static int dabusb_loadmem (pdabusb_t s, const char *fname) +{ + int ret; + PINTEL_HEX_RECORD ptr = firmware; + + dbg("Enter dabusb_loadmem (internal)"); + + ret = dabusb_8051_reset (s, 1); + while (ptr->Type == 0) { + + dbg("dabusb_writemem: %04X %p %d)", ptr->Address, ptr->Data, ptr->Length); + + ret = dabusb_writemem (s, ptr->Address, ptr->Data, ptr->Length); + if (ret < 0) { + err("dabusb_writemem failed (%d %04X %p %d)", ret, ptr->Address, ptr->Data, ptr->Length); + break; + } + ptr++; + } + ret = dabusb_8051_reset (s, 0); + + dbg("dabusb_loadmem: exit"); + + return ret; +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_clear (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2a; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_clear"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_init (pdabusb_t s, pbulk_transfer_t b) +{ + b->size = 4; + b->data[0] = 0x2c; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 0; + + dbg("dabusb_fpga_init"); + + return dabusb_bulk (s, b); +} +/* --------------------------------------------------------------------- */ +static int dabusb_fpga_download (pdabusb_t s, const char *fname) +{ + pbulk_transfer_t b = kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + unsigned int blen, n; + int ret; + unsigned char *buf = bitstream; + + dbg("Enter dabusb_fpga_download (internal)"); + + if (!b) { + err("kmalloc(sizeof(bulk_transfer_t))==NULL"); + return -ENOMEM; + } + + b->pipe = 1; + ret = dabusb_fpga_clear (s, b); + mdelay (10); + blen = buf[73] + (buf[72] << 8); + + dbg("Bitstream len: %i", blen); + + b->data[0] = 0x2b; + b->data[1] = 0; + b->data[2] = 0; + b->data[3] = 60; + + for (n = 0; n <= blen + 60; n += 60) { + // some cclks for startup + b->size = 64; + memcpy (b->data + 4, buf + 74 + n, 60); + ret = dabusb_bulk (s, b); + if (ret < 0) { + err("dabusb_bulk failed."); + break; + } + mdelay (1); + } + + ret = dabusb_fpga_init (s, b); + kfree (b); + + dbg("exit dabusb_fpga_download"); + + return ret; +} + +static loff_t dabusb_llseek (struct file *file, loff_t offset, int origin) +{ + return -ESPIPE; +} + +static int dabusb_stop (pdabusb_t s) +{ + dbg("dabusb_stop"); + + s->state = _stopped; + dabusb_cancel_queue (s, &s->rec_buff_list); + + dbg("pending_io: %d", s->pending_io.counter); + + s->pending_io.counter = 0; + return 0; +} + +static int dabusb_startrek (pdabusb_t s) +{ + if (!s->got_mem && s->state != _started) { + + dbg("dabusb_startrek"); + + if (dabusb_alloc_buffers (s) < 0) + return -ENOMEM; + dabusb_stop (s); + s->state = _started; + s->readptr = 0; + } + + if (!list_empty (&s->free_buff_list)) { + pbuff_t end; + int ret; + + while (!dabusb_add_buf_tail (s, &s->rec_buff_list, &s->free_buff_list)) { + + dbg("submitting: end:%p s->rec_buff_list:%p", s->rec_buff_list.prev, &s->rec_buff_list); + + end = list_entry (s->rec_buff_list.prev, buff_t, buff_list); + + ret = usb_submit_urb (end->purb); + if (ret) { + err("usb_submit_urb returned:%d", ret); + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("startrek: dabusb_add_buf_tail failed"); + break; + } + else + atomic_inc (&s->pending_io); + } + dbg("pending_io: %d",s->pending_io.counter); + } + + return 0; +} + +static ssize_t dabusb_read (struct file *file, char *buf, size_t count, loff_t * ppos) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + unsigned long flags; + unsigned ret = 0; + int rem; + int cnt; + pbuff_t b; + purb_t purb = NULL; + + dbg("dabusb_read"); + + if (*ppos) + return -ESPIPE; + + if (s->remove_pending) + return -EIO; + + + if (!s->usbdev) + return -EIO; + + while (count > 0) { + dabusb_startrek (s); + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + + spin_unlock_irqrestore(&s->lock, flags); + + err("error: rec_buf_list is empty"); + goto err; + } + + b = list_entry (s->rec_buff_list.next, buff_t, buff_list); + purb = b->purb; + + spin_unlock_irqrestore(&s->lock, flags); + + if (purb->status == -EINPROGRESS) { + if (file->f_flags & O_NONBLOCK) // return nonblocking + { + if (!ret) + ret = -EAGAIN; + goto err; + } + + interruptible_sleep_on (&s->wait); + + if (signal_pending (current)) { + if (!ret) + ret = -ERESTARTSYS; + goto err; + } + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (&s->rec_buff_list)) { + spin_unlock_irqrestore(&s->lock, flags); + err("error: still no buffer available."); + goto err; + } + spin_unlock_irqrestore(&s->lock, flags); + s->readptr = 0; + } + if (s->remove_pending) { + ret = -EIO; + goto err; + } + + rem = purb->actual_length - s->readptr; // set remaining bytes to copy + + if (count >= rem) + cnt = rem; + else + cnt = count; + + dbg("copy_to_user:%p %p %d",buf, purb->transfer_buffer + s->readptr, cnt); + + if (copy_to_user (buf, purb->transfer_buffer + s->readptr, cnt)) { + err("read: copy_to_user failed"); + if (!ret) + ret = -EFAULT; + goto err; + } + + s->readptr += cnt; + count -= cnt; + buf += cnt; + ret += cnt; + + if (s->readptr == purb->actual_length) { + // finished, take next buffer + if (dabusb_add_buf_tail (s, &s->free_buff_list, &s->rec_buff_list)) + err("read: dabusb_add_buf_tail failed"); + s->readptr = 0; + } + } + err: //up(&s->mutex); + return ret; +} + +static int dabusb_open (struct inode *inode, struct file *file) +{ + int devnum = MINOR (inode->i_rdev); + pdabusb_t s; + + if (devnum < DABUSB_MINOR || devnum >= (DABUSB_MINOR + NRDABUSB)) + return -EIO; + + s = &dabusb[devnum - DABUSB_MINOR]; + + dbg("dabusb_open"); + down (&s->mutex); + + while (!s->usbdev || s->opened) { + up (&s->mutex); + + if (file->f_flags & O_NONBLOCK) { + return -EBUSY; + } + schedule_timeout (HZ / 2); + + if (signal_pending (current)) { + return -EAGAIN; + } + down (&s->mutex); + } + if (usb_set_interface (s->usbdev, _DABUSB_IF, 1) < 0) { + err("set_interface failed"); + return -EINVAL; + } + s->opened = 1; + up (&s->mutex); + + file->f_pos = 0; + file->private_data = s; + + return 0; +} + +static int dabusb_release (struct inode *inode, struct file *file) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + + dbg("dabusb_release"); + + down (&s->mutex); + dabusb_stop (s); + dabusb_free_buffers (s); + up (&s->mutex); + + if (!s->remove_pending) { + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) + err("set_interface failed"); + } + else + wake_up (&s->remove_ok); + + s->opened = 0; + return 0; +} + +static int dabusb_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + pdabusb_t s = (pdabusb_t) file->private_data; + pbulk_transfer_t pbulk; + int ret = 0; + int version = DABUSB_VERSION; + + dbg("dabusb_ioctl"); + + if (s->remove_pending) + return -EIO; + + down (&s->mutex); + + if (!s->usbdev) { + up (&s->mutex); + return -EIO; + } + + switch (cmd) { + + case IOCTL_DAB_BULK: + pbulk = (pbulk_transfer_t) kmalloc (sizeof (bulk_transfer_t), GFP_KERNEL); + + if (!pbulk) { + ret = -ENOMEM; + break; + } + + if (copy_from_user (pbulk, (void *) arg, sizeof (bulk_transfer_t))) { + ret = -EFAULT; + kfree (pbulk); + break; + } + + dabusb_bulk (s, pbulk); + ret = copy_to_user ((void *) arg, pbulk, sizeof (bulk_transfer_t)); + kfree (pbulk); + break; + + case IOCTL_DAB_OVERRUNS: + ret = put_user (s->overruns, (unsigned int *) arg); + break; + + case IOCTL_DAB_VERSION: + ret = put_user (version, (unsigned int *) arg); + break; + + default: + ret = -ENOIOCTLCMD; + break; + } + up (&s->mutex); + return ret; +} + +static struct file_operations dabusb_fops = +{ + llseek: dabusb_llseek, + read: dabusb_read, + ioctl: dabusb_ioctl, + open: dabusb_open, + release: dabusb_release, +}; + +static int dabusb_find_struct (void) +{ + int u; + + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + if (!s->usbdev) + return u; + } + return -1; +} + +/* --------------------------------------------------------------------- */ +static void *dabusb_probe (struct usb_device *usbdev, unsigned int ifnum) +{ + int devnum; + pdabusb_t s; + + dbg("dabusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); + + /* the 1234:5678 is just a self assigned test ID */ + if ((usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x2131) && + (usbdev->descriptor.idVendor != 0x0547 || usbdev->descriptor.idProduct != 0x9999)) + return NULL; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return NULL; + + if (ifnum != _DABUSB_IF && usbdev->descriptor.idProduct == 0x9999) + return NULL; + + devnum = dabusb_find_struct (); + if (devnum == -1) + return NULL; + + s = &dabusb[devnum]; + + down (&s->mutex); + s->remove_pending = 0; + s->usbdev = usbdev; + + if (usb_set_configuration (usbdev, usbdev->config[0].bConfigurationValue) < 0) { + err("set_configuration failed"); + goto reject; + } + if (usbdev->descriptor.idProduct == 0x2131) { + dabusb_loadmem (s, NULL); + goto reject; + } + else { + dabusb_fpga_download (s, NULL); + + if (usb_set_interface (s->usbdev, _DABUSB_IF, 0) < 0) { + err("set_interface failed"); + goto reject; + } + } + dbg("bound to interface: %d", ifnum); + up (&s->mutex); + MOD_INC_USE_COUNT; + return s; + + reject: + up (&s->mutex); + s->usbdev = NULL; + return NULL; +} + +static void dabusb_disconnect (struct usb_device *usbdev, void *ptr) +{ + pdabusb_t s = (pdabusb_t) ptr; + + dbg("dabusb_disconnect"); + + s->remove_pending = 1; + wake_up (&s->wait); + if (s->state == _started) + sleep_on (&s->remove_ok); + s->usbdev = NULL; + s->overruns = 0; + MOD_DEC_USE_COUNT; +} + +static struct usb_driver dabusb_driver = +{ + "dabusb", + dabusb_probe, + dabusb_disconnect, + {NULL, NULL}, + &dabusb_fops, + DABUSB_MINOR +}; + +/* --------------------------------------------------------------------- */ + +int __init dabusb_init (void) +{ + unsigned u; + + /* register misc device */ + if (usb_register(&dabusb_driver)) + return -1; + + /* initialize struct */ + for (u = 0; u < NRDABUSB; u++) { + pdabusb_t s = &dabusb[u]; + memset (s, 0, sizeof (dabusb_t)); + init_MUTEX (&s->mutex); + s->usbdev = NULL; + s->total_buffer_size = buffers; + init_waitqueue_head (&s->wait); + init_waitqueue_head (&s->remove_ok); + spin_lock_init (&s->lock); + INIT_LIST_HEAD (&s->free_buff_list); + INIT_LIST_HEAD (&s->rec_buff_list); + } + + dbg("dabusb_init: driver registered"); + return 0; +} + +void __exit dabusb_cleanup (void) +{ + dbg("dabusb_cleanup"); + + usb_deregister (&dabusb_driver); +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de"); +MODULE_DESCRIPTION ("DAB-USB Interface Driver for Linux (c)1999"); +MODULE_PARM (buffers, "i"); + +int __init init_module (void) +{ + return dabusb_init (); +} + +void __exit cleanup_module (void) +{ + dabusb_cleanup (); +} + +#endif + +/* --------------------------------------------------------------------- */ diff --git a/drivers/usb/dabusb.h b/drivers/usb/dabusb.h new file mode 100644 index 000000000000..82877003c008 --- /dev/null +++ b/drivers/usb/dabusb.h @@ -0,0 +1,84 @@ +#define _BULK_DATA_LEN 64 +typedef struct +{ + unsigned char data[_BULK_DATA_LEN]; + unsigned int size; + unsigned int pipe; +}bulk_transfer_t,*pbulk_transfer_t; + +#define DABUSB_MINOR 240 /* some unassigned USB minor */ +#define DABUSB_VERSION 0x1000 +#define IOCTL_DAB_BULK _IOWR('d', 0x30, bulk_transfer_t) +#define IOCTL_DAB_OVERRUNS _IOR('d', 0x15, int) +#define IOCTL_DAB_VERSION _IOR('d', 0x3f, int) + +#ifdef __KERNEL__ + +typedef enum { _stopped=0, _started } driver_state_t; + +typedef struct +{ + struct semaphore mutex; + struct usb_device *usbdev; + wait_queue_head_t wait; + wait_queue_head_t remove_ok; + spinlock_t lock; + volatile atomic_t pending_io; + driver_state_t state; + int remove_pending; + int got_mem; + int total_buffer_size; + unsigned int overruns; + int readptr; + int opened; + struct list_head free_buff_list; + struct list_head rec_buff_list; +} dabusb_t,*pdabusb_t; + +typedef struct +{ + pdabusb_t s; + purb_t purb; + struct list_head buff_list; +} buff_t,*pbuff_t; + +typedef struct +{ + wait_queue_head_t wait; +} bulk_completion_context_t, *pbulk_completion_context_t; + + +#define _DABUSB_IF 2 +#define _DABUSB_ISOPIPE 0x09 +#define _ISOPIPESIZE 16384 + +#define _BULK_DATA_LEN 64 +// Vendor specific request code for Anchor Upload/Download +// This one is implemented in the core +#define ANCHOR_LOAD_INTERNAL 0xA0 + +// EZ-USB Control and Status Register. Bit 0 controls 8051 reset +#define CPUCS_REG 0x7F92 +#define _TOTAL_BUFFERS 384 + +#define MAX_INTEL_HEX_RECORD_LENGTH 16 + +#ifndef _BYTE_DEFINED +#define _BYTE_DEFINED +typedef unsigned char BYTE; +#endif // !_BYTE_DEFINED + +#ifndef _WORD_DEFINED +#define _WORD_DEFINED +typedef unsigned short WORD; +#endif // !_WORD_DEFINED + +typedef struct _INTEL_HEX_RECORD +{ + BYTE Length; + WORD Address; + BYTE Type; + BYTE Data[MAX_INTEL_HEX_RECORD_LENGTH]; +} INTEL_HEX_RECORD, *PINTEL_HEX_RECORD; + +#endif diff --git a/drivers/usb/dc2xx.c b/drivers/usb/dc2xx.c new file mode 100644 index 000000000000..2f693c7de955 --- /dev/null +++ b/drivers/usb/dc2xx.c @@ -0,0 +1,475 @@ +/* + * Copyright (C) 1999-2000 by David Brownell + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * USB driver for Kodak DC-2XX series digital still cameras + * + * The protocol here is the same as the one going over a serial line, but + * it uses USB for speed. Set up /dev/kodak, get gphoto (www.gphoto.org), + * and have fun! + * + * This should also work for a number of other digital (non-Kodak) cameras, + * by adding the vendor and product IDs to the table below. They'll need + * to be the sort using USB just as a fast bulk data channel. + */ + +/* + * HISTORY + * + * 26 August, 1999 -- first release (0.1), works with my DC-240. + * The DC-280 (2Mpixel) should also work, but isn't tested. + * If you use gphoto, make sure you have the USB updates. + * Lives in a 2.3.14 or so Linux kernel, in drivers/usb. + * 31 August, 1999 -- minor update to recognize DC-260 and handle + * its endpoints being in a different order. Note that as + * of gPhoto 0.36pre, the USB updates are integrated. + * 12 Oct, 1999 -- handle DC-280 interface class (0xff not 0x0); + * added timeouts to bulk_msg calls. Minor updates, docs. + * 03 Nov, 1999 -- update for 2.3.25 kernel API changes. + * 08 Jan, 2000 .. multiple camera support + * + * Thanks to: the folk who've provided USB product IDs, sent in + * patches, and shared their sucesses! + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef DEBUG +#include + + + +/* current USB framework handles max of 16 USB devices per driver */ +#define MAX_CAMERAS 8 + +/* USB char devs use USB_MAJOR and from USB_CAMERA_MINOR_BASE up */ +#define USB_CAMERA_MINOR_BASE 80 + + +// XXX remove packet size limit, now that bulk transfers seem fixed + +/* Application protocol limit is 0x8002; USB has disliked that limit! */ +#define MAX_PACKET_SIZE 0x2000 /* e.g. image downloading */ + +#define MAX_READ_RETRY 5 /* times to retry reads */ +#define MAX_WRITE_RETRY 5 /* times to retry writes */ +#define RETRY_TIMEOUT (HZ) /* sleep between retries */ + + +/* table of cameras that work through this driver */ +static const struct camera { + unsigned short idVendor; + unsigned short idProduct; + /* plus hooks for camera-specific info if needed */ +} cameras [] = { + /* These have the same application level protocol */ + { 0x040a, 0x0120 }, // Kodak DC-240 + { 0x040a, 0x0130 }, // Kodak DC-280 + + /* These have a different application level protocol which + * is part of the Flashpoint "DigitaOS". That supports some + * non-camera devices, and some non-Kodak cameras. + */ + { 0x040a, 0x0100 }, // Kodak DC-220 + { 0x040a, 0x0110 }, // Kodak DC-260 + { 0x040a, 0x0111 }, // Kodak DC-265 + { 0x040a, 0x0112 }, // Kodak DC-290 + { 0xf003, 0x6002 }, // HP PhotoSmart C500 + + /* Other USB devices may well work here too, so long as they + * just stick to half duplex bulk packet exchanges. That + * means, among other things, no iso or interrupt endpoints. + */ +}; + + +struct camera_state { + struct usb_device *dev; /* USB device handle */ + int inEP; /* read endpoint */ + int outEP; /* write endpoint */ + const struct camera *info; /* DC-240, etc */ + int subminor; /* which minor dev #? */ + int isActive; /* I/O taking place? */ + + /* this is non-null iff the device is open */ + char *buf; /* buffer for I/O */ + + /* always valid */ + wait_queue_head_t wait; /* for timed waits */ +}; + + +/* Support multiple cameras, possibly of different types. */ +static struct camera_state *minor_data [MAX_CAMERAS]; + + +static ssize_t camera_read (struct file *file, + char *buf, size_t len, loff_t *ppos) +{ + struct camera_state *camera; + int retries; + + if (len > MAX_PACKET_SIZE) + return -EINVAL; + + camera = (struct camera_state *) file->private_data; + if (!camera->dev) + return -ENODEV; + if (camera->isActive++) + return -EBUSY; + + /* Big reads are common, for image downloading. Smaller ones + * are also common (even "directory listing" commands don't + * send very much data). We preserve packet boundaries here, + * they matter in the application protocol. + */ + for (retries = 0; retries < MAX_READ_RETRY; retries++) { + int count; + int result; + + if (signal_pending (current)) { + camera->isActive = 0; + return -EINTR; + } + if (!camera->dev) { + camera->isActive = 0; + return -ENODEV; + } + + result = usb_bulk_msg (camera->dev, + usb_rcvbulkpipe (camera->dev, camera->inEP), + camera->buf, len, &count, HZ*10); + + dbg ("read (%d) - 0x%x %d", len, result, count); + + if (!result) { + if (copy_to_user (buf, camera->buf, count)) + return -EFAULT; + camera->isActive = 0; + return count; + } + if (result != USB_ST_TIMEOUT) + break; + interruptible_sleep_on_timeout (&camera->wait, RETRY_TIMEOUT); + + dbg ("read (%d) - retry", len); + } + camera->isActive = 0; + return -EIO; +} + +static ssize_t camera_write (struct file *file, + const char *buf, size_t len, loff_t *ppos) +{ + struct camera_state *camera; + ssize_t bytes_written = 0; + + if (len > MAX_PACKET_SIZE) + return -EINVAL; + + camera = (struct camera_state *) file->private_data; + if (!camera->dev) + return -ENODEV; + if (camera->isActive++) + return -EBUSY; + + /* most writes will be small: simple commands, sometimes with + * parameters. putting images (like borders) into the camera + * would be the main use of big writes. + */ + while (len > 0) { + char *obuf = camera->buf; + int maxretry = MAX_WRITE_RETRY; + unsigned long copy_size, thistime; + + /* it's not clear that retrying can do any good ... or that + * fragmenting application packets into N writes is correct. + */ + thistime = copy_size = len; + if (copy_from_user (obuf, buf, copy_size)) { + bytes_written = -EFAULT; + break; + } + while (thistime) { + int result; + int count; + + if (signal_pending (current)) { + if (!bytes_written) + bytes_written = -EINTR; + goto done; + } + if (!camera->dev) { + if (!bytes_written) + bytes_written = -ENODEV; + goto done; + } + + result = usb_bulk_msg (camera->dev, + usb_sndbulkpipe (camera->dev, camera->outEP), + obuf, thistime, &count, HZ*10); + + if (result) + dbg ("write USB err - %x", result); + + if (count) { + obuf += count; + thistime -= count; + maxretry = MAX_WRITE_RETRY; + continue; + } else if (!result) + break; + + if (result == USB_ST_TIMEOUT) { /* NAK - delay a bit */ + if (!maxretry--) { + if (!bytes_written) + bytes_written = -ETIME; + goto done; + } + interruptible_sleep_on_timeout (&camera->wait, + RETRY_TIMEOUT); + continue; + } + if (!bytes_written) + bytes_written = -EIO; + goto done; + } + bytes_written += copy_size; + len -= copy_size; + buf += copy_size; + } +done: + camera->isActive = 0; + dbg ("wrote %d", bytes_written); + return bytes_written; +} + +static int camera_open (struct inode *inode, struct file *file) +{ + struct camera_state *camera; + int subminor; + + subminor = MINOR (inode->i_rdev) - USB_CAMERA_MINOR_BASE; + if (subminor < 0 || subminor >= MAX_CAMERAS + || !(camera = minor_data [subminor])) { + return -ENODEV; + } + + if (!(camera->buf = (char *) kmalloc (MAX_PACKET_SIZE, GFP_KERNEL))) { + return -ENOMEM; + } + + dbg ("open"); + + camera->isActive = 0; + file->private_data = camera; + return 0; +} + +static int camera_release (struct inode *inode, struct file *file) +{ + struct camera_state *camera; + + camera = (struct camera_state *) file->private_data; + kfree (camera->buf); + + /* If camera was unplugged with open file ... */ + if (!camera->dev) { + minor_data [camera->subminor] = NULL; + kfree (camera); + } + + dbg ("close"); + + return 0; +} + + /* XXX should define some ioctls to expose camera type + * to applications ... what USB exposes should suffice. + * apps should be able to see the camera type. + */ +static /* const */ struct file_operations usb_camera_fops = { + /* Uses GCC initializer extension; simpler to maintain */ + read: camera_read, + write: camera_write, + open: camera_open, + release: camera_release, +}; + + + +static void * camera_probe(struct usb_device *dev, unsigned int ifnum) +{ + int i; + const struct camera *camera_info = NULL; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + int direction, ep; + struct camera_state *camera; + + /* Is it a supported camera? */ + for (i = 0; i < sizeof (cameras) / sizeof (struct camera); i++) { + if (cameras [i].idVendor != dev->descriptor.idVendor) + continue; + if (cameras [i].idProduct != dev->descriptor.idProduct) + continue; + camera_info = &cameras [i]; + break; + } + if (camera_info == NULL) + return NULL; + + /* these have one config, one interface */ + if (dev->descriptor.bNumConfigurations != 1 + || dev->config[0].bNumInterfaces != 1) { + dbg ("Bogus camera config info"); + return NULL; + } + + /* models differ in how they report themselves */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + if ((interface->bInterfaceClass != USB_CLASS_PER_INTERFACE + && interface->bInterfaceClass != USB_CLASS_VENDOR_SPEC) + || interface->bInterfaceSubClass != 0 + || interface->bInterfaceProtocol != 0 + || interface->bNumEndpoints != 2 + ) { + dbg ("Bogus camera interface info"); + return NULL; + } + + + /* select "subminor" number (part of a minor number) */ + for (i = 0; i < MAX_CAMERAS; i++) { + if (!minor_data [i]) + break; + } + if (i >= MAX_CAMERAS) { + info ("Ignoring additional USB Camera"); + return NULL; + } + + /* allocate & init camera state */ + camera = minor_data [i] = kmalloc (sizeof *camera, GFP_KERNEL); + if (!camera) { + err ("no memory!"); + return NULL; + } + camera->info = camera_info; + camera->subminor = i; + camera->isActive = 0; + camera->buf = NULL; + init_waitqueue_head (&camera->wait); + info ("USB Camera #%d connected", camera->subminor); + + + /* get input and output endpoints (either order) */ + endpoint = interface->endpoint; + camera->outEP = camera->inEP = -1; + + ep = endpoint [0].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + direction = endpoint [0].bEndpointAddress & USB_ENDPOINT_DIR_MASK; + if (direction == USB_DIR_IN) + camera->inEP = ep; + else + camera->outEP = ep; + + ep = endpoint [1].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + direction = endpoint [1].bEndpointAddress & USB_ENDPOINT_DIR_MASK; + if (direction == USB_DIR_IN) + camera->inEP = ep; + else + camera->outEP = ep; + + if (camera->outEP == -1 || camera->inEP == -1 + || endpoint [0].bmAttributes != USB_ENDPOINT_XFER_BULK + || endpoint [1].bmAttributes != USB_ENDPOINT_XFER_BULK + ) { + dbg ("Bogus endpoints"); + goto error; + } + + + if (usb_set_configuration (dev, dev->config[0].bConfigurationValue)) { + err ("Failed usb_set_configuration"); + goto error; + } + + camera->dev = dev; + return camera; + +error: + minor_data [camera->subminor] = NULL; + kfree (camera); + return NULL; +} + +static void camera_disconnect(struct usb_device *dev, void *ptr) +{ + struct camera_state *camera = (struct camera_state *) ptr; + int subminor = camera->subminor; + + /* If camera's not opened, we can clean up right away. + * Else apps see a disconnect on next I/O; the release cleans. + */ + if (!camera->buf) { + minor_data [subminor] = NULL; + kfree (camera); + } else + camera->dev = NULL; + + info ("USB Camera #%d disconnected", subminor); +} + +static /* const */ struct usb_driver camera_driver = { + "dc2xx", + camera_probe, + camera_disconnect, + { NULL, NULL }, + &usb_camera_fops, + USB_CAMERA_MINOR_BASE +}; + + +int __init usb_dc2xx_init(void) +{ + if (usb_register (&camera_driver) < 0) + return -1; + return 0; +} + +void __exit usb_dc2xx_cleanup(void) +{ + usb_deregister (&camera_driver); +} + + +MODULE_AUTHOR("David Brownell, david-b@pacbell.net"); +MODULE_DESCRIPTION("USB Camera Driver for Kodak DC-2xx series cameras"); + +module_init (usb_dc2xx_init); +module_exit (usb_dc2xx_cleanup); diff --git a/drivers/usb/devices.c b/drivers/usb/devices.c new file mode 100644 index 000000000000..c99f86973278 --- /dev/null +++ b/drivers/usb/devices.c @@ -0,0 +1,526 @@ +/* + * devices.c + * (C) Copyright 1999 Randy Dunlap. + * (C) Copyright 1999,2000 Thomas Sailer . (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * $id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ************************************************************* + * + * /devices contains USB topology, device, config, class, + * interface, & endpoint data. + * + * I considered using /proc/bus/usb/devices/device# for each device + * as it is attached or detached, but I didn't like this for some + * reason -- maybe it's just too deep of a directory structure. + * I also don't like looking in multiple places to gather and view + * the data. Having only one file for ./devices also prevents race + * conditions that could arise if a program was reading device info + * for devices that are being removed (unplugged). (That is, the + * program may find a directory for devnum_12 then try to open it, + * but it was just unplugged, so the directory is now deleted. + * But programs would just have to be prepared for situations like + * this in any plug-and-play environment.) + * + * 1999-12-16: Thomas Sailer + * Converted the whole proc stuff to real + * read methods. Now not the whole device list needs to fit + * into one page, only the device list for one bus. + * Added a poll method to /proc/bus/usb/devices, to wake + * up an eventual usbd + * 2000-01-04: Thomas Sailer + * Turned into its own filesystem + * + * $Id: devices.c,v 1.5 2000/01/11 13:58:21 tom Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define MAX_TOPO_LEVEL 6 + +/* Define ALLOW_SERIAL_NUMBER if you want to see the serial number of devices */ +#define ALLOW_SERIAL_NUMBER + +static char *format_topo = +/* T: Bus=dd Lev=dd Prnt=dd Port=dd Cnt=dd Dev#=ddd Spd=ddd MxCh=dd */ + "T: Bus=%2.2d Lev=%2.2d Prnt=%2.2d Port=%2.2d Cnt=%2.2d Dev#=%3d Spd=%3s MxCh=%2d\n"; + +static char *format_string_manufacturer = +/* S: Manufacturer=xxxx */ + "S: Manufacturer=%.100s\n"; + +static char *format_string_product = +/* S: Product=xxxx */ + "S: Product=%.100s\n"; + +#ifdef ALLOW_SERIAL_NUMBER +static char *format_string_serialnumber = +/* S: SerialNumber=xxxx */ + "S: SerialNumber=%.100s\n"; +#endif + +static char *format_bandwidth = +/* B: Alloc=ddd/ddd us (xx%), #Int=ddd, #Iso=ddd */ + "B: Alloc=%3d/%3d us (%2d%%), #Int=%3d, #Iso=%3d\n"; + +static char *format_device1 = +/* D: Ver=xx.xx Cls=xx(sssss) Sub=xx Prot=xx MxPS=dd #Cfgs=dd */ + "D: Ver=%2x.%02x Cls=%02x(%-5s) Sub=%02x Prot=%02x MxPS=%2d #Cfgs=%3d\n"; + +static char *format_device2 = +/* P: Vendor=xxxx ProdID=xxxx Rev=xx.xx */ + "P: Vendor=%04x ProdID=%04x Rev=%2x.%02x\n"; + +static char *format_config = +/* C: #Ifs=dd Cfg#=dd Atr=xx MPwr=dddmA */ + "C:%c #Ifs=%2d Cfg#=%2d Atr=%02x MxPwr=%3dmA\n"; + +static char *format_iface = +/* I: If#=dd Alt=dd #EPs=dd Cls=xx(sssss) Sub=xx Prot=xx Driver=xxxx*/ + "I: If#=%2d Alt=%2d #EPs=%2d Cls=%02x(%-5s) Sub=%02x Prot=%02x Driver=%s\n"; + +static char *format_endpt = +/* E: Ad=xx(s) Atr=xx(ssss) MxPS=dddd Ivl=dddms */ + "E: Ad=%02x(%c) Atr=%02x(%-4s) MxPS=%4d Ivl=%3dms\n"; + + +/* + * Need access to the driver and USB bus lists. + * extern struct list_head usb_driver_list; + * extern struct list_head usb_bus_list; + * However, these will come from functions that return ptrs to each of them. + */ + +static DECLARE_WAIT_QUEUE_HEAD(deviceconndiscwq); +static unsigned int conndiscevcnt = 0; + +/* this struct stores the poll state for /devices pollers */ +struct usb_device_status { + unsigned int lastev; +}; + +struct class_info { + int class; + char *class_name; +}; + +static const struct class_info clas_info[] = +{ /* max. 5 chars. per name string */ + {USB_CLASS_PER_INTERFACE, ">ifc"}, + {USB_CLASS_AUDIO, "audio"}, + {USB_CLASS_COMM, "comm."}, + {USB_CLASS_HID, "HID"}, + {USB_CLASS_HUB, "hub"}, + {USB_CLASS_PRINTER, "print"}, + {USB_CLASS_MASS_STORAGE, "stor."}, + {USB_CLASS_DATA, "data"}, + {USB_CLASS_VENDOR_SPEC, "vend."}, + {-1, "unk."} /* leave as last */ +}; + +/*****************************************************************/ + +void usbdevfs_conn_disc_event(void) +{ + wake_up(&deviceconndiscwq); + conndiscevcnt++; +} + +static const char *class_decode(const int class) +{ + int ix; + + for (ix = 0; clas_info[ix].class != -1; ix++) + if (clas_info[ix].class == class) + break; + return (clas_info[ix].class_name); +} + +static char *usb_dump_endpoint_descriptor(char *start, char *end, const struct usb_endpoint_descriptor *desc) +{ + char *EndpointType [4] = {"Ctrl", "Isoc", "Bulk", "Int."}; + + if (start > end) + return start; + start += sprintf(start, format_endpt, desc->bEndpointAddress, + (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_CONTROL + ? 'B' : /* bidirectional */ + (desc->bEndpointAddress & USB_DIR_IN) ? 'I' : 'O', + desc->bmAttributes, EndpointType[desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK], + desc->wMaxPacketSize, desc->bInterval); + return start; +} + +static char *usb_dump_endpoint(char *start, char *end, const struct usb_endpoint_descriptor *endpoint) +{ + return usb_dump_endpoint_descriptor(start, end, endpoint); +} + +static char *usb_dump_interface_descriptor(char *start, char *end, const struct usb_interface *iface, int setno) +{ + struct usb_interface_descriptor *desc = &iface->altsetting[setno]; + + if (start > end) + return start; + start += sprintf(start, format_iface, + desc->bInterfaceNumber, + desc->bAlternateSetting, + desc->bNumEndpoints, + desc->bInterfaceClass, + class_decode(desc->bInterfaceClass), + desc->bInterfaceSubClass, + desc->bInterfaceProtocol, + iface->driver ? iface->driver->name : "(none)"); + return start; +} + +static char *usb_dump_interface(char *start, char *end, const struct usb_interface *iface, int setno) +{ + struct usb_interface_descriptor *desc = &iface->altsetting[setno]; + int i; + + start = usb_dump_interface_descriptor(start, end, iface, setno); + for (i = 0; i < desc->bNumEndpoints; i++) { + if (start > end) + return start; + start = usb_dump_endpoint(start, end, desc->endpoint + i); + } + return start; +} + +/* TBD: + * 0. TBDs + * 1. marking active config and ifaces (code lists all, but should mark + * which ones are active, if any) + * 2. add status to each endpoint line + */ + +static char *usb_dump_config_descriptor(char *start, char *end, const struct usb_config_descriptor *desc, int active) +{ + if (start > end) + return start; + start += sprintf(start, format_config, + active ? '*' : ' ', /* mark active/actual/current cfg. */ + desc->bNumInterfaces, + desc->bConfigurationValue, + desc->bmAttributes, + desc->MaxPower * 2); + return start; +} + +static char *usb_dump_config(char *start, char *end, const struct usb_config_descriptor *config, int active) +{ + int i, j; + struct usb_interface *interface; + + if (start > end) + return start; + if (!config) /* getting these some in 2.3.7; none in 2.3.6 */ + return start + sprintf(start, "(null Cfg. desc.)\n"); + start = usb_dump_config_descriptor(start, end, config, active); + for (i = 0; i < config->bNumInterfaces; i++) { + interface = config->interface + i; + if (!interface) + break; + for (j = 0; j < interface->num_altsetting; j++) { + if (start > end) + return start; + start = usb_dump_interface(start, end, interface, j); + } + } + return start; +} + +/* + * Dump the different USB descriptors. + */ +static char *usb_dump_device_descriptor(char *start, char *end, const struct usb_device_descriptor *desc) +{ + if (start > end) + return start; + start += sprintf (start, format_device1, + desc->bcdUSB >> 8, desc->bcdUSB & 0xff, + desc->bDeviceClass, + class_decode (desc->bDeviceClass), + desc->bDeviceSubClass, + desc->bDeviceProtocol, + desc->bMaxPacketSize0, + desc->bNumConfigurations); + if (start > end) + return start; + start += sprintf(start, format_device2, + desc->idVendor, desc->idProduct, + desc->bcdDevice >> 8, desc->bcdDevice & 0xff); + return start; +} + +/* + * Dump the different strings that this device holds. + */ +static char *usb_dump_device_strings (char *start, char *end, struct usb_device *dev) +{ + char *buf; + + if (start > end) + return start; + buf = kmalloc(128, GFP_KERNEL); + if (!buf) + return start; + if (dev->descriptor.iManufacturer) { + if (usb_string(dev, dev->descriptor.iManufacturer, buf, 128) > 0) + start += sprintf(start, format_string_manufacturer, buf); + } + if (start > end) + goto out; + if (dev->descriptor.iProduct) { + if (usb_string(dev, dev->descriptor.iProduct, buf, 128) > 0) + start += sprintf(start, format_string_product, buf); + } + if (start > end) + goto out; +#ifdef ALLOW_SERIAL_NUMBER + if (dev->descriptor.iSerialNumber) { + if (usb_string(dev, dev->descriptor.iSerialNumber, buf, 128) > 0) + start += sprintf(start, format_string_serialnumber, buf); + } +#endif + out: + kfree(buf); + return start; +} + +static char *usb_dump_desc(char *start, char *end, struct usb_device *dev) +{ + int i; + + if (start > end) + return start; + + start = usb_dump_device_descriptor(start, end, &dev->descriptor); + + if (start > end) + return start; + + start = usb_dump_device_strings (start, end, dev); + + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { + if (start > end) + return start; + start = usb_dump_config(start, end, dev->config + i, + (dev->config + i) == dev->actconfig); /* active ? */ + } + return start; +} + + +#ifdef PROC_EXTRA /* TBD: may want to add this code later */ + +static char *usb_dump_hub_descriptor(char *start, char *end, const struct usb_hub_descriptor * desc) +{ + int leng = USB_DT_HUB_NONVAR_SIZE; + unsigned char *ptr = (unsigned char *)desc; + + if (start > end) + return start; + start += sprintf(start, "Interface:"); + while (leng && start <= end) { + start += sprintf(start, " %02x", *ptr); + ptr++; leng--; + } + *start++ = '\n'; + return start; +} + +static char *usb_dump_string(char *start, char *end, const struct usb_device *dev, char *id, int index) +{ + if (start > end) + return start; + start += sprintf(start, "Interface:"); + if (index <= dev->maxstring && dev->stringindex && dev->stringindex[index]) + start += sprintf(start, "%s: %.100s ", id, dev->stringindex[index]); + return start; +} + +#endif /* PROC_EXTRA */ + +/*****************************************************************/ + +static char *usb_device_dump(char *start, char *end, struct usb_device *usbdev, + struct usb_bus *bus, int level, int index, int count) +{ + int chix; + int cnt = 0; + int parent_devnum = 0; + + if (level > MAX_TOPO_LEVEL) + return start; + if (usbdev->parent && usbdev->parent->devnum != -1) + parent_devnum = usbdev->parent->devnum; + /* + * So the root hub's parent is 0 and any device that is + * plugged into the root hub has a parent of 0. + */ + start += sprintf(start, format_topo, bus->busnum, level, parent_devnum, index, count, + usbdev->devnum, usbdev->slow ? "1.5" : "12 ", usbdev->maxchild); + /* + * level = topology-tier level; + * parent_devnum = parent device number; + * index = parent's connector number; + * count = device count at this level + */ + /* If this is the root hub, display the bandwidth information */ + if (level == 0) + start += sprintf(start, format_bandwidth, bus->bandwidth_allocated, + FRAME_TIME_MAX_USECS_ALLOC, + (100 * bus->bandwidth_allocated + FRAME_TIME_MAX_USECS_ALLOC / 2) / FRAME_TIME_MAX_USECS_ALLOC, + bus->bandwidth_int_reqs, bus->bandwidth_isoc_reqs); + start = usb_dump_desc(start, end, usbdev); + if (start > end) + return start + sprintf(start, "(truncated)\n"); + /* Now look at all of this device's children. */ + for (chix = 0; chix < usbdev->maxchild; chix++) { + if (start > end) + return start; + if (usbdev->children[chix]) + start = usb_device_dump(start, end, usbdev->children[chix], bus, level + 1, chix, ++cnt); + } + return start; +} + +static ssize_t usb_device_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +{ + struct list_head *buslist; + struct usb_bus *bus; + char *page, *end; + ssize_t ret = 0; + unsigned int pos, len; + + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_pages(GFP_KERNEL,1))) + return -ENOMEM; + pos = *ppos; + /* enumerate busses */ + for (buslist = usb_bus_list.next; buslist != &usb_bus_list; buslist = buslist->next) { + /* print devices for this bus */ + bus = list_entry(buslist, struct usb_bus, bus_list); + end = usb_device_dump(page, page + (2*PAGE_SIZE - 256), bus->root_hub, bus, 0, 0, 0); + len = end - page; + if (len > pos) { + len -= pos; + if (len > nbytes) + len = nbytes; + if (copy_to_user(buf, page + pos, len)) { + if (!ret) + ret = -EFAULT; + break; + } + nbytes -= len; + buf += len; + ret += len; + pos = 0; + *ppos += len; + } else + pos -= len; + } + free_pages((unsigned long)page, 1); + return ret; +} + +/* Kernel lock for "lastev" protection */ +static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usb_device_status *st = (struct usb_device_status *)file->private_data; + unsigned int mask = 0; + + lock_kernel(); + if (!st) { + st = kmalloc(sizeof(struct usb_device_status), GFP_KERNEL); + if (!st) + return POLLIN; + /* + * need to prevent the module from being unloaded, since + * proc_unregister does not call the release method and + * we would have a memory leak + */ + st->lastev = conndiscevcnt; + file->private_data = st; + mask = POLLIN; + } + if (file->f_mode & FMODE_READ) + poll_wait(file, &deviceconndiscwq, wait); + if (st->lastev != conndiscevcnt) + mask |= POLLIN; + st->lastev = conndiscevcnt; + unlock_kernel(); + return mask; +} + +static int usb_device_open(struct inode *inode, struct file *file) +{ + file->private_data = NULL; + return 0; +} + +static int usb_device_release(struct inode *inode, struct file *file) +{ + if (file->private_data) { + kfree(file->private_data); + file->private_data = NULL; + } + + return 0; +} + +static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +struct file_operations usbdevfs_devices_fops = { + llseek: usb_device_lseek, + read: usb_device_read, + poll: usb_device_poll, + open: usb_device_open, + release: usb_device_release, +}; diff --git a/drivers/usb/devio.c b/drivers/usb/devio.c new file mode 100644 index 000000000000..235385515157 --- /dev/null +++ b/drivers/usb/devio.c @@ -0,0 +1,1201 @@ +/*****************************************************************************/ + +/* + * devio.c -- User space communication with USB devices. + * + * Copyright (C) 1999-2000 Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: devio.c,v 1.7 2000/02/01 17:28:48 fliegl Exp $ + * + * This file implements the usbdevfs/x/y files, where + * x is the bus number and y the device number. + * + * It allows user space programs/"drivers" to communicate directly + * with USB devices without intervening kernel driver. + * + * Revision history + * 22.12.1999 0.1 Initial release (split from proc_usb.c) + * 04.01.2000 0.2 Turned into its own filesystem + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +struct async { + struct list_head asynclist; + struct dev_state *ps; + struct task_struct *task; + unsigned int signr; + void *userbuffer; + void *userurb; + urb_t urb; +}; + +/* + * my own sync control and bulk methods. Here to experiment + * and because the kernel ones set the process to TASK_UNINTERRUPTIBLE. + */ + +struct sync { + wait_queue_head_t wait; +}; + +static void sync_completed(purb_t urb) +{ + struct sync *s = (struct sync *)urb->context; + + wake_up(&s->wait); +} + +static int do_sync(purb_t urb, int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + unsigned long tm; + signed long tmdiff; + struct sync s; + int ret; + + tm = jiffies+timeout; + init_waitqueue_head(&s.wait); + add_wait_queue(&s.wait, &wait); + urb->context = &s; + urb->complete = sync_completed; + set_current_state(TASK_INTERRUPTIBLE); + if ((ret = usb_submit_urb(urb))) + goto out; + while (urb->status == -EINPROGRESS) { + tmdiff = tm - jiffies; + if (tmdiff <= 0) { + ret = -ETIMEDOUT; + goto out; + } + if (signal_pending(current)) { + ret = -EINTR; + goto out; + } + schedule_timeout(tmdiff); + } + ret = urb->status; + out: + set_current_state(TASK_RUNNING); + usb_unlink_urb(urb); + remove_wait_queue(&s.wait, &wait); + return ret; +} + +static int my_usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, + __u16 value, __u16 index, void *data, __u16 size, int timeout) +{ + urb_t *urb; + int ret; + + if (!(urb = usb_alloc_urb(0))) + return -ENOMEM; + if (!(urb->setup_packet = kmalloc(8, GFP_KERNEL))) { + usb_free_urb(urb); + return -ENOMEM; + } + urb->setup_packet[0] = requesttype; + urb->setup_packet[1] = request; + urb->setup_packet[2] = value; + urb->setup_packet[3] = value >> 8; + urb->setup_packet[4] = index; + urb->setup_packet[5] = index >> 8; + urb->setup_packet[6] = size; + urb->setup_packet[7] = size >> 8; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = data; + urb->transfer_buffer_length = size; + ret = do_sync(urb, timeout); + //if (ret >= 0) + // ret = urb->status; + if (ret >= 0) + ret = urb->actual_length; + kfree(urb->setup_packet); + usb_free_urb(urb); + return ret; +} + +static int my_usb_bulk_msg(struct usb_device *dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + urb_t *urb; + int ret; + + if (!(urb = usb_alloc_urb(0))) + return -ENOMEM; + urb->dev = dev; + urb->pipe = pipe; + urb->transfer_buffer = data; + urb->transfer_buffer_length = len; + ret = do_sync(urb, timeout); + //if (ret >= 0) + // ret = urb->status; + if (ret >= 0 && actual_length != NULL) + *actual_length = urb->actual_length; + usb_free_urb(urb); + return ret; +} + +static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +static ssize_t usbdev_read(struct file *file, char * buf, size_t nbytes, loff_t *ppos) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + ssize_t ret = 0; + unsigned len; + loff_t pos; + int i; + + pos = *ppos; + down_read(&ps->devsem); + if (!ps->dev) { + ret = -ENODEV; + goto err; + } else if (pos < 0) { + ret = -EINVAL; + goto err; + } + + if (pos < sizeof(struct usb_device_descriptor)) { + len = sizeof(struct usb_device_descriptor) - pos; + if (len > nbytes) + len = nbytes; + if (copy_to_user(buf, ((char *)&ps->dev->descriptor) + pos, len)) { + ret = -EFAULT; + goto err; + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos = sizeof(struct usb_device_descriptor); + for (i = 0; nbytes && i < ps->dev->descriptor.bNumConfigurations; i++) { + struct usb_config_descriptor *config = + (struct usb_config_descriptor *)ps->dev->rawdescriptors[i]; + unsigned int length = le16_to_cpu(config->wTotalLength); + + if (*ppos < pos + length) { + len = length - (*ppos - pos); + if (len > nbytes) + len = nbytes; + + if (copy_to_user(buf, + ps->dev->rawdescriptors[i] + (*ppos - pos), len)) { + ret = -EFAULT; + goto err; + } + + *ppos += len; + buf += len; + nbytes -= len; + ret += len; + } + + pos += length; + } + +err: + up_read(&ps->devsem); + return ret; +} + +extern inline unsigned int ld2(unsigned int x) +{ + unsigned int r = 0; + + if (x >= 0x10000) { + x >>= 16; + r += 16; + } + if (x >= 0x100) { + x >>= 8; + r += 8; + } + if (x >= 0x10) { + x >>= 4; + r += 4; + } + if (x >= 4) { + x >>= 2; + r += 2; + } + if (x >= 2) + r++; + return r; +} + +/* + * async list handling + */ + +static struct async *alloc_async(unsigned int numisoframes) +{ + unsigned int assize = sizeof(struct async) + numisoframes * sizeof(iso_packet_descriptor_t); + struct async *as = kmalloc(assize, GFP_KERNEL); + if (!as) + return NULL; + memset(as, 0, assize); + as->urb.number_of_packets = numisoframes; + return as; +} + +static void free_async(struct async *as) +{ + if (as->urb.transfer_buffer) + kfree(as->urb.transfer_buffer); + kfree(as); +} + +extern __inline__ void async_newpending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_add_tail(&as->asynclist, &ps->async_pending); + spin_unlock_irqrestore(&ps->lock, flags); +} + +extern __inline__ void async_removepending(struct async *as) +{ + struct dev_state *ps = as->ps; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); +} + +extern __inline__ struct async *async_getcompleted(struct dev_state *ps) +{ + unsigned long flags; + struct async *as = NULL; + + spin_lock_irqsave(&ps->lock, flags); + if (!list_empty(&ps->async_completed)) { + as = list_entry(ps->async_completed.next, struct async, asynclist); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + } + spin_unlock_irqrestore(&ps->lock, flags); + return as; +} + +extern __inline__ struct async *async_getpending(struct dev_state *ps, void *userurb) +{ + unsigned long flags; + struct async *as; + struct list_head *p; + + spin_lock_irqsave(&ps->lock, flags); + for (p = ps->async_pending.next; p != &ps->async_pending; ) { + as = list_entry(p, struct async, asynclist); + p = p->next; + if (as->userurb != userurb) + continue; + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + return as; + } + spin_unlock_irqrestore(&ps->lock, flags); + return NULL; +} + +static void async_completed(purb_t urb) +{ + struct async *as = (struct async *)urb->context; + struct dev_state *ps = as->ps; + struct siginfo sinfo; + +#if 1 + printk(KERN_DEBUG "usbdevfs: async_completed: status %d errcount %d actlen %d pipe 0x%x\n", + urb->status, urb->error_count, urb->actual_length, urb->pipe); +#endif + spin_lock(&ps->lock); + list_del(&as->asynclist); + list_add_tail(&as->asynclist, &ps->async_completed); + spin_unlock(&ps->lock); + wake_up(&ps->wait); + if (as->signr) { + sinfo.si_signo = as->signr; + sinfo.si_errno = as->urb.status; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = as->userurb; + send_sig_info(as->signr, &sinfo, as->task); + } +} + +static void destroy_all_async(struct dev_state *ps) +{ + struct async *as; + unsigned long flags; + + spin_lock_irqsave(&ps->lock, flags); + while (!list_empty(&ps->async_pending)) { + as = list_entry(ps->async_pending.next, struct async, asynclist); + list_del(&as->asynclist); + INIT_LIST_HEAD(&as->asynclist); + spin_unlock_irqrestore(&ps->lock, flags); + /* usb_unlink_urb calls the completion handler with status == USB_ST_URB_KILLED */ + usb_unlink_urb(&as->urb); + spin_lock_irqsave(&ps->lock, flags); + } + spin_unlock_irqrestore(&ps->lock, flags); + while ((as = async_getcompleted(ps))) + free_async(as); +} + +/* + * interface claiming + */ + +static void *driver_probe(struct usb_device *dev, unsigned int intf) +{ + return NULL; +} + +static void driver_disconnect(struct usb_device *dev, void *context) +{ + struct dev_state *ps = (struct dev_state *)context; + + ps->ifclaimed = 0; +} + +struct usb_driver usbdevfs_driver = { + name: "usbdevfs", + probe: driver_probe, + disconnect: driver_disconnect, +}; + +static int claimintf(struct dev_state *ps, unsigned int intf) +{ + struct usb_device *dev = ps->dev; + struct usb_interface *iface; + int err; + + if (intf >= 8*sizeof(ps->ifclaimed) || !dev || intf >= dev->actconfig->bNumInterfaces) + return -EINVAL; + /* already claimed */ + if (test_bit(intf, &ps->ifclaimed)) + return 0; + iface = &dev->actconfig->interface[intf]; + err = -EBUSY; + lock_kernel(); + if (!usb_interface_claimed(iface)) { + usb_driver_claim_interface(&usbdevfs_driver, iface, ps); + set_bit(intf, &ps->ifclaimed); + err = 0; + } + unlock_kernel(); + return err; +} + +static int releaseintf(struct dev_state *ps, unsigned int intf) +{ + struct usb_device *dev; + struct usb_interface *iface; + int err; + + if (intf >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + err = -EINVAL; + lock_kernel(); + dev = ps->dev; + if (dev && test_and_clear_bit(intf, &ps->ifclaimed)) { + iface = &dev->actconfig->interface[intf]; + usb_driver_release_interface(&usbdevfs_driver, iface); + err = 0; + } + unlock_kernel(); + return err; +} + +static int checkintf(struct dev_state *ps, unsigned int intf) +{ + if (intf >= 8*sizeof(ps->ifclaimed)) + return -EINVAL; + if (test_bit(intf, &ps->ifclaimed)) + return 0; + /* if not yet claimed, claim it for the driver */ + printk(KERN_WARNING "usbdevfs: process %d (%s) did not claim interface %u before use\n", + current->pid, current->comm, intf); + return claimintf(ps, intf); +} + +static int findintfep(struct usb_device *dev, unsigned int ep) +{ + unsigned int i, j, e; + struct usb_interface *iface; + struct usb_interface_descriptor *alts; + struct usb_endpoint_descriptor *endpt; + + if (ep & ~(USB_DIR_IN|0xf)) + return -EINVAL; + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j < iface->num_altsetting; j++) { + alts = &iface->altsetting[j]; + for (e = 0; e < alts->bNumEndpoints; e++) { + endpt = &alts->endpoint[e]; + if (endpt->bEndpointAddress == ep) + return i; + } + } + } + return -ENOENT; +} + +static int findintfif(struct usb_device *dev, unsigned int ifn) +{ + unsigned int i, j; + struct usb_interface *iface; + struct usb_interface_descriptor *alts; + + if (ifn & ~0xff) + return -EINVAL; + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + iface = &dev->actconfig->interface[i]; + for (j = 0; j < iface->num_altsetting; j++) { + alts = &iface->altsetting[j]; + if (alts->bInterfaceNumber == ifn) + return i; + } + } + return -ENOENT; +} + +extern struct list_head usb_driver_list; + +static int finddriver(struct usb_driver **driver, char *name) +{ + struct list_head *tmp; + + tmp = usb_driver_list.next; + while (tmp != &usb_driver_list) { + struct usb_driver *d = list_entry(tmp, struct usb_driver, + driver_list); + + if (!strcmp(d->name, name)) { + *driver = d; + return 0; + } + + tmp = tmp->next; + } + + return -EINVAL; +} + +/* + * file operations + */ +static int usbdev_open(struct inode *inode, struct file *file) +{ + struct usb_device *dev; + struct dev_state *ps; + int ret; + + /* + * no locking necessary here, as both sys_open (actually filp_open) + * and the hub thread have the kernel lock + * (still acquire the kernel lock for safety) + */ + lock_kernel(); + ret = -ENOENT; + if (ITYPE(inode->i_ino) != IDEVICE) + goto out; + dev = inode->u.usbdev_i.p.dev; + if (!dev) + goto out; + ret = -ENOMEM; + if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL))) + goto out; + ret = 0; + ps->dev = dev; + ps->file = file; + spin_lock_init(&ps->lock); + INIT_LIST_HEAD(&ps->async_pending); + INIT_LIST_HEAD(&ps->async_completed); + init_waitqueue_head(&ps->wait); + init_rwsem(&ps->devsem); + ps->discsignr = 0; + ps->disctask = current; + ps->disccontext = NULL; + ps->ifclaimed = 0; + wmb(); + list_add_tail(&ps->list, &dev->filelist); + file->private_data = ps; + out: + unlock_kernel(); + return ret; +} + +static int usbdev_release(struct inode *inode, struct file *file) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + unsigned int i; + + lock_kernel(); + list_del(&ps->list); + INIT_LIST_HEAD(&ps->list); + if (ps->dev) { + for (i = 0; ps->ifclaimed && i < 8*sizeof(ps->ifclaimed); i++) + if (test_bit(i, &ps->ifclaimed)) + releaseintf(ps, i); + } + unlock_kernel(); + destroy_all_async(ps); + kfree(ps); + return 0; +} + +static int proc_control(struct dev_state *ps, void *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_ctrltransfer ctrl; + unsigned int tmo; + unsigned char *tbuf; + int i, ret; + + copy_from_user_ret(&ctrl, (void *)arg, sizeof(ctrl), -EFAULT); + switch (ctrl.requesttype & 0x1f) { + case USB_RECIP_ENDPOINT: + if ((ret = findintfep(ps->dev, ctrl.index & 0xff)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + break; + + case USB_RECIP_INTERFACE: + if ((ret = findintfif(ps->dev, ctrl.index & 0xff)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + break; + } + if (ctrl.length > PAGE_SIZE) + return -EINVAL; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + tmo = (ctrl.timeout * HZ + 999) / 1000; + if (ctrl.requesttype & 0x80) { + if (ctrl.length && !access_ok(VERIFY_WRITE, ctrl.data, ctrl.length)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = my_usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); + if ((i > 0) && ctrl.length) { + copy_to_user_ret(ctrl.data, tbuf, ctrl.length, -EFAULT); + } + } else { + if (ctrl.length) { + copy_from_user_ret(tbuf, ctrl.data, ctrl.length, -EFAULT); + } + i = my_usb_control_msg(dev, usb_sndctrlpipe(dev, 0), ctrl.request, ctrl.requesttype, + ctrl.value, ctrl.index, tbuf, ctrl.length, tmo); + } + free_page((unsigned long)tbuf); + if (i<0) { + printk(KERN_DEBUG "usbdevfs: USBDEVFS_CONTROL failed dev %d rqt %u rq %u len %u ret %d\n", + dev->devnum, ctrl.requesttype, ctrl.request, ctrl.length, i); + } + return i; +} + +static int proc_bulk(struct dev_state *ps, void *arg) +{ + struct usb_device *dev = ps->dev; + struct usbdevfs_bulktransfer bulk; + unsigned int tmo, len1, pipe; + int len2; + unsigned char *tbuf; + int i, ret; + + copy_from_user_ret(&bulk, (void *)arg, sizeof(bulk), -EFAULT); + if ((ret = findintfep(ps->dev, bulk.ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + if (bulk.ep & USB_DIR_IN) + pipe = usb_rcvbulkpipe(dev, bulk.ep & 0x7f); + else + pipe = usb_sndbulkpipe(dev, bulk.ep & 0x7f); + if (!usb_maxpacket(dev, pipe, !(bulk.ep & USB_DIR_IN))) + return -EINVAL; + len1 = bulk.len; + if (len1 > PAGE_SIZE) + len1 = PAGE_SIZE; + if (!(tbuf = (unsigned char *)__get_free_page(GFP_KERNEL))) + return -ENOMEM; + tmo = (bulk.timeout * HZ + 999) / 1000; + if (bulk.ep & 0x80) { + if (len1 && !access_ok(VERIFY_WRITE, bulk.data, len1)) { + free_page((unsigned long)tbuf); + return -EINVAL; + } + i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + if (!i && len2) { + copy_to_user_ret(bulk.data, tbuf, len2, -EFAULT); + } + } else { + if (len1) { + copy_from_user_ret(tbuf, bulk.data, len1, -EFAULT); + } + i = my_usb_bulk_msg(dev, pipe, tbuf, len1, &len2, tmo); + } + free_page((unsigned long)tbuf); + if (i < 0) { + printk(KERN_WARNING "usbdevfs: USBDEVFS_BULK failed dev %d ep 0x%x len %u ret %d\n", + dev->devnum, bulk.ep, bulk.len, i); + return i; + } + return len2; +} + +static int proc_resetep(struct dev_state *ps, void *arg) +{ + unsigned int ep; + int ret; + + get_user_ret(ep, (unsigned int *)arg, -EFAULT); + if ((ret = findintfep(ps->dev, ep)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + usb_settoggle(ps->dev, ep & 0xf, !(ep & USB_DIR_IN), 0); + return 0; +} + +static int proc_getdriver(struct dev_state *ps, void *arg) +{ + struct usbdevfs_getdriver gd; + struct usb_interface *interface; + int ret; + + copy_from_user_ret(&gd, arg, sizeof(gd), -EFAULT); + if ((ret = findintfif(ps->dev, gd.interface)) < 0) + return ret; + interface = usb_ifnum_to_if(ps->dev, gd.interface); + if (!interface) + return -EINVAL; + if (!interface->driver) + return -ENODATA; + strcpy(gd.driver, interface->driver->name); + copy_to_user_ret(arg, &gd, sizeof(gd), -EFAULT); + return 0; +} + +static int proc_connectinfo(struct dev_state *ps, void *arg) +{ + struct usbdevfs_connectinfo ci; + + ci.devnum = ps->dev->devnum; + ci.slow = ps->dev->slow; + copy_to_user_ret(arg, &ci, sizeof(ci), -EFAULT); + return 0; +} + +static int proc_resetdevice(struct dev_state *ps) +{ + int i, ret; + + ret = usb_reset_device(ps->dev); + if (ret < 0) + return ret; + + for (i = 0; i < ps->dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = &ps->dev->actconfig->interface[i]; + + /* Don't simulate interfaces we've claimed */ + if (test_bit(i, &ps->ifclaimed)) + continue; + + if (intf->driver) { + down(&intf->driver->serialize); + intf->driver->disconnect(ps->dev, intf->private_data); + intf->driver->probe(ps->dev, i); + up(&intf->driver->serialize); + } + } + + return 0; +} + +static int proc_setintf(struct dev_state *ps, void *arg) +{ + struct usbdevfs_setinterface setintf; + struct usb_interface *interface; + int ret; + + copy_from_user_ret(&setintf, arg, sizeof(setintf), -EFAULT); + if ((ret = findintfif(ps->dev, setintf.interface)) < 0) + return ret; + interface = usb_ifnum_to_if(ps->dev, setintf.interface); + if (!interface) + return -EINVAL; + if (interface->driver) { + if ((ret = checkintf(ps, ret))) + return ret; + } + if (usb_set_interface(ps->dev, setintf.interface, setintf.altsetting)) + return -EINVAL; + return 0; +} + +static int proc_setconfig(struct dev_state *ps, void *arg) +{ + unsigned int u; + + get_user_ret(u, (unsigned int *)arg, -EFAULT); + if (usb_set_configuration(ps->dev, u) < 0) + return -EINVAL; + return 0; +} + +static int proc_submiturb(struct dev_state *ps, void *arg) +{ + struct usbdevfs_urb uurb; + struct usbdevfs_iso_packet_desc *isopkt = NULL; + struct async *as; + unsigned int u, totlen, isofrmlen; + int ret; + + copy_from_user_ret(&uurb, arg, sizeof(uurb), -EFAULT); + if (uurb.flags & ~(USBDEVFS_URB_ISO_ASAP|USBDEVFS_URB_DISABLE_SPD)) + return -EINVAL; + if (!uurb.buffer) + return -EINVAL; + if (uurb.signr != 0 && (uurb.signr < SIGRTMIN || uurb.signr > SIGRTMAX)) + return -EINVAL; + if ((ret = findintfep(ps->dev, uurb.endpoint)) < 0) + return ret; + if ((ret = checkintf(ps, ret))) + return ret; + switch(uurb.type) { + case USBDEVFS_URB_TYPE_BULK: + uurb.number_of_packets = 0; + if (uurb.buffer_length > 16384) + return -EINVAL; + if (!access_ok((uurb.endpoint & USB_DIR_IN) ? VERIFY_WRITE : VERIFY_READ, uurb.buffer, uurb.buffer_length)) + return -EFAULT; + break; + + case USBDEVFS_URB_TYPE_ISO: + /* arbitrary limit */ + if (uurb.number_of_packets < 1 || uurb.number_of_packets > 128) + return -EINVAL; + isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) * uurb.number_of_packets; + if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL))) + return -ENOMEM; + if (copy_from_user(isopkt, &((struct usbdevfs_urb *)arg)->iso_frame_desc, isofrmlen)) { + kfree(isopkt); + return -EFAULT; + } + for (totlen = u = 0; u < uurb.number_of_packets; u++) { + if (isopkt[u].length > 1023) { + kfree(isopkt); + return -EINVAL; + } + totlen += isopkt[u].length; + } + if (totlen > 32768) { + kfree(isopkt); + return -ENOMEM; + } + uurb.buffer_length = totlen; + break; + + default: + return -EINVAL; + } + if (!(as = alloc_async(uurb.number_of_packets))) { + if (isopkt) + kfree(isopkt); + return -ENOMEM; + } + if (!(as->urb.transfer_buffer = kmalloc(uurb.buffer_length, GFP_KERNEL))) { + if (isopkt) + kfree(isopkt); + free_async(as); + return -ENOMEM; + } + as->urb.next = NULL; + as->urb.dev = ps->dev; + as->urb.pipe = (uurb.type << 30) | __create_pipe(ps->dev, uurb.endpoint & 0xf) | (uurb.endpoint & USB_DIR_IN); + as->urb.transfer_flags = uurb.flags; + as->urb.transfer_buffer_length = uurb.buffer_length; + as->urb.start_frame = uurb.start_frame; + as->urb.number_of_packets = uurb.number_of_packets; + as->urb.context = as; + as->urb.complete = async_completed; + for (totlen = u = 0; u < uurb.number_of_packets; u++) { + as->urb.iso_frame_desc[u].offset = totlen; + as->urb.iso_frame_desc[u].length = isopkt[u].length; + totlen += isopkt[u].length; + } + if (isopkt) + kfree(isopkt); + as->ps = ps; + as->userurb = arg; + if (uurb.endpoint & USB_DIR_IN) + as->userbuffer = uurb.buffer; + else + as->userbuffer = NULL; + as->signr = uurb.signr; + as->task = current; + if (!(uurb.endpoint & USB_DIR_IN)) { + if (copy_from_user(as->urb.transfer_buffer, uurb.buffer, as->urb.transfer_buffer_length)) { + free_async(as); + return -EFAULT; + } + } + async_newpending(as); + if ((ret = usb_submit_urb(&as->urb))) { + printk(KERN_DEBUG "usbdevfs: usb_submit_urb returned %d\n", ret); + async_removepending(as); + free_async(as); + return ret; + } + return 0; +} + +static int proc_unlinkurb(struct dev_state *ps, void *arg) +{ + struct async *as; + + as = async_getpending(ps, arg); + if (!as) + return -EINVAL; + usb_unlink_urb(&as->urb); + return 0; +} + +static int processcompl(struct async *as) +{ + unsigned int i; + + if (as->userbuffer) + if (copy_to_user(as->userbuffer, as->urb.transfer_buffer, as->urb.transfer_buffer_length)) + return -EFAULT; + put_user_ret(as->urb.status, &((struct usbdevfs_urb *)as->userurb)->status, -EFAULT); + put_user_ret(as->urb.actual_length, &((struct usbdevfs_urb *)as->userurb)->actual_length, -EFAULT); + put_user_ret(as->urb.error_count, &((struct usbdevfs_urb *)as->userurb)->error_count, -EFAULT); + if (!(usb_pipeisoc(as->urb.pipe))) + return 0; + for (i = 0; i < as->urb.number_of_packets; i++) { + put_user_ret(as->urb.iso_frame_desc[i].actual_length, + &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].actual_length, + -EFAULT); + put_user_ret(as->urb.iso_frame_desc[i].status, + &((struct usbdevfs_urb *)as->userurb)->iso_frame_desc[i].status, + -EFAULT); + } + return 0; +} + +static int proc_reapurb(struct dev_state *ps, void *arg) +{ + DECLARE_WAITQUEUE(wait, current); + struct async *as = NULL; + void *addr; + int ret; + + add_wait_queue(&ps->wait, &wait); + while (ps->dev) { + __set_current_state(TASK_INTERRUPTIBLE); + if ((as = async_getcompleted(ps))) + break; + if (signal_pending(current)) + break; + up_read(&ps->devsem); + schedule(); + down_read(&ps->devsem); + } + remove_wait_queue(&ps->wait, &wait); + set_current_state(TASK_RUNNING); + if (as) { + ret = processcompl(as); + addr = as->userurb; + free_async(as); + if (ret) + return ret; + put_user_ret(addr, (void **)arg, -EFAULT); + return 0; + } + if (signal_pending(current)) + return -EINTR; + return -EIO; +} + +static int proc_reapurbnonblock(struct dev_state *ps, void *arg) +{ + struct async *as; + void *addr; + int ret; + + if (!(as = async_getcompleted(ps))) + return -EAGAIN; + ret = processcompl(as); + addr = as->userurb; + free_async(as); + if (ret) + return ret; + put_user_ret(addr, (void **)arg, -EFAULT); + return 0; +} + +static int proc_disconnectsignal(struct dev_state *ps, void *arg) +{ + struct usbdevfs_disconnectsignal ds; + + copy_from_user_ret(&ds, arg, sizeof(ds), -EFAULT); + if (ds.signr != 0 && (ds.signr < SIGRTMIN || ds.signr > SIGRTMAX)) + return -EINVAL; + ps->discsignr = ds.signr; + ps->disccontext = ds.context; + return 0; +} + +static int proc_claiminterface(struct dev_state *ps, void *arg) +{ + unsigned int intf; + int ret; + + get_user_ret(intf, (unsigned int *)arg, -EFAULT); + if ((ret = findintfif(ps->dev, intf)) < 0) + return ret; + return claimintf(ps, ret); +} + +static int proc_releaseinterface(struct dev_state *ps, void *arg) +{ + unsigned int intf; + int ret; + + get_user_ret(intf, (unsigned int *)arg, -EFAULT); + if ((ret = findintfif(ps->dev, intf)) < 0) + return ret; + return releaseintf(ps, intf); +} + +static int proc_ioctl (struct dev_state *ps, void *arg) +{ + struct usbdevfs_ioctl ctrl; + int size; + void *buf = 0; + int retval = 0; + + /* get input parameters and alloc buffer */ + copy_from_user_ret (&ctrl, (void *) arg, sizeof (ctrl), -EFAULT); + if ((size = _IOC_SIZE (ctrl.ioctl_code)) > 0) { + if ((buf = kmalloc (size, GFP_KERNEL)) == 0) + return -ENOMEM; + if ((_IOC_DIR (ctrl.ioctl_code) & _IOC_WRITE) != 0 + && copy_from_user (buf, ctrl.data, size) != 0) { + kfree (buf); + return -EFAULT; + } else + memset (arg, size, 0); + } + + /* ioctl to device */ + if (ctrl.ifno < 0) { + switch (ctrl.ioctl_code) { + /* access/release token for issuing control messages + * ask a particular driver to bind/unbind, ... etc + */ + } + retval = -ENOSYS; + + /* ioctl to the driver which has claimed a given interface */ + } else { + struct usb_interface *ifp = 0; + if (!ps->dev) + retval = -ENODEV; + else if (ctrl.ifno >= ps->dev->actconfig->bNumInterfaces) + retval = -EINVAL; + else { + if (!(ifp = usb_ifnum_to_if (ps->dev, ctrl.ifno))) + retval = -EINVAL; + else if (ifp->driver == 0 || ifp->driver->ioctl == 0) + retval = -ENOSYS; + } + if (retval == 0) + /* ifno might usefully be passed ... */ + retval = ifp->driver->ioctl (ps->dev, ctrl.ioctl_code, buf); + /* size = min (size, retval)? */ + } + + /* cleanup and return */ + if (retval >= 0 + && (_IOC_DIR (ctrl.ioctl_code) & _IOC_READ) != 0 + && size > 0 + && copy_to_user (ctrl.data, buf, size) != 0) + retval = -EFAULT; + if (buf != 0) + kfree (buf); + return retval; +} + +static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + int ret = -ENOIOCTLCMD; + + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + down_read(&ps->devsem); + if (!ps->dev) { + up_read(&ps->devsem); + return -ENODEV; + } + switch (cmd) { + case USBDEVFS_CONTROL: + ret = proc_control(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_BULK: + ret = proc_bulk(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESETEP: + ret = proc_resetep(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_RESET: + ret = proc_resetdevice(ps); + break; + + case USBDEVFS_GETDRIVER: + ret = proc_getdriver(ps, (void *)arg); + break; + + case USBDEVFS_CONNECTINFO: + ret = proc_connectinfo(ps, (void *)arg); + break; + + case USBDEVFS_SETINTERFACE: + ret = proc_setintf(ps, (void *)arg); + break; + + case USBDEVFS_SETCONFIGURATION: + ret = proc_setconfig(ps, (void *)arg); + break; + + case USBDEVFS_SUBMITURB: + ret = proc_submiturb(ps, (void *)arg); + if (ret >= 0) + inode->i_mtime = CURRENT_TIME; + break; + + case USBDEVFS_DISCARDURB: + ret = proc_unlinkurb(ps, (void *)arg); + break; + + case USBDEVFS_REAPURB: + ret = proc_reapurb(ps, (void *)arg); + break; + + case USBDEVFS_REAPURBNDELAY: + ret = proc_reapurbnonblock(ps, (void *)arg); + break; + + case USBDEVFS_DISCSIGNAL: + ret = proc_disconnectsignal(ps, (void *)arg); + break; + + case USBDEVFS_CLAIMINTERFACE: + ret = proc_claiminterface(ps, (void *)arg); + break; + + case USBDEVFS_RELEASEINTERFACE: + ret = proc_releaseinterface(ps, (void *)arg); + break; + + case USBDEVFS_IOCTL: + ret = proc_ioctl(ps, (void *) arg); + break; + } + up_read(&ps->devsem); + if (ret >= 0) + inode->i_atime = CURRENT_TIME; + return ret; +} + +/* No kernel lock - fine */ +static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait) +{ + struct dev_state *ps = (struct dev_state *)file->private_data; + unsigned int mask = 0; + + poll_wait(file, &ps->wait, wait); + if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed)) + mask |= POLLOUT | POLLWRNORM; + if (!ps->dev) + mask |= POLLERR | POLLHUP; + return mask; +} + +struct file_operations usbdevfs_device_file_operations = { + llseek: usbdev_lseek, + read: usbdev_read, + poll: usbdev_poll, + ioctl: usbdev_ioctl, + open: usbdev_open, + release: usbdev_release, +}; diff --git a/drivers/usb/drivers.c b/drivers/usb/drivers.c new file mode 100644 index 000000000000..28589b1e6da5 --- /dev/null +++ b/drivers/usb/drivers.c @@ -0,0 +1,119 @@ +/* + * drivers.c + * (C) Copyright 1999 Randy Dunlap. + * (C) Copyright 1999, 2000 Thomas Sailer . (proc file per device) + * (C) Copyright 1999 Deti Fliegl (new USB architecture) + * + * $id$ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + ************************************************************* + * + * 1999-12-16: Thomas Sailer + * Converted the whole proc stuff to real + * read methods. Now not the whole device list needs to fit + * into one page, only the device list for one bus. + * Added a poll method to /proc/bus/usb/devices, to wake + * up an eventual usbd + * 2000-01-04: Thomas Sailer + * Turned into its own filesystem + * + * $Id: drivers.c,v 1.3 2000/01/11 13:58:24 tom Exp $ + */ + +#include +#include +#include +#include +#include + +/*****************************************************************/ + +/* + * Dump usb_driver_list. + * + * We now walk the list of registered USB drivers. + */ +static ssize_t usb_driver_read(struct file *file, char *buf, size_t nbytes, loff_t *ppos) +{ + struct list_head *tmp = usb_driver_list.next; + char *page, *start, *end; + ssize_t ret = 0; + unsigned int pos, len; + + if (*ppos < 0) + return -EINVAL; + if (nbytes <= 0) + return 0; + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + if (!(page = (char*) __get_free_page(GFP_KERNEL))) + return -ENOMEM; + start = page; + end = page + (PAGE_SIZE - 100); + pos = *ppos; + for (; tmp != &usb_driver_list; tmp = tmp->next) { + struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + int minor = driver->fops ? driver->minor : -1; + if (minor == -1) + start += sprintf (start, " %s\n", driver->name); + else + start += sprintf (start, "%3d-%3d: %s\n", minor, minor + 15, driver->name); + if (start > end) { + start += sprintf(start, "(truncated)\n"); + break; + } + } + if (start == page) + start += sprintf(start, "(none)\n"); + len = start - page; + if (len > pos) { + len -= pos; + if (len > nbytes) + len = nbytes; + ret = len; + if (copy_to_user(buf, page + pos, len)) + ret = -EFAULT; + else + *ppos += len; + } + free_page((unsigned long)page); + return ret; +} + +static loff_t usb_driver_lseek(struct file * file, loff_t offset, int orig) +{ + switch (orig) { + case 0: + file->f_pos = offset; + return file->f_pos; + + case 1: + file->f_pos += offset; + return file->f_pos; + + case 2: + return -EINVAL; + + default: + return -EINVAL; + } +} + +struct file_operations usbdevfs_drivers_fops = { + llseek: usb_driver_lseek, + read: usb_driver_read, +}; diff --git a/drivers/usb/dsbr100.c b/drivers/usb/dsbr100.c new file mode 100644 index 000000000000..e2056a2e3320 --- /dev/null +++ b/drivers/usb/dsbr100.c @@ -0,0 +1,354 @@ +/* A driver for the D-Link DSB-R100 USB radio. The R100 plugs + into both the USB and an analog audio input, so this thing + only deals with initialisation and frequency setting, the + audio data has to be handled by a sound driver. + + Major issue: I can't find out where the device reports the signal + strength, and indeed the windows software appearantly just looks + at the stereo indicator as well. So, scanning will only find + stereo stations. Sad, but I can't help it. + + Also, the windows program sends oodles of messages over to the + device, and I couldn't figure out their meaning. My suspicion + is that they don't have any:-) + + You might find some interesting stuff about this module at + http://unimut.fsk.uni-heidelberg.de/unimut/demi/dsbr + + Copyright (c) 2000 Markus Demleitner + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + History: + + Version 0.23: + Markus: Sign extension bug fixed by declaring transfer_buffer unsigned + + Version 0.22: + Markus: Some (brown bag) cleanup in what VIDIOCSTUNER returns, + thanks to Mike Cox for pointing the problem out. + + Version 0.21: + Markus: Minor cleanup, warnings if something goes wrong, lame attempt + to adhere to Documentation/CodingStyle + + Version 0.2: + Brad Hards : Fixes to make it work as non-module + Markus: Copyright clarification + + Version 0.01: Markus: initial release + +*/ + + +#include +#include +#include +#include +#include +#include +#include + +#define DSB100_VENDOR 0x04b4 +#define DSB100_PRODUCT 0x1002 + +#define TB_LEN 16 + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr); +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg); +static int usb_dsbr100_open(struct video_device *dev, int flags); +static void usb_dsbr100_close(struct video_device *dev); + + +typedef struct +{ struct urb readurb,writeurb; + struct usb_device *dev; + unsigned char transfer_buffer[TB_LEN]; + int curfreq; + int stereo; + int ifnum; +} usb_dsbr100; + + +static struct video_device usb_dsbr100_radio= +{ + "D-Link DSB R-100 USB radio", + VID_TYPE_TUNER, + VID_HARDWARE_AZTECH, + usb_dsbr100_open, + usb_dsbr100_close, + NULL, /* Can't read (no capture ability) */ + NULL, /* Can't write */ + NULL, /* No poll */ + usb_dsbr100_ioctl, + NULL, + NULL +}; + +static int users = 0; + +static struct usb_driver usb_dsbr100_driver = { + name: "dsbr100", + probe: usb_dsbr100_probe, + disconnect: usb_dsbr100_disconnect, + driver_list: {NULL,NULL}, + fops: NULL, + minor: 0 +}; + + +static int dsbr100_start(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0xC7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x01, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_stop(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x16, 0x1C, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x02, 0xC0, 0x00, 0x00, radio->transfer_buffer, 8, 300)<0) + return -1; + return (radio->transfer_buffer)[0]; +} + + +static int dsbr100_setfreq(usb_dsbr100 *radio, int freq) +{ + freq = (freq*80)/16+856; + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x01, 0xC0, (freq&0xff00)>>8, freq&0xff, + radio->transfer_buffer, 8, 300)<0 + || usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x96, 0xB7, radio->transfer_buffer, 8, 300)<0 || + usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00, 0x24, radio->transfer_buffer, 8, 300)<0) { + radio->stereo = -1; + return -1; + } + radio->stereo = ! ((radio->transfer_buffer)[0]&0x01); + return (radio->transfer_buffer)[0]; +} + +static void dsbr100_getstat(usb_dsbr100 *radio) +{ + if (usb_control_msg(radio->dev, usb_rcvctrlpipe(radio->dev, 0), + 0x00, 0xC0, 0x00 , 0x24, radio->transfer_buffer, 8, 300)<0) + radio->stereo = -1; + else + radio->stereo = ! (radio->transfer_buffer[0]&0x01); +} + + +static void *usb_dsbr100_probe(struct usb_device *dev, unsigned int ifnum) +{ + usb_dsbr100 *radio; + + if (dev->descriptor.idVendor!=DSB100_VENDOR || + dev->descriptor.idProduct!=DSB100_PRODUCT) + return NULL; + if (!(radio = kmalloc(sizeof(usb_dsbr100),GFP_KERNEL))) + return NULL; + usb_dsbr100_radio.priv = radio; + radio->dev = dev; + radio->ifnum = ifnum; + radio->curfreq = 1454; + return (void*)radio; +} + +static void usb_dsbr100_disconnect(struct usb_device *dev, void *ptr) +{ + usb_dsbr100 *radio=ptr; + + if (users) + return; + kfree(radio); + usb_dsbr100_radio.priv = NULL; +} + +static int usb_dsbr100_ioctl(struct video_device *dev, unsigned int cmd, + void *arg) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return -EINVAL; + + switch(cmd) + { + case VIDIOCGCAP: { + struct video_capability v; + v.type=VID_TYPE_TUNER; + v.channels=1; + v.audios=1; + /* No we don't do pictures */ + v.maxwidth=0; + v.maxheight=0; + v.minwidth=0; + v.minheight=0; + strcpy(v.name, "D-Link R-100 USB Radio"); + if(copy_to_user(arg,&v,sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCGTUNER: { + struct video_tuner v; + dsbr100_getstat(radio); + if(copy_from_user(&v, arg,sizeof(v))!=0) + return -EFAULT; + if(v.tuner) /* Only 1 tuner */ + return -EINVAL; + v.rangelow = 87*16; + v.rangehigh = 108*16; + v.flags = VIDEO_TUNER_LOW; + v.mode = VIDEO_MODE_AUTO; + v.signal = radio->stereo*0x7000; + /* Don't know how to get signal strength */ + v.flags |= VIDEO_TUNER_STEREO_ON*radio->stereo; + strcpy(v.name, "DSB R-100"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSTUNER: { + struct video_tuner v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.tuner!=0) + return -EINVAL; + /* Only 1 tuner so no setting needed ! */ + return 0; + } + case VIDIOCGFREQ: + if (radio->curfreq==-1) + return -EINVAL; + if(copy_to_user(arg, &(radio->curfreq), + sizeof(radio->curfreq))) + return -EFAULT; + return 0; + + case VIDIOCSFREQ: + if(copy_from_user(&(radio->curfreq), arg, + sizeof(radio->curfreq))) + return -EFAULT; + if (dsbr100_setfreq(radio, radio->curfreq)==-1) + warn("set frequency failed"); + return 0; + + case VIDIOCGAUDIO: { + struct video_audio v; + memset(&v,0, sizeof(v)); + v.flags|=VIDEO_AUDIO_MUTABLE; + v.mode=VIDEO_SOUND_STEREO; + v.volume=1; + v.step=1; + strcpy(v.name, "Radio"); + if(copy_to_user(arg,&v, sizeof(v))) + return -EFAULT; + return 0; + } + case VIDIOCSAUDIO: { + struct video_audio v; + if(copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if(v.audio) + return -EINVAL; + + if(v.flags&VIDEO_AUDIO_MUTE) { + if (dsbr100_stop(radio)==-1) + warn("radio did not respond properly"); + } + else + if (dsbr100_start(radio)==-1) + warn("radio did not respond properly"); + return 0; + } + default: + return -ENOIOCTLCMD; + } +} + + +static int usb_dsbr100_open(struct video_device *dev, int flags) +{ + usb_dsbr100 *radio=dev->priv; + + if (! radio) { + warn("radio not initialised"); + return -EAGAIN; + } + if(users) + { + warn("radio in use"); + return -EBUSY; + } + users++; + MOD_INC_USE_COUNT; + if (dsbr100_start(radio)<0) + warn("radio did not start up properly"); + dsbr100_setfreq(radio,radio->curfreq); + return 0; +} + +static void usb_dsbr100_close(struct video_device *dev) +{ + usb_dsbr100 *radio=dev->priv; + + if (!radio) + return; + users--; + dsbr100_stop(radio); + MOD_DEC_USE_COUNT; +} + +int __init dsbr100_init(void) +{ + usb_dsbr100_radio.priv = NULL; + usb_register(&usb_dsbr100_driver); + if (video_register_device(&usb_dsbr100_radio,VFL_TYPE_RADIO)==-1) { + warn("couldn't register video device"); + return -EINVAL; + } + return 0; +} + +int __init init_module(void) +{ + return dsbr100_init(); +} + +void cleanup_module(void) +{ + usb_dsbr100 *radio=usb_dsbr100_radio.priv; + + if (radio) + dsbr100_stop(radio); + video_unregister_device(&usb_dsbr100_radio); + usb_deregister(&usb_dsbr100_driver); +} + +/* +vi: ts=8 +Sigh. Of course, I am one of the ts=2 heretics, but Linus' wish is +my command. +*/ diff --git a/drivers/usb/evdev.c b/drivers/usb/evdev.c new file mode 100644 index 000000000000..06f0ab2cba9b --- /dev/null +++ b/drivers/usb/evdev.c @@ -0,0 +1,349 @@ +/* + * $Id: evdev.c,v 1.8 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Event char devices, giving access to raw input device events. + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define EVDEV_MINOR_BASE 64 +#define EVDEV_MINORS 32 +#define EVDEV_BUFFER_SIZE 64 + +#include +#include +#include +#include +#include +#include + +struct evdev { + int used; + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + devfs_handle_t devfs; + struct evdev_list *list; +}; + +struct evdev_list { + struct input_event buffer[EVDEV_BUFFER_SIZE]; + int head; + int tail; + struct fasync_struct *fasync; + struct evdev *evdev; + struct evdev_list *next; +}; + +static struct evdev *evdev_table[EVDEV_MINORS] = { NULL, /* ... */ }; + +static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct evdev *evdev = handle->private; + struct evdev_list *list = evdev->list; + + while (list) { + + get_fast_time(&list->buffer[list->head].time); + list->buffer[list->head].type = type; + list->buffer[list->head].code = code; + list->buffer[list->head].value = value; + list->head = (list->head + 1) & (EVDEV_BUFFER_SIZE - 1); + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&evdev->wait); +} + +static int evdev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct evdev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int evdev_release(struct inode * inode, struct file * file) +{ + struct evdev_list *list = file->private_data; + struct evdev_list **listptr = &list->evdev->list; + + evdev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->evdev->open) + input_close_device(&list->evdev->handle); + + if (!--list->evdev->used) { + input_unregister_minor(list->evdev->devfs); + evdev_table[list->evdev->minor] = NULL; + kfree(list->evdev); + } + + kfree(list); + + return 0; +} + +static int evdev_open(struct inode * inode, struct file * file) +{ + struct evdev_list *list; + int i = MINOR(inode->i_rdev) - EVDEV_MINOR_BASE; + + if (i > EVDEV_MINORS || !evdev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct evdev_list), GFP_KERNEL))) { + return -ENOMEM; + } + memset(list, 0, sizeof(struct evdev_list)); + + list->evdev = evdev_table[i]; + list->next = evdev_table[i]->list; + evdev_table[i]->list = list; + + file->private_data = list; + + list->evdev->used++; + + if (!list->evdev->open++) + input_open_device(&list->evdev->handle); + + return 0; +} + +static ssize_t evdev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t evdev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct evdev_list *list = file->private_data; + int retval = 0; + + if (list->head == list->tail) { + + add_wait_queue(&list->evdev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (list->head == list->tail) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->evdev->wait, &wait); + } + + if (retval) + return retval; + + while (list->head != list->tail && retval + sizeof(struct input_event) <= count) { + if (copy_to_user(buffer + retval, list->buffer + list->tail, + sizeof(struct input_event))) return -EFAULT; + list->tail = (list->tail + 1) & (EVDEV_BUFFER_SIZE - 1); + retval += sizeof(struct input_event); + } + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int evdev_poll(struct file *file, poll_table *wait) +{ + struct evdev_list *list = file->private_data; + poll_wait(file, &list->evdev->wait, wait); + if (list->head != list->tail) + return POLLIN | POLLRDNORM; + return 0; +} + +static int evdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct evdev_list *list = file->private_data; + struct evdev *evdev = list->evdev; + struct input_dev *dev = evdev->handle.dev; + int retval; + + switch (cmd) { + + case EVIOCGVERSION: + return put_user(EV_VERSION, (int *) arg); + + case EVIOCGID: + if ((retval = put_user(dev->idbus, ((short *) arg) + 0))) return retval; + if ((retval = put_user(dev->idvendor, ((short *) arg) + 1))) return retval; + if ((retval = put_user(dev->idproduct, ((short *) arg) + 2))) return retval; + if ((retval = put_user(dev->idversion, ((short *) arg) + 3))) return retval; + return 0; + + default: + + if (_IOC_TYPE(cmd) != 'E' || _IOC_DIR(cmd) != _IOC_READ) + return -EINVAL; + + if ((_IOC_NR(cmd) & ~EV_MAX) == _IOC_NR(EVIOCGBIT(0,0))) { + + long *bits; + int len; + + switch (_IOC_NR(cmd) & EV_MAX) { + case 0: bits = dev->evbit; len = EV_MAX; break; + case EV_KEY: bits = dev->keybit; len = KEY_MAX; break; + case EV_REL: bits = dev->relbit; len = REL_MAX; break; + case EV_ABS: bits = dev->absbit; len = ABS_MAX; break; + case EV_LED: bits = dev->ledbit; len = LED_MAX; break; + case EV_SND: bits = dev->sndbit; len = SND_MAX; break; + default: return -EINVAL; + } + len = NBITS(len) * sizeof(long); + if (len > _IOC_SIZE(cmd)) { + printk(KERN_WARNING "evdev.c: Truncating bitfield length from %d to %d\n", + len, _IOC_SIZE(cmd)); + len = _IOC_SIZE(cmd); + } + return copy_to_user((char *) arg, bits, len) ? -EFAULT : len; + } + + if (_IOC_NR(cmd) == _IOC_NR(EVIOCGNAME(0))) { + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + return copy_to_user((char *) arg, dev->name, len) ? -EFAULT : len; + } + + if ((_IOC_NR(cmd) & ~ABS_MAX) == _IOC_NR(EVIOCGABS(0))) { + + int t = _IOC_NR(cmd) & ABS_MAX; + + if ((retval = put_user(dev->abs[t], ((int *) arg) + 0))) return retval; + if ((retval = put_user(dev->absmin[t], ((int *) arg) + 1))) return retval; + if ((retval = put_user(dev->absmax[t], ((int *) arg) + 2))) return retval; + if ((retval = put_user(dev->absfuzz[t], ((int *) arg) + 3))) return retval; + if ((retval = put_user(dev->absflat[t], ((int *) arg) + 4))) return retval; + + return 0; + } + } + return -EINVAL; +} + +static struct file_operations evdev_fops = { + read: evdev_read, + write: evdev_write, + poll: evdev_poll, + open: evdev_open, + release: evdev_release, + ioctl: evdev_ioctl, + fasync: evdev_fasync, +}; + +static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct evdev *evdev; + int minor; + + for (minor = 0; minor < EVDEV_MINORS && evdev_table[minor]; minor++); + if (evdev_table[minor]) { + printk(KERN_ERR "evdev: no more free evdev devices\n"); + return NULL; + } + + if (!(evdev = kmalloc(sizeof(struct evdev), GFP_KERNEL))) + return NULL; + memset(evdev, 0, sizeof(struct evdev)); + + init_waitqueue_head(&evdev->wait); + + evdev->minor = minor; + evdev_table[minor] = evdev; + + evdev->handle.dev = dev; + evdev->handle.handler = handler; + evdev->handle.private = evdev; + + evdev->used = 1; + + evdev->devfs = input_register_minor("event%d", minor, EVDEV_MINOR_BASE); + + printk(KERN_INFO "event%d: Event device for input%d\n", minor, dev->number); + + return &evdev->handle; +} + +static void evdev_disconnect(struct input_handle *handle) +{ + struct evdev *evdev = handle->private; + + if (evdev->open) + input_close_device(handle); + + if (!--evdev->used) { + input_unregister_minor(evdev->devfs); + evdev_table[evdev->minor] = NULL; + kfree(evdev); + } +} + +static struct input_handler evdev_handler = { + event: evdev_event, + connect: evdev_connect, + disconnect: evdev_disconnect, + fops: &evdev_fops, + minor: EVDEV_MINOR_BASE, +}; + +static int __init evdev_init(void) +{ + input_register_handler(&evdev_handler); + return 0; +} + +static void __exit evdev_exit(void) +{ + input_unregister_handler(&evdev_handler); +} + +module_init(evdev_init); +module_exit(evdev_exit); diff --git a/drivers/usb/hid-debug.h b/drivers/usb/hid-debug.h new file mode 100644 index 000000000000..26f5e9e1b640 --- /dev/null +++ b/drivers/usb/hid-debug.h @@ -0,0 +1,235 @@ +/* + * $Id: hid-debug.h,v 1.2 2000/05/29 10:54:53 vojtech Exp $ + * + * (c) 1999 Andreas Gal + * (c) 2000 Vojtech Pavlik + * + * Some debug stuff for the HID parser. + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +struct hid_usage_entry { + unsigned page; + unsigned usage; + char *description; +}; + +static struct hid_usage_entry hid_usage_table[] = { + { 1, 0, "GenericDesktop" }, + {0, 0x01, "Pointer"}, + {0, 0x02, "Mouse"}, + {0, 0x04, "Joystick"}, + {0, 0x05, "GamePad"}, + {0, 0x06, "Keyboard"}, + {0, 0x07, "Keypad"}, + {0, 0x08, "MultiAxis"}, + {0, 0x30, "X"}, + {0, 0x31, "Y"}, + {0, 0x32, "Z"}, + {0, 0x33, "Rx"}, + {0, 0x34, "Ry"}, + {0, 0x35, "Rz"}, + {0, 0x36, "Slider"}, + {0, 0x37, "Dial"}, + {0, 0x38, "Wheel"}, + {0, 0x39, "HatSwitch"}, + {0, 0x3a, "CountedBuffer"}, + {0, 0x3b, "ByteCount"}, + {0, 0x3c, "MotionWakeup"}, + {0, 0x3d, "Start"}, + {0, 0x3e, "Select"}, + {0, 0x40, "Vx"}, + {0, 0x41, "Vy"}, + {0, 0x42, "Vz"}, + {0, 0x43, "Vbrx"}, + {0, 0x44, "Vbry"}, + {0, 0x45, "Vbrz"}, + {0, 0x46, "Vno"}, + {0, 0x80, "SystemControl"}, + {0, 0x81, "SystemPowerDown"}, + {0, 0x82, "SystemSleep"}, + {0, 0x83, "SystemWakeUp"}, + {0, 0x84, "SystemContextMenu"}, + {0, 0x85, "SystemMainMenu"}, + {0, 0x86, "SystemAppMenu"}, + {0, 0x87, "SystemMenuHelp"}, + {0, 0x88, "SystemMenuExit"}, + {0, 0x89, "SystemMenuSelect"}, + {0, 0x8a, "SystemMenuRight"}, + {0, 0x8b, "SystemMenuLeft"}, + {0, 0x8c, "SystemMenuUp"}, + {0, 0x8d, "SystemMenuDown"}, + {0, 0x90, "D-padUp"}, + {0, 0x91, "D-padDown"}, + {0, 0x92, "D-padRight"}, + {0, 0x93, "D-padLeft"}, + { 7, 0, "Keyboard" }, + { 8, 0, "LED" }, + { 9, 0, "Button" }, + { 12, 0, "Hotkey" }, + { 13, 0, "Digitizers" }, + {0, 0x01, "Digitizer"}, + {0, 0x02, "Pen"}, + {0, 0x03, "LightPen"}, + {0, 0x04, "TouchScreen"}, + {0, 0x05, "TouchPad"}, + {0, 0x20, "Stylus"}, + {0, 0x21, "Puck"}, + {0, 0x22, "Finger"}, + {0, 0x30, "TipPressure"}, + {0, 0x31, "BarrelPressure"}, + {0, 0x32, "InRange"}, + {0, 0x33, "Touch"}, + {0, 0x34, "UnTouch"}, + {0, 0x35, "Tap"}, + {0, 0x39, "TabletFunctionKey"}, + {0, 0x3a, "ProgramChangeKey"}, + {0, 0x3c, "Invert"}, + {0, 0x42, "TipSwitch"}, + {0, 0x43, "SecondaryTipSwitch"}, + {0, 0x44, "BarrelSwitch"}, + {0, 0x45, "Eraser"}, + {0, 0x46, "TabletPick"}, + { 15, 0, "PhysicalInterfaceDevice" }, + { 0, 0, NULL } +}; + +static void resolv_usage_page(unsigned page) { + struct hid_usage_entry *p; + + for (p = hid_usage_table; p->description; p++) + if (p->page == page) { + printk("%s", p->description); + return; + } + printk("%04x", page); +} + +static void resolv_usage(unsigned usage) { + struct hid_usage_entry *p; + + resolv_usage_page(usage >> 16); + printk("."); + for (p = hid_usage_table; p->description; p++) + if (p->page == (usage >> 16)) { + for(++p; p->description && p->page == 0; p++) + if (p->usage == (usage & 0xffff)) { + printk("%s", p->description); + return; + } + break; + } + printk("%04x", usage & 0xffff); +} + +__inline__ static void tab(int n) { + while (n--) printk(" "); +} + +static void hid_dump_field(struct hid_field *field, int n) { + int j; + + if (field->physical) { + tab(n); + printk("Physical("); + resolv_usage(field->physical); printk(")\n"); + } + if (field->logical) { + tab(n); + printk("Logical("); + resolv_usage(field->logical); printk(")\n"); + } + tab(n); printk("Usage(%d)\n", field->maxusage); + for (j = 0; j < field->maxusage; j++) { + tab(n+2);resolv_usage(field->usage[j].hid); printk("\n"); + } + if (field->logical_minimum != field->logical_maximum) { + tab(n); printk("Logical Minimum(%d)\n", field->logical_minimum); + tab(n); printk("Logical Maximum(%d)\n", field->logical_maximum); + } + if (field->physical_minimum != field->physical_maximum) { + tab(n); printk("Physical Minimum(%d)\n", field->physical_minimum); + tab(n); printk("Physical Maximum(%d)\n", field->physical_maximum); + } + if (field->unit_exponent) { + tab(n); printk("Unit Exponent(%d)\n", field->unit_exponent); + } + if (field->unit) { + tab(n); printk("Unit(%u)\n", field->unit); + } + tab(n); printk("Report Size(%u)\n", field->report_size); + tab(n); printk("Report Count(%u)\n", field->report_count); + tab(n); printk("Report Offset(%u)\n", field->report_offset); + + tab(n); printk("Flags( "); + j = field->flags; + printk("%s", HID_MAIN_ITEM_CONSTANT & j ? "Constant " : ""); + printk("%s", HID_MAIN_ITEM_VARIABLE & j ? "Variable " : "Array "); + printk("%s", HID_MAIN_ITEM_RELATIVE & j ? "Relative " : "Absolute "); + printk("%s", HID_MAIN_ITEM_WRAP & j ? "Wrap " : ""); + printk("%s", HID_MAIN_ITEM_NONLINEAR & j ? "NonLinear " : ""); + printk("%s", HID_MAIN_ITEM_NO_PREFERRED & j ? "NoPrefferedState " : ""); + printk("%s", HID_MAIN_ITEM_NULL_STATE & j ? "NullState " : ""); + printk("%s", HID_MAIN_ITEM_VOLATILE & j ? "Volatile " : ""); + printk("%s", HID_MAIN_ITEM_BUFFERED_BYTE & j ? "BufferedByte " : ""); + printk(")\n"); +} + +void hid_dump_device(struct hid_device *device) { + struct hid_report_enum *report_enum; + struct hid_report *report; + struct list_head *list; + unsigned i,k; + static char *table[] = {"INPUT", "OUTPUT", "FEATURE"}; + + printk("Application("); + resolv_usage(device->application); + printk(")\n"); + + for (i = 0; i < HID_REPORT_TYPES; i++) { + report_enum = device->report_enum + i; + list = report_enum->report_list.next; + while (list != &report_enum->report_list) { + report = (struct hid_report *) list; + tab(2); + printk("%s", table[i]); + if (report->id) + printk("(%d)", report->id); + printk("[%s]", table[report->type]); + printk("\n"); + for (k = 0; k < report->maxfield; k++) { + tab(4); + printk("Field(%d)\n", k); + hid_dump_field(report->field[k], 6); + } + list = list->next; + } + } +} + +void hid_dump_input(struct hid_usage *usage, __s32 value) { + printk("hidd: input "); + resolv_usage(usage->hid); + printk(" = %d\n", value); +} diff --git a/drivers/usb/hid.c b/drivers/usb/hid.c new file mode 100644 index 000000000000..ae529b7464a6 --- /dev/null +++ b/drivers/usb/hid.c @@ -0,0 +1,1526 @@ +/* + * $Id: hid.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000 Vojtech Pavlik + * + * USB HID support for the Linux input drivers + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#undef DEBUG +#undef DEBUG_DATA +#include + +#include + +#include "hid.h" + +#ifdef DEBUG +#include "hid-debug.h" +#else +#define hid_dump_input(a,b) do { } while (0) +#define hid_dump_device(c) do { } while (0) +#endif + +#define unk KEY_UNKNOWN + +static unsigned char hid_keyboard[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, + 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114,unk,unk,unk,unk,unk,124,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk,unk, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140,unk,unk,unk,unk +}; + +static struct { + __s32 x; + __s32 y; +} hid_hat_to_axis[] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}, { 0, 0}}; + +static char *hid_types[] = {"Device", "Pointer", "Mouse", "Device", "Joystick", + "Gamepad", "Keyboard", "Keypad", "Multi-Axis Controller"}; + +/* + * Register a new report for a device. + */ + +static struct hid_report *hid_register_report(struct hid_device *device, unsigned type, unsigned id) +{ + struct hid_report_enum *report_enum = device->report_enum + type; + struct hid_report *report; + + if (report_enum->report_id_hash[id]) + return report_enum->report_id_hash[id]; + + if (!(report = kmalloc(sizeof(struct hid_report), GFP_KERNEL))) + return NULL; + memset(report, 0, sizeof(struct hid_report)); + + if (id != 0) report_enum->numbered = 1; + + report->id = id; + report->type = type; + report->size = 0; + report->device = device; + report_enum->report_id_hash[id] = report; + + list_add_tail(&report->list, &report_enum->report_list); + + return report; +} + +/* + * Register a new field for this report. + */ + +static struct hid_field *hid_register_field(struct hid_report *report, unsigned usages, unsigned values) +{ + if (report->maxfield < HID_MAX_FIELDS) { + struct hid_field *field; + + if (!(field = kmalloc(sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + + values * sizeof(unsigned), GFP_KERNEL))) + return NULL; + memset(field, 0, sizeof(struct hid_field) + usages * sizeof(struct hid_usage) + + values * sizeof(unsigned)); + + report->field[report->maxfield++] = field; + field->usage = (struct hid_usage *)(field + 1); + field->value = (unsigned *)(field->usage + usages); + field->report = report; + + return field; + } + + dbg("too many fields in report"); + return NULL; +} + +/* + * Open a collection. The type/usage is pushed on the stack. + */ + +static int open_collection(struct hid_parser *parser, unsigned type) +{ + unsigned usage; + + usage = parser->local.usage[0]; + + if (type == HID_COLLECTION_APPLICATION) + parser->device->application = usage; + + if (parser->collection_stack_ptr < HID_COLLECTION_STACK_SIZE) { /* PUSH on stack */ + struct hid_collection *collection = parser->collection_stack + parser->collection_stack_ptr++; + collection->type = type; + collection->usage = usage; + return 0; + } + + dbg("collection stack overflow"); + return -1; +} + +/* + * Close a collection. + */ + +static int close_collection(struct hid_parser *parser) +{ + if (parser->collection_stack_ptr > 0) { /* POP from stack */ + parser->collection_stack_ptr--; + return 0; + } + dbg("collection stack underflow"); + return -1; +} + +/* + * Climb up the stack, search for the specified collection type + * and return the usage. + */ + +static unsigned hid_lookup_collection(struct hid_parser *parser, unsigned type) +{ + int n; + for (n = parser->collection_stack_ptr - 1; n >= 0; n--) + if (parser->collection_stack[n].type == type) + return parser->collection_stack[n].usage; + return 0; /* we know nothing about this usage type */ +} + +/* + * Add a usage to the temporary parser table. + */ + +static int hid_add_usage(struct hid_parser *parser, unsigned usage) +{ + if (parser->local.usage_index >= MAX_USAGES) { + dbg("usage index exceeded"); + return -1; + } + parser->local.usage[parser->local.usage_index++] = usage; + return 0; +} + +/* + * Register a new field for this report. + */ + +static int hid_add_field(struct hid_parser *parser, unsigned report_type, unsigned flags) +{ + struct hid_report *report; + struct hid_field *field; + int usages; + unsigned offset; + int i; + + if (!(report = hid_register_report(parser->device, report_type, parser->global.report_id))) { + dbg("hid_register_report failed"); + return -1; + } + + if (HID_MAIN_ITEM_VARIABLE & ~flags) { /* ARRAY */ + if (parser->global.logical_maximum <= parser->global.logical_minimum) { + dbg("logical range invalid %d %d", parser->global.logical_minimum, parser->global.logical_maximum); + return -1; + } + usages = parser->local.usage_index; + /* Hint: we can assume usages < MAX_USAGE here */ + } else { /* VARIABLE */ + usages = parser->global.report_count; + } + offset = report->size; + report->size += parser->global.report_size * + parser->global.report_count; + if (usages == 0) + return 0; /* ignore padding fields */ + if ((field = hid_register_field(report, usages, + parser->global.report_count)) == NULL) + return 0; + field->physical = hid_lookup_collection(parser, HID_COLLECTION_PHYSICAL); + field->logical = hid_lookup_collection(parser, HID_COLLECTION_LOGICAL); + for (i = 0; i < usages; i++) field->usage[i].hid = parser->local.usage[i]; + field->maxusage = usages; + field->flags = flags; + field->report_offset = offset; + field->report_type = report_type; + field->report_size = parser->global.report_size; + field->report_count = parser->global.report_count; + field->logical_minimum = parser->global.logical_minimum; + field->logical_maximum = parser->global.logical_maximum; + field->physical_minimum = parser->global.physical_minimum; + field->physical_maximum = parser->global.physical_maximum; + field->unit_exponent = parser->global.unit_exponent; + field->unit = parser->global.unit; + return 0; +} + +/* + * Read data value from item. + */ + +static __inline__ __u32 item_udata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.u8; + case 2: return item->data.u16; + case 4: return item->data.u32; + } + return 0; +} + +static __inline__ __s32 item_sdata(struct hid_item *item) +{ + switch (item->size) { + case 1: return item->data.s8; + case 2: return item->data.s16; + case 4: return item->data.s32; + } + return 0; +} + +/* + * Process a global item. + */ + +static int hid_parser_global(struct hid_parser *parser, struct hid_item *item) +{ + switch (item->tag) { + + case HID_GLOBAL_ITEM_TAG_PUSH: + + if (parser->global_stack_ptr < HID_GLOBAL_STACK_SIZE) { + memcpy(parser->global_stack + parser->global_stack_ptr++, + &parser->global, sizeof(struct hid_global)); + return 0; + } + dbg("global enviroment stack overflow"); + return -1; + + case HID_GLOBAL_ITEM_TAG_POP: + + if (parser->global_stack_ptr > 0) { + memcpy(&parser->global, parser->global_stack + --parser->global_stack_ptr, + sizeof(struct hid_global)); + return 0; + } + dbg("global enviroment stack underflow"); + return -1; + + case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: + parser->global.usage_page = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: + parser->global.logical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: + parser->global.logical_maximum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: + parser->global.physical_minimum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: + parser->global.physical_maximum = item_sdata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: + parser->global.unit_exponent = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_UNIT: + parser->global.unit = item_udata(item); + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: + if ((parser->global.report_size = item_udata(item)) > 32) { + dbg("invalid report_size %d", parser->global.report_size); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: + if ((parser->global.report_count = item_udata(item)) > MAX_USAGES) { + dbg("invalid report_count %d", parser->global.report_count); + return -1; + } + return 0; + + case HID_GLOBAL_ITEM_TAG_REPORT_ID: + if ((parser->global.report_id = item_udata(item)) == 0) { + dbg("report_id 0 is invalid"); + return -1; + } + return 0; + + default: + dbg("unknown global tag 0x%x", item->tag); + return -1; + } +} + +/* + * Process a local item. + */ + +static int hid_parser_local(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + + if (item->size == 0) { + dbg("item data expected for local item"); + return -1; + } + + data = item_udata(item); + + switch (item->tag) { + + case HID_LOCAL_ITEM_TAG_DELIMITER: + + if (data) { + /* + * We treat items before the first delimiter + * as global to all usage sets (branch 0). + * In the moment we process only these global + * items and the first delimiter set. + */ + if (parser->local.delimiter_depth != 0) { + dbg("nested delimiters"); + return -1; + } + parser->local.delimiter_depth++; + parser->local.delimiter_branch++; + } else { + if (parser->local.delimiter_depth < 1) { + dbg("bogus close delimiter"); + return -1; + } + parser->local.delimiter_depth--; + } + return 1; + + case HID_LOCAL_ITEM_TAG_USAGE: + + if (parser->local.delimiter_branch < 2) { + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + return hid_add_usage(parser, data); + } + dbg("alternative usage ignored"); + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: + + if (parser->local.delimiter_branch < 2) { + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + parser->local.usage_minimum = data; + return 0; + } + dbg("alternative usage ignored"); + return 0; + + case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: + + if (parser->local.delimiter_branch < 2) { + unsigned n; + if (item->size <= 2) + data = (parser->global.usage_page << 16) + data; + for (n = parser->local.usage_minimum; n <= data; n++) + if (hid_add_usage(parser, n)) { + dbg("hid_add_usage failed\n"); + return -1; + } + return 0; + } + dbg("alternative usage ignored"); + return 0; + + default: + + dbg("unknown local item tag 0x%x", item->tag); + return 0; + } +} + +/* + * Process a main item. + */ + +static int hid_parser_main(struct hid_parser *parser, struct hid_item *item) +{ + __u32 data; + int ret; + + data = item_udata(item); + + switch (item->tag) { + case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: + ret = open_collection(parser, data & 3); + break; + case HID_MAIN_ITEM_TAG_END_COLLECTION: + ret = close_collection(parser); + break; + case HID_MAIN_ITEM_TAG_INPUT: + ret = hid_add_field(parser, HID_INPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_OUTPUT: + ret = hid_add_field(parser, HID_OUTPUT_REPORT, data); + break; + case HID_MAIN_ITEM_TAG_FEATURE: + ret = hid_add_field(parser, HID_FEATURE_REPORT, data); + break; + default: + dbg("unknown main item tag 0x%x", item->tag); + ret = 0; + } + + memset(&parser->local, 0, sizeof(parser->local)); /* Reset the local parser environment */ + + return ret; +} + +/* + * Process a reserved item. + */ + +static int hid_parser_reserved(struct hid_parser *parser, struct hid_item *item) +{ + dbg("reserved item type, tag 0x%x", item->tag); + return 0; +} + +/* + * Free a report and all registered fields. The field->usage and + * field->value table's are allocated behind the field, so we need + * only to free(field) itself. + */ + +static void hid_free_report(struct hid_report *report) +{ + unsigned n; + + for (n = 0; n < report->maxfield; n++) + kfree(report->field[n]); + kfree(report); +} + +/* + * Free a device structure, all reports, and all fields. + */ + +static void hid_free_device(struct hid_device *device) +{ + unsigned i,j; + + for (i = 0; i < HID_REPORT_TYPES; i++) { + struct hid_report_enum *report_enum = device->report_enum + i; + + for (j = 0; j < 256; j++) { + struct hid_report *report = report_enum->report_id_hash[j]; + if (report) hid_free_report(report); + } + } + + if (device->rdesc) kfree(device->rdesc); +} + +/* + * Fetch a report description item from the data stream. We support long + * items, though they are not used yet. + */ + +static __u8 *fetch_item(__u8 *start, __u8 *end, struct hid_item *item) +{ + if ((end - start) > 0) { + + __u8 b = *start++; + item->type = (b >> 2) & 3; + item->tag = (b >> 4) & 15; + + if (item->tag == HID_ITEM_TAG_LONG) { + + item->format = HID_ITEM_FORMAT_LONG; + + if ((end - start) >= 2) { + + item->size = *start++; + item->tag = *start++; + + if ((end - start) >= item->size) { + item->data.longdata = start; + start += item->size; + return start; + } + } + } else { + + item->format = HID_ITEM_FORMAT_SHORT; + item->size = b & 3; + switch (item->size) { + + case 0: + return start; + + case 1: + if ((end - start) >= 1) { + item->data.u8 = *start++; + return start; + } + break; + + case 2: + if ((end - start) >= 2) { + item->data.u16 = le16_to_cpu( get_unaligned(((__u16*)start)++)); + return start; + } + + case 3: + item->size++; + if ((end - start) >= 4) { + item->data.u32 = le32_to_cpu( get_unaligned(((__u32*)start)++)); + return start; + } + } + } + } + return NULL; +} + +/* + * Parse a report description into a hid_device structure. Reports are + * enumerated, fields are attached to these reports. + */ + +static struct hid_device *hid_parse_report(__u8 *start, unsigned size) +{ + struct hid_device *device; + struct hid_parser *parser; + struct hid_item item; + __u8 *end; + unsigned i; + static int (*dispatch_type[])(struct hid_parser *parser, + struct hid_item *item) = { + hid_parser_main, + hid_parser_global, + hid_parser_local, + hid_parser_reserved + }; + + if (!(device = kmalloc(sizeof(struct hid_device), GFP_KERNEL))) + return NULL; + memset(device, 0, sizeof(struct hid_device)); + + for (i = 0; i < HID_REPORT_TYPES; i++) + INIT_LIST_HEAD(&device->report_enum[i].report_list); + + if (!(device->rdesc = (__u8 *)kmalloc(size, GFP_KERNEL))) { + kfree(device); + return NULL; + } + memcpy(device->rdesc, start, size); + + if (!(parser = kmalloc(sizeof(struct hid_parser), GFP_KERNEL))) { + kfree(device->rdesc); + kfree(device); + return NULL; + } + memset(parser, 0, sizeof(struct hid_parser)); + parser->device = device; + + end = start + size; + while ((start = fetch_item(start, end, &item)) != 0) { + if (item.format != HID_ITEM_FORMAT_SHORT) { + dbg("unexpected long global item"); + hid_free_device(device); + kfree(parser); + return NULL; + } + if (dispatch_type[item.type](parser, &item)) { + dbg("item %u %u %u %u parsing failed\n", + item.format, (unsigned)item.size, (unsigned)item.type, (unsigned)item.tag); + hid_free_device(device); + kfree(parser); + return NULL; + } + + if (start == end) { + if (parser->collection_stack_ptr) { + dbg("unbalanced collection at end of report description"); + hid_free_device(device); + kfree(parser); + return NULL; + } + if (parser->local.delimiter_depth) { + dbg("unbalanced delimiter at end of report description"); + hid_free_device(device); + kfree(parser); + return NULL; + } + kfree(parser); + return device; + } + } + + dbg("item fetching failed at offset %d\n", (int)(end - start)); + hid_free_device(device); + kfree(parser); + return NULL; +} + +/* + * Convert a signed n-bit integer to signed 32-bit integer. Common + * cases are done through the compiler, the screwed things has to be + * done by hand. + */ + +static __inline__ __s32 snto32(__u32 value, unsigned n) +{ + switch (n) { + case 8: return ((__s8)value); + case 16: return ((__s16)value); + case 32: return ((__s32)value); + } + return value & (1 << (n - 1)) ? value | (-1 << n) : value; +} + +/* + * Convert a signed 32-bit integer to a signed n-bit integer. + */ + +static __inline__ __u32 s32ton(__s32 value, unsigned n) +{ + __s32 a = value >> (n - 1); + if (a && a != -1) return value > 0 ? 1 << (n - 1) : (1 << n) - 1; + return value & ((1 << n) - 1); +} + +/* + * Extract/implement a data field from/to a report. We use 64-bit unsigned, + * 32-bit aligned, so that we can possibly have alignment problems on some + * odd architectures. + */ + +static __inline__ __u32 extract(__u8 *report, unsigned offset, unsigned n) +{ + report += (offset >> 5) << 2; offset &= 31; + return (le64_to_cpu(get_unaligned((__u64*)report)) >> offset) & ((1 << n) - 1); +} + +static __inline__ void implement(__u8 *report, unsigned offset, unsigned n, __u32 value) +{ + report += (offset >> 5) << 2; offset &= 31; + *(__u64*)report &= cpu_to_le64(~((((__u64) 1 << n) - 1) << offset)); + *(__u64*)report |= cpu_to_le64((__u64)value << offset); +} + +static void hid_configure_usage(struct hid_device *device, struct hid_field *field, struct hid_usage *usage) +{ + struct input_dev *input = &device->input; + int max; + unsigned long *bit; + + switch (usage->hid & HID_USAGE_PAGE) { + + case HID_UP_KEYBOARD: + + set_bit(EV_REP, input->evbit); + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + + if ((usage->hid & HID_USAGE) < 256) { + if (!(usage->code = hid_keyboard[usage->hid & HID_USAGE])) + return; + clear_bit(usage->code, bit); + } else + usage->code = KEY_UNKNOWN; + + break; + + case HID_UP_BUTTON: + + usage->code = ((usage->hid - 1) & 0xf) + 0x100; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + + switch (device->application) { + case HID_GD_GAMEPAD: usage->code += 0x10; + case HID_GD_JOYSTICK: usage->code += 0x10; + case HID_GD_MOUSE: usage->code += 0x10; break; + default: + if (field->physical == HID_GD_POINTER) + usage->code += 0x10; + break; + } + break; + + case HID_UP_GENDESK: + + if ((usage->hid & 0xf0) == 0x80) { /* SystemControl */ + switch (usage->hid & 0xf) { + case 0x1: usage->code = KEY_POWER; break; + case 0x2: usage->code = KEY_SLEEP; break; + case 0x3: usage->code = KEY_WAKEUP; break; + default: usage->code = KEY_UNKNOWN; break; + } + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + usage->code = usage->hid & 0xf; + + if (field->report_size == 1) { + usage->code = BTN_MISC; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + usage->type = EV_REL; bit = input->relbit; max = REL_MAX; + break; + } + + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + + if (usage->hid == HID_GD_HATSWITCH) { + usage->code = ABS_HAT0X; + usage->hat = 1 + (field->logical_maximum == 4); + } + break; + + case HID_UP_LED: + + usage->code = (usage->hid - 1) & 0xf; + usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; + break; + + case HID_UP_DIGITIZER: + + switch (usage->hid & 0xff) { + + case 0x30: /* TipPressure */ + + if (!test_bit(BTN_TOUCH, input->keybit)) { + device->quirks |= HID_QUIRK_NOTOUCH; + set_bit(EV_KEY, input->evbit); + set_bit(BTN_TOUCH, input->keybit); + } + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + usage->code = ABS_PRESSURE; + clear_bit(usage->code, bit); + break; + + case 0x32: /* InRange */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + switch (field->physical & 0xff) { + case 0x21: usage->code = BTN_TOOL_MOUSE; break; + case 0x22: usage->code = BTN_TOOL_FINGER; break; + default: usage->code = BTN_TOOL_PEN; break; + } + break; + + case 0x3c: /* Invert */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_TOOL_RUBBER; + clear_bit(usage->code, bit); + break; + + case 0x33: /* Touch */ + case 0x42: /* TipSwitch */ + case 0x43: /* TipSwitch2 */ + + device->quirks &= ~HID_QUIRK_NOTOUCH; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_TOUCH; + clear_bit(usage->code, bit); + break; + + case 0x44: /* BarrelSwitch */ + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + usage->code = BTN_STYLUS; + clear_bit(usage->code, bit); + break; + + default: goto unknown; + } + break; + + case HID_UP_CONSUMER: /* USB HUT v1.1, pages 56-62 */ + + switch (usage->hid & HID_USAGE) { + + case 0x034: usage->code = KEY_SLEEP; break; + case 0x036: usage->code = BTN_MISC; break; + case 0x08a: usage->code = KEY_WWW; break; + case 0x095: usage->code = KEY_HELP; break; + + case 0x0b4: usage->code = KEY_REWIND; break; + case 0x0b5: usage->code = KEY_NEXTSONG; break; + case 0x0b6: usage->code = KEY_PREVIOUSSONG; break; + case 0x0b7: usage->code = KEY_STOPCD; break; + case 0x0b8: usage->code = KEY_EJECTCD; break; + case 0x0cd: usage->code = KEY_PLAYPAUSE; break; + + case 0x0e2: usage->code = KEY_MUTE; break; + case 0x0e9: usage->code = KEY_VOLUMEUP; break; + case 0x0ea: usage->code = KEY_VOLUMEDOWN; break; + + case 0x183: usage->code = KEY_CONFIG; break; + case 0x18a: usage->code = KEY_MAIL; break; + case 0x192: usage->code = KEY_CALC; break; + case 0x194: usage->code = KEY_FILE; break; + + case 0x21a: usage->code = KEY_UNDO; break; + case 0x21b: usage->code = KEY_COPY; break; + case 0x21c: usage->code = KEY_CUT; break; + case 0x21d: usage->code = KEY_PASTE; break; + + case 0x221: usage->code = KEY_FIND; break; + case 0x223: usage->code = KEY_HOMEPAGE; break; + case 0x224: usage->code = KEY_BACK; break; + case 0x225: usage->code = KEY_FORWARD; break; + case 0x226: usage->code = KEY_STOP; break; + case 0x227: usage->code = KEY_REFRESH; break; + case 0x22a: usage->code = KEY_BOOKMARKS; break; + + default: usage->code = KEY_UNKNOWN; break; + + } + + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + + default: + unknown: + + if (field->report_size == 1) { + usage->code = BTN_MISC; + usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; + break; + } + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + usage->code = REL_MISC; + usage->type = EV_REL; bit = input->relbit; max = REL_MAX; + break; + } + + usage->code = ABS_MISC; + usage->type = EV_ABS; bit = input->absbit; max = ABS_MAX; + break; + } + + set_bit(usage->type, input->evbit); + + while (usage->code <= max && test_and_set_bit(usage->code, bit)) { + usage->code = find_next_zero_bit(bit, max + 1, usage->code); + } + + if (usage->type == EV_ABS) { + int a = field->logical_minimum; + int b = field->logical_maximum; + + input->absmin[usage->code] = a; + input->absmax[usage->code] = b; + input->absfuzz[usage->code] = (b - a) >> 8; + input->absflat[usage->code] = (b - a) >> 4; + } + + if (usage->hat) { + int i; + for (i = usage->code; i < usage->code + 2; i++) { + input->absmax[i] = 1; + input->absmin[i] = -1; + input->absfuzz[i] = 0; + input->absflat[i] = 0; + } + set_bit(usage->code + 1, input->absbit); + } +} + +static void hid_process_event(struct input_dev *input, int *quirks, struct hid_field *field, struct hid_usage *usage, __s32 value) +{ + hid_dump_input(usage, value); + + if (usage->hat) { + if (usage->hat == 2) value = value * 2; + if (value > 8) value = 8; + input_event(input, usage->type, usage->code , hid_hat_to_axis[value].x); + input_event(input, usage->type, usage->code + 1, hid_hat_to_axis[value].y); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x003c)) { /* Invert */ + *quirks = value ? (*quirks | HID_QUIRK_INVERT) : (*quirks & ~HID_QUIRK_INVERT); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x0032)) { /* InRange */ + if (value) { + input_event(input, usage->type, (*quirks & HID_QUIRK_INVERT) ? BTN_TOOL_RUBBER : usage->code, 1); + return; + } + input_event(input, usage->type, usage->code, 0); + input_event(input, usage->type, BTN_TOOL_RUBBER, 0); + return; + } + + if (usage->hid == (HID_UP_DIGITIZER | 0x0030) && (*quirks & HID_QUIRK_NOTOUCH)) { /* Pressure */ + int a = field->logical_minimum; + int b = field->logical_maximum; + input_event(input, EV_KEY, BTN_TOUCH, value > a + ((b - a) >> 3)); + } + + input_event(input, usage->type, usage->code, value); + + if ((field->flags & HID_MAIN_ITEM_RELATIVE) && (usage->type == EV_KEY)) + input_event(input, usage->type, usage->code, 0); +} + +/* + * Search an array for a value. + */ + +static __inline__ int search(__s32 *array, __s32 value, unsigned n) +{ + while (n--) if (*array++ == value) return 0; + return -1; +} + +/* + * Analyse a received field, and fetch the data from it. The field + * content is stored for next report processing (we do differential + * reporting to the layer). + */ + +static void hid_input_field(struct hid_device *dev, struct hid_field *field, __u8 *data) +{ + unsigned n; + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + __s32 min = field->logical_minimum; + __s32 max = field->logical_maximum; + __s32 value[count]; /* WARNING: gcc specific */ + + for (n = 0; n < count; n++) + value[n] = min < 0 ? snto32(extract(data, offset + n * size, size), size) : + extract(data, offset + n * size, size); + + for (n = 0; n < count; n++) { + + if (HID_MAIN_ITEM_VARIABLE & field->flags) { + + if (field->flags & HID_MAIN_ITEM_RELATIVE) { + if (!value[n]) continue; + } else { + if (value[n] == field->value[n]) continue; + } + hid_process_event(&dev->input, &dev->quirks, field, &field->usage[n], value[n]); + + } else { + + if (field->value[n] >= min && field->value[n] <= max /* non-NULL value */ + && field->usage[field->value[n] - min].hid /* nonzero usage */ + && search(value, field->value[n], count)) + hid_process_event(&dev->input, &dev->quirks, field, + &field->usage[field->value[n] - min], 0); + + if (value[n] >= min && value[n] <= max /* non-NULL value */ + && field->usage[value[n] - min].hid /* nonzero usage */ + && search(field->value, value[n], count)) + hid_process_event(&dev->input, &dev->quirks, + field, &field->usage[value[n] - min], 1); + } + } + + memcpy(field->value, value, count * sizeof(__s32)); +} + +/* + * Interrupt input handler - analyse a received report. + */ + +static void hid_irq(struct urb *urb) +{ + struct hid_device *device = urb->context; + struct hid_report_enum *report_enum = device->report_enum + HID_INPUT_REPORT; + struct hid_report *report; + __u8 *data = urb->transfer_buffer; + int len = urb->actual_length; + int n; + + if (urb->status) { + dbg("nonzero status in irq %d", urb->status); + return; + } + + if (!len) { + dbg("empty report"); + return; + } + +#ifdef DEBUG_DATA + printk(KERN_DEBUG __FILE__ ": report (size %u) (%snumbered) = ", len, report_enum->numbered ? "" : "un"); + for (n = 0; n < len; n++) + printk(" %02x", data[n]); + printk("\n"); +#endif + + n = 0; /* Normally report number is 0 */ + + if (report_enum->numbered) { /* Device uses numbered reports, data[0] is report number */ + n = *data++; + len--; + } + + if (!(report = report_enum->report_id_hash[n])) { + dbg("undefined report_id %d received", n); +#ifdef DEBUG + printk(KERN_DEBUG __FILE__ ": report (size %u) = ", len); + for (n = 0; n < len; n++) + printk(" %02x", data[n]); + printk("\n"); +#endif + + return; + } + + if (len < ((report->size - 1) >> 3) + 1) { + dbg("report %d is too short, (%d < %d)", report->id, len, ((report->size - 1) >> 3) + 1); + return; + } + + for (n = 0; n < report->maxfield; n++) + hid_input_field(device, report->field[n], data); + + return; +} + +/* + * hid_read_report() s intended to read the hid devices values even + * before the input device is registered, so that the userland interface + * modules start with real values. This is especially important for joydev.c + * automagic calibration. Doesn't work yet, though. Don't know why, the control + * request just times out on most devices I have and returns nonsense on others. + */ + +static void hid_read_report(struct hid_device *hid, struct hid_report *report) +{ +#if 0 + int rlen = ((report->size - 1) >> 3) + 1; + char rdata[rlen]; + struct urb urb; + int read, j; + + memset(&urb, 0, sizeof(struct urb)); + memset(rdata, 0, rlen); + + urb.transfer_buffer = rdata; + urb.actual_length = rlen; + urb.context = hid; + + dbg("getting report type %d id %d len %d", report->type + 1, report->id, rlen); + + if ((read = usb_get_report(hid->dev, hid->ifnum, report->type + 1, report->id, rdata, rlen)) != rlen) { + dbg("reading report failed rlen %d read %d", rlen, read); +#ifdef DEBUG + printk(KERN_DEBUG __FILE__ ": report = "); + for (j = 0; j < rlen; j++) printk(" %02x", rdata[j]); + printk("\n"); +#endif + return; + } + + hid_irq(&urb); +#endif +} + +/* + * Output the field into the report. + */ + +static void hid_output_field(struct hid_field *field, __u8 *data) +{ + unsigned count = field->report_count; + unsigned offset = field->report_offset; + unsigned size = field->report_size; + unsigned n; + + for (n = 0; n < count; n++) { + if (field->logical_minimum < 0) /* signed values */ + implement(data, offset + n * size, size, s32ton(field->value[n], size)); + else /* unsigned values */ + implement(data, offset + n * size, size, field->value[n]); + } +} + +/* + * Create a report. + */ + +void hid_output_report(struct hid_report *report, __u8 *data) +{ + unsigned n; + for (n = 0; n < report->maxfield; n++) + hid_output_field(report->field[n], data); +}; + +/* + * Set a field value. The report this field belongs to has to be + * created and transfered to the device, to set this value in the + * device. + */ + +int hid_set_field(struct hid_field *field, unsigned offset, __s32 value) +{ + unsigned size = field->report_size; + + hid_dump_input(field->usage + offset, value); + + if (offset >= field->report_count) { + dbg("offset exceeds report_count"); + return -1; + } + if (field->logical_minimum < 0) { + if (value != snto32(s32ton(value, size), size)) { + dbg("value %d is out of range", value); + return -1; + } + } + if ( (value > field->logical_maximum) + || (value < field->logical_minimum)) { + dbg("value %d is invalid", value); + return -1; + } + field->value[offset] = value; + return 0; +} + +static int hid_find_field(struct hid_device *hid, unsigned int type, unsigned int code, struct hid_field **field) +{ + struct hid_report_enum *report_enum = hid->report_enum + HID_OUTPUT_REPORT; + struct list_head *list = report_enum->report_list.next; + int i, j; + + while (list != &report_enum->report_list) { + struct hid_report *report = (struct hid_report *) list; + list = list->next; + for (i = 0; i < report->maxfield; i++) { + *field = report->field[i]; + for (j = 0; j < (*field)->maxusage; j++) + if ((*field)->usage[j].type == type && (*field)->usage[j].code == code) + return j; + } + } + return -1; +} + +static void hid_ctrl(struct urb *urb) +{ + if (urb->status) + warn("ctrl urb status %d received", urb->status); +} + +static int hid_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct hid_device *hid = dev->private; + struct hid_field *field = NULL; + int offset; + + if ((offset = hid_find_field(hid, type, code, &field)) == -1) { + warn("event field not found"); + return -1; + } + + hid_set_field(field, offset, value); + + if (hid->urbout.status == -EINPROGRESS) { + warn("had to kill output urb"); + usb_unlink_urb(&hid->urbout); + } + + hid_output_report(field->report, hid->bufout); + + hid->dr.value = 0x200 | field->report->id; + hid->dr.length = ((field->report->size - 1) >> 3) + 1; + hid->urbout.transfer_buffer_length = hid->dr.length; + + if (usb_submit_urb(&hid->urbout)) { + err("usb_submit_urb(out) failed"); + return -1; + } + + return 0; +} + +static int hid_open(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (hid->open++) + return 0; + + if (usb_submit_urb(&hid->urb)) + return -EIO; + + return 0; +} + +static void hid_close(struct input_dev *dev) +{ + struct hid_device *hid = dev->private; + + if (!--hid->open) + usb_unlink_urb(&hid->urb); +} + +/* + * Configure the input layer interface + * Read all reports and initalize the absoulte field values. + */ + +static void hid_init_input(struct hid_device *hid) +{ + struct hid_report_enum *report_enum; + struct list_head *list; + int i, j, k; + + hid->input.private = hid; + hid->input.event = hid_event; + hid->input.open = hid_open; + hid->input.close = hid_close; + + for (k = HID_INPUT_REPORT; k <= HID_OUTPUT_REPORT; k++) { + + report_enum = hid->report_enum + k; + list = report_enum->report_list.next; + + while (list != &report_enum->report_list) { + + struct hid_report *report = (struct hid_report *) list; + + list = list->next; + + for (i = 0; i < report->maxfield; i++) + for (j = 0; j < report->field[i]->maxusage; j++) + hid_configure_usage(hid, report->field[i], report->field[i]->usage + j); + + if (k == HID_INPUT_REPORT) { + usb_set_idle(hid->dev, hid->ifnum, 0, report->id); + hid_read_report(hid, report); + } + } + } +} + +#define USB_VENDOR_ID_WACOM 0x056a +#define USB_DEVICE_ID_WACOM_GRAPHIRE 0x0010 +#define USB_DEVICE_ID_WACOM_INTUOS 0x0020 + +struct hid_blacklist { + __u16 idVendor; + __u16 idProduct; +} hid_blacklist[] = { + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_GRAPHIRE }, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS }, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 1}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 2}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 3}, + { USB_VENDOR_ID_WACOM, USB_DEVICE_ID_WACOM_INTUOS + 4}, + { 0, 0 } +}; + +static struct hid_device *usb_hid_configure(struct usb_device *dev, int ifnum, char *name) +{ + struct usb_interface_descriptor *interface = dev->actconfig->interface[ifnum].altsetting + 0; + struct hid_descriptor *hdesc; + struct hid_device *hid; + unsigned rsize = 0; + int n; + + for (n = 0; hid_blacklist[n].idVendor; n++) + if ((hid_blacklist[n].idVendor == dev->descriptor.idVendor) && + (hid_blacklist[n].idProduct == dev->descriptor.idProduct)) return NULL; + + if (interface->bInterfaceClass != USB_INTERFACE_CLASS_HID) + return NULL; + + if (usb_get_extra_descriptor(interface, USB_DT_HID, &hdesc) + && usb_get_extra_descriptor(&interface->endpoint[0], USB_DT_HID, &hdesc)) { + dbg("class descriptor not present\n"); + return NULL; + } + + for (n = 0; n < hdesc->bNumDescriptors; n++) + if (hdesc->desc[n].bDescriptorType == USB_DT_REPORT) + rsize = le16_to_cpu(hdesc->desc[n].wDescriptorLength); + + if (!rsize || rsize > 1024) { + dbg("weird size of report descriptor (%u)", rsize); + return NULL; + } + + { + __u8 rdesc[rsize]; + + if ((n = usb_get_class_descriptor(dev, interface->bInterfaceNumber, USB_DT_REPORT, 0, rdesc, rsize)) < 0) { + dbg("reading report descriptor failed"); + return NULL; + } + +#ifdef DEBUG_DATA + printk(KERN_DEBUG __FILE__ ": report (size %u, read %d) = ", rsize, n); + for (n = 0; n < rsize; n++) + printk(" %02x", (unsigned) rdesc[n]); + printk("\n"); +#endif + + if (!(hid = hid_parse_report(rdesc, rsize))) { + dbg("parsing report descriptor failed"); + return NULL; + } + } + + for (n = 0; n < interface->bNumEndpoints; n++) { + + struct usb_endpoint_descriptor *endpoint = &interface->endpoint[n]; + int pipe, maxp; + + if ((endpoint->bmAttributes & 3) != 3) /* Not an interrupt endpoint */ + continue; + + if (!(endpoint->bEndpointAddress & 0x80)) /* Not an input endpoint */ + continue; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + FILL_INT_URB(&hid->urb, dev, pipe, hid->buffer, maxp > 32 ? 32 : maxp, hid_irq, hid, endpoint->bInterval); + + break; + } + + if (n == interface->bNumEndpoints) { + dbg("couldn't find an input interrupt endpoint"); + hid_free_device(hid); + return NULL; + } + + hid->version = hdesc->bcdHID; + hid->country = hdesc->bCountryCode; + hid->dev = dev; + hid->ifnum = interface->bInterfaceNumber; + + hid->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + hid->dr.request = USB_REQ_SET_REPORT; + hid->dr.value = 0x200; + hid->dr.index = hid->ifnum; + hid->dr.length = 1; + + hid->input.name = hid->name; + hid->input.idbus = BUS_USB; + hid->input.idvendor = dev->descriptor.idVendor; + hid->input.idproduct = dev->descriptor.idProduct; + hid->input.idversion = dev->descriptor.bcdDevice; + + if (strlen(name)) + strcpy(hid->name, name); + else + sprintf(hid->name, "USB HID %s %04x:%04x", + ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? + hid_types[hid->application & 0xffff] : "Device", + hid->input.idvendor, hid->input.idproduct); + + FILL_CONTROL_URB(&hid->urbout, dev, usb_sndctrlpipe(dev, 0), + (void*) &hid->dr, hid->bufout, 1, hid_ctrl, hid); + + if (interface->bInterfaceSubClass == 1) + usb_set_protocol(dev, hid->ifnum, 1); + + return hid; +} + +static void* hid_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct hid_device *hid; + char name[128]; + char *buf; + + dbg("HID probe called for ifnum %d", ifnum); + + name[0] = 0; + + if (!(buf = kmalloc(63, GFP_KERNEL))) + return NULL; + + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(name, "%s %s", name, buf); + + kfree(buf); + + if (!(hid = usb_hid_configure(dev, ifnum, name))) + return NULL; + + hid_dump_device(hid); + + hid_init_input(hid); + input_register_device(&hid->input); + + printk(KERN_INFO "input%d: USB HID v%x.%02x %s", + hid->input.number, + hid->version >> 8, hid->version & 0xff, + ((hid->application >= 0x00010000) && (hid->application <= 0x00010008)) ? + hid_types[hid->application & 0xffff] : "Device"); + + if (strlen(name)) + printk(" [%s]", name); + else + printk(" [%04x:%04x]", hid->input.idvendor, hid->input.idproduct); + + printk(" on usb%d:%d.%d\n", dev->bus->busnum, dev->devnum, ifnum); + + return hid; +} + +static void hid_disconnect(struct usb_device *dev, void *ptr) +{ + struct hid_device *hid = ptr; + + dbg("cleanup called"); + usb_unlink_urb(&hid->urb); + input_unregister_device(&hid->input); + hid_free_device(hid); +} + +static struct usb_driver hid_driver = { + name: "hid", + probe: hid_probe, + disconnect: hid_disconnect +}; + +static int __init hid_init(void) +{ + usb_register(&hid_driver); + return 0; +} + +static void __exit hid_exit(void) +{ + usb_deregister(&hid_driver); +} + +module_init(hid_init); +module_exit(hid_exit); diff --git a/drivers/usb/hid.h b/drivers/usb/hid.h new file mode 100644 index 000000000000..88ab5eacade3 --- /dev/null +++ b/drivers/usb/hid.h @@ -0,0 +1,330 @@ +#ifndef __HID_H +#define __HID_H + +/* + * $Id: hid.h,v 1.4 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999 Andreas Gal + * Copyright (c) 2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include + +/* + * USB HID (Human Interface Device) interface class code + */ + +#define USB_INTERFACE_CLASS_HID 3 + +/* + * We parse each description item into this structure. Short items data + * values are expanded to 32-bit signed int, long items contain a pointer + * into the data area. + */ + +struct hid_item { + unsigned format; + __u8 size; + __u8 type; + __u8 tag; + union { + __u8 u8; + __s8 s8; + __u16 u16; + __s16 s16; + __u32 u32; + __s32 s32; + __u8 *longdata; + } data; +}; + +/* + * HID report item format + */ + +#define HID_ITEM_FORMAT_SHORT 0 +#define HID_ITEM_FORMAT_LONG 1 + +/* + * Special tag indicating long items + */ + +#define HID_ITEM_TAG_LONG 15 + +/* + * HID report descriptor item type (prefix bit 2,3) + */ + +#define HID_ITEM_TYPE_MAIN 0 +#define HID_ITEM_TYPE_GLOBAL 1 +#define HID_ITEM_TYPE_LOCAL 2 +#define HID_ITEM_TYPE_RESERVED 3 + +/* + * HID report descriptor main item tags + */ + +#define HID_MAIN_ITEM_TAG_INPUT 8 +#define HID_MAIN_ITEM_TAG_OUTPUT 9 +#define HID_MAIN_ITEM_TAG_FEATURE 11 +#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION 10 +#define HID_MAIN_ITEM_TAG_END_COLLECTION 12 + +/* + * HID report descriptor main item contents + */ + +#define HID_MAIN_ITEM_CONSTANT 0x001 +#define HID_MAIN_ITEM_VARIABLE 0x002 +#define HID_MAIN_ITEM_RELATIVE 0x004 +#define HID_MAIN_ITEM_WRAP 0x008 +#define HID_MAIN_ITEM_NONLINEAR 0x010 +#define HID_MAIN_ITEM_NO_PREFERRED 0x020 +#define HID_MAIN_ITEM_NULL_STATE 0x040 +#define HID_MAIN_ITEM_VOLATILE 0x080 +#define HID_MAIN_ITEM_BUFFERED_BYTE 0x100 + +/* + * HID report descriptor collection item types + */ + +#define HID_COLLECTION_PHYSICAL 0 +#define HID_COLLECTION_APPLICATION 1 +#define HID_COLLECTION_LOGICAL 2 + +/* + * HID report descriptor global item tags + */ + +#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE 0 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM 1 +#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM 2 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM 3 +#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM 4 +#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT 5 +#define HID_GLOBAL_ITEM_TAG_UNIT 6 +#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE 7 +#define HID_GLOBAL_ITEM_TAG_REPORT_ID 8 +#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT 9 +#define HID_GLOBAL_ITEM_TAG_PUSH 10 +#define HID_GLOBAL_ITEM_TAG_POP 11 + +/* + * HID report descriptor local item tags + */ + +#define HID_LOCAL_ITEM_TAG_USAGE 0 +#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM 1 +#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM 2 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX 3 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM 4 +#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM 5 +#define HID_LOCAL_ITEM_TAG_STRING_INDEX 7 +#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM 8 +#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM 9 +#define HID_LOCAL_ITEM_TAG_DELIMITER 10 + +/* + * HID usage tables + */ + +#define HID_USAGE_PAGE 0xffff0000 + +#define HID_UP_GENDESK 0x00010000 +#define HID_UP_KEYBOARD 0x00070000 +#define HID_UP_LED 0x00080000 +#define HID_UP_BUTTON 0x00090000 +#define HID_UP_CONSUMER 0x000c0000 +#define HID_UP_DIGITIZER 0x000d0000 +#define HID_UP_PID 0x000f0000 + +#define HID_USAGE 0x0000ffff + +#define HID_GD_POINTER 0x00010001 +#define HID_GD_MOUSE 0x00010002 +#define HID_GD_JOYSTICK 0x00010004 +#define HID_GD_GAMEPAD 0x00010005 +#define HID_GD_HATSWITCH 0x00010039 + +/* + * HID report types --- Ouch! HID spec says 1 2 3! + */ + +#define HID_INPUT_REPORT 0 +#define HID_OUTPUT_REPORT 1 +#define HID_FEATURE_REPORT 2 + +/* + * HID device quirks. + */ + +#define HID_QUIRK_INVERT 0x01 +#define HID_QUIRK_NOTOUCH 0x02 + +/* + * This is the global enviroment of the parser. This information is + * persistent for main-items. The global enviroment can be saved and + * restored with PUSH/POP statements. + */ + +struct hid_global { + unsigned usage_page; + __s32 logical_minimum; + __s32 logical_maximum; + __s32 physical_minimum; + __s32 physical_maximum; + unsigned unit_exponent; + unsigned unit; + unsigned report_id; + unsigned report_size; + unsigned report_count; +}; + +/* + * This is the local enviroment. It is resistent up the the next main-item. + */ + +#define MAX_USAGES 1024 + +struct hid_local { + unsigned usage[MAX_USAGES]; /* usage array */ + unsigned usage_index; + unsigned usage_minimum; + unsigned delimiter_depth; + unsigned delimiter_branch; +}; + +/* + * This is the collection stack. We climb up the stack to determine + * application and function of each field. + */ + +struct hid_collection { + unsigned type; + unsigned usage; +}; + +struct hid_usage { + unsigned hid; /* hid usage code */ + __u16 code; /* input driver code */ + __u8 type; /* input driver type */ + __u8 hat; /* hat switch fun */ +}; + +struct hid_field { + unsigned physical; /* physical usage for this field */ + unsigned logical; /* logical usage for this field */ + struct hid_usage *usage; /* usage table for this function */ + unsigned maxusage; /* maximum usage index */ + unsigned flags; /* main-item flags (i.e. volatile,array,constant) */ + unsigned report_offset; /* bit offset in the report */ + unsigned report_size; /* size of this field in the report */ + unsigned report_count; /* number of this field in the report */ + unsigned report_type; /* (input,output,feature) */ + __s32 *value; /* last known value(s) */ + __s32 logical_minimum; + __s32 logical_maximum; + __s32 physical_minimum; + __s32 physical_maximum; + unsigned unit_exponent; + unsigned unit; + struct hid_report *report; /* associated report */ +}; + +#define HID_MAX_FIELDS 64 + +struct hid_report { + struct list_head list; + unsigned id; /* id of this report */ + unsigned type; /* report type */ + struct hid_field *field[HID_MAX_FIELDS]; /* fields of the report */ + unsigned maxfield; /* maximum valid field index */ + unsigned size; /* size of the report (bits) */ + struct hid_device *device; /* associated device */ +}; + +struct hid_report_enum { + unsigned numbered; + struct list_head report_list; + struct hid_report *report_id_hash[256]; +}; + +#define HID_REPORT_TYPES 3 + +struct hid_device { /* device report descriptor */ + __u8 *rdesc; + unsigned rsize; + unsigned application; /* HID application, i.e. Digitizer */ + unsigned version; /* HID version */ + unsigned country; /* HID country */ + struct hid_report_enum report_enum[HID_REPORT_TYPES]; + + struct usb_device *dev; /* USB device */ + int ifnum; /* USB interface number */ + + char buffer[32]; /* Receive buffer */ + char bufout[32]; /* Transmit buffer */ + devrequest dr; /* Startup packet */ + struct urb urb; /* USB URB structure */ + struct urb urbout; /* Output URB */ + struct input_dev input; /* input device structure */ + int open; /* is the device open by input? */ + int quirks; /* Various nasty tricks the device can pull on us */ + char name[128]; /* Device name */ +}; + +#define HID_GLOBAL_STACK_SIZE 4 +#define HID_COLLECTION_STACK_SIZE 4 + +struct hid_parser { + struct hid_global global; + struct hid_global global_stack[HID_GLOBAL_STACK_SIZE]; + unsigned global_stack_ptr; + struct hid_local local; + struct hid_collection collection_stack[HID_COLLECTION_STACK_SIZE]; + unsigned collection_stack_ptr; + struct hid_device *device; +}; + +struct hid_class_descriptor { + __u8 bDescriptorType; + __u16 wDescriptorLength; +} __attribute__ ((packed)); + +struct hid_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u16 bcdHID; + __u8 bCountryCode; + __u8 bNumDescriptors; + + struct hid_class_descriptor desc[1]; +} __attribute__ ((packed)); + + +#endif + diff --git a/drivers/usb/hub.c b/drivers/usb/hub.c index 16789b9445c0..fef8830ba481 100644 --- a/drivers/usb/hub.c +++ b/drivers/usb/hub.c @@ -1,220 +1,317 @@ /* * USB hub driver. * - * This is horrible, it knows about the UHCI driver - * internals, but it's just meant as a rough example, - * let's do the virtualization later when this works. - * * (C) Copyright 1999 Linus Torvalds * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Gregory P. Smith */ +#include #include #include #include #include #include +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include +#include -#include +#include +#include +#include -#include "usb.h" -#include "uhci.h" #include "hub.h" -extern struct usb_operations uhci_device_operations; - /* Wakes up khubd */ -static struct wait_queue *usb_hub_wait = NULL; static spinlock_t hub_event_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_MUTEX(usb_address0_sem); + +static LIST_HEAD(hub_event_list); /* List of hubs needing servicing */ +static LIST_HEAD(hub_list); /* List containing all of the hubs (for cleanup) */ + +static DECLARE_WAIT_QUEUE_HEAD(khubd_wait); +static int khubd_pid = 0; /* PID of khubd */ +static int khubd_running = 0; + +static int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB, + USB_DT_HUB << 8, 0, data, size, HZ); +} + +static int usb_clear_hub_feature(struct usb_device *dev, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_HUB, feature, 0, NULL, 0, HZ); +} + +static int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); +} + +static int usb_set_port_feature(struct usb_device *dev, int port, int feature) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_FEATURE, USB_RT_PORT, feature, port, NULL, 0, HZ); +} -/* List of hubs needing servicing */ -static struct list_head hub_event_list; +static int usb_get_hub_status(struct usb_device *dev, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB, 0, 0, + data, sizeof(struct usb_hub_status), HZ); +} -/* PID of khubd */ -static int khubd_pid = 0; +static int usb_get_port_status(struct usb_device *dev, int port, void *data) +{ + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT, 0, port, + data, sizeof(struct usb_hub_status), HZ); +} /* * A irq handler returns non-zero to indicate to * the low-level driver that it wants to be re-activated, * or zero to say "I'm done". */ -static int hub_irq(int status, void *__buffer, void *dev_id) +static void hub_irq(struct urb *urb) { - struct usb_hub *hub = dev_id; + struct usb_hub *hub = (struct usb_hub *)urb->context; unsigned long flags; - if (waitqueue_active(&usb_hub_wait)) { + if (urb->status) { + if (urb->status != -ENOENT) + dbg("nonzero status in irq %d", urb->status); + + return; + } + + /* Something happened, let khubd figure it out */ + if (waitqueue_active(&khubd_wait)) { /* Add the hub to the event queue */ spin_lock_irqsave(&hub_event_lock, flags); if (hub->event_list.next == &hub->event_list) { list_add(&hub->event_list, &hub_event_list); /* Wake up khubd */ - wake_up(&usb_hub_wait); + wake_up(&khubd_wait); } spin_unlock_irqrestore(&hub_event_lock, flags); } +} + +static void usb_hub_power_on(struct usb_hub *hub) +{ + int i; - return 1; + /* Enable power to the ports */ + dbg("enabling power on all ports"); + for (i = 0; i < hub->nports; i++) + usb_set_port_feature(hub->dev, i + 1, USB_PORT_FEAT_POWER); } -static void usb_hub_configure(struct usb_hub *hub) +static int usb_hub_configure(struct usb_hub *hub) { struct usb_device *dev = hub->dev; - unsigned char hubdescriptor[8], buf[4]; - int charac, i; + unsigned char buffer[HUB_DESCRIPTOR_MAX_SIZE], *bitmap; + struct usb_hub_descriptor *descriptor; + struct usb_descriptor_header *header; + struct usb_hub_status *hubsts; + int i, ret; + + /* Request the entire hub descriptor. */ + header = (struct usb_descriptor_header *)buffer; + ret = usb_get_hub_descriptor(dev, buffer, sizeof(buffer)); + /* is large enough for a hub with 127 ports; + * the hub can/will return fewer bytes here. */ + if (ret < 0) { + err("Unable to get hub descriptor (err = %d)", ret); + return -1; + } - usb_set_configuration(dev, dev->config[0].bConfigurationValue); + bitmap = kmalloc(header->bLength, GFP_KERNEL); + if (!bitmap) { + err("Unable to kmalloc %d bytes for bitmap", header->bLength); + return -1; + } - if (usb_get_hub_descriptor(dev, hubdescriptor, 8)) - return; + memcpy (bitmap, buffer, header->bLength); + descriptor = (struct usb_hub_descriptor *)bitmap; - hub->nports = dev->maxchild = hubdescriptor[2]; - printk("hub: %d-port%s detected\n", hub->nports, - (hub->nports == 1) ? "" : "s"); + hub->nports = dev->maxchild = descriptor->bNbrPorts; + info("%d port%s detected", hub->nports, (hub->nports == 1) ? "" : "s"); - charac = (hubdescriptor[4] << 8) + hubdescriptor[3]; - switch (charac & HUB_CHAR_LPSM) { + switch (descriptor->wHubCharacteristics & HUB_CHAR_LPSM) { case 0x00: - printk("hub: ganged power switching\n"); + dbg("ganged power switching"); break; case 0x01: - printk("hub: individual port power switching\n"); + dbg("individual port power switching"); break; case 0x02: case 0x03: - printk("hub: unknown reserved power switching mode\n"); + dbg("unknown reserved power switching mode"); break; } - if (charac & HUB_CHAR_COMPOUND) - printk("hub: part of a compound device\n"); + if (descriptor->wHubCharacteristics & HUB_CHAR_COMPOUND) + dbg("part of a compound device"); else - printk("hub: standalone hub\n"); + dbg("standalone hub"); - switch (charac & HUB_CHAR_OCPM) { + switch (descriptor->wHubCharacteristics & HUB_CHAR_OCPM) { case 0x00: - printk("hub: global over current protection\n"); + dbg("global over-current protection"); break; case 0x08: - printk("hub: individual port over current protection\n"); + dbg("individual port over-current protection"); break; case 0x10: case 0x18: - printk("hub: no over current protection\n"); + dbg("no over-current protection"); break; } - printk("hub: power on to power good time: %dms\n", - hubdescriptor[5] * 2); - - printk("hub: hub controller current requirement: %dmA\n", - hubdescriptor[6]); + dbg("power on to power good time: %dms", descriptor->bPwrOn2PwrGood * 2); + dbg("hub controller current requirement: %dmA", descriptor->bHubContrCurrent); for (i = 0; i < dev->maxchild; i++) - printk("hub: port %d is%s removable\n", i + 1, - hubdescriptor[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) + dbg("port %d is%s removable", i + 1, + bitmap[7 + ((i + 1)/8)] & (1 << ((i + 1) % 8)) ? " not" : ""); - if (usb_get_hub_status(dev, buf)) - return; + kfree(bitmap); - printk("hub: local power source is %s\n", - (buf[0] & 1) ? "lost (inactive)" : "good"); + ret = usb_get_hub_status(dev, buffer); + if (ret < 0) { + err("Unable to get hub status (err = %d)", ret); + return -1; + } - printk("hub: %sover current condition exists\n", - (buf[0] & 2) ? "" : "no "); + hubsts = (struct usb_hub_status *)buffer; + dbg("local power source is %s", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_LOCAL_POWER) ? "lost (inactive)" : "good"); -#if 0 - for (i = 0; i < hub->nports; i++) { - int portstat, portchange; - unsigned char portstatus[4]; + dbg("%sover-current condition exists", + (le16_to_cpu(hubsts->wHubStatus) & HUB_STATUS_OVERCURRENT) ? "" : "no "); - if (usb_get_port_status(dev, i + 1, portstatus)) - return; - portstat = (portstatus[1] << 8) + portstatus[0]; - portchange = (portstatus[3] << 8) + portstatus[2]; - - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstat & 1) ? "" : "no "); - printk("hub: %s\n", (portstat & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstat & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstat & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstat & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstat & 0x200) ? "low" : "full"); - } -#endif + usb_hub_power_on(hub); - /* Enable power to the ports */ - printk("enabling power on all ports\n"); - for (i = 0; i < hub->nports; i++) - usb_set_port_feature(dev, i + 1, USB_PORT_FEAT_POWER); + return 0; } -static int hub_probe(struct usb_device *dev) +static void *hub_probe(struct usb_device *dev, unsigned int i) { struct usb_interface_descriptor *interface; struct usb_endpoint_descriptor *endpoint; struct usb_hub *hub; + unsigned long flags; + unsigned int pipe; + int maxp, ret; - /* We don't handle multi-config hubs */ - if (dev->descriptor.bNumConfigurations != 1) - return -1; - - /* We don't handle multi-interface hubs */ - if (dev->config[0].bNumInterfaces != 1) - return -1; - - interface = &dev->config[0].interface[0]; + interface = &dev->actconfig->interface[i].altsetting[0]; /* Is it a hub? */ - if (interface->bInterfaceClass != 9) - return -1; + if (interface->bInterfaceClass != USB_CLASS_HUB) + return NULL; + + /* Some hubs have a subclass of 1, which AFAICT according to the */ + /* specs is not defined, but it works */ if ((interface->bInterfaceSubClass != 0) && (interface->bInterfaceSubClass != 1)) - return -1; + return NULL; /* Multiple endpoints? What kind of mutant ninja-hub is this? */ if (interface->bNumEndpoints != 1) - return -1; + return NULL; endpoint = &interface->endpoint[0]; /* Output endpoint? Curiousier and curiousier.. */ - if (!(endpoint->bEndpointAddress & 0x80)) - return -1; + if (!(endpoint->bEndpointAddress & USB_DIR_IN)) { + err("Device is hub class, but has output endpoint?"); + return NULL; + } /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) - return -1; + if ((endpoint->bmAttributes & 3) != 3) { + err("Device is hub class, but has endpoint other than interrupt?"); + return NULL; + } /* We found a hub */ - printk("USB hub found\n"); + info("USB hub found"); - if ((hub = kmalloc(sizeof(*hub), GFP_KERNEL)) == NULL) { - printk("couldn't kmalloc hub struct\n"); - return -1; + hub = kmalloc(sizeof(*hub), GFP_KERNEL); + if (!hub) { + err("couldn't kmalloc hub struct"); + return NULL; } memset(hub, 0, sizeof(*hub)); - dev->private = hub; - INIT_LIST_HEAD(&hub->event_list); hub->dev = dev; - usb_hub_configure(hub); + /* Record the new hub's existence */ + spin_lock_irqsave(&hub_event_lock, flags); + INIT_LIST_HEAD(&hub->hub_list); + list_add(&hub->hub_list, &hub_list); + spin_unlock_irqrestore(&hub_event_lock, flags); - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), hub_irq, endpoint->bInterval, hub); + if (usb_hub_configure(hub) >= 0) { + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); - /* Wake up khubd */ - wake_up(&usb_hub_wait); + if (maxp > sizeof(hub->buffer)) + maxp = sizeof(hub->buffer); - return 0; + hub->urb = usb_alloc_urb(0); + if (!hub->urb) { + err("couldn't allocate interrupt urb"); + goto fail; + } + + FILL_INT_URB(hub->urb, dev, pipe, hub->buffer, maxp, hub_irq, + hub, endpoint->bInterval); + ret = usb_submit_urb(hub->urb); + if (ret) { + err("usb_submit_urb failed (%d)", ret); + goto fail; + } + + /* Wake up khubd */ + wake_up(&khubd_wait); + } + + return hub; + +fail: + /* free hub, but first clean up its list. */ + spin_lock_irqsave(&hub_event_lock, flags); + + /* Delete it and then reset it */ + list_del(&hub->event_list); + INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); + + spin_unlock_irqrestore(&hub_event_lock, flags); + + kfree(hub); + + return NULL; } -static void hub_disconnect(struct usb_device *dev) +static void hub_disconnect(struct usb_device *dev, void *ptr) { - struct usb_hub *hub = dev->private; + struct usb_hub *hub = (struct usb_hub *)ptr; unsigned long flags; spin_lock_irqsave(&hub_event_lock, flags); @@ -222,201 +319,478 @@ static void hub_disconnect(struct usb_device *dev) /* Delete it and then reset it */ list_del(&hub->event_list); INIT_LIST_HEAD(&hub->event_list); + list_del(&hub->hub_list); + INIT_LIST_HEAD(&hub->hub_list); spin_unlock_irqrestore(&hub_event_lock, flags); + if (hub->urb) { + usb_unlink_urb(hub->urb); + usb_free_urb(hub->urb); + hub->urb = NULL; + } + /* Free the memory */ kfree(hub); } +static int hub_ioctl (struct usb_device *hub, unsigned int code, void *user_data) +{ + /* assert ifno == 0 (part of hub spec) */ + switch (code) { + case USBDEVFS_HUB_PORTINFO: { + struct usbdevfs_hub_portinfo *info = user_data; + unsigned long flags; + int i; + + spin_lock_irqsave (&hub_event_lock, flags); + if (hub->devnum <= 0) + info->nports = 0; + else { + info->nports = hub->maxchild; + for (i = 0; i < info->nports; i++) { + if (hub->children [i] == NULL) + info->port [i] = 0; + else + info->port [i] = hub->children [i]->devnum; + } + } + spin_unlock_irqrestore (&hub_event_lock, flags); + + return info->nports + 1; + } + + default: + return -ENOSYS; + } +} + static void usb_hub_port_connect_change(struct usb_device *hub, int port) { struct usb_device *usb; - unsigned char buf[4]; + struct usb_port_status portsts; unsigned short portstatus, portchange; + int ret, tries; - usb_disconnect(&hub->children[port]); + wait_ms(100); + + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + return; + } - usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dbg("portstatus %x, change %x, %s", portstatus, portchange, + portstatus&(1<children[port])) { + usb_disconnect(&hub->children[port]); + /* Return now if nothing is connected */ + if (!(portstatus & USB_PORT_STAT_CONNECTION)) + return; } + wait_ms(400); - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + down(&usb_address0_sem); - if ((!(portstatus & USB_PORT_STAT_CONNECTION)) && - (!(portstatus & USB_PORT_STAT_ENABLE))) { - /* We're done now, we already disconnected the device */ - /* printk("not connected/enabled\n"); */ - return; +#define MAX_TRIES 5 + /* Reset the port */ + for (tries = 0; tries < MAX_TRIES ; tries++) { + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + wait_ms(200); + + ret = usb_get_port_status(hub, port + 1, &portsts); + if (ret < 0) { + err("get_port_status(%d) failed (err = %d)", port + 1, ret); + goto out; + } + + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); + dbg("portstatus %x, change %x, %s", portstatus ,portchange, + portstatus&(1<bus->op->allocate(hub); - if (!usb) { - printk("couldn't allocate usb_device\n"); - return; + if (tries >= MAX_TRIES) { + err("Cannot enable port %i after %i retries, disabling port.", port+1, MAX_TRIES); + err("Maybe the USB cable is bad?"); + goto out; } - usb_connect(usb); + usb_clear_port_feature(hub, port + 1, USB_PORT_FEAT_C_RESET); + + /* Allocate a new device struct for it */ + usb = usb_alloc_dev(hub, hub->bus); + if (!usb) { + err("couldn't allocate usb_device"); + goto out; + } usb->slow = (portstatus & USB_PORT_STAT_LOW_SPEED) ? 1 : 0; hub->children[port] = usb; - usb_new_device(usb); + /* Find a new device ID for it */ + usb_connect(usb); + + /* Run it through the hoops (find a driver, etc) */ + ret = usb_new_device(usb); + if (ret && usb->devnum != -1) { + /* Try resetting the device. Windows does this and it */ + /* gets some devices working correctly */ + usb_set_port_feature(hub, port + 1, USB_PORT_FEAT_RESET); + + ret = usb_new_device(usb); + if (ret) { + usb_disconnect(&hub->children[port]); + + /* Woops, disable the port */ + dbg("hub: disabling port %d", port + 1); + usb_clear_port_feature(hub, port + 1, + USB_PORT_FEAT_ENABLE); + } + } + +out: + up(&usb_address0_sem); } static void usb_hub_events(void) { unsigned long flags; - unsigned char buf[4]; - unsigned short portstatus, portchange; int i; - struct list_head *next, *tmp, *head = &hub_event_list; + struct list_head *tmp; struct usb_device *dev; struct usb_hub *hub; + struct usb_hub_status hubsts; + unsigned short hubstatus, hubchange; - spin_lock_irqsave(&hub_event_lock, flags); + /* + * We restart the list everytime to avoid a deadlock with + * deleting hubs downstream from this one. This should be + * safe since we delete the hub from the event list. + * Not the most efficient, but avoids deadlocks. + */ + while (1) { + spin_lock_irqsave(&hub_event_lock, flags); + + if (list_empty(&hub_event_list)) + goto he_unlock; + + /* Grab the next entry from the beginning of the list */ + tmp = hub_event_list.next; - tmp = head->next; - while (tmp != head) { hub = list_entry(tmp, struct usb_hub, event_list); dev = hub->dev; - next = tmp->next; - list_del(tmp); INIT_LIST_HEAD(tmp); + spin_unlock_irqrestore(&hub_event_lock, flags); + for (i = 0; i < hub->nports; i++) { - if (usb_get_port_status(dev, i + 1, buf)) { - printk("get_port_status failed\n"); + struct usb_port_status portsts; + unsigned short portstatus, portchange; + + if (usb_get_port_status(dev, i + 1, &portsts) < 0) { + err("get_port_status failed"); continue; } - portstatus = *((unsigned short *)buf + 0); - portchange = *((unsigned short *)buf + 1); + portstatus = le16_to_cpu(portsts.wPortStatus); + portchange = le16_to_cpu(portsts.wPortChange); if (portchange & USB_PORT_STAT_C_CONNECTION) { - printk("hub: port %d connection change\n", i + 1); - - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_CONNECTION); + dbg("port %d connection change", i + 1); usb_hub_port_connect_change(dev, i); } if (portchange & USB_PORT_STAT_C_ENABLE) { - printk("hub: port %d enable change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_ENABLE); + dbg("port %d enable change, status %x", i + 1, portstatus); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_ENABLE); + + // EM interference sometimes causes bad shielded USB devices to + // be shutdown by the hub, this hack enables them again. + // Works at least with mouse driver. + if (!(portstatus & USB_PORT_STAT_ENABLE) && + (portstatus & USB_PORT_STAT_CONNECTION) && (dev->children[i])) { + err("already running port %i disabled by hub (EMI?), re-enabling...", + i + 1); + usb_hub_port_connect_change(dev, i); + } } - if (portchange & USB_PORT_STAT_C_SUSPEND) - printk("hub: port %d suspend change\n", i + 1); - - if (portchange & USB_PORT_STAT_C_OVERCURRENT) - printk("hub: port %d over-current change\n", i + 1); + if (portstatus & USB_PORT_STAT_SUSPEND) { + dbg("port %d suspend change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_SUSPEND); + } + + if (portchange & USB_PORT_STAT_C_OVERCURRENT) { + err("port %d over-current change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_OVER_CURRENT); + usb_hub_power_on(hub); + } if (portchange & USB_PORT_STAT_C_RESET) { - printk("hub: port %d reset change\n", i + 1); - usb_clear_port_feature(dev, i + 1, - USB_PORT_FEAT_C_RESET); + dbg("port %d reset change", i + 1); + usb_clear_port_feature(dev, i + 1, USB_PORT_FEAT_C_RESET); + } + } /* end for i */ + + /* deal with hub status changes */ + if (usb_get_hub_status(dev, &hubsts) < 0) { + err("get_hub_status failed"); + } else { + hubstatus = le16_to_cpup(&hubsts.wHubStatus); + hubchange = le16_to_cpup(&hubsts.wHubChange); + if (hubchange & HUB_CHANGE_LOCAL_POWER) { + dbg("hub power change"); + usb_clear_hub_feature(dev, C_HUB_LOCAL_POWER); + } + if (hubchange & HUB_CHANGE_OVERCURRENT) { + dbg("hub overcurrent change"); + wait_ms(500); //Cool down + usb_clear_hub_feature(dev, C_HUB_OVER_CURRENT); + usb_hub_power_on(hub); } - -#if 0 - if (!portchange) - continue; - - if (usb_get_port_status(dev, i + 1, buf)) - return; - - portstatus = (buf[1] << 8) + buf[0]; - portchange = (buf[3] << 8) + buf[2]; - - printk("hub: port %d status\n", i + 1); - printk("hub: %sdevice present\n", (portstatus & 1) ? "" : "no "); - printk("hub: %s\n", (portstatus & 2) ? "enabled" : "disabled"); - printk("hub: %ssuspended\n", (portstatus & 4) ? "" : "not "); - printk("hub: %sover current\n", (portstatus & 8) ? "" : "not "); - printk("hub: has %spower\n", (portstatus & 0x100) ? "" : "no "); - printk("hub: %s speed\n", (portstatus & 0x200) ? "low" : "full"); -#endif } - tmp = next; -#if 0 - wait_ms(1000); -#endif - } + } /* end while (1) */ +he_unlock: spin_unlock_irqrestore(&hub_event_lock, flags); } static int usb_hub_thread(void *__hub) { + khubd_running = 1; + lock_kernel(); /* * This thread doesn't need any user-level access, * so get rid of all our resources */ - printk("usb_hub_thread at %p\n", &usb_hub_thread); - exit_mm(current); - exit_files(current); - exit_fs(current); + exit_files(current); /* daemonize doesn't do exit_files */ + daemonize(); /* Setup a nice name */ strcpy(current->comm, "khubd"); /* Send me a signal to get me die (for debugging) */ do { - interruptible_sleep_on(&usb_hub_wait); usb_hub_events(); + interruptible_sleep_on(&khubd_wait); } while (!signal_pending(current)); - printk("usb_hub_thread exiting\n"); + dbg("usb_hub_thread exiting"); + khubd_running = 0; return 0; } static struct usb_driver hub_driver = { - "hub", - hub_probe, - hub_disconnect, - { NULL, NULL } + name: "hub", + probe: hub_probe, + ioctl: hub_ioctl, + disconnect: hub_disconnect }; /* * This should be a separate module. */ -int hub_init(void) +int usb_hub_init(void) { int pid; - INIT_LIST_HEAD(&hub_event_list); + if (usb_register(&hub_driver) < 0) { + err("Unable to register USB hub driver"); + return -1; + } - usb_register(&hub_driver); - pid = kernel_thread(usb_hub_thread, NULL, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); + pid = kernel_thread(usb_hub_thread, NULL, + CLONE_FS | CLONE_FILES | CLONE_SIGHAND); if (pid >= 0) { khubd_pid = pid; + return 0; } /* Fall through if kernel_thread failed */ usb_deregister(&hub_driver); - return 0; + return -1; } -void hub_cleanup(void) +void usb_hub_cleanup(void) { - if (khubd_pid >= 0) - kill_proc(khubd_pid, SIGINT, 1); + int ret; + + /* Kill the thread */ + ret = kill_proc(khubd_pid, SIGTERM, 1); + if (!ret) { + /* Wait 10 seconds */ + int count = 10 * 100; + + while (khubd_running && --count) { + current->state = TASK_INTERRUPTIBLE; + schedule_timeout(1); + } + + if (!count) + err("giving up on killing khubd"); + } + /* + * Hub resources are freed for us by usb_deregister. It + * usb_driver_purge on every device which in turn calls that + * devices disconnect function if it is using this driver. + * The hub_disconnect function takes care of releasing the + * individual hub resources. -greg + */ usb_deregister(&hub_driver); +} /* usb_hub_cleanup() */ + +/* + * WARNING - If a driver calls usb_reset_device, you should simulate a + * disconnect() and probe() for other interfaces you doesn't claim. This + * is left up to the driver writer right now. This insures other drivers + * have a chance to re-setup their interface. + * + * Take a look at proc_resetdevice in devio.c for some sample code to + * do this. + */ +int usb_reset_device(struct usb_device *dev) +{ + struct usb_device *parent = dev->parent; + struct usb_device_descriptor descriptor; + int i, ret, port = -1; + + if (!parent) { + err("attempting to reset root hub!"); + return -EINVAL; + } + + for (i = 0; i < parent->maxchild; i++) + if (parent->children[i] == dev) { + port = i; + break; + } + + if (port < 0) + return -ENOENT; + + down(&usb_address0_sem); + + /* Send a reset to the device */ + usb_set_port_feature(parent, port + 1, USB_PORT_FEAT_RESET); + + wait_ms(200); + + usb_clear_port_feature(parent, port + 1, USB_PORT_FEAT_C_RESET); + + /* Reprogram the Address */ + ret = usb_set_address(dev); + if (ret < 0) { + err("USB device not accepting new address (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + up(&usb_address0_sem); + return ret; + } + + wait_ms(10); /* Let the SET_ADDRESS settle */ + + up(&usb_address0_sem); + + /* + * Now we fetch the configuration descriptors for the device and + * see if anything has changed. If it has, we dump the current + * parsed descriptors and reparse from scratch. Then we leave + * the device alone for the caller to finish setting up. + * + * If nothing changed, we reprogram the configuration and then + * the alternate settings. + */ + ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &descriptor, + sizeof(descriptor)); + if (ret < 0) + return ret; + + le16_to_cpus(&descriptor.bcdUSB); + le16_to_cpus(&descriptor.idVendor); + le16_to_cpus(&descriptor.idProduct); + le16_to_cpus(&descriptor.bcdDevice); + + if (memcmp(&dev->descriptor, &descriptor, sizeof(descriptor))) { + usb_destroy_configuration(dev); + + ret = usb_get_device_descriptor(dev); + if (ret < sizeof(dev->descriptor)) { + if (ret < 0) + err("unable to get device descriptor (error=%d)", ret); + else + err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), ret); + + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return -EIO; + } + + ret = usb_get_configuration(dev); + if (ret < 0) { + err("unable to get configuration (error=%d)", ret); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; + } + + dev->actconfig = dev->config; + usb_set_maxpacket(dev); + + return 1; + } else { + ret = usb_set_configuration(dev, + dev->actconfig->bConfigurationValue); + if (ret < 0) { + err("failed to set active configuration (error=%d)", + ret); + return ret; + } + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *intf = + &dev->actconfig->interface[i]; + struct usb_interface_descriptor *as = + &intf->altsetting[intf->act_altsetting]; + + ret = usb_set_interface(dev, as->bInterfaceNumber, + as->bAlternateSetting); + if (ret < 0) { + err("failed to set active alternate setting for interface %d (error=%d)", i, ret); + return ret; + } + } + + return 0; + } + + return 0; } + diff --git a/drivers/usb/hub.h b/drivers/usb/hub.h index d015c5a33063..1be8422dae7e 100644 --- a/drivers/usb/hub.h +++ b/drivers/usb/hub.h @@ -4,7 +4,14 @@ #include /* - * Hub feature numbers + * Hub request types + */ + +#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) +#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) + +/* + * Hub Class feature numbers */ #define C_HUB_LOCAL_POWER 0 #define C_HUB_OVER_CURRENT 1 @@ -12,6 +19,7 @@ /* * Port feature numbers */ +#define USB_PORT_FEAT_CONNECTION 0 #define USB_PORT_FEAT_ENABLE 1 #define USB_PORT_FEAT_SUSPEND 2 #define USB_PORT_FEAT_OVER_CURRENT 3 @@ -24,7 +32,12 @@ #define USB_PORT_FEAT_C_OVER_CURRENT 19 #define USB_PORT_FEAT_C_RESET 20 -/* wPortStatus */ +struct usb_port_status { + __u16 wPortStatus; + __u16 wPortChange; +} __attribute__ ((packed)); + +/* wPortStatus bits */ #define USB_PORT_STAT_CONNECTION 0x0001 #define USB_PORT_STAT_ENABLE 0x0002 #define USB_PORT_STAT_SUSPEND 0x0004 @@ -33,18 +46,46 @@ #define USB_PORT_STAT_POWER 0x0100 #define USB_PORT_STAT_LOW_SPEED 0x0200 -/* wPortChange */ +/* wPortChange bits */ #define USB_PORT_STAT_C_CONNECTION 0x0001 #define USB_PORT_STAT_C_ENABLE 0x0002 #define USB_PORT_STAT_C_SUSPEND 0x0004 #define USB_PORT_STAT_C_OVERCURRENT 0x0008 #define USB_PORT_STAT_C_RESET 0x0010 -/* Characteristics */ +/* wHubCharacteristics (masks) */ #define HUB_CHAR_LPSM 0x0003 #define HUB_CHAR_COMPOUND 0x0004 #define HUB_CHAR_OCPM 0x0018 +struct usb_hub_status { + __u16 wHubStatus; + __u16 wHubChange; +} __attribute__ ((packed)); + +/* + *Hub Status & Hub Change bit masks + */ +#define HUB_STATUS_LOCAL_POWER 0x0001 +#define HUB_STATUS_OVERCURRENT 0x0002 + +#define HUB_CHANGE_LOCAL_POWER 0x0001 +#define HUB_CHANGE_OVERCURRENT 0x0002 + +#define HUB_DESCRIPTOR_MAX_SIZE 39 /* enough for 127 ports on a hub */ + +/* Hub descriptor */ +struct usb_hub_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bNbrPorts; + __u16 wHubCharacteristics; + __u8 bPwrOn2PwrGood; + __u8 bHubContrCurrent; + /* DeviceRemovable and PortPwrCtrlMask want to be variable-length + bitmaps that hold max 256 entries, but for now they're ignored */ +} __attribute__ ((packed)); + struct usb_device; typedef enum { @@ -67,6 +108,14 @@ struct usb_hub { /* Device structure */ struct usb_device *dev; + /* Interrupt polling pipe */ + struct urb *urb; + + char buffer[USB_MAXCHILDREN / 8]; + + /* List of hubs */ + struct list_head hub_list; + /* Temporary event list */ struct list_head event_list; @@ -77,4 +126,3 @@ struct usb_hub { }; #endif - diff --git a/drivers/usb/ibmcam.c b/drivers/usb/ibmcam.c new file mode 100644 index 000000000000..f643dee4751c --- /dev/null +++ b/drivers/usb/ibmcam.c @@ -0,0 +1,3146 @@ +/* + * USB IBM C-It Video Camera driver + * + * Supports IBM C-It Video Camera. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * 5/24/00 Removed optional (and unnecessary) locking of the driver while + * the device remains plugged in. Corrected race conditions in ibmcam_open + * and ibmcam_probe() routines using this as a guideline: + * + * (2) The big kernel lock is automatically released when a process sleeps + * in the kernel and is automatically reacquired on reschedule if the + * process had the lock originally. Any code that can be compiled as + * a module and is entered with the big kernel lock held *MUST* + * increment the use count to activate the indirect module protection + * before doing anything that might sleep. + * + * In practice, this means that all routines that live in modules and + * are invoked under the big kernel lock should do MOD_INC_USE_COUNT + * as their very first action. And all failure paths from that + * routine must do MOD_DEC_USE_COUNT before returning. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "ibmcam.h" + +#define ENABLE_HEXDUMP 0 /* Enable if you need it */ +static int debug = 0; + +/* Completion states of the data parser */ +typedef enum { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +} scan_state_t; + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) + +static int flags = 0; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */ + +/* This is the size of V4L frame that we provide */ +static const int imgwidth = V4L_FRAME_WIDTH_USED; +static const int imgheight = V4L_FRAME_HEIGHT; +static const int min_imgwidth = 8; +static const int min_imgheight = 4; + +static int lighting = 1; /* Medium */ + +#define SHARPNESS_MIN 0 +#define SHARPNESS_MAX 6 +static int sharpness = 4; /* Low noise, good details */ + +#define FRAMERATE_MIN 0 +#define FRAMERATE_MAX 6 +static int framerate = 2; /* Lower, reliable frame rate (8-12 fps) */ + +enum { + VIDEOSIZE_128x96 = 0, + VIDEOSIZE_176x144, + VIDEOSIZE_352x288, + VIDEOSIZE_320x240, + VIDEOSIZE_352x240, +}; + +static int videosize = VIDEOSIZE_352x288; + +/* + * The value of 'scratchbufsize' affects quality of the picture + * in many ways. Shorter buffers may cause loss of data when client + * is too slow. Larger buffers are memory-consuming and take longer + * to work with. This setting can be adjusted, but the default value + * should be OK for most desktop users. + */ +#define DEFAULT_SCRATCH_BUF_SIZE (0x10000) /* 64 KB */ +static const int scratchbufsize = DEFAULT_SCRATCH_BUF_SIZE; + +/* + * Here we define several initialization variables. They may + * be used to automatically set color, hue, brightness and + * contrast to desired values. This is particularly useful in + * case of webcams (which have no controls and no on-screen + * output) and also when a client V4L software is used that + * does not have some of those controls. In any case it's + * good to have startup values as options. + * + * These values are all in [0..255] range. This simplifies + * operation. Note that actual values of V4L variables may + * be scaled up (as much as << 8). User can see that only + * on overlay output, however, or through a V4L client. + */ +static int init_brightness = 128; +static int init_contrast = 192; +static int init_color = 128; +static int init_hue = 128; +static int hue_correction = 128; + +/* Settings for camera model 2 */ +static int init_model2_rg = -1; +static int init_model2_rg2 = -1; +static int init_model2_sat = -1; +static int init_model2_yb = -1; + +MODULE_PARM(debug, "i"); +MODULE_PARM(flags, "i"); +MODULE_PARM(framerate, "i"); +MODULE_PARM(lighting, "i"); +MODULE_PARM(sharpness, "i"); +MODULE_PARM(videosize, "i"); +MODULE_PARM(init_brightness, "i"); +MODULE_PARM(init_contrast, "i"); +MODULE_PARM(init_color, "i"); +MODULE_PARM(init_hue, "i"); +MODULE_PARM(hue_correction, "i"); + +MODULE_PARM(init_model2_rg, "i"); +MODULE_PARM(init_model2_rg2, "i"); +MODULE_PARM(init_model2_sat, "i"); +MODULE_PARM(init_model2_yb, "i"); + +MODULE_AUTHOR ("module author"); +MODULE_DESCRIPTION ("IBM/Xirlink C-it USB Camera Driver for Linux (c) 2000"); + +/* Still mysterious i2c commands */ +static const unsigned short unknown_88 = 0x0088; +static const unsigned short unknown_89 = 0x0089; +static const unsigned short bright_3x[3] = { 0x0031, 0x0032, 0x0033 }; +static const unsigned short contrast_14 = 0x0014; +static const unsigned short light_27 = 0x0027; +static const unsigned short sharp_13 = 0x0013; + +/* i2c commands for Model 2 cameras */ +static const unsigned short mod2_brightness = 0x001a; /* $5b .. $ee; default=$5a */ +static const unsigned short mod2_set_framerate = 0x001c; /* 0 (fast).. $1F (slow) */ +static const unsigned short mod2_color_balance_rg2 = 0x001e; /* 0 (red) .. $7F (green) */ +static const unsigned short mod2_saturation = 0x0020; /* 0 (b/w) - $7F (full color) */ +static const unsigned short mod2_color_balance_yb = 0x0022; /* 0..$7F, $50 is about right */ +static const unsigned short mod2_color_balance_rg = 0x0024; /* 0..$7F, $70 is about right */ +static const unsigned short mod2_sensitivity = 0x0028; /* 0 (min) .. $1F (max) */ + +#define MAX_IBMCAM 4 + +struct usb_ibmcam cams[MAX_IBMCAM]; + +/*******************************/ +/* Memory management functions */ +/*******************************/ + +#define MDEBUG(x) do { } while(0) /* Debug memory management */ + +static struct usb_driver ibmcam_driver; +static void usb_ibmcam_release(struct usb_ibmcam *ibmcam); + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | (adr & (PAGE_SIZE-1)); + } + } + MDEBUG(printk("uv2kva(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long uvirt_to_bus(unsigned long adr) +{ + unsigned long kva, ret; + + kva = uvirt_to_kva(pgd_offset(current->mm, adr), adr); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("uv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +static inline unsigned long kvirt_to_bus(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = virt_to_bus((void *)kva); + MDEBUG(printk("kv2b(%lx-->%lx)", adr, ret)); + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + MDEBUG(printk("kv2pa(%lx-->%lx)", adr, ret)); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + +#if ENABLE_HEXDUMP +static void ibmcam_hexdump(const unsigned char *data, int len) +{ + char tmp[80]; + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && (i%16 == 0)) { + printk("%s\n", tmp); + k=0; + } + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} +#endif + +/* + * usb_ibmcam_overlaychar() + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaychar( + struct usb_ibmcam *ibmcam, + struct ibmcam_frame *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + + if ((ibmcam == NULL) || (frame == NULL)) + return; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'A' && ch <= 'F') + ch = 10 + (ch - 'A'); + else if (ch >= 'a' && ch <= 'f') + ch = 10 + (ch - 'a'); + else + return; + digit = digits[ch]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + IBMCAM_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + digit = digit << 1; + } + } +} + +/* + * usb_ibmcam_overlaystring() + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaystring( + struct usb_ibmcam *ibmcam, + struct ibmcam_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usb_ibmcam_overlaychar(ibmcam, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usb_ibmcam_overlaystats() + * + * Overlays important debugging information. + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_overlaystats(struct usb_ibmcam *ibmcam, struct ibmcam_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10; + int y = 10; + + sprintf(tmp, "%8x", ibmcam->frame_num); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->urb_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->urb_length); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->data_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->header_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->scratch_ovf_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->iso_skip_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", ibmcam->iso_err_count); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.colour); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.hue); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.brightness >> 8); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", ibmcam->vpic.contrast >> 12); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", ibmcam->vpic.whiteness >> 8); + usb_ibmcam_overlaystring(ibmcam, frame, x, y, tmp); + y += y_diff; +} + +/* + * usb_ibmcam_testpattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 1/2/00 Created. + */ +void usb_ibmcam_testpattern(struct usb_ibmcam *ibmcam, int fullframe, int pmode) +{ + static const char proc[] = "usb_ibmcam_testpattern"; + struct ibmcam_frame *frame; + unsigned char *f; + int num_cell = 0; + int scan_length = 0; + static int num_pass = 0; + + if (ibmcam == NULL) { + printk(KERN_ERR "%s: ibmcam == NULL\n", proc); + return; + } + if ((ibmcam->curframe < 0) || (ibmcam->curframe >= IBMCAM_NUMFRAMES)) { + printk(KERN_ERR "%s: ibmcam->curframe=%d.\n", proc, ibmcam->curframe); + return; + } + + /* Grab the current frame */ + frame = &ibmcam->frame[ibmcam->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->scanlength = 0; + } + + /* Form every scan line */ + for (; frame->curline < imgheight; frame->curline++) { + int i; + + f = frame->data + (imgwidth * 3 * frame->curline); + for (i=0; i < imgwidth; i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->grabstate = FRAME_DONE; + frame->scanlength += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usb_ibmcam_overlaystats(ibmcam, frame); +} + +static unsigned char *ibmcam_model1_find_header(unsigned char hdr_sig, unsigned char *data, int len) +{ + while (len >= 4) + { + if ((data[0] == 0x00) && (data[1] == 0xFF) && (data[2] == 0x00)) + { +#if 0 + /* This code helps to detect new frame markers */ + printk(KERN_DEBUG "Header sig: 00 FF 00 %02X\n", data[3]); +#endif + if (data[3] == hdr_sig) { + if (debug > 2) + printk(KERN_DEBUG "Header found.\n"); + return data+4; + } + } + ++data; + --len; + } + return NULL; +} + +static unsigned char *ibmcam_model2_find_header(unsigned char hdr_sig, unsigned char *data, int len) +{ + int marker_len = 0; + + switch (videosize) { + case VIDEOSIZE_176x144: + marker_len = 10; + break; + default: + marker_len = 2; + break; + } + while (len >= marker_len) + { + if ((data[0] == 0x00) && (data[1] == 0xFF)) + { +#if 0 + /* This code helps to detect new frame markers */ + static int pass = 0; + if (pass++ == 0) + ibmcam_hexdump(data, (len > 16) ? 16 : len); +#endif + if (debug > 2) + printk(KERN_DEBUG "Header found.\n"); + return data+marker_len; + } + ++data; + --len; + } + return NULL; +} + +/* How much data is left in the scratch buf? */ +#define scratch_left(x) (ibmcam->scratchlen - (int)((char *)x - (char *)ibmcam->scratch)) + +/* Grab the remaining */ +static void usb_ibmcam_align_scratch(struct usb_ibmcam *ibmcam, unsigned char *data) +{ + unsigned long left; + + left = scratch_left(data); + memmove(ibmcam->scratch, data, left); + ibmcam->scratchlen = left; +} + +/* + * usb_ibmcam_find_header() + * + * Locate one of supported header markers in the scratch buffer. + * Once found, remove all preceding bytes AND the marker (4 bytes) + * from the scratch buffer. Whatever follows must be video lines. + * + * History: + * 1/21/00 Created. + */ +static scan_state_t usb_ibmcam_find_header(struct usb_ibmcam *ibmcam) +{ + struct ibmcam_frame *frame; + unsigned char *data, *tmp; + + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + if (ibmcam->camera_model == IBMCAM_MODEL_1) + tmp = ibmcam_model1_find_header(frame->hdr_sig, data, scratch_left(data)); + else if (ibmcam->camera_model == IBMCAM_MODEL_2) + tmp = ibmcam_model2_find_header(frame->hdr_sig, data, scratch_left(data)); + else + tmp = NULL; + + if (tmp == NULL) { + /* No header - entire scratch buffer is useless! */ + if (debug > 2) + printk(KERN_DEBUG "Skipping frame, no header\n"); + ibmcam->scratchlen = 0; + return scan_EndParse; + } + /* Header found */ + data = tmp; + + ibmcam->has_hdr = 1; + ibmcam->header_count++; + frame->scanstate = STATE_LINES; + frame->curline = 0; + + if (flags & FLAGS_FORCE_TESTPATTERN) { + usb_ibmcam_testpattern(ibmcam, 1, 1); + return scan_NextFrame; + } + usb_ibmcam_align_scratch(ibmcam, data); + return scan_Continue; +} + +/* + * usb_ibmcam_parse_lines() + * + * Parse one line (TODO: more than one!) from the scratch buffer, put + * decoded RGB value into the current frame buffer and add the written + * number of bytes (RGB) to the *pcopylen. + * + * History: + * 1/21/00 Created. + */ +static scan_state_t usb_ibmcam_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +{ + struct ibmcam_frame *frame; + unsigned char *data, *f, *chromaLine; + unsigned int len; + const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ + const int hue_corr = (ibmcam->vpic.hue - 0x8000) >> 10; /* -32..+31 */ + const int hue2_corr = (hue_correction - 128) / 4; /* -32..+31 */ + const int ccm = 128; /* Color correction median - see below */ + int y, u, v, i, frame_done=0, mono_plane, color_corr; + + color_corr = (ibmcam->vpic.colour - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(color_corr, -ccm, ccm+1); + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + len = frame->frmwidth * 3; /* 1 line of mono + 1 line of color */ + /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ + + mono_plane = ((frame->curline & 1) == 0); + + /* + * Lines are organized this way (or are they?) + * + * I420: + * ~~~~ + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 176 -->|<------ 176*2 ------>| Total 72. pairs of lines + * |... ... ...| / + * |___________|_____________________| / + * - odd line- ------- even line --- + * + * another format: + * ~~~~~~~~~~~~~~ + * ___________________________________ + * |-----Y-----|---UVUVUV...UVUV-----| \ + * |-----------+---------------------| \ + * |<-- 352 -->|<------ 352*2 ------>| Total 144. pairs of lines + * |... ... ...| / + * |___________|_____________________| / + * - odd line- ------- even line --- + */ + + /* Make sure there's enough data for the entire line */ + if (scratch_left(data) < (len+1024)) { + /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ + return scan_Out; + } + +#if 0 + { /* This code prints beginning of the source frame */ + static int pass = 0; + if ((pass++ % 3000) == 0) + ibmcam_hexdump(data, 16); + } +#endif + +#if 0 + if (frame->curline == 10 || frame->curline == 11) { + /* This code prints beginning of 10th (mono), 11th (chroma) line */ + static int pass = 0; + if ((pass % 100) == 0) + ibmcam_hexdump(data, 16); + if (frame->curline == 11) + pass++; + } +#endif + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + return scan_NextFrame; + + /* + * Now we are sure that entire line (representing all 'frame->frmwidth' + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* + * chromaLine points to 1st pixel of the line with chrominance. + * If current line is monochrome then chromaLine points to next + * line after monochrome one. If current line has chrominance + * then chromaLine points to this very line. Such method allows + * to access chrominance data uniformly. + * + * To obtain chrominance data from the 'chromaLine' use this: + * v = chromaLine[0]; // 0-1:[0], 2-3:[4], 4-5:[8]... + * u = chromaLine[2]; // 0-1:[2], 2-3:[6], 4-5:[10]... + * + * Indices must be calculated this way: + * v_index = (i >> 1) << 2; + * u_index = (i >> 1) << 2 + 2; + * + * where 'i' is the column number [0..frame->frmwidth-1] + */ + chromaLine = data; + if (mono_plane) + chromaLine += frame->frmwidth; + + for (i = 0; i < frame->frmwidth; i++, data += (mono_plane ? 1 : 2)) + { + unsigned char rv, gv, bv; /* RGB components */ + + /* + * Search for potential Start-Of-Frame marker. It should + * not be here, of course, but if your formats don't match + * you might exceed the frame. We must match the marker to + * each byte of multi-byte data element if it is multi-byte. + */ +#if 1 + if ((ibmcam->camera_model == IBMCAM_MODEL_1) && (scratch_left(data) >= (4+2))) { + unsigned char *dp; + int j; + + for (j=0, dp=data; j < 2; j++, dp++) { + if ((dp[0] == 0x00) && (dp[1] == 0xFF) && + (dp[2] == 0x00) && (dp[3] == frame->hdr_sig)) { + ibmcam->has_hdr = 2; + frame_done++; + break; + } + } + } +#endif + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { + /* + * This is bad and should not happen. This means that + * we somehow overshoot the line and encountered new + * frame! Obviously our camera/V4L frame size is out + * of whack. This cyan dot will help you to figure + * out where exactly the new frame arrived. + */ + if (ibmcam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + ibmcam->has_hdr = 0; + goto make_pixel; + } + + if (mono_plane || frame->order_yc) + y = data[0]; + else + y = data[1]; + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else { + int off_0, off_2; + + off_0 = (i >> 1) << 2; + off_2 = off_0 + 2; + + if (frame->order_yc) { + off_0++; + off_2++; + } + if (!frame->order_uv) { + off_0 += 2; + off_2 -= 2; + } + u = chromaLine[off_0] + hue_corr; + v = chromaLine[off_2] + hue2_corr; + + /* Apply color correction */ + if (color_corr != 0) { + /* Magnify up to 2 times, reduce down to zero saturation */ + u = 128 + ((ccm + color_corr) * (u - 128)) / ccm; + v = 128 + ((ccm + color_corr) * (v - 128)) / ccm; + } + YUV_TO_RGB_BY_THE_BOOK(y, u, v, rv, gv, bv); + } + + make_pixel: + /* + * The purpose of creating the pixel here, in one, + * dedicated place is that we may need to make the + * pixel wider and taller than it actually is. This + * may be used if camera generates small frames for + * sake of frame rate (or any other reason.) + * + * The output data consists of B, G, R bytes + * (in this order). + */ +#if USES_IBMCAM_PUTPIXEL + IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); +#else + *f++ = bv; + *f++ = gv; + *f++ = rv; +#endif + /* + * Typically we do not decide within a legitimate frame + * that we want to end the frame. However debugging code + * may detect marker of new frame within the data. Then + * this condition activates. The 'data' pointer is already + * pointing at the new marker, so we'd better leave it as is. + */ + if (frame_done) + break; /* End scanning of lines */ + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline++; + *pcopylen += v4l_linesize; + usb_ibmcam_align_scratch(ibmcam, data); + + if (frame_done || (frame->curline >= frame->frmheight)) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * usb_ibmcam_model2_parse_lines() + * + * This procedure deals with a weird RGB format that is produced by IBM + * camera model 2 in modes 320x240 and above; 'x' below is 159 or 175, + * depending on horizontal size of the picture: + * + * <--- 160 or 176 pairs of RA,RB bytes -----> + * *-----------------------------------------* \ + * | RA0 | RB0 | RA1 | RB1 | ... | RAx | RBx | \ + * |-----+-----+-----+-----+ ... +-----+-----| *- This is pair of horizontal lines, + * | B0 | G0 | B1 | G1 | ... | Bx | Gx | / total 240 or 288 lines (120 or 144 + * |=====+=====+=====+=====+ ... +=====+=====| / such pairs). + * + * Each group of FOUR bytes (RAi, RBi, Bi, Gi) where i=0..frame_width/2-1 + * defines ONE pixel. Therefore this format yields 176x144 "decoded" + * resolution at best. I do not know why camera sends such format - the + * previous model just used I420 and everyone was happy. + * + * I do not know what is the difference between RAi and RBi bytes. Both + * seemingly represent R component, but slightly vary in value (so that + * the picture looks a bit colored if one or another is used). I use + * them both as R component in attempt to at least partially recover the + * lost resolution. + */ +static scan_state_t usb_ibmcam_model2_parse_lines(struct usb_ibmcam *ibmcam, long *pcopylen) +{ + struct ibmcam_frame *frame; + unsigned char *data, *f, *la, *lb; + unsigned int len; + const int v4l_linesize = imgwidth * V4L_BYTES_PER_PIXEL; /* V4L line offset */ + int i, j, frame_done=0, color_corr; + + color_corr = (ibmcam->vpic.colour) >> 8; /* 0..+255 */ + + data = ibmcam->scratch; + frame = &ibmcam->frame[ibmcam->curframe]; + + /* Here we deal with pairs of horizontal lines */ + + len = frame->frmwidth * 2; /* 2 lines */ + /*printk(KERN_DEBUG "len=%d. left=%d.\n",len,scratch_left(data));*/ + + /* Make sure there's enough data for the entire line */ + if (scratch_left(data) < (len+32)) { + /*printk(KERN_DEBUG "out of data, need %u.\n", len);*/ + return scan_Out; + } + + /* + * Make sure that our writing into output buffer + * will not exceed the buffer. Mind that we may write + * not into current output scanline but in several after + * it as well (if we enlarge image vertically.) + */ + if ((frame->curline + 1) >= V4L_FRAME_HEIGHT) + return scan_NextFrame; + + if ((frame->curline & 1) == 0) { + la = data; + lb = data + frame->frmwidth; + } else { + la = data + frame->frmwidth; + lb = data; + } + + /* + * Now we are sure that entire line (representing all 'frame->frmwidth' + * pixels from the camera) is available in the scratch buffer. We + * start copying the line left-aligned to the V4L buffer (which + * might be larger - not smaller, hopefully). If the camera + * line is shorter then we should pad the V4L buffer with something + * (black in this case) to complete the line. + */ + f = frame->data + (v4l_linesize * frame->curline); + + /* Fill the 2-line strip */ + for (i = 0; i < frame->frmwidth; i++) { + int y, rv, gv, bv; /* RGB components */ + + j = i & (~1); + + /* Check for various visual debugging hints (colorized pixels) */ + if ((flags & FLAGS_DISPLAY_HINTS) && (ibmcam->has_hdr)) { + if (ibmcam->has_hdr == 1) { + bv = 0; /* Yellow marker */ + gv = 0xFF; + rv = 0xFF; + } else { + bv = 0xFF; /* Cyan marker */ + gv = 0xFF; + rv = 0; + } + ibmcam->has_hdr = 0; + goto make_pixel; + } + + /* + * Here I use RA and RB components, one per physical pixel. + * This causes fine vertical grid on the picture but may improve + * horizontal resolution. If you prefer replicating, use this: + * rv = la[j + 0]; ... or ... rv = la[j + 1]; + * then the pixel will be replicated. + */ + rv = la[i]; + gv = lb[j + 1]; + bv = lb[j + 0]; + + y = (rv + gv + bv) / 3; /* Brightness (badly calculated) */ + + if (flags & FLAGS_MONOCHROME) /* Use monochrome for debugging */ + rv = gv = bv = y; + else if (color_corr != 128) { + + /* Calculate difference between color and brightness */ + rv -= y; + gv -= y; + bv -= y; + + /* Scale differences */ + rv = (rv * color_corr) / 128; + gv = (gv * color_corr) / 128; + bv = (bv * color_corr) / 128; + + /* Reapply brightness */ + rv += y; + gv += y; + bv += y; + + /* Watch for overflows */ + RESTRICT_TO_RANGE(rv, 0, 255); + RESTRICT_TO_RANGE(gv, 0, 255); + RESTRICT_TO_RANGE(bv, 0, 255); + } + + make_pixel: + IBMCAM_PUTPIXEL(frame, i, frame->curline, rv, gv, bv); + IBMCAM_PUTPIXEL(frame, i, frame->curline+1, rv, gv, bv); + } + /* + * Account for number of bytes that we wrote into output V4L frame. + * We do it here, after we are done with the scanline, because we + * may fill more than one output scanline if we do vertical + * enlargement. + */ + frame->curline += 2; + *pcopylen += v4l_linesize * 2; + data += frame->frmwidth * 2; + usb_ibmcam_align_scratch(ibmcam, data); + + if (frame_done || (frame->curline >= frame->frmheight)) + return scan_NextFrame; + else + return scan_Continue; +} + +/* + * ibmcam_parse_data() + * + * Generic routine to parse the scratch buffer. It employs either + * usb_ibmcam_find_header() or usb_ibmcam_parse_lines() to do most + * of work. + * + * History: + * 1/21/00 Created. + */ +static void ibmcam_parse_data(struct usb_ibmcam *ibmcam) +{ + struct ibmcam_frame *frame; + unsigned char *data = ibmcam->scratch; + scan_state_t newstate; + long copylen = 0; + + /* Grab the current frame and the previous frame */ + frame = &ibmcam->frame[ibmcam->curframe]; + + /* printk(KERN_DEBUG "parsing %u.\n", ibmcam->scratchlen); */ + + while (1) { + + newstate = scan_Out; + if (scratch_left(data)) { + if (frame->scanstate == STATE_SCANNING) + newstate = usb_ibmcam_find_header(ibmcam); + else if (frame->scanstate == STATE_LINES) { + if ((ibmcam->camera_model == IBMCAM_MODEL_2) && + (videosize >= VIDEOSIZE_352x288)) { + newstate = usb_ibmcam_model2_parse_lines(ibmcam, ©len); + } + else { + newstate = usb_ibmcam_parse_lines(ibmcam, ©len); + } + } + } + if (newstate == scan_Continue) + continue; + else if ((newstate == scan_NextFrame) || (newstate == scan_Out)) + break; + else + return; /* scan_EndParse */ + } + + if (newstate == scan_NextFrame) { + frame->grabstate = FRAME_DONE; + ibmcam->curframe = -1; + ibmcam->frame_num++; + + /* Optionally display statistics on the screen */ + if (flags & FLAGS_OVERLAY_STATS) + usb_ibmcam_overlaystats(ibmcam, frame); + + /* This will cause the process to request another frame. */ + if (waitqueue_active(&frame->wq)) + wake_up_interruptible(&frame->wq); + } + + /* Update the frame's uncompressed length. */ + frame->scanlength += copylen; +} + +/* + * Make all of the blocks of data contiguous + */ +static int ibmcam_compress_isochronous(struct usb_ibmcam *ibmcam, urb_t *urb) +{ + unsigned char *cdata, *data, *data0; + int i, totlen = 0; + + data = data0 = ibmcam->scratch + ibmcam->scratchlen; + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (debug >= 1) { + printk(KERN_ERR "ibmcam data error: [%d] len=%d, status=%X\n", + i, n, st); + } + ibmcam->iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + ibmcam->iso_skip_count++; + continue; + } + + /* + * If camera continues to feed us with data but there is no + * consumption (if, for example, V4L client fell asleep) we + * may overflow the buffer. We have to move old data over to + * free room for new data. This is bad for old data. If we + * just drop new data then it's bad for new data... choose + * your favorite evil here. + */ + if ((ibmcam->scratchlen + n) > scratchbufsize) { +#if 0 + ibmcam->scratch_ovf_count++; + if (debug >= 3) + printk(KERN_ERR "ibmcam: scratch buf overflow! " + "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); + return totlen; +#else + int mv; + + ibmcam->scratch_ovf_count++; + if (debug >= 3) { + printk(KERN_ERR "ibmcam: scratch buf overflow! " + "scr_len: %d, n: %d\n", ibmcam->scratchlen, n ); + } + mv = (ibmcam->scratchlen + n) - scratchbufsize; + if (ibmcam->scratchlen >= mv) { + int newslen = ibmcam->scratchlen - mv; + memmove(ibmcam->scratch, ibmcam->scratch + mv, newslen); + ibmcam->scratchlen = newslen; + data = data0 = ibmcam->scratch + ibmcam->scratchlen; + } else { + printk(KERN_ERR "ibmcam: scratch buf too small\n"); + return totlen; + } +#endif + } + + /* Now we know that there is enough room in scratch buffer */ + memmove(data, cdata, n); + data += n; + totlen += n; + ibmcam->scratchlen += n; + } +#if 0 + if (totlen > 0) { + static int foo=0; + if (foo < 1) { + printk(KERN_DEBUG "+%d.\n", totlen); + ibmcam_hexdump(data0, (totlen > 64) ? 64:totlen); + ++foo; + } + } +#endif + return totlen; +} + +static void ibmcam_isoc_irq(struct urb *urb) +{ + int len; + struct usb_ibmcam *ibmcam = urb->context; + struct ibmcam_sbuf *sbuf; + int i; + + /* We don't want to do anything if we are about to be removed! */ + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return; + +#if 0 + if (urb->actual_length > 0) { + printk(KERN_DEBUG "ibmcam_isoc_irq: %p status %d, " + " errcount = %d, length = %d\n", urb, urb->status, + urb->error_count, urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + printk(KERN_DEBUG "ibmcam_isoc_irq: no data\n"); + } +#endif + + if (!ibmcam->streaming) { + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: oops, not streaming, but interrupt\n"); + return; + } + + sbuf = &ibmcam->sbuf[ibmcam->cursbuf]; + + /* Copy the data received into our scratch buffer */ + len = ibmcam_compress_isochronous(ibmcam, urb); + + ibmcam->urb_count++; + ibmcam->urb_length = len; + ibmcam->data_count += len; + +#if 0 /* This code prints few initial bytes of ISO data: used to decode markers */ + if (ibmcam->urb_count % 64 == 1) { + if (ibmcam->urb_count == 1) { + ibmcam_hexdump(ibmcam->scratch, + (ibmcam->scratchlen > 32) ? 32 : ibmcam->scratchlen); + } + } +#endif + + /* If we collected enough data let's parse! */ + if (ibmcam->scratchlen) { + /* If we don't have a frame we're current working on, complain */ + if (ibmcam->curframe >= 0) + ibmcam_parse_data(ibmcam); + else { + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: received data, but no frame available\n"); + } + } + + for (i = 0; i < FRAMES_PER_DESC; i++) { + sbuf->urb->iso_frame_desc[i].status = 0; + sbuf->urb->iso_frame_desc[i].actual_length = 0; + } + + /* Move to the next sbuf */ + ibmcam->cursbuf = (ibmcam->cursbuf + 1) % IBMCAM_NUMSBUF; + + return; +} + +/* + * usb_ibmcam_veio() + * + * History: + * 1/27/00 Added check for dev == NULL; this happens if camera is unplugged. + */ +static int usb_ibmcam_veio( + struct usb_ibmcam *ibmcam, + unsigned char req, + unsigned short value, + unsigned short index) +{ + static const char proc[] = "usb_ibmcam_veio"; + unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */; + int i; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return 0; + + if (req == 1) { + i = usb_control_msg( + ibmcam->dev, + usb_rcvctrlpipe(ibmcam->dev, 0), + req, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + cp, + sizeof(cp), + HZ); +#if 0 + printk(KERN_DEBUG "USB => %02x%02x%02x%02x%02x%02x%02x%02x " + "(req=$%02x val=$%04x ind=$%04x)\n", + cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7], + req, value, index); +#endif + } else { + i = usb_control_msg( + ibmcam->dev, + usb_sndctrlpipe(ibmcam->dev, 0), + req, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT, + value, + index, + NULL, + 0, + HZ); + } + if (i < 0) { + printk(KERN_ERR "%s: ERROR=%d. Camera stopped - " + "reconnect or reload driver.\n", proc, i); + ibmcam->last_error = i; + } + return i; +} + +/* + * usb_ibmcam_calculate_fps() + * + * This procedure roughly calculates the real frame rate based + * on FPS code (framerate=NNN option). Actual FPS differs + * slightly depending on lighting conditions, so that actual frame + * rate is determined by the camera. Since I don't know how to ask + * the camera what FPS is now I have to use the FPS code instead. + * + * The FPS code is in range [0..6], 0 is slowest, 6 is fastest. + * Corresponding real FPS should be in range [3..30] frames per second. + * The conversion formula is obvious: + * + * real_fps = 3 + (fps_code * 4.5) + * + * History: + * 1/18/00 Created. + */ +static int usb_ibmcam_calculate_fps(void) +{ + return 3 + framerate*4 + framerate/2; +} + +/* + * usb_ibmcam_send_FF_04_02() + * + * This procedure sends magic 3-command prefix to the camera. + * The purpose of this prefix is not known. + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_send_FF_04_02(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00FF, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); +} + +static void usb_ibmcam_send_00_04_06(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0004, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0006, 0x0124); +} + +static void usb_ibmcam_send_x_00(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); +} + +static void usb_ibmcam_send_x_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_send_x_00(ibmcam, x); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); +} + +static void usb_ibmcam_send_x_01_00_05(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02_01(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); +} + +static void usb_ibmcam_send_x_00_05_02_08_01(struct usb_ibmcam *ibmcam, unsigned short x) +{ + usb_ibmcam_veio(ibmcam, 0, x, 0x0127); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0005, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x0124); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0124); +} + +static void usb_ibmcam_Packet_Format1(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +{ + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, fkey); + usb_ibmcam_send_x_00_05_02_08_01(ibmcam, val); + usb_ibmcam_send_x_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05_02_01 (ibmcam, fkey); + usb_ibmcam_send_x_00_05 (ibmcam, unknown_89); + usb_ibmcam_send_x_00 (ibmcam, fkey); + usb_ibmcam_send_00_04_06 (ibmcam); + usb_ibmcam_veio (ibmcam, 1, 0x0000, 0x0126); + usb_ibmcam_send_FF_04_02 (ibmcam); +} + +static void usb_ibmcam_PacketFormat2(struct usb_ibmcam *ibmcam, unsigned char fkey, unsigned char val) +{ + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, fkey); + usb_ibmcam_send_x_00_05_02 (ibmcam, val); +} + +static void usb_ibmcam_model2_Packet2(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012d); + usb_ibmcam_veio(ibmcam, 0, 0xfea3, 0x0124); +} + +static void usb_ibmcam_model2_Packet1(struct usb_ibmcam *ibmcam, unsigned short v1, unsigned short v2) +{ + usb_ibmcam_veio(ibmcam, 0, 0x00aa, 0x012d); + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x012e); + usb_ibmcam_veio(ibmcam, 0, v1, 0x012f); + usb_ibmcam_veio(ibmcam, 0, 0x00ff, 0x0130); + usb_ibmcam_veio(ibmcam, 0, 0xc719, 0x0124); + usb_ibmcam_veio(ibmcam, 0, v2, 0x0127); + + usb_ibmcam_model2_Packet2(ibmcam); +} + +/* + * usb_ibmcam_adjust_contrast() + * + * The contrast value changes from 0 (high contrast) to 15 (low contrast). + * This is in reverse to usual order of things (such as TV controls), so + * we reverse it again here. + * + * TODO: we probably don't need to send the setup 5 times... + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_adjust_contrast(struct usb_ibmcam *ibmcam) +{ + unsigned char new_contrast = ibmcam->vpic.contrast >> 12; + const int ntries = 5; + + if (new_contrast >= 16) + new_contrast = 15; + new_contrast = 15 - new_contrast; + if (new_contrast != ibmcam->vpic_old.contrast) { + ibmcam->vpic_old.contrast = new_contrast; + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + int i; + for (i=0; i < ntries; i++) { + usb_ibmcam_Packet_Format1(ibmcam, contrast_14, new_contrast); + usb_ibmcam_send_FF_04_02(ibmcam); + } + } else { + /* Camera model 2 does not have this control; implemented in software. */ + } + } +} + +/* + * usb_ibmcam_change_lighting_conditions() + * + * Camera model 1: + * We have 3 levels of lighting conditions: 0=Bright, 1=Medium, 2=Low. + * + * Camera model 2: + * We have 16 levels of lighting, 0 for bright light and up to 15 for + * low light. But values above 5 or so are useless because camera is + * not really capable to produce anything worth viewing at such light. + * This setting may be altered only in certain camera state. + * + * Low lighting forces slower FPS. Lighting is set as a module parameter. + * + * History: + * 1/5/00 Created. + * 2/20/00 Added support for Model 2 cameras. + */ +static void usb_ibmcam_change_lighting_conditions(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_change_lighting_conditions"; + + if (debug > 0) + printk(KERN_INFO "%s: Set lighting to %hu.\n", proc, lighting); + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + const int ntries = 5; + int i; + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, light_27, (unsigned short) lighting); + } else { + /* + * This command apparently requires camera to be stopped. My + * experiments showed that it -is- possible to alter the lighting + * conditions setting "on the fly", but why bother? This setting does + * not work reliably in all cases, so I decided simply to leave the + * setting where Xirlink put it - in the camera setup phase. This code + * is commented out because it does not work at -any- moment, so its + * presence makes no sense. You may use it for experiments. + */ +#if 0 + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop camera */ + usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Start camera */ +#endif + } +} + +/* + * usb_ibmcam_set_sharpness() + * + * Cameras model 1 have internal smoothing feature. It is controlled by value in + * range [0..6], where 0 is most smooth and 6 is most sharp (raw image, I guess). + * Recommended value is 4. Cameras model 2 do not have this feature at all. + */ +static void usb_ibmcam_set_sharpness(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_set_sharpness"; + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + static const unsigned short sa[] = { 0x11, 0x13, 0x16, 0x18, 0x1a, 0x8, 0x0a }; + unsigned short i, sv; + + RESTRICT_TO_RANGE(sharpness, SHARPNESS_MIN, SHARPNESS_MAX); + if (debug > 0) + printk(KERN_INFO "%s: Set sharpness to %hu.\n", proc, sharpness); + + sv = sa[sharpness - SHARPNESS_MIN]; + for (i=0; i < 2; i++) { + usb_ibmcam_send_x_01_00_05 (ibmcam, unknown_88); + usb_ibmcam_send_x_00_05 (ibmcam, sharp_13); + usb_ibmcam_send_x_00_05_02 (ibmcam, sv); + } + } else { + /* Camera model 2 does not have this control */ + } +} + +/* + * usb_ibmcam_set_brightness() + * + * This procedure changes brightness of the picture. + */ +static void usb_ibmcam_set_brightness(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "usb_ibmcam_set_brightness"; + static const unsigned short n = 1; + unsigned short i, j, bv[3]; + + bv[0] = bv[1] = bv[2] = ibmcam->vpic.brightness >> 10; + if (bv[0] == (ibmcam->vpic_old.brightness >> 10)) + return; + ibmcam->vpic_old.brightness = ibmcam->vpic.brightness; + + if (debug > 0) + printk(KERN_INFO "%s: Set brightness to (%hu,%hu,%hu)\n", + proc, bv[0], bv[1], bv[2]); + + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + for (j=0; j < 3; j++) + for (i=0; i < n; i++) + usb_ibmcam_Packet_Format1(ibmcam, bright_3x[j], bv[j]); + } else { + i = ibmcam->vpic.brightness >> 12; /* 0 .. 15 */ + j = 0x60 + i * ((0xee - 0x60) / 16); /* 0x60 .. 0xee or so */ + usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, j); + } +} + +static void usb_ibmcam_model2_set_hue(struct usb_ibmcam *ibmcam) +{ + unsigned short hue = ibmcam->vpic.hue >> 9; /* 0 .. 7F */ + + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, hue); + /* usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, sat); */ +} + +/* + * usb_ibmcam_adjust_picture() + * + * This procedure gets called from V4L interface to update picture settings. + * Here we change brightness and contrast. + */ +static void usb_ibmcam_adjust_picture(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_adjust_contrast(ibmcam); + usb_ibmcam_set_brightness(ibmcam); + if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_model2_set_hue(ibmcam); + } +} + +static int usb_ibmcam_model1_setup(struct usb_ibmcam *ibmcam) +{ + const int ntries = 5; + int i; + + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0128); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0108); + + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0112); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x06, 0x0115); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x44, 0x0116); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x40, 0x0116); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x0e, 0x0115); + usb_ibmcam_veio(ibmcam, 0, 0x19, 0x012c); + + usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x1e); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x0d); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x09); + usb_ibmcam_Packet_Format1(ibmcam, 0x3b, 0x00); + usb_ibmcam_Packet_Format1(ibmcam, 0x28, 0x22); + usb_ibmcam_Packet_Format1(ibmcam, light_27, 0); + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x00); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x14); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x01, 0xe1); + usb_ibmcam_PacketFormat2(ibmcam, 0x02, 0xcd); + usb_ibmcam_PacketFormat2(ibmcam, 0x03, 0xcd); + usb_ibmcam_PacketFormat2(ibmcam, 0x04, 0xfa); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x0a, 0x37); + usb_ibmcam_PacketFormat2(ibmcam, 0x0b, 0xb8); + usb_ibmcam_PacketFormat2(ibmcam, 0x0c, 0xf3); + usb_ibmcam_PacketFormat2(ibmcam, 0x0d, 0xe3); + usb_ibmcam_PacketFormat2(ibmcam, 0x0e, 0x0d); + usb_ibmcam_PacketFormat2(ibmcam, 0x0f, 0xf2); + usb_ibmcam_PacketFormat2(ibmcam, 0x10, 0xd5); + usb_ibmcam_PacketFormat2(ibmcam, 0x11, 0xba); + usb_ibmcam_PacketFormat2(ibmcam, 0x12, 0x53); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x02); + usb_ibmcam_PacketFormat2(ibmcam, 0x16, 0x00); + usb_ibmcam_PacketFormat2(ibmcam, 0x17, 0x28); + usb_ibmcam_PacketFormat2(ibmcam, 0x18, 0x7d); + usb_ibmcam_PacketFormat2(ibmcam, 0x19, 0xbe); + usb_ibmcam_PacketFormat2(ibmcam, 0x3f, 0xff); + usb_ibmcam_PacketFormat2(ibmcam, 0x39, 0x00); + + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x00, 0x18); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x13, 0x18); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x14, 0x06); + + /* This is default brightness */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0x37); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0x46); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0x55); + + usb_ibmcam_Packet_Format1(ibmcam, 0x2e, 0x04); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x2d, 0x04); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x29, 0x80); + usb_ibmcam_Packet_Format1(ibmcam, 0x2c, 0x01); + usb_ibmcam_Packet_Format1(ibmcam, 0x30, 0x17); + usb_ibmcam_Packet_Format1(ibmcam, 0x39, 0x08); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x34, 0x00); + + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0101); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010a); + + switch (videosize) { + case VIDEOSIZE_128x96: + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x60, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x0c, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x0b, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + break; + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x8f, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x06, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x0d, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0129); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0xb0, 0x0103); + usb_ibmcam_veio(ibmcam, 0, 0x90, 0x0105); + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x010b); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011b); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x05, 0x011d); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x011e); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0129); + break; + } + + usb_ibmcam_veio(ibmcam, 0, 0xff, 0x012b); + + /* This is another brightness - don't know why */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x31, 0xc3); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x32, 0xd2); + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, 0x33, 0xe1); + + /* Default contrast */ + for (i=0; i < ntries; i++) + usb_ibmcam_Packet_Format1(ibmcam, contrast_14, 0x0a); + + /* Default sharpness */ + for (i=0; i < 2; i++) + usb_ibmcam_PacketFormat2(ibmcam, sharp_13, 0x1a); /* Level 4 FIXME */ + + /* Default lighting conditions */ + usb_ibmcam_Packet_Format1(ibmcam, light_27, lighting); /* 0=Bright 2=Low */ + + /* Assorted init */ + + switch (videosize) { + case VIDEOSIZE_128x96: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x36, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x1a, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ +#if 0 + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0x38, 0x0107); +#else + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0x2a, 0x0107); +#endif + break; + case VIDEOSIZE_176x144: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1e); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x02, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2b, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0xca, 0x0107); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_Packet_Format1(ibmcam, 0x2b, 0x1f); + usb_ibmcam_veio(ibmcam, 0, 0xc9, 0x0119); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x80, 0x0109); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x08, 0x0102); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0104); + usb_ibmcam_veio(ibmcam, 0, 0x04, 0x011a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x2f, 0x011c); + usb_ibmcam_veio(ibmcam, 0, 0x23, 0x012a); /* Same everywhere */ + usb_ibmcam_veio(ibmcam, 0, 0x03, 0x0106); + usb_ibmcam_veio(ibmcam, 0, 0xf6, 0x0107); + break; + } + return IBMCAM_IS_OPERATIONAL(ibmcam); +} + +static int usb_ibmcam_model2_setup(struct usb_ibmcam *ibmcam) +{ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ + usb_ibmcam_veio(ibmcam, 1, 0x0000, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x0060, 0x0116); + usb_ibmcam_veio(ibmcam, 0, 0x0002, 0x0112); + usb_ibmcam_veio(ibmcam, 0, 0x00bc, 0x012c); + usb_ibmcam_veio(ibmcam, 0, 0x0008, 0x012b); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0108); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0133); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0102); + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + usb_ibmcam_veio(ibmcam, 0, 0x00b9, 0x010a); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0038, 0x0119); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0090, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_320x240: + usb_ibmcam_veio(ibmcam, 0, 0x0028, 0x0103); /* Unique to this mode */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0098, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x240: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x001e, 0x0105); /* 320x240, 352x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x00da, 0x0107); /* Unique to every mode*/ + break; + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0x002c, 0x0103); /* All except 320x240 */ + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0104); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x0024, 0x0105); /* 176x144, 352x288 */ + usb_ibmcam_veio(ibmcam, 0, 0x0039, 0x010a); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0070, 0x0119); /* All except 176x144 */ + usb_ibmcam_veio(ibmcam, 0, 0x0003, 0x0106); /* Same */ + usb_ibmcam_veio(ibmcam, 0, 0x00fe, 0x0107); /* Unique to every mode*/ + break; + } + return IBMCAM_IS_OPERATIONAL(ibmcam); +} + +/* + * usb_ibmcam_model1_setup_after_video_if() + * + * This code adds finishing touches to the video data interface. + * Here we configure the frame rate and turn on the LED. + */ +static void usb_ibmcam_model1_setup_after_video_if(struct usb_ibmcam *ibmcam) +{ + unsigned short internal_frame_rate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + internal_frame_rate = FRAMERATE_MAX - framerate; /* 0=Fast 6=Slow */ + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0100); /* LED On */ + usb_ibmcam_veio(ibmcam, 0, internal_frame_rate, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); +} + +static void usb_ibmcam_model2_setup_after_video_if(struct usb_ibmcam *ibmcam) +{ + unsigned short setup_model2_rg, setup_model2_rg2, setup_model2_sat, setup_model2_yb; + + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0100); /* LED on */ + + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_veio(ibmcam, 0, 0x0050, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00d0, 0x0111); + break; + case VIDEOSIZE_320x240: + case VIDEOSIZE_352x240: + case VIDEOSIZE_352x288: + usb_ibmcam_veio(ibmcam, 0, 0x0040, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x0111); + break; + } + usb_ibmcam_veio(ibmcam, 0, 0x009b, 0x010f); + usb_ibmcam_veio(ibmcam, 0, 0x00bb, 0x010f); + + /* + * Hardware settings, may affect CMOS sensor; not user controls! + * ------------------------------------------------------------- + * 0x0004: no effect + * 0x0006: hardware effect + * 0x0008: no effect + * 0x000a: stops video stream, probably important h/w setting + * 0x000c: changes color in hardware manner (not user setting) + * 0x0012: changes number of colors (does not affect speed) + * 0x002a: no effect + * 0x002c: hardware setting (related to scan lines) + * 0x002e: stops video stream, probably important h/w setting + */ + usb_ibmcam_model2_Packet1(ibmcam, 0x000a, 0x005c); + usb_ibmcam_model2_Packet1(ibmcam, 0x0004, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x0006, 0x00fb); + usb_ibmcam_model2_Packet1(ibmcam, 0x0008, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x000c, 0x0009); + usb_ibmcam_model2_Packet1(ibmcam, 0x0012, 0x000a); + usb_ibmcam_model2_Packet1(ibmcam, 0x002a, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x002c, 0x0000); + usb_ibmcam_model2_Packet1(ibmcam, 0x002e, 0x0008); + + /* + * Function 0x0030 pops up all over the place. Apparently + * it is a hardware control register, with every bit assigned to + * do something. + */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0000); + + /* + * Magic control of CMOS sensor. Only lower values like + * 0-3 work, and picture shifts left or right. Don't change. + */ + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0002); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + break; + case VIDEOSIZE_320x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0005); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Another hardware setting */ + break; + case VIDEOSIZE_352x240: + /* This mode doesn't work as Windows programs it; changed to work */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0009); /* Windows sets this to 8 */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0003); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x0044); /* Windows sets this to 0x0045 */ + break; + case VIDEOSIZE_352x288: + usb_ibmcam_model2_Packet1(ibmcam, 0x0014, 0x0003); + usb_ibmcam_model2_Packet1(ibmcam, 0x0016, 0x0002); /* Horizontal shift */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0018, 0x004a); /* Another hardware setting */ + break; + } + + usb_ibmcam_model2_Packet1(ibmcam, mod2_brightness, 0x005a); + + /* + * We have our own frame rate setting varying from 0 (slowest) to 6 (fastest). + * The camera model 2 allows frame rate in range [0..0x1F] where 0 is also the + * slowest setting. However for all practical reasons high settings make no + * sense because USB is not fast enough to support high FPS. Be aware that + * the picture datastream will be severely disrupted if you ask for + * frame rate faster than allowed for the video size - see below: + * + * Allowable ranges (obtained experimentally on OHCI, K6-3, 450 MHz): + * ----------------------------------------------------------------- + * 176x144: [6..31] + * 320x240: [8..31] + * 352x240: [10..31] + * 352x288: [16..31] I have to raise lower threshold for stability... + * + * As usual, slower FPS provides better sensitivity. + */ + { + short hw_fps=31, i_framerate; + + RESTRICT_TO_RANGE(framerate, FRAMERATE_MIN, FRAMERATE_MAX); + i_framerate = FRAMERATE_MAX - framerate + FRAMERATE_MIN; + switch (videosize) { + case VIDEOSIZE_176x144: + hw_fps = 6 + i_framerate*4; + break; + case VIDEOSIZE_320x240: + hw_fps = 8 + i_framerate*3; + break; + case VIDEOSIZE_352x240: + hw_fps = 10 + i_framerate*2; + break; + case VIDEOSIZE_352x288: + hw_fps = 28 + i_framerate/2; + break; + } + if (debug > 0) + printk(KERN_DEBUG "Framerate (hardware): %hd.\n", hw_fps); + RESTRICT_TO_RANGE(hw_fps, 0, 31); + usb_ibmcam_model2_Packet1(ibmcam, mod2_set_framerate, hw_fps); + } + + /* + * This setting does not visibly affect pictures; left it here + * because it was present in Windows USB data stream. This function + * does not allow arbitrary values and apparently is a bit mask, to + * be activated only at appropriate time. Don't change it randomly! + */ + switch (videosize) { + case VIDEOSIZE_176x144: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x00c2); + break; + case VIDEOSIZE_320x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0044); + break; + case VIDEOSIZE_352x240: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0046); + break; + case VIDEOSIZE_352x288: + usb_ibmcam_model2_Packet1(ibmcam, 0x0026, 0x0048); + break; + } + + usb_ibmcam_model2_Packet1(ibmcam, mod2_sensitivity, lighting); + + if (init_model2_rg >= 0) { + RESTRICT_TO_RANGE(init_model2_rg, 0, 255); + setup_model2_rg = init_model2_rg; + } else + setup_model2_rg = 0x0070; + + if (init_model2_rg2 >= 0) { + RESTRICT_TO_RANGE(init_model2_rg2, 0, 255); + setup_model2_rg2 = init_model2_rg2; + } else + setup_model2_rg2 = 0x002f; + + if (init_model2_sat >= 0) { + RESTRICT_TO_RANGE(init_model2_sat, 0, 255); + setup_model2_sat = init_model2_sat; + } else + setup_model2_sat = 0x0034; + + if (init_model2_yb >= 0) { + RESTRICT_TO_RANGE(init_model2_yb, 0, 255); + setup_model2_yb = init_model2_yb; + } else + setup_model2_yb = 0x00a0; + + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg2, setup_model2_rg2); + usb_ibmcam_model2_Packet1(ibmcam, mod2_saturation, setup_model2_sat); + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_yb, setup_model2_yb); + usb_ibmcam_model2_Packet1(ibmcam, mod2_color_balance_rg, setup_model2_rg); + + /* Hardware control command */ + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); + + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); /* Go camera, go! */ + usb_clear_halt(ibmcam->dev, ibmcam->video_endp); +} + +/* + * usb_ibmcam_setup_video_stop() + * + * This code tells camera to stop streaming. The interface remains + * configured and bandwidth - claimed. + */ +static void usb_ibmcam_setup_video_stop(struct usb_ibmcam *ibmcam) +{ + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x01, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0xc0, 0x010c); + usb_ibmcam_veio(ibmcam, 0, 0x00, 0x010c); + usb_ibmcam_send_FF_04_02(ibmcam); + usb_ibmcam_veio(ibmcam, 1, 0x00, 0x0100); + usb_ibmcam_veio(ibmcam, 0, 0x81, 0x0100); /* LED Off */ + } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x010c); /* Stop the camera */ + + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0004); + + usb_ibmcam_veio(ibmcam, 0, 0x0080, 0x0100); /* LED Off */ + usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x00a0, 0x0111); + + usb_ibmcam_model2_Packet1(ibmcam, 0x0030, 0x0002); + + usb_ibmcam_veio(ibmcam, 0, 0x0020, 0x0111); + usb_ibmcam_veio(ibmcam, 0, 0x0000, 0x0112); + } +} + +/* + * usb_ibmcam_reinit_iso() + * + * This procedure sends couple of commands to the camera and then + * resets the video pipe. This sequence was observed to reinit the + * camera or, at least, to initiate ISO data stream. + * + * History: + * 1/2/00 Created. + */ +static void usb_ibmcam_reinit_iso(struct usb_ibmcam *ibmcam, int do_stop) +{ + if (ibmcam->camera_model == IBMCAM_MODEL_1) { + if (do_stop) + usb_ibmcam_setup_video_stop(ibmcam); + usb_ibmcam_veio(ibmcam, 0, 0x0001, 0x0114); + usb_ibmcam_veio(ibmcam, 0, 0x00c0, 0x010c); + usb_clear_halt(ibmcam->dev, ibmcam->video_endp); + usb_ibmcam_model1_setup_after_video_if(ibmcam); + } else if (ibmcam->camera_model == IBMCAM_MODEL_2) { + usb_ibmcam_model2_setup_after_video_if(ibmcam); + } +} + +/* + * ibmcam_init_isoc() + * + * History: + * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltActive instead of hardcoded values. + * Simplified by using for loop, allowed any number of URBs. + */ +static int ibmcam_init_isoc(struct usb_ibmcam *ibmcam) +{ + struct usb_device *dev = ibmcam->dev; + int i, err; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + ibmcam->compress = 0; + ibmcam->curframe = -1; + ibmcam->cursbuf = 0; + ibmcam->scratchlen = 0; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, ibmcam->iface, ibmcam->ifaceAltActive); + if (i < 0) { + printk(KERN_ERR "usb_set_interface error\n"); + ibmcam->last_error = i; + return -EBUSY; + } + usb_ibmcam_change_lighting_conditions(ibmcam); + usb_ibmcam_set_sharpness(ibmcam); + usb_ibmcam_reinit_iso(ibmcam, 0); + + /* We double buffer the Iso lists */ + + for (i=0; i < IBMCAM_NUMSBUF; i++) { + int j, k; + urb_t *urb; + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (urb == NULL) { + printk(KERN_ERR "ibmcam_init_isoc: usb_init_isoc() failed.\n"); + return -ENOMEM; + } + ibmcam->sbuf[i].urb = urb; + urb->dev = dev; + urb->context = ibmcam; + urb->pipe = usb_rcvisocpipe(dev, ibmcam->video_endp); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ibmcam->sbuf[i].data; + urb->complete = ibmcam_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ibmcam->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += ibmcam->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = ibmcam->iso_packet_len; + } + } + + /* Link URBs into a ring so that they invoke each other infinitely */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + if ((i+1) < IBMCAM_NUMSBUF) + ibmcam->sbuf[i].urb->next = ibmcam->sbuf[i+1].urb; + else + ibmcam->sbuf[i].urb->next = ibmcam->sbuf[0].urb; + } + + /* Submit all URBs */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + err = usb_submit_urb(ibmcam->sbuf[i].urb); + if (err) + printk(KERN_ERR "ibmcam_init_isoc: usb_run_isoc(%d) ret %d\n", + i, err); + } + + ibmcam->streaming = 1; + /* printk(KERN_DEBUG "streaming=1 ibmcam->video_endp=$%02x\n", ibmcam->video_endp); */ + return 0; +} + +/* + * ibmcam_stop_isoc() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 1/22/00 Corrected order of actions to work after surprise removal. + * 1/27/00 Used ibmcam->iface, ibmcam->ifaceAltInactive instead of hardcoded values. + */ +static void ibmcam_stop_isoc(struct usb_ibmcam *ibmcam) +{ + static const char proc[] = "ibmcam_stop_isoc"; + int i, j; + + if (!ibmcam->streaming || (ibmcam->dev == NULL)) + return; + + /* Unschedule all of the iso td's */ + for (i=0; i < IBMCAM_NUMSBUF; i++) { + j = usb_unlink_urb(ibmcam->sbuf[i].urb); + if (j < 0) + printk(KERN_ERR "%s: usb_unlink_urb() error %d.\n", proc, j); + } + /* printk(KERN_DEBUG "streaming=0\n"); */ + ibmcam->streaming = 0; + + /* Delete them all */ + for (i=0; i < IBMCAM_NUMSBUF; i++) + usb_free_urb(ibmcam->sbuf[i].urb); + + if (!ibmcam->remove_pending) { + usb_ibmcam_setup_video_stop(ibmcam); + + /* Set packet size to 0 */ + j = usb_set_interface(ibmcam->dev, ibmcam->iface, ibmcam->ifaceAltInactive); + if (j < 0) { + printk(KERN_ERR "%s: usb_set_interface() error %d.\n", proc, j); + ibmcam->last_error = j; + } + } +} + +/* + * ibmcam_new_frame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + */ +static int ibmcam_new_frame(struct usb_ibmcam *ibmcam, int framenum) +{ + struct ibmcam_frame *frame; + int n, width, height; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (ibmcam->curframe != -1) + return 0; + + n = (framenum - 1 + IBMCAM_NUMFRAMES) % IBMCAM_NUMFRAMES; + if (ibmcam->frame[n].grabstate == FRAME_READY) + framenum = n; + + frame = &ibmcam->frame[framenum]; + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->scanlength = 0; /* Accumulated in ibmcam_parse_data() */ + ibmcam->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + memmove(frame->data, ibmcam->frame[1-framenum].data, MAX_FRAME_SIZE); + } else { + if (flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, MAX_FRAME_SIZE); + } + } + switch (videosize) { + case VIDEOSIZE_128x96: + frame->frmwidth = 128; + frame->frmheight = 96; + frame->order_uv = 1; /* U Y V Y ... */ + frame->hdr_sig = 0x06; /* 00 FF 00 06 */ + break; + case VIDEOSIZE_176x144: + frame->frmwidth = 176; + frame->frmheight = 144; + frame->order_uv = 1; /* U Y V Y ... */ + frame->hdr_sig = 0x0E; /* 00 FF 00 0E */ + break; + case VIDEOSIZE_320x240: /* For model 2 only */ + frame->frmwidth = 320; + frame->frmheight = 240; + break; + case VIDEOSIZE_352x240: /* For model 2 only */ + frame->frmwidth = 352; + frame->frmheight = 240; + break; + case VIDEOSIZE_352x288: + frame->frmwidth = 352; + frame->frmheight = 288; + frame->order_uv = 0; /* V Y U Y ... */ + frame->hdr_sig = 0x00; /* 00 FF 00 00 */ + break; + } + frame->order_yc = (ibmcam->camera_model == IBMCAM_MODEL_2); + + width = frame->width; + RESTRICT_TO_RANGE(width, min_imgwidth, imgwidth); + width &= ~7; /* Multiple of 8 */ + + height = frame->height; + RESTRICT_TO_RANGE(height, min_imgheight, imgheight); + height &= ~3; /* Multiple of 4 */ + + return 0; +} + +/* + * ibmcam_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'ibmcam->user'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 1/22/00 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static int ibmcam_open(struct video_device *dev, int flags) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + const int sb_size = FRAMES_PER_DESC * ibmcam->iso_packet_len; + int i, err = 0; + + MOD_INC_USE_COUNT; + down(&ibmcam->lock); + + if (ibmcam->user) + err = -EBUSY; + else { + /* Clean pointers so we know if we allocated something */ + for (i=0; i < IBMCAM_NUMSBUF; i++) + ibmcam->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + ibmcam->fbuf_size = IBMCAM_NUMFRAMES * MAX_FRAME_SIZE; + ibmcam->fbuf = rvmalloc(ibmcam->fbuf_size); + ibmcam->scratch = kmalloc(scratchbufsize, GFP_KERNEL); + ibmcam->scratchlen = 0; + if ((ibmcam->fbuf == NULL) || (ibmcam->scratch == NULL)) + err = -ENOMEM; + else { + /* Allocate all buffers */ + for (i=0; i < IBMCAM_NUMFRAMES; i++) { + ibmcam->frame[i].grabstate = FRAME_UNUSED; + ibmcam->frame[i].data = ibmcam->fbuf + i*MAX_FRAME_SIZE; + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + ibmcam->frame[i].width = imgwidth; + ibmcam->frame[i].height = imgheight; + ibmcam->frame[i].bytes_read = 0; + } + for (i=0; i < IBMCAM_NUMSBUF; i++) { + ibmcam->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (ibmcam->sbuf[i].data == NULL) { + err = -ENOMEM; + break; + } + } + } + if (err) { + /* Have to free all that memory */ + if (ibmcam->fbuf != NULL) { + rvfree(ibmcam->fbuf, ibmcam->fbuf_size); + ibmcam->fbuf = NULL; + } + if (ibmcam->scratch != NULL) { + kfree(ibmcam->scratch); + ibmcam->scratch = NULL; + } + for (i=0; i < IBMCAM_NUMSBUF; i++) { + if (ibmcam->sbuf[i].data != NULL) { + kfree (ibmcam->sbuf[i].data); + ibmcam->sbuf[i].data = NULL; + } + } + } + } + + /* If so far no errors then we shall start the camera */ + if (!err) { + err = ibmcam_init_isoc(ibmcam); + if (!err) { + /* Send init sequence only once, it's large! */ + if (!ibmcam->initialized) { + int setup_ok = 0; + if (ibmcam->camera_model == IBMCAM_MODEL_1) + setup_ok = usb_ibmcam_model1_setup(ibmcam); + else if (ibmcam->camera_model == IBMCAM_MODEL_2) + setup_ok = usb_ibmcam_model2_setup(ibmcam); + if (setup_ok) + ibmcam->initialized = 1; + else + err = -EBUSY; + } + if (!err) + ibmcam->user++; + } + } + up(&ibmcam->lock); + if (err) + MOD_DEC_USE_COUNT; + return err; +} + +/* + * ibmcam_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in ibmcam_open(). + * + * History: + * 1/22/00 Moved scratch buffer deallocation here. + * 1/27/00 Used IBMCAM_NUMSBUF as number of URB buffers. + * 5/24/00 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +static void ibmcam_close(struct video_device *dev) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + int i; + + down(&ibmcam->lock); + + ibmcam_stop_isoc(ibmcam); + + rvfree(ibmcam->fbuf, ibmcam->fbuf_size); + kfree(ibmcam->scratch); + for (i=0; i < IBMCAM_NUMSBUF; i++) + kfree(ibmcam->sbuf[i].data); + + ibmcam->user--; + + if (ibmcam->remove_pending) { + printk(KERN_INFO "ibmcam_close: Final disconnect.\n"); + usb_ibmcam_release(ibmcam); + } + up(&ibmcam->lock); + MOD_DEC_USE_COUNT; +} + +static int ibmcam_init_done(struct video_device *dev) +{ + return 0; +} + +static long ibmcam_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +/* + * ibmcam_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 1/22/00 Corrected VIDIOCSPICT to reject unsupported settings. + */ +static int ibmcam_ioctl(struct video_device *dev, unsigned int cmd, void *arg) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + switch (cmd) { + case VIDIOCGCAP: + { + if (copy_to_user(arg, &ibmcam->vcap, sizeof(ibmcam->vcap))) + return -EFAULT; + return 0; + } + case VIDIOCGCHAN: + { + if (copy_to_user(arg, &ibmcam->vchan, sizeof(ibmcam->vchan))) + return -EFAULT; + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if ((v < 0) || (v >= 3)) /* 3 grades of lighting conditions */ + return -EINVAL; + if (v != ibmcam->vchan.channel) { + ibmcam->vchan.channel = v; + usb_ibmcam_change_lighting_conditions(ibmcam); + } + return 0; + } + case VIDIOCGPICT: + { + if (copy_to_user(arg, &ibmcam->vpic, sizeof(ibmcam->vpic))) + return -EFAULT; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture tmp; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + if (copy_from_user(&tmp, arg, sizeof(tmp))) + return -EFAULT; + ibmcam->vpic.brightness = tmp.brightness; + ibmcam->vpic.hue = tmp.hue; + ibmcam->vpic.colour = tmp.colour; + ibmcam->vpic.contrast = tmp.contrast; + usb_ibmcam_adjust_picture(ibmcam); + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (vw.height != imgheight) + return -EINVAL; + if (vw.width != imgwidth) + return -EINVAL; + + ibmcam->compress = 0; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; + vw.y = 0; + vw.width = imgwidth; + vw.height = imgheight; + vw.chromakey = 0; + vw.flags = usb_ibmcam_calculate_fps(); + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + memset(&vm, 0, sizeof(vm)); + vm.size = MAX_FRAME_SIZE * 2; + vm.frames = 2; + vm.offsets[0] = 0; + vm.offsets[1] = MAX_FRAME_SIZE; + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) + return -EFAULT; + + if (debug >= 1) + printk(KERN_DEBUG "frame: %d, size: %dx%d, format: %d\n", + vm.frame, vm.width, vm.height, vm.format); + + if (vm.format != VIDEO_PALETTE_RGB24) + return -EINVAL; + + if ((vm.frame != 0) && (vm.frame != 1)) + return -EINVAL; + + if (ibmcam->frame[vm.frame].grabstate == FRAME_GRABBING) + return -EBUSY; + + /* Don't compress if the size changed */ + if ((ibmcam->frame[vm.frame].width != vm.width) || + (ibmcam->frame[vm.frame].height != vm.height)) + ibmcam->compress = 0; + + ibmcam->frame[vm.frame].width = vm.width; + ibmcam->frame[vm.frame].height = vm.height; + + /* Mark it as ready */ + ibmcam->frame[vm.frame].grabstate = FRAME_READY; + + return ibmcam_new_frame(ibmcam, vm.frame); + } + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam: syncing to frame %d\n", frame); + + switch (ibmcam->frame[frame].grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: + { + int ntries; + redo: + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EIO; + ntries = 0; + do { + interruptible_sleep_on(&ibmcam->frame[frame].wq); + if (signal_pending(current)) { + if (flags & FLAGS_RETRY_VIDIOCSYNC) { + /* Polling apps will destroy frames with that! */ + ibmcam_new_frame(ibmcam, frame); + usb_ibmcam_testpattern(ibmcam, 1, 0); + ibmcam->curframe = -1; + ibmcam->frame_num++; + + /* This will request another frame. */ + if (waitqueue_active(&ibmcam->frame[frame].wq)) + wake_up_interruptible(&ibmcam->frame[frame].wq); + return 0; + } else { + /* Standard answer: not ready yet! */ + return -EINTR; + } + } + } while (ibmcam->frame[frame].grabstate == FRAME_GRABBING); + + if (ibmcam->frame[frame].grabstate == FRAME_ERROR) { + int ret = ibmcam_new_frame(ibmcam, frame); + if (ret < 0) + return ret; + goto redo; + } + } + case FRAME_DONE: + ibmcam->frame[frame].grabstate = FRAME_UNUSED; + break; + } + + ibmcam->frame[frame].grabstate = FRAME_UNUSED; + + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long ibmcam_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + int frmx = -1; + volatile struct ibmcam_frame *frame; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: %ld bytes, noblock=%d\n", count, noblock); + + if (!IBMCAM_IS_OPERATIONAL(ibmcam) || (buf == NULL)) + return -EFAULT; + + /* See if a frame is completed, then use it. */ + if (ibmcam->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (ibmcam->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + frmx = 1; + + if (noblock && (frmx == -1)) + return -EAGAIN; + + /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ + /* See if a frame is in process (grabbing), then use it. */ + if (frmx == -1) { + if (ibmcam->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (ibmcam->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) + ibmcam_new_frame(ibmcam, frmx = 0); + + frame = &ibmcam->frame[frmx]; + +restart: + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EIO; + while (frame->grabstate == FRAME_GRABBING) { + interruptible_sleep_on((void *)&frame->wq); + if (signal_pending(current)) + return -EINTR; + } + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; + if (ibmcam_new_frame(ibmcam, frmx)) + printk(KERN_ERR "ibmcam_read: ibmcam_new_frame error\n"); + goto restart; + } + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: frmx=%d, bytes_read=%ld, scanlength=%ld\n", + frmx, frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ + if ((count + frame->bytes_read) > frame->scanlength) + count = frame->scanlength - frame->bytes_read; + + if (copy_to_user(buf, frame->data + frame->bytes_read, count)) + return -EFAULT; + + frame->bytes_read += count; + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_read: {copy} count used=%ld, new bytes_read=%ld\n", + count, frame->bytes_read); + + if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + ibmcam->frame[frmx].grabstate = FRAME_UNUSED; + if (ibmcam_new_frame(ibmcam, frmx ? 0 : 1)) + printk(KERN_ERR "ibmcam_read: ibmcam_new_frame returned error\n"); + } + + return count; +} + +static int ibmcam_mmap(struct video_device *dev, const char *adr, unsigned long size) +{ + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (!IBMCAM_IS_OPERATIONAL(ibmcam)) + return -EFAULT; + + if (size > (((2 * MAX_FRAME_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long)ibmcam->fbuf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +static struct video_device ibmcam_template = { + "CPiA USB Camera", + VID_TYPE_CAPTURE, + VID_HARDWARE_CPIA, + ibmcam_open, + ibmcam_close, + ibmcam_read, + ibmcam_write, + NULL, + ibmcam_ioctl, + ibmcam_mmap, + ibmcam_init_done, + NULL, + 0, + 0 +}; + +static void usb_ibmcam_configure_video(struct usb_ibmcam *ibmcam) +{ + if (ibmcam == NULL) + return; + + RESTRICT_TO_RANGE(init_brightness, 0, 255); + RESTRICT_TO_RANGE(init_contrast, 0, 255); + RESTRICT_TO_RANGE(init_color, 0, 255); + RESTRICT_TO_RANGE(init_hue, 0, 255); + RESTRICT_TO_RANGE(hue_correction, 0, 255); + + memset(&ibmcam->vpic, 0, sizeof(ibmcam->vpic)); + memset(&ibmcam->vpic_old, 0x55, sizeof(ibmcam->vpic_old)); + + ibmcam->vpic.colour = init_color << 8; + ibmcam->vpic.hue = init_hue << 8; + ibmcam->vpic.brightness = init_brightness << 8; + ibmcam->vpic.contrast = init_contrast << 8; + ibmcam->vpic.whiteness = 105 << 8; /* This one isn't used */ + ibmcam->vpic.depth = 24; + ibmcam->vpic.palette = VIDEO_PALETTE_RGB24; + + memset(&ibmcam->vcap, 0, sizeof(ibmcam->vcap)); + strcpy(ibmcam->vcap.name, "IBM USB Camera"); + ibmcam->vcap.type = VID_TYPE_CAPTURE; + ibmcam->vcap.channels = 1; + ibmcam->vcap.audios = 0; + ibmcam->vcap.maxwidth = imgwidth; + ibmcam->vcap.maxheight = imgheight; + ibmcam->vcap.minwidth = min_imgwidth; + ibmcam->vcap.minheight = min_imgheight; + + memset(&ibmcam->vchan, 0, sizeof(ibmcam->vchan)); + ibmcam->vchan.flags = 0; + ibmcam->vchan.tuners = 0; + ibmcam->vchan.channel = 0; + ibmcam->vchan.type = VIDEO_TYPE_CAMERA; + strcpy(ibmcam->vchan.name, "Camera"); +} + +/* + * ibmcam_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 1/27/00 Created. + */ +static int ibmcam_find_struct(void) +{ + int i, u; + + for (u = 0; u < MAX_IBMCAM; u++) { + struct usb_ibmcam *ibmcam = &cams[u]; + if (!ibmcam->ibmcam_used) /* This one is free */ + { + ibmcam->ibmcam_used = 1; /* In use now */ + for (i=0; i < IBMCAM_NUMFRAMES; i++) + init_waitqueue_head(&ibmcam->frame[i].wq); + init_MUTEX(&ibmcam->lock); /* to 1 == available */ + ibmcam->dev = NULL; + memcpy(&ibmcam->vdev, &ibmcam_template, sizeof(ibmcam_template)); + return u; + } + } + return -1; +} + +/* + * usb_ibmcam_probe() + * + * This procedure queries device descriptor and accepts the interface + * if it looks like IBM C-it camera. + * + * History: + * 1/22/00 Moved camera init code to ibmcam_open() + * 1/27/00 Changed to use static structures, added locking. + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static void *usb_ibmcam_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_ibmcam *ibmcam = NULL; + const unsigned char *p_rev; + const struct usb_interface_descriptor *interface; + const struct usb_endpoint_descriptor *endpoint; + int devnum, model=0; + + if (debug >= 1) + printk(KERN_DEBUG "ibmcam_probe(%p,%u.)\n", dev, ifnum); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + /* Is it an IBM camera? */ + if ((dev->descriptor.idVendor != 0x0545) || + (dev->descriptor.idProduct != 0x8080)) + return NULL; + + /* Check the version/revision */ + p_rev = (const unsigned char *) &dev->descriptor.bcdDevice; + if (p_rev[1] == 0x00 && p_rev[0] == 0x02) { + if (ifnum != 2) + return NULL; + printk(KERN_INFO "IBM USB camera found (model 1).\n"); + model = IBMCAM_MODEL_1; + } else if (p_rev[1] == 0x03 && p_rev[0] == 0x0A) { + if (ifnum != 0) + return NULL; + printk(KERN_INFO "IBM USB camera found (model 2).\n"); + model = IBMCAM_MODEL_2; + } else { + printk(KERN_ERR "IBM camera revision=%02x.%02x not supported\n", + p_rev[1], p_rev[0]); + return NULL; + } + + /* Validate found interface: must have one ISO endpoint */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + if (interface->bNumEndpoints != 1) { + printk(KERN_ERR "IBM camera: interface %d. has %u. endpoints!\n", + ifnum, (unsigned)(interface->bNumEndpoints)); + return NULL; + } + endpoint = &interface->endpoint[0]; + if ((endpoint->bmAttributes & 0x03) != 0x01) { + printk(KERN_ERR "IBM camera: interface %d. has non-ISO endpoint!\n", ifnum); + return NULL; + } + if ((endpoint->bEndpointAddress & 0x80) == 0) { + printk(KERN_ERR "IBM camera: interface %d. has ISO OUT endpoint!\n", ifnum); + return NULL; + } + + /* Validate options */ + if (model == IBMCAM_MODEL_1) { + RESTRICT_TO_RANGE(lighting, 0, 2); + RESTRICT_TO_RANGE(videosize, VIDEOSIZE_128x96, VIDEOSIZE_352x288); + } else { + RESTRICT_TO_RANGE(lighting, 0, 15); + RESTRICT_TO_RANGE(videosize, VIDEOSIZE_176x144, VIDEOSIZE_352x240); + } + + /* Code below may sleep, need to lock module while we are here */ + MOD_INC_USE_COUNT; + + devnum = ibmcam_find_struct(); + if (devnum == -1) { + printk(KERN_INFO "IBM USB camera driver: Too many devices!\n"); + ibmcam = NULL; /* Do not free, it's preallocated */ + goto probe_done; + } + ibmcam = &cams[devnum]; + + down(&ibmcam->lock); + ibmcam->camera_model = model; + ibmcam->remove_pending = 0; + ibmcam->last_error = 0; + ibmcam->dev = dev; + ibmcam->iface = ifnum; + ibmcam->ifaceAltInactive = 0; + ibmcam->ifaceAltActive = 1; + ibmcam->video_endp = endpoint->bEndpointAddress; + ibmcam->iso_packet_len = 1014; + ibmcam->compress = 0; + ibmcam->user=0; + + usb_ibmcam_configure_video(ibmcam); + up (&ibmcam->lock); + + if (video_register_device(&ibmcam->vdev, VFL_TYPE_GRABBER) == -1) { + printk(KERN_ERR "video_register_device failed\n"); + ibmcam = NULL; /* Do not free, it's preallocated */ + } + if (debug > 1) + printk(KERN_DEBUG "video_register_device() successful\n"); +probe_done: + MOD_DEC_USE_COUNT; + return ibmcam; +} + +/* + * usb_ibmcam_release() + * + * This code does final release of struct usb_ibmcam. This happens + * after the device is disconnected -and- all clients closed their files. + * + * History: + * 1/27/00 Created. + */ +static void usb_ibmcam_release(struct usb_ibmcam *ibmcam) +{ + video_unregister_device(&ibmcam->vdev); + if (debug > 0) + printk(KERN_DEBUG "usb_ibmcam_release: Video unregistered.\n"); + ibmcam->ibmcam_used = 0; +} + +/* + * usb_ibmcam_disconnect() + * + * This procedure stops all driver activity, deallocates interface-private + * structure (pointed by 'ptr') and after that driver should be removable + * with no ill consequences. + * + * This code handles surprise removal. The ibmcam->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set ibmcam->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * ibmcam_close() - we decrement ibmcam->ibmcam_used and usage counter. + * + * History: + * 1/22/00 Added polling of MOD_IN_USE to delay removal until all users gone. + * 1/27/00 Reworked to allow pending disconnects; see ibmcam_close() + * 5/24/00 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static void usb_ibmcam_disconnect(struct usb_device *dev, void *ptr) +{ + static const char proc[] = "usb_ibmcam_disconnect"; + struct usb_ibmcam *ibmcam = (struct usb_ibmcam *) ptr; + + MOD_INC_USE_COUNT; + + if (debug > 0) + printk(KERN_DEBUG "%s(%p,%p.)\n", proc, dev, ptr); + + down(&ibmcam->lock); + ibmcam->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + ibmcam_stop_isoc(ibmcam); + + ibmcam->dev = NULL; /* USB device is no more */ + + if (ibmcam->user) + printk(KERN_INFO "%s: In use, disconnect pending.\n", proc); + else + usb_ibmcam_release(ibmcam); + up(&ibmcam->lock); + printk(KERN_INFO "IBM USB camera disconnected.\n"); + + MOD_DEC_USE_COUNT; +} + +static struct usb_driver ibmcam_driver = { + "ibmcam", + usb_ibmcam_probe, + usb_ibmcam_disconnect, + { NULL, NULL } +}; + +/* + * usb_ibmcam_init() + * + * This code is run to initialize the driver. + * + * History: + * 1/27/00 Reworked to use statically allocated usb_ibmcam structures. + */ +int usb_ibmcam_init(void) +{ + unsigned u; + + /* Initialize struct */ + for (u = 0; u < MAX_IBMCAM; u++) { + struct usb_ibmcam *ibmcam = &cams[u]; + memset (ibmcam, 0, sizeof(struct usb_ibmcam)); + } + return usb_register(&ibmcam_driver); +} + +void usb_ibmcam_cleanup(void) +{ + usb_deregister(&ibmcam_driver); +} + +#ifdef MODULE +int init_module(void) +{ + return usb_ibmcam_init(); +} + +void cleanup_module(void) +{ + usb_ibmcam_cleanup(); +} +#endif diff --git a/drivers/usb/ibmcam.h b/drivers/usb/ibmcam.h new file mode 100644 index 000000000000..b2511dbdc8f1 --- /dev/null +++ b/drivers/usb/ibmcam.h @@ -0,0 +1,240 @@ +/* + * Header file for USB IBM C-It Video Camera driver. + * + * Supports IBM C-It Video Camera. + * + * This driver is based on earlier work of: + * + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + */ + +#ifndef __LINUX_IBMCAM_H +#define __LINUX_IBMCAM_H + +#include + +#define USES_IBMCAM_PUTPIXEL 0 /* 0=Fast/oops 1=Slow/secure */ + +/* Video Size 384 x 288 x 3 bytes for RGB */ +/* 384 because xawtv tries to grab 384 even though we tell it 352 is our max */ +#define V4L_FRAME_WIDTH 384 +#define V4L_FRAME_WIDTH_USED 352 +#define V4L_FRAME_HEIGHT 288 +#define V4L_BYTES_PER_PIXEL 3 +#define MAX_FRAME_SIZE (V4L_FRAME_WIDTH * V4L_FRAME_HEIGHT * V4L_BYTES_PER_PIXEL) + +/* Camera capabilities (maximum) */ +#define CAMERA_IMAGE_WIDTH 352 +#define CAMERA_IMAGE_HEIGHT 288 +#define CAMERA_IMAGE_LINE_SZ ((CAMERA_IMAGE_WIDTH * 3) / 2) /* Bytes */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ + +#define IBMCAM_NUMFRAMES 2 +#define IBMCAM_NUMSBUF 2 + +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +/* + * This macro performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + */ +#define IBMCAM_PUTPIXEL(fr, ix, iy, vr, vg, vb) { \ + register unsigned char *pf; \ + int limiter = 0, mx, my; \ + mx = ix; \ + my = iy; \ + if (mx < 0) { \ + mx=0; \ + limiter++; \ + } else if (mx >= 352) { \ + mx=351; \ + limiter++; \ + } \ + if (my < 0) { \ + my = 0; \ + limiter++; \ + } else if (my >= V4L_FRAME_HEIGHT) { \ + my = V4L_FRAME_HEIGHT - 1; \ + limiter++; \ + } \ + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*352 + (ix)); \ + if (limiter) { \ + *pf++ = 0; \ + *pf++ = 0; \ + *pf++ = 0xFF; \ + } else { \ + *pf++ = (vb); \ + *pf++ = (vg); \ + *pf++ = (vr); \ + } \ +} + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +/* Debugging aid */ +#define IBMCAM_SAY_AND_WAIT(what) { \ + wait_queue_head_t wq; \ + init_waitqueue_head(&wq); \ + printk(KERN_INFO "Say: %s\n", what); \ + interruptible_sleep_on_timeout (&wq, HZ*3); \ +} + +/* + * This macro checks if ibmcam is still operational. The 'ibmcam' + * pointer must be valid, ibmcam->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define IBMCAM_IS_OPERATIONAL(ibm_cam) (\ + (ibm_cam != NULL) && \ + ((ibm_cam)->dev != NULL) && \ + ((ibm_cam)->last_error == 0) && \ + (!(ibm_cam)->remove_pending)) + +enum { + STATE_SCANNING, /* Scanning for header */ + STATE_LINES, /* Parsing lines */ +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +struct usb_device; + +struct ibmcam_sbuf { + char *data; + urb_t *urb; +}; + +struct ibmcam_frame { + char *data; /* Frame buffer */ + int order_uv; /* True=UV False=VU */ + int order_yc; /* True=Yc False=cY ('c'=either U or V) */ + unsigned char hdr_sig; /* "00 FF 00 ??" where 'hdr_sig' is '??' */ + + int width; /* Width application is expecting */ + int height; /* Height */ + + int frmwidth; /* Width the frame actually is */ + int frmheight; /* Height */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int curline; /* Line of frame we're working on */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from *data */ + + wait_queue_head_t wq; /* Processes waiting */ +}; + +#define IBMCAM_MODEL_1 1 /* XVP-501, 3 interfaces, rev. 0.02 */ +#define IBMCAM_MODEL_2 2 /* KSX-X9903, 2 interfaces, rev. 3.0a */ + +struct usb_ibmcam { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; /* Video interface number */ + unsigned char ifaceAltActive, ifaceAltInactive; /* Alt settings */ + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int ibmcam_used; /* Is this structure in use? */ + int initialized; /* Had we already sent init sequence? */ + int camera_model; /* What type of IBM camera we got? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int last_error; /* What calamity struck us? */ + + int compress; /* Should the next frame be compressed? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + struct ibmcam_frame frame[IBMCAM_NUMFRAMES]; /* Double buffering */ + + int cursbuf; /* Current receiving sbuf */ + struct ibmcam_sbuf sbuf[IBMCAM_NUMSBUF]; /* Double buffering */ + volatile int remove_pending; /* If set then about to exit */ + + /* + * Scratch space from the Isochronous pipe. + * Scratch buffer should contain at least one pair of lines + * (CAMERA_IMAGE_LINE_SZ). We set it to two pairs here. + * This will be approximately 2 KB. HOWEVER in reality this + * buffer must be as large as hundred of KB because otherwise + * you'll get lots of overflows because V4L client may request + * frames not as uniformly as USB sources them. + */ + unsigned char *scratch; + int scratchlen; + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + unsigned char video_endp; /* 0x82 for IBM camera */ + int has_hdr; + int frame_num; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + /* Statistics that can be overlayed on screen */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long scratch_ovf_count;/* How many times we overflowed scratch */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +}; + +#endif /* __LINUX_IBMCAM_H */ diff --git a/drivers/usb/inits.h b/drivers/usb/inits.h deleted file mode 100644 index 81979ce399fa..000000000000 --- a/drivers/usb/inits.h +++ /dev/null @@ -1,6 +0,0 @@ -int bp_mouse_init(void); -int usb_kbd_init(void); -int usb_audio_init(void); -int hub_init(void); -void hub_cleanup(void); -void usb_mouse_cleanup(void); diff --git a/drivers/usb/inode.c b/drivers/usb/inode.c new file mode 100644 index 000000000000..280b9b91b065 --- /dev/null +++ b/drivers/usb/inode.c @@ -0,0 +1,712 @@ +/*****************************************************************************/ + +/* + * inode.c -- Inode/Dentry functions for the USB device file system. + * + * Copyright (C) 2000 + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: inode.c,v 1.3 2000/01/11 13:58:25 tom Exp $ + * + * History: + * 0.1 04.01.2000 Created + */ + +/*****************************************************************************/ + +#define __NO_VERSION__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +static LIST_HEAD(superlist); + +struct special { + const char *name; + struct inode_operations *iops; + struct list_head inodes; +}; + +static struct inode_operations usbdevfs_devices_inode_operations = { + default_file_ops: &usbdevfs_devices_fops, +}; + +static struct inode_operations usbdevfs_drivers_inode_operations = { + default_file_ops: &usbdevfs_drivers_fops, +}; + +static struct inode_operations usbdevfs_device_inode_operations = { + default_file_ops: &usbdevfs_device_file_operations, +}; + +static struct special special[] = { + { "devices", &usbdevfs_devices_inode_operations, }, + { "drivers", &usbdevfs_drivers_inode_operations, } +}; + +#define NRSPECIAL (sizeof(special)/sizeof(special[0])) + +/* --------------------------------------------------------------------- */ + +static int dnumber(struct dentry *dentry) +{ + const char *name; + unsigned int s; + + if (dentry->d_name.len != 3) + return -1; + name = dentry->d_name.name; + if (name[0] < '0' || name[0] > '9' || + name[1] < '0' || name[1] > '9' || + name[2] < '0' || name[2] > '9') + return -1; + s = name[0] - '0'; + s = s * 10 + name[1] - '0'; + s = s * 10 + name[2] - '0'; + return s; +} + +/* + * utility functions; should be called with the kernel lock held + * to protect against busses/devices appearing/disappearing + */ + +static void new_dev_inode(struct usb_device *dev, struct super_block *sb) +{ + struct inode *inode; + unsigned int devnum = dev->devnum; + unsigned int busnum = dev->bus->busnum; + + if (devnum < 1 || devnum > 127 || busnum > 255) + return; + inode = iget(sb, IDEVICE | (busnum << 8) | devnum); + if (!inode) { + printk(KERN_ERR "usbdevfs: cannot create inode for bus %u device %u\n", busnum, devnum); + return; + } + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_uid = sb->u.usbdevfs_sb.devuid; + inode->i_gid = sb->u.usbdevfs_sb.devgid; + inode->i_mode = sb->u.usbdevfs_sb.devmode | S_IFREG; + inode->i_op = &usbdevfs_device_inode_operations; + inode->i_size = sizeof(struct usb_device_descriptor); + inode->u.usbdev_i.p.dev = dev; + list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &dev->inodes); +} + +static void recurse_new_dev_inode(struct usb_device *dev, struct super_block *sb) +{ + unsigned int i; + + if (!dev) + return; + new_dev_inode(dev, sb); + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + recurse_new_dev_inode(dev->children[i], sb); + } +} + +static void new_bus_inode(struct usb_bus *bus, struct super_block *sb) +{ + struct inode *inode; + unsigned int busnum = bus->busnum; + + if (busnum > 255) + return; + inode = iget(sb, IBUS | (busnum << 8)); + if (!inode) { + printk(KERN_ERR "usbdevfs: cannot create inode for bus %u\n", busnum); + return; + } + inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_uid = sb->u.usbdevfs_sb.busuid; + inode->i_gid = sb->u.usbdevfs_sb.busgid; + inode->i_mode = sb->u.usbdevfs_sb.busmode | S_IFDIR; + inode->i_op = &usbdevfs_bus_inode_operations; + inode->u.usbdev_i.p.bus = bus; + list_add_tail(&inode->u.usbdev_i.slist, &sb->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &bus->inodes); +} + +static void free_inode(struct inode *inode) +{ + inode->u.usbdev_i.p.bus = NULL; + inode->u.usbdev_i.p.dev = NULL; + inode->i_mode &= ~S_IRWXUGO; + inode->i_uid = inode->i_gid = 0; + inode->i_size = 0; + list_del(&inode->u.usbdev_i.slist); + INIT_LIST_HEAD(&inode->u.usbdev_i.slist); + list_del(&inode->u.usbdev_i.dlist); + INIT_LIST_HEAD(&inode->u.usbdev_i.dlist); + iput(inode); +} + +static struct usb_bus *usbdevfs_findbus(int busnr) +{ + struct list_head *list; + struct usb_bus *bus; + + for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { + bus = list_entry(list, struct usb_bus, bus_list); + if (bus->busnum == busnr) + return bus; + } + return NULL; +} + +#if 0 +static struct usb_device *finddev(struct usb_device *dev, int devnr) +{ + unsigned int i; + struct usb_device *d2; + + if (!dev) + return NULL; + if (dev->devnum == devnr) + return dev; + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + if ((d2 = finddev(dev->children[i], devnr))) + return d2; + } + return NULL; +} + +static struct usb_device *usbdevfs_finddevice(struct usb_bus *bus, int devnr) +{ + return finddev(bus->root_hub, devnr); +} +#endif + +/* --------------------------------------------------------------------- */ + +static int usbdevfs_revalidate(struct dentry *dentry, int flags) +{ + struct inode *inode = dentry->d_inode; + + if (!inode) + return 0; + if (ITYPE(inode->i_ino) == IBUS && !inode->u.usbdev_i.p.bus) + return 0; + if (ITYPE(inode->i_ino) == IDEVICE && !inode->u.usbdev_i.p.dev) + return 0; + return 1; +} + +static struct dentry_operations usbdevfs_dentry_operations = { + d_revalidate: usbdevfs_revalidate, +}; + +static struct dentry *usbdevfs_root_lookup(struct inode *dir, struct dentry *dentry) +{ + int busnr; + unsigned long ino = 0; + unsigned int i; + struct inode *inode; + + /* sanity check */ + if (dir->i_ino != IROOT) + return ERR_PTR(-EINVAL); + dentry->d_op = &usbdevfs_dentry_operations; + busnr = dnumber(dentry); + if (busnr >= 0 && busnr <= 255) + ino = IBUS | (busnr << 8); + if (!ino) { + for (i = 0; i < NRSPECIAL; i++) { + if (strlen(special[i].name) == dentry->d_name.len && + !strncmp(special[i].name, dentry->d_name.name, dentry->d_name.len)) { + ino = ISPECIAL | (i + IROOT + 1); + break; + } + } + } + if (!ino) + return ERR_PTR(-ENOENT); + inode = iget(dir->i_sb, ino); + if (!inode) + return ERR_PTR(-EINVAL); + if (inode && ITYPE(ino) == IBUS && inode->u.usbdev_i.p.bus == NULL) { + iput(inode); + inode = NULL; + } + d_add(dentry, inode); + return NULL; +} + +static struct dentry *usbdevfs_bus_lookup(struct inode *dir, struct dentry *dentry) +{ + struct inode *inode; + int devnr; + + /* sanity check */ + if (ITYPE(dir->i_ino) != IBUS) + return ERR_PTR(-EINVAL); + dentry->d_op = &usbdevfs_dentry_operations; + devnr = dnumber(dentry); + if (devnr < 1 || devnr > 127) + return ERR_PTR(-ENOENT); + inode = iget(dir->i_sb, IDEVICE | (dir->i_ino & (0xff << 8)) | devnr); + if (!inode) + return ERR_PTR(-EINVAL); + if (inode && inode->u.usbdev_i.p.dev == NULL) { + iput(inode); + inode = NULL; + } + d_add(dentry, inode); + return NULL; +} + +static int usbdevfs_root_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned long ino = inode->i_ino; + struct special *spec; + struct list_head *list; + struct usb_bus *bus; + char numbuf[8]; + unsigned int i; + + /* sanity check */ + if (ino != IROOT) + return -EINVAL; + i = filp->f_pos; + switch (i) { + case 0: + if (filldir(dirent, ".", 1, i, IROOT) < 0) + return 0; + filp->f_pos++; + i++; + /* fall through */ + + case 1: + if (filldir(dirent, "..", 2, i, IROOT) < 0) + return 0; + filp->f_pos++; + i++; + /* fall through */ + + default: + + while (i >= 2 && i < 2+NRSPECIAL) { + spec = &special[filp->f_pos-2]; + if (filldir(dirent, spec->name, strlen(spec->name), i, ISPECIAL | (filp->f_pos-2+IROOT)) < 0) + return 0; + filp->f_pos++; + i++; + } + if (i < 2+NRSPECIAL) + return 0; + i -= 2+NRSPECIAL; + lock_kernel(); + for (list = usb_bus_list.next; list != &usb_bus_list; list = list->next) { + if (i > 0) { + i--; + continue; + } + bus = list_entry(list, struct usb_bus, bus_list); + sprintf(numbuf, "%03d", bus->busnum); + if (filldir(dirent, numbuf, 3, filp->f_pos, IBUS | ((bus->busnum & 0xff) << 8)) < 0) + break; + filp->f_pos++; + } + unlock_kernel(); + return 0; + } +} + +static int bus_readdir(struct usb_device *dev, unsigned long ino, int pos, struct file *filp, void *dirent, filldir_t filldir) +{ + char numbuf[8]; + unsigned int i; + + if (!dev) + return pos; + sprintf(numbuf, "%03d", dev->devnum); + if (pos > 0) + pos--; + else { + if (filldir(dirent, numbuf, 3, filp->f_pos, ino | (dev->devnum & 0xff)) < 0) + return -1; + filp->f_pos++; + } + for (i = 0; i < dev->maxchild; i++) { + if (!dev->children[i]) + continue; + pos = bus_readdir(dev->children[i], ino, pos, filp, dirent, filldir); + if (pos < 0) + return -1; + } + return pos; +} + +static int usbdevfs_bus_readdir(struct file *filp, void *dirent, filldir_t filldir) +{ + struct inode *inode = filp->f_dentry->d_inode; + unsigned long ino = inode->i_ino; + struct usb_bus *bus; + + /* sanity check */ + if (ITYPE(ino) != IBUS) + return -EINVAL; + switch ((unsigned int)filp->f_pos) { + case 0: + if (filldir(dirent, ".", 1, filp->f_pos, ino) < 0) + return 0; + filp->f_pos++; + /* fall through */ + + case 1: + if (filldir(dirent, "..", 2, filp->f_pos, IROOT) < 0) + return 0; + filp->f_pos++; + /* fall through */ + + default: + lock_kernel(); + bus = usbdevfs_findbus(IBUSNR(ino)); + bus_readdir(bus->root_hub, IDEVICE | ((bus->busnum & 0xff) << 8), filp->f_pos-2, filp, dirent, filldir); + unlock_kernel(); + return 0; + } +} + +static struct file_operations usbdevfs_root_file_operations = { + readdir: usbdevfs_root_readdir, +}; + +static struct inode_operations usbdevfs_root_inode_operations = { + default_file_ops: &usbdevfs_root_file_operations, + lookup: usbdevfs_root_lookup, +}; + +static struct file_operations usbdevfs_bus_file_operations = { + readdir: usbdevfs_bus_readdir, +}; + +static struct inode_operations usbdevfs_bus_inode_operations = { + default_file_ops: &usbdevfs_bus_file_operations, + lookup: usbdevfs_bus_lookup, +}; + +static void usbdevfs_read_inode(struct inode *inode) +{ + struct special *spec; + + inode->i_ctime = inode->i_mtime = inode->i_atime = CURRENT_TIME; + inode->i_mode = S_IFREG; + inode->i_gid = inode->i_uid = 0; + INIT_LIST_HEAD(&inode->u.usbdev_i.dlist); + INIT_LIST_HEAD(&inode->u.usbdev_i.slist); + inode->u.usbdev_i.p.dev = NULL; + inode->u.usbdev_i.p.bus = NULL; + switch (ITYPE(inode->i_ino)) { + case ISPECIAL: + if (inode->i_ino == IROOT) { + inode->i_op = &usbdevfs_root_inode_operations; + inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO; + return; + } + if (inode->i_ino <= IROOT || inode->i_ino > IROOT+NRSPECIAL) + return; + spec = &special[inode->i_ino-(IROOT+1)]; + inode->i_op = spec->iops; + return; + + case IDEVICE: + return; + + case IBUS: + return; + + default: + return; + } +} + +static void usbdevfs_put_super(struct super_block *sb) +{ + list_del(&sb->u.usbdevfs_sb.slist); + INIT_LIST_HEAD(&sb->u.usbdevfs_sb.slist); + while (!list_empty(&sb->u.usbdevfs_sb.ilist)) + free_inode(list_entry(sb->u.usbdevfs_sb.ilist.next, struct inode, u.usbdev_i.slist)); + MOD_DEC_USE_COUNT; +} + +static int usbdevfs_statfs(struct super_block *sb, struct statfs *buf, int x) +{ + buf->f_type = USBDEVICE_SUPER_MAGIC; + buf->f_bsize = PAGE_SIZE/sizeof(long); /* ??? */ + buf->f_bfree = 0; + buf->f_bavail = 0; + buf->f_ffree = 0; + buf->f_namelen = NAME_MAX; + return 0; +} + +static struct super_operations usbdevfs_sops = { + read_inode: usbdevfs_read_inode, + put_super: usbdevfs_put_super, + statfs: usbdevfs_statfs, +}; + +struct super_block *usbdevfs_read_super(struct super_block *s, void *data, int silent) +{ + struct inode *root_inode, *inode; + struct list_head *blist; + struct usb_bus *bus; + unsigned int i; + uid_t devuid = 0, busuid = 0, listuid = 0; + gid_t devgid = 0, busgid = 0, listgid = 0; + umode_t devmode = S_IWUSR | S_IRUGO, busmode = S_IXUGO | S_IRUGO, listmode = S_IRUGO; + char *curopt = NULL, *value; + + MOD_INC_USE_COUNT; + /* parse options */ + if (data) + curopt = strtok(data, ","); + for (; curopt; curopt = strtok(NULL, ",")) { + if ((value = strchr(curopt, '=')) != NULL) + *value++ = 0; + if (!strcmp(curopt, "devuid")) { + if (!value || !value[0]) + goto opterr; + devuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "devgid")) { + if (!value || !value[0]) + goto opterr; + devgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "devmode")) { + if (!value || !value[0]) + goto opterr; + devmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busuid")) { + if (!value || !value[0]) + goto opterr; + busuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busgid")) { + if (!value || !value[0]) + goto opterr; + busgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "busmode")) { + if (!value || !value[0]) + goto opterr; + busmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listuid")) { + if (!value || !value[0]) + goto opterr; + listuid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listgid")) { + if (!value || !value[0]) + goto opterr; + listgid = simple_strtoul(value, &value, 0); + if (*value) + goto opterr; + } + if (!strcmp(curopt, "listmode")) { + if (!value || !value[0]) + goto opterr; + listmode = simple_strtoul(value, &value, 0) & S_IRWXUGO; + if (*value) + goto opterr; + } + } + /* fill superblock */ + s->s_blocksize = 1024; + s->s_blocksize_bits = 10; + s->s_magic = USBDEVICE_SUPER_MAGIC; + s->s_op = &usbdevfs_sops; + INIT_LIST_HEAD(&s->u.usbdevfs_sb.slist); + INIT_LIST_HEAD(&s->u.usbdevfs_sb.ilist); + s->u.usbdevfs_sb.devuid = devuid; + s->u.usbdevfs_sb.devgid = devgid; + s->u.usbdevfs_sb.devmode = devmode; + s->u.usbdevfs_sb.busuid = busuid; + s->u.usbdevfs_sb.busgid = busgid; + s->u.usbdevfs_sb.busmode = busmode; + root_inode = iget(s, IROOT); + if (!root_inode) + goto out_no_root; + s->s_root = d_alloc_root(root_inode, NULL); + if (!s->s_root) + goto out_no_root; + list_add_tail(&s->u.usbdevfs_sb.slist, &superlist); + for (i = 0; i < NRSPECIAL; i++) { + if (!(inode = iget(s, IROOT+1+i))) + continue; + inode->i_uid = listuid; + inode->i_gid = listgid; + inode->i_mode = listmode | S_IFREG; + list_add_tail(&inode->u.usbdev_i.slist, &s->u.usbdevfs_sb.ilist); + list_add_tail(&inode->u.usbdev_i.dlist, &special[i].inodes); + } + lock_kernel(); + for (blist = usb_bus_list.next; blist != &usb_bus_list; blist = blist->next) { + bus = list_entry(blist, struct usb_bus, bus_list); + new_bus_inode(bus, s); + recurse_new_dev_inode(bus->root_hub, s); + } + unlock_kernel(); + return s; + + out_no_root: + printk("usbdevfs_read_super: get root inode failed\n"); + iput(root_inode); + MOD_DEC_USE_COUNT; + return NULL; + + opterr: + printk(KERN_WARNING "usbdevfs: mount parameter error\n"); + MOD_DEC_USE_COUNT; + return NULL; +} + +static DECLARE_FSTYPE(usbdevice_fs_type, "usbdevfs", usbdevfs_read_super, 0); + +/* --------------------------------------------------------------------- */ + +void usbdevfs_add_bus(struct usb_bus *bus) +{ + struct list_head *slist; + + lock_kernel(); + for (slist = superlist.next; slist != &superlist; slist = slist->next) + new_bus_inode(bus, list_entry(slist, struct super_block, u.usbdevfs_sb.slist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_remove_bus(struct usb_bus *bus) +{ + lock_kernel(); + while (!list_empty(&bus->inodes)) + free_inode(list_entry(bus->inodes.next, struct inode, u.usbdev_i.dlist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_add_device(struct usb_device *dev) +{ + struct list_head *slist; + + lock_kernel(); + for (slist = superlist.next; slist != &superlist; slist = slist->next) + new_dev_inode(dev, list_entry(slist, struct super_block, u.usbdevfs_sb.slist)); + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +void usbdevfs_remove_device(struct usb_device *dev) +{ + struct dev_state *ds; + struct siginfo sinfo; + + lock_kernel(); + while (!list_empty(&dev->inodes)) + free_inode(list_entry(dev->inodes.next, struct inode, u.usbdev_i.dlist)); + while (!list_empty(&dev->filelist)) { + ds = list_entry(dev->filelist.next, struct dev_state, list); + list_del(&ds->list); + INIT_LIST_HEAD(&ds->list); + down_write(&ds->devsem); + ds->dev = NULL; + up_write(&ds->devsem); + if (ds->discsignr) { + sinfo.si_signo = SIGPIPE; + sinfo.si_errno = EPIPE; + sinfo.si_code = SI_ASYNCIO; + sinfo.si_addr = ds->disccontext; + send_sig_info(ds->discsignr, &sinfo, ds->disctask); + } + } + unlock_kernel(); + usbdevfs_conn_disc_event(); +} + +/* --------------------------------------------------------------------- */ + +#ifdef CONFIG_PROC_FS +static struct proc_dir_entry *usbdir = NULL; +#endif + +int __init usbdevfs_init(void) +{ + int ret; + + for (ret = 0; ret < NRSPECIAL; ret++) { + INIT_LIST_HEAD(&special[ret].inodes); + } + if ((ret = usb_register(&usbdevfs_driver))) + return ret; + if ((ret = register_filesystem(&usbdevice_fs_type))) + usb_deregister(&usbdevfs_driver); +#ifdef CONFIG_PROC_FS + /* create mount point for usbdevfs */ + usbdir = proc_mkdir("usb", proc_bus); +#endif + return ret; +} + +void __exit usbdevfs_cleanup(void) +{ +#ifdef CONFIG_PROC_FS + if (usbdir) + remove_proc_entry("usb", proc_bus); +#endif + usb_deregister(&usbdevfs_driver); + unregister_filesystem(&usbdevice_fs_type); +} + +#if 0 +module_init(usbdevfs_init); +module_exit(usbdevfs_cleanup); +#endif diff --git a/drivers/usb/input.c b/drivers/usb/input.c new file mode 100644 index 000000000000..f5e7213c9de8 --- /dev/null +++ b/drivers/usb/input.c @@ -0,0 +1,408 @@ +/* + * $Id: input.c,v 1.7 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * The input layer module itself + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +EXPORT_SYMBOL(input_register_device); +EXPORT_SYMBOL(input_unregister_device); +EXPORT_SYMBOL(input_register_handler); +EXPORT_SYMBOL(input_unregister_handler); +EXPORT_SYMBOL(input_register_minor); +EXPORT_SYMBOL(input_unregister_minor); +EXPORT_SYMBOL(input_open_device); +EXPORT_SYMBOL(input_close_device); +EXPORT_SYMBOL(input_event); + +#define INPUT_MAJOR 13 +#define INPUT_DEVICES 256 + +static struct input_dev *input_dev = NULL; +static struct input_handler *input_handler = NULL; +static struct input_handler *input_table[8] = { NULL, /* ... */ }; +static devfs_handle_t input_devfs_handle = NULL; +static int input_number = 0; +static long input_devices[NBITS(INPUT_DEVICES)] = { 0, /* ... */ }; + +void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct input_handle *handle = dev->handle; + +/* + * Filter non-events, and bad input values out. + */ + + if (type > EV_MAX || !test_bit(type, dev->evbit)) + return; + + switch (type) { + + case EV_KEY: + + if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value) + return; + + if (value == 2) break; + + change_bit(code, dev->key); + + if (test_bit(EV_REP, dev->evbit) && dev->timer.function) { + if (value) { + mod_timer(&dev->timer, jiffies + dev->rep[REP_DELAY]); + dev->repeat_key = code; + break; + } + if (dev->repeat_key == code) + del_timer(&dev->timer); + } + + break; + + case EV_ABS: + + if (code > ABS_MAX || !test_bit(code, dev->absbit)) + return; + + if (dev->absfuzz[code]) { + if ((value > dev->abs[code] - (dev->absfuzz[code] >> 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] >> 1))) + return; + + if ((value > dev->abs[code] - dev->absfuzz[code]) && + (value < dev->abs[code] + dev->absfuzz[code])) + value = (dev->abs[code] * 3 + value) >> 2; + + if ((value > dev->abs[code] - (dev->absfuzz[code] << 1)) && + (value < dev->abs[code] + (dev->absfuzz[code] << 1))) + value = (dev->abs[code] + value) >> 1; + } + + if (dev->abs[code] == value) + return; + + dev->abs[code] = value; + break; + + case EV_REL: + + if (code > REL_MAX || !test_bit(code, dev->relbit) || (value == 0)) + return; + + break; + + case EV_LED: + + if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value) + return; + + change_bit(code, dev->led); + if (dev->event) dev->event(dev, type, code, value); + + break; + + case EV_SND: + + if (code > SND_MAX || !test_bit(code, dev->sndbit) || !!test_bit(code, dev->snd) == value) + return; + + change_bit(code, dev->snd); + if (dev->event) dev->event(dev, type, code, value); + + break; + + case EV_REP: + + if (code > REP_MAX || dev->rep[code] == value) return; + + dev->rep[code] = value; + if (dev->event) dev->event(dev, type, code, value); + + break; + } +/* + * Add randomness. + */ + +#if 0 /* BUG */ + add_input_randomness(((unsigned long) dev) ^ (type << 24) ^ (code << 16) ^ value); +#endif + +/* + * Distribute the event to handler modules. + */ + + while (handle) { + if (handle->open) + handle->handler->event(handle, type, code, value); + handle = handle->dnext; + } +} + +static void input_repeat_key(unsigned long data) +{ + struct input_dev *dev = (void *) data; + input_event(dev, EV_KEY, dev->repeat_key, 2); + mod_timer(&dev->timer, jiffies + dev->rep[REP_PERIOD]); +} + +int input_open_device(struct input_handle *handle) +{ + handle->open++; + if (handle->dev->open) + return handle->dev->open(handle->dev); + return 0; +} + +void input_close_device(struct input_handle *handle) +{ + if (handle->dev->close) + handle->dev->close(handle->dev); + handle->open--; +} + +static void input_link_handle(struct input_handle *handle) +{ + handle->dnext = handle->dev->handle; + handle->hnext = handle->handler->handle; + handle->dev->handle = handle; + handle->handler->handle = handle; +} + +static void input_unlink_handle(struct input_handle *handle) +{ + struct input_handle **handleptr; + + handleptr = &handle->dev->handle; + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->dnext); + *handleptr = (*handleptr)->dnext; + + handleptr = &handle->handler->handle; + while (*handleptr && (*handleptr != handle)) + handleptr = &((*handleptr)->hnext); + *handleptr = (*handleptr)->hnext; +} + +void input_register_device(struct input_dev *dev) +{ + struct input_handler *handler = input_handler; + struct input_handle *handle; + +/* + * Initialize repeat timer to default values. + */ + + init_timer(&dev->timer); + dev->timer.data = (long) dev; + dev->timer.function = input_repeat_key; + dev->rep[REP_DELAY] = HZ/4; + dev->rep[REP_PERIOD] = HZ/33; + +/* + * Add the device. + */ + + if (input_number >= INPUT_DEVICES) { + printk(KERN_WARNING "input: ran out of input device numbers!\n"); + dev->number = input_number; + } else { + dev->number = find_first_zero_bit(input_devices, INPUT_DEVICES); + set_bit(dev->number, input_devices); + } + + dev->next = input_dev; + input_dev = dev; + input_number++; + +/* + * Notify handlers. + */ + + while (handler) { + if ((handle = handler->connect(handler, dev))) + input_link_handle(handle); + handler = handler->next; + } +} + +void input_unregister_device(struct input_dev *dev) +{ + struct input_handle *handle = dev->handle; + struct input_dev **devptr = &input_dev; + struct input_handle *dnext; + +/* + * Kill any pending repeat timers. + */ + + del_timer(&dev->timer); + +/* + * Notify handlers. + */ + + while (handle) { + dnext = handle->dnext; + input_unlink_handle(handle); + handle->handler->disconnect(handle); + handle = dnext; + } + +/* + * Remove the device. + */ + + while (*devptr && (*devptr != dev)) + devptr = &((*devptr)->next); + *devptr = (*devptr)->next; + + input_number--; + + if (dev->number < INPUT_DEVICES) + clear_bit(dev->number, input_devices); +} + +void input_register_handler(struct input_handler *handler) +{ + struct input_dev *dev = input_dev; + struct input_handle *handle; + +/* + * Add minors if needed. + */ + + if (handler->fops != NULL) + input_table[handler->minor >> 5] = handler; + +/* + * Add the handler. + */ + + handler->next = input_handler; + input_handler = handler; + +/* + * Notify it about all existing devices. + */ + + while (dev) { + if ((handle = handler->connect(handler, dev))) + input_link_handle(handle); + dev = dev->next; + } +} + +void input_unregister_handler(struct input_handler *handler) +{ + struct input_handler **handlerptr = &input_handler; + struct input_handle *handle = handler->handle; + struct input_handle *hnext; + +/* + * Tell the handler to disconnect from all devices it keeps open. + */ + + while (handle) { + hnext = handle->hnext; + input_unlink_handle(handle); + handler->disconnect(handle); + handle = hnext; + } + +/* + * Remove it. + */ + + while (*handlerptr && (*handlerptr != handler)) + handlerptr = &((*handlerptr)->next); + + *handlerptr = (*handlerptr)->next; + +/* + * Remove minors. + */ + + if (handler->fops != NULL) + input_table[handler->minor >> 5] = NULL; +} + +static int input_open_file(struct inode *inode, struct file *file) +{ + struct input_handler *handler = input_table[MINOR(inode->i_rdev) >> 5]; + + if (!handler || !handler->fops || !handler->fops->open) + return -ENODEV; + + file->f_op = handler->fops; + + return handler->fops->open(inode, file); +} + +static struct file_operations input_fops = { + open: input_open_file, +}; + +devfs_handle_t input_register_minor(char *name, int minor, int minor_base) +{ + char devfs_name[16]; + sprintf(devfs_name, name, minor); + return devfs_register(input_devfs_handle, devfs_name, 0, DEVFS_FL_DEFAULT, INPUT_MAJOR, minor + minor_base, + S_IFCHR | S_IRUGO | S_IWUSR, 0, 0, &input_fops, NULL); +} + +void input_unregister_minor(devfs_handle_t handle) +{ + devfs_unregister(handle); +} + +static int __init input_init(void) +{ + if (devfs_register_chrdev(INPUT_MAJOR, "input", &input_fops)) { + printk(KERN_ERR "input: unable to register char major %d", INPUT_MAJOR); + return -EBUSY; + } + input_devfs_handle = devfs_mk_dir(NULL, "input", 5, NULL); + return 0; +} + +static void __exit input_exit(void) +{ + devfs_unregister(input_devfs_handle); + if (devfs_unregister_chrdev(INPUT_MAJOR, "input")) + printk(KERN_ERR "input: can't unregister char major %d", INPUT_MAJOR); +} + +module_init(input_init); +module_exit(input_exit); diff --git a/drivers/usb/joydev.c b/drivers/usb/joydev.c new file mode 100644 index 000000000000..89bf98ddbc07 --- /dev/null +++ b/drivers/usb/joydev.c @@ -0,0 +1,487 @@ +/* + * $Id: joydev.c,v 1.7 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * Copyright (c) 1999 Colin Van Dyke + * + * Joystick device driver for the input driver suite. + * + * Sponsored by SuSE and Intel + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define JOYDEV_MINOR_BASE 0 +#define JOYDEV_MINORS 32 +#define JOYDEV_BUFFER_SIZE 64 + +struct joydev { + int used; + int open; + int minor; + struct input_handle handle; + wait_queue_head_t wait; + devfs_handle_t devfs; + struct joydev *next; + struct joydev_list *list; + struct js_corr corr[ABS_MAX]; + struct JS_DATA_SAVE_TYPE glue; + int nabs; + int nkey; + __u16 keymap[KEY_MAX - BTN_MISC]; + __u16 keypam[KEY_MAX - BTN_MISC]; + __u8 absmap[ABS_MAX]; + __u8 abspam[ABS_MAX]; +}; + +struct joydev_list { + struct js_event buffer[JOYDEV_BUFFER_SIZE]; + int head; + int tail; + int startup; + struct fasync_struct *fasync; + struct joydev *joydev; + struct joydev_list *next; +}; + +static struct joydev *joydev_table[JOYDEV_MINORS]; + +MODULE_AUTHOR("Vojtech Pavlik "); +MODULE_SUPPORTED_DEVICE("input/js"); + +static int joydev_correct(int value, struct js_corr *corr) +{ + switch (corr->type) { + case JS_CORR_NONE: + break; + case JS_CORR_BROKEN: + value = value > corr->coef[0] ? (value < corr->coef[1] ? 0 : + ((corr->coef[3] * (value - corr->coef[1])) >> 14)) : + ((corr->coef[2] * (value - corr->coef[0])) >> 14); + break; + default: + return 0; + } + + if (value < -32767) return -32767; + if (value > 32767) return 32767; + + return value; +} + +static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct joydev *joydev = handle->private; + struct joydev_list *list = joydev->list; + struct js_event event; + + switch (type) { + + case EV_KEY: + if (code < BTN_MISC || value == 2) return; + event.type = JS_EVENT_BUTTON; + event.number = joydev->keymap[code - BTN_MISC]; + event.value = value; + break; + + case EV_ABS: + event.type = JS_EVENT_AXIS; + event.number = joydev->absmap[code]; + event.value = joydev_correct(value, &joydev->corr[event.number]); + break; + + default: + return; + } + + event.time = jiffies * (1000 / HZ); + + while (list) { + + memcpy(list->buffer + list->head, &event, sizeof(struct js_event)); + + if (list->startup == joydev->nabs + joydev->nkey) + if (list->tail == (list->head = (list->head + 1) & (JOYDEV_BUFFER_SIZE - 1))) + list->startup = 0; + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&joydev->wait); +} + +static int joydev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct joydev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int joydev_release(struct inode * inode, struct file * file) +{ + struct joydev_list *list = file->private_data; + struct joydev_list **listptr = &list->joydev->list; + + joydev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->joydev->open) + input_close_device(&list->joydev->handle); + + if (!--list->joydev->used) { + input_unregister_minor(list->joydev->devfs); + joydev_table[list->joydev->minor] = NULL; + kfree(list->joydev); + } + + kfree(list); + + return 0; +} + +static int joydev_open(struct inode *inode, struct file *file) +{ + struct joydev_list *list; + int i = MINOR(inode->i_rdev) - JOYDEV_MINOR_BASE; + + if (i > JOYDEV_MINORS || !joydev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct joydev_list), GFP_KERNEL))) { + return -ENOMEM; + } + memset(list, 0, sizeof(struct joydev_list)); + + list->joydev = joydev_table[i]; + list->next = joydev_table[i]->list; + joydev_table[i]->list = list; + + file->private_data = list; + + list->joydev->used++; + + if (!list->joydev->open++) + input_open_device(&list->joydev->handle); + + return 0; +} + +static ssize_t joydev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + return -EINVAL; +} + +static ssize_t joydev_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + struct input_dev *input = joydev->handle.dev; + int retval = 0; + + if (count < sizeof(struct js_event)) + return -EINVAL; + + if (count == sizeof(struct JS_DATA_TYPE)) { + + struct JS_DATA_TYPE data; + + data.buttons = ((joydev->nkey > 0 && test_bit(joydev->keypam[0], input->key)) ? 1 : 0) | + ((joydev->nkey > 1 && test_bit(joydev->keypam[1], input->key)) ? 2 : 0); + data.x = ((joydev_correct(input->abs[ABS_X], &joydev->corr[0]) / 256) + 128) >> joydev->glue.JS_CORR.x; + data.y = ((joydev_correct(input->abs[ABS_Y], &joydev->corr[1]) / 256) + 128) >> joydev->glue.JS_CORR.y; + + if (copy_to_user(buf, &data, sizeof(struct JS_DATA_TYPE))) + return -EFAULT; + + list->startup = 0; + list->tail = list->head; + + return sizeof(struct JS_DATA_TYPE); + } + + if (list->head == list->tail && list->startup == joydev->nabs + joydev->nkey) { + + add_wait_queue(&list->joydev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (list->head == list->tail) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->joydev->wait, &wait); + } + + if (retval) + return retval; + + while (list->startup < joydev->nabs + joydev->nkey && retval + sizeof(struct js_event) <= count) { + + struct js_event event; + + event.time = jiffies * (1000/HZ); + + if (list->startup < joydev->nkey) { + event.type = JS_EVENT_BUTTON | JS_EVENT_INIT; + event.value = !!test_bit(joydev->keypam[list->startup], input->key); + event.number = list->startup; + } else { + event.type = JS_EVENT_AXIS | JS_EVENT_INIT; + event.value = joydev_correct(input->abs[joydev->abspam[list->startup - joydev->nkey]], + &joydev->corr[list->startup - joydev->nkey]); + event.number = list->startup - joydev->nkey; + } + + if (copy_to_user(buf + retval, &event, sizeof(struct js_event))) + return -EFAULT; + + list->startup++; + retval += sizeof(struct js_event); + } + + while (list->head != list->tail && retval + sizeof(struct js_event) <= count) { + + if (copy_to_user(buf + retval, list->buffer + list->tail, sizeof(struct js_event))) + return -EFAULT; + + list->tail = (list->tail + 1) & (JOYDEV_BUFFER_SIZE - 1); + retval += sizeof(struct js_event); + } + + return retval; +} + +/* No kernel lock - fine */ +static unsigned int joydev_poll(struct file *file, poll_table *wait) +{ + struct joydev_list *list = file->private_data; + poll_wait(file, &list->joydev->wait, wait); + if (list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) + return POLLIN | POLLRDNORM; + return 0; +} + +static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + struct joydev_list *list = file->private_data; + struct joydev *joydev = list->joydev; + struct input_dev *dev = joydev->handle.dev; + + + switch (cmd) { + + case JS_SET_CAL: + return copy_from_user(&joydev->glue.JS_CORR, (struct JS_DATA_TYPE *) arg, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_GET_CAL: + return copy_to_user((struct JS_DATA_TYPE *) arg, &joydev->glue.JS_CORR, + sizeof(struct JS_DATA_TYPE)) ? -EFAULT : 0; + case JS_SET_TIMEOUT: + return get_user(joydev->glue.JS_TIMEOUT, (int *) arg); + case JS_GET_TIMEOUT: + return put_user(joydev->glue.JS_TIMEOUT, (int *) arg); + case JS_SET_TIMELIMIT: + return get_user(joydev->glue.JS_TIMELIMIT, (long *) arg); + case JS_GET_TIMELIMIT: + return put_user(joydev->glue.JS_TIMELIMIT, (long *) arg); + case JS_SET_ALL: + return copy_from_user(&joydev->glue, (struct JS_DATA_SAVE_TYPE *) arg, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + case JS_GET_ALL: + return copy_to_user((struct JS_DATA_SAVE_TYPE *) arg, &joydev->glue, + sizeof(struct JS_DATA_SAVE_TYPE)) ? -EFAULT : 0; + + case JSIOCGVERSION: + return put_user(JS_VERSION, (__u32 *) arg); + case JSIOCGAXES: + return put_user(joydev->nabs, (__u8 *) arg); + case JSIOCGBUTTONS: + return put_user(joydev->nkey, (__u8 *) arg); + case JSIOCSCORR: + return copy_from_user(joydev->corr, (struct js_corr *) arg, + sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + case JSIOCGCORR: + return copy_to_user((struct js_corr *) arg, joydev->corr, + sizeof(struct js_corr) * joydev->nabs) ? -EFAULT : 0; + default: + if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) { + int len; + if (!dev->name) return 0; + len = strlen(dev->name) + 1; + if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd); + if (copy_to_user((char *) arg, dev->name, len)) return -EFAULT; + return len; + } + } + return -EINVAL; +} + +static struct file_operations joydev_fops = { + read: joydev_read, + write: joydev_write, + poll: joydev_poll, + open: joydev_open, + release: joydev_release, + ioctl: joydev_ioctl, + fasync: joydev_fasync, +}; + +static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct joydev *joydev; + int i, j, minor; + + if (!(test_bit(EV_KEY, dev->evbit) && test_bit(EV_ABS, dev->evbit) && + test_bit(ABS_X, dev->absbit) && test_bit(ABS_Y, dev->absbit) && + (test_bit(BTN_TRIGGER, dev->keybit) || test_bit(BTN_A, dev->keybit) + || test_bit(BTN_1, dev->keybit)))) return NULL; + + for (minor = 0; minor < JOYDEV_MINORS && joydev_table[minor]; minor++); + if (joydev_table[minor]) { + printk(KERN_ERR "joydev: no more free joydev devices\n"); + return NULL; + } + + if (!(joydev = kmalloc(sizeof(struct joydev), GFP_KERNEL))) + return NULL; + memset(joydev, 0, sizeof(struct joydev)); + + init_waitqueue_head(&joydev->wait); + + joydev->minor = minor; + joydev_table[minor] = joydev; + + joydev->handle.dev = dev; + joydev->handle.handler = handler; + joydev->handle.private = joydev; + + joydev->used = 1; + + for (i = 0; i < ABS_MAX; i++) + if (test_bit(i, dev->absbit)) { + joydev->absmap[i] = joydev->nabs; + joydev->abspam[joydev->nabs] = i; + joydev->nabs++; + } + + for (i = BTN_JOYSTICK - BTN_MISC; i < KEY_MAX - BTN_MISC; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < BTN_JOYSTICK - BTN_MISC; i++) + if (test_bit(i + BTN_MISC, dev->keybit)) { + joydev->keymap[i] = joydev->nkey; + joydev->keypam[joydev->nkey] = i + BTN_MISC; + joydev->nkey++; + } + + for (i = 0; i < joydev->nabs; i++) { + j = joydev->abspam[i]; + if (dev->absmax[j] == dev->absmin[j]) { + joydev->corr[i].type = JS_CORR_NONE; + continue; + } + joydev->corr[i].type = JS_CORR_BROKEN; + joydev->corr[i].prec = dev->absfuzz[j]; + joydev->corr[i].coef[0] = (dev->absmax[j] + dev->absmin[j]) / 2 - dev->absflat[j]; + joydev->corr[i].coef[1] = (dev->absmax[j] + dev->absmin[j]) / 2 + dev->absflat[j]; + joydev->corr[i].coef[2] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + joydev->corr[i].coef[3] = (1 << 29) / ((dev->absmax[j] - dev->absmin[j]) / 2 - 2 * dev->absflat[j]); + } + + joydev->devfs = input_register_minor("js%d", minor, JOYDEV_MINOR_BASE); + + printk(KERN_INFO "js%d: Joystick device for input%d\n", minor, dev->number); + + return &joydev->handle; +} + +static void joydev_disconnect(struct input_handle *handle) +{ + struct joydev *joydev = handle->private; + + if (joydev->open) + input_close_device(handle); + + if (!--joydev->used) { + input_unregister_minor(joydev->devfs); + joydev_table[joydev->minor] = NULL; + kfree(joydev); + } +} + +static struct input_handler joydev_handler = { + event: joydev_event, + connect: joydev_connect, + disconnect: joydev_disconnect, + fops: &joydev_fops, + minor: JOYDEV_MINOR_BASE, +}; + +static int __init joydev_init(void) +{ + input_register_handler(&joydev_handler); + return 0; +} + +static void __exit joydev_exit(void) +{ + input_unregister_handler(&joydev_handler); +} + +module_init(joydev_init); +module_exit(joydev_exit); diff --git a/drivers/usb/kaweth.c b/drivers/usb/kaweth.c new file mode 100644 index 000000000000..c6226e75f5b4 --- /dev/null +++ b/drivers/usb/kaweth.c @@ -0,0 +1,1030 @@ +/**************************************************************** + * + * kawasaki.c - driver for KL5KUSB101 based USB->Ethernet + * + * (c) 2000 Interlan Communications + * + * Original author: The Zapman + * Inspired by, and much credit goes to Michael Rothwell + * for the test equipment, help, and patience + * Based off of (and with thanks to) Petko Manolov's pegaus.c driver. + * Also many thanks to Joel Silverman at Kawasaki for providing the firmware + * and driver resources. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2, or (at your option) any + * later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + ****************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kawethfw.h" + +#define KAWETH_MTU 1514 +#define KAWETH_BUF_SIZE 1664 +#define KAWETH_TX_TIMEOUT (1 * HZ) +#define KAWETH_FIRMWARE_BUF_SIZE 4096 +#define KAWETH_CONTROL_TIMEOUT (1 * HZ) + +#define KAWETH_STATUS_BROKEN 0x0000001 +#define KAWETH_STATUS_CLOSING 0x0000002 + +#define KAWETH_PACKET_FILTER_PROMISCUOUS 0x01 +#define KAWETH_PACKET_FILTER_ALL_MULTICAST 0x02 +#define KAWETH_PACKET_FILTER_DIRECTED 0x04 +#define KAWETH_PACKET_FILTER_BROADCAST 0x08 +#define KAWETH_PACKET_FILTER_MULTICAST 0x10 + +#define KAWETH_COMMAND_GET_ETHERNET_DESC 0x00 +#define KAWETH_COMMAND_SET_PACKET_FILTER 0x02 +#define KAWETH_COMMAND_GET_MAC 0x06 +#define KAWETH_COMMAND_SET_URB_SIZE 0x08 +#define KAWETH_COMMAND_SET_SOFS_WAIT 0x09 +#define KAWETH_COMMAND_SCAN 0xFF + +#define KAWETH_SOFS_TO_WAIT 0x05 + + +MODULE_AUTHOR("Michael Zappe "); +MODULE_DESCRIPTION("KL5USB101 USB Ethernet driver"); + +static void *kaweth_probe(struct usb_device *dev, unsigned int ifnum); +static void kaweth_disconnect(struct usb_device *dev, void *ptr); + +/**************************************************************** + * kaweth_driver + ****************************************************************/ +static struct usb_driver kaweth_driver = { + name: "kaweth", + probe: kaweth_probe, + disconnect: kaweth_disconnect, +}; +// __attribute__ ((packed)) +typedef __u8 eth_addr_t[6]; + +/**************************************************************** + * usb_eth_dev + ****************************************************************/ +struct usb_eth_dev { + char *name; + __u16 vendor; + __u16 device; + void *pdata; +}; + +/**************************************************************** + * kaweth_ethernet_configuration + ****************************************************************/ +struct kaweth_ethernet_configuration +{ + __u8 size; + __u8 reserved1; + __u8 reserved2; + eth_addr_t hw_addr; + __u32 statistics_mask; + __u16 segment_size; + __u16 max_multicast_filters; + __u8 reserved3; +} __attribute__ ((packed)); + +/**************************************************************** + * kaweth_device + ****************************************************************/ +struct kaweth_device +{ + spinlock_t device_lock; + + __u32 status; + + struct usb_device *dev; + struct net_device *net; + wait_queue_head_t control_wait; + + struct urb *rx_urb; + struct urb *tx_urb; + + __u8 firmware_buf[KAWETH_FIRMWARE_BUF_SIZE]; + __u8 tx_buf[KAWETH_BUF_SIZE]; + __u8 rx_buf[KAWETH_BUF_SIZE]; + + struct kaweth_ethernet_configuration configuration; + + struct net_device_stats stats; +} __attribute__ ((packed)); + +/**************************************************************** + * usb_dev_id + ****************************************************************/ +static struct usb_eth_dev usb_dev_id[] = { + { "KLSI KL5KUSB101B", 0x05e9, 0x0008, NULL }, + { "NetGear EA-101", 0x0846, 0x1001, NULL }, + { "Peracom Enet", 0x0565, 0x0002, NULL }, + { "Peracom Enet2", 0x0565, 0x0005, NULL }, + { "3Com 3C19250", 0x0506, 0x03e8, NULL }, + { "Linksys USB10T", 0x066b, 0x2202, NULL }, + { "D-Link DSB-650C", 0x2001, 0x4000, NULL }, + { "D-Link DSB-650", 0x0557, 0x2002, NULL }, + { "AOX USB Ethernet", 0x03e8, 0x0008, NULL }, + { "ADS USB-10BT", 0x06e1, 0x0008, NULL }, + { "Entrega E45", 0x1645, 0x0005, NULL}, + { "SMC 2202USB", 0x0707, 0x0100, NULL}, + { "Correga K.K.", 0x07aa, 0x0001, NULL}, + { NULL, 0, 0, NULL }, +}; + +/**************************************************************** + * kaweth_check_device_ids + ****************************************************************/ +static int kaweth_check_device_ids(__u16 vendor, __u16 product) +{ + int i=0; + + while (usb_dev_id[i].name) + { + if((usb_dev_id[i].vendor == vendor) && + (usb_dev_id[i].device == product)) + { + printk("%s connected...\n", usb_dev_id[i].name); + + return 0; + } + i++; + } + return 1; +} + +/**************************************************************** + * kaweth_control_complete + ****************************************************************/ +static void kaweth_control_complete(struct urb *urb) +{ + struct kaweth_device *kaweth = (struct kaweth_device *)urb->context; + + if(waitqueue_active(&kaweth->control_wait)) + { + wake_up(&kaweth->control_wait); + } +} + +/**************************************************************** + * kaweth_control + ****************************************************************/ +static int kaweth_control(struct kaweth_device *kaweth, + unsigned int pipe, + __u8 request, + __u8 requesttype, + __u16 value, + __u16 index, + void *data, + __u16 size, + int timeout) +{ + DECLARE_WAITQUEUE(wait, current); + purb_t urb; + devrequest *dr; + int status; + + if(in_interrupt()) + { + return -EBUSY; + } + + urb = usb_alloc_urb(0); + dr = kmalloc(sizeof(devrequest), in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + + if(!urb || !dr) + { + if(urb) kfree(urb); + + return -ENOMEM; + } + + spin_lock(&kaweth->device_lock); + + urb->dev = kaweth->dev; + urb->pipe = pipe; + urb->setup_packet = (void *)dr; + urb->transfer_flags = USB_DISABLE_SPD; + urb->transfer_buffer = (void *)data; + urb->transfer_buffer_length = size; + urb->actual_length = size; + urb->complete = kaweth_control_complete; + urb->context = kaweth; + + dr->requesttype = requesttype; + dr->request = request; + dr->value = value; + dr->index = index; + dr->length = size; + + printk("Request type: %x Request: %x Value: %x Index: %x Length: %x\n", + (int)dr->requesttype, + (int)dr->request, + (int)dr->value, + (int)dr->index, + (int)dr->length); + + init_waitqueue_head(&kaweth->control_wait); + + current->state = TASK_INTERRUPTIBLE; + + add_wait_queue(&kaweth->control_wait, &wait); + + status = usb_submit_urb(urb); + +// printk("pipe: %x\n", pipe); +// printk("maxpacket: %x\n", usb_maxpacket(kaweth->dev, pipe, usb_pipeout(pipe))); + + if(status) + { + usb_free_urb(urb); + remove_wait_queue(&kaweth->control_wait, &wait); + spin_unlock(&kaweth->device_lock); + return status; + } + + if(urb->status == -EINPROGRESS) + { + while(timeout && urb->status == -EINPROGRESS) + { + status = timeout = schedule_timeout(timeout); + } + } + else + { + status = 1; + } + + remove_wait_queue(&kaweth->control_wait, &wait); + + if(!status) + { + usb_unlink_urb(urb); + + printk("kaweth timeout\n"); + + status = -ETIMEDOUT; + } + else + { + status = urb->status; + + if(urb->status) + { + printk("kaweth control message failed (urb addr: %x)\n", (int)urb); + + usb_clear_halt(kaweth->dev, usb_rcvctrlpipe(kaweth->dev, 0)); + usb_clear_halt(kaweth->dev, usb_sndctrlpipe(kaweth->dev, 0)); + + usb_unlink_urb(urb); + } + } + + spin_unlock(&kaweth->device_lock); + + printk("Actual length: %d, length %d\n", urb->actual_length, urb->transfer_buffer_length); + +// printk("pipe: %x\n", pipe); +// printk("maxpacket: %x\n", usb_maxpacket(kaweth->dev, pipe, usb_pipeout(pipe))); + + usb_free_urb(urb); + kfree(dr); + + return status; +} + +/**************************************************************** + * kaweth_read_configuration + ****************************************************************/ +static int kaweth_read_configuration(struct kaweth_device *kaweth) +{ + int retval; + + printk("Reading kaweth configuration\n"); + + retval = kaweth_control(kaweth, + usb_rcvctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_GET_ETHERNET_DESC, + USB_TYPE_VENDOR | USB_DIR_IN | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->configuration, + sizeof(kaweth->configuration), + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_urb_size + ****************************************************************/ +static int kaweth_set_urb_size(struct kaweth_device *kaweth, __u16 urb_size) +{ + int retval; + + printk("Setting URB size to %d\n", (unsigned)urb_size); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_URB_SIZE, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + urb_size, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_sofs_wait + ****************************************************************/ +static int kaweth_set_sofs_wait(struct kaweth_device *kaweth, __u16 sofs_wait) +{ + int retval; + + printk("Set SOFS wait to %d\n", (unsigned)sofs_wait); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_SOFS_WAIT, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + sofs_wait, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_set_recieve_filter + ****************************************************************/ +static int kaweth_set_recieve_filter(struct kaweth_device *kaweth, __u16 recieve_filter) +{ + int retval; + + printk("Set recieve filter to %d\n", (unsigned)recieve_filter); + + retval = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_PACKET_FILTER, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + recieve_filter, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + return retval; +} + +/**************************************************************** + * kaweth_download_firmware + ****************************************************************/ +static int kaweth_download_firmware(struct kaweth_device *kaweth, + __u8 *data, + __u16 data_len, + __u8 interrupt, + __u8 type) +{ + if(data_len > KAWETH_FIRMWARE_BUF_SIZE) + { + printk("Firmware too big: %d\n", data_len); + + return -ENOSPC; + } + + memcpy(kaweth->firmware_buf, data, data_len); + + kaweth->firmware_buf[2] = (data_len & 0xFF) - 7; + kaweth->firmware_buf[3] = data_len >> 8; + kaweth->firmware_buf[4] = type; + kaweth->firmware_buf[5] = interrupt; + + printk("Downloading firmware at %x to kaweth device at %x...\n", (int)data, (int)kaweth); + printk("Firmware length: %d\n", data_len); + + return kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SCAN, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->firmware_buf, + data_len, + KAWETH_CONTROL_TIMEOUT); +} + +/**************************************************************** + * kaweth_trigger_firmware + ****************************************************************/ +static int kaweth_trigger_firmware(struct kaweth_device *kaweth, + __u8 interrupt) +{ + kaweth->firmware_buf[0] = 0xB6; + kaweth->firmware_buf[1] = 0xC3; + kaweth->firmware_buf[2] = 1; + kaweth->firmware_buf[3] = 0; + kaweth->firmware_buf[4] = 6; + kaweth->firmware_buf[5] = 100; + kaweth->firmware_buf[6] = 0; + kaweth->firmware_buf[7] = 0; + + printk("Triggering firmware\n"); + + return kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SCAN, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + 0, + 0, + (void *)&kaweth->firmware_buf, + 8, + KAWETH_CONTROL_TIMEOUT); +} + +/**************************************************************** + * kaweth_reset + ****************************************************************/ +static int kaweth_reset(struct kaweth_device *kaweth) +{ + int result; + + result = usb_set_configuration(kaweth->dev, 1); + + udelay(10000); + + return result; +} + +static void kaweth_usb_recieve(struct urb *); + +/**************************************************************** + * kaweth_resubmit_rx_urb + ****************************************************************/ +static inline void kaweth_resubmit_rx_urb(struct kaweth_device *kaweth) +{ + int result; + + memset(kaweth->rx_urb, 0, sizeof(*kaweth->rx_urb)); + + spin_lock_init(&kaweth->rx_urb->lock); + + FILL_BULK_URB(kaweth->rx_urb, + kaweth->dev, + usb_rcvbulkpipe(kaweth->dev, 1), + kaweth->rx_buf, + KAWETH_BUF_SIZE, + kaweth_usb_recieve, + kaweth); + +// kaweth->rx_urb->transfer_flags = USB_DISABLE_SPD | USB_URB_EARLY_COMPLETE; + + if((result = usb_submit_urb(kaweth->rx_urb))) + { + printk("kaweth: resubmitting rx_urb %d failed\n", result); + } +} + +/**************************************************************** + * kaweth_usb_recieve + ****************************************************************/ +static void kaweth_usb_recieve(struct urb *urb) +{ + struct kaweth_device *kaweth = urb->context; + struct net_device *net = kaweth->net; + + int count = urb->actual_length, + count2 = urb->transfer_buffer_length; + + __u16 pkt_len = *(__u16 *)kaweth->rx_buf; + + struct sk_buff *skb; + + if(kaweth->status & KAWETH_STATUS_CLOSING) + { + return; + } + +#define RX_FAILED kaweth_resubmit_rx_urb(kaweth); \ + return + + if(urb->status && urb->status != -EREMOTEIO && count != 1) + { + printk("%s: RX status: %d count: %d packet_len: %d\n", + net->name, urb->status, count, (int)pkt_len); + + RX_FAILED; + } + + if(kaweth->net && (count > 2)) + { + if(pkt_len > (count - 2)) + { + printk("kaweth error: packet length too long for USB frame (pkt_len: %x, count: %x)\n", + pkt_len, count); + + printk("Packet len & 2047: %x\n", pkt_len & 2047); + printk("Count 2: %x\n", count2); + + RX_FAILED; + } + + if(!(skb = dev_alloc_skb(pkt_len+2))) + { + RX_FAILED; + } + + skb->dev = net; + + eth_copy_and_sum(skb, kaweth->rx_buf + 2, pkt_len, 0); + + skb_put(skb, pkt_len); + + skb->protocol = eth_type_trans(skb, net); + + netif_rx(skb); + + kaweth->stats.rx_packets++; + kaweth->stats.rx_bytes += pkt_len; + } + + kaweth_resubmit_rx_urb(kaweth); +} + +/**************************************************************** + * kaweth_open + ****************************************************************/ +static int kaweth_open(struct net_device *net) +{ + struct kaweth_device *kaweth = (struct kaweth_device *)net->priv; + + printk("Dev usage: %d\n", kaweth->dev->refcnt.counter); + + printk("Opening network device...\n"); + + kaweth_resubmit_rx_urb(kaweth); + + netif_start_queue(net); + + MOD_INC_USE_COUNT; + + return 0; +} + +/**************************************************************** + * kaweth_close + ****************************************************************/ +static int kaweth_close(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + + netif_stop_queue(net); + + kaweth->status |= KAWETH_STATUS_CLOSING; + + usb_unlink_urb(kaweth->rx_urb); + + kaweth->status &= ~KAWETH_STATUS_CLOSING; + + MOD_DEC_USE_COUNT; + + printk("Dev usage: %d\n", kaweth->dev->refcnt.counter); + + return 0; +} + +/**************************************************************** + * kaweth_ioctl + ****************************************************************/ +static int kaweth_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ +// __u16 *data = (__u16 *)&rq->ifr_data; +// struct kaweth_device *kaweth = net->priv; +/* + switch(cmd) { + default: + return -EOPNOTSUPP; + } +*/ + return -EOPNOTSUPP; +} + +/**************************************************************** + * kaweth_usb_transmit_complete + ****************************************************************/ +static void kaweth_usb_transmit_complete(struct urb *urb) +{ + struct kaweth_device *kaweth = urb->context; + + spin_lock(&kaweth->device_lock); + + if (urb->status) + printk("%s: TX status %d", kaweth->net->name, urb->status); + + netif_wake_queue(kaweth->net); + + spin_unlock(&kaweth->device_lock); +} + +/**************************************************************** + * kaweth_start_xmit + ****************************************************************/ +static int kaweth_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; +// int count = ((skb->len) & 0x3f) ? skb->len+2 : skb->len+3; + int count = skb->len; + + int res; + + spin_lock(&kaweth->device_lock); + + netif_stop_queue(net); + + *((__u16 *)kaweth->tx_buf) = skb->len; + + memcpy(kaweth->tx_buf + 2, skb->data, skb->len); + + memset(kaweth->tx_urb, 0, sizeof(*kaweth->tx_urb)); + spin_lock_init(&kaweth->tx_urb->lock); + + FILL_BULK_URB(kaweth->tx_urb, + kaweth->dev, + usb_sndbulkpipe(kaweth->dev, 2), + kaweth->tx_buf, + count + 2, + kaweth_usb_transmit_complete, + kaweth); + + if((res = usb_submit_urb(kaweth->tx_urb))) + { + warn("kaweth failed tx_urb %d", res); + kaweth->stats.tx_errors++; + + netif_start_queue(net); + } + else + { + kaweth->stats.tx_packets++; + kaweth->stats.tx_bytes += skb->len; + net->trans_start = jiffies; + } + + dev_kfree_skb(skb); + + spin_unlock(&kaweth->device_lock); + + return 0; +} + +/**************************************************************** + * kaweth_set_rx_mode + ****************************************************************/ +static void kaweth_set_rx_mode(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + int result; + + __u16 packet_filter_bitmap = KAWETH_PACKET_FILTER_DIRECTED | + KAWETH_PACKET_FILTER_BROADCAST | + KAWETH_PACKET_FILTER_MULTICAST; + + printk("Setting Rx mode\n"); + + netif_stop_queue(net); + + if (net->flags & IFF_PROMISC) + { + packet_filter_bitmap |= KAWETH_PACKET_FILTER_PROMISCUOUS; + } + else if ((net->mc_count) || // > multicast_filter_limit) || + (net->flags & IFF_ALLMULTI)) + { + packet_filter_bitmap |= KAWETH_PACKET_FILTER_ALL_MULTICAST; + } + + result = kaweth_control(kaweth, + usb_sndctrlpipe(kaweth->dev, 0), + KAWETH_COMMAND_SET_PACKET_FILTER, + USB_TYPE_VENDOR | USB_DIR_OUT | USB_RECIP_DEVICE, + packet_filter_bitmap, + 0, + (void *)&kaweth->firmware_buf, + 0, + KAWETH_CONTROL_TIMEOUT); + + if(result < 0) + { + printk("Failed to set Rx mode: %d\n", result); + } + else + { + printk("%s: set Rx mode to %d\n", net->name, packet_filter_bitmap); + } + + netif_wake_queue(net); +} + +/**************************************************************** + * kaweth_netdev_stats + ****************************************************************/ +static struct net_device_stats *kaweth_netdev_stats(struct net_device *dev) +{ + return &((struct kaweth_device *)dev->priv)->stats; +} + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 0) +/**************************************************************** + * kaweth_tx_timeout + ****************************************************************/ +static void kaweth_tx_timeout(struct net_device *net) +{ + struct kaweth_device *kaweth = net->priv; + + printk("%s: Tx timed out. Reseting...\n", net->name); + kaweth->stats.tx_errors++; + net->trans_start = jiffies; + + usb_unlink_urb(kaweth->tx_urb); + + netif_wake_queue(net); +} +#endif + +/**************************************************************** + * kaweth_probe + ****************************************************************/ +static void *kaweth_probe(struct usb_device *dev, unsigned int ifnum) +{ +// struct net_device *net; +// eth_addr_t mac_addr; + struct kaweth_device *kaweth; + const eth_addr_t bcast_addr = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + int result = 0; + int i; + + printk("Kawasaki Device Probe (Device number:%x): 0x%4.4x:0x%4.4x\n", + dev->devnum, + (int)dev->descriptor.idVendor, + (int)dev->descriptor.idProduct); + + printk("Device at %p\n", dev); + + printk("Descriptor length: %x type: %x\n", + (int)dev->descriptor.bLength, + (int)dev->descriptor.bDescriptorType); + + if (kaweth_check_device_ids(dev->descriptor.idVendor, + dev->descriptor.idProduct)) + { + return NULL; + } + + if(!(kaweth = kmalloc(sizeof(struct kaweth_device), GFP_KERNEL))) + { + printk("out of memory allocating device structure\n"); + return NULL; + } + + memset(kaweth, 0, sizeof(*kaweth)); + + kaweth->dev = dev; + kaweth->status = 0; + kaweth->net = NULL; + kaweth->device_lock = SPIN_LOCK_UNLOCKED; + + if((result = kaweth_read_configuration(kaweth)) < 0) + { +// printk("Resetting (jiffies: %x)...\n", jiffies); + printk("Resetting...\n"); + + kaweth_reset(kaweth); + +// printk("Dowloading firmware (jiffies: %x)...\n", jiffies); + + if((result = kaweth_download_firmware(kaweth, kaweth_new_code, len_kaweth_new_code, 100, 2)) < 0) + { + printk("Error downloading firmware (%d), no net device created\n", result); + + kfree(kaweth); + + return NULL; + } + +// printk("Dowloading firmware fix (jiffies: %x)...\n", jiffies); + + if((result = kaweth_download_firmware(kaweth, kaweth_new_code_fix, len_kaweth_new_code_fix, 100, 3)) < 0) + { + printk("Error downloading firmware fix (%d), no net device created\n", result); + + kfree(kaweth); + + return NULL; + } + +// printk("Dowloading firmware trigger (jiffies: %x)...\n", jiffies); + + if((result = kaweth_trigger_firmware(kaweth, 100)) < 0) + { + printk("Error triggering firmware (%d), no net device created\n", result); + + kfree(kaweth); + + return NULL; + } + + udelay(1000); + + printk("Resetting device (jiffies: %lx)...\n", jiffies); + + if(kaweth_reset(kaweth)) + { + printk("Error resetting device\n"); + + kfree(kaweth); + + return NULL; + } + + printk("Reset device (jiffies: %lx)...\n", jiffies); + } + + result = kaweth_read_configuration(kaweth); + + printk("Statitstics collection: %x\n", kaweth->configuration.statistics_mask); + printk("Multicast filter limit: %x\n", kaweth->configuration.max_multicast_filters & ((1 << 15) - 1)); + printk("MTU: %x\n", kaweth->configuration.segment_size); + printk("Read MAC address %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n", + (int)kaweth->configuration.hw_addr[0], + (int)kaweth->configuration.hw_addr[1], + (int)kaweth->configuration.hw_addr[2], + (int)kaweth->configuration.hw_addr[3], + (int)kaweth->configuration.hw_addr[4], + (int)kaweth->configuration.hw_addr[5]); + + if(!memcmp(&kaweth->configuration.hw_addr, &bcast_addr, sizeof(bcast_addr))) + { + printk("Firmware not functioning properly, no net device created\n"); + + kfree(kaweth); + + return NULL; + } + + for(i = 0; i < sizeof(kaweth->configuration); i++) + { + printk(":%2.2x:", (unsigned int)((unsigned char *)&kaweth->configuration)[i]); + } + + printk("\n"); + + if(result < 0) + { + printk("Error reading configuration (%d), no net device created\n", result); + + return kaweth; + } + + result = kaweth_set_urb_size(kaweth, KAWETH_BUF_SIZE); + + if(result < 0) + { + printk("Error setting URB size\n"); + + return kaweth; + } + + result = kaweth_set_sofs_wait(kaweth, KAWETH_SOFS_TO_WAIT); + + if(result < 0) + { + printk("Error setting SOFS wait\n"); + + return kaweth; + } + + result = kaweth_set_recieve_filter(kaweth, KAWETH_PACKET_FILTER_DIRECTED | + KAWETH_PACKET_FILTER_BROADCAST | + KAWETH_PACKET_FILTER_MULTICAST); + + if(result < 0) + { + printk("Error setting recieve filter\n"); + + return kaweth; + } + + printk("Initializing net device...\n"); + + kaweth->tx_urb = usb_alloc_urb(0); + kaweth->rx_urb = usb_alloc_urb(0); + + kaweth->net = init_etherdev(0, 0); + + memcpy(kaweth->net->broadcast, &bcast_addr, sizeof(bcast_addr)); + memcpy(kaweth->net->dev_addr, &kaweth->configuration.hw_addr, sizeof(kaweth->configuration.hw_addr)); + + kaweth->net->priv = kaweth; + kaweth->net->open = kaweth_open; + kaweth->net->stop = kaweth_close; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 3, 0) + kaweth->net->watchdog_timeo = KAWETH_TX_TIMEOUT; + kaweth->net->tx_timeout = kaweth_tx_timeout; +#endif + + kaweth->net->do_ioctl = kaweth_ioctl; + kaweth->net->hard_start_xmit = kaweth_start_xmit; + kaweth->net->set_multicast_list = kaweth_set_rx_mode; + kaweth->net->get_stats = kaweth_netdev_stats; + kaweth->net->mtu = kaweth->configuration.segment_size; + + memset(&kaweth->stats, 0, sizeof(kaweth->stats)); + + printk("kaweth interface created at %s\n", kaweth->net->name); + + printk("Kaweth probe returning...\n"); + + return kaweth; +} + +/**************************************************************** + * kaweth_disconnect + ****************************************************************/ +static void kaweth_disconnect(struct usb_device *dev, void *ptr) +{ + struct kaweth_device *kaweth = ptr; + + printk("Unregistering kaweth\n"); + + if (!kaweth) { + warn("unregistering non-existant device"); + return; + } + + if(kaweth->net) + { + if(kaweth->net->flags & IFF_UP) + { + printk("Closing net device\n"); + + dev_close(kaweth->net); + } + + printk("Unregistering net device\n"); + + unregister_netdev(kaweth->net); + } + + usb_free_urb(kaweth->rx_urb); + usb_free_urb(kaweth->tx_urb); + + kfree(kaweth); +} + + +/**************************************************************** + * kaweth_init + ****************************************************************/ +int __init kaweth_init(void) +{ + printk("Kawasaki USB->Ethernet Driver loading...\n"); + + return usb_register(&kaweth_driver); +} + +/**************************************************************** + * kaweth_exit + ****************************************************************/ +void __exit kaweth_exit(void) +{ + usb_deregister(&kaweth_driver); +} + +module_init(kaweth_init); +module_exit(kaweth_exit); + diff --git a/drivers/usb/kawethfw.h b/drivers/usb/kawethfw.h new file mode 100644 index 000000000000..50333b3c389c --- /dev/null +++ b/drivers/usb/kawethfw.h @@ -0,0 +1,639 @@ +/******************************************/ +/* NOTE: B6/C3 is data header signature */ +/* 0xAA/0xBB is data length = total */ +/* bytes - 7, 0xCC is type, 0xDD is */ +/* interrupt to use. */ +/******************************************/ + +/**************************************************************** + * kaweth_trigger_code + ****************************************************************/ +static __u8 kaweth_trigger_code[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0xc8, 0x07, 0xa0, 0x00, 0xf0, 0x07, 0x5e, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x0a, 0x00, 0x08, 0x00, + 0xf0, 0x09, 0x00, 0x00, 0x02, 0x00, 0xe7, 0x07, + 0x36, 0x00, 0x00, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x10, 0xc0, + 0xf0, 0x09, 0x0e, 0xc0, 0x00, 0x00, 0xe7, 0x87, + 0x01, 0x00, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x07, 0xa0, 0x00, + 0xe7, 0x17, 0x50, 0xc3, 0x10, 0xc0, 0x30, 0xd8, + 0x04, 0x00, 0x30, 0x5c, 0x08, 0x00, 0x04, 0x00, + 0xb0, 0xc0, 0x06, 0x00, 0xc8, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0x49, 0xaf, + 0xc0, 0x07, 0x00, 0x00, 0x60, 0xaf, 0x4a, 0xaf, + 0x00, 0x0c, 0x0c, 0x00, 0x40, 0xd2, 0x00, 0x1c, + 0x0c, 0x00, 0x40, 0xd2, 0x30, 0x00, 0x08, 0x00, + 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, 0xf0, 0x07, + 0x86, 0x00, 0x06, 0x00, 0x67, 0xcf, 0x27, 0x0c, + 0x02, 0x00, 0x00, 0x00, 0x27, 0x0c, 0x00, 0x00, + 0x0e, 0xc0, 0x49, 0xaf, 0x64, 0xaf, 0xc0, 0x07, + 0x00, 0x00, 0x4b, 0xaf, 0x4a, 0xaf, 0x5a, 0xcf, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x94, 0x00, 0x05, 0x00, + 0, 0 +}; + +/**************************************************************** + * kaweth_trigger_code_fix + ****************************************************************/ +static __u8 kaweth_trigger_code_fix[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x02, 0x00, 0x06, 0x00, 0x18, 0x00, 0x3e, 0x00, + 0x80, 0x00, 0x98, 0x00, 0xaa, 0x00, + 0, 0 +}; + +/**************************************************************** + * kaweth_new_code + ****************************************************************/ +static __u8 kaweth_new_code[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x9f, 0xcf, 0xbc, 0x08, 0xe7, 0x57, 0x00, 0x00, + 0x9a, 0x08, 0x97, 0xc1, 0xe7, 0x67, 0xff, 0x1f, + 0x28, 0xc0, 0xe7, 0x87, 0x00, 0x04, 0x24, 0xc0, + 0xe7, 0x67, 0xff, 0xf9, 0x22, 0xc0, 0x97, 0xcf, + 0xe7, 0x09, 0xa2, 0xc0, 0x94, 0x08, 0xd7, 0x09, + 0x00, 0xc0, 0xe7, 0x59, 0xba, 0x08, 0x94, 0x08, + 0x03, 0xc1, 0xe7, 0x67, 0xff, 0xf7, 0x24, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0xa7, 0xcf, 0x92, 0x08, + 0xe7, 0x57, 0x00, 0x00, 0x8e, 0x08, 0xa7, 0xa1, + 0x8e, 0x08, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0xf2, 0x09, 0x0a, 0xc0, 0xe7, 0x57, 0x00, 0x00, + 0xa4, 0xc0, 0xa7, 0xc0, 0x56, 0x08, 0x9f, 0xaf, + 0x70, 0x09, 0xe7, 0x07, 0x00, 0x00, 0xf2, 0x09, + 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, 0x9f, 0xa0, + 0x40, 0x00, 0xe7, 0x59, 0x90, 0x08, 0x94, 0x08, + 0x9f, 0xa0, 0x40, 0x00, 0xc8, 0x09, 0xa2, 0x08, + 0x08, 0x62, 0x9f, 0xa1, 0x14, 0x0a, 0xe7, 0x57, + 0x00, 0x00, 0x52, 0x08, 0xa7, 0xc0, 0x56, 0x08, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x57, 0x00, 0x00, + 0x8e, 0x08, 0xa7, 0xc1, 0x56, 0x08, 0xc0, 0x09, + 0xa8, 0x08, 0x00, 0x60, 0x05, 0xc4, 0xc0, 0x59, + 0x94, 0x08, 0x02, 0xc0, 0x9f, 0xaf, 0xee, 0x00, + 0xe7, 0x59, 0xae, 0x08, 0x94, 0x08, 0x02, 0xc1, + 0x9f, 0xaf, 0xf6, 0x00, 0x9f, 0xaf, 0x9e, 0x03, + 0xef, 0x57, 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xa1, + 0xde, 0x01, 0xe7, 0x57, 0x00, 0x00, 0x78, 0x08, + 0x9f, 0xa0, 0xe4, 0x03, 0x9f, 0xaf, 0x2c, 0x04, + 0xa7, 0xcf, 0x56, 0x08, 0x48, 0x02, 0xe7, 0x09, + 0x94, 0x08, 0xa8, 0x08, 0xc8, 0x37, 0x04, 0x00, + 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xa6, 0x08, 0x97, 0xc0, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, 0x9c, 0x08, + 0x08, 0x62, 0x1d, 0xc0, 0x27, 0x04, 0x9c, 0x08, + 0x10, 0x94, 0xf0, 0x07, 0xee, 0x09, 0x02, 0x00, + 0xc1, 0x07, 0x01, 0x00, 0x70, 0x00, 0x04, 0x00, + 0xf0, 0x07, 0x44, 0x01, 0x06, 0x00, 0x50, 0xaf, + 0xe7, 0x09, 0x94, 0x08, 0xae, 0x08, 0xe7, 0x17, + 0x14, 0x00, 0xae, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xae, 0x08, 0xe7, 0x07, 0xff, 0xff, 0xa8, 0x08, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0x08, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0x48, 0x02, 0xd0, 0x09, 0x9c, 0x08, + 0x27, 0x02, 0x9c, 0x08, 0xe7, 0x09, 0x20, 0xc0, + 0xee, 0x09, 0xe7, 0xd0, 0xee, 0x09, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x48, 0x02, 0xc8, 0x37, + 0x04, 0x00, 0x00, 0x0c, 0x0c, 0x00, 0x00, 0x60, + 0x21, 0xc0, 0xc0, 0x37, 0x3e, 0x00, 0x23, 0xc9, + 0xc0, 0x57, 0xb4, 0x05, 0x1b, 0xc8, 0xc0, 0x17, + 0x3f, 0x00, 0xc0, 0x67, 0xc0, 0xff, 0x30, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x4c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0xbe, 0x01, 0x0a, 0x00, + 0x48, 0x02, 0xc1, 0x07, 0x02, 0x00, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0x51, 0xaf, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, + 0x9f, 0xaf, 0xe4, 0x03, 0x97, 0xcf, 0x9f, 0xaf, + 0xe4, 0x03, 0xc9, 0x37, 0x04, 0x00, 0xc1, 0xdf, + 0xc8, 0x09, 0x70, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x70, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc0, 0xdf, + 0x9f, 0xaf, 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x97, 0xc1, 0xe7, 0x57, + 0x01, 0x00, 0x7a, 0x08, 0x97, 0xc0, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x00, 0x02, + 0xc0, 0x17, 0x0e, 0x00, 0x27, 0x00, 0x34, 0x01, + 0x27, 0x0c, 0x0c, 0x00, 0x36, 0x01, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x9f, 0xc0, 0xbe, 0x02, + 0xe7, 0x57, 0x00, 0x00, 0xb0, 0x08, 0x97, 0xc1, + 0xe7, 0x07, 0x09, 0x00, 0x12, 0xc0, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x9f, 0xc1, 0xb6, 0x02, + 0xe7, 0x57, 0x09, 0x00, 0x12, 0xc0, 0x77, 0xc9, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x77, + 0x00, 0x08, 0x20, 0xc0, 0x2f, 0xc1, 0xe7, 0x07, + 0x00, 0x00, 0x42, 0xc0, 0xe7, 0x07, 0x05, 0x00, + 0x90, 0xc0, 0xc8, 0x07, 0x0a, 0x00, 0xe7, 0x77, + 0x04, 0x00, 0x20, 0xc0, 0x09, 0xc1, 0x08, 0xda, + 0x7a, 0xc1, 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, + 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, 0x1a, 0xcf, + 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, 0x00, 0xd8, + 0x27, 0x50, 0x34, 0x01, 0x17, 0xc1, 0xe7, 0x77, + 0x02, 0x00, 0x20, 0xc0, 0x79, 0xc1, 0x27, 0x50, + 0x34, 0x01, 0x10, 0xc1, 0xe7, 0x77, 0x02, 0x00, + 0x20, 0xc0, 0x79, 0xc0, 0x9f, 0xaf, 0xd8, 0x02, + 0xe7, 0x05, 0x00, 0xc0, 0x00, 0x60, 0x9f, 0xc0, + 0xde, 0x01, 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x00, + 0xb8, 0x08, 0x06, 0xcf, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x50, 0xc3, 0x12, 0xc0, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x01, 0x00, 0xb8, 0x08, 0x97, 0xcf, 0xe7, 0x07, + 0x50, 0xc3, 0x12, 0xc0, 0xe7, 0x07, 0x30, 0x0e, + 0x02, 0x00, 0xe7, 0x07, 0x01, 0x00, 0x7a, 0x08, + 0xe7, 0x07, 0x05, 0x00, 0x90, 0xc0, 0x97, 0xcf, + 0xe7, 0x07, 0x00, 0x01, 0x42, 0xc0, 0xe7, 0x07, + 0x04, 0x00, 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x57, 0x0f, 0x00, 0xb2, 0x08, + 0x13, 0xc1, 0x9f, 0xaf, 0x2e, 0x08, 0xca, 0x09, + 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, 0x5c, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x08, 0xe7, 0x07, 0x01, 0x00, + 0xb4, 0x08, 0xc0, 0x07, 0xff, 0xff, 0x97, 0xcf, + 0x9f, 0xaf, 0x4c, 0x03, 0xc0, 0x69, 0xb4, 0x08, + 0x57, 0x00, 0x9f, 0xde, 0x33, 0x00, 0xc1, 0x05, + 0x27, 0xd8, 0xb2, 0x08, 0x27, 0xd2, 0xb4, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xb4, 0x08, 0xe7, 0x67, + 0xff, 0x03, 0xb4, 0x08, 0x00, 0x60, 0x97, 0xc0, + 0xe7, 0x07, 0x01, 0x00, 0xb0, 0x08, 0x27, 0x00, + 0x12, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0xb6, 0x08, + 0x00, 0xd2, 0x02, 0xc3, 0xc0, 0x97, 0x05, 0x80, + 0x27, 0x00, 0xb6, 0x08, 0xc0, 0x99, 0x82, 0x08, + 0xc0, 0x99, 0xa2, 0xc0, 0x97, 0xcf, 0xe7, 0x07, + 0x00, 0x00, 0xb0, 0x08, 0xc0, 0xdf, 0x97, 0xcf, + 0xc8, 0x09, 0x72, 0x08, 0x08, 0x62, 0x02, 0xc0, + 0x10, 0x64, 0x07, 0xc1, 0xe7, 0x07, 0x00, 0x00, + 0x64, 0x08, 0xe7, 0x07, 0xc8, 0x05, 0x24, 0x00, + 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, 0xc8, 0x17, + 0x0e, 0x00, 0x27, 0x02, 0x64, 0x08, 0xe7, 0x07, + 0xd6, 0x05, 0x24, 0x00, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x62, 0x08, 0x13, 0xc1, 0x9f, 0xaf, 0x70, 0x03, + 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, 0x13, 0xc0, + 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, 0xe7, 0x07, + 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, 0x10, 0x00, + 0x96, 0xc0, 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, + 0x04, 0xcf, 0xe7, 0x57, 0x00, 0x00, 0x64, 0x08, + 0x02, 0xc1, 0x9f, 0xaf, 0x70, 0x03, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0xc1, 0xdf, 0xc8, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x78, 0x08, 0x08, 0x62, 0x03, 0xc1, 0xe7, 0x05, + 0x00, 0xc0, 0x97, 0xcf, 0x27, 0x04, 0x72, 0x08, + 0xe7, 0x05, 0x00, 0xc0, 0xf0, 0x07, 0x40, 0x00, + 0x08, 0x00, 0xf0, 0x07, 0x00, 0x00, 0x04, 0x00, + 0x00, 0x02, 0xc0, 0x17, 0x0c, 0x00, 0x30, 0x00, + 0x06, 0x00, 0xf0, 0x07, 0x64, 0x01, 0x0a, 0x00, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x02, 0x00, + 0x51, 0xaf, 0x97, 0xcf, 0xe7, 0x57, 0x00, 0x00, + 0x6a, 0x08, 0x97, 0xc0, 0xc1, 0xdf, 0xc8, 0x09, + 0x6a, 0x08, 0x27, 0x04, 0x6a, 0x08, 0x27, 0x52, + 0x6c, 0x08, 0x03, 0xc1, 0xe7, 0x07, 0x6a, 0x08, + 0x6c, 0x08, 0xc0, 0xdf, 0x17, 0x02, 0xc8, 0x17, + 0x0e, 0x00, 0x9f, 0xaf, 0x16, 0x05, 0xc8, 0x05, + 0x00, 0x60, 0x03, 0xc0, 0x9f, 0xaf, 0x80, 0x04, + 0x97, 0xcf, 0x9f, 0xaf, 0x68, 0x04, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x08, 0x62, + 0x1c, 0xc0, 0xd0, 0x09, 0x72, 0x08, 0x27, 0x02, + 0x72, 0x08, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x04, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x06, 0x00, 0xca, 0x17, 0x2c, 0x00, 0xf8, 0x77, + 0x01, 0x00, 0x0e, 0x00, 0x06, 0xc0, 0xca, 0xd9, + 0xf8, 0x57, 0xff, 0x00, 0x0e, 0x00, 0x01, 0xc1, + 0xca, 0xd9, 0x22, 0x1c, 0x0c, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xe2, 0x17, 0x01, 0x00, 0xe2, 0x27, + 0x00, 0x00, 0xca, 0x05, 0x00, 0x0c, 0x0c, 0x00, + 0xc0, 0x17, 0x41, 0x00, 0xc0, 0x67, 0xc0, 0xff, + 0x30, 0x00, 0x08, 0x00, 0x00, 0x02, 0xc0, 0x17, + 0x0c, 0x00, 0x30, 0x00, 0x06, 0x00, 0xf0, 0x07, + 0xdc, 0x00, 0x0a, 0x00, 0xf0, 0x07, 0x00, 0x00, + 0x04, 0x00, 0x00, 0x0c, 0x08, 0x00, 0x40, 0xd1, + 0x01, 0x00, 0xc0, 0x19, 0xa6, 0x08, 0xc0, 0x59, + 0x98, 0x08, 0x04, 0xc9, 0x49, 0xaf, 0x9f, 0xaf, + 0xee, 0x00, 0x4a, 0xaf, 0x67, 0x10, 0xa6, 0x08, + 0xc8, 0x17, 0x04, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x50, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, 0xc0, 0x07, + 0x01, 0x00, 0xc1, 0x09, 0x7c, 0x08, 0xc1, 0x77, + 0x01, 0x00, 0x97, 0xc1, 0xd8, 0x77, 0x01, 0x00, + 0x12, 0xc0, 0xc9, 0x07, 0x4c, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x04, 0xc1, 0xc1, 0x77, 0x08, 0x00, + 0x13, 0xc0, 0x97, 0xcf, 0xc1, 0x77, 0x02, 0x00, + 0x97, 0xc1, 0xc1, 0x77, 0x10, 0x00, 0x0c, 0xc0, + 0x9f, 0xaf, 0x86, 0x05, 0x97, 0xcf, 0xc1, 0x77, + 0x04, 0x00, 0x06, 0xc0, 0xc9, 0x07, 0x7e, 0x08, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x00, 0xcf, + 0x00, 0x90, 0x97, 0xcf, 0x50, 0x54, 0x97, 0xc1, + 0x70, 0x5c, 0x02, 0x00, 0x02, 0x00, 0x97, 0xc1, + 0x70, 0x5c, 0x04, 0x00, 0x04, 0x00, 0x97, 0xcf, + 0xc0, 0x00, 0x60, 0x00, 0x30, 0x00, 0x18, 0x00, + 0x0c, 0x00, 0x06, 0x00, 0x00, 0x00, 0xcb, 0x09, + 0x88, 0x08, 0xcc, 0x09, 0x8a, 0x08, 0x0b, 0x53, + 0x11, 0xc0, 0xc9, 0x02, 0xca, 0x07, 0x78, 0x05, + 0x9f, 0xaf, 0x64, 0x05, 0x97, 0xc0, 0x0a, 0xc8, + 0x82, 0x08, 0x0a, 0xcf, 0x82, 0x08, 0x9f, 0xaf, + 0x64, 0x05, 0x97, 0xc0, 0x05, 0xc2, 0x89, 0x30, + 0x82, 0x60, 0x78, 0xc1, 0x00, 0x90, 0x97, 0xcf, + 0x89, 0x10, 0x09, 0x53, 0x79, 0xc2, 0x89, 0x30, + 0x82, 0x08, 0x7a, 0xcf, 0xc0, 0xdf, 0x97, 0xcf, + 0xe7, 0x09, 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, + 0x98, 0xc0, 0x68, 0x08, 0x0f, 0xcf, 0xe7, 0x09, + 0x96, 0xc0, 0x66, 0x08, 0xe7, 0x09, 0x98, 0xc0, + 0x68, 0x08, 0xe7, 0x09, 0x64, 0x08, 0x30, 0x01, + 0xe7, 0x07, 0xf2, 0x05, 0x32, 0x01, 0xe7, 0x07, + 0x10, 0x00, 0x96, 0xc0, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x02, 0xc8, 0x09, 0x62, 0x08, 0xc8, 0x37, + 0x0e, 0x00, 0xe7, 0x57, 0x04, 0x00, 0x68, 0x08, + 0x3d, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x77, 0x2a, 0x00, 0x66, 0x08, + 0x30, 0xc0, 0x97, 0x02, 0xca, 0x09, 0xac, 0x08, + 0xe7, 0x77, 0x20, 0x00, 0x66, 0x08, 0x0e, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x10, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x12, 0x00, 0xe7, 0x77, 0x0a, 0x00, + 0x66, 0x08, 0xca, 0x05, 0x1e, 0xc0, 0x97, 0x02, + 0xca, 0x09, 0xac, 0x08, 0xf2, 0x17, 0x01, 0x00, + 0x0c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x0e, 0x00, + 0xe7, 0x77, 0x02, 0x00, 0x66, 0x08, 0x07, 0xc0, + 0xf2, 0x17, 0x01, 0x00, 0x44, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x46, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x60, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x62, 0x00, 0xca, 0x05, 0x9f, 0xaf, 0x68, 0x04, + 0x0f, 0xcf, 0x57, 0x02, 0x09, 0x02, 0xf1, 0x09, + 0x68, 0x08, 0x0c, 0x00, 0xf1, 0xda, 0x0c, 0x00, + 0xc8, 0x09, 0x6c, 0x08, 0x50, 0x02, 0x67, 0x02, + 0x6c, 0x08, 0xd1, 0x07, 0x00, 0x00, 0xc9, 0x05, + 0xe7, 0x09, 0x64, 0x08, 0x62, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x62, 0x08, 0x02, 0xc0, 0x9f, 0xaf, + 0x70, 0x03, 0xc8, 0x05, 0xe7, 0x05, 0x00, 0xc0, + 0xc0, 0xdf, 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, + 0x17, 0x00, 0x17, 0x02, 0x97, 0x02, 0xc0, 0x09, + 0x92, 0xc0, 0xe7, 0x87, 0x00, 0x08, 0x24, 0xc0, + 0xe7, 0x09, 0x94, 0x08, 0xba, 0x08, 0xe7, 0x17, + 0x64, 0x00, 0xba, 0x08, 0xe7, 0x67, 0xff, 0x07, + 0xba, 0x08, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xca, 0x09, 0xac, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x7a, 0x08, 0xe7, 0x07, 0x66, 0x03, 0x02, 0x00, + 0xc0, 0x77, 0x02, 0x00, 0x10, 0xc0, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x04, 0xc0, 0x9f, 0xaf, + 0xd8, 0x02, 0x9f, 0xcf, 0x12, 0x08, 0xf2, 0x17, + 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x52, 0x00, 0x9f, 0xcf, 0x12, 0x08, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x08, 0xc0, 0xe7, 0x57, + 0x00, 0x00, 0xb8, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0xb8, 0x08, 0x0a, 0xc0, 0x03, 0xcf, 0xc0, 0x77, + 0x10, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x58, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5a, 0x00, + 0xc0, 0x77, 0x80, 0x00, 0x06, 0xc0, 0xf2, 0x17, + 0x01, 0x00, 0x70, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x72, 0x00, 0xc0, 0x77, 0x08, 0x00, 0x1d, 0xc1, + 0xf2, 0x17, 0x01, 0x00, 0x08, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x0a, 0x00, 0xc0, 0x77, 0x00, 0x02, + 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, 0x64, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x66, 0x00, 0xc0, 0x77, + 0x40, 0x00, 0x06, 0xc0, 0xf2, 0x17, 0x01, 0x00, + 0x5c, 0x00, 0xf2, 0x27, 0x00, 0x00, 0x5e, 0x00, + 0xc0, 0x77, 0x01, 0x00, 0x01, 0xc0, 0x37, 0xcf, + 0x36, 0xcf, 0xf2, 0x17, 0x01, 0x00, 0x00, 0x00, + 0xf2, 0x27, 0x00, 0x00, 0x02, 0x00, 0xef, 0x57, + 0x00, 0x00, 0xf0, 0x09, 0x18, 0xc0, 0xe7, 0x57, + 0x01, 0x00, 0xb2, 0x08, 0x0e, 0xc2, 0x07, 0xc8, + 0xf2, 0x17, 0x01, 0x00, 0x50, 0x00, 0xf2, 0x27, + 0x00, 0x00, 0x52, 0x00, 0x06, 0xcf, 0xf2, 0x17, + 0x01, 0x00, 0x54, 0x00, 0xf2, 0x27, 0x00, 0x00, + 0x56, 0x00, 0xe7, 0x07, 0x00, 0x00, 0xb2, 0x08, + 0xe7, 0x07, 0x01, 0x00, 0xb4, 0x08, 0xc8, 0x09, + 0x34, 0x01, 0xca, 0x17, 0x14, 0x00, 0xd8, 0x77, + 0x01, 0x00, 0x05, 0xc0, 0xca, 0xd9, 0xd8, 0x57, + 0xff, 0x00, 0x01, 0xc0, 0xca, 0xd9, 0xe2, 0x19, + 0x94, 0xc0, 0xe2, 0x27, 0x00, 0x00, 0xe2, 0x17, + 0x01, 0x00, 0xe2, 0x27, 0x00, 0x00, 0x9f, 0xaf, + 0x2e, 0x08, 0x9f, 0xaf, 0xde, 0x01, 0xe7, 0x57, + 0x00, 0x00, 0xaa, 0x08, 0x9f, 0xa1, 0xf0, 0x0b, + 0xca, 0x05, 0xc8, 0x05, 0xc0, 0x05, 0xe7, 0x05, + 0x00, 0xc0, 0xc0, 0xdf, 0x97, 0xcf, 0xc8, 0x09, + 0x6e, 0x08, 0x08, 0x62, 0x97, 0xc0, 0x27, 0x04, + 0x6e, 0x08, 0x27, 0x52, 0x70, 0x08, 0x03, 0xc1, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x9f, 0xaf, + 0x68, 0x04, 0x97, 0xcf, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0xff, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x33, 0xcc, + 0x00, 0x00, 0x00, 0x00, 0xe7, 0x57, 0x00, 0x80, + 0xb2, 0x00, 0x06, 0xc2, 0xe7, 0x07, 0x52, 0x0e, + 0x12, 0x00, 0xe7, 0x07, 0x98, 0x0e, 0xb2, 0x00, + 0xe7, 0x07, 0xa4, 0x09, 0xf2, 0x02, 0xc8, 0x09, + 0xb4, 0x00, 0xf8, 0x07, 0x02, 0x00, 0x0d, 0x00, + 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x0e, 0xc0, 0xc8, 0x09, 0xdc, 0x00, 0xf0, 0x07, + 0xff, 0xff, 0x09, 0x00, 0xf0, 0x07, 0xfb, 0x13, + 0x0b, 0x00, 0xe7, 0x09, 0xc0, 0x00, 0x58, 0x08, + 0xe7, 0x09, 0xbe, 0x00, 0x54, 0x08, 0xe7, 0x09, + 0x10, 0x00, 0x92, 0x08, 0xc8, 0x07, 0xb4, 0x09, + 0x9f, 0xaf, 0x8c, 0x09, 0x9f, 0xaf, 0xe2, 0x0b, + 0xc0, 0x07, 0x80, 0x01, 0x44, 0xaf, 0x27, 0x00, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0x27, 0x00, + 0x8c, 0x08, 0xc0, 0x07, 0x74, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xac, 0x08, 0x08, 0x00, 0x00, 0x90, + 0xc1, 0x07, 0x1d, 0x00, 0x20, 0x00, 0x20, 0x00, + 0x01, 0xda, 0x7c, 0xc1, 0x9f, 0xaf, 0x8a, 0x0b, + 0xc0, 0x07, 0x4c, 0x00, 0x48, 0xaf, 0x27, 0x00, + 0x56, 0x08, 0x9f, 0xaf, 0x72, 0x0c, 0xe7, 0x07, + 0x00, 0x80, 0x96, 0x08, 0xef, 0x57, 0x00, 0x00, + 0xf0, 0x09, 0x03, 0xc0, 0xe7, 0x07, 0x01, 0x00, + 0x1c, 0xc0, 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, + 0x49, 0xaf, 0xe7, 0x87, 0x43, 0x00, 0x0e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0x8a, 0x0c, 0xc0, 0x07, 0x01, 0x00, 0x60, 0xaf, + 0x4a, 0xaf, 0x97, 0xcf, 0x00, 0x08, 0x09, 0x08, + 0x11, 0x08, 0x00, 0xda, 0x7c, 0xc1, 0x97, 0xcf, + 0x67, 0x04, 0xcc, 0x02, 0xc0, 0xdf, 0x51, 0x94, + 0xb1, 0xaf, 0x06, 0x00, 0xc1, 0xdf, 0xc9, 0x09, + 0xcc, 0x02, 0x49, 0x62, 0x75, 0xc1, 0xc0, 0xdf, + 0xa7, 0xcf, 0xd6, 0x02, 0x0e, 0x00, 0x24, 0x00, + 0xd6, 0x05, 0x22, 0x00, 0xc4, 0x06, 0xd0, 0x00, + 0xf0, 0x0b, 0xaa, 0x00, 0x0e, 0x0a, 0xbe, 0x00, + 0x2c, 0x0c, 0x10, 0x00, 0x20, 0x00, 0x04, 0x00, + 0xc4, 0x05, 0x02, 0x00, 0x66, 0x03, 0x06, 0x00, + 0x00, 0x00, 0x24, 0xc0, 0x04, 0x04, 0x28, 0xc0, + 0xfe, 0xfb, 0x1e, 0xc0, 0x00, 0x04, 0x22, 0xc0, + 0xff, 0xf0, 0xc0, 0x00, 0x60, 0x0b, 0x00, 0x00, + 0x00, 0x00, 0xff, 0xff, 0x34, 0x0a, 0x3e, 0x0a, + 0x9e, 0x0a, 0xa8, 0x0a, 0xce, 0x0a, 0xd2, 0x0a, + 0xd6, 0x0a, 0x00, 0x0b, 0x10, 0x0b, 0x1e, 0x0b, + 0x20, 0x0b, 0x28, 0x0b, 0x28, 0x0b, 0x27, 0x02, + 0xa2, 0x08, 0x97, 0xcf, 0xe7, 0x07, 0x00, 0x00, + 0xa2, 0x08, 0x0a, 0x0e, 0x01, 0x00, 0xca, 0x57, + 0x0e, 0x00, 0x9f, 0xc3, 0x2a, 0x0b, 0xca, 0x37, + 0x00, 0x00, 0x9f, 0xc2, 0x2a, 0x0b, 0x0a, 0xd2, + 0xb2, 0xcf, 0xf4, 0x09, 0xc8, 0x09, 0xde, 0x00, + 0x07, 0x06, 0x9f, 0xcf, 0x3c, 0x0b, 0xf0, 0x57, + 0x80, 0x01, 0x06, 0x00, 0x9f, 0xc8, 0x2a, 0x0b, + 0x27, 0x0c, 0x02, 0x00, 0x86, 0x08, 0xc0, 0x09, + 0x88, 0x08, 0x27, 0x00, 0x8a, 0x08, 0xe7, 0x07, + 0x00, 0x00, 0x84, 0x08, 0x27, 0x00, 0x5c, 0x08, + 0x00, 0x1c, 0x06, 0x00, 0x27, 0x00, 0x8c, 0x08, + 0x41, 0x90, 0x67, 0x50, 0x86, 0x08, 0x0d, 0xc0, + 0x67, 0x00, 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, + 0x5e, 0x08, 0xe7, 0x07, 0x8a, 0x0a, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, + 0x97, 0xcf, 0x9f, 0xaf, 0xac, 0x0e, 0xe7, 0x09, + 0x8c, 0x08, 0x8a, 0x08, 0xe7, 0x09, 0x86, 0x08, + 0x84, 0x08, 0x59, 0xaf, 0x97, 0xcf, 0x27, 0x0c, + 0x02, 0x00, 0x7c, 0x08, 0x59, 0xaf, 0x97, 0xcf, + 0x09, 0x0c, 0x02, 0x00, 0x09, 0xda, 0x49, 0xd2, + 0xc9, 0x19, 0xac, 0x08, 0xc8, 0x07, 0x5a, 0x08, + 0xe0, 0x07, 0x00, 0x00, 0x60, 0x02, 0xe0, 0x07, + 0x04, 0x00, 0xd0, 0x07, 0x9a, 0x0a, 0x48, 0xdb, + 0x41, 0x90, 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, + 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, 0xf0, 0x57, + 0x06, 0x00, 0x06, 0x00, 0x26, 0xc1, 0xe7, 0x07, + 0x7e, 0x08, 0x5c, 0x08, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0x27, 0x0c, 0x06, 0x00, 0x5e, 0x08, + 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, 0xc8, 0x07, + 0x5a, 0x08, 0x41, 0x90, 0x51, 0xaf, 0x97, 0xcf, + 0x07, 0x0c, 0x06, 0x00, 0xc7, 0x57, 0x06, 0x00, + 0x10, 0xc1, 0xc8, 0x07, 0x7e, 0x08, 0x16, 0xcf, + 0x00, 0x0c, 0x02, 0x00, 0x00, 0xda, 0x40, 0xd1, + 0x27, 0x00, 0x98, 0x08, 0x1f, 0xcf, 0x1e, 0xcf, + 0x27, 0x0c, 0x02, 0x00, 0xa4, 0x08, 0x1a, 0xcf, + 0x00, 0xcf, 0x27, 0x02, 0x20, 0x01, 0xe7, 0x07, + 0x08, 0x00, 0x22, 0x01, 0xe7, 0x07, 0x13, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0x41, 0x90, 0x67, 0x00, + 0x5a, 0x08, 0xe7, 0x01, 0x5e, 0x08, 0x27, 0x02, + 0x5c, 0x08, 0xe7, 0x07, 0x5c, 0x0b, 0x60, 0x08, + 0xc8, 0x07, 0x5a, 0x08, 0xc1, 0x07, 0x00, 0x80, + 0x50, 0xaf, 0x97, 0xcf, 0x59, 0xaf, 0x97, 0xcf, + 0x00, 0x60, 0x05, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x9a, 0x08, 0xa7, 0xcf, 0x58, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x01, 0x00, 0x9a, 0x08, + 0x49, 0xaf, 0xd7, 0x09, 0x00, 0xc0, 0x07, 0xaf, + 0xe7, 0x05, 0x00, 0xc0, 0x4a, 0xaf, 0xa7, 0xcf, + 0x58, 0x08, 0xc0, 0x07, 0x40, 0x00, 0x44, 0xaf, + 0x27, 0x00, 0xa0, 0x08, 0x08, 0x00, 0xc0, 0x07, + 0x20, 0x00, 0x20, 0x94, 0x00, 0xda, 0x7d, 0xc1, + 0xc0, 0x07, 0xfe, 0x7f, 0x44, 0xaf, 0x40, 0x00, + 0x41, 0x90, 0xc0, 0x37, 0x08, 0x00, 0xdf, 0xde, + 0x50, 0x06, 0xc0, 0x57, 0x10, 0x00, 0x02, 0xc2, + 0xc0, 0x07, 0x10, 0x00, 0x27, 0x00, 0x76, 0x08, + 0x41, 0x90, 0x9f, 0xde, 0x40, 0x06, 0x44, 0xaf, + 0x27, 0x00, 0x74, 0x08, 0xc0, 0x09, 0x76, 0x08, + 0x41, 0x90, 0x00, 0xd2, 0x00, 0xd8, 0x9f, 0xde, + 0x08, 0x00, 0x44, 0xaf, 0x27, 0x00, 0x9e, 0x08, + 0x97, 0xcf, 0xe7, 0x87, 0x00, 0x84, 0x28, 0xc0, + 0xe7, 0x67, 0xff, 0xf3, 0x24, 0xc0, 0x97, 0xcf, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0xe7, 0x57, + 0x00, 0x00, 0x7a, 0x08, 0x97, 0xc1, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x87, 0x00, 0x06, 0x22, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x90, 0xc0, 0xe7, 0x67, + 0xfe, 0xff, 0x3e, 0xc0, 0xe7, 0x07, 0x2e, 0x00, + 0x0a, 0xc0, 0xe7, 0x87, 0x01, 0x00, 0x3e, 0xc0, + 0xe7, 0x07, 0xff, 0xff, 0x94, 0x08, 0x9f, 0xaf, + 0xf0, 0x0c, 0x97, 0xcf, 0x17, 0x00, 0xa7, 0xaf, + 0x54, 0x08, 0xc0, 0x05, 0x27, 0x00, 0x52, 0x08, + 0xe7, 0x87, 0x01, 0x00, 0xaa, 0x08, 0x9f, 0xaf, + 0xe2, 0x0b, 0xe7, 0x07, 0x0c, 0x00, 0x40, 0xc0, + 0x9f, 0xaf, 0xf0, 0x0c, 0xe7, 0x07, 0x00, 0x00, + 0x78, 0x08, 0x00, 0x90, 0xe7, 0x09, 0x88, 0x08, + 0x8a, 0x08, 0x27, 0x00, 0x84, 0x08, 0x27, 0x00, + 0x7c, 0x08, 0x9f, 0xaf, 0x8a, 0x0c, 0xe7, 0x07, + 0x00, 0x00, 0xb2, 0x02, 0xe7, 0x07, 0x00, 0x00, + 0xb4, 0x02, 0xc0, 0x07, 0x06, 0x00, 0xc8, 0x09, + 0xde, 0x00, 0xc8, 0x17, 0x03, 0x00, 0xc9, 0x07, + 0x7e, 0x08, 0x29, 0x0a, 0x00, 0xda, 0x7d, 0xc1, + 0x97, 0xcf, 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, + 0x00, 0x90, 0x27, 0x00, 0x6a, 0x08, 0xe7, 0x07, + 0x6a, 0x08, 0x6c, 0x08, 0x27, 0x00, 0x6e, 0x08, + 0xe7, 0x07, 0x6e, 0x08, 0x70, 0x08, 0x27, 0x00, + 0x78, 0x08, 0x27, 0x00, 0x62, 0x08, 0x27, 0x00, + 0x64, 0x08, 0xc8, 0x09, 0x74, 0x08, 0xc1, 0x09, + 0x76, 0x08, 0xc9, 0x07, 0x72, 0x08, 0x11, 0x02, + 0x09, 0x02, 0xc8, 0x17, 0x40, 0x06, 0x01, 0xda, + 0x7a, 0xc1, 0x51, 0x94, 0xc8, 0x09, 0x9e, 0x08, + 0xc9, 0x07, 0x9c, 0x08, 0xc1, 0x09, 0x76, 0x08, + 0x01, 0xd2, 0x01, 0xd8, 0x11, 0x02, 0x09, 0x02, + 0xc8, 0x17, 0x08, 0x00, 0x01, 0xda, 0x7a, 0xc1, + 0x51, 0x94, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0xe7, 0x57, 0x00, 0x00, 0x52, 0x08, 0x97, 0xc0, + 0x9f, 0xaf, 0x04, 0x00, 0xe7, 0x09, 0x94, 0x08, + 0x90, 0x08, 0xe7, 0x57, 0xff, 0xff, 0x90, 0x08, + 0x04, 0xc1, 0xe7, 0x07, 0xf0, 0x0c, 0x8e, 0x08, + 0x97, 0xcf, 0xe7, 0x17, 0x32, 0x00, 0x90, 0x08, + 0xe7, 0x67, 0xff, 0x07, 0x90, 0x08, 0xe7, 0x07, + 0x26, 0x0d, 0x8e, 0x08, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0xc1, 0xdf, 0xe7, 0x57, 0x00, 0x00, + 0x96, 0x08, 0x23, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x80, 0xc0, 0xe7, 0x07, 0x04, 0x00, 0x90, 0xc0, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xc0, 0x07, 0x00, 0x00, + 0xc0, 0x07, 0x00, 0x00, 0xc0, 0x07, 0x00, 0x00, + 0xe7, 0x07, 0x00, 0x00, 0x80, 0xc0, 0xe7, 0x07, + 0x00, 0x80, 0x80, 0xc0, 0xe7, 0x07, 0x00, 0x80, + 0x40, 0xc0, 0xc0, 0x07, 0x00, 0x00, 0xe7, 0x07, + 0x00, 0x00, 0x40, 0xc0, 0xe7, 0x07, 0x00, 0x00, + 0x80, 0xc0, 0xef, 0x57, 0x00, 0x00, 0xf1, 0x09, + 0x9f, 0xa0, 0xc0, 0x0d, 0xe7, 0x07, 0x04, 0x00, + 0x90, 0xc0, 0xe7, 0x07, 0x00, 0x02, 0x40, 0xc0, + 0xe7, 0x07, 0x0c, 0x02, 0x40, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x96, 0x08, 0xe7, 0x07, 0x00, 0x00, + 0x8e, 0x08, 0xe7, 0x07, 0x00, 0x00, 0xaa, 0x08, + 0xd7, 0x09, 0x00, 0xc0, 0xc1, 0xdf, 0x9f, 0xaf, + 0x9e, 0x03, 0xe7, 0x05, 0x00, 0xc0, 0x9f, 0xaf, + 0xde, 0x01, 0xe7, 0x05, 0x00, 0xc0, 0x97, 0xcf, + 0x9f, 0xaf, 0xde, 0x0d, 0xef, 0x77, 0x00, 0x00, + 0xf1, 0x09, 0x97, 0xc1, 0x9f, 0xaf, 0xde, 0x0d, + 0xef, 0x77, 0x00, 0x00, 0xf1, 0x09, 0x97, 0xc1, + 0xef, 0x07, 0x01, 0x00, 0xf1, 0x09, 0xe7, 0x87, + 0x00, 0x08, 0x1e, 0xc0, 0xe7, 0x87, 0x00, 0x08, + 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, 0x22, 0xc0, + 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, 0x11, 0xc0, + 0xe7, 0x67, 0xff, 0xf7, 0x1e, 0xc0, 0xe7, 0x87, + 0x00, 0x08, 0x22, 0xc0, 0xe7, 0x67, 0xff, 0xf7, + 0x22, 0xc0, 0xe7, 0x77, 0x00, 0x08, 0x20, 0xc0, + 0x04, 0xc1, 0xe7, 0x87, 0x00, 0x08, 0x22, 0xc0, + 0x97, 0xcf, 0xe7, 0x07, 0x01, 0x01, 0xf0, 0x09, + 0xef, 0x57, 0x18, 0x00, 0xfe, 0xff, 0x97, 0xc2, + 0xef, 0x07, 0x00, 0x00, 0xf0, 0x09, 0x97, 0xcf, + 0xd7, 0x09, 0x00, 0xc0, 0x17, 0x00, 0x17, 0x02, + 0x97, 0x02, 0xe7, 0x57, 0x00, 0x00, 0x7a, 0x08, + 0x06, 0xc0, 0xc0, 0x09, 0x92, 0xc0, 0xc0, 0x77, + 0x09, 0x02, 0x9f, 0xc1, 0xea, 0x06, 0x9f, 0xcf, + 0x20, 0x08, 0xd7, 0x09, 0x0e, 0xc0, 0xe7, 0x07, + 0x00, 0x00, 0x0e, 0xc0, 0x9f, 0xaf, 0x66, 0x0e, + 0xe7, 0x05, 0x0e, 0xc0, 0x97, 0xcf, 0xd7, 0x09, + 0x00, 0xc0, 0x17, 0x02, 0xc8, 0x09, 0xb0, 0xc0, + 0xe7, 0x67, 0xfe, 0x7f, 0xb0, 0xc0, 0xc8, 0x77, + 0x00, 0x20, 0x9f, 0xc1, 0x64, 0xeb, 0xe7, 0x57, + 0x00, 0x00, 0xc8, 0x02, 0x9f, 0xc1, 0x80, 0xeb, + 0xc8, 0x99, 0xca, 0x02, 0xc8, 0x67, 0x04, 0x00, + 0x9f, 0xc1, 0x96, 0xeb, 0x9f, 0xcf, 0x4c, 0xeb, + 0xe7, 0x07, 0x00, 0x00, 0xa6, 0xc0, 0xe7, 0x09, + 0xb0, 0xc0, 0xc8, 0x02, 0xe7, 0x07, 0x03, 0x00, + 0xb0, 0xc0, 0x97, 0xcf, 0xc0, 0x09, 0x86, 0x08, + 0xc0, 0x37, 0x01, 0x00, 0x97, 0xc9, 0xc9, 0x09, + 0x88, 0x08, 0x02, 0x00, 0x41, 0x90, 0x48, 0x02, + 0xc9, 0x17, 0x06, 0x00, 0x9f, 0xaf, 0x64, 0x05, + 0x9f, 0xa2, 0xd6, 0x0e, 0x02, 0xda, 0x77, 0xc1, + 0x41, 0x60, 0x71, 0xc1, 0x97, 0xcf, 0x17, 0x02, + 0x57, 0x02, 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, + 0x43, 0x04, 0x21, 0x04, 0xe0, 0x00, 0x43, 0x04, + 0x21, 0x04, 0xe0, 0x00, 0xc1, 0x07, 0x01, 0x00, + 0xc9, 0x05, 0xc8, 0x05, 0x97, 0xcf, + 0, 0 +}; + +/**************************************************************** + * kaweth_new_code_fix + ****************************************************************/ +static __u8 kaweth_new_code_fix[] = +{ + 0xB6, 0xC3, 0xAA, 0xBB, 0xCC, 0xDD, + 0x02, 0x00, 0x08, 0x00, 0x24, 0x00, 0x2e, 0x00, + 0x2c, 0x00, 0x3e, 0x00, 0x44, 0x00, 0x48, 0x00, + 0x50, 0x00, 0x5c, 0x00, 0x60, 0x00, 0x66, 0x00, + 0x6c, 0x00, 0x70, 0x00, 0x76, 0x00, 0x74, 0x00, + 0x7a, 0x00, 0x7e, 0x00, 0x84, 0x00, 0x8a, 0x00, + 0x8e, 0x00, 0x92, 0x00, 0x98, 0x00, 0x9c, 0x00, + 0xa0, 0x00, 0xa8, 0x00, 0xae, 0x00, 0xb4, 0x00, + 0xb2, 0x00, 0xba, 0x00, 0xbe, 0x00, 0xc4, 0x00, + 0xc8, 0x00, 0xce, 0x00, 0xd2, 0x00, 0xd6, 0x00, + 0xda, 0x00, 0xe2, 0x00, 0xe0, 0x00, 0xea, 0x00, + 0xf2, 0x00, 0xfe, 0x00, 0x06, 0x01, 0x0c, 0x01, + 0x1a, 0x01, 0x24, 0x01, 0x22, 0x01, 0x2a, 0x01, + 0x30, 0x01, 0x36, 0x01, 0x3c, 0x01, 0x4e, 0x01, + 0x52, 0x01, 0x58, 0x01, 0x5c, 0x01, 0x9c, 0x01, + 0xb6, 0x01, 0xba, 0x01, 0xc0, 0x01, 0xca, 0x01, + 0xd0, 0x01, 0xda, 0x01, 0xe2, 0x01, 0xea, 0x01, + 0xf0, 0x01, 0x0a, 0x02, 0x0e, 0x02, 0x14, 0x02, + 0x26, 0x02, 0x6c, 0x02, 0x8e, 0x02, 0x98, 0x02, + 0xa0, 0x02, 0xa6, 0x02, 0xba, 0x02, 0xc6, 0x02, + 0xce, 0x02, 0xe8, 0x02, 0xee, 0x02, 0xf4, 0x02, + 0xf8, 0x02, 0x0a, 0x03, 0x10, 0x03, 0x1a, 0x03, + 0x1e, 0x03, 0x2a, 0x03, 0x2e, 0x03, 0x34, 0x03, + 0x3a, 0x03, 0x44, 0x03, 0x4e, 0x03, 0x5a, 0x03, + 0x5e, 0x03, 0x6a, 0x03, 0x72, 0x03, 0x80, 0x03, + 0x84, 0x03, 0x8c, 0x03, 0x94, 0x03, 0x98, 0x03, + 0xa8, 0x03, 0xae, 0x03, 0xb4, 0x03, 0xba, 0x03, + 0xce, 0x03, 0xcc, 0x03, 0xd6, 0x03, 0xdc, 0x03, + 0xec, 0x03, 0xf0, 0x03, 0xfe, 0x03, 0x1c, 0x04, + 0x30, 0x04, 0x38, 0x04, 0x3c, 0x04, 0x40, 0x04, + 0x48, 0x04, 0x46, 0x04, 0x54, 0x04, 0x5e, 0x04, + 0x64, 0x04, 0x74, 0x04, 0x78, 0x04, 0x84, 0x04, + 0xd8, 0x04, 0xec, 0x04, 0xf0, 0x04, 0xf8, 0x04, + 0xfe, 0x04, 0x1c, 0x05, 0x2c, 0x05, 0x30, 0x05, + 0x4a, 0x05, 0x56, 0x05, 0x5a, 0x05, 0x88, 0x05, + 0x8c, 0x05, 0x96, 0x05, 0x9a, 0x05, 0xa8, 0x05, + 0xcc, 0x05, 0xd2, 0x05, 0xda, 0x05, 0xe0, 0x05, + 0xe4, 0x05, 0xfc, 0x05, 0x06, 0x06, 0x14, 0x06, + 0x12, 0x06, 0x1a, 0x06, 0x20, 0x06, 0x26, 0x06, + 0x2e, 0x06, 0x34, 0x06, 0x48, 0x06, 0x52, 0x06, + 0x64, 0x06, 0x86, 0x06, 0x90, 0x06, 0x9a, 0x06, + 0xa0, 0x06, 0xac, 0x06, 0xaa, 0x06, 0xb2, 0x06, + 0xb8, 0x06, 0xdc, 0x06, 0xda, 0x06, 0xe2, 0x06, + 0xe8, 0x06, 0xf2, 0x06, 0xf8, 0x06, 0xfc, 0x06, + 0x0a, 0x07, 0x10, 0x07, 0x14, 0x07, 0x24, 0x07, + 0x2a, 0x07, 0x32, 0x07, 0x38, 0x07, 0xb2, 0x07, + 0xba, 0x07, 0xde, 0x07, 0xe4, 0x07, 0x10, 0x08, + 0x14, 0x08, 0x1a, 0x08, 0x1e, 0x08, 0x30, 0x08, + 0x38, 0x08, 0x3c, 0x08, 0x44, 0x08, 0x42, 0x08, + 0x48, 0x08, 0xc6, 0x08, 0xcc, 0x08, 0xd2, 0x08, + 0xfe, 0x08, 0x04, 0x09, 0x0a, 0x09, 0x0e, 0x09, + 0x12, 0x09, 0x16, 0x09, 0x20, 0x09, 0x24, 0x09, + 0x28, 0x09, 0x32, 0x09, 0x46, 0x09, 0x4a, 0x09, + 0x50, 0x09, 0x54, 0x09, 0x5a, 0x09, 0x60, 0x09, + 0x7c, 0x09, 0x80, 0x09, 0xb8, 0x09, 0xbc, 0x09, + 0xc0, 0x09, 0xc4, 0x09, 0xc8, 0x09, 0xcc, 0x09, + 0xd0, 0x09, 0xd4, 0x09, 0xec, 0x09, 0xf4, 0x09, + 0xf6, 0x09, 0xf8, 0x09, 0xfa, 0x09, 0xfc, 0x09, + 0xfe, 0x09, 0x00, 0x0a, 0x02, 0x0a, 0x04, 0x0a, + 0x06, 0x0a, 0x08, 0x0a, 0x0a, 0x0a, 0x0c, 0x0a, + 0x10, 0x0a, 0x18, 0x0a, 0x24, 0x0a, 0x2c, 0x0a, + 0x32, 0x0a, 0x3c, 0x0a, 0x46, 0x0a, 0x4c, 0x0a, + 0x50, 0x0a, 0x54, 0x0a, 0x5a, 0x0a, 0x5e, 0x0a, + 0x66, 0x0a, 0x6c, 0x0a, 0x72, 0x0a, 0x78, 0x0a, + 0x7e, 0x0a, 0x7c, 0x0a, 0x82, 0x0a, 0x8c, 0x0a, + 0x92, 0x0a, 0x90, 0x0a, 0x98, 0x0a, 0x96, 0x0a, + 0xa2, 0x0a, 0xb2, 0x0a, 0xb6, 0x0a, 0xc4, 0x0a, + 0xe2, 0x0a, 0xe0, 0x0a, 0xe8, 0x0a, 0xee, 0x0a, + 0xf4, 0x0a, 0xf2, 0x0a, 0xf8, 0x0a, 0x0c, 0x0b, + 0x1a, 0x0b, 0x24, 0x0b, 0x40, 0x0b, 0x44, 0x0b, + 0x48, 0x0b, 0x4e, 0x0b, 0x4c, 0x0b, 0x52, 0x0b, + 0x68, 0x0b, 0x6c, 0x0b, 0x70, 0x0b, 0x76, 0x0b, + 0x88, 0x0b, 0x92, 0x0b, 0xbe, 0x0b, 0xca, 0x0b, + 0xce, 0x0b, 0xde, 0x0b, 0xf4, 0x0b, 0xfa, 0x0b, + 0x00, 0x0c, 0x24, 0x0c, 0x28, 0x0c, 0x30, 0x0c, + 0x36, 0x0c, 0x3c, 0x0c, 0x40, 0x0c, 0x4a, 0x0c, + 0x50, 0x0c, 0x58, 0x0c, 0x56, 0x0c, 0x5c, 0x0c, + 0x60, 0x0c, 0x64, 0x0c, 0x80, 0x0c, 0x94, 0x0c, + 0x9a, 0x0c, 0x98, 0x0c, 0x9e, 0x0c, 0xa4, 0x0c, + 0xa2, 0x0c, 0xa8, 0x0c, 0xac, 0x0c, 0xb0, 0x0c, + 0xb4, 0x0c, 0xb8, 0x0c, 0xbc, 0x0c, 0xce, 0x0c, + 0xd2, 0x0c, 0xd6, 0x0c, 0xf4, 0x0c, 0xfa, 0x0c, + 0x00, 0x0d, 0xfe, 0x0c, 0x06, 0x0d, 0x0e, 0x0d, + 0x0c, 0x0d, 0x16, 0x0d, 0x1c, 0x0d, 0x22, 0x0d, + 0x20, 0x0d, 0x30, 0x0d, 0x7e, 0x0d, 0x82, 0x0d, + 0x9a, 0x0d, 0xa0, 0x0d, 0xa6, 0x0d, 0xb0, 0x0d, + 0xb8, 0x0d, 0xc2, 0x0d, 0xc8, 0x0d, 0xce, 0x0d, + 0xd4, 0x0d, 0xdc, 0x0d, 0x1e, 0x0e, 0x2c, 0x0e, + 0x3e, 0x0e, 0x4c, 0x0e, 0x50, 0x0e, 0x5e, 0x0e, + 0xae, 0x0e, 0xb8, 0x0e, 0xc6, 0x0e, 0xca, 0x0e, + 0, 0 +}; + +const int len_kaweth_trigger_code = sizeof(kaweth_trigger_code); +const int len_kaweth_trigger_code_fix = sizeof(kaweth_trigger_code_fix); +const int len_kaweth_new_code = sizeof(kaweth_new_code); +const int len_kaweth_new_code_fix = sizeof(kaweth_new_code_fix); diff --git a/drivers/usb/keybdev.c b/drivers/usb/keybdev.c new file mode 100644 index 000000000000..5cd60248ce57 --- /dev/null +++ b/drivers/usb/keybdev.c @@ -0,0 +1,198 @@ +/* + * $Id: keybdev.c,v 1.3 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Input driver to keyboard driver binding. + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#if defined(__i386__) || defined(__alpha__) || defined(__mips__) + +static int x86_sysrq_alt = 0; + +static unsigned short x86_keycodes[256] = + { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 43, 85, 86, 87, 88,115,119,120,121,375,123, 90, + 284,285,309,298,312, 91,327,328,329,331,333,335,336,337,338,339, + 367,294,293,286,350, 92,334,512,116,377,109,111,373,347,348,349, + 360, 93, 94, 95, 98,376,100,101,357,316,354,304,289,102,351,355, + 103,104,105,275,281,272,306,106,274,107,288,364,358,363,362,361, + 291,108,381,290,287,292,279,305,280, 99,112,257,258,113,270,114, + 118,117,125,374,379,259,260,261,262,263,264,265,266,267,268,269, + 271,273,276,277,278,282,283,295,296,297,299,300,301,302,303,307, + 308,310,313,314,315,317,318,319,320,321,322,323,324,325,326,330, + 332,340,341,342,343,344,345,346,356,359,365,368,369,370,371,372 }; + +static int emulate_raw(unsigned int keycode, int down) +{ + if (keycode > 255 || !x86_keycodes[keycode]) + return -1; + + if (keycode == KEY_PAUSE) { + handle_scancode(0xe1, 1); + handle_scancode(0x1d, down); + handle_scancode(0x45, down); + return 0; + } + + if (keycode == KEY_SYSRQ && x86_sysrq_alt) { + handle_scancode(0x54, down); + return 0; + } + + if (x86_keycodes[keycode] & 0x100) + handle_scancode(0xe0, 1); + + handle_scancode(x86_keycodes[keycode] & 0x7f, down); + + if (keycode == KEY_SYSRQ) { + handle_scancode(0xe0, 1); + handle_scancode(0x37, down); + } + + if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT) + x86_sysrq_alt = down; + + return 0; +} + +#elif defined(CONFIG_MAC_KEYBOARD) + +static unsigned char mac_keycodes[128] = + { 0, 53, 18, 19, 20, 21, 23, 22, 26, 28, 25, 29, 27, 24, 51, 48, + 12, 13, 14, 15, 17, 16, 32, 34, 31, 35, 33, 30, 36, 54,128, 1, + 2, 3, 5, 4, 38, 40, 37, 41, 39, 50, 56, 42, 6, 7, 8, 9, + 11, 45, 46, 43, 47, 44,123, 67, 58, 49, 57,122,120, 99,118, 96, + 97, 98,100,101,109, 71,107, 89, 91, 92, 78, 86, 87, 88, 69, 83, + 84, 85, 82, 65, 42, 0, 10,103,111, 0, 0, 0, 0, 0, 0, 0, + 76,125, 75,105,124, 0,115, 62,116, 59, 60,119, 61,121,114,117, + 0, 0, 0, 0,127, 81, 0,113, 0, 0, 0, 0, 0, 55, 55 }; + +static int emulate_raw(unsigned int keycode, int down) +{ + if (keycode > 127 || !mac_keycodes[keycode]) + return -1; + + handle_scancode(mac_keycodes[keycode] & 0x7f, down); + + return 0; +} + +#endif + +static struct input_handler keybdev_handler; + +void keybdev_ledfunc(unsigned int led) +{ + struct input_handle *handle; + + for (handle = keybdev_handler.handle; handle; handle = handle->hnext) { + + input_event(handle->dev, EV_LED, LED_SCROLLL, !!(led & 0x01)); + input_event(handle->dev, EV_LED, LED_NUML, !!(led & 0x02)); + input_event(handle->dev, EV_LED, LED_CAPSL, !!(led & 0x04)); + + } +} + +void keybdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int down) +{ + if (type != EV_KEY) return; + + if (emulate_raw(code, down)) + printk(KERN_WARNING "keyboard.c: can't emulate rawmode for keycode %d\n", code); + + mark_bh(KEYBOARD_BH); +} + +static struct input_handle *keybdev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct input_handle *handle; + int i; + + if (!test_bit(EV_KEY, dev->evbit)) + return NULL; + + for (i = KEY_RESERVED; i < BTN_MISC; i++) + if (test_bit(i, dev->keybit)) break; + + if (i == BTN_MISC) + return NULL; + + if (!(handle = kmalloc(sizeof(struct input_handle), GFP_KERNEL))) + return NULL; + memset(handle, 0, sizeof(struct input_handle)); + + handle->dev = dev; + handle->handler = handler; + + input_open_device(handle); + + printk(KERN_INFO "keybdev.c: Adding keyboard: input%d\n", dev->number); + + return handle; +} + +static void keybdev_disconnect(struct input_handle *handle) +{ + printk(KERN_INFO "keybdev.c: Removing keyboard: input%d\n", handle->dev->number); + input_close_device(handle); + kfree(handle); +} + +static struct input_handler keybdev_handler = { + event: keybdev_event, + connect: keybdev_connect, + disconnect: keybdev_disconnect, +}; + +static int __init keybdev_init(void) +{ + input_register_handler(&keybdev_handler); + kbd_ledfunc = keybdev_ledfunc; + return 0; +} + +static void __exit keybdev_exit(void) +{ + kbd_ledfunc = NULL; + input_unregister_handler(&keybdev_handler); +} + +module_init(keybdev_init); +module_exit(keybdev_exit); diff --git a/drivers/usb/keyboard.c b/drivers/usb/keyboard.c deleted file mode 100644 index c60c812a587e..000000000000 --- a/drivers/usb/keyboard.c +++ /dev/null @@ -1,226 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "usb.h" - -#define PCKBD_PRESSED 0x00 -#define PCKBD_RELEASED 0x80 -#define PCKBD_NEEDS_E0 0x80 - -#define USBKBD_MODIFIER_BASE 120 -#define USBKBD_KEYCODE_OFFSET 2 -#define USBKBD_KEYCODE_COUNT 6 - -#define USBKBD_VALID_KEYCODE(key) ((unsigned char)(key) > 3) -#define USBKBD_FIND_KEYCODE(down, key, count) \ - ((unsigned char*) memscan((down), (key), (count)) < ((down) + (count))) - -#define USBKBD_REPEAT_DELAY (HZ / 4) -#define USBKBD_REPEAT_RATE (HZ / 20) - -struct usb_keyboard -{ - struct usb_device *dev; - unsigned long down[2]; - unsigned char repeat_key; - struct timer_list repeat_timer; - struct list_head list; -}; - -extern unsigned char usb_kbd_map[]; - -static int usb_kbd_probe(struct usb_device *dev); -static void usb_kbd_disconnect(struct usb_device *dev); -static void usb_kbd_repeat(unsigned long dummy); - -static LIST_HEAD(usb_kbd_list); - -static struct usb_driver usb_kbd_driver = -{ - "keyboard", - usb_kbd_probe, - usb_kbd_disconnect, - {NULL, NULL} -}; - - -static void -usb_kbd_handle_key(unsigned char key, int down) -{ - int scancode = (int) usb_kbd_map[key]; - if(scancode) - { - if(scancode & PCKBD_NEEDS_E0) - { - handle_scancode(0xe0, 1); - } - handle_scancode((scancode & ~PCKBD_NEEDS_E0), down); - } -} - -static void -usb_kbd_repeat(unsigned long dev_id) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; - - unsigned long flags; - save_flags(flags); - cli(); - - if(kbd->repeat_key) - { - usb_kbd_handle_key(kbd->repeat_key, 1); - - /* reset repeat timer */ - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_RATE; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } - - restore_flags(flags); -} - -static int -usb_kbd_irq(int state, void *buffer, void *dev_id) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev_id; - unsigned long *down = (unsigned long*) buffer; - - if(kbd->down[0] != down[0] || kbd->down[1] != down[1]) - { - unsigned char *olddown, *newdown; - unsigned char modsdelta, key; - int i; - - /* handle modifier change */ - modsdelta = (*(unsigned char*) down ^ *(unsigned char*) kbd->down); - if(modsdelta) - { - for(i = 0; i < 8; i++) - { - if(modsdelta & 0x01) - { - int pressed = (*(unsigned char*) down >> i) & 0x01; - usb_kbd_handle_key( - i + USBKBD_MODIFIER_BASE, - pressed); - } - modsdelta >>= 1; - } - } - - olddown = (unsigned char*) kbd->down + USBKBD_KEYCODE_OFFSET; - newdown = (unsigned char*) down + USBKBD_KEYCODE_OFFSET; - - /* handle released keys */ - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = olddown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(newdown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 0); - } - } - - /* handle pressed keys */ - kbd->repeat_key = 0; - for(i = 0; i < USBKBD_KEYCODE_COUNT; i++) - { - key = newdown[i]; - if(USBKBD_VALID_KEYCODE(key) - && !USBKBD_FIND_KEYCODE(olddown, key, USBKBD_KEYCODE_COUNT)) - { - usb_kbd_handle_key(key, 1); - kbd->repeat_key = key; - } - } - - /* set repeat timer if any keys were pressed */ - if(kbd->repeat_key) - { - del_timer(&kbd->repeat_timer); - kbd->repeat_timer.function = usb_kbd_repeat; - kbd->repeat_timer.expires = jiffies + USBKBD_REPEAT_DELAY; - kbd->repeat_timer.data = (unsigned long) kbd; - kbd->repeat_timer.prev = NULL; - kbd->repeat_timer.next = NULL; - add_timer(&kbd->repeat_timer); - } - - kbd->down[0] = down[0]; - kbd->down[1] = down[1]; - } - - return 1; -} - -static int -usb_kbd_probe(struct usb_device *dev) -{ - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct usb_keyboard *kbd; - - interface = &dev->config[0].interface[0]; - endpoint = &interface->endpoint[0]; - - if(interface->bInterfaceClass != 3 - || interface->bInterfaceSubClass != 1 - || interface->bInterfaceProtocol != 1) - { - return -1; - } - - printk(KERN_INFO "USB HID boot protocol keyboard detected.\n"); - - kbd = kmalloc(sizeof(struct usb_keyboard), GFP_KERNEL); - if(kbd) - { - memset(kbd, 0, sizeof(*kbd)); - kbd->dev = dev; - dev->private = kbd; - - usb_set_configuration(dev, dev->config[0].bConfigurationValue); - usb_set_protocol(dev, 0); - usb_set_idle(dev, 0, 0); - - usb_request_irq(dev, - usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), - usb_kbd_irq, - endpoint->bInterval, - kbd); - - list_add(&kbd->list, &usb_kbd_list); - } - - return 0; -} - -static void -usb_kbd_disconnect(struct usb_device *dev) -{ - struct usb_keyboard *kbd = (struct usb_keyboard*) dev->private; - if(kbd) - { - dev->private = NULL; - list_del(&kbd->list); - del_timer(&kbd->repeat_timer); - kfree(kbd); - } - - printk(KERN_INFO "USB HID boot protocol keyboard removed.\n"); -} - -int -usb_kbd_init(void) -{ - usb_register(&usb_kbd_driver); - return 0; -} diff --git a/drivers/usb/keymap.c b/drivers/usb/keymap.c deleted file mode 100644 index 16c7b28f3b5e..000000000000 --- a/drivers/usb/keymap.c +++ /dev/null @@ -1,50 +0,0 @@ -unsigned char usb_kbd_map[256] = -{ - 0x00, 0x00, 0x00, 0x00, 0x1e, 0x30, 0x2e, 0x20, - 0x12, 0x21, 0x22, 0x23, 0x17, 0x24, 0x25, 0x26, - - 0x32, 0x31, 0x18, 0x19, 0x10, 0x13, 0x1f, 0x14, - 0x16, 0x2f, 0x11, 0x2d, 0x15, 0x2c, 0x02, 0x03, - - 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, - 0x1c, 0x01, 0xd3, 0x0f, 0x39, 0x0c, 0x0d, 0x1a, - - 0x1b, 0x2b, 0x00, 0x27, 0x28, 0x29, 0x33, 0x34, - 0x35, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, - - 0x41, 0x42, 0x43, 0x44, 0x57, 0x58, 0xb7, 0x46, - 0x00, 0xd2, 0xc7, 0xc9, 0x63, 0xcf, 0xd1, 0xcd, - - 0xcb, 0xd0, 0xc8, 0x45, 0xb5, 0x37, 0x4a, 0x4e, - 0x9c, 0x4f, 0x50, 0x51, 0x4b, 0x4c, 0x4d, 0x47, - - 0x48, 0x49, 0x52, 0x53, 0x00, 0x6d, 0x00, 0x00, - 0xbd, 0xbe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1d, 0x2a, 0x38, 0xdb, 0x9d, 0x36, 0xb8, 0xdc, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, -}; diff --git a/drivers/usb/maps/fixup.map b/drivers/usb/maps/fixup.map deleted file mode 100644 index fc5d1ed7f227..000000000000 --- a/drivers/usb/maps/fixup.map +++ /dev/null @@ -1,31 +0,0 @@ -# misc fixes -keycode 0 = Pause -keycode 29 = Control -keycode 99 = Remove -keycode 42 = Shift -keycode 54 = Shift_R -keycode 109 = Application - -# E0 keys (or'ed with 0x80) -keycode 156 = KP_Enter -keycode 157 = Control_R -keycode 181 = KP_Divide -keycode 183 = Print_Screen -keycode 184 = Alt_R -keycode 189 = F13 -keycode 190 = F14 -keycode 193 = F17 -keycode 198 = Break -keycode 199 = Home -keycode 200 = Up -keycode 201 = Prior -keycode 203 = Left -keycode 205 = Right -keycode 207 = End -keycode 208 = Down -keycode 209 = Next -keycode 210 = Insert -keycode 211 = Delete -keycode 219 = Window -keycode 220 = Window_R -keycode 221 = Menu diff --git a/drivers/usb/maps/serial.map b/drivers/usb/maps/serial.map deleted file mode 100644 index e421a0d23814..000000000000 --- a/drivers/usb/maps/serial.map +++ /dev/null @@ -1,370 +0,0 @@ -keymaps 0-2,4-6,8-9,12 -keycode 1 = Escape - alt keycode 1 = Meta_Escape - shift alt keycode 1 = Meta_Escape - control alt keycode 1 = Meta_Escape -keycode 2 = one exclam - alt keycode 2 = Meta_one - shift alt keycode 2 = Meta_exclam -keycode 3 = two at at nul nul - alt keycode 3 = Meta_two - shift alt keycode 3 = Meta_at - control alt keycode 3 = Meta_nul -keycode 4 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three - shift alt keycode 4 = Meta_numbersign -keycode 5 = four dollar dollar Control_backslash - alt keycode 5 = Meta_four - shift alt keycode 5 = Meta_dollar - control alt keycode 5 = Meta_Control_backslash -keycode 6 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five - shift alt keycode 6 = Meta_percent -keycode 7 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six - shift alt keycode 7 = Meta_asciicircum -keycode 8 = seven ampersand braceleft Control_underscore - alt keycode 8 = Meta_seven - shift alt keycode 8 = Meta_ampersand - control alt keycode 8 = Meta_Control_underscore -keycode 9 = eight asterisk bracketleft Delete - alt keycode 9 = Meta_eight - shift alt keycode 9 = Meta_asterisk - control alt keycode 9 = Meta_Delete -keycode 10 = nine parenleft bracketright - alt keycode 10 = Meta_nine - shift alt keycode 10 = Meta_parenleft -keycode 11 = zero parenright braceright - alt keycode 11 = Meta_zero - shift alt keycode 11 = Meta_parenright -keycode 12 = minus underscore backslash Control_underscore Control_underscore - alt keycode 12 = Meta_minus - shift alt keycode 12 = Meta_underscore - control alt keycode 12 = Meta_Control_underscore -keycode 13 = equal plus - alt keycode 13 = Meta_equal - shift alt keycode 13 = Meta_plus -keycode 14 = Delete - alt keycode 14 = Meta_Delete - shift alt keycode 14 = Meta_Delete - control alt keycode 14 = Meta_Delete -keycode 15 = Tab - alt keycode 15 = Meta_Tab - shift alt keycode 15 = Meta_Tab - control alt keycode 15 = Meta_Tab -keycode 16 = q -keycode 17 = w -keycode 18 = e -keycode 19 = r -keycode 20 = t -keycode 21 = y -keycode 22 = u -keycode 23 = i -keycode 24 = o -keycode 25 = p -keycode 26 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft - shift alt keycode 26 = Meta_braceleft -keycode 27 = bracketright braceright asciitilde Control_bracketright - alt keycode 27 = Meta_bracketright - shift alt keycode 27 = Meta_braceright - control alt keycode 27 = Meta_Control_bracketright -keycode 28 = Return - alt keycode 28 = Meta_Control_m -keycode 29 = Control -keycode 30 = a -keycode 31 = s -keycode 32 = d -keycode 33 = f -keycode 34 = g -keycode 35 = h -keycode 36 = j -keycode 37 = k -keycode 38 = l -keycode 39 = semicolon colon - alt keycode 39 = Meta_semicolon - shift alt keycode 39 = Meta_colon -keycode 40 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe - shift alt keycode 40 = Meta_quotedbl -keycode 41 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave - shift alt keycode 41 = Meta_asciitilde -keycode 42 = Shift -keycode 43 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash - shift alt keycode 43 = Meta_bar -keycode 44 = z -keycode 45 = x -keycode 46 = c -keycode 47 = v -keycode 48 = b -keycode 49 = n -keycode 50 = m -keycode 51 = comma less - alt keycode 51 = Meta_comma - shift alt keycode 51 = Meta_less -keycode 52 = period greater - alt keycode 52 = Meta_period - shift alt keycode 52 = Meta_greater -keycode 53 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash - shift alt keycode 53 = Meta_question -keycode 54 = Shift -keycode 55 = KP_Multiply - altgr keycode 55 = Hex_C -keycode 56 = Alt -keycode 57 = space - control keycode 57 = nul - alt keycode 57 = Meta_space - shift alt keycode 57 = Meta_space - control alt keycode 57 = Meta_nul -keycode 58 = Caps_Lock -keycode 59 = F1 F13 Console_13 F25 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 60 = F2 F14 Console_14 F26 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 61 = F3 F15 Console_15 F27 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 62 = F4 F16 Console_16 F28 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 63 = F5 F17 Console_17 F29 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 64 = F6 F18 Console_18 F30 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 65 = F7 F19 Console_19 F31 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 66 = F8 F20 Console_20 F32 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 67 = F9 F21 Console_21 F33 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 68 = F10 F22 Console_22 F34 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 69 = Num_Lock - altgr keycode 69 = Hex_E -keycode 70 = Scroll_Lock Show_Memory Show_Registers Show_State - alt keycode 70 = Scroll_Lock -keycode 71 = KP_7 - altgr keycode 71 = Hex_7 - alt keycode 71 = Ascii_7 -keycode 72 = KP_8 - altgr keycode 72 = Hex_8 - alt keycode 72 = Ascii_8 -keycode 73 = KP_9 - altgr keycode 73 = Hex_9 - alt keycode 73 = Ascii_9 -keycode 74 = KP_Subtract -keycode 75 = KP_4 - altgr keycode 75 = Hex_4 - alt keycode 75 = Ascii_4 -keycode 76 = KP_5 - altgr keycode 76 = Hex_5 - alt keycode 76 = Ascii_5 -keycode 77 = KP_6 - altgr keycode 77 = Hex_6 - alt keycode 77 = Ascii_6 -keycode 78 = KP_Add -keycode 79 = KP_1 - altgr keycode 79 = Hex_1 - alt keycode 79 = Ascii_1 -keycode 80 = KP_2 - altgr keycode 80 = Hex_2 - alt keycode 80 = Ascii_2 -keycode 81 = KP_3 - altgr keycode 81 = Hex_3 - alt keycode 81 = Ascii_3 -keycode 82 = KP_0 - altgr keycode 82 = Hex_0 - alt keycode 82 = Ascii_0 -keycode 83 = KP_Period - altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 84 = Last_Console -keycode 85 = -keycode 86 = less greater bar - alt keycode 86 = Meta_less - shift alt keycode 86 = Meta_greater -keycode 87 = F11 F23 Console_23 F35 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 88 = F12 F24 Console_24 F36 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 89 = -keycode 90 = -keycode 91 = -keycode 92 = -keycode 93 = -keycode 94 = -keycode 95 = -keycode 96 = KP_Enter -keycode 97 = Control -keycode 98 = KP_Divide - altgr keycode 98 = Hex_B -keycode 99 = Control_backslash - alt keycode 99 = Meta_Control_backslash - shift alt keycode 99 = Meta_Control_backslash - control alt keycode 99 = Meta_Control_backslash -keycode 100 = AltGr -keycode 101 = Break -keycode 102 = Find -keycode 103 = Up - alt keycode 103 = KeyboardSignal -keycode 104 = Prior - shift keycode 104 = Scroll_Backward -keycode 105 = Left - alt keycode 105 = Decr_Console -keycode 106 = Right - alt keycode 106 = Incr_Console -keycode 107 = Select -keycode 108 = Down -keycode 109 = Next - shift keycode 109 = Scroll_Forward -keycode 110 = Insert -keycode 111 = Remove - altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 112 = Macro - altgr control keycode 112 = VoidSymbol - shift alt keycode 112 = VoidSymbol -keycode 113 = F13 - altgr control keycode 113 = VoidSymbol - shift alt keycode 113 = VoidSymbol -keycode 114 = F14 - altgr control keycode 114 = VoidSymbol - shift alt keycode 114 = VoidSymbol -keycode 115 = Help - altgr control keycode 115 = VoidSymbol - shift alt keycode 115 = VoidSymbol -keycode 116 = Do - altgr control keycode 116 = VoidSymbol - shift alt keycode 116 = VoidSymbol -keycode 117 = F17 - altgr control keycode 117 = VoidSymbol - shift alt keycode 117 = VoidSymbol -keycode 118 = KP_MinPlus - altgr control keycode 118 = VoidSymbol - shift alt keycode 118 = VoidSymbol -keycode 119 = Pause -keycode 120 = -keycode 121 = -keycode 122 = -keycode 123 = -keycode 124 = -keycode 125 = -keycode 126 = -keycode 127 = -string F1 = "\033[[A" -string F2 = "\033[[B" -string F3 = "\033[[C" -string F4 = "\033[[D" -string F5 = "\033[[E" -string F6 = "\033[17~" -string F7 = "\033[18~" -string F8 = "\033[19~" -string F9 = "\033[20~" -string F10 = "\033[21~" -string F11 = "\033[23~" -string F12 = "\033[24~" -string F13 = "\033[25~" -string F14 = "\033[26~" -string F15 = "\033[28~" -string F16 = "\033[29~" -string F17 = "\033[31~" -string F18 = "\033[32~" -string F19 = "\033[33~" -string F20 = "\033[34~" -string Find = "\033[1~" -string Insert = "\033[2~" -string Remove = "\033[3~" -string Select = "\033[4~" -string Prior = "\033[5~" -string Next = "\033[6~" -string Macro = "\033[M" -string Pause = "\033[P" -compose '`' 'A' to 'À' -compose '`' 'a' to 'à' -compose '\'' 'A' to 'Á' -compose '\'' 'a' to 'á' -compose '^' 'A' to 'Â' -compose '^' 'a' to 'â' -compose '~' 'A' to 'Ã' -compose '~' 'a' to 'ã' -compose '"' 'A' to 'Ä' -compose '"' 'a' to 'ä' -compose 'O' 'A' to 'Å' -compose 'o' 'a' to 'å' -compose '0' 'A' to 'Å' -compose '0' 'a' to 'å' -compose 'A' 'A' to 'Å' -compose 'a' 'a' to 'å' -compose 'A' 'E' to 'Æ' -compose 'a' 'e' to 'æ' -compose ',' 'C' to 'Ç' -compose ',' 'c' to 'ç' -compose '`' 'E' to 'È' -compose '`' 'e' to 'è' -compose '\'' 'E' to 'É' -compose '\'' 'e' to 'é' -compose '^' 'E' to 'Ê' -compose '^' 'e' to 'ê' -compose '"' 'E' to 'Ë' -compose '"' 'e' to 'ë' -compose '`' 'I' to 'Ì' -compose '`' 'i' to 'ì' -compose '\'' 'I' to 'Í' -compose '\'' 'i' to 'í' -compose '^' 'I' to 'Î' -compose '^' 'i' to 'î' -compose '"' 'I' to 'Ï' -compose '"' 'i' to 'ï' -compose '-' 'D' to 'Ð' -compose '-' 'd' to 'ð' -compose '~' 'N' to 'Ñ' -compose '~' 'n' to 'ñ' -compose '`' 'O' to 'Ò' -compose '`' 'o' to 'ò' -compose '\'' 'O' to 'Ó' -compose '\'' 'o' to 'ó' -compose '^' 'O' to 'Ô' -compose '^' 'o' to 'ô' -compose '~' 'O' to 'Õ' -compose '~' 'o' to 'õ' -compose '"' 'O' to 'Ö' -compose '"' 'o' to 'ö' -compose '/' 'O' to 'Ø' -compose '/' 'o' to 'ø' -compose '`' 'U' to 'Ù' -compose '`' 'u' to 'ù' -compose '\'' 'U' to 'Ú' -compose '\'' 'u' to 'ú' -compose '^' 'U' to 'Û' -compose '^' 'u' to 'û' -compose '"' 'U' to 'Ü' -compose '"' 'u' to 'ü' -compose '\'' 'Y' to 'Ý' -compose '\'' 'y' to 'ý' -compose 'T' 'H' to 'Þ' -compose 't' 'h' to 'þ' -compose 's' 's' to 'ß' -compose '"' 'y' to 'ÿ' -compose 's' 'z' to 'ß' -compose 'i' 'j' to 'ÿ' diff --git a/drivers/usb/maps/usb.map b/drivers/usb/maps/usb.map deleted file mode 100644 index e05c71cd81a4..000000000000 --- a/drivers/usb/maps/usb.map +++ /dev/null @@ -1,233 +0,0 @@ -# USB kernel keymap. -keymaps 0-2,4-5,8,12 - -keycode 4 = a - altgr keycode 30 = Hex_A -keycode 5 = b - altgr keycode 48 = Hex_B -keycode 6 = c - altgr keycode 46 = Hex_C -keycode 7 = d - altgr keycode 32 = Hex_D -keycode 8 = e - altgr keycode 18 = Hex_E -keycode 9 = f - altgr keycode 33 = Hex_F -keycode 10 = g -keycode 11 = h -keycode 12 = i -keycode 13 = j -keycode 14 = k -keycode 15 = l -keycode 16 = m -keycode 17 = n -keycode 18 = o -keycode 19 = p -keycode 20 = q -keycode 21 = r -keycode 22 = s -keycode 23 = t -keycode 24 = u -keycode 25 = v -keycode 26 = w -keycode 27 = x -keycode 28 = y -keycode 29 = z -keycode 30 = one exclam - alt keycode 2 = Meta_one -keycode 31 = two at - control keycode 3 = nul - shift control keycode 3 = nul - alt keycode 3 = Meta_two -keycode 32 = three numbersign - control keycode 4 = Escape - alt keycode 4 = Meta_three -keycode 33 = four dollar - control keycode 5 = Control_backslash - alt keycode 5 = Meta_four -keycode 34 = five percent - control keycode 6 = Control_bracketright - alt keycode 6 = Meta_five -keycode 35 = six asciicircum - control keycode 7 = Control_asciicircum - alt keycode 7 = Meta_six -keycode 36 = seven ampersand - control keycode 8 = Control_underscore - alt keycode 8 = Meta_seven -keycode 37 = eight asterisk - control keycode 9 = Delete - alt keycode 9 = Meta_eight -keycode 38 = nine parenleft - alt keycode 10 = Meta_nine -keycode 39 = zero parenright - alt keycode 11 = Meta_zero -keycode 40 = Return - alt keycode 28 = Meta_Control_m -keycode 41 = Escape Escape - alt keycode 1 = Meta_Escape -keycode 42 = Delete Delete - control keycode 14 = BackSpace - alt keycode 14 = Meta_Delete -keycode 43 = Tab Tab - alt keycode 15 = Meta_Tab -keycode 44 = space space - control keycode 57 = nul - alt keycode 57 = Meta_space -keycode 45 = minus underscore backslash - control keycode 12 = Control_underscore - shift control keycode 12 = Control_underscore - alt keycode 12 = Meta_minus -keycode 46 = equal plus - alt keycode 13 = Meta_equal -keycode 47 = bracketleft braceleft - control keycode 26 = Escape - alt keycode 26 = Meta_bracketleft -keycode 48 = bracketright braceright asciitilde - control keycode 27 = Control_bracketright - alt keycode 27 = Meta_bracketright -keycode 49 = backslash bar - control keycode 43 = Control_backslash - alt keycode 43 = Meta_backslash -keycode 50 = -keycode 51 = semicolon colon - alt keycode 39 = Meta_semicolon -keycode 52 = apostrophe quotedbl - control keycode 40 = Control_g - alt keycode 40 = Meta_apostrophe -keycode 53 = grave asciitilde - control keycode 41 = nul - alt keycode 41 = Meta_grave -keycode 54 = comma less - alt keycode 51 = Meta_comma -keycode 55 = period greater - control keycode 52 = Compose - alt keycode 52 = Meta_period -keycode 56 = slash question - control keycode 53 = Delete - alt keycode 53 = Meta_slash -keycode 57 = Caps_Lock -keycode 58 = F1 F11 Console_13 - control keycode 59 = F1 - alt keycode 59 = Console_1 - control alt keycode 59 = Console_1 -keycode 59 = F2 F12 Console_14 - control keycode 60 = F2 - alt keycode 60 = Console_2 - control alt keycode 60 = Console_2 -keycode 60 = F3 F13 Console_15 - control keycode 61 = F3 - alt keycode 61 = Console_3 - control alt keycode 61 = Console_3 -keycode 61 = F4 F14 Console_16 - control keycode 62 = F4 - alt keycode 62 = Console_4 - control alt keycode 62 = Console_4 -keycode 62 = F5 F15 Console_17 - control keycode 63 = F5 - alt keycode 63 = Console_5 - control alt keycode 63 = Console_5 -keycode 63 = F6 F16 Console_18 - control keycode 64 = F6 - alt keycode 64 = Console_6 - control alt keycode 64 = Console_6 -keycode 64 = F7 F17 Console_19 - control keycode 65 = F7 - alt keycode 65 = Console_7 - control alt keycode 65 = Console_7 -keycode 65 = F8 F18 Console_20 - control keycode 66 = F8 - alt keycode 66 = Console_8 - control alt keycode 66 = Console_8 -keycode 66 = F9 F19 Console_21 - control keycode 67 = F9 - alt keycode 67 = Console_9 - control alt keycode 67 = Console_9 -keycode 67 = F10 F20 Console_22 - control keycode 68 = F10 - alt keycode 68 = Console_10 - control alt keycode 68 = Console_10 -keycode 68 = F11 F11 Console_23 - control keycode 87 = F11 - alt keycode 87 = Console_11 - control alt keycode 87 = Console_11 -keycode 69 = F12 F12 Console_24 - control keycode 88 = F12 - alt keycode 88 = Console_12 - control alt keycode 88 = Console_12 -keycode 70 = Print_Screen -keycode 71 = Scroll_Lock Show_Memory Show_Registers - control keycode 70 = Show_State - alt keycode 70 = Scroll_Lock -keycode 72 = Pause -keycode 73 = Insert -keycode 74 = Home -keycode 75 = Prior - shift keycode 104 = Scroll_Backward -keycode 76 = Remove -# altgr control keycode 111 = Boot - control alt keycode 111 = Boot -keycode 77 = End -keycode 78 = Next - shift keycode 109 = Scroll_Forward -keycode 79 = Right - alt keycode 106 = Incr_Console -keycode 80 = Left - alt keycode 105 = Decr_Console -keycode 81 = Down -keycode 82 = Up -keycode 83 = Num_Lock - shift keycode 69 = Bare_Num_Lock -keycode 84 = KP_Divide -keycode 85 = KP_Multiply -keycode 86 = KP_Subtract -keycode 87 = KP_Add -keycode 88 = KP_Enter -keycode 89 = KP_1 - alt keycode 79 = Ascii_1 - altgr keycode 79 = Hex_1 -keycode 90 = KP_2 - alt keycode 80 = Ascii_2 - altgr keycode 80 = Hex_2 -keycode 91 = KP_3 - alt keycode 81 = Ascii_3 - altgr keycode 81 = Hex_3 -keycode 92 = KP_4 - alt keycode 75 = Ascii_4 - altgr keycode 75 = Hex_4 -keycode 93 = KP_5 - alt keycode 76 = Ascii_5 - altgr keycode 76 = Hex_5 -keycode 94 = KP_6 - alt keycode 77 = Ascii_6 - altgr keycode 77 = Hex_6 -keycode 95 = KP_7 - alt keycode 71 = Ascii_7 - altgr keycode 71 = Hex_7 -keycode 96 = KP_8 - alt keycode 72 = Ascii_8 - altgr keycode 72 = Hex_8 -keycode 97 = KP_9 - alt keycode 73 = Ascii_9 - altgr keycode 73 = Hex_9 -keycode 98 = KP_0 - alt keycode 82 = Ascii_0 - altgr keycode 82 = Hex_0 -keycode 99 = KP_Period -# altgr control keycode 83 = Boot - control alt keycode 83 = Boot -keycode 100 = -keycode 101 = Application -keycode 102 = -keycode 103 = -keycode 104 = F13 -keycode 105 = F14 - -# modifiers -keycode 120 = Control -keycode 121 = Shift -keycode 122 = Alt -keycode 123 = Window -keycode 124 = Control_R -keycode 125 = Shift_R -keycode 126 = Alt_R -keycode 127 = Window_R diff --git a/drivers/usb/mdc800.c b/drivers/usb/mdc800.c new file mode 100644 index 000000000000..99b71b66cdcd --- /dev/null +++ b/drivers/usb/mdc800.c @@ -0,0 +1,935 @@ +/* + * copyright (C) 1999/2000 by Henning Zabel + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + + +/* + * USB-Kernel Driver for the Mustek MDC800 Digital Camera + * (c) 1999/2000 Henning Zabel + * + * + * The driver brings the USB functions of the MDC800 to Linux. + * To use the Camera you must support the USB Protocoll of the camera + * to the Kernel Node. + * The Driver uses a misc device Node. Create it with : + * mknod /dev/mustek c 180 32 + * + * The driver supports only one camera. + * + * version 0.7.3 + * bugfix : The mdc800->state field gets set to READY after the + * the diconnect function sets it to NOT_CONNECTED. This makes the + * driver running like the camera is connected and causes some + * hang ups. + * + * version 0.7.1 + * MOD_INC and MOD_DEC are changed in usb_probe to prevent load/unload + * problems when compiled as Module. + * (04/04/2000) + * + * The mdc800 driver gets assigned the USB Minor 32-47. The Registration + * was updated to use these values. + * (26/03/2000) + * + * The Init und Exit Module Function are updated. + * (01/03/2000) + * + * version 0.7.0 + * Rewrite of the driver : The driver now uses URB's. The old stuff + * has been removed. + * + * version 0.6.0 + * Rewrite of this driver: The Emulation of the rs232 protocoll + * has been removed from the driver. A special executeCommand function + * for this driver is included to gphoto. + * The driver supports two kind of communication to bulk endpoints. + * Either with the dev->bus->ops->bulk... or with callback function. + * (09/11/1999) + * + * version 0.5.0: + * first Version that gets a version number. Most of the needed + * functions work. + * (20/10/1999) + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define VERSION "0.7.3" +#define RELEASE_DATE "(24/04/2000)" + +/* Vendor and Product Information */ +#define MDC800_VENDOR_ID 0x055f +#define MDC800_PRODUCT_ID 0xa800 + +/* Timeouts (msec) */ +#define TO_DOWNLOAD_GET_READY 1500 +#define TO_DOWNLOAD_GET_BUSY 1500 +#define TO_WRITE_GET_READY 1000 +#define TO_DEFAULT_COMMAND 5000 +#define TO_READ_FROM_IRQ TO_DEFAULT_COMMAND +#define TO_GET_READY TO_DEFAULT_COMMAND + +/* Minor Number of the device (create with mknod /dev/mustek c 180 32) */ +#define MDC800_DEVICE_MINOR_BASE 32 + + +/************************************************************************** + Data and structs +***************************************************************************/ + + +typedef enum { + NOT_CONNECTED, READY, WORKING, DOWNLOAD +} mdc800_state; + + +/* Data for the driver */ +struct mdc800_data +{ + struct usb_device * dev; // Device Data + mdc800_state state; + + unsigned int endpoint [4]; + + purb_t irq_urb; + wait_queue_head_t irq_wait; + char* irq_urb_buffer; + + int camera_busy; // is camera busy ? + int camera_request_ready; // Status to synchronize with irq + char camera_response [8]; // last Bytes send after busy + + purb_t write_urb; + char* write_urb_buffer; + wait_queue_head_t write_wait; + + + purb_t download_urb; + char* download_urb_buffer; + wait_queue_head_t download_wait; + int download_left; // Bytes left to download ? + + + /* Device Data */ + char out [64]; // Answer Buffer + int out_ptr; // Index to the first not readen byte + int out_count; // Bytes in the buffer + + int open; // Camera device open ? + int rw_lock; // Block read <-> write + + char in [8]; // Command Input Buffer + int in_count; + + int pic_index; // Cache for the Imagesize (-1 for nothing cached ) + int pic_len; +}; + + +/* Specification of the Endpoints */ +static struct usb_endpoint_descriptor mdc800_ed [4] = +{ + { 0,0, 0x01, 0x02, 8, 0,0,0 }, + { 0,0, 0x82, 0x03, 8, 0,0,0 }, + { 0,0, 0x03, 0x02, 64, 0,0,0 }, + { 0,0, 0x84, 0x02, 64, 0,0,0 } +}; + + +/* The Variable used by the driver */ +static struct mdc800_data* mdc800=0; + + +/*************************************************************************** + The USB Part of the driver +****************************************************************************/ + +static int mdc800_endpoint_equals (struct usb_endpoint_descriptor *a,struct usb_endpoint_descriptor *b) +{ + return ( + ( a->bEndpointAddress == b->bEndpointAddress ) + && ( a->bmAttributes == b->bmAttributes ) + && ( a->wMaxPacketSize == b->wMaxPacketSize ) + ); +} + + +/* + * Checks wether the camera responds busy + */ +static int mdc800_isBusy (char* ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0x99) + return 0; + i++; + } + return 1; +} + + +/* + * Checks wether the Camera is ready + */ +static int mdc800_isReady (char *ch) +{ + int i=0; + while (i<8) + { + if (ch [i] != (char)0xbb) + return 0; + i++; + } + return 1; +} + + + +/* + * USB IRQ Handler for InputLine + */ +static void mdc800_usb_irq (struct urb *urb) +{ + int data_received=0, wake_up; + unsigned char* b=urb->transfer_buffer; + struct mdc800_data* mdc800=urb->context; + + if (urb->status >= 0) + { + + //dbg ("%i %i %i %i %i %i %i %i \n",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]); + + if (mdc800_isBusy (b)) + { + if (!mdc800->camera_busy) + { + mdc800->camera_busy=1; + dbg ("gets busy"); + } + } + else + { + if (mdc800->camera_busy && mdc800_isReady (b)) + { + mdc800->camera_busy=0; + dbg ("gets ready"); + } + } + if (!(mdc800_isBusy (b) || mdc800_isReady (b))) + { + /* Store Data in camera_answer field */ + dbg ("%i %i %i %i %i %i %i %i ",b[0],b[1],b[2],b[3],b[4],b[5],b[6],b[7]); + + memcpy (mdc800->camera_response,b,8); + data_received=1; + } + } + wake_up= ( mdc800->camera_request_ready > 0 ) + && + ( + ((mdc800->camera_request_ready == 1) && (!mdc800->camera_busy)) + || + ((mdc800->camera_request_ready == 2) && data_received) + || + ((mdc800->camera_request_ready == 3) && (mdc800->camera_busy)) + || + (urb->status < 0) + ); + + if (wake_up) + { + mdc800->camera_request_ready=0; + wake_up_interruptible (&mdc800->irq_wait); + } +} + + +/* + * Waits a while until the irq responds that camera is ready + * + * mode : 0: Wait for camera gets ready + * 1: Wait for receiving data + * 2: Wait for camera gets busy + * + * msec: Time to wait + */ +static int mdc800_usb_waitForIRQ (int mode, int msec) +{ + mdc800->camera_request_ready=1+mode; + + interruptible_sleep_on_timeout (&mdc800->irq_wait, msec*HZ/1000); + + if (mdc800->camera_request_ready>0) + { + mdc800->camera_request_ready=0; + err ("timeout waiting for camera."); + return -1; + } + + if (mdc800->state == NOT_CONNECTED) + { + warn ("Camera gets disconnected during waiting for irq."); + mdc800->camera_request_ready=0; + return -2; + } + + return 0; +} + + +/* + * The write_urb callback function + */ +static void mdc800_usb_write_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + + if (urb->status != 0) + { + err ("writing command fails (status=%i)", urb->status); + } + else + { + mdc800->state=READY; + } + wake_up_interruptible (&mdc800->write_wait); +} + + +/* + * The download_urb callback function + */ +static void mdc800_usb_download_notify (struct urb *urb) +{ + struct mdc800_data* mdc800=urb->context; + + if (urb->status == 0) + { + /* Fill output buffer with these data */ + memcpy (mdc800->out, urb->transfer_buffer, 64); + mdc800->out_count=64; + mdc800->out_ptr=0; + mdc800->download_left-=64; + if (mdc800->download_left == 0) + { + mdc800->state=READY; + } + } + else + { + err ("request bytes fails (status:%i)", urb->status); + } + wake_up_interruptible (&mdc800->download_wait); +} + + +/*************************************************************************** + Probing for the Camera + ***************************************************************************/ + +static struct usb_driver mdc800_usb_driver; + +/* + * Callback to search the Mustek MDC800 on the USB Bus + */ +static void* mdc800_usb_probe (struct usb_device *dev ,unsigned int ifnum ) +{ + int i,j; + struct usb_interface_descriptor *intf_desc; + int irq_interval=0; + + dbg ("(mdc800_usb_probe) called."); + + + if (dev->descriptor.idVendor != MDC800_VENDOR_ID) + return 0; + if (dev->descriptor.idProduct != MDC800_PRODUCT_ID) + return 0; + + if (mdc800->dev != 0) + { + warn ("only one Mustek MDC800 is supported."); + return 0; + } + + if (dev->descriptor.bNumConfigurations != 1) + { + err ("probe fails -> wrong Number of Configuration"); + return 0; + } + intf_desc=&dev->actconfig->interface[ifnum].altsetting[0]; + + if ( + ( intf_desc->bInterfaceClass != 0xff ) + || ( intf_desc->bInterfaceSubClass != 0 ) + || ( intf_desc->bInterfaceProtocol != 0 ) + || ( intf_desc->bNumEndpoints != 4) + ) + { + err ("probe fails -> wrong Interface"); + return 0; + } + + /* Check the Endpoints */ + for (i=0; i<4; i++) + { + mdc800->endpoint[i]=-1; + for (j=0; j<4; j++) + { + if (mdc800_endpoint_equals (&intf_desc->endpoint [j],&mdc800_ed [i])) + { + mdc800->endpoint[i]=intf_desc->endpoint [j].bEndpointAddress ; + if (i==1) + { + irq_interval=intf_desc->endpoint [j].bInterval; + } + + continue; + } + } + if (mdc800->endpoint[i] == -1) + { + err ("probe fails -> Wrong Endpoints."); + return 0; + } + } + + + usb_driver_claim_interface (&mdc800_usb_driver, &dev->actconfig->interface[ifnum], mdc800); + if (usb_set_interface (dev, ifnum, 0) < 0) + { + err ("MDC800 Configuration fails."); + return 0; + } + + info ("Found Mustek MDC800 on USB."); + + mdc800->dev=dev; + mdc800->state=READY; + mdc800->open=0; + mdc800->rw_lock=0; + + /* Setup URB Structs */ + FILL_INT_URB ( + mdc800->irq_urb, + mdc800->dev, + usb_rcvintpipe (mdc800->dev,mdc800->endpoint [1]), + mdc800->irq_urb_buffer, + 8, + mdc800_usb_irq, + mdc800, + irq_interval + ); + + FILL_BULK_URB ( + mdc800->write_urb, + mdc800->dev, + usb_sndbulkpipe (mdc800->dev, mdc800->endpoint[0]), + mdc800->write_urb_buffer, + 8, + mdc800_usb_write_notify, + mdc800 + ); + + FILL_BULK_URB ( + mdc800->download_urb, + mdc800->dev, + usb_rcvbulkpipe (mdc800->dev, mdc800->endpoint [3]), + mdc800->download_urb_buffer, + 64, + mdc800_usb_download_notify, + mdc800 + ); + + return mdc800; +} + + +/* + * Disconnect USB device (maybe the MDC800) + */ +static void mdc800_usb_disconnect (struct usb_device *dev,void* ptr) +{ + struct mdc800_data* mdc800=(struct mdc800_data*) ptr; + + dbg ("(mdc800_usb_disconnect) called"); + + if (mdc800->state == NOT_CONNECTED) + return; + + mdc800->state=NOT_CONNECTED; + + usb_unlink_urb (mdc800->irq_urb); + usb_unlink_urb (mdc800->write_urb); + usb_unlink_urb (mdc800->download_urb); + + usb_driver_release_interface (&mdc800_usb_driver, &dev->actconfig->interface[1]); + + mdc800->dev=0; + info ("Mustek MDC800 disconnected from USB."); +} + + +/*************************************************************************** + The Misc device Part (file_operations) +****************************************************************************/ + +/* + * This Function calc the Answersize for a command. + */ +static int mdc800_getAnswerSize (char command) +{ + switch ((unsigned char) command) + { + case 0x2a: + case 0x49: + case 0x51: + case 0x0d: + case 0x20: + case 0x07: + case 0x01: + case 0x25: + case 0x00: + return 8; + + case 0x05: + case 0x3e: + return mdc800->pic_len; + + case 0x09: + return 4096; + + default: + return 0; + } +} + + +/* + * Init the device: (1) alloc mem (2) Increase MOD Count .. + */ +static int mdc800_device_open (struct inode* inode, struct file *file) +{ + int retval=0; + + if (mdc800->state == NOT_CONNECTED) + { + return -EBUSY; + } + + if (mdc800->open) + { + return -EBUSY; + } + + mdc800->rw_lock=0; + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->pic_index=0; + mdc800->pic_len=-1; + mdc800->download_left=0; + + mdc800->camera_busy=0; + mdc800->camera_request_ready=0; + + retval=0; + if (usb_submit_urb (mdc800->irq_urb)) + { + err ("request USB irq fails (submit_retval=%i urb_status=%i).",retval, mdc800->irq_urb->status); + return -EIO; + } + + mdc800->open=1; + + dbg ("Mustek MDC800 device opened."); + return 0; +} + + +/* + * Close the Camera and release Memory + */ +static int mdc800_device_release (struct inode* inode, struct file *file) +{ + int retval=0; + dbg ("Mustek MDC800 device closed."); + + if (mdc800->open && (mdc800->state != NOT_CONNECTED)) + { + mdc800->open=0; + usb_unlink_urb (mdc800->irq_urb); + usb_unlink_urb (mdc800->write_urb); + usb_unlink_urb (mdc800->download_urb); + } + else + { + retval=-EIO; + } + + return retval; +} + + +/* + * The Device read callback Function + */ +static ssize_t mdc800_device_read (struct file *file, char *buf, size_t len, loff_t *pos) +{ + int left=len, sts=len; /* single transfer size */ + char* ptr=buf; + + if (mdc800->state == NOT_CONNECTED) + return -EBUSY; + + if (mdc800->state == WORKING) + { + warn ("Illegal State \"working\" reached during read ?!"); + return -EBUSY; + } + + if (!mdc800->open || mdc800->rw_lock) + return -EBUSY; + mdc800->rw_lock=1; + + while (left) + { + if (signal_pending (current)) { + mdc800->rw_lock=0; + return -EINTR; + } + + sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left; + + if (sts <= 0) + { + /* Too less Data in buffer */ + if (mdc800->state == DOWNLOAD) + { + mdc800->out_count=0; + mdc800->out_ptr=0; + + /* Download -> Request new bytes */ + if (usb_submit_urb (mdc800->download_urb)) + { + err ("Can't submit download urb (status=%i)",mdc800->download_urb->status); + mdc800->rw_lock=0; + return len-left; + } + interruptible_sleep_on_timeout (&mdc800->download_wait, TO_DOWNLOAD_GET_READY*HZ/1000); + if (mdc800->download_urb->status != 0) + { + err ("request download-bytes fails (status=%i)",mdc800->download_urb->status); + mdc800->rw_lock=0; + return len-left; + } + } + else + { + /* No more bytes -> that's an error*/ + mdc800->rw_lock=0; + return -EIO; + } + } + else + { + /* memcpy Bytes */ + memcpy (ptr, &mdc800->out [mdc800->out_ptr], sts); + ptr+=sts; + left-=sts; + mdc800->out_ptr+=sts; + } + } + + mdc800->rw_lock=0; + return len-left; +} + + +/* + * The Device write callback Function + * If a 8Byte Command is received, it will be send to the camera. + * After this the driver initiates the request for the answer or + * just waits until the camera becomes ready. + */ +static ssize_t mdc800_device_write (struct file *file, const char *buf, size_t len, loff_t *pos) +{ + int i=0; + + if (mdc800->state != READY) + return -EBUSY; + + if (!mdc800->open || mdc800->rw_lock) + return -EBUSY; + mdc800->rw_lock=1; + + while (irw_lock=0; + return -EINTR; + } + + /* check for command start */ + if (buf [i] == (char) 0x55) + { + mdc800->in_count=0; + mdc800->out_count=0; + mdc800->out_ptr=0; + mdc800->download_left=0; + } + + /* save command byte */ + if (mdc800->in_count < 8) + { + mdc800->in[mdc800->in_count]=buf[i]; + mdc800->in_count++; + } + else + { + err ("Command is to long !\n"); + mdc800->rw_lock=0; + return -EIO; + } + + /* Command Buffer full ? -> send it to camera */ + if (mdc800->in_count == 8) + { + int answersize; + + if (mdc800_usb_waitForIRQ (0,TO_GET_READY)) + { + err ("Camera didn't get ready.\n"); + mdc800->rw_lock=0; + return -EIO; + } + + answersize=mdc800_getAnswerSize (mdc800->in[1]); + + mdc800->state=WORKING; + memcpy (mdc800->write_urb->transfer_buffer, mdc800->in,8); + if (usb_submit_urb (mdc800->write_urb)) + { + err ("submitting write urb fails (status=%i)", mdc800->write_urb->status); + mdc800->rw_lock=0; + return -EIO; + } + interruptible_sleep_on_timeout (&mdc800->write_wait, TO_WRITE_GET_READY*HZ/1000); + if (mdc800->state == WORKING) + { + usb_unlink_urb (mdc800->write_urb); + mdc800->rw_lock=0; + return -EIO; + } + + switch ((unsigned char) mdc800->in[1]) + { + case 0x05: /* Download Image */ + case 0x3e: /* Take shot in Fine Mode (WCam Mode) */ + if (mdc800->pic_len < 0) + { + err ("call 0x07 before 0x05,0x3e"); + mdc800->state=READY; + mdc800->rw_lock=0; + return -EIO; + } + mdc800->pic_len=-1; + + case 0x09: /* Download Thumbnail */ + mdc800->download_left=answersize+64; + mdc800->state=DOWNLOAD; + mdc800_usb_waitForIRQ (0,TO_DOWNLOAD_GET_BUSY); + break; + + + default: + if (answersize) + { + + if (mdc800_usb_waitForIRQ (1,TO_READ_FROM_IRQ)) + { + err ("requesting answer from irq fails"); + mdc800->rw_lock=0; + return -EIO; + } + + /* Write dummy data, (this is ugly but part of the USB Protokoll */ + /* if you use endpoint 1 as bulk and not as irq */ + memcpy (mdc800->out, mdc800->camera_response,8); + + /* This is the interpreted answer */ + memcpy (&mdc800->out[8], mdc800->camera_response,8); + + mdc800->out_ptr=0; + mdc800->out_count=16; + + /* Cache the Imagesize, if command was getImageSize */ + if (mdc800->in [1] == (char) 0x07) + { + mdc800->pic_len=(int) 65536*(unsigned char) mdc800->camera_response[0]+256*(unsigned char) mdc800->camera_response[1]+(unsigned char) mdc800->camera_response[2]; + + dbg ("cached imagesize = %i",mdc800->pic_len); + } + + } + else + { + if (mdc800_usb_waitForIRQ (0,TO_DEFAULT_COMMAND)) + { + err ("Command Timeout."); + mdc800->rw_lock=0; + return -EIO; + } + } + mdc800->state=READY; + break; + } + } + i++; + } + mdc800->rw_lock=0; + return i; +} + + +/*************************************************************************** + Init and Cleanup this driver (Structs and types) +****************************************************************************/ + +/* File Operations of this drivers */ +static struct file_operations mdc800_device_ops = +{ + read: mdc800_device_read, + write: mdc800_device_write, + open: mdc800_device_open, + release: mdc800_device_release, +}; + + + +/* + * USB Driver Struct for this device + */ +static struct usb_driver mdc800_usb_driver = +{ + "mdc800", + mdc800_usb_probe, + mdc800_usb_disconnect, + { 0,0 }, + &mdc800_device_ops, + MDC800_DEVICE_MINOR_BASE +}; + + + +/************************************************************************ + Init and Cleanup this driver (Main Functions) +*************************************************************************/ + +#define try(A) if ((A) == 0) goto cleanup_on_fail; +#define try_free_mem(A) if (A != 0) { kfree (A); A=0; } +#define try_free_urb(A) if (A != 0) { usb_free_urb (A); A=0; } + +int __init usb_mdc800_init (void) +{ + /* Allocate Memory */ + try (mdc800=kmalloc (sizeof (struct mdc800_data), GFP_KERNEL)); + + mdc800->dev=0; + mdc800->open=0; + mdc800->state=NOT_CONNECTED; + memset(mdc800, 0, sizeof(struct mdc800_data)); + + init_waitqueue_head (&mdc800->irq_wait); + init_waitqueue_head (&mdc800->write_wait); + init_waitqueue_head (&mdc800->download_wait); + + try (mdc800->irq_urb_buffer=kmalloc (8, GFP_KERNEL)); + try (mdc800->write_urb_buffer=kmalloc (8, GFP_KERNEL)); + try (mdc800->download_urb_buffer=kmalloc (64, GFP_KERNEL)); + + try (mdc800->irq_urb=usb_alloc_urb (0)); + try (mdc800->download_urb=usb_alloc_urb (0)); + try (mdc800->write_urb=usb_alloc_urb (0)); + + /* Register the driver */ + if (usb_register (&mdc800_usb_driver) < 0) + goto cleanup_on_fail; + + info ("Mustek Digital Camera Driver " VERSION " (MDC800)"); + info (RELEASE_DATE " Henning Zabel "); + + return 0; + + /* Clean driver up, when something fails */ + +cleanup_on_fail: + + if (mdc800 != 0) + { + err ("can't alloc memory!"); + + try_free_mem (mdc800->download_urb_buffer); + try_free_mem (mdc800->write_urb_buffer); + try_free_mem (mdc800->irq_urb_buffer); + + try_free_urb (mdc800->write_urb); + try_free_urb (mdc800->download_urb); + try_free_urb (mdc800->irq_urb); + + kfree (mdc800); + } + mdc800=0; + return -1; +} + + +void __exit usb_mdc800_cleanup (void) +{ + usb_deregister (&mdc800_usb_driver); + + usb_free_urb (mdc800->irq_urb); + usb_free_urb (mdc800->download_urb); + usb_free_urb (mdc800->write_urb); + + kfree (mdc800->irq_urb_buffer); + kfree (mdc800->write_urb_buffer); + kfree (mdc800->download_urb_buffer); + + kfree (mdc800); + mdc800=0; +} + + +MODULE_AUTHOR ("Henning Zabel "); +MODULE_DESCRIPTION ("USB Driver for Mustek MDC800 Digital Camera"); + +module_init (usb_mdc800_init); +module_exit (usb_mdc800_cleanup); diff --git a/drivers/usb/microtek.c b/drivers/usb/microtek.c new file mode 100644 index 000000000000..f36398e55452 --- /dev/null +++ b/drivers/usb/microtek.c @@ -0,0 +1,1055 @@ +/* Driver for Microtek Scanmaker X6 USB scanner, and possibly others. + * + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * + * Parts shamelessly stolen from usb-storage and copyright by their + * authors. Thanks to Matt Dharm for giving us permission! + * + * This driver implements a SCSI host controller driver and a USB + * device driver. To avoid confusion, all the USB related stuff is + * prefixed by mts_usb_ and all the SCSI stuff by mts_scsi_. + * + * Microtek (www.microtek.com) did not release the specifications for + * their USB protocol to us, so we had to reverse engineer them. We + * don't know for which models they are valid. + * + * The X6 USB has three bulk endpoints, one output (0x1) down which + * commands and outgoing data are sent, and two input: 0x82 from which + * normal data is read from the scanner (in packets of maximum 32 + * bytes) and from which the status byte is read, and 0x83 from which + * the results of a scan (or preview) are read in up to 64 * 1024 byte + * chunks by the Windows driver. We don't know how much it is possible + * to read at a time from 0x83. + * + * It seems possible to read (with URB transfers) everything from 0x82 + * in one go, without bothering to read in 32 byte chunks. + * + * There seems to be an optimisation of a further READ implicit if + * you simply read from 0x83. + * + * Guessed protocol: + * + * Send raw SCSI command to EP 0x1 + * + * If there is data to receive: + * If the command was READ datatype=image: + * Read a lot of data from EP 0x83 + * Else: + * Read data from EP 0x82 + * Else: + * If there is data to transmit: + * Write it to EP 0x1 + * + * Read status byte from EP 0x82 + * + * References: + * + * The SCSI command set for the scanner is available from + * ftp://ftp.microtek.com/microtek/devpack/ + * + * Microtek NV sent us a more up to date version of the document. If + * you want it, just send mail. + * + * Status: + * + * This driver does not work properly yet. + * Untested with multiple scanners. + * Untested on SMP. + * Untested on UHCI. + * + * History: + * + * 20000417 starting history + * 20000417 fixed load oops + * 20000417 fixed unload oops + * 20000419 fixed READ IMAGE detection + * 20000424 started conversion to use URBs + * 20000502 handled short transfers as errors + * 20000513 rename and organisation of functions (john) + * 20000513 added IDs for all products supported by Windows driver (john) + * 20000514 Rewrote mts_scsi_queuecommand to use URBs (john) + * 20000514 Version 0.0.8j + * 20000514 Fix reporting of non-existant devices to SCSI layer (john) + * 20000514 Added MTS_DEBUG_INT (john) + * 20000514 Changed "usb-microtek" to "microtek" for consistency (john) + * 20000514 Stupid bug fixes (john) + * 20000514 Version 0.0.9j + * 20000515 Put transfer context and URB in mts_desc (john) + * 20000515 Added prelim turn off debugging support (john) + * 20000515 Version 0.0.10j + * 20000515 Fixed up URB allocation (clear URB on alloc) (john) + * 20000515 Version 0.0.11j + * 20000516 Removed unnecessary spinlock in mts_transfer_context (john) + * 20000516 Removed unnecessary up on instance lock in mts_remove_nolock (john) + * 20000516 Implemented (badly) scsi_abort (john) + * 20000516 Version 0.0.12j + * 20000517 Hopefully removed mts_remove_nolock quasideadlock (john) + * 20000517 Added mts_debug_dump to print ll USB info (john) + * 20000518 Tweaks and documentation updates (john) + * 20000518 Version 0.0.13j + * 20000518 Cleaned up abort handling (john) + * 20000523 Removed scsi_command and various scsi_..._resets (john) + * 20000523 Added unlink URB on scsi_abort, now OHCI supports it (john) + * 20000523 Fixed last tiresome compile warning (john) + * 20000523 Version 0.0.14j (though version 0.1 has come out?) + * 20000602 Added primitive reset + * 20000602 Version 0.2.0 + * 20000603 various cosmetic changes + * 20000603 Version 0.2.1 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "microtek.h" + +/* Constants */ + +#define MTS_ABORT_TIMEOUT HZ /*jiffies*/ + + +/* Should we do debugging? */ + +#define MTS_DO_DEBUG + + +/* USB layer driver interface */ + +static void *mts_usb_probe(struct usb_device *dev, unsigned int interface); +static void mts_usb_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver mts_usb_driver = { + "microtek", + mts_usb_probe, + mts_usb_disconnect, + { NULL, NULL } /* we need no fops. let gcc fill it */ +}; + + +/* Internal driver stuff */ + +#define MTS_VERSION "0.2.1" +#define MTS_NAME "microtek usb (rev " MTS_VERSION "): " + +#define MTS_WARNING(x...) \ + printk( KERN_WARNING MTS_NAME ## x ) +#define MTS_ERROR(x...) \ + printk( KERN_ERR MTS_NAME ## x ) +#define MTS_INT_ERROR(x...) \ + MTS_ERROR( ## x ) +#define MTS_MESSAGE(x...) \ + printk( KERN_INFO MTS_NAME ## x ) + +#if defined MTS_DO_DEBUG + +#define MTS_DEBUG(x...) \ + printk( KERN_DEBUG MTS_NAME ## x ) + +#define MTS_DEBUG_GOT_HERE() \ + MTS_DEBUG("got to %s:%d (%s)\n", __FILE__, (int)__LINE__, __PRETTY_FUNCTION__ ) +#define MTS_DEBUG_INT() \ + do { MTS_DEBUG_GOT_HERE(); \ + MTS_DEBUG("transfer = %x context = %x\n",(int)transfer,(int)context ); \ + MTS_DEBUG("status = %x data-length = %x sent = %x\n",(int)transfer->status,(int)context->data_length, (int)transfer->actual_length ); \ + mts_debug_dump(context->instance);\ + } while(0) +#else + +#define MTS_NUL_STATEMENT do { } while(0) + +#define MTS_DEBUG(x...) MTS_NUL_STATEMENT +#define MTS_DEBUG_GOT_HERE() MTS_NUL_STATEMENT +#define MTS_DEBUG_INT() MTS_NUL_STATEMENT + +#endif + + + +#define MTS_INT_INIT()\ + do {\ + context = (struct mts_transfer_context*)transfer->context; \ + if (atomic_read(&context->do_abort)) {\ + mts_transfer_cleanup(transfer);\ + return;\ + }\ + MTS_DEBUG_INT();\ + } while (0) + +static inline void mts_debug_dump(struct mts_desc* desc) { + MTS_DEBUG("desc at 0x%x: halted = %x%x, toggle = %x%x\n", + (int)desc,(int)desc->usb_dev->halted[1],(int)desc->usb_dev->halted[0], + (int)desc->usb_dev->toggle[1],(int)desc->usb_dev->toggle[0] + ); + MTS_DEBUG("ep_out=%x ep_response=%x ep_image=%x\n", + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_response), + usb_rcvbulkpipe(desc->usb_dev,desc->ep_image) + ); +} + + +static inline void mts_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: + MTS_DEBUG("can't decode command\n"); + goto out; + break; + } + MTS_DEBUG( "Command %s (%d bytes)\n", what, srb->cmd_len); + + out: + MTS_DEBUG( " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); +} + +static inline int mts_is_aborting(struct mts_desc* desc) { + return (atomic_read(&desc->context.do_abort)); +} + +static inline void mts_request_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + atomic_set(&desc->context.do_abort,1); +} + +static inline void mts_urb_abort(struct mts_desc* desc) { + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + if ( desc->urb.status == USB_ST_URB_PENDING ) { + usb_unlink_urb( &desc->urb ); + } +} + +static inline void mts_wait_abort(struct mts_desc* desc) +{ + mts_request_abort(desc); + + while( !atomic_read(&desc->lock.count) ) { +/* Is there a function to check if the semaphore is locked? */ + schedule_timeout( MTS_ABORT_TIMEOUT ); + MTS_DEBUG_GOT_HERE(); + mts_urb_abort(desc); + } + +} + + +static struct mts_desc * mts_list; /* list of active scanners */ +struct semaphore mts_list_semaphore; + +/* Internal list operations */ + +static +void mts_remove_nolock( struct mts_desc* to_remove ) +{ + MTS_DEBUG( "removing 0x%x from list\n", + (int)to_remove ); + + mts_wait_abort(to_remove); + + down( &to_remove->lock ); + + MTS_DEBUG_GOT_HERE(); + + if ( to_remove != mts_list ) { + MTS_DEBUG_GOT_HERE(); + if (to_remove->prev && to_remove->next) + to_remove->prev->next = to_remove->next; + } else { + MTS_DEBUG_GOT_HERE(); + mts_list = to_remove->next; + if (mts_list) { + MTS_DEBUG_GOT_HERE(); + mts_list->prev = 0; + } + } + + if ( to_remove->next ) { + MTS_DEBUG_GOT_HERE(); + to_remove->next->prev = to_remove->prev; + } + + MTS_DEBUG_GOT_HERE(); + scsi_unregister_module(MODULE_SCSI_HA, &(to_remove->ctempl)); + + kfree( to_remove ); +} + +static +void mts_add_nolock( struct mts_desc* to_add ) +{ + MTS_DEBUG( "adding 0x%x to list\n", (int)to_add ); + + to_add->prev = 0; + to_add->next = mts_list; + if ( mts_list ) { + mts_list->prev = to_add; + } + + mts_list = to_add; +} + + + + +/* SCSI driver interface */ + +/* scsi related functions - dummies for now mostly */ + +static int mts_scsi_release(struct Scsi_Host *psh) +{ + MTS_DEBUG_GOT_HERE(); + + return 0; +} + +static int mts_scsi_abort (Scsi_Cmnd *srb) +/* interrupt context (!) */ +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + + mts_request_abort(desc); + mts_urb_abort(desc); + + return SCSI_ABORT_PENDING; +} + +static int mts_scsi_host_reset (Scsi_Cmnd *srb) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + + MTS_DEBUG_GOT_HERE(); + mts_debug_dump(desc); + + usb_reset_device(desc->usb_dev); + return 0; /* RANT why here 0 and not SUCCESS */ +} + +/* the core of the scsi part */ + +/* faking a detection - which can't fail :-) */ + +static int mts_scsi_detect (struct SHT * sht) +{ + /* Whole function stolen from usb-storage */ + + struct mts_desc * desc = (struct mts_desc *)sht->proc_dir; + /* What a hideous hack! */ + + char local_name[48]; + + MTS_DEBUG_GOT_HERE(); + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "microtek-%d", desc->host_number); + sht->name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + /* FIXME: where is this freed ? */ + + if (!sht->name) { + MTS_ERROR( "unable to allocate memory for proc interface!!\n" ); + return 0; + } + + strcpy((char *)sht->name, local_name); + + sht->proc_dir = NULL; + + /* In host->hostdata we store a pointer to desc */ + desc->host = scsi_register(sht, sizeof(desc)); + desc->host->hostdata[0] = (unsigned long)desc; +/* FIXME: what if sizeof(void*) != sizeof(unsigned long)? */ + + return 1; +} + + + +/* Main entrypoint: SCSI commands are dispatched to here */ + + + +static +int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ); + +static void mts_transfer_cleanup( struct urb *transfer ); + + +inline static +void mts_int_submit_urb (struct urb* transfer, + int pipe, + void* data, + unsigned length, + mts_usb_urb_callback callback ) +/* Interrupt context! */ + +/* Holding transfer->context->lock! */ +{ + int res; + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + FILL_BULK_URB(transfer, + context->instance->usb_dev, + pipe, + data, + length, + callback, + context + ); + +/* transfer->transfer_flags = USB_DISABLE_SPD;*/ + transfer->transfer_flags = USB_ASYNC_UNLINK; + transfer->status = 0; + transfer->timeout = 100; + + res = usb_submit_urb( transfer ); + if ( res ) { + MTS_INT_ERROR( "could not submit URB! Error was %d\n",(int)res ); + context->srb->result = DID_ERROR << 16; + context->state = mts_con_error; + mts_transfer_cleanup(transfer); + } + return; +} + + +static void mts_transfer_cleanup( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context = (struct mts_transfer_context*)transfer->context; + + up( &context->instance->lock ); + if ( context->final_callback ) + context->final_callback(context->srb); + +} + +static void mts_transfer_done( struct urb *transfer ) +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->srb->result &= MTS_MAX_CHUNK_MASK; + context->srb->result |= (unsigned)context->status<<1; + + mts_transfer_cleanup(transfer); + + return; +} + + +static void mts_get_status( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + context->state = mts_con_status; + + mts_int_submit_urb(transfer, + usb_rcvbulkpipe(context->instance->usb_dev, + context->instance->ep_response), + &context->status, + 1, + mts_transfer_done ); + + + return; +} + +static void mts_data_done( struct urb* transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( context->data_length != transfer->actual_length ) { +#if 0 + context->srb->resid = context->data_length - transfer->actual_length; +#else + printk(KERN_WARNING "microtek.c: Don't know how to handle resid on 2.2 kernels\n"); +#endif + } else if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + } + + mts_get_status(transfer); + + return; +} + + +static void mts_command_done( struct urb *transfer ) +/* Interrupt context! */ +{ + struct mts_transfer_context* context; + + MTS_INT_INIT(); + + if ( transfer->status ) { + context->srb->result = DID_ERROR<<16; + mts_transfer_cleanup(transfer); + + return; + } + + if ( context->data ) { + context->state = mts_con_data; + mts_int_submit_urb(transfer, + context->data_pipe, + context->data, + context->data_length, + mts_data_done); + } else mts_get_status(transfer); + + return; +} + + + + static const u8 mts_read_image_sig[] = { 0x28, 00, 00, 00 }; + static const u8 mts_read_image_sig_len = 4; + static const unsigned char mts_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + +#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1) + +static void +mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc ) +{ + + int pipe; + + + MTS_DEBUG_GOT_HERE(); + + desc->context.instance = desc; + desc->context.srb = srb; + desc->context.state = mts_con_command; + atomic_set(&desc->context.do_abort,0); + + if ( !srb->bufflen ){ + desc->context.data = 0; + desc->context.data_length = 0; + return; + } else { + desc->context.data = srb->buffer; + desc->context.data_length = srb->bufflen; + } + + /* can't rely on srb->sc_data_direction */ + + /* Brutally ripped from usb-storage */ + + if ( !memcmp( srb->cmnd, mts_read_image_sig, mts_read_image_sig_len ) +) { pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_image); + MTS_DEBUG( "transfering from desc->ep_image == %d\n", + (int)desc->ep_image ); + } else if ( MTS_DIRECTION_IS_IN(srb->cmnd[0]) ) { + pipe = usb_rcvbulkpipe(desc->usb_dev,desc->ep_response); + MTS_DEBUG( "transfering from desc->ep_response == %d\n", + (int)desc->ep_response); + } else { + MTS_DEBUG("transfering to desc->ep_out == %d\n", + (int)desc->ep_out); + pipe = usb_sndbulkpipe(desc->usb_dev,desc->ep_out); + } + desc->context.data_pipe = pipe; +} + + +static +int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback ) +{ + struct mts_desc* desc = (struct mts_desc*)(srb->host->hostdata[0]); + int err = 0; + int res; + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + mts_debug_dump(desc); + + if ( srb->device->lun || srb->device->id || srb->device->channel ) { + + MTS_DEBUG("Command to LUN=%d ID=%d CHANNEL=%d from SCSI layer\n",(int)srb->device->lun,(int)srb->device->id, (int)srb->device->channel ); + + MTS_DEBUG("this device doesn't exist\n"); + + srb->result = DID_BAD_TARGET << 16; + + if(callback) + callback(srb); + + goto out; + } + + down(&desc->lock); + + MTS_DEBUG_GOT_HERE(); + mts_show_command(srb); + + + FILL_BULK_URB(&desc->urb, + desc->usb_dev, + usb_sndbulkpipe(desc->usb_dev,desc->ep_out), + srb->cmnd, + srb->cmd_len, + mts_command_done, + &desc->context + ); + + + mts_build_transfer_context( srb, desc ); + desc->context.final_callback = callback; + desc->urb.timeout = 100; + desc->urb.transfer_flags = USB_ASYNC_UNLINK; + +/* desc->urb.transfer_flags = USB_DISABLE_SPD;*/ + + res=usb_submit_urb(&desc->urb); + + if(res){ + MTS_ERROR("error %d submitting URB\n",(int)res); + srb->result = DID_ERROR << 16; + + if(callback) + callback(srb); + + goto out; + } + + MTS_DEBUG_GOT_HERE(); + + out: + return err; +} +/* + * this defines our 'host' + */ + +/* NOTE: This is taken from usb-storage, should be right. */ + + +static Scsi_Host_Template mts_scsi_host_template = { + name: "microtek", + detect: mts_scsi_detect, + release: mts_scsi_release, + command: 0, + queuecommand: mts_scsi_queuecommand, + + eh_abort_handler: mts_scsi_abort, + eh_device_reset_handler:0, + eh_bus_reset_handler: 0, + eh_host_reset_handler: mts_scsi_host_reset, + + can_queue: 1, + this_id: -1, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: FALSE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + + +/* USB layer driver interface implementation */ + +static void mts_usb_disconnect (struct usb_device *dev, void *ptr) +{ + struct mts_desc* to_remove = (struct mts_desc*)ptr; + + MTS_DEBUG_GOT_HERE(); + + /* leave the list - lock it */ + down(&mts_list_semaphore); + + mts_remove_nolock(to_remove); + + up(&mts_list_semaphore); +} + +struct vendor_product +{ + u16 idVendor; + u16 idProduct; + char* name; + enum + { + mts_sup_unknown=0, + mts_sup_alpha, + mts_sup_full + } + support_status; +} ; + + +/* These are taken from the msmUSB.inf file on the Windows driver CD */ +const static struct vendor_product mts_supported_products[] = +{ + { + 0x4ce, 0x300,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x94,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0x99,"Scanmaker X6",mts_sup_alpha + }, + { + 0x5da, 0x9a,"Phantom C6",mts_sup_unknown + }, + { + 0x5da, 0xa0,"Phantom 336CX",mts_sup_unknown + }, + { + 0x5da, 0xa3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80a3,"ScanMaker V6USL",mts_sup_unknown + }, + { + 0x5da, 0x80ac,"Scanmaker V6UL",mts_sup_unknown + } +} +; +const static struct vendor_product* mts_last_product = &mts_supported_products[ sizeof(mts_supported_products) / sizeof(struct vendor_product) ]; + /* Must never be derefed, points to one after last entry */ + + +static void * mts_usb_probe (struct usb_device *dev, unsigned int interface) +{ + int i; + int result; + int ep_out = -1; + int ep_in_set[3]; /* this will break if we have more than three endpoints + which is why we check */ + int *ep_in_current = ep_in_set; + + struct mts_desc * new_desc; + struct vendor_product const* p; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting; + + MTS_DEBUG_GOT_HERE(); + MTS_DEBUG( "usb-device descriptor at %x\n", (int)dev ); + + MTS_DEBUG( "product id = 0x%x, vendor id = 0x%x\n", + (int)dev->descriptor.idProduct, + (int)dev->descriptor.idVendor ); + + MTS_DEBUG_GOT_HERE(); + + /* checking IDs */ + for( p = mts_supported_products; p != mts_last_product; p++ ) + if ( dev->descriptor.idVendor == p->idVendor && + dev->descriptor.idProduct == p->idProduct ) + goto is_supported; + else + MTS_DEBUG( "doesn't appear to be model %s\n", p->name ); + + MTS_DEBUG( "returning NULL: unsupported\n" ); + + return NULL; + + is_supported: + + MTS_DEBUG_GOT_HERE(); + + MTS_DEBUG( "found model %s\n", p->name ); + if ( p->support_status != mts_sup_full ) + MTS_MESSAGE( "model %s is not known to be fully supported, reports welcome!\n", + p->name ); + + /* the altsettting 0 on the interface we're probing */ + altsetting = + &(dev->actconfig->interface[interface].altsetting[0]); + + + /* Check if the config is sane */ + + if ( altsetting->bNumEndpoints != MTS_EP_TOTAL ) { + MTS_WARNING( "expecting %d got %d endpoints! Bailing out.\n", + (int)MTS_EP_TOTAL, (int)altsetting->bNumEndpoints ); + return NULL; + } + + for( i = 0; i < altsetting->bNumEndpoints; i++ ) { + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK) { + + MTS_WARNING( "can only deal with bulk endpoints; endpoint %d is not bulk.\n", + (int)altsetting->endpoint[i].bEndpointAddress ); + } else { + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + *ep_in_current++ + = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + else { + if ( ep_out != -1 ) { + MTS_WARNING( "can only deal with one output endpoints. Bailing out." ); + return NULL; + } + + ep_out = altsetting->endpoint[i].bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + } + } + + } + + + if ( ep_out == -1 ) { + MTS_WARNING( "couldn't find an output bulk endpoint. Bailing out.\n" ); + return NULL; + } + + + /* I don't understand the following fully (it's from usb-storage) -- John */ + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + + MTS_DEBUG("usb_set_interface returned %d.\n",result); + switch( result ) + { + case 0: /* no error */ + break; + + case -EPIPE: + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + MTS_DEBUG( "clearing clearing stall on control interface\n" ); + break; + + default: + MTS_DEBUG( "unknown error %d from usb_set_interface\n", + (int)result ); + return NULL; + } + + + /* allocating a new descriptor */ + new_desc = (struct mts_desc *)kmalloc(sizeof(struct mts_desc), GFP_KERNEL); + if (new_desc == NULL) + { + MTS_ERROR("couldn't allocate scanner desc, bailing out!\n"); + return NULL; + } + + /* As done by usb_alloc_urb */ + memset( new_desc, 0, sizeof(*new_desc) ); + spin_lock_init(&new_desc->urb.lock); + + + /* initialising that descriptor */ + new_desc->usb_dev = dev; + new_desc->interface = interface; + + init_MUTEX(&new_desc->lock); + + if(mts_list){ + new_desc->host_number = mts_list->host_number+1; + } else { + new_desc->host_number = 0; + } + + /* endpoints */ + + new_desc->ep_out = ep_out; + new_desc->ep_response = ep_in_set[0]; + new_desc->ep_image = ep_in_set[1]; + + + if ( new_desc->ep_out != MTS_EP_OUT ) + MTS_WARNING( "will this work? Command EP is not usually %d\n", + (int)new_desc->ep_out ); + + if ( new_desc->ep_response != MTS_EP_RESPONSE ) + MTS_WARNING( "will this work? Response EP is not usually %d\n", + (int)new_desc->ep_response ); + + if ( new_desc->ep_image != MTS_EP_IMAGE ) + MTS_WARNING( "will this work? Image data EP is not usually %d\n", + (int)new_desc->ep_image ); + + + /* Initialize the host template based on the default one */ + memcpy(&(new_desc->ctempl), &mts_scsi_host_template, sizeof(mts_scsi_host_template)); + /* HACK from usb-storage - this is needed for scsi detection */ + (struct mts_desc *)new_desc->ctempl.proc_dir = new_desc; /* FIXME */ + + MTS_DEBUG("registering SCSI module\n"); + + new_desc->ctempl.module = THIS_MODULE; + result = scsi_register_module(MODULE_SCSI_HA, &(new_desc->ctempl)); + /* Will get hit back in microtek_detect by this func */ + if ( result ) + { + MTS_ERROR( "error %d from scsi_register_module! Help!\n", + (int)result ); + + /* FIXME: need more cleanup? */ + kfree( new_desc ); + return NULL; + } + MTS_DEBUG_GOT_HERE(); + + /* FIXME: the bomb is armed, must the host be registered under lock ? */ + /* join the list - lock it */ + down(&mts_list_semaphore); + + mts_add_nolock( new_desc ); + + up(&mts_list_semaphore); + + + MTS_DEBUG("completed probe and exiting happily\n"); + + return (void *)new_desc; +} + + + +/* get us noticed by the rest of the kernel */ + +int __init microtek_drv_init(void) +{ + int result; + + MTS_DEBUG_GOT_HERE(); + init_MUTEX(&mts_list_semaphore); + + if ((result = usb_register(&mts_usb_driver)) < 0) { + MTS_DEBUG("usb_register returned %d\n", result ); + return -1; + } else { + MTS_DEBUG("driver registered.\n"); + } + + return 0; +} + +void __exit microtek_drv_exit(void) +{ + struct mts_desc* next; + + MTS_DEBUG_GOT_HERE(); + + usb_deregister(&mts_usb_driver); + + down(&mts_list_semaphore); + + while (mts_list) { + /* keep track of where the next one is */ + next = mts_list->next; + + mts_remove_nolock( mts_list ); + + /* advance the list pointer */ + mts_list = next; + } + + up(&mts_list_semaphore); +} + +module_init(microtek_drv_init); +module_exit(microtek_drv_exit); diff --git a/drivers/usb/microtek.h b/drivers/usb/microtek.h new file mode 100644 index 000000000000..939234560bb9 --- /dev/null +++ b/drivers/usb/microtek.h @@ -0,0 +1,73 @@ + /* + * Driver for Microtek Scanmaker X6 USB scanner and possibly others. + * + * (C) Copyright 2000 John Fremlin + * (C) Copyright 2000 Oliver Neukum + * + * See microtek.c for history + * + */ + +typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *); +typedef void (*mts_usb_urb_callback) (struct urb *); + + +struct mts_transfer_context +{ + struct mts_desc* instance; + mts_scsi_cmnd_callback final_callback; + Scsi_Cmnd *srb; + + void* data; + unsigned data_length; + int data_pipe; + + enum { + mts_con_none, + mts_con_command, + mts_con_data, + mts_con_status, + mts_con_error, + mts_con_done + } + state; + + atomic_t do_abort; /* when != 0 URB completion routines will + return straightaway */ + + u8 status; /* status returned from ep_response after command completion */ +}; + + +struct mts_desc { + struct mts_desc *next; + struct mts_desc *prev; + + struct usb_device *usb_dev; + + int interface; + + /* Endpoint addresses */ + u8 ep_out; + u8 ep_response; + u8 ep_image; + + struct Scsi_Host * host; + Scsi_Host_Template ctempl; + int host_number; + + struct semaphore lock; + + struct urb urb; + struct mts_transfer_context context; +}; + + +#define MTS_EP_OUT 0x1 +#define MTS_EP_RESPONSE 0x2 +#define MTS_EP_IMAGE 0x3 +#define MTS_EP_TOTAL 0x3 + +#define MTS_MAX_CHUNK_MASK ~0x3fu +/*maximum amount the scanner will transmit at once */ + diff --git a/drivers/usb/mkmap b/drivers/usb/mkmap deleted file mode 100644 index 35808f227cca..000000000000 --- a/drivers/usb/mkmap +++ /dev/null @@ -1,83 +0,0 @@ -#!/usr/bin/perl - -($ME = $0) =~ s|.*/||; - -$file = "maps/serial.map"; -$line = 1; -open(PC, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*(\S+)/) - { - my($idx) = int($1); - my($sym) = $2; - if(defined($map{uc($sym)})) - { - # print STDERR "$file:$line: warning: `$sym' redefined\n"; - } - $map{uc($sym)} = $idx; - } - $line++; -} -close(PC); - -$file = "maps/fixup.map"; -$line = 1; -open(FIXUP, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*/) - { - my($idx) = int($1); - for $sym (split(/\s+/, $')) - { - $map{uc($sym)} = $idx; - } - } - $line++; -} -close(FIXUP); - -$file = "maps/usb.map"; -$line = 1; -open(USB, $file) || die("$!"); -while() -{ - if(/^\s*keycode\s+(\d+)\s*=\s*/) - { - my($idx) = int($1); - for $sym (split(/\s+/, $')) - { - my($val) = $map{uc($sym)}; - $map[$idx] = $val; - if(!defined($val)) - { - print STDERR "$file:$line: warning: `$sym' undefined\n"; - } - else - { - last; - } - } - } - $line++; -} -close(USB); - -print "unsigned char usb_kbd_map[256] = \n{\n"; -for($x = 0; $x < 32; $x++) -{ - if($x && !($x % 2)) - { - print "\n"; - } - print " "; - for($y = 0; $y < 8; $y++) - { - my($idx) = $x * 8 + $y; - print sprintf(" 0x%02x,", - int(defined($map[$idx]) ? $map[$idx]:0)); - } - print "\n"; -} -print "};\n"; diff --git a/drivers/usb/mouse.c b/drivers/usb/mouse.c deleted file mode 100644 index 4d346c41ac98..000000000000 --- a/drivers/usb/mouse.c +++ /dev/null @@ -1,293 +0,0 @@ -/* - * USB HID boot protocol mouse support based on MS BusMouse driver, psaux - * driver, and Linus's skeleton USB mouse driver. Fixed up a lot by Linus. - * - * Brad Keryan 4/3/1999 - * - * version 0.20: Linus rewrote read_mouse() to do PS/2 and do it - * correctly. Events are added together, not queued, to keep the rodent sober. - * - * version 0.02: Hmm, the mouse seems drunk because I'm queueing the events. - * This is wrong: when an application (like X or gpm) reads the mouse device, - * it wants to find out the mouse's current position, not its recent history. - * The button thing turned out to be UHCI not flipping data toggle, so half the - * packets were thrown out. - * - * version 0.01: Switched over to busmouse protocol, and changed the minor - * number to 32 (same as uusbd's hidbp driver). Buttons work more sanely now, - * but it still doesn't generate button events unless you move the mouse. - * - * version 0.0: Driver emulates a PS/2 mouse, stealing /dev/psaux (sorry, I - * know that's not very nice). Moving in the X and Y axes works. Buttons don't - * work right yet: X sees a lot of MotionNotify/ButtonPress/ButtonRelease - * combos when you hold down a button and drag the mouse around. Probably has - * some additional bugs on an SMP machine. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "usb.h" - -#define USB_MOUSE_MINOR 32 - -struct mouse_state { - unsigned char buttons; /* current button state */ - long dx; /* dx, dy, dz are change since last read */ - long dy; - long dz; - int present; /* this mouse is plugged in */ - int active; /* someone is has this mouse's device open */ - int ready; /* the mouse has changed state since the last read */ - struct wait_queue *wait; /* for polling */ - struct fasync_struct *fasync; - /* later, add a list here to support multiple mice */ - /* but we will also need a list of file pointers to identify it */ -}; - -static struct mouse_state static_mouse_state; - -spinlock_t usb_mouse_lock = SPIN_LOCK_UNLOCKED; - -static int mouse_irq(int state, void *__buffer, void *dev_id) -{ - signed char *data = __buffer; - /* finding the mouse is easy when there's only one */ - struct mouse_state *mouse = &static_mouse_state; - - /* if a mouse moves with no one listening, do we care? no */ - if(!mouse->active) - return 1; - - /* if the USB mouse sends an interrupt, then something noteworthy - must have happened */ - mouse->buttons = data[0] & 0x07; - mouse->dx += data[1]; /* data[] is signed, so this works */ - mouse->dy -= data[2]; /* y-axis is reversed */ - mouse->dz += data[3]; - mouse->ready = 1; - - add_mouse_randomness((mouse->buttons << 24) + (mouse->dz << 16 ) + - (mouse->dy << 8) + mouse->dx); - - wake_up_interruptible(&mouse->wait); - if (mouse->fasync) - kill_fasync(mouse->fasync, SIGIO); - - return 1; -} - -static int fasync_mouse(int fd, struct file *filp, int on) -{ - int retval; - struct mouse_state *mouse = &static_mouse_state; - - retval = fasync_helper(fd, filp, on, &mouse->fasync); - if (retval < 0) - return retval; - return 0; -} - -static int release_mouse(struct inode * inode, struct file * file) -{ - struct mouse_state *mouse = &static_mouse_state; - - fasync_mouse(-1, file, 0); - if (--mouse->active) - return 0; - return 0; -} - -static int open_mouse(struct inode * inode, struct file * file) -{ - struct mouse_state *mouse = &static_mouse_state; - - if (!mouse->present) - return -EINVAL; - if (mouse->active++) - return 0; - /* flush state */ - mouse->buttons = mouse->dx = mouse->dy = mouse->dz = 0; - return 0; -} - -static ssize_t write_mouse(struct file * file, - const char * buffer, size_t count, loff_t *ppos) -{ - return -EINVAL; -} - -/* - * Look like a PS/2 mouse, please.. - * - * The PS/2 protocol is fairly strange, but - * oh, well, it's at least common.. - */ -static ssize_t read_mouse(struct file * file, char * buffer, size_t count, loff_t *ppos) -{ - int retval = 0; - static int state = 0; - struct mouse_state *mouse = &static_mouse_state; - - if (count) { - mouse->ready = 0; - switch (state) { - case 0: { /* buttons and sign */ - int buttons = mouse->buttons; - mouse->buttons = 0; - if (mouse->dx < 0) - buttons |= 0x10; - if (mouse->dy < 0) - buttons |= 0x20; - put_user(buttons, buffer); - buffer++; - retval++; - state = 1; - if (!--count) - break; - } - case 1: { /* dx */ - int dx = mouse->dx; - mouse->dx = 0; - put_user(dx, buffer); - buffer++; - retval++; - state = 2; - if (!--count) - break; - } - case 2: { /* dy */ - int dy = mouse->dy; - mouse->dy = 0; - put_user(dy, buffer); - buffer++; - retval++; - state = 0; - } - break; - } - } - return retval; -} - -static unsigned int mouse_poll(struct file *file, poll_table * wait) -{ - struct mouse_state *mouse = &static_mouse_state; - - poll_wait(file, &mouse->wait, wait); - if (mouse->ready) - return POLLIN | POLLRDNORM; - return 0; -} - -struct file_operations usb_mouse_fops = { - NULL, /* mouse_seek */ - read_mouse, - write_mouse, - NULL, /* mouse_readdir */ - mouse_poll, /* mouse_poll */ - NULL, /* mouse_ioctl */ - NULL, /* mouse_mmap */ - open_mouse, - NULL, /* flush */ - release_mouse, - NULL, - fasync_mouse, -}; - -static struct miscdevice usb_mouse = { - USB_MOUSE_MINOR, "USB mouse", &usb_mouse_fops -}; - -static int mouse_probe(struct usb_device *dev) -{ - struct usb_interface_descriptor *interface; - struct usb_endpoint_descriptor *endpoint; - struct mouse_state *mouse = &static_mouse_state; - - /* We don't handle multi-config mice */ - if (dev->descriptor.bNumConfigurations != 1) - return -1; - - /* We don't handle multi-interface mice */ - if (dev->config[0].bNumInterfaces != 1) - return -1; - - /* Is it a mouse interface? */ - interface = &dev->config[0].interface[0]; - if (interface->bInterfaceClass != 3) - return -1; - if (interface->bInterfaceSubClass != 1) - return -1; - if (interface->bInterfaceProtocol != 2) - return -1; - - /* Multiple endpoints? What kind of mutant ninja-mouse is this? */ - if (interface->bNumEndpoints != 1) - return -1; - - endpoint = &interface->endpoint[0]; - - /* Output endpoint? Curiousier and curiousier.. */ - if (!(endpoint->bEndpointAddress & 0x80)) - return -1; - - /* If it's not an interrupt endpoint, we'd better punt! */ - if ((endpoint->bmAttributes & 3) != 3) - return -1; - - printk("USB mouse found\n"); - - usb_set_configuration(dev, dev->config[0].bConfigurationValue); - - usb_request_irq(dev, usb_rcvctrlpipe(dev, endpoint->bEndpointAddress), mouse_irq, endpoint->bInterval, NULL); - - mouse->present = 1; - return 0; -} - -static void mouse_disconnect(struct usb_device *dev) -{ - struct mouse_state *mouse = &static_mouse_state; - - /* this might need work */ - mouse->present = 0; -} - -static struct usb_driver mouse_driver = { - "mouse", - mouse_probe, - mouse_disconnect, - { NULL, NULL } -}; - -int usb_mouse_init(void) -{ - struct mouse_state *mouse = &static_mouse_state; - - misc_register(&usb_mouse); - - mouse->present = mouse->active = 0; - mouse->wait = NULL; - mouse->fasync = NULL; - - usb_register(&mouse_driver); - printk(KERN_INFO "USB HID boot protocol mouse registered.\n"); - return 0; -} - -void usb_mouse_cleanup(void) -{ - /* this, too, probably needs work */ - usb_deregister(&mouse_driver); - misc_deregister(&usb_mouse); -} diff --git a/drivers/usb/mousedev.c b/drivers/usb/mousedev.c new file mode 100644 index 000000000000..5009fa2d35b8 --- /dev/null +++ b/drivers/usb/mousedev.c @@ -0,0 +1,468 @@ +/* + * $Id: mousedev.c,v 1.8 2000/05/28 17:31:36 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Input driver to PS/2 or ImPS/2 device driver module. + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#define MOUSEDEV_MINOR_BASE 32 +#define MOUSEDEV_MINORS 32 +#define MOUSEDEV_MIX 31 + +#include +#include +#include +#include +#include +#include +#include + +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_X +#define CONFIG_INPUT_MOUSEDEV_SCREEN_X 1024 +#endif +#ifndef CONFIG_INPUT_MOUSEDEV_SCREEN_Y +#define CONFIG_INPUT_MOUSEDEV_SCREEN_Y 768 +#endif + +struct mousedev { + int used; + int open; + int minor; + wait_queue_head_t wait; + struct mousedev_list *list; + struct input_handle handle; + devfs_handle_t devfs; +}; + +struct mousedev_list { + struct fasync_struct *fasync; + struct mousedev *mousedev; + struct mousedev_list *next; + int dx, dy, dz, oldx, oldy; + char ps2[6]; + unsigned long buttons; + unsigned char ready, buffer, bufsiz; + unsigned char mode, genseq, impseq; +}; + +#define MOUSEDEV_GENIUS_LEN 5 +#define MOUSEDEV_IMPS_LEN 6 + +static unsigned char mousedev_genius_seq[] = { 0xe8, 3, 0xe6, 0xe6, 0xe6 }; +static unsigned char mousedev_imps_seq[] = { 0xf3, 200, 0xf3, 100, 0xf3, 80 }; + +static struct input_handler mousedev_handler; + +static struct mousedev *mousedev_table[MOUSEDEV_MINORS]; +static struct mousedev mousedev_mix; + +static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value) +{ + struct mousedev *mousedevs[3] = { handle->private, &mousedev_mix, NULL }; + struct mousedev **mousedev = mousedevs; + struct mousedev_list *list; + int index, size; + + while (*mousedev) { + list = (*mousedev)->list; + while (list) { + switch (type) { + case EV_ABS: + switch (code) { + case ABS_X: + size = handle->dev->absmax[ABS_X] - handle->dev->absmin[ABS_X]; + list->dx += (value * CONFIG_INPUT_MOUSEDEV_SCREEN_X - list->oldx) / size; + list->oldx += list->dx * size; + break; + case ABS_Y: + size = handle->dev->absmax[ABS_Y] - handle->dev->absmin[ABS_Y]; + list->dy -= (value * CONFIG_INPUT_MOUSEDEV_SCREEN_Y - list->oldy) / size; + list->oldy -= list->dy * size; + break; + } + break; + case EV_REL: + switch (code) { + case REL_X: list->dx += value; break; + case REL_Y: list->dy -= value; break; + case REL_WHEEL: if (list->mode) list->dz -= value; break; + } + break; + + case EV_KEY: + switch (code) { + case BTN_0: + case BTN_TOUCH: + case BTN_LEFT: index = 0; break; + case BTN_4: + case BTN_EXTRA: if (list->mode > 1) { index = 4; break; } + case BTN_STYLUS: + case BTN_1: + case BTN_RIGHT: index = 1; break; + case BTN_3: + case BTN_SIDE: if (list->mode > 1) { index = 3; break; } + case BTN_2: + case BTN_STYLUS2: + case BTN_MIDDLE: index = 2; break; + default: return; + } + switch (value) { + case 0: clear_bit(index, &list->buttons); break; + case 1: set_bit(index, &list->buttons); break; + case 2: return; + } + break; + } + + list->ready = 1; + + kill_fasync(list->fasync, SIGIO); + + list = list->next; + } + + wake_up_interruptible(&((*mousedev)->wait)); + mousedev++; + } +} + +static int mousedev_fasync(int fd, struct file *file, int on) +{ + int retval; + struct mousedev_list *list = file->private_data; + retval = fasync_helper(fd, file, on, &list->fasync); + return retval < 0 ? retval : 0; +} + +static int mousedev_release(struct inode * inode, struct file * file) +{ + struct mousedev_list *list = file->private_data; + struct mousedev_list **listptr = &list->mousedev->list; + + mousedev_fasync(-1, file, 0); + + while (*listptr && (*listptr != list)) + listptr = &((*listptr)->next); + *listptr = (*listptr)->next; + + if (!--list->mousedev->open) { + if (list->mousedev->minor == MOUSEDEV_MIX) { + struct input_handle *handle = mousedev_handler.handle; + while (handle) { + struct mousedev *mousedev = handle->private; + if (!mousedev->open) + input_close_device(handle); + handle = handle->hnext; + } + } else { + if (!mousedev_mix.open) + input_close_device(&list->mousedev->handle); + } + } + + if (!--list->mousedev->used) { + input_unregister_minor(list->mousedev->devfs); + mousedev_table[list->mousedev->minor] = NULL; + kfree(list->mousedev); + } + + kfree(list); + + return 0; +} + +static int mousedev_open(struct inode * inode, struct file * file) +{ + struct mousedev_list *list; + int i = MINOR(inode->i_rdev) - MOUSEDEV_MINOR_BASE; + + if (i > MOUSEDEV_MINORS || !mousedev_table[i]) + return -ENODEV; + + if (!(list = kmalloc(sizeof(struct mousedev_list), GFP_KERNEL))) + return -ENOMEM; + memset(list, 0, sizeof(struct mousedev_list)); + + list->mousedev = mousedev_table[i]; + list->next = mousedev_table[i]->list; + mousedev_table[i]->list = list; + file->private_data = list; + + list->mousedev->used++; + + if (!list->mousedev->open++) { + if (list->mousedev->minor == MOUSEDEV_MIX) { + struct input_handle *handle = mousedev_handler.handle; + while (handle) { + struct mousedev *mousedev = handle->private; + if (!mousedev->open) + input_open_device(handle); + handle = handle->hnext; + } + } else { + if (!mousedev_mix.open) + input_open_device(&list->mousedev->handle); + } + } + + return 0; +} + +static void mousedev_packet(struct mousedev_list *list, unsigned char off) +{ + list->ps2[off] = 0x08 | ((list->dx < 0) << 4) | ((list->dy < 0) << 5) | (list->buttons & 0x07); + list->ps2[off + 1] = (list->dx > 127 ? 127 : (list->dx < -127 ? -127 : list->dx)); + list->ps2[off + 2] = (list->dy > 127 ? 127 : (list->dy < -127 ? -127 : list->dy)); + list->dx -= list->ps2[off + 1]; + list->dy -= list->ps2[off + 2]; + list->bufsiz = off + 3; + + if (list->mode > 1) + list->ps2[off] |= ((list->buttons & 0x30) << 2); + + if (list->mode) { + list->ps2[off + 3] = (list->dz > 127 ? 127 : (list->dz < -127 ? -127 : list->dz)); + list->bufsiz++; + list->dz -= list->ps2[off + 3]; + } + if (!list->dx && !list->dy && (!list->mode || !list->dz)) list->ready = 0; + list->buffer = list->bufsiz; +} + + +static ssize_t mousedev_write(struct file * file, const char * buffer, size_t count, loff_t *ppos) +{ + struct mousedev_list *list = file->private_data; + unsigned char c; + int i; + + for (i = 0; i < count; i++) { + + c = buffer[i]; + + if (c == mousedev_genius_seq[list->genseq]) { + if (++list->genseq == MOUSEDEV_GENIUS_LEN) { + list->genseq = 0; + list->ready = 1; + list->mode = 2; + } + } else list->genseq = 0; + + if (c == mousedev_imps_seq[list->impseq]) { + if (++list->impseq == MOUSEDEV_IMPS_LEN) { + list->impseq = 0; + list->ready = 1; + list->mode = 1; + } + } else list->impseq = 0; + + list->ps2[0] = 0xfa; + list->bufsiz = 1; + + switch (c) { + + case 0xeb: /* Poll */ + mousedev_packet(list, 1); + break; + + case 0xf2: /* Get ID */ + list->ps2[1] = (list->mode == 1) ? 3 : 0; + list->bufsiz = 2; + break; + + case 0xe9: /* Get info */ + if (list->mode == 2) { + list->ps2[1] = 0x00; list->ps2[2] = 0x33; list->ps2[3] = 0x55; + } else { + list->ps2[1] = 0x60; list->ps2[2] = 3; list->ps2[3] = 200; + } + list->bufsiz = 4; + break; + } + + list->buffer = list->bufsiz; + } + + kill_fasync(list->fasync, SIGIO); + + wake_up_interruptible(&list->mousedev->wait); + + return count; +} + +static ssize_t mousedev_read(struct file * file, char * buffer, size_t count, loff_t *ppos) +{ + DECLARE_WAITQUEUE(wait, current); + struct mousedev_list *list = file->private_data; + int retval = 0; + + if (!list->ready && !list->buffer) { + + add_wait_queue(&list->mousedev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + while (!list->ready) { + + if (file->f_flags & O_NONBLOCK) { + retval = -EAGAIN; + break; + } + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + + schedule(); + } + + current->state = TASK_RUNNING; + remove_wait_queue(&list->mousedev->wait, &wait); + } + + if (retval) + return retval; + + if (!list->buffer) + mousedev_packet(list, 0); + + if (count > list->buffer) + count = list->buffer; + + if (copy_to_user(buffer, list->ps2 + list->bufsiz - list->buffer, count)) + return -EFAULT; + + list->buffer -= count; + + return count; +} + +/* No kernel lock - fine */ +static unsigned int mousedev_poll(struct file *file, poll_table *wait) +{ + struct mousedev_list *list = file->private_data; + poll_wait(file, &list->mousedev->wait, wait); + if (list->ready || list->buffer) + return POLLIN | POLLRDNORM; + return 0; +} + +struct file_operations mousedev_fops = { + read: mousedev_read, + write: mousedev_write, + poll: mousedev_poll, + open: mousedev_open, + release: mousedev_release, + fasync: mousedev_fasync, +}; + +static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev) +{ + struct mousedev *mousedev; + int minor = 0; + + if (!test_bit(EV_KEY, dev->evbit) || + (!test_bit(BTN_LEFT, dev->keybit) && !test_bit(BTN_TOUCH, dev->keybit))) + return NULL; + + if ((!test_bit(EV_REL, dev->evbit) || !test_bit(REL_X, dev->relbit)) && + (!test_bit(EV_ABS, dev->evbit) || !test_bit(ABS_X, dev->absbit))) + return NULL; + + for (minor = 0; minor < MOUSEDEV_MINORS && mousedev_table[minor]; minor++); + if (mousedev_table[minor]) { + printk(KERN_ERR "mousedev: no more free mousedev devices\n"); + return NULL; + } + + if (!(mousedev = kmalloc(sizeof(struct mousedev), GFP_KERNEL))) + return NULL; + memset(mousedev, 0, sizeof(struct mousedev)); + init_waitqueue_head(&mousedev->wait); + + mousedev->used = 1; + mousedev->minor = minor; + mousedev_table[minor] = mousedev; + + mousedev->handle.dev = dev; + mousedev->handle.handler = handler; + mousedev->handle.private = mousedev; + + mousedev->devfs = input_register_minor("mouse%d", minor, MOUSEDEV_MINOR_BASE); + + if (mousedev_mix.open) + input_open_device(&mousedev->handle); + + printk(KERN_INFO "mouse%d: PS/2 mouse device for input%d\n", minor, dev->number); + + return &mousedev->handle; +} + +static void mousedev_disconnect(struct input_handle *handle) +{ + struct mousedev *mousedev = handle->private; + + if (mousedev->open || mousedev_mix.open) + input_close_device(handle); + + if (!--mousedev->used) { + input_unregister_minor(mousedev->devfs); + mousedev_table[mousedev->minor] = NULL; + kfree(mousedev); + } +} + +static struct input_handler mousedev_handler = { + event: mousedev_event, + connect: mousedev_connect, + disconnect: mousedev_disconnect, + fops: &mousedev_fops, + minor: MOUSEDEV_MINOR_BASE, +}; + +static int __init mousedev_init(void) +{ + input_register_handler(&mousedev_handler); + + memset(&mousedev_mix, 0, sizeof(struct mousedev)); + init_waitqueue_head(&mousedev_mix.wait); + mousedev_table[MOUSEDEV_MIX] = &mousedev_mix; + mousedev_mix.used = 1; + mousedev_mix.minor = MOUSEDEV_MIX; + mousedev_mix.devfs = input_register_minor("mice", MOUSEDEV_MIX, MOUSEDEV_MINOR_BASE); + + printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n"); + + return 0; +} + +static void __exit mousedev_exit(void) +{ + input_unregister_minor(mousedev_mix.devfs); + input_unregister_handler(&mousedev_handler); +} + +module_init(mousedev_init); +module_exit(mousedev_exit); diff --git a/drivers/usb/ohci-debug.c b/drivers/usb/ohci-debug.c deleted file mode 100644 index 1510bb9da441..000000000000 --- a/drivers/usb/ohci-debug.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - * OHCI debugging code. It's gross. - * - * (C) Copyright 1999 Gregory P. Smith - */ - -#include -#include - -#include "ohci.h" - -void show_ohci_status(struct ohci *ohci) -{ - struct ohci_regs regs; - int i; - - regs.revision = readl(&ohci->regs->revision); - regs.control = readl(&ohci->regs->control); - regs.cmdstatus = readl(&ohci->regs->cmdstatus); - regs.intrstatus = readl(&ohci->regs->intrstatus); - regs.intrenable = readl(&ohci->regs->intrenable); - regs.hcca = readl(&ohci->regs->hcca); - regs.ed_periodcurrent = readl(&ohci->regs->ed_periodcurrent); - regs.ed_controlhead = readl(&ohci->regs->ed_controlhead); - regs.ed_controlcurrent = readl(&ohci->regs->ed_controlcurrent); - regs.ed_bulkhead = readl(&ohci->regs->ed_bulkhead); - regs.ed_bulkcurrent = readl(&ohci->regs->ed_bulkcurrent); - regs.current_donehead = readl(&ohci->regs->current_donehead); - regs.fminterval = readl(&ohci->regs->fminterval); - regs.fmremaining = readl(&ohci->regs->fmremaining); - regs.fmnumber = readl(&ohci->regs->fmnumber); - regs.periodicstart = readl(&ohci->regs->periodicstart); - regs.lsthresh = readl(&ohci->regs->lsthresh); - regs.roothub.a = readl(&ohci->regs->roothub.a); - regs.roothub.b = readl(&ohci->regs->roothub.b); - regs.roothub.status = readl(&ohci->regs->roothub.status); - for (i=0; iregs->roothub.portstatus[i]); - - printk(KERN_DEBUG " ohci revision = %x\n", regs.revision); - printk(KERN_DEBUG " ohci control = %x\n", regs.control); - printk(KERN_DEBUG " ohci cmdstatus = %x\n", regs.cmdstatus); - printk(KERN_DEBUG " ohci intrstatus = %x\n", regs.intrstatus); - printk(KERN_DEBUG " ohci intrenable = %x\n", regs.intrenable); - - printk(KERN_DEBUG " ohci hcca = %x\n", regs.hcca); - printk(KERN_DEBUG " ohci ed_pdcur = %x\n", regs.ed_periodcurrent); - printk(KERN_DEBUG " ohci ed_ctrlhead = %x\n", regs.ed_controlhead); - printk(KERN_DEBUG " ohci ed_ctrlcur = %x\n", regs.ed_controlcurrent); - printk(KERN_DEBUG " ohci ed_bulkhead = %x\n", regs.ed_bulkhead); - printk(KERN_DEBUG " ohci ed_bulkcur = %x\n", regs.ed_bulkcurrent); - printk(KERN_DEBUG " ohci curdonehead = %x\n", regs.current_donehead); - - printk(KERN_DEBUG " ohci fminterval = %x\n", regs.fminterval); - printk(KERN_DEBUG " ohci fmremaining = %x\n", regs.fmremaining); - printk(KERN_DEBUG " ohci fmnumber = %x\n", regs.fmnumber); - printk(KERN_DEBUG " ohci pdstart = %x\n", regs.periodicstart); - printk(KERN_DEBUG " ohci lsthresh = %x\n", regs.lsthresh); - - printk(KERN_DEBUG " ohci roothub.a = %x\n", regs.roothub.a); - printk(KERN_DEBUG " ohci roothub.b = %x\n", regs.roothub.b); - printk(KERN_DEBUG " ohci root status = %x\n", regs.roothub.status); - printk(KERN_DEBUG " roothub.port0 = %x\n", regs.roothub.portstatus[0]); - printk(KERN_DEBUG " roothub.port1 = %x\n", regs.roothub.portstatus[1]); -} /* show_ohci_status() */ - - -void show_ohci_ed(struct ohci_ed *ed) -{ - int stat = ed->status; - int skip = (stat & OHCI_ED_SKIP); - int mps = (stat & OHCI_ED_MPS) >> 16; - int isoc = (stat & OHCI_ED_F_ISOC); - int low_speed = (stat & OHCI_ED_S_LOW); - int dir = (stat & OHCI_ED_D); - int endnum = (stat & OHCI_ED_EN) >> 7; - int funcaddr = (stat & OHCI_ED_FA); - int halted = (ed->_head_td & 1); - int toggle = (ed->_head_td & 2) >> 1; - - printk(KERN_DEBUG " ohci ED:\n"); - printk(KERN_DEBUG " status = 0x%x\n", stat); - printk(KERN_DEBUG " %sMPS %d%s%s%s%s tc%d e%d fa%d\n", - skip ? "Skip " : "", - mps, - isoc ? "Isoc. " : "", - low_speed ? " LowSpd" : "", - (dir == OHCI_ED_D_IN) ? " Input" : - (dir == OHCI_ED_D_OUT) ? " Output" : "", - halted ? " Halted" : "", - toggle, - endnum, - funcaddr); - printk(KERN_DEBUG " tail_td = 0x%x\n", ed->tail_td); - printk(KERN_DEBUG " head_td = 0x%x\n", ed_head_td(ed)); - printk(KERN_DEBUG " next_ed = 0x%x\n", ed->next_ed); -} /* show_ohci_ed() */ - - -void show_ohci_td(struct ohci_td *td) -{ - int td_round = td->info & OHCI_TD_ROUND; - int td_dir = td->info & OHCI_TD_D; - int td_int_delay = (td->info & OHCI_TD_IOC_DELAY) >> 21; - int td_toggle = (td->info & OHCI_TD_DT) >> 24; - int td_errcnt = td_errorcount(*td); - int td_cc = OHCI_TD_CC_GET(td->info); - - printk(KERN_DEBUG " ohci TD hardware fields:\n"); - printk(KERN_DEBUG " info = 0x%x\n", td->info); - printk(KERN_DEBUG " %s%s%s%d %s\n", - td_round ? "Rounding " : "", - (td_dir == OHCI_TD_D_IN) ? "Input " : - (td_dir == OHCI_TD_D_OUT) ? "Output " : - (td_dir == OHCI_TD_D_SETUP) ? "Setup " : "", - "IntDelay ", td_int_delay, - (td_toggle < 2) ? " " : - (td_toggle & 1) ? "Data1 " : "Data0 "); - printk(KERN_DEBUG " %s%d %s0x%x, %sAccessed, %sActive\n", - "ErrorCnt ", td_errcnt, - "ComplCode ", td_cc, - td_cc_accessed(*td) ? "" : "Not ", - td_active(*td) ? "" : "Not "); - - printk(KERN_DEBUG " cur_buf = 0x%x\n", td->cur_buf); - printk(KERN_DEBUG " next_td = 0x%x\n", td->next_td); - printk(KERN_DEBUG " buf_end = 0x%x\n", td->buf_end); - printk(KERN_DEBUG " ohci TD driver fields:\n"); - printk(KERN_DEBUG " data = %p\n", td->data); - printk(KERN_DEBUG " dev_id = %p\n", td->dev_id); - printk(KERN_DEBUG " ed = %p\n", td->ed); - if (td->data != NULL) { - unsigned char *d = td->data; - printk(KERN_DEBUG " DATA: %02x %02x %02x %02x %02x %02x %02x %02x\n", - d[0], d[1], d[2], d[3], d[4], d[5], d[6], d[7] ); - } -} /* show_ohci_td() */ - - -void show_ohci_device(struct ohci_device *dev) -{ - int idx; - printk(KERN_DEBUG " ohci_device usb = %p\n", dev->usb); - printk(KERN_DEBUG " ohci_device ohci = %p\n", dev->ohci); - printk(KERN_DEBUG " ohci_device ohci_hcca = %p\n", dev->hcca); - for (idx=0; idx<3 /*NUM_EDS*/; ++idx) { - printk(KERN_DEBUG " [ed num %d] ", idx); - show_ohci_ed(&dev->ed[idx]); - } - for (idx=0; idx<3 /*NUM_TDS*/; ++idx) { - printk(KERN_DEBUG " [td num %d] ", idx); - show_ohci_td(&dev->td[idx]); - } - printk(KERN_DEBUG " ohci_device data\n "); - for (idx=0; idx<4; ++idx) { - printk(KERN_DEBUG " %08lx", dev->data[idx]); - } - printk(KERN_DEBUG "\n"); -} /* show_ohci_device() */ - - -void show_ohci_hcca(struct ohci_hcca *hcca) -{ - int idx; - - printk(KERN_DEBUG " ohci_hcca\n"); - - for (idx=0; idxint_table +idx); - } - - printk(KERN_DEBUG " frame_no == %d\n", hcca->frame_no); - printk(KERN_DEBUG " donehead == 0x%08x\n", hcca->donehead); -} /* show_ohci_hcca() */ - - -/* vim:sw=8 - */ diff --git a/drivers/usb/ohci-hcd.c b/drivers/usb/ohci-hcd.c deleted file mode 100644 index da3ef6657dc0..000000000000 --- a/drivers/usb/ohci-hcd.c +++ /dev/null @@ -1,1489 +0,0 @@ -/* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * - * The OHCI HCD layer is a simple but nearly complete implementation of what the - * USB people would call a HCD for the OHCI. - * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) - * The layer on top of it, is for interfacing to the alternate-usb device-drivers. - * - * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] - * [ Open Host Controller Interface driver for USB. ] - * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] - * [ (C) Copyright 1999 Gregory P. Smith ] - * [ $Log: ohci.c,v $ ] - * [ Revision 1.1 1999/04/05 08:32:30 greg ] - * - * - * v2.1 1999/05/09 ep_addr correction, code clean up - * v2.0 1999/05/04 - * virtual root hub is now an option, - * memory allocation based on kmalloc and kfree now, Bus error handling, - * INT and CTRL transfers enabled, Bulk included but disabled, ISO needs completion - * - * from Linus Torvalds (uhci.c) (APM not tested; hub, usb_device, bus and related stuff) - * from Greg Smith (ohci.c) (reset controller handling, hub) - * - * v1.0 1999/04/27 initial release - * ohci-hcd.c - */ - -/* #define OHCI_DBG */ /* printk some debug information */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "usb.h" -#include "ohci-hcd.h" -#include "inits.h" - - - -#ifdef CONFIG_APM -#include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; -#endif - - - -static struct wait_queue *control_wakeup; -static struct wait_queue *root_hub = NULL; - -static __u8 cc_to_status[16] = { /* mapping of the OHCI CC to the UHCI status codes; first guess */ -/* Activ, Stalled, Data Buffer Err, Babble Detected : NAK recvd, CRC/Timeout, Bitstuff, reservd */ -/* No Error */ 0x00, -/* CRC Error */ 0x04, -/* Bit Stuff */ 0x02, -/* Data Togg */ 0x40, -/* Stall */ 0x40, -/* DevNotResp */ 0x04, -/* PIDCheck */ 0x04, -/* UnExpPID */ 0x40, -/* DataOver */ 0x20, -/* DataUnder */ 0x20, -/* reservd */ 0x40, -/* reservd */ 0x40, -/* BufferOver */ 0x20, -/* BuffUnder */ 0x20, -/* Not Access */ 0x80, -/* Not Access */ 0x80 - }; - - -/******** - **** Interface functions - ***********************************************/ - -static int sohci_int_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1) -{ - - struct ohci * ohci = ohci_in; - usb_device_irq handler=(void *) lw0; - void *dev_id = (void *) lw1; - int ret; - - OHCI_DEBUG({ int i; printk("USB HC IRQ <<<: %x: data(%d):", ep_addr, data_len);) - OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - - ret = handler(cc_to_status[status & 0xf], data, dev_id); - if(ret == 0) return 0; /* 0 .. do not requeue */ - if(status > 0) return -1; /* error occured do not requeue ? */ - ohci_trans_req(ohci, ep_addr, 0, NULL, data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); /* requeue int request */ - return 0; -} - -static int sohci_ctrl_handler(void * ohci_in, unsigned int ep_addr, int ctrl_len, void * ctrl, void * data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw) -{ - *(int * )lw0 = status; - wake_up(&control_wakeup); - - OHCI_DEBUG( { int i; printk("USB HC CTRL<<<: %x: ctrl(%d):", ep_addr, ctrl_len);) - OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) ctrl)[i]);) - OHCI_DEBUG( printk(" data(%d):", data_len);) - OHCI_DEBUG( for(i=0; i < data_len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk(" ret_status: %x\n", status); }) - return 0; -} - -static int sohci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) -{ - struct ohci * ohci = usb_dev->bus->hcpriv; - union ep_addr_ ep_addr; - - ep_addr.iep = 0; - ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */ - | (pipe & 0x80) /* direction */ - | (1 << 5); /* type = int*/ - ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */ - - OHCI_DEBUG( printk("USB HC IRQ >>>: %x: every %d ms\n", ep_addr.iep, period);) - - usb_ohci_add_ep(ohci, ep_addr.iep, period, 1, sohci_int_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01); - - ohci_trans_req(ohci, ep_addr.iep, 0, NULL, ((struct ohci_device *) usb_dev->hcpriv)->data, 8, (__OHCI_BAG) handler, (__OHCI_BAG) dev_id); - return 0; -} - - -static int sohci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) -{ - struct wait_queue wait = { current, NULL }; - struct ohci * ohci = usb_dev->bus->hcpriv; - int status; - union ep_addr_ ep_addr; - - ep_addr.iep = 0; - ep_addr.bep.ep = ((pipe >> 15) & 0x0f) /* endpoint address */ - | (pipe & 0x80) /* direction */ - | (1 << 6); /* type = ctrl*/ - ep_addr.bep.fa = ((pipe >> 8) & 0x7f); /* device address */ - - status = 0xf; /* CC not Accessed */ - OHCI_DEBUG( { int i; printk("USB HC CTRL>>>: %x: ctrl(%d):", ep_addr.iep, 8);) - OHCI_DEBUG( for(i=0; i < 8; i++ ) printk(" %02x", ((__u8 *) cmd)[i]);) - OHCI_DEBUG( printk(" data(%d):", len);) - OHCI_DEBUG( for(i=0; i < len; i++ ) printk(" %02x", ((__u8 *) data)[i]);) - OHCI_DEBUG( printk("\n"); }) - - usb_ohci_add_ep(ohci, ep_addr.iep, 0, 1, sohci_ctrl_handler, 1 << ((pipe & 0x03) + 3) , (pipe >> 26) & 0x01); - - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); - - ohci_trans_req(ohci, ep_addr.iep, 8, cmd, data, len, (__OHCI_BAG) &status, 0); - - schedule_timeout(HZ/10); - - remove_wait_queue(&control_wakeup, &wait); - - OHCI_DEBUG(printk("USB HC status::: %x\n", cc_to_status[status & 0x0f]);) - - return cc_to_status[status & 0x0f]; -} - - -static int sohci_usb_deallocate(struct usb_device *usb_dev) { - struct ohci_device *dev = usb_to_ohci(usb_dev); - union ep_addr_ ep_addr; - - ep_addr.iep = 0; - - OHCI_DEBUG(printk("USB HC dealloc %x\n", usb_dev->devnum);) - - /* wait_ms(20); */ - - if(usb_dev->devnum >=0) { - ep_addr.bep.fa = usb_dev->devnum; - usb_ohci_rm_function(((struct ohci_device *)usb_dev->hcpriv)->ohci, ep_addr.iep); - } - - USB_FREE(dev); - USB_FREE(usb_dev); - - return 0; -} - -static struct usb_device *sohci_usb_allocate(struct usb_device *parent) { - - struct usb_device *usb_dev; - struct ohci_device *dev; - - - USB_ALLOC(usb_dev, sizeof(*usb_dev)); - if (!usb_dev) - return NULL; - - memset(usb_dev, 0, sizeof(*usb_dev)); - - USB_ALLOC(dev, sizeof(*dev)); - if (!dev) { - USB_FREE(usb_dev); - return NULL; - } - - /* Initialize "dev" */ - memset(dev, 0, sizeof(*dev)); - - usb_dev->hcpriv = dev; - dev->usb = usb_dev; - - usb_dev->parent = parent; - - if (parent) { - usb_dev->bus = parent->bus; - dev->ohci = usb_to_ohci(parent)->ohci; - } - return usb_dev; -} - -struct usb_operations sohci_device_operations = { - sohci_usb_allocate, - sohci_usb_deallocate, - sohci_control_msg, - sohci_request_irq, -}; - - -/****** - *** ED handling functions - ************************************/ - - - -/* - * search for the right place to insert an interrupt ed into the int tree - * do some load ballancing - * */ - -static int usb_ohci_int_ballance(struct ohci * ohci, int interval, int load) { - - int i,j; - - j = 0; /* search for the least loaded interrupt endpoint branch of all 32 branches */ - for(i=0; i< 32; i++) if(ohci->ohci_int_load[j] > ohci->ohci_int_load[i]) j=i; - - if(interval < 1) interval = 1; - if(interval > 32) interval = 32; - for(i= 0; ((interval >> i) > 1 ); interval &= (0xfffe << i++ )); /* interval = 2^int(ld(interval)) */ - - - for(i=j%interval; i< 32; i+=interval) ohci->ohci_int_load[i] += load; - j = interval + (j % interval); - - OHCI_DEBUG(printk("USB HC new int ed on pos : %x \n",j);) - - return j; -} - -/* get the ed from the endpoint / device adress */ - -struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in) { - -union ep_addr_ ep_addr; -struct usb_ohci_ed *tmp; -unsigned int mask; - -mask = 0; -ep_addr.iep = ep_addr_in; - -#ifdef VROOTHUB - if(ep_addr.bep.fa == ohci->root_hub_funct_addr) { - if((ep_addr.bep.ep & 0x0f) == 0) - return &ohci->ed_rh_ep0; /* root hub ep0 */ - else - return &ohci->ed_rh_epi; /* root hub int ep */ - } -#endif - - tmp = ohci->ed_func_ep0[ep_addr.bep.fa]; - mask = ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); - ep_addr.bep.ep &= mask; /* mask out direction of ctrl ep */ - - while (tmp != NULL) { - if (tmp->ep_addr.iep == ep_addr.iep) - return tmp; - tmp = tmp->ed_list; - } - return NULL; -} - -spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; -/* add a new endpoint ep_addr */ -struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr_in, int interval, int load, f_handler handler, int ep_size, int speed) { - - struct usb_ohci_ed * ed; - struct usb_ohci_td * td; - union ep_addr_ ep_addr; - - - int int_junk; - - struct usb_ohci_ed *tmp; - - ep_addr.iep = ep_addr_in ; - ep_addr.bep.ep &= ((((ep_addr.bep.ep >> 5) & 0x03)==2)?0x7f:0xff); /* mask out direction of ctrl ep */ - - spin_lock(&usb_ed_lock); - - tmp = ohci_find_ep(ohci, ep_addr.iep); - if (tmp != NULL) { - -#ifdef VROOTHUB - if(ep_addr.bep.fa == ohci->root_hub_funct_addr) { - if((ep_addr.bep.ep & 0x0f) != 0) { /* root hub int ep */ - ohci->ed_rh_epi.handler = handler; - ohci_init_rh_int_timer(ohci, interval); - } - else { /* root hub ep0 */ - ohci->ed_rh_ep0.handler = handler; - } - } - - else -#endif - - { - tmp->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7) - - | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0) - | (speed << 13) - | ep_size <<16; - - tmp->handler = handler; - } - spin_unlock(&usb_ed_lock); - return tmp; /* ed already in use */ - } - - - OHCI_ALLOC(td, sizeof(td)); /* dummy td; end of td list for ed */ - OHCI_ALLOC(ed, sizeof(ed)); - td->prev_td = NULL; - - ed->hw.tail_td = virt_to_bus(&td->hw); - ed->hw.head_td = ed->hw.tail_td; - ed->hw.info = ep_addr.bep.fa | ((ep_addr.bep.ep & 0xf) <<7) - /* | ((ep_addr.bep.port & 0x80)? 0x1000 : 0x0800 ) */ - | (((ep_addr.bep.ep & 0x60) == 0)? 0x8000 : 0) - | (speed << 13) - | ep_size <<16; - - ed->handler = handler; - - switch((ep_addr.bep.ep >> 5) & 0x03) { - case CTRL: - ed->hw.next_ed = 0; - if(ohci->ed_controltail == NULL) { - writel(virt_to_bus(&ed->hw), &ohci->regs->ed_controlhead); - } - else { - ohci->ed_controltail->hw.next_ed = virt_to_bus(&ed->hw); - } - ed->ed_prev = ohci->ed_controltail; - ohci->ed_controltail = ed; - break; - case BULK: - ed->hw.next_ed = 0; - if(ohci->ed_bulktail == NULL) { - writel(virt_to_bus(&ed->hw), &ohci->regs->ed_bulkhead); - } - else { - ohci->ed_bulktail->hw.next_ed = virt_to_bus(&ed->hw); - } - ed->ed_prev = ohci->ed_bulktail; - ohci->ed_bulktail = ed; - break; - case INT: - int_junk = usb_ohci_int_ballance(ohci, interval, load); - ed->hw.next_ed = ohci->hc_area->ed[int_junk].next_ed; - ohci->hc_area->ed[int_junk].next_ed = virt_to_bus(&ed->hw); - ed->ed_prev = (struct usb_ohci_ed *) &ohci->hc_area->ed[int_junk]; - break; - case ISO: - ed->hw.next_ed = 0; - ohci->ed_isotail->hw.next_ed = virt_to_bus(&ed->hw); - ed->ed_prev = ohci->ed_isotail; - ohci->ed_isotail = ed; - break; - } - ed->ep_addr = ep_addr; - - /* Add it to the "hash"-table of known endpoint descriptors */ - - ed->ed_list = ohci->ed_func_ep0[ed->ep_addr.bep.fa]; - ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed; - - spin_unlock(&usb_ed_lock); - - OHCI_DEBUG(printk("USB HC new ed %x: %x :", ep_addr.iep, (unsigned int ) ed); ) - OHCI_DEBUG({ int i; for( i= 0; i<8 ;i++) printk(" %4x", ((unsigned int *) ed)[i]) ; printk("\n"); }; ) - return 0; -} - -/***** - * Request the removal of an endpoint - * - * put the ep on the rm_list and request a stop of the bulk or ctrl list - * real removal is done at the next start of frame hardware interrupt - */ -int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed) -{ - unsigned int flags; - struct usb_ohci_ed *tmp; - - OHCI_DEBUG(printk("USB HC remove ed %x: %x :\n", ed->ep_addr.iep, (unsigned int ) ed); ) - - spin_lock_irqsave(&usb_ed_lock, flags); - - tmp = ohci->ed_func_ep0[ed->ep_addr.bep.fa]; - if (tmp == NULL) { - spin_unlock_irqrestore(&usb_ed_lock, flags); - return 0; - } - - if(tmp == ed) { - ohci->ed_func_ep0[ed->ep_addr.bep.fa] = ed->ed_list; - } - else { - while (tmp->ed_list != ed) { - if (tmp->ed_list == NULL) { - spin_unlock_irqrestore(&usb_ed_lock, flags); - return 0; - } - tmp = tmp->ed_list; - } - tmp->ed_list = ed->ed_list; - } - ed->ed_list = ohci->ed_rm_list; - ohci->ed_rm_list = ed; - ed->hw.info |= OHCI_ED_SKIP; - - switch((ed->ep_addr.bep.ep >> 5) & 0x03) { - case CTRL: - writel_mask(~(0x01<<4), &ohci->regs->control); /* stop CTRL list */ - break; - case BULK: - writel_mask(~(0x01<<5), &ohci->regs->control); /* stop BULK list */ - break; - } - - - writel( OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ - - spin_unlock_irqrestore(&usb_ed_lock, flags); - - return 1; -} - -/* we have requested to stop the bulk or CTRL list, - * now we can remove the eds on the rm_list */ - -static int ohci_rm_eds(struct ohci * ohci) { - - unsigned int flags; - struct usb_ohci_ed *ed; - struct usb_ohci_ed *ed_tmp; - struct usb_ohci_td *td; - __u32 td_hw_tmp; - __u32 td_hw; - - spin_lock_irqsave(&usb_ed_lock, flags); - - ed = ohci->ed_rm_list; - - while (ed != NULL) { - - switch((ed->ep_addr.bep.ep >> 5) & 0x03) { - case CTRL: - if(ed->ed_prev == NULL) { - writel(ed->hw.next_ed, &ohci->regs->ed_controlhead); - } - else { - ed->ed_prev->hw.next_ed = ed->hw.next_ed; - } - if(ohci->ed_controltail == ed) { - ohci->ed_controltail = ed->ed_prev; - } - break; - case BULK: - if(ed->ed_prev == NULL) { - writel(ed->hw.next_ed, &ohci->regs->ed_bulkhead); - } - else { - ed->ed_prev->hw.next_ed = ed->hw.next_ed; - } - if(ohci->ed_bulktail == ed) { - ohci->ed_bulktail = ed->ed_prev; - } - break; - case INT: - ed->ed_prev->hw.next_ed = ed->hw.next_ed; - break; - case ISO: - ed->ed_prev->hw.next_ed = ed->hw.next_ed; - if(ohci->ed_isotail == ed) { - ohci->ed_isotail = ed->ed_prev; - } - break; - } - - if(ed->hw.next_ed != 0) ((struct usb_ohci_ed *) bus_to_virt(ed->hw.next_ed))->ed_prev = ed->ed_prev; - - -/* tds directly connected to ed */ - - td_hw = ed->hw.head_td & 0xfffffff0; - while(td_hw != 0) { - td = bus_to_virt(td_hw); - td_hw_tmp = td_hw; - td_hw = td->hw.next_td; - OHCI_FREE(td); /* free pending tds */ - if(td_hw_tmp == ed->hw.tail_td) break; - - } - - /* mark TDs on the hc done list (if there are any) */ - td_hw = readl(&ohci->regs->donehead) & 0xfffffff0; - while(td_hw != 0) { - td = bus_to_virt(td_hw); - td_hw = td->hw.next_td; - if(td->ep == ed) td->ep = 0; - } - - /* mark TDs on the hcca done list (if there are any) */ - td_hw = ohci->hc_area->hcca.done_head & 0xfffffff0 ; - - while(td_hw != 0) { - td = bus_to_virt(td_hw); - td_hw = td->hw.next_td; - if(td->ep == ed) td->ep = 0; - } - - ed_tmp = ed; - ed = ed->ed_list; - OHCI_FREE(ed_tmp); /* free ed */ - } - writel(0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ - writel(0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ - writel_set((0x01<<4), &ohci->regs->control); /* start CTRL u. (BULK list) */ - - spin_unlock_irqrestore(&usb_ed_lock, flags); - - ohci->ed_rm_list = NULL; - OHCI_DEBUG(printk("USB HC after rm ed control: %4x intrstat: %4x intrenable: %4x\n", readl(&ohci->regs->control),readl(&ohci->regs->intrstatus),readl(&ohci->regs->intrenable));) - - - return 0; -} - -/* remove all endpoints of a function (device) */ -int usb_ohci_rm_function( struct ohci * ohci, unsigned int ep_addr_in) -{ - struct usb_ohci_ed *ed; - struct usb_ohci_ed *tmp; - union ep_addr_ ep_addr; - - - - ep_addr.iep = ep_addr_in; - - for(ed = ohci->ed_func_ep0[ep_addr.bep.fa]; ed != NULL;) { - tmp = ed; - ed = ed->ed_list; - usb_ohci_rm_ep(ohci, tmp); - } - - - - return 1; -} - - - - - -/****** - *** TD handling functions - ************************************/ - - -#define FILL_TD(TD_PT, HANDLER, INFO, DATA, LEN, LW0, LW1) \ - td_pt = (TD_PT); \ - td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); \ - td_pt1->ep = usb_ep; \ - td_pt1->handler = (HANDLER); \ - td_pt1->buffer_start = (DATA); \ - td_pt1->lw0 = (LW0); \ - td_pt1->lw1 = (LW1); \ - td_pt1->hw.info = (INFO); \ - td_pt1->hw.cur_buf = virt_to_bus(DATA); \ - td_pt1->hw.buf_end = td_pt1->hw.cur_buf + (LEN) - 1; \ - td_pt1->hw.next_td = virt_to_bus(td_pt); \ - usb_ep->hw.tail_td = virt_to_bus(td_pt); \ - td_pt->prev_td = td_pt1; \ - td_pt->hw.next_td = 0 - -spinlock_t usb_req_lock = SPIN_LOCK_UNLOCKED; - -int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int ctrl_len, void *ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1) { - - int ed_type; - unsigned int flags; - struct usb_ohci_td *td_pt; - struct usb_ohci_td *td_pt1; - struct usb_ohci_td *td_pt_a1, *td_pt_a2, *td_pt_a3; - struct usb_ohci_ed *usb_ep; - f_handler handler; - - - td_pt_a1 =NULL; - td_pt_a2 =NULL; - td_pt_a3 =NULL; - - usb_ep = ohci_find_ep(ohci, ep_addr); - if(usb_ep == NULL ) return -1; /* not known ep */ - - handler = usb_ep->handler; - -#ifdef VROOTHUB - if(usb_ep == &ohci->ed_rh_ep0) { /* root hub ep 0 control endpoint */ - root_hub_control_msg(ohci, 8, ctrl, data, data_len, lw0, lw1, handler); - return 0; - } - - if(usb_ep == &ohci->ed_rh_epi) { /* root hub interrupt endpoint */ - - root_hub_int_req(ohci, 8, ctrl, data, data_len, lw0, lw1, handler); - return 0; - } -#endif - /* struct usb_ohci_ed * usb_ep = usb_ohci_add_ep(pipe, ohci, interval, 1); */ - - ed_type = ((((union ep_addr_)ep_addr).bep.ep >> 5) & 0x07); - - switch(ed_type) { - case BULK_IN: - case BULK_OUT: - case INT_IN: - case INT_OUT: - OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1)); - break; - - case CTRL_IN: - case CTRL_OUT: - OHCI_ALLOC(td_pt_a1, sizeof(td_pt_a1)); - OHCI_ALLOC(td_pt_a3, sizeof(td_pt_a3)); - if(data_len > 0) { - OHCI_ALLOC(td_pt_a2, sizeof(td_pt_a2)); - } - break; - - case ISO_IN: - case ISO_OUT: - - } - - spin_lock_irqsave(&usb_req_lock, flags); - - switch(ed_type) { - case BULK_IN: - FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 ); - writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case BULK_OUT: - FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 ); - writel( OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ - break; - - case INT_IN: - FILL_TD( td_pt_a1, handler, TD_CC | TD_R | TD_DP_IN | TD_T_TOGGLE, data, data_len, lw0, lw1 ); - break; - - case INT_OUT: - FILL_TD( td_pt_a1, handler, TD_CC | TD_DP_OUT | TD_T_TOGGLE, data, data_len, lw0, lw1 ); - break; - - case CTRL_IN: - FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 ); - if(data_len > 0) { - FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_IN | TD_T_DATA1, data, data_len, 0, 0 ); - } - FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_OUT | TD_T_DATA1, NULL, 0, lw0, lw1 ); - writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - case CTRL_OUT: - FILL_TD( td_pt_a1, NULL, TD_CC | TD_DP_SETUP | TD_T_DATA0, ctrl, ctrl_len, 0, 0 ); - if(data_len > 0) { - FILL_TD( td_pt_a2, NULL, TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1, data, data_len, 0, 0 ); - } - FILL_TD( td_pt_a3, handler, TD_CC | TD_DP_IN | TD_T_DATA1, NULL, 0, lw0, lw1 ); - writel( OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ - break; - - case ISO_IN: - case ISO_OUT: - break; - } - - - - - td_pt1 = (struct usb_ohci_td *) bus_to_virt(usb_ep->hw.tail_td); - - - if(td_pt_a3 != NULL) td_pt_a3->prev_td = NULL; - else if (td_pt_a2 != NULL) td_pt_a2->prev_td = NULL; - else if (td_pt_a1 != NULL) td_pt_a1->prev_td = NULL; - - spin_unlock_irqrestore(&usb_req_lock, flags); - return 0; -} - - -/****** - *** Done List handling functions - ************************************/ - -/* replies to the request have to be on a FIFO basis so - * we reverse the reversed done-list */ - -static struct usb_ohci_td * ohci_reverse_done_list(struct ohci * ohci) { - - __u32 td_list_hc; - struct usb_ohci_td * td_list = NULL; - struct usb_ohci_td * td_rev = NULL; - - td_list_hc = ohci->hc_area->hcca.done_head & 0xfffffff0; - ohci->hc_area->hcca.done_head = 0; - - while(td_list_hc) { - - td_list = (struct usb_ohci_td *) bus_to_virt(td_list_hc); - td_list->next_dl_td = td_rev; - - td_rev = td_list; - td_list_hc = td_list->hw.next_td & 0xfffffff0; - } - return td_list; -} - -/* all done requests are replied here */ -static int usb_ohci_done_list(struct ohci * ohci) { - - struct usb_ohci_td * td = NULL; - struct usb_ohci_td * td_list; - struct usb_ohci_td * td_list_next = NULL; - struct usb_ohci_td * td_err = NULL; - __u32 td_hw; - - - td_list = ohci_reverse_done_list(ohci); - - while(td_list) { - td_list_next = td_list->next_dl_td; - td = td_list; - - if(td->ep == NULL) { /* removed ep */ - OHCI_FREE(td_list); - break; - } - - /* the HC halts an ED if an error occurs; put all pendings TDs of an halted ED on the - * done list; they are marked with an 0xf CC_error code - */ - - if(TD_CC_GET(td_list->hw.info) != TD_CC_NOERROR) { /* on error move all pending tds of an ed into the done list */ - printk("******* USB BUS error %x @ep %x\n", TD_CC_GET(td_list->hw.info), td_list->ep->ep_addr.iep); - td_err= td_list; - td_hw = td_list->ep->hw.head_td & 0xfffffff0; - while(td_hw != 0) { - if(td_hw == td_list->ep->hw.tail_td) break; - td = bus_to_virt(td_hw); - td_err->next_dl_td = td; - td_err= td; - td_hw = td->hw.next_td; - } - td_list->ep->hw.head_td = td_list->ep->hw.tail_td; - td->next_dl_td = td_list_next; - td_list_next = td_list->next_dl_td; - - } - /* send the reply */ - if(td_list->handler != NULL) { - if(td_list->prev_td == NULL) { - td_list->handler((void *) ohci, - td_list->ep->ep_addr.iep, - 0, - NULL, - td_list->buffer_start, - td_list->hw.buf_end-virt_to_bus(td_list->buffer_start)+1, - TD_CC_GET(td_list->hw.info), - td_list->lw0, - td_list->lw1); - OHCI_FREE(td_list); - } - else { - if(td_list->prev_td->prev_td == NULL) { /* cntrl 2 Transactions dataless */ - td_list->handler((void *) ohci, - td_list->ep->ep_addr.iep, - td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1, - td_list->prev_td->buffer_start, - NULL, - 0, - (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info), - td_list->lw0, - td_list->lw1); - OHCI_FREE(td_list->prev_td); - OHCI_FREE(td_list); - } - else { /* cntrl 3 Transactions */ - td_list->handler((void *) ohci, - td_list->ep->ep_addr.iep, - td_list->prev_td->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->prev_td->buffer_start)+1, - td_list->prev_td->prev_td->buffer_start, - td_list->prev_td->buffer_start, - td_list->prev_td->hw.buf_end-virt_to_bus(td_list->prev_td->buffer_start)+1, - (TD_CC_GET(td_list->prev_td->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->prev_td->hw.info) - : (TD_CC_GET(td_list->prev_td->hw.info) > 0) ? TD_CC_GET(td_list->prev_td->hw.info) : TD_CC_GET(td_list->hw.info), - td_list->lw0, - td_list->lw1); - OHCI_FREE(td_list->prev_td->prev_td); - OHCI_FREE(td_list->prev_td); - OHCI_FREE(td_list); - - } - } - - } - td_list = td_list_next; - } - return 0; -} - - - -/****** - *** HC functions - ************************************/ - - - -void reset_hc(struct ohci *ohci) { - int retries = 5; - int timeout = 30; - int fminterval; - - if(readl(&ohci->regs->control) & 0x100) { /* SMM owns the HC */ - writel(0x08, &ohci->regs->cmdstatus); /* request ownership */ - printk("USB HC TakeOver from SMM\n"); - do { - wait_ms(100); - if(--retries) { - printk("USB HC TakeOver timed out!\n"); - break; - } - } - while(readl(&ohci->regs->control) & 0x100); - } - - writel((1<<31), &ohci->regs->intrdisable); /* Disable HC interrupts */ - OHCI_DEBUG(printk("USB HC reset_hc: %x ; retries: %d\n", readl(&ohci->regs->control), 5-retries);) - fminterval = readl(&ohci->regs->fminterval) & 0x3fff; - writel(1, &ohci->regs->cmdstatus); /* HC Reset */ - while ((readl(&ohci->regs->cmdstatus) & 0x01) != 0) { /* 10us Reset */ - if (--timeout == 0) { - printk("USB HC reset timed out!\n"); - return; - } - udelay(1); - } - /* set the timing */ - fminterval |= (((fminterval -210) * 6)/7)<<16; - writel(fminterval, &ohci->regs->fminterval); - writel(((fminterval&0x3fff)*9)/10, &ohci->regs->periodicstart); -} - - -/* - * Reset and start an OHCI controller - */ -void start_hc(struct ohci *ohci) -{ - /* int fminterval; */ - unsigned int mask; - /* fminterval = readl(&ohci->regs->fminterval) & 0x3fff; - reset_hc(ohci); */ - - - writel(virt_to_bus(&ohci->hc_area->hcca), &ohci->regs->hcca); /* a reset clears this */ - - /* Choose the interrupts we care about now, others later on demand */ - mask = OHCI_INTR_MIE | OHCI_INTR_WDH; - /* | OHCI_INTR_SO | OHCI_INTR_UE |OHCI_INTR_RHSC |OHCI_INTR_SF| - OHCI_INTR_FNO */ - - - - writel((0x00), &ohci->regs->control); /* USB Reset BUS */ - wait_ms(10); - - writel((0x97), &ohci->regs->control); /* USB Operational */ - - writel( 0x10000, &ohci->regs->roothub.status); /* root hub power on */ - wait_ms(50); - - OHCI_DEBUG(printk("USB HC rstart_hc_operational: %x\n", readl(&ohci->regs->control)); ) - OHCI_DEBUG(printk("USB HC roothubstata: %x \n", readl( &(ohci->regs->roothub.a) )); ) - OHCI_DEBUG(printk("USB HC roothubstatb: %x \n", readl( &(ohci->regs->roothub.b) )); ) - OHCI_DEBUG(printk("USB HC roothubstatu: %x \n", readl( &(ohci->regs->roothub.status) )); ) - OHCI_DEBUG(printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); ) - OHCI_DEBUG(printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); ) - - /* control_wakeup = NULL; */ - writel(mask, &ohci->regs->intrenable); - writel(mask, &ohci->regs->intrstatus); - -#ifdef VROOTHUB - { - - struct usb_device * usb_dev; - struct ohci_device *dev; - - usb_dev = sohci_usb_allocate(ohci->root_hub->usb); - dev = usb_dev->hcpriv; - - dev->ohci = ohci; - - usb_connect(usb_dev); - - ohci->root_hub->usb->children[0] = usb_dev; - - usb_new_device(usb_dev); - } -#endif - - - -} - - - - -static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) -{ - struct ohci *ohci = __ohci; - struct ohci_regs *regs = ohci->regs; - - int ints; - - - if((ohci->hc_area->hcca.done_head != 0) && !(ohci->hc_area->hcca.done_head & 0x01)) { - ints = OHCI_INTR_WDH; - } - else { - if((ints = (readl(®s->intrstatus) & readl(®s->intrenable))) == 0) - return; - } - - ohci->intrstatus |= ints; - OHCI_DEBUG(printk("USB HC interrupt: %x (%x) \n", ints, readl(&ohci->regs->intrstatus));) - - /* ints &= ~(OHCI_INTR_WDH); WH Bit will be set by done list subroutine */ - /* if(ints & OHCI_INTR_FNO) { - writel(OHCI_INTR_FNO, ®s->intrstatus); - if (waitqueue_active(&ohci_tasks)) wake_up(&ohci_tasks); - } */ - - if(ints & OHCI_INTR_WDH) { - writel(OHCI_INTR_WDH, ®s->intrdisable); - ohci->intrstatus &= (~OHCI_INTR_WDH); - usb_ohci_done_list(ohci); /* prepare out channel list */ - writel(OHCI_INTR_WDH, &ohci->regs->intrstatus); - writel(OHCI_INTR_WDH, &ohci->regs->intrenable); - - } - - if(ints & OHCI_INTR_SF) { - writel(OHCI_INTR_SF, ®s->intrdisable); - writel(OHCI_INTR_SF, &ohci->regs->intrstatus); - ohci->intrstatus &= (~OHCI_INTR_SF); - if(ohci->ed_rm_list != NULL) { - ohci_rm_eds(ohci); - } - } -#ifndef VROOTHUB - if(ints & OHCI_INTR_RHSC) { - writel(OHCI_INTR_RHSC, ®s->intrdisable); - writel(OHCI_INTR_RHSC, &ohci->regs->intrstatus); - wake_up(&root_hub); - - } - #endif - - writel(OHCI_INTR_MIE, ®s->intrenable); - -} - -#ifndef VROOTHUB -/* - * This gets called if the connect status on the root - * hub (and the root hub only) changes. - */ -static void ohci_connect_change(struct ohci *ohci, unsigned int port_nr) -{ - struct usb_device *usb_dev; - struct ohci_device *dev; - OHCI_DEBUG(printk("uhci_connect_change: called for %d stat %x\n", port_nr,readl(&ohci->regs->roothub.portstatus[port_nr]) );) - - /* - * Even if the status says we're connected, - * the fact that the status bits changed may - * that we got disconnected and then reconnected. - * - * So start off by getting rid of any old devices.. - */ - usb_disconnect(&ohci->root_hub->usb->children[port_nr]); - - if(!(readl(&ohci->regs->roothub.portstatus[port_nr]) & RH_PS_CCS)) { - writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[port_nr]); - return; /* nothing connected */ - } - /* - * Ok, we got a new connection. Allocate a device to it, - * and find out what it wants to do.. - */ - usb_dev = sohci_usb_allocate(ohci->root_hub->usb); - dev = usb_dev->hcpriv; - dev->ohci = ohci; - usb_connect(dev->usb); - ohci->root_hub->usb->children[port_nr] = usb_dev; - wait_ms(200); /* wait for powerup */ - /* reset port/device */ - writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[port_nr]); /* reset port */ - while(!(readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_PRSC)) wait_ms(10); /* reset active ? */ - writel(RH_PS_PES, &ohci->regs->roothub.portstatus[port_nr]); /* enable port */ - wait_ms(10); - /* Get speed information */ - usb_dev->slow = (readl( &ohci->regs->roothub.portstatus[port_nr]) & RH_PS_LSDA) ? 1 : 0; - - /* - * Ok, all the stuff specific to the root hub has been done. - * The rest is generic for any new USB attach, regardless of - * hub type. - */ - usb_new_device(usb_dev); -} -#endif - - - -/* - * Allocate the resources required for running an OHCI controller. - * Host controller interrupts must not be running while calling this - * function. - * - * The mem_base parameter must be the usable -virtual- address of the - * host controller's memory mapped I/O registers. - * - * This is where OHCI triumphs over UHCI, because good is dumb. - * Note how much simpler this function is than in uhci.c. - * - * OHCI hardware takes care of most of the scheduling of different - * transfer types with the correct prioritization for us. - */ - - -static struct ohci *alloc_ohci(void* mem_base) -{ - int i,j; - struct ohci *ohci; - struct ohci_hc_area *hc_area; - struct usb_bus *bus; - struct ohci_device *dev; - struct usb_device *usb; - - /* - * Here we allocate some dummy EDs as well as the - * OHCI host controller communications area. The HCCA is just - * a nice pool of memory with pointers to endpoint descriptors - * for the different interrupts. - * - * The first page of memory contains the HCCA and ohci structure - */ - hc_area = (struct ohci_hc_area *) __get_free_pages(GFP_KERNEL, 1); - if (!hc_area) - return NULL; - memset(hc_area, 0, sizeof(*hc_area)); - ohci = &hc_area->ohci; - ohci->irq = -1; - ohci->regs = mem_base; - - ohci->hc_area = hc_area; - /* Tell the controller where the HCCA is */ - writel(virt_to_bus(&hc_area->hcca), &ohci->regs->hcca); - - - /* - * Initialize the ED polling "tree", full tree; - * dummy eds ed[i] (hc should skip them) - * i == 0 is the end of the iso list; - * 1 is the 1ms node, - * 2,3 2ms nodes, - * 4,5,6,7 4ms nodes, - * 8 ... 15 8ms nodes, - * 16 ... 31 16ms nodes, - * 32 ... 63 32ms nodes - * Sequenzes: - * 32-16- 8-4-2-1-0 - * 33-17- 9-5-3-1-0 - * 34-18-10-6-2-1-0 - * 35-19-11-7-3-1-0 - * 36-20-12-4-2-1-0 - * 37-21-13-5-3-1-0 - * 38-22-14-6-2-1-0 - * 39-23-15-7-3-1-0 - * 40-24- 8-4-2-1-0 - * 41-25- 9-5-3-1-0 - * 42-26-10-6-2-1-0 - * : : - * 63-31-15-7-3-1-0 - */ - hc_area->ed[ED_ISO].info |= OHCI_ED_SKIP; /* place holder, so skip it */ - hc_area->ed[ED_ISO].next_ed = 0x0000; /* end of iso list */ - - hc_area->ed[1].next_ed = virt_to_bus(&(hc_area->ed[ED_ISO])); - hc_area->ed[1].info |= OHCI_ED_SKIP; /* place holder, skip it */ - - j=1; - for (i = 2; i < (NUM_INTS * 2); i++) { - if (i >= NUM_INTS) - hc_area->hcca.int_table[i - NUM_INTS] = virt_to_bus(&(hc_area->ed[i])); - - if( i == j*4) j *= 2; - hc_area->ed[i].next_ed = virt_to_bus(&(hc_area->ed[j+ i%j])); - hc_area->ed[i].info |= OHCI_ED_SKIP; /* place holder, skip it */ - } - - - /* - * for load ballancing of the interrupt branches - */ - for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; - - /* - * Store the end of control and bulk list eds. So, we know where we can add - * elements to these lists. - */ - ohci->ed_isotail = (struct usb_ohci_ed *) &(hc_area->ed[ED_ISO]); - ohci->ed_controltail = NULL; - ohci->ed_bulktail = NULL; - - /* - * Tell the controller where the control and bulk lists are - * The lists are empty now. - */ - writel(0, &ohci->regs->ed_controlhead); - writel(0, &ohci->regs->ed_bulkhead); - - - USB_ALLOC(bus, sizeof(*bus)); - if (!bus) - return NULL; - - memset(bus, 0, sizeof(*bus)); - - ohci->bus = bus; - bus->hcpriv = (void *) ohci; - bus->op = &sohci_device_operations; - - - usb = sohci_usb_allocate(NULL); - if (!usb) - return NULL; - - dev = ohci->root_hub = usb_to_ohci(usb); - - usb->bus = bus; - /* bus->root_hub = ohci_to_usb(ohci->root_hub); */ - dev->ohci = ohci; - - /* Initialize the root hub */ - - usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; - usb_init_root_hub(usb); - - return ohci; -} - - -/* - * De-allocate all resources.. - */ - -static void release_ohci(struct ohci *ohci) -{ - int i; - union ep_addr_ ep_addr; - ep_addr.iep = 0; - - OHCI_DEBUG(printk("USB HC release ohci \n");) - - if (ohci->irq >= 0) { - free_irq(ohci->irq, ohci); - ohci->irq = -1; - } - - /* stop hc */ - writel(OHCI_USB_SUSPEND, &ohci->regs->control); - - /* deallocate all EDs and TDs */ - for(i=0; i < 128; i ++) { - ep_addr.bep.fa = i; - usb_ohci_rm_function(ohci, ep_addr.iep); - } - ohci_rm_eds(ohci); /* remove eds */ - - /* disconnect all devices */ - if(ohci->root_hub) - for(i = 0; i < ohci->root_hub->usb->maxchild; i++) - usb_disconnect(ohci->root_hub->usb->children + i); - - USB_FREE(ohci->root_hub->usb); - USB_FREE(ohci->root_hub); - USB_FREE(ohci->bus); - - /* unmap the IO address space */ - iounmap(ohci->regs); - - - free_pages((unsigned int) ohci->hc_area, 1); - -} - - -void cleanup_drivers(void); - -static int ohci_roothub_thread(void * __ohci) -{ - struct ohci *ohci = (struct ohci *)__ohci; - lock_kernel(); - - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources.. - */ - printk("ohci_roothub_thread at %p\n", &ohci_roothub_thread); - exit_mm(current); - exit_files(current); - exit_fs(current); - - - strcpy(current->comm, "root-hub"); - - - start_hc(ohci); - writel( 0x10000, &ohci->regs->roothub.status); - wait_ms(50); /* root hub power on */ - do { -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume = 0; - start_hc(ohci); - continue; - } -#endif - - OHCI_DEBUG(printk("USB RH tasks: int: %x\n", ohci->intrstatus); ) -#ifndef VROOTHUB - /* if (ohci->intrstatus & OHCI_INTR_RHSC) */ - { - int port_nr; - for(port_nr=0; port_nr< ohci->root_hub->usb->maxchild; port_nr++) - if(readl(&ohci->regs->roothub.portstatus[port_nr]) & (RH_PS_CSC | RH_PS_PRSC)) { - ohci_connect_change(ohci, port_nr); - writel(0xffff0000, &ohci->regs->roothub.portstatus[port_nr]); - } - ohci->intrstatus &= ~(OHCI_INTR_RHSC); - writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); - } -#endif - - interruptible_sleep_on(&root_hub); - - } while (!signal_pending(current)); - -#ifdef VROOTHUB - ohci_del_rh_int_timer(ohci); -#endif - - - cleanup_drivers(); - /* reset_hc(ohci); */ - - release_ohci(ohci); - MOD_DEC_USE_COUNT; - - printk("ohci_control_thread exiting\n"); - - return 0; -} - - - - -/* - * Increment the module usage count, start the control thread and - * return success. - */ -static int found_ohci(int irq, void* mem_base) -{ - int retval; - struct ohci *ohci; - OHCI_DEBUG(printk("USB HC found ohci: irq= %d membase= %x \n", irq, (int)mem_base);) - /* Allocate the running OHCI structures */ - ohci = alloc_ohci(mem_base); - if (!ohci) { - return -ENOMEM; - } - - reset_hc(ohci); - - retval = -EBUSY; - if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "ohci-usb", ohci) == 0) { - int pid; - - MOD_INC_USE_COUNT; - ohci->irq = irq; - - pid = kernel_thread(ohci_roothub_thread, ohci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >= 0) - return 0; - - - MOD_DEC_USE_COUNT; - retval = pid; - } - - release_ohci(ohci); - return retval; -} - -static int start_ohci(struct pci_dev *dev) -{ - unsigned int mem_base = dev->base_address[0]; - - /* If its OHCI, its memory */ - if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) - return -ENODEV; - - /* Get the memory address and map it for IO */ - mem_base &= PCI_BASE_ADDRESS_MEM_MASK; - - /* - * FIXME ioremap_nocache isn't implemented on all CPUs (such - * as the Alpha) [?] What should I use instead... - * - * The iounmap() is done on in release_ohci. - */ - mem_base = (unsigned int) ioremap_nocache(mem_base, 4096); - - if (!mem_base) { - printk("Error mapping OHCI memory\n"); - return -EFAULT; - } - - return found_ohci(dev->irq, (void *) mem_base); -} - - - -#ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) -{ - static int down = 0; - - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - if (down) { - printk(KERN_DEBUG "ohci: received extra suspend event\n"); - break; - } - down = 1; - break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - if (!down) { - printk(KERN_DEBUG "ohci: received bogus resume event\n"); - break; - } - down = 0; - if (waitqueue_active(&root_hub)) { - apm_resume = 1; - wake_up(&root_hub); - } - break; - } - return 0; -} -#endif - - - int usb_mouse_init(void); -#ifdef MODULE - -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -} - -#define ohci_hcd_init init_module - -#endif - -#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 -#define PCI_CLASS_SERIAL_USB_OHCI_PG 0x10 - - -int ohci_hcd_init(void) -{ - int retval; - struct pci_dev *dev = NULL; - - retval = -ENODEV; - - dev = NULL; - while((dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev))) { /* OHCI */ - retval = start_ohci(dev); - if (retval < 0) break; - - -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif -#ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); -#endif - - return 0; - } - return retval; -} - -void cleanup_drivers(void) -{ - hub_cleanup(); -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); -#endif -} - diff --git a/drivers/usb/ohci-hcd.h b/drivers/usb/ohci-hcd.h deleted file mode 100644 index aa782bb1df7f..000000000000 --- a/drivers/usb/ohci-hcd.h +++ /dev/null @@ -1,404 +0,0 @@ - /* - * OHCI HCD (Host Controller Driver) for USB. - * - * (C) Copyright 1999 Roman Weissgaerber - * - * The OHCI HCD layer is a simple but nearly complete implementation of what the - * USB people would call a HCD for the OHCI. - * (ISO comming soon, Bulk disabled, INT u. CTRL transfers enabled) - * The layer on top of it, is for interfacing to the alternate-usb device-drivers. - * - * [ This is based on Linus' UHCI code and gregs OHCI fragments (0.03c source tree). ] - * [ Open Host Controller Interface driver for USB. ] - * [ (C) Copyright 1999 Linus Torvalds (uhci.c) ] - * [ (C) Copyright 1999 Gregory P. Smith ] - * [ $Log: ohci.c,v $ ] - * [ Revision 1.1 1999/04/05 08:32:30 greg ] - * - * - * v2.1 1999/05/09 ep_addr correction, code clean up - * v2.0 1999/05/04 - * v1.0 1999/04/27 - * ohci-hcd.h - */ - -#include - -#ifdef CONFIG_USB_OHCI_VROOTHUB -#define VROOTHUB -#endif -/* enables virtual root hub - * (root hub will be managed by the hub controller - * hub.c of the alternate usb driver) - * last time I did more testing without virtual root hub - * -> the virtual root hub could be more unstable now */ - - - -#ifdef OHCI_DBG -#define OHCI_DEBUG(X) X -#else -#define OHCI_DEBUG(X) -#endif - -/* for readl writel functions */ -#include -#include - -/* for ED and TD structures */ - -typedef void * __OHCI_BAG; -typedef int (*f_handler )(void * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void *data, int data_len, int status, __OHCI_BAG lw0, __OHCI_BAG lw1); - - - -struct ep_address { - __u8 ep; /* bit 7: IN/-OUT, 6,5: type 10..CTRL 00..ISO 11..BULK 10..INT, 3..0: ep nr */ - __u8 fa; /* function address */ - __u8 hc; - __u8 host; -}; - -union ep_addr_ { - unsigned int iep; - struct ep_address bep; -}; - -/* - * ED and TD descriptors has to be 16-byte aligned - */ -struct ohci_hw_ed { - __u32 info; - __u32 tail_td; /* TD Queue tail pointer */ - __u32 head_td; /* TD Queue head pointer */ - __u32 next_ed; /* Next ED */ -} __attribute((aligned(16))); - - -struct usb_ohci_ed { - struct ohci_hw_ed hw; - /* struct ohci * ohci; */ - f_handler handler; - union ep_addr_ ep_addr; - struct usb_ohci_ed *ed_list; - struct usb_ohci_ed *ed_prev; -} __attribute((aligned(32))); - - /* OHCI Hardware fields */ -struct ohci_hw_td { - __u32 info; - __u32 cur_buf; /* Current Buffer Pointer */ - __u32 next_td; /* Next TD Pointer */ - __u32 buf_end; /* Memory Buffer End Pointer */ -} __attribute((aligned(16))); - -/* TD info field */ -#define TD_CC 0xf0000000 -#define TD_CC_GET(td_p) ((td_p >>28) & 0x04) -#define TD_EC 0x0C000000 -#define TD_T 0x03000000 -#define TD_T_DATA0 0x02000000 -#define TD_T_DATA1 0x03000000 -#define TD_T_TOGGLE 0x00000000 -#define TD_R 0x00040000 -#define TD_DI 0x00E00000 -#define TD_DI_SET(X) (((X) & 0x07)<< 21) -#define TD_DP 0x00180000 -#define TD_DP_SETUP 0x00000000 -#define TD_DP_IN 0x00100000 -#define TD_DP_OUT 0x00080000 - -/* CC Codes */ -#define TD_CC_NOERROR 0x00 -#define TD_CC_CRC 0x01 -#define TD_CC_BITSTUFFING 0x02 -#define TD_CC_DATATOGGLEM 0x03 -#define TD_CC_STALL 0x04 -#define TD_DEVNOTRESP 0x05 -#define TD_PIDCHECKFAIL 0x06 -#define TD_UNEXPECTEDPID 0x07 -#define TD_DATAOVERRUN 0x08 -#define TD_DATAUNDERRUN 0x09 -#define TD_BUFFEROVERRUN 0x0C -#define TD_BUFFERUNDERRUN 0x0D -#define TD_NOTACCESSED 0x0F - - - -struct usb_ohci_td { - struct ohci_hw_td hw; - void * buffer_start; - f_handler handler; - struct usb_ohci_td *prev_td; - struct usb_ohci_ed *ep; - struct usb_ohci_td *next_dl_td; - __OHCI_BAG lw0; - __OHCI_BAG lw1; -} __attribute((aligned(32))); - - - -/* TD types */ -#define BULK 0x03 -#define INT 0x01 -#define CTRL 0x02 -#define ISO 0x00 -/* TD types with direction */ -#define BULK_IN 0x07 -#define BULK_OUT 0x03 -#define INT_IN 0x05 -#define INT_OUT 0x01 -#define CTRL_IN 0x06 -#define CTRL_OUT 0x02 -#define ISO_IN 0x04 -#define ISO_OUT 0x00 - -struct ohci_rep_td { - int cmd_len; - void * cmd; - void * data; - int data_len; - f_handler handler; - struct ohci_rep_td *next_td; - int ep_addr; - __OHCI_BAG lw0; - __OHCI_BAG lw1; - __u32 status; -} __attribute((aligned(32))); - -#define OHCI_ED_SKIP (1 << 14) -#define OHCI_ED_MPS (0x7ff << 16) -#define OHCI_ED_F_NORM (0) -#define OHCI_ED_F_ISOC (1 << 15) -#define OHCI_ED_S_LOW (1 << 13) -#define OHCI_ED_S_HIGH (0) -#define OHCI_ED_D (3 << 11) -#define OHCI_ED_D_IN (2 << 11) -#define OHCI_ED_D_OUT (1 << 11) -#define OHCI_ED_EN (0xf << 7) -#define OHCI_ED_FA (0x7f) - - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 done_head; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute((aligned(256))); - - - -#define ED_INT_1 1 -#define ED_INT_2 2 -#define ED_INT_4 4 -#define ED_INT_8 8 -#define ED_INT_16 16 -#define ED_INT_32 32 -#define ED_CONTROL 64 -#define ED_BULK 65 -#define ED_ISO 0 /* same as 1ms interrupt queue */ - - -/* - * This is the maximum number of root hub ports. I don't think we'll - * ever see more than two as that's the space available on an ATX - * motherboard's case, but it could happen. The OHCI spec allows for - * up to 15... (which is insane!) - * - * Although I suppose several "ports" could be connected directly to - * internal laptop devices such as a keyboard, mouse, camera and - * serial/parallel ports. hmm... That'd be neat. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 donehead; - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - - -/* - * Read a MMIO register and re-write it after ANDing with (m) - */ -#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) ) - -/* - * Read a MMIO register and re-write it after ORing with (b) - */ -#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) ) - -/* - * cmdstatus register */ -#define OHCI_CLF 0x02 -#define OHCI_BLF 0x04 - -/* - * Interrupt register masks - */ -#define OHCI_INTR_SO (1) -#define OHCI_INTR_WDH (1 << 1) -#define OHCI_INTR_SF (1 << 2) -#define OHCI_INTR_RD (1 << 3) -#define OHCI_INTR_UE (1 << 4) -#define OHCI_INTR_FNO (1 << 5) -#define OHCI_INTR_RHSC (1 << 6) -#define OHCI_INTR_OC (1 << 30) -#define OHCI_INTR_MIE (1 << 31) - -/* - * Control register masks - */ -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ - - -struct ohci { - int irq; - struct ohci_regs *regs; /* OHCI controller's memory */ - struct ohci_hc_area *hc_area; /* hcca, int ed-tree, ohci itself .. */ - int root_hub_funct_addr; /* Address of Root Hub endpoint */ - int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load ballancing)*/ - struct usb_ohci_ed * ed_rm_list; /* list of all endpoints to be removed */ - struct usb_ohci_ed * ed_bulktail; /* last endpoint of bulk list */ - struct usb_ohci_ed * ed_controltail; /* last endpoint of control list */ - struct usb_ohci_ed * ed_isotail; /* last endpoint of iso list */ - struct ohci_device * root_hub; - struct usb_ohci_ed ed_rh_ep0; - struct usb_ohci_ed ed_rh_epi; - struct ohci_rep_td *td_rh_epi; - int intrstatus; - struct usb_ohci_ed *ed_func_ep0[128]; /* "hash"-table for ep to ed mapping */ - struct ohci_rep_td *repl_queue; /* for internal requests */ - int rh_int_interval; - int rh_int_timer; - struct usb_bus *bus; - - -}; - -/* - * Warning: This constant must not be so large as to cause the - * ohci_device structure to exceed one 4096 byte page. Or "weird - * things will happen" as the alloc_ohci() function assumes that - * its less than one page at the moment. (FIXME) - */ -#define NUM_TDS 4 /* num of preallocated transfer descriptors */ -#define NUM_EDS 80 /* num of preallocated endpoint descriptors */ - -struct ohci_hc_area { - - struct ohci_hcca hcca; /* OHCI mem. mapped IO area 256 Bytes*/ - - struct ohci_hw_ed ed[NUM_EDS]; /* Endpoint Descriptors 80 * 16 : 1280 Bytes */ - struct ohci_hw_td td[NUM_TDS]; /* Transfer Descriptors 2 * 32 : 64 Bytes */ - struct ohci ohci; - -}; -struct ohci_device { - struct usb_device *usb; - struct ohci *ohci; - unsigned long data[16]; -}; - -#define ohci_to_usb(uhci) ((ohci)->usb) -#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) - -/* Debugging code */ -/*void show_ed(struct ohci_ed *ed); -void show_td(struct ohci_td *td); -void show_status(struct ohci *ohci); */ - -/* hcd */ -int ohci_trans_req(struct ohci * ohci, unsigned int ep_addr, int cmd_len, void *cmd, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1); -struct usb_ohci_ed *usb_ohci_add_ep(struct ohci * ohci, unsigned int ep_addr, int interval, int load, f_handler handler, int ep_size, int speed); -int usb_ohci_rm_function(struct ohci * ohci, unsigned int ep_addr); -int usb_ohci_rm_ep(struct ohci * ohci, struct usb_ohci_ed *ed); -struct usb_ohci_ed * ohci_find_ep(struct ohci *ohci, unsigned int ep_addr_in); - -/* roothub */ -int ohci_del_rh_int_timer(struct ohci * ohci); -int ohci_init_rh_int_timer(struct ohci * ohci, int interval); -int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); -int root_hub_send_irq(struct ohci * ohci, void * data, int data_len ); -int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); -int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler); -int send_replies(struct ohci * ohci); - - - - -/* Root-Hub Register info */ - -#define RH_PS_CCS 0x00000001 -#define RH_PS_PES 0x00000002 -#define RH_PS_PSS 0x00000004 -#define RH_PS_POCI 0x00000008 -#define RH_PS_PRS 0x00000010 -#define RH_PS_PPS 0x00000100 -#define RH_PS_LSDA 0x00000200 -#define RH_PS_CSC 0x00010000 -#define RH_PS_PESC 0x00020000 -#define RH_PS_PSSC 0x00040000 -#define RH_PS_OCIC 0x00080000 -#define RH_PS_PRSC 0x00100000 - - -#ifdef OHCI_DBG -#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d\n", -- __ohci_free_cnt) -#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("OHCI ALLO: %d\n", ++ __ohci_free_cnt) -#define USB_FREE(x) kfree(x); printk("USB FREE: %d\n", -- __ohci_free1_cnt) -#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL); printk("USB ALLO: %d\n", ++ __ohci_free1_cnt) -static int __ohci_free_cnt = 0; -static int __ohci_free1_cnt = 0; -#else -#define OHCI_FREE(x) kfree(x) -#define OHCI_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) -#define USB_FREE(x) kfree(x) -#define USB_ALLOC(x,size) (x) = kmalloc(size, GFP_KERNEL) -#endif - diff --git a/drivers/usb/ohci-root-hub.c b/drivers/usb/ohci-root-hub.c deleted file mode 100644 index 6d1018eff575..000000000000 --- a/drivers/usb/ohci-root-hub.c +++ /dev/null @@ -1,604 +0,0 @@ -/* - * HCD (OHCI) Virtual Root Hub for USB. - * - * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) - * - * The Root Hub is build into the HC (UHCI or OHCI) hardware. - * This piece of code lets it look like it resides on the usb - * like the other hubs. - * (for anyone who wants to do a control operation on the root hub) - * - * v2.1 1999/05/09 - * v2.0 1999/05/04 - * v1.0 1999/04/27 - * ohci-root-hub.c - * - */ - - - -#include -#include -#include -#include -#include -#include - -#include "usb.h" -#include "ohci-hcd.h" - -#ifdef VROOTHUB - -#include "ohci-root-hub.h" - - -static __u8 root_hub_dev_des[] = -{ - 0x12, /* __u8 bLength; */ - 0x01, /* __u8 bDescriptorType; Device */ - 0x00, /* __u16 bcdUSB; v1.0 */ - 0x01, - 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ - 0x00, /* __u8 bDeviceSubClass; */ - 0x00, /* __u8 bDeviceProtocol; */ - 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ - 0x00, /* __u16 idVendor; */ - 0x00, - 0x00, /* __u16 idProduct; */ - 0x00, - 0x00, /* __u16 bcdDevice; */ - 0x00, - 0x00, /* __u8 iManufacturer; */ - 0x00, /* __u8 iProduct; */ - 0x00, /* __u8 iSerialNumber; */ - 0x01 /* __u8 bNumConfigurations; */ -}; - - -/* Configuration descriptor */ -static __u8 root_hub_config_des[] = -{ - 0x09, /* __u8 bLength; */ - 0x02, /* __u8 bDescriptorType; Configuration */ - 0x19, /* __u16 wTotalLength; */ - 0x00, - 0x01, /* __u8 bNumInterfaces; */ - 0x01, /* __u8 bConfigurationValue; */ - 0x00, /* __u8 iConfiguration; */ - 0x40, /* __u8 bmAttributes; - Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ - 0x00, /* __u8 MaxPower; */ - - /* interface */ - 0x09, /* __u8 if_bLength; */ - 0x04, /* __u8 if_bDescriptorType; Interface */ - 0x00, /* __u8 if_bInterfaceNumber; */ - 0x00, /* __u8 if_bAlternateSetting; */ - 0x01, /* __u8 if_bNumEndpoints; */ - 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ - 0x00, /* __u8 if_bInterfaceSubClass; */ - 0x00, /* __u8 if_bInterfaceProtocol; */ - 0x00, /* __u8 if_iInterface; */ - - /* endpoint */ - 0x07, /* __u8 ep_bLength; */ - 0x05, /* __u8 ep_bDescriptorType; Endpoint */ - 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ - 0x03, /* __u8 ep_bmAttributes; Interrupt */ - 0x40, /* __u16 ep_wMaxPacketSize; 64 Bytes */ - 0x00, - 0xff /* __u8 ep_bInterval; 255 ms */ -}; - -/* -For OHCI we need just the 2nd Byte, so we -don't need this constant byte-array - -static __u8 root_hub_hub_des[] = -{ - 0x00, * __u8 bLength; * - 0x29, * __u8 bDescriptorType; Hub-descriptor * - 0x02, * __u8 bNbrPorts; * - 0x00, * __u16 wHubCharacteristics; * - 0x00, - 0x01, * __u8 bPwrOn2pwrGood; 2ms * - 0x00, * __u8 bHubContrCurrent; 0 mA * - 0x00, * __u8 DeviceRemovable; *** 8 Ports max *** * - 0xff * __u8 PortPwrCtrlMask; *** 8 ports max *** * -}; -*/ - - -int root_hub_control_msg(struct ohci *ohci, int cmd_len, void *rh_cmd, void *rh_data, int leni, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) -{ - - __u32 stat; - __u32 rep_handler; - int req_reply=0; - union ep_addr_ ep_addr; - union ep_addr_ ep_addr_ret; - __u8 * cmd = rh_cmd; - __u8 * data = rh_data; - int i; - int len =leni; - - __u8 bmRequestType = cmd[0]; - __u8 bRequest = cmd[1]; - __u16 wValue = cmd[3] << 8 | cmd [2]; - __u16 wIndex = cmd[5] << 8 | cmd [4]; - __u16 wLength = cmd[7] << 8 | cmd [6]; -printk("USB root hub: adr: %8x cmd(%8x): ", ohci->root_hub_funct_addr, 8); -for(i=0;i<8;i++) - printk("%2x", ((char *)rh_cmd)[i]); - -printk(" ; \n"); - - ep_addr_ret.iep = 0; - ep_addr_ret.bep.fa = ohci->root_hub_funct_addr; - ep_addr_ret.bep.ep = (bmRequestType & 0x80) | 0x40; - - switch (bmRequestType | bRequest << 8) { - /* Request Destination: - without flags: Device, - RH_INTERFACE: interface, - RH_ENDPOINT: endpoint, - RH_CLASS means HUB here, - RH_OTHER | RH_CLASS almost ever means HUB_PORT here - */ - - case RH_GET_STATUS: - len = 2; - data[0] = 0x01; - data[1] = 0x00; - req_reply = RH_ACK; - break; - case RH_GET_STATUS | RH_INTERFACE: - len = 2; - data[0] = 0x00; - data[1] = 0x00; - req_reply = RH_ACK; - break; - case RH_GET_STATUS | RH_ENDPOINT: - len = 2; - data[0] = 0x00; - data[1] = 0x00; - req_reply = RH_ACK; - break; - case RH_GET_STATUS | RH_CLASS: /* HUB_STATUS */ - stat = readl(&ohci->regs->roothub.status) & 0x7fff7fff; /* bit 31 u. 15 has other meaning */ - data[0] = stat & 0xff; - data[1] = (stat >> 8) & 0xff; - data[2] = (stat >> 16) & 0xff; - data[3] = (stat >> 24) & 0xff; - len = 4; - req_reply = RH_ACK; - break; - case RH_GET_STATUS | RH_OTHER | RH_CLASS: /* PORT_STATUS */ - stat = readl(&ohci->regs->roothub.portstatus[wIndex-1]); - data[0] = stat & 0xff; - data[1] = (stat >> 8) & 0xff; - data[2] = (stat >> 16) & 0xff; - data[3] = (stat >> 24) & 0xff; - len = 4; - req_reply = RH_ACK; - printk("rh: stat %4x wIndex %4x;\n", stat , wIndex); - break; - - case RH_CLEAR_FEATURE: - switch (wValue) { - case (RH_DEVICE_REMOTE_WAKEUP): - default: - } - break; - - case RH_CLEAR_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - len=0; - req_reply = RH_ACK; - break; - default: - } - break; - - case RH_CLEAR_FEATURE | RH_CLASS: - switch (wValue) { - /* case (RH_C_HUB_LOCAL_POWER): OHCI says: no switching of this one */ - case (RH_C_HUB_OVER_CURRENT): - writel(RH_PS_OCIC, &ohci->regs->roothub.status); - len=0; - req_reply = RH_ACK; - break; - default: - } - break; - case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_ENABLE): - writel(RH_PS_CCS, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_PORT_SUSPEND): - writel(RH_PS_POCI, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_PORT_POWER): - writel(RH_PS_LSDA, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_C_PORT_CONNECTION): - writel(RH_PS_CSC, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_C_PORT_ENABLE): - writel(RH_PS_PESC, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_C_PORT_SUSPEND): - writel(RH_PS_PSSC, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_C_PORT_OVER_CURRENT): - writel(RH_PS_OCIC, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_C_PORT_RESET): - writel(RH_PS_PRSC, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - /* - case (RH_PORT_CONNECTION): - case (RH_PORT_OVER_CURRENT): - case (RH_PORT_RESET): - case (RH_PORT_LOW_SPEED): - */ - default: - } - break; - case RH_SET_FEATURE: - switch (wValue) { - case (RH_DEVICE_REMOTE_WAKEUP): - default: - } - break; - - case RH_SET_FEATURE | RH_ENDPOINT: - switch (wValue) { - case (RH_ENDPOINT_STALL): - default: - } - break; - - case RH_SET_FEATURE | RH_CLASS: - switch (wValue) { - /* case (RH_C_HUB_LOCAL_POWER): Root Hub has no local Power - case (RH_C_HUB_OVER_CURRENT): */ - default: - } - break; - case RH_SET_FEATURE | RH_OTHER | RH_CLASS: - switch (wValue) { - case (RH_PORT_SUSPEND): - writel(RH_PS_PSS, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_PORT_RESET): - if((readl(&ohci->regs->roothub.portstatus[wIndex-1]) &1) != 0) /* BUG IN HUP CODE *********/ - writel(RH_PS_PRS, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_PORT_POWER): - writel(RH_PS_PPS, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - case (RH_PORT_ENABLE): - writel(RH_PS_PES, &ohci->regs->roothub.portstatus[wIndex-1]); - len=0; - req_reply = RH_ACK; - break; - /* - case (RH_PORT_CONNECTION): - case (RH_PORT_OVER_CURRENT): - case (RH_PORT_LOW_SPEED): - case (RH_C_PORT_CONNECTION): - case (RH_C_PORT_ENABLE): - case (RH_C_PORT_SUSPEND): - case (RH_C_PORT_OVER_CURRENT): - case (RH_C_PORT_RESET): - */ - default: - } - break; - - case RH_SET_ADDRESS: - ohci->root_hub_funct_addr = wValue; - /* ohci->ed_func_ep0[wValue] = &ohci->ed_rh_ep0; - ohci->ed_func_ep0[wValue]->ep_addr.bep.fa = wValue; - ohci->ed_func_ep0[wValue]->ed_list = &ohci->ed_rh_epi; */ - ohci->ed_rh_epi.ed_list = NULL; - ohci->ed_rh_epi.ep_addr.bep.fa = wValue; - ohci->ed_rh_epi.ep_addr.bep.ep = 0xa1; /* Int in port 1 */ - ohci->ed_func_ep0[0]= NULL; - len = 0; - req_reply = RH_ACK; - break; - - case RH_GET_DESCRIPTOR: - switch ((wValue & 0xff00) >> 8) { - case (0x01): /* device descriptor */ - len = min(sizeof(root_hub_dev_des), wLength); - memcpy(data, root_hub_dev_des, len); - req_reply = RH_ACK; - break; - case (0x02): /* configuration descriptor */ - len = min(sizeof(root_hub_config_des), wLength); - memcpy(data, root_hub_config_des, len); - req_reply = RH_ACK; - break; - case (0x03): /* string descriptors */ - default: - } - break; - case RH_GET_DESCRIPTOR | RH_CLASS: - data[1] = 0x29; - stat = readl(&ohci->regs->roothub.a); - data[2] = stat & 0xff; /* number of ports */ - data[0] = (data[2] / 8) * 2 + 9; /* length of descriptor */ - if(data[0] > wLength) { - req_reply = RH_REQ_ERR; - break; - } - data[3] = (stat >> 8) & 0xff; - data[4] = (stat >> 16) & 0xff; - data[5] = (stat >> 24) & 0xff; - data[6] = 0; /* Root Hub needs no current from bus */ - stat = readl(&ohci->regs->roothub.b); - if(data[2] <= 8) { /* less than 8 Ports */ - data[7] = stat & 0xff; - data[8] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1 ?, stat >> 16 for USB Rev. 1.0 */ - } - else { - data[7] = stat & 0xff; - data[8] = (stat >> 8) & 0xff; - data[9] = (stat >> 16) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 16 for USB Rev. 1.0 */ - data[10] = (stat >> 24) & 0xff; /* 0xff for USB Rev. 1.1?, stat >> 24 for USB Rev. 1.0 */ - } - len = data[0]; - req_reply = RH_ACK; - break; - - case RH_SET_DESCRIPTOR: - break; - - case RH_GET_CONFIGURATION: - len = 1; - data[0] = 0x01; - req_reply = RH_ACK; - break; - - case RH_SET_CONFIGURATION: /* start it up */ - writel( 0x10000, &ohci->regs->roothub.status); - /*writel( OHCI_INTR_RHSC, &ohci->regs->intrenable);*/ - len = 0; - req_reply = RH_ACK; - break; - /* Optional or meaningless requests - case RH_GET_STATE | RH_OTHER | RH_CLASS: - case RH_GET_INTERFACE | RH_INTERFACE: - case RH_SET_INTERFACE | RH_INTERFACE: - case RH_SYNC_FRAME | RH_ENDPOINT: - */ - - /* Vendor Requests, we are the vendor! - Will the USB-Consortium give us a Vendor Id - for a virtual hub-device :-) ? - We could use these requests for configuration purposes on the HCD Driver, not used in the altenate usb !*/ - - case RH_SET_FEATURE | RH_VENDOR: /* remove all endpoints of device wIndex = Dev << 8 */ - switch(wValue) { - case RH_REMOVE_EP: - ep_addr.iep = 0; - ep_addr.bep.ep = wIndex & 0xff; - ep_addr.bep.fa = (wIndex << 8) & 0xff00; - usb_ohci_rm_function(ohci, ep_addr.iep); - len=0; - req_reply = RH_ACK; - break; - } - break; - case RH_SET_FEATURE | RH_ENDPOINT | RH_VENDOR: /* remove endpoint wIndex = Dev << 8 | EP */ - switch(wValue) { - case RH_REMOVE_EP: - ep_addr.iep = 0; - ep_addr.bep.ep = wIndex & 0xff; - ep_addr.bep.fa = (wIndex << 8) & 0xff00; - usb_ohci_rm_ep(ohci, ohci_find_ep(ohci, ep_addr.iep)); - len=0; - req_reply = RH_ACK; - break; - } - break; - case RH_SET_EP | RH_ENDPOINT | RH_VENDOR: - ep_addr.bep.ep = data[0]; - ep_addr.bep.fa = data[1]; - ep_addr.bep.hc = data[2]; - ep_addr.bep.host = data[3]; - rep_handler = data[7] << 24 |data[6] << 16 | data[5] << 8 | data[4]; - /* struct usb_ohci_ed *usb_ohci_add_ep(union ep_addr_ ep_addr, - struct ohci * ohci, int interval, int load, int (*handler)(int, void*), int ep_size, int speed) */ - usb_ohci_add_ep(ohci, ep_addr.iep, data[8], data[9], (f_handler) rep_handler, data[11] << 8 | data[10] , data[12]); - len=0; - req_reply = RH_ACK; - break; - - default: - } - printk("USB HC roothubstat1: %x \n", readl( &(ohci->regs->roothub.portstatus[0]) )); - printk("USB HC roothubstat2: %x \n", readl( &(ohci->regs->roothub.portstatus[1]) )); - - /* if (req_reply == RH_ACK) len; */ - queue_reply(ohci, ep_addr_ret.iep, 8, rh_cmd, data, len, lw0, lw1, handler); - return 0; -} - -int root_hub_int_req(struct ohci * ohci, int cmd_len, void * ctrl, void * data, int data_len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler){ - - struct ohci_rep_td *td; - struct ohci_rep_td *tmp; - union ep_addr_ ep_addr; - - td = kmalloc(sizeof(td),GFP_KERNEL); - tmp = ohci->td_rh_epi; - td->next_td = NULL; - if(tmp == NULL) { /* queue td */ - ohci->td_rh_epi = td; - } - else { - while(tmp->next_td != NULL) tmp = tmp->next_td; - tmp->next_td = td; - } - ep_addr.iep = 0; - ep_addr.bep.fa = ohci->root_hub_funct_addr; - ep_addr.bep.ep = 0xA1; /* INT IN EP endpoint 1 */ - td->cmd_len = 0; - td->cmd = NULL; - td->data = data; - td->data_len = data_len; - td->handler = handler; - td->next_td = NULL; - td->ep_addr = ep_addr.iep; - td->lw0 = lw0; - td->lw1 = lw1; - ohci_init_rh_int_timer(ohci, 255); - return 0; -} - - -/* prepare Interrupt pipe transaction data; HUP INTERRUPT ENDPOINT */ -int root_hub_send_irq(struct ohci * ohci, void * rh_data, int rh_len ) { - - int num_ports; - int i; - int ret; - int len; - - __u8 * data = rh_data; - - num_ports = readl(&ohci->regs->roothub.a) & 0xff; - data[0] = (readl(&ohci->regs->roothub.status) & 0x00030000)>0?1:0; - ret = data[0]; - - for(i=0; i < num_ports; i++) { - data[i/8] |= ((readl(&ohci->regs->roothub.portstatus[i]) & 0x001f0000)>0?1:0) << ((i+1) % 8); - ret += data[i/8]; - } - len = i/8 + 1; - - if (ret > 0) return len; - - return RH_NACK; -} - - - - - -static struct timer_list rh_int_timer; - -/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ -static void rh_int_timer_do(unsigned long ptr) { - int len; - int interval; - struct ohci * ohci = (struct ohci *) ptr; - struct ohci_rep_td *td = ohci->td_rh_epi; - - if(td != NULL) { /* if ther is a TD handle the INT request */ - - len = root_hub_send_irq(ohci, td->data, td->data_len ); - if(len > 0) { - ohci->td_rh_epi = td->next_td; - td->next_td = ohci->repl_queue; - ohci->repl_queue = td; - send_replies(ohci); - } - } - interval = ohci->rh_int_interval; - init_timer(& rh_int_timer); - rh_int_timer.function = rh_int_timer_do; - rh_int_timer.data = (unsigned long) ohci; - rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000; - add_timer(&rh_int_timer); -} - -/* Root Hub INTs are polled by this timer */ -int ohci_init_rh_int_timer(struct ohci * ohci, int interval) { - - if(!(ohci->rh_int_timer)) { - ohci->rh_int_timer = 1; - ohci->rh_int_interval = interval; - init_timer(& rh_int_timer); - rh_int_timer.function = rh_int_timer_do; - rh_int_timer.data = (unsigned long) ohci; - rh_int_timer.expires = jiffies + (HZ * (interval<30?30: interval)) /1000; - add_timer(&rh_int_timer); - } - return 0; -} - -int ohci_del_rh_int_timer(struct ohci * ohci) { - del_timer(&rh_int_timer); - return 0; -} -/* for root hub replies, queue the reply, (it will be sent immediately now) */ - -int queue_reply(struct ohci * ohci, unsigned int ep_addr, int cmd_len,void * cmd, void * data,int len, __OHCI_BAG lw0, __OHCI_BAG lw1, f_handler handler) { - - struct ohci_rep_td *td; - int status = 0; - -printk("queue_reply ep: %x len: %x\n", ep_addr, len); -td = kmalloc(sizeof(td), GFP_KERNEL); - - if (len < 0) { status = len; len = 0;} - td->cmd_len = cmd_len; - td->cmd = cmd; - td->data = data; - td->data_len = len; - td->handler = handler; - td->next_td = ohci->repl_queue; ohci->repl_queue = td; - td->ep_addr = ep_addr; - td->lw0 = lw0; - td->lw1 = lw1; - td->status = status; - send_replies(ohci); -return 0; -} - -/* for root hub replies; send the reply */ -int send_replies(struct ohci * ohci) { - struct ohci_rep_td *td; - struct ohci_rep_td *tmp; - - td = ohci->repl_queue; ohci->repl_queue = NULL; - while ( td != NULL) { - td->handler((void *) ohci, td->ep_addr,td->cmd_len,td->cmd, td->data, td->data_len, td->status, td->lw0, td->lw1); - tmp = td; - td = td->next_td; - - kfree(tmp); - } - return 0; -} - -#endif diff --git a/drivers/usb/ohci-root-hub.h b/drivers/usb/ohci-root-hub.h deleted file mode 100644 index 8f1e80998278..000000000000 --- a/drivers/usb/ohci-root-hub.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * HCD (OHCI) Virtual Root Hub Protocol for USB. - * - * (C) Copyright 1999 Roman Weissgaerber (weissg@vienna.at) - * - * The Root Hub is build into the HC (UHCI or OHCI) hardware. - * This piece of code lets it look like it resides on the bus - * like the other hubs. - * (for anyone who wants to do a control operation on the root hub) - * - * v1.0 1999/04/27 - * ohci-root-hub.h - * - */ - -/* destination of request */ -#define RH_INTERFACE 0x01 -#define RH_ENDPOINT 0x02 -#define RH_OTHER 0x03 - -#define RH_CLASS 0x20 -#define RH_VENDOR 0x40 - -/* Requests: bRequest << 8 | bmRequestType */ -#define RH_GET_STATUS 0x0080 -#define RH_CLEAR_FEATURE 0x0100 -#define RH_SET_FEATURE 0x0300 -#define RH_SET_ADDRESS 0x0500 -#define RH_GET_DESCRIPTOR 0x0680 -#define RH_SET_DESCRIPTOR 0x0700 -#define RH_GET_CONFIGURATION 0x0880 -#define RH_SET_CONFIGURATION 0x0900 -#define RH_GET_STATE 0x0280 -#define RH_GET_INTERFACE 0x0A80 -#define RH_SET_INTERFACE 0x0B00 -#define RH_SYNC_FRAME 0x0C80 -/* Our Vendor Specific Request */ -#define RH_SET_EP 0x2000 - - -/* Hub port features */ -#define RH_PORT_CONNECTION 0x00 -#define RH_PORT_ENABLE 0x01 -#define RH_PORT_SUSPEND 0x02 -#define RH_PORT_OVER_CURRENT 0x03 -#define RH_PORT_RESET 0x04 -#define RH_PORT_POWER 0x08 -#define RH_PORT_LOW_SPEED 0x09 -#define RH_C_PORT_CONNECTION 0x10 -#define RH_C_PORT_ENABLE 0x11 -#define RH_C_PORT_SUSPEND 0x12 -#define RH_C_PORT_OVER_CURRENT 0x13 -#define RH_C_PORT_RESET 0x14 - -/* Hub features */ -#define RH_C_HUB_LOCAL_POWER 0x00 -#define RH_C_HUB_OVER_CURRENT 0x01 - -#define RH_DEVICE_REMOTE_WAKEUP 0x00 -#define RH_ENDPOINT_STALL 0x01 - -/* Our Vendor Specific feature */ -#define RH_REMOVE_EP 0x00 - - -#define RH_ACK 0x01 -#define RH_REQ_ERR -1 -#define RH_NACK 0x00 - -#define min(a,b) (((a)<(b))?(a):(b)) - diff --git a/drivers/usb/ohci.c b/drivers/usb/ohci.c deleted file mode 100644 index b12d0114c690..000000000000 --- a/drivers/usb/ohci.c +++ /dev/null @@ -1,1669 +0,0 @@ -/* - * Open Host Controller Interface driver for USB. - * - * (C) Copyright 1999 Gregory P. Smith - * - * This is the "other" host controller interface for USB. You will - * find this on many non-Intel based motherboards, and of course the - * Mac. As Linus hacked his UHCI driver together first, I modeled - * this after his.. (it should be obvious) - * - * From the programming standpoint the OHCI interface seems a little - * prettier and potentially less CPU intensive. This remains to be - * proven. In reality, I don't believe it'll make one darn bit of - * difference. USB v1.1 is a slow bus by today's standards. - * - * OHCI hardware takes care of most of the scheduling of different - * transfer types with the correct prioritization for us. - * - * To get started in USB, I used the "Universal Serial Bus System - * Architecture" book by Mindshare, Inc. It was a reasonable introduction - * and overview of USB and the two dominant host controller interfaces - * however you're better off just reading the real specs available - * from www.usb.org as you'll need them to get enough detailt to - * actually implement a HCD. The book has many typos and omissions - * Beware, the specs are the victim of a committee. - * - * This code was written with Guinness on the brain, xsnow on the desktop - * and Orbital, Orb, Enya & Massive Attack on the CD player. What a life! ;) - * - * No filesystems were harmed in the development of this code. - * - * $Id: ohci.c,v 1.26 1999/05/11 07:34:47 greg Exp $ - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "ohci.h" -#include "inits.h" - -#ifdef CONFIG_APM -#include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; -#endif - -static struct wait_queue *ohci_configure = NULL; - -#ifdef OHCI_TIMER -static struct timer_list ohci_timer; /* timer for root hub polling */ -#endif - - -static int ohci_td_result(struct ohci_device *dev, struct ohci_td *td) -{ - unsigned int status; - - status = td->info & OHCI_TD_CC; - - /* TODO Debugging code for TD failures goes here */ - - return status; -} /* ohci_td_result() */ - - -static spinlock_t ohci_edtd_lock = SPIN_LOCK_UNLOCKED; - -/* - * Add a TD to the end of the TD list on a given ED. If td->next_td - * points to any more TDs, they will be added as well (naturally). - * Otherwise td->next_td must be 0. - * - * The SKIP flag will be cleared after this function. - * - * Important! This function needs locking and atomicity as it works - * in parallel with the HC's DMA. Locking ohci_edtd_lock while using - * the function is a must. - * - * This function can be called by the interrupt handler. - */ -static void ohci_add_td_to_ed(struct ohci_td *td, struct ohci_ed *ed) -{ - /* don't let the HC pull anything from underneath us */ - ed->status |= OHCI_ED_SKIP; - - if (ed_head_td(ed) == 0) { /* empty list, put it on the head */ - set_ed_head_td(ed, virt_to_bus(td)); - ed->tail_td = 0; - } else { - struct ohci_td *tail, *head; - head = (ed_head_td(ed) == 0) ? NULL : bus_to_virt(ed_head_td(ed)); - tail = (ed->tail_td == 0) ? NULL : bus_to_virt(ed->tail_td); - if (!tail) { /* no tail, single element list */ - td->next_td = head->next_td; - head->next_td = virt_to_bus(td); - ed->tail_td = virt_to_bus(td); - } else { /* append to the list */ - td->next_td = tail->next_td; - tail->next_td = virt_to_bus(td); - ed->tail_td = virt_to_bus(td); - } - } - - /* save the ED link in each of the TDs added */ - td->ed = ed; - while (td->next_td != 0) { - td = bus_to_virt(td->next_td); - td->ed = ed; - } - - /* turn off the SKIP flag */ - ed->status &= ~OHCI_ED_SKIP; -} /* ohci_add_td_to_ed() */ - - -inline void ohci_start_control(struct ohci *ohci) -{ - /* tell the HC to start processing the control list */ - writel(OHCI_CMDSTAT_CLF, &ohci->regs->cmdstatus); -} - -inline void ohci_start_bulk(struct ohci *ohci) -{ - /* tell the HC to start processing the bulk list */ - writel(OHCI_CMDSTAT_BLF, &ohci->regs->cmdstatus); -} - -inline void ohci_start_periodic(struct ohci *ohci) -{ - /* enable processing periodc transfers starting next frame */ - writel_set(OHCI_USB_PLE, &ohci->regs->control); -} - -inline void ohci_start_isoc(struct ohci *ohci) -{ - /* enable processing isoc. transfers starting next frame */ - writel_set(OHCI_USB_IE, &ohci->regs->control); -} - -/* - * Add an ED to the hardware register ED list pointed to by hw_listhead_p - */ -static void ohci_add_ed_to_hw(struct ohci_ed *ed, void* hw_listhead_p) -{ - __u32 listhead; - unsigned long flags; - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - listhead = readl(hw_listhead_p); - - /* if the list is not empty, insert this ED at the front */ - /* XXX should they go on the end? */ - if (listhead) { - ed->next_ed = listhead; - } - - /* update the hardware listhead pointer */ - writel(virt_to_bus(ed), hw_listhead_p); - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_add_ed() */ - - -/* - * Put another control ED on the controller's list - */ -void ohci_add_control_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_add_ed_to_hw(ed, &ohci->regs->ed_controlhead); - ohci_start_control(ohci); -} /* ohci_add_control_ed() */ - - -#if 0 -/* - * Put another control ED on the controller's list - */ -void ohci_add_periodic_ed(struct ohci *ohci, struct ohci_ed *ed, int period) -{ - ohci_add_ed_to_hw(ed, /* XXX */); - ohci_start_periodic(ohci); -} /* ohci_add_control_ed() */ -#endif - - -/* - * Remove an ED from the HC list whos bus headpointer is pointed to - * by hw_listhead_p - * - * Note that the SKIP bit is left on in the removed ED. - */ -void ohci_remove_ed_from_hw(struct ohci_ed *ed, __u32* hw_listhead_p) -{ - unsigned long flags; - struct ohci_ed *cur; - __u32 bus_ed = virt_to_bus(ed); - __u32 bus_cur; - - if (ed == NULL || !bus_ed) - return; - - /* tell the controller this skip ED */ - ed->status |= OHCI_ED_SKIP; - - bus_cur = readl(hw_listhead_p); - - if (bus_cur == 0) - return; /* the list is already empty */ - - cur = bus_to_virt(bus_cur); - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - /* if its the head ED, move the head */ - if (bus_cur == bus_ed) { - writel(cur->next_ed, hw_listhead_p); - } else if (cur->next_ed != 0) { - struct ohci_ed *prev; - - /* walk the list and unlink the ED if found */ - for (;;) { - prev = cur; - cur = bus_to_virt(cur->next_ed); - - if (virt_to_bus(cur) == bus_ed) { - /* unlink from the list */ - prev->next_ed = cur->next_ed; - break; - } - - if (cur->next_ed == 0) - break; - } - } - - /* clear any links from the ED for safety */ - ed->next_ed = 0; - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_remove_ed_from_hw() */ - -/* - * Remove an ED from the controller's control list. Note that the SKIP bit - * is left on in the removed ED. - */ -inline void ohci_remove_control_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_remove_ed_from_hw(ed, &ohci->regs->ed_controlhead); -} - -/* - * Remove an ED from the controller's bulk list. Note that the SKIP bit - * is left on in the removed ED. - */ -inline void ohci_remove_bulk_ed(struct ohci *ohci, struct ohci_ed *ed) -{ - ohci_remove_ed_from_hw(ed, &ohci->regs->ed_bulkhead); -} - - -/* - * Remove a TD from the given EDs TD list. - */ -static void ohci_remove_td_from_ed(struct ohci_td *td, struct ohci_ed *ed) -{ - unsigned long flags; - struct ohci_td *head_td; - - if ((td == NULL) || (ed == NULL)) - return; - - spin_lock_irqsave(&ohci_edtd_lock, flags); - - if (ed_head_td(ed) == 0) - return; - - /* set the "skip me bit" in this ED */ - ed->status |= OHCI_ED_SKIP; - - /* XXX Assuming this list will never be circular */ - - head_td = bus_to_virt(ed_head_td(ed)); - if (virt_to_bus(td) == ed_head_td(ed)) { - /* It's the first TD, remove it. */ - set_ed_head_td(ed, head_td->next_td); - } else { - struct ohci_td *prev_td, *cur_td; - - /* FIXME: collapse this into a nice simple loop :) */ - if (head_td->next_td != 0) { - prev_td = head_td; - cur_td = bus_to_virt(head_td->next_td); - for (;;) { - if (td == cur_td) { - /* remove it */ - prev_td->next_td = cur_td->next_td; - break; - } - if (cur_td->next_td == 0) - break; - prev_td = cur_td; - cur_td = bus_to_virt(cur_td->next_td); - } - } - } - - td->next_td = 0; /* remove the TDs links */ - td->ed = NULL; - - /* TODO return this TD to the pool of free TDs */ - - /* unset the "skip me bit" in this ED */ - ed->status &= ~OHCI_ED_SKIP; - - spin_unlock_irqrestore(&ohci_edtd_lock, flags); -} /* ohci_remove_td_from_ed() */ - - -/* - * Get a pointer (virtual) to an available TD from the given device's - * pool. - * - * Return NULL if none are left. - */ -static struct ohci_td *ohci_get_free_td(struct ohci_device *dev) -{ - int idx; - - for (idx=0; idx < NUM_TDS; idx++) { - if (!td_allocated(dev->td[idx])) { - struct ohci_td *new_td = &dev->td[idx]; - /* zero out the TD */ - memset(new_td, 0, sizeof(*new_td)); - /* mark the new TDs as unaccessed */ - new_td->info = OHCI_TD_CC_NEW; - /* mark it as allocated */ - allocate_td(new_td); - return new_td; - } - } - - printk("usb-ohci error: unable to allocate a TD\n"); - return NULL; -} /* ohci_get_free_td() */ - - -/* - * Initialize a TD - * - * dir = OHCI_TD_D_IN, OHCI_TD_D_OUT, or OHCI_TD_D_SETUP - * toggle = TOGGLE_AUTO, TOGGLE_DATA0, TOGGLE_DATA1 - */ -inline struct ohci_td *ohci_fill_new_td(struct ohci_td *td, int dir, int toggle, __u32 flags, void *data, __u32 len, void *dev_id, usb_device_irq completed) -{ - /* hardware fields */ - td->info = OHCI_TD_CC_NEW | - (dir & OHCI_TD_D) | - (toggle & OHCI_TD_DT) | - flags; - td->cur_buf = (data == NULL) ? 0 : virt_to_bus(data); - td->buf_end = (len == 0) ? 0 : td->cur_buf + len - 1; - - /* driver fields */ - td->data = data; - td->dev_id = dev_id; - td->completed = completed; - - return td; -} /* ohci_fill_new_td() */ - - -/********************************** - * OHCI interrupt list operations * - **********************************/ - -/* - * Request an interrupt handler for one "pipe" of a USB device. - * (this function is pretty minimal right now) - * - * At the moment this is only good for input interrupts. (ie: for a - * mouse or keyboard) - * - * Period is desired polling interval in ms. The closest, shorter - * match will be used. Powers of two from 1-32 are supported by OHCI. - */ -static int ohci_request_irq(struct usb_device *usb, unsigned int pipe, - usb_device_irq handler, int period, void *dev_id) -{ - struct ohci_device *dev = usb_to_ohci(usb); - struct ohci_td *td; - struct ohci_ed *interrupt_ed; /* endpoint descriptor for this irq */ - - /* - * Pick a good frequency endpoint based on the requested period - */ - interrupt_ed = &dev->ohci->root_hub->ed[ms_to_ed_int(period)]; - - /* - * Set the max packet size, device speed, endpoint number, usb - * device number (function address), and type of TD. - * - * FIXME: Isochronous transfers need a pool of special 32 byte - * TDs (32 byte aligned) in order to be supported. - */ - interrupt_ed->status = \ - ed_set_maxpacket(usb_maxpacket(pipe)) | - ed_set_speed(usb_pipeslow(pipe)) | - usb_pipe_endpdev(pipe) | - OHCI_ED_F_NORM; - - td = ohci_get_free_td(dev); - /* FIXME: check for NULL */ - - /* Fill in the TD */ - ohci_fill_new_td(td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_AUTO, - OHCI_TD_ROUND, - dev->data, DATA_BUF_LEN, - dev_id, handler); - /* - * TODO: be aware that OHCI won't advance out of the 4kb - * page cur_buf started in. It'll wrap around to the start - * of the page... annoying or useful? you decide. - * - * We should make sure dev->data doesn't cross a page... - */ - - /* FIXME: this just guarantees that its the end of the list */ - td->next_td = 0; - - /* Linus did this. see asm/system.h; scary concept... I don't - * know if its needed here or not but it won't hurt. */ - wmb(); - - /* - * Put the TD onto our ED - */ - { - unsigned long flags; - spin_lock_irqsave(&ohci_edtd_lock, flags); - ohci_add_td_to_ed(td, interrupt_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - } - -#if 0 - /* Assimilate the new ED into the collective */ - /* - * When dynamic ED allocation is done, this call will be - * useful. For now, the correct ED already on the - * controller's proper periodic ED lists was chosen above. - */ - ohci_add_periodic_ed(dev->ohci, interrupt_ed, period); -#else - /* enable periodic (interrupt) transfers on the HC */ - ohci_start_periodic(dev->ohci); -#endif - - return 0; -} /* ohci_request_irq() */ - - -/* - * Control thread operations: - */ -static struct wait_queue *control_wakeup; - -/* - * This is the handler that gets called when a control transaction - * completes. - * - * This function is called from the interrupt handler. - */ -static int ohci_control_completed(int stats, void *buffer, void *dev_id) -{ - wake_up(&control_wakeup); - return 0; -} /* ohci_control_completed() */ - - -/* - * Send or receive a control message on a "pipe" - * - * The cmd parameter is a pointer to the 8 byte setup command to be - * sent. FIXME: This is a devrequest in usb.h. The function - * should be updated to accept a devrequest* instead of void*.. - * - * A control message contains: - * - The command itself - * - An optional data phase (if len > 0) - * - Status complete phase - */ -static int ohci_control_msg(struct usb_device *usb, unsigned int pipe, void *cmd, void *data, int len) -{ - struct ohci_device *dev = usb_to_ohci(usb); - /* - * ideally dev->ed should be linked into the root hub's - * control_ed list and used instead of just using it directly. - * This could present a problem as is with more than one - * device. (but who wants to use a keyboard AND a mouse - * anyways? ;) - */ - struct ohci_ed *control_ed = &dev->ohci->root_hub->ed[ED_CONTROL]; - struct ohci_td *setup_td, *data_td, *status_td; - struct wait_queue wait = { current, NULL }; - -#if 0 - printk(KERN_DEBUG "entering ohci_control_msg %p (ohci_dev: %p) pipe 0x%x, cmd %p, data %p, len %d\n", usb, dev, pipe, cmd, data, len); -#endif - - /* - * Set the max packet size, device speed, endpoint number, usb - * device number (function address), and type of TD. - * - */ - control_ed->status = \ - ed_set_maxpacket(usb_maxpacket(pipe)) | - ed_set_speed(usb_pipeslow(pipe)) | - usb_pipe_endpdev(pipe) | - OHCI_ED_F_NORM; - - /* - * Build the control TD - */ - - /* get a TD to send this control message with */ - setup_td = ohci_get_free_td(dev); - /* TODO check for NULL */ - - /* - * Set the not accessed condition code, allow odd sized data, - * and set the data transfer type to SETUP. Setup DATA always - * uses a DATA0 packet. - * - * The setup packet contains a devrequest (usb.h) which - * will always be 8 bytes long. FIXME: the cmd parameter - * should be a pointer to one of these instead of a void* !!! - */ - ohci_fill_new_td(setup_td, OHCI_TD_D_SETUP, TOGGLE_DATA0, - OHCI_TD_IOC_OFF, - cmd, 8, /* cmd is always 8 bytes long */ - NULL, NULL); - - /* allocate the next TD */ - data_td = ohci_get_free_td(dev); /* TODO check for NULL */ - - /* link to the next TD */ - setup_td->next_td = virt_to_bus(data_td); - - if (len > 0) { - - /* build the Control DATA TD, it starts with a DATA1. */ - ohci_fill_new_td(data_td, td_set_dir_out(usb_pipeout(pipe)), - TOGGLE_DATA1, - OHCI_TD_ROUND | OHCI_TD_IOC_OFF, - data, len, - NULL, NULL); - - /* - * XXX we should check that the data buffer doesn't - * cross a 4096 byte boundary. If so, it needs to be - * copied into a single 4096 byte aligned area for the - * OHCI's TD logic to see it all, or multiple TDs need - * to be made for each page. - * - * It's not likely a control transfer will run into - * this problem.. (famous last words) - */ - - status_td = ohci_get_free_td(dev); /* TODO check for NULL */ - data_td->next_td = virt_to_bus(status_td); - } else { - status_td = data_td; /* no data_td, use it for status */ - } - - /* The control status packet always uses a DATA1 */ - ohci_fill_new_td(status_td, - td_set_dir_in(usb_pipeout(pipe) | (len == 0)), - TOGGLE_DATA1, - 0, - NULL, 0, - NULL, ohci_control_completed); - status_td->next_td = 0; /* end of TDs */ - - /* - * Start the control transaction.. - */ - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); - - /* - * Add the chain of 2-3 control TDs to the control ED's TD list - */ - { - unsigned long flags; - spin_lock_irqsave(&ohci_edtd_lock, flags); - ohci_add_td_to_ed(setup_td, control_ed); - spin_unlock_irqrestore(&ohci_edtd_lock, flags); - } - -#if 0 - /* complete transaction debugging output (before) */ - printk(KERN_DEBUG " Control ED %lx:\n", virt_to_bus(control_ed)); - show_ohci_ed(control_ed); - printk(KERN_DEBUG " Setup TD %lx:\n", virt_to_bus(setup_td)); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " Data TD %lx:\n", virt_to_bus(data_td)); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " Status TD %lx:\n", virt_to_bus(status_td)); - show_ohci_td(status_td); -#endif - - /* Give the ED to the HC */ - ohci_add_control_ed(dev->ohci, control_ed); - - /* FIXME: - * this should really check to see that the transaction completed. - */ - schedule_timeout(HZ/10); - - remove_wait_queue(&control_wakeup, &wait); - -#if 0 - /* complete transaction debugging output (after) */ - printk(KERN_DEBUG " (after) Control ED:\n"); - show_ohci_ed(control_ed); - printk(KERN_DEBUG " (after) Setup TD:\n"); - show_ohci_td(setup_td); - if (data_td != status_td) { - printk(KERN_DEBUG " (after) Data TD:\n"); - show_ohci_td(data_td); - } - printk(KERN_DEBUG " (after) Status TD:\n"); - show_ohci_td(status_td); -#endif - - /* clean up incase it failed */ - /* XXX only do this if their ed pointer still points to control_ed - * incase they've been reclaimed and used by something else - * already. -greg */ - ohci_remove_td_from_ed(setup_td, control_ed); - ohci_remove_td_from_ed(data_td, control_ed); - ohci_remove_td_from_ed(status_td, control_ed); - - /* remove the control ED */ - ohci_remove_control_ed(dev->ohci, control_ed); - -#if 0 - printk(KERN_DEBUG "leaving ohci_control_msg\n"); -#endif - - return ohci_td_result(dev, status_td); -} /* ohci_control_msg() */ - - -/* - * Allocate a new USB device to be attached to an OHCI controller - */ -static struct usb_device *ohci_usb_allocate(struct usb_device *parent) -{ - struct usb_device *usb_dev; - struct ohci_device *dev; - - /* - * Allocate the generic USB device - */ - usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL); - if (!usb_dev) - return NULL; - - memset(usb_dev, 0, sizeof(*usb_dev)); - - /* - * Allocate an OHCI device (EDs and TDs for this device) - */ - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(usb_dev); - return NULL; - } - - memset(dev, 0, sizeof(*dev)); - - /* - * Link them together - */ - usb_dev->hcpriv = dev; - dev->usb = usb_dev; - - /* - * Link the device to its parent (hub, etc..) if any. - */ - usb_dev->parent = parent; - - if (parent) { - usb_dev->bus = parent->bus; - dev->ohci = usb_to_ohci(parent)->ohci; - } - - return usb_dev; -} /* ohci_usb_allocate() */ - - -/* - * Free a usb device. - * - * TODO This function needs to take better care of the EDs and TDs, etc. - */ -static int ohci_usb_deallocate(struct usb_device *usb_dev) -{ - kfree(usb_to_ohci(usb_dev)); - kfree(usb_dev); - return 0; -} - - -/* - * functions for the generic USB driver - */ -struct usb_operations ohci_device_operations = { - ohci_usb_allocate, - ohci_usb_deallocate, - ohci_control_msg, - ohci_request_irq, -}; - - -/* - * Reset an OHCI controller. Returns >= 0 on success. - * - * Afterwards the HC will be in the "suspend" state which prevents you - * from writing to some registers. Bring it to the operational state - * ASAP. - */ -static int reset_hc(struct ohci *ohci) -{ - int timeout = 1000; /* prevent an infinite loop */ - -#if 0 - printk(KERN_DEBUG "usb-ohci: resetting HC %p\n", ohci); -#endif - - writel(~0x0, &ohci->regs->intrdisable); /* Disable HC interrupts */ - writel(1, &ohci->regs->cmdstatus); /* HC Reset */ - writel_mask(0x3f, &ohci->regs->control); /* move to UsbReset state */ - - while ((readl(&ohci->regs->cmdstatus) & OHCI_CMDSTAT_HCR) != 0) { - if (!--timeout) { - printk("usb-ohci: USB HC reset timed out!\n"); - return -1; - } - udelay(1); - } - - printk(KERN_DEBUG "usb-ohci: HC %p reset.\n", ohci); - - return 0; -} /* reset_hc() */ - - -/* - * Reset and start an OHCI controller. Returns >= 0 on success. - */ -static int start_hc(struct ohci *ohci) -{ - int ret = 0; - int fminterval; - - fminterval = readl(&ohci->regs->fminterval) & 0x3fff; -#if 0 - printk(KERN_DEBUG "entering start_hc %p\n", ohci); -#endif - - if (reset_hc(ohci) < 0) - return -1; - - /* restore registers cleared by the reset */ - writel(virt_to_bus(ohci->root_hub->hcca), &ohci->regs->hcca); - - /* - * XXX Should fminterval also be set here? - * The spec suggests 0x2edf [11,999]. (FIXME: make this a constant) - */ - fminterval |= (0x2edf << 16); - writel(fminterval, &ohci->regs->fminterval); - /* Start periodic transfers at 90% of fminterval (fmremaining - * counts down; this will put them in the first 10% of the - * frame). */ - writel((0x2edf*9)/10, &ohci->regs->periodicstart); - - /* - * FNO (frame number overflow) could be enabled... they - * occur every 32768 frames (every 32-33 seconds). This is - * useful for debugging and as a bus heartbeat. -greg - */ - /* Choose the interrupts we care about */ - writel( OHCI_INTR_MIE | /* OHCI_INTR_RHSC | */ - OHCI_INTR_WDH | OHCI_INTR_FNO, - &ohci->regs->intrenable); - - /* Enter the USB Operational state & start the frames a flowing.. */ - writel_set(OHCI_USB_OPER, &ohci->regs->control); - - /* Enable control lists */ - writel_set(OHCI_USB_IE | OHCI_USB_CLE | OHCI_USB_BLE, &ohci->regs->control); - - /* Turn on power to the root hub ports (thanks Roman!) */ - writel( OHCI_ROOT_LPSC, &ohci->regs->roothub.status ); - - printk("usb-ohci: host controller operational\n"); - - return ret; -} /* start_hc() */ - - -/* - * Reset a root hub port - */ -static void ohci_reset_port(struct ohci *ohci, unsigned int port) -{ - int status; - - /* Don't allow overflows. */ - if (port >= MAX_ROOT_PORTS) { - printk("usb-ohci: bad port #%d in ohci_reset_port\n", port); - port = MAX_ROOT_PORTS-1; - } - - writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); /* Reset */ - - /* - * Wait for the reset to complete. - */ - wait_ms(10); - - /* check port status to see that the reset completed */ - status = readl(&ohci->regs->roothub.portstatus[port]); - if (status & PORT_PRS) { - /* reset failed, try harder? */ - printk("usb-ohci: port %d reset failed, retrying\n", port); - writel(PORT_PRS, &ohci->regs->roothub.portstatus[port]); - wait_ms(50); - } - - /* TODO we might need to re-enable the port here or is that - * done elsewhere? */ - -} /* ohci_reset_port */ - - -/* - * This gets called if the connect status on the root hub changes. - */ -static void ohci_connect_change(struct ohci * ohci, int port) -{ - struct usb_device *usb_dev; - struct ohci_device *dev; - /* memory I/O address of the port status register */ - void *portaddr = &ohci->regs->roothub.portstatus[port]; - int portstatus; - - printk(KERN_DEBUG "ohci_connect_change(%p, %d)\n", ohci, port); - - /* - * Because of the status change we have to forget - * everything we think we know about the device - * on this root hub port. It may have changed. - */ - usb_disconnect(ohci->root_hub->usb->children + port); - - portstatus = readl(portaddr); - - /* disable the port if nothing is connected */ - if (!(portstatus & PORT_CCS)) { - writel(PORT_CCS, portaddr); - return; - } - - /* - * Allocate a device for the new thingy that's been attached - */ - usb_dev = ohci_usb_allocate(ohci->root_hub->usb); - dev = usb_dev->hcpriv; - - dev->ohci = ohci; - - usb_connect(dev->usb); - - /* link it into the bus's device tree */ - ohci->root_hub->usb->children[port] = usb_dev; - - wait_ms(200); /* wait for powerup; XXX is this needed? */ - ohci_reset_port(ohci, port); - - /* Get information on speed by using LSD */ - usb_dev->slow = readl(portaddr) & PORT_LSDA ? 1 : 0; - - /* - * Do generic USB device tree processing on the new device. - */ - usb_new_device(usb_dev); - -} /* ohci_connect_change() */ - - -/* - * This gets called when the root hub configuration - * has changed. Just go through each port, seeing if - * there is something interesting happening. - */ -static void ohci_check_configuration(struct ohci *ohci) -{ - struct ohci_regs *regs = ohci->regs; - int num = 0; - int maxport = readl(&ohci->regs->roothub) & 0xff; - -#if 1 - printk(KERN_DEBUG "entering ohci_check_configuration %p\n", ohci); -#endif - - do { - if (readl(®s->roothub.portstatus[num]) & PORT_CSC) { - /* reset the connect status change bit */ - writel(PORT_CSC, ®s->roothub.portstatus[num]); - /* check the port for a nifty device */ - ohci_connect_change(ohci, num); - } - } while (++num < maxport); - -#if 0 - printk(KERN_DEBUG "leaving ohci_check_configuration %p\n", ohci); -#endif -} /* ohci_check_configuration() */ - - - -/* - * Check root hub port status and wake the control thread up if - * anything has changed. - * - * This function is called from the interrupt handler. - */ -static void ohci_root_hub_events(struct ohci *ohci) -{ - if (waitqueue_active(&ohci_configure)) { - int num = 0; - int maxport = ohci->root_hub->usb->maxchild; - - do { - if (readl(&ohci->regs->roothub.portstatus[num]) & - PORT_CSC) { - if (waitqueue_active(&ohci_configure)) - wake_up(&ohci_configure); - return; - } - } while (++num < maxport); - } -} /* ohci_root_hub_events() */ - - -/* - * The done list is in reverse order; we need to process TDs in the - * order they were finished (FIFO). This function builds the FIFO - * list using the next_dl_td pointer. - * - * This function originally by Roman Weissgaerber (weissg@vienna.at) - * - * This function is called from the interrupt handler. - */ -static struct ohci_td * ohci_reverse_donelist(struct ohci * ohci) -{ - __u32 td_list_hc; - struct ohci_hcca *hcca = ohci->root_hub->hcca; - struct ohci_td *td_list = NULL; - struct ohci_td *td_rev = NULL; - - td_list_hc = hcca->donehead & 0xfffffff0; - hcca->donehead = 0; - - while(td_list_hc) { - td_list = (struct ohci_td *) bus_to_virt(td_list_hc); - td_list->next_dl_td = td_rev; - - td_rev = td_list; - td_list_hc = td_list->next_td & 0xfffffff0; - } - - return td_list; -} /* ohci_reverse_donelist() */ - - -/* - * Collect this interrupt's goodies off of the list of finished TDs - * that the OHCI controller is kind enough to setup for us. - * - * This function is called from the interrupt handler. - */ -static void ohci_reap_donelist(struct ohci *ohci) -{ - struct ohci_td *td; /* used for walking the list */ - - spin_lock(&ohci_edtd_lock); - - /* create the FIFO ordered donelist */ - td = ohci_reverse_donelist(ohci); - - while (td != NULL) { - struct ohci_td *next_td = td->next_dl_td; - - /* FIXME: munge td->info into a future standard status format */ - /* Check if TD should be re-queued */ - if ((td->completed != NULL) && - (td->completed(OHCI_TD_CC_GET(td->info), td->data, td->dev_id))) - { - /* Mark the TD as active again: - * Set the not accessed condition code - * FIXME: should this reset OHCI_TD_ERRCNT? - */ - td->info |= OHCI_TD_CC_NEW; - - /* point it back to the start of the data buffer */ - td->cur_buf = virt_to_bus(td->data); - - /* XXX disabled for debugging reasons right now.. */ - /* insert it back on its ED */ - ohci_add_td_to_ed(td, td->ed); - } else { - /* return it to the pool of free TDs */ - ohci_free_td(td); - } - - td = next_td; - } - - spin_unlock(&ohci_edtd_lock); -} /* ohci_reap_donelist() */ - - -#if 0 -static int in_int = 0; -#endif -/* - * Get annoyed at the controller for bothering us. - * This pretty much follows the OHCI v1.0a spec, section 5.3. - */ -static void ohci_interrupt(int irq, void *__ohci, struct pt_regs *r) -{ - struct ohci *ohci = __ohci; - struct ohci_regs *regs = ohci->regs; - struct ohci_hcca *hcca = ohci->root_hub->hcca; - __u32 status, context; - -#if 0 - /* for debugging to keep IRQs from running away. */ - if (in_int >= 2) - return; - ++in_int; - return; -#endif - - /* Save the status of the interrupts that are enabled */ - status = readl(®s->intrstatus); - status &= readl(®s->intrenable); - - - /* make context = the interrupt status bits that we care about */ - if (hcca->donehead != 0) { - context = OHCI_INTR_WDH; /* hcca donehead needs processing */ - if (hcca->donehead & 1) { - context |= status; /* other status change to check */ - } - } else { - context = status; - if (!context) { - /* TODO increment a useless interrupt counter here */ - return; - } - } - - /* Disable HC interrupts */ - writel(OHCI_INTR_MIE, ®s->intrdisable); - - /* Process the done list */ - if (context & OHCI_INTR_WDH) { - /* See which TD's completed.. */ - ohci_reap_donelist(ohci); - - /* reset the done queue and tell the controller */ - hcca->donehead = 0; - writel(OHCI_INTR_WDH, ®s->intrstatus); - - context &= ~OHCI_INTR_WDH; /* mark this as checked */ - } - - /* Process any root hub status changes */ - if (context & OHCI_INTR_RHSC) { - /* Wake the thread to process root hub events */ - if (waitqueue_active(&ohci_configure)) - wake_up(&ohci_configure); - - writel(OHCI_INTR_RHSC, ®s->intrstatus); - /* - * Don't unset RHSC in context; it should be disabled. - * The control thread will re-enable it after it has - * checked the root hub status. - */ - } else { - /* check the root hub status anyways. Some controllers - * might not generate the interrupt properly. (?) */ - ohci_root_hub_events(ohci); - } - - /* Check those "other" pesky bits */ - if (context & (OHCI_INTR_FNO)) { - writel(OHCI_INTR_FNO, ®s->intrstatus); - context &= ~OHCI_INTR_FNO; /* mark this as checked */ - } - if (context & OHCI_INTR_SO) { - writel(OHCI_INTR_SO, ®s->intrstatus); - context &= ~OHCI_INTR_SO; /* mark this as checked */ - } - if (context & OHCI_INTR_RD) { - writel(OHCI_INTR_RD, ®s->intrstatus); - context &= ~OHCI_INTR_RD; /* mark this as checked */ - } - if (context & OHCI_INTR_UE) { - /* FIXME: need to have the control thread reset the - * controller now and keep a count of unrecoverable - * errors. If there are too many, it should just shut - * the broken controller down entirely. */ - writel(OHCI_INTR_UE, ®s->intrstatus); - context &= ~OHCI_INTR_UE; /* mark this as checked */ - } - if (context & OHCI_INTR_OC) { - writel(OHCI_INTR_OC, ®s->intrstatus); - context &= ~OHCI_INTR_OC; /* mark this as checked */ - } - - /* Mask out any remaining unprocessed interrupts so we don't - * get any more of them. */ - if (context & ~OHCI_INTR_MIE) { - writel(context, ®s->intrdisable); - } - - /* Re-enable HC interrupts */ - writel(OHCI_INTR_MIE, ®s->intrenable); - -} /* ohci_interrupt() */ - - -/* - * Allocate the resources required for running an OHCI controller. - * Host controller interrupts must not be running while calling this - * function or the penguins will get angry. - * - * The mem_base parameter must be the usable -virtual- address of the - * host controller's memory mapped I/O registers. - */ -static struct ohci *alloc_ohci(void* mem_base) -{ - int i; - struct ohci *ohci; - struct usb_bus *bus; - struct ohci_device *dev; - struct usb_device *usb; - -#if 0 - printk(KERN_DEBUG "entering alloc_ohci %p\n", mem_base); -#endif - - ohci = kmalloc(sizeof(*ohci), GFP_KERNEL); - if (!ohci) - return NULL; - - memset(ohci, 0, sizeof(*ohci)); - - ohci->irq = -1; - ohci->regs = mem_base; - INIT_LIST_HEAD(&ohci->interrupt_list); - - bus = kmalloc(sizeof(*bus), GFP_KERNEL); - if (!bus) - return NULL; - - memset(bus, 0, sizeof(*bus)); - - ohci->bus = bus; - bus->hcpriv = ohci; - bus->op = &ohci_device_operations; - - /* - * Allocate the USB device structure and root hub. - * - * Here we allocate our own root hub and TDs as well as the - * OHCI host controller communications area. The HCCA is just - * a nice pool of memory with pointers to endpoint descriptors - * for the different interrupts. - */ - usb = ohci_usb_allocate(NULL); - if (!usb) - return NULL; - - dev = ohci->root_hub = usb_to_ohci(usb); - - usb->bus = bus; - - /* Initialize the root hub */ - dev->ohci = ohci; /* link back to the controller */ - - /* - * Allocate the Host Controller Communications Area on a 256 - * byte boundary. XXX take the easy way out and just grab a - * page as that's guaranteed to have a nice boundary. - */ - dev->hcca = (struct ohci_hcca *) __get_free_page(GFP_KERNEL); - - /* Tell the controller where the HCCA is */ - writel(virt_to_bus(dev->hcca), &ohci->regs->hcca); - -#if 0 - printk(KERN_DEBUG "usb-ohci: HCCA allocated at %p (bus %p)\n", dev->hcca, (void*)virt_to_bus(dev->hcca)); -#endif - - /* Get the number of ports on the root hub */ - usb->maxchild = readl(&ohci->regs->roothub.a) & 0xff; - if (usb->maxchild > MAX_ROOT_PORTS) { - printk("usb-ohci: Limited to %d ports\n", MAX_ROOT_PORTS); - usb->maxchild = MAX_ROOT_PORTS; - } - if (usb->maxchild < 1) { - printk("usb-ohci: Less than one root hub port? Impossible!\n"); - usb->maxchild = 1; - } - printk("usb-ohci: %d root hub ports found\n", usb->maxchild); - - /* - * Initialize the ED polling "tree" (for simplicity's sake in - * this driver many nodes in the tree will be identical) - */ - dev->ed[ED_INT_32].next_ed = virt_to_bus(&dev->ed[ED_INT_16]); - dev->ed[ED_INT_16].next_ed = virt_to_bus(&dev->ed[ED_INT_8]); - dev->ed[ED_INT_8].next_ed = virt_to_bus(&dev->ed[ED_INT_4]); - dev->ed[ED_INT_4].next_ed = virt_to_bus(&dev->ed[ED_INT_2]); - dev->ed[ED_INT_2].next_ed = virt_to_bus(&dev->ed[ED_INT_1]); - - /* - * Initialize the polling table to call interrupts at the - * intended intervals. - */ - dev->hcca->int_table[0] = virt_to_bus(&dev->ed[ED_INT_32]); - for (i = 1; i < NUM_INTS; i++) { - if (i & 1) - dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_16]); - else if (i & 2) - dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_8]); - else if (i & 4) - dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_4]); - else if (i & 8) - dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_2]); - else if (i & 16) - dev->hcca->int_table[i] = - virt_to_bus(&dev->ed[ED_INT_1]); - } - - /* - * Tell the controller where the control and bulk lists are - * The lists start out empty. - */ - writel(0, &ohci->regs->ed_controlhead); - writel(0, &ohci->regs->ed_bulkhead); - /* - writel(virt_to_bus(&dev->ed[ED_CONTROL]), &ohci->regs->ed_controlhead); - writel(virt_to_bus(&dev->ed[ED_BULK]), &ohci->regs->ed_bulkhead); - */ - -#if 0 - printk(KERN_DEBUG "alloc_ohci(): controller\n"); - show_ohci_status(ohci); -#endif - -#if 0 - printk(KERN_DEBUG "leaving alloc_ohci %p\n", ohci); -#endif - - return ohci; -} /* alloc_ohci() */ - - -/* - * De-allocate all resoueces.. - */ -static void release_ohci(struct ohci *ohci) -{ - printk(KERN_DEBUG "entering release_ohci %p\n", ohci); - -#ifdef OHCI_TIMER - /* stop our timer */ - del_timer(&ohci_timer); -#endif - if (ohci->irq >= 0) { - free_irq(ohci->irq, ohci); - ohci->irq = -1; - } - - /* stop all OHCI interrupts */ - writel(~0x0, &ohci->regs->intrdisable); - - if (ohci->root_hub) { - /* ensure that HC is stopped before releasing the HCCA */ - writel(OHCI_USB_SUSPEND, &ohci->regs->control); - free_page((unsigned long) ohci->root_hub->hcca); - kfree(ohci->root_hub); - ohci->root_hub->hcca = NULL; - ohci->root_hub = NULL; - } - - /* unmap the IO address space */ - iounmap(ohci->regs); - - kfree(ohci); - - MOD_DEC_USE_COUNT; - - /* If the ohci itself were dynamic we'd free it here */ - - printk(KERN_DEBUG "usb-ohci: HC resources released.\n"); -} /* release_ohci() */ - - -/* - * USB OHCI control thread - */ -static int ohci_control_thread(void * __ohci) -{ - struct ohci *ohci = (struct ohci *)__ohci; - - /* - * I'm unfamiliar with the SMP kernel locking.. where should - * this be released and what does it do? -greg - */ - lock_kernel(); - - /* - * This thread doesn't need any user-level access, - * so get rid of all of our resources.. - */ - printk("ohci_control_thread code at %p\n", &ohci_control_thread); - exit_mm(current); - exit_files(current); - exit_fs(current); - - strcpy(current->comm, "ohci-control"); - - /* - * Damn the torpedoes, full speed ahead - */ - if (start_hc(ohci) < 0) { - printk("usb-ohci: failed to start the controller\n"); - release_ohci(ohci); - printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci); - return 0; - } - - for(;;) { - siginfo_t info; - int unsigned long signr; - - wait_ms(200); - - /* check the root hub configuration for changes. */ - ohci_check_configuration(ohci); - - /* re-enable root hub status change interrupts. */ -#if 0 - writel(OHCI_INTR_RHSC, &ohci->regs->intrenable); -#endif - - printk(KERN_DEBUG "ohci-control thread sleeping\n"); - interruptible_sleep_on(&ohci_configure); -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume = 0; - if (start_hc(ohci) < 0) - break; - continue; - } -#endif - - /* - * If we were woken up by a signal, see if its useful, - * otherwise exit. - */ - if (signal_pending(current)) { - /* sending SIGUSR1 makes us print out some info */ - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - if(signr == SIGUSR1) { - /* FIXME: have it do a full ed/td queue dump */ - printk(KERN_DEBUG "OHCI status dump:\n"); - show_ohci_status(ohci); - } else { - /* unknown signal, exit the thread */ - break; - } - } - } /* for (;;) */ - - reset_hc(ohci); - release_ohci(ohci); - - printk(KERN_DEBUG "leaving ohci_control_thread %p\n", __ohci); - - return 0; -} /* ohci_control_thread() */ - - -#ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) -{ - static int down = 0; - - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - if (down) { - printk(KERN_DEBUG "usb-ohci: received extra suspend event\n"); - break; - } - down = 1; - break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - if (!down) { - printk(KERN_DEBUG "usb-ohci: received bogus resume event\n"); - break; - } - down = 0; - if (waitqueue_active(&ohci_configure)) { - apm_resume = 1; - wake_up(&ohci_configure); - } - break; - } - return 0; -} /* handle_apm_event() */ -#endif - - -#ifdef OHCI_TIMER -/* - * Inspired by Iñaky's driver. This function is a timer routine that - * is called OHCI_TIMER_FREQ times per second. It polls the root hub - * for status changes as on my system things are acting a bit odd at - * the moment.. - */ -static void ohci_timer_func (unsigned long ohci_ptr) -{ - struct ohci *ohci = (struct ohci*)ohci_ptr; - - ohci_root_hub_events(ohci); - - /* press the snooze button... */ - mod_timer(&ohci_timer, jiffies + (OHCI_TIMER_FREQ*HZ)); -} /* ohci_timer_func() */ -#endif - - -/* - * Increment the module usage count, start the control thread and - * return success if the controller is good. - */ -static int found_ohci(int irq, void* mem_base) -{ - int retval; - struct ohci *ohci; - -#if 0 - printk(KERN_DEBUG "entering found_ohci %d %p\n", irq, mem_base); -#endif - - /* Allocate the running OHCI structures */ - ohci = alloc_ohci(mem_base); - if (!ohci) { - return -ENOMEM; - } - -#ifdef OHCI_TIMER - init_timer(&ohci_timer); - ohci_timer.expires = jiffies + (OHCI_TIMER_FREQ*HZ); - ohci_timer.data = (unsigned long)ohci; - ohci_timer.function = ohci_timer_func; -#endif - - retval = -EBUSY; - if (request_irq(irq, ohci_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) { - int pid; - - ohci->irq = irq; - -#if 0 - printk(KERN_DEBUG "usb-ohci: starting ohci-control thread\n"); -#endif - - /* fork off the handler */ - pid = kernel_thread(ohci_control_thread, ohci, - CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >= 0) { - return 0; - } - - retval = pid; - } else { - printk("usb-ohci: Couldn't allocate interrupt %d\n", irq); - } - release_ohci(ohci); - -#if 0 - printk(KERN_DEBUG "leaving found_ohci %d %p\n", irq, mem_base); -#endif - - return retval; -} /* found_ohci() */ - - -/* - * If this controller is for real, map the IO memory and proceed - */ -static int init_ohci(struct pci_dev *dev) -{ - unsigned long mem_base = dev->base_address[0]; - - /* If its OHCI, its memory */ - if (mem_base & PCI_BASE_ADDRESS_SPACE_IO) - return -ENODEV; - - /* Get the memory address and map it for IO */ - mem_base &= PCI_BASE_ADDRESS_MEM_MASK; - - /* no interrupt won't work... */ - if (dev->irq == 0) { - printk("usb-ohci: no irq assigned? check your BIOS settings.\n"); - return -ENODEV; - } - - /* - * FIXME ioremap_nocache isn't implemented on all CPUs (such - * as the Alpha) [?] What should I use instead... - * - * The iounmap() is done on in release_ohci. - */ - mem_base = (unsigned long) ioremap_nocache(mem_base, 4096); - - if (!mem_base) { - printk("Error mapping OHCI memory\n"); - return -EFAULT; - } - MOD_INC_USE_COUNT; - - if (found_ohci(dev->irq, (void *) mem_base) < 0) { - MOD_DEC_USE_COUNT; - return -1; - } - - return 0; -} /* init_ohci() */ - -#ifdef MODULE -/* - * Clean up when unloading the module - */ -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); -#endif - printk("usb-ohci: module unloaded\n"); -} - -#define ohci_init init_module - -#endif - - -/* TODO this should be named following Linux convention and go in pci.h */ -#define PCI_CLASS_SERIAL_USB_OHCI ((PCI_CLASS_SERIAL_USB << 8) | 0x0010) - -/* - * Search the PCI bus for an OHCI USB controller and set it up - * - * If anyone wants multiple controllers this will need to be - * updated.. Right now, it just picks the first one it finds. - */ -int ohci_init(void) -{ - int retval; - struct pci_dev *dev = NULL; - /*u8 type;*/ - - if (sizeof(struct ohci_device) > 4096) { - printk("usb-ohci: struct ohci_device to large\n"); - return -ENODEV; - } - - printk("OHCI USB Driver loading\n"); - - retval = -ENODEV; - for (;;) { - /* Find an OHCI USB controller */ - dev = pci_find_class(PCI_CLASS_SERIAL_USB_OHCI, dev); - if (!dev) - break; - - /* Verify that its OpenHCI by checking for MMIO */ - /* pci_read_config_byte(dev, PCI_CLASS_PROG, &type); - if (!type) - continue; */ - - /* Ok, set it up */ - retval = init_ohci(dev); - if (retval < 0) - continue; - - /* TODO check module params here to determine what to load */ - -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif -#ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); -#endif - - return 0; /* no error */ - } - return retval; -} /* ohci_init */ - -/* vim:sw=8 - */ diff --git a/drivers/usb/ohci.h b/drivers/usb/ohci.h deleted file mode 100644 index 8714cd2e833b..000000000000 --- a/drivers/usb/ohci.h +++ /dev/null @@ -1,349 +0,0 @@ -#ifndef __LINUX_OHCI_H -#define __LINUX_OHCI_H - -/* - * Open Host Controller Interface data structures and defines. - * - * (C) Copyright 1999 Gregory P. Smith - * - * $Id: ohci.h,v 1.15 1999/05/09 23:25:49 greg Exp $ - */ - -#include -#include - -#include "usb.h" - -struct ohci_ed; - -/* - * Each TD must be aligned on a 16-byte boundary. From the OHCI v1.0 spec - * it does not state that TDs must be contiguious in memory (due to the - * use of the next_td field). This gives us extra room at the end of a - * TD for our own driver specific data. - * - * This structure's size must be a multiple of 16 bytes. ?? no way, I - * don't see why. Alignment should be all that matters. - */ -struct ohci_td { - /* OHCI Hardware fields */ - __u32 info; /* TD status & type flags */ - __u32 cur_buf; /* Current Buffer Pointer (bus address) */ - __u32 next_td; /* Next TD Pointer (bus address) */ - __u32 buf_end; /* Memory Buffer End Pointer (bus address) */ - - /* Driver specific fields */ - struct ohci_ed *ed; /* address of the ED this TD is on */ - struct ohci_td *next_dl_td; /* used during donelist processing */ - void *data; /* virt. address of the the buffer */ - usb_device_irq completed; /* Completion handler routine */ - int allocated; /* boolean: is this TD allocated? */ - - /* User or Device class driver specific fields */ - void *dev_id; /* user defined pointer passed to irq handler */ -} __attribute((aligned(16))); - -#define OHCI_TD_ROUND (1 << 18) /* buffer rounding bit */ -#define OHCI_TD_D (3 << 19) /* direction of xfer: */ -#define OHCI_TD_D_IN (2 << 19) -#define OHCI_TD_D_OUT (1 << 19) -#define OHCI_TD_D_SETUP (0 << 19) -#define td_set_dir_in(d) ((d) ? OHCI_TD_D_IN : OHCI_TD_D_OUT ) -#define td_set_dir_out(d) ((d) ? OHCI_TD_D_OUT : OHCI_TD_D_IN ) -#define OHCI_TD_IOC_DELAY (7 << 21) /* frame delay allowed before int. */ -#define OHCI_TD_IOC_OFF (OHCI_TD_IOC_DELAY) /* no interrupt on complete */ -#define OHCI_TD_DT (3 << 24) /* data toggle bits */ -#define TOGGLE_AUTO (0 << 24) /* automatic (from the ED) */ -#define TOGGLE_DATA0 (2 << 24) /* force Data0 */ -#define TOGGLE_DATA1 (3 << 24) /* force Data1 */ -#define td_force_toggle(b) (((b) | 2) << 24) -#define OHCI_TD_ERRCNT (3 << 26) /* error count */ -#define td_errorcount(td) (((td).info >> 26) & 3) -#define OHCI_TD_CC (0xf << 28) /* condition code */ -#define OHCI_TD_CC_GET(td_i) (((td_i) >> 28) & 0xf) -#define OHCI_TD_CC_NEW (OHCI_TD_CC) /* set this on all unaccessed TDs! */ -#define td_cc_notaccessed(td) (((td).info >> 29) == 7) -#define td_cc_accessed(td) (((td).info >> 29) != 7) -#define td_cc_noerror(td) ((((td).info) & OHCI_TD_CC) == 0) -#define td_active(td) (!td_cc_noerror((td)) && (td_errorcount((td)) < 3)) -#define td_done(td) (td_cc_noerror((td)) || (td_errorcount((td)) == 3)) - -#define td_allocated(td) ((td).allocated) -#define allocate_td(td) ((td)->allocated = 1) -#define ohci_free_td(td) ((td)->allocated = 0) - - -/* - * The endpoint descriptors also requires 16-byte alignment - */ -struct ohci_ed { - /* OHCI hardware fields */ - __u32 status; - __u32 tail_td; /* TD Queue tail pointer */ - __u32 _head_td; /* TD Queue head pointer, toggle carry & halted bits */ - __u32 next_ed; /* Next ED */ -} __attribute((aligned(16))); - -/* get the head_td */ -#define ed_head_td(ed) ((ed)->_head_td & 0xfffffff0) - -/* save the carry flag while setting the head_td */ -#define set_ed_head_td(ed, td) ((ed)->_head_td = (td) | ((ed)->_head_td & 3)) - -#define OHCI_ED_SKIP (1 << 14) -#define OHCI_ED_MPS (0x7ff << 16) -/* FIXME: should cap at the USB max packet size [0x4ff] */ -#define ed_set_maxpacket(s) (((s) << 16) & OHCI_ED_MPS) -#define OHCI_ED_F_NORM (0) -#define OHCI_ED_F_ISOC (1 << 15) -#define ed_set_type_isoc(i) ((i) ? OHCI_ED_F_ISOC : OHCI_ED_F_NORM) -#define OHCI_ED_S_LOW (1 << 13) -#define OHCI_ED_S_HIGH (0) -#define ed_set_speed(s) ((s) ? OHCI_ED_S_LOW : OHCI_ED_S_HIGH) -#define OHCI_ED_D (3 << 11) -#define OHCI_ED_D_IN (2 << 11) -#define OHCI_ED_D_OUT (1 << 11) -#define ed_set_dir_in(d) ((d) ? OHCI_ED_D_IN : OHCI_ED_D_OUT) -#define ed_set_dir_out(d) ((d) ? OHCI_ED_D_OUT : OHCI_ED_D_IN) -#define OHCI_ED_EN (0xf << 7) -#define OHCI_ED_FA (0x7f) - - -/* NOTE: bits 27-31 of the status dword are reserved for the driver */ -/* - * We'll use this status flag for to mark if an ED is in use by the - * driver or not. If the bit is set, it is used. - * - * FIXME: implement this! - */ -#define ED_USED (1 << 31) - -/* - * The HCCA (Host Controller Communications Area) is a 256 byte - * structure defined in the OHCI spec. that the host controller is - * told the base address of. It must be 256-byte aligned. - */ -#define NUM_INTS 32 /* part of the OHCI standard */ -struct ohci_hcca { - __u32 int_table[NUM_INTS]; /* Interrupt ED table */ - __u16 frame_no; /* current frame number */ - __u16 pad1; /* set to 0 on each frame_no change */ - __u32 donehead; /* info returned for an interrupt */ - u8 reserved_for_hc[116]; -} __attribute((aligned(256))); - -/* - * The TD entries here are pre-allocated as Linus did with his simple - * UHCI implementation. With the current state of this driver that - * shouldn't be a problem. However if someone ever connects 127 - * supported devices to this driver and tries to use them all at once: - * a) They're insane! - * b) They should code in dynamic allocation - */ -struct ohci; - -/* - * Warning: These constants must not be so large as to cause the - * ohci_device structure to exceed one 4096 byte page. Or "weird - * things will happen" as the alloc_ohci() function assumes that - * its less than one page. (FIXME) - */ -#define NUM_TDS 32 /* num of preallocated transfer descriptors */ -#define DATA_BUF_LEN 16 /* num of unsigned long's for the data buf */ - -/* - * For this "simple" driver we only support a single ED for each - * polling rate. - * - * Later on this driver should be extended to use a full tree of EDs - * so that there can be 32 different 32ms polling frames, etc. - * Individual devices shouldn't need as many as the root hub in that - * case; complicating how things are done at the moment. - * - * Bulk and Control transfers hang off of their own ED lists. - */ -#define NUM_EDS 16 /* num of preallocated endpoint descriptors */ - -#define ohci_to_usb(ohci) ((ohci)->usb) -#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) - -/* The usb_device must be first! */ -struct ohci_device { - struct usb_device *usb; - - struct ohci *ohci; - struct ohci_hcca *hcca; /* OHCI mem. mapped IO area */ - - struct ohci_ed ed[NUM_EDS]; /* Endpoint Descriptors */ - struct ohci_td td[NUM_TDS]; /* Transfer Descriptors */ - - unsigned long data[DATA_BUF_LEN]; -}; - -/* .... */ - -#define ED_INT_1 0 -#define ED_INT_2 1 -#define ED_INT_4 2 -#define ED_INT_8 3 -#define ED_INT_16 4 -#define ED_INT_32 5 -#define ED_CONTROL 6 -#define ED_BULK 7 -#define ED_ISO ED_INT_1 /* same as 1ms interrupt queue */ -#define ED_FIRST_AVAIL 8 /* first non-reserved ED */ - -/* - * Given a period p in ms, convert it to the closest endpoint - * interrupt frequency; rounding down. This is a gross macro. - * Feel free to toss it for actual code. (gasp!) - */ -#define ms_to_ed_int(p) \ - ((p >= 32) ? ED_INT_32 : \ - ((p & 16) ? ED_INT_16 : \ - ((p & 8) ? ED_INT_8 : \ - ((p & 4) ? ED_INT_4 : \ - ((p & 2) ? ED_INT_2 : \ - ED_INT_1))))) /* hmm.. scheme or lisp anyone? */ - -/* - * This is the maximum number of root hub ports. I don't think we'll - * ever see more than two as that's the space available on an ATX - * motherboard's case, but it could happen. The OHCI spec allows for - * up to 15... (which is insane!) - * - * Although I suppose several "ports" could be connected directly to - * internal laptop devices such as a keyboard, mouse, camera and - * serial/parallel ports. hmm... That'd be neat. - */ -#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ - -/* - * This is the structure of the OHCI controller's memory mapped I/O - * region. This is Memory Mapped I/O. You must use the readl() and - * writel() macros defined in asm/io.h to access these!! - */ -struct ohci_regs { - /* control and status registers */ - __u32 revision; - __u32 control; - __u32 cmdstatus; - __u32 intrstatus; - __u32 intrenable; - __u32 intrdisable; - /* memory pointers */ - __u32 hcca; - __u32 ed_periodcurrent; - __u32 ed_controlhead; - __u32 ed_controlcurrent; - __u32 ed_bulkhead; - __u32 ed_bulkcurrent; - __u32 current_donehead; /* The driver should get this from the HCCA */ - /* frame counters */ - __u32 fminterval; - __u32 fmremaining; - __u32 fmnumber; - __u32 periodicstart; - __u32 lsthresh; - /* Root hub ports */ - struct ohci_roothub_regs { - __u32 a; - __u32 b; - __u32 status; - __u32 portstatus[MAX_ROOT_PORTS]; - } roothub; -} __attribute((aligned(32))); - -/* - * Read a MMIO register and re-write it after ANDing with (m) - */ -#define writel_mask(m, a) writel( (readl((__u32)(a))) & (__u32)(m), (__u32)(a) ) - -/* - * Read a MMIO register and re-write it after ORing with (b) - */ -#define writel_set(b, a) writel( (readl((__u32)(a))) | (__u32)(b), (__u32)(a) ) - - -#define PORT_CCS (1) /* port current connect status */ -#define PORT_PES (1 << 1) /* port enable status */ -#define PORT_PSS (1 << 2) /* port suspend status */ -#define PORT_POCI (1 << 3) /* port overcurrent indicator */ -#define PORT_PRS (1 << 4) /* port reset status */ -#define PORT_PPS (1 << 8) /* port power status */ -#define PORT_LSDA (1 << 9) /* port low speed dev. attached */ -#define PORT_CSC (1 << 16) /* port connect status change */ -#define PORT_PESC (1 << 17) /* port enable status change */ -#define PORT_PSSC (1 << 18) /* port suspend status change */ -#define PORT_OCIC (1 << 19) /* port over current indicator chg */ -#define PORT_PRSC (1 << 20) /* port reset status change */ - -/* - * Root Hub status register masks - */ -#define OHCI_ROOT_LPS (1) /* turn off root hub ports power */ -#define OHCI_ROOT_OCI (1 << 1) /* Overcurrent Indicator */ -#define OHCI_ROOT_DRWE (1 << 15) /* Device remote wakeup enable */ -#define OHCI_ROOT_LPSC (1 << 16) /* turn on root hub ports power */ -#define OHCI_ROOT_OCIC (1 << 17) /* Overcurrent indicator change */ -#define OHCI_ROOT_CRWE (1 << 31) /* Clear RemoteWakeupEnable */ - -/* - * Interrupt register masks - */ -#define OHCI_INTR_SO (1) -#define OHCI_INTR_WDH (1 << 1) -#define OHCI_INTR_SF (1 << 2) -#define OHCI_INTR_RD (1 << 3) -#define OHCI_INTR_UE (1 << 4) -#define OHCI_INTR_FNO (1 << 5) -#define OHCI_INTR_RHSC (1 << 6) -#define OHCI_INTR_OC (1 << 30) -#define OHCI_INTR_MIE (1 << 31) - -/* - * Control register masks - */ -#define OHCI_USB_OPER (2 << 6) -#define OHCI_USB_SUSPEND (3 << 6) -#define OHCI_USB_PLE (1 << 2) /* Periodic (interrupt) list enable */ -#define OHCI_USB_IE (1 << 3) /* Isochronous list enable */ -#define OHCI_USB_CLE (1 << 4) /* Control list enable */ -#define OHCI_USB_BLE (1 << 5) /* Bulk list enable */ - -/* - * Command status register masks - */ -#define OHCI_CMDSTAT_HCR (1) -#define OHCI_CMDSTAT_CLF (1 << 1) -#define OHCI_CMDSTAT_BLF (1 << 2) -#define OHCI_CMDSTAT_OCR (1 << 3) -#define OHCI_CMDSTAT_SOC (3 << 16) - -/* - * This is the full ohci controller description - * - * Note how the "proper" USB information is just - * a subset of what the full implementation needs. (Linus) - */ -struct ohci { - int irq; - struct ohci_regs *regs; /* OHCI controller's memory */ - struct usb_bus *bus; - struct ohci_device *root_hub; /* Root hub & controller */ - struct list_head interrupt_list; /* List of interrupt active TDs for this OHCI */ -}; - -#define OHCI_TIMER -#define OHCI_TIMER_FREQ (1) /* frequency of OHCI status checks */ - -/* Debugging code */ -void show_ohci_ed(struct ohci_ed *ed); -void show_ohci_td(struct ohci_td *td); -void show_ohci_status(struct ohci *ohci); -void show_ohci_device(struct ohci_device *dev); -void show_ohci_hcca(struct ohci_hcca *hcca); - -#endif -/* vim:sw=8 - */ diff --git a/drivers/usb/ov511.c b/drivers/usb/ov511.c new file mode 100644 index 000000000000..64a89d05357a --- /dev/null +++ b/drivers/usb/ov511.c @@ -0,0 +1,2757 @@ +/* + * OmniVision OV511 Camera-to-USB Bridge Driver + * + * Copyright (c) 1999-2000 Mark W. McClelland + * Many improvements by Bret Wallach + * Color fixes by by Orion Sky Lawlor (2/26/2000) + * Snapshot code by Kevin Moore + * OV7620 fixes by Charl P. Botha + * Changes by Claudio Matsuoka + * + * Based on the Linux CPiA driver written by Peter Pregler, + * Scott J. Bertin and Johannes Erdfelt. + * + * Please see the file: linux/Documentation/usb/ov511.txt + * and the website at: http://alpha.dyndns.org/ov511 + * for more info. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +static const char version[] = "1.17"; + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KMOD +#include +#endif + +#include "ov511.h" + +#undef OV511_GBR422 /* Experimental -- sets the 7610 to GBR422 */ + +#define OV511_I2C_RETRIES 3 + +/* Video Size 640 x 480 x 3 bytes for RGB */ +#define MAX_FRAME_SIZE (640 * 480 * 3) +#define MAX_DATA_SIZE (MAX_FRAME_SIZE + sizeof(struct timeval)) + +#define DEFAULT_WIDTH 640 +#define DEFAULT_HEIGHT 480 + +#define GET_SEGSIZE(p) ((p) == VIDEO_PALETTE_GREY ? 256 : 384) +#define GET_DEPTH(p) ((p) == VIDEO_PALETTE_GREY ? 8 : 24) + +/* PARAMETER VARIABLES: */ +static int autoadjust = 1; /* CCD dynamically changes exposure, etc... */ + +/* 0=no debug messages + * 1=init/detection/unload and other significant messages, + * 2=some warning messages + * 3=config/control function calls + * 4=most function calls and data parsing messages + * 5=highly repetitive mesgs + * NOTE: This should be changed to 0, 1, or 2 for production kernels + */ +static int debug = 3; + +/* Fix vertical misalignment of red and blue at 640x480 */ +static int fix_rgb_offset = 0; + +/* Snapshot mode enabled flag */ +static int snapshot = 0; + +/* Sensor detection override (global for all attached cameras) */ +static int sensor = 0; + +/* Increase this if you are getting "Failed to read sensor ID..." */ +static int i2c_detect_tries = 5; + +/* For legal values, see the OV7610/7620 specs under register Common F, + * upper nybble (set to 0-F) */ +static int aperture = -1; + +/* Force image to be read in RGB instead of BGR. This option allow + * programs that expect RGB data (e.g. gqcam) to work with this driver. */ +static int force_rgb = 0; + +MODULE_PARM(autoadjust, "i"); +MODULE_PARM(debug, "i"); +MODULE_PARM(fix_rgb_offset, "i"); +MODULE_PARM(snapshot, "i"); +MODULE_PARM(sensor, "i"); +MODULE_PARM(i2c_detect_tries, "i"); +MODULE_PARM(aperture, "i"); +MODULE_PARM(force_rgb, "i"); + +MODULE_AUTHOR("Mark McClelland & Bret Wallach & Orion Sky Lawlor & Kevin Moore & Charl P. Botha & Claudio Matsuoka "); +MODULE_DESCRIPTION("OV511 USB Camera Driver"); + +char kernel_version[] = UTS_RELEASE; + +static struct usb_driver ov511_driver; + +/********************************************************************** + * List of known OV511-based cameras + **********************************************************************/ + +static struct cam_list clist[] = { + { 0, "generic model (no ID)" }, + { 3, "D-Link DSB-C300" }, + { 4, "generic OV511/OV7610" }, + { 5, "Puretek PT-6007" }, + { 21, "Creative Labs WebCam 3" }, + { 36, "Koala-Cam" }, + { 38, "Lifeview USB Life TV" }, /* No support yet! */ + { 100, "Lifeview RoboCam" }, + { 102, "AverMedia InterCam Elite" }, + { 112, "MediaForte MV300" }, /* or OV7110 evaluation kit */ + { -1, NULL } +}; + +static struct palette_list plist[] = { + { VIDEO_PALETTE_GREY, "GREY" }, + { VIDEO_PALETTE_HI240, "HI240" }, + { VIDEO_PALETTE_RGB565, "RGB565" }, + { VIDEO_PALETTE_RGB24, "RGB24" }, + { VIDEO_PALETTE_RGB32, "RGB32" }, + { VIDEO_PALETTE_RGB555, "RGB555" }, + { VIDEO_PALETTE_YUV422, "YUV422" }, + { VIDEO_PALETTE_YUYV, "YUYV" }, + { VIDEO_PALETTE_UYVY, "UYVY" }, + { VIDEO_PALETTE_YUV420, "YUV420" }, + { VIDEO_PALETTE_YUV411, "YUV411" }, + { VIDEO_PALETTE_RAW, "RAW" }, + { VIDEO_PALETTE_YUV422P,"YUV422P" }, + { VIDEO_PALETTE_YUV411P,"YUV411P" }, + { VIDEO_PALETTE_YUV420P,"YUV420P" }, + { VIDEO_PALETTE_YUV410P,"YUV410P" }, + { -1, NULL } +}; + +/********************************************************************** + * + * Memory management + * + * This is a shameless copy from the USB-cpia driver (linux kernel + * version 2.3.29 or so, I have no idea what this code actually does ;). + * Actually it seems to be a copy of a shameless copy of the bttv-driver. + * Or that is a copy of a shameless copy of ... (To the powers: is there + * no generic kernel-function to do this sort of stuff?) + * + * Yes, it was a shameless copy from the bttv-driver. IIRC, Alan says + * there will be one, but apparentely not yet -jerdfelt + * + * So I copied it again for the OV511 driver -claudio + **********************************************************************/ + +/* Given PGD from the address space's page table, return the kernel + * virtual mapping of the physical memory mapped at ADR. + */ +static inline unsigned long uvirt_to_kva(pgd_t *pgd, unsigned long adr) +{ + unsigned long ret = 0UL; + pmd_t *pmd; + pte_t *ptep, pte; + + if (!pgd_none(*pgd)) { + pmd = pmd_offset(pgd, adr); + if (!pmd_none(*pmd)) { + ptep = pte_offset(pmd, adr); + pte = *ptep; + if (pte_present(pte)) + ret = page_address(pte_page(pte)) | + (adr & (PAGE_SIZE-1)); + } + } + + return ret; +} + +/* Here we want the physical address of the memory. + * This is used when initializing the contents of the + * area and marking the pages as reserved. + */ +static inline unsigned long kvirt_to_pa(unsigned long adr) +{ + unsigned long va, kva, ret; + + va = VMALLOC_VMADDR(adr); + kva = uvirt_to_kva(pgd_offset_k(va), va); + ret = __pa(kva); + return ret; +} + +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr, page; + + /* Round it off to PAGE_SIZE */ + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_reserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr, page; + + if (!mem) + return; + + size += (PAGE_SIZE - 1); + size &= ~(PAGE_SIZE - 1); + + adr=(unsigned long) mem; + while (size > 0) { + page = kvirt_to_pa(adr); + mem_map_unreserve(MAP_NR(__va(page))); + adr += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + vfree(mem); +} + +/********************************************************************** + * /proc interface + * Based on the CPiA driver version 0.7.4 -claudio + **********************************************************************/ + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + +static struct proc_dir_entry *ov511_proc_entry = NULL; +extern struct proc_dir_entry *video_proc_entry; + +#define YES_NO(x) ((x) ? "yes" : "no") + +static int ov511_read_proc(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + char *out = page; + int i, j, len; + struct usb_ov511 *ov511 = data; + + /* IMPORTANT: This output MUST be kept under PAGE_SIZE + * or we need to get more sophisticated. */ + + out += sprintf (out, "driver_version : %s\n", version); + out += sprintf (out, "custom_id : %d\n", ov511->customid); + out += sprintf (out, "model : %s\n", ov511->desc ? + clist[ov511->desc].description : "unknown"); + out += sprintf (out, "streaming : %s\n", YES_NO (ov511->streaming)); + out += sprintf (out, "grabbing : %s\n", YES_NO (ov511->grabbing)); + out += sprintf (out, "compress : %s\n", YES_NO (ov511->compress)); + out += sprintf (out, "subcapture : %s\n", YES_NO (ov511->sub_flag)); + out += sprintf (out, "sub_size : %d %d %d %d\n", + ov511->subx, ov511->suby, ov511->subw, ov511->subh); + out += sprintf (out, "data_format : %s\n", force_rgb ? "RGB" : "BGR"); + out += sprintf (out, "brightness : %d\n", ov511->brightness >> 8); + out += sprintf (out, "colour : %d\n", ov511->colour >> 8); + out += sprintf (out, "contrast : %d\n", ov511->contrast >> 8); + out += sprintf (out, "num_frames : %d\n", OV511_NUMFRAMES); + for (i = 0; i < OV511_NUMFRAMES; i++) { + out += sprintf (out, "frame : %d\n", i); + out += sprintf (out, " depth : %d\n", + ov511->frame[i].depth); + out += sprintf (out, " size : %d %d\n", + ov511->frame[i].width, ov511->frame[i].height); + out += sprintf (out, " format : "); + for (j = 0; plist[j].num >= 0; j++) { + if (plist[j].num == ov511->frame[i].format) { + out += sprintf (out, "%s\n", plist[j].name); + break; + } + } + if (plist[j].num < 0) + out += sprintf (out, "unknown\n"); + out += sprintf (out, " segsize : %d\n", + ov511->frame[i].segsize); + out += sprintf (out, " data_buffer : 0x%p\n", + ov511->frame[i].data); + } + out += sprintf (out, "snap_enabled : %s\n", YES_NO (ov511->snap_enabled)); + out += sprintf (out, "bridge : %s\n", + ov511->bridge == BRG_OV511 ? "OV511" : + ov511->bridge == BRG_OV511PLUS ? "OV511+" : + "unknown"); + out += sprintf (out, "sensor : %s\n", + ov511->sensor == SEN_OV7610 ? "OV7610" : + ov511->sensor == SEN_OV7620 ? "OV7620" : + ov511->sensor == SEN_OV7620AE ? "OV7620AE" : + "unknown"); + out += sprintf (out, "packet_size : %d\n", ov511->packet_size); + out += sprintf (out, "framebuffer : 0x%p\n", ov511->fbuf); + + len = out - page; + len -= off; + if (len < count) { + *eof = 1; + if (len <= 0) return 0; + } else + len = count; + + *start = page + off; + + return len; +} + +static int ov511_write_proc(struct file *file, const char *buffer, + unsigned long count, void *data) +{ + return -EINVAL; +} + +static void create_proc_ov511_cam (struct usb_ov511 *ov511) +{ + char name[7]; + struct proc_dir_entry *ent; + + if (!ov511_proc_entry || !ov511) + return; + + sprintf(name, "video%d", ov511->vdev.minor); + PDEBUG (4, "creating /proc/video/ov511/%s", name); + + ent = create_proc_entry(name, S_IFREG|S_IRUGO|S_IWUSR, ov511_proc_entry); + + if (!ent) + return; + + ent->data = ov511; + ent->read_proc = ov511_read_proc; + ent->write_proc = ov511_write_proc; + ov511->proc_entry = ent; +} + +static void destroy_proc_ov511_cam (struct usb_ov511 *ov511) +{ + char name[7]; + + if (!ov511 || !ov511->proc_entry) + return; + + sprintf(name, "video%d", ov511->vdev.minor); + PDEBUG (4, "destroying %s", name); + remove_proc_entry(name, ov511_proc_entry); + ov511->proc_entry = NULL; +} + +static void proc_ov511_create(void) +{ + /* No current standard here. Alan prefers /proc/video/ as it keeps + * /proc "less cluttered than /proc/randomcardifoundintheshed/" + * -claudio + */ + if (video_proc_entry == NULL) { + err("Unable to initialise /proc/video/ov511"); + return; + } + + ov511_proc_entry = create_proc_entry("ov511", S_IFDIR, video_proc_entry); + + if (ov511_proc_entry) + ov511_proc_entry->owner = THIS_MODULE; + else + err("Unable to initialise /proc/ov511"); +} + +static void proc_ov511_destroy(void) +{ + PDEBUG (3, "removing /proc/video/ov511"); + + if (ov511_proc_entry == NULL) + return; + + remove_proc_entry("ov511", video_proc_entry); +} +#endif /* CONFIG_PROC_FS && CONFIG_VIDEO_PROC_FS */ + + +/********************************************************************** + * + * Camera interface + * + **********************************************************************/ + +static int ov511_reg_write(struct usb_device *dev, + unsigned char reg, + unsigned char value) +{ + int rc; + + rc = usb_control_msg(dev, + usb_sndctrlpipe(dev, 0), + 2 /* REG_IO */, + USB_TYPE_CLASS | USB_RECIP_DEVICE, + 0, (__u16)reg, &value, 1, HZ); + + PDEBUG(5, "reg write: 0x%02X:0x%02X, 0x%x", reg, value, rc); + + if (rc < 0) + err("ov511_reg_write: error %d", rc); + + return rc; +} + +/* returns: negative is error, pos or zero is data */ +static int ov511_reg_read(struct usb_device *dev, unsigned char reg) +{ + int rc; + unsigned char buffer[1]; + + rc = usb_control_msg(dev, + usb_rcvctrlpipe(dev, 0), + 2 /* REG_IO */, + USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE, + 0, (__u16)reg, buffer, 1, HZ); + + PDEBUG(5, "reg read: 0x%02X:0x%02X", reg, buffer[0]); + + if (rc < 0) { + err("ov511_reg_read: error %d", rc); + return rc; + } else { + return buffer[0]; + } +} + +static int ov511_i2c_write(struct usb_device *dev, + unsigned char reg, + unsigned char value) +{ + int rc, retries; + + PDEBUG(5, "i2c write: 0x%02X:0x%02X", reg, value); + + /* Three byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_3_BYTE, reg); + if (rc < 0) goto error; + + /* Write "value" to I2C data port of OV511 */ + rc = ov511_reg_write(dev, OV511_REG_I2C_DATA_PORT, value); + if (rc < 0) goto error; + + /* Initiate 3-byte write cycle */ + rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x01); + if (rc < 0) goto error; + + do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) goto error; + + if ((rc&2) == 0) /* Ack? */ + break; +#if 0 + /* I2C abort */ + ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); +#endif + if (--retries < 0) { + err("i2c write retries exhausted"); + rc = -1; + goto error; + } + } + + return 0; + +error: + err("ov511_i2c_write: error %d", rc); + return rc; +} + +/* returns: negative is error, pos or zero is data */ +static int ov511_i2c_read(struct usb_device *dev, unsigned char reg) +{ + int rc, value, retries; + + /* Two byte write cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Select camera register */ + rc = ov511_reg_write(dev, OV511_REG_I2C_SUB_ADDRESS_2_BYTE, reg); + if (rc < 0) goto error; + + /* Initiate 2-byte write cycle */ + rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x03); + if (rc < 0) goto error; + + do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) goto error; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); + + if (--retries < 0) { + err("i2c write retries exhausted"); + rc = -1; + goto error; + } + } + + /* Two byte read cycle */ + for (retries = OV511_I2C_RETRIES; ; ) { + /* Initiate 2-byte read cycle */ + rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); + if (rc < 0) goto error; + + do rc = ov511_reg_read(dev, OV511_REG_I2C_CONTROL); + while (rc > 0 && ((rc&1) == 0)); /* Retry until idle */ + if (rc < 0) goto error; + + if ((rc&2) == 0) /* Ack? */ + break; + + /* I2C abort */ + rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x10); + if (rc < 0) goto error; + + if (--retries < 0) { + err("i2c read retries exhausted"); + rc = -1; + goto error; + } + } + + value = ov511_reg_read(dev, OV511_REG_I2C_DATA_PORT); + + PDEBUG(5, "i2c read: 0x%02X:0x%02X", reg, value); + + /* This is needed to make ov511_i2c_write() work */ + rc = ov511_reg_write(dev, OV511_REG_I2C_CONTROL, 0x05); + if (rc < 0) + goto error; + + return value; + +error: + err("ov511_i2c_read: error %d", rc); + return rc; +} + +static int ov511_write_regvals(struct usb_device *dev, + struct ov511_regvals * pRegvals) +{ + int rc; + + while (pRegvals->bus != OV511_DONE_BUS) { + if (pRegvals->bus == OV511_REG_BUS) { + if ((rc = ov511_reg_write(dev, pRegvals->reg, + pRegvals->val)) < 0) + goto error; + } else if (pRegvals->bus == OV511_I2C_BUS) { + if ((rc = ov511_i2c_write(dev, pRegvals->reg, + pRegvals->val)) < 0) + goto error; + } else { + err("Bad regval array"); + rc = -1; + goto error; + } + pRegvals++; + } + return 0; + +error: + err("ov511_write_regvals: error %d", rc); + return rc; +} + +#ifdef OV511_DEBUG +static void ov511_dump_i2c_range(struct usb_device *dev, int reg1, int regn) +{ + int i; + int rc; + for(i = reg1; i <= regn; i++) { + rc = ov511_i2c_read(dev, i); + PDEBUG(1, "OV7610[0x%X] = 0x%X", i, rc); + } +} + +static void ov511_dump_i2c_regs(struct usb_device *dev) +{ + PDEBUG(3, "I2C REGS"); + ov511_dump_i2c_range(dev, 0x00, 0x38); +} + +static void ov511_dump_reg_range(struct usb_device *dev, int reg1, int regn) +{ + int i; + int rc; + for(i = reg1; i <= regn; i++) { + rc = ov511_reg_read(dev, i); + PDEBUG(1, "OV511[0x%X] = 0x%X", i, rc); + } +} + +#if 0 +static void ov511_dump_regs(struct usb_device *dev) +{ + PDEBUG(1, "CAMERA INTERFACE REGS"); + ov511_dump_reg_range(dev, 0x10, 0x1f); + PDEBUG(1, "DRAM INTERFACE REGS"); + ov511_dump_reg_range(dev, 0x20, 0x23); + PDEBUG(1, "ISO FIFO REGS"); + ov511_dump_reg_range(dev, 0x30, 0x31); + PDEBUG(1, "PIO REGS"); + ov511_dump_reg_range(dev, 0x38, 0x39); + ov511_dump_reg_range(dev, 0x3e, 0x3e); + PDEBUG(1, "I2C REGS"); + ov511_dump_reg_range(dev, 0x40, 0x49); + PDEBUG(1, "SYSTEM CONTROL REGS"); + ov511_dump_reg_range(dev, 0x50, 0x53); + ov511_dump_reg_range(dev, 0x5e, 0x5f); + PDEBUG(1, "OmniCE REGS"); + ov511_dump_reg_range(dev, 0x70, 0x79); + ov511_dump_reg_range(dev, 0x80, 0x9f); + ov511_dump_reg_range(dev, 0xa0, 0xbf); + +} +#endif +#endif + +static int ov511_reset(struct usb_device *dev, unsigned char reset_type) +{ + int rc; + + PDEBUG(3, "Reset: type=0x%X", reset_type); + rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, reset_type); + rc = ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0); + + if (rc < 0) + err("reset: command failed"); + + return rc; +} + +/* Temporarily stops OV511 from functioning. Must do this before changing + * registers while the camera is streaming */ +static inline int ov511_stop(struct usb_device *dev) +{ + PDEBUG(4, "stopping"); + return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x3d)); +} + +/* Restarts OV511 after ov511_stop() is called */ +static inline int ov511_restart(struct usb_device *dev) +{ + PDEBUG(4, "restarting"); + return (ov511_reg_write(dev, OV511_REG_SYSTEM_RESET, 0x00)); +} + +static int ov511_set_packet_size(struct usb_ov511 *ov511, int size) +{ + int alt, mult; + + if (ov511_stop(ov511->dev) < 0) + return -EIO; + + mult = size >> 5; + + if (ov511->bridge == BRG_OV511) { + if (size == 0) alt = OV511_ALT_SIZE_0; + else if (size == 257) alt = OV511_ALT_SIZE_257; +// else if (size == 512) alt = OV511_ALT_SIZE_512; + else if (size == 513) alt = OV511_ALT_SIZE_513; +// else if (size == 768) alt = OV511_ALT_SIZE_768; + else if (size == 769) alt = OV511_ALT_SIZE_769; +// else if (size == 992) alt = OV511_ALT_SIZE_992; + else if (size == 993) alt = OV511_ALT_SIZE_993; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else if (ov511->bridge == BRG_OV511PLUS) { + if (size == 0) alt = OV511PLUS_ALT_SIZE_0; + else if (size == 33) alt = OV511PLUS_ALT_SIZE_33; + else if (size == 129) alt = OV511PLUS_ALT_SIZE_129; + else if (size == 257) alt = OV511PLUS_ALT_SIZE_257; + else if (size == 385) alt = OV511PLUS_ALT_SIZE_385; + else if (size == 513) alt = OV511PLUS_ALT_SIZE_513; + else if (size == 769) alt = OV511PLUS_ALT_SIZE_769; + else if (size == 961) alt = OV511PLUS_ALT_SIZE_961; + else { + err("Set packet size: invalid size (%d)", size); + return -EINVAL; + } + } else { + err("Set packet size: Invalid bridge type"); + return -EINVAL; + } + + PDEBUG(3, "set packet size: %d, mult=%d, alt=%d", size, mult, alt); + + if (ov511_reg_write(ov511->dev, OV511_REG_FIFO_PACKET_SIZE, mult) < 0) + return -ENOMEM; + + if (usb_set_interface(ov511->dev, ov511->iface, alt) < 0) { + err("Set packet size: set interface error"); + return -EBUSY; + } + + // FIXME - Should we only reset the FIFO? + if (ov511_reset(ov511->dev, OV511_RESET_NOREGS) < 0) + return -ENOMEM; + + ov511->packet_size = size; + + if (ov511_restart(ov511->dev) < 0) + return -EIO; + + return 0; +} + + +static inline int +ov7610_set_picture(struct usb_ov511 *ov511, struct video_picture *p) +{ + int ret; + struct usb_device *dev = ov511->dev; + + PDEBUG(4, "ov511_set_picture"); + + if (ov511_stop(dev) < 0) + return -EIO; + + ov511->contrast = p->contrast; + ov511->brightness = p->brightness; + ov511->colour = p->colour; + ov511->hue = p->hue; + ov511->whiteness = p->whiteness; + + if ((ret = ov511_i2c_read(dev, OV7610_REG_COM_B)) < 0) + return -EIO; +#if 0 + /* disable auto adjust mode */ + if (ov511_i2c_write(dev, OV7610_REG_COM_B, ret & 0xfe) < 0) + return -EIO; +#endif + if (ov511->sensor == SEN_OV7610 || ov511->sensor == SEN_OV7620AE) + if(ov511_i2c_write(dev, OV7610_REG_SAT, p->colour >> 8) < 0) + return -EIO; + + if (ov511->sensor == SEN_OV7610) { + if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + return -EIO; + + if(ov511_i2c_write(dev, OV7610_REG_BRT, p->brightness >> 8) < 0) + return -EIO; + } else if ((ov511->sensor == SEN_OV7620) + || (ov511->sensor == SEN_OV7620AE)) { +#if 0 + cur_con = ov511_i2c_read(dev, OV7610_REG_CNT); + cur_brt = ov511_i2c_read(dev, OV7610_REG_BRT); + // DEBUG_CODE + PDEBUG(1, "con=%d brt=%d", ov511_i2c_read(dev, OV7610_REG_CNT), + ov511_i2c_read(dev, OV7610_REG_BRT)); + + if(ov511_i2c_write(dev, OV7610_REG_CNT, p->contrast >> 8) < 0) + return -EIO; +#endif + } + + if (ov511_restart(dev) < 0) + return -EIO; + + return 0; +} + +static inline int +ov7610_get_picture(struct usb_ov511 *ov511, struct video_picture *p) +{ + int ret; + struct usb_device *dev = ov511->dev; + + PDEBUG(4, "ov511_get_picture"); + + if (ov511_stop(dev) < 0) + return -EIO; + + if((ret = ov511_i2c_read(dev, OV7610_REG_SAT)) < 0) return -EIO; + p->colour = ret << 8; + + if((ret = ov511_i2c_read(dev, OV7610_REG_CNT)) < 0) return -EIO; + p->contrast = ret << 8; + + if((ret = ov511_i2c_read(dev, OV7610_REG_BRT)) < 0) return -EIO; + p->brightness = ret << 8; + + p->hue = 0x8000; + p->whiteness = 105 << 8; + + /* Can we get these from frame[0]? -claudio? */ + p->depth = ov511->frame[0].depth; + p->palette = ov511->frame[0].format; + + if (ov511_restart(dev) < 0) + return -EIO; + + return 0; +} + +/* FIXME: 176x144, 160x140 */ +/* LNCNT values fixed by Lawrence Glaister */ +static struct mode_list mlist[] = { + /* W H C PXCNT LNCNT PXDIV LNDIV M420 COMA COMC COML */ + { 640, 480, 0, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, + { 640, 480, 1, 0x4f, 0x3b, 0x00, 0x00, 0x03, 0x24, 0x04, 0x9e }, + { 320, 240, 0, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, + { 320, 240, 1, 0x27, 0x1d, 0x00, 0x00, 0x03, 0x04, 0x24, 0x1e }, + { 352, 288, 0, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 352, 288, 1, 0x2b, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, 0, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 384, 288, 1, 0x2f, 0x25, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, 0, 0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 448, 336, 1 ,0x37, 0x29, 0x00, 0x00, 0x03, 0x04, 0x04, 0x1e }, + { 0, 0 } +}; + +static int +ov511_mode_init_regs(struct usb_ov511 *ov511, + int width, int height, int mode, int sub_flag) +{ + int i; + struct usb_device *dev = ov511->dev; + int hwsbase = 0, hwebase = 0; + + PDEBUG(3, "width:%d, height:%d, mode:%d, sub:%d", + width, height, mode, sub_flag); + + if (ov511_stop(ov511->dev) < 0) + return -EIO; + + if (mode == VIDEO_PALETTE_GREY) { + ov511_reg_write(dev, 0x16, 0x00); + if (ov511->sensor == SEN_OV7610 + || ov511->sensor == SEN_OV7620AE) { + /* these aren't valid on the OV7620 */ + ov511_i2c_write(dev, 0x0e, 0x44); + } + ov511_i2c_write(dev, 0x13, autoadjust ? 0x21 : 0x20); + + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x00); + ov511_reg_write(dev, 0x1f, 0x01); + } else { + ov511_reg_write(dev, 0x16, 0x01); + if (ov511->sensor == SEN_OV7610 + || ov511->sensor == SEN_OV7620AE) { + /* not valid on the OV7620 */ + ov511_i2c_write(dev, 0x0e, 0x04); + } + ov511_i2c_write(dev, 0x13, autoadjust ? 0x01 : 0x00); + + /* For snapshot */ + ov511_reg_write(dev, 0x1e, 0x01); + ov511_reg_write(dev, 0x1f, 0x03); + } + + /* The different sensor ICs handle setting up of window differently */ + switch (ov511->sensor) { + case SEN_OV7610: + case SEN_OV7620AE: + hwsbase = 0x38; + hwebase = 0x3a; + break; + case SEN_OV7620: + hwsbase = 0x2c; + hwebase = 0x2d; + break; + default: + hwsbase = 0; + hwebase = 0; + break; + } + + if (sub_flag) { + ov511_i2c_write(dev, 0x17, hwsbase+(ov511->subx>>2)); + ov511_i2c_write(dev, 0x18, hwebase+((ov511->subx+ov511->subw)>>2)); + ov511_i2c_write(dev, 0x19, 0x5+(ov511->suby>>1)); + ov511_i2c_write(dev, 0x1a, 0x5+((ov511->suby+ov511->subh)>>1)); + } else { + ov511_i2c_write(dev, 0x17, hwsbase); + ov511_i2c_write(dev, 0x18, hwebase + (640>>2)); + ov511_i2c_write(dev, 0x19, 0x5); + ov511_i2c_write(dev, 0x1a, 5 + (480>>1)); + } + + for (i = 0; mlist[i].width; i++) { + int lncnt, pxcnt, clock; + + if (width != mlist[i].width || height != mlist[i].height) + continue; + + if (!mlist[i].color && mode != VIDEO_PALETTE_GREY) + continue; + + /* Here I'm assuming that snapshot size == image size. + * I hope that's always true. --claudio + */ + pxcnt = sub_flag ? (ov511->subw >> 3) - 1 : mlist[i].pxcnt; + lncnt = sub_flag ? (ov511->subh >> 3) - 1 : mlist[i].lncnt; + + ov511_reg_write(dev, 0x12, pxcnt); + ov511_reg_write(dev, 0x13, lncnt); + ov511_reg_write(dev, 0x14, mlist[i].pxdv); + ov511_reg_write(dev, 0x15, mlist[i].lndv); + ov511_reg_write(dev, 0x18, mlist[i].m420); + + /* Snapshot additions */ + ov511_reg_write(dev, 0x1a, pxcnt); + ov511_reg_write(dev, 0x1b, lncnt); + ov511_reg_write(dev, 0x1c, mlist[i].pxdv); + ov511_reg_write(dev, 0x1d, mlist[i].lndv); + + /* Calculate and set the clock divisor */ + clock = ((sub_flag ? ov511->subw * ov511->subh : width * height) + * (mlist[i].color ? 3 : 2) / 2) / 66000; + ov511_i2c_write(dev, 0x11, clock); + +#ifdef OV511_GBR422 + ov511_i2c_write(dev, 0x12, mlist[i].common_A | 0x08); +#else + ov511_i2c_write(dev, 0x12, mlist[i].common_A); +#endif + ov511_i2c_write(dev, 0x14, mlist[i].common_C); + + /* 7620 doesn't have register 0x35, so play it safe */ + if (ov511->sensor != SEN_OV7620) + ov511_i2c_write(dev, 0x35, mlist[i].common_L); + + break; + } + + if (ov511_restart(ov511->dev) < 0) + return -EIO; + + if (mlist[i].width == 0) { + err("Unknown mode (%d, %d): %d", width, height, mode); + return -EINVAL; + } + +#ifdef OV511_DEBUG + if (debug >= 5) + ov511_dump_i2c_regs(dev); +#endif + + return 0; +} + +/********************************************************************** + * + * Color correction functions + * + **********************************************************************/ + +/* + * Turn a YUV4:2:0 block into an RGB block + * + * Video4Linux seems to use the blue, green, red channel + * order convention-- rgb[0] is blue, rgb[1] is green, rgb[2] is red. + * + * Color space conversion coefficients taken from the excellent + * http://www.inforamp.net/~poynton/ColorFAQ.html + * In his terminology, this is a CCIR 601.1 YCbCr -> RGB. + * Y values are given for all 4 pixels, but the U (Pb) + * and V (Pr) are assumed constant over the 2x2 block. + * + * To avoid floating point arithmetic, the color conversion + * coefficients are scaled into 16.16 fixed-point integers. + */ + +/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */ +#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16))) + +static inline void +ov511_move_420_block(int yTL, int yTR, int yBL, int yBR, int u, int v, + int rowPixels, unsigned char * rgb) +{ + const int rvScale = (1402000 * 1024) / 16525; /* 1.402 */ + const int guScale = (-344136 * 1024) / 16525; /* -.344136 */ + const int gvScale = (-714136 * 1024) / 16525; /* -.714136 */ + const int buScale = (1772000 * 1024) / 16525; /* 1.772 */ + const int yScale = (1000000 * 1024) / 16525; /* 1.0 */ + int r, g, b; + + g = guScale * u + gvScale * v; + if (force_rgb) { + r = buScale * u; + b = rvScale * v; + } else { + r = rvScale * v; + b = buScale * u; + } + + yTL *= yScale; yTR *= yScale; + yBL *= yScale; yBR *= yScale; + + /* Write out top two pixels */ + rgb[0] = LIMIT(b+yTL); rgb[1] = LIMIT(g+yTL); rgb[2] = LIMIT(r+yTL); + rgb[3] = LIMIT(b+yTR); rgb[4] = LIMIT(g+yTR); rgb[5] = LIMIT(r+yTR); + + /* Skip down to next line to write out bottom two pixels */ + rgb += 3 * rowPixels; + rgb[0] = LIMIT(b+yBL); rgb[1] = LIMIT(g+yBL); rgb[2] = LIMIT(r+yBL); + rgb[3] = LIMIT(b+yBR); rgb[4] = LIMIT(g+yBR); rgb[5] = LIMIT(r+yBR); +} + +/* + * For a 640x480 YUV4:2:0 images, data shows up in 1200 384 byte segments. + * The first 64 bytes of each segment are U, the next 64 are V. The U and + * V are arranged as follows: + * + * 0 1 ... 7 + * 8 9 ... 15 + * ... + * 56 57 ... 63 + * + * U and V are shipped at half resolution (1 U,V sample -> one 2x2 block). + * + * The next 256 bytes are full resolution Y data and represent 4 squares + * of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 ... 248 249 ... 255 + * + * Note that the U and V data in one segment represents a 16 x 16 pixel + * area, but the Y data represents a 32 x 8 pixel area. + * + * If OV511_DUMPPIX is defined, _parse_data just dumps the incoming segments, + * verbatim, in order, into the frame. When used with vidcat -f ppm -s 640x480 + * this puts the data on the standard output and can be analyzed with the + * parseppm.c utility I wrote. That's a much faster way for figuring out how + * this data is scrambled. + */ + +#define HDIV 8 +#define WDIV (256/HDIV) + +#undef OV511_DUMPPIX + +#ifdef OV511_GBR422 +static void +ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + pIn = pIn0; + pOut = pOut0 + iOutUV + (force_rgb ? 2 : 0); + + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) = + *(pOut1 + iWidth*3 + 3) = *pIn++; + pOut1 += 6; + } + pOut += iWidth*3*2; + } + + pIn = pIn0 + 64; + pOut = pOut0 + iOutUV + (force_rgb ? 0 : 2); + for (k = 0; k < 8; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + *pOut1 = *(pOut1 + 3) = *(pOut1 + iWidth*3) = + *(pOut1 + iWidth*3 + 3) = *pIn++; + pOut1 += 6; + } + pOut += iWidth*3*2; + } + + pIn = pIn0 + 128; + pOut = pOut0 + iOutY + 1; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = *pIn++; + pOut1 += 3; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } +} + +#else + +static void +ov511_parse_data_rgb24(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iOutUV, int iHalf, int iWidth) +{ +#ifndef OV511_DUMPPIX + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + /* Just copy the Y's if in the first stripe */ + if (!iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1 = *pIn++; + pOut1 += 3; + } + pOut1 += (iWidth - 8) * 3; + } + pOut += 8 * 3; + } + } + + /* Use the first half of VUs to calculate value */ + pIn = pIn0; + pOut = pOut0 + iOutUV; + for (l = 0; l < 4; l++) { + for (m=0; m<8; m++) { + int y00 = *(pOut); + int y01 = *(pOut+3); + int y10 = *(pOut+iWidth*3); + int y11 = *(pOut+iWidth*3+3); + int v = *(pIn+64) - 128; + int u = *pIn++ - 128; + ov511_move_420_block(y00, y01, y10, y11, u, v, iWidth, + pOut); + pOut += 6; + } + pOut += (iWidth*2 - 16) * 3; + } + + /* Just copy the other UV rows */ + for (l = 0; l < 4; l++) { + for (m = 0; m < 8; m++) { + *pOut++ = *(pIn + 64); + *pOut = *pIn++; + pOut += 5; + } + pOut += (iWidth*2 - 16) * 3; + } + + /* Calculate values if it's the second half */ + if (iHalf) { + pIn = pIn0 + 128; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l=0; l<4; l++) { + for (m=0; m<4; m++) { + int y10 = *(pIn+8); + int y00 = *pIn++; + int y11 = *(pIn+8); + int y01 = *pIn++; + int v = *pOut1 - 128; + int u = *(pOut1+1) - 128; + ov511_move_420_block(y00, y01, y10, + y11, u, v, iWidth, pOut1); + pOut1 += 6; + } + pOut1 += (iWidth*2 - 8) * 3; + pIn += 8; + } + pOut += 8 * 3; + } + } +#else + /* Just dump pix data straight out for debug */ + int i, j; + + pOut0 += iOutY; + for (i = 0; i < HDIV; i++) { + for (j = 0; j < WDIV; j++) { + *pOut0++ = *pIn0++; + *pOut0++ = *pIn0++; + *pOut0++ = *pIn0++; + } + pOut0 += (iWidth - WDIV) * 3; + } +#endif +} +#endif + +/* + * For 640x480 RAW BW images, data shows up in 1200 256 byte segments. + * The segments represent 4 squares of 8x8 pixels as follows: + * + * 0 1 ... 7 64 65 ... 71 ... 192 193 ... 199 + * 8 9 ... 15 72 73 ... 79 200 201 ... 207 + * ... ... ... + * 56 57 ... 63 120 121 ... 127 248 249 ... 255 + * + */ +static void +ov511_parse_data_grey(unsigned char *pIn0, unsigned char *pOut0, + int iOutY, int iWidth) +{ + int k, l, m; + unsigned char *pIn; + unsigned char *pOut, *pOut1; + + pIn = pIn0; + pOut = pOut0 + iOutY; + for (k = 0; k < 4; k++) { + pOut1 = pOut; + for (l = 0; l < 8; l++) { + for (m = 0; m < 8; m++) { + *pOut1++ = *pIn++; + } + pOut1 += iWidth - 8; + } + pOut += 8; + } +} + +/* + * fixFrameRGBoffset-- + * My camera seems to return the red channel about 1 pixel + * low, and the blue channel about 1 pixel high. After YUV->RGB + * conversion, we can correct this easily. OSL 2/24/2000. + */ +static void fixFrameRGBoffset(struct ov511_frame *frame) +{ + int x, y; + int rowBytes = frame->width*3, w = frame->width; + unsigned char *rgb = frame->data; + const int shift = 1; /* Distance to shift pixels by, vertically */ + + /* Don't bother with little images */ + if (frame->width < 400) + return; + + /* Shift red channel up */ + for (y = shift; y < frame->height; y++) { + int lp = (y-shift)*rowBytes; /* Previous line offset */ + int lc = y*rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[lp+x*3+2] = rgb[lc+x*3+2]; /* Shift red up */ + } + + /* Shift blue channel down */ + for (y = frame->height-shift-1; y >= 0; y--) { + int ln = (y + shift) * rowBytes; /* Next line offset */ + int lc = y * rowBytes; /* Current line offset */ + for (x = 0; x < w; x++) + rgb[ln+x*3+0] = rgb[lc+x*3+0]; /* Shift blue down */ + } +} + +/********************************************************************** + * + * OV511 data transfer, IRQ handler + * + **********************************************************************/ + +static int ov511_move_data(struct usb_ov511 *ov511, urb_t *urb) +{ + unsigned char *cdata; + int i, totlen = 0; + int aPackNum[10]; + struct ov511_frame *frame; + unsigned char *pData; + int iPix; + + PDEBUG (4, "Moving %d packets", urb->number_of_packets); + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = 0; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + aPackNum[i] = n ? cdata[ov511->packet_size - 1] : -1; + + if (!n || ov511->curframe == -1) + continue; + + if (st) + PDEBUG(2, "data error: [%d] len=%d, status=%d", i, n, st); + + frame = &ov511->frame[ov511->curframe]; + + /* SOF/EOF packets have 1st to 8th bytes zeroed and the 9th + * byte non-zero. The EOF packet has image width/height in the + * 10th and 11th packets. The 9th bit is given as follows: + * + * bit 7: EOF + * 6: compression enabled + * 5: 422/420/400 modes + * 4: 422/420/400 modes + * 3: 1 + * 2: snapshot bottom on + * 1: snapshot frame + * 0: even/odd field + */ + + /* Check for SOF/EOF packet */ + if ((cdata[0] | cdata[1] | cdata[2] | cdata[3] | + cdata[4] | cdata[5] | cdata[6] | cdata[7]) || + (~cdata[8] & 0x08)) + goto check_middle; + + /* Frame end */ + if (cdata[8] & 0x80) { + struct timeval *ts; + + ts = (struct timeval *)(frame->data + MAX_FRAME_SIZE); + do_gettimeofday (ts); + + PDEBUG(4, "Frame end, curframe = %d, packnum=%d, hw=%d, vw=%d", + ov511->curframe, (int)(cdata[ov511->packet_size - 1]), + (int)(cdata[9]), (int)(cdata[10])); + + if (frame->scanstate == STATE_LINES) { + int iFrameNext; + + if (fix_rgb_offset) + fixFrameRGBoffset(frame); + frame->grabstate = FRAME_DONE; + + if (waitqueue_active(&frame->wq)) { + frame->grabstate = FRAME_DONE; + wake_up_interruptible(&frame->wq); + } + + /* If next frame is ready or grabbing, + * point to it */ + iFrameNext = (ov511->curframe + 1) % OV511_NUMFRAMES; + if (ov511->frame[iFrameNext].grabstate == FRAME_READY + || ov511->frame[iFrameNext].grabstate == FRAME_GRABBING) { + ov511->curframe = iFrameNext; + ov511->frame[iFrameNext].scanstate = STATE_SCANNING; + } else { + if (frame->grabstate == FRAME_DONE) { + PDEBUG(4, "Frame done! congratulations"); + } else { + PDEBUG(4, "Frame not ready? state = %d", + ov511->frame[iFrameNext].grabstate); + } + + ov511->curframe = -1; + } + } + /* Image corruption caused by misplaced frame->segment = 0 + * fixed by carlosf@conectiva.com.br + */ + } else { + /* Frame start */ + PDEBUG(4, "Frame start, framenum = %d", ov511->curframe); + + /* Check to see if it's a snapshot frame */ + /* FIXME?? Should the snapshot reset go here? Performance? */ + if (cdata[8] & 0x02) { + frame->snapshot = 1; + PDEBUG(3, "snapshot detected"); + } + + frame->scanstate = STATE_LINES; + frame->segment = 0; + } + +check_middle: + /* Are we in a frame? */ + if (frame->scanstate != STATE_LINES) + continue; + + /* Deal with leftover from last segment, if any */ + if (frame->segment) { + pData = ov511->scratch; + iPix = -ov511->scratchlen; + memmove(pData + ov511->scratchlen, cdata, + iPix+frame->segsize); + } else { + pData = &cdata[iPix = 9]; + } + + /* Parse the segments */ + while (iPix <= (ov511->packet_size - 1) - frame->segsize && + frame->segment < frame->width * frame->height / 256) { + int iSegY, iSegUV; + int iY, jY, iUV, jUV; + int iOutY, iOutUV; + unsigned char *pOut; + + iSegY = iSegUV = frame->segment; + pOut = frame->data; + frame->segment++; + iPix += frame->segsize; + + /* Handle subwindow */ + if (frame->sub_flag) { + int iSeg1; + + iSeg1 = iSegY / (ov511->subw / 32); + iSeg1 *= frame->width / 32; + iSegY = iSeg1 + (iSegY % (ov511->subw / 32)); + if (iSegY >= frame->width * ov511->subh / 256) + break; + + iSeg1 = iSegUV / (ov511->subw / 16); + iSeg1 *= frame->width / 16; + iSegUV = iSeg1 + (iSegUV % (ov511->subw / 16)); + + pOut += (ov511->subx + ov511->suby * frame->width) * + (frame->depth >> 3); + } + + /* + * i counts segment lines + * j counts segment columns + * iOut is the offset (in bytes) of the upper left corner + */ + iY = iSegY / (frame->width / WDIV); + jY = iSegY - iY * (frame->width / WDIV); + iOutY = (iY*HDIV*frame->width + jY*WDIV) * (frame->depth >> 3); + iUV = iSegUV / (frame->width / WDIV * 2); + jUV = iSegUV - iUV * (frame->width / WDIV * 2); + iOutUV = (iUV*HDIV*2*frame->width + jUV*WDIV/2) * (frame->depth >> 3); + + switch (frame->format) { + case VIDEO_PALETTE_GREY: + ov511_parse_data_grey (pData, pOut, iOutY, frame->width); + break; + case VIDEO_PALETTE_RGB24: + ov511_parse_data_rgb24 (pData, pOut, iOutY, iOutUV, + iY & 1, frame->width); + break; + } + + pData = &cdata[iPix]; + } + + /* Save extra data for next time */ + if (frame->segment < frame->width * frame->height / 256) { + ov511->scratchlen = (ov511->packet_size - 1) - iPix; + if (ov511->scratchlen < frame->segsize) + memmove(ov511->scratch, pData, ov511->scratchlen); + else + ov511->scratchlen = 0; + } + } + + PDEBUG(5, "pn: %d %d %d %d %d %d %d %d %d %d", + aPackNum[0], aPackNum[1], aPackNum[2], aPackNum[3], aPackNum[4], + aPackNum[5],aPackNum[6], aPackNum[7], aPackNum[8], aPackNum[9]); + + return totlen; +} + +static void ov511_isoc_irq(struct urb *urb) +{ + int len; + struct usb_ov511 *ov511 = urb->context; + struct ov511_sbuf *sbuf; + + if (!ov511->dev) + return; + + if (!ov511->streaming) { + PDEBUG(2, "hmmm... not streaming, but got interrupt"); + return; + } + + sbuf = &ov511->sbuf[ov511->cursbuf]; + + /* Copy the data received into our scratch buffer */ + if (ov511->curframe >= 0) { + len = ov511_move_data(ov511, urb); + } else if (waitqueue_active(&ov511->wq)) { + wake_up_interruptible(&ov511->wq); + } + + /* Move to the next sbuf */ + ov511->cursbuf = (ov511->cursbuf + 1) % OV511_NUMSBUF; + + return; +} + +static int ov511_init_isoc(struct usb_ov511 *ov511) +{ + urb_t *urb; + int fx, err; + + PDEBUG(3, "*** Initializing capture ***"); + + ov511->compress = 0; + ov511->curframe = -1; + ov511->cursbuf = 0; + ov511->scratchlen = 0; + + if (ov511->bridge == BRG_OV511) + ov511_set_packet_size(ov511, 993); + else if (ov511->bridge == BRG_OV511PLUS) + ov511_set_packet_size(ov511, 961); + else + err("invalid bridge type"); + + /* We double buffer the Iso lists */ + urb = usb_alloc_urb(FRAMES_PER_DESC); + + if (!urb) { + err("ov511_init_isoc: usb_alloc_urb ret. NULL"); + return -ENOMEM; + } + ov511->sbuf[0].urb = urb; + urb->dev = ov511->dev; + urb->context = ov511; + urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ov511->sbuf[0].data; + urb->complete = ov511_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = ov511->packet_size * fx; + urb->iso_frame_desc[fx].length = ov511->packet_size; + } + + urb = usb_alloc_urb(FRAMES_PER_DESC); + if (!urb) { + err("ov511_init_isoc: usb_alloc_urb ret. NULL"); + return -ENOMEM; + } + ov511->sbuf[1].urb = urb; + urb->dev = ov511->dev; + urb->context = ov511; + urb->pipe = usb_rcvisocpipe(ov511->dev, OV511_ENDPOINT_ADDRESS); + urb->transfer_flags = USB_ISO_ASAP; + urb->transfer_buffer = ov511->sbuf[1].data; + urb->complete = ov511_isoc_irq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = ov511->packet_size * FRAMES_PER_DESC; + for (fx = 0; fx < FRAMES_PER_DESC; fx++) { + urb->iso_frame_desc[fx].offset = ov511->packet_size * fx; + urb->iso_frame_desc[fx].length = ov511->packet_size; + } + + ov511->sbuf[1].urb->next = ov511->sbuf[0].urb; + ov511->sbuf[0].urb->next = ov511->sbuf[1].urb; + + err = usb_submit_urb(ov511->sbuf[0].urb); + if (err) + err("ov511_init_isoc: usb_submit_urb(0) ret %d", err); + err = usb_submit_urb(ov511->sbuf[1].urb); + if (err) + err("ov511_init_isoc: usb_submit_urb(1) ret %d", err); + + ov511->streaming = 1; + + return 0; +} + +static void ov511_stop_isoc(struct usb_ov511 *ov511) +{ + if (!ov511->streaming || !ov511->dev) + return; + + PDEBUG (3, "*** Stopping capture ***"); + + ov511_set_packet_size(ov511, 0); + + ov511->streaming = 0; + + /* Unschedule all of the iso td's */ + if (ov511->sbuf[1].urb) { + ov511->sbuf[1].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[1].urb); + usb_free_urb(ov511->sbuf[1].urb); + ov511->sbuf[1].urb = NULL; + } + if (ov511->sbuf[0].urb) { + ov511->sbuf[0].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[0].urb); + usb_free_urb(ov511->sbuf[0].urb); + ov511->sbuf[0].urb = NULL; + } +} + +static int ov511_new_frame(struct usb_ov511 *ov511, int framenum) +{ + struct ov511_frame *frame; + int width, height; + + PDEBUG(4, "ov511->curframe = %d, framenum = %d", ov511->curframe, + framenum); + if (!ov511->dev) + return -1; + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (ov511->curframe == -1) { + if (ov511->frame[(framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES].grabstate == FRAME_READY) + framenum = (framenum - 1 + OV511_NUMFRAMES) % OV511_NUMFRAMES; + } else + return 0; + + frame = &ov511->frame[framenum]; + width = frame->width; + height = frame->height; + + PDEBUG (4, "framenum = %d, width = %d, height = %d", framenum, width, + height); + + frame->grabstate = FRAME_GRABBING; + frame->scanstate = STATE_SCANNING; + frame->scanlength = 0; /* accumulated in ov511_parse_data() */ + frame->snapshot = 0; + + ov511->curframe = framenum; + + /* Make sure it's not too big */ + if (width > DEFAULT_WIDTH) + width = DEFAULT_WIDTH; + + width &= ~7L; /* Multiple of 8 */ + + if (height > DEFAULT_HEIGHT) + height = DEFAULT_HEIGHT; + + width &= ~3L; /* Multiple of 4 */ + + return 0; +} + +/**************************************************************************** + * + * V4L API + * + ***************************************************************************/ + +static int ov511_open(struct video_device *dev, int flags) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i, err = 0; + + MOD_INC_USE_COUNT; + PDEBUG(4, "opening"); + down(&ov511->lock); + + err = -EBUSY; + if (ov511->user) + goto out; + + err = -ENOMEM; + + /* Allocate memory for the frame buffers */ + ov511->fbuf = rvmalloc(OV511_NUMFRAMES * MAX_DATA_SIZE); + if (!ov511->fbuf) + goto out; + + ov511->sub_flag = 0; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].grabstate = FRAME_UNUSED; + ov511->frame[i].data = ov511->fbuf + i * MAX_DATA_SIZE; + PDEBUG(4, "frame [%d] @ %p", i, ov511->frame[0].data); + + ov511->sbuf[i].data = kmalloc(FRAMES_PER_DESC * + MAX_FRAME_SIZE_PER_DESC, GFP_KERNEL); + if (!ov511->sbuf[i].data) { +open_free_ret: + while (--i) kfree(ov511->sbuf[i].data); + rvfree(ov511->fbuf, 2 * MAX_DATA_SIZE); + goto out; + } + PDEBUG(4, "sbuf[%d] @ %p", i, ov511->sbuf[i].data); + } + + err = ov511_init_isoc(ov511); + if (err) + goto open_free_ret; + + ov511->user++; + +out: + up(&ov511->lock); + + if (err) + MOD_DEC_USE_COUNT; + + return err; +} + +static void ov511_close(struct video_device *dev) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; + + PDEBUG(4, "ov511_close"); + + down(&ov511->lock); + ov511->user--; + + ov511_stop_isoc(ov511); + + rvfree(ov511->fbuf, OV511_NUMFRAMES * MAX_DATA_SIZE); + for (i = 0; i < OV511_NUMFRAMES; i++) + kfree(ov511->sbuf[i].data); + + up(&ov511->lock); + + if (!ov511->dev) { + video_unregister_device(&ov511->vdev); + kfree(ov511); + } + + MOD_DEC_USE_COUNT; + +} + +static int ov511_init_done(struct video_device *dev) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + create_proc_ov511_cam((struct usb_ov511 *)dev); +#endif + + return 0; +} + +static long ov511_write(struct video_device *dev, const char *buf, unsigned long count, int noblock) +{ + return -EINVAL; +} + +static int ov511_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)vdev; + + PDEBUG(4, "IOCtl: 0x%X", cmd); + + if (!ov511->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability b; + + PDEBUG (4, "VIDIOCGCAP"); + + strcpy(b.name, "OV511 USB Camera"); + b.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = DEFAULT_WIDTH; + b.maxheight = DEFAULT_HEIGHT; + b.minwidth = 32; + b.minheight = 16; + + if (copy_to_user(arg, &b, sizeof(b))) + return -EFAULT; + + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + if (v.channel != 0) + return -EINVAL; + + v.flags = 0; + v.tuners = 0; + v.type = VIDEO_TYPE_CAMERA; + strcpy(v.name, "Camera"); + + if (copy_to_user(arg, &v, sizeof(v))) + return -EFAULT; + + return 0; + } + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, arg, sizeof(v))) + return -EFAULT; + + if (v != 0) + return -EINVAL; + + return 0; + } + case VIDIOCGPICT: + { + struct video_picture p; + + PDEBUG (4, "VIDIOCGPICT"); + + if (ov7610_get_picture(ov511, &p)) + return -EIO; + + if (copy_to_user(arg, &p, sizeof(p))) + return -EFAULT; + + return 0; + } + case VIDIOCSPICT: + { + struct video_picture p; + int i; + + PDEBUG (4, "VIDIOCSPICT"); + + if (copy_from_user(&p, arg, sizeof(p))) + return -EFAULT; + + if (ov7610_set_picture(ov511, &p)) + return -EIO; + + /* FIXME: check validity */ + + PDEBUG(4, "Setting depth=%d, palette=%d", p.depth, p.palette); + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].depth = p.depth; + ov511->frame[i].format = p.palette; + ov511->frame[i].segsize = GET_SEGSIZE(p.palette); + } + + return 0; + } + case VIDIOCGCAPTURE: + { + int vf; + + PDEBUG (4, "VIDIOCGCAPTURE"); + + if (copy_from_user(&vf, arg, sizeof(vf))) + return -EFAULT; + ov511->sub_flag = vf; + return 0; + } + case VIDIOCSCAPTURE: + { + struct video_capture vc; + + if (copy_from_user(&vc, arg, sizeof(vc))) + return -EFAULT; + if (vc.flags) + return -EINVAL; + if (vc.decimation) + return -EINVAL; + + vc.x &= ~3L; + vc.y &= ~1L; + vc.y &= ~31L; + + if (vc.width == 0) + vc.width = 32; + + vc.height /= 16; + vc.height *= 16; + if (vc.height == 0) + vc.height = 16; + + ov511->subx = vc.x; + ov511->suby = vc.y; + ov511->subw = vc.width; + ov511->subh = vc.height; + + return 0; + } + case VIDIOCSWIN: + { + struct video_window vw; + int i, result; + + if (copy_from_user(&vw, arg, sizeof(vw))) + return -EFAULT; + + PDEBUG (4, "VIDIOCSWIN: width=%d, height=%d", + vw.width, vw.height); + +#if 0 + if (vw.flags) + return -EINVAL; + if (vw.clipcount) + return -EINVAL; + if (vw.height != DEFAULT_HEIGHT) + return -EINVAL; + if (vw.width != DEFAULT_WIDTH) + return -EINVAL; +#endif + + /* If we're collecting previous frame wait + before changing modes */ + interruptible_sleep_on(&ov511->wq); + if (signal_pending(current)) return -EINTR; + + result = ov511_mode_init_regs(ov511, vw.width, vw.height, + ov511->frame[0].format, ov511->sub_flag); + if (result < 0) + return result; + + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = vw.width; + ov511->frame[i].height = vw.height; + } + + return 0; + } + case VIDIOCGWIN: + { + struct video_window vw; + + vw.x = 0; /* FIXME */ + vw.y = 0; + vw.width = ov511->frame[0].width; + vw.height = ov511->frame[0].height; + vw.chromakey = 0; + vw.flags = 30; + + PDEBUG (4, "VIDIOCGWIN: %dx%d", vw.width, vw.height); + + if (copy_to_user(arg, &vw, sizeof(vw))) + return -EFAULT; + + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf vm; + + memset(&vm, 0, sizeof(vm)); + vm.size = 2 * MAX_DATA_SIZE; + vm.frames = 2; + vm.offsets[0] = 0; + vm.offsets[1] = MAX_FRAME_SIZE + sizeof (struct timeval); + + if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) + return -EFAULT; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + int ret; + + if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) + return -EFAULT; + + PDEBUG(4, "MCAPTURE"); + PDEBUG(4, "frame: %d, size: %dx%d, format: %d", + vm.frame, vm.width, vm.height, vm.format); + + if (vm.format != VIDEO_PALETTE_RGB24 && + vm.format != VIDEO_PALETTE_GREY) + return -EINVAL; + + if ((vm.frame != 0) && (vm.frame != 1)) + return -EINVAL; + + if (ov511->frame[vm.frame].grabstate == FRAME_GRABBING) + return -EBUSY; + + /* Don't compress if the size changed */ + if ((ov511->frame[vm.frame].width != vm.width) || + (ov511->frame[vm.frame].height != vm.height) || + (ov511->frame[vm.frame].format != vm.format) || + (ov511->frame[vm.frame].sub_flag != + ov511->sub_flag)) { + /* If we're collecting previous frame wait + before changing modes */ + interruptible_sleep_on(&ov511->wq); + if (signal_pending(current)) return -EINTR; + ret = ov511_mode_init_regs(ov511, vm.width, vm.height, + vm.format, ov511->sub_flag); +#if 0 + if (ret < 0) + return ret; +#endif + } + + ov511->frame[vm.frame].width = vm.width; + ov511->frame[vm.frame].height = vm.height; + ov511->frame[vm.frame].format = vm.format; + ov511->frame[vm.frame].sub_flag = ov511->sub_flag; + ov511->frame[vm.frame].segsize = GET_SEGSIZE(vm.format); + ov511->frame[vm.frame].depth = GET_DEPTH(vm.format); + + /* Mark it as ready */ + ov511->frame[vm.frame].grabstate = FRAME_READY; + + return ov511_new_frame(ov511, vm.frame); + } + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, arg, sizeof(int))) + return -EFAULT; + + PDEBUG(4, "syncing to frame %d, grabstate = %d", frame, + ov511->frame[frame].grabstate); + + switch (ov511->frame[frame].grabstate) { + case FRAME_UNUSED: + return -EINVAL; + case FRAME_READY: + case FRAME_GRABBING: + case FRAME_ERROR: +redo: + if (!ov511->dev) + return -EIO; + + do { +#if 0 + init_waitqueue_head(&ov511->frame[frame].wq); +#endif + interruptible_sleep_on(&ov511->frame[frame].wq); + if (signal_pending(current)) + return -EINTR; + } while (ov511->frame[frame].grabstate == FRAME_GRABBING); + + if (ov511->frame[frame].grabstate == FRAME_ERROR) { + int ret; + + if ((ret = ov511_new_frame(ov511, frame)) < 0) + return ret; + goto redo; + } + case FRAME_DONE: + ov511->frame[frame].grabstate = FRAME_UNUSED; + + /* Reset the hardware snapshot button */ + /* FIXME - Is this the best place for this? */ + if ((ov511->snap_enabled) && + (ov511->frame[frame].snapshot)) { + ov511->frame[frame].snapshot = 0; + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + } + break; + } /* end switch */ + + return 0; + } + case VIDIOCGFBUF: + { + struct video_buffer vb; + + memset(&vb, 0, sizeof(vb)); + vb.base = NULL; /* frame buffer not supported, not used */ + + if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) + return -EFAULT; + + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static long ov511_read(struct video_device *dev, char *buf, unsigned long count, int noblock) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + int i; + int frmx = -1; + volatile struct ov511_frame *frame; + + PDEBUG(4, "%ld bytes, noblock=%d", count, noblock); + + if (!dev || !buf) + return -EFAULT; + + if (!ov511->dev) + return -EIO; + + /* See if a frame is completed, then use it. */ + if (ov511->frame[0].grabstate >= FRAME_DONE) /* _DONE or _ERROR */ + frmx = 0; + else if (ov511->frame[1].grabstate >= FRAME_DONE)/* _DONE or _ERROR */ + frmx = 1; + + /* If nonblocking we return immediately */ + if (noblock && (frmx == -1)) + return -EAGAIN; + + /* If no FRAME_DONE, look for a FRAME_GRABBING state. */ + /* See if a frame is in process (grabbing), then use it. */ + if (frmx == -1) { + if (ov511->frame[0].grabstate == FRAME_GRABBING) + frmx = 0; + else if (ov511->frame[1].grabstate == FRAME_GRABBING) + frmx = 1; + } + + /* If no frame is active, start one. */ + if (frmx == -1) + ov511_new_frame(ov511, frmx = 0); + + frame = &ov511->frame[frmx]; + +restart: + if (!ov511->dev) + return -EIO; + + /* Wait while we're grabbing the image */ + PDEBUG(4, "Waiting image grabbing"); + while (frame->grabstate == FRAME_GRABBING) { + interruptible_sleep_on(&ov511->frame[frmx].wq); + if (signal_pending(current)) + return -EINTR; + } + PDEBUG(4, "Got image, frame->grabstate = %d", frame->grabstate); + + if (frame->grabstate == FRAME_ERROR) { + frame->bytes_read = 0; + err("** ick! ** Errored frame %d", ov511->curframe); + if (ov511_new_frame(ov511, frmx)) + err("ov511_read: ov511_new_frame error"); + goto restart; + } + + + /* Repeat until we get a snapshot frame */ + if (ov511->snap_enabled) + PDEBUG (4, "Waiting snapshot frame"); + if (ov511->snap_enabled && !frame->snapshot) { + frame->bytes_read = 0; + if (ov511_new_frame(ov511, frmx)) + err("ov511_new_frame error"); + goto restart; + } + + /* Clear the snapshot */ + if (ov511->snap_enabled && frame->snapshot) { + frame->snapshot = 0; + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x03); + ov511_reg_write(ov511->dev, OV511_REG_SYSTEM_SNAPSHOT, 0x01); + } + + PDEBUG(4, "frmx=%d, bytes_read=%ld, scanlength=%ld", frmx, + frame->bytes_read, frame->scanlength); + + /* copy bytes to user space; we allow for partials reads */ +// if ((count + frame->bytes_read) > frame->scanlength) +// count = frame->scanlength - frame->bytes_read; + + /* FIXME - count hardwired to be one frame... */ + count = frame->width * frame->height * (frame->depth >> 3); + + PDEBUG(4, "Copy to user space: %ld bytes", count); + if ((i = copy_to_user(buf, frame->data + frame->bytes_read, count))) { + PDEBUG(4, "Copy failed! %d bytes not copied", i); + return -EFAULT; + } + + frame->bytes_read += count; + PDEBUG(4, "{copy} count used=%ld, new bytes_read=%ld", + count, frame->bytes_read); + + if (frame->bytes_read >= frame->scanlength) { /* All data has been read */ + frame->bytes_read = 0; + + /* Mark it as available to be used again. */ + ov511->frame[frmx].grabstate = FRAME_UNUSED; + if (ov511_new_frame(ov511, !frmx)) + err("ov511_new_frame returned error"); + } + + PDEBUG(4, "read finished, returning %ld (sweet)", count); + + return count; +} + +static int ov511_mmap(struct video_device *dev, const char *adr, + unsigned long size) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *)dev; + unsigned long start = (unsigned long)adr; + unsigned long page, pos; + + if (ov511->dev == NULL) + return -EIO; + + PDEBUG(4, "mmap: %ld (%lX) bytes", size, size); + + if (size > (((2 * MAX_DATA_SIZE) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long)ov511->fbuf; + while (size > 0) { + page = kvirt_to_pa(pos); + if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +static struct video_device ov511_template = { + name: "OV511 USB Camera", + type: VID_TYPE_CAPTURE, + hardware: VID_HARDWARE_OV511, + open: ov511_open, + close: ov511_close, + read: ov511_read, + write: ov511_write, + ioctl: ov511_ioctl, + mmap: ov511_mmap, + initialize: ov511_init_done, +}; + +/**************************************************************************** + * + * OV511/OV7610 configuration + * + ***************************************************************************/ + +static int ov76xx_configure(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i, success; + int rc; + + /* Lawrence Glaister reports: + * + * Register 0x0f in the 7610 has the following effects: + * + * 0x85 (AEC method 1): Best overall, good contrast range + * 0x45 (AEC method 2): Very overexposed + * 0xa5 (spec sheet default): Ok, but the black level is + * shifted resulting in loss of contrast + * 0x05 (old driver setting): very overexposed, too much + * contrast + */ + static struct ov511_regvals aRegvalsNorm7610[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x06, 0x00 }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x38, 0x81 }, + { OV511_I2C_BUS, 0x28, 0x24 }, /* 0c */ + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x20, 0x1c }, + { OV511_I2C_BUS, 0x23, 0x2a }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xc2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, /* 91 */ + { OV511_I2C_BUS, 0x2a, 0x04 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + static struct ov511_regvals aRegvalsNorm7620[] = { + { OV511_I2C_BUS, 0x10, 0xff }, + { OV511_I2C_BUS, 0x16, 0x06 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x2b, 0xac }, + { OV511_I2C_BUS, 0x12, 0x00 }, + { OV511_I2C_BUS, 0x28, 0x24 }, + { OV511_I2C_BUS, 0x05, 0x00 }, + { OV511_I2C_BUS, 0x0f, 0x85 }, /* lg's setting */ + { OV511_I2C_BUS, 0x15, 0x01 }, + { OV511_I2C_BUS, 0x23, 0x00 }, + { OV511_I2C_BUS, 0x24, 0x10 }, + { OV511_I2C_BUS, 0x25, 0x8a }, + { OV511_I2C_BUS, 0x27, 0xe2 }, + { OV511_I2C_BUS, 0x29, 0x03 }, + { OV511_I2C_BUS, 0x2a, 0x00 }, + { OV511_I2C_BUS, 0x2c, 0xfe }, + { OV511_I2C_BUS, 0x30, 0x71 }, + { OV511_I2C_BUS, 0x31, 0x60 }, + { OV511_I2C_BUS, 0x32, 0x26 }, + { OV511_I2C_BUS, 0x33, 0x20 }, + { OV511_I2C_BUS, 0x34, 0x48 }, + { OV511_I2C_BUS, 0x12, 0x24 }, + { OV511_I2C_BUS, 0x11, 0x01 }, + { OV511_I2C_BUS, 0x0c, 0x24 }, + { OV511_I2C_BUS, 0x0d, 0x24 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + PDEBUG (4, "starting configuration"); + + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_WRITE, + OV7610_I2C_WRITE_ID) < 0) + return -1; + + if(ov511_reg_write(dev, OV511_REG_I2C_SLAVE_ID_READ, + OV7610_I2C_READ_ID) < 0) + return -1; + + if (ov511_reset(dev, OV511_RESET_NOREGS) < 0) + return -1; + + /* Reset the 76xx */ + if (ov511_i2c_write(dev, 0x12, 0x80) < 0) return -1; + + for (i = 0, success = 0; i < i2c_detect_tries && !success; i++) { + if ((ov511_i2c_read(dev, OV7610_REG_ID_HIGH) == 0x7F) && + (ov511_i2c_read(dev, OV7610_REG_ID_LOW) == 0xA2)) + success = 1; + + /* Dummy read to sync I2C */ + if (ov511_i2c_read(dev, 0x00) < 0) return -1; + /* Wait for it to initialize */ + schedule_timeout (1 + 150 * HZ / 1000); + } + + if (success) { + PDEBUG(1, "I2C synced in %d attempt(s)", i); + } else { + err("Failed to read sensor ID. You might not have an OV76xx,"); + err("or it may be not responding. Report this to"); + err("mmcclelland@delphi.com"); + return -1; + } + + /* Detect sensor if user didn't use override param */ + if (sensor == 0) { + rc = ov511_i2c_read(dev, OV7610_REG_COM_I); + + if (rc < 0) { + err("Error detecting sensor type"); + return -1; + } else if((rc & 3) == 3) { + info("Sensor is an OV7610"); + ov511->sensor = SEN_OV7610; + } else if((rc & 3) == 1) { + info("Sensor is an OV7620AE"); + ov511->sensor = SEN_OV7620AE; + } else if((rc & 3) == 0) { + info("Sensor is an OV7620"); + ov511->sensor = SEN_OV7620; + } else { + err("Unknown image sensor version: %d", rc & 3); + return -1; + } + } else { /* sensor != 0; user overrode detection */ + ov511->sensor = sensor; + info("Sensor set to type %d", ov511->sensor); + } + + if (ov511->sensor == SEN_OV7620) { + PDEBUG(4, "Writing 7620 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm7620)) + return -1; + } else { + PDEBUG(4, "Writing 7610 registers"); + if (ov511_write_regvals(dev, aRegvalsNorm7610)) + return -1; + } + + if (aperture < 0) { /* go with the default */ + if (ov511_i2c_write(dev, 0x26, 0xa2) < 0) return -1; + } else if (aperture <= 0xf) { /* user overrode default */ + if (ov511_i2c_write(dev, 0x26, (aperture << 4) + 2) < 0) + return -1; + } else { + err("Invalid setting for aperture; legal value: 0 - 15"); + return -1; + } + + if (autoadjust) { + if (ov511_i2c_write(dev, 0x13, 0x01) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x91:0x93) < 0) return -1; + } else { + if (ov511_i2c_write(dev, 0x13, 0x00) < 0) return -1; + if (ov511_i2c_write(dev, 0x2d, + ov511->sensor==SEN_OV7620?0x81:0x83) < 0) return -1; + ov511_i2c_write(dev, 0x28, ov511_i2c_read(dev, 0x28) | 8); + } + + return 0; +} + + +static int ov511_configure(struct usb_ov511 *ov511) +{ + struct usb_device *dev = ov511->dev; + int i; + + static struct ov511_regvals aRegvalsInit[] = { + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x7f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3f }, + { OV511_REG_BUS, OV511_REG_SYSTEM_INIT, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_RESET, 0x3d }, + { OV511_DONE_BUS, 0x0, 0x00}, + }; + + static struct ov511_regvals aRegvalsNorm511[] = { + { OV511_REG_BUS, OV511_REG_DRAM_ENABLE_FLOW_CONTROL, 0x01 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x02 }, + { OV511_REG_BUS, OV511_REG_SYSTEM_SNAPSHOT, 0x00 }, + { OV511_REG_BUS, OV511_REG_FIFO_BITMASK, 0x1f }, /* 0f */ + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_Y, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_HORIZ_UV, 0x3f }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_PREDICTION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_HORIZ_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_Y, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_QUANTIZATION_VERT_UV, 0x01 }, + { OV511_REG_BUS, OV511_OMNICE_ENABLE, 0x06 }, + { OV511_REG_BUS, OV511_OMNICE_LUT_ENABLE, 0x03 }, + { OV511_DONE_BUS, 0x0, 0x00 }, + }; + + memcpy(&ov511->vdev, &ov511_template, sizeof(ov511_template)); + + init_waitqueue_head(&ov511->frame[0].wq); + init_waitqueue_head(&ov511->frame[1].wq); + init_waitqueue_head(&ov511->wq); + + if (video_register_device(&ov511->vdev, VFL_TYPE_GRABBER) == -1) { + err("video_register_device failed"); + return -EBUSY; + } + + if (ov511_write_regvals(dev, aRegvalsInit)) goto error; + if (ov511_write_regvals(dev, aRegvalsNorm511)) goto error; + + ov511_set_packet_size(ov511, 0); + + ov511->snap_enabled = snapshot; + + /* Set default sizes in case IOCTL (VIDIOCMCAPTURE) is not used + * (using read() instead). */ + for (i = 0; i < OV511_NUMFRAMES; i++) { + ov511->frame[i].width = DEFAULT_WIDTH; + ov511->frame[i].height = DEFAULT_HEIGHT; + ov511->frame[i].depth = 24; + ov511->frame[i].bytes_read = 0; + ov511->frame[i].segment = 0; + ov511->frame[i].format = VIDEO_PALETTE_RGB24; + ov511->frame[i].segsize = GET_SEGSIZE(ov511->frame[i].format); + } + + /* Initialize to DEFAULT_WIDTH, DEFAULT_HEIGHT, YUV4:2:0 */ + + if(ov76xx_configure(ov511) < 0) { + err("failed to configure OV76xx"); + goto error; + } + + if (ov511_mode_init_regs(ov511, DEFAULT_WIDTH, DEFAULT_HEIGHT, + VIDEO_PALETTE_RGB24, 0) < 0) + goto error; + + return 0; + +error: + video_unregister_device(&ov511->vdev); + usb_driver_release_interface(&ov511_driver, + &dev->actconfig->interface[ov511->iface]); + + return -EBUSY; +} + + +/**************************************************************************** + * + * USB routines + * + ***************************************************************************/ + +static void* ov511_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_ov511 *ov511; + int i; + + PDEBUG(1, "probing for device..."); + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + + /* Is it an OV511/OV511+? */ + if (dev->descriptor.idVendor != 0x05a9) + return NULL; + if (dev->descriptor.idProduct != 0x0511 + && dev->descriptor.idProduct != 0xA511) + return NULL; + + /* Checking vendor/product should be enough, but what the hell */ + if (interface->bInterfaceClass != 0xFF) + return NULL; + if (interface->bInterfaceSubClass != 0x00) + return NULL; + + /* Since code below may sleep, we use this as a lock */ + MOD_INC_USE_COUNT; + + if ((ov511 = kmalloc(sizeof(*ov511), GFP_KERNEL)) == NULL) { + err("couldn't kmalloc ov511 struct"); + goto error; + } + + memset(ov511, 0, sizeof(*ov511)); + + ov511->dev = dev; + ov511->iface = interface->bInterfaceNumber; + + switch (dev->descriptor.idProduct) { + case 0x0511: + info("USB OV511 camera found"); + ov511->bridge = BRG_OV511; + break; + case 0xA511: + info("USB OV511+ camera found"); + ov511->bridge = BRG_OV511PLUS; + break; + } + + ov511->customid = ov511_reg_read(dev, OV511_REG_SYSTEM_CUSTOM_ID); + if (ov511->customid < 0) { + err("Unable to read camera bridge registers"); + goto error; + } + + ov511->desc = -1; + PDEBUG (4, "CustomID = %d", ov511->customid); + for (i = 0; clist[i].id >= 0; i++) { + if (ov511->customid == clist[i].id) { + info("camera: %s", clist[i].description); + ov511->desc = i; + break; + } + } + + /* Lifeview USB Life TV not supported */ + if (clist[i].id == 38) { + err("This device is not supported yet."); + goto error; + } + + if (clist[i].id == -1) { + err("Camera type (%d) not recognized", ov511->customid); + err("Please contact mmcclelland@delphi.com to request"); + err("support for your camera."); + } + + /* Workaround for some applications that want data in RGB + * instead of BGR */ + if (force_rgb) + info("data format set to RGB"); + + if (!ov511_configure(ov511)) { + ov511->user = 0; + init_MUTEX(&ov511->lock); /* to 1 == available */ + } else { + err("Failed to configure camera"); + goto error; + } + + MOD_DEC_USE_COUNT; + return ov511; + +error: + if (ov511) { + kfree(ov511); + ov511 = NULL; + } + + MOD_DEC_USE_COUNT; + return NULL; +} + + +static void ov511_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_ov511 *ov511 = (struct usb_ov511 *) ptr; + + MOD_INC_USE_COUNT; + + /* We don't want people trying to open up the device */ + if (!ov511->user) + video_unregister_device(&ov511->vdev); + + usb_driver_release_interface(&ov511_driver, + &ov511->dev->actconfig->interface[ov511->iface]); + + ov511->dev = NULL; + ov511->frame[0].grabstate = FRAME_ERROR; + ov511->frame[1].grabstate = FRAME_ERROR; + ov511->curframe = -1; + + /* This will cause the process to request another frame */ + if (waitqueue_active(&ov511->frame[0].wq)) + wake_up_interruptible(&ov511->frame[0].wq); + if (waitqueue_active(&ov511->frame[1].wq)) + wake_up_interruptible(&ov511->frame[1].wq); + if (waitqueue_active(&ov511->wq)) + wake_up_interruptible(&ov511->wq); + + ov511->streaming = 0; + + /* Unschedule all of the iso td's */ + if (ov511->sbuf[1].urb) { + ov511->sbuf[1].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[1].urb); + usb_free_urb(ov511->sbuf[1].urb); + ov511->sbuf[1].urb = NULL; + } + if (ov511->sbuf[0].urb) { + ov511->sbuf[0].urb->next = NULL; + usb_unlink_urb(ov511->sbuf[0].urb); + usb_free_urb(ov511->sbuf[0].urb); + ov511->sbuf[0].urb = NULL; + } + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + destroy_proc_ov511_cam(ov511); +#endif + + /* Free the memory */ + if (ov511 && !ov511->user) { + kfree(ov511); + ov511 = NULL; + } + + MOD_DEC_USE_COUNT; +} + +static struct usb_driver ov511_driver = { + "ov511", + ov511_probe, + ov511_disconnect, + { NULL, NULL } +}; + + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init usb_ov511_init(void) +{ +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_ov511_create(); +#endif + + if (usb_register(&ov511_driver) < 0) + return -1; + + info("ov511 driver version %s registered", version); + + return 0; +} + +static void __exit usb_ov511_exit(void) +{ + usb_deregister(&ov511_driver); + info("ov511 driver deregistered"); + +#if defined(CONFIG_PROC_FS) && defined(CONFIG_VIDEO_PROC_FS) + proc_ov511_destroy(); +#endif +} + +module_init(usb_ov511_init); +module_exit(usb_ov511_exit); diff --git a/drivers/usb/ov511.h b/drivers/usb/ov511.h new file mode 100644 index 000000000000..92827afb3321 --- /dev/null +++ b/drivers/usb/ov511.h @@ -0,0 +1,354 @@ + +#ifndef __LINUX_OV511_H +#define __LINUX_OV511_H + +#include +#include +#include + +#define OV511_DEBUG /* Turn on debug messages */ + +#ifdef OV511_DEBUG +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +#else +# define PDEBUG(level, fmt, args...) do {} while(0) +#endif + +/* Camera interface register numbers */ +#define OV511_REG_CAMERA_DELAY_MODE 0x10 +#define OV511_REG_CAMERA_EDGE_MODE 0x11 +#define OV511_REG_CAMERA_CLAMPED_PIXEL_NUM 0x12 +#define OV511_REG_CAMERA_CLAMPED_LINE_NUM 0x13 +#define OV511_REG_CAMERA_PIXEL_DIVISOR 0x14 +#define OV511_REG_CAMERA_LINE_DIVISOR 0x15 +#define OV511_REG_CAMERA_DATA_INPUT_SELECT 0x16 +#define OV511_REG_CAMERA_RESERVED_LINE_MODE 0x17 +#define OV511_REG_CAMERA_BITMASK 0x18 + +/* Snapshot mode camera interface register numbers */ +#define OV511_REG_SNAP_CAPTURED_FRAME 0x19 +#define OV511_REG_SNAP_CLAMPED_PIXEL_NUM 0x1A +#define OV511_REG_SNAP_CLAMPED_LINE_NUM 0x1B +#define OV511_REG_SNAP_PIXEL_DIVISOR 0x1C +#define OV511_REG_SNAP_LINE_DIVISOR 0x1D +#define OV511_REG_SNAP_DATA_INPUT_SELECT 0x1E +#define OV511_REG_SNAP_BITMASK 0x1F + +/* DRAM register numbers */ +#define OV511_REG_DRAM_ENABLE_FLOW_CONTROL 0x20 +#define OV511_REG_DRAM_READ_CYCLE_PREDICT 0x21 +#define OV511_REG_DRAM_MANUAL_READ_CYCLE 0x22 +#define OV511_REG_DRAM_REFRESH_COUNTER 0x23 + +/* ISO FIFO register numbers */ +#define OV511_REG_FIFO_PACKET_SIZE 0x30 +#define OV511_REG_FIFO_BITMASK 0x31 + +/* PIO register numbers */ +#define OV511_REG_PIO_BITMASK 0x38 +#define OV511_REG_PIO_DATA_PORT 0x39 +#define OV511_REG_PIO_BIST 0x3E + +/* I2C register numbers */ +#define OV511_REG_I2C_CONTROL 0x40 +#define OV511_REG_I2C_SLAVE_ID_WRITE 0x41 +#define OV511_REG_I2C_SUB_ADDRESS_3_BYTE 0x42 +#define OV511_REG_I2C_SUB_ADDRESS_2_BYTE 0x43 +#define OV511_REG_I2C_SLAVE_ID_READ 0x44 +#define OV511_REG_I2C_DATA_PORT 0x45 +#define OV511_REG_I2C_CLOCK_PRESCALER 0x46 +#define OV511_REG_I2C_TIME_OUT_COUNTER 0x47 + +/* I2C snapshot register numbers */ +#define OV511_REG_I2C_SNAP_SUB_ADDRESS 0x48 +#define OV511_REG_I2C_SNAP_DATA_PORT 0x49 + +/* System control register numbers */ +#define OV511_REG_SYSTEM_RESET 0x50 +#define OV511_RESET_UDC 0x01 +#define OV511_RESET_I2C 0x02 +#define OV511_RESET_FIFO 0x04 +#define OV511_RESET_OMNICE 0x08 +#define OV511_RESET_DRAM_INTF 0x10 +#define OV511_RESET_CAMERA_INTF 0x20 +#define OV511_RESET_OV511 0x40 +#define OV511_RESET_NOREGS 0x3F /* All but OV511 & regs */ +#define OV511_RESET_ALL 0x7F +#define OV511_REG_SYSTEM_CLOCK_DIVISOR 0x51 +#define OV511_REG_SYSTEM_SNAPSHOT 0x52 +#define OV511_REG_SYSTEM_INIT 0x53 +#define OV511_REG_SYSTEM_PWR_CLK 0x54 /* OV511+ only */ +#define OV511_REG_SYSTEM_LED_CTL 0x55 /* OV511+ only */ +#define OV511_REG_SYSTEM_USER_DEFINED 0x5E +#define OV511_REG_SYSTEM_CUSTOM_ID 0x5F + +/* OmniCE register numbers */ +#define OV511_OMNICE_PREDICTION_HORIZ_Y 0x70 +#define OV511_OMNICE_PREDICTION_HORIZ_UV 0x71 +#define OV511_OMNICE_PREDICTION_VERT_Y 0x72 +#define OV511_OMNICE_PREDICTION_VERT_UV 0x73 +#define OV511_OMNICE_QUANTIZATION_HORIZ_Y 0x74 +#define OV511_OMNICE_QUANTIZATION_HORIZ_UV 0x75 +#define OV511_OMNICE_QUANTIZATION_VERT_Y 0x76 +#define OV511_OMNICE_QUANTIZATION_VERT_UV 0x77 +#define OV511_OMNICE_ENABLE 0x78 +#define OV511_OMNICE_LUT_ENABLE 0x79 +#define OV511_OMNICE_Y_LUT_BEGIN 0x80 +#define OV511_OMNICE_Y_LUT_END 0x9F +#define OV511_OMNICE_UV_LUT_BEGIN 0xA0 +#define OV511_OMNICE_UV_LUT_END 0xBF + +/* Alternate numbers for various max packet sizes (OV511 only) */ +#define OV511_ALT_SIZE_992 0 +#define OV511_ALT_SIZE_993 1 +#define OV511_ALT_SIZE_768 2 +#define OV511_ALT_SIZE_769 3 +#define OV511_ALT_SIZE_512 4 +#define OV511_ALT_SIZE_513 5 +#define OV511_ALT_SIZE_257 6 +#define OV511_ALT_SIZE_0 7 + +/* Alternate numbers for various max packet sizes (OV511+ only) */ +#define OV511PLUS_ALT_SIZE_0 0 +#define OV511PLUS_ALT_SIZE_33 1 +#define OV511PLUS_ALT_SIZE_129 2 +#define OV511PLUS_ALT_SIZE_257 3 +#define OV511PLUS_ALT_SIZE_385 4 +#define OV511PLUS_ALT_SIZE_513 5 +#define OV511PLUS_ALT_SIZE_769 6 +#define OV511PLUS_ALT_SIZE_961 7 + +/* OV7610 registers */ +#define OV7610_REG_GAIN 0x00 /* gain setting (5:0) */ +#define OV7610_REG_BLUE 0x01 /* blue channel balance */ +#define OV7610_REG_RED 0x02 /* red channel balance */ +#define OV7610_REG_SAT 0x03 /* saturation */ + /* 04 reserved */ +#define OV7610_REG_CNT 0x05 /* Y contrast */ +#define OV7610_REG_BRT 0x06 /* Y brightness */ + /* 08-0b reserved */ +#define OV7610_REG_BLUE_BIAS 0x0C /* blue channel bias (5:0) */ +#define OV7610_REG_RED_BIAS 0x0D /* read channel bias (5:0) */ +#define OV7610_REG_GAMMA_COEFF 0x0E /* gamma settings */ +#define OV7610_REG_WB_RANGE 0x0F /* AEC/ALC/S-AWB settings */ +#define OV7610_REG_EXP 0x10 /* manual exposure setting */ +#define OV7610_REG_CLOCK 0x11 /* polarity/clock prescaler */ +#define OV7610_REG_COM_A 0x12 /* misc common regs */ +#define OV7610_REG_COM_B 0x13 /* misc common regs */ +#define OV7610_REG_COM_C 0x14 /* misc common regs */ +#define OV7610_REG_COM_D 0x15 /* misc common regs */ +#define OV7610_REG_FIELD_DIVIDE 0x16 /* field interval/mode settings */ +#define OV7610_REG_HWIN_START 0x17 /* horizontal window start */ +#define OV7610_REG_HWIN_END 0x18 /* horizontal window end */ +#define OV7610_REG_VWIN_START 0x19 /* vertical window start */ +#define OV7610_REG_VWIN_END 0x1A /* vertical window end */ +#define OV7610_REG_PIXEL_SHIFT 0x1B /* pixel shift */ +#define OV7610_REG_ID_HIGH 0x1C /* manufacturer ID MSB */ +#define OV7610_REG_ID_LOW 0x1D /* manufacturer ID LSB */ + /* 0e-0f reserved */ +#define OV7610_REG_COM_E 0x20 /* misc common regs */ +#define OV7610_REG_YOFFSET 0x21 /* Y channel offset */ +#define OV7610_REG_UOFFSET 0x22 /* U channel offset */ + /* 23 reserved */ +#define OV7610_REG_ECW 0x24 /* Exposure white level for AEC */ +#define OV7610_REG_ECB 0x25 /* Exposure black level for AEC */ +#define OV7610_REG_COM_F 0x26 /* misc settings */ +#define OV7610_REG_COM_G 0x27 /* misc settings */ +#define OV7610_REG_COM_H 0x28 /* misc settings */ +#define OV7610_REG_COM_I 0x29 /* misc settings */ +#define OV7610_REG_FRAMERATE_H 0x2A /* frame rate MSB + misc */ +#define OV7610_REG_FRAMERATE_L 0x2B /* frame rate LSB */ +#define OV7610_REG_ALC 0x2C /* Auto Level Control settings */ +#define OV7610_REG_COM_J 0x2D /* misc settings */ +#define OV7610_REG_VOFFSET 0x2E /* V channel offset adjustment */ +#define OV7610_REG_ARRAY_BIAS 0x2F /* Array bias -- don't change */ + /* 30-32 reserved */ +#define OV7610_REG_YGAMMA 0x33 /* misc gamma settings (7:6) */ +#define OV7610_REG_BIAS_ADJUST 0x34 /* misc bias settings */ +#define OV7610_REG_COM_L 0x35 /* misc settings */ + /* 36-37 reserved */ +#define OV7610_REG_COM_K 0x38 /* misc registers */ + + +#define STREAM_BUF_SIZE (PAGE_SIZE * 4) + +#define SCRATCH_BUF_SIZE 384 + +#define FRAMES_PER_DESC 10 /* FIXME - What should this be? */ +#define FRAME_SIZE_PER_DESC 993 /* FIXME - Deprecated */ +#define MAX_FRAME_SIZE_PER_DESC 993 /* For statically allocated stuff */ + +#define OV511_ENDPOINT_ADDRESS 1 /* Isoc endpoint number */ + +// CAMERA SPECIFIC +// FIXME - these can vary between specific models +#define OV7610_I2C_WRITE_ID 0x42 +#define OV7610_I2C_READ_ID 0x43 +#define OV511_I2C_CLOCK_PRESCALER 0x03 + +/* Prototypes */ +int usb_ov511_reg_read(struct usb_device *dev, unsigned char reg); +int usb_ov511_reg_write(struct usb_device *dev, + unsigned char reg, + unsigned char value); + +/* Bridge types */ +enum { + BRG_OV511, + BRG_OV511PLUS, +}; + +/* Sensor types */ +enum { + SEN_UNKNOWN, + SEN_OV7610, + SEN_OV7620, + SEN_OV7620AE, +}; + +enum { + STATE_SCANNING, /* Scanning for start */ + STATE_HEADER, /* Parsing header */ + STATE_LINES, /* Parsing lines */ +}; + +struct usb_device; + +struct ov511_sbuf { + char *data; + urb_t *urb; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +struct ov511_regvals { + enum { + OV511_DONE_BUS, + OV511_REG_BUS, + OV511_I2C_BUS, + } bus; + unsigned char reg; + unsigned char val; +}; + +struct ov511_frame { + char *data; /* Frame buffer */ + + int depth; /* Bytes per pixel */ + int width; /* Width application is expecting */ + int height; /* Height */ + + int hdrwidth; /* Width the frame actually is */ + int hdrheight; /* Height */ + + int sub_flag; /* Sub-capture mode for this frame? */ + int format; /* Format for this frame */ + int segsize; /* How big is each segment from the camera? */ + + volatile int grabstate; /* State of grabbing */ + int scanstate; /* State of scanning */ + + int curline; /* Line of frame we're working on */ + int curpix; + int segment; /* Segment from the incoming data */ + + long scanlength; /* uncompressed, raw data length of frame */ + long bytes_read; /* amount of scanlength that has been read from *data */ + + wait_queue_head_t wq; /* Processes waiting */ + + int snapshot; /* True if frame was a snapshot */ +}; + +#define OV511_NUMFRAMES 2 +#define OV511_NUMSBUF 2 + +struct usb_ov511 { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + int customid; + int desc; + unsigned char iface; + + int brightness; + int colour; + int contrast; + int hue; + int whiteness; + + struct semaphore lock; + int user; /* user count for exclusive use */ + + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + + int compress; /* Should the next frame be compressed? */ + + char *fbuf; /* Videodev buffer area */ + + int sub_flag; /* Pix Array subcapture on flag */ + int subx; /* Pix Array subcapture x offset */ + int suby; /* Pix Array subcapture y offset */ + int subw; /* Pix Array subcapture width */ + int subh; /* Pix Array subcapture height */ + + int curframe; /* Current receiving sbuf */ + struct ov511_frame frame[OV511_NUMFRAMES]; + + int cursbuf; /* Current receiving sbuf */ + struct ov511_sbuf sbuf[OV511_NUMSBUF]; + + /* Scratch space from the Isochronous pipe */ + unsigned char scratch[SCRATCH_BUF_SIZE]; + int scratchlen; + + wait_queue_head_t wq; /* Processes waiting */ + + int snap_enabled; /* Snapshot mode enabled */ + + int bridge; /* Type of bridge (OV511 or OV511+) */ + int sensor; /* Type of image sensor chip */ + + int packet_size; /* Frame size per isoc desc */ + + /* proc interface */ + struct semaphore param_lock; /* params lock for this camera */ + struct proc_dir_entry *proc_entry; /* /proc/ov511/videoX */ +}; + +struct cam_list { + int id; + char *description; +}; + +struct palette_list { + int num; + char *name; +}; + +struct mode_list { + int width; + int height; + int color; /* 0=grayscale, 1=color */ + u8 pxcnt; /* pixel counter */ + u8 lncnt; /* line counter */ + u8 pxdv; /* pixel divisor */ + u8 lndv; /* line divisor */ + u8 m420; + u8 common_A; + u8 common_C; + u8 common_L; +}; + +#endif + diff --git a/drivers/usb/pegasus.c b/drivers/usb/pegasus.c new file mode 100644 index 000000000000..6cd04557cb02 --- /dev/null +++ b/drivers/usb/pegasus.c @@ -0,0 +1,564 @@ +/* +** Pegasus: USB 10/100Mbps/HomePNA (1Mbps) Controller +** +** Copyright (R) 1999,2000 Petko Manolov - Petkan (petkan@spct.net) +** +** Distribute under GPL version 2 or later. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + + +static const char *version = __FILE__ ": v0.3.14 2000/06/09 (C) 1999-2000 Petko Manolov (petkan@spct.net)\n"; + + +#define PEGASUS_MTU 1500 +#define PEGASUS_MAX_MTU 1536 +#define SROM_WRITE 0x01 +#define SROM_READ 0x02 +#define PEGASUS_TX_TIMEOUT (HZ*5) +#define PEGASUS_RESET 1 +#define ALIGN(x) x __attribute__((aligned(L1_CACHE_BYTES))) + + +struct pegasus { + struct usb_device *usb; + struct net_device *net; + struct net_device_stats stats; + int flags; + spinlock_t pegasus_lock; + struct urb rx_urb, tx_urb, intr_urb; + unsigned char ALIGN(rx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(tx_buff[PEGASUS_MAX_MTU]); + unsigned char ALIGN(intr_buff[8]); +}; + +struct usb_eth_dev { + char *name; + __u16 vendor; + __u16 device; + void *private; +}; + + +static int loopback = 0; +static int multicast_filter_limit = 32; + + +MODULE_AUTHOR("Petko Manolov "); +MODULE_DESCRIPTION("ADMtek AN986 Pegasus USB Ethernet driver"); +MODULE_PARM(loopback, "i"); + + +static struct usb_eth_dev usb_dev_id[] = { + {"Billionton USB-100", 0x08dd, 0x0986, NULL}, + {"Corega FEter USB-TX", 0x7aa, 0x0004, NULL}, + {"MELCO/BUFFALO LUA-TX", 0x0411, 0x0001, NULL}, + {"D-Link DSB-650TX", 0x2001, 0x4001, NULL}, + {"D-Link DSB-650TX", 0x2001, 0x4002, NULL}, + {"D-Link DSB-650TX(PNA)", 0x2001, 0x4003, NULL}, + {"D-Link DU-10", 0x07b8, 0xabc1, NULL}, + {"D-Link DU-E100", 0x07b8, 0x4002, NULL}, + {"Linksys USB100TX", 0x066b, 0x2203, NULL}, + {"Linksys USB100TX", 0x066b, 0x2204, NULL}, + {"Linksys USB Ethernet Adapter", 0x066b, 0x2206, NULL}, + {"SMC 202 USB Ethernet", 0x0707, 0x0200, NULL}, + {"ADMtek AN986 \"Pegasus\" USB Ethernet (eval board)", 0x07a6, 0x0986, NULL}, + {"Accton USB 10/100 Ethernet Adapter", 0x083a, 0x1046, NULL}, + {"IO DATA USB ET/TX", 0x04bb, 0x0904, NULL}, + {"LANEED USB Ethernet LD-USB/TX", 0x056e, 0x4002, NULL}, + {NULL, 0, 0, NULL} +}; + + +#define pegasus_get_registers(dev, indx, size, data)\ + usb_control_msg(dev, usb_rcvctrlpipe(dev,0), 0xf0, 0xc0, 0, indx, data, size, HZ); +#define pegasus_set_registers(dev, indx, size, data)\ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, 0, indx, data, size, HZ); +#define pegasus_set_register(dev, indx, value) \ + { __u8 __data = value; \ + usb_control_msg(dev, usb_sndctrlpipe(dev,0), 0xf1, 0x40, __data, indx, &__data, 1, HZ);} + + +static int pegasus_read_phy_word(struct usb_device *dev, __u8 index, __u16 *regdata) +{ + int i; + __u8 data[4] = { 1, 0, 0, 0x40 + index }; + + pegasus_set_registers(dev, 0x25, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x26, 3, data); + if (data[2] & 0x80) { + *regdata = *(__u16 *)(data); + return 0; + } + udelay(100); + } + + warn("read_phy_word() failed"); + return 1; +} + + +static int pegasus_write_phy_word(struct usb_device *dev, __u8 index, __u16 regdata) +{ + int i; + __u8 data[4] = { 1, regdata, regdata >> 8, 0x20 + index }; + + pegasus_set_registers(dev, 0x25, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x28, 1, data); + if (data[0] & 0x80) + return 0; + udelay(100); + } + + warn("write_phy_word() failed"); + return 1; +} + + +static int pegasus_rw_srom_word(struct usb_device *dev, __u8 index, __u16 *retdata, __u8 direction) +{ + int i; + __u8 data[4] = { index, 0, 0, direction }; + + pegasus_set_registers(dev, 0x20, 4, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 0x23, 1, data); + if (data[0] & 4) { + pegasus_get_registers(dev, 0x21, 2, data); + *retdata = *(__u16 *)data; + return 0; + } + } + + warn("pegasus_rw_srom_word() failed"); + return 1; +} + + +static int pegasus_get_node_id(struct usb_device *dev, __u8 *id) +{ + int i; + for (i = 0; i < 3; i++) + if (pegasus_rw_srom_word(dev,i,(__u16 *)&id[i * 2],SROM_READ)) + return 1; + return 0; +} + + +static int pegasus_reset_mac(struct usb_device *dev) +{ + __u8 data = 0x8; + int i; + + pegasus_set_register(dev, 1, data); + for (i = 0; i < 100; i++) { + pegasus_get_registers(dev, 1, 1, &data); + if (~data & 0x08) { + if (loopback & 1) + return 0; + if (loopback & 2) + pegasus_write_phy_word(dev, 0, 0x4000); + pegasus_set_register(dev, 0x7e, 0x24); + pegasus_set_register(dev, 0x7e, 0x27); + return 0; + } + } + + return 1; +} + + +static int pegasus_start_net(struct net_device *dev, struct usb_device *usb) +{ + __u16 partmedia, temp; + __u8 node_id[6]; + __u8 data[4]; + + if (pegasus_get_node_id(usb, node_id)) + return 1; + + pegasus_set_registers(usb, 0x10, 6, node_id); + memcpy(dev->dev_addr, node_id, 6); + if (pegasus_read_phy_word(usb, 1, &temp)) + return 2; + + if ((~temp & 4) && !loopback) { + warn("%s: link NOT established (0x%x), check the cable.", + dev->name, temp); + /* return 3; FIXME */ + } + + if (pegasus_read_phy_word(usb, 5, &partmedia)) + return 4; + + if ((partmedia & 0x1f) != 1) { + warn("party FAIL %x", partmedia); + /* return 5; FIXME */ + } + + data[0] = 0xc9; + data[1] = (partmedia & 0x100) ? 0x30 : ((partmedia & 0x80) ? 0x10 : 0); + data[2] = (loopback & 1) ? 0x09 : 0x01; + + pegasus_set_registers(usb, 0, 3, data); + + return 0; +} + + +static void pegasus_read_bulk(struct urb *urb) +{ + struct pegasus *pegasus = urb->context; + struct net_device *net = pegasus->net; + int count = urb->actual_length, res; + int rx_status = *(int *)(pegasus->rx_buff + count - 4); + struct sk_buff *skb; + __u16 pkt_len; + + if (urb->status) { + dbg("%s: RX status %d", net->name, urb->status); + goto goon; + } + + if (!count) + goto goon; +#if 0 + if (rx_status & 0x00010000) + goto goon; +#endif + if (rx_status & 0x000e0000) { + + dbg("%s: error receiving packet %x", net->name, rx_status & 0xe0000); + pegasus->stats.rx_errors++; + if(rx_status & 0x060000) pegasus->stats.rx_length_errors++; + if(rx_status & 0x080000) pegasus->stats.rx_crc_errors++; + if(rx_status & 0x100000) pegasus->stats.rx_frame_errors++; + + goto goon; + } + + pkt_len = (rx_status & 0xfff) - 8; + + if(!(skb = dev_alloc_skb(pkt_len+2))) + goto goon; + + skb->dev = net; + skb_reserve(skb, 2); + eth_copy_and_sum(skb, pegasus->rx_buff, pkt_len, 0); + skb_put(skb, pkt_len); + + skb->protocol = eth_type_trans(skb, net); + netif_rx(skb); + pegasus->stats.rx_packets++; + pegasus->stats.rx_bytes += pkt_len; + +goon: + if ((res = usb_submit_urb(&pegasus->rx_urb))) + warn("(prb)failed rx_urb %d", res); +} + + +static void pegasus_irq(urb_t *urb) +{ + __u8 *d = urb->transfer_buffer; + + if ( d[0] ) + dbg("txst0=0x%2x", d[0]); +} + + +static void pegasus_write_bulk(struct urb *urb) +{ + struct pegasus *pegasus = urb->context; + + spin_lock(&pegasus->pegasus_lock); + + if (urb->status) + info("%s: TX status %d", pegasus->net->name, urb->status); + netif_wake_queue(pegasus->net); + + spin_unlock(&pegasus->pegasus_lock); +} + +#if 0 +static void pegasus_tx_timeout(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + warn("%s: Tx timed out. Reseting...", net->name); + usb_unlink_urb(&pegasus->tx_urb); + pegasus->stats.tx_errors++; + net->trans_start = jiffies; + pegasus->flags |= PEGASUS_RESET; + + netif_wake_queue(net); +} +#endif + + +static int pegasus_start_xmit(struct sk_buff *skb, struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + int count = ((skb->len+2) & 0x3f) ? skb->len+2 : skb->len+3; + int res; + + if (net->tbusy) { + int tickssofar = jiffies - net->trans_start; + if (tickssofar < PEGASUS_TX_TIMEOUT) + return 1; + warn("%s: Tx timed out. Reseting...", net->name); + pegasus->stats.tx_errors++; + net->trans_start = jiffies; + net->tbusy = 0; + netif_wake_queue(net); + dev_kfree_skb(skb); + return 0; + } + spin_lock(&pegasus->pegasus_lock); + + netif_stop_queue(net); + + ((__u16 *)pegasus->tx_buff)[0] = skb->len; + memcpy(pegasus->tx_buff+2, skb->data, skb->len); + (&pegasus->tx_urb)->transfer_buffer_length = count; + + if ((res = usb_submit_urb(&pegasus->tx_urb))) { + warn("failed tx_urb %d", res); + pegasus->stats.tx_errors++; + netif_start_queue(net); + } else { + pegasus->stats.tx_packets++; + pegasus->stats.tx_bytes += skb->len; + net->trans_start = jiffies; + } + + dev_kfree_skb(skb); + + spin_unlock(&pegasus->pegasus_lock); + + return 0; +} + + +static struct net_device_stats *pegasus_netdev_stats(struct net_device *dev) +{ + return &((struct pegasus *)dev->priv)->stats; +} + + +static int pegasus_open(struct net_device *net) +{ + struct pegasus *pegasus = (struct pegasus *)net->priv; + int res; + + if ((res = pegasus_start_net(net, pegasus->usb))) { + err("can't start_net() - %d", res); + return -EIO; + } + + if ((res = usb_submit_urb(&pegasus->rx_urb))) + warn("(open)failed rx_urb %d", res); + + if ((res = usb_submit_urb(&pegasus->intr_urb))) + warn("(open)failed intr_urb %d", res); + + netif_start_queue(net); + + MOD_INC_USE_COUNT; + + return 0; +} + + +static int pegasus_close(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + netif_stop_queue(net); + + if ( pegasus->rx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->rx_urb); + if ( pegasus->tx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->tx_urb); + if ( pegasus->intr_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->intr_urb); + + MOD_DEC_USE_COUNT; + + return 0; +} + + +static int pegasus_ioctl(struct net_device *net, struct ifreq *rq, int cmd) +{ + __u16 *data = (__u16 *)&rq->ifr_data; + struct pegasus *pegasus = net->priv; + + switch(cmd) { + case SIOCDEVPRIVATE: + data[0] = 1; + case SIOCDEVPRIVATE+1: + pegasus_read_phy_word(pegasus->usb, data[1] & 0x1f, &data[3]); + return 0; + case SIOCDEVPRIVATE+2: + if (!capable(CAP_NET_ADMIN)) + return -EPERM; + pegasus_write_phy_word(pegasus->usb, data[1] & 0x1f, data[2]); + return 0; + default: + return -EOPNOTSUPP; + } +} + + +static void pegasus_set_rx_mode(struct net_device *net) +{ + struct pegasus *pegasus = net->priv; + + netif_stop_queue(net); + + if (net->flags & IFF_PROMISC) { + info("%s: Promiscuous mode enabled", net->name); +/* pegasus_set_register(pegasus->usb, 2, 0x04); FIXME */ + } else if ((net->mc_count > multicast_filter_limit) || + (net->flags & IFF_ALLMULTI)) { + pegasus_set_register(pegasus->usb, 0, 0xfa); + pegasus_set_register(pegasus->usb, 2, 0); + info("%s set allmulti", net->name); + } else { + info("%s: set Rx mode", net->name); + } + + netif_wake_queue(net); +} + + +static int check_device_ids( __u16 vendor, __u16 product ) +{ + int i=0; + + while ( usb_dev_id[i].name ) { + if ( (usb_dev_id[i].vendor == vendor) && + (usb_dev_id[i].device == product) ) + return i; + i++; + } + return -1; +} + + +static void * pegasus_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct net_device *net; + struct pegasus *pegasus; + int dev_indx; + + if ( (dev_indx = check_device_ids(dev->descriptor.idVendor, dev->descriptor.idProduct)) == -1 ) { + return NULL; + } + + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + err("usb_set_configuration() failed"); + return NULL; + } + + if(!(pegasus = kmalloc(sizeof(struct pegasus), GFP_KERNEL))) { + err("out of memory allocating device structure"); + return NULL; + } + memset(pegasus, 0, sizeof(struct pegasus)); + + if (pegasus_reset_mac(dev)) { + err("can't reset MAC"); + kfree(pegasus); + return NULL; + } + + net = init_etherdev(0, 0); + net->priv = pegasus; + net->open = pegasus_open; + net->stop = pegasus_close; +#if 0 + net->watchdog_timeo = PEGASUS_TX_TIMEOUT; + net->tx_timeout = pegasus_tx_timeout; +#endif + net->do_ioctl = pegasus_ioctl; + net->hard_start_xmit = pegasus_start_xmit; + net->set_multicast_list = pegasus_set_rx_mode; + net->get_stats = pegasus_netdev_stats; + net->mtu = PEGASUS_MTU; + + pegasus->usb = dev; + pegasus->net = net; + pegasus->pegasus_lock = SPIN_LOCK_UNLOCKED; + + FILL_BULK_URB(&pegasus->rx_urb, dev, usb_rcvbulkpipe(dev, 1), + pegasus->rx_buff, PEGASUS_MAX_MTU, pegasus_read_bulk, + pegasus); + FILL_BULK_URB(&pegasus->tx_urb, dev, usb_sndbulkpipe(dev, 2), + pegasus->tx_buff, PEGASUS_MAX_MTU, pegasus_write_bulk, + pegasus); + FILL_INT_URB(&pegasus->intr_urb, dev, usb_rcvintpipe(dev, 3), + pegasus->intr_buff, 8, pegasus_irq, pegasus, 500); + + + printk(KERN_INFO "%s: %s\n", net->name, usb_dev_id[dev_indx].name); + + return pegasus; +} + + +static void pegasus_disconnect(struct usb_device *dev, void *ptr) +{ + struct pegasus *pegasus = ptr; + + if (!pegasus) { + warn("unregistering non-existant device"); + return; + } + + if (pegasus->net->flags & IFF_UP) + dev_close(pegasus->net); + + unregister_netdev(pegasus->net); + + if ( pegasus->rx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->rx_urb); + if ( pegasus->tx_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->tx_urb); + if ( pegasus->intr_urb.status == -EINPROGRESS ) + usb_unlink_urb(&pegasus->intr_urb); + + kfree(pegasus); +} + + +static struct usb_driver pegasus_driver = { + name: "pegasus", + probe: pegasus_probe, + disconnect: pegasus_disconnect, +}; + +int __init pegasus_init(void) +{ + printk( version ); + return usb_register(&pegasus_driver); +} + +void __exit pegasus_exit(void) +{ + usb_deregister(&pegasus_driver); +} + +module_init(pegasus_init); +module_exit(pegasus_exit); diff --git a/drivers/usb/plusb.c b/drivers/usb/plusb.c new file mode 100644 index 000000000000..9165779cb117 --- /dev/null +++ b/drivers/usb/plusb.c @@ -0,0 +1,621 @@ +/*****************************************************************************/ + +/* + * plusb.c -- prolific pl-2302 driver. + * + * Copyright (C) 2000 Deti Fliegl (deti@fliegl.de) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * + * $Id: plusb.c,v 1.18 2000/02/14 10:38:58 fliegl Exp $ + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include + +#include "plusb.h" + +/* --------------------------------------------------------------------- */ + +#define NRPLUSB 4 + +/*-------------------------------------------------------------------*/ + +static plusb_t plusb[NRPLUSB]; + +/* --------------------------------------------------------------------- */ +static int plusb_add_buf_tail (plusb_t *s, struct list_head *dst, struct list_head *src) +{ + unsigned long flags; + struct list_head *tmp; + int ret = 0; + + spin_lock_irqsave (&s->lock, flags); + + if (list_empty (src)) { + // no elements in source buffer + ret = -1; + goto err; + } + tmp = src->next; + list_del (tmp); + list_add_tail (tmp, dst); + + err: spin_unlock_irqrestore (&s->lock, flags); + return ret; +} +/*-------------------------------------------------------------------*/ + +static int plusb_my_bulk(plusb_t *s, int pipe, void *data, int size, int *actual_length) +{ + int ret; + + dbg("plusb_my_bulk: len:%d",size); + + ret=usb_bulk_msg(s->usbdev, pipe, data, size, actual_length, 500); + if(ret<0) { + err("plusb: usb_bulk_msg failed(%d)",ret); + } + + if( ret == -EPIPE ) { + warn("CLEAR_FEATURE request to remove STALL condition."); + if(usb_clear_halt(s->usbdev, usb_pipeendpoint(pipe))) + err("request failed"); + } + + dbg("plusb_my_bulk: finished act: %d", *actual_length); + return ret; +} + +/* --------------------------------------------------------------------- */ + +static void plusb_bh(void *context) +{ + plusb_t *s=context; + struct net_device_stats *stats=&s->net_stats; + int ret=0; + int actual_length; + skb_list_t *skb_list; + struct sk_buff *skb; + + dbg("plusb_bh: i:%d",in_interrupt()); + + while(!list_empty(&s->tx_skb_list)) { + + if(!(s->status&_PLUSB_TXOK)) + break; + + skb_list = list_entry (s->tx_skb_list.next, skb_list_t, skb_list); + if(!skb_list->state) { + dbg("plusb_bh: not yet ready"); + schedule(); + continue; + } + + skb=skb_list->skb; + ret=plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), + skb->data, skb->len, &actual_length); + + if(ret || skb->len != actual_length ||!(skb->len%64)) { + plusb_my_bulk(s, usb_sndbulkpipe (s->usbdev, _PLUSB_BULKOUTPIPE), + NULL, 0, &actual_length); + } + + if(!ret) { + stats->tx_packets++; + stats->tx_bytes+=skb->len; + } + else { + stats->tx_errors++; + stats->tx_aborted_errors++; + } + + dbg("plusb_bh: dev_kfree_skb"); + + dev_kfree_skb(skb); + skb_list->state=0; + plusb_add_buf_tail (s, &s->free_skb_list, &s->tx_skb_list); + } + + dbg("plusb_bh: finished"); + s->in_bh=0; +} + +/* --------------------------------------------------------------------- */ + +static int plusb_net_xmit(struct sk_buff *skb, struct net_device *dev) +{ + plusb_t *s=dev->priv; + skb_list_t *skb_list; + int ret=NET_XMIT_SUCCESS; + + dbg("plusb_net_xmit: len:%d i:%d",skb->len,in_interrupt()); + + if(!s->connected || list_empty(&s->free_skb_list)) { + ret=NET_XMIT_CN; + goto lab; + } + + plusb_add_buf_tail (s, &s->tx_skb_list, &s->free_skb_list); + skb_list = list_entry (s->tx_skb_list.prev, skb_list_t, skb_list); + skb_list->skb=skb; + skb_list->state=1; + +lab: + if(s->in_bh) + return ret; + + dbg("plusb_net_xmit: queue_task"); + + s->in_bh=1; + queue_task(&s->bh, &tq_scheduler); + + dbg("plusb_net_xmit: finished"); + return ret; + +} + +/* --------------------------------------------------------------------- */ + +static void plusb_bulk_complete(urb_t *purb) +{ + plusb_t *s=purb->context; + + dbg("plusb_bulk_complete: status:%d length:%d",purb->status,purb->actual_length); + if(!s->connected) + return; + + if( !purb->status) { + struct sk_buff *skb; + unsigned char *dst; + int len=purb->transfer_buffer_length; + struct net_device_stats *stats=&s->net_stats; + + skb=dev_alloc_skb(len); + + if(!skb) { + err("plusb_bulk_complete: dev_alloc_skb(%d)=NULL, dropping frame",len); + stats->rx_dropped++; + return; + } + + dst=(char *)skb_put(skb, len); + memcpy( dst, purb->transfer_buffer, len); + + skb->dev=&s->net_dev; + skb->protocol=eth_type_trans(skb, skb->dev); + stats->rx_packets++; + stats->rx_bytes+=len; + netif_rx(skb); + } + else + purb->status=0; +} + +/* --------------------------------------------------------------------- */ + +static void plusb_int_complete(urb_t *purb) +{ + plusb_t *s=purb->context; + s->status=((unsigned char*)purb->transfer_buffer)[0]&255; +#if 0 + if((s->status&0x3f)!=0x20) { + warn("invalid device status %02X", s->status); + return; + } +#endif + if(!s->connected) + return; + + if(s->status&_PLUSB_RXD) { + int ret; + + if(s->bulkurb->status) { + err("plusb_int_complete: URB still in use"); + return; + } + + ret=usb_submit_urb(s->bulkurb); + if(ret && ret!=-EBUSY) { + err("plusb_int_complete: usb_submit_urb failed"); + } + } + + if(purb->status || s->status!=160) + dbg("status: %p %d buf: %02X", purb->dev, purb->status, s->status); +} + +/* --------------------------------------------------------------------- */ + +static void plusb_free_all(plusb_t *s) +{ + struct list_head *skb; + skb_list_t *skb_list; + + dbg("plusb_free_all"); + run_task_queue(&tq_immediate); + + if(s->inturb) { + dbg("unlink inturb"); + usb_unlink_urb(s->inturb); + } + + if(s->inturb && s->inturb->transfer_buffer) { + dbg("kfree inturb->transfer_buffer"); + kfree(s->inturb->transfer_buffer); + s->inturb->transfer_buffer=NULL; + } + + if(s->inturb) { + dbg("free_urb inturb"); + usb_free_urb(s->inturb); + s->inturb=NULL; + } + + if(s->bulkurb) { + dbg("unlink bulkurb"); + usb_unlink_urb(s->bulkurb); + } + + if(s->bulkurb && s->bulkurb->transfer_buffer) { + dbg("kfree bulkurb->transfer_buffer"); + kfree(s->bulkurb->transfer_buffer); + s->bulkurb->transfer_buffer=NULL; + } + if(s->bulkurb) { + dbg("free_urb bulkurb"); + usb_free_urb(s->bulkurb); + s->bulkurb=NULL; + } + + while(!list_empty(&s->free_skb_list)) { + skb=s->free_skb_list.next; + list_del(skb); + skb_list = list_entry (skb, skb_list_t, skb_list); + kfree(skb_list); + } + + while(!list_empty(&s->tx_skb_list)) { + skb=s->tx_skb_list.next; + list_del(skb); + skb_list = list_entry (skb, skb_list_t, skb_list); + kfree(skb_list); + } + dbg("plusb_free_all: finished"); +} + +/*-------------------------------------------------------------------*/ + +static int plusb_alloc(plusb_t *s) +{ + int i; + skb_list_t *skb; + + dbg("plusb_alloc"); + + for(i=0 ; i < _SKB_NUM ; i++) { + skb=kmalloc(sizeof(skb_list_t), GFP_KERNEL); + if(!skb) { + err("kmalloc for skb_list failed"); + goto reject; + } + memset(skb, 0, sizeof(skb_list_t)); + list_add(&skb->skb_list, &s->free_skb_list); + } + + dbg("inturb allocation:"); + s->inturb=usb_alloc_urb(0); + if(!s->inturb) { + err("alloc_urb failed"); + goto reject; + } + + dbg("bulkurb allocation:"); + s->bulkurb=usb_alloc_urb(0); + if(!s->bulkurb) { + err("alloc_urb failed"); + goto reject; + } + + dbg("bulkurb/inturb init:"); + s->inturb->dev=s->usbdev; + s->inturb->pipe=usb_rcvintpipe (s->usbdev, _PLUSB_INTPIPE); + s->inturb->transfer_buffer=kmalloc(64, GFP_KERNEL); + if(!s->inturb->transfer_buffer) { + err("kmalloc failed"); + goto reject; + } + + s->inturb->transfer_buffer_length=1; + s->inturb->complete=plusb_int_complete; + s->inturb->context=s; + s->inturb->interval=10; + + dbg("inturb submission:"); + if(usb_submit_urb(s->inturb)<0) { + err("usb_submit_urb failed"); + goto reject; + } + + dbg("bulkurb init:"); + s->bulkurb->dev=s->usbdev; + s->bulkurb->pipe=usb_rcvbulkpipe (s->usbdev, _PLUSB_BULKINPIPE); + s->bulkurb->transfer_buffer=kmalloc(_BULK_DATA_LEN, GFP_KERNEL); + if(!s->bulkurb->transfer_buffer) { + err("kmalloc failed"); + goto reject; + } + + s->bulkurb->transfer_buffer_length=_BULK_DATA_LEN; + s->bulkurb->complete=plusb_bulk_complete; + s->bulkurb->context=s; + + dbg("plusb_alloc: finished"); + + return 0; + + reject: + dbg("plusb_alloc: failed"); + + plusb_free_all(s); + return -ENOMEM; +} + +/*-------------------------------------------------------------------*/ + +static int plusb_net_open(struct net_device *dev) +{ + plusb_t *s=dev->priv; + + dbg("plusb_net_open"); + + if(plusb_alloc(s)) + return -ENOMEM; + + s->opened=1; + MOD_INC_USE_COUNT; + + dbg("plusb_net_open: success"); + + return 0; + +} + +/* --------------------------------------------------------------------- */ + +static int plusb_net_stop(struct net_device *dev) +{ + plusb_t *s=dev->priv; + + dbg("plusb_net_stop"); + + plusb_free_all(s); + s->opened=0; + MOD_DEC_USE_COUNT; + dbg("plusb_net_stop:finished"); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static struct net_device_stats *plusb_net_get_stats(struct net_device *dev) +{ + plusb_t *s=dev->priv; + + dbg("net_device_stats"); + + return &s->net_stats; +} + +/* --------------------------------------------------------------------- */ + +static plusb_t *plusb_find_struct (void) +{ + int u; + + for (u = 0; u < NRPLUSB; u++) { + plusb_t *s = &plusb[u]; + if (!s->connected) + return s; + } + return NULL; +} + +/* --------------------------------------------------------------------- */ + +static void plusb_disconnect (struct usb_device *usbdev, void *ptr) +{ + plusb_t *s = ptr; + + dbg("plusb_disconnect"); + s->connected = 0; + + plusb_free_all(s); + + if(!s->opened && s->net_dev.name) { + dbg("unregistering netdev: %s",s->net_dev.name); + unregister_netdev(&s->net_dev); + s->net_dev.name[0] = '\0'; + } + + dbg("plusb_disconnect: finished"); + MOD_DEC_USE_COUNT; +} + +/* --------------------------------------------------------------------- */ + +int plusb_net_init(struct net_device *dev) +{ + dbg("plusb_net_init"); + + dev->open=plusb_net_open; + dev->stop=plusb_net_stop; + dev->hard_start_xmit=plusb_net_xmit; + dev->get_stats = plusb_net_get_stats; + ether_setup(dev); + dev->tx_queue_len = 0; + dev->flags = IFF_POINTOPOINT|IFF_NOARP; + + + dbg("plusb_net_init: finished"); + return 0; +} + +/* --------------------------------------------------------------------- */ + +static void *plusb_probe (struct usb_device *usbdev, unsigned int ifnum) +{ + plusb_t *s; + + dbg("plusb: probe: vendor id 0x%x, device id 0x%x ifnum:%d", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct, ifnum); + + if (usbdev->descriptor.idVendor != 0x067b || usbdev->descriptor.idProduct > 0x1) + return NULL; + + /* We don't handle multiple configurations */ + if (usbdev->descriptor.bNumConfigurations != 1) + return NULL; + + s = plusb_find_struct (); + if (!s) + return NULL; + + s->usbdev = usbdev; + + if (usb_set_configuration (s->usbdev, usbdev->config[0].bConfigurationValue) < 0) { + err("set_configuration failed"); + return NULL; + } + + if (usb_set_interface (s->usbdev, 0, 0) < 0) { + err("set_interface failed"); + return NULL; + } + + if(!s->net_dev.name[0]) { + strcpy(s->net_dev.name, "plusb%d"); + s->net_dev.init=plusb_net_init; + s->net_dev.priv=s; + if(!register_netdev(&s->net_dev)) + info("registered: %s", s->net_dev.name); + else { + err("register_netdev failed"); + s->net_dev.name[0] = '\0'; + } + } + + s->connected = 1; + + if(s->opened) { + dbg("net device already allocated, restarting USB transfers"); + plusb_alloc(s); + } + + info("bound to interface: %d dev: %p", ifnum, usbdev); + MOD_INC_USE_COUNT; + return s; +} +/* --------------------------------------------------------------------- */ + +static struct usb_driver plusb_driver = +{ + name: "plusb", + probe: plusb_probe, + disconnect: plusb_disconnect, +}; + +/* --------------------------------------------------------------------- */ + +int __init plusb_init (void) +{ + unsigned u; + dbg("plusb_init"); + + /* initialize struct */ + for (u = 0; u < NRPLUSB; u++) { + plusb_t *s = &plusb[u]; + memset (s, 0, sizeof (plusb_t)); + s->bh.routine = (void (*)(void *))plusb_bh; + s->bh.data = s; + INIT_LIST_HEAD (&s->tx_skb_list); + INIT_LIST_HEAD (&s->free_skb_list); + spin_lock_init (&s->lock); + } + + /* register misc device */ + usb_register (&plusb_driver); + + dbg("plusb_init: driver registered"); + + return 0; +} + +/* --------------------------------------------------------------------- */ + +void __exit plusb_cleanup (void) +{ + unsigned u; + + dbg("plusb_cleanup"); + for (u = 0; u < NRPLUSB; u++) { + plusb_t *s = &plusb[u]; + if(s->net_dev.name[0]) { + dbg("unregistering netdev: %s",s->net_dev.name); + unregister_netdev(&s->net_dev); + } + } + usb_deregister (&plusb_driver); + dbg("plusb_cleanup: finished"); +} + +/* --------------------------------------------------------------------- */ + +#ifdef MODULE +MODULE_AUTHOR ("Deti Fliegl, deti@fliegl.de"); +MODULE_DESCRIPTION ("PL-2302 USB Interface Driver for Linux (c)2000"); + +/* --------------------------------------------------------------------- */ +int __init init_module (void) +{ + return plusb_init (); +} +/* --------------------------------------------------------------------- */ +void __exit cleanup_module (void) +{ + plusb_cleanup (); +} + +#endif + +/* --------------------------------------------------------------------- */ diff --git a/drivers/usb/plusb.h b/drivers/usb/plusb.h new file mode 100644 index 000000000000..a77ae0f01326 --- /dev/null +++ b/drivers/usb/plusb.h @@ -0,0 +1,48 @@ +#define _PLUSB_INTPIPE 0x1 +#define _PLUSB_BULKOUTPIPE 0x2 +#define _PLUSB_BULKINPIPE 0x3 + +#define _SKB_NUM 1000 +// 7 6 5 4 3 2 1 0 +// tx rx 1 0 +// 1110 0000 rxdata +// 1010 0000 idle +// 0010 0000 tx over +// 0110 tx over + rxd + +#define _PLUSB_RXD 0x40 +#define _PLUSB_TXOK 0x80 + +#ifdef __KERNEL__ +#define _BULK_DATA_LEN 16384 + +typedef struct +{ + struct list_head skb_list; + struct sk_buff *skb; + int state; +} skb_list_t,*pskb_list_t; + +typedef struct +{ + struct usb_device *usbdev; + + int status; + int connected; + int in_bh; + int opened; + + spinlock_t lock; + + urb_t *inturb; + urb_t *bulkurb; + + struct list_head tx_skb_list; + struct list_head free_skb_list; + struct tq_struct bh; + + struct net_device net_dev; + struct net_device_stats net_stats; +} plusb_t,*pplusb_t; + +#endif diff --git a/drivers/usb/printer.c b/drivers/usb/printer.c new file mode 100644 index 000000000000..906fa821fb50 --- /dev/null +++ b/drivers/usb/printer.c @@ -0,0 +1,526 @@ +/* + * printer.c Version 0.5 + * + * Copyright (c) 1999 Michael Gee + * Copyright (c) 1999 Pavel Machek + * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2000 Randy Dunlap + * + * USB Printer Device Class driver for USB printers and printer cables + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.1 - thorough cleaning, URBification, almost a rewrite + * v0.2 - some more cleanups + * v0.3 - cleaner again, waitqueue fixes + * v0.4 - fixes in unidirectional mode + * v0.5 - add DEVICE_ID string support + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +//#define DEBUG +#include + +#define USBLP_BUF_SIZE 8192 +#define DEVICE_ID_SIZE 1024 + +#define IOCNR_GET_DEVICE_ID 1 +#define LPIOC_GET_DEVICE_ID(len) _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len) /* get device_id string */ + +/* + * A DEVICE_ID string may include the printer's serial number. + * It should end with a semi-colon (';'). + * An example from an HP 970C DeskJet printer is (this is one long string, + * with the serial number changed): +MFG:HEWLETT-PACKARD;MDL:DESKJET 970C;CMD:MLC,PCL,PML;CLASS:PRINTER;DESCRIPTION:Hewlett-Packard DeskJet 970C;SERN:US970CSEPROF;VSTATUS:$HB0$NC0,ff,DN,IDLE,CUT,K1,C0,DP,NR,KP000,CP027;VP:0800,FL,B0;VJ: ; + */ + +/* + * USB Printer Requests + */ + +#define USBLP_REQ_GET_ID 0x00 +#define USBLP_REQ_GET_STATUS 0x01 +#define USBLP_REQ_RESET 0x02 + +#define USBLP_MINORS 16 +#define USBLP_MINOR_BASE 0 + +#define USBLP_WRITE_TIMEOUT (60*HZ) /* 60 seconds */ + +struct usblp { + struct usb_device *dev; /* USB device */ + struct urb readurb, writeurb; /* The urbs */ + wait_queue_head_t wait; /* Zzzzz ... */ + int readcount; /* Counter for reads */ + int ifnum; /* Interface number */ + int minor; /* minor number of device */ + unsigned char used; /* True if open */ + unsigned char bidir; /* interface is bidirectional */ + unsigned char *device_id_string; /* IEEE 1284 DEVICE ID string (ptr) */ + /* first 2 bytes are (big-endian) length */ +}; + +static struct usblp *usblp_table[USBLP_MINORS] = { NULL, /* ... */ }; + +/* + * Functions for usblp control messages. + */ + +static int usblp_ctrl_msg(struct usblp *usblp, int request, int dir, int recip, int value, void *buf, int len) +{ + int retval = usb_control_msg(usblp->dev, + dir ? usb_rcvctrlpipe(usblp->dev, 0) : usb_sndctrlpipe(usblp->dev, 0), + request, USB_TYPE_CLASS | dir | recip, value, usblp->ifnum, buf, len, HZ * 5); + dbg("usblp_control_msg: rq: 0x%02x dir: %d recip: %d value: %d len: %#x result: %d", + request, !!dir, recip, value, len, retval); + return retval < 0 ? retval : 0; +} + +#define usblp_read_status(usblp, status)\ + usblp_ctrl_msg(usblp, USBLP_REQ_GET_STATUS, USB_DIR_IN, USB_RECIP_INTERFACE, 0, status, 1) +#define usblp_get_id(usblp, config, id, maxlen)\ + usblp_ctrl_msg(usblp, USBLP_REQ_GET_ID, USB_DIR_IN, USB_RECIP_INTERFACE, config, id, maxlen) +#define usblp_reset(usblp)\ + usblp_ctrl_msg(usblp, USBLP_REQ_RESET, USB_DIR_OUT, USB_RECIP_OTHER, 0, NULL, 0) + +/* + * URB callback. + */ + +static void usblp_bulk(struct urb *urb) +{ + struct usblp *usblp = urb->context; + + if (!usblp || !usblp->dev || !usblp->used) + return; + + if (urb->status) + warn("nonzero read/write bulk status received: %d", urb->status); + + wake_up_interruptible(&usblp->wait); +} + +/* + * Get and print printer errors. + */ + +static int usblp_check_status(struct usblp *usblp) +{ + unsigned char status; + + if (usblp_read_status(usblp, &status)) { + err("failed reading usblp status"); + return -EIO; + } + + if (~status & LP_PERRORP) { + if (status & LP_POUTPA) { + info("usblp%d: out of paper", usblp->minor); + return -ENOSPC; + } + if (~status & LP_PSELECD) { + info("usblp%d: off-line", usblp->minor); + return -EIO; + } + if (~status & LP_PERRORP) { + info("usblp%d: on fire", usblp->minor); + return -EIO; + } + } + + return 0; +} + +/* + * File op functions. + */ + +static int usblp_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev) - USBLP_MINOR_BASE; + struct usblp *usblp; + int retval; + + if (minor < 0 || minor >= USBLP_MINORS) + return -ENODEV; + + usblp = usblp_table[minor]; + + if (!usblp || !usblp->dev) + return -ENODEV; + + if (usblp->used) + return -EBUSY; + + if ((retval = usblp_check_status(usblp))) { + return retval; + } + + usblp->used = 1; + file->private_data = usblp; + + usblp->writeurb.transfer_buffer_length = 0; + usblp->writeurb.status = 0; + + if (usblp->bidir) { + usblp->readcount = 0; + usb_submit_urb(&usblp->readurb); + } + + return 0; +} + +static int usblp_release(struct inode *inode, struct file *file) +{ + struct usblp *usblp = file->private_data; + + usblp->used = 0; + + if (usblp->dev) { + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); + usb_unlink_urb(&usblp->writeurb); + return 0; + } + + usblp_table[usblp->minor] = NULL; + kfree(usblp->device_id_string); + kfree(usblp); + + return 0; +} + +/* No kernel lock - fine */ +static unsigned int usblp_poll(struct file *file, struct poll_table_struct *wait) +{ + struct usblp *usblp = file->private_data; + poll_wait(file, &usblp->wait, wait); + return ((usblp->bidir || usblp->readurb.status == -EINPROGRESS) ? 0 : POLLIN | POLLRDNORM) + | (usblp->writeurb.status == -EINPROGRESS ? 0 : POLLOUT | POLLWRNORM); +} + +static int usblp_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int length; + struct usblp *usblp = file->private_data; + + if ((_IOC_TYPE(cmd) != 'P') || (_IOC_DIR(cmd) != _IOC_READ)) + return -EINVAL; + + switch (_IOC_NR(cmd)) { + case IOCNR_GET_DEVICE_ID: /* get the DEVICE_ID string */ + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ +#if 0 + dbg ("usblp_ioctl GET_DEVICE_ID: actlen=%d, user size=%d, string='%s'", + length, _IOC_SIZE(cmd), &usblp->device_id_string[2]); +#endif + if (length > _IOC_SIZE(cmd)) + length = _IOC_SIZE(cmd); /* truncate */ + if (copy_to_user ((unsigned char *)arg, usblp->device_id_string, (unsigned long) length)) + return -EFAULT; + break; + + default: + return -EINVAL; + } + + return 0; +} + +static ssize_t usblp_write(struct file *file, const char *buffer, size_t count, loff_t *ppos) +{ + struct usblp *usblp = file->private_data; + int retval, timeout, writecount = 0; + + while (writecount < count) { + + if (usblp->writeurb.status == -EINPROGRESS) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + timeout = USBLP_WRITE_TIMEOUT; + while (timeout && usblp->writeurb.status == -EINPROGRESS) { + + if (signal_pending(current)) + return writecount ? writecount : -EINTR; + + timeout = interruptible_sleep_on_timeout(&usblp->wait, timeout); + } + } + + if (usblp->writeurb.status == -EINPROGRESS) { + usb_unlink_urb(&usblp->writeurb); + err("usblp%d: timed out", usblp->minor); + return -EIO; + } + + if (!usblp->dev) + return -ENODEV; + + if (!usblp->writeurb.status) { + writecount += usblp->writeurb.transfer_buffer_length; + usblp->writeurb.transfer_buffer_length = 0; + } else { + if (!(retval = usblp_check_status(usblp))) { + err("usblp%d: error %d writing to printer (retval=%d)", + usblp->minor, usblp->writeurb.status, retval); + return -EIO; + } + + return retval; + } + + if (writecount == count) + continue; + + usblp->writeurb.transfer_buffer_length = (count - writecount) < USBLP_BUF_SIZE ? + (count - writecount) : USBLP_BUF_SIZE; + + if (copy_from_user(usblp->writeurb.transfer_buffer, buffer + writecount, + usblp->writeurb.transfer_buffer_length)) return -EFAULT; + + usb_submit_urb(&usblp->writeurb); + } + + return count; +} + +static ssize_t usblp_read(struct file *file, char *buffer, size_t count, loff_t *ppos) +{ + struct usblp *usblp = file->private_data; + + if (!usblp->bidir) + return -EINVAL; + + if (usblp->readurb.status == -EINPROGRESS) { + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + while (usblp->readurb.status == -EINPROGRESS) { + if (signal_pending(current)) + return -EINTR; + interruptible_sleep_on(&usblp->wait); + } + } + + if (!usblp->dev) + return -ENODEV; + + if (usblp->readurb.status) { + err("usblp%d: error %d reading from printer", + usblp->minor, usblp->readurb.status); + usb_submit_urb(&usblp->readurb); + return -EIO; + } + + count = count < usblp->readurb.actual_length - usblp->readcount ? + count : usblp->readurb.actual_length - usblp->readcount; + + if (copy_to_user(buffer, usblp->readurb.transfer_buffer + usblp->readcount, count)) + return -EFAULT; + + if ((usblp->readcount += count) == usblp->readurb.actual_length) + usb_submit_urb(&usblp->readurb); + + return count; +} + +static void *usblp_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *epread, *epwrite; + struct usblp *usblp; + int minor, i, alts = -1, bidir = 0; + int length, err; + char *buf; + + for (i = 0; i < dev->actconfig->interface[ifnum].num_altsetting; i++) { + + interface = &dev->actconfig->interface[ifnum].altsetting[i]; + + if (interface->bInterfaceClass != 7 || interface->bInterfaceSubClass != 1 || + (interface->bInterfaceProtocol != 1 && interface->bInterfaceProtocol != 2) || + (interface->bInterfaceProtocol > interface->bNumEndpoints)) + continue; + + if (alts == -1) + alts = i; + + if (!bidir && interface->bInterfaceProtocol == 2) { + bidir = 1; + alts = i; + } + } + + if (alts == -1) + return NULL; + + interface = &dev->actconfig->interface[ifnum].altsetting[alts]; + if (usb_set_interface(dev, ifnum, alts)) + err("can't set desired altsetting %d on interface %d", alts, ifnum); + + epwrite = interface->endpoint + 0; + epread = bidir ? interface->endpoint + 1 : NULL; + + if ((epwrite->bEndpointAddress & 0x80) == 0x80) { + if (interface->bNumEndpoints == 1) + return NULL; + epwrite = interface->endpoint + 1; + epread = bidir ? interface->endpoint + 0 : NULL; + } + + if ((epwrite->bEndpointAddress & 0x80) == 0x80) + return NULL; + + if (bidir && (epread->bEndpointAddress & 0x80) != 0x80) + return NULL; + + for (minor = 0; minor < USBLP_MINORS && usblp_table[minor]; minor++); + if (usblp_table[minor]) { + err("no more free usblp devices"); + return NULL; + } + + if (!(usblp = kmalloc(sizeof(struct usblp), GFP_KERNEL))) { + err("out of memory"); + return NULL; + } + memset(usblp, 0, sizeof(struct usblp)); + + usblp->dev = dev; + usblp->ifnum = ifnum; + usblp->minor = minor; + usblp->bidir = bidir; + + init_waitqueue_head(&usblp->wait); + + if (!(buf = kmalloc(USBLP_BUF_SIZE * (bidir ? 2 : 1), GFP_KERNEL))) { + err("out of memory"); + kfree(usblp); + return NULL; + } + + if (!(usblp->device_id_string = kmalloc(DEVICE_ID_SIZE, GFP_KERNEL))) { + err("out of memory"); + kfree(usblp); + kfree(buf); + return NULL; + } + + FILL_BULK_URB(&usblp->writeurb, dev, usb_sndbulkpipe(dev, epwrite->bEndpointAddress), + buf, 0, usblp_bulk, usblp); + + if (bidir) + FILL_BULK_URB(&usblp->readurb, dev, usb_rcvbulkpipe(dev, epread->bEndpointAddress), + buf + USBLP_BUF_SIZE, USBLP_BUF_SIZE, usblp_bulk, usblp); + + /* Get the device_id string if possible. FIXME: Could make this kmalloc(length). */ + err = usblp_get_id(usblp, 0, usblp->device_id_string, DEVICE_ID_SIZE - 1); + if (err >= 0) { + length = (usblp->device_id_string[0] << 8) + usblp->device_id_string[1]; /* big-endian */ + if (length < DEVICE_ID_SIZE) + usblp->device_id_string[length] = '\0'; + else + usblp->device_id_string[DEVICE_ID_SIZE - 1] = '\0'; + dbg ("usblp%d Device ID string [%d]=%s", + minor, length, &usblp->device_id_string[2]); + } + else { + err ("usblp%d: error = %d reading IEEE-1284 Device ID string", + minor, err); + usblp->device_id_string[0] = usblp->device_id_string[1] = '\0'; + } + +#ifdef DEBUG + usblp_check_status(usblp); +#endif + + info("usblp%d: USB %sdirectional printer dev %d if %d alt %d", + minor, bidir ? "Bi" : "Uni", dev->devnum, ifnum, alts); + + return usblp_table[minor] = usblp; +} + +static void usblp_disconnect(struct usb_device *dev, void *ptr) +{ + struct usblp *usblp = ptr; + + if (!usblp || !usblp->dev) { + err("disconnect on nonexisting interface"); + return; + } + + usblp->dev = NULL; + + usb_unlink_urb(&usblp->writeurb); + if (usblp->bidir) + usb_unlink_urb(&usblp->readurb); + + kfree(usblp->writeurb.transfer_buffer); + + if (usblp->used) return; + + kfree(usblp->device_id_string); + + usblp_table[usblp->minor] = NULL; + kfree(usblp); +} + +static struct file_operations usblp_fops = { + read: usblp_read, + write: usblp_write, + poll: usblp_poll, + ioctl: usblp_ioctl, + open: usblp_open, + release: usblp_release, +}; + +static struct usb_driver usblp_driver = { + name: "usblp", + probe: usblp_probe, + disconnect: usblp_disconnect, + fops: &usblp_fops, + minor: USBLP_MINOR_BASE +}; + +static int __init usblp_init(void) +{ + if (usb_register(&usblp_driver)) + return -1; + return 0; +} + +static void __exit usblp_exit(void) +{ + usb_deregister(&usblp_driver); +} + +module_init(usblp_init); +module_exit(usblp_exit); diff --git a/drivers/usb/restart b/drivers/usb/restart deleted file mode 100644 index 98ca05367e19..000000000000 --- a/drivers/usb/restart +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/sh - -ME=`basename $0` - -#UMOD=`lsmod | grep '^bp-mouse' | grep -v grep` -#if test "$UMOD"; then -# echo "$ME: removing bp-mouse.o" -# if ! rmmod bp-mouse; then -# echo "$ME: cannot remove bp-mouse.o" -# exit 1 -# fi -#fi - -UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'` -if test "$UPID"; then - echo "$ME: killing $UPID" - kill $UPID -fi - -UMOD=`lsmod | grep '^usb-ohci' | grep -v grep` -if test "$UMOD"; then - echo "$ME: removing usb-ohci.o" - sleep 1 - if ! rmmod usb-ohci; then - echo "$ME: cannot remove usb-ohci.o" - exit 1 - fi -fi - -dmesg -c > /dev/null - -echo "$ME: starting usb-ohci.o" -insmod -m usb-ohci.o > usb-ohci.map - -sleep 1 -UPID=`ps aux | grep ohci-control | grep -v grep | awk '{print $2}'` -if test "$UPID"; then echo "$ME: ohci-control is pid $UPID" ; fi - diff --git a/drivers/usb/rio500.c b/drivers/usb/rio500.c new file mode 100644 index 000000000000..011f853f6715 --- /dev/null +++ b/drivers/usb/rio500.c @@ -0,0 +1,447 @@ +/* -*- linux-c -*- */ + +/* + * Driver for USB Rio 500 + * + * Cesar Miquel (miquel@df.uba.ar) + * + * based on hp_scanner.c by David E. Nelson (dnelson@jump.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based upon mouse.c (Brad Keryan) and printer.c (Michael Gee). + * + * */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "rio500_usb.h" + +#define RIO_MINOR 64 + +/* stall/wait timeout for rio */ +#define NAK_TIMEOUT (HZ) + +#define IBUF_SIZE 0x1000 + +/* Size of the rio buffer */ +#define OBUF_SIZE 0x10000 + +struct rio_usb_data { + struct usb_device *rio_dev; /* init: probe_rio */ + unsigned int ifnum; /* Interface number of the USB device */ + int isopen; /* nz if open */ + int present; /* Device is present on the bus */ + char *obuf, *ibuf; /* transfer buffers */ + char bulk_in_ep, bulk_out_ep; /* Endpoint assignments */ + wait_queue_head_t wait_q; /* for timeouts */ +}; + +static struct rio_usb_data rio_instance; + +static int open_rio(struct inode *inode, struct file *file) +{ + struct rio_usb_data *rio = &rio_instance; + + if (rio->isopen || !rio->present) { + return -EBUSY; + } + rio->isopen = 1; + + init_waitqueue_head(&rio->wait_q); + + MOD_INC_USE_COUNT; + + info("Rio opened."); + + return 0; +} + +static int close_rio(struct inode *inode, struct file *file) +{ + struct rio_usb_data *rio = &rio_instance; + + rio->isopen = 0; + + MOD_DEC_USE_COUNT; + + info("Rio closed."); + return 0; +} + +static int +ioctl_rio(struct inode *inode, struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct RioCommand rio_cmd; + struct rio_usb_data *rio = &rio_instance; + void *data; + unsigned char *buffer; + int result, requesttype; + int retries; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + switch (cmd) { + case RIO_RECV_COMMAND: + data = (void *) arg; + if (data == NULL) + break; + copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand), + -EFAULT); + if (rio_cmd.length > PAGE_SIZE) + return -EINVAL; + buffer = (unsigned char *) __get_free_page(GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length, + -EFAULT); + + requesttype = rio_cmd.requesttype | USB_DIR_IN | + USB_TYPE_VENDOR | USB_RECIP_DEVICE; + dbg + ("sending command:reqtype=%0x req=%0x value=%0x index=%0x len=%0x", + requesttype, rio_cmd.request, rio_cmd.value, + rio_cmd.index, rio_cmd.length); + /* Send rio control message */ + retries = 3; + while (retries) { + result = usb_control_msg(rio->rio_dev, + usb_rcvctrlpipe(rio-> rio_dev, 0), + rio_cmd.request, + requesttype, + rio_cmd.value, + rio_cmd.index, buffer, + rio_cmd.length, + rio_cmd.timeout); + if (result == -ETIMEDOUT) + retries--; + else if (result < 0) { + err("Error executing ioctrl. code = %d", + le32_to_cpu(result)); + retries = 0; + } else { + dbg("Executed ioctl. Result = %d (data=%04x)", + le32_to_cpu(result), + le32_to_cpu(*((long *) buffer))); + copy_to_user_ret(rio_cmd.buffer, buffer, + rio_cmd.length, -EFAULT); + retries = 0; + } + + /* rio_cmd.buffer contains a raw stream of single byte + data which has been returned from rio. Data is + interpreted at application level. For data that + will be cast to data types longer than 1 byte, data + will be little_endian and will potentially need to + be swapped at the app level */ + + } + free_page((unsigned long) buffer); + break; + + case RIO_SEND_COMMAND: + data = (void *) arg; + if (data == NULL) + break; + copy_from_user_ret(&rio_cmd, data, sizeof(struct RioCommand), + -EFAULT); + if (rio_cmd.length > PAGE_SIZE) + return -EINVAL; + buffer = (unsigned char *) __get_free_page(GFP_KERNEL); + if (buffer == NULL) + return -ENOMEM; + copy_from_user_ret(buffer, rio_cmd.buffer, rio_cmd.length, + -EFAULT); + + requesttype = rio_cmd.requesttype | USB_DIR_OUT | + USB_TYPE_VENDOR | USB_RECIP_DEVICE; + dbg("sending command: reqtype=%0x req=%0x value=%0x index=%0x len=%0x", + requesttype, rio_cmd.request, rio_cmd.value, + rio_cmd.index, rio_cmd.length); + /* Send rio control message */ + retries = 3; + while (retries) { + result = usb_control_msg(rio->rio_dev, + usb_sndctrlpipe(rio-> rio_dev, 0), + rio_cmd.request, + requesttype, + rio_cmd.value, + rio_cmd.index, buffer, + rio_cmd.length, + rio_cmd.timeout); + if (result == -ETIMEDOUT) + retries--; + else if (result < 0) { + err("Error executing ioctrl. code = %d", + le32_to_cpu(result)); + retries = 0; + } else { + dbg("Executed ioctl. Result = %d", + le32_to_cpu(result)); + retries = 0; + + } + + } + free_page((unsigned long) buffer); + break; + + default: + return -ENOIOCTLCMD; + break; + } + + return 0; +} + +static ssize_t +write_rio(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + struct rio_usb_data *rio = &rio_instance; + + unsigned long copy_size; + unsigned long bytes_written = 0; + unsigned int partial; + + int result = 0; + int maxretry; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + do { + unsigned long thistime; + char *obuf = rio->obuf; + + thistime = copy_size = + (count >= OBUF_SIZE) ? OBUF_SIZE : count; + if (copy_from_user(rio->obuf, buffer, copy_size)) + return -EFAULT; + maxretry = 5; + while (thistime) { + if (!rio->rio_dev) + return -ENODEV; + if (signal_pending(current)) { + return bytes_written ? bytes_written : -EINTR; + } + + result = usb_bulk_msg(rio->rio_dev, + usb_sndbulkpipe(rio->rio_dev, 2), + obuf, thistime, &partial, 5 * HZ); + + dbg("write stats: result:%d thistime:%lu partial:%u", + result, thistime, partial); + + if (result == USB_ST_TIMEOUT) { /* NAK - so hold for a while */ + if (!maxretry--) { + return -ETIME; + } + interruptible_sleep_on_timeout(&rio-> wait_q, NAK_TIMEOUT); + continue; + } else if (!result & partial) { + obuf += partial; + thistime -= partial; + } else + break; + }; + if (result) { + err("Write Whoops - %x", result); + return -EIO; + } + bytes_written += copy_size; + count -= copy_size; + buffer += copy_size; + } while (count > 0); + + return bytes_written ? bytes_written : -EIO; +} + +static ssize_t +read_rio(struct file *file, char *buffer, size_t count, loff_t * ppos) +{ + struct rio_usb_data *rio = &rio_instance; + ssize_t read_count; + unsigned int partial; + int this_read; + int result; + int maxretry = 10; + char *ibuf = rio->ibuf; + + /* Sanity check to make sure rio is connected, powered, etc */ + if ( rio == NULL || + rio->present == 0 || + rio->rio_dev == NULL ) + return -1; + + read_count = 0; + + while (count > 0) { + if (signal_pending(current)) { + return read_count ? read_count : -EINTR; + } + if (!rio->rio_dev) + return -ENODEV; + this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; + + result = usb_bulk_msg(rio->rio_dev, + usb_rcvbulkpipe(rio->rio_dev, 1), + ibuf, this_read, &partial, + (int) (HZ * 8)); + + dbg(KERN_DEBUG "read stats: result:%d this_read:%u partial:%u", + result, this_read, partial); + + if (partial) { + count = this_read = partial; + } else if (result == USB_ST_TIMEOUT || result == 15) { /* FIXME: 15 ??? */ + if (!maxretry--) { + err("read_rio: maxretry timeout"); + return -ETIME; + } + interruptible_sleep_on_timeout(&rio->wait_q, + NAK_TIMEOUT); + continue; + } else if (result != USB_ST_DATAUNDERRUN) { + err("Read Whoops - result:%u partial:%u this_read:%u", + result, partial, this_read); + return -EIO; + } else { + return (0); + } + + if (this_read) { + if (copy_to_user(buffer, ibuf, this_read)) + return -EFAULT; + count -= this_read; + read_count += this_read; + buffer += this_read; + } + } + return read_count; +} + +static void *probe_rio(struct usb_device *dev, unsigned int ifnum) +{ + struct rio_usb_data *rio = &rio_instance; + + if (dev->descriptor.idVendor != 0x841) { + return NULL; + } + + if (dev->descriptor.idProduct != 0x1 /* RIO 500 */ ) { + warn(KERN_INFO "Rio player model not supported/tested."); + return NULL; + } + + info("USB Rio found at address %d", dev->devnum); + + rio->present = 1; + rio->rio_dev = dev; + + if (!(rio->obuf = (char *) kmalloc(OBUF_SIZE, GFP_KERNEL))) { + err("probe_rio: Not enough memory for the output buffer"); + return NULL; + } + dbg("probe_rio: obuf address:%p", rio->obuf); + + if (!(rio->ibuf = (char *) kmalloc(IBUF_SIZE, GFP_KERNEL))) { + err("probe_rio: Not enough memory for the input buffer"); + kfree(rio->obuf); + return NULL; + } + dbg("probe_rio: ibuf address:%p", rio->ibuf); + + return rio; +} + +static void disconnect_rio(struct usb_device *dev, void *ptr) +{ + struct rio_usb_data *rio = (struct rio_usb_data *) ptr; + + if (rio->isopen) { + rio->isopen = 0; + /* better let it finish - the release will do whats needed */ + rio->rio_dev = NULL; + return; + } + kfree(rio->ibuf); + kfree(rio->obuf); + + info("USB Rio disconnected."); + + rio->present = 0; +} + +static struct +file_operations usb_rio_fops = { + read: read_rio, + write: write_rio, + ioctl: ioctl_rio, + open: open_rio, + release: close_rio, +}; + +static struct +usb_driver rio_driver = { + "rio500", + probe_rio, + disconnect_rio, + {NULL, NULL}, + &usb_rio_fops, + RIO_MINOR +}; + +int usb_rio_init(void) +{ + if (usb_register(&rio_driver) < 0) + return -1; + + info("USB Rio support registered."); + return 0; +} + + +void usb_rio_cleanup(void) +{ + struct rio_usb_data *rio = &rio_instance; + + rio->present = 0; + usb_deregister(&rio_driver); + + +} + +module_init(usb_rio_init); +module_exit(usb_rio_cleanup); + diff --git a/drivers/usb/rio500_usb.h b/drivers/usb/rio500_usb.h new file mode 100644 index 000000000000..9fc712f3d292 --- /dev/null +++ b/drivers/usb/rio500_usb.h @@ -0,0 +1,37 @@ +/* ---------------------------------------------------------------------- + + Copyright (C) 2000 Cesar Miquel (miquel@df.uba.ar) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + + ---------------------------------------------------------------------- */ + + + +#define RIO_SEND_COMMAND 0x1 +#define RIO_RECV_COMMAND 0x2 + +#define RIO_DIR_OUT 0x0 +#define RIO_DIR_IN 0x1 + +struct RioCommand { + short length; + int request; + int requesttype; + int value; + int index; + void *buffer; + int timeout; +}; diff --git a/drivers/usb/scanner.c b/drivers/usb/scanner.c new file mode 100644 index 000000000000..d326e58922af --- /dev/null +++ b/drivers/usb/scanner.c @@ -0,0 +1,876 @@ +/* -*- linux-c -*- */ + +/* + * Driver for USB Scanners (linux-2.4.0test1-ac7) + * + * Copyright (C) 1999, 2000 David E. Nelson + * + * Portions may be copyright Brad Keryan and Michael Gee. + * + * David E. Nelson (dnelson@jump.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Originally based upon mouse.c (Brad Keryan) and printer.c (Michael Gee). + * + * History + * + * 0.1 8/31/1999 + * + * Developed/tested using linux-2.3.15 with minor ohci.c changes to + * support short packes during bulk xfer mode. Some testing was + * done with ohci-hcd but the performace was low. Very limited + * testing was performed with uhci but I was unable to get it to + * work. Initial relase to the linux-usb development effort. + * + * + * 0.2 10/16/1999 + * + * - Device can't be opened unless a scanner is plugged into the USB. + * - Finally settled on a reasonable value for the I/O buffer's. + * - Cleaned up write_scanner() + * - Disabled read/write stats + * - A little more code cleanup + * + * + * 0.3 10/18/1999 + * + * - Device registration changed to reflect new device + * allocation/registration for linux-2.3.22+. + * - Adopted David Brownell's technique for + * assigning bulk endpoints. + * - Removed unnessesary #include's + * - Scanner model now reported via syslog INFO after being detected + * *and* configured. + * - Added user specified vendor:product USB ID's which can be passed + * as module parameters. + * + * + * 0.3.1 + * + * - Applied patches for linux-2.3.25. + * - Error number reporting changed to reflect negative return codes. + * + * + * 0.3.2 + * + * - Applied patches for linux-2.3.26 to scanner_init(). + * - Debug read/write stats now report values as signed decimal. + * + * + * 0.3.3 + * + * - Updated the bulk_msg() calls to usb usb_bulk_msg(). + * - Added a small delay in the write_scanner() method to aid in + * avoiding NULL data reads on HP scanners. We'll see how this works. + * - Return values from usb_bulk_msg() now ignore positive values for + * use with the ohci driver. + * - Added conditional debugging instead of commenting/uncommenting + * all over the place. + * - kfree()'d the pointer after using usb_string() as documented in + * linux-usb-api.txt. + * - Added usb_set_configuration(). It got lost in version 0.3 -- ack! + * - Added the HP 5200C USB Vendor/Product ID's. + * + * + * 0.3.4 1/23/2000 + * + * - Added Greg K-H's patch for better handling of + * Product/Vendor detection. + * - The driver now autoconfigures its endpoints including interrupt + * endpoints if one is detected. The concept was originally based + * upon David Brownell's method. + * - Added some Seiko/Epson ID's. Thanks to Karl Heinz + * Kremer . + * - Added some preliminary ioctl() calls for the PV8630 which is used + * by the HP4200. The ioctl()'s still have to be registered. Thanks + * to Adrian Perez Jorge . + * - Moved/migrated stuff to scanner.h + * - Removed the usb_set_configuration() since this is handled by + * the usb_new_device() routine in usb.c. + * - Added the HP 3300C. Thanks to Bruce Tenison. + * - Changed user specified vendor/product id so that root hub doesn't + * get falsely attached to. Thanks to Greg K-H. + * - Added some Mustek ID's. Thanks to Gernot Hoyler + * . + * - Modified the usb_string() reporting. See kfree() comment above. + * - Added Umax Astra 2000U. Thanks to Doug Alcorn . + * - Updated the printk()'s to use the info/warn/dbg macros. + * - Updated usb_bulk_msg() argument types to fix gcc warnings. + * + * + * 0.4 2/4/2000 + * + * - Removed usb_string() from probe_scanner since the core now does a + * good job of reporting what was connnected. + * - Finally, simultaneous multiple device attachment! + * - Fixed some potential memory freeing issues should memory allocation + * fail in probe_scanner(); + * - Some fixes to disconnect_scanner(). + * - Added interrupt endpoint support. + * - Added Agfa SnapScan Touch. Thanks to Jan Van den Bergh + * . + * - Added Umax 1220U ID's. Thanks to Maciek Klimkowski + * . + * - Fixed bug in write_scanner(). The buffer was not being properly + * updated for writes larger than OBUF_SIZE. Thanks to Henrik + * Johansson for identifying it. + * - Added Microtek X6 ID's. Thanks to Oliver Neukum + * . + * + * + * 0.4.1 2/15/2000 + * + * - Fixed 'count' bug in read_scanner(). Thanks to Henrik + * Johansson for identifying it. Amazing + * it has worked this long. + * - Fixed '>=' bug in both read/write_scanner methods. + * - Cleaned up both read/write_scanner() methods so that they are + * a little more readable. + * - Added a lot of Microtek ID's. Thanks to Adrian Perez Jorge. + * - Adopted the __initcall(). + * - Added #include to scanner.h for __initcall(). + * - Added one liner in irq_scanner() to keep gcc from complaining + * about an unused variable (data) if debugging was disabled + * in scanner.c. + * - Increased the timeout parameter in read_scanner() to 120 Secs. + * + * + * 0.4.2 3/23/2000 + * + * - Added Umax 1236U ID. Thanks to Philipp Baer . + * - Added Primax, ReadyScan, Visioneer, Colorado, and Genius ID's. + * Thanks to Adrian Perez Jorge . + * - Fixed error number reported for non-existant devices. Thanks to + * Spyridon Papadimitriou . + * - Added Acer Prisascan 620U ID's. Thanks to Joao . + * - Replaced __initcall() with module_init()/module_exit(). Updates + * from patch-2.3.48. + * - Replaced file_operations structure with new syntax. Updates + * from patch-2.3.49. + * - Changed #include "usb.h" to #include + * - Added #define SCN_IOCTL to exclude development areas + * since 2.4.x is about to be released. This mainly affects the + * ioctl() stuff. See scanner.h for more details. + * - Changed the return value for signal_pending() from -ERESTARTSYS to + * -EINTR. + * + * + * 0.4.3 4/30/2000 + * + * - Added Umax Astra 2200 ID. Thanks to Flynn Marquardt + * . + * - Added iVina 1200U ID. Thanks to Dyson Lin . + * - Added access time update for the device file courtesy of Paul + * Mackerras . This allows a user space daemon + * to turn the lamp off for a Umax 1220U scanner after a prescribed + * time. + * - Fixed HP S20 ID's. Thanks to Ruud Linders . + * - Added Acer ScanPrisa 620U ID. Thanks to Oliver + * Schwartz via sane-devel mail list. + * - Fixed bug in read_scanner for copy_to_user() function. The returned + * value should be 'partial' not 'this_read'. + * - Fixed bug in read_scanner. 'count' should be decremented + * by 'this_read' and not by 'partial'. This resulted in twice as many + * calls to read_scanner() for small amounts of data and possibly + * unexpected returns of '0'. Thanks to Karl Heinz + * Kremer and Alain Knaff + * for discovering this. + * - Integrated Randy Dunlap's patch for a + * scanner lookup/ident table. Thanks Randy. + * - Documentation updates. + * - Added wait queues to read_scanner(). + * + * + * 0.4.3.1 + * + * - Fixed HP S20 ID's...again..sigh. Thanks to Ruud + * Linders . + * + * + * TODO + * + * - Performance + * - Select/poll methods + * - More testing + * - Proper registry/assignment for LM9830 ioctl's + * + * + * Thanks to: + * + * - All the folks on the linux-usb list who put up with me. :) This + * has been a great learning experience for me. + * - To Linus Torvalds for this great OS. + * - The GNU folks. + * - The folks that forwarded Vendor:Product ID's to me. + * - Johannes Erdfelt for the loaning of a USB analyzer for tracking an + * issue with HP-4100 and uhci. + * - Adolfo Montero for his assistance. + * - All the folks who chimed in with reports and suggestions. + * - All the developers that are working on USB SANE backends or other + * applications to use USB scanners. + * + * Performance: + * + * System: Pentium 120, 80 MB RAM, OHCI, Linux 2.3.23, HP 4100C USB Scanner + * 300 dpi scan of the entire bed + * 24 Bit Color ~ 70 secs - 3.6 Mbit/sec + * 8 Bit Gray ~ 17 secs - 4.2 Mbit/sec + */ + +/* + * Scanner definitions, macros, module info, + * debug/ioctl/data_dump enable, and other constants. + */ +#include "scanner.h" + +static void +irq_scanner(struct urb *urb) +{ + +/* + * For the meantime, this is just a placeholder until I figure out what + * all I want to do with it -- or somebody else for that matter. + */ + + struct scn_usb_data *scn = urb->context; + unsigned char *data = &scn->button; + data += 0; /* Keep gcc from complaining about unused var */ + + if (urb->status) { + return; + } + + dbg("irq_scanner(%d): data:%x", scn->scn_minor, *data); + return; +} + + +static int +open_scanner(struct inode * inode, struct file * file) +{ + struct scn_usb_data *scn; + struct usb_device *dev; + + kdev_t scn_minor; + + scn_minor = USB_SCN_MINOR(inode); + + dbg("open_scanner: scn_minor:%d", scn_minor); + + if (!p_scn_table[scn_minor]) { + err("open_scanner(%d): Unable to access minor data", scn_minor); + return -ENODEV; + } + + scn = p_scn_table[scn_minor]; + + dev = scn->scn_dev; + + if (!dev) { + err("open_scanner(%d): Scanner device not present", scn_minor); + return -ENODEV; + } + + if (!scn->present) { + err("open_scanner(%d): Scanner is not present", scn_minor); + return -ENODEV; + } + + if (scn->isopen) { + err("open_scanner(%d): Scanner device is already open", scn_minor); + return -EBUSY; + } + + init_waitqueue_head(&scn->rd_wait_q); + + scn->isopen = 1; + + file->private_data = scn; /* Used by the read and write metheds */ + + MOD_INC_USE_COUNT; + + return 0; +} + +static int +close_scanner(struct inode * inode, struct file * file) +{ + struct scn_usb_data *scn; + + kdev_t scn_minor; + + scn_minor = USB_SCN_MINOR (inode); + + dbg("close_scanner: scn_minor:%d", scn_minor); + + if (!p_scn_table[scn_minor]) { + err("close_scanner(%d): invalid scn_minor", scn_minor); + return -ENODEV; + } + + scn = p_scn_table[scn_minor]; + + scn->isopen = 0; + + file->private_data = NULL; + + MOD_DEC_USE_COUNT; + + return 0; +} + +static ssize_t +write_scanner(struct file * file, const char * buffer, + size_t count, loff_t *ppos) +{ + struct scn_usb_data *scn; + struct usb_device *dev; + + ssize_t bytes_written = 0; /* Overall count of bytes written */ + ssize_t ret = 0; + + kdev_t scn_minor; + + int this_write; /* Number of bytes to write */ + int partial; /* Number of bytes successfully written */ + int result = 0; + + char *obuf; + + scn = file->private_data; + + scn_minor = scn->scn_minor; + + obuf = scn->obuf; + + dev = scn->scn_dev; + + file->f_dentry->d_inode->i_atime = CURRENT_TIME; + + while (count > 0) { + + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + this_write = (count >= OBUF_SIZE) ? OBUF_SIZE : count; + + if (copy_from_user(scn->obuf, buffer, this_write)) { + ret = -EFAULT; + break; + } + + result = usb_bulk_msg(dev,usb_sndbulkpipe(dev, scn->bulk_out_ep), obuf, this_write, &partial, 60*HZ); + dbg("write stats(%d): result:%d this_write:%d partial:%d", scn_minor, result, this_write, partial); + + if (result == USB_ST_TIMEOUT) { /* NAK -- shouldn't happen */ + warn("write_scanner: NAK recieved."); + ret = -ETIME; + break; + } else if (result < 0) { /* We should not get any I/O errors */ + warn("write_scanner(%d): funky result: %d. Please notify the maintainer.", scn_minor, result); + ret = -EIO; + break; + } + +#ifdef WR_DATA_DUMP + if (partial) { + unsigned char cnt, cnt_max; + cnt_max = (partial > 24) ? 24 : partial; + printk(KERN_DEBUG "dump(%d): ", scn_minor); + for (cnt=0; cnt < cnt_max; cnt++) { + printk("%X ", obuf[cnt]); + } + printk("\n"); + } +#endif + if (partial != this_write) { /* Unable to write all contents of obuf */ + ret = -EIO; + break; + } + + if (partial) { /* Data written */ + buffer += partial; + count -= partial; + bytes_written += partial; + } else { /* No data written */ + ret = 0; + break; + } + } + mdelay(5); /* This seems to help with SANE queries */ + return ret ? ret : bytes_written; +} + +static ssize_t +read_scanner(struct file * file, char * buffer, + size_t count, loff_t *ppos) +{ + struct scn_usb_data *scn; + struct usb_device *dev; + + ssize_t bytes_read; /* Overall count of bytes_read */ + ssize_t ret; + + kdev_t scn_minor; + + int partial; /* Number of bytes successfully read */ + int this_read; /* Max number of bytes to read */ + int result; + int rd_expire = RD_EXPIRE; + + char *ibuf; + + scn = file->private_data; + + scn_minor = scn->scn_minor; + + ibuf = scn->ibuf; + + dev = scn->scn_dev; + + bytes_read = 0; + ret = 0; + + file->f_dentry->d_inode->i_atime = CURRENT_TIME; /* Update the + atime of + the device + node */ + + while (count > 0) { + if (signal_pending(current)) { + ret = -EINTR; + break; + } + + this_read = (count >= IBUF_SIZE) ? IBUF_SIZE : count; + + result = usb_bulk_msg(dev, usb_rcvbulkpipe(dev, scn->bulk_in_ep), ibuf, this_read, &partial, RD_NAK_TIMEOUT); + dbg("read stats(%d): result:%d this_read:%d partial:%d count:%d", scn_minor, result, this_read, partial, count); + +/* + * Scanners are sometimes inheriently slow since they are mechanical + * in nature. USB bulk reads tend to timeout while the scanner is + * positioning, resetting, warming up the lamp, etc if the timeout is + * set too low. A very long timeout parameter for bulk reads was used + * to overcome this limitation, but this sometimes resulted in folks + * having to wait for the timeout to expire after pressing Ctrl-C from + * an application. The user was sometimes left with the impression + * that something had hung or crashed when in fact the USB read was + * just waiting on data. So, the below code retains the same long + * timeout period, but splits it up into smaller parts so that + * Ctrl-C's are acted upon in a reasonable amount of time. + */ + + if (result == USB_ST_TIMEOUT && !partial) { /* Timeout + and no + data */ + if (--rd_expire <= 0) { + warn("read_scanner(%d): excessive NAK's received", scn_minor); + ret = -ETIME; + break; + } else { + interruptible_sleep_on_timeout(&scn->rd_wait_q, RD_NAK_TIMEOUT); + continue; + } + } else if ((result < 0) && (result != USB_ST_DATAUNDERRUN)) { + warn("read_scanner(%d): funky result:%d. Please notify the maintainer.", scn_minor, (int)result); + ret = -EIO; + break; + } + +#ifdef RD_DATA_DUMP + if (partial) { + unsigned char cnt, cnt_max; + cnt_max = (partial > 24) ? 24 : partial; + printk(KERN_DEBUG "dump(%d): ", scn_minor); + for (cnt=0; cnt < cnt_max; cnt++) { + printk("%X ", ibuf[cnt]); + } + printk("\n"); + } +#endif + + if (partial) { /* Data returned */ + if (copy_to_user(buffer, ibuf, partial)) { + ret = -EFAULT; + break; + } + count -= this_read; /* Compensate for short reads */ + bytes_read += partial; /* Keep tally of what actually was read */ + buffer += partial; + } else { + ret = 0; + break; + } + } + + return ret ? ret : bytes_read; +} + +static void * +probe_scanner(struct usb_device *dev, unsigned int ifnum) +{ + struct scn_usb_data *scn; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + + int ep_cnt; + int ix; + + kdev_t scn_minor; + + char valid_device = 0; + char have_bulk_in, have_bulk_out, have_intr; + + if (vendor != -1 && product != -1) { + info("probe_scanner: User specified USB scanner -- Vendor:Product - %x:%x", vendor, product); + } + + dbg("probe_scanner: USB dev address:%p", dev); + dbg("probe_scanner: ifnum:%u", ifnum); + +/* + * 1. Check Vendor/Product + * 2. Determine/Assign Bulk Endpoints + * 3. Determine/Assign Intr Endpoint + */ + +/* + * There doesn't seem to be an imaging class defined in the USB + * Spec. (yet). If there is, HP isn't following it and it doesn't + * look like anybody else is either. Therefore, we have to test the + * Vendor and Product ID's to see what we have. Also, other scanners + * may be able to use this driver by specifying both vendor and + * product ID's as options to the scanner module in conf.modules. + * + * NOTE: Just because a product is supported here does not mean that + * applications exist that support the product. It's in the hopes + * that this will allow developers a means to produce applications + * that will support USB products. + * + * Until we detect a device which is pleasing, we silently punt. + */ + + for (ix = 0; ix < sizeof (scanner_device_ids) / sizeof (struct scanner_device); ix++) { + if ((dev->descriptor.idVendor == scanner_device_ids [ix].idVendor) && + (dev->descriptor.idProduct == scanner_device_ids [ix].idProduct)) { + valid_device = 1; + break; + } + } + if (dev->descriptor.idVendor == vendor && /* User specified */ + dev->descriptor.idProduct == product) { /* User specified */ + valid_device = 1; + } + + if (!valid_device) + return NULL; /* We didn't find anything pleasing */ + +/* + * After this point we can be a little noisy about what we are trying to + * configure. + */ + + if (dev->descriptor.bNumConfigurations != 1) { + info("probe_scanner: Only one device configuration is supported."); + return NULL; + } + + if (dev->config[0].bNumInterfaces != 1) { + info("probe_scanner: Only one device interface is supported."); + return NULL; + } + + interface = dev->config[0].interface[ifnum].altsetting; + endpoint = interface[ifnum].endpoint; + +/* + * Start checking for two bulk endpoints OR two bulk endpoints *and* one + * interrupt endpoint. If we have an interrupt endpoint go ahead and + * setup the handler. FIXME: This is a future enhancement... + */ + + dbg("probe_scanner: Number of Endpoints:%d", (int) interface->bNumEndpoints); + + if ((interface->bNumEndpoints != 2) && (interface->bNumEndpoints != 3)) { + info("probe_scanner: Only two or three endpoints supported."); + return NULL; + } + + ep_cnt = have_bulk_in = have_bulk_out = have_intr = 0; + + while (ep_cnt < interface->bNumEndpoints) { + + if (!have_bulk_in && IS_EP_BULK_IN(endpoint[ep_cnt])) { + ep_cnt++; + have_bulk_in = ep_cnt; + dbg("probe_scanner: bulk_in_ep:%d", have_bulk_in); + continue; + } + + if (!have_bulk_out && IS_EP_BULK_OUT(endpoint[ep_cnt])) { + ep_cnt++; + have_bulk_out = ep_cnt; + dbg("probe_scanner: bulk_out_ep:%d", have_bulk_out); + continue; + } + + if (!have_intr && IS_EP_INTR(endpoint[ep_cnt])) { + ep_cnt++; + have_intr = ep_cnt; + dbg("probe_scanner: intr_ep:%d", have_intr); + continue; + } + info("probe_scanner: Undetected endpoint. Notify the maintainer."); + return NULL; /* Shouldn't ever get here unless we have something weird */ + } + + +/* + * Perform a quick check to make sure that everything worked as it + * should have. + */ + + switch(interface->bNumEndpoints) { + case 2: + if (!have_bulk_in || !have_bulk_out) { + info("probe_scanner: Two bulk endpoints required."); + return NULL; + } + break; + case 3: + if (!have_bulk_in || !have_bulk_out || !have_intr) { + info("probe_scanner: Two bulk endpoints and one interrupt endpoint required."); + return NULL; + } + break; + default: + info("probe_scanner: Endpoint determination failed. Notify the maintainer."); + return NULL; + } + + +/* + * Determine a minor number and initialize the structure associated + * with it. The problem with this is that we are counting on the fact + * that the user will sequentially add device nodes for the scanner + * devices. */ + + for (scn_minor = 0; scn_minor < SCN_MAX_MNR; scn_minor++) { + if (!p_scn_table[scn_minor]) + break; + } + +/* Check to make sure that the last slot isn't already taken */ + if (p_scn_table[scn_minor]) { + err("probe_scanner: No more minor devices remaining."); + return NULL; + } + + dbg("probe_scanner: Allocated minor:%d", scn_minor); + + if (!(scn = kmalloc (sizeof (struct scn_usb_data), GFP_KERNEL))) { + err("probe_scanner: Out of memory."); + return NULL; + } + memset (scn, 0, sizeof(struct scn_usb_data)); + dbg ("probe_scanner(%d): Address of scn:%p", scn_minor, scn); + + +/* Ok, if we detected an interrupt EP, setup a handler for it */ + if (have_intr) { + dbg("probe_scanner(%d): Configuring IRQ handler for intr EP:%d", scn_minor, have_intr); + FILL_INT_URB(&scn->scn_irq, dev, + usb_rcvintpipe(dev, have_intr), + &scn->button, 1, irq_scanner, scn, + // endpoint[(int)have_intr].bInterval); + 250); + + if (usb_submit_urb(&scn->scn_irq)) { + err("probe_scanner(%d): Unable to allocate INT URB.", scn_minor); + kfree(scn); + return NULL; + } + } + + +/* Ok, now initialize all the relevant values */ + if (!(scn->obuf = (char *)kmalloc(OBUF_SIZE, GFP_KERNEL))) { + err("probe_scanner(%d): Not enough memory for the output buffer.", scn_minor); + kfree(scn); + return NULL; + } + dbg("probe_scanner(%d): obuf address:%p", scn_minor, scn->obuf); + + if (!(scn->ibuf = (char *)kmalloc(IBUF_SIZE, GFP_KERNEL))) { + err("probe_scanner(%d): Not enough memory for the input buffer.", scn_minor); + kfree(scn->obuf); + kfree(scn); + return NULL; + } + dbg("probe_scanner(%d): ibuf address:%p", scn_minor, scn->ibuf); + + scn->bulk_in_ep = have_bulk_in; + scn->bulk_out_ep = have_bulk_out; + scn->intr_ep = have_intr; + scn->present = 1; + scn->scn_dev = dev; + scn->scn_minor = scn_minor; + scn->isopen = 0; + + return p_scn_table[scn_minor] = scn; +} + +static void +disconnect_scanner(struct usb_device *dev, void *ptr) +{ + struct scn_usb_data *scn = (struct scn_usb_data *) ptr; + + if(scn->intr_ep) { + dbg("disconnect_scanner(%d): Unlinking IRQ URB", scn->scn_minor); + usb_unlink_urb(&scn->scn_irq); + } + usb_driver_release_interface(&scanner_driver, + &scn->scn_dev->actconfig->interface[scn->ifnum]); + + kfree(scn->ibuf); + kfree(scn->obuf); + + dbg("disconnect_scanner: De-allocating minor:%d", scn->scn_minor); + p_scn_table[scn->scn_minor] = NULL; + kfree (scn); +} + +#ifdef SCN_IOCTL +static int +ioctl_scanner(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_device *dev; + + int result; + + kdev_t scn_minor; + + scn_minor = USB_SCN_MINOR(inode); + + if (!p_scn_table[scn_minor]) { + err("ioctl_scanner(%d): invalid scn_minor", scn_minor); + return -ENODEV; + } + + dev = p_scn_table[scn_minor]->scn_dev; + + switch (cmd) + { + case PV8630_IOCTL_INREQUEST : + { + struct { + __u8 data; + __u8 request; + __u16 value; + __u16 index; + } args; + + if (copy_from_user(&args, (void *)arg, sizeof(args))) + return -EFAULT; + + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + args.request, USB_TYPE_VENDOR| + USB_RECIP_DEVICE|USB_DIR_IN, + args.value, args.index, &args.data, + 1, HZ*5); + + dbg("ioctl_scanner(%d): inreq: args.data:%x args.value:%x args.index:%x args.request:%x\n", scn_minor, args.data, args.value, args.index, args.request); + + if (copy_to_user((void *)arg, &args, sizeof(args))) + return -EFAULT; + + dbg("ioctl_scanner(%d): inreq: result:%d\n", scn_minor, result); + + return result; + } + case PV8630_IOCTL_OUTREQUEST : + { + struct { + __u8 request; + __u16 value; + __u16 index; + } args; + + if (copy_from_user(&args, (void *)arg, sizeof(args))) + return -EFAULT; + + dbg("ioctl_scanner(%d): outreq: args.value:%x args.index:%x args.request:%x\n", scn_minor, args.value, args.index, args.request); + + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + args.request, USB_TYPE_VENDOR| + USB_RECIP_DEVICE|USB_DIR_OUT, + args.value, args.index, NULL, + 0, HZ*5); + + dbg("ioctl_scanner(%d): outreq: result:%d\n", scn_minor, result); + + return result; + } + default: + return -ENOIOCTLCMD; + } + return 0; +} +#endif /* SCN_IOCTL */ + +static struct +file_operations usb_scanner_fops = { + read: read_scanner, + write: write_scanner, +#ifdef SCN_IOCTL + ioctl: ioctl_scanner, +#endif /* SCN_IOCTL */ + open: open_scanner, + release: close_scanner, +}; + +static struct +usb_driver scanner_driver = { + "usbscanner", + probe_scanner, + disconnect_scanner, + { NULL, NULL }, + &usb_scanner_fops, + SCN_BASE_MNR +}; + +void __exit +usb_scanner_exit(void) +{ + usb_deregister(&scanner_driver); +} + +int __init +usb_scanner_init (void) +{ + if (usb_register(&scanner_driver) < 0) + return -1; + + info("USB Scanner support registered."); + return 0; +} + +module_init(usb_scanner_init); +module_exit(usb_scanner_exit); diff --git a/drivers/usb/scanner.h b/drivers/usb/scanner.h new file mode 100644 index 000000000000..7662f125e1f4 --- /dev/null +++ b/drivers/usb/scanner.h @@ -0,0 +1,166 @@ +/* + * Driver for USB Scanners (linux-2.4.0test1-ac7) + * + * Copyright (C) 1999, 2000 David E. Nelson + * + * David E. Nelson (dnelson@jump.net) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// #define DEBUG + +#include + +static __s32 vendor=-1, product=-1; + +MODULE_AUTHOR("David E. Nelson, dnelson@jump.net, http://www.jump.net/~dnelson"); +MODULE_DESCRIPTION("USB Scanner Driver"); + +MODULE_PARM(vendor, "i"); +MODULE_PARM_DESC(vendor, "User specified USB idVendor"); + +MODULE_PARM(product, "i"); +MODULE_PARM_DESC(product, "User specified USB idProduct"); + + +/* Enable to activate the ioctl interface. This is mainly meant for */ +/* development purposes until an ioctl number is officially registered */ +// #define SCN_IOCTL + +/* WARNING: These DATA_DUMP's can produce a lot of data. Caveat Emptor. */ +// #define RD_DATA_DUMP /* Enable to dump data - limited to 24 bytes */ +// #define WR_DATA_DUMP /* DEBUG does not have to be defined. */ + +#define IS_EP_BULK(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_BULK ? 1 : 0) +#define IS_EP_BULK_IN(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) +#define IS_EP_BULK_OUT(ep) (IS_EP_BULK(ep) && ((ep).bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) +#define IS_EP_INTR(ep) ((ep).bmAttributes == USB_ENDPOINT_XFER_INT ? 1 : 0) + +#define USB_SCN_MINOR(X) MINOR((X)->i_rdev) - SCN_BASE_MNR + +#ifdef DEBUG +#define SCN_DEBUG(X) X +#else +#define SCN_DEBUG(X) +#endif + +#define IBUF_SIZE 32768 +#define OBUF_SIZE 4096 + +/* read_scanner timeouts -- RD_NAK_TIMEOUT * RD_EXPIRE = Number of seconds */ +#define RD_NAK_TIMEOUT (10*HZ) /* Number of X seconds to wait */ +#define RD_EXPIRE 12 /* Number of attempts to wait X seconds */ + + +/* FIXME: These are NOT registered ioctls()'s */ +#define PV8630_IOCTL_INREQUEST 69 +#define PV8630_IOCTL_OUTREQUEST 70 + +#define SCN_MAX_MNR 16 /* We're allocated 16 minors */ +#define SCN_BASE_MNR 48 /* USB Scanners start at minor 48 */ + +struct scn_usb_data { + struct usb_device *scn_dev; + struct urb scn_irq; + unsigned int ifnum; /* Interface number of the USB device */ + kdev_t scn_minor; /* Scanner minor - used in disconnect() */ + unsigned char button; /* Front panel buffer */ + char isopen; /* Not zero if the device is open */ + char present; /* Not zero if device is present */ + char *obuf, *ibuf; /* transfer buffers */ + char bulk_in_ep, bulk_out_ep, intr_ep; /* Endpoint assignments */ + wait_queue_head_t rd_wait_q; /* read timeouts */ +}; + +static struct scn_usb_data *p_scn_table[SCN_MAX_MNR] = { NULL, /* ... */}; + +/* table of scanners that may work with this driver */ +static const struct scanner_device { + __u16 idVendor; + __u16 idProduct; +} scanner_device_ids [] = { + /* Acer */ + { 0x04a5, 0x2060 }, /* Prisa Acerscan 620U & 640U (!) */ + { 0x04a5, 0x2040 }, /* Prisa AcerScan 620U (!) */ + /* Agfa */ + { 0x06bd, 0x0001 }, /* SnapScan 1212U */ + { 0x06bd, 0x2061 }, /* Another SnapScan 1212U (?) */ + { 0x06bd, 0x0100 }, /* SnapScan Touch */ + /* Colorado -- See Primax/Colorado below */ + /* Epson -- See Seiko/Epson below */ + /* Genius */ + { 0x0458, 0x2001 }, /* ColorPage-Vivid Pro */ + /* Hewlett Packard */ + { 0x03f0, 0x0205 }, /* 3300C */ + { 0x03f0, 0x0101 }, /* 4100C */ + { 0x03f0, 0x0105 }, /* 4200C */ + { 0x03f0, 0x0102 }, /* PhotoSmart S20 */ + { 0x03f0, 0x0401 }, /* 5200C */ + { 0x03f0, 0x0201 }, /* 6200C */ + { 0x03f0, 0x0601 }, /* 6300C */ + /* iVina */ + { 0x0638, 0x0268 }, /* 1200U */ + /* Microtek */ + { 0x05da, 0x0099 }, /* ScanMaker X6 - X6U */ + { 0x05da, 0x0094 }, /* Phantom 336CX - C3 */ + { 0x05da, 0x00a0 }, /* Phantom 336CX - C3 #2 */ + { 0x05da, 0x009a }, /* Phantom C6 */ + { 0x05da, 0x00a3 }, /* ScanMaker V6USL */ + { 0x05da, 0x80a3 }, /* ScanMaker V6USL #2 */ + { 0x05da, 0x80ac }, /* ScanMaker V6UL - SpicyU */ + /* Mustek */ + { 0x055f, 0x0001 }, /* 1200 CU */ + /* Primax/Colorado */ + { 0x0461, 0x0300 }, /* G2-300 #1 */ + { 0x0461, 0x0380 }, /* G2-600 #1 */ + { 0x0461, 0x0301 }, /* G2E-300 #1 */ + { 0x0461, 0x0381 }, /* ReadyScan 636i */ + { 0x0461, 0x0302 }, /* G2-300 #2 */ + { 0x0461, 0x0382 }, /* G2-600 #2 */ + { 0x0461, 0x0303 }, /* G2E-300 #2 */ + { 0x0461, 0x0383 }, /* G2E-600 */ + { 0x0461, 0x0340 }, /* Colorado USB 9600 */ + { 0x0461, 0x0360 }, /* Colorado USB 19200 */ + { 0x0461, 0x0341 }, /* Colorado 600u */ + { 0x0461, 0x0361 }, /* Colorado 1200u */ + /* Seiko/Epson Corp. */ + { 0x04b8, 0x0101 }, /* Perfection 636U and 636Photo */ + { 0x04b8, 0x0103 }, /* Perfection 610 */ + { 0x04b8, 0x0104 }, /* Perfection 1200U and 1200Photo */ + /* Umax */ + { 0x1606, 0x0010 }, /* Astra 1220U */ + { 0x1606, 0x0002 }, /* Astra 1236U */ + { 0x1606, 0x0030 }, /* Astra 2000U */ + { 0x1606, 0x0230 }, /* Astra 2200U */ + /* Visioneer */ + { 0x04a7, 0x0221 }, /* OneTouch 5300 */ + { 0x04a7, 0x0221 }, /* OneTouch 7600 duplicate ID (!) */ + { 0x04a7, 0x0231 }, /* 6100 */ +}; + +/* Forward declarations */ +static struct usb_driver scanner_driver; diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile new file mode 100644 index 000000000000..7d24c34f915f --- /dev/null +++ b/drivers/usb/serial/Makefile @@ -0,0 +1,29 @@ +# +# Makefile for the USB serial device drivers. +# + +O_TARGET := usb-serial.o +M_OBJS := usb-serial.o +O_OBJS := usbserial.o +MOD_LIST_NAME := USB_SERIAL_MODULES + +ifeq ($(CONFIG_USB_SERIAL_VISOR),y) + O_OBJS += visor.o +endif +ifeq ($(CONFIG_USB_SERIAL_WHITEHEAT),y) + O_OBJS += whiteheat.o +endif +ifeq ($(CONFIG_USB_SERIAL_FTDI_SIO),y) + O_OBJS += ftdi_sio.o +endif +ifeq ($(CONFIG_USB_SERIAL_KEYSPAN_PDA),y) + O_OBJS += keyspan_pda.o +endif +ifeq ($(CONFIG_USB_SERIAL_OMNINET),y) + O_OBJS += omninet.o +endif +ifeq ($(CONFIG_USB_SERIAL_DIGI_ACCELEPORT),y) + O_OBJS += digi_acceleport.o +endif + +include $(TOPDIR)/Rules.make diff --git a/drivers/usb/serial/Makefile-keyspan_pda_fw b/drivers/usb/serial/Makefile-keyspan_pda_fw new file mode 100644 index 000000000000..c20baf728011 --- /dev/null +++ b/drivers/usb/serial/Makefile-keyspan_pda_fw @@ -0,0 +1,16 @@ + +# some rules to handle the quirks of the 'as31' assembler, like +# insisting upon fixed suffixes for the input and output files, +# and its lack of preprocessor support + +all: keyspan_pda_fw.h + +%.asm: %.S + gcc -x assembler-with-cpp -P -E -o $@ $< + +%.hex: %.asm + as31 -l $< + mv $*.obj $@ + +%_fw.h: %.hex ezusb_convert.pl + perl ezusb_convert.pl $* < $< > $@ diff --git a/drivers/usb/serial/digi_acceleport.c b/drivers/usb/serial/digi_acceleport.c new file mode 100644 index 000000000000..89880b78aa5c --- /dev/null +++ b/drivers/usb/serial/digi_acceleport.c @@ -0,0 +1,1710 @@ +/* +* Digi AccelePort USB-4 Serial Converter +* +* Copyright 2000 by Digi International +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* Shamelessly based on Brian Warner's keyspan_pda.c and Greg Kroah-Hartman's +* usb-serial driver. +* +* Peter Berger (pberger@brimson.com) +* Al Borchers (borchers@steinerpoint.com) +* +* (6/4/2000) pberger and borchers +* -- Replaced separate calls to spin_unlock_irqrestore and +* interruptible_sleep_on_interruptible with a new function +* cond_wait_interruptible_timeout_irqrestore. This eliminates +* the race condition where the wake up could happen after +* the unlock and before the sleep. +* -- Close now waits for output to drain. +* -- Open waits until any close in progress is finished. +* -- All out of band responses are now processed, not just the +* first in a USB packet. +* -- Fixed a bug that prevented the driver from working when the +* first Digi port was not the first USB serial port--the driver +* was mistakenly using the external USB serial port number to +* try to index into its internal ports. +* -- Fixed an SMP bug -- write_bulk_callback is called directly from +* an interrupt, so spin_lock_irqsave/spin_unlock_irqrestore are +* needed for locks outside write_bulk_callback that are also +* acquired by write_bulk_callback to prevent deadlocks. +* -- Fixed support for select() by making digi_chars_in_buffer() +* return 256 when -EINPROGRESS is set, as the line discipline +* code in n_tty.c expects. +* -- Fixed an include file ordering problem that prevented debugging +* messages from working. +* -- Fixed an intermittent timeout problem that caused writes to +* sometimes get stuck on some machines on some kernels. It turns +* out in these circumstances write_chan() (in n_tty.c) was +* asleep waiting for our wakeup call. Even though we call +* wake_up_interruptible() in digi_write_bulk_callback(), there is +* a race condition that could cause the wakeup to fail: if our +* wake_up_interruptible() call occurs between the time that our +* driver write routine finishes and write_chan() sets current->state +* to TASK_INTERRUPTIBLE, the effect of our wakeup setting the state +* to TASK_RUNNING will be lost and write_chan's subsequent call to +* schedule() will never return (unless it catches a signal). +* This race condition occurs because write_bulk_callback() (and thus +* the wakeup) are called asynchonously from an interrupt, rather than +* from the scheduler. We can avoid the race by calling the wakeup +* from the scheduler queue and that's our fix: Now, at the end of +* write_bulk_callback() we queue up a wakeup call on the scheduler +* task queue. We still also invoke the wakeup directly since that +* squeezes a bit more performance out of the driver, and any lost +* race conditions will get cleaned up at the next scheduler run. +* +* NOTE: The problem also goes away if you comment out +* the two code lines in write_chan() where current->state +* is set to TASK_RUNNING just before calling driver.write() and to +* TASK_INTERRUPTIBLE immediately afterwards. This is why the +* problem did not show up with the 2.2 kernels -- they do not +* include that code. +* +* (5/16/2000) pberger and borchers +* -- Added timeouts to sleeps, to defend against lost wake ups. +* -- Handle transition to/from B0 baud rate in digi_set_termios. +* +* (5/13/2000) pberger and borchers +* -- All commands now sent on out of band port, using +* digi_write_oob_command. +* -- Get modem control signals whenever they change, support TIOCMGET/ +* SET/BIS/BIC ioctls. +* -- digi_set_termios now supports parity, word size, stop bits, and +* receive enable. +* -- Cleaned up open and close, use digi_set_termios and +* digi_write_oob_command to set port parameters. +* -- Added digi_startup_device to start read chains on all ports. +* -- Write buffer is only used when count==1, to be sure put_char can +* write a char (unless the buffer is full). +* +* (5/10/2000) pberger and borchers +* -- Added MOD_INC_USE_COUNT/MOD_DEC_USE_COUNT calls on open/close. +* -- Fixed problem where the first incoming character is lost on +* port opens after the first close on that port. Now we keep +* the read_urb chain open until shutdown. +* -- Added more port conditioning calls in digi_open and digi_close. +* -- Convert port->active to a use count so that we can deal with multiple +* opens and closes properly. +* -- Fixed some problems with the locking code. +* +* (5/3/2000) pberger and borchers +* -- First alpha version of the driver--many known limitations and bugs. +* +* +* Locking and SMP +* +* - Each port, including the out-of-band port, has a lock used to +* serialize all access to the port's private structure. +* - The port lock is also used to serialize all writes and access to +* the port's URB. +* - The port lock is also used for the port write_wait condition +* variable. Holding the port lock will prevent a wake up on the +* port's write_wait; this can be used with cond_wait_... to be sure +* the wake up is not lost in a race when dropping the lock and +* sleeping waiting for the wakeup. +* - digi_write() does not sleep, since it is sometimes called on +* interrupt time. +* - digi_write_bulk_callback() and digi_read_bulk_callback() are +* called directly from interrupts. Hence spin_lock_irqsave() +* and spin_lock_irqrestore() are used in the rest of the code +* for any locks they acquire. +* - digi_write_bulk_callback() gets the port lock before waking up +* processes sleeping on the port write_wait. It also schedules +* wake ups so they happen from the scheduler, because the tty +* system can miss wake ups from interrupts. +* - All sleeps use a timeout of DIGI_RETRY_TIMEOUT before looping to +* recheck the condition they are sleeping on. This is defensive, +* in case a wake up is lost. +* +* $Id: digi_acceleport.c,v 1.56 2000/06/07 22:47:30 root Exp root $ +*/ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif + +#include +#include "usb-serial.h" + + +/* Defines */ + +/* port buffer length -- must be <= transfer buffer length - 2 */ +/* so we can be sure to send the full buffer in one urb */ +#define DIGI_PORT_BUF_LEN 8 + +/* retry timeout while sleeping */ +#define DIGI_RETRY_TIMEOUT (HZ/10) + +/* timeout while waiting for tty output to drain in close */ +/* this delay is used twice in close, so the total delay could */ +/* be twice this value */ +#define DIGI_CLOSE_TIMEOUT (5*HZ) + + +/* AccelePort USB Defines */ + +/* ids */ +#define DIGI_VENDOR_ID 0x05c5 +#define DIGI_ID 0x0004 + +/* commands + * "INB": can be used on the in-band endpoint + * "OOB": can be used on the out-of-band endpoint + */ +#define DIGI_CMD_SET_BAUD_RATE 0 /* INB, OOB */ +#define DIGI_CMD_SET_WORD_SIZE 1 /* INB, OOB */ +#define DIGI_CMD_SET_PARITY 2 /* INB, OOB */ +#define DIGI_CMD_SET_STOP_BITS 3 /* INB, OOB */ +#define DIGI_CMD_SET_INPUT_FLOW_CONTROL 4 /* INB, OOB */ +#define DIGI_CMD_SET_OUTPUT_FLOW_CONTROL 5 /* INB, OOB */ +#define DIGI_CMD_SET_DTR_SIGNAL 6 /* INB, OOB */ +#define DIGI_CMD_SET_RTS_SIGNAL 7 /* INB, OOB */ +#define DIGI_CMD_READ_INPUT_SIGNALS 8 /* OOB */ +#define DIGI_CMD_IFLUSH_FIFO 9 /* OOB */ +#define DIGI_CMD_RECEIVE_ENABLE 10 /* INB, OOB */ +#define DIGI_CMD_BREAK_CONTROL 11 /* INB, OOB */ +#define DIGI_CMD_LOCAL_LOOPBACK 12 /* INB, OOB */ +#define DIGI_CMD_TRANSMIT_IDLE 13 /* INB, OOB */ +#define DIGI_CMD_READ_UART_REGISTER 14 /* OOB */ +#define DIGI_CMD_WRITE_UART_REGISTER 15 /* INB, OOB */ +#define DIGI_CMD_AND_UART_REGISTER 16 /* INB, OOB */ +#define DIGI_CMD_OR_UART_REGISTER 17 /* INB, OOB */ +#define DIGI_CMD_SEND_DATA 18 /* INB */ +#define DIGI_CMD_RECEIVE_DATA 19 /* INB */ +#define DIGI_CMD_RECEIVE_DISABLE 20 /* INB */ +#define DIGI_CMD_GET_PORT_TYPE 21 /* OOB */ + +/* baud rates */ +#define DIGI_BAUD_50 0 +#define DIGI_BAUD_75 1 +#define DIGI_BAUD_110 2 +#define DIGI_BAUD_150 3 +#define DIGI_BAUD_200 4 +#define DIGI_BAUD_300 5 +#define DIGI_BAUD_600 6 +#define DIGI_BAUD_1200 7 +#define DIGI_BAUD_1800 8 +#define DIGI_BAUD_2400 9 +#define DIGI_BAUD_4800 10 +#define DIGI_BAUD_7200 11 +#define DIGI_BAUD_9600 12 +#define DIGI_BAUD_14400 13 +#define DIGI_BAUD_19200 14 +#define DIGI_BAUD_28800 15 +#define DIGI_BAUD_38400 16 +#define DIGI_BAUD_57600 17 +#define DIGI_BAUD_76800 18 +#define DIGI_BAUD_115200 19 +#define DIGI_BAUD_153600 20 +#define DIGI_BAUD_230400 21 +#define DIGI_BAUD_460800 22 + +/* arguments */ +#define DIGI_WORD_SIZE_5 0 +#define DIGI_WORD_SIZE_6 1 +#define DIGI_WORD_SIZE_7 2 +#define DIGI_WORD_SIZE_8 3 + +#define DIGI_PARITY_NONE 0 +#define DIGI_PARITY_ODD 1 +#define DIGI_PARITY_EVEN 2 +#define DIGI_PARITY_MARK 3 +#define DIGI_PARITY_SPACE 4 + +#define DIGI_STOP_BITS_1 0 +#define DIGI_STOP_BITS_2 1 + +#define DIGI_INPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_INPUT_FLOW_CONTROL_RTS 2 +#define DIGI_INPUT_FLOW_CONTROL_DTR 4 + +#define DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF 1 +#define DIGI_OUTPUT_FLOW_CONTROL_CTS 2 +#define DIGI_OUTPUT_FLOW_CONTROL_DSR 4 + +#define DIGI_DTR_INACTIVE 0 +#define DIGI_DTR_ACTIVE 1 +#define DIGI_DTR_INPUT_FLOW_CONTROL 2 + +#define DIGI_RTS_INACTIVE 0 +#define DIGI_RTS_ACTIVE 1 +#define DIGI_RTS_INPUT_FLOW_CONTROL 2 +#define DIGI_RTS_TOGGLE 3 + +#define DIGI_FLUSH_TX 1 +#define DIGI_FLUSH_RX 2 +#define DIGI_RESUME_TX 4 /* clears xoff condition */ + +#define DIGI_TRANSMIT_NOT_IDLE 0 +#define DIGI_TRANSMIT_IDLE 1 + +#define DIGI_DISABLE 0 +#define DIGI_ENABLE 1 + +#define DIGI_DEASSERT 0 +#define DIGI_ASSERT 1 + +/* in band status codes */ +#define DIGI_OVERRUN_ERROR 4 +#define DIGI_PARITY_ERROR 8 +#define DIGI_FRAMING_ERROR 16 +#define DIGI_BREAK_ERROR 32 + +/* out of band status */ +#define DIGI_NO_ERROR 0 +#define DIGI_BAD_FIRST_PARAMETER 1 +#define DIGI_BAD_SECOND_PARAMETER 2 +#define DIGI_INVALID_LINE 3 +#define DIGI_INVALID_OPCODE 4 + +/* input signals */ +#define DIGI_READ_INPUT_SIGNALS_SLOT 1 +#define DIGI_READ_INPUT_SIGNALS_ERR 2 +#define DIGI_READ_INPUT_SIGNALS_BUSY 4 +#define DIGI_READ_INPUT_SIGNALS_PE 8 +#define DIGI_READ_INPUT_SIGNALS_CTS 16 +#define DIGI_READ_INPUT_SIGNALS_DSR 32 +#define DIGI_READ_INPUT_SIGNALS_RI 64 +#define DIGI_READ_INPUT_SIGNALS_DCD 128 + + +/* macros */ +#define MAX(a,b) (((a)>(b))?(a):(b)) +#define MIN(a,b) (((a)<(b))?(a):(b)) + + +/* Structures */ + +typedef struct digi_private { + int dp_port_num; + spinlock_t dp_port_lock; + int dp_buf_len; + unsigned char dp_buf[DIGI_PORT_BUF_LEN]; + unsigned int dp_modem_signals; + int dp_transmit_idle; + int dp_in_close; + wait_queue_head_t dp_close_wait; /* wait queue for close */ + struct tq_struct dp_tasks; +} digi_private_t; + + +/* Local Function Declarations */ + +static void digi_wakeup_write( struct usb_serial_port *port ); +static void digi_wakeup_write_lock( struct usb_serial_port *port ); +static int digi_write_oob_command( unsigned char *buf, int count ); +static int digi_write_inb_command( struct usb_serial_port *port, + unsigned char *buf, int count ) __attribute__((unused)); +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ); +static int digi_transmit_idle( struct usb_serial_port *port, + unsigned long timeout ); +static void digi_rx_throttle (struct usb_serial_port *port); +static void digi_rx_unthrottle (struct usb_serial_port *port); +static void digi_set_termios( struct usb_serial_port *port, + struct termios *old_termios ); +static void digi_break_ctl( struct usb_serial_port *port, int break_state ); +static int digi_ioctl( struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg ); +static int digi_write( struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count ); +static void digi_write_bulk_callback( struct urb *urb ); +static int digi_write_room( struct usb_serial_port *port ); +static int digi_chars_in_buffer( struct usb_serial_port *port ); +static int digi_open( struct usb_serial_port *port, struct file *filp ); +static void digi_close( struct usb_serial_port *port, struct file *filp ); +static int digi_startup_device( struct usb_serial *serial ); +static int digi_startup( struct usb_serial *serial ); +static void digi_shutdown( struct usb_serial *serial ); +static void digi_read_bulk_callback( struct urb *urb ); +static void digi_read_oob_callback( struct urb *urb ); + + +/* Statics */ + +/* device info needed for the Digi serial converter */ +static u16 digi_vendor_id = DIGI_VENDOR_ID; +static u16 digi_product_id = DIGI_ID; + +/* out of band port */ +static int oob_port_num; /* index of out-of-band port */ +static struct usb_serial_port *oob_port; /* out-of-band port */ +static int device_startup = 0; + +spinlock_t startup_lock; /* used by startup_device */ + +static wait_queue_head_t modem_change_wait; +static wait_queue_head_t transmit_idle_wait; + + +/* Globals */ + +struct usb_serial_device_type digi_acceleport_device = { + name: "Digi USB", + idVendor: &digi_vendor_id, + idProduct: &digi_product_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 0, + num_bulk_in: 5, + num_bulk_out: 5, + num_ports: 4, + open: digi_open, + close: digi_close, + write: digi_write, + write_room: digi_write_room, + write_bulk_callback: digi_write_bulk_callback, + read_bulk_callback: digi_read_bulk_callback, + chars_in_buffer: digi_chars_in_buffer, + throttle: digi_rx_throttle, + unthrottle: digi_rx_unthrottle, + ioctl: digi_ioctl, + set_termios: digi_set_termios, + break_ctl: digi_break_ctl, + startup: digi_startup, + shutdown: digi_shutdown, +}; + + +/* Functions */ + +/* +* Cond Wait Interruptible Timeout Irqrestore +* +* Do spin_unlock_irqrestore and interruptible_sleep_on_timeout +* so that wake ups are not lost if they occur between the unlock +* and the sleep. In other words, spin_lock_irqrestore and +* interruptible_sleep_on_timeout are "atomic" with respect to +* wake ups. This is used to implement condition variables. +*/ + +static long cond_wait_interruptible_timeout_irqrestore( + wait_queue_head_t *q, long timeout, + spinlock_t *lock, unsigned long flags ) +{ + + wait_queue_t wait; + + + init_waitqueue_entry( &wait, current ); + + set_current_state( TASK_INTERRUPTIBLE ); + + add_wait_queue( q, &wait ); + + spin_unlock_irqrestore( lock, flags ); + + timeout = schedule_timeout(timeout); + + set_current_state( TASK_RUNNING ); + + remove_wait_queue( q, &wait ); + + return( timeout ); + +} + + +/* +* Digi Wakeup Write +* +* Wake up port, line discipline, and tty processes sleeping +* on writes. +*/ + +static void digi_wakeup_write_lock( struct usb_serial_port *port ) +{ + + unsigned long flags; + digi_private_t *priv = (digi_private_t *)(port->private); + + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + digi_wakeup_write( port ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + +} + +static void digi_wakeup_write( struct usb_serial_port *port ) +{ + + struct tty_struct *tty = port->tty; + + + /* wake up port processes */ + wake_up_interruptible( &port->write_wait ); + + /* wake up line discipline */ + if( (tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) + && tty->ldisc.write_wakeup ) + (tty->ldisc.write_wakeup)(tty); + + /* wake up other tty processes */ + wake_up_interruptible( &tty->write_wait ); + +} + + +/* +* Digi Write OOB Command +* +* Write commands on the out of band port. Commands are 4 +* bytes each, multiple commands can be sent at once, and +* no command will be split across USB packets. Returns 0 +* if successful, -EINTR if interrupted while sleeping, or +* a negative error returned by usb_submit_urb. +*/ + +static int digi_write_oob_command( unsigned char *buf, int count ) +{ + + int ret = 0; + int len; + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); + unsigned long flags = 0; + + +dbg( "digi_write_oob_command: TOP: port=%d, count=%d", oob_port_num, count ); + + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + + while( count > 0 ) { + + while( oob_port->write_urb->status == -EINPROGRESS ) { + cond_wait_interruptible_timeout_irqrestore( + &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + } + + /* len must be a multiple of 4, so commands are not split */ + len = MIN( count, oob_port->bulk_out_size ); + if( len > 4 ) + len &= ~3; + + memcpy( oob_port->write_urb->transfer_buffer, buf, len ); + oob_port->write_urb->transfer_buffer_length = len; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + count -= len; + buf += len; + } else { + dbg( + "digi_write_oob_command: usb_submit_urb failed, ret=%d", + ret ); + break; + } + + } + + spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); + + return( ret ); + +} + + +/* +* Digi Write In Band Command +* +* Write commands on the given port. Commands are 4 +* bytes each, multiple commands can be sent at once, and +* no command will be split across USB packets. Returns 0 +* if successful, or a negative error returned by digi_write. +*/ + +static int digi_write_inb_command( struct usb_serial_port *port, + unsigned char *buf, int count ) +{ + + int ret = 0; + int len; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long flags = 0; + + +dbg( "digi_write_inb_command: TOP: port=%d, count=%d", priv->dp_port_num, +count ); + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + while( count > 0 ) { + + while( port->write_urb->status == -EINPROGRESS ) { + cond_wait_interruptible_timeout_irqrestore( + &port->write_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + /* len must be a multiple of 4 and small enough to */ + /* guarantee the write will send all data (or none), */ + /* so commands are not split */ + len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); + if( len > 4 ) + len &= ~3; + + /* write any buffered data first */ + if( priv->dp_buf_len > 0 ) { + data[0] = DIGI_CMD_SEND_DATA; + data[1] = priv->dp_buf_len; + memcpy( data+2, priv->dp_buf, priv->dp_buf_len ); + memcpy( data+2+priv->dp_buf_len, buf, len ); + port->write_urb->transfer_buffer_length + = priv->dp_buf_len+2+len; + } else { + memcpy( data, buf, len ); + port->write_urb->transfer_buffer_length = len; + } + +#ifdef DEBUG_DATA + { + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + priv->dp_port_num, port->write_urb->transfer_buffer_length ); + for( i=0; iwrite_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); + } +#endif + + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_buf_len = 0; + count -= len; + buf += len; + } else { + dbg( + "digi_write_inb_command: usb_submit_urb failed, ret=%d", + ret ); + break; + } + + } + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + return( ret ); + +} + + +/* +* Digi Set Modem Signals +* +* Sets or clears DTR and RTS on the port, according to the +* modem_signals argument. Use TIOCM_DTR and TIOCM_RTS flags +* for the modem_signals argument. Returns 0 if successful, +* -EINTR if interrupted while sleeping, or a non-zero error +* returned by usb_submit_urb. +*/ + +static int digi_set_modem_signals( struct usb_serial_port *port, + unsigned int modem_signals ) +{ + + int ret; + unsigned char *data = oob_port->write_urb->transfer_buffer; + digi_private_t *port_priv = (digi_private_t *)(port->private); + digi_private_t *oob_priv = (digi_private_t *)(oob_port->private); + unsigned long flags = 0; + + +dbg( "digi_set_modem_signals: TOP: port=%d, modem_signals=0x%x", +port_priv->dp_port_num, modem_signals ); + + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + spin_lock_irqsave( &port_priv->dp_port_lock, flags ); + + while( oob_port->write_urb->status == -EINPROGRESS ) { + spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + cond_wait_interruptible_timeout_irqrestore( + &oob_port->write_wait, DIGI_RETRY_TIMEOUT, + &oob_priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &oob_priv->dp_port_lock, flags ); + spin_lock_irqsave( &port_priv->dp_port_lock, flags ); + } + + data[0] = DIGI_CMD_SET_DTR_SIGNAL; + data[1] = port_priv->dp_port_num; + data[2] = (modem_signals&TIOCM_DTR) ? + DIGI_DTR_ACTIVE : DIGI_DTR_INACTIVE; + data[3] = 0; + + data[4] = DIGI_CMD_SET_RTS_SIGNAL; + data[5] = port_priv->dp_port_num; + data[6] = (modem_signals&TIOCM_RTS) ? + DIGI_RTS_ACTIVE : DIGI_RTS_INACTIVE; + data[7] = 0; + + oob_port->write_urb->transfer_buffer_length = 8; + + if( (ret=usb_submit_urb(oob_port->write_urb)) == 0 ) { + port_priv->dp_modem_signals = + (port_priv->dp_modem_signals&~(TIOCM_DTR|TIOCM_RTS)) + | (modem_signals&(TIOCM_DTR|TIOCM_RTS)); + } else { + dbg( "digi_set_modem_signals: usb_submit_urb failed, ret=%d", + ret ); + } + + spin_unlock_irqrestore( &port_priv->dp_port_lock, flags ); + spin_unlock_irqrestore( &oob_priv->dp_port_lock, flags ); + + return( ret ); + +} + + +/* +* Digi Transmit Idle +* +* Digi transmit idle waits, up to timeout ticks, for the transmitter +* to go idle. It returns 0 if successful or a negative error. +* +* There are race conditions here if more than one process is calling +* digi_transmit_idle on the same port at the same time. However, this +* is only called from close, and only one process can be in close on a +* port at a time, so its ok. +*/ + +static int digi_transmit_idle( struct usb_serial_port *port, + unsigned long timeout ) +{ + + int ret; + unsigned char buf[2]; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; + + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + buf[0] = DIGI_CMD_TRANSMIT_IDLE; + buf[1] = 0; + + if( (ret=digi_write_inb_command( port, buf, 2 )) != 0 ) + return( ret ); + + timeout += jiffies; + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + while( jiffies < timeout && !priv->dp_transmit_idle ) { + cond_wait_interruptible_timeout_irqrestore( + &transmit_idle_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + priv->dp_transmit_idle = 0; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + return( 0 ); + +} + + +static void digi_rx_throttle( struct usb_serial_port *port ) +{ + +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_rx_throttle: TOP: port=%d", priv->dp_port_num ); + + /* stop receiving characters. We just turn off the URB request, and + let chars pile up in the device. If we're doing hardware + flowcontrol, the device will signal the other end when its buffer + fills up. If we're doing XON/XOFF, this would be a good time to + send an XOFF, although it might make sense to foist that off + upon the device too. */ + + // usb_unlink_urb(port->interrupt_in_urb); + +} + + +static void digi_rx_unthrottle( struct usb_serial_port *port ) +{ + +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_rx_unthrottle: TOP: port=%d", priv->dp_port_num ); + + /* just restart the receive interrupt URB */ + //if (usb_submit_urb(port->interrupt_in_urb)) + // dbg( "digi_rx_unthrottle: usb_submit_urb failed" ); + +} + + +static void digi_set_termios( struct usb_serial_port *port, + struct termios *old_termios ) +{ + + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned int iflag = port->tty->termios->c_iflag; + unsigned int cflag = port->tty->termios->c_cflag; + unsigned int old_iflag = old_termios->c_iflag; + unsigned int old_cflag = old_termios->c_cflag; + unsigned char buf[32]; + int arg,ret; + int i = 0; + + +dbg( "digi_set_termios: TOP: port=%d, iflag=0x%x, old_iflag=0x%x, cflag=0x%x, old_cflag=0x%x", priv->dp_port_num, iflag, old_iflag, cflag, old_cflag ); + + /* set baud rate */ + if( (cflag&CBAUD) != (old_cflag&CBAUD) ) { + + arg = -1; + + /* reassert DTR and (maybe) RTS on transition from B0 */ + if( (old_cflag&CBAUD) == B0 ) { + /* don't set RTS if using hardware flow control */ + /* and throttling input -- not implemented yet */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + } + + switch( (cflag&CBAUD) ) { + /* drop DTR and RTS on transition to B0 */ + case B0: digi_set_modem_signals( port, 0 ); break; + case B50: arg = DIGI_BAUD_50; break; + case B75: arg = DIGI_BAUD_75; break; + case B110: arg = DIGI_BAUD_110; break; + case B150: arg = DIGI_BAUD_150; break; + case B200: arg = DIGI_BAUD_200; break; + case B300: arg = DIGI_BAUD_300; break; + case B600: arg = DIGI_BAUD_600; break; + case B1200: arg = DIGI_BAUD_1200; break; + case B1800: arg = DIGI_BAUD_1800; break; + case B2400: arg = DIGI_BAUD_2400; break; + case B4800: arg = DIGI_BAUD_4800; break; + case B9600: arg = DIGI_BAUD_9600; break; + case B19200: arg = DIGI_BAUD_19200; break; + case B38400: arg = DIGI_BAUD_38400; break; + case B57600: arg = DIGI_BAUD_57600; break; + case B115200: arg = DIGI_BAUD_115200; break; + case B230400: arg = DIGI_BAUD_230400; break; + case B460800: arg = DIGI_BAUD_460800; break; + default: + dbg( "digi_set_termios: can't handle baud rate 0x%x", + (cflag&CBAUD) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_BAUD_RATE; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set parity */ + if( (cflag&(PARENB|PARODD)) != (old_cflag&(PARENB|PARODD)) ) { + + if( (cflag&PARENB) ) { + if( (cflag&PARODD) ) + arg = DIGI_PARITY_ODD; + else + arg = DIGI_PARITY_EVEN; + } else { + arg = DIGI_PARITY_NONE; + } + + buf[i++] = DIGI_CMD_SET_PARITY; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set word size */ + if( (cflag&CSIZE) != (old_cflag&CSIZE) ) { + + arg = -1; + + switch( (cflag&CSIZE) ) { + case CS5: arg = DIGI_WORD_SIZE_5; break; + case CS6: arg = DIGI_WORD_SIZE_6; break; + case CS7: arg = DIGI_WORD_SIZE_7; break; + case CS8: arg = DIGI_WORD_SIZE_8; break; + default: + dbg( "digi_set_termios: can't handle word size %d", + (cflag&CSIZE) ); + break; + } + + if( arg != -1 ) { + buf[i++] = DIGI_CMD_SET_WORD_SIZE; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + } + + } + + /* set stop bits */ + if( (cflag&CSTOPB) != (old_cflag&CSTOPB) ) { + + if( (cflag&CSTOPB) ) + arg = DIGI_STOP_BITS_2; + else + arg = DIGI_STOP_BITS_1; + + buf[i++] = DIGI_CMD_SET_STOP_BITS; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set input flow control */ + if( (iflag&IXOFF) != (old_iflag&IXOFF) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) ) { + + arg = 0; + + if( (iflag&IXOFF) ) + arg |= DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_XON_XOFF; + + if( (cflag&CRTSCTS) ) + arg |= DIGI_INPUT_FLOW_CONTROL_RTS; + else + arg &= ~DIGI_INPUT_FLOW_CONTROL_RTS; + + buf[i++] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set output flow control */ + /*if( (iflag&IXON) != (old_iflag&IXON) + || (cflag&CRTSCTS) != (old_cflag&CRTSCTS) )*/ { + + arg = 0; + + if( (iflag&IXON) ) + arg |= DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_XON_XOFF; + + if( (cflag&CRTSCTS) ) + arg |= DIGI_OUTPUT_FLOW_CONTROL_CTS; + else + arg &= ~DIGI_OUTPUT_FLOW_CONTROL_CTS; + + buf[i++] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + + } + + /* set receive enable/disable */ + if( (cflag&CREAD) != (old_cflag&CREAD) ) { + + if( (cflag&CREAD) ) + arg = DIGI_ENABLE; + else + arg = DIGI_DISABLE; + + buf[i++] = DIGI_CMD_RECEIVE_ENABLE; + buf[i++] = priv->dp_port_num; + buf[i++] = arg; + buf[i++] = 0; + + } + + if( (ret=digi_write_oob_command( buf, i )) != 0 ) + dbg( "digi_set_termios: write oob failed, ret=%d", ret ); + +} + + +static void digi_break_ctl( struct usb_serial_port *port, int break_state ) +{ + +#ifdef DEBUG + digi_private_t *priv = (digi_private_t *)(port->private); +#endif + + +dbg( "digi_break_ctl: TOP: port=%d", priv->dp_port_num ); + +} + + +static int digi_ioctl( struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg ) +{ + + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned int val; + unsigned long flags = 0; + + +dbg( "digi_ioctl: TOP: port=%d, cmd=0x%x", priv->dp_port_num, cmd ); + + switch (cmd) { + + case TIOCMGET: + spin_lock_irqsave( &priv->dp_port_lock, flags ); + val = priv->dp_modem_signals; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + if( copy_to_user((unsigned int *)arg, &val, sizeof(int)) ) + return( -EFAULT ); + return( 0 ); + + case TIOCMSET: + case TIOCMBIS: + case TIOCMBIC: + if( copy_from_user(&val, (unsigned int *)arg, sizeof(int)) ) + return( -EFAULT ); + spin_lock_irqsave( &priv->dp_port_lock, flags ); + if( cmd == TIOCMBIS ) + val = priv->dp_modem_signals | val; + else if( cmd == TIOCMBIC ) + val = priv->dp_modem_signals & ~val; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( digi_set_modem_signals( port, val ) ); + + case TIOCMIWAIT: + /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ + /* TODO */ + return( 0 ); + + case TIOCGICOUNT: + /* return count of modemline transitions */ + /* TODO */ + return 0; + + } + + return( -ENOIOCTLCMD ); + +} + + +static int digi_write( struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count ) +{ + + int ret,data_len,new_len; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned char *data = port->write_urb->transfer_buffer; + unsigned long flags = 0; + + +dbg( "digi_write: TOP: port=%d, count=%d, from_user=%d, in_interrupt=%d", +priv->dp_port_num, count, from_user, in_interrupt() ); + + /* be sure only one write proceeds at a time */ + /* there are races on the port private buffer */ + /* and races to check write_urb->status */ + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + /* wait for urb status clear to submit another urb */ + if( port->write_urb->status == -EINPROGRESS ) { + + /* buffer data if count is 1 (probably put_char) if possible */ + if( count == 1 ) { + new_len = MIN( count, + DIGI_PORT_BUF_LEN-priv->dp_buf_len ); + memcpy( priv->dp_buf+priv->dp_buf_len, buf, new_len ); + priv->dp_buf_len += new_len; + } else { + new_len = 0; + } + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + return( new_len ); + + } + + /* allow space for any buffered data and for new data, up to */ + /* transfer buffer size - 2 (for command and length bytes) */ + new_len = MIN( count, port->bulk_out_size-2-priv->dp_buf_len ); + data_len = new_len + priv->dp_buf_len; + + if( data_len == 0 ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( 0 ); + } + + port->write_urb->transfer_buffer_length = data_len+2; + + *data++ = DIGI_CMD_SEND_DATA; + *data++ = data_len; + + /* copy in buffered data first */ + memcpy( data, priv->dp_buf, priv->dp_buf_len ); + data += priv->dp_buf_len; + + /* copy in new data */ + if( from_user ) { + if( copy_from_user( data, buf, new_len ) ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( -EFAULT ); + } + } else { + memcpy( data, buf, new_len ); + } + +#ifdef DEBUG_DATA + { + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write: port=%d, length=%d, data=", + priv->dp_port_num, port->write_urb->transfer_buffer_length ); + for( i=0; iwrite_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); + } +#endif + + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + ret = new_len; + priv->dp_buf_len = 0; + } else { + dbg( "digi_write: usb_submit_urb failed, ret=%d", + ret ); + } + + /* return length of new data written, or error */ +dbg( "digi_write: returning %d", ret ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( ret ); + +} + + +static void digi_write_bulk_callback( struct urb *urb ) +{ + + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + digi_private_t *priv = (digi_private_t *)(port->private); + int ret; + + +dbg( "digi_write_bulk_callback: TOP: port=%d", priv->dp_port_num ); + + /* handle callback on out-of-band port */ + if( priv->dp_port_num == oob_port_num ) { + dbg( "digi_write_bulk_callback: oob callback" ); + spin_lock( &priv->dp_port_lock ); + wake_up_interruptible( &port->write_wait ); + spin_unlock( &priv->dp_port_lock ); + return; + } + + /* sanity checks */ + if( port_paranoia_check( port, "digi_write_bulk_callback" ) + || serial_paranoia_check( serial, "digi_write_bulk_callback" ) ) { + return; + } + + /* try to send any buffered data on this port */ + spin_lock( &priv->dp_port_lock ); + if( port->write_urb->status != -EINPROGRESS && priv->dp_buf_len > 0 ) { + + *((unsigned char *)(port->write_urb->transfer_buffer)) + = (unsigned char)DIGI_CMD_SEND_DATA; + *((unsigned char *)(port->write_urb->transfer_buffer)+1) + = (unsigned char)priv->dp_buf_len; + + port->write_urb->transfer_buffer_length = priv->dp_buf_len+2; + + memcpy( port->write_urb->transfer_buffer+2, priv->dp_buf, + priv->dp_buf_len ); + +#ifdef DEBUG_DATA + { + int i; + + printk( KERN_DEBUG __FILE__ ": digi_write_bulk_callback: port=%d, length=%d, data=", + priv->dp_port_num, port->write_urb->transfer_buffer_length ); + for( i=0; iwrite_urb->transfer_buffer_length; ++i ) { + printk( "%.2x ", + ((unsigned char *)port->write_urb->transfer_buffer)[i] ); + } + printk( "\n" ); + } +#endif + + if( (ret=usb_submit_urb(port->write_urb)) == 0 ) { + priv->dp_buf_len = 0; + } else { + dbg( "digi_write_bulk_callback: usb_submit_urb failed, ret=%d", ret ); + } + + } + + /* wake up processes sleeping on writes immediately */ + digi_wakeup_write( port ); + + spin_unlock( &priv->dp_port_lock ); + + /* also queue up a wakeup at scheduler time, in case we */ + /* lost the race in write_chan(). */ + priv->dp_tasks.routine = (void *)digi_wakeup_write_lock; + priv->dp_tasks.data = (void *)port; + queue_task( &(priv->dp_tasks), &tq_scheduler ); + +} + + +static int digi_write_room( struct usb_serial_port *port ) +{ + + int room; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; + + +dbg( "digi_write_room: TOP: port=%d", priv->dp_port_num ); + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + + if( port->write_urb->status == -EINPROGRESS ) + room = 0; + else + room = port->bulk_out_size - 2 - priv->dp_buf_len; + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + +dbg( "digi_write_room: port=%d, room=%d", priv->dp_port_num, room ); + return( room ); + +} + + +static int digi_chars_in_buffer( struct usb_serial_port *port ) +{ + + digi_private_t *priv = (digi_private_t *)(port->private); + + +dbg( "digi_chars_in_buffer: TOP: port=%d", priv->dp_port_num ); + + if( port->write_urb->status == -EINPROGRESS ) { +dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, port->bulk_out_size - 2 ); + /* return( port->bulk_out_size - 2 ); */ + return( 256 ); + } else { +dbg( "digi_chars_in_buffer: port=%d, chars=%d", priv->dp_port_num, priv->dp_buf_len ); + return( priv->dp_buf_len ); + } + +} + + +static int digi_open( struct usb_serial_port *port, struct file *filp ) +{ + + int ret; + unsigned char buf[32]; + digi_private_t *priv = (digi_private_t *)(port->private); + struct termios not_termios; + unsigned long flags = 0; + + +dbg( "digi_open: TOP: port %d, active:%d", priv->dp_port_num, port->active ); + + /* be sure the device is started up */ + if( digi_startup_device( port->serial ) != 0 ) + return( -ENXIO ); + + /* if port is already open, just return */ + /* be sure exactly one open proceeds */ + spin_lock_irqsave( &priv->dp_port_lock, flags ); + if( port->active >= 1 && !priv->dp_in_close ) { + ++port->active; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_INC_USE_COUNT; + return( 0 ); + } + + /* don't wait on a close in progress for non-blocking opens */ + if( priv->dp_in_close && (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return( -EAGAIN ); + } + + /* prevent other opens from proceeding, before giving up lock */ + ++port->active; + + /* wait for close to finish */ + while( priv->dp_in_close ) { + cond_wait_interruptible_timeout_irqrestore( + &priv->dp_close_wait, DIGI_RETRY_TIMEOUT, + &priv->dp_port_lock, flags ); + if( signal_pending(current) ) { + --port->active; + return( -EINTR ); + } + spin_lock_irqsave( &priv->dp_port_lock, flags ); + } + + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_INC_USE_COUNT; + + /* read modem signals automatically whenever they change */ + buf[0] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[1] = priv->dp_port_num; + buf[2] = DIGI_ENABLE; + buf[3] = 0; + + /* flush fifos */ + buf[4] = DIGI_CMD_IFLUSH_FIFO; + buf[5] = priv->dp_port_num; + buf[6] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[7] = 0; + + if( (ret=digi_write_oob_command( buf, 8 )) != 0 ) + dbg( "digi_open: write oob failed, ret=%d", ret ); + + /* set termios settings */ + not_termios.c_cflag = ~port->tty->termios->c_cflag; + not_termios.c_iflag = ~port->tty->termios->c_iflag; + digi_set_termios( port, ¬_termios ); + + /* set DTR and RTS */ + digi_set_modem_signals( port, TIOCM_DTR|TIOCM_RTS ); + + return( 0 ); + +} + + +static void digi_close( struct usb_serial_port *port, struct file *filp ) +{ + + int ret; + unsigned char buf[32]; + struct tty_struct *tty = port->tty; + digi_private_t *priv = (digi_private_t *)(port->private); + unsigned long flags = 0; + + +dbg( "digi_close: TOP: port %d, active:%d", priv->dp_port_num, port->active ); + + + /* do cleanup only after final close on this port */ + spin_lock_irqsave( &priv->dp_port_lock, flags ); + if( port->active > 1 ) { + --port->active; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + MOD_DEC_USE_COUNT; + return; + } else if( port->active <= 0 ) { + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + return; + } + priv->dp_in_close = 1; + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + /* tell line discipline to process only XON/XOFF */ + tty->closing = 1; + + /* wait for output to drain */ + if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + tty_wait_until_sent( tty, DIGI_CLOSE_TIMEOUT ); + } + + /* flush driver and line discipline buffers */ + if( tty->driver.flush_buffer ) + tty->driver.flush_buffer( tty ); + if( tty->ldisc.flush_buffer ) + tty->ldisc.flush_buffer( tty ); + + /* wait for transmit idle */ + if( (filp->f_flags&(O_NDELAY|O_NONBLOCK)) == 0 ) { + digi_transmit_idle( port, DIGI_CLOSE_TIMEOUT ); + } + + /* drop DTR and RTS */ + digi_set_modem_signals( port, 0 ); + + /* disable input flow control */ + buf[0] = DIGI_CMD_SET_INPUT_FLOW_CONTROL; + buf[1] = priv->dp_port_num; + buf[2] = DIGI_DISABLE; + buf[3] = 0; + + /* disable output flow control */ + buf[4] = DIGI_CMD_SET_OUTPUT_FLOW_CONTROL; + buf[5] = priv->dp_port_num; + buf[6] = DIGI_DISABLE; + buf[7] = 0; + + /* disable reading modem signals automatically */ + buf[8] = DIGI_CMD_READ_INPUT_SIGNALS; + buf[9] = priv->dp_port_num; + buf[10] = DIGI_DISABLE; + buf[11] = 0; + + /* flush fifos */ + buf[12] = DIGI_CMD_IFLUSH_FIFO; + buf[13] = priv->dp_port_num; + buf[14] = DIGI_FLUSH_TX | DIGI_FLUSH_RX; + buf[15] = 0; + + /* disable receive */ + buf[16] = DIGI_CMD_RECEIVE_ENABLE; + buf[17] = priv->dp_port_num; + buf[18] = DIGI_DISABLE; + buf[19] = 0; + + if( (ret=digi_write_oob_command( buf, 20 )) != 0 ) + dbg( "digi_close: write oob failed, ret=%d", ret ); + + /* wait for final commands on oob port to complete */ + while( oob_port->write_urb->status == -EINPROGRESS ) { + interruptible_sleep_on_timeout( &oob_port->write_wait, + DIGI_RETRY_TIMEOUT ); + if( signal_pending(current) ) { + break; + } + } + + /* shutdown any outstanding bulk writes */ + usb_unlink_urb (port->write_urb); + + tty->closing = 0; + + spin_lock_irqsave( &priv->dp_port_lock, flags ); + --port->active; + priv->dp_in_close = 0; + wake_up_interruptible( &priv->dp_close_wait ); + spin_unlock_irqrestore( &priv->dp_port_lock, flags ); + + MOD_DEC_USE_COUNT; + +dbg( "digi_close: done" ); +} + + +/* +* Digi Startup Device +* +* Starts reads on all ports. Must be called AFTER startup, with +* urbs initialized. Returns 0 if successful, non-zero error otherwise. +*/ + +static int digi_startup_device( struct usb_serial *serial ) +{ + + int i,ret = 0; + + + spin_lock( &startup_lock ); + + /* be sure this happens exactly once */ + if( device_startup ) { + spin_unlock( &startup_lock ); + return( 0 ); + } + + /* start reading from each bulk in endpoint for the device */ + for( i=0; iport[i].read_urb)) != 0 ) { + dbg( "digi_startup_device: usb_submit_urb failed, port=%d, ret=%d", + i, ret ); + break; + } + + } + + device_startup = 1; + + spin_unlock( &startup_lock ); + + return( ret ); + +} + + +static int digi_startup( struct usb_serial *serial ) +{ + + int i; + digi_private_t *priv; + + +dbg( "digi_startup: TOP" ); + + spin_lock_init( &startup_lock ); + init_waitqueue_head( &modem_change_wait ); + init_waitqueue_head( &transmit_idle_wait ); + + /* allocate the private data structures for all ports */ + /* number of regular ports + 1 for the out-of-band port */ + for( i=0; iport[i].active = 0; + + /* allocate private structure */ + priv = serial->port[i].private = + (digi_private_t *)kmalloc( sizeof(digi_private_t), + GFP_KERNEL ); + if( priv == (digi_private_t *)0 ) + return( 1 ); /* error */ + + /* initialize private structure */ + priv->dp_port_num = i; + priv->dp_buf_len = 0; + priv->dp_modem_signals = 0; + priv->dp_transmit_idle = 0; + priv->dp_in_close = 0; + init_waitqueue_head( &priv->dp_close_wait ); + priv->dp_tasks.next = NULL; + priv->dp_tasks.data = NULL; + spin_lock_init( &priv->dp_port_lock ); + + /* initialize write wait queue for this port */ + init_waitqueue_head(&serial->port[i].write_wait); + + } + + /* initialize out of band port info */ + oob_port_num = digi_acceleport_device.num_ports; + oob_port = &serial->port[oob_port_num]; + device_startup = 0; + + return( 0 ); + +} + + +static void digi_shutdown( struct usb_serial *serial ) +{ + + int i; + + +dbg( "digi_shutdown: TOP" ); + + /* stop reads and writes on all ports */ + for( i=0; iport[i].read_urb); + usb_unlink_urb (serial->port[i].write_urb); + } + + device_startup = 0; + + /* free the private data structures for all ports */ + /* number of regular ports + 1 for the out-of-band port */ + for( i=0; iport[i].private ); + +} + + +static void digi_read_bulk_callback( struct urb *urb ) +{ + + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + struct tty_struct *tty = port->tty; + digi_private_t *priv = (digi_private_t *)(port->private); + int opcode = ((unsigned char *)urb->transfer_buffer)[0]; + int len = ((unsigned char *)urb->transfer_buffer)[1]; + int status = ((unsigned char *)urb->transfer_buffer)[2]; + unsigned char *data = ((unsigned char *)urb->transfer_buffer)+3; + int ret,i; + + +dbg( "digi_read_bulk_callback: TOP: port=%d", priv->dp_port_num ); + + /* handle oob callback */ + if( priv->dp_port_num == oob_port_num ) { + digi_read_oob_callback( urb ); + return; + } + + /* sanity checks */ + if( port_paranoia_check( port, "digi_read_bulk_callback" ) + || serial_paranoia_check( serial, "digi_read_bulk_callback" ) ) { + goto resubmit; + } + + if( urb->status ) { + dbg( "digi_read_bulk_callback: nonzero read bulk status: %d", + urb->status ); + if( urb->status == -ENOENT ) + return; + goto resubmit; + } + +#ifdef DEBUG_DATA +if( urb->actual_length ) { + printk( KERN_DEBUG __FILE__ ": digi_read_bulk_callback: port=%d, length=%d, data=", + priv->dp_port_num, urb->actual_length ); + for( i=0; iactual_length; ++i ) { + printk( "%.2x ", ((unsigned char *)urb->transfer_buffer)[i] ); + } + printk( "\n" ); +} +#endif + + if( urb->actual_length != len + 2 ) + err( KERN_INFO "digi_read_bulk_callback: INCOMPLETE PACKET, port=%d, opcode=%d, len=%d, actual_length=%d, status=%d", priv->dp_port_num, opcode, len, urb->actual_length, status ); + + /* receive data */ + if( opcode == DIGI_CMD_RECEIVE_DATA && urb->actual_length > 3 ) { + len = MIN( len, urb->actual_length-3 ); + for( i=0; icontext; + struct usb_serial *serial = port->serial; + digi_private_t *priv; + int opcode, line, status, val; + int i,ret; + + +dbg( "digi_read_oob_callback: len=%d", urb->actual_length ); + + if( urb->status ) { + dbg( "digi_read_oob_callback: nonzero read bulk status on oob: %d", + urb->status ); + if( urb->status == -ENOENT ) + return; + goto resubmit; + } + + for( i=0; iactual_length-3; ) { + + opcode = ((unsigned char *)urb->transfer_buffer)[i++]; + line = ((unsigned char *)urb->transfer_buffer)[i++]; + status = ((unsigned char *)urb->transfer_buffer)[i++]; + val = ((unsigned char *)urb->transfer_buffer)[i++]; + +dbg( "digi_read_oob_callback: opcode=%d, line=%d, status=%d, val=%d", opcode, line, status, val ); + + if( status != 0 ) + continue; + + priv = serial->port[line].private; + + if( opcode == DIGI_CMD_READ_INPUT_SIGNALS ) { + + spin_lock( &priv->dp_port_lock ); + + /* convert from digi flags to termiox flags */ + if( val & DIGI_READ_INPUT_SIGNALS_CTS ) + priv->dp_modem_signals |= TIOCM_CTS; + else + priv->dp_modem_signals &= ~TIOCM_CTS; + if( val & DIGI_READ_INPUT_SIGNALS_DSR ) + priv->dp_modem_signals |= TIOCM_DSR; + else + priv->dp_modem_signals &= ~TIOCM_DSR; + if( val & DIGI_READ_INPUT_SIGNALS_RI ) + priv->dp_modem_signals |= TIOCM_RI; + else + priv->dp_modem_signals &= ~TIOCM_RI; + if( val & DIGI_READ_INPUT_SIGNALS_DCD ) + priv->dp_modem_signals |= TIOCM_CD; + else + priv->dp_modem_signals &= ~TIOCM_CD; + + wake_up_interruptible( &modem_change_wait ); + spin_unlock( &priv->dp_port_lock ); + + } else if( opcode == DIGI_CMD_TRANSMIT_IDLE ) { + + spin_lock( &priv->dp_port_lock ); + priv->dp_transmit_idle = 1; + wake_up_interruptible( &transmit_idle_wait ); + spin_unlock( &priv->dp_port_lock ); + + } + + } + + +resubmit: + if( (ret=usb_submit_urb(urb)) != 0 ) { + dbg( "digi_read_oob_callback: failed resubmitting oob urb, ret=%d", + ret ); + } + +} diff --git a/drivers/usb/serial/ezusb_convert.pl b/drivers/usb/serial/ezusb_convert.pl new file mode 100644 index 000000000000..13f11469116e --- /dev/null +++ b/drivers/usb/serial/ezusb_convert.pl @@ -0,0 +1,50 @@ +#! /usr/bin/perl -w + + +# convert an Intel HEX file into a set of C records usable by the firmware +# loading code in usb-serial.c (or others) + +# accepts the .hex file(s) on stdin, a basename (to name the initialized +# array) as an argument, and prints the .h file to stdout. Typical usage: +# perl ezusb_convert.pl foo fw_foo.h + + +my $basename = $ARGV[0]; +die "no base name specified" unless $basename; + +while () { + # ':' '\r' + # len, type, crc are 2-char hex, addr is 4-char hex. type is 00 for + # normal records, 01 for EOF + my($lenstring, $addrstring, $typestring, $reststring, $doscrap) = + /^:(\w\w)(\w\w\w\w)(\w\w)(\w+)(\r?)$/; + die "malformed line: $_" unless $reststring; + last if $typestring eq '01'; + my($len) = hex($lenstring); + my($addr) = hex($addrstring); + my(@bytes) = unpack("C*", pack("H".(2*$len), $reststring)); + #pop(@bytes); # last byte is a CRC + push(@records, [$addr, \@bytes]); +} + +@sorted_records = sort { $a->[0] <=> $b->[0] } @records; + +print <<"EOF"; +/* + * ${basename}_fw.h + * + * Generated from ${basename}.s by ezusb_convert.pl + * This file is presumed to be under the same copyright as the source file + * from which it was derived. + */ + +EOF + +print "static const struct ezusb_hex_record ${basename}_firmware[] = {\n"; +foreach $r (@sorted_records) { + printf("{ 0x%04x,\t%d,\t{", $r->[0], scalar(@{$r->[1]})); + print join(", ", map {sprintf('0x%02x', $_);} @{$r->[1]}); + print "} },\n"; +} +print "{ 0xffff,\t0,\t{0x00} }\n"; +print "};\n"; diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c new file mode 100644 index 000000000000..86f8e00f60c9 --- /dev/null +++ b/drivers/usb/serial/ftdi_sio.c @@ -0,0 +1,723 @@ +/* + * USB FTDI SIO driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * Bill Ryder (bryder@sgi.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (04/04/2000) Bill Ryder + * Fixed bugs in TCGET/TCSET ioctls (by removing them - they are + * handled elsewhere in the serial driver chain). + * + * (03/30/2000) Bill Ryder + * Implemented lots of ioctls + * Fixed a race condition in write + * Changed some dbg's to errs + * + * (03/26/2000) gkh + * Split driver up into device specific pieces. + * + */ + +/* Bill Ryder - bryder@sgi.com - wrote the FTDI_SIO implementation */ +/* Thanx to FTDI for so kindly providing details of the protocol required */ +/* to talk to the device */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +#include "usb-serial.h" + +#include "ftdi_sio.h" + +#define FTDI_VENDOR_ID 0x0403 +#define FTDI_SIO_SERIAL_CONVERTER_ID 0x8372 + +/* function prototypes for a FTDI serial converter */ +static int ftdi_sio_startup (struct usb_serial *serial); +static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp); +static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp); +static int ftdi_sio_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static void ftdi_sio_write_bulk_callback (struct urb *urb); +static void ftdi_sio_read_bulk_callback (struct urb *urb); +static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios * old); +static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); + +/* All of the device info needed for the FTDI SIO serial converter */ +static __u16 ftdi_vendor_id = FTDI_VENDOR_ID; +static __u16 ftdi_sio_product_id = FTDI_SIO_SERIAL_CONVERTER_ID; +struct usb_serial_device_type ftdi_sio_device = { + name: "FTDI SIO", + idVendor: &ftdi_vendor_id, /* the FTDI vendor ID */ + idProduct: &ftdi_sio_product_id, /* the FTDI SIO product id */ + needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 0, + num_bulk_in: 1, + num_bulk_out: 1, + num_ports: 1, + open: ftdi_sio_open, + close: ftdi_sio_close, + write: ftdi_sio_write, + read_bulk_callback: ftdi_sio_read_bulk_callback, + write_bulk_callback: ftdi_sio_write_bulk_callback, + ioctl: ftdi_sio_ioctl, + set_termios: ftdi_sio_set_termios, + startup: ftdi_sio_startup, +}; + + +/* + * *************************************************************************** + * FTDI SIO Serial Converter specific driver functions + * *************************************************************************** + * + * Bill Ryder bryder@sgi.com of Silicon Graphics, Inc. did the FTDI_SIO code + * Thanx to FTDI for so kindly providing details of the protocol required + * to talk to the device - http://www.ftdi.co.uk + * + * Tested as at this version - other stuff might work + * 23 March 2000 + * Works: + * Baudrates - 9600, 38400,19200, 57600, 115200 + * TIOCMBIC - TIOCM_DTR / TIOCM_RTS + * TIOCMBIS - TIOCM_DTR / TIOCM_RTS + * TIOCMSET - DTR on/RTSon / DTR off, RTS off + * no parity:CS8 even parity:CS7 odd parity:CS7 + * CRTSCTS flow control + * + * Pilot-xfer zillions of times + * + * cu works with dir option + * + * Not Tested (ie might not work): + * xon/xoff flow control + * ppp (modem handling in general) + * + * KNOWN BUGS: + * Multiple Opens + * ============== + * Seems to have problem when opening an already open port, + * Get I/O error on first attempt, then it lets you in. + * Need to do proper usage counting - keep registered callbacks for first opener. + * + * Reproduce with: + * cu -l /dev/ttyUSB0 dir + * whilst cu is running do: + * stty -a < /dev/ttyUSB0 + * + * from stty get: 'bash: /dev/ttyUSB0: Invalid argument ' + * from cu get + * write: Invalid argument + * + * Initialisation Problem + * ====================== + * Pilot transfer required me to run the serial_loopback program before it would work. + * Still working on this. See the webpage http://reality.sgi.com/bryder_wellington/ftdi_sio + * + */ + +#define WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */ + +/* do some startup allocations not currently performed by usb_serial_probe() */ +static int ftdi_sio_startup (struct usb_serial *serial) +{ + init_waitqueue_head(&serial->port[0].write_wait); + + return (0); +} + +static int ftdi_sio_open (struct usb_serial_port *port, struct file *filp) +{ /* ftdi_sio_open */ + struct termios tmp_termios; + struct usb_serial *serial = port->serial; + char buf[1]; /* Needed for the usb_control_msg I think */ + + dbg("ftdi_sio_open port %d", port->number); + + /* FIXME - multiple concurrent opens cause trouble */ + if (port->active) { + err ("port already open"); + return -EINVAL; + } + port->active = 1; /* FIXME - For multiple open this should increment */ + + /* See ftdi_sio.h for description of what is reset */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_RESET_REQUEST, FTDI_SIO_RESET_REQUEST_TYPE, + FTDI_SIO_RESET_SIO, + 0, buf, 0, WDR_TIMEOUT); + + /* Setup termios */ + port->tty->termios->c_cflag = + B9600 | CS8 | CREAD | HUPCL | CLOCAL; + + + ftdi_sio_set_termios(port, &tmp_termios); + + /* Disable flow control */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("error from flowcontrol urb"); + return(-EINVAL); + } + + /* Turn on RTS and DTR since we are not flow controlling*/ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_DTR_HIGH, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from DTR HIGH urb"); + } + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_RTS_HIGH, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from RTS HIGH urb"); + } + + /*Start reading from the device*/ + if (usb_submit_urb(port->read_urb)) + err("usb_submit_urb(read bulk) failed"); + + return (0); +} /* ftdi_sio_open */ + + +static void ftdi_sio_close (struct usb_serial_port *port, struct file *filp) +{ /* ftdi_sio_close */ + struct usb_serial *serial = port->serial; + unsigned int c_cflag = port->tty->termios->c_cflag; + char buf[1]; + + dbg("ftdi_sio_close port %d", port->number); + + if (c_cflag & HUPCL){ + /* Disable flow control */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("error from flowcontrol urb"); + } + + /* drop DTR */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_DTR_LOW, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from DTR LOW urb"); + } + /* drop RTS */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_RTS_LOW, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from RTS LOW urb"); + } + } + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + port->active = 0; +} /* ftdi_sio_close */ + + + +/* The ftdi_sio requires the first byte to have: + * B0 1 + * B1 0 + * B2..7 length of message excluding byte 0 + */ +static int ftdi_sio_write (struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count) +{ /* ftdi_sio_write */ + struct usb_serial *serial = port->serial; + const int data_offset = 1; + int rc; + DECLARE_WAITQUEUE(wait, current); + + dbg("ftdi_sio_serial_write port %d, %d bytes", port->number, count); + + if (count == 0) { + err("write request of 0 bytes"); + return 0; + } + + /* only do something if we have a bulk out endpoint */ + if (serial->num_bulk_out) { + unsigned char *first_byte = port->write_urb->transfer_buffer; + + /* Was seeing a race here, got a read callback, then write callback before + hitting interuptible_sleep_on - so wrapping in add_wait_queue stuff */ + + add_wait_queue(&port->write_wait, &wait); + set_current_state (TASK_INTERRUPTIBLE); + while (port->write_urb->status == -EINPROGRESS) { + dbg("ftdi_sio - write in progress - retrying"); + if (0 /* file->f_flags & O_NONBLOCK */) { + rc = -EAGAIN; + goto err; + } + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&port->write_wait, &wait); + rc = -ERESTARTSYS; + goto err; + } + schedule(); + } + remove_wait_queue(&port->write_wait, &wait); + set_current_state(TASK_RUNNING); + + count += data_offset; + count = (count > port->bulk_out_size) ? port->bulk_out_size : count; + if (count == 0) { + return 0; + } + + /* Copy in the data to send */ + if (from_user) { + copy_from_user(port->write_urb->transfer_buffer + data_offset , + buf, count - data_offset ); + } + else { + memcpy(port->write_urb->transfer_buffer + data_offset, + buf, count - data_offset ); + } + + /* Write the control byte at the front of the packet*/ + first_byte = port->write_urb->transfer_buffer; + *first_byte = 1 | ((count-data_offset) << 2) ; + +#ifdef CONFIG_USB_SERIAL_DEBUG + dbg("Bytes: %d, Control Byte: 0o%03o",count, first_byte[0]); + + if (count) { + int i; + printk (KERN_DEBUG __FILE__ ": data written - length = %d, data = ", count); + for (i = 0; i < count; ++i) { + printk ( "0x%02x ", first_byte[i]); + if (first_byte[i] > ' ' && first_byte[i] < '~') { + printk( "%c ", first_byte[i]); + } else { + printk( " "); + } + } + + + printk ( "\n"); + } + +#endif + /* send the data out the bulk port */ + port->write_urb->transfer_buffer_length = count; + + if (usb_submit_urb(port->write_urb)) + err("usb_submit_urb(write bulk) failed"); + + dbg("write returning: %d", count - data_offset); + return (count - data_offset); + } + + /* no bulk out, so return 0 bytes written */ + return 0; + err: /* error exit */ + return(rc); +} /* ftdi_sio_write */ + +static void ftdi_sio_write_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial; + struct tty_struct *tty = port->tty; + + dbg("ftdi_sio_write_bulk_callback"); + + if (port_paranoia_check (port, "ftdi_sio_write_bulk_callback")) { + return; + } + + serial = port->serial; + if (serial_paranoia_check (serial, "ftdi_sio_write_bulk_callback")) { + return; + } + + if (urb->status) { + dbg("nonzero write bulk status received: %d", urb->status); + return; + } + + wake_up_interruptible(&port->write_wait); + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + return; +} /* ftdi_sio_write_bulk_callback */ + +static void ftdi_sio_read_bulk_callback (struct urb *urb) +{ /* ftdi_sio_serial_buld_callback */ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial; + struct tty_struct *tty = port->tty ; + unsigned char *data = urb->transfer_buffer; + + const int data_offset = 2; + int i; + + dbg("ftdi_sio read callback"); + + if (port_paranoia_check (port, "ftdi_sio_read_bulk_callback")) { + return; + } + + serial = port->serial; + if (serial_paranoia_check (serial, "ftdi_sio_read_bulk_callback")) { + return; + } + + /* TO DO -- check for hung up line and handle appropriately: */ + /* send hangup (need to find out how to do this) */ + + + if (urb->status) { + /* This will happen at close every time so it is a dbg not an err */ + dbg("nonzero read bulk status received: %d", urb->status); + return; + } + +#ifdef CONFIG_USB_SERIAL_DEBUG + if (urb->actual_length > 2) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ( "0x%.2x ", data[i]); + if (data[i] > ' ' && data[i] < '~') { + printk( "%c ", data[i]); + } else { + printk( " "); + } + } + printk ( "\n"); + } else { + dbg("Just status"); + } +#endif + + + if (urb->actual_length > data_offset) { + for (i = data_offset ; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Continue trying to always read */ + if (usb_submit_urb(urb)) + err("failed resubmitting read urb"); + + return; +} /* ftdi_sio_serial_read_bulk_callback */ + +/* As I understand this - old_termios contains the original termios settings */ +/* and tty->termios contains the new setting to be used */ +/* */ +/* WARNING: set_termios calls this with old_termios in kernel space */ + +static void ftdi_sio_set_termios (struct usb_serial_port *port, struct termios *old_termios) +{ /* ftdi_sio_set_termios */ + struct usb_serial *serial = port->serial; + unsigned int cflag = port->tty->termios->c_cflag; + __u16 urb_value; /* Will hold the new flags */ + char buf[1]; /* Perhaps I should dynamically alloc this? */ + + dbg("ftdi_sio_set_termios port %d", port->number); + + + /* FIXME -For this cut I don't care if the line is really changing or + not - so just do the change regardless - should be able to + compare old_termios and tty->termios */ + /* NOTE These routines can get interrupted by + ftdi_sio_read_bulk_callback - need to examine what this + means - don't see any problems yet */ + + /* Set number of data bits, parity, stop bits */ + + urb_value = 0; + urb_value |= (cflag & CSTOPB ? FTDI_SIO_SET_DATA_STOP_BITS_2 : + FTDI_SIO_SET_DATA_STOP_BITS_1); + urb_value |= (cflag & PARENB ? + (cflag & PARODD ? FTDI_SIO_SET_DATA_PARITY_ODD : + FTDI_SIO_SET_DATA_PARITY_EVEN) : + FTDI_SIO_SET_DATA_PARITY_NONE); + if (cflag & CSIZE) { + switch (cflag & CSIZE) { + case CS5: urb_value |= 5; dbg("Setting CS5"); break; + case CS6: urb_value |= 6; dbg("Setting CS6"); break; + case CS7: urb_value |= 7; dbg("Setting CS7"); break; + case CS8: urb_value |= 8; dbg("Setting CS8"); break; + default: + err("CSIZE was set but not CS5-CS8"); + } + } + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_DATA_REQUEST, + FTDI_SIO_SET_DATA_REQUEST_TYPE, + urb_value , 0, + buf, 0, 100) < 0) { + err("FAILED to set databits/stopbits/parity"); + } + + /* Now do the baudrate */ + + switch(cflag & CBAUD){ + case B0: break; /* Handled below */ + case B300: urb_value = ftdi_sio_b300; dbg("Set to 300"); break; + case B600: urb_value = ftdi_sio_b600; dbg("Set to 600") ; break; + case B1200: urb_value = ftdi_sio_b1200; dbg("Set to 1200") ; break; + case B2400: urb_value = ftdi_sio_b2400; dbg("Set to 2400") ; break; + case B4800: urb_value = ftdi_sio_b4800; dbg("Set to 4800") ; break; + case B9600: urb_value = ftdi_sio_b9600; dbg("Set to 9600") ; break; + case B19200: urb_value = ftdi_sio_b19200; dbg("Set to 19200") ; break; + case B38400: urb_value = ftdi_sio_b38400; dbg("Set to 38400") ; break; + case B57600: urb_value = ftdi_sio_b57600; dbg("Set to 57600") ; break; + case B115200: urb_value = ftdi_sio_b115200; dbg("Set to 115200") ; break; + default: dbg("FTDI_SIO does not support the baudrate requested"); + /* FIXME - how to return an error for this? */ break; + } + if ((cflag & CBAUD) == B0 ) { + /* Disable flow control */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("error from disable flowcontrol urb"); + } + /* Drop RTS and DTR */ + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_DTR_LOW, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from DTR LOW urb"); + } + if (usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + (unsigned)FTDI_SIO_SET_RTS_LOW, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("Error from RTS LOW urb"); + } + + } else { + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_BAUDRATE_REQUEST, + FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE, + urb_value, 0, + buf, 0, 100) < 0) { + err("urb failed to set baurdrate"); + } + } + /* Set flow control */ + /* Note device also supports DTR/CD (ugh) and Xon/Xoff in hardware */ + if (cflag & CRTSCTS) { + dbg("Setting to CRTSCTS flow control"); + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0 , FTDI_SIO_RTS_CTS_HS, + buf, 0, WDR_TIMEOUT) < 0) { + err("urb failed to set to rts/cts flow control"); + } + + } else { + /* CHECK Assuming XON/XOFF handled by stack - not by device */ + /* Disable flow control */ + dbg("Turning off hardware flow control"); + if (usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_FLOW_CTRL_REQUEST, + FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE, + 0, 0, + buf, 0, WDR_TIMEOUT) < 0) { + err("urb failed to clear flow control"); + } + + } + return; +} /* ftdi_sio_set_termios */ + +static int ftdi_sio_ioctl (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct usb_serial *serial = port->serial; + __u16 urb_value=0; /* Will hold the new flags */ + char buf[1]; + int ret, mask; + + dbg("ftdi_sio_ioctl - cmd 0x%04x", cmd); + + /* Based on code from acm.c and others */ + switch (cmd) { + + case TIOCMGET: + dbg("TIOCMGET"); + /* Request the status from the device */ + if ((ret = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + FTDI_SIO_GET_MODEM_STATUS_REQUEST, + FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE, + 0, 0, + buf, 1, HZ * 5)) < 0 ) { + dbg("Get not get modem status of device"); + return(ret); + } + + return put_user((buf[0] & FTDI_SIO_DSR_MASK ? TIOCM_DSR : 0) | + (buf[0] & FTDI_SIO_CTS_MASK ? TIOCM_CTS : 0) | + (buf[0] & FTDI_SIO_RI_MASK ? TIOCM_RI : 0) | + (buf[0] & FTDI_SIO_RLSD_MASK ? TIOCM_CD : 0), + (unsigned long *) arg); + break; + + case TIOCMSET: /* Turns on and off the lines as specified by the mask */ + dbg("TIOCMSET"); + if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + urb_value = ((mask & TIOCM_DTR) ? FTDI_SIO_SET_DTR_HIGH : FTDI_SIO_SET_DTR_LOW); + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + urb_value , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to set DTR failed"); + return(ret); + } + urb_value = ((mask & TIOCM_RTS) ? FTDI_SIO_SET_RTS_HIGH : FTDI_SIO_SET_RTS_LOW); + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + urb_value , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to set RTS failed"); + return(ret); + } + break; + + case TIOCMBIS: /* turns on (Sets) the lines as specified by the mask */ + dbg("TIOCMBIS"); + if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + if (mask & TIOCM_DTR){ + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + FTDI_SIO_SET_DTR_HIGH , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to set DTR failed"); + return(ret); + } + } + if (mask & TIOCM_RTS) { + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + FTDI_SIO_SET_RTS_HIGH , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to set RTS failed"); + return(ret); + } + } + break; + + case TIOCMBIC: /* turns off (Clears) the lines as specified by the mask */ + dbg("TIOCMBIC"); + if ((ret = get_user(mask, (unsigned long *) arg))) return ret; + if (mask & TIOCM_DTR){ + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + FTDI_SIO_SET_DTR_LOW , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to unset DTR failed"); + return(ret); + } + } + if (mask & TIOCM_RTS) { + if ((ret = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + FTDI_SIO_SET_MODEM_CTRL_REQUEST, + FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE, + FTDI_SIO_SET_RTS_LOW , 0, + buf, 0, WDR_TIMEOUT)) < 0){ + err("Urb to unset RTS failed"); + return(ret); + } + } + break; + + /* + * I had originally implemented TCSET{A,S}{,F,W} and + * TCGET{A,S} here separately, however when testing I + * found that the higher layers actually do the termios + * conversions themselves and pass the call onto + * ftdi_sio_set_termios. + * + */ + + default: + /* This is not an error - turns out the higher layers will do + * some ioctls itself (see comment above) + */ + dbg("ftdi_sio ioctl arg not supported - it was 0x%04x",cmd); + return(-ENOIOCTLCMD); + break; + } + dbg("ftdi_sio_ioctl returning 0"); + return 0; +} /* ftdi_sio_ioctl */ + diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h new file mode 100644 index 000000000000..fe5f545db327 --- /dev/null +++ b/drivers/usb/serial/ftdi_sio.h @@ -0,0 +1,413 @@ +/* + * Definitions for the FTDI USB Single Port Serial Converter - + * known as FTDI_SIO (Serial Input/Output application of the chipset) + * + * The example I have is known as the USC-1000 which is available from + * http://www.dse.co.nz - cat no XH4214 It looks similar to this: + * http://www.dansdata.com/usbser.htm but I can't be sure There are other + * USC-1000s which don't look like my device though so beware! + * + * The device is based on the FTDI FT8U100AX chip. It has a DB25 on one side, + * USB on the other. + * + * Thanx to FTDI (http://www.ftdi.co.uk) for so kindly providing details + * of the protocol required to talk to the device and ongoing assistence + * during development. + * + * Bill Ryder - bryder@sgi.com of Silicon Graphics, Inc.- wrote the + * FTDI_SIO implementation. + * + */ + +#define FTDI_VID 0x0403 /* Vendor Id */ +#define FTDI_SIO_PID 0x8372 /* Product Id */ + +/* Vendor Request Interface */ +#define FTDI_SIO_RESET 0 /* Reset the port */ +#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */ +#define FTDI_SIO_SET_FLOW_CTRL 2 /* Set flow control register */ +#define FTDI_SIO_SET_BAUD_RATE 3 /* Set baud rate */ +#define FTDI_SIO_SET_DATA 4 /* Set the data characteristics of the port */ +#define FTDI_SIO_GET_MODEM_STATUS 5 /* Retrieve current value of modern status register */ +#define FTDI_SIO_SET_EVENT_CHAR 6 /* Set the event character */ +#define FTDI_SIO_SET_ERROR_CHAR 7 /* Set the error character */ + +/* Port Identifier Table */ +#define PIT_DEFAULT 0 /* SIOA */ +#define PIT_SIOA 1 /* SIOA */ +/* The device this driver is tested with one has only one port */ +#define PIT_SIOB 2 /* SIOB */ +#define PIT_PARALLEL 3 /* Parallel */ + +/* FTDI_SIO_RESET */ +#define FTDI_SIO_RESET_REQUEST FTDI_SIO_RESET +#define FTDI_SIO_RESET_REQUEST_TYPE 0x40 +#define FTDI_SIO_RESET_SIO 0 +#define FTDI_SIO_RESET_PURGE_RX 1 +#define FTDI_SIO_RESET_PURGE_TX 2 + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_RESET + * wValue: Control Value + * 0 = Reset SIO + * 1 = Purge RX buffer + * 2 = Purge TX buffer + * wIndex: Port + * wLength: 0 + * Data: None + * + * The Reset SIO command has this effect: + * + * Sets flow control set to 'none' + * Event char = $0D + * Event trigger = disabled + * Purge RX buffer + * Purge TX buffer + * Clear DTR + * Clear RTS + * baud and data format not reset + * + * The Purge RX and TX buffer commands affect nothing except the buffers + * + */ + +/* FTDI_SIO_SET_BAUDRATE */ +#define FTDI_SIO_SET_BAUDRATE_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_BAUDRATE_REQUEST 3 + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_SET_BAUDRATE + * wValue: BaudRate value - see below + * wIndex: Port + * wLength: 0 + * Data: None + */ + +typedef enum { + ftdi_sio_b300 = 0, + ftdi_sio_b600 = 1, + ftdi_sio_b1200 = 2, + ftdi_sio_b2400 = 3, + ftdi_sio_b4800 = 4, + ftdi_sio_b9600 = 5, + ftdi_sio_b19200 = 6, + ftdi_sio_b38400 = 7, + ftdi_sio_b57600 = 8, + ftdi_sio_b115200 = 9 +} FTDI_SIO_baudrate_t ; + +#define FTDI_SIO_SET_DATA_REQUEST FTDI_SIO_SET_DATA +#define FTDI_SIO_SET_DATA_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_DATA_PARITY_NONE (0x0 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_ODD (0x1 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_EVEN (0x2 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_MARK (0x3 << 8 ) +#define FTDI_SIO_SET_DATA_PARITY_SPACE (0x4 << 8 ) +#define FTDI_SIO_SET_DATA_STOP_BITS_1 (0x0 << 11 ) +#define FTDI_SIO_SET_DATA_STOP_BITS_15 (0x1 << 11 ) +#define FTDI_SIO_SET_DATA_STOP_BITS_2 (0x2 << 11 ) + +/* FTDI_SIO_SET_DATA */ + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_SET_DATA + * wValue: Data characteristics (see below) + * wIndex: Port + * wLength: 0 + * Data: No + * + * Data characteristics + * + * B0..7 Number of data bits + * B8..10 Parity + * 0 = None + * 1 = Odd + * 2 = Even + * 3 = Mark + * 4 = Space + * B11..13 Stop Bits + * 0 = 1 + * 1 = 1.5 + * 2 = 2 + * B14..15 Reserved + * + */ + + + +/* FTDI_SIO_MODEM_CTRL */ +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_MODEM_CTRL_REQUEST FTDI_SIO_MODEM_CTRL + +/* + * BmRequestType: 0100 0000B + * bRequest: FTDI_SIO_MODEM_CTRL + * wValue: ControlValue (see below) + * wIndex: Port + * wLength: 0 + * Data: None + * + * NOTE: If the device is in RTS/CTS flow control, the RTS set by this + * command will be IGNORED without an error being returned + * Also - you can not set DTR and RTS with one control message + */ + +#define FTDI_SIO_SET_DTR_MASK 0x1 +#define FTDI_SIO_SET_DTR_HIGH ( 1 | ( FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_DTR_LOW ( 0 | ( FTDI_SIO_SET_DTR_MASK << 8)) +#define FTDI_SIO_SET_RTS_MASK 0x2 +#define FTDI_SIO_SET_RTS_HIGH ( 2 | ( FTDI_SIO_SET_RTS_MASK << 8 )) +#define FTDI_SIO_SET_RTS_LOW ( 0 | ( FTDI_SIO_SET_RTS_MASK << 8 )) + +/* + * ControlValue + * B0 DTR state + * 0 = reset + * 1 = set + * B1 RTS state + * 0 = reset + * 1 = set + * B2..7 Reserved + * B8 DTR state enable + * 0 = ignore + * 1 = use DTR state + * B9 RTS state enable + * 0 = ignore + * 1 = use RTS state + * B10..15 Reserved + */ + +/* FTDI_SIO_SET_FLOW_CTRL */ +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST_TYPE 0x40 +#define FTDI_SIO_SET_FLOW_CTRL_REQUEST FTDI_SIO_SET_FLOW_CTRL +#define FTDI_SIO_DISABLE_FLOW_CTRL 0x0 +#define FTDI_SIO_RTS_CTS_HS (0x1 << 8) +#define FTDI_SIO_DTR_DSR_HS (0x2 << 8) +#define FTDI_SIO_XON_XOFF_HS (0x4 << 8) +/* + * BmRequestType: 0100 0000b + * bRequest: FTDI_SIO_SET_FLOW_CTRL + * wValue: Xoff/Xon + * wIndex: Protocol/Port - hIndex is protocl / lIndex is port + * wLength: 0 + * Data: None + * + * hIndex protocol is: + * B0 Output handshaking using RTS/CTS + * 0 = disabled + * 1 = enabled + * B1 Output handshaking using DTR/DSR + * 0 = disabled + * 1 = enabled + * B2 Xon/Xoff handshaking + * 0 = disabled + * 1 = enabled + * + * A value of zero in the hIndex field disables handshaking + * + * If Xon/Xoff handshaking is specified, the hValue field should contain the XOFF character + * and the lValue field contains the XON character. + */ + +/* + * FTDI_SIO_SET_EVENT_CHAR + * + * Set the special event character for the specified communications port. + * If the device sees this character it will immediately return the + * data read so far - rather than wait 40ms or until 62 bytes are read + * which is what normally happens. + */ + + +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST FTDI_SIO_SET_EVENT_CHAR +#define FTDI_SIO_SET_EVENT_CHAR_REQUEST_TYPE 0x40 + + +/* + * BmRequestType: 0100 0000b + * bRequest: FTDI_SIO_SET_EVENT_CHAR + * wValue: EventChar + * wIndex: Port + * wLength: 0 + * Data: None + * + * wValue: + * B0..7 Event Character + * B8 Event Character Processing + * 0 = disabled + * 1 = enabled + * B9..15 Reserved + * + */ + +/* FTDI_SIO_SET_ERROR_CHAR */ + +/* Set the parity error replacement character for the specified communications port */ + +/* + * BmRequestType: 0100 0000b + * bRequest: FTDI_SIO_SET_EVENT_CHAR + * wValue: Error Char + * wIndex: Port + * wLength: 0 + * Data: None + * + *Error Char + * B0..7 Error Character + * B8 Error Character Processing + * 0 = disabled + * 1 = enabled + * B9..15 Reserved + * + */ + +/* FTDI_SIO_GET_MODEM_STATUS */ +/* Retreive the current value of the modem status register */ + +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST_TYPE 0xc0 +#define FTDI_SIO_GET_MODEM_STATUS_REQUEST FTDI_SIO_GET_MODEM_STATUS +#define FTDI_SIO_CTS_MASK 0x10 +#define FTDI_SIO_DSR_MASK 0x20 +#define FTDI_SIO_RI_MASK 0x40 +#define FTDI_SIO_RLSD_MASK 0x80 +/* + * BmRequestType: 1100 0000b + * bRequest: FTDI_SIO_GET_MODEM_STATUS + * wValue: zero + * wIndex: Port + * wLength: 1 + * Data: Status + * + * One byte of data is returned + * B0..3 0 + * B4 CTS + * 0 = inactive + * 1 = active + * B5 DSR + * 0 = inactive + * 1 = active + * B6 Ring Indicator (RI) + * 0 = inactive + * 1 = active + * B7 Receive Line Signal Detect (RLSD) + * 0 = inactive + * 1 = active + */ + + + +/* Descriptors returned by the device + * + * Device Descriptor + * + * Offset Field Size Value Description + * 0 bLength 1 0x12 Size of descriptor in bytes + * 1 bDescriptorType 1 0x01 DEVICE Descriptor Type + * 2 bcdUSB 2 0x0110 USB Spec Release Number + * 4 bDeviceClass 1 0x00 Class Code + * 5 bDeviceSubClass 1 0x00 SubClass Code + * 6 bDeviceProtocol 1 0x00 Protocol Code + * 7 bMaxPacketSize0 1 0x08 Maximum packet size for endpoint 0 + * 8 idVendor 2 0x0403 Vendor ID + * 10 idProduct 2 0x8372 Product ID (FTDI_SIO_PID) + * 12 bcdDevice 2 0x0001 Device release number + * 14 iManufacturer 1 0x01 Index of man. string desc + * 15 iProduct 1 0x02 Index of prod string desc + * 16 iSerialNumber 1 0x02 Index of serial nmr string desc + * 17 bNumConfigurations 1 0x01 Number of possible configurations + * + * Configuration Descriptor + * + * Offset Field Size Value + * 0 bLength 1 0x09 Size of descriptor in bytes + * 1 bDescriptorType 1 0x02 CONFIGURATION Descriptor Type + * 2 wTotalLength 2 0x0020 Total length of data + * 4 bNumInterfaces 1 0x01 Number of interfaces supported + * 5 bConfigurationValue 1 0x01 Argument for SetCOnfiguration() req + * 6 iConfiguration 1 0x02 Index of config string descriptor + * 7 bmAttributes 1 0x20 Config characteristics Remote Wakeup + * 8 MaxPower 1 0x1E Max power consumption + * + * Interface Descriptor + * + * Offset Field Size Value + * 0 bLength 1 0x09 Size of descriptor in bytes + * 1 bDescriptorType 1 0x04 INTERFACE Descriptor Type + * 2 bInterfaceNumber 1 0x00 Number of interface + * 3 bAlternateSetting 1 0x00 Value used to select alternate + * 4 bNumEndpoints 1 0x02 Number of endpoints + * 5 bInterfaceClass 1 0xFF Class Code + * 6 bInterfaceSubClass 1 0xFF Subclass Code + * 7 bInterfaceProtocol 1 0xFF Protocol Code + * 8 iInterface 1 0x02 Index of interface string description + * + * IN Endpoint Descriptor + * + * Offset Field Size Value + * 0 bLength 1 0x07 Size of descriptor in bytes + * 1 bDescriptorType 1 0x05 ENDPOINT descriptor type + * 2 bEndpointAddress 1 0x82 Address of endpoint + * 3 bmAttributes 1 0x02 Endpoint attributes - Bulk + * 4 bNumEndpoints 2 0x0040 maximum packet size + * 5 bInterval 1 0x00 Interval for polling endpoint + * + * OUT Endpoint Descriptor + * + * Offset Field Size Value + * 0 bLength 1 0x07 Size of descriptor in bytes + * 1 bDescriptorType 1 0x05 ENDPOINT descriptor type + * 2 bEndpointAddress 1 0x02 Address of endpoint + * 3 bmAttributes 1 0x02 Endpoint attributes - Bulk + * 4 bNumEndpoints 2 0x0040 maximum packet size + * 5 bInterval 1 0x00 Interval for polling endpoint + * + * DATA FORMAT + * + * IN Endpoint + * + * The device reserves the first two bytes of data on this endpoint to contain the current + * values of the modem and line status registers. In the absence of data, the device + * generates a message consisting of these two status bytes every 40 ms + * + * Byte 0: Modem Status + * + * Offset Description + * B0 Reserved - must be 1 + * B1 Reserved - must be 0 + * B2 Reserved - must be 0 + * B3 Reserved - must be 0 + * B4 Clear to Send (CTS) + * B5 Data Set Ready (DSR) + * B6 Ring Indicator (RI) + * B7 Receive Line Signal Detect (RLSD) + * + * Byte 1: Line Status + * + * Offset Description + * B0 Data Ready (DR) + * B1 Overrun Error (OE) + * B2 Parity Error (PE) + * B3 Framing Error (FE) + * B4 Break Interrupt (BI) + * B5 Transmitter Holding Register (THRE) + * B6 Transmitter Empty (TEMT) + * B7 Error in RCVR FIFO + * + * OUT Endpoint + * + * This device reserves the first bytes of data on this endpoint contain the length + * and port identifier of the message. For the FTDI USB Serial converter the port + * identifier is always 1. + * + * Byte 0: Line Status + * + * Offset Description + * B0 Reserved - must be 1 + * B1 Reserved - must be 0 + * B2..7 Length of message - (not including Byte 0) + * + */ + diff --git a/drivers/usb/serial/keyspan_pda.S b/drivers/usb/serial/keyspan_pda.S new file mode 100644 index 000000000000..10c99eadd36f --- /dev/null +++ b/drivers/usb/serial/keyspan_pda.S @@ -0,0 +1,1124 @@ +/* $Id: loop.s,v 1.23 2000/03/20 09:49:06 warner Exp $ + * + * Firmware for the Keyspan PDA Serial Adapter, a USB serial port based on + * the EzUSB microcontroller. + * + * (C) Copyright 2000 Brian Warner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * "Keyspan PDA Serial Adapter" is probably a copyright of Keyspan, the + * company. + * + * This serial adapter is basically an EzUSB chip and an RS-232 line driver + * in a little widget that has a DB-9 on one end and a USB plug on the other. + * It uses the EzUSB's internal UART0 (using the pins from Port C) and timer2 + * as a baud-rate generator. The wiring is: + * PC0/RxD0 <- rxd (DB9 pin 2) PC4 <- dsr pin 6 + * PC1/TxD0 -> txd pin 3 PC5 <- ri pin 9 + * PC2 -> rts pin 7 PC6 <- dcd pin 1 + * PC3 <- cts pin 8 PC7 -> dtr pin 4 + * PB1 -> line driver standby + * + * The EzUSB register constants below come from their excellent documentation + * and sample code (which used to be available at www.anchorchips.com, but + * that has now been absorbed into Cypress' site and the CD-ROM contents + * don't appear to be available online anymore). If we get multiple + * EzUSB-based drivers into the kernel, it might be useful to pull them out + * into a separate .h file. + * + * THEORY OF OPERATION: + * + * There are two 256-byte ring buffers, one for tx, one for rx. + * + * EP2out is pure tx data. When it appears, the data is copied into the tx + * ring and serial transmission is started if it wasn't already running. The + * "tx buffer empty" interrupt may kick off another character if the ring + * still has data. If the host is tx-blocked because the ring filled up, + * it will request a "tx unthrottle" interrupt. If sending a serial character + * empties the ring below the desired threshold, we set a bit that will send + * up the tx unthrottle message as soon as the rx buffer becomes free. + * + * EP2in (interrupt) is used to send both rx chars and rx status messages + * (only "tx unthrottle" at this time) back up to the host. The first byte + * of the rx message indicates data (0) or status msg (1). Status messages + * are sent before any data. + * + * Incoming serial characters are put into the rx ring by the serial + * interrupt, and the EP2in buffer sent if it wasn't already in transit. + * When the EP2in buffer returns, the interrupt prompts us to send more + * rx chars (or status messages) if they are pending. + * + * Device control happens through "vendor specific" control messages on EP0. + * All messages are destined for the "Interface" (with the index always 0, + * so that if their two-port device might someday use similar firmware, we + * can use index=1 to refer to the second port). The messages defined are: + * + * bRequest = 0 : set baud/bits/parity + * 1 : unused + * 2 : reserved for setting HW flow control (CTSRTS) + * 3 : get/set "modem info" (pin states: DTR, RTS, DCD, RI, etc) + * 4 : set break (on/off) + * 5 : reserved for requesting interrupts on pin state change + * 6 : query buffer room or chars in tx buffer + * 7 : request tx unthrottle interrupt + * + * The host-side driver is set to recognize the device ID values stashed in + * serial EEPROM (0x06cd, 0x0103), program this firmware into place, then + * start it running. This firmware will use EzUSB's "renumeration" trick by + * simulating a bus disconnect, then reconnect with a different device ID + * (encoded in the desc_device descriptor below). The host driver then + * recognizes the new device ID and glues it to the real serial driver code. + * + * USEFUL DOCS: + * EzUSB Technical Reference Manual: + * 8051 manuals: everywhere, but try www.dalsemi.com because the EzUSB is + * basically the Dallas enhanced 8051 code. Remember that the EzUSB IO ports + * use totally different registers! + * USB 1.1 spec: www.usb.org + * + * HOW TO BUILD: + * gcc -x assembler-with-cpp -P -E -o keyspan_pda.asm keyspan_pda.s + * as31 -l keyspan_pda.asm + * mv keyspan_pda.obj keyspan_pda.hex + * perl ezusb_convert.pl keyspan_pda < keyspan_pda.hex > keyspan_pda_fw.h + * Get as31 from , and hack on it + * a bit to make it build. + * + * THANKS: + * Greg Kroah-Hartman, for coordinating the whole usb-serial thing. + * AnchorChips, for making such an incredibly useful little microcontroller. + * KeySpan, for making a handy, cheap ($40) widget that was so easy to take + * apart and trace with an ohmmeter. + * + * TODO: + * lots. grep for TODO. Interrupt safety needs stress-testing. Better flow + * control. Interrupting host upon change in DCD, etc, counting transitions. + * Need to find a safe device id to use (the one used by the Keyspan firmware + * under Windows would be ideal.. can anyone figure out what it is?). Parity. + * More baud rates. Oh, and the string-descriptor-length silicon bug + * workaround should be implemented, but I'm lazy, and the consequence is + * that the device name strings that show up in your kernel log will have + * lots of trailing binary garbage in them (appears as ????). Device strings + * should be made more accurate. + * + * Questions, bugs, patches to Brian. + * + * -Brian Warner + * + */ + +#define HIGH(x) (((x) & 0xff00) / 256) +#define LOW(x) ((x) & 0xff) + +#define dpl1 0x84 +#define dph1 0x85 +#define dps 0x86 + +;;; our bit assignments +#define TX_RUNNING 0 +#define DO_TX_UNTHROTTLE 1 + + ;; stack from 0x60 to 0x7f: should really set SP to 0x60-1, not 0x60 +#define STACK #0x60-1 + +#define EXIF 0x91 +#define EIE 0xe8 + .flag EUSB, EIE.0 + .flag ES0, IE.4 + +#define EP0CS #0x7fb4 +#define EP0STALLbit #0x01 +#define IN0BUF #0x7f00 +#define IN0BC #0x7fb5 +#define OUT0BUF #0x7ec0 +#define OUT0BC #0x7fc5 +#define IN2BUF #0x7e00 +#define IN2BC #0x7fb9 +#define IN2CS #0x7fb8 +#define OUT2BC #0x7fc9 +#define OUT2CS #0x7fc8 +#define OUT2BUF #0x7dc0 +#define IN4BUF #0x7d00 +#define IN4BC #0x7fbd +#define IN4CS #0x7fbc +#define OEB #0x7f9d +#define OUTB #0x7f97 +#define OEC #0x7f9e +#define OUTC #0x7f98 +#define PINSC #0x7f9b +#define PORTCCFG #0x7f95 +#define IN07IRQ #0x7fa9 +#define OUT07IRQ #0x7faa +#define IN07IEN #0x7fac +#define OUT07IEN #0x7fad +#define USBIRQ #0x7fab +#define USBIEN #0x7fae +#define USBBAV #0x7faf +#define USBCS #0x7fd6 +#define SUDPTRH #0x7fd4 +#define SUDPTRL #0x7fd5 +#define SETUPDAT #0x7fe8 + + ;; usb interrupt : enable is EIE.0 (0xe8), flag is EXIF.4 (0x91) + + .org 0 + ljmp start + ;; interrupt vectors + .org 23H + ljmp serial_int + .byte 0 + + .org 43H + ljmp USB_Jump_Table + .byte 0 ; filled in by the USB core + +;;; local variables. These are not initialized properly: do it by hand. + .org 30H +rx_ring_in: .byte 0 +rx_ring_out: .byte 0 +tx_ring_in: .byte 0 +tx_ring_out: .byte 0 +tx_unthrottle_threshold: .byte 0 + + .org 0x100H ; wants to be on a page boundary +USB_Jump_Table: + ljmp ISR_Sudav ; Setup Data Available + .byte 0 + ljmp 0 ; Start of Frame + .byte 0 + ljmp 0 ; Setup Data Loading + .byte 0 + ljmp 0 ; Global Suspend + .byte 0 + ljmp 0 ; USB Reset + .byte 0 + ljmp 0 ; Reserved + .byte 0 + ljmp 0 ; End Point 0 In + .byte 0 + ljmp 0 ; End Point 0 Out + .byte 0 + ljmp 0 ; End Point 1 In + .byte 0 + ljmp 0 ; End Point 1 Out + .byte 0 + ljmp ISR_Ep2in + .byte 0 + ljmp ISR_Ep2out + .byte 0 + + + .org 0x200 + +start: mov SP,STACK-1 ; set stack + ;; clear local variables + clr a + mov tx_ring_in, a + mov tx_ring_out, a + mov rx_ring_in, a + mov rx_ring_out, a + mov tx_unthrottle_threshold, a + clr TX_RUNNING + clr DO_TX_UNTHROTTLE + + ;; clear fifo with "fe" + mov r1, 0 + mov a, #0xfe + mov dptr, #tx_ring +clear_tx_ring_loop: + movx @dptr, a + inc dptr + djnz r1, clear_tx_ring_loop + + mov a, #0xfd + mov dptr, #rx_ring +clear_rx_ring_loop: + movx @dptr, a + inc dptr + djnz r1, clear_rx_ring_loop + +;;; turn on the RS-232 driver chip (bring the STANDBY pin low) + ;; set OEB.1 + mov a, #02H + mov dptr,OEB + movx @dptr,a + ;; clear PB1 + mov a, #00H + mov dptr,OUTB + movx @dptr,a + ;; set OEC.[127] + mov a, #0x86 + mov dptr,OEC + movx @dptr,a + ;; set PORTCCFG.[01] to route TxD0,RxD0 to serial port + mov dptr, PORTCCFG + mov a, #0x03 + movx @dptr, a + + ;; set up interrupts, autovectoring + mov dptr, USBBAV + movx a,@dptr + setb acc.0 ; AVEN bit to 0 + movx @dptr, a + + mov a,#0x01 ; enable SUDAV: setup data available (for ep0) + mov dptr, USBIRQ + movx @dptr, a ; clear SUDAVI + mov dptr, USBIEN + movx @dptr, a + + mov dptr, IN07IEN + mov a,#0x04 ; enable IN2 int + movx @dptr, a + + mov dptr, OUT07IEN + mov a,#0x04 ; enable OUT2 int + movx @dptr, a + mov dptr, OUT2BC + movx @dptr, a ; arm OUT2 + + mov a, #0x84 ; turn on RTS, DTR + mov dptr,OUTC + movx @dptr, a + ;; setup the serial port. 9600 8N1. + mov a,#01010011 ; mode 1, enable rx, clear int + mov SCON, a + ;; using timer2, in 16-bit baud-rate-generator mode + ;; (xtal 12MHz, internal fosc 24MHz) + ;; RCAP2H,RCAP2L = 65536 - fosc/(32*baud) + ;; 57600: 0xFFF2.F, say 0xFFF3 + ;; 9600: 0xFFB1.E, say 0xFFB2 + ;; 300: 0xF63C +#define BAUD 9600 +#define BAUD_TIMEOUT(rate) (65536 - (24 * 1000 * 1000) / (32 * rate)) +#define BAUD_HIGH(rate) HIGH(BAUD_TIMEOUT(rate)) +#define BAUD_LOW(rate) LOW(BAUD_TIMEOUT(rate)) + + mov T2CON, #030h ; rclk=1,tclk=1,cp=0,tr2=0(enable later) + mov r3, #5 + acall set_baud + setb TR2 + mov SCON, #050h + +#if 0 + mov r1, #0x40 + mov a, #0x41 +send: + mov SBUF, a + inc a + anl a, #0x3F + orl a, #0x40 +; xrl a, #0x02 +wait1: + jnb TI, wait1 + clr TI + djnz r1, send +;done: sjmp done + +#endif + + setb EUSB + setb EA + setb ES0 + ;acall dump_stat + + ;; hey, what say we RENUMERATE! (TRM p.62) + mov a, #0 + mov dps, a + mov dptr, USBCS + mov a, #0x02 ; DISCON=0, DISCOE=0, RENUM=1 + movx @dptr, a + ;; now presence pin is floating, simulating disconnect. wait 0.5s + mov r1, #46 +renum_wait1: + mov r2, #0 +renum_wait2: + mov r3, #0 +renum_wait3: + djnz r3, renum_wait3 + djnz r2, renum_wait2 + djnz r1, renum_wait1 ; wait about n*(256^2) 6MHz clocks + mov a, #0x06 ; DISCON=0, DISCOE=1, RENUM=1 + movx @dptr, a + ;; we are back online. the host device will now re-query us + + +main: sjmp main + + + +ISR_Sudav: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, USBIRQ ; clear USB int + mov a,#01h + movx @dptr,a + + ;; get request type + mov dptr, SETUPDAT + movx a, @dptr + mov r1, a ; r1 = bmRequestType + inc dptr + movx a, @dptr + mov r2, a ; r2 = bRequest + inc dptr + movx a, @dptr + mov r3, a ; r3 = wValueL + inc dptr + movx a, @dptr + mov r4, a ; r4 = wValueH + + ;; main switch on bmRequest.type: standard or vendor + mov a, r1 + anl a, #0x60 + cjne a, #0x00, setup_bmreq_type_not_standard + ;; standard request: now main switch is on bRequest + ljmp setup_bmreq_is_standard + +setup_bmreq_type_not_standard: + ;; a still has bmreq&0x60 + cjne a, #0x40, setup_bmreq_type_not_vendor + ;; Anchor reserves bRequest 0xa0-0xaf, we use small ones + ;; switch on bRequest. bmRequest will always be 0x41 or 0xc1 + cjne r2, #0x00, setup_ctrl_not_00 + ;; 00 is set baud, wValue[0] has baud rate index + lcall set_baud ; index in r3, carry set if error + jc setup_bmreq_type_not_standard__do_stall + ljmp setup_done_ack +setup_bmreq_type_not_standard__do_stall: + ljmp setup_stall +setup_ctrl_not_00: + cjne r2, #0x01, setup_ctrl_not_01 + ;; 01 is reserved for set bits (parity). TODO + ljmp setup_stall +setup_ctrl_not_01: + cjne r2, #0x02, setup_ctrl_not_02 + ;; 02 is set HW flow control. TODO + ljmp setup_stall +setup_ctrl_not_02: + cjne r2, #0x03, setup_ctrl_not_03 + ;; 03 is control pins (RTS, DTR). + ljmp control_pins ; will jump to setup_done_ack, + ; or setup_return_one_byte +setup_ctrl_not_03: + cjne r2, #0x04, setup_ctrl_not_04 + ;; 04 is send break (really "turn break on/off"). TODO + cjne r3, #0x00, setup_ctrl_do_break_on + ;; do break off: restore PORTCCFG.1 to reconnect TxD0 to serial port + mov dptr, PORTCCFG + movx a, @dptr + orl a, #0x02 + movx @dptr, a + ljmp setup_done_ack +setup_ctrl_do_break_on: + ;; do break on: clear PORTCCFG.0, set TxD high(?) (b1 low) + mov dptr, OUTC + movx a, @dptr + anl a, #0xfd ; ~0x02 + movx @dptr, a + mov dptr, PORTCCFG + movx a, @dptr + anl a, #0xfd ; ~0x02 + movx @dptr, a + ljmp setup_done_ack +setup_ctrl_not_04: + cjne r2, #0x05, setup_ctrl_not_05 + ;; 05 is set desired interrupt bitmap. TODO + ljmp setup_stall +setup_ctrl_not_05: + cjne r2, #0x06, setup_ctrl_not_06 + ;; 06 is query room + cjne r3, #0x00, setup_ctrl_06_not_00 + ;; 06, wValue[0]=0 is query write_room + mov a, tx_ring_out + setb c + subb a, tx_ring_in ; out-1-in = 255 - (in-out) + ljmp setup_return_one_byte +setup_ctrl_06_not_00: + cjne r3, #0x01, setup_ctrl_06_not_01 + ;; 06, wValue[0]=1 is query chars_in_buffer + mov a, tx_ring_in + clr c + subb a, tx_ring_out ; in-out + ljmp setup_return_one_byte +setup_ctrl_06_not_01: + ljmp setup_stall +setup_ctrl_not_06: + cjne r2, #0x07, setup_ctrl_not_07 + ;; 07 is request tx unthrottle interrupt + mov tx_unthrottle_threshold, r3; wValue[0] is threshold value + ljmp setup_done_ack +setup_ctrl_not_07: + ljmp setup_stall + +setup_bmreq_type_not_vendor: + ljmp setup_stall + + +setup_bmreq_is_standard: + cjne r2, #0x00, setup_breq_not_00 + ;; 00: Get_Status (sub-switch on bmRequestType: device, ep, int) + cjne r1, #0x80, setup_Get_Status_not_device + ;; Get_Status(device) + ;; are we self-powered? no. can we do remote wakeup? no + ;; so return two zero bytes. This is reusable +setup_return_two_zero_bytes: + mov dptr, IN0BUF + clr a + movx @dptr, a + inc dptr + movx @dptr, a + mov dptr, IN0BC + mov a, #2 + movx @dptr, a + ljmp setup_done_ack +setup_Get_Status_not_device: + cjne r1, #0x82, setup_Get_Status_not_endpoint + ;; Get_Status(endpoint) + ;; must get stall bit for ep[wIndexL], return two bytes, bit in lsb 0 + ;; for now: cheat. TODO + sjmp setup_return_two_zero_bytes +setup_Get_Status_not_endpoint: + cjne r1, #0x81, setup_Get_Status_not_interface + ;; Get_Status(interface): return two zeros + sjmp setup_return_two_zero_bytes +setup_Get_Status_not_interface: + ljmp setup_stall + +setup_breq_not_00: + cjne r2, #0x01, setup_breq_not_01 + ;; 01: Clear_Feature (sub-switch on wValueL: stall, remote wakeup) + cjne r3, #0x00, setup_Clear_Feature_not_stall + ;; Clear_Feature(stall). should clear a stall bit. TODO + ljmp setup_stall +setup_Clear_Feature_not_stall: + cjne r3, #0x01, setup_Clear_Feature_not_rwake + ;; Clear_Feature(remote wakeup). ignored. + ljmp setup_done_ack +setup_Clear_Feature_not_rwake: + ljmp setup_stall + +setup_breq_not_01: + cjne r2, #0x03, setup_breq_not_03 + ;; 03: Set_Feature (sub-switch on wValueL: stall, remote wakeup) + cjne r3, #0x00, setup_Set_Feature_not_stall + ;; Set_Feature(stall). Should set a stall bit. TODO + ljmp setup_stall +setup_Set_Feature_not_stall: + cjne r3, #0x01, setup_Set_Feature_not_rwake + ;; Set_Feature(remote wakeup). ignored. + ljmp setup_done_ack +setup_Set_Feature_not_rwake: + ljmp setup_stall + +setup_breq_not_03: + cjne r2, #0x06, setup_breq_not_06 + ;; 06: Get_Descriptor (s-switch on wValueH: dev, config[n], string[n]) + cjne r4, #0x01, setup_Get_Descriptor_not_device + ;; Get_Descriptor(device) + mov dptr, SUDPTRH + mov a, #HIGH(desc_device) + movx @dptr, a + mov dptr, SUDPTRL + mov a, #LOW(desc_device) + movx @dptr, a + ljmp setup_done_ack +setup_Get_Descriptor_not_device: + cjne r4, #0x02, setup_Get_Descriptor_not_config + ;; Get_Descriptor(config[n]) + cjne r3, #0x00, setup_stall; only handle n==0 + ;; Get_Descriptor(config[0]) + mov dptr, SUDPTRH + mov a, #HIGH(desc_config1) + movx @dptr, a + mov dptr, SUDPTRL + mov a, #LOW(desc_config1) + movx @dptr, a + ljmp setup_done_ack +setup_Get_Descriptor_not_config: + cjne r4, #0x03, setup_Get_Descriptor_not_string + ;; Get_Descriptor(string[wValueL]) + ;; if (wValueL >= maxstrings) stall + mov a, #((desc_strings_end-desc_strings)/2) + clr c + subb a,r3 ; a=4, r3 = 0..3 . if a<=0 then stall + jc setup_stall + jz setup_stall + mov a, r3 + add a, r3 ; a = 2*wValueL + mov dptr, #desc_strings + add a, dpl + mov dpl, a + mov a, #0 + addc a, dph + mov dph, a ; dph = desc_strings[a]. big endian! (handy) + ;; it looks like my adapter uses a revision of the EZUSB that + ;; contains "rev D errata number 8", as hinted in the EzUSB example + ;; code. I cannot find an actual errata description on the Cypress + ;; web site, but from the example code it looks like this bug causes + ;; the length of string descriptors to be read incorrectly, possibly + ;; sending back more characters than the descriptor has. The workaround + ;; is to manually send out all of the data. The consequence of not + ;; using the workaround is that the strings gathered by the kernel + ;; driver are too long and are filled with trailing garbage (including + ;; leftover strings). Writing this out by hand is a nuisance, so for + ;; now I will just live with the bug. + movx a, @dptr + mov r1, a + inc dptr + movx a, @dptr + mov r2, a + mov dptr, SUDPTRH + mov a, r1 + movx @dptr, a + mov dptr, SUDPTRL + mov a, r2 + movx @dptr, a + ;; done + ljmp setup_done_ack + +setup_Get_Descriptor_not_string: + ljmp setup_stall + +setup_breq_not_06: + cjne r2, #0x08, setup_breq_not_08 + ;; Get_Configuration. always 1. return one byte. + ;; this is reusable + mov a, #1 +setup_return_one_byte: + mov dptr, IN0BUF + movx @dptr, a + mov a, #1 + mov dptr, IN0BC + movx @dptr, a + ljmp setup_done_ack +setup_breq_not_08: + cjne r2, #0x09, setup_breq_not_09 + ;; 09: Set_Configuration. ignored. + ljmp setup_done_ack +setup_breq_not_09: + cjne r2, #0x0a, setup_breq_not_0a + ;; 0a: Get_Interface. get the current altsetting for int[wIndexL] + ;; since we only have one interface, ignore wIndexL, return a 0 + mov a, #0 + ljmp setup_return_one_byte +setup_breq_not_0a: + cjne r2, #0x0b, setup_breq_not_0b + ;; 0b: Set_Interface. set altsetting for interface[wIndexL]. ignored + ljmp setup_done_ack +setup_breq_not_0b: + ljmp setup_stall + + +setup_done_ack: + ;; now clear HSNAK + mov dptr, EP0CS + mov a, #0x02 + movx @dptr, a + sjmp setup_done +setup_stall: + ;; unhandled. STALL + ;EP0CS |= bmEPSTALL + mov dptr, EP0CS + movx a, @dptr + orl a, EP0STALLbit + movx @dptr, a + sjmp setup_done + +setup_done: + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +;;; ============================================================== + +set_baud: ; baud index in r3 + ;; verify a < 10 + mov a, r3 + jb ACC.7, set_baud__badbaud + clr c + subb a, #10 + jnc set_baud__badbaud + mov a, r3 + rl a ; a = index*2 + add a, #LOW(baud_table) + mov dpl, a + mov a, #HIGH(baud_table) + addc a, #0 + mov dph, a + ;; TODO: shut down xmit/receive + ;; TODO: wait for current xmit char to leave + ;; TODO: shut down timer to avoid partial-char glitch + movx a,@dptr ; BAUD_HIGH + mov RCAP2H, a + mov TH2, a + inc dptr + movx a,@dptr ; BAUD_LOW + mov RCAP2L, a + mov TL2, a + ;; TODO: restart xmit/receive + ;; TODO: reenable interrupts, resume tx if pending + clr c ; c=0: success + ret +set_baud__badbaud: + setb c ; c=1: failure + ret + +;;; ================================================== +control_pins: + cjne r1, #0x41, control_pins_in +control_pins_out: + mov a, r3 ; wValue[0] holds new bits: b7 is new DTR, b2 is new RTS + xrl a, #0xff ; 1 means active, 0V, +12V ? + anl a, #0x84 + mov r3, a + mov dptr, OUTC + movx a, @dptr ; only change bits 7 and 2 + anl a, #0x7b ; ~0x84 + orl a, r3 + movx @dptr, a ; other pins are inputs, bits ignored + ljmp setup_done_ack +control_pins_in: + mov dptr, PINSC + movx a, @dptr + xrl a, #0xff + ljmp setup_return_one_byte + +;;; ======================================== + +ISR_Ep2in: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, IN07IRQ ; clear USB int + mov a,#04h + movx @dptr,a + + ;; do stuff + lcall start_in + + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +ISR_Ep2out: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + mov a,EXIF + clr acc.4 + mov EXIF,a ; clear INT2 first + mov dptr, OUT07IRQ ; clear USB int + mov a,#04h + movx @dptr,a + + ;; do stuff + + ;; copy data into buffer. for now, assume we will have enough space + mov dptr, OUT2BC ; get byte count + movx a,@dptr + mov r1, a + clr a + mov dps, a + mov dptr, OUT2BUF ; load DPTR0 with source + mov dph1, #HIGH(tx_ring) ; load DPTR1 with target + mov dpl1, tx_ring_in +OUT_loop: + movx a,@dptr ; read + inc dps ; switch to DPTR1: target + inc dpl1 ; target = tx_ring_in+1 + movx @dptr,a ; store + mov a,dpl1 + cjne a, tx_ring_out, OUT_no_overflow + sjmp OUT_overflow +OUT_no_overflow: + inc tx_ring_in ; tx_ring_in++ + inc dps ; switch to DPTR0: source + inc dptr + djnz r1, OUT_loop + sjmp OUT_done +OUT_overflow: + ;; signal overflow + ;; fall through +OUT_done: + ;; ack + mov dptr,OUT2BC + movx @dptr,a + + ;; start tx + acall maybe_start_tx + ;acall dump_stat + + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +dump_stat: + ;; fill in EP4in with a debugging message: + ;; tx_ring_in, tx_ring_out, rx_ring_in, rx_ring_out + ;; tx_active + ;; tx_ring[0..15] + ;; 0xfc + ;; rx_ring[0..15] + clr a + mov dps, a + + mov dptr, IN4CS + movx a, @dptr + jb acc.1, dump_stat__done; busy: cannot dump, old one still pending + mov dptr, IN4BUF + + mov a, tx_ring_in + movx @dptr, a + inc dptr + mov a, tx_ring_out + movx @dptr, a + inc dptr + + mov a, rx_ring_in + movx @dptr, a + inc dptr + mov a, rx_ring_out + movx @dptr, a + inc dptr + + clr a + jnb TX_RUNNING, dump_stat__no_tx_running + inc a +dump_stat__no_tx_running: + movx @dptr, a + inc dptr + ;; tx_ring[0..15] + inc dps + mov dptr, #tx_ring ; DPTR1: source + mov r1, #16 +dump_stat__tx_ring_loop: + movx a, @dptr + inc dptr + inc dps + movx @dptr, a + inc dptr + inc dps + djnz r1, dump_stat__tx_ring_loop + inc dps + + mov a, #0xfc + movx @dptr, a + inc dptr + + ;; rx_ring[0..15] + inc dps + mov dptr, #rx_ring ; DPTR1: source + mov r1, #16 +dump_stat__rx_ring_loop: + movx a, @dptr + inc dptr + inc dps + movx @dptr, a + inc dptr + inc dps + djnz r1, dump_stat__rx_ring_loop + + ;; now send it + clr a + mov dps, a + mov dptr, IN4BC + mov a, #38 + movx @dptr, a +dump_stat__done: + ret + +;;; ============================================================ + +maybe_start_tx: + ;; make sure the tx process is running. + jb TX_RUNNING, start_tx_done +start_tx: + ;; is there work to be done? + mov a, tx_ring_in + cjne a,tx_ring_out, start_tx__work + ret ; no work +start_tx__work: + ;; tx was not running. send the first character, setup the TI int + inc tx_ring_out ; [++tx_ring_out] + mov dph, #HIGH(tx_ring) + mov dpl, tx_ring_out + movx a, @dptr + mov sbuf, a + setb TX_RUNNING +start_tx_done: + ;; can we unthrottle the host tx process? + ;; step 1: do we care? + mov a, #0 + cjne a, tx_unthrottle_threshold, start_tx__maybe_unthrottle_tx + ;; nope +start_tx_really_done: + ret +start_tx__maybe_unthrottle_tx: + ;; step 2: is there now room? + mov a, tx_ring_out + setb c + subb a, tx_ring_in + ;; a is now write_room. If thresh >= a, we can unthrottle + clr c + subb a, tx_unthrottle_threshold + jc start_tx_really_done ; nope + ;; yes, we can unthrottle. remove the threshold and mark a request + mov tx_unthrottle_threshold, #0 + setb DO_TX_UNTHROTTLE + ;; prod rx, which will actually send the message when in2 becomes free + ljmp start_in + + +serial_int: + push dps + push dpl + push dph + push dpl1 + push dph1 + push acc + jnb TI, serial_int__not_tx + ;; tx finished. send another character if we have one + clr TI ; clear int + clr TX_RUNNING + lcall start_tx +serial_int__not_tx: + jnb RI, serial_int__not_rx + lcall get_rx_char + clr RI ; clear int +serial_int__not_rx: + ;; return + pop acc + pop dph1 + pop dpl1 + pop dph + pop dpl + pop dps + reti + +get_rx_char: + mov dph, #HIGH(rx_ring) + mov dpl, rx_ring_in + inc dpl ; target = rx_ring_in+1 + mov a, sbuf + movx @dptr, a + ;; check for overflow before incrementing rx_ring_in + mov a, dpl + cjne a, rx_ring_out, get_rx_char__no_overflow + ;; signal overflow + ret +get_rx_char__no_overflow: + inc rx_ring_in + ;; kick off USB INpipe + acall start_in + ret + +start_in: + ;; check if the inpipe is already running. + mov dptr, IN2CS + movx a, @dptr + jb acc.1, start_in__done; int will handle it + jb DO_TX_UNTHROTTLE, start_in__do_tx_unthrottle + ;; see if there is any work to do. a serial interrupt might occur + ;; during this sequence? + mov a, rx_ring_in + cjne a, rx_ring_out, start_in__have_work + ret ; nope +start_in__have_work: + ;; now copy as much data as possible into the pipe. 63 bytes max. + clr a + mov dps, a + mov dph, #HIGH(rx_ring) ; load DPTR0 with source + inc dps + mov dptr, IN2BUF ; load DPTR1 with target + movx @dptr, a ; in[0] signals that rest of IN is rx data + inc dptr + inc dps + ;; loop until we run out of data, or we have copied 64 bytes + mov r1, #1 ; INbuf size counter +start_in__loop: + mov a, rx_ring_in + cjne a, rx_ring_out, start_in__still_copying + sjmp start_in__kick +start_in__still_copying: + inc rx_ring_out + mov dpl, rx_ring_out + movx a, @dptr + inc dps + movx @dptr, a ; write into IN buffer + inc dptr + inc dps + inc r1 + cjne r1, #64, start_in__loop; loop +start_in__kick: + ;; either we ran out of data, or we copied 64 bytes. r1 has byte count + ;; kick off IN + mov dptr, IN2BC + mov a, r1 + jz start_in__done + movx @dptr, a + ;; done +start_in__done: + ;acall dump_stat + ret +start_in__do_tx_unthrottle: + ;; special sequence: send a tx unthrottle message + clr DO_TX_UNTHROTTLE + clr a + mov dps, a + mov dptr, IN2BUF + mov a, #1 + movx @dptr, a + inc dptr + mov a, #2 + movx @dptr, a + mov dptr, IN2BC + movx @dptr, a + ret + +putchar: + clr TI + mov SBUF, a +putchar_wait: + jnb TI, putchar_wait + clr TI + ret + + +baud_table: ; baud_high, then baud_low + ;; baud[0]: 110 + .byte BAUD_HIGH(110) + .byte BAUD_LOW(110) + ;; baud[1]: 300 + .byte BAUD_HIGH(300) + .byte BAUD_LOW(300) + ;; baud[2]: 1200 + .byte BAUD_HIGH(1200) + .byte BAUD_LOW(1200) + ;; baud[3]: 2400 + .byte BAUD_HIGH(2400) + .byte BAUD_LOW(2400) + ;; baud[4]: 4800 + .byte BAUD_HIGH(4800) + .byte BAUD_LOW(4800) + ;; baud[5]: 9600 + .byte BAUD_HIGH(9600) + .byte BAUD_LOW(9600) + ;; baud[6]: 19200 + .byte BAUD_HIGH(19200) + .byte BAUD_LOW(19200) + ;; baud[7]: 38400 + .byte BAUD_HIGH(38400) + .byte BAUD_LOW(38400) + ;; baud[8]: 57600 + .byte BAUD_HIGH(57600) + .byte BAUD_LOW(57600) + ;; baud[9]: 115200 + .byte BAUD_HIGH(115200) + .byte BAUD_LOW(115200) + +desc_device: + .byte 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40 + .byte 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab, 1, 2, 3, 0x01 +;;; The "real" device id, which must match the host driver, is that +;;; "0xcd 0x06 0x04 0x01" sequence, which is 0x06cd, 0x0104 + +desc_config1: + .byte 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32 + .byte 0x09, 0x04, 0x00, 0x00, 0x02, 0xff, 0xff, 0xff, 0x00 + .byte 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01 + .byte 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00 + +desc_strings: + .word string_langids, string_mfg, string_product, string_serial +desc_strings_end: + +string_langids: .byte string_langids_end-string_langids + .byte 3 + .word 0 +string_langids_end: + + ;; sigh. These strings are Unicode, meaning UTF16? 2 bytes each. Now + ;; *that* is a pain in the ass to encode. And they are little-endian + ;; too. Use this perl snippet to get the bytecodes: + /* while (<>) { + @c = split(//); + foreach $c (@c) { + printf("0x%02x, 0x00, ", ord($c)); + } + } + */ + +string_mfg: .byte string_mfg_end-string_mfg + .byte 3 +; .byte "ACME usb widgets" + .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00, 0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00, 0x73, 0x00 +string_mfg_end: + +string_product: .byte string_product_end-string_product + .byte 3 +; .byte "ACME USB serial widget" + .byte 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00, 0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00, 0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00 +string_product_end: + +string_serial: .byte string_serial_end-string_serial + .byte 3 +; .byte "47" + .byte 0x34, 0x00, 0x37, 0x00 +string_serial_end: + +;;; ring buffer memory + ;; tx_ring_in+1 is where the next input byte will go + ;; [tx_ring_out] has been sent + ;; if tx_ring_in == tx_ring_out, theres no work to do + ;; there are (tx_ring_in - tx_ring_out) chars to be written + ;; dont let _in lap _out + ;; cannot inc if tx_ring_in+1 == tx_ring_out + ;; write [tx_ring_in+1] then tx_ring_in++ + ;; if (tx_ring_in+1 == tx_ring_out), overflow + ;; else tx_ring_in++ + ;; read/send [tx_ring_out+1], then tx_ring_out++ + + ;; rx_ring_in works the same way + + .org 0x1000 +tx_ring: + .skip 0x100 ; 256 bytes +rx_ring: + .skip 0x100 ; 256 bytes + + + .END + diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c new file mode 100644 index 000000000000..b1fb6919418d --- /dev/null +++ b/drivers/usb/serial/keyspan_pda.c @@ -0,0 +1,700 @@ +/* + * USB Keyspan PDA Converter driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (03/26/2000) gkh + * Split driver up into device specific pieces. + * + */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +struct ezusb_hex_record { + __u16 address; + __u8 data_size; + __u8 data[16]; +}; + +#include "keyspan_pda_fw.h" + +#include "usb-serial.h" + +struct keyspan_pda_private { + int tx_room; + int tx_throttled; +}; + +#define KEYSPAN_VENDOR_ID 0x06cd +#define KEYSPAN_PDA_FAKE_ID 0x0103 +#define KEYSPAN_PDA_ID 0x0104 /* no clue */ + +/* All of the device info needed for the Keyspan PDA serial converter */ +static __u16 keyspan_vendor_id = KEYSPAN_VENDOR_ID; +static __u16 keyspan_pda_fake_product_id = KEYSPAN_PDA_FAKE_ID; +static __u16 keyspan_pda_product_id = KEYSPAN_PDA_ID; + + + +static void keyspan_pda_rx_interrupt (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial; + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + int i; + struct keyspan_pda_private *priv; + priv = (struct keyspan_pda_private *)(port->private); + + /* the urb might have been killed. */ + if (urb->status) + return; + + if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) { + return; + } + + serial = port->serial; + if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) { + return; + } + + /* see if the message is data or a status interrupt */ + switch (data[0]) { + case 0: + /* rest of message is rx data */ + if (urb->actual_length) { + tty = serial->port[0].tty; + for (i = 1; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + break; + case 1: + /* status interrupt */ + dbg(" rx int, d1=%d, d2=%d", data[1], data[2]); + switch (data[1]) { + case 1: /* modemline change */ + break; + case 2: /* tx unthrottle interrupt */ + tty = serial->port[0].tty; + priv->tx_throttled = 0; + wake_up(&port->write_wait); /* wake up writer */ + wake_up(&tty->write_wait); /* them too */ + break; + default: + break; + } + break; + default: + break; + } + + /* INT urbs are automatically re-submitted */ +} + + +static void keyspan_pda_rx_throttle (struct usb_serial_port *port) +{ + /* stop receiving characters. We just turn off the URB request, and + let chars pile up in the device. If we're doing hardware + flowcontrol, the device will signal the other end when its buffer + fills up. If we're doing XON/XOFF, this would be a good time to + send an XOFF, although it might make sense to foist that off + upon the device too. */ + + dbg("keyspan_pda_rx_throttle port %d", port->number); + usb_unlink_urb(port->interrupt_in_urb); +} + + +static void keyspan_pda_rx_unthrottle (struct usb_serial_port *port) +{ + /* just restart the receive interrupt URB */ + dbg("keyspan_pda_rx_unthrottle port %d", port->number); + if (usb_submit_urb(port->interrupt_in_urb)) + dbg(" usb_submit_urb(read urb) failed"); + return; +} + + +static int keyspan_pda_setbaud (struct usb_serial *serial, int baud) +{ + int rc; + int bindex; + + switch(baud) { + case 110: bindex = 0; break; + case 300: bindex = 1; break; + case 1200: bindex = 2; break; + case 2400: bindex = 3; break; + case 4800: bindex = 4; break; + case 9600: bindex = 5; break; + case 19200: bindex = 6; break; + case 38400: bindex = 7; break; + case 57600: bindex = 8; break; + case 115200: bindex = 9; break; + default: return -EINVAL; + } + + /* rather than figure out how to sleep while waiting for this + to complete, I just use the "legacy" API. */ + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + 0, /* set baud */ + USB_TYPE_VENDOR + | USB_RECIP_INTERFACE + | USB_DIR_OUT, /* type */ + bindex, /* value */ + 0, /* index */ + NULL, /* &data */ + 0, /* size */ + 2*HZ); /* timeout */ + return(rc); +} + + +static void keyspan_pda_break_ctl (struct usb_serial_port *port, int break_state) +{ + struct usb_serial *serial = port->serial; + int value; + if (break_state == -1) + value = 1; /* start break */ + else + value = 0; /* clear break */ + usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + 4, /* set break */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE | USB_DIR_OUT, + value, 0, NULL, 0, 2*HZ); + /* there is something funky about this.. the TCSBRK that 'cu' performs + ought to translate into a break_ctl(-1),break_ctl(0) pair HZ/4 + seconds apart, but it feels like the break sent isn't as long as it + is on /dev/ttyS0 */ +} + + +static void keyspan_pda_set_termios (struct usb_serial_port *port, + struct termios *old_termios) +{ + struct usb_serial *serial = port->serial; + unsigned int cflag = port->tty->termios->c_cflag; + + /* cflag specifies lots of stuff: number of stop bits, parity, number + of data bits, baud. What can the device actually handle?: + CSTOPB (1 stop bit or 2) + PARENB (parity) + CSIZE (5bit .. 8bit) + There is minimal hw support for parity (a PSW bit seems to hold the + parity of whatever is in the accumulator). The UART either deals + with 10 bits (start, 8 data, stop) or 11 bits (start, 8 data, + 1 special, stop). So, with firmware changes, we could do: + 8N1: 10 bit + 8N2: 11 bit, extra bit always (mark?) + 8[EOMS]1: 11 bit, extra bit is parity + 7[EOMS]1: 10 bit, b0/b7 is parity + 7[EOMS]2: 11 bit, b0/b7 is parity, extra bit always (mark?) + + HW flow control is dictated by the tty->termios->c_cflags & CRTSCTS + bit. + + For now, just do baud. */ + + switch (cflag & CBAUD) { + /* we could support more values here, just need to calculate + the necessary divisors in the firmware. + has the Bnnn constants. */ + case B110: keyspan_pda_setbaud(serial, 110); break; + case B300: keyspan_pda_setbaud(serial, 300); break; + case B1200: keyspan_pda_setbaud(serial, 1200); break; + case B2400: keyspan_pda_setbaud(serial, 2400); break; + case B4800: keyspan_pda_setbaud(serial, 4800); break; + case B9600: keyspan_pda_setbaud(serial, 9600); break; + case B19200: keyspan_pda_setbaud(serial, 19200); break; + case B38400: keyspan_pda_setbaud(serial, 38400); break; + case B57600: keyspan_pda_setbaud(serial, 57600); break; + case B115200: keyspan_pda_setbaud(serial, 115200); break; + default: dbg("can't handle requested baud rate"); break; + } +} + + +/* modem control pins: DTR and RTS are outputs and can be controlled. + DCD, RI, DSR, CTS are inputs and can be read. All outputs can also be + read. The byte passed is: DTR(b7) DCD RI DSR CTS RTS(b2) unused unused */ + +static int keyspan_pda_get_modem_info(struct usb_serial *serial, + unsigned char *value) +{ + int rc; + unsigned char data; + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + 3, /* get pins */ + USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_IN, + 0, 0, &data, 1, 2*HZ); + if (rc > 0) + *value = data; + return rc; +} + + +static int keyspan_pda_set_modem_info(struct usb_serial *serial, + unsigned char value) +{ + int rc; + rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), + 3, /* set pins */ + USB_TYPE_VENDOR|USB_RECIP_INTERFACE|USB_DIR_OUT, + value, 0, NULL, 0, 2*HZ); + return rc; +} + + +static int keyspan_pda_ioctl(struct usb_serial_port *port, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct usb_serial *serial = port->serial; + int rc; + unsigned int value; + unsigned char status, mask; + + switch (cmd) { + case TIOCMGET: /* get modem pins state */ + rc = keyspan_pda_get_modem_info(serial, &status); + if (rc < 0) + return rc; + value = + ((status & (1<<7)) ? TIOCM_DTR : 0) | + ((status & (1<<6)) ? TIOCM_CAR : 0) | + ((status & (1<<5)) ? TIOCM_RNG : 0) | + ((status & (1<<4)) ? TIOCM_DSR : 0) | + ((status & (1<<3)) ? TIOCM_CTS : 0) | + ((status & (1<<2)) ? TIOCM_RTS : 0); + if (copy_to_user((unsigned int *)arg, &value, sizeof(int))) + return -EFAULT; + return 0; + case TIOCMSET: /* set a state as returned by MGET */ + if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) + return -EFAULT; + status = + ((value & TIOCM_DTR) ? (1<<7) : 0) | + ((value & TIOCM_CAR) ? (1<<6) : 0) | + ((value & TIOCM_RNG) ? (1<<5) : 0) | + ((value & TIOCM_DSR) ? (1<<4) : 0) | + ((value & TIOCM_CTS) ? (1<<3) : 0) | + ((value & TIOCM_RTS) ? (1<<2) : 0); + rc = keyspan_pda_set_modem_info(serial, status); + if (rc < 0) + return rc; + return 0; + case TIOCMBIS: /* set bits in bitmask */ + case TIOCMBIC: /* clear bits from bitmask */ + if (copy_from_user(&value, (unsigned int *)arg, sizeof(int))) + return -EFAULT; + rc = keyspan_pda_get_modem_info(serial, &status); + if (rc < 0) + return rc; + mask = + ((value & TIOCM_RTS) ? (1<<2) : 0) | + ((value & TIOCM_DTR) ? (1<<7) : 0); + if (cmd == TIOCMBIS) + status |= mask; + else + status &= ~mask; + rc = keyspan_pda_set_modem_info(serial, status); + if (rc < 0) + return rc; + return 0; + case TIOCMIWAIT: + /* wait for any of the 4 modem inputs (DCD,RI,DSR,CTS)*/ + /* TODO */ + case TIOCGICOUNT: + /* return count of modemline transitions */ + return 0; /* TODO */ + } + + return -ENOIOCTLCMD; +} + +static int keyspan_pda_write(struct usb_serial_port *port, int from_user, + const unsigned char *buf, int count) +{ + struct usb_serial *serial = port->serial; + int request_unthrottle = 0; + int rc = 0; + struct keyspan_pda_private *priv; + DECLARE_WAITQUEUE(wait, current); + + priv = (struct keyspan_pda_private *)(port->private); + /* guess how much room is left in the device's ring buffer, and if we + want to send more than that, check first, updating our notion of + what is left. If our write will result in no room left, ask the + device to give us an interrupt when the room available rises above + a threshold, and hold off all writers (eventually, those using + select() or poll() too) until we receive that unthrottle interrupt. + Block if we can't write anything at all, otherwise write as much as + we can. */ + dbg("keyspan_pda_write(%d)",count); + if (count == 0) { + dbg(" write request of 0 bytes"); + return (0); + } + + /* we might block because of: + the TX urb is in-flight (wait until it completes) + the device is full (wait until it says there is room) + */ + while (port->write_urb->status == -EINPROGRESS) { + if (0 /* file->f_flags & O_NONBLOCK */) { + rc = -EAGAIN; + goto err; + } + interruptible_sleep_on(&port->write_wait); + if (signal_pending(current)) { + rc = -ERESTARTSYS; + goto err; + } + } + + /* at this point the URB is in our control, nobody else can submit it + again (the only sudden transition was the one from EINPROGRESS to + finished) */ + + /* the next potential block is that our TX process might be throttled. + The transition from throttled->not happens because of an Rx + interrupt, and the wake_up occurs during the same interrupt, so we + have to be careful to avoid a race that would cause us to sleep + forever. */ + + add_wait_queue(&port->write_wait, &wait); + set_current_state(TASK_INTERRUPTIBLE); + while (priv->tx_throttled) { + /* device can't accomodate any more characters. Sleep until it + can. Woken up by an Rx interrupt message, which clears + tx_throttled first. */ + dbg(" tx_throttled, going to sleep"); + if (signal_pending(current)) { + current->state = TASK_RUNNING; + remove_wait_queue(&port->write_wait, &wait); + dbg(" woke up because of signal"); + rc = -ERESTARTSYS; + goto err; + } + schedule(); + dbg(" woke up"); + } + remove_wait_queue(&port->write_wait, &wait); + set_current_state(TASK_RUNNING); + + count = (count > port->bulk_out_size) ? port->bulk_out_size : count; + if (count > priv->tx_room) { + unsigned char room; + /* Looks like we might overrun the Tx buffer. Ask the device + how much room it really has */ + rc = usb_control_msg(serial->dev, + usb_rcvctrlpipe(serial->dev, 0), + 6, /* write_room */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_IN, + 0, /* value: 0 means "remaining room" */ + 0, /* index */ + &room, + 1, + 2*HZ); + if (rc < 0) { + dbg(" roomquery failed"); + return rc; /* failed */ + } + if (rc == 0) { + dbg(" roomquery returned 0 bytes"); + return -EIO; /* device didn't return any data */ + } + dbg(" roomquery says %d", room); + priv->tx_room = room; + if (count > priv->tx_room) { + /* we're about to completely fill the Tx buffer, so + we'll be throttled afterwards. */ + count = priv->tx_room; + request_unthrottle = 1; + } + } + priv->tx_room -= count; + + if (count) { + /* now transfer data */ + if (from_user) { + copy_from_user(port->write_urb->transfer_buffer, buf, count); + } + else { + memcpy (port->write_urb->transfer_buffer, buf, count); + } + /* send the data out the bulk port */ + port->write_urb->transfer_buffer_length = count; + + if (usb_submit_urb(port->write_urb)) + dbg(" usb_submit_urb(write bulk) failed"); + } + else { + /* There wasn't any room left, so we are throttled until + the buffer empties a bit */ + request_unthrottle = 1; + } + + if (request_unthrottle) { + dbg(" request_unthrottle"); + /* ask the device to tell us when the tx buffer becomes + sufficiently empty */ + priv->tx_throttled = 1; /* block writers */ + rc = usb_control_msg(serial->dev, + usb_sndctrlpipe(serial->dev, 0), + 7, /* request_unthrottle */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_OUT, + 16, /* value: threshold */ + 0, /* index */ + NULL, + 0, + 2*HZ); + } + + return (count); + err: + return (rc); +} + + +static void keyspan_pda_write_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial; + struct tty_struct *tty; + + if (port_paranoia_check (port, "keyspan_pda_rx_interrupt")) { + return; + } + + serial = port->serial; + if (serial_paranoia_check (serial, "keyspan_pda_rx_interrupt")) { + return; + } + + wake_up_interruptible(&port->write_wait); + + tty = port->tty; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); +} + + +static int keyspan_pda_write_room (struct usb_serial_port *port) +{ + struct keyspan_pda_private *priv; + priv = (struct keyspan_pda_private *)(port->private); + + /* used by n_tty.c for processing of tabs and such. Giving it our + conservative guess is probably good enough, but needs testing by + running a console through the device. */ + + return (priv->tx_room); +} + + +static int keyspan_pda_chars_in_buffer (struct usb_serial_port *port) +{ + struct keyspan_pda_private *priv; + priv = (struct keyspan_pda_private *)(port->private); + + /* when throttled, return at least WAKEUP_CHARS to tell select() (via + n_tty.c:normal_poll() ) that we're not writeable. */ + if (priv->tx_throttled) + return 256; + return 0; +} + + +static int keyspan_pda_open (struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + unsigned char room; + int rc; + struct keyspan_pda_private *priv; + priv = (struct keyspan_pda_private *)(port->private); + + if (port->active) { + return -EINVAL; + } + port->active = 1; + + /* find out how much room is in the Tx ring */ + rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0), + 6, /* write_room */ + USB_TYPE_VENDOR | USB_RECIP_INTERFACE + | USB_DIR_IN, + 0, /* value */ + 0, /* index */ + &room, + 1, + 2*HZ); + if (rc < 0) { + dbg(" roomquery failed"); + return rc; /* failed */ + } + if (rc == 0) { + dbg(" roomquery returned 0 bytes"); + return -EIO; /* device didn't return any data */ + } + priv->tx_room = room; + priv->tx_throttled = room ? 0 : 1; + + /* the normal serial device seems to always turn on DTR and RTS here, + so do the same */ + if (port->tty->termios->c_cflag & CBAUD) + keyspan_pda_set_modem_info(serial, (1<<7) | (1<<2) ); + else + keyspan_pda_set_modem_info(serial, 0); + + /*Start reading from the device*/ + if (usb_submit_urb(port->interrupt_in_urb)) + dbg(" usb_submit_urb(read int) failed"); + + return (0); +} + + +static void keyspan_pda_close(struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + + /* the normal serial device seems to always shut off DTR and RTS now */ + if (port->tty->termios->c_cflag & HUPCL) + keyspan_pda_set_modem_info(serial, 0); + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->interrupt_in_urb); + port->active = 0; +} + + +/* download the firmware to a "fake" device (pre-renumeration) */ +static int keyspan_pda_fake_startup (struct usb_serial *serial) +{ + int response; + const struct ezusb_hex_record *record; + + /* download the firmware here ... */ + response = ezusb_set_reset(serial, 1); + + record = &keyspan_pda_firmware[0]; + while(record->address != 0xffff) { + response = ezusb_writememory(serial, record->address, + (unsigned char *)record->data, + record->data_size, 0xa0); + if (response < 0) { + err("ezusb_writememory failed for Keyspan PDA " + "firmware (%d %04X %p %d)", + response, + record->address, record->data, record->data_size); + break; + } + record++; + } + /* bring device out of reset. Renumeration will occur in a moment + and the new device will bind to the real driver */ + response = ezusb_set_reset(serial, 0); + + /* we want this device to fail to have a driver assigned to it. */ + return (1); +} + +static int keyspan_pda_startup (struct usb_serial *serial) +{ + /* allocate the private data structures for all ports. Well, for all + one ports. */ + + serial->port[0].private = kmalloc(sizeof(struct keyspan_pda_private), + GFP_KERNEL); + if (!serial->port[0].private) + return (1); /* error */ + init_waitqueue_head(&serial->port[0].write_wait); + return (0); +} + +static void keyspan_pda_shutdown (struct usb_serial *serial) +{ + kfree(serial->port[0].private); +} + +struct usb_serial_device_type keyspan_pda_fake_device = { + name: "Keyspan PDA - (prerenumeration)", + idVendor: &keyspan_vendor_id, + idProduct: &keyspan_pda_fake_product_id, + needs_interrupt_in: DONT_CARE, + needs_bulk_in: DONT_CARE, + needs_bulk_out: DONT_CARE, + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 1, + startup: keyspan_pda_fake_startup, +}; + +struct usb_serial_device_type keyspan_pda_device = { + name: "Keyspan PDA", + idVendor: &keyspan_vendor_id, + idProduct: &keyspan_pda_product_id, + needs_interrupt_in: MUST_HAVE, + needs_bulk_in: DONT_CARE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 1, + num_bulk_in: 0, + num_bulk_out: 1, + num_ports: 1, + open: keyspan_pda_open, + close: keyspan_pda_close, + write: keyspan_pda_write, + write_room: keyspan_pda_write_room, + write_bulk_callback: keyspan_pda_write_bulk_callback, + read_int_callback: keyspan_pda_rx_interrupt, + chars_in_buffer: keyspan_pda_chars_in_buffer, + throttle: keyspan_pda_rx_throttle, + unthrottle: keyspan_pda_rx_unthrottle, + ioctl: keyspan_pda_ioctl, + set_termios: keyspan_pda_set_termios, + break_ctl: keyspan_pda_break_ctl, + startup: keyspan_pda_startup, + shutdown: keyspan_pda_shutdown, +}; + diff --git a/drivers/usb/serial/keyspan_pda_fw.h b/drivers/usb/serial/keyspan_pda_fw.h new file mode 100644 index 000000000000..5366c0a28f98 --- /dev/null +++ b/drivers/usb/serial/keyspan_pda_fw.h @@ -0,0 +1,93 @@ +/* + * keyspan_pda_fw.h + * + * Generated from keyspan_pda.s by ezusb_convert.pl + * This file is presumed to be under the same copyright as the source file + * from which it was derived. + */ + +static const struct ezusb_hex_record keyspan_pda_firmware[] = { +{ 0x0000, 3, {0x02, 0x02, 0x00} }, +{ 0x0023, 4, {0x02, 0x05, 0x5f, 0x00} }, +{ 0x0043, 4, {0x02, 0x01, 0x00, 0x00} }, +{ 0x0030, 5, {0x00, 0x00, 0x00, 0x00, 0x00} }, +{ 0x0100, 16, {0x02, 0x02, 0x96, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00} }, +{ 0x0110, 16, {0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00} }, +{ 0x0120, 16, {0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x04, 0x61, 0x00, 0x02, 0x04, 0x89, 0x00} }, +{ 0x0200, 16, {0x75, 0x81, 0x5e, 0xe4, 0xf5, 0x32, 0xf5, 0x33, 0xf5, 0x30, 0xf5, 0x31, 0xf5, 0x34, 0xc2, 0x00} }, +{ 0x0210, 16, {0xc2, 0x01, 0xa9, 0x00, 0x74, 0xfe, 0x90, 0x10, 0x00, 0xf0, 0xa3, 0xd9, 0xfc, 0x74, 0xfd, 0x90} }, +{ 0x0220, 16, {0x11, 0x00, 0xf0, 0xa3, 0xd9, 0xfc, 0x74, 0x02, 0x90, 0x7f, 0x9d, 0xf0, 0x74, 0x00, 0x90, 0x7f} }, +{ 0x0230, 16, {0x97, 0xf0, 0x74, 0x86, 0x90, 0x7f, 0x9e, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0x03, 0xf0, 0x90, 0x7f} }, +{ 0x0240, 16, {0xaf, 0xe0, 0xd2, 0xe0, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xab, 0xf0, 0x90, 0x7f, 0xae, 0xf0, 0x90} }, +{ 0x0250, 16, {0x7f, 0xac, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xad, 0x74, 0x04, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x74} }, +{ 0x0260, 16, {0x84, 0x90, 0x7f, 0x98, 0xf0, 0x74, 0x00, 0xf5, 0x98, 0x75, 0xc8, 0x30, 0x7b, 0x05, 0x91, 0x20} }, +{ 0x0270, 16, {0xd2, 0xca, 0x75, 0x98, 0x50, 0xd2, 0xe8, 0xd2, 0xaf, 0xd2, 0xac, 0x74, 0x00, 0xf5, 0x86, 0x90} }, +{ 0x0280, 16, {0x7f, 0xd6, 0x74, 0x02, 0xf0, 0x79, 0x2e, 0x7a, 0x00, 0x7b, 0x00, 0xdb, 0xfe, 0xda, 0xfa, 0xd9} }, +{ 0x0290, 16, {0xf6, 0x74, 0x06, 0xf0, 0x80, 0xfe, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85} }, +{ 0x02a0, 16, {0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xab, 0x74, 0x01, 0xf0, 0x90, 0x7f} }, +{ 0x02b0, 16, {0xe8, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb, 0xa3, 0xe0, 0xfc, 0xe9, 0x54, 0x60, 0xb4} }, +{ 0x02c0, 16, {0x00, 0x03, 0x02, 0x03, 0x39, 0xb4, 0x40, 0x6e, 0xba, 0x00, 0x0b, 0x12, 0x04, 0x20, 0x40, 0x03} }, +{ 0x02d0, 16, {0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x01, 0x03, 0x02, 0x04, 0x0a, 0xba, 0x02, 0x03, 0x02} }, +{ 0x02e0, 16, {0x04, 0x0a, 0xba, 0x03, 0x03, 0x02, 0x04, 0x44, 0xba, 0x04, 0x1e, 0xbb, 0x00, 0x0a, 0x90, 0x7f} }, +{ 0x02f0, 16, {0x95, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 0x98, 0xe0, 0x54, 0xfd, 0xf0, 0x90} }, +{ 0x0300, 16, {0x7f, 0x95, 0xe0, 0x54, 0xfd, 0xf0, 0x02, 0x04, 0x02, 0xba, 0x05, 0x03, 0x02, 0x04, 0x0a, 0xba} }, +{ 0x0310, 16, {0x06, 0x19, 0xbb, 0x00, 0x08, 0xe5, 0x33, 0xd3, 0x95, 0x32, 0x02, 0x03, 0xde, 0xbb, 0x01, 0x08} }, +{ 0x0320, 16, {0xe5, 0x32, 0xc3, 0x95, 0x33, 0x02, 0x03, 0xde, 0x02, 0x04, 0x0a, 0xba, 0x07, 0x05, 0x8b, 0x34} }, +{ 0x0330, 16, {0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0x02, 0x04, 0x0a, 0xba, 0x00, 0x20, 0xb9, 0x80, 0x10, 0x90} }, +{ 0x0340, 16, {0x7f, 0x00, 0xe4, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x04, 0x02, 0xb9} }, +{ 0x0350, 16, {0x82, 0x02, 0x80, 0xeb, 0xb9, 0x81, 0x02, 0x80, 0xe6, 0x02, 0x04, 0x0a, 0xba, 0x01, 0x0f, 0xbb} }, +{ 0x0360, 16, {0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x03} }, +{ 0x0370, 16, {0x0f, 0xbb, 0x00, 0x03, 0x02, 0x04, 0x0a, 0xbb, 0x01, 0x03, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a} }, +{ 0x0380, 16, {0xba, 0x06, 0x56, 0xbc, 0x01, 0x0f, 0x90, 0x7f, 0xd4, 0x74, 0x06, 0xf0, 0x90, 0x7f, 0xd5, 0x74} }, +{ 0x0390, 16, {0x12, 0xf0, 0x02, 0x04, 0x02, 0xbc, 0x02, 0x12, 0xbb, 0x00, 0x6f, 0x90, 0x7f, 0xd4, 0x74, 0x06} }, +{ 0x03a0, 16, {0xf0, 0x90, 0x7f, 0xd5, 0x74, 0x24, 0xf0, 0x02, 0x04, 0x02, 0xbc, 0x03, 0x29, 0x74, 0x04, 0xc3} }, +{ 0x03b0, 16, {0x9b, 0x40, 0x57, 0x60, 0x55, 0xeb, 0x2b, 0x90, 0x06, 0x44, 0x25, 0x82, 0xf5, 0x82, 0x74, 0x00} }, +{ 0x03c0, 16, {0x35, 0x83, 0xf5, 0x83, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0x90, 0x7f, 0xd4, 0xe9, 0xf0, 0x90, 0x7f} }, +{ 0x03d0, 16, {0xd5, 0xea, 0xf0, 0x02, 0x04, 0x02, 0x02, 0x04, 0x0a, 0xba, 0x08, 0x0f, 0x74, 0x01, 0x90, 0x7f} }, +{ 0x03e0, 16, {0x00, 0xf0, 0x74, 0x01, 0x90, 0x7f, 0xb5, 0xf0, 0x02, 0x04, 0x02, 0xba, 0x09, 0x03, 0x02, 0x04} }, +{ 0x03f0, 16, {0x02, 0xba, 0x0a, 0x05, 0x74, 0x00, 0x02, 0x03, 0xde, 0xba, 0x0b, 0x03, 0x02, 0x04, 0x02, 0x02} }, +{ 0x0400, 16, {0x04, 0x0a, 0x90, 0x7f, 0xb4, 0x74, 0x02, 0xf0, 0x80, 0x09, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} }, +{ 0x0410, 16, {0xf0, 0x80, 0x00, 0xd0, 0xe0, 0xd0, 0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32} }, +{ 0x0420, 16, {0xeb, 0x20, 0xe7, 0x1e, 0xc3, 0x94, 0x0a, 0x50, 0x19, 0xeb, 0x23, 0x24, 0xfe, 0xf5, 0x82, 0x74} }, +{ 0x0430, 16, {0x05, 0x34, 0x00, 0xf5, 0x83, 0xe0, 0xf5, 0xcb, 0xf5, 0xcd, 0xa3, 0xe0, 0xf5, 0xca, 0xf5, 0xcc} }, +{ 0x0440, 16, {0xc3, 0x22, 0xd3, 0x22, 0xb9, 0x41, 0x11, 0xeb, 0x64, 0xff, 0x54, 0x84, 0xfb, 0x90, 0x7f, 0x98} }, +{ 0x0450, 16, {0xe0, 0x54, 0x7b, 0x4b, 0xf0, 0x02, 0x04, 0x02, 0x90, 0x7f, 0x9b, 0xe0, 0x64, 0xff, 0x02, 0x03} }, +{ 0x0460, 16, {0xde, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2} }, +{ 0x0470, 16, {0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xa9, 0x74, 0x04, 0xf0, 0x12, 0x05, 0xa0, 0xd0, 0xe0, 0xd0, 0x85} }, +{ 0x0480, 16, {0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0xc0, 0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0} }, +{ 0x0490, 16, {0x84, 0xc0, 0x85, 0xc0, 0xe0, 0xe5, 0x91, 0xc2, 0xe4, 0xf5, 0x91, 0x90, 0x7f, 0xaa, 0x74, 0x04} }, +{ 0x04a0, 16, {0xf0, 0x90, 0x7f, 0xc9, 0xe0, 0xf9, 0xe4, 0xf5, 0x86, 0x90, 0x7d, 0xc0, 0x75, 0x85, 0x10, 0x85} }, +{ 0x04b0, 16, {0x32, 0x84, 0xe0, 0x05, 0x86, 0x05, 0x84, 0xf0, 0xe5, 0x84, 0xb5, 0x33, 0x02, 0x80, 0x09, 0x05} }, +{ 0x04c0, 16, {0x32, 0x05, 0x86, 0xa3, 0xd9, 0xec, 0x80, 0x00, 0x90, 0x7f, 0xc9, 0xf0, 0xb1, 0x31, 0xd0, 0xe0} }, +{ 0x04d0, 16, {0xd0, 0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0xe4, 0xf5, 0x86, 0x90, 0x7f} }, +{ 0x04e0, 16, {0xbc, 0xe0, 0x20, 0xe1, 0x4b, 0x90, 0x7d, 0x00, 0xe5, 0x32, 0xf0, 0xa3, 0xe5, 0x33, 0xf0, 0xa3} }, +{ 0x04f0, 16, {0xe5, 0x30, 0xf0, 0xa3, 0xe5, 0x31, 0xf0, 0xa3, 0xe4, 0x30, 0x00, 0x01, 0x04, 0xf0, 0xa3, 0x05} }, +{ 0x0500, 16, {0x86, 0x90, 0x10, 0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6} }, +{ 0x0510, 16, {0x05, 0x86, 0x74, 0xfc, 0xf0, 0xa3, 0x05, 0x86, 0x90, 0x11, 0x00, 0x79, 0x10, 0xe0, 0xa3, 0x05} }, +{ 0x0520, 16, {0x86, 0xf0, 0xa3, 0x05, 0x86, 0xd9, 0xf6, 0xe4, 0xf5, 0x86, 0x90, 0x7f, 0xbd, 0x74, 0x26, 0xf0} }, +{ 0x0530, 16, {0x22, 0x20, 0x00, 0x13, 0xe5, 0x32, 0xb5, 0x33, 0x01, 0x22, 0x05, 0x33, 0x75, 0x83, 0x10, 0x85} }, +{ 0x0540, 16, {0x33, 0x82, 0xe0, 0xf5, 0x99, 0xd2, 0x00, 0x74, 0x00, 0xb5, 0x34, 0x01, 0x22, 0xe5, 0x33, 0xd3} }, +{ 0x0550, 16, {0x95, 0x32, 0xc3, 0x95, 0x34, 0x40, 0xf5, 0x75, 0x34, 0x00, 0xd2, 0x01, 0x02, 0x05, 0xa0, 0xc0} }, +{ 0x0560, 16, {0x86, 0xc0, 0x82, 0xc0, 0x83, 0xc0, 0x84, 0xc0, 0x85, 0xc0, 0xe0, 0x30, 0x99, 0x07, 0xc2, 0x99} }, +{ 0x0570, 16, {0xc2, 0x00, 0x12, 0x05, 0x34, 0x30, 0x98, 0x05, 0x12, 0x05, 0x8a, 0xc2, 0x98, 0xd0, 0xe0, 0xd0} }, +{ 0x0580, 16, {0x85, 0xd0, 0x84, 0xd0, 0x83, 0xd0, 0x82, 0xd0, 0x86, 0x32, 0x75, 0x83, 0x11, 0x85, 0x30, 0x82} }, +{ 0x0590, 16, {0x05, 0x82, 0xe5, 0x99, 0xf0, 0xe5, 0x82, 0xb5, 0x31, 0x01, 0x22, 0x05, 0x30, 0xb1, 0xa0, 0x22} }, +{ 0x05a0, 16, {0x90, 0x7f, 0xb8, 0xe0, 0x20, 0xe1, 0x38, 0x20, 0x01, 0x36, 0xe5, 0x30, 0xb5, 0x31, 0x01, 0x22} }, +{ 0x05b0, 16, {0xe4, 0xf5, 0x86, 0x75, 0x83, 0x11, 0x05, 0x86, 0x90, 0x7e, 0x00, 0xf0, 0xa3, 0x05, 0x86, 0x79} }, +{ 0x05c0, 16, {0x01, 0xe5, 0x30, 0xb5, 0x31, 0x02, 0x80, 0x10, 0x05, 0x31, 0x85, 0x31, 0x82, 0xe0, 0x05, 0x86} }, +{ 0x05d0, 16, {0xf0, 0xa3, 0x05, 0x86, 0x09, 0xb9, 0x40, 0xe9, 0x90, 0x7f, 0xb9, 0xe9, 0x60, 0x01, 0xf0, 0x22} }, +{ 0x05e0, 16, {0xc2, 0x01, 0xe4, 0xf5, 0x86, 0x90, 0x7e, 0x00, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x02, 0xf0, 0x90} }, +{ 0x05f0, 16, {0x7f, 0xb9, 0xf0, 0x22, 0xc2, 0x99, 0xf5, 0x99, 0x30, 0x99, 0xfd, 0xc2, 0x99, 0x22, 0xe5, 0x5e} }, +{ 0x0600, 16, {0xf6, 0x3c, 0xfd, 0x8f, 0xfe, 0xc8, 0xff, 0x64, 0xff, 0xb2, 0xff, 0xd9, 0xff, 0xed, 0xff, 0xf3} }, +{ 0x0610, 16, {0xff, 0xfa, 0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40, 0xcd, 0x06, 0x04, 0x01, 0x89, 0xab} }, +{ 0x0620, 16, {0x01, 0x02, 0x03, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32, 0x09, 0x04, 0x00} }, +{ 0x0630, 16, {0x00, 0x02, 0xff, 0xff, 0xff, 0x00, 0x07, 0x05, 0x82, 0x03, 0x40, 0x00, 0x01, 0x07, 0x05, 0x02} }, +{ 0x0640, 16, {0x02, 0x40, 0x00, 0x00, 0x06, 0x4c, 0x06, 0x50, 0x06, 0x72, 0x06, 0xa0, 0x04, 0x03, 0x00, 0x00} }, +{ 0x0650, 16, {0x22, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x75, 0x00, 0x73, 0x00} }, +{ 0x0660, 16, {0x62, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00} }, +{ 0x0670, 16, {0x73, 0x00, 0x2e, 0x03, 0x41, 0x00, 0x43, 0x00, 0x4d, 0x00, 0x45, 0x00, 0x20, 0x00, 0x55, 0x00} }, +{ 0x0680, 16, {0x53, 0x00, 0x42, 0x00, 0x20, 0x00, 0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x69, 0x00, 0x61, 0x00} }, +{ 0x0690, 16, {0x6c, 0x00, 0x20, 0x00, 0x77, 0x00, 0x69, 0x00, 0x64, 0x00, 0x67, 0x00, 0x65, 0x00, 0x74, 0x00} }, +{ 0x06a0, 6, {0x06, 0x03, 0x34, 0x00, 0x37, 0x00} }, +{ 0xffff, 0, {0x00} } +}; diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c new file mode 100644 index 000000000000..1809f8bece85 --- /dev/null +++ b/drivers/usb/serial/omninet.c @@ -0,0 +1,337 @@ +/* + * USB ZyXEL omni.net LCD PLUS driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * Please report both successes and troubles to the author at omninet@kroah.com + * + */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define isalpha(x) ( ( x > 96 && x < 123) || ( x > 64 && x < 91) || (x > 47 && x < 58) ) + #define DEBUG +#else + #undef DEBUG +#endif + +#include + +#include "usb-serial.h" + + +#define ZYXEL_VENDOR_ID 0x0586 +#define ZYXEL_OMNINET_ID 0x1000 + +/* function prototypes */ +static int omninet_open (struct usb_serial_port *port, struct file *filp); +static void omninet_close (struct usb_serial_port *port, struct file *filp); +static void omninet_read_bulk_callback (struct urb *urb); +static void omninet_write_bulk_callback (struct urb *urb); +static int omninet_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int omninet_write_room (struct usb_serial_port *port); + +/* All of the device info needed for the omni.net */ +static __u16 zyxel_vendor_id = ZYXEL_VENDOR_ID; +static __u16 zyxel_omninet_product_id = ZYXEL_OMNINET_ID; + +struct usb_serial_device_type zyxel_omninet_device = { + name: "ZyXEL - omni.net lcd plus usb", + idVendor: &zyxel_vendor_id, + idProduct: &zyxel_omninet_product_id, + needs_interrupt_in: MUST_HAVE, + needs_bulk_in: MUST_HAVE, + needs_bulk_out: MUST_HAVE, + num_interrupt_in: 1, + num_bulk_in: 1, + num_bulk_out: 2, + num_ports: 1, + open: omninet_open, + close: omninet_close, + write: omninet_write, + write_room: omninet_write_room, + read_bulk_callback: omninet_read_bulk_callback, + write_bulk_callback: omninet_write_bulk_callback, +}; + + +/* The protocol. + * + * The omni.net always exchange 64 bytes of data with the host. The first + * four bytes are the control header, you can see it in the above structure. + * + * oh_seq is a sequence number. Don't know if/how it's used. + * oh_len is the length of the data bytes in the packet. + * oh_xxx Bit-mapped, related to handshaking and status info. + * I normally set it to 0x03 in trasmitted frames. + * 7: Active when the TA is in a CONNECTed state. + * 6: unknown + * 5: handshaking, unknown + * 4: handshaking, unknown + * 3: unknown, usually 0 + * 2: unknown, usually 0 + * 1: handshaking, unknown, usually set to 1 in trasmitted frames + * 0: handshaking, unknown, usually set to 1 in trasmitted frames + * oh_pad Probably a pad byte. + * + * After the header you will find data bytes if oh_len was greater than zero. + * + */ + +struct omninet_header +{ + __u8 oh_seq; + __u8 oh_len; + __u8 oh_xxx; + __u8 oh_pad; +}; + +struct omninet_data +{ + __u8 od_outseq; // Sequence number for bulk_out URBs +}; + +static int omninet_open (struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + struct usb_serial_port *wport = &serial->port[1]; + struct omninet_data *od; + + dbg("omninet_open port %d", port->number); + + if (port->active) { + dbg ("device already open"); + return -EINVAL; + } + port->active = 1; + + od = kmalloc( sizeof(struct omninet_data), GFP_KERNEL ); + + if( !od ) + { + err("omninet_open: kmalloc(%d) failed.", sizeof(struct omninet_data)); + return -ENOMEM; + } + + port->private = od; + + /* Start reading from the device */ + if (usb_submit_urb(port->read_urb)) + dbg("usb_submit_urb(read bulk, %p) failed", port->read_urb); + + wport->tty = port->tty; + + return (0); +} + +static void omninet_close (struct usb_serial_port *port, struct file * filp) +{ + struct usb_serial *serial = port->serial; + struct usb_serial_port *wport = &serial->port[1]; + struct omninet_data *od = (struct omninet_data *) port->private; + + port->active = 0; + + + dbg("zyxel_close port %d", port->number); + + usb_unlink_urb (wport->write_urb); + usb_unlink_urb (port->read_urb); + + if(od) kfree(od); +} + + +#define OMNINET_DATAOFFSET 0x04 +#define OMNINET_HEADERLEN sizeof(struct omninet_header) +#define OMNINET_BULKOUTSIZE (64 - OMNINET_HEADERLEN) + +static void omninet_read_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = port->serial; + + unsigned char *data = urb->transfer_buffer; + struct omninet_header *header = (struct omninet_header *) &data[0]; + + int i; + +// dbg("omninet_read_bulk_callback"); + + if (port_paranoia_check (port, "omninet_read_bulk_callback")) { + return; + } + + if (serial_paranoia_check (serial, "omninet_read_bulk_callback")) { + return; + } + + if (urb->status) { + dbg("nonzero read bulk status received: %d", urb->status); + return; + } + +#ifdef DEBUG + if(header->oh_xxx != 0x30) + { + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": omninet_read %d: ", header->oh_len); + for (i = 0; i < (header->oh_len + OMNINET_HEADERLEN); i++) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } + } +#endif + + if (urb->actual_length && header->oh_len) + { + for (i = 0; i < header->oh_len; i++) { + tty_insert_flip_char(port->tty, data[OMNINET_DATAOFFSET + i], 0); + } + tty_flip_buffer_push(port->tty); + } + + /* Continue trying to always read */ + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); + + return; +} + +static int omninet_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial *serial = port->serial; + struct usb_serial_port *wport = &serial->port[1]; + + struct omninet_data *od = (struct omninet_data *) port->private; + struct omninet_header *header = (struct omninet_header *) wport->write_urb->transfer_buffer; +/* +#ifdef DEBUG + int i; +#endif +*/ + +// dbg("omninet_write port %d", port->number); + + if (count == 0) { + dbg("write request of 0 bytes"); + return (0); + } +/* +#ifdef DEBUG + printk (KERN_DEBUG __FILE__ ": omninet_write %d: ", count); + for (i = 0; i < count; i++) { + if( isalpha(buf[i]) ) + printk ("%c ", buf[i]); + else + printk ("%.2x ", buf[i]); + } + printk ("\n"); +#endif +*/ + if (wport->write_urb->status == -EINPROGRESS) { + dbg ("already writing"); + return (0); + } + + count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count; + + if (from_user) { + copy_from_user(wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count); + } + else { + memcpy (wport->write_urb->transfer_buffer + OMNINET_DATAOFFSET, buf, count); + } + + + header->oh_seq = od->od_outseq++; + header->oh_len = count; + header->oh_xxx = 0x03; + header->oh_pad = 0x00; + + /* send the data out the bulk port, always 64 bytes */ + wport->write_urb->transfer_buffer_length = 64; + + if (usb_submit_urb(wport->write_urb)) + dbg("usb_submit_urb(write bulk) failed"); + +// dbg("omninet_write returns %d", count); + + return (count); +} + + +static int omninet_write_room (struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + struct usb_serial_port *wport = &serial->port[1]; + + int room = 0; // Default: no room + + if (wport->write_urb->status != -EINPROGRESS) + room = wport->bulk_out_size - OMNINET_HEADERLEN; + +// dbg("omninet_write_room returns %d", room); + + return (room); +} + +static void omninet_write_bulk_callback (struct urb *urb) +{ +/* struct omninet_header *header = (struct omninet_header *) urb->transfer_buffer; */ + struct usb_serial_port *port = (struct usb_serial_port *) urb->context; + struct usb_serial *serial; + struct tty_struct *tty; + +// dbg("omninet_write_bulk_callback, port %0x\n", port); + + + if (port_paranoia_check (port, "omninet_write_bulk_callback")) { + return; + } + + serial = port->serial; + if (serial_paranoia_check (serial, "omninet_write_bulk_callback")) { + return; + } + + if (urb->status) { + dbg("nonzero write bulk status received: %d", urb->status); + return; + } + + tty = port->tty; + + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + +// dbg("omninet_write_bulk_callback, tty %0x\n", tty); + + return; +} + diff --git a/drivers/usb/serial/usb-serial.h b/drivers/usb/serial/usb-serial.h new file mode 100644 index 000000000000..b8e2e1d7445f --- /dev/null +++ b/drivers/usb/serial/usb-serial.h @@ -0,0 +1,183 @@ +/* + * USB Serial Converter driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + */ + + +#ifndef __LINUX_USB_SERIAL_H +#define __LINUX_USB_SERIAL_H + +#include + +#define SERIAL_TTY_MAJOR 188 /* Nice legal number now */ +#define SERIAL_TTY_MINORS 255 /* loads of devices :) */ + +#define MAX_NUM_PORTS 8 /* The maximum number of ports one device can grab at once */ + +#define USB_SERIAL_MAGIC 0x6702 /* magic number for usb_serial struct */ +#define USB_SERIAL_PORT_MAGIC 0x7301 /* magic number for usb_serial_port struct */ + +/* parity check flag */ +#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) + + +struct usb_serial_port { + int magic; + struct usb_serial *serial; /* pointer back to the owner of this port */ + struct tty_struct * tty; /* the coresponding tty for this port */ + unsigned char minor; + unsigned char number; + char active; /* someone has this device open */ + + unsigned char * interrupt_in_buffer; + struct urb * interrupt_in_urb; + + unsigned char * bulk_in_buffer; + struct urb * read_urb; + + unsigned char * bulk_out_buffer; + int bulk_out_size; + struct urb * write_urb; + + wait_queue_head_t write_wait; + + void * private; /* data private to the specific port */ +}; + +struct usb_serial { + int magic; + struct usb_device * dev; + struct usb_serial_device_type * type; /* the type of usb serial device this is */ + struct tty_driver * tty_driver; /* the tty_driver for this device */ + unsigned char minor; /* the starting minor number for this device */ + unsigned char num_ports; /* the number of ports this device has */ + char num_interrupt_in; /* number of interrupt in endpoints we have */ + char num_bulk_in; /* number of bulk in endpoints we have */ + char num_bulk_out; /* number of bulk out endpoints we have */ + struct usb_serial_port port[MAX_NUM_PORTS]; + + void * private; /* data private to the specific driver */ +}; + + +#define MUST_HAVE_NOT 0x01 +#define MUST_HAVE 0x02 +#define DONT_CARE 0x03 + +#define HAS 0x02 +#define HAS_NOT 0x01 + +#define NUM_DONT_CARE (-1) + + +/* This structure defines the individual serial converter. */ +struct usb_serial_device_type { + char *name; + __u16 *idVendor; + __u16 *idProduct; + char needs_interrupt_in; + char needs_bulk_in; + char needs_bulk_out; + char num_interrupt_in; + char num_bulk_in; + char num_bulk_out; + char num_ports; /* number of serial ports this device has */ + + /* function call to make before accepting driver */ + int (*startup) (struct usb_serial *serial); /* return 0 to continue initialization, anything else to abort */ + void (*shutdown) (struct usb_serial *serial); + + /* serial function calls */ + int (*open) (struct usb_serial_port *port, struct file * filp); + void (*close) (struct usb_serial_port *port, struct file * filp); + int (*write) (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); + int (*write_room) (struct usb_serial_port *port); + int (*ioctl) (struct usb_serial_port *port, struct file * file, unsigned int cmd, unsigned long arg); + void (*set_termios) (struct usb_serial_port *port, struct termios * old); + void (*break_ctl) (struct usb_serial_port *port, int break_state); + int (*chars_in_buffer) (struct usb_serial_port *port); + void (*throttle) (struct usb_serial_port *port); + void (*unthrottle) (struct usb_serial_port *port); + + void (*read_int_callback)(struct urb *urb); + void (*read_bulk_callback)(struct urb *urb); + void (*write_bulk_callback)(struct urb *urb); +}; + + + +extern struct usb_serial_device_type handspring_device; +extern struct usb_serial_device_type whiteheat_fake_device; +extern struct usb_serial_device_type whiteheat_device; +extern struct usb_serial_device_type ftdi_sio_device; +extern struct usb_serial_device_type keyspan_pda_fake_device; +extern struct usb_serial_device_type keyspan_pda_device; +extern struct usb_serial_device_type zyxel_omninet_device; +extern struct usb_serial_device_type digi_acceleport_device; + + +/* determine if we should include the EzUSB loader functions */ +#if defined(CONFIG_USB_SERIAL_KEYSPAN_PDA) || defined(CONFIG_USB_SERIAL_WHITEHEAT) + #define USES_EZUSB_FUNCTIONS + extern int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest); + extern int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit); +#else + #undef USES_EZUSB_FUNCTIONS +#endif + + +/* Inline functions to check the sanity of a pointer that is passed to us */ +static inline int serial_paranoia_check (struct usb_serial *serial, const char *function) +{ + if (!serial) { + dbg("%s - serial == NULL", function); + return -1; + } + if (serial->magic != USB_SERIAL_MAGIC) { + dbg("%s - bad magic number for serial", function); + return -1; + } + if (!serial->type) { + dbg("%s - serial->type == NULL!", function); + return -1; + } + + return 0; +} + + +static inline int port_paranoia_check (struct usb_serial_port *port, const char *function) +{ + if (!port) { + dbg("%s - port == NULL", function); + return -1; + } + if (port->magic != USB_SERIAL_PORT_MAGIC) { + dbg("%s - bad magic number for port", function); + return -1; + } + if (!port->serial) { + dbg("%s - port->serial == NULL", function); + return -1; + } + if (!port->tty) { + dbg("%s - port->tty == NULL", function); + return -1; + } + + return 0; +} + + +#endif /* ifdef __LINUX_USB_SERIAL_H */ + diff --git a/drivers/usb/serial/usbserial.c b/drivers/usb/serial/usbserial.c new file mode 100644 index 000000000000..9e2e4ce57692 --- /dev/null +++ b/drivers/usb/serial/usbserial.c @@ -0,0 +1,1285 @@ +/* + * USB Serial Converter driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This driver was originally based on the ACM driver by Armin Fuerst (which was + * based on a driver by Brad Keryan) + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (05/22/2000) gkh + * Changed the makefile, enabling the big CONFIG_USB_SERIAL_SOMTHING to be + * removed from the individual device source files. + * + * (05/03/2000) gkh + * Added the Digi Acceleport driver from Al Borchers and Peter Berger. + * + * (05/02/2000) gkh + * Changed devfs and tty register code to work properly now. This was based on + * the ACM driver changes by Vojtech Pavlik. + * + * (04/27/2000) Ryan VanderBijl + * Put calls to *_paranoia_checks into one function. + * + * (04/23/2000) gkh + * Fixed bug that Randy Dunlap found for Generic devices with no bulk out ports. + * Moved when the startup code printed out the devices that are supported. + * + * (04/19/2000) gkh + * Added driver for ZyXEL omni.net lcd plus ISDN TA + * Made startup info message specify which drivers were compiled in. + * + * (04/03/2000) gkh + * Changed the probe process to remove the module unload races. + * Changed where the tty layer gets initialized to have devfs work nicer. + * Added initial devfs support. + * + * (03/26/2000) gkh + * Split driver up into device specific pieces. + * + * (03/19/2000) gkh + * Fixed oops that could happen when device was removed while a program + * was talking to the device. + * Removed the static urbs and now all urbs are created and destroyed + * dynamically. + * Reworked the internal interface. Now everything is based on the + * usb_serial_port structure instead of the larger usb_serial structure. + * This fixes the bug that a multiport device could not have more than + * one port open at one time. + * + * (03/17/2000) gkh + * Added config option for debugging messages. + * Added patch for keyspan pda from Brian Warner. + * + * (03/06/2000) gkh + * Added the keyspan pda code from Brian Warner + * Moved a bunch of the port specific stuff into its own structure. This + * is in anticipation of the true multiport devices (there's a bug if you + * try to access more than one port of any multiport device right now) + * + * (02/21/2000) gkh + * Made it so that any serial devices only have to specify which functions + * they want to overload from the generic function calls (great, + * inheritance in C, in a driver, just what I wanted...) + * Added support for set_termios and ioctl function calls. No drivers take + * advantage of this yet. + * Removed the #ifdef MODULE, now there is no module specific code. + * Cleaned up a few comments in usb-serial.h that were wrong (thanks again + * to Miles Lott). + * Small fix to get_free_serial. + * + * (02/14/2000) gkh + * Removed the Belkin and Peracom functionality from the driver due to + * the lack of support from the vendor, and me not wanting people to + * accidenatly buy the device, expecting it to work with Linux. + * Added read_bulk_callback and write_bulk_callback to the type structure + * for the needs of the FTDI and WhiteHEAT driver. + * Changed all reverences to FTDI to FTDI_SIO at the request of Bill + * Ryder. + * Changed the output urb size back to the max endpoint size to make + * the ftdi_sio driver have it easier, and due to the fact that it didn't + * really increase the speed any. + * + * (02/11/2000) gkh + * Added VISOR_FUNCTION_CONSOLE to the visor startup function. This was a + * patch from Miles Lott (milos@insync.net). + * Fixed bug with not restoring the minor range that a device grabs, if + * the startup function fails (thanks Miles for finding this). + * + * (02/05/2000) gkh + * Added initial framework for the Keyspan PDA serial converter so that + * Brian Warner has a place to put his code. + * Made the ezusb specific functions generic enough that different + * devices can use them (whiteheat and keyspan_pda both need them). + * Split out a whole bunch of structure and other stuff to a seperate + * usb-serial.h file. + * Made the Visor connection messages a little more understandable, now + * that Miles Lott (milos@insync.net) has gotten the Generic channel to + * work. Also made them always show up in the log file. + * + * (01/25/2000) gkh + * Added initial framework for FTDI serial converter so that Bill Ryder + * has a place to put his code. + * Added the vendor specific info from Handspring. Now we can print out + * informational debug messages as well as understand what is happening. + * + * (01/23/2000) gkh + * Fixed problem of crash when trying to open a port that didn't have a + * device assigned to it. Made the minor node finding a little smarter, + * now it looks to find a continous space for the new device. + * + * (01/21/2000) gkh + * Fixed bug in visor_startup with patch from Miles Lott (milos@insync.net) + * Fixed get_serial_by_minor which was all messed up for multi port + * devices. Fixed multi port problem for generic devices. Now the number + * of ports is determined by the number of bulk out endpoints for the + * generic device. + * + * (01/19/2000) gkh + * Removed lots of cruft that was around from the old (pre urb) driver + * interface. + * Made the serial_table dynamic. This should save lots of memory when + * the number of minor nodes goes up to 256. + * Added initial support for devices that have more than one port. + * Added more debugging comments for the Visor, and added a needed + * set_configuration call. + * + * (01/17/2000) gkh + * Fixed the WhiteHEAT firmware (my processing tool had a bug) + * and added new debug loader firmware for it. + * Removed the put_char function as it isn't really needed. + * Added visor startup commands as found by the Win98 dump. + * + * (01/13/2000) gkh + * Fixed the vendor id for the generic driver to the one I meant it to be. + * + * (01/12/2000) gkh + * Forget the version numbering...that's pretty useless... + * Made the driver able to be compiled so that the user can select which + * converter they want to use. This allows people who only want the Visor + * support to not pay the memory size price of the WhiteHEAT. + * Fixed bug where the generic driver (idVendor=0000 and idProduct=0000) + * grabbed the root hub. Not good. + * + * version 0.4.0 (01/10/2000) gkh + * Added whiteheat.h containing the firmware for the ConnectTech WhiteHEAT + * device. Added startup function to allow firmware to be downloaded to + * a device if it needs to be. + * Added firmware download logic to the WhiteHEAT device. + * Started to add #defines to split up the different drivers for potential + * configuration option. + * + * version 0.3.1 (12/30/99) gkh + * Fixed problems with urb for bulk out. + * Added initial support for multiple sets of endpoints. This enables + * the Handspring Visor to be attached successfully. Only the first + * bulk in / bulk out endpoint pair is being used right now. + * + * version 0.3.0 (12/27/99) gkh + * Added initial support for the Handspring Visor based on a patch from + * Miles Lott (milos@sneety.insync.net) + * Cleaned up the code a bunch and converted over to using urbs only. + * + * version 0.2.3 (12/21/99) gkh + * Added initial support for the Connect Tech WhiteHEAT converter. + * Incremented the number of ports in expectation of getting the + * WhiteHEAT to work properly (4 ports per connection). + * Added notification on insertion and removal of what port the + * device is/was connected to (and what kind of device it was). + * + * version 0.2.2 (12/16/99) gkh + * Changed major number to the new allocated number. We're legal now! + * + * version 0.2.1 (12/14/99) gkh + * Fixed bug that happens when device node is opened when there isn't a + * device attached to it. Thanks to marek@webdesign.no for noticing this. + * + * version 0.2.0 (11/10/99) gkh + * Split up internals to make it easier to add different types of serial + * converters to the code. + * Added a "generic" driver that gets it's vendor and product id + * from when the module is loaded. Thanks to David E. Nelson (dnelson@jump.net) + * for the idea and sample code (from the usb scanner driver.) + * Cleared up any licensing questions by releasing it under the GNU GPL. + * + * version 0.1.2 (10/25/99) gkh + * Fixed bug in detecting device. + * + * version 0.1.1 (10/05/99) gkh + * Changed the major number to not conflict with anything else. + * + * version 0.1 (09/28/99) gkh + * Can recognize the two different devices and start up a read from + * device when asked to. Writes also work. No control signals yet, this + * all is vendor specific data (i.e. no spec), also no control for + * different baud rates or other bit settings. + * Currently we are using the same devid as the acm driver. This needs + * to change. + * + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +/* Module information */ +MODULE_AUTHOR("Greg Kroah-Hartman, greg@kroah.com, http://www.kroah.com/linux-usb/"); +MODULE_DESCRIPTION("USB Serial Driver"); + +#include "usb-serial.h" + +#define MAX(a,b) (((a)>(b))?(a):(b)) + +/* function prototypes for a "generic" type serial converter (no flow control, not all endpoints needed) */ +/* need to always compile these in, as some of the other devices use these functions as their own. */ +/* if a driver does not provide a function pointer, the generic function will be called. */ +static int generic_open (struct usb_serial_port *port, struct file *filp); +static void generic_close (struct usb_serial_port *port, struct file *filp); +static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count); +static int generic_write_room (struct usb_serial_port *port); +static int generic_chars_in_buffer (struct usb_serial_port *port); +static void generic_read_bulk_callback (struct urb *urb); +static void generic_write_bulk_callback (struct urb *urb); + + +#ifdef CONFIG_USB_SERIAL_GENERIC +static __u16 vendor = 0x05f9; +static __u16 product = 0xffff; +MODULE_PARM(vendor, "i"); +MODULE_PARM_DESC(vendor, "User specified USB idVendor"); + +MODULE_PARM(product, "i"); +MODULE_PARM_DESC(product, "User specified USB idProduct"); + +/* All of the device info needed for the Generic Serial Converter */ +static struct usb_serial_device_type generic_device = { + name: "Generic", + idVendor: &vendor, /* use the user specified vendor id */ + idProduct: &product, /* use the user specified product id */ + needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ + needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ + needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 1, +}; +#endif + + +/* To add support for another serial converter, create a usb_serial_device_type + structure for that device, and add it to this list, making sure that the + last entry is NULL. */ +static struct usb_serial_device_type *usb_serial_devices[] = { +#ifdef CONFIG_USB_SERIAL_GENERIC + &generic_device, +#endif +#ifdef CONFIG_USB_SERIAL_WHITEHEAT + &whiteheat_fake_device, + &whiteheat_device, +#endif +#ifdef CONFIG_USB_SERIAL_VISOR + &handspring_device, +#endif +#ifdef CONFIG_USB_SERIAL_FTDI_SIO + &ftdi_sio_device, +#endif +#ifdef CONFIG_USB_SERIAL_KEYSPAN_PDA + &keyspan_pda_fake_device, + &keyspan_pda_device, +#endif +#ifdef CONFIG_USB_SERIAL_OMNINET + &zyxel_omninet_device, +#endif +#ifdef CONFIG_USB_SERIAL_DIGI_ACCELEPORT + &digi_acceleport_device, +#endif + NULL +}; + + +/* local function prototypes */ +static int serial_open (struct tty_struct *tty, struct file * filp); +static void serial_close (struct tty_struct *tty, struct file * filp); +static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count); +static int serial_write_room (struct tty_struct *tty); +static int serial_chars_in_buffer (struct tty_struct *tty); +static void serial_throttle (struct tty_struct * tty); +static void serial_unthrottle (struct tty_struct * tty); +static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg); +static void serial_set_termios (struct tty_struct *tty, struct termios * old); + +static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum); +static void usb_serial_disconnect(struct usb_device *dev, void *ptr); + +static struct usb_driver usb_serial_driver = { + name: "serial", + probe: usb_serial_probe, + disconnect: usb_serial_disconnect, +}; + +static int serial_refcount; +static struct tty_driver serial_tty_driver; +static struct tty_struct * serial_tty[SERIAL_TTY_MINORS]; +static struct termios * serial_termios[SERIAL_TTY_MINORS]; +static struct termios * serial_termios_locked[SERIAL_TTY_MINORS]; +static struct usb_serial *serial_table[SERIAL_TTY_MINORS] = {NULL, }; + + +static inline struct usb_serial* get_usb_serial (struct usb_serial_port *port, const char *function) +{ + /* if no port was specified, or it fails a paranoia check */ + if (!port || + port_paranoia_check (port, function) || + serial_paranoia_check (port->serial, function)) { + /* then say that we dont have a valid usb_serial thing, which will + * end up genrating -ENODEV return values */ + return NULL; + } + + return port->serial; +} + + +static struct usb_serial *get_serial_by_minor (int minor) +{ + return serial_table[minor]; +} + + +static struct usb_serial *get_free_serial (int num_ports, int *minor) +{ + struct usb_serial *serial = NULL; + int i, j; + int good_spot; + + dbg("get_free_serial %d", num_ports); + + *minor = 0; + for (i = 0; i < SERIAL_TTY_MINORS; ++i) { + if (serial_table[i]) + continue; + + good_spot = 1; + for (j = 1; j <= num_ports-1; ++j) + if (serial_table[i+j]) + good_spot = 0; + if (good_spot == 0) + continue; + + if (!(serial = kmalloc(sizeof(struct usb_serial), GFP_KERNEL))) { + err("Out of memory"); + return NULL; + } + memset(serial, 0, sizeof(struct usb_serial)); + serial->magic = USB_SERIAL_MAGIC; + serial_table[i] = serial; + *minor = i; + dbg("minor base = %d", *minor); + for (i = *minor+1; (i < (*minor + num_ports)) && (i < SERIAL_TTY_MINORS); ++i) + serial_table[i] = serial; + return serial; + } + return NULL; +} + + +static void return_serial (struct usb_serial *serial) +{ + int i; + + dbg("return_serial"); + + if (serial == NULL) + return; + + for (i = 0; i < serial->num_ports; ++i) { + serial_table[serial->minor + i] = NULL; + } + + return; +} + + +#ifdef USES_EZUSB_FUNCTIONS +/* EZ-USB Control and Status Register. Bit 0 controls 8051 reset */ +#define CPUCS_REG 0x7F92 + +int ezusb_writememory (struct usb_serial *serial, int address, unsigned char *data, int length, __u8 bRequest) +{ + int result; + unsigned char *transfer_buffer = kmalloc (length, GFP_KERNEL); + +// dbg("ezusb_writememory %x, %d", address, length); + + if (!transfer_buffer) { + err("ezusb_writememory: kmalloc(%d) failed.", length); + return -ENOMEM; + } + memcpy (transfer_buffer, data, length); + result = usb_control_msg (serial->dev, usb_sndctrlpipe(serial->dev, 0), bRequest, 0x40, address, 0, transfer_buffer, length, 300); + kfree (transfer_buffer); + return result; +} + + +int ezusb_set_reset (struct usb_serial *serial, unsigned char reset_bit) +{ + int response; + dbg("ezusb_set_reset: %d", reset_bit); + response = ezusb_writememory (serial, CPUCS_REG, &reset_bit, 1, 0xa0); + if (response < 0) { + err("ezusb_set_reset %d failed", reset_bit); + } + return response; +} + +#endif /* USES_EZUSB_FUNCTIONS */ + + +/***************************************************************************** + * Driver tty interface functions + *****************************************************************************/ +static int serial_open (struct tty_struct *tty, struct file * filp) +{ + struct usb_serial *serial; + struct usb_serial_port *port; + int portNumber; + + dbg("serial_open"); + + /* initialize the pointer incase something fails */ + tty->driver_data = NULL; + + /* get the serial object associated with this tty pointer */ + serial = get_serial_by_minor (MINOR(tty->device)); + + if (serial_paranoia_check (serial, "serial_open")) { + return -ENODEV; + } + + /* set up our port structure making the tty driver remember our port object, and us it */ + portNumber = MINOR(tty->device) - serial->minor; + port = &serial->port[portNumber]; + tty->driver_data = port; + port->tty = tty; + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->open) { + return (serial->type->open(port, filp)); + } else { + return (generic_open(port, filp)); + } +} + + +static void serial_close(struct tty_struct *tty, struct file * filp) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_close"); + + if (!serial) { + return; + } + + dbg("serial_close port %d", port->number); + + if (!port->active) { + dbg ("port not opened"); + return; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->close) { + serial->type->close(port, filp); + } else { + generic_close(port, filp); + } +} + + +static int serial_write (struct tty_struct * tty, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_write"); + + if (!serial) { + return -ENODEV; + } + + dbg("serial_write port %d, %d byte(s)", port->number, count); + + if (!port->active) { + dbg ("port not opened"); + return -EINVAL; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->write) { + return (serial->type->write(port, from_user, buf, count)); + } else { + return (generic_write(port, from_user, buf, count)); + } +} + + +static int serial_write_room (struct tty_struct *tty) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_write_room"); + + if (!serial) { + return -ENODEV; + } + + dbg("serial_write_room port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return -EINVAL; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->write_room) { + return (serial->type->write_room(port)); + } else { + return (generic_write_room(port)); + } +} + + +static int serial_chars_in_buffer (struct tty_struct *tty) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_chars_in_buffer"); + + if (!serial) { + return -ENODEV; + } + + if (!port->active) { + dbg ("port not open"); + return -EINVAL; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->chars_in_buffer) { + return (serial->type->chars_in_buffer(port)); + } else { + return (generic_chars_in_buffer(port)); + } +} + + +static void serial_throttle (struct tty_struct * tty) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_throttle"); + + if (!serial) { + return; + } + + dbg("serial_throttle port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return; + } + + /* pass on to the driver specific version of this function */ + if (serial->type->throttle) { + serial->type->throttle(port); + } + + return; +} + + +static void serial_unthrottle (struct tty_struct * tty) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_unthrottle"); + + if (!serial) { + return; + } + + dbg("serial_unthrottle port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return; + } + + /* pass on to the driver specific version of this function */ + if (serial->type->unthrottle) { + serial->type->unthrottle(port); + } + + return; +} + + +static int serial_ioctl (struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_ioctl"); + + if (!serial) { + return -ENODEV; + } + + dbg("serial_ioctl port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return -ENODEV; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->ioctl) { + return (serial->type->ioctl(port, file, cmd, arg)); + } else { + return -ENOIOCTLCMD; + } +} + + +static void serial_set_termios (struct tty_struct *tty, struct termios * old) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_set_termios"); + + if (!serial) { + return; + } + + dbg("serial_set_termios port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return; + } + + /* pass on to the driver specific version of this function if it is available */ + if (serial->type->set_termios) { + serial->type->set_termios(port, old); + } + + return; +} + + +static void serial_break (struct tty_struct *tty, int break_state) +{ + struct usb_serial_port *port = (struct usb_serial_port *) tty->driver_data; + struct usb_serial *serial = get_usb_serial (port, "serial_break"); + + if (!serial) { + return; + } + + dbg("serial_break port %d", port->number); + + if (!port->active) { + dbg ("port not open"); + return; + } + + /* pass on to the driver specific version of this function if it is + available */ + if (serial->type->break_ctl) { + serial->type->break_ctl(port, break_state); + } +} + + + +/***************************************************************************** + * generic devices specific driver functions + *****************************************************************************/ +static int generic_open (struct usb_serial_port *port, struct file *filp) +{ + struct usb_serial *serial = port->serial; + + dbg("generic_open port %d", port->number); + + if (port->active) { + dbg ("device already open"); + return -EINVAL; + } + port->active = 1; + + /* if we have a bulk interrupt, start reading from it */ + if (serial->num_bulk_in) { + /*Start reading from the device*/ + if (usb_submit_urb(port->read_urb)) + dbg("usb_submit_urb(read bulk) failed"); + } + + return (0); +} + + +static void generic_close (struct usb_serial_port *port, struct file * filp) +{ + struct usb_serial *serial = port->serial; + + dbg("generic_close port %d", port->number); + + /* shutdown any bulk reads that might be going on */ + if (serial->num_bulk_out) { + usb_unlink_urb (port->write_urb); + } + if (serial->num_bulk_in) { + usb_unlink_urb (port->read_urb); + } + + port->active = 0; +} + + +static int generic_write (struct usb_serial_port *port, int from_user, const unsigned char *buf, int count) +{ + struct usb_serial *serial = port->serial; + + dbg("generic_serial_write port %d", port->number); + + if (count == 0) { + dbg("write request of 0 bytes"); + return (0); + } + + /* only do something if we have a bulk out endpoint */ + if (serial->num_bulk_out) { + if (port->write_urb->status == -EINPROGRESS) { + dbg ("already writing"); + return (0); + } + + count = (count > port->bulk_out_size) ? port->bulk_out_size : count; + + if (from_user) { + copy_from_user(port->write_urb->transfer_buffer, buf, count); + } + else { + memcpy (port->write_urb->transfer_buffer, buf, count); + } + + /* send the data out the bulk port */ + port->write_urb->transfer_buffer_length = count; + + if (usb_submit_urb(port->write_urb)) + dbg("usb_submit_urb(write bulk) failed"); + + return (count); + } + + /* no bulk out, so return 0 bytes written */ + return (0); +} + + +static int generic_write_room (struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + int room; + + dbg("generic_write_room port %d", port->number); + + if (serial->num_bulk_out) { + if (port->write_urb->status == -EINPROGRESS) + room = 0; + else + room = port->bulk_out_size; + dbg("generic_write_room returns %d", room); + return (room); + } + + return (0); +} + + +static int generic_chars_in_buffer (struct usb_serial_port *port) +{ + struct usb_serial *serial = port->serial; + + dbg("generic_chars_in_buffer port %d", port->number); + + if (serial->num_bulk_out) { + if (port->write_urb->status == -EINPROGRESS) { + return (port->bulk_out_size); + } + } + + return (0); +} + + +static void generic_read_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = get_usb_serial (port, "generic_read_bulk_callback"); + struct tty_struct *tty; + unsigned char *data = urb->transfer_buffer; + int i; + + if (!serial) { + return; + } + + if (urb->status) { + dbg("nonzero read bulk status received: %d", urb->status); + return; + } + +#ifdef DEBUG + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } +#endif + + tty = port->tty; + if (urb->actual_length) { + for (i = 0; i < urb->actual_length ; ++i) { + tty_insert_flip_char(tty, data[i], 0); + } + tty_flip_buffer_push(tty); + } + + /* Continue trying to always read */ + if (usb_submit_urb(urb)) + dbg("failed resubmitting read urb"); + + return; +} + + +static void generic_write_bulk_callback (struct urb *urb) +{ + struct usb_serial_port *port = (struct usb_serial_port *)urb->context; + struct usb_serial *serial = get_usb_serial (port, "generic_write_bulk_callback"); + struct tty_struct *tty; + + if (!serial) { + return; + } + + if (urb->status) { + dbg("nonzero write bulk status received: %d", urb->status); + return; + } + + tty = port->tty; + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); + + wake_up_interruptible(&tty->write_wait); + + return; +} + + +static void * usb_serial_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_serial *serial = NULL; + struct usb_serial_port *port; + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_endpoint_descriptor *interrupt_in_endpoint[MAX_NUM_PORTS]; + struct usb_endpoint_descriptor *bulk_in_endpoint[MAX_NUM_PORTS]; + struct usb_endpoint_descriptor *bulk_out_endpoint[MAX_NUM_PORTS]; + struct usb_serial_device_type *type; + int device_num; + int minor; + int buffer_size; + int i; + char interrupt_pipe; + char bulk_in_pipe; + char bulk_out_pipe; + int num_interrupt_in = 0; + int num_bulk_in = 0; + int num_bulk_out = 0; + int num_ports; + int max_endpoints; + + /* loop through our list of known serial converters, and see if this + device matches. */ + for (device_num = 0; usb_serial_devices[device_num]; device_num++) { + type = usb_serial_devices[device_num]; + dbg ("Looking at %s Vendor id=%.4x Product id=%.4x", + type->name, *(type->idVendor), *(type->idProduct)); + + /* look at the device descriptor */ + if ((dev->descriptor.idVendor == *(type->idVendor)) && + (dev->descriptor.idProduct == *(type->idProduct))) { + dbg("descriptor matches"); + break; + } + } + if (!usb_serial_devices[device_num]) { + /* no match */ + dbg("none matched"); + return(NULL); + } + + /* descriptor matches, let's find the endpoints needed */ + interrupt_pipe = bulk_in_pipe = bulk_out_pipe = HAS_NOT; + + /* check out the endpoints */ + interface = &dev->actconfig->interface[ifnum].altsetting[0]; + for (i = 0; i < interface->bNumEndpoints; ++i) { + endpoint = &interface->endpoint[i]; + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk in endpoint */ + dbg("found bulk in"); + bulk_in_pipe = HAS; + bulk_in_endpoint[num_bulk_in] = endpoint; + ++num_bulk_in; + } + + if (((endpoint->bEndpointAddress & 0x80) == 0x00) && + ((endpoint->bmAttributes & 3) == 0x02)) { + /* we found a bulk out endpoint */ + dbg("found bulk out"); + bulk_out_pipe = HAS; + bulk_out_endpoint[num_bulk_out] = endpoint; + ++num_bulk_out; + } + + if ((endpoint->bEndpointAddress & 0x80) && + ((endpoint->bmAttributes & 3) == 0x03)) { + /* we found a interrupt in endpoint */ + dbg("found interrupt in"); + interrupt_pipe = HAS; + interrupt_in_endpoint[num_interrupt_in] = endpoint; + ++num_interrupt_in; + } + } + + /* verify that we found all of the endpoints that we need */ + if (!((interrupt_pipe & type->needs_interrupt_in) && + (bulk_in_pipe & type->needs_bulk_in) && + (bulk_out_pipe & type->needs_bulk_out))) { + /* nope, they don't match what we expected */ + info("descriptors matched, but endpoints did not"); + return NULL; + } + + /* found all that we need */ + MOD_INC_USE_COUNT; + info("%s converter detected", type->name); + +#ifdef CONFIG_USB_SERIAL_GENERIC + if (type == &generic_device) { + num_ports = num_bulk_out; + if (num_ports == 0) { + err("Generic device with no bulk out, not allowed."); + MOD_DEC_USE_COUNT; + return NULL; + } + } else +#endif + num_ports = type->num_ports; + + serial = get_free_serial (num_ports, &minor); + if (serial == NULL) { + err("No more free serial devices"); + MOD_DEC_USE_COUNT; + return NULL; + } + + serial->dev = dev; + serial->type = type; + serial->minor = minor; + serial->num_ports = num_ports; + serial->num_bulk_in = num_bulk_in; + serial->num_bulk_out = num_bulk_out; + serial->num_interrupt_in = num_interrupt_in; + + /* if this device type has a startup function, call it */ + if (type->startup) { + if (type->startup (serial)) { + goto probe_error; + } + } + + /* set up the endpoint information */ + for (i = 0; i < num_bulk_in; ++i) { + endpoint = bulk_in_endpoint[i]; + port = &serial->port[i]; + port->read_urb = usb_alloc_urb (0); + if (!port->read_urb) { + err("No free urbs available"); + goto probe_error; + } + buffer_size = endpoint->wMaxPacketSize; + port->bulk_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!port->bulk_in_buffer) { + err("Couldn't allocate bulk_in_buffer"); + goto probe_error; + } + FILL_BULK_URB(port->read_urb, dev, + usb_rcvbulkpipe(dev, endpoint->bEndpointAddress), + port->bulk_in_buffer, buffer_size, + ((serial->type->read_bulk_callback) ? + serial->type->read_bulk_callback : + generic_read_bulk_callback), + port); + } + + for (i = 0; i < num_bulk_out; ++i) { + endpoint = bulk_out_endpoint[i]; + port = &serial->port[i]; + port->write_urb = usb_alloc_urb(0); + if (!port->write_urb) { + err("No free urbs available"); + goto probe_error; + } + buffer_size = endpoint->wMaxPacketSize; + port->bulk_out_size = buffer_size; + port->bulk_out_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!port->bulk_out_buffer) { + err("Couldn't allocate bulk_out_buffer"); + goto probe_error; + } + FILL_BULK_URB(port->write_urb, dev, + usb_sndbulkpipe(dev, endpoint->bEndpointAddress), + port->bulk_out_buffer, buffer_size, + ((serial->type->write_bulk_callback) ? + serial->type->write_bulk_callback : + generic_write_bulk_callback), + port); + } + + for (i = 0; i < num_interrupt_in; ++i) { + endpoint = interrupt_in_endpoint[i]; + port = &serial->port[i]; + port->interrupt_in_urb = usb_alloc_urb(0); + if (!port->interrupt_in_urb) { + err("No free urbs available"); + goto probe_error; + } + buffer_size = endpoint->wMaxPacketSize; + port->interrupt_in_buffer = kmalloc (buffer_size, GFP_KERNEL); + if (!port->interrupt_in_buffer) { + err("Couldn't allocate interrupt_in_buffer"); + goto probe_error; + } + FILL_INT_URB(port->interrupt_in_urb, dev, + usb_rcvintpipe(dev, endpoint->bEndpointAddress), + port->interrupt_in_buffer, buffer_size, + serial->type->read_int_callback, + port, + endpoint->bInterval); + } + + /* initialize some parts of the port structures */ + /* we don't use num_ports here cauz some devices have more endpoint pairs than ports */ + max_endpoints = MAX(num_bulk_in, num_bulk_out); + max_endpoints = MAX(max_endpoints, num_interrupt_in); + for (i = 0; i < max_endpoints; ++i) { + port = &serial->port[i]; + port->number = i + serial->minor; + port->serial = serial; + port->magic = USB_SERIAL_PORT_MAGIC; + } + + /* initialize the devfs nodes for this device and let the user know what ports we are bound to */ + for (i = 0; i < serial->num_ports; ++i) { + tty_register_devfs (&serial_tty_driver, 0, serial->port[i].number); + info("%s converter now attached to ttyUSB%d (or usb/tts/%d for devfs)", + type->name, serial->port[i].number, serial->port[i].number); + } + + return serial; /* success */ + + +probe_error: + for (i = 0; i < num_bulk_in; ++i) { + port = &serial->port[i]; + if (port->read_urb) + usb_free_urb (port->read_urb); + if (port->bulk_in_buffer) + kfree (port->bulk_in_buffer); + } + for (i = 0; i < num_bulk_out; ++i) { + port = &serial->port[i]; + if (port->write_urb) + usb_free_urb (port->write_urb); + if (port->bulk_out_buffer) + kfree (port->bulk_out_buffer); + } + for (i = 0; i < num_interrupt_in; ++i) { + port = &serial->port[i]; + if (port->interrupt_in_urb) + usb_free_urb (port->interrupt_in_urb); + if (port->interrupt_in_buffer) + kfree (port->interrupt_in_buffer); + } + + /* return the minor range that this device had */ + return_serial (serial); + + /* free up any memory that we allocated */ + kfree (serial); + MOD_DEC_USE_COUNT; + return NULL; +} + + +static void usb_serial_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_serial *serial = (struct usb_serial *) ptr; + struct usb_serial_port *port; + int i; + + if (serial) { + if (serial->type->shutdown) + serial->type->shutdown(serial); + + for (i = 0; i < serial->num_ports; ++i) + serial->port[i].active = 0; + + for (i = 0; i < serial->num_bulk_in; ++i) { + port = &serial->port[i]; + if (port->read_urb) { + usb_unlink_urb (port->read_urb); + usb_free_urb (port->read_urb); + } + if (port->bulk_in_buffer) + kfree (port->bulk_in_buffer); + } + for (i = 0; i < serial->num_bulk_out; ++i) { + port = &serial->port[i]; + if (port->write_urb) { + usb_unlink_urb (port->write_urb); + usb_free_urb (port->write_urb); + } + if (port->bulk_out_buffer) + kfree (port->bulk_out_buffer); + } + for (i = 0; i < serial->num_interrupt_in; ++i) { + port = &serial->port[i]; + if (port->interrupt_in_urb) { + usb_unlink_urb (port->interrupt_in_urb); + usb_free_urb (port->interrupt_in_urb); + } + if (port->interrupt_in_buffer) + kfree (port->interrupt_in_buffer); + } + + for (i = 0; i < serial->num_ports; ++i) { + tty_unregister_devfs (&serial_tty_driver, serial->port[i].number); + info("%s converter now disconnected from ttyUSB%d", serial->type->name, serial->port[i].number); + } + + /* return the minor range that this device had */ + return_serial (serial); + + /* free up any memory that we allocated */ + kfree (serial); + + } else { + info("device disconnected"); + } + + MOD_DEC_USE_COUNT; +} + + +static struct tty_driver serial_tty_driver = { + magic: TTY_DRIVER_MAGIC, + driver_name: "usb-serial", + name: "usb/tts/%d", + major: SERIAL_TTY_MAJOR, + minor_start: 0, + num: SERIAL_TTY_MINORS, + type: TTY_DRIVER_TYPE_SERIAL, + subtype: SERIAL_TYPE_NORMAL, + flags: TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS, + + refcount: &serial_refcount, + table: serial_tty, + termios: serial_termios, + termios_locked: serial_termios_locked, + + open: serial_open, + close: serial_close, + write: serial_write, + write_room: serial_write_room, + ioctl: serial_ioctl, + set_termios: serial_set_termios, + throttle: serial_throttle, + unthrottle: serial_unthrottle, + break_ctl: serial_break, + chars_in_buffer: serial_chars_in_buffer, +}; + + +int usb_serial_init(void) +{ + int i; + int something; + int result; + + /* Initalize our global data */ + for (i = 0; i < SERIAL_TTY_MINORS; ++i) { + serial_table[i] = NULL; + } + + /* tell the world what devices this driver currently supports */ + something = 0; + for (i = 0; usb_serial_devices[i]; ++i) { + if (!strstr (usb_serial_devices[i]->name, "prerenumeration")) { + info ("USB Serial support registered for %s", usb_serial_devices[i]->name); + something = 1; + } + } + if (!something) + info ("USB Serial driver is not configured for any devices!"); + + /* register the tty driver */ + serial_tty_driver.init_termios = tty_std_termios; + serial_tty_driver.init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL; + if (tty_register_driver (&serial_tty_driver)) { + err("failed to register tty driver"); + return -1; + } + + /* register the USB driver */ + result = usb_register(&usb_serial_driver); + if (result < 0) { + tty_unregister_driver(&serial_tty_driver); + err("usb_register failed for the usb-serial driver. Error number %d", result); + return -1; + } + + return 0; +} + + +void usb_serial_exit(void) +{ + usb_deregister(&usb_serial_driver); + tty_unregister_driver(&serial_tty_driver); +} + + +module_init(usb_serial_init); +module_exit(usb_serial_exit); + + diff --git a/drivers/usb/serial/visor.c b/drivers/usb/serial/visor.c new file mode 100644 index 000000000000..95a0f883d3f0 --- /dev/null +++ b/drivers/usb/serial/visor.c @@ -0,0 +1,208 @@ +/* + * USB HandSpring Visor driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (04/27/2000) Ryan VanderBijl + * Fixed memory leak in visor_close + * + * (03/26/2000) gkh + * Split driver up into device specific pieces. + * + */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +#include "usb-serial.h" + +#include "visor.h" + + +/* function prototypes for a handspring visor */ +static int visor_open (struct usb_serial_port *port, struct file *filp); +static void visor_close (struct usb_serial_port *port, struct file *filp); +static void visor_throttle (struct usb_serial_port *port); +static void visor_unthrottle (struct usb_serial_port *port); +static int visor_startup (struct usb_serial *serial); + +/* All of the device info needed for the Handspring Visor */ +static __u16 handspring_vendor_id = HANDSPRING_VENDOR_ID; +static __u16 handspring_product_id = HANDSPRING_VISOR_ID; +struct usb_serial_device_type handspring_device = { + name: "Handspring Visor", + idVendor: &handspring_vendor_id, /* the Handspring vendor ID */ + idProduct: &handspring_product_id, /* the Handspring Visor product id */ + needs_interrupt_in: MUST_HAVE_NOT, /* this device must not have an interrupt in endpoint */ + needs_bulk_in: MUST_HAVE, /* this device must have a bulk in endpoint */ + needs_bulk_out: MUST_HAVE, /* this device must have a bulk out endpoint */ + num_interrupt_in: 0, + num_bulk_in: 2, + num_bulk_out: 2, + num_ports: 2, + open: visor_open, + close: visor_close, + throttle: visor_throttle, + unthrottle: visor_unthrottle, + startup: visor_startup, +}; + + +/****************************************************************************** + * Handspring Visor specific driver functions + ******************************************************************************/ +static int visor_open (struct usb_serial_port *port, struct file *filp) +{ + dbg("visor_open port %d", port->number); + + if (port->active) { + dbg ("device already open"); + return -EINVAL; + } + + port->active = 1; + + /*Start reading from the device*/ + if (usb_submit_urb(port->read_urb)) + dbg("usb_submit_urb(read bulk) failed"); + + return (0); +} + + +static void visor_close (struct usb_serial_port *port, struct file * filp) +{ + struct usb_serial *serial = port->serial; + unsigned char *transfer_buffer = kmalloc (0x12, GFP_KERNEL); + + dbg("visor_close port %d", port->number); + + if (!transfer_buffer) { + err("visor_close: kmalloc(%d) failed.", 0x12); + } else { + /* send a shutdown message to the device */ + usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_CLOSE_NOTIFICATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); + kfree (transfer_buffer); + } + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + port->active = 0; +} + + +static void visor_throttle (struct usb_serial_port *port) +{ + dbg("visor_throttle port %d", port->number); + + usb_unlink_urb (port->read_urb); + + return; +} + + +static void visor_unthrottle (struct usb_serial_port *port) +{ + dbg("visor_unthrottle port %d", port->number); + + if (usb_unlink_urb (port->read_urb)) + dbg("usb_submit_urb(read bulk) failed"); + + return; +} + + +static int visor_startup (struct usb_serial *serial) +{ + int response; + int i; + unsigned char *transfer_buffer = kmalloc (256, GFP_KERNEL); + + if (!transfer_buffer) { + err("visor_startup: kmalloc(%d) failed.", 256); + return -ENOMEM; + } + + dbg("visor_startup"); + + dbg("visor_setup: Set config to 1"); + usb_set_configuration (serial->dev, 1); + + /* send a get connection info request */ + response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_GET_CONNECTION_INFORMATION, + 0xc2, 0x0000, 0x0000, transfer_buffer, 0x12, 300); + if (response < 0) { + err("visor_startup: error getting connection information"); + } else { + struct visor_connection_info *connection_info = (struct visor_connection_info *)transfer_buffer; + char *string; + info("%s: Number of ports: %d", serial->type->name, connection_info->num_ports); + for (i = 0; i < connection_info->num_ports; ++i) { + switch (connection_info->connections[i].port_function_id) { + case VISOR_FUNCTION_GENERIC: + string = "Generic"; + break; + case VISOR_FUNCTION_DEBUGGER: + string = "Debugger"; + break; + case VISOR_FUNCTION_HOTSYNC: + string = "HotSync"; + break; + case VISOR_FUNCTION_CONSOLE: + string = "Console"; + break; + case VISOR_FUNCTION_REMOTE_FILE_SYS: + string = "Remote File System"; + break; + default: + string = "unknown"; + break; + } + info("%s: port %d, is for %s use and is bound to ttyUSB%d", serial->type->name, connection_info->connections[i].port, string, serial->minor + i); + } + } + + /* ask for the number of bytes available, but ignore the response as it is broken */ + response = usb_control_msg (serial->dev, usb_rcvctrlpipe(serial->dev, 0), VISOR_REQUEST_BYTES_AVAILABLE, + 0xc2, 0x0000, 0x0005, transfer_buffer, 0x02, 300); + if (response < 0) { + err("visor_startup: error getting bytes available request"); + } + + kfree (transfer_buffer); + + /* continue on with initialization */ + return (0); +} + diff --git a/drivers/usb/serial/visor.h b/drivers/usb/serial/visor.h new file mode 100644 index 000000000000..6407a7811d43 --- /dev/null +++ b/drivers/usb/serial/visor.h @@ -0,0 +1,74 @@ +/* + * USB HandSpring Visor driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + */ + +#ifndef __LINUX_USB_SERIAL_VISOR_H +#define __LINUX_USB_SERIAL_VISOR_H + + +#define HANDSPRING_VENDOR_ID 0x082d +#define HANDSPRING_VISOR_ID 0x0100 + +/**************************************************************************** + * Handspring Visor Vendor specific request codes (bRequest values) + * A big thank you to Handspring for providing the following information. + * If anyone wants the original file where these values and structures came + * from, send email to . + ****************************************************************************/ + +/**************************************************************************** + * VISOR_REQUEST_BYTES_AVAILABLE asks the visor for the number of bytes that + * are available to be transfered to the host for the specified endpoint. + * Currently this is not used, and always returns 0x0001 + ****************************************************************************/ +#define VISOR_REQUEST_BYTES_AVAILABLE 0x01 + +/**************************************************************************** + * VISOR_CLOSE_NOTIFICATION is set to the device to notify it that the host + * is now closing the pipe. An empty packet is sent in response. + ****************************************************************************/ +#define VISOR_CLOSE_NOTIFICATION 0x02 + +/**************************************************************************** + * VISOR_GET_CONNECTION_INFORMATION is sent by the host during enumeration to + * get the endpoints used by the connection. + ****************************************************************************/ +#define VISOR_GET_CONNECTION_INFORMATION 0x03 + + +/**************************************************************************** + * VISOR_GET_CONNECTION_INFORMATION returns data in the following format + ****************************************************************************/ +struct visor_connection_info { + __u16 num_ports; + struct { + __u8 port_function_id; + __u8 port; + } connections[2]; +}; + + +/* struct visor_connection_info.connection[x].port defines: */ +#define VISOR_ENDPOINT_1 0x01 +#define VISOR_ENDPOINT_2 0x02 + +/* struct visor_connection_info.connection[x].port_function_id defines: */ +#define VISOR_FUNCTION_GENERIC 0x00 +#define VISOR_FUNCTION_DEBUGGER 0x01 +#define VISOR_FUNCTION_HOTSYNC 0x02 +#define VISOR_FUNCTION_CONSOLE 0x03 +#define VISOR_FUNCTION_REMOTE_FILE_SYS 0x04 + +#endif + diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c new file mode 100644 index 000000000000..4b911de284e8 --- /dev/null +++ b/drivers/usb/serial/whiteheat.c @@ -0,0 +1,408 @@ +/* + * USB ConnectTech WhiteHEAT driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + * (05/04/2000) gkh + * First cut at open and close commands. Data can flow through the ports at + * default speeds now. + * + * (03/26/2000) gkh + * Split driver up into device specific pieces. + * + */ + +#define __NO_VERSION__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_USB_SERIAL_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +#include "usb-serial.h" + +#include "whiteheat_fw.h" /* firmware for the ConnectTech WhiteHEAT device */ + +#include "whiteheat.h" /* WhiteHEAT specific commands */ + +#define CONNECT_TECH_VENDOR_ID 0x0710 +#define CONNECT_TECH_FAKE_WHITE_HEAT_ID 0x0001 +#define CONNECT_TECH_WHITE_HEAT_ID 0x8001 + +/* function prototypes for the Connect Tech WhiteHEAT serial converter */ +static int whiteheat_open (struct usb_serial_port *port, struct file *filp); +static void whiteheat_close (struct usb_serial_port *port, struct file *filp); +static void whiteheat_set_termios (struct usb_serial_port *port, struct termios * old); +static void whiteheat_throttle (struct usb_serial_port *port); +static void whiteheat_unthrottle (struct usb_serial_port *port); +static int whiteheat_startup (struct usb_serial *serial); +static void whiteheat_shutdown (struct usb_serial *serial); + +/* All of the device info needed for the Connect Tech WhiteHEAT */ +static __u16 connecttech_vendor_id = CONNECT_TECH_VENDOR_ID; +static __u16 connecttech_whiteheat_fake_product_id = CONNECT_TECH_FAKE_WHITE_HEAT_ID; +static __u16 connecttech_whiteheat_product_id = CONNECT_TECH_WHITE_HEAT_ID; +struct usb_serial_device_type whiteheat_fake_device = { + name: "Connect Tech - WhiteHEAT - (prerenumeration)", + idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ + idProduct: &connecttech_whiteheat_fake_product_id, /* the White Heat initial product id */ + needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ + needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ + needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 1, + startup: whiteheat_startup +}; +struct usb_serial_device_type whiteheat_device = { + name: "Connect Tech - WhiteHEAT", + idVendor: &connecttech_vendor_id, /* the Connect Tech vendor id */ + idProduct: &connecttech_whiteheat_product_id, /* the White Heat real product id */ + needs_interrupt_in: DONT_CARE, /* don't have to have an interrupt in endpoint */ + needs_bulk_in: DONT_CARE, /* don't have to have a bulk in endpoint */ + needs_bulk_out: DONT_CARE, /* don't have to have a bulk out endpoint */ + num_interrupt_in: NUM_DONT_CARE, + num_bulk_in: NUM_DONT_CARE, + num_bulk_out: NUM_DONT_CARE, + num_ports: 4, + open: whiteheat_open, + close: whiteheat_close, + throttle: whiteheat_throttle, + unthrottle: whiteheat_unthrottle, + set_termios: whiteheat_set_termios, + shutdown: whiteheat_shutdown, +}; + +struct whiteheat_private { + __u8 command_finished; + wait_queue_head_t wait_command; /* for handling sleeping while waiting for a command to finish */ +}; + + +#define COMMAND_PORT 4 +#define COMMAND_TIMEOUT (2*HZ) /* 2 second timeout for a command */ + +/***************************************************************************** + * Connect Tech's White Heat specific driver functions + *****************************************************************************/ +static void command_port_write_callback (struct urb *urb) +{ + unsigned char *data = urb->transfer_buffer; +#ifdef DEBUG + int i; +#endif + + dbg ("command_port_write_callback"); + + if (urb->status) { + dbg ("nonzero urb status: %d", urb->status); + return; + } + +#ifdef DEBUG + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } +#endif + + return; +} + + +static void command_port_read_callback (struct urb *urb) +{ + struct whiteheat_private *info = (struct whiteheat_private *)urb->context; + unsigned char *data = urb->transfer_buffer; +#ifdef DEBUG + int i; +#endif + + dbg ("command_port_write_callback"); + + if (urb->status) { + dbg ("nonzero urb status: %d", urb->status); + return; + } + +#ifdef DEBUG + if (urb->actual_length) { + printk (KERN_DEBUG __FILE__ ": data read - length = %d, data = ", urb->actual_length); + for (i = 0; i < urb->actual_length; ++i) { + printk ("%.2x ", data[i]); + } + printk ("\n"); + } +#endif + + /* right now, if the command is COMMAND_COMPLETE, just flip the bit saying the command finished */ + /* in the future we're going to have to pay attention to the actual command that completed */ + if (data[0] == WHITEHEAT_CMD_COMPLETE) { + info->command_finished = TRUE; + } + + return; +} + + +static int whiteheat_send_cmd (struct usb_serial *serial, __u8 command, __u8 *data, __u8 datasize) +{ + struct whiteheat_private *info; + struct usb_serial_port *port; + int timeout; + __u8 *transfer_buffer; + + dbg("whiteheat_send_cmd: %d", command); + + port = &serial->port[COMMAND_PORT]; + info = (struct whiteheat_private *)port->private; + info->command_finished = FALSE; + + transfer_buffer = (__u8 *)port->write_urb->transfer_buffer; + transfer_buffer[0] = command; + memcpy (&transfer_buffer[1], data, datasize); + port->write_urb->transfer_buffer_length = datasize + 1; + if (usb_submit_urb (port->write_urb)) + dbg ("submit urb failed"); + + /* wait for the command to complete */ + timeout = COMMAND_TIMEOUT; + while (timeout && (info->command_finished == FALSE)) { + timeout = interruptible_sleep_on_timeout (&info->wait_command, timeout); + } + + if (info->command_finished == FALSE) { + return -1; + } + + return 0; +} + + +static int whiteheat_open (struct usb_serial_port *port, struct file *filp) +{ + struct whiteheat_min_set open_command; + struct usb_serial_port *command_port; + struct whiteheat_private *info; + + dbg("whiteheat_open port %d", port->number); + + if (port->active) { + dbg ("device already open"); + return -EINVAL; + } + port->active = 1; + + /* set up some stuff for our command port */ + command_port = &port->serial->port[COMMAND_PORT]; + if (command_port->private == NULL) { + info = (struct whiteheat_private *)kmalloc (sizeof(struct whiteheat_private), GFP_KERNEL); + if (info == NULL) { + err("out of memory"); + return -ENOMEM; + } + + init_waitqueue_head(&info->wait_command); + command_port->private = info; + command_port->write_urb->complete = command_port_write_callback; + command_port->read_urb->complete = command_port_read_callback; + usb_submit_urb (command_port->read_urb); + } + + /* Start reading from the device */ + if (usb_submit_urb(port->read_urb)) + dbg("usb_submit_urb(read bulk) failed"); + + /* send an open port command */ + open_command.port = port->number - port->minor; + whiteheat_send_cmd (port->serial, WHITEHEAT_OPEN, (__u8 *)&open_command, sizeof(open_command)); + + /* Need to do device specific setup here (control lines, baud rate, etc.) */ + /* FIXME!!! */ + + dbg("whiteheat_open exit"); + + return (0); +} + + +static void whiteheat_close(struct usb_serial_port *port, struct file * filp) +{ + struct whiteheat_min_set close_command; + + dbg("whiteheat_close port %d", port->number); + + /* send a close command to the port */ + close_command.port = port->number - port->minor; + whiteheat_send_cmd (port->serial, WHITEHEAT_CLOSE, (__u8 *)&close_command, sizeof(close_command)); + + /* Need to change the control lines here */ + /* FIXME */ + + /* shutdown our bulk reads and writes */ + usb_unlink_urb (port->write_urb); + usb_unlink_urb (port->read_urb); + port->active = 0; +} + + +static void whiteheat_set_termios (struct usb_serial_port *port, struct termios *old_termios) +{ + unsigned int cflag = port->tty->termios->c_cflag; + + dbg("whiteheat_set_termios port %d", port->number); + + /* check that they really want us to change something */ + if (old_termios) { + if ((cflag == old_termios->c_cflag) && + (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) { + dbg("nothing to change..."); + return; + } + } + + /* do the parsing of the cflag to see what to set the line to */ + /* FIXME!! */ + + return; +} + +static void whiteheat_throttle (struct usb_serial_port *port) +{ + dbg("whiteheat_throttle port %d", port->number); + + /* Change the control signals */ + /* FIXME!!! */ + + return; +} + + +static void whiteheat_unthrottle (struct usb_serial_port *port) +{ + dbg("whiteheat_unthrottle port %d", port->number); + + /* Change the control signals */ + /* FIXME!!! */ + + return; +} + + +/* steps to download the firmware to the WhiteHEAT device: + - hold the reset (by writing to the reset bit of the CPUCS register) + - download the VEND_AX.HEX file to the chip using VENDOR_REQUEST-ANCHOR_LOAD + - release the reset (by writing to the CPUCS register) + - download the WH.HEX file for all addresses greater than 0x1b3f using + VENDOR_REQUEST-ANCHOR_EXTERNAL_RAM_LOAD + - hold the reset + - download the WH.HEX file for all addresses less than 0x1b40 using + VENDOR_REQUEST_ANCHOR_LOAD + - release the reset + - device renumerated itself and comes up as new device id with all + firmware download completed. +*/ +static int whiteheat_startup (struct usb_serial *serial) +{ + int response; + const struct whiteheat_hex_record *record; + + dbg("whiteheat_startup"); + + response = ezusb_set_reset (serial, 1); + + record = &whiteheat_loader[0]; + while (record->address != 0xffff) { + response = ezusb_writememory (serial, record->address, + (unsigned char *)record->data, record->data_size, 0xa0); + if (response < 0) { + err("ezusb_writememory failed for loader (%d %04X %p %d)", + response, record->address, record->data, record->data_size); + break; + } + ++record; + } + + response = ezusb_set_reset (serial, 0); + + record = &whiteheat_firmware[0]; + while (record->address < 0x1b40) { + ++record; + } + while (record->address != 0xffff) { + response = ezusb_writememory (serial, record->address, + (unsigned char *)record->data, record->data_size, 0xa3); + if (response < 0) { + err("ezusb_writememory failed for first firmware step (%d %04X %p %d)", + response, record->address, record->data, record->data_size); + break; + } + ++record; + } + + response = ezusb_set_reset (serial, 1); + + record = &whiteheat_firmware[0]; + while (record->address < 0x1b40) { + response = ezusb_writememory (serial, record->address, + (unsigned char *)record->data, record->data_size, 0xa0); + if (response < 0) { + err("ezusb_writememory failed for second firmware step (%d %04X %p %d)", + response, record->address, record->data, record->data_size); + break; + } + ++record; + } + + response = ezusb_set_reset (serial, 0); + + /* we want this device to fail to have a driver assigned to it. */ + return 1; +} + + +static void whiteheat_shutdown (struct usb_serial *serial) +{ + struct usb_serial_port *command_port; + + dbg("whiteheat_shutdown"); + + /* set up some stuff for our command port */ + command_port = &serial->port[COMMAND_PORT]; + if (command_port->private != NULL) { + kfree (command_port->private); + command_port->private = NULL; + } + + return; +} + diff --git a/drivers/usb/serial/whiteheat.h b/drivers/usb/serial/whiteheat.h new file mode 100644 index 000000000000..970a555c0276 --- /dev/null +++ b/drivers/usb/serial/whiteheat.h @@ -0,0 +1,169 @@ +/* + * USB ConnectTech WhiteHEAT driver + * + * Copyright (C) 1999, 2000 + * Greg Kroah-Hartman (greg@kroah.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * See Documentation/usb/usb-serial.txt for more information on using this driver + * + */ + +#ifndef __LINUX_USB_SERIAL_WHITEHEAT_H +#define __LINUX_USB_SERIAL_WHITEHEAT_H + + +#define FALSE 0 +#define TRUE 1 + +/* WhiteHEAT commands */ +#define WHITEHEAT_OPEN 1 /* open the port */ +#define WHITEHEAT_CLOSE 2 /* close the port */ +#define WHITEHEAT_SETUP_PORT 3 /* change port settings */ +#define WHITEHEAT_SET_RTS 4 /* turn RTS on or off */ +#define WHITEHEAT_SET_DTR 5 /* turn DTR on or off */ +#define WHITEHEAT_SET_BREAK 6 /* turn BREAK on or off */ +#define WHITEHEAT_DUMP 7 /* dump memory */ +#define WHITEHEAT_STATUS 8 /* get status */ +#define WHITEHEAT_PURGE 9 /* clear the UART fifos */ +#define WHITEHEAT_GET_DTR_RTS 10 /* get the state of DTR and RTS for a port */ +#define WHITEHEAT_GET_HW_INFO 11 /* get EEPROM info and hardware ID */ +#define WHITEHEAT_REPORT_TX_DONE 12 /* get the next TX done */ +#define WHITEHEAT_EVENT 13 /* unsolicited status events */ +#define WHITEHEAT_ECHO 14 /* send data to the indicated IN endpoint */ +#define WHITEHEAT_DO_TEST 15 /* perform the specified test */ +#define WHITEHEAT_CMD_COMPLETE 16 /* reply for certain commands */ + +/* Data for the WHITEHEAT_SETUP_PORT command */ +#define WHITEHEAT_CTS_FLOW 0x08 +#define WHITEHEAT_RTS_FLOW 0x80 +#define WHITEHEAT_DSR_FLOW 0x10 +#define WHITEHEAT_DTR_FLOW 0x02 +struct whiteheat_port_settings { + __u8 port; /* port number (1 to N) */ + __u32 baud; /* any value allowed, default 9600, arrives little endian, range is 7 - 460800 */ + __u8 bits; /* 5, 6, 7, or 8, default 8 */ + __u8 stop; /* 1 or 2, default 1 (2 = 1.5 if bits = 5) */ + __u8 parity; /* 'n, e, o, 0, or 1' (ascii), default 'n' + * n = none e = even o = odd + * 0 = force 0 1 = force 1 */ + __u8 sflow; /* 'n, r, t, or b' (ascii), default 'n' + * n = none + * r = receive (XOFF/XON transmitted when receiver fills / empties) + * t = transmit (XOFF/XON received will stop/start TX) + * b = both */ + __u8 xoff; /* XOFF byte value, default 0x13 */ + __u8 xon; /* XON byte value, default 0x11 */ + __u8 hflow; /* bits indicate mode as follows: + * CTS (0x08) (CTS off/on will control/cause TX off/on) + * DSR (0x10) (DSR off/on will control/cause TX off/on) + * RTS (0x80) (RTS off/on when receiver fills/empties) + * DTR (0x02) (DTR off/on when receiver fills/empties) */ + __u8 lloop; /* local loopback 0 or 1, default 0 */ +}; + +/* data for WHITEHEAT_SET_RTS, WHITEHEAT_SET_DTR, and WHITEHEAT_SET_BREAK commands */ +struct whiteheat_rdb_set { + __u8 port; /* port number (1 to N) */ + __u8 state; /* 0 = off, non-zero = on */ +}; + +/* data for: + WHITEHEAT_OPEN + WHITEHEAT_CLOSE + WHITEHEAT_STATUS + WHITEHEAT_GET_DTR_RTS + WHITEHEAT_REPORT_TX_DONE */ +struct whiteheat_min_set { + __u8 port; /* port number (1 to N) */ +}; + +/* data for WHITEHEAT_PURGE command */ +#define WHITEHEAT_PURGE_INPUT 0x01 +#define WHITEHEAT_PURGE_OUTPUT 0x02 +struct whiteheat_purge_set { + __u8 port; /* port number (1 to N) */ + __u8 what; /* bit pattern of what to purge */ +}; + +/* data for WHITEHEAT_DUMP command */ +struct whiteheat_dump_info { + __u8 mem_type; /* memory type: 'd' = data, 'i' = idata, 'b' = bdata, 'x' = xdata */ + __u16 addr; /* memory address to dump, address range depends on the above mem_type: + * 'd' = 0 to ff (80 to FF is SFR's) + * 'i' = 80 to ff + * 'b' = 20 to 2f (bits returned as bytes) + * 'x' = 0000 to ffff (also code space) */ + __u16 length; /* number of bytes to dump, max 64 */ +}; + +/* data for WHITEHEAT_ECHO command */ +struct whiteheat_echo_set { + __u8 port; /* port number (1 to N) */ + __u8 length; /* length of message to echo */ + __u8 echo_data[61]; /* data to echo */ +}; + +/* data returned from WHITEHEAT_STATUS command */ +#define WHITEHEAT_OVERRUN_ERROR 0x02 +#define WHITEHEAT_PARITY_ERROR 0x04 +#define WHITEHEAT_FRAMING_ERROR 0x08 +#define WHITEHEAT_BREAK_ERROR 0x10 + +#define WHITEHEAT_OHFLOW 0x01 /* TX is stopped by CTS (waiting for CTS to go ON) */ +#define WHITEHEAT_IHFLOW 0x02 /* remote TX is stopped by RTS */ +#define WHITEHEAT_OSFLOW 0x04 /* TX is stopped by XOFF received (waiting for XON to occur) */ +#define WHITEHEAT_ISFLOW 0x08 /* remote TX is stopped by XOFF transmitted */ +#define WHITEHEAT_TX_DONE 0x80 /* TX has completed */ + +#define WHITEHEAT_MODEM_EVENT 0x01 +#define WHITEHEAT_ERROR_EVENT 0x02 +#define WHITEHEAT_FLOW_EVENT 0x04 +#define WHITEHEAT_CONNECT_EVENT 0x08 +struct whiteheat_status_info { + __u8 port; /* port number (1 to N) */ + __u8 event; /* indicates which of the following bytes are the current event */ + __u8 modem; /* modem signal status (copy of UART MSR register) */ + __u8 error; /* PFO and RX break (copy of UART LSR register) */ + __u8 flow; /* flow control state */ + __u8 connect; /* connect state, non-zero value indicates connected */ +}; + +/* data returned from WHITEHEAT_EVENT command */ +struct whiteheat_event { + __u8 port; /* port number (1 to N) */ + __u8 event; /* indicates which of the following bytes are the current event */ + __u8 info; /* either modem, error, flow, or connect information */ +}; + +/* data retured by the WHITEHEAT_GET_HW_INFO command */ +struct whiteheat_hw_info { + __u8 hw_id; /* hardware id number, WhiteHEAT = 0 */ + __u8 sw_major_rev; /* major version number */ + __u8 sw_minor_rev; /* minor version number */ + struct whiteheat_hw_eeprom_info { + __u8 b0; /* B0 */ + __u8 vendor_id_low; /* vendor id (low byte) */ + __u8 vendor_id_high; /* vendor id (high byte) */ + __u8 product_id_low; /* product id (low byte) */ + __u8 product_id_high; /* product id (high byte) */ + __u8 device_id_low; /* device id (low byte) */ + __u8 device_id_high; /* device id (high byte) */ + __u8 not_used_1; + __u8 serial_number_0; /* serial number (low byte) */ + __u8 serial_number_1; /* serial number */ + __u8 serial_number_2; /* serial number */ + __u8 serial_number_3; /* serial number (high byte) */ + __u8 not_used_2; + __u8 not_used_3; + __u8 checksum_low; /* checksum (low byte) */ + __u8 checksum_high; /* checksum (high byte */ + } hw_eeprom_info; /* EEPROM contents */ +}; + +#endif + diff --git a/drivers/usb/serial/whiteheat_fw.h b/drivers/usb/serial/whiteheat_fw.h new file mode 100644 index 000000000000..7d942ba004c3 --- /dev/null +++ b/drivers/usb/serial/whiteheat_fw.h @@ -0,0 +1,1556 @@ +/***************************************************************************** + * + * whiteheat.h -- ConnectTech WhiteHEAT Firmware. + * + * Copyright (C) 2000 ConnectTech Inc (http://www.connecttech.com/) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * (04/09/2000) gkh + * Updated the firmware with the latest provided by ConnectTech. + * + * (01/16/2000) gkh + * Fixed my intel hex processing tool, so now the firmware actually + * matches the original file (this was causing a few problems...) + * + * (01/15/2000) gkh + * Added debug loader firmware if DEBUG is #defined: + * Port 1 LED flashes when the vend_ax program is running + * Port 2 LED flashes when any SETUP command arrives + * Port 3 LED flashes when any valid VENDOR request occurs + * Port 4 LED flashes when the EXTERNAL RAM DOWNLOAD request occurs + * + * version 1.0 (01/09/2000) gkh + * Original firmware from ConnectTech massaged a little to be program + * readable. + * + *****************************************************************************/ + +#define whiteheat_DATE "20000106" + +struct whiteheat_hex_record { + __u16 address; + __u8 data_size; + __u8 data[16]; +}; + +static const struct whiteheat_hex_record whiteheat_firmware[] = { +{ 0x0000, 3, {0x02, 0x93, 0xa9} }, +{ 0x0003, 3, {0x02, 0x13, 0x12} }, +{ 0x000b, 3, {0x02, 0x0a, 0x80} }, +{ 0x0033, 3, {0x02, 0x08, 0x15} }, +{ 0x0043, 3, {0x02, 0x0a, 0x00} }, +{ 0x005b, 3, {0x02, 0x82, 0x31} }, +{ 0x0370, 16, {0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x73, 0x14, 0x70, 0x03, 0x02, 0x04, 0xe7, 0x24} }, +{ 0x0380, 16, {0xfe, 0x70, 0x03, 0x02, 0x05, 0x45, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x64, 0x14, 0x70, 0x03} }, +{ 0x0390, 16, {0x02, 0x04, 0x52, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x49, 0x24} }, +{ 0x03a0, 16, {0x05, 0x60, 0x03, 0x02, 0x05, 0x94, 0x90, 0x7f, 0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60} }, +{ 0x03b0, 16, {0x36, 0x24, 0x02, 0x70, 0x7b, 0x74, 0x12, 0x90, 0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5} }, +{ 0x03c0, 16, {0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x09, 0x7b, 0xea, 0x49, 0x60, 0x0d} }, +{ 0x03d0, 16, {0xea, 0x90, 0x7f, 0xd4, 0xf0, 0xe9, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4} }, +{ 0x03e0, 16, {0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x09, 0x3a, 0xea} }, +{ 0x03f0, 16, {0x49, 0x60, 0x33, 0x12, 0x9b, 0x72, 0xf5, 0x4b, 0x90, 0x7f, 0xee, 0xe0, 0xff, 0xe5, 0x4b, 0xd3} }, +{ 0x0400, 16, {0x9f, 0x40, 0x03, 0xe0, 0xf5, 0x4b, 0xe5, 0x4b, 0xd3, 0x94, 0x40, 0x40, 0x03, 0x75, 0x4b, 0x40} }, +{ 0x0410, 16, {0xae, 0x02, 0xaf, 0x01, 0x7c, 0x7f, 0x7d, 0x00, 0xab, 0x4b, 0x12, 0x8d, 0xd9, 0x90, 0x7f, 0xb5} }, +{ 0x0420, 16, {0xe5, 0x4b, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b} }, +{ 0x0430, 16, {0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0x00, 0xe5, 0x21, 0xf0} }, +{ 0x0440, 16, {0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x21, 0x02} }, +{ 0x0450, 16, {0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x31, 0xd2, 0x02, 0x43, 0x88, 0x10, 0xd2, 0xeb, 0xd2} }, +{ 0x0460, 16, {0xa8, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0x00, 0xe5, 0x31, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0} }, +{ 0x0470, 16, {0x02, 0x05, 0x9b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02} }, +{ 0x0480, 16, {0x70, 0x5b, 0xa2, 0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x05, 0xe4, 0x33, 0x4f, 0x90} }, +{ 0x0490, 16, {0x7f, 0x00, 0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0xe4} }, +{ 0x04a0, 16, {0x90, 0x7f, 0x00, 0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0x90} }, +{ 0x04b0, 16, {0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25} }, +{ 0x04c0, 16, {0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0x01, 0x90, 0x7f, 0x00} }, +{ 0x04d0, 16, {0xf0, 0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xb4} }, +{ 0x04e0, 16, {0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x1d, 0x24} }, +{ 0x04f0, 16, {0x02, 0x60, 0x03, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x05, 0xc2, 0x00, 0x02} }, +{ 0x0500, 16, {0x05, 0x9b, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0x9b, 0x90, 0x7f, 0xea, 0xe0} }, +{ 0x0510, 16, {0x70, 0x2a, 0x90, 0x7f, 0xec, 0xe0, 0xff, 0xf4, 0x54, 0x80, 0xfe, 0xc4, 0x54, 0x0f, 0xfe, 0xef} }, +{ 0x0520, 16, {0x54, 0x07, 0xfd, 0x2e, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4} }, +{ 0x0530, 16, {0xf0, 0x90, 0x7f, 0xd7, 0xed, 0xf0, 0xef, 0x44, 0x20, 0xf0, 0x80, 0x5f, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x0540, 16, {0x44, 0x01, 0xf0, 0x80, 0x56, 0x90, 0x7f, 0xe8, 0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70} }, +{ 0x0550, 16, {0x4a, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04, 0xd2, 0x00, 0x80, 0x3f, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x0560, 16, {0x44, 0x01, 0xf0, 0x80, 0x36, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4} }, +{ 0x0570, 16, {0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5} }, +{ 0x0580, 16, {0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x10, 0x90, 0x7f, 0xb4, 0xe0, 0x44} }, +{ 0x0590, 16, {0x01, 0xf0, 0x80, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44} }, +{ 0x05a0, 2, {0x02, 0xf0} }, +{ 0x05a2, 1, {0x22} }, +{ 0x05a3, 16, {0x75, 0x47, 0xff, 0x75, 0x46, 0xff, 0x75, 0x45, 0x0f, 0x75, 0x44, 0x00, 0xd2, 0x03, 0xc2, 0x06} }, +{ 0x05b3, 16, {0xc2, 0x02, 0xc2, 0x00, 0xc2, 0x05, 0xc2, 0x01, 0x90, 0x03, 0x00, 0x74, 0x19, 0xf0, 0xe4, 0x90} }, +{ 0x05c3, 16, {0x01, 0xbc, 0xf0, 0xc2, 0x04, 0x90, 0x01, 0xc0, 0xf0, 0xa3, 0xf0, 0xc2, 0xaf, 0xc2, 0xa8, 0x12} }, +{ 0x05d3, 16, {0x0b, 0x8d, 0xe4, 0x90, 0x02, 0xaf, 0xf0, 0x90, 0x01, 0x00, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3} }, +{ 0x05e3, 16, {0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x08, 0xf0, 0x7e} }, +{ 0x05f3, 16, {0x01, 0x7f, 0x00, 0x12, 0x19, 0xc1, 0x75, 0x49, 0x12, 0x75, 0x4a, 0x0a, 0x90, 0x01, 0x0b, 0xe0} }, +{ 0x0603, 16, {0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83} }, +{ 0x0613, 16, {0xef, 0xf0, 0x90, 0x01, 0x0c, 0xe0, 0x44, 0x80, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70} }, +{ 0x0623, 16, {0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x01, 0x0d, 0xe0, 0xff, 0x05} }, +{ 0x0633, 16, {0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0} }, +{ 0x0643, 16, {0x90, 0x01, 0x0e, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14} }, +{ 0x0653, 16, {0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x12, 0x0a, 0xe4, 0x93, 0xff, 0x74, 0x01, 0x93, 0x90} }, +{ 0x0663, 16, {0x01, 0x1c, 0xcf, 0xf0, 0xa3, 0xef, 0xf0, 0x90, 0x01, 0x1c, 0xe0, 0xff, 0xa3, 0xe0, 0xfe, 0xef} }, +{ 0x0673, 16, {0x6e, 0xff, 0x90, 0x01, 0x1c, 0xf0, 0xa3, 0xe0, 0x6f, 0xff, 0xf0, 0x90, 0x01, 0x1c, 0xe0, 0x6f} }, +{ 0x0683, 16, {0xf0, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xe4, 0xfc, 0xfd, 0x75, 0x4f, 0x10, 0x75, 0x50, 0x02, 0x75} }, +{ 0x0693, 16, {0x51, 0x12, 0x75, 0x52, 0xac, 0x12, 0x90, 0x15, 0x75, 0x49, 0x12, 0x75, 0x4a, 0xb2, 0x90, 0x01} }, +{ 0x06a3, 16, {0x0d, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14, 0xf5, 0x82} }, +{ 0x06b3, 16, {0x8c, 0x83, 0xef, 0xf0, 0x90, 0x01, 0x0e, 0xe0, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70} }, +{ 0x06c3, 16, {0x02, 0x05, 0x49, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4} }, +{ 0x06d3, 16, {0x54, 0x0f, 0x24, 0x41, 0xff, 0x05, 0x4a, 0xe5, 0x4a, 0xac, 0x49, 0x70, 0x02, 0x05, 0x49, 0x14} }, +{ 0x06e3, 16, {0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x05, 0x4a, 0xe5, 0x4a, 0xae, 0x49, 0x70, 0x02, 0x05, 0x49} }, +{ 0x06f3, 16, {0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe4, 0xf0, 0x75, 0x82, 0x10, 0x75, 0x83, 0x01, 0xe0, 0xfc, 0xa3} }, +{ 0x0703, 16, {0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x90, 0x01, 0x18, 0x12, 0x9d, 0x25, 0x7e, 0x01} }, +{ 0x0713, 16, {0x7f, 0x18, 0x12, 0x85, 0x08, 0x90, 0x01, 0x18, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe} }, +{ 0x0723, 16, {0xa3, 0xe0, 0xff, 0x75, 0x4f, 0x0a, 0x75, 0x50, 0x06, 0x75, 0x51, 0x12, 0x75, 0x52, 0xb8, 0x12} }, +{ 0x0733, 16, {0x90, 0x15, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x53, 0x91, 0xef} }, +{ 0x0743, 16, {0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0xe0, 0x44, 0x1f, 0xf0, 0xd2, 0xaf} }, +{ 0x0753, 16, {0x20, 0x01, 0x2e, 0x20, 0x01, 0x2b, 0xa2, 0x03, 0x92, 0x07, 0x12, 0x09, 0xca, 0x75, 0x43, 0x50} }, +{ 0x0763, 16, {0x75, 0x42, 0x6d, 0x75, 0x41, 0x33, 0x75, 0x40, 0x00, 0x20, 0x01, 0xe4, 0x7f, 0xff, 0x7e, 0xff} }, +{ 0x0773, 16, {0x7d, 0xff, 0x7c, 0xff, 0x78, 0x40, 0x12, 0x9d, 0x0e, 0xec, 0x4d, 0x4e, 0x4f, 0x60, 0xd1, 0x80} }, +{ 0x0783, 16, {0xe8, 0x30, 0x01, 0x05, 0x12, 0x03, 0x70, 0xc2, 0x01, 0x30, 0x06, 0x0d, 0x12, 0x0a, 0xed, 0x50} }, +{ 0x0793, 16, {0x06, 0x12, 0x0b, 0x00, 0x12, 0x0a, 0xf2, 0xc2, 0x06, 0x12, 0x92, 0x38, 0x90, 0x01, 0xbd, 0xe0} }, +{ 0x07a3, 16, {0x60, 0x10, 0x7e, 0x7b, 0x7f, 0x40, 0x12, 0x8e, 0xc1, 0xe4, 0x90, 0x01, 0xbd, 0xf0, 0x90, 0x7f} }, +{ 0x07b3, 16, {0xd3, 0xf0, 0x90, 0x02, 0xaf, 0xe0, 0xb4, 0x0f, 0x03, 0x12, 0x95, 0x30, 0x12, 0x99, 0xcc, 0xe4} }, +{ 0x07c3, 16, {0xff, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xfe, 0x90, 0x01, 0xbc} }, +{ 0x07d3, 16, {0xe0, 0x5e, 0x60, 0x14, 0x74, 0x27, 0x2f, 0xf8, 0xe6, 0xd3, 0x94, 0x0a, 0x40, 0x04, 0x7e, 0x01} }, +{ 0x07e3, 16, {0x80, 0x02, 0x7e, 0x00, 0x8e, 0x48, 0x80, 0x03, 0x75, 0x48, 0x01, 0x74, 0x68, 0x2f, 0xf5, 0x82} }, +{ 0x07f3, 16, {0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe5, 0x48, 0xf0, 0x0f, 0xbf, 0x04, 0xc5, 0xe5, 0x2b, 0xd3, 0x94} }, +{ 0x0803, 16, {0x0a, 0x40, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0x90, 0x20, 0x6c, 0xef, 0xf0, 0x02, 0x07} }, +{ 0x0813, 1, {0x84} }, +{ 0x0814, 1, {0x22} }, +{ 0x0815, 4, {0x53, 0xd8, 0xef, 0x32} }, +{ 0x0819, 16, {0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0x74, 0x89} }, +{ 0x0829, 16, {0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x09, 0x23, 0x90, 0x7f} }, +{ 0x0839, 16, {0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x02, 0x7d, 0xff} }, +{ 0x0849, 16, {0x12, 0x81, 0xe0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02} }, +{ 0x0859, 16, {0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0} }, +{ 0x0869, 16, {0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf} }, +{ 0x0879, 16, {0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f} }, +{ 0x0889, 7, {0x32, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x22} }, +{ 0x0890, 16, {0x90, 0x7f, 0x96, 0xe0, 0x54, 0xfd, 0xf0, 0xe0, 0x44, 0x80, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12} }, +{ 0x08a0, 16, {0x09, 0x23, 0x7f, 0x02, 0xe4, 0xfd, 0x12, 0x81, 0xe0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23} }, +{ 0x08b0, 16, {0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f} }, +{ 0x08c0, 16, {0x96, 0xe0, 0x44, 0x04, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0} }, +{ 0x08d0, 16, {0x54, 0xf7, 0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x01} }, +{ 0x08e0, 12, {0xf0, 0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x12, 0x0b, 0x00, 0x22} }, +{ 0x08ec, 16, {0x90, 0x11, 0xfc, 0xe4, 0x93, 0x70, 0x2f, 0x90, 0x7f, 0x93, 0x74, 0x30, 0xf0, 0x90, 0x7f, 0x94} }, +{ 0x08fc, 16, {0x74, 0x3c, 0xf0, 0x90, 0x7f, 0x95, 0x74, 0xc6, 0xf0, 0xe4, 0x90, 0x7f, 0x97, 0xf0, 0x90, 0x7f} }, +{ 0x090c, 16, {0x9d, 0x74, 0x02, 0xf0, 0x90, 0x7f, 0xe2, 0x74, 0x12, 0xf0, 0x12, 0x08, 0x19, 0x75, 0x82, 0xfc} }, +{ 0x091c, 7, {0x75, 0x83, 0x11, 0x74, 0xff, 0xf0, 0x22} }, +{ 0x0923, 16, {0x8e, 0x58, 0x8f, 0x59, 0xe5, 0x59, 0x15, 0x59, 0xae, 0x58, 0x70, 0x02, 0x15, 0x58, 0x4e, 0x60} }, +{ 0x0933, 7, {0x05, 0x12, 0x09, 0xef, 0x80, 0xee, 0x22} }, +{ 0x093a, 2, {0x8f, 0x4c} }, +{ 0x093c, 16, {0xe4, 0xf5, 0x4d, 0x75, 0x4e, 0xff, 0x75, 0x4f, 0x12, 0x75, 0x50, 0x6a, 0xab, 0x4e, 0xaa, 0x4f} }, +{ 0x094c, 16, {0xa9, 0x50, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0xb4, 0x03, 0x1d, 0xaf, 0x4d, 0x05, 0x4d, 0xef} }, +{ 0x095c, 16, {0xb5, 0x4c, 0x01, 0x22, 0x12, 0x9b, 0x72, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} }, +{ 0x096c, 14, {0x4e, 0xff, 0xf5, 0x4f, 0x89, 0x50, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x097a, 1, {0x22} }, +{ 0x097b, 16, {0xe4, 0xfe, 0x75, 0x4e, 0xff, 0x75, 0x4f, 0x12, 0x75, 0x50, 0x12, 0xab, 0x4e, 0xaa, 0x4f, 0xa9} }, +{ 0x098b, 16, {0x50, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} }, +{ 0x099b, 16, {0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x9b, 0xe4, 0x85, 0xf0, 0x4c, 0xf5, 0x4d, 0x62, 0x4c} }, +{ 0x09ab, 16, {0xe5, 0x4c, 0x62, 0x4d, 0xe5, 0x4d, 0x62, 0x4c, 0x29, 0xfd, 0xe5, 0x4c, 0x3a, 0xa9, 0x05, 0x75} }, +{ 0x09bb, 14, {0x4e, 0xff, 0xf5, 0x4f, 0x89, 0x50, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x09c9, 1, {0x22} }, +{ 0x09ca, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x07, 0x04, 0xe0, 0x44} }, +{ 0x09da, 16, {0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x09, 0x23, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} }, +{ 0x09ea, 5, {0xe0, 0x44, 0x04, 0xf0, 0x22} }, +{ 0x09ef, 16, {0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} }, +{ 0x09ff, 1, {0x22} }, +{ 0x0a00, 16, {0x02, 0x0b, 0xaa, 0x00, 0x02, 0x0b, 0xdd, 0x00, 0x02, 0x0b, 0xc2, 0x00, 0x02, 0x0c, 0x1c, 0x00} }, +{ 0x0a10, 16, {0x02, 0x0c, 0x06, 0x00, 0x02, 0x0a, 0xf7, 0x00, 0x02, 0x0a, 0xf8, 0x00, 0x02, 0x0a, 0xf9, 0x00} }, +{ 0x0a20, 16, {0x02, 0x0c, 0x37, 0x00, 0x02, 0x0d, 0x27, 0x00, 0x02, 0x0c, 0x73, 0x00, 0x02, 0x0d, 0x7b, 0x00} }, +{ 0x0a30, 16, {0x02, 0x0c, 0xaf, 0x00, 0x02, 0x0d, 0xcf, 0x00, 0x02, 0x0c, 0xeb, 0x00, 0x02, 0x0e, 0x23, 0x00} }, +{ 0x0a40, 16, {0x02, 0x0a, 0xfa, 0x00, 0x02, 0x0a, 0xfc, 0x00, 0x02, 0x0a, 0xfb, 0x00, 0x02, 0x0a, 0xfd, 0x00} }, +{ 0x0a50, 8, {0x02, 0x0e, 0x77, 0x00, 0x02, 0x0e, 0x8d, 0x00} }, +{ 0x0a58, 16, {0x53, 0x8e, 0xf7, 0xe5, 0x89, 0x54, 0xf1, 0x44, 0x01, 0xf5, 0x89, 0x75, 0x8c, 0xb1, 0xd2, 0xa9} }, +{ 0x0a68, 16, {0x75, 0x98, 0x40, 0x75, 0xcb, 0xff, 0x75, 0xca, 0xf3, 0x75, 0xc8, 0x34, 0xe4, 0xff, 0x7f, 0x05} }, +{ 0x0a78, 7, {0x78, 0x27, 0xe4, 0xf6, 0x08, 0xdf, 0xfc} }, +{ 0x0a7f, 1, {0x22} }, +{ 0x0a80, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x00, 0xc0, 0x00, 0xc0, 0x06, 0xc0} }, +{ 0x0a90, 1, {0x07} }, +{ 0x0a91, 16, {0x30, 0x04, 0x16, 0x75, 0x8c, 0xf8, 0x75, 0x8a, 0x30, 0x7f, 0x2f, 0xae, 0x07, 0x1f, 0xee, 0x60} }, +{ 0x0aa1, 16, {0x3c, 0x90, 0x20, 0x00, 0x74, 0x55, 0xf0, 0x80, 0xf2, 0x75, 0x8c, 0xb1, 0x7f, 0x27, 0xef, 0xd3} }, +{ 0x0ab1, 16, {0x94, 0x2b, 0x50, 0x09, 0xa8, 0x07, 0xe6, 0x60, 0x01, 0x16, 0x0f, 0x80, 0xf1, 0x90, 0x03, 0x00} }, +{ 0x0ac1, 16, {0xe0, 0x60, 0x02, 0x14, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x60, 0x0e, 0x90} }, +{ 0x0ad1, 13, {0x01, 0xc1, 0xe0, 0x24, 0xff, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x34, 0xff, 0xf0} }, +{ 0x0ade, 15, {0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0aed, 5, {0x12, 0x08, 0x90, 0xd3, 0x22} }, +{ 0x0af2, 5, {0x12, 0x08, 0x19, 0xd3, 0x22} }, +{ 0x0af7, 1, {0x32} }, +{ 0x0af8, 1, {0x32} }, +{ 0x0af9, 1, {0x32} }, +{ 0x0afa, 1, {0x32} }, +{ 0x0afb, 1, {0x32} }, +{ 0x0afc, 1, {0x32} }, +{ 0x0afd, 1, {0x32} }, +{ 0x0b00, 9, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} }, +{ 0x0b7d, 16, {0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} }, +{ 0x0b8d, 16, {0xd2, 0x00, 0x75, 0x8e, 0x10, 0xe4, 0x90, 0x7f, 0x92, 0xf0, 0x12, 0x10, 0x0c, 0x12, 0x08, 0xec} }, +{ 0x0b9d, 13, {0x12, 0x0e, 0xa9, 0x12, 0x90, 0xe6, 0x12, 0x1b, 0x0c, 0x12, 0x0a, 0x58, 0x22} }, +{ 0x0baa, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} }, +{ 0x0bba, 8, {0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0bc2, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0bd2, 11, {0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0bdd, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0x90} }, +{ 0x0bed, 16, {0x7f, 0xd8, 0xe0, 0x70, 0x0d, 0x90, 0x7f, 0xd9, 0xe0, 0x70, 0x07, 0xe5, 0x2b, 0x70, 0x03, 0x75} }, +{ 0x0bfd, 9, {0x2b, 0x14, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0c06, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} }, +{ 0x0c16, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0c1c, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x30, 0x02, 0x02, 0xd2, 0x06, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0c2c, 11, {0xab, 0x74, 0x08, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0c37, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0c47, 16, {0xa9, 0x74, 0x02, 0xf0, 0xe5, 0x30, 0x30, 0xe0, 0x13, 0xe5, 0x3b, 0x30, 0xe0, 0x07, 0x90, 0x20} }, +{ 0x0c57, 16, {0x04, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x01, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} }, +{ 0x0c67, 12, {0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0c73, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0c83, 16, {0xa9, 0x74, 0x04, 0xf0, 0xe5, 0x30, 0x30, 0xe1, 0x13, 0xe5, 0x3b, 0x30, 0xe1, 0x07, 0x90, 0x20} }, +{ 0x0c93, 16, {0x0c, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x09, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} }, +{ 0x0ca3, 12, {0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0caf, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0cbf, 16, {0xa9, 0x74, 0x08, 0xf0, 0xe5, 0x30, 0x30, 0xe2, 0x13, 0xe5, 0x3b, 0x30, 0xe2, 0x07, 0x90, 0x20} }, +{ 0x0ccf, 16, {0x14, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x11, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} }, +{ 0x0cdf, 12, {0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0ceb, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0cfb, 16, {0xa9, 0x74, 0x10, 0xf0, 0xe5, 0x30, 0x30, 0xe3, 0x13, 0xe5, 0x3b, 0x30, 0xe3, 0x07, 0x90, 0x20} }, +{ 0x0d0b, 16, {0x1c, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x44, 0x01, 0xf0, 0xe5, 0x2b, 0x70, 0x03} }, +{ 0x0d1b, 12, {0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d27, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x0d37, 16, {0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x02, 0xf0, 0xe5, 0x30, 0x20} }, +{ 0x0d47, 16, {0xe0, 0x06, 0x90, 0x7f, 0xc7, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe0, 0x0a, 0x90, 0x7f, 0xc7} }, +{ 0x0d57, 16, {0xe0, 0x90, 0x02, 0xf8, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x01, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} }, +{ 0x0d67, 16, {0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} }, +{ 0x0d77, 4, {0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d7b, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x0d8b, 16, {0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x04, 0xf0, 0xe5, 0x30, 0x20} }, +{ 0x0d9b, 16, {0xe1, 0x06, 0x90, 0x7f, 0xc9, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe1, 0x0a, 0x90, 0x7f, 0xc9} }, +{ 0x0dab, 16, {0xe0, 0x90, 0x02, 0xf9, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x09, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} }, +{ 0x0dbb, 16, {0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} }, +{ 0x0dcb, 4, {0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0dcf, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x0ddf, 16, {0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x08, 0xf0, 0xe5, 0x30, 0x20} }, +{ 0x0def, 16, {0xe2, 0x06, 0x90, 0x7f, 0xcb, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe2, 0x0a, 0x90, 0x7f, 0xcb} }, +{ 0x0dff, 16, {0xe0, 0x90, 0x02, 0xfa, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x11, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} }, +{ 0x0e0f, 16, {0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} }, +{ 0x0e1f, 4, {0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0e23, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x0e33, 16, {0xd0, 0x75, 0xd0, 0x10, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x10, 0xf0, 0xe5, 0x30, 0x20} }, +{ 0x0e43, 16, {0xe3, 0x06, 0x90, 0x7f, 0xcd, 0xf0, 0x80, 0x16, 0xe5, 0x3b, 0x30, 0xe3, 0x0a, 0x90, 0x7f, 0xcd} }, +{ 0x0e53, 16, {0xe0, 0x90, 0x02, 0xfb, 0xf0, 0x80, 0x07, 0x90, 0x20, 0x19, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x2b} }, +{ 0x0e63, 16, {0x70, 0x03, 0x75, 0x2b, 0x14, 0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0} }, +{ 0x0e73, 4, {0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0e77, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xa9, 0x74, 0x80, 0xf0, 0xd0} }, +{ 0x0e87, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0e8d, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xaa, 0x74, 0x80, 0xf0, 0x90} }, +{ 0x0e9d, 12, {0x01, 0xbd, 0x74, 0xff, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0ea9, 16, {0x90, 0x01, 0x20, 0x12, 0x9d, 0x31, 0x00, 0x00, 0x25, 0x80, 0x90, 0x01, 0x24, 0x74, 0x08, 0xf0} }, +{ 0x0eb9, 16, {0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x6e, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x13, 0xf0, 0xa3, 0x74} }, +{ 0x0ec9, 16, {0x11, 0xf0, 0xe4, 0xa3, 0xf0, 0xa3, 0xf0, 0x90, 0x01, 0x1e, 0xf0, 0x90, 0x01, 0x1e, 0xe0, 0xff} }, +{ 0x0ed9, 16, {0xc3, 0x94, 0x04, 0x50, 0x13, 0xef, 0x04, 0xa3, 0xf0, 0x7e, 0x01, 0x7f, 0x1f, 0x12, 0x85, 0xe1} }, +{ 0x0ee9, 16, {0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0x80, 0xe3, 0xe4, 0xf5, 0x26, 0x90, 0x01, 0x1e, 0xf0, 0x90} }, +{ 0x0ef9, 16, {0x01, 0x1e, 0xe0, 0xff, 0xc3, 0x94, 0x04, 0x50, 0x1a, 0x74, 0xf8, 0x2f, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x0f09, 16, {0x02, 0xf5, 0x83, 0xe4, 0xf0, 0x74, 0x22, 0x2f, 0xf8, 0xe4, 0xf6, 0x90, 0x01, 0x1e, 0xe0, 0x04} }, +{ 0x0f19, 16, {0xf0, 0x80, 0xdc, 0xe4, 0xf5, 0x30, 0xe5, 0xc0, 0x60, 0x2f, 0x90, 0x01, 0x1e, 0x74, 0x01, 0xf0} }, +{ 0x0f29, 16, {0x90, 0x01, 0x1e, 0xe0, 0xff, 0xd3, 0x94, 0x04, 0x50, 0x1f, 0xef, 0x14, 0xff, 0x74, 0x01, 0xa8} }, +{ 0x0f39, 16, {0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42, 0x30, 0x7e, 0x01, 0x7f, 0x1e, 0x12, 0x83} }, +{ 0x0f49, 16, {0x5f, 0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0x80, 0xd7, 0xe4, 0xf5, 0x3a, 0x90, 0x01, 0x1e, 0xf0} }, +{ 0x0f59, 16, {0x90, 0x01, 0x1e, 0xe0, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x34, 0x20} }, +{ 0x0f69, 16, {0xf5, 0x83, 0xe0, 0x54, 0xf0, 0xfe, 0x74, 0xc5, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5, 0x83} }, +{ 0x0f79, 16, {0xee, 0xf0, 0x74, 0x36, 0x2f, 0xf8, 0xa6, 0x06, 0x74, 0x32, 0x2f, 0xf8, 0xe4, 0xf6, 0x74, 0x2c} }, +{ 0x0f89, 16, {0x2f, 0xf8, 0xe4, 0xf6, 0x74, 0xfc, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0} }, +{ 0x0f99, 16, {0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0, 0xe0, 0xb4, 0x04, 0xb6, 0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f} }, +{ 0x0fa9, 16, {0xf5, 0x4b, 0x60, 0x5e, 0xe4, 0x90, 0x01, 0x1e, 0xf0, 0x90, 0x01, 0x1e, 0xe0, 0xff, 0xc3, 0x94} }, +{ 0x0fb9, 16, {0x04, 0x50, 0xe7, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x55, 0x4b} }, +{ 0x0fc9, 16, {0x60, 0x38, 0x90, 0x01, 0x1e, 0xe0, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4} }, +{ 0x0fd9, 16, {0x34, 0x20, 0xf5, 0x83, 0xe0, 0xfe, 0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4} }, +{ 0x0fe9, 16, {0x34, 0x20, 0xf5, 0x83, 0xe0, 0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x0ff9, 16, {0x20, 0xf5, 0x83, 0xe0, 0xfe, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x90, 0x01, 0x1e, 0xe0, 0x04, 0xf0} }, +{ 0x1009, 3, {0x80, 0xa7, 0x22} }, +{ 0x100c, 16, {0x7b, 0xff, 0x7a, 0x12, 0x79, 0x1b, 0x90, 0x00, 0x04, 0x12, 0x9b, 0x8b, 0xfd, 0x8b, 0x4d, 0x75} }, +{ 0x101c, 16, {0x4e, 0x12, 0x75, 0x4f, 0x24, 0xe4, 0x90, 0x7f, 0xe1, 0xf0, 0x90, 0x7f, 0xe0, 0xf0, 0xf5, 0x4b} }, +{ 0x102c, 16, {0xf5, 0x4c, 0x90, 0x02, 0xae, 0xf0, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xde, 0xf0, 0x90, 0x7f} }, +{ 0x103c, 16, {0xa9, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0xe4, 0xfc, 0xec, 0x25, 0xe0, 0x24, 0xb4, 0xf5} }, +{ 0x104c, 16, {0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x0c, 0xbc, 0x10, 0xee, 0xe4, 0x90, 0x7f, 0xdd} }, +{ 0x105c, 16, {0xf0, 0xaf, 0x05, 0x1d, 0xef, 0x70, 0x03, 0x02, 0x11, 0xd2, 0xab, 0x4d, 0xaa, 0x4e, 0xa9, 0x4f} }, +{ 0x106c, 16, {0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0x64, 0x05, 0x60, 0x03, 0x02, 0x11, 0xc1, 0x90, 0x00, 0x03} }, +{ 0x107c, 16, {0x12, 0x9b, 0x8b, 0x64, 0x01, 0x60, 0x03, 0x02, 0x11, 0x48, 0x90, 0x00, 0x02, 0x12, 0x9b, 0x8b} }, +{ 0x108c, 16, {0xff, 0x54, 0x7f, 0xfc, 0xd3, 0x94, 0x07, 0x50, 0x03, 0x02, 0x11, 0x22, 0xec, 0xc3, 0x94, 0x10} }, +{ 0x109c, 16, {0x40, 0x03, 0x02, 0x11, 0x22, 0xef, 0x30, 0xe7, 0x42, 0xe5, 0x4c, 0xae, 0x4b, 0x78, 0x02, 0xce} }, +{ 0x10ac, 16, {0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0x74, 0xf0, 0x2c, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5} }, +{ 0x10bc, 16, {0x83, 0xef, 0xf0, 0x90, 0x7f, 0xe0, 0xe0, 0xff, 0xec, 0x24, 0xf8, 0xfe, 0x74, 0x01, 0xa8, 0x06} }, +{ 0x10cc, 16, {0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4f, 0x90, 0x7f, 0xe0, 0xf0, 0x90, 0x02, 0xae, 0xe0} }, +{ 0x10dc, 16, {0x04, 0xf0, 0x90, 0x7f, 0xdd, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x3e, 0xe5, 0x4c, 0xae, 0x4b, 0x78} }, +{ 0x10ec, 16, {0x02, 0xce, 0xc3, 0x13, 0xce, 0x13, 0xd8, 0xf9, 0xff, 0x74, 0xe8, 0x2c, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x10fc, 16, {0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x90, 0x7f, 0xe1, 0xe0, 0xff, 0xec, 0x24, 0xf8, 0xfe, 0x74, 0x01} }, +{ 0x110c, 16, {0xa8, 0x06, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x4f, 0x90, 0x7f, 0xe1, 0xf0, 0x90, 0x02} }, +{ 0x111c, 16, {0xae, 0xe0, 0x04, 0xf0, 0x80, 0x03, 0x7f, 0xff, 0x22, 0x90, 0x00, 0x04, 0x12, 0x9b, 0x8b, 0x25} }, +{ 0x112c, 16, {0x4c, 0xf5, 0x4c, 0xe4, 0x35, 0x4b, 0xf5, 0x4b, 0x90, 0x00, 0x05, 0x12, 0x9b, 0x8b, 0xfe, 0xe4} }, +{ 0x113c, 16, {0x25, 0x4c, 0xf5, 0x4c, 0xee, 0x35, 0x4b, 0xf5, 0x4b, 0x02, 0x11, 0xc4, 0xab, 0x4d, 0xaa, 0x4e} }, +{ 0x114c, 16, {0xa9, 0x4f, 0x90, 0x00, 0x03, 0x12, 0x9b, 0x8b, 0xff, 0x64, 0x02, 0x60, 0x05, 0xef, 0x64, 0x03} }, +{ 0x115c, 16, {0x70, 0x60, 0x90, 0x00, 0x02, 0x12, 0x9b, 0x8b, 0xff, 0x54, 0x7f, 0xfc, 0xd3, 0x94, 0x07, 0x50} }, +{ 0x116c, 16, {0x4e, 0xef, 0x30, 0xe7, 0x1e, 0x90, 0x7f, 0xde, 0xe0, 0xff, 0x74, 0x01, 0xa8, 0x04, 0x08, 0x80} }, +{ 0x117c, 16, {0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xfe, 0x4f, 0x90, 0x7f, 0xde, 0xf0, 0x90, 0x7f, 0xac, 0xe0, 0x4e} }, +{ 0x118c, 16, {0xf0, 0x80, 0x35, 0x90, 0x7f, 0xdf, 0xe0, 0xff, 0x74, 0x01, 0xa8, 0x04, 0x08, 0x80, 0x02, 0xc3} }, +{ 0x119c, 16, {0x33, 0xd8, 0xfc, 0xfe, 0x4f, 0x90, 0x7f, 0xdf, 0xf0, 0x90, 0x7f, 0xad, 0xe0, 0x4e, 0xf0, 0xec} }, +{ 0x11ac, 16, {0x25, 0xe0, 0x24, 0xc5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xec, 0xf0, 0x80, 0x09, 0x7f} }, +{ 0x11bc, 16, {0xff, 0x22, 0x7f, 0xff, 0x22, 0x7f, 0xff, 0x22, 0x74, 0x07, 0x25, 0x4f, 0xf5, 0x4f, 0xe4, 0x35} }, +{ 0x11cc, 16, {0x4e, 0xf5, 0x4e, 0x02, 0x10, 0x5d, 0x20, 0x03, 0x0d, 0x90, 0x02, 0xae, 0xe0, 0x60, 0x07, 0x90} }, +{ 0x11dc, 8, {0x7f, 0xae, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0x00} }, +{ 0x11e4, 1, {0x22} }, +{ 0x11e5, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xff, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33} }, +{ 0x11f5, 7, {0xd8, 0xfc, 0x42, 0x3a, 0x7f, 0x00, 0x22} }, +{ 0x11fc, 3, {0x00, 0x03, 0x15} }, +{ 0x1200, 16, {0x12, 0x01, 0x00, 0x01, 0xff, 0xff, 0xff, 0x40, 0x10, 0x07, 0x01, 0x80, 0x42, 0x00, 0x01, 0x02} }, +{ 0x1210, 16, {0x03, 0x01, 0x09, 0x02, 0x58, 0x00, 0x01, 0x01, 0x04, 0x80, 0x3c, 0x09, 0x04, 0x00, 0x00, 0x0a} }, +{ 0x1220, 16, {0xff, 0xff, 0xff, 0x05, 0x07, 0x05, 0x81, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x01, 0x02, 0x40} }, +{ 0x1230, 16, {0x00, 0x00, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40, 0x00, 0x00} }, +{ 0x1240, 16, {0x07, 0x05, 0x83, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x03, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05} }, +{ 0x1250, 16, {0x84, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x04, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x87, 0x02} }, +{ 0x1260, 16, {0x40, 0x00, 0x00, 0x07, 0x05, 0x07, 0x02, 0x40, 0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x24, 0x03} }, +{ 0x1270, 16, {0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x6e, 0x00, 0x65, 0x00, 0x63, 0x00, 0x74, 0x00, 0x20, 0x00} }, +{ 0x1280, 16, {0x54, 0x00, 0x65, 0x00, 0x63, 0x00, 0x68, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00} }, +{ 0x1290, 16, {0x2e, 0x00, 0x18, 0x03, 0x57, 0x00, 0x68, 0x00, 0x69, 0x00, 0x74, 0x00, 0x65, 0x00, 0x48, 0x00} }, +{ 0x12a0, 16, {0x45, 0x00, 0x41, 0x00, 0x54, 0x00, 0x2d, 0x00, 0x34, 0x00, 0x1a, 0x03, 0x58, 0x00, 0x58, 0x00} }, +{ 0x12b0, 16, {0x2d, 0x00, 0x58, 0x00, 0x58, 0x00, 0x2d, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00, 0x58, 0x00} }, +{ 0x12c0, 16, {0x58, 0x00, 0x58, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} }, +{ 0x12d0, 16, {0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} }, +{ 0x12e0, 16, {0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} }, +{ 0x12f0, 16, {0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} }, +{ 0x1300, 16, {0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} }, +{ 0x1310, 2, {0x00, 0x00} }, +{ 0x1312, 16, {0xc0, 0xe0, 0xc0, 0xf0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0xc0, 0xd0} }, +{ 0x1322, 16, {0x75, 0x86, 0x00, 0x75, 0xd0, 0x18, 0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f, 0xf5, 0xf0, 0x70, 0x11} }, +{ 0x1332, 16, {0xd0, 0xd0, 0xd0, 0x86, 0xd0, 0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xf0, 0xd0, 0xe0} }, +{ 0x1342, 16, {0x32, 0x75, 0x86, 0x00, 0x10, 0xf0, 0x0b, 0x10, 0xf1, 0x12, 0x10, 0xf2, 0x19, 0x10, 0xf3, 0x20} }, +{ 0x1352, 16, {0x80, 0xd4, 0xe5, 0x27, 0x70, 0x03, 0x75, 0x27, 0x14, 0x02, 0x13, 0x7c, 0xe5, 0x28, 0x70, 0x03} }, +{ 0x1362, 16, {0x75, 0x28, 0x14, 0x02, 0x15, 0x0d, 0xe5, 0x29, 0x70, 0x03, 0x75, 0x29, 0x14, 0x02, 0x16, 0x9e} }, +{ 0x1372, 16, {0xe5, 0x2a, 0x70, 0x03, 0x75, 0x2a, 0x14, 0x02, 0x18, 0x2f, 0x90, 0x20, 0x02, 0xe0, 0x54, 0x3f} }, +{ 0x1382, 16, {0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14, 0x60, 0x09, 0x02, 0x13} }, +{ 0x1392, 16, {0x43, 0x02, 0x14, 0x65, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5, 0x36, 0x02, 0x13, 0x43} }, +{ 0x13a2, 16, {0x43, 0x82, 0x04, 0xe0, 0x43, 0x2c, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x05} }, +{ 0x13b2, 16, {0xe0, 0x42, 0x32, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13, 0x43, 0x30, 0xe1, 0x02} }, +{ 0x13c2, 16, {0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe0, 0x0a, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x04, 0xe0} }, +{ 0x13d2, 16, {0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74, 0x00, 0xf0, 0x90, 0x20} }, +{ 0x13e2, 16, {0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86} }, +{ 0x13f2, 16, {0x90, 0x7e, 0x80, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f} }, +{ 0x1402, 16, {0xe5, 0xe5, 0x3c, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0} }, +{ 0x1412, 16, {0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x90} }, +{ 0x1422, 16, {0x7f, 0xb7, 0xed, 0xf0, 0x90, 0x20, 0x01, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x7f, 0x40} }, +{ 0x1432, 16, {0x90, 0x7e, 0x80, 0x05, 0x86, 0x90, 0x20, 0x00, 0xe5, 0x84, 0xfe, 0x24, 0x05, 0xfd, 0x8d, 0x84} }, +{ 0x1442, 16, {0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xef, 0x05} }, +{ 0x1452, 16, {0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xb7, 0xf0, 0x05, 0x86, 0xa3, 0xe0, 0x54, 0xfe, 0xf0} }, +{ 0x1462, 16, {0x02, 0x13, 0x43, 0x53, 0x2c, 0xfa, 0xe5, 0x22, 0x60, 0x08, 0x75, 0x22, 0x00, 0xd2, 0xe7, 0xfe} }, +{ 0x1472, 16, {0x80, 0x0a, 0x90, 0x7f, 0xc7, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x14, 0xff, 0x90, 0x20, 0x50, 0x74} }, +{ 0x1482, 16, {0x00, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90} }, +{ 0x1492, 16, {0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7e, 0x40, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0} }, +{ 0x14a2, 16, {0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0, 0x24, 0x38, 0xf0, 0x05} }, +{ 0x14b2, 16, {0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60, 0x30, 0x03, 0x03, 0x03} }, +{ 0x14c2, 16, {0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0} }, +{ 0x14d2, 16, {0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11, 0x8b, 0x22, 0x90, 0x7f} }, +{ 0x14e2, 16, {0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80, 0x1b, 0xe0, 0xde, 0xfd} }, +{ 0x14f2, 16, {0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x90, 0x20, 0x01} }, +{ 0x1502, 16, {0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xc7, 0xf0, 0x02, 0x13, 0x43, 0x90, 0x20, 0x0a, 0xe0, 0x54} }, +{ 0x1512, 16, {0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14, 0x60, 0x09, 0x02} }, +{ 0x1522, 16, {0x13, 0x43, 0x02, 0x15, 0xf6, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5, 0x37, 0x02, 0x13} }, +{ 0x1532, 16, {0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2d, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82, 0xf8, 0x43, 0x82} }, +{ 0x1542, 16, {0x05, 0xe0, 0x42, 0x33, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13, 0x43, 0x30, 0xe1} }, +{ 0x1552, 16, {0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe1, 0x0a, 0x53, 0x82, 0xf8, 0x43, 0x82, 0x04} }, +{ 0x1562, 16, {0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74, 0x01, 0xf0, 0x90} }, +{ 0x1572, 16, {0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05} }, +{ 0x1582, 16, {0x86, 0x90, 0x7e, 0x00, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90} }, +{ 0x1592, 16, {0x7f, 0xe5, 0xe5, 0x3d, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0} }, +{ 0x15a2, 16, {0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} }, +{ 0x15b2, 16, {0x90, 0x7f, 0xb9, 0xed, 0xf0, 0x90, 0x20, 0x09, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x7f} }, +{ 0x15c2, 16, {0x40, 0x90, 0x7e, 0x00, 0x05, 0x86, 0x90, 0x20, 0x08, 0xe5, 0x84, 0xfe, 0x24, 0x05, 0xfd, 0x8d} }, +{ 0x15d2, 16, {0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05, 0x86, 0xdf, 0xef} }, +{ 0x15e2, 16, {0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xb9, 0xf0, 0x05, 0x86, 0xa3, 0xe0, 0x54, 0xfe} }, +{ 0x15f2, 16, {0xf0, 0x02, 0x13, 0x43, 0x53, 0x2d, 0xfa, 0xe5, 0x23, 0x60, 0x08, 0x75, 0x23, 0x00, 0xd2, 0xe7} }, +{ 0x1602, 16, {0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xc9, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x16, 0x90, 0x90, 0x20, 0x50} }, +{ 0x1612, 16, {0x74, 0x01, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0} }, +{ 0x1622, 16, {0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0xc0, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84} }, +{ 0x1632, 16, {0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0, 0x24, 0x38, 0xf0} }, +{ 0x1642, 16, {0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60, 0x30, 0x03, 0x03} }, +{ 0x1652, 16, {0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0} }, +{ 0x1662, 16, {0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11, 0x8b, 0x23, 0x90} }, +{ 0x1672, 16, {0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80, 0x1b, 0xe0, 0xde} }, +{ 0x1682, 14, {0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} }, +{ 0x1690, 16, {0x90, 0x20, 0x09, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xc9, 0xf0, 0x02, 0x13, 0x43, 0x90, 0x20} }, +{ 0x16a0, 16, {0x12, 0xe0, 0x54, 0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5, 0x14} }, +{ 0x16b0, 16, {0x60, 0x09, 0x02, 0x13, 0x43, 0x02, 0x17, 0x87, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0xf5} }, +{ 0x16c0, 16, {0x38, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2e, 0x01, 0x02, 0x13, 0x43, 0x53, 0x82} }, +{ 0x16d0, 16, {0xf8, 0x43, 0x82, 0x05, 0xe0, 0x42, 0x34, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02, 0x13} }, +{ 0x16e0, 16, {0x43, 0x30, 0xe1, 0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe2, 0x0a, 0x53, 0x82, 0xf8} }, +{ 0x16f0, 16, {0x43, 0x82, 0x04, 0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50, 0x74} }, +{ 0x1700, 16, {0x02, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0, 0x90} }, +{ 0x1710, 16, {0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x80, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84, 0xf0} }, +{ 0x1720, 16, {0x05, 0x86, 0x90, 0x7f, 0xe5, 0xe5, 0x3e, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0, 0xf0} }, +{ 0x1730, 16, {0xf0, 0xf0, 0xf0, 0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58} }, +{ 0x1740, 16, {0x74, 0x00, 0xf0, 0x90, 0x7f, 0xbb, 0xed, 0xf0, 0x90, 0x20, 0x11, 0xe0, 0x54, 0xfe, 0xf0, 0x02} }, +{ 0x1750, 16, {0x13, 0x43, 0x7f, 0x40, 0x90, 0x7d, 0x80, 0x05, 0x86, 0x90, 0x20, 0x10, 0xe5, 0x84, 0xfe, 0x24} }, +{ 0x1760, 16, {0x05, 0xfd, 0x8d, 0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3, 0x05} }, +{ 0x1770, 16, {0x86, 0xdf, 0xef, 0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xbb, 0xf0, 0x05, 0x86, 0xa3} }, +{ 0x1780, 16, {0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x53, 0x2e, 0xfa, 0xe5, 0x24, 0x60, 0x08, 0x75, 0x24} }, +{ 0x1790, 16, {0x00, 0xd2, 0xe7, 0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xcb, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x18, 0x21} }, +{ 0x17a0, 16, {0x90, 0x20, 0x50, 0x74, 0x02, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0} }, +{ 0x17b0, 16, {0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x40, 0x05, 0x86, 0xe5, 0x85, 0xf0} }, +{ 0x17c0, 16, {0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86, 0xe0} }, +{ 0x17d0, 16, {0x24, 0x38, 0xf0, 0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78, 0x60} }, +{ 0x17e0, 16, {0x30, 0x03, 0x03, 0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0, 0xe0} }, +{ 0x17f0, 16, {0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70, 0x11} }, +{ 0x1800, 16, {0x8b, 0x24, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0, 0x80} }, +{ 0x1810, 16, {0x1b, 0xe0, 0xde, 0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00} }, +{ 0x1820, 16, {0xf0, 0x90, 0x20, 0x11, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xcb, 0xf0, 0x02, 0x13, 0x43, 0x90} }, +{ 0x1830, 16, {0x20, 0x1a, 0xe0, 0x54, 0x3f, 0x20, 0xe2, 0x3a, 0x20, 0xe1, 0x0b, 0x20, 0xe4, 0x0b, 0x20, 0xe5} }, +{ 0x1840, 16, {0x14, 0x60, 0x09, 0x02, 0x13, 0x43, 0x02, 0x19, 0x18, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0} }, +{ 0x1850, 16, {0xf5, 0x39, 0x02, 0x13, 0x43, 0x43, 0x82, 0x04, 0xe0, 0x43, 0x2f, 0x01, 0x02, 0x13, 0x43, 0x53} }, +{ 0x1860, 16, {0x82, 0xf8, 0x43, 0x82, 0x05, 0xe0, 0x42, 0x35, 0x53, 0x82, 0xfb, 0xe0, 0x54, 0xfb, 0xf0, 0x02} }, +{ 0x1870, 16, {0x13, 0x43, 0x30, 0xe1, 0x02, 0x80, 0xe8, 0xf5, 0x85, 0xe5, 0x3b, 0x30, 0xe3, 0x0a, 0x53, 0x82} }, +{ 0x1880, 16, {0xf8, 0x43, 0x82, 0x04, 0xe0, 0x54, 0xfe, 0xf0, 0xe5, 0x85, 0x20, 0xe3, 0x56, 0x90, 0x20, 0x50} }, +{ 0x1890, 16, {0x74, 0x03, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2, 0xe0, 0x44, 0x40, 0xf0} }, +{ 0x18a0, 16, {0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7d, 0x00, 0x05, 0x86, 0xe5, 0x85, 0xf0, 0xa3, 0xe5, 0x84} }, +{ 0x18b0, 16, {0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xe5, 0x3f, 0xfd, 0x03, 0x03, 0x03, 0xfe, 0xf0, 0xf0, 0xf0} }, +{ 0x18c0, 16, {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xde, 0xf6, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20} }, +{ 0x18d0, 16, {0x58, 0x74, 0x00, 0xf0, 0x90, 0x7f, 0xbd, 0xed, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x54, 0xfe, 0xf0} }, +{ 0x18e0, 16, {0x02, 0x13, 0x43, 0x7f, 0x40, 0x90, 0x7d, 0x00, 0x05, 0x86, 0x90, 0x20, 0x18, 0xe5, 0x84, 0xfe} }, +{ 0x18f0, 16, {0x24, 0x05, 0xfd, 0x8d, 0x84, 0xe0, 0x8e, 0x84, 0x30, 0xe0, 0x09, 0xe0, 0x05, 0x86, 0xf0, 0xa3} }, +{ 0x1900, 16, {0x05, 0x86, 0xdf, 0xef, 0x05, 0x86, 0xc3, 0x74, 0x40, 0x9f, 0x90, 0x7f, 0xbd, 0xf0, 0x05, 0x86} }, +{ 0x1910, 16, {0xa3, 0xe0, 0x54, 0xfe, 0xf0, 0x02, 0x13, 0x43, 0x53, 0x2f, 0xfa, 0xe5, 0x25, 0x60, 0x08, 0x75} }, +{ 0x1920, 16, {0x25, 0x00, 0xd2, 0xe7, 0xfe, 0x80, 0x0a, 0x90, 0x7f, 0xcd, 0xe0, 0xfe, 0x70, 0x03, 0x02, 0x19} }, +{ 0x1930, 16, {0xb2, 0x90, 0x20, 0x50, 0x74, 0x03, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xe2} }, +{ 0x1940, 16, {0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xe3, 0x05, 0x86, 0x90, 0x7c, 0xc0, 0x05, 0x86, 0xe5, 0x85} }, +{ 0x1950, 16, {0xf0, 0xa3, 0xe5, 0x84, 0xf0, 0x05, 0x86, 0x90, 0x7f, 0xe5, 0xee, 0x30, 0xe7, 0x08, 0x05, 0x86} }, +{ 0x1960, 16, {0xe0, 0x24, 0x38, 0xf0, 0x05, 0x86, 0xee, 0x54, 0x7f, 0xfe, 0x54, 0x07, 0xfb, 0xee, 0x54, 0x78} }, +{ 0x1970, 16, {0x60, 0x30, 0x03, 0x03, 0x03, 0x30, 0xe3, 0x04, 0x74, 0x07, 0x7b, 0x08, 0xfd, 0xfc, 0xe0, 0xe0} }, +{ 0x1980, 16, {0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xe0, 0xdd, 0xf6, 0xeb, 0xfe, 0x60, 0x19, 0xec, 0x64, 0x07, 0x70} }, +{ 0x1990, 16, {0x11, 0x8b, 0x25, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74, 0x00, 0xf0} }, +{ 0x19a0, 16, {0x80, 0x1b, 0xe0, 0xde, 0xfd, 0x90, 0x7f, 0xe2, 0xe0, 0x54, 0xbf, 0xf0, 0x90, 0x20, 0x58, 0x74} }, +{ 0x19b0, 16, {0x00, 0xf0, 0x90, 0x20, 0x19, 0xe0, 0x54, 0xfd, 0xf0, 0x90, 0x7f, 0xcd, 0xf0, 0x02, 0x13, 0x43} }, +{ 0x19c0, 1, {0x32} }, +{ 0x19c1, 4, {0xad, 0x07, 0xac, 0x06} }, +{ 0x19c5, 16, {0x79, 0x06, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb} }, +{ 0x19d5, 16, {0x4a, 0x70, 0x03, 0x02, 0x1b, 0x09, 0xe9, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x1a, 0xdb, 0x90} }, +{ 0x19e5, 16, {0x19, 0xeb, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x1a, 0xb9, 0x02, 0x1a, 0x71, 0x02, 0x1a, 0x5a, 0x02} }, +{ 0x19f5, 16, {0x1a, 0x40, 0x02, 0x1a, 0x2f, 0x02, 0x1a, 0x1a, 0x02, 0x1a, 0x00, 0x90, 0x7f, 0xa5, 0xe0, 0x44} }, +{ 0x1a05, 16, {0x80, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90, 0x7f, 0xa6} }, +{ 0x1a15, 16, {0xf0, 0x19, 0x02, 0x1a, 0xdb, 0x19, 0x8d, 0x82, 0x8c, 0x83, 0xe0, 0xc3, 0x94, 0x20, 0x40, 0x0a} }, +{ 0x1a25, 16, {0xa3, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x02, 0x1a, 0xdb, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3} }, +{ 0x1a35, 16, {0xe0, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x19, 0x02, 0x1a, 0xdb, 0x90, 0x7f, 0xa5, 0xe0, 0x44} }, +{ 0x1a45, 16, {0x80, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa1, 0x90, 0x7f, 0xa6} }, +{ 0x1a55, 16, {0xf0, 0x19, 0x02, 0x1a, 0xdb, 0xeb, 0x64, 0x01, 0x4a, 0x70, 0x08, 0x90, 0x7f, 0xa5, 0xe0, 0x44} }, +{ 0x1a65, 16, {0x20, 0xf0, 0x19, 0x90, 0x7f, 0xa6, 0xe0, 0xf5, 0x54, 0x19, 0x80, 0x6a, 0xed, 0x24, 0x04, 0xf5} }, +{ 0x1a75, 16, {0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfe, 0xa3, 0xe0, 0x64, 0x02, 0x4e, 0x70, 0x08, 0x90, 0x7f} }, +{ 0x1a85, 16, {0xa5, 0xe0, 0x44, 0x20, 0xf0, 0x19, 0x90, 0x7f, 0xa6, 0xe0, 0xff, 0xed, 0x24, 0x06, 0xf5, 0x82} }, +{ 0x1a95, 16, {0xe4, 0x3c, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83} }, +{ 0x1aa5, 16, {0xef, 0xf0, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0x74, 0xff, 0xf5, 0xf0, 0x12} }, +{ 0x1ab5, 16, {0x9b, 0xb8, 0x80, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xff} }, +{ 0x1ac5, 16, {0xed, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x3c, 0xf5, 0x83, 0xe0, 0xfa, 0xa3, 0xe0, 0xf5, 0x82, 0x8a} }, +{ 0x1ad5, 16, {0x83, 0xef, 0xf0, 0x7f, 0x08, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0xf5, 0x54, 0x30, 0xe0, 0xf7, 0x30} }, +{ 0x1ae5, 16, {0xe2, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06, 0x22, 0xe9, 0xd3, 0x94, 0x02, 0x50, 0x03, 0x02} }, +{ 0x1af5, 16, {0x19, 0xc7, 0xe5, 0x54, 0x30, 0xe1, 0x03, 0x02, 0x19, 0xc7, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40} }, +{ 0x1b05, 6, {0xf0, 0x7f, 0x07, 0x22, 0x7f, 0x08} }, +{ 0x1b0b, 1, {0x22} }, +{ 0x1b0c, 16, {0x7f, 0x05, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x02, 0x7d, 0xff, 0x12, 0x81, 0xe0, 0x7f, 0x05} }, +{ 0x1b1c, 13, {0x7e, 0x00, 0x12, 0x09, 0x23, 0x7f, 0x03, 0x7d, 0xff, 0x12, 0x81, 0xe0, 0x22} }, +{ 0x1b29, 16, {0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9, 0xf8, 0xf7, 0xf6, 0xf5, 0xf4, 0xf3, 0xf2, 0xf1, 0xf0} }, +{ 0x8000, 4, {0x8e, 0x54, 0x8f, 0x55} }, +{ 0x8004, 16, {0x75, 0x56, 0x03, 0xe5, 0x55, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xfe} }, +{ 0x8014, 16, {0xa3, 0xe0, 0x4e, 0x70, 0x03, 0x02, 0x81, 0x0e, 0xe5, 0x56, 0x60, 0x4e, 0x14, 0x60, 0x38, 0x14} }, +{ 0x8024, 16, {0x60, 0x20, 0x14, 0x60, 0x03, 0x02, 0x80, 0xb2, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0x85} }, +{ 0x8034, 16, {0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90, 0x7f, 0xa6, 0xf0} }, +{ 0x8044, 16, {0x80, 0x6c, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0xc3, 0x94, 0x20, 0x40, 0x09, 0xa3, 0xa3} }, +{ 0x8054, 16, {0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x57, 0x15, 0x56, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3} }, +{ 0x8064, 16, {0xa3, 0xe0, 0xa3, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x44, 0xe5, 0x55, 0x24, 0x06, 0xf5, 0x82} }, +{ 0x8074, 16, {0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5} }, +{ 0x8084, 16, {0x83, 0xe0, 0x90, 0x7f, 0xa6, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe5, 0x55, 0x24} }, +{ 0x8094, 16, {0x04, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0x74, 0xff, 0xf5, 0xf0, 0x12, 0x9b, 0xb8, 0x85} }, +{ 0x80a4, 16, {0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xb8, 0x90, 0x7f} }, +{ 0x80b4, 16, {0xa5, 0xe0, 0xf5, 0x57, 0x30, 0xe0, 0xf7, 0x30, 0xe2, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06} }, +{ 0x80c4, 16, {0x22, 0xe5, 0x57, 0x20, 0xe1, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x07, 0x22} }, +{ 0x80d4, 16, {0xe5, 0x56, 0x70, 0x31, 0x7f, 0x01, 0x7e, 0x00, 0x12, 0x09, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44} }, +{ 0x80e4, 16, {0x80, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x25, 0xe0, 0x44, 0xa0, 0x90} }, +{ 0x80f4, 16, {0x7f, 0xa6, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0xf5, 0x57, 0x30, 0xe0, 0xf7, 0x30, 0xe1, 0xd5, 0x75} }, +{ 0x8104, 12, {0x56, 0x03, 0x02, 0x80, 0x07, 0x15, 0x56, 0x02, 0x80, 0x07, 0x7f, 0x08} }, +{ 0x8110, 1, {0x22} }, +{ 0x8111, 2, {0xac, 0x07} }, +{ 0x8113, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xec, 0x25, 0xe0, 0x44, 0x41, 0x90, 0x7f, 0xa6, 0xf0} }, +{ 0x8123, 16, {0x7b, 0x3c, 0xaf, 0x03, 0x1b, 0xef, 0x70, 0x16, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90} }, +{ 0x8133, 16, {0x7f, 0xa6, 0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x60, 0xd4, 0x80, 0xf8, 0x90, 0x7f} }, +{ 0x8143, 16, {0xa5, 0xe0, 0xfd, 0x30, 0xe0, 0xdc, 0x20, 0xe1, 0x09, 0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f} }, +{ 0x8153, 16, {0xf9, 0x22, 0xed, 0x30, 0xe2, 0x0c, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f} }, +{ 0x8163, 16, {0xfa, 0x22, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0x7b, 0x1e} }, +{ 0x8173, 16, {0xaf, 0x03, 0x1b, 0xef, 0x70, 0x16, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6} }, +{ 0x8183, 16, {0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x60, 0x86, 0x80, 0xf8, 0x90, 0x7f, 0xa5, 0xe0} }, +{ 0x8193, 16, {0xfd, 0x20, 0xe0, 0xdc, 0x7b, 0x3c, 0xaf, 0x03, 0x1b, 0xef, 0x70, 0x19, 0x90, 0x7f, 0xa5, 0xe0} }, +{ 0x81a3, 16, {0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0x7d, 0x32, 0xaf, 0x05, 0x1d, 0xef, 0x70, 0x03} }, +{ 0x81b3, 16, {0x02, 0x81, 0x13, 0x80, 0xf5, 0x90, 0x7f, 0xa5, 0xe0, 0xfd, 0x30, 0xe0, 0xd9, 0x30, 0xe2, 0x09} }, +{ 0x81c3, 16, {0xe0, 0x44, 0x40, 0xf0, 0x7e, 0xff, 0x7f, 0xfa, 0x22, 0xc2, 0xaf, 0x90, 0x7f, 0xa5, 0xe0, 0x44} }, +{ 0x81d3, 12, {0x40, 0xf0, 0x90, 0x7f, 0xa6, 0xe0, 0xfd, 0xd2, 0xaf, 0xff, 0x7e, 0x00} }, +{ 0x81df, 1, {0x22} }, +{ 0x81e0, 2, {0xae, 0x07} }, +{ 0x81e2, 16, {0x7c, 0x02, 0xec, 0x14, 0x60, 0x15, 0x14, 0x70, 0x1e, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0} }, +{ 0x81f2, 16, {0xee, 0x25, 0xe0, 0x44, 0x40, 0x90, 0x7f, 0xa6, 0xf0, 0x80, 0x0c, 0x90, 0x7f, 0xa6, 0xed, 0xf0} }, +{ 0x8202, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x90, 0x7f, 0xa5, 0xe0, 0xfb, 0x30, 0xe0, 0xf8, 0xbc} }, +{ 0x8212, 16, {0x02, 0x0a, 0x20, 0xe1, 0x07, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x07, 0x22, 0xeb, 0x30, 0xe2, 0x0a} }, +{ 0x8222, 14, {0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x06, 0x22, 0xdc, 0xb6, 0x7f, 0x08} }, +{ 0x8230, 1, {0x22} }, +{ 0x8231, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc2, 0xa9, 0x90, 0x03, 0x00, 0x74, 0x19, 0xf0, 0xd2, 0xa9} }, +{ 0x8241, 15, {0x53, 0x91, 0x7f, 0x90, 0x01, 0xc4, 0xe4, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x8250, 16, {0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xab, 0x82, 0xfa, 0xf5} }, +{ 0x8260, 16, {0x83, 0xa3, 0xe4, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0xf5, 0x5e, 0x74, 0xbf} }, +{ 0x8270, 16, {0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xe0, 0x44, 0x10, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3} }, +{ 0x8280, 16, {0xa3, 0xa3, 0xe4, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xf0, 0xf9, 0xed, 0x60, 0x1d, 0x74, 0x01} }, +{ 0x8290, 16, {0x7e, 0x00, 0xa8, 0x07, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff, 0xe4} }, +{ 0x82a0, 16, {0xef, 0x55, 0x3b, 0x60, 0x04, 0x79, 0x09, 0x80, 0x02, 0x79, 0x0d, 0x8b, 0x82, 0x8a, 0x83, 0xa3} }, +{ 0x82b0, 16, {0xa3, 0xa3, 0x74, 0xbf, 0xf0, 0x8b, 0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xe0, 0x54, 0xef, 0xf0, 0x8b} }, +{ 0x82c0, 16, {0x82, 0x8a, 0x83, 0xa3, 0xa3, 0xa3, 0xe5, 0x5e, 0xf0, 0xae, 0x02, 0xaf, 0x03, 0x8f, 0x82, 0x8e} }, +{ 0x82d0, 4, {0x83, 0xa3, 0xe9, 0xf0} }, +{ 0x82d4, 1, {0x22} }, +{ 0x82d5, 4, {0x8f, 0x5e, 0x8d, 0x5f} }, +{ 0x82d9, 16, {0xe4, 0xf5, 0x60, 0x74, 0x3c, 0x2f, 0xf8, 0x76, 0x08, 0xe5, 0x5e, 0x75, 0xf0, 0x0d, 0xa4, 0x24} }, +{ 0x82e9, 16, {0x02, 0xf5, 0x82, 0xe4, 0x34, 0x03, 0xf5, 0x83, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe} }, +{ 0x82f9, 16, {0xa3, 0xe0, 0xff, 0x7b, 0x80, 0x7a, 0x25, 0x79, 0x00, 0x78, 0x00, 0xc3, 0x12, 0x9c, 0xea, 0x50} }, +{ 0x8309, 16, {0x3c, 0xe5, 0x5e, 0x75, 0xf0, 0x0d, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, 0x34, 0x03, 0xf5, 0x83} }, +{ 0x8319, 16, {0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0x7b, 0x00, 0x7a, 0x96, 0x78} }, +{ 0x8329, 16, {0x00, 0xc3, 0x12, 0x9c, 0xea, 0x40, 0x0c, 0x75, 0x60, 0x40, 0x74, 0x3c, 0x25, 0x5e, 0xf8, 0x76} }, +{ 0x8339, 16, {0x10, 0x80, 0x0a, 0x75, 0x60, 0x80, 0x74, 0x3c, 0x25, 0x5e, 0xf8, 0x76, 0x38, 0xe5, 0x60, 0x45} }, +{ 0x8349, 16, {0x5f, 0x44, 0x01, 0xff, 0xe5, 0x5e, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x02, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x8359, 5, {0x20, 0xf5, 0x83, 0xef, 0xf0} }, +{ 0x835e, 1, {0x22} }, +{ 0x835f, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x54, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82} }, +{ 0x836f, 16, {0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x55, 0x8f, 0x56, 0xe5, 0x54, 0x25, 0xe0, 0x24, 0xc6, 0xf5} }, +{ 0x837f, 16, {0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x0f, 0xe5, 0x54, 0x25, 0xe0, 0x24, 0xc7} }, +{ 0x838f, 16, {0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x74, 0x22, 0x25, 0x54, 0xf8, 0xe4, 0xf6} }, +{ 0x839f, 16, {0xe5, 0x56, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x55, 0xf5, 0x83, 0xe0, 0x44, 0x03, 0xf0, 0xaf} }, +{ 0x83af, 16, {0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0xaf, 0x54, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x85, 0x56, 0x82} }, +{ 0x83bf, 16, {0x85, 0x55, 0x83, 0xa3, 0xa3, 0xe0, 0x20, 0xe0, 0x22, 0xe0, 0xff, 0xe5, 0x56, 0x24, 0x05, 0xf5} }, +{ 0x83cf, 16, {0x82, 0xe4, 0x35, 0x55, 0xf5, 0x83, 0xe0, 0xe5, 0x56, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x55} }, +{ 0x83df, 16, {0xf5, 0x83, 0xe0, 0xff, 0xaf, 0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x74, 0xf8, 0x25, 0x54, 0xf5} }, +{ 0x83ef, 16, {0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x54, 0x25, 0xe0, 0xff, 0xc3, 0x74, 0x0c} }, +{ 0x83ff, 16, {0x9f, 0x75, 0xf0, 0x40, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x7b, 0xaf, 0x82, 0xfe} }, +{ 0x840f, 16, {0xe5, 0x54, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xee, 0xf0, 0xa3} }, +{ 0x841f, 16, {0xef, 0xf0, 0xaf, 0x54, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42} }, +{ 0x842f, 3, {0x30, 0x7f, 0x00} }, +{ 0x8432, 1, {0x22} }, +{ 0x8433, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x54, 0xff, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0x74, 0xf8} }, +{ 0x8443, 16, {0x25, 0x54, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0xf0, 0xe5, 0x54, 0x75, 0xf0, 0x08} }, +{ 0x8453, 16, {0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x56, 0x8f, 0x57, 0xf5, 0x83} }, +{ 0x8463, 16, {0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xaf} }, +{ 0x8473, 16, {0x54, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0xe5, 0x57, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x56, 0xf5} }, +{ 0x8483, 16, {0x83, 0xe0, 0x30, 0xe0, 0x09, 0x85, 0x57, 0x82, 0x85, 0x56, 0x83, 0xe0, 0xf5, 0x55, 0xaf, 0x54} }, +{ 0x8493, 16, {0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf4, 0x52, 0x30, 0xe5, 0x54} }, +{ 0x84a3, 16, {0x25, 0xe0, 0x24, 0xc6, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x20, 0xe1, 0x0f, 0xe5} }, +{ 0x84b3, 16, {0x54, 0x25, 0xe0, 0x24, 0xc7, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0x7f, 0x00} }, +{ 0x84c3, 1, {0x22} }, +{ 0x84c4, 4, {0x8e, 0x54, 0x8f, 0x55} }, +{ 0x84c8, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x56, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x01, 0xf5, 0x82} }, +{ 0x84d8, 16, {0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x54, 0x03, 0x70, 0x23, 0x85, 0x55, 0x82, 0x8e, 0x83, 0xa3} }, +{ 0x84e8, 16, {0xe0, 0x30, 0xe0, 0x07, 0xaf, 0x56, 0x7d, 0x02, 0x12, 0x82, 0xd5, 0x85, 0x55, 0x82, 0x85, 0x54} }, +{ 0x84f8, 15, {0x83, 0xa3, 0xe0, 0x30, 0xe1, 0x07, 0xaf, 0x56, 0x7d, 0x04, 0x12, 0x82, 0xd5, 0x7f, 0x00} }, +{ 0x8507, 1, {0x22} }, +{ 0x8508, 16, {0x8f, 0x82, 0x8e, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0xa3, 0xa3, 0xa3, 0xe0, 0xfc, 0xed} }, +{ 0x8518, 16, {0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xa3, 0xc0, 0x83, 0xc0} }, +{ 0x8528, 16, {0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0} }, +{ 0x8538, 16, {0x8f, 0x82, 0x8e, 0x83, 0xc0, 0x83, 0xc0, 0x82, 0xa3, 0xa3, 0xa3, 0xe0, 0xfd, 0xec, 0x6d, 0xd0} }, +{ 0x8548, 16, {0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x8f} }, +{ 0x8558, 16, {0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82} }, +{ 0x8568, 16, {0x8e, 0x83, 0xa3, 0xa3, 0xc0, 0x83, 0xc0, 0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xe0} }, +{ 0x8578, 16, {0xfc, 0xed, 0x6c, 0xd0, 0x82, 0xd0, 0x83, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xc0, 0x83, 0xc0} }, +{ 0x8588, 16, {0x82, 0xe0, 0xfd, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3, 0xe0, 0xff, 0xed, 0x6f, 0xd0, 0x82, 0xd0} }, +{ 0x8598, 3, {0x83, 0xf0, 0x22} }, +{ 0x859b, 16, {0x79, 0x0d, 0x8e, 0x54, 0x8f, 0x55, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x0d, 0xa4, 0x24} }, +{ 0x85ab, 16, {0xf4, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xaf, 0x82, 0xfe, 0xad, 0x01, 0x19, 0xed, 0x60, 0x24, 0x0f} }, +{ 0x85bb, 16, {0xef, 0xac, 0x06, 0x70, 0x01, 0x0e, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe0, 0xfd, 0x05, 0x55, 0xe5} }, +{ 0x85cb, 16, {0x55, 0xaa, 0x54, 0x70, 0x02, 0x05, 0x54, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xe0, 0x6d, 0x60, 0xd9} }, +{ 0x85db, 5, {0x7f, 0x01, 0x22, 0x7f, 0x00} }, +{ 0x85e0, 1, {0x22} }, +{ 0x85e1, 4, {0x8e, 0x54, 0x8f, 0x55} }, +{ 0x85e5, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xf5, 0x5b, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82} }, +{ 0x85f5, 16, {0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x5c, 0x8f, 0x5d, 0xaa, 0x06, 0xa9, 0x55, 0x7b, 0x01, 0xc0} }, +{ 0x8605, 16, {0x03, 0xc0, 0x01, 0xe5, 0x5b, 0x75, 0xf0, 0x0d, 0xa4, 0x24, 0x01, 0xf9, 0x74, 0x03, 0x35, 0xf0} }, +{ 0x8615, 16, {0xa8, 0x01, 0xfc, 0xad, 0x03, 0xd0, 0x01, 0xd0, 0x03, 0x7e, 0x00, 0x7f, 0x0d, 0x12, 0x9b, 0x49} }, +{ 0x8625, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3} }, +{ 0x8635, 16, {0xe0, 0xff, 0x7b, 0x08, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xd3, 0x12, 0x9c, 0xea, 0x40, 0x10} }, +{ 0x8645, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0x12, 0x9d, 0x31, 0x00, 0x00, 0x00, 0x08, 0x80, 0x2e} }, +{ 0x8655, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfc, 0xa3, 0xe0, 0xfd, 0xa3, 0xe0, 0xfe, 0xa3} }, +{ 0x8665, 16, {0xe0, 0xff, 0x7b, 0x00, 0x7a, 0x08, 0x79, 0x07, 0x78, 0x00, 0xc3, 0x12, 0x9c, 0xea, 0x50, 0x0e} }, +{ 0x8675, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0x12, 0x9d, 0x31, 0x00, 0x07, 0x08, 0x00, 0x85, 0x55} }, +{ 0x8685, 16, {0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xf8, 0xa3, 0xe0, 0xf9, 0xa3, 0xe0, 0xfa, 0xa3, 0xe0, 0xfb} }, +{ 0x8695, 16, {0x7f, 0x00, 0x7e, 0x50, 0x7d, 0x46, 0x7c, 0x00, 0x12, 0x9c, 0x58, 0x8f, 0x59, 0x8e, 0x58, 0x8d} }, +{ 0x86a5, 16, {0x57, 0x8c, 0x56, 0x7b, 0x0a, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0x12, 0x9c, 0x58, 0xaf, 0x03} }, +{ 0x86b5, 16, {0x8f, 0x5a, 0xaf, 0x59, 0xae, 0x58, 0xad, 0x57, 0xac, 0x56, 0x7b, 0x0a, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x86c5, 16, {0x78, 0x00, 0x12, 0x9c, 0x58, 0x8f, 0x59, 0x8e, 0x58, 0x8d, 0x57, 0x8c, 0x56, 0xe5, 0x5a, 0xc3} }, +{ 0x86d5, 16, {0x94, 0x05, 0x40, 0x15, 0xe5, 0x59, 0x24, 0x01, 0xf5, 0x59, 0xe4, 0x35, 0x58, 0xf5, 0x58, 0xe4} }, +{ 0x86e5, 16, {0x35, 0x57, 0xf5, 0x57, 0xe4, 0x35, 0x56, 0xf5, 0x56, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3} }, +{ 0x86f5, 16, {0xe4, 0xf0, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x44, 0x80, 0xf0, 0x85} }, +{ 0x8705, 16, {0x5d, 0x82, 0x85, 0x5c, 0x83, 0xe5, 0x59, 0xf0, 0xaf, 0x59, 0xae, 0x58, 0xad, 0x57, 0xac, 0x56} }, +{ 0x8715, 16, {0x78, 0x08, 0x12, 0x9c, 0xfb, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xef, 0xf0, 0x85, 0x5d} }, +{ 0x8725, 16, {0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0xe4, 0xf5, 0x5a, 0xe5, 0x55} }, +{ 0x8735, 16, {0x24, 0x08, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xb4, 0x62, 0x05, 0x43, 0x5a} }, +{ 0x8745, 16, {0x0a, 0x80, 0x10, 0xef, 0xb4, 0x72, 0x05, 0x43, 0x5a, 0x08, 0x80, 0x07, 0xef, 0xb4, 0x74, 0x03} }, +{ 0x8755, 16, {0x43, 0x5a, 0x02, 0xe5, 0x55, 0x24, 0x0b, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff} }, +{ 0x8765, 16, {0x30, 0xe3, 0x03, 0x43, 0x5a, 0x80, 0xef, 0x30, 0xe7, 0x12, 0x43, 0x5a, 0x40, 0xe5, 0x5d, 0x24} }, +{ 0x8775, 16, {0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0xe5, 0x55, 0x24, 0x0b} }, +{ 0x8785, 16, {0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0x20, 0xe1, 0x03, 0x30, 0xe4, 0x23, 0xaf} }, +{ 0x8795, 16, {0x5b, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x42, 0x3b, 0xe5, 0x5d} }, +{ 0x87a5, 16, {0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x44, 0x01, 0xf0, 0xe4, 0xf5, 0x5a} }, +{ 0x87b5, 16, {0x80, 0x10, 0xaf, 0x5b, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf4} }, +{ 0x87c5, 16, {0x52, 0x3b, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0x74, 0xbf, 0xf0, 0x85, 0x5d} }, +{ 0x87d5, 16, {0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xe4, 0xf0, 0xe5, 0x5a, 0xf0, 0xe5, 0x55, 0x24, 0x0a, 0xf5} }, +{ 0x87e5, 16, {0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35} }, +{ 0x87f5, 16, {0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x55, 0x24, 0x0a, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83} }, +{ 0x8805, 16, {0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5} }, +{ 0x8815, 16, {0x55, 0x24, 0x09, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x06} }, +{ 0x8825, 16, {0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x55, 0x24, 0x09, 0xf5, 0x82, 0xe4} }, +{ 0x8835, 16, {0x35, 0x54, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x5d, 0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5} }, +{ 0x8845, 16, {0x83, 0xef, 0xf0, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe4, 0xf0, 0x85, 0x5d} }, +{ 0x8855, 16, {0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xf0, 0xaf, 0x5b, 0x7d, 0x06, 0x12, 0x82, 0xd5, 0x75, 0x5a} }, +{ 0x8865, 16, {0x08, 0xe5, 0x55, 0x24, 0x0c, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0x60, 0x03, 0x43} }, +{ 0x8875, 16, {0x5a, 0x10, 0xe5, 0x5d, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x5c, 0xf5, 0x83, 0xe0, 0x54, 0x03} }, +{ 0x8885, 16, {0x45, 0x5a, 0xf0, 0xe5, 0x55, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5, 0x83, 0xe0, 0x14} }, +{ 0x8895, 16, {0xff, 0x25, 0xe0, 0x25, 0xe0, 0xff, 0xe5, 0x55, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x54, 0xf5} }, +{ 0x88a5, 16, {0x83, 0xe0, 0x24, 0xfb, 0x4f, 0xf5, 0x5a, 0xe5, 0x55, 0x24, 0x07, 0xf5, 0x82, 0xe4, 0x35, 0x54} }, +{ 0x88b5, 16, {0xf5, 0x83, 0xe0, 0x24, 0xd0, 0x60, 0x15, 0x14, 0x60, 0x17, 0x24, 0xc2, 0x60, 0x09, 0x24, 0x0a} }, +{ 0x88c5, 16, {0x70, 0x12, 0x43, 0x5a, 0x18, 0x80, 0x0d, 0x43, 0x5a, 0x08, 0x80, 0x08, 0x43, 0x5a, 0x38, 0x80} }, +{ 0x88d5, 16, {0x03, 0x43, 0x5a, 0x28, 0x85, 0x5d, 0x82, 0x85, 0x5c, 0x83, 0xa3, 0xa3, 0xa3, 0xe5, 0x5a, 0xf0} }, +{ 0x88e5, 9, {0xaf, 0x5b, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x7f, 0x00} }, +{ 0x88ee, 1, {0x22} }, +{ 0x88ef, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x14, 0xff, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4} }, +{ 0x88ff, 16, {0x34, 0x20, 0xad, 0x82, 0xfc, 0x90, 0x01, 0x2c, 0x74, 0x08, 0xf0, 0xef, 0x04, 0xa3, 0xf0, 0xe4} }, +{ 0x890f, 16, {0xa3, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x06, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5} }, +{ 0x891f, 16, {0x83, 0xe0, 0x90, 0x01, 0x2f, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xe5, 0x82, 0x24, 0x05, 0xf5, 0x82} }, +{ 0x892f, 16, {0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0x1e, 0x90, 0x01, 0x30, 0xf0, 0x74, 0x2c, 0x2f, 0xf8} }, +{ 0x893f, 16, {0xe6, 0xa3, 0xf0, 0x74, 0x01, 0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x54} }, +{ 0x894f, 16, {0x7f, 0x02, 0x12, 0x81, 0x11, 0xc3, 0xee, 0x64, 0x80, 0x94, 0x80, 0x40, 0xf3, 0xe5, 0x54, 0x5f} }, +{ 0x895f, 16, {0x90, 0x01, 0x32, 0xf0, 0x7e, 0x01, 0x7f, 0x2c, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e} }, +{ 0x896f, 11, {0x00, 0x75, 0x5d, 0x00, 0x7d, 0x07, 0x12, 0x8e, 0x0c, 0x7f, 0x00} }, +{ 0x897a, 1, {0x22} }, +{ 0x897b, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} }, +{ 0x898b, 16, {0xaf, 0x82, 0xfe, 0x90, 0x01, 0x33, 0x74, 0x0a, 0xf0, 0x8f, 0x82, 0x8e, 0x83, 0xe5, 0x82, 0x24} }, +{ 0x899b, 16, {0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x90, 0x01, 0x34, 0xf0, 0x7e, 0x01, 0x7f} }, +{ 0x89ab, 16, {0x33, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x02, 0x12} }, +{ 0x89bb, 4, {0x8e, 0x0c, 0x7f, 0x00} }, +{ 0x89bf, 1, {0x22} }, +{ 0x89c0, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} }, +{ 0x89d0, 16, {0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0f, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c} }, +{ 0x89e0, 16, {0xf5, 0x83, 0xe0, 0x44, 0x02, 0xf0, 0x80, 0x11, 0xae, 0x04, 0xaf, 0x05, 0xef, 0x24, 0x04, 0xf5} }, +{ 0x89f0, 11, {0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0xf0, 0x7f, 0x00} }, +{ 0x89fb, 1, {0x22} }, +{ 0x89fc, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} }, +{ 0x8a0c, 16, {0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0f, 0xed, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x3c} }, +{ 0x8a1c, 16, {0xf5, 0x83, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x11, 0xae, 0x04, 0xaf, 0x05, 0xef, 0x24, 0x04, 0xf5} }, +{ 0x8a2c, 11, {0x82, 0xe4, 0x3e, 0xf5, 0x83, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x00} }, +{ 0x8a37, 1, {0x22} }, +{ 0x8a38, 16, {0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0xf8, 0xf5, 0x82, 0xe4, 0x34, 0x1f} }, +{ 0x8a48, 16, {0xad, 0x82, 0xfc, 0x8f, 0x82, 0xa3, 0xe0, 0x60, 0x0d, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3, 0xa3} }, +{ 0x8a58, 16, {0xe0, 0x44, 0x40, 0xf0, 0x80, 0x0f, 0xae, 0x04, 0xaf, 0x05, 0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xa3} }, +{ 0x8a68, 7, {0xa3, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x00} }, +{ 0x8a6f, 1, {0x22} }, +{ 0x8a70, 4, {0x8e, 0x54, 0x8f, 0x55} }, +{ 0x8a74, 16, {0x8f, 0x82, 0x8e, 0x83, 0xa3, 0xe0, 0xf5, 0x58, 0x8f, 0x82, 0x8e, 0x83, 0xe0, 0x24, 0xfe, 0x60} }, +{ 0x8a84, 16, {0x16, 0x14, 0x60, 0x1f, 0x14, 0x60, 0x28, 0x24, 0x03, 0x70, 0x2e, 0x7e, 0x7e, 0x7f, 0x80, 0x75} }, +{ 0x8a94, 16, {0x56, 0x7e, 0x75, 0x57, 0x80, 0x80, 0x22, 0x7e, 0x7e, 0x7f, 0x00, 0x75, 0x56, 0x7e, 0x75, 0x57} }, +{ 0x8aa4, 16, {0x00, 0x80, 0x16, 0x7e, 0x7d, 0x7f, 0x80, 0x75, 0x56, 0x7d, 0x75, 0x57, 0x80, 0x80, 0x0a, 0x7e} }, +{ 0x8ab4, 16, {0x7d, 0x7f, 0x00, 0x75, 0x56, 0x7d, 0x75, 0x57, 0x00, 0xe5, 0x58, 0x70, 0x20, 0x85, 0x57, 0x82} }, +{ 0x8ac4, 16, {0x85, 0x56, 0x83, 0x74, 0xff, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x25, 0xe0, 0x24} }, +{ 0x8ad4, 16, {0xb5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01, 0xf0, 0x80, 0x4d, 0xe5, 0x55, 0x24} }, +{ 0x8ae4, 16, {0x02, 0xff, 0xe4, 0x35, 0x54, 0xfe, 0xe5, 0x58, 0x60, 0x23, 0x0f, 0xef, 0xac, 0x06, 0x70, 0x01} }, +{ 0x8af4, 16, {0x0e, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe0, 0xfd, 0x05, 0x57, 0xe5, 0x57, 0xaa, 0x56, 0x70, 0x02} }, +{ 0x8b04, 16, {0x05, 0x56, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xed, 0xf0, 0x15, 0x58, 0x80, 0xd9, 0x85, 0x55, 0x82} }, +{ 0x8b14, 16, {0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x25, 0xe0, 0x24} }, +{ 0x8b24, 12, {0xb5, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x7f, 0x00} }, +{ 0x8b30, 1, {0x22} }, +{ 0x8b31, 16, {0xef, 0x24, 0x05, 0xf5, 0x55, 0xe4, 0x3e, 0xf5, 0x54, 0x90, 0x01, 0x35, 0x74, 0x07, 0xf0, 0x90} }, +{ 0x8b41, 16, {0x01, 0x7a, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x36, 0xf0, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3} }, +{ 0x8b51, 16, {0xa3, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0x8e, 0x56, 0xf5, 0x57, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} }, +{ 0x8b61, 16, {0xe0, 0x24, 0x9e, 0x60, 0x61, 0x24, 0xf9, 0x60, 0x0e, 0x24, 0xf1, 0x70, 0x03, 0x02, 0x8c, 0x12} }, +{ 0x8b71, 16, {0x24, 0x14, 0x60, 0x03, 0x02, 0x8c, 0x5e, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe} }, +{ 0x8b81, 16, {0xa3, 0xe0, 0xff, 0xc3, 0xe4, 0x9f, 0xf5, 0x59, 0x74, 0x01, 0x9e, 0xf5, 0x58, 0xd3, 0xe5, 0x59} }, +{ 0x8b91, 16, {0x94, 0x40, 0xe5, 0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x40, 0xd3, 0xe5} }, +{ 0x8ba1, 16, {0x57, 0x95, 0x59, 0xe5, 0x56, 0x95, 0x58, 0x50, 0x03, 0x02, 0x8c, 0x61, 0xae, 0x58, 0xaf, 0x59} }, +{ 0x8bb1, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e} }, +{ 0x8bc1, 16, {0x56, 0xf5, 0x57, 0x02, 0x8c, 0x61, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3} }, +{ 0x8bd1, 16, {0xe0, 0xff, 0xc3, 0x74, 0x30, 0x9f, 0xf5, 0x59, 0xe4, 0x9e, 0xf5, 0x58, 0xd3, 0xe5, 0x59, 0x94} }, +{ 0x8be1, 16, {0x10, 0xe5, 0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x10, 0xd3, 0xe5, 0x57} }, +{ 0x8bf1, 16, {0x95, 0x59, 0xe5, 0x56, 0x95, 0x58, 0x40, 0x68, 0xae, 0x58, 0xaf, 0x59, 0x85, 0x55, 0x82, 0x85} }, +{ 0x8c01, 16, {0x54, 0x83, 0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e, 0x56, 0xf5, 0x57, 0x80} }, +{ 0x8c11, 16, {0x4f, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0xe4, 0x9f} }, +{ 0x8c21, 16, {0xf5, 0x59, 0xe4, 0x9e, 0xf5, 0x58, 0x45, 0x59, 0x60, 0x0b, 0xd3, 0xe5, 0x59, 0x94, 0x40, 0xe5} }, +{ 0x8c31, 16, {0x58, 0x94, 0x00, 0x40, 0x06, 0x75, 0x58, 0x00, 0x75, 0x59, 0x40, 0xd3, 0xe5, 0x57, 0x95, 0x59} }, +{ 0x8c41, 16, {0xe5, 0x56, 0x95, 0x58, 0x40, 0x17, 0xae, 0x58, 0xaf, 0x59, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} }, +{ 0x8c51, 16, {0xa3, 0xa3, 0xa3, 0xee, 0xf0, 0xfe, 0xa3, 0xef, 0xf0, 0x8e, 0x56, 0xf5, 0x57, 0x7f, 0x01, 0x22} }, +{ 0x8c61, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xe0, 0x24, 0x9e, 0x70, 0x03, 0x02, 0x8d, 0x21, 0x24, 0xf9} }, +{ 0x8c71, 16, {0x60, 0x58, 0x24, 0xf1, 0x70, 0x03, 0x02, 0x8d, 0x71, 0x24, 0x14, 0x60, 0x03, 0x02, 0x8d, 0xb5} }, +{ 0x8c81, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xd3, 0x94, 0xff, 0xee} }, +{ 0x8c91, 16, {0x94, 0x00, 0x40, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x75, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57} }, +{ 0x8ca1, 16, {0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x70, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x75, 0xe0} }, +{ 0x8cb1, 16, {0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b} }, +{ 0x8cc1, 16, {0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd2, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83} }, +{ 0x8cd1, 16, {0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0x94, 0x80, 0xee, 0x94, 0x00, 0x50, 0x03, 0x02, 0x8d} }, +{ 0x8ce1, 16, {0xb5, 0xd3, 0xef, 0x94, 0xff, 0xee, 0x94, 0x00, 0x40, 0x03, 0x02, 0x8d, 0xb5, 0x90, 0x01, 0x76} }, +{ 0x8cf1, 16, {0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x70, 0x03, 0x02} }, +{ 0x8d01, 16, {0x8d, 0xb5, 0x90, 0x01, 0x76, 0xe0, 0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a} }, +{ 0x8d11, 16, {0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd2} }, +{ 0x8d21, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xfe, 0xa3, 0xe0, 0xff, 0xc3, 0x94, 0x20, 0xee} }, +{ 0x8d31, 16, {0x94, 0x00, 0x50, 0x03, 0x02, 0x8d, 0xb5, 0xd3, 0xef, 0x94, 0x2f, 0xee, 0x94, 0x00, 0x50, 0x74} }, +{ 0x8d41, 16, {0x90, 0x01, 0x77, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e} }, +{ 0x8d51, 16, {0x60, 0x62, 0x90, 0x01, 0x77, 0xe0, 0xff, 0x04, 0xf0, 0xa8, 0x07, 0xe6, 0xff, 0x90, 0x01, 0x7a} }, +{ 0x8d61, 16, {0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xef, 0xf0, 0x80, 0xd5} }, +{ 0x8d71, 16, {0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xe0, 0xff, 0xa3, 0xe0, 0x90, 0x01, 0x78, 0xcf, 0xf0} }, +{ 0x8d81, 16, {0xa3, 0xef, 0xf0, 0xe5, 0x57, 0x15, 0x57, 0xae, 0x56, 0x70, 0x02, 0x15, 0x56, 0x4e, 0x60, 0x24} }, +{ 0x8d91, 16, {0x90, 0x01, 0x78, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xe0} }, +{ 0x8da1, 16, {0xff, 0x90, 0x01, 0x7a, 0xe4, 0x75, 0xf0, 0x01, 0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83} }, +{ 0x8db1, 16, {0xef, 0xf0, 0x80, 0xcf, 0x7e, 0x01, 0x7f, 0x35, 0x85, 0x55, 0x82, 0x85, 0x54, 0x83, 0xa3, 0xa3} }, +{ 0x8dc1, 16, {0xa3, 0xe0, 0xa3, 0xe0, 0x04, 0xfd, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75} }, +{ 0x8dd1, 7, {0x5d, 0x00, 0x12, 0x8e, 0x0c, 0x7f, 0x00} }, +{ 0x8dd8, 1, {0x22} }, +{ 0x8dd9, 16, {0x8e, 0x61, 0x8f, 0x62, 0x8c, 0x63, 0x8d, 0x64, 0xaf, 0x03, 0x1b, 0xef, 0x60, 0x24, 0x05, 0x62} }, +{ 0x8de9, 16, {0xe5, 0x62, 0xae, 0x61, 0x70, 0x02, 0x05, 0x61, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe0, 0xff, 0x05} }, +{ 0x8df9, 16, {0x64, 0xe5, 0x64, 0xac, 0x63, 0x70, 0x02, 0x05, 0x63, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0} }, +{ 0x8e09, 3, {0x80, 0xd6, 0x22} }, +{ 0x8e0c, 16, {0x8d, 0x5c, 0xab, 0x07, 0xaa, 0x06, 0xaf, 0x60, 0xae, 0x5f, 0xad, 0x5e, 0xac, 0x5d, 0xec, 0x4d} }, +{ 0x8e1c, 16, {0x4e, 0x4f, 0x60, 0x1c, 0x90, 0x7f, 0xc2, 0xe0, 0x30, 0xe1, 0x1c, 0x7f, 0xff, 0x7e, 0xff, 0x7d} }, +{ 0x8e2c, 16, {0xff, 0x7c, 0xff, 0x78, 0x5d, 0x12, 0x9d, 0x0e, 0xec, 0x4d, 0x4e, 0x4f, 0x60, 0x09, 0x80, 0xe4} }, +{ 0x8e3c, 16, {0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0xf9, 0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x16, 0xaf, 0x03} }, +{ 0x8e4c, 16, {0xae, 0x02, 0x7c, 0x7b, 0x7d, 0x80, 0xab, 0x5c, 0x12, 0x8d, 0xd9, 0x90, 0x7f, 0xc3, 0xe5, 0x5c} }, +{ 0x8e5c, 7, {0xf0, 0x7f, 0x01, 0x22, 0x7f, 0x00, 0x22} }, +{ 0x8e63, 16, {0x90, 0x01, 0x84, 0x74, 0x0b, 0xf0, 0x90, 0x20, 0x70, 0xe0, 0x54, 0xf0, 0xff, 0xc4, 0x54, 0x0f} }, +{ 0x8e73, 16, {0x90, 0x01, 0x85, 0xf0, 0x90, 0x11, 0xfd, 0xe4, 0x93, 0x90, 0x01, 0x86, 0xf0, 0x90, 0x11, 0xfe} }, +{ 0x8e83, 16, {0xe4, 0x93, 0x90, 0x01, 0x87, 0xf0, 0xe4, 0x90, 0x01, 0x7c, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3} }, +{ 0x8e93, 16, {0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0x88, 0xf0, 0x7e} }, +{ 0x8ea3, 16, {0x01, 0x7f, 0x7c, 0x12, 0x19, 0xc1, 0x7e, 0x01, 0x7f, 0x84, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00} }, +{ 0x8eb3, 13, {0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x14, 0x12, 0x8e, 0x0c, 0x7f, 0x00} }, +{ 0x8ec0, 1, {0x22} }, +{ 0x8ec1, 4, {0x8e, 0x4b, 0x8f, 0x4c} }, +{ 0x8ec5, 16, {0x85, 0x4b, 0x4e, 0x85, 0x4c, 0x4f, 0xe5, 0x4f, 0x24, 0x01, 0xf5, 0x53, 0xe4, 0x35, 0x4e, 0xf5} }, +{ 0x8ed5, 16, {0x52, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xe0, 0x14, 0xb4, 0x0f, 0x00, 0x40, 0x03, 0x02, 0x8f} }, +{ 0x8ee5, 16, {0xd6, 0x90, 0x8e, 0xed, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x8f, 0x1a, 0x02, 0x8f, 0x26, 0x02, 0x8f} }, +{ 0x8ef5, 16, {0x32, 0x02, 0x8f, 0x5c, 0x02, 0x8f, 0x67, 0x02, 0x8f, 0x72, 0x02, 0x8f, 0x7d, 0x02, 0x8f, 0x88} }, +{ 0x8f05, 16, {0x02, 0x8f, 0x93, 0x02, 0x8f, 0x9e, 0x02, 0x8f, 0xa9, 0x02, 0x8f, 0xb0, 0x02, 0x8f, 0xd6, 0x02} }, +{ 0x8f15, 16, {0x8f, 0xbb, 0x02, 0x8f, 0xc6, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x83, 0x5f, 0x8f, 0x4d, 0x02, 0x8f} }, +{ 0x8f25, 16, {0xd6, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x84, 0x33, 0x8f, 0x4d, 0x02, 0x8f, 0xd6, 0x85, 0x52, 0x50} }, +{ 0x8f35, 16, {0x85, 0x53, 0x51, 0xe5, 0x51, 0x24, 0x01, 0xff, 0xe4, 0x35, 0x50, 0xfe, 0x12, 0x85, 0x08, 0xaf} }, +{ 0x8f45, 16, {0x51, 0xae, 0x50, 0x12, 0x85, 0x9b, 0xef, 0x70, 0x03, 0x02, 0x8f, 0xd6, 0xaf, 0x51, 0xae, 0x50} }, +{ 0x8f55, 16, {0x12, 0x85, 0xe1, 0x8f, 0x4d, 0x80, 0x7a, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0xc0, 0x8f, 0x4d} }, +{ 0x8f65, 16, {0x80, 0x6f, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0xfc, 0x8f, 0x4d, 0x80, 0x64, 0xaf, 0x53, 0xae} }, +{ 0x8f75, 16, {0x52, 0x12, 0x8a, 0x38, 0x8f, 0x4d, 0x80, 0x59, 0xaf, 0x4c, 0xae, 0x4b, 0x12, 0x8b, 0x31, 0x8f} }, +{ 0x8f85, 16, {0x4d, 0x80, 0x4e, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x88, 0xef, 0x8f, 0x4d, 0x80, 0x43, 0xaf, 0x53} }, +{ 0x8f95, 16, {0xae, 0x52, 0x12, 0x84, 0xc4, 0x8f, 0x4d, 0x80, 0x38, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x89, 0x7b} }, +{ 0x8fa5, 16, {0x8f, 0x4d, 0x80, 0x2d, 0x12, 0x8e, 0x63, 0x8f, 0x4d, 0x80, 0x26, 0xaf, 0x53, 0xae, 0x52, 0x12} }, +{ 0x8fb5, 16, {0x11, 0xe5, 0x8f, 0x4d, 0x80, 0x1b, 0xaf, 0x53, 0xae, 0x52, 0x12, 0x8a, 0x70, 0x8f, 0x4d, 0x80} }, +{ 0x8fc5, 16, {0x10, 0xaf, 0x4c, 0xae, 0x4b, 0x7c, 0x02, 0x7d, 0xaf, 0x7b, 0x40, 0x12, 0x8d, 0xd9, 0xe4, 0xf5} }, +{ 0x8fd5, 16, {0x4d, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xe0, 0xff, 0x14, 0x24, 0xfa, 0x50, 0x04, 0x24, 0xfe} }, +{ 0x8fe5, 16, {0x70, 0x2b, 0x90, 0x01, 0x98, 0x74, 0x10, 0xf0, 0xa3, 0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} }, +{ 0x8ff5, 16, {0x83, 0xe0, 0x90, 0x01, 0x9a, 0xf0, 0x7e, 0x01, 0x7f, 0x98, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00} }, +{ 0x9005, 15, {0x75, 0x5e, 0x00, 0x75, 0x5d, 0x00, 0x7d, 0x03, 0x12, 0x8e, 0x0c, 0x8f, 0x4d, 0xaf, 0x4d} }, +{ 0x9014, 1, {0x22} }, +{ 0x9015, 8, {0x8f, 0x4e, 0x8e, 0x4d, 0x8d, 0x4c, 0x8c, 0x4b} }, +{ 0x901d, 16, {0x75, 0x55, 0x01, 0x75, 0x56, 0x9c, 0xe4, 0xf5, 0x54, 0xaf, 0x50, 0x15, 0x50, 0xef, 0x70, 0x03} }, +{ 0x902d, 16, {0x02, 0x90, 0xb3, 0xaf, 0x4f, 0xe4, 0xfc, 0xfd, 0xfe, 0xf8, 0xf9, 0xfa, 0xab, 0x07, 0xaf, 0x4e} }, +{ 0x903d, 16, {0xae, 0x4d, 0xad, 0x4c, 0xac, 0x4b, 0x12, 0x9c, 0x58, 0xaf, 0x03, 0x8f, 0x53, 0xaf, 0x4e, 0xae} }, +{ 0x904d, 16, {0x4d, 0xad, 0x4c, 0xac, 0x4b, 0xc0, 0x04, 0xc0, 0x05, 0xc0, 0x06, 0xc0, 0x07, 0xaf, 0x4f, 0xe4} }, +{ 0x905d, 16, {0xfc, 0xfd, 0xfe, 0xf8, 0xf9, 0xfa, 0xab, 0x07, 0xd0, 0x07, 0xd0, 0x06, 0xd0, 0x05, 0xd0, 0x04} }, +{ 0x906d, 16, {0x12, 0x9c, 0x58, 0x8f, 0x4e, 0x8e, 0x4d, 0x8d, 0x4c, 0x8c, 0x4b, 0xe5, 0x53, 0x24, 0x30, 0xf5} }, +{ 0x907d, 16, {0x53, 0xd3, 0x94, 0x39, 0x40, 0x06, 0x74, 0x07, 0x25, 0x53, 0xf5, 0x53, 0x05, 0x56, 0xe5, 0x56} }, +{ 0x908d, 16, {0xae, 0x55, 0x70, 0x02, 0x05, 0x55, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe4, 0xf0, 0x05, 0x56, 0xe5} }, +{ 0x909d, 16, {0x56, 0xae, 0x55, 0x70, 0x02, 0x05, 0x55, 0x14, 0xf5, 0x82, 0x8e, 0x83, 0xe5, 0x53, 0xf0, 0x05} }, +{ 0x90ad, 16, {0x54, 0x05, 0x54, 0x02, 0x90, 0x26, 0xe5, 0x56, 0x15, 0x56, 0x70, 0x02, 0x15, 0x55, 0xaf, 0x54} }, +{ 0x90bd, 16, {0x15, 0x54, 0xef, 0x60, 0x23, 0xe5, 0x56, 0x15, 0x56, 0xae, 0x55, 0x70, 0x02, 0x15, 0x55, 0xf5} }, +{ 0x90cd, 16, {0x82, 0x8e, 0x83, 0xe0, 0xff, 0x05, 0x52, 0xe5, 0x52, 0xac, 0x51, 0x70, 0x02, 0x05, 0x51, 0x14} }, +{ 0x90dd, 8, {0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x80, 0xd6} }, +{ 0x90e5, 1, {0x22} }, +{ 0x90e6, 16, {0xe4, 0x90, 0x01, 0xc9, 0xf0, 0x7e, 0x01, 0x7f, 0xca, 0x90, 0x01, 0xbe, 0xee, 0xf0, 0xa3, 0xef} }, +{ 0x90f6, 10, {0xf0, 0x90, 0x01, 0xc2, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x22} }, +{ 0x9100, 16, {0xaa, 0x07, 0xa9, 0x05, 0x90, 0x01, 0xc9, 0xe0, 0xc3, 0x94, 0x40, 0x50, 0x61, 0xac, 0x02, 0x74} }, +{ 0x9110, 16, {0x01, 0x7e, 0x00, 0xa8, 0x04, 0x08, 0x80, 0x05, 0xc3, 0x33, 0xce, 0x33, 0xce, 0xd8, 0xf9, 0xff} }, +{ 0x9120, 16, {0xe4, 0xef, 0x55, 0x30, 0x60, 0x45, 0xea, 0x04, 0xff, 0x90, 0x01, 0xc2, 0xe0, 0xfc, 0xa3, 0xe0} }, +{ 0x9130, 16, {0xfd, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0xa3, 0xe9, 0xf0, 0x8d, 0x82, 0x8c, 0x83, 0xa3, 0xa3} }, +{ 0x9140, 16, {0xeb, 0xf0, 0x90, 0x01, 0xc2, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x9b, 0xb8, 0xfc, 0xd3, 0xe5, 0xf0} }, +{ 0x9150, 16, {0x94, 0x87, 0xec, 0x94, 0x02, 0x40, 0x0a, 0x90, 0x01, 0xc2, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0xca} }, +{ 0x9160, 16, {0xf0, 0xc2, 0xaf, 0x90, 0x01, 0xc9, 0xe0, 0x04, 0xf0, 0xd2, 0xaf, 0x7f, 0x01, 0x22, 0x7f, 0x00} }, +{ 0x9170, 1, {0x22} }, +{ 0x9171, 16, {0x90, 0x01, 0xc9, 0xe0, 0xd3, 0x94, 0x00, 0x40, 0x55, 0x90, 0x01, 0xbe, 0xe0, 0xfc, 0xa3, 0xe0} }, +{ 0x9181, 16, {0xaa, 0x04, 0xf9, 0x7b, 0x01, 0xc0, 0x03, 0xc0, 0x02, 0xc0, 0x01, 0xaa, 0x06, 0xa9, 0x07, 0xa8} }, +{ 0x9191, 16, {0x01, 0xac, 0x02, 0xad, 0x03, 0xd0, 0x01, 0xd0, 0x02, 0xd0, 0x03, 0x7e, 0x00, 0x7f, 0x03, 0x12} }, +{ 0x91a1, 16, {0x9b, 0x49, 0x90, 0x01, 0xbe, 0xe4, 0x75, 0xf0, 0x03, 0x12, 0x9b, 0xb8, 0xfc, 0xd3, 0xe5, 0xf0} }, +{ 0x91b1, 16, {0x94, 0x87, 0xec, 0x94, 0x02, 0x40, 0x0a, 0x90, 0x01, 0xbe, 0x74, 0x01, 0xf0, 0xa3, 0x74, 0xca} }, +{ 0x91c1, 16, {0xf0, 0xc2, 0xaf, 0x90, 0x01, 0xc9, 0xe0, 0x14, 0xf0, 0xd2, 0xaf, 0x7f, 0x01, 0x22, 0x7f, 0x00} }, +{ 0x91d1, 1, {0x22} }, +{ 0x91d2, 16, {0x90, 0x7f, 0xc2, 0xe0, 0x20, 0xe1, 0x5e, 0x7e, 0x7b, 0x7f, 0x80, 0x75, 0x50, 0x7b, 0x75, 0x51} }, +{ 0x91e2, 16, {0x80, 0xe5, 0x51, 0x24, 0x01, 0xff, 0xe4, 0x35, 0x50, 0xa9, 0x07, 0x7b, 0x01, 0x8b, 0x52, 0xf5} }, +{ 0x91f2, 16, {0x53, 0x89, 0x54, 0xfe, 0x12, 0x91, 0x71, 0xef, 0x60, 0x3b, 0xab, 0x52, 0xaa, 0x53, 0xa9, 0x54} }, +{ 0x9202, 16, {0x12, 0x9b, 0x72, 0x14, 0xff, 0x90, 0x00, 0x01, 0x12, 0x9b, 0x8b, 0xb4, 0x02, 0x16, 0xc2, 0xaf} }, +{ 0x9212, 16, {0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x01, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x44} }, +{ 0x9222, 16, {0x04, 0xf0, 0xd2, 0xaf, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0x74, 0x0d, 0xf0, 0x90, 0x7f, 0xc3} }, +{ 0x9232, 5, {0x74, 0x04, 0xf0, 0xd2, 0xaf} }, +{ 0x9237, 1, {0x22} }, +{ 0x9238, 16, {0x12, 0x91, 0xd2, 0xe4, 0xf5, 0x4b, 0x74, 0x36, 0x25, 0x4b, 0xf8, 0xe6, 0x54, 0xf0, 0xf5, 0x4c} }, +{ 0x9248, 16, {0x74, 0xc5, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5, 0x83, 0xe0, 0x65, 0x4c, 0xff, 0xc4} }, +{ 0x9258, 16, {0x54, 0x0f, 0xf5, 0x4d, 0x60, 0x22, 0x74, 0xc5, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x01, 0xf5} }, +{ 0x9268, 16, {0x83, 0xe5, 0x4c, 0xf0, 0xaf, 0x4b, 0x7d, 0x01, 0xe5, 0x4c, 0x45, 0x4d, 0xfb, 0x12, 0x91, 0x00} }, +{ 0x9278, 16, {0xef, 0x70, 0x05, 0x12, 0x91, 0xd2, 0x80, 0xec, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40} }, +{ 0x9288, 16, {0xb5, 0x12, 0x91, 0xd2, 0xe5, 0x3a, 0x60, 0x48, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8} }, +{ 0x9298, 16, {0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x55, 0x3a, 0x60, 0x29, 0xe5, 0x4b} }, +{ 0x92a8, 16, {0x75, 0xf0, 0x08, 0xa4, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xf5, 0x83, 0xe0, 0x30, 0xe6} }, +{ 0x92b8, 16, {0x16, 0xaf, 0x4b, 0x7d, 0x04, 0x7b, 0x80, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05, 0x12, 0x91, 0xd2} }, +{ 0x92c8, 16, {0x80, 0xef, 0xe5, 0x4c, 0xf4, 0x52, 0x3a, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0xbb} }, +{ 0x92d8, 16, {0x90, 0x03, 0x00, 0xe0, 0x60, 0x03, 0x02, 0x93, 0xa5, 0x74, 0x19, 0xf0, 0x7f, 0x02, 0x12, 0x81} }, +{ 0x92e8, 16, {0x11, 0x8e, 0x4e, 0x8f, 0x4f, 0xc3, 0xe5, 0x4e, 0x64, 0x80, 0x94, 0x80, 0x40, 0xee, 0x90, 0x01} }, +{ 0x92f8, 16, {0xbc, 0xe0, 0x65, 0x4f, 0xf0, 0x60, 0x37, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8, 0x07} }, +{ 0x9308, 16, {0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x90, 0x01, 0xbc, 0xe0, 0x55, 0x4c, 0x60} }, +{ 0x9318, 16, {0x14, 0xaf, 0x4b, 0x7d, 0x08, 0xe5, 0x4c, 0x55, 0x4f, 0xfb, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05} }, +{ 0x9328, 16, {0x12, 0x91, 0xd2, 0x80, 0xec, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0xcc, 0x90, 0x01} }, +{ 0x9338, 16, {0xbc, 0xe5, 0x4f, 0xf0, 0xe4, 0xf5, 0x4b, 0xc2, 0xaf, 0x74, 0x32, 0x25, 0x4b, 0xf8, 0xe6, 0xf5} }, +{ 0x9348, 16, {0x4c, 0xe4, 0xf6, 0xd2, 0xaf, 0x53, 0x4c, 0x1e, 0xe5, 0x4c, 0x60, 0x11, 0xaf, 0x4b, 0x7d, 0x02} }, +{ 0x9358, 16, {0xab, 0x4c, 0x12, 0x91, 0x00, 0xef, 0x70, 0x05, 0x12, 0x91, 0xd2, 0x80, 0xef, 0x74, 0x2c, 0x25} }, +{ 0x9368, 16, {0x4b, 0xf8, 0xe6, 0xf5, 0x4c, 0x74, 0xfc, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83} }, +{ 0x9378, 16, {0xe0, 0x65, 0x4c, 0x60, 0x11, 0xaf, 0x4b, 0x7d, 0x04, 0xab, 0x4c, 0x12, 0x91, 0x00, 0xef, 0x70} }, +{ 0x9388, 16, {0x05, 0x12, 0x91, 0xd2, 0x80, 0xef, 0x74, 0xfc, 0x25, 0x4b, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5} }, +{ 0x9398, 16, {0x83, 0xe5, 0x4c, 0xf0, 0x05, 0x4b, 0xe5, 0x4b, 0xc3, 0x94, 0x04, 0x40, 0x9a, 0x12, 0x91, 0xd2} }, +{ 0x93a8, 1, {0x22} }, +{ 0x93a9, 12, {0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x64, 0x02, 0x93, 0xf0} }, +{ 0x93b5, 16, {0x02, 0x05, 0xa3, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} }, +{ 0x93c5, 16, {0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} }, +{ 0x93d5, 16, {0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} }, +{ 0x93e5, 16, {0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x94, 0x35, 0xe4, 0x7e} }, +{ 0x93f5, 16, {0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} }, +{ 0x9405, 16, {0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} }, +{ 0x9415, 16, {0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} }, +{ 0x9425, 16, {0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} }, +{ 0x9435, 16, {0x60, 0x24, 0x02, 0x8a, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x81, 0x82, 0x84, 0x88} }, +{ 0x9445, 16, {0x90, 0xa0, 0xc0, 0xc1, 0xc2, 0xc4, 0xc8, 0xd0, 0xe0, 0xe1, 0xe2, 0xe4, 0xe8, 0xf0, 0xf1, 0xf2} }, +{ 0x9455, 8, {0xf4, 0xf8, 0xf9, 0xfa, 0xfc, 0xfd, 0xfe, 0xff} }, +{ 0x945d, 1, {0x00} }, +{ 0x945e, 11, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0xd0, 0x75, 0xd0, 0x18} }, +{ 0x9469, 16, {0x90, 0x20, 0x60, 0xe0, 0x54, 0x0f, 0xfe, 0x30, 0xe0, 0x05, 0x90, 0x20, 0x02, 0xe0, 0xff, 0xee} }, +{ 0x9479, 16, {0x30, 0xe1, 0x05, 0x90, 0x20, 0x0a, 0xe0, 0xff, 0xee, 0x30, 0xe2, 0x05, 0x90, 0x20, 0x12, 0xe0} }, +{ 0x9489, 16, {0xff, 0xee, 0x30, 0xe3, 0x05, 0x90, 0x20, 0x1a, 0xe0, 0xff, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x1e} }, +{ 0x9499, 10, {0x04, 0xe4, 0xf0, 0x80, 0x05, 0x90, 0x01, 0xc4, 0xee, 0xf0} }, +{ 0x94a3, 9, {0xd0, 0xd0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x94ac, 2, {0xa9, 0x03} }, +{ 0x94ae, 16, {0xef, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xab, 0x82, 0xfa, 0xe5} }, +{ 0x94be, 16, {0x57, 0x45, 0x58, 0xf5, 0x59, 0xe9, 0x60, 0x14, 0x8a, 0x83, 0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82} }, +{ 0x94ce, 16, {0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x4d, 0xf0, 0xe4, 0xfe, 0x80, 0x13, 0xeb, 0x24, 0x04, 0xf5} }, +{ 0x94de, 16, {0x82, 0xe4, 0x3a, 0xf5, 0x83, 0xe0, 0xff, 0xed, 0xf4, 0xfc, 0xef, 0x5c, 0xf0, 0xae, 0x59, 0xeb} }, +{ 0x94ee, 16, {0x24, 0x06, 0xf5, 0x82, 0xe4, 0x3a, 0xf5, 0x83, 0xe0, 0x55, 0x59, 0xfc, 0xb5, 0x06, 0x03, 0xaf} }, +{ 0x94fe, 16, {0x05, 0x22, 0xe5, 0x57, 0x5c, 0xfe, 0xe5, 0x58, 0x5c, 0xfd, 0xe9, 0x60, 0x16, 0xee, 0x70, 0x04} }, +{ 0x950e, 16, {0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0xae, 0x07, 0xed, 0x70, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f} }, +{ 0x951e, 16, {0x00, 0xad, 0x07, 0xee, 0x60, 0x03, 0xaf, 0x57, 0x22, 0xed, 0x60, 0x03, 0xaf, 0x58, 0x22, 0x7f} }, +{ 0x952e, 1, {0x00} }, +{ 0x952f, 1, {0x22} }, +{ 0x9530, 16, {0x75, 0x50, 0x02, 0x75, 0x51, 0xb0, 0x90, 0x03, 0x35, 0x74, 0x0f, 0xf0, 0x85, 0x51, 0x82, 0x85} }, +{ 0x9540, 16, {0x50, 0x83, 0xe0, 0xff, 0x90, 0x03, 0x36, 0xf0, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xa3, 0xe0} }, +{ 0x9550, 16, {0x90, 0x03, 0x37, 0xf0, 0xa3, 0x74, 0xff, 0xf0, 0x75, 0x52, 0x03, 0x75, 0x53, 0x39, 0xef, 0x14} }, +{ 0x9560, 16, {0xb4, 0x0b, 0x00, 0x40, 0x03, 0x02, 0x99, 0x98, 0x90, 0x95, 0x6f, 0xf8, 0x28, 0x28, 0x73, 0x02} }, +{ 0x9570, 16, {0x95, 0x90, 0x02, 0x96, 0x2f, 0x02, 0x97, 0x34, 0x02, 0x97, 0x54, 0x02, 0x97, 0x54, 0x02, 0x97} }, +{ 0x9580, 16, {0xef, 0x02, 0x98, 0x2a, 0x02, 0x98, 0x4f, 0x02, 0x99, 0x0d, 0x02, 0x99, 0x39, 0x02, 0x99, 0x65} }, +{ 0x9590, 16, {0xe4, 0xf5, 0x4b, 0xe5, 0x4b, 0x75, 0xf0, 0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20} }, +{ 0x95a0, 16, {0xaf, 0x82, 0xf5, 0x4e, 0x8f, 0x4f, 0xe4, 0xff, 0xe4, 0xfe, 0xef, 0x60, 0x10, 0x74, 0x8a, 0x2e} }, +{ 0x95b0, 16, {0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0xf4, 0xf5, 0x4c, 0x80, 0x0d, 0x74, 0x8a, 0x2e} }, +{ 0x95c0, 16, {0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0xf5, 0x4c, 0xe5, 0x4f, 0x24, 0x07, 0xf5, 0x82} }, +{ 0x95d0, 16, {0xe4, 0x35, 0x4e, 0xf5, 0x83, 0xe5, 0x4c, 0xf0, 0xe0, 0xf5, 0x4d, 0x65, 0x4c, 0x60, 0x38, 0xe4} }, +{ 0x95e0, 16, {0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xfd, 0x05, 0x53, 0xe5, 0x53, 0xaa, 0x52, 0x70, 0x02} }, +{ 0x95f0, 16, {0x05, 0x52, 0x14, 0xf5, 0x82, 0x8a, 0x83, 0xed, 0xf0, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52, 0x70} }, +{ 0x9600, 16, {0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xe5, 0x4c, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} }, +{ 0x9610, 16, {0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0x0e, 0xbe, 0x24, 0x8f, 0x0f, 0xef, 0x64, 0x02, 0x70} }, +{ 0x9620, 16, {0x87, 0x05, 0x4b, 0xe5, 0x4b, 0x64, 0x04, 0x60, 0x03, 0x02, 0x95, 0x93, 0x02, 0x99, 0x9e, 0xe4} }, +{ 0x9630, 16, {0xf5, 0x4b, 0xaf, 0x4b, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0x05, 0x4b, 0xe5, 0x4b, 0xd3, 0x94, 0x03} }, +{ 0x9640, 16, {0x40, 0xf0, 0x90, 0x00, 0x04, 0x74, 0x94, 0xf0, 0xa3, 0x74, 0x5e, 0xf0, 0xe4, 0xf5, 0x4d, 0x7e} }, +{ 0x9650, 16, {0x20, 0x7f, 0x00, 0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0xf5, 0x4b, 0xaf, 0x4b, 0x74, 0x01, 0xa8} }, +{ 0x9660, 16, {0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0xf5, 0x4c, 0x90, 0x01, 0xc4, 0xf0, 0x90, 0x01} }, +{ 0x9670, 16, {0xc0, 0xe4, 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0x74, 0x02} }, +{ 0x9680, 16, {0xf0, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x4c, 0x34, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0} }, +{ 0x9690, 16, {0x70, 0xef, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xff, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52} }, +{ 0x96a0, 16, {0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} }, +{ 0x96b0, 16, {0x83, 0x74, 0xff, 0xf0, 0xe4, 0x90, 0x01, 0xc4, 0xf0, 0x75, 0x4d, 0xff, 0x90, 0x01, 0xc4, 0xe0} }, +{ 0x96c0, 16, {0xff, 0x60, 0x37, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04, 0xfe, 0x05, 0x53, 0xe5, 0x53} }, +{ 0x96d0, 16, {0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xee, 0xf0, 0x05, 0x53, 0xe5} }, +{ 0x96e0, 16, {0x53, 0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83, 0xef, 0xf0, 0x85, 0x53} }, +{ 0x96f0, 16, {0x82, 0x85, 0x52, 0x83, 0xe5, 0x4c, 0xf0, 0x75, 0x4d, 0xff, 0xe5, 0x4d, 0x70, 0x16, 0x74, 0x08} }, +{ 0x9700, 16, {0x25, 0x4f, 0xf5, 0x4f, 0xe4, 0x35, 0x4e, 0xf5, 0x4e, 0x05, 0x4b, 0xe5, 0x4b, 0x64, 0x04, 0x60} }, +{ 0x9710, 16, {0x03, 0x02, 0x96, 0x5b, 0xe4, 0xf5, 0x4b, 0xaf, 0x4b, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x05, 0x4b} }, +{ 0x9720, 16, {0xe5, 0x4b, 0xd3, 0x94, 0x03, 0x40, 0xf0, 0x90, 0x00, 0x04, 0x74, 0x13, 0xf0, 0xa3, 0x74, 0x12} }, +{ 0x9730, 16, {0xf0, 0x02, 0x99, 0x9e, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xa3, 0xe0, 0x14, 0xff, 0x74, 0x01} }, +{ 0x9740, 16, {0xa8, 0x07, 0x08, 0x80, 0x02, 0xc3, 0x33, 0xd8, 0xfc, 0x90, 0x02, 0xf7, 0xf0, 0x90, 0x01, 0xc4} }, +{ 0x9750, 16, {0xf0, 0x02, 0x99, 0x9e, 0x90, 0x01, 0xc0, 0x74, 0x03, 0xf0, 0xa3, 0x74, 0xe8, 0xf0, 0xe4, 0xf5} }, +{ 0x9760, 16, {0x4d, 0x90, 0x02, 0xf7, 0xe0, 0xff, 0x90, 0x01, 0xc4, 0xe0, 0xb5, 0x07, 0x19, 0x90, 0x01, 0xc0} }, +{ 0x9770, 16, {0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70, 0xea, 0x90, 0x03, 0x38, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52} }, +{ 0x9780, 16, {0x83, 0x74, 0xff, 0xf0, 0xf5, 0x4d, 0xe5, 0x4d, 0x60, 0x03, 0x02, 0x99, 0x9e, 0x90, 0x01, 0xc0} }, +{ 0x9790, 16, {0xf0, 0xa3, 0x74, 0x96, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70, 0xf6, 0x7f} }, +{ 0x97a0, 16, {0x02, 0x12, 0x81, 0x11, 0xc3, 0xee, 0x64, 0x80, 0x94, 0x80, 0x40, 0xf3, 0xef, 0x54, 0x0f, 0xf5} }, +{ 0x97b0, 16, {0x4d, 0x90, 0x02, 0xf7, 0xe0, 0x55, 0x4d, 0x70, 0x04, 0x7f, 0x01, 0x80, 0x02, 0x7f, 0x00, 0x8f} }, +{ 0x97c0, 16, {0x4c, 0x85, 0x51, 0x82, 0x85, 0x50, 0x83, 0xe0, 0xb4, 0x05, 0x0c, 0xe5, 0x4c, 0x70, 0x04, 0x7f} }, +{ 0x97d0, 16, {0x01, 0x80, 0x02, 0x7f, 0x00, 0x8f, 0x4c, 0xe5, 0x4c, 0x70, 0x03, 0x02, 0x99, 0x9e, 0xe4, 0x90} }, +{ 0x97e0, 16, {0x03, 0x38, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52, 0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0xe4} }, +{ 0x97f0, 16, {0xff, 0xfd, 0x12, 0x82, 0x50, 0x7e, 0x20, 0x7f, 0x00, 0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0x85} }, +{ 0x9800, 16, {0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0xa3, 0xa3, 0xe0, 0x44, 0x80, 0xf0, 0x85, 0x4f, 0x82, 0x85} }, +{ 0x9810, 16, {0x4e, 0x83, 0x74, 0x01, 0xf0, 0xa3, 0xe4, 0xf0, 0x85, 0x4f, 0x82, 0x85, 0x4e, 0x83, 0xa3, 0xa3} }, +{ 0x9820, 16, {0xa3, 0xe0, 0x54, 0x7f, 0xf0, 0xd2, 0x04, 0x02, 0x99, 0x9e, 0xc2, 0x04, 0x7e, 0x20, 0x7f, 0x00} }, +{ 0x9830, 16, {0x75, 0x4e, 0x20, 0x75, 0x4f, 0x00, 0xe5, 0x4f, 0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x4e, 0xf5} }, +{ 0x9840, 16, {0x83, 0xe0, 0x30, 0xe6, 0xf1, 0xe4, 0xff, 0x7d, 0x01, 0x12, 0x82, 0x50, 0x02, 0x99, 0x9e, 0xe4} }, +{ 0x9850, 16, {0xf5, 0x4d, 0xf5, 0x4b, 0xaf, 0x4b, 0xe4, 0xfd, 0x12, 0x82, 0x50, 0xe5, 0x4b, 0x75, 0xf0, 0x08} }, +{ 0x9860, 16, {0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xaf, 0x82, 0xf5, 0x4e, 0x8f, 0x4f, 0xf5, 0x83} }, +{ 0x9870, 16, {0xe5, 0x82, 0x24, 0x04, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0xfc, 0xf0, 0xaf} }, +{ 0x9880, 16, {0x4b, 0x7d, 0x01, 0x7b, 0x01, 0x75, 0x57, 0x80, 0x75, 0x58, 0x40, 0x12, 0x94, 0xac, 0x8f, 0x4d} }, +{ 0x9890, 16, {0xe5, 0x4d, 0x70, 0x11, 0xaf, 0x4b, 0x7d, 0x02, 0x7b, 0x01, 0x75, 0x57, 0x10, 0x75, 0x58, 0x20} }, +{ 0x98a0, 16, {0x12, 0x94, 0xac, 0x8f, 0x4d, 0xe5, 0x4d, 0x70, 0x10, 0xaf, 0x4b, 0x7d, 0x01, 0xfb, 0x75, 0x57} }, +{ 0x98b0, 16, {0x80, 0x75, 0x58, 0x40, 0x12, 0x94, 0xac, 0x8f, 0x4d, 0xe5, 0x4d, 0x70, 0x10, 0xaf, 0x4b, 0x7d} }, +{ 0x98c0, 16, {0x02, 0xfb, 0x75, 0x57, 0x10, 0x75, 0x58, 0x20, 0x12, 0x94, 0xac, 0x8f, 0x4d, 0xaf, 0x4b, 0x7d} }, +{ 0x98d0, 16, {0x01, 0x12, 0x82, 0x50, 0xe5, 0x4d, 0x60, 0x26, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0xe5, 0x4b, 0x04} }, +{ 0x98e0, 16, {0xff, 0x05, 0x53, 0xe5, 0x53, 0xac, 0x52, 0x70, 0x02, 0x05, 0x52, 0x14, 0xf5, 0x82, 0x8c, 0x83} }, +{ 0x98f0, 16, {0xef, 0xf0, 0x85, 0x53, 0x82, 0x85, 0x52, 0x83, 0xe5, 0x4d, 0xf0, 0x02, 0x99, 0x9e, 0x05, 0x4b} }, +{ 0x9900, 16, {0xe5, 0x4b, 0xd3, 0x94, 0x03, 0x50, 0x03, 0x02, 0x98, 0x54, 0x02, 0x99, 0x9e, 0xe4, 0x90, 0x03} }, +{ 0x9910, 16, {0x59, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0x74} }, +{ 0x9920, 16, {0x1b, 0xf0, 0xa3, 0x74, 0x29, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x80, 0x00, 0xef, 0x64, 0x08} }, +{ 0x9930, 16, {0x60, 0x6c, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0x80, 0x65, 0xe4, 0x90, 0x03, 0x59, 0xf0, 0xa3, 0xf0} }, +{ 0x9940, 16, {0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xa3, 0xe5, 0x52, 0xf0, 0xa3, 0xe5} }, +{ 0x9950, 16, {0x53, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x19, 0xc1, 0xef, 0x64, 0x08, 0x60, 0x40, 0xe4, 0x90} }, +{ 0x9960, 16, {0x03, 0x38, 0xf0, 0x80, 0x39, 0xe4, 0x90, 0x03, 0x59, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0} }, +{ 0x9970, 16, {0xa3, 0xf0, 0xa3, 0x74, 0x10, 0xf0, 0xe5, 0x51, 0x24, 0x02, 0x90, 0x03, 0x60, 0xf0, 0xe4, 0x35} }, +{ 0x9980, 16, {0x50, 0x90, 0x03, 0x5f, 0xf0, 0x7e, 0x03, 0x7f, 0x59, 0x12, 0x80, 0x00, 0xef, 0x64, 0x08, 0x60} }, +{ 0x9990, 16, {0x0d, 0xe4, 0x90, 0x03, 0x38, 0xf0, 0x80, 0x06, 0x90, 0x03, 0x38, 0x74, 0x01, 0xf0, 0x90, 0x01} }, +{ 0x99a0, 16, {0xc0, 0xe4, 0xf0, 0xa3, 0x74, 0x0a, 0xf0, 0x90, 0x01, 0xc0, 0xe0, 0x70, 0x02, 0xa3, 0xe0, 0x70} }, +{ 0x99b0, 16, {0xf6, 0x7e, 0x03, 0x7f, 0x35, 0x75, 0x60, 0x00, 0x75, 0x5f, 0x00, 0x75, 0x5e, 0x00, 0x75, 0x5d} }, +{ 0x99c0, 11, {0x00, 0x7d, 0x24, 0x12, 0x8e, 0x0c, 0xe4, 0x90, 0x02, 0xaf, 0xf0} }, +{ 0x99cb, 1, {0x22} }, +{ 0x99cc, 16, {0xe4, 0xff, 0x74, 0xf8, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0x70, 0x03, 0x02} }, +{ 0x99dc, 16, {0x9a, 0x6f, 0x74, 0x36, 0x2f, 0xf8, 0xe6, 0x20, 0xe5, 0x03, 0x02, 0x9a, 0x6f, 0xef, 0x75, 0xf0} }, +{ 0x99ec, 16, {0x08, 0xa4, 0x24, 0x00, 0xf5, 0x82, 0xe4, 0x34, 0x20, 0xad, 0x82, 0xfc, 0xf5, 0x83, 0xe5, 0x82} }, +{ 0x99fc, 16, {0x24, 0x05, 0xf5, 0x82, 0xe4, 0x35, 0x83, 0xf5, 0x83, 0xe0, 0x54, 0x60, 0x64, 0x60, 0x70, 0x63} }, +{ 0x9a0c, 16, {0xef, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe4, 0x75, 0xf0, 0x01} }, +{ 0x9a1c, 16, {0x12, 0x9b, 0xce, 0x85, 0xf0, 0x82, 0xf5, 0x83, 0xe0, 0x8d, 0x82, 0x8c, 0x83, 0xf0, 0x74, 0xf8} }, +{ 0x9a2c, 16, {0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xe0, 0x14, 0xf0, 0x70, 0x36, 0xef, 0x25, 0xe0} }, +{ 0x9a3c, 16, {0x24, 0xc7, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe4, 0xf0, 0xef, 0x25, 0xe0, 0xfe, 0xc3} }, +{ 0x9a4c, 16, {0x74, 0x0c, 0x9e, 0x75, 0xf0, 0x40, 0xa4, 0x24, 0x40, 0xf5, 0x82, 0xe5, 0xf0, 0x34, 0x7b, 0xad} }, +{ 0x9a5c, 16, {0x82, 0xfc, 0xef, 0x25, 0xe0, 0x24, 0xef, 0xf5, 0x82, 0xe4, 0x34, 0x02, 0xf5, 0x83, 0xec, 0xf0} }, +{ 0x9a6c, 12, {0xa3, 0xed, 0xf0, 0x0f, 0xef, 0x64, 0x04, 0x60, 0x03, 0x02, 0x99, 0xce} }, +{ 0x9a78, 1, {0x22} }, +{ 0x9a79, 16, {0xe7, 0x09, 0xf6, 0x08, 0xdf, 0xfa, 0x80, 0x46, 0xe7, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x3e} }, +{ 0x9a89, 16, {0x88, 0x82, 0x8c, 0x83, 0xe7, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x32, 0xe3, 0x09, 0xf6, 0x08} }, +{ 0x9a99, 16, {0xdf, 0xfa, 0x80, 0x78, 0xe3, 0x09, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x70, 0x88, 0x82, 0x8c, 0x83} }, +{ 0x9aa9, 16, {0xe3, 0x09, 0xf0, 0xa3, 0xdf, 0xfa, 0x80, 0x64, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf6, 0x08} }, +{ 0x9ab9, 16, {0xdf, 0xfa, 0x80, 0x58, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0xa3, 0xf2, 0x08, 0xdf, 0xfa, 0x80, 0x4c} }, +{ 0x9ac9, 16, {0x80, 0xd2, 0x80, 0xfa, 0x80, 0xc6, 0x80, 0xd4, 0x80, 0x69, 0x80, 0xf2, 0x80, 0x33, 0x80, 0x10} }, +{ 0x9ad9, 16, {0x80, 0xa6, 0x80, 0xea, 0x80, 0x9a, 0x80, 0xa8, 0x80, 0xda, 0x80, 0xe2, 0x80, 0xca, 0x80, 0x33} }, +{ 0x9ae9, 16, {0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83} }, +{ 0x9af9, 16, {0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xe9, 0xde, 0xe7, 0x80} }, +{ 0x9b09, 16, {0x0d, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf6, 0x08, 0xdf, 0xf9, 0xec, 0xfa, 0xa9, 0xf0} }, +{ 0x9b19, 16, {0xed, 0xfb, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xec, 0xfa, 0xe0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc} }, +{ 0x9b29, 16, {0xc5, 0x83, 0xcc, 0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xcc, 0xc5, 0x83, 0xcc, 0xdf, 0xea, 0xde} }, +{ 0x9b39, 16, {0xe8, 0x80, 0xdb, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0xa3, 0xf2, 0x08, 0xdf, 0xf9, 0x80, 0xcc} }, +{ 0x9b49, 16, {0x88, 0xf0, 0xed, 0x24, 0x02, 0xb4, 0x04, 0x00, 0x50, 0xc2, 0xf5, 0x82, 0xeb, 0x24, 0x02, 0xb4} }, +{ 0x9b59, 16, {0x04, 0x00, 0x50, 0xb8, 0x23, 0x23, 0x45, 0x82, 0xf5, 0x82, 0xef, 0x4e, 0x60, 0xae, 0xef, 0x60} }, +{ 0x9b69, 9, {0x01, 0x0e, 0xe5, 0x82, 0x23, 0x90, 0x9a, 0xc9, 0x73} }, +{ 0x9b72, 16, {0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} }, +{ 0x9b82, 9, {0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x9b8b, 16, {0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} }, +{ 0x9b9b, 16, {0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} }, +{ 0x9bab, 13, {0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x9bb8, 16, {0xc5, 0xf0, 0xf8, 0xa3, 0xe0, 0x28, 0xf0, 0xc5, 0xf0, 0xf8, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02} }, +{ 0x9bc8, 6, {0x15, 0x83, 0xe0, 0x38, 0xf0, 0x22} }, +{ 0x9bce, 16, {0xa3, 0xf8, 0xe0, 0xc5, 0xf0, 0x25, 0xf0, 0xf0, 0xe5, 0x82, 0x15, 0x82, 0x70, 0x02, 0x15, 0x83} }, +{ 0x9bde, 6, {0xe0, 0xc8, 0x38, 0xf0, 0xe8, 0x22} }, +{ 0x9be4, 16, {0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} }, +{ 0x9bf4, 16, {0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} }, +{ 0x9c04, 16, {0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} }, +{ 0x9c14, 8, {0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} }, +{ 0x9c1c, 16, {0x75, 0xf0, 0x08, 0x75, 0x82, 0x00, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xcd, 0x33, 0xcd, 0xcc} }, +{ 0x9c2c, 16, {0x33, 0xcc, 0xc5, 0x82, 0x33, 0xc5, 0x82, 0x9b, 0xed, 0x9a, 0xec, 0x99, 0xe5, 0x82, 0x98, 0x40} }, +{ 0x9c3c, 16, {0x0c, 0xf5, 0x82, 0xee, 0x9b, 0xfe, 0xed, 0x9a, 0xfd, 0xec, 0x99, 0xfc, 0x0f, 0xd5, 0xf0, 0xd6} }, +{ 0x9c4c, 16, {0xe4, 0xce, 0xfb, 0xe4, 0xcd, 0xfa, 0xe4, 0xcc, 0xf9, 0xa8, 0x82, 0x22, 0xb8, 0x00, 0xc1, 0xb9} }, +{ 0x9c5c, 16, {0x00, 0x59, 0xba, 0x00, 0x2d, 0xec, 0x8b, 0xf0, 0x84, 0xcf, 0xce, 0xcd, 0xfc, 0xe5, 0xf0, 0xcb} }, +{ 0x9c6c, 16, {0xf9, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc, 0xeb} }, +{ 0x9c7c, 16, {0x33, 0xfb, 0x10, 0xd7, 0x03, 0x99, 0x40, 0x04, 0xeb, 0x99, 0xfb, 0x0f, 0xd8, 0xe5, 0xe4, 0xf9} }, +{ 0x9c8c, 16, {0xfa, 0x22, 0x78, 0x18, 0xef, 0x2f, 0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xec, 0x33, 0xfc} }, +{ 0x9c9c, 16, {0xc9, 0x33, 0xc9, 0x10, 0xd7, 0x05, 0x9b, 0xe9, 0x9a, 0x40, 0x07, 0xec, 0x9b, 0xfc, 0xe9, 0x9a} }, +{ 0x9cac, 16, {0xf9, 0x0f, 0xd8, 0xe0, 0xe4, 0xc9, 0xfa, 0xe4, 0xcc, 0xfb, 0x22, 0x75, 0xf0, 0x10, 0xef, 0x2f} }, +{ 0x9cbc, 16, {0xff, 0xee, 0x33, 0xfe, 0xed, 0x33, 0xfd, 0xcc, 0x33, 0xcc, 0xc8, 0x33, 0xc8, 0x10, 0xd7, 0x07} }, +{ 0x9ccc, 16, {0x9b, 0xec, 0x9a, 0xe8, 0x99, 0x40, 0x0a, 0xed, 0x9b, 0xfd, 0xec, 0x9a, 0xfc, 0xe8, 0x99, 0xf8} }, +{ 0x9cdc, 14, {0x0f, 0xd5, 0xf0, 0xda, 0xe4, 0xcd, 0xfb, 0xe4, 0xcc, 0xfa, 0xe4, 0xc8, 0xf9, 0x22} }, +{ 0x9cea, 16, {0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} }, +{ 0x9cfa, 1, {0x22} }, +{ 0x9cfb, 16, {0xe8, 0x60, 0x0f, 0xec, 0xc3, 0x13, 0xfc, 0xed, 0x13, 0xfd, 0xee, 0x13, 0xfe, 0xef, 0x13, 0xff} }, +{ 0x9d0b, 3, {0xd8, 0xf1, 0x22} }, +{ 0x9d0e, 16, {0x08, 0x08, 0x08, 0xe6, 0xcf, 0x2f, 0xf6, 0x18, 0xe6, 0xce, 0x3e, 0xf6, 0x18, 0xe6, 0xcd, 0x3d} }, +{ 0x9d1e, 7, {0xf6, 0x18, 0xe6, 0xcc, 0x3c, 0xf6, 0x22} }, +{ 0x9d25, 12, {0xec, 0xf0, 0xa3, 0xed, 0xf0, 0xa3, 0xee, 0xf0, 0xa3, 0xef, 0xf0, 0x22} }, +{ 0x9d31, 16, {0xa8, 0x82, 0x85, 0x83, 0xf0, 0xd0, 0x83, 0xd0, 0x82, 0x12, 0x9d, 0x48, 0x12, 0x9d, 0x48, 0x12} }, +{ 0x9d41, 16, {0x9d, 0x48, 0x12, 0x9d, 0x48, 0xe4, 0x73, 0xe4, 0x93, 0xa3, 0xc5, 0x83, 0xc5, 0xf0, 0xc5, 0x83} }, +{ 0x9d51, 16, {0xc8, 0xc5, 0x82, 0xc8, 0xf0, 0xa3, 0xc5, 0x83, 0xc5, 0xf0, 0xc5, 0x83, 0xc8, 0xc5, 0x82, 0xc8} }, +{ 0x9d61, 1, {0x22} }, +{ 0xffff, 0, {0x00} } +}; + +#ifdef DEBUG +static const struct whiteheat_hex_record whiteheat_loader[] = { +{ 0x0000, 3, {0x02, 0x09, 0x8d} }, +{ 0x0033, 3, {0x02, 0x0e, 0x70} }, +{ 0x0043, 3, {0x02, 0x0b, 0x00} }, +{ 0x004b, 3, {0x02, 0x05, 0xb3} }, +{ 0x0100, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x54, 0x10, 0xff, 0xc4, 0x54, 0x0f, 0x44, 0x50, 0xf5, 0x0f, 0x13, 0xe4} }, +{ 0x0110, 16, {0x33, 0xf5, 0x11, 0x90, 0x7f, 0xe9, 0xe0, 0x24, 0x5e, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x03} }, +{ 0x0120, 16, {0x7c, 0x90, 0x01, 0x28, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x01, 0xbc, 0x02, 0x01, 0xbc, 0x02, 0x01} }, +{ 0x0130, 16, {0x91, 0x02, 0x01, 0x3d, 0x02, 0x01, 0x53, 0x02, 0x01, 0x6f, 0x02, 0x01, 0x9a, 0x90, 0x7f, 0x00} }, +{ 0x0140, 16, {0xe5, 0x11, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} }, +{ 0x0150, 16, {0x02, 0x03, 0x7c, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x90, 0x7f, 0x00, 0xf0, 0x90} }, +{ 0x0160, 16, {0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03, 0x7c, 0x12} }, +{ 0x0170, 16, {0x0a, 0x89, 0x50, 0x07, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0x80, 0x06, 0x90, 0x7f, 0x00, 0x74, 0x0f} }, +{ 0x0180, 16, {0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03} }, +{ 0x0190, 16, {0x7c, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0f, 0x02, 0x03, 0x7c, 0x90, 0x7f, 0x00, 0x74, 0x07, 0xf0} }, +{ 0x01a0, 16, {0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xe8, 0x7e} }, +{ 0x01b0, 16, {0x03, 0x12, 0x0d, 0xd5, 0xd2, 0x06, 0x12, 0x0d, 0x0d, 0x02, 0x03, 0x7c, 0x90, 0x7f, 0xea, 0xe0} }, +{ 0x01c0, 16, {0x75, 0x29, 0x00, 0xf5, 0x2a, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x29, 0x90, 0x7f, 0xee, 0xe0} }, +{ 0x01d0, 16, {0x75, 0x2b, 0x00, 0xf5, 0x2c, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x2b, 0x90, 0x7f, 0xe8, 0xe0} }, +{ 0x01e0, 16, {0x64, 0xc0, 0x60, 0x03, 0x02, 0x02, 0xc9, 0xe5, 0x2c, 0x45, 0x2b, 0x70, 0x03, 0x02, 0x03, 0x7c} }, +{ 0x01f0, 16, {0xc3, 0xe5, 0x2c, 0x94, 0x40, 0xe5, 0x2b, 0x94, 0x00, 0x50, 0x08, 0x85, 0x2b, 0x2d, 0x85, 0x2c} }, +{ 0x0200, 16, {0x2e, 0x80, 0x06, 0x75, 0x2d, 0x00, 0x75, 0x2e, 0x40, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70} }, +{ 0x0210, 16, {0x34, 0xf5, 0x31, 0xf5, 0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5, 0x31, 0x95, 0x2d, 0x50, 0x5c} }, +{ 0x0220, 16, {0xe5, 0x2a, 0x25, 0x32, 0xf5, 0x82, 0xe5, 0x31, 0x35, 0x29, 0xf5, 0x83, 0xe0, 0xff, 0x74, 0x00} }, +{ 0x0230, 16, {0x25, 0x32, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70} }, +{ 0x0240, 16, {0x02, 0x05, 0x31, 0x80, 0xd0, 0xe4, 0xf5, 0x31, 0xf5, 0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5} }, +{ 0x0250, 16, {0x31, 0x95, 0x2d, 0x50, 0x18, 0x74, 0x00, 0x25, 0x32, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83} }, +{ 0x0260, 16, {0x74, 0xcd, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70, 0x02, 0x05, 0x31, 0x80, 0xdd, 0xaf, 0x2a, 0xae} }, +{ 0x0270, 16, {0x29, 0xad, 0x2e, 0x7a, 0x7f, 0x79, 0x00, 0x7b, 0x00, 0x12, 0x0b, 0xf4, 0x90, 0x7f, 0xb5, 0xe5} }, +{ 0x0280, 16, {0x2e, 0xf0, 0xe5, 0x2e, 0x25, 0x2a, 0xf5, 0x2a, 0xe5, 0x2d, 0x35, 0x29, 0xf5, 0x29, 0xc3, 0xe5} }, +{ 0x0290, 16, {0x2c, 0x95, 0x2e, 0xf5, 0x2c, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0x90, 0x7f, 0x92, 0xe0, 0xff} }, +{ 0x02a0, 16, {0xc4, 0x54, 0x0f, 0x75, 0x2f, 0x00, 0xf5, 0x30, 0xd3, 0x94, 0x00, 0xe5, 0x2f, 0x94, 0x00, 0x50} }, +{ 0x02b0, 16, {0x0c, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe1, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xb4} }, +{ 0x02c0, 16, {0xe0, 0x20, 0xe2, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xe8, 0xe0, 0x64, 0x40, 0x60} }, +{ 0x02d0, 16, {0x03, 0x02, 0x03, 0x7c, 0xe5, 0x2c, 0x45, 0x2b, 0x70, 0x03, 0x02, 0x03, 0x7c, 0xe4, 0x90, 0x7f} }, +{ 0x02e0, 16, {0xc5, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x75, 0x2f, 0x00, 0xf5, 0x30, 0xd3} }, +{ 0x02f0, 16, {0x94, 0x00, 0xe5, 0x2f, 0x94, 0x00, 0x50, 0x09, 0x90, 0x7f, 0xc4, 0xe0, 0x30, 0xe1, 0x09, 0x80} }, +{ 0x0300, 16, {0xf7, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe3, 0xf9, 0x90, 0x7f, 0xc5, 0xe0, 0x75, 0x2d, 0x00, 0xf5} }, +{ 0x0310, 16, {0x2e, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70, 0x38, 0x90, 0x20, 0x6b, 0xf0, 0xf5, 0x31, 0xf5} }, +{ 0x0320, 16, {0x32, 0xc3, 0xe5, 0x32, 0x95, 0x2e, 0xe5, 0x31, 0x95, 0x2d, 0x50, 0x34, 0x74, 0xc0, 0x25, 0x32} }, +{ 0x0330, 16, {0xf5, 0x82, 0xe4, 0x34, 0x7e, 0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x2a, 0x25, 0x32, 0xf5, 0x82, 0xe5} }, +{ 0x0340, 16, {0x31, 0x35, 0x29, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x32, 0xe5, 0x32, 0x70, 0x02, 0x05, 0x31, 0x80} }, +{ 0x0350, 16, {0xd0, 0xaf, 0x2a, 0xae, 0x29, 0xad, 0x2e, 0x7a, 0x7e, 0x79, 0xc0, 0x7b, 0xc0, 0x12, 0x0c, 0x80} }, +{ 0x0360, 16, {0xe5, 0x2e, 0x25, 0x2a, 0xf5, 0x2a, 0xe5, 0x2d, 0x35, 0x29, 0xf5, 0x29, 0xc3, 0xe5, 0x2c, 0x95} }, +{ 0x0370, 13, {0x2e, 0xf5, 0x2c, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0x02, 0x02, 0xd4, 0xc3} }, +{ 0x037d, 1, {0x22} }, +{ 0x037e, 16, {0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x56, 0x14, 0x70, 0x03, 0x02, 0x04, 0xd2, 0x24} }, +{ 0x038e, 16, {0xfe, 0x70, 0x03, 0x02, 0x05, 0x46, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x50, 0x14, 0x70, 0x03} }, +{ 0x039e, 16, {0x02, 0x04, 0x4a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3e, 0x14, 0x70, 0x03, 0x02, 0x04, 0x44, 0x24} }, +{ 0x03ae, 16, {0x05, 0x60, 0x03, 0x02, 0x05, 0x9a, 0x12, 0x0e, 0x7b, 0x40, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f} }, +{ 0x03be, 16, {0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02, 0x70, 0x69, 0x74, 0x11, 0x90} }, +{ 0x03ce, 16, {0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0} }, +{ 0x03de, 16, {0xff, 0x12, 0x0b, 0x58, 0x8b, 0x26, 0x8a, 0x27, 0x89, 0x28, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02} }, +{ 0x03ee, 16, {0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90} }, +{ 0x03fe, 16, {0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x0c} }, +{ 0x040e, 16, {0x3f, 0x8b, 0x26, 0x8a, 0x27, 0x89, 0x28, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f} }, +{ 0x041e, 16, {0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x042e, 16, {0x44, 0x01, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xab} }, +{ 0x043e, 16, {0x12, 0x0e, 0x52, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x60, 0x02, 0x05, 0xab, 0x12, 0x0a, 0xf7, 0x02} }, +{ 0x044e, 16, {0x05, 0xab, 0x12, 0x08, 0xf1, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x7d, 0x40, 0x03, 0x02, 0x05, 0xab} }, +{ 0x045e, 16, {0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} }, +{ 0x046e, 16, {0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x02, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} }, +{ 0x047e, 16, {0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0xe4, 0x90, 0x7f, 0x00} }, +{ 0x048e, 16, {0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xec, 0xe0} }, +{ 0x049e, 16, {0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} }, +{ 0x04ae, 16, {0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} }, +{ 0x04be, 16, {0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} }, +{ 0x04ce, 16, {0xf0, 0x02, 0x05, 0xab, 0x12, 0x0e, 0x7f, 0x40, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xe8, 0xe0} }, +{ 0x04de, 16, {0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xea, 0xe0, 0xb4} }, +{ 0x04ee, 16, {0x01, 0x05, 0xc2, 0x00, 0x02, 0x05, 0xab, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05} }, +{ 0x04fe, 16, {0xab, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4} }, +{ 0x050e, 16, {0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f} }, +{ 0x051e, 16, {0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f} }, +{ 0x052e, 16, {0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x6e, 0x90} }, +{ 0x053e, 16, {0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x65, 0x12, 0x0e, 0x81, 0x50, 0x60, 0x90, 0x7f, 0xe8} }, +{ 0x054e, 16, {0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x54, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04} }, +{ 0x055e, 16, {0xd2, 0x00, 0x80, 0x49, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x40, 0x90, 0x7f, 0xea} }, +{ 0x056e, 16, {0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0} }, +{ 0x057e, 16, {0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01} }, +{ 0x058e, 16, {0xf0, 0x80, 0x1a, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x11, 0xe4, 0x90, 0x20, 0x6a} }, +{ 0x059e, 16, {0xf0, 0x12, 0x01, 0x00, 0x50, 0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4} }, +{ 0x05ae, 4, {0xe0, 0x44, 0x02, 0xf0} }, +{ 0x05b2, 1, {0x22} }, +{ 0x05b3, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x05c3, 16, {0xd0, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0x7f, 0xa5} }, +{ 0x05d3, 16, {0xe0, 0x30, 0xe2, 0x06, 0x75, 0x0d, 0x06, 0x02, 0x06, 0x7f, 0x90, 0x7f, 0xa5, 0xe0, 0x20, 0xe1} }, +{ 0x05e3, 16, {0x0c, 0xe5, 0x0d, 0x64, 0x02, 0x60, 0x06, 0x75, 0x0d, 0x07, 0x02, 0x06, 0x7f, 0xaf, 0x0d, 0xef} }, +{ 0x05f3, 16, {0x24, 0xfe, 0x60, 0x48, 0x14, 0x60, 0x2c, 0x24, 0xfe, 0x60, 0x77, 0x24, 0x04, 0x60, 0x03, 0x02} }, +{ 0x0603, 16, {0x06, 0x7f, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xaf, 0x0c, 0x05, 0x0c, 0x8f, 0x82, 0x75, 0x83} }, +{ 0x0613, 16, {0x00, 0x12, 0x07, 0x85, 0x90, 0x7f, 0xa6, 0xf0, 0xe5, 0x0c, 0x65, 0x08, 0x70, 0x5e, 0x75, 0x0d} }, +{ 0x0623, 16, {0x05, 0x80, 0x59, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e} }, +{ 0x0633, 16, {0x82, 0x75, 0x83, 0x00, 0x12, 0x07, 0xb2, 0x75, 0x0d, 0x02, 0x80, 0x40, 0xe5, 0x08, 0x24, 0xfe} }, +{ 0x0643, 16, {0xb5, 0x0c, 0x07, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0xe5, 0x08, 0x14, 0xb5, 0x0c, 0x0a} }, +{ 0x0653, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09} }, +{ 0x0663, 16, {0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e, 0x82, 0x75, 0x83, 0x00, 0x12, 0x07, 0xb2, 0x05, 0x0c} }, +{ 0x0673, 16, {0x80, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x53, 0x91, 0xdf, 0xd0} }, +{ 0x0683, 16, {0x07, 0xd0, 0x06, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x86, 0xd0} }, +{ 0x0693, 10, {0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x069d, 16, {0xc2, 0x04, 0xd2, 0x05, 0xe4, 0xf5, 0x25, 0xc2, 0x03, 0xc2, 0x00, 0xc2, 0x02, 0xc2, 0x01, 0x12} }, +{ 0x06ad, 16, {0x0e, 0x74, 0xd2, 0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9} }, +{ 0x06bd, 16, {0xf0, 0x90, 0x7f, 0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0x90} }, +{ 0x06cd, 16, {0x7f, 0x93, 0x74, 0x30, 0xf0, 0x12, 0x0a, 0x19, 0x75, 0x24, 0x48, 0x75, 0x23, 0x92, 0x75, 0x22} }, +{ 0x06dd, 16, {0x00, 0x75, 0x21, 0x00, 0xe4, 0xff, 0xfe, 0x7e, 0x05, 0x90, 0x20, 0x68, 0x74, 0x01, 0xf0, 0xa3} }, +{ 0x06ed, 16, {0xde, 0xfc, 0x7e, 0x00, 0x7f, 0x05, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae} }, +{ 0x06fd, 16, {0xe0, 0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0x12, 0x0e, 0x68, 0x30, 0x01, 0x0a, 0xe4, 0x90, 0x20, 0x69} }, +{ 0x070d, 16, {0xf0, 0x12, 0x03, 0x7e, 0xc2, 0x01, 0x30, 0x04, 0x1a, 0x12, 0x0e, 0x77, 0x50, 0x13, 0x12, 0x09} }, +{ 0x071d, 16, {0x00, 0x30, 0x00, 0x07, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0xf3, 0x12, 0x0d, 0x8b, 0x12, 0x0e} }, +{ 0x072d, 16, {0x79, 0xc2, 0x03, 0x7f, 0xff, 0x7e, 0xff, 0x7d, 0xff, 0x7c, 0xff, 0x78, 0x21, 0x12, 0x08, 0x1d} }, +{ 0x073d, 16, {0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xc3, 0x12, 0x08, 0x0c, 0x70, 0x1b, 0x75, 0x24} }, +{ 0x074d, 16, {0x48, 0x75, 0x23, 0x92, 0xf5, 0x22, 0xf5, 0x21, 0x63, 0x25, 0xff, 0x90, 0x20, 0x68, 0xe5, 0x25} }, +{ 0x075d, 14, {0xf0, 0xa3, 0x74, 0x01, 0xf0, 0xa3, 0xf0, 0xa3, 0xf0, 0x12, 0x08, 0xff, 0x80, 0x9b} }, +{ 0x076b, 1, {0x22} }, +{ 0x076c, 16, {0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} }, +{ 0x077c, 9, {0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x0785, 16, {0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} }, +{ 0x0795, 16, {0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} }, +{ 0x07a5, 13, {0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x07b2, 16, {0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0} }, +{ 0x07c2, 16, {0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8} }, +{ 0x07d2, 2, {0xf2, 0x22} }, +{ 0x07d4, 16, {0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} }, +{ 0x07e4, 16, {0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} }, +{ 0x07f4, 16, {0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} }, +{ 0x0804, 8, {0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} }, +{ 0x080c, 16, {0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} }, +{ 0x081c, 1, {0x22} }, +{ 0x081d, 16, {0x08, 0x08, 0x08, 0xe6, 0x2f, 0xff, 0xf6, 0x18, 0xe6, 0x3e, 0xfe, 0xf6, 0x18, 0xe6, 0x3d, 0xfd} }, +{ 0x082d, 7, {0xf6, 0x18, 0xe6, 0x3c, 0xfc, 0xf6, 0x22} }, +{ 0x0834, 4, {0x8c, 0x34, 0x8d, 0x35} }, +{ 0x0838, 16, {0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0xe4, 0xf5, 0x36, 0xf5, 0x37, 0xc3, 0xe5, 0x37, 0x95} }, +{ 0x0848, 16, {0x35, 0xe5, 0x36, 0x95, 0x34, 0x50, 0x69, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5} }, +{ 0x0858, 16, {0x83, 0x74, 0xff, 0xf0, 0xf4, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36} }, +{ 0x0868, 16, {0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36} }, +{ 0x0878, 16, {0x3e, 0xf5, 0x83, 0x74, 0xaa, 0xf0, 0x64, 0xaa, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x37, 0xf5} }, +{ 0x0888, 16, {0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0x74, 0x55, 0xf0, 0x64, 0x55, 0x60, 0x02, 0xc3, 0x22, 0xad} }, +{ 0x0898, 16, {0x37, 0xe5, 0x37, 0x2f, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xed, 0xf0, 0xfc, 0xac, 0x05} }, +{ 0x08a8, 16, {0xed, 0x6c, 0x60, 0x02, 0xc3, 0x22, 0x05, 0x37, 0xe5, 0x37, 0x70, 0x02, 0x05, 0x36, 0x80, 0x8c} }, +{ 0x08b8, 16, {0xe4, 0xf5, 0x36, 0xf5, 0x37, 0xc3, 0xe5, 0x37, 0x95, 0x35, 0xe5, 0x36, 0x95, 0x34, 0x50, 0x27} }, +{ 0x08c8, 16, {0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xe0, 0x65, 0x37, 0x60, 0x02, 0xc3} }, +{ 0x08d8, 16, {0x22, 0xef, 0x25, 0x37, 0xf5, 0x82, 0xe5, 0x36, 0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x05, 0x37, 0xe5} }, +{ 0x08e8, 8, {0x37, 0x70, 0x02, 0x05, 0x36, 0x80, 0xce, 0xd3} }, +{ 0x08f0, 1, {0x22} }, +{ 0x08f1, 14, {0x90, 0x7f, 0x00, 0xe5, 0x10, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} }, +{ 0x08ff, 1, {0x22} }, +{ 0x0900, 9, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} }, +{ 0x097d, 16, {0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} }, +{ 0x098d, 12, {0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x3a, 0x02, 0x09, 0xd4} }, +{ 0x0999, 16, {0x02, 0x06, 0x9d, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} }, +{ 0x09a9, 16, {0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} }, +{ 0x09b9, 16, {0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} }, +{ 0x09c9, 16, {0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0e, 0x2d, 0xe4, 0x7e} }, +{ 0x09d9, 16, {0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} }, +{ 0x09e9, 16, {0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} }, +{ 0x09f9, 16, {0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} }, +{ 0x0a09, 16, {0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} }, +{ 0x0a19, 16, {0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0x74, 0x89} }, +{ 0x0a29, 16, {0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0d, 0xd5, 0x90, 0x7f} }, +{ 0x0a39, 16, {0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x7f, 0x05, 0x7e, 0x00} }, +{ 0x0a49, 16, {0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02, 0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05} }, +{ 0x0a59, 16, {0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00} }, +{ 0x0a69, 16, {0x12, 0x0d, 0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d} }, +{ 0x0a79, 16, {0xd5, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d, 0xd5, 0x22} }, +{ 0x0a89, 16, {0x75, 0x33, 0x01, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x01, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x0e} }, +{ 0x0a99, 16, {0x7d, 0x00, 0x7c, 0x01, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12} }, +{ 0x0aa9, 16, {0x0e, 0x18, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x02, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x80, 0x7d} }, +{ 0x0ab9, 16, {0x00, 0x7c, 0x80, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0e} }, +{ 0x0ac9, 16, {0x18, 0xe5, 0x33, 0x60, 0x1b, 0x7f, 0x03, 0x12, 0x0e, 0x18, 0x7f, 0x00, 0x7e, 0x20, 0x7d, 0x40} }, +{ 0x0ad9, 16, {0x7c, 0x5b, 0x12, 0x08, 0x34, 0xe4, 0x33, 0xf5, 0x33, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0e, 0x18} }, +{ 0x0ae9, 13, {0xe5, 0x33, 0x60, 0x05, 0xe4, 0xff, 0x12, 0x0e, 0x18, 0xe5, 0x33, 0x24, 0xff} }, +{ 0x0af6, 1, {0x22} }, +{ 0x0af7, 8, {0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x10, 0xd3, 0x22} }, +{ 0x0aff, 1, {0x32} }, +{ 0x0b00, 16, {0x02, 0x0d, 0xa5, 0x00, 0x02, 0x0d, 0xec, 0x00, 0x02, 0x0d, 0x70, 0x00, 0x02, 0x0d, 0xbd, 0x00} }, +{ 0x0b10, 16, {0x02, 0x0e, 0x02, 0x00, 0x02, 0x0a, 0xff, 0x00, 0x02, 0x0e, 0x83, 0x00, 0x02, 0x0e, 0x84, 0x00} }, +{ 0x0b20, 16, {0x02, 0x0e, 0x85, 0x00, 0x02, 0x0e, 0x86, 0x00, 0x02, 0x0e, 0x87, 0x00, 0x02, 0x0e, 0x88, 0x00} }, +{ 0x0b30, 16, {0x02, 0x0e, 0x89, 0x00, 0x02, 0x0e, 0x8a, 0x00, 0x02, 0x0e, 0x8b, 0x00, 0x02, 0x0e, 0x8c, 0x00} }, +{ 0x0b40, 16, {0x02, 0x0e, 0x8d, 0x00, 0x02, 0x0e, 0x8e, 0x00, 0x02, 0x0e, 0x8f, 0x00, 0x02, 0x0e, 0x90, 0x00} }, +{ 0x0b50, 8, {0x02, 0x0e, 0x91, 0x00, 0x02, 0x0e, 0x92, 0x00} }, +{ 0x0b58, 16, {0xe4, 0xfe, 0x75, 0x2b, 0xff, 0x75, 0x2c, 0x11, 0x75, 0x2d, 0x12, 0xab, 0x2b, 0xaa, 0x2c, 0xa9} }, +{ 0x0b68, 16, {0x2d, 0x90, 0x00, 0x01, 0x12, 0x07, 0x85, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} }, +{ 0x0b78, 16, {0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x07, 0xd4, 0x85, 0xf0, 0x29, 0xf5, 0x2a, 0x62, 0x29} }, +{ 0x0b88, 16, {0xe5, 0x29, 0x62, 0x2a, 0xe5, 0x2a, 0x62, 0x29, 0x29, 0xfd, 0xe5, 0x29, 0x3a, 0xa9, 0x05, 0x75} }, +{ 0x0b98, 14, {0x2b, 0xff, 0xf5, 0x2c, 0x89, 0x2d, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x0ba6, 1, {0x22} }, +{ 0x0ba7, 6, {0xab, 0x07, 0xaa, 0x06, 0xac, 0x05} }, +{ 0x0bad, 16, {0xe4, 0xfd, 0xe5, 0x11, 0x60, 0x11, 0xea, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24, 0x10, 0xf5, 0x82} }, +{ 0x0bbd, 16, {0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xeb, 0xae, 0x05, 0x0d, 0x74, 0x10, 0x2e, 0xf5, 0x82} }, +{ 0x0bcd, 16, {0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xeb, 0xf0, 0xaf, 0x05, 0x0d, 0x74, 0x10, 0x2f, 0xf5, 0x82, 0xe4} }, +{ 0x0bdd, 16, {0x34, 0x0f, 0xf5, 0x83, 0xec, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f, 0x7b, 0x10, 0x12, 0x0d, 0x51, 0x7f} }, +{ 0x0bed, 6, {0x0a, 0x7e, 0x00, 0x12, 0x0d, 0xd5} }, +{ 0x0bf3, 1, {0x22} }, +{ 0x0bf4, 10, {0x8e, 0x33, 0x8f, 0x34, 0x8d, 0x35, 0x8a, 0x36, 0x8b, 0x37} }, +{ 0x0bfe, 16, {0xe4, 0xfd, 0xf5, 0x38, 0xe5, 0x11, 0x60, 0x12, 0xe5, 0x33, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24} }, +{ 0x0c0e, 16, {0x13, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x34, 0xae, 0x05, 0x0d, 0x74} }, +{ 0x0c1e, 16, {0x13, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xe5, 0x34, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f} }, +{ 0x0c2e, 16, {0x7b, 0x13, 0x12, 0x0d, 0x51, 0xaf, 0x0f, 0xad, 0x35, 0xab, 0x37, 0xaa, 0x36, 0x12, 0x0d, 0x32} }, +{ 0x0c3e, 1, {0x22} }, +{ 0x0c3f, 2, {0x8f, 0x29} }, +{ 0x0c41, 16, {0xe4, 0xf5, 0x2a, 0x75, 0x2b, 0xff, 0x75, 0x2c, 0x11, 0x75, 0x2d, 0x32, 0xab, 0x2b, 0xaa, 0x2c} }, +{ 0x0c51, 16, {0xa9, 0x2d, 0x90, 0x00, 0x01, 0x12, 0x07, 0x85, 0xb4, 0x03, 0x1d, 0xaf, 0x2a, 0x05, 0x2a, 0xef} }, +{ 0x0c61, 16, {0xb5, 0x29, 0x01, 0x22, 0x12, 0x07, 0x6c, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} }, +{ 0x0c71, 14, {0x2b, 0xff, 0xf5, 0x2c, 0x89, 0x2d, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x0c7f, 1, {0x22} }, +{ 0x0c80, 10, {0x8e, 0x33, 0x8f, 0x34, 0x8d, 0x35, 0x8a, 0x36, 0x8b, 0x37} }, +{ 0x0c8a, 16, {0xe4, 0xf5, 0x38, 0xe5, 0x38, 0xc3, 0x95, 0x35, 0x50, 0x20, 0x05, 0x34, 0xe5, 0x34, 0xae, 0x33} }, +{ 0x0c9a, 16, {0x70, 0x02, 0x05, 0x33, 0x14, 0xff, 0xe5, 0x37, 0x25, 0x38, 0xf5, 0x82, 0xe4, 0x35, 0x36, 0xf5} }, +{ 0x0caa, 10, {0x83, 0xe0, 0xfd, 0x12, 0x0b, 0xa7, 0x05, 0x38, 0x80, 0xd9} }, +{ 0x0cb4, 1, {0x22} }, +{ 0x0cb5, 16, {0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x25, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} }, +{ 0x0cc5, 16, {0x44, 0x01, 0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a} }, +{ 0x0cd5, 13, {0x0a, 0x89, 0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x03, 0xd3, 0x22, 0xc3, 0x22} }, +{ 0x0ce2, 16, {0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} }, +{ 0x0cf2, 16, {0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a, 0x0a, 0x89} }, +{ 0x0d02, 11, {0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x01, 0xd3, 0x22, 0xc3, 0x22} }, +{ 0x0d0d, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x06, 0x04, 0xe0, 0x44} }, +{ 0x0d1d, 16, {0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x0d, 0xd5, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} }, +{ 0x0d2d, 5, {0xe0, 0x44, 0x04, 0xf0, 0x22} }, +{ 0x0d32, 16, {0x12, 0x0c, 0xb5, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} }, +{ 0x0d42, 15, {0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} }, +{ 0x0d51, 16, {0x12, 0x0c, 0xe2, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} }, +{ 0x0d61, 15, {0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} }, +{ 0x0d70, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0d80, 11, {0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d8b, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x12, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x14, 0x7e, 0x00, 0x12} }, +{ 0x0d9b, 10, {0x0d, 0xd5, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} }, +{ 0x0da5, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} }, +{ 0x0db5, 8, {0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0dbd, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x03, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} }, +{ 0x0dcd, 8, {0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0dd5, 16, {0x8e, 0x39, 0x8f, 0x3a, 0xe5, 0x3a, 0x15, 0x3a, 0xae, 0x39, 0x70, 0x02, 0x15, 0x39, 0x4e, 0x60} }, +{ 0x0de5, 7, {0x05, 0x12, 0x0e, 0x41, 0x80, 0xee, 0x22} }, +{ 0x0dec, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0} }, +{ 0x0dfc, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0e02, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} }, +{ 0x0e12, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0e18, 16, {0xae, 0x07, 0x7f, 0x21, 0x7d, 0x01, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xab, 0x82} }, +{ 0x0e28, 5, {0xfa, 0x12, 0x0d, 0x51, 0x22} }, +{ 0x0e2d, 16, {0x50, 0x0f, 0x00, 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6} }, +{ 0x0e3d, 3, {0xa1, 0x86, 0x8e} }, +{ 0x0e40, 1, {0x00} }, +{ 0x0e41, 16, {0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} }, +{ 0x0e51, 1, {0x22} }, +{ 0x0e52, 14, {0x90, 0x7f, 0x00, 0xe5, 0x0e, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} }, +{ 0x0e60, 8, {0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0e, 0xd3, 0x22} }, +{ 0x0e68, 8, {0xe4, 0xf5, 0x0d, 0xd2, 0xe9, 0xd2, 0xaf, 0x22} }, +{ 0x0e70, 4, {0x53, 0xd8, 0xef, 0x32} }, +{ 0x0e74, 3, {0xd2, 0x00, 0x22} }, +{ 0x0e77, 2, {0xd3, 0x22} }, +{ 0x0e79, 2, {0xd3, 0x22} }, +{ 0x0e7b, 2, {0xd3, 0x22} }, +{ 0x0e7d, 2, {0xd3, 0x22} }, +{ 0x0e7f, 2, {0xd3, 0x22} }, +{ 0x0e81, 2, {0xd3, 0x22} }, +{ 0x0e83, 1, {0x32} }, +{ 0x0e84, 1, {0x32} }, +{ 0x0e85, 1, {0x32} }, +{ 0x0e86, 1, {0x32} }, +{ 0x0e87, 1, {0x32} }, +{ 0x0e88, 1, {0x32} }, +{ 0x0e89, 1, {0x32} }, +{ 0x0e8a, 1, {0x32} }, +{ 0x0e8b, 1, {0x32} }, +{ 0x0e8c, 1, {0x32} }, +{ 0x0e8d, 1, {0x32} }, +{ 0x0e8e, 1, {0x32} }, +{ 0x0e8f, 1, {0x32} }, +{ 0x0e90, 1, {0x32} }, +{ 0x0e91, 1, {0x32} }, +{ 0x0e92, 1, {0x32} }, +{ 0x1100, 16, {0x12, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x47, 0x05, 0x10, 0x27, 0x01, 0x00, 0x01, 0x02} }, +{ 0x1110, 16, {0x00, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x03, 0xa0, 0x00, 0x09, 0x04, 0x00, 0x00, 0x02} }, +{ 0x1120, 16, {0xff, 0x00, 0x00, 0x04, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} }, +{ 0x1130, 16, {0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x26, 0x03, 0x41, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x68, 0x00} }, +{ 0x1140, 16, {0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00, 0x69, 0x00, 0x70, 0x00, 0x73, 0x00} }, +{ 0x1150, 16, {0x2c, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x28, 0x03, 0x46, 0x00} }, +{ 0x1160, 16, {0x69, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00} }, +{ 0x1170, 16, {0x46, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00} }, +{ 0x1180, 16, {0x6b, 0x00, 0x73, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} }, +{ 0x1190, 16, {0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} }, +{ 0x11a0, 16, {0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} }, +{ 0x11b0, 16, {0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} }, +{ 0x11c0, 16, {0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} }, +{ 0x11d0, 2, {0x00, 0x00} }, +{ 0xffff, 0, {0x00} } +}; + +#else + +static const struct whiteheat_hex_record whiteheat_loader[] = { +{ 0x0000, 3, {0x02, 0x09, 0x8d} }, +{ 0x0033, 3, {0x02, 0x08, 0xfb} }, +{ 0x0043, 3, {0x02, 0x0b, 0x00} }, +{ 0x004b, 3, {0x02, 0x05, 0xaa} }, +{ 0x0100, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x54, 0x10, 0xff, 0xc4, 0x54, 0x0f, 0x44, 0x50, 0xf5, 0x0f, 0x13, 0xe4} }, +{ 0x0110, 16, {0x33, 0xf5, 0x11, 0x90, 0x7f, 0xe9, 0xe0, 0x24, 0x5e, 0xb4, 0x07, 0x00, 0x40, 0x03, 0x02, 0x03} }, +{ 0x0120, 16, {0x78, 0x90, 0x01, 0x28, 0xf8, 0x28, 0x28, 0x73, 0x02, 0x01, 0xbc, 0x02, 0x01, 0xbc, 0x02, 0x01} }, +{ 0x0130, 16, {0x91, 0x02, 0x01, 0x3d, 0x02, 0x01, 0x53, 0x02, 0x01, 0x6f, 0x02, 0x01, 0x9a, 0x90, 0x7f, 0x00} }, +{ 0x0140, 16, {0xe5, 0x11, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0} }, +{ 0x0150, 16, {0x02, 0x03, 0x78, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x90, 0x7f, 0x00, 0xf0, 0x90} }, +{ 0x0160, 16, {0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03, 0x78, 0x12} }, +{ 0x0170, 16, {0x0a, 0x89, 0x50, 0x07, 0xe4, 0x90, 0x7f, 0x00, 0xf0, 0x80, 0x06, 0x90, 0x7f, 0x00, 0x74, 0x0f} }, +{ 0x0180, 16, {0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x02, 0x03} }, +{ 0x0190, 16, {0x78, 0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0f, 0x02, 0x03, 0x78, 0x90, 0x7f, 0x00, 0x74, 0x07, 0xf0} }, +{ 0x01a0, 16, {0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x7f, 0xe8, 0x7e} }, +{ 0x01b0, 16, {0x03, 0x12, 0x0d, 0x94, 0xd2, 0x06, 0x12, 0x0c, 0xcc, 0x02, 0x03, 0x78, 0x90, 0x7f, 0xea, 0xe0} }, +{ 0x01c0, 16, {0x75, 0x28, 0x00, 0xf5, 0x29, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x28, 0x90, 0x7f, 0xee, 0xe0} }, +{ 0x01d0, 16, {0x75, 0x2a, 0x00, 0xf5, 0x2b, 0xa3, 0xe0, 0xfe, 0xe4, 0xee, 0x42, 0x2a, 0x90, 0x7f, 0xe8, 0xe0} }, +{ 0x01e0, 16, {0x64, 0xc0, 0x60, 0x03, 0x02, 0x02, 0xc9, 0xe5, 0x2b, 0x45, 0x2a, 0x70, 0x03, 0x02, 0x03, 0x78} }, +{ 0x01f0, 16, {0xc3, 0xe5, 0x2b, 0x94, 0x40, 0xe5, 0x2a, 0x94, 0x00, 0x50, 0x08, 0x85, 0x2a, 0x2c, 0x85, 0x2b} }, +{ 0x0200, 16, {0x2d, 0x80, 0x06, 0x75, 0x2c, 0x00, 0x75, 0x2d, 0x40, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70} }, +{ 0x0210, 16, {0x34, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31, 0x95, 0x2d, 0xe5, 0x30, 0x95, 0x2c, 0x50, 0x5c} }, +{ 0x0220, 16, {0xe5, 0x29, 0x25, 0x31, 0xf5, 0x82, 0xe5, 0x30, 0x35, 0x28, 0xf5, 0x83, 0xe0, 0xff, 0x74, 0x00} }, +{ 0x0230, 16, {0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xef, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70} }, +{ 0x0240, 16, {0x02, 0x05, 0x30, 0x80, 0xd0, 0xe4, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31, 0x95, 0x2d, 0xe5} }, +{ 0x0250, 16, {0x30, 0x95, 0x2c, 0x50, 0x18, 0x74, 0x00, 0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83} }, +{ 0x0260, 16, {0x74, 0xcd, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70, 0x02, 0x05, 0x30, 0x80, 0xdd, 0xaf, 0x29, 0xae} }, +{ 0x0270, 16, {0x28, 0xad, 0x2d, 0x7a, 0x7f, 0x79, 0x00, 0x7b, 0x00, 0x12, 0x0b, 0xf4, 0x90, 0x7f, 0xb5, 0xe5} }, +{ 0x0280, 16, {0x2d, 0xf0, 0xe5, 0x2d, 0x25, 0x29, 0xf5, 0x29, 0xe5, 0x2c, 0x35, 0x28, 0xf5, 0x28, 0xc3, 0xe5} }, +{ 0x0290, 16, {0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0xe5, 0x2a, 0x95, 0x2c, 0xf5, 0x2a, 0x90, 0x7f, 0x92, 0xe0, 0xff} }, +{ 0x02a0, 16, {0xc4, 0x54, 0x0f, 0x75, 0x2e, 0x00, 0xf5, 0x2f, 0xd3, 0x94, 0x00, 0xe5, 0x2e, 0x94, 0x00, 0x50} }, +{ 0x02b0, 16, {0x0c, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe1, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xb4} }, +{ 0x02c0, 16, {0xe0, 0x20, 0xe2, 0x03, 0x02, 0x01, 0xe7, 0x80, 0xf4, 0x90, 0x7f, 0xe8, 0xe0, 0x64, 0x40, 0x60} }, +{ 0x02d0, 16, {0x03, 0x02, 0x03, 0x78, 0xe5, 0x2b, 0x45, 0x2a, 0x70, 0x03, 0x02, 0x03, 0x78, 0xe4, 0x90, 0x7f} }, +{ 0x02e0, 16, {0xc5, 0xf0, 0x90, 0x7f, 0x92, 0xe0, 0xff, 0xc4, 0x54, 0x0f, 0x75, 0x2e, 0x00, 0xf5, 0x2f, 0xd3} }, +{ 0x02f0, 16, {0x94, 0x00, 0xe5, 0x2e, 0x94, 0x00, 0x50, 0x09, 0x90, 0x7f, 0xc4, 0xe0, 0x30, 0xe1, 0x09, 0x80} }, +{ 0x0300, 16, {0xf7, 0x90, 0x7f, 0xb4, 0xe0, 0x20, 0xe3, 0xf9, 0x90, 0x7f, 0xc5, 0xe0, 0x75, 0x2c, 0x00, 0xf5} }, +{ 0x0310, 16, {0x2d, 0x90, 0x7f, 0xe9, 0xe0, 0x64, 0xa3, 0x70, 0x34, 0xf5, 0x30, 0xf5, 0x31, 0xc3, 0xe5, 0x31} }, +{ 0x0320, 16, {0x95, 0x2d, 0xe5, 0x30, 0x95, 0x2c, 0x50, 0x34, 0x74, 0xc0, 0x25, 0x31, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x0330, 1, {0x7e} }, +{ 0x0331, 16, {0xf5, 0x83, 0xe0, 0xff, 0xe5, 0x29, 0x25, 0x31, 0xf5, 0x82, 0xe5, 0x30, 0x35, 0x28, 0xf5, 0x83} }, +{ 0x0341, 16, {0xef, 0xf0, 0x05, 0x31, 0xe5, 0x31, 0x70, 0x02, 0x05, 0x30, 0x80, 0xd0, 0xaf, 0x29, 0xae, 0x28} }, +{ 0x0351, 16, {0xad, 0x2d, 0x7a, 0x7e, 0x79, 0xc0, 0x7b, 0xc0, 0x12, 0x0c, 0x3f, 0xe5, 0x2d, 0x25, 0x29, 0xf5} }, +{ 0x0361, 16, {0x29, 0xe5, 0x2c, 0x35, 0x28, 0xf5, 0x28, 0xc3, 0xe5, 0x2b, 0x95, 0x2d, 0xf5, 0x2b, 0xe5, 0x2a} }, +{ 0x0371, 9, {0x95, 0x2c, 0xf5, 0x2a, 0x02, 0x02, 0xd4, 0xc3, 0x22} }, +{ 0x037a, 16, {0x90, 0x7f, 0xe9, 0xe0, 0x70, 0x03, 0x02, 0x04, 0x52, 0x14, 0x70, 0x03, 0x02, 0x04, 0xce, 0x24} }, +{ 0x038a, 16, {0xfe, 0x70, 0x03, 0x02, 0x05, 0x42, 0x24, 0xfb, 0x70, 0x03, 0x02, 0x04, 0x4c, 0x14, 0x70, 0x03} }, +{ 0x039a, 16, {0x02, 0x04, 0x46, 0x14, 0x70, 0x03, 0x02, 0x04, 0x3a, 0x14, 0x70, 0x03, 0x02, 0x04, 0x40, 0x24} }, +{ 0x03aa, 16, {0x05, 0x60, 0x03, 0x02, 0x05, 0x96, 0x12, 0x0e, 0x44, 0x40, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f} }, +{ 0x03ba, 16, {0xeb, 0xe0, 0x24, 0xfe, 0x60, 0x16, 0x14, 0x60, 0x40, 0x24, 0x02, 0x70, 0x69, 0x74, 0x11, 0x90} }, +{ 0x03ca, 16, {0x7f, 0xd4, 0xf0, 0x74, 0x00, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0} }, +{ 0x03da, 16, {0xff, 0x12, 0x0b, 0x58, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02} }, +{ 0x03ea, 16, {0xee, 0x90, 0x7f, 0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90} }, +{ 0x03fa, 16, {0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0, 0xff, 0x12, 0x08} }, +{ 0x040a, 16, {0xba, 0x8b, 0x25, 0x8a, 0x26, 0x89, 0x27, 0xea, 0x49, 0x60, 0x11, 0xae, 0x02, 0xee, 0x90, 0x7f} }, +{ 0x041a, 16, {0xd4, 0xf0, 0xaf, 0x01, 0xef, 0x90, 0x7f, 0xd5, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0} }, +{ 0x042a, 16, {0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05, 0xa2} }, +{ 0x043a, 16, {0x12, 0x0e, 0x1f, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x2d, 0x02, 0x05, 0xa2, 0x12, 0x0a, 0xf7, 0x02} }, +{ 0x044a, 16, {0x05, 0xa2, 0x12, 0x0e, 0x11, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x46, 0x40, 0x03, 0x02, 0x05, 0xa2} }, +{ 0x045a, 16, {0x90, 0x7f, 0xe8, 0xe0, 0x24, 0x7f, 0x60, 0x24, 0x14, 0x60, 0x31, 0x24, 0x02, 0x70, 0x5b, 0xa2} }, +{ 0x046a, 16, {0x00, 0xe4, 0x33, 0xff, 0x25, 0xe0, 0xff, 0xa2, 0x02, 0xe4, 0x33, 0x4f, 0x90, 0x7f, 0x00, 0xf0} }, +{ 0x047a, 16, {0xe4, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0xe4, 0x90, 0x7f, 0x00} }, +{ 0x048a, 16, {0xf0, 0xa3, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xec, 0xe0} }, +{ 0x049a, 16, {0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4} }, +{ 0x04aa, 16, {0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0xe0, 0x54, 0xfd, 0x90, 0x7f, 0x00, 0xf0, 0xe4, 0xa3} }, +{ 0x04ba, 16, {0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x02, 0xf0, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01} }, +{ 0x04ca, 16, {0xf0, 0x02, 0x05, 0xa2, 0x12, 0x0e, 0x48, 0x40, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xe8, 0xe0} }, +{ 0x04da, 16, {0x24, 0xfe, 0x60, 0x1d, 0x24, 0x02, 0x60, 0x03, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xea, 0xe0, 0xb4} }, +{ 0x04ea, 16, {0x01, 0x05, 0xc2, 0x00, 0x02, 0x05, 0xa2, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x02, 0x05} }, +{ 0x04fa, 16, {0xa2, 0x90, 0x7f, 0xea, 0xe0, 0x70, 0x38, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4} }, +{ 0x050a, 16, {0x54, 0x0f, 0xff, 0xe0, 0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f} }, +{ 0x051a, 16, {0xf5, 0x83, 0xe4, 0xf0, 0x90, 0x7f, 0xec, 0xe0, 0x54, 0x80, 0xff, 0x13, 0x13, 0x13, 0x54, 0x1f} }, +{ 0x052a, 16, {0xff, 0xe0, 0x54, 0x07, 0x2f, 0x90, 0x7f, 0xd7, 0xf0, 0xe0, 0x44, 0x20, 0xf0, 0x80, 0x69, 0x90} }, +{ 0x053a, 16, {0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x60, 0x12, 0x0e, 0x4a, 0x50, 0x5b, 0x90, 0x7f, 0xe8} }, +{ 0x054a, 16, {0xe0, 0x24, 0xfe, 0x60, 0x18, 0x24, 0x02, 0x70, 0x4f, 0x90, 0x7f, 0xea, 0xe0, 0xb4, 0x01, 0x04} }, +{ 0x055a, 16, {0xd2, 0x00, 0x80, 0x44, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x3b, 0x90, 0x7f, 0xea} }, +{ 0x056a, 16, {0xe0, 0x70, 0x20, 0x90, 0x7f, 0xec, 0xe0, 0xf4, 0x54, 0x80, 0xff, 0xc4, 0x54, 0x0f, 0xff, 0xe0} }, +{ 0x057a, 16, {0x54, 0x07, 0x2f, 0x25, 0xe0, 0x24, 0xb4, 0xf5, 0x82, 0xe4, 0x34, 0x7f, 0xf5, 0x83, 0x74, 0x01} }, +{ 0x058a, 16, {0xf0, 0x80, 0x15, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x80, 0x0c, 0x12, 0x01, 0x00, 0x50} }, +{ 0x059a, 16, {0x07, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xb4, 0xe0, 0x44, 0x02, 0xf0, 0x22} }, +{ 0x05aa, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xc0, 0x85, 0xc0, 0x84, 0xc0, 0x86, 0x75, 0x86, 0x00, 0xc0} }, +{ 0x05ba, 16, {0xd0, 0xc0, 0x00, 0xc0, 0x01, 0xc0, 0x02, 0xc0, 0x03, 0xc0, 0x06, 0xc0, 0x07, 0x90, 0x7f, 0xa5} }, +{ 0x05ca, 16, {0xe0, 0x30, 0xe2, 0x06, 0x75, 0x0d, 0x06, 0x02, 0x06, 0x76, 0x90, 0x7f, 0xa5, 0xe0, 0x20, 0xe1} }, +{ 0x05da, 16, {0x0c, 0xe5, 0x0d, 0x64, 0x02, 0x60, 0x06, 0x75, 0x0d, 0x07, 0x02, 0x06, 0x76, 0xaf, 0x0d, 0xef} }, +{ 0x05ea, 16, {0x24, 0xfe, 0x60, 0x48, 0x14, 0x60, 0x2c, 0x24, 0xfe, 0x60, 0x77, 0x24, 0x04, 0x60, 0x03, 0x02} }, +{ 0x05fa, 16, {0x06, 0x76, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xaf, 0x0c, 0x05, 0x0c, 0x8f, 0x82, 0x75, 0x83} }, +{ 0x060a, 16, {0x00, 0x12, 0x08, 0x22, 0x90, 0x7f, 0xa6, 0xf0, 0xe5, 0x0c, 0x65, 0x08, 0x70, 0x5e, 0x75, 0x0d} }, +{ 0x061a, 16, {0x05, 0x80, 0x59, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09, 0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e} }, +{ 0x062a, 16, {0x82, 0x75, 0x83, 0x00, 0x12, 0x08, 0x4f, 0x75, 0x0d, 0x02, 0x80, 0x40, 0xe5, 0x08, 0x24, 0xfe} }, +{ 0x063a, 16, {0xb5, 0x0c, 0x07, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x20, 0xf0, 0xe5, 0x08, 0x14, 0xb5, 0x0c, 0x0a} }, +{ 0x064a, 16, {0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x90, 0x7f, 0xa6, 0xe0, 0xab, 0x09} }, +{ 0x065a, 16, {0xaa, 0x0a, 0xa9, 0x0b, 0xae, 0x0c, 0x8e, 0x82, 0x75, 0x83, 0x00, 0x12, 0x08, 0x4f, 0x05, 0x0c} }, +{ 0x066a, 16, {0x80, 0x0a, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x40, 0xf0, 0xe4, 0xf5, 0x0d, 0x53, 0x91, 0xdf, 0xd0} }, +{ 0x067a, 16, {0x07, 0xd0, 0x06, 0xd0, 0x03, 0xd0, 0x02, 0xd0, 0x01, 0xd0, 0x00, 0xd0, 0xd0, 0xd0, 0x86, 0xd0} }, +{ 0x068a, 10, {0x84, 0xd0, 0x85, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0694, 16, {0x8c, 0x33, 0x8d, 0x34, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0xe4, 0xf5, 0x35, 0xf5, 0x36} }, +{ 0x06a4, 16, {0xc3, 0xe5, 0x36, 0x95, 0x34, 0xe5, 0x35, 0x95, 0x33, 0x50, 0x69, 0xef, 0x25, 0x36, 0xf5, 0x82} }, +{ 0x06b4, 16, {0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0xff, 0xf0, 0xf4, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36} }, +{ 0x06c4, 16, {0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe4, 0xf0, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36} }, +{ 0x06d4, 16, {0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0xaa, 0xf0, 0x64, 0xaa, 0x60, 0x02, 0xc3, 0x22} }, +{ 0x06e4, 16, {0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0x74, 0x55, 0xf0, 0x64, 0x55, 0x60} }, +{ 0x06f4, 16, {0x02, 0xc3, 0x22, 0xad, 0x36, 0xe5, 0x36, 0x2f, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xed} }, +{ 0x0704, 16, {0xf0, 0xfc, 0xac, 0x05, 0xed, 0x6c, 0x60, 0x02, 0xc3, 0x22, 0x05, 0x36, 0xe5, 0x36, 0x70, 0x02} }, +{ 0x0714, 16, {0x05, 0x35, 0x80, 0x8c, 0xe4, 0xf5, 0x35, 0xf5, 0x36, 0xc3, 0xe5, 0x36, 0x95, 0x34, 0xe5, 0x35} }, +{ 0x0724, 16, {0x95, 0x33, 0x50, 0x27, 0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe0, 0x65} }, +{ 0x0734, 16, {0x36, 0x60, 0x02, 0xc3, 0x22, 0xef, 0x25, 0x36, 0xf5, 0x82, 0xe5, 0x35, 0x3e, 0xf5, 0x83, 0xe4} }, +{ 0x0744, 13, {0xf0, 0x05, 0x36, 0xe5, 0x36, 0x70, 0x02, 0x05, 0x35, 0x80, 0xce, 0xd3, 0x22} }, +{ 0x0751, 16, {0xc2, 0x04, 0xd2, 0x05, 0xc2, 0x03, 0xc2, 0x00, 0xc2, 0x02, 0xc2, 0x01, 0x12, 0x0e, 0x3d, 0xd2} }, +{ 0x0761, 16, {0xe8, 0x43, 0xd8, 0x20, 0x90, 0x7f, 0xab, 0x74, 0xff, 0xf0, 0x90, 0x7f, 0xa9, 0xf0, 0x90, 0x7f} }, +{ 0x0771, 16, {0xaa, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f, 0x95, 0xe0, 0x44, 0xc0, 0xf0, 0x90, 0x7f, 0x93, 0x74} }, +{ 0x0781, 16, {0x30, 0xf0, 0x12, 0x0a, 0x19, 0x90, 0x7f, 0xaf, 0xe0, 0x44, 0x01, 0xf0, 0x90, 0x7f, 0xae, 0xe0} }, +{ 0x0791, 16, {0x44, 0x0d, 0xf0, 0xd2, 0xaf, 0x12, 0x0e, 0x35, 0x20, 0x01, 0x42, 0x75, 0x24, 0x00, 0x75, 0x23} }, +{ 0x07a1, 16, {0x00, 0x75, 0x22, 0x00, 0x75, 0x21, 0x00, 0x7f, 0x48, 0x7e, 0x92, 0x7d, 0x00, 0x7c, 0x00, 0xab} }, +{ 0x07b1, 16, {0x24, 0xaa, 0x23, 0xa9, 0x22, 0xa8, 0x21, 0xc3, 0x12, 0x08, 0xa9, 0x50, 0xdb, 0x20, 0x01, 0xd8} }, +{ 0x07c1, 16, {0x7a, 0x00, 0x79, 0x00, 0x78, 0x00, 0xe5, 0x24, 0x24, 0x01, 0xf5, 0x24, 0xea, 0x35, 0x23, 0xf5} }, +{ 0x07d1, 16, {0x23, 0xe9, 0x35, 0x22, 0xf5, 0x22, 0xe8, 0x35, 0x21, 0xf5, 0x21, 0x80, 0xca, 0x30, 0x01, 0x05} }, +{ 0x07e1, 16, {0x12, 0x03, 0x7a, 0xc2, 0x01, 0x30, 0x04, 0x1a, 0x12, 0x0e, 0x40, 0x50, 0x13, 0x12, 0x09, 0x00} }, +{ 0x07f1, 16, {0x30, 0x00, 0x07, 0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0xf3, 0x12, 0x0d, 0x4a, 0x12, 0x0e, 0x42} }, +{ 0x0801, 8, {0xc2, 0x03, 0x12, 0x08, 0xff, 0x80, 0xd6, 0x22} }, +{ 0x0809, 16, {0xbb, 0x01, 0x06, 0x89, 0x82, 0x8a, 0x83, 0xe0, 0x22, 0x50, 0x02, 0xe7, 0x22, 0xbb, 0xfe, 0x02} }, +{ 0x0819, 9, {0xe3, 0x22, 0x89, 0x82, 0x8a, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x0822, 16, {0xbb, 0x01, 0x0c, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0x22, 0x50} }, +{ 0x0832, 16, {0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe6, 0x22, 0xbb, 0xfe, 0x06, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0x22} }, +{ 0x0842, 13, {0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe4, 0x93, 0x22} }, +{ 0x084f, 16, {0xf8, 0xbb, 0x01, 0x0d, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe8, 0xf0} }, +{ 0x085f, 16, {0x22, 0x50, 0x06, 0xe9, 0x25, 0x82, 0xc8, 0xf6, 0x22, 0xbb, 0xfe, 0x05, 0xe9, 0x25, 0x82, 0xc8} }, +{ 0x086f, 2, {0xf2, 0x22} }, +{ 0x0871, 16, {0xbb, 0x01, 0x10, 0xe5, 0x82, 0x29, 0xf5, 0x82, 0xe5, 0x83, 0x3a, 0xf5, 0x83, 0xe0, 0xf5, 0xf0} }, +{ 0x0881, 16, {0xa3, 0xe0, 0x22, 0x50, 0x09, 0xe9, 0x25, 0x82, 0xf8, 0x86, 0xf0, 0x08, 0xe6, 0x22, 0xbb, 0xfe} }, +{ 0x0891, 16, {0x0a, 0xe9, 0x25, 0x82, 0xf8, 0xe2, 0xf5, 0xf0, 0x08, 0xe2, 0x22, 0xe5, 0x83, 0x2a, 0xf5, 0x83} }, +{ 0x08a1, 8, {0xe9, 0x93, 0xf5, 0xf0, 0xa3, 0xe9, 0x93, 0x22} }, +{ 0x08a9, 16, {0xeb, 0x9f, 0xf5, 0xf0, 0xea, 0x9e, 0x42, 0xf0, 0xe9, 0x9d, 0x42, 0xf0, 0xe8, 0x9c, 0x45, 0xf0} }, +{ 0x08b9, 1, {0x22} }, +{ 0x08ba, 2, {0x8f, 0x28} }, +{ 0x08bc, 16, {0xe4, 0xf5, 0x29, 0x75, 0x2a, 0xff, 0x75, 0x2b, 0x11, 0x75, 0x2c, 0x32, 0xab, 0x2a, 0xaa, 0x2b} }, +{ 0x08cc, 16, {0xa9, 0x2c, 0x90, 0x00, 0x01, 0x12, 0x08, 0x22, 0xb4, 0x03, 0x1d, 0xaf, 0x29, 0x05, 0x29, 0xef} }, +{ 0x08dc, 16, {0xb5, 0x28, 0x01, 0x22, 0x12, 0x08, 0x09, 0x7e, 0x00, 0x29, 0xff, 0xee, 0x3a, 0xa9, 0x07, 0x75} }, +{ 0x08ec, 14, {0x2a, 0xff, 0xf5, 0x2b, 0x89, 0x2c, 0x80, 0xd4, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x08fa, 1, {0x22} }, +{ 0x08fb, 4, {0x53, 0xd8, 0xef, 0x32} }, +{ 0x08ff, 1, {0x22} }, +{ 0x0900, 9, {0x90, 0x7f, 0xd6, 0xe0, 0x44, 0x80, 0xf0, 0x80, 0x74} }, +{ 0x097d, 16, {0x43, 0x87, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22} }, +{ 0x098d, 12, {0x78, 0x7f, 0xe4, 0xf6, 0xd8, 0xfd, 0x75, 0x81, 0x39, 0x02, 0x09, 0xd4} }, +{ 0x0999, 16, {0x02, 0x07, 0x51, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0x40, 0x03, 0xf6, 0x80, 0x01, 0xf2} }, +{ 0x09a9, 16, {0x08, 0xdf, 0xf4, 0x80, 0x29, 0xe4, 0x93, 0xa3, 0xf8, 0x54, 0x07, 0x24, 0x0c, 0xc8, 0xc3, 0x33} }, +{ 0x09b9, 16, {0xc4, 0x54, 0x0f, 0x44, 0x20, 0xc8, 0x83, 0x40, 0x04, 0xf4, 0x56, 0x80, 0x01, 0x46, 0xf6, 0xdf} }, +{ 0x09c9, 16, {0xe4, 0x80, 0x0b, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x90, 0x0d, 0xec, 0xe4, 0x7e} }, +{ 0x09d9, 16, {0x01, 0x93, 0x60, 0xbc, 0xa3, 0xff, 0x54, 0x3f, 0x30, 0xe5, 0x09, 0x54, 0x1f, 0xfe, 0xe4, 0x93} }, +{ 0x09e9, 16, {0xa3, 0x60, 0x01, 0x0e, 0xcf, 0x54, 0xc0, 0x25, 0xe0, 0x60, 0xa8, 0x40, 0xb8, 0xe4, 0x93, 0xa3} }, +{ 0x09f9, 16, {0xfa, 0xe4, 0x93, 0xa3, 0xf8, 0xe4, 0x93, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca} }, +{ 0x0a09, 16, {0xf0, 0xa3, 0xc8, 0xc5, 0x82, 0xc8, 0xca, 0xc5, 0x83, 0xca, 0xdf, 0xe9, 0xde, 0xe7, 0x80, 0xbe} }, +{ 0x0a19, 16, {0xe4, 0x90, 0x7f, 0x9c, 0xf0, 0x7f, 0x0a, 0xfe, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0x74, 0x89} }, +{ 0x0a29, 16, {0xf0, 0x90, 0x7f, 0x9c, 0x74, 0xcf, 0xf0, 0x7f, 0xf4, 0x7e, 0x01, 0x12, 0x0d, 0x94, 0x90, 0x7f} }, +{ 0x0a39, 16, {0x96, 0xe0, 0x54, 0xfe, 0xf0, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x7f, 0x05, 0x7e, 0x00} }, +{ 0x0a49, 16, {0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x02, 0xf0, 0xe0, 0x54, 0x7f, 0xf0, 0x7f, 0x05} }, +{ 0x0a59, 16, {0x7e, 0x00, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x05, 0x7e, 0x00} }, +{ 0x0a69, 16, {0x12, 0x0d, 0x94, 0x90, 0x7f, 0x96, 0xe0, 0x54, 0xbf, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d} }, +{ 0x0a79, 16, {0x94, 0x90, 0x7f, 0x96, 0xe0, 0x44, 0x40, 0xf0, 0x7f, 0x32, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x22} }, +{ 0x0a89, 16, {0x75, 0x32, 0x01, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x01, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x0e} }, +{ 0x0a99, 16, {0x7d, 0x00, 0x7c, 0x01, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12} }, +{ 0x0aa9, 16, {0x0d, 0xd7, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x02, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x80, 0x7d} }, +{ 0x0ab9, 16, {0x00, 0x7c, 0x80, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0d} }, +{ 0x0ac9, 16, {0xd7, 0xe5, 0x32, 0x60, 0x1b, 0x7f, 0x03, 0x12, 0x0d, 0xd7, 0x7f, 0x00, 0x7e, 0x20, 0x7d, 0x40} }, +{ 0x0ad9, 16, {0x7c, 0x5b, 0x12, 0x06, 0x94, 0xe4, 0x33, 0xf5, 0x32, 0x70, 0x05, 0x7f, 0x0f, 0x12, 0x0d, 0xd7} }, +{ 0x0ae9, 14, {0xe5, 0x32, 0x60, 0x05, 0xe4, 0xff, 0x12, 0x0d, 0xd7, 0xe5, 0x32, 0x24, 0xff, 0x22} }, +{ 0x0af7, 8, {0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x10, 0xd3, 0x22} }, +{ 0x0aff, 1, {0x32} }, +{ 0x0b00, 16, {0x02, 0x0d, 0x64, 0x00, 0x02, 0x0d, 0xab, 0x00, 0x02, 0x0d, 0x2f, 0x00, 0x02, 0x0d, 0x7c, 0x00} }, +{ 0x0b10, 16, {0x02, 0x0d, 0xc1, 0x00, 0x02, 0x0a, 0xff, 0x00, 0x02, 0x0e, 0x4c, 0x00, 0x02, 0x0e, 0x4d, 0x00} }, +{ 0x0b20, 16, {0x02, 0x0e, 0x4e, 0x00, 0x02, 0x0e, 0x4f, 0x00, 0x02, 0x0e, 0x50, 0x00, 0x02, 0x0e, 0x51, 0x00} }, +{ 0x0b30, 16, {0x02, 0x0e, 0x52, 0x00, 0x02, 0x0e, 0x53, 0x00, 0x02, 0x0e, 0x54, 0x00, 0x02, 0x0e, 0x55, 0x00} }, +{ 0x0b40, 16, {0x02, 0x0e, 0x56, 0x00, 0x02, 0x0e, 0x57, 0x00, 0x02, 0x0e, 0x58, 0x00, 0x02, 0x0e, 0x59, 0x00} }, +{ 0x0b50, 8, {0x02, 0x0e, 0x5a, 0x00, 0x02, 0x0e, 0x5b, 0x00} }, +{ 0x0b58, 16, {0xe4, 0xfe, 0x75, 0x2a, 0xff, 0x75, 0x2b, 0x11, 0x75, 0x2c, 0x12, 0xab, 0x2a, 0xaa, 0x2b, 0xa9} }, +{ 0x0b68, 16, {0x2c, 0x90, 0x00, 0x01, 0x12, 0x08, 0x22, 0x64, 0x02, 0x70, 0x2d, 0xad, 0x06, 0x0e, 0xed, 0xb5} }, +{ 0x0b78, 16, {0x07, 0x01, 0x22, 0x90, 0x00, 0x02, 0x12, 0x08, 0x71, 0x85, 0xf0, 0x28, 0xf5, 0x29, 0x62, 0x28} }, +{ 0x0b88, 16, {0xe5, 0x28, 0x62, 0x29, 0xe5, 0x29, 0x62, 0x28, 0x29, 0xfd, 0xe5, 0x28, 0x3a, 0xa9, 0x05, 0x75} }, +{ 0x0b98, 14, {0x2a, 0xff, 0xf5, 0x2b, 0x89, 0x2c, 0x80, 0xc3, 0x7b, 0x00, 0x7a, 0x00, 0x79, 0x00} }, +{ 0x0ba6, 1, {0x22} }, +{ 0x0ba7, 16, {0xab, 0x07, 0xaa, 0x06, 0xac, 0x05, 0xe4, 0xfd, 0xe5, 0x11, 0x60, 0x11, 0xea, 0xff, 0xae, 0x05} }, +{ 0x0bb7, 16, {0x0d, 0xee, 0x24, 0x10, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xef, 0xf0, 0xeb, 0xae, 0x05} }, +{ 0x0bc7, 16, {0x0d, 0x74, 0x10, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xeb, 0xf0, 0xaf, 0x05, 0x0d} }, +{ 0x0bd7, 16, {0x74, 0x10, 0x2f, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xf5, 0x83, 0xec, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f} }, +{ 0x0be7, 13, {0x7b, 0x10, 0x12, 0x0d, 0x10, 0x7f, 0x0a, 0x7e, 0x00, 0x12, 0x0d, 0x94, 0x22} }, +{ 0x0bf4, 16, {0x8e, 0x32, 0x8f, 0x33, 0x8d, 0x34, 0x8a, 0x35, 0x8b, 0x36, 0xe4, 0xfd, 0xf5, 0x37, 0xe5, 0x11} }, +{ 0x0c04, 16, {0x60, 0x12, 0xe5, 0x32, 0xff, 0xae, 0x05, 0x0d, 0xee, 0x24, 0x13, 0xf5, 0x82, 0xe4, 0x34, 0x0f} }, +{ 0x0c14, 16, {0xf5, 0x83, 0xef, 0xf0, 0xe5, 0x33, 0xae, 0x05, 0x0d, 0x74, 0x13, 0x2e, 0xf5, 0x82, 0xe4, 0x34} }, +{ 0x0c24, 16, {0x0f, 0xf5, 0x83, 0xe5, 0x33, 0xf0, 0xaf, 0x0f, 0x7a, 0x0f, 0x7b, 0x13, 0x12, 0x0d, 0x10, 0xaf} }, +{ 0x0c34, 11, {0x0f, 0xad, 0x34, 0xab, 0x36, 0xaa, 0x35, 0x12, 0x0c, 0xf1, 0x22} }, +{ 0x0c3f, 16, {0x8e, 0x32, 0x8f, 0x33, 0x8d, 0x34, 0x8a, 0x35, 0x8b, 0x36, 0xe4, 0xf5, 0x37, 0xe5, 0x37, 0xc3} }, +{ 0x0c4f, 16, {0x95, 0x34, 0x50, 0x20, 0x05, 0x33, 0xe5, 0x33, 0xae, 0x32, 0x70, 0x02, 0x05, 0x32, 0x14, 0xff} }, +{ 0x0c5f, 16, {0xe5, 0x36, 0x25, 0x37, 0xf5, 0x82, 0xe4, 0x35, 0x35, 0xf5, 0x83, 0xe0, 0xfd, 0x12, 0x0b, 0xa7} }, +{ 0x0c6f, 5, {0x05, 0x37, 0x80, 0xd9, 0x22} }, +{ 0x0c74, 16, {0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x25, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} }, +{ 0x0c84, 16, {0x44, 0x01, 0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a} }, +{ 0x0c94, 13, {0x0a, 0x89, 0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x03, 0xd3, 0x22, 0xc3, 0x22} }, +{ 0x0ca1, 16, {0xa9, 0x07, 0xe5, 0x0d, 0x70, 0x23, 0x90, 0x7f, 0xa5, 0xe0, 0x44, 0x80, 0xf0, 0xe9, 0x25, 0xe0} }, +{ 0x0cb1, 16, {0x90, 0x7f, 0xa6, 0xf0, 0x8d, 0x08, 0xaf, 0x03, 0xa9, 0x07, 0x75, 0x09, 0x01, 0x8a, 0x0a, 0x89} }, +{ 0x0cc1, 11, {0x0b, 0xe4, 0xf5, 0x0c, 0x75, 0x0d, 0x01, 0xd3, 0x22, 0xc3, 0x22} }, +{ 0x0ccc, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfb, 0xf0, 0xe0, 0x44, 0x08, 0xf0, 0x30, 0x06, 0x04, 0xe0, 0x44} }, +{ 0x0cdc, 16, {0x02, 0xf0, 0x7f, 0xd0, 0x7e, 0x07, 0x12, 0x0d, 0x94, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xf7, 0xf0} }, +{ 0x0cec, 5, {0xe0, 0x44, 0x04, 0xf0, 0x22} }, +{ 0x0cf1, 16, {0x12, 0x0c, 0x74, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} }, +{ 0x0d01, 15, {0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} }, +{ 0x0d10, 16, {0x12, 0x0c, 0xa1, 0xe5, 0x0d, 0x24, 0xfa, 0x60, 0x10, 0x14, 0x60, 0x07, 0x24, 0x07, 0x70, 0xf3} }, +{ 0x0d20, 15, {0x7f, 0x08, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x07, 0x22, 0xe4, 0xf5, 0x0d, 0x7f, 0x06, 0x22} }, +{ 0x0d2f, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x90, 0x7f, 0xc4, 0xe4, 0xf0, 0x53, 0x91, 0xef, 0x90, 0x7f} }, +{ 0x0d3f, 11, {0xab, 0x74, 0x04, 0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d4a, 16, {0x90, 0x7f, 0xd6, 0xe0, 0x30, 0xe7, 0x12, 0xe0, 0x44, 0x01, 0xf0, 0x7f, 0x14, 0x7e, 0x00, 0x12} }, +{ 0x0d5a, 10, {0x0d, 0x94, 0x90, 0x7f, 0xd6, 0xe0, 0x54, 0xfe, 0xf0, 0x22} }, +{ 0x0d64, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x01, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x01} }, +{ 0x0d74, 8, {0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d7c, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0xd2, 0x03, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x08} }, +{ 0x0d8c, 8, {0xf0, 0xd0, 0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0d94, 16, {0x8e, 0x38, 0x8f, 0x39, 0xe5, 0x39, 0x15, 0x39, 0xae, 0x38, 0x70, 0x02, 0x15, 0x38, 0x4e, 0x60} }, +{ 0x0da4, 7, {0x05, 0x12, 0x0e, 0x00, 0x80, 0xee, 0x22} }, +{ 0x0dab, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x02, 0xf0, 0xd0} }, +{ 0x0dbb, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0dc1, 16, {0xc0, 0xe0, 0xc0, 0x83, 0xc0, 0x82, 0x53, 0x91, 0xef, 0x90, 0x7f, 0xab, 0x74, 0x10, 0xf0, 0xd0} }, +{ 0x0dd1, 6, {0x82, 0xd0, 0x83, 0xd0, 0xe0, 0x32} }, +{ 0x0dd7, 16, {0xae, 0x07, 0x7f, 0x21, 0x7d, 0x01, 0x74, 0x00, 0x2e, 0xf5, 0x82, 0xe4, 0x34, 0x0f, 0xab, 0x82} }, +{ 0x0de7, 5, {0xfa, 0x12, 0x0d, 0x10, 0x22} }, +{ 0x0dec, 16, {0x50, 0x0f, 0x00, 0xc0, 0xf9, 0xa4, 0xb0, 0x99, 0x92, 0x82, 0xf8, 0x80, 0x98, 0x88, 0x83, 0xc6} }, +{ 0x0dfc, 3, {0xa1, 0x86, 0x8e} }, +{ 0x0dff, 1, {0x00} }, +{ 0x0e00, 16, {0x74, 0x00, 0xf5, 0x86, 0x90, 0xfd, 0xa5, 0x7c, 0x05, 0xa3, 0xe5, 0x82, 0x45, 0x83, 0x70, 0xf9} }, +{ 0x0e10, 1, {0x22} }, +{ 0x0e11, 14, {0x90, 0x7f, 0x00, 0xe5, 0x10, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} }, +{ 0x0e1f, 14, {0x90, 0x7f, 0x00, 0xe5, 0x0e, 0xf0, 0x90, 0x7f, 0xb5, 0x74, 0x01, 0xf0, 0xd3, 0x22} }, +{ 0x0e2d, 8, {0x90, 0x7f, 0xea, 0xe0, 0xf5, 0x0e, 0xd3, 0x22} }, +{ 0x0e35, 8, {0xe4, 0xf5, 0x0d, 0xd2, 0xe9, 0xd2, 0xaf, 0x22} }, +{ 0x0e3d, 3, {0xd2, 0x00, 0x22} }, +{ 0x0e40, 2, {0xd3, 0x22} }, +{ 0x0e42, 2, {0xd3, 0x22} }, +{ 0x0e44, 2, {0xd3, 0x22} }, +{ 0x0e46, 2, {0xd3, 0x22} }, +{ 0x0e48, 2, {0xd3, 0x22} }, +{ 0x0e4a, 2, {0xd3, 0x22} }, +{ 0x0e4c, 1, {0x32} }, +{ 0x0e4d, 1, {0x32} }, +{ 0x0e4e, 1, {0x32} }, +{ 0x0e4f, 1, {0x32} }, +{ 0x0e50, 1, {0x32} }, +{ 0x0e51, 1, {0x32} }, +{ 0x0e52, 1, {0x32} }, +{ 0x0e53, 1, {0x32} }, +{ 0x0e54, 1, {0x32} }, +{ 0x0e55, 1, {0x32} }, +{ 0x0e56, 1, {0x32} }, +{ 0x0e57, 1, {0x32} }, +{ 0x0e58, 1, {0x32} }, +{ 0x0e59, 1, {0x32} }, +{ 0x0e5a, 1, {0x32} }, +{ 0x0e5b, 1, {0x32} }, +{ 0x1100, 16, {0x12, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x40, 0x47, 0x05, 0x10, 0x27, 0x01, 0x00, 0x01, 0x02} }, +{ 0x1110, 16, {0x00, 0x01, 0x09, 0x02, 0x20, 0x00, 0x01, 0x01, 0x03, 0xa0, 0x00, 0x09, 0x04, 0x00, 0x00, 0x02} }, +{ 0x1120, 16, {0xff, 0x00, 0x00, 0x04, 0x07, 0x05, 0x82, 0x02, 0x40, 0x00, 0x00, 0x07, 0x05, 0x02, 0x02, 0x40} }, +{ 0x1130, 16, {0x00, 0x00, 0x04, 0x03, 0x09, 0x04, 0x26, 0x03, 0x41, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x68, 0x00} }, +{ 0x1140, 16, {0x6f, 0x00, 0x72, 0x00, 0x20, 0x00, 0x43, 0x00, 0x68, 0x00, 0x69, 0x00, 0x70, 0x00, 0x73, 0x00} }, +{ 0x1150, 16, {0x2c, 0x00, 0x20, 0x00, 0x49, 0x00, 0x6e, 0x00, 0x63, 0x00, 0x2e, 0x00, 0x28, 0x03, 0x46, 0x00} }, +{ 0x1160, 16, {0x69, 0x00, 0x72, 0x00, 0x6d, 0x00, 0x77, 0x00, 0x61, 0x00, 0x72, 0x00, 0x65, 0x00, 0x20, 0x00} }, +{ 0x1170, 16, {0x46, 0x00, 0x72, 0x00, 0x61, 0x00, 0x6d, 0x00, 0x65, 0x00, 0x57, 0x00, 0x6f, 0x00, 0x72, 0x00} }, +{ 0x1180, 16, {0x6b, 0x00, 0x73, 0x00, 0x2a, 0x03, 0x43, 0x00, 0x6f, 0x00, 0x6e, 0x00, 0x66, 0x00, 0x69, 0x00} }, +{ 0x1190, 16, {0x67, 0x00, 0x75, 0x00, 0x72, 0x00, 0x61, 0x00, 0x74, 0x00, 0x69, 0x00, 0x6f, 0x00, 0x6e, 0x00} }, +{ 0x11a0, 16, {0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00, 0x22, 0x03} }, +{ 0x11b0, 16, {0x49, 0x00, 0x6e, 0x00, 0x74, 0x00, 0x65, 0x00, 0x72, 0x00, 0x66, 0x00, 0x61, 0x00, 0x63, 0x00} }, +{ 0x11c0, 16, {0x65, 0x00, 0x20, 0x00, 0x53, 0x00, 0x74, 0x00, 0x72, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x67, 0x00} }, +{ 0x11d0, 2, {0x00, 0x00} }, +{ 0xffff, 0, {0x00} } +}; +#endif diff --git a/drivers/usb/stopusb b/drivers/usb/stopusb deleted file mode 100644 index 1dc46980b537..000000000000 --- a/drivers/usb/stopusb +++ /dev/null @@ -1,7 +0,0 @@ -#!/bin/sh - -killall ohci-control - -sleep 2 - -rmmod usb-ohci diff --git a/drivers/usb/uhci-debug.c b/drivers/usb/uhci-debug.c deleted file mode 100644 index fd2aba6da44a..000000000000 --- a/drivers/usb/uhci-debug.c +++ /dev/null @@ -1,168 +0,0 @@ -/* - * UHCI-specific debugging code. Invaluable when something - * goes wrong, but don't get in my face. - * - * (C) Copyright 1999 Linus Torvalds - */ - -#include -#include - -#include "uhci.h" - -void show_td(struct uhci_td * td) -{ - printk("%08x ", td->link); - printk("%se%d %s%s%s%s%s%s%s%s%s%sLength=%x ", - ((td->status >> 29) & 1) ? "SPD " : "", - ((td->status >> 27) & 3), - ((td->status >> 26) & 1) ? "LS " : "", - ((td->status >> 25) & 1) ? "IOS " : "", - ((td->status >> 24) & 1) ? "IOC " : "", - ((td->status >> 23) & 1) ? "Active " : "", - ((td->status >> 22) & 1) ? "Stalled " : "", - ((td->status >> 21) & 1) ? "DataBufErr " : "", - ((td->status >> 20) & 1) ? "Babble " : "", - ((td->status >> 19) & 1) ? "NAK " : "", - ((td->status >> 18) & 1) ? "CRC/Timeo " : "", - ((td->status >> 17) & 1) ? "BitStuff " : "", - td->status & 0x7ff); - printk("MaxLen=%x %sEndPt=%x Dev=%x, PID=%x ", - td->info >> 21, - ((td->info >> 19) & 1) ? "DT " : "", - (td->info >> 15) & 15, - (td->info >> 8) & 127, - td->info & 0xff); - printk("(buf=%08x)\n", td->buffer); -} - -static void show_sc(int port, unsigned short status) -{ - printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n", - port, - status, - (status & (1 << 12)) ? " PortSuspend" : "", - (status & (1 << 9)) ? " PortReset" : "", - (status & (1 << 8)) ? " LowSpeed" : "", - (status & 0x40) ? " ResumeDetect" : "", - (status & 0x08) ? " EnableChange" : "", - (status & 0x04) ? " PortEnabled" : "", - (status & 0x02) ? " ConnectChange" : "", - (status & 0x01) ? " PortConnected" : ""); -} - -void show_status(struct uhci *uhci) -{ - unsigned int io_addr = uhci->io_addr; - unsigned short usbcmd, usbstat, usbint, usbfrnum; - unsigned int flbaseadd; - unsigned char sof; - unsigned short portsc1, portsc2; - - usbcmd = inw(io_addr + 0); - usbstat = inw(io_addr + 2); - usbint = inw(io_addr + 4); - usbfrnum = inw(io_addr + 6); - flbaseadd = inl(io_addr + 8); - sof = inb(io_addr + 12); - portsc1 = inw(io_addr + 16); - portsc2 = inw(io_addr + 18); - - printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n", - usbcmd, - (usbcmd & 0x80) ? " Maxp64" : " Maxp32", - (usbcmd & 0x40) ? " CF" : "", - (usbcmd & 0x20) ? " SWDBG" : "", - (usbcmd & 0x10) ? " FGR" : "", - (usbcmd & 0x08) ? " EGSM" : "", - (usbcmd & 0x04) ? " GRESET" : "", - (usbcmd & 0x02) ? " HCRESET" : "", - (usbcmd & 0x01) ? " RS" : ""); - - printk(" usbstat = %04x %s%s%s%s%s%s\n", - usbstat, - (usbstat & 0x20) ? " HCHalted" : "", - (usbstat & 0x10) ? " HostControllerProcessError" : "", - (usbstat & 0x08) ? " HostSystemError" : "", - (usbstat & 0x04) ? " ResumeDetect" : "", - (usbstat & 0x02) ? " USBError" : "", - (usbstat & 0x01) ? " USBINT" : ""); - - printk(" usbint = %04x\n", usbint); - printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, 0xfff & (4*(unsigned int)usbfrnum)); - printk(" flbaseadd = %08x\n", flbaseadd); - printk(" sof = %02x\n", sof); - show_sc(1, portsc1); - show_sc(2, portsc2); -} - -#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x)) - -struct uhci_td * uhci_link_to_td(unsigned int link) -{ - if (link & 1) - return NULL; - - return bus_to_virt(link & ~15); -} - -void show_queue(struct uhci_qh *qh) -{ - struct uhci_td *td; - int i = 0; - -#if 0 - printk(" link = %p, element = %p\n", qh->link, qh->element); -#endif - if(!qh->element) { - printk(" td 0 = NULL\n"); - return; - } - - for(td = uhci_link_to_td(qh->element); td; - td = uhci_link_to_td(td->link)) { - printk(" td %d = %p\n", i++, td); - printk(" "); - show_td(td); - } -} - -int is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) -{ - int j; - - for (j = 0; j < UHCI_MAXQH; j++) - if (qh == uhci->root_hub->qh + j) - return 1; - - return 0; -} - -static const char *qh_names[] = {"isochronous", "interrupt2", "interrupt4", - "interrupt8", "interrupt16", "interrupt32", - "interrupt64", "interrupt128", "interrupt256", - "control", "bulk0", "bulk1", "bulk2", "bulk3", - "unused", "unused"}; - -void show_queues(struct uhci *uhci) -{ - int i; - struct uhci_qh *qh; - - for (i = 0; i < UHCI_MAXQH; ++i) { - printk(" %s:\n", qh_names[i]); -#if 0 - printk(" qh #%d, %p\n", i, virt_to_bus(uhci->root_hub->qh + i)); - show_queue(uhci->root_hub->qh + i); -#endif - - qh = uhci_link_to_qh(uhci->root_hub->qh[i].link); - for (; qh; qh = uhci_link_to_qh(qh->link)) { - if (is_skeleton_qh(uhci, qh)) - break; - - show_queue(qh); - } - } -} - diff --git a/drivers/usb/uhci-debug.h b/drivers/usb/uhci-debug.h new file mode 100644 index 000000000000..12ebbb84461b --- /dev/null +++ b/drivers/usb/uhci-debug.h @@ -0,0 +1,316 @@ +/* + * UHCI-specific debugging code. Invaluable when something + * goes wrong, but don't get in my face. + * + * Kernel visible pointers are surrounded in []'s and bus + * visible pointers are surrounded in ()'s + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + */ + +#include +#include + +#include "uhci.h" + +void uhci_show_td(struct uhci_td *td) +{ + char *spid; + + printk("%08x ", td->link); + printk("e%d %s%s%s%s%s%s%s%s%s%sLength=%x ", + ((td->status >> 27) & 3), + (td->status & TD_CTRL_SPD) ? "SPD " : "", + (td->status & TD_CTRL_LS) ? "LS " : "", + (td->status & TD_CTRL_IOC) ? "IOC " : "", + (td->status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->status & TD_CTRL_NAK) ? "NAK " : "", + (td->status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->status & TD_CTRL_BITSTUFF) ? "BitStuff " : "", + td->status & 0x7ff); + + switch (td->info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = "OUT"; + break; + case USB_PID_IN: + spid = "IN"; + break; + default: + spid = "?"; + break; + } + + printk("MaxLen=%x DT%d EndPt=%x Dev=%x, PID=%x(%s) ", + td->info >> 21, + ((td->info >> 19) & 1), + (td->info >> 15) & 15, + (td->info >> 8) & 127, + (td->info & 0xff), + spid); + printk("(buf=%08x)\n", td->buffer); +} + +static void uhci_show_sc(int port, unsigned short status) +{ + printk(" stat%d = %04x %s%s%s%s%s%s%s%s\n", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} + +void uhci_show_status(struct uhci *uhci) +{ + unsigned int io_addr = uhci->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw(io_addr + 0); + usbstat = inw(io_addr + 2); + usbint = inw(io_addr + 4); + usbfrnum = inw(io_addr + 6); + flbaseadd = inl(io_addr + 8); + sof = inb(io_addr + 12); + portsc1 = inw(io_addr + 16); + portsc2 = inw(io_addr + 18); + + printk(" usbcmd = %04x %s%s%s%s%s%s%s%s\n", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + printk(" usbstat = %04x %s%s%s%s%s%s\n", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + printk(" usbint = %04x\n", usbint); + printk(" usbfrnum = (%d)%03x\n", (usbfrnum >> 10) & 1, + 0xfff & (4*(unsigned int)usbfrnum)); + printk(" flbaseadd = %08x\n", flbaseadd); + printk(" sof = %02x\n", sof); + uhci_show_sc(1, portsc1); + uhci_show_sc(2, portsc2); +} + +#define uhci_link_to_qh(x) ((struct uhci_qh *) uhci_link_to_td(x)) + +struct uhci_td *uhci_link_to_td(unsigned int link) +{ + if (link & UHCI_PTR_TERM) + return NULL; + + return bus_to_virt(link & ~UHCI_PTR_BITS); +} + +void uhci_show_urb_queue(struct urb *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct list_head *head, *tmp; + int i, checked = 0, prevactive = 0; + + printk(" URB [%p] urbp [%p]\n", urb, urbp); + + if (urbp->qh) + printk(" QH [%p]\n", urbp->qh); + else + printk(" QH [%p] element (%08x) link (%08x)\n", urbp->qh, + urbp->qh->element, urbp->qh->link); + + i = 0; + + head = &urbp->list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + printk(" td %d: [%p]\n", i++, td); + printk(" "); + uhci_show_td(td); + + if (i > 10 && !checked && prevactive && tmp != head) { + struct list_head *ntmp = tmp; + struct uhci_td *ntd = td; + int active = 1, ni = i; + + checked = 1; + + while (ntmp != head && ntmp->next != head && active) { + ntd = list_entry(ntmp, struct uhci_td, list); + + ntmp = ntmp->next; + + active = ntd->status & TD_CTRL_ACTIVE; + + ni++; + } + + if (active && ni > i) { + printk(" [skipped %d active TD's]\n", ni - i); + tmp = ntmp; + td = ntd; + i = ni; + } + } + + prevactive = td->status & TD_CTRL_ACTIVE; + } +} + +void uhci_show_queue(struct uhci_qh *qh) +{ + struct uhci_td *td, *first; + int i = 0, count = 1000; + + if (qh->element & UHCI_PTR_QH) + printk(" Element points to QH (bug?)\n"); + + if (qh->element & UHCI_PTR_DEPTH) + printk(" Depth traverse\n"); + + if (qh->element & UHCI_PTR_TERM) + printk(" Terminate\n"); + + if (!(qh->element & ~UHCI_PTR_BITS)) { + printk(" td 0: [NULL]\n"); + return; + } + + first = uhci_link_to_td(qh->element); + + /* Make sure it doesn't runaway */ + for (td = first; td && count > 0; + td = uhci_link_to_td(td->link), --count) { + printk(" td %d: [%p]\n", i++, td); + printk(" "); + uhci_show_td(td); + + if (td == uhci_link_to_td(td->link)) { + printk(KERN_ERR "td links to itself!\n"); + break; + } + } +} + +static int uhci_is_skeleton_td(struct uhci *uhci, struct uhci_td *td) +{ + int j; + + for (j = 0; j < UHCI_NUM_SKELTD; j++) + if (td == uhci->skeltd + j) + return 1; + + return 0; +} + +static int uhci_is_skeleton_qh(struct uhci *uhci, struct uhci_qh *qh) +{ + int j; + + for (j = 0; j < UHCI_NUM_SKELQH; j++) + if (qh == uhci->skelqh + j) + return 1; + + return 0; +} + +static const char *td_names[] = {"interrupt1", "interrupt2", "interrupt4", + "interrupt8", "interrupt16", "interrupt32", + "interrupt64", "interrupt128", "interrupt256" }; +static const char *qh_names[] = { "control", "bulk" }; + +void uhci_show_queues(struct uhci *uhci) +{ + int i, isqh = 0; + struct uhci_qh *qh; + struct uhci_td *td; + + for (i = 0; i < UHCI_NUMFRAMES; ++i) { + int shown = 0; + + td = uhci_link_to_td(uhci->fl->frame[i]); + if (td) + isqh = uhci->fl->frame[i] & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + if (!shown) { + printk(" Frame %d\n", i); + shown = 1; + } + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELTD; ++i) { + printk(" %s: [%p] (%08x)\n", td_names[i], + &uhci->skeltd[i], + uhci->skeltd[i].link); + + td = uhci_link_to_td(uhci->skeltd[i].link); + if (td) + isqh = uhci->skeltd[i].link & UHCI_PTR_QH; + while (td && !isqh) { + if (uhci_is_skeleton_td(uhci, td)) + break; + + printk("[%p] ", td); + + uhci_show_td(td); + td = uhci_link_to_td(td->link); + if (td) + isqh = td->link & UHCI_PTR_QH; + } + } + for (i = 0; i < UHCI_NUM_SKELQH; ++i) { + printk(" %s: [%p] (%08x) (%08x)\n", qh_names[i], + &uhci->skelqh[i], + uhci->skelqh[i].link, uhci->skelqh[i].element); + + qh = uhci_link_to_qh(uhci->skelqh[i].link); + for (; qh; qh = uhci_link_to_qh(qh->link)) { + if (uhci_is_skeleton_qh(uhci, qh)) + break; + + printk(" [%p] (%08x) (%08x)\n", + qh, qh->link, qh->element); + + uhci_show_queue(qh); + } + } +} + diff --git a/drivers/usb/uhci.c b/drivers/usb/uhci.c index 3d8ccdb7d9d9..a1e0acfd3cf4 100644 --- a/drivers/usb/uhci.c +++ b/drivers/usb/uhci.c @@ -2,15 +2,18 @@ * Universal Host Controller Interface driver for USB. * * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999-2000 Johannes Erdfelt, jerdfelt@sventech.com + * (C) Copyright 1999 Randy Dunlap + * (C) Copyright 1999 Georg Acher, acher@in.tum.de + * (C) Copyright 1999 Deti Fliegl, deti@fliegl.de + * (C) Copyright 1999 Thomas Sailer, sailer@ife.ee.ethz.ch + * (C) Copyright 1999 Roman Weissgaerber, weissg@vienna.at * * Intel documents this fairly well, and as far as I know there * are no royalties or anything like that, but even so there are * people who decided that they want to do the same thing in a * completely different way. * - * Oh, well. The intel version is the more common by far. As such, - * that's the one I care about right now. - * * WARNING! The USB documentation is downright evil. Most of it * is just crap, written by a committee. You're better off ignoring * most of it, the important stuff is: @@ -18,9 +21,6 @@ * - working around the horridness of the rest */ -/* 4/4/1999 added data toggle for interrupt pipes -keryan */ - -#include #include #include #include @@ -30,726 +30,1934 @@ #include #include #include +#include +#include +#include +//#define DEBUG +#include -#include +#include #include #include #include #include "uhci.h" -#include "inits.h" +#include "uhci-debug.h" -#ifdef CONFIG_APM -#include -static int handle_apm_event(apm_event_t event); -static int apm_resume = 0; -#endif +#include +static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data); + +static int debug = 1; +MODULE_PARM(debug, "i"); -#define compile_assert(x) do { switch (0) { case 1: case !(x): } } while (0) +static kmem_cache_t *uhci_td_cachep; +static kmem_cache_t *uhci_qh_cachep; +static kmem_cache_t *uhci_up_cachep; /* urb_priv */ -static struct wait_queue *uhci_configure = NULL; +static LIST_HEAD(uhci_list); + +static int rh_submit_urb(struct urb *urb); +static int rh_unlink_urb(struct urb *urb); +static int uhci_get_current_frame_number(struct usb_device *dev); +static int uhci_unlink_generic(struct urb *urb); +static int uhci_unlink_urb(struct urb *urb); + +#define min(a,b) (((a)<(b))?(a):(b)) + +/* If a transfer is still active after this much time, turn off FSBR */ +#define IDLE_TIMEOUT (HZ / 20) /* 50 ms */ /* - * Return the result of a TD.. + * Only the USB core should call uhci_alloc_dev and uhci_free_dev */ -static int uhci_td_result(struct uhci_device *dev, struct uhci_td *td) +static int uhci_alloc_dev(struct usb_device *dev) { - unsigned int status; + return 0; +} - status = (td->status >> 16) & 0xff; +static int uhci_free_dev(struct usb_device *dev) +{ + struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; + struct list_head *tmp, *head = &uhci->urb_list; + unsigned long flags; - /* Some debugging code */ - if (status) { - int i = 10; - struct uhci_td *tmp = dev->control_td; - printk("uhci_td_result() failed with status %d\n", status); - show_status(dev->uhci); - do { - show_td(tmp); - tmp++; - if (!--i) - break; - } while (tmp <= td); + /* Walk through the entire URB list and forcefully remove any */ + /* URBs that are still active for that device */ + nested_lock(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + if (u->dev == dev) + uhci_unlink_urb(u); } - return status; + nested_unlock(&uhci->urblist_lock, flags); + + return 0; } -/* - * Inserts a td into qh list at the top. - * - * Careful about atomicity: even on UP this - * requires a locked access due to the concurrent - * DMA engine. - * - * NOTE! This assumes that first->last is a valid - * list of TD's with the proper backpointers set - * up and all.. - */ -static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct uhci_td *first, struct uhci_td *last) +static void uhci_add_urb_list(struct uhci *uhci, struct urb *urb) { - unsigned int link = qh->element; - unsigned int new = 4 | virt_to_bus(first); + unsigned long flags; - for (;;) { - unsigned char success; - - last->link = link; - first->backptr = &qh->element; - asm volatile("lock ; cmpxchg %4,%2 ; sete %0" - :"=q" (success), "=a" (link) - :"m" (qh->element), "1" (link), "r" (new) - :"memory"); - if (success) { - /* Was there a successor entry? Fix it's backpointer.. */ - if ((link & 1) == 0) { - struct uhci_td *next = bus_to_virt(link & ~15); - next->backptr = &last->link; - } - break; - } + nested_lock(&uhci->urblist_lock, flags); + list_add(&urb->urb_list, &uhci->urb_list); + nested_unlock(&uhci->urblist_lock, flags); +} + +static void uhci_remove_urb_list(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + + nested_lock(&uhci->urblist_lock, flags); + if (!list_empty(&urb->urb_list)) { + list_del(&urb->urb_list); + INIT_LIST_HEAD(&urb->urb_list); } + nested_unlock(&uhci->urblist_lock, flags); } -static inline void uhci_insert_td_in_qh(struct uhci_qh *qh, struct uhci_td *td) +void uhci_set_next_interrupt(struct uhci *uhci) { - uhci_insert_tds_in_qh(qh, td, td); + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + uhci->skel_term_td.status |= TD_CTRL_IOC; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); } -static void uhci_insert_qh(struct uhci_qh *qh, struct uhci_qh *newqh) +void uhci_clear_next_interrupt(struct uhci *uhci) { - newqh->link = qh->link; - qh->link = virt_to_bus(newqh) | 2; + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + uhci->skel_term_td.status &= ~TD_CTRL_IOC; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); } -static void uhci_remove_qh(struct uhci_qh *qh, struct uhci_qh *remqh) +static struct uhci_td *uhci_alloc_td(struct usb_device *dev) { - unsigned int remphys = virt_to_bus(remqh); - struct uhci_qh *lqh = qh; + struct uhci_td *td; - while ((lqh->link & ~0xF) != remphys) { - if (lqh->link & 1) - break; + td = kmem_cache_alloc(uhci_td_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!td) + return NULL; + + td->link = UHCI_PTR_TERM; + td->buffer = 0; + + td->frameptr = NULL; + td->nexttd = td->prevtd = NULL; + td->dev = dev; + INIT_LIST_HEAD(&td->list); + + usb_inc_dev_use(dev); + + return td; +} + +static void inline uhci_fill_td(struct uhci_td *td, __u32 status, + __u32 info, __u32 buffer) +{ + td->status = status; + td->info = info; + td->buffer = buffer; +} + +static void uhci_insert_td(struct uhci *uhci, struct uhci_td *skeltd, struct uhci_td *td) +{ + unsigned long flags; + + spin_lock_irqsave(&uhci->framelist_lock, flags); + + /* Fix the linked list pointers */ + td->nexttd = skeltd->nexttd; + td->prevtd = skeltd; + if (skeltd->nexttd) + skeltd->nexttd->prevtd = td; + skeltd->nexttd = td; + + td->link = skeltd->link; + skeltd->link = virt_to_bus(td); + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +/* + * We insert Isochronous transfers directly into the frame list at the + * beginning + * The layout looks as follows: + * frame list pointer -> iso td's (if any) -> + * periodic interrupt td (if frame 0) -> irq td's -> control qh -> bulk qh + */ + +static void uhci_insert_td_frame_list(struct uhci *uhci, struct uhci_td *td, unsigned framenum) +{ + unsigned long flags; + struct uhci_td *nexttd; + + framenum %= UHCI_NUMFRAMES; - lqh = bus_to_virt(lqh->link & ~0xF); + spin_lock_irqsave(&uhci->framelist_lock, flags); + + td->frameptr = &uhci->fl->frame[framenum]; + td->link = uhci->fl->frame[framenum]; + if (!(td->link & (UHCI_PTR_TERM | UHCI_PTR_QH))) { + nexttd = (struct uhci_td *)uhci_ptr_to_virt(td->link); + td->nexttd = nexttd; + nexttd->prevtd = td; + nexttd->frameptr = NULL; } + uhci->fl->frame[framenum] = virt_to_bus(td); + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_remove_td(struct uhci *uhci, struct uhci_td *td) +{ + unsigned long flags; - if (lqh->link & 1) { - printk("couldn't find qh in chain!\n"); + /* If it's not inserted, don't remove it */ + if (!td->frameptr && !td->prevtd && !td->nexttd) return; - } - lqh->link = remqh->link; + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (td->frameptr) { + *(td->frameptr) = td->link; + if (td->nexttd) { + td->nexttd->frameptr = td->frameptr; + td->nexttd->prevtd = NULL; + td->nexttd = NULL; + } + td->frameptr = NULL; + } else { + if (td->prevtd) { + td->prevtd->nexttd = td->nexttd; + td->prevtd->link = td->link; + } + if (td->nexttd) + td->nexttd->prevtd = td->prevtd; + td->prevtd = td->nexttd = NULL; + } + td->link = UHCI_PTR_TERM; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); } /* - * Removes td from qh if present. - * - * NOTE! We keep track of both forward and back-pointers, - * so this should be trivial, right? - * - * Wrong. While all TD insert/remove operations are synchronous - * on the CPU, the UHCI controller can (and does) play with the - * very first forward pointer. So we need to validate the backptr - * before we change it, so that we don't by mistake reset the QH - * head to something old. + * Inserts a td into qh list at the top. */ -static void uhci_remove_td(struct uhci_td *td) +static void uhci_insert_tds_in_qh(struct uhci_qh *qh, struct urb *urb, int breadth) { - unsigned int *backptr = td->backptr; - unsigned int link = td->link; - unsigned int me; + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td, *prevtd; + + if (!urbp) + return; - if (!backptr) + head = &urbp->list; + tmp = head->next; + if (head == tmp) return; - td->backptr = NULL; + td = list_entry(tmp, struct uhci_td, list); - /* - * This is the easy case: the UHCI will never change "td->link", - * so we can always just look at that and fix up the backpointer - * of any next element.. - */ - if (!(link & 1)) { - struct uhci_td *next = bus_to_virt(link & ~15); - next->backptr = backptr; + /* Add the first TD to the QH element pointer */ + qh->element = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH); + + prevtd = td; + + /* Then link the rest of the TD's */ + tmp = tmp->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + prevtd->link = virt_to_bus(td) | (breadth ? 0 : UHCI_PTR_DEPTH); + + prevtd = td; } - /* - * The nasty case is "backptr->next", which we need to - * update to "link" _only_ if "backptr" still points - * to us (it may not: maybe backptr is a QH->element - * pointer and the UHCI has changed the value). - */ - me = virt_to_bus(td) | (0xe & *backptr); - asm volatile("lock ; cmpxchg %0,%1" - : - :"r" (link), "m" (*backptr), "a" (me) - :"memory"); + prevtd->link = UHCI_PTR_TERM; +} + +static void uhci_free_td(struct uhci_td *td) +{ + if (!list_empty(&td->list)) + dbg("td is still in URB list!"); + + if (td->dev) + usb_dec_dev_use(td->dev); + + kmem_cache_free(uhci_td_cachep, td); } -static struct uhci_qh *uhci_qh_allocate(struct uhci_device *dev) +static struct uhci_qh *uhci_alloc_qh(struct usb_device *dev) { struct uhci_qh *qh; - int inuse; - qh = dev->qh; - for (; (inuse = test_and_set_bit(0, &qh->inuse)) != 0 && qh < &dev->qh[UHCI_MAXQH]; qh++) - ; + qh = kmem_cache_alloc(uhci_qh_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!qh) + return NULL; + + qh->element = UHCI_PTR_TERM; + qh->link = UHCI_PTR_TERM; + + qh->dev = dev; + qh->prevqh = qh->nextqh = NULL; - if (!inuse) - return(qh); + INIT_LIST_HEAD(&qh->remove_list); - printk("ran out of qh's for dev %p\n", dev); - return(NULL); + usb_inc_dev_use(dev); + + return qh; } -static void uhci_qh_deallocate(struct uhci_qh *qh) +static void uhci_free_qh(struct uhci_qh *qh) { - if (qh->element != 1) - printk("qh %p leaving dangling entries? (%X)\n", qh, qh->element); - - qh->element = 1; - qh->link = 1; + if (qh->dev) + usb_dec_dev_use(qh->dev); - clear_bit(0, &qh->inuse); + kmem_cache_free(uhci_qh_cachep, qh); } -static struct uhci_td *uhci_td_allocate(struct uhci_device *dev) +static void uhci_insert_qh(struct uhci *uhci, struct uhci_qh *skelqh, struct uhci_qh *qh) { - struct uhci_td *td; - int inuse; + unsigned long flags; - td = dev->td; - for (; (inuse = test_and_set_bit(0, &td->inuse)) != 0 && td < &dev->td[UHCI_MAXTD]; td++) - ; + spin_lock_irqsave(&uhci->framelist_lock, flags); - if (!inuse) - return(td); + /* Fix the linked list pointers */ + qh->nextqh = skelqh->nextqh; + qh->prevqh = skelqh; + if (skelqh->nextqh) + skelqh->nextqh->prevqh = qh; + skelqh->nextqh = qh; - printk("ran out of td's for dev %p\n", dev); - return(NULL); + qh->link = skelqh->link; + skelqh->link = virt_to_bus(qh) | UHCI_PTR_QH; + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); } -/* - * This MUST only be called when it has been removed from a QH already (or - * the QH has been removed from the skeleton - */ -static void uhci_td_deallocate(struct uhci_td *td) +static void uhci_remove_qh(struct uhci *uhci, struct uhci_qh *qh) { - td->link = 1; + unsigned long flags; + int delayed; - clear_bit(0, &td->inuse); + /* If the QH isn't queued, then we don't need to delay unlink it */ + delayed = (qh->prevqh || qh->nextqh); + + spin_lock_irqsave(&uhci->framelist_lock, flags); + if (qh->prevqh) { + qh->prevqh->nextqh = qh->nextqh; + qh->prevqh->link = qh->link; + } + if (qh->nextqh) + qh->nextqh->prevqh = qh->prevqh; + qh->prevqh = qh->nextqh = NULL; + qh->element = qh->link = UHCI_PTR_TERM; + spin_unlock_irqrestore(&uhci->framelist_lock, flags); + + if (delayed) { + spin_lock_irqsave(&uhci->qh_remove_lock, flags); + + /* Check to see if the remove list is empty */ + /* Set the IOC bit to force an interrupt so we can remove the QH */ + if (list_empty(&uhci->qh_remove_list)) + uhci_set_next_interrupt(uhci); + + /* Add it */ + list_add(&qh->remove_list, &uhci->qh_remove_list); + + spin_unlock_irqrestore(&uhci->qh_remove_lock, flags); + } else + uhci_free_qh(qh); } -/* - * UHCI interrupt list operations.. - */ -static spinlock_t irqlist_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t uhci_append_urb_lock = SPIN_LOCK_UNLOCKED; -static void uhci_add_irq_list(struct uhci *uhci, struct uhci_td *td, usb_device_irq completed, void *dev_id) +/* This function will append one URB's QH to another URB's QH. This is for */ +/* USB_QUEUE_BULK support */ +static void uhci_append_queued_urb(struct uhci *uhci, struct urb *eurb, struct urb *urb) { + struct urb_priv *eurbp, *urbp, *furbp, *lurbp; + struct list_head *tmp; + struct uhci_td *td, *ltd; unsigned long flags; - td->completed = completed; - td->dev_id = dev_id; + eurbp = eurb->hcpriv; + urbp = urb->hcpriv; + + spin_lock_irqsave(&uhci_append_urb_lock, flags); + + /* Find the beginning URB in the queue */ + if (eurbp->queued) { + struct list_head *head = &eurbp->urb_queue_list; + + tmp = head->next; + while (tmp != head) { + struct urb_priv *turbp = + list_entry(tmp, struct urb_priv, urb_queue_list); + + tmp = tmp->next; + + if (!turbp->queued) + break; + } + } else + tmp = &eurbp->urb_queue_list; + + furbp = list_entry(tmp, struct urb_priv, urb_queue_list); + + tmp = furbp->urb_queue_list.prev; + lurbp = list_entry(tmp, struct urb_priv, urb_queue_list); + + /* Add this one to the end */ + list_add_tail(&urbp->urb_queue_list, &furbp->urb_queue_list); + + /* Grab the last TD from the last URB */ + ltd = list_entry(lurbp->list.prev, struct uhci_td, list); + + /* Grab the first TD from the first URB */ + td = list_entry(urbp->list.next, struct uhci_td, list); + + /* No breadth since this will only be called for bulk transfers */ + ltd->link = virt_to_bus(td); - spin_lock_irqsave(&irqlist_lock, flags); - list_add(&td->irq_list, &uhci->interrupt_list); - spin_unlock_irqrestore(&irqlist_lock, flags); + spin_unlock_irqrestore(&uhci_append_urb_lock, flags); } -static void uhci_remove_irq_list(struct uhci_td *td) +static void uhci_delete_queued_urb(struct uhci *uhci, struct urb *urb) { + struct urb_priv *urbp, *nurbp; unsigned long flags; - spin_lock_irqsave(&irqlist_lock, flags); - list_del(&td->irq_list); - spin_unlock_irqrestore(&irqlist_lock, flags); + urbp = urb->hcpriv; + + spin_lock_irqsave(&uhci_append_urb_lock, flags); + + nurbp = list_entry(urbp->urb_queue_list.next, struct urb_priv, + urb_queue_list); + + if (!urbp->queued) { + /* We're the head, so just insert the QH for the next URB */ + uhci_insert_qh(uhci, &uhci->skel_bulk_qh, nurbp->qh); + nurbp->queued = 0; + } else { + struct urb_priv *purbp; + struct uhci_td *ptd; + + /* We're somewhere in the middle (or end). A bit trickier */ + /* than the head scenario */ + purbp = list_entry(urbp->urb_queue_list.prev, struct urb_priv, + urb_queue_list); + + ptd = list_entry(purbp->list.prev, struct uhci_td, list); + if (nurbp->queued) + /* Close the gap between the two */ + ptd->link = virt_to_bus(list_entry(nurbp->list.next, + struct uhci_td, list)); + else + /* The next URB happens to be the beggining, so */ + /* we're the last, end the chain */ + ptd->link = UHCI_PTR_TERM; + + } + + list_del(&urbp->urb_queue_list); + + spin_unlock_irqrestore(&uhci_append_urb_lock, flags); } -/* - * Request a interrupt handler.. - */ -static int uhci_request_irq(struct usb_device *usb_dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +struct urb_priv *uhci_alloc_urb_priv(struct urb *urb) { - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci_td *td = uhci_td_allocate(dev); - struct uhci_qh *interrupt_qh = uhci_qh_allocate(dev); + struct urb_priv *urbp; + + urbp = kmem_cache_alloc(uhci_up_cachep, in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL); + if (!urbp) + return NULL; - unsigned int destination, status; + memset((void *)urbp, 0, sizeof(*urbp)); - /* Destination: pipe destination with INPUT */ - destination = (pipe & 0x0007ff00) | 0x69; + urbp->inserttime = jiffies; + urbp->urb = urb; + + INIT_LIST_HEAD(&urbp->list); + INIT_LIST_HEAD(&urbp->urb_queue_list); - /* Status: slow/fast, Interrupt, Active, Short Packet Detect Infinite Errors */ - status = (pipe & (1 << 26)) | (1 << 24) | (1 << 23) | (1 << 29) | (0 << 27); + urb->hcpriv = urbp; - if(interrupt_qh->element != 1) - printk("interrupt_qh->element = 0x%x\n", - interrupt_qh->element); + usb_inc_dev_use(urb->dev); - td->link = 1; - td->status = status; /* In */ - td->info = destination | (7 << 21); /* 8 bytes of data */ - td->buffer = virt_to_bus(dev->data); - td->qh = interrupt_qh; - interrupt_qh->skel = &dev->uhci->root_hub->skel_int8_qh; + return urbp; +} - uhci_add_irq_list(dev->uhci, td, handler, dev_id); +static void uhci_add_td_to_urb(struct urb *urb, struct uhci_td *td) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - uhci_insert_td_in_qh(interrupt_qh, td); + td->urb = urb; - /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->root_hub->skel_int8_qh, interrupt_qh); - return 0; + list_add_tail(&td->list, &urbp->list); } -/* - * Control thread operations: we just mark the last TD - * in a control thread as an interrupt TD, and wake up - * the front-end on completion. - * - * We need to remove the TD from the lists (both interrupt - * list and TD lists) by hand if something bad happens! - */ -static struct wait_queue *control_wakeup; - -static int uhci_control_completed(int status, void *buffer, void *dev_id) +static void uhci_remove_td_from_urb(struct urb *urb, struct uhci_td *td) { - wake_up(&control_wakeup); - return 0; /* Don't re-instate */ + urb = NULL; /* No warnings */ + + if (list_empty(&td->list)) + return; + + list_del(&td->list); + INIT_LIST_HEAD(&td->list); + + td->urb = NULL; } -/* td points to the last td in the list, which interrupts on completion */ -static int uhci_run_control(struct uhci_device *dev, struct uhci_td *first, struct uhci_td *last) +static void uhci_destroy_urb_priv(struct urb *urb) { - struct wait_queue wait = { current, NULL }; - struct uhci_qh *ctrl_qh = uhci_qh_allocate(dev); - struct uhci_td *curtd; + struct list_head *tmp, *head; + struct urb_priv *urbp; + struct uhci *uhci; + struct uhci_td *td; + unsigned long flags; - current->state = TASK_UNINTERRUPTIBLE; - add_wait_queue(&control_wakeup, &wait); + spin_lock_irqsave(&urb->lock, flags); - uhci_add_irq_list(dev->uhci, last, uhci_control_completed, NULL); - - /* FIXME: This is kinda kludged */ - /* Walk the TD list and update the QH pointer */ - { - int maxcount = 100; - - curtd = first; - do { - curtd->qh = ctrl_qh; - if (curtd->link & 1) - break; + urbp = (struct urb_priv *)urb->hcpriv; + if (!urbp) + goto unlock; - curtd = bus_to_virt(curtd->link & ~0xF); - if (!--maxcount) { - printk("runaway tds!\n"); - break; - } - } while (1); + if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) + goto unlock; + + uhci = urb->dev->bus->hcpriv; + + head = &urbp->list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + uhci_remove_td_from_urb(urb, td); + + uhci_remove_td(uhci, td); + + uhci_free_td(td); } - uhci_insert_tds_in_qh(ctrl_qh, first, last); + urb->hcpriv = NULL; + kmem_cache_free(uhci_up_cachep, urbp); - /* Add it into the skeleton */ - uhci_insert_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh); + usb_dec_dev_use(urb->dev); + +unlock: + spin_unlock_irqrestore(&urb->lock, flags); +} - schedule_timeout(HZ/10); +static void uhci_inc_fsbr(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; - remove_wait_queue(&control_wakeup, &wait); + if (!urbp) + return; - /* Clean up in case it failed.. */ - uhci_remove_irq_list(last); + spin_lock_irqsave(&uhci->framelist_lock, flags); -#if 0 - printk("Looking for tds [%p, %p]\n", dev->control_td, td); -#endif + if (!urbp->fsbr) { + urbp->fsbr = 1; + if (!uhci->fsbr++) + uhci->skel_term_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH; + } + + spin_unlock_irqrestore(&uhci->framelist_lock, flags); +} + +static void uhci_dec_fsbr(struct uhci *uhci, struct urb *urb) +{ + unsigned long flags; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + if (!urbp) + return; - /* Remove it from the skeleton */ - uhci_remove_qh(&dev->uhci->root_hub->skel_control_qh, ctrl_qh); + spin_lock_irqsave(&uhci->framelist_lock, flags); - uhci_qh_deallocate(ctrl_qh); + if (urbp->fsbr) { + urbp->fsbr = 0; + if (!--uhci->fsbr) + uhci->skel_term_qh.link = UHCI_PTR_TERM; + } - return uhci_td_result(dev, last); + spin_unlock_irqrestore(&uhci->framelist_lock, flags); } /* - * Send or receive a control message on a pipe. - * - * Note that the "pipe" structure is set up to map - * easily to the uhci destination fields. - * - * A control message is built up from three parts: - * - The command itself - * - [ optional ] data phase - * - Status complete phase - * - * The data phase can be an arbitrary number of TD's - * although we currently had better not have more than - * 29 TD's here (we have 31 TD's allocated for control - * operations, and two of them are used for command and - * status). + * Map status to standard result codes * - * 29 TD's is a minimum of 232 bytes worth of control - * information, that's just ridiculously high. Most - * control messages have just a few bytes of data. + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status)] + * is True for output TDs and False for input TDs. */ -static int uhci_control_msg(struct usb_device *usb_dev, unsigned int pipe, void *cmd, void *data, int len) +static int uhci_map_status(int status, int dir_out) { - struct uhci_device *dev = usb_to_uhci(usb_dev); - struct uhci_td *first, *td, *prevtd; - unsigned long destination, status; - int ret; + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EPIPE; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; - if (len > usb_maxpacket(usb_dev->maxpacketsize) * 29) - printk("Warning, too much data for a control packet, crashing\n"); + return -EINVAL; +} - first = td = uhci_td_allocate(dev); +/* + * Control transfers + */ +static int uhci_submit_control(struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; - /* The "pipe" thing contains the destination in bits 8--18, 0x2D is SETUP */ - destination = (pipe & 0x0007ff00) | 0x2D; + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; - /* Status: slow/fast, Active, Short Packet Detect Three Errors */ - status = (pipe & (1 << 26)) | (1 << 23) | (1 << 29) | (3 << 27); + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | (3 << 27); /* * Build the TD for the control request */ - td->status = status; /* Try forever */ - td->info = destination | (7 << 21); /* 8 bytes of data */ - td->buffer = virt_to_bus(cmd); + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | (7 << 21), + virt_to_bus(urb->setup_packet)); /* * If direction is "send", change the frame from SETUP (0x2D) - * to OUT (0xE1). Else change it from SETUP to IN (0x69) + * to OUT (0xE1). Else change it from SETUP to IN (0x69). */ - destination ^= (0x2D ^ 0x69); /* SETUP -> IN */ - if (usb_pipeout(pipe)) - destination ^= (0xE1 ^ 0x69); /* IN -> OUT */ + destination ^= (USB_PID_SETUP ^ usb_packetid(urb->pipe)); - prevtd = td; - td = uhci_td_allocate(dev); - prevtd->link = 4 | virt_to_bus(td); + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; /* * Build the DATA TD's */ while (len > 0) { - /* Build the TD for control status */ int pktsze = len; - int maxsze = usb_maxpacket(pipe); if (pktsze > maxsze) pktsze = maxsze; + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; + /* Alternate Data0/1 (start with Data1) */ - destination ^= 1 << 19; + destination ^= 1 << TD_TOKEN_TOGGLE; - td->status = status; /* Status */ - td->info = destination | ((pktsze-1) << 21); /* pktsze bytes of data */ - td->buffer = virt_to_bus(data); - td->backptr = &prevtd->link; - - prevtd = td; - td = uhci_td_allocate(dev); - prevtd->link = 4 | virt_to_bus(td); /* Update previous TD */ + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21), + virt_to_bus(data)); - data += maxsze; - len -= maxsze; + data += pktsze; + len -= pktsze; } /* - * Build the final TD for control status + * Build the final TD for control status */ - destination ^= (0xE1 ^ 0x69); /* OUT -> IN */ - destination |= 1 << 19; /* End in Data1 */ + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; - td->link = 1; /* Terminate */ - td->status = status | (1 << 24); /* IOC */ - td->info = destination | (0x7ff << 21); /* 0 bytes of data */ - td->buffer = 0; - td->backptr = &prevtd->link; + /* + * It's IN if the pipe is an output pipe or we're not expecting + * data back. + */ + destination &= ~TD_PID; + if (usb_pipeout(urb->pipe) || !urb->transfer_buffer_length) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; - /* Start it up.. */ - ret = uhci_run_control(dev, first, td); + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ - { - int maxcount = 100; - struct uhci_td *curtd = first; - unsigned int nextlink; + status &= ~TD_CTRL_SPD; - do { - nextlink = curtd->link; - uhci_remove_td(curtd); - uhci_td_deallocate(curtd); - if (nextlink & 1) /* Tail? */ - break; + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status | TD_CTRL_IOC, + destination | (UHCI_NULL_DATA_SIZE << 21), 0); - curtd = bus_to_virt(nextlink & ~0xF); - if (!--maxcount) { - printk("runaway td's!?\n"); - break; + qh = uhci_alloc_qh(urb->dev); + if (!qh) + return -ENOMEM; + + /* Low speed or small transfers gets a different queue and treatment */ + if (urb->pipe & TD_CTRL_LS) { + uhci_insert_tds_in_qh(qh, urb, 0); + uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, qh); + } else { + uhci_insert_tds_in_qh(qh, urb, 1); + uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, qh); + uhci_inc_fsbr(uhci, urb); + } + + urbp->qh = qh; + + uhci_add_urb_list(uhci, urb); + + usb_inc_dev_use(urb->dev); + + return -EINPROGRESS; +} + +static int usb_control_retrigger_status(struct urb *urb); + +static int uhci_result_control(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret = 0; + + if (!urbp) + return -EINVAL; + + head = &urbp->list; + if (head->next == head) + return -EINVAL; + + if (urbp->short_control_packet) { + tmp = head->prev; + goto status_phase; + } + + tmp = head->next; + td = list_entry(tmp, struct uhci_td, list); + + /* The first TD is the SETUP phase, check the status, but skip */ + /* the count */ + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + urb->actual_length = 0; + + /* The rest of the TD's (but the last) are data */ + tmp = tmp->next; + while (tmp != head && tmp->next != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && + !(td->status & TD_CTRL_ACTIVE)) { + uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); + urbp->fsbr_timeout = 0; + td->status &= ~TD_CTRL_IOC; + } + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + if (status) + goto td_error; + + /* Check to see if we received a short packet */ + if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { + if (urb->transfer_flags & USB_DISABLE_SPD) { + ret = -EREMOTEIO; + goto err; } - } while (1); + + if (uhci_packetid(td->info) == USB_PID_IN) + return usb_control_retrigger_status(urb); + else + return 0; + } + } + +status_phase: + td = list_entry(tmp, struct uhci_td, list); + + /* Control status phase */ + status = uhci_status_bits(td->status); + +#ifdef I_HAVE_BUGGY_APC_BACKUPS + /* APC BackUPS Pro kludge */ + /* It tries to send all of the descriptor instead of the amount */ + /* we requested */ + if (td->status & TD_CTRL_IOC && /* IOC is masked out by uhci_status_bits */ + status & TD_CTRL_ACTIVE && + status & TD_CTRL_NAK) + return 0; +#endif + + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + if (status) + goto td_error; + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td->info)); + if (ret == -EPIPE) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + +err: + if (debug && ret != -EPIPE) { + /* Some debugging code */ + dbg("uhci_result_control() failed with status %x", status); + + /* Print the chain for debugging purposes */ + uhci_show_urb_queue(urb); } return ret; } -static struct usb_device *uhci_usb_allocate(struct usb_device *parent) +static int usb_control_retrigger_status(struct urb *urb) { - struct usb_device *usb_dev; - struct uhci_device *dev; - int i; + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci *uhci = urb->dev->bus->hcpriv; - usb_dev = kmalloc(sizeof(*usb_dev), GFP_KERNEL); - if (!usb_dev) - return NULL; + urbp->short_control_packet = 1; + + /* Create a new QH to avoid pointer overwriting problems */ + uhci_remove_qh(uhci, urbp->qh); + + /* Delete all of the TD's except for the status TD at the end */ + head = &urbp->list; + tmp = head->next; + while (tmp != head && tmp->next != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + uhci_remove_td_from_urb(urb, td); + + uhci_remove_td(uhci, td); + + uhci_free_td(td); + } + + urbp->qh = uhci_alloc_qh(urb->dev); + if (!urbp->qh) { + err("unable to allocate new QH for control retrigger"); + return -ENOMEM; + } + + /* One TD, who cares about Breadth first? */ + uhci_insert_tds_in_qh(urbp->qh, urb, 0); + + /* Low speed or small transfers gets a different queue and treatment */ + if (urb->pipe & TD_CTRL_LS) + uhci_insert_qh(uhci, &uhci->skel_ls_control_qh, urbp->qh); + else + uhci_insert_qh(uhci, &uhci->skel_hs_control_qh, urbp->qh); + + return -EINPROGRESS; +} + +/* + * Interrupt transfers + */ +static int uhci_submit_interrupt(struct urb *urb) +{ + struct uhci_td *td; + unsigned long destination, status; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (urb->transfer_buffer_length > usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; + + destination |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + destination |= ((urb->transfer_buffer_length - 1) << 21); + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination, + virt_to_bus(urb->transfer_buffer)); + + uhci_insert_td(uhci, &uhci->skeltd[__interval_to_skel(urb->interval)], td); + + uhci_add_urb_list(uhci, urb); + + return -EINPROGRESS; +} + +static int uhci_result_interrupt(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = urb->hcpriv; + struct uhci_td *td; + unsigned int status; + int ret = 0; + + if (!urbp) + return -EINVAL; + + urb->actual_length = 0; + + head = &urbp->list; + tmp = head->next; + while (tmp != head) { + td = list_entry(tmp, struct uhci_td, list); + + tmp = tmp->next; + + if (urbp->fsbr_timeout && (td->status & TD_CTRL_IOC) && + !(td->status & TD_CTRL_ACTIVE)) { + uhci_inc_fsbr(urb->dev->bus->hcpriv, urb); + urbp->fsbr_timeout = 0; + td->status &= ~TD_CTRL_IOC; + } + + status = uhci_status_bits(td->status); + if (status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + urb->actual_length += uhci_actual_length(td->status); + + if (status) + goto td_error; + + if (uhci_actual_length(td->status) < uhci_expected_length(td->info)) { + usb_settoggle(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info), + uhci_toggle(td->info) ^ 1); + + if (urb->transfer_flags & USB_DISABLE_SPD) { + ret = -EREMOTEIO; + goto err; + } else + return 0; + } + } + + return 0; + +td_error: + ret = uhci_map_status(status, uhci_packetout(td->info)); + if (ret == -EPIPE) + /* endpoint has stalled - mark it halted */ + usb_endpoint_halt(urb->dev, uhci_endpoint(td->info), + uhci_packetout(td->info)); + +err: + if (debug && ret != -EPIPE) { + /* Some debugging code */ + dbg("uhci_result_interrupt/bulk() failed with status %x", + status); + + /* Print the chain for debugging purposes */ + if (urbp->qh) + uhci_show_urb_queue(urb); + else + uhci_show_td(td); + } + + return ret; +} + +static void uhci_reset_interrupt(struct urb *urb) +{ + struct list_head *tmp; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct uhci_td *td; + + if (!urbp) + return; + + tmp = urbp->list.next; + td = list_entry(tmp, struct uhci_td, list); + if (!td) + return; + + td->status = (td->status & 0x2F000000) | TD_CTRL_ACTIVE | TD_CTRL_IOC; + td->info &= ~(1 << TD_TOKEN_TOGGLE); + td->info |= (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE); + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + urb->status = -EINPROGRESS; +} + +/* + * Bulk transfers + */ +static int uhci_submit_bulk(struct urb *urb, struct urb *eurb) +{ + struct uhci_td *td; + struct uhci_qh *qh; + unsigned long destination, status; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + int maxsze = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)); + int len = urb->transfer_buffer_length; + unsigned char *data = urb->transfer_buffer; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + + if (len < 0) + return -EINVAL; + + /* Can't have low speed bulk transfers */ + if (urb->pipe & TD_CTRL_LS) + return -EINVAL; + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + /* 3 errors */ + status = TD_CTRL_ACTIVE | (3 << TD_CTRL_C_ERR_SHIFT); + + if (!(urb->transfer_flags & USB_DISABLE_SPD)) + status |= TD_CTRL_SPD; + + /* + * Build the DATA TD's + */ + do { /* Allow zero length packets */ + int pktsze = len; + + if (pktsze > maxsze) + pktsze = maxsze; + + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((pktsze - 1) << 21) | + (usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)) << TD_TOKEN_TOGGLE), + virt_to_bus(data)); + + data += pktsze; + len -= maxsze; + + if (len <= 0) + td->status |= TD_CTRL_IOC; + + usb_dotoggle(urb->dev, usb_pipeendpoint(urb->pipe), + usb_pipeout(urb->pipe)); + } while (len > 0); + + qh = uhci_alloc_qh(urb->dev); + if (!qh) + return -ENOMEM; + + urbp->qh = qh; + + /* Always assume depth first */ + uhci_insert_tds_in_qh(qh, urb, 1); + + if (urb->transfer_flags & USB_QUEUE_BULK && eurb) { + urbp->queued = 1; + uhci_append_queued_urb(uhci, eurb, urb); + } else + uhci_insert_qh(uhci, &uhci->skel_bulk_qh, qh); + + uhci_add_urb_list(uhci, urb); + + uhci_inc_fsbr(uhci, urb); + + return -EINPROGRESS; +} + +/* We can use the result interrupt since they're identical */ +#define uhci_result_bulk uhci_result_interrupt + +/* + * Isochronous transfers + */ +static int isochronous_find_limits(struct urb *urb, unsigned int *start, unsigned int *end) +{ + struct urb *last_urb = NULL; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head *tmp, *head = &uhci->urb_list; + int ret = 0; + unsigned long flags; + + nested_lock(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + /* look for pending URB's with identical pipe handle */ + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && + (u->status == -EINPROGRESS) && (u != urb)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + ret = 0; + } else + ret = -1; /* no previous urb found */ + + nested_unlock(&uhci->urblist_lock, flags); + + return ret; +} + +static int isochronous_find_start(struct urb *urb) +{ + int limits; + unsigned int start = 0, end = 0; + + if (urb->number_of_packets > 900) /* 900? Why? */ + return -EFBIG; + + limits = isochronous_find_limits(urb, &start, &end); + + if (urb->transfer_flags & USB_ISO_ASAP) { + if (limits) { + int curframe; + + curframe = uhci_get_current_frame_number(urb->dev) % UHCI_NUMFRAMES; + urb->start_frame = (curframe + 10) % UHCI_NUMFRAMES; + } else + urb->start_frame = end; + } else { + urb->start_frame %= UHCI_NUMFRAMES; + /* FIXME: Sanity check */ + } + + return 0; +} + +static int uhci_submit_isochronous(struct urb *urb) +{ + struct uhci_td *td; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + int i, ret, framenum; + int status, destination; + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid(urb->pipe); + + ret = isochronous_find_start(urb); + if (ret) + return ret; + + framenum = urb->start_frame; + for (i = 0; i < urb->number_of_packets; i++, framenum++) { + if (!urb->iso_frame_desc[i].length) + continue; + + td = uhci_alloc_td(urb->dev); + if (!td) + return -ENOMEM; + + uhci_add_td_to_urb(urb, td); + uhci_fill_td(td, status, destination | ((urb->iso_frame_desc[i].length - 1) << 21), + virt_to_bus(urb->transfer_buffer + urb->iso_frame_desc[i].offset)); + + if (i + 1 >= urb->number_of_packets) + td->status |= TD_CTRL_IOC; + + uhci_insert_td_frame_list(uhci, td, framenum); + } + + uhci_add_urb_list(uhci, urb); + + return -EINPROGRESS; +} + +static int uhci_result_isochronous(struct urb *urb) +{ + struct list_head *tmp, *head; + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + int status; + int i, ret = 0; + + if (!urbp) + return -EINVAL; + + urb->actual_length = 0; + + i = 0; + head = &urbp->list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); + int actlength; + + tmp = tmp->next; + + if (td->status & TD_CTRL_ACTIVE) + return -EINPROGRESS; + + actlength = uhci_actual_length(td->status); + urb->iso_frame_desc[i].actual_length = actlength; + urb->actual_length += actlength; + + status = uhci_map_status(uhci_status_bits(td->status), usb_pipeout(urb->pipe)); + urb->iso_frame_desc[i].status = status; + if (status != 0) { + urb->error_count++; + ret = status; + } + + i++; + } + + return ret; +} + +static struct urb *uhci_find_urb_ep(struct uhci *uhci, struct urb *urb) +{ + struct list_head *tmp, *head = &uhci->urb_list; + unsigned long flags; + struct urb *u = NULL; + + if (usb_pipeisoc(urb->pipe)) + return NULL; + + nested_lock(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + u = list_entry(tmp, struct urb, urb_list); + + tmp = tmp->next; + + if (u->dev == urb->dev && + u->pipe == urb->pipe) + goto found; + } + u = NULL; + +found: + nested_unlock(&uhci->urblist_lock, flags); + + return u; +} + +static int uhci_submit_urb(struct urb *urb) +{ + int ret = -EINVAL; + struct uhci *uhci; + unsigned long flags; + struct urb *u; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus || !urb->dev->bus->hcpriv) + return -ENODEV; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + /* Short circuit the virtual root hub */ + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_submit_urb(urb); + + u = uhci_find_urb_ep(uhci, urb); + if (u && !(urb->transfer_flags & USB_QUEUE_BULK)) + return -ENXIO; + + spin_lock_irqsave(&urb->lock, flags); + + if (!uhci_alloc_urb_priv(urb)) { + spin_unlock_irqrestore(&urb->lock, flags); + return -ENOMEM; + } + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_submit_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_submit_bulk(urb, u); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_submit_isochronous(urb); + break; + } + + urb->status = ret; + + spin_unlock_irqrestore(&urb->lock, flags); + + if (ret == -EINPROGRESS) + ret = 0; + else + uhci_unlink_generic(urb); + + return ret; +} + +/* + * Return the result of a transfer + * + * Must be called with urblist_lock acquired + */ +static void uhci_transfer_result(struct urb *urb) +{ + struct urb *turb; + int proceed = 0, is_ring = 0; + int ret = -EINVAL; + unsigned long flags; + + spin_lock_irqsave(&urb->lock, flags); + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + ret = uhci_result_control(urb); + break; + case PIPE_INTERRUPT: + ret = uhci_result_interrupt(urb); + break; + case PIPE_BULK: + ret = uhci_result_bulk(urb); + break; + case PIPE_ISOCHRONOUS: + ret = uhci_result_isochronous(urb); + break; + } + + urb->status = ret; + + spin_unlock_irqrestore(&urb->lock, flags); + + if (ret == -EINPROGRESS) + return; + + switch (usb_pipetype(urb->pipe)) { + case PIPE_CONTROL: + case PIPE_BULK: + case PIPE_ISOCHRONOUS: + uhci_unlink_generic(urb); + break; + case PIPE_INTERRUPT: + /* Interrupts are an exception */ + urb->complete(urb); + if (urb->interval) + uhci_reset_interrupt(urb); + else + uhci_unlink_generic(urb); + return; /* <-- Note the return */ + } + + if (urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS) { + proceed = 1; + break; + } + + turb = turb->next; + } while (turb && turb != urb && turb != urb->next); + + if (turb == urb || turb == urb->next) + is_ring = 1; + } + + if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + urb->complete(urb); + if (!proceed && is_ring) + uhci_submit_urb(urb); + } + + if (proceed && urb->next) { + turb = urb->next; + do { + if (turb->status != -EINPROGRESS && + uhci_submit_urb(turb) != 0) + + turb = turb->next; + } while (turb && turb != urb->next); + + if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) + urb->complete(urb); + } +} + +static int uhci_unlink_generic(struct urb *urb) +{ + struct urb_priv *urbp = urb->hcpriv; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + + if (!urbp) + return -EINVAL; + + uhci_dec_fsbr(uhci, urb); /* Safe since it checks */ + + uhci_remove_urb_list(uhci, urb); + + if (urbp->qh) + /* The interrupt loop will reclaim the QH's */ + uhci_remove_qh(uhci, urbp->qh); + + if (!list_empty(&urbp->urb_queue_list)) + uhci_delete_queued_urb(uhci, urb); + + uhci_destroy_urb_priv(urb); + + return 0; +} + +static int uhci_unlink_urb(struct urb *urb) +{ + struct uhci *uhci; + int ret = 0; + unsigned long flags; + + if (!urb) + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + uhci = (struct uhci *)urb->dev->bus->hcpriv; + + /* Short circuit the virtual root hub */ + if (usb_pipedevice(urb->pipe) == uhci->rh.devnum) + return rh_unlink_urb(urb); + + if (urb->status == -EINPROGRESS) { + uhci_unlink_generic(urb); + + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + urb->status = -ECONNABORTED; + + spin_lock_irqsave(&uhci->urb_remove_lock, flags); + + /* Check to see if the remove list is empty */ + if (list_empty(&uhci->urb_remove_list)) + uhci_set_next_interrupt(uhci); + + list_add(&urb->urb_list, &uhci->urb_remove_list); + + spin_unlock_irqrestore(&uhci->urb_remove_lock, flags); + } else { + urb->status = -ENOENT; + + if (in_interrupt()) { /* wait at least 1 frame */ + static int errorcount = 10; + + if (errorcount--) + dbg("uhci_unlink_urb called from interrupt for urb %p", urb); + udelay(1000); + } else + schedule_timeout(1+1*HZ/1000); + + if (urb->complete) + urb->complete(urb); + } + } + + return ret; +} + +static int uhci_fsbr_timeout(struct uhci *uhci, struct urb *urb) +{ + struct urb_priv *urbp = (struct urb_priv *)urb->hcpriv; + struct list_head *head, *tmp; + + uhci_dec_fsbr(uhci, urb); + + /* There is a race with updating IOC in here, but it's not worth */ + /* trying to fix since this is merely an optimization. The only */ + /* time we'd lose is if the status of the packet got updated */ + /* and we'd be turning on FSBR next frame anyway, so it's a wash */ + urbp->fsbr_timeout = 1; + + head = &urbp->list; + tmp = head->next; + while (tmp != head) { + struct uhci_td *td = list_entry(tmp, struct uhci_td, list); - memset(usb_dev, 0, sizeof(*usb_dev)); + tmp = tmp->next; - dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - usb_destroy_configuration(usb_dev); - kfree(usb_dev); - return NULL; + if (td->status & TD_CTRL_ACTIVE) { + td->status |= TD_CTRL_IOC; + break; + } } - /* Initialize "dev" */ - memset(dev, 0, sizeof(*dev)); + return 0; +} - usb_dev->hcpriv = dev; - dev->usb = usb_dev; +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +static int uhci_get_current_frame_number(struct usb_device *dev) +{ + struct uhci *uhci = (struct uhci *)dev->bus->hcpriv; - usb_dev->parent = parent; + return inw(uhci->io_addr + USBFRNUM); +} - if (parent) { - usb_dev->bus = parent->bus; - dev->uhci = usb_to_uhci(parent)->uhci; - } +struct usb_operations uhci_device_operations = { + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; - /* Reset the QH's and TD's */ - for (i = 0; i < UHCI_MAXQH; i++) { - dev->qh[i].link = 1; - dev->qh[i].element = 1; - dev->qh[i].inuse = 0; - } +/* ------------------------------------------------------------------- + Virtual Root Hub + ------------------------------------------------------------------- */ - for (i = 0; i < UHCI_MAXTD; i++) { - dev->td[i].link = 1; - dev->td[i].inuse = 0; - } +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; - return usb_dev; -} -static int uhci_usb_deallocate(struct usb_device *usb_dev) +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = { - struct uhci_device *dev = usb_to_uhci(usb_dev); - int i; + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, + Bit 5 Remote-wakeup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; - /* There are UHCI_MAXTD preallocated tds */ - for (i = 0; i < UHCI_MAXTD; ++i) { - struct uhci_td *td = dev->td + i; +static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; - /* And remove it from the irq list, if it's active */ - if (td->status & (1 << 23)) - uhci_remove_irq_list(td); +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +static int rh_send_irq(struct urb *urb) +{ + int i, len = 1; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; - if (td->inuse) - uhci_remove_td(td); + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw(io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; } - /* Remove the td from any queues */ - for (i = 0; i < UHCI_MAXQH; ++i) { - struct uhci_qh *qh = dev->qh + i; + *(__u16 *) urb->transfer_buffer = cpu_to_le16(data); + urb->actual_length = len; + urb->status = USB_ST_NOERROR; - if (qh->inuse) - uhci_remove_qh(qh->skel, qh); + if ((data > 0) && (uhci->rh.send != 0)) { + dbg("root-hub INT complete: port1: %x port2: %x data: %x", + inw(io_addr + USBPORTSC1), inw(io_addr + USBPORTSC2), data); + urb->complete(urb); } - kfree(dev); - usb_destroy_configuration(usb_dev); - kfree(usb_dev); - - return 0; + return USB_ST_NOERROR; } -struct usb_operations uhci_device_operations = { - uhci_usb_allocate, - uhci_usb_deallocate, - uhci_control_msg, - uhci_request_irq, -}; +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ +static int rh_init_int_timer(struct urb *urb); -/* - * This is just incredibly fragile. The timings must be just - * right, and they aren't really documented very well. - * - * Note the short delay between disabling reset and enabling - * the port.. - */ -static void uhci_reset_port(unsigned int port) +static void rh_int_timer_do(unsigned long ptr) { - unsigned short status; + struct urb *urb = (struct urb *)ptr; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + struct list_head *tmp, *head = &uhci->urb_list; + struct urb_priv *urbp; + int len; + unsigned long flags; - status = inw(port); - outw(status | USBPORTSC_PR, port); /* reset port */ - wait_ms(10); - outw(status & ~USBPORTSC_PR, port); - udelay(5); + if (uhci->rh.send) { + len = rh_send_irq(urb); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete(urb); + } + } - status = inw(port); - outw(status | USBPORTSC_PE, port); /* enable port */ - wait_ms(10); + nested_lock(&uhci->urblist_lock, flags); + tmp = head->next; + while (tmp != head) { + struct urb *u = list_entry(tmp, urb_t, urb_list); + + tmp = tmp->next; - status = inw(port); - if(!(status & USBPORTSC_PE)) { - outw(status | USBPORTSC_PE, port); /* one more try at enabling port */ - wait_ms(50); + urbp = (struct urb_priv *)u->hcpriv; + if (urbp) { + /* Check if the FSBR timed out */ + if (urbp->fsbr && time_after(urbp->inserttime + IDLE_TIMEOUT, jiffies)) + uhci_fsbr_timeout(uhci, u); + + /* Check if the URB timed out */ + if (u->timeout && time_after(u->timeout, jiffies)) { + u->transfer_flags |= USB_ASYNC_UNLINK | USB_TIMEOUT_KILLED; + uhci_unlink_urb(u); + } + } } + nested_unlock(&uhci->urblist_lock, flags); + rh_init_int_timer(urb); } - -/* - * This gets called if the connect status on the root - * hub (and the root hub only) changes. - */ -static void uhci_connect_change(struct uhci *uhci, unsigned int port, unsigned int nr) +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer */ +static int rh_init_int_timer(struct urb *urb) { - struct usb_device *usb_dev; - struct uhci_device *dev; - unsigned short status; + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - printk("uhci_connect_change: called for %d\n", nr); + uhci->rh.interval = urb->interval; + init_timer(&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long)urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * (urb->interval < 30 ? 30 : urb->interval)) / 1000; + add_timer(&uhci->rh.rh_int_timer); - /* - * Even if the status says we're connected, - * the fact that the status bits changed may - * that we got disconnected and then reconnected. - * - * So start off by getting rid of any old devices.. - */ - usb_disconnect(&uhci->root_hub->usb->children[nr]); + return 0; +} - status = inw(port); +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break - /* If we have nothing connected, then clear change status and disable the port */ - status = (status & ~USBPORTSC_PE) | USBPORTSC_PEC; - if (!(status & USBPORTSC_CCS)) { - outw(status, port); - return; - } +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) - /* - * Ok, we got a new connection. Allocate a device to it, - * and find out what it wants to do.. - */ - usb_dev = uhci_usb_allocate(uhci->root_hub->usb); - dev = usb_dev->hcpriv; +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr + USBPORTSC1 + 2 * (wIndex-1)) - dev->uhci = uhci; - usb_connect(usb_dev); +/*-------------------------------------------------------------------------*/ +/************************* + ** Root Hub Control Pipe + *************************/ - uhci->root_hub->usb->children[nr] = usb_dev; +static int rh_submit_urb(struct urb *urb) +{ + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest *cmd = (devrequest *)urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = USB_ST_NOERROR; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype(pipe) == PIPE_INTERRUPT) { + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer(urb); + + return USB_ST_NOERROR; + } - wait_ms(200); /* wait for powerup */ - uhci_reset_port(port); + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = le16_to_cpu(cmd->value); + wIndex = le16_to_cpu(cmd->index); + wLength = le16_to_cpu(cmd->length); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *)data = cpu_to_le16(1); + OK(2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *)data = cpu_to_le16(0); + OK(2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *)data = cpu_to_le32(0); + OK(4); /* hub power */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw(io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *)data = cpu_to_le16(status); + *(__u16 *)(data + 2) = cpu_to_le16(cstatus); + OK(4); + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case RH_ENDPOINT_STALL: + OK(0); + } + break; + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_OVER_CURRENT: + OK(0); /* hub power over current */ + } + break; + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_ENABLE: + CLR_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + case RH_PORT_SUSPEND: + CLR_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power */ + case RH_C_PORT_CONNECTION: + SET_RH_PORTSTAT(USBPORTSC_CSC); + OK(0); + case RH_C_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PEC); + OK(0); + case RH_C_PORT_SUSPEND: + /*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK(0); + case RH_C_PORT_OVER_CURRENT: + OK(0); /* port power over current */ + case RH_C_PORT_RESET: + uhci->rh.c_p_r[wIndex - 1] = 0; + OK(0); + } + break; + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case RH_PORT_SUSPEND: + SET_RH_PORTSTAT(USBPORTSC_SUSP); + OK(0); + case RH_PORT_RESET: + SET_RH_PORTSTAT(USBPORTSC_PR); + wait_ms(50); /* USB v1.1 7.1.7.3 */ + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT(USBPORTSC_PR); + udelay(10); + SET_RH_PORTSTAT(USBPORTSC_PE); + wait_ms(10); + SET_RH_PORTSTAT(0xa); + OK(0); + case RH_PORT_POWER: + OK(0); /* port power ** */ + case RH_PORT_ENABLE: + SET_RH_PORTSTAT(USBPORTSC_PE); + OK(0); + } + break; + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK(0); + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case 0x01: /* device descriptor */ + len = min(leni, min(sizeof(root_hub_dev_des), wLength)); + memcpy(data, root_hub_dev_des, len); + OK(len); + case 0x02: /* configuration descriptor */ + len = min(leni, min(sizeof(root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK(len); + case 0x03: /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, + uhci->io_addr, "UHCI-alt", + data, wLength); + if (len > 0) { + OK (min (leni, len)); + } else + stat = -EPIPE; + } + break; + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min(leni, min(sizeof(root_hub_hub_des), wLength)); + memcpy(data, root_hub_hub_des, len); + OK(len); + case RH_GET_CONFIGURATION: + *(__u8 *)data = 0x01; + OK(1); + case RH_SET_CONFIGURATION: + OK(0); + case RH_GET_INTERFACE | RH_INTERFACE: + *(__u8 *)data = 0x00; + OK(1); + case RH_SET_INTERFACE | RH_INTERFACE: + OK(0); + default: + stat = -EPIPE; + } - /* Get speed information */ - usb_dev->slow = (inw(port) & USBPORTSC_LSDA) ? 1 : 0; + urb->actual_length = len; + urb->status = stat; + if (urb->complete) + urb->complete(urb); - /* - * Ok, all the stuff specific to the root hub has been done. - * The rest is generic for any new USB attach, regardless of - * hub type. - */ - usb_new_device(usb_dev); + return USB_ST_NOERROR; } +/*-------------------------------------------------------------------------*/ -/* - * This gets called when the root hub configuration - * has changed. Just go through each port, seeing if - * there is something interesting happening. - */ -static void uhci_check_configuration(struct uhci *uhci) +static int rh_unlink_urb(struct urb *urb) { - unsigned int io_addr = uhci->io_addr + USBPORTSC1; - int maxchild = uhci->root_hub->usb->maxchild; - int nr = 0; - - do { - unsigned short status = inw(io_addr); + struct uhci *uhci = (struct uhci *)urb->dev->bus->hcpriv; - if (status & USBPORTSC_CSC) - uhci_connect_change(uhci, io_addr, nr); - - nr++; io_addr += 2; - } while (nr < maxchild); + if (uhci->rh.urb == urb) { + uhci->rh.send = 0; + del_timer(&uhci->rh.rh_int_timer); + } + return 0; } +/*-------------------------------------------------------------------*/ -static void uhci_interrupt_notify(struct uhci *uhci) +void uhci_free_pending_qhs(struct uhci *uhci) { - struct list_head *head = &uhci->interrupt_list; - struct list_head *tmp; + struct list_head *tmp, *head; + unsigned long flags; - spin_lock(&irqlist_lock); + /* Free any pending QH's */ + spin_lock_irqsave(&uhci->qh_remove_lock, flags); + head = &uhci->qh_remove_list; tmp = head->next; while (tmp != head) { - struct uhci_td *td = list_entry(tmp, struct uhci_td, irq_list); - struct list_head *next; - - next = tmp->next; + struct uhci_qh *qh = list_entry(tmp, struct uhci_qh, remove_list); - if (!(td->status & (1 << 23))) { /* No longer active? */ - /* remove from IRQ list */ - __list_del(tmp->prev, next); - INIT_LIST_HEAD(tmp); - if (td->completed(td->status, bus_to_virt(td->buffer), td->dev_id)) { - struct uhci_qh *interrupt_qh = td->qh; + tmp = tmp->next; - list_add(&td->irq_list, &uhci->interrupt_list); - td->info ^= 1 << 19; /* toggle between data0 and data1 */ - td->status = (td->status & 0x2f000000) | (1 << 23) | (1 << 24); /* active */ - - /* Remove then readd? Is that necessary */ - uhci_remove_td(td); - uhci_insert_td_in_qh(interrupt_qh, td); - } - /* If completed wants to not reactivate, then it's */ - /* responsible for free'ing the TD's and QH's */ - /* or another function (such as run_control) */ - } - tmp = next; - } - spin_unlock(&irqlist_lock); -} + list_del(&qh->remove_list); -/* - * Check port status - Connect Status Change - for - * each of the attached ports (defaults to two ports, - * but at least in theory there can be more of them). - * - * Wake up the configurator if something happened, we - * can't really do much at interrupt time. - */ -static void uhci_root_hub_events(struct uhci *uhci, unsigned int io_addr) -{ - if (waitqueue_active(&uhci_configure)) { - int ports = uhci->root_hub->usb->maxchild; - io_addr += USBPORTSC1; - do { - if (inw(io_addr) & USBPORTSC_CSC) { - wake_up(&uhci_configure); - return; - } - io_addr += 2; - } while (--ports > 0); + uhci_free_qh(qh); } + spin_unlock_irqrestore(&uhci->qh_remove_lock, flags); } static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) @@ -757,39 +1965,63 @@ static void uhci_interrupt(int irq, void *__uhci, struct pt_regs *regs) struct uhci *uhci = __uhci; unsigned int io_addr = uhci->io_addr; unsigned short status; + unsigned long flags; + struct list_head *tmp, *head; /* - * Read the interrupt status, and write it back to clear the interrupt cause + * Read the interrupt status, and write it back to clear the + * interrupt cause */ status = inw(io_addr + USBSTS); + if (!status) /* shared interrupt, not mine */ + return; outw(status, io_addr + USBSTS); - /* Walk the list of pending TD's to see which ones completed.. */ - uhci_interrupt_notify(uhci); + if (status & ~(USBSTS_USBINT | USBSTS_ERROR)) { + if (status & USBSTS_RD) + printk(KERN_INFO "uhci: resume detected, not implemented\n"); + if (status & USBSTS_HSE) + printk(KERN_ERR "uhci: host system error, PCI problems?\n"); + if (status & USBSTS_HCPE) + printk(KERN_ERR "uhci: host controller process error. something bad happened\n"); + if (status & USBSTS_HCH) { + printk(KERN_ERR "uhci: host controller halted. very bad\n"); + /* FIXME: Reset the controller, fix the offending TD */ + } + } + + uhci_free_pending_qhs(uhci); - /* Check if there are any events on the root hub.. */ - uhci_root_hub_events(uhci, io_addr); -} + spin_lock(&uhci->urb_remove_lock); + head = &uhci->urb_remove_list; + tmp = head->next; + while (tmp != head) { + struct urb *urb = list_entry(tmp, struct urb, urb_list); -/* - * We init one packet, and mark it just IOC and _not_ - * active. Which will result in no actual USB traffic, - * but _will_ result in an interrupt every second. - * - * Which is exactly what we want. - */ -static void uhci_init_ticktd(struct uhci *uhci) -{ - struct uhci_device *dev = uhci->root_hub; - struct uhci_td *td = uhci_td_allocate(dev); + tmp = tmp->next; - td->link = 1; - td->status = (1 << 24); /* interrupt on completion */ - td->info = (15 << 21) | 0x7f69; /* (ignored) input packet, 16 bytes, device 127 */ - td->buffer = 0; - td->qh = NULL; + list_del(&urb->urb_list); + + if (urb->complete) + urb->complete(urb); + } + spin_unlock(&uhci->urb_remove_lock); + + uhci_clear_next_interrupt(uhci); + + /* Walk the list of pending TD's to see which ones completed */ + nested_lock(&uhci->urblist_lock, flags); + head = &uhci->urb_list; + tmp = head->next; + while (tmp != head) { + struct urb *urb = list_entry(tmp, struct urb, urb_list); - uhci->fl->frame[0] = virt_to_bus(td); + tmp = tmp->next; + + /* Checks the status and does all of the magic necessary */ + uhci_transfer_result(urb); + } + nested_unlock(&uhci->urblist_lock, flags); } static void reset_hc(struct uhci *uhci) @@ -797,9 +2029,9 @@ static void reset_hc(struct uhci *uhci) unsigned int io_addr = uhci->io_addr; /* Global reset for 50ms */ - outw(USBCMD_GRESET, io_addr+USBCMD); + outw(USBCMD_GRESET, io_addr + USBCMD); wait_ms(50); - outw(0, io_addr+USBCMD); + outw(0, io_addr + USBCMD); wait_ms(10); } @@ -808,8 +2040,6 @@ static void start_hc(struct uhci *uhci) unsigned int io_addr = uhci->io_addr; int timeout = 1000; - uhci_init_ticktd(uhci); - /* * Reset the HC - this will force us to get a * new notification of any already connected @@ -819,21 +2049,25 @@ static void start_hc(struct uhci *uhci) outw(USBCMD_HCRESET, io_addr + USBCMD); while (inw(io_addr + USBCMD) & USBCMD_HCRESET) { if (!--timeout) { - printk("USBCMD_HCRESET timed out!\n"); + printk(KERN_ERR "uhci: USBCMD_HCRESET timed out!\n"); break; } } - outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); + /* Turn on all interrupts */ + outw(USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, + io_addr + USBINTR); + + /* Start at frame 0 */ outw(0, io_addr + USBFRNUM); outl(virt_to_bus(uhci->fl), io_addr + USBFLBASEADD); /* Run and mark it configured with a 64-byte max packet */ - outw(USBCMD_RS | USBCMD_CF, io_addr + USBCMD); + outw(USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); } /* - * Allocate a frame list, and four regular queues. + * Allocate a frame list, and then setup the skeleton * * The hardware doesn't really know any difference * in the queues, but the order does matter for the @@ -843,20 +2077,14 @@ static void start_hc(struct uhci *uhci) * of the queues. We don't do that here, because * we'll create the actual TD entries on demand. * - The first queue is the "interrupt queue". - * - The second queue is the "control queue". + * - The second queue is the "control queue", split into low and high speed * - The third queue is "bulk data". - * - * We could certainly have multiple queues of the same - * type, and maybe we should. We could have per-device - * queues, for example. We begin small. */ -static struct uhci *alloc_uhci(unsigned int io_addr) +static struct uhci *alloc_uhci(unsigned int io_addr, unsigned int io_size) { - int i; + int i, port; struct uhci *uhci; struct usb_bus *bus; - struct uhci_device *dev; - struct usb_device *usb; uhci = kmalloc(sizeof(*uhci), GFP_KERNEL); if (!uhci) @@ -866,69 +2094,87 @@ static struct uhci *alloc_uhci(unsigned int io_addr) uhci->irq = -1; uhci->io_addr = io_addr; - INIT_LIST_HEAD(&uhci->interrupt_list); + uhci->io_size = io_size; + + spin_lock_init(&uhci->qh_remove_lock); + INIT_LIST_HEAD(&uhci->qh_remove_list); + + spin_lock_init(&uhci->urb_remove_lock); + INIT_LIST_HEAD(&uhci->urb_remove_list); + + nested_init(&uhci->urblist_lock); + INIT_LIST_HEAD(&uhci->urb_list); + + spin_lock_init(&uhci->framelist_lock); /* We need exactly one page (per UHCI specs), how convenient */ + /* We assume that one page is atleast 4k (1024 frames * 4 bytes) */ uhci->fl = (void *)__get_free_page(GFP_KERNEL); + if (!uhci->fl) + goto au_free_uhci; - bus = kmalloc(sizeof(*bus), GFP_KERNEL); + bus = usb_alloc_bus(&uhci_device_operations); if (!bus) - return NULL; - - memset(bus, 0, sizeof(*bus)); + goto au_free_fl; uhci->bus = bus; bus->hcpriv = uhci; - bus->op = &uhci_device_operations; - - /* - * We allocate a 8kB area for the UHCI hub. The area - * is described by the uhci_device structure, and basically - * contains everything needed for normal operation. - * - * The first page is the actual device descriptor for the - * hub. - * - * The second page is used for the frame list. - */ - usb = uhci_usb_allocate(NULL); - if (!usb) - return NULL; - - dev = uhci->root_hub = usb_to_uhci(usb); - - usb->bus = bus; /* Initialize the root hub */ + /* UHCI specs says devices must have 2 ports, but goes on to say */ - /* they may have more but give no way to determine how many they */ - /* have, so default to 2 */ - usb->maxchild = 2; - usb_init_root_hub(usb); + /* they may have more but give no way to determine how many they */ + /* have. However, according to the UHCI spec, Bit 7 is always set */ + /* to 1. So we try to use this to our advantage */ + for (port = 0; port < (io_size - 0x10) / 2; port++) { + unsigned int portstatus; + + portstatus = inw(io_addr + 0x10 + (port * 2)); + if (!(portstatus & 0x0080)) + break; + } + if (debug) + info("detected %d ports", port); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (port < 2 || port > 8) { + info("port count misdetected? forcing to 2 ports"); + port = 2; + } + + uhci->rh.numports = port; /* - * Initialize the queues. They all start out empty, - * linked to each other in the proper order. + * 9 Interrupt queues; link int2 to int1, int4 to int2, etc + * then link int1 to control and control to bulk */ - for (i = 1 ; i < 9; i++) { - dev->qh[i].link = 2 | virt_to_bus(&dev->skel_control_qh); - dev->qh[i].element = 1; + for (i = 1; i < 9; i++) { + struct uhci_td *td = &uhci->skeltd[i]; + + uhci_fill_td(td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + td->link = virt_to_bus(&uhci->skeltd[i - 1]); } - - dev->skel_control_qh.link = 2 | virt_to_bus(&dev->skel_bulk0_qh); - dev->skel_control_qh.element = 1; - dev->skel_bulk0_qh.link = 2 | virt_to_bus(&dev->skel_bulk1_qh); - dev->skel_bulk0_qh.element = 1; - dev->skel_bulk1_qh.link = 2 | virt_to_bus(&dev->skel_bulk2_qh); - dev->skel_bulk1_qh.element = 1; + uhci_fill_td(&uhci->skel_int1_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_int1_td.link = virt_to_bus(&uhci->skel_ls_control_qh) | UHCI_PTR_QH; + + uhci->skel_ls_control_qh.link = virt_to_bus(&uhci->skel_hs_control_qh) | UHCI_PTR_QH; + uhci->skel_ls_control_qh.element = UHCI_PTR_TERM; - dev->skel_bulk2_qh.link = 2 | virt_to_bus(&dev->skel_bulk3_qh); - dev->skel_bulk2_qh.element = 1; + uhci->skel_hs_control_qh.link = virt_to_bus(&uhci->skel_bulk_qh) | UHCI_PTR_QH; + uhci->skel_hs_control_qh.element = UHCI_PTR_TERM; - dev->skel_bulk3_qh.link = 1; - dev->skel_bulk3_qh.element = 1; + uhci->skel_bulk_qh.link = virt_to_bus(&uhci->skel_term_qh) | UHCI_PTR_QH; + uhci->skel_bulk_qh.element = UHCI_PTR_TERM; + + /* This dummy TD is to work around a bug in Intel PIIX controllers */ + uhci_fill_td(&uhci->skel_term_td, 0, (UHCI_NULL_DATA_SIZE << 21) | (0x7f << 8) | USB_PID_IN, 0); + uhci->skel_term_td.link = UHCI_PTR_TERM; + + uhci->skel_term_qh.link = UHCI_PTR_TERM; + uhci->skel_term_qh.element = virt_to_bus(&uhci->skel_term_td); /* * Fill the frame list: make all entries point to @@ -939,7 +2185,8 @@ static struct uhci *alloc_uhci(unsigned int io_addr) * us a reasonable dynamic range for irq latencies. */ for (i = 0; i < 1024; i++) { - struct uhci_qh * irq = &dev->skel_int2_qh; + struct uhci_td *irq = &uhci->skel_int1_td; + if (i & 1) { irq++; if (i & 2) { @@ -952,21 +2199,31 @@ static struct uhci *alloc_uhci(unsigned int io_addr) irq++; if (i & 32) { irq++; - if (i & 64) { + if (i & 64) irq++; - } } } } } } } - uhci->fl->frame[i] = 2 | virt_to_bus(irq); + + /* Only place we don't use the frame list routines */ + uhci->fl->frame[i] = virt_to_bus(irq); } return uhci; -} +/* + * error exits: + */ +au_free_fl: + free_page((unsigned long)uhci->fl); +au_free_uhci: + kfree(uhci); + + return NULL; +} /* * De-allocate all resources.. @@ -978,236 +2235,259 @@ static void release_uhci(struct uhci *uhci) uhci->irq = -1; } -#if 0 - if (uhci->root_hub) { - uhci_usb_deallocate(uhci_to_usb(uhci->root_hub)); - uhci->root_hub = NULL; - } -#endif - if (uhci->fl) { free_page((unsigned long)uhci->fl); uhci->fl = NULL; } - kfree(uhci->bus); + usb_free_bus(uhci->bus); kfree(uhci); } -void cleanup_drivers(void); - -static int uhci_control_thread(void * __uhci) +int uhci_start_root_hub(struct uhci *uhci) { - struct uhci *uhci = (struct uhci *)__uhci; + struct usb_device *dev; - lock_kernel(); - request_region(uhci->io_addr, 32, "usb-uhci"); + dev = usb_alloc_dev(NULL, uhci->bus); + if (!dev) + return -1; - /* - * This thread doesn't need any user-level access, - * so get rid of all our resources.. - */ - printk("uhci_control_thread at %p\n", &uhci_control_thread); - exit_mm(current); - exit_files(current); - exit_fs(current); + uhci->bus->root_hub = dev; + usb_connect(dev); - strcpy(current->comm, "uhci-control"); + if (usb_new_device(dev) != 0) { + usb_free_dev(dev); - /* - * Ok, all systems are go.. - */ - start_hc(uhci); - for(;;) { - siginfo_t info; - int unsigned long signr; - - interruptible_sleep_on(&uhci_configure); -#ifdef CONFIG_APM - if (apm_resume) { - apm_resume = 0; - start_hc(uhci); - continue; - } -#endif - uhci_check_configuration(uhci); - - if(signal_pending(current)) { - /* sending SIGUSR1 makes us print out some info */ - spin_lock_irq(¤t->sigmask_lock); - signr = dequeue_signal(¤t->blocked, &info); - spin_unlock_irq(¤t->sigmask_lock); - - if(signr == SIGUSR1) { - printk("UHCI queue dump:\n"); - show_queues(uhci); - } else { - break; - } - } + return -1; } -#if 0 - if(uhci->root_hub) - for(i = 0; i < uhci->root_hub->usb->maxchild; i++) - usb_disconnect(uhci->root_hub->usb->children + i); -#endif - - cleanup_drivers(); - - reset_hc(uhci); - release_region(uhci->io_addr, 32); - - release_uhci(uhci); - MOD_DEC_USE_COUNT; - - printk("uhci_control_thread exiting\n"); - return 0; -} +} /* * If we've successfully found a UHCI, now is the time to increment the - * module usage count, start the control thread, and return success.. + * module usage count, and return success.. */ -static int found_uhci(int irq, unsigned int io_addr) +static int setup_uhci(struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) { int retval; struct uhci *uhci; + char buf[8], *bufp = buf; + +#ifndef __sparc__ + sprintf(buf, "%d", irq); +#else + bufp = __irq_itoa(irq); +#endif + printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", + io_addr, bufp); - uhci = alloc_uhci(io_addr); + uhci = alloc_uhci(io_addr, io_size); if (!uhci) return -ENOMEM; + INIT_LIST_HEAD(&uhci->uhci_list); + list_add(&uhci->uhci_list, &uhci_list); + + request_region(uhci->io_addr, io_size, "usb-uhci"); + reset_hc(uhci); - retval = -EBUSY; - if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb", uhci) == 0) { - int pid; + usb_register_bus(uhci->bus); + start_hc(uhci); - MOD_INC_USE_COUNT; + retval = -EBUSY; + if (request_irq(irq, uhci_interrupt, SA_SHIRQ, "usb-uhci", uhci) == 0) { uhci->irq = irq; - pid = kernel_thread(uhci_control_thread, uhci, CLONE_FS | CLONE_FILES | CLONE_SIGHAND); - if (pid >= 0) - return 0; - MOD_DEC_USE_COUNT; - retval = pid; + if (!uhci_start_root_hub(uhci)) { + struct pm_dev *pmdev; + + pmdev = pm_register(PM_PCI_DEV, + PM_PCI_ID(dev), + handle_pm_event); + if (pmdev) + pmdev->data = uhci; + return 0; + } } + + /* Couldn't allocate IRQ if we got here */ + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); release_uhci(uhci); + return retval; } -static int start_uhci(struct pci_dev *dev) +static int found_uhci(struct pci_dev *dev) { int i; + /* disable legacy emulation */ + pci_write_config_word(dev, USBLEGSUP, USBLEGSUP_DEFAULT); + + if (pci_enable_device(dev) < 0) + return -1; + + if (!dev->irq) { + err("found UHCI device with no IRQ assigned. check BIOS settings!"); + return -1; + } + /* Search for the IO base address.. */ for (i = 0; i < 6; i++) { - unsigned int io_addr = dev->base_address[i]; + unsigned int io_addr = pci_resource_start(dev, i); + unsigned int io_size = pci_resource_len(dev, i); /* IO address? */ - if (!(io_addr & 1)) + if (!(pci_resource_flags(dev, i) & IORESOURCE_IO)) continue; - io_addr &= PCI_BASE_ADDRESS_IO_MASK; - /* Is it already in use? */ - if (check_region(io_addr, 32)) + if (check_region(io_addr, io_size)) break; - return found_uhci(dev->irq, io_addr); + return setup_uhci(dev, dev->irq, io_addr, io_size); } + return -1; } -#ifdef CONFIG_APM -static int handle_apm_event(apm_event_t event) +static int handle_pm_event(struct pm_dev *dev, pm_request_t rqst, void *data) { - static int down = 0; - - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - if (down) { - printk(KERN_DEBUG "uhci: received extra suspend event\n"); - break; - } - down = 1; + struct uhci *uhci = dev->data; + switch (rqst) { + case PM_SUSPEND: + reset_hc(uhci); break; - case APM_NORMAL_RESUME: - case APM_CRITICAL_RESUME: - if (!down) { - printk(KERN_DEBUG "uhci: received bogus resume event\n"); - break; - } - down = 0; - if (waitqueue_active(&uhci_configure)) { - apm_resume = 1; - wake_up(&uhci_configure); - } + case PM_RESUME: + reset_hc(uhci); + start_hc(uhci); break; } return 0; } -#endif - -#ifdef MODULE - -void cleanup_module(void) -{ -#ifdef CONFIG_APM - apm_unregister_callback(&handle_apm_event); -#endif -} - -#define uhci_init init_module - -#endif int uhci_init(void) { int retval; - struct pci_dev *dev = NULL; + struct pci_dev *dev; u8 type; + retval = -ENOMEM; + + /* We throw all of the TD's and QH's into a kmem cache */ + /* TD's and QH's need to be 16 byte aligned and SLAB_HWCACHE_ALIGN */ + /* does this for us */ + uhci_td_cachep = kmem_cache_create("uhci_td", + sizeof(struct uhci_td), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_td_cachep) + goto td_failed; + + uhci_qh_cachep = kmem_cache_create("uhci_qh", + sizeof(struct uhci_qh), 0, + SLAB_HWCACHE_ALIGN, NULL, NULL); + + if (!uhci_qh_cachep) + goto qh_failed; + + uhci_up_cachep = kmem_cache_create("uhci_urb_priv", + sizeof(struct urb_priv), 0, 0, NULL, NULL); + + if (!uhci_up_cachep) + goto up_failed; + retval = -ENODEV; + dev = NULL; for (;;) { - dev = pci_find_class(PCI_CLASS_SERIAL_USB<<8, dev); + dev = pci_find_class(PCI_CLASS_SERIAL_USB << 8, dev); if (!dev) break; - /* Is it UHCI */ + + /* Is it the UHCI programming interface? */ pci_read_config_byte(dev, PCI_CLASS_PROG, &type); - if(type != 0) + if (type != 0) continue; + /* Ok set it up */ - retval = start_uhci(dev); - if (retval < 0) - continue; + retval = found_uhci(dev); + } -#ifdef CONFIG_USB_MOUSE - usb_mouse_init(); -#endif -#ifdef CONFIG_USB_KBD - usb_kbd_init(); -#endif - hub_init(); -#ifdef CONFIG_USB_AUDIO - usb_audio_init(); -#endif -#ifdef CONFIG_APM - apm_register_callback(&handle_apm_event); -#endif + /* We only want to return an error code if ther was an error */ + /* and we didn't find a UHCI controller */ + if (retval && list_empty(&uhci_list)) + goto init_failed; - return 0; - } + return 0; + +init_failed: + if (kmem_cache_destroy(uhci_up_cachep)) + printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); + +up_failed: + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + +qh_failed: + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); + +td_failed: return retval; } -void cleanup_drivers(void) +void uhci_cleanup(void) { - hub_cleanup(); -#ifdef CONFIG_USB_MOUSE - usb_mouse_cleanup(); -#endif + struct list_head *tmp, *head = &uhci_list; + + tmp = head->next; + while (tmp != head) { + struct uhci *uhci = list_entry(tmp, struct uhci, uhci_list); + + tmp = tmp->next; + + list_del(&uhci->uhci_list); + INIT_LIST_HEAD(&uhci->uhci_list); + + if (uhci->bus->root_hub) + usb_disconnect(&uhci->bus->root_hub); + + usb_deregister_bus(uhci->bus); + + reset_hc(uhci); + release_region(uhci->io_addr, uhci->io_size); + + uhci_free_pending_qhs(uhci); + + release_uhci(uhci); + } + + if (kmem_cache_destroy(uhci_up_cachep)) + printk(KERN_INFO "uhci: not all urb_priv's were freed\n"); + + if (kmem_cache_destroy(uhci_qh_cachep)) + printk(KERN_INFO "uhci: not all QH's were freed\n"); + + if (kmem_cache_destroy(uhci_td_cachep)) + printk(KERN_INFO "uhci: not all TD's were freed\n"); +} + +#ifdef MODULE +int init_module(void) +{ + return uhci_init(); +} + +void cleanup_module(void) +{ + pm_unregister_all(handle_pm_event); + uhci_cleanup(); } +#endif //MODULE + diff --git a/drivers/usb/uhci.h b/drivers/usb/uhci.h index f063356acc58..423f75309cec 100644 --- a/drivers/usb/uhci.h +++ b/drivers/usb/uhci.h @@ -2,8 +2,37 @@ #define __LINUX_UHCI_H #include +#include -#include "usb.h" +/* + * This nested spinlock code is courtesy of Davide Libenzi + */ +struct s_nested_lock { + spinlock_t lock; + void *uniq; + short int count; +}; + +#define nested_init(snl) \ + spin_lock_init(&(snl)->lock); \ + (snl)->uniq = NULL; \ + (snl)->count = 0; + +#define nested_lock(snl, flags) \ + if ((snl)->uniq == current) { \ + (snl)->count++; \ + flags = 0; /* No warnings */ \ + } else { \ + spin_lock_irqsave(&(snl)->lock, flags); \ + (snl)->count++; \ + (snl)->uniq = current; \ + } + +#define nested_unlock(snl, flags) \ + if (!--(snl)->count) { \ + (snl)->uniq = NULL; \ + spin_unlock_irqrestore(&(snl)->lock, flags); \ + } /* * Universal Host Controller Interface data structures and defines @@ -53,17 +82,83 @@ #define USBPORTSC_PR 0x0200 /* Port Reset */ #define USBPORTSC_SUSP 0x1000 /* Suspend */ -struct uhci_qh { - unsigned int link; /* Next queue */ - unsigned int element; /* Queue element pointer */ - int inuse; /* Inuse? */ - struct uhci_qh *skel; /* Skeleton head */ -} __attribute__((aligned(16))); +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7FF /* for UHCI controller TD */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ struct uhci_framelist { - unsigned int frame[1024]; + __u32 frame[UHCI_NUMFRAMES]; } __attribute__((aligned(4096))); +struct uhci_td; + +struct uhci_qh { + /* Hardware fields */ + __u32 link; /* Next queue */ + __u32 element; /* Queue element pointer */ + + /* Software fields */ + /* Can't use list_head since we want a specific order */ + struct usb_device *dev; /* The owning device */ + + struct uhci_qh *prevqh, *nextqh; + + struct list_head remove_list; +} __attribute__((aligned(16))); + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_C_ERR_SHIFT 27 +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7FF /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ + +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 +#define TD_PID 0xFF + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_expected_length(info) (((info >> 21) + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + /* * The documentation says "4 words for hardware, 4 words for software". * @@ -75,7 +170,7 @@ struct uhci_framelist { * On 64-bit machines we probably want to take advantage of the fact that * hw doesn't really care about the size of the sw-only area. * - * Alas, not anymore, we have more than 4 words of software, woops + * Alas, not anymore, we have more than 4 words for software, woops */ struct uhci_td { /* Hardware fields */ @@ -85,48 +180,14 @@ struct uhci_td { __u32 buffer; /* Software fields */ - struct list_head irq_list; /* Active interrupt list.. */ - usb_device_irq completed; /* Completion handler routine */ - unsigned int *backptr; /* Where to remove this from.. */ - void *dev_id; - int inuse; /* Inuse? */ - struct uhci_qh *qh; -} __attribute__((aligned(32))); - -/* - * Note the alignment requirements of the entries - * - * Each UHCI device has pre-allocated QH and TD entries. - * You can use more than the pre-allocated ones, but I - * don't see you usually needing to. - */ -struct uhci; - -#define UHCI_MAXTD 64 - -#define UHCI_MAXQH 16 + unsigned int *frameptr; /* Frame list pointer */ + struct uhci_td *prevtd, *nexttd; /* Previous and next TD in queue */ -/* The usb device part must be first! */ -struct uhci_device { - struct usb_device *usb; + struct usb_device *dev; + struct urb *urb; /* URB this TD belongs to */ - struct uhci *uhci; - struct uhci_qh qh[UHCI_MAXQH]; /* These are the "common" qh's for each device */ - struct uhci_td td[UHCI_MAXTD]; - - unsigned long data[16]; -}; - -#define uhci_to_usb(uhci) ((uhci)->usb) -#define usb_to_uhci(usb) ((struct uhci_device *)(usb)->hcpriv) - -/* - * The root hub pre-allocated QH's and TD's have - * some special global uses.. - */ -#define control_td td /* Td's 0-30 */ -/* This is only for the root hub's TD list */ -#define tick_td td[31] + struct list_head list; +} __attribute__((aligned(16))); /* * There are various standard queues. We set up several different @@ -151,49 +212,92 @@ struct uhci_device { * be scared, it kinda makes sense. Look at this wonderful picture care of * Linus: * - * generic-iso-QH -> dev1-iso-QH -> generic-irq-QH -> dev1-irq-QH -> ... - * | | | | - * End dev1-iso-TD1 End dev1-irq-TD1 - * | - * dev1-iso-TD2 - * | - * .... + * generic- -> dev1- -> generic- -> dev1- -> control- -> bulk- -> ... + * iso-QH iso-QH irq-QH irq-QH QH QH + * | | | | | | + * End dev1-iso-TD1 End dev1-irq-TD1 ... ... + * | + * dev1-iso-TD2 + * | + * .... * * This may vary a bit (the UHCI docs don't explicitly say you can put iso * transfers in QH's and all of their pictures don't have that either) but * other than that, that is what we're doing now * - * To keep with Linus' nomenclature, this is called the qh skeleton. These - * labels (below) are only signficant to the root hub's qh's + * And now we don't put Iso transfers in QH's, so we don't waste one on it + * --jerdfelt + * + * To keep with Linus' nomenclature, this is called the QH skeleton. These + * labels (below) are only signficant to the root hub's QH's */ -#define skel_iso_qh qh[0] -#define skel_int2_qh qh[1] -#define skel_int4_qh qh[2] -#define skel_int8_qh qh[3] -#define skel_int16_qh qh[4] -#define skel_int32_qh qh[5] -#define skel_int64_qh qh[6] -#define skel_int128_qh qh[7] -#define skel_int256_qh qh[8] - -#define skel_control_qh qh[9] - -#define skel_bulk0_qh qh[10] -#define skel_bulk1_qh qh[11] -#define skel_bulk2_qh qh[12] -#define skel_bulk3_qh qh[13] +#define UHCI_NUM_SKELTD 10 +#define skel_int1_td skeltd[0] +#define skel_int2_td skeltd[1] +#define skel_int4_td skeltd[2] +#define skel_int8_td skeltd[3] +#define skel_int16_td skeltd[4] +#define skel_int32_td skeltd[5] +#define skel_int64_td skeltd[6] +#define skel_int128_td skeltd[7] +#define skel_int256_td skeltd[8] +#define skel_term_td skeltd[9] /* To work around PIIX UHCI bug */ + +#define UHCI_NUM_SKELQH 4 +#define skel_ls_control_qh skelqh[0] +#define skel_hs_control_qh skelqh[1] +#define skel_bulk_qh skelqh[2] +#define skel_term_qh skelqh[3] /* - * These are significant to the devices allocation of QH's + * Search tree for determining where fits in the + * skelqh[] skeleton. + * + * An interrupt request should be placed into the slowest skelqh[] + * which meets the interval/period/frequency requirement. + * An interrupt request is allowed to be faster than but not slower. + * + * For a given , this function returns the appropriate/matching + * skelqh[] index value. + * + * NOTE: For UHCI, we don't really need int256_qh since the maximum interval + * is 255 ms. However, we do need an int1_qh since 1 is a valid interval + * and we should meet that frequency when requested to do so. + * This will require some change(s) to the UHCI skeleton. */ -#if 0 -#define iso_qh qh[0] -#define int_qh qh[1] /* We have 2 "common" interrupt QH's */ -#define control_qh qh[3] -#define bulk_qh qh[4] /* We have 4 "common" bulk QH's */ -#define extra_qh qh[8] /* The rest, anything goes */ -#endif +static inline int __interval_to_skel(int interval) +{ + if (interval < 16) { + if (interval < 4) { + if (interval < 2) + return 0; /* int1 for 0-1 ms */ + return 1; /* int2 for 2-3 ms */ + } + if (interval < 8) + return 2; /* int4 for 4-7 ms */ + return 3; /* int8 for 8-15 ms */ + } + if (interval < 64) { + if (interval < 32) + return 4; /* int16 for 16-31 ms */ + return 5; /* int32 for 32-63 ms */ + } + if (interval < 128) + return 6; /* int64 for 64-127 ms */ + return 7; /* int128 for 128-255 ms (Max.) */ +} + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; /* * This describes the full uhci information. @@ -202,28 +306,117 @@ struct uhci_device { * a subset of what the full implementation needs. */ struct uhci { + /* Grabbed from PCI */ int irq; unsigned int io_addr; + unsigned int io_size; + + struct list_head uhci_list; struct usb_bus *bus; -#if 0 - /* These are "standard" QH's for the entire bus */ - struct uhci_qh qh[UHCI_MAXQH]; -#endif - struct uhci_device *root_hub; /* Root hub device descriptor.. */ + struct uhci_td skeltd[UHCI_NUM_SKELTD]; /* Skeleton TD's */ + struct uhci_qh skelqh[UHCI_NUM_SKELQH]; /* Skeleton QH's */ + spinlock_t framelist_lock; struct uhci_framelist *fl; /* Frame list */ - struct list_head interrupt_list; /* List of interrupt-active TD's for this uhci */ + int fsbr; /* Full speed bandwidth reclamation */ + + spinlock_t qh_remove_lock; + struct list_head qh_remove_list; + + spinlock_t urb_remove_lock; + struct list_head urb_remove_list; + + struct s_nested_lock urblist_lock; + struct list_head urb_list; + + struct virt_root_hub rh; /* private data of the virtual root hub */ +}; + +struct urb_priv { + struct urb *urb; + + struct uhci_qh *qh; /* QH for this URB */ + + int fsbr : 1; /* URB turned on FSBR */ + int fsbr_timeout : 1; /* URB timed out on FSBR */ + int queued : 1; /* QH was queued (not linked in) */ + int short_control_packet : 1; /* If we get a short packet during */ + /* a control transfer, retrigger */ + /* the status phase */ + + unsigned long inserttime; /* In jiffies */ + + struct list_head list; + + struct list_head urb_queue_list; /* URB's linked together */ }; +/* ------------------------------------------------------------------------- + Virtual Root HUB + ------------------------------------------------------------------------- */ +/* destination of request */ +#define RH_DEVICE 0x00 +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + /* needed for the debugging code */ struct uhci_td *uhci_link_to_td(unsigned int element); /* Debugging code */ -void show_td(struct uhci_td * td); -void show_status(struct uhci *uhci); -void show_queues(struct uhci *uhci); +void uhci_show_td(struct uhci_td *td); +void uhci_show_status(struct uhci *uhci); +void uhci_show_urb_queue(struct urb *urb); +void uhci_show_queue(struct uhci_qh *qh); +void uhci_show_queues(struct uhci *uhci); #endif diff --git a/drivers/usb/usb-core.c b/drivers/usb/usb-core.c new file mode 100644 index 000000000000..d2ca4f3e0ec4 --- /dev/null +++ b/drivers/usb/usb-core.c @@ -0,0 +1,179 @@ +/* + * driver/usb/usb-core.c + * + * (C) Copyright David Waite 1999 + * based on code from usb.c, by Linus Torvalds + * + * The purpose of this file is to pull any and all generic modular code from + * usb.c and put it in a separate file. This way usb.c is kept as a generic + * library, while this file handles starting drivers, etc. + * + */ + +#include +#include +#include +#include + +/* + * USB core + */ + +int usb_hub_init(void); +void usb_hub_cleanup(void); +int usb_major_init(void); +void usb_major_cleanup(void); + +/* + * USB device drivers + */ + +int input_init_module(void); +int acm_init_module(void); +int usb_dc2xx_init_module(void); +int evdev_init_module(void); +int hid_init_module(void); +int joydev_init_module(void); +int keybdev_init_module(void); +int mousedev_init_module(void); +int microtek_drv_init_module(void); +int usblp_init_module(void); +int usb_ov511_init_module(void); +int usb_rio_init_module(void); +int usb_scanner_init_module(void); +int usb_serial_init_module(void); +int usb_stor_init_module(void); +int usb_kbd_init_module(void); +int usb_mouse_init_module(void); +int wacom_init_module(void); +int wmforce_init_module(void); + +int usb_audio_init(void); +int usb_ibmcam_init(void); +int dabusb_init(void); +int plusb_init(void); +int dsbr100_init(void); + +/* + * HCI drivers + */ + +int uhci_init(void); +int ohci_hcd_init(void); + +#ifdef MODULE + +/* + * Cleanup + */ + +void cleanup_module(void) +{ + usb_major_cleanup(); + usbdevfs_cleanup(); + usb_hub_cleanup(); +} + +/* + * Init + */ + +int init_module(void) +#else +int usb_init(void) +#endif +{ + usb_major_init(); + usbdevfs_init(); + usb_hub_init(); + +#ifndef CONFIG_USB_MODULE +#ifdef CONFIG_USB_AUDIO + usb_audio_init(); +#endif +#ifdef CONFIG_USB_IBMCAM + usb_ibmcam_init(); +#endif +#ifdef CONFIG_USB_DABUSB + dabusb_init(); +#endif +#ifdef CONFIG_USB_DSBR + dsbr100_init(); +#endif +#ifdef CONFIG_USB_PLUSB + plusb_init(); +#endif + +#if defined(CONFIG_INPUT_KEYBDEV) || defined(CONFIG_INPUT_MOUSEDEV) || \ + defined(CONFIG_INPUT_JOYDEV) || defined(CONFIG_INPUT_EVDEV) || \ + defined(CONFIG_INPUT_KBD) || defined(CONFIG_INPUT_MOUSE) || \ + defined(CONFIG_INPUT_WACOM) || defined(CONFIG_INPUT_WMFORCE) || \ + defined(CONFIG_INPUT_HID) + input_init_module(); +#endif +#ifdef CONFIG_USB_ACM + acm_init_module(); +#endif +#ifdef CONFIG_USB_DC2XX + usb_dc2xx_init_module(); +#endif +#ifdef CONFIG_INPUT_EVDEV + evdev_init_module(); +#endif +#ifdef CONFIG_USB_HID + hid_init_module(); +#endif +#ifdef CONFIG_INPUT_JOYDEV + joydev_init_module(); +#endif +#ifdef CONFIG_INPUT_KEYBDEV + keybdev_init_module(); +#endif +#ifdef CONFIG_INPUT_MOUSEDEV + mousedev_init_module(); +#endif +#ifdef CONFIG_USB_MICROTEK + microtek_drv_init_module(); +#endif +#ifdef CONFIG_USB_PRINTER + usblp_init_module(); +#endif +#ifdef CONFIG_VIDEO_OV511 + usb_ov511_init_module(); +#endif +#ifdef CONFIG_USB_RIO500 + usb_rio_init_module(); +#endif +#ifdef CONFIG_USB_SCANNER + usb_scanner_init_module(); +#endif +#ifdef CONFIG_USB_SERIAL + usb_serial_init_module(); +#endif +#ifdef CONFIG_USB_STORAGE + usb_stor_init_module(); +#endif +#ifdef CONFIG_USB_KBD + usb_kbd_init_module(); +#endif +#ifdef CONFIG_USB_MOUSE + usb_mouse_init_module(); +#endif +#ifdef CONFIG_USB_WACOM + wacom_init_module(); +#endif +#ifdef CONFIG_USB_WMFORCE + wmforce_init_module(); +#endif +#ifdef CONFIG_USB_UHCI + uhci_init(); +#endif +#ifdef CONFIG_USB_UHCI_ALT + uhci_init(); +#endif +#ifdef CONFIG_USB_OHCI + ohci_hcd_init(); +#endif +#endif + return 0; +} diff --git a/drivers/usb/usb-debug.c b/drivers/usb/usb-debug.c index 86d08cd787f1..e8da9e376588 100644 --- a/drivers/usb/usb-debug.c +++ b/drivers/usb/usb-debug.c @@ -4,31 +4,49 @@ * I just want these out of the way where they aren't in your * face, but so that you can still use them.. */ +#include +#include #include - -#include "usb.h" +#include +#include +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include static void usb_show_endpoint(struct usb_endpoint_descriptor *endpoint) { usb_show_endpoint_descriptor(endpoint); } -static void usb_show_interface(struct usb_interface_descriptor *interface) +static void usb_show_interface(struct usb_interface_descriptor *altsetting) { int i; - usb_show_interface_descriptor(interface); - for (i = 0 ; i < interface->bNumEndpoints; i++) - usb_show_endpoint(interface->endpoint + i); + usb_show_interface_descriptor(altsetting); + + for (i = 0; i < altsetting->bNumEndpoints; i++) + usb_show_endpoint(altsetting->endpoint + i); } static void usb_show_config(struct usb_config_descriptor *config) { - int i; + int i, j; + struct usb_interface *ifp; usb_show_config_descriptor(config); - for (i = 0 ; i < config->bNumInterfaces; i++) - usb_show_interface(config->interface + i); + for (i = 0; i < config->bNumInterfaces; i++) { + ifp = config->interface + i; + + if (!ifp) + break; + + printk("\n Interface: %d\n", i); + for (j = 0; j < ifp->num_altsetting; j++) + usb_show_interface(ifp->altsetting + j); + } } void usb_show_device(struct usb_device *dev) @@ -40,26 +58,54 @@ void usb_show_device(struct usb_device *dev) usb_show_config(dev->config + i); } - /* * Parse and show the different USB descriptors. */ void usb_show_device_descriptor(struct usb_device_descriptor *desc) { - printk(" USB version %x.%02x\n", desc->bcdUSB >> 8, desc->bcdUSB & 0xff); - printk(" Vendor: %04x\n", desc->idVendor); - printk(" Product: %04x\n", desc->idProduct); - printk(" Configurations: %d\n", desc->bNumConfigurations); - - printk(" Device Class: %d\n", desc->bDeviceClass); + if (!desc) + { + printk("Invalid USB device descriptor (NULL POINTER)\n"); + return; + } + printk(" Length = %2d%s\n", desc->bLength, + desc->bLength == USB_DT_DEVICE_SIZE ? "" : " (!!!)"); + printk(" DescriptorType = %02x\n", desc->bDescriptorType); + + printk(" USB version = %x.%02x\n", + desc->bcdUSB >> 8, desc->bcdUSB & 0xff); + printk(" Vendor:Product = %04x:%04x\n", + desc->idVendor, desc->idProduct); + printk(" MaxPacketSize0 = %d\n", desc->bMaxPacketSize0); + printk(" NumConfigurations = %d\n", desc->bNumConfigurations); + printk(" Device version = %x.%02x\n", + desc->bcdDevice >> 8, desc->bcdDevice & 0xff); + + printk(" Device Class:SubClass:Protocol = %02x:%02x:%02x\n", + desc->bDeviceClass, desc->bDeviceSubClass, desc->bDeviceProtocol); switch (desc->bDeviceClass) { case 0: printk(" Per-interface classes\n"); break; - case 9: + case USB_CLASS_AUDIO: + printk(" Audio device class\n"); + break; + case USB_CLASS_COMM: + printk(" Communications class\n"); + break; + case USB_CLASS_HID: + printk(" Human Interface Devices class\n"); + break; + case USB_CLASS_PRINTER: + printk(" Printer device class\n"); + break; + case USB_CLASS_MASS_STORAGE: + printk(" Mass Storage device class\n"); + break; + case USB_CLASS_HUB: printk(" Hub device class\n"); break; - case 0xff: + case USB_CLASS_VENDOR_SPEC: printk(" Vendor class\n"); break; default: @@ -67,11 +113,11 @@ void usb_show_device_descriptor(struct usb_device_descriptor *desc) } } -void usb_show_config_descriptor(struct usb_config_descriptor * desc) +void usb_show_config_descriptor(struct usb_config_descriptor *desc) { printk("Configuration:\n"); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 9 ? "" : " (!!!)"); + desc->bLength == USB_DT_CONFIG_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" wTotalLength = %04x\n", desc->wTotalLength); printk(" bNumInterfaces = %02x\n", desc->bNumInterfaces); @@ -81,47 +127,77 @@ void usb_show_config_descriptor(struct usb_config_descriptor * desc) printk(" MaxPower = %4dmA\n", desc->MaxPower * 2); } -void usb_show_interface_descriptor(struct usb_interface_descriptor * desc) +void usb_show_interface_descriptor(struct usb_interface_descriptor *desc) { - printk(" Interface:\n"); + printk(" Alternate Setting: %2d\n", desc->bAlternateSetting); printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 9 ? "" : " (!!!)"); + desc->bLength == USB_DT_INTERFACE_SIZE ? "" : " (!!!)"); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" bInterfaceNumber = %02x\n", desc->bInterfaceNumber); printk(" bAlternateSetting = %02x\n", desc->bAlternateSetting); printk(" bNumEndpoints = %02x\n", desc->bNumEndpoints); - printk(" bInterfaceClass = %02x\n", desc->bInterfaceClass); - printk(" bInterfaceSubClass = %02x\n", desc->bInterfaceSubClass); - printk(" bInterfaceProtocol = %02x\n", desc->bInterfaceProtocol); + printk(" bInterface Class:SubClass:Protocol = %02x:%02x:%02x\n", + desc->bInterfaceClass, desc->bInterfaceSubClass, desc->bInterfaceProtocol); printk(" iInterface = %02x\n", desc->iInterface); } -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor * desc) +void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *desc) { + char *LengthCommentString = (desc->bLength == + USB_DT_ENDPOINT_AUDIO_SIZE) ? " (Audio)" : (desc->bLength == + USB_DT_ENDPOINT_SIZE) ? "" : " (!!!)"; char *EndpointType[4] = { "Control", "Isochronous", "Bulk", "Interrupt" }; + printk(" Endpoint:\n"); - printk(" bLength = %4d%s\n", desc->bLength, - desc->bLength == 7 ? "" : " (!!!)"); + printk(" bLength = %4d%s\n", + desc->bLength, LengthCommentString); printk(" bDescriptorType = %02x\n", desc->bDescriptorType); printk(" bEndpointAddress = %02x (%s)\n", desc->bEndpointAddress, - (desc->bEndpointAddress & 0x80) ? "in" : "out"); + (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL ? "i/o" : + (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? "in" : "out"); printk(" bmAttributes = %02x (%s)\n", desc->bmAttributes, - EndpointType[3 & desc->bmAttributes]); + EndpointType[USB_ENDPOINT_XFERTYPE_MASK & desc->bmAttributes]); printk(" wMaxPacketSize = %04x\n", desc->wMaxPacketSize); printk(" bInterval = %02x\n", desc->bInterval); + + /* Audio extensions to the endpoint descriptor */ + if (desc->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) { + printk(" bRefresh = %02x\n", desc->bRefresh); + printk(" bSynchAddress = %02x\n", desc->bSynchAddress); + } } -void usb_show_hub_descriptor(struct usb_hub_descriptor * desc) +void usb_show_string(struct usb_device *dev, char *id, int index) { - int len = 7; - unsigned char *ptr = (unsigned char *) desc; - - printk("Interface:"); - while (len) { - printk(" %02x", *ptr); - ptr++; len--; - } - printk("\n"); + char *buf; + + if (!index) + return; + if (!(buf = kmalloc(256, GFP_KERNEL))) + return; + if (usb_string(dev, index, buf, 256) > 0) + printk(KERN_INFO "%s: %s\n", id, buf); + kfree(buf); } +void usb_dump_urb (purb_t purb) +{ + printk ("urb :%p\n", purb); + printk ("next :%p\n", purb->next); + printk ("dev :%p\n", purb->dev); + printk ("pipe :%08X\n", purb->pipe); + printk ("status :%d\n", purb->status); + printk ("transfer_flags :%08X\n", purb->transfer_flags); + printk ("transfer_buffer :%p\n", purb->transfer_buffer); + printk ("transfer_buffer_length:%d\n", purb->transfer_buffer_length); + printk ("actual_length :%d\n", purb->actual_length); + printk ("setup_packet :%p\n", purb->setup_packet); + printk ("start_frame :%d\n", purb->start_frame); + printk ("number_of_packets :%d\n", purb->number_of_packets); + printk ("interval :%d\n", purb->interval); + printk ("error_count :%d\n", purb->error_count); + printk ("context :%p\n", purb->context); + printk ("complete :%p\n", purb->complete); +} diff --git a/drivers/usb/usb-ohci.c b/drivers/usb/usb-ohci.c new file mode 100644 index 000000000000..61011d31aef9 --- /dev/null +++ b/drivers/usb/usb-ohci.c @@ -0,0 +1,2137 @@ +/* + * URB OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * + * [ Initialisation is based on Linus' ] + * [ uhci code and gregs ohci fragments ] + * [ (C) Copyright 1999 Linus Torvalds ] + * [ (C) Copyright 1999 Gregory P. Smith] + * + * + * History: + * + * v5.2 1999/12/07 URB 3rd preview, + * v5.1 1999/11/30 URB 2nd preview, cpia, (usb-scsi) + * v5.0 1999/11/22 URB Technical preview, Paul Mackerras powerbook susp/resume + * i386: HUB, Keyboard, Mouse, Printer + * + * v4.3 1999/10/27 multiple HCs, bulk_request + * v4.2 1999/09/05 ISO API alpha, new dev alloc, neg Error-codes + * v4.1 1999/08/27 Randy Dunlap's - ISO API first impl. + * v4.0 1999/08/18 + * v3.0 1999/06/25 + * v2.1 1999/05/09 code clean up + * v2.0 1999/05/04 + * v1.0 1999/04/27 initial release + * + + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#undef DEBUG +#include + +#include +#include +#include +#include + +#define OHCI_USE_NPS // force NoPowerSwitching mode + +#include "usb-ohci.h" + +#include +static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data); + +#ifdef CONFIG_PMAC_PBOOK +#include +#include +#endif + + +/* For initializing controller (mask in an HCFS mode too) */ +#define OHCI_CONTROL_INIT \ + (OHCI_CTRL_CBSR & 0x3) \ + | OHCI_CTRL_BLE | OHCI_CTRL_CLE | OHCI_CTRL_IE | OHCI_CTRL_PLE + +static DECLARE_WAIT_QUEUE_HEAD (op_wakeup); +static LIST_HEAD (ohci_hcd_list); +static spinlock_t usb_ed_lock = SPIN_LOCK_UNLOCKED; + +/*-------------------------------------------------------------------------* + * URB support functions + *-------------------------------------------------------------------------*/ + +/* free the private part of an URB */ + +static void urb_rm_priv (urb_t * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + int i; + + if (!urb_priv) return; + + for (i = 0; i < urb_priv->length; i++) { + if (urb_priv->td [i]) { + OHCI_FREE (urb_priv->td [i]); + } + } + kfree (urb->hcpriv); + urb->hcpriv = NULL; + +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +static int sohci_get_current_frame_number (struct usb_device * dev); + +/* debug| print the main components of an URB + * small: 0) header + data packets 1) just header */ + +static void urb_print (urb_t * urb, char * str, int small) +{ + unsigned int pipe= urb->pipe; + int i, len; + + if (!urb->dev || !urb->dev->bus) { + dbg("%s URB: no dev", str); + return; + } + + dbg("%s URB:[%4x] dev:%2d,ep:%2d-%c,type:%s,flags:%4x,len:%d/%d,stat:%d(%x)", + str, + sohci_get_current_frame_number (urb->dev), + usb_pipedevice (pipe), + usb_pipeendpoint (pipe), + usb_pipeout (pipe)? 'O': 'I', + usb_pipetype (pipe) < 2? (usb_pipeint (pipe)? "INTR": "ISOC"): + (usb_pipecontrol (pipe)? "CTRL": "BULK"), + urb->transfer_flags, + urb->actual_length, + urb->transfer_buffer_length, + urb->status, urb->status); + if (!small) { + if (usb_pipecontrol (pipe)) { + printk (KERN_DEBUG __FILE__ ": cmd(8):"); + for (i = 0; i < 8 ; i++) + printk (" %02x", ((__u8 *) urb->setup_packet) [i]); + printk ("\n"); + } + if (urb->transfer_buffer_length > 0 && urb->transfer_buffer) { + printk (KERN_DEBUG __FILE__ ": data(%d/%d):", + urb->actual_length, + urb->transfer_buffer_length); + len = usb_pipeout (pipe)? + urb->transfer_buffer_length: urb->actual_length; + for (i = 0; i < 16 && i < len; i++) + printk (" %02x", ((__u8 *) urb->transfer_buffer) [i]); + printk ("%s stat:%d\n", i < len? "...": "", urb->status); + } + } +} + +/* just for debugging; prints non-empty branches of the int ed tree inclusive iso eds*/ +void ep_print_int_eds (ohci_t * ohci, char * str) { + int i, j; + __u32 * ed_p; + for (i= 0; i < 32; i++) { + j = 5; + ed_p = &(ohci->hcca.int_table [i]); + if (*ed_p == 0) + continue; + printk (KERN_DEBUG __FILE__ ": %s branch int %2d(%2x):", str, i, i); + while (*ed_p != 0 && j--) { + ed_t *ed = (ed_t *) bus_to_virt(le32_to_cpup(ed_p)); + printk (" ed: %4x;", ed->hwINFO); + ed_p = &ed->hwNextED; + } + printk ("\n"); + } +} + + +static void ohci_dump_intr_mask (char *label, __u32 mask) +{ + dbg ("%s: 0x%08x%s%s%s%s%s%s%s%s%s", + label, + mask, + (mask & OHCI_INTR_MIE) ? " MIE" : "", + (mask & OHCI_INTR_OC) ? " OC" : "", + (mask & OHCI_INTR_RHSC) ? " RHSC" : "", + (mask & OHCI_INTR_FNO) ? " FNO" : "", + (mask & OHCI_INTR_UE) ? " UE" : "", + (mask & OHCI_INTR_RD) ? " RD" : "", + (mask & OHCI_INTR_SF) ? " SF" : "", + (mask & OHCI_INTR_WDH) ? " WDH" : "", + (mask & OHCI_INTR_SO) ? " SO" : "" + ); +} + +static void maybe_print_eds (char *label, __u32 value) +{ + if (value) + dbg ("%s %08x", label, value); +} + +static char *hcfs2string (int state) +{ + switch (state) { + case OHCI_USB_RESET: return "reset"; + case OHCI_USB_RESUME: return "resume"; + case OHCI_USB_OPER: return "operational"; + case OHCI_USB_SUSPEND: return "suspend"; + } + return "?"; +} + +// dump control and status registers +static void ohci_dump_status (ohci_t *controller) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp; + + temp = readl (®s->revision) & 0xff; + if (temp != 0x10) + dbg ("spec %d.%d", (temp >> 4), (temp & 0x0f)); + + temp = readl (®s->control); + dbg ("control: 0x%08x%s%s%s HCFS=%s%s%s%s%s CBSR=%d", temp, + (temp & OHCI_CTRL_RWE) ? " RWE" : "", + (temp & OHCI_CTRL_RWC) ? " RWC" : "", + (temp & OHCI_CTRL_IR) ? " IR" : "", + hcfs2string (temp & OHCI_CTRL_HCFS), + (temp & OHCI_CTRL_BLE) ? " BLE" : "", + (temp & OHCI_CTRL_CLE) ? " CLE" : "", + (temp & OHCI_CTRL_IE) ? " IE" : "", + (temp & OHCI_CTRL_PLE) ? " PLE" : "", + temp & OHCI_CTRL_CBSR + ); + + temp = readl (®s->cmdstatus); + dbg ("cmdstatus: 0x%08x SOC=%d%s%s%s%s", temp, + (temp & OHCI_SOC) >> 16, + (temp & OHCI_OCR) ? " OCR" : "", + (temp & OHCI_BLF) ? " BLF" : "", + (temp & OHCI_CLF) ? " CLF" : "", + (temp & OHCI_HCR) ? " HCR" : "" + ); + + ohci_dump_intr_mask ("intrstatus", readl (®s->intrstatus)); + ohci_dump_intr_mask ("intrenable", readl (®s->intrenable)); + // intrdisable always same as intrenable + // ohci_dump_intr_mask ("intrdisable", readl (®s->intrdisable)); + + maybe_print_eds ("ed_periodcurrent", readl (®s->ed_periodcurrent)); + + maybe_print_eds ("ed_controlhead", readl (®s->ed_controlhead)); + maybe_print_eds ("ed_controlcurrent", readl (®s->ed_controlcurrent)); + + maybe_print_eds ("ed_bulkhead", readl (®s->ed_bulkhead)); + maybe_print_eds ("ed_bulkcurrent", readl (®s->ed_bulkcurrent)); + + maybe_print_eds ("donehead", readl (®s->donehead)); +} + +static void ohci_dump_roothub (ohci_t *controller, int verbose) +{ + struct ohci_regs *regs = controller->regs; + __u32 temp, ndp, i; + + temp = readl (®s->roothub.a); + ndp = (temp & RH_A_NDP); + + if (verbose) { + dbg ("roothub.a: %08x POTPGT=%d%s%s%s%s%s NDP=%d", temp, + ((temp & RH_A_POTPGT) >> 24) & 0xff, + (temp & RH_A_NOCP) ? " NOCP" : "", + (temp & RH_A_OCPM) ? " OCPM" : "", + (temp & RH_A_DT) ? " DT" : "", + (temp & RH_A_NPS) ? " NPS" : "", + (temp & RH_A_PSM) ? " PSM" : "", + ndp + ); + temp = readl (®s->roothub.b); + dbg ("roothub.b: %08x PPCM=%04x DR=%04x", + temp, + (temp & RH_B_PPCM) >> 16, + (temp & RH_B_DR) + ); + temp = readl (®s->roothub.status); + dbg ("roothub.status: %08x%s%s%s%s%s%s", + temp, + (temp & RH_HS_CRWE) ? " CRWE" : "", + (temp & RH_HS_OCIC) ? " OCIC" : "", + (temp & RH_HS_LPSC) ? " LPSC" : "", + (temp & RH_HS_DRWE) ? " DRWE" : "", + (temp & RH_HS_OCI) ? " OCI" : "", + (temp & RH_HS_LPS) ? " LPS" : "" + ); + } + + for (i = 0; i < ndp; i++) { + temp = readl (®s->roothub.portstatus [i]); + dbg ("roothub.portstatus [%d] = 0x%08x%s%s%s%s%s%s%s%s%s%s%s%s", + i, + temp, + (temp & RH_PS_PRSC) ? " PRSC" : "", + (temp & RH_PS_OCIC) ? " OCIC" : "", + (temp & RH_PS_PSSC) ? " PSSC" : "", + (temp & RH_PS_PESC) ? " PESC" : "", + (temp & RH_PS_CSC) ? " CSC" : "", + + (temp & RH_PS_LSDA) ? " LSDA" : "", + (temp & RH_PS_PPS) ? " PPS" : "", + (temp & RH_PS_PRS) ? " PRS" : "", + (temp & RH_PS_POCI) ? " POCI" : "", + (temp & RH_PS_PSS) ? " PSS" : "", + + (temp & RH_PS_PES) ? " PES" : "", + (temp & RH_PS_CCS) ? " CCS" : "" + ); + } +} + +static void ohci_dump (ohci_t *controller, int verbose) +{ + dbg ("OHCI controller %p state", controller->regs); + + // dumps some of the state we know about + ohci_dump_status (controller); + if (verbose) + ep_print_int_eds (controller, "hcca"); + dbg ("hcca frame #%04x", controller->hcca.frame_no); + ohci_dump_roothub (controller, 1); +} + + +#endif + +/*-------------------------------------------------------------------------* + * Interface functions (URB) + *-------------------------------------------------------------------------*/ + +/* return a request to the completion handler */ + +static int sohci_return_urb (urb_t * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + urb_t * urbt; + unsigned long flags; + int i; + + /* just to be sure */ + if (!urb->complete) { + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + return -1; + } + + if (!urb_priv) return -1; /* urb already unlinked */ + +#ifdef DEBUG + urb_print (urb, "RET", usb_pipeout (urb->pipe)); +#endif + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + urb->complete (urb); /* call complete and requeue URB */ + urb->actual_length = 0; + urb->status = USB_ST_URB_PENDING; + if (urb_priv->state != URB_DEL) + td_submit_urb (urb); + break; + + case PIPE_ISOCHRONOUS: + for (urbt = urb->next; urbt && (urbt != urb); urbt = urbt->next); + if (urbt) { /* send the reply and requeue URB */ + urb->complete (urb); + + spin_lock_irqsave (&usb_ed_lock, flags); + urb->actual_length = 0; + urb->status = USB_ST_URB_PENDING; + urb->start_frame = urb_priv->ed->last_iso + 1; + if (urb_priv->state != URB_DEL) { + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + td_submit_urb (urb); + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + + } else { /* unlink URB, call complete */ + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + urb->complete (urb); + } + break; + + case PIPE_BULK: + case PIPE_CONTROL: /* unlink URB, call complete */ + urb_rm_priv (urb); + usb_dec_dev_use (urb->dev); + urb->complete (urb); + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* get a transfer request */ + +static int sohci_submit_urb (urb_t * urb) +{ + ohci_t * ohci; + ed_t * ed; + urb_priv_t * urb_priv; + unsigned int pipe = urb->pipe; + int i, size = 0; + unsigned long flags; + + if (!urb->dev || !urb->dev->bus) return -EINVAL; + + if (urb->hcpriv) return -EINVAL; /* urb already in use */ + +// if(usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) +// return -EPIPE; + + usb_inc_dev_use (urb->dev); + ohci = (ohci_t *) urb->dev->bus->hcpriv; + +#ifdef DEBUG + urb_print (urb, "SUB", usb_pipein (pipe)); +#endif + + /* a request to the virtual root hub */ + if (usb_pipedevice (pipe) == ohci->rh.devnum) + return rh_submit_urb (urb); + + /* when controller's hung, permit only hub cleanup attempts + * such as powering down ports */ + if (ohci->disabled) + return -ESHUTDOWN; + + /* every endpoint has a ed, locate and fill it */ + if (!(ed = ep_add_ed (urb->dev, pipe, urb->interval, 1))) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + + /* for the private part of the URB we need the number of TDs (size) */ + switch (usb_pipetype (pipe)) { + case PIPE_BULK: /* one TD for every 4096 Byte */ + size = (urb->transfer_buffer_length - 1) / 4096 + 1; + break; + case PIPE_ISOCHRONOUS: /* number of packets from URB */ + size = urb->number_of_packets; + for (i = 0; i < urb->number_of_packets; i++) { + urb->iso_frame_desc[i].actual_length = 0; + urb->iso_frame_desc[i].status = -EXDEV; + } + break; + case PIPE_CONTROL: /* 1 TD for setup, 1 for ACK and 1 for every 4096 B */ + size = (urb->transfer_buffer_length == 0)? 2: + (urb->transfer_buffer_length - 1) / 4096 + 3; + break; + case PIPE_INTERRUPT: /* one TD */ + size = 1; + + break; + } + + /* allocate the private part or the URB */ + urb_priv = kmalloc (sizeof (urb_priv_t) + size * sizeof (td_t *), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urb_priv) { + usb_dec_dev_use (urb->dev); + return -ENOMEM; + } + memset (urb_priv, 0, sizeof (urb_priv_t) + size * sizeof (td_t *)); + + /* fill the private part of the URB */ + urb->hcpriv = urb_priv; + urb_priv->length = size; + urb_priv->td_cnt = 0; + urb_priv->state = 0; + urb_priv->ed = ed; + + /* allocate the TDs */ + for (i = 0; i < size; i++) { + OHCI_ALLOC (urb_priv->td[i], sizeof (td_t)); + if (!urb_priv->td[i]) { + usb_dec_dev_use (urb->dev); + urb_rm_priv (urb); + return -ENOMEM; + } + } + spin_lock_irqsave (&usb_ed_lock, flags); + if (ed->state == ED_NEW || (ed->state & ED_DEL)) { + urb_rm_priv(urb); + usb_dec_dev_use (urb->dev); + spin_unlock_irqrestore(&usb_ed_lock, flags); + return -EINVAL; + } + + /* for ISOC transfers calculate start frame index */ + if (urb->transfer_flags & USB_ISO_ASAP) { + urb->start_frame = ((ed->state == ED_OPER)? (ed->last_iso + 1): + (le16_to_cpu (ohci->hcca.frame_no) + 10)) & 0xffff; + } + urb->status = USB_ST_URB_PENDING; + urb->actual_length = 0; + + if (ed->state != ED_OPER) /* link the ed into a chain if is not already */ + ep_link (ohci, ed); + + urb->status = USB_ST_URB_PENDING; + td_submit_urb (urb); /* fill the TDs and link it to the ed */ + + spin_unlock_irqrestore (&usb_ed_lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* deactivate all TDs and remove the private part of the URB */ + +static int sohci_unlink_urb (urb_t * urb) +{ + unsigned long flags; + ohci_t * ohci; + DECLARE_WAITQUEUE (wait, current); + + if (!urb) /* just to be sure */ + return -EINVAL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + ohci = (ohci_t *) urb->dev->bus->hcpriv; + +#ifdef DEBUG + urb_print (urb, "UNLINK", 1); +#endif + + if (usb_pipedevice (urb->pipe) == ohci->rh.devnum) { + usb_dec_dev_use(urb->dev); + return rh_unlink_urb (urb); /* a request to the virtual root hub */ + } + + if (urb->hcpriv) { + /* URB active? */ + if (urb->status == USB_ST_URB_PENDING && !ohci->disabled) { + urb_priv_t * urb_priv = urb->hcpriv; + urb_priv->state = URB_DEL; + + /* we want to delete the TDs of an URB from an ed + * request the deletion, it will be handled at the + * next USB-frame */ + + spin_lock_irqsave (&usb_ed_lock, flags); + ep_rm_ed (urb->dev, urb_priv->ed); + urb_priv->ed->state |= ED_URB_DEL; + spin_unlock_irqrestore (&usb_ed_lock, flags); + if (!(urb->transfer_flags & USB_ASYNC_UNLINK)) { + usb_dec_dev_use (urb->dev); + add_wait_queue (&op_wakeup, &wait); + current->state = TASK_UNINTERRUPTIBLE; + if (!schedule_timeout (HZ / 10)) /* wait until all TDs are deleted */ + err("unlink URB timeout!"); + remove_wait_queue (&op_wakeup, &wait); + urb->status = -ENOENT; + } else { + /* usb_dec_dev_use done in dl_del_list() */ + urb->status = -EINPROGRESS; + } + } else { + usb_dec_dev_use (urb->dev); + urb_rm_priv (urb); + if (urb->complete && (urb->transfer_flags & USB_ASYNC_UNLINK)) { + urb->complete (urb); + urb->status = 0; + } else + urb->status = -ENOENT; + } + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* allocate private data space for a usb device */ + +static int sohci_alloc_dev (struct usb_device *usb_dev) +{ + struct ohci_device * dev; + + dev = kmalloc (sizeof (*dev), GFP_KERNEL); + if (!dev) + return -ENOMEM; + + memset (dev, 0, sizeof (*dev)); + + usb_dev->hcpriv = dev; + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* free private data space of usb device */ + +static int sohci_free_dev (struct usb_device * usb_dev) +{ + unsigned long flags; + int i, cnt = 0; + ed_t * ed; + DECLARE_WAITQUEUE (wait, current); + struct ohci_device * dev = usb_to_ohci (usb_dev); + ohci_t * ohci = usb_dev->bus->hcpriv; + + if (!dev) return 0; + + if (usb_dev->devnum >= 0) { + + /* delete all TDs of all EDs */ + spin_lock_irqsave (&usb_ed_lock, flags); + for(i = 0; i < NUM_EDS; i++) { + ed = &(dev->ed[i]); + if (ed->state != ED_NEW) { + if (ed->state == ED_OPER) ep_unlink (ohci, ed); + ep_rm_ed (usb_dev, ed); + ed->state = ED_DEL; + cnt++; + } + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + + if (cnt > 0) { + add_wait_queue (&op_wakeup, &wait); + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout (HZ / 10); + remove_wait_queue (&op_wakeup, &wait); + } + } + kfree (dev); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* tell us the current USB frame number */ + +static int sohci_get_current_frame_number (struct usb_device *usb_dev) +{ + ohci_t * ohci = usb_dev->bus->hcpriv; + + return le16_to_cpu (ohci->hcca.frame_no); +} + +/*-------------------------------------------------------------------------*/ + +struct usb_operations sohci_device_operations = { + sohci_alloc_dev, + sohci_free_dev, + sohci_get_current_frame_number, + sohci_submit_urb, + sohci_unlink_urb +}; + +/*-------------------------------------------------------------------------* + * ED handling functions + *-------------------------------------------------------------------------*/ + +/* search for the right branch to insert an interrupt ed into the int tree + * do some load ballancing; + * returns the branch and + * sets the interval to interval = 2^integer (ld (interval)) */ + +static int ep_int_ballance (ohci_t * ohci, int interval, int load) +{ + int i, branch = 0; + + /* search for the least loaded interrupt endpoint branch of all 32 branches */ + for (i = 0; i < 32; i++) + if (ohci->ohci_int_load [branch] > ohci->ohci_int_load [i]) branch = i; + + branch = branch % interval; + for (i = branch; i < 32; i += interval) ohci->ohci_int_load [i] += load; + + return branch; +} + +/*-------------------------------------------------------------------------*/ + +/* 2^int( ld (inter)) */ + +static int ep_2_n_interval (int inter) +{ + int i; + for (i = 0; ((inter >> i) > 1 ) && (i < 5); i++); + return 1 << i; +} + +/*-------------------------------------------------------------------------*/ + +/* the int tree is a binary tree + * in order to process it sequentially the indexes of the branches have to be mapped + * the mapping reverses the bits of a word of num_bits length */ + +static int ep_rev (int num_bits, int word) +{ + int i, wout = 0; + + for (i = 0; i < num_bits; i++) wout |= (((word >> i) & 1) << (num_bits - i - 1)); + return wout; +} + +/*-------------------------------------------------------------------------*/ + +/* link an ed into one of the HC chains */ + +static int ep_link (ohci_t * ohci, ed_t * edi) +{ + int int_branch; + int i; + int inter; + int interval; + int load; + __u32 * ed_p; + volatile ed_t * ed = edi; + + ed->state = ED_OPER; + + switch (ed->type) { + case CTRL: + ed->hwNextED = 0; + if (ohci->ed_controltail == NULL) { + writel (virt_to_bus (ed), &ohci->regs->ed_controlhead); + } else { + ohci->ed_controltail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); + } + ed->ed_prev = ohci->ed_controltail; + ohci->ed_controltail = edi; + break; + + case BULK: + ed->hwNextED = 0; + if (ohci->ed_bulktail == NULL) { + writel (virt_to_bus (ed), &ohci->regs->ed_bulkhead); + } else { + ohci->ed_bulktail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); + } + ed->ed_prev = ohci->ed_bulktail; + ohci->ed_bulktail = edi; + break; + + case INT: + load = ed->int_load; + interval = ep_2_n_interval (ed->int_period); + ed->int_interval = interval; + int_branch = ep_int_ballance (ohci, interval, load); + ed->int_branch = int_branch; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]); + (*ed_p != 0) && (((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval >= interval); + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); + ed->hwNextED = *ed_p; + *ed_p = cpu_to_le32 (virt_to_bus (ed)); + } +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_INT"); +#endif + break; + + case ISO: + ed->hwNextED = 0; + ed->int_interval = 1; + if (ohci->ed_isotail != NULL) { + ohci->ed_isotail->hwNextED = cpu_to_le32 (virt_to_bus (ed)); + ed->ed_prev = ohci->ed_isotail; + } else { + for ( i = 0; i < 32; i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); + *ed_p != 0; + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); + *ed_p = cpu_to_le32 (virt_to_bus (ed)); + } + ed->ed_prev = NULL; + } + ohci->ed_isotail = edi; +#ifdef DEBUG + ep_print_int_eds (ohci, "LINK_ISO"); +#endif + break; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* unlink an ed from one of the HC chains. + * just the link to the ed is unlinked. + * the link from the ed still points to another operational ed or 0 + * so the HC can eventually finish the processing of the unlinked ed */ + +static int ep_unlink (ohci_t * ohci, ed_t * ed) +{ + int int_branch; + int i; + int inter; + int interval; + __u32 * ed_p; + + + switch (ed->type) { + case CTRL: + if (ed->ed_prev == NULL) { + writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_controlhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if(ohci->ed_controltail == ed) { + ohci->ed_controltail = ed->ed_prev; + } else { + ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case BULK: + if (ed->ed_prev == NULL) { + writel (le32_to_cpup (&ed->hwNextED), &ohci->regs->ed_bulkhead); + } else { + ed->ed_prev->hwNextED = ed->hwNextED; + } + if (ohci->ed_bulktail == ed) { + ohci->ed_bulktail = ed->ed_prev; + } else { + ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; + } + break; + + case INT: + int_branch = ed->int_branch; + interval = ed->int_interval; + + for (i = 0; i < ep_rev (6, interval); i += inter) { + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i) + int_branch]), inter = 1; + (*ed_p != 0) && (*ed_p != ed->hwNextED); + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED), + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval)) { + if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + } + } + for (i = int_branch; i < 32; i += interval) + ohci->ohci_int_load[i] -= ed->int_load; +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_INT"); +#endif + break; + + case ISO: + if (ohci->ed_isotail == ed) + ohci->ed_isotail = ed->ed_prev; + if (ed->hwNextED != 0) + ((ed_t *) bus_to_virt (le32_to_cpup (&ed->hwNextED)))->ed_prev = ed->ed_prev; + + if (ed->ed_prev != NULL) { + ed->ed_prev->hwNextED = ed->hwNextED; + } else { + for (i = 0; i < 32; i += inter) { + inter = 1; + for (ed_p = &(ohci->hcca.int_table[ep_rev (5, i)]); + *ed_p != 0; + ed_p = &(((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->hwNextED)) { + inter = ep_rev (6, ((ed_t *) bus_to_virt (le32_to_cpup (ed_p)))->int_interval); + if(((ed_t *) bus_to_virt (le32_to_cpup (ed_p))) == ed) { + *ed_p = ed->hwNextED; + break; + } + } + } + } +#ifdef DEBUG + ep_print_int_eds (ohci, "UNLINK_ISO"); +#endif + break; + } + ed->state = ED_UNLINK; + return 0; +} + + +/*-------------------------------------------------------------------------*/ + +/* add/reinit an endpoint; this should be done once at the usb_set_configuration command, + * but the USB stack is a little bit stateless so we do it at every transaction + * if the state of the ed is ED_NEW then a dummy td is added and the state is changed to ED_UNLINK + * in all other cases the state is left unchanged + * the ed info fields are setted anyway even though most of them should not change */ + +static ed_t * ep_add_ed (struct usb_device * usb_dev, unsigned int pipe, int interval, int load) +{ + ohci_t * ohci = usb_dev->bus->hcpriv; + td_t * td; + ed_t * ed_ret; + volatile ed_t * ed; + + + spin_lock (&usb_ed_lock); + + ed = ed_ret = &(usb_to_ohci (usb_dev)->ed[(usb_pipeendpoint (pipe) << 1) | + (usb_pipecontrol (pipe)? 0: usb_pipeout (pipe))]); + + if((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) + return NULL; /* pending delete request */ + + if (ed->state == ED_NEW) { + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); /* skip ed */ + OHCI_ALLOC (td, sizeof (*td)); /* dummy td; end of td list for ed */ + if(!td) return NULL; /* out of memory */ + ed->hwTailP = cpu_to_le32 (virt_to_bus (td)); + ed->hwHeadP = ed->hwTailP; + ed->state = ED_UNLINK; + ed->type = usb_pipetype (pipe); + usb_to_ohci (usb_dev)->ed_cnt++; + } + + ohci->dev[usb_pipedevice (pipe)] = usb_dev; + + ed->hwINFO = cpu_to_le32 (usb_pipedevice (pipe) + | usb_pipeendpoint (pipe) << 7 + | (usb_pipeisoc (pipe)? 0x8000: 0) + | (usb_pipecontrol (pipe)? 0: (usb_pipeout (pipe)? 0x800: 0x1000)) + | usb_pipeslow (pipe) << 13 + | usb_maxpacket (usb_dev, pipe, usb_pipeout (pipe)) << 16); + + if (ed->type == INT && ed->state == ED_UNLINK) { + ed->int_period = interval; + ed->int_load = load; + } + + spin_unlock(&usb_ed_lock); + return ed_ret; +} + +/*-------------------------------------------------------------------------*/ + +/* request the removal of an endpoint + * put the ep on the rm_list and request a stop of the bulk or ctrl list + * real removal is done at the next start of frame (SOF) hardware interrupt */ + +static void ep_rm_ed (struct usb_device * usb_dev, ed_t * ed) +{ + unsigned int frame; + ohci_t * ohci = usb_dev->bus->hcpriv; + + if ((ed->state & ED_DEL) || (ed->state & ED_URB_DEL)) return; + + ed->hwINFO |= cpu_to_le32 (OHCI_ED_SKIP); + + writel (OHCI_INTR_SF, &ohci->regs->intrstatus); + writel (OHCI_INTR_SF, &ohci->regs->intrenable); /* enable sof interrupt */ + + frame = le16_to_cpu (ohci->hcca.frame_no) & 0x1; + ed->ed_rm_list = ohci->ed_rm_list[frame]; + ohci->ed_rm_list[frame] = ed; + + switch (ed->type) { + case CTRL: /* stop CTRL list */ + writel (ohci->hc_control &= ~OHCI_CTRL_CLE, &ohci->regs->control); + break; + case BULK: /* stop BULK list */ + writel (ohci->hc_control &= ~OHCI_CTRL_BLE, &ohci->regs->control); + break; + } +} + +/*-------------------------------------------------------------------------* + * TD handling functions + *-------------------------------------------------------------------------*/ + +/* prepare a TD */ + +static void td_fill (unsigned int info, void * data, int len, urb_t * urb, int index) +{ + volatile td_t * td, * td_pt; + urb_priv_t * urb_priv = urb->hcpriv; + + if (index >= urb_priv->length) { + err("internal OHCI error: TD index > length"); + return; + } + + td_pt = urb_priv->td [index]; + /* fill the old dummy TD */ + td = urb_priv->td [index] = (td_t *) bus_to_virt (le32_to_cpup (&urb_priv->ed->hwTailP) & 0xfffffff0); + td->ed = urb_priv->ed; + td->index = index; + td->urb = urb; + td->hwINFO = cpu_to_le32 (info); + if ((td->ed->type & 3) == PIPE_ISOCHRONOUS) { + td->hwCBP = cpu_to_le32 (((!data || !len)? + 0 : virt_to_bus (data)) & 0xFFFFF000); + td->ed->last_iso = info & 0xffff; + } else { + td->hwCBP = cpu_to_le32 (((!data || !len)? 0 : virt_to_bus (data))); + } + td->hwBE = cpu_to_le32 ((!data || !len )? 0: virt_to_bus (data + len - 1)); + td->hwNextTD = cpu_to_le32 (virt_to_bus (td_pt)); + td->hwPSW [0] = cpu_to_le16 ((virt_to_bus (data) & 0x0FFF) | 0xE000); + td_pt->hwNextTD = 0; + td->ed->hwTailP = td->hwNextTD; + + td->next_dl_td = NULL; //td_pt; +} + +/*-------------------------------------------------------------------------*/ + +/* prepare all TDs of a transfer */ + +static void td_submit_urb (urb_t * urb) +{ + urb_priv_t * urb_priv = urb->hcpriv; + ohci_t * ohci = (ohci_t *) urb->dev->bus->hcpriv; + void * ctrl = urb->setup_packet; + void * data = urb->transfer_buffer; + int data_len = urb->transfer_buffer_length; + int cnt = 0; + __u32 info = 0; + unsigned int toggle = 0; + + /* OHCI handles the DATA-toggles itself, we just use the USB-toggle bits for reseting */ + if(usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe))) { + toggle = TD_T_TOGGLE; + } else { + toggle = TD_T_DATA0; + usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe), 1); + } + + urb_priv->td_cnt = 0; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_BULK: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_DP_IN ; + while(data_len > 4096) { + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, 4096, urb, cnt); + data += 4096; data_len -= 4096; cnt++; + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT : TD_CC | TD_R | TD_DP_IN ; + td_fill (info | (cnt? TD_T_TOGGLE:toggle), data, data_len, urb, cnt); + cnt++; + writel (OHCI_BLF, &ohci->regs->cmdstatus); /* start bulk list */ + break; + + case PIPE_INTERRUPT: + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_OUT | toggle: TD_CC | TD_R | TD_DP_IN | toggle; + td_fill (info, data, data_len, urb, cnt++); + break; + + case PIPE_CONTROL: + info = TD_CC | TD_DP_SETUP | TD_T_DATA0; + td_fill (info, ctrl, 8, urb, cnt++); + if (data_len > 0) { + info = usb_pipeout (urb->pipe)? + TD_CC | TD_R | TD_DP_OUT | TD_T_DATA1 : TD_CC | TD_R | TD_DP_IN | TD_T_DATA1; + td_fill (info, data, data_len, urb, cnt++); + } + info = usb_pipeout (urb->pipe)? + TD_CC | TD_DP_IN | TD_T_DATA1: TD_CC | TD_DP_OUT | TD_T_DATA1; + td_fill (info, NULL, 0, urb, cnt++); + writel (OHCI_CLF, &ohci->regs->cmdstatus); /* start Control list */ + break; + + case PIPE_ISOCHRONOUS: + for (cnt = 0; cnt < urb->number_of_packets; cnt++) { + td_fill (TD_CC|TD_ISO | ((urb->start_frame + cnt) & 0xffff), + (__u8 *) data + urb->iso_frame_desc[cnt].offset, + urb->iso_frame_desc[cnt].length, urb, cnt); + } + break; + } + if (urb_priv->length != cnt) + dbg("TD LENGTH %d != CNT %d", urb_priv->length, cnt); +} + +/*-------------------------------------------------------------------------* + * Done List handling functions + *-------------------------------------------------------------------------*/ + + +/* calculate the transfer length and update the urb */ + +static void dl_transfer_length(td_t * td) +{ + __u32 tdINFO, tdBE, tdCBP; + __u16 tdPSW; + urb_t * urb = td->urb; + urb_priv_t * urb_priv = urb->hcpriv; + int dlen = 0; + int cc = 0; + + tdINFO = le32_to_cpup (&td->hwINFO); + tdBE = le32_to_cpup (&td->hwBE); + tdCBP = le32_to_cpup (&td->hwCBP); + + + if (tdINFO & TD_ISO) { + tdPSW = le16_to_cpu (td->hwPSW[0]); + cc = (tdPSW >> 12) & 0xF; + if (cc < 0xE) { + if (usb_pipeout(urb->pipe)) { + dlen = urb->iso_frame_desc[td->index].length; + } else { + dlen = tdPSW & 0x3ff; + } + urb->actual_length += dlen; + urb->iso_frame_desc[td->index].actual_length = dlen; + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + + urb->iso_frame_desc[td->index].status = cc_to_error[cc]; + } + } else { /* BULK, INT, CONTROL DATA */ + if (!(usb_pipetype (urb->pipe) == PIPE_CONTROL && + ((td->index == 0) || (td->index == urb_priv->length - 1)))) { + if (tdBE != 0) { + if (td->hwCBP == 0) + urb->actual_length = bus_to_virt (tdBE) - urb->transfer_buffer + 1; + else + urb->actual_length = bus_to_virt (tdCBP) - urb->transfer_buffer; + } + } + } +} + +/*-------------------------------------------------------------------------*/ + +/* replies to the request have to be on a FIFO basis so + * we reverse the reversed done-list */ + +static td_t * dl_reverse_done_list (ohci_t * ohci) +{ + __u32 td_list_hc; + td_t * td_rev = NULL; + td_t * td_list = NULL; + urb_priv_t * urb_priv = NULL; + unsigned long flags; + + spin_lock_irqsave (&usb_ed_lock, flags); + + td_list_hc = le32_to_cpup (&ohci->hcca.done_head) & 0xfffffff0; + ohci->hcca.done_head = 0; + + while (td_list_hc) { + td_list = (td_t *) bus_to_virt (td_list_hc); + + if (TD_CC_GET (le32_to_cpup (&td_list->hwINFO))) { + urb_priv = (urb_priv_t *) td_list->urb->hcpriv; + dbg(" USB-error/status: %x : %p", + TD_CC_GET (le32_to_cpup (&td_list->hwINFO)), td_list); + if (td_list->ed->hwHeadP & cpu_to_le32 (0x1)) { + if (urb_priv && ((td_list->index + 1) < urb_priv->length)) { + td_list->ed->hwHeadP = + (urb_priv->td[urb_priv->length - 1]->hwNextTD & cpu_to_le32 (0xfffffff0)) | + (td_list->ed->hwHeadP & cpu_to_le32 (0x2)); + urb_priv->td_cnt += urb_priv->length - td_list->index - 1; + } else + td_list->ed->hwHeadP &= cpu_to_le32 (0xfffffff2); + } + } + + td_list->next_dl_td = td_rev; + td_rev = td_list; + td_list_hc = le32_to_cpup (&td_list->hwNextTD) & 0xfffffff0; + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + return td_list; +} + +/*-------------------------------------------------------------------------*/ + +/* there are some pending requests to remove + * - some of the eds (if ed->state & ED_DEL (set by sohci_free_dev) + * - some URBs/TDs if urb_priv->state == URB_DEL */ + +static void dl_del_list (ohci_t * ohci, unsigned int frame) +{ + unsigned long flags; + ed_t * ed; + __u32 edINFO; + __u32 tdINFO; + td_t * td = NULL, * td_next = NULL, * tdHeadP = NULL, * tdTailP; + __u32 * td_p; + int ctrl = 0, bulk = 0; + + spin_lock_irqsave (&usb_ed_lock, flags); + for (ed = ohci->ed_rm_list[frame]; ed != NULL; ed = ed->ed_rm_list) { + + tdTailP = bus_to_virt (le32_to_cpup (&ed->hwTailP) & 0xfffffff0); + tdHeadP = bus_to_virt (le32_to_cpup (&ed->hwHeadP) & 0xfffffff0); + edINFO = le32_to_cpup (&ed->hwINFO); + td_p = &ed->hwHeadP; + + for (td = tdHeadP; td != tdTailP; td = td_next) { + urb_t * urb = td->urb; + urb_priv_t * urb_priv = td->urb->hcpriv; + + td_next = bus_to_virt (le32_to_cpup (&td->hwNextTD) & 0xfffffff0); + if ((urb_priv->state == URB_DEL) || (ed->state & ED_DEL)) { + tdINFO = le32_to_cpup (&td->hwINFO); + if (TD_CC_GET (tdINFO) < 0xE) dl_transfer_length (td); + *td_p = td->hwNextTD | (*td_p & cpu_to_le32 (0x3)); + if(++ (urb_priv->td_cnt) == urb_priv->length) + urb_rm_priv (urb); + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + usb_dec_dev_use (urb->dev); + urb->status = -ECONNRESET; + urb->complete (urb); + } else { + wake_up (&op_wakeup); + } + } else { + td_p = &td->hwNextTD; + } + + } + if (ed->state & ED_DEL) { /* set by sohci_free_dev */ + struct ohci_device * dev = usb_to_ohci (ohci->dev[edINFO & 0x7F]); + OHCI_FREE (tdTailP); /* free dummy td */ + ed->hwINFO = cpu_to_le32 (OHCI_ED_SKIP); + ed->state = ED_NEW; + /* if all eds are removed wake up sohci_free_dev */ + if (!--dev->ed_cnt) + wake_up (&op_wakeup); + } + else { + ed->state &= ~ED_URB_DEL; + ed->hwINFO &= ~cpu_to_le32 (OHCI_ED_SKIP); + } + + if ((ed->type & 3) == CTRL) ctrl |= 1; + if ((ed->type & 3) == BULK) bulk |= 1; + } + + if (ctrl) writel (0, &ohci->regs->ed_controlcurrent); /* reset CTRL list */ + if (bulk) writel (0, &ohci->regs->ed_bulkcurrent); /* reset BULK list */ + if (!ohci->ed_rm_list[!frame]) { /* enable CTRL and BULK lists */ + ohci->hc_control |= OHCI_CTRL_CLE | OHCI_CTRL_BLE; + writel (ohci->hc_control, &ohci->regs->control); + } + ohci->ed_rm_list[frame] = NULL; + + spin_unlock_irqrestore (&usb_ed_lock, flags); +} + + + +/*-------------------------------------------------------------------------*/ + +/* td done list */ + +static void dl_done_list (ohci_t * ohci, td_t * td_list) +{ + td_t * td_list_next = NULL; + ed_t * ed; + int cc = 0; + urb_t * urb; + urb_priv_t * urb_priv; + __u32 tdINFO, edHeadP, edTailP; + + unsigned long flags; + + while (td_list) { + td_list_next = td_list->next_dl_td; + + urb = td_list->urb; + urb_priv = urb->hcpriv; + tdINFO = le32_to_cpup (&td_list->hwINFO); + + ed = td_list->ed; + + dl_transfer_length(td_list); + + /* error code of transfer */ + cc = TD_CC_GET (tdINFO); + if( cc == TD_CC_STALL) usb_endpoint_halt(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe)); + + if (!(urb->transfer_flags & USB_DISABLE_SPD) && (cc == TD_DATAUNDERRUN)) + cc = TD_CC_NOERROR; + if (++(urb_priv->td_cnt) == urb_priv->length) { + if (urb_priv->state != URB_DEL && !(ed->state & ED_DEL) && ed->state != ED_NEW) { + urb->status = cc_to_error[cc]; + sohci_return_urb (urb); + } else { + urb_rm_priv (urb); + } + } + + spin_lock_irqsave (&usb_ed_lock, flags); + if (ed->state != ED_NEW) { + edHeadP = le32_to_cpup (&ed->hwHeadP) & 0xfffffff0; + edTailP = le32_to_cpup (&ed->hwTailP); + + if((edHeadP == edTailP) && (ed->state == ED_OPER)) + ep_unlink (ohci, ed); /* unlink eds if they are not busy */ + + } + spin_unlock_irqrestore (&usb_ed_lock, flags); + + td_list = td_list_next; + } +} + + + + +/*-------------------------------------------------------------------------* + * Virtual Root Hub + *-------------------------------------------------------------------------*/ + +/* Device descriptor */ +static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x10, /* __u16 bcdUSB; v1.1 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x02, /* __u16 ep_wMaxPacketSize; ((MAX_ROOT_PORTS + 1) / 8 */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + +/* Hub class-specific descriptor is constructed dynamically */ + + +/*-------------------------------------------------------------------------*/ + +/* prepare Interrupt pipe data; HUB INTERRUPT ENDPOINT */ + +static int rh_send_irq (ohci_t * ohci, void * rh_data, int rh_len) +{ + int num_ports; + int i; + int ret; + int len; + + __u8 data[8]; + + num_ports = readl (&ohci->regs->roothub.a) & RH_A_NDP; + *(__u8 *) data = (readl (&ohci->regs->roothub.status) & (RH_HS_LPSC | RH_HS_OCIC)) + ? 1: 0; + ret = *(__u8 *) data; + + for ( i = 0; i < num_ports; i++) { + *(__u8 *) (data + (i + 1) / 8) |= + ((readl (&ohci->regs->roothub.portstatus[i]) & + (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC | RH_PS_PRSC)) + ? 1: 0) << ((i + 1) % 8); + ret += *(__u8 *) (data + (i + 1) / 8); + } + len = i/8 + 1; + + if (ret > 0) { + memcpy (rh_data, data, min (len, min (rh_len, sizeof(data)))); + return len; + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Virtual Root Hub INTs are polled by this timer every "interval" ms */ + +static void rh_int_timer_do (unsigned long ptr) +{ + int len; + + urb_t * urb = (urb_t *) ptr; + ohci_t * ohci = urb->dev->bus->hcpriv; + + if (ohci->disabled) + return; + + if(ohci->rh.send) { + len = rh_send_irq (ohci, urb->transfer_buffer, urb->transfer_buffer_length); + if (len > 0) { + urb->actual_length = len; +#ifdef DEBUG + urb_print (urb, "RET-t(rh)", usb_pipeout (urb->pipe)); +#endif + if (urb->complete) urb->complete (urb); + } + } + rh_init_int_timer (urb); +} + +/*-------------------------------------------------------------------------*/ + +/* Root Hub INTs are polled by this timer */ + +static int rh_init_int_timer (urb_t * urb) +{ + ohci_t * ohci = urb->dev->bus->hcpriv; + + ohci->rh.interval = urb->interval; + init_timer (&ohci->rh.rh_int_timer); + ohci->rh.rh_int_timer.function = rh_int_timer_do; + ohci->rh.rh_int_timer.data = (unsigned long) urb; + ohci->rh.rh_int_timer.expires = + jiffies + (HZ * (urb->interval < 30? 30: urb->interval)) / 1000; + add_timer (&ohci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#define OK(x) len = (x); break +#define WR_RH_STAT(x) writel((x), &ohci->regs->roothub.status) +#define WR_RH_PORTSTAT(x) writel((x), &ohci->regs->roothub.portstatus[wIndex-1]) +#define RD_RH_STAT readl(&ohci->regs->roothub.status) +#define RD_RH_PORTSTAT readl(&ohci->regs->roothub.portstatus[wIndex-1]) + +/* request to virtual root hub */ + +static int rh_submit_urb (urb_t * urb) +{ + struct usb_device * usb_dev = urb->dev; + ohci_t * ohci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest * cmd = (devrequest *) urb->setup_packet; + void * data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = TD_CC_NOERROR; + + __u32 datab[4]; + __u8 * data_buf = (__u8 *) datab; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipeint(pipe)) { + + ohci->rh.urb = urb; + ohci->rh.send = 1; + ohci->rh.interval = urb->interval; + rh_init_int_timer(urb); + urb->status = cc_to_error [TD_CC_NOERROR]; + + return 0; + } + + bmRType_bReq = cmd->requesttype | (cmd->request << 8); + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + + dbg ("rh_submit_urb, req = %d(%x) len=%d", bmRType_bReq, + bmRType_bReq, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data_buf = cpu_to_le16 (1); OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data_buf = cpu_to_le16 (0); OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 ( + RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE)); + OK (4); + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + *(__u32 *) data_buf = cpu_to_le32 (RD_RH_PORTSTAT); OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case RH_C_HUB_LOCAL_POWER: + OK(0); + case (RH_C_HUB_OVER_CURRENT): + WR_RH_STAT(RH_HS_OCIC); OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_CCS ); OK (0); + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_POCI); OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_LSDA); OK (0); + case (RH_C_PORT_CONNECTION): + WR_RH_PORTSTAT (RH_PS_CSC ); OK (0); + case (RH_C_PORT_ENABLE): + WR_RH_PORTSTAT (RH_PS_PESC); OK (0); + case (RH_C_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSSC); OK (0); + case (RH_C_PORT_OVER_CURRENT): + WR_RH_PORTSTAT (RH_PS_OCIC); OK (0); + case (RH_C_PORT_RESET): + WR_RH_PORTSTAT (RH_PS_PRSC); OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + WR_RH_PORTSTAT (RH_PS_PSS ); OK (0); + case (RH_PORT_RESET): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PRS); + OK (0); + case (RH_PORT_POWER): + WR_RH_PORTSTAT (RH_PS_PPS ); OK (0); + case (RH_PORT_ENABLE): /* BUG IN HUP CODE *********/ + if (RD_RH_PORTSTAT & RH_PS_CCS) + WR_RH_PORTSTAT (RH_PS_PES ); + OK (0); + } + break; + + case RH_SET_ADDRESS: ohci->rh.devnum = wValue; OK(0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min (sizeof (root_hub_dev_des), wLength)); + data_buf = root_hub_dev_des; OK(len); + case (0x02): /* configuration descriptor */ + len = min (leni, min (sizeof (root_hub_config_des), wLength)); + data_buf = root_hub_config_des; OK(len); + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, + (int)(long) ohci->regs, "OHCI", + data, wLength); + if (len > 0) { + data_buf = data; + OK (min (leni, len)); + } + // else fallthrough + default: + status = TD_CC_STALL; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + { + __u32 temp = readl (&ohci->regs->roothub.a); + + data_buf [0] = 9; // min length; + data_buf [1] = 0x29; + data_buf [2] = temp & RH_A_NDP; + data_buf [3] = 0; + if (temp & RH_A_PSM) /* per-port power switching? */ + data_buf [3] |= 0x1; + if (temp & RH_A_NOCP) /* no overcurrent reporting? */ + data_buf [3] |= 0x10; + else if (temp & RH_A_OCPM) /* per-port overcurrent reporting? */ + data_buf [3] |= 0x8; + + datab [1] = 0; + data_buf [5] = (temp & RH_A_POTPGT) >> 24; + temp = readl (&ohci->regs->roothub.b); + data_buf [7] = temp & RH_B_DR; + if (data_buf [2] < 7) { + data_buf [8] = 0xff; + } else { + data_buf [0] += 2; + data_buf [8] = (temp & RH_B_DR) >> 8; + data_buf [10] = data_buf [9] = 0xff; + } + + len = min (leni, min (data_buf [0], wLength)); + OK (len); + } + + case RH_GET_CONFIGURATION: *(__u8 *) data_buf = 0x01; OK (1); + + case RH_SET_CONFIGURATION: WR_RH_STAT (0x10000); OK (0); + + default: + dbg ("unsupported root hub command"); + status = TD_CC_STALL; + } + +#ifdef DEBUG + ohci_dump_roothub (ohci, 0); +#endif + + len = min(len, leni); + if (data != data_buf) + memcpy (data, data_buf, len); + urb->actual_length = len; + urb->status = cc_to_error [status]; + +#ifdef DEBUG + urb_print (urb, "RET(rh)", usb_pipeout (urb->pipe)); +#endif + + if (urb->complete) urb->complete (urb); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int rh_unlink_urb (urb_t * urb) +{ + ohci_t * ohci = urb->dev->bus->hcpriv; + + if (ohci->rh.urb == urb) { + ohci->rh.send = 0; + del_timer (&ohci->rh.rh_int_timer); + } + return 0; +} + +/*-------------------------------------------------------------------------* + * HC functions + *-------------------------------------------------------------------------*/ + +/* reset the HC and BUS */ + +static int hc_reset (ohci_t * ohci) +{ + int timeout = 30; + int smm_timeout = 50; /* 0,5 sec */ + + if (readl (&ohci->regs->control) & OHCI_CTRL_IR) { /* SMM owns the HC */ + writel (OHCI_OCR, &ohci->regs->cmdstatus); /* request ownership */ + dbg("USB HC TakeOver from SMM"); + while (readl (&ohci->regs->control) & OHCI_CTRL_IR) { + wait_ms (10); + if (--smm_timeout == 0) { + err("USB HC TakeOver failed!"); + return -1; + } + } + } + + /* Disable HC interrupts */ + writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + + dbg("USB HC reset_hc: %x ;", readl (&ohci->regs->control)); + + /* Reset USB (needed by some controllers) */ + writel (0, &ohci->regs->control); + + /* HC Reset requires max 10 ms delay */ + writel (OHCI_HCR, &ohci->regs->cmdstatus); + while ((readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + if (--timeout == 0) { + err("USB HC reset timed out!"); + return -1; + } + udelay (1); + } + ohci->disabled = 0; + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* Start an OHCI controller, set the BUS operational + * enable interrupts + * connect the virtual root hub */ + +static int hc_start (ohci_t * ohci) +{ + __u32 mask; + unsigned int fminterval; + struct usb_device * usb_dev; + struct ohci_device * dev; + + /* Tell the controller where the control and bulk lists are + * The lists are empty now. */ + + writel (0, &ohci->regs->ed_controlhead); + writel (0, &ohci->regs->ed_bulkhead); + + writel (virt_to_bus (&ohci->hcca), &ohci->regs->hcca); /* a reset clears this */ + + fminterval = 0x2edf; + writel ((fminterval * 9) / 10, &ohci->regs->periodicstart); + fminterval |= ((((fminterval - 210) * 6) / 7) << 16); + writel (fminterval, &ohci->regs->fminterval); + writel (0x628, &ohci->regs->lsthresh); + + /* start controller operations */ + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci->regs->control); + + /* Choose the interrupts we care about now, others later on demand */ + mask = OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_WDH | OHCI_INTR_SO; + writel (mask, &ohci->regs->intrenable); + writel (mask, &ohci->regs->intrstatus); + +#ifdef OHCI_USE_NPS + writel ((readl(&ohci->regs->roothub.a) | RH_A_NPS) & ~RH_A_PSM, + &ohci->regs->roothub.a); + writel (RH_HS_LPSC, &ohci->regs->roothub.status); + // POTPGT delay is bits 24-31, in 2 ms units. + mdelay ((readl(&ohci->regs->roothub.a) >> 23) & 0x1fe); +#endif /* OHCI_USE_NPS */ + + /* connect the virtual root hub */ + ohci->rh.devnum = 0; + usb_dev = usb_alloc_dev (NULL, ohci->bus); + if (!usb_dev) + return -ENOMEM; + + dev = usb_to_ohci (usb_dev); + ohci->bus->root_hub = usb_dev; + usb_connect (usb_dev); + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -ENODEV; + } + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +/* an interrupt happens */ + +static void hc_interrupt (int irq, void * __ohci, struct pt_regs * r) +{ + ohci_t * ohci = __ohci; + struct ohci_regs * regs = ohci->regs; + int ints; + + if ((ohci->hcca.done_head != 0) && !(le32_to_cpup (&ohci->hcca.done_head) & 0x01)) { + ints = OHCI_INTR_WDH; + } else { + if ((ints = (readl (®s->intrstatus) & readl (®s->intrenable))) == 0) + return; + } + + // dbg("Interrupt: %x frame: %x", ints, le16_to_cpu (ohci->hcca.frame_no)); + + if (ints & OHCI_INTR_UE) { + ohci->disabled++; + err ("OHCI Unrecoverable Error, controller disabled"); + // e.g. due to PCI Master/Target Abort + } + + if (ints & OHCI_INTR_WDH) { + writel (OHCI_INTR_WDH, ®s->intrdisable); + dl_done_list (ohci, dl_reverse_done_list (ohci)); + writel (OHCI_INTR_WDH, ®s->intrenable); + } + + if (ints & OHCI_INTR_SO) { + dbg("USB Schedule overrun"); + writel (OHCI_INTR_SO, ®s->intrenable); + } + + if (ints & OHCI_INTR_SF) { + unsigned int frame = le16_to_cpu (ohci->hcca.frame_no) & 1; + writel (OHCI_INTR_SF, ®s->intrdisable); + if (ohci->ed_rm_list[!frame] != NULL) { + dl_del_list (ohci, !frame); + } + if (ohci->ed_rm_list[frame] != NULL) writel (OHCI_INTR_SF, ®s->intrenable); + } + writel (ints, ®s->intrstatus); + writel (OHCI_INTR_MIE, ®s->intrenable); +} + +/*-------------------------------------------------------------------------*/ + +/* reinitialize after controller reset */ + +static void hc_reinit_ohci (ohci_t *ohci) +{ + int i; + + /* for load balancing of the interrupt branches */ + for (i = 0; i < NUM_INTS; i++) ohci->ohci_int_load[i] = 0; + for (i = 0; i < NUM_INTS; i++) ohci->hcca.int_table[i] = 0; + + ohci->ed_rm_list [0] = NULL; + ohci->ed_rm_list [1] = NULL; + + /* end of control and bulk lists */ + ohci->ed_isotail = NULL; + ohci->ed_controltail = NULL; + ohci->ed_bulktail = NULL; +} + +/* allocate OHCI */ + +static ohci_t * hc_alloc_ohci (void * mem_base) +{ + ohci_t * ohci; + struct usb_bus * bus; + + ohci = (ohci_t *) __get_free_pages (GFP_KERNEL, 1); + if (!ohci) + return NULL; + + memset (ohci, 0, sizeof (ohci_t)); + + ohci->irq = -1; + ohci->regs = mem_base; + + bus = usb_alloc_bus (&sohci_device_operations); + if (!bus) { + free_pages ((unsigned long) ohci, 1); + return NULL; + } + + ohci->bus = bus; + bus->hcpriv = (void *) ohci; + + return ohci; +} + + +/*-------------------------------------------------------------------------*/ + +/* De-allocate all resources.. */ + +static void hc_release_ohci (ohci_t * ohci) +{ + dbg("USB HC release ohci"); + + /* disconnect all devices */ + if (ohci->bus->root_hub) + usb_disconnect (&ohci->bus->root_hub); + + hc_reset (ohci); + writel (OHCI_USB_RESET, &ohci->regs->control); + wait_ms (10); + + if (ohci->irq >= 0) { + free_irq (ohci->irq, ohci); + ohci->irq = -1; + } + + usb_deregister_bus (ohci->bus); + usb_free_bus (ohci->bus); + + /* unmap the IO address space */ + iounmap (ohci->regs); + + free_pages ((unsigned long) ohci, 1); +} + +/*-------------------------------------------------------------------------*/ + +/* Increment the module usage count, start the control thread and + * return success. */ + +static int hc_found_ohci (struct pci_dev *dev, int irq, void * mem_base) +{ + ohci_t * ohci; + char buf[8], *bufp = buf; + +#ifndef __sparc__ + sprintf(buf, "%d", irq); +#else + bufp = __irq_itoa(irq); +#endif + printk(KERN_INFO __FILE__ ": USB OHCI at membase 0x%lx, IRQ %s\n", + (unsigned long) mem_base, bufp); + + ohci = hc_alloc_ohci (mem_base); + if (!ohci) { + return -ENOMEM; + } + + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + list_add (&ohci->ohci_hcd_list, &ohci_hcd_list); + + if (hc_reset (ohci) < 0) { + hc_release_ohci (ohci); + return -ENODEV; + } + + writel (ohci->hc_control = OHCI_USB_RESET, &ohci->regs->control); + wait_ms (10); + usb_register_bus (ohci->bus); + + if (request_irq (irq, hc_interrupt, SA_SHIRQ, "usb-ohci", ohci) == 0) { + struct pm_dev *pmdev; + + ohci->irq = irq; + hc_start (ohci); + + pmdev = pm_register (PM_PCI_DEV, + PM_PCI_ID(dev), + handle_pm_event); + if (pmdev) + pmdev->data = ohci; + +#ifdef DEBUG + ohci_dump (ohci, 1); +#endif + + return 0; + } + err("request interrupt %d failed", irq); + hc_release_ohci (ohci); + return -EBUSY; +} + +/*-------------------------------------------------------------------------*/ + +static int hc_start_ohci (struct pci_dev * dev) +{ + unsigned long mem_base; + u16 cmd; + + if (pci_enable_device(dev) < 0) + return -ENODEV; + + pci_set_master (dev); + mem_base = pci_resource_start(dev, 0); + mem_base = (unsigned long) ioremap_nocache (mem_base, 4096); + + /* Some Mac firmware will switch memory response off */ + pci_read_config_word(dev, PCI_COMMAND, &cmd); + pci_write_config_word(dev, PCI_COMMAND, cmd | PCI_COMMAND_MEMORY); + + if (!mem_base) { + err("Error mapping OHCI memory"); + return -EFAULT; + } + return hc_found_ohci (dev, dev->irq, (void *) mem_base); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef CONFIG_PMAC_PBOOK + +/* On Powerbooks, put the controller into suspend mode when going + * to sleep, and do a resume when waking up. */ + +static int ohci_sleep_notify (struct pmu_sleep_notifier * self, int when) +{ + struct list_head * ohci_l; + ohci_t * ohci; + + for (ohci_l = ohci_hcd_list.next; ohci_l != &ohci_hcd_list; ohci_l = ohci_l->next) { + ohci = list_entry (ohci_l, ohci_t, ohci_hcd_list); + + switch (when) { + case PBOOK_SLEEP_NOW: + disable_irq (ohci->irq); + writel (ohci->hc_control = OHCI_USB_SUSPEND, &ohci->regs->control); + wait_ms (10); + break; + case PBOOK_WAKE: + writel (ohci->hc_control = OHCI_USB_RESUME, &ohci->regs->control); + wait_ms (20); + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci->regs->control); + enable_irq (ohci->irq); + break; + } + } + return PBOOK_SLEEP_OK; +} + +static struct pmu_sleep_notifier ohci_sleep_notifier = { + ohci_sleep_notify, SLEEP_LEVEL_MISC, +}; +#endif /* CONFIG_PMAC_PBOOK */ + +/*-------------------------------------------------------------------------*/ + +static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + ohci_t * ohci = (ohci_t*) dev->data; + int temp = 0; + + if (ohci) { + switch (rqst) { + case PM_SUSPEND: + /* act as if usb suspend can always be used */ + dbg("USB suspend: %p", ohci->regs); + ohci->hc_control = OHCI_USB_SUSPEND; + writel (ohci->hc_control, &ohci->regs->control); + wait_ms (10); + break; + + case PM_RESUME: + /* did we suspend, or were we powered off? */ + ohci->hc_control = readl (&ohci->regs->control); + temp = ohci->hc_control & OHCI_CTRL_HCFS; + switch (temp) { + + case OHCI_USB_RESET: // lost power + dbg("USB reset: %p", ohci->regs); + ohci->disabled = 1; + if (ohci->bus->root_hub) + usb_disconnect (&ohci->bus->root_hub); + hc_reinit_ohci (ohci); + if ((temp = hc_reset (ohci)) < 0 + || (temp = hc_start (ohci)) < 0) { + ohci->disabled = 1; + err ("can't restart, %d", temp); + } + dbg ("reset done"); + break; + + case OHCI_USB_SUSPEND: // host wakeup + case OHCI_USB_RESUME: // remote wakeup + dbg("USB resume: %p", ohci->regs); + ohci->hc_control = OHCI_USB_RESUME; + writel (ohci->hc_control, &ohci->regs->control); + wait_ms (20); + + ohci->hc_control = OHCI_CONTROL_INIT | OHCI_USB_OPER; + writel (ohci->hc_control, &ohci->regs->control); + break; + + default: + warn ("odd PM_RESUME"); + } + break; + } + } + return 0; +} + +/*-------------------------------------------------------------------------*/ + +#define PCI_CLASS_SERIAL_USB_OHCI 0x0C0310 + +int ohci_hcd_init (void) +{ + int ret = -ENODEV; + struct pci_dev * dev = NULL; + + while ((dev = pci_find_class (PCI_CLASS_SERIAL_USB_OHCI, dev))) { + if (hc_start_ohci(dev) >= 0) ret = 0; + } + +#ifdef CONFIG_PMAC_PBOOK + pmu_register_sleep_notifier (&ohci_sleep_notifier); +#endif + return ret; +} + +/*-------------------------------------------------------------------------*/ + +#ifdef MODULE +int init_module (void) +{ + return ohci_hcd_init (); +} + +/*-------------------------------------------------------------------------*/ + +void cleanup_module (void) +{ + ohci_t * ohci; + + pm_unregister_all (handle_pm_event); + +#ifdef CONFIG_PMAC_PBOOK + pmu_unregister_sleep_notifier (&ohci_sleep_notifier); +#endif + + while (!list_empty (&ohci_hcd_list)) { + ohci = list_entry (ohci_hcd_list.next, ohci_t, ohci_hcd_list); + list_del (&ohci->ohci_hcd_list); + INIT_LIST_HEAD (&ohci->ohci_hcd_list); + hc_release_ohci (ohci); + } +} +#endif //MODULE + diff --git a/drivers/usb/usb-ohci.h b/drivers/usb/usb-ohci.h new file mode 100644 index 000000000000..3ac5dafb24cb --- /dev/null +++ b/drivers/usb/usb-ohci.h @@ -0,0 +1,450 @@ + /* + * URB OHCI HCD (Host Controller Driver) for USB. + * + *(C) Copyright 1999 Roman Weissgaerber + * + * usb-ohci.h + * + */ + + +#define MODSTR "ohci: " + + +static int cc_to_error[16] = { + +/* mapping of the OHCI CC status to error codes */ +#ifdef USB_ST_CRC /* status codes */ + /* No Error */ USB_ST_NOERROR, + /* CRC Error */ USB_ST_CRC, + /* Bit Stuff */ USB_ST_BITSTUFF, + /* Data Togg */ USB_ST_CRC, + /* Stall */ USB_ST_STALL, + /* DevNotResp */ USB_ST_NORESPONSE, + /* PIDCheck */ USB_ST_BITSTUFF, + /* UnExpPID */ USB_ST_BITSTUFF, + /* DataOver */ USB_ST_DATAOVERRUN, + /* DataUnder */ USB_ST_DATAUNDERRUN, + /* reservd */ USB_ST_NORESPONSE, + /* reservd */ USB_ST_NORESPONSE, + /* BufferOver */ USB_ST_BUFFEROVERRUN, + /* BuffUnder */ USB_ST_BUFFERUNDERRUN, + /* Not Access */ USB_ST_NORESPONSE, + /* Not Access */ USB_ST_NORESPONSE +}; + +#else /* error codes */ + /* No Error */ 0, + /* CRC Error */ -EILSEQ, + /* Bit Stuff */ -EPROTO, + /* Data Togg */ -EILSEQ, + /* Stall */ -EPIPE, + /* DevNotResp */ -ETIMEDOUT, + /* PIDCheck */ -EPROTO, + /* UnExpPID */ -EPROTO, + /* DataOver */ -EOVERFLOW, + /* DataUnder */ -EREMOTEIO, + /* reservd */ -ETIMEDOUT, + /* reservd */ -ETIMEDOUT, + /* BufferOver */ -ECOMM, + /* BuffUnder */ -ECOMM, + /* Not Access */ -ETIMEDOUT, + /* Not Access */ -ETIMEDOUT +}; +#define USB_ST_URB_PENDING -EINPROGRESS +#endif + + + +struct ed; +struct td; +/* for ED and TD structures */ + +/* ED States */ + +#define ED_NEW 0x00 +#define ED_UNLINK 0x01 +#define ED_OPER 0x02 +#define ED_DEL 0x04 +#define ED_URB_DEL 0x08 + +/* usb_ohci_ed */ +typedef struct ed { + __u32 hwINFO; + __u32 hwTailP; + __u32 hwHeadP; + __u32 hwNextED; + + struct ed * ed_prev; + __u8 int_period; + __u8 int_branch; + __u8 int_load; + __u8 int_interval; + __u8 state; + __u8 type; + __u16 last_iso; + struct ed * ed_rm_list; + +} ed_t; + + +/* TD info field */ +#define TD_CC 0xf0000000 +#define TD_CC_GET(td_p) ((td_p >>28) & 0x0f) +#define TD_CC_SET(td_p, cc) (td_p) = ((td_p) & 0x0fffffff) | (((cc) & 0x0f) << 28) +#define TD_EC 0x0C000000 +#define TD_T 0x03000000 +#define TD_T_DATA0 0x02000000 +#define TD_T_DATA1 0x03000000 +#define TD_T_TOGGLE 0x00000000 +#define TD_R 0x00040000 +#define TD_DI 0x00E00000 +#define TD_DI_SET(X) (((X) & 0x07)<< 21) +#define TD_DP 0x00180000 +#define TD_DP_SETUP 0x00000000 +#define TD_DP_IN 0x00100000 +#define TD_DP_OUT 0x00080000 + +#define TD_ISO 0x00010000 +#define TD_DEL 0x00020000 + +/* CC Codes */ +#define TD_CC_NOERROR 0x00 +#define TD_CC_CRC 0x01 +#define TD_CC_BITSTUFFING 0x02 +#define TD_CC_DATATOGGLEM 0x03 +#define TD_CC_STALL 0x04 +#define TD_DEVNOTRESP 0x05 +#define TD_PIDCHECKFAIL 0x06 +#define TD_UNEXPECTEDPID 0x07 +#define TD_DATAOVERRUN 0x08 +#define TD_DATAUNDERRUN 0x09 +#define TD_BUFFEROVERRUN 0x0C +#define TD_BUFFERUNDERRUN 0x0D +#define TD_NOTACCESSED 0x0F + + +#define MAXPSW 1 + +typedef struct td { + __u32 hwINFO; + __u32 hwCBP; /* Current Buffer Pointer */ + __u32 hwNextTD; /* Next TD Pointer */ + __u32 hwBE; /* Memory Buffer End Pointer */ + __u16 hwPSW[MAXPSW]; + + __u8 type; + __u8 index; + struct ed * ed; + struct td * next_dl_td; + urb_t * urb; +} td_t; + + +/* TD types */ +#define BULK 0x03 +#define INT 0x01 +#define CTRL 0x02 +#define ISO 0x00 + +#define SEND 0x01 +#define ST_ADDR 0x02 +#define ADD_LEN 0x04 +#define DEL 0x08 + + +#define OHCI_ED_SKIP (1 << 14) + +/* + * The HCCA (Host Controller Communications Area) is a 256 byte + * structure defined in the OHCI spec. that the host controller is + * told the base address of. It must be 256-byte aligned. + */ + +#define NUM_INTS 32 /* part of the OHCI standard */ +struct ohci_hcca { + __u32 int_table[NUM_INTS]; /* Interrupt ED table */ + __u16 frame_no; /* current frame number */ + __u16 pad1; /* set to 0 on each frame_no change */ + __u32 done_head; /* info returned for an interrupt */ + u8 reserved_for_hc[116]; +} __attribute((aligned(256))); + + +/* + * Maximum number of root hub ports. + */ +#define MAX_ROOT_PORTS 15 /* maximum OHCI root hub ports */ + +/* + * This is the structure of the OHCI controller's memory mapped I/O + * region. This is Memory Mapped I/O. You must use the readl() and + * writel() macros defined in asm/io.h to access these!! + */ +struct ohci_regs { + /* control and status registers */ + __u32 revision; + __u32 control; + __u32 cmdstatus; + __u32 intrstatus; + __u32 intrenable; + __u32 intrdisable; + /* memory pointers */ + __u32 hcca; + __u32 ed_periodcurrent; + __u32 ed_controlhead; + __u32 ed_controlcurrent; + __u32 ed_bulkhead; + __u32 ed_bulkcurrent; + __u32 donehead; + /* frame counters */ + __u32 fminterval; + __u32 fmremaining; + __u32 fmnumber; + __u32 periodicstart; + __u32 lsthresh; + /* Root hub ports */ + struct ohci_roothub_regs { + __u32 a; + __u32 b; + __u32 status; + __u32 portstatus[MAX_ROOT_PORTS]; + } roothub; +} __attribute((aligned(32))); + + +/* OHCI CONTROL AND STATUS REGISTER MASKS */ + +/* + * HcControl (control) register masks + */ +#define OHCI_CTRL_CBSR (3 << 0) /* control/bulk service ratio */ +#define OHCI_CTRL_PLE (1 << 2) /* periodic list enable */ +#define OHCI_CTRL_IE (1 << 3) /* isochronous enable */ +#define OHCI_CTRL_CLE (1 << 4) /* control list enable */ +#define OHCI_CTRL_BLE (1 << 5) /* bulk list enable */ +#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ +#define OHCI_CTRL_IR (1 << 8) /* interrupt routing */ +#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ +#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ + +/* pre-shifted values for HCFS */ +# define OHCI_USB_RESET (0 << 6) +# define OHCI_USB_RESUME (1 << 6) +# define OHCI_USB_OPER (2 << 6) +# define OHCI_USB_SUSPEND (3 << 6) + +/* + * HcCommandStatus (cmdstatus) register masks + */ +#define OHCI_HCR (1 << 0) /* host controller reset */ +#define OHCI_CLF (1 << 1) /* control list filled */ +#define OHCI_BLF (1 << 2) /* bulk list filled */ +#define OHCI_OCR (1 << 3) /* ownership change request */ +#define OHCI_SOC (3 << 16) /* scheduling overrun count */ + +/* + * masks used with interrupt registers: + * HcInterruptStatus (intrstatus) + * HcInterruptEnable (intrenable) + * HcInterruptDisable (intrdisable) + */ +#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ +#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ +#define OHCI_INTR_SF (1 << 2) /* start frame */ +#define OHCI_INTR_RD (1 << 3) /* resume detect */ +#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ +#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ +#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ +#define OHCI_INTR_OC (1 << 30) /* ownership change */ +#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ + + + +/* Virtual Root HUB */ +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void * urb; + void * int_addr; + int send; + int interval; + struct timer_list rh_int_timer; +}; + + +/* USB HUB CONSTANTS (not OHCI-specific; see hub.h) */ + +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 + +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + + +/* OHCI ROOT HUB REGISTER MASKS */ + +/* roothub.portstatus [i] bits */ +#define RH_PS_CCS 0x00000001 /* current connect status */ +#define RH_PS_PES 0x00000002 /* port enable status*/ +#define RH_PS_PSS 0x00000004 /* port suspend status */ +#define RH_PS_POCI 0x00000008 /* port over current indicator */ +#define RH_PS_PRS 0x00000010 /* port reset status */ +#define RH_PS_PPS 0x00000100 /* port power status */ +#define RH_PS_LSDA 0x00000200 /* low speed device attached */ +#define RH_PS_CSC 0x00010000 /* connect status change */ +#define RH_PS_PESC 0x00020000 /* port enable status change */ +#define RH_PS_PSSC 0x00040000 /* port suspend status change */ +#define RH_PS_OCIC 0x00080000 /* over current indicator change */ +#define RH_PS_PRSC 0x00100000 /* port reset status change */ + +/* roothub.status bits */ +#define RH_HS_LPS 0x00000001 /* local power status */ +#define RH_HS_OCI 0x00000002 /* over current indicator */ +#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ +#define RH_HS_LPSC 0x00010000 /* local power status change */ +#define RH_HS_OCIC 0x00020000 /* over current indicator change */ +#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ + +/* roothub.b masks */ +#define RH_B_DR 0x0000ffff /* device removable flags */ +#define RH_B_PPCM 0xffff0000 /* port power control mask */ + +/* roothub.a masks */ +#define RH_A_NDP (0xff << 0) /* number of downstream ports */ +#define RH_A_PSM (1 << 8) /* power switching mode */ +#define RH_A_NPS (1 << 9) /* no power switching */ +#define RH_A_DT (1 << 10) /* device type (mbz) */ +#define RH_A_OCPM (1 << 11) /* over current protection mode */ +#define RH_A_NOCP (1 << 12) /* no over current protection */ +#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ + +#define min(a,b) (((a)<(b))?(a):(b)) + + +/* urb */ +typedef struct +{ + ed_t * ed; + __u16 length; // number of tds associated with this request + __u16 td_cnt; // number of tds already serviced + int state; + void * wait; + td_t * td[0]; // list pointer to all corresponding TDs associated with this request + +} urb_priv_t; +#define URB_DEL 1 + +/* + * This is the full ohci controller description + * + * Note how the "proper" USB information is just + * a subset of what the full implementation needs. (Linus) + */ + + +typedef struct ohci { + struct ohci_hcca hcca; /* hcca */ + + int irq; + int disabled; /* e.g. got a UE, we're hung */ + + struct ohci_regs * regs; /* OHCI controller's memory */ + struct list_head ohci_hcd_list; /* list of all ohci_hcd */ + + struct ohci * next; // chain of uhci device contexts + struct list_head urb_list; // list of all pending urbs + spinlock_t urb_list_lock; // lock to keep consistency + + int ohci_int_load[32]; /* load of the 32 Interrupt Chains (for load balancing)*/ + ed_t * ed_rm_list[2]; /* lists of all endpoints to be removed */ + ed_t * ed_bulktail; /* last endpoint of bulk list */ + ed_t * ed_controltail; /* last endpoint of control list */ + ed_t * ed_isotail; /* last endpoint of iso list */ + int intrstatus; + __u32 hc_control; /* copy of the hc control reg */ + struct usb_bus * bus; + struct usb_device * dev[128]; + struct virt_root_hub rh; +} ohci_t; + + +#define NUM_TDS 0 /* num of preallocated transfer descriptors */ +#define NUM_EDS 32 /* num of preallocated endpoint descriptors */ + +struct ohci_device { + ed_t ed[NUM_EDS]; + int ed_cnt; + void * wait; +}; + +// #define ohci_to_usb(ohci) ((ohci)->usb) +#define usb_to_ohci(usb) ((struct ohci_device *)(usb)->hcpriv) + +/* hcd */ +/* endpoint */ +static int ep_link(ohci_t * ohci, ed_t * ed); +static int ep_unlink(ohci_t * ohci, ed_t * ed); +static ed_t * ep_add_ed(struct usb_device * usb_dev, unsigned int pipe, int interval, int load); +static void ep_rm_ed(struct usb_device * usb_dev, ed_t * ed); +/* td */ +static void td_fill(unsigned int info, void * data, int len, urb_t * urb, int index); +static void td_submit_urb(urb_t * urb); +/* root hub */ +static int rh_submit_urb(urb_t * urb); +static int rh_unlink_urb(urb_t * urb); +static int rh_init_int_timer(urb_t * urb); + +#ifdef DEBUG +#define OHCI_FREE(x) kfree(x); printk("OHCI FREE: %d: %4x\n", -- __ohci_free_cnt, (unsigned int) x) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); printk("OHCI ALLO: %d: %4x\n", ++ __ohci_free_cnt,(unsigned int) x) +static int __ohci_free_cnt = 0; +#else +#define OHCI_FREE(x) kfree(x) +#define OHCI_ALLOC(x,size) (x) = kmalloc(size, in_interrupt() ? GFP_ATOMIC : GFP_KERNEL) +#endif + diff --git a/drivers/usb/usb-storage-debug.h b/drivers/usb/usb-storage-debug.h new file mode 100644 index 000000000000..1c5fcc0e2fe7 --- /dev/null +++ b/drivers/usb/usb-storage-debug.h @@ -0,0 +1,89 @@ +#include +#ifdef CONFIG_USB_STORAGE_DEBUG + +/* Debug output for Driver for USB mass storage (scsi-like) devices + * + * (C) Michael Gee (michael@linuxspecific.com) 1999 + * + */ + +void us_show_command(Scsi_Cmnd *srb) +{ + char *what = NULL; + + switch (srb->cmnd[0]) { + case TEST_UNIT_READY: what = "TEST_UNIT_READY"; break; + case REZERO_UNIT: what = "REZERO_UNIT"; break; + case REQUEST_SENSE: what = "REQUEST_SENSE"; break; + case FORMAT_UNIT: what = "FORMAT_UNIT"; break; + case READ_BLOCK_LIMITS: what = "READ_BLOCK_LIMITS"; break; + case REASSIGN_BLOCKS: what = "REASSIGN_BLOCKS"; break; + case READ_6: what = "READ_6"; break; + case WRITE_6: what = "WRITE_6"; break; + case SEEK_6: what = "SEEK_6"; break; + case READ_REVERSE: what = "READ_REVERSE"; break; + case WRITE_FILEMARKS: what = "WRITE_FILEMARKS"; break; + case SPACE: what = "SPACE"; break; + case INQUIRY: what = "INQUIRY"; break; + case RECOVER_BUFFERED_DATA: what = "RECOVER_BUFFERED_DATA"; break; + case MODE_SELECT: what = "MODE_SELECT"; break; + case RESERVE: what = "RESERVE"; break; + case RELEASE: what = "RELEASE"; break; + case COPY: what = "COPY"; break; + case ERASE: what = "ERASE"; break; + case MODE_SENSE: what = "MODE_SENSE"; break; + case START_STOP: what = "START_STOP"; break; + case RECEIVE_DIAGNOSTIC: what = "RECEIVE_DIAGNOSTIC"; break; + case SEND_DIAGNOSTIC: what = "SEND_DIAGNOSTIC"; break; + case ALLOW_MEDIUM_REMOVAL: what = "ALLOW_MEDIUM_REMOVAL"; break; + case SET_WINDOW: what = "SET_WINDOW"; break; + case READ_CAPACITY: what = "READ_CAPACITY"; break; + case READ_10: what = "READ_10"; break; + case WRITE_10: what = "WRITE_10"; break; + case SEEK_10: what = "SEEK_10"; break; + case WRITE_VERIFY: what = "WRITE_VERIFY"; break; + case VERIFY: what = "VERIFY"; break; + case SEARCH_HIGH: what = "SEARCH_HIGH"; break; + case SEARCH_EQUAL: what = "SEARCH_EQUAL"; break; + case SEARCH_LOW: what = "SEARCH_LOW"; break; + case SET_LIMITS: what = "SET_LIMITS"; break; + case READ_POSITION: what = "READ_POSITION"; break; + case SYNCHRONIZE_CACHE: what = "SYNCHRONIZE_CACHE"; break; + case LOCK_UNLOCK_CACHE: what = "LOCK_UNLOCK_CACHE"; break; + case READ_DEFECT_DATA: what = "READ_DEFECT_DATA"; break; + case MEDIUM_SCAN: what = "MEDIUM_SCAN"; break; + case COMPARE: what = "COMPARE"; break; + case COPY_VERIFY: what = "COPY_VERIFY"; break; + case WRITE_BUFFER: what = "WRITE_BUFFER"; break; + case READ_BUFFER: what = "READ_BUFFER"; break; + case UPDATE_BLOCK: what = "UPDATE_BLOCK"; break; + case READ_LONG: what = "READ_LONG"; break; + case WRITE_LONG: what = "WRITE_LONG"; break; + case CHANGE_DEFINITION: what = "CHANGE_DEFINITION"; break; + case WRITE_SAME: what = "WRITE_SAME"; break; + case READ_TOC: what = "READ_TOC"; break; + case LOG_SELECT: what = "LOG_SELECT"; break; + case LOG_SENSE: what = "LOG_SENSE"; break; + case MODE_SELECT_10: what = "MODE_SELECT_10"; break; + case MODE_SENSE_10: what = "MODE_SENSE_10"; break; + case MOVE_MEDIUM: what = "MOVE_MEDIUM"; break; + case READ_12: what = "READ_12"; break; + case WRITE_12: what = "WRITE_12"; break; + case WRITE_VERIFY_12: what = "WRITE_VERIFY_12"; break; + case SEARCH_HIGH_12: what = "SEARCH_HIGH_12"; break; + case SEARCH_EQUAL_12: what = "SEARCH_EQUAL_12"; break; + case SEARCH_LOW_12: what = "SEARCH_LOW_12"; break; + case READ_ELEMENT_STATUS: what = "READ_ELEMENT_STATUS"; break; + case SEND_VOLUME_TAG: what = "SEND_VOLUME_TAG"; break; + case WRITE_LONG_2: what = "WRITE_LONG_2"; break; + default: break; + } + printk(KERN_DEBUG USB_STORAGE + "Command %s (%d bytes)\n", what, srb->cmd_len); + printk(KERN_DEBUG USB_STORAGE + " %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + srb->cmnd[0], srb->cmnd[1], srb->cmnd[2], srb->cmnd[3], srb->cmnd[4], srb->cmnd[5], + srb->cmnd[6], srb->cmnd[7], srb->cmnd[8], srb->cmnd[9]); +} + +#endif diff --git a/drivers/usb/usb-storage.c b/drivers/usb/usb-storage.c new file mode 100644 index 000000000000..85600c2b1f89 --- /dev/null +++ b/drivers/usb/usb-storage.c @@ -0,0 +1,2200 @@ +/* Driver for USB Mass Storage compliant devices + * + * Initial work by: + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * + * Current development and maintainance by: + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + * This driver is based on the 'USB Mass Storage Class' document. This + * describes in detail the protocol used to communicate with such + * devices. Clearly, the designers had SCSI and ATAPI commands in + * mind when they created this document. The commands are all very + * similar to commands in the SCSI-II and ATAPI specifications. + * + * It is important to note that in a number of cases this class + * exhibits class-specific exemptions from the USB specification. + * Notably the usage of NAK, STALL and ACK differs from the norm, in + * that they are used to communicate wait, failed and OK on commands. + * + * Also, for certain devices, the interrupt endpoint is used to convey + * status of a command. + * + * Please see http://www.one-eyed-alien.net/~mdharm/linux-usb for more + * information about this driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "../scsi/scsi.h" +#include "../scsi/hosts.h" +#include "../scsi/sd.h" + +#include "usb-storage.h" +#include "usb-storage-debug.h" + +/* direction table -- this indicates the direction of the data + * transfer for each command code -- a 1 indicates input + */ +/* FIXME: we need to use the new direction indicators in the Scsi_Cmnd + * structure, not this table. First we need to evaluate if it's being set + * correctly for us, though + */ +unsigned char us_direction[256/8] = { + 0x28, 0x81, 0x14, 0x14, 0x20, 0x01, 0x90, 0x77, + 0x0C, 0x20, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +/* + * Per device data + */ + +static int my_host_number; + +struct us_data; + +typedef int (*trans_cmnd)(Scsi_Cmnd*, struct us_data*); +typedef int (*trans_reset)(struct us_data*); +typedef void (*proto_cmnd)(Scsi_Cmnd*, struct us_data*); + +/* we allocate one of these for every device that we remember */ +struct us_data { + struct us_data *next; /* next device */ + + /* the device we're working with */ + struct semaphore dev_semaphore; /* protect pusb_dev */ + struct usb_device *pusb_dev; /* this usb_device */ + + unsigned int flags; /* from filter initially */ + + /* information about the device -- always good */ + char vendor[USB_STOR_STRING_LEN]; + char product[USB_STOR_STRING_LEN]; + char serial[USB_STOR_STRING_LEN]; + char *transport_name; + char *protocol_name; + u8 subclass; + u8 protocol; + + /* information about the device -- only good if device is attached */ + u8 ifnum; /* interface number */ + u8 ep_in; /* bulk in endpoint */ + u8 ep_out; /* bulk out endpoint */ + struct usb_endpoint_descriptor *ep_int; /* interrupt endpoint */ + + /* function pointers for this device */ + trans_cmnd transport; /* transport function */ + trans_reset transport_reset; /* transport device reset */ + proto_cmnd proto_handler; /* protocol handler */ + + /* SCSI interfaces */ + GUID(guid); /* unique dev id */ + struct Scsi_Host *host; /* our dummy host data */ + Scsi_Host_Template htmplt; /* own host template */ + int host_number; /* to find us */ + int host_no; /* allocated by scsi */ + Scsi_Cmnd *srb; /* current srb */ + + /* thread information */ + Scsi_Cmnd *queue_srb; /* the single queue slot */ + int action; /* what to do */ + int pid; /* control thread */ + + /* interrupt info for CBI devices -- only good if attached */ + struct semaphore ip_waitq; /* for CBI interrupts */ + int ip_wanted; /* is an IRQ expected? */ + + /* interrupt communications data */ + struct semaphore irq_urb_sem; /* to protect irq_urb */ + struct urb *irq_urb; /* for USB int requests */ + unsigned char irqbuf[2]; /* buffer for USB IRQ */ + + /* control and bulk communications data */ + struct semaphore current_urb_sem; /* to protect irq_urb */ + struct urb *current_urb; /* non-int USB requests */ + + /* mutual exclusion structures */ + struct semaphore notify; /* thread begin/end */ + struct semaphore sleeper; /* to sleep the thread on */ + struct semaphore queue_exclusion; /* to protect data structs */ +}; + +/* + * kernel thread actions + */ + +#define US_ACT_COMMAND 1 +#define US_ACT_DEVICE_RESET 2 +#define US_ACT_BUS_RESET 3 +#define US_ACT_HOST_RESET 4 +#define US_ACT_EXIT 5 + +/* The list of structures and the protective lock for them */ +static struct us_data *us_list; +struct semaphore us_list_semaphore; + +static void * storage_probe(struct usb_device *dev, unsigned int ifnum); +static void storage_disconnect(struct usb_device *dev, void *ptr); +static struct usb_driver storage_driver = { + name: "usb-storage", + probe: storage_probe, + disconnect: storage_disconnect, +}; + +struct proc_dir_entry proc_scsi_usb_scsi = +{PROC_SCSI_USB_SCSI, 3, "usb", S_IFDIR | S_IRUGO | S_IXUGO, 2}; + +/*********************************************************************** + * Data transfer routines + ***********************************************************************/ + +/* This is the completion handler which will wake us up when an URB + * completes. + */ +static void usb_stor_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +} + +/* This is our function to emulate usb_control_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_control_msg(struct us_data *us, unsigned int pipe, + u8 request, u8 requesttype, u16 value, u16 index, + void *data, u16 size) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + devrequest *dr; + + /* allocate the device request structure */ + dr = kmalloc(sizeof(devrequest), GFP_KERNEL); + if (!dr) + return -ENOMEM; + + /* fill in the structure */ + dr->requesttype = requesttype; + dr->request = request; + dr->value = cpu_to_le16(value); + dr->index = cpu_to_le16(index); + dr->length = cpu_to_le16(size); + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_CONTROL_URB(us->current_urb, us->pusb_dev, pipe, + (unsigned char*) dr, data, size, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + kfree(dr); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_control_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred if no error*/ + if (status >= 0) + status = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + kfree(dr); + return status; +} + +/* This is our function to emulate usb_bulk_msg() but give us enough + * access to make aborts/resets work + */ +int usb_stor_bulk_msg(struct us_data *us, void *data, int pipe, + unsigned int len, unsigned int *act_len) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + /* set up data structures for the wakeup system */ + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + add_wait_queue(&wqh, &wait); + + /* lock the URB */ + down(&(us->current_urb_sem)); + + /* fill the URB */ + FILL_BULK_URB(us->current_urb, us->pusb_dev, pipe, data, len, + usb_stor_blocking_completion, &awd); + + /* submit the URB */ + set_current_state(TASK_UNINTERRUPTIBLE); + status = usb_submit_urb(us->current_urb); + if (status) { + /* something went wrong */ + up(&(us->current_urb_sem)); + remove_wait_queue(&wqh, &wait); + return status; + } + + /* wait for the completion of the URB */ + up(&(us->current_urb_sem)); + if (us->current_urb->status == -EINPROGRESS) + schedule_timeout(10*HZ); + down(&(us->current_urb_sem)); + + /* we either timed out or got woken up -- clean up either way */ + set_current_state(TASK_RUNNING); + remove_wait_queue(&wqh, &wait); + + /* did we time out? */ + if (us->current_urb->status == -EINPROGRESS) { + US_DEBUGP("usb_stor_bulk_msg() timeout\n"); + usb_unlink_urb(us->current_urb); + status = -ETIMEDOUT; + } else + status = us->current_urb->status; + + /* return the actual length of the data transferred */ + *act_len = us->current_urb->actual_length; + + /* release the lock and return status */ + up(&(us->current_urb_sem)); + return status; +} + +/* + * Transfer one SCSI scatter-gather buffer via bulk transfer + * + * Note that this function is necessary because we want the ability to + * use scatter-gather memory. Good performance is achieved by a combination + * of scatter-gather and clustering (which makes each chunk bigger). + * + * Note that the lower layer will always retry when a NAK occurs, up to the + * timeout limit. Thus we don't have to worry about it for individual + * packets. + */ +static int us_transfer_partial(struct us_data *us, char *buf, int length) +{ + int result; + int partial; + int pipe; + + /* calculate the appropriate pipe information */ + if (US_DIRECTION(us->srb->cmnd[0])) + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + else + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + /* transfer the data */ + US_DEBUGP("us_transfer_partial(): xfer %d bytes\n", length); + result = usb_stor_bulk_msg(us, buf, pipe, length, &partial); + US_DEBUGP("usb_stor_bulk_msg() returned %d xferred %d/%d\n", + result, partial, length); + + /* if we stall, we need to clear it before we go on */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* did we send all the data? */ + if (partial == length) { + US_DEBUGP("us_transfer_partial(): transfer complete\n"); + return US_BULK_TRANSFER_GOOD; + } + + /* uh oh... we have an error code, so something went wrong. */ + if (result) { + /* NAK - that means we've retried a few times allready */ + if (result == -ETIMEDOUT) { + US_DEBUGP("us_transfer_partial(): device NAKed\n"); + return US_BULK_TRANSFER_FAILED; + } + + /* -ENOENT -- we canceled this transfer */ + if (result == -ENOENT) { + US_DEBUGP("us_transfer_partial(): transfer aborted\n"); + return US_BULK_TRANSFER_ABORTED; + } + + /* the catch-all case */ + US_DEBUGP("us_transfer_partial(): unknown error\n"); + return US_BULK_TRANSFER_FAILED; + } + + /* no error code, so we must have transferred some data, + * just not all of it */ + return US_BULK_TRANSFER_SHORT; +} + +/* + * Transfer an entire SCSI command's worth of data payload over the bulk + * pipe. + * + * Note that this uses us_transfer_partial to achieve it's goals -- this + * function simply determines if we're going to use scatter-gather or not, + * and acts appropriately. For now, it also re-interprets the error codes. + */ +static void us_transfer(Scsi_Cmnd *srb, struct us_data* us, int dir_in) +{ + int i; + int result = -1; + struct scatterlist *sg; + + /* are we scatter-gathering? */ + if (srb->use_sg) { + + /* loop over all the scatter gather structures and + * make the appropriate requests for each, until done + */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) { + result = us_transfer_partial(us, sg[i].address, + sg[i].length); + if (result) + break; + } + } + else + /* no scatter-gather, just make the request */ + result = us_transfer_partial(us, srb->request_buffer, + srb->request_bufflen); + + /* return the result in the data structure itself */ + srb->result = result; +} + +/* Calculate the length of the data transfer (not the command) for any + * given SCSI command + */ +static unsigned int us_transfer_length(Scsi_Cmnd *srb, struct us_data *us) +{ + int i; + unsigned int total = 0; + struct scatterlist *sg; + + /* support those devices which need the length calculated + * differently + */ + if (us->flags & US_FL_ALT_LENGTH) { + if (srb->cmnd[0] == INQUIRY) { + srb->cmnd[4] = 36; + } + + if ((srb->cmnd[0] == INQUIRY) || (srb->cmnd[0] == MODE_SENSE)) + return srb->cmnd[4]; + + if (srb->cmnd[0] == TEST_UNIT_READY) + return 0; + } + + /* Are we going to scatter gather? */ + if (srb->use_sg) { + /* Add up the sizes of all the scatter-gather segments */ + sg = (struct scatterlist *) srb->request_buffer; + for (i = 0; i < srb->use_sg; i++) + total += sg[i].length; + + return total; + } + else + /* Just return the length of the buffer */ + return srb->request_bufflen; +} + +/*********************************************************************** + * Transport routines + ***********************************************************************/ + +/* Invoke the transport and basic error-handling/recovery methods + * + * This is used by the protocol layers to actually send the message to + * the device and recieve the response. + */ +static void invoke_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int need_auto_sense; + int result; + + /* send the command to the transport layer */ + result = us->transport(srb, us); + + /* Determine if we need to auto-sense + * + * I normally don't use a flag like this, but it's almost impossible + * to understand what's going on here if I don't. + */ + need_auto_sense = 0; + + /* + * If we're running the CB transport, which is incapable + * of determining status on it's own, we need to auto-sense almost + * every time. + */ + if (us->protocol == US_PR_CB) { + US_DEBUGP("-- CB transport device requiring auto-sense\n"); + need_auto_sense = 1; + + /* There are some exceptions to this. Notably, if this is + * a UFI device and the command is REQUEST_SENSE or INQUIRY, + * then it is impossible to truly determine status. + */ + if (us->subclass == US_SC_UFI && + ((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY))) { + US_DEBUGP("** no auto-sense for a special command\n"); + need_auto_sense = 0; + } + } + + /* + * If we have an error, we're going to do a REQUEST_SENSE + * automatically. Note that we differentiate between a command + * "failure" and an "error" in the transport mechanism. + */ + if (result == USB_STOR_TRANSPORT_FAILED) { + US_DEBUGP("-- transport indicates command failure\n"); + need_auto_sense = 1; + } + if (result == USB_STOR_TRANSPORT_ERROR) { + /* FIXME: we need to invoke a transport reset here */ + US_DEBUGP("-- transport indicates transport failure\n"); + need_auto_sense = 0; + srb->result = DID_ERROR << 16; + return; + } + + /* + * Also, if we have a short transfer on a command that can't have + * a short transfer, we're going to do this. + */ + if ((srb->result == US_BULK_TRANSFER_SHORT) && + !((srb->cmnd[0] == REQUEST_SENSE) || + (srb->cmnd[0] == INQUIRY) || + (srb->cmnd[0] == MODE_SENSE) || + (srb->cmnd[0] == LOG_SENSE) || + (srb->cmnd[0] == MODE_SENSE_10))) { + US_DEBUGP("-- unexpectedly short transfer\n"); + need_auto_sense = 1; + } + + /* Now, if we need to do the auto-sense, let's do it */ + if (need_auto_sense) { + int temp_result; + void* old_request_buffer; + int old_sg; + int old_request_bufflen; + unsigned char old_cmnd[MAX_COMMAND_SIZE]; + + US_DEBUGP("Issuing auto-REQUEST_SENSE\n"); + + /* save the old command */ + memcpy(old_cmnd, srb->cmnd, MAX_COMMAND_SIZE); + + srb->cmnd[0] = REQUEST_SENSE; + srb->cmnd[1] = 0; + srb->cmnd[2] = 0; + srb->cmnd[3] = 0; + srb->cmnd[4] = 18; + srb->cmnd[5] = 0; + + /* set the buffer length for transfer */ + old_request_buffer = srb->request_buffer; + old_request_bufflen = srb->request_bufflen; + old_sg = srb->use_sg; + srb->use_sg = 0; + srb->request_bufflen = 18; + srb->request_buffer = srb->sense_buffer; + + /* issue the auto-sense command */ + temp_result = us->transport(us->srb, us); + if (temp_result != USB_STOR_TRANSPORT_GOOD) { + /* FIXME: we need to invoke a transport reset here */ + US_DEBUGP("-- auto-sense failure\n"); + srb->result = DID_ERROR << 16; + return; + } + + US_DEBUGP("-- Result from auto-sense is %d\n", temp_result); + US_DEBUGP("-- code: 0x%x, key: 0x%x, ASC: 0x%x, ASCQ: 0x%x\n", + srb->sense_buffer[0], + srb->sense_buffer[2] & 0xf, + srb->sense_buffer[12], + srb->sense_buffer[13]); + + /* set the result so the higher layers expect this data */ + srb->result = CHECK_CONDITION; + + /* we're done here, let's clean up */ + srb->request_buffer = old_request_buffer; + srb->request_bufflen = old_request_bufflen; + srb->use_sg = old_sg; + memcpy(srb->cmnd, old_cmnd, MAX_COMMAND_SIZE); + + /* If things are really okay, then let's show that */ + if ((srb->sense_buffer[2] & 0xf) == 0x0) + srb->result = GOOD; + } else /* if (need_auto_sense) */ + srb->result = GOOD; + + /* Regardless of auto-sense, if we _know_ we have an error + * condition, show that in the result code + */ + if (result == USB_STOR_TRANSPORT_FAILED) + srb->result = CHECK_CONDITION; + + /* If we think we're good, then make sure the sense data shows it. + * This is necessary because the auto-sense for some devices always + * sets byte 0 == 0x70, even if there is no error + */ + if ((us->protocol == US_PR_CB) && + (result == USB_STOR_TRANSPORT_GOOD) && + ((srb->sense_buffer[2] & 0xf) == 0x0)) + srb->sense_buffer[0] = 0x0; +} + +/* + * Control/Bulk/Interrupt transport + */ + +/* The interrupt handler for CBI devices */ +static void CBI_irq(struct urb *urb) +{ + struct us_data *us = (struct us_data *)urb->context; + + US_DEBUGP("USB IRQ recieved for device on host %d\n", us->host_no); + US_DEBUGP("-- IRQ data length is %d\n", urb->actual_length); + US_DEBUGP("-- IRQ state is %d\n", urb->status); + + /* is the device removed? */ + if (urb->status != -ENOENT) { + /* save the data for interpretation later */ + US_DEBUGP("-- Interrupt Status (0x%x, 0x%x)\n", + ((unsigned char*)urb->transfer_buffer)[0], + ((unsigned char*)urb->transfer_buffer)[1]); + + + /* was this a wanted interrupt? */ + if (us->ip_wanted) { + us->ip_wanted = 0; + up(&(us->ip_waitq)); + } else + US_DEBUGP("ERROR: Unwanted interrupt received!\n"); + } else + US_DEBUGP("-- device has been removed\n"); +} + +static int CBI_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int result; + + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); + + /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); + if (result < 0) { + /* STALL must be cleared when they are detected */ + if (result == -EPIPE) { + US_DEBUGP("-- Stall on control pipe. Clearing\n"); + result = usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + return USB_STOR_TRANSPORT_FAILED; + } + + /* Uh oh... serious problem here */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* Set up for status notification */ + us->ip_wanted = 1; + + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (us_transfer_length(srb, us)) { + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CBI data stage result is 0x%x\n", srb->result); + } + + /* STATUS STAGE */ + + /* go to sleep until we get this interrupt */ + down(&(us->ip_waitq)); + + /* if we were woken up by an abort instead of the actual interrupt */ + if (us->ip_wanted) { + US_DEBUGP("Did not get interrupt on CBI\n"); + us->ip_wanted = 0; + return USB_STOR_TRANSPORT_ERROR; + } + + US_DEBUGP("Got interrupt data (0x%x, 0x%x)\n", + ((unsigned char*)us->irq_urb->transfer_buffer)[0], + ((unsigned char*)us->irq_urb->transfer_buffer)[1]); + + /* UFI gives us ASC and ASCQ, like a request sense + * + * REQUEST_SENSE and INQUIRY don't affect the sense data on UFI + * devices, so we ignore the information for those commands. Note + * that this means we could be ignoring a real error on these + * commands, but that can't be helped. + */ + if (us->subclass == US_SC_UFI) { + if (srb->cmnd[0] == REQUEST_SENSE || + srb->cmnd[0] == INQUIRY) + return USB_STOR_TRANSPORT_GOOD; + else + if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) + return USB_STOR_TRANSPORT_FAILED; + else + return USB_STOR_TRANSPORT_GOOD; + } + + /* If not UFI, we interpret the data as a result code + * The first byte should always be a 0x0 + * The second byte & 0x0F should be 0x0 for good, otherwise error + */ + if (((unsigned char*)us->irq_urb->transfer_buffer)[0]) { + US_DEBUGP("CBI IRQ data showed reserved bType\n"); + return USB_STOR_TRANSPORT_ERROR; + } + switch (((unsigned char*)us->irq_urb->transfer_buffer)[1] & 0x0F) { + case 0x00: + return USB_STOR_TRANSPORT_GOOD; + case 0x01: + return USB_STOR_TRANSPORT_FAILED; + default: + return USB_STOR_TRANSPORT_ERROR; + } + + US_DEBUGP("CBI_transport() reached end of function\n"); + return USB_STOR_TRANSPORT_ERROR; +} + +/* + * Control/Bulk transport + */ +static int CB_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + int result; + + /* COMMAND STAGE */ + /* let's send the command via the control pipe */ + result = usb_stor_control_msg(us, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0, + us->ifnum, srb->cmnd, srb->cmd_len); + + /* check the return code for the command */ + US_DEBUGP("Call to usb_stor_control_msg() returned %d\n", result); + if (result < 0) { + /* a stall is a fatal condition from the device */ + if (result == -EPIPE) { + US_DEBUGP("-- Stall on control pipe. Clearing\n"); + result = usb_clear_halt(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev, + 0)); + US_DEBUGP("-- usb_clear_halt() returns %d\n", result); + return USB_STOR_TRANSPORT_FAILED; + } + + /* Uh oh... serious problem here */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* DATA STAGE */ + /* transfer the data payload for this command, if one exists*/ + if (us_transfer_length(srb, us)) { + us_transfer(srb, us, US_DIRECTION(srb->cmnd[0])); + US_DEBUGP("CB data stage result is 0x%x\n", srb->result); + } + + + /* STATUS STAGE */ + /* NOTE: CB does not have a status stage. Silly, I know. So + * we have to catch this at a higher level. + */ + return USB_STOR_TRANSPORT_GOOD; +} + +/* + * Bulk only transport + */ + +/* Determine what the maximum LUN supported is */ +static int Bulk_max_lun(struct us_data *us) +{ + unsigned char data; + int result; + int pipe; + + /* issue the command */ + pipe = usb_rcvctrlpipe(us->pusb_dev, 0); + result = usb_control_msg(us->pusb_dev, pipe, + US_BULK_GET_MAX_LUN, + USB_DIR_IN | USB_TYPE_CLASS | + USB_RECIP_INTERFACE, + 0, us->ifnum, &data, sizeof(data), HZ); + + US_DEBUGP("GetMaxLUN command result is %d, data is %d\n", + result, data); + + /* if we have a successful request, return the result */ + if (!result) + return data; + + /* if we get a STALL, clear the stall */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* return the default -- no LUNs */ + return 0; +} + +static int Bulk_transport(Scsi_Cmnd *srb, struct us_data *us) +{ + struct bulk_cb_wrap bcb; + struct bulk_cs_wrap bcs; + int result; + int pipe; + int partial; + + /* set up the command wrapper */ + bcb.Signature = cpu_to_le32(US_BULK_CB_SIGN); + bcb.DataTransferLength = cpu_to_le32(us_transfer_length(srb, us)); + bcb.Flags = US_DIRECTION(srb->cmnd[0]) << 7; + bcb.Tag = srb->serial_number; + bcb.Lun = srb->cmnd[1] >> 5; + bcb.Length = srb->cmd_len; + + /* construct the pipe handle */ + pipe = usb_sndbulkpipe(us->pusb_dev, us->ep_out); + + /* copy the command payload */ + memset(bcb.CDB, 0, sizeof(bcb.CDB)); + memcpy(bcb.CDB, srb->cmnd, bcb.Length); + + /* send it to out endpoint */ + US_DEBUGP("Bulk command S 0x%x T 0x%x LUN %d L %d F %d CL %d\n", + le32_to_cpu(bcb.Signature), bcb.Tag, bcb.Lun, + bcb.DataTransferLength, bcb.Flags, bcb.Length); + result = usb_stor_bulk_msg(us, &bcb, pipe, US_BULK_CB_WRAP_LEN, + &partial); + US_DEBUGP("Bulk command transfer result=%d\n", result); + + /* if we stall, we need to clear it before we go on */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + } + + /* if the command transfered well, then we go to the data stage */ + if (result == 0) { + /* send/receive data payload, if there is any */ + if (bcb.DataTransferLength) { + us_transfer(srb, us, bcb.Flags); + US_DEBUGP("Bulk data transfer result 0x%x\n", + srb->result); + } + } + + /* See flow chart on pg 15 of the Bulk Only Transport spec for + * an explanation of how this code works. + */ + + /* construct the pipe handle */ + pipe = usb_rcvbulkpipe(us->pusb_dev, us->ep_in); + + /* get CSW for device status */ + US_DEBUGP("Attempting to get CSW...\n"); + result = usb_stor_bulk_msg(us, &bcs, pipe, US_BULK_CS_WRAP_LEN, + &partial); + + /* did the attempt to read the CSW fail? */ + if (result == -EPIPE) { + US_DEBUGP("clearing endpoint halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + + /* get the status again */ + US_DEBUGP("Attempting to get CSW (2nd try)...\n"); + result = usb_stor_bulk_msg(us, &bcs, pipe, + US_BULK_CS_WRAP_LEN, &partial); + + /* if it fails again, we need a reset and return an error*/ + if (result == -EPIPE) { + US_DEBUGP("clearing halt for pipe 0x%x\n", pipe); + usb_clear_halt(us->pusb_dev, pipe); + return USB_STOR_TRANSPORT_ERROR; + } + } + + /* if we still have a failure at this point, we're in trouble */ + US_DEBUGP("Bulk status result = %d\n", result); + if (result) { + return USB_STOR_TRANSPORT_ERROR; + } + + /* check bulk status */ + US_DEBUGP("Bulk status S 0x%x T 0x%x R %d V 0x%x\n", + le32_to_cpu(bcs.Signature), bcs.Tag, + bcs.Residue, bcs.Status); + if (bcs.Signature != cpu_to_le32(US_BULK_CS_SIGN) || + bcs.Tag != bcb.Tag || + bcs.Status > US_BULK_STAT_PHASE || partial != 13) { + US_DEBUGP("Bulk logical error\n"); + return USB_STOR_TRANSPORT_ERROR; + } + + /* based on the status code, we report good or bad */ + switch (bcs.Status) { + case US_BULK_STAT_OK: + /* command good -- note that we could be short on data */ + return USB_STOR_TRANSPORT_GOOD; + + case US_BULK_STAT_FAIL: + /* command failed */ + return USB_STOR_TRANSPORT_FAILED; + + case US_BULK_STAT_PHASE: + /* phase error */ + return USB_STOR_TRANSPORT_ERROR; + } + + /* we should never get here, but if we do, we're in trouble */ + return USB_STOR_TRANSPORT_ERROR; +} + +/*********************************************************************** + * Protocol routines + ***********************************************************************/ + +static void ATAPI_command(Scsi_Cmnd *srb, struct us_data *us) +{ + int old_cmnd = 0; + + /* Fix some commands -- this is a form of mode translation + * ATAPI devices only accept 12 byte long commands + * + * NOTE: This only works because a Scsi_Cmnd struct field contains + * a unsigned char cmnd[12], so we know we have storage available + */ + + /* set command length to 12 bytes */ + srb->cmd_len = 12; + + /* determine the correct (or minimum) data length for these commands */ + switch (srb->cmnd[0]) { + + /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */ + case MODE_SENSE: + case MODE_SELECT: + /* save the command so we can tell what it was */ + old_cmnd = srb->cmnd[0]; + + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = 0; + srb->cmnd[4] = 0; + srb->cmnd[3] = 0; + srb->cmnd[2] = srb->cmnd[2]; + srb->cmnd[1] = srb->cmnd[1]; + srb->cmnd[0] = srb->cmnd[0] | 0x40; + break; + + /* change READ_6/WRITE_6 to READ_10/WRITE_10, which + * are ATAPI commands */ + case WRITE_6: + case READ_6: + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = srb->cmnd[3]; + srb->cmnd[4] = srb->cmnd[2]; + srb->cmnd[3] = srb->cmnd[1] & 0x1F; + srb->cmnd[2] = 0; + srb->cmnd[1] = srb->cmnd[1] & 0xE0; + srb->cmnd[0] = srb->cmnd[0] | 0x20; + break; + } /* end switch on cmnd[0] */ + + /* send the command to the transport layer */ + invoke_transport(srb, us); + + /* Fix the MODE_SENSE data if we translated the command + */ + if (old_cmnd == MODE_SENSE) { + unsigned char *dta = (unsigned char *)us->srb->request_buffer; + + /* FIXME: we need to compress the entire data structure here + */ + dta[0] = dta[1]; /* data len */ + dta[1] = dta[2]; /* med type */ + dta[2] = dta[3]; /* dev-spec prm */ + dta[3] = dta[7]; /* block desc len */ + printk (KERN_DEBUG USB_STORAGE + "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", + dta[0], dta[1], dta[2], dta[3]); + } + + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ + if (srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; + } +} + + +static void ufi_command(Scsi_Cmnd *srb, struct us_data *us) +{ + int old_cmnd = 0; + + /* fix some commands -- this is a form of mode translation + * UFI devices only accept 12 byte long commands + * + * NOTE: This only works because a Scsi_Cmnd struct field contains + * a unsigned char cmnd[12], so we know we have storage available + */ + + /* set command length to 12 bytes (this affects the transport layer) */ + srb->cmd_len = 12; + + /* determine the correct (or minimum) data length for these commands */ + switch (srb->cmnd[0]) { + + /* for INQUIRY, UFI devices only ever return 36 bytes */ + case INQUIRY: + srb->cmnd[4] = 36; + break; + + /* change MODE_SENSE/MODE_SELECT from 6 to 10 byte commands */ + case MODE_SENSE: + case MODE_SELECT: + /* save the command so we can tell what it was */ + old_cmnd = srb->cmnd[0]; + + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + + /* if we're sending data, we send all. If getting data, + * get the minimum */ + if (srb->cmnd[0] == MODE_SELECT) + srb->cmnd[8] = srb->cmnd[4]; + else + srb->cmnd[8] = 8; + + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = 0; + srb->cmnd[4] = 0; + srb->cmnd[3] = 0; + srb->cmnd[2] = srb->cmnd[2]; + srb->cmnd[1] = srb->cmnd[1]; + srb->cmnd[0] = srb->cmnd[0] | 0x40; + break; + + /* again, for MODE_SENSE_10, we get the minimum (8) */ + case MODE_SENSE_10: + srb->cmnd[7] = 0; + srb->cmnd[8] = 8; + break; + + /* for REQUEST_SENSE, UFI devices only ever return 18 bytes */ + case REQUEST_SENSE: + srb->cmnd[4] = 18; + break; + + /* change READ_6/WRITE_6 to READ_10/WRITE_10, which + * are UFI commands */ + case WRITE_6: + case READ_6: + srb->cmnd[11] = 0; + srb->cmnd[10] = 0; + srb->cmnd[9] = 0; + srb->cmnd[8] = srb->cmnd[4]; + srb->cmnd[7] = 0; + srb->cmnd[6] = 0; + srb->cmnd[5] = srb->cmnd[3]; + srb->cmnd[4] = srb->cmnd[2]; + srb->cmnd[3] = srb->cmnd[1] & 0x1F; + srb->cmnd[2] = 0; + srb->cmnd[1] = srb->cmnd[1] & 0xE0; + srb->cmnd[0] = srb->cmnd[0] | 0x20; + break; + } /* end switch on cmnd[0] */ + + /* send the command to the transport layer */ + invoke_transport(srb, us); + + /* Fix the MODE_SENSE data here if we had to translate the command + */ + if (old_cmnd == MODE_SENSE) { + unsigned char *dta = (unsigned char *)us->srb->request_buffer; + + /* FIXME: we need to compress the entire data structure here + */ + dta[0] = dta[1]; /* data len */ + dta[1] = dta[2]; /* med type */ + dta[2] = dta[3]; /* dev-spec prm */ + dta[3] = dta[7]; /* block desc len */ + printk (KERN_DEBUG USB_STORAGE + "new MODE_SENSE_6 data = %.2X %.2X %.2X %.2X\n", + dta[0], dta[1], dta[2], dta[3]); + } + + /* Fix-up the return data from an INQUIRY command to show + * ANSI SCSI rev 2 so we don't confuse the SCSI layers above us + */ + if (srb->cmnd[0] == INQUIRY) { + ((unsigned char *)us->srb->request_buffer)[2] |= 0x2; + } +} + +static void transparent_scsi_command(Scsi_Cmnd *srb, struct us_data *us) +{ + /* This code supports devices which do not support {READ|WRITE}_6 + * Apparently, neither Windows or MacOS will use these commands, + * so some devices do not support them + */ + if (us->flags & US_FL_MODE_XLATE) { + + /* translate READ_6 to READ_10 */ + if (srb->cmnd[0] == 0x08) { + + /* get the control */ + srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + srb->cmnd[8] = us->srb->cmnd[6]; + srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + srb->cmnd[6] = 0; + + /* get LBA */ + srb->cmnd[5] = us->srb->cmnd[3]; + srb->cmnd[4] = us->srb->cmnd[2]; + srb->cmnd[3] = 0; + srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + srb->cmnd[0] = 0x28; + + US_DEBUGP("Changing READ_6 to READ_10\n"); + US_DEBUG(us_show_command(srb)); + } + + /* translate WRITE_6 to WRITE_10 */ + if (srb->cmnd[0] == 0x0A) { + + /* get the control */ + srb->cmnd[9] = us->srb->cmnd[5]; + + /* get the length */ + srb->cmnd[8] = us->srb->cmnd[4]; + srb->cmnd[7] = 0; + + /* set the reserved area to 0 */ + srb->cmnd[6] = 0; + + /* get LBA */ + srb->cmnd[5] = us->srb->cmnd[3]; + srb->cmnd[4] = us->srb->cmnd[2]; + srb->cmnd[3] = 0; + srb->cmnd[2] = 0; + + /* LUN and other info in cmnd[1] can stay */ + + /* fix command code */ + srb->cmnd[0] = 0x2A; + + US_DEBUGP("Changing WRITE_6 to WRITE_10\n"); + US_DEBUG(us_show_command(us->srb)); + } + } /* if (us->flags & US_FL_MODE_XLATE) */ + + /* send the command to the transport layer */ + invoke_transport(srb, us); + + /* fix the results of an INQUIRY */ + if (srb->cmnd[0] == INQUIRY) { + US_DEBUGP("Fixing INQUIRY data, setting SCSI rev to 2\n"); + ((unsigned char*)us->srb->request_buffer)[2] |= 2; + } +} + +/*********************************************************************** + * Reset routines + ***********************************************************************/ + +/* This issues a CB[I] Reset to the device in question + */ +static int CB_reset(struct us_data *us) +{ + unsigned char cmd[12]; + int result; + + US_DEBUGP("CB_reset() called\n"); + + memset(cmd, 0xFF, sizeof(cmd)); + cmd[0] = SEND_DIAGNOSTIC; + cmd[1] = 4; + result = usb_control_msg(us->pusb_dev, usb_sndctrlpipe(us->pusb_dev,0), + US_CBI_ADSC, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, cmd, sizeof(cmd), HZ*5); + + /* long wait for reset */ + schedule_timeout(HZ*6); + + US_DEBUGP("CB_reset: clearing endpoint halt\n"); + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_out)); + + US_DEBUGP("CB_reset done\n"); + return 0; +} + +/* FIXME: Does this work? */ +static int Bulk_reset(struct us_data *us) +{ + int result; + + result = usb_control_msg(us->pusb_dev, + usb_sndctrlpipe(us->pusb_dev,0), + US_BULK_RESET_REQUEST, + USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, us->ifnum, NULL, 0, HZ*5); + + if (result < 0) + US_DEBUGP("Bulk hard reset failed %d\n", result); + + usb_clear_halt(us->pusb_dev, + usb_rcvbulkpipe(us->pusb_dev, us->ep_in)); + usb_clear_halt(us->pusb_dev, + usb_sndbulkpipe(us->pusb_dev, us->ep_out)); + + /* long wait for reset */ + schedule_timeout(HZ*6); + + return result; +} + +/*********************************************************************** + * Host functions + ***********************************************************************/ + +static const char* us_info(struct Scsi_Host *host) +{ + return "SCSI emulation for USB Mass Storage devices"; +} + +/* detect a virtual adapter (always works) */ +static int us_detect(struct SHT *sht) +{ + struct us_data *us; + char local_name[32]; + + /* This is not nice at all, but how else are we to get the + * data here? */ + us = (struct us_data *)sht->proc_dir; + + /* set up the name of our subdirectory under /proc/scsi/ */ + sprintf(local_name, "usb-storage-%d", us->host_number); + sht->name = kmalloc (strlen(local_name) + 1, GFP_KERNEL); + if (!sht->name) + return 0; + strcpy((char *)sht->name, local_name); + + /* we start with no /proc directory entry */ + sht->proc_dir = &proc_scsi_usb_scsi; + + /* register the host */ + us->host = scsi_register(sht, sizeof(us)); + if (us->host) { + us->host->hostdata[0] = (unsigned long)us; + us->host_no = us->host->host_no; + return 1; + } + + /* odd... didn't register properly. Abort and free pointers */ + kfree(sht->name); + sht->name = NULL; + + return 0; +} + +/* Release all resources used by the virtual host + * + * NOTE: There is no contention here, because we're allready deregistered + * the driver and we're doing each virtual host in turn, not in parallel + */ +static int us_release(struct Scsi_Host *psh) +{ + struct us_data *us = (struct us_data *)psh->hostdata[0]; + + US_DEBUGP("us_release() called for host %s\n", us->htmplt.name); + + /* Kill the control threads + * + * Enqueue the command, wake up the thread, and wait for + * notification that it's exited. + */ + US_DEBUGP("-- sending US_ACT_EXIT command to thread\n"); + us->action = US_ACT_EXIT; + up(&(us->sleeper)); + down(&(us->notify)); + + /* free the data structure we were using */ + US_DEBUGP("-- freeing private host data structure\n"); + kfree(us->current_urb); + kfree(us); + (struct us_data*)psh->hostdata[0] = NULL; + + /* we always have a successful release */ + return 0; +} + +/* run command */ +static int us_command( Scsi_Cmnd *srb ) +{ + US_DEBUGP("Bad use of us_command\n"); + + return DID_BAD_TARGET << 16; +} + +/* run command */ +static int us_queuecommand( Scsi_Cmnd *srb , void (*done)(Scsi_Cmnd *)) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + US_DEBUGP("us_queuecommand() called\n"); + srb->host_scribble = (unsigned char *)us; + + /* get exclusive access to the structures we want */ + down(&(us->queue_exclusion)); + + /* enqueue the command */ + us->queue_srb = srb; + srb->scsi_done = done; + us->action = US_ACT_COMMAND; + + /* wake up the process task */ + up(&(us->queue_exclusion)); + up(&(us->sleeper)); + + return 0; +} + +/*********************************************************************** + * Error handling functions + ***********************************************************************/ + +/* Command abort + * + * Note that this is really only meaningful right now for CBI transport + * devices which have failed to give us the command completion interrupt + */ +static int us_abort( Scsi_Cmnd *srb ) +{ + struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + US_DEBUGP("us_abort() called\n"); + + /* if we're stuck waiting for an IRQ, simulate it */ + if (us->ip_wanted) { + US_DEBUGP("-- simulating missing IRQ\n"); + up(&(us->ip_waitq)); + return SUCCESS; + } + + return FAILED; +} + +/* FIXME: this doesn't do anything right now */ +static int us_bus_reset( Scsi_Cmnd *srb ) +{ + // struct us_data *us = (struct us_data *)srb->host->hostdata[0]; + + printk(KERN_CRIT "usb-storage: bus_reset() requested but not implemented\n" ); + US_DEBUGP("Bus reset requested\n"); + // us->transport_reset(us); + return FAILED; +} + +/* FIXME: This doesn't actually reset anything */ +static int us_host_reset( Scsi_Cmnd *srb ) +{ + printk(KERN_CRIT "usb-storage: host_reset() requested but not implemented\n" ); + return FAILED; +} + +/*********************************************************************** + * /proc/scsi/ functions + ***********************************************************************/ + +/* we use this macro to help us write into the buffer */ +#undef SPRINTF +#define SPRINTF(args...) \ + do { if (pos < buffer+length) pos += sprintf(pos, ## args); } while (0) + +int usb_stor_proc_info (char *buffer, char **start, off_t offset, + int length, int hostno, int inout) +{ + struct us_data *us; + char *pos = buffer; + + /* if someone is sending us data, just throw it away */ + if (inout) + return length; + + /* lock the data structures */ + down(&us_list_semaphore); + + /* find our data from hostno */ + us = us_list; + while (us) { + if (us->host_no == hostno) + break; + us = us->next; + } + + /* if we couldn't find it, we return an error */ + if (!us) { + up(&us_list_semaphore); + return -ESRCH; + } + + /* print the controler name */ + SPRINTF(" Host scsi%d: usb-storage\n", hostno); + + /* print product, vendor, and serial number strings */ + SPRINTF(" Vendor: %s\n", us->vendor); + SPRINTF(" Product: %s\n", us->product); + SPRINTF("Serial Number: %s\n", us->serial); + + /* show the protocol and transport */ + SPRINTF(" Protocol: %s\n", us->protocol_name); + SPRINTF(" Transport: %s\n", us->transport_name); + + /* show the GUID of the device */ + SPRINTF(" GUID: " GUID_FORMAT "\n", GUID_ARGS(us->guid)); + + /* release our lock on the data structures */ + up(&us_list_semaphore); + + /* + * Calculate start of next buffer, and return value. + */ + *start = buffer + offset; + + if ((pos - buffer) < offset) + return (0); + else if ((pos - buffer - offset) < length) + return (pos - buffer - offset); + else + return (length); +} + +/* + * this defines our 'host' + */ + +static Scsi_Host_Template my_host_template = { + name: "usb-storage", + proc_info: usb_stor_proc_info, + info: us_info, + + detect: us_detect, + release: us_release, + command: us_command, + queuecommand: us_queuecommand, + + eh_abort_handler: us_abort, + eh_device_reset_handler:us_bus_reset, + eh_bus_reset_handler: us_bus_reset, + eh_host_reset_handler: us_host_reset, + + can_queue: 1, + this_id: -1, + + sg_tablesize: SG_ALL, + cmd_per_lun: 1, + present: 0, + unchecked_isa_dma: FALSE, + use_clustering: TRUE, + use_new_eh_code: TRUE, + emulated: TRUE +}; + +static unsigned char sense_notready[] = { + [0] = 0x70, /* current error */ + [2] = 0x02, /* not ready */ + [5] = 0x0a, /* additional length */ + [10] = 0x04, /* not ready */ + [11] = 0x03 /* manual intervention */ +}; + +static int usb_stor_control_thread(void * __us) +{ + struct us_data *us = (struct us_data *)__us; + int action; + + lock_kernel(); + + /* + * This thread doesn't need any user-level access, + * so get rid of all our resources.. + */ + daemonize(); + + /* set our name for identification purposes */ + sprintf(current->comm, "usb-storage-%d", us->host_number); + + unlock_kernel(); + + /* signal that we've started the thread */ + up(&(us->notify)); + + for(;;) { + US_DEBUGP("*** thread sleeping.\n"); + down(&(us->sleeper)); + down(&(us->queue_exclusion)); + US_DEBUGP("*** thread awakened.\n"); + + /* take the command off the queue */ + action = us->action; + us->action = 0; + us->srb = us->queue_srb; + + /* release the queue lock as fast as possible */ + up(&(us->queue_exclusion)); + + switch (action) { + case US_ACT_COMMAND: + /* reject if target != 0 or if single-lun device + * and LUN != 0 + */ + if (us->srb->target || + ((us->flags & US_FL_SINGLE_LUN) && us->srb->lun)) { + US_DEBUGP("Bad device number (%d/%d)\n", + us->srb->target, us->srb->lun); + + us->srb->result = DID_BAD_TARGET << 16; + + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + } + + /* handle those devices which can't do a START_STOP */ + if ((us->srb->cmnd[0] == START_STOP) && + (us->flags & US_FL_START_STOP)) { + us->srb->result = GOOD; + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + } + + /* lock the device pointers */ + down(&(us->dev_semaphore)); + + /* our device has gone - pretend not ready */ + if (!us->pusb_dev) { + US_DEBUGP("Request is for removed device\n"); + /* For REQUEST_SENSE, it's the data. But + * for anything else, it should look like + * we auto-sensed for it. + */ + if (us->srb->cmnd[0] == REQUEST_SENSE) { + memcpy(us->srb->request_buffer, + sense_notready, + sizeof(sense_notready)); + us->srb->result = GOOD; + } else { + memcpy(us->srb->sense_buffer, + sense_notready, + sizeof(sense_notready)); + us->srb->result = CHECK_CONDITION; + } + } else { /* !us->pusb_dev */ + /* we've got a command, let's do it! */ + US_DEBUG(us_show_command(us->srb)); + us->proto_handler(us->srb, us); + } + + /* unlock the device pointers */ + up(&(us->dev_semaphore)); + + /* indicate that the command is done */ + US_DEBUGP("scsi cmd done, result=0x%x\n", + us->srb->result); + us->srb->scsi_done(us->srb); + us->srb = NULL; + break; + + case US_ACT_DEVICE_RESET: + break; + + case US_ACT_BUS_RESET: + break; + + case US_ACT_HOST_RESET: + break; + + } /* end switch on action */ + + /* exit if we get a signal to exit */ + if (action == US_ACT_EXIT) { + US_DEBUGP("-- US_ACT_EXIT command recieved\n"); + break; + } + } /* for (;;) */ + + /* notify the exit routine that we're actually exiting now */ + up(&(us->notify)); + + return 0; +} + +/* This is the list of devices we recognize, along with their flag data */ +static struct us_unusual_dev us_unusual_dev_list[] = { + { 0x03f0, 0x0107, 0x0200, + "HP USB CD-Writer Plus", US_SC_8070, US_PR_CB, 0}, + { 0x04e6, 0x0001, 0x0200, + "Matshita LS-120", US_SC_8020, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x04e6, 0x0002, 0x0100, + "Shuttle eUSCSI Bridge", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x04e6, 0x0006, 0x0100, + "Shuttle eUSB MMC Adapter", US_SC_SCSI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x057b, 0x0000, 0x0114, + "Y-E Data Flashbuster-U", US_SC_UFI, US_PR_CB, US_FL_SINGLE_LUN}, + { 0x059b, 0x0030, 0x0100, + "Iomega Zip 250", US_SC_SCSI, US_PR_BULK, US_FL_SINGLE_LUN}, + { 0x0693, 0x0002, 0x0100, + "Hagiwara FlashGate SmartMedia", US_SC_SCSI, US_PR_BULK, + US_FL_ALT_LENGTH}, + { 0x0781, 0x0001, 0x0200, + "Sandisk ImageMate (SDDR-01)", US_SC_SCSI, US_PR_CB, + US_FL_SINGLE_LUN | US_FL_START_STOP}, + { 0x0781, 0x0002, 0x0009, + "Sandisk Imagemate (SDDR-31)", US_SC_SCSI, US_PR_BULK, + US_FL_SINGLE_LUN | US_FL_IGNORE_SER}, + { 0x07af, 0x0005, 0x0100, + "Microtech USB-SCSI-HD50", US_SC_SCSI, US_PR_BULK, US_FL_ALT_LENGTH}, + { 0x0000, 0x0000, 0x0, + "", 0, 0, 0} +}; + +/* Search our ususual device list, based on vendor/product combinations + * to see if we can support this device. Returns a pointer to a structure + * defining how we should support this device, or NULL if it's not in the + * list + */ +static struct us_unusual_dev* us_find_dev(u16 idVendor, u16 idProduct, + u16 bcdDevice) +{ + struct us_unusual_dev* ptr; + + US_DEBUGP("Searching unusual device list for (0x%x, 0x%x, 0x%x)...\n", + idVendor, idProduct, bcdDevice); + + ptr = us_unusual_dev_list; + while ((ptr->idVendor != 0x0000) && + !((ptr->idVendor == idVendor) && + (ptr->idProduct == idProduct) && + (ptr->bcdDevice == bcdDevice))) + ptr++; + + /* if the search ended because we hit the end record, we failed */ + if (ptr->idVendor == 0x0000) { + US_DEBUGP("-- did not find a matching device\n"); + return NULL; + } + + /* otherwise, we found one! */ + US_DEBUGP("-- found matching device: %s\n", ptr->name); + return ptr; +} + +/* Set up the IRQ pipe and handler + * Note that this function assumes that all the data in the us_data + * strucuture is current. This includes the ep_int field, which gives us + * the endpoint for the interrupt. + * Returns non-zero on failure, zero on success + */ +static int usb_stor_allocate_irq(struct us_data *ss) +{ + unsigned int pipe; + int maxp; + int result; + + US_DEBUGP("Allocating IRQ for CBI transport\n"); + + /* lock access to the data structure */ + down(&(ss->irq_urb_sem)); + + /* allocate the URB */ + ss->irq_urb = usb_alloc_urb(0); + if (!ss->irq_urb) { + up(&(ss->irq_urb_sem)); + US_DEBUGP("couldn't allocate interrupt URB"); + return 1; + } + + /* calculate the pipe and max packet size */ + pipe = usb_rcvintpipe(ss->pusb_dev, ss->ep_int->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK); + maxp = usb_maxpacket(ss->pusb_dev, pipe, usb_pipeout(pipe)); + if (maxp > sizeof(ss->irqbuf)) + maxp = sizeof(ss->irqbuf); + + /* fill in the URB with our data */ + FILL_INT_URB(ss->irq_urb, ss->pusb_dev, pipe, ss->irqbuf, maxp, + CBI_irq, ss, ss->ep_int->bInterval); + + /* submit the URB for processing */ + result = usb_submit_urb(ss->irq_urb); + US_DEBUGP("usb_submit_urb() returns %d\n", result); + if (result) { + usb_free_urb(ss->irq_urb); + up(&(ss->irq_urb_sem)); + return 2; + } + + /* unlock the data structure and return success */ + up(&(ss->irq_urb_sem)); + return 0; +} + +/* Probe to see if a new device is actually a SCSI device */ +static void * storage_probe(struct usb_device *dev, unsigned int ifnum) +{ + int i; + char mf[USB_STOR_STRING_LEN]; /* manufacturer */ + char prod[USB_STOR_STRING_LEN]; /* product */ + char serial[USB_STOR_STRING_LEN]; /* serial number */ + GUID(guid); /* Global Unique Identifier */ + unsigned int flags; + struct us_unusual_dev *unusual_dev; + struct us_data *ss = NULL; + int result; + + /* these are temporary copies -- we test on these, then put them + * in the us-data structure + */ + struct usb_endpoint_descriptor *ep_in = NULL; + struct usb_endpoint_descriptor *ep_out = NULL; + struct usb_endpoint_descriptor *ep_int = NULL; + u8 subclass = 0; + u8 protocol = 0; + + /* the altsettting 0 on the interface we're probing */ + struct usb_interface_descriptor *altsetting = + &(dev->actconfig->interface[ifnum].altsetting[0]); + + /* clear the temporary strings */ + memset(mf, 0, sizeof(mf)); + memset(prod, 0, sizeof(prod)); + memset(serial, 0, sizeof(serial)); + + /* search for this device in our unusual device list */ + unusual_dev = us_find_dev(dev->descriptor.idVendor, + dev->descriptor.idProduct, + dev->descriptor.bcdDevice); + + /* + * Can we support this device, either because we know about it + * from our unusual device list, or because it advertises that it's + * compliant to the specification? + */ + if (!unusual_dev && + !(dev->descriptor.bDeviceClass == 0 && + altsetting->bInterfaceClass == USB_CLASS_MASS_STORAGE && + altsetting->bInterfaceSubClass >= US_SC_MIN && + altsetting->bInterfaceSubClass <= US_SC_MAX)) { + /* if it's not a mass storage, we go no further */ + return NULL; + } + + /* At this point, we know we've got a live one */ + US_DEBUGP("USB Mass Storage device detected\n"); + + /* Determine subclass and protocol, or copy from the interface */ + if (unusual_dev) { + subclass = unusual_dev->useProtocol; + protocol = unusual_dev->useTransport; + flags = unusual_dev->flags; + } else { + subclass = altsetting->bInterfaceSubClass; + protocol = altsetting->bInterfaceProtocol; + flags = 0; + } + + /* + * Find the endpoints we need + * We are expecting a minimum of 2 endpoints - in and out (bulk). + * An optional interrupt is OK (necessary for CBI protocol). + * We will ignore any others. + */ + for (i = 0; i < altsetting->bNumEndpoints; i++) { + /* is it an BULK endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK) { + /* BULK in or out? */ + if (altsetting->endpoint[i].bEndpointAddress & + USB_DIR_IN) + ep_in = &altsetting->endpoint[i]; + else + ep_out = &altsetting->endpoint[i]; + } + + /* is it an interrupt endpoint? */ + if ((altsetting->endpoint[i].bmAttributes & + USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT) { + ep_int = &altsetting->endpoint[i]; + } + } + US_DEBUGP("Endpoints: In: 0x%p Out: 0x%p Int: 0x%p (Period %d)\n", + ep_in, ep_out, ep_int, ep_int ? ep_int->bInterval : 0); + + /* set the interface -- STALL is an acceptable response here */ + result = usb_set_interface(dev, altsetting->bInterfaceNumber, 0); + US_DEBUGP("Result from usb_set_interface is %d\n", result); + if (result == -EPIPE) { + US_DEBUGP("-- clearing stall on control interface\n"); + usb_clear_halt(dev, usb_sndctrlpipe(dev, 0)); + } else if (result != 0) { + /* it's not a stall, but another error -- time to bail */ + US_DEBUGP("-- Unknown error. Rejecting device\n"); + return NULL; + } + + /* Do some basic sanity checks, and bail if we find a problem */ + if (!ep_in || !ep_out || (protocol == US_PR_CBI && !ep_int)) { + US_DEBUGP("Sanity check failed. Rejecting device.\n"); + return NULL; + } + + /* At this point, we're committed to using the device */ + + /* clear the GUID and fetch the strings */ + GUID_CLEAR(guid); + if (dev->descriptor.iManufacturer) + usb_string(dev, dev->descriptor.iManufacturer, + mf, sizeof(mf)); + if (dev->descriptor.iProduct) + usb_string(dev, dev->descriptor.iProduct, + prod, sizeof(prod)); + if (dev->descriptor.iSerialNumber && !(flags & US_FL_IGNORE_SER)) + usb_string(dev, dev->descriptor.iSerialNumber, + serial, sizeof(serial)); + + /* Create a GUID for this device */ + if (dev->descriptor.iSerialNumber && serial[0]) { + /* If we have a serial number, and it's a non-NULL string */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, serial); + } else { + /* We don't have a serial number, so we use 0 */ + make_guid(guid, dev->descriptor.idVendor, + dev->descriptor.idProduct, "0"); + } + + /* lock access to the data structures */ + down(&us_list_semaphore); + + /* + * Now check if we have seen this GUID before + * We're looking for a device with a matching GUID that isn't + * allready on the system + */ + ss = us_list; + while ((ss != NULL) && + ((ss->pusb_dev) || !GUID_EQUAL(guid, ss->guid))) + ss = ss->next; + + if (ss != NULL) { + /* Existing device -- re-connect */ + US_DEBUGP("Found existing GUID " GUID_FORMAT "\n", + GUID_ARGS(guid)); + + /* establish the connection to the new device upon reconnect */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + + /* copy over the endpoint data */ + if (ep_in) + ss->ep_in = ep_in->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + if (ep_out) + ss->ep_out = ep_out->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->ep_int = ep_int; + + /* allocate an IRQ callback if one is needed */ + if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) + return NULL; + } else { + /* New device -- allocate memory and initialize */ + US_DEBUGP("New GUID " GUID_FORMAT "\n", GUID_ARGS(guid)); + + if ((ss = (struct us_data *)kmalloc(sizeof(struct us_data), + GFP_KERNEL)) == NULL) { + printk(KERN_WARNING USB_STORAGE "Out of memory\n"); + up(&us_list_semaphore); + return NULL; + } + memset(ss, 0, sizeof(struct us_data)); + + /* allocate the URB we're going to use */ + ss->current_urb = usb_alloc_urb(0); + if (!ss->current_urb) { + kfree(ss); + return NULL; + } + + /* Initialize the mutexes only when the struct is new */ + init_MUTEX_LOCKED(&(ss->sleeper)); + init_MUTEX_LOCKED(&(ss->notify)); + init_MUTEX_LOCKED(&(ss->ip_waitq)); + init_MUTEX(&(ss->queue_exclusion)); + init_MUTEX(&(ss->irq_urb_sem)); + init_MUTEX(&(ss->current_urb_sem)); + init_MUTEX(&(ss->dev_semaphore)); + + /* copy over the subclass and protocol data */ + ss->subclass = subclass; + ss->protocol = protocol; + ss->flags = flags; + + /* copy over the endpoint data */ + if (ep_in) + ss->ep_in = ep_in->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + if (ep_out) + ss->ep_out = ep_out->bEndpointAddress & + USB_ENDPOINT_NUMBER_MASK; + ss->ep_int = ep_int; + + /* establish the connection to the new device */ + ss->ifnum = ifnum; + ss->pusb_dev = dev; + + /* copy over the identifiying strings */ + strncpy(ss->vendor, mf, USB_STOR_STRING_LEN); + strncpy(ss->product, prod, USB_STOR_STRING_LEN); + strncpy(ss->serial, serial, USB_STOR_STRING_LEN); + if (strlen(ss->vendor) == 0) + strncpy(ss->vendor, "Unknown", USB_STOR_STRING_LEN); + if (strlen(ss->product) == 0) + strncpy(ss->product, "Unknown", USB_STOR_STRING_LEN); + if (strlen(ss->serial) == 0) + strncpy(ss->serial, "None", USB_STOR_STRING_LEN); + + /* copy the GUID we created before */ + memcpy(ss->guid, guid, sizeof(guid)); + + /* + * Set the handler pointers based on the protocol + * Again, this data is persistant across reattachments + */ + switch (ss->protocol) { + case US_PR_CB: + ss->transport_name = "Control/Bulk"; + ss->transport = CB_transport; + ss->transport_reset = CB_reset; + break; + + case US_PR_CBI: + ss->transport_name = "Control/Bulk/Interrupt"; + ss->transport = CBI_transport; + ss->transport_reset = CB_reset; + break; + + case US_PR_BULK: + ss->transport_name = "Bulk"; + ss->transport = Bulk_transport; + ss->transport_reset = Bulk_reset; + /* FIXME: for testing purposes only */ + Bulk_max_lun(ss); + break; + + default: + ss->transport_name = "Unknown"; + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + } + US_DEBUGP("Transport: %s\n", ss->transport_name); + + switch (ss->subclass) { + case US_SC_RBC: + ss->protocol_name = "Reduced Block Commands (RBC)"; + ss->proto_handler = transparent_scsi_command; + break; + + case US_SC_8020: + ss->protocol_name = "8020i"; + ss->proto_handler = ATAPI_command; + break; + + case US_SC_QIC: + ss->protocol_name = "QIC-157"; + US_DEBUGP("Sorry, device not supported. Please\n"); + US_DEBUGP("contact mdharm-usb@one-eyed-alien.net\n"); + US_DEBUGP("if you see this message.\n"); + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + + case US_SC_8070: + ss->protocol_name = "8070i"; + ss->proto_handler = ATAPI_command; + break; + + case US_SC_SCSI: + ss->protocol_name = "Transparent SCSI"; + ss->proto_handler = transparent_scsi_command; + break; + + case US_SC_UFI: + ss->protocol_name = "Uniform Floppy Interface (UFI)"; + ss->proto_handler = ufi_command; + break; + + default: + ss->protocol_name = "Unknown"; + up(&us_list_semaphore); + kfree(ss->current_urb); + kfree(ss); + return NULL; + break; + } + US_DEBUGP("Protocol: %s\n", ss->protocol_name); + + /* allocate an IRQ callback if one is needed */ + if ((ss->protocol == US_PR_CBI) && usb_stor_allocate_irq(ss)) + return NULL; + + /* + * Since this is a new device, we need to generate a scsi + * host definition, and register with the higher SCSI layers + */ + + /* Initialize the host template based on the default one */ + memcpy(&(ss->htmplt), &my_host_template, + sizeof(my_host_template)); + + /* Grab the next host number */ + ss->host_number = my_host_number++; + + /* We abuse this pointer so we can pass the ss pointer to + * the host controler thread in us_detect. But how else are + * we to do it? + */ + (struct us_data *)ss->htmplt.proc_dir = ss; + + /* start up our control thread */ + ss->pid = kernel_thread(usb_stor_control_thread, ss, + CLONE_FS | CLONE_FILES | + CLONE_SIGHAND); + if (ss->pid < 0) { + printk(KERN_WARNING USB_STORAGE + "Unable to start control thread\n"); + kfree(ss->current_urb); + kfree(ss); + return NULL; + } + + /* wait for the thread to start */ + down(&(ss->notify)); + + /* now register - our detect function will be called */ + ss->htmplt.module = THIS_MODULE; + scsi_register_module(MODULE_SCSI_HA, &(ss->htmplt)); + + /* put us in the list */ + ss->next = us_list; + us_list = ss; + } + + /* release the data structure lock */ + up(&us_list_semaphore); + + printk(KERN_DEBUG + "WARNING: USB Mass Storage data integrity not assured\n"); + printk(KERN_DEBUG + "USB Mass Storage device found at %d\n", dev->devnum); + + /* return a pointer for the disconnect function */ + return ss; +} + +/* Handle a disconnect event from the USB core */ +static void storage_disconnect(struct usb_device *dev, void *ptr) +{ + struct us_data *ss = ptr; + int result; + + US_DEBUGP("storage_disconnect() called\n"); + + /* this is the odd case -- we disconnected but weren't using it */ + if (!ss) { + US_DEBUGP("-- device was not in use\n"); + return; + } + + /* lock access to the device data structure */ + down(&(ss->dev_semaphore)); + + /* release the IRQ, if we have one */ + down(&(ss->irq_urb_sem)); + if (ss->irq_urb) { + US_DEBUGP("-- releasing irq handle\n"); + result = usb_unlink_urb(ss->irq_urb); + ss->irq_urb = NULL; + US_DEBUGP("-- usb_unlink_urb() returned %d\n", result); + usb_free_urb(ss->irq_urb); + } + up(&(ss->irq_urb_sem)); + + /* mark the device as gone */ + ss->pusb_dev = NULL; + + /* lock access to the device data structure */ + up(&(ss->dev_semaphore)); +} + + +/*********************************************************************** + * Initialization and registration + ***********************************************************************/ + +int __init usb_stor_init(void) +{ + /* initialize internal global data elements */ + us_list = NULL; + init_MUTEX(&us_list_semaphore); + my_host_number = 0; + + /* register the driver, return -1 if error */ + if (usb_register(&storage_driver) < 0) + return -1; + + /* we're all set */ + printk(KERN_INFO "USB Mass Storage support registered.\n"); + return 0; +} + +void __exit usb_stor_exit(void) +{ + struct us_data *next; + + US_DEBUGP("usb_stor_exit() called\n"); + + /* Deregister the driver + * This eliminates races with probes and disconnects + */ + US_DEBUGP("-- calling usb_deregister()\n"); + usb_deregister(&storage_driver) ; + + /* lock access to the data structures */ + down(&us_list_semaphore); + + /* While there are still virtual hosts, unregister them + * + * Note that the us_release() routine will destroy the local data + * structure. So we have to peel these off the top of the list + * and keep updating the head pointer as we go. + */ + while (us_list) { + /* keep track of where the next one is */ + next = us_list->next; + + US_DEBUGP("-- calling scsi_unregister_module()\n"); + scsi_unregister_module(MODULE_SCSI_HA, &(us_list->htmplt)); + + /* advance the list pointer */ + us_list = next; + } + + /* unlock the data structures */ + up(&us_list_semaphore); +} + +module_init(usb_stor_init) ; +module_exit(usb_stor_exit) ; diff --git a/drivers/usb/usb-storage.h b/drivers/usb/usb-storage.h new file mode 100644 index 000000000000..6e54b059be04 --- /dev/null +++ b/drivers/usb/usb-storage.h @@ -0,0 +1,159 @@ +/* Driver for USB mass storage - include file + * + * (c) 1999 Michael Gee (michael@linuxspecific.com) + * (c) 1999, 2000 Matthew Dharm (mdharm-usb@one-eyed-alien.net) + * + */ + +#include + +#define USB_STORAGE "usb-storage.c: " +#define USB_STOR_STRING_LEN 32 + +#ifdef CONFIG_USB_STORAGE_DEBUG +void us_show_command(Scsi_Cmnd *srb); +#define US_DEBUGP(x...) printk( KERN_DEBUG USB_STORAGE ## x ) +#define US_DEBUGPX(x...) printk( ## x ) +#define US_DEBUG(x) x +#else +#define US_DEBUGP(x...) +#define US_DEBUGPX(x...) +#define US_DEBUG(x) +#endif + +/* bit set if input */ +extern unsigned char us_direction[256/8]; +#define US_DIRECTION(x) ((us_direction[x>>3] >> (x & 7)) & 1) + +/* Sub Classes */ + +#define US_SC_RBC 0x01 /* Typically, flash devices */ +#define US_SC_8020 0x02 /* CD-ROM */ +#define US_SC_QIC 0x03 /* QIC-157 Tapes */ +#define US_SC_UFI 0x04 /* Floppy */ +#define US_SC_8070 0x05 /* Removable media */ +#define US_SC_SCSI 0x06 /* Transparent */ +#define US_SC_MIN US_SC_RBC +#define US_SC_MAX US_SC_SCSI + +/* Protocols */ + +#define US_PR_CBI 0x00 /* Control/Bulk/Interrupt */ +#define US_PR_CB 0x01 /* Control/Bulk w/o interrupt */ +#define US_PR_BULK 0x50 /* bulk only */ + +/* + * Bulk only data structures + */ + +/* command block wrapper */ +struct bulk_cb_wrap { + __u32 Signature; /* contains 'USBC' */ + __u32 Tag; /* unique per command id */ + __u32 DataTransferLength; /* size of data */ + __u8 Flags; /* direction in bit 0 */ + __u8 Lun; /* LUN normally 0 */ + __u8 Length; /* of of the CDB */ + __u8 CDB[16]; /* max command */ +}; + +#define US_BULK_CB_WRAP_LEN 31 +#define US_BULK_CB_SIGN 0x43425355 /*spells out USBC */ +#define US_BULK_FLAG_IN 1 +#define US_BULK_FLAG_OUT 0 + +/* command status wrapper */ +struct bulk_cs_wrap { + __u32 Signature; /* should = 'USBS' */ + __u32 Tag; /* same as original command */ + __u32 Residue; /* amount not transferred */ + __u8 Status; /* see below */ + __u8 Filler[18]; +}; + +#define US_BULK_CS_WRAP_LEN 13 +#define US_BULK_CS_SIGN 0x53425355 /* spells out 'USBS' */ +#define US_BULK_STAT_OK 0 +#define US_BULK_STAT_FAIL 1 +#define US_BULK_STAT_PHASE 2 + +/* bulk-only class specific requests */ +#define US_BULK_RESET_REQUEST 0xff +#define US_BULK_GET_MAX_LUN 0xfe + +/* + * us_bulk_transfer() return codes + */ +#define US_BULK_TRANSFER_GOOD 0 /* good transfer */ +#define US_BULK_TRANSFER_SHORT 1 /* transfered less than expected */ +#define US_BULK_TRANSFER_FAILED 2 /* transfer died in the middle */ +#define US_BULK_TRANSFER_ABORTED 3 /* transfer canceled */ + +/* + * Transport return codes + */ + +#define USB_STOR_TRANSPORT_GOOD 0 /* Transport good, command good */ +#define USB_STOR_TRANSPORT_FAILED 1 /* Transport good, command failed */ +#define USB_STOR_TRANSPORT_ERROR 2 /* Transport bad (i.e. device dead) */ +#define USB_STOR_TRANSPORT_ABORTED 3 /* Transport aborted */ + +/* + * CBI accept device specific command + */ + +#define US_CBI_ADSC 0 + +/* + * GUID definitions + */ + +#define GUID(x) __u32 x[3] +#define GUID_EQUAL(x, y) (x[0] == y[0] && x[1] == y[1] && x[2] == y[2]) +#define GUID_CLEAR(x) x[0] = x[1] = x[2] = 0; +#define GUID_NONE(x) (!x[0] && !x[1] && !x[2]) +#define GUID_FORMAT "%08x%08x%08x" +#define GUID_ARGS(x) x[0], x[1], x[2] + +static inline void make_guid( __u32 *pg, __u16 vendor, __u16 product, char *serial) +{ + pg[0] = (vendor << 16) | product; + pg[1] = pg[2] = 0; + while (*serial) { + pg[1] <<= 4; + pg[1] |= pg[2] >> 28; + pg[2] <<= 4; + if (*serial >= 'a') + *serial -= 'a' - 'A'; + pg[2] |= (*serial <= '9' && *serial >= '0') ? *serial - '0' + : *serial - 'A' + 10; + serial++; + } +} + +/* + * Unusual device list definitions + */ + +struct us_unusual_dev { + /* we search the list based on these parameters */ + __u16 idVendor; + __u16 idProduct; + __u16 bcdDevice; + + /* the list specifies these parameters */ + const char* name; + __u8 useProtocol; + __u8 useTransport; + unsigned int flags; +}; + +/* Flag definitions */ +#define US_FL_SINGLE_LUN 0x00000001 /* allow access to only LUN 0 */ +#define US_FL_MODE_XLATE 0x00000002 /* translate _6 to _10 comands for + Win/MacOS compatibility */ +#define US_FL_START_STOP 0x00000004 /* ignore START_STOP commands */ +#define US_FL_ALT_LENGTH 0x00000008 /* use the alternate algorithm for + us_transfer_length() */ +#define US_FL_IGNORE_SER 0x00000010 /* Ignore the serial number given */ + diff --git a/drivers/usb/usb-uhci-debug.h b/drivers/usb/usb-uhci-debug.h new file mode 100644 index 000000000000..4eedc418360e --- /dev/null +++ b/drivers/usb/usb-uhci-debug.h @@ -0,0 +1,182 @@ +#ifdef DEBUG +static void uhci_show_qh (puhci_desc_t qh) +{ + if (qh->type != QH_TYPE) { + dbg("qh has not QH_TYPE"); + return; + } + dbg("QH @ %p/%08lX:", qh, virt_to_bus (qh)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + dbg(" Head Terminate"); + else + dbg(" Head: %s @ %08X", + (qh->hw.qh.head & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.head & ~UHCI_PTR_BITS); + + if (qh->hw.qh.element & UHCI_PTR_TERM) + dbg(" Element Terminate"); + else + dbg(" Element: %s @ %08X", + (qh->hw.qh.element & UHCI_PTR_QH?"QH":"TD"), + qh->hw.qh.element & ~UHCI_PTR_BITS); +} +#endif + +static void uhci_show_td (puhci_desc_t td) +{ + char *spid; + + switch (td->hw.td.info & 0xff) { + case USB_PID_SETUP: + spid = "SETUP"; + break; + case USB_PID_OUT: + spid = " OUT "; + break; + case USB_PID_IN: + spid = " IN "; + break; + default: + spid = " ? "; + break; + } + + warn(" TD @ %p/%08lX, MaxLen=%02x DT%d EP=%x Dev=%x PID=(%s) buf=%08x", + td, virt_to_bus (td), + td->hw.td.info >> 21, + ((td->hw.td.info >> 19) & 1), + (td->hw.td.info >> 15) & 15, + (td->hw.td.info >> 8) & 127, + spid, + td->hw.td.buffer); + + warn(" Len=%02x e%d %s%s%s%s%s%s%s%s%s%s", + td->hw.td.status & 0x7ff, + ((td->hw.td.status >> 27) & 3), + (td->hw.td.status & TD_CTRL_SPD) ? "SPD " : "", + (td->hw.td.status & TD_CTRL_LS) ? "LS " : "", + (td->hw.td.status & TD_CTRL_IOC) ? "IOC " : "", + (td->hw.td.status & TD_CTRL_ACTIVE) ? "Active " : "", + (td->hw.td.status & TD_CTRL_STALLED) ? "Stalled " : "", + (td->hw.td.status & TD_CTRL_DBUFERR) ? "DataBufErr " : "", + (td->hw.td.status & TD_CTRL_BABBLE) ? "Babble " : "", + (td->hw.td.status & TD_CTRL_NAK) ? "NAK " : "", + (td->hw.td.status & TD_CTRL_CRCTIMEO) ? "CRC/Timeo " : "", + (td->hw.td.status & TD_CTRL_BITSTUFF) ? "BitStuff " : "" + ); + + if (td->hw.td.link & UHCI_PTR_TERM) + warn(" TD Link Terminate"); + else + warn(" Link points to %s @ %08x, %s", + (td->hw.td.link & UHCI_PTR_QH?"QH":"TD"), + td->hw.td.link & ~UHCI_PTR_BITS, + (td->hw.td.link & UHCI_PTR_DEPTH ? "Depth first" : "Breadth first")); +} +#ifdef DEBUG +static void uhci_show_td_queue (puhci_desc_t td) +{ + //dbg("uhci_show_td_queue %p (%08lX):", td, virt_to_bus (td)); + while (1) { + uhci_show_td (td); + if (td->hw.td.link & UHCI_PTR_TERM) + break; + if (td != bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS)) + td = bus_to_virt (td->hw.td.link & ~UHCI_PTR_BITS); + else { + dbg("td points to itself!"); + break; + } + } +} + +static void uhci_show_queue (puhci_desc_t qh) +{ + uhci_desc_t *start_qh=qh; + + dbg("uhci_show_queue %p:", qh); + while (1) { + uhci_show_qh (qh); + + if (!(qh->hw.qh.element & UHCI_PTR_TERM)) + uhci_show_td_queue (bus_to_virt (qh->hw.qh.element & ~UHCI_PTR_BITS)); + + if (qh->hw.qh.head & UHCI_PTR_TERM) + break; + + if (qh != bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS)) + qh = bus_to_virt (qh->hw.qh.head & ~UHCI_PTR_BITS); + else { + dbg("qh points to itself!"); + break; + } + + if (qh==start_qh) { // avoid loop + dbg("Loop detect"); + break; + } + } +} + +static void uhci_show_sc (int port, unsigned short status) +{ + dbg(" stat%d = %04x %s%s%s%s%s%s%s%s", + port, + status, + (status & USBPORTSC_SUSP) ? "PortSuspend " : "", + (status & USBPORTSC_PR) ? "PortReset " : "", + (status & USBPORTSC_LSDA) ? "LowSpeed " : "", + (status & USBPORTSC_RD) ? "ResumeDetect " : "", + (status & USBPORTSC_PEC) ? "EnableChange " : "", + (status & USBPORTSC_PE) ? "PortEnabled " : "", + (status & USBPORTSC_CSC) ? "ConnectChange " : "", + (status & USBPORTSC_CCS) ? "PortConnected " : ""); +} + +void uhci_show_status (puhci_t s) +{ + unsigned int io_addr = s->io_addr; + unsigned short usbcmd, usbstat, usbint, usbfrnum; + unsigned int flbaseadd; + unsigned char sof; + unsigned short portsc1, portsc2; + + usbcmd = inw (io_addr + 0); + usbstat = inw (io_addr + 2); + usbint = inw (io_addr + 4); + usbfrnum = inw (io_addr + 6); + flbaseadd = inl (io_addr + 8); + sof = inb (io_addr + 12); + portsc1 = inw (io_addr + 16); + portsc2 = inw (io_addr + 18); + + dbg(" usbcmd = %04x %s%s%s%s%s%s%s%s", + usbcmd, + (usbcmd & USBCMD_MAXP) ? "Maxp64 " : "Maxp32 ", + (usbcmd & USBCMD_CF) ? "CF " : "", + (usbcmd & USBCMD_SWDBG) ? "SWDBG " : "", + (usbcmd & USBCMD_FGR) ? "FGR " : "", + (usbcmd & USBCMD_EGSM) ? "EGSM " : "", + (usbcmd & USBCMD_GRESET) ? "GRESET " : "", + (usbcmd & USBCMD_HCRESET) ? "HCRESET " : "", + (usbcmd & USBCMD_RS) ? "RS " : ""); + + dbg(" usbstat = %04x %s%s%s%s%s%s", + usbstat, + (usbstat & USBSTS_HCH) ? "HCHalted " : "", + (usbstat & USBSTS_HCPE) ? "HostControllerProcessError " : "", + (usbstat & USBSTS_HSE) ? "HostSystemError " : "", + (usbstat & USBSTS_RD) ? "ResumeDetect " : "", + (usbstat & USBSTS_ERROR) ? "USBError " : "", + (usbstat & USBSTS_USBINT) ? "USBINT " : ""); + + dbg(" usbint = %04x", usbint); + dbg(" usbfrnum = (%d)%03x", (usbfrnum >> 10) & 1, + 0xfff & (4 * (unsigned int) usbfrnum)); + dbg(" flbaseadd = %08x", flbaseadd); + dbg(" sof = %02x", sof); + uhci_show_sc (1, portsc1); + uhci_show_sc (2, portsc2); +} +#endif diff --git a/drivers/usb/usb-uhci.c b/drivers/usb/usb-uhci.c new file mode 100644 index 000000000000..9574c904ad84 --- /dev/null +++ b/drivers/usb/usb-uhci.c @@ -0,0 +1,2886 @@ +/* + * Universal Host Controller Interface driver for USB (take II). + * + * (c) 1999 Georg Acher, acher@in.tum.de (executive slave) (base guitar) + * Deti Fliegl, deti@fliegl.de (executive slave) (lead voice) + * Thomas Sailer, sailer@ife.ee.ethz.ch (chief consultant) (cheer leader) + * Roman Weissgaerber, weissg@vienna.at (virt root hub) (studio porter) + * + * HW-initalization based on material of + * + * (C) Copyright 1999 Linus Torvalds + * (C) Copyright 1999 Johannes Erdfelt + * (C) Copyright 1999 Randy Dunlap + * + * $Id: usb-uhci.c,v 1.232 2000/06/11 13:18:30 acher Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#include +#include +#include + +#include +#include +#include +#include + +/* This enables more detailed sanity checks in submit_iso */ +//#define ISO_SANITY_CHECK + +/* This enables debug printks */ +//#define DEBUG + +/* This enables all symbols to be exported, to ease debugging oopses */ +//#define DEBUG_SYMBOLS + +/* This enables an extra UHCI slab for memory debugging */ +#define DEBUG_SLAB + +#define VERSTR "$Revision: 1.232 $ time " __TIME__ " " __DATE__ + +#include +#include "usb-uhci.h" +#include "usb-uhci-debug.h" + +#undef DEBUG +#undef dbg +#define dbg(format, arg...) do {} while (0) +#define DEBUG_SYMBOLS +#ifdef DEBUG_SYMBOLS + #define _static + #ifndef EXPORT_SYMTAB + #define EXPORT_SYMTAB + #endif +#else + #define _static static +#endif + +#define queue_dbg dbg //err +#define async_dbg dbg //err + +#ifdef DEBUG_SLAB + static kmem_cache_t *uhci_desc_kmem; + static kmem_cache_t *urb_priv_kmem; +#endif + +#define SLAB_FLAG (in_interrupt ()? SLAB_ATOMIC : SLAB_KERNEL) +#define KMALLOC_FLAG (in_interrupt ()? GFP_ATOMIC : GFP_KERNEL) + +#define CONFIG_USB_UHCI_HIGH_BANDWIDTH +#define USE_CTRL_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first +#define USE_BULK_DEPTH_FIRST 0 // 0: Breadth first, 1: Depth first + +// stop bandwidth reclamation after (roughly) 50ms +#define IDLE_TIMEOUT (HZ/20) + +_static int rh_submit_urb (urb_t *urb); +_static int rh_unlink_urb (urb_t *urb); +_static int delete_qh (uhci_t *s, uhci_desc_t *qh); +_static int process_transfer (uhci_t *s, urb_t *urb, int mode); +_static int process_interrupt (uhci_t *s, urb_t *urb); +_static int process_iso (uhci_t *s, urb_t *urb, int force); + +static uhci_t *devs = NULL; + +/* used by userspace UHCI data structure dumper */ +uhci_t **uhci_devices = &devs; + +/*-------------------------------------------------------------------*/ +// Cleans up collected QHs +void clean_descs(uhci_t *s, int force) +{ + struct list_head *q; + uhci_desc_t *qh; + int now=UHCI_GET_CURRENT_FRAME(s); + + q=s->free_desc.prev; + + while (q != &s->free_desc) { + qh = list_entry (q, uhci_desc_t, horizontal); + q=qh->horizontal.prev; + + if ((qh->last_used!=now) || force) + delete_qh(s,qh); + } +} +/*-------------------------------------------------------------------*/ +_static void uhci_switch_timer_int(uhci_t *s) +{ + + if (!list_empty(&s->urb_unlinked)) { + s->td1ms->hw.td.status |= TD_CTRL_IOC; + } + else { + s->td1ms->hw.td.status &= ~TD_CTRL_IOC; + } + + if (s->timeout_urbs) { + s->td32ms->hw.td.status |= TD_CTRL_IOC; + } + else { + s->td32ms->hw.td.status &= ~TD_CTRL_IOC; + } + + wmb(); +} +/*-------------------------------------------------------------------*/ +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH +_static void enable_desc_loop(uhci_t *s, urb_t *urb) +{ + int flags; + + spin_lock_irqsave (&s->qh_lock, flags); + s->chain_end->hw.qh.head&=~UHCI_PTR_TERM; + mb(); + s->loop_usage++; + ((urb_priv_t*)urb->hcpriv)->use_loop=1; + spin_unlock_irqrestore (&s->qh_lock, flags); +} +/*-------------------------------------------------------------------*/ +_static void disable_desc_loop(uhci_t *s, urb_t *urb) +{ + int flags; + + spin_lock_irqsave (&s->qh_lock, flags); + + if (((urb_priv_t*)urb->hcpriv)->use_loop) { + s->loop_usage--; + + if (!s->loop_usage) { + s->chain_end->hw.qh.head|=UHCI_PTR_TERM; + mb(); + } + ((urb_priv_t*)urb->hcpriv)->use_loop=0; + } + spin_unlock_irqrestore (&s->qh_lock, flags); +} +#endif +/*-------------------------------------------------------------------*/ +_static void queue_urb_unlocked (uhci_t *s, urb_t *urb) +{ + struct list_head *p=&urb->urb_list; +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + { + int type; + type=usb_pipetype (urb->pipe); + + if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) + enable_desc_loop(s, urb); + } +#endif + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + list_add (p, &s->urb_list); + if (urb->timeout) + s->timeout_urbs++; + uhci_switch_timer_int(s); +} +/*-------------------------------------------------------------------*/ +_static void queue_urb (uhci_t *s, urb_t *urb) +{ + unsigned long flags=0; + + spin_lock_irqsave (&s->urb_list_lock, flags); + queue_urb_unlocked(s,urb); + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} +/*-------------------------------------------------------------------*/ +_static void dequeue_urb (uhci_t *s, urb_t *urb) +{ +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + int type; + + type=usb_pipetype (urb->pipe); + + if ((type == PIPE_BULK) || (type == PIPE_CONTROL)) + disable_desc_loop(s, urb); +#endif + + list_del (&urb->urb_list); + if (urb->timeout && s->timeout_urbs) + s->timeout_urbs--; + +} +/*-------------------------------------------------------------------*/ +_static int alloc_td (uhci_desc_t ** new, int flags) +{ +#ifdef DEBUG_SLAB + *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG); +#else + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG); +#endif + if (!*new) + return -ENOMEM; + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->hw.td.link = UHCI_PTR_TERM | (flags & UHCI_PTR_BITS); // last by default + (*new)->type = TD_TYPE; + mb(); + INIT_LIST_HEAD (&(*new)->vertical); + INIT_LIST_HEAD (&(*new)->horizontal); + + return 0; +} +/*-------------------------------------------------------------------*/ +// append a qh to td.link physically, the SW linkage is not affected +_static void append_qh(uhci_t *s, uhci_desc_t *td, uhci_desc_t* qh, int flags) +{ + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + td->hw.td.link = virt_to_bus (qh) | (flags & UHCI_PTR_DEPTH) | UHCI_PTR_QH; + + mb(); + spin_unlock_irqrestore (&s->td_lock, xxx); +} +/*-------------------------------------------------------------------*/ +/* insert td at last position in td-list of qh (vertical) */ +_static int insert_td (uhci_t *s, uhci_desc_t *qh, uhci_desc_t* new, int flags) +{ + uhci_desc_t *prev; + unsigned long xxx; + + spin_lock_irqsave (&s->td_lock, xxx); + + list_add_tail (&new->vertical, &qh->vertical); + + prev = list_entry (new->vertical.prev, uhci_desc_t, vertical); + + if (qh == prev ) { + // virgin qh without any tds + qh->hw.qh.element = virt_to_bus (new); + } + else { + // already tds inserted, implicitely remove TERM bit of prev + prev->hw.td.link = virt_to_bus (new) | (flags & UHCI_PTR_DEPTH); + } + mb(); + spin_unlock_irqrestore (&s->td_lock, xxx); + + return 0; +} +/*-------------------------------------------------------------------*/ +/* insert new_td after td (horizontal) */ +_static int insert_td_horizontal (uhci_t *s, uhci_desc_t *td, uhci_desc_t* new) +{ + uhci_desc_t *next; + unsigned long flags; + + spin_lock_irqsave (&s->td_lock, flags); + + next = list_entry (td->horizontal.next, uhci_desc_t, horizontal); + list_add (&new->horizontal, &td->horizontal); + new->hw.td.link = td->hw.td.link; + td->hw.td.link = virt_to_bus (new); + mb(); + spin_unlock_irqrestore (&s->td_lock, flags); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int unlink_td (uhci_t *s, uhci_desc_t *element, int phys_unlink) +{ + uhci_desc_t *next, *prev; + int dir = 0; + unsigned long flags; + + spin_lock_irqsave (&s->td_lock, flags); + + next = list_entry (element->vertical.next, uhci_desc_t, vertical); + + if (next == element) { + dir = 1; + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); + } + else + prev = list_entry (element->vertical.prev, uhci_desc_t, vertical); + + if (phys_unlink) { + // really remove HW linking + if (prev->type == TD_TYPE) + prev->hw.td.link = element->hw.td.link; + else + prev->hw.qh.element = element->hw.td.link; + } + + mb (); + + if (dir == 0) + list_del (&element->vertical); + else + list_del (&element->horizontal); + + spin_unlock_irqrestore (&s->td_lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------*/ +_static int delete_desc (uhci_desc_t *element) +{ +#ifdef DEBUG_SLAB + kmem_cache_free(uhci_desc_kmem, element); +#else + kfree (element); +#endif + return 0; +} +/*-------------------------------------------------------------------*/ +// Allocates qh element +_static int alloc_qh (uhci_desc_t ** new) +{ +#ifdef DEBUG_SLAB + *new= kmem_cache_alloc(uhci_desc_kmem, SLAB_FLAG); +#else + *new = (uhci_desc_t *) kmalloc (sizeof (uhci_desc_t), KMALLOC_FLAG); +#endif + if (!*new) + return -ENOMEM; + memset (*new, 0, sizeof (uhci_desc_t)); + (*new)->hw.qh.head = UHCI_PTR_TERM; + (*new)->hw.qh.element = UHCI_PTR_TERM; + (*new)->type = QH_TYPE; + + mb(); + INIT_LIST_HEAD (&(*new)->horizontal); + INIT_LIST_HEAD (&(*new)->vertical); + + dbg("Allocated qh @ %p", *new); + + return 0; +} +/*-------------------------------------------------------------------*/ +// inserts new qh before/after the qh at pos +// flags: 0: insert before pos, 1: insert after pos (for low speed transfers) +_static int insert_qh (uhci_t *s, uhci_desc_t *pos, uhci_desc_t *new, int order) +{ + uhci_desc_t *old; + unsigned long flags; + + spin_lock_irqsave (&s->qh_lock, flags); + + if (!order) { + // (OLD) (POS) -> (OLD) (NEW) (POS) + old = list_entry (pos->horizontal.prev, uhci_desc_t, horizontal); + list_add_tail (&new->horizontal, &pos->horizontal); + new->hw.qh.head = MAKE_QH_ADDR (pos) ; + if (!(old->hw.qh.head & UHCI_PTR_TERM)) + old->hw.qh.head = MAKE_QH_ADDR (new) ; + } + else { + // (POS) (OLD) -> (POS) (NEW) (OLD) + old = list_entry (pos->horizontal.next, uhci_desc_t, horizontal); + list_add (&new->horizontal, &pos->horizontal); + new->hw.qh.head = MAKE_QH_ADDR (old); + pos->hw.qh.head = MAKE_QH_ADDR (new) ; + } + + mb (); + + spin_unlock_irqrestore (&s->qh_lock, flags); + + return 0; +} + +/*-------------------------------------------------------------------*/ +_static int unlink_qh (uhci_t *s, uhci_desc_t *element) +{ + uhci_desc_t *prev; + unsigned long flags; + + spin_lock_irqsave (&s->qh_lock, flags); + + prev = list_entry (element->horizontal.prev, uhci_desc_t, horizontal); + prev->hw.qh.head = element->hw.qh.head; + + list_del(&element->horizontal); + + mb (); + spin_unlock_irqrestore (&s->qh_lock, flags); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int delete_qh (uhci_t *s, uhci_desc_t *qh) +{ + uhci_desc_t *td; + struct list_head *p; + + list_del (&qh->horizontal); + + while ((p = qh->vertical.next) != &qh->vertical) { + td = list_entry (p, uhci_desc_t, vertical); + dbg("unlink td @ %p",td); + unlink_td (s, td, 0); // no physical unlink + delete_desc (td); + } + + delete_desc (qh); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static void clean_td_chain (uhci_desc_t *td) +{ + struct list_head *p; + uhci_desc_t *td1; + + if (!td) + return; + + while ((p = td->horizontal.next) != &td->horizontal) { + td1 = list_entry (p, uhci_desc_t, horizontal); + delete_desc (td1); + } + + delete_desc (td); +} + +/*-------------------------------------------------------------------*/ +_static void fill_td (uhci_desc_t *td, int status, int info, __u32 buffer) +{ + td->hw.td.status = status; + td->hw.td.info = info; + td->hw.td.buffer = buffer; +} +/*-------------------------------------------------------------------*/ +// Removes ALL qhs in chain (paranoia!) +_static void cleanup_skel (uhci_t *s) +{ + unsigned int n; + uhci_desc_t *td; + + dbg("cleanup_skel"); + + clean_descs(s,1); + + + if (s->td32ms) { + + unlink_td(s,s->td32ms,1); + delete_desc(s->td32ms); + } + + for (n = 0; n < 8; n++) { + td = s->int_chain[n]; + clean_td_chain (td); + } + + if (s->iso_td) { + for (n = 0; n < 1024; n++) { + td = s->iso_td[n]; + clean_td_chain (td); + } + kfree (s->iso_td); + } + + if (s->framelist) + free_page ((unsigned long) s->framelist); + + if (s->control_chain) { + // completed init_skel? + struct list_head *p; + uhci_desc_t *qh, *qh1; + + qh = s->control_chain; + while ((p = qh->horizontal.next) != &qh->horizontal) { + qh1 = list_entry (p, uhci_desc_t, horizontal); + delete_qh (s, qh1); + } + + delete_qh (s, qh); + } + else { + if (s->ls_control_chain) + delete_desc (s->ls_control_chain); + if (s->control_chain) + delete_desc(s->control_chain); + if (s->bulk_chain) + delete_desc (s->bulk_chain); + if (s->chain_end) + delete_desc (s->chain_end); + } + dbg("cleanup_skel finished"); +} +/*-------------------------------------------------------------------*/ +// allocates framelist and qh-skeletons +// only HW-links provide continous linking, SW-links stay in their domain (ISO/INT) +_static int init_skel (uhci_t *s) +{ + int n, ret; + uhci_desc_t *qh, *td; + + dbg("init_skel"); + + s->framelist = (__u32 *) get_free_page (GFP_KERNEL); + + if (!s->framelist) + return -ENOMEM; + + memset (s->framelist, 0, 4096); + + dbg("allocating iso desc pointer list"); + s->iso_td = (uhci_desc_t **) kmalloc (1024 * sizeof (uhci_desc_t*), GFP_KERNEL); + + if (!s->iso_td) + goto init_skel_cleanup; + + s->ls_control_chain = NULL; + s->control_chain = NULL; + s->bulk_chain = NULL; + s->chain_end = NULL; + + dbg("allocating iso descs"); + for (n = 0; n < 1024; n++) { + // allocate skeleton iso/irq-tds + ret = alloc_td (&td, 0); + if (ret) + goto init_skel_cleanup; + s->iso_td[n] = td; + s->framelist[n] = ((__u32) virt_to_bus (td)); + } + + dbg("allocating qh: chain_end"); + ret = alloc_qh (&qh); + + if (ret) + goto init_skel_cleanup; + + s->chain_end = qh; + + ret = alloc_td (&td, 0); + + if (ret) + goto init_skel_cleanup; + + fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 1ms interrupt (enabled on demand) + insert_td (s, qh, td, 0); + s->td1ms=td; + + dbg("allocating qh: bulk_chain"); + ret = alloc_qh (&qh); + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->chain_end, qh, 0); + s->bulk_chain = qh; + + dbg("allocating qh: control_chain"); + ret = alloc_qh (&qh); + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->bulk_chain, qh, 0); + s->control_chain = qh; + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + // disabled reclamation loop + s->chain_end->hw.qh.head=virt_to_bus(s->control_chain) | UHCI_PTR_QH | UHCI_PTR_TERM; +#endif + + dbg("allocating qh: ls_control_chain"); + ret = alloc_qh (&qh); + if (ret) + goto init_skel_cleanup; + + insert_qh (s, s->control_chain, qh, 0); + s->ls_control_chain = qh; + + for (n = 0; n < 8; n++) + s->int_chain[n] = 0; + + dbg("allocating skeleton INT-TDs"); + + for (n = 0; n < 8; n++) { + uhci_desc_t *td; + + alloc_td (&td, 0); + if (!td) + goto init_skel_cleanup; + s->int_chain[n] = td; + if (n == 0) { + s->int_chain[0]->hw.td.link = virt_to_bus (s->ls_control_chain) | UHCI_PTR_QH; + } + else { + s->int_chain[n]->hw.td.link = virt_to_bus (s->int_chain[0]); + } + } + + dbg("Linking skeleton INT-TDs"); + + for (n = 0; n < 1024; n++) { + // link all iso-tds to the interrupt chains + int m, o; + dbg("framelist[%i]=%x",n,s->framelist[n]); + if ((n&127)==127) + ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus(s->int_chain[0]); + else + for (o = 1, m = 2; m <= 128; o++, m += m) + if ((n & (m - 1)) == ((m - 1) / 2)) + ((uhci_desc_t*) s->iso_td[n])->hw.td.link = virt_to_bus (s->int_chain[o]); + } + + ret = alloc_td (&td, 0); + + if (ret) + goto init_skel_cleanup; + + fill_td (td, 0 * TD_CTRL_IOC, 0, 0); // generate 32ms interrupt + s->td32ms=td; + + insert_td_horizontal (s, s->int_chain[5], td); + + mb(); + //uhci_show_queue(s->control_chain); + dbg("init_skel exit"); + return 0; + + init_skel_cleanup: + cleanup_skel (s); + return -ENOMEM; +} + +/*-------------------------------------------------------------------*/ +// LOW LEVEL STUFF +// assembles QHs und TDs for control, bulk and iso +/*-------------------------------------------------------------------*/ +_static int uhci_submit_control_urb (urb_t *urb) +{ + uhci_desc_t *qh, *td; + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + unsigned long destination, status; + int maxsze = usb_maxpacket (urb->dev, urb->pipe, usb_pipeout (urb->pipe)); + unsigned long len; + char *data; + int depth_first=USE_CTRL_DEPTH_FIRST; // UHCI descriptor chasing method + + if (!maxsze) { + err("uhci_submit_control_urb: pipesize for pipe %x is zero", urb->pipe); + return -EINVAL; + } + + dbg("uhci_submit_control start"); + alloc_qh (&qh); // alloc qh for this request + + if (!qh) + return -ENOMEM; + + alloc_td (&td, UHCI_PTR_DEPTH * depth_first); // get td for setup stage + + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } + + /* The "pipe" thing contains the destination in bits 8--18 */ + destination = (urb->pipe & PIPE_DEVEP_MASK) | USB_PID_SETUP; + + /* 3 errors */ + status = (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + + /* Build the TD for the control request, try forever, 8 bytes of data */ + fill_td (td, status, destination | (7 << 21), virt_to_bus (urb->setup_packet)); + + insert_td (s, qh, td, 0); // queue 'setup stage'-td in qh +#if 0 + { + char *sp=urb->setup_packet; + dbg("SETUP to pipe %x: %x %x %x %x %x %x %x %x", urb->pipe, + sp[0],sp[1],sp[2],sp[3],sp[4],sp[5],sp[6],sp[7]); + } + //uhci_show_td(td); +#endif + + len = urb->transfer_buffer_length; + data = urb->transfer_buffer; + + /* If direction is "send", change the frame from SETUP (0x2D) + to OUT (0xE1). Else change it from SETUP to IN (0x69). */ + + destination = (urb->pipe & PIPE_DEVEP_MASK) | (usb_pipeout (urb->pipe)?USB_PID_OUT:USB_PID_IN); + + while (len > 0) { + int pktsze = len; + + alloc_td (&td, UHCI_PTR_DEPTH * depth_first); + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } + + if (pktsze > maxsze) + pktsze = maxsze; + + destination ^= 1 << TD_TOKEN_TOGGLE; // toggle DATA0/1 + + fill_td (td, status, destination | ((pktsze - 1) << 21), + virt_to_bus (data)); // Status, pktsze bytes of data + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue 'data stage'-td in qh + + data += pktsze; + len -= pktsze; + } + + /* Build the final TD for control status */ + /* It's only IN if the pipe is out AND we aren't expecting data */ + + destination &= ~UHCI_PID; + + if (usb_pipeout (urb->pipe) || (urb->transfer_buffer_length == 0)) + destination |= USB_PID_IN; + else + destination |= USB_PID_OUT; + + destination |= 1 << TD_TOKEN_TOGGLE; /* End in Data1 */ + + alloc_td (&td, UHCI_PTR_DEPTH); + + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } + status &=~TD_CTRL_SPD; + + /* no limit on errors on final packet , 0 bytes of data */ + fill_td (td, status | TD_CTRL_IOC, destination | (UHCI_NULL_DATA_SIZE << 21), + 0); + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); // queue status td + + list_add (&qh->desc_list, &urb_priv->desc_list); + + urb->status = -EINPROGRESS; + queue_urb (s, urb); // queue before inserting in desc chain + + qh->hw.qh.element &= ~UHCI_PTR_TERM; + + //uhci_show_queue(qh); + /* Start it up... put low speed first */ + if (urb->pipe & TD_CTRL_LS) + insert_qh (s, s->control_chain, qh, 0); + else + insert_qh (s, s->bulk_chain, qh, 0); + + dbg("uhci_submit_control end"); + return 0; +} +/*-------------------------------------------------------------------*/ +// For queued bulk transfers, two additional QH helpers are allocated (nqh, bqh) +// Due to the linking with other bulk urbs, it has to be locked with urb_list_lock! + +_static int uhci_submit_bulk_urb (urb_t *urb, urb_t *bulk_urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + uhci_desc_t *qh, *td, *nqh, *bqh; + unsigned long destination, status; + char *data; + unsigned int pipe = urb->pipe; + int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); + int info, len; + int depth_first=USE_BULK_DEPTH_FIRST; // UHCI descriptor chasing method + urb_priv_t *upriv, *bpriv; + + if (usb_endpoint_halted (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe))) + return -EPIPE; + + if (urb->transfer_buffer_length < 0) { + err("Negative transfer length in submit_bulk"); + return -EINVAL; + } + + if (!maxsze) + return -EMSGSIZE; + + queue_dbg("uhci_submit_bulk_urb: urb %p, old %p, pipe %08x, len %i", + urb,bulk_urb,urb->pipe,urb->transfer_buffer_length); + + upriv=(urb_priv_t*)urb->hcpriv; + + if (!bulk_urb) { + alloc_qh (&qh); // get qh for this request + + if (!qh) + return -ENOMEM; + + if (urb->transfer_flags & USB_QUEUE_BULK) { + alloc_qh(&nqh); // placeholder for clean unlink + if (!nqh) { + delete_desc (qh); + return -ENOMEM; + } + upriv->next_qh = nqh; + queue_dbg("new next qh %p",nqh); + } + } + else { + bpriv = (urb_priv_t*)bulk_urb->hcpriv; + qh = bpriv->bottom_qh; // re-use bottom qh and next qh + nqh = bpriv->next_qh; + upriv->next_qh=nqh; + bpriv->next_queued_urb=urb; + upriv->prev_queued_urb=bulk_urb; + } + + queue_dbg("uhci_submit_bulk: qh=%p, nqh=%p\n",bqh,nqh); + + if (urb->transfer_flags & USB_QUEUE_BULK) { + alloc_qh (&bqh); // "bottom" QH, + + if (!bqh) { + if (!bulk_urb) { + delete_desc(qh); + delete_desc(nqh); + } + return -ENOMEM; + } + bqh->hw.qh.element = UHCI_PTR_TERM; + bqh->hw.qh.element = virt_to_bus(nqh)|UHCI_PTR_QH; + upriv->bottom_qh = bqh; + queue_dbg("uhci_submit_bulk: new bqh %p\n",bqh); + } + + + /* The "pipe" thing contains the destination in bits 8--18. */ + destination = (pipe & PIPE_DEVEP_MASK) | usb_packetid (pipe); + + /* 3 errors */ + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | + ((urb->transfer_flags & USB_DISABLE_SPD) ? 0 : TD_CTRL_SPD) | (3 << 27); + + /* Build the TDs for the bulk request */ + len = urb->transfer_buffer_length; + data = urb->transfer_buffer; + + do { // TBD: Really allow zero-length packets? + int pktsze = len; + + alloc_td (&td, UHCI_PTR_DEPTH * depth_first); + + if (!td) { + delete_qh (s, qh); + return -ENOMEM; + } + + if (pktsze > maxsze) + pktsze = maxsze; + + // pktsze bytes of data + info = destination | (((pktsze - 1)&UHCI_NULL_DATA_SIZE) << 21) | + (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); + + fill_td (td, status, info, virt_to_bus (data)); + + data += pktsze; + len -= pktsze; + + if (!len) + td->hw.td.status |= TD_CTRL_IOC; // last one generates INT + + insert_td (s, qh, td, UHCI_PTR_DEPTH * depth_first); + usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + } while (len > 0); + + list_add (&qh->desc_list, &urb_priv->desc_list); + + if (urb->transfer_flags & USB_QUEUE_BULK) { + qh->hw.qh.element&=~UHCI_PTR_TERM; + append_qh(s, td, bqh, UHCI_PTR_DEPTH * depth_first); + } + + urb->status = -EINPROGRESS; + queue_urb_unlocked (s, urb); + + qh->hw.qh.element &= ~UHCI_PTR_TERM; + + if (!bulk_urb) { + if (urb->transfer_flags & USB_QUEUE_BULK) { + spin_lock (&s->td_lock); // both QHs in one go + insert_qh (s, s->chain_end, qh, 0); // Main QH + insert_qh (s, s->chain_end, nqh, 0); // Helper QH + spin_unlock (&s->td_lock); + } + else + insert_qh (s, s->chain_end, qh, 0); + } + + //uhci_show_queue(s->bulk_chain); + //dbg("uhci_submit_bulk_urb: exit\n"); + return 0; +} +/*-------------------------------------------------------------------*/ +_static void uhci_clean_iso_step1(uhci_t *s, urb_priv_t *urb_priv) +{ + struct list_head *p; + uhci_desc_t *td; + + for (p = urb_priv->desc_list.next; p != &urb_priv->desc_list; p = p->next) { + td = list_entry (p, uhci_desc_t, desc_list); + unlink_td (s, td, 1); + } +} +/*-------------------------------------------------------------------*/ +_static void uhci_clean_iso_step2(uhci_t *s, urb_priv_t *urb_priv) +{ + struct list_head *p; + uhci_desc_t *td; + + while ((p = urb_priv->desc_list.next) != &urb_priv->desc_list) { + td = list_entry (p, uhci_desc_t, desc_list); + list_del (p); + delete_desc (td); + } +} +/*-------------------------------------------------------------------*/ +// mode: 0: unlink + no deletion mark, 1: regular (unlink/delete-mark), 2: don't unlink +// looks a bit complicated because of all the bulk queueing goodies + +_static void uhci_clean_transfer (uhci_t *s, urb_t *urb, uhci_desc_t *qh, int mode) +{ + uhci_desc_t *bqh, *nqh, *prevqh; + int now; + urb_priv_t *priv=(urb_priv_t*)urb->hcpriv; + + now=UHCI_GET_CURRENT_FRAME(s); + + dbg("clean transfer urb %p, qh %p, mode %i",urb,qh,mode); + bqh=priv->bottom_qh; + + if (!priv->next_queued_urb) { // no more appended bulk queues + + if (mode != 2) + unlink_qh (s, qh); + + if (priv->prev_queued_urb) { + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + + ppriv->bottom_qh = priv->bottom_qh; + ppriv->next_queued_urb = NULL; + } + else if (bqh) { // queue dead + nqh=priv->next_qh; + + if (mode != 2) + unlink_qh(s, nqh); + + if (mode) { + nqh->last_used = bqh->last_used = now; + list_add_tail (&nqh->horizontal, &s->free_desc); + list_add_tail (&bqh->horizontal, &s->free_desc); + } + } + } + else { // there are queued urbs following + urb_t *nurb; + unsigned long flags; + + nurb=priv->next_queued_urb; + spin_lock_irqsave (&s->qh_lock, flags); + + if (!priv->prev_queued_urb) { // top + if (mode !=2) { + prevqh = list_entry (qh->horizontal.prev, uhci_desc_t, horizontal); + prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; + queue_dbg ("TOP relink of %p to %p-%p",qh,prevqh,bqh); + + list_del (&qh->horizontal); + list_add (&bqh->horizontal, &prevqh->horizontal); + } + } + else { //intermediate + urb_priv_t* ppriv=(urb_priv_t*)priv->prev_queued_urb->hcpriv; + uhci_desc_t * bnqh; + + bnqh=list_entry (&((urb_priv_t*)(nurb->hcpriv))->desc_list.next, uhci_desc_t, desc_list); + ppriv->bottom_qh=bnqh; + ppriv->next_queued_urb=nurb; + + if (mode!=2) { + prevqh = list_entry (ppriv->desc_list.next, uhci_desc_t, desc_list); + prevqh->hw.qh.head = virt_to_bus(bqh) | UHCI_PTR_QH; + queue_dbg ("IM relink of %p to %p-%p",qh,prevqh,bqh); + } + } + mb(); + spin_unlock_irqrestore (&s->qh_lock, flags); + ((urb_priv_t*)nurb->hcpriv)->prev_queued_urb=priv->prev_queued_urb; + } + + if (mode) { + qh->last_used = now; + list_add_tail (&qh->horizontal, &s->free_desc); // mark for later deletion + } +} +/*-------------------------------------------------------------------*/ +// unlinks an urb by dequeuing its qh, waits some frames and forgets it +_static int uhci_unlink_urb_sync (uhci_t *s, urb_t *urb) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + unsigned long flags=0; + + spin_lock_irqsave (&s->urb_list_lock, flags); + + if (urb->status == -EINPROGRESS) { + // URB probably still in work + dequeue_urb (s, urb); + uhci_switch_timer_int(s); + s->unlink_urb_done=1; + spin_unlock_irqrestore (&s->urb_list_lock, flags); + + urb->status = -ENOENT; // mark urb as killed + urb_priv = urb->hcpriv; + + switch (usb_pipetype (urb->pipe)) { + + case PIPE_INTERRUPT: + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + + case PIPE_ISOCHRONOUS: + uhci_clean_iso_step1(s, urb_priv); + uhci_wait_ms(1); + uhci_clean_iso_step2(s, urb_priv); + break; + + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); + spin_lock_irqsave (&s->urb_list_lock, flags); + uhci_clean_transfer(s, urb, qh, 1); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + uhci_wait_ms(1); + } + +#ifdef DEBUG_SLAB + kmem_cache_free (urb_priv_kmem, urb->hcpriv); +#else + kfree (urb->hcpriv); +#endif + if (urb->complete) { + dbg("unlink_urb: calling completion"); + urb->complete ((struct urb *) urb); + } + usb_dec_dev_use (urb->dev); + return 0; + } + else + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return 0; +} +/*-------------------------------------------------------------------*/ +// async unlink_urb completion/cleanup work +// has to be protected by urb_list_lock! +// features: if set in transfer_flags, the resulting status of the killed +// transaction is not overwritten + +_static void uhci_cleanup_unlink(uhci_t *s, int force) +{ + struct list_head *q; + urb_t *urb; + struct usb_device *dev; + int pipe,now; + urb_priv_t *urb_priv; + + q=s->urb_unlinked.next; + now=UHCI_GET_CURRENT_FRAME(s); + + while (q != &s->urb_unlinked) { + + urb = list_entry (q, urb_t, urb_list); + + urb_priv = (urb_priv_t*)urb->hcpriv; + q = urb->urb_list.next; + + if (force || + ((urb_priv->started != 0xffffffff) && (urb_priv->started != now))) { + async_dbg("async cleanup %p",urb); + switch (usb_pipetype (urb->pipe)) { // process descriptors + case PIPE_CONTROL: + process_transfer (s, urb, 2); + break; + case PIPE_BULK: + if (!s->avoid_bulk.counter) + process_transfer (s, urb, 2); // don't unlink (already done) + else + continue; + break; + case PIPE_ISOCHRONOUS: + process_iso (s, urb, 1); // force, don't unlink + break; + case PIPE_INTERRUPT: + process_interrupt (s, urb); + break; + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ECONNRESET; // mark as asynchronously killed + + pipe = urb->pipe; // completion may destroy all... + dev = urb->dev; + urb_priv = urb->hcpriv; + + if (urb->complete) { + spin_unlock(&s->urb_list_lock); + urb->complete ((struct urb *) urb); + spin_lock(&s->urb_list_lock); + } + + if (!(urb->transfer_flags & USB_TIMEOUT_KILLED)) + urb->status = -ENOENT; // now the urb is really dead + switch (usb_pipetype (pipe)) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + uhci_clean_iso_step2(s, urb_priv); + break; + } + + usb_dec_dev_use (dev); +#ifdef DEBUG_SLAB + kmem_cache_free (urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + + list_del (&urb->urb_list); + } + } +} + +/*-------------------------------------------------------------------*/ +_static int uhci_unlink_urb_async (uhci_t *s,urb_t *urb) +{ + uhci_desc_t *qh; + urb_priv_t *urb_priv; + + async_dbg("unlink_urb_async called %p",urb); + + if ((urb->status == -EINPROGRESS) || + ((usb_pipetype (urb->pipe) == PIPE_INTERRUPT) && ((urb_priv_t*)urb->hcpriv)->flags)) + { + ((urb_priv_t*)urb->hcpriv)->started = ~0; + + dequeue_urb (s, urb); + list_add_tail (&urb->urb_list, &s->urb_unlinked); // store urb + uhci_switch_timer_int(s); + + s->unlink_urb_done = 1; + + urb->status = -ECONNABORTED; // mark urb as "waiting to be killed" + urb_priv = (urb_priv_t*)urb->hcpriv; + + switch (usb_pipetype (urb->pipe)) { + case PIPE_INTERRUPT: + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + + case PIPE_ISOCHRONOUS: + uhci_clean_iso_step1 (s, urb_priv); + break; + + case PIPE_BULK: + case PIPE_CONTROL: + qh = list_entry (urb_priv->desc_list.next, uhci_desc_t, desc_list); + uhci_clean_transfer (s, urb, qh, 0); + break; + } + ((urb_priv_t*)urb->hcpriv)->started = UHCI_GET_CURRENT_FRAME(s); + } + + return -EINPROGRESS; +} +/*-------------------------------------------------------------------*/ +_static int uhci_unlink_urb (urb_t *urb) +{ + uhci_t *s; + unsigned long flags=0; + dbg("uhci_unlink_urb called for %p",urb); + if (!urb || !urb->dev) // you never know... + return -EINVAL; + + s = (uhci_t*) urb->dev->bus->hcpriv; + + if (usb_pipedevice (urb->pipe) == s->rh.devnum) + return rh_unlink_urb (urb); + + if (!urb->hcpriv) + return -EINVAL; + + if (urb->transfer_flags & USB_ASYNC_UNLINK) { + int ret; + + spin_lock_irqsave (&s->urb_list_lock, flags); + ret = uhci_unlink_urb_async(s, urb); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return ret; + } + else + return uhci_unlink_urb_sync(s, urb); +} +/*-------------------------------------------------------------------*/ +// In case of ASAP iso transfer, search the URB-list for already queued URBs +// for this EP and calculate the earliest start frame for the new +// URB (easy seamless URB continuation!) +_static int find_iso_limits (urb_t *urb, unsigned int *start, unsigned int *end) +{ + urb_t *u, *last_urb = NULL; + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + struct list_head *p; + int ret=-1; + unsigned long flags; + + spin_lock_irqsave (&s->urb_list_lock, flags); + p=s->urb_list.prev; + + for (; p != &s->urb_list; p = p->prev) { + u = list_entry (p, urb_t, urb_list); + // look for pending URBs with identical pipe handle + // works only because iso doesn't toggle the data bit! + if ((urb->pipe == u->pipe) && (urb->dev == u->dev) && (u->status == -EINPROGRESS)) { + if (!last_urb) + *start = u->start_frame; + last_urb = u; + } + } + + if (last_urb) { + *end = (last_urb->start_frame + last_urb->number_of_packets) & 1023; + ret=0; + } + + spin_unlock_irqrestore(&s->urb_list_lock, flags); + + return ret; +} +/*-------------------------------------------------------------------*/ +// adjust start_frame according to scheduling constraints (ASAP etc) + +_static int iso_find_start (urb_t *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + unsigned int now; + unsigned int start_limit = 0, stop_limit = 0, queued_size; + int limits; + + now = UHCI_GET_CURRENT_FRAME (s) & 1023; + + if ((unsigned) urb->number_of_packets > 900) + return -EFBIG; + + limits = find_iso_limits (urb, &start_limit, &stop_limit); + queued_size = (stop_limit - start_limit) & 1023; + + if (urb->transfer_flags & USB_ISO_ASAP) { + // first iso + if (limits) { + // 10ms setup should be enough //FIXME! + urb->start_frame = (now + 10) & 1023; + } + else { + urb->start_frame = stop_limit; //seamless linkage + + if (((now - urb->start_frame) & 1023) <= (unsigned) urb->number_of_packets) { + info("iso_find_start: gap in seamless isochronous scheduling"); + dbg("iso_find_start: now %u start_frame %u number_of_packets %u pipe 0x%08x", + now, urb->start_frame, urb->number_of_packets, urb->pipe); + urb->start_frame = (now + 5) & 1023; // 5ms setup should be enough //FIXME! + } + } + } + else { + urb->start_frame &= 1023; + if (((now - urb->start_frame) & 1023) < (unsigned) urb->number_of_packets) { + dbg("iso_find_start: now between start_frame and end"); + return -EAGAIN; + } + } + + /* check if either start_frame or start_frame+number_of_packets-1 lies between start_limit and stop_limit */ + if (limits) + return 0; + + if (((urb->start_frame - start_limit) & 1023) < queued_size || + ((urb->start_frame + urb->number_of_packets - 1 - start_limit) & 1023) < queued_size) { + dbg("iso_find_start: start_frame %u number_of_packets %u start_limit %u stop_limit %u", + urb->start_frame, urb->number_of_packets, start_limit, stop_limit); + return -EAGAIN; + } + + return 0; +} +/*-------------------------------------------------------------------*/ +// submits USB interrupt (ie. polling ;-) +// ASAP-flag set implicitely +// if period==0, the the transfer is only done once + +_static int uhci_submit_int_urb (urb_t *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + int nint, n, ret; + uhci_desc_t *td; + int status, destination; + int info; + unsigned int pipe = urb->pipe; + + if (urb->interval < 0 || urb->interval >= 256) + return -EINVAL; + + if (urb->interval == 0) + nint = 0; + else { + for (nint = 0, n = 1; nint <= 8; nint++, n += n) // round interval down to 2^n + { + if (urb->interval < n) { + urb->interval = n / 2; + break; + } + } + nint--; + } + + dbg("Rounded interval to %i, chain %i", urb->interval, nint); + + urb->start_frame = UHCI_GET_CURRENT_FRAME (s) & 1023; // remember start frame, just in case... + + urb->number_of_packets = 1; + + // INT allows only one packet + if (urb->transfer_buffer_length > usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe))) + return -EINVAL; + + ret = alloc_td (&td, UHCI_PTR_DEPTH); + + if (ret) + return -ENOMEM; + + status = (pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe) | + (((urb->transfer_buffer_length - 1) & 0x7ff) << 21); + + + info = destination | (usb_gettoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)) << TD_TOKEN_TOGGLE); + + fill_td (td, status, info, virt_to_bus (urb->transfer_buffer)); + list_add_tail (&td->desc_list, &urb_priv->desc_list); + + urb->status = -EINPROGRESS; + queue_urb (s, urb); + + insert_td_horizontal (s, s->int_chain[nint], td); // store in INT-TDs + + usb_dotoggle (urb->dev, usb_pipeendpoint (pipe), usb_pipeout (pipe)); + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_iso_urb (urb_t *urb) +{ + uhci_t *s = (uhci_t*) urb->dev->bus->hcpriv; + urb_priv_t *urb_priv = urb->hcpriv; + int pipe=urb->pipe; + int maxsze = usb_maxpacket (urb->dev, pipe, usb_pipeout (pipe)); + int n, ret, last=0; + uhci_desc_t *td, **tdm; + int status, destination; + unsigned long flags; + + __save_flags(flags); + __cli(); // Disable IRQs to schedule all ISO-TDs in time + ret = iso_find_start (urb); // adjusts urb->start_frame for later use + + if (ret) + goto err; + + tdm = (uhci_desc_t **) kmalloc (urb->number_of_packets * sizeof (uhci_desc_t*), KMALLOC_FLAG); + + if (!tdm) { + ret = -ENOMEM; + goto err; + } + + // First try to get all TDs + for (n = 0; n < urb->number_of_packets; n++) { + dbg("n:%d urb->iso_frame_desc[n].length:%d", n, urb->iso_frame_desc[n].length); + if (!urb->iso_frame_desc[n].length) { + // allows ISO striping by setting length to zero in iso_descriptor + tdm[n] = 0; + continue; + } + + if(urb->iso_frame_desc[n].length > maxsze) { +#ifdef ISO_SANITY_CHECK + err("submit_iso: urb->iso_frame_desc[%d].length(%d)>%d",n , urb->iso_frame_desc[n].length, maxsze); + tdm[n] = 0; + ret=-EINVAL; + goto inval; +#endif + } + + ret = alloc_td (&td, UHCI_PTR_DEPTH); + inval: + if (ret) { + int i; // Cleanup allocated TDs + + for (i = 0; i < n; n++) + if (tdm[i]) + delete_desc(tdm[i]); + kfree (tdm); + goto err; + } + last=n; + tdm[n] = td; + } + + status = TD_CTRL_ACTIVE | TD_CTRL_IOS; //| (urb->transfer_flags&USB_DISABLE_SPD?0:TD_CTRL_SPD); + + destination = (urb->pipe & PIPE_DEVEP_MASK) | usb_packetid (urb->pipe); + + + // Queue all allocated TDs + for (n = 0; n < urb->number_of_packets; n++) { + td = tdm[n]; + if (!td) + continue; + + if (n == last) + status |= TD_CTRL_IOC; + + fill_td (td, status, destination | (((urb->iso_frame_desc[n].length - 1) & 0x7ff) << 21), + virt_to_bus (urb->transfer_buffer + urb->iso_frame_desc[n].offset)); + list_add_tail (&td->desc_list, &urb_priv->desc_list); + + if (n == last) { + urb->status = -EINPROGRESS; + queue_urb (s, urb); + } + insert_td_horizontal (s, s->iso_td[(urb->start_frame + n) & 1023], td); // store in iso-tds + //uhci_show_td(td); + + } + + kfree (tdm); + dbg("ISO-INT# %i, start %i, now %i", urb->number_of_packets, urb->start_frame, UHCI_GET_CURRENT_FRAME (s) & 1023); + ret = 0; + + err: + __restore_flags(flags); + return ret; + +} +/*-------------------------------------------------------------------*/ +// returns: 0 (no transfer queued), urb* (this urb already queued) + +_static urb_t* search_dev_ep (uhci_t *s, urb_t *urb) +{ + struct list_head *p; + urb_t *tmp; + unsigned int mask = usb_pipecontrol(urb->pipe) ? (~USB_DIR_IN) : (~0); + + dbg("search_dev_ep:"); + + p=s->urb_list.next; + + for (; p != &s->urb_list; p = p->next) { + tmp = list_entry (p, urb_t, urb_list); + dbg("urb: %p", tmp); + // we can accept this urb if it is not queued at this time + // or if non-iso transfer requests should be scheduled for the same device and pipe + if ((!usb_pipeisoc(urb->pipe) && (tmp->dev == urb->dev) && !((tmp->pipe ^ urb->pipe) & mask)) || + (urb == tmp)) { + return tmp; // found another urb already queued for processing + } + } + + return 0; +} +/*-------------------------------------------------------------------*/ +_static int uhci_submit_urb (urb_t *urb) +{ + uhci_t *s; + urb_priv_t *urb_priv; + int ret = 0; + unsigned long flags; + urb_t *bulk_urb=NULL; + + if (!urb->dev || !urb->dev->bus) + return -ENODEV; + + s = (uhci_t*) urb->dev->bus->hcpriv; + //dbg("submit_urb: %p type %d",urb,usb_pipetype(urb->pipe)); + + if (!s->running) + return -ENODEV; + + if (usb_pipedevice (urb->pipe) == s->rh.devnum) + return rh_submit_urb (urb); /* virtual root hub */ + + usb_inc_dev_use (urb->dev); + + spin_lock_irqsave (&s->urb_list_lock, flags); + + bulk_urb = search_dev_ep (s, urb); + + if (bulk_urb) { + + queue_dbg("found bulk urb %p\n",bulk_urb); + + if ((usb_pipetype (urb->pipe) != PIPE_BULK) || + ((usb_pipetype (urb->pipe) == PIPE_BULK) && + (!(urb->transfer_flags & USB_QUEUE_BULK) || !(bulk_urb->transfer_flags & USB_QUEUE_BULK)))) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + usb_dec_dev_use (urb->dev); + err("ENXIO %08x, flags %x, urb %p, burb %p",urb->pipe,urb->transfer_flags,urb,bulk_urb); + return -ENXIO; // urb already queued + } + } + +#ifdef DEBUG_SLAB + urb_priv = kmem_cache_alloc(urb_priv_kmem, SLAB_FLAG); +#else + urb_priv = kmalloc (sizeof (urb_priv_t), KMALLOC_FLAG); +#endif + if (!urb_priv) { + usb_dec_dev_use (urb->dev); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + return -ENOMEM; + } + + urb->hcpriv = urb_priv; + INIT_LIST_HEAD (&urb_priv->desc_list); + urb_priv->flags = 0; + dbg("submit_urb: scheduling %p", urb); + urb_priv->next_queued_urb = NULL; + urb_priv->prev_queued_urb = NULL; + urb_priv->bottom_qh = NULL; + urb_priv->next_qh = NULL; + + if (usb_pipetype (urb->pipe) == PIPE_BULK) { + + if (bulk_urb) { + while (((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb) // find last queued bulk + bulk_urb=((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb; + + ((urb_priv_t*)bulk_urb->hcpriv)->next_queued_urb=urb; + } + atomic_inc (&s->avoid_bulk); + ret = uhci_submit_bulk_urb (urb, bulk_urb); + atomic_dec (&s->avoid_bulk); + spin_unlock_irqrestore (&s->urb_list_lock, flags); + } + else { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + switch (usb_pipetype (urb->pipe)) { + case PIPE_ISOCHRONOUS: + ret = uhci_submit_iso_urb (urb); + break; + case PIPE_INTERRUPT: + ret = uhci_submit_int_urb (urb); + break; + case PIPE_CONTROL: + ret = uhci_submit_control_urb (urb); + break; + default: + ret = -EINVAL; + } + } + + dbg("submit_urb: scheduled with ret: %d", ret); + + if (ret != 0) { + usb_dec_dev_use (urb->dev); +#ifdef DEBUG_SLAB + kmem_cache_free(urb_priv_kmem, urb_priv); +#else + kfree (urb_priv); +#endif + return ret; + } + + return 0; +} + +// Checks for URB timeout and removes bandwidth reclamation +// if URB idles too long +_static void uhci_check_timeouts(uhci_t *s) +{ + struct list_head *p,*p2; + urb_t *urb; + int type; + + p = s->urb_list.prev; + + while (p != &s->urb_list) { + urb_priv_t *hcpriv; + + p2 = p; + p = p->prev; + urb = list_entry (p2, urb_t, urb_list); + type = usb_pipetype (urb->pipe); + + hcpriv = (urb_priv_t*)urb->hcpriv; + + if ( urb->timeout && + ((hcpriv->started + urb->timeout) < jiffies)) { + urb->transfer_flags |= USB_TIMEOUT_KILLED | USB_ASYNC_UNLINK; + async_dbg("uhci_check_timeout: timeout for %p",urb); + uhci_unlink_urb_async(s, urb); + } +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + else if (((type == PIPE_BULK) || (type == PIPE_CONTROL)) && + (hcpriv->use_loop) && + ((hcpriv->started + IDLE_TIMEOUT) < jiffies)) + disable_desc_loop(s, urb); +#endif + + } + s->timeout_check=jiffies; +} + +/*------------------------------------------------------------------- + Virtual Root Hub + -------------------------------------------------------------------*/ + +_static __u8 root_hub_dev_des[] = +{ + 0x12, /* __u8 bLength; */ + 0x01, /* __u8 bDescriptorType; Device */ + 0x00, /* __u16 bcdUSB; v1.0 */ + 0x01, + 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */ + 0x00, /* __u8 bDeviceSubClass; */ + 0x00, /* __u8 bDeviceProtocol; */ + 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */ + 0x00, /* __u16 idVendor; */ + 0x00, + 0x00, /* __u16 idProduct; */ + 0x00, + 0x00, /* __u16 bcdDevice; */ + 0x00, + 0x00, /* __u8 iManufacturer; */ + 0x02, /* __u8 iProduct; */ + 0x01, /* __u8 iSerialNumber; */ + 0x01 /* __u8 bNumConfigurations; */ +}; + + +/* Configuration descriptor */ +_static __u8 root_hub_config_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x02, /* __u8 bDescriptorType; Configuration */ + 0x19, /* __u16 wTotalLength; */ + 0x00, + 0x01, /* __u8 bNumInterfaces; */ + 0x01, /* __u8 bConfigurationValue; */ + 0x00, /* __u8 iConfiguration; */ + 0x40, /* __u8 bmAttributes; + Bit 7: Bus-powered, 6: Self-powered, 5 Remote-wakwup, 4..0: resvd */ + 0x00, /* __u8 MaxPower; */ + + /* interface */ + 0x09, /* __u8 if_bLength; */ + 0x04, /* __u8 if_bDescriptorType; Interface */ + 0x00, /* __u8 if_bInterfaceNumber; */ + 0x00, /* __u8 if_bAlternateSetting; */ + 0x01, /* __u8 if_bNumEndpoints; */ + 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */ + 0x00, /* __u8 if_bInterfaceSubClass; */ + 0x00, /* __u8 if_bInterfaceProtocol; */ + 0x00, /* __u8 if_iInterface; */ + + /* endpoint */ + 0x07, /* __u8 ep_bLength; */ + 0x05, /* __u8 ep_bDescriptorType; Endpoint */ + 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */ + 0x03, /* __u8 ep_bmAttributes; Interrupt */ + 0x08, /* __u16 ep_wMaxPacketSize; 8 Bytes */ + 0x00, + 0xff /* __u8 ep_bInterval; 255 ms */ +}; + + +_static __u8 root_hub_hub_des[] = +{ + 0x09, /* __u8 bLength; */ + 0x29, /* __u8 bDescriptorType; Hub-descriptor */ + 0x02, /* __u8 bNbrPorts; */ + 0x00, /* __u16 wHubCharacteristics; */ + 0x00, + 0x01, /* __u8 bPwrOn2pwrGood; 2ms */ + 0x00, /* __u8 bHubContrCurrent; 0 mA */ + 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */ + 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */ +}; + +/*-------------------------------------------------------------------------*/ +/* prepare Interrupt pipe transaction data; HUB INTERRUPT ENDPOINT */ +_static int rh_send_irq (urb_t *urb) +{ + int len = 1; + int i; + uhci_t *uhci = urb->dev->bus->hcpriv; + unsigned int io_addr = uhci->io_addr; + __u16 data = 0; + + for (i = 0; i < uhci->rh.numports; i++) { + data |= ((inw (io_addr + USBPORTSC1 + i * 2) & 0xa) > 0 ? (1 << (i + 1)) : 0); + len = (i + 1) / 8 + 1; + } + + *(__u16 *) urb->transfer_buffer = cpu_to_le16 (data); + urb->actual_length = len; + urb->status = 0; + + if ((data > 0) && (uhci->rh.send != 0)) { + dbg("Root-Hub INT complete: port1: %x port2: %x data: %x", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2), data); + urb->complete (urb); + } + return 0; +} + +/*-------------------------------------------------------------------------*/ +/* Virtual Root Hub INTs are polled by this timer every "intervall" ms */ +_static int rh_init_int_timer (urb_t *urb); + +_static void rh_int_timer_do (unsigned long ptr) +{ + int len; + urb_t *urb = (urb_t*) ptr; + uhci_t *uhci = urb->dev->bus->hcpriv; + + if (uhci->rh.send) { + len = rh_send_irq (urb); + if (len > 0) { + urb->actual_length = len; + if (urb->complete) + urb->complete (urb); + } + } + rh_init_int_timer (urb); +} + +/*-------------------------------------------------------------------------*/ +/* Root Hub INTs are polled by this timer, polling interval 20ms */ +/* This time is also used for URB-timeout checking */ + +_static int rh_init_int_timer (urb_t *urb) +{ + uhci_t *uhci = urb->dev->bus->hcpriv; + + uhci->rh.interval = urb->interval; + init_timer (&uhci->rh.rh_int_timer); + uhci->rh.rh_int_timer.function = rh_int_timer_do; + uhci->rh.rh_int_timer.data = (unsigned long) urb; + uhci->rh.rh_int_timer.expires = jiffies + (HZ * 20) / 1000; + add_timer (&uhci->rh.rh_int_timer); + + return 0; +} + +/*-------------------------------------------------------------------------*/ +#define OK(x) len = (x); break + +#define CLR_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) & ~(x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + +#define SET_RH_PORTSTAT(x) \ + status = inw(io_addr+USBPORTSC1+2*(wIndex-1)); \ + status = (status & 0xfff5) | (x); \ + outw(status, io_addr+USBPORTSC1+2*(wIndex-1)) + + +/*-------------------------------------------------------------------------*/ +/**** + ** Root Hub Control Pipe + *************************/ + + +_static int rh_submit_urb (urb_t *urb) +{ + struct usb_device *usb_dev = urb->dev; + uhci_t *uhci = usb_dev->bus->hcpriv; + unsigned int pipe = urb->pipe; + devrequest *cmd = (devrequest *) urb->setup_packet; + void *data = urb->transfer_buffer; + int leni = urb->transfer_buffer_length; + int len = 0; + int status = 0; + int stat = 0; + int i; + unsigned int io_addr = uhci->io_addr; + __u16 cstatus; + + __u16 bmRType_bReq; + __u16 wValue; + __u16 wIndex; + __u16 wLength; + + if (usb_pipetype (pipe) == PIPE_INTERRUPT) { + dbg("Root-Hub submit IRQ: every %d ms", urb->interval); + uhci->rh.urb = urb; + uhci->rh.send = 1; + uhci->rh.interval = urb->interval; + rh_init_int_timer (urb); + + return 0; + } + + + bmRType_bReq = cmd->requesttype | cmd->request << 8; + wValue = le16_to_cpu (cmd->value); + wIndex = le16_to_cpu (cmd->index); + wLength = le16_to_cpu (cmd->length); + + for (i = 0; i < 8; i++) + uhci->rh.c_p_r[i] = 0; + + dbg("Root-Hub: adr: %2x cmd(%1x): %04x %04x %04x %04x", + uhci->rh.devnum, 8, bmRType_bReq, wValue, wIndex, wLength); + + switch (bmRType_bReq) { + /* Request Destination: + without flags: Device, + RH_INTERFACE: interface, + RH_ENDPOINT: endpoint, + RH_CLASS means HUB here, + RH_OTHER | RH_CLASS almost ever means HUB_PORT here + */ + + case RH_GET_STATUS: + *(__u16 *) data = cpu_to_le16 (1); + OK (2); + case RH_GET_STATUS | RH_INTERFACE: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_ENDPOINT: + *(__u16 *) data = cpu_to_le16 (0); + OK (2); + case RH_GET_STATUS | RH_CLASS: + *(__u32 *) data = cpu_to_le32 (0); + OK (4); /* hub power ** */ + case RH_GET_STATUS | RH_OTHER | RH_CLASS: + status = inw (io_addr + USBPORTSC1 + 2 * (wIndex - 1)); + cstatus = ((status & USBPORTSC_CSC) >> (1 - 0)) | + ((status & USBPORTSC_PEC) >> (3 - 1)) | + (uhci->rh.c_p_r[wIndex - 1] << (0 + 4)); + status = (status & USBPORTSC_CCS) | + ((status & USBPORTSC_PE) >> (2 - 1)) | + ((status & USBPORTSC_SUSP) >> (12 - 2)) | + ((status & USBPORTSC_PR) >> (9 - 4)) | + (1 << 8) | /* power on ** */ + ((status & USBPORTSC_LSDA) << (-8 + 9)); + + *(__u16 *) data = cpu_to_le16 (status); + *(__u16 *) (data + 2) = cpu_to_le16 (cstatus); + OK (4); + + case RH_CLEAR_FEATURE | RH_ENDPOINT: + switch (wValue) { + case (RH_ENDPOINT_STALL): + OK (0); + } + break; + + case RH_CLEAR_FEATURE | RH_CLASS: + switch (wValue) { + case (RH_C_HUB_OVER_CURRENT): + OK (0); /* hub power over current ** */ + } + break; + + case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_ENABLE): + CLR_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + case (RH_PORT_SUSPEND): + CLR_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_C_PORT_CONNECTION): + SET_RH_PORTSTAT (USBPORTSC_CSC); + OK (0); + case (RH_C_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PEC); + OK (0); + case (RH_C_PORT_SUSPEND): +/*** WR_RH_PORTSTAT(RH_PS_PSSC); */ + OK (0); + case (RH_C_PORT_OVER_CURRENT): + OK (0); /* port power over current ** */ + case (RH_C_PORT_RESET): + uhci->rh.c_p_r[wIndex - 1] = 0; + OK (0); + } + break; + + case RH_SET_FEATURE | RH_OTHER | RH_CLASS: + switch (wValue) { + case (RH_PORT_SUSPEND): + SET_RH_PORTSTAT (USBPORTSC_SUSP); + OK (0); + case (RH_PORT_RESET): + SET_RH_PORTSTAT (USBPORTSC_PR); + uhci_wait_ms (10); + uhci->rh.c_p_r[wIndex - 1] = 1; + CLR_RH_PORTSTAT (USBPORTSC_PR); + udelay (10); + SET_RH_PORTSTAT (USBPORTSC_PE); + uhci_wait_ms (10); + SET_RH_PORTSTAT (0xa); + OK (0); + case (RH_PORT_POWER): + OK (0); /* port power ** */ + case (RH_PORT_ENABLE): + SET_RH_PORTSTAT (USBPORTSC_PE); + OK (0); + } + break; + + case RH_SET_ADDRESS: + uhci->rh.devnum = wValue; + OK (0); + + case RH_GET_DESCRIPTOR: + switch ((wValue & 0xff00) >> 8) { + case (0x01): /* device descriptor */ + len = min (leni, min (sizeof (root_hub_dev_des), wLength)); + memcpy (data, root_hub_dev_des, len); + OK (len); + case (0x02): /* configuration descriptor */ + len = min (leni, min (sizeof (root_hub_config_des), wLength)); + memcpy (data, root_hub_config_des, len); + OK (len); + case (0x03): /* string descriptors */ + len = usb_root_hub_string (wValue & 0xff, + uhci->io_addr, "UHCI", + data, wLength); + if (len > 0) { + OK (min (leni, len)); + } else + stat = -EPIPE; + } + break; + + case RH_GET_DESCRIPTOR | RH_CLASS: + root_hub_hub_des[2] = uhci->rh.numports; + len = min (leni, min (sizeof (root_hub_hub_des), wLength)); + memcpy (data, root_hub_hub_des, len); + OK (len); + + case RH_GET_CONFIGURATION: + *(__u8 *) data = 0x01; + OK (1); + + case RH_SET_CONFIGURATION: + OK (0); + default: + stat = -EPIPE; + } + + dbg("Root-Hub stat port1: %x port2: %x", + inw (io_addr + USBPORTSC1), inw (io_addr + USBPORTSC2)); + + urb->actual_length = len; + urb->status = stat; + if (urb->complete) + urb->complete (urb); + return 0; +} +/*-------------------------------------------------------------------------*/ + +_static int rh_unlink_urb (urb_t *urb) +{ + uhci_t *uhci = urb->dev->bus->hcpriv; + + if (uhci->rh.urb==urb) { + dbg("Root-Hub unlink IRQ"); + uhci->rh.send = 0; + del_timer (&uhci->rh.rh_int_timer); + } + return 0; +} +/*-------------------------------------------------------------------*/ + +/* + * Map status to standard result codes + * + * is (td->status & 0xFE0000) [a.k.a. uhci_status_bits(td->status) + * is True for output TDs and False for input TDs. + */ +_static int uhci_map_status (int status, int dir_out) +{ + if (!status) + return 0; + if (status & TD_CTRL_BITSTUFF) /* Bitstuff error */ + return -EPROTO; + if (status & TD_CTRL_CRCTIMEO) { /* CRC/Timeout */ + if (dir_out) + return -ETIMEDOUT; + else + return -EILSEQ; + } + if (status & TD_CTRL_NAK) /* NAK */ + return -ETIMEDOUT; + if (status & TD_CTRL_BABBLE) /* Babble */ + return -EPIPE; + if (status & TD_CTRL_DBUFERR) /* Buffer error */ + return -ENOSR; + if (status & TD_CTRL_STALLED) /* Stalled */ + return -EPIPE; + if (status & TD_CTRL_ACTIVE) /* Active */ + return 0; + + return -EPROTO; +} + +/* + * Only the USB core should call uhci_alloc_dev and uhci_free_dev + */ +_static int uhci_alloc_dev (struct usb_device *usb_dev) +{ + return 0; +} + +_static void uhci_unlink_urbs(uhci_t *s, struct usb_device *usb_dev, int remove_all) +{ + unsigned long flags; + struct list_head *p; + struct list_head *p2; + urb_t *urb; + + spin_lock_irqsave (&s->urb_list_lock, flags); + p = s->urb_list.prev; + while (p != &s->urb_list) { + p2 = p; + p = p->prev ; + urb = list_entry (p2, urb_t, urb_list); + dbg("urb: %p, dev %p, %p", urb, usb_dev,urb->dev); + + //urb->transfer_flags |=USB_ASYNC_UNLINK; + + if (remove_all || (usb_dev == urb->dev)) { + spin_unlock_irqrestore (&s->urb_list_lock, flags); + warn("forced removing of queued URB %p due to disconnect",urb); + uhci_unlink_urb(urb); + urb->dev = NULL; // avoid further processing of this UR + spin_lock_irqsave (&s->urb_list_lock, flags); + p = s->urb_list.prev; + } + } + spin_unlock_irqrestore (&s->urb_list_lock, flags); +} + +_static int uhci_free_dev (struct usb_device *usb_dev) +{ + uhci_t *s; + + + if(!usb_dev || !usb_dev->bus || !usb_dev->bus->hcpriv) + return -EINVAL; + + s=(uhci_t*) usb_dev->bus->hcpriv; + uhci_unlink_urbs(s, usb_dev, 0); + + return 0; +} + +/* + * uhci_get_current_frame_number() + * + * returns the current frame number for a USB bus/controller. + */ +_static int uhci_get_current_frame_number (struct usb_device *usb_dev) +{ + return UHCI_GET_CURRENT_FRAME ((uhci_t*) usb_dev->bus->hcpriv); +} + +struct usb_operations uhci_device_operations = +{ + uhci_alloc_dev, + uhci_free_dev, + uhci_get_current_frame_number, + uhci_submit_urb, + uhci_unlink_urb +}; + +/* + * For IN-control transfers, process_transfer gets a bit more complicated, + * since there are devices that return less data (eg. strings) than they + * have announced. This leads to a queue abort due to the short packet, + * the status stage is not executed. If this happens, the status stage + * is manually re-executed. + * mode: 0: QHs already unlinked + */ + +_static int process_transfer (uhci_t *s, urb_t *urb, int mode) +{ + int ret = 0; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *qhl = urb_priv->desc_list.next; + uhci_desc_t *qh = list_entry (qhl, uhci_desc_t, desc_list); + struct list_head *p = qh->vertical.next; + uhci_desc_t *desc= list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + uhci_desc_t *last_desc = list_entry (desc->vertical.prev, uhci_desc_t, vertical); + int data_toggle = usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); // save initial data_toggle + int maxlength; // extracted and remapped info from TD + int actual_length; + int status = 0; + + //dbg("process_transfer: urb contains bulk/control request"); + + /* if the status phase has been retriggered and the + queue is empty or the last status-TD is inactive, the retriggered + status stage is completed + */ + + if (urb_priv->flags && + ((qh->hw.qh.element == UHCI_PTR_TERM) ||(!(last_desc->hw.td.status & TD_CTRL_ACTIVE)))) + goto transfer_finished; + + urb->actual_length=0; + + for (; p != &qh->vertical; p = p->next) { + desc = list_entry (p, uhci_desc_t, vertical); + + if (desc->hw.td.status & TD_CTRL_ACTIVE) // do not process active TDs + return ret; + + actual_length = (desc->hw.td.status + 1) & 0x7ff; // extract transfer parameters from TD + maxlength = (((desc->hw.td.info >> 21) & 0x7ff) + 1) & 0x7ff; + status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe)); + + if (status == -EPIPE) { // see if EP is stalled + // set up stalled condition + usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } + + if (status != 0) { // if any error occured stop processing of further TDs + // only set ret if status returned an error + if (status != -EPIPE) + uhci_show_td (desc); + ret = status; + urb->error_count++; + break; + } + else if ((desc->hw.td.info & 0xff) != USB_PID_SETUP) + urb->actual_length += actual_length; + + // got less data than requested + if ( (actual_length < maxlength)) { + if (urb->transfer_flags & USB_DISABLE_SPD) { + status = -EREMOTEIO; // treat as real error + dbg("process_transfer: SPD!!"); + break; // exit after this TD because SP was detected + } + + // short read during control-IN: re-start status stage + if ((usb_pipetype (urb->pipe) == PIPE_CONTROL)) { + if (uhci_packetid(last_desc->hw.td.info) == USB_PID_OUT) { + + qh->hw.qh.element = virt_to_bus (last_desc); // re-trigger status stage + dbg("short packet during control transfer, retrigger status stage @ %p",last_desc); + //uhci_show_td (desc); + //uhci_show_td (last_desc); + urb_priv->flags = 1; // mark as short control packet + return 0; + } + } + // all other cases: short read is OK + data_toggle = uhci_toggle (desc->hw.td.info); + break; + } + + data_toggle = uhci_toggle (desc->hw.td.info); + queue_dbg("process_transfer: len:%d status:%x mapped:%x toggle:%d", actual_length, desc->hw.td.status,status, data_toggle); + + } + + usb_settoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe), !data_toggle); + + transfer_finished: + + uhci_clean_transfer(s, urb, qh, (mode==0?2:1)); + + urb->status = status; + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + disable_desc_loop(s,urb); +#endif + + queue_dbg("process_transfer: (end) urb %p, wanted len %d, len %d status %x err %d", + urb,urb->transfer_buffer_length,urb->actual_length, urb->status, urb->error_count); + return ret; +} + +_static int process_interrupt (uhci_t *s, urb_t *urb) +{ + int i, ret = -EINPROGRESS; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *p = urb_priv->desc_list.next; + uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + + int actual_length; + int status = 0; + + //dbg("urb contains interrupt request"); + + for (i = 0; p != &urb_priv->desc_list; p = p->next, i++) // Maybe we allow more than one TD later ;-) + { + desc = list_entry (p, uhci_desc_t, desc_list); + + if (desc->hw.td.status & TD_CTRL_ACTIVE) { + // do not process active TDs + //dbg("TD ACT Status @%p %08x",desc,desc->hw.td.status); + break; + } + + if (!desc->hw.td.status & TD_CTRL_IOC) { + // do not process one-shot TDs, no recycling + break; + } + // extract transfer parameters from TD + + actual_length = (desc->hw.td.status + 1) & 0x7ff; + status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe)); + + // see if EP is stalled + if (status == -EPIPE) { + // set up stalled condition + usb_endpoint_halt (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } + + // if any error occured: ignore this td, and continue + if (status != 0) { + //uhci_show_td (desc); + urb->error_count++; + goto recycle; + } + else + urb->actual_length = actual_length; + + recycle: + if (urb->complete) { + //dbg("process_interrupt: calling completion, status %i",status); + urb->status = status; + ((urb_priv_t*)urb->hcpriv)->flags=1; // if unlink_urb is called during completion + + spin_unlock(&s->urb_list_lock); + + urb->complete ((struct urb *) urb); + + spin_lock(&s->urb_list_lock); + + ((urb_priv_t*)urb->hcpriv)->flags=0; + } + + if ((urb->status != -ECONNABORTED) && (urb->status != ECONNRESET) && + (urb->status != -ENOENT)) { + + urb->status = -EINPROGRESS; + + // Recycle INT-TD if interval!=0, else mark TD as one-shot + if (urb->interval) { + + desc->hw.td.info &= ~(1 << TD_TOKEN_TOGGLE); + if (status==0) { + ((urb_priv_t*)urb->hcpriv)->started=jiffies; + desc->hw.td.info |= (usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + usb_dotoggle (urb->dev, usb_pipeendpoint (urb->pipe), usb_pipeout (urb->pipe)); + } else { + desc->hw.td.info |= (!usb_gettoggle (urb->dev, usb_pipeendpoint (urb->pipe), + usb_pipeout (urb->pipe)) << TD_TOKEN_TOGGLE); + } + desc->hw.td.status= (urb->pipe & TD_CTRL_LS) | TD_CTRL_ACTIVE | TD_CTRL_IOC | + (urb->transfer_flags & USB_DISABLE_SPD ? 0 : TD_CTRL_SPD) | (3 << 27); + mb(); + } + else { + uhci_unlink_urb_async(s, urb); + desc->hw.td.status &= ~TD_CTRL_IOC; // inactivate TD + } + } + } + + return ret; +} + +// mode: 1: force processing, don't unlink tds (already unlinked) +_static int process_iso (uhci_t *s, urb_t *urb, int mode) +{ + int i; + int ret = 0; + urb_priv_t *urb_priv = urb->hcpriv; + struct list_head *p = urb_priv->desc_list.next; + uhci_desc_t *desc = list_entry (urb_priv->desc_list.prev, uhci_desc_t, desc_list); + + dbg("urb contains iso request"); + if ((desc->hw.td.status & TD_CTRL_ACTIVE) && !mode) + return -EXDEV; // last TD not finished + + urb->error_count = 0; + urb->actual_length = 0; + urb->status = 0; + dbg("process iso urb %p, %li, %i, %i, %i %08x",urb,jiffies,UHCI_GET_CURRENT_FRAME(s), + urb->number_of_packets,mode,desc->hw.td.status); + + for (i = 0; p != &urb_priv->desc_list; i++) { + desc = list_entry (p, uhci_desc_t, desc_list); + + //uhci_show_td(desc); + if (desc->hw.td.status & TD_CTRL_ACTIVE) { + // means we have completed the last TD, but not the TDs before + desc->hw.td.status &= ~TD_CTRL_ACTIVE; + dbg("TD still active (%x)- grrr. paranoia!", desc->hw.td.status); + ret = -EXDEV; + urb->iso_frame_desc[i].status = ret; + unlink_td (s, desc, 1); + // FIXME: immediate deletion may be dangerous + goto err; + } + + if (!mode) + unlink_td (s, desc, 1); + + if (urb->number_of_packets <= i) { + dbg("urb->number_of_packets (%d)<=(%d)", urb->number_of_packets, i); + ret = -EINVAL; + goto err; + } + + if (urb->iso_frame_desc[i].offset + urb->transfer_buffer != bus_to_virt (desc->hw.td.buffer)) { + // Hm, something really weird is going on + dbg("Pointer Paranoia: %p!=%p", urb->iso_frame_desc[i].offset + urb->transfer_buffer, bus_to_virt (desc->hw.td.buffer)); + ret = -EINVAL; + urb->iso_frame_desc[i].status = ret; + goto err; + } + urb->iso_frame_desc[i].actual_length = (desc->hw.td.status + 1) & 0x7ff; + urb->iso_frame_desc[i].status = uhci_map_status (uhci_status_bits (desc->hw.td.status), usb_pipeout (urb->pipe)); + urb->actual_length += urb->iso_frame_desc[i].actual_length; + + err: + + if (urb->iso_frame_desc[i].status != 0) { + urb->error_count++; + urb->status = urb->iso_frame_desc[i].status; + } + dbg("process_iso: %i: len:%d %08x status:%x", + i, urb->iso_frame_desc[i].actual_length, desc->hw.td.status,urb->iso_frame_desc[i].status); + + list_del (p); + p = p->next; + delete_desc (desc); + } + + dbg("process_iso: exit %i (%d), actual_len %i", i, ret,urb->actual_length); + return ret; +} + + +_static int process_urb (uhci_t *s, struct list_head *p) +{ + int ret = 0; + urb_t *urb; + + + urb=list_entry (p, urb_t, urb_list); + //dbg("process_urb: found queued urb: %p", urb); + + switch (usb_pipetype (urb->pipe)) { + case PIPE_CONTROL: + ret = process_transfer (s, urb, 1); + break; + case PIPE_BULK: + if (!s->avoid_bulk.counter) + ret = process_transfer (s, urb, 1); + else + return 0; + break; + case PIPE_ISOCHRONOUS: + ret = process_iso (s, urb, 0); + break; + case PIPE_INTERRUPT: + ret = process_interrupt (s, urb); + break; + } + + if (urb->status != -EINPROGRESS) { + int proceed = 0; + + dbg("dequeued urb: %p", urb); + dequeue_urb (s, urb); + +#ifdef DEBUG_SLAB + kmem_cache_free(urb_priv_kmem, urb->hcpriv); +#else + kfree (urb->hcpriv); +#endif + + if ((usb_pipetype (urb->pipe) != PIPE_INTERRUPT)) { + urb_t *tmp = urb->next; // pointer to first urb + int is_ring = 0; + + if (urb->next) { + do { + if (tmp->status != -EINPROGRESS) { + proceed = 1; + break; + } + tmp = tmp->next; + } + while (tmp != NULL && tmp != urb->next); + if (tmp == urb->next) + is_ring = 1; + } + + spin_unlock(&s->urb_list_lock); + + // In case you need the current URB status for your completion handler + if (urb->complete && (!proceed || (urb->transfer_flags & USB_URB_EARLY_COMPLETE))) { + dbg("process_transfer: calling early completion"); + urb->complete ((struct urb *) urb); + if (!proceed && is_ring && (urb->status != -ENOENT)) + uhci_submit_urb (urb); + } + + if (proceed && urb->next) { + // if there are linked urbs - handle submitting of them right now. + tmp = urb->next; // pointer to first urb + + do { + if ((tmp->status != -EINPROGRESS) && (tmp->status != -ENOENT) && uhci_submit_urb (tmp) != 0) + break; + tmp = tmp->next; + } + while (tmp != NULL && tmp != urb->next); // submit until we reach NULL or our own pointer or submit fails + + if (urb->complete && !(urb->transfer_flags & USB_URB_EARLY_COMPLETE)) { + dbg("process_transfer: calling completion"); + urb->complete ((struct urb *) urb); + } + } + + spin_lock(&s->urb_list_lock); + + usb_dec_dev_use (urb->dev); + } + } + + return ret; +} + +_static void uhci_interrupt (int irq, void *__uhci, struct pt_regs *regs) +{ + uhci_t *s = __uhci; + unsigned int io_addr = s->io_addr; + unsigned short status; + struct list_head *p, *p2; + + /* + * Read the interrupt status, and write it back to clear the + * interrupt cause + */ + + status = inw (io_addr + USBSTS); + + if (!status) /* shared interrupt, not mine */ + return; + + dbg("interrupt"); + + if (status != 1) { + /* warn("interrupt, status %x, frame# %i", status, + UHCI_GET_CURRENT_FRAME(s)); */ + + // remove host controller halted state + if ((status&0x20) && (s->running)) { + outw (USBCMD_RS | inw(io_addr + USBCMD), io_addr + USBCMD); + } + //uhci_show_status (s); + } + /* + * traverse the list in *reverse* direction, because new entries + * may be added at the end. + * also, because process_urb may unlink the current urb, + * we need to advance the list before + */ + + spin_lock (&s->urb_list_lock); +restart: + s->unlink_urb_done=0; + p = s->urb_list.prev; + + while (p != &s->urb_list) { + p2 = p; + p = p->prev; + process_urb (s, p2); + if (s->unlink_urb_done) { + s->unlink_urb_done=0; + goto restart; + } + } + if ((jiffies - s->timeout_check) > (HZ/30)) + uhci_check_timeouts(s); + + clean_descs(s,0); + uhci_cleanup_unlink(s, 0); + uhci_switch_timer_int(s); + + spin_unlock (&s->urb_list_lock); + + outw (status, io_addr + USBSTS); + + //dbg("uhci_interrupt: done"); +} + +_static void reset_hc (uhci_t *s) +{ + unsigned int io_addr = s->io_addr; + + s->apm_state = 0; + /* Global reset for 50ms */ + outw (USBCMD_GRESET, io_addr + USBCMD); + uhci_wait_ms (50); + outw (0, io_addr + USBCMD); + uhci_wait_ms (10); +} + +_static void start_hc (uhci_t *s) +{ + unsigned int io_addr = s->io_addr; + int timeout = 1000; + + /* + * Reset the HC - this will force us to get a + * new notification of any already connected + * ports due to the virtual disconnect that it + * implies. + */ + outw (USBCMD_HCRESET, io_addr + USBCMD); + + while (inw (io_addr + USBCMD) & USBCMD_HCRESET) { + if (!--timeout) { + err("USBCMD_HCRESET timed out!"); + break; + } + } + + /* Turn on all interrupts */ + outw (USBINTR_TIMEOUT | USBINTR_RESUME | USBINTR_IOC | USBINTR_SP, io_addr + USBINTR); + + /* Start at frame 0 */ + outw (0, io_addr + USBFRNUM); + outl (virt_to_bus (s->framelist), io_addr + USBFLBASEADD); + + /* Run and mark it configured with a 64-byte max packet */ + outw (USBCMD_RS | USBCMD_CF | USBCMD_MAXP, io_addr + USBCMD); + s->apm_state = 1; + s->running = 1; +} + +_static void uhci_cleanup_dev(uhci_t *s) +{ + struct usb_device *root_hub = s->bus->root_hub; + + s->running = 0; // Don't allow submit_urb + + if (root_hub) + usb_disconnect (&root_hub); + + reset_hc (s); + wait_ms (1); + + uhci_unlink_urbs (s, 0, 1); // Forced unlink of remaining URBs + uhci_cleanup_unlink (s, 1); // force cleanup of async killed URBs + + usb_deregister_bus (s->bus); + + release_region (s->io_addr, s->io_size); + free_irq (s->irq, s); + usb_free_bus (s->bus); + cleanup_skel (s); + kfree (s); +} + +_static int __init uhci_start_usb (uhci_t *s) +{ /* start it up */ + /* connect the virtual root hub */ + struct usb_device *usb_dev; + + usb_dev = usb_alloc_dev (NULL, s->bus); + if (!usb_dev) + return -1; + + s->bus->root_hub = usb_dev; + usb_connect (usb_dev); + + if (usb_new_device (usb_dev) != 0) { + usb_free_dev (usb_dev); + return -1; + } + + return 0; +} + +_static int handle_pm_event (struct pm_dev *dev, pm_request_t rqst, void *data) +{ + uhci_t *s = (uhci_t*) dev->data; + dbg("handle_apm_event(%d)", rqst); + if (s) { + switch (rqst) { + case PM_SUSPEND: + reset_hc (s); + break; + case PM_RESUME: + start_hc (s); + break; + } + } + return 0; +} + +_static int __init alloc_uhci (struct pci_dev *dev, int irq, unsigned int io_addr, unsigned int io_size) +{ + uhci_t *s; + struct usb_bus *bus; + struct pm_dev *pmdev; + char buf[8], *bufp = buf; + +#ifndef __sparc__ + sprintf(buf, "%d", irq); +#else + bufp = __irq_itoa(irq); +#endif + printk(KERN_INFO __FILE__ ": USB UHCI at I/O 0x%x, IRQ %s\n", + io_addr, bufp); + + s = kmalloc (sizeof (uhci_t), GFP_KERNEL); + if (!s) + return -1; + + memset (s, 0, sizeof (uhci_t)); + INIT_LIST_HEAD (&s->free_desc); + INIT_LIST_HEAD (&s->urb_list); + INIT_LIST_HEAD (&s->urb_unlinked); + spin_lock_init (&s->urb_list_lock); + spin_lock_init (&s->qh_lock); + spin_lock_init (&s->td_lock); + atomic_set(&s->avoid_bulk, 0); + s->timeout_urbs = 0; + s->irq = -1; + s->io_addr = io_addr; + s->io_size = io_size; + s->next = devs; //chain new uhci device into global list + s->timeout_check = 0; + s->uhci_pci=dev; + + bus = usb_alloc_bus (&uhci_device_operations); + if (!bus) { + kfree (s); + return -1; + } + + s->bus = bus; + bus->hcpriv = s; + + /* UHCI specs says devices must have 2 ports, but goes on to say */ + /* they may have more but give no way to determine how many they */ + /* have, so default to 2 */ + /* According to the UHCI spec, Bit 7 is always set to 1. So we try */ + /* to use this to our advantage */ + + for (s->maxports = 0; s->maxports < (io_size - 0x10) / 2; s->maxports++) { + unsigned int portstatus; + + portstatus = inw (io_addr + 0x10 + (s->maxports * 2)); + dbg("port %i, adr %x status %x", s->maxports, + io_addr + 0x10 + (s->maxports * 2), portstatus); + if (!(portstatus & 0x0080)) + break; + } + warn("Detected %d ports", s->maxports); + + /* This is experimental so anything less than 2 or greater than 8 is */ + /* something weird and we'll ignore it */ + if (s->maxports < 2 || s->maxports > 8) { + dbg("Port count misdetected, forcing to 2 ports"); + s->maxports = 2; + } + + s->rh.numports = s->maxports; + s->loop_usage=0; + if (init_skel (s)) { + usb_free_bus (bus); + kfree(s); + return -1; + } + + request_region (s->io_addr, io_size, MODNAME); + reset_hc (s); + usb_register_bus (s->bus); + + start_hc (s); + + if (request_irq (irq, uhci_interrupt, SA_SHIRQ, MODNAME, s)) { + err("request_irq %d failed!",irq); + usb_free_bus (bus); + reset_hc (s); + release_region (s->io_addr, s->io_size); + cleanup_skel(s); + kfree(s); + return -1; + } + + s->irq = irq; + + if(uhci_start_usb (s) < 0) { + uhci_cleanup_dev(s); + return -1; + } + + //chain new uhci device into global list + devs = s; + + pmdev = pm_register(PM_PCI_DEV, PM_PCI_ID(dev), handle_pm_event); + if (pmdev) + pmdev->data = s; + + return 0; +} + +_static int __init start_uhci (struct pci_dev *dev) +{ + int i; + + /* Search for the IO base address.. */ + for (i = 0; i < 6; i++) { + + unsigned int io_addr = dev->base_address[i]; + unsigned int io_size = 0x14; + if (!(io_addr & 1)) + continue; + io_addr &= ~1; + + /* Is it already in use? */ + if (check_region (io_addr, io_size)) + break; + /* disable legacy emulation */ + pci_write_config_word (dev, USBLEGSUP, USBLEGSUP_DEFAULT); + if(dev->vendor==0x8086) { + info("Intel USB controller: setting latency timer to %d", UHCI_LATENCY_TIMER); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, UHCI_LATENCY_TIMER); + } + + return alloc_uhci(dev, dev->irq, io_addr, io_size); + } + return -1; +} + +int __init uhci_init (void) +{ + int retval = -ENODEV; + struct pci_dev *dev = NULL; + u8 type; + int i=0; + +#ifdef DEBUG_SLAB + + uhci_desc_kmem = kmem_cache_create("uhci_desc", sizeof(uhci_desc_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if(!uhci_desc_kmem) { + err("kmem_cache_create for uhci_desc failed (out of memory)"); + return -ENOMEM; + } + + urb_priv_kmem = kmem_cache_create("urb_priv", sizeof(urb_priv_t), 0, SLAB_HWCACHE_ALIGN, NULL, NULL); + + if(!urb_priv_kmem) { + err("kmem_cache_create for urb_priv_t failed (out of memory)"); + return -ENOMEM; + } +#endif + info(VERSTR); + +#ifdef CONFIG_USB_UHCI_HIGH_BANDWIDTH + info("High bandwidth mode enabled"); +#endif + for (;;) { + dev = pci_find_class (PCI_CLASS_SERIAL_USB << 8, dev); + if (!dev) + break; + + /* Is it UHCI */ + pci_read_config_byte (dev, PCI_CLASS_PROG, &type); + if (type != 0) + continue; + + if (pci_enable_device (dev) < 0) + continue; + + if(!dev->irq) + { + err("Found UHCI device with no IRQ assigned. Check BIOS settings!"); + continue; + } + + /* Ok set it up */ + retval = start_uhci (dev); + + if (!retval) + i++; + } + + return retval; +} + +void __exit uhci_cleanup (void) +{ + uhci_t *s; + while ((s = devs)) { + devs = devs->next; + uhci_cleanup_dev(s); + } +#ifdef DEBUG_SLAB + if(kmem_cache_destroy(uhci_desc_kmem)) + err("uhci_desc_kmem remained"); + + if(kmem_cache_destroy(urb_priv_kmem)) + err("urb_priv_kmem remained"); +#endif +} + +#ifdef MODULE +int init_module (void) +{ + return uhci_init (); +} + +void cleanup_module (void) +{ + pm_unregister_all (handle_pm_event); + uhci_cleanup (); +} + +#endif //MODULE diff --git a/drivers/usb/usb-uhci.h b/drivers/usb/usb-uhci.h new file mode 100644 index 000000000000..67eb4d210cbf --- /dev/null +++ b/drivers/usb/usb-uhci.h @@ -0,0 +1,282 @@ +#ifndef __LINUX_UHCI_H +#define __LINUX_UHCI_H + +/* + $Id: usb-uhci.h,v 1.55 2000/05/13 12:50:30 acher Exp $ + */ +#define MODNAME "usb-uhci" +#define UHCI_LATENCY_TIMER 0 + +static __inline__ void uhci_wait_ms(unsigned int ms) +{ + if(!in_interrupt()) + { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + } + else + mdelay(ms); +} + +/* Command register */ +#define USBCMD 0 +#define USBCMD_RS 0x0001 /* Run/Stop */ +#define USBCMD_HCRESET 0x0002 /* Host reset */ +#define USBCMD_GRESET 0x0004 /* Global reset */ +#define USBCMD_EGSM 0x0008 /* Global Suspend Mode */ +#define USBCMD_FGR 0x0010 /* Force Global Resume */ +#define USBCMD_SWDBG 0x0020 /* SW Debug mode */ +#define USBCMD_CF 0x0040 /* Config Flag (sw only) */ +#define USBCMD_MAXP 0x0080 /* Max Packet (0 = 32, 1 = 64) */ + +/* Status register */ +#define USBSTS 2 +#define USBSTS_USBINT 0x0001 /* Interrupt due to IOC */ +#define USBSTS_ERROR 0x0002 /* Interrupt due to error */ +#define USBSTS_RD 0x0004 /* Resume Detect */ +#define USBSTS_HSE 0x0008 /* Host System Error - basically PCI problems */ +#define USBSTS_HCPE 0x0010 /* Host Controller Process Error - the scripts were buggy */ +#define USBSTS_HCH 0x0020 /* HC Halted */ + +/* Interrupt enable register */ +#define USBINTR 4 +#define USBINTR_TIMEOUT 0x0001 /* Timeout/CRC error enable */ +#define USBINTR_RESUME 0x0002 /* Resume interrupt enable */ +#define USBINTR_IOC 0x0004 /* Interrupt On Complete enable */ +#define USBINTR_SP 0x0008 /* Short packet interrupt enable */ + +#define USBFRNUM 6 +#define USBFLBASEADD 8 +#define USBSOF 12 + +/* USB port status and control registers */ +#define USBPORTSC1 16 +#define USBPORTSC2 18 +#define USBPORTSC_CCS 0x0001 /* Current Connect Status ("device present") */ +#define USBPORTSC_CSC 0x0002 /* Connect Status Change */ +#define USBPORTSC_PE 0x0004 /* Port Enable */ +#define USBPORTSC_PEC 0x0008 /* Port Enable Change */ +#define USBPORTSC_LS 0x0030 /* Line Status */ +#define USBPORTSC_RD 0x0040 /* Resume Detect */ +#define USBPORTSC_LSDA 0x0100 /* Low Speed Device Attached */ +#define USBPORTSC_PR 0x0200 /* Port Reset */ +#define USBPORTSC_SUSP 0x1000 /* Suspend */ + +/* Legacy support register */ +#define USBLEGSUP 0xc0 +#define USBLEGSUP_DEFAULT 0x2000 /* only PIRQ enable set */ + +#define UHCI_NULL_DATA_SIZE 0x7ff /* for UHCI controller TD */ +#define UHCI_PID 0xff /* PID MASK */ + +#define UHCI_PTR_BITS 0x000F +#define UHCI_PTR_TERM 0x0001 +#define UHCI_PTR_QH 0x0002 +#define UHCI_PTR_DEPTH 0x0004 + +#define UHCI_NUMFRAMES 1024 /* in the frame list [array] */ +#define UHCI_MAX_SOF_NUMBER 2047 /* in an SOF packet */ +#define CAN_SCHEDULE_FRAMES 1000 /* how far future frames can be scheduled */ + +/* + * for TD : + */ +#define TD_CTRL_SPD (1 << 29) /* Short Packet Detect */ +#define TD_CTRL_C_ERR_MASK (3 << 27) /* Error Counter bits */ +#define TD_CTRL_LS (1 << 26) /* Low Speed Device */ +#define TD_CTRL_IOS (1 << 25) /* Isochronous Select */ +#define TD_CTRL_IOC (1 << 24) /* Interrupt on Complete */ +#define TD_CTRL_ACTIVE (1 << 23) /* TD Active */ +#define TD_CTRL_STALLED (1 << 22) /* TD Stalled */ +#define TD_CTRL_DBUFERR (1 << 21) /* Data Buffer Error */ +#define TD_CTRL_BABBLE (1 << 20) /* Babble Detected */ +#define TD_CTRL_NAK (1 << 19) /* NAK Received */ +#define TD_CTRL_CRCTIMEO (1 << 18) /* CRC/Time Out Error */ +#define TD_CTRL_BITSTUFF (1 << 17) /* Bit Stuff Error */ +#define TD_CTRL_ACTLEN_MASK 0x7ff /* actual length, encoded as n - 1 */ + +#define TD_CTRL_ANY_ERROR (TD_CTRL_STALLED | TD_CTRL_DBUFERR | \ + TD_CTRL_BABBLE | TD_CTRL_CRCTIME | TD_CTRL_BITSTUFF) + +#define uhci_status_bits(ctrl_sts) (ctrl_sts & 0xFE0000) +#define uhci_actual_length(ctrl_sts) ((ctrl_sts + 1) & TD_CTRL_ACTLEN_MASK) /* 1-based */ +#define uhci_ptr_to_virt(x) bus_to_virt(x & ~UHCI_PTR_BITS) + +/* + * for TD : + */ +#define UHCI_TD_REMOVE 0x0001 /* Remove when done */ + +/* + * for TD : (a.k.a. Token) + */ +#define TD_TOKEN_TOGGLE 19 + +#define uhci_maxlen(token) ((token) >> 21) +#define uhci_toggle(token) (((token) >> TD_TOKEN_TOGGLE) & 1) +#define uhci_endpoint(token) (((token) >> 15) & 0xf) +#define uhci_devaddr(token) (((token) >> 8) & 0x7f) +#define uhci_devep(token) (((token) >> 8) & 0x7ff) +#define uhci_packetid(token) ((token) & 0xff) +#define uhci_packetout(token) (uhci_packetid(token) != USB_PID_IN) +#define uhci_packetin(token) (uhci_packetid(token) == USB_PID_IN) + +/* ------------------------------------------------------------------------------------ + New TD/QH-structures + ------------------------------------------------------------------------------------ */ +typedef enum { + TD_TYPE, QH_TYPE +} uhci_desc_type_t; + +typedef struct { + __u32 link; + __u32 status; + __u32 info; + __u32 buffer; +} uhci_td_t, *puhci_td_t; + +typedef struct { + __u32 head; + __u32 element; /* Queue element pointer */ +} uhci_qh_t, *puhci_qh_t; + +typedef struct { + union { + uhci_td_t td; + uhci_qh_t qh; + } hw; + uhci_desc_type_t type; + struct list_head horizontal; + struct list_head vertical; + struct list_head desc_list; + int last_used; +} uhci_desc_t, *puhci_desc_t; + +typedef struct { + struct list_head desc_list; // list pointer to all corresponding TDs/QHs associated with this request + unsigned long started; + urb_t *next_queued_urb; // next queued urb for this EP + urb_t *prev_queued_urb; + uhci_desc_t *bottom_qh; + uhci_desc_t *next_qh; // next helper QH + char use_loop; + char flags; +} urb_priv_t, *purb_priv_t; + +struct virt_root_hub { + int devnum; /* Address of Root Hub endpoint */ + void *urb; + void *int_addr; + int send; + int interval; + int numports; + int c_p_r[8]; + struct timer_list rh_int_timer; +}; + +typedef struct uhci { + int irq; + unsigned int io_addr; + unsigned int io_size; + unsigned int maxports; + int running; + + int apm_state; + + struct uhci *next; // chain of uhci device contexts + + struct list_head urb_list; // list of all pending urbs + + spinlock_t urb_list_lock; // lock to keep consistency + + int unlink_urb_done; + atomic_t avoid_bulk; + + struct usb_bus *bus; // our bus + + __u32 *framelist; + uhci_desc_t **iso_td; + uhci_desc_t *int_chain[8]; + uhci_desc_t *ls_control_chain; + uhci_desc_t *control_chain; + uhci_desc_t *bulk_chain; + uhci_desc_t *chain_end; + uhci_desc_t *td1ms; + uhci_desc_t *td32ms; + struct list_head free_desc; + spinlock_t qh_lock; + spinlock_t td_lock; + struct virt_root_hub rh; //private data of the virtual root hub + int loop_usage; // URBs using bandwidth reclamation + + struct list_head urb_unlinked; // list of all unlinked urbs + long timeout_check; + int timeout_urbs; + struct pci_dev *uhci_pci; +} uhci_t, *puhci_t; + + +#define MAKE_TD_ADDR(a) (virt_to_bus(a)&~UHCI_PTR_QH) +#define MAKE_QH_ADDR(a) (virt_to_bus(a)|UHCI_PTR_QH) +#define UHCI_GET_CURRENT_FRAME(uhci) (inw ((uhci)->io_addr + USBFRNUM)) + +/* ------------------------------------------------------------------------------------ + Virtual Root HUB + ------------------------------------------------------------------------------------ */ +/* destination of request */ +#define RH_INTERFACE 0x01 +#define RH_ENDPOINT 0x02 +#define RH_OTHER 0x03 + +#define RH_CLASS 0x20 +#define RH_VENDOR 0x40 + +/* Requests: bRequest << 8 | bmRequestType */ +#define RH_GET_STATUS 0x0080 +#define RH_CLEAR_FEATURE 0x0100 +#define RH_SET_FEATURE 0x0300 +#define RH_SET_ADDRESS 0x0500 +#define RH_GET_DESCRIPTOR 0x0680 +#define RH_SET_DESCRIPTOR 0x0700 +#define RH_GET_CONFIGURATION 0x0880 +#define RH_SET_CONFIGURATION 0x0900 +#define RH_GET_STATE 0x0280 +#define RH_GET_INTERFACE 0x0A80 +#define RH_SET_INTERFACE 0x0B00 +#define RH_SYNC_FRAME 0x0C80 +/* Our Vendor Specific Request */ +#define RH_SET_EP 0x2000 + + +/* Hub port features */ +#define RH_PORT_CONNECTION 0x00 +#define RH_PORT_ENABLE 0x01 +#define RH_PORT_SUSPEND 0x02 +#define RH_PORT_OVER_CURRENT 0x03 +#define RH_PORT_RESET 0x04 +#define RH_PORT_POWER 0x08 +#define RH_PORT_LOW_SPEED 0x09 +#define RH_C_PORT_CONNECTION 0x10 +#define RH_C_PORT_ENABLE 0x11 +#define RH_C_PORT_SUSPEND 0x12 +#define RH_C_PORT_OVER_CURRENT 0x13 +#define RH_C_PORT_RESET 0x14 + +/* Hub features */ +#define RH_C_HUB_LOCAL_POWER 0x00 +#define RH_C_HUB_OVER_CURRENT 0x01 + +#define RH_DEVICE_REMOTE_WAKEUP 0x00 +#define RH_ENDPOINT_STALL 0x01 + +/* Our Vendor Specific feature */ +#define RH_REMOVE_EP 0x00 + + +#define RH_ACK 0x01 +#define RH_REQ_ERR -1 +#define RH_NACK 0x00 + +#define min(a,b) (((a)<(b))?(a):(b)) + +#endif diff --git a/drivers/usb/usb.c b/drivers/usb/usb.c index f5e89ea8986c..f922354764f6 100644 --- a/drivers/usb/usb.c +++ b/drivers/usb/usb.c @@ -1,7 +1,12 @@ /* - * driver/usb/usb.c + * drivers/usb/usb.c * * (C) Copyright Linus Torvalds 1999 + * (C) Copyright Johannes Erdfelt 1999 + * (C) Copyright Andreas Gal 1999 + * (C) Copyright Gregory P. Smith 1999 + * (C) Copyright Deti Fliegl 1999 (new USB architecture) + * (C) Copyright Randy Dunlap 2000 * * NOTE! This is not actually a driver at all, rather this is * just a collection of helper routines that implement the @@ -10,318 +15,1076 @@ * Think of this as a "USB library" rather than anything else. * It should be considered a slave, with no callbacks. Callbacks * are evil. - */ - -/* - * Table 9-2 * - * Offset Field Size Value Desc - * 0 bmRequestType 1 Bitmap D7: Direction - * 0 = Host-to-device - * 1 = Device-to-host - * D6..5: Type - * 0 = Standard - * 1 = Class - * 2 = Vendor - * 3 = Reserved - * D4..0: Recipient - * 0 = Device - * 1 = Interface - * 2 = Endpoint - * 3 = Other - * 4..31 = Reserved - * 1 bRequest 1 Value Specific request (9-3) - * 2 wValue 2 Value Varies - * 4 wIndex 2 Index/Offset Varies - * 6 wLength 2 Count Bytes for data + * $Id: usb.c,v 1.53 2000/01/14 16:19:09 acher Exp $ */ +#include +#include #include #include #include +#include /* for in_interrupt() */ +#ifdef CONFIG_USB_DEBUG + #define DEBUG +#else + #undef DEBUG +#endif +#include + +static const int usb_bandwidth_option = +#ifdef CONFIG_USB_BANDWIDTH + 1; +#else + 0; +#endif -#include "usb.h" +/* + * Prototypes for the device driver probing/loading functions + */ +static void usb_find_drivers(struct usb_device *); +static int usb_find_interface_driver(struct usb_device *, unsigned int); +static void usb_check_support(struct usb_device *); /* * We have a per-interface "registered driver" list. */ -static LIST_HEAD(usb_driver_list); +LIST_HEAD(usb_driver_list); +LIST_HEAD(usb_bus_list); + +static struct usb_busmap busmap; + +static struct usb_driver *usb_minors[16]; int usb_register(struct usb_driver *new_driver) { + struct list_head *tmp; + + if (new_driver->fops != NULL) { + if (usb_minors[new_driver->minor/16]) { + err("error registering %s driver", new_driver->name); + return -EINVAL; + } + usb_minors[new_driver->minor/16] = new_driver; + } + + info("registered new driver %s", new_driver->name); + + init_MUTEX(&new_driver->serialize); + /* Add it to the list of known drivers */ list_add(&new_driver->driver_list, &usb_driver_list); /* - * We should go through all existing devices, and see if any of - * them would be acceptable to the new driver.. Let's do that - * in version 2.0. + * We go through all existing devices, and see if any of them would + * be acceptable to the new driver.. This is done using a depth-first + * search for devices without a registered driver already, then + * running 'probe' with each of the drivers registered on every one + * of these. */ + tmp = usb_bus_list.next; + while (tmp != &usb_bus_list) { + struct usb_bus *bus = list_entry(tmp,struct usb_bus, bus_list); + + tmp = tmp->next; + usb_check_support(bus->root_hub); + } return 0; } +/* + * This function is part of a depth-first search down the device tree, + * removing any instances of a device driver. + */ +static void usb_drivers_purge(struct usb_driver *driver,struct usb_device *dev) +{ + int i; + + if (!dev) { + err("null device being purged!!!"); + return; + } + + for (i=0; ichildren[i]) + usb_drivers_purge(driver, dev->children[i]); + + if (!dev->actconfig) + return; + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *interface = &dev->actconfig->interface[i]; + + if (interface->driver == driver) { + down(&driver->serialize); + driver->disconnect(dev, interface->private_data); + up(&driver->serialize); + usb_driver_release_interface(driver, interface); + /* + * This will go through the list looking for another + * driver that can handle the device + */ + usb_find_interface_driver(dev, i); + } + } +} + +/* + * Unlink a driver from the driver list when it is unloaded + */ void usb_deregister(struct usb_driver *driver) { + struct list_head *tmp; + + info("deregistering driver %s", driver->name); + if (driver->fops != NULL) + usb_minors[driver->minor/16] = NULL; + + /* + * first we remove the driver, to be sure it doesn't get used by + * another thread while we are stepping through removing entries + */ list_del(&driver->driver_list); + + tmp = usb_bus_list.next; + while (tmp != &usb_bus_list) { + struct usb_bus *bus = list_entry(tmp,struct usb_bus,bus_list); + + tmp = tmp->next; + usb_drivers_purge(driver, bus->root_hub); + } +} + +struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum) +{ + int i; + + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) + if (dev->actconfig->interface[i].altsetting[0].bInterfaceNumber == ifnum) + return &dev->actconfig->interface[i]; + + return NULL; +} + +/* + * usb_calc_bus_time: + * + * returns (approximate) USB bus time in nanoseconds for a USB transaction. + */ +static long usb_calc_bus_time (int low_speed, int input_dir, int isoc, int bytecount) +{ + unsigned long tmp; + + if (low_speed) /* no isoc. here */ + { + if (input_dir) + { + tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + else + { + tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp); + } + } + + /* for full-speed: */ + + if (!isoc) /* Input or Output */ + { + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (9107L + BW_HOST_DELAY + tmp); + } /* end not Isoc */ + + /* for isoc: */ + + tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L; + return (((input_dir) ? 7268L : 6265L) + BW_HOST_DELAY + tmp); +} + +/* + * usb_check_bandwidth(): + * + * old_alloc is from host_controller->bandwidth_allocated in microseconds; + * bustime is from calc_bus_time(), but converted to microseconds. + * + * returns if successful, + * or USB_ST_BANDWIDTH_ERROR if bandwidth request fails. + * + * FIXME: + * This initial implementation does not use Endpoint.bInterval + * in managing bandwidth allocation. + * It probably needs to be expanded to use Endpoint.bInterval. + * This can be done as a later enhancement (correction). + * This will also probably require some kind of + * frame allocation tracking...meaning, for example, + * that if multiple drivers request interrupts every 10 USB frames, + * they don't all have to be allocated at + * frame numbers N, N+10, N+20, etc. Some of them could be at + * N+11, N+21, N+31, etc., and others at + * N+12, N+22, N+32, etc. + * However, this first cut at USB bandwidth allocation does not + * contain any frame allocation tracking. + */ +int usb_check_bandwidth (struct usb_device *dev, struct urb *urb) +{ + int new_alloc; + int old_alloc = dev->bus->bandwidth_allocated; + unsigned int pipe = urb->pipe; + long bustime; + + bustime = usb_calc_bus_time (usb_pipeslow(pipe), usb_pipein(pipe), + usb_pipeisoc(pipe), usb_maxpacket(dev, pipe, usb_pipeout(pipe))); + if (usb_pipeisoc(pipe)) + bustime = NS_TO_US(bustime) / urb->number_of_packets; + else + bustime = NS_TO_US(bustime); + + new_alloc = old_alloc + (int)bustime; + /* what new total allocated bus time would be */ + + if (new_alloc > FRAME_TIME_MAX_USECS_ALLOC) + dbg("usb-check-bandwidth %sFAILED: was %u, would be %u, bustime = %ld us", + usb_bandwidth_option ? "" : "would have ", + old_alloc, new_alloc, bustime); + + if (!usb_bandwidth_option) /* don't enforce it */ + return (bustime); + return (new_alloc <= FRAME_TIME_MAX_USECS_ALLOC) ? bustime : USB_ST_BANDWIDTH_ERROR; +} + +void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc) +{ + dev->bus->bandwidth_allocated += bustime; + if (isoc) + dev->bus->bandwidth_isoc_reqs++; + else + dev->bus->bandwidth_int_reqs++; + urb->bandwidth = bustime; + + dbg("bw_alloc increased by %d to %d for %d requesters", + bustime, + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); +} + +/* + * usb_release_bandwidth(): + * + * called to release a pipe's bandwidth (in microseconds) + */ +void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc) +{ + dev->bus->bandwidth_allocated -= urb->bandwidth; + if (isoc) + dev->bus->bandwidth_isoc_reqs--; + else + dev->bus->bandwidth_int_reqs--; + + dbg("bw_alloc reduced by %d to %d for %d requesters", + urb->bandwidth, + dev->bus->bandwidth_allocated, + dev->bus->bandwidth_int_reqs + dev->bus->bandwidth_isoc_reqs); + urb->bandwidth = 0; +} + +/* + * New functions for (de)registering a controller + */ +struct usb_bus *usb_alloc_bus(struct usb_operations *op) +{ + struct usb_bus *bus; + + bus = kmalloc(sizeof(*bus), GFP_KERNEL); + if (!bus) + return NULL; + + memset(&bus->devmap, 0, sizeof(struct usb_devmap)); + + bus->op = op; + bus->root_hub = NULL; + bus->hcpriv = NULL; + bus->busnum = -1; + bus->bandwidth_allocated = 0; + bus->bandwidth_int_reqs = 0; + bus->bandwidth_isoc_reqs = 0; + + INIT_LIST_HEAD(&bus->bus_list); + INIT_LIST_HEAD(&bus->inodes); + + return bus; +} + +void usb_free_bus(struct usb_bus *bus) +{ + if (!bus) + return; + + kfree(bus); +} + +void usb_register_bus(struct usb_bus *bus) +{ + int busnum; + + busnum = find_next_zero_bit(busmap.busmap, USB_MAXBUS, 1); + if (busnum < USB_MAXBUS) { + set_bit(busnum, busmap.busmap); + bus->busnum = busnum; + } else + warn("too many buses"); + + /* Add it to the list of buses */ + list_add(&bus->bus_list, &usb_bus_list); + + usbdevfs_add_bus(bus); + + info("new USB bus registered, assigned bus number %d", bus->busnum); +} + +void usb_deregister_bus(struct usb_bus *bus) +{ + info("USB bus %d deregistered", bus->busnum); + + /* + * NOTE: make sure that all the devices are removed by the + * controller code, as well as having it call this when cleaning + * itself up + */ + list_del(&bus->bus_list); + + usbdevfs_remove_bus(bus); + + clear_bit(bus->busnum, busmap.busmap); +} + +/* + * This function is for doing a depth-first search for devices which + * have support, for dynamic loading of driver modules. + */ +static void usb_check_support(struct usb_device *dev) +{ + int i; + + if (!dev) { + err("null device being checked!!!"); + return; + } + + for (i=0; ichildren[i]) + usb_check_support(dev->children[i]); + + if (!dev->actconfig) + return; + + /* now we check this device */ + if (dev->devnum > 0) + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) + usb_find_interface_driver(dev, i); +} + + +/* + * This is intended to be used by usb device drivers that need to + * claim more than one interface on a device at once when probing + * (audio and acm are good examples). No device driver should have + * to mess with the internal usb_interface or usb_device structure + * members. + */ +void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv) +{ + if (!iface || !driver) + return; + + dbg("%s driver claimed interface %p", driver->name, iface); + + iface->driver = driver; + iface->private_data = priv; +} /* usb_driver_claim_interface() */ + +/* + * This should be used by drivers to check other interfaces to see if + * they are available or not. + */ +int usb_interface_claimed(struct usb_interface *iface) +{ + if (!iface) + return 0; + + return (iface->driver != NULL); +} /* usb_interface_claimed() */ + +/* + * This should be used by drivers to release their claimed interfaces + */ +void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface) +{ + /* this should never happen, don't release something that's not ours */ + if (iface->driver != driver || !iface) + return; + + iface->driver = NULL; + iface->private_data = NULL; } /* * This entrypoint gets called for each new device. * * We now walk the list of registered USB drivers, - * looking for one that will accept this device as - * his.. + * looking for one that will accept this interface. + * + * The probe return value is changed to be a private pointer. This way + * the drivers don't have to dig around in our structures to set the + * private pointer if they only need one interface. + * + * Returns: 0 if a driver accepted the interface, -1 otherwise */ -void usb_device_descriptor(struct usb_device *dev) +static int usb_find_interface_driver(struct usb_device *dev, unsigned ifnum) { struct list_head *tmp = usb_driver_list.next; + struct usb_interface *interface; + + if ((!dev) || (ifnum >= dev->actconfig->bNumInterfaces)) { + err("bad find_interface_driver params"); + return -1; + } + + interface = dev->actconfig->interface + ifnum; + + if (usb_interface_claimed(interface)) + return -1; while (tmp != &usb_driver_list) { - struct usb_driver *driver = list_entry(tmp, struct usb_driver, driver_list); + void *private; + struct usb_driver *driver = list_entry(tmp, struct usb_driver, + driver_list); + tmp = tmp->next; - if (driver->probe(dev)) + down(&driver->serialize); + private = driver->probe(dev, ifnum); + up(&driver->serialize); + if (!private) continue; - dev->driver = driver; - return; + usb_driver_claim_interface(driver, interface, private); + + return 0; } + + return -1; +} - /* - * Ok, no driver accepted the device, so show the info - * for debugging.. - */ - printk("Unknown new USB device:\n"); - usb_show_device(dev); +/* + * This entrypoint gets called for each new device. + * + * All interfaces are scanned for matching drivers. + */ +static void usb_find_drivers(struct usb_device *dev) +{ + unsigned ifnum; + unsigned rejected = 0; + unsigned claimed = 0; + + for (ifnum = 0; ifnum < dev->actconfig->bNumInterfaces; ifnum++) { + /* if this interface hasn't already been claimed */ + if (!usb_interface_claimed(dev->actconfig->interface + ifnum)) { + if (usb_find_interface_driver(dev, ifnum)) + rejected++; + else + claimed++; + } + } + + if (rejected) + dbg("unhandled interfaces on device"); + + if (!claimed) { + warn("This device is not recognized by any installed USB driver."); +#ifdef DEBUG + usb_show_device(dev); +#endif + } } /* - * Parse the fairly incomprehensible output of - * the USB configuration data, and build up the - * USB device database. + * Only HC's should call usb_alloc_dev and usb_free_dev directly + * Anybody may use usb_inc_dev_use or usb_dec_dev_use */ -static int usb_expect_descriptor(unsigned char *ptr, int len, unsigned char desctype, unsigned char descindex) +struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus) { - int parsed = 0; - int n_len; - unsigned short n_desc; + struct usb_device *dev; - for (;;) { - int i; + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) + return NULL; - if (len < descindex) - return -1; - n_desc = *(unsigned short *)ptr; - n_len = n_desc & 0xff; + memset(dev, 0, sizeof(*dev)); - if (n_desc == ((desctype << 8) + descindex)) - break; + dev->bus = bus; + dev->parent = parent; + atomic_set(&dev->refcnt, 1); + INIT_LIST_HEAD(&dev->inodes); + INIT_LIST_HEAD(&dev->filelist); - if (((n_desc >> 8)&0xFF) == desctype && - n_len > descindex) - { - printk("bug: oversized descriptor.\n"); - break; - } - - if (n_len < 2 || n_len > len) - { - printk("Short descriptor.\n"); - return -1; - } - printk( - "Expected descriptor %02X/%02X, got %02X/%02X - skipping\n", - desctype, descindex, - (n_desc >> 8) & 0xFF, n_desc & 0xFF); - for (i = 0 ; i < n_len; i++) - printk(" %d %02x\n", i, ptr[i]); - len -= n_len; - ptr += n_len; - parsed += n_len; + dev->bus->op->allocate(dev); + + return dev; +} + +void usb_free_dev(struct usb_device *dev) +{ + if (atomic_dec_and_test(&dev->refcnt)) { + usb_destroy_configuration(dev); + dev->bus->op->deallocate(dev); + kfree(dev); } +} + +void usb_inc_dev_use(struct usb_device *dev) +{ + atomic_inc(&dev->refcnt); +} +/* ------------------------------------------------------------------------------------- + * New USB Core Functions + * -------------------------------------------------------------------------------------*/ + +urb_t *usb_alloc_urb(int iso_packets) +{ + urb_t *urb; + + urb = (urb_t *)kmalloc(sizeof(urb_t) + iso_packets * sizeof(iso_packet_descriptor_t), + in_interrupt() ? GFP_ATOMIC : GFP_KERNEL); + if (!urb) { + err("alloc_urb: kmalloc failed"); + return NULL; + } + + memset(urb, 0, sizeof(*urb)); + + spin_lock_init(&urb->lock); + + return urb; +} + +/*-------------------------------------------------------------------*/ +void usb_free_urb(urb_t* urb) +{ + if (urb) + kfree(urb); +} +/*-------------------------------------------------------------------*/ +int usb_submit_urb(urb_t *urb) +{ + if (urb && urb->dev) + return urb->dev->bus->op->submit_urb(urb); + else + return -1; +} + +/*-------------------------------------------------------------------*/ +int usb_unlink_urb(urb_t *urb) +{ + if (urb && urb->dev) + return urb->dev->bus->op->unlink_urb(urb); + else + return -1; +} +/*-------------------------------------------------------------------* + * COMPLETION HANDLERS * + *-------------------------------------------------------------------*/ + +/*-------------------------------------------------------------------* + * completion handler for compatibility wrappers (sync control/bulk) * + *-------------------------------------------------------------------*/ +static void usb_api_blocking_completion(urb_t *urb) +{ + api_wrapper_data *awd = (api_wrapper_data *)urb->context; + + if (waitqueue_active(awd->wakeup)) + wake_up(awd->wakeup); +#if 0 + else + dbg("(blocking_completion): waitqueue empty!"); + // even occurs if urb was unlinked by timeout... +#endif +} + +/*-------------------------------------------------------------------* + * COMPATIBILITY STUFF * + *-------------------------------------------------------------------*/ + +// Starts urb and waits for completion or timeout +static int usb_start_wait_urb(urb_t *urb, int timeout, int* actual_length) +{ + DECLARE_WAITQUEUE(wait, current); + DECLARE_WAIT_QUEUE_HEAD(wqh); + api_wrapper_data awd; + int status; + + awd.wakeup = &wqh; + awd.handler = 0; + init_waitqueue_head(&wqh); + current->state = TASK_INTERRUPTIBLE; + add_wait_queue(&wqh, &wait); + urb->context = &awd; + status = usb_submit_urb(urb); + if (status) { + // something went wrong + usb_free_urb(urb); + remove_wait_queue(&wqh, &wait); + return status; + } + + if (urb->status == -EINPROGRESS) { + while (timeout && urb->status == -EINPROGRESS) + status = timeout = schedule_timeout(timeout); + } else + status = 1; + + remove_wait_queue(&wqh, &wait); + + if (!status) { + // timeout + printk("usb_control/bulk_msg: timeout\n"); + usb_unlink_urb(urb); // remove urb safely + status = -ETIMEDOUT; + } else + status = urb->status; + + if (actual_length) + *actual_length = urb->actual_length; + + usb_free_urb(urb); + return status; +} + +/*-------------------------------------------------------------------*/ +// returns status (negative) or length (positive) +int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, + devrequest *cmd, void *data, int len, int timeout) +{ + urb_t *urb; + int retv; + int length; + + urb = usb_alloc_urb(0); + if (!urb) + return -ENOMEM; + + FILL_CONTROL_URB(urb, usb_dev, pipe, (unsigned char*)cmd, data, len, /* build urb */ + (usb_complete_t)usb_api_blocking_completion,0); + + retv = usb_start_wait_urb(urb, timeout, &length); + if (retv < 0) + return retv; + else + return length; - printk("Found %02X:%02X\n", - desctype, descindex); - return parsed; +} + +/*-------------------------------------------------------------------*/ +int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, + __u16 value, __u16 index, void *data, __u16 size, int timeout) +{ + devrequest *dr = kmalloc(sizeof(devrequest), GFP_KERNEL); + int ret; + + if (!dr) + return -ENOMEM; + + dr->requesttype = requesttype; + dr->request = request; + dr->value = cpu_to_le16p(&value); + dr->index = cpu_to_le16p(&index); + dr->length = cpu_to_le16p(&size); + + //dbg("usb_control_msg"); + + ret = usb_internal_control_msg(dev, pipe, dr, data, size, timeout); + + kfree(dr); + + return ret; +} + +/*-------------------------------------------------------------------*/ +/* compatibility wrapper, builds bulk urb, and waits for completion */ +/* synchronous behavior */ + +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, + void *data, int len, int *actual_length, int timeout) +{ + urb_t *urb; + + if (len < 0) + return -EINVAL; + + urb=usb_alloc_urb(0); + if (!urb) + return -ENOMEM; + + FILL_BULK_URB(urb,usb_dev,pipe,(unsigned char*)data,len, /* build urb */ + (usb_complete_t)usb_api_blocking_completion,0); + + return usb_start_wait_urb(urb,timeout,actual_length); } /* - * Parse the even more incomprehensible mess made of the USB spec - * by USB audio having private magic to go with it. + * usb_get_current_frame_number() + * + * returns the current frame number for the parent USB bus/controller + * of the given USB device. */ - -static int usb_check_descriptor(unsigned char *ptr, int len, unsigned char desctype) +int usb_get_current_frame_number(struct usb_device *usb_dev) { - int n_len = ptr[0]; + return usb_dev->bus->op->get_frame_number (usb_dev); +} +/*-------------------------------------------------------------------*/ + +static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *buffer, int size) +{ + struct usb_descriptor_header *header; + unsigned char *begin; + int parsed = 0, len, numskipped; + + header = (struct usb_descriptor_header *)buffer; + + /* Everything should be fine being passed into here, but we sanity */ + /* check JIC */ + if (header->bLength > size) { + err("ran out of descriptors parsing"); + return -1; + } + + if (header->bDescriptorType != USB_DT_ENDPOINT) { + warn("unexpected descriptor 0x%X, expecting endpoint descriptor, type 0x%X", + endpoint->bDescriptorType, USB_DT_ENDPOINT); + return parsed; + } + + if (header->bLength == USB_DT_ENDPOINT_AUDIO_SIZE) + memcpy(endpoint, buffer, USB_DT_ENDPOINT_AUDIO_SIZE); + else + memcpy(endpoint, buffer, USB_DT_ENDPOINT_SIZE); + + le16_to_cpus(&endpoint->wMaxPacketSize); + + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; + + /* Skip over the rest of the Class Specific or Vendor Specific */ + /* descriptors */ + begin = buffer; + numskipped = 0; + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if (header->bLength < 2) { + err("invalid descriptor length of %d", header->bLength); + return -1; + } + + /* If we find another descriptor which is at or below us */ + /* in the descriptor heirarchy then we're done */ + if ((header->bDescriptorType == USB_DT_ENDPOINT) || + (header->bDescriptorType == USB_DT_INTERFACE) || + (header->bDescriptorType == USB_DT_CONFIG) || + (header->bDescriptorType == USB_DT_DEVICE)) + break; + + dbg("skipping descriptor 0x%X", + header->bDescriptorType); + numskipped++; + + buffer += header->bLength; + size -= header->bLength; + parsed += header->bLength; + } + if (numskipped) + dbg("skipped %d class/vendor specific endpoint descriptors", numskipped); + + /* Copy any unknown descriptors into a storage area for drivers */ + /* to later parse */ + len = (int)(buffer - begin); + if (!len) { + endpoint->extra = NULL; + endpoint->extralen = 0; + return parsed; + } + + endpoint->extra = kmalloc(len, GFP_KERNEL); + + if (!endpoint->extra) { + err("couldn't allocate memory for endpoint extra descriptors"); + endpoint->extralen = 0; + return parsed; + } + + memcpy(endpoint->extra, begin, len); + endpoint->extralen = len; + + return parsed; +} + +static int usb_parse_interface(struct usb_device *dev, struct usb_interface *interface, unsigned char *buffer, int size) +{ + int i, len, numskipped, retval, parsed = 0; + struct usb_descriptor_header *header; + struct usb_interface_descriptor *ifp; + unsigned char *begin; + + interface->act_altsetting = 0; + interface->num_altsetting = 0; + interface->max_altsetting = USB_ALTSETTINGALLOC; + + interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL); + + if (!interface->altsetting) { + err("couldn't kmalloc interface->altsetting"); + return -1; + } + + while (size > 0) { + if (interface->num_altsetting >= interface->max_altsetting) { + void *ptr; + int oldmas; + + oldmas = interface->max_altsetting; + interface->max_altsetting += USB_ALTSETTINGALLOC; + if (interface->max_altsetting > USB_MAXALTSETTING) { + warn("too many alternate settings (max %d)", + USB_MAXALTSETTING); + return -1; + } + + ptr = interface->altsetting; + interface->altsetting = kmalloc(sizeof(struct usb_interface_descriptor) * interface->max_altsetting, GFP_KERNEL); + if (!interface->altsetting) { + err("couldn't kmalloc interface->altsetting"); + interface->altsetting = ptr; + return -1; + } + memcpy(interface->altsetting, ptr, sizeof(struct usb_interface_descriptor) * oldmas); + + kfree(ptr); + } + + ifp = interface->altsetting + interface->num_altsetting; + interface->num_altsetting++; + + memcpy(ifp, buffer, USB_DT_INTERFACE_SIZE); - if (n_len < 2 || n_len > len) - { - printk("Short descriptor.\n"); - return -1; - } + /* Skip over the interface */ + buffer += ifp->bLength; + parsed += ifp->bLength; + size -= ifp->bLength; - if (ptr[1] == desctype) - return 0; - - return -1; -} + begin = buffer; + numskipped = 0; + /* Skip over any interface, class or vendor descriptors */ + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; -static int usb_parse_endpoint(struct usb_device *dev, struct usb_endpoint_descriptor *endpoint, unsigned char *ptr, int len) -{ - int parsed = usb_expect_descriptor(ptr, len, USB_DT_ENDPOINT, 7); - int i; + if (header->bLength < 2) { + err("invalid descriptor length of %d", header->bLength); + return -1; + } - if (parsed < 0) - return parsed; - memcpy(endpoint, ptr + parsed, ptr[parsed]); + /* If we find another descriptor which is at or below */ + /* us in the descriptor heirarchy then return */ + if ((header->bDescriptorType == USB_DT_INTERFACE) || + (header->bDescriptorType == USB_DT_ENDPOINT) || + (header->bDescriptorType == USB_DT_CONFIG) || + (header->bDescriptorType == USB_DT_DEVICE)) + break; - parsed += ptr[parsed]; - len -= ptr[parsed]; + numskipped++; - while((i = usb_check_descriptor(ptr+parsed, len, 0x25))>=0) - { - usb_audio_endpoint(endpoint, ptr+parsed+i); - len -= ptr[parsed+i]; - parsed += ptr[parsed+i]; - } - - return parsed;// + ptr[parsed]; -} + buffer += header->bLength; + parsed += header->bLength; + size -= header->bLength; + } -static int usb_parse_interface(struct usb_device *dev, struct usb_interface_descriptor *interface, unsigned char *ptr, int len) -{ - int i; - int parsed = usb_expect_descriptor(ptr, len, USB_DT_INTERFACE, 9); - int retval; + if (numskipped) + dbg("skipped %d class/vendor specific interface descriptors", numskipped); + + /* Copy any unknown descriptors into a storage area for */ + /* drivers to later parse */ + len = (int)(buffer - begin); + if (!len) { + ifp->extra = NULL; + ifp->extralen = 0; + } else { + ifp->extra = kmalloc(len, GFP_KERNEL); + + if (!ifp->extra) { + err("couldn't allocate memory for interface extra descriptors"); + ifp->extralen = 0; + return -1; + } + memcpy(ifp->extra, begin, len); + ifp->extralen = len; + } - if (parsed < 0) - return parsed; + /* Did we hit an unexpected descriptor? */ + header = (struct usb_descriptor_header *)buffer; + if ((size >= sizeof(struct usb_descriptor_header)) && + ((header->bDescriptorType == USB_DT_CONFIG) || + (header->bDescriptorType == USB_DT_DEVICE))) + return parsed; - memcpy(interface, ptr + parsed, *ptr); - len -= ptr[parsed]; - parsed += ptr[parsed]; + if (ifp->bNumEndpoints > USB_MAXENDPOINTS) { + warn("too many endpoints"); + return -1; + } - while((i=usb_check_descriptor(ptr+parsed, len, 0x24))>=0) - { - usb_audio_interface(interface, ptr+parsed+i); - len -= ptr[parsed+i]; - parsed += ptr[parsed+i]; - } - - if (interface->bNumEndpoints > USB_MAXENDPOINTS) - { - printk(KERN_WARNING "usb: too many endpoints.\n"); - return -1; - } + ifp->endpoint = (struct usb_endpoint_descriptor *) + kmalloc(ifp->bNumEndpoints * + sizeof(struct usb_endpoint_descriptor), GFP_KERNEL); + if (!ifp->endpoint) { + err("out of memory"); + return -1; + } - interface->endpoint = (struct usb_endpoint_descriptor *) - kmalloc(interface->bNumEndpoints * sizeof(struct usb_endpoint_descriptor), GFP_KERNEL); - if(interface->endpoint==NULL) - { - printk(KERN_WARNING "usb: out of memory.\n"); - return -1; - } - memset(interface->endpoint, 0, interface->bNumEndpoints*sizeof(struct usb_endpoint_descriptor)); + memset(ifp->endpoint, 0, ifp->bNumEndpoints * + sizeof(struct usb_endpoint_descriptor)); - for (i = 0; i < interface->bNumEndpoints; i++) { -// if(((USB_DT_HID << 8) | 9) == *(unsigned short*)(ptr + parsed)) { -// parsed += 9; /* skip over the HID descriptor for now */ -// len -= 9; -// } - retval = usb_parse_endpoint(dev, interface->endpoint + i, ptr + parsed, len); - if (retval < 0) return retval; - parsed += retval; - len -= retval; + for (i = 0; i < ifp->bNumEndpoints; i++) { + header = (struct usb_descriptor_header *)buffer; + + if (header->bLength > size) { + err("ran out of descriptors parsing"); + return -1; + } + + retval = usb_parse_endpoint(dev, ifp->endpoint + i, buffer, size); + if (retval < 0) + return retval; + + buffer += retval; + parsed += retval; + size -= retval; + } + + /* We check to see if it's an alternate to this one */ + ifp = (struct usb_interface_descriptor *)buffer; + if (size < USB_DT_INTERFACE_SIZE || + ifp->bDescriptorType != USB_DT_INTERFACE || + !ifp->bAlternateSetting) + return parsed; } + return parsed; } -static int usb_parse_config(struct usb_device *dev, struct usb_config_descriptor *config, unsigned char *ptr, int len) +int usb_parse_configuration(struct usb_device *dev, struct usb_config_descriptor *config, char *buffer) { int i; - int parsed = usb_expect_descriptor(ptr, len, USB_DT_CONFIG, 9); - - if (parsed < 0) - return parsed; + int retval; + int size; + struct usb_descriptor_header *header; - memcpy(config, ptr + parsed, *ptr); - len -= *ptr; - parsed += *ptr; + memcpy(config, buffer, USB_DT_CONFIG_SIZE); + le16_to_cpus(&config->wTotalLength); + size = config->wTotalLength; - if (config->bNumInterfaces > USB_MAXINTERFACES) - { - printk(KERN_WARNING "usb: too many interfaces.\n"); + if (config->bNumInterfaces > USB_MAXINTERFACES) { + warn("too many interfaces"); return -1; - } - config->interface = (struct usb_interface_descriptor *) - kmalloc(config->bNumInterfaces * sizeof(struct usb_interface_descriptor), GFP_KERNEL); - if(config->interface==NULL) - { - printk(KERN_WARNING "usb: out of memory.\n"); + config->interface = (struct usb_interface *) + kmalloc(config->bNumInterfaces * + sizeof(struct usb_interface), GFP_KERNEL); + dbg("kmalloc IF %p, numif %i",config->interface,config->bNumInterfaces); + if (!config->interface) { + err("out of memory"); return -1; } - memset(config->interface, 0, config->bNumInterfaces*sizeof(struct usb_interface_descriptor)); + + memset(config->interface, 0, + config->bNumInterfaces * sizeof(struct usb_interface)); + + buffer += config->bLength; + size -= config->bLength; for (i = 0; i < config->bNumInterfaces; i++) { - int retval = usb_parse_interface(dev, config->interface + i, ptr + parsed, len); - if (retval < 0) - return parsed; // HACK -// return retval; - parsed += retval; - len -= retval; - } - return parsed; -} - -int usb_parse_configuration(struct usb_device *dev, void *__buf, int bytes) -{ - int i; - unsigned char *ptr = __buf; + header = (struct usb_descriptor_header *)buffer; + if ((header->bLength > size) || (header->bLength <= 2)) { + err("ran out of descriptors parsing"); + return -1; + } + + if (header->bDescriptorType != USB_DT_INTERFACE) { + warn("unexpected descriptor 0x%X", + header->bDescriptorType); - if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) - { - printk(KERN_WARNING "usb: too many configurations.\n"); - return -1; - } + buffer += header->bLength; + size -= header->bLength; + continue; + } - dev->config = (struct usb_config_descriptor *) - kmalloc(dev->descriptor.bNumConfigurations * sizeof(struct usb_config_descriptor), GFP_KERNEL); - if(dev->config==NULL) - { - printk(KERN_WARNING "usb: out of memory.\n"); - return -1; - } - memset(dev->config, 0, dev->descriptor.bNumConfigurations*sizeof(struct usb_config_descriptor)); - for (i = 0; i < dev->descriptor.bNumConfigurations; i++) { - int retval = usb_parse_config(dev, dev->config + i, ptr, bytes); + retval = usb_parse_interface(dev, config->interface + i, buffer, size); if (retval < 0) return retval; - ptr += retval; - bytes += retval; + + buffer += retval; + size -= retval; } - return 0; + + return size; } void usb_destroy_configuration(struct usb_device *dev) { - int c, i; - struct usb_config_descriptor *cf; - struct usb_interface_descriptor *ifp; + int c, i, j, k; - if(dev->config==NULL) + if (!dev->config) return; - for(c=0;cdescriptor.bNumConfigurations;c++) - { - cf=&dev->config[c]; - if(cf->interface==NULL) + + if (dev->rawdescriptors) { + for (i = 0; i < dev->descriptor.bNumConfigurations; i++) + kfree(dev->rawdescriptors[i]); + + kfree(dev->rawdescriptors); + } + + for (c = 0; c < dev->descriptor.bNumConfigurations; c++) { + struct usb_config_descriptor *cf = &dev->config[c]; + + if (!cf->interface) break; - for(i=0;ibNumInterfaces;i++) - { - ifp=&cf->interface[i]; - if(ifp->endpoint==NULL) + + for (i = 0; i < cf->bNumInterfaces; i++) { + struct usb_interface *ifp = + &cf->interface[i]; + + if (!ifp->altsetting) break; - kfree(ifp->endpoint); + + for (j = 0; j < ifp->num_altsetting; j++) { + struct usb_interface_descriptor *as = + &ifp->altsetting[j]; + + if(as->extra) { + kfree(as->extra); + } + + if (!as->endpoint) + break; + + for(k = 0; k < as->bNumEndpoints; k++) { + if(as->endpoint[k].extra) { + kfree(as->endpoint[k].extra); + } + } + kfree(as->endpoint); + } + + kfree(ifp->altsetting); } kfree(cf->interface); } @@ -332,6 +1095,83 @@ void usb_init_root_hub(struct usb_device *dev) { dev->devnum = -1; dev->slow = 0; + dev->actconfig = NULL; +} + +/* for returning string descriptors in UTF-16LE */ +static int ascii2utf (char *ascii, __u8 *utf, int utfmax) +{ + int retval; + + for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) { + *utf++ = *ascii++ & 0x7f; + *utf++ = 0; + } + return retval; +} + +/* + * root_hub_string is used by each host controller's root hub code, + * so that they're identified consistently throughout the system. + */ +int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len) +{ + char buf [30]; + + // assert (len > (2 * (sizeof (buf) + 1))); + // assert (strlen (type) <= 8); + + // language ids + if (id == 0) { + *data++ = 4; *data++ = 3; /* 4 bytes data */ + *data++ = 0; *data++ = 0; /* some language id */ + return 4; + + // serial number + } else if (id == 1) { + sprintf (buf, "%x", serial); + + // product description + } else if (id == 2) { + sprintf (buf, "USB %s Root Hub", type); + + // id 3 == vendor description + + // unsupported IDs --> "stall" + } else + return 0; + + data [0] = 2 + ascii2utf (buf, data + 2, len - 2); + data [1] = 3; + return data [0]; +} + +/* + * __usb_get_extra_descriptor() finds a descriptor of specific type in the + * extra field of the interface and endpoint descriptor structs. + */ + +int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr) +{ + struct usb_descriptor_header *header; + + while (size >= sizeof(struct usb_descriptor_header)) { + header = (struct usb_descriptor_header *)buffer; + + if (header->bLength < 2) { + err("invalid descriptor length of %d", header->bLength); + return -1; + } + + if (header->bDescriptorType == type) { + *ptr = header; + return 0; + } + + buffer += header->bLength; + size -= header->bLength; + } + return -1; } /* @@ -340,29 +1180,44 @@ void usb_init_root_hub(struct usb_device *dev) void usb_disconnect(struct usb_device **pdev) { struct usb_device * dev = *pdev; + int i; - if (dev) { - int i; - - *pdev = NULL; + if (!dev) + return; - printk("USB disconnect on device %d\n", dev->devnum); + *pdev = NULL; - if(dev->driver) dev->driver->disconnect(dev); + info("USB disconnect on device %d", dev->devnum); - /* Free up all the children.. */ - for (i = 0; i < USB_MAXCHILDREN; i++) { - struct usb_device **child = dev->children + i; - usb_disconnect(child); + if (dev->actconfig) { + for (i = 0; i < dev->actconfig->bNumInterfaces; i++) { + struct usb_interface *interface = &dev->actconfig->interface[i]; + struct usb_driver *driver = interface->driver; + if (driver) { + down(&driver->serialize); + driver->disconnect(dev, interface->private_data); + up(&driver->serialize); + usb_driver_release_interface(driver, interface); + } } + } - /* Free up the device itself, including its device number */ - if (dev->devnum > 0) - clear_bit(dev->devnum, &dev->bus->devmap.devicemap); - dev->bus->op->deallocate(dev); + /* Free up all the children.. */ + for (i = 0; i < USB_MAXCHILDREN; i++) { + struct usb_device **child = dev->children + i; + if (*child) + usb_disconnect(child); } -} + /* remove /proc/bus/usb entry */ + usbdevfs_remove_device(dev); + + /* Free up the device itself, including its device number */ + if (dev->devnum > 0) + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + + usb_free_dev(dev); +} /* * Connect a new USB device. This basically just initializes @@ -373,9 +1228,11 @@ void usb_disconnect(struct usb_device **pdev) void usb_connect(struct usb_device *dev) { int devnum; - - dev->descriptor.bMaxPacketSize0 = 8; /* XXX fixed 8 bytes for now */ - + // FIXME needs locking for SMP!! + /* why? this is called only from the hub thread, + * which hopefully doesn't run on multiple CPU's simulatenously 8-) + */ + dev->descriptor.bMaxPacketSize0 = 8; /* Start off at 8 bytes */ devnum = find_next_zero_bit(dev->bus->devmap.devicemap, 128, 1); if (devnum < 128) { set_bit(devnum, dev->bus->devmap.devicemap); @@ -387,287 +1244,571 @@ void usb_connect(struct usb_device *dev) * These are the actual routines to send * and receive control messages. */ -int usb_set_address(struct usb_device *dev) -{ - devrequest dr; - dr.requesttype = 0; - dr.request = USB_REQ_SET_ADDRESS; - dr.value = dev->devnum; - dr.index = 0; - dr.length = 0; +#define GET_TIMEOUT 3 +#define SET_TIMEOUT 3 - return dev->bus->op->control_msg(dev, usb_snddefctrl(dev), &dr, NULL, 0); +int usb_set_address(struct usb_device *dev) +{ + return usb_control_msg(dev, usb_snddefctrl(dev), USB_REQ_SET_ADDRESS, + 0, dev->devnum, 0, NULL, 0, HZ * GET_TIMEOUT); } int usb_get_descriptor(struct usb_device *dev, unsigned char type, unsigned char index, void *buf, int size) { - devrequest dr; - - dr.requesttype = 0x80; - dr.request = USB_REQ_GET_DESCRIPTOR; - dr.value = (type << 8) + index; - dr.index = 0; - dr.length = size; + int i = 5; + int result; + + memset(buf,0,size); // Make sure we parse really received data - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, buf, size); + while (i--) { + if ((result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (type << 8) + index, 0, buf, size, HZ * GET_TIMEOUT)) >= 0 || + result == -EPIPE) + break; + } + return result; } -int usb_get_device_descriptor(struct usb_device *dev) +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, + unsigned char type, unsigned char id, void *buf, int size) { - return usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, sizeof(dev->descriptor)); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_RECIP_INTERFACE | USB_DIR_IN, + (type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT); } -int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size) +int usb_get_string(struct usb_device *dev, unsigned short langid, unsigned char index, void *buf, int size) { - devrequest dr; - - dr.requesttype = USB_RT_HUB | 0x80; - dr.request = USB_REQ_GET_DESCRIPTOR; - dr.value = (USB_DT_HUB << 8); - dr.index = 0; - dr.length = size; - - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, size); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, + (USB_DT_STRING << 8) + index, langid, buf, size, HZ * GET_TIMEOUT); } -int usb_clear_port_feature(struct usb_device *dev, int port, int feature) +int usb_get_device_descriptor(struct usb_device *dev) { - devrequest dr; - - dr.requesttype = USB_RT_PORT; - dr.request = USB_REQ_CLEAR_FEATURE; - dr.value = feature; - dr.index = port; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + int ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, + sizeof(dev->descriptor)); + if (ret >= 0) { + le16_to_cpus(&dev->descriptor.bcdUSB); + le16_to_cpus(&dev->descriptor.idVendor); + le16_to_cpus(&dev->descriptor.idProduct); + le16_to_cpus(&dev->descriptor.bcdDevice); + } + return ret; } -int usb_set_port_feature(struct usb_device *dev, int port, int feature) +int usb_get_status(struct usb_device *dev, int type, int target, void *data) { - devrequest dr; - - dr.requesttype = USB_RT_PORT; - dr.request = USB_REQ_SET_FEATURE; - dr.value = feature; - dr.index = port; - dr.length = 0; - - return dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev,0), &dr, NULL, 0); + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, data, 2, HZ * GET_TIMEOUT); } -int usb_get_hub_status(struct usb_device *dev, void *data) +int usb_get_protocol(struct usb_device *dev, int ifnum) { - devrequest dr; + unsigned char type; + int ret; - dr.requesttype = USB_RT_HUB | 0x80; - dr.request = USB_REQ_GET_STATUS; - dr.value = 0; - dr.index = 0; - dr.length = 4; + if ((ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_PROTOCOL, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + 0, ifnum, &type, 1, HZ * GET_TIMEOUT)) < 0) + return ret; - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4); + return type; } -int usb_get_port_status(struct usb_device *dev, int port, void *data) +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol) { - devrequest dr; + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_PROTOCOL, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + protocol, ifnum, NULL, 0, HZ * SET_TIMEOUT); +} - dr.requesttype = USB_RT_PORT | 0x80; - dr.request = USB_REQ_GET_STATUS; - dr.value = 0; - dr.index = port; - dr.length = 4; +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_IDLE, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (duration << 8) | report_id, ifnum, NULL, 0, HZ * SET_TIMEOUT); +} - return dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev,0), &dr, data, 4); +void usb_set_maxpacket(struct usb_device *dev) +{ + int i, b; + + for (i=0; iactconfig->bNumInterfaces; i++) { + struct usb_interface *ifp = dev->actconfig->interface + i; + struct usb_interface_descriptor *as = ifp->altsetting + ifp->act_altsetting; + struct usb_endpoint_descriptor *ep = as->endpoint; + int e; + + for (e=0; ebNumEndpoints; e++) { + b = ep[e].bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; + if ((ep[e].bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == + USB_ENDPOINT_XFER_CONTROL) { /* Control => bidirectional */ + dev->epmaxpacketout[b] = ep[e].wMaxPacketSize; + dev->epmaxpacketin [b] = ep[e].wMaxPacketSize; + } + else if (usb_endpoint_out(ep[e].bEndpointAddress)) { + if (ep[e].wMaxPacketSize > dev->epmaxpacketout[b]) + dev->epmaxpacketout[b] = ep[e].wMaxPacketSize; + } + else { + if (ep[e].wMaxPacketSize > dev->epmaxpacketin [b]) + dev->epmaxpacketin [b] = ep[e].wMaxPacketSize; + } + } + } } -int usb_get_protocol(struct usb_device *dev) +/* + * endp: endpoint number in bits 0-3; + * direction flag in bit 7 (1 = IN, 0 = OUT) + */ +int usb_clear_halt(struct usb_device *dev, int pipe) { - unsigned char buf[8]; - devrequest dr; + int result; + __u16 status; + int endp=usb_pipeendpoint(pipe)|(usb_pipein(pipe)<<7); + +/* + if (!usb_endpoint_halted(dev, endp & 0x0f, usb_endpoint_out(endp))) + return 0; +*/ - dr.requesttype = USB_RT_HIDD | 0x80; - dr.request = USB_REQ_GET_PROTOCOL; - dr.value = 0; - dr.index = 1; - dr.length = 1; + result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT, 0, endp, NULL, 0, HZ * SET_TIMEOUT); - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 1)) - return -1; + /* don't clear if failed */ + if (result < 0) + return result; - return buf[0]; -} + result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_STATUS, USB_DIR_IN | USB_RECIP_ENDPOINT, 0, endp, + &status, sizeof(status), HZ * SET_TIMEOUT); + if (result < 0) + return result; -int usb_set_protocol(struct usb_device *dev, int protocol) -{ - devrequest dr; + if (status & 1) + return -EPIPE; /* still halted */ - dr.requesttype = USB_RT_HIDD; - dr.request = USB_REQ_SET_PROTOCOL; - dr.value = protocol; - dr.index = 1; - dr.length = 0; + usb_endpoint_running(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe)); - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) - return -1; + /* toggle is reset on clear */ + + usb_settoggle(dev, usb_pipeendpoint(pipe), usb_pipeout(pipe), 0); return 0; } -/* keyboards want a nonzero duration according to HID spec, but - mice should use infinity (0) -keryan */ -int usb_set_idle(struct usb_device *dev, int duration, int report_id) +int usb_set_interface(struct usb_device *dev, int interface, int alternate) { - devrequest dr; + struct usb_interface *iface; + int ret; - dr.requesttype = USB_RT_HIDD; - dr.request = USB_REQ_SET_IDLE; - dr.value = (duration << 8) | report_id; - dr.index = 1; - dr.length = 0; + iface = usb_ifnum_to_if(dev, interface); + if (!iface) { + warn("selecting invalid interface %d", interface); + return -EINVAL; + } - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) - return -1; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE, alternate, + interface, NULL, 0, HZ * 5)) < 0) + return ret; + iface->act_altsetting = alternate; + dev->toggle[0] = 0; /* 9.1.1.5 says to do this */ + dev->toggle[1] = 0; + usb_set_maxpacket(dev); return 0; } int usb_set_configuration(struct usb_device *dev, int configuration) { - devrequest dr; + int i, ret; + struct usb_config_descriptor *cp = NULL; + + for (i=0; idescriptor.bNumConfigurations; i++) { + if (dev->config[i].bConfigurationValue == configuration) { + cp = &dev->config[i]; + break; + } + } + if (!cp) { + warn("selecting invalid configuration %d", configuration); + return -1; + } - dr.requesttype = 0; - dr.request = USB_REQ_SET_CONFIGURATION; - dr.value = configuration; - dr.index = 0; - dr.length = 0; + if ((ret = usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_CONFIGURATION, 0, configuration, 0, NULL, 0, HZ * SET_TIMEOUT)) < 0) + return ret; - if (dev->bus->op->control_msg(dev, usb_sndctrlpipe(dev, 0), &dr, NULL, 0)) - return -1; + dev->actconfig = cp; + dev->toggle[0] = 0; + dev->toggle[1] = 0; + usb_set_maxpacket(dev); return 0; } -int usb_get_report(struct usb_device *dev) +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size) { - unsigned char buf[8]; - devrequest dr; - - dr.requesttype = USB_RT_HIDD | 0x80; - dr.request = USB_REQ_GET_REPORT; - dr.value = 0x100; - dr.index = 1; - dr.length = 3; - - if (dev->bus->op->control_msg(dev, usb_rcvctrlpipe(dev, 0), &dr, buf, 3)) - return -1; + return usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), + USB_REQ_GET_REPORT, USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, HZ * GET_TIMEOUT); +} - return buf[0]; +int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, unsigned char id, void *buf, int size) +{ + return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), + USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE, + (type << 8) + id, ifnum, buf, size, HZ); } int usb_get_configuration(struct usb_device *dev) { - unsigned int cfgno,size; - unsigned char buffer[400]; - unsigned char * bufptr; - - bufptr=buffer; - for (cfgno=0;cfgnodescriptor.bNumConfigurations;cfgno++) { - /* Get the first 8 bytes - guaranteed */ - if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8)) - return -1; + int result; + unsigned int cfgno, length; + unsigned char buffer[8]; + unsigned char *bigbuffer; + struct usb_config_descriptor *desc = + (struct usb_config_descriptor *)buffer; + + if (dev->descriptor.bNumConfigurations > USB_MAXCONFIG) { + warn("too many configurations"); + return -1; + } + + if (dev->descriptor.bNumConfigurations < 1) { + warn("not enough configurations"); + return -1; + } + + dev->config = (struct usb_config_descriptor *) + kmalloc(dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor), GFP_KERNEL); + if (!dev->config) { + err("out of memory"); + return -1; + } + memset(dev->config, 0, dev->descriptor.bNumConfigurations * + sizeof(struct usb_config_descriptor)); + + dev->rawdescriptors = (char **)kmalloc(sizeof(char *) * + dev->descriptor.bNumConfigurations, GFP_KERNEL); + if (!dev->rawdescriptors) { + err("out of memory"); + return -1; + } + + for (cfgno = 0; cfgno < dev->descriptor.bNumConfigurations; cfgno++) { + /* We grab the first 8 bytes so we know how long the whole */ + /* configuration is */ + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, buffer, 8); + if (result < 8) { + if (result < 0) + err("unable to get descriptor"); + else + err("config descriptor too short (expected %i, got %i)", 8, result); + goto err; + } /* Get the full buffer */ - size = *(unsigned short *)(bufptr+2); - if (bufptr+size > buffer+sizeof(buffer)) - { - printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size); - size = buffer+sizeof(buffer)-bufptr; + length = le16_to_cpu(desc->wTotalLength); + + bigbuffer = kmalloc(length, GFP_KERNEL); + if (!bigbuffer) { + err("unable to allocate memory for configuration descriptors"); + result = -ENOMEM; + goto err; } - if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size)) - return -1; - - /* Prepare for next configuration */ - bufptr+=size; + + /* Now that we know the length, get the whole thing */ + result = usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bigbuffer, length); + if (result < 0) { + err("couldn't get all of config descriptors"); + kfree(bigbuffer); + goto err; + } + + if (result < length) { + err("config descriptor too short (expected %i, got %i)", length, result); + kfree(bigbuffer); + goto err; + } + + dev->rawdescriptors[cfgno] = bigbuffer; + + result = usb_parse_configuration(dev, &dev->config[cfgno], bigbuffer); + if (result > 0) + dbg("descriptor data left"); + else if (result < 0) { + result = -1; + goto err; + } + } + + return 0; +err: + dev->descriptor.bNumConfigurations = cfgno; + return result; +} + +/* + * usb_string: + * returns string length (> 0) or error (< 0) + */ +int usb_string(struct usb_device *dev, int index, char *buf, size_t size) +{ + unsigned char *tbuf; + int err; + unsigned int u, idx; + + if (size <= 0 || !buf || !index) + return -EINVAL; + buf[0] = 0; + tbuf = kmalloc(256, GFP_KERNEL); + if (!tbuf) + return -ENOMEM; + + /* get langid for strings if it's not yet known */ + if (!dev->have_langid) { + err = usb_get_string(dev, 0, 0, tbuf, 4); + if (err < 0) { + err("error getting string descriptor 0 (error=%d)", err); + goto errout; + } else if (tbuf[0] < 4) { + err("string descriptor 0 too short"); + err = -EINVAL; + goto errout; + } else { + dev->have_langid = -1; + dev->string_langid = tbuf[2] | (tbuf[3]<< 8); + /* always use the first langid listed */ + dbg("USB device number %d default language ID 0x%x", + dev->devnum, dev->string_langid); + } + } + + /* + * Just ask for a maximum length string and then take the length + * that was returned. + */ + err = usb_get_string(dev, dev->string_langid, index, tbuf, 255); + if (err < 0) + goto errout; + + size--; /* leave room for trailing NULL char in output buffer */ + for (idx = 0, u = 2; u < err; u += 2) { + if (idx >= size) + break; + if (tbuf[u+1]) /* high byte */ + buf[idx++] = '?'; /* non-ASCII character */ + else + buf[idx++] = tbuf[u]; } - return usb_parse_configuration(dev, buffer, size); + buf[idx] = 0; + err = idx; + + errout: + kfree(tbuf); + return err; } /* * By the time we get here, the device has gotten a new device ID * and is in the default state. We need to identify the thing and * get the ball rolling.. + * + * Returns 0 for success, != 0 for error. */ -void usb_new_device(struct usb_device *dev) +int usb_new_device(struct usb_device *dev) { - int addr, i; - - printk("USB new device connect, assigned device number %d\n", - dev->devnum); - - dev->maxpacketsize = 0; /* Default to 8 byte max packet size */ + int err; - addr = dev->devnum; - dev->devnum = 0; + info("USB new device connect, assigned device number %d", dev->devnum); + + /* USB v1.1 5.5.3 */ + /* We read the first 8 bytes from the device descriptor to get to */ + /* the bMaxPacketSize0 field. Then we set the maximum packet size */ + /* for the control pipe, and retrieve the rest */ + dev->epmaxpacketin [0] = 8; + dev->epmaxpacketout[0] = 8; + + err = usb_set_address(dev); + if (err < 0) { + err("USB device not accepting new address (error=%d)", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; + } - /* Slow devices */ - for (i = 0; i < 5; i++) { - if (!usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8)) - break; + wait_ms(10); /* Let the SET_ADDRESS settle */ - printk("get_descriptor failed, waiting\n"); - wait_ms(200); + err = usb_get_descriptor(dev, USB_DT_DEVICE, 0, &dev->descriptor, 8); + if (err < 8) { + if (err < 0) + err("USB device not responding, giving up (error=%d)", err); + else + err("USB device descriptor short read (expected %i, got %i)",8,err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; } - if (i == 5) { - printk("giving up\n"); - return; + dev->epmaxpacketin [0] = dev->descriptor.bMaxPacketSize0; + dev->epmaxpacketout[0] = dev->descriptor.bMaxPacketSize0; + + err = usb_get_device_descriptor(dev); + if (err < sizeof(dev->descriptor)) { + if (err < 0) + err("unable to get device descriptor (error=%d)", err); + else + err("USB device descriptor short read (expected %i, got %i)", sizeof(dev->descriptor), err); + + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; } -#if 0 - printk("maxpacketsize: %d\n", dev->descriptor.bMaxPacketSize0); -#endif - switch (dev->descriptor.bMaxPacketSize0) { - case 8: dev->maxpacketsize = 0; break; - case 16: dev->maxpacketsize = 1; break; - case 32: dev->maxpacketsize = 2; break; - case 64: dev->maxpacketsize = 3; break; + err = usb_get_configuration(dev); + if (err < 0) { + err("unable to get configuration (error=%d)", err); + clear_bit(dev->devnum, &dev->bus->devmap.devicemap); + dev->devnum = -1; + return 1; } -#if 0 - printk("dev->mps: %d\n", dev->maxpacketsize); -#endif - dev->devnum = addr; + dev->actconfig = dev->config; + usb_set_maxpacket(dev); - if (usb_set_address(dev)) { - printk("Unable to set address\n"); - /* FIXME: We should disable the port */ - return; + /* we set the default configuration here */ + if (usb_set_configuration(dev, dev->config[0].bConfigurationValue)) { + err("failed to set default configuration"); + return -1; } - wait_ms(10); /* Let the SET_ADDRESS settle */ + dbg("new device strings: Mfr=%d, Product=%d, SerialNumber=%d", + dev->descriptor.iManufacturer, dev->descriptor.iProduct, dev->descriptor.iSerialNumber); +#ifdef DEBUG + if (dev->descriptor.iManufacturer) + usb_show_string(dev, "Manufacturer", dev->descriptor.iManufacturer); + if (dev->descriptor.iProduct) + usb_show_string(dev, "Product", dev->descriptor.iProduct); + if (dev->descriptor.iSerialNumber) + usb_show_string(dev, "SerialNumber", dev->descriptor.iSerialNumber); +#endif - if (usb_get_device_descriptor(dev)) { - printk("Unable to get device descriptor\n"); - return; - } + /* now that the basic setup is over, add a /proc/bus/usb entry */ + usbdevfs_add_device(dev); - if (usb_get_configuration(dev)) { - printk("Unable to get configuration\n"); - return; + /* find drivers willing to handle this device */ + usb_find_drivers(dev); + + return 0; +} + +static int usb_open(struct inode * inode, struct file * file) +{ + int minor = MINOR(inode->i_rdev); + struct usb_driver *c = usb_minors[minor/16]; + int err = -ENODEV; + + if (!c || !c->fops) + return err; + + file->f_op = c->fops; + + return file->f_op->open(inode,file); +} + +static struct file_operations usb_fops = { + open: usb_open, +}; + +int usb_major_init(void) +{ + if (register_chrdev(USB_MAJOR,"usb",&usb_fops)) { + err("unable to get major %d for usb devices", USB_MAJOR); + return -EBUSY; } + return 0; +} + +void usb_major_cleanup(void) +{ + unregister_chrdev(USB_MAJOR, "usb"); +} -#if 0 - printk("Vendor: %X\n", dev->descriptor.idVendor); - printk("Product: %X\n", dev->descriptor.idProduct); -#endif - usb_device_descriptor(dev); +#ifdef CONFIG_PROC_FS +struct list_head *usb_driver_get_list(void) +{ + return &usb_driver_list; } -int usb_request_irq(struct usb_device *dev, unsigned int pipe, usb_device_irq handler, int period, void *dev_id) +struct list_head *usb_bus_get_list(void) { - return dev->bus->op->request_irq(dev, pipe, handler, period, dev_id); + return &usb_bus_list; } +#endif +/* + * USB may be built into the kernel or be built as modules. + * If the USB core [and maybe a host controller driver] is built + * into the kernel, and other device drivers are built as modules, + * then these symbols need to be exported for the modules to use. + */ +EXPORT_SYMBOL(usb_ifnum_to_if); + +EXPORT_SYMBOL(usb_register); +EXPORT_SYMBOL(usb_deregister); +EXPORT_SYMBOL(usb_alloc_bus); +EXPORT_SYMBOL(usb_free_bus); +EXPORT_SYMBOL(usb_register_bus); +EXPORT_SYMBOL(usb_deregister_bus); +EXPORT_SYMBOL(usb_alloc_dev); +EXPORT_SYMBOL(usb_free_dev); +EXPORT_SYMBOL(usb_inc_dev_use); + +EXPORT_SYMBOL(usb_driver_claim_interface); +EXPORT_SYMBOL(usb_interface_claimed); +EXPORT_SYMBOL(usb_driver_release_interface); + +EXPORT_SYMBOL(usb_init_root_hub); +EXPORT_SYMBOL(usb_root_hub_string); +EXPORT_SYMBOL(usb_new_device); +EXPORT_SYMBOL(usb_reset_device); +EXPORT_SYMBOL(usb_connect); +EXPORT_SYMBOL(usb_disconnect); + +EXPORT_SYMBOL(usb_check_bandwidth); +EXPORT_SYMBOL(usb_claim_bandwidth); +EXPORT_SYMBOL(usb_release_bandwidth); + +EXPORT_SYMBOL(usb_set_address); +EXPORT_SYMBOL(usb_get_descriptor); +EXPORT_SYMBOL(usb_get_class_descriptor); +EXPORT_SYMBOL(__usb_get_extra_descriptor); +EXPORT_SYMBOL(usb_get_device_descriptor); +EXPORT_SYMBOL(usb_get_string); +EXPORT_SYMBOL(usb_string); +EXPORT_SYMBOL(usb_get_protocol); +EXPORT_SYMBOL(usb_set_protocol); +EXPORT_SYMBOL(usb_get_report); +EXPORT_SYMBOL(usb_set_report); +EXPORT_SYMBOL(usb_set_idle); +EXPORT_SYMBOL(usb_clear_halt); +EXPORT_SYMBOL(usb_set_interface); +EXPORT_SYMBOL(usb_get_configuration); +EXPORT_SYMBOL(usb_set_configuration); + +EXPORT_SYMBOL(usb_get_current_frame_number); + +EXPORT_SYMBOL(usb_alloc_urb); +EXPORT_SYMBOL(usb_free_urb); +EXPORT_SYMBOL(usb_submit_urb); +EXPORT_SYMBOL(usb_unlink_urb); + +EXPORT_SYMBOL(usb_control_msg); +EXPORT_SYMBOL(usb_bulk_msg); diff --git a/drivers/usb/usb.h b/drivers/usb/usb.h deleted file mode 100644 index 10379ef88da4..000000000000 --- a/drivers/usb/usb.h +++ /dev/null @@ -1,376 +0,0 @@ -#ifndef __LINUX_USB_H -#define __LINUX_USB_H - -#include -#include -#include -#include - -static __inline__ void wait_ms(unsigned int ms) -{ - current->state = TASK_UNINTERRUPTIBLE; - schedule_timeout(1 + ms * HZ / 1000); -} - - -typedef struct { - unsigned char requesttype; - unsigned char request; - unsigned short value; - unsigned short index; - unsigned short length; -} devrequest; - -/* - * Class codes - */ -#define USB_CLASS_HUB 9 - -/* - * Descriptor types - */ -#define USB_DT_DEVICE 0x01 -#define USB_DT_CONFIG 0x02 -#define USB_DT_STRING 0x03 -#define USB_DT_INTERFACE 0x04 -#define USB_DT_ENDPOINT 0x05 - -#define USB_DT_HUB 0x29 -#define USB_DT_HID 0x21 - -/* - * Standard requests - */ -#define USB_REQ_GET_STATUS 0x00 -#define USB_REQ_CLEAR_FEATURE 0x01 -/* 0x02 is reserved */ -#define USB_REQ_SET_FEATURE 0x03 -/* 0x04 is reserved */ -#define USB_REQ_SET_ADDRESS 0x05 -#define USB_REQ_GET_DESCRIPTOR 0x06 -#define USB_REQ_SET_DESCRIPTOR 0x07 -#define USB_REQ_GET_CONFIGURATION 0x08 -#define USB_REQ_SET_CONFIGURATION 0x09 -#define USB_REQ_GET_INTERFACE 0x0A -#define USB_REQ_SET_INTERFACE 0x0B -#define USB_REQ_SYNCH_FRAME 0x0C - -/* - * HIDD requests - */ -#define USB_REQ_GET_REPORT 0x01 -#define USB_REQ_GET_IDLE 0x02 -#define USB_REQ_GET_PROTOCOL 0x03 -#define USB_REQ_SET_REPORT 0x09 -#define USB_REQ_SET_IDLE 0x0A -#define USB_REQ_SET_PROTOCOL 0x0B - -#define USB_TYPE_STANDARD (0x00 << 5) -#define USB_TYPE_CLASS (0x01 << 5) -#define USB_TYPE_VENDOR (0x02 << 5) -#define USB_TYPE_RESERVED (0x03 << 5) - -#define USB_RECIP_DEVICE 0x00 -#define USB_RECIP_INTERFACE 0x01 -#define USB_RECIP_ENDPOINT 0x02 -#define USB_RECIP_OTHER 0x03 - -/* - * Request target types. - */ -#define USB_RT_DEVICE 0x00 -#define USB_RT_INTERFACE 0x01 -#define USB_RT_ENDPOINT 0x02 - -#define USB_RT_HUB (USB_TYPE_CLASS | USB_RECIP_DEVICE) -#define USB_RT_PORT (USB_TYPE_CLASS | USB_RECIP_OTHER) - -#define USB_RT_HIDD (USB_TYPE_CLASS | USB_RECIP_INTERFACE) - -/* - * USB device number allocation bitmap. There's one bitmap - * per USB tree. - */ -struct usb_devmap { - unsigned long devicemap[128 / (8*sizeof(unsigned long))]; -}; - -/* - * This is a USB device descriptor. - * - * USB device information - * - */ - -#define USB_MAXCONFIG 8 -#define USB_MAXINTERFACES 32 -#define USB_MAXENDPOINTS 32 - -struct usb_device_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u16 bcdUSB; - __u8 bDeviceClass; - __u8 bDeviceSubClass; - __u8 bDeviceProtocol; - __u8 bMaxPacketSize0; - __u16 idVendor; - __u16 idProduct; - __u16 bcdDevice; - __u8 iManufacturer; - __u8 iProduct; - __u8 iSerialNumber; - __u8 bNumConfigurations; -}; - -/* Endpoint descriptor */ -struct usb_endpoint_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bEndpointAddress; - __u8 bmAttributes; - __u16 wMaxPacketSize; - __u8 bInterval; - void *audio; -}; - -/* Interface descriptor */ -struct usb_interface_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bInterfaceNumber; - __u8 bAlternateSetting; - __u8 bNumEndpoints; - __u8 bInterfaceClass; - __u8 bInterfaceSubClass; - __u8 bInterfaceProtocol; - __u8 iInterface; - - struct usb_endpoint_descriptor *endpoint; - void *audio; -}; - -/* Configuration descriptor information.. */ -struct usb_config_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u16 wTotalLength; - __u8 bNumInterfaces; - __u8 bConfigurationValue; - __u8 iConfiguration; - __u8 bmAttributes; - __u8 MaxPower; - - struct usb_interface_descriptor *interface; -}; - -/* String descriptor */ -struct usb_string_descriptor { - __u8 bLength; - __u8 bDescriptorType; -}; - -/* Hub descriptor */ -struct usb_hub_descriptor { - __u8 bLength; - __u8 bDescriptorType; - __u8 bNbrPorts; - __u16 wHubCharacteristics; - __u8 bPwrOn2PwrGood; - __u8 bHubContrCurrent; - /* DeviceRemovable and PortPwrCtrlMask want to be variable-length - bitmaps that hold max 256 entries, but for now they're ignored */ - __u8 filler; -}; - -struct usb_device; - -struct usb_driver { - const char * name; - int (*probe)(struct usb_device *); - void (*disconnect)(struct usb_device *); - struct list_head driver_list; -}; - -/* - * Pointer to a device endpoint interrupt function -greg - * Parameters: - * int status - This needs to be defined. Right now each HCD - * passes different transfer status bits back. Don't use it - * until we come up with a common meaning. - * void *buffer - This is a pointer to the data used in this - * USB transfer. - * void *dev_id - This is a user defined pointer set when the IRQ - * is requested that is passed back. - */ -typedef int (*usb_device_irq)(int, void *, void *); - -struct usb_operations { - struct usb_device *(*allocate)(struct usb_device *); - int (*deallocate)(struct usb_device *); - int (*control_msg)(struct usb_device *, unsigned int, void *, void *, int); - int (*request_irq)(struct usb_device *, unsigned int, usb_device_irq, int, void *); -}; - -/* - * Allocated per bus we have - */ -struct usb_bus { - struct usb_devmap devmap; /* Device map */ - struct usb_operations *op; /* Operations (specific to the HC) */ - struct usb_device *root_hub; /* Root hub */ - void *hcpriv; /* Host Controller private data */ -}; - - -#define USB_MAXCHILDREN (8) - -struct usb_device { - int devnum; /* Device number on USB bus */ - int slow; /* Slow device? */ - int maxpacketsize; /* Maximum packet size */ - - struct usb_bus *bus; /* Bus we're apart of */ - struct usb_driver *driver; /* Driver */ - struct usb_device_descriptor descriptor; /* Descriptor */ - struct usb_config_descriptor *config; /* All of the configs */ - struct usb_device *parent; - - /* - * Child devices - these can be either new devices - * (if this is a hub device), or different instances - * of this same device. - * - * Each instance needs its own set of data structuctures. - */ - - int maxchild; /* Number of ports if hub */ - struct usb_device *children[USB_MAXCHILDREN]; - - void *hcpriv; /* Host Controller private data */ - void *private; /* Upper layer private data */ -}; - -extern int usb_register(struct usb_driver *); -extern void usb_deregister(struct usb_driver *); - -extern int usb_request_irq(struct usb_device *, unsigned int, usb_device_irq, int, void *); - -extern void usb_init_root_hub(struct usb_device *dev); -extern void usb_connect(struct usb_device *dev); -extern void usb_disconnect(struct usb_device **); -extern void usb_device_descriptor(struct usb_device *dev); - -extern int usb_parse_configuration(struct usb_device *dev, void *buf, int len); - -/* - * Calling this entity a "pipe" is glorifying it. A USB pipe - * is something embarrassingly simple: it basically consists - * of the following information: - * - device number (7 bits) - * - endpoint number (4 bits) - * - current Data0/1 state (1 bit) - * - direction (1 bit) - * - speed (1 bit) - * - max packet size (2 bits: 8, 16, 32 or 64) - * - pipe type (2 bits: control, interrupt, bulk, isochronous) - * - * That's 18 bits. Really. Nothing more. And the USB people have - * documented these eighteen bits as some kind of glorious - * virtual data structure. - * - * Let's not fall in that trap. We'll just encode it as a simple - * unsigned int. The encoding is: - * - * - device: bits 8-14 - * - endpoint: bits 15-18 - * - Data0/1: bit 19 - * - direction: bit 7 (0 = Host-to-Device, 1 = Device-to-Host) - * - speed: bit 26 (0 = High, 1 = Low Speed) - * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) - * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk) - * - * Why? Because it's arbitrary, and whatever encoding we select is really - * up to us. This one happens to share a lot of bit positions with the UCHI - * specification, so that much of the uhci driver can just mask the bits - * appropriately. - */ - -#define usb_maxpacket(pipe) (8 << ((pipe) & 3)) -#define usb_packetid(pipe) (((pipe) & 0x80) ? 0x69 : 0xE1) - -#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) -#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) -#define usb_pipedata(pipe) (((pipe) >> 19) & 1) -#define usb_pipeout(pipe) (((pipe) & 0x80) == 0) -#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) - -#define usb_pipetype(pipe) (((pipe) >> 30) & 3) -#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == 0) -#define usb_pipeint(pipe) (usb_pipetype((pipe)) == 1) -#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == 2) -#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == 3) - -#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) - -static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint) -{ - return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26) | dev->maxpacketsize; -} - -static inline unsigned int __default_pipe(struct usb_device *dev) -{ - return (dev->slow << 26); -} - -/* Create control pipes.. */ -#define usb_sndctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint)) -#define usb_rcvctrlpipe(dev,endpoint) ((2 << 30) | __create_pipe(dev,endpoint) | 0x80) -#define usb_snddefctrl(dev) ((2 << 30) | __default_pipe(dev)) -#define usb_rcvdefctrl(dev) ((2 << 30) | __default_pipe(dev) | 0x80) - -/* Create .. */ - -/* - * Send and receive control messages.. - */ -void usb_new_device(struct usb_device *dev); -int usb_set_address(struct usb_device *dev); -int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, unsigned -char descindex, void *buf, int size); -int usb_get_device_descriptor(struct usb_device *dev); -int usb_get_hub_descriptor(struct usb_device *dev, void *data, int size); -int usb_clear_port_feature(struct usb_device *dev, int port, int feature); -int usb_set_port_feature(struct usb_device *dev, int port, int feature); -int usb_get_hub_status(struct usb_device *dev, void *data); -int usb_get_port_status(struct usb_device *dev, int port, void *data); -int usb_get_protocol(struct usb_device *dev); -int usb_set_protocol(struct usb_device *dev, int protocol); -int usb_set_idle(struct usb_device *dev, int duration, int report_id); -int usb_set_configuration(struct usb_device *dev, int configuration); -int usb_get_report(struct usb_device *dev); - -/* - * Debugging helpers.. - */ -void usb_show_device_descriptor(struct usb_device_descriptor *); -void usb_show_config_descriptor(struct usb_config_descriptor *); -void usb_show_interface_descriptor(struct usb_interface_descriptor *); -void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *); -void usb_show_hub_descriptor(struct usb_hub_descriptor *); -void usb_show_device(struct usb_device *); - -/* - * Audio parsing helpers - */ - -#ifdef CONFIG_USB_AUDIO -void usb_audio_interface(struct usb_interface_descriptor *, u8 *); -void usb_audio_endpoint(struct usb_endpoint_descriptor *, u8 *); -#else -extern inline void usb_audio_interface(struct usb_interface_descriptor *interface, u8 *data) {} -extern inline void usb_audio_endpoint(struct usb_endpoint_descriptor *interface, u8 *data) {} -#endif - -#endif - diff --git a/drivers/usb/usbkbd.c b/drivers/usb/usbkbd.c new file mode 100644 index 000000000000..42f82604e78d --- /dev/null +++ b/drivers/usb/usbkbd.c @@ -0,0 +1,262 @@ +/* + * $Id: usbkbd.c,v 1.11 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * USB HIDBP Keyboard support + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +static unsigned char usb_kbd_keycode[256] = { + 0, 0, 0, 0, 30, 48, 46, 32, 18, 33, 34, 35, 23, 36, 37, 38, + 50, 49, 24, 25, 16, 19, 31, 20, 22, 47, 17, 45, 21, 44, 2, 3, + 4, 5, 6, 7, 8, 9, 10, 11, 28, 1, 14, 15, 57, 12, 13, 26, + 27, 43, 84, 39, 40, 41, 51, 52, 53, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 87, 88, 99, 70,119,110,102,104,111,107,109,106, + 105,108,103, 69, 98, 55, 74, 78, 96, 79, 80, 81, 75, 76, 77, 71, + 72, 73, 82, 83, 86,127,116,117, 85, 89, 90, 91, 92, 93, 94, 95, + 120,121,122,123,134,138,130,132,128,129,131,137,133,135,136,113, + 115,114, 0, 0, 0, 0, 0,124, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 29, 42, 56,125, 97, 54,100,126,164,166,165,163,161,115,114,113, + 150,158,159,128,136,177,178,176,142,152,173,140 +}; + +struct usb_kbd { + struct input_dev dev; + unsigned char new[8]; + unsigned char old[8]; + struct urb irq, led; + devrequest dr; + unsigned char leds; + char name[128]; + int open; +}; + +static void usb_kbd_irq(struct urb *urb) +{ + struct usb_kbd *kbd = urb->context; + int i; + + if (urb->status) return; + + for (i = 0; i < 8; i++) + input_report_key(&kbd->dev, usb_kbd_keycode[i + 224], (kbd->new[0] >> i) & 1); + + for (i = 2; i < 8; i++) { + + if (kbd->old[i] > 3 && memscan(kbd->new + 2, kbd->old[i], 6) == kbd->new + 8) { + if (usb_kbd_keycode[kbd->old[i]]) + input_report_key(&kbd->dev, usb_kbd_keycode[kbd->old[i]], 0); + else + info("Unknown key (scancode %#x) released.", kbd->old[i]); + } + + if (kbd->new[i] > 3 && memscan(kbd->old + 2, kbd->new[i], 6) == kbd->old + 8) { + if (usb_kbd_keycode[kbd->new[i]]) + input_report_key(&kbd->dev, usb_kbd_keycode[kbd->new[i]], 1); + else + info("Unknown key (scancode %#x) pressed.", kbd->new[i]); + } + } + + memcpy(kbd->old, kbd->new, 8); +} + +int usb_kbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value) +{ + struct usb_kbd *kbd = dev->private; + + if (type != EV_LED) return -1; + + if (kbd->led.status == -EINPROGRESS) { + warn("had to kill led urb"); + usb_unlink_urb(&kbd->led); + } + + kbd->leds = (!!test_bit(LED_KANA, dev->led) << 3) | (!!test_bit(LED_COMPOSE, dev->led) << 3) | + (!!test_bit(LED_SCROLLL, dev->led) << 2) | (!!test_bit(LED_CAPSL, dev->led) << 1) | + (!!test_bit(LED_NUML, dev->led)); + + + if (usb_submit_urb(&kbd->led)) { + err("usb_submit_urb(leds) failed"); + return -1; + } + + return 0; +} + +static void usb_kbd_led(struct urb *urb) +{ + if (urb->status) + warn("led urb status %d received", urb->status); +} + +static int usb_kbd_open(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + if (kbd->open++) + return 0; + + if (usb_submit_urb(&kbd->irq)) + return -EIO; + + return 0; +} + +static void usb_kbd_close(struct input_dev *dev) +{ + struct usb_kbd *kbd = dev->private; + + if (!--kbd->open) + usb_unlink_urb(&kbd->irq); +} + +static void *usb_kbd_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_kbd *kbd; + int i, pipe, maxp; + char *buf; + + if (dev->descriptor.bNumConfigurations != 1) return NULL; + interface = dev->config[0].interface[ifnum].altsetting + 0; + + if (interface->bInterfaceClass != 3) return NULL; + if (interface->bInterfaceSubClass != 1) return NULL; + if (interface->bInterfaceProtocol != 1) return NULL; + if (interface->bNumEndpoints != 1) return NULL; + + endpoint = interface->endpoint + 0; + if (!(endpoint->bEndpointAddress & 0x80)) return NULL; + if ((endpoint->bmAttributes & 3) != 3) return NULL; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + usb_set_protocol(dev, interface->bInterfaceNumber, 0); + usb_set_idle(dev, interface->bInterfaceNumber, 0, 0); + + if (!(kbd = kmalloc(sizeof(struct usb_kbd), GFP_KERNEL))) return NULL; + memset(kbd, 0, sizeof(struct usb_kbd)); + + kbd->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_LED) | BIT(EV_REP); + kbd->dev.ledbit[0] = BIT(LED_NUML) | BIT(LED_CAPSL) | BIT(LED_SCROLLL) | BIT(LED_COMPOSE) | BIT(LED_KANA); + + for (i = 0; i < 255; i++) + set_bit(usb_kbd_keycode[i], kbd->dev.keybit); + clear_bit(0, kbd->dev.keybit); + + kbd->dev.private = kbd; + kbd->dev.event = usb_kbd_event; + kbd->dev.open = usb_kbd_open; + kbd->dev.close = usb_kbd_close; + + FILL_INT_URB(&kbd->irq, dev, pipe, kbd->new, maxp > 8 ? 8 : maxp, + usb_kbd_irq, kbd, endpoint->bInterval); + + kbd->dr.requesttype = USB_TYPE_CLASS | USB_RECIP_INTERFACE; + kbd->dr.request = USB_REQ_SET_REPORT; + kbd->dr.value = 0x200; + kbd->dr.index = interface->bInterfaceNumber; + kbd->dr.length = 1; + + kbd->dev.name = kbd->name; + kbd->dev.idbus = BUS_USB; + kbd->dev.idvendor = dev->descriptor.idVendor; + kbd->dev.idproduct = dev->descriptor.idProduct; + kbd->dev.idversion = dev->descriptor.bcdDevice; + + if (!(buf = kmalloc(63, GFP_KERNEL))) { + kfree(kbd); + return NULL; + } + + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(kbd->name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(kbd->name, "%s %s", kbd->name, buf); + + if (!strlen(kbd->name)) + sprintf(kbd->name, "USB HIDBP Keyboard %04x:%04x", + kbd->dev.idvendor, kbd->dev.idproduct); + + kfree(buf); + + FILL_CONTROL_URB(&kbd->led, dev, usb_sndctrlpipe(dev, 0), + (void*) &kbd->dr, &kbd->leds, 1, usb_kbd_led, kbd); + + input_register_device(&kbd->dev); + + printk(KERN_INFO "input%d: %s on on usb%d:%d.%d\n", + kbd->dev.number, kbd->name, dev->bus->busnum, dev->devnum, ifnum); + + return kbd; +} + +static void usb_kbd_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_kbd *kbd = ptr; + usb_unlink_urb(&kbd->irq); + input_unregister_device(&kbd->dev); + kfree(kbd); +} + +static struct usb_driver usb_kbd_driver = { + name: "keyboard", + probe: usb_kbd_probe, + disconnect: usb_kbd_disconnect +}; + +static int __init usb_kbd_init(void) +{ + usb_register(&usb_kbd_driver); + return 0; +} + +static void __exit usb_kbd_exit(void) +{ + usb_deregister(&usb_kbd_driver); +} + +module_init(usb_kbd_init); +module_exit(usb_kbd_exit); diff --git a/drivers/usb/usbmouse.c b/drivers/usb/usbmouse.c new file mode 100644 index 000000000000..55bd14fdbba8 --- /dev/null +++ b/drivers/usb/usbmouse.c @@ -0,0 +1,187 @@ +/* + * $Id: usbmouse.c,v 1.5 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * USB HIDBP Mouse support + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +struct usb_mouse { + signed char data[8]; + char name[128]; + struct input_dev dev; + struct urb irq; + int open; +}; + +static void usb_mouse_irq(struct urb *urb) +{ + struct usb_mouse *mouse = urb->context; + signed char *data = mouse->data; + struct input_dev *dev = &mouse->dev; + + if (urb->status) return; + + input_report_key(dev, BTN_LEFT, data[0] & 0x01); + input_report_key(dev, BTN_RIGHT, data[0] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[0] & 0x04); + input_report_key(dev, BTN_SIDE, data[0] & 0x08); + input_report_key(dev, BTN_EXTRA, data[0] & 0x10); + + input_report_rel(dev, REL_X, data[1]); + input_report_rel(dev, REL_Y, data[2]); + input_report_rel(dev, REL_WHEEL, data[3]); +} + +static int usb_mouse_open(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + if (mouse->open++) + return 0; + + if (usb_submit_urb(&mouse->irq)) + return -EIO; + + return 0; +} + +static void usb_mouse_close(struct input_dev *dev) +{ + struct usb_mouse *mouse = dev->private; + + if (!--mouse->open) + usb_unlink_urb(&mouse->irq); +} + +static void *usb_mouse_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct usb_mouse *mouse; + int pipe, maxp; + char *buf; + + if (dev->descriptor.bNumConfigurations != 1) return NULL; + interface = dev->config[0].interface[ifnum].altsetting + 0; + + if (interface->bInterfaceClass != 3) return NULL; + if (interface->bInterfaceSubClass != 1) return NULL; + if (interface->bInterfaceProtocol != 2) return NULL; + if (interface->bNumEndpoints != 1) return NULL; + + endpoint = interface->endpoint + 0; + if (!(endpoint->bEndpointAddress & 0x80)) return NULL; + if ((endpoint->bmAttributes & 3) != 3) return NULL; + + pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress); + maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe)); + + usb_set_idle(dev, interface->bInterfaceNumber, 0, 0); + + if (!(mouse = kmalloc(sizeof(struct usb_mouse), GFP_KERNEL))) return NULL; + memset(mouse, 0, sizeof(struct usb_mouse)); + + mouse->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REL); + mouse->dev.keybit[LONG(BTN_MOUSE)] = BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE); + mouse->dev.relbit[0] = BIT(REL_X) | BIT(REL_Y); + mouse->dev.keybit[LONG(BTN_MOUSE)] |= BIT(BTN_SIDE) | BIT(BTN_EXTRA); + mouse->dev.relbit[0] |= BIT(REL_WHEEL); + + mouse->dev.private = mouse; + mouse->dev.open = usb_mouse_open; + mouse->dev.close = usb_mouse_close; + + mouse->dev.name = mouse->name; + mouse->dev.idbus = BUS_USB; + mouse->dev.idvendor = dev->descriptor.idVendor; + mouse->dev.idproduct = dev->descriptor.idProduct; + mouse->dev.idversion = dev->descriptor.bcdDevice; + + if (!(buf = kmalloc(63, GFP_KERNEL))) { + kfree(mouse); + return NULL; + } + + if (dev->descriptor.iManufacturer && + usb_string(dev, dev->descriptor.iManufacturer, buf, 63) > 0) + strcat(mouse->name, buf); + if (dev->descriptor.iProduct && + usb_string(dev, dev->descriptor.iProduct, buf, 63) > 0) + sprintf(mouse->name, "%s %s", mouse->name, buf); + + if (!strlen(mouse->name)) + sprintf(mouse->name, "USB HIDBP Mouse %04x:%04x", + mouse->dev.idvendor, mouse->dev.idproduct); + + kfree(buf); + + FILL_INT_URB(&mouse->irq, dev, pipe, mouse->data, maxp > 8 ? 8 : maxp, + usb_mouse_irq, mouse, endpoint->bInterval); + + input_register_device(&mouse->dev); + + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + mouse->dev.number, mouse->name, dev->bus->busnum, dev->devnum, ifnum); + + return mouse; +} + +static void usb_mouse_disconnect(struct usb_device *dev, void *ptr) +{ + struct usb_mouse *mouse = ptr; + usb_unlink_urb(&mouse->irq); + input_unregister_device(&mouse->dev); + kfree(mouse); +} + +static struct usb_driver usb_mouse_driver = { + name: "usb_mouse", + probe: usb_mouse_probe, + disconnect: usb_mouse_disconnect, +}; + +static int __init usb_mouse_init(void) +{ + usb_register(&usb_mouse_driver); + return 0; +} + +static void __exit usb_mouse_exit(void) +{ + usb_deregister(&usb_mouse_driver); +} + +module_init(usb_mouse_init); +module_exit(usb_mouse_exit); diff --git a/drivers/usb/uss720.c b/drivers/usb/uss720.c new file mode 100644 index 000000000000..12cb880e4b81 --- /dev/null +++ b/drivers/usb/uss720.c @@ -0,0 +1,663 @@ +/*****************************************************************************/ + +/* + * uss720.c -- USS720 USB Parport Cable. + * + * Copyright (C) 1999 + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Based on parport_pc.c + * + * History: + * 0.1 04.08.99 Created + * 0.2 07.08.99 Some fixes mainly suggested by Tim Waugh + * Interrupt handling currently disabled because + * usb_request_irq crashes somewhere within ohci.c + * for no apparent reason (that is for me, anyway) + * ECP currently untested + * 0.3 10.08.99 fixing merge errors + * 0.4 13.08.99 Added Vendor/Product ID of Brad Hard's cable + * 0.5 20.09.99 usb_control_msg wrapper used + * + */ + +/*****************************************************************************/ + +#include +#include +#include +#include +#include + +/* --------------------------------------------------------------------- */ + +struct parport_uss720_private { + struct usb_device *usbdev; + void *irqhandle; + unsigned int irqpipe; + unsigned char reg[7]; /* USB registers */ +}; + +/* --------------------------------------------------------------------- */ + +static int get_1284_register(struct parport *pp, unsigned char reg, unsigned char *val) +{ + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + static const unsigned char regindex[9] = { + 4, 0, 1, 5, 5, 0, 2, 3, 6 + }; + int ret; + + if (!usbdev) + return -1; + ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev,0), 3, 0xc0, ((unsigned int)reg) << 8, 0, priv->reg, 7, HZ); + if (ret) { + printk(KERN_DEBUG "uss720: get_1284_register(%d) failed, status 0x%x\n", + (unsigned int)reg, ret); + } else { +#if 0 + printk(KERN_DEBUG "uss720: get_1284_register(%d) return %02x %02x %02x %02x %02x %02x %02x\n", + (unsigned int)reg, (unsigned int)priv->reg[0], (unsigned int)priv->reg[1], + (unsigned int)priv->reg[2], (unsigned int)priv->reg[3], (unsigned int)priv->reg[4], + (unsigned int)priv->reg[5], (unsigned int)priv->reg[6]); +#endif + /* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */ + if (priv->reg[2] & priv->reg[1] & 0x10) + parport_generic_irq(0, pp, NULL); + } + if (val) + *val = priv->reg[(reg >= 9) ? 0 : regindex[reg]]; + return ret; +} + +static int set_1284_register(struct parport *pp, unsigned char reg, unsigned char val) +{ + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + int ret; + + if (!usbdev) + return -1; + ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev,0), 4, 0x40, (((unsigned int)reg) << 8) | val, 0, NULL, 0, HZ); + if (ret) { + printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x) failed, status 0x%x\n", + (unsigned int)reg, (unsigned int)val, ret); + } else { +#if 0 + printk(KERN_DEBUG "uss720: set_1284_register(%u,0x%02x)\n", + (unsigned int)reg, (unsigned int)val); +#endif + } + return ret; +} + +/* --------------------------------------------------------------------- */ + +/* ECR modes */ +#define ECR_SPP 00 +#define ECR_PS2 01 +#define ECR_PPF 02 +#define ECR_ECP 03 +#define ECR_EPP 04 + +/* Safely change the mode bits in the ECR */ +static int change_mode(struct parport *pp, int m) +{ + struct parport_uss720_private *priv = pp->private_data; + int mode; + + if (get_1284_register(pp, 6, NULL)) + return -EIO; + /* Bits <7:5> contain the mode. */ + mode = (priv->reg[2] >> 5) & 0x7; + if (mode == m) + return 0; + /* We have to go through mode 000 or 001 */ + if (mode > ECR_PS2 && m > ECR_PS2) + if (change_mode(pp, ECR_PS2)) + return -EIO; + + if (m <= ECR_PS2 && !(priv->reg[1] & 0x20)) { + /* This mode resets the FIFO, so we may + * have to wait for it to drain first. */ + long expire = jiffies + pp->physport->cad->timeout; + switch (mode) { + case ECR_PPF: /* Parallel Port FIFO mode */ + case ECR_ECP: /* ECP Parallel Port mode */ + /* Poll slowly. */ + for (;;) { + if (get_1284_register(pp, 6, NULL)) + return -EIO; + if (priv->reg[2] & 0x01) + break; + if (time_after_eq (jiffies, expire)) + /* The FIFO is stuck. */ + return -EBUSY; + current->state = TASK_INTERRUPTIBLE; + schedule_timeout((HZ + 99) / 100); + if (signal_pending (current)) + break; + } + } + } + /* Set the mode. */ + if (set_1284_register(pp, 6, m << 5)) + return -EIO; + return 0; +} + +/* + * Clear TIMEOUT BIT in EPP MODE + */ +static int clear_epp_timeout(struct parport *pp) +{ + unsigned char stat; + + if (get_1284_register(pp, 1, &stat)) + return 1; + return stat & 1; +} + +/* + * Access functions. + */ +#if 0 +static int uss720_irq(int usbstatus, void *buffer, int len, void *dev_id) +{ + struct parport *pp = (struct parport *)dev_id; + struct parport_uss720_private *priv = pp->private_data; + + if (usbstatus != USB_ST_NOERROR || len < 4 || !buffer) + return 1; + memcpy(priv->reg, buffer, 4); + /* if nAck interrupts are enabled and we have an interrupt, call the interrupt procedure */ + if (priv->reg[2] & priv->reg[1] & 0x10) + parport_generic_irq(0, pp, NULL); + return 1; +} +#endif + +static void parport_uss720_write_data(struct parport *pp, unsigned char d) +{ + set_1284_register(pp, 0, d); +} + +static unsigned char parport_uss720_read_data(struct parport *pp) +{ + unsigned char ret; + + if (get_1284_register(pp, 0, &ret)) + return 0; + return ret; +} + +static void parport_uss720_write_control(struct parport *pp, unsigned char d) +{ + struct parport_uss720_private *priv = pp->private_data; + + d = (d & 0xf) | (priv->reg[1] & 0xf0); + if (set_1284_register(pp, 2, d)) + return; + priv->reg[1] = d; +} + +static unsigned char parport_uss720_read_control(struct parport *pp) +{ + struct parport_uss720_private *priv = pp->private_data; + return priv->reg[1] & 0xf; /* Use soft copy */ +} + +static unsigned char parport_uss720_frob_control(struct parport *pp, unsigned char mask, unsigned char val) +{ + struct parport_uss720_private *priv = pp->private_data; + unsigned char d; + + mask &= 0x0f; + val &= 0x0f; + d = (priv->reg[1] & (~mask)) ^ val; + if (set_1284_register(pp, 2, d)) + return 0; + priv->reg[1] = d; + return d & 0xf; +} + +static unsigned char parport_uss720_read_status(struct parport *pp) +{ + unsigned char ret; + + if (get_1284_register(pp, 1, &ret)) + return 0; + return ret & 0xf8; +} + +static void parport_uss720_disable_irq(struct parport *pp) +{ + struct parport_uss720_private *priv = pp->private_data; + unsigned char d; + + d = priv->reg[1] & ~0x10; + if (set_1284_register(pp, 2, d)) + return; + priv->reg[1] = d; +} + +static void parport_uss720_enable_irq(struct parport *pp) +{ + struct parport_uss720_private *priv = pp->private_data; + unsigned char d; + + d = priv->reg[1] | 0x10; + if (set_1284_register(pp, 2, d)) + return; + priv->reg[1] = d; +} + +static void parport_uss720_data_forward (struct parport *pp) +{ + struct parport_uss720_private *priv = pp->private_data; + unsigned char d; + + d = priv->reg[1] & ~0x20; + if (set_1284_register(pp, 2, d)) + return; + priv->reg[1] = d; +} + +static void parport_uss720_data_reverse (struct parport *pp) +{ + struct parport_uss720_private *priv = pp->private_data; + unsigned char d; + + d = priv->reg[1] | 0x20; + if (set_1284_register(pp, 2, d)) + return; + priv->reg[1] = d; +} + +static void parport_uss720_init_state(struct pardevice *dev, struct parport_state *s) +{ + s->u.pc.ctr = 0xc | (dev->irq_func ? 0x10 : 0x0); + s->u.pc.ecr = 0x24; +} + +static void parport_uss720_save_state(struct parport *pp, struct parport_state *s) +{ + struct parport_uss720_private *priv = pp->private_data; + + if (get_1284_register(pp, 2, NULL)) + return; + s->u.pc.ctr = priv->reg[1]; + s->u.pc.ecr = priv->reg[2]; +} + +static void parport_uss720_restore_state(struct parport *pp, struct parport_state *s) +{ + set_1284_register(pp, 2, s->u.pc.ctr); + set_1284_register(pp, 6, s->u.pc.ecr); + get_1284_register(pp, 2, NULL); +} + +static size_t parport_uss720_epp_read_data(struct parport *pp, void *buf, size_t length, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + size_t got = 0; + + if (change_mode(pp, ECR_EPP)) + return 0; + for (; got < length; got++) { + if (get_1284_register(pp, 4, (char *)buf)) + break; + ((char*)buf)++; + if (priv->reg[0] & 0x01) { + clear_epp_timeout(pp); + break; + } + } + change_mode(pp, ECR_PS2); + return got; +} + +static size_t parport_uss720_epp_write_data(struct parport *pp, const void *buf, size_t length, int flags) +{ +#if 0 + struct parport_uss720_private *priv = pp->private_data; + size_t written = 0; + + if (change_mode(pp, ECR_EPP)) + return 0; + for (; written < length; written++) { + if (set_1284_register(pp, 4, (char *)buf)) + break; + ((char*)buf)++; + if (get_1284_register(pp, 1, NULL)) + break; + if (priv->reg[0] & 0x01) { + clear_epp_timeout(pp); + break; + } + } + change_mode(pp, ECR_PS2); + return written; +#else + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + int rlen; + int i; + + if (!usbdev) + return 0; + if (change_mode(pp, ECR_EPP)) + return 0; + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buf, length, &rlen, HZ*20); + if (i) + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buf, length, rlen); + change_mode(pp, ECR_PS2); + return rlen; +#endif +} + +static size_t parport_uss720_epp_read_addr(struct parport *pp, void *buf, size_t length, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + size_t got = 0; + + if (change_mode(pp, ECR_EPP)) + return 0; + for (; got < length; got++) { + if (get_1284_register(pp, 3, (char *)buf)) + break; + ((char*)buf)++; + if (priv->reg[0] & 0x01) { + clear_epp_timeout(pp); + break; + } + } + change_mode(pp, ECR_PS2); + return got; +} + +static size_t parport_uss720_epp_write_addr(struct parport *pp, const void *buf, size_t length, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + size_t written = 0; + + if (change_mode(pp, ECR_EPP)) + return 0; + for (; written < length; written++) { + if (set_1284_register(pp, 3, *(char *)buf)) + break; + ((char*)buf)++; + if (get_1284_register(pp, 1, NULL)) + break; + if (priv->reg[0] & 0x01) { + clear_epp_timeout(pp); + break; + } + } + change_mode(pp, ECR_PS2); + return written; +} + +static size_t parport_uss720_ecp_write_data(struct parport *pp, const void *buffer, size_t len, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + int rlen; + int i; + + if (!usbdev) + return 0; + if (change_mode(pp, ECR_ECP)) + return 0; + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, HZ*20); + if (i) + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buffer, len, rlen); + change_mode(pp, ECR_PS2); + return rlen; +} + +static size_t parport_uss720_ecp_read_data(struct parport *pp, void *buffer, size_t len, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + int rlen; + int i; + + if (!usbdev) + return 0; + if (change_mode(pp, ECR_ECP)) + return 0; + i = usb_bulk_msg(usbdev, usb_rcvbulkpipe(usbdev, 2), buffer, len, &rlen, HZ*20); + if (i) + printk(KERN_ERR "uss720: recvbulk ep 2 buf %p len %u rlen %u\n", buffer, len, rlen); + change_mode(pp, ECR_PS2); + return rlen; +} + +static size_t parport_uss720_ecp_write_addr(struct parport *pp, const void *buffer, size_t len, int flags) +{ + size_t written = 0; + + if (change_mode(pp, ECR_ECP)) + return 0; + for (; written < len; written++) { + if (set_1284_register(pp, 5, *(char *)buffer)) + break; + ((char*)buffer)++; + } + change_mode(pp, ECR_PS2); + return written; +} + +static size_t parport_uss720_write_compat(struct parport *pp, const void *buffer, size_t len, int flags) +{ + struct parport_uss720_private *priv = pp->private_data; + struct usb_device *usbdev = priv->usbdev; + int rlen; + int i; + + if (!usbdev) + return 0; + if (change_mode(pp, ECR_PPF)) + return 0; + i = usb_bulk_msg(usbdev, usb_sndbulkpipe(usbdev, 1), (void *)buffer, len, &rlen, HZ*20); + if (i) + printk(KERN_ERR "uss720: sendbulk ep 1 buf %p len %u rlen %u\n", buffer, len, rlen); + change_mode(pp, ECR_PS2); + return rlen; +} + +void parport_uss720_inc_use_count(void) +{ +#ifdef MODULE + MOD_INC_USE_COUNT; +#endif +} + +void parport_uss720_dec_use_count(void) +{ +#ifdef MODULE + MOD_DEC_USE_COUNT; +#endif +} + +/* --------------------------------------------------------------------- */ + +static struct parport_operations parport_uss720_ops = +{ + parport_uss720_write_data, + parport_uss720_read_data, + + parport_uss720_write_control, + parport_uss720_read_control, + parport_uss720_frob_control, + + parport_uss720_read_status, + + parport_uss720_enable_irq, + parport_uss720_disable_irq, + + parport_uss720_data_forward, + parport_uss720_data_reverse, + + parport_uss720_init_state, + parport_uss720_save_state, + parport_uss720_restore_state, + + parport_uss720_inc_use_count, + parport_uss720_dec_use_count, + + parport_uss720_epp_write_data, + parport_uss720_epp_read_data, + parport_uss720_epp_write_addr, + parport_uss720_epp_read_addr, + + parport_uss720_ecp_write_data, + parport_uss720_ecp_read_data, + parport_uss720_ecp_write_addr, + + parport_uss720_write_compat, + parport_ieee1284_read_nibble, + parport_ieee1284_read_byte, +}; + +/* --------------------------------------------------------------------- */ + +static void * uss720_probe(struct usb_device *usbdev, unsigned int ifnum) +{ + struct usb_interface_descriptor *interface; + struct usb_endpoint_descriptor *endpoint; + struct parport_uss720_private *priv; + struct parport *pp; + int i; + + if ((usbdev->descriptor.idVendor != 0x047e || usbdev->descriptor.idProduct != 0x1001) && + (usbdev->descriptor.idVendor != 0x0557 || usbdev->descriptor.idProduct != 0x2001) && + (usbdev->descriptor.idVendor != 0x0729 || usbdev->descriptor.idProduct != 0x1284)) + return NULL; + + printk(KERN_DEBUG "uss720: probe: vendor id 0x%x, device id 0x%x\n", + usbdev->descriptor.idVendor, usbdev->descriptor.idProduct); + + /* our known interfaces have 3 alternate settings */ + if (usbdev->actconfig->interface[ifnum].num_altsetting != 3) + return NULL; + + i = usb_set_interface(usbdev, ifnum, 2); + printk(KERN_DEBUG "uss720: set inteface result %d\n", i); + + interface = &usbdev->actconfig->interface[ifnum].altsetting[2]; + + /* + * Allocate parport interface + */ + printk(KERN_INFO "uss720: (C) 1999 by Thomas Sailer, \n"); + + if (!(priv = kmalloc(sizeof(struct parport_uss720_private), GFP_KERNEL))) + return NULL; + if (!(pp = parport_register_port(0, PARPORT_IRQ_NONE, PARPORT_DMA_NONE, &parport_uss720_ops))) { + printk(KERN_WARNING "usb-uss720: could not register parport\n"); + goto probe_abort; + } + + pp->private_data = priv; + priv->usbdev = usbdev; + pp->modes = PARPORT_MODE_PCSPP | PARPORT_MODE_TRISTATE | PARPORT_MODE_EPP | PARPORT_MODE_ECP | PARPORT_MODE_COMPAT; + + /* set the USS720 control register to manual mode, no ECP compression, enable all ints */ + set_1284_register(pp, 7, 0x00); + set_1284_register(pp, 6, 0x30); /* PS/2 mode */ + set_1284_register(pp, 2, 0x0c); + /* debugging */ + get_1284_register(pp, 0, NULL); + printk("uss720: reg: %02x %02x %02x %02x %02x %02x %02x\n", + priv->reg[0], priv->reg[1], priv->reg[2], priv->reg[3], priv->reg[4], priv->reg[5], priv->reg[6]); + + endpoint = &interface->endpoint[2]; + printk(KERN_DEBUG "uss720: epaddr %d interval %d\n", endpoint->bEndpointAddress, endpoint->bInterval); +#if 0 + priv->irqpipe = usb_rcvctrlpipe(usbdev, endpoint->bEndpointAddress); + i = usb_request_irq(usbdev, priv->irqpipe, + uss720_irq, endpoint->bInterval, + pp, &priv->irqhandle); + if (i) { + printk (KERN_WARNING "usb-uss720: usb_request_irq failed (0x%x)\n", i); + goto probe_abort_port; + } +#endif + parport_proc_register(pp); + parport_announce_port(pp); + + MOD_INC_USE_COUNT; + return pp; + +#if 0 +probe_abort_port: + parport_unregister_port(pp); +#endif +probe_abort: + kfree(priv); + return NULL; +} + +static void uss720_disconnect(struct usb_device *usbdev, void *ptr) +{ + struct parport *pp = (struct parport *)ptr; + struct parport_uss720_private *priv = pp->private_data; + +#if 0 + usb_release_irq(usbdev, priv->irqhandle, priv->irqpipe); +#endif + priv->usbdev = NULL; + parport_proc_unregister(pp); + parport_unregister_port(pp); + kfree(priv); + MOD_DEC_USE_COUNT; +} + +static struct usb_driver uss720_driver = { + "uss720", + uss720_probe, + uss720_disconnect, + { NULL, NULL } +}; + +/* --------------------------------------------------------------------- */ + +MODULE_AUTHOR("Thomas M. Sailer, sailer@ife.ee.ethz.ch"); +MODULE_DESCRIPTION("USB Parport Cable driver for Cables using the Lucent Technologies USS720 Chip"); + +static int __init uss720_init(void) +{ + if (usb_register(&uss720_driver) < 0) + return -1; + + printk(KERN_INFO "uss720: USB<->IEEE1284 cable driver v0.4 registered.\n" + KERN_INFO "uss720: (C) 1999 by Thomas Sailer, \n"); + return 0; +} + +static void __exit uss720_cleanup(void) +{ + usb_deregister(&uss720_driver); +} + +module_init(uss720_init); +module_exit(uss720_cleanup); + +/* --------------------------------------------------------------------- */ diff --git a/drivers/usb/wacom.c b/drivers/usb/wacom.c new file mode 100644 index 000000000000..de2821e47097 --- /dev/null +++ b/drivers/usb/wacom.c @@ -0,0 +1,333 @@ +/* + * $Id: wacom.c,v 1.9 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * Copyright (c) 2000 Andreas Bach Aaen + * Copyright (c) 2000 Clifford Wolf + * Copyright (c) 2000 Sam Mosel + * + * USB Wacom Graphire and Wacom Intuos tablet support + * + * Sponsored by SuSE + * + * ChangeLog: + * v0.1 (vp) - Initial release + * v0.2 (aba) - Support for all buttons / combinations + * v0.3 (vp) - Support for Intuos added + * v0.4 (sm) - Support for more Intuos models, menustrip + * relative mode, proximity. + * v0.5 (vp) - Big cleanup, nifty features removed, + * they belong in userspace + * v1.8 (vp) - Submit URB only when operating, moved to CVS, + * use input_report_key instead of report_btn and + * other cleanups + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +/* + * Wacom Graphire packet: + * + * byte 0: report ID (2) + * byte 1: bit7 pointer in range + * bit5-6 pointer type 0 - pen, 1 - rubber, 2 - mouse + * bit4 1 ? + * bit3 0 ? + * bit2 mouse middle button / pen button2 + * bit1 mouse right button / pen button1 + * bit0 mouse left button / touch + * byte 2: X low bits + * byte 3: X high bits + * byte 4: Y low bits + * byte 5: Y high bits + * byte 6: pen pressure low bits / mouse wheel + * byte 7: pen presure high bits / mouse distance + * + * There are also two single-byte feature reports (2 and 3). + * + * Wacom Intuos status packet: + * + * byte 0: report ID (2) + * byte 1: bit7 1 - sync bit + * bit6 pointer in range + * bit5 pointer type report + * bit4 0 ? + * bit3 0 ? + * bit2 pen button2 + * bit1 pen button1 + * bit0 0 ? + * byte 2: X high bits + * byte 3: X low bits + * byte 4: Y high bits + * byte 5: Y low bits + * byte 6: bits 0-7: pressure (bits 2-9) + * byte 7: bits 6-7: pressure (bits 0-1) + * byte 7: bits 0-5: X tilt (bits 1-6) + * byte 8: bit 7: X tilt (bit 0) + * byte 8: bits 0-6: Y tilt (bits 0-6) + * byte 9: bits 4-7: distance + */ + +#define USB_VENDOR_ID_WACOM 0x056a + +struct wacom_features { + char *name; + int idProduct; + int pktlen; + int x_max; + int y_max; + int pressure_max; + int distance_max; + void (*irq)(struct urb *urb); + unsigned long evbit; + unsigned long absbit; + unsigned long relbit; + unsigned long btnbit; + unsigned long digibit; +}; + +struct wacom { + signed char data[10]; + struct input_dev dev; + struct urb irq; + struct wacom_features *features; + int tool; + int open; +}; + +static void wacom_graphire_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + + if (urb->status) return; + + if (data[0] != 2) + dbg("received unknown report #%d", data[0]); + + if ( data[1] & 0x80 ) { + input_report_abs(dev, ABS_X, data[2] | ((__u32)data[3] << 8)); + input_report_abs(dev, ABS_Y, data[4] | ((__u32)data[5] << 8)); + } + + switch ((data[1] >> 5) & 3) { + + case 0: /* Pen */ + input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x80); + break; + + case 1: /* Rubber */ + input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x80); + break; + + case 2: /* Mouse */ + input_report_key(dev, BTN_TOOL_MOUSE, data[7] > 24); + input_report_key(dev, BTN_LEFT, data[1] & 0x01); + input_report_key(dev, BTN_RIGHT, data[1] & 0x02); + input_report_key(dev, BTN_MIDDLE, data[1] & 0x04); + input_report_abs(dev, ABS_DISTANCE, data[7]); + input_report_rel(dev, REL_WHEEL, (signed char) data[6]); + return; + } + + input_report_abs(dev, ABS_PRESSURE, data[6] | ((__u32)data[7] << 8)); + + input_report_key(dev, BTN_TOUCH, data[1] & 0x01); + input_report_key(dev, BTN_STYLUS, data[1] & 0x02); + input_report_key(dev, BTN_STYLUS2, data[1] & 0x04); +} + +static void wacom_intuos_irq(struct urb *urb) +{ + struct wacom *wacom = urb->context; + unsigned char *data = wacom->data; + struct input_dev *dev = &wacom->dev; + unsigned int t; + + if (urb->status) return; + + if (data[0] != 2) + dbg("received unknown report #%d", data[0]); + + if (((data[1] >> 5) & 0x3) == 0x2) { /* Enter report */ + + switch (((__u32)data[2] << 4) | (data[3] >> 4)) { + case 0x012: wacom->tool = BTN_TOOL_PENCIL; break; /* Inking pen */ + case 0x822: + case 0x022: wacom->tool = BTN_TOOL_PEN; break; /* Pen */ + case 0x032: wacom->tool = BTN_TOOL_BRUSH; break; /* Stroke pen */ + case 0x094: wacom->tool = BTN_TOOL_MOUSE; break; /* Mouse 4D */ + case 0x096: wacom->tool = BTN_TOOL_LENS; break; /* Lens cursor */ + case 0x82a: + case 0x0fa: wacom->tool = BTN_TOOL_RUBBER; break; /* Eraser */ + case 0x112: wacom->tool = BTN_TOOL_AIRBRUSH; break; /* Airbrush */ + default: wacom->tool = BTN_TOOL_PEN; break; /* Unknown tool */ + } + input_report_key(dev, wacom->tool, 1); + return; + } + + if ((data[1] | data[2] | data[3] | data[4] | data[5] | + data[6] | data[7] | data[8] | data[9]) == 0x80) { /* Exit report */ + input_report_key(dev, wacom->tool, 0); + return; + } + + input_report_abs(dev, ABS_X, ((__u32)data[2] << 8) | data[3]); + input_report_abs(dev, ABS_Y, ((__u32)data[4] << 8) | data[5]); + input_report_abs(dev, ABS_PRESSURE, t = ((__u32)data[6] << 2) | ((data[7] >> 6) & 3)); + input_report_abs(dev, ABS_DISTANCE, data[9] >> 4); + input_report_abs(dev, ABS_TILT_X, ((data[7] << 1) & 0x7e) | (data[8] >> 7)); + input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f); + + input_report_key(dev, BTN_STYLUS, data[1] & 2); + input_report_key(dev, BTN_STYLUS2, data[1] & 4); + input_report_key(dev, BTN_TOUCH, t > 10); +} + +#define WACOM_INTUOS_TOOLS (BIT(BTN_TOOL_BRUSH) | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS)) + +struct wacom_features wacom_features[] = { + { "Wacom Graphire", 0x10, 8, 10206, 7422, 511, 32, wacom_graphire_irq, + BIT(EV_REL), 0, BIT(REL_WHEEL), BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE), 0 }, + { "Wacom Intuos 4x5", 0x20, 10, 12700, 10360, 1023, 15, wacom_intuos_irq, + 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + { "Wacom Intuos 6x8", 0x21, 10, 20320, 15040, 1023, 15, wacom_intuos_irq, + 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + { "Wacom Intuos 9x12", 0x22, 10, 30480, 23060, 1023, 15, wacom_intuos_irq, + 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + { "Wacom Intuos 12x12", 0x23, 10, 30480, 30480, 1023, 15, wacom_intuos_irq, + 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + { "Wacom Intuos 12x18", 0x24, 10, 47720, 30480, 1023, 15, wacom_intuos_irq, + 0, BIT(ABS_TILT_X) | BIT(ABS_TILT_Y), 0, 0, WACOM_INTUOS_TOOLS }, + { NULL , 0 } +}; + +static int wacom_open(struct input_dev *dev) +{ + struct wacom *wacom = dev->private; + + if (wacom->open++) + return 0; + + if (usb_submit_urb(&wacom->irq)) + return -EIO; + + return 0; +} + +static void wacom_close(struct input_dev *dev) +{ + struct wacom *wacom = dev->private; + + if (!--wacom->open) + usb_unlink_urb(&wacom->irq); +} + +static void *wacom_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct wacom *wacom; + int i; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_WACOM) return NULL; + for (i = 0; wacom_features[i].idProduct && wacom_features[i].idProduct != dev->descriptor.idProduct; i++); + if (!wacom_features[i].idProduct) return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(wacom = kmalloc(sizeof(struct wacom), GFP_KERNEL))) return NULL; + memset(wacom, 0, sizeof(struct wacom)); + + wacom->features = wacom_features + i; + + wacom->dev.evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS) | wacom->features->evbit; + wacom->dev.absbit[0] |= BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) | BIT(ABS_DISTANCE) | wacom->features->absbit; + wacom->dev.relbit[0] |= wacom->features->relbit; + wacom->dev.keybit[LONG(BTN_LEFT)] |= wacom->features->btnbit; + wacom->dev.keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | + BIT(BTN_TOUCH) | BIT(BTN_STYLUS) | BIT(BTN_STYLUS2) | wacom->features->digibit; + + wacom->dev.absmax[ABS_X] = wacom->features->x_max; + wacom->dev.absmax[ABS_Y] = wacom->features->y_max; + wacom->dev.absmax[ABS_PRESSURE] = wacom->features->pressure_max; + wacom->dev.absmax[ABS_DISTANCE] = wacom->features->distance_max; + wacom->dev.absmax[ABS_TILT_X] = 127; + wacom->dev.absmax[ABS_TILT_Y] = 127; + + wacom->dev.private = wacom; + wacom->dev.open = wacom_open; + wacom->dev.close = wacom_close; + + wacom->dev.name = wacom->features->name; + wacom->dev.idbus = BUS_USB; + wacom->dev.idvendor = dev->descriptor.idVendor; + wacom->dev.idproduct = dev->descriptor.idProduct; + wacom->dev.idversion = dev->descriptor.bcdDevice; + + FILL_INT_URB(&wacom->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wacom->data, wacom->features->pktlen, wacom->features->irq, wacom, endpoint->bInterval); + + input_register_device(&wacom->dev); + + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + wacom->dev.number, wacom->features->name, dev->bus->busnum, dev->devnum, ifnum); + + return wacom; +} + +static void wacom_disconnect(struct usb_device *dev, void *ptr) +{ + struct wacom *wacom = ptr; + usb_unlink_urb(&wacom->irq); + input_unregister_device(&wacom->dev); + kfree(wacom); +} + +static struct usb_driver wacom_driver = { + name: "wacom", + probe: wacom_probe, + disconnect: wacom_disconnect, +}; + +static int __init wacom_init(void) +{ + usb_register(&wacom_driver); + return 0; +} + +static void __exit wacom_exit(void) +{ + usb_deregister(&wacom_driver); +} + +module_init(wacom_init); +module_exit(wacom_exit); diff --git a/drivers/usb/wmforce.c b/drivers/usb/wmforce.c new file mode 100644 index 000000000000..dff963b74996 --- /dev/null +++ b/drivers/usb/wmforce.c @@ -0,0 +1,190 @@ +/* + * $Id: wmforce.c,v 1.6 2000/05/29 09:01:52 vojtech Exp $ + * + * Copyright (c) 2000 Vojtech Pavlik + * + * USB Logitech WingMan Force joystick support + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#include +#include +#include +#include +#include +#include + +MODULE_AUTHOR("Vojtech Pavlik "); + +#define USB_VENDOR_ID_LOGITECH 0x046d +#define USB_DEVICE_ID_LOGITECH_WMFORCE 0xc281 + +struct wmforce { + signed char data[8]; + struct input_dev dev; + struct urb irq; + int open; +}; + +static struct { + __s32 x; + __s32 y; +} wmforce_hat_to_axis[16] = {{ 0,-1}, { 1,-1}, { 1, 0}, { 1, 1}, { 0, 1}, {-1, 1}, {-1, 0}, {-1,-1}}; + +static char *wmforce_name = "Logitech WingMan Force"; + +static void wmforce_irq(struct urb *urb) +{ + struct wmforce *wmforce = urb->context; + unsigned char *data = wmforce->data; + struct input_dev *dev = &wmforce->dev; + + if (urb->status) return; + + if (data[0] != 1) { + if (data[0] != 2) + warn("received unknown report #%d", data[0]); + return; + } + + input_report_abs(dev, ABS_X, (__s16) (((__s16)data[2] << 8) | data[1])); + input_report_abs(dev, ABS_Y, (__s16) (((__s16)data[4] << 8) | data[3])); + input_report_abs(dev, ABS_THROTTLE, data[5]); + input_report_abs(dev, ABS_HAT0X, wmforce_hat_to_axis[data[7] >> 4].x); + input_report_abs(dev, ABS_HAT0Y, wmforce_hat_to_axis[data[7] >> 4].y); + + input_report_key(dev, BTN_TRIGGER, data[6] & 0x01); + input_report_key(dev, BTN_TOP, data[6] & 0x02); + input_report_key(dev, BTN_THUMB, data[6] & 0x04); + input_report_key(dev, BTN_TOP2, data[6] & 0x08); + input_report_key(dev, BTN_BASE, data[6] & 0x10); + input_report_key(dev, BTN_BASE2, data[6] & 0x20); + input_report_key(dev, BTN_BASE3, data[6] & 0x40); + input_report_key(dev, BTN_BASE4, data[6] & 0x80); + input_report_key(dev, BTN_BASE5, data[7] & 0x01); +} + +static int wmforce_open(struct input_dev *dev) +{ + struct wmforce *wmforce = dev->private; + + if (wmforce->open++) + return 0; + + if (usb_submit_urb(&wmforce->irq)) + return -EIO; + + return 0; +} + +static void wmforce_close(struct input_dev *dev) +{ + struct wmforce *wmforce = dev->private; + + if (!--wmforce->open) + usb_unlink_urb(&wmforce->irq); +} + +static void *wmforce_probe(struct usb_device *dev, unsigned int ifnum) +{ + struct usb_endpoint_descriptor *endpoint; + struct wmforce *wmforce; + int i; + + if (dev->descriptor.idVendor != USB_VENDOR_ID_LOGITECH || + dev->descriptor.idProduct != USB_DEVICE_ID_LOGITECH_WMFORCE) + return NULL; + + endpoint = dev->config[0].interface[ifnum].altsetting[0].endpoint + 0; + + if (!(wmforce = kmalloc(sizeof(struct wmforce), GFP_KERNEL))) return NULL; + memset(wmforce, 0, sizeof(struct wmforce)); + + wmforce->dev.evbit[0] = BIT(EV_KEY) | BIT(EV_ABS); + wmforce->dev.keybit[LONG(BTN_JOYSTICK)] = BIT(BTN_TRIGGER) | BIT(BTN_TOP) | BIT(BTN_THUMB) | BIT(BTN_TOP2) | + BIT(BTN_BASE) | BIT(BTN_BASE2) | BIT(BTN_BASE3) | BIT(BTN_BASE4) | BIT(BTN_BASE5); + wmforce->dev.absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_THROTTLE) | BIT(ABS_HAT0X) | BIT(ABS_HAT0Y); + + for (i = ABS_X; i <= ABS_Y; i++) { + wmforce->dev.absmax[i] = 1920; + wmforce->dev.absmin[i] = -1920; + wmforce->dev.absflat[i] = 128; + } + + wmforce->dev.absmax[ABS_THROTTLE] = 255; + wmforce->dev.absmin[ABS_THROTTLE] = 0; + + for (i = ABS_HAT0X; i <= ABS_HAT0Y; i++) { + wmforce->dev.absmax[i] = 1; + wmforce->dev.absmin[i] = -1; + } + + wmforce->dev.private = wmforce; + wmforce->dev.open = wmforce_open; + wmforce->dev.close = wmforce_close; + + wmforce->dev.name = wmforce_name; + wmforce->dev.idbus = BUS_USB; + wmforce->dev.idvendor = dev->descriptor.idVendor; + wmforce->dev.idproduct = dev->descriptor.idProduct; + wmforce->dev.idversion = dev->descriptor.bcdDevice; + + FILL_INT_URB(&wmforce->irq, dev, usb_rcvintpipe(dev, endpoint->bEndpointAddress), + wmforce->data, 8, wmforce_irq, wmforce, endpoint->bInterval); + + input_register_device(&wmforce->dev); + + printk(KERN_INFO "input%d: %s on usb%d:%d.%d\n", + wmforce->dev.number, wmforce_name, dev->bus->busnum, dev->devnum, ifnum); + + return wmforce; +} + +static void wmforce_disconnect(struct usb_device *dev, void *ptr) +{ + struct wmforce *wmforce = ptr; + usb_unlink_urb(&wmforce->irq); + input_unregister_device(&wmforce->dev); + kfree(wmforce); +} + +static struct usb_driver wmforce_driver = { + name: "wmforce", + probe: wmforce_probe, + disconnect: wmforce_disconnect, +}; + +static int __init wmforce_init(void) +{ + usb_register(&wmforce_driver); + return 0; +} + +static void __exit wmforce_exit(void) +{ + usb_deregister(&wmforce_driver); +} + +module_init(wmforce_init); +module_exit(wmforce_exit); diff --git a/drivers/video/Config.in b/drivers/video/Config.in index d07c626e51d0..700d0750f524 100644 --- a/drivers/video/Config.in +++ b/drivers/video/Config.in @@ -6,7 +6,7 @@ if [ "$CONFIG_FB" = "y" ]; then define_bool CONFIG_DUMMY_CONSOLE y if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then if [ "$CONFIG_AMIGA" = "y" -o "$CONFIG_PCI" = "y" ]; then - bool 'Permedia2 support (experimental)' CONFIG_FB_PM2 + tristate 'Permedia2 support (experimental)' CONFIG_FB_PM2 if [ "$CONFIG_FB_PM2" = "y" ]; then if [ "$CONFIG_PCI" = "y" ]; then bool ' enable FIFO disconnect feature' CONFIG_FB_PM2_FIFO_DISCONNECT @@ -51,7 +51,7 @@ if [ "$CONFIG_FB" = "y" ]; then bool 'Atari native chipset support' CONFIG_FB_ATARI fi if [ "$CONFIG_ATARI" = "y" -o "$CONFIG_PCI" != "n" ]; then - bool 'ATI Mach64 display support' CONFIG_FB_ATY + tristate 'ATI Mach64 display support' CONFIG_FB_ATY fi if [ "$CONFIG_PPC" = "y" ]; then bool 'Open Firmware frame buffer device support' CONFIG_FB_OF @@ -84,7 +84,7 @@ if [ "$CONFIG_FB" = "y" ]; then define_bool CONFIG_BUS_I2C y fi if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then - if [ "$CONFIG_PCI" != "n" ]; then + if [ "$CONFIG_PCI" != "n" -a "$ARCH" != "sparc" -a "$ARCH" != "sparc64" ]; then tristate 'Matrox acceleration' CONFIG_FB_MATROX if [ "$CONFIG_FB_MATROX" != "n" ]; then bool ' Millennium I/II support' CONFIG_FB_MATROX_MILLENIUM diff --git a/drivers/video/atyfb.c b/drivers/video/atyfb.c index 42213543351e..60ed9e12d5c8 100644 --- a/drivers/video/atyfb.c +++ b/drivers/video/atyfb.c @@ -394,7 +394,7 @@ static int read_aty_sense(const struct fb_info_aty *info); * Interface used by the world */ -void atyfb_init(void); +int atyfb_init(void); #ifdef CONFIG_FB_OF void atyfb_of_init(struct device_node *dp); #endif @@ -2880,7 +2880,7 @@ __initfunc(static int aty_init(struct fb_info_aty *info, const char *name)) return 1; } -__initfunc(void atyfb_init(void)) +int __init atyfb_init(void) { #if defined(CONFIG_FB_OF) /* We don't want to be called like this. */ @@ -2900,7 +2900,7 @@ __initfunc(void atyfb_init(void)) /* Do not attach when we have a serial console. */ if (!con_is_present()) - return; + return 0; #else u16 tmp; #endif @@ -2912,7 +2912,7 @@ __initfunc(void atyfb_init(void)) info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC); if (!info) { printk("atyfb_init: can't alloc fb_info_aty\n"); - return; + return 0; } memset(info, 0, sizeof(struct fb_info_aty)); @@ -2948,7 +2948,7 @@ __initfunc(void atyfb_init(void)) if (!info->mmap_map) { printk("atyfb_init: can't alloc mmap_map\n"); kfree(info); - return; + return 0; } memset(info->mmap_map, 0, j * sizeof(*info->mmap_map)); @@ -3133,7 +3133,7 @@ __initfunc(void atyfb_init(void)) if(!info->ati_regbase) { kfree(info); - return; + return 0; } info->ati_regbase_phys += 0xc00; @@ -3160,7 +3160,7 @@ __initfunc(void atyfb_init(void)) if(!info->frame_buffer) { kfree(info); - return; + return 0; } #endif /* __sparc__ */ @@ -3169,7 +3169,7 @@ __initfunc(void atyfb_init(void)) if (info->mmap_map) kfree(info->mmap_map); kfree(info); - return; + return 0; } #ifdef __sparc__ @@ -3207,7 +3207,7 @@ __initfunc(void atyfb_init(void)) info = kmalloc(sizeof(struct fb_info_aty), GFP_ATOMIC); if (!info) { printk("atyfb_init: can't alloc fb_info_aty\n"); - return; + return 0; } memset(info, 0, sizeof(struct fb_info_aty)); @@ -3223,10 +3223,11 @@ __initfunc(void atyfb_init(void)) if (!aty_init(info, "ISA bus")) { kfree(info); /* This is insufficient! kernel_map has added two large chunks!! */ - return; + return 0; } } #endif + return 1; } #ifdef CONFIG_FB_OF @@ -4213,3 +4214,46 @@ aty_sleep_notify(struct pmu_sleep_notifier *self, int when) return result; } #endif /* CONFIG_PMAC_PBOOK */ + +#ifdef MODULE + +int blink = 1; +static u32 vram = 0; +static int pll = 0; +static int mclk = 0; +#if defined(CONFIG_PPC) +static int vmode = VMODE_NVRAM; +static int cmode = CMODE_NVRAM; +#endif + +MODULE_PARM(noaccel, "i"); +MODULE_PARM_DESC(noaccel, "Do not use accelerating engine (0 or 1=disabled) (default=0)"); +MODULE_PARM(blink, "i"); +MODULE_PARM_DESC(blink, "Enables hardware cursor blinking (0 or 1) (default=1)"); +#ifdef CONFIG_PPC +MODULE_PARM(vmode, "i"); +MODULE_PARM_DESC(vmode, "Specify the vmode mode number that should be used (640x480 default)"); +MODULE_PARM(cmode, "i"); +MODULE_PARM_DESC(cmode, "Specify the video depth that should be used (8bit default)"); +#endif + +int init_module(void) +{ + curblink = blink; + default_vram = vram; + default_pll = pll; + default_mclk = mclk; +#ifdef CONFIG_PPC + default_vmode = vmode; + default_cmode = cmode; +#endif + if (!atyfb_init()) + return -ENXIO; + MOD_INC_USE_COUNT; + return 0; +} + +void cleanup_module(void) +{ +} +#endif /* MODULE */ diff --git a/drivers/video/pm2fb.c b/drivers/video/pm2fb.c index 33b0abf75b00..466127a1bdaf 100644 --- a/drivers/video/pm2fb.c +++ b/drivers/video/pm2fb.c @@ -2041,12 +2041,12 @@ void pm2fb_cleanup(struct fb_info* info) { board_table[i->board].cleanup(i); } -__initfunc(void pm2fb_init(void)) { +int __init pm2fb_init(void) { memset(&fb_info, 0, sizeof(fb_info)); memcpy(&fb_info.current_par, &pm2fb_options.user_mode, sizeof(fb_info.current_par)); if (!pm2fb_conf(&fb_info)) - return; + return 0; pm2fb_reset(&fb_info); fb_info.disp.scrollmode=SCROLL_YNOMOVE; fb_info.gen.parsize=sizeof(struct pm2fb_par); @@ -2065,7 +2065,7 @@ __initfunc(void pm2fb_init(void)) { fbgen_install_cmap(0, &fb_info.gen); if (register_framebuffer(&fb_info.gen.info)<0) { printk("pm2fb: unable to register.\n"); - return; + return 0; } printk("fb%d: %s (%s), using %uK of video memory.\n", GET_FB_IDX(fb_info.gen.info.node), @@ -2073,6 +2073,7 @@ __initfunc(void pm2fb_init(void)) { permedia2_name, (u32 )(fb_info.regions.fb_size>>10)); MOD_INC_USE_COUNT; + return 1; } static void __init pm2fb_mode_setup(char* options) { @@ -2127,7 +2128,8 @@ MODULE_PARM(mode, "s"); int init_module(void) { if (mode) pm2fb_mode_setup(mode); - pm2fb_init(); + if (!pm2fb_init()) + return -ENXIO; } void cleanup_module(void) { diff --git a/include/linux/agp_backend.h b/include/linux/agp_backend.h new file mode 100644 index 000000000000..a350944506a5 --- /dev/null +++ b/include/linux/agp_backend.h @@ -0,0 +1,228 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AGP_BACKEND_H +#define _AGP_BACKEND_H 1 + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#define AGPGART_VERSION_MAJOR 0 +#define AGPGART_VERSION_MINOR 99 + +enum chipset_type { + NOT_SUPPORTED, + INTEL_GENERIC, + INTEL_LX, + INTEL_BX, + INTEL_GX, + INTEL_I810, + INTEL_I840, + VIA_GENERIC, + VIA_VP3, + VIA_MVP3, + VIA_MVP4, + VIA_APOLLO_SUPER, + VIA_APOLLO_PRO, + SIS_GENERIC, + AMD_GENERIC, + AMD_IRONGATE, + ALI_M1541, + ALI_GENERIC +}; + +typedef struct _agp_version { + u16 major; + u16 minor; +} agp_version; + +typedef struct _agp_kern_info { + agp_version version; + struct pci_dev *device; + enum chipset_type chipset; + unsigned long mode; + off_t aper_base; + size_t aper_size; + int max_memory; /* In pages */ + int current_memory; +} agp_kern_info; + +/* + * The agp_memory structure has information + * about the block of agp memory allocated. + * A caller may manipulate the next and prev + * pointers to link each allocated item into + * a list. These pointers are ignored by the + * backend. Everything else should never be + * written to, but the caller may read any of + * the items to detrimine the status of this + * block of agp memory. + * + */ + +typedef struct _agp_memory { + int key; + struct _agp_memory *next; + struct _agp_memory *prev; + size_t page_count; + int num_scratch_pages; + unsigned long *memory; + off_t pg_start; + u32 type; + u32 physical; + u8 is_bound; + u8 is_flushed; +} agp_memory; + +#define AGP_NORMAL_MEMORY 0 + +extern void agp_free_memory(agp_memory *); + +/* + * agp_free_memory : + * + * This function frees memory associated with + * an agp_memory pointer. It is the only function + * that can be called when the backend is not owned + * by the caller. (So it can free memory on client + * death.) + * + * It takes an agp_memory pointer as an argument. + * + */ + +extern agp_memory *agp_allocate_memory(size_t, u32); + +/* + * agp_allocate_memory : + * + * This function allocates a group of pages of + * a certain type. + * + * It takes a size_t argument of the number of pages, and + * an u32 argument of the type of memory to be allocated. + * Every agp bridge device will allow you to allocate + * AGP_NORMAL_MEMORY which maps to physical ram. Any other + * type is device dependant. + * + * It returns NULL whenever memory is unavailable. + * + */ + +extern void agp_copy_info(agp_kern_info *); + +/* + * agp_copy_info : + * + * This function copies information about the + * agp bridge device and the state of the agp + * backend into an agp_kern_info pointer. + * + * It takes an agp_kern_info pointer as an + * argument. The caller should insure that + * this pointer is valid. + * + */ + +extern int agp_bind_memory(agp_memory *, off_t); + +/* + * agp_bind_memory : + * + * This function binds an agp_memory structure + * into the graphics aperture translation table. + * + * It takes an agp_memory pointer and an offset into + * the graphics aperture translation table as arguments + * + * It returns -EINVAL if the pointer == NULL. + * It returns -EBUSY if the area of the table + * requested is already in use. + * + */ + +extern int agp_unbind_memory(agp_memory *); + +/* + * agp_unbind_memory : + * + * This function removes an agp_memory structure + * from the graphics aperture translation table. + * + * It takes an agp_memory pointer as an argument. + * + * It returns -EINVAL if this piece of agp_memory + * is not currently bound to the graphics aperture + * translation table or if the agp_memory + * pointer == NULL + * + */ + +extern void agp_enable(u32); + +/* + * agp_enable : + * + * This function initializes the agp point-to-point + * connection. + * + * It takes an agp mode register as an argument + * + */ + +extern int agp_backend_acquire(void); + +/* + * agp_backend_acquire : + * + * This Function attempts to acquire the agp + * backend. + * + * returns -EBUSY if agp is in use, + * returns 0 if the caller owns the agp backend + */ + +extern void agp_backend_release(void); + +/* + * agp_backend_release : + * + * This Function releases the lock on the agp + * backend. + * + * The caller must insure that the graphics + * aperture translation table is read for use + * by another entity. (Ensure that all memory + * it bound is unbound.) + * + */ + +#endif /* _AGP_BACKEND_H */ diff --git a/include/linux/agpgart.h b/include/linux/agpgart.h new file mode 100644 index 000000000000..7457cd114baf --- /dev/null +++ b/include/linux/agpgart.h @@ -0,0 +1,224 @@ +/* + * AGPGART module version 0.99 + * Copyright (C) 1999 Jeff Hartmann + * Copyright (C) 1999 Precision Insight, Inc. + * Copyright (C) 1999 Xi Graphics, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included + * in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + * JEFF HARTMANN, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + */ + +#ifndef _AGP_H +#define _AGP_H 1 + +#define AGPIOC_BASE 'A' +#define AGPIOC_INFO _IOR (AGPIOC_BASE, 0, agp_info*) +#define AGPIOC_ACQUIRE _IO (AGPIOC_BASE, 1) +#define AGPIOC_RELEASE _IO (AGPIOC_BASE, 2) +#define AGPIOC_SETUP _IOW (AGPIOC_BASE, 3, agp_setup*) +#define AGPIOC_RESERVE _IOW (AGPIOC_BASE, 4, agp_region*) +#define AGPIOC_PROTECT _IOW (AGPIOC_BASE, 5, agp_region*) +#define AGPIOC_ALLOCATE _IOWR(AGPIOC_BASE, 6, agp_allocate*) +#define AGPIOC_DEALLOCATE _IOW (AGPIOC_BASE, 7, int) +#define AGPIOC_BIND _IOW (AGPIOC_BASE, 8, agp_bind*) +#define AGPIOC_UNBIND _IOW (AGPIOC_BASE, 9, agp_unbind*) + +#define AGP_DEVICE "/dev/agpgart" + +#ifndef TRUE +#define TRUE 1 +#endif + +#ifndef FALSE +#define FALSE 0 +#endif + +#ifndef __KERNEL__ +#include +#include + +typedef struct _agp_version { + __u16 major; + __u16 minor; +} agp_version; + +typedef struct _agp_info { + agp_version version; /* version of the driver */ + __u32 bridge_id; /* bridge vendor/device */ + __u32 agp_mode; /* mode info of bridge */ + off_t aper_base; /* base of aperture */ + size_t aper_size; /* size of aperture */ + size_t pg_total; /* max pages (swap + system) */ + size_t pg_system; /* max pages (system) */ + size_t pg_used; /* current pages used */ +} agp_info; + +typedef struct _agp_setup { + __u32 agp_mode; /* mode info of bridge */ +} agp_setup; + +/* + * The "prot" down below needs still a "sleep" flag somehow ... + */ +typedef struct _agp_segment { + off_t pg_start; /* starting page to populate */ + size_t pg_count; /* number of pages */ + int prot; /* prot flags for mmap */ +} agp_segment; + +typedef struct _agp_region { + pid_t pid; /* pid of process */ + size_t seg_count; /* number of segments */ + struct _agp_segment *seg_list; +} agp_region; + +typedef struct _agp_allocate { + int key; /* tag of allocation */ + size_t pg_count; /* number of pages */ + __u32 type; /* 0 == normal, other devspec */ + __u32 physical; /* device specific (some devices + * need a phys address of the + * actual page behind the gatt + * table) */ +} agp_allocate; + +typedef struct _agp_bind { + int key; /* tag of allocation */ + off_t pg_start; /* starting page to populate */ +} agp_bind; + +typedef struct _agp_unbind { + int key; /* tag of allocation */ + __u32 priority; /* priority for paging out */ +} agp_unbind; + +#else /* __KERNEL__ */ + +#define AGPGART_MINOR 175 + +#define AGP_UNLOCK() up(&(agp_fe.agp_mutex)); +#define AGP_LOCK() down(&(agp_fe.agp_mutex)); +#define AGP_LOCK_INIT() sema_init(&(agp_fe.agp_mutex), 1) + +#ifndef _AGP_BACKEND_H +typedef struct _agp_version { + u16 major; + u16 minor; +} agp_version; + +#endif + +typedef struct _agp_info { + agp_version version; /* version of the driver */ + u32 bridge_id; /* bridge vendor/device */ + u32 agp_mode; /* mode info of bridge */ + off_t aper_base; /* base of aperture */ + size_t aper_size; /* size of aperture */ + size_t pg_total; /* max pages (swap + system) */ + size_t pg_system; /* max pages (system) */ + size_t pg_used; /* current pages used */ +} agp_info; + +typedef struct _agp_setup { + u32 agp_mode; /* mode info of bridge */ +} agp_setup; + +/* + * The "prot" down below needs still a "sleep" flag somehow ... + */ +typedef struct _agp_segment { + off_t pg_start; /* starting page to populate */ + size_t pg_count; /* number of pages */ + int prot; /* prot flags for mmap */ +} agp_segment; + +typedef struct _agp_segment_priv { + off_t pg_start; + size_t pg_count; + pgprot_t prot; +} agp_segment_priv; + +typedef struct _agp_region { + pid_t pid; /* pid of process */ + size_t seg_count; /* number of segments */ + struct _agp_segment *seg_list; +} agp_region; + +typedef struct _agp_allocate { + int key; /* tag of allocation */ + size_t pg_count; /* number of pages */ + u32 type; /* 0 == normal, other devspec */ + u32 physical; /* device specific (some devices + * need a phys address of the + * actual page behind the gatt + * table) */ +} agp_allocate; + +typedef struct _agp_bind { + int key; /* tag of allocation */ + off_t pg_start; /* starting page to populate */ +} agp_bind; + +typedef struct _agp_unbind { + int key; /* tag of allocation */ + u32 priority; /* priority for paging out */ +} agp_unbind; + +typedef struct _agp_client { + struct _agp_client *next; + struct _agp_client *prev; + pid_t pid; + int num_segments; + agp_segment_priv **segments; +} agp_client; + +typedef struct _agp_controller { + struct _agp_controller *next; + struct _agp_controller *prev; + pid_t pid; + int num_clients; + agp_memory *pool; + agp_client *clients; +} agp_controller; + +#define AGP_FF_ALLOW_CLIENT 0 +#define AGP_FF_ALLOW_CONTROLLER 1 +#define AGP_FF_IS_CLIENT 2 +#define AGP_FF_IS_CONTROLLER 3 +#define AGP_FF_IS_VALID 4 + +typedef struct _agp_file_private { + struct _agp_file_private *next; + struct _agp_file_private *prev; + pid_t my_pid; + u32 access_flags; +} agp_file_private; + +struct agp_front_data { + struct semaphore agp_mutex; + agp_controller *current_controller; + agp_controller *controllers; + agp_file_private *file_priv_list; + u8 used_by_controller; + u8 backend_acquired; +}; + +#endif /* __KERNEL__ */ + +#endif /* _AGP_H */ diff --git a/include/linux/cciss_ioctl.h b/include/linux/cciss_ioctl.h new file mode 100644 index 000000000000..989549b2413f --- /dev/null +++ b/include/linux/cciss_ioctl.h @@ -0,0 +1,186 @@ +#ifndef CCISS_IOCTLH +#define CCISS_IOCTLH + +#include +#include + +#define CCISS_IOC_MAGIC 'B' + + +typedef struct _cciss_pci_info_struct +{ + unsigned char bus; + unsigned char dev_fn; + __u32 board_id; +} cciss_pci_info_struct; + +typedef struct _cciss_coalint_struct +{ + __u32 delay; + __u32 count; +} cciss_coalint_struct; + +typedef char NodeName_type[16]; + +typedef __u32 Heartbeat_type; + +#define CISS_PARSCSIU2 0x0001 +#define CISS_PARCSCIU3 0x0002 +#define CISS_FIBRE1G 0x0100 +#define CISS_FIBRE2G 0x0200 +typedef __u32 BusTypes_type; + +typedef char FirmwareVer_type[4]; +typedef __u32 DriverVer_type; + + +#ifndef CCISS_CMD_H +// This defines are duplicated in cciss_cmd.h in the driver directory + +//general boundary defintions +#define SENSEINFOBYTES 32//note that this value may vary between host implementations + +//Command Status value +#define CMD_SUCCESS 0x0000 +#define CMD_TARGET_STATUS 0x0001 +#define CMD_DATA_UNDERRUN 0x0002 +#define CMD_DATA_OVERRUN 0x0003 +#define CMD_INVALID 0x0004 +#define CMD_PROTOCOL_ERR 0x0005 +#define CMD_HARDWARE_ERR 0x0006 +#define CMD_CONNECTION_LOST 0x0007 +#define CMD_ABORTED 0x0008 +#define CMD_ABORT_FAILED 0x0009 +#define CMD_UNSOLICITED_ABORT 0x000A +#define CMD_TIMEOUT 0x000B +#define CMD_UNABORTABLE 0x000C + +//transfer direction +#define XFER_NONE 0x00 +#define XFER_WRITE 0x01 +#define XFER_READ 0x02 +#define XFER_RSVD 0x03 + +//task attribute +#define ATTR_UNTAGGED 0x00 +#define ATTR_SIMPLE 0x04 +#define ATTR_HEADOFQUEUE 0x05 +#define ATTR_ORDERED 0x06 +#define ATTR_ACA 0x07 + +//cdb type +#define TYPE_CMD 0x00 +#define TYPE_MSG 0x01 + +// Type defs used in the following structs +#define BYTE __u8 +#define WORD __u16 +#define HWORD __u16 +#define DWORD __u32 + +#define CISS_MAX_LUN 16 + +#pragma pack(1) + +//Command List Structure +typedef union _SCSI3Addr_struct { + struct { + BYTE Bus:6; + BYTE Mode:2; // b00 + BYTE Dev; + } PeripDev; + struct { + BYTE DevMSB:6; + BYTE Mode:2; // b01 + BYTE DevLSB; + } LogDev; + struct { + BYTE Targ:6; + BYTE Mode:2; // b10 + BYTE Dev:5; + BYTE Bus:3; + } LogUnit; +} SCSI3Addr_struct; + +typedef struct _PhysDevAddr_struct { + DWORD TargetId:24; + DWORD Bus:6; + DWORD Mode:2; + SCSI3Addr_struct Target[2]; //2 level target device addr +} PhysDevAddr_struct; + +typedef struct _LogDevAddr_struct { + DWORD VolId:30; + DWORD Mode:2; + BYTE reserved[4]; +} LogDevAddr_struct; + +typedef union _LUNAddr_struct { + BYTE LunAddrBytes[8]; + SCSI3Addr_struct SCSI3Lun[4]; + PhysDevAddr_struct PhysDev; + LogDevAddr_struct LogDev; +} LUNAddr_struct; + +typedef struct _RequestBlock_struct { + BYTE CDBLen; + struct { + BYTE Type:3; + BYTE Attribute:3; + BYTE Direction:2; + } Type; + HWORD Timeout; + BYTE CDB[16]; +} RequestBlock_struct; + +typedef union _MoreErrInfo_struct{ + struct { + BYTE Reserved[3]; + BYTE Type; + DWORD ErrorInfo; + }Common_Info; + struct{ + BYTE Reserved[2]; + BYTE offense_size;//size of offending entry + BYTE offense_num; //byte # of offense 0-base + DWORD offense_value; + }Invalid_Cmd; +}MoreErrInfo_struct; +typedef struct _ErrorInfo_struct { + BYTE ScsiStatus; + BYTE SenseLen; + HWORD CommandStatus; + DWORD ResidualCnt; + MoreErrInfo_struct MoreErrInfo; + BYTE SenseInfo[SENSEINFOBYTES]; +} ErrorInfo_struct; + +#pragma pack() +#endif /* CCISS_CMD_H */ + +typedef struct _IOCTL_Command_struct { + LUNAddr_struct LUN_info; + RequestBlock_struct Request; + ErrorInfo_struct error_info; + WORD buf_size; /* size in bytes of the buf */ + BYTE *buf; +} IOCTL_Command_struct; + + +#define CCISS_GETPCIINFO _IOR(CCISS_IOC_MAGIC, 1, cciss_pci_info_struct) + +#define CCISS_GETINTINFO _IOR(CCISS_IOC_MAGIC, 2, cciss_coalint_struct) +#define CCISS_SETINTINFO _IOW(CCISS_IOC_MAGIC, 3, cciss_coalint_struct) + +#define CCISS_GETNODENAME _IOR(CCISS_IOC_MAGIC, 4, NodeName_type) +#define CCISS_SETNODENAME _IOW(CCISS_IOC_MAGIC, 5, NodeName_type) + +#define CCISS_GETHEARTBEAT _IOR(CCISS_IOC_MAGIC, 6, Heartbeat_type) +#define CCISS_GETBUSTYPES _IOR(CCISS_IOC_MAGIC, 7, BusTypes_type) +#define CCISS_GETFIRMVER _IOR(CCISS_IOC_MAGIC, 8, FirmwareVer_type) +#define CCISS_GETDRIVVER _IOR(CCISS_IOC_MAGIC, 9, DriverVer_type) +#define CCISS_REVALIDVOLS _IO(CCISS_IOC_MAGIC, 10) +#define CCISS_PASSTHRU _IOWR(CCISS_IOC_MAGIC, 11, IOCTL_Command_struct) + + +#endif diff --git a/include/linux/devfs_fs_kernel.h b/include/linux/devfs_fs_kernel.h new file mode 100644 index 000000000000..b14727d080fb --- /dev/null +++ b/include/linux/devfs_fs_kernel.h @@ -0,0 +1,7 @@ +typedef int* devfs_handle_t; + +#define devfs_register(a,b,c,d,e,f,g,h,i,j,k) NULL +#define devfs_unregister(a) +#define devfs_mk_dir(a,b,c,d) NULL +#define devfs_register_chrdev(a,b,c) register_chrdev(a,b,c) +#define devfs_unregister_chrdev(a,b) unregister_chrdev(a,b) diff --git a/include/linux/fs.h b/include/linux/fs.h index fe6845043cb6..c64f5f4db987 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -279,6 +279,7 @@ static inline int buffer_protected(struct buffer_head * bh) #include #include #include +#include /* * Attribute flags. These should be or-ed together to figure out what @@ -392,6 +393,7 @@ struct inode { struct hfs_inode_info hfs_i; struct adfs_inode_info adfs_i; struct qnx4_inode_info qnx4_i; + struct usbdev_inode_info usbdev_i; struct socket socket_i; void *generic_ip; } u; @@ -514,6 +516,7 @@ extern int fasync_helper(int, struct file *, int, struct fasync_struct **); #include #include #include +#include extern struct list_head super_blocks; @@ -557,6 +560,7 @@ struct super_block { struct hfs_sb_info hfs_sb; struct adfs_sb_info adfs_sb; struct qnx4_sb_info qnx4_sb; + struct usbdev_sb_info usbdevfs_sb; void *generic_sbp; } u; /* diff --git a/include/linux/input.h b/include/linux/input.h new file mode 100644 index 000000000000..6d19fc2a9f66 --- /dev/null +++ b/include/linux/input.h @@ -0,0 +1,532 @@ +#ifndef _INPUT_H +#define _INPUT_H + +/* + * $Id: input.h,v 1.13 2000/05/29 10:54:53 vojtech Exp $ + * + * Copyright (c) 1999-2000 Vojtech Pavlik + * + * Sponsored by SuSE + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Should you need to contact me, the author, you can do so either by + * e-mail - mail your message to , or by paper mail: + * Vojtech Pavlik, Ucitelska 1576, Prague 8, 182 00 Czech Republic + */ + +#ifdef __KERNEL__ +#include +#else +#include +#include +#endif + +/* + * The event structure itself + */ + +struct input_event { + struct timeval time; + unsigned short type; + unsigned short code; + unsigned int value; +}; + +/* + * Protocol version. + */ + +#define EV_VERSION 0x010000 + +/* + * IOCTLs (0x00 - 0x7f) + */ + +#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ +#define EVIOCGID _IOR('E', 0x02, short[4]) /* get device ID */ +#define EVIOCGREP _IOR('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCSREP _IOW('E', 0x03, int[2]) /* get repeat settings */ +#define EVIOCGKEYCODE _IOR('E', 0x04, int[2]) /* get keycode */ +#define EVIOCSKEYCODE _IOW('E', 0x04, int[2]) /* set keycode */ +#define EVIOCGKEY _IOR('E', 0x05, int[2]) /* get key value */ +#define EVIOCGNAME(len) _IOC(_IOC_READ, 'E', 0x06, len) /* get device name */ + +#define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + ev, len) /* get event bits */ +#define EVIOCGABS(abs) _IOR('E', 0x40 + abs, int[5]) /* get abs value/limits */ + +/* + * Event types + */ + +#define EV_RST 0x00 +#define EV_KEY 0x01 +#define EV_REL 0x02 +#define EV_ABS 0x03 +#define EV_LED 0x11 +#define EV_SND 0x12 +#define EV_REP 0x14 +#define EV_MAX 0x1f + +/* + * Keys and buttons + */ + +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 +#define KEY_103RD 84 +#define KEY_F13 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_F14 89 +#define KEY_F15 90 +#define KEY_F16 91 +#define KEY_F17 92 +#define KEY_F18 93 +#define KEY_F19 94 +#define KEY_F20 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_F21 120 +#define KEY_F22 121 +#define KEY_F23 122 +#define KEY_F24 123 +#define KEY_JPN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 +#define KEY_AGAIN 129 +#define KEY_PROPS 130 +#define KEY_UNDO 131 +#define KEY_FRONT 132 +#define KEY_COPY 133 +#define KEY_OPEN 134 +#define KEY_PASTE 135 +#define KEY_FIND 136 +#define KEY_CUT 137 +#define KEY_HELP 138 +#define KEY_MENU 139 +#define KEY_CALC 140 +#define KEY_SETUP 141 +#define KEY_SLEEP 142 +#define KEY_WAKEUP 143 +#define KEY_FILE 144 +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 +#define KEY_DIRECTION 153 +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 +#define KEY_COMPUTER 157 +#define KEY_BACK 158 +#define KEY_FORWARD 159 +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 + +#define KEY_CONFIG 171 +#define KEY_HOMEPAGE 172 +#define KEY_REFRESH 173 +#define KEY_EXIT 174 +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 + +#define KEY_UNKNOWN 180 + +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b + +#define BTN_GAMEPAD 0x130 +#define BTN_A 0x130 +#define BTN_B 0x131 +#define BTN_C 0x132 +#define BTN_X 0x133 +#define BTN_Y 0x134 +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c + +#define KEY_MAX 0x1ff + +/* + * Relative axes + */ + +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +#define REL_MAX 0x0f + +/* + * Absolute axes + */ + +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_TL 0x08 +#define ABS_TR 0x09 +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_MISC 0x1c +#define ABS_MAX 0x1f + +/* + * LEDs + */ + +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_MAX 0x0f + +/* + * Autorepeat values + */ + +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 + +/* + * Sounds + */ + +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_MAX 0x07 + +/* + * IDs. + */ + +#define ID_BUS 0 +#define ID_VENDOR 1 +#define ID_PRODUCT 2 +#define ID_VERSION 3 + +#define BUS_PCI 0x01 +#define BUS_ISAPNP 0x02 +#define BUS_USB 0x03 + +#define BUS_ISA 0x10 +#define BUS_I8042 0x11 +#define BUS_XTKBD 0x12 +#define BUS_RS232 0x13 +#define BUS_GAMEPORT 0x14 +#define BUS_PARPORT 0x15 +#define BUS_AMIGA 0x16 + +#ifdef __KERNEL__ + +/* + * In-kernel definitions. + */ + +#include +#include + +#define NBITS(x) ((((x)-1)/BITS_PER_LONG)+1) +#define BIT(x) (1UL<<((x)%BITS_PER_LONG)) +#define LONG(x) ((x)/BITS_PER_LONG) + +struct input_dev { + + void *private; + + int number; + char *name; + unsigned short idbus; + unsigned short idvendor; + unsigned short idproduct; + unsigned short idversion; + + unsigned long evbit[NBITS(EV_MAX)]; + unsigned long keybit[NBITS(KEY_MAX)]; + unsigned long relbit[NBITS(REL_MAX)]; + unsigned long absbit[NBITS(ABS_MAX)]; + unsigned long ledbit[NBITS(LED_MAX)]; + unsigned long sndbit[NBITS(SND_MAX)]; + + unsigned int keycodemax; + unsigned int keycodesize; + void *keycode; + + unsigned int repeat_key; + struct timer_list timer; + + int abs[ABS_MAX + 1]; + int rep[REP_MAX + 1]; + + unsigned long key[NBITS(KEY_MAX)]; + unsigned long led[NBITS(LED_MAX)]; + unsigned long snd[NBITS(SND_MAX)]; + + int absmax[ABS_MAX + 1]; + int absmin[ABS_MAX + 1]; + int absfuzz[ABS_MAX + 1]; + int absflat[ABS_MAX + 1]; + + int (*open)(struct input_dev *dev); + void (*close)(struct input_dev *dev); + int (*event)(struct input_dev *dev, unsigned int type, unsigned int code, int value); + + struct input_handle *handle; + struct input_dev *next; +}; + +struct input_handler { + + void *private; + + void (*event)(struct input_handle *handle, unsigned int type, unsigned int code, int value); + struct input_handle* (*connect)(struct input_handler *handler, struct input_dev *dev); + void (*disconnect)(struct input_handle *handle); + + struct file_operations *fops; + int minor; + + struct input_handle *handle; + struct input_handler *next; +}; + +struct input_handle { + + void *private; + + int open; + + struct input_dev *dev; + struct input_handler *handler; + + struct input_handle *dnext; + struct input_handle *hnext; +}; + +void input_register_device(struct input_dev *); +void input_unregister_device(struct input_dev *); + +void input_register_handler(struct input_handler *); +void input_unregister_handler(struct input_handler *); + +int input_open_device(struct input_handle *); +void input_close_device(struct input_handle *); + +devfs_handle_t input_register_minor(char *name, int minor, int minor_base); +void input_unregister_minor(devfs_handle_t handle); + +void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); + +#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c)) +#define input_report_rel(a,b,c) input_event(a, EV_REL, b, c) +#define input_report_abs(a,b,c) input_event(a, EV_ABS, b, c) + +#endif +#endif diff --git a/include/linux/joystick.h b/include/linux/joystick.h index b5b39d0f59e3..6dbb24eb2372 100644 --- a/include/linux/joystick.h +++ b/include/linux/joystick.h @@ -137,6 +137,14 @@ struct JS_DATA_SAVE_TYPE { #if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) #include typedef struct wait_queue *wait_queue_head_t; +#define __exit +#ifdef MODULE +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#else +#define module_init(x) int x##_module(void) { return x(); } +#define module_exit(x) void x##_module(void) { x(); } +#endif #define __setup(a,b) #define BASE_ADDRESS(x,i) ((x)->base_address[i]) #define DECLARE_WAITQUEUE(x,y) struct wait_queue x = { y, NULL } diff --git a/include/linux/kbd_kern.h b/include/linux/kbd_kern.h index f5ae9ed93523..9b215fb148bc 100644 --- a/include/linux/kbd_kern.h +++ b/include/linux/kbd_kern.h @@ -69,6 +69,8 @@ extern void setledstate(struct kbd_struct *kbd, unsigned int led); extern int do_poke_blanked_console; +extern void (*kbd_ledfunc)(unsigned int led); + extern inline void show_console(void) { do_poke_blanked_console = 1; diff --git a/include/linux/kcomp.h b/include/linux/kcomp.h new file mode 100644 index 000000000000..98b1ca61921d --- /dev/null +++ b/include/linux/kcomp.h @@ -0,0 +1,96 @@ +/* + * Kernel compatibility glue to allow USB compile on 2.2.x kernels + */ + +#include +#include +#include +#include + +#define LIST_HEAD_INIT(name) { &(name), &(name) } +static __inline__ void list_add_tail(struct list_head *new, struct list_head *head) +{ + __list_add(new, head->prev, head); +} + +typedef struct wait_queue wait_queue_t; +typedef struct wait_queue *wait_queue_head_t; +#define DECLARE_WAITQUEUE(wait, current) struct wait_queue wait = { current, NULL } +#define DECLARE_WAIT_QUEUE_HEAD(wait) wait_queue_head_t wait +#define init_waitqueue_head(x) *(x)=NULL +#define init_waitqueue_entry(q,p) ((q)->task)=(p) + +#define init_MUTEX(x) *(x)=MUTEX +#define init_MUTEX_LOCKED(x) *(x)=MUTEX_LOCKED +#define DECLARE_MUTEX(name) struct semaphore name=MUTEX +#define DECLARE_MUTEX_LOCKED(name) struct semaphore name=MUTEX_LOCKED + +#define __set_current_state(state_value) do { current->state = state_value; } while (0) +#ifdef __SMP__ +#define set_current_state(state_value) do { mb(); __set_current_state(state_value); } while (0) +#else +#define set_current_state(state_value) __set_current_state(state_value) +#endif + +#define __exit + +#ifdef __alpha +extern long __kernel_thread (unsigned long, int (*)(void *), void *); +static inline long kernel_thread (int (*fn) (void *), void *arg, unsigned long flags) +{ + return __kernel_thread (flags | CLONE_VM, fn, arg); +} +#undef CONFIG_APM +#endif + +#define proc_mkdir(buf, usbdir) create_proc_entry(buf, S_IFDIR, usbdir) + +#define pci_enable_device(x) 0 + +#define VID_HARDWARE_CPIA 24 +#define VID_HARDWARE_OV511 27 + +#define page_address(x) (x | PAGE_OFFSET) + +#ifdef MODULE +#define module_init(x) int init_module(void) { return x(); } +#define module_exit(x) void cleanup_module(void) { x(); } +#define THIS_MODULE (&__this_module) +#else +#define module_init(x) int x##_module(void) { return x(); } +#define module_exit(x) void x##_module(void) { x(); } +#define THIS_MODULE NULL +#endif + +#define TTY_DRIVER_NO_DEVFS 0 + +#define __initcall(x) + +#define net_device device +#define dev_kfree_skb_irq(a) dev_kfree_skb(a) +#define netif_wake_queue(dev) clear_bit(0, &dev->tbusy) +#define netif_stop_queue(dev) set_bit(0, &dev->tbusy) +#define netif_start_queue(dev) do { dev->tbusy = 0; dev->interrupt = 0; dev->start = 1; } while (0) +#define netif_queue_stopped(dev) dev->tbusy +#define netif_running(dev) dev->start + +#define NET_XMIT_SUCCESS 0 +#define NET_XMIT_DROP 1 +#define NET_XMIT_CN 2 + +#define tty_register_devfs(driver,flags,minor) +#define tty_unregister_devfs(driver,minor) + +#define DECLARE_FSTYPE(var,type,read,flags) \ +struct file_system_type var = { \ + name: type, \ + read_super: read, \ + fs_flags: flags, \ +} + +#define IORESOURCE_IO 1 +#define pci_resource_start(dev, i) (dev->base_address[i] & ~IORESOURCE_IO) +#define pci_resource_len(dev, i) 0x100 /* FIXME */ +#define pci_resource_flags(dev, i) (dev->base_address[i] & IORESOURCE_IO) + +#define vmalloc_32 vmalloc diff --git a/include/linux/major.h b/include/linux/major.h index f66693edc768..3a7c3cc8baa1 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -109,6 +109,15 @@ #define SPECIALIX_NORMAL_MAJOR 75 #define SPECIALIX_CALLOUT_MAJOR 76 +#define COMPAQ_CISS_MAJOR 104 +#define COMPAQ_CISS_MAJOR1 105 +#define COMPAQ_CISS_MAJOR2 106 +#define COMPAQ_CISS_MAJOR3 107 +#define COMPAQ_CISS_MAJOR4 108 +#define COMPAQ_CISS_MAJOR5 109 +#define COMPAQ_CISS_MAJOR6 110 +#define COMPAQ_CISS_MAJOR7 111 + #define DASD_MAJOR 94 /* Official assignations from Peter */ #define LVM_CHAR_MAJOR 109 /* Logical Volume Manager */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 1a3f64c5256b..b87a9b6f8c77 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -298,6 +298,7 @@ #define PCI_DEVICE_ID_COMPAQ_NETEL100D 0xae40 #define PCI_DEVICE_ID_COMPAQ_NETEL100PI 0xae43 #define PCI_DEVICE_ID_COMPAQ_NETEL100I 0xb011 +#define PCI_DEVICE_ID_COMPAQ_CISS 0xb060 #define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130 #define PCI_DEVICE_ID_COMPAQ_NETFLEX3B 0xf150 @@ -766,6 +767,7 @@ #define PCI_DEVICE_ID_TTI_HPT343 0x0003 #define PCI_VENDOR_ID_VIA 0x1106 +#define PCI_DEVICE_ID_VIA_8371_0 0x0391 #define PCI_DEVICE_ID_VIA_82C505 0x0505 #define PCI_DEVICE_ID_VIA_82C561 0x0561 #define PCI_DEVICE_ID_VIA_82C586_1 0x0571 diff --git a/include/linux/pm.h b/include/linux/pm.h new file mode 100644 index 000000000000..fb77050059c9 --- /dev/null +++ b/include/linux/pm.h @@ -0,0 +1,88 @@ +#ifndef __LINUX_PM_H +#define __LINUX_PM_H + + +typedef int pm_request_t; + +struct pm_dev { + void *data; + struct pm_dev *next; + int (*event)(struct pm_dev *, pm_request_t, void *); +}; + +#define PM_SUSPEND 1 +#define PM_RESUME 2 + +#ifdef CONFIG_APM + +#include + +#define PM_PCI_DEV 0 +#define PM_PCI_ID(a) a + +static struct pm_dev *pm_dev = NULL; + +static int handle_apm_event(apm_event_t apm_event) +{ + struct pm_dev *list = pm_dev; + pm_request_t action; + + switch (apm_event) { + + case APM_SYS_SUSPEND: + case APM_CRITICAL_SUSPEND: + case APM_USER_SUSPEND: + action = PM_SUSPEND; + break; + case APM_NORMAL_RESUME: + case APM_CRITICAL_RESUME: + case APM_STANDBY_RESUME: + action = PM_RESUME; + break; + default: + return 0; + } + + while (list) { + list->event(list, action, list->data); + list = list->next; + } + + return 0; +} + +static struct pm_dev *pm_register(int a, void *b, int (*event)(struct pm_dev *, pm_request_t, void *)) +{ + struct pm_dev *list; + + list = kmalloc(sizeof(struct pm_dev), GFP_KERNEL); + list->event = event; + list->next = pm_dev; + pm_dev = list; + + apm_register_callback(handle_apm_event); + + return list; +} + +static void pm_unregister_all(int (*event)(struct pm_dev *, pm_request_t, void *)) +{ + struct pm_dev *list; + + while (pm_dev) { + list = pm_dev; + pm_dev = pm_dev->next; + kfree(list); + } + + apm_unregister_callback(handle_apm_event); +} + +#else + +#define pm_register(a,b,c) 0 +#define pm_unregister_all(a) do {} while (0) + +#endif + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index a20124be73e0..52bf90605167 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -688,6 +688,8 @@ extern void exit_fs(struct task_struct *); extern void exit_files(struct task_struct *); extern void exit_sighand(struct task_struct *); +extern void daemonize(void); + extern int do_execve(char *, char **, char **, struct pt_regs *); extern int do_fork(unsigned long, unsigned long, struct pt_regs *); diff --git a/include/linux/slab.h b/include/linux/slab.h index 0b46d3b735ab..3097a8db26f2 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -51,6 +51,7 @@ extern kmem_cache_t *kmem_find_general_cachep(size_t); extern kmem_cache_t *kmem_cache_create(const char *, size_t, size_t, unsigned long, void (*)(void *, kmem_cache_t *, unsigned long), void (*)(void *, kmem_cache_t *, unsigned long)); +extern int kmem_cache_destroy(kmem_cache_t *); extern int kmem_cache_shrink(kmem_cache_t *); extern void *kmem_cache_alloc(kmem_cache_t *, int); extern void kmem_cache_free(kmem_cache_t *, void *); diff --git a/include/linux/spinlock.h b/include/linux/spinlock.h new file mode 100644 index 000000000000..790ac18cb959 --- /dev/null +++ b/include/linux/spinlock.h @@ -0,0 +1,4 @@ +#ifndef __LINUX_SPINLOCK_H +#define __LINUX_SPINLOCK_H +#include +#endif diff --git a/include/linux/usb.h b/include/linux/usb.h new file mode 100644 index 000000000000..398aed5bce16 --- /dev/null +++ b/include/linux/usb.h @@ -0,0 +1,786 @@ +#ifndef __LINUX_USB_H +#define __LINUX_USB_H + +/* USB constants */ + +/* + * Device and/or Interface Class codes + */ +#define USB_CLASS_PER_INTERFACE 0 /* for DeviceClass */ +#define USB_CLASS_AUDIO 1 +#define USB_CLASS_COMM 2 +#define USB_CLASS_HID 3 +#define USB_CLASS_PRINTER 7 +#define USB_CLASS_MASS_STORAGE 8 +#define USB_CLASS_HUB 9 +#define USB_CLASS_DATA 10 +#define USB_CLASS_VENDOR_SPEC 0xff + +/* + * USB types + */ +#define USB_TYPE_STANDARD (0x00 << 5) +#define USB_TYPE_CLASS (0x01 << 5) +#define USB_TYPE_VENDOR (0x02 << 5) +#define USB_TYPE_RESERVED (0x03 << 5) + +/* + * USB recipients + */ +#define USB_RECIP_DEVICE 0x00 +#define USB_RECIP_INTERFACE 0x01 +#define USB_RECIP_ENDPOINT 0x02 +#define USB_RECIP_OTHER 0x03 + +/* + * USB directions + */ +#define USB_DIR_OUT 0 +#define USB_DIR_IN 0x80 + +/* + * Descriptor types + */ +#define USB_DT_DEVICE 0x01 +#define USB_DT_CONFIG 0x02 +#define USB_DT_STRING 0x03 +#define USB_DT_INTERFACE 0x04 +#define USB_DT_ENDPOINT 0x05 + +#define USB_DT_HID (USB_TYPE_CLASS | 0x01) +#define USB_DT_REPORT (USB_TYPE_CLASS | 0x02) +#define USB_DT_PHYSICAL (USB_TYPE_CLASS | 0x03) +#define USB_DT_HUB (USB_TYPE_CLASS | 0x09) + +/* + * Descriptor sizes per descriptor type + */ +#define USB_DT_DEVICE_SIZE 18 +#define USB_DT_CONFIG_SIZE 9 +#define USB_DT_INTERFACE_SIZE 9 +#define USB_DT_ENDPOINT_SIZE 7 +#define USB_DT_ENDPOINT_AUDIO_SIZE 9 /* Audio extension */ +#define USB_DT_HUB_NONVAR_SIZE 7 +#define USB_DT_HID_SIZE 9 + +/* + * Endpoints + */ +#define USB_ENDPOINT_NUMBER_MASK 0x0f /* in bEndpointAddress */ +#define USB_ENDPOINT_DIR_MASK 0x80 + +#define USB_ENDPOINT_XFERTYPE_MASK 0x03 /* in bmAttributes */ +#define USB_ENDPOINT_XFER_CONTROL 0 +#define USB_ENDPOINT_XFER_ISOC 1 +#define USB_ENDPOINT_XFER_BULK 2 +#define USB_ENDPOINT_XFER_INT 3 + +/* + * USB Packet IDs (PIDs) + */ +#define USB_PID_UNDEF_0 0xf0 +#define USB_PID_OUT 0xe1 +#define USB_PID_ACK 0xd2 +#define USB_PID_DATA0 0xc3 +#define USB_PID_PING 0xb4 /* USB 2.0 */ +#define USB_PID_SOF 0xa5 +#define USB_PID_NYET 0x96 /* USB 2.0 */ +#define USB_PID_DATA2 0x87 /* USB 2.0 */ +#define USB_PID_SPLIT 0x78 /* USB 2.0 */ +#define USB_PID_IN 0x69 +#define USB_PID_NAK 0x5a +#define USB_PID_DATA1 0x4b +#define USB_PID_PREAMBLE 0x3c /* Token mode */ +#define USB_PID_ERR 0x3c /* USB 2.0: handshake mode */ +#define USB_PID_SETUP 0x2d +#define USB_PID_STALL 0x1e +#define USB_PID_MDATA 0x0f /* USB 2.0 */ + +/* + * Standard requests + */ +#define USB_REQ_GET_STATUS 0x00 +#define USB_REQ_CLEAR_FEATURE 0x01 +#define USB_REQ_SET_FEATURE 0x03 +#define USB_REQ_SET_ADDRESS 0x05 +#define USB_REQ_GET_DESCRIPTOR 0x06 +#define USB_REQ_SET_DESCRIPTOR 0x07 +#define USB_REQ_GET_CONFIGURATION 0x08 +#define USB_REQ_SET_CONFIGURATION 0x09 +#define USB_REQ_GET_INTERFACE 0x0A +#define USB_REQ_SET_INTERFACE 0x0B +#define USB_REQ_SYNCH_FRAME 0x0C + +/* + * HID requests + */ +#define USB_REQ_GET_REPORT 0x01 +#define USB_REQ_GET_IDLE 0x02 +#define USB_REQ_GET_PROTOCOL 0x03 +#define USB_REQ_SET_REPORT 0x09 +#define USB_REQ_SET_IDLE 0x0A +#define USB_REQ_SET_PROTOCOL 0x0B + + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include +#include +#include /* for in_interrupt() */ +#include +#include + +#define USB_MAJOR 180 + +static __inline__ void wait_ms(unsigned int ms) +{ + if(!in_interrupt()) { + current->state = TASK_UNINTERRUPTIBLE; + schedule_timeout(1 + ms * HZ / 1000); + } + else + mdelay(ms); +} + +typedef struct { + __u8 requesttype; + __u8 request; + __u16 value; + __u16 index; + __u16 length; +} devrequest __attribute__ ((packed)); + +/* + * USB-status codes: + * USB_ST* maps to -E* and should go away in the future + */ + +#define USB_ST_NOERROR 0 +#define USB_ST_CRC (-EILSEQ) +#define USB_ST_BITSTUFF (-EPROTO) +#define USB_ST_NORESPONSE (-ETIMEDOUT) /* device not responding/handshaking */ +#define USB_ST_DATAOVERRUN (-EOVERFLOW) +#define USB_ST_DATAUNDERRUN (-EREMOTEIO) +#define USB_ST_BUFFEROVERRUN (-ECOMM) +#define USB_ST_BUFFERUNDERRUN (-ENOSR) +#define USB_ST_INTERNALERROR (-EPROTO) /* unknown error */ +#define USB_ST_SHORT_PACKET (-EREMOTEIO) +#define USB_ST_PARTIAL_ERROR (-EXDEV) /* ISO transfer only partially completed */ +#define USB_ST_URB_KILLED (-ENOENT) /* URB canceled by user */ +#define USB_ST_URB_PENDING (-EINPROGRESS) +#define USB_ST_REMOVED (-ENODEV) /* device not existing or removed */ +#define USB_ST_TIMEOUT (-ETIMEDOUT) /* communication timed out, also in urb->status**/ +#define USB_ST_NOTSUPPORTED (-ENOSYS) +#define USB_ST_BANDWIDTH_ERROR (-ENOSPC) /* too much bandwidth used */ +#define USB_ST_URB_INVALID_ERROR (-EINVAL) /* invalid value/transfer type */ +#define USB_ST_URB_REQUEST_ERROR (-ENXIO) /* invalid endpoint */ +#define USB_ST_STALL (-EPIPE) /* pipe stalled, also in urb->status*/ + +/* + * USB device number allocation bitmap. There's one bitmap + * per USB tree. + */ +struct usb_devmap { + unsigned long devicemap[128 / (8*sizeof(unsigned long))]; +}; + +#define USB_MAXBUS 64 + +struct usb_busmap { + unsigned long busmap[USB_MAXBUS / (8*sizeof(unsigned long))]; +}; + +/* + * This is a USB device descriptor. + * + * USB device information + */ + +/* Everything but the endpoint maximums are aribtrary */ +#define USB_MAXCONFIG 8 +#define USB_ALTSETTINGALLOC 4 +#define USB_MAXALTSETTING 128 /* Hard limit */ +#define USB_MAXINTERFACES 32 +#define USB_MAXENDPOINTS 32 + +/* All standard descriptors have these 2 fields in common */ +struct usb_descriptor_header { + __u8 bLength; + __u8 bDescriptorType; +} __attribute__ ((packed)); + +/* Device descriptor */ +struct usb_device_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u16 bcdUSB; + __u8 bDeviceClass; + __u8 bDeviceSubClass; + __u8 bDeviceProtocol; + __u8 bMaxPacketSize0; + __u16 idVendor; + __u16 idProduct; + __u16 bcdDevice; + __u8 iManufacturer; + __u8 iProduct; + __u8 iSerialNumber; + __u8 bNumConfigurations; +} __attribute__ ((packed)); + +/* Endpoint descriptor */ +struct usb_endpoint_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bEndpointAddress; + __u8 bmAttributes; + __u16 wMaxPacketSize; + __u8 bInterval; + __u8 bRefresh; + __u8 bSynchAddress; + + unsigned char *extra; /* Extra descriptors */ + int extralen; +} __attribute__ ((packed)); + +/* Interface descriptor */ +struct usb_interface_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u8 bInterfaceNumber; + __u8 bAlternateSetting; + __u8 bNumEndpoints; + __u8 bInterfaceClass; + __u8 bInterfaceSubClass; + __u8 bInterfaceProtocol; + __u8 iInterface; + + struct usb_endpoint_descriptor *endpoint; + + unsigned char *extra; + int extralen; +} __attribute__ ((packed)); + +struct usb_interface { + struct usb_interface_descriptor *altsetting; + + int act_altsetting; /* active alternate setting */ + int num_altsetting; /* number of alternate settings */ + int max_altsetting; /* total memory allocated */ + + struct usb_driver *driver; /* driver */ + void *private_data; +}; + +/* Configuration descriptor information.. */ +struct usb_config_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u16 wTotalLength; + __u8 bNumInterfaces; + __u8 bConfigurationValue; + __u8 iConfiguration; + __u8 bmAttributes; + __u8 MaxPower; + + struct usb_interface *interface; +} __attribute__ ((packed)); + +/* String descriptor */ +struct usb_string_descriptor { + __u8 bLength; + __u8 bDescriptorType; + __u16 wData[1]; +} __attribute__ ((packed)); + +struct usb_device; + +struct usb_driver { + const char *name; + + void * (*probe)(struct usb_device *, unsigned int); + void (*disconnect)(struct usb_device *, void *); + + struct list_head driver_list; + + struct file_operations *fops; + int minor; + + struct semaphore serialize; + + int (*ioctl)(struct usb_device *dev, unsigned int code, void *buf); +}; + +/* + * Pointer to a device endpoint interrupt function -greg + * Parameters: + * int status - This needs to be defined. Right now each HCD + * passes different transfer status bits back. Don't use it + * until we come up with a common meaning. + * void *buffer - This is a pointer to the data used in this + * USB transfer. + * int length - This is the number of bytes transferred in or out + * of the buffer by this transfer. (-1 = unknown/unsupported) + * void *dev_id - This is a user defined pointer set when the IRQ + * is requested that is passed back. + * + * Special Cases: + * if (status == USB_ST_REMOVED), don't trust buffer or len. + */ +typedef int (*usb_device_irq)(int, void *, int, void *); + +/*----------------------------------------------------------------------------* + * New USB Structures * + *----------------------------------------------------------------------------*/ + +/* + * urb->transfer_flags: + */ +#define USB_DISABLE_SPD 0x0001 +#define USB_ISO_ASAP 0x0002 +#define USB_URB_EARLY_COMPLETE 0x0004 +#define USB_ASYNC_UNLINK 0x0008 +#define USB_QUEUE_BULK 0x0010 +#define USB_TIMEOUT_KILLED 0x1000 // only set by HCD! + +typedef struct +{ + unsigned int offset; + unsigned int length; // expected length + unsigned int actual_length; + unsigned int status; +} iso_packet_descriptor_t, *piso_packet_descriptor_t; + +struct urb; +typedef void (*usb_complete_t)(struct urb *); + +typedef struct urb +{ + spinlock_t lock; // lock for the URB + void *hcpriv; // private data for host controller + struct list_head urb_list; // list pointer to all active urbs + struct urb *next; // pointer to next URB + struct usb_device *dev; // pointer to associated USB device + unsigned int pipe; // pipe information + int status; // returned status + unsigned int transfer_flags; // USB_DISABLE_SPD | USB_ISO_ASAP | USB_URB_EARLY_COMPLETE + void *transfer_buffer; // associated data buffer + int transfer_buffer_length; // data buffer length + int actual_length; // actual data buffer length + int bandwidth; // bandwidth for this transfer request (INT or ISO) + unsigned char *setup_packet; // setup packet (control only) + // + int start_frame; // start frame (iso/irq only) + int number_of_packets; // number of packets in this request (iso) + int interval; // polling interval (irq only) + int error_count; // number of errors in this transfer (iso only) + int timeout; // timeout (in jiffies) + // + void *context; // context for completion routine + usb_complete_t complete; // pointer to completion routine + // + iso_packet_descriptor_t iso_frame_desc[0]; +} urb_t, *purb_t; + +#define FILL_CONTROL_URB(a,aa,b,c,d,e,f,g) \ + do {\ + spin_lock_init(&(a)->lock);\ + (a)->dev=aa;\ + (a)->pipe=b;\ + (a)->setup_packet=c;\ + (a)->transfer_buffer=d;\ + (a)->transfer_buffer_length=e;\ + (a)->complete=f;\ + (a)->context=g;\ + } while (0) + +#define FILL_BULK_URB(a,aa,b,c,d,e,f) \ + do {\ + spin_lock_init(&(a)->lock);\ + (a)->dev=aa;\ + (a)->pipe=b;\ + (a)->transfer_buffer=c;\ + (a)->transfer_buffer_length=d;\ + (a)->complete=e;\ + (a)->context=f;\ + } while (0) + +#define FILL_INT_URB(a,aa,b,c,d,e,f,g) \ + do {\ + spin_lock_init(&(a)->lock);\ + (a)->dev=aa;\ + (a)->pipe=b;\ + (a)->transfer_buffer=c;\ + (a)->transfer_buffer_length=d;\ + (a)->complete=e;\ + (a)->context=f;\ + (a)->interval=g;\ + (a)->start_frame=-1;\ + } while (0) + +#define FILL_CONTROL_URB_TO(a,aa,b,c,d,e,f,g,h) \ + do {\ + spin_lock_init(&(a)->lock);\ + (a)->dev=aa;\ + (a)->pipe=b;\ + (a)->setup_packet=c;\ + (a)->transfer_buffer=d;\ + (a)->transfer_buffer_length=e;\ + (a)->complete=f;\ + (a)->context=g;\ + (a)->timeout=h;\ + } while (0) + +#define FILL_BULK_URB_TO(a,aa,b,c,d,e,f,g) \ + do {\ + spin_lock_init(&(a)->lock);\ + (a)->dev=aa;\ + (a)->pipe=b;\ + (a)->transfer_buffer=c;\ + (a)->transfer_buffer_length=d;\ + (a)->complete=e;\ + (a)->context=f;\ + (a)->timeout=g;\ + } while (0) + +purb_t usb_alloc_urb(int iso_packets); +void usb_free_urb (purb_t purb); +int usb_submit_urb(purb_t purb); +int usb_unlink_urb(purb_t purb); +int usb_internal_control_msg(struct usb_device *usb_dev, unsigned int pipe, devrequest *cmd, void *data, int len, int timeout); +int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe, void *data, int len, int *actual_length, int timeout); + +/*-------------------------------------------------------------------* + * COMPATIBILITY STUFF * + *-------------------------------------------------------------------*/ +typedef struct +{ + wait_queue_head_t *wakeup; + + usb_device_irq handler; + void* stuff; + /* more to follow */ +} api_wrapper_data; + +struct irq_wrapper_data { + void *context; + usb_device_irq handler; +}; + +/* -------------------------------------------------------------------------- */ + +struct usb_operations { + int (*allocate)(struct usb_device *); + int (*deallocate)(struct usb_device *); + int (*get_frame_number) (struct usb_device *usb_dev); + int (*submit_urb) (struct urb* purb); + int (*unlink_urb) (struct urb* purb); +}; + +/* + * Allocated per bus we have + */ +struct usb_bus { + int busnum; /* Bus number (in order of reg) */ + + struct usb_devmap devmap; /* Device map */ + struct usb_operations *op; /* Operations (specific to the HC) */ + struct usb_device *root_hub; /* Root hub */ + struct list_head bus_list; + void *hcpriv; /* Host Controller private data */ + + int bandwidth_allocated; /* on this Host Controller; */ + /* applies to Int. and Isoc. pipes; */ + /* measured in microseconds/frame; */ + /* range is 0..900, where 900 = */ + /* 90% of a 1-millisecond frame */ + int bandwidth_int_reqs; /* number of Interrupt requesters */ + int bandwidth_isoc_reqs; /* number of Isoc. requesters */ + + /* usbdevfs inode list */ + struct list_head inodes; +}; + +#define USB_MAXCHILDREN (8) /* This is arbitrary */ + +struct usb_device { + int devnum; /* Device number on USB bus */ + int slow; /* Slow device? */ + + atomic_t refcnt; /* Reference count */ + + unsigned int toggle[2]; /* one bit for each endpoint ([0] = IN, [1] = OUT) */ + unsigned int halted[2]; /* endpoint halts; one bit per endpoint # & direction; */ + /* [0] = IN, [1] = OUT */ + int epmaxpacketin[16]; /* INput endpoint specific maximums */ + int epmaxpacketout[16]; /* OUTput endpoint specific maximums */ + + struct usb_device *parent; + struct usb_bus *bus; /* Bus we're part of */ + + struct usb_device_descriptor descriptor;/* Descriptor */ + struct usb_config_descriptor *config; /* All of the configs */ + struct usb_config_descriptor *actconfig;/* the active configuration */ + + char **rawdescriptors; /* Raw descriptors for each config */ + + int have_langid; /* whether string_langid is valid yet */ + int string_langid; /* language ID for strings */ + + void *hcpriv; /* Host Controller private data */ + + /* usbdevfs inode list */ + struct list_head inodes; + struct list_head filelist; + + /* + * Child devices - these can be either new devices + * (if this is a hub device), or different instances + * of this same device. + * + * Each instance needs its own set of data structures. + */ + + int maxchild; /* Number of ports if hub */ + struct usb_device *children[USB_MAXCHILDREN]; +}; + +extern struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum); + +extern int usb_register(struct usb_driver *); +extern void usb_deregister(struct usb_driver *); + +/* used these for multi-interface device registration */ +extern void usb_driver_claim_interface(struct usb_driver *driver, struct usb_interface *iface, void* priv); +extern int usb_interface_claimed(struct usb_interface *iface); +extern void usb_driver_release_interface(struct usb_driver *driver, struct usb_interface *iface); + +extern struct usb_bus *usb_alloc_bus(struct usb_operations *); +extern void usb_free_bus(struct usb_bus *); +extern void usb_register_bus(struct usb_bus *); +extern void usb_deregister_bus(struct usb_bus *); + +extern struct usb_device *usb_alloc_dev(struct usb_device *parent, struct usb_bus *); +extern void usb_free_dev(struct usb_device *); +extern void usb_inc_dev_use(struct usb_device *); +#define usb_dec_dev_use usb_free_dev + +extern int usb_check_bandwidth (struct usb_device *dev, struct urb *urb); +extern void usb_claim_bandwidth (struct usb_device *dev, struct urb *urb, int bustime, int isoc); +extern void usb_release_bandwidth(struct usb_device *dev, struct urb *urb, int isoc); + +extern int usb_control_msg(struct usb_device *dev, unsigned int pipe, __u8 request, __u8 requesttype, __u16 value, __u16 index, void *data, __u16 size, int timeout); + +extern void usb_init_root_hub(struct usb_device *dev); +extern int usb_root_hub_string(int id, int serial, char *type, __u8 *data, int len); +extern void usb_connect(struct usb_device *dev); +extern void usb_disconnect(struct usb_device **); + +extern void usb_destroy_configuration(struct usb_device *dev); + +int usb_get_current_frame_number (struct usb_device *usb_dev); + +/* + * Calling this entity a "pipe" is glorifying it. A USB pipe + * is something embarrassingly simple: it basically consists + * of the following information: + * - device number (7 bits) + * - endpoint number (4 bits) + * - current Data0/1 state (1 bit) + * - direction (1 bit) + * - speed (1 bit) + * - max packet size (2 bits: 8, 16, 32 or 64) [Historical; now gone.] + * - pipe type (2 bits: control, interrupt, bulk, isochronous) + * + * That's 18 bits. Really. Nothing more. And the USB people have + * documented these eighteen bits as some kind of glorious + * virtual data structure. + * + * Let's not fall in that trap. We'll just encode it as a simple + * unsigned int. The encoding is: + * + * - max size: bits 0-1 (00 = 8, 01 = 16, 10 = 32, 11 = 64) [Historical; now gone.] + * - direction: bit 7 (0 = Host-to-Device [Out], 1 = Device-to-Host [In]) + * - device: bits 8-14 + * - endpoint: bits 15-18 + * - Data0/1: bit 19 + * - speed: bit 26 (0 = Full, 1 = Low Speed) + * - pipe type: bits 30-31 (00 = isochronous, 01 = interrupt, 10 = control, 11 = bulk) + * + * Why? Because it's arbitrary, and whatever encoding we select is really + * up to us. This one happens to share a lot of bit positions with the UHCI + * specification, so that much of the uhci driver can just mask the bits + * appropriately. + */ + +#define PIPE_ISOCHRONOUS 0 +#define PIPE_INTERRUPT 1 +#define PIPE_CONTROL 2 +#define PIPE_BULK 3 + +#define usb_maxpacket(dev, pipe, out) (out \ + ? (dev)->epmaxpacketout[usb_pipeendpoint(pipe)] \ + : (dev)->epmaxpacketin [usb_pipeendpoint(pipe)] ) +#define usb_packetid(pipe) (((pipe) & USB_DIR_IN) ? USB_PID_IN : USB_PID_OUT) + +#define usb_pipeout(pipe) ((((pipe) >> 7) & 1) ^ 1) +#define usb_pipein(pipe) (((pipe) >> 7) & 1) +#define usb_pipedevice(pipe) (((pipe) >> 8) & 0x7f) +#define usb_pipe_endpdev(pipe) (((pipe) >> 8) & 0x7ff) +#define usb_pipeendpoint(pipe) (((pipe) >> 15) & 0xf) +#define usb_pipedata(pipe) (((pipe) >> 19) & 1) +#define usb_pipeslow(pipe) (((pipe) >> 26) & 1) +#define usb_pipetype(pipe) (((pipe) >> 30) & 3) +#define usb_pipeisoc(pipe) (usb_pipetype((pipe)) == PIPE_ISOCHRONOUS) +#define usb_pipeint(pipe) (usb_pipetype((pipe)) == PIPE_INTERRUPT) +#define usb_pipecontrol(pipe) (usb_pipetype((pipe)) == PIPE_CONTROL) +#define usb_pipebulk(pipe) (usb_pipetype((pipe)) == PIPE_BULK) + +#define PIPE_DEVEP_MASK 0x0007ff00 + +/* The D0/D1 toggle bits */ +#define usb_gettoggle(dev, ep, out) (((dev)->toggle[out] >> ep) & 1) +#define usb_dotoggle(dev, ep, out) ((dev)->toggle[out] ^= (1 << ep)) +#define usb_settoggle(dev, ep, out, bit) ((dev)->toggle[out] = ((dev)->toggle[out] & ~(1 << ep)) | ((bit) << ep)) + +/* Endpoint halt control/status */ +#define usb_endpoint_out(ep_dir) (((ep_dir >> 7) & 1) ^ 1) +#define usb_endpoint_halt(dev, ep, out) ((dev)->halted[out] |= (1 << (ep))) +#define usb_endpoint_running(dev, ep, out) ((dev)->halted[out] &= ~(1 << (ep))) +#define usb_endpoint_halted(dev, ep, out) ((dev)->halted[out] & (1 << (ep))) + +static inline unsigned int __create_pipe(struct usb_device *dev, unsigned int endpoint) +{ + return (dev->devnum << 8) | (endpoint << 15) | (dev->slow << 26); +} + +static inline unsigned int __default_pipe(struct usb_device *dev) +{ + return (dev->slow << 26); +} + +/* Create various pipes... */ +#define usb_sndctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvctrlpipe(dev,endpoint) ((PIPE_CONTROL << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#define usb_sndisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvisocpipe(dev,endpoint) ((PIPE_ISOCHRONOUS << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#define usb_sndbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvbulkpipe(dev,endpoint) ((PIPE_BULK << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#define usb_sndintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint)) +#define usb_rcvintpipe(dev,endpoint) ((PIPE_INTERRUPT << 30) | __create_pipe(dev,endpoint) | USB_DIR_IN) +#define usb_snddefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev)) +#define usb_rcvdefctrl(dev) ((PIPE_CONTROL << 30) | __default_pipe(dev) | USB_DIR_IN) + +/* + * Send and receive control messages.. + */ +int usb_new_device(struct usb_device *dev); +int usb_reset_device(struct usb_device *dev); +int usb_set_address(struct usb_device *dev); +int usb_get_descriptor(struct usb_device *dev, unsigned char desctype, + unsigned char descindex, void *buf, int size); +int usb_get_class_descriptor(struct usb_device *dev, int ifnum, unsigned char desctype, + unsigned char descindex, void *buf, int size); +int usb_get_device_descriptor(struct usb_device *dev); +int __usb_get_extra_descriptor(char *buffer, unsigned size, unsigned char type, void **ptr); +int usb_get_status(struct usb_device *dev, int type, int target, void *data); +int usb_get_configuration(struct usb_device *dev); +int usb_get_protocol(struct usb_device *dev, int ifnum); +int usb_set_protocol(struct usb_device *dev, int ifnum, int protocol); +int usb_set_interface(struct usb_device *dev, int ifnum, int alternate); +int usb_set_idle(struct usb_device *dev, int ifnum, int duration, int report_id); +int usb_set_configuration(struct usb_device *dev, int configuration); +int usb_get_report(struct usb_device *dev, int ifnum, unsigned char type, + unsigned char id, void *buf, int size); +int usb_set_report(struct usb_device *dev, int ifnum, unsigned char type, + unsigned char id, void *buf, int size); +int usb_string(struct usb_device *dev, int index, char *buf, size_t size); +int usb_clear_halt(struct usb_device *dev, int pipe); +void usb_set_maxpacket(struct usb_device *dev); + +#define usb_get_extra_descriptor(ifpoint,type,ptr)\ + __usb_get_extra_descriptor((ifpoint)->extra,(ifpoint)->extralen,type,(void**)ptr) + +/* + * Some USB bandwidth allocation constants. + */ +#define BW_HOST_DELAY 1000L /* nanoseconds */ +#define BW_HUB_LS_SETUP 333L /* nanoseconds */ + /* 4 full-speed bit times (est.) */ + +#define FRAME_TIME_BITS 12000L /* frame = 1 millisecond */ +#define FRAME_TIME_MAX_BITS_ALLOC (90L * FRAME_TIME_BITS / 100L) +#define FRAME_TIME_USECS 1000L +#define FRAME_TIME_MAX_USECS_ALLOC (90L * FRAME_TIME_USECS / 100L) + +#define BitTime(bytecount) (7 * 8 * bytecount / 6) /* with integer truncation */ + /* Trying not to use worst-case bit-stuffing + of (7/6 * 8 * bytecount) = 9.33 * bytecount */ + /* bytecount = data payload byte count */ + +#define NS_TO_US(ns) ((ns + 500L) / 1000L) + /* convert & round nanoseconds to microseconds */ + +/* + * Debugging helpers.. + */ +void usb_show_device_descriptor(struct usb_device_descriptor *); +void usb_show_config_descriptor(struct usb_config_descriptor *); +void usb_show_interface_descriptor(struct usb_interface_descriptor *); +void usb_show_endpoint_descriptor(struct usb_endpoint_descriptor *); +void usb_show_device(struct usb_device *); +void usb_show_string(struct usb_device *dev, char *id, int index); + +#ifdef DEBUG +#define dbg(format, arg...) printk(KERN_DEBUG __FILE__ ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) do {} while (0) +#endif +#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg) + + +/* + * bus and driver list + */ + +extern struct list_head usb_driver_list; +extern struct list_head usb_bus_list; + +/* + * USB device fs stuff + */ + +#ifdef CONFIG_USB_DEVICEFS + +/* + * these are expected to be called from the USB core/hub thread + * with the kernel lock held + */ +extern void usbdevfs_add_bus(struct usb_bus *bus); +extern void usbdevfs_remove_bus(struct usb_bus *bus); +extern void usbdevfs_add_device(struct usb_device *dev); +extern void usbdevfs_remove_device(struct usb_device *dev); + +extern int usbdevfs_init(void); +extern void usbdevfs_cleanup(void); + +#else /* CONFIG_USB_DEVICEFS */ + +extern inline void usbdevfs_add_bus(struct usb_bus *bus) {} +extern inline void usbdevfs_remove_bus(struct usb_bus *bus) {} +extern inline void usbdevfs_add_device(struct usb_device *dev) {} +extern inline void usbdevfs_remove_device(struct usb_device *dev) {} + +extern inline int usbdevfs_init(void) { return 0; } +extern inline void usbdevfs_cleanup(void) { } + +#endif /* CONFIG_USB_DEVICEFS */ + +#endif /* __KERNEL__ */ + +#endif diff --git a/include/linux/usbdev_fs_i.h b/include/linux/usbdev_fs_i.h new file mode 100644 index 000000000000..13bfad5406bd --- /dev/null +++ b/include/linux/usbdev_fs_i.h @@ -0,0 +1,11 @@ +struct usb_device; +struct usb_bus; + +struct usbdev_inode_info { + struct list_head dlist; + struct list_head slist; + union { + struct usb_device *dev; + struct usb_bus *bus; + } p; +}; diff --git a/include/linux/usbdev_fs_sb.h b/include/linux/usbdev_fs_sb.h new file mode 100644 index 000000000000..7e161efd8caf --- /dev/null +++ b/include/linux/usbdev_fs_sb.h @@ -0,0 +1,10 @@ +struct usbdev_sb_info { + struct list_head slist; + struct list_head ilist; + uid_t devuid; + gid_t devgid; + umode_t devmode; + uid_t busuid; + gid_t busgid; + umode_t busmode; +}; diff --git a/include/linux/usbdevice_fs.h b/include/linux/usbdevice_fs.h new file mode 100644 index 000000000000..0bf8d308722d --- /dev/null +++ b/include/linux/usbdevice_fs.h @@ -0,0 +1,203 @@ +/*****************************************************************************/ + +/* + * usbdevice_fs.h -- USB device file system. + * + * Copyright (C) 2000 + * Thomas Sailer (sailer@ife.ee.ethz.ch) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * History: + * 0.1 04.01.2000 Created + * + * $Id: usbdevice_fs.h,v 1.1 2000/01/06 18:40:41 tom Exp $ + */ + +/*****************************************************************************/ + +#ifndef _LINUX_USBDEVICE_FS_H +#define _LINUX_USBDEVICE_FS_H + +#include + +/* --------------------------------------------------------------------- */ + +#define USBDEVICE_SUPER_MAGIC 0x9fa2 + +/* usbdevfs ioctl codes */ + +struct usbdevfs_ctrltransfer { + __u8 requesttype; + __u8 request; + __u16 value; + __u16 index; + __u16 length; + __u32 timeout; /* in milliseconds */ + void *data; +}; + +struct usbdevfs_bulktransfer { + unsigned int ep; + unsigned int len; + unsigned int timeout; /* in milliseconds */ + void *data; +}; + +struct usbdevfs_setinterface { + unsigned int interface; + unsigned int altsetting; +}; + +struct usbdevfs_disconnectsignal { + unsigned int signr; + void *context; +}; + +#define USBDEVFS_MAXDRIVERNAME 255 + +struct usbdevfs_getdriver { + unsigned int interface; + char driver[USBDEVFS_MAXDRIVERNAME + 1]; +}; + +struct usbdevfs_connectinfo { + unsigned int devnum; + unsigned char slow; +}; + +#define USBDEVFS_URB_DISABLE_SPD 1 +#define USBDEVFS_URB_ISO_ASAP 2 + +#define USBDEVFS_URB_TYPE_ISO 0 +#define USBDEVFS_URB_TYPE_INTERRUPT 1 +#define USBDEVFS_URB_TYPE_CONTROL 2 +#define USBDEVFS_URB_TYPE_BULK 3 + +struct usbdevfs_iso_packet_desc { + unsigned int length; + unsigned int actual_length; + unsigned int status; +}; + +struct usbdevfs_urb { + unsigned char type; + unsigned char endpoint; + int status; + unsigned int flags; + void *buffer; + int buffer_length; + int actual_length; + int start_frame; + int number_of_packets; + int error_count; + unsigned int signr; /* signal to be sent on error, -1 if none should be sent */ + void *usercontext; + struct usbdevfs_iso_packet_desc iso_frame_desc[0]; +}; + +/* ioctls for talking to drivers in the usbcore module: */ +struct usbdevfs_ioctl { + int ifno; /* interface 0..N ; negative numbers reserved */ + int ioctl_code; /* MUST encode size + direction of data so the + * macros in give correct values */ + void *data; /* param buffer (in, or out) */ +}; + +/* You can do most things with hubs just through control messages, + * except find out what device connects to what port. */ +struct usbdevfs_hub_portinfo { + char nports; /* number of downstream ports in this hub */ + char port [127]; /* e.g. port 3 connects to device 27 */ +}; + +#define USBDEVFS_CONTROL _IOWR('U', 0, struct usbdevfs_ctrltransfer) +#define USBDEVFS_BULK _IOWR('U', 2, struct usbdevfs_bulktransfer) +#define USBDEVFS_RESETEP _IOR('U', 3, unsigned int) +#define USBDEVFS_SETINTERFACE _IOR('U', 4, struct usbdevfs_setinterface) +#define USBDEVFS_SETCONFIGURATION _IOR('U', 5, unsigned int) +#define USBDEVFS_GETDRIVER _IOW('U', 8, struct usbdevfs_getdriver) +#define USBDEVFS_SUBMITURB _IOR('U', 10, struct usbdevfs_urb) +#define USBDEVFS_DISCARDURB _IO('U', 11) +#define USBDEVFS_REAPURB _IOW('U', 12, void *) +#define USBDEVFS_REAPURBNDELAY _IOW('U', 13, void *) +#define USBDEVFS_DISCSIGNAL _IOR('U', 14, struct usbdevfs_disconnectsignal) +#define USBDEVFS_CLAIMINTERFACE _IOR('U', 15, unsigned int) +#define USBDEVFS_RELEASEINTERFACE _IOR('U', 16, unsigned int) +#define USBDEVFS_CONNECTINFO _IOW('U', 17, struct usbdevfs_connectinfo) +#define USBDEVFS_IOCTL _IOWR('U', 18, struct usbdevfs_ioctl) +#define USBDEVFS_HUB_PORTINFO _IOR('U', 19, struct usbdevfs_hub_portinfo) +#define USBDEVFS_RESET _IO('U', 20) + +/* --------------------------------------------------------------------- */ + +#ifdef __KERNEL__ + +#include +#include + +/* + * inode number macros + */ +#define ITYPE(x) ((x)&(0xf<<28)) +#define ISPECIAL (0<<28) +#define IBUS (1<<28) +#define IDEVICE (2<<28) +#define IBUSNR(x) (((x)>>8)&0xff) +#define IDEVNR(x) ((x)&0xff) + +#define IROOT 1 + +/* + * sigh. rwsemaphores do not (yet) work from modules + */ + +#define rw_semaphore semaphore +#define init_rwsem init_MUTEX +#define down_read down +#define down_write down +#define up_read up +#define up_write up + + +struct dev_state { + struct list_head list; /* state list */ + struct rw_semaphore devsem; /* protects modifications to dev (dev == NULL indicating disconnect) */ + struct usb_device *dev; + struct file *file; + spinlock_t lock; /* protects the async urb lists */ + struct list_head async_pending; + struct list_head async_completed; + wait_queue_head_t wait; /* wake up if a request completed */ + unsigned int discsignr; + struct task_struct *disctask; + void *disccontext; + unsigned long ifclaimed; +}; + +/* internal methods & data */ +extern struct usb_driver usbdevfs_driver; +extern struct file_operations usbdevfs_drivers_fops; +extern struct file_operations usbdevfs_devices_fops; +extern struct file_operations usbdevfs_device_file_operations; +extern struct inode_operations usbdevfs_device_inode_operations; +extern struct inode_operations usbdevfs_bus_inode_operations; +extern struct file_operations usbdevfs_bus_file_operations; +extern void usbdevfs_conn_disc_event(void); + +#endif /* __KERNEL__ */ + +/* --------------------------------------------------------------------- */ +#endif /* _LINUX_USBDEVICE_FS_H */ diff --git a/init/main.c b/init/main.c index 50e86b35edd0..1b500c730f3b 100644 --- a/init/main.c +++ b/init/main.c @@ -402,6 +402,11 @@ extern void dquot_init_hash(void); extern void md_setup(char *str,int *ints) __init; #endif +#ifdef CONFIG_AGP +extern int agp_initialize (void); +extern void agp_setup(char *str, int *ints); +#endif + /* * Boot command-line arguments */ @@ -1052,6 +1057,9 @@ static struct kernel_param cooked_params[] __initdata = { #endif #ifdef CONFIG_BLK_CPQ_DA { "smart2=", cpqarray_setup }, +#endif +#ifdef CONFIG_AGP + { "agp_try_unsupported=", agp_setup}, #endif { 0, 0 } }; @@ -1524,6 +1532,9 @@ static void __init do_basic_setup(void) #ifdef CONFIG_MAC nubus_init(); #endif +#ifdef CONFIG_AGP + agp_initialize (); +#endif /* Networking initialization needs a process context */ sock_init(); diff --git a/kernel/ksyms.c b/kernel/ksyms.c index 264c72d7a83e..89e3bc6a5093 100644 --- a/kernel/ksyms.c +++ b/kernel/ksyms.c @@ -90,6 +90,7 @@ EXPORT_SYMBOL(exit_mm); EXPORT_SYMBOL(exit_files); EXPORT_SYMBOL(exit_fs); EXPORT_SYMBOL(exit_sighand); +EXPORT_SYMBOL(daemonize); /* internal kernel memory management */ EXPORT_SYMBOL(__get_free_pages); @@ -98,6 +99,7 @@ EXPORT_SYMBOL(__free_pages); EXPORT_SYMBOL(kmem_find_general_cachep); EXPORT_SYMBOL(kmem_cache_create); EXPORT_SYMBOL(kmem_cache_shrink); +EXPORT_SYMBOL(kmem_cache_destroy); EXPORT_SYMBOL(kmem_cache_alloc); EXPORT_SYMBOL(kmem_cache_free); EXPORT_SYMBOL(kmalloc); diff --git a/kernel/sched.c b/kernel/sched.c index 5349e64ec399..193ade8726d4 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -2047,6 +2047,34 @@ void show_state(void) read_unlock(&tasklist_lock); } +/* + * Put all the gunge required to become a kernel thread without + * attached user resources in one place where it belongs. + */ + +void daemonize(void) +{ + struct fs_struct *fs; + + /* + * If we were started as result of loading a module, close all of the + * user space pages. We don't need them, and if we didn't close them + * they would be locked into memory. + */ + exit_mm(current); + + current->session = 1; + current->pgrp = 1; + + /* Become as one with the init task */ + + exit_fs(current); /* current->fs->count--; */ + fs = init_task.fs; + current->fs = fs; + atomic_inc(&fs->count); + +} + void __init init_idle(void) { cycles_t t; diff --git a/mm/slab.c b/mm/slab.c index 85051047cd29..b67614e2803e 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -987,8 +987,8 @@ opps: * is available. To help debugging, a zero exit status indicates all slabs * were released. */ -int -kmem_cache_shrink(kmem_cache_t *cachep) + +static int __kmem_cache_shrink(kmem_cache_t *cachep, int validated) { kmem_cache_t *searchp; kmem_slab_t *slabp; @@ -1003,26 +1003,29 @@ kmem_cache_shrink(kmem_cache_t *cachep) return 2; } - /* Find the cache in the chain of caches. */ - down(&cache_chain_sem); /* Semaphore is needed. */ - searchp = &cache_cache; - for (;searchp->c_nextp != &cache_cache; searchp = searchp->c_nextp) { - if (searchp->c_nextp != cachep) - continue; - - /* Accessing clock_searchp is safe - we hold the mutex. */ - if (cachep == clock_searchp) - clock_searchp = cachep->c_nextp; - goto found; - } - up(&cache_chain_sem); - printk(KERN_ERR "kmem_shrink: Invalid cache addr %p\n", cachep); - return 2; + if(validated==0) + { + /* Find the cache in the chain of caches. */ + down(&cache_chain_sem); /* Semaphore is needed. */ + searchp = &cache_cache; + for (;searchp->c_nextp != &cache_cache; searchp = searchp->c_nextp) { + if (searchp->c_nextp != cachep) + continue; + + /* Accessing clock_searchp is safe - we hold the mutex. */ + if (cachep == clock_searchp) + clock_searchp = cachep->c_nextp; + goto found; + } + up(&cache_chain_sem); + printk(KERN_ERR "kmem_shrink: Invalid cache addr %p\n", cachep); + return 2; found: - /* Release the semaphore before getting the cache-lock. This could - * mean multiple engines are shrinking the cache, but so what. - */ - up(&cache_chain_sem); + /* Release the semaphore before getting the cache-lock. This could + * mean multiple engines are shrinking the cache, but so what. + */ + up(&cache_chain_sem); + } spin_lock_irq(&cachep->c_spinlock); /* If the cache is growing, stop shrinking. */ @@ -1042,6 +1045,71 @@ found: return ret; } +int kmem_cache_shrink(kmem_cache_t *cachep) +{ + __kmem_cache_shrink(cachep,0); +} + +/* + * Remove a kmem_cache_t object from the slab cache. When returns 0 it + * completed succesfully. -arca + */ +int kmem_cache_destroy(kmem_cache_t * cachep) +{ + kmem_cache_t * prev; + int ret; + + if (!cachep) { + printk(KERN_ERR "kmem_destroy: NULL ptr\n"); + return 1; + } + if (in_interrupt()) { + printk(KERN_ERR "kmem_destroy: Called during int - %s\n", + cachep->c_name); + return 1; + } + + ret = 0; + /* Find the cache in the chain of caches. */ + down(&cache_chain_sem); + for (prev = &cache_cache; prev->c_nextp != &cache_cache; + prev = prev->c_nextp) { + if (prev->c_nextp != cachep) + continue; + + /* Accessing clock_searchp is safe - we hold the mutex. */ + if (cachep == clock_searchp) + clock_searchp = cachep->c_nextp; + + /* remove the cachep from the cache_cache list. -arca */ + prev->c_nextp = cachep->c_nextp; + + ret = 1; + break; + } + up(&cache_chain_sem); + + if (!ret) { + printk(KERN_ERR "kmem_destroy: Invalid cache addr %p\n", + cachep); + return 1; + } + + if (__kmem_cache_shrink(cachep, 1)) { + printk(KERN_ERR "kmem_destroy: Can't free all objects %p\n", + cachep); + down(&cache_chain_sem); + cachep->c_nextp = cache_cache.c_nextp; + cache_cache.c_nextp = cachep; + up(&cache_chain_sem); + return 1; + } + + kmem_cache_free(&cache_cache, cachep); + + return 0; +} + /* Get the memory for a slab management obj. */ static inline kmem_slab_t * kmem_cache_slabmgmt(kmem_cache_t *cachep, void *objp, int local_flags) -- 2.39.5