aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/acpi/Kconfig2
-rw-r--r--drivers/acpi/acpi_memhotplug.c146
-rw-r--r--drivers/acpi/numa.c15
-rw-r--r--drivers/acpi/processor_idle.c21
-rw-r--r--drivers/amba/bus.c5
-rw-r--r--drivers/atm/ambassador.c3
-rw-r--r--drivers/atm/firestream.c10
-rw-r--r--drivers/base/cpu.c28
-rw-r--r--drivers/base/dmapool.c3
-rw-r--r--drivers/base/memory.c4
-rw-r--r--drivers/base/node.c61
-rw-r--r--drivers/base/power/resume.c6
-rw-r--r--drivers/base/power/suspend.c13
-rw-r--r--drivers/base/topology.c4
-rw-r--r--drivers/block/loop.c28
-rw-r--r--drivers/block/paride/pf.c2
-rw-r--r--drivers/block/rd.c2
-rw-r--r--drivers/block/sx8.c5
-rw-r--r--drivers/bluetooth/dtl1_cs.c3
-rw-r--r--drivers/char/Kconfig56
-rw-r--r--drivers/char/Makefile4
-rw-r--r--drivers/char/agp/Kconfig4
-rw-r--r--drivers/char/agp/amd-k7-agp.c2
-rw-r--r--drivers/char/agp/amd64-agp.c81
-rw-r--r--drivers/char/agp/ati-agp.c2
-rw-r--r--drivers/char/agp/efficeon-agp.c2
-rw-r--r--drivers/char/agp/sgi-agp.c5
-rw-r--r--drivers/char/applicom.c9
-rw-r--r--drivers/char/drm/drm_memory_debug.h2
-rw-r--r--drivers/char/drm/via_dmablit.c2
-rw-r--r--drivers/char/epca.c2
-rw-r--r--drivers/char/hangcheck-timer.c4
-rw-r--r--drivers/char/hvcs.c11
-rw-r--r--drivers/char/hw_random.c698
-rw-r--r--drivers/char/hw_random/Kconfig90
-rw-r--r--drivers/char/hw_random/Makefile11
-rw-r--r--drivers/char/hw_random/amd-rng.c152
-rw-r--r--drivers/char/hw_random/core.c354
-rw-r--r--drivers/char/hw_random/geode-rng.c128
-rw-r--r--drivers/char/hw_random/intel-rng.c189
-rw-r--r--drivers/char/hw_random/ixp4xx-rng.c73
-rw-r--r--drivers/char/hw_random/omap-rng.c208
-rw-r--r--drivers/char/hw_random/via-rng.c183
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c23
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c69
-rw-r--r--drivers/char/ipmi/ipmi_watchdog.c18
-rw-r--r--drivers/char/istallion.c1819
-rw-r--r--drivers/char/keyboard.c26
-rw-r--r--drivers/char/moxa.c2
-rw-r--r--drivers/char/mxser.c1
-rw-r--r--drivers/char/n_tty.c6
-rw-r--r--drivers/char/nsc_gpio.c142
-rw-r--r--drivers/char/pc8736x_gpio.c340
-rw-r--r--drivers/char/pty.c2
-rw-r--r--drivers/char/rio/riointr.c2
-rw-r--r--drivers/char/scx200_gpio.c162
-rw-r--r--drivers/char/specialix.c2
-rw-r--r--drivers/char/stallion.c211
-rw-r--r--drivers/char/sx.c2
-rw-r--r--drivers/char/tlclk.c2
-rw-r--r--drivers/char/tty_io.c75
-rw-r--r--drivers/char/vr41xx_giu.c4
-rw-r--r--drivers/char/vt.c580
-rw-r--r--drivers/char/watchdog/at91_wdt.c82
-rw-r--r--drivers/char/watchdog/i8xx_tco.c28
-rw-r--r--drivers/char/watchdog/pcwd_pci.c30
-rw-r--r--drivers/char/watchdog/pcwd_usb.c23
-rw-r--r--drivers/clocksource/Makefile3
-rw-r--r--drivers/clocksource/acpi_pm.c177
-rw-r--r--drivers/clocksource/cyclone.c119
-rw-r--r--drivers/clocksource/scx200_hrt.c101
-rw-r--r--drivers/cpufreq/cpufreq.c8
-rw-r--r--drivers/cpufreq/cpufreq_stats.c4
-rw-r--r--drivers/crypto/padlock-aes.c39
-rw-r--r--drivers/dma/ioatdma.c5
-rw-r--r--drivers/hwmon/asb100.c2
-rw-r--r--drivers/hwmon/lm78.c2
-rw-r--r--drivers/hwmon/lm80.c2
-rw-r--r--drivers/hwmon/lm87.c2
-rw-r--r--drivers/hwmon/sis5595.c2
-rw-r--r--drivers/hwmon/smsc47m1.c2
-rw-r--r--drivers/hwmon/w83627hf.c2
-rw-r--r--drivers/hwmon/w83781d.c2
-rw-r--r--drivers/hwmon/w83792d.c2
-rw-r--r--drivers/i2c/busses/i2c-i801.c16
-rw-r--r--drivers/ide/ide-disk.c2
-rw-r--r--drivers/ide/ide-io.c6
-rw-r--r--drivers/ide/ide-iops.c4
-rw-r--r--drivers/ide/ide-lib.c4
-rw-r--r--drivers/ide/ide-timing.h8
-rw-r--r--drivers/ide/pci/aec62xx.c19
-rw-r--r--drivers/ide/pci/amd74xx.c7
-rw-r--r--drivers/ide/pci/cmd64x.c15
-rw-r--r--drivers/ide/pci/hpt34x.c2
-rw-r--r--drivers/ide/pci/pdc202xx_new.c6
-rw-r--r--drivers/ide/pci/pdc202xx_old.c159
-rw-r--r--drivers/ide/pci/piix.c12
-rw-r--r--drivers/ide/pci/sc1200.c4
-rw-r--r--drivers/ide/pci/serverworks.c53
-rw-r--r--drivers/ide/pci/siimage.c62
-rw-r--r--drivers/ide/pci/sl82c105.c4
-rw-r--r--drivers/ide/pci/slc90e66.c11
-rw-r--r--drivers/ieee1394/eth1394.c3
-rw-r--r--drivers/ieee1394/hosts.c2
-rw-r--r--drivers/ieee1394/ieee1394_core.h2
-rw-r--r--drivers/ieee1394/ohci1394.c17
-rw-r--r--drivers/ieee1394/raw1394.c3
-rw-r--r--drivers/infiniband/Kconfig2
-rw-r--r--drivers/infiniband/Makefile1
-rw-r--r--drivers/infiniband/core/cma.c2
-rw-r--r--drivers/infiniband/core/mad.c9
-rw-r--r--drivers/infiniband/core/mad_rmpp.c3
-rw-r--r--drivers/infiniband/core/uverbs_cmd.c2
-rw-r--r--drivers/infiniband/core/uverbs_main.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c8
-rw-r--r--drivers/infiniband/hw/mthca/mthca_main.c5
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_multicast.c6
-rw-r--r--drivers/infiniband/ulp/iser/Kconfig11
-rw-r--r--drivers/infiniband/ulp/iser/Makefile4
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c790
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h354
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c738
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c401
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c827
-rw-r--r--drivers/input/evdev.c10
-rw-r--r--drivers/input/input.c86
-rw-r--r--drivers/input/joydev.c69
-rw-r--r--drivers/input/joystick/a3d.c2
-rw-r--r--drivers/input/joystick/analog.c23
-rw-r--r--drivers/input/joystick/cobra.c3
-rw-r--r--drivers/input/joystick/db9.c5
-rw-r--r--drivers/input/joystick/gamecon.c3
-rw-r--r--drivers/input/joystick/gf2k.c2
-rw-r--r--drivers/input/joystick/grip.c3
-rw-r--r--drivers/input/joystick/guillemot.c2
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c8
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c6
-rw-r--r--drivers/input/joystick/interact.c2
-rw-r--r--drivers/input/joystick/magellan.c2
-rw-r--r--drivers/input/joystick/sidewinder.c15
-rw-r--r--drivers/input/joystick/spaceball.c2
-rw-r--r--drivers/input/joystick/spaceorb.c2
-rw-r--r--drivers/input/joystick/stinger.c2
-rw-r--r--drivers/input/joystick/twidjoy.c2
-rw-r--r--drivers/input/joystick/warrior.c2
-rw-r--r--drivers/input/keyboard/atkbd.c219
-rw-r--r--drivers/input/keyboard/lkkbd.c9
-rw-r--r--drivers/input/keyboard/newtonkbd.c2
-rw-r--r--drivers/input/keyboard/sunkbd.c2
-rw-r--r--drivers/input/keyboard/xtkbd.c2
-rw-r--r--drivers/input/misc/wistron_btns.c19
-rw-r--r--drivers/input/mouse/alps.c2
-rw-r--r--drivers/input/mouse/psmouse-base.c39
-rw-r--r--drivers/input/mouse/sermouse.c2
-rw-r--r--drivers/input/mouse/vsxxxaa.c22
-rw-r--r--drivers/input/mousedev.c42
-rw-r--r--drivers/input/serio/ct82c710.c6
-rw-r--r--drivers/input/touchscreen/gunze.c2
-rw-r--r--drivers/input/touchscreen/h3600_ts_input.c2
-rw-r--r--drivers/input/touchscreen/mtouch.c2
-rw-r--r--drivers/input/tsdev.c22
-rw-r--r--drivers/isdn/capi/capi.c54
-rw-r--r--drivers/isdn/divert/isdn_divert.c2
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c298
-rw-r--r--drivers/isdn/gigaset/common.c2
-rw-r--r--drivers/isdn/gigaset/ev-layer.c13
-rw-r--r--drivers/isdn/hisax/hfc_pci.c2
-rw-r--r--drivers/isdn/hisax/q931.c4
-rw-r--r--drivers/isdn/hisax/telespci.c5
-rw-r--r--drivers/isdn/i4l/isdn_common.c2
-rw-r--r--drivers/isdn/i4l/isdn_tty.c2
-rw-r--r--drivers/isdn/i4l/isdn_x25iface.c4
-rw-r--r--drivers/leds/led-core.c2
-rw-r--r--drivers/leds/led-triggers.c2
-rw-r--r--drivers/macintosh/Makefile2
-rw-r--r--drivers/macintosh/adbhid.c2
-rw-r--r--drivers/macintosh/macio_asic.c4
-rw-r--r--drivers/macintosh/via-pmu-event.c80
-rw-r--r--drivers/macintosh/via-pmu-event.h8
-rw-r--r--drivers/macintosh/via-pmu.c8
-rw-r--r--drivers/md/Kconfig46
-rw-r--r--drivers/md/Makefile5
-rw-r--r--drivers/md/bitmap.c483
-rw-r--r--drivers/md/dm-crypt.c56
-rw-r--r--drivers/md/dm-emc.c40
-rw-r--r--drivers/md/dm-exception-store.c67
-rw-r--r--drivers/md/dm-ioctl.c109
-rw-r--r--drivers/md/dm-linear.c8
-rw-r--r--drivers/md/dm-log.c157
-rw-r--r--drivers/md/dm-mpath.c43
-rw-r--r--drivers/md/dm-raid1.c97
-rw-r--r--drivers/md/dm-round-robin.c6
-rw-r--r--drivers/md/dm-snap.c16
-rw-r--r--drivers/md/dm-stripe.c25
-rw-r--r--drivers/md/dm-table.c57
-rw-r--r--drivers/md/dm-target.c2
-rw-r--r--drivers/md/dm-zero.c8
-rw-r--r--drivers/md/dm.c184
-rw-r--r--drivers/md/dm.h81
-rw-r--r--drivers/md/kcopyd.c4
-rw-r--r--drivers/md/linear.c74
-rw-r--r--drivers/md/md.c634
-rw-r--r--drivers/md/raid1.c43
-rw-r--r--drivers/md/raid10.c77
-rw-r--r--drivers/md/raid5.c1307
-rw-r--r--drivers/md/raid6main.c2427
-rw-r--r--drivers/media/Kconfig3
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c27
-rw-r--r--drivers/media/dvb/ttpci/av7110.c8
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c25
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c50
-rw-r--r--drivers/media/video/Kconfig14
-rw-r--r--drivers/media/video/Makefile1
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c10
-rw-r--r--drivers/media/video/cx2341x.c57
-rw-r--r--drivers/media/video/cx88/Kconfig15
-rw-r--r--drivers/media/video/cx88/Makefile5
-rw-r--r--drivers/media/video/cx88/cx88-alsa.c8
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c30
-rw-r--r--drivers/media/video/cx88/cx88-cards.c5
-rw-r--r--drivers/media/video/cx88/cx88-core.c6
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c1
-rw-r--r--drivers/media/video/cx88/cx88-mpeg.c4
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c2
-rw-r--r--drivers/media/video/cx88/cx88-video.c10
-rw-r--r--drivers/media/video/cx88/cx88.h7
-rw-r--r--drivers/media/video/pvrusb2/Kconfig62
-rw-r--r--drivers/media/video/pvrusb2/Makefile18
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.c204
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-audio.h40
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.c230
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-context.h92
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.c593
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.h123
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c279
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h53
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debug.h67
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.c478
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-debugifc.h53
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-demod.c126
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-demod.h38
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.c164
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-eeprom.h40
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c418
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.h42
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h384
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c3120
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.h335
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c115
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c232
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h47
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c937
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.h96
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.c695
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-io.h102
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.c513
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ioread.h50
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c172
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.c408
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-std.h60
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c865
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.h47
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-tuner.c122
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-tuner.h38
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-util.h63
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c1126
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.h40
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.c253
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-video-v4l.h52
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.c170
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-wm8775.h53
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2.h43
-rw-r--r--drivers/media/video/saa7134/saa6752hs.c4
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c8
-rw-r--r--drivers/media/video/stradis.c4
-rw-r--r--drivers/media/video/tda9887.c31
-rw-r--r--drivers/media/video/tuner-core.c18
-rw-r--r--drivers/media/video/usbvideo/quickcam_messenger.c2
-rw-r--r--drivers/media/video/v4l2-common.c14
-rw-r--r--drivers/message/fusion/mptfc.c6
-rw-r--r--drivers/message/fusion/mptsas.c3
-rw-r--r--drivers/message/i2o/iop.c15
-rw-r--r--drivers/mfd/ucb1x00-core.c4
-rw-r--r--drivers/misc/ibmasm/module.c2
-rw-r--r--drivers/mmc/mmci.c4
-rw-r--r--drivers/mtd/Kconfig4
-rw-r--r--drivers/mtd/chips/cfi_cmdset_0001.c1
-rw-r--r--drivers/mtd/chips/jedec.c1
-rw-r--r--drivers/mtd/chips/map_absent.c3
-rw-r--r--drivers/mtd/chips/map_ram.c1
-rw-r--r--drivers/mtd/chips/map_rom.c1
-rw-r--r--drivers/mtd/devices/block2mtd.c1
-rw-r--r--drivers/mtd/devices/ms02-nv.c1
-rw-r--r--drivers/mtd/devices/mtd_dataflash.c1
-rw-r--r--drivers/mtd/devices/phram.c1
-rw-r--r--drivers/mtd/devices/pmc551.c11
-rw-r--r--drivers/mtd/devices/slram.c1
-rw-r--r--drivers/mtd/maps/Kconfig2
-rw-r--r--drivers/mtd/maps/amd76xrom.c5
-rw-r--r--drivers/mtd/maps/ichxrom.c5
-rw-r--r--drivers/mtd/maps/ixp2000.c2
-rw-r--r--drivers/mtd/maps/physmap.c2
-rw-r--r--drivers/mtd/maps/scx200_docflash.c5
-rw-r--r--drivers/mtd/maps/sun_uflash.c198
-rw-r--r--drivers/mtd/mtdchar.c2
-rw-r--r--drivers/mtd/nand/nand_base.c16
-rw-r--r--drivers/mtd/nand/ndfc.c6
-rw-r--r--drivers/mtd/nand/s3c2410.c164
-rw-r--r--drivers/mtd/nand/ts7250.c2
-rw-r--r--drivers/net/3c501.c4
-rw-r--r--drivers/net/3c59x.c85
-rw-r--r--drivers/net/8139cp.c11
-rw-r--r--drivers/net/8139too.c6
-rw-r--r--drivers/net/dl2k.h12
-rw-r--r--drivers/net/dm9000.c34
-rw-r--r--drivers/net/e100.c4
-rw-r--r--drivers/net/eepro100.c5
-rw-r--r--drivers/net/epic100.c20
-rw-r--r--drivers/net/fealnx.c10
-rw-r--r--drivers/net/fec.c310
-rw-r--r--drivers/net/fec.h2
-rw-r--r--drivers/net/fs_enet/fs_enet-mii.c3
-rw-r--r--drivers/net/hamradio/dmascc.c2
-rw-r--r--drivers/net/irda/irport.c2
-rw-r--r--drivers/net/irda/nsc-ircc.c8
-rw-r--r--drivers/net/natsemi.c100
-rw-r--r--drivers/net/pcmcia/smc91c92_cs.c2
-rw-r--r--drivers/net/pcnet32.c6
-rw-r--r--drivers/net/phy/lxt.c8
-rw-r--r--drivers/net/ppp_generic.c3
-rw-r--r--drivers/net/skge.c4
-rw-r--r--drivers/net/sky2.c6
-rw-r--r--drivers/net/tulip/de2104x.c9
-rw-r--r--drivers/net/tulip/tulip_core.c6
-rw-r--r--drivers/net/tulip/winbond-840.c26
-rw-r--r--drivers/net/typhoon.c5
-rw-r--r--drivers/net/wan/c101.c2
-rw-r--r--drivers/net/wan/dscc4.c12
-rw-r--r--drivers/net/wan/n2.c2
-rw-r--r--drivers/net/wan/pc300_drv.c4
-rw-r--r--drivers/net/wan/pci200syn.c2
-rw-r--r--drivers/net/wireless/bcm43xx/Kconfig1
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h6
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c37
-rw-r--r--drivers/net/wireless/ipw2100.c2
-rw-r--r--drivers/net/wireless/ipw2200.c22
-rw-r--r--drivers/net/yellowfin.c24
-rw-r--r--drivers/parisc/dino.c4
-rw-r--r--drivers/parisc/eisa.c2
-rw-r--r--drivers/parisc/gsc.c8
-rw-r--r--drivers/parisc/iosapic.c2
-rw-r--r--drivers/parisc/superio.c2
-rw-r--r--drivers/parport/parport_gsc.c2
-rw-r--r--drivers/parport/parport_gsc.h2
-rw-r--r--drivers/parport/parport_pc.c2
-rw-r--r--drivers/parport/parport_sunbpp.c2
-rw-r--r--drivers/parport/procfs.c2
-rw-r--r--drivers/pci/bus.c10
-rw-r--r--drivers/pci/hotplug/cpcihp_zt5550.c9
-rw-r--r--drivers/pci/hotplug/cpqphp_core.c10
-rw-r--r--drivers/pci/hotplug/pciehp_hpc.c5
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c18
-rw-r--r--drivers/pci/msi-apic.c1
-rw-r--r--drivers/pci/msi.c6
-rw-r--r--drivers/pci/pci-sysfs.c4
-rw-r--r--drivers/pci/pci.c8
-rw-r--r--drivers/pci/pci.h6
-rw-r--r--drivers/pci/proc.c20
-rw-r--r--drivers/pci/rom.c10
-rw-r--r--drivers/pci/setup-bus.c6
-rw-r--r--drivers/pci/setup-res.c34
-rw-r--r--drivers/pcmcia/hd64465_ss.c6
-rw-r--r--drivers/pcmcia/i82365.c5
-rw-r--r--drivers/pcmcia/m8xx_pcmcia.c4
-rw-r--r--drivers/pcmcia/pd6729.c3
-rw-r--r--drivers/pcmcia/rsrc_nonstatic.c26
-rw-r--r--drivers/pcmcia/tcic.c5
-rw-r--r--drivers/pnp/interface.c8
-rw-r--r--drivers/pnp/manager.c15
-rw-r--r--drivers/pnp/resource.c8
-rw-r--r--drivers/rapidio/rio-access.c4
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/class.c2
-rw-r--r--drivers/rtc/rtc-ds1553.c2
-rw-r--r--drivers/rtc/rtc-rs5c348.c246
-rw-r--r--drivers/rtc/rtc-sa1100.c2
-rw-r--r--drivers/rtc/rtc-vr41xx.c2
-rw-r--r--drivers/s390/block/dasd.c105
-rw-r--r--drivers/s390/block/dasd_3370_erp.c27
-rw-r--r--drivers/s390/block/dasd_3990_erp.c189
-rw-r--r--drivers/s390/block/dasd_9336_erp.c27
-rw-r--r--drivers/s390/block/dasd_9343_erp.c2
-rw-r--r--drivers/s390/block/dasd_devmap.c102
-rw-r--r--drivers/s390/block/dasd_diag.c6
-rw-r--r--drivers/s390/block/dasd_diag.h2
-rw-r--r--drivers/s390/block/dasd_eckd.c337
-rw-r--r--drivers/s390/block/dasd_eckd.h24
-rw-r--r--drivers/s390/block/dasd_eer.c6
-rw-r--r--drivers/s390/block/dasd_erp.c8
-rw-r--r--drivers/s390/block/dasd_fba.c49
-rw-r--r--drivers/s390/block/dasd_fba.h2
-rw-r--r--drivers/s390/block/dasd_int.h39
-rw-r--r--drivers/s390/block/dasd_ioctl.c12
-rw-r--r--drivers/s390/char/raw3270.c67
-rw-r--r--drivers/s390/cio/blacklist.c35
-rw-r--r--drivers/s390/cio/ccwgroup.c17
-rw-r--r--drivers/s390/cio/chsc.c6
-rw-r--r--drivers/s390/cio/cmf.c623
-rw-r--r--drivers/s390/cio/css.c63
-rw-r--r--drivers/s390/cio/device.c4
-rw-r--r--drivers/s390/cio/device.h10
-rw-r--r--drivers/s390/cio/device_fsm.c20
-rw-r--r--drivers/s390/cio/device_ops.c10
-rw-r--r--drivers/s390/net/lcs.c7
-rw-r--r--drivers/s390/s390mach.c5
-rw-r--r--drivers/s390/scsi/zfcp_erp.c14
-rw-r--r--drivers/sbus/char/cpwatchdog.c2
-rw-r--r--drivers/sbus/char/openprom.c593
-rw-r--r--drivers/sbus/char/riowatchdog.c4
-rw-r--r--drivers/scsi/Kconfig12
-rw-r--r--drivers/scsi/Makefile1
-rw-r--r--drivers/scsi/NCR5380.c2
-rw-r--r--drivers/scsi/aacraid/comminit.c5
-rw-r--r--drivers/scsi/advansys.c2
-rw-r--r--drivers/scsi/ahci.c2
-rw-r--r--drivers/scsi/ata_piix.c2
-rw-r--r--drivers/scsi/dc395x.c2
-rw-r--r--drivers/scsi/ibmmca.c8
-rw-r--r--drivers/scsi/imm.c3
-rw-r--r--drivers/scsi/imm.h2
-rw-r--r--drivers/scsi/ips.c4
-rw-r--r--drivers/scsi/libata-core.c153
-rw-r--r--drivers/scsi/libata-eh.c63
-rw-r--r--drivers/scsi/libata-scsi.c4
-rw-r--r--drivers/scsi/libata.h4
-rw-r--r--drivers/scsi/ncr53c8xx.c3
-rw-r--r--drivers/scsi/ppa.c2
-rw-r--r--drivers/scsi/ppa.h2
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c3
-rw-r--r--drivers/scsi/sata_nv.c2
-rw-r--r--drivers/scsi/sata_sil.c31
-rw-r--r--drivers/scsi/sata_sil24.c2
-rw-r--r--drivers/scsi/sata_svw.c2
-rw-r--r--drivers/scsi/sata_uli.c2
-rw-r--r--drivers/scsi/sata_via.c10
-rw-r--r--drivers/scsi/sata_vsc.c12
-rw-r--r--drivers/scsi/st.c2
-rw-r--r--drivers/serial/68328serial.c102
-rw-r--r--drivers/serial/8250_pci.c4
-rw-r--r--drivers/serial/8250_pnp.c4
-rw-r--r--drivers/serial/crisv10.c6
-rw-r--r--drivers/serial/jsm/jsm_tty.c7
-rw-r--r--drivers/serial/mcfserial.c28
-rw-r--r--drivers/sn/ioc3.c2
-rw-r--r--drivers/sn/ioc4.c2
-rw-r--r--drivers/spi/spi.c1
-rw-r--r--drivers/telephony/ixj.c2
-rw-r--r--drivers/usb/host/hc_crisv10.c3
-rw-r--r--drivers/usb/host/sl811-hcd.c10
-rw-r--r--drivers/usb/input/fixp-arith.h15
-rw-r--r--drivers/usb/input/hid-debug.h2
-rw-r--r--drivers/usb/serial/ir-usb.c3
-rw-r--r--drivers/usb/serial/whiteheat.c19
-rw-r--r--drivers/usb/storage/usb.h4
-rw-r--r--drivers/video/Kconfig49
-rw-r--r--drivers/video/Makefile9
-rw-r--r--drivers/video/aty/aty128fb.c36
-rw-r--r--drivers/video/aty/atyfb_base.c43
-rw-r--r--drivers/video/aty/mach64_accel.c10
-rw-r--r--drivers/video/aty/mach64_cursor.c33
-rw-r--r--drivers/video/aty/radeon_backlight.c6
-rw-r--r--drivers/video/aty/radeon_base.c2
-rw-r--r--drivers/video/au1100fb.c44
-rw-r--r--drivers/video/backlight/Kconfig12
-rw-r--r--drivers/video/backlight/Makefile2
-rw-r--r--drivers/video/backlight/hp680_bl.c2
-rw-r--r--drivers/video/backlight/locomolcd.c123
-rw-r--r--drivers/video/cfbimgblt.c1
-rw-r--r--drivers/video/cirrusfb.c2
-rw-r--r--drivers/video/console/fbcon.c361
-rw-r--r--drivers/video/console/fbcon.h1
-rw-r--r--drivers/video/console/mdacon.c2
-rw-r--r--drivers/video/console/newport_con.c38
-rw-r--r--drivers/video/console/promcon.c4
-rw-r--r--drivers/video/console/sticon.c2
-rw-r--r--drivers/video/console/vgacon.c40
-rw-r--r--drivers/video/epson1355fb.c29
-rw-r--r--drivers/video/fbcvt.c1
-rw-r--r--drivers/video/fbmem.c58
-rw-r--r--drivers/video/fbmon.c33
-rw-r--r--drivers/video/fbsysfs.c52
-rw-r--r--drivers/video/geode/gx1fb_core.c3
-rw-r--r--drivers/video/geode/gxfb_core.c3
-rw-r--r--drivers/video/i810/i810_main.c3
-rw-r--r--drivers/video/imacfb.c345
-rw-r--r--drivers/video/macmodes.c6
-rw-r--r--drivers/video/macmodes.h7
-rw-r--r--drivers/video/modedb.c11
-rw-r--r--drivers/video/neofb.c32
-rw-r--r--drivers/video/nvidia/nv_hw.c10
-rw-r--r--drivers/video/nvidia/nvidia.c370
-rw-r--r--drivers/video/riva/fbdev.c2
-rw-r--r--drivers/video/s3c2410fb.c17
-rw-r--r--drivers/video/savage/savagefb.h45
-rw-r--r--drivers/video/savage/savagefb_driver.c1475
-rw-r--r--drivers/video/sgivwfb.c6
-rw-r--r--drivers/video/sis/sis_main.c2
-rw-r--r--drivers/video/skeletonfb.c5
-rw-r--r--drivers/video/tgafb.c1
-rw-r--r--drivers/video/vesafb.c59
-rw-r--r--drivers/video/vfb.c29
-rw-r--r--drivers/video/vga16fb.c40
515 files changed, 29202 insertions, 10213 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 3c5170310bd0..fc2d744a4e4a 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -74,4 +74,5 @@ obj-$(CONFIG_SGI_SN) += sn/
obj-y += firmware/
obj-$(CONFIG_CRYPTO) += crypto/
obj-$(CONFIG_SUPERH) += sh/
+obj-$(CONFIG_GENERIC_TIME) += clocksource/
obj-$(CONFIG_DMA_ENGINE) += dma/
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 94b8d820c512..610d2cc02cf8 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -328,7 +328,7 @@ config ACPI_CONTAINER
config ACPI_HOTPLUG_MEMORY
tristate "Memory Hotplug"
depends on ACPI
- depends on MEMORY_HOTPLUG || X86_64
+ depends on MEMORY_HOTPLUG
default n
help
This driver adds supports for ACPI Memory Hotplug. This driver
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index e0a95ba72371..1012284ff4f7 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -57,6 +57,7 @@ MODULE_LICENSE("GPL");
static int acpi_memory_device_add(struct acpi_device *device);
static int acpi_memory_device_remove(struct acpi_device *device, int type);
+static int acpi_memory_device_start(struct acpi_device *device);
static struct acpi_driver acpi_memory_device_driver = {
.name = ACPI_MEMORY_DEVICE_DRIVER_NAME,
@@ -65,48 +66,79 @@ static struct acpi_driver acpi_memory_device_driver = {
.ops = {
.add = acpi_memory_device_add,
.remove = acpi_memory_device_remove,
+ .start = acpi_memory_device_start,
},
};
+struct acpi_memory_info {
+ struct list_head list;
+ u64 start_addr; /* Memory Range start physical addr */
+ u64 length; /* Memory Range length */
+ unsigned short caching; /* memory cache attribute */
+ unsigned short write_protect; /* memory read/write attribute */
+ unsigned int enabled:1;
+};
+
struct acpi_memory_device {
acpi_handle handle;
unsigned int state; /* State of the memory device */
- unsigned short caching; /* memory cache attribute */
- unsigned short write_protect; /* memory read/write attribute */
- u64 start_addr; /* Memory Range start physical addr */
- u64 length; /* Memory Range length */
+ struct list_head res_list;
};
+static acpi_status
+acpi_memory_get_resource(struct acpi_resource *resource, void *context)
+{
+ struct acpi_memory_device *mem_device = context;
+ struct acpi_resource_address64 address64;
+ struct acpi_memory_info *info, *new;
+ acpi_status status;
+
+ status = acpi_resource_to_address64(resource, &address64);
+ if (ACPI_FAILURE(status) ||
+ (address64.resource_type != ACPI_MEMORY_RANGE))
+ return AE_OK;
+
+ list_for_each_entry(info, &mem_device->res_list, list) {
+ /* Can we combine the resource range information? */
+ if ((info->caching == address64.info.mem.caching) &&
+ (info->write_protect == address64.info.mem.write_protect) &&
+ (info->start_addr + info->length == address64.minimum)) {
+ info->length += address64.address_length;
+ return AE_OK;
+ }
+ }
+
+ new = kzalloc(sizeof(struct acpi_memory_info), GFP_KERNEL);
+ if (!new)
+ return AE_ERROR;
+
+ INIT_LIST_HEAD(&new->list);
+ new->caching = address64.info.mem.caching;
+ new->write_protect = address64.info.mem.write_protect;
+ new->start_addr = address64.minimum;
+ new->length = address64.address_length;
+ list_add_tail(&new->list, &mem_device->res_list);
+
+ return AE_OK;
+}
+
static int
acpi_memory_get_device_resources(struct acpi_memory_device *mem_device)
{
acpi_status status;
- struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
- struct acpi_resource *resource = NULL;
- struct acpi_resource_address64 address64;
+ struct acpi_memory_info *info, *n;
ACPI_FUNCTION_TRACE("acpi_memory_get_device_resources");
- /* Get the range from the _CRS */
- status = acpi_get_current_resources(mem_device->handle, &buffer);
- if (ACPI_FAILURE(status))
- return_VALUE(-EINVAL);
-
- resource = (struct acpi_resource *)buffer.pointer;
- status = acpi_resource_to_address64(resource, &address64);
- if (ACPI_SUCCESS(status)) {
- if (address64.resource_type == ACPI_MEMORY_RANGE) {
- /* Populate the structure */
- mem_device->caching = address64.info.mem.caching;
- mem_device->write_protect =
- address64.info.mem.write_protect;
- mem_device->start_addr = address64.minimum;
- mem_device->length = address64.address_length;
- }
+ status = acpi_walk_resources(mem_device->handle, METHOD_NAME__CRS,
+ acpi_memory_get_resource, mem_device);
+ if (ACPI_FAILURE(status)) {
+ list_for_each_entry_safe(info, n, &mem_device->res_list, list)
+ kfree(info);
+ return -EINVAL;
}
- acpi_os_free(buffer.pointer);
- return_VALUE(0);
+ return 0;
}
static int
@@ -181,7 +213,9 @@ static int acpi_memory_check_device(struct acpi_memory_device *mem_device)
static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
{
- int result;
+ int result, num_enabled = 0;
+ struct acpi_memory_info *info;
+ int node;
ACPI_FUNCTION_TRACE("acpi_memory_enable_device");
@@ -194,15 +228,35 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
return result;
}
+ node = acpi_get_node(mem_device->handle);
/*
* Tell the VM there is more memory here...
* Note: Assume that this function returns zero on success
+ * We don't have memory-hot-add rollback function,now.
+ * (i.e. memory-hot-remove function)
*/
- result = add_memory(mem_device->start_addr, mem_device->length);
- if (result) {
+ list_for_each_entry(info, &mem_device->res_list, list) {
+ u64 start_pfn, end_pfn;
+
+ start_pfn = info->start_addr >> PAGE_SHIFT;
+ end_pfn = (info->start_addr + info->length - 1) >> PAGE_SHIFT;
+
+ if (pfn_valid(start_pfn) || pfn_valid(end_pfn)) {
+ /* already enabled. try next area */
+ num_enabled++;
+ continue;
+ }
+
+ result = add_memory(node, info->start_addr, info->length);
+ if (result)
+ continue;
+ info->enabled = 1;
+ num_enabled++;
+ }
+ if (!num_enabled) {
ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "\nadd_memory failed\n"));
mem_device->state = MEMORY_INVALID_STATE;
- return result;
+ return -EINVAL;
}
return result;
@@ -246,8 +300,7 @@ static int acpi_memory_powerdown_device(struct acpi_memory_device *mem_device)
static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
{
int result;
- u64 start = mem_device->start_addr;
- u64 len = mem_device->length;
+ struct acpi_memory_info *info, *n;
ACPI_FUNCTION_TRACE("acpi_memory_disable_device");
@@ -255,10 +308,13 @@ static int acpi_memory_disable_device(struct acpi_memory_device *mem_device)
* Ask the VM to offline this memory range.
* Note: Assume that this function returns zero on success
*/
- result = remove_memory(start, len);
- if (result) {
- ACPI_DEBUG_PRINT((ACPI_DB_ERROR, "Hot-Remove failed.\n"));
- return_VALUE(result);
+ list_for_each_entry_safe(info, n, &mem_device->res_list, list) {
+ if (info->enabled) {
+ result = remove_memory(info->start_addr, info->length);
+ if (result)
+ return result;
+ }
+ kfree(info);
}
/* Power-off and eject the device */
@@ -356,6 +412,7 @@ static int acpi_memory_device_add(struct acpi_device *device)
return_VALUE(-ENOMEM);
memset(mem_device, 0, sizeof(struct acpi_memory_device));
+ INIT_LIST_HEAD(&mem_device->res_list);
mem_device->handle = device->handle;
sprintf(acpi_device_name(device), "%s", ACPI_MEMORY_DEVICE_NAME);
sprintf(acpi_device_class(device), "%s", ACPI_MEMORY_DEVICE_CLASS);
@@ -391,6 +448,25 @@ static int acpi_memory_device_remove(struct acpi_device *device, int type)
return_VALUE(0);
}
+static int acpi_memory_device_start (struct acpi_device *device)
+{
+ struct acpi_memory_device *mem_device;
+ int result = 0;
+
+ ACPI_FUNCTION_TRACE("acpi_memory_device_start");
+
+ mem_device = acpi_driver_data(device);
+
+ if (!acpi_memory_check_device(mem_device)) {
+ /* call add_memory func */
+ result = acpi_memory_enable_device(mem_device);
+ if (result)
+ ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
+ "Error in acpi_memory_enable_device\n"));
+ }
+ return_VALUE(result);
+}
+
/*
* Helper function to check for memory device
*/
diff --git a/drivers/acpi/numa.c b/drivers/acpi/numa.c
index e2c1a16078c9..13d6d5bdea26 100644
--- a/drivers/acpi/numa.c
+++ b/drivers/acpi/numa.c
@@ -254,5 +254,18 @@ int acpi_get_pxm(acpi_handle h)
} while (ACPI_SUCCESS(status));
return -1;
}
-
EXPORT_SYMBOL(acpi_get_pxm);
+
+int acpi_get_node(acpi_handle *handle)
+{
+ int pxm, node = -1;
+
+ ACPI_FUNCTION_TRACE("acpi_get_node");
+
+ pxm = acpi_get_pxm(handle);
+ if (pxm >= 0)
+ node = acpi_map_pxm_to_node(pxm);
+
+ return_VALUE(node);
+}
+EXPORT_SYMBOL(acpi_get_node);
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 3b97a5eae9e8..8a74bf3efd8e 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -206,11 +206,11 @@ acpi_processor_power_activate(struct acpi_processor *pr,
static void acpi_safe_halt(void)
{
- clear_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status &= ~TS_POLLING;
smp_mb__after_clear_bit();
if (!need_resched())
safe_halt();
- set_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status |= TS_POLLING;
}
static atomic_t c3_cpu_count;
@@ -330,10 +330,10 @@ static void acpi_processor_idle(void)
* Invoke the current Cx state to put the processor to sleep.
*/
if (cx->type == ACPI_STATE_C2 || cx->type == ACPI_STATE_C3) {
- clear_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status &= ~TS_POLLING;
smp_mb__after_clear_bit();
if (need_resched()) {
- set_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status |= TS_POLLING;
local_irq_enable();
return;
}
@@ -369,9 +369,14 @@ static void acpi_processor_idle(void)
t2 = inl(acpi_fadt.xpm_tmr_blk.address);
/* Get end time (ticks) */
t2 = inl(acpi_fadt.xpm_tmr_blk.address);
+
+#ifdef CONFIG_GENERIC_TIME
+ /* TSC halts in C2, so notify users */
+ mark_tsc_unstable();
+#endif
/* Re-enable interrupts */
local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status |= TS_POLLING;
/* Compute time (ticks) that we were actually asleep */
sleep_ticks =
ticks_elapsed(t1, t2) - cx->latency_ticks - C2_OVERHEAD;
@@ -409,9 +414,13 @@ static void acpi_processor_idle(void)
ACPI_MTX_DO_NOT_LOCK);
}
+#ifdef CONFIG_GENERIC_TIME
+ /* TSC halts in C3, so notify users */
+ mark_tsc_unstable();
+#endif
/* Re-enable interrupts */
local_irq_enable();
- set_thread_flag(TIF_POLLING_NRFLAG);
+ current_thread_info()->status |= TS_POLLING;
/* Compute time (ticks) that we were actually asleep */
sleep_ticks =
ticks_elapsed(t1, t2) - cx->latency_ticks - C3_OVERHEAD;
diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
index 889855d8d9f9..9e3e2a69c03a 100644
--- a/drivers/amba/bus.c
+++ b/drivers/amba/bus.c
@@ -180,8 +180,9 @@ static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL)
amba_attr(id, "%08x\n", dev->periphid);
amba_attr(irq0, "%u\n", dev->irq[0]);
amba_attr(irq1, "%u\n", dev->irq[1]);
-amba_attr(resource, "\t%08lx\t%08lx\t%08lx\n",
- dev->res.start, dev->res.end, dev->res.flags);
+amba_attr(resource, "\t%016llx\t%016llx\t%016lx\n",
+ (unsigned long long)dev->res.start, (unsigned long long)dev->res.end,
+ dev->res.flags);
/**
* amba_device_register - register an AMBA device
diff --git a/drivers/atm/ambassador.c b/drivers/atm/ambassador.c
index 4b6bf19c39c0..4048681f36d5 100644
--- a/drivers/atm/ambassador.c
+++ b/drivers/atm/ambassador.c
@@ -2257,7 +2257,8 @@ static int __devinit amb_probe(struct pci_dev *pci_dev, const struct pci_device_
}
PRINTD (DBG_INFO, "found Madge ATM adapter (amb) at"
- " IO %lx, IRQ %u, MEM %p", pci_resource_start(pci_dev, 1),
+ " IO %llx, IRQ %u, MEM %p",
+ (unsigned long long)pci_resource_start(pci_dev, 1),
irq, bus_to_virt(pci_resource_start(pci_dev, 0)));
// check IO region
diff --git a/drivers/atm/firestream.c b/drivers/atm/firestream.c
index 7f7ec288824d..d40605c1af73 100644
--- a/drivers/atm/firestream.c
+++ b/drivers/atm/firestream.c
@@ -33,6 +33,7 @@
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/pci.h>
+#include <linux/poison.h>
#include <linux/errno.h>
#include <linux/atm.h>
#include <linux/atmdev.h>
@@ -754,7 +755,7 @@ static void process_txdone_queue (struct fs_dev *dev, struct queue *q)
fs_kfree_skb (skb);
fs_dprintk (FS_DEBUG_ALLOC, "Free trans-d: %p\n", td);
- memset (td, 0x12, sizeof (struct FS_BPENTRY));
+ memset (td, ATM_POISON_FREE, sizeof(struct FS_BPENTRY));
kfree (td);
break;
default:
@@ -951,7 +952,7 @@ static int fs_open(struct atm_vcc *atm_vcc)
it most likely that the chip will notice it. It also prevents us
from having to wait for completion. On the other hand, we may
need to wait for completion anyway, to see if it completed
- succesfully. */
+ successfully. */
switch (atm_vcc->qos.aal) {
case ATM_AAL2:
@@ -1657,9 +1658,10 @@ static int __devinit fs_init (struct fs_dev *dev)
func_enter ();
pci_dev = dev->pci_dev;
- printk (KERN_INFO "found a FireStream %d card, base %08lx, irq%d.\n",
+ printk (KERN_INFO "found a FireStream %d card, base %16llx, irq%d.\n",
IS_FS50(dev)?50:155,
- pci_resource_start(pci_dev, 0), dev->pci_dev->irq);
+ (unsigned long long)pci_resource_start(pci_dev, 0),
+ dev->pci_dev->irq);
if (fs_debug & FS_DEBUG_INIT)
my_hd ((unsigned char *) dev, sizeof (*dev));
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index dd712b24ec91..4bef76a2f3f2 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -8,6 +8,7 @@
#include <linux/cpu.h>
#include <linux/topology.h>
#include <linux/device.h>
+#include <linux/node.h>
#include "base.h"
@@ -57,13 +58,12 @@ static void __devinit register_cpu_control(struct cpu *cpu)
{
sysdev_create_file(&cpu->sysdev, &attr_online);
}
-void unregister_cpu(struct cpu *cpu, struct node *root)
+void unregister_cpu(struct cpu *cpu)
{
int logical_cpu = cpu->sysdev.id;
- if (root)
- sysfs_remove_link(&root->sysdev.kobj,
- kobject_name(&cpu->sysdev.kobj));
+ unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu));
+
sysdev_remove_file(&cpu->sysdev, &attr_online);
sysdev_unregister(&cpu->sysdev);
@@ -109,23 +109,21 @@ static SYSDEV_ATTR(crash_notes, 0400, show_crash_notes, NULL);
*
* Initialize and register the CPU device.
*/
-int __devinit register_cpu(struct cpu *cpu, int num, struct node *root)
+int __devinit register_cpu(struct cpu *cpu, int num)
{
int error;
-
cpu->node_id = cpu_to_node(num);
cpu->sysdev.id = num;
cpu->sysdev.cls = &cpu_sysdev_class;
error = sysdev_register(&cpu->sysdev);
- if (!error && root)
- error = sysfs_create_link(&root->sysdev.kobj,
- &cpu->sysdev.kobj,
- kobject_name(&cpu->sysdev.kobj));
+
if (!error && !cpu->no_control)
register_cpu_control(cpu);
if (!error)
cpu_sys_devices[num] = &cpu->sysdev;
+ if (!error)
+ register_cpu_under_node(num, cpu_to_node(num));
#ifdef CONFIG_KEXEC
if (!error)
@@ -145,5 +143,13 @@ EXPORT_SYMBOL_GPL(get_cpu_sysdev);
int __init cpu_dev_init(void)
{
- return sysdev_class_register(&cpu_sysdev_class);
+ int err;
+
+ err = sysdev_class_register(&cpu_sysdev_class);
+#if defined(CONFIG_SCHED_MC) || defined(CONFIG_SCHED_SMT)
+ if (!err)
+ err = sched_create_sysfs_power_savings_entries(&cpu_sysdev_class);
+#endif
+
+ return err;
}
diff --git a/drivers/base/dmapool.c b/drivers/base/dmapool.c
index e2f64f91ed05..33c5cce1560b 100644
--- a/drivers/base/dmapool.c
+++ b/drivers/base/dmapool.c
@@ -7,6 +7,7 @@
#include <linux/dmapool.h>
#include <linux/slab.h>
#include <linux/module.h>
+#include <linux/poison.h>
/*
* Pool allocator ... wraps the dma_alloc_coherent page allocator, so
@@ -35,8 +36,6 @@ struct dma_page { /* cacheable header for 'allocation' bytes */
};
#define POOL_TIMEOUT_JIFFIES ((100 /* msec */ * HZ) / 1000)
-#define POOL_POISON_FREED 0xa7 /* !inuse */
-#define POOL_POISON_ALLOCATED 0xa9 /* !initted */
static DECLARE_MUTEX (pools_lock);
diff --git a/drivers/base/memory.c b/drivers/base/memory.c
index dd547af4681a..c6b7d9c4b651 100644
--- a/drivers/base/memory.c
+++ b/drivers/base/memory.c
@@ -306,11 +306,13 @@ static ssize_t
memory_probe_store(struct class *class, const char *buf, size_t count)
{
u64 phys_addr;
+ int nid;
int ret;
phys_addr = simple_strtoull(buf, NULL, 0);
- ret = add_memory(phys_addr, PAGES_PER_SECTION << PAGE_SHIFT);
+ nid = memory_add_physaddr_to_nid(phys_addr);
+ ret = add_memory(nid, phys_addr, PAGES_PER_SECTION << PAGE_SHIFT);
if (ret)
count = ret;
diff --git a/drivers/base/node.c b/drivers/base/node.c
index c80c3aeed004..eae2bdc183bb 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -11,6 +11,7 @@
#include <linux/cpumask.h>
#include <linux/topology.h>
#include <linux/nodemask.h>
+#include <linux/cpu.h>
static struct sysdev_class node_class = {
set_kset_name("node"),
@@ -190,6 +191,66 @@ void unregister_node(struct node *node)
sysdev_unregister(&node->sysdev);
}
+struct node node_devices[MAX_NUMNODES];
+
+/*
+ * register cpu under node
+ */
+int register_cpu_under_node(unsigned int cpu, unsigned int nid)
+{
+ if (node_online(nid)) {
+ struct sys_device *obj = get_cpu_sysdev(cpu);
+ if (!obj)
+ return 0;
+ return sysfs_create_link(&node_devices[nid].sysdev.kobj,
+ &obj->kobj,
+ kobject_name(&obj->kobj));
+ }
+
+ return 0;
+}
+
+int unregister_cpu_under_node(unsigned int cpu, unsigned int nid)
+{
+ if (node_online(nid)) {
+ struct sys_device *obj = get_cpu_sysdev(cpu);
+ if (obj)
+ sysfs_remove_link(&node_devices[nid].sysdev.kobj,
+ kobject_name(&obj->kobj));
+ }
+ return 0;
+}
+
+int register_one_node(int nid)
+{
+ int error = 0;
+ int cpu;
+
+ if (node_online(nid)) {
+ int p_node = parent_node(nid);
+ struct node *parent = NULL;
+
+ if (p_node != nid)
+ parent = &node_devices[p_node];
+
+ error = register_node(&node_devices[nid], nid, parent);
+
+ /* link cpu under this node */
+ for_each_present_cpu(cpu) {
+ if (cpu_to_node(cpu) == nid)
+ register_cpu_under_node(cpu, nid);
+ }
+ }
+
+ return error;
+
+}
+
+void unregister_one_node(int nid)
+{
+ unregister_node(&node_devices[nid]);
+}
+
static int __init register_node_type(void)
{
return sysdev_class_register(&node_class);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 520679ce53a8..826093ef4c7e 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -53,8 +53,7 @@ void dpm_resume(void)
struct device * dev = to_device(entry);
get_device(dev);
- list_del_init(entry);
- list_add_tail(entry, &dpm_active);
+ list_move_tail(entry, &dpm_active);
up(&dpm_list_sem);
if (!dev->power.prev_state.event)
@@ -101,8 +100,7 @@ void dpm_power_up(void)
struct device * dev = to_device(entry);
get_device(dev);
- list_del_init(entry);
- list_add_tail(entry, &dpm_active);
+ list_move_tail(entry, &dpm_active);
resume_device(dev);
put_device(dev);
}
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 1a1fe43a3057..69509e02f703 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -116,12 +116,10 @@ int device_suspend(pm_message_t state)
/* Check if the device got removed */
if (!list_empty(&dev->power.entry)) {
/* Move it to the dpm_off or dpm_off_irq list */
- if (!error) {
- list_del(&dev->power.entry);
- list_add(&dev->power.entry, &dpm_off);
- } else if (error == -EAGAIN) {
- list_del(&dev->power.entry);
- list_add(&dev->power.entry, &dpm_off_irq);
+ if (!error)
+ list_move(&dev->power.entry, &dpm_off);
+ else if (error == -EAGAIN) {
+ list_move(&dev->power.entry, &dpm_off_irq);
error = 0;
}
}
@@ -139,8 +137,7 @@ int device_suspend(pm_message_t state)
*/
while (!list_empty(&dpm_off_irq)) {
struct list_head * entry = dpm_off_irq.next;
- list_del(entry);
- list_add(entry, &dpm_off);
+ list_move(entry, &dpm_off);
}
dpm_resume();
}
diff --git a/drivers/base/topology.c b/drivers/base/topology.c
index 8c52421cbc54..c2d621632383 100644
--- a/drivers/base/topology.c
+++ b/drivers/base/topology.c
@@ -107,7 +107,7 @@ static int __cpuinit topology_remove_dev(struct sys_device * sys_dev)
return 0;
}
-static int topology_cpu_callback(struct notifier_block *nfb,
+static int __cpuinit topology_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
@@ -125,7 +125,7 @@ static int topology_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static struct notifier_block topology_cpu_notifier =
+static struct notifier_block __cpuinitdata topology_cpu_notifier =
{
.notifier_call = topology_cpu_callback,
};
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 9dc294a74953..18dd026f470d 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -74,7 +74,6 @@
#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/gfp.h>
-#include <linux/kthread.h>
#include <asm/uaccess.h>
@@ -211,7 +210,7 @@ static int do_lo_send_aops(struct loop_device *lo, struct bio_vec *bvec,
{
struct file *file = lo->lo_backing_file; /* kudos to NFsckingS */
struct address_space *mapping = file->f_mapping;
- struct address_space_operations *aops = mapping->a_ops;
+ const struct address_space_operations *aops = mapping->a_ops;
pgoff_t index;
unsigned offset, bv_offs;
int len, ret;
@@ -579,6 +578,8 @@ static int loop_thread(void *data)
struct loop_device *lo = data;
struct bio *bio;
+ daemonize("loop%d", lo->lo_number);
+
/*
* loop can be used in an encrypted device,
* hence, it mustn't be stopped at all
@@ -591,6 +592,11 @@ static int loop_thread(void *data)
lo->lo_state = Lo_bound;
lo->lo_pending = 1;
+ /*
+ * complete it, we are running
+ */
+ complete(&lo->lo_done);
+
for (;;) {
int pending;
@@ -623,6 +629,7 @@ static int loop_thread(void *data)
break;
}
+ complete(&lo->lo_done);
return 0;
}
@@ -739,7 +746,6 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
unsigned lo_blocksize;
int lo_flags = 0;
int error;
- struct task_struct *tsk;
loff_t size;
/* This is safe, since we have a reference from open(). */
@@ -778,7 +784,7 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
error = -EINVAL;
if (S_ISREG(inode->i_mode) || S_ISBLK(inode->i_mode)) {
- struct address_space_operations *aops = mapping->a_ops;
+ const struct address_space_operations *aops = mapping->a_ops;
/*
* If we can't read - sorry. If we only can't write - well,
* it's going to be read-only.
@@ -833,11 +839,10 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
set_blocksize(bdev, lo_blocksize);
- tsk = kthread_run(loop_thread, lo, "loop%d", lo->lo_number);
- if (IS_ERR(tsk)) {
- error = PTR_ERR(tsk);
+ error = kernel_thread(loop_thread, lo, CLONE_KERNEL);
+ if (error < 0)
goto out_putf;
- }
+ wait_for_completion(&lo->lo_done);
return 0;
out_putf:
@@ -893,9 +898,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
if (lo->lo_state != Lo_bound)
return -ENXIO;
- if (!lo->lo_thread)
- return -EINVAL;
-
if (lo->lo_refcnt > 1) /* we needed one fd for the ioctl */
return -EBUSY;
@@ -909,7 +911,7 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
complete(&lo->lo_bh_done);
spin_unlock_irq(&lo->lo_lock);
- kthread_stop(lo->lo_thread);
+ wait_for_completion(&lo->lo_done);
lo->lo_backing_file = NULL;
@@ -922,7 +924,6 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
lo->lo_sizelimit = 0;
lo->lo_encrypt_key_size = 0;
lo->lo_flags = 0;
- lo->lo_thread = NULL;
memset(lo->lo_encrypt_key, 0, LO_KEY_SIZE);
memset(lo->lo_crypt_name, 0, LO_NAME_SIZE);
memset(lo->lo_file_name, 0, LO_NAME_SIZE);
@@ -1287,6 +1288,7 @@ static int __init loop_init(void)
if (!lo->lo_queue)
goto out_mem4;
mutex_init(&lo->lo_ctl_mutex);
+ init_completion(&lo->lo_done);
init_completion(&lo->lo_bh_done);
lo->lo_number = i;
spin_lock_init(&lo->lo_lock);
diff --git a/drivers/block/paride/pf.c b/drivers/block/paride/pf.c
index 852b564e903a..1a9dee19efcf 100644
--- a/drivers/block/paride/pf.c
+++ b/drivers/block/paride/pf.c
@@ -707,7 +707,7 @@ static int pf_detect(void)
if (pi_init(pf->pi, 0, conf[D_PRT], conf[D_MOD],
conf[D_UNI], conf[D_PRO], conf[D_DLY],
pf_scratch, PI_PF, verbose, pf->name)) {
- if (!pf_probe(pf) && pf->disk) {
+ if (pf->disk && !pf_probe(pf)) {
pf->present = 1;
k++;
} else
diff --git a/drivers/block/rd.c b/drivers/block/rd.c
index 940bfd7951e5..0378da04cfa2 100644
--- a/drivers/block/rd.c
+++ b/drivers/block/rd.c
@@ -191,7 +191,7 @@ static int ramdisk_set_page_dirty(struct page *page)
return 0;
}
-static struct address_space_operations ramdisk_aops = {
+static const struct address_space_operations ramdisk_aops = {
.readpage = ramdisk_readpage,
.prepare_write = ramdisk_prepare_write,
.commit_write = ramdisk_commit_write,
diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c
index 2ae08b343b93..8144ce9f4df0 100644
--- a/drivers/block/sx8.c
+++ b/drivers/block/sx8.c
@@ -1694,9 +1694,10 @@ static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
DPRINTK("waiting for probe_comp\n");
wait_for_completion(&host->probe_comp);
- printk(KERN_INFO "%s: pci %s, ports %d, io %lx, irq %u, major %d\n",
+ printk(KERN_INFO "%s: pci %s, ports %d, io %llx, irq %u, major %d\n",
host->name, pci_name(pdev), (int) CARM_MAX_PORTS,
- pci_resource_start(pdev, 0), pdev->irq, host->major);
+ (unsigned long long)pci_resource_start(pdev, 0),
+ pdev->irq, host->major);
carm_host_id++;
pci_set_drvdata(pdev, host);
diff --git a/drivers/bluetooth/dtl1_cs.c b/drivers/bluetooth/dtl1_cs.c
index a71a240611e0..ed8dca84ff69 100644
--- a/drivers/bluetooth/dtl1_cs.c
+++ b/drivers/bluetooth/dtl1_cs.c
@@ -423,6 +423,9 @@ static int dtl1_hci_send_frame(struct sk_buff *skb)
nsh.len = skb->len;
s = bt_skb_alloc(NSHL + skb->len + 1, GFP_ATOMIC);
+ if (!s)
+ return -ENOMEM;
+
skb_reserve(s, NSHL);
memcpy(skb_put(s, skb->len), skb->data, skb->len);
if (skb->len & 0x0001)
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 63f28d169b36..c40e487d9f5c 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -62,6 +62,23 @@ config HW_CONSOLE
depends on VT && !S390 && !UML
default y
+config VT_HW_CONSOLE_BINDING
+ bool "Support for binding and unbinding console drivers"
+ depends on HW_CONSOLE
+ default n
+ ---help---
+ The virtual terminal is the device that interacts with the physical
+ terminal through console drivers. On these systems, at least one
+ console driver is loaded. In other configurations, additional console
+ drivers may be enabled, such as the framebuffer console. If more than
+ 1 console driver is enabled, setting this to 'y' will allow you to
+ select the console driver that will serve as the backend for the
+ virtual terminals.
+
+ See <file:Documentation/console/console.txt> for more
+ information. For framebuffer console users, please refer to
+ <file:Documentation/fb/fbcon.txt>.
+
config SERIAL_NONSTANDARD
bool "Non-standard serial port support"
---help---
@@ -670,20 +687,7 @@ config NWFLASH
If you're not sure, say N.
-config HW_RANDOM
- tristate "Intel/AMD/VIA HW Random Number Generator support"
- depends on (X86 || IA64) && PCI
- ---help---
- This driver provides kernel-side support for the Random Number
- Generator hardware found on Intel i8xx-based motherboards,
- AMD 76x-based motherboards, and Via Nehemiah CPUs.
-
- Provides a character driver, used to read() entropy data.
-
- To compile this driver as a module, choose M here: the
- module will be called hw_random.
-
- If unsure, say N.
+source "drivers/char/hw_random/Kconfig"
config NVRAM
tristate "/dev/nvram support"
@@ -935,12 +939,36 @@ config MWAVE
config SCx200_GPIO
tristate "NatSemi SCx200 GPIO Support"
depends on SCx200
+ select NSC_GPIO
help
Give userspace access to the GPIO pins on the National
Semiconductor SCx200 processors.
If compiled as a module, it will be called scx200_gpio.
+config PC8736x_GPIO
+ tristate "NatSemi PC8736x GPIO Support"
+ depends on X86
+ default SCx200_GPIO # mostly N
+ select NSC_GPIO # needed for support routines
+ help
+ Give userspace access to the GPIO pins on the National
+ Semiconductor PC-8736x (x=[03456]) SuperIO chip. The chip
+ has multiple functional units, inc several managed by
+ hwmon/pc87360 driver. Tested with PC-87366
+
+ If compiled as a module, it will be called pc8736x_gpio.
+
+config NSC_GPIO
+ tristate "NatSemi Base GPIO Support"
+ depends on X86_32
+ # selected by SCx200_GPIO and PC8736x_GPIO
+ # what about 2 selectors differing: m != y
+ help
+ Common support used (and needed) by scx200_gpio and
+ pc8736x_gpio drivers. If those drivers are built as
+ modules, this one will be too, named nsc_gpio
+
config CS5535_GPIO
tristate "AMD CS5535/CS5536 GPIO (Geode Companion Device)"
depends on X86_32
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index fb919bfb2824..6e0f4469d8bb 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -75,13 +75,15 @@ endif
obj-$(CONFIG_TOSHIBA) += toshiba.o
obj-$(CONFIG_I8K) += i8k.o
obj-$(CONFIG_DS1620) += ds1620.o
-obj-$(CONFIG_HW_RANDOM) += hw_random.o
+obj-$(CONFIG_HW_RANDOM) += hw_random/
obj-$(CONFIG_FTAPE) += ftape/
obj-$(CONFIG_COBALT_LCD) += lcd.o
obj-$(CONFIG_PPDEV) += ppdev.o
obj-$(CONFIG_NWBUTTON) += nwbutton.o
obj-$(CONFIG_NWFLASH) += nwflash.o
obj-$(CONFIG_SCx200_GPIO) += scx200_gpio.o
+obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o
+obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o
obj-$(CONFIG_CS5535_GPIO) += cs5535_gpio.o
obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o
obj-$(CONFIG_TANBAC_TB0219) += tb0219.o
diff --git a/drivers/char/agp/Kconfig b/drivers/char/agp/Kconfig
index 46685a540772..9826a399fa02 100644
--- a/drivers/char/agp/Kconfig
+++ b/drivers/char/agp/Kconfig
@@ -55,9 +55,9 @@ config AGP_AMD
X on AMD Irongate, 761, and 762 chipsets.
config AGP_AMD64
- tristate "AMD Opteron/Athlon64 on-CPU GART support" if !GART_IOMMU
+ tristate "AMD Opteron/Athlon64 on-CPU GART support" if !IOMMU
depends on AGP && X86
- default y if GART_IOMMU
+ default y if IOMMU
help
This option gives you AGP support for the GLX component of
X using the on-CPU northbridge of the AMD Athlon64/Opteron CPUs.
diff --git a/drivers/char/agp/amd-k7-agp.c b/drivers/char/agp/amd-k7-agp.c
index 1f776651ac64..51d0d562d01e 100644
--- a/drivers/char/agp/amd-k7-agp.c
+++ b/drivers/char/agp/amd-k7-agp.c
@@ -118,7 +118,7 @@ static int amd_create_gatt_pages(int nr_tables)
return retval;
}
-/* Since we don't need contigious memory we just try
+/* Since we don't need contiguous memory we just try
* to get the gatt table once
*/
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index ac3c33a2e37d..f690ee8cb732 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -15,11 +15,9 @@
#include <linux/agp_backend.h>
#include <linux/mmzone.h>
#include <asm/page.h> /* PAGE_SIZE */
+#include <asm/k8.h>
#include "agp.h"
-/* Will need to be increased if AMD64 ever goes >8-way. */
-#define MAX_HAMMER_GARTS 8
-
/* PTE bits. */
#define GPTE_VALID 1
#define GPTE_COHERENT 2
@@ -53,28 +51,12 @@
#define ULI_X86_64_HTT_FEA_REG 0x50
#define ULI_X86_64_ENU_SCR_REG 0x54
-static int nr_garts;
-static struct pci_dev * hammers[MAX_HAMMER_GARTS];
-
static struct resource *aperture_resource;
static int __initdata agp_try_unsupported = 1;
-#define for_each_nb() for(gart_iterator=0;gart_iterator<nr_garts;gart_iterator++)
-
-static void flush_amd64_tlb(struct pci_dev *dev)
-{
- u32 tmp;
-
- pci_read_config_dword (dev, AMD64_GARTCACHECTL, &tmp);
- tmp |= INVGART;
- pci_write_config_dword (dev, AMD64_GARTCACHECTL, tmp);
-}
-
static void amd64_tlbflush(struct agp_memory *temp)
{
- int gart_iterator;
- for_each_nb()
- flush_amd64_tlb(hammers[gart_iterator]);
+ k8_flush_garts();
}
static int amd64_insert_memory(struct agp_memory *mem, off_t pg_start, int type)
@@ -153,7 +135,7 @@ static int amd64_fetch_size(void)
u32 temp;
struct aper_size_info_32 *values;
- dev = hammers[0];
+ dev = k8_northbridges[0];
if (dev==NULL)
return 0;
@@ -201,9 +183,6 @@ static u64 amd64_configure (struct pci_dev *hammer, u64 gatt_table)
tmp &= ~(DISGARTCPU | DISGARTIO);
pci_write_config_dword(hammer, AMD64_GARTAPERTURECTL, tmp);
- /* keep CPU's coherent. */
- flush_amd64_tlb (hammer);
-
return aper_base;
}
@@ -222,13 +201,14 @@ static struct aper_size_info_32 amd_8151_sizes[7] =
static int amd_8151_configure(void)
{
unsigned long gatt_bus = virt_to_gart(agp_bridge->gatt_table_real);
- int gart_iterator;
+ int i;
/* Configure AGP regs in each x86-64 host bridge. */
- for_each_nb() {
+ for (i = 0; i < num_k8_northbridges; i++) {
agp_bridge->gart_bus_addr =
- amd64_configure(hammers[gart_iterator],gatt_bus);
+ amd64_configure(k8_northbridges[i], gatt_bus);
}
+ k8_flush_garts();
return 0;
}
@@ -236,12 +216,13 @@ static int amd_8151_configure(void)
static void amd64_cleanup(void)
{
u32 tmp;
- int gart_iterator;
- for_each_nb() {
+ int i;
+ for (i = 0; i < num_k8_northbridges; i++) {
+ struct pci_dev *dev = k8_northbridges[i];
/* disable gart translation */
- pci_read_config_dword (hammers[gart_iterator], AMD64_GARTAPERTURECTL, &tmp);
+ pci_read_config_dword (dev, AMD64_GARTAPERTURECTL, &tmp);
tmp &= ~AMD64_GARTEN;
- pci_write_config_dword (hammers[gart_iterator], AMD64_GARTAPERTURECTL, tmp);
+ pci_write_config_dword (dev, AMD64_GARTAPERTURECTL, tmp);
}
}
@@ -311,7 +292,7 @@ static int __devinit aperture_valid(u64 aper, u32 size)
/*
* W*s centric BIOS sometimes only set up the aperture in the AGP
* bridge, not the northbridge. On AMD64 this is handled early
- * in aperture.c, but when GART_IOMMU is not enabled or we run
+ * in aperture.c, but when IOMMU is not enabled or we run
* on a 32bit kernel this needs to be redone.
* Unfortunately it is impossible to fix the aperture here because it's too late
* to allocate that much memory. But at least error out cleanly instead of
@@ -361,17 +342,15 @@ static __devinit int fix_northbridge(struct pci_dev *nb, struct pci_dev *agp,
static __devinit int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
{
- struct pci_dev *loop_dev = NULL;
- int i = 0;
-
- /* cache pci_devs of northbridges. */
- while ((loop_dev = pci_get_device(PCI_VENDOR_ID_AMD, 0x1103, loop_dev))
- != NULL) {
- if (i == MAX_HAMMER_GARTS) {
- printk(KERN_ERR PFX "Too many northbridges for AGP\n");
- return -1;
- }
- if (fix_northbridge(loop_dev, pdev, cap_ptr) < 0) {
+ int i;
+
+ if (cache_k8_northbridges() < 0)
+ return -ENODEV;
+
+ i = 0;
+ for (i = 0; i < num_k8_northbridges; i++) {
+ struct pci_dev *dev = k8_northbridges[i];
+ if (fix_northbridge(dev, pdev, cap_ptr) < 0) {
printk(KERN_ERR PFX "No usable aperture found.\n");
#ifdef __x86_64__
/* should port this to i386 */
@@ -379,10 +358,8 @@ static __devinit int cache_nbs (struct pci_dev *pdev, u32 cap_ptr)
#endif
return -1;
}
- hammers[i++] = loop_dev;
}
- nr_garts = i;
- return i == 0 ? -1 : 0;
+ return 0;
}
/* Handle AMD 8151 quirks */
@@ -450,7 +427,7 @@ static int __devinit uli_agp_init(struct pci_dev *pdev)
}
/* shadow x86-64 registers into ULi registers */
- pci_read_config_dword (hammers[0], AMD64_GARTAPERTUREBASE, &httfea);
+ pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &httfea);
/* if x86-64 aperture base is beyond 4G, exit here */
if ((httfea & 0x7fff) >> (32 - 25))
@@ -513,7 +490,7 @@ static int __devinit nforce3_agp_init(struct pci_dev *pdev)
pci_write_config_dword(dev1, NVIDIA_X86_64_1_APSIZE, tmp);
/* shadow x86-64 registers into NVIDIA registers */
- pci_read_config_dword (hammers[0], AMD64_GARTAPERTUREBASE, &apbase);
+ pci_read_config_dword (k8_northbridges[0], AMD64_GARTAPERTUREBASE, &apbase);
/* if x86-64 aperture base is beyond 4G, exit here */
if ( (apbase & 0x7fff) >> (32 - 25) ) {
@@ -754,10 +731,6 @@ static struct pci_driver agp_amd64_pci_driver = {
int __init agp_amd64_init(void)
{
int err = 0;
- static struct pci_device_id amd64nb[] = {
- { PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x1103) },
- { },
- };
if (agp_off)
return -EINVAL;
@@ -774,7 +747,7 @@ int __init agp_amd64_init(void)
}
/* First check that we have at least one AMD64 NB */
- if (!pci_dev_present(amd64nb))
+ if (!pci_dev_present(k8_nb_ids))
return -ENODEV;
/* Look for any AGP bridge */
@@ -802,7 +775,7 @@ static void __exit agp_amd64_cleanup(void)
/* On AMD64 the PCI driver needs to initialize this driver early
for the IOMMU, so it has to be called via a backdoor. */
-#ifndef CONFIG_GART_IOMMU
+#ifndef CONFIG_IOMMU
module_init(agp_amd64_init);
module_exit(agp_amd64_cleanup);
#endif
diff --git a/drivers/char/agp/ati-agp.c b/drivers/char/agp/ati-agp.c
index 06fd10ba0c5e..160564345993 100644
--- a/drivers/char/agp/ati-agp.c
+++ b/drivers/char/agp/ati-agp.c
@@ -261,7 +261,7 @@ static int agp_ati_suspend(struct pci_dev *dev, pm_message_t state)
#endif
/*
- *Since we don't need contigious memory we just try
+ *Since we don't need contiguous memory we just try
* to get the gatt table once
*/
diff --git a/drivers/char/agp/efficeon-agp.c b/drivers/char/agp/efficeon-agp.c
index 86a966b65236..b788b0a3bbf3 100644
--- a/drivers/char/agp/efficeon-agp.c
+++ b/drivers/char/agp/efficeon-agp.c
@@ -177,7 +177,7 @@ static int efficeon_free_gatt_table(struct agp_bridge_data *bridge)
/*
- * Since we don't need contigious memory we just try
+ * Since we don't need contiguous memory we just try
* to get the gatt table once
*/
diff --git a/drivers/char/agp/sgi-agp.c b/drivers/char/agp/sgi-agp.c
index cfa7922cb431..d73be4c2db8a 100644
--- a/drivers/char/agp/sgi-agp.c
+++ b/drivers/char/agp/sgi-agp.c
@@ -329,9 +329,8 @@ static int __devinit agp_sgi_init(void)
static void __devexit agp_sgi_cleanup(void)
{
- if (sgi_tioca_agp_bridges)
- kfree(sgi_tioca_agp_bridges);
- sgi_tioca_agp_bridges=NULL;
+ kfree(sgi_tioca_agp_bridges);
+ sgi_tioca_agp_bridges = NULL;
}
module_init(agp_sgi_init);
diff --git a/drivers/char/applicom.c b/drivers/char/applicom.c
index 9275d5e52e6d..72fb60765c45 100644
--- a/drivers/char/applicom.c
+++ b/drivers/char/applicom.c
@@ -209,13 +209,16 @@ static int __init applicom_init(void)
RamIO = ioremap(dev->resource[0].start, LEN_RAM_IO);
if (!RamIO) {
- printk(KERN_INFO "ac.o: Failed to ioremap PCI memory space at 0x%lx\n", dev->resource[0].start);
+ printk(KERN_INFO "ac.o: Failed to ioremap PCI memory "
+ "space at 0x%llx\n",
+ (unsigned long long)dev->resource[0].start);
pci_disable_device(dev);
return -EIO;
}
- printk(KERN_INFO "Applicom %s found at mem 0x%lx, irq %d\n",
- applicom_pci_devnames[dev->device-1], dev->resource[0].start,
+ printk(KERN_INFO "Applicom %s found at mem 0x%llx, irq %d\n",
+ applicom_pci_devnames[dev->device-1],
+ (unsigned long long)dev->resource[0].start,
dev->irq);
boardno = ac_register_board(dev->resource[0].start, RamIO,0);
diff --git a/drivers/char/drm/drm_memory_debug.h b/drivers/char/drm/drm_memory_debug.h
index 6543b9a14c42..d117cc997192 100644
--- a/drivers/char/drm/drm_memory_debug.h
+++ b/drivers/char/drm/drm_memory_debug.h
@@ -43,7 +43,7 @@ typedef struct drm_mem_stats {
unsigned long bytes_freed;
} drm_mem_stats_t;
-static spinlock_t drm_mem_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(drm_mem_lock);
static unsigned long drm_ram_available = 0; /* In pages */
static unsigned long drm_ram_used = 0;
static drm_mem_stats_t drm_mem_stats[] =
diff --git a/drivers/char/drm/via_dmablit.c b/drivers/char/drm/via_dmablit.c
index b7f17457b424..78a81a4a99c5 100644
--- a/drivers/char/drm/via_dmablit.c
+++ b/drivers/char/drm/via_dmablit.c
@@ -557,7 +557,7 @@ via_init_dmablit(drm_device_t *dev)
blitq->num_outstanding = 0;
blitq->is_active = 0;
blitq->aborting = 0;
- blitq->blit_lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&blitq->blit_lock);
for (j=0; j<VIA_NUM_BLIT_SLOTS; ++j) {
DRM_INIT_WAITQUEUE(blitq->blit_queue + j);
}
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 9cad8501d62c..dc0602ae8503 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -80,7 +80,7 @@ static int invalid_lilo_config;
/* The ISA boards do window flipping into the same spaces so its only sane
with a single lock. It's still pretty efficient */
-static spinlock_t epca_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(epca_lock);
/* -----------------------------------------------------------------------
MAXBOARDS is typically 12, but ISA and EISA cards are restricted to
diff --git a/drivers/char/hangcheck-timer.c b/drivers/char/hangcheck-timer.c
index ac626418b329..d69f2ad9a67d 100644
--- a/drivers/char/hangcheck-timer.c
+++ b/drivers/char/hangcheck-timer.c
@@ -117,12 +117,12 @@ __setup("hcheck_reboot", hangcheck_parse_reboot);
__setup("hcheck_dump_tasks", hangcheck_parse_dump_tasks);
#endif /* not MODULE */
-#if defined(CONFIG_X86) || defined(CONFIG_S390)
+#if defined(CONFIG_X86_64) || defined(CONFIG_S390)
# define HAVE_MONOTONIC
# define TIMER_FREQ 1000000000ULL
#elif defined(CONFIG_IA64)
# define TIMER_FREQ ((unsigned long long)local_cpu_data->itc_freq)
-#elif defined(CONFIG_PPC64)
+#else
# define TIMER_FREQ (HZ*loops_per_jiffy)
#endif
diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c
index 8d97b3911293..afa26b65dac3 100644
--- a/drivers/char/hvcs.c
+++ b/drivers/char/hvcs.c
@@ -1320,11 +1320,12 @@ static struct tty_operations hvcs_ops = {
static int hvcs_alloc_index_list(int n)
{
int i;
+
hvcs_index_list = kmalloc(n * sizeof(hvcs_index_count),GFP_KERNEL);
if (!hvcs_index_list)
return -ENOMEM;
hvcs_index_count = n;
- for(i = 0; i < hvcs_index_count; i++)
+ for (i = 0; i < hvcs_index_count; i++)
hvcs_index_list[i] = -1;
return 0;
}
@@ -1332,11 +1333,9 @@ static int hvcs_alloc_index_list(int n)
static void hvcs_free_index_list(void)
{
/* Paranoia check to be thorough. */
- if (hvcs_index_list) {
- kfree(hvcs_index_list);
- hvcs_index_list = NULL;
- hvcs_index_count = 0;
- }
+ kfree(hvcs_index_list);
+ hvcs_index_list = NULL;
+ hvcs_index_count = 0;
}
static int __init hvcs_module_init(void)
diff --git a/drivers/char/hw_random.c b/drivers/char/hw_random.c
deleted file mode 100644
index 29dc87e59020..000000000000
--- a/drivers/char/hw_random.c
+++ /dev/null
@@ -1,698 +0,0 @@
-/*
- Added support for the AMD Geode LX RNG
- (c) Copyright 2004-2005 Advanced Micro Devices, Inc.
-
- derived from
-
- Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
- (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
-
- derived from
-
- Hardware driver for the AMD 768 Random Number Generator (RNG)
- (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
-
- derived from
-
- Hardware driver for Intel i810 Random Number Generator (RNG)
- Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
- Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
-
- Please read Documentation/hw_random.txt for details on use.
-
- ----------------------------------------------------------
- This software may be used and distributed according to the terms
- of the GNU General Public License, incorporated herein by reference.
-
- */
-
-
-#include <linux/module.h>
-#include <linux/kernel.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/pci.h>
-#include <linux/interrupt.h>
-#include <linux/spinlock.h>
-#include <linux/random.h>
-#include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
-#include <linux/mm.h>
-#include <linux/delay.h>
-
-#ifdef __i386__
-#include <asm/msr.h>
-#include <asm/cpufeature.h>
-#endif
-
-#include <asm/io.h>
-#include <asm/uaccess.h>
-
-
-/*
- * core module and version information
- */
-#define RNG_VERSION "1.0.0"
-#define RNG_MODULE_NAME "hw_random"
-#define RNG_DRIVER_NAME RNG_MODULE_NAME " hardware driver " RNG_VERSION
-#define PFX RNG_MODULE_NAME ": "
-
-
-/*
- * debugging macros
- */
-
-/* pr_debug() collapses to a no-op if DEBUG is not defined */
-#define DPRINTK(fmt, args...) pr_debug(PFX "%s: " fmt, __FUNCTION__ , ## args)
-
-
-#undef RNG_NDEBUG /* define to enable lightweight runtime checks */
-#ifdef RNG_NDEBUG
-#define assert(expr) \
- if(!(expr)) { \
- printk(KERN_DEBUG PFX "Assertion failed! %s,%s,%s," \
- "line=%d\n", #expr, __FILE__, __FUNCTION__, __LINE__); \
- }
-#else
-#define assert(expr)
-#endif
-
-#define RNG_MISCDEV_MINOR 183 /* official */
-
-static int rng_dev_open (struct inode *inode, struct file *filp);
-static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
- loff_t * offp);
-
-static int __init intel_init (struct pci_dev *dev);
-static void intel_cleanup(void);
-static unsigned int intel_data_present (void);
-static u32 intel_data_read (void);
-
-static int __init amd_init (struct pci_dev *dev);
-static void amd_cleanup(void);
-static unsigned int amd_data_present (void);
-static u32 amd_data_read (void);
-
-#ifdef __i386__
-static int __init via_init(struct pci_dev *dev);
-static void via_cleanup(void);
-static unsigned int via_data_present (void);
-static u32 via_data_read (void);
-#endif
-
-static int __init geode_init(struct pci_dev *dev);
-static void geode_cleanup(void);
-static unsigned int geode_data_present (void);
-static u32 geode_data_read (void);
-
-struct rng_operations {
- int (*init) (struct pci_dev *dev);
- void (*cleanup) (void);
- unsigned int (*data_present) (void);
- u32 (*data_read) (void);
- unsigned int n_bytes; /* number of bytes per ->data_read */
-};
-static struct rng_operations *rng_ops;
-
-static struct file_operations rng_chrdev_ops = {
- .owner = THIS_MODULE,
- .open = rng_dev_open,
- .read = rng_dev_read,
-};
-
-
-static struct miscdevice rng_miscdev = {
- RNG_MISCDEV_MINOR,
- RNG_MODULE_NAME,
- &rng_chrdev_ops,
-};
-
-enum {
- rng_hw_none,
- rng_hw_intel,
- rng_hw_amd,
-#ifdef __i386__
- rng_hw_via,
-#endif
- rng_hw_geode,
-};
-
-static struct rng_operations rng_vendor_ops[] = {
- /* rng_hw_none */
- { },
-
- /* rng_hw_intel */
- { intel_init, intel_cleanup, intel_data_present,
- intel_data_read, 1 },
-
- /* rng_hw_amd */
- { amd_init, amd_cleanup, amd_data_present, amd_data_read, 4 },
-
-#ifdef __i386__
- /* rng_hw_via */
- { via_init, via_cleanup, via_data_present, via_data_read, 1 },
-#endif
-
- /* rng_hw_geode */
- { geode_init, geode_cleanup, geode_data_present, geode_data_read, 4 }
-};
-
-/*
- * Data for PCI driver interface
- *
- * This data only exists for exporting the supported
- * PCI ids via MODULE_DEVICE_TABLE. We do not actually
- * register a pci_driver, because someone else might one day
- * want to register another driver on the same PCI id.
- */
-static struct pci_device_id rng_pci_tbl[] = {
- { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd },
- { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_amd },
-
- { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
- { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
- { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
- { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
- { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
- { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_intel },
-
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, rng_hw_geode },
-
- { 0, }, /* terminate list */
-};
-MODULE_DEVICE_TABLE (pci, rng_pci_tbl);
-
-
-/***********************************************************************
- *
- * Intel RNG operations
- *
- */
-
-/*
- * RNG registers (offsets from rng_mem)
- */
-#define INTEL_RNG_HW_STATUS 0
-#define INTEL_RNG_PRESENT 0x40
-#define INTEL_RNG_ENABLED 0x01
-#define INTEL_RNG_STATUS 1
-#define INTEL_RNG_DATA_PRESENT 0x01
-#define INTEL_RNG_DATA 2
-
-/*
- * Magic address at which Intel PCI bridges locate the RNG
- */
-#define INTEL_RNG_ADDR 0xFFBC015F
-#define INTEL_RNG_ADDR_LEN 3
-
-/* token to our ioremap'd RNG register area */
-static void __iomem *rng_mem;
-
-static inline u8 intel_hwstatus (void)
-{
- assert (rng_mem != NULL);
- return readb (rng_mem + INTEL_RNG_HW_STATUS);
-}
-
-static inline u8 intel_hwstatus_set (u8 hw_status)
-{
- assert (rng_mem != NULL);
- writeb (hw_status, rng_mem + INTEL_RNG_HW_STATUS);
- return intel_hwstatus ();
-}
-
-static unsigned int intel_data_present(void)
-{
- assert (rng_mem != NULL);
-
- return (readb (rng_mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT) ?
- 1 : 0;
-}
-
-static u32 intel_data_read(void)
-{
- assert (rng_mem != NULL);
-
- return readb (rng_mem + INTEL_RNG_DATA);
-}
-
-static int __init intel_init (struct pci_dev *dev)
-{
- int rc;
- u8 hw_status;
-
- DPRINTK ("ENTER\n");
-
- rng_mem = ioremap (INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
- if (rng_mem == NULL) {
- printk (KERN_ERR PFX "cannot ioremap RNG Memory\n");
- rc = -EBUSY;
- goto err_out;
- }
-
- /* Check for Intel 82802 */
- hw_status = intel_hwstatus ();
- if ((hw_status & INTEL_RNG_PRESENT) == 0) {
- printk (KERN_ERR PFX "RNG not detected\n");
- rc = -ENODEV;
- goto err_out_free_map;
- }
-
- /* turn RNG h/w on, if it's off */
- if ((hw_status & INTEL_RNG_ENABLED) == 0)
- hw_status = intel_hwstatus_set (hw_status | INTEL_RNG_ENABLED);
- if ((hw_status & INTEL_RNG_ENABLED) == 0) {
- printk (KERN_ERR PFX "cannot enable RNG, aborting\n");
- rc = -EIO;
- goto err_out_free_map;
- }
-
- DPRINTK ("EXIT, returning 0\n");
- return 0;
-
-err_out_free_map:
- iounmap (rng_mem);
- rng_mem = NULL;
-err_out:
- DPRINTK ("EXIT, returning %d\n", rc);
- return rc;
-}
-
-static void intel_cleanup(void)
-{
- u8 hw_status;
-
- hw_status = intel_hwstatus ();
- if (hw_status & INTEL_RNG_ENABLED)
- intel_hwstatus_set (hw_status & ~INTEL_RNG_ENABLED);
- else
- printk(KERN_WARNING PFX "unusual: RNG already disabled\n");
- iounmap(rng_mem);
- rng_mem = NULL;
-}
-
-/***********************************************************************
- *
- * AMD RNG operations
- *
- */
-
-static u32 pmbase; /* PMxx I/O base */
-static struct pci_dev *amd_dev;
-
-static unsigned int amd_data_present (void)
-{
- return inl(pmbase + 0xF4) & 1;
-}
-
-
-static u32 amd_data_read (void)
-{
- return inl(pmbase + 0xF0);
-}
-
-static int __init amd_init (struct pci_dev *dev)
-{
- int rc;
- u8 rnen;
-
- DPRINTK ("ENTER\n");
-
- pci_read_config_dword(dev, 0x58, &pmbase);
-
- pmbase &= 0x0000FF00;
-
- if (pmbase == 0)
- {
- printk (KERN_ERR PFX "power management base not set\n");
- rc = -EIO;
- goto err_out;
- }
-
- pci_read_config_byte(dev, 0x40, &rnen);
- rnen |= (1 << 7); /* RNG on */
- pci_write_config_byte(dev, 0x40, rnen);
-
- pci_read_config_byte(dev, 0x41, &rnen);
- rnen |= (1 << 7); /* PMIO enable */
- pci_write_config_byte(dev, 0x41, rnen);
-
- pr_info( PFX "AMD768 system management I/O registers at 0x%X.\n",
- pmbase);
-
- amd_dev = dev;
-
- DPRINTK ("EXIT, returning 0\n");
- return 0;
-
-err_out:
- DPRINTK ("EXIT, returning %d\n", rc);
- return rc;
-}
-
-static void amd_cleanup(void)
-{
- u8 rnen;
-
- pci_read_config_byte(amd_dev, 0x40, &rnen);
- rnen &= ~(1 << 7); /* RNG off */
- pci_write_config_byte(amd_dev, 0x40, rnen);
-
- /* FIXME: twiddle pmio, also? */
-}
-
-#ifdef __i386__
-/***********************************************************************
- *
- * VIA RNG operations
- *
- */
-
-enum {
- VIA_STRFILT_CNT_SHIFT = 16,
- VIA_STRFILT_FAIL = (1 << 15),
- VIA_STRFILT_ENABLE = (1 << 14),
- VIA_RAWBITS_ENABLE = (1 << 13),
- VIA_RNG_ENABLE = (1 << 6),
- VIA_XSTORE_CNT_MASK = 0x0F,
-
- VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */
- VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */
- VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF,
- VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */
- VIA_RNG_CHUNK_2_MASK = 0xFFFF,
- VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */
- VIA_RNG_CHUNK_1_MASK = 0xFF,
-};
-
-static u32 via_rng_datum;
-
-/*
- * Investigate using the 'rep' prefix to obtain 32 bits of random data
- * in one insn. The upside is potentially better performance. The
- * downside is that the instruction becomes no longer atomic. Due to
- * this, just like familiar issues with /dev/random itself, the worst
- * case of a 'rep xstore' could potentially pause a cpu for an
- * unreasonably long time. In practice, this condition would likely
- * only occur when the hardware is failing. (or so we hope :))
- *
- * Another possible performance boost may come from simply buffering
- * until we have 4 bytes, thus returning a u32 at a time,
- * instead of the current u8-at-a-time.
- */
-
-static inline u32 xstore(u32 *addr, u32 edx_in)
-{
- u32 eax_out;
-
- asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
- :"=m"(*addr), "=a"(eax_out)
- :"D"(addr), "d"(edx_in));
-
- return eax_out;
-}
-
-static unsigned int via_data_present(void)
-{
- u32 bytes_out;
-
- /* We choose the recommended 1-byte-per-instruction RNG rate,
- * for greater randomness at the expense of speed. Larger
- * values 2, 4, or 8 bytes-per-instruction yield greater
- * speed at lesser randomness.
- *
- * If you change this to another VIA_CHUNK_n, you must also
- * change the ->n_bytes values in rng_vendor_ops[] tables.
- * VIA_CHUNK_8 requires further code changes.
- *
- * A copy of MSR_VIA_RNG is placed in eax_out when xstore
- * completes.
- */
- via_rng_datum = 0; /* paranoia, not really necessary */
- bytes_out = xstore(&via_rng_datum, VIA_RNG_CHUNK_1) & VIA_XSTORE_CNT_MASK;
- if (bytes_out == 0)
- return 0;
-
- return 1;
-}
-
-static u32 via_data_read(void)
-{
- return via_rng_datum;
-}
-
-static int __init via_init(struct pci_dev *dev)
-{
- u32 lo, hi, old_lo;
-
- /* Control the RNG via MSR. Tread lightly and pay very close
- * close attention to values written, as the reserved fields
- * are documented to be "undefined and unpredictable"; but it
- * does not say to write them as zero, so I make a guess that
- * we restore the values we find in the register.
- */
- rdmsr(MSR_VIA_RNG, lo, hi);
-
- old_lo = lo;
- lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT);
- lo &= ~VIA_XSTORE_CNT_MASK;
- lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE);
- lo |= VIA_RNG_ENABLE;
-
- if (lo != old_lo)
- wrmsr(MSR_VIA_RNG, lo, hi);
-
- /* perhaps-unnecessary sanity check; remove after testing if
- unneeded */
- rdmsr(MSR_VIA_RNG, lo, hi);
- if ((lo & VIA_RNG_ENABLE) == 0) {
- printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n");
- return -ENODEV;
- }
-
- return 0;
-}
-
-static void via_cleanup(void)
-{
- /* do nothing */
-}
-#endif
-
-/***********************************************************************
- *
- * AMD Geode RNG operations
- *
- */
-
-static void __iomem *geode_rng_base = NULL;
-
-#define GEODE_RNG_DATA_REG 0x50
-#define GEODE_RNG_STATUS_REG 0x54
-
-static u32 geode_data_read(void)
-{
- u32 val;
-
- assert(geode_rng_base != NULL);
- val = readl(geode_rng_base + GEODE_RNG_DATA_REG);
- return val;
-}
-
-static unsigned int geode_data_present(void)
-{
- u32 val;
-
- assert(geode_rng_base != NULL);
- val = readl(geode_rng_base + GEODE_RNG_STATUS_REG);
- return val;
-}
-
-static void geode_cleanup(void)
-{
- iounmap(geode_rng_base);
- geode_rng_base = NULL;
-}
-
-static int geode_init(struct pci_dev *dev)
-{
- unsigned long rng_base = pci_resource_start(dev, 0);
-
- if (rng_base == 0)
- return 1;
-
- geode_rng_base = ioremap(rng_base, 0x58);
-
- if (geode_rng_base == NULL) {
- printk(KERN_ERR PFX "Cannot ioremap RNG memory\n");
- return -EBUSY;
- }
-
- return 0;
-}
-
-/***********************************************************************
- *
- * /dev/hwrandom character device handling (major 10, minor 183)
- *
- */
-
-static int rng_dev_open (struct inode *inode, struct file *filp)
-{
- /* enforce read-only access to this chrdev */
- if ((filp->f_mode & FMODE_READ) == 0)
- return -EINVAL;
- if (filp->f_mode & FMODE_WRITE)
- return -EINVAL;
-
- return 0;
-}
-
-
-static ssize_t rng_dev_read (struct file *filp, char __user *buf, size_t size,
- loff_t * offp)
-{
- static DEFINE_SPINLOCK(rng_lock);
- unsigned int have_data;
- u32 data = 0;
- ssize_t ret = 0;
-
- while (size) {
- spin_lock(&rng_lock);
-
- have_data = 0;
- if (rng_ops->data_present()) {
- data = rng_ops->data_read();
- have_data = rng_ops->n_bytes;
- }
-
- spin_unlock (&rng_lock);
-
- while (have_data && size) {
- if (put_user((u8)data, buf++)) {
- ret = ret ? : -EFAULT;
- break;
- }
- size--;
- ret++;
- have_data--;
- data>>=8;
- }
-
- if (filp->f_flags & O_NONBLOCK)
- return ret ? : -EAGAIN;
-
- if(need_resched())
- schedule_timeout_interruptible(1);
- else
- udelay(200); /* FIXME: We could poll for 250uS ?? */
-
- if (signal_pending (current))
- return ret ? : -ERESTARTSYS;
- }
- return ret;
-}
-
-
-
-/*
- * rng_init_one - look for and attempt to init a single RNG
- */
-static int __init rng_init_one (struct pci_dev *dev)
-{
- int rc;
-
- DPRINTK ("ENTER\n");
-
- assert(rng_ops != NULL);
-
- rc = rng_ops->init(dev);
- if (rc)
- goto err_out;
-
- rc = misc_register (&rng_miscdev);
- if (rc) {
- printk (KERN_ERR PFX "misc device register failed\n");
- goto err_out_cleanup_hw;
- }
-
- DPRINTK ("EXIT, returning 0\n");
- return 0;
-
-err_out_cleanup_hw:
- rng_ops->cleanup();
-err_out:
- DPRINTK ("EXIT, returning %d\n", rc);
- return rc;
-}
-
-
-
-MODULE_AUTHOR("The Linux Kernel team");
-MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
-MODULE_LICENSE("GPL");
-
-
-/*
- * rng_init - initialize RNG module
- */
-static int __init rng_init (void)
-{
- int rc;
- struct pci_dev *pdev = NULL;
- const struct pci_device_id *ent;
-
- DPRINTK ("ENTER\n");
-
- /* Probe for Intel, AMD, Geode RNGs */
- for_each_pci_dev(pdev) {
- ent = pci_match_id(rng_pci_tbl, pdev);
- if (ent) {
- rng_ops = &rng_vendor_ops[ent->driver_data];
- goto match;
- }
- }
-
-#ifdef __i386__
- /* Probe for VIA RNG */
- if (cpu_has_xstore) {
- rng_ops = &rng_vendor_ops[rng_hw_via];
- pdev = NULL;
- goto match;
- }
-#endif
-
- DPRINTK ("EXIT, returning -ENODEV\n");
- return -ENODEV;
-
-match:
- rc = rng_init_one (pdev);
- if (rc)
- return rc;
-
- pr_info( RNG_DRIVER_NAME " loaded\n");
-
- DPRINTK ("EXIT, returning 0\n");
- return 0;
-}
-
-
-/*
- * rng_init - shutdown RNG module
- */
-static void __exit rng_cleanup (void)
-{
- DPRINTK ("ENTER\n");
-
- misc_deregister (&rng_miscdev);
-
- if (rng_ops->cleanup)
- rng_ops->cleanup();
-
- DPRINTK ("EXIT\n");
-}
-
-
-module_init (rng_init);
-module_exit (rng_cleanup);
diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig
new file mode 100644
index 000000000000..9f7635f75178
--- /dev/null
+++ b/drivers/char/hw_random/Kconfig
@@ -0,0 +1,90 @@
+#
+# Hardware Random Number Generator (RNG) configuration
+#
+
+config HW_RANDOM
+ bool "Hardware Random Number Generator Core support"
+ default y
+ ---help---
+ Hardware Random Number Generator Core infrastructure.
+
+ If unsure, say Y.
+
+config HW_RANDOM_INTEL
+ tristate "Intel HW Random Number Generator support"
+ depends on HW_RANDOM && (X86 || IA64) && PCI
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on Intel i8xx-based motherboards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called intel-rng.
+
+ If unsure, say Y.
+
+config HW_RANDOM_AMD
+ tristate "AMD HW Random Number Generator support"
+ depends on HW_RANDOM && X86 && PCI
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on AMD 76x-based motherboards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amd-rng.
+
+ If unsure, say Y.
+
+config HW_RANDOM_GEODE
+ tristate "AMD Geode HW Random Number Generator support"
+ depends on HW_RANDOM && X86 && PCI
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on the AMD Geode LX.
+
+ To compile this driver as a module, choose M here: the
+ module will be called geode-rng.
+
+ If unsure, say Y.
+
+config HW_RANDOM_VIA
+ tristate "VIA HW Random Number Generator support"
+ depends on HW_RANDOM && X86_32
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on VIA based motherboards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called via-rng.
+
+ If unsure, say Y.
+
+config HW_RANDOM_IXP4XX
+ tristate "Intel IXP4xx NPU HW Random Number Generator support"
+ depends on HW_RANDOM && ARCH_IXP4XX
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random
+ Number Generator hardware found on the Intel IXP4xx NPU.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ixp4xx-rng.
+
+ If unsure, say Y.
+
+config HW_RANDOM_OMAP
+ tristate "OMAP Random Number Generator support"
+ depends on HW_RANDOM && (ARCH_OMAP16XX || ARCH_OMAP24XX)
+ default y
+ ---help---
+ This driver provides kernel-side support for the Random Number
+ Generator hardware found on OMAP16xx and OMAP24xx multimedia
+ processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap-rng.
+
+ If unsure, say Y.
diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile
new file mode 100644
index 000000000000..e263ae96f940
--- /dev/null
+++ b/drivers/char/hw_random/Makefile
@@ -0,0 +1,11 @@
+#
+# Makefile for HW Random Number Generator (RNG) device drivers.
+#
+
+obj-$(CONFIG_HW_RANDOM) += core.o
+obj-$(CONFIG_HW_RANDOM_INTEL) += intel-rng.o
+obj-$(CONFIG_HW_RANDOM_AMD) += amd-rng.o
+obj-$(CONFIG_HW_RANDOM_GEODE) += geode-rng.o
+obj-$(CONFIG_HW_RANDOM_VIA) += via-rng.o
+obj-$(CONFIG_HW_RANDOM_IXP4XX) += ixp4xx-rng.o
+obj-$(CONFIG_HW_RANDOM_OMAP) += omap-rng.o
diff --git a/drivers/char/hw_random/amd-rng.c b/drivers/char/hw_random/amd-rng.c
new file mode 100644
index 000000000000..71e4e0f3fd54
--- /dev/null
+++ b/drivers/char/hw_random/amd-rng.c
@@ -0,0 +1,152 @@
+/*
+ * RNG driver for AMD RNGs
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * with the majority of the code coming from:
+ *
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/hw_random.h>
+#include <asm/io.h>
+
+
+#define PFX KBUILD_MODNAME ": "
+
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static const struct pci_device_id pci_tbl[] = {
+ { 0x1022, 0x7443, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x1022, 0x746b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+static struct pci_dev *amd_pdev;
+
+
+static int amd_rng_data_present(struct hwrng *rng)
+{
+ u32 pmbase = (u32)rng->priv;
+
+ return !!(inl(pmbase + 0xF4) & 1);
+}
+
+static int amd_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ u32 pmbase = (u32)rng->priv;
+
+ *data = inl(pmbase + 0xF0);
+
+ return 4;
+}
+
+static int amd_rng_init(struct hwrng *rng)
+{
+ u8 rnen;
+
+ pci_read_config_byte(amd_pdev, 0x40, &rnen);
+ rnen |= (1 << 7); /* RNG on */
+ pci_write_config_byte(amd_pdev, 0x40, rnen);
+
+ pci_read_config_byte(amd_pdev, 0x41, &rnen);
+ rnen |= (1 << 7); /* PMIO enable */
+ pci_write_config_byte(amd_pdev, 0x41, rnen);
+
+ return 0;
+}
+
+static void amd_rng_cleanup(struct hwrng *rng)
+{
+ u8 rnen;
+
+ pci_read_config_byte(amd_pdev, 0x40, &rnen);
+ rnen &= ~(1 << 7); /* RNG off */
+ pci_write_config_byte(amd_pdev, 0x40, rnen);
+}
+
+
+static struct hwrng amd_rng = {
+ .name = "amd",
+ .init = amd_rng_init,
+ .cleanup = amd_rng_cleanup,
+ .data_present = amd_rng_data_present,
+ .data_read = amd_rng_data_read,
+};
+
+
+static int __init mod_init(void)
+{
+ int err = -ENODEV;
+ struct pci_dev *pdev = NULL;
+ const struct pci_device_id *ent;
+ u32 pmbase;
+
+ for_each_pci_dev(pdev) {
+ ent = pci_match_id(pci_tbl, pdev);
+ if (ent)
+ goto found;
+ }
+ /* Device not found. */
+ goto out;
+
+found:
+ err = pci_read_config_dword(pdev, 0x58, &pmbase);
+ if (err)
+ goto out;
+ err = -EIO;
+ pmbase &= 0x0000FF00;
+ if (pmbase == 0)
+ goto out;
+ amd_rng.priv = (unsigned long)pmbase;
+ amd_pdev = pdev;
+
+ printk(KERN_INFO "AMD768 RNG detected\n");
+ err = hwrng_register(&amd_rng);
+ if (err) {
+ printk(KERN_ERR PFX "RNG registering failed (%d)\n",
+ err);
+ goto out;
+ }
+out:
+ return err;
+}
+
+static void __exit mod_exit(void)
+{
+ hwrng_unregister(&amd_rng);
+}
+
+subsys_initcall(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("The Linux Kernel team");
+MODULE_DESCRIPTION("H/W RNG driver for AMD chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/core.c b/drivers/char/hw_random/core.c
new file mode 100644
index 000000000000..88b026639f10
--- /dev/null
+++ b/drivers/char/hw_random/core.c
@@ -0,0 +1,354 @@
+/*
+ Added support for the AMD Geode LX RNG
+ (c) Copyright 2004-2005 Advanced Micro Devices, Inc.
+
+ derived from
+
+ Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+
+ derived from
+
+ Hardware driver for the AMD 768 Random Number Generator (RNG)
+ (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+
+ derived from
+
+ Hardware driver for Intel i810 Random Number Generator (RNG)
+ Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+
+ Added generic RNG API
+ Copyright 2006 Michael Buesch <mbuesch@freenet.de>
+ Copyright 2005 (c) MontaVista Software, Inc.
+
+ Please read Documentation/hw_random.txt for details on use.
+
+ ----------------------------------------------------------
+ This software may be used and distributed according to the terms
+ of the GNU General Public License, incorporated herein by reference.
+
+ */
+
+
+#include <linux/device.h>
+#include <linux/hw_random.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/miscdevice.h>
+#include <linux/delay.h>
+#include <asm/uaccess.h>
+
+
+#define RNG_MODULE_NAME "hw_random"
+#define PFX RNG_MODULE_NAME ": "
+#define RNG_MISCDEV_MINOR 183 /* official */
+
+
+static struct hwrng *current_rng;
+static LIST_HEAD(rng_list);
+static DEFINE_MUTEX(rng_mutex);
+
+
+static inline int hwrng_init(struct hwrng *rng)
+{
+ if (!rng->init)
+ return 0;
+ return rng->init(rng);
+}
+
+static inline void hwrng_cleanup(struct hwrng *rng)
+{
+ if (rng && rng->cleanup)
+ rng->cleanup(rng);
+}
+
+static inline int hwrng_data_present(struct hwrng *rng)
+{
+ if (!rng->data_present)
+ return 1;
+ return rng->data_present(rng);
+}
+
+static inline int hwrng_data_read(struct hwrng *rng, u32 *data)
+{
+ return rng->data_read(rng, data);
+}
+
+
+static int rng_dev_open(struct inode *inode, struct file *filp)
+{
+ /* enforce read-only access to this chrdev */
+ if ((filp->f_mode & FMODE_READ) == 0)
+ return -EINVAL;
+ if (filp->f_mode & FMODE_WRITE)
+ return -EINVAL;
+ return 0;
+}
+
+static ssize_t rng_dev_read(struct file *filp, char __user *buf,
+ size_t size, loff_t *offp)
+{
+ u32 data;
+ ssize_t ret = 0;
+ int i, err = 0;
+ int data_present;
+ int bytes_read;
+
+ while (size) {
+ err = -ERESTARTSYS;
+ if (mutex_lock_interruptible(&rng_mutex))
+ goto out;
+ if (!current_rng) {
+ mutex_unlock(&rng_mutex);
+ err = -ENODEV;
+ goto out;
+ }
+ if (filp->f_flags & O_NONBLOCK) {
+ data_present = hwrng_data_present(current_rng);
+ } else {
+ /* Some RNG require some time between data_reads to gather
+ * new entropy. Poll it.
+ */
+ for (i = 0; i < 20; i++) {
+ data_present = hwrng_data_present(current_rng);
+ if (data_present)
+ break;
+ udelay(10);
+ }
+ }
+ bytes_read = 0;
+ if (data_present)
+ bytes_read = hwrng_data_read(current_rng, &data);
+ mutex_unlock(&rng_mutex);
+
+ err = -EAGAIN;
+ if (!bytes_read && (filp->f_flags & O_NONBLOCK))
+ goto out;
+
+ err = -EFAULT;
+ while (bytes_read && size) {
+ if (put_user((u8)data, buf++))
+ goto out;
+ size--;
+ ret++;
+ bytes_read--;
+ data >>= 8;
+ }
+
+ if (need_resched())
+ schedule_timeout_interruptible(1);
+ err = -ERESTARTSYS;
+ if (signal_pending(current))
+ goto out;
+ }
+out:
+ return ret ? : err;
+}
+
+
+static struct file_operations rng_chrdev_ops = {
+ .owner = THIS_MODULE,
+ .open = rng_dev_open,
+ .read = rng_dev_read,
+};
+
+static struct miscdevice rng_miscdev = {
+ .minor = RNG_MISCDEV_MINOR,
+ .name = RNG_MODULE_NAME,
+ .fops = &rng_chrdev_ops,
+};
+
+
+static ssize_t hwrng_attr_current_store(struct class_device *class,
+ const char *buf, size_t len)
+{
+ int err;
+ struct hwrng *rng;
+
+ err = mutex_lock_interruptible(&rng_mutex);
+ if (err)
+ return -ERESTARTSYS;
+ err = -ENODEV;
+ list_for_each_entry(rng, &rng_list, list) {
+ if (strcmp(rng->name, buf) == 0) {
+ if (rng == current_rng) {
+ err = 0;
+ break;
+ }
+ err = hwrng_init(rng);
+ if (err)
+ break;
+ hwrng_cleanup(current_rng);
+ current_rng = rng;
+ err = 0;
+ break;
+ }
+ }
+ mutex_unlock(&rng_mutex);
+
+ return err ? : len;
+}
+
+static ssize_t hwrng_attr_current_show(struct class_device *class,
+ char *buf)
+{
+ int err;
+ ssize_t ret;
+ const char *name = "none";
+
+ err = mutex_lock_interruptible(&rng_mutex);
+ if (err)
+ return -ERESTARTSYS;
+ if (current_rng)
+ name = current_rng->name;
+ ret = snprintf(buf, PAGE_SIZE, "%s\n", name);
+ mutex_unlock(&rng_mutex);
+
+ return ret;
+}
+
+static ssize_t hwrng_attr_available_show(struct class_device *class,
+ char *buf)
+{
+ int err;
+ ssize_t ret = 0;
+ struct hwrng *rng;
+
+ err = mutex_lock_interruptible(&rng_mutex);
+ if (err)
+ return -ERESTARTSYS;
+ buf[0] = '\0';
+ list_for_each_entry(rng, &rng_list, list) {
+ strncat(buf, rng->name, PAGE_SIZE - ret - 1);
+ ret += strlen(rng->name);
+ strncat(buf, " ", PAGE_SIZE - ret - 1);
+ ret++;
+ }
+ strncat(buf, "\n", PAGE_SIZE - ret - 1);
+ ret++;
+ mutex_unlock(&rng_mutex);
+
+ return ret;
+}
+
+static CLASS_DEVICE_ATTR(rng_current, S_IRUGO | S_IWUSR,
+ hwrng_attr_current_show,
+ hwrng_attr_current_store);
+static CLASS_DEVICE_ATTR(rng_available, S_IRUGO,
+ hwrng_attr_available_show,
+ NULL);
+
+
+static void unregister_miscdev(void)
+{
+ class_device_remove_file(rng_miscdev.class,
+ &class_device_attr_rng_available);
+ class_device_remove_file(rng_miscdev.class,
+ &class_device_attr_rng_current);
+ misc_deregister(&rng_miscdev);
+}
+
+static int register_miscdev(void)
+{
+ int err;
+
+ err = misc_register(&rng_miscdev);
+ if (err)
+ goto out;
+ err = class_device_create_file(rng_miscdev.class,
+ &class_device_attr_rng_current);
+ if (err)
+ goto err_misc_dereg;
+ err = class_device_create_file(rng_miscdev.class,
+ &class_device_attr_rng_available);
+ if (err)
+ goto err_remove_current;
+out:
+ return err;
+
+err_remove_current:
+ class_device_remove_file(rng_miscdev.class,
+ &class_device_attr_rng_current);
+err_misc_dereg:
+ misc_deregister(&rng_miscdev);
+ goto out;
+}
+
+int hwrng_register(struct hwrng *rng)
+{
+ int must_register_misc;
+ int err = -EINVAL;
+ struct hwrng *old_rng, *tmp;
+
+ if (rng->name == NULL ||
+ rng->data_read == NULL)
+ goto out;
+
+ mutex_lock(&rng_mutex);
+
+ /* Must not register two RNGs with the same name. */
+ err = -EEXIST;
+ list_for_each_entry(tmp, &rng_list, list) {
+ if (strcmp(tmp->name, rng->name) == 0)
+ goto out_unlock;
+ }
+
+ must_register_misc = (current_rng == NULL);
+ old_rng = current_rng;
+ if (!old_rng) {
+ err = hwrng_init(rng);
+ if (err)
+ goto out_unlock;
+ current_rng = rng;
+ }
+ err = 0;
+ if (must_register_misc) {
+ err = register_miscdev();
+ if (err) {
+ if (!old_rng) {
+ hwrng_cleanup(rng);
+ current_rng = NULL;
+ }
+ goto out_unlock;
+ }
+ }
+ INIT_LIST_HEAD(&rng->list);
+ list_add_tail(&rng->list, &rng_list);
+out_unlock:
+ mutex_unlock(&rng_mutex);
+out:
+ return err;
+}
+EXPORT_SYMBOL_GPL(hwrng_register);
+
+void hwrng_unregister(struct hwrng *rng)
+{
+ int err;
+
+ mutex_lock(&rng_mutex);
+
+ list_del(&rng->list);
+ if (current_rng == rng) {
+ hwrng_cleanup(rng);
+ if (list_empty(&rng_list)) {
+ current_rng = NULL;
+ } else {
+ current_rng = list_entry(rng_list.prev, struct hwrng, list);
+ err = hwrng_init(current_rng);
+ if (err)
+ current_rng = NULL;
+ }
+ }
+ if (list_empty(&rng_list))
+ unregister_miscdev();
+
+ mutex_unlock(&rng_mutex);
+}
+EXPORT_SYMBOL_GPL(hwrng_unregister);
+
+
+MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/geode-rng.c b/drivers/char/hw_random/geode-rng.c
new file mode 100644
index 000000000000..be61f22ee7bb
--- /dev/null
+++ b/drivers/char/hw_random/geode-rng.c
@@ -0,0 +1,128 @@
+/*
+ * RNG driver for AMD Geode RNGs
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * with the majority of the code coming from:
+ *
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/hw_random.h>
+#include <asm/io.h>
+
+
+#define PFX KBUILD_MODNAME ": "
+
+#define GEODE_RNG_DATA_REG 0x50
+#define GEODE_RNG_STATUS_REG 0x54
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static const struct pci_device_id pci_tbl[] = {
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LX_AES,
+ PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+
+static int geode_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+
+ *data = readl(mem + GEODE_RNG_DATA_REG);
+
+ return 4;
+}
+
+static int geode_rng_data_present(struct hwrng *rng)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+
+ return !!(readl(mem + GEODE_RNG_STATUS_REG));
+}
+
+
+static struct hwrng geode_rng = {
+ .name = "geode",
+ .data_present = geode_rng_data_present,
+ .data_read = geode_rng_data_read,
+};
+
+
+static int __init mod_init(void)
+{
+ int err = -ENODEV;
+ struct pci_dev *pdev = NULL;
+ const struct pci_device_id *ent;
+ void __iomem *mem;
+ unsigned long rng_base;
+
+ for_each_pci_dev(pdev) {
+ ent = pci_match_id(pci_tbl, pdev);
+ if (ent)
+ goto found;
+ }
+ /* Device not found. */
+ goto out;
+
+found:
+ rng_base = pci_resource_start(pdev, 0);
+ if (rng_base == 0)
+ goto out;
+ err = -ENOMEM;
+ mem = ioremap(rng_base, 0x58);
+ if (!mem)
+ goto out;
+ geode_rng.priv = (unsigned long)mem;
+
+ printk(KERN_INFO "AMD Geode RNG detected\n");
+ err = hwrng_register(&geode_rng);
+ if (err) {
+ printk(KERN_ERR PFX "RNG registering failed (%d)\n",
+ err);
+ goto out;
+ }
+out:
+ return err;
+}
+
+static void __exit mod_exit(void)
+{
+ void __iomem *mem = (void __iomem *)geode_rng.priv;
+
+ hwrng_unregister(&geode_rng);
+ iounmap(mem);
+}
+
+subsys_initcall(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("H/W RNG driver for AMD Geode LX CPUs");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c
new file mode 100644
index 000000000000..6594bd5645f4
--- /dev/null
+++ b/drivers/char/hw_random/intel-rng.c
@@ -0,0 +1,189 @@
+/*
+ * RNG driver for Intel RNGs
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * with the majority of the code coming from:
+ *
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/hw_random.h>
+#include <asm/io.h>
+
+
+#define PFX KBUILD_MODNAME ": "
+
+/*
+ * RNG registers
+ */
+#define INTEL_RNG_HW_STATUS 0
+#define INTEL_RNG_PRESENT 0x40
+#define INTEL_RNG_ENABLED 0x01
+#define INTEL_RNG_STATUS 1
+#define INTEL_RNG_DATA_PRESENT 0x01
+#define INTEL_RNG_DATA 2
+
+/*
+ * Magic address at which Intel PCI bridges locate the RNG
+ */
+#define INTEL_RNG_ADDR 0xFFBC015F
+#define INTEL_RNG_ADDR_LEN 3
+
+/*
+ * Data for PCI driver interface
+ *
+ * This data only exists for exporting the supported
+ * PCI ids via MODULE_DEVICE_TABLE. We do not actually
+ * register a pci_driver, because someone else might one day
+ * want to register another driver on the same PCI id.
+ */
+static const struct pci_device_id pci_tbl[] = {
+ { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, },
+ { 0, }, /* terminate list */
+};
+MODULE_DEVICE_TABLE(pci, pci_tbl);
+
+
+static inline u8 hwstatus_get(void __iomem *mem)
+{
+ return readb(mem + INTEL_RNG_HW_STATUS);
+}
+
+static inline u8 hwstatus_set(void __iomem *mem,
+ u8 hw_status)
+{
+ writeb(hw_status, mem + INTEL_RNG_HW_STATUS);
+ return hwstatus_get(mem);
+}
+
+static int intel_rng_data_present(struct hwrng *rng)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+
+ return !!(readb(mem + INTEL_RNG_STATUS) & INTEL_RNG_DATA_PRESENT);
+}
+
+static int intel_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+
+ *data = readb(mem + INTEL_RNG_DATA);
+
+ return 1;
+}
+
+static int intel_rng_init(struct hwrng *rng)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+ u8 hw_status;
+ int err = -EIO;
+
+ hw_status = hwstatus_get(mem);
+ /* turn RNG h/w on, if it's off */
+ if ((hw_status & INTEL_RNG_ENABLED) == 0)
+ hw_status = hwstatus_set(mem, hw_status | INTEL_RNG_ENABLED);
+ if ((hw_status & INTEL_RNG_ENABLED) == 0) {
+ printk(KERN_ERR PFX "cannot enable RNG, aborting\n");
+ goto out;
+ }
+ err = 0;
+out:
+ return err;
+}
+
+static void intel_rng_cleanup(struct hwrng *rng)
+{
+ void __iomem *mem = (void __iomem *)rng->priv;
+ u8 hw_status;
+
+ hw_status = hwstatus_get(mem);
+ if (hw_status & INTEL_RNG_ENABLED)
+ hwstatus_set(mem, hw_status & ~INTEL_RNG_ENABLED);
+ else
+ printk(KERN_WARNING PFX "unusual: RNG already disabled\n");
+}
+
+
+static struct hwrng intel_rng = {
+ .name = "intel",
+ .init = intel_rng_init,
+ .cleanup = intel_rng_cleanup,
+ .data_present = intel_rng_data_present,
+ .data_read = intel_rng_data_read,
+};
+
+
+static int __init mod_init(void)
+{
+ int err = -ENODEV;
+ void __iomem *mem;
+ u8 hw_status;
+
+ if (!pci_dev_present(pci_tbl))
+ goto out; /* Device not found. */
+
+ err = -ENOMEM;
+ mem = ioremap(INTEL_RNG_ADDR, INTEL_RNG_ADDR_LEN);
+ if (!mem)
+ goto out;
+ intel_rng.priv = (unsigned long)mem;
+
+ /* Check for Intel 82802 */
+ err = -ENODEV;
+ hw_status = hwstatus_get(mem);
+ if ((hw_status & INTEL_RNG_PRESENT) == 0)
+ goto err_unmap;
+
+ printk(KERN_INFO "Intel 82802 RNG detected\n");
+ err = hwrng_register(&intel_rng);
+ if (err) {
+ printk(KERN_ERR PFX "RNG registering failed (%d)\n",
+ err);
+ goto out;
+ }
+out:
+ return err;
+
+err_unmap:
+ iounmap(mem);
+ goto out;
+}
+
+static void __exit mod_exit(void)
+{
+ void __iomem *mem = (void __iomem *)intel_rng.priv;
+
+ hwrng_unregister(&intel_rng);
+ iounmap(mem);
+}
+
+subsys_initcall(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("H/W RNG driver for Intel chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/ixp4xx-rng.c b/drivers/char/hw_random/ixp4xx-rng.c
new file mode 100644
index 000000000000..ef71022423c9
--- /dev/null
+++ b/drivers/char/hw_random/ixp4xx-rng.c
@@ -0,0 +1,73 @@
+/*
+ * drivers/char/rng/ixp4xx-rng.c
+ *
+ * RNG driver for Intel IXP4xx family of NPUs
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * Fixes by Michael Buesch
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/hw_random.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+
+
+static int ixp4xx_rng_data_read(struct hwrng *rng, u32 *buffer)
+{
+ void __iomem * rng_base = (void __iomem *)rng->priv;
+
+ *buffer = __raw_readl(rng_base);
+
+ return 4;
+}
+
+static struct hwrng ixp4xx_rng_ops = {
+ .name = "ixp4xx",
+ .data_read = ixp4xx_rng_data_read,
+};
+
+static int __init ixp4xx_rng_init(void)
+{
+ void __iomem * rng_base;
+ int err;
+
+ rng_base = ioremap(0x70002100, 4);
+ if (!rng_base)
+ return -ENOMEM;
+ ixp4xx_rng_ops.priv = (unsigned long)rng_base;
+ err = hwrng_register(&ixp4xx_rng_ops);
+ if (err)
+ iounmap(rng_base);
+
+ return err;
+}
+
+static void __exit ixp4xx_rng_exit(void)
+{
+ void __iomem * rng_base = (void __iomem *)ixp4xx_rng_ops.priv;
+
+ hwrng_unregister(&ixp4xx_rng_ops);
+ iounmap(rng_base);
+}
+
+subsys_initcall(ixp4xx_rng_init);
+module_exit(ixp4xx_rng_exit);
+
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
+MODULE_DESCRIPTION("H/W Random Number Generator (RNG) driver for IXP4xx");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/omap-rng.c b/drivers/char/hw_random/omap-rng.c
new file mode 100644
index 000000000000..819516b35a79
--- /dev/null
+++ b/drivers/char/hw_random/omap-rng.c
@@ -0,0 +1,208 @@
+/*
+ * driver/char/hw_random/omap-rng.c
+ *
+ * RNG driver for TI OMAP CPU family
+ *
+ * Author: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * Mostly based on original driver:
+ *
+ * Copyright (C) 2005 Nokia Corporation
+ * Author: Juha Yrj��<juha.yrjola@nokia.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ *
+ * TODO:
+ *
+ * - Make status updated be interrupt driven so we don't poll
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/random.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/hw_random.h>
+
+#include <asm/io.h>
+#include <asm/hardware/clock.h>
+
+#define RNG_OUT_REG 0x00 /* Output register */
+#define RNG_STAT_REG 0x04 /* Status register
+ [0] = STAT_BUSY */
+#define RNG_ALARM_REG 0x24 /* Alarm register
+ [7:0] = ALARM_COUNTER */
+#define RNG_CONFIG_REG 0x28 /* Configuration register
+ [11:6] = RESET_COUNT
+ [5:3] = RING2_DELAY
+ [2:0] = RING1_DELAY */
+#define RNG_REV_REG 0x3c /* Revision register
+ [7:0] = REV_NB */
+#define RNG_MASK_REG 0x40 /* Mask and reset register
+ [2] = IT_EN
+ [1] = SOFTRESET
+ [0] = AUTOIDLE */
+#define RNG_SYSSTATUS 0x44 /* System status
+ [0] = RESETDONE */
+
+static void __iomem *rng_base;
+static struct clk *rng_ick;
+static struct device *rng_dev;
+
+static u32 omap_rng_read_reg(int reg)
+{
+ return __raw_readl(rng_base + reg);
+}
+
+static void omap_rng_write_reg(int reg, u32 val)
+{
+ __raw_writel(val, rng_base + reg);
+}
+
+/* REVISIT: Does the status bit really work on 16xx? */
+static int omap_rng_data_present(struct hwrng *rng)
+{
+ return omap_rng_read_reg(RNG_STAT_REG) ? 0 : 1;
+}
+
+static int omap_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ *data = omap_rng_read_reg(RNG_OUT_REG);
+
+ return 4;
+}
+
+static struct hwrng omap_rng_ops = {
+ .name = "omap",
+ .data_present = omap_rng_data_present,
+ .data_read = omap_rng_data_read,
+};
+
+static int __init omap_rng_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct resource *res, *mem;
+ int ret;
+
+ /*
+ * A bit ugly, and it will never actually happen but there can
+ * be only one RNG and this catches any bork
+ */
+ BUG_ON(rng_dev);
+
+ if (cpu_is_omap24xx()) {
+ rng_ick = clk_get(NULL, "rng_ick");
+ if (IS_ERR(rng_ick)) {
+ dev_err(dev, "Could not get rng_ick\n");
+ ret = PTR_ERR(rng_ick);
+ return ret;
+ }
+ else {
+ clk_use(rng_ick);
+ }
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ if (!res)
+ return -ENOENT;
+
+ mem = request_mem_region(res->start, res->end - res->start + 1,
+ pdev->name);
+ if (mem == NULL)
+ return -EBUSY;
+
+ dev_set_drvdata(dev, mem);
+ rng_base = (u32 __iomem *)io_p2v(res->start);
+
+ ret = hwrng_register(&omap_rng_ops);
+ if (ret) {
+ release_resource(mem);
+ rng_base = NULL;
+ return ret;
+ }
+
+ dev_info(dev, "OMAP Random Number Generator ver. %02x\n",
+ omap_rng_read_reg(RNG_REV_REG));
+ omap_rng_write_reg(RNG_MASK_REG, 0x1);
+
+ rng_dev = dev;
+
+ return 0;
+}
+
+static int __exit omap_rng_remove(struct device *dev)
+{
+ struct resource *mem = dev_get_drvdata(dev);
+
+ hwrng_unregister(&omap_rng_ops);
+
+ omap_rng_write_reg(RNG_MASK_REG, 0x0);
+
+ if (cpu_is_omap24xx()) {
+ clk_unuse(rng_ick);
+ clk_put(rng_ick);
+ }
+
+ release_resource(mem);
+ rng_base = NULL;
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int omap_rng_suspend(struct device *dev, pm_message_t message, u32 level)
+{
+ omap_rng_write_reg(RNG_MASK_REG, 0x0);
+
+ return 0;
+}
+
+static int omap_rng_resume(struct device *dev, pm_message_t message, u32 level)
+{
+ omap_rng_write_reg(RNG_MASK_REG, 0x1);
+
+ return 1;
+}
+
+#else
+
+#define omap_rng_suspend NULL
+#define omap_rng_resume NULL
+
+#endif
+
+
+static struct device_driver omap_rng_driver = {
+ .name = "omap_rng",
+ .bus = &platform_bus_type,
+ .probe = omap_rng_probe,
+ .remove = __exit_p(omap_rng_remove),
+ .suspend = omap_rng_suspend,
+ .resume = omap_rng_resume
+};
+
+static int __init omap_rng_init(void)
+{
+ if (!cpu_is_omap16xx() && !cpu_is_omap24xx())
+ return -ENODEV;
+
+ return driver_register(&omap_rng_driver);
+}
+
+static void __exit omap_rng_exit(void)
+{
+ driver_unregister(&omap_rng_driver);
+}
+
+module_init(omap_rng_init);
+module_exit(omap_rng_exit);
+
+MODULE_AUTHOR("Deepak Saxena (and others)");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/hw_random/via-rng.c b/drivers/char/hw_random/via-rng.c
new file mode 100644
index 000000000000..0e786b617bb8
--- /dev/null
+++ b/drivers/char/hw_random/via-rng.c
@@ -0,0 +1,183 @@
+/*
+ * RNG driver for VIA RNGs
+ *
+ * Copyright 2005 (c) MontaVista Software, Inc.
+ *
+ * with the majority of the code coming from:
+ *
+ * Hardware driver for the Intel/AMD/VIA Random Number Generators (RNG)
+ * (c) Copyright 2003 Red Hat Inc <jgarzik@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for the AMD 768 Random Number Generator (RNG)
+ * (c) Copyright 2001 Red Hat Inc <alan@redhat.com>
+ *
+ * derived from
+ *
+ * Hardware driver for Intel i810 Random Number Generator (RNG)
+ * Copyright 2000,2001 Jeff Garzik <jgarzik@pobox.com>
+ * Copyright 2000,2001 Philipp Rumpf <prumpf@mandrakesoft.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/hw_random.h>
+#include <asm/io.h>
+#include <asm/msr.h>
+#include <asm/cpufeature.h>
+
+
+#define PFX KBUILD_MODNAME ": "
+
+
+enum {
+ VIA_STRFILT_CNT_SHIFT = 16,
+ VIA_STRFILT_FAIL = (1 << 15),
+ VIA_STRFILT_ENABLE = (1 << 14),
+ VIA_RAWBITS_ENABLE = (1 << 13),
+ VIA_RNG_ENABLE = (1 << 6),
+ VIA_XSTORE_CNT_MASK = 0x0F,
+
+ VIA_RNG_CHUNK_8 = 0x00, /* 64 rand bits, 64 stored bits */
+ VIA_RNG_CHUNK_4 = 0x01, /* 32 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_4_MASK = 0xFFFFFFFF,
+ VIA_RNG_CHUNK_2 = 0x02, /* 16 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_2_MASK = 0xFFFF,
+ VIA_RNG_CHUNK_1 = 0x03, /* 8 rand bits, 32 stored bits */
+ VIA_RNG_CHUNK_1_MASK = 0xFF,
+};
+
+/*
+ * Investigate using the 'rep' prefix to obtain 32 bits of random data
+ * in one insn. The upside is potentially better performance. The
+ * downside is that the instruction becomes no longer atomic. Due to
+ * this, just like familiar issues with /dev/random itself, the worst
+ * case of a 'rep xstore' could potentially pause a cpu for an
+ * unreasonably long time. In practice, this condition would likely
+ * only occur when the hardware is failing. (or so we hope :))
+ *
+ * Another possible performance boost may come from simply buffering
+ * until we have 4 bytes, thus returning a u32 at a time,
+ * instead of the current u8-at-a-time.
+ */
+
+static inline u32 xstore(u32 *addr, u32 edx_in)
+{
+ u32 eax_out;
+
+ asm(".byte 0x0F,0xA7,0xC0 /* xstore %%edi (addr=%0) */"
+ :"=m"(*addr), "=a"(eax_out)
+ :"D"(addr), "d"(edx_in));
+
+ return eax_out;
+}
+
+static int via_rng_data_present(struct hwrng *rng)
+{
+ u32 bytes_out;
+ u32 *via_rng_datum = (u32 *)(&rng->priv);
+
+ /* We choose the recommended 1-byte-per-instruction RNG rate,
+ * for greater randomness at the expense of speed. Larger
+ * values 2, 4, or 8 bytes-per-instruction yield greater
+ * speed at lesser randomness.
+ *
+ * If you change this to another VIA_CHUNK_n, you must also
+ * change the ->n_bytes values in rng_vendor_ops[] tables.
+ * VIA_CHUNK_8 requires further code changes.
+ *
+ * A copy of MSR_VIA_RNG is placed in eax_out when xstore
+ * completes.
+ */
+
+ *via_rng_datum = 0; /* paranoia, not really necessary */
+ bytes_out = xstore(via_rng_datum, VIA_RNG_CHUNK_1);
+ bytes_out &= VIA_XSTORE_CNT_MASK;
+ if (bytes_out == 0)
+ return 0;
+ return 1;
+}
+
+static int via_rng_data_read(struct hwrng *rng, u32 *data)
+{
+ u32 via_rng_datum = (u32)rng->priv;
+
+ *data = via_rng_datum;
+
+ return 1;
+}
+
+static int via_rng_init(struct hwrng *rng)
+{
+ u32 lo, hi, old_lo;
+
+ /* Control the RNG via MSR. Tread lightly and pay very close
+ * close attention to values written, as the reserved fields
+ * are documented to be "undefined and unpredictable"; but it
+ * does not say to write them as zero, so I make a guess that
+ * we restore the values we find in the register.
+ */
+ rdmsr(MSR_VIA_RNG, lo, hi);
+
+ old_lo = lo;
+ lo &= ~(0x7f << VIA_STRFILT_CNT_SHIFT);
+ lo &= ~VIA_XSTORE_CNT_MASK;
+ lo &= ~(VIA_STRFILT_ENABLE | VIA_STRFILT_FAIL | VIA_RAWBITS_ENABLE);
+ lo |= VIA_RNG_ENABLE;
+
+ if (lo != old_lo)
+ wrmsr(MSR_VIA_RNG, lo, hi);
+
+ /* perhaps-unnecessary sanity check; remove after testing if
+ unneeded */
+ rdmsr(MSR_VIA_RNG, lo, hi);
+ if ((lo & VIA_RNG_ENABLE) == 0) {
+ printk(KERN_ERR PFX "cannot enable VIA C3 RNG, aborting\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+
+static struct hwrng via_rng = {
+ .name = "via",
+ .init = via_rng_init,
+ .data_present = via_rng_data_present,
+ .data_read = via_rng_data_read,
+};
+
+
+static int __init mod_init(void)
+{
+ int err;
+
+ if (!cpu_has_xstore)
+ return -ENODEV;
+ printk(KERN_INFO "VIA RNG detected\n");
+ err = hwrng_register(&via_rng);
+ if (err) {
+ printk(KERN_ERR PFX "RNG registering failed (%d)\n",
+ err);
+ goto out;
+ }
+out:
+ return err;
+}
+
+static void __exit mod_exit(void)
+{
+ hwrng_unregister(&via_rng);
+}
+
+subsys_initcall(mod_init);
+module_exit(mod_exit);
+
+MODULE_DESCRIPTION("H/W RNG driver for VIA chipsets");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 9f2f8fdec69a..ad26f4b997c5 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -57,8 +57,7 @@ static int ipmi_init_msghandler(void);
static int initialized = 0;
#ifdef CONFIG_PROC_FS
-struct proc_dir_entry *proc_ipmi_root = NULL;
-EXPORT_SYMBOL(proc_ipmi_root);
+static struct proc_dir_entry *proc_ipmi_root = NULL;
#endif /* CONFIG_PROC_FS */
#define MAX_EVENTS_IN_QUEUE 25
@@ -936,11 +935,8 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
if (val) {
/* Deliver any queued events. */
- list_for_each_entry_safe(msg, msg2, &intf->waiting_events,
- link) {
- list_del(&msg->link);
- list_add_tail(&msg->link, &msgs);
- }
+ list_for_each_entry_safe(msg, msg2, &intf->waiting_events, link)
+ list_move_tail(&msg->link, &msgs);
intf->waiting_events_count = 0;
}
@@ -3677,7 +3673,7 @@ static void send_panic_events(char *str)
}
#endif /* CONFIG_IPMI_PANIC_EVENT */
-static int has_paniced = 0;
+static int has_panicked = 0;
static int panic_event(struct notifier_block *this,
unsigned long event,
@@ -3686,9 +3682,9 @@ static int panic_event(struct notifier_block *this,
int i;
ipmi_smi_t intf;
- if (has_paniced)
+ if (has_panicked)
return NOTIFY_DONE;
- has_paniced = 1;
+ has_panicked = 1;
/* For every registered interface, set it to run to completion. */
for (i = 0; i < MAX_IPMI_INTERFACES; i++) {
@@ -3742,11 +3738,8 @@ static int ipmi_init_msghandler(void)
proc_ipmi_root->owner = THIS_MODULE;
#endif /* CONFIG_PROC_FS */
- init_timer(&ipmi_timer);
- ipmi_timer.data = 0;
- ipmi_timer.function = ipmi_timeout;
- ipmi_timer.expires = jiffies + IPMI_TIMEOUT_JIFFIES;
- add_timer(&ipmi_timer);
+ setup_timer(&ipmi_timer, ipmi_timeout, 0);
+ mod_timer(&ipmi_timer, jiffies + IPMI_TIMEOUT_JIFFIES);
atomic_notifier_chain_register(&panic_notifier_list, &panic_block);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index 02a7dd7a8a55..bd4f2248b758 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -55,23 +55,6 @@
#include <linux/mutex.h>
#include <linux/kthread.h>
#include <asm/irq.h>
-#ifdef CONFIG_HIGH_RES_TIMERS
-#include <linux/hrtime.h>
-# if defined(schedule_next_int)
-/* Old high-res timer code, do translations. */
-# define get_arch_cycles(a) quick_update_jiffies_sub(a)
-# define arch_cycles_per_jiffy cycles_per_jiffies
-# endif
-static inline void add_usec_to_timer(struct timer_list *t, long v)
-{
- t->arch_cycle_expires += nsec_to_arch_cycle(v * 1000);
- while (t->arch_cycle_expires >= arch_cycles_per_jiffy)
- {
- t->expires++;
- t->arch_cycle_expires -= arch_cycles_per_jiffy;
- }
-}
-#endif
#include <linux/interrupt.h>
#include <linux/rcupdate.h>
#include <linux/ipmi_smi.h>
@@ -243,8 +226,6 @@ static int register_xaction_notifier(struct notifier_block * nb)
return atomic_notifier_chain_register(&xaction_notifier_list, nb);
}
-static void si_restart_short_timer(struct smi_info *smi_info);
-
static void deliver_recv_msg(struct smi_info *smi_info,
struct ipmi_smi_msg *msg)
{
@@ -768,7 +749,6 @@ static void sender(void *send_info,
&& (smi_info->curr_msg == NULL))
{
start_next_msg(smi_info);
- si_restart_short_timer(smi_info);
}
spin_unlock_irqrestore(&(smi_info->si_lock), flags);
}
@@ -809,7 +789,7 @@ static int ipmi_thread(void *data)
/* do nothing */
}
else if (smi_result == SI_SM_CALL_WITH_DELAY)
- udelay(1);
+ schedule();
else
schedule_timeout_interruptible(1);
}
@@ -833,37 +813,6 @@ static void request_events(void *send_info)
static int initialized = 0;
-/* Must be called with interrupts off and with the si_lock held. */
-static void si_restart_short_timer(struct smi_info *smi_info)
-{
-#if defined(CONFIG_HIGH_RES_TIMERS)
- unsigned long flags;
- unsigned long jiffies_now;
- unsigned long seq;
-
- if (del_timer(&(smi_info->si_timer))) {
- /* If we don't delete the timer, then it will go off
- immediately, anyway. So we only process if we
- actually delete the timer. */
-
- do {
- seq = read_seqbegin_irqsave(&xtime_lock, flags);
- jiffies_now = jiffies;
- smi_info->si_timer.expires = jiffies_now;
- smi_info->si_timer.arch_cycle_expires
- = get_arch_cycles(jiffies_now);
- } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
-
- add_usec_to_timer(&smi_info->si_timer, SI_SHORT_TIMEOUT_USEC);
-
- add_timer(&(smi_info->si_timer));
- spin_lock_irqsave(&smi_info->count_lock, flags);
- smi_info->timeout_restarts++;
- spin_unlock_irqrestore(&smi_info->count_lock, flags);
- }
-#endif
-}
-
static void smi_timeout(unsigned long data)
{
struct smi_info *smi_info = (struct smi_info *) data;
@@ -904,31 +853,15 @@ static void smi_timeout(unsigned long data)
/* If the state machine asks for a short delay, then shorten
the timer timeout. */
if (smi_result == SI_SM_CALL_WITH_DELAY) {
-#if defined(CONFIG_HIGH_RES_TIMERS)
- unsigned long seq;
-#endif
spin_lock_irqsave(&smi_info->count_lock, flags);
smi_info->short_timeouts++;
spin_unlock_irqrestore(&smi_info->count_lock, flags);
-#if defined(CONFIG_HIGH_RES_TIMERS)
- do {
- seq = read_seqbegin_irqsave(&xtime_lock, flags);
- smi_info->si_timer.expires = jiffies;
- smi_info->si_timer.arch_cycle_expires
- = get_arch_cycles(smi_info->si_timer.expires);
- } while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
- add_usec_to_timer(&smi_info->si_timer, SI_SHORT_TIMEOUT_USEC);
-#else
smi_info->si_timer.expires = jiffies + 1;
-#endif
} else {
spin_lock_irqsave(&smi_info->count_lock, flags);
smi_info->long_timeouts++;
spin_unlock_irqrestore(&smi_info->count_lock, flags);
smi_info->si_timer.expires = jiffies + SI_TIMEOUT_JIFFIES;
-#if defined(CONFIG_HIGH_RES_TIMERS)
- smi_info->si_timer.arch_cycle_expires = 0;
-#endif
}
do_add_timer:
diff --git a/drivers/char/ipmi/ipmi_watchdog.c b/drivers/char/ipmi/ipmi_watchdog.c
index 8f8867170973..1a0a19c53605 100644
--- a/drivers/char/ipmi/ipmi_watchdog.c
+++ b/drivers/char/ipmi/ipmi_watchdog.c
@@ -949,9 +949,10 @@ static int wdog_reboot_handler(struct notifier_block *this,
/* Disable the WDT if we are shutting down. */
ipmi_watchdog_state = WDOG_TIMEOUT_NONE;
panic_halt_ipmi_set_timeout();
- } else {
+ } else if (ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
/* Set a long timer to let the reboot happens, but
- reboot if it hangs. */
+ reboot if it hangs, but only if the watchdog
+ timer was already running. */
timeout = 120;
pretimeout = 0;
ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
@@ -973,16 +974,17 @@ static int wdog_panic_handler(struct notifier_block *this,
{
static int panic_event_handled = 0;
- /* On a panic, if we have a panic timeout, make sure that the thing
- reboots, even if it hangs during that panic. */
- if (watchdog_user && !panic_event_handled) {
- /* Make sure the panic doesn't hang, and make sure we
- do this only once. */
+ /* On a panic, if we have a panic timeout, make sure to extend
+ the watchdog timer to a reasonable value to complete the
+ panic, if the watchdog timer is running. Plus the
+ pretimeout is meaningless at panic time. */
+ if (watchdog_user && !panic_event_handled &&
+ ipmi_watchdog_state != WDOG_TIMEOUT_NONE) {
+ /* Make sure we do this only once. */
panic_event_handled = 1;
timeout = 255;
pretimeout = 0;
- ipmi_watchdog_state = WDOG_TIMEOUT_RESET;
panic_halt_ipmi_set_timeout();
}
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index ef20c1fc9c4c..216c79256de3 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -42,13 +42,12 @@
#include <linux/devfs_fs_kernel.h>
#include <linux/device.h>
#include <linux/wait.h>
+#include <linux/eisa.h>
#include <asm/io.h>
#include <asm/uaccess.h>
-#ifdef CONFIG_PCI
#include <linux/pci.h>
-#endif
/*****************************************************************************/
@@ -137,6 +136,10 @@ static stlconf_t stli_brdconf[] = {
static int stli_nrbrds = ARRAY_SIZE(stli_brdconf);
+/* stli_lock must NOT be taken holding brd_lock */
+static spinlock_t stli_lock; /* TTY logic lock */
+static spinlock_t brd_lock; /* Board logic lock */
+
/*
* There is some experimental EISA board detection code in this driver.
* By default it is disabled, but for those that want to try it out,
@@ -173,14 +176,6 @@ static char *stli_serialname = "ttyE";
static struct tty_driver *stli_serial;
-/*
- * We will need to allocate a temporary write buffer for chars that
- * come direct from user space. The problem is that a copy from user
- * space might cause a page fault (typically on a system that is
- * swapping!). All ports will share one buffer - since if the system
- * is already swapping a shared buffer won't make things any worse.
- */
-static char *stli_tmpwritebuf;
#define STLI_TXBUFSIZE 4096
@@ -419,7 +414,7 @@ static int stli_eisamempsize = ARRAY_SIZE(stli_eisamemprobeaddrs);
#endif
static struct pci_device_id istallion_pci_tbl[] = {
- { PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA), },
{ 0 }
};
MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
@@ -682,7 +677,7 @@ static int stli_startbrd(stlibrd_t *brdp);
static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
-static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp);
+static void stli_brdpoll(stlibrd_t *brdp, cdkhdr_t __iomem *hdrp);
static void stli_poll(unsigned long arg);
static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp);
static int stli_initopen(stlibrd_t *brdp, stliport_t *portp);
@@ -693,7 +688,8 @@ static void stli_dohangup(void *arg);
static int stli_setport(stliport_t *portp);
static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
-static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp);
+static void __stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback);
+static void stli_dodelaycmd(stliport_t *portp, cdkctrl_t __iomem *cp);
static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp);
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts);
static long stli_mktiocm(unsigned long sigvalue);
@@ -799,18 +795,8 @@ static struct class *istallion_class;
static int __init istallion_module_init(void)
{
- unsigned long flags;
-
-#ifdef DEBUG
- printk("init_module()\n");
-#endif
-
- save_flags(flags);
- cli();
stli_init();
- restore_flags(flags);
-
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -819,33 +805,24 @@ static void __exit istallion_module_exit(void)
{
stlibrd_t *brdp;
stliport_t *portp;
- unsigned long flags;
int i, j;
-#ifdef DEBUG
- printk("cleanup_module()\n");
-#endif
-
printk(KERN_INFO "Unloading %s: version %s\n", stli_drvtitle,
stli_drvversion);
- save_flags(flags);
- cli();
-
-/*
- * Free up all allocated resources used by the ports. This includes
- * memory and interrupts.
- */
+ /*
+ * Free up all allocated resources used by the ports. This includes
+ * memory and interrupts.
+ */
if (stli_timeron) {
stli_timeron = 0;
- del_timer(&stli_timerlist);
+ del_timer_sync(&stli_timerlist);
}
i = tty_unregister_driver(stli_serial);
if (i) {
printk("STALLION: failed to un-register tty driver, "
"errno=%d\n", -i);
- restore_flags(flags);
return;
}
put_tty_driver(stli_serial);
@@ -859,16 +836,15 @@ static void __exit istallion_module_exit(void)
printk("STALLION: failed to un-register serial memory device, "
"errno=%d\n", -i);
- kfree(stli_tmpwritebuf);
kfree(stli_txcookbuf);
for (i = 0; (i < stli_nrbrds); i++) {
- if ((brdp = stli_brds[i]) == (stlibrd_t *) NULL)
+ if ((brdp = stli_brds[i]) == NULL)
continue;
for (j = 0; (j < STL_MAXPORTS); j++) {
portp = brdp->ports[j];
- if (portp != (stliport_t *) NULL) {
- if (portp->tty != (struct tty_struct *) NULL)
+ if (portp != NULL) {
+ if (portp->tty != NULL)
tty_hangup(portp->tty);
kfree(portp);
}
@@ -878,10 +854,8 @@ static void __exit istallion_module_exit(void)
if (brdp->iosize > 0)
release_region(brdp->iobase, brdp->iosize);
kfree(brdp);
- stli_brds[i] = (stlibrd_t *) NULL;
+ stli_brds[i] = NULL;
}
-
- restore_flags(flags);
}
module_init(istallion_module_init);
@@ -895,19 +869,15 @@ module_exit(istallion_module_exit);
static void stli_argbrds(void)
{
- stlconf_t conf;
- stlibrd_t *brdp;
- int i;
-
-#ifdef DEBUG
- printk("stli_argbrds()\n");
-#endif
+ stlconf_t conf;
+ stlibrd_t *brdp;
+ int i;
for (i = stli_nrbrds; i < ARRAY_SIZE(stli_brdsp); i++) {
memset(&conf, 0, sizeof(conf));
if (stli_parsebrd(&conf, stli_brdsp[i]) == 0)
continue;
- if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
+ if ((brdp = stli_allocbrd()) == NULL)
continue;
stli_nrbrds = i + 1;
brdp->brdnr = i;
@@ -926,9 +896,9 @@ static void stli_argbrds(void)
static unsigned long stli_atol(char *str)
{
- unsigned long val;
- int base, c;
- char *sp;
+ unsigned long val;
+ int base, c;
+ char *sp;
val = 0;
sp = str;
@@ -962,15 +932,11 @@ static unsigned long stli_atol(char *str)
static int stli_parsebrd(stlconf_t *confp, char **argp)
{
- char *sp;
- int i;
-
-#ifdef DEBUG
- printk("stli_parsebrd(confp=%x,argp=%x)\n", (int) confp, (int) argp);
-#endif
+ char *sp;
+ int i;
- if ((argp[0] == (char *) NULL) || (*argp[0] == 0))
- return(0);
+ if (argp[0] == NULL || *argp[0] == 0)
+ return 0;
for (sp = argp[0], i = 0; ((*sp != 0) && (i < 25)); sp++, i++)
*sp = TOLOWER(*sp);
@@ -985,9 +951,9 @@ static int stli_parsebrd(stlconf_t *confp, char **argp)
}
confp->brdtype = stli_brdstr[i].type;
- if ((argp[1] != (char *) NULL) && (*argp[1] != 0))
+ if (argp[1] != NULL && *argp[1] != 0)
confp->ioaddr1 = stli_atol(argp[1]);
- if ((argp[2] != (char *) NULL) && (*argp[2] != 0))
+ if (argp[2] != NULL && *argp[2] != 0)
confp->memaddr = stli_atol(argp[2]);
return(1);
}
@@ -998,34 +964,29 @@ static int stli_parsebrd(stlconf_t *confp, char **argp)
static int stli_open(struct tty_struct *tty, struct file *filp)
{
- stlibrd_t *brdp;
- stliport_t *portp;
- unsigned int minordev;
- int brdnr, portnr, rc;
-
-#ifdef DEBUG
- printk("stli_open(tty=%x,filp=%x): device=%s\n", (int) tty,
- (int) filp, tty->name);
-#endif
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned int minordev;
+ int brdnr, portnr, rc;
minordev = tty->index;
brdnr = MINOR2BRD(minordev);
if (brdnr >= stli_nrbrds)
- return(-ENODEV);
+ return -ENODEV;
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
if ((brdp->state & BST_STARTED) == 0)
- return(-ENODEV);
+ return -ENODEV;
portnr = MINOR2PORT(minordev);
if ((portnr < 0) || (portnr > brdp->nrports))
- return(-ENODEV);
+ return -ENODEV;
portp = brdp->ports[portnr];
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
+ if (portp == NULL)
+ return -ENODEV;
if (portp->devnr < 1)
- return(-ENODEV);
+ return -ENODEV;
/*
@@ -1037,8 +998,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
if (portp->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&portp->close_wait);
if (portp->flags & ASYNC_HUP_NOTIFY)
- return(-EAGAIN);
- return(-ERESTARTSYS);
+ return -EAGAIN;
+ return -ERESTARTSYS;
}
/*
@@ -1054,7 +1015,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_INITIALIZING, &portp->state));
if (signal_pending(current))
- return(-ERESTARTSYS);
+ return -ERESTARTSYS;
if ((portp->flags & ASYNC_INITIALIZED) == 0) {
set_bit(ST_INITIALIZING, &portp->state);
@@ -1065,7 +1026,7 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
clear_bit(ST_INITIALIZING, &portp->state);
wake_up_interruptible(&portp->raw_wait);
if (rc < 0)
- return(rc);
+ return rc;
}
/*
@@ -1077,8 +1038,8 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
if (portp->flags & ASYNC_CLOSING) {
interruptible_sleep_on(&portp->close_wait);
if (portp->flags & ASYNC_HUP_NOTIFY)
- return(-EAGAIN);
- return(-ERESTARTSYS);
+ return -EAGAIN;
+ return -ERESTARTSYS;
}
/*
@@ -1088,38 +1049,33 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
*/
if (!(filp->f_flags & O_NONBLOCK)) {
if ((rc = stli_waitcarrier(brdp, portp, filp)) != 0)
- return(rc);
+ return rc;
}
portp->flags |= ASYNC_NORMAL_ACTIVE;
- return(0);
+ return 0;
}
/*****************************************************************************/
static void stli_close(struct tty_struct *tty, struct file *filp)
{
- stlibrd_t *brdp;
- stliport_t *portp;
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_close(tty=%x,filp=%x)\n", (int) tty, (int) filp);
-#endif
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ unsigned long flags;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&stli_lock, flags);
if (tty_hung_up_p(filp)) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&stli_lock, flags);
return;
}
if ((tty->count == 1) && (portp->refcount != 1))
portp->refcount = 1;
if (portp->refcount-- > 1) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&stli_lock, flags);
return;
}
@@ -1134,6 +1090,8 @@ static void stli_close(struct tty_struct *tty, struct file *filp)
if (tty == stli_txcooktty)
stli_flushchars(tty);
tty->closing = 1;
+ spin_unlock_irqrestore(&stli_lock, flags);
+
if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, portp->closing_wait);
@@ -1157,7 +1115,7 @@ static void stli_close(struct tty_struct *tty, struct file *filp)
stli_flushbuffer(tty);
tty->closing = 0;
- portp->tty = (struct tty_struct *) NULL;
+ portp->tty = NULL;
if (portp->openwaitcnt) {
if (portp->close_delay)
@@ -1167,7 +1125,6 @@ static void stli_close(struct tty_struct *tty, struct file *filp)
portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&portp->close_wait);
- restore_flags(flags);
}
/*****************************************************************************/
@@ -1182,45 +1139,41 @@ static void stli_close(struct tty_struct *tty, struct file *filp)
static int stli_initopen(stlibrd_t *brdp, stliport_t *portp)
{
- struct tty_struct *tty;
- asynotify_t nt;
- asyport_t aport;
- int rc;
-
-#ifdef DEBUG
- printk("stli_initopen(brdp=%x,portp=%x)\n", (int) brdp, (int) portp);
-#endif
+ struct tty_struct *tty;
+ asynotify_t nt;
+ asyport_t aport;
+ int rc;
if ((rc = stli_rawopen(brdp, portp, 0, 1)) < 0)
- return(rc);
+ return rc;
memset(&nt, 0, sizeof(asynotify_t));
nt.data = (DT_TXLOW | DT_TXEMPTY | DT_RXBUSY | DT_RXBREAK);
nt.signal = SG_DCD;
if ((rc = stli_cmdwait(brdp, portp, A_SETNOTIFY, &nt,
sizeof(asynotify_t), 0)) < 0)
- return(rc);
+ return rc;
tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
- return(-ENODEV);
+ if (tty == NULL)
+ return -ENODEV;
stli_mkasyport(portp, &aport, tty->termios);
if ((rc = stli_cmdwait(brdp, portp, A_SETPORT, &aport,
sizeof(asyport_t), 0)) < 0)
- return(rc);
+ return rc;
set_bit(ST_GETSIGS, &portp->state);
if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS, &portp->asig,
sizeof(asysigs_t), 1)) < 0)
- return(rc);
+ return rc;
if (test_and_clear_bit(ST_GETSIGS, &portp->state))
portp->sigs = stli_mktiocm(portp->asig.sigvalue);
stli_mkasysigs(&portp->asig, 1, 1);
if ((rc = stli_cmdwait(brdp, portp, A_SETSIGNALS, &portp->asig,
sizeof(asysigs_t), 0)) < 0)
- return(rc);
+ return rc;
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -1234,22 +1187,15 @@ static int stli_initopen(stlibrd_t *brdp, stliport_t *portp)
static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
{
- volatile cdkhdr_t *hdrp;
- volatile cdkctrl_t *cp;
- volatile unsigned char *bits;
- unsigned long flags;
- int rc;
-
-#ifdef DEBUG
- printk("stli_rawopen(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
- (int) brdp, (int) portp, (int) arg, wait);
-#endif
+ cdkhdr_t __iomem *hdrp;
+ cdkctrl_t __iomem *cp;
+ unsigned char __iomem *bits;
+ unsigned long flags;
+ int rc;
/*
* Send a message to the slave to open this port.
*/
- save_flags(flags);
- cli();
/*
* Slave is already closing this port. This can happen if a hangup
@@ -1260,7 +1206,6 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
- restore_flags(flags);
return -ERESTARTSYS;
}
@@ -1269,19 +1214,20 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
* memory. Once the message is in set the service bits to say that
* this port wants service.
*/
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
- cp->openarg = arg;
- cp->open = 1;
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+ cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ writel(arg, &cp->openarg);
+ writeb(1, &cp->open);
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
- *bits |= portp->portbit;
+ writeb(readb(bits) | portp->portbit, bits);
EBRDDISABLE(brdp);
if (wait == 0) {
- restore_flags(flags);
- return(0);
+ spin_unlock_irqrestore(&brd_lock, flags);
+ return 0;
}
/*
@@ -1290,15 +1236,16 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
*/
rc = 0;
set_bit(ST_OPENING, &portp->state);
+ spin_unlock_irqrestore(&brd_lock, flags);
+
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_OPENING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
- restore_flags(flags);
if ((rc == 0) && (portp->rc != 0))
rc = -EIO;
- return(rc);
+ return rc;
}
/*****************************************************************************/
@@ -1311,19 +1258,11 @@ static int stli_rawopen(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, i
static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg, int wait)
{
- volatile cdkhdr_t *hdrp;
- volatile cdkctrl_t *cp;
- volatile unsigned char *bits;
- unsigned long flags;
- int rc;
-
-#ifdef DEBUG
- printk("stli_rawclose(brdp=%x,portp=%x,arg=%x,wait=%d)\n",
- (int) brdp, (int) portp, (int) arg, wait);
-#endif
-
- save_flags(flags);
- cli();
+ cdkhdr_t __iomem *hdrp;
+ cdkctrl_t __iomem *cp;
+ unsigned char __iomem *bits;
+ unsigned long flags;
+ int rc;
/*
* Slave is already closing this port. This can happen if a hangup
@@ -1333,7 +1272,6 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current)) {
- restore_flags(flags);
return -ERESTARTSYS;
}
}
@@ -1341,21 +1279,22 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
/*
* Write the close command into shared memory.
*/
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
- cp->closearg = arg;
- cp->close = 1;
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+ cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ writel(arg, &cp->closearg);
+ writeb(1, &cp->close);
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
- *bits |= portp->portbit;
+ writeb(readb(bits) |portp->portbit, bits);
EBRDDISABLE(brdp);
set_bit(ST_CLOSING, &portp->state);
- if (wait == 0) {
- restore_flags(flags);
- return(0);
- }
+ spin_unlock_irqrestore(&brd_lock, flags);
+
+ if (wait == 0)
+ return 0;
/*
* Slave is in action, so now we must wait for the open acknowledgment
@@ -1366,11 +1305,10 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
!test_bit(ST_CLOSING, &portp->state));
if (signal_pending(current))
rc = -ERESTARTSYS;
- restore_flags(flags);
if ((rc == 0) && (portp->rc != 0))
rc = -EIO;
- return(rc);
+ return rc;
}
/*****************************************************************************/
@@ -1384,36 +1322,21 @@ static int stli_rawclose(stlibrd_t *brdp, stliport_t *portp, unsigned long arg,
static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
{
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_cmdwait(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
- "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
- (int) arg, size, copyback);
-#endif
-
- save_flags(flags);
- cli();
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CMDING, &portp->state));
- if (signal_pending(current)) {
- restore_flags(flags);
+ if (signal_pending(current))
return -ERESTARTSYS;
- }
stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
wait_event_interruptible(portp->raw_wait,
!test_bit(ST_CMDING, &portp->state));
- if (signal_pending(current)) {
- restore_flags(flags);
+ if (signal_pending(current))
return -ERESTARTSYS;
- }
- restore_flags(flags);
if (portp->rc != 0)
- return(-EIO);
- return(0);
+ return -EIO;
+ return 0;
}
/*****************************************************************************/
@@ -1425,22 +1348,18 @@ static int stli_cmdwait(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, v
static int stli_setport(stliport_t *portp)
{
- stlibrd_t *brdp;
- asyport_t aport;
-
-#ifdef DEBUG
- printk("stli_setport(portp=%x)\n", (int) portp);
-#endif
+ stlibrd_t *brdp;
+ asyport_t aport;
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
- if (portp->tty == (struct tty_struct *) NULL)
- return(-ENODEV);
- if ((portp->brdnr < 0) && (portp->brdnr >= stli_nrbrds))
- return(-ENODEV);
+ if (portp == NULL)
+ return -ENODEV;
+ if (portp->tty == NULL)
+ return -ENODEV;
+ if (portp->brdnr < 0 && portp->brdnr >= stli_nrbrds)
+ return -ENODEV;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
stli_mkasyport(portp, &aport, portp->tty->termios);
return(stli_cmdwait(brdp, portp, A_SETPORT, &aport, sizeof(asyport_t), 0));
@@ -1455,13 +1374,8 @@ static int stli_setport(stliport_t *portp)
static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *filp)
{
- unsigned long flags;
- int rc, doclocal;
-
-#ifdef DEBUG
- printk("stli_waitcarrier(brdp=%x,portp=%x,filp=%x)\n",
- (int) brdp, (int) portp, (int) filp);
-#endif
+ unsigned long flags;
+ int rc, doclocal;
rc = 0;
doclocal = 0;
@@ -1469,11 +1383,11 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil
if (portp->tty->termios->c_cflag & CLOCAL)
doclocal++;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&stli_lock, flags);
portp->openwaitcnt++;
if (! tty_hung_up_p(filp))
portp->refcount--;
+ spin_unlock_irqrestore(&stli_lock, flags);
for (;;) {
stli_mkasysigs(&portp->asig, 1, 1);
@@ -1499,12 +1413,13 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil
interruptible_sleep_on(&portp->open_wait);
}
+ spin_lock_irqsave(&stli_lock, flags);
if (! tty_hung_up_p(filp))
portp->refcount++;
portp->openwaitcnt--;
- restore_flags(flags);
+ spin_unlock_irqrestore(&stli_lock, flags);
- return(rc);
+ return rc;
}
/*****************************************************************************/
@@ -1517,46 +1432,38 @@ static int stli_waitcarrier(stlibrd_t *brdp, stliport_t *portp, struct file *fil
static int stli_write(struct tty_struct *tty, const unsigned char *buf, int count)
{
- volatile cdkasy_t *ap;
- volatile cdkhdr_t *hdrp;
- volatile unsigned char *bits;
- unsigned char *shbuf, *chbuf;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int len, stlen, head, tail, size;
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_write(tty=%x,buf=%x,count=%d)\n",
- (int) tty, (int) buf, count);
-#endif
+ cdkasy_t __iomem *ap;
+ cdkhdr_t __iomem *hdrp;
+ unsigned char __iomem *bits;
+ unsigned char __iomem *shbuf;
+ unsigned char *chbuf;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int len, stlen, head, tail, size;
+ unsigned long flags;
- if ((tty == (struct tty_struct *) NULL) ||
- (stli_tmpwritebuf == (char *) NULL))
- return(0);
if (tty == stli_txcooktty)
stli_flushchars(tty);
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(0);
+ if (portp == NULL)
+ return 0;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
chbuf = (unsigned char *) buf;
/*
* All data is now local, shove as much as possible into shared memory.
*/
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- head = (unsigned int) ap->txq.head;
- tail = (unsigned int) ap->txq.tail;
- if (tail != ((unsigned int) ap->txq.tail))
- tail = (unsigned int) ap->txq.tail;
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+ head = (unsigned int) readw(&ap->txq.head);
+ tail = (unsigned int) readw(&ap->txq.tail);
+ if (tail != ((unsigned int) readw(&ap->txq.tail)))
+ tail = (unsigned int) readw(&ap->txq.tail);
size = portp->txsize;
if (head >= tail) {
len = size - (head - tail) - 1;
@@ -1568,11 +1475,11 @@ static int stli_write(struct tty_struct *tty, const unsigned char *buf, int coun
len = MIN(len, count);
count = 0;
- shbuf = (char *) EBRDGETMEMPTR(brdp, portp->txoffset);
+ shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->txoffset);
while (len > 0) {
stlen = MIN(len, stlen);
- memcpy((shbuf + head), chbuf, stlen);
+ memcpy_toio(shbuf + head, chbuf, stlen);
chbuf += stlen;
len -= stlen;
count += stlen;
@@ -1583,20 +1490,19 @@ static int stli_write(struct tty_struct *tty, const unsigned char *buf, int coun
}
}
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- ap->txq.head = head;
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+ writew(head, &ap->txq.head);
if (test_bit(ST_TXBUSY, &portp->state)) {
- if (ap->changed.data & DT_TXEMPTY)
- ap->changed.data &= ~DT_TXEMPTY;
+ if (readl(&ap->changed.data) & DT_TXEMPTY)
+ writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
}
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
- *bits |= portp->portbit;
+ writeb(readb(bits) | portp->portbit, bits);
set_bit(ST_TXBUSY, &portp->state);
EBRDDISABLE(brdp);
-
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
return(count);
}
@@ -1613,14 +1519,8 @@ static int stli_write(struct tty_struct *tty, const unsigned char *buf, int coun
static void stli_putchar(struct tty_struct *tty, unsigned char ch)
{
-#ifdef DEBUG
- printk("stli_putchar(tty=%x,ch=%x)\n", (int) tty, (int) ch);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
if (tty != stli_txcooktty) {
- if (stli_txcooktty != (struct tty_struct *) NULL)
+ if (stli_txcooktty != NULL)
stli_flushchars(stli_txcooktty);
stli_txcooktty = tty;
}
@@ -1640,29 +1540,26 @@ static void stli_putchar(struct tty_struct *tty, unsigned char ch)
static void stli_flushchars(struct tty_struct *tty)
{
- volatile cdkhdr_t *hdrp;
- volatile unsigned char *bits;
- volatile cdkasy_t *ap;
- struct tty_struct *cooktty;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int len, stlen, head, tail, size, count, cooksize;
- unsigned char *buf, *shbuf;
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_flushchars(tty=%x)\n", (int) tty);
-#endif
+ cdkhdr_t __iomem *hdrp;
+ unsigned char __iomem *bits;
+ cdkasy_t __iomem *ap;
+ struct tty_struct *cooktty;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int len, stlen, head, tail, size, count, cooksize;
+ unsigned char *buf;
+ unsigned char __iomem *shbuf;
+ unsigned long flags;
cooksize = stli_txcooksize;
cooktty = stli_txcooktty;
stli_txcooksize = 0;
stli_txcookrealsize = 0;
- stli_txcooktty = (struct tty_struct *) NULL;
+ stli_txcooktty = NULL;
- if (tty == (struct tty_struct *) NULL)
+ if (tty == NULL)
return;
- if (cooktty == (struct tty_struct *) NULL)
+ if (cooktty == NULL)
return;
if (tty != cooktty)
tty = cooktty;
@@ -1670,23 +1567,22 @@ static void stli_flushchars(struct tty_struct *tty)
return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- head = (unsigned int) ap->txq.head;
- tail = (unsigned int) ap->txq.tail;
- if (tail != ((unsigned int) ap->txq.tail))
- tail = (unsigned int) ap->txq.tail;
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+ head = (unsigned int) readw(&ap->txq.head);
+ tail = (unsigned int) readw(&ap->txq.tail);
+ if (tail != ((unsigned int) readw(&ap->txq.tail)))
+ tail = (unsigned int) readw(&ap->txq.tail);
size = portp->txsize;
if (head >= tail) {
len = size - (head - tail) - 1;
@@ -1703,7 +1599,7 @@ static void stli_flushchars(struct tty_struct *tty)
while (len > 0) {
stlen = MIN(len, stlen);
- memcpy((shbuf + head), buf, stlen);
+ memcpy_toio(shbuf + head, buf, stlen);
buf += stlen;
len -= stlen;
count += stlen;
@@ -1714,73 +1610,66 @@ static void stli_flushchars(struct tty_struct *tty)
}
}
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- ap->txq.head = head;
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+ writew(head, &ap->txq.head);
if (test_bit(ST_TXBUSY, &portp->state)) {
- if (ap->changed.data & DT_TXEMPTY)
- ap->changed.data &= ~DT_TXEMPTY;
+ if (readl(&ap->changed.data) & DT_TXEMPTY)
+ writel(readl(&ap->changed.data) & ~DT_TXEMPTY, &ap->changed.data);
}
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
- *bits |= portp->portbit;
+ writeb(readb(bits) | portp->portbit, bits);
set_bit(ST_TXBUSY, &portp->state);
EBRDDISABLE(brdp);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
static int stli_writeroom(struct tty_struct *tty)
{
- volatile cdkasyrq_t *rp;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int head, tail, len;
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_writeroom(tty=%x)\n", (int) tty);
-#endif
+ cdkasyrq_t __iomem *rp;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int head, tail, len;
+ unsigned long flags;
- if (tty == (struct tty_struct *) NULL)
- return(0);
if (tty == stli_txcooktty) {
if (stli_txcookrealsize != 0) {
len = stli_txcookrealsize - stli_txcooksize;
- return(len);
+ return len;
}
}
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(0);
+ if (portp == NULL)
+ return 0;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
- head = (unsigned int) rp->head;
- tail = (unsigned int) rp->tail;
- if (tail != ((unsigned int) rp->tail))
- tail = (unsigned int) rp->tail;
+ rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+ head = (unsigned int) readw(&rp->head);
+ tail = (unsigned int) readw(&rp->tail);
+ if (tail != ((unsigned int) readw(&rp->tail)))
+ tail = (unsigned int) readw(&rp->tail);
len = (head >= tail) ? (portp->txsize - (head - tail)) : (tail - head);
len--;
EBRDDISABLE(brdp);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
if (tty == stli_txcooktty) {
stli_txcookrealsize = len;
len -= stli_txcooksize;
}
- return(len);
+ return len;
}
/*****************************************************************************/
@@ -1795,44 +1684,37 @@ static int stli_writeroom(struct tty_struct *tty)
static int stli_charsinbuffer(struct tty_struct *tty)
{
- volatile cdkasyrq_t *rp;
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int head, tail, len;
- unsigned long flags;
-
-#ifdef DEBUG
- printk("stli_charsinbuffer(tty=%x)\n", (int) tty);
-#endif
+ cdkasyrq_t __iomem *rp;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int head, tail, len;
+ unsigned long flags;
- if (tty == (struct tty_struct *) NULL)
- return(0);
if (tty == stli_txcooktty)
stli_flushchars(tty);
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(0);
+ if (portp == NULL)
+ return 0;
if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
- head = (unsigned int) rp->head;
- tail = (unsigned int) rp->tail;
- if (tail != ((unsigned int) rp->tail))
- tail = (unsigned int) rp->tail;
+ rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->txq;
+ head = (unsigned int) readw(&rp->head);
+ tail = (unsigned int) readw(&rp->tail);
+ if (tail != ((unsigned int) readw(&rp->tail)))
+ tail = (unsigned int) readw(&rp->tail);
len = (head >= tail) ? (head - tail) : (portp->txsize - (tail - head));
if ((len == 0) && test_bit(ST_TXBUSY, &portp->state))
len = 1;
EBRDDISABLE(brdp);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
- return(len);
+ return len;
}
/*****************************************************************************/
@@ -1843,12 +1725,8 @@ static int stli_charsinbuffer(struct tty_struct *tty)
static int stli_getserial(stliport_t *portp, struct serial_struct __user *sp)
{
- struct serial_struct sio;
- stlibrd_t *brdp;
-
-#ifdef DEBUG
- printk("stli_getserial(portp=%x,sp=%x)\n", (int) portp, (int) sp);
-#endif
+ struct serial_struct sio;
+ stlibrd_t *brdp;
memset(&sio, 0, sizeof(struct serial_struct));
sio.type = PORT_UNKNOWN;
@@ -1863,7 +1741,7 @@ static int stli_getserial(stliport_t *portp, struct serial_struct __user *sp)
sio.hub6 = 0;
brdp = stli_brds[portp->brdnr];
- if (brdp != (stlibrd_t *) NULL)
+ if (brdp != NULL)
sio.port = brdp->iobase;
return copy_to_user(sp, &sio, sizeof(struct serial_struct)) ?
@@ -1880,12 +1758,8 @@ static int stli_getserial(stliport_t *portp, struct serial_struct __user *sp)
static int stli_setserial(stliport_t *portp, struct serial_struct __user *sp)
{
- struct serial_struct sio;
- int rc;
-
-#ifdef DEBUG
- printk("stli_setserial(portp=%p,sp=%p)\n", portp, sp);
-#endif
+ struct serial_struct sio;
+ int rc;
if (copy_from_user(&sio, sp, sizeof(struct serial_struct)))
return -EFAULT;
@@ -1894,7 +1768,7 @@ static int stli_setserial(stliport_t *portp, struct serial_struct __user *sp)
(sio.close_delay != portp->close_delay) ||
((sio.flags & ~ASYNC_USR_MASK) !=
(portp->flags & ~ASYNC_USR_MASK)))
- return(-EPERM);
+ return -EPERM;
}
portp->flags = (portp->flags & ~ASYNC_USR_MASK) |
@@ -1905,8 +1779,8 @@ static int stli_setserial(stliport_t *portp, struct serial_struct __user *sp)
portp->custom_divisor = sio.custom_divisor;
if ((rc = stli_setport(portp)) < 0)
- return(rc);
- return(0);
+ return rc;
+ return 0;
}
/*****************************************************************************/
@@ -1917,19 +1791,19 @@ static int stli_tiocmget(struct tty_struct *tty, struct file *file)
stlibrd_t *brdp;
int rc;
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ if (portp == NULL)
+ return -ENODEV;
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
if (tty->flags & (1 << TTY_IO_ERROR))
- return(-EIO);
+ return -EIO;
if ((rc = stli_cmdwait(brdp, portp, A_GETSIGNALS,
&portp->asig, sizeof(asysigs_t), 1)) < 0)
- return(rc);
+ return rc;
return stli_mktiocm(portp->asig.sigvalue);
}
@@ -1941,15 +1815,15 @@ static int stli_tiocmset(struct tty_struct *tty, struct file *file,
stlibrd_t *brdp;
int rts = -1, dtr = -1;
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ if (portp == NULL)
+ return -ENODEV;
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
if (tty->flags & (1 << TTY_IO_ERROR))
- return(-EIO);
+ return -EIO;
if (set & TIOCM_RTS)
rts = 1;
@@ -1968,32 +1842,25 @@ static int stli_tiocmset(struct tty_struct *tty, struct file *file,
static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg)
{
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned int ival;
- int rc;
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned int ival;
+ int rc;
void __user *argp = (void __user *)arg;
-#ifdef DEBUG
- printk("stli_ioctl(tty=%x,file=%x,cmd=%x,arg=%x)\n",
- (int) tty, (int) file, cmd, (int) arg);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return(-ENODEV);
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return(0);
+ if (portp == NULL)
+ return -ENODEV;
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
+ return 0;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(0);
+ if (brdp == NULL)
+ return 0;
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != COM_GETPORTSTATS) && (cmd != COM_CLRPORTSTATS)) {
if (tty->flags & (1 << TTY_IO_ERROR))
- return(-EIO);
+ return -EIO;
}
rc = 0;
@@ -2040,7 +1907,7 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm
break;
}
- return(rc);
+ return rc;
}
/*****************************************************************************/
@@ -2052,24 +1919,20 @@ static int stli_ioctl(struct tty_struct *tty, struct file *file, unsigned int cm
static void stli_settermios(struct tty_struct *tty, struct termios *old)
{
- stliport_t *portp;
- stlibrd_t *brdp;
- struct termios *tiosp;
- asyport_t aport;
-
-#ifdef DEBUG
- printk("stli_settermios(tty=%x,old=%x)\n", (int) tty, (int) old);
-#endif
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ struct termios *tiosp;
+ asyport_t aport;
- if (tty == (struct tty_struct *) NULL)
+ if (tty == NULL)
return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
tiosp = tty->termios;
@@ -2102,18 +1965,9 @@ static void stli_settermios(struct tty_struct *tty, struct termios *old)
static void stli_throttle(struct tty_struct *tty)
{
- stliport_t *portp;
-
-#ifdef DEBUG
- printk("stli_throttle(tty=%x)\n", (int) tty);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
+ stliport_t *portp = tty->driver_data;
+ if (portp == NULL)
return;
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return;
-
set_bit(ST_RXSTOP, &portp->state);
}
@@ -2127,88 +1981,30 @@ static void stli_throttle(struct tty_struct *tty)
static void stli_unthrottle(struct tty_struct *tty)
{
- stliport_t *portp;
-
-#ifdef DEBUG
- printk("stli_unthrottle(tty=%x)\n", (int) tty);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ stliport_t *portp = tty->driver_data;
+ if (portp == NULL)
return;
-
clear_bit(ST_RXSTOP, &portp->state);
}
/*****************************************************************************/
/*
- * Stop the transmitter. Basically to do this we will just turn TX
- * interrupts off.
+ * Stop the transmitter.
*/
static void stli_stop(struct tty_struct *tty)
{
- stlibrd_t *brdp;
- stliport_t *portp;
- asyctrl_t actrl;
-
-#ifdef DEBUG
- printk("stli_stop(tty=%x)\n", (int) tty);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return;
- brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return;
-
- memset(&actrl, 0, sizeof(asyctrl_t));
- actrl.txctrl = CT_STOPFLOW;
-#if 0
- stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
-#endif
}
/*****************************************************************************/
/*
- * Start the transmitter again. Just turn TX interrupts back on.
+ * Start the transmitter again.
*/
static void stli_start(struct tty_struct *tty)
{
- stliport_t *portp;
- stlibrd_t *brdp;
- asyctrl_t actrl;
-
-#ifdef DEBUG
- printk("stli_start(tty=%x)\n", (int) tty);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
- portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
- return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
- return;
- brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return;
-
- memset(&actrl, 0, sizeof(asyctrl_t));
- actrl.txctrl = CT_STARTFLOW;
-#if 0
- stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
-#endif
}
/*****************************************************************************/
@@ -2224,22 +2020,9 @@ static void stli_start(struct tty_struct *tty)
static void stli_dohangup(void *arg)
{
- stliport_t *portp;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_dohangup(portp=%x)\n", (int) arg);
-#endif
-
- /*
- * FIXME: There's a module removal race here: tty_hangup
- * calls schedule_work which will call into this
- * driver later.
- */
- portp = (stliport_t *) arg;
- if (portp != (stliport_t *) NULL) {
- if (portp->tty != (struct tty_struct *) NULL) {
- tty_hangup(portp->tty);
- }
+ stliport_t *portp = (stliport_t *) arg;
+ if (portp->tty != NULL) {
+ tty_hangup(portp->tty);
}
}
@@ -2254,31 +2037,25 @@ static void stli_dohangup(void *arg)
static void stli_hangup(struct tty_struct *tty)
{
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned long flags;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_hangup(tty=%x)\n", (int) tty);
-#endif
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned long flags;
- if (tty == (struct tty_struct *) NULL)
- return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
portp->flags &= ~ASYNC_INITIALIZED;
- save_flags(flags);
- cli();
- if (! test_bit(ST_CLOSING, &portp->state))
+ if (!test_bit(ST_CLOSING, &portp->state))
stli_rawclose(brdp, portp, 0, 0);
+
+ spin_lock_irqsave(&stli_lock, flags);
if (tty->termios->c_cflag & HUPCL) {
stli_mkasysigs(&portp->asig, 0, 0);
if (test_bit(ST_CMDING, &portp->state)) {
@@ -2290,14 +2067,15 @@ static void stli_hangup(struct tty_struct *tty)
&portp->asig, sizeof(asysigs_t), 0);
}
}
- restore_flags(flags);
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
set_bit(TTY_IO_ERROR, &tty->flags);
- portp->tty = (struct tty_struct *) NULL;
+ portp->tty = NULL;
portp->flags &= ~ASYNC_NORMAL_ACTIVE;
portp->refcount = 0;
+ spin_unlock_irqrestore(&stli_lock, flags);
+
wake_up_interruptible(&portp->open_wait);
}
@@ -2312,29 +2090,22 @@ static void stli_hangup(struct tty_struct *tty)
static void stli_flushbuffer(struct tty_struct *tty)
{
- stliport_t *portp;
- stlibrd_t *brdp;
- unsigned long ftype, flags;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_flushbuffer(tty=%x)\n", (int) tty);
-#endif
+ stliport_t *portp;
+ stlibrd_t *brdp;
+ unsigned long ftype, flags;
- if (tty == (struct tty_struct *) NULL)
- return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
if (tty == stli_txcooktty) {
- stli_txcooktty = (struct tty_struct *) NULL;
+ stli_txcooktty = NULL;
stli_txcooksize = 0;
stli_txcookrealsize = 0;
}
@@ -2346,15 +2117,10 @@ static void stli_flushbuffer(struct tty_struct *tty)
ftype |= FLUSHRX;
clear_bit(ST_DOFLUSHRX, &portp->state);
}
- stli_sendcmd(brdp, portp, A_FLUSH, &ftype,
- sizeof(unsigned long), 0);
+ __stli_sendcmd(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
}
- restore_flags(flags);
-
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
+ spin_unlock_irqrestore(&brd_lock, flags);
+ tty_wakeup(tty);
}
/*****************************************************************************/
@@ -2364,55 +2130,31 @@ static void stli_breakctl(struct tty_struct *tty, int state)
stlibrd_t *brdp;
stliport_t *portp;
long arg;
- /* long savestate, savetime; */
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_breakctl(tty=%x,state=%d)\n", (int) tty, state);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
-/*
- * Due to a bug in the tty send_break() code we need to preserve
- * the current process state and timeout...
- savetime = current->timeout;
- savestate = current->state;
- */
-
arg = (state == -1) ? BREAKON : BREAKOFF;
stli_cmdwait(brdp, portp, A_BREAK, &arg, sizeof(long), 0);
-
-/*
- *
- current->timeout = savetime;
- current->state = savestate;
- */
}
/*****************************************************************************/
static void stli_waituntilsent(struct tty_struct *tty, int timeout)
{
- stliport_t *portp;
- unsigned long tend;
+ stliport_t *portp;
+ unsigned long tend;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_waituntilsent(tty=%x,timeout=%x)\n", (int) tty, timeout);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
+ if (tty == NULL)
return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
if (timeout == 0)
@@ -2436,19 +2178,13 @@ static void stli_sendxchar(struct tty_struct *tty, char ch)
stliport_t *portp;
asyctrl_t actrl;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_sendxchar(tty=%x,ch=%x)\n", (int) tty, ch);
-#endif
-
- if (tty == (struct tty_struct *) NULL)
- return;
portp = tty->driver_data;
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
return;
- if ((portp->brdnr < 0) || (portp->brdnr >= stli_nrbrds))
+ if (portp->brdnr < 0 || portp->brdnr >= stli_nrbrds)
return;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
return;
memset(&actrl, 0, sizeof(asyctrl_t));
@@ -2460,7 +2196,6 @@ static void stli_sendxchar(struct tty_struct *tty, char ch)
actrl.txctrl = CT_SENDCHR;
actrl.tximdch = ch;
}
-
stli_cmdwait(brdp, portp, A_PORTCTRL, &actrl, sizeof(asyctrl_t), 0);
}
@@ -2476,17 +2211,17 @@ static void stli_sendxchar(struct tty_struct *tty, char ch)
static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *pos)
{
- char *sp, *uart;
- int rc, cnt;
+ char *sp, *uart;
+ int rc, cnt;
rc = stli_portcmdstats(portp);
uart = "UNKNOWN";
if (brdp->state & BST_STARTED) {
switch (stli_comstats.hwid) {
- case 0: uart = "2681"; break;
- case 1: uart = "SC26198"; break;
- default: uart = "CD1400"; break;
+ case 0: uart = "2681"; break;
+ case 1: uart = "SC26198"; break;
+ default:uart = "CD1400"; break;
}
}
@@ -2537,17 +2272,11 @@ static int stli_portinfo(stlibrd_t *brdp, stliport_t *portp, int portnr, char *p
static int stli_readproc(char *page, char **start, off_t off, int count, int *eof, void *data)
{
- stlibrd_t *brdp;
- stliport_t *portp;
- int brdnr, portnr, totalport;
- int curoff, maxoff;
- char *pos;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_readproc(page=%x,start=%x,off=%x,count=%d,eof=%x,"
- "data=%x\n", (int) page, (int) start, (int) off, count,
- (int) eof, (int) data);
-#endif
+ stlibrd_t *brdp;
+ stliport_t *portp;
+ int brdnr, portnr, totalport;
+ int curoff, maxoff;
+ char *pos;
pos = page;
totalport = 0;
@@ -2568,7 +2297,7 @@ static int stli_readproc(char *page, char **start, off_t off, int count, int *eo
*/
for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
continue;
if (brdp->state == 0)
continue;
@@ -2583,7 +2312,7 @@ static int stli_readproc(char *page, char **start, off_t off, int count, int *eo
for (portnr = 0; (portnr < brdp->nrports); portnr++,
totalport++) {
portp = brdp->ports[portnr];
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
continue;
if (off >= (curoff += MAXLINE))
continue;
@@ -2610,49 +2339,54 @@ stli_readdone:
* a poll routine that does not have user context. Therefore you cannot
* copy back directly into user space, or to the kernel stack of a
* process. This routine does not sleep, so can be called from anywhere.
+ *
+ * The caller must hold the brd_lock (see also stli_sendcmd the usual
+ * entry point)
*/
-static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+static void __stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
{
- volatile cdkhdr_t *hdrp;
- volatile cdkctrl_t *cp;
- volatile unsigned char *bits;
- unsigned long flags;
+ cdkhdr_t __iomem *hdrp;
+ cdkctrl_t __iomem *cp;
+ unsigned char __iomem *bits;
+ unsigned long flags;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_sendcmd(brdp=%x,portp=%x,cmd=%x,arg=%x,size=%d,"
- "copyback=%d)\n", (int) brdp, (int) portp, (int) cmd,
- (int) arg, size, copyback);
-#endif
-
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
if (test_bit(ST_CMDING, &portp->state)) {
printk(KERN_ERR "STALLION: command already busy, cmd=%x!\n",
(int) cmd);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
return;
}
EBRDENABLE(brdp);
- cp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
+ cp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->ctrl;
if (size > 0) {
- memcpy((void *) &(cp->args[0]), arg, size);
+ memcpy_toio((void __iomem *) &(cp->args[0]), arg, size);
if (copyback) {
portp->argp = arg;
portp->argsize = size;
}
}
- cp->status = 0;
- cp->cmd = cmd;
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- bits = ((volatile unsigned char *) hdrp) + brdp->slaveoffset +
+ writel(0, &cp->status);
+ writel(cmd, &cp->cmd);
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ bits = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset +
portp->portidx;
- *bits |= portp->portbit;
+ writeb(readb(bits) | portp->portbit, bits);
set_bit(ST_CMDING, &portp->state);
EBRDDISABLE(brdp);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
+}
+
+static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd, void *arg, int size, int copyback)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&brd_lock, flags);
+ __stli_sendcmd(brdp, portp, cmd, arg, size, copyback);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -2667,28 +2401,23 @@ static void stli_sendcmd(stlibrd_t *brdp, stliport_t *portp, unsigned long cmd,
static void stli_read(stlibrd_t *brdp, stliport_t *portp)
{
- volatile cdkasyrq_t *rp;
- volatile char *shbuf;
+ cdkasyrq_t __iomem *rp;
+ char __iomem *shbuf;
struct tty_struct *tty;
- unsigned int head, tail, size;
- unsigned int len, stlen;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_read(brdp=%x,portp=%d)\n",
- (int) brdp, (int) portp);
-#endif
+ unsigned int head, tail, size;
+ unsigned int len, stlen;
if (test_bit(ST_RXSTOP, &portp->state))
return;
tty = portp->tty;
- if (tty == (struct tty_struct *) NULL)
+ if (tty == NULL)
return;
- rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
- head = (unsigned int) rp->head;
- if (head != ((unsigned int) rp->head))
- head = (unsigned int) rp->head;
- tail = (unsigned int) rp->tail;
+ rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+ head = (unsigned int) readw(&rp->head);
+ if (head != ((unsigned int) readw(&rp->head)))
+ head = (unsigned int) readw(&rp->head);
+ tail = (unsigned int) readw(&rp->tail);
size = portp->rxsize;
if (head >= tail) {
len = head - tail;
@@ -2699,12 +2428,15 @@ static void stli_read(stlibrd_t *brdp, stliport_t *portp)
}
len = tty_buffer_request_room(tty, len);
- /* FIXME : iomap ? */
- shbuf = (volatile char *) EBRDGETMEMPTR(brdp, portp->rxoffset);
+
+ shbuf = (char __iomem *) EBRDGETMEMPTR(brdp, portp->rxoffset);
while (len > 0) {
+ unsigned char *cptr;
+
stlen = MIN(len, stlen);
- tty_insert_flip_string(tty, (char *)(shbuf + tail), stlen);
+ tty_prepare_flip_string(tty, &cptr, stlen);
+ memcpy_fromio(cptr, shbuf + tail, stlen);
len -= stlen;
tail += stlen;
if (tail >= size) {
@@ -2712,8 +2444,8 @@ static void stli_read(stlibrd_t *brdp, stliport_t *portp)
stlen = head;
}
}
- rp = &((volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
- rp->tail = tail;
+ rp = &((cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr))->rxq;
+ writew(tail, &rp->tail);
if (head != tail)
set_bit(ST_RXING, &portp->state);
@@ -2729,9 +2461,9 @@ static void stli_read(stlibrd_t *brdp, stliport_t *portp)
* difficult to deal with them here.
*/
-static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
+static void stli_dodelaycmd(stliport_t *portp, cdkctrl_t __iomem *cp)
{
- int cmd;
+ int cmd;
if (test_bit(ST_DOSIGS, &portp->state)) {
if (test_bit(ST_DOFLUSHTX, &portp->state) &&
@@ -2746,10 +2478,10 @@ static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
clear_bit(ST_DOFLUSHTX, &portp->state);
clear_bit(ST_DOFLUSHRX, &portp->state);
clear_bit(ST_DOSIGS, &portp->state);
- memcpy((void *) &(cp->args[0]), (void *) &portp->asig,
+ memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &portp->asig,
sizeof(asysigs_t));
- cp->status = 0;
- cp->cmd = cmd;
+ writel(0, &cp->status);
+ writel(cmd, &cp->cmd);
set_bit(ST_CMDING, &portp->state);
} else if (test_bit(ST_DOFLUSHTX, &portp->state) ||
test_bit(ST_DOFLUSHRX, &portp->state)) {
@@ -2757,9 +2489,9 @@ static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
cmd |= ((test_bit(ST_DOFLUSHRX, &portp->state)) ? FLUSHRX : 0);
clear_bit(ST_DOFLUSHTX, &portp->state);
clear_bit(ST_DOFLUSHRX, &portp->state);
- memcpy((void *) &(cp->args[0]), (void *) &cmd, sizeof(int));
- cp->status = 0;
- cp->cmd = A_FLUSH;
+ memcpy_toio((void __iomem *) &(cp->args[0]), (void *) &cmd, sizeof(int));
+ writel(0, &cp->status);
+ writel(A_FLUSH, &cp->cmd);
set_bit(ST_CMDING, &portp->state);
}
}
@@ -2779,30 +2511,25 @@ static void stli_dodelaycmd(stliport_t *portp, volatile cdkctrl_t *cp)
static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
{
- volatile cdkasy_t *ap;
- volatile cdkctrl_t *cp;
- struct tty_struct *tty;
- asynotify_t nt;
- unsigned long oldsigs;
- int rc, donerx;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_hostcmd(brdp=%x,channr=%d)\n",
- (int) brdp, channr);
-#endif
-
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
+ cdkasy_t __iomem *ap;
+ cdkctrl_t __iomem *cp;
+ struct tty_struct *tty;
+ asynotify_t nt;
+ unsigned long oldsigs;
+ int rc, donerx;
+
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
cp = &ap->ctrl;
/*
* Check if we are waiting for an open completion message.
*/
if (test_bit(ST_OPENING, &portp->state)) {
- rc = (int) cp->openarg;
- if ((cp->open == 0) && (rc != 0)) {
+ rc = readl(&cp->openarg);
+ if (readb(&cp->open) == 0 && rc != 0) {
if (rc > 0)
rc--;
- cp->openarg = 0;
+ writel(0, &cp->openarg);
portp->rc = rc;
clear_bit(ST_OPENING, &portp->state);
wake_up_interruptible(&portp->raw_wait);
@@ -2813,11 +2540,11 @@ static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
* Check if we are waiting for a close completion message.
*/
if (test_bit(ST_CLOSING, &portp->state)) {
- rc = (int) cp->closearg;
- if ((cp->close == 0) && (rc != 0)) {
+ rc = (int) readl(&cp->closearg);
+ if (readb(&cp->close) == 0 && rc != 0) {
if (rc > 0)
rc--;
- cp->closearg = 0;
+ writel(0, &cp->closearg);
portp->rc = rc;
clear_bit(ST_CLOSING, &portp->state);
wake_up_interruptible(&portp->raw_wait);
@@ -2829,16 +2556,16 @@ static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
* need to copy out the command results associated with this command.
*/
if (test_bit(ST_CMDING, &portp->state)) {
- rc = cp->status;
- if ((cp->cmd == 0) && (rc != 0)) {
+ rc = readl(&cp->status);
+ if (readl(&cp->cmd) == 0 && rc != 0) {
if (rc > 0)
rc--;
- if (portp->argp != (void *) NULL) {
- memcpy(portp->argp, (void *) &(cp->args[0]),
+ if (portp->argp != NULL) {
+ memcpy_fromio(portp->argp, (void __iomem *) &(cp->args[0]),
portp->argsize);
- portp->argp = (void *) NULL;
+ portp->argp = NULL;
}
- cp->status = 0;
+ writel(0, &cp->status);
portp->rc = rc;
clear_bit(ST_CMDING, &portp->state);
stli_dodelaycmd(portp, cp);
@@ -2877,18 +2604,15 @@ static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
if (nt.data & DT_TXEMPTY)
clear_bit(ST_TXBUSY, &portp->state);
if (nt.data & (DT_TXEMPTY | DT_TXLOW)) {
- if (tty != (struct tty_struct *) NULL) {
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup) {
- (tty->ldisc.write_wakeup)(tty);
- EBRDENABLE(brdp);
- }
+ if (tty != NULL) {
+ tty_wakeup(tty);
+ EBRDENABLE(brdp);
wake_up_interruptible(&tty->write_wait);
}
}
if ((nt.data & DT_RXBREAK) && (portp->rxmarkmsk & BRKINT)) {
- if (tty != (struct tty_struct *) NULL) {
+ if (tty != NULL) {
tty_insert_flip_char(tty, 0, TTY_BREAK);
if (portp->flags & ASYNC_SAK) {
do_SAK(tty);
@@ -2932,14 +2656,14 @@ static int stli_hostcmd(stlibrd_t *brdp, stliport_t *portp)
* at the cdk header structure.
*/
-static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
+static void stli_brdpoll(stlibrd_t *brdp, cdkhdr_t __iomem *hdrp)
{
- stliport_t *portp;
- unsigned char hostbits[(STL_MAXCHANS / 8) + 1];
- unsigned char slavebits[(STL_MAXCHANS / 8) + 1];
- unsigned char *slavep;
- int bitpos, bitat, bitsize;
- int channr, nrdevs, slavebitchange;
+ stliport_t *portp;
+ unsigned char hostbits[(STL_MAXCHANS / 8) + 1];
+ unsigned char slavebits[(STL_MAXCHANS / 8) + 1];
+ unsigned char __iomem *slavep;
+ int bitpos, bitat, bitsize;
+ int channr, nrdevs, slavebitchange;
bitsize = brdp->bitsize;
nrdevs = brdp->nrdevs;
@@ -2951,7 +2675,7 @@ static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
* 8 service bits at a time in the inner loop, so we can bypass
* the lot if none of them want service.
*/
- memcpy(&hostbits[0], (((unsigned char *) hdrp) + brdp->hostoffset),
+ memcpy_fromio(&hostbits[0], (((unsigned char __iomem *) hdrp) + brdp->hostoffset),
bitsize);
memset(&slavebits[0], 0, bitsize);
@@ -2978,11 +2702,11 @@ static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
* service may initiate more slave requests.
*/
if (slavebitchange) {
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- slavep = ((unsigned char *) hdrp) + brdp->slaveoffset;
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ slavep = ((unsigned char __iomem *) hdrp) + brdp->slaveoffset;
for (bitpos = 0; (bitpos < bitsize); bitpos++) {
- if (slavebits[bitpos])
- slavep[bitpos] &= ~slavebits[bitpos];
+ if (readb(slavebits + bitpos))
+ writeb(readb(slavep + bitpos) & ~slavebits[bitpos], slavebits + bitpos);
}
}
}
@@ -3000,9 +2724,9 @@ static void stli_brdpoll(stlibrd_t *brdp, volatile cdkhdr_t *hdrp)
static void stli_poll(unsigned long arg)
{
- volatile cdkhdr_t *hdrp;
- stlibrd_t *brdp;
- int brdnr;
+ cdkhdr_t __iomem *hdrp;
+ stlibrd_t *brdp;
+ int brdnr;
stli_timerlist.expires = STLI_TIMEOUT;
add_timer(&stli_timerlist);
@@ -3012,16 +2736,18 @@ static void stli_poll(unsigned long arg)
*/
for (brdnr = 0; (brdnr < stli_nrbrds); brdnr++) {
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
continue;
if ((brdp->state & BST_STARTED) == 0)
continue;
+ spin_lock(&brd_lock);
EBRDENABLE(brdp);
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
- if (hdrp->hostreq)
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ if (readb(&hdrp->hostreq))
stli_brdpoll(brdp, hdrp);
EBRDDISABLE(brdp);
+ spin_unlock(&brd_lock);
}
}
@@ -3034,11 +2760,6 @@ static void stli_poll(unsigned long arg)
static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tiosp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_mkasyport(portp=%x,pp=%x,tiosp=%d)\n",
- (int) portp, (int) pp, (int) tiosp);
-#endif
-
memset(pp, 0, sizeof(asyport_t));
/*
@@ -3157,11 +2878,6 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio
static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_mkasysigs(sp=%x,dtr=%d,rts=%d)\n",
- (int) sp, dtr, rts);
-#endif
-
memset(sp, 0, sizeof(asysigs_t));
if (dtr >= 0) {
sp->signal |= SG_DTR;
@@ -3182,13 +2898,7 @@ static void stli_mkasysigs(asysigs_t *sp, int dtr, int rts)
static long stli_mktiocm(unsigned long sigvalue)
{
- long tiocm;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_mktiocm(sigvalue=%x)\n", (int) sigvalue);
-#endif
-
- tiocm = 0;
+ long tiocm = 0;
tiocm |= ((sigvalue & SG_DCD) ? TIOCM_CD : 0);
tiocm |= ((sigvalue & SG_CTS) ? TIOCM_CTS : 0);
tiocm |= ((sigvalue & SG_RI) ? TIOCM_RI : 0);
@@ -3210,10 +2920,6 @@ static int stli_initports(stlibrd_t *brdp)
stliport_t *portp;
int i, panelnr, panelport;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_initports(brdp=%x)\n", (int) brdp);
-#endif
-
for (i = 0, panelnr = 0, panelport = 0; (i < brdp->nrports); i++) {
portp = kzalloc(sizeof(stliport_t), GFP_KERNEL);
if (!portp) {
@@ -3240,7 +2946,7 @@ static int stli_initports(stlibrd_t *brdp)
brdp->ports[i] = portp;
}
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -3253,10 +2959,6 @@ static void stli_ecpinit(stlibrd_t *brdp)
{
unsigned long memconf;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpinit(brdp=%d)\n", (int) brdp);
-#endif
-
outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
udelay(10);
outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
@@ -3270,9 +2972,6 @@ static void stli_ecpinit(stlibrd_t *brdp)
static void stli_ecpenable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpenable(brdp=%x)\n", (int) brdp);
-#endif
outb(ECP_ATENABLE, (brdp->iobase + ECP_ATCONFR));
}
@@ -3280,9 +2979,6 @@ static void stli_ecpenable(stlibrd_t *brdp)
static void stli_ecpdisable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpdisable(brdp=%x)\n", (int) brdp);
-#endif
outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
}
@@ -3290,13 +2986,8 @@ static void stli_ecpdisable(stlibrd_t *brdp)
static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
- void *ptr;
- unsigned char val;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
- (int) offset);
-#endif
+ void *ptr;
+ unsigned char val;
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
@@ -3316,10 +3007,6 @@ static char *stli_ecpgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
static void stli_ecpreset(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpreset(brdp=%x)\n", (int) brdp);
-#endif
-
outb(ECP_ATSTOP, (brdp->iobase + ECP_ATCONFR));
udelay(10);
outb(ECP_ATDISABLE, (brdp->iobase + ECP_ATCONFR));
@@ -3330,9 +3017,6 @@ static void stli_ecpreset(stlibrd_t *brdp)
static void stli_ecpintr(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpintr(brdp=%x)\n", (int) brdp);
-#endif
outb(0x1, brdp->iobase);
}
@@ -3346,10 +3030,6 @@ static void stli_ecpeiinit(stlibrd_t *brdp)
{
unsigned long memconf;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpeiinit(brdp=%x)\n", (int) brdp);
-#endif
-
outb(0x1, (brdp->iobase + ECP_EIBRDENAB));
outb(ECP_EISTOP, (brdp->iobase + ECP_EICONFR));
udelay(10);
@@ -3383,11 +3063,6 @@ static char *stli_ecpeigetmemptr(stlibrd_t *brdp, unsigned long offset, int line
void *ptr;
unsigned char val;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecpeigetmemptr(brdp=%x,offset=%x,line=%d)\n",
- (int) brdp, (int) offset, line);
-#endif
-
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
"range at line=%d(%d), brd=%d\n",
@@ -3437,8 +3112,8 @@ static void stli_ecpmcdisable(stlibrd_t *brdp)
static char *stli_ecpmcgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
- void *ptr;
- unsigned char val;
+ void *ptr;
+ unsigned char val;
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
@@ -3472,10 +3147,6 @@ static void stli_ecpmcreset(stlibrd_t *brdp)
static void stli_ecppciinit(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecppciinit(brdp=%x)\n", (int) brdp);
-#endif
-
outb(ECP_PCISTOP, (brdp->iobase + ECP_PCICONFR));
udelay(10);
outb(0, (brdp->iobase + ECP_PCICONFR));
@@ -3489,11 +3160,6 @@ static char *stli_ecppcigetmemptr(stlibrd_t *brdp, unsigned long offset, int lin
void *ptr;
unsigned char val;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_ecppcigetmemptr(brdp=%x,offset=%x,line=%d)\n",
- (int) brdp, (int) offset, line);
-#endif
-
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
"range at line=%d(%d), board=%d\n",
@@ -3528,10 +3194,6 @@ static void stli_onbinit(stlibrd_t *brdp)
{
unsigned long memconf;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbinit(brdp=%d)\n", (int) brdp);
-#endif
-
outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
udelay(10);
outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
@@ -3547,9 +3209,6 @@ static void stli_onbinit(stlibrd_t *brdp)
static void stli_onbenable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbenable(brdp=%x)\n", (int) brdp);
-#endif
outb((brdp->enabval | ONB_ATENABLE), (brdp->iobase + ONB_ATCONFR));
}
@@ -3557,9 +3216,6 @@ static void stli_onbenable(stlibrd_t *brdp)
static void stli_onbdisable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbdisable(brdp=%x)\n", (int) brdp);
-#endif
outb((brdp->enabval | ONB_ATDISABLE), (brdp->iobase + ONB_ATCONFR));
}
@@ -3569,11 +3225,6 @@ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
void *ptr;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
- (int) offset);
-#endif
-
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
"range at line=%d(%d), brd=%d\n",
@@ -3589,11 +3240,6 @@ static char *stli_onbgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
static void stli_onbreset(stlibrd_t *brdp)
{
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbreset(brdp=%x)\n", (int) brdp);
-#endif
-
outb(ONB_ATSTOP, (brdp->iobase + ONB_ATCONFR));
udelay(10);
outb(ONB_ATDISABLE, (brdp->iobase + ONB_ATCONFR));
@@ -3610,10 +3256,6 @@ static void stli_onbeinit(stlibrd_t *brdp)
{
unsigned long memconf;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbeinit(brdp=%d)\n", (int) brdp);
-#endif
-
outb(0x1, (brdp->iobase + ONB_EIBRDENAB));
outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
udelay(10);
@@ -3632,9 +3274,6 @@ static void stli_onbeinit(stlibrd_t *brdp)
static void stli_onbeenable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbeenable(brdp=%x)\n", (int) brdp);
-#endif
outb(ONB_EIENABLE, (brdp->iobase + ONB_EICONFR));
}
@@ -3642,9 +3281,6 @@ static void stli_onbeenable(stlibrd_t *brdp)
static void stli_onbedisable(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbedisable(brdp=%x)\n", (int) brdp);
-#endif
outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
}
@@ -3652,13 +3288,8 @@ static void stli_onbedisable(stlibrd_t *brdp)
static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
- void *ptr;
- unsigned char val;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_onbegetmemptr(brdp=%x,offset=%x,line=%d)\n",
- (int) brdp, (int) offset, line);
-#endif
+ void *ptr;
+ unsigned char val;
if (offset > brdp->memsize) {
printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
@@ -3681,11 +3312,6 @@ static char *stli_onbegetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
static void stli_onbereset(stlibrd_t *brdp)
{
-
-#ifdef DEBUG
- printk(KERN_ERR "stli_onbereset(brdp=%x)\n", (int) brdp);
-#endif
-
outb(ONB_EISTOP, (brdp->iobase + ONB_EICONFR));
udelay(10);
outb(ONB_EIDISABLE, (brdp->iobase + ONB_EICONFR));
@@ -3700,11 +3326,6 @@ static void stli_onbereset(stlibrd_t *brdp)
static void stli_bbyinit(stlibrd_t *brdp)
{
-
-#ifdef DEBUG
- printk(KERN_ERR "stli_bbyinit(brdp=%d)\n", (int) brdp);
-#endif
-
outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
udelay(10);
outb(0, (brdp->iobase + BBY_ATCONFR));
@@ -3717,24 +3338,13 @@ static void stli_bbyinit(stlibrd_t *brdp)
static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
- void *ptr;
- unsigned char val;
+ void *ptr;
+ unsigned char val;
-#ifdef DEBUG
- printk(KERN_ERR "stli_bbygetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
- (int) offset);
-#endif
+ BUG_ON(offset > brdp->memsize);
- if (offset > brdp->memsize) {
- printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
- "range at line=%d(%d), brd=%d\n",
- (int) offset, line, __LINE__, brdp->brdnr);
- ptr = NULL;
- val = 0;
- } else {
- ptr = brdp->membase + (offset % BBY_PAGESIZE);
- val = (unsigned char) (offset / BBY_PAGESIZE);
- }
+ ptr = brdp->membase + (offset % BBY_PAGESIZE);
+ val = (unsigned char) (offset / BBY_PAGESIZE);
outb(val, (brdp->iobase + BBY_ATCONFR));
return(ptr);
}
@@ -3743,11 +3353,6 @@ static char *stli_bbygetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
static void stli_bbyreset(stlibrd_t *brdp)
{
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_bbyreset(brdp=%x)\n", (int) brdp);
-#endif
-
outb(BBY_ATSTOP, (brdp->iobase + BBY_ATCONFR));
udelay(10);
outb(0, (brdp->iobase + BBY_ATCONFR));
@@ -3762,11 +3367,6 @@ static void stli_bbyreset(stlibrd_t *brdp)
static void stli_stalinit(stlibrd_t *brdp)
{
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_stalinit(brdp=%d)\n", (int) brdp);
-#endif
-
outb(0x1, brdp->iobase);
mdelay(1000);
}
@@ -3775,36 +3375,18 @@ static void stli_stalinit(stlibrd_t *brdp)
static char *stli_stalgetmemptr(stlibrd_t *brdp, unsigned long offset, int line)
{
- void *ptr;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_stalgetmemptr(brdp=%x,offset=%x)\n", (int) brdp,
- (int) offset);
-#endif
-
- if (offset > brdp->memsize) {
- printk(KERN_ERR "STALLION: shared memory pointer=%x out of "
- "range at line=%d(%d), brd=%d\n",
- (int) offset, line, __LINE__, brdp->brdnr);
- ptr = NULL;
- } else {
- ptr = brdp->membase + (offset % STAL_PAGESIZE);
- }
- return(ptr);
+ BUG_ON(offset > brdp->memsize);
+ return brdp->membase + (offset % STAL_PAGESIZE);
}
/*****************************************************************************/
static void stli_stalreset(stlibrd_t *brdp)
{
- volatile unsigned long *vecp;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_stalreset(brdp=%x)\n", (int) brdp);
-#endif
+ u32 __iomem *vecp;
- vecp = (volatile unsigned long *) (brdp->membase + 0x30);
- *vecp = 0xffff0000;
+ vecp = (u32 __iomem *) (brdp->membase + 0x30);
+ writel(0xffff0000, vecp);
outb(0, brdp->iobase);
mdelay(1000);
}
@@ -3818,15 +3400,11 @@ static void stli_stalreset(stlibrd_t *brdp)
static int stli_initecp(stlibrd_t *brdp)
{
- cdkecpsig_t sig;
- cdkecpsig_t *sigsp;
- unsigned int status, nxtid;
- char *name;
- int panelnr, nrports;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_initecp(brdp=%x)\n", (int) brdp);
-#endif
+ cdkecpsig_t sig;
+ cdkecpsig_t __iomem *sigsp;
+ unsigned int status, nxtid;
+ char *name;
+ int panelnr, nrports;
if (!request_region(brdp->iobase, brdp->iosize, "istallion"))
return -EIO;
@@ -3834,7 +3412,7 @@ static int stli_initecp(stlibrd_t *brdp)
if ((brdp->iobase == 0) || (brdp->memaddr == 0))
{
release_region(brdp->iobase, brdp->iosize);
- return(-ENODEV);
+ return -ENODEV;
}
brdp->iosize = ECP_IOSIZE;
@@ -3903,7 +3481,7 @@ static int stli_initecp(stlibrd_t *brdp)
default:
release_region(brdp->iobase, brdp->iosize);
- return(-EINVAL);
+ return -EINVAL;
}
/*
@@ -3915,10 +3493,10 @@ static int stli_initecp(stlibrd_t *brdp)
EBRDINIT(brdp);
brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
- if (brdp->membase == (void *) NULL)
+ if (brdp->membase == NULL)
{
release_region(brdp->iobase, brdp->iosize);
- return(-ENOMEM);
+ return -ENOMEM;
}
/*
@@ -3927,23 +3505,14 @@ static int stli_initecp(stlibrd_t *brdp)
* this is, and what it is connected to it.
*/
EBRDENABLE(brdp);
- sigsp = (cdkecpsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+ sigsp = (cdkecpsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
memcpy(&sig, sigsp, sizeof(cdkecpsig_t));
EBRDDISABLE(brdp);
-#if 0
- printk("%s(%d): sig-> magic=%x rom=%x panel=%x,%x,%x,%x,%x,%x,%x,%x\n",
- __FILE__, __LINE__, (int) sig.magic, sig.romver, sig.panelid[0],
- (int) sig.panelid[1], (int) sig.panelid[2],
- (int) sig.panelid[3], (int) sig.panelid[4],
- (int) sig.panelid[5], (int) sig.panelid[6],
- (int) sig.panelid[7]);
-#endif
-
- if (sig.magic != ECP_MAGIC)
+ if (sig.magic != cpu_to_le32(ECP_MAGIC))
{
release_region(brdp->iobase, brdp->iosize);
- return(-ENODEV);
+ return -ENODEV;
}
/*
@@ -3967,7 +3536,7 @@ static int stli_initecp(stlibrd_t *brdp)
brdp->state |= BST_FOUND;
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -3979,20 +3548,16 @@ static int stli_initecp(stlibrd_t *brdp)
static int stli_initonb(stlibrd_t *brdp)
{
- cdkonbsig_t sig;
- cdkonbsig_t *sigsp;
- char *name;
- int i;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_initonb(brdp=%x)\n", (int) brdp);
-#endif
+ cdkonbsig_t sig;
+ cdkonbsig_t __iomem *sigsp;
+ char *name;
+ int i;
/*
* Do a basic sanity check on the IO and memory addresses.
*/
- if ((brdp->iobase == 0) || (brdp->memaddr == 0))
- return(-ENODEV);
+ if (brdp->iobase == 0 || brdp->memaddr == 0)
+ return -ENODEV;
brdp->iosize = ONB_IOSIZE;
@@ -4010,7 +3575,6 @@ static int stli_initonb(stlibrd_t *brdp)
case BRD_ONBOARD2:
case BRD_ONBOARD2_32:
case BRD_ONBOARDRS:
- brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ONB_MEMSIZE;
brdp->pagesize = ONB_ATPAGESIZE;
brdp->init = stli_onbinit;
@@ -4028,7 +3592,6 @@ static int stli_initonb(stlibrd_t *brdp)
break;
case BRD_ONBOARDE:
- brdp->membase = (void *) brdp->memaddr;
brdp->memsize = ONB_EIMEMSIZE;
brdp->pagesize = ONB_EIPAGESIZE;
brdp->init = stli_onbeinit;
@@ -4044,7 +3607,6 @@ static int stli_initonb(stlibrd_t *brdp)
case BRD_BRUMBY4:
case BRD_BRUMBY8:
case BRD_BRUMBY16:
- brdp->membase = (void *) brdp->memaddr;
brdp->memsize = BBY_MEMSIZE;
brdp->pagesize = BBY_PAGESIZE;
brdp->init = stli_bbyinit;
@@ -4058,7 +3620,6 @@ static int stli_initonb(stlibrd_t *brdp)
break;
case BRD_STALLION:
- brdp->membase = (void *) brdp->memaddr;
brdp->memsize = STAL_MEMSIZE;
brdp->pagesize = STAL_PAGESIZE;
brdp->init = stli_stalinit;
@@ -4073,7 +3634,7 @@ static int stli_initonb(stlibrd_t *brdp)
default:
release_region(brdp->iobase, brdp->iosize);
- return(-EINVAL);
+ return -EINVAL;
}
/*
@@ -4085,10 +3646,10 @@ static int stli_initonb(stlibrd_t *brdp)
EBRDINIT(brdp);
brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
- if (brdp->membase == (void *) NULL)
+ if (brdp->membase == NULL)
{
release_region(brdp->iobase, brdp->iosize);
- return(-ENOMEM);
+ return -ENOMEM;
}
/*
@@ -4097,21 +3658,17 @@ static int stli_initonb(stlibrd_t *brdp)
* this is, and how many ports.
*/
EBRDENABLE(brdp);
- sigsp = (cdkonbsig_t *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
- memcpy(&sig, sigsp, sizeof(cdkonbsig_t));
+ sigsp = (cdkonbsig_t __iomem *) EBRDGETMEMPTR(brdp, CDK_SIGADDR);
+ memcpy_fromio(&sig, sigsp, sizeof(cdkonbsig_t));
EBRDDISABLE(brdp);
-#if 0
- printk("%s(%d): sig-> magic=%x:%x:%x:%x romver=%x amask=%x:%x:%x\n",
- __FILE__, __LINE__, sig.magic0, sig.magic1, sig.magic2,
- sig.magic3, sig.romver, sig.amask0, sig.amask1, sig.amask2);
-#endif
-
- if ((sig.magic0 != ONB_MAGIC0) || (sig.magic1 != ONB_MAGIC1) ||
- (sig.magic2 != ONB_MAGIC2) || (sig.magic3 != ONB_MAGIC3))
+ if (sig.magic0 != cpu_to_le16(ONB_MAGIC0) ||
+ sig.magic1 != cpu_to_le16(ONB_MAGIC1) ||
+ sig.magic2 != cpu_to_le16(ONB_MAGIC2) ||
+ sig.magic3 != cpu_to_le16(ONB_MAGIC3))
{
release_region(brdp->iobase, brdp->iosize);
- return(-ENODEV);
+ return -ENODEV;
}
/*
@@ -4132,7 +3689,7 @@ static int stli_initonb(stlibrd_t *brdp)
brdp->state |= BST_FOUND;
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4145,31 +3702,25 @@ static int stli_initonb(stlibrd_t *brdp)
static int stli_startbrd(stlibrd_t *brdp)
{
- volatile cdkhdr_t *hdrp;
- volatile cdkmem_t *memp;
- volatile cdkasy_t *ap;
- unsigned long flags;
- stliport_t *portp;
- int portnr, nrdevs, i, rc;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_startbrd(brdp=%x)\n", (int) brdp);
-#endif
-
- rc = 0;
-
- save_flags(flags);
- cli();
+ cdkhdr_t __iomem *hdrp;
+ cdkmem_t __iomem *memp;
+ cdkasy_t __iomem *ap;
+ unsigned long flags;
+ stliport_t *portp;
+ int portnr, nrdevs, i, rc = 0;
+ u32 memoff;
+
+ spin_lock_irqsave(&brd_lock, flags);
EBRDENABLE(brdp);
- hdrp = (volatile cdkhdr_t *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
+ hdrp = (cdkhdr_t __iomem *) EBRDGETMEMPTR(brdp, CDK_CDKADDR);
nrdevs = hdrp->nrdevs;
#if 0
printk("%s(%d): CDK version %d.%d.%d --> "
"nrdevs=%d memp=%x hostp=%x slavep=%x\n",
- __FILE__, __LINE__, hdrp->ver_release, hdrp->ver_modification,
- hdrp->ver_fix, nrdevs, (int) hdrp->memp, (int) hdrp->hostp,
- (int) hdrp->slavep);
+ __FILE__, __LINE__, readb(&hdrp->ver_release), readb(&hdrp->ver_modification),
+ readb(&hdrp->ver_fix), nrdevs, (int) readl(&hdrp->memp), readl(&hdrp->hostp),
+ readl(&hdrp->slavep));
#endif
if (nrdevs < (brdp->nrports + 1)) {
@@ -4181,14 +3732,14 @@ static int stli_startbrd(stlibrd_t *brdp)
brdp->hostoffset = hdrp->hostp - CDK_CDKADDR;
brdp->slaveoffset = hdrp->slavep - CDK_CDKADDR;
brdp->bitsize = (nrdevs + 7) / 8;
- memp = (volatile cdkmem_t *) hdrp->memp;
- if (((unsigned long) memp) > brdp->memsize) {
+ memoff = readl(&hdrp->memp);
+ if (memoff > brdp->memsize) {
printk(KERN_ERR "STALLION: corrupted shared memory region?\n");
rc = -EIO;
goto stli_donestartup;
}
- memp = (volatile cdkmem_t *) EBRDGETMEMPTR(brdp, (unsigned long) memp);
- if (memp->dtype != TYP_ASYNCTRL) {
+ memp = (cdkmem_t __iomem *) EBRDGETMEMPTR(brdp, memoff);
+ if (readw(&memp->dtype) != TYP_ASYNCTRL) {
printk(KERN_ERR "STALLION: no slave control device found\n");
goto stli_donestartup;
}
@@ -4200,19 +3751,19 @@ static int stli_startbrd(stlibrd_t *brdp)
* change pages while reading memory map.
*/
for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++, memp++) {
- if (memp->dtype != TYP_ASYNC)
+ if (readw(&memp->dtype) != TYP_ASYNC)
break;
portp = brdp->ports[portnr];
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
break;
portp->devnr = i;
- portp->addr = memp->offset;
+ portp->addr = readl(&memp->offset);
portp->reqbit = (unsigned char) (0x1 << (i * 8 / nrdevs));
portp->portidx = (unsigned char) (i / 8);
portp->portbit = (unsigned char) (0x1 << (i % 8));
}
- hdrp->slavereq = 0xff;
+ writeb(0xff, &hdrp->slavereq);
/*
* For each port setup a local copy of the RX and TX buffer offsets
@@ -4221,22 +3772,22 @@ static int stli_startbrd(stlibrd_t *brdp)
*/
for (i = 1, portnr = 0; (i < nrdevs); i++, portnr++) {
portp = brdp->ports[portnr];
- if (portp == (stliport_t *) NULL)
+ if (portp == NULL)
break;
if (portp->addr == 0)
break;
- ap = (volatile cdkasy_t *) EBRDGETMEMPTR(brdp, portp->addr);
- if (ap != (volatile cdkasy_t *) NULL) {
- portp->rxsize = ap->rxq.size;
- portp->txsize = ap->txq.size;
- portp->rxoffset = ap->rxq.offset;
- portp->txoffset = ap->txq.offset;
+ ap = (cdkasy_t __iomem *) EBRDGETMEMPTR(brdp, portp->addr);
+ if (ap != NULL) {
+ portp->rxsize = readw(&ap->rxq.size);
+ portp->txsize = readw(&ap->txq.size);
+ portp->rxoffset = readl(&ap->rxq.offset);
+ portp->txoffset = readl(&ap->txq.offset);
}
}
stli_donestartup:
EBRDDISABLE(brdp);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
if (rc == 0)
brdp->state |= BST_STARTED;
@@ -4247,7 +3798,7 @@ stli_donestartup:
add_timer(&stli_timerlist);
}
- return(rc);
+ return rc;
}
/*****************************************************************************/
@@ -4258,10 +3809,6 @@ stli_donestartup:
static int __init stli_brdinit(stlibrd_t *brdp)
{
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_brdinit(brdp=%x)\n", (int) brdp);
-#endif
-
stli_brds[brdp->brdnr] = brdp;
switch (brdp->brdtype) {
@@ -4289,11 +3836,11 @@ static int __init stli_brdinit(stlibrd_t *brdp)
case BRD_ECHPCI:
printk(KERN_ERR "STALLION: %s board type not supported in "
"this driver\n", stli_brdnames[brdp->brdtype]);
- return(ENODEV);
+ return -ENODEV;
default:
printk(KERN_ERR "STALLION: board=%d is unknown board "
"type=%d\n", brdp->brdnr, brdp->brdtype);
- return(ENODEV);
+ return -ENODEV;
}
if ((brdp->state & BST_FOUND) == 0) {
@@ -4301,7 +3848,7 @@ static int __init stli_brdinit(stlibrd_t *brdp)
"io=%x mem=%x\n",
stli_brdnames[brdp->brdtype], brdp->brdnr,
brdp->iobase, (int) brdp->memaddr);
- return(ENODEV);
+ return -ENODEV;
}
stli_initports(brdp);
@@ -4309,7 +3856,7 @@ static int __init stli_brdinit(stlibrd_t *brdp)
"nrpanels=%d nrports=%d\n", stli_brdnames[brdp->brdtype],
brdp->brdnr, brdp->iobase, (int) brdp->memaddr,
brdp->nrpanels, brdp->nrports);
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4321,14 +3868,10 @@ static int __init stli_brdinit(stlibrd_t *brdp)
static int stli_eisamemprobe(stlibrd_t *brdp)
{
- cdkecpsig_t ecpsig, *ecpsigp;
- cdkonbsig_t onbsig, *onbsigp;
+ cdkecpsig_t ecpsig, __iomem *ecpsigp;
+ cdkonbsig_t onbsig, __iomem *onbsigp;
int i, foundit;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_eisamemprobe(brdp=%x)\n", (int) brdp);
-#endif
-
/*
* First up we reset the board, to get it into a known state. There
* is only 2 board types here we need to worry about. Don;t use the
@@ -4352,7 +3895,7 @@ static int stli_eisamemprobe(stlibrd_t *brdp)
mdelay(1);
stli_onbeenable(brdp);
} else {
- return(-ENODEV);
+ return -ENODEV;
}
foundit = 0;
@@ -4364,25 +3907,24 @@ static int stli_eisamemprobe(stlibrd_t *brdp)
*/
for (i = 0; (i < stli_eisamempsize); i++) {
brdp->memaddr = stli_eisamemprobeaddrs[i];
- brdp->membase = (void *) brdp->memaddr;
brdp->membase = ioremap(brdp->memaddr, brdp->memsize);
- if (brdp->membase == (void *) NULL)
+ if (brdp->membase == NULL)
continue;
if (brdp->brdtype == BRD_ECPE) {
- ecpsigp = (cdkecpsig_t *) stli_ecpeigetmemptr(brdp,
+ ecpsigp = (cdkecpsig_t __iomem *) stli_ecpeigetmemptr(brdp,
CDK_SIGADDR, __LINE__);
- memcpy(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
- if (ecpsig.magic == ECP_MAGIC)
+ memcpy_fromio(&ecpsig, ecpsigp, sizeof(cdkecpsig_t));
+ if (ecpsig.magic == cpu_to_le32(ECP_MAGIC))
foundit = 1;
} else {
- onbsigp = (cdkonbsig_t *) stli_onbegetmemptr(brdp,
+ onbsigp = (cdkonbsig_t __iomem *) stli_onbegetmemptr(brdp,
CDK_SIGADDR, __LINE__);
- memcpy(&onbsig, onbsigp, sizeof(cdkonbsig_t));
- if ((onbsig.magic0 == ONB_MAGIC0) &&
- (onbsig.magic1 == ONB_MAGIC1) &&
- (onbsig.magic2 == ONB_MAGIC2) &&
- (onbsig.magic3 == ONB_MAGIC3))
+ memcpy_fromio(&onbsig, onbsigp, sizeof(cdkonbsig_t));
+ if ((onbsig.magic0 == cpu_to_le16(ONB_MAGIC0)) &&
+ (onbsig.magic1 == cpu_to_le16(ONB_MAGIC1)) &&
+ (onbsig.magic2 == cpu_to_le16(ONB_MAGIC2)) &&
+ (onbsig.magic3 == cpu_to_le16(ONB_MAGIC3)))
foundit = 1;
}
@@ -4406,9 +3948,9 @@ static int stli_eisamemprobe(stlibrd_t *brdp)
printk(KERN_ERR "STALLION: failed to probe shared memory "
"region for %s in EISA slot=%d\n",
stli_brdnames[brdp->brdtype], (brdp->iobase >> 12));
- return(-ENODEV);
+ return -ENODEV;
}
- return(0);
+ return 0;
}
static int stli_getbrdnr(void)
@@ -4439,22 +3981,16 @@ static int stli_getbrdnr(void)
static int stli_findeisabrds(void)
{
- stlibrd_t *brdp;
- unsigned int iobase, eid;
- int i;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_findeisabrds()\n");
-#endif
+ stlibrd_t *brdp;
+ unsigned int iobase, eid;
+ int i;
/*
- * Firstly check if this is an EISA system. Do this by probing for
- * the system board EISA ID. If this is not an EISA system then
+ * Firstly check if this is an EISA system. If this is not an EISA system then
* don't bother going any further!
*/
- outb(0xff, 0xc80);
- if (inb(0xc80) == 0xff)
- return(0);
+ if (EISA_bus)
+ return 0;
/*
* Looks like an EISA system, so go searching for EISA boards.
@@ -4472,7 +4008,7 @@ static int stli_findeisabrds(void)
*/
for (i = 0; (i < STL_MAXBRDS); i++) {
brdp = stli_brds[i];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
continue;
if (brdp->iobase == iobase)
break;
@@ -4484,10 +4020,10 @@ static int stli_findeisabrds(void)
* We have found a Stallion board and it is not configured already.
* Allocate a board structure and initialize it.
*/
- if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
- return(-ENOMEM);
+ if ((brdp = stli_allocbrd()) == NULL)
+ return -ENOMEM;
if ((brdp->brdnr = stli_getbrdnr()) < 0)
- return(-ENOMEM);
+ return -ENOMEM;
eid = inb(iobase + 0xc82);
if (eid == ECP_EISAID)
brdp->brdtype = BRD_ECPE;
@@ -4502,7 +4038,7 @@ static int stli_findeisabrds(void)
stli_brdinit(brdp);
}
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4523,32 +4059,18 @@ static int stli_findeisabrds(void)
static int stli_initpcibrd(int brdtype, struct pci_dev *devp)
{
- stlibrd_t *brdp;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_initpcibrd(brdtype=%d,busnr=%x,devnr=%x)\n",
- brdtype, dev->bus->number, dev->devfn);
-#endif
+ stlibrd_t *brdp;
if (pci_enable_device(devp))
- return(-EIO);
- if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
- return(-ENOMEM);
+ return -EIO;
+ if ((brdp = stli_allocbrd()) == NULL)
+ return -ENOMEM;
if ((brdp->brdnr = stli_getbrdnr()) < 0) {
printk(KERN_INFO "STALLION: too many boards found, "
"maximum supported %d\n", STL_MAXBRDS);
- return(0);
+ return 0;
}
brdp->brdtype = brdtype;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "%s(%d): BAR[]=%lx,%lx,%lx,%lx\n", __FILE__, __LINE__,
- pci_resource_start(devp, 0),
- pci_resource_start(devp, 1),
- pci_resource_start(devp, 2),
- pci_resource_start(devp, 3));
-#endif
-
/*
* We have all resources from the board, so lets setup the actual
* board structure now.
@@ -4557,7 +4079,7 @@ static int stli_initpcibrd(int brdtype, struct pci_dev *devp)
brdp->memaddr = pci_resource_start(devp, 2);
stli_brdinit(brdp);
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4569,20 +4091,12 @@ static int stli_initpcibrd(int brdtype, struct pci_dev *devp)
static int stli_findpcibrds(void)
{
- struct pci_dev *dev = NULL;
- int rc;
-
-#ifdef DEBUG
- printk("stli_findpcibrds()\n");
-#endif
+ struct pci_dev *dev = NULL;
- while ((dev = pci_find_device(PCI_VENDOR_ID_STALLION,
- PCI_DEVICE_ID_ECRA, dev))) {
- if ((rc = stli_initpcibrd(BRD_ECPPCI, dev)))
- return(rc);
+ while ((dev = pci_get_device(PCI_VENDOR_ID_STALLION, PCI_DEVICE_ID_ECRA, dev))) {
+ stli_initpcibrd(BRD_ECPPCI, dev);
}
-
- return(0);
+ return 0;
}
#endif
@@ -4595,17 +4109,16 @@ static int stli_findpcibrds(void)
static stlibrd_t *stli_allocbrd(void)
{
- stlibrd_t *brdp;
+ stlibrd_t *brdp;
brdp = kzalloc(sizeof(stlibrd_t), GFP_KERNEL);
if (!brdp) {
printk(KERN_ERR "STALLION: failed to allocate memory "
- "(size=%d)\n", sizeof(stlibrd_t));
+ "(size=%Zd)\n", sizeof(stlibrd_t));
return NULL;
}
-
brdp->magic = STLI_BOARDMAGIC;
- return(brdp);
+ return brdp;
}
/*****************************************************************************/
@@ -4617,13 +4130,9 @@ static stlibrd_t *stli_allocbrd(void)
static int stli_initbrds(void)
{
- stlibrd_t *brdp, *nxtbrdp;
- stlconf_t *confp;
- int i, j;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_initbrds()\n");
-#endif
+ stlibrd_t *brdp, *nxtbrdp;
+ stlconf_t *confp;
+ int i, j;
if (stli_nrbrds > STL_MAXBRDS) {
printk(KERN_INFO "STALLION: too many boards in configuration "
@@ -4638,11 +4147,9 @@ static int stli_initbrds(void)
*/
for (i = 0; (i < stli_nrbrds); i++) {
confp = &stli_brdconf[i];
-#ifdef MODULE
stli_parsebrd(confp, stli_brdsp[i]);
-#endif
- if ((brdp = stli_allocbrd()) == (stlibrd_t *) NULL)
- return(-ENOMEM);
+ if ((brdp = stli_allocbrd()) == NULL)
+ return -ENOMEM;
brdp->brdnr = i;
brdp->brdtype = confp->brdtype;
brdp->iobase = confp->ioaddr1;
@@ -4654,9 +4161,7 @@ static int stli_initbrds(void)
* Static configuration table done, so now use dynamic methods to
* see if any more boards should be configured.
*/
-#ifdef MODULE
stli_argbrds();
-#endif
if (STLI_EISAPROBE)
stli_findeisabrds();
#ifdef CONFIG_PCI
@@ -4672,11 +4177,11 @@ static int stli_initbrds(void)
if (stli_nrbrds > 1) {
for (i = 0; (i < stli_nrbrds); i++) {
brdp = stli_brds[i];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
continue;
for (j = i + 1; (j < stli_nrbrds); j++) {
nxtbrdp = stli_brds[j];
- if (nxtbrdp == (stlibrd_t *) NULL)
+ if (nxtbrdp == NULL)
continue;
if ((brdp->membase >= nxtbrdp->membase) &&
(brdp->membase <= (nxtbrdp->membase +
@@ -4691,7 +4196,7 @@ static int stli_initbrds(void)
if (stli_shared == 0) {
for (i = 0; (i < stli_nrbrds); i++) {
brdp = stli_brds[i];
- if (brdp == (stlibrd_t *) NULL)
+ if (brdp == NULL)
continue;
if (brdp->state & BST_FOUND) {
EBRDENABLE(brdp);
@@ -4701,7 +4206,7 @@ static int stli_initbrds(void)
}
}
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4714,48 +4219,55 @@ static int stli_initbrds(void)
static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp)
{
- unsigned long flags;
- void *memptr;
- stlibrd_t *brdp;
- int brdnr, size, n;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_memread(fp=%x,buf=%x,count=%x,offp=%x)\n",
- (int) fp, (int) buf, count, (int) offp);
-#endif
+ unsigned long flags;
+ void *memptr;
+ stlibrd_t *brdp;
+ int brdnr, size, n;
+ void *p;
+ loff_t off = *offp;
brdnr = iminor(fp->f_dentry->d_inode);
if (brdnr >= stli_nrbrds)
- return(-ENODEV);
+ return -ENODEV;
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
if (brdp->state == 0)
- return(-ENODEV);
- if (fp->f_pos >= brdp->memsize)
- return(0);
+ return -ENODEV;
+ if (off >= brdp->memsize || off + count < off)
+ return 0;
- size = MIN(count, (brdp->memsize - fp->f_pos));
+ size = MIN(count, (brdp->memsize - off));
+
+ /*
+ * Copy the data a page at a time
+ */
+
+ p = (void *)__get_free_page(GFP_KERNEL);
+ if(p == NULL)
+ return -ENOMEM;
- save_flags(flags);
- cli();
- EBRDENABLE(brdp);
while (size > 0) {
- memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
- n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
- if (copy_to_user(buf, memptr, n)) {
+ spin_lock_irqsave(&brd_lock, flags);
+ EBRDENABLE(brdp);
+ memptr = (void *) EBRDGETMEMPTR(brdp, off);
+ n = MIN(size, (brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
+ n = MIN(n, PAGE_SIZE);
+ memcpy_fromio(p, memptr, n);
+ EBRDDISABLE(brdp);
+ spin_unlock_irqrestore(&brd_lock, flags);
+ if (copy_to_user(buf, p, n)) {
count = -EFAULT;
goto out;
}
- fp->f_pos += n;
+ off += n;
buf += n;
size -= n;
}
out:
- EBRDDISABLE(brdp);
- restore_flags(flags);
-
- return(count);
+ *offp = off;
+ free_page((unsigned long)p);
+ return count;
}
/*****************************************************************************/
@@ -4764,54 +4276,65 @@ out:
* Code to handle an "staliomem" write operation. This device is the
* contents of the board shared memory. It is used for down loading
* the slave image (and debugging :-)
+ *
+ * FIXME: copy under lock
*/
static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp)
{
- unsigned long flags;
- void *memptr;
- stlibrd_t *brdp;
- char __user *chbuf;
- int brdnr, size, n;
-
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_memwrite(fp=%x,buf=%x,count=%x,offp=%x)\n",
- (int) fp, (int) buf, count, (int) offp);
-#endif
+ unsigned long flags;
+ void *memptr;
+ stlibrd_t *brdp;
+ char __user *chbuf;
+ int brdnr, size, n;
+ void *p;
+ loff_t off = *offp;
brdnr = iminor(fp->f_dentry->d_inode);
+
if (brdnr >= stli_nrbrds)
- return(-ENODEV);
+ return -ENODEV;
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
if (brdp->state == 0)
- return(-ENODEV);
- if (fp->f_pos >= brdp->memsize)
- return(0);
+ return -ENODEV;
+ if (off >= brdp->memsize || off + count < off)
+ return 0;
chbuf = (char __user *) buf;
- size = MIN(count, (brdp->memsize - fp->f_pos));
+ size = MIN(count, (brdp->memsize - off));
+
+ /*
+ * Copy the data a page at a time
+ */
+
+ p = (void *)__get_free_page(GFP_KERNEL);
+ if(p == NULL)
+ return -ENOMEM;
- save_flags(flags);
- cli();
- EBRDENABLE(brdp);
while (size > 0) {
- memptr = (void *) EBRDGETMEMPTR(brdp, fp->f_pos);
- n = MIN(size, (brdp->pagesize - (((unsigned long) fp->f_pos) % brdp->pagesize)));
- if (copy_from_user(memptr, chbuf, n)) {
- count = -EFAULT;
+ n = MIN(size, (brdp->pagesize - (((unsigned long) off) % brdp->pagesize)));
+ n = MIN(n, PAGE_SIZE);
+ if (copy_from_user(p, chbuf, n)) {
+ if (count == 0)
+ count = -EFAULT;
goto out;
}
- fp->f_pos += n;
+ spin_lock_irqsave(&brd_lock, flags);
+ EBRDENABLE(brdp);
+ memptr = (void *) EBRDGETMEMPTR(brdp, off);
+ memcpy_toio(memptr, p, n);
+ EBRDDISABLE(brdp);
+ spin_unlock_irqrestore(&brd_lock, flags);
+ off += n;
chbuf += n;
size -= n;
}
out:
- EBRDDISABLE(brdp);
- restore_flags(flags);
-
- return(count);
+ free_page((unsigned long) p);
+ *offp = off;
+ return count;
}
/*****************************************************************************/
@@ -4822,16 +4345,16 @@ out:
static int stli_getbrdstats(combrd_t __user *bp)
{
- stlibrd_t *brdp;
- int i;
+ stlibrd_t *brdp;
+ int i;
if (copy_from_user(&stli_brdstats, bp, sizeof(combrd_t)))
return -EFAULT;
if (stli_brdstats.brd >= STL_MAXBRDS)
- return(-ENODEV);
+ return -ENODEV;
brdp = stli_brds[stli_brdstats.brd];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
memset(&stli_brdstats, 0, sizeof(combrd_t));
stli_brdstats.brd = brdp->brdnr;
@@ -4850,7 +4373,7 @@ static int stli_getbrdstats(combrd_t __user *bp)
if (copy_to_user(bp, &stli_brdstats, sizeof(combrd_t)))
return -EFAULT;
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4861,19 +4384,19 @@ static int stli_getbrdstats(combrd_t __user *bp)
static stliport_t *stli_getport(int brdnr, int panelnr, int portnr)
{
- stlibrd_t *brdp;
- int i;
+ stlibrd_t *brdp;
+ int i;
- if ((brdnr < 0) || (brdnr >= STL_MAXBRDS))
- return((stliport_t *) NULL);
+ if (brdnr < 0 || brdnr >= STL_MAXBRDS)
+ return NULL;
brdp = stli_brds[brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return((stliport_t *) NULL);
+ if (brdp == NULL)
+ return NULL;
for (i = 0; (i < panelnr); i++)
portnr += brdp->panels[i];
if ((portnr < 0) || (portnr >= brdp->nrports))
- return((stliport_t *) NULL);
- return(brdp->ports[portnr]);
+ return NULL;
+ return brdp->ports[portnr];
}
/*****************************************************************************/
@@ -4892,16 +4415,16 @@ static int stli_portcmdstats(stliport_t *portp)
memset(&stli_comstats, 0, sizeof(comstats_t));
- if (portp == (stliport_t *) NULL)
- return(-ENODEV);
+ if (portp == NULL)
+ return -ENODEV;
brdp = stli_brds[portp->brdnr];
- if (brdp == (stlibrd_t *) NULL)
- return(-ENODEV);
+ if (brdp == NULL)
+ return -ENODEV;
if (brdp->state & BST_STARTED) {
if ((rc = stli_cmdwait(brdp, portp, A_GETSTATS,
&stli_cdkstats, sizeof(asystats_t), 1)) < 0)
- return(rc);
+ return rc;
} else {
memset(&stli_cdkstats, 0, sizeof(asystats_t));
}
@@ -4912,13 +4435,12 @@ static int stli_portcmdstats(stliport_t *portp)
stli_comstats.state = portp->state;
stli_comstats.flags = portp->flags;
- save_flags(flags);
- cli();
- if (portp->tty != (struct tty_struct *) NULL) {
+ spin_lock_irqsave(&brd_lock, flags);
+ if (portp->tty != NULL) {
if (portp->tty->driver_data == portp) {
stli_comstats.ttystate = portp->tty->flags;
- stli_comstats.rxbuffered = -1 /*portp->tty->flip.count*/;
- if (portp->tty->termios != (struct termios *) NULL) {
+ stli_comstats.rxbuffered = -1;
+ if (portp->tty->termios != NULL) {
stli_comstats.cflags = portp->tty->termios->c_cflag;
stli_comstats.iflags = portp->tty->termios->c_iflag;
stli_comstats.oflags = portp->tty->termios->c_oflag;
@@ -4926,7 +4448,7 @@ static int stli_portcmdstats(stliport_t *portp)
}
}
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
stli_comstats.txtotal = stli_cdkstats.txchars;
stli_comstats.rxtotal = stli_cdkstats.rxchars + stli_cdkstats.ringover;
@@ -4948,7 +4470,7 @@ static int stli_portcmdstats(stliport_t *portp)
stli_comstats.hwid = stli_cdkstats.hwid;
stli_comstats.signals = stli_mktiocm(stli_cdkstats.signals);
- return(0);
+ return 0;
}
/*****************************************************************************/
@@ -4961,8 +4483,8 @@ static int stli_portcmdstats(stliport_t *portp)
static int stli_getportstats(stliport_t *portp, comstats_t __user *cp)
{
- stlibrd_t *brdp;
- int rc;
+ stlibrd_t *brdp;
+ int rc;
if (!portp) {
if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
@@ -4992,8 +4514,8 @@ static int stli_getportstats(stliport_t *portp, comstats_t __user *cp)
static int stli_clrportstats(stliport_t *portp, comstats_t __user *cp)
{
- stlibrd_t *brdp;
- int rc;
+ stlibrd_t *brdp;
+ int rc;
if (!portp) {
if (copy_from_user(&stli_comstats, cp, sizeof(comstats_t)))
@@ -5031,7 +4553,7 @@ static int stli_clrportstats(stliport_t *portp, comstats_t __user *cp)
static int stli_getportstruct(stliport_t __user *arg)
{
- stliport_t *portp;
+ stliport_t *portp;
if (copy_from_user(&stli_dummyport, arg, sizeof(stliport_t)))
return -EFAULT;
@@ -5052,7 +4574,7 @@ static int stli_getportstruct(stliport_t __user *arg)
static int stli_getbrdstruct(stlibrd_t __user *arg)
{
- stlibrd_t *brdp;
+ stlibrd_t *brdp;
if (copy_from_user(&stli_dummybrd, arg, sizeof(stlibrd_t)))
return -EFAULT;
@@ -5076,15 +4598,10 @@ static int stli_getbrdstruct(stlibrd_t __user *arg)
static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
{
- stlibrd_t *brdp;
- int brdnr, rc, done;
+ stlibrd_t *brdp;
+ int brdnr, rc, done;
void __user *argp = (void __user *)arg;
-#ifdef DEBUG
- printk(KERN_DEBUG "stli_memioctl(ip=%x,fp=%x,cmd=%x,arg=%x)\n",
- (int) ip, (int) fp, cmd, (int) arg);
-#endif
-
/*
* First up handle the board independent ioctls.
*/
@@ -5115,7 +4632,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
}
if (done)
- return(rc);
+ return rc;
/*
* Now handle the board specific ioctls. These all depend on the
@@ -5123,12 +4640,12 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
*/
brdnr = iminor(ip);
if (brdnr >= STL_MAXBRDS)
- return(-ENODEV);
+ return -ENODEV;
brdp = stli_brds[brdnr];
if (!brdp)
- return(-ENODEV);
+ return -ENODEV;
if (brdp->state == 0)
- return(-ENODEV);
+ return -ENODEV;
switch (cmd) {
case STL_BINTR:
@@ -5152,8 +4669,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
rc = -ENOIOCTLCMD;
break;
}
-
- return(rc);
+ return rc;
}
static struct tty_operations stli_ops = {
@@ -5187,6 +4703,9 @@ int __init stli_init(void)
int i;
printk(KERN_INFO "%s: version %s\n", stli_drvtitle, stli_drvversion);
+ spin_lock_init(&stli_lock);
+ spin_lock_init(&brd_lock);
+
stli_initbrds();
stli_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
@@ -5196,10 +4715,6 @@ int __init stli_init(void)
/*
* Allocate a temporary write buffer.
*/
- stli_tmpwritebuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
- if (!stli_tmpwritebuf)
- printk(KERN_ERR "STALLION: failed to allocate memory "
- "(size=%d)\n", STLI_TXBUFSIZE);
stli_txcookbuf = kmalloc(STLI_TXBUFSIZE, GFP_KERNEL);
if (!stli_txcookbuf)
printk(KERN_ERR "STALLION: failed to allocate memory "
@@ -5243,7 +4758,7 @@ int __init stli_init(void)
printk(KERN_ERR "STALLION: failed to register serial driver\n");
return -EBUSY;
}
- return(0);
+ return 0;
}
/*****************************************************************************/
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index edd996f6fb87..4bb3d2272604 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -151,6 +151,7 @@ unsigned char kbd_sysrq_xlate[KEY_MAX + 1] =
"230\177\000\000\213\214\000\000\000\000\000\000\000\000\000\000" /* 0x50 - 0x5f */
"\r\000/"; /* 0x60 - 0x6f */
static int sysrq_down;
+static int sysrq_alt_use;
#endif
static int sysrq_alt;
@@ -673,7 +674,7 @@ static void k_dead2(struct vc_data *vc, unsigned char value, char up_flag, struc
*/
static void k_dead(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
{
- static unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
+ static const unsigned char ret_diacr[NR_DEAD] = {'`', '\'', '^', '~', '"', ',' };
value = ret_diacr[value];
k_deadunicode(vc, value, up_flag, regs);
}
@@ -710,8 +711,8 @@ static void k_cur(struct vc_data *vc, unsigned char value, char up_flag, struct
static void k_pad(struct vc_data *vc, unsigned char value, char up_flag, struct pt_regs *regs)
{
- static const char *pad_chars = "0123456789+-*/\015,.?()#";
- static const char *app_map = "pqrstuvwxylSRQMnnmPQS";
+ static const char pad_chars[] = "0123456789+-*/\015,.?()#";
+ static const char app_map[] = "pqrstuvwxylSRQMnnmPQS";
if (up_flag)
return; /* no action, if this is a key release */
@@ -1036,7 +1037,7 @@ static void kbd_refresh_leds(struct input_handle *handle)
#define HW_RAW(dev) (test_bit(EV_MSC, dev->evbit) && test_bit(MSC_RAW, dev->mscbit) &&\
((dev)->id.bustype == BUS_I8042) && ((dev)->id.vendor == 0x0001) && ((dev)->id.product == 0x0001))
-static unsigned short x86_keycodes[256] =
+static const 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,
@@ -1074,11 +1075,13 @@ static int emulate_raw(struct vc_data *vc, unsigned int keycode,
put_queue(vc, 0x1d | up_flag);
put_queue(vc, 0x45 | up_flag);
return 0;
- case KEY_HANGUEL:
- if (!up_flag) put_queue(vc, 0xf1);
+ case KEY_HANGEUL:
+ if (!up_flag)
+ put_queue(vc, 0xf2);
return 0;
case KEY_HANJA:
- if (!up_flag) put_queue(vc, 0xf2);
+ if (!up_flag)
+ put_queue(vc, 0xf1);
return 0;
}
@@ -1143,7 +1146,7 @@ static void kbd_keycode(unsigned int keycode, int down,
kbd = kbd_table + fg_console;
if (keycode == KEY_LEFTALT || keycode == KEY_RIGHTALT)
- sysrq_alt = down;
+ sysrq_alt = down ? keycode : 0;
#ifdef CONFIG_SPARC
if (keycode == KEY_STOP)
sparc_l1_a_state = down;
@@ -1163,9 +1166,14 @@ static void kbd_keycode(unsigned int keycode, int down,
#ifdef CONFIG_MAGIC_SYSRQ /* Handle the SysRq Hack */
if (keycode == KEY_SYSRQ && (sysrq_down || (down == 1 && sysrq_alt))) {
- sysrq_down = down;
+ if (!sysrq_down) {
+ sysrq_down = down;
+ sysrq_alt_use = sysrq_alt;
+ }
return;
}
+ if (sysrq_down && !down && keycode == sysrq_alt_use)
+ sysrq_down = 0;
if (sysrq_down && down && !rep) {
handle_sysrq(kbd_sysrq_xlate[keycode], regs, tty);
return;
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index f43c2e04eadd..01247cccb89f 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -301,7 +301,7 @@ static struct tty_operations moxa_ops = {
.tiocmset = moxa_tiocmset,
};
-static spinlock_t moxa_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(moxa_lock);
#ifdef CONFIG_PCI
static int moxa_get_PCI_conf(struct pci_dev *p, int board_type, moxa_board_conf * board)
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 645d9d713aec..72cfd09091e0 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -996,7 +996,6 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
info->session = current->signal->session;
info->pgrp = process_group(current);
- clear_bit(TTY_DONT_FLIP, &tty->flags);
/*
status = mxser_get_msr(info->base, 0, info->port);
diff --git a/drivers/char/n_tty.c b/drivers/char/n_tty.c
index b9371d5bf790..603b9ade5eb0 100644
--- a/drivers/char/n_tty.c
+++ b/drivers/char/n_tty.c
@@ -1132,7 +1132,7 @@ static inline int input_available_p(struct tty_struct *tty, int amt)
* buffer, and once to drain the space from the (physical) beginning of
* the buffer to head pointer.
*
- * Called under the tty->atomic_read_lock sem and with TTY_DONT_FLIP set
+ * Called under the tty->atomic_read_lock sem
*
*/
@@ -1271,7 +1271,6 @@ do_it_again:
}
add_wait_queue(&tty->read_wait, &wait);
- set_bit(TTY_DONT_FLIP, &tty->flags);
while (nr) {
/* First test for status change. */
if (tty->packet && tty->link->ctrl_status) {
@@ -1315,9 +1314,7 @@ do_it_again:
break;
}
n_tty_set_room(tty);
- clear_bit(TTY_DONT_FLIP, &tty->flags);
timeout = schedule_timeout(timeout);
- set_bit(TTY_DONT_FLIP, &tty->flags);
continue;
}
__set_current_state(TASK_RUNNING);
@@ -1394,7 +1391,6 @@ do_it_again:
if (time)
timeout = time;
}
- clear_bit(TTY_DONT_FLIP, &tty->flags);
mutex_unlock(&tty->atomic_read_lock);
remove_wait_queue(&tty->read_wait, &wait);
diff --git a/drivers/char/nsc_gpio.c b/drivers/char/nsc_gpio.c
new file mode 100644
index 000000000000..5b91e4e25641
--- /dev/null
+++ b/drivers/char/nsc_gpio.c
@@ -0,0 +1,142 @@
+/* linux/drivers/char/nsc_gpio.c
+
+ National Semiconductor common GPIO device-file/VFS methods.
+ Allows a user space process to control the GPIO pins.
+
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
+ Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
+*/
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/nsc_gpio.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define NAME "nsc_gpio"
+
+void nsc_gpio_dump(struct nsc_gpio_ops *amp, unsigned index)
+{
+ /* retrieve current config w/o changing it */
+ u32 config = amp->gpio_config(index, ~0, 0);
+
+ /* user requested via 'v' command, so its INFO */
+ dev_info(amp->dev, "io%02u: 0x%04x %s %s %s %s %s %s %s\tio:%d/%d\n",
+ index, config,
+ (config & 1) ? "OE" : "TS", /* output-enabled/tristate */
+ (config & 2) ? "PP" : "OD", /* push pull / open drain */
+ (config & 4) ? "PUE" : "PUD", /* pull up enabled/disabled */
+ (config & 8) ? "LOCKED" : "", /* locked / unlocked */
+ (config & 16) ? "LEVEL" : "EDGE",/* level/edge input */
+ (config & 32) ? "HI" : "LO", /* trigger on rise/fall edge */
+ (config & 64) ? "DEBOUNCE" : "", /* debounce */
+
+ amp->gpio_get(index), amp->gpio_current(index));
+}
+
+ssize_t nsc_gpio_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ unsigned m = iminor(file->f_dentry->d_inode);
+ struct nsc_gpio_ops *amp = file->private_data;
+ struct device *dev = amp->dev;
+ size_t i;
+ int err = 0;
+
+ for (i = 0; i < len; ++i) {
+ char c;
+ if (get_user(c, data + i))
+ return -EFAULT;
+ switch (c) {
+ case '0':
+ amp->gpio_set(m, 0);
+ break;
+ case '1':
+ amp->gpio_set(m, 1);
+ break;
+ case 'O':
+ dev_dbg(dev, "GPIO%d output enabled\n", m);
+ amp->gpio_config(m, ~1, 1);
+ break;
+ case 'o':
+ dev_dbg(dev, "GPIO%d output disabled\n", m);
+ amp->gpio_config(m, ~1, 0);
+ break;
+ case 'T':
+ dev_dbg(dev, "GPIO%d output is push pull\n",
+ m);
+ amp->gpio_config(m, ~2, 2);
+ break;
+ case 't':
+ dev_dbg(dev, "GPIO%d output is open drain\n",
+ m);
+ amp->gpio_config(m, ~2, 0);
+ break;
+ case 'P':
+ dev_dbg(dev, "GPIO%d pull up enabled\n", m);
+ amp->gpio_config(m, ~4, 4);
+ break;
+ case 'p':
+ dev_dbg(dev, "GPIO%d pull up disabled\n", m);
+ amp->gpio_config(m, ~4, 0);
+ break;
+ case 'v':
+ /* View Current pin settings */
+ amp->gpio_dump(amp, m);
+ break;
+ case '\n':
+ /* end of settings string, do nothing */
+ break;
+ default:
+ dev_err(dev, "io%2d bad setting: chr<0x%2x>\n",
+ m, (int)c);
+ err++;
+ }
+ }
+ if (err)
+ return -EINVAL; /* full string handled, report error */
+
+ return len;
+}
+
+ssize_t nsc_gpio_read(struct file *file, char __user * buf,
+ size_t len, loff_t * ppos)
+{
+ unsigned m = iminor(file->f_dentry->d_inode);
+ int value;
+ struct nsc_gpio_ops *amp = file->private_data;
+
+ value = amp->gpio_get(m);
+ if (put_user(value ? '1' : '0', buf))
+ return -EFAULT;
+
+ return 1;
+}
+
+/* common file-ops routines for both scx200_gpio and pc87360_gpio */
+EXPORT_SYMBOL(nsc_gpio_write);
+EXPORT_SYMBOL(nsc_gpio_read);
+EXPORT_SYMBOL(nsc_gpio_dump);
+
+static int __init nsc_gpio_init(void)
+{
+ printk(KERN_DEBUG NAME " initializing\n");
+ return 0;
+}
+
+static void __exit nsc_gpio_cleanup(void)
+{
+ printk(KERN_DEBUG NAME " cleanup\n");
+}
+
+module_init(nsc_gpio_init);
+module_exit(nsc_gpio_cleanup);
+
+MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("NatSemi GPIO Common Methods");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c
new file mode 100644
index 000000000000..1c706ccfdbb3
--- /dev/null
+++ b/drivers/char/pc8736x_gpio.c
@@ -0,0 +1,340 @@
+/* linux/drivers/char/pc8736x_gpio.c
+
+ National Semiconductor PC8736x GPIO driver. Allows a user space
+ process to play with the GPIO pins.
+
+ Copyright (c) 2005 Jim Cromie <jim.cromie@gmail.com>
+
+ adapted from linux/drivers/char/scx200_gpio.c
+ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>,
+*/
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/mutex.h>
+#include <linux/nsc_gpio.h>
+#include <linux/platform_device.h>
+#include <asm/uaccess.h>
+
+#define DEVNAME "pc8736x_gpio"
+
+MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("NatSemi PC-8736x GPIO Pin Driver");
+MODULE_LICENSE("GPL");
+
+static int major; /* default to dynamic major */
+module_param(major, int, 0);
+MODULE_PARM_DESC(major, "Major device number");
+
+static DEFINE_MUTEX(pc8736x_gpio_config_lock);
+static unsigned pc8736x_gpio_base;
+static u8 pc8736x_gpio_shadow[4];
+
+#define SIO_BASE1 0x2E /* 1st command-reg to check */
+#define SIO_BASE2 0x4E /* alt command-reg to check */
+#define SIO_BASE_OFFSET 0x20
+
+#define SIO_SID 0x20 /* SuperI/O ID Register */
+#define SIO_SID_VALUE 0xe9 /* Expected value in SuperI/O ID Register */
+
+#define SIO_CF1 0x21 /* chip config, bit0 is chip enable */
+
+#define PC8736X_GPIO_SIZE 16
+
+#define SIO_UNIT_SEL 0x7 /* unit select reg */
+#define SIO_UNIT_ACT 0x30 /* unit enable */
+#define SIO_GPIO_UNIT 0x7 /* unit number of GPIO */
+#define SIO_VLM_UNIT 0x0D
+#define SIO_TMS_UNIT 0x0E
+
+/* config-space addrs to read/write each unit's runtime addr */
+#define SIO_BASE_HADDR 0x60
+#define SIO_BASE_LADDR 0x61
+
+/* GPIO config-space pin-control addresses */
+#define SIO_GPIO_PIN_SELECT 0xF0
+#define SIO_GPIO_PIN_CONFIG 0xF1
+#define SIO_GPIO_PIN_EVENT 0xF2
+
+static unsigned char superio_cmd = 0;
+static unsigned char selected_device = 0xFF; /* bogus start val */
+
+/* GPIO port runtime access, functionality */
+static int port_offset[] = { 0, 4, 8, 10 }; /* non-uniform offsets ! */
+/* static int event_capable[] = { 1, 1, 0, 0 }; ports 2,3 are hobbled */
+
+#define PORT_OUT 0
+#define PORT_IN 1
+#define PORT_EVT_EN 2
+#define PORT_EVT_STST 3
+
+static struct platform_device *pdev; /* use in dev_*() */
+
+static inline void superio_outb(int addr, int val)
+{
+ outb_p(addr, superio_cmd);
+ outb_p(val, superio_cmd + 1);
+}
+
+static inline int superio_inb(int addr)
+{
+ outb_p(addr, superio_cmd);
+ return inb_p(superio_cmd + 1);
+}
+
+static int pc8736x_superio_present(void)
+{
+ /* try the 2 possible values, read a hardware reg to verify */
+ superio_cmd = SIO_BASE1;
+ if (superio_inb(SIO_SID) == SIO_SID_VALUE)
+ return superio_cmd;
+
+ superio_cmd = SIO_BASE2;
+ if (superio_inb(SIO_SID) == SIO_SID_VALUE)
+ return superio_cmd;
+
+ return 0;
+}
+
+static void device_select(unsigned devldn)
+{
+ superio_outb(SIO_UNIT_SEL, devldn);
+ selected_device = devldn;
+}
+
+static void select_pin(unsigned iminor)
+{
+ /* select GPIO port/pin from device minor number */
+ device_select(SIO_GPIO_UNIT);
+ superio_outb(SIO_GPIO_PIN_SELECT,
+ ((iminor << 1) & 0xF0) | (iminor & 0x7));
+}
+
+static inline u32 pc8736x_gpio_configure_fn(unsigned index, u32 mask, u32 bits,
+ u32 func_slct)
+{
+ u32 config, new_config;
+
+ mutex_lock(&pc8736x_gpio_config_lock);
+
+ device_select(SIO_GPIO_UNIT);
+ select_pin(index);
+
+ /* read current config value */
+ config = superio_inb(func_slct);
+
+ /* set new config */
+ new_config = (config & mask) | bits;
+ superio_outb(func_slct, new_config);
+
+ mutex_unlock(&pc8736x_gpio_config_lock);
+
+ return config;
+}
+
+static u32 pc8736x_gpio_configure(unsigned index, u32 mask, u32 bits)
+{
+ return pc8736x_gpio_configure_fn(index, mask, bits,
+ SIO_GPIO_PIN_CONFIG);
+}
+
+static int pc8736x_gpio_get(unsigned minor)
+{
+ int port, bit, val;
+
+ port = minor >> 3;
+ bit = minor & 7;
+ val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+ val >>= bit;
+ val &= 1;
+
+ dev_dbg(&pdev->dev, "_gpio_get(%d from %x bit %d) == val %d\n",
+ minor, pc8736x_gpio_base + port_offset[port] + PORT_IN, bit,
+ val);
+
+ return val;
+}
+
+static void pc8736x_gpio_set(unsigned minor, int val)
+{
+ int port, bit, curval;
+
+ minor &= 0x1f;
+ port = minor >> 3;
+ bit = minor & 7;
+ curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ dev_dbg(&pdev->dev, "addr:%x cur:%x bit-pos:%d cur-bit:%x + new:%d -> bit-new:%d\n",
+ pc8736x_gpio_base + port_offset[port] + PORT_OUT,
+ curval, bit, (curval & ~(1 << bit)), val, (val << bit));
+
+ val = (curval & ~(1 << bit)) | (val << bit);
+
+ dev_dbg(&pdev->dev, "gpio_set(minor:%d port:%d bit:%d)"
+ " %2x -> %2x\n", minor, port, bit, curval, val);
+
+ outb_p(val, pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+
+ curval = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_OUT);
+ val = inb_p(pc8736x_gpio_base + port_offset[port] + PORT_IN);
+
+ dev_dbg(&pdev->dev, "wrote %x, read: %x\n", curval, val);
+ pc8736x_gpio_shadow[port] = val;
+}
+
+static void pc8736x_gpio_set_high(unsigned index)
+{
+ pc8736x_gpio_set(index, 1);
+}
+
+static void pc8736x_gpio_set_low(unsigned index)
+{
+ pc8736x_gpio_set(index, 0);
+}
+
+static int pc8736x_gpio_current(unsigned minor)
+{
+ int port, bit;
+ minor &= 0x1f;
+ port = minor >> 3;
+ bit = minor & 7;
+ return ((pc8736x_gpio_shadow[port] >> bit) & 0x01);
+}
+
+static void pc8736x_gpio_change(unsigned index)
+{
+ pc8736x_gpio_set(index, !pc8736x_gpio_current(index));
+}
+
+static struct nsc_gpio_ops pc8736x_access = {
+ .owner = THIS_MODULE,
+ .gpio_config = pc8736x_gpio_configure,
+ .gpio_dump = nsc_gpio_dump,
+ .gpio_get = pc8736x_gpio_get,
+ .gpio_set = pc8736x_gpio_set,
+ .gpio_set_high = pc8736x_gpio_set_high,
+ .gpio_set_low = pc8736x_gpio_set_low,
+ .gpio_change = pc8736x_gpio_change,
+ .gpio_current = pc8736x_gpio_current
+};
+
+static int pc8736x_gpio_open(struct inode *inode, struct file *file)
+{
+ unsigned m = iminor(inode);
+ file->private_data = &pc8736x_access;
+
+ dev_dbg(&pdev->dev, "open %d\n", m);
+
+ if (m > 63)
+ return -EINVAL;
+ return nonseekable_open(inode, file);
+}
+
+static struct file_operations pc8736x_gpio_fops = {
+ .owner = THIS_MODULE,
+ .open = pc8736x_gpio_open,
+ .write = nsc_gpio_write,
+ .read = nsc_gpio_read,
+};
+
+static void __init pc8736x_init_shadow(void)
+{
+ int port;
+
+ /* read the current values driven on the GPIO signals */
+ for (port = 0; port < 4; ++port)
+ pc8736x_gpio_shadow[port]
+ = inb_p(pc8736x_gpio_base + port_offset[port]
+ + PORT_OUT);
+
+}
+
+static int __init pc8736x_gpio_init(void)
+{
+ int rc = 0;
+
+ pdev = platform_device_alloc(DEVNAME, 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ rc = platform_device_add(pdev);
+ if (rc) {
+ rc = -ENODEV;
+ goto undo_platform_dev_alloc;
+ }
+ dev_info(&pdev->dev, "NatSemi pc8736x GPIO Driver Initializing\n");
+
+ if (!pc8736x_superio_present()) {
+ rc = -ENODEV;
+ dev_err(&pdev->dev, "no device found\n");
+ goto undo_platform_dev_add;
+ }
+ pc8736x_access.dev = &pdev->dev;
+
+ /* Verify that chip and it's GPIO unit are both enabled.
+ My BIOS does this, so I take minimum action here
+ */
+ rc = superio_inb(SIO_CF1);
+ if (!(rc & 0x01)) {
+ rc = -ENODEV;
+ dev_err(&pdev->dev, "device not enabled\n");
+ goto undo_platform_dev_add;
+ }
+ device_select(SIO_GPIO_UNIT);
+ if (!superio_inb(SIO_UNIT_ACT)) {
+ rc = -ENODEV;
+ dev_err(&pdev->dev, "GPIO unit not enabled\n");
+ goto undo_platform_dev_add;
+ }
+
+ /* read the GPIO unit base addr that chip responds to */
+ pc8736x_gpio_base = (superio_inb(SIO_BASE_HADDR) << 8
+ | superio_inb(SIO_BASE_LADDR));
+
+ if (!request_region(pc8736x_gpio_base, 16, DEVNAME)) {
+ rc = -ENODEV;
+ dev_err(&pdev->dev, "GPIO ioport %x busy\n",
+ pc8736x_gpio_base);
+ goto undo_platform_dev_add;
+ }
+ dev_info(&pdev->dev, "GPIO ioport %x reserved\n", pc8736x_gpio_base);
+
+ rc = register_chrdev(major, DEVNAME, &pc8736x_gpio_fops);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "register-chrdev failed: %d\n", rc);
+ goto undo_platform_dev_add;
+ }
+ if (!major) {
+ major = rc;
+ dev_dbg(&pdev->dev, "got dynamic major %d\n", major);
+ }
+
+ pc8736x_init_shadow();
+ return 0;
+
+undo_platform_dev_add:
+ platform_device_put(pdev);
+undo_platform_dev_alloc:
+ kfree(pdev);
+ return rc;
+}
+
+static void __exit pc8736x_gpio_cleanup(void)
+{
+ dev_dbg(&pdev->dev, " cleanup\n");
+
+ release_region(pc8736x_gpio_base, 16);
+
+ unregister_chrdev(major, DEVNAME);
+}
+
+EXPORT_SYMBOL(pc8736x_access);
+
+module_init(pc8736x_gpio_init);
+module_exit(pc8736x_gpio_cleanup);
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 9b5a2c0e7008..0c17f61549b4 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -101,7 +101,7 @@ static void pty_unthrottle(struct tty_struct * tty)
*
* FIXME: Our pty_write method is called with our ldisc lock held but
* not our partners. We can't just take the other one blindly without
- * risking deadlocks. There is also the small matter of TTY_DONT_FLIP
+ * risking deadlocks.
*/
static int pty_write(struct tty_struct * tty, const unsigned char *buf, int count)
{
diff --git a/drivers/char/rio/riointr.c b/drivers/char/rio/riointr.c
index eec1fea0cb92..0bd09040a5c0 100644
--- a/drivers/char/rio/riointr.c
+++ b/drivers/char/rio/riointr.c
@@ -546,7 +546,7 @@ static void RIOReceive(struct rio_info *p, struct Port *PortP)
** run out of space it will be set to the offset of the
** next byte to copy from the packet data area. The packet
** length field is decremented by the number of bytes that
- ** we succesfully removed from the packet. When this reaches
+ ** we successfully removed from the packet. When this reaches
** zero, we reset the offset pointer to be zero, and free
** the packet from the front of the queue.
*/
diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
index 664a6e97eb1a..5a280a330401 100644
--- a/drivers/char/scx200_gpio.c
+++ b/drivers/char/scx200_gpio.c
@@ -1,4 +1,4 @@
-/* linux/drivers/char/scx200_gpio.c
+/* linux/drivers/char/scx200_gpio.c
National Semiconductor SCx200 GPIO driver. Allows a user space
process to play with the GPIO pins.
@@ -6,17 +6,26 @@
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> */
#include <linux/config.h>
+#include <linux/device.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
+#include <linux/platform_device.h>
#include <asm/uaccess.h>
#include <asm/io.h>
+#include <linux/types.h>
+#include <linux/cdev.h>
+
#include <linux/scx200_gpio.h>
+#include <linux/nsc_gpio.h>
#define NAME "scx200_gpio"
+#define DEVNAME NAME
+
+static struct platform_device *pdev;
MODULE_AUTHOR("Christer Weinigel <wingel@nano-system.com>");
MODULE_DESCRIPTION("NatSemi SCx200 GPIO Pin Driver");
@@ -26,70 +35,23 @@ static int major = 0; /* default to dynamic major */
module_param(major, int, 0);
MODULE_PARM_DESC(major, "Major device number");
-static ssize_t scx200_gpio_write(struct file *file, const char __user *data,
- size_t len, loff_t *ppos)
-{
- unsigned m = iminor(file->f_dentry->d_inode);
- size_t i;
-
- for (i = 0; i < len; ++i) {
- char c;
- if (get_user(c, data+i))
- return -EFAULT;
- switch (c)
- {
- case '0':
- scx200_gpio_set(m, 0);
- break;
- case '1':
- scx200_gpio_set(m, 1);
- break;
- case 'O':
- printk(KERN_INFO NAME ": GPIO%d output enabled\n", m);
- scx200_gpio_configure(m, ~1, 1);
- break;
- case 'o':
- printk(KERN_INFO NAME ": GPIO%d output disabled\n", m);
- scx200_gpio_configure(m, ~1, 0);
- break;
- case 'T':
- printk(KERN_INFO NAME ": GPIO%d output is push pull\n", m);
- scx200_gpio_configure(m, ~2, 2);
- break;
- case 't':
- printk(KERN_INFO NAME ": GPIO%d output is open drain\n", m);
- scx200_gpio_configure(m, ~2, 0);
- break;
- case 'P':
- printk(KERN_INFO NAME ": GPIO%d pull up enabled\n", m);
- scx200_gpio_configure(m, ~4, 4);
- break;
- case 'p':
- printk(KERN_INFO NAME ": GPIO%d pull up disabled\n", m);
- scx200_gpio_configure(m, ~4, 0);
- break;
- }
- }
-
- return len;
-}
-
-static ssize_t scx200_gpio_read(struct file *file, char __user *buf,
- size_t len, loff_t *ppos)
-{
- unsigned m = iminor(file->f_dentry->d_inode);
- int value;
-
- value = scx200_gpio_get(m);
- if (put_user(value ? '1' : '0', buf))
- return -EFAULT;
-
- return 1;
-}
+struct nsc_gpio_ops scx200_access = {
+ .owner = THIS_MODULE,
+ .gpio_config = scx200_gpio_configure,
+ .gpio_dump = nsc_gpio_dump,
+ .gpio_get = scx200_gpio_get,
+ .gpio_set = scx200_gpio_set,
+ .gpio_set_high = scx200_gpio_set_high,
+ .gpio_set_low = scx200_gpio_set_low,
+ .gpio_change = scx200_gpio_change,
+ .gpio_current = scx200_gpio_current
+};
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
unsigned m = iminor(inode);
+ file->private_data = &scx200_access;
+
if (m > 63)
return -EINVAL;
return nonseekable_open(inode, file);
@@ -103,47 +65,81 @@ static int scx200_gpio_release(struct inode *inode, struct file *file)
static struct file_operations scx200_gpio_fops = {
.owner = THIS_MODULE,
- .write = scx200_gpio_write,
- .read = scx200_gpio_read,
+ .write = nsc_gpio_write,
+ .read = nsc_gpio_read,
.open = scx200_gpio_open,
.release = scx200_gpio_release,
};
+struct cdev *scx200_devices;
+static int num_pins = 32;
+
static int __init scx200_gpio_init(void)
{
- int r;
-
- printk(KERN_DEBUG NAME ": NatSemi SCx200 GPIO Driver\n");
+ int rc, i;
+ dev_t dev = MKDEV(major, 0);
if (!scx200_gpio_present()) {
- printk(KERN_ERR NAME ": no SCx200 gpio pins available\n");
+ printk(KERN_ERR NAME ": no SCx200 gpio present\n");
return -ENODEV;
}
- r = register_chrdev(major, NAME, &scx200_gpio_fops);
- if (r < 0) {
- printk(KERN_ERR NAME ": unable to register character device\n");
- return r;
+ /* support dev_dbg() with pdev->dev */
+ pdev = platform_device_alloc(DEVNAME, 0);
+ if (!pdev)
+ return -ENOMEM;
+
+ rc = platform_device_add(pdev);
+ if (rc)
+ goto undo_malloc;
+
+ /* nsc_gpio uses dev_dbg(), so needs this */
+ scx200_access.dev = &pdev->dev;
+
+ if (major)
+ rc = register_chrdev_region(dev, num_pins, "scx200_gpio");
+ else {
+ rc = alloc_chrdev_region(&dev, 0, num_pins, "scx200_gpio");
+ major = MAJOR(dev);
}
- if (!major) {
- major = r;
- printk(KERN_DEBUG NAME ": got dynamic major %d\n", major);
+ if (rc < 0) {
+ dev_err(&pdev->dev, "SCx200 chrdev_region err: %d\n", rc);
+ goto undo_platform_device_add;
+ }
+ scx200_devices = kzalloc(num_pins * sizeof(struct cdev), GFP_KERNEL);
+ if (!scx200_devices) {
+ rc = -ENOMEM;
+ goto undo_chrdev_region;
+ }
+ for (i = 0; i < num_pins; i++) {
+ struct cdev *cdev = &scx200_devices[i];
+ cdev_init(cdev, &scx200_gpio_fops);
+ cdev->owner = THIS_MODULE;
+ rc = cdev_add(cdev, MKDEV(major, i), 1);
+ /* tolerate 'minor' errors */
+ if (rc)
+ dev_err(&pdev->dev, "Error %d on minor %d", rc, i);
}
- return 0;
+ return 0; /* succeed */
+
+undo_chrdev_region:
+ unregister_chrdev_region(dev, num_pins);
+undo_platform_device_add:
+ platform_device_put(pdev);
+undo_malloc:
+ kfree(pdev);
+ return rc;
}
static void __exit scx200_gpio_cleanup(void)
{
- unregister_chrdev(major, NAME);
+ kfree(scx200_devices);
+ unregister_chrdev_region(MKDEV(major, 0), num_pins);
+ platform_device_put(pdev);
+ platform_device_unregister(pdev);
+ /* kfree(pdev); */
}
module_init(scx200_gpio_init);
module_exit(scx200_gpio_cleanup);
-
-/*
- Local variables:
- compile-command: "make -k -C ../.. SUBDIRS=drivers/char modules"
- c-basic-offset: 8
- End:
-*/
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
index 1b5330299e30..d2d6b01dcd05 100644
--- a/drivers/char/specialix.c
+++ b/drivers/char/specialix.c
@@ -2477,7 +2477,7 @@ static int __init specialix_init(void)
#endif
for (i = 0; i < SX_NBOARD; i++)
- sx_board[i].lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&sx_board[i].lock);
if (sx_init_drivers()) {
func_exit();
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index a9c5a7230f89..00b4a2187164 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -141,15 +141,6 @@ static char *stl_drvversion = "5.6.0";
static struct tty_driver *stl_serial;
/*
- * We will need to allocate a temporary write buffer for chars that
- * come direct from user space. The problem is that a copy from user
- * space might cause a page fault (typically on a system that is
- * swapping!). All ports will share one buffer - since if the system
- * is already swapping a shared buffer won't make things any worse.
- */
-static char *stl_tmpwritebuf;
-
-/*
* Define a local default termios struct. All ports will be created
* with this termios initially. Basically all it defines is a raw port
* at 9600, 8 data bits, 1 stop bit.
@@ -363,6 +354,14 @@ static unsigned char stl_vecmap[] = {
};
/*
+ * Lock ordering is that you may not take stallion_lock holding
+ * brd_lock.
+ */
+
+static spinlock_t brd_lock; /* Guard the board mapping */
+static spinlock_t stallion_lock; /* Guard the tty driver */
+
+/*
* Set up enable and disable macros for the ECH boards. They require
* the secondary io address space to be activated and deactivated.
* This way all ECH boards can share their secondary io region.
@@ -725,17 +724,7 @@ static struct class *stallion_class;
static int __init stallion_module_init(void)
{
- unsigned long flags;
-
-#ifdef DEBUG
- printk("init_module()\n");
-#endif
-
- save_flags(flags);
- cli();
stl_init();
- restore_flags(flags);
-
return 0;
}
@@ -746,7 +735,6 @@ static void __exit stallion_module_exit(void)
stlbrd_t *brdp;
stlpanel_t *panelp;
stlport_t *portp;
- unsigned long flags;
int i, j, k;
#ifdef DEBUG
@@ -756,9 +744,6 @@ static void __exit stallion_module_exit(void)
printk(KERN_INFO "Unloading %s: version %s\n", stl_drvtitle,
stl_drvversion);
- save_flags(flags);
- cli();
-
/*
* Free up all allocated resources used by the ports. This includes
* memory and interrupts. As part of this process we will also do
@@ -770,7 +755,6 @@ static void __exit stallion_module_exit(void)
if (i) {
printk("STALLION: failed to un-register tty driver, "
"errno=%d\n", -i);
- restore_flags(flags);
return;
}
for (i = 0; i < 4; i++) {
@@ -783,8 +767,6 @@ static void __exit stallion_module_exit(void)
"errno=%d\n", -i);
class_destroy(stallion_class);
- kfree(stl_tmpwritebuf);
-
for (i = 0; (i < stl_nrbrds); i++) {
if ((brdp = stl_brds[i]) == (stlbrd_t *) NULL)
continue;
@@ -814,8 +796,6 @@ static void __exit stallion_module_exit(void)
kfree(brdp);
stl_brds[i] = (stlbrd_t *) NULL;
}
-
- restore_flags(flags);
}
module_init(stallion_module_init);
@@ -948,7 +928,7 @@ static stlbrd_t *stl_allocbrd(void)
brdp = kzalloc(sizeof(stlbrd_t), GFP_KERNEL);
if (!brdp) {
- printk("STALLION: failed to allocate memory (size=%d)\n",
+ printk("STALLION: failed to allocate memory (size=%Zd)\n",
sizeof(stlbrd_t));
return NULL;
}
@@ -1066,16 +1046,17 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp)
rc = 0;
doclocal = 0;
+ spin_lock_irqsave(&stallion_lock, flags);
+
if (portp->tty->termios->c_cflag & CLOCAL)
doclocal++;
- save_flags(flags);
- cli();
portp->openwaitcnt++;
if (! tty_hung_up_p(filp))
portp->refcount--;
for (;;) {
+ /* Takes brd_lock internally */
stl_setsignals(portp, 1, 1);
if (tty_hung_up_p(filp) ||
((portp->flags & ASYNC_INITIALIZED) == 0)) {
@@ -1093,13 +1074,14 @@ static int stl_waitcarrier(stlport_t *portp, struct file *filp)
rc = -ERESTARTSYS;
break;
}
+ /* FIXME */
interruptible_sleep_on(&portp->open_wait);
}
if (! tty_hung_up_p(filp))
portp->refcount++;
portp->openwaitcnt--;
- restore_flags(flags);
+ spin_unlock_irqrestore(&stallion_lock, flags);
return rc;
}
@@ -1119,16 +1101,15 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
if (portp == (stlport_t *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&stallion_lock, flags);
if (tty_hung_up_p(filp)) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&stallion_lock, flags);
return;
}
if ((tty->count == 1) && (portp->refcount != 1))
portp->refcount = 1;
if (portp->refcount-- > 1) {
- restore_flags(flags);
+ spin_unlock_irqrestore(&stallion_lock, flags);
return;
}
@@ -1142,11 +1123,18 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
* (The sc26198 has no "end-of-data" interrupt only empty FIFO)
*/
tty->closing = 1;
+
+ spin_unlock_irqrestore(&stallion_lock, flags);
+
if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, portp->closing_wait);
stl_waituntilsent(tty, (HZ / 2));
+
+ spin_lock_irqsave(&stallion_lock, flags);
portp->flags &= ~ASYNC_INITIALIZED;
+ spin_unlock_irqrestore(&stallion_lock, flags);
+
stl_disableintrs(portp);
if (tty->termios->c_cflag & HUPCL)
stl_setsignals(portp, 0, 0);
@@ -1173,7 +1161,6 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
portp->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&portp->close_wait);
- restore_flags(flags);
}
/*****************************************************************************/
@@ -1195,9 +1182,6 @@ static int stl_write(struct tty_struct *tty, const unsigned char *buf, int count
(int) tty, (int) buf, count);
#endif
- if ((tty == (struct tty_struct *) NULL) ||
- (stl_tmpwritebuf == (char *) NULL))
- return 0;
portp = tty->driver_data;
if (portp == (stlport_t *) NULL)
return 0;
@@ -1302,11 +1286,6 @@ static void stl_flushchars(struct tty_struct *tty)
if (portp->tx.buf == (char *) NULL)
return;
-#if 0
- if (tty->stopped || tty->hw_stopped ||
- (portp->tx.head == portp->tx.tail))
- return;
-#endif
stl_startrxtx(portp, -1, 1);
}
@@ -1977,12 +1956,14 @@ static int stl_eiointr(stlbrd_t *brdp)
unsigned int iobase;
int handled = 0;
+ spin_lock(&brd_lock);
panelp = brdp->panels[0];
iobase = panelp->iobase;
while (inb(brdp->iostatus) & EIO_INTRPEND) {
handled = 1;
(* panelp->isr)(panelp, iobase);
}
+ spin_unlock(&brd_lock);
return handled;
}
@@ -2168,7 +2149,7 @@ static int __init stl_initports(stlbrd_t *brdp, stlpanel_t *panelp)
portp = kzalloc(sizeof(stlport_t), GFP_KERNEL);
if (!portp) {
printk("STALLION: failed to allocate memory "
- "(size=%d)\n", sizeof(stlport_t));
+ "(size=%Zd)\n", sizeof(stlport_t));
break;
}
@@ -2304,7 +2285,7 @@ static inline int stl_initeio(stlbrd_t *brdp)
panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL);
if (!panelp) {
printk(KERN_WARNING "STALLION: failed to allocate memory "
- "(size=%d)\n", sizeof(stlpanel_t));
+ "(size=%Zd)\n", sizeof(stlpanel_t));
return -ENOMEM;
}
@@ -2478,7 +2459,7 @@ static inline int stl_initech(stlbrd_t *brdp)
panelp = kzalloc(sizeof(stlpanel_t), GFP_KERNEL);
if (!panelp) {
printk("STALLION: failed to allocate memory "
- "(size=%d)\n", sizeof(stlpanel_t));
+ "(size=%Zd)\n", sizeof(stlpanel_t));
break;
}
panelp->magic = STL_PANELMAGIC;
@@ -2879,8 +2860,7 @@ static int stl_getportstats(stlport_t *portp, comstats_t __user *cp)
portp->stats.lflags = 0;
portp->stats.rxbuffered = 0;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&stallion_lock, flags);
if (portp->tty != (struct tty_struct *) NULL) {
if (portp->tty->driver_data == portp) {
portp->stats.ttystate = portp->tty->flags;
@@ -2894,7 +2874,7 @@ static int stl_getportstats(stlport_t *portp, comstats_t __user *cp)
}
}
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&stallion_lock, flags);
head = portp->tx.head;
tail = portp->tx.tail;
@@ -3049,6 +3029,9 @@ static int __init stl_init(void)
int i;
printk(KERN_INFO "%s: version %s\n", stl_drvtitle, stl_drvversion);
+ spin_lock_init(&stallion_lock);
+ spin_lock_init(&brd_lock);
+
stl_initbrds();
stl_serial = alloc_tty_driver(STL_MAXBRDS * STL_MAXPORTS);
@@ -3056,14 +3039,6 @@ static int __init stl_init(void)
return -1;
/*
- * Allocate a temporary write buffer.
- */
- stl_tmpwritebuf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
- if (!stl_tmpwritebuf)
- printk("STALLION: failed to allocate memory (size=%d)\n",
- STL_TXBUFSIZE);
-
-/*
* Set up a character driver for per board stuff. This is mainly used
* to do stats ioctls on the ports.
*/
@@ -3147,11 +3122,13 @@ static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
unsigned int gfrcr;
int chipmask, i, j;
int nrchips, uartaddr, ioaddr;
+ unsigned long flags;
#ifdef DEBUG
printk("stl_panelinit(brdp=%x,panelp=%x)\n", (int) brdp, (int) panelp);
#endif
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(panelp->brdnr, panelp->pagenr);
/*
@@ -3189,6 +3166,7 @@ static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
}
BRDDISABLE(panelp->brdnr);
+ spin_unlock_irqrestore(&brd_lock, flags);
return chipmask;
}
@@ -3200,6 +3178,7 @@ static int stl_cd1400panelinit(stlbrd_t *brdp, stlpanel_t *panelp)
static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *portp)
{
+ unsigned long flags;
#ifdef DEBUG
printk("stl_cd1400portinit(brdp=%x,panelp=%x,portp=%x)\n",
(int) brdp, (int) panelp, (int) portp);
@@ -3209,6 +3188,7 @@ static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *po
(portp == (stlport_t *) NULL))
return;
+ spin_lock_irqsave(&brd_lock, flags);
portp->ioaddr = panelp->iobase + (((brdp->brdtype == BRD_ECHPCI) ||
(portp->portnr < 8)) ? 0 : EREG_BANKSIZE);
portp->uartaddr = (portp->portnr & 0x04) << 5;
@@ -3219,6 +3199,7 @@ static void stl_cd1400portinit(stlbrd_t *brdp, stlpanel_t *panelp, stlport_t *po
stl_cd1400setreg(portp, LIVR, (portp->portnr << 3));
portp->hwid = stl_cd1400getreg(portp, GFRCR);
BRDDISABLE(portp->brdnr);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3428,8 +3409,7 @@ static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x3));
srer = stl_cd1400getreg(portp, SRER);
@@ -3466,7 +3446,7 @@ static void stl_cd1400setport(stlport_t *portp, struct termios *tiosp)
portp->sigs &= ~TIOCM_CD;
stl_cd1400setreg(portp, SRER, ((srer & ~sreroff) | sreron));
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3492,8 +3472,7 @@ static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
if (rts > 0)
msvr2 = MSVR2_RTS;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
if (rts >= 0)
@@ -3501,7 +3480,7 @@ static void stl_cd1400setsignals(stlport_t *portp, int dtr, int rts)
if (dtr >= 0)
stl_cd1400setreg(portp, MSVR1, msvr1);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3520,14 +3499,13 @@ static int stl_cd1400getsignals(stlport_t *portp)
printk("stl_cd1400getsignals(portp=%x)\n", (int) portp);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
msvr1 = stl_cd1400getreg(portp, MSVR1);
msvr2 = stl_cd1400getreg(portp, MSVR2);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
sigs = 0;
sigs |= (msvr1 & MSVR1_DCD) ? TIOCM_CD : 0;
@@ -3569,15 +3547,14 @@ static void stl_cd1400enablerxtx(stlport_t *portp, int rx, int tx)
else if (rx > 0)
ccr |= CCR_RXENABLE;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
stl_cd1400ccrwait(portp);
stl_cd1400setreg(portp, CCR, ccr);
stl_cd1400ccrwait(portp);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3609,8 +3586,7 @@ static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
else if (rx > 0)
sreron |= SRER_RXDATA;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
stl_cd1400setreg(portp, SRER,
@@ -3618,7 +3594,7 @@ static void stl_cd1400startrxtx(stlport_t *portp, int rx, int tx)
BRDDISABLE(portp->brdnr);
if (tx > 0)
set_bit(ASYI_TXBUSY, &portp->istate);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3634,13 +3610,12 @@ static void stl_cd1400disableintrs(stlport_t *portp)
#ifdef DEBUG
printk("stl_cd1400disableintrs(portp=%x)\n", (int) portp);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
stl_cd1400setreg(portp, SRER, 0);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3653,8 +3628,7 @@ static void stl_cd1400sendbreak(stlport_t *portp, int len)
printk("stl_cd1400sendbreak(portp=%x,len=%d)\n", (int) portp, len);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
stl_cd1400setreg(portp, SRER,
@@ -3664,7 +3638,7 @@ static void stl_cd1400sendbreak(stlport_t *portp, int len)
portp->brklen = len;
if (len == 1)
portp->stats.txbreaks++;
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3688,8 +3662,7 @@ static void stl_cd1400flowctrl(stlport_t *portp, int state)
if (tty == (struct tty_struct *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
@@ -3729,7 +3702,7 @@ static void stl_cd1400flowctrl(stlport_t *portp, int state)
}
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3753,8 +3726,7 @@ static void stl_cd1400sendflow(stlport_t *portp, int state)
if (tty == (struct tty_struct *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
if (state) {
@@ -3769,7 +3741,7 @@ static void stl_cd1400sendflow(stlport_t *portp, int state)
stl_cd1400ccrwait(portp);
}
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3785,8 +3757,7 @@ static void stl_cd1400flush(stlport_t *portp)
if (portp == (stlport_t *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_cd1400setreg(portp, CAR, (portp->portnr & 0x03));
stl_cd1400ccrwait(portp);
@@ -3794,7 +3765,7 @@ static void stl_cd1400flush(stlport_t *portp)
stl_cd1400ccrwait(portp);
portp->tx.tail = portp->tx.head;
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -3833,6 +3804,7 @@ static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase)
(int) panelp, iobase);
#endif
+ spin_lock(&brd_lock);
outb(SVRR, iobase);
svrtype = inb(iobase + EREG_DATA);
if (panelp->nrports > 4) {
@@ -3846,6 +3818,8 @@ static void stl_cd1400eiointr(stlpanel_t *panelp, unsigned int iobase)
stl_cd1400txisr(panelp, iobase);
else if (svrtype & SVRR_MDM)
stl_cd1400mdmisr(panelp, iobase);
+
+ spin_unlock(&brd_lock);
}
/*****************************************************************************/
@@ -4433,8 +4407,7 @@ static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
tiosp->c_cc[VSTART], tiosp->c_cc[VSTOP]);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_sc26198setreg(portp, IMR, 0);
stl_sc26198updatereg(portp, MR0, mr0);
@@ -4461,7 +4434,7 @@ static void stl_sc26198setport(stlport_t *portp, struct termios *tiosp)
portp->imr = (portp->imr & ~imroff) | imron;
stl_sc26198setreg(portp, IMR, portp->imr);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4491,13 +4464,12 @@ static void stl_sc26198setsignals(stlport_t *portp, int dtr, int rts)
else if (rts > 0)
iopioron |= IPR_RTS;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_sc26198setreg(portp, IOPIOR,
((stl_sc26198getreg(portp, IOPIOR) & ~iopioroff) | iopioron));
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4516,12 +4488,11 @@ static int stl_sc26198getsignals(stlport_t *portp)
printk("stl_sc26198getsignals(portp=%x)\n", (int) portp);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
ipr = stl_sc26198getreg(portp, IPR);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
sigs = 0;
sigs |= (ipr & IPR_DCD) ? 0 : TIOCM_CD;
@@ -4558,13 +4529,12 @@ static void stl_sc26198enablerxtx(stlport_t *portp, int rx, int tx)
else if (rx > 0)
ccr |= CR_RXENABLE;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_sc26198setreg(portp, SCCR, ccr);
BRDDISABLE(portp->brdnr);
portp->crenable = ccr;
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4593,15 +4563,14 @@ static void stl_sc26198startrxtx(stlport_t *portp, int rx, int tx)
else if (rx > 0)
imr |= IR_RXRDY | IR_RXBREAK | IR_RXWATCHDOG;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_sc26198setreg(portp, IMR, imr);
BRDDISABLE(portp->brdnr);
portp->imr = imr;
if (tx > 0)
set_bit(ASYI_TXBUSY, &portp->istate);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4618,13 +4587,12 @@ static void stl_sc26198disableintrs(stlport_t *portp)
printk("stl_sc26198disableintrs(portp=%x)\n", (int) portp);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
portp->imr = 0;
stl_sc26198setreg(portp, IMR, 0);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4637,8 +4605,7 @@ static void stl_sc26198sendbreak(stlport_t *portp, int len)
printk("stl_sc26198sendbreak(portp=%x,len=%d)\n", (int) portp, len);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
if (len == 1) {
stl_sc26198setreg(portp, SCCR, CR_TXSTARTBREAK);
@@ -4647,7 +4614,7 @@ static void stl_sc26198sendbreak(stlport_t *portp, int len)
stl_sc26198setreg(portp, SCCR, CR_TXSTOPBREAK);
}
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4672,8 +4639,7 @@ static void stl_sc26198flowctrl(stlport_t *portp, int state)
if (tty == (struct tty_struct *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
if (state) {
@@ -4719,7 +4685,7 @@ static void stl_sc26198flowctrl(stlport_t *portp, int state)
}
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4744,8 +4710,7 @@ static void stl_sc26198sendflow(stlport_t *portp, int state)
if (tty == (struct tty_struct *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
if (state) {
mr0 = stl_sc26198getreg(portp, MR0);
@@ -4765,7 +4730,7 @@ static void stl_sc26198sendflow(stlport_t *portp, int state)
stl_sc26198setreg(portp, MR0, mr0);
}
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4781,14 +4746,13 @@ static void stl_sc26198flush(stlport_t *portp)
if (portp == (stlport_t *) NULL)
return;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
stl_sc26198setreg(portp, SCCR, CR_TXRESET);
stl_sc26198setreg(portp, SCCR, portp->crenable);
BRDDISABLE(portp->brdnr);
portp->tx.tail = portp->tx.head;
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
}
/*****************************************************************************/
@@ -4815,12 +4779,11 @@ static int stl_sc26198datastate(stlport_t *portp)
if (test_bit(ASYI_TXBUSY, &portp->istate))
return 1;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&brd_lock, flags);
BRDENABLE(portp->brdnr, portp->pagenr);
sr = stl_sc26198getreg(portp, SR);
BRDDISABLE(portp->brdnr);
- restore_flags(flags);
+ spin_unlock_irqrestore(&brd_lock, flags);
return (sr & SR_TXEMPTY) ? 0 : 1;
}
@@ -4878,6 +4841,8 @@ static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
stlport_t *portp;
unsigned int iack;
+ spin_lock(&brd_lock);
+
/*
* Work around bug in sc26198 chip... Cannot have A6 address
* line of UART high, else iack will be returned as 0.
@@ -4893,6 +4858,8 @@ static void stl_sc26198intr(stlpanel_t *panelp, unsigned int iobase)
stl_sc26198txisr(portp);
else
stl_sc26198otherisr(portp, iack);
+
+ spin_unlock(&brd_lock);
}
/*****************************************************************************/
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index 3b4747230270..76b9107f7f81 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -2320,7 +2320,7 @@ static int sx_init_portstructs (int nboards, int nports)
#ifdef NEW_WRITE_LOCKING
port->gs.port_write_mutex = MUTEX;
#endif
- port->gs.driver_lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&port->gs.driver_lock);
/*
* Initializing wait queue
*/
diff --git a/drivers/char/tlclk.c b/drivers/char/tlclk.c
index f58ad7f68267..ef68d152d3e4 100644
--- a/drivers/char/tlclk.c
+++ b/drivers/char/tlclk.c
@@ -343,7 +343,7 @@ static ssize_t store_received_ref_clk3b(struct device *d,
val = (unsigned char)tmp;
spin_lock_irqsave(&event_lock, flags);
- SET_PORT_BITS(TLCLK_REG1, 0xef, val << 1);
+ SET_PORT_BITS(TLCLK_REG1, 0xdf, val << 1);
spin_unlock_irqrestore(&event_lock, flags);
return strnlen(buf, count);
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 8b2a59969868..8d19f7281f0b 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -267,7 +267,6 @@ static struct tty_buffer *tty_buffer_alloc(size_t size)
p->used = 0;
p->size = size;
p->next = NULL;
- p->active = 0;
p->commit = 0;
p->read = 0;
p->char_buf_ptr = (char *)(p->data);
@@ -327,10 +326,9 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size)
/* OPTIMISATION: We could keep a per tty "zero" sized buffer to
remove this conditional if its worth it. This would be invisible
to the callers */
- if ((b = tty->buf.tail) != NULL) {
+ if ((b = tty->buf.tail) != NULL)
left = b->size - b->used;
- b->active = 1;
- } else
+ else
left = 0;
if (left < size) {
@@ -338,12 +336,10 @@ int tty_buffer_request_room(struct tty_struct *tty, size_t size)
if ((n = tty_buffer_find(tty, size)) != NULL) {
if (b != NULL) {
b->next = n;
- b->active = 0;
b->commit = b->used;
} else
tty->buf.head = n;
tty->buf.tail = n;
- n->active = 1;
} else
size = left;
}
@@ -404,10 +400,8 @@ void tty_schedule_flip(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL) {
- tty->buf.tail->active = 0;
+ if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
- }
spin_unlock_irqrestore(&tty->buf.lock, flags);
schedule_delayed_work(&tty->buf.work, 1);
}
@@ -784,11 +778,8 @@ restart:
}
clear_bit(TTY_LDISC, &tty->flags);
- clear_bit(TTY_DONT_FLIP, &tty->flags);
- if (o_tty) {
+ if (o_tty)
clear_bit(TTY_LDISC, &o_tty->flags);
- clear_bit(TTY_DONT_FLIP, &o_tty->flags);
- }
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
/*
@@ -1955,7 +1946,6 @@ static void release_dev(struct file * filp)
* race with the set_ldisc code path.
*/
clear_bit(TTY_LDISC, &tty->flags);
- clear_bit(TTY_DONT_FLIP, &tty->flags);
cancel_delayed_work(&tty->buf.work);
/*
@@ -2621,10 +2611,9 @@ int tty_ioctl(struct inode * inode, struct file * file,
tty->driver->break_ctl(tty, 0);
return 0;
case TCSBRK: /* SVID version: non-zero arg --> no break */
- /*
- * XXX is the above comment correct, or the
- * code below correct? Is this ioctl used at
- * all by anyone?
+ /* non-zero arg means wait for all output data
+ * to be sent (performed above) but don't send break.
+ * This is used by the tcdrain() termios function.
*/
if (!arg)
return send_break(tty, 250);
@@ -2776,8 +2765,7 @@ static void flush_to_ldisc(void *private_)
struct tty_struct *tty = (struct tty_struct *) private_;
unsigned long flags;
struct tty_ldisc *disc;
- struct tty_buffer *tbuf;
- int count;
+ struct tty_buffer *tbuf, *head;
char *char_buf;
unsigned char *flag_buf;
@@ -2785,32 +2773,37 @@ static void flush_to_ldisc(void *private_)
if (disc == NULL) /* !TTY_LDISC */
return;
- if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
- /*
- * Do it after the next timer tick:
- */
- schedule_delayed_work(&tty->buf.work, 1);
- goto out;
- }
spin_lock_irqsave(&tty->buf.lock, flags);
- while((tbuf = tty->buf.head) != NULL) {
- while ((count = tbuf->commit - tbuf->read) != 0) {
- char_buf = tbuf->char_buf_ptr + tbuf->read;
- flag_buf = tbuf->flag_buf_ptr + tbuf->read;
- tbuf->read += count;
+ head = tty->buf.head;
+ if (head != NULL) {
+ tty->buf.head = NULL;
+ for (;;) {
+ int count = head->commit - head->read;
+ if (!count) {
+ if (head->next == NULL)
+ break;
+ tbuf = head;
+ head = head->next;
+ tty_buffer_free(tty, tbuf);
+ continue;
+ }
+ if (!tty->receive_room) {
+ schedule_delayed_work(&tty->buf.work, 1);
+ break;
+ }
+ if (count > tty->receive_room)
+ count = tty->receive_room;
+ char_buf = head->char_buf_ptr + head->read;
+ flag_buf = head->flag_buf_ptr + head->read;
+ head->read += count;
spin_unlock_irqrestore(&tty->buf.lock, flags);
disc->receive_buf(tty, char_buf, flag_buf, count);
spin_lock_irqsave(&tty->buf.lock, flags);
}
- if (tbuf->active)
- break;
- tty->buf.head = tbuf->next;
- if (tty->buf.head == NULL)
- tty->buf.tail = NULL;
- tty_buffer_free(tty, tbuf);
+ tty->buf.head = head;
}
spin_unlock_irqrestore(&tty->buf.lock, flags);
-out:
+
tty_ldisc_deref(disc);
}
@@ -2903,10 +2896,8 @@ void tty_flip_buffer_push(struct tty_struct *tty)
{
unsigned long flags;
spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL) {
- tty->buf.tail->active = 0;
+ if (tty->buf.tail != NULL)
tty->buf.tail->commit = tty->buf.tail->used;
- }
spin_unlock_irqrestore(&tty->buf.lock, flags);
if (tty->low_latency)
diff --git a/drivers/char/vr41xx_giu.c b/drivers/char/vr41xx_giu.c
index 05e6e814d86f..073da48c092e 100644
--- a/drivers/char/vr41xx_giu.c
+++ b/drivers/char/vr41xx_giu.c
@@ -689,9 +689,9 @@ static int __devinit giu_probe(struct platform_device *dev)
for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) {
if (i < GIU_IRQ(GIUINT_HIGH_OFFSET))
- irq_desc[i].handler = &giuint_low_irq_type;
+ irq_desc[i].chip = &giuint_low_irq_type;
else
- irq_desc[i].handler = &giuint_high_irq_type;
+ irq_desc[i].chip = &giuint_high_irq_type;
}
return cascade_irq(GIUINT_IRQ, giu_get_irq);
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index 6c94879e0b99..714d95ff2f1e 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -98,7 +98,22 @@
#include <asm/system.h>
#include <asm/uaccess.h>
+#define MAX_NR_CON_DRIVER 16
+#define CON_DRIVER_FLAG_MODULE 1
+#define CON_DRIVER_FLAG_INIT 2
+
+struct con_driver {
+ const struct consw *con;
+ const char *desc;
+ struct class_device *class_dev;
+ int node;
+ int first;
+ int last;
+ int flag;
+};
+
+static struct con_driver registered_con_driver[MAX_NR_CON_DRIVER];
const struct consw *conswitchp;
/* A bitmap for codes <32. A bit of 1 indicates that the code
@@ -2557,7 +2572,7 @@ static int __init con_init(void)
{
const char *display_desc = NULL;
struct vc_data *vc;
- unsigned int currcons = 0;
+ unsigned int currcons = 0, i;
acquire_console_sem();
@@ -2569,6 +2584,22 @@ static int __init con_init(void)
return 0;
}
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = conswitchp;
+ con_driver->desc = display_desc;
+ con_driver->flag = CON_DRIVER_FLAG_INIT;
+ con_driver->first = 0;
+ con_driver->last = MAX_NR_CONSOLES - 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ con_driver_map[i] = conswitchp;
+
init_timer(&console_timer);
console_timer.function = blank_screen_t;
if (blankinterval) {
@@ -2656,38 +2687,53 @@ int __init vty_init(void)
}
#ifndef VT_SINGLE_DRIVER
+#include <linux/device.h>
-/*
- * If we support more console drivers, this function is used
- * when a driver wants to take over some existing consoles
- * and become default driver for newly opened ones.
- */
+static struct class *vtconsole_class;
-int take_over_console(const struct consw *csw, int first, int last, int deflt)
+static int bind_con_driver(const struct consw *csw, int first, int last,
+ int deflt)
{
- int i, j = -1;
- const char *desc;
- struct module *owner;
+ struct module *owner = csw->owner;
+ const char *desc = NULL;
+ struct con_driver *con_driver;
+ int i, j = -1, k = -1, retval = -ENODEV;
- owner = csw->owner;
if (!try_module_get(owner))
return -ENODEV;
acquire_console_sem();
- desc = csw->con_startup();
- if (!desc) {
- release_console_sem();
- module_put(owner);
- return -ENODEV;
+ /* check if driver is registered */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw) {
+ desc = con_driver->desc;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ if (!(con_driver->flag & CON_DRIVER_FLAG_INIT)) {
+ csw->con_startup();
+ con_driver->flag |= CON_DRIVER_FLAG_INIT;
}
+
if (deflt) {
if (conswitchp)
module_put(conswitchp->owner);
+
__module_get(owner);
conswitchp = csw;
}
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
for (i = first; i <= last; i++) {
int old_was_color;
struct vc_data *vc = vc_cons[i].d;
@@ -2701,15 +2747,17 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
continue;
j = i;
- if (CON_IS_VISIBLE(vc))
+
+ if (CON_IS_VISIBLE(vc)) {
+ k = i;
save_screen(vc);
+ }
+
old_was_color = vc->vc_can_do_color;
vc->vc_sw->con_deinit(vc);
vc->vc_origin = (unsigned long)vc->vc_screenbuf;
- vc->vc_visible_origin = vc->vc_origin;
- vc->vc_scr_end = vc->vc_origin + vc->vc_screenbuf_size;
- vc->vc_pos = vc->vc_origin + vc->vc_size_row * vc->vc_y + 2 * vc->vc_x;
visual_init(vc, i, 0);
+ set_origin(vc);
update_attr(vc);
/* If the console changed between mono <-> color, then
@@ -2718,36 +2766,506 @@ int take_over_console(const struct consw *csw, int first, int last, int deflt)
*/
if (old_was_color != vc->vc_can_do_color)
clear_buffer_attributes(vc);
-
- if (CON_IS_VISIBLE(vc))
- update_screen(vc);
}
+
printk("Console: switching ");
if (!deflt)
printk("consoles %d-%d ", first+1, last+1);
- if (j >= 0)
+ if (j >= 0) {
+ struct vc_data *vc = vc_cons[j].d;
+
printk("to %s %s %dx%d\n",
- vc_cons[j].d->vc_can_do_color ? "colour" : "mono",
- desc, vc_cons[j].d->vc_cols, vc_cons[j].d->vc_rows);
- else
+ vc->vc_can_do_color ? "colour" : "mono",
+ desc, vc->vc_cols, vc->vc_rows);
+
+ if (k >= 0) {
+ vc = vc_cons[k].d;
+ update_screen(vc);
+ }
+ } else
printk("to %s\n", desc);
+ retval = 0;
+err:
release_console_sem();
+ module_put(owner);
+ return retval;
+};
+
+#ifdef CONFIG_VT_HW_CONSOLE_BINDING
+static int con_is_graphics(const struct consw *csw, int first, int last)
+{
+ int i, retval = 0;
+
+ for (i = first; i <= last; i++) {
+ struct vc_data *vc = vc_cons[i].d;
+
+ if (vc && vc->vc_mode == KD_GRAPHICS) {
+ retval = 1;
+ break;
+ }
+ }
+
+ return retval;
+}
+
+static int unbind_con_driver(const struct consw *csw, int first, int last,
+ int deflt)
+{
+ struct module *owner = csw->owner;
+ const struct consw *defcsw = NULL;
+ struct con_driver *con_driver = NULL, *con_back = NULL;
+ int i, retval = -ENODEV;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ /* check if driver is registered and if it is unbindable */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ retval = -ENODEV;
+
+ /* check if backup driver exists */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_back = &registered_con_driver[i];
+
+ if (con_back->con &&
+ !(con_back->flag & CON_DRIVER_FLAG_MODULE)) {
+ defcsw = con_back->con;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval) {
+ release_console_sem();
+ goto err;
+ }
+
+ if (!con_is_bound(csw)) {
+ release_console_sem();
+ goto err;
+ }
+
+ first = max(first, con_driver->first);
+ last = min(last, con_driver->last);
+
+ for (i = first; i <= last; i++) {
+ if (con_driver_map[i] == csw) {
+ module_put(csw->owner);
+ con_driver_map[i] = NULL;
+ }
+ }
+
+ if (!con_is_bound(defcsw)) {
+ const struct consw *defconsw = conswitchp;
+
+ defcsw->con_startup();
+ con_back->flag |= CON_DRIVER_FLAG_INIT;
+ /*
+ * vgacon may change the default driver to point
+ * to dummycon, we restore it here...
+ */
+ conswitchp = defconsw;
+ }
+
+ if (!con_is_bound(csw))
+ con_driver->flag &= ~CON_DRIVER_FLAG_INIT;
+ release_console_sem();
+ /* ignore return value, binding should not fail */
+ bind_con_driver(defcsw, first, last, deflt);
+err:
module_put(owner);
+ return retval;
+
+}
+
+static int vt_bind(struct con_driver *con)
+{
+ const struct consw *defcsw = NULL, *csw = NULL;
+ int i, more = 1, first = -1, last = -1, deflt = 0;
+
+ if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
+ con_is_graphics(con->con, con->first, con->last))
+ goto err;
+
+ csw = con->con;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = &registered_con_driver[i];
+
+ if (con->con && !(con->flag & CON_DRIVER_FLAG_MODULE)) {
+ defcsw = con->con;
+ break;
+ }
+ }
+
+ if (!defcsw)
+ goto err;
+
+ while (more) {
+ more = 0;
+
+ for (i = con->first; i <= con->last; i++) {
+ if (con_driver_map[i] == defcsw) {
+ if (first == -1)
+ first = i;
+ last = i;
+ more = 1;
+ } else if (first != -1)
+ break;
+ }
+
+ if (first == 0 && last == MAX_NR_CONSOLES -1)
+ deflt = 1;
+
+ if (first != -1)
+ bind_con_driver(csw, first, last, deflt);
+
+ first = -1;
+ last = -1;
+ deflt = 0;
+ }
+
+err:
return 0;
}
-void give_up_console(const struct consw *csw)
+static int vt_unbind(struct con_driver *con)
+{
+ const struct consw *csw = NULL;
+ int i, more = 1, first = -1, last = -1, deflt = 0;
+
+ if (!con->con || !(con->flag & CON_DRIVER_FLAG_MODULE) ||
+ con_is_graphics(con->con, con->first, con->last))
+ goto err;
+
+ csw = con->con;
+
+ while (more) {
+ more = 0;
+
+ for (i = con->first; i <= con->last; i++) {
+ if (con_driver_map[i] == csw) {
+ if (first == -1)
+ first = i;
+ last = i;
+ more = 1;
+ } else if (first != -1)
+ break;
+ }
+
+ if (first == 0 && last == MAX_NR_CONSOLES -1)
+ deflt = 1;
+
+ if (first != -1)
+ unbind_con_driver(csw, first, last, deflt);
+
+ first = -1;
+ last = -1;
+ deflt = 0;
+ }
+
+err:
+ return 0;
+}
+#else
+static inline int vt_bind(struct con_driver *con)
+{
+ return 0;
+}
+static inline int vt_unbind(struct con_driver *con)
+{
+ return 0;
+}
+#endif /* CONFIG_VT_HW_CONSOLE_BINDING */
+
+static ssize_t store_bind(struct class_device *class_device,
+ const char *buf, size_t count)
+{
+ struct con_driver *con = class_get_devdata(class_device);
+ int bind = simple_strtoul(buf, NULL, 0);
+
+ if (bind)
+ vt_bind(con);
+ else
+ vt_unbind(con);
+
+ return count;
+}
+
+static ssize_t show_bind(struct class_device *class_device, char *buf)
+{
+ struct con_driver *con = class_get_devdata(class_device);
+ int bind = con_is_bound(con->con);
+
+ return snprintf(buf, PAGE_SIZE, "%i\n", bind);
+}
+
+static ssize_t show_name(struct class_device *class_device, char *buf)
+{
+ struct con_driver *con = class_get_devdata(class_device);
+
+ return snprintf(buf, PAGE_SIZE, "%s %s\n",
+ (con->flag & CON_DRIVER_FLAG_MODULE) ? "(M)" : "(S)",
+ con->desc);
+
+}
+
+static struct class_device_attribute class_device_attrs[] = {
+ __ATTR(bind, S_IRUGO|S_IWUSR, show_bind, store_bind),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+};
+
+static int vtconsole_init_class_device(struct con_driver *con)
+{
+ int i;
+
+ class_set_devdata(con->class_dev, con);
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_create_file(con->class_dev,
+ &class_device_attrs[i]);
+
+ return 0;
+}
+
+static void vtconsole_deinit_class_device(struct con_driver *con)
{
int i;
- for(i = 0; i < MAX_NR_CONSOLES; i++)
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_remove_file(con->class_dev,
+ &class_device_attrs[i]);
+}
+
+/**
+ * con_is_bound - checks if driver is bound to the console
+ * @csw: console driver
+ *
+ * RETURNS: zero if unbound, nonzero if bound
+ *
+ * Drivers can call this and if zero, they should release
+ * all resources allocated on con_startup()
+ */
+int con_is_bound(const struct consw *csw)
+{
+ int i, bound = 0;
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++) {
if (con_driver_map[i] == csw) {
- module_put(csw->owner);
- con_driver_map[i] = NULL;
+ bound = 1;
+ break;
+ }
+ }
+
+ return bound;
+}
+EXPORT_SYMBOL(con_is_bound);
+
+/**
+ * register_con_driver - register console driver to console layer
+ * @csw: console driver
+ * @first: the first console to take over, minimum value is 0
+ * @last: the last console to take over, maximum value is MAX_NR_CONSOLES -1
+ *
+ * DESCRIPTION: This function registers a console driver which can later
+ * bind to a range of consoles specified by @first and @last. It will
+ * also initialize the console driver by calling con_startup().
+ */
+int register_con_driver(const struct consw *csw, int first, int last)
+{
+ struct module *owner = csw->owner;
+ struct con_driver *con_driver;
+ const char *desc;
+ int i, retval = 0;
+
+ if (!try_module_get(owner))
+ return -ENODEV;
+
+ acquire_console_sem();
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ /* already registered */
+ if (con_driver->con == csw)
+ retval = -EINVAL;
+ }
+
+ if (retval)
+ goto err;
+
+ desc = csw->con_startup();
+
+ if (!desc)
+ goto err;
+
+ retval = -EINVAL;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == NULL) {
+ con_driver->con = csw;
+ con_driver->desc = desc;
+ con_driver->node = i;
+ con_driver->flag = CON_DRIVER_FLAG_MODULE |
+ CON_DRIVER_FLAG_INIT;
+ con_driver->first = first;
+ con_driver->last = last;
+ retval = 0;
+ break;
+ }
+ }
+
+ if (retval)
+ goto err;
+
+ con_driver->class_dev = class_device_create(vtconsole_class, NULL,
+ MKDEV(0, con_driver->node),
+ NULL, "vtcon%i",
+ con_driver->node);
+
+ if (IS_ERR(con_driver->class_dev)) {
+ printk(KERN_WARNING "Unable to create class_device for %s; "
+ "errno = %ld\n", con_driver->desc,
+ PTR_ERR(con_driver->class_dev));
+ con_driver->class_dev = NULL;
+ } else {
+ vtconsole_init_class_device(con_driver);
+ }
+err:
+ release_console_sem();
+ module_put(owner);
+ return retval;
+}
+EXPORT_SYMBOL(register_con_driver);
+
+/**
+ * unregister_con_driver - unregister console driver from console layer
+ * @csw: console driver
+ *
+ * DESCRIPTION: All drivers that registers to the console layer must
+ * call this function upon exit, or if the console driver is in a state
+ * where it won't be able to handle console services, such as the
+ * framebuffer console without loaded framebuffer drivers.
+ *
+ * The driver must unbind first prior to unregistration.
+ */
+int unregister_con_driver(const struct consw *csw)
+{
+ int i, retval = -ENODEV;
+
+ acquire_console_sem();
+
+ /* cannot unregister a bound driver */
+ if (con_is_bound(csw))
+ goto err;
+
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con_driver = &registered_con_driver[i];
+
+ if (con_driver->con == csw &&
+ con_driver->flag & CON_DRIVER_FLAG_MODULE) {
+ vtconsole_deinit_class_device(con_driver);
+ class_device_destroy(vtconsole_class,
+ MKDEV(0, con_driver->node));
+ con_driver->con = NULL;
+ con_driver->desc = NULL;
+ con_driver->class_dev = NULL;
+ con_driver->node = 0;
+ con_driver->flag = 0;
+ con_driver->first = 0;
+ con_driver->last = 0;
+ retval = 0;
+ break;
+ }
+ }
+err:
+ release_console_sem();
+ return retval;
+}
+EXPORT_SYMBOL(unregister_con_driver);
+
+/*
+ * If we support more console drivers, this function is used
+ * when a driver wants to take over some existing consoles
+ * and become default driver for newly opened ones.
+ *
+ * take_over_console is basically a register followed by unbind
+ */
+int take_over_console(const struct consw *csw, int first, int last, int deflt)
+{
+ int err;
+
+ err = register_con_driver(csw, first, last);
+
+ if (!err)
+ bind_con_driver(csw, first, last, deflt);
+
+ return err;
+}
+
+/*
+ * give_up_console is a wrapper to unregister_con_driver. It will only
+ * work if driver is fully unbound.
+ */
+void give_up_console(const struct consw *csw)
+{
+ unregister_con_driver(csw);
+}
+
+static int __init vtconsole_class_init(void)
+{
+ int i;
+
+ vtconsole_class = class_create(THIS_MODULE, "vtconsole");
+ if (IS_ERR(vtconsole_class)) {
+ printk(KERN_WARNING "Unable to create vt console class; "
+ "errno = %ld\n", PTR_ERR(vtconsole_class));
+ vtconsole_class = NULL;
+ }
+
+ /* Add system drivers to sysfs */
+ for (i = 0; i < MAX_NR_CON_DRIVER; i++) {
+ struct con_driver *con = &registered_con_driver[i];
+
+ if (con->con && !con->class_dev) {
+ con->class_dev =
+ class_device_create(vtconsole_class, NULL,
+ MKDEV(0, con->node), NULL,
+ "vtcon%i", con->node);
+
+ if (IS_ERR(con->class_dev)) {
+ printk(KERN_WARNING "Unable to create "
+ "class_device for %s; errno = %ld\n",
+ con->desc, PTR_ERR(con->class_dev));
+ con->class_dev = NULL;
+ } else {
+ vtconsole_init_class_device(con);
+ }
}
+ }
+
+ return 0;
}
+postcore_initcall(vtconsole_class_init);
#endif
diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c
index ac83bc4b019a..00080655533d 100644
--- a/drivers/char/watchdog/at91_wdt.c
+++ b/drivers/char/watchdog/at91_wdt.c
@@ -17,14 +17,15 @@
#include <linux/miscdevice.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
+#include <linux/platform_device.h>
#include <linux/types.h>
#include <linux/watchdog.h>
#include <asm/bitops.h>
#include <asm/uaccess.h>
-#define WDT_DEFAULT_TIME 5 /* 5 seconds */
-#define WDT_MAX_TIME 256 /* 256 seconds */
+#define WDT_DEFAULT_TIME 5 /* seconds */
+#define WDT_MAX_TIME 256 /* seconds */
static int wdt_time = WDT_DEFAULT_TIME;
static int nowayout = WATCHDOG_NOWAYOUT;
@@ -32,8 +33,10 @@ static int nowayout = WATCHDOG_NOWAYOUT;
module_param(wdt_time, int, 0);
MODULE_PARM_DESC(wdt_time, "Watchdog time in seconds. (default="__MODULE_STRING(WDT_DEFAULT_TIME) ")");
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
module_param(nowayout, int, 0);
MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
+#endif
static unsigned long at91wdt_busy;
@@ -138,7 +141,7 @@ static int at91_wdt_ioctl(struct inode *inode, struct file *file,
case WDIOC_SETTIMEOUT:
if (get_user(new_value, p))
return -EFAULT;
-
+
if (at91_wdt_settimeout(new_value))
return -EINVAL;
@@ -196,27 +199,84 @@ static struct miscdevice at91wdt_miscdev = {
.fops = &at91wdt_fops,
};
-static int __init at91_wdt_init(void)
+static int __init at91wdt_probe(struct platform_device *pdev)
{
int res;
- /* Check that the heartbeat value is within range; if not reset to the default */
- if (at91_wdt_settimeout(wdt_time)) {
- at91_wdt_settimeout(WDT_DEFAULT_TIME);
- printk(KERN_INFO "at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time);
- }
+ if (at91wdt_miscdev.dev)
+ return -EBUSY;
+ at91wdt_miscdev.dev = &pdev->dev;
res = misc_register(&at91wdt_miscdev);
if (res)
return res;
- printk("AT91 Watchdog Timer enabled (%d seconds, nowayout=%d)\n", wdt_time, nowayout);
+ printk("AT91 Watchdog Timer enabled (%d seconds%s)\n", wdt_time, nowayout ? ", nowayout" : "");
return 0;
}
+static int __exit at91wdt_remove(struct platform_device *pdev)
+{
+ int res;
+
+ res = misc_deregister(&at91wdt_miscdev);
+ if (!res)
+ at91wdt_miscdev.dev = NULL;
+
+ return res;
+}
+
+static void at91wdt_shutdown(struct platform_device *pdev)
+{
+ at91_wdt_stop();
+}
+
+#ifdef CONFIG_PM
+
+static int at91wdt_suspend(struct platform_device *pdev, pm_message_t message)
+{
+ at91_wdt_stop();
+ return 0;
+}
+
+static int at91wdt_resume(struct platform_device *pdev)
+{
+ if (at91wdt_busy)
+ at91_wdt_start();
+ return 0;
+}
+
+#else
+#define at91wdt_suspend NULL
+#define at91wdt_resume NULL
+#endif
+
+static struct platform_driver at91wdt_driver = {
+ .probe = at91wdt_probe,
+ .remove = __exit_p(at91wdt_remove),
+ .shutdown = at91wdt_shutdown,
+ .suspend = at91wdt_suspend,
+ .resume = at91wdt_resume,
+ .driver = {
+ .name = "at91_wdt",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init at91_wdt_init(void)
+{
+ /* Check that the heartbeat value is within range; if not reset to the default */
+ if (at91_wdt_settimeout(wdt_time)) {
+ at91_wdt_settimeout(WDT_DEFAULT_TIME);
+ pr_info("at91_wdt: wdt_time value must be 1 <= wdt_time <= 256, using %d\n", wdt_time);
+ }
+
+ return platform_driver_register(&at91wdt_driver);
+}
+
static void __exit at91_wdt_exit(void)
{
- misc_deregister(&at91wdt_miscdev);
+ platform_driver_unregister(&at91wdt_driver);
}
module_init(at91_wdt_init);
diff --git a/drivers/char/watchdog/i8xx_tco.c b/drivers/char/watchdog/i8xx_tco.c
index fa2ba9ebe42a..bfbdbbf3c2f2 100644
--- a/drivers/char/watchdog/i8xx_tco.c
+++ b/drivers/char/watchdog/i8xx_tco.c
@@ -205,6 +205,23 @@ static int tco_timer_set_heartbeat (int t)
return 0;
}
+static int tco_timer_get_timeleft (int *time_left)
+{
+ unsigned char val;
+
+ spin_lock(&tco_lock);
+
+ /* read the TCO Timer */
+ val = inb (TCO1_RLD);
+ val &= 0x3f;
+
+ spin_unlock(&tco_lock);
+
+ *time_left = (int)((val * 6) / 10);
+
+ return 0;
+}
+
/*
* /dev/watchdog handling
*/
@@ -272,6 +289,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
{
int new_options, retval = -EINVAL;
int new_heartbeat;
+ int time_left;
void __user *argp = (void __user *)arg;
int __user *p = argp;
static struct watchdog_info ident = {
@@ -320,7 +338,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
return -EFAULT;
if (tco_timer_set_heartbeat(new_heartbeat))
- return -EINVAL;
+ return -EINVAL;
tco_timer_keepalive ();
/* Fall */
@@ -329,6 +347,14 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
+ case WDIOC_GETTIMELEFT:
+ {
+ if (tco_timer_get_timeleft(&time_left))
+ return -EINVAL;
+
+ return put_user(time_left, p);
+ }
+
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c
index 2451edbefece..1f40ecefbf72 100644
--- a/drivers/char/watchdog/pcwd_pci.c
+++ b/drivers/char/watchdog/pcwd_pci.c
@@ -21,7 +21,7 @@
*/
/*
- * A bells and whistles driver is available from:
+ * A bells and whistles driver is available from:
* http://www.kernel.org/pub/linux/kernel/people/wim/pcwd/pcwd_pci/
*
* More info available at http://www.berkprod.com/ or http://www.pcwatchdog.com/
@@ -390,6 +390,24 @@ static int pcipcwd_get_temperature(int *temperature)
return 0;
}
+static int pcipcwd_get_timeleft(int *time_left)
+{
+ int msb;
+ int lsb;
+
+ /* Read the time that's left before rebooting */
+ /* Note: if the board is not yet armed then we will read 0xFFFF */
+ send_command(CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
+
+ *time_left = (msb << 8) + lsb;
+
+ if (debug >= VERBOSE)
+ printk(KERN_DEBUG PFX "Time left before next reboot: %d\n",
+ *time_left);
+
+ return 0;
+}
+
/*
* /dev/watchdog handling
*/
@@ -512,6 +530,16 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
+ case WDIOC_GETTIMELEFT:
+ {
+ int time_left;
+
+ if (pcipcwd_get_timeleft(&time_left))
+ return -EFAULT;
+
+ return put_user(time_left, p);
+ }
+
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c
index 3fdfda9324fa..0d072bed501d 100644
--- a/drivers/char/watchdog/pcwd_usb.c
+++ b/drivers/char/watchdog/pcwd_usb.c
@@ -317,6 +317,19 @@ static int usb_pcwd_get_temperature(struct usb_pcwd_private *usb_pcwd, int *temp
return 0;
}
+static int usb_pcwd_get_timeleft(struct usb_pcwd_private *usb_pcwd, int *time_left)
+{
+ unsigned char msb, lsb;
+
+ /* Read the time that's left before rebooting */
+ /* Note: if the board is not yet armed then we will read 0xFFFF */
+ usb_pcwd_send_command(usb_pcwd, CMD_READ_WATCHDOG_TIMEOUT, &msb, &lsb);
+
+ *time_left = (msb << 8) + lsb;
+
+ return 0;
+}
+
/*
* /dev/watchdog handling
*/
@@ -422,6 +435,16 @@ static int usb_pcwd_ioctl(struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(heartbeat, p);
+ case WDIOC_GETTIMELEFT:
+ {
+ int time_left;
+
+ if (usb_pcwd_get_timeleft(usb_pcwd_device, &time_left))
+ return -EFAULT;
+
+ return put_user(time_left, p);
+ }
+
default:
return -ENOIOCTLCMD;
}
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
new file mode 100644
index 000000000000..a52225470225
--- /dev/null
+++ b/drivers/clocksource/Makefile
@@ -0,0 +1,3 @@
+obj-$(CONFIG_X86_CYCLONE_TIMER) += cyclone.o
+obj-$(CONFIG_X86_PM_TIMER) += acpi_pm.o
+obj-$(CONFIG_SCx200HR_TIMER) += scx200_hrt.o
diff --git a/drivers/clocksource/acpi_pm.c b/drivers/clocksource/acpi_pm.c
new file mode 100644
index 000000000000..7ad3be8c0f49
--- /dev/null
+++ b/drivers/clocksource/acpi_pm.c
@@ -0,0 +1,177 @@
+/*
+ * linux/drivers/clocksource/acpi_pm.c
+ *
+ * This file contains the ACPI PM based clocksource.
+ *
+ * This code was largely moved from the i386 timer_pm.c file
+ * which was (C) Dominik Brodowski <linux@brodo.de> 2003
+ * and contained the following comments:
+ *
+ * Driver to use the Power Management Timer (PMTMR) available in some
+ * southbridges as primary timing source for the Linux kernel.
+ *
+ * Based on parts of linux/drivers/acpi/hardware/hwtimer.c, timer_pit.c,
+ * timer_hpet.c, and on Arjan van de Ven's implementation for 2.4.
+ *
+ * This file is licensed under the GPL v2.
+ */
+
+#include <linux/clocksource.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <asm/io.h>
+
+/* Number of PMTMR ticks expected during calibration run */
+#define PMTMR_TICKS_PER_SEC 3579545
+
+/*
+ * The I/O port the PMTMR resides at.
+ * The location is detected during setup_arch(),
+ * in arch/i386/acpi/boot.c
+ */
+u32 pmtmr_ioport __read_mostly;
+
+#define ACPI_PM_MASK CLOCKSOURCE_MASK(24) /* limit it to 24 bits */
+
+static inline u32 read_pmtmr(void)
+{
+ /* mask the output to 24 bits */
+ return inl(pmtmr_ioport) & ACPI_PM_MASK;
+}
+
+static cycle_t acpi_pm_read_verified(void)
+{
+ u32 v1 = 0, v2 = 0, v3 = 0;
+
+ /*
+ * It has been reported that because of various broken
+ * chipsets (ICH4, PIIX4 and PIIX4E) where the ACPI PM clock
+ * source is not latched, you must read it multiple
+ * times to ensure a safe value is read:
+ */
+ do {
+ v1 = read_pmtmr();
+ v2 = read_pmtmr();
+ v3 = read_pmtmr();
+ } while ((v1 > v2 && v1 < v3) || (v2 > v3 && v2 < v1)
+ || (v3 > v1 && v3 < v2));
+
+ return (cycle_t)v2;
+}
+
+static cycle_t acpi_pm_read(void)
+{
+ return (cycle_t)read_pmtmr();
+}
+
+static struct clocksource clocksource_acpi_pm = {
+ .name = "acpi_pm",
+ .rating = 200,
+ .read = acpi_pm_read,
+ .mask = (cycle_t)ACPI_PM_MASK,
+ .mult = 0, /*to be caluclated*/
+ .shift = 22,
+ .is_continuous = 1,
+};
+
+
+#ifdef CONFIG_PCI
+static int acpi_pm_good;
+static int __init acpi_pm_good_setup(char *__str)
+{
+ acpi_pm_good = 1;
+ return 1;
+}
+__setup("acpi_pm_good", acpi_pm_good_setup);
+
+static inline void acpi_pm_need_workaround(void)
+{
+ clocksource_acpi_pm.read = acpi_pm_read_verified;
+ clocksource_acpi_pm.rating = 110;
+}
+
+/*
+ * PIIX4 Errata:
+ *
+ * The power management timer may return improper results when read.
+ * Although the timer value settles properly after incrementing,
+ * while incrementing there is a 3 ns window every 69.8 ns where the
+ * timer value is indeterminate (a 4.2% chance that the data will be
+ * incorrect when read). As a result, the ACPI free running count up
+ * timer specification is violated due to erroneous reads.
+ */
+static void __devinit acpi_pm_check_blacklist(struct pci_dev *dev)
+{
+ u8 rev;
+
+ if (acpi_pm_good)
+ return;
+
+ pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
+ /* the bug has been fixed in PIIX4M */
+ if (rev < 3) {
+ printk(KERN_WARNING "* Found PM-Timer Bug on the chipset."
+ " Due to workarounds for a bug,\n"
+ "* this clock source is slow. Consider trying"
+ " other clock sources\n");
+
+ acpi_pm_need_workaround();
+ }
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3,
+ acpi_pm_check_blacklist);
+
+static void __devinit acpi_pm_check_graylist(struct pci_dev *dev)
+{
+ if (acpi_pm_good)
+ return;
+
+ printk(KERN_WARNING "* The chipset may have PM-Timer Bug. Due to"
+ " workarounds for a bug,\n"
+ "* this clock source is slow. If you are sure your timer"
+ " does not have\n"
+ "* this bug, please use \"acpi_pm_good\" to disable the"
+ " workaround\n");
+
+ acpi_pm_need_workaround();
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
+ acpi_pm_check_graylist);
+#endif
+
+
+static int __init init_acpi_pm_clocksource(void)
+{
+ u32 value1, value2;
+ unsigned int i;
+
+ if (!pmtmr_ioport)
+ return -ENODEV;
+
+ clocksource_acpi_pm.mult = clocksource_hz2mult(PMTMR_TICKS_PER_SEC,
+ clocksource_acpi_pm.shift);
+
+ /* "verify" this timing source: */
+ value1 = read_pmtmr();
+ for (i = 0; i < 10000; i++) {
+ value2 = read_pmtmr();
+ if (value2 == value1)
+ continue;
+ if (value2 > value1)
+ goto pm_good;
+ if ((value2 < value1) && ((value2) < 0xFFF))
+ goto pm_good;
+ printk(KERN_INFO "PM-Timer had inconsistent results:"
+ " 0x%#x, 0x%#x - aborting.\n", value1, value2);
+ return -EINVAL;
+ }
+ printk(KERN_INFO "PM-Timer had no reasonable result:"
+ " 0x%#x - aborting.\n", value1);
+ return -ENODEV;
+
+pm_good:
+ return clocksource_register(&clocksource_acpi_pm);
+}
+
+module_init(init_acpi_pm_clocksource);
diff --git a/drivers/clocksource/cyclone.c b/drivers/clocksource/cyclone.c
new file mode 100644
index 000000000000..bf4d3d50d1c4
--- /dev/null
+++ b/drivers/clocksource/cyclone.c
@@ -0,0 +1,119 @@
+#include <linux/clocksource.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/timex.h>
+#include <linux/init.h>
+
+#include <asm/pgtable.h>
+#include <asm/io.h>
+
+#include "mach_timer.h"
+
+#define CYCLONE_CBAR_ADDR 0xFEB00CD0 /* base address ptr */
+#define CYCLONE_PMCC_OFFSET 0x51A0 /* offset to control register */
+#define CYCLONE_MPCS_OFFSET 0x51A8 /* offset to select register */
+#define CYCLONE_MPMC_OFFSET 0x51D0 /* offset to count register */
+#define CYCLONE_TIMER_FREQ 99780000 /* 100Mhz, but not really */
+#define CYCLONE_TIMER_MASK CLOCKSOURCE_MASK(32) /* 32 bit mask */
+
+int use_cyclone = 0;
+static void __iomem *cyclone_ptr;
+
+static cycle_t read_cyclone(void)
+{
+ return (cycle_t)readl(cyclone_ptr);
+}
+
+static struct clocksource clocksource_cyclone = {
+ .name = "cyclone",
+ .rating = 250,
+ .read = read_cyclone,
+ .mask = CYCLONE_TIMER_MASK,
+ .mult = 10,
+ .shift = 0,
+ .is_continuous = 1,
+};
+
+static int __init init_cyclone_clocksource(void)
+{
+ unsigned long base; /* saved value from CBAR */
+ unsigned long offset;
+ u32 __iomem* volatile cyclone_timer; /* Cyclone MPMC0 register */
+ u32 __iomem* reg;
+ int i;
+
+ /* make sure we're on a summit box: */
+ if (!use_cyclone)
+ return -ENODEV;
+
+ printk(KERN_INFO "Summit chipset: Starting Cyclone Counter.\n");
+
+ /* find base address: */
+ offset = CYCLONE_CBAR_ADDR;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if (!reg) {
+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR register.\n");
+ return -ENODEV;
+ }
+ /* even on 64bit systems, this is only 32bits: */
+ base = readl(reg);
+ if (!base) {
+ printk(KERN_ERR "Summit chipset: Could not find valid CBAR value.\n");
+ return -ENODEV;
+ }
+ iounmap(reg);
+
+ /* setup PMCC: */
+ offset = base + CYCLONE_PMCC_OFFSET;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if (!reg) {
+ printk(KERN_ERR "Summit chipset: Could not find valid PMCC register.\n");
+ return -ENODEV;
+ }
+ writel(0x00000001,reg);
+ iounmap(reg);
+
+ /* setup MPCS: */
+ offset = base + CYCLONE_MPCS_OFFSET;
+ reg = ioremap_nocache(offset, sizeof(reg));
+ if (!reg) {
+ printk(KERN_ERR "Summit chipset: Could not find valid MPCS register.\n");
+ return -ENODEV;
+ }
+ writel(0x00000001,reg);
+ iounmap(reg);
+
+ /* map in cyclone_timer: */
+ offset = base + CYCLONE_MPMC_OFFSET;
+ cyclone_timer = ioremap_nocache(offset, sizeof(u64));
+ if (!cyclone_timer) {
+ printk(KERN_ERR "Summit chipset: Could not find valid MPMC register.\n");
+ return -ENODEV;
+ }
+
+ /* quick test to make sure its ticking: */
+ for (i = 0; i < 3; i++){
+ u32 old = readl(cyclone_timer);
+ int stall = 100;
+
+ while (stall--)
+ barrier();
+
+ if (readl(cyclone_timer) == old) {
+ printk(KERN_ERR "Summit chipset: Counter not counting! DISABLED\n");
+ iounmap(cyclone_timer);
+ cyclone_timer = NULL;
+ return -ENODEV;
+ }
+ }
+ cyclone_ptr = cyclone_timer;
+
+ /* sort out mult/shift values: */
+ clocksource_cyclone.shift = 22;
+ clocksource_cyclone.mult = clocksource_hz2mult(CYCLONE_TIMER_FREQ,
+ clocksource_cyclone.shift);
+
+ return clocksource_register(&clocksource_cyclone);
+}
+
+module_init(init_cyclone_clocksource);
diff --git a/drivers/clocksource/scx200_hrt.c b/drivers/clocksource/scx200_hrt.c
new file mode 100644
index 000000000000..d418b8297211
--- /dev/null
+++ b/drivers/clocksource/scx200_hrt.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2006 Jim Cromie
+ *
+ * This is a clocksource driver for the Geode SCx200's 1 or 27 MHz
+ * high-resolution timer. The Geode SC-1100 (at least) has a buggy
+ * time stamp counter (TSC), which loses time unless 'idle=poll' is
+ * given as a boot-arg. In its absence, the Generic Timekeeping code
+ * will detect and de-rate the bad TSC, allowing this timer to take
+ * over timekeeping duties.
+ *
+ * Based on work by John Stultz, and Ted Phelps (in a 2.6.12-rc6 patch)
+ *
+ * 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 <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/ioport.h>
+#include <linux/scx200.h>
+
+#define NAME "scx200_hrt"
+
+static int mhz27;
+module_param(mhz27, int, 0); /* load time only */
+MODULE_PARM_DESC(mhz27, "count at 27.0 MHz (default is 1.0 MHz)");
+
+static int ppm;
+module_param(ppm, int, 0); /* load time only */
+MODULE_PARM_DESC(ppm, "+-adjust to actual XO freq (ppm)");
+
+/* HiRes Timer configuration register address */
+#define SCx200_TMCNFG_OFFSET (SCx200_TIMER_OFFSET + 5)
+
+/* and config settings */
+#define HR_TMEN (1 << 0) /* timer interrupt enable */
+#define HR_TMCLKSEL (1 << 1) /* 1|0 counts at 27|1 MHz */
+#define HR_TM27MPD (1 << 2) /* 1 turns off input clock (power-down) */
+
+/* The base timer frequency, * 27 if selected */
+#define HRT_FREQ 1000000
+
+static cycle_t read_hrt(void)
+{
+ /* Read the timer value */
+ return (cycle_t) inl(scx200_cb_base + SCx200_TIMER_OFFSET);
+}
+
+#define HRT_SHIFT_1 22
+#define HRT_SHIFT_27 26
+
+static struct clocksource cs_hrt = {
+ .name = "scx200_hrt",
+ .rating = 250,
+ .read = read_hrt,
+ .mask = CLOCKSOURCE_MASK(32),
+ .is_continuous = 1,
+ /* mult, shift are set based on mhz27 flag */
+};
+
+static int __init init_hrt_clocksource(void)
+{
+ /* Make sure scx200 has initializedd the configuration block */
+ if (!scx200_cb_present())
+ return -ENODEV;
+
+ /* Reserve the timer's ISA io-region for ourselves */
+ if (!request_region(scx200_cb_base + SCx200_TIMER_OFFSET,
+ SCx200_TIMER_SIZE,
+ "NatSemi SCx200 High-Resolution Timer")) {
+ printk(KERN_WARNING NAME ": unable to lock timer region\n");
+ return -ENODEV;
+ }
+
+ /* write timer config */
+ outb(HR_TMEN | (mhz27) ? HR_TMCLKSEL : 0,
+ scx200_cb_base + SCx200_TMCNFG_OFFSET);
+
+ if (mhz27) {
+ cs_hrt.shift = HRT_SHIFT_27;
+ cs_hrt.mult = clocksource_hz2mult((HRT_FREQ + ppm) * 27,
+ cs_hrt.shift);
+ } else {
+ cs_hrt.shift = HRT_SHIFT_1;
+ cs_hrt.mult = clocksource_hz2mult(HRT_FREQ + ppm,
+ cs_hrt.shift);
+ }
+ printk(KERN_INFO "enabling scx200 high-res timer (%s MHz +%d ppm)\n",
+ mhz27 ? "27":"1", ppm);
+
+ return clocksource_register(&cs_hrt);
+}
+
+module_init(init_hrt_clocksource);
+
+MODULE_AUTHOR("Jim Cromie <jim.cromie@gmail.com>");
+MODULE_DESCRIPTION("clocksource on SCx200 HiRes Timer");
+MODULE_LICENSE("GPL");
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index 44d1eca83a72..35e0b9ceecf7 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -1497,6 +1497,7 @@ int cpufreq_update_policy(unsigned int cpu)
}
EXPORT_SYMBOL(cpufreq_update_policy);
+#ifdef CONFIG_HOTPLUG_CPU
static int cpufreq_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
@@ -1532,10 +1533,11 @@ static int cpufreq_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-static struct notifier_block cpufreq_cpu_notifier =
+static struct notifier_block __cpuinitdata cpufreq_cpu_notifier =
{
.notifier_call = cpufreq_cpu_callback,
};
+#endif /* CONFIG_HOTPLUG_CPU */
/*********************************************************************
* REGISTER / UNREGISTER CPUFREQ DRIVER *
@@ -1596,7 +1598,7 @@ int cpufreq_register_driver(struct cpufreq_driver *driver_data)
}
if (!ret) {
- register_cpu_notifier(&cpufreq_cpu_notifier);
+ register_hotcpu_notifier(&cpufreq_cpu_notifier);
dprintk("driver %s up and running\n", driver_data->name);
cpufreq_debug_enable_ratelimit();
}
@@ -1628,7 +1630,7 @@ int cpufreq_unregister_driver(struct cpufreq_driver *driver)
dprintk("unregistering driver %s\n", driver->name);
sysdev_driver_unregister(&cpu_sysdev_class, &cpufreq_sysdev_driver);
- unregister_cpu_notifier(&cpufreq_cpu_notifier);
+ unregister_hotcpu_notifier(&cpufreq_cpu_notifier);
spin_lock_irqsave(&cpufreq_driver_lock, flags);
cpufreq_driver = NULL;
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index c576c0b3f452..145061b8472a 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -350,7 +350,7 @@ __init cpufreq_stats_init(void)
return ret;
}
- register_cpu_notifier(&cpufreq_stat_cpu_notifier);
+ register_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
lock_cpu_hotplug();
for_each_online_cpu(cpu) {
cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_ONLINE,
@@ -368,7 +368,7 @@ __exit cpufreq_stats_exit(void)
CPUFREQ_POLICY_NOTIFIER);
cpufreq_unregister_notifier(&notifier_trans_block,
CPUFREQ_TRANSITION_NOTIFIER);
- unregister_cpu_notifier(&cpufreq_stat_cpu_notifier);
+ unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
lock_cpu_hotplug();
for_each_online_cpu(cpu) {
cpufreq_stat_cpu_callback(&cpufreq_stat_cpu_notifier, CPU_DEAD,
diff --git a/drivers/crypto/padlock-aes.c b/drivers/crypto/padlock-aes.c
index 5158a9db4bc5..17ee684144f9 100644
--- a/drivers/crypto/padlock-aes.c
+++ b/drivers/crypto/padlock-aes.c
@@ -60,15 +60,14 @@
#define AES_EXTENDED_KEY_SIZE_B (AES_EXTENDED_KEY_SIZE * sizeof(uint32_t))
struct aes_ctx {
- uint32_t e_data[AES_EXTENDED_KEY_SIZE];
- uint32_t d_data[AES_EXTENDED_KEY_SIZE];
struct {
struct cword encrypt;
struct cword decrypt;
} cword;
- uint32_t *E;
- uint32_t *D;
+ u32 *D;
int key_length;
+ u32 E[AES_EXTENDED_KEY_SIZE];
+ u32 d_data[AES_EXTENDED_KEY_SIZE];
};
/* ====== Key management routines ====== */
@@ -282,19 +281,20 @@ aes_hw_extkey_available(uint8_t key_len)
return 0;
}
-static inline struct aes_ctx *aes_ctx(void *ctx)
+static inline struct aes_ctx *aes_ctx(struct crypto_tfm *tfm)
{
+ unsigned long addr = (unsigned long)crypto_tfm_ctx(tfm);
unsigned long align = PADLOCK_ALIGNMENT;
if (align <= crypto_tfm_ctx_alignment())
align = 1;
- return (struct aes_ctx *)ALIGN((unsigned long)ctx, align);
+ return (struct aes_ctx *)ALIGN(addr, align);
}
-static int
-aes_set_key(void *ctx_arg, const uint8_t *in_key, unsigned int key_len, uint32_t *flags)
+static int aes_set_key(struct crypto_tfm *tfm, const u8 *in_key,
+ unsigned int key_len, u32 *flags)
{
- struct aes_ctx *ctx = aes_ctx(ctx_arg);
+ struct aes_ctx *ctx = aes_ctx(tfm);
const __le32 *key = (const __le32 *)in_key;
uint32_t i, t, u, v, w;
uint32_t P[AES_EXTENDED_KEY_SIZE];
@@ -312,8 +312,7 @@ aes_set_key(void *ctx_arg, const uint8_t *in_key, unsigned int key_len, uint32_t
* itself we must supply the plain key for both encryption
* and decryption.
*/
- ctx->E = ctx->e_data;
- ctx->D = ctx->e_data;
+ ctx->D = ctx->E;
E_KEY[0] = le32_to_cpu(key[0]);
E_KEY[1] = le32_to_cpu(key[1]);
@@ -414,24 +413,22 @@ static inline u8 *padlock_xcrypt_cbc(const u8 *input, u8 *output, void *key,
return iv;
}
-static void
-aes_encrypt(void *ctx_arg, uint8_t *out, const uint8_t *in)
+static void aes_encrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
- struct aes_ctx *ctx = aes_ctx(ctx_arg);
+ struct aes_ctx *ctx = aes_ctx(tfm);
padlock_xcrypt_ecb(in, out, ctx->E, &ctx->cword.encrypt, 1);
}
-static void
-aes_decrypt(void *ctx_arg, uint8_t *out, const uint8_t *in)
+static void aes_decrypt(struct crypto_tfm *tfm, u8 *out, const u8 *in)
{
- struct aes_ctx *ctx = aes_ctx(ctx_arg);
+ struct aes_ctx *ctx = aes_ctx(tfm);
padlock_xcrypt_ecb(in, out, ctx->D, &ctx->cword.decrypt, 1);
}
static unsigned int aes_encrypt_ecb(const struct cipher_desc *desc, u8 *out,
const u8 *in, unsigned int nbytes)
{
- struct aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(desc->tfm));
+ struct aes_ctx *ctx = aes_ctx(desc->tfm);
padlock_xcrypt_ecb(in, out, ctx->E, &ctx->cword.encrypt,
nbytes / AES_BLOCK_SIZE);
return nbytes & ~(AES_BLOCK_SIZE - 1);
@@ -440,7 +437,7 @@ static unsigned int aes_encrypt_ecb(const struct cipher_desc *desc, u8 *out,
static unsigned int aes_decrypt_ecb(const struct cipher_desc *desc, u8 *out,
const u8 *in, unsigned int nbytes)
{
- struct aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(desc->tfm));
+ struct aes_ctx *ctx = aes_ctx(desc->tfm);
padlock_xcrypt_ecb(in, out, ctx->D, &ctx->cword.decrypt,
nbytes / AES_BLOCK_SIZE);
return nbytes & ~(AES_BLOCK_SIZE - 1);
@@ -449,7 +446,7 @@ static unsigned int aes_decrypt_ecb(const struct cipher_desc *desc, u8 *out,
static unsigned int aes_encrypt_cbc(const struct cipher_desc *desc, u8 *out,
const u8 *in, unsigned int nbytes)
{
- struct aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(desc->tfm));
+ struct aes_ctx *ctx = aes_ctx(desc->tfm);
u8 *iv;
iv = padlock_xcrypt_cbc(in, out, ctx->E, desc->info,
@@ -462,7 +459,7 @@ static unsigned int aes_encrypt_cbc(const struct cipher_desc *desc, u8 *out,
static unsigned int aes_decrypt_cbc(const struct cipher_desc *desc, u8 *out,
const u8 *in, unsigned int nbytes)
{
- struct aes_ctx *ctx = aes_ctx(crypto_tfm_ctx(desc->tfm));
+ struct aes_ctx *ctx = aes_ctx(desc->tfm);
padlock_xcrypt_cbc(in, out, ctx->D, desc->info, &ctx->cword.decrypt,
nbytes / AES_BLOCK_SIZE);
return nbytes & ~(AES_BLOCK_SIZE - 1);
diff --git a/drivers/dma/ioatdma.c b/drivers/dma/ioatdma.c
index 0fdf7fbd6495..2801d14a5e42 100644
--- a/drivers/dma/ioatdma.c
+++ b/drivers/dma/ioatdma.c
@@ -824,10 +824,9 @@ static int __init ioat_init_module(void)
{
/* it's currently unsafe to unload this module */
/* if forced, worst case is that rmmod hangs */
- if (THIS_MODULE != NULL)
- THIS_MODULE->unsafe = 1;
+ __unsafe(THIS_MODULE);
- return pci_module_init(&ioat_pci_drv);
+ pci_module_init(&ioat_pci_drv);
}
module_init(ioat_init_module);
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index 65b2709f750c..facc1ccb8338 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -341,7 +341,7 @@ static ssize_t set_fan_min(struct device *dev, const char *buf,
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index 94be3d797e61..a6ce7abf8602 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -358,7 +358,7 @@ static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index f72120d08c4c..b4ccdfc01203 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -253,7 +253,7 @@ set_fan(min2, fan_min[1], LM80_REG_FAN_MIN(2), fan_div[1]);
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index e229daf666de..e6c1b638c971 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -421,7 +421,7 @@ static void set_fan_min(struct device *dev, const char *buf, int nr)
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
- of least suprise; the user doesn't expect the fan minimum to change just
+ of least surprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index 6f3fda73f70c..063f71c5f07e 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -380,7 +380,7 @@ static ssize_t show_fan_div(struct device *dev, char *buf, int nr)
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
index 7732aec54594..825e8f72698f 100644
--- a/drivers/hwmon/smsc47m1.c
+++ b/drivers/hwmon/smsc47m1.c
@@ -207,7 +207,7 @@ static ssize_t set_fan_min(struct device *dev, const char *buf,
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan clock divider. This follows the principle
- of least suprise; the user doesn't expect the fan minimum to change just
+ of least surprise; the user doesn't expect the fan minimum to change just
because the divider changed. */
static ssize_t set_fan_div(struct device *dev, const char *buf,
size_t count, int nr)
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index 71fb7f1af8f5..79368d53c363 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -781,7 +781,7 @@ show_fan_div_reg(struct device *dev, char *buf, int nr)
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t
store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr)
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index e4c700356c44..7be469ed0f8f 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -630,7 +630,7 @@ show_fan_div_reg(struct device *dev, char *buf, int nr)
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t
store_fan_div_reg(struct device *dev, const char *buf, size_t count, int nr)
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 4ef884c216e2..e407c74bda35 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -463,7 +463,7 @@ show_fan_div(struct device *dev, struct device_attribute *attr,
/* Note: we save and restore the fan minimum here, because its value is
determined in part by the fan divisor. This follows the principle of
- least suprise; the user doesn't expect the fan minimum to change just
+ least surprise; the user doesn't expect the fan minimum to change just
because the divisor changed. */
static ssize_t
store_fan_div(struct device *dev, struct device_attribute *attr,
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 3e0d04d5a800..8b46ef7d9ff8 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -488,7 +488,7 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
dev_err(&dev->dev, "SMBus base address uninitialized, "
"upgrade BIOS\n");
err = -ENODEV;
- goto exit_disable;
+ goto exit;
}
err = pci_request_region(dev, SMBBAR, i801_driver.name);
@@ -496,7 +496,7 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
dev_err(&dev->dev, "Failed to request SMBus region "
"0x%lx-0x%lx\n", i801_smba,
pci_resource_end(dev, SMBBAR));
- goto exit_disable;
+ goto exit;
}
pci_read_config_byte(I801_dev, SMBHSTCFG, &temp);
@@ -520,11 +520,12 @@ static int __devinit i801_probe(struct pci_dev *dev, const struct pci_device_id
err = i2c_add_adapter(&i801_adapter);
if (err) {
dev_err(&dev->dev, "Failed to add SMBus adapter\n");
- goto exit_disable;
+ goto exit_release;
}
+ return 0;
-exit_disable:
- pci_disable_device(dev);
+exit_release:
+ pci_release_region(dev, SMBBAR);
exit:
return err;
}
@@ -533,7 +534,10 @@ static void __devexit i801_remove(struct pci_dev *dev)
{
i2c_del_adapter(&i801_adapter);
pci_release_region(dev, SMBBAR);
- pci_disable_device(dev);
+ /*
+ * do not call pci_disable_device(dev) since it can cause hard hangs on
+ * some systems during power-off (eg. Fujitsu-Siemens Lifebook E8010)
+ */
}
static struct pci_driver i801_driver = {
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index a5017de72da5..f033d732f387 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -37,7 +37,7 @@
* Version 1.15 convert all calls to ide_raw_taskfile
* since args will return register content.
* Version 1.16 added suspend-resume-checkpower
- * Version 1.17 do flush on standy, do flush on ATA < ATA6
+ * Version 1.17 do flush on standby, do flush on ATA < ATA6
* fix wcache setup.
*/
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index 622a55c72f03..26ceab1e90bb 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -505,7 +505,7 @@ static ide_startstop_t ide_ata_error(ide_drive_t *drive, struct request *rq, u8
}
}
- if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ)
+ if ((stat & DRQ_STAT) && rq_data_dir(rq) == READ && hwif->err_stops_fifo == 0)
try_to_flush_leftover_data(drive);
if (hwif->INB(IDE_STATUS_REG) & (BUSY_STAT|DRQ_STAT))
@@ -959,7 +959,7 @@ static void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
printk(KERN_WARNING "%s: bus not ready on wakeup\n", drive->name);
SELECT_DRIVE(drive);
HWIF(drive)->OUTB(8, HWIF(drive)->io_ports[IDE_CONTROL_OFFSET]);
- rc = ide_wait_not_busy(HWIF(drive), 10000);
+ rc = ide_wait_not_busy(HWIF(drive), 100000);
if (rc)
printk(KERN_WARNING "%s: drive not ready on wakeup\n", drive->name);
}
@@ -1665,7 +1665,7 @@ irqreturn_t ide_intr (int irq, void *dev_id, struct pt_regs *regs)
* Initialize a request before we fill it in and send it down to
* ide_do_drive_cmd. Commands must be set up by this function. Right
* now it doesn't do a lot, but if that changes abusers will have a
- * nasty suprise.
+ * nasty surprise.
*/
void ide_init_drive_cmd (struct request *rq)
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 97a49e77a8f1..32117f0ec5c0 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -597,6 +597,10 @@ u8 eighty_ninty_three (ide_drive_t *drive)
{
if(HWIF(drive)->udma_four == 0)
return 0;
+
+ /* Check for SATA but only if we are ATA5 or higher */
+ if (drive->id->hw_config == 0 && (drive->id->major_rev_num & 0x7FE0))
+ return 1;
if (!(drive->id->hw_config & 0x6000))
return 0;
#ifndef CONFIG_IDEDMA_IVB
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 16a143133f93..7ddb11828731 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -485,7 +485,7 @@ static u8 ide_dump_ata_status(ide_drive_t *drive, const char *msg, u8 stat)
unsigned long flags;
u8 err = 0;
- local_irq_set(flags);
+ local_irq_save(flags);
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
if (stat & BUSY_STAT)
printk("Busy ");
@@ -567,7 +567,7 @@ static u8 ide_dump_atapi_status(ide_drive_t *drive, const char *msg, u8 stat)
status.all = stat;
error.all = 0;
- local_irq_set(flags);
+ local_irq_save(flags);
printk("%s: %s: status=0x%02x { ", drive->name, msg, stat);
if (status.b.bsy)
printk("Busy ");
diff --git a/drivers/ide/ide-timing.h b/drivers/ide/ide-timing.h
index 2fcfac6e967a..c0864b1e9228 100644
--- a/drivers/ide/ide-timing.h
+++ b/drivers/ide/ide-timing.h
@@ -220,6 +220,12 @@ static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing
return -EINVAL;
/*
+ * Copy the timing from the table.
+ */
+
+ *t = *s;
+
+/*
* If the drive is an EIDE drive, it can tell us it needs extended
* PIO/MWDMA cycle timing.
*/
@@ -247,7 +253,7 @@ static int ide_timing_compute(ide_drive_t *drive, short speed, struct ide_timing
* Convert the timing to bus clock counts.
*/
- ide_timing_quantize(s, t, T, UT);
+ ide_timing_quantize(t, t, T, UT);
/*
* Even in DMA/UDMA modes we still use PIO access for IDENTIFY, S.M.A.R.T
diff --git a/drivers/ide/pci/aec62xx.c b/drivers/ide/pci/aec62xx.c
index c743e68c33aa..3edd7060510f 100644
--- a/drivers/ide/pci/aec62xx.c
+++ b/drivers/ide/pci/aec62xx.c
@@ -22,7 +22,7 @@ struct chipset_bus_clock_list_entry {
u8 ultra_settings;
};
-static struct chipset_bus_clock_list_entry aec6xxx_33_base [] = {
+static const struct chipset_bus_clock_list_entry aec6xxx_33_base [] = {
{ XFER_UDMA_6, 0x31, 0x07 },
{ XFER_UDMA_5, 0x31, 0x06 },
{ XFER_UDMA_4, 0x31, 0x05 },
@@ -42,7 +42,7 @@ static struct chipset_bus_clock_list_entry aec6xxx_33_base [] = {
{ 0, 0x00, 0x00 }
};
-static struct chipset_bus_clock_list_entry aec6xxx_34_base [] = {
+static const struct chipset_bus_clock_list_entry aec6xxx_34_base [] = {
{ XFER_UDMA_6, 0x41, 0x06 },
{ XFER_UDMA_5, 0x41, 0x05 },
{ XFER_UDMA_4, 0x41, 0x04 },
@@ -254,7 +254,8 @@ static unsigned int __devinit init_chipset_aec62xx(struct pci_dev *dev, const ch
if (dev->resource[PCI_ROM_RESOURCE].start) {
pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
- printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
+ printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
+ (unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
}
if (bus_speed <= 33)
@@ -425,12 +426,12 @@ static int __devinit aec62xx_init_one(struct pci_dev *dev, const struct pci_devi
return d->init_setup(dev, d);
}
-static struct pci_device_id aec62xx_pci_tbl[] = {
- { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
- { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
- { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
- { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3 },
- { PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4 },
+static const struct pci_device_id aec62xx_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP850UF), 0 },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860), 1 },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP860R), 2 },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865), 3 },
+ { PCI_DEVICE(PCI_VENDOR_ID_ARTOP, PCI_DEVICE_ID_ARTOP_ATP865R), 4 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, aec62xx_pci_tbl);
diff --git a/drivers/ide/pci/amd74xx.c b/drivers/ide/pci/amd74xx.c
index 6e9dbf4d8077..85007cb12c52 100644
--- a/drivers/ide/pci/amd74xx.c
+++ b/drivers/ide/pci/amd74xx.c
@@ -75,6 +75,7 @@ static struct amd_ide_chip {
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, 0x50, AMD_UDMA_133 },
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, 0x50, AMD_UDMA_133 },
{ PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, 0x50, AMD_UDMA_133 },
+ { PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, 0x50, AMD_UDMA_133 },
{ PCI_DEVICE_ID_AMD_CS5536_IDE, 0x40, AMD_UDMA_100 },
{ 0 }
};
@@ -490,7 +491,8 @@ static ide_pci_device_t amd74xx_chipsets[] __devinitdata = {
/* 15 */ DECLARE_NV_DEV("NFORCE-MCP51"),
/* 16 */ DECLARE_NV_DEV("NFORCE-MCP55"),
/* 17 */ DECLARE_NV_DEV("NFORCE-MCP61"),
- /* 18 */ DECLARE_AMD_DEV("AMD5536"),
+ /* 18 */ DECLARE_NV_DEV("NFORCE-MCP65"),
+ /* 19 */ DECLARE_AMD_DEV("AMD5536"),
};
static int __devinit amd74xx_probe(struct pci_dev *dev, const struct pci_device_id *id)
@@ -528,7 +530,8 @@ static struct pci_device_id amd74xx_pci_tbl[] = {
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP51_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 15 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP55_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 16 },
{ PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 17 },
- { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18 },
+ { PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NFORCE_MCP65_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 18 },
+ { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CS5536_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 19 },
{ 0, },
};
MODULE_DEVICE_TABLE(pci, amd74xx_pci_tbl);
diff --git a/drivers/ide/pci/cmd64x.c b/drivers/ide/pci/cmd64x.c
index 3d9c7afc8695..92b7b1549b16 100644
--- a/drivers/ide/pci/cmd64x.c
+++ b/drivers/ide/pci/cmd64x.c
@@ -190,14 +190,6 @@ static int cmd64x_get_info (char *buffer, char **addr, off_t offset, int count)
#endif /* defined(DISPLAY_CMD64X_TIMINGS) && defined(CONFIG_PROC_FS) */
/*
- * Registers and masks for easy access by drive index:
- */
-#if 0
-static u8 prefetch_regs[4] = {CNTRL, CNTRL, ARTTIM23, ARTTIM23};
-static u8 prefetch_masks[4] = {CNTRL_DIS_RA0, CNTRL_DIS_RA1, ARTTIM23_DIS_RA2, ARTTIM23_DIS_RA3};
-#endif
-
-/*
* This routine writes the prepared setup/active/recovery counts
* for a drive into the cmd646 chipset registers to active them.
*/
@@ -606,13 +598,6 @@ static unsigned int __devinit init_chipset_cmd64x(struct pci_dev *dev, const cha
pci_read_config_dword(dev, PCI_CLASS_REVISION, &class_rev);
class_rev &= 0xff;
-#ifdef __i386__
- if (dev->resource[PCI_ROM_RESOURCE].start) {
- pci_write_config_dword(dev, PCI_ROM_ADDRESS, dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
- printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name, dev->resource[PCI_ROM_RESOURCE].start);
- }
-#endif
-
switch(dev->device) {
case PCI_DEVICE_ID_CMD_643:
break;
diff --git a/drivers/ide/pci/hpt34x.c b/drivers/ide/pci/hpt34x.c
index be334da7a754..7da550281cf2 100644
--- a/drivers/ide/pci/hpt34x.c
+++ b/drivers/ide/pci/hpt34x.c
@@ -176,7 +176,7 @@ static unsigned int __devinit init_chipset_hpt34x(struct pci_dev *dev, const cha
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
printk(KERN_INFO "HPT345: ROM enabled at 0x%08lx\n",
- dev->resource[PCI_ROM_RESOURCE].start);
+ (unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
}
pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0xF0);
} else {
diff --git a/drivers/ide/pci/pdc202xx_new.c b/drivers/ide/pci/pdc202xx_new.c
index acd63173199b..5a8334d134fb 100644
--- a/drivers/ide/pci/pdc202xx_new.c
+++ b/drivers/ide/pci/pdc202xx_new.c
@@ -313,8 +313,8 @@ static unsigned int __devinit init_chipset_pdcnew(struct pci_dev *dev, const cha
if (dev->resource[PCI_ROM_RESOURCE].start) {
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
- printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n",
- name, dev->resource[PCI_ROM_RESOURCE].start);
+ printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
+ (unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
}
#ifdef CONFIG_PPC_PMAC
@@ -338,6 +338,8 @@ static void __devinit init_hwif_pdc202new(ide_hwif_t *hwif)
hwif->ultra_mask = 0x7f;
hwif->mwdma_mask = 0x07;
+ hwif->err_stops_fifo = 1;
+
hwif->ide_dma_check = &pdcnew_config_drive_xfer_rate;
hwif->ide_dma_lostirq = &pdcnew_ide_dma_lostirq;
hwif->ide_dma_timeout = &pdcnew_ide_dma_timeout;
diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c
index 7ce5bf783688..1e209d8f9437 100644
--- a/drivers/ide/pci/pdc202xx_old.c
+++ b/drivers/ide/pci/pdc202xx_old.c
@@ -101,31 +101,6 @@ static const char *pdc_quirk_drives[] = {
#define MC1 0x02 /* DMA"C" timing */
#define MC0 0x01 /* DMA"C" timing */
-#if 0
- unsigned long bibma = pci_resource_start(dev, 4);
- u8 hi = 0, lo = 0;
-
- u8 sc1c = inb_p((u16)bibma + 0x1c);
- u8 sc1e = inb_p((u16)bibma + 0x1e);
- u8 sc1f = inb_p((u16)bibma + 0x1f);
-
- p += sprintf(p, "Host Mode : %s\n",
- (sc1f & 0x08) ? "Tri-Stated" : "Normal");
- p += sprintf(p, "Bus Clocking : %s\n",
- ((sc1f & 0xC0) == 0xC0) ? "100 External" :
- ((sc1f & 0x80) == 0x80) ? "66 External" :
- ((sc1f & 0x40) == 0x40) ? "33 External" : "33 PCI Internal");
- p += sprintf(p, "IO pad select : %s mA\n",
- ((sc1c & 0x03) == 0x03) ? "10" :
- ((sc1c & 0x02) == 0x02) ? "8" :
- ((sc1c & 0x01) == 0x01) ? "6" :
- ((sc1c & 0x00) == 0x00) ? "4" : "??");
- hi = sc1e >> 4;
- lo = sc1e & 0xf;
- p += sprintf(p, "Status Polling Period : %d\n", hi);
- p += sprintf(p, "Interrupt Check Status Polling Delay : %d\n", lo);
-#endif
-
static u8 pdc202xx_ratemask (ide_drive_t *drive)
{
u8 mode;
@@ -370,7 +345,6 @@ chipset_is_set:
if (!(speed)) {
/* restore original pci-config space */
pci_write_config_dword(dev, drive_pci, drive_conf);
- hwif->tuneproc(drive, 5);
return 0;
}
@@ -415,8 +389,6 @@ static void pdc202xx_old_ide_dma_start(ide_drive_t *drive)
if (drive->addressing == 1) {
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
-// struct pci_dev *dev = hwif->pci_dev;
-// unsgned long high_16 = pci_resource_start(dev, 4);
unsigned long high_16 = hwif->dma_master;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
u32 word_count = 0;
@@ -436,7 +408,6 @@ static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
{
if (drive->addressing == 1) {
ide_hwif_t *hwif = HWIF(drive);
-// unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4);
unsigned long high_16 = hwif->dma_master;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
u8 clock = 0;
@@ -453,8 +424,6 @@ static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
static int pdc202xx_old_ide_dma_test_irq(ide_drive_t *drive)
{
ide_hwif_t *hwif = HWIF(drive);
-// struct pci_dev *dev = hwif->pci_dev;
-// unsigned long high_16 = pci_resource_start(dev, 4);
unsigned long high_16 = hwif->dma_master;
u8 dma_stat = hwif->INB(hwif->dma_status);
u8 sc1d = hwif->INB((high_16 + 0x001d));
@@ -492,12 +461,7 @@ static int pdc202xx_ide_dma_timeout(ide_drive_t *drive)
static void pdc202xx_reset_host (ide_hwif_t *hwif)
{
-#ifdef CONFIG_BLK_DEV_IDEDMA
-// unsigned long high_16 = hwif->dma_base - (8*(hwif->channel));
unsigned long high_16 = hwif->dma_master;
-#else /* !CONFIG_BLK_DEV_IDEDMA */
- unsigned long high_16 = pci_resource_start(hwif->pci_dev, 4);
-#endif /* CONFIG_BLK_DEV_IDEDMA */
u8 udma_speed_flag = hwif->INB(high_16|0x001f);
hwif->OUTB((udma_speed_flag | 0x10), (high_16|0x001f));
@@ -516,98 +480,20 @@ static void pdc202xx_reset (ide_drive_t *drive)
pdc202xx_reset_host(hwif);
pdc202xx_reset_host(mate);
-#if 0
- /*
- * FIXME: Have to kick all the drives again :-/
- * What a pain in the ACE!
- */
- if (hwif->present) {
- u16 hunit = 0;
- for (hunit = 0; hunit < MAX_DRIVES; ++hunit) {
- ide_drive_t *hdrive = &hwif->drives[hunit];
- if (hdrive->present) {
- if (hwif->ide_dma_check)
- hwif->ide_dma_check(hdrive);
- else
- hwif->tuneproc(hdrive, 5);
- }
- }
- }
- if (mate->present) {
- u16 munit = 0;
- for (munit = 0; munit < MAX_DRIVES; ++munit) {
- ide_drive_t *mdrive = &mate->drives[munit];
- if (mdrive->present) {
- if (mate->ide_dma_check)
- mate->ide_dma_check(mdrive);
- else
- mate->tuneproc(mdrive, 5);
- }
- }
- }
-#else
hwif->tuneproc(drive, 5);
-#endif
-}
-
-/*
- * Since SUN Cobalt is attempting to do this operation, I should disclose
- * this has been a long time ago Thu Jul 27 16:40:57 2000 was the patch date
- * HOTSWAP ATA Infrastructure.
- */
-static int pdc202xx_tristate (ide_drive_t * drive, int state)
-{
- ide_hwif_t *hwif = HWIF(drive);
-// unsigned long high_16 = hwif->dma_base - (8*(hwif->channel));
- unsigned long high_16 = hwif->dma_master;
- u8 sc1f = hwif->INB(high_16|0x001f);
-
- if (!hwif)
- return -EINVAL;
-
-// hwif->bus_state = state;
-
- if (state) {
- hwif->OUTB(sc1f | 0x08, (high_16|0x001f));
- } else {
- hwif->OUTB(sc1f & ~0x08, (high_16|0x001f));
- }
- return 0;
}
-static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev, const char *name)
+static unsigned int __devinit init_chipset_pdc202xx(struct pci_dev *dev,
+ const char *name)
{
+ /* This doesn't appear needed */
if (dev->resource[PCI_ROM_RESOURCE].start) {
pci_write_config_dword(dev, PCI_ROM_ADDRESS,
dev->resource[PCI_ROM_RESOURCE].start | PCI_ROM_ADDRESS_ENABLE);
- printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n",
- name, dev->resource[PCI_ROM_RESOURCE].start);
+ printk(KERN_INFO "%s: ROM enabled at 0x%08lx\n", name,
+ (unsigned long)dev->resource[PCI_ROM_RESOURCE].start);
}
- /*
- * software reset - this is required because the bios
- * will set UDMA timing on if the hdd supports it. The
- * user may want to turn udma off. A bug in the pdc20262
- * is that it cannot handle a downgrade in timing from
- * UDMA to DMA. Disk accesses after issuing a set
- * feature command will result in errors. A software
- * reset leaves the timing registers intact,
- * but resets the drives.
- */
-#if 0
- if ((dev->device == PCI_DEVICE_ID_PROMISE_20267) ||
- (dev->device == PCI_DEVICE_ID_PROMISE_20265) ||
- (dev->device == PCI_DEVICE_ID_PROMISE_20263) ||
- (dev->device == PCI_DEVICE_ID_PROMISE_20262)) {
- unsigned long high_16 = pci_resource_start(dev, 4);
- byte udma_speed_flag = inb(high_16 + 0x001f);
- outb(udma_speed_flag | 0x10, high_16 + 0x001f);
- mdelay(100);
- outb(udma_speed_flag & ~0x10, high_16 + 0x001f);
- mdelay(2000); /* 2 seconds ?! */
- }
-
-#endif
return dev->irq;
}
@@ -624,10 +510,8 @@ static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif)
hwif->tuneproc = &config_chipset_for_pio;
hwif->quirkproc = &pdc202xx_quirkproc;
- if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246) {
- hwif->busproc = &pdc202xx_tristate;
+ if (hwif->pci_dev->device != PCI_DEVICE_ID_PROMISE_20246)
hwif->resetproc = &pdc202xx_reset;
- }
hwif->speedproc = &pdc202xx_tune_chipset;
@@ -637,6 +521,8 @@ static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif)
hwif->mwdma_mask = 0x07;
hwif->swdma_mask = 0x07;
+ hwif->err_stops_fifo = 1;
+
hwif->ide_dma_check = &pdc202xx_config_drive_xfer_rate;
hwif->ide_dma_lostirq = &pdc202xx_ide_dma_lostirq;
hwif->ide_dma_timeout = &pdc202xx_ide_dma_timeout;
@@ -725,19 +611,6 @@ static int __devinit init_setup_pdc202ata4(struct pci_dev *dev,
"mirror fixed.\n", d->name);
}
}
-
-#if 0
- if (dev->device == PCI_DEVICE_ID_PROMISE_20262)
- if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) ||
- (tmp & e->mask) != e->val))
-
- if (d->enablebits[0].reg != d->enablebits[1].reg) {
- d->enablebits[0].reg = d->enablebits[1].reg;
- d->enablebits[0].mask = d->enablebits[1].mask;
- d->enablebits[0].val = d->enablebits[1].val;
- }
-#endif
-
return ide_setup_pci_device(dev, d);
}
@@ -752,22 +625,6 @@ static int __devinit init_setup_pdc20265(struct pci_dev *dev,
"attached to I2O RAID controller.\n");
return -ENODEV;
}
-
-#if 0
- {
- u8 pri = 0, sec = 0;
-
- if (e->reg && (pci_read_config_byte(dev, e->reg, &tmp) ||
- (tmp & e->mask) != e->val))
-
- if (d->enablebits[0].reg != d->enablebits[1].reg) {
- d->enablebits[0].reg = d->enablebits[1].reg;
- d->enablebits[0].mask = d->enablebits[1].mask;
- d->enablebits[0].val = d->enablebits[1].val;
- }
- }
-#endif
-
return ide_setup_pci_device(dev, d);
}
diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
index e9b83e1a3028..7fac6f57b5d6 100644
--- a/drivers/ide/pci/piix.c
+++ b/drivers/ide/pci/piix.c
@@ -222,6 +222,8 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio)
unsigned long flags;
u16 master_data;
u8 slave_data;
+ static DEFINE_SPINLOCK(tune_lock);
+
/* ISP RTC */
u8 timings[][2] = { { 0, 0 },
{ 0, 0 },
@@ -230,7 +232,13 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio)
{ 2, 3 }, };
pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
- spin_lock_irqsave(&ide_lock, flags);
+
+ /*
+ * Master vs slave is synchronized above us but the slave register is
+ * shared by the two hwifs so the corner case of two slave timeouts in
+ * parallel must be locked.
+ */
+ spin_lock_irqsave(&tune_lock, flags);
pci_read_config_word(dev, master_port, &master_data);
if (is_slave) {
master_data = master_data | 0x4000;
@@ -250,7 +258,7 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio)
pci_write_config_word(dev, master_port, master_data);
if (is_slave)
pci_write_config_byte(dev, slave_port, slave_data);
- spin_unlock_irqrestore(&ide_lock, flags);
+ spin_unlock_irqrestore(&tune_lock, flags);
}
/**
diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c
index 24e21b2838c1..778b82ae964d 100644
--- a/drivers/ide/pci/sc1200.c
+++ b/drivers/ide/pci/sc1200.c
@@ -395,7 +395,6 @@ static int sc1200_resume (struct pci_dev *dev)
{
ide_hwif_t *hwif = NULL;
-printk("SC1200: resume\n");
pci_set_power_state(dev, PCI_D0); // bring chip back from sleep state
dev->current_state = PM_EVENT_ON;
pci_enable_device(dev);
@@ -405,7 +404,6 @@ printk("SC1200: resume\n");
while ((hwif = lookup_pci_dev(hwif, dev)) != NULL) {
unsigned int basereg, r, d, format;
sc1200_saved_state_t *ss = (sc1200_saved_state_t *)hwif->config_data;
-printk("%s: SC1200: resume\n", hwif->name);
//
// Restore timing registers: this may be unnecessary if BIOS also does it
@@ -493,7 +491,7 @@ static int __devinit sc1200_init_one(struct pci_dev *dev, const struct pci_devic
}
static struct pci_device_id sc1200_pci_tbl[] = {
- { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_SCx200_IDE), 0},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, sc1200_pci_tbl);
diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c
index 0d3073f4eab4..5100b827a935 100644
--- a/drivers/ide/pci/serverworks.c
+++ b/drivers/ide/pci/serverworks.c
@@ -123,11 +123,11 @@ static u8 svwks_csb_check (struct pci_dev *dev)
}
static int svwks_tune_chipset (ide_drive_t *drive, u8 xferspeed)
{
- u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
- u8 dma_modes[] = { 0x77, 0x21, 0x20 };
- u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
- u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 };
- u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 };
+ static const u8 udma_modes[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05 };
+ static const u8 dma_modes[] = { 0x77, 0x21, 0x20 };
+ static const u8 pio_modes[] = { 0x5d, 0x47, 0x34, 0x22, 0x20 };
+ static const u8 drive_pci[] = { 0x41, 0x40, 0x43, 0x42 };
+ static const u8 drive_pci2[] = { 0x45, 0x44, 0x47, 0x46 };
ide_hwif_t *hwif = HWIF(drive);
struct pci_dev *dev = hwif->pci_dev;
@@ -392,16 +392,6 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
}
outb_p(0x06, 0x0c00);
dev->irq = inb_p(0x0c01);
-#if 0
- printk("%s: device class (0x%04x)\n",
- name, dev->class);
- if ((dev->class >> 8) != PCI_CLASS_STORAGE_IDE) {
- dev->class &= ~0x000F0F00;
- // dev->class |= ~0x00000400;
- dev->class |= ~0x00010100;
- /**/
- }
-#endif
} else {
struct pci_dev * findev = NULL;
u8 reg41 = 0;
@@ -452,7 +442,7 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
pci_write_config_byte(dev, 0x5A, btr);
}
- return (dev->irq) ? dev->irq : 0;
+ return dev->irq;
}
static unsigned int __devinit ata66_svwks_svwks (ide_hwif_t *hwif)
@@ -500,11 +490,6 @@ static unsigned int __devinit ata66_svwks (ide_hwif_t *hwif)
{
struct pci_dev *dev = hwif->pci_dev;
- /* Per Specified Design by OEM, and ASIC Architect */
- if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
- (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2))
- return 1;
-
/* Server Works */
if (dev->subsystem_vendor == PCI_VENDOR_ID_SERVERWORKS)
return ata66_svwks_svwks (hwif);
@@ -517,10 +502,14 @@ static unsigned int __devinit ata66_svwks (ide_hwif_t *hwif)
if (dev->subsystem_vendor == PCI_VENDOR_ID_SUN)
return ata66_svwks_cobalt (hwif);
+ /* Per Specified Design by OEM, and ASIC Architect */
+ if ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE) ||
+ (dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2))
+ return 1;
+
return 0;
}
-#undef CAN_SW_DMA
static void __devinit init_hwif_svwks (ide_hwif_t *hwif)
{
u8 dma_stat = 0;
@@ -537,9 +526,6 @@ static void __devinit init_hwif_svwks (ide_hwif_t *hwif)
hwif->ultra_mask = 0x3f;
hwif->mwdma_mask = 0x07;
-#ifdef CAN_SW_DMA
- hwif->swdma_mask = 0x07;
-#endif /* CAN_SW_DMA */
hwif->autodma = 0;
@@ -562,8 +548,6 @@ static void __devinit init_hwif_svwks (ide_hwif_t *hwif)
hwif->drives[1].autodma = (dma_stat & 0x40);
hwif->drives[0].autotune = (!(dma_stat & 0x20));
hwif->drives[1].autotune = (!(dma_stat & 0x40));
-// hwif->drives[0].autodma = hwif->autodma;
-// hwif->drives[1].autodma = hwif->autodma;
}
/*
@@ -593,11 +577,6 @@ static int __devinit init_setup_csb6 (struct pci_dev *dev, ide_pci_device_t *d)
if (dev->resource[0].start == 0x01f1)
d->bootable = ON_BOARD;
}
-#if 0
- if ((IDE_PCI_DEVID_EQ(d->devid, DEVID_CSB6) &&
- (!(PCI_FUNC(dev->devfn) & 1)))
- d->autodma = AUTODMA;
-#endif
d->channels = ((dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE ||
dev->device == PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2) &&
@@ -671,11 +650,11 @@ static int __devinit svwks_init_one(struct pci_dev *dev, const struct pci_device
}
static struct pci_device_id svwks_pci_tbl[] = {
- { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
- { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
- { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 3},
- { PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4},
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_OSB4IDE), 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB5IDE), 1},
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE), 2},
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2), 3},
+ { PCI_DEVICE(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000IDE), 4},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, svwks_pci_tbl);
diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
index f1ca154dd52c..72dade14c725 100644
--- a/drivers/ide/pci/siimage.c
+++ b/drivers/ide/pci/siimage.c
@@ -38,9 +38,6 @@
#include <asm/io.h>
-#undef SIIMAGE_VIRTUAL_DMAPIO
-#undef SIIMAGE_LARGE_DMA
-
/**
* pdev_is_sata - check if device is SATA
* @pdev: PCI device to check
@@ -461,36 +458,6 @@ static int siimage_io_ide_dma_test_irq (ide_drive_t *drive)
return 0;
}
-#if 0
-/**
- * siimage_mmio_ide_dma_count - DMA bytes done
- * @drive
- *
- * If we are doing VDMA the CMD680 requires a little bit
- * of more careful handling and we have to read the counts
- * off ourselves. For non VDMA life is normal.
- */
-
-static int siimage_mmio_ide_dma_count (ide_drive_t *drive)
-{
-#ifdef SIIMAGE_VIRTUAL_DMAPIO
- struct request *rq = HWGROUP(drive)->rq;
- ide_hwif_t *hwif = HWIF(drive);
- u32 count = (rq->nr_sectors * SECTOR_SIZE);
- u32 rcount = 0;
- unsigned long addr = siimage_selreg(hwif, 0x1C);
-
- hwif->OUTL(count, addr);
- rcount = hwif->INL(addr);
-
- printk("\n%s: count = %d, rcount = %d, nr_sectors = %lu\n",
- drive->name, count, rcount, rq->nr_sectors);
-
-#endif /* SIIMAGE_VIRTUAL_DMAPIO */
- return __ide_dma_count(drive);
-}
-#endif
-
/**
* siimage_mmio_ide_dma_test_irq - check we caused an IRQ
* @drive: drive we are testing
@@ -512,12 +479,10 @@ static int siimage_mmio_ide_dma_test_irq (ide_drive_t *drive)
u32 sata_error = hwif->INL(SATA_ERROR_REG);
hwif->OUTL(sata_error, SATA_ERROR_REG);
watchdog = (sata_error & 0x00680000) ? 1 : 0;
-#if 1
printk(KERN_WARNING "%s: sata_error = 0x%08x, "
"watchdog = %d, %s\n",
drive->name, sata_error, watchdog,
__FUNCTION__);
-#endif
} else {
watchdog = (ext_stat & 0x8000) ? 1 : 0;
@@ -863,7 +828,7 @@ static unsigned int __devinit init_chipset_siimage(struct pci_dev *dev, const ch
* time.
*
* The hardware supports buffered taskfiles and also some rather nice
- * extended PRD tables. Unfortunately right now we don't.
+ * extended PRD tables. For better SI3112 support use the libata driver
*/
static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
@@ -900,9 +865,6 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
* so we can't currently use it sanely since we want to
* use LBA48 mode.
*/
-// base += 0x10;
-// hwif->no_lba48 = 1;
-
hw.io_ports[IDE_DATA_OFFSET] = base;
hw.io_ports[IDE_ERROR_OFFSET] = base + 1;
hw.io_ports[IDE_NSECTOR_OFFSET] = base + 2;
@@ -936,15 +898,8 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
base = (unsigned long) addr;
-#ifdef SIIMAGE_LARGE_DMA
-/* Watch the brackets - even Ken and Dennis get some language design wrong */
- hwif->dma_base = base + (ch ? 0x18 : 0x10);
- hwif->dma_base2 = base + (ch ? 0x08 : 0x00);
- hwif->dma_prdtable = hwif->dma_base2 + 4;
-#else /* ! SIIMAGE_LARGE_DMA */
hwif->dma_base = base + (ch ? 0x08 : 0x00);
hwif->dma_base2 = base + (ch ? 0x18 : 0x10);
-#endif /* SIIMAGE_LARGE_DMA */
hwif->mmio = 2;
}
@@ -1052,9 +1007,16 @@ static void __devinit init_hwif_siimage(ide_hwif_t *hwif)
hwif->reset_poll = &siimage_reset_poll;
hwif->pre_reset = &siimage_pre_reset;
- if(is_sata(hwif))
+ if(is_sata(hwif)) {
+ static int first = 1;
+
hwif->busproc = &siimage_busproc;
+ if (first) {
+ printk(KERN_INFO "siimage: For full SATA support you should use the libata sata_sil module.\n");
+ first = 0;
+ }
+ }
if (!hwif->dma_base) {
hwif->drives[0].autotune = 1;
hwif->drives[1].autotune = 1;
@@ -1121,10 +1083,10 @@ static int __devinit siimage_init_one(struct pci_dev *dev, const struct pci_devi
}
static struct pci_device_id siimage_pci_tbl[] = {
- { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_680, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_680), 0},
#ifdef CONFIG_BLK_DEV_IDE_SATA
- { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_3112, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1},
- { PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_1210SA, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2},
+ { PCI_DEVICE(PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_3112), 1},
+ { PCI_DEVICE(PCI_VENDOR_ID_CMD, PCI_DEVICE_ID_SII_1210SA), 2},
#endif
{ 0, },
};
diff --git a/drivers/ide/pci/sl82c105.c b/drivers/ide/pci/sl82c105.c
index 8a5c7b286b2b..900301e43818 100644
--- a/drivers/ide/pci/sl82c105.c
+++ b/drivers/ide/pci/sl82c105.c
@@ -447,7 +447,6 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif)
printk(" %s: Winbond 553 bridge revision %d, BM-DMA disabled\n",
hwif->name, rev);
} else {
-#ifdef CONFIG_BLK_DEV_IDEDMA
dma_state |= 0x60;
hwif->atapi_dma = 1;
@@ -468,7 +467,6 @@ static void __devinit init_hwif_sl82c105(ide_hwif_t *hwif)
if (hwif->mate)
hwif->serialized = hwif->mate->serialized = 1;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
}
hwif->OUTB(dma_state, hwif->dma_base + 2);
}
@@ -489,7 +487,7 @@ static int __devinit sl82c105_init_one(struct pci_dev *dev, const struct pci_dev
}
static struct pci_device_id sl82c105_pci_tbl[] = {
- { PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_WINBOND, PCI_DEVICE_ID_WINBOND_82C105), 0},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, sl82c105_pci_tbl);
diff --git a/drivers/ide/pci/slc90e66.c b/drivers/ide/pci/slc90e66.c
index 5112c726633b..0968f6bc669a 100644
--- a/drivers/ide/pci/slc90e66.c
+++ b/drivers/ide/pci/slc90e66.c
@@ -72,7 +72,8 @@ static void slc90e66_tune_drive (ide_drive_t *drive, u8 pio)
u16 master_data;
u8 slave_data;
/* ISP RTC */
- u8 timings[][2] = { { 0, 0 },
+ static const u8 timings[][2]= {
+ { 0, 0 },
{ 0, 0 },
{ 1, 0 },
{ 2, 1 },
@@ -119,7 +120,6 @@ static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed)
pci_read_config_word(dev, 0x4a, &reg4a);
switch(speed) {
-#ifdef CONFIG_BLK_DEV_IDEDMA
case XFER_UDMA_4: u_speed = 4 << (drive->dn * 4); break;
case XFER_UDMA_3: u_speed = 3 << (drive->dn * 4); break;
case XFER_UDMA_2: u_speed = 2 << (drive->dn * 4); break;
@@ -128,7 +128,6 @@ static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed)
case XFER_MW_DMA_2:
case XFER_MW_DMA_1:
case XFER_SW_DMA_2: break;
-#endif /* CONFIG_BLK_DEV_IDEDMA */
case XFER_PIO_4:
case XFER_PIO_3:
case XFER_PIO_2:
@@ -156,7 +155,6 @@ static int slc90e66_tune_chipset (ide_drive_t *drive, u8 xferspeed)
return (ide_config_drive_speed(drive, speed));
}
-#ifdef CONFIG_BLK_DEV_IDEDMA
static int slc90e66_config_drive_for_dma (ide_drive_t *drive)
{
u8 speed = ide_dma_speed(drive, slc90e66_ratemask(drive));
@@ -194,7 +192,6 @@ fast_ata_pio:
/* IORDY not supported */
return 0;
}
-#endif /* CONFIG_BLK_DEV_IDEDMA */
static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif)
{
@@ -222,7 +219,6 @@ static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif)
hwif->mwdma_mask = 0x07;
hwif->swdma_mask = 0x07;
-#ifdef CONFIG_BLK_DEV_IDEDMA
if (!(hwif->udma_four))
/* bit[0(1)]: 0:80, 1:40 */
hwif->udma_four = (reg47 & mask) ? 0 : 1;
@@ -232,7 +228,6 @@ static void __devinit init_hwif_slc90e66 (ide_hwif_t *hwif)
hwif->autodma = 1;
hwif->drives[0].autodma = hwif->autodma;
hwif->drives[1].autodma = hwif->autodma;
-#endif /* !CONFIG_BLK_DEV_IDEDMA */
}
static ide_pci_device_t slc90e66_chipset __devinitdata = {
@@ -250,7 +245,7 @@ static int __devinit slc90e66_init_one(struct pci_dev *dev, const struct pci_dev
}
static struct pci_device_id slc90e66_pci_tbl[] = {
- { PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_EFAR, PCI_DEVICE_ID_EFAR_SLC90E66_1), 0},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, slc90e66_pci_tbl);
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
index 5bda15904a08..2d5b57be98c3 100644
--- a/drivers/ieee1394/eth1394.c
+++ b/drivers/ieee1394/eth1394.c
@@ -1074,8 +1074,7 @@ static inline int update_partial_datagram(struct list_head *pdgl, struct list_he
/* Move list entry to beginnig of list so that oldest partial
* datagrams percolate to the end of the list */
- list_del(lh);
- list_add(lh, pdgl);
+ list_move(lh, pdgl);
return 0;
}
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
index 2d47b11777a5..ad49c040b674 100644
--- a/drivers/ieee1394/hosts.c
+++ b/drivers/ieee1394/hosts.c
@@ -103,7 +103,7 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
* driver specific parts, enable the controller and make it available
* to the general subsystem using hpsb_add_host().
*
- * Return Value: a pointer to the &hpsb_host if succesful, %NULL if
+ * Return Value: a pointer to the &hpsb_host if successful, %NULL if
* no memory was available.
*/
static DEFINE_MUTEX(host_num_alloc);
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
index e7b55e895f50..0ecbf335c64f 100644
--- a/drivers/ieee1394/ieee1394_core.h
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -139,7 +139,7 @@ int hpsb_bus_reset(struct hpsb_host *host);
/*
* Hand over received selfid packet to the core. Complement check (second
- * quadlet is complement of first) is expected to be done and succesful.
+ * quadlet is complement of first) is expected to be done and successful.
*/
void hpsb_selfid_received(struct hpsb_host *host, quadlet_t sid);
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index 3d278412e1ca..800c8d518430 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -590,11 +590,11 @@ static void ohci_initialize(struct ti_ohci *ohci)
buf = reg_read(ohci, OHCI1394_Version);
sprintf (irq_buf, "%d", ohci->dev->irq);
PRINT(KERN_INFO, "OHCI-1394 %d.%d (PCI): IRQ=[%s] "
- "MMIO=[%lx-%lx] Max Packet=[%d] IR/IT contexts=[%d/%d]",
+ "MMIO=[%llx-%llx] Max Packet=[%d] IR/IT contexts=[%d/%d]",
((((buf) >> 16) & 0xf) + (((buf) >> 20) & 0xf) * 10),
((((buf) >> 4) & 0xf) + ((buf) & 0xf) * 10), irq_buf,
- pci_resource_start(ohci->dev, 0),
- pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
+ (unsigned long long)pci_resource_start(ohci->dev, 0),
+ (unsigned long long)pci_resource_start(ohci->dev, 0) + OHCI1394_REGISTER_SIZE - 1,
ohci->max_packet_size,
ohci->nb_iso_rcv_ctx, ohci->nb_iso_xmit_ctx);
@@ -3217,7 +3217,7 @@ static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
{
struct hpsb_host *host;
struct ti_ohci *ohci; /* shortcut to currently handled device */
- unsigned long ohci_base;
+ resource_size_t ohci_base;
if (pci_enable_device(dev))
FAIL(-ENXIO, "Failed to enable OHCI hardware");
@@ -3270,15 +3270,16 @@ static int __devinit ohci1394_pci_probe(struct pci_dev *dev,
* clearly says it's 2kb, so this shouldn't be a problem. */
ohci_base = pci_resource_start(dev, 0);
if (pci_resource_len(dev, 0) < OHCI1394_REGISTER_SIZE)
- PRINT(KERN_WARNING, "PCI resource length of %lx too small!",
- pci_resource_len(dev, 0));
+ PRINT(KERN_WARNING, "PCI resource length of 0x%llx too small!",
+ (unsigned long long)pci_resource_len(dev, 0));
/* Seems PCMCIA handles this internally. Not sure why. Seems
* pretty bogus to force a driver to special case this. */
#ifndef PCMCIA
if (!request_mem_region (ohci_base, OHCI1394_REGISTER_SIZE, OHCI1394_DRIVER_NAME))
- FAIL(-ENOMEM, "MMIO resource (0x%lx - 0x%lx) unavailable",
- ohci_base, ohci_base + OHCI1394_REGISTER_SIZE);
+ FAIL(-ENOMEM, "MMIO resource (0x%llx - 0x%llx) unavailable",
+ (unsigned long long)ohci_base,
+ (unsigned long long)ohci_base + OHCI1394_REGISTER_SIZE);
#endif
ohci->init_state = OHCI_INIT_HAVE_MEM_REGION;
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
index 20ce539580f1..571ea68c0cf2 100644
--- a/drivers/ieee1394/raw1394.c
+++ b/drivers/ieee1394/raw1394.c
@@ -132,8 +132,7 @@ static void free_pending_request(struct pending_request *req)
static void __queue_complete_req(struct pending_request *req)
{
struct file_info *fi = req->file_info;
- list_del(&req->list);
- list_add_tail(&req->list, &fi->req_complete);
+ list_move_tail(&req->list, &fi->req_complete);
up(&fi->complete_sem);
wake_up_interruptible(&fi->poll_wait_complete);
diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig
index ba2d6505e9a4..69a53d476b5b 100644
--- a/drivers/infiniband/Kconfig
+++ b/drivers/infiniband/Kconfig
@@ -41,4 +41,6 @@ source "drivers/infiniband/ulp/ipoib/Kconfig"
source "drivers/infiniband/ulp/srp/Kconfig"
+source "drivers/infiniband/ulp/iser/Kconfig"
+
endmenu
diff --git a/drivers/infiniband/Makefile b/drivers/infiniband/Makefile
index eea27322a22d..c7ff58c1d0e5 100644
--- a/drivers/infiniband/Makefile
+++ b/drivers/infiniband/Makefile
@@ -3,3 +3,4 @@ obj-$(CONFIG_INFINIBAND_MTHCA) += hw/mthca/
obj-$(CONFIG_IPATH_CORE) += hw/ipath/
obj-$(CONFIG_INFINIBAND_IPOIB) += ulp/ipoib/
obj-$(CONFIG_INFINIBAND_SRP) += ulp/srp/
+obj-$(CONFIG_INFINIBAND_ISER) += ulp/iser/
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index a76834edf608..863f64befc7c 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -476,7 +476,7 @@ static inline int cma_zero_addr(struct sockaddr *addr)
else {
ip6 = &((struct sockaddr_in6 *) addr)->sin6_addr;
return (ip6->s6_addr32[0] | ip6->s6_addr32[1] |
- ip6->s6_addr32[3] | ip6->s6_addr32[4]) == 0;
+ ip6->s6_addr32[2] | ip6->s6_addr32[3]) == 0;
}
}
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index b38e02a5db35..5ed4dab52a6f 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -1775,11 +1775,9 @@ ib_find_send_mad(struct ib_mad_agent_private *mad_agent_priv,
void ib_mark_mad_done(struct ib_mad_send_wr_private *mad_send_wr)
{
mad_send_wr->timeout = 0;
- if (mad_send_wr->refcount == 1) {
- list_del(&mad_send_wr->agent_list);
- list_add_tail(&mad_send_wr->agent_list,
+ if (mad_send_wr->refcount == 1)
+ list_move_tail(&mad_send_wr->agent_list,
&mad_send_wr->mad_agent_priv->done_list);
- }
}
static void ib_mad_complete_recv(struct ib_mad_agent_private *mad_agent_priv,
@@ -2098,8 +2096,7 @@ retry:
queued_send_wr = container_of(mad_list,
struct ib_mad_send_wr_private,
mad_list);
- list_del(&mad_list->list);
- list_add_tail(&mad_list->list, &send_queue->list);
+ list_move_tail(&mad_list->list, &send_queue->list);
}
spin_unlock_irqrestore(&send_queue->lock, flags);
diff --git a/drivers/infiniband/core/mad_rmpp.c b/drivers/infiniband/core/mad_rmpp.c
index d4704e054e30..ebcd5b181770 100644
--- a/drivers/infiniband/core/mad_rmpp.c
+++ b/drivers/infiniband/core/mad_rmpp.c
@@ -665,8 +665,7 @@ static void process_rmpp_ack(struct ib_mad_agent_private *agent,
goto out;
mad_send_wr->refcount++;
- list_del(&mad_send_wr->agent_list);
- list_add_tail(&mad_send_wr->agent_list,
+ list_move_tail(&mad_send_wr->agent_list,
&mad_send_wr->mad_agent_priv->send_list);
}
out:
diff --git a/drivers/infiniband/core/uverbs_cmd.c b/drivers/infiniband/core/uverbs_cmd.c
index 76bf61e9b552..a908a7bdcd7f 100644
--- a/drivers/infiniband/core/uverbs_cmd.c
+++ b/drivers/infiniband/core/uverbs_cmd.c
@@ -1530,7 +1530,6 @@ ssize_t ib_uverbs_post_send(struct ib_uverbs_file *file,
out_put:
put_qp_read(qp);
-out:
while (wr) {
if (is_ud && wr->wr.ud.ah)
put_ah_read(wr->wr.ud.ah);
@@ -1539,6 +1538,7 @@ out:
wr = next;
}
+out:
kfree(user_wr);
return ret ? ret : in_len;
diff --git a/drivers/infiniband/core/uverbs_main.c b/drivers/infiniband/core/uverbs_main.c
index e57d3c50f75f..e725cccc7cde 100644
--- a/drivers/infiniband/core/uverbs_main.c
+++ b/drivers/infiniband/core/uverbs_main.c
@@ -188,7 +188,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
idr_remove_uobj(&ib_uverbs_ah_idr, uobj);
ib_destroy_ah(ah);
- list_del(&uobj->list);
kfree(uobj);
}
@@ -200,7 +199,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
idr_remove_uobj(&ib_uverbs_qp_idr, uobj);
ib_uverbs_detach_umcast(qp, uqp);
ib_destroy_qp(qp);
- list_del(&uobj->list);
ib_uverbs_release_uevent(file, &uqp->uevent);
kfree(uqp);
}
@@ -213,7 +211,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
idr_remove_uobj(&ib_uverbs_cq_idr, uobj);
ib_destroy_cq(cq);
- list_del(&uobj->list);
ib_uverbs_release_ucq(file, ev_file, ucq);
kfree(ucq);
}
@@ -225,7 +222,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
idr_remove_uobj(&ib_uverbs_srq_idr, uobj);
ib_destroy_srq(srq);
- list_del(&uobj->list);
ib_uverbs_release_uevent(file, uevent);
kfree(uevent);
}
@@ -243,7 +239,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
memobj = container_of(uobj, struct ib_umem_object, uobject);
ib_umem_release_on_close(mrdev, &memobj->umem);
- list_del(&uobj->list);
kfree(memobj);
}
@@ -252,7 +247,6 @@ static int ib_uverbs_cleanup_ucontext(struct ib_uverbs_file *file,
idr_remove_uobj(&ib_uverbs_pd_idr, uobj);
ib_dealloc_pd(pd);
- list_del(&uobj->list);
kfree(uobj);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index dddcdae736ac..e4b897fa569a 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -460,10 +460,10 @@ static int __devinit ipath_init_one(struct pci_dev *pdev,
for (j = 0; j < 6; j++) {
if (!pdev->resource[j].start)
continue;
- ipath_cdbg(VERBOSE, "BAR %d start %lx, end %lx, len %lx\n",
- j, pdev->resource[j].start,
- pdev->resource[j].end,
- pci_resource_len(pdev, j));
+ ipath_cdbg(VERBOSE, "BAR %d start %llx, end %llx, len %llx\n",
+ j, (unsigned long long)pdev->resource[j].start,
+ (unsigned long long)pdev->resource[j].end,
+ (unsigned long long)pci_resource_len(pdev, j));
}
if (!addr) {
diff --git a/drivers/infiniband/hw/mthca/mthca_main.c b/drivers/infiniband/hw/mthca/mthca_main.c
index 9b9ff7bff357..465fd220569c 100644
--- a/drivers/infiniband/hw/mthca/mthca_main.c
+++ b/drivers/infiniband/hw/mthca/mthca_main.c
@@ -172,8 +172,9 @@ static int __devinit mthca_dev_lim(struct mthca_dev *mdev, struct mthca_dev_lim
if (dev_lim->uar_size > pci_resource_len(mdev->pdev, 2)) {
mthca_err(mdev, "HCA reported UAR size of 0x%x bigger than "
- "PCI resource 2 size of 0x%lx, aborting.\n",
- dev_lim->uar_size, pci_resource_len(mdev->pdev, 2));
+ "PCI resource 2 size of 0x%llx, aborting.\n",
+ dev_lim->uar_size,
+ (unsigned long long)pci_resource_len(mdev->pdev, 2));
return -ENODEV;
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
index 216471fa01cc..ab40488182b3 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_multicast.c
@@ -864,8 +864,7 @@ void ipoib_mcast_restart_task(void *dev_ptr)
if (mcast) {
/* Destroy the send only entry */
- list_del(&mcast->list);
- list_add_tail(&mcast->list, &remove_list);
+ list_move_tail(&mcast->list, &remove_list);
rb_replace_node(&mcast->rb_node,
&nmcast->rb_node,
@@ -890,8 +889,7 @@ void ipoib_mcast_restart_task(void *dev_ptr)
rb_erase(&mcast->rb_node, &priv->multicast_tree);
/* Move to the remove list */
- list_del(&mcast->list);
- list_add_tail(&mcast->list, &remove_list);
+ list_move_tail(&mcast->list, &remove_list);
}
}
diff --git a/drivers/infiniband/ulp/iser/Kconfig b/drivers/infiniband/ulp/iser/Kconfig
new file mode 100644
index 000000000000..fead87d1eff9
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/Kconfig
@@ -0,0 +1,11 @@
+config INFINIBAND_ISER
+ tristate "ISCSI RDMA Protocol"
+ depends on INFINIBAND && SCSI
+ select SCSI_ISCSI_ATTRS
+ ---help---
+ Support for the ISCSI RDMA Protocol over InfiniBand. This
+ allows you to access storage devices that speak ISER/ISCSI
+ over InfiniBand.
+
+ The ISER protocol is defined by IETF.
+ See <http://www.ietf.org/>.
diff --git a/drivers/infiniband/ulp/iser/Makefile b/drivers/infiniband/ulp/iser/Makefile
new file mode 100644
index 000000000000..fe6cd15f2317
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/Makefile
@@ -0,0 +1,4 @@
+obj-$(CONFIG_INFINIBAND_ISER) += ib_iser.o
+
+ib_iser-y := iser_verbs.o iser_initiator.o iser_memory.o \
+ iscsi_iser.o
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
new file mode 100644
index 000000000000..4c3f2de2a06e
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -0,0 +1,790 @@
+/*
+ * iSCSI Initiator over iSER Data-Path
+ *
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 Mike Christie
+ * Copyright (c) 2005, 2006 Voltaire, Inc. All rights reserved.
+ * maintained by openib-general@openib.org
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * Credits:
+ * Christoph Hellwig
+ * FUJITA Tomonori
+ * Arne Redlich
+ * Zhenyu Wang
+ * Modified by:
+ * Erez Zilber
+ *
+ *
+ * $Id: iscsi_iser.c 6965 2006-05-07 11:36:20Z ogerlitz $
+ */
+
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/hardirq.h>
+#include <linux/kfifo.h>
+#include <linux/blkdev.h>
+#include <linux/init.h>
+#include <linux/ioctl.h>
+#include <linux/devfs_fs_kernel.h>
+#include <linux/cdev.h>
+#include <linux/in.h>
+#include <linux/net.h>
+#include <linux/scatterlist.h>
+#include <linux/delay.h>
+
+#include <net/sock.h>
+
+#include <asm/uaccess.h>
+
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include "iscsi_iser.h"
+
+static unsigned int iscsi_max_lun = 512;
+module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO);
+
+int iser_debug_level = 0;
+
+MODULE_DESCRIPTION("iSER (iSCSI Extensions for RDMA) Datamover "
+ "v" DRV_VER " (" DRV_DATE ")");
+MODULE_LICENSE("Dual BSD/GPL");
+MODULE_AUTHOR("Alex Nezhinsky, Dan Bar Dov, Or Gerlitz");
+
+module_param_named(debug_level, iser_debug_level, int, 0644);
+MODULE_PARM_DESC(debug_level, "Enable debug tracing if > 0 (default:disabled)");
+
+struct iser_global ig;
+
+void
+iscsi_iser_recv(struct iscsi_conn *conn,
+ struct iscsi_hdr *hdr, char *rx_data, int rx_data_len)
+{
+ int rc = 0;
+ uint32_t ret_itt;
+ int datalen;
+ int ahslen;
+
+ /* verify PDU length */
+ datalen = ntoh24(hdr->dlength);
+ if (datalen != rx_data_len) {
+ printk(KERN_ERR "iscsi_iser: datalen %d (hdr) != %d (IB) \n",
+ datalen, rx_data_len);
+ rc = ISCSI_ERR_DATALEN;
+ goto error;
+ }
+
+ /* read AHS */
+ ahslen = hdr->hlength * 4;
+
+ /* verify itt (itt encoding: age+cid+itt) */
+ rc = iscsi_verify_itt(conn, hdr, &ret_itt);
+
+ if (!rc)
+ rc = iscsi_complete_pdu(conn, hdr, rx_data, rx_data_len);
+
+ if (rc && rc != ISCSI_ERR_NO_SCSI_CMD)
+ goto error;
+
+ return;
+error:
+ iscsi_conn_failure(conn, rc);
+}
+
+
+/**
+ * iscsi_iser_cmd_init - Initialize iSCSI SCSI_READ or SCSI_WRITE commands
+ *
+ **/
+static void
+iscsi_iser_cmd_init(struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_iser_conn *iser_conn = ctask->conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ iser_ctask->command_sent = 0;
+ iser_ctask->iser_conn = iser_conn;
+
+ if (sc->sc_data_direction == DMA_TO_DEVICE) {
+ BUG_ON(ctask->total_length == 0);
+ /* bytes to be sent via RDMA operations */
+ iser_ctask->rdma_data_count = ctask->total_length -
+ ctask->imm_count -
+ ctask->unsol_count;
+
+ debug_scsi("cmd [itt %x total %d imm %d unsol_data %d "
+ "rdma_data %d]\n",
+ ctask->itt, ctask->total_length, ctask->imm_count,
+ ctask->unsol_count, iser_ctask->rdma_data_count);
+ } else
+ /* bytes to be sent via RDMA operations */
+ iser_ctask->rdma_data_count = ctask->total_length;
+
+ iser_ctask_rdma_init(iser_ctask);
+}
+
+/**
+ * iscsi_mtask_xmit - xmit management(immediate) task
+ * @conn: iscsi connection
+ * @mtask: task management task
+ *
+ * Notes:
+ * The function can return -EAGAIN in which case caller must
+ * call it again later, or recover. '0' return code means successful
+ * xmit.
+ *
+ **/
+static int
+iscsi_iser_mtask_xmit(struct iscsi_conn *conn,
+ struct iscsi_mgmt_task *mtask)
+{
+ int error = 0;
+
+ debug_scsi("mtask deq [cid %d itt 0x%x]\n", conn->id, mtask->itt);
+
+ error = iser_send_control(conn, mtask);
+
+ /* since iser xmits control with zero copy, mtasks can not be recycled
+ * right after sending them.
+ * The recycling scheme is based on whether a response is expected
+ * - if yes, the mtask is recycled at iscsi_complete_pdu
+ * - if no, the mtask is recycled at iser_snd_completion
+ */
+ if (error && error != -EAGAIN)
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+
+ return error;
+}
+
+static int
+iscsi_iser_ctask_xmit_unsol_data(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_data hdr;
+ int error = 0;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+
+ /* Send data-out PDUs while there's still unsolicited data to send */
+ while (ctask->unsol_count > 0) {
+ iscsi_prep_unsolicit_data_pdu(ctask, &hdr,
+ iser_ctask->rdma_data_count);
+
+ debug_scsi("Sending data-out: itt 0x%x, data count %d\n",
+ hdr.itt, ctask->data_count);
+
+ /* the buffer description has been passed with the command */
+ /* Send the command */
+ error = iser_send_data_out(conn, ctask, &hdr);
+ if (error) {
+ ctask->unsol_datasn--;
+ goto iscsi_iser_ctask_xmit_unsol_data_exit;
+ }
+ ctask->unsol_count -= ctask->data_count;
+ debug_scsi("Need to send %d more as data-out PDUs\n",
+ ctask->unsol_count);
+ }
+
+iscsi_iser_ctask_xmit_unsol_data_exit:
+ return error;
+}
+
+static int
+iscsi_iser_ctask_xmit(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ int error = 0;
+
+ debug_scsi("ctask deq [cid %d itt 0x%x]\n",
+ conn->id, ctask->itt);
+
+ /*
+ * serialize with TMF AbortTask
+ */
+ if (ctask->mtask)
+ return error;
+
+ /* Send the cmd PDU */
+ if (!iser_ctask->command_sent) {
+ error = iser_send_command(conn, ctask);
+ if (error)
+ goto iscsi_iser_ctask_xmit_exit;
+ iser_ctask->command_sent = 1;
+ }
+
+ /* Send unsolicited data-out PDU(s) if necessary */
+ if (ctask->unsol_count)
+ error = iscsi_iser_ctask_xmit_unsol_data(conn, ctask);
+
+ iscsi_iser_ctask_xmit_exit:
+ if (error && error != -EAGAIN)
+ iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED);
+ return error;
+}
+
+static void
+iscsi_iser_cleanup_ctask(struct iscsi_conn *conn, struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+
+ if (iser_ctask->status == ISER_TASK_STATUS_STARTED) {
+ iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
+ iser_ctask_rdma_finalize(iser_ctask);
+ }
+}
+
+static struct iser_conn *
+iscsi_iser_ib_conn_lookup(__u64 ep_handle)
+{
+ struct iser_conn *ib_conn;
+ struct iser_conn *uib_conn = (struct iser_conn *)(unsigned long)ep_handle;
+
+ mutex_lock(&ig.connlist_mutex);
+ list_for_each_entry(ib_conn, &ig.connlist, conn_list) {
+ if (ib_conn == uib_conn) {
+ mutex_unlock(&ig.connlist_mutex);
+ return ib_conn;
+ }
+ }
+ mutex_unlock(&ig.connlist_mutex);
+ iser_err("no conn exists for eph %llx\n",(unsigned long long)ep_handle);
+ return NULL;
+}
+
+static struct iscsi_cls_conn *
+iscsi_iser_conn_create(struct iscsi_cls_session *cls_session, uint32_t conn_idx)
+{
+ struct iscsi_conn *conn;
+ struct iscsi_cls_conn *cls_conn;
+ struct iscsi_iser_conn *iser_conn;
+
+ cls_conn = iscsi_conn_setup(cls_session, conn_idx);
+ if (!cls_conn)
+ return NULL;
+ conn = cls_conn->dd_data;
+
+ /*
+ * due to issues with the login code re iser sematics
+ * this not set in iscsi_conn_setup - FIXME
+ */
+ conn->max_recv_dlength = 128;
+
+ iser_conn = kzalloc(sizeof(*iser_conn), GFP_KERNEL);
+ if (!iser_conn)
+ goto conn_alloc_fail;
+
+ /* currently this is the only field which need to be initiated */
+ rwlock_init(&iser_conn->lock);
+
+ conn->dd_data = iser_conn;
+ iser_conn->iscsi_conn = conn;
+
+ return cls_conn;
+
+conn_alloc_fail:
+ iscsi_conn_teardown(cls_conn);
+ return NULL;
+}
+
+static void
+iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+
+ iscsi_conn_teardown(cls_conn);
+ kfree(iser_conn);
+}
+
+static int
+iscsi_iser_conn_bind(struct iscsi_cls_session *cls_session,
+ struct iscsi_cls_conn *cls_conn, uint64_t transport_eph,
+ int is_leading)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_iser_conn *iser_conn;
+ struct iser_conn *ib_conn;
+ int error;
+
+ error = iscsi_conn_bind(cls_session, cls_conn, is_leading);
+ if (error)
+ return error;
+
+ /* the transport ep handle comes from user space so it must be
+ * verified against the global ib connections list */
+ ib_conn = iscsi_iser_ib_conn_lookup(transport_eph);
+ if (!ib_conn) {
+ iser_err("can't bind eph %llx\n",
+ (unsigned long long)transport_eph);
+ return -EINVAL;
+ }
+ /* binds the iSER connection retrieved from the previously
+ * connected ep_handle to the iSCSI layer connection. exchanges
+ * connection pointers */
+ iser_err("binding iscsi conn %p to iser_conn %p\n",conn,ib_conn);
+ iser_conn = conn->dd_data;
+ ib_conn->iser_conn = iser_conn;
+ iser_conn->ib_conn = ib_conn;
+
+ conn->recv_lock = &iser_conn->lock;
+
+ return 0;
+}
+
+static int
+iscsi_iser_conn_start(struct iscsi_cls_conn *cls_conn)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ int err;
+
+ err = iscsi_conn_start(cls_conn);
+ if (err)
+ return err;
+
+ return iser_conn_set_full_featured_mode(conn);
+}
+
+static void
+iscsi_iser_conn_terminate(struct iscsi_conn *conn)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_conn *ib_conn = iser_conn->ib_conn;
+
+ BUG_ON(!ib_conn);
+ /* starts conn teardown process, waits until all previously *
+ * posted buffers get flushed, deallocates all conn resources */
+ iser_conn_terminate(ib_conn);
+ iser_conn->ib_conn = NULL;
+ conn->recv_lock = NULL;
+}
+
+
+static struct iscsi_transport iscsi_iser_transport;
+
+static struct iscsi_cls_session *
+iscsi_iser_session_create(struct iscsi_transport *iscsit,
+ struct scsi_transport_template *scsit,
+ uint32_t initial_cmdsn, uint32_t *hostno)
+{
+ struct iscsi_cls_session *cls_session;
+ struct iscsi_session *session;
+ int i;
+ uint32_t hn;
+ struct iscsi_cmd_task *ctask;
+ struct iscsi_mgmt_task *mtask;
+ struct iscsi_iser_cmd_task *iser_ctask;
+ struct iser_desc *desc;
+
+ cls_session = iscsi_session_setup(iscsit, scsit,
+ sizeof(struct iscsi_iser_cmd_task),
+ sizeof(struct iser_desc),
+ initial_cmdsn, &hn);
+ if (!cls_session)
+ return NULL;
+
+ *hostno = hn;
+ session = class_to_transport_session(cls_session);
+
+ /* libiscsi setup itts, data and pool so just set desc fields */
+ for (i = 0; i < session->cmds_max; i++) {
+ ctask = session->cmds[i];
+ iser_ctask = ctask->dd_data;
+ ctask->hdr = (struct iscsi_cmd *)&iser_ctask->desc.iscsi_header;
+ }
+
+ for (i = 0; i < session->mgmtpool_max; i++) {
+ mtask = session->mgmt_cmds[i];
+ desc = mtask->dd_data;
+ mtask->hdr = &desc->iscsi_header;
+ desc->data = mtask->data;
+ }
+
+ return cls_session;
+}
+
+static int
+iscsi_iser_conn_set_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, uint32_t value)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+ struct iscsi_session *session = conn->session;
+
+ spin_lock_bh(&session->lock);
+ if (conn->c_stage != ISCSI_CONN_INITIAL_STAGE &&
+ conn->stop_stage != STOP_CONN_RECOVER) {
+ printk(KERN_ERR "iscsi_iser: can not change parameter [%d]\n",
+ param);
+ spin_unlock_bh(&session->lock);
+ return 0;
+ }
+ spin_unlock_bh(&session->lock);
+
+ switch (param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ /* TBD */
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ conn->max_xmit_dlength = value;
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ if (value) {
+ printk(KERN_ERR "DataDigest wasn't negotiated to None");
+ return -EPROTO;
+ }
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ if (value) {
+ printk(KERN_ERR "DataDigest wasn't negotiated to None");
+ return -EPROTO;
+ }
+ break;
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ session->initial_r2t_en = value;
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ session->imm_data_en = value;
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ session->first_burst = value;
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ session->max_burst = value;
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ session->pdu_inorder_en = value;
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ session->dataseq_inorder_en = value;
+ break;
+ case ISCSI_PARAM_ERL:
+ session->erl = value;
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ if (value) {
+ printk(KERN_ERR "IFMarker wasn't negotiated to No");
+ return -EPROTO;
+ }
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ if (value) {
+ printk(KERN_ERR "OFMarker wasn't negotiated to No");
+ return -EPROTO;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_iser_session_get_param(struct iscsi_cls_session *cls_session,
+ enum iscsi_param param, uint32_t *value)
+{
+ struct Scsi_Host *shost = iscsi_session_to_shost(cls_session);
+ struct iscsi_session *session = iscsi_hostdata(shost->hostdata);
+
+ switch (param) {
+ case ISCSI_PARAM_INITIAL_R2T_EN:
+ *value = session->initial_r2t_en;
+ break;
+ case ISCSI_PARAM_MAX_R2T:
+ *value = session->max_r2t;
+ break;
+ case ISCSI_PARAM_IMM_DATA_EN:
+ *value = session->imm_data_en;
+ break;
+ case ISCSI_PARAM_FIRST_BURST:
+ *value = session->first_burst;
+ break;
+ case ISCSI_PARAM_MAX_BURST:
+ *value = session->max_burst;
+ break;
+ case ISCSI_PARAM_PDU_INORDER_EN:
+ *value = session->pdu_inorder_en;
+ break;
+ case ISCSI_PARAM_DATASEQ_INORDER_EN:
+ *value = session->dataseq_inorder_en;
+ break;
+ case ISCSI_PARAM_ERL:
+ *value = session->erl;
+ break;
+ case ISCSI_PARAM_IFMARKER_EN:
+ *value = 0;
+ break;
+ case ISCSI_PARAM_OFMARKER_EN:
+ *value = 0;
+ break;
+ default:
+ return ISCSI_ERR_PARAM_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+static int
+iscsi_iser_conn_get_param(struct iscsi_cls_conn *cls_conn,
+ enum iscsi_param param, uint32_t *value)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ switch(param) {
+ case ISCSI_PARAM_MAX_RECV_DLENGTH:
+ *value = conn->max_recv_dlength;
+ break;
+ case ISCSI_PARAM_MAX_XMIT_DLENGTH:
+ *value = conn->max_xmit_dlength;
+ break;
+ case ISCSI_PARAM_HDRDGST_EN:
+ *value = 0;
+ break;
+ case ISCSI_PARAM_DATADGST_EN:
+ *value = 0;
+ break;
+ /*case ISCSI_PARAM_TARGET_RECV_DLENGTH:
+ *value = conn->target_recv_dlength;
+ break;
+ case ISCSI_PARAM_INITIATOR_RECV_DLENGTH:
+ *value = conn->initiator_recv_dlength;
+ break;*/
+ default:
+ return ISCSI_ERR_PARAM_NOT_FOUND;
+ }
+
+ return 0;
+}
+
+
+static void
+iscsi_iser_conn_get_stats(struct iscsi_cls_conn *cls_conn, struct iscsi_stats *stats)
+{
+ struct iscsi_conn *conn = cls_conn->dd_data;
+
+ stats->txdata_octets = conn->txdata_octets;
+ stats->rxdata_octets = conn->rxdata_octets;
+ stats->scsicmd_pdus = conn->scsicmd_pdus_cnt;
+ stats->dataout_pdus = conn->dataout_pdus_cnt;
+ stats->scsirsp_pdus = conn->scsirsp_pdus_cnt;
+ stats->datain_pdus = conn->datain_pdus_cnt; /* always 0 */
+ stats->r2t_pdus = conn->r2t_pdus_cnt; /* always 0 */
+ stats->tmfcmd_pdus = conn->tmfcmd_pdus_cnt;
+ stats->tmfrsp_pdus = conn->tmfrsp_pdus_cnt;
+ stats->custom_length = 3;
+ strcpy(stats->custom[0].desc, "qp_tx_queue_full");
+ stats->custom[0].value = 0; /* TB iser_conn->qp_tx_queue_full; */
+ strcpy(stats->custom[1].desc, "fmr_map_not_avail");
+ stats->custom[1].value = 0; /* TB iser_conn->fmr_map_not_avail */;
+ strcpy(stats->custom[2].desc, "eh_abort_cnt");
+ stats->custom[2].value = conn->eh_abort_cnt;
+}
+
+static int
+iscsi_iser_ep_connect(struct sockaddr *dst_addr, int non_blocking,
+ __u64 *ep_handle)
+{
+ int err;
+ struct iser_conn *ib_conn;
+
+ err = iser_conn_init(&ib_conn);
+ if (err)
+ goto out;
+
+ err = iser_connect(ib_conn, NULL, (struct sockaddr_in *)dst_addr, non_blocking);
+ if (!err)
+ *ep_handle = (__u64)(unsigned long)ib_conn;
+
+out:
+ return err;
+}
+
+static int
+iscsi_iser_ep_poll(__u64 ep_handle, int timeout_ms)
+{
+ struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
+ int rc;
+
+ if (!ib_conn)
+ return -EINVAL;
+
+ rc = wait_event_interruptible_timeout(ib_conn->wait,
+ ib_conn->state == ISER_CONN_UP,
+ msecs_to_jiffies(timeout_ms));
+
+ /* if conn establishment failed, return error code to iscsi */
+ if (!rc &&
+ (ib_conn->state == ISER_CONN_TERMINATING ||
+ ib_conn->state == ISER_CONN_DOWN))
+ rc = -1;
+
+ iser_err("ib conn %p rc = %d\n", ib_conn, rc);
+
+ if (rc > 0)
+ return 1; /* success, this is the equivalent of POLLOUT */
+ else if (!rc)
+ return 0; /* timeout */
+ else
+ return rc; /* signal */
+}
+
+static void
+iscsi_iser_ep_disconnect(__u64 ep_handle)
+{
+ struct iser_conn *ib_conn = iscsi_iser_ib_conn_lookup(ep_handle);
+
+ if (!ib_conn)
+ return;
+
+ iser_err("ib conn %p state %d\n",ib_conn, ib_conn->state);
+
+ iser_conn_terminate(ib_conn);
+}
+
+static struct scsi_host_template iscsi_iser_sht = {
+ .name = "iSCSI Initiator over iSER, v." DRV_VER,
+ .queuecommand = iscsi_queuecommand,
+ .can_queue = ISCSI_XMIT_CMDS_MAX - 1,
+ .sg_tablesize = ISCSI_ISER_SG_TABLESIZE,
+ .cmd_per_lun = ISCSI_MAX_CMD_PER_LUN,
+ .eh_abort_handler = iscsi_eh_abort,
+ .eh_host_reset_handler = iscsi_eh_host_reset,
+ .use_clustering = DISABLE_CLUSTERING,
+ .proc_name = "iscsi_iser",
+ .this_id = -1,
+};
+
+static struct iscsi_transport iscsi_iser_transport = {
+ .owner = THIS_MODULE,
+ .name = "iser",
+ .caps = CAP_RECOVERY_L0 | CAP_MULTI_R2T,
+ .param_mask = ISCSI_MAX_RECV_DLENGTH |
+ ISCSI_MAX_XMIT_DLENGTH |
+ ISCSI_HDRDGST_EN |
+ ISCSI_DATADGST_EN |
+ ISCSI_INITIAL_R2T_EN |
+ ISCSI_MAX_R2T |
+ ISCSI_IMM_DATA_EN |
+ ISCSI_FIRST_BURST |
+ ISCSI_MAX_BURST |
+ ISCSI_PDU_INORDER_EN |
+ ISCSI_DATASEQ_INORDER_EN,
+ .host_template = &iscsi_iser_sht,
+ .conndata_size = sizeof(struct iscsi_conn),
+ .max_lun = ISCSI_ISER_MAX_LUN,
+ .max_cmd_len = ISCSI_ISER_MAX_CMD_LEN,
+ /* session management */
+ .create_session = iscsi_iser_session_create,
+ .destroy_session = iscsi_session_teardown,
+ /* connection management */
+ .create_conn = iscsi_iser_conn_create,
+ .bind_conn = iscsi_iser_conn_bind,
+ .destroy_conn = iscsi_iser_conn_destroy,
+ .set_param = iscsi_iser_conn_set_param,
+ .get_conn_param = iscsi_iser_conn_get_param,
+ .get_session_param = iscsi_iser_session_get_param,
+ .start_conn = iscsi_iser_conn_start,
+ .stop_conn = iscsi_conn_stop,
+ /* these are called as part of conn recovery */
+ .suspend_conn_recv = NULL, /* FIXME is/how this relvant to iser? */
+ .terminate_conn = iscsi_iser_conn_terminate,
+ /* IO */
+ .send_pdu = iscsi_conn_send_pdu,
+ .get_stats = iscsi_iser_conn_get_stats,
+ .init_cmd_task = iscsi_iser_cmd_init,
+ .xmit_cmd_task = iscsi_iser_ctask_xmit,
+ .xmit_mgmt_task = iscsi_iser_mtask_xmit,
+ .cleanup_cmd_task = iscsi_iser_cleanup_ctask,
+ /* recovery */
+ .session_recovery_timedout = iscsi_session_recovery_timedout,
+
+ .ep_connect = iscsi_iser_ep_connect,
+ .ep_poll = iscsi_iser_ep_poll,
+ .ep_disconnect = iscsi_iser_ep_disconnect
+};
+
+static int __init iser_init(void)
+{
+ int err;
+
+ iser_dbg("Starting iSER datamover...\n");
+
+ if (iscsi_max_lun < 1) {
+ printk(KERN_ERR "Invalid max_lun value of %u\n", iscsi_max_lun);
+ return -EINVAL;
+ }
+
+ iscsi_iser_transport.max_lun = iscsi_max_lun;
+
+ memset(&ig, 0, sizeof(struct iser_global));
+
+ ig.desc_cache = kmem_cache_create("iser_descriptors",
+ sizeof (struct iser_desc),
+ 0, SLAB_HWCACHE_ALIGN,
+ NULL, NULL);
+ if (ig.desc_cache == NULL)
+ return -ENOMEM;
+
+ /* device init is called only after the first addr resolution */
+ mutex_init(&ig.device_list_mutex);
+ INIT_LIST_HEAD(&ig.device_list);
+ mutex_init(&ig.connlist_mutex);
+ INIT_LIST_HEAD(&ig.connlist);
+
+ if (!iscsi_register_transport(&iscsi_iser_transport)) {
+ iser_err("iscsi_register_transport failed\n");
+ err = -EINVAL;
+ goto register_transport_failure;
+ }
+
+ return 0;
+
+register_transport_failure:
+ kmem_cache_destroy(ig.desc_cache);
+
+ return err;
+}
+
+static void __exit iser_exit(void)
+{
+ iser_dbg("Removing iSER datamover...\n");
+ iscsi_unregister_transport(&iscsi_iser_transport);
+ kmem_cache_destroy(ig.desc_cache);
+}
+
+module_init(iser_init);
+module_exit(iser_exit);
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
new file mode 100644
index 000000000000..3350ba690cfe
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -0,0 +1,354 @@
+/*
+ * iSER transport for the Open iSCSI Initiator & iSER transport internals
+ *
+ * Copyright (C) 2004 Dmitry Yusupov
+ * Copyright (C) 2004 Alex Aizman
+ * Copyright (C) 2005 Mike Christie
+ * based on code maintained by open-iscsi@googlegroups.com
+ *
+ * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: iscsi_iser.h 7051 2006-05-10 12:29:11Z ogerlitz $
+ */
+#ifndef __ISCSI_ISER_H__
+#define __ISCSI_ISER_H__
+
+#include <linux/types.h>
+#include <linux/net.h>
+#include <scsi/libiscsi.h>
+#include <scsi/scsi_transport_iscsi.h>
+
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/slab.h>
+#include <linux/dma-mapping.h>
+#include <linux/mutex.h>
+#include <linux/mempool.h>
+#include <linux/uio.h>
+
+#include <linux/socket.h>
+#include <linux/in.h>
+#include <linux/in6.h>
+
+#include <rdma/ib_verbs.h>
+#include <rdma/ib_fmr_pool.h>
+#include <rdma/rdma_cm.h>
+
+#define DRV_NAME "iser"
+#define PFX DRV_NAME ": "
+#define DRV_VER "0.1"
+#define DRV_DATE "May 7th, 2006"
+
+#define iser_dbg(fmt, arg...) \
+ do { \
+ if (iser_debug_level > 0) \
+ printk(KERN_DEBUG PFX "%s:" fmt,\
+ __func__ , ## arg); \
+ } while (0)
+
+#define iser_err(fmt, arg...) \
+ do { \
+ printk(KERN_ERR PFX "%s:" fmt, \
+ __func__ , ## arg); \
+ } while (0)
+
+ /* support upto 512KB in one RDMA */
+#define ISCSI_ISER_SG_TABLESIZE (0x80000 >> PAGE_SHIFT)
+#define ISCSI_ISER_MAX_LUN 256
+#define ISCSI_ISER_MAX_CMD_LEN 16
+
+/* QP settings */
+/* Maximal bounds on received asynchronous PDUs */
+#define ISER_MAX_RX_MISC_PDUS 4 /* NOOP_IN(2) , ASYNC_EVENT(2) */
+
+#define ISER_MAX_TX_MISC_PDUS 6 /* NOOP_OUT(2), TEXT(1), *
+ * SCSI_TMFUNC(2), LOGOUT(1) */
+
+#define ISER_QP_MAX_RECV_DTOS (ISCSI_XMIT_CMDS_MAX + \
+ ISER_MAX_RX_MISC_PDUS + \
+ ISER_MAX_TX_MISC_PDUS)
+
+/* the max TX (send) WR supported by the iSER QP is defined by *
+ * max_send_wr = T * (1 + D) + C ; D is how many inflight dataouts we expect *
+ * to have at max for SCSI command. The tx posting & completion handling code *
+ * supports -EAGAIN scheme where tx is suspended till the QP has room for more *
+ * send WR. D=8 comes from 64K/8K */
+
+#define ISER_INFLIGHT_DATAOUTS 8
+
+#define ISER_QP_MAX_REQ_DTOS (ISCSI_XMIT_CMDS_MAX * \
+ (1 + ISER_INFLIGHT_DATAOUTS) + \
+ ISER_MAX_TX_MISC_PDUS + \
+ ISER_MAX_RX_MISC_PDUS)
+
+#define ISER_VER 0x10
+#define ISER_WSV 0x08
+#define ISER_RSV 0x04
+
+struct iser_hdr {
+ u8 flags;
+ u8 rsvd[3];
+ __be32 write_stag; /* write rkey */
+ __be64 write_va;
+ __be32 read_stag; /* read rkey */
+ __be64 read_va;
+} __attribute__((packed));
+
+
+/* Length of an object name string */
+#define ISER_OBJECT_NAME_SIZE 64
+
+enum iser_ib_conn_state {
+ ISER_CONN_INIT, /* descriptor allocd, no conn */
+ ISER_CONN_PENDING, /* in the process of being established */
+ ISER_CONN_UP, /* up and running */
+ ISER_CONN_TERMINATING, /* in the process of being terminated */
+ ISER_CONN_DOWN, /* shut down */
+ ISER_CONN_STATES_NUM
+};
+
+enum iser_task_status {
+ ISER_TASK_STATUS_INIT = 0,
+ ISER_TASK_STATUS_STARTED,
+ ISER_TASK_STATUS_COMPLETED
+};
+
+enum iser_data_dir {
+ ISER_DIR_IN = 0, /* to initiator */
+ ISER_DIR_OUT, /* from initiator */
+ ISER_DIRS_NUM
+};
+
+struct iser_data_buf {
+ void *buf; /* pointer to the sg list */
+ unsigned int size; /* num entries of this sg */
+ unsigned long data_len; /* total data len */
+ unsigned int dma_nents; /* returned by dma_map_sg */
+ char *copy_buf; /* allocated copy buf for SGs unaligned *
+ * for rdma which are copied */
+ struct scatterlist sg_single; /* SG-ified clone of a non SG SC or *
+ * unaligned SG */
+ };
+
+/* fwd declarations */
+struct iser_device;
+struct iscsi_iser_conn;
+struct iscsi_iser_cmd_task;
+
+struct iser_mem_reg {
+ u32 lkey;
+ u32 rkey;
+ u64 va;
+ u64 len;
+ void *mem_h;
+};
+
+struct iser_regd_buf {
+ struct iser_mem_reg reg; /* memory registration info */
+ void *virt_addr;
+ struct iser_device *device; /* device->device for dma_unmap */
+ dma_addr_t dma_addr; /* if non zero, addr for dma_unmap */
+ enum dma_data_direction direction; /* direction for dma_unmap */
+ unsigned int data_size;
+ atomic_t ref_count; /* refcount, freed when dec to 0 */
+};
+
+#define MAX_REGD_BUF_VECTOR_LEN 2
+
+struct iser_dto {
+ struct iscsi_iser_cmd_task *ctask;
+ struct iscsi_iser_conn *conn;
+ int notify_enable;
+
+ /* vector of registered buffers */
+ unsigned int regd_vector_len;
+ struct iser_regd_buf *regd[MAX_REGD_BUF_VECTOR_LEN];
+
+ /* offset into the registered buffer may be specified */
+ unsigned int offset[MAX_REGD_BUF_VECTOR_LEN];
+
+ /* a smaller size may be specified, if 0, then full size is used */
+ unsigned int used_sz[MAX_REGD_BUF_VECTOR_LEN];
+};
+
+enum iser_desc_type {
+ ISCSI_RX,
+ ISCSI_TX_CONTROL ,
+ ISCSI_TX_SCSI_COMMAND,
+ ISCSI_TX_DATAOUT
+};
+
+struct iser_desc {
+ struct iser_hdr iser_header;
+ struct iscsi_hdr iscsi_header;
+ struct iser_regd_buf hdr_regd_buf;
+ void *data; /* used by RX & TX_CONTROL */
+ struct iser_regd_buf data_regd_buf; /* used by RX & TX_CONTROL */
+ enum iser_desc_type type;
+ struct iser_dto dto;
+};
+
+struct iser_device {
+ struct ib_device *ib_device;
+ struct ib_pd *pd;
+ struct ib_cq *cq;
+ struct ib_mr *mr;
+ struct tasklet_struct cq_tasklet;
+ struct list_head ig_list; /* entry in ig devices list */
+ int refcount;
+};
+
+struct iser_conn {
+ struct iscsi_iser_conn *iser_conn; /* iser conn for upcalls */
+ enum iser_ib_conn_state state; /* rdma connection state */
+ spinlock_t lock; /* used for state changes */
+ struct iser_device *device; /* device context */
+ struct rdma_cm_id *cma_id; /* CMA ID */
+ struct ib_qp *qp; /* QP */
+ struct ib_fmr_pool *fmr_pool; /* pool of IB FMRs */
+ int disc_evt_flag; /* disconn event delivered */
+ wait_queue_head_t wait; /* waitq for conn/disconn */
+ atomic_t post_recv_buf_count; /* posted rx count */
+ atomic_t post_send_buf_count; /* posted tx count */
+ struct work_struct comperror_work; /* conn term sleepable ctx*/
+ char name[ISER_OBJECT_NAME_SIZE];
+ struct iser_page_vec *page_vec; /* represents SG to fmr maps*
+ * maps serialized as tx is*/
+ struct list_head conn_list; /* entry in ig conn list */
+};
+
+struct iscsi_iser_conn {
+ struct iscsi_conn *iscsi_conn;/* ptr to iscsi conn */
+ struct iser_conn *ib_conn; /* iSER IB conn */
+
+ rwlock_t lock;
+};
+
+struct iscsi_iser_cmd_task {
+ struct iser_desc desc;
+ struct iscsi_iser_conn *iser_conn;
+ int rdma_data_count;/* RDMA bytes */
+ enum iser_task_status status;
+ int command_sent; /* set if command sent */
+ int dir[ISER_DIRS_NUM]; /* set if dir use*/
+ struct iser_regd_buf rdma_regd[ISER_DIRS_NUM];/* regd rdma buf */
+ struct iser_data_buf data[ISER_DIRS_NUM]; /* orig. data des*/
+ struct iser_data_buf data_copy[ISER_DIRS_NUM];/* contig. copy */
+};
+
+struct iser_page_vec {
+ u64 *pages;
+ int length;
+ int offset;
+ int data_size;
+};
+
+struct iser_global {
+ struct mutex device_list_mutex;/* */
+ struct list_head device_list; /* all iSER devices */
+ struct mutex connlist_mutex;
+ struct list_head connlist; /* all iSER IB connections */
+
+ kmem_cache_t *desc_cache;
+};
+
+extern struct iser_global ig;
+extern int iser_debug_level;
+
+/* allocate connection resources needed for rdma functionality */
+int iser_conn_set_full_featured_mode(struct iscsi_conn *conn);
+
+int iser_send_control(struct iscsi_conn *conn,
+ struct iscsi_mgmt_task *mtask);
+
+int iser_send_command(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask);
+
+int iser_send_data_out(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr);
+
+void iscsi_iser_recv(struct iscsi_conn *conn,
+ struct iscsi_hdr *hdr,
+ char *rx_data,
+ int rx_data_len);
+
+int iser_conn_init(struct iser_conn **ib_conn);
+
+void iser_conn_terminate(struct iser_conn *ib_conn);
+
+void iser_conn_release(struct iser_conn *ib_conn);
+
+void iser_rcv_completion(struct iser_desc *desc,
+ unsigned long dto_xfer_len);
+
+void iser_snd_completion(struct iser_desc *desc);
+
+void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *ctask);
+
+void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *ctask);
+
+void iser_dto_buffs_release(struct iser_dto *dto);
+
+int iser_regd_buff_release(struct iser_regd_buf *regd_buf);
+
+void iser_reg_single(struct iser_device *device,
+ struct iser_regd_buf *regd_buf,
+ enum dma_data_direction direction);
+
+int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
+ enum iser_data_dir cmd_dir);
+
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *ctask,
+ enum iser_data_dir cmd_dir);
+
+int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *ctask,
+ enum iser_data_dir cmd_dir);
+
+int iser_connect(struct iser_conn *ib_conn,
+ struct sockaddr_in *src_addr,
+ struct sockaddr_in *dst_addr,
+ int non_blocking);
+
+int iser_reg_page_vec(struct iser_conn *ib_conn,
+ struct iser_page_vec *page_vec,
+ struct iser_mem_reg *mem_reg);
+
+void iser_unreg_mem(struct iser_mem_reg *mem_reg);
+
+int iser_post_recv(struct iser_desc *rx_desc);
+int iser_post_send(struct iser_desc *tx_desc);
+
+int iser_conn_state_comp(struct iser_conn *ib_conn,
+ enum iser_ib_conn_state comp);
+#endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
new file mode 100644
index 000000000000..ccf56f6f7236
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: iser_initiator.c 6964 2006-05-07 11:11:43Z ogerlitz $
+ */
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/scatterlist.h>
+#include <linux/kfifo.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_host.h>
+
+#include "iscsi_iser.h"
+
+/* Constant PDU lengths calculations */
+#define ISER_TOTAL_HEADERS_LEN (sizeof (struct iser_hdr) + \
+ sizeof (struct iscsi_hdr))
+
+/* iser_dto_add_regd_buff - increments the reference count for *
+ * the registered buffer & adds it to the DTO object */
+static void iser_dto_add_regd_buff(struct iser_dto *dto,
+ struct iser_regd_buf *regd_buf,
+ unsigned long use_offset,
+ unsigned long use_size)
+{
+ int add_idx;
+
+ atomic_inc(&regd_buf->ref_count);
+
+ add_idx = dto->regd_vector_len;
+ dto->regd[add_idx] = regd_buf;
+ dto->used_sz[add_idx] = use_size;
+ dto->offset[add_idx] = use_offset;
+
+ dto->regd_vector_len++;
+}
+
+static int iser_dma_map_task_data(struct iscsi_iser_cmd_task *iser_ctask,
+ struct iser_data_buf *data,
+ enum iser_data_dir iser_dir,
+ enum dma_data_direction dma_dir)
+{
+ struct device *dma_device;
+
+ iser_ctask->dir[iser_dir] = 1;
+ dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+
+ data->dma_nents = dma_map_sg(dma_device, data->buf, data->size, dma_dir);
+ if (data->dma_nents == 0) {
+ iser_err("dma_map_sg failed!!!\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask)
+{
+ struct device *dma_device;
+ struct iser_data_buf *data;
+
+ dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+
+ if (iser_ctask->dir[ISER_DIR_IN]) {
+ data = &iser_ctask->data[ISER_DIR_IN];
+ dma_unmap_sg(dma_device, data->buf, data->size, DMA_FROM_DEVICE);
+ }
+
+ if (iser_ctask->dir[ISER_DIR_OUT]) {
+ data = &iser_ctask->data[ISER_DIR_OUT];
+ dma_unmap_sg(dma_device, data->buf, data->size, DMA_TO_DEVICE);
+ }
+}
+
+/* Register user buffer memory and initialize passive rdma
+ * dto descriptor. Total data size is stored in
+ * iser_ctask->data[ISER_DIR_IN].data_len
+ */
+static int iser_prepare_read_cmd(struct iscsi_cmd_task *ctask,
+ unsigned int edtl)
+
+{
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iser_regd_buf *regd_buf;
+ int err;
+ struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
+ struct iser_data_buf *buf_in = &iser_ctask->data[ISER_DIR_IN];
+
+ err = iser_dma_map_task_data(iser_ctask,
+ buf_in,
+ ISER_DIR_IN,
+ DMA_FROM_DEVICE);
+ if (err)
+ return err;
+
+ if (edtl > iser_ctask->data[ISER_DIR_IN].data_len) {
+ iser_err("Total data length: %ld, less than EDTL: "
+ "%d, in READ cmd BHS itt: %d, conn: 0x%p\n",
+ iser_ctask->data[ISER_DIR_IN].data_len, edtl,
+ ctask->itt, iser_ctask->iser_conn);
+ return -EINVAL;
+ }
+
+ err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_IN);
+ if (err) {
+ iser_err("Failed to set up Data-IN RDMA\n");
+ return err;
+ }
+ regd_buf = &iser_ctask->rdma_regd[ISER_DIR_IN];
+
+ hdr->flags |= ISER_RSV;
+ hdr->read_stag = cpu_to_be32(regd_buf->reg.rkey);
+ hdr->read_va = cpu_to_be64(regd_buf->reg.va);
+
+ iser_dbg("Cmd itt:%d READ tags RKEY:%#.4X VA:%#llX\n",
+ ctask->itt, regd_buf->reg.rkey,
+ (unsigned long long)regd_buf->reg.va);
+
+ return 0;
+}
+
+/* Register user buffer memory and initialize passive rdma
+ * dto descriptor. Total data size is stored in
+ * ctask->data[ISER_DIR_OUT].data_len
+ */
+static int
+iser_prepare_write_cmd(struct iscsi_cmd_task *ctask,
+ unsigned int imm_sz,
+ unsigned int unsol_sz,
+ unsigned int edtl)
+{
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iser_regd_buf *regd_buf;
+ int err;
+ struct iser_dto *send_dto = &iser_ctask->desc.dto;
+ struct iser_hdr *hdr = &iser_ctask->desc.iser_header;
+ struct iser_data_buf *buf_out = &iser_ctask->data[ISER_DIR_OUT];
+
+ err = iser_dma_map_task_data(iser_ctask,
+ buf_out,
+ ISER_DIR_OUT,
+ DMA_TO_DEVICE);
+ if (err)
+ return err;
+
+ if (edtl > iser_ctask->data[ISER_DIR_OUT].data_len) {
+ iser_err("Total data length: %ld, less than EDTL: %d, "
+ "in WRITE cmd BHS itt: %d, conn: 0x%p\n",
+ iser_ctask->data[ISER_DIR_OUT].data_len,
+ edtl, ctask->itt, ctask->conn);
+ return -EINVAL;
+ }
+
+ err = iser_reg_rdma_mem(iser_ctask,ISER_DIR_OUT);
+ if (err != 0) {
+ iser_err("Failed to register write cmd RDMA mem\n");
+ return err;
+ }
+
+ regd_buf = &iser_ctask->rdma_regd[ISER_DIR_OUT];
+
+ if (unsol_sz < edtl) {
+ hdr->flags |= ISER_WSV;
+ hdr->write_stag = cpu_to_be32(regd_buf->reg.rkey);
+ hdr->write_va = cpu_to_be64(regd_buf->reg.va + unsol_sz);
+
+ iser_dbg("Cmd itt:%d, WRITE tags, RKEY:%#.4X "
+ "VA:%#llX + unsol:%d\n",
+ ctask->itt, regd_buf->reg.rkey,
+ (unsigned long long)regd_buf->reg.va, unsol_sz);
+ }
+
+ if (imm_sz > 0) {
+ iser_dbg("Cmd itt:%d, WRITE, adding imm.data sz: %d\n",
+ ctask->itt, imm_sz);
+ iser_dto_add_regd_buff(send_dto,
+ regd_buf,
+ 0,
+ imm_sz);
+ }
+
+ return 0;
+}
+
+/**
+ * iser_post_receive_control - allocates, initializes and posts receive DTO.
+ */
+static int iser_post_receive_control(struct iscsi_conn *conn)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_desc *rx_desc;
+ struct iser_regd_buf *regd_hdr;
+ struct iser_regd_buf *regd_data;
+ struct iser_dto *recv_dto = NULL;
+ struct iser_device *device = iser_conn->ib_conn->device;
+ int rx_data_size, err = 0;
+
+ rx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
+ if (rx_desc == NULL) {
+ iser_err("Failed to alloc desc for post recv\n");
+ return -ENOMEM;
+ }
+ rx_desc->type = ISCSI_RX;
+
+ /* for the login sequence we must support rx of upto 8K; login is done
+ * after conn create/bind (connect) and conn stop/bind (reconnect),
+ * what's common for both schemes is that the connection is not started
+ */
+ if (conn->c_stage != ISCSI_CONN_STARTED)
+ rx_data_size = DEFAULT_MAX_RECV_DATA_SEGMENT_LENGTH;
+ else /* FIXME till user space sets conn->max_recv_dlength correctly */
+ rx_data_size = 128;
+
+ rx_desc->data = kmalloc(rx_data_size, GFP_NOIO);
+ if (rx_desc->data == NULL) {
+ iser_err("Failed to alloc data buf for post recv\n");
+ err = -ENOMEM;
+ goto post_rx_kmalloc_failure;
+ }
+
+ recv_dto = &rx_desc->dto;
+ recv_dto->conn = iser_conn;
+ recv_dto->regd_vector_len = 0;
+
+ regd_hdr = &rx_desc->hdr_regd_buf;
+ memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
+ regd_hdr->device = device;
+ regd_hdr->virt_addr = rx_desc; /* == &rx_desc->iser_header */
+ regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
+
+ iser_reg_single(device, regd_hdr, DMA_FROM_DEVICE);
+
+ iser_dto_add_regd_buff(recv_dto, regd_hdr, 0, 0);
+
+ regd_data = &rx_desc->data_regd_buf;
+ memset(regd_data, 0, sizeof(struct iser_regd_buf));
+ regd_data->device = device;
+ regd_data->virt_addr = rx_desc->data;
+ regd_data->data_size = rx_data_size;
+
+ iser_reg_single(device, regd_data, DMA_FROM_DEVICE);
+
+ iser_dto_add_regd_buff(recv_dto, regd_data, 0, 0);
+
+ err = iser_post_recv(rx_desc);
+ if (!err)
+ return 0;
+
+ /* iser_post_recv failed */
+ iser_dto_buffs_release(recv_dto);
+ kfree(rx_desc->data);
+post_rx_kmalloc_failure:
+ kmem_cache_free(ig.desc_cache, rx_desc);
+ return err;
+}
+
+/* creates a new tx descriptor and adds header regd buffer */
+static void iser_create_send_desc(struct iscsi_iser_conn *iser_conn,
+ struct iser_desc *tx_desc)
+{
+ struct iser_regd_buf *regd_hdr = &tx_desc->hdr_regd_buf;
+ struct iser_dto *send_dto = &tx_desc->dto;
+
+ memset(regd_hdr, 0, sizeof(struct iser_regd_buf));
+ regd_hdr->device = iser_conn->ib_conn->device;
+ regd_hdr->virt_addr = tx_desc; /* == &tx_desc->iser_header */
+ regd_hdr->data_size = ISER_TOTAL_HEADERS_LEN;
+
+ send_dto->conn = iser_conn;
+ send_dto->notify_enable = 1;
+ send_dto->regd_vector_len = 0;
+
+ memset(&tx_desc->iser_header, 0, sizeof(struct iser_hdr));
+ tx_desc->iser_header.flags = ISER_VER;
+
+ iser_dto_add_regd_buff(send_dto, regd_hdr, 0, 0);
+}
+
+/**
+ * iser_conn_set_full_featured_mode - (iSER API)
+ */
+int iser_conn_set_full_featured_mode(struct iscsi_conn *conn)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+
+ int i;
+ /* no need to keep it in a var, we are after login so if this should
+ * be negotiated, by now the result should be available here */
+ int initial_post_recv_bufs_num = ISER_MAX_RX_MISC_PDUS;
+
+ iser_dbg("Initially post: %d\n", initial_post_recv_bufs_num);
+
+ /* Check that there is no posted recv or send buffers left - */
+ /* they must be consumed during the login phase */
+ BUG_ON(atomic_read(&iser_conn->ib_conn->post_recv_buf_count) != 0);
+ BUG_ON(atomic_read(&iser_conn->ib_conn->post_send_buf_count) != 0);
+
+ /* Initial post receive buffers */
+ for (i = 0; i < initial_post_recv_bufs_num; i++) {
+ if (iser_post_receive_control(conn) != 0) {
+ iser_err("Failed to post recv bufs at:%d conn:0x%p\n",
+ i, conn);
+ return -ENOMEM;
+ }
+ }
+ iser_dbg("Posted %d post recv bufs, conn:0x%p\n", i, conn);
+ return 0;
+}
+
+static int
+iser_check_xmit(struct iscsi_conn *conn, void *task)
+{
+ int rc = 0;
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+
+ write_lock_bh(conn->recv_lock);
+ if (atomic_read(&iser_conn->ib_conn->post_send_buf_count) ==
+ ISER_QP_MAX_REQ_DTOS) {
+ iser_dbg("%ld can't xmit task %p, suspending tx\n",jiffies,task);
+ set_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ rc = -EAGAIN;
+ }
+ write_unlock_bh(conn->recv_lock);
+ return rc;
+}
+
+
+/**
+ * iser_send_command - send command PDU
+ */
+int iser_send_command(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iser_dto *send_dto = NULL;
+ unsigned long edtl;
+ int err = 0;
+ struct iser_data_buf *data_buf;
+
+ struct iscsi_cmd *hdr = ctask->hdr;
+ struct scsi_cmnd *sc = ctask->sc;
+
+ if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
+ iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
+ return -EPERM;
+ }
+ if (iser_check_xmit(conn, ctask))
+ return -EAGAIN;
+
+ edtl = ntohl(hdr->data_length);
+
+ /* build the tx desc regd header and add it to the tx desc dto */
+ iser_ctask->desc.type = ISCSI_TX_SCSI_COMMAND;
+ send_dto = &iser_ctask->desc.dto;
+ send_dto->ctask = iser_ctask;
+ iser_create_send_desc(iser_conn, &iser_ctask->desc);
+
+ if (hdr->flags & ISCSI_FLAG_CMD_READ)
+ data_buf = &iser_ctask->data[ISER_DIR_IN];
+ else
+ data_buf = &iser_ctask->data[ISER_DIR_OUT];
+
+ if (sc->use_sg) { /* using a scatter list */
+ data_buf->buf = sc->request_buffer;
+ data_buf->size = sc->use_sg;
+ } else if (sc->request_bufflen) {
+ /* using a single buffer - convert it into one entry SG */
+ sg_init_one(&data_buf->sg_single,
+ sc->request_buffer, sc->request_bufflen);
+ data_buf->buf = &data_buf->sg_single;
+ data_buf->size = 1;
+ }
+
+ data_buf->data_len = sc->request_bufflen;
+
+ if (hdr->flags & ISCSI_FLAG_CMD_READ) {
+ err = iser_prepare_read_cmd(ctask, edtl);
+ if (err)
+ goto send_command_error;
+ }
+ if (hdr->flags & ISCSI_FLAG_CMD_WRITE) {
+ err = iser_prepare_write_cmd(ctask,
+ ctask->imm_count,
+ ctask->imm_count +
+ ctask->unsol_count,
+ edtl);
+ if (err)
+ goto send_command_error;
+ }
+
+ iser_reg_single(iser_conn->ib_conn->device,
+ send_dto->regd[0], DMA_TO_DEVICE);
+
+ if (iser_post_receive_control(conn) != 0) {
+ iser_err("post_recv failed!\n");
+ err = -ENOMEM;
+ goto send_command_error;
+ }
+
+ iser_ctask->status = ISER_TASK_STATUS_STARTED;
+
+ err = iser_post_send(&iser_ctask->desc);
+ if (!err)
+ return 0;
+
+send_command_error:
+ iser_dto_buffs_release(send_dto);
+ iser_err("conn %p failed ctask->itt %d err %d\n",conn, ctask->itt, err);
+ return err;
+}
+
+/**
+ * iser_send_data_out - send data out PDU
+ */
+int iser_send_data_out(struct iscsi_conn *conn,
+ struct iscsi_cmd_task *ctask,
+ struct iscsi_data *hdr)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iscsi_iser_cmd_task *iser_ctask = ctask->dd_data;
+ struct iser_desc *tx_desc = NULL;
+ struct iser_dto *send_dto = NULL;
+ unsigned long buf_offset;
+ unsigned long data_seg_len;
+ unsigned int itt;
+ int err = 0;
+
+ if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
+ iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
+ return -EPERM;
+ }
+
+ if (iser_check_xmit(conn, ctask))
+ return -EAGAIN;
+
+ itt = ntohl(hdr->itt);
+ data_seg_len = ntoh24(hdr->dlength);
+ buf_offset = ntohl(hdr->offset);
+
+ iser_dbg("%s itt %d dseg_len %d offset %d\n",
+ __func__,(int)itt,(int)data_seg_len,(int)buf_offset);
+
+ tx_desc = kmem_cache_alloc(ig.desc_cache, GFP_NOIO);
+ if (tx_desc == NULL) {
+ iser_err("Failed to alloc desc for post dataout\n");
+ return -ENOMEM;
+ }
+
+ tx_desc->type = ISCSI_TX_DATAOUT;
+ memcpy(&tx_desc->iscsi_header, hdr, sizeof(struct iscsi_hdr));
+
+ /* build the tx desc regd header and add it to the tx desc dto */
+ send_dto = &tx_desc->dto;
+ send_dto->ctask = iser_ctask;
+ iser_create_send_desc(iser_conn, tx_desc);
+
+ iser_reg_single(iser_conn->ib_conn->device,
+ send_dto->regd[0], DMA_TO_DEVICE);
+
+ /* all data was registered for RDMA, we can use the lkey */
+ iser_dto_add_regd_buff(send_dto,
+ &iser_ctask->rdma_regd[ISER_DIR_OUT],
+ buf_offset,
+ data_seg_len);
+
+ if (buf_offset + data_seg_len > iser_ctask->data[ISER_DIR_OUT].data_len) {
+ iser_err("Offset:%ld & DSL:%ld in Data-Out "
+ "inconsistent with total len:%ld, itt:%d\n",
+ buf_offset, data_seg_len,
+ iser_ctask->data[ISER_DIR_OUT].data_len, itt);
+ err = -EINVAL;
+ goto send_data_out_error;
+ }
+ iser_dbg("data-out itt: %d, offset: %ld, sz: %ld\n",
+ itt, buf_offset, data_seg_len);
+
+
+ err = iser_post_send(tx_desc);
+ if (!err)
+ return 0;
+
+send_data_out_error:
+ iser_dto_buffs_release(send_dto);
+ kmem_cache_free(ig.desc_cache, tx_desc);
+ iser_err("conn %p failed err %d\n",conn, err);
+ return err;
+}
+
+int iser_send_control(struct iscsi_conn *conn,
+ struct iscsi_mgmt_task *mtask)
+{
+ struct iscsi_iser_conn *iser_conn = conn->dd_data;
+ struct iser_desc *mdesc = mtask->dd_data;
+ struct iser_dto *send_dto = NULL;
+ unsigned int itt;
+ unsigned long data_seg_len;
+ int err = 0;
+ unsigned char opcode;
+ struct iser_regd_buf *regd_buf;
+ struct iser_device *device;
+
+ if (!iser_conn_state_comp(iser_conn->ib_conn, ISER_CONN_UP)) {
+ iser_err("Failed to send, conn: 0x%p is not up\n", iser_conn->ib_conn);
+ return -EPERM;
+ }
+
+ if (iser_check_xmit(conn,mtask))
+ return -EAGAIN;
+
+ /* build the tx desc regd header and add it to the tx desc dto */
+ mdesc->type = ISCSI_TX_CONTROL;
+ send_dto = &mdesc->dto;
+ send_dto->ctask = NULL;
+ iser_create_send_desc(iser_conn, mdesc);
+
+ device = iser_conn->ib_conn->device;
+
+ iser_reg_single(device, send_dto->regd[0], DMA_TO_DEVICE);
+
+ itt = ntohl(mtask->hdr->itt);
+ opcode = mtask->hdr->opcode & ISCSI_OPCODE_MASK;
+ data_seg_len = ntoh24(mtask->hdr->dlength);
+
+ if (data_seg_len > 0) {
+ regd_buf = &mdesc->data_regd_buf;
+ memset(regd_buf, 0, sizeof(struct iser_regd_buf));
+ regd_buf->device = device;
+ regd_buf->virt_addr = mtask->data;
+ regd_buf->data_size = mtask->data_count;
+ iser_reg_single(device, regd_buf,
+ DMA_TO_DEVICE);
+ iser_dto_add_regd_buff(send_dto, regd_buf,
+ 0,
+ data_seg_len);
+ }
+
+ if (iser_post_receive_control(conn) != 0) {
+ iser_err("post_rcv_buff failed!\n");
+ err = -ENOMEM;
+ goto send_control_error;
+ }
+
+ err = iser_post_send(mdesc);
+ if (!err)
+ return 0;
+
+send_control_error:
+ iser_dto_buffs_release(send_dto);
+ iser_err("conn %p failed err %d\n",conn, err);
+ return err;
+}
+
+/**
+ * iser_rcv_dto_completion - recv DTO completion
+ */
+void iser_rcv_completion(struct iser_desc *rx_desc,
+ unsigned long dto_xfer_len)
+{
+ struct iser_dto *dto = &rx_desc->dto;
+ struct iscsi_iser_conn *conn = dto->conn;
+ struct iscsi_session *session = conn->iscsi_conn->session;
+ struct iscsi_cmd_task *ctask;
+ struct iscsi_iser_cmd_task *iser_ctask;
+ struct iscsi_hdr *hdr;
+ char *rx_data = NULL;
+ int rx_data_len = 0;
+ unsigned int itt;
+ unsigned char opcode;
+
+ hdr = &rx_desc->iscsi_header;
+
+ iser_dbg("op 0x%x itt 0x%x\n", hdr->opcode,hdr->itt);
+
+ if (dto_xfer_len > ISER_TOTAL_HEADERS_LEN) { /* we have data */
+ rx_data_len = dto_xfer_len - ISER_TOTAL_HEADERS_LEN;
+ rx_data = dto->regd[1]->virt_addr;
+ rx_data += dto->offset[1];
+ }
+
+ opcode = hdr->opcode & ISCSI_OPCODE_MASK;
+
+ if (opcode == ISCSI_OP_SCSI_CMD_RSP) {
+ itt = hdr->itt & ISCSI_ITT_MASK; /* mask out cid and age bits */
+ if (!(itt < session->cmds_max))
+ iser_err("itt can't be matched to task!!!"
+ "conn %p opcode %d cmds_max %d itt %d\n",
+ conn->iscsi_conn,opcode,session->cmds_max,itt);
+ /* use the mapping given with the cmds array indexed by itt */
+ ctask = (struct iscsi_cmd_task *)session->cmds[itt];
+ iser_ctask = ctask->dd_data;
+ iser_dbg("itt %d ctask %p\n",itt,ctask);
+ iser_ctask->status = ISER_TASK_STATUS_COMPLETED;
+ iser_ctask_rdma_finalize(iser_ctask);
+ }
+
+ iser_dto_buffs_release(dto);
+
+ iscsi_iser_recv(conn->iscsi_conn, hdr, rx_data, rx_data_len);
+
+ kfree(rx_desc->data);
+ kmem_cache_free(ig.desc_cache, rx_desc);
+
+ /* decrementing conn->post_recv_buf_count only --after-- freeing the *
+ * task eliminates the need to worry on tasks which are completed in *
+ * parallel to the execution of iser_conn_term. So the code that waits *
+ * for the posted rx bufs refcount to become zero handles everything */
+ atomic_dec(&conn->ib_conn->post_recv_buf_count);
+}
+
+void iser_snd_completion(struct iser_desc *tx_desc)
+{
+ struct iser_dto *dto = &tx_desc->dto;
+ struct iscsi_iser_conn *iser_conn = dto->conn;
+ struct iscsi_conn *conn = iser_conn->iscsi_conn;
+ struct iscsi_mgmt_task *mtask;
+
+ iser_dbg("Initiator, Data sent dto=0x%p\n", dto);
+
+ iser_dto_buffs_release(dto);
+
+ if (tx_desc->type == ISCSI_TX_DATAOUT)
+ kmem_cache_free(ig.desc_cache, tx_desc);
+
+ atomic_dec(&iser_conn->ib_conn->post_send_buf_count);
+
+ write_lock(conn->recv_lock);
+ if (conn->suspend_tx) {
+ iser_dbg("%ld resuming tx\n",jiffies);
+ clear_bit(ISCSI_SUSPEND_BIT, &conn->suspend_tx);
+ scsi_queue_work(conn->session->host, &conn->xmitwork);
+ }
+ write_unlock(conn->recv_lock);
+
+ if (tx_desc->type == ISCSI_TX_CONTROL) {
+ /* this arithmetic is legal by libiscsi dd_data allocation */
+ mtask = (void *) ((long)(void *)tx_desc -
+ sizeof(struct iscsi_mgmt_task));
+ if (mtask->hdr->itt == cpu_to_be32(ISCSI_RESERVED_TAG)) {
+ struct iscsi_session *session = conn->session;
+
+ spin_lock(&conn->session->lock);
+ list_del(&mtask->running);
+ __kfifo_put(session->mgmtpool.queue, (void*)&mtask,
+ sizeof(void*));
+ spin_unlock(&session->lock);
+ }
+ }
+}
+
+void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
+
+{
+ iser_ctask->status = ISER_TASK_STATUS_INIT;
+
+ iser_ctask->dir[ISER_DIR_IN] = 0;
+ iser_ctask->dir[ISER_DIR_OUT] = 0;
+
+ iser_ctask->data[ISER_DIR_IN].data_len = 0;
+ iser_ctask->data[ISER_DIR_OUT].data_len = 0;
+
+ memset(&iser_ctask->rdma_regd[ISER_DIR_IN], 0,
+ sizeof(struct iser_regd_buf));
+ memset(&iser_ctask->rdma_regd[ISER_DIR_OUT], 0,
+ sizeof(struct iser_regd_buf));
+}
+
+void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
+{
+ int deferred;
+
+ /* if we were reading, copy back to unaligned sglist,
+ * anyway dma_unmap and free the copy
+ */
+ if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL)
+ iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
+ if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL)
+ iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
+
+ if (iser_ctask->dir[ISER_DIR_IN]) {
+ deferred = iser_regd_buff_release
+ (&iser_ctask->rdma_regd[ISER_DIR_IN]);
+ if (deferred) {
+ iser_err("References remain for BUF-IN rdma reg\n");
+ BUG();
+ }
+ }
+
+ if (iser_ctask->dir[ISER_DIR_OUT]) {
+ deferred = iser_regd_buff_release
+ (&iser_ctask->rdma_regd[ISER_DIR_OUT]);
+ if (deferred) {
+ iser_err("References remain for BUF-OUT rdma reg\n");
+ BUG();
+ }
+ }
+
+ iser_dma_unmap_task_data(iser_ctask);
+}
+
+void iser_dto_buffs_release(struct iser_dto *dto)
+{
+ int i;
+
+ for (i = 0; i < dto->regd_vector_len; i++)
+ iser_regd_buff_release(dto->regd[i]);
+}
+
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
new file mode 100644
index 000000000000..31950a522a1c
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -0,0 +1,401 @@
+/*
+ * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: iser_memory.c 6964 2006-05-07 11:11:43Z ogerlitz $
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <asm/io.h>
+#include <asm/scatterlist.h>
+#include <linux/scatterlist.h>
+
+#include "iscsi_iser.h"
+
+#define ISER_KMALLOC_THRESHOLD 0x20000 /* 128K - kmalloc limit */
+/**
+ * Decrements the reference count for the
+ * registered buffer & releases it
+ *
+ * returns 0 if released, 1 if deferred
+ */
+int iser_regd_buff_release(struct iser_regd_buf *regd_buf)
+{
+ struct device *dma_device;
+
+ if ((atomic_read(&regd_buf->ref_count) == 0) ||
+ atomic_dec_and_test(&regd_buf->ref_count)) {
+ /* if we used the dma mr, unreg is just NOP */
+ if (regd_buf->reg.rkey != 0)
+ iser_unreg_mem(&regd_buf->reg);
+
+ if (regd_buf->dma_addr) {
+ dma_device = regd_buf->device->ib_device->dma_device;
+ dma_unmap_single(dma_device,
+ regd_buf->dma_addr,
+ regd_buf->data_size,
+ regd_buf->direction);
+ }
+ /* else this regd buf is associated with task which we */
+ /* dma_unmap_single/sg later */
+ return 0;
+ } else {
+ iser_dbg("Release deferred, regd.buff: 0x%p\n", regd_buf);
+ return 1;
+ }
+}
+
+/**
+ * iser_reg_single - fills registered buffer descriptor with
+ * registration information
+ */
+void iser_reg_single(struct iser_device *device,
+ struct iser_regd_buf *regd_buf,
+ enum dma_data_direction direction)
+{
+ dma_addr_t dma_addr;
+
+ dma_addr = dma_map_single(device->ib_device->dma_device,
+ regd_buf->virt_addr,
+ regd_buf->data_size, direction);
+ BUG_ON(dma_mapping_error(dma_addr));
+
+ regd_buf->reg.lkey = device->mr->lkey;
+ regd_buf->reg.rkey = 0; /* indicate there's no need to unreg */
+ regd_buf->reg.len = regd_buf->data_size;
+ regd_buf->reg.va = dma_addr;
+
+ regd_buf->dma_addr = dma_addr;
+ regd_buf->direction = direction;
+}
+
+/**
+ * iser_start_rdma_unaligned_sg
+ */
+int iser_start_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+ enum iser_data_dir cmd_dir)
+{
+ int dma_nents;
+ struct device *dma_device;
+ char *mem = NULL;
+ struct iser_data_buf *data = &iser_ctask->data[cmd_dir];
+ unsigned long cmd_data_len = data->data_len;
+
+ if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
+ mem = (void *)__get_free_pages(GFP_NOIO,
+ long_log2(roundup_pow_of_two(cmd_data_len)) - PAGE_SHIFT);
+ else
+ mem = kmalloc(cmd_data_len, GFP_NOIO);
+
+ if (mem == NULL) {
+ iser_err("Failed to allocate mem size %d %d for copying sglist\n",
+ data->size,(int)cmd_data_len);
+ return -ENOMEM;
+ }
+
+ if (cmd_dir == ISER_DIR_OUT) {
+ /* copy the unaligned sg the buffer which is used for RDMA */
+ struct scatterlist *sg = (struct scatterlist *)data->buf;
+ int i;
+ char *p, *from;
+
+ for (p = mem, i = 0; i < data->size; i++) {
+ from = kmap_atomic(sg[i].page, KM_USER0);
+ memcpy(p,
+ from + sg[i].offset,
+ sg[i].length);
+ kunmap_atomic(from, KM_USER0);
+ p += sg[i].length;
+ }
+ }
+
+ sg_init_one(&iser_ctask->data_copy[cmd_dir].sg_single, mem, cmd_data_len);
+ iser_ctask->data_copy[cmd_dir].buf =
+ &iser_ctask->data_copy[cmd_dir].sg_single;
+ iser_ctask->data_copy[cmd_dir].size = 1;
+
+ iser_ctask->data_copy[cmd_dir].copy_buf = mem;
+
+ dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+
+ if (cmd_dir == ISER_DIR_OUT)
+ dma_nents = dma_map_sg(dma_device,
+ &iser_ctask->data_copy[cmd_dir].sg_single,
+ 1, DMA_TO_DEVICE);
+ else
+ dma_nents = dma_map_sg(dma_device,
+ &iser_ctask->data_copy[cmd_dir].sg_single,
+ 1, DMA_FROM_DEVICE);
+
+ BUG_ON(dma_nents == 0);
+
+ iser_ctask->data_copy[cmd_dir].dma_nents = dma_nents;
+ return 0;
+}
+
+/**
+ * iser_finalize_rdma_unaligned_sg
+ */
+void iser_finalize_rdma_unaligned_sg(struct iscsi_iser_cmd_task *iser_ctask,
+ enum iser_data_dir cmd_dir)
+{
+ struct device *dma_device;
+ struct iser_data_buf *mem_copy;
+ unsigned long cmd_data_len;
+
+ dma_device = iser_ctask->iser_conn->ib_conn->device->ib_device->dma_device;
+ mem_copy = &iser_ctask->data_copy[cmd_dir];
+
+ if (cmd_dir == ISER_DIR_OUT)
+ dma_unmap_sg(dma_device, &mem_copy->sg_single, 1,
+ DMA_TO_DEVICE);
+ else
+ dma_unmap_sg(dma_device, &mem_copy->sg_single, 1,
+ DMA_FROM_DEVICE);
+
+ if (cmd_dir == ISER_DIR_IN) {
+ char *mem;
+ struct scatterlist *sg;
+ unsigned char *p, *to;
+ unsigned int sg_size;
+ int i;
+
+ /* copy back read RDMA to unaligned sg */
+ mem = mem_copy->copy_buf;
+
+ sg = (struct scatterlist *)iser_ctask->data[ISER_DIR_IN].buf;
+ sg_size = iser_ctask->data[ISER_DIR_IN].size;
+
+ for (p = mem, i = 0; i < sg_size; i++){
+ to = kmap_atomic(sg[i].page, KM_SOFTIRQ0);
+ memcpy(to + sg[i].offset,
+ p,
+ sg[i].length);
+ kunmap_atomic(to, KM_SOFTIRQ0);
+ p += sg[i].length;
+ }
+ }
+
+ cmd_data_len = iser_ctask->data[cmd_dir].data_len;
+
+ if (cmd_data_len > ISER_KMALLOC_THRESHOLD)
+ free_pages((unsigned long)mem_copy->copy_buf,
+ long_log2(roundup_pow_of_two(cmd_data_len)) - PAGE_SHIFT);
+ else
+ kfree(mem_copy->copy_buf);
+
+ mem_copy->copy_buf = NULL;
+}
+
+/**
+ * iser_sg_to_page_vec - Translates scatterlist entries to physical addresses
+ * and returns the length of resulting physical address array (may be less than
+ * the original due to possible compaction).
+ *
+ * we build a "page vec" under the assumption that the SG meets the RDMA
+ * alignment requirements. Other then the first and last SG elements, all
+ * the "internal" elements can be compacted into a list whose elements are
+ * dma addresses of physical pages. The code supports also the weird case
+ * where --few fragments of the same page-- are present in the SG as
+ * consecutive elements. Also, it handles one entry SG.
+ */
+static int iser_sg_to_page_vec(struct iser_data_buf *data,
+ struct iser_page_vec *page_vec)
+{
+ struct scatterlist *sg = (struct scatterlist *)data->buf;
+ dma_addr_t first_addr, last_addr, page;
+ int start_aligned, end_aligned;
+ unsigned int cur_page = 0;
+ unsigned long total_sz = 0;
+ int i;
+
+ /* compute the offset of first element */
+ page_vec->offset = (u64) sg[0].offset;
+
+ for (i = 0; i < data->dma_nents; i++) {
+ total_sz += sg_dma_len(&sg[i]);
+
+ first_addr = sg_dma_address(&sg[i]);
+ last_addr = first_addr + sg_dma_len(&sg[i]);
+
+ start_aligned = !(first_addr & ~PAGE_MASK);
+ end_aligned = !(last_addr & ~PAGE_MASK);
+
+ /* continue to collect page fragments till aligned or SG ends */
+ while (!end_aligned && (i + 1 < data->dma_nents)) {
+ i++;
+ total_sz += sg_dma_len(&sg[i]);
+ last_addr = sg_dma_address(&sg[i]) + sg_dma_len(&sg[i]);
+ end_aligned = !(last_addr & ~PAGE_MASK);
+ }
+
+ first_addr = first_addr & PAGE_MASK;
+
+ for (page = first_addr; page < last_addr; page += PAGE_SIZE)
+ page_vec->pages[cur_page++] = page;
+
+ }
+ page_vec->data_size = total_sz;
+ iser_dbg("page_vec->data_size:%d cur_page %d\n", page_vec->data_size,cur_page);
+ return cur_page;
+}
+
+#define MASK_4K ((1UL << 12) - 1) /* 0xFFF */
+#define IS_4K_ALIGNED(addr) ((((unsigned long)addr) & MASK_4K) == 0)
+
+/**
+ * iser_data_buf_aligned_len - Tries to determine the maximal correctly aligned
+ * for RDMA sub-list of a scatter-gather list of memory buffers, and returns
+ * the number of entries which are aligned correctly. Supports the case where
+ * consecutive SG elements are actually fragments of the same physcial page.
+ */
+static unsigned int iser_data_buf_aligned_len(struct iser_data_buf *data)
+{
+ struct scatterlist *sg;
+ dma_addr_t end_addr, next_addr;
+ int i, cnt;
+ unsigned int ret_len = 0;
+
+ sg = (struct scatterlist *)data->buf;
+
+ for (cnt = 0, i = 0; i < data->dma_nents; i++, cnt++) {
+ /* iser_dbg("Checking sg iobuf [%d]: phys=0x%08lX "
+ "offset: %ld sz: %ld\n", i,
+ (unsigned long)page_to_phys(sg[i].page),
+ (unsigned long)sg[i].offset,
+ (unsigned long)sg[i].length); */
+ end_addr = sg_dma_address(&sg[i]) +
+ sg_dma_len(&sg[i]);
+ /* iser_dbg("Checking sg iobuf end address "
+ "0x%08lX\n", end_addr); */
+ if (i + 1 < data->dma_nents) {
+ next_addr = sg_dma_address(&sg[i+1]);
+ /* are i, i+1 fragments of the same page? */
+ if (end_addr == next_addr)
+ continue;
+ else if (!IS_4K_ALIGNED(end_addr)) {
+ ret_len = cnt + 1;
+ break;
+ }
+ }
+ }
+ if (i == data->dma_nents)
+ ret_len = cnt; /* loop ended */
+ iser_dbg("Found %d aligned entries out of %d in sg:0x%p\n",
+ ret_len, data->dma_nents, data);
+ return ret_len;
+}
+
+static void iser_data_buf_dump(struct iser_data_buf *data)
+{
+ struct scatterlist *sg = (struct scatterlist *)data->buf;
+ int i;
+
+ for (i = 0; i < data->size; i++)
+ iser_err("sg[%d] dma_addr:0x%lX page:0x%p "
+ "off:%d sz:%d dma_len:%d\n",
+ i, (unsigned long)sg_dma_address(&sg[i]),
+ sg[i].page, sg[i].offset,
+ sg[i].length,sg_dma_len(&sg[i]));
+}
+
+static void iser_dump_page_vec(struct iser_page_vec *page_vec)
+{
+ int i;
+
+ iser_err("page vec length %d data size %d\n",
+ page_vec->length, page_vec->data_size);
+ for (i = 0; i < page_vec->length; i++)
+ iser_err("%d %lx\n",i,(unsigned long)page_vec->pages[i]);
+}
+
+static void iser_page_vec_build(struct iser_data_buf *data,
+ struct iser_page_vec *page_vec)
+{
+ int page_vec_len = 0;
+
+ page_vec->length = 0;
+ page_vec->offset = 0;
+
+ iser_dbg("Translating sg sz: %d\n", data->dma_nents);
+ page_vec_len = iser_sg_to_page_vec(data,page_vec);
+ iser_dbg("sg len %d page_vec_len %d\n", data->dma_nents,page_vec_len);
+
+ page_vec->length = page_vec_len;
+
+ if (page_vec_len * PAGE_SIZE < page_vec->data_size) {
+ iser_err("page_vec too short to hold this SG\n");
+ iser_data_buf_dump(data);
+ iser_dump_page_vec(page_vec);
+ BUG();
+ }
+}
+
+/**
+ * iser_reg_rdma_mem - Registers memory intended for RDMA,
+ * obtaining rkey and va
+ *
+ * returns 0 on success, errno code on failure
+ */
+int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
+ enum iser_data_dir cmd_dir)
+{
+ struct iser_conn *ib_conn = iser_ctask->iser_conn->ib_conn;
+ struct iser_data_buf *mem = &iser_ctask->data[cmd_dir];
+ struct iser_regd_buf *regd_buf;
+ int aligned_len;
+ int err;
+
+ regd_buf = &iser_ctask->rdma_regd[cmd_dir];
+
+ aligned_len = iser_data_buf_aligned_len(mem);
+ if (aligned_len != mem->size) {
+ iser_err("rdma alignment violation %d/%d aligned\n",
+ aligned_len, mem->size);
+ iser_data_buf_dump(mem);
+ /* allocate copy buf, if we are writing, copy the */
+ /* unaligned scatterlist, dma map the copy */
+ if (iser_start_rdma_unaligned_sg(iser_ctask, cmd_dir) != 0)
+ return -ENOMEM;
+ mem = &iser_ctask->data_copy[cmd_dir];
+ }
+
+ iser_page_vec_build(mem, ib_conn->page_vec);
+ err = iser_reg_page_vec(ib_conn, ib_conn->page_vec, &regd_buf->reg);
+ if (err)
+ return err;
+
+ /* take a reference on this regd buf such that it will not be released *
+ * (eg in send dto completion) before we get the scsi response */
+ atomic_inc(&regd_buf->ref_count);
+ return 0;
+}
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
new file mode 100644
index 000000000000..ff117bbf81b4
--- /dev/null
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2004, 2005, 2006 Voltaire, Inc. All rights reserved.
+ * Copyright (c) 2005, 2006 Cisco Systems. All rights reserved.
+ *
+ * This software is available to you under a choice of one of two
+ * licenses. You may choose to be licensed under the terms of the GNU
+ * General Public License (GPL) Version 2, available from the file
+ * COPYING in the main directory of this source tree, or the
+ * OpenIB.org BSD license below:
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials
+ * provided with the distribution.
+ *
+ * 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 THE AUTHORS OR COPYRIGHT HOLDERS
+ * 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.
+ *
+ * $Id: iser_verbs.c 7051 2006-05-10 12:29:11Z ogerlitz $
+ */
+#include <asm/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/smp_lock.h>
+#include <linux/delay.h>
+#include <linux/version.h>
+
+#include "iscsi_iser.h"
+
+#define ISCSI_ISER_MAX_CONN 8
+#define ISER_MAX_CQ_LEN ((ISER_QP_MAX_RECV_DTOS + \
+ ISER_QP_MAX_REQ_DTOS) * \
+ ISCSI_ISER_MAX_CONN)
+
+static void iser_cq_tasklet_fn(unsigned long data);
+static void iser_cq_callback(struct ib_cq *cq, void *cq_context);
+static void iser_comp_error_worker(void *data);
+
+static void iser_cq_event_callback(struct ib_event *cause, void *context)
+{
+ iser_err("got cq event %d \n", cause->event);
+}
+
+static void iser_qp_event_callback(struct ib_event *cause, void *context)
+{
+ iser_err("got qp event %d\n",cause->event);
+}
+
+/**
+ * iser_create_device_ib_res - creates Protection Domain (PD), Completion
+ * Queue (CQ), DMA Memory Region (DMA MR) with the device associated with
+ * the adapator.
+ *
+ * returns 0 on success, -1 on failure
+ */
+static int iser_create_device_ib_res(struct iser_device *device)
+{
+ device->pd = ib_alloc_pd(device->ib_device);
+ if (IS_ERR(device->pd))
+ goto pd_err;
+
+ device->cq = ib_create_cq(device->ib_device,
+ iser_cq_callback,
+ iser_cq_event_callback,
+ (void *)device,
+ ISER_MAX_CQ_LEN);
+ if (IS_ERR(device->cq))
+ goto cq_err;
+
+ if (ib_req_notify_cq(device->cq, IB_CQ_NEXT_COMP))
+ goto cq_arm_err;
+
+ tasklet_init(&device->cq_tasklet,
+ iser_cq_tasklet_fn,
+ (unsigned long)device);
+
+ device->mr = ib_get_dma_mr(device->pd,
+ IB_ACCESS_LOCAL_WRITE);
+ if (IS_ERR(device->mr))
+ goto dma_mr_err;
+
+ return 0;
+
+dma_mr_err:
+ tasklet_kill(&device->cq_tasklet);
+cq_arm_err:
+ ib_destroy_cq(device->cq);
+cq_err:
+ ib_dealloc_pd(device->pd);
+pd_err:
+ iser_err("failed to allocate an IB resource\n");
+ return -1;
+}
+
+/**
+ * iser_free_device_ib_res - destory/dealloc/dereg the DMA MR,
+ * CQ and PD created with the device associated with the adapator.
+ */
+static void iser_free_device_ib_res(struct iser_device *device)
+{
+ BUG_ON(device->mr == NULL);
+
+ tasklet_kill(&device->cq_tasklet);
+
+ (void)ib_dereg_mr(device->mr);
+ (void)ib_destroy_cq(device->cq);
+ (void)ib_dealloc_pd(device->pd);
+
+ device->mr = NULL;
+ device->cq = NULL;
+ device->pd = NULL;
+}
+
+/**
+ * iser_create_ib_conn_res - Creates FMR pool and Queue-Pair (QP)
+ *
+ * returns 0 on success, -1 on failure
+ */
+static int iser_create_ib_conn_res(struct iser_conn *ib_conn)
+{
+ struct iser_device *device;
+ struct ib_qp_init_attr init_attr;
+ int ret;
+ struct ib_fmr_pool_param params;
+
+ BUG_ON(ib_conn->device == NULL);
+
+ device = ib_conn->device;
+
+ ib_conn->page_vec = kmalloc(sizeof(struct iser_page_vec) +
+ (sizeof(u64) * (ISCSI_ISER_SG_TABLESIZE +1)),
+ GFP_KERNEL);
+ if (!ib_conn->page_vec) {
+ ret = -ENOMEM;
+ goto alloc_err;
+ }
+ ib_conn->page_vec->pages = (u64 *) (ib_conn->page_vec + 1);
+
+ params.page_shift = PAGE_SHIFT;
+ /* when the first/last SG element are not start/end *
+ * page aligned, the map whould be of N+1 pages */
+ params.max_pages_per_fmr = ISCSI_ISER_SG_TABLESIZE + 1;
+ /* make the pool size twice the max number of SCSI commands *
+ * the ML is expected to queue, watermark for unmap at 50% */
+ params.pool_size = ISCSI_XMIT_CMDS_MAX * 2;
+ params.dirty_watermark = ISCSI_XMIT_CMDS_MAX;
+ params.cache = 0;
+ params.flush_function = NULL;
+ params.access = (IB_ACCESS_LOCAL_WRITE |
+ IB_ACCESS_REMOTE_WRITE |
+ IB_ACCESS_REMOTE_READ);
+
+ ib_conn->fmr_pool = ib_create_fmr_pool(device->pd, &params);
+ if (IS_ERR(ib_conn->fmr_pool)) {
+ ret = PTR_ERR(ib_conn->fmr_pool);
+ goto fmr_pool_err;
+ }
+
+ memset(&init_attr, 0, sizeof init_attr);
+
+ init_attr.event_handler = iser_qp_event_callback;
+ init_attr.qp_context = (void *)ib_conn;
+ init_attr.send_cq = device->cq;
+ init_attr.recv_cq = device->cq;
+ init_attr.cap.max_send_wr = ISER_QP_MAX_REQ_DTOS;
+ init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS;
+ init_attr.cap.max_send_sge = MAX_REGD_BUF_VECTOR_LEN;
+ init_attr.cap.max_recv_sge = 2;
+ init_attr.sq_sig_type = IB_SIGNAL_REQ_WR;
+ init_attr.qp_type = IB_QPT_RC;
+
+ ret = rdma_create_qp(ib_conn->cma_id, device->pd, &init_attr);
+ if (ret)
+ goto qp_err;
+
+ ib_conn->qp = ib_conn->cma_id->qp;
+ iser_err("setting conn %p cma_id %p: fmr_pool %p qp %p\n",
+ ib_conn, ib_conn->cma_id,
+ ib_conn->fmr_pool, ib_conn->cma_id->qp);
+ return ret;
+
+qp_err:
+ (void)ib_destroy_fmr_pool(ib_conn->fmr_pool);
+fmr_pool_err:
+ kfree(ib_conn->page_vec);
+alloc_err:
+ iser_err("unable to alloc mem or create resource, err %d\n", ret);
+ return ret;
+}
+
+/**
+ * releases the FMR pool, QP and CMA ID objects, returns 0 on success,
+ * -1 on failure
+ */
+static int iser_free_ib_conn_res(struct iser_conn *ib_conn)
+{
+ BUG_ON(ib_conn == NULL);
+
+ iser_err("freeing conn %p cma_id %p fmr pool %p qp %p\n",
+ ib_conn, ib_conn->cma_id,
+ ib_conn->fmr_pool, ib_conn->qp);
+
+ /* qp is created only once both addr & route are resolved */
+ if (ib_conn->fmr_pool != NULL)
+ ib_destroy_fmr_pool(ib_conn->fmr_pool);
+
+ if (ib_conn->qp != NULL)
+ rdma_destroy_qp(ib_conn->cma_id);
+
+ if (ib_conn->cma_id != NULL)
+ rdma_destroy_id(ib_conn->cma_id);
+
+ ib_conn->fmr_pool = NULL;
+ ib_conn->qp = NULL;
+ ib_conn->cma_id = NULL;
+ kfree(ib_conn->page_vec);
+
+ return 0;
+}
+
+/**
+ * based on the resolved device node GUID see if there already allocated
+ * device for this device. If there's no such, create one.
+ */
+static
+struct iser_device *iser_device_find_by_ib_device(struct rdma_cm_id *cma_id)
+{
+ struct list_head *p_list;
+ struct iser_device *device = NULL;
+
+ mutex_lock(&ig.device_list_mutex);
+
+ p_list = ig.device_list.next;
+ while (p_list != &ig.device_list) {
+ device = list_entry(p_list, struct iser_device, ig_list);
+ /* find if there's a match using the node GUID */
+ if (device->ib_device->node_guid == cma_id->device->node_guid)
+ break;
+ }
+
+ if (device == NULL) {
+ device = kzalloc(sizeof *device, GFP_KERNEL);
+ if (device == NULL)
+ goto out;
+ /* assign this device to the device */
+ device->ib_device = cma_id->device;
+ /* init the device and link it into ig device list */
+ if (iser_create_device_ib_res(device)) {
+ kfree(device);
+ device = NULL;
+ goto out;
+ }
+ list_add(&device->ig_list, &ig.device_list);
+ }
+out:
+ BUG_ON(device == NULL);
+ device->refcount++;
+ mutex_unlock(&ig.device_list_mutex);
+ return device;
+}
+
+/* if there's no demand for this device, release it */
+static void iser_device_try_release(struct iser_device *device)
+{
+ mutex_lock(&ig.device_list_mutex);
+ device->refcount--;
+ iser_err("device %p refcount %d\n",device,device->refcount);
+ if (!device->refcount) {
+ iser_free_device_ib_res(device);
+ list_del(&device->ig_list);
+ kfree(device);
+ }
+ mutex_unlock(&ig.device_list_mutex);
+}
+
+int iser_conn_state_comp(struct iser_conn *ib_conn,
+ enum iser_ib_conn_state comp)
+{
+ int ret;
+
+ spin_lock_bh(&ib_conn->lock);
+ ret = (ib_conn->state == comp);
+ spin_unlock_bh(&ib_conn->lock);
+ return ret;
+}
+
+static int iser_conn_state_comp_exch(struct iser_conn *ib_conn,
+ enum iser_ib_conn_state comp,
+ enum iser_ib_conn_state exch)
+{
+ int ret;
+
+ spin_lock_bh(&ib_conn->lock);
+ if ((ret = (ib_conn->state == comp)))
+ ib_conn->state = exch;
+ spin_unlock_bh(&ib_conn->lock);
+ return ret;
+}
+
+/**
+ * triggers start of the disconnect procedures and wait for them to be done
+ */
+void iser_conn_terminate(struct iser_conn *ib_conn)
+{
+ int err = 0;
+
+ /* change the ib conn state only if the conn is UP, however always call
+ * rdma_disconnect since this is the only way to cause the CMA to change
+ * the QP state to ERROR
+ */
+
+ iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP, ISER_CONN_TERMINATING);
+ err = rdma_disconnect(ib_conn->cma_id);
+ if (err)
+ iser_err("Failed to disconnect, conn: 0x%p err %d\n",
+ ib_conn,err);
+
+ wait_event_interruptible(ib_conn->wait,
+ ib_conn->state == ISER_CONN_DOWN);
+
+ iser_conn_release(ib_conn);
+}
+
+static void iser_connect_error(struct rdma_cm_id *cma_id)
+{
+ struct iser_conn *ib_conn;
+ ib_conn = (struct iser_conn *)cma_id->context;
+
+ ib_conn->state = ISER_CONN_DOWN;
+ wake_up_interruptible(&ib_conn->wait);
+}
+
+static void iser_addr_handler(struct rdma_cm_id *cma_id)
+{
+ struct iser_device *device;
+ struct iser_conn *ib_conn;
+ int ret;
+
+ device = iser_device_find_by_ib_device(cma_id);
+ ib_conn = (struct iser_conn *)cma_id->context;
+ ib_conn->device = device;
+
+ ret = rdma_resolve_route(cma_id, 1000);
+ if (ret) {
+ iser_err("resolve route failed: %d\n", ret);
+ iser_connect_error(cma_id);
+ }
+ return;
+}
+
+static void iser_route_handler(struct rdma_cm_id *cma_id)
+{
+ struct rdma_conn_param conn_param;
+ int ret;
+
+ ret = iser_create_ib_conn_res((struct iser_conn *)cma_id->context);
+ if (ret)
+ goto failure;
+
+ iser_dbg("path.mtu is %d setting it to %d\n",
+ cma_id->route.path_rec->mtu, IB_MTU_1024);
+
+ /* we must set the MTU to 1024 as this is what the target is assuming */
+ if (cma_id->route.path_rec->mtu > IB_MTU_1024)
+ cma_id->route.path_rec->mtu = IB_MTU_1024;
+
+ memset(&conn_param, 0, sizeof conn_param);
+ conn_param.responder_resources = 4;
+ conn_param.initiator_depth = 1;
+ conn_param.retry_count = 7;
+ conn_param.rnr_retry_count = 6;
+
+ ret = rdma_connect(cma_id, &conn_param);
+ if (ret) {
+ iser_err("failure connecting: %d\n", ret);
+ goto failure;
+ }
+
+ return;
+failure:
+ iser_connect_error(cma_id);
+}
+
+static void iser_connected_handler(struct rdma_cm_id *cma_id)
+{
+ struct iser_conn *ib_conn;
+
+ ib_conn = (struct iser_conn *)cma_id->context;
+ ib_conn->state = ISER_CONN_UP;
+ wake_up_interruptible(&ib_conn->wait);
+}
+
+static void iser_disconnected_handler(struct rdma_cm_id *cma_id)
+{
+ struct iser_conn *ib_conn;
+
+ ib_conn = (struct iser_conn *)cma_id->context;
+ ib_conn->disc_evt_flag = 1;
+
+ /* getting here when the state is UP means that the conn is being *
+ * terminated asynchronously from the iSCSI layer's perspective. */
+ if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
+ ISER_CONN_TERMINATING))
+ iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
+ ISCSI_ERR_CONN_FAILED);
+
+ /* Complete the termination process if no posts are pending */
+ if ((atomic_read(&ib_conn->post_recv_buf_count) == 0) &&
+ (atomic_read(&ib_conn->post_send_buf_count) == 0)) {
+ ib_conn->state = ISER_CONN_DOWN;
+ wake_up_interruptible(&ib_conn->wait);
+ }
+}
+
+static int iser_cma_handler(struct rdma_cm_id *cma_id, struct rdma_cm_event *event)
+{
+ int ret = 0;
+
+ iser_err("event %d conn %p id %p\n",event->event,cma_id->context,cma_id);
+
+ switch (event->event) {
+ case RDMA_CM_EVENT_ADDR_RESOLVED:
+ iser_addr_handler(cma_id);
+ break;
+ case RDMA_CM_EVENT_ROUTE_RESOLVED:
+ iser_route_handler(cma_id);
+ break;
+ case RDMA_CM_EVENT_ESTABLISHED:
+ iser_connected_handler(cma_id);
+ break;
+ case RDMA_CM_EVENT_ADDR_ERROR:
+ case RDMA_CM_EVENT_ROUTE_ERROR:
+ case RDMA_CM_EVENT_CONNECT_ERROR:
+ case RDMA_CM_EVENT_UNREACHABLE:
+ case RDMA_CM_EVENT_REJECTED:
+ iser_err("event: %d, error: %d\n", event->event, event->status);
+ iser_connect_error(cma_id);
+ break;
+ case RDMA_CM_EVENT_DISCONNECTED:
+ iser_disconnected_handler(cma_id);
+ break;
+ case RDMA_CM_EVENT_DEVICE_REMOVAL:
+ BUG();
+ break;
+ case RDMA_CM_EVENT_CONNECT_RESPONSE:
+ BUG();
+ break;
+ case RDMA_CM_EVENT_CONNECT_REQUEST:
+ default:
+ break;
+ }
+ return ret;
+}
+
+int iser_conn_init(struct iser_conn **ibconn)
+{
+ struct iser_conn *ib_conn;
+
+ ib_conn = kzalloc(sizeof *ib_conn, GFP_KERNEL);
+ if (!ib_conn) {
+ iser_err("can't alloc memory for struct iser_conn\n");
+ return -ENOMEM;
+ }
+ ib_conn->state = ISER_CONN_INIT;
+ init_waitqueue_head(&ib_conn->wait);
+ atomic_set(&ib_conn->post_recv_buf_count, 0);
+ atomic_set(&ib_conn->post_send_buf_count, 0);
+ INIT_WORK(&ib_conn->comperror_work, iser_comp_error_worker,
+ ib_conn);
+ INIT_LIST_HEAD(&ib_conn->conn_list);
+ spin_lock_init(&ib_conn->lock);
+
+ *ibconn = ib_conn;
+ return 0;
+}
+
+ /**
+ * starts the process of connecting to the target
+ * sleeps untill the connection is established or rejected
+ */
+int iser_connect(struct iser_conn *ib_conn,
+ struct sockaddr_in *src_addr,
+ struct sockaddr_in *dst_addr,
+ int non_blocking)
+{
+ struct sockaddr *src, *dst;
+ int err = 0;
+
+ sprintf(ib_conn->name,"%d.%d.%d.%d:%d",
+ NIPQUAD(dst_addr->sin_addr.s_addr), dst_addr->sin_port);
+
+ /* the device is known only --after-- address resolution */
+ ib_conn->device = NULL;
+
+ iser_err("connecting to: %d.%d.%d.%d, port 0x%x\n",
+ NIPQUAD(dst_addr->sin_addr), dst_addr->sin_port);
+
+ ib_conn->state = ISER_CONN_PENDING;
+
+ ib_conn->cma_id = rdma_create_id(iser_cma_handler,
+ (void *)ib_conn,
+ RDMA_PS_TCP);
+ if (IS_ERR(ib_conn->cma_id)) {
+ err = PTR_ERR(ib_conn->cma_id);
+ iser_err("rdma_create_id failed: %d\n", err);
+ goto id_failure;
+ }
+
+ src = (struct sockaddr *)src_addr;
+ dst = (struct sockaddr *)dst_addr;
+ err = rdma_resolve_addr(ib_conn->cma_id, src, dst, 1000);
+ if (err) {
+ iser_err("rdma_resolve_addr failed: %d\n", err);
+ goto addr_failure;
+ }
+
+ if (!non_blocking) {
+ wait_event_interruptible(ib_conn->wait,
+ (ib_conn->state != ISER_CONN_PENDING));
+
+ if (ib_conn->state != ISER_CONN_UP) {
+ err = -EIO;
+ goto connect_failure;
+ }
+ }
+
+ mutex_lock(&ig.connlist_mutex);
+ list_add(&ib_conn->conn_list, &ig.connlist);
+ mutex_unlock(&ig.connlist_mutex);
+ return 0;
+
+id_failure:
+ ib_conn->cma_id = NULL;
+addr_failure:
+ ib_conn->state = ISER_CONN_DOWN;
+connect_failure:
+ iser_conn_release(ib_conn);
+ return err;
+}
+
+/**
+ * Frees all conn objects and deallocs conn descriptor
+ */
+void iser_conn_release(struct iser_conn *ib_conn)
+{
+ struct iser_device *device = ib_conn->device;
+
+ BUG_ON(ib_conn->state != ISER_CONN_DOWN);
+
+ mutex_lock(&ig.connlist_mutex);
+ list_del(&ib_conn->conn_list);
+ mutex_unlock(&ig.connlist_mutex);
+
+ iser_free_ib_conn_res(ib_conn);
+ ib_conn->device = NULL;
+ /* on EVENT_ADDR_ERROR there's no device yet for this conn */
+ if (device != NULL)
+ iser_device_try_release(device);
+ kfree(ib_conn);
+}
+
+
+/**
+ * iser_reg_page_vec - Register physical memory
+ *
+ * returns: 0 on success, errno code on failure
+ */
+int iser_reg_page_vec(struct iser_conn *ib_conn,
+ struct iser_page_vec *page_vec,
+ struct iser_mem_reg *mem_reg)
+{
+ struct ib_pool_fmr *mem;
+ u64 io_addr;
+ u64 *page_list;
+ int status;
+
+ page_list = page_vec->pages;
+ io_addr = page_list[0];
+
+ mem = ib_fmr_pool_map_phys(ib_conn->fmr_pool,
+ page_list,
+ page_vec->length,
+ &io_addr);
+
+ if (IS_ERR(mem)) {
+ status = (int)PTR_ERR(mem);
+ iser_err("ib_fmr_pool_map_phys failed: %d\n", status);
+ return status;
+ }
+
+ mem_reg->lkey = mem->fmr->lkey;
+ mem_reg->rkey = mem->fmr->rkey;
+ mem_reg->len = page_vec->length * PAGE_SIZE;
+ mem_reg->va = io_addr;
+ mem_reg->mem_h = (void *)mem;
+
+ mem_reg->va += page_vec->offset;
+ mem_reg->len = page_vec->data_size;
+
+ iser_dbg("PHYSICAL Mem.register, [PHYS p_array: 0x%p, sz: %d, "
+ "entry[0]: (0x%08lx,%ld)] -> "
+ "[lkey: 0x%08X mem_h: 0x%p va: 0x%08lX sz: %ld]\n",
+ page_vec, page_vec->length,
+ (unsigned long)page_vec->pages[0],
+ (unsigned long)page_vec->data_size,
+ (unsigned int)mem_reg->lkey, mem_reg->mem_h,
+ (unsigned long)mem_reg->va, (unsigned long)mem_reg->len);
+ return 0;
+}
+
+/**
+ * Unregister (previosuly registered) memory.
+ */
+void iser_unreg_mem(struct iser_mem_reg *reg)
+{
+ int ret;
+
+ iser_dbg("PHYSICAL Mem.Unregister mem_h %p\n",reg->mem_h);
+
+ ret = ib_fmr_pool_unmap((struct ib_pool_fmr *)reg->mem_h);
+ if (ret)
+ iser_err("ib_fmr_pool_unmap failed %d\n", ret);
+
+ reg->mem_h = NULL;
+}
+
+/**
+ * iser_dto_to_iov - builds IOV from a dto descriptor
+ */
+static void iser_dto_to_iov(struct iser_dto *dto, struct ib_sge *iov, int iov_len)
+{
+ int i;
+ struct ib_sge *sge;
+ struct iser_regd_buf *regd_buf;
+
+ if (dto->regd_vector_len > iov_len) {
+ iser_err("iov size %d too small for posting dto of len %d\n",
+ iov_len, dto->regd_vector_len);
+ BUG();
+ }
+
+ for (i = 0; i < dto->regd_vector_len; i++) {
+ sge = &iov[i];
+ regd_buf = dto->regd[i];
+
+ sge->addr = regd_buf->reg.va;
+ sge->length = regd_buf->reg.len;
+ sge->lkey = regd_buf->reg.lkey;
+
+ if (dto->used_sz[i] > 0) /* Adjust size */
+ sge->length = dto->used_sz[i];
+
+ /* offset and length should not exceed the regd buf length */
+ if (sge->length + dto->offset[i] > regd_buf->reg.len) {
+ iser_err("Used len:%ld + offset:%d, exceed reg.buf.len:"
+ "%ld in dto:0x%p [%d], va:0x%08lX\n",
+ (unsigned long)sge->length, dto->offset[i],
+ (unsigned long)regd_buf->reg.len, dto, i,
+ (unsigned long)sge->addr);
+ BUG();
+ }
+
+ sge->addr += dto->offset[i]; /* Adjust offset */
+ }
+}
+
+/**
+ * iser_post_recv - Posts a receive buffer.
+ *
+ * returns 0 on success, -1 on failure
+ */
+int iser_post_recv(struct iser_desc *rx_desc)
+{
+ int ib_ret, ret_val = 0;
+ struct ib_recv_wr recv_wr, *recv_wr_failed;
+ struct ib_sge iov[2];
+ struct iser_conn *ib_conn;
+ struct iser_dto *recv_dto = &rx_desc->dto;
+
+ /* Retrieve conn */
+ ib_conn = recv_dto->conn->ib_conn;
+
+ iser_dto_to_iov(recv_dto, iov, 2);
+
+ recv_wr.next = NULL;
+ recv_wr.sg_list = iov;
+ recv_wr.num_sge = recv_dto->regd_vector_len;
+ recv_wr.wr_id = (unsigned long)rx_desc;
+
+ atomic_inc(&ib_conn->post_recv_buf_count);
+ ib_ret = ib_post_recv(ib_conn->qp, &recv_wr, &recv_wr_failed);
+ if (ib_ret) {
+ iser_err("ib_post_recv failed ret=%d\n", ib_ret);
+ atomic_dec(&ib_conn->post_recv_buf_count);
+ ret_val = -1;
+ }
+
+ return ret_val;
+}
+
+/**
+ * iser_start_send - Initiate a Send DTO operation
+ *
+ * returns 0 on success, -1 on failure
+ */
+int iser_post_send(struct iser_desc *tx_desc)
+{
+ int ib_ret, ret_val = 0;
+ struct ib_send_wr send_wr, *send_wr_failed;
+ struct ib_sge iov[MAX_REGD_BUF_VECTOR_LEN];
+ struct iser_conn *ib_conn;
+ struct iser_dto *dto = &tx_desc->dto;
+
+ ib_conn = dto->conn->ib_conn;
+
+ iser_dto_to_iov(dto, iov, MAX_REGD_BUF_VECTOR_LEN);
+
+ send_wr.next = NULL;
+ send_wr.wr_id = (unsigned long)tx_desc;
+ send_wr.sg_list = iov;
+ send_wr.num_sge = dto->regd_vector_len;
+ send_wr.opcode = IB_WR_SEND;
+ send_wr.send_flags = dto->notify_enable ? IB_SEND_SIGNALED : 0;
+
+ atomic_inc(&ib_conn->post_send_buf_count);
+
+ ib_ret = ib_post_send(ib_conn->qp, &send_wr, &send_wr_failed);
+ if (ib_ret) {
+ iser_err("Failed to start SEND DTO, dto: 0x%p, IOV len: %d\n",
+ dto, dto->regd_vector_len);
+ iser_err("ib_post_send failed, ret:%d\n", ib_ret);
+ atomic_dec(&ib_conn->post_send_buf_count);
+ ret_val = -1;
+ }
+
+ return ret_val;
+}
+
+static void iser_comp_error_worker(void *data)
+{
+ struct iser_conn *ib_conn = data;
+
+ /* getting here when the state is UP means that the conn is being *
+ * terminated asynchronously from the iSCSI layer's perspective. */
+ if (iser_conn_state_comp_exch(ib_conn, ISER_CONN_UP,
+ ISER_CONN_TERMINATING))
+ iscsi_conn_failure(ib_conn->iser_conn->iscsi_conn,
+ ISCSI_ERR_CONN_FAILED);
+
+ /* complete the termination process if disconnect event was delivered *
+ * note there are no more non completed posts to the QP */
+ if (ib_conn->disc_evt_flag) {
+ ib_conn->state = ISER_CONN_DOWN;
+ wake_up_interruptible(&ib_conn->wait);
+ }
+}
+
+static void iser_handle_comp_error(struct iser_desc *desc)
+{
+ struct iser_dto *dto = &desc->dto;
+ struct iser_conn *ib_conn = dto->conn->ib_conn;
+
+ iser_dto_buffs_release(dto);
+
+ if (desc->type == ISCSI_RX) {
+ kfree(desc->data);
+ kmem_cache_free(ig.desc_cache, desc);
+ atomic_dec(&ib_conn->post_recv_buf_count);
+ } else { /* type is TX control/command/dataout */
+ if (desc->type == ISCSI_TX_DATAOUT)
+ kmem_cache_free(ig.desc_cache, desc);
+ atomic_dec(&ib_conn->post_send_buf_count);
+ }
+
+ if (atomic_read(&ib_conn->post_recv_buf_count) == 0 &&
+ atomic_read(&ib_conn->post_send_buf_count) == 0)
+ schedule_work(&ib_conn->comperror_work);
+}
+
+static void iser_cq_tasklet_fn(unsigned long data)
+{
+ struct iser_device *device = (struct iser_device *)data;
+ struct ib_cq *cq = device->cq;
+ struct ib_wc wc;
+ struct iser_desc *desc;
+ unsigned long xfer_len;
+
+ while (ib_poll_cq(cq, 1, &wc) == 1) {
+ desc = (struct iser_desc *) (unsigned long) wc.wr_id;
+ BUG_ON(desc == NULL);
+
+ if (wc.status == IB_WC_SUCCESS) {
+ if (desc->type == ISCSI_RX) {
+ xfer_len = (unsigned long)wc.byte_len;
+ iser_rcv_completion(desc, xfer_len);
+ } else /* type == ISCSI_TX_CONTROL/SCSI_CMD/DOUT */
+ iser_snd_completion(desc);
+ } else {
+ iser_err("comp w. error op %d status %d\n",desc->type,wc.status);
+ iser_handle_comp_error(desc);
+ }
+ }
+ /* #warning "it is assumed here that arming CQ only once its empty" *
+ * " would not cause interrupts to be missed" */
+ ib_req_notify_cq(cq, IB_CQ_NEXT_COMP);
+}
+
+static void iser_cq_callback(struct ib_cq *cq, void *cq_context)
+{
+ struct iser_device *device = (struct iser_device *)cq_context;
+
+ tasklet_schedule(&device->cq_tasklet);
+}
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 5f561fce32d8..a29d5ceb00cf 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -78,14 +78,19 @@ 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_flush(struct file * file, fl_owner_t id)
+static int evdev_flush(struct file *file, fl_owner_t id)
{
struct evdev_list *list = file->private_data;
- if (!list->evdev->exist) return -ENODEV;
+
+ if (!list->evdev->exist)
+ return -ENODEV;
+
return input_flush_device(&list->evdev->handle, file);
}
@@ -300,6 +305,7 @@ static ssize_t evdev_read(struct file * file, char __user * buffer, size_t count
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);
return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
(list->evdev->exist ? 0 : (POLLHUP | POLLERR));
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 3038c268917d..a90486f5e491 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -28,20 +28,6 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
MODULE_DESCRIPTION("Input core");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(input_allocate_device);
-EXPORT_SYMBOL(input_register_device);
-EXPORT_SYMBOL(input_unregister_device);
-EXPORT_SYMBOL(input_register_handler);
-EXPORT_SYMBOL(input_unregister_handler);
-EXPORT_SYMBOL(input_grab_device);
-EXPORT_SYMBOL(input_release_device);
-EXPORT_SYMBOL(input_open_device);
-EXPORT_SYMBOL(input_close_device);
-EXPORT_SYMBOL(input_accept_process);
-EXPORT_SYMBOL(input_flush_device);
-EXPORT_SYMBOL(input_event);
-EXPORT_SYMBOL_GPL(input_class);
-
#define INPUT_DEVICES 256
static LIST_HEAD(input_dev_list);
@@ -63,11 +49,13 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
case EV_SYN:
switch (code) {
case SYN_CONFIG:
- if (dev->event) dev->event(dev, type, code, value);
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
case SYN_REPORT:
- if (dev->sync) return;
+ if (dev->sync)
+ return;
dev->sync = 1;
break;
}
@@ -136,7 +124,8 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
if (code > MSC_MAX || !test_bit(code, dev->mscbit))
return;
- if (dev->event) dev->event(dev, type, code, value);
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
@@ -146,7 +135,9 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
return;
change_bit(code, dev->led);
- if (dev->event) dev->event(dev, type, code, value);
+
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
@@ -158,21 +149,25 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
if (!!test_bit(code, dev->snd) != !!value)
change_bit(code, dev->snd);
- if (dev->event) dev->event(dev, type, code, value);
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
case EV_REP:
- if (code > REP_MAX || value < 0 || dev->rep[code] == value) return;
+ if (code > REP_MAX || value < 0 || dev->rep[code] == value)
+ return;
dev->rep[code] = value;
- if (dev->event) dev->event(dev, type, code, value);
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
case EV_FF:
- if (dev->event) dev->event(dev, type, code, value);
+ if (dev->event)
+ dev->event(dev, type, code, value);
break;
}
@@ -186,6 +181,7 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
if (handle->open)
handle->handler->event(handle, type, code, value);
}
+EXPORT_SYMBOL(input_event);
static void input_repeat_key(unsigned long data)
{
@@ -208,6 +204,7 @@ int input_accept_process(struct input_handle *handle, struct file *file)
return 0;
}
+EXPORT_SYMBOL(input_accept_process);
int input_grab_device(struct input_handle *handle)
{
@@ -217,12 +214,14 @@ int input_grab_device(struct input_handle *handle)
handle->dev->grab = handle;
return 0;
}
+EXPORT_SYMBOL(input_grab_device);
void input_release_device(struct input_handle *handle)
{
if (handle->dev->grab == handle)
handle->dev->grab = NULL;
}
+EXPORT_SYMBOL(input_release_device);
int input_open_device(struct input_handle *handle)
{
@@ -245,6 +244,7 @@ int input_open_device(struct input_handle *handle)
return err;
}
+EXPORT_SYMBOL(input_open_device);
int input_flush_device(struct input_handle* handle, struct file* file)
{
@@ -253,6 +253,7 @@ int input_flush_device(struct input_handle* handle, struct file* file)
return 0;
}
+EXPORT_SYMBOL(input_flush_device);
void input_close_device(struct input_handle *handle)
{
@@ -268,6 +269,7 @@ void input_close_device(struct input_handle *handle)
mutex_unlock(&dev->mutex);
}
+EXPORT_SYMBOL(input_close_device);
static void input_link_handle(struct input_handle *handle)
{
@@ -335,9 +337,11 @@ static inline void input_wakeup_procfs_readers(void)
static unsigned int input_proc_devices_poll(struct file *file, poll_table *wait)
{
int state = input_devices_state;
+
poll_wait(file, &input_devices_poll_wait, wait);
if (state != input_devices_state)
return POLLIN | POLLRDNORM;
+
return 0;
}
@@ -629,7 +633,7 @@ static ssize_t input_dev_show_modalias(struct class_device *dev, char *buf)
len = input_print_modalias(buf, PAGE_SIZE, id, 1);
- return max_t(int, len, PAGE_SIZE);
+ return min_t(int, len, PAGE_SIZE);
}
static CLASS_DEVICE_ATTR(modalias, S_IRUGO, input_dev_show_modalias, NULL);
@@ -862,6 +866,7 @@ struct class input_class = {
.release = input_dev_release,
.uevent = input_dev_uevent,
};
+EXPORT_SYMBOL_GPL(input_class);
struct input_dev *input_allocate_device(void)
{
@@ -872,12 +877,27 @@ struct input_dev *input_allocate_device(void)
dev->dynalloc = 1;
dev->cdev.class = &input_class;
class_device_initialize(&dev->cdev);
+ mutex_init(&dev->mutex);
INIT_LIST_HEAD(&dev->h_list);
INIT_LIST_HEAD(&dev->node);
}
return dev;
}
+EXPORT_SYMBOL(input_allocate_device);
+
+void input_free_device(struct input_dev *dev)
+{
+ if (dev) {
+
+ mutex_lock(&dev->mutex);
+ dev->name = dev->phys = dev->uniq = NULL;
+ mutex_unlock(&dev->mutex);
+
+ input_put_device(dev);
+ }
+}
+EXPORT_SYMBOL(input_free_device);
int input_register_device(struct input_dev *dev)
{
@@ -895,7 +915,6 @@ int input_register_device(struct input_dev *dev)
return -EINVAL;
}
- mutex_init(&dev->mutex);
set_bit(EV_SYN, dev->evbit);
/*
@@ -956,12 +975,14 @@ int input_register_device(struct input_dev *dev)
fail1: class_device_del(&dev->cdev);
return error;
}
+EXPORT_SYMBOL(input_register_device);
void input_unregister_device(struct input_dev *dev)
{
- struct list_head * node, * next;
+ struct list_head *node, *next;
- if (!dev) return;
+ if (!dev)
+ return;
del_timer_sync(&dev->timer);
@@ -977,10 +998,16 @@ void input_unregister_device(struct input_dev *dev)
sysfs_remove_group(&dev->cdev.kobj, &input_dev_caps_attr_group);
sysfs_remove_group(&dev->cdev.kobj, &input_dev_id_attr_group);
sysfs_remove_group(&dev->cdev.kobj, &input_dev_attr_group);
+
+ mutex_lock(&dev->mutex);
+ dev->name = dev->phys = dev->uniq = NULL;
+ mutex_unlock(&dev->mutex);
+
class_device_unregister(&dev->cdev);
input_wakeup_procfs_readers();
}
+EXPORT_SYMBOL(input_unregister_device);
void input_register_handler(struct input_handler *handler)
{
@@ -988,7 +1015,8 @@ void input_register_handler(struct input_handler *handler)
struct input_handle *handle;
struct input_device_id *id;
- if (!handler) return;
+ if (!handler)
+ return;
INIT_LIST_HEAD(&handler->h_list);
@@ -1005,10 +1033,11 @@ void input_register_handler(struct input_handler *handler)
input_wakeup_procfs_readers();
}
+EXPORT_SYMBOL(input_register_handler);
void input_unregister_handler(struct input_handler *handler)
{
- struct list_head * node, * next;
+ struct list_head *node, *next;
list_for_each_safe(node, next, &handler->h_list) {
struct input_handle * handle = to_handle_h(node);
@@ -1024,6 +1053,7 @@ void input_unregister_handler(struct input_handler *handler)
input_wakeup_procfs_readers();
}
+EXPORT_SYMBOL(input_unregister_handler);
static int input_open_file(struct inode *inode, struct file *file)
{
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index 949bdcef8c2b..d67157513bf7 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -81,10 +81,7 @@ static int joydev_correct(int value, struct js_corr *corr)
return 0;
}
- if (value < -32767) return -32767;
- if (value > 32767) return 32767;
-
- return value;
+ return value < -32767 ? -32767 : (value > 32767 ? 32767 : value);
}
static void joydev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
@@ -96,7 +93,8 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
switch (type) {
case EV_KEY:
- if (code < BTN_MISC || value == 2) return;
+ if (code < BTN_MISC || value == 2)
+ return;
event.type = JS_EVENT_BUTTON;
event.number = joydev->keymap[code - BTN_MISC];
event.value = value;
@@ -106,7 +104,8 @@ static void joydev_event(struct input_handle *handle, unsigned int type, unsigne
event.type = JS_EVENT_AXIS;
event.number = joydev->absmap[code];
event.value = joydev_correct(value, joydev->corr + event.number);
- if (event.value == joydev->abs[event.number]) return;
+ if (event.value == joydev->abs[event.number])
+ return;
joydev->abs[event.number] = event.value;
break;
@@ -134,7 +133,9 @@ 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;
}
@@ -222,12 +223,12 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
return sizeof(struct JS_DATA_TYPE);
}
- if (list->startup == joydev->nabs + joydev->nkey
- && list->head == list->tail && (file->f_flags & O_NONBLOCK))
- return -EAGAIN;
+ if (list->startup == joydev->nabs + joydev->nkey &&
+ list->head == list->tail && (file->f_flags & O_NONBLOCK))
+ return -EAGAIN;
retval = wait_event_interruptible(list->joydev->wait,
- !list->joydev->exist ||
+ !list->joydev->exist ||
list->startup < joydev->nabs + joydev->nkey ||
list->head != list->tail);
@@ -276,8 +277,9 @@ static ssize_t joydev_read(struct file *file, char __user *buf, size_t count, lo
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);
- return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ?
+ return ((list->head != list->tail || list->startup < list->joydev->nabs + list->joydev->nkey) ?
(POLLIN | POLLRDNORM) : 0) | (list->joydev->exist ? 0 : (POLLHUP | POLLERR));
}
@@ -291,20 +293,26 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
case JS_SET_CAL:
return copy_from_user(&joydev->glue.JS_CORR, argp,
sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+
case JS_GET_CAL:
return copy_to_user(argp, &joydev->glue.JS_CORR,
sizeof(joydev->glue.JS_CORR)) ? -EFAULT : 0;
+
case JS_SET_TIMEOUT:
return get_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
+
case JS_GET_TIMEOUT:
return put_user(joydev->glue.JS_TIMEOUT, (s32 __user *) argp);
case JSIOCGVERSION:
return put_user(JS_VERSION, (__u32 __user *) argp);
+
case JSIOCGAXES:
return put_user(joydev->nabs, (__u8 __user *) argp);
+
case JSIOCGBUTTONS:
return put_user(joydev->nkey, (__u8 __user *) argp);
+
case JSIOCSCORR:
if (copy_from_user(joydev->corr, argp,
sizeof(joydev->corr[0]) * joydev->nabs))
@@ -314,38 +322,49 @@ static int joydev_ioctl_common(struct joydev *joydev, unsigned int cmd, void __u
joydev->abs[i] = joydev_correct(dev->abs[j], joydev->corr + i);
}
return 0;
+
case JSIOCGCORR:
return copy_to_user(argp, joydev->corr,
sizeof(joydev->corr[0]) * joydev->nabs) ? -EFAULT : 0;
+
case JSIOCSAXMAP:
if (copy_from_user(joydev->abspam, argp, sizeof(__u8) * (ABS_MAX + 1)))
return -EFAULT;
for (i = 0; i < joydev->nabs; i++) {
- if (joydev->abspam[i] > ABS_MAX) return -EINVAL;
+ if (joydev->abspam[i] > ABS_MAX)
+ return -EINVAL;
joydev->absmap[joydev->abspam[i]] = i;
}
return 0;
+
case JSIOCGAXMAP:
return copy_to_user(argp, joydev->abspam,
sizeof(__u8) * (ABS_MAX + 1)) ? -EFAULT : 0;
+
case JSIOCSBTNMAP:
if (copy_from_user(joydev->keypam, argp, sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)))
return -EFAULT;
for (i = 0; i < joydev->nkey; i++) {
- if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC) return -EINVAL;
+ if (joydev->keypam[i] > KEY_MAX || joydev->keypam[i] < BTN_MISC)
+ return -EINVAL;
joydev->keymap[joydev->keypam[i] - BTN_MISC] = i;
}
return 0;
+
case JSIOCGBTNMAP:
return copy_to_user(argp, joydev->keypam,
sizeof(__u16) * (KEY_MAX - BTN_MISC + 1)) ? -EFAULT : 0;
+
default:
if ((cmd & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT)) == JSIOCGNAME(0)) {
int len;
- if (!dev->name) return 0;
+ if (!dev->name)
+ return 0;
len = strlen(dev->name) + 1;
- if (len > _IOC_SIZE(cmd)) len = _IOC_SIZE(cmd);
- if (copy_to_user(argp, dev->name, len)) return -EFAULT;
+ if (len > _IOC_SIZE(cmd))
+ len = _IOC_SIZE(cmd);
+ if (copy_to_user(argp, dev->name, len))
+ return -EFAULT;
return len;
}
}
@@ -362,7 +381,9 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
struct JS_DATA_SAVE_TYPE_32 ds32;
int err;
- if (!joydev->exist) return -ENODEV;
+ if (!joydev->exist)
+ return -ENODEV;
+
switch(cmd) {
case JS_SET_TIMELIMIT:
err = get_user(tmp32, (s32 __user *) arg);
@@ -395,8 +416,7 @@ static long joydev_compat_ioctl(struct file *file, unsigned int cmd, unsigned lo
ds32.JS_SAVE = joydev->glue.JS_SAVE;
ds32.JS_CORR = joydev->glue.JS_CORR;
- err = copy_to_user(argp, &ds32,
- sizeof(ds32)) ? -EFAULT : 0;
+ err = copy_to_user(argp, &ds32, sizeof(ds32)) ? -EFAULT : 0;
break;
default:
@@ -412,7 +432,8 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
struct joydev *joydev = list->joydev;
void __user *argp = (void __user *)arg;
- if (!joydev->exist) return -ENODEV;
+ if (!joydev->exist)
+ return -ENODEV;
switch(cmd) {
case JS_SET_TIMELIMIT:
@@ -546,8 +567,8 @@ static struct input_device_id joydev_blacklist[] = {
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT(EV_KEY) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
- }, /* Avoid itouchpads, touchscreens and tablets */
- { }, /* Terminating entry */
+ }, /* Avoid itouchpads, touchscreens and tablets */
+ { } /* Terminating entry */
};
static struct input_device_id joydev_ids[] = {
@@ -566,7 +587,7 @@ static struct input_device_id joydev_ids[] = {
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_THROTTLE) },
},
- { }, /* Terminating entry */
+ { } /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, joydev_ids);
@@ -579,7 +600,7 @@ static struct input_handler joydev_handler = {
.minor = JOYDEV_MINOR_BASE,
.name = "joydev",
.id_table = joydev_ids,
- .blacklist = joydev_blacklist,
+ .blacklist = joydev_blacklist,
};
static int __init joydev_init(void)
diff --git a/drivers/input/joystick/a3d.c b/drivers/input/joystick/a3d.c
index 4612d13ea756..b11a4bbc84c4 100644
--- a/drivers/input/joystick/a3d.c
+++ b/drivers/input/joystick/a3d.c
@@ -306,7 +306,7 @@ static int a3d_connect(struct gameport *gameport, struct gameport_driver *drv)
gameport_set_poll_handler(gameport, a3d_poll);
gameport_set_poll_interval(gameport, 20);
- sprintf(a3d->phys, "%s/input0", gameport->phys);
+ snprintf(a3d->phys, sizeof(a3d->phys), "%s/input0", gameport->phys);
input_dev->name = a3d_names[a3d->mode];
input_dev->phys = a3d->phys;
diff --git a/drivers/input/joystick/analog.c b/drivers/input/joystick/analog.c
index 3121961e3e7c..01dc0b195d59 100644
--- a/drivers/input/joystick/analog.c
+++ b/drivers/input/joystick/analog.c
@@ -408,21 +408,23 @@ static void analog_calibrate_timer(struct analog_port *port)
static void analog_name(struct analog *analog)
{
- sprintf(analog->name, "Analog %d-axis %d-button",
- hweight8(analog->mask & ANALOG_AXES_STD),
- hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
- hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
+ snprintf(analog->name, sizeof(analog->name), "Analog %d-axis %d-button",
+ hweight8(analog->mask & ANALOG_AXES_STD),
+ hweight8(analog->mask & ANALOG_BTNS_STD) + !!(analog->mask & ANALOG_BTNS_CHF) * 2 +
+ hweight16(analog->mask & ANALOG_BTNS_GAMEPAD) + !!(analog->mask & ANALOG_HBTN_CHF) * 4);
if (analog->mask & ANALOG_HATS_ALL)
- sprintf(analog->name, "%s %d-hat",
- analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
+ snprintf(analog->name, sizeof(analog->name), "%s %d-hat",
+ analog->name, hweight16(analog->mask & ANALOG_HATS_ALL));
if (analog->mask & ANALOG_HAT_FCS)
- strcat(analog->name, " FCS");
+ strlcat(analog->name, " FCS", sizeof(analog->name));
if (analog->mask & ANALOG_ANY_CHF)
- strcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF");
+ strlcat(analog->name, (analog->mask & ANALOG_SAITEK) ? " Saitek" : " CHF",
+ sizeof(analog->name));
- strcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick");
+ strlcat(analog->name, (analog->mask & ANALOG_GAMEPAD) ? " gamepad": " joystick",
+ sizeof(analog->name));
}
/*
@@ -435,7 +437,8 @@ static int analog_init_device(struct analog_port *port, struct analog *analog, i
int i, j, t, v, w, x, y, z;
analog_name(analog);
- sprintf(analog->phys, "%s/input%d", port->gameport->phys, index);
+ snprintf(analog->phys, sizeof(analog->phys),
+ "%s/input%d", port->gameport->phys, index);
analog->buttons = (analog->mask & ANALOG_GAMEPAD) ? analog_pad_btn : analog_joy_btn;
analog->dev = input_dev = input_allocate_device();
diff --git a/drivers/input/joystick/cobra.c b/drivers/input/joystick/cobra.c
index 1909f7ef340c..d5e42eb88a20 100644
--- a/drivers/input/joystick/cobra.c
+++ b/drivers/input/joystick/cobra.c
@@ -202,7 +202,8 @@ static int cobra_connect(struct gameport *gameport, struct gameport_driver *drv)
goto fail3;
}
- sprintf(cobra->phys[i], "%s/input%d", gameport->phys, i);
+ snprintf(cobra->phys[i], sizeof(cobra->phys[i]),
+ "%s/input%d", gameport->phys, i);
input_dev->name = "Creative Labs Blaster GamePad Cobra";
input_dev->phys = cobra->phys[i];
diff --git a/drivers/input/joystick/db9.c b/drivers/input/joystick/db9.c
index e61894685cb1..5080e15c6d30 100644
--- a/drivers/input/joystick/db9.c
+++ b/drivers/input/joystick/db9.c
@@ -584,7 +584,7 @@ static struct db9 __init *db9_probe(int parport, int mode)
goto err_out;
}
- if (db9_mode[mode].bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
+ if (db9_mode->bidirectional && !(pp->modes & PARPORT_MODE_TRISTATE)) {
printk(KERN_ERR "db9.c: specified parport is not bidirectional\n");
err = -EINVAL;
goto err_put_pp;
@@ -620,7 +620,8 @@ static struct db9 __init *db9_probe(int parport, int mode)
goto err_unreg_devs;
}
- sprintf(db9->phys[i], "%s/input%d", db9->pd->port->name, i);
+ snprintf(db9->phys[i], sizeof(db9->phys[i]),
+ "%s/input%d", db9->pd->port->name, i);
input_dev->name = db9_mode->name;
input_dev->phys = db9->phys[i];
diff --git a/drivers/input/joystick/gamecon.c b/drivers/input/joystick/gamecon.c
index ecbdb6b9bbd6..fe12aa37393d 100644
--- a/drivers/input/joystick/gamecon.c
+++ b/drivers/input/joystick/gamecon.c
@@ -761,7 +761,8 @@ static struct gc __init *gc_probe(int parport, int *pads, int n_pads)
if (!pads[i])
continue;
- sprintf(gc->phys[i], "%s/input%d", gc->pd->port->name, i);
+ snprintf(gc->phys[i], sizeof(gc->phys[i]),
+ "%s/input%d", gc->pd->port->name, i);
err = gc_setup_pad(gc, i, pads[i]);
if (err)
goto err_unreg_devs;
diff --git a/drivers/input/joystick/gf2k.c b/drivers/input/joystick/gf2k.c
index 8a3ad455eb38..e4a699f6ec87 100644
--- a/drivers/input/joystick/gf2k.c
+++ b/drivers/input/joystick/gf2k.c
@@ -298,7 +298,7 @@ static int gf2k_connect(struct gameport *gameport, struct gameport_driver *drv)
gameport_set_poll_handler(gameport, gf2k_poll);
gameport_set_poll_interval(gameport, 20);
- sprintf(gf2k->phys, "%s/input0", gameport->phys);
+ snprintf(gf2k->phys, sizeof(gf2k->phys), "%s/input0", gameport->phys);
gf2k->length = gf2k_lens[gf2k->id];
diff --git a/drivers/input/joystick/grip.c b/drivers/input/joystick/grip.c
index 20cb98ac2d79..17a90c436de8 100644
--- a/drivers/input/joystick/grip.c
+++ b/drivers/input/joystick/grip.c
@@ -354,7 +354,8 @@ static int grip_connect(struct gameport *gameport, struct gameport_driver *drv)
goto fail3;
}
- sprintf(grip->phys[i], "%s/input%d", gameport->phys, i);
+ snprintf(grip->phys[i], sizeof(grip->phys[i]),
+ "%s/input%d", gameport->phys, i);
input_dev->name = grip_name[grip->mode[i]];
input_dev->phys = grip->phys[i];
diff --git a/drivers/input/joystick/guillemot.c b/drivers/input/joystick/guillemot.c
index 6e2c721c26ba..840ed9b512b2 100644
--- a/drivers/input/joystick/guillemot.c
+++ b/drivers/input/joystick/guillemot.c
@@ -222,7 +222,7 @@ static int guillemot_connect(struct gameport *gameport, struct gameport_driver *
gameport_set_poll_handler(gameport, guillemot_poll);
gameport_set_poll_interval(gameport, 20);
- sprintf(guillemot->phys, "%s/input0", gameport->phys);
+ snprintf(guillemot->phys, sizeof(guillemot->phys), "%s/input0", gameport->phys);
guillemot->type = guillemot_type + i;
input_dev->name = guillemot_type[i].name;
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
index 2b8e8456c9fa..50c90765aee1 100644
--- a/drivers/input/joystick/iforce/iforce-ff.c
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -47,7 +47,7 @@ static int make_magnitude_modifier(struct iforce* iforce,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
mutex_unlock(&iforce->mem_mutex);
- return -ENOMEM;
+ return -ENOSPC;
}
mutex_unlock(&iforce->mem_mutex);
}
@@ -80,7 +80,7 @@ static int make_period_modifier(struct iforce* iforce,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
mutex_unlock(&iforce->mem_mutex);
- return -ENOMEM;
+ return -ENOSPC;
}
mutex_unlock(&iforce->mem_mutex);
}
@@ -120,7 +120,7 @@ static int make_envelope_modifier(struct iforce* iforce,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
mutex_unlock(&iforce->mem_mutex);
- return -ENOMEM;
+ return -ENOSPC;
}
mutex_unlock(&iforce->mem_mutex);
}
@@ -157,7 +157,7 @@ static int make_condition_modifier(struct iforce* iforce,
iforce->device_memory.start, iforce->device_memory.end, 2L,
NULL, NULL)) {
mutex_unlock(&iforce->mem_mutex);
- return -ENOMEM;
+ return -ENOSPC;
}
mutex_unlock(&iforce->mem_mutex);
}
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index ab0a26b924ca..6d99e3c37884 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -86,7 +86,7 @@ static struct iforce_device iforce_device[] = {
static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
- struct iforce* iforce = (struct iforce*)(dev->private);
+ struct iforce* iforce = dev->private;
unsigned char data[3];
if (type != EV_FF)
@@ -138,7 +138,7 @@ static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned
*/
static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
{
- struct iforce* iforce = (struct iforce*)(dev->private);
+ struct iforce* iforce = dev->private;
int id;
int ret;
int is_update;
@@ -218,7 +218,7 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
*/
static int iforce_erase_effect(struct input_dev *dev, int effect_id)
{
- struct iforce* iforce = (struct iforce*)(dev->private);
+ struct iforce* iforce = dev->private;
int err = 0;
struct iforce_core_effect* core_effect;
diff --git a/drivers/input/joystick/interact.c b/drivers/input/joystick/interact.c
index c4ed01758226..bbfeb9c59b87 100644
--- a/drivers/input/joystick/interact.c
+++ b/drivers/input/joystick/interact.c
@@ -251,7 +251,7 @@ static int interact_connect(struct gameport *gameport, struct gameport_driver *d
gameport_set_poll_handler(gameport, interact_poll);
gameport_set_poll_interval(gameport, 20);
- sprintf(interact->phys, "%s/input0", gameport->phys);
+ snprintf(interact->phys, sizeof(interact->phys), "%s/input0", gameport->phys);
interact->type = i;
interact->length = interact_type[i].length;
diff --git a/drivers/input/joystick/magellan.c b/drivers/input/joystick/magellan.c
index ca3cc2319d6a..168b1061a03b 100644
--- a/drivers/input/joystick/magellan.c
+++ b/drivers/input/joystick/magellan.c
@@ -162,7 +162,7 @@ static int magellan_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
magellan->dev = input_dev;
- sprintf(magellan->phys, "%s/input0", serio->phys);
+ snprintf(magellan->phys, sizeof(magellan->phys), "%s/input0", serio->phys);
input_dev->name = "LogiCad3D Magellan / SpaceMouse";
input_dev->phys = magellan->phys;
diff --git a/drivers/input/joystick/sidewinder.c b/drivers/input/joystick/sidewinder.c
index 95c0de7964a0..e58b22c018e4 100644
--- a/drivers/input/joystick/sidewinder.c
+++ b/drivers/input/joystick/sidewinder.c
@@ -541,7 +541,7 @@ static void sw_print_packet(char *name, int length, unsigned char *buf, char bit
* Unfortunately I don't know how to do this for the other SW types.
*/
-static void sw_3dp_id(unsigned char *buf, char *comment)
+static void sw_3dp_id(unsigned char *buf, char *comment, size_t size)
{
int i;
char pnp[8], rev[9];
@@ -554,7 +554,7 @@ static void sw_3dp_id(unsigned char *buf, char *comment)
pnp[7] = rev[8] = 0;
- sprintf(comment, " [PnP %d.%02d id %s rev %s]",
+ snprintf(comment, size, " [PnP %d.%02d id %s rev %s]",
(int) ((sw_get_bits(buf, 8, 6, 1) << 6) | /* Two 6-bit values */
sw_get_bits(buf, 16, 6, 1)) / 100,
(int) ((sw_get_bits(buf, 8, 6, 1) << 6) |
@@ -695,7 +695,7 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
sw->type = SW_ID_FFP;
sprintf(comment, " [AC %s]", sw_get_bits(idbuf,38,1,3) ? "off" : "on");
} else
- sw->type = SW_ID_PP;
+ sw->type = SW_ID_PP;
break;
case 66:
sw->bits = 3;
@@ -703,7 +703,8 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
sw->length = 22;
case 64:
sw->type = SW_ID_3DP;
- if (j == 160) sw_3dp_id(idbuf, comment);
+ if (j == 160)
+ sw_3dp_id(idbuf, comment, sizeof(comment));
break;
}
}
@@ -733,8 +734,10 @@ static int sw_connect(struct gameport *gameport, struct gameport_driver *drv)
for (i = 0; i < sw->number; i++) {
int bits, code;
- sprintf(sw->name, "Microsoft SideWinder %s", sw_name[sw->type]);
- sprintf(sw->phys[i], "%s/input%d", gameport->phys, i);
+ snprintf(sw->name, sizeof(sw->name),
+ "Microsoft SideWinder %s", sw_name[sw->type]);
+ snprintf(sw->phys[i], sizeof(sw->phys[i]),
+ "%s/input%d", gameport->phys, i);
sw->dev[i] = input_dev = input_allocate_device();
if (!input_dev) {
diff --git a/drivers/input/joystick/spaceball.c b/drivers/input/joystick/spaceball.c
index d6f8db8ec3fd..75eb5ca59992 100644
--- a/drivers/input/joystick/spaceball.c
+++ b/drivers/input/joystick/spaceball.c
@@ -220,7 +220,7 @@ static int spaceball_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
spaceball->dev = input_dev;
- sprintf(spaceball->phys, "%s/input0", serio->phys);
+ snprintf(spaceball->phys, sizeof(spaceball->phys), "%s/input0", serio->phys);
input_dev->name = spaceball_names[id];
input_dev->phys = spaceball->phys;
diff --git a/drivers/input/joystick/spaceorb.c b/drivers/input/joystick/spaceorb.c
index 7c123a01c58e..3e2782e79834 100644
--- a/drivers/input/joystick/spaceorb.c
+++ b/drivers/input/joystick/spaceorb.c
@@ -177,7 +177,7 @@ static int spaceorb_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
spaceorb->dev = input_dev;
- sprintf(spaceorb->phys, "%s/input0", serio->phys);
+ snprintf(spaceorb->phys, sizeof(spaceorb->phys), "%s/input0", serio->phys);
input_dev->name = "SpaceTec SpaceOrb 360 / Avenger";
input_dev->phys = spaceorb->phys;
diff --git a/drivers/input/joystick/stinger.c b/drivers/input/joystick/stinger.c
index 0a9ed1d30636..011ec4858e15 100644
--- a/drivers/input/joystick/stinger.c
+++ b/drivers/input/joystick/stinger.c
@@ -148,7 +148,7 @@ static int stinger_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
stinger->dev = input_dev;
- sprintf(stinger->phys, "%s/serio0", serio->phys);
+ snprintf(stinger->phys, sizeof(stinger->phys), "%s/serio0", serio->phys);
input_dev->name = "Gravis Stinger";
input_dev->phys = stinger->phys;
diff --git a/drivers/input/joystick/twidjoy.c b/drivers/input/joystick/twidjoy.c
index 7f8b0093c5bc..076f237d9654 100644
--- a/drivers/input/joystick/twidjoy.c
+++ b/drivers/input/joystick/twidjoy.c
@@ -199,7 +199,7 @@ static int twidjoy_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
twidjoy->dev = input_dev;
- sprintf(twidjoy->phys, "%s/input0", serio->phys);
+ snprintf(twidjoy->phys, sizeof(twidjoy->phys), "%s/input0", serio->phys);
input_dev->name = "Handykey Twiddler";
input_dev->phys = twidjoy->phys;
diff --git a/drivers/input/joystick/warrior.c b/drivers/input/joystick/warrior.c
index 1849b176cf18..f9c1a03214eb 100644
--- a/drivers/input/joystick/warrior.c
+++ b/drivers/input/joystick/warrior.c
@@ -154,7 +154,7 @@ static int warrior_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
warrior->dev = input_dev;
- sprintf(warrior->phys, "%s/input0", serio->phys);
+ snprintf(warrior->phys, sizeof(warrior->phys), "%s/input0", serio->phys);
input_dev->name = "Logitech WingMan Warrior";
input_dev->phys = warrior->phys;
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index fad04b66d268..ce1f10e8984b 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -55,7 +55,7 @@ static int atkbd_softraw = 1;
module_param_named(softraw, atkbd_softraw, bool, 0);
MODULE_PARM_DESC(softraw, "Use software generated rawmode");
-static int atkbd_scroll = 0;
+static int atkbd_scroll;
module_param_named(scroll, atkbd_scroll, bool, 0);
MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
@@ -150,8 +150,8 @@ static unsigned char atkbd_unxlate_table[128] = {
#define ATKBD_RET_EMUL0 0xe0
#define ATKBD_RET_EMUL1 0xe1
#define ATKBD_RET_RELEASE 0xf0
-#define ATKBD_RET_HANGUEL 0xf1
-#define ATKBD_RET_HANJA 0xf2
+#define ATKBD_RET_HANJA 0xf1
+#define ATKBD_RET_HANGEUL 0xf2
#define ATKBD_RET_ERR 0xff
#define ATKBD_KEY_UNKNOWN 0
@@ -170,6 +170,13 @@ static unsigned char atkbd_unxlate_table[128] = {
#define ATKBD_LED_EVENT_BIT 0
#define ATKBD_REP_EVENT_BIT 1
+#define ATKBD_XL_ERR 0x01
+#define ATKBD_XL_BAT 0x02
+#define ATKBD_XL_ACK 0x04
+#define ATKBD_XL_NAK 0x08
+#define ATKBD_XL_HANGEUL 0x10
+#define ATKBD_XL_HANJA 0x20
+
static struct {
unsigned char keycode;
unsigned char set2;
@@ -211,8 +218,7 @@ struct atkbd {
unsigned char emul;
unsigned char resend;
unsigned char release;
- unsigned char bat_xl;
- unsigned char err_xl;
+ unsigned long xl_bit;
unsigned int last;
unsigned long time;
@@ -245,17 +251,65 @@ ATKBD_DEFINE_ATTR(set);
ATKBD_DEFINE_ATTR(softrepeat);
ATKBD_DEFINE_ATTR(softraw);
+static const unsigned int xl_table[] = {
+ ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
+ ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
+};
-static void atkbd_report_key(struct input_dev *dev, struct pt_regs *regs, int code, int value)
+/*
+ * Checks if we should mangle the scancode to extract 'release' bit
+ * in translated mode.
+ */
+static int atkbd_need_xlate(unsigned long xl_bit, unsigned char code)
{
- input_regs(dev, regs);
- if (value == 3) {
- input_report_key(dev, code, 1);
- input_sync(dev);
- input_report_key(dev, code, 0);
- } else
- input_event(dev, EV_KEY, code, value);
- input_sync(dev);
+ int i;
+
+ if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(xl_table); i++)
+ if (code == xl_table[i])
+ return test_bit(i, &xl_bit);
+
+ return 1;
+}
+
+/*
+ * Calculates new value of xl_bit so the driver can distinguish
+ * between make/break pair of scancodes for select keys and PS/2
+ * protocol responses.
+ */
+static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xl_table); i++) {
+ if (!((code ^ xl_table[i]) & 0x7f)) {
+ if (code & 0x80)
+ __clear_bit(i, &atkbd->xl_bit);
+ else
+ __set_bit(i, &atkbd->xl_bit);
+ break;
+ }
+ }
+}
+
+/*
+ * Encode the scancode, 0xe0 prefix, and high bit into a single integer,
+ * keeping kernel 2.4 compatibility for set 2
+ */
+static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code)
+{
+ if (atkbd->set == 3) {
+ if (atkbd->emul == 1)
+ code |= 0x100;
+ } else {
+ code = (code & 0x7f) | ((code & 0x80) << 1);
+ if (atkbd->emul == 1)
+ code |= 0x80;
+ }
+
+ return code;
}
/*
@@ -267,9 +321,11 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
unsigned int flags, struct pt_regs *regs)
{
struct atkbd *atkbd = serio_get_drvdata(serio);
+ struct input_dev *dev = atkbd->dev;
unsigned int code = data;
- int scroll = 0, hscroll = 0, click = -1;
+ int scroll = 0, hscroll = 0, click = -1, add_release_event = 0;
int value;
+ unsigned char keycode;
#ifdef ATKBD_DEBUG
printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
@@ -298,25 +354,17 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
if (!atkbd->enabled)
goto out;
- input_event(atkbd->dev, EV_MSC, MSC_RAW, code);
+ input_event(dev, EV_MSC, MSC_RAW, code);
if (atkbd->translated) {
- if (atkbd->emul ||
- (code != ATKBD_RET_EMUL0 && code != ATKBD_RET_EMUL1 &&
- code != ATKBD_RET_HANGUEL && code != ATKBD_RET_HANJA &&
- (code != ATKBD_RET_ERR || atkbd->err_xl) &&
- (code != ATKBD_RET_BAT || atkbd->bat_xl))) {
+ if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
atkbd->release = code >> 7;
code &= 0x7f;
}
- if (!atkbd->emul) {
- if ((code & 0x7f) == (ATKBD_RET_BAT & 0x7f))
- atkbd->bat_xl = !(data >> 7);
- if ((code & 0x7f) == (ATKBD_RET_ERR & 0x7f))
- atkbd->err_xl = !(data >> 7);
- }
+ if (!atkbd->emul)
+ atkbd_calculate_xl_bit(atkbd, data);
}
switch (code) {
@@ -333,47 +381,48 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
case ATKBD_RET_RELEASE:
atkbd->release = 1;
goto out;
- case ATKBD_RET_HANGUEL:
- atkbd_report_key(atkbd->dev, regs, KEY_HANGUEL, 3);
+ case ATKBD_RET_ACK:
+ case ATKBD_RET_NAK:
+ printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
+ "Some program might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
goto out;
+ case ATKBD_RET_HANGEUL:
case ATKBD_RET_HANJA:
- atkbd_report_key(atkbd->dev, regs, KEY_HANJA, 3);
- goto out;
+ /*
+ * These keys do not report release and thus need to be
+ * flagged properly
+ */
+ add_release_event = 1;
+ break;
case ATKBD_RET_ERR:
printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
goto out;
}
- if (atkbd->set != 3)
- code = (code & 0x7f) | ((code & 0x80) << 1);
- if (atkbd->emul) {
- if (--atkbd->emul)
- goto out;
- code |= (atkbd->set != 3) ? 0x80 : 0x100;
- }
+ code = atkbd_compat_scancode(atkbd, code);
+
+ if (atkbd->emul && --atkbd->emul)
+ goto out;
- if (atkbd->keycode[code] != ATKBD_KEY_NULL)
- input_event(atkbd->dev, EV_MSC, MSC_SCAN, code);
+ keycode = atkbd->keycode[code];
- switch (atkbd->keycode[code]) {
+ if (keycode != ATKBD_KEY_NULL)
+ input_event(dev, EV_MSC, MSC_SCAN, code);
+
+ switch (keycode) {
case ATKBD_KEY_NULL:
break;
case ATKBD_KEY_UNKNOWN:
- if (data == ATKBD_RET_ACK || data == ATKBD_RET_NAK) {
- printk(KERN_WARNING "atkbd.c: Spurious %s on %s. Some program, "
- "like XFree86, might be trying access hardware directly.\n",
- data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
- } else {
- printk(KERN_WARNING "atkbd.c: Unknown key %s "
- "(%s set %d, code %#x on %s).\n",
- atkbd->release ? "released" : "pressed",
- atkbd->translated ? "translated" : "raw",
- atkbd->set, code, serio->phys);
- printk(KERN_WARNING "atkbd.c: Use 'setkeycodes %s%02x <keycode>' "
- "to make it known.\n",
- code & 0x80 ? "e0" : "", code & 0x7f);
- }
- input_sync(atkbd->dev);
+ printk(KERN_WARNING
+ "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n",
+ atkbd->release ? "released" : "pressed",
+ atkbd->translated ? "translated" : "raw",
+ atkbd->set, code, serio->phys);
+ printk(KERN_WARNING
+ "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
+ code & 0x80 ? "e0" : "", code & 0x7f);
+ input_sync(dev);
break;
case ATKBD_SCR_1:
scroll = 1 - atkbd->release * 2;
@@ -397,33 +446,35 @@ static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
hscroll = 1;
break;
default:
- value = atkbd->release ? 0 :
- (1 + (!atkbd->softrepeat && test_bit(atkbd->keycode[code], atkbd->dev->key)));
-
- switch (value) { /* Workaround Toshiba laptop multiple keypress */
- case 0:
- atkbd->last = 0;
- break;
- case 1:
- atkbd->last = code;
- atkbd->time = jiffies + msecs_to_jiffies(atkbd->dev->rep[REP_DELAY]) / 2;
- break;
- case 2:
- if (!time_after(jiffies, atkbd->time) && atkbd->last == code)
- value = 1;
- break;
+ if (atkbd->release) {
+ value = 0;
+ atkbd->last = 0;
+ } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
+ /* Workaround Toshiba laptop multiple keypress */
+ value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
+ } else {
+ value = 1;
+ atkbd->last = code;
+ atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
}
- atkbd_report_key(atkbd->dev, regs, atkbd->keycode[code], value);
+ input_regs(dev, regs);
+ input_event(dev, EV_KEY, keycode, value);
+ input_sync(dev);
+
+ if (value && add_release_event) {
+ input_report_key(dev, keycode, 0);
+ input_sync(dev);
+ }
}
if (atkbd->scroll) {
- input_regs(atkbd->dev, regs);
+ input_regs(dev, regs);
if (click != -1)
- input_report_key(atkbd->dev, BTN_MIDDLE, click);
- input_report_rel(atkbd->dev, REL_WHEEL, scroll);
- input_report_rel(atkbd->dev, REL_HWHEEL, hscroll);
- input_sync(atkbd->dev);
+ input_report_key(dev, BTN_MIDDLE, click);
+ input_report_rel(dev, REL_WHEEL, scroll);
+ input_report_rel(dev, REL_HWHEEL, hscroll);
+ input_sync(dev);
}
atkbd->release = 0;
@@ -764,6 +815,9 @@ static void atkbd_set_keycode_table(struct atkbd *atkbd)
for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++)
atkbd->keycode[atkbd_scroll_keys[i].set2] = atkbd_scroll_keys[i].keycode;
}
+
+ atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL)] = KEY_HANGUEL;
+ atkbd->keycode[atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA)] = KEY_HANJA;
}
/*
@@ -776,12 +830,15 @@ static void atkbd_set_device_attrs(struct atkbd *atkbd)
int i;
if (atkbd->extra)
- sprintf(atkbd->name, "AT Set 2 Extra keyboard");
+ snprintf(atkbd->name, sizeof(atkbd->name),
+ "AT Set 2 Extra keyboard");
else
- sprintf(atkbd->name, "AT %s Set %d keyboard",
- atkbd->translated ? "Translated" : "Raw", atkbd->set);
+ snprintf(atkbd->name, sizeof(atkbd->name),
+ "AT %s Set %d keyboard",
+ atkbd->translated ? "Translated" : "Raw", atkbd->set);
- sprintf(atkbd->phys, "%s/input0", atkbd->ps2dev.serio->phys);
+ snprintf(atkbd->phys, sizeof(atkbd->phys),
+ "%s/input0", atkbd->ps2dev.serio->phys);
input_dev->name = atkbd->name;
input_dev->phys = atkbd->phys;
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
index 77c4d9669ad0..5174224cadb4 100644
--- a/drivers/input/keyboard/lkkbd.c
+++ b/drivers/input/keyboard/lkkbd.c
@@ -384,18 +384,21 @@ lkkbd_detection_done (struct lkkbd *lk)
*/
switch (lk->id[4]) {
case 1:
- sprintf (lk->name, "DEC LK201 keyboard");
+ strlcpy (lk->name, "DEC LK201 keyboard",
+ sizeof (lk->name));
if (lk201_compose_is_alt)
lk->keycode[0xb1] = KEY_LEFTALT;
break;
case 2:
- sprintf (lk->name, "DEC LK401 keyboard");
+ strlcpy (lk->name, "DEC LK401 keyboard",
+ sizeof (lk->name));
break;
default:
- sprintf (lk->name, "Unknown DEC keyboard");
+ strlcpy (lk->name, "Unknown DEC keyboard",
+ sizeof (lk->name));
printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
"please report to Jan-Benedict Glaw "
"<jbglaw@lug-owl.de>\n", lk->phys);
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
index d10983c521e6..40a3f551247e 100644
--- a/drivers/input/keyboard/newtonkbd.c
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -96,7 +96,7 @@ static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
nkbd->serio = serio;
nkbd->dev = input_dev;
- sprintf(nkbd->phys, "%s/input0", serio->phys);
+ snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
input_dev->name = "Newton Keyboard";
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
index b15b6d8d4f83..9dbd7b85686d 100644
--- a/drivers/input/keyboard/sunkbd.c
+++ b/drivers/input/keyboard/sunkbd.c
@@ -263,7 +263,7 @@ static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
}
- sprintf(sunkbd->name, "Sun Type %d keyboard", sunkbd->type);
+ snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
input_dev->name = sunkbd->name;
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
index 4135e3e16c51..0821d53cf0c1 100644
--- a/drivers/input/keyboard/xtkbd.c
+++ b/drivers/input/keyboard/xtkbd.c
@@ -100,7 +100,7 @@ static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
xtkbd->serio = serio;
xtkbd->dev = input_dev;
- sprintf(xtkbd->phys, "%s/input0", serio->phys);
+ snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys);
memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
input_dev->name = "XT Keyboard";
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index e4e5be111c96..ccf0faeee5c1 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -285,6 +285,15 @@ static struct key_entry keymap_fujitsu_n3510[] = {
{ KE_END, 0 }
};
+static struct key_entry keymap_wistron_ms2111[] = {
+ { KE_KEY, 0x11, KEY_PROG1 },
+ { KE_KEY, 0x12, KEY_PROG2 },
+ { KE_KEY, 0x13, KEY_PROG3 },
+ { KE_KEY, 0x31, KEY_MAIL },
+ { KE_KEY, 0x36, KEY_WWW },
+ { KE_END, 0 }
+};
+
static struct key_entry keymap_wistron_ms2141[] = {
{ KE_KEY, 0x11, KEY_PROG1 },
{ KE_KEY, 0x12, KEY_PROG2 },
@@ -326,6 +335,7 @@ static struct key_entry keymap_aopen_1559as[] = {
{ KE_WIFI, 0x30, 0 },
{ KE_KEY, 0x31, KEY_MAIL },
{ KE_KEY, 0x36, KEY_WWW },
+ { KE_END, 0 },
};
/*
@@ -388,6 +398,15 @@ static struct dmi_system_id dmi_ids[] = {
},
.driver_data = keymap_aopen_1559as
},
+ {
+ .callback = dmi_matched,
+ .ident = "Medion MD 9783",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "MEDIONNB"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "MD 9783"),
+ },
+ .driver_data = keymap_wistron_ms2111
+ },
{ NULL, }
};
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index a0e2e797c6d5..070d75330afd 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -470,7 +470,7 @@ int alps_init(struct psmouse *psmouse)
dev1->keybit[LONG(BTN_BACK)] |= BIT(BTN_BACK);
}
- sprintf(priv->phys, "%s/input1", psmouse->ps2dev.serio->phys);
+ snprintf(priv->phys, sizeof(priv->phys), "%s/input1", psmouse->ps2dev.serio->phys);
dev2->phys = priv->phys;
dev2->name = (priv->i->flags & ALPS_DUALPOINT) ? "DualPoint Stick" : "PS/2 Mouse";
dev2->id.bustype = BUS_I8042;
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 136321a2cfdb..8bc9f51ae6c2 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -150,9 +150,20 @@ static psmouse_ret_t psmouse_process_byte(struct psmouse *psmouse, struct pt_reg
*/
if (psmouse->type == PSMOUSE_IMEX) {
- input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
- input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
- input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+ switch (packet[3] & 0xC0) {
+ case 0x80: /* vertical scroll on IntelliMouse Explorer 4.0 */
+ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ break;
+ case 0x40: /* horizontal scroll on IntelliMouse Explorer 4.0 */
+ input_report_rel(dev, REL_HWHEEL, (int) (packet[3] & 32) - (int) (packet[3] & 31));
+ break;
+ case 0x00:
+ case 0xC0:
+ input_report_rel(dev, REL_WHEEL, (int) (packet[3] & 8) - (int) (packet[3] & 7));
+ input_report_key(dev, BTN_SIDE, (packet[3] >> 4) & 1);
+ input_report_key(dev, BTN_EXTRA, (packet[3] >> 5) & 1);
+ break;
+ }
}
/*
@@ -466,9 +477,25 @@ static int im_explorer_detect(struct psmouse *psmouse, int set_properties)
if (param[0] != 4)
return -1;
+/* Magic to enable horizontal scrolling on IntelliMouse 4.0 */
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 80;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 40;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 200;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ param[0] = 60;
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+
if (set_properties) {
set_bit(BTN_MIDDLE, psmouse->dev->keybit);
set_bit(REL_WHEEL, psmouse->dev->relbit);
+ set_bit(REL_HWHEEL, psmouse->dev->relbit);
set_bit(BTN_SIDE, psmouse->dev->keybit);
set_bit(BTN_EXTRA, psmouse->dev->keybit);
@@ -1057,8 +1084,8 @@ static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_proto
if (psmouse->resync_time && psmouse->poll(psmouse))
psmouse->resync_time = 0;
- sprintf(psmouse->devname, "%s %s %s",
- psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name);
+ snprintf(psmouse->devname, sizeof(psmouse->devname), "%s %s %s",
+ psmouse_protocol_by_type(psmouse->type)->name, psmouse->vendor, psmouse->name);
input_dev->name = psmouse->devname;
input_dev->phys = psmouse->phys;
@@ -1099,7 +1126,7 @@ static int psmouse_connect(struct serio *serio, struct serio_driver *drv)
ps2_init(&psmouse->ps2dev, serio);
INIT_WORK(&psmouse->resync_work, psmouse_resync, psmouse);
psmouse->dev = input_dev;
- sprintf(psmouse->phys, "%s/input0", serio->phys);
+ snprintf(psmouse->phys, sizeof(psmouse->phys), "%s/input0", serio->phys);
psmouse_set_state(psmouse, PSMOUSE_INITIALIZING);
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index 2f9a04ae725f..a89742431717 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -254,7 +254,7 @@ static int sermouse_connect(struct serio *serio, struct serio_driver *drv)
goto fail;
sermouse->dev = input_dev;
- sprintf(sermouse->phys, "%s/input0", serio->phys);
+ snprintf(sermouse->phys, sizeof(sermouse->phys), "%s/input0", serio->phys);
sermouse->type = serio->id.proto;
input_dev->name = sermouse_protocols[sermouse->type];
diff --git a/drivers/input/mouse/vsxxxaa.c b/drivers/input/mouse/vsxxxaa.c
index 36e9442a16b2..7b85bc21ae4a 100644
--- a/drivers/input/mouse/vsxxxaa.c
+++ b/drivers/input/mouse/vsxxxaa.c
@@ -153,22 +153,25 @@ vsxxxaa_detection_done (struct vsxxxaa *mouse)
{
switch (mouse->type) {
case 0x02:
- sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse");
+ strlcpy (mouse->name, "DEC VSXXX-AA/-GA mouse",
+ sizeof (mouse->name));
break;
case 0x04:
- sprintf (mouse->name, "DEC VSXXX-AB digitizer");
+ strlcpy (mouse->name, "DEC VSXXX-AB digitizer",
+ sizeof (mouse->name));
break;
default:
- sprintf (mouse->name, "unknown DEC pointer device "
- "(type = 0x%02x)", mouse->type);
+ snprintf (mouse->name, sizeof (mouse->name),
+ "unknown DEC pointer device (type = 0x%02x)",
+ mouse->type);
break;
}
- printk (KERN_INFO "Found %s version 0x%02x from country 0x%02x "
- "on port %s\n", mouse->name, mouse->version,
- mouse->country, mouse->phys);
+ printk (KERN_INFO
+ "Found %s version 0x%02x from country 0x%02x on port %s\n",
+ mouse->name, mouse->version, mouse->country, mouse->phys);
}
/*
@@ -503,8 +506,9 @@ vsxxxaa_connect (struct serio *serio, struct serio_driver *drv)
mouse->dev = input_dev;
mouse->serio = serio;
- sprintf (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer");
- sprintf (mouse->phys, "%s/input0", serio->phys);
+ strlcat (mouse->name, "DEC VSXXX-AA/-GA mouse or VSXXX-AB digitizer",
+ sizeof (mouse->name));
+ snprintf (mouse->phys, sizeof (mouse->phys), "%s/input0", serio->phys);
input_dev->name = mouse->name;
input_dev->phys = mouse->phys;
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index b685a507955d..eb721b11ff37 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -123,7 +123,9 @@ static void mousedev_touchpad_event(struct input_dev *dev, struct mousedev *mous
if (mousedev->touch) {
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
- if (size == 0) size = 256 * 2;
+ if (size == 0)
+ size = 256 * 2;
+
switch (code) {
case ABS_X:
fx(0) = value;
@@ -155,18 +157,24 @@ static void mousedev_abs_event(struct input_dev *dev, struct mousedev *mousedev,
switch (code) {
case ABS_X:
size = dev->absmax[ABS_X] - dev->absmin[ABS_X];
- if (size == 0) size = xres ? : 1;
- if (value > dev->absmax[ABS_X]) value = dev->absmax[ABS_X];
- if (value < dev->absmin[ABS_X]) value = dev->absmin[ABS_X];
+ if (size == 0)
+ size = xres ? : 1;
+ if (value > dev->absmax[ABS_X])
+ value = dev->absmax[ABS_X];
+ if (value < dev->absmin[ABS_X])
+ value = dev->absmin[ABS_X];
mousedev->packet.x = ((value - dev->absmin[ABS_X]) * xres) / size;
mousedev->packet.abs_event = 1;
break;
case ABS_Y:
size = dev->absmax[ABS_Y] - dev->absmin[ABS_Y];
- if (size == 0) size = yres ? : 1;
- if (value > dev->absmax[ABS_Y]) value = dev->absmax[ABS_Y];
- if (value < dev->absmin[ABS_Y]) value = dev->absmin[ABS_Y];
+ if (size == 0)
+ size = yres ? : 1;
+ if (value > dev->absmax[ABS_Y])
+ value = dev->absmax[ABS_Y];
+ if (value < dev->absmin[ABS_Y])
+ value = dev->absmin[ABS_Y];
mousedev->packet.y = yres - ((value - dev->absmin[ABS_Y]) * yres) / size;
mousedev->packet.abs_event = 1;
break;
@@ -202,7 +210,7 @@ static void mousedev_key_event(struct mousedev *mousedev, unsigned int code, int
case BTN_SIDE: index = 3; break;
case BTN_4:
case BTN_EXTRA: index = 4; break;
- default: return;
+ default: return;
}
if (value) {
@@ -285,10 +293,9 @@ static void mousedev_touchpad_touch(struct mousedev *mousedev, int value)
mousedev->touch = mousedev->pkt_count = 0;
mousedev->frac_dx = 0;
mousedev->frac_dy = 0;
- }
- else
- if (!mousedev->touch)
- mousedev->touch = jiffies;
+
+ } else if (!mousedev->touch)
+ mousedev->touch = jiffies;
}
static void mousedev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
@@ -327,7 +334,7 @@ static void mousedev_event(struct input_handle *handle, unsigned int type, unsig
mousedev->pkt_count++;
/* Input system eats duplicate events, but we need all of them
* to do correct averaging so apply present one forward
- */
+ */
fx(0) = fx(1);
fy(0) = fy(1);
}
@@ -346,7 +353,9 @@ 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;
}
@@ -507,14 +516,16 @@ static ssize_t mousedev_write(struct file * file, const char __user * buffer, si
list->imexseq = 0;
list->mode = MOUSEDEV_EMUL_EXPS;
}
- } else list->imexseq = 0;
+ } else
+ list->imexseq = 0;
if (c == mousedev_imps_seq[list->impsseq]) {
if (++list->impsseq == MOUSEDEV_SEQ_LEN) {
list->impsseq = 0;
list->mode = MOUSEDEV_EMUL_IMPS;
}
- } else list->impsseq = 0;
+ } else
+ list->impsseq = 0;
list->ps2[0] = 0xfa;
@@ -598,6 +609,7 @@ static ssize_t mousedev_read(struct file * file, char __user * buffer, size_t co
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);
return ((list->ready || list->buffer) ? (POLLIN | POLLRDNORM) : 0) |
(list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
diff --git a/drivers/input/serio/ct82c710.c b/drivers/input/serio/ct82c710.c
index 096b6a0b5cca..1ac739ef2ffa 100644
--- a/drivers/input/serio/ct82c710.c
+++ b/drivers/input/serio/ct82c710.c
@@ -189,7 +189,7 @@ static int __devinit ct82c710_probe(struct platform_device *dev)
strlcpy(ct82c710_port->name, "C&T 82c710 mouse port",
sizeof(ct82c710_port->name));
snprintf(ct82c710_port->phys, sizeof(ct82c710_port->phys),
- "isa%04lx/serio0", CT82C710_DATA);
+ "isa%16llx/serio0", (unsigned long long)CT82C710_DATA);
serio_register_port(ct82c710_port);
@@ -241,8 +241,8 @@ static int __init ct82c710_init(void)
serio_register_port(ct82c710_port);
- printk(KERN_INFO "serio: C&T 82c710 mouse port at %#lx irq %d\n",
- CT82C710_DATA, CT82C710_IRQ);
+ printk(KERN_INFO "serio: C&T 82c710 mouse port at %#llx irq %d\n",
+ (unsigned long long)CT82C710_DATA, CT82C710_IRQ);
return 0;
diff --git a/drivers/input/touchscreen/gunze.c b/drivers/input/touchscreen/gunze.c
index 466da190ceec..b769b21973b7 100644
--- a/drivers/input/touchscreen/gunze.c
+++ b/drivers/input/touchscreen/gunze.c
@@ -129,7 +129,7 @@ static int gunze_connect(struct serio *serio, struct serio_driver *drv)
gunze->serio = serio;
gunze->dev = input_dev;
- sprintf(gunze->phys, "%s/input0", serio->phys);
+ snprintf(gunze->phys, sizeof(serio->phys), "%s/input0", serio->phys);
input_dev->private = gunze;
input_dev->name = "Gunze AHL-51S TouchScreen";
diff --git a/drivers/input/touchscreen/h3600_ts_input.c b/drivers/input/touchscreen/h3600_ts_input.c
index a595d386312f..2de2139f2fed 100644
--- a/drivers/input/touchscreen/h3600_ts_input.c
+++ b/drivers/input/touchscreen/h3600_ts_input.c
@@ -363,7 +363,7 @@ static int h3600ts_connect(struct serio *serio, struct serio_driver *drv)
ts->serio = serio;
ts->dev = input_dev;
- sprintf(ts->phys, "%s/input0", serio->phys);
+ snprintf(ts->phys, sizeof(ts->phys), "%s/input0", serio->phys);
input_dev->name = "H3600 TouchScreen";
input_dev->phys = ts->phys;
diff --git a/drivers/input/touchscreen/mtouch.c b/drivers/input/touchscreen/mtouch.c
index 1d0d37eeef6e..8647a905df80 100644
--- a/drivers/input/touchscreen/mtouch.c
+++ b/drivers/input/touchscreen/mtouch.c
@@ -143,7 +143,7 @@ static int mtouch_connect(struct serio *serio, struct serio_driver *drv)
mtouch->serio = serio;
mtouch->dev = input_dev;
- sprintf(mtouch->phys, "%s/input0", serio->phys);
+ snprintf(mtouch->phys, sizeof(mtouch->phys), "%s/input0", serio->phys);
input_dev->private = mtouch;
input_dev->name = "MicroTouch Serial TouchScreen";
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
index d678d144bbf8..5f9ecad2ca75 100644
--- a/drivers/input/tsdev.c
+++ b/drivers/input/tsdev.c
@@ -35,7 +35,7 @@
* e-mail - mail your message to <jsimmons@infradead.org>.
*/
-#define TSDEV_MINOR_BASE 128
+#define TSDEV_MINOR_BASE 128
#define TSDEV_MINORS 32
/* First 16 devices are h3600_ts compatible; second 16 are h3600_tsraw */
#define TSDEV_MINOR_MASK 15
@@ -230,6 +230,7 @@ static ssize_t tsdev_read(struct file *file, char __user *buffer, size_t count,
static unsigned int tsdev_poll(struct file *file, poll_table * wait)
{
struct tsdev_list *list = file->private_data;
+
poll_wait(file, &list->tsdev->wait, wait);
return ((list->head == list->tail) ? 0 : (POLLIN | POLLRDNORM)) |
(list->tsdev->exist ? 0 : (POLLHUP | POLLERR));
@@ -248,11 +249,13 @@ static int tsdev_ioctl(struct inode *inode, struct file *file,
sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
+
case TS_SET_CAL:
if (copy_from_user (&tsdev->cal, (void __user *)arg,
sizeof (struct ts_calibration)))
retval = -EFAULT;
break;
+
default:
retval = -EINVAL;
break;
@@ -284,9 +287,11 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
case ABS_X:
tsdev->x = value;
break;
+
case ABS_Y:
tsdev->y = value;
break;
+
case ABS_PRESSURE:
if (value > handle->dev->absmax[ABS_PRESSURE])
value = handle->dev->absmax[ABS_PRESSURE];
@@ -307,6 +312,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
else if (tsdev->x > xres)
tsdev->x = xres;
break;
+
case REL_Y:
tsdev->y += value;
if (tsdev->y < 0)
@@ -323,6 +329,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
case 0:
tsdev->pressure = 0;
break;
+
case 1:
if (!tsdev->pressure)
tsdev->pressure = 1;
@@ -370,9 +377,8 @@ static struct input_handle *tsdev_connect(struct input_handler *handler,
struct class_device *cdev;
int minor, delta;
- for (minor = 0; minor < TSDEV_MINORS/2 && tsdev_table[minor];
- minor++);
- if (minor >= TSDEV_MINORS/2) {
+ for (minor = 0; minor < TSDEV_MINORS / 2 && tsdev_table[minor]; minor++);
+ if (minor >= TSDEV_MINORS / 2) {
printk(KERN_ERR
"tsdev: You have way too many touchscreens\n");
return NULL;
@@ -444,22 +450,22 @@ static struct input_device_id tsdev_ids[] = {
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
.keybit = { [LONG(BTN_LEFT)] = BIT(BTN_LEFT) },
.relbit = { BIT(REL_X) | BIT(REL_Y) },
- },/* A mouse like device, at least one button, two relative axes */
+ }, /* A mouse like device, at least one button, two relative axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_ABS) },
.keybit = { [LONG(BTN_TOUCH)] = BIT(BTN_TOUCH) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) },
- },/* A tablet like device, at least touch detection, two absolute axes */
+ }, /* A tablet like device, at least touch detection, two absolute axes */
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
.absbit = { BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE) },
- },/* A tablet like device with several gradations of pressure */
+ }, /* A tablet like device with several gradations of pressure */
- {},/* Terminating entry */
+ {} /* Terminating entry */
};
MODULE_DEVICE_TABLE(input, tsdev_ids);
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 173c899a1fb4..2e541fa02024 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -87,6 +87,11 @@ struct capincci;
#ifdef CONFIG_ISDN_CAPI_MIDDLEWARE
struct capiminor;
+struct datahandle_queue {
+ struct list_head list;
+ u16 datahandle;
+};
+
struct capiminor {
struct list_head list;
struct capincci *nccip;
@@ -109,12 +114,9 @@ struct capiminor {
int outbytes;
/* transmit path */
- struct datahandle_queue {
- struct datahandle_queue *next;
- u16 datahandle;
- } *ackqueue;
+ struct list_head ackqueue;
int nack;
-
+ spinlock_t ackqlock;
};
#endif /* CONFIG_ISDN_CAPI_MIDDLEWARE */
@@ -156,48 +158,54 @@ static LIST_HEAD(capiminor_list);
static int capincci_add_ack(struct capiminor *mp, u16 datahandle)
{
- struct datahandle_queue *n, **pp;
+ struct datahandle_queue *n;
+ unsigned long flags;
n = kmalloc(sizeof(*n), GFP_ATOMIC);
- if (!n) {
- printk(KERN_ERR "capi: alloc datahandle failed\n");
- return -1;
+ if (unlikely(!n)) {
+ printk(KERN_ERR "capi: alloc datahandle failed\n");
+ return -1;
}
- n->next = NULL;
n->datahandle = datahandle;
- for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) ;
- *pp = n;
+ INIT_LIST_HEAD(&n->list);
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_add_tail(&n->list, &mp->ackqueue);
mp->nack++;
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
return 0;
}
static int capiminor_del_ack(struct capiminor *mp, u16 datahandle)
{
- struct datahandle_queue **pp, *p;
+ struct datahandle_queue *p, *tmp;
+ unsigned long flags;
- for (pp = &mp->ackqueue; *pp; pp = &(*pp)->next) {
- if ((*pp)->datahandle == datahandle) {
- p = *pp;
- *pp = (*pp)->next;
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
+ if (p->datahandle == datahandle) {
+ list_del(&p->list);
kfree(p);
mp->nack--;
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
return 0;
}
}
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
return -1;
}
static void capiminor_del_all_ack(struct capiminor *mp)
{
- struct datahandle_queue **pp, *p;
+ struct datahandle_queue *p, *tmp;
+ unsigned long flags;
- pp = &mp->ackqueue;
- while (*pp) {
- p = *pp;
- *pp = (*pp)->next;
+ spin_lock_irqsave(&mp->ackqlock, flags);
+ list_for_each_entry_safe(p, tmp, &mp->ackqueue, list) {
+ list_del(&p->list);
kfree(p);
mp->nack--;
}
+ spin_unlock_irqrestore(&mp->ackqlock, flags);
}
@@ -220,6 +228,8 @@ static struct capiminor *capiminor_alloc(struct capi20_appl *ap, u32 ncci)
mp->ncci = ncci;
mp->msgid = 0;
atomic_set(&mp->ttyopencount,0);
+ INIT_LIST_HEAD(&mp->ackqueue);
+ spin_lock_init(&mp->ackqlock);
skb_queue_head_init(&mp->inqueue);
skb_queue_head_init(&mp->outqueue);
diff --git a/drivers/isdn/divert/isdn_divert.c b/drivers/isdn/divert/isdn_divert.c
index f1a1f9a9b88e..1f5ebe9ee72c 100644
--- a/drivers/isdn/divert/isdn_divert.c
+++ b/drivers/isdn/divert/isdn_divert.c
@@ -592,7 +592,7 @@ static int put_address(char *st, u_char *p, int len)
} /* put_address */
/*************************************/
-/* report a succesfull interrogation */
+/* report a successful interrogation */
/*************************************/
static int interrogate_success(isdn_ctrl *ic, struct call_struc *cs)
{ char *src = ic->parm.dss1_io.data;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index eb41aba3ddef..8a45715dd4c1 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -65,23 +65,22 @@ static struct usb_device_id gigaset_table [] = {
MODULE_DEVICE_TABLE(usb, gigaset_table);
-/*======================= local function prototypes =============================*/
+/*======================= local function prototypes ==========================*/
-/* This function is called if a new device is connected to the USB port. It
- * checks whether this new device belongs to this driver.
- */
+/* function called if a new device belonging to this driver is connected */
static int gigaset_probe(struct usb_interface *interface,
const struct usb_device_id *id);
/* Function will be called if the device is unplugged */
static void gigaset_disconnect(struct usb_interface *interface);
-static void read_ctrl_callback(struct urb *, struct pt_regs *);
+static int atread_submit(struct cardstate *, int);
static void stopurbs(struct bas_bc_state *);
+static int req_submit(struct bc_state *, int, int, int);
static int atwrite_submit(struct cardstate *, unsigned char *, int);
static int start_cbsend(struct cardstate *);
-/*==============================================================================*/
+/*============================================================================*/
struct bas_cardstate {
struct usb_device *udev; /* USB device pointer */
@@ -91,6 +90,7 @@ struct bas_cardstate {
struct urb *urb_ctrl; /* control pipe default URB */
struct usb_ctrlrequest dr_ctrl;
struct timer_list timer_ctrl; /* control request timeout */
+ int retry_ctrl;
struct timer_list timer_atrdy; /* AT command ready timeout */
struct urb *urb_cmd_out; /* for sending AT commands */
@@ -307,6 +307,7 @@ static int gigaset_set_line_ctrl(struct cardstate *cs, unsigned cflag)
* hang up any existing connection because of an unrecoverable error
* This function may be called from any context and takes care of scheduling
* the necessary actions for execution outside of interrupt context.
+ * cs->lock must not be held.
* argument:
* B channel control structure
*/
@@ -325,14 +326,17 @@ static inline void error_hangup(struct bc_state *bcs)
/* error_reset
* reset Gigaset device because of an unrecoverable error
- * This function may be called from any context, and should take care of
+ * This function may be called from any context, and takes care of
* scheduling the necessary actions for execution outside of interrupt context.
- * Right now, it just generates a kernel message calling for help.
+ * cs->lock must not be held.
* argument:
* controller state structure
*/
static inline void error_reset(struct cardstate *cs)
{
+ /* close AT command channel to recover (ignore errors) */
+ req_submit(cs->bcs, HD_CLOSE_ATCHANNEL, 0, BAS_TIMEOUT);
+
//FIXME try to recover without bothering the user
dev_err(cs->dev,
"unrecoverable error - please disconnect Gigaset base to reset\n");
@@ -403,14 +407,30 @@ static void cmd_in_timeout(unsigned long data)
{
struct cardstate *cs = (struct cardstate *) data;
struct bas_cardstate *ucs = cs->hw.bas;
+ int rc;
if (!ucs->rcvbuf_size) {
gig_dbg(DEBUG_USBREQ, "%s: no receive in progress", __func__);
return;
}
- dev_err(cs->dev, "timeout reading AT response\n");
- error_reset(cs); //FIXME retry?
+ if (ucs->retry_cmd_in++ < BAS_RETRY) {
+ dev_notice(cs->dev, "control read: timeout, retry %d\n",
+ ucs->retry_cmd_in);
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ } else {
+ dev_err(cs->dev,
+ "control read: timeout, giving up after %d tries\n",
+ ucs->retry_cmd_in);
+ }
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ error_reset(cs);
}
/* set/clear bits in base connection state, return previous state
@@ -428,6 +448,96 @@ inline static int update_basstate(struct bas_cardstate *ucs,
return state;
}
+/* read_ctrl_callback
+ * USB completion handler for control pipe input
+ * called by the USB subsystem in interrupt context
+ * parameter:
+ * urb USB request block
+ * urb->context = inbuf structure for controller state
+ */
+static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct inbuf_t *inbuf = urb->context;
+ struct cardstate *cs = inbuf->cs;
+ struct bas_cardstate *ucs = cs->hw.bas;
+ int have_data = 0;
+ unsigned numbytes;
+ int rc;
+
+ update_basstate(ucs, 0, BS_ATRDPEND);
+
+ if (!ucs->rcvbuf_size) {
+ dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
+ return;
+ }
+
+ del_timer(&ucs->timer_cmd_in);
+
+ switch (urb->status) {
+ case 0: /* normal completion */
+ numbytes = urb->actual_length;
+ if (unlikely(numbytes != ucs->rcvbuf_size)) {
+ dev_warn(cs->dev,
+ "control read: received %d chars, expected %d\n",
+ numbytes, ucs->rcvbuf_size);
+ if (numbytes > ucs->rcvbuf_size)
+ numbytes = ucs->rcvbuf_size;
+ }
+
+ /* copy received bytes to inbuf */
+ have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);
+
+ if (unlikely(numbytes < ucs->rcvbuf_size)) {
+ /* incomplete - resubmit for remaining bytes */
+ ucs->rcvbuf_size -= numbytes;
+ ucs->retry_cmd_in = 0;
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ error_reset(cs);
+ }
+ break;
+
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ /* no action necessary */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(urb->status));
+ break;
+
+ default: /* severe trouble */
+ dev_warn(cs->dev, "control read: %s\n",
+ get_usb_statmsg(urb->status));
+ if (ucs->retry_cmd_in++ < BAS_RETRY) {
+ dev_notice(cs->dev, "control read: retry %d\n",
+ ucs->retry_cmd_in);
+ rc = atread_submit(cs, BAS_TIMEOUT);
+ if (rc >= 0 || rc == -ENODEV)
+ /* resubmitted or disconnected */
+ /* - bypass regular exit block */
+ return;
+ } else {
+ dev_err(cs->dev,
+ "control read: giving up after %d tries\n",
+ ucs->retry_cmd_in);
+ }
+ error_reset(cs);
+ }
+
+ kfree(ucs->rcvbuf);
+ ucs->rcvbuf = NULL;
+ ucs->rcvbuf_size = 0;
+ if (have_data) {
+ gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
+ gigaset_schedule_event(cs);
+ }
+}
+
/* atread_submit
* submit an HD_READ_ATMESSAGE command URB and optionally start a timeout
* parameters:
@@ -466,7 +576,7 @@ static int atread_submit(struct cardstate *cs, int timeout)
if ((ret = usb_submit_urb(ucs->urb_cmd_in, SLAB_ATOMIC)) != 0) {
update_basstate(ucs, 0, BS_ATRDPEND);
dev_err(cs->dev, "could not submit HD_READ_ATMESSAGE: %s\n",
- get_usb_statmsg(ret));
+ get_usb_rcmsg(ret));
return ret;
}
@@ -611,9 +721,12 @@ static void read_int_callback(struct urb *urb, struct pt_regs *regs)
kfree(ucs->rcvbuf);
ucs->rcvbuf = NULL;
ucs->rcvbuf_size = 0;
- if (rc != -ENODEV)
+ if (rc != -ENODEV) {
//FIXME corrective action?
+ spin_unlock_irqrestore(&cs->lock, flags);
error_reset(cs);
+ break;
+ }
}
spin_unlock_irqrestore(&cs->lock, flags);
break;
@@ -643,97 +756,6 @@ resubmit:
}
}
-/* read_ctrl_callback
- * USB completion handler for control pipe input
- * called by the USB subsystem in interrupt context
- * parameter:
- * urb USB request block
- * urb->context = inbuf structure for controller state
- */
-static void read_ctrl_callback(struct urb *urb, struct pt_regs *regs)
-{
- struct inbuf_t *inbuf = urb->context;
- struct cardstate *cs = inbuf->cs;
- struct bas_cardstate *ucs = cs->hw.bas;
- int have_data = 0;
- unsigned numbytes;
- int rc;
-
- update_basstate(ucs, 0, BS_ATRDPEND);
-
- if (!ucs->rcvbuf_size) {
- dev_warn(cs->dev, "%s: no receive in progress\n", __func__);
- return;
- }
-
- del_timer(&ucs->timer_cmd_in);
-
- switch (urb->status) {
- case 0: /* normal completion */
- numbytes = urb->actual_length;
- if (unlikely(numbytes == 0)) {
- dev_warn(cs->dev,
- "control read: empty block received\n");
- goto retry;
- }
- if (unlikely(numbytes != ucs->rcvbuf_size)) {
- dev_warn(cs->dev,
- "control read: received %d chars, expected %d\n",
- numbytes, ucs->rcvbuf_size);
- if (numbytes > ucs->rcvbuf_size)
- numbytes = ucs->rcvbuf_size;
- }
-
- /* copy received bytes to inbuf */
- have_data = gigaset_fill_inbuf(inbuf, ucs->rcvbuf, numbytes);
-
- if (unlikely(numbytes < ucs->rcvbuf_size)) {
- /* incomplete - resubmit for remaining bytes */
- ucs->rcvbuf_size -= numbytes;
- ucs->retry_cmd_in = 0;
- goto retry;
- }
- break;
-
- case -ENOENT: /* cancelled */
- case -ECONNRESET: /* cancelled (async) */
- case -EINPROGRESS: /* pending */
- case -ENODEV: /* device removed */
- case -ESHUTDOWN: /* device shut down */
- /* no action necessary */
- gig_dbg(DEBUG_USBREQ, "%s: %s",
- __func__, get_usb_statmsg(urb->status));
- break;
-
- default: /* severe trouble */
- dev_warn(cs->dev, "control read: %s\n",
- get_usb_statmsg(urb->status));
- retry:
- if (ucs->retry_cmd_in++ < BAS_RETRY) {
- dev_notice(cs->dev, "control read: retry %d\n",
- ucs->retry_cmd_in);
- rc = atread_submit(cs, BAS_TIMEOUT);
- if (rc >= 0 || rc == -ENODEV)
- /* resubmitted or disconnected */
- /* - bypass regular exit block */
- return;
- } else {
- dev_err(cs->dev,
- "control read: giving up after %d tries\n",
- ucs->retry_cmd_in);
- }
- error_reset(cs);
- }
-
- kfree(ucs->rcvbuf);
- ucs->rcvbuf = NULL;
- ucs->rcvbuf_size = 0;
- if (have_data) {
- gig_dbg(DEBUG_INTR, "%s-->BH", __func__);
- gigaset_schedule_event(cs);
- }
-}
-
/* read_iso_callback
* USB completion handler for B channel isochronous input
* called by the USB subsystem in interrupt context
@@ -1378,6 +1400,7 @@ static void req_timeout(unsigned long data)
case HD_CLOSE_B1CHANNEL:
dev_err(bcs->cs->dev, "timeout closing channel %d\n",
bcs->channel + 1);
+ error_reset(bcs->cs);
break;
default:
@@ -1396,22 +1419,61 @@ static void req_timeout(unsigned long data)
static void write_ctrl_callback(struct urb *urb, struct pt_regs *regs)
{
struct bas_cardstate *ucs = urb->context;
+ int rc;
unsigned long flags;
- spin_lock_irqsave(&ucs->lock, flags);
- if (urb->status && ucs->pending) {
- dev_err(&ucs->interface->dev,
- "control request 0x%02x failed: %s\n",
- ucs->pending, get_usb_statmsg(urb->status));
- del_timer(&ucs->timer_ctrl);
- ucs->pending = 0;
- }
- /* individual handling of specific request types */
- switch (ucs->pending) {
- case HD_DEVICE_INIT_ACK: /* no reply expected */
- ucs->pending = 0;
+ /* check status */
+ switch (urb->status) {
+ case 0: /* normal completion */
+ spin_lock_irqsave(&ucs->lock, flags);
+ switch (ucs->pending) {
+ case HD_DEVICE_INIT_ACK: /* no reply expected */
+ del_timer(&ucs->timer_ctrl);
+ ucs->pending = 0;
+ break;
+ }
+ spin_unlock_irqrestore(&ucs->lock, flags);
+ return;
+
+ case -ENOENT: /* cancelled */
+ case -ECONNRESET: /* cancelled (async) */
+ case -EINPROGRESS: /* pending */
+ case -ENODEV: /* device removed */
+ case -ESHUTDOWN: /* device shut down */
+ /* ignore silently */
+ gig_dbg(DEBUG_USBREQ, "%s: %s",
+ __func__, get_usb_statmsg(urb->status));
break;
+
+ default: /* any failure */
+ if (++ucs->retry_ctrl > BAS_RETRY) {
+ dev_err(&ucs->interface->dev,
+ "control request 0x%02x failed: %s\n",
+ ucs->dr_ctrl.bRequest,
+ get_usb_statmsg(urb->status));
+ break; /* give up */
+ }
+ dev_notice(&ucs->interface->dev,
+ "control request 0x%02x: %s, retry %d\n",
+ ucs->dr_ctrl.bRequest, get_usb_statmsg(urb->status),
+ ucs->retry_ctrl);
+ /* urb->dev is clobbered by USB subsystem */
+ urb->dev = ucs->udev;
+ rc = usb_submit_urb(urb, SLAB_ATOMIC);
+ if (unlikely(rc)) {
+ dev_err(&ucs->interface->dev,
+ "could not resubmit request 0x%02x: %s\n",
+ ucs->dr_ctrl.bRequest, get_usb_rcmsg(rc));
+ break;
+ }
+ /* resubmitted */
+ return;
}
+
+ /* failed, clear pending request */
+ spin_lock_irqsave(&ucs->lock, flags);
+ del_timer(&ucs->timer_ctrl);
+ ucs->pending = 0;
spin_unlock_irqrestore(&ucs->lock, flags);
}
@@ -1455,9 +1517,11 @@ static int req_submit(struct bc_state *bcs, int req, int val, int timeout)
usb_sndctrlpipe(ucs->udev, 0),
(unsigned char*) &ucs->dr_ctrl, NULL, 0,
write_ctrl_callback, ucs);
- if ((ret = usb_submit_urb(ucs->urb_ctrl, SLAB_ATOMIC)) != 0) {
+ ucs->retry_ctrl = 0;
+ ret = usb_submit_urb(ucs->urb_ctrl, SLAB_ATOMIC);
+ if (unlikely(ret)) {
dev_err(bcs->cs->dev, "could not submit request 0x%02x: %s\n",
- req, get_usb_statmsg(ret));
+ req, get_usb_rcmsg(ret));
spin_unlock_irqrestore(&ucs->lock, flags);
return ret;
}
diff --git a/drivers/isdn/gigaset/common.c b/drivers/isdn/gigaset/common.c
index acb7e2656780..2a56bf33a673 100644
--- a/drivers/isdn/gigaset/common.c
+++ b/drivers/isdn/gigaset/common.c
@@ -981,7 +981,7 @@ exit:
EXPORT_SYMBOL_GPL(gigaset_stop);
static LIST_HEAD(drivers);
-static spinlock_t driver_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(driver_lock);
struct cardstate *gigaset_get_cs_by_id(int id)
{
diff --git a/drivers/isdn/gigaset/ev-layer.c b/drivers/isdn/gigaset/ev-layer.c
index 18e05c09b71c..44f02dbd1111 100644
--- a/drivers/isdn/gigaset/ev-layer.c
+++ b/drivers/isdn/gigaset/ev-layer.c
@@ -1262,7 +1262,8 @@ static void do_action(int action, struct cardstate *cs,
break;
case ACT_HUPMODEM:
/* send "+++" (hangup in unimodem mode) */
- cs->ops->write_cmd(cs, "+++", 3, NULL);
+ if (cs->connected)
+ cs->ops->write_cmd(cs, "+++", 3, NULL);
break;
case ACT_RING:
/* get fresh AT state structure for new CID */
@@ -1294,7 +1295,6 @@ static void do_action(int action, struct cardstate *cs,
break;
case ACT_ICALL:
handle_icall(cs, bcs, p_at_state);
- at_state = *p_at_state;
break;
case ACT_FAILSDOWN:
dev_warn(cs->dev, "Could not shut down the device.\n");
@@ -1334,10 +1334,8 @@ static void do_action(int action, struct cardstate *cs,
*/
at_state->pending_commands |= PC_DLE0;
atomic_set(&cs->commands_pending, 1);
- } else {
+ } else
disconnect(p_at_state);
- at_state = *p_at_state;
- }
break;
case ACT_FAKEDLE0:
at_state->int_var[VAR_ZDLE] = 0;
@@ -1354,10 +1352,8 @@ static void do_action(int action, struct cardstate *cs,
at_state->cid = -1;
if (bcs && cs->onechannel)
at_state->pending_commands |= PC_DLE0;
- else {
+ else
disconnect(p_at_state);
- at_state = *p_at_state;
- }
schedule_init(cs, MS_RECOVER);
break;
case ACT_FAILDLE0:
@@ -1410,7 +1406,6 @@ static void do_action(int action, struct cardstate *cs,
case ACT_ABORTACCEPT: /* hangup/error/timeout during ICALL processing */
disconnect(p_at_state);
- at_state = *p_at_state;
break;
case ACT_ABORTDIAL: /* error/timeout during dial preparation */
diff --git a/drivers/isdn/hisax/hfc_pci.c b/drivers/isdn/hisax/hfc_pci.c
index 91d25acb5ede..3622720f0505 100644
--- a/drivers/isdn/hisax/hfc_pci.c
+++ b/drivers/isdn/hisax/hfc_pci.c
@@ -1688,7 +1688,7 @@ setup_hfcpci(struct IsdnCard *card)
printk(KERN_WARNING "HFC-PCI: No IRQ for PCI card found\n");
return (0);
}
- cs->hw.hfcpci.pci_io = (char *) dev_hfcpci->resource[ 1].start;
+ cs->hw.hfcpci.pci_io = (char *)(unsigned long)dev_hfcpci->resource[1].start;
printk(KERN_INFO "HiSax: HFC-PCI card manufacturer: %s card name: %s\n", id_list[i].vendor_name, id_list[i].card_name);
} else {
printk(KERN_WARNING "HFC-PCI: No PCI card found\n");
diff --git a/drivers/isdn/hisax/q931.c b/drivers/isdn/hisax/q931.c
index abecabf8c271..aacbf0d14b64 100644
--- a/drivers/isdn/hisax/q931.c
+++ b/drivers/isdn/hisax/q931.c
@@ -1402,12 +1402,12 @@ dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir)
}
/* No, locate it in the table */
if (cset == 0) {
- for (i = 0; i < IESIZE; i++)
+ for (i = 0; i < IESIZE_NI1; i++)
if (*buf == ielist_ni1[i].nr)
break;
/* When not found, give appropriate msg */
- if (i != IESIZE) {
+ if (i != IESIZE_NI1) {
dp += sprintf(dp, " %s\n", ielist_ni1[i].descr);
dp += ielist_ni1[i].f(dp, buf);
} else
diff --git a/drivers/isdn/hisax/telespci.c b/drivers/isdn/hisax/telespci.c
index e2bb4fd8e25e..e82ab2251b82 100644
--- a/drivers/isdn/hisax/telespci.c
+++ b/drivers/isdn/hisax/telespci.c
@@ -311,8 +311,9 @@ setup_telespci(struct IsdnCard *card)
}
cs->hw.teles0.membase = ioremap(pci_resource_start(dev_tel, 0),
PAGE_SIZE);
- printk(KERN_INFO "Found: Zoran, base-address: 0x%lx, irq: 0x%x\n",
- pci_resource_start(dev_tel, 0), dev_tel->irq);
+ printk(KERN_INFO "Found: Zoran, base-address: 0x%llx, irq: 0x%x\n",
+ (unsigned long long)pci_resource_start(dev_tel, 0),
+ dev_tel->irq);
} else {
printk(KERN_WARNING "TelesPCI: No PCI card found\n");
return(0);
diff --git a/drivers/isdn/i4l/isdn_common.c b/drivers/isdn/i4l/isdn_common.c
index 81accdf35168..b26e509ec759 100644
--- a/drivers/isdn/i4l/isdn_common.c
+++ b/drivers/isdn/i4l/isdn_common.c
@@ -933,7 +933,7 @@ isdn_readbchan_tty(int di, int channel, struct tty_struct *tty, int cisco_hack)
count_put = count_pull;
if(count_put > 1)
tty_insert_flip_string(tty, skb->data, count_put - 1);
- last = skb->data[count_put] - 1;
+ last = skb->data[count_put - 1];
len -= count_put;
#ifdef CONFIG_ISDN_AUDIO
}
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
index 2ac90242d263..433389daedb2 100644
--- a/drivers/isdn/i4l/isdn_tty.c
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -82,7 +82,7 @@ isdn_tty_try_read(modem_info * info, struct sk_buff *skb)
int l = skb->len;
unsigned char *dp = skb->data;
while (--l) {
- if (*skb->data == DLE)
+ if (*dp == DLE)
tty_insert_flip_char(tty, DLE, 0);
tty_insert_flip_char(tty, *dp++, 0);
}
diff --git a/drivers/isdn/i4l/isdn_x25iface.c b/drivers/isdn/i4l/isdn_x25iface.c
index 743ac4077f35..8b3efc243161 100644
--- a/drivers/isdn/i4l/isdn_x25iface.c
+++ b/drivers/isdn/i4l/isdn_x25iface.c
@@ -208,7 +208,7 @@ static int isdn_x25iface_receive(struct concap_proto *cprot, struct sk_buff *skb
*/
static int isdn_x25iface_connect_ind(struct concap_proto *cprot)
{
- struct sk_buff * skb = dev_alloc_skb(1);
+ struct sk_buff * skb;
enum wan_states *state_p
= &( ( (ix25_pdata_t*) (cprot->proto_data) ) -> state);
IX25DEBUG( "isdn_x25iface_connect_ind %s \n"
@@ -220,6 +220,8 @@ static int isdn_x25iface_connect_ind(struct concap_proto *cprot)
return -1;
}
*state_p = WAN_CONNECTED;
+
+ skb = dev_alloc_skb(1);
if( skb ){
*( skb_put(skb, 1) ) = 0x01;
skb->protocol = x25_type_trans(skb, cprot->net_dev);
diff --git a/drivers/leds/led-core.c b/drivers/leds/led-core.c
index fe6541326c71..9b015f9af351 100644
--- a/drivers/leds/led-core.c
+++ b/drivers/leds/led-core.c
@@ -18,7 +18,7 @@
#include <linux/leds.h>
#include "leds.h"
-rwlock_t leds_list_lock = RW_LOCK_UNLOCKED;
+DEFINE_RWLOCK(leds_list_lock);
LIST_HEAD(leds_list);
EXPORT_SYMBOL_GPL(leds_list);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 5e2cd8be1191..1b1ce6523960 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -26,7 +26,7 @@
/*
* Nests outside led_cdev->trigger_lock
*/
-static rwlock_t triggers_list_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(triggers_list_lock);
static LIST_HEAD(trigger_list);
ssize_t led_trigger_store(struct class_device *dev, const char *buf,
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 8972e53d2dcb..45a268f8047e 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -11,7 +11,7 @@ obj-$(CONFIG_MAC_EMUMOUSEBTN) += mac_hid.o
obj-$(CONFIG_INPUT_ADBHID) += adbhid.o
obj-$(CONFIG_ANSLCD) += ans-lcd.o
-obj-$(CONFIG_ADB_PMU) += via-pmu.o
+obj-$(CONFIG_ADB_PMU) += via-pmu.o via-pmu-event.o
obj-$(CONFIG_PMAC_BACKLIGHT) += via-pmu-backlight.o
obj-$(CONFIG_ADB_CUDA) += via-cuda.o
obj-$(CONFIG_PMAC_APM_EMU) += apm_emu.o
diff --git a/drivers/macintosh/adbhid.c b/drivers/macintosh/adbhid.c
index c26e1236b275..cbfbbe2b150a 100644
--- a/drivers/macintosh/adbhid.c
+++ b/drivers/macintosh/adbhid.c
@@ -179,7 +179,7 @@ u8 adb_to_linux_keycodes[128] = {
/* 0x65 */ KEY_F9, /* 67 */
/* 0x66 */ KEY_HANJA, /* 123 */
/* 0x67 */ KEY_F11, /* 87 */
- /* 0x68 */ KEY_HANGUEL, /* 122 */
+ /* 0x68 */ KEY_HANGEUL, /* 122 */
/* 0x69 */ KEY_SYSRQ, /* 99 */
/* 0x6a */ 0,
/* 0x6b */ KEY_SCROLLLOCK, /* 70 */
diff --git a/drivers/macintosh/macio_asic.c b/drivers/macintosh/macio_asic.c
index 431bd37225a1..c687ac703941 100644
--- a/drivers/macintosh/macio_asic.c
+++ b/drivers/macintosh/macio_asic.c
@@ -428,10 +428,10 @@ static struct macio_dev * macio_add_one_device(struct macio_chip *chip,
/* MacIO itself has a different reg, we use it's PCI base */
if (np == chip->of_node) {
- sprintf(dev->ofdev.dev.bus_id, "%1d.%08lx:%.*s",
+ sprintf(dev->ofdev.dev.bus_id, "%1d.%016llx:%.*s",
chip->lbus.index,
#ifdef CONFIG_PCI
- pci_resource_start(chip->lbus.pdev, 0),
+ (unsigned long long)pci_resource_start(chip->lbus.pdev, 0),
#else
0, /* NuBus may want to do something better here */
#endif
diff --git a/drivers/macintosh/via-pmu-event.c b/drivers/macintosh/via-pmu-event.c
new file mode 100644
index 000000000000..25cd56542328
--- /dev/null
+++ b/drivers/macintosh/via-pmu-event.c
@@ -0,0 +1,80 @@
+/*
+ * via-pmu event device for reporting some events that come through the PMU
+ *
+ * Copyright 2006 Johannes Berg <johannes@sipsolutions.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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include <linux/input.h>
+#include <linux/adb.h>
+#include <linux/pmu.h>
+#include "via-pmu-event.h"
+
+static struct input_dev *pmu_input_dev;
+
+static int __init via_pmu_event_init(void)
+{
+ int err;
+
+ /* do other models report button/lid status? */
+ if (pmu_get_model() != PMU_KEYLARGO_BASED)
+ return -ENODEV;
+
+ pmu_input_dev = input_allocate_device();
+ if (!pmu_input_dev)
+ return -ENOMEM;
+
+ pmu_input_dev->name = "PMU";
+ pmu_input_dev->id.bustype = BUS_HOST;
+ pmu_input_dev->id.vendor = 0x0001;
+ pmu_input_dev->id.product = 0x0001;
+ pmu_input_dev->id.version = 0x0100;
+
+ set_bit(EV_KEY, pmu_input_dev->evbit);
+ set_bit(EV_SW, pmu_input_dev->evbit);
+ set_bit(KEY_POWER, pmu_input_dev->keybit);
+ set_bit(SW_LID, pmu_input_dev->swbit);
+
+ err = input_register_device(pmu_input_dev);
+ if (err)
+ input_free_device(pmu_input_dev);
+ return err;
+}
+
+void via_pmu_event(int key, int down)
+{
+
+ if (unlikely(!pmu_input_dev))
+ return;
+
+ switch (key) {
+ case PMU_EVT_POWER:
+ input_report_key(pmu_input_dev, KEY_POWER, down);
+ break;
+ case PMU_EVT_LID:
+ input_report_switch(pmu_input_dev, SW_LID, down);
+ break;
+ default:
+ /* no such key handled */
+ return;
+ }
+
+ input_sync(pmu_input_dev);
+}
+
+late_initcall(via_pmu_event_init);
diff --git a/drivers/macintosh/via-pmu-event.h b/drivers/macintosh/via-pmu-event.h
new file mode 100644
index 000000000000..72c54de408e8
--- /dev/null
+++ b/drivers/macintosh/via-pmu-event.h
@@ -0,0 +1,8 @@
+#ifndef __VIA_PMU_EVENT_H
+#define __VIA_PMU_EVENT_H
+
+#define PMU_EVT_POWER 0
+#define PMU_EVT_LID 1
+extern void via_pmu_event(int key, int down);
+
+#endif /* __VIA_PMU_EVENT_H */
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 2a355ae59562..1ab4f16c08b9 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -69,6 +69,8 @@
#include <asm/open_pic.h>
#endif
+#include "via-pmu-event.h"
+
/* Some compile options */
#undef SUSPEND_USES_PMU
#define DEBUG_SLEEP
@@ -1427,6 +1429,12 @@ next:
if (pmu_battery_count)
query_battery_state();
pmu_pass_intr(data, len);
+ /* len == 6 is probably a bad check. But how do I
+ * know what PMU versions send what events here? */
+ if (len == 6) {
+ via_pmu_event(PMU_EVT_POWER, !!(data[1]&8));
+ via_pmu_event(PMU_EVT_LID, data[1]&1);
+ }
} else {
pmu_pass_intr(data, len);
}
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index ac25a48362ac..bf869ed03eed 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -90,7 +90,7 @@ config MD_RAID10
depends on BLK_DEV_MD && EXPERIMENTAL
---help---
RAID-10 provides a combination of striping (RAID-0) and
- mirroring (RAID-1) with easier configuration and more flexable
+ mirroring (RAID-1) with easier configuration and more flexible
layout.
Unlike RAID-0, but like RAID-1, RAID-10 requires all devices to
be the same size (or at least, only as much as the smallest device
@@ -104,8 +104,8 @@ config MD_RAID10
If unsure, say Y.
-config MD_RAID5
- tristate "RAID-4/RAID-5 mode"
+config MD_RAID456
+ tristate "RAID-4/RAID-5/RAID-6 mode"
depends on BLK_DEV_MD
---help---
A RAID-5 set of N drives with a capacity of C MB per drive provides
@@ -116,20 +116,28 @@ config MD_RAID5
while a RAID-5 set distributes the parity across the drives in one
of the available parity distribution methods.
+ A RAID-6 set of N drives with a capacity of C MB per drive
+ provides the capacity of C * (N - 2) MB, and protects
+ against a failure of any two drives. For a given sector
+ (row) number, (N - 2) drives contain data sectors, and two
+ drives contains two independent redundancy syndromes. Like
+ RAID-5, RAID-6 distributes the syndromes across the drives
+ in one of the available parity distribution methods.
+
Information about Software RAID on Linux is contained in the
Software-RAID mini-HOWTO, available from
<http://www.tldp.org/docs.html#howto>. There you will also
learn where to get the supporting user space utilities raidtools.
- If you want to use such a RAID-4/RAID-5 set, say Y. To
+ If you want to use such a RAID-4/RAID-5/RAID-6 set, say Y. To
compile this code as a module, choose M here: the module
- will be called raid5.
+ will be called raid456.
If unsure, say Y.
config MD_RAID5_RESHAPE
bool "Support adding drives to a raid-5 array (experimental)"
- depends on MD_RAID5 && EXPERIMENTAL
+ depends on MD_RAID456 && EXPERIMENTAL
---help---
A RAID-5 set can be expanded by adding extra drives. This
requires "restriping" the array which means (almost) every
@@ -139,7 +147,7 @@ config MD_RAID5_RESHAPE
is online. However it is still EXPERIMENTAL code. It should
work, but please be sure that you have backups.
- You will need mdadm verion 2.4.1 or later to use this
+ You will need mdadm version 2.4.1 or later to use this
feature safely. During the early stage of reshape there is
a critical section where live data is being over-written. A
crash during this time needs extra care for recovery. The
@@ -154,28 +162,6 @@ config MD_RAID5_RESHAPE
There should be enough spares already present to make the new
array workable.
-config MD_RAID6
- tristate "RAID-6 mode"
- depends on BLK_DEV_MD
- ---help---
- A RAID-6 set of N drives with a capacity of C MB per drive
- provides the capacity of C * (N - 2) MB, and protects
- against a failure of any two drives. For a given sector
- (row) number, (N - 2) drives contain data sectors, and two
- drives contains two independent redundancy syndromes. Like
- RAID-5, RAID-6 distributes the syndromes across the drives
- in one of the available parity distribution methods.
-
- RAID-6 requires mdadm-1.5.0 or later, available at:
-
- ftp://ftp.kernel.org/pub/linux/utils/raid/mdadm/
-
- If you want to use such a RAID-6 set, say Y. To compile
- this code as a module, choose M here: the module will be
- called raid6.
-
- If unsure, say Y.
-
config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
@@ -235,7 +221,7 @@ config DM_SNAPSHOT
tristate "Snapshot target (EXPERIMENTAL)"
depends on BLK_DEV_DM && EXPERIMENTAL
---help---
- Allow volume managers to take writeable snapshots of a device.
+ Allow volume managers to take writable snapshots of a device.
config DM_MIRROR
tristate "Mirror target (EXPERIMENTAL)"
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index d3efedf6a6ad..34957a68d921 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -8,7 +8,7 @@ dm-multipath-objs := dm-hw-handler.o dm-path-selector.o dm-mpath.o
dm-snapshot-objs := dm-snap.o dm-exception-store.o
dm-mirror-objs := dm-log.o dm-raid1.o
md-mod-objs := md.o bitmap.o
-raid6-objs := raid6main.o raid6algos.o raid6recov.o raid6tables.o \
+raid456-objs := raid5.o raid6algos.o raid6recov.o raid6tables.o \
raid6int1.o raid6int2.o raid6int4.o \
raid6int8.o raid6int16.o raid6int32.o \
raid6altivec1.o raid6altivec2.o raid6altivec4.o \
@@ -25,8 +25,7 @@ obj-$(CONFIG_MD_LINEAR) += linear.o
obj-$(CONFIG_MD_RAID0) += raid0.o
obj-$(CONFIG_MD_RAID1) += raid1.o
obj-$(CONFIG_MD_RAID10) += raid10.o
-obj-$(CONFIG_MD_RAID5) += raid5.o xor.o
-obj-$(CONFIG_MD_RAID6) += raid6.o xor.o
+obj-$(CONFIG_MD_RAID456) += raid456.o xor.o
obj-$(CONFIG_MD_MULTIPATH) += multipath.o
obj-$(CONFIG_MD_FAULTY) += faulty.o
obj-$(CONFIG_BLK_DEV_MD) += md-mod.o
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index f8ffaee20ff8..ebbd2d856256 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -7,7 +7,6 @@
* additions, Copyright (C) 2003-2004, Paul Clements, SteelEye Technology, Inc.:
* - added disk storage for bitmap
* - changes to allow various bitmap chunk sizes
- * - added bitmap daemon (to asynchronously clear bitmap bits from disk)
*/
/*
@@ -15,9 +14,6 @@
*
* flush after percent set rather than just time based. (maybe both).
* wait if count gets too high, wake when it drops to half.
- * allow bitmap to be mirrored with superblock (before or after...)
- * allow hot-add to re-instate a current device.
- * allow hot-add of bitmap after quiessing device
*/
#include <linux/module.h>
@@ -73,24 +69,6 @@ static inline char * bmname(struct bitmap *bitmap)
/*
- * test if the bitmap is active
- */
-int bitmap_active(struct bitmap *bitmap)
-{
- unsigned long flags;
- int res = 0;
-
- if (!bitmap)
- return res;
- spin_lock_irqsave(&bitmap->lock, flags);
- res = bitmap->flags & BITMAP_ACTIVE;
- spin_unlock_irqrestore(&bitmap->lock, flags);
- return res;
-}
-
-#define WRITE_POOL_SIZE 256
-
-/*
* just a placeholder - calls kmalloc for bitmap pages
*/
static unsigned char *bitmap_alloc_page(struct bitmap *bitmap)
@@ -269,6 +247,8 @@ static struct page *read_sb_page(mddev_t *mddev, long offset, unsigned long inde
if (sync_page_io(rdev->bdev, target, PAGE_SIZE, page, READ)) {
page->index = index;
+ attach_page_buffers(page, NULL); /* so that free_buffer will
+ * quietly no-op */
return page;
}
}
@@ -300,77 +280,132 @@ static int write_sb_page(mddev_t *mddev, long offset, struct page *page, int wai
*/
static int write_page(struct bitmap *bitmap, struct page *page, int wait)
{
- int ret = -ENOMEM;
+ struct buffer_head *bh;
if (bitmap->file == NULL)
return write_sb_page(bitmap->mddev, bitmap->offset, page, wait);
- flush_dcache_page(page); /* make sure visible to anyone reading the file */
+ bh = page_buffers(page);
- if (wait)
- lock_page(page);
- else {
- if (TestSetPageLocked(page))
- return -EAGAIN; /* already locked */
- if (PageWriteback(page)) {
- unlock_page(page);
- return -EAGAIN;
- }
+ while (bh && bh->b_blocknr) {
+ atomic_inc(&bitmap->pending_writes);
+ set_buffer_locked(bh);
+ set_buffer_mapped(bh);
+ submit_bh(WRITE, bh);
+ bh = bh->b_this_page;
}
- ret = page->mapping->a_ops->prepare_write(bitmap->file, page, 0, PAGE_SIZE);
- if (!ret)
- ret = page->mapping->a_ops->commit_write(bitmap->file, page, 0,
- PAGE_SIZE);
- if (ret) {
- unlock_page(page);
- return ret;
+ if (wait) {
+ wait_event(bitmap->write_wait,
+ atomic_read(&bitmap->pending_writes)==0);
+ return (bitmap->flags & BITMAP_WRITE_ERROR) ? -EIO : 0;
}
+ return 0;
+}
- set_page_dirty(page); /* force it to be written out */
-
- if (!wait) {
- /* add to list to be waited for by daemon */
- struct page_list *item = mempool_alloc(bitmap->write_pool, GFP_NOIO);
- item->page = page;
- get_page(page);
- spin_lock(&bitmap->write_lock);
- list_add(&item->list, &bitmap->complete_pages);
- spin_unlock(&bitmap->write_lock);
- md_wakeup_thread(bitmap->writeback_daemon);
+static void end_bitmap_write(struct buffer_head *bh, int uptodate)
+{
+ struct bitmap *bitmap = bh->b_private;
+ unsigned long flags;
+
+ if (!uptodate) {
+ spin_lock_irqsave(&bitmap->lock, flags);
+ bitmap->flags |= BITMAP_WRITE_ERROR;
+ spin_unlock_irqrestore(&bitmap->lock, flags);
+ }
+ if (atomic_dec_and_test(&bitmap->pending_writes))
+ wake_up(&bitmap->write_wait);
+}
+
+/* copied from buffer.c */
+static void
+__clear_page_buffers(struct page *page)
+{
+ ClearPagePrivate(page);
+ set_page_private(page, 0);
+ page_cache_release(page);
+}
+static void free_buffers(struct page *page)
+{
+ struct buffer_head *bh = page_buffers(page);
+
+ while (bh) {
+ struct buffer_head *next = bh->b_this_page;
+ free_buffer_head(bh);
+ bh = next;
}
- return write_one_page(page, wait);
+ __clear_page_buffers(page);
+ put_page(page);
}
-/* read a page from a file, pinning it into cache, and return bytes_read */
+/* read a page from a file.
+ * We both read the page, and attach buffers to the page to record the
+ * address of each block (using bmap). These addresses will be used
+ * to write the block later, completely bypassing the filesystem.
+ * This usage is similar to how swap files are handled, and allows us
+ * to write to a file with no concerns of memory allocation failing.
+ */
static struct page *read_page(struct file *file, unsigned long index,
- unsigned long *bytes_read)
+ struct bitmap *bitmap,
+ unsigned long count)
{
- struct inode *inode = file->f_mapping->host;
struct page *page = NULL;
- loff_t isize = i_size_read(inode);
- unsigned long end_index = isize >> PAGE_SHIFT;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct buffer_head *bh;
+ sector_t block;
PRINTK("read bitmap file (%dB @ %Lu)\n", (int)PAGE_SIZE,
(unsigned long long)index << PAGE_SHIFT);
- page = read_cache_page(inode->i_mapping, index,
- (filler_t *)inode->i_mapping->a_ops->readpage, file);
+ page = alloc_page(GFP_KERNEL);
+ if (!page)
+ page = ERR_PTR(-ENOMEM);
if (IS_ERR(page))
goto out;
- wait_on_page_locked(page);
- if (!PageUptodate(page) || PageError(page)) {
+
+ bh = alloc_page_buffers(page, 1<<inode->i_blkbits, 0);
+ if (!bh) {
put_page(page);
- page = ERR_PTR(-EIO);
+ page = ERR_PTR(-ENOMEM);
goto out;
}
+ attach_page_buffers(page, bh);
+ block = index << (PAGE_SHIFT - inode->i_blkbits);
+ while (bh) {
+ if (count == 0)
+ bh->b_blocknr = 0;
+ else {
+ bh->b_blocknr = bmap(inode, block);
+ if (bh->b_blocknr == 0) {
+ /* Cannot use this file! */
+ free_buffers(page);
+ page = ERR_PTR(-EINVAL);
+ goto out;
+ }
+ bh->b_bdev = inode->i_sb->s_bdev;
+ if (count < (1<<inode->i_blkbits))
+ count = 0;
+ else
+ count -= (1<<inode->i_blkbits);
+
+ bh->b_end_io = end_bitmap_write;
+ bh->b_private = bitmap;
+ atomic_inc(&bitmap->pending_writes);
+ set_buffer_locked(bh);
+ set_buffer_mapped(bh);
+ submit_bh(READ, bh);
+ }
+ block++;
+ bh = bh->b_this_page;
+ }
+ page->index = index;
- if (index > end_index) /* we have read beyond EOF */
- *bytes_read = 0;
- else if (index == end_index) /* possible short read */
- *bytes_read = isize & ~PAGE_MASK;
- else
- *bytes_read = PAGE_SIZE; /* got a full page */
+ wait_event(bitmap->write_wait,
+ atomic_read(&bitmap->pending_writes)==0);
+ if (bitmap->flags & BITMAP_WRITE_ERROR) {
+ free_buffers(page);
+ page = ERR_PTR(-EIO);
+ }
out:
if (IS_ERR(page))
printk(KERN_ALERT "md: bitmap read error: (%dB @ %Lu): %ld\n",
@@ -441,16 +476,14 @@ static int bitmap_read_sb(struct bitmap *bitmap)
char *reason = NULL;
bitmap_super_t *sb;
unsigned long chunksize, daemon_sleep, write_behind;
- unsigned long bytes_read;
unsigned long long events;
int err = -EINVAL;
/* page 0 is the superblock, read it... */
if (bitmap->file)
- bitmap->sb_page = read_page(bitmap->file, 0, &bytes_read);
+ bitmap->sb_page = read_page(bitmap->file, 0, bitmap, PAGE_SIZE);
else {
bitmap->sb_page = read_sb_page(bitmap->mddev, bitmap->offset, 0);
- bytes_read = PAGE_SIZE;
}
if (IS_ERR(bitmap->sb_page)) {
err = PTR_ERR(bitmap->sb_page);
@@ -460,13 +493,6 @@ static int bitmap_read_sb(struct bitmap *bitmap)
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
- if (bytes_read < sizeof(*sb)) { /* short read */
- printk(KERN_INFO "%s: bitmap file superblock truncated\n",
- bmname(bitmap));
- err = -ENOSPC;
- goto out;
- }
-
chunksize = le32_to_cpu(sb->chunksize);
daemon_sleep = le32_to_cpu(sb->daemon_sleep);
write_behind = le32_to_cpu(sb->write_behind);
@@ -550,7 +576,6 @@ static void bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
spin_unlock_irqrestore(&bitmap->lock, flags);
return;
}
- get_page(bitmap->sb_page);
spin_unlock_irqrestore(&bitmap->lock, flags);
sb = (bitmap_super_t *)kmap_atomic(bitmap->sb_page, KM_USER0);
switch (op) {
@@ -561,7 +586,6 @@ static void bitmap_mask_state(struct bitmap *bitmap, enum bitmap_state bits,
default: BUG();
}
kunmap_atomic(sb, KM_USER0);
- put_page(bitmap->sb_page);
}
/*
@@ -614,48 +638,17 @@ static void bitmap_file_unmap(struct bitmap *bitmap)
while (pages--)
if (map[pages]->index != 0) /* 0 is sb_page, release it below */
- put_page(map[pages]);
+ free_buffers(map[pages]);
kfree(map);
kfree(attr);
- safe_put_page(sb_page);
-}
-
-static void bitmap_stop_daemon(struct bitmap *bitmap);
-
-/* dequeue the next item in a page list -- don't call from irq context */
-static struct page_list *dequeue_page(struct bitmap *bitmap)
-{
- struct page_list *item = NULL;
- struct list_head *head = &bitmap->complete_pages;
-
- spin_lock(&bitmap->write_lock);
- if (list_empty(head))
- goto out;
- item = list_entry(head->prev, struct page_list, list);
- list_del(head->prev);
-out:
- spin_unlock(&bitmap->write_lock);
- return item;
-}
-
-static void drain_write_queues(struct bitmap *bitmap)
-{
- struct page_list *item;
-
- while ((item = dequeue_page(bitmap))) {
- /* don't bother to wait */
- put_page(item->page);
- mempool_free(item, bitmap->write_pool);
- }
-
- wake_up(&bitmap->write_wait);
+ if (sb_page)
+ free_buffers(sb_page);
}
static void bitmap_file_put(struct bitmap *bitmap)
{
struct file *file;
- struct inode *inode;
unsigned long flags;
spin_lock_irqsave(&bitmap->lock, flags);
@@ -663,17 +656,14 @@ static void bitmap_file_put(struct bitmap *bitmap)
bitmap->file = NULL;
spin_unlock_irqrestore(&bitmap->lock, flags);
- bitmap_stop_daemon(bitmap);
-
- drain_write_queues(bitmap);
-
+ if (file)
+ wait_event(bitmap->write_wait,
+ atomic_read(&bitmap->pending_writes)==0);
bitmap_file_unmap(bitmap);
if (file) {
- inode = file->f_mapping->host;
- spin_lock(&inode->i_lock);
- atomic_set(&inode->i_writecount, 1); /* allow writes again */
- spin_unlock(&inode->i_lock);
+ struct inode *inode = file->f_dentry->d_inode;
+ invalidate_inode_pages(inode->i_mapping);
fput(file);
}
}
@@ -708,26 +698,27 @@ static void bitmap_file_kick(struct bitmap *bitmap)
}
enum bitmap_page_attr {
- BITMAP_PAGE_DIRTY = 1, // there are set bits that need to be synced
- BITMAP_PAGE_CLEAN = 2, // there are bits that might need to be cleared
- BITMAP_PAGE_NEEDWRITE=4, // there are cleared bits that need to be synced
+ BITMAP_PAGE_DIRTY = 0, // there are set bits that need to be synced
+ BITMAP_PAGE_CLEAN = 1, // there are bits that might need to be cleared
+ BITMAP_PAGE_NEEDWRITE=2, // there are cleared bits that need to be synced
};
static inline void set_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- bitmap->filemap_attr[page->index] |= attr;
+ __set_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
static inline void clear_page_attr(struct bitmap *bitmap, struct page *page,
enum bitmap_page_attr attr)
{
- bitmap->filemap_attr[page->index] &= ~attr;
+ __clear_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
-static inline unsigned long get_page_attr(struct bitmap *bitmap, struct page *page)
+static inline unsigned long test_page_attr(struct bitmap *bitmap, struct page *page,
+ enum bitmap_page_attr attr)
{
- return bitmap->filemap_attr[page->index];
+ return test_bit((page->index<<2) + attr, bitmap->filemap_attr);
}
/*
@@ -751,11 +742,6 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
page = filemap_get_page(bitmap, chunk);
bit = file_page_offset(chunk);
-
- /* make sure the page stays cached until it gets written out */
- if (! (get_page_attr(bitmap, page) & BITMAP_PAGE_DIRTY))
- get_page(page);
-
/* set the bit */
kaddr = kmap_atomic(page, KM_USER0);
if (bitmap->flags & BITMAP_HOSTENDIAN)
@@ -775,7 +761,8 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
* sync the dirty pages of the bitmap file to disk */
int bitmap_unplug(struct bitmap *bitmap)
{
- unsigned long i, attr, flags;
+ unsigned long i, flags;
+ int dirty, need_write;
struct page *page;
int wait = 0;
int err;
@@ -792,35 +779,26 @@ int bitmap_unplug(struct bitmap *bitmap)
return 0;
}
page = bitmap->filemap[i];
- attr = get_page_attr(bitmap, page);
+ dirty = test_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
+ need_write = test_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
clear_page_attr(bitmap, page, BITMAP_PAGE_DIRTY);
clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
- if ((attr & BITMAP_PAGE_DIRTY))
+ if (dirty)
wait = 1;
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (attr & (BITMAP_PAGE_DIRTY | BITMAP_PAGE_NEEDWRITE)) {
+ if (dirty | need_write)
err = write_page(bitmap, page, 0);
- if (err == -EAGAIN) {
- if (attr & BITMAP_PAGE_DIRTY)
- err = write_page(bitmap, page, 1);
- else
- err = 0;
- }
- if (err)
- return 1;
- }
}
if (wait) { /* if any writes were performed, we need to wait on them */
- if (bitmap->file) {
- spin_lock_irq(&bitmap->write_lock);
- wait_event_lock_irq(bitmap->write_wait,
- list_empty(&bitmap->complete_pages), bitmap->write_lock,
- wake_up_process(bitmap->writeback_daemon->tsk));
- spin_unlock_irq(&bitmap->write_lock);
- } else
+ if (bitmap->file)
+ wait_event(bitmap->write_wait,
+ atomic_read(&bitmap->pending_writes)==0);
+ else
md_super_wait(bitmap->mddev);
}
+ if (bitmap->flags & BITMAP_WRITE_ERROR)
+ bitmap_file_kick(bitmap);
return 0;
}
@@ -842,7 +820,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
struct page *page = NULL, *oldpage = NULL;
unsigned long num_pages, bit_cnt = 0;
struct file *file;
- unsigned long bytes, offset, dummy;
+ unsigned long bytes, offset;
int outofdate;
int ret = -ENOSPC;
void *paddr;
@@ -879,7 +857,12 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
if (!bitmap->filemap)
goto out;
- bitmap->filemap_attr = kzalloc(sizeof(long) * num_pages, GFP_KERNEL);
+ /* We need 4 bits per page, rounded up to a multiple of sizeof(unsigned long) */
+ bitmap->filemap_attr = kzalloc(
+ (((num_pages*4/8)+sizeof(unsigned long)-1)
+ /sizeof(unsigned long))
+ *sizeof(unsigned long),
+ GFP_KERNEL);
if (!bitmap->filemap_attr)
goto out;
@@ -890,7 +873,12 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
index = file_page_index(i);
bit = file_page_offset(i);
if (index != oldindex) { /* this is a new page, read it in */
+ int count;
/* unmap the old page, we're done with it */
+ if (index == num_pages-1)
+ count = bytes - index * PAGE_SIZE;
+ else
+ count = PAGE_SIZE;
if (index == 0) {
/*
* if we're here then the superblock page
@@ -900,7 +888,7 @@ static int bitmap_init_from_disk(struct bitmap *bitmap, sector_t start)
page = bitmap->sb_page;
offset = sizeof(bitmap_super_t);
} else if (file) {
- page = read_page(file, index, &dummy);
+ page = read_page(file, index, bitmap, count);
offset = 0;
} else {
page = read_sb_page(bitmap->mddev, bitmap->offset, index);
@@ -971,12 +959,11 @@ void bitmap_write_all(struct bitmap *bitmap)
/* We don't actually write all bitmap blocks here,
* just flag them as needing to be written
*/
+ int i;
- unsigned long chunks = bitmap->chunks;
- unsigned long bytes = (chunks+7)/8 + sizeof(bitmap_super_t);
- unsigned long num_pages = (bytes + PAGE_SIZE-1) / PAGE_SIZE;
- while (num_pages--)
- bitmap->filemap_attr[num_pages] |= BITMAP_PAGE_NEEDWRITE;
+ for (i=0; i < bitmap->file_pages; i++)
+ set_page_attr(bitmap, bitmap->filemap[i],
+ BITMAP_PAGE_NEEDWRITE);
}
@@ -1007,7 +994,6 @@ int bitmap_daemon_work(struct bitmap *bitmap)
struct page *page = NULL, *lastpage = NULL;
int err = 0;
int blocks;
- int attr;
void *paddr;
if (bitmap == NULL)
@@ -1029,43 +1015,34 @@ int bitmap_daemon_work(struct bitmap *bitmap)
if (page != lastpage) {
/* skip this page unless it's marked as needing cleaning */
- if (!((attr=get_page_attr(bitmap, page)) & BITMAP_PAGE_CLEAN)) {
- if (attr & BITMAP_PAGE_NEEDWRITE) {
- get_page(page);
+ if (!test_page_attr(bitmap, page, BITMAP_PAGE_CLEAN)) {
+ int need_write = test_page_attr(bitmap, page,
+ BITMAP_PAGE_NEEDWRITE);
+ if (need_write)
clear_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
- }
+
spin_unlock_irqrestore(&bitmap->lock, flags);
- if (attr & BITMAP_PAGE_NEEDWRITE) {
+ if (need_write) {
switch (write_page(bitmap, page, 0)) {
- case -EAGAIN:
- set_page_attr(bitmap, page, BITMAP_PAGE_NEEDWRITE);
- break;
case 0:
break;
default:
bitmap_file_kick(bitmap);
}
- put_page(page);
}
continue;
}
/* grab the new page, sync and release the old */
- get_page(page);
if (lastpage != NULL) {
- if (get_page_attr(bitmap, lastpage) & BITMAP_PAGE_NEEDWRITE) {
+ if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
err = write_page(bitmap, lastpage, 0);
- if (err == -EAGAIN) {
- err = 0;
- set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
- }
} else {
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
}
- put_page(lastpage);
if (err)
bitmap_file_kick(bitmap);
} else
@@ -1107,131 +1084,19 @@ int bitmap_daemon_work(struct bitmap *bitmap)
/* now sync the final page */
if (lastpage != NULL) {
spin_lock_irqsave(&bitmap->lock, flags);
- if (get_page_attr(bitmap, lastpage) &BITMAP_PAGE_NEEDWRITE) {
+ if (test_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE)) {
clear_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
err = write_page(bitmap, lastpage, 0);
- if (err == -EAGAIN) {
- set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
- err = 0;
- }
} else {
set_page_attr(bitmap, lastpage, BITMAP_PAGE_NEEDWRITE);
spin_unlock_irqrestore(&bitmap->lock, flags);
}
-
- put_page(lastpage);
}
return err;
}
-static void daemon_exit(struct bitmap *bitmap, mdk_thread_t **daemon)
-{
- mdk_thread_t *dmn;
- unsigned long flags;
-
- /* if no one is waiting on us, we'll free the md thread struct
- * and exit, otherwise we let the waiter clean things up */
- spin_lock_irqsave(&bitmap->lock, flags);
- if ((dmn = *daemon)) { /* no one is waiting, cleanup and exit */
- *daemon = NULL;
- spin_unlock_irqrestore(&bitmap->lock, flags);
- kfree(dmn);
- complete_and_exit(NULL, 0); /* do_exit not exported */
- }
- spin_unlock_irqrestore(&bitmap->lock, flags);
-}
-
-static void bitmap_writeback_daemon(mddev_t *mddev)
-{
- struct bitmap *bitmap = mddev->bitmap;
- struct page *page;
- struct page_list *item;
- int err = 0;
-
- if (signal_pending(current)) {
- printk(KERN_INFO
- "%s: bitmap writeback daemon got signal, exiting...\n",
- bmname(bitmap));
- err = -EINTR;
- goto out;
- }
- if (bitmap == NULL)
- /* about to be stopped. */
- return;
-
- PRINTK("%s: bitmap writeback daemon woke up...\n", bmname(bitmap));
- /* wait on bitmap page writebacks */
- while ((item = dequeue_page(bitmap))) {
- page = item->page;
- mempool_free(item, bitmap->write_pool);
- PRINTK("wait on page writeback: %p\n", page);
- wait_on_page_writeback(page);
- PRINTK("finished page writeback: %p\n", page);
-
- err = PageError(page);
- put_page(page);
- if (err) {
- printk(KERN_WARNING "%s: bitmap file writeback "
- "failed (page %lu): %d\n",
- bmname(bitmap), page->index, err);
- bitmap_file_kick(bitmap);
- goto out;
- }
- }
- out:
- wake_up(&bitmap->write_wait);
- if (err) {
- printk(KERN_INFO "%s: bitmap writeback daemon exiting (%d)\n",
- bmname(bitmap), err);
- daemon_exit(bitmap, &bitmap->writeback_daemon);
- }
-}
-
-static mdk_thread_t *bitmap_start_daemon(struct bitmap *bitmap,
- void (*func)(mddev_t *), char *name)
-{
- mdk_thread_t *daemon;
- char namebuf[32];
-
-#ifdef INJECT_FATAL_FAULT_2
- daemon = NULL;
-#else
- sprintf(namebuf, "%%s_%s", name);
- daemon = md_register_thread(func, bitmap->mddev, namebuf);
-#endif
- if (!daemon) {
- printk(KERN_ERR "%s: failed to start bitmap daemon\n",
- bmname(bitmap));
- return ERR_PTR(-ECHILD);
- }
-
- md_wakeup_thread(daemon); /* start it running */
-
- PRINTK("%s: %s daemon (pid %d) started...\n",
- bmname(bitmap), name, daemon->tsk->pid);
-
- return daemon;
-}
-
-static void bitmap_stop_daemon(struct bitmap *bitmap)
-{
- /* the daemon can't stop itself... it'll just exit instead... */
- if (bitmap->writeback_daemon && ! IS_ERR(bitmap->writeback_daemon) &&
- current->pid != bitmap->writeback_daemon->tsk->pid) {
- mdk_thread_t *daemon;
- unsigned long flags;
-
- spin_lock_irqsave(&bitmap->lock, flags);
- daemon = bitmap->writeback_daemon;
- bitmap->writeback_daemon = NULL;
- spin_unlock_irqrestore(&bitmap->lock, flags);
- if (daemon && ! IS_ERR(daemon))
- md_unregister_thread(daemon); /* destroy the thread */
- }
-}
-
static bitmap_counter_t *bitmap_get_counter(struct bitmap *bitmap,
sector_t offset, int *blocks,
int create)
@@ -1500,8 +1365,6 @@ static void bitmap_free(struct bitmap *bitmap)
/* free all allocated memory */
- mempool_destroy(bitmap->write_pool);
-
if (bp) /* deallocate the page memory */
for (k = 0; k < pages; k++)
if (bp[k].map && !bp[k].hijacked)
@@ -1549,20 +1412,20 @@ int bitmap_create(mddev_t *mddev)
return -ENOMEM;
spin_lock_init(&bitmap->lock);
- bitmap->mddev = mddev;
-
- spin_lock_init(&bitmap->write_lock);
- INIT_LIST_HEAD(&bitmap->complete_pages);
+ atomic_set(&bitmap->pending_writes, 0);
init_waitqueue_head(&bitmap->write_wait);
- bitmap->write_pool = mempool_create_kmalloc_pool(WRITE_POOL_SIZE,
- sizeof(struct page_list));
- err = -ENOMEM;
- if (!bitmap->write_pool)
- goto error;
+
+ bitmap->mddev = mddev;
bitmap->file = file;
bitmap->offset = mddev->bitmap_offset;
- if (file) get_file(file);
+ if (file) {
+ get_file(file);
+ do_sync_file_range(file, 0, LLONG_MAX,
+ SYNC_FILE_RANGE_WAIT_BEFORE |
+ SYNC_FILE_RANGE_WRITE |
+ SYNC_FILE_RANGE_WAIT_AFTER);
+ }
/* read superblock from bitmap file (this sets bitmap->chunksize) */
err = bitmap_read_sb(bitmap);
if (err)
@@ -1594,8 +1457,6 @@ int bitmap_create(mddev_t *mddev)
if (!bitmap->bp)
goto error;
- bitmap->flags |= BITMAP_ACTIVE;
-
/* now that we have some pages available, initialize the in-memory
* bitmap from the on-disk bitmap */
start = 0;
@@ -1613,15 +1474,6 @@ int bitmap_create(mddev_t *mddev)
mddev->bitmap = bitmap;
- if (file)
- /* kick off the bitmap writeback daemon */
- bitmap->writeback_daemon =
- bitmap_start_daemon(bitmap,
- bitmap_writeback_daemon,
- "bitmap_wb");
-
- if (IS_ERR(bitmap->writeback_daemon))
- return PTR_ERR(bitmap->writeback_daemon);
mddev->thread->timeout = bitmap->daemon_sleep * HZ;
return bitmap_update_sb(bitmap);
@@ -1638,4 +1490,3 @@ EXPORT_SYMBOL(bitmap_start_sync);
EXPORT_SYMBOL(bitmap_end_sync);
EXPORT_SYMBOL(bitmap_unplug);
EXPORT_SYMBOL(bitmap_close_sync);
-EXPORT_SYMBOL(bitmap_daemon_work);
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index 61a590bb6241..6022ed12a795 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -20,7 +20,7 @@
#include "dm.h"
-#define PFX "crypt: "
+#define DM_MSG_PREFIX "crypt"
/*
* per bio private data
@@ -125,19 +125,19 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
u8 *salt;
if (opts == NULL) {
- ti->error = PFX "Digest algorithm missing for ESSIV mode";
+ ti->error = "Digest algorithm missing for ESSIV mode";
return -EINVAL;
}
/* Hash the cipher key with the given hash algorithm */
hash_tfm = crypto_alloc_tfm(opts, CRYPTO_TFM_REQ_MAY_SLEEP);
if (hash_tfm == NULL) {
- ti->error = PFX "Error initializing ESSIV hash";
+ ti->error = "Error initializing ESSIV hash";
return -EINVAL;
}
if (crypto_tfm_alg_type(hash_tfm) != CRYPTO_ALG_TYPE_DIGEST) {
- ti->error = PFX "Expected digest algorithm for ESSIV hash";
+ ti->error = "Expected digest algorithm for ESSIV hash";
crypto_free_tfm(hash_tfm);
return -EINVAL;
}
@@ -145,7 +145,7 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
saltsize = crypto_tfm_alg_digestsize(hash_tfm);
salt = kmalloc(saltsize, GFP_KERNEL);
if (salt == NULL) {
- ti->error = PFX "Error kmallocing salt storage in ESSIV";
+ ti->error = "Error kmallocing salt storage in ESSIV";
crypto_free_tfm(hash_tfm);
return -ENOMEM;
}
@@ -159,20 +159,20 @@ static int crypt_iv_essiv_ctr(struct crypt_config *cc, struct dm_target *ti,
CRYPTO_TFM_MODE_ECB |
CRYPTO_TFM_REQ_MAY_SLEEP);
if (essiv_tfm == NULL) {
- ti->error = PFX "Error allocating crypto tfm for ESSIV";
+ ti->error = "Error allocating crypto tfm for ESSIV";
kfree(salt);
return -EINVAL;
}
if (crypto_tfm_alg_blocksize(essiv_tfm)
!= crypto_tfm_alg_ivsize(cc->tfm)) {
- ti->error = PFX "Block size of ESSIV cipher does "
+ ti->error = "Block size of ESSIV cipher does "
"not match IV size of block cipher";
crypto_free_tfm(essiv_tfm);
kfree(salt);
return -EINVAL;
}
if (crypto_cipher_setkey(essiv_tfm, salt, saltsize) < 0) {
- ti->error = PFX "Failed to set key for ESSIV cipher";
+ ti->error = "Failed to set key for ESSIV cipher";
crypto_free_tfm(essiv_tfm);
kfree(salt);
return -EINVAL;
@@ -521,7 +521,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
unsigned long long tmpll;
if (argc != 5) {
- ti->error = PFX "Not enough arguments";
+ ti->error = "Not enough arguments";
return -EINVAL;
}
@@ -532,21 +532,21 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
ivmode = strsep(&ivopts, ":");
if (tmp)
- DMWARN(PFX "Unexpected additional cipher options");
+ DMWARN("Unexpected additional cipher options");
key_size = strlen(argv[1]) >> 1;
cc = kmalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);
if (cc == NULL) {
ti->error =
- PFX "Cannot allocate transparent encryption context";
+ "Cannot allocate transparent encryption context";
return -ENOMEM;
}
cc->key_size = key_size;
if ((!key_size && strcmp(argv[1], "-") != 0) ||
(key_size && crypt_decode_key(cc->key, argv[1], key_size) < 0)) {
- ti->error = PFX "Error decoding key";
+ ti->error = "Error decoding key";
goto bad1;
}
@@ -562,22 +562,22 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
else if (strcmp(chainmode, "ecb") == 0)
crypto_flags = CRYPTO_TFM_MODE_ECB;
else {
- ti->error = PFX "Unknown chaining mode";
+ ti->error = "Unknown chaining mode";
goto bad1;
}
if (crypto_flags != CRYPTO_TFM_MODE_ECB && !ivmode) {
- ti->error = PFX "This chaining mode requires an IV mechanism";
+ ti->error = "This chaining mode requires an IV mechanism";
goto bad1;
}
tfm = crypto_alloc_tfm(cipher, crypto_flags | CRYPTO_TFM_REQ_MAY_SLEEP);
if (!tfm) {
- ti->error = PFX "Error allocating crypto tfm";
+ ti->error = "Error allocating crypto tfm";
goto bad1;
}
if (crypto_tfm_alg_type(tfm) != CRYPTO_ALG_TYPE_CIPHER) {
- ti->error = PFX "Expected cipher algorithm";
+ ti->error = "Expected cipher algorithm";
goto bad2;
}
@@ -595,7 +595,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
else if (strcmp(ivmode, "essiv") == 0)
cc->iv_gen_ops = &crypt_iv_essiv_ops;
else {
- ti->error = PFX "Invalid IV mode";
+ ti->error = "Invalid IV mode";
goto bad2;
}
@@ -610,7 +610,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
else {
cc->iv_size = 0;
if (cc->iv_gen_ops) {
- DMWARN(PFX "Selected cipher does not support IVs");
+ DMWARN("Selected cipher does not support IVs");
if (cc->iv_gen_ops->dtr)
cc->iv_gen_ops->dtr(cc);
cc->iv_gen_ops = NULL;
@@ -619,36 +619,36 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
cc->io_pool = mempool_create_slab_pool(MIN_IOS, _crypt_io_pool);
if (!cc->io_pool) {
- ti->error = PFX "Cannot allocate crypt io mempool";
+ ti->error = "Cannot allocate crypt io mempool";
goto bad3;
}
cc->page_pool = mempool_create_page_pool(MIN_POOL_PAGES, 0);
if (!cc->page_pool) {
- ti->error = PFX "Cannot allocate page mempool";
+ ti->error = "Cannot allocate page mempool";
goto bad4;
}
if (tfm->crt_cipher.cit_setkey(tfm, cc->key, key_size) < 0) {
- ti->error = PFX "Error setting key";
+ ti->error = "Error setting key";
goto bad5;
}
if (sscanf(argv[2], "%llu", &tmpll) != 1) {
- ti->error = PFX "Invalid iv_offset sector";
+ ti->error = "Invalid iv_offset sector";
goto bad5;
}
cc->iv_offset = tmpll;
if (sscanf(argv[4], "%llu", &tmpll) != 1) {
- ti->error = PFX "Invalid device sector";
+ ti->error = "Invalid device sector";
goto bad5;
}
cc->start = tmpll;
if (dm_get_device(ti, argv[3], cc->start, ti->len,
dm_table_get_mode(ti->table), &cc->dev)) {
- ti->error = PFX "Device lookup failed";
+ ti->error = "Device lookup failed";
goto bad5;
}
@@ -657,7 +657,7 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
*(ivopts - 1) = ':';
cc->iv_mode = kmalloc(strlen(ivmode) + 1, GFP_KERNEL);
if (!cc->iv_mode) {
- ti->error = PFX "Error kmallocing iv_mode string";
+ ti->error = "Error kmallocing iv_mode string";
goto bad5;
}
strcpy(cc->iv_mode, ivmode);
@@ -918,13 +918,13 @@ static int __init dm_crypt_init(void)
_kcryptd_workqueue = create_workqueue("kcryptd");
if (!_kcryptd_workqueue) {
r = -ENOMEM;
- DMERR(PFX "couldn't create kcryptd");
+ DMERR("couldn't create kcryptd");
goto bad1;
}
r = dm_register_target(&crypt_target);
if (r < 0) {
- DMERR(PFX "register failed %d", r);
+ DMERR("register failed %d", r);
goto bad2;
}
@@ -942,7 +942,7 @@ static void __exit dm_crypt_exit(void)
int r = dm_unregister_target(&crypt_target);
if (r < 0)
- DMERR(PFX "unregister failed %d", r);
+ DMERR("unregister failed %d", r);
destroy_workqueue(_kcryptd_workqueue);
kmem_cache_destroy(_crypt_io_pool);
diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
index c7067674dcb7..2a374ccb30dd 100644
--- a/drivers/md/dm-emc.c
+++ b/drivers/md/dm-emc.c
@@ -12,6 +12,8 @@
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
+#define DM_MSG_PREFIX "multipath emc"
+
struct emc_handler {
spinlock_t lock;
@@ -66,7 +68,7 @@ static struct bio *get_failover_bio(struct path *path, unsigned data_size)
bio = bio_alloc(GFP_ATOMIC, 1);
if (!bio) {
- DMERR("dm-emc: get_failover_bio: bio_alloc() failed.");
+ DMERR("get_failover_bio: bio_alloc() failed.");
return NULL;
}
@@ -78,13 +80,13 @@ static struct bio *get_failover_bio(struct path *path, unsigned data_size)
page = alloc_page(GFP_ATOMIC);
if (!page) {
- DMERR("dm-emc: get_failover_bio: alloc_page() failed.");
+ DMERR("get_failover_bio: alloc_page() failed.");
bio_put(bio);
return NULL;
}
if (bio_add_page(bio, page, data_size, 0) != data_size) {
- DMERR("dm-emc: get_failover_bio: alloc_page() failed.");
+ DMERR("get_failover_bio: alloc_page() failed.");
__free_page(page);
bio_put(bio);
return NULL;
@@ -103,7 +105,7 @@ static struct request *get_failover_req(struct emc_handler *h,
/* FIXME: Figure out why it fails with GFP_ATOMIC. */
rq = blk_get_request(q, WRITE, __GFP_WAIT);
if (!rq) {
- DMERR("dm-emc: get_failover_req: blk_get_request failed");
+ DMERR("get_failover_req: blk_get_request failed");
return NULL;
}
@@ -160,7 +162,7 @@ static struct request *emc_trespass_get(struct emc_handler *h,
bio = get_failover_bio(path, data_size);
if (!bio) {
- DMERR("dm-emc: emc_trespass_get: no bio");
+ DMERR("emc_trespass_get: no bio");
return NULL;
}
@@ -173,7 +175,7 @@ static struct request *emc_trespass_get(struct emc_handler *h,
/* get request for block layer packet command */
rq = get_failover_req(h, bio, path);
if (!rq) {
- DMERR("dm-emc: emc_trespass_get: no rq");
+ DMERR("emc_trespass_get: no rq");
free_bio(bio);
return NULL;
}
@@ -200,18 +202,18 @@ static void emc_pg_init(struct hw_handler *hwh, unsigned bypassed,
* initial state passed into us and then get an update here.
*/
if (!q) {
- DMINFO("dm-emc: emc_pg_init: no queue");
+ DMINFO("emc_pg_init: no queue");
goto fail_path;
}
/* FIXME: The request should be pre-allocated. */
rq = emc_trespass_get(hwh->context, path);
if (!rq) {
- DMERR("dm-emc: emc_pg_init: no rq");
+ DMERR("emc_pg_init: no rq");
goto fail_path;
}
- DMINFO("dm-emc: emc_pg_init: sending switch-over command");
+ DMINFO("emc_pg_init: sending switch-over command");
elv_add_request(q, rq, ELEVATOR_INSERT_FRONT, 1);
return;
@@ -241,18 +243,18 @@ static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
hr = 0;
short_trespass = 0;
} else if (argc != 2) {
- DMWARN("dm-emc hwhandler: incorrect number of arguments");
+ DMWARN("incorrect number of arguments");
return -EINVAL;
} else {
if ((sscanf(argv[0], "%u", &short_trespass) != 1)
|| (short_trespass > 1)) {
- DMWARN("dm-emc: invalid trespass mode selected");
+ DMWARN("invalid trespass mode selected");
return -EINVAL;
}
if ((sscanf(argv[1], "%u", &hr) != 1)
|| (hr > 1)) {
- DMWARN("dm-emc: invalid honor reservation flag selected");
+ DMWARN("invalid honor reservation flag selected");
return -EINVAL;
}
}
@@ -264,14 +266,14 @@ static int emc_create(struct hw_handler *hwh, unsigned argc, char **argv)
hwh->context = h;
if ((h->short_trespass = short_trespass))
- DMWARN("dm-emc: short trespass command will be send");
+ DMWARN("short trespass command will be send");
else
- DMWARN("dm-emc: long trespass command will be send");
+ DMWARN("long trespass command will be send");
if ((h->hr = hr))
- DMWARN("dm-emc: honor reservation bit will be set");
+ DMWARN("honor reservation bit will be set");
else
- DMWARN("dm-emc: honor reservation bit will not be set (default)");
+ DMWARN("honor reservation bit will not be set (default)");
return 0;
}
@@ -336,9 +338,9 @@ static int __init dm_emc_init(void)
int r = dm_register_hw_handler(&emc_hwh);
if (r < 0)
- DMERR("emc: register failed %d", r);
+ DMERR("register failed %d", r);
- DMINFO("dm-emc version 0.0.3 loaded");
+ DMINFO("version 0.0.3 loaded");
return r;
}
@@ -348,7 +350,7 @@ static void __exit dm_emc_exit(void)
int r = dm_unregister_hw_handler(&emc_hwh);
if (r < 0)
- DMERR("emc: unregister failed %d", r);
+ DMERR("unregister failed %d", r);
}
module_init(dm_emc_init);
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index cc07bbebbb16..d12379b5cdb5 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -16,6 +16,8 @@
#include <linux/vmalloc.h>
#include <linux/slab.h>
+#define DM_MSG_PREFIX "snapshots"
+
/*-----------------------------------------------------------------
* Persistent snapshots, by persistent we mean that the snapshot
* will survive a reboot.
@@ -91,7 +93,6 @@ struct pstore {
struct dm_snapshot *snap; /* up pointer to my snapshot */
int version;
int valid;
- uint32_t chunk_size;
uint32_t exceptions_per_area;
/*
@@ -133,7 +134,7 @@ static int alloc_area(struct pstore *ps)
int r = -ENOMEM;
size_t len;
- len = ps->chunk_size << SECTOR_SHIFT;
+ len = ps->snap->chunk_size << SECTOR_SHIFT;
/*
* Allocate the chunk_size block of memory that will hold
@@ -160,8 +161,8 @@ static int chunk_io(struct pstore *ps, uint32_t chunk, int rw)
unsigned long bits;
where.bdev = ps->snap->cow->bdev;
- where.sector = ps->chunk_size * chunk;
- where.count = ps->chunk_size;
+ where.sector = ps->snap->chunk_size * chunk;
+ where.count = ps->snap->chunk_size;
return dm_io_sync_vm(1, &where, rw, ps->area, &bits);
}
@@ -188,7 +189,7 @@ static int area_io(struct pstore *ps, uint32_t area, int rw)
static int zero_area(struct pstore *ps, uint32_t area)
{
- memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+ memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT);
return area_io(ps, area, WRITE);
}
@@ -196,6 +197,7 @@ static int read_header(struct pstore *ps, int *new_snapshot)
{
int r;
struct disk_header *dh;
+ chunk_t chunk_size;
r = chunk_io(ps, 0, READ);
if (r)
@@ -210,8 +212,29 @@ static int read_header(struct pstore *ps, int *new_snapshot)
*new_snapshot = 0;
ps->valid = le32_to_cpu(dh->valid);
ps->version = le32_to_cpu(dh->version);
- ps->chunk_size = le32_to_cpu(dh->chunk_size);
-
+ chunk_size = le32_to_cpu(dh->chunk_size);
+ if (ps->snap->chunk_size != chunk_size) {
+ DMWARN("chunk size %llu in device metadata overrides "
+ "table chunk size of %llu.",
+ (unsigned long long)chunk_size,
+ (unsigned long long)ps->snap->chunk_size);
+
+ /* We had a bogus chunk_size. Fix stuff up. */
+ dm_io_put(sectors_to_pages(ps->snap->chunk_size));
+ free_area(ps);
+
+ ps->snap->chunk_size = chunk_size;
+ ps->snap->chunk_mask = chunk_size - 1;
+ ps->snap->chunk_shift = ffs(chunk_size) - 1;
+
+ r = alloc_area(ps);
+ if (r)
+ return r;
+
+ r = dm_io_get(sectors_to_pages(chunk_size));
+ if (r)
+ return r;
+ }
} else {
DMWARN("Invalid/corrupt snapshot");
r = -ENXIO;
@@ -224,13 +247,13 @@ static int write_header(struct pstore *ps)
{
struct disk_header *dh;
- memset(ps->area, 0, ps->chunk_size << SECTOR_SHIFT);
+ memset(ps->area, 0, ps->snap->chunk_size << SECTOR_SHIFT);
dh = (struct disk_header *) ps->area;
dh->magic = cpu_to_le32(SNAP_MAGIC);
dh->valid = cpu_to_le32(ps->valid);
dh->version = cpu_to_le32(ps->version);
- dh->chunk_size = cpu_to_le32(ps->chunk_size);
+ dh->chunk_size = cpu_to_le32(ps->snap->chunk_size);
return chunk_io(ps, 0, WRITE);
}
@@ -365,7 +388,7 @@ static void persistent_destroy(struct exception_store *store)
{
struct pstore *ps = get_info(store);
- dm_io_put(sectors_to_pages(ps->chunk_size));
+ dm_io_put(sectors_to_pages(ps->snap->chunk_size));
vfree(ps->callbacks);
free_area(ps);
kfree(ps);
@@ -384,6 +407,16 @@ static int persistent_read_metadata(struct exception_store *store)
return r;
/*
+ * Now we know correct chunk_size, complete the initialisation.
+ */
+ ps->exceptions_per_area = (ps->snap->chunk_size << SECTOR_SHIFT) /
+ sizeof(struct disk_exception);
+ ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
+ sizeof(*ps->callbacks));
+ if (!ps->callbacks)
+ return -ENOMEM;
+
+ /*
* Do we need to setup a new snapshot ?
*/
if (new_snapshot) {
@@ -533,9 +566,6 @@ int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
ps->snap = store->snap;
ps->valid = 1;
ps->version = SNAPSHOT_DISK_VERSION;
- ps->chunk_size = chunk_size;
- ps->exceptions_per_area = (chunk_size << SECTOR_SHIFT) /
- sizeof(struct disk_exception);
ps->next_free = 2; /* skipping the header and first area */
ps->current_committed = 0;
@@ -543,18 +573,9 @@ int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
if (r)
goto bad;
- /*
- * Allocate space for all the callbacks.
- */
ps->callback_count = 0;
atomic_set(&ps->pending_count, 0);
- ps->callbacks = dm_vcalloc(ps->exceptions_per_area,
- sizeof(*ps->callbacks));
-
- if (!ps->callbacks) {
- r = -ENOMEM;
- goto bad;
- }
+ ps->callbacks = NULL;
store->destroy = persistent_destroy;
store->read_metadata = persistent_read_metadata;
diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c
index 8edd6435414d..3edb3477f987 100644
--- a/drivers/md/dm-ioctl.c
+++ b/drivers/md/dm-ioctl.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
- * Copyright (C) 2004 - 2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004 - 2006 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -19,6 +19,7 @@
#include <asm/uaccess.h>
+#define DM_MSG_PREFIX "ioctl"
#define DM_DRIVER_EMAIL "dm-devel@redhat.com"
/*-----------------------------------------------------------------
@@ -48,7 +49,7 @@ struct vers_iter {
static struct list_head _name_buckets[NUM_BUCKETS];
static struct list_head _uuid_buckets[NUM_BUCKETS];
-static void dm_hash_remove_all(void);
+static void dm_hash_remove_all(int keep_open_devices);
/*
* Guards access to both hash tables.
@@ -73,7 +74,7 @@ static int dm_hash_init(void)
static void dm_hash_exit(void)
{
- dm_hash_remove_all();
+ dm_hash_remove_all(0);
devfs_remove(DM_DIR);
}
@@ -102,8 +103,10 @@ static struct hash_cell *__get_name_cell(const char *str)
unsigned int h = hash_str(str);
list_for_each_entry (hc, _name_buckets + h, name_list)
- if (!strcmp(hc->name, str))
+ if (!strcmp(hc->name, str)) {
+ dm_get(hc->md);
return hc;
+ }
return NULL;
}
@@ -114,8 +117,10 @@ static struct hash_cell *__get_uuid_cell(const char *str)
unsigned int h = hash_str(str);
list_for_each_entry (hc, _uuid_buckets + h, uuid_list)
- if (!strcmp(hc->uuid, str))
+ if (!strcmp(hc->uuid, str)) {
+ dm_get(hc->md);
return hc;
+ }
return NULL;
}
@@ -191,7 +196,7 @@ static int unregister_with_devfs(struct hash_cell *hc)
*/
static int dm_hash_insert(const char *name, const char *uuid, struct mapped_device *md)
{
- struct hash_cell *cell;
+ struct hash_cell *cell, *hc;
/*
* Allocate the new cells.
@@ -204,14 +209,19 @@ static int dm_hash_insert(const char *name, const char *uuid, struct mapped_devi
* Insert the cell into both hash tables.
*/
down_write(&_hash_lock);
- if (__get_name_cell(name))
+ hc = __get_name_cell(name);
+ if (hc) {
+ dm_put(hc->md);
goto bad;
+ }
list_add(&cell->name_list, _name_buckets + hash_str(name));
if (uuid) {
- if (__get_uuid_cell(uuid)) {
+ hc = __get_uuid_cell(uuid);
+ if (hc) {
list_del(&cell->name_list);
+ dm_put(hc->md);
goto bad;
}
list_add(&cell->uuid_list, _uuid_buckets + hash_str(uuid));
@@ -251,19 +261,41 @@ static void __hash_remove(struct hash_cell *hc)
free_cell(hc);
}
-static void dm_hash_remove_all(void)
+static void dm_hash_remove_all(int keep_open_devices)
{
- int i;
+ int i, dev_skipped, dev_removed;
struct hash_cell *hc;
struct list_head *tmp, *n;
down_write(&_hash_lock);
+
+retry:
+ dev_skipped = dev_removed = 0;
for (i = 0; i < NUM_BUCKETS; i++) {
list_for_each_safe (tmp, n, _name_buckets + i) {
hc = list_entry(tmp, struct hash_cell, name_list);
+
+ if (keep_open_devices &&
+ dm_lock_for_deletion(hc->md)) {
+ dev_skipped++;
+ continue;
+ }
__hash_remove(hc);
+ dev_removed = 1;
}
}
+
+ /*
+ * Some mapped devices may be using other mapped devices, so if any
+ * still exist, repeat until we make no further progress.
+ */
+ if (dev_skipped) {
+ if (dev_removed)
+ goto retry;
+
+ DMWARN("remove_all left %d open device(s)", dev_skipped);
+ }
+
up_write(&_hash_lock);
}
@@ -289,6 +321,7 @@ static int dm_hash_rename(const char *old, const char *new)
if (hc) {
DMWARN("asked to rename to an already existing name %s -> %s",
old, new);
+ dm_put(hc->md);
up_write(&_hash_lock);
kfree(new_name);
return -EBUSY;
@@ -328,6 +361,7 @@ static int dm_hash_rename(const char *old, const char *new)
dm_table_put(table);
}
+ dm_put(hc->md);
up_write(&_hash_lock);
kfree(old_name);
return 0;
@@ -344,7 +378,7 @@ typedef int (*ioctl_fn)(struct dm_ioctl *param, size_t param_size);
static int remove_all(struct dm_ioctl *param, size_t param_size)
{
- dm_hash_remove_all();
+ dm_hash_remove_all(1);
param->data_size = 0;
return 0;
}
@@ -524,7 +558,6 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
{
struct gendisk *disk = dm_disk(md);
struct dm_table *table;
- struct block_device *bdev;
param->flags &= ~(DM_SUSPEND_FLAG | DM_READONLY_FLAG |
DM_ACTIVE_PRESENT_FLAG);
@@ -534,20 +567,12 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
param->dev = huge_encode_dev(MKDEV(disk->major, disk->first_minor));
- if (!(param->flags & DM_SKIP_BDGET_FLAG)) {
- bdev = bdget_disk(disk, 0);
- if (!bdev)
- return -ENXIO;
-
- /*
- * Yes, this will be out of date by the time it gets back
- * to userland, but it is still very useful for
- * debugging.
- */
- param->open_count = bdev->bd_openers;
- bdput(bdev);
- } else
- param->open_count = -1;
+ /*
+ * Yes, this will be out of date by the time it gets back
+ * to userland, but it is still very useful for
+ * debugging.
+ */
+ param->open_count = dm_open_count(md);
if (disk->policy)
param->flags |= DM_READONLY_FLAG;
@@ -567,7 +592,7 @@ static int __dev_status(struct mapped_device *md, struct dm_ioctl *param)
static int dev_create(struct dm_ioctl *param, size_t param_size)
{
- int r;
+ int r, m = DM_ANY_MINOR;
struct mapped_device *md;
r = check_name(param->name);
@@ -575,10 +600,9 @@ static int dev_create(struct dm_ioctl *param, size_t param_size)
return r;
if (param->flags & DM_PERSISTENT_DEV_FLAG)
- r = dm_create_with_minor(MINOR(huge_decode_dev(param->dev)), &md);
- else
- r = dm_create(&md);
+ m = MINOR(huge_decode_dev(param->dev));
+ r = dm_create(m, &md);
if (r)
return r;
@@ -611,10 +635,8 @@ static struct hash_cell *__find_device_hash_cell(struct dm_ioctl *param)
return __get_name_cell(param->name);
md = dm_get_md(huge_decode_dev(param->dev));
- if (md) {
+ if (md)
mdptr = dm_get_mdptr(md);
- dm_put(md);
- }
return mdptr;
}
@@ -628,7 +650,6 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
hc = __find_device_hash_cell(param);
if (hc) {
md = hc->md;
- dm_get(md);
/*
* Sneakily write in both the name and the uuid
@@ -653,6 +674,8 @@ static struct mapped_device *find_device(struct dm_ioctl *param)
static int dev_remove(struct dm_ioctl *param, size_t param_size)
{
struct hash_cell *hc;
+ struct mapped_device *md;
+ int r;
down_write(&_hash_lock);
hc = __find_device_hash_cell(param);
@@ -663,8 +686,22 @@ static int dev_remove(struct dm_ioctl *param, size_t param_size)
return -ENXIO;
}
+ md = hc->md;
+
+ /*
+ * Ensure the device is not open and nothing further can open it.
+ */
+ r = dm_lock_for_deletion(md);
+ if (r) {
+ DMWARN("unable to remove open device %s", hc->name);
+ up_write(&_hash_lock);
+ dm_put(md);
+ return r;
+ }
+
__hash_remove(hc);
up_write(&_hash_lock);
+ dm_put(md);
param->data_size = 0;
return 0;
}
@@ -790,7 +827,6 @@ static int do_resume(struct dm_ioctl *param)
}
md = hc->md;
- dm_get(md);
new_map = hc->new_map;
hc->new_map = NULL;
@@ -1078,6 +1114,7 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
{
int r;
struct hash_cell *hc;
+ struct mapped_device *md;
down_write(&_hash_lock);
@@ -1096,7 +1133,9 @@ static int table_clear(struct dm_ioctl *param, size_t param_size)
param->flags &= ~DM_INACTIVE_PRESENT_FLAG;
r = __dev_status(hc->md, param);
+ md = hc->md;
up_write(&_hash_lock);
+ dm_put(md);
return r;
}
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index daf586c0898d..47b3c62bbdb8 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -12,6 +12,8 @@
#include <linux/bio.h>
#include <linux/slab.h>
+#define DM_MSG_PREFIX "linear"
+
/*
* Linear: maps a linear range of a device.
*/
@@ -29,7 +31,7 @@ static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
unsigned long long tmp;
if (argc != 2) {
- ti->error = "dm-linear: Invalid argument count";
+ ti->error = "Invalid argument count";
return -EINVAL;
}
@@ -111,7 +113,7 @@ int __init dm_linear_init(void)
int r = dm_register_target(&linear_target);
if (r < 0)
- DMERR("linear: register failed %d", r);
+ DMERR("register failed %d", r);
return r;
}
@@ -121,5 +123,5 @@ void dm_linear_exit(void)
int r = dm_unregister_target(&linear_target);
if (r < 0)
- DMERR("linear: unregister failed %d", r);
+ DMERR("unregister failed %d", r);
}
diff --git a/drivers/md/dm-log.c b/drivers/md/dm-log.c
index d73779a42417..64b764bd02cc 100644
--- a/drivers/md/dm-log.c
+++ b/drivers/md/dm-log.c
@@ -12,6 +12,8 @@
#include "dm-log.h"
#include "dm-io.h"
+#define DM_MSG_PREFIX "mirror log"
+
static LIST_HEAD(_log_types);
static DEFINE_SPINLOCK(_lock);
@@ -155,8 +157,6 @@ struct log_c {
struct io_region header_location;
struct log_header *disk_header;
-
- struct io_region bits_location;
};
/*
@@ -241,43 +241,21 @@ static inline int write_header(struct log_c *log)
}
/*----------------------------------------------------------------
- * Bits IO
- *--------------------------------------------------------------*/
-static int read_bits(struct log_c *log)
-{
- int r;
- unsigned long ebits;
-
- r = dm_io_sync_vm(1, &log->bits_location, READ,
- log->clean_bits, &ebits);
- if (r)
- return r;
-
- return 0;
-}
-
-static int write_bits(struct log_c *log)
-{
- unsigned long ebits;
- return dm_io_sync_vm(1, &log->bits_location, WRITE,
- log->clean_bits, &ebits);
-}
-
-/*----------------------------------------------------------------
* core log constructor/destructor
*
* argv contains region_size followed optionally by [no]sync
*--------------------------------------------------------------*/
#define BYTE_SHIFT 3
-static int core_ctr(struct dirty_log *log, struct dm_target *ti,
- unsigned int argc, char **argv)
+static int create_log_context(struct dirty_log *log, struct dm_target *ti,
+ unsigned int argc, char **argv,
+ struct dm_dev *dev)
{
enum sync sync = DEFAULTSYNC;
struct log_c *lc;
uint32_t region_size;
unsigned int region_count;
- size_t bitset_size;
+ size_t bitset_size, buf_size;
if (argc < 1 || argc > 2) {
DMWARN("wrong number of arguments to mirror log");
@@ -319,22 +297,53 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti,
* Work out how many "unsigned long"s we need to hold the bitset.
*/
bitset_size = dm_round_up(region_count,
- sizeof(unsigned long) << BYTE_SHIFT);
+ sizeof(*lc->clean_bits) << BYTE_SHIFT);
bitset_size >>= BYTE_SHIFT;
- lc->bitset_uint32_count = bitset_size / 4;
- lc->clean_bits = vmalloc(bitset_size);
- if (!lc->clean_bits) {
- DMWARN("couldn't allocate clean bitset");
- kfree(lc);
- return -ENOMEM;
+ lc->bitset_uint32_count = bitset_size / sizeof(*lc->clean_bits);
+
+ /*
+ * Disk log?
+ */
+ if (!dev) {
+ lc->clean_bits = vmalloc(bitset_size);
+ if (!lc->clean_bits) {
+ DMWARN("couldn't allocate clean bitset");
+ kfree(lc);
+ return -ENOMEM;
+ }
+ lc->disk_header = NULL;
+ } else {
+ lc->log_dev = dev;
+ lc->header_location.bdev = lc->log_dev->bdev;
+ lc->header_location.sector = 0;
+
+ /*
+ * Buffer holds both header and bitset.
+ */
+ buf_size = dm_round_up((LOG_OFFSET << SECTOR_SHIFT) +
+ bitset_size, ti->limits.hardsect_size);
+ lc->header_location.count = buf_size >> SECTOR_SHIFT;
+
+ lc->disk_header = vmalloc(buf_size);
+ if (!lc->disk_header) {
+ DMWARN("couldn't allocate disk log buffer");
+ kfree(lc);
+ return -ENOMEM;
+ }
+
+ lc->clean_bits = (void *)lc->disk_header +
+ (LOG_OFFSET << SECTOR_SHIFT);
}
+
memset(lc->clean_bits, -1, bitset_size);
lc->sync_bits = vmalloc(bitset_size);
if (!lc->sync_bits) {
DMWARN("couldn't allocate sync bitset");
- vfree(lc->clean_bits);
+ if (!dev)
+ vfree(lc->clean_bits);
+ vfree(lc->disk_header);
kfree(lc);
return -ENOMEM;
}
@@ -345,25 +354,40 @@ static int core_ctr(struct dirty_log *log, struct dm_target *ti,
if (!lc->recovering_bits) {
DMWARN("couldn't allocate sync bitset");
vfree(lc->sync_bits);
- vfree(lc->clean_bits);
+ if (!dev)
+ vfree(lc->clean_bits);
+ vfree(lc->disk_header);
kfree(lc);
return -ENOMEM;
}
memset(lc->recovering_bits, 0, bitset_size);
lc->sync_search = 0;
log->context = lc;
+
return 0;
}
-static void core_dtr(struct dirty_log *log)
+static int core_ctr(struct dirty_log *log, struct dm_target *ti,
+ unsigned int argc, char **argv)
+{
+ return create_log_context(log, ti, argc, argv, NULL);
+}
+
+static void destroy_log_context(struct log_c *lc)
{
- struct log_c *lc = (struct log_c *) log->context;
- vfree(lc->clean_bits);
vfree(lc->sync_bits);
vfree(lc->recovering_bits);
kfree(lc);
}
+static void core_dtr(struct dirty_log *log)
+{
+ struct log_c *lc = (struct log_c *) log->context;
+
+ vfree(lc->clean_bits);
+ destroy_log_context(lc);
+}
+
/*----------------------------------------------------------------
* disk log constructor/destructor
*
@@ -373,8 +397,6 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
unsigned int argc, char **argv)
{
int r;
- size_t size;
- struct log_c *lc;
struct dm_dev *dev;
if (argc < 2 || argc > 3) {
@@ -387,49 +409,22 @@ static int disk_ctr(struct dirty_log *log, struct dm_target *ti,
if (r)
return r;
- r = core_ctr(log, ti, argc - 1, argv + 1);
+ r = create_log_context(log, ti, argc - 1, argv + 1, dev);
if (r) {
dm_put_device(ti, dev);
return r;
}
- lc = (struct log_c *) log->context;
- lc->log_dev = dev;
-
- /* setup the disk header fields */
- lc->header_location.bdev = lc->log_dev->bdev;
- lc->header_location.sector = 0;
- lc->header_location.count = 1;
-
- /*
- * We can't read less than this amount, even though we'll
- * not be using most of this space.
- */
- lc->disk_header = vmalloc(1 << SECTOR_SHIFT);
- if (!lc->disk_header)
- goto bad;
-
- /* setup the disk bitset fields */
- lc->bits_location.bdev = lc->log_dev->bdev;
- lc->bits_location.sector = LOG_OFFSET;
-
- size = dm_round_up(lc->bitset_uint32_count * sizeof(uint32_t),
- 1 << SECTOR_SHIFT);
- lc->bits_location.count = size >> SECTOR_SHIFT;
return 0;
-
- bad:
- dm_put_device(ti, lc->log_dev);
- core_dtr(log);
- return -ENOMEM;
}
static void disk_dtr(struct dirty_log *log)
{
struct log_c *lc = (struct log_c *) log->context;
+
dm_put_device(lc->ti, lc->log_dev);
vfree(lc->disk_header);
- core_dtr(log);
+ destroy_log_context(lc);
}
static int count_bits32(uint32_t *addr, unsigned size)
@@ -454,12 +449,7 @@ static int disk_resume(struct dirty_log *log)
if (r)
return r;
- /* read the bits */
- r = read_bits(lc);
- if (r)
- return r;
-
- /* set or clear any new bits */
+ /* set or clear any new bits -- device has grown */
if (lc->sync == NOSYNC)
for (i = lc->header.nr_regions; i < lc->region_count; i++)
/* FIXME: amazingly inefficient */
@@ -469,15 +459,14 @@ static int disk_resume(struct dirty_log *log)
/* FIXME: amazingly inefficient */
log_clear_bit(lc, lc->clean_bits, i);
+ /* clear any old bits -- device has shrunk */
+ for (i = lc->region_count; i % (sizeof(*lc->clean_bits) << BYTE_SHIFT); i++)
+ log_clear_bit(lc, lc->clean_bits, i);
+
/* copy clean across to sync */
memcpy(lc->sync_bits, lc->clean_bits, size);
lc->sync_count = count_bits32(lc->clean_bits, lc->bitset_uint32_count);
- /* write the bits */
- r = write_bits(lc);
- if (r)
- return r;
-
/* set the correct number of regions in the header */
lc->header.nr_regions = lc->region_count;
@@ -518,7 +507,7 @@ static int disk_flush(struct dirty_log *log)
if (!lc->touched)
return 0;
- r = write_bits(lc);
+ r = write_header(lc);
if (!r)
lc->touched = 0;
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 1816f30678ed..217615b33223 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -21,6 +21,7 @@
#include <linux/workqueue.h>
#include <asm/atomic.h>
+#define DM_MSG_PREFIX "multipath"
#define MESG_STR(x) x, sizeof(x)
/* Path properties */
@@ -446,8 +447,6 @@ struct param {
char *error;
};
-#define ESTR(s) ("dm-multipath: " s)
-
static int read_param(struct param *param, char *str, unsigned *v, char **error)
{
if (!str ||
@@ -495,12 +494,12 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
unsigned ps_argc;
static struct param _params[] = {
- {0, 1024, ESTR("invalid number of path selector args")},
+ {0, 1024, "invalid number of path selector args"},
};
pst = dm_get_path_selector(shift(as));
if (!pst) {
- ti->error = ESTR("unknown path selector type");
+ ti->error = "unknown path selector type";
return -EINVAL;
}
@@ -511,7 +510,7 @@ static int parse_path_selector(struct arg_set *as, struct priority_group *pg,
r = pst->create(&pg->ps, ps_argc, as->argv);
if (r) {
dm_put_path_selector(pst);
- ti->error = ESTR("path selector constructor failed");
+ ti->error = "path selector constructor failed";
return r;
}
@@ -529,7 +528,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
/* we need at least a path arg */
if (as->argc < 1) {
- ti->error = ESTR("no device given");
+ ti->error = "no device given";
return NULL;
}
@@ -540,7 +539,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
r = dm_get_device(ti, shift(as), ti->begin, ti->len,
dm_table_get_mode(ti->table), &p->path.dev);
if (r) {
- ti->error = ESTR("error getting device");
+ ti->error = "error getting device";
goto bad;
}
@@ -562,8 +561,8 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
struct dm_target *ti)
{
static struct param _params[] = {
- {1, 1024, ESTR("invalid number of paths")},
- {0, 1024, ESTR("invalid number of selector args")}
+ {1, 1024, "invalid number of paths"},
+ {0, 1024, "invalid number of selector args"}
};
int r;
@@ -572,13 +571,13 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
if (as->argc < 2) {
as->argc = 0;
- ti->error = ESTR("not enough priority group aruments");
+ ti->error = "not enough priority group aruments";
return NULL;
}
pg = alloc_priority_group();
if (!pg) {
- ti->error = ESTR("couldn't allocate priority group");
+ ti->error = "couldn't allocate priority group";
return NULL;
}
pg->m = m;
@@ -633,7 +632,7 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m,
unsigned hw_argc;
static struct param _params[] = {
- {0, 1024, ESTR("invalid number of hardware handler args")},
+ {0, 1024, "invalid number of hardware handler args"},
};
r = read_param(_params, shift(as), &hw_argc, &ti->error);
@@ -645,14 +644,14 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m,
hwht = dm_get_hw_handler(shift(as));
if (!hwht) {
- ti->error = ESTR("unknown hardware handler type");
+ ti->error = "unknown hardware handler type";
return -EINVAL;
}
r = hwht->create(&m->hw_handler, hw_argc - 1, as->argv);
if (r) {
dm_put_hw_handler(hwht);
- ti->error = ESTR("hardware handler constructor failed");
+ ti->error = "hardware handler constructor failed";
return r;
}
@@ -669,7 +668,7 @@ static int parse_features(struct arg_set *as, struct multipath *m,
unsigned argc;
static struct param _params[] = {
- {0, 1, ESTR("invalid number of feature args")},
+ {0, 1, "invalid number of feature args"},
};
r = read_param(_params, shift(as), &argc, &ti->error);
@@ -692,8 +691,8 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
{
/* target parameters */
static struct param _params[] = {
- {1, 1024, ESTR("invalid number of priority groups")},
- {1, 1024, ESTR("invalid initial priority group number")},
+ {1, 1024, "invalid number of priority groups"},
+ {1, 1024, "invalid initial priority group number"},
};
int r;
@@ -707,7 +706,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
m = alloc_multipath();
if (!m) {
- ti->error = ESTR("can't allocate multipath");
+ ti->error = "can't allocate multipath";
return -EINVAL;
}
@@ -746,7 +745,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
}
if (pg_count != m->nr_priority_groups) {
- ti->error = ESTR("priority group count mismatch");
+ ti->error = "priority group count mismatch";
r = -EINVAL;
goto bad;
}
@@ -807,7 +806,7 @@ static int fail_path(struct pgpath *pgpath)
if (!pgpath->path.is_active)
goto out;
- DMWARN("dm-multipath: Failing path %s.", pgpath->path.dev->name);
+ DMWARN("Failing path %s.", pgpath->path.dev->name);
pgpath->pg->ps.type->fail_path(&pgpath->pg->ps, &pgpath->path);
pgpath->path.is_active = 0;
@@ -1250,7 +1249,7 @@ static int multipath_message(struct dm_target *ti, unsigned argc, char **argv)
r = dm_get_device(ti, argv[1], ti->begin, ti->len,
dm_table_get_mode(ti->table), &dev);
if (r) {
- DMWARN("dm-multipath message: error getting device %s",
+ DMWARN("message: error getting device %s",
argv[1]);
return -EINVAL;
}
@@ -1309,7 +1308,7 @@ static int __init dm_multipath_init(void)
return -ENOMEM;
}
- DMINFO("dm-multipath version %u.%u.%u loaded",
+ DMINFO("version %u.%u.%u loaded",
multipath_target.version[0], multipath_target.version[1],
multipath_target.version[2]);
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index d12cf3e5e076..be48cedf986b 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -20,6 +20,8 @@
#include <linux/vmalloc.h>
#include <linux/workqueue.h>
+#define DM_MSG_PREFIX "raid1"
+
static struct workqueue_struct *_kmirrord_wq;
static struct work_struct _kmirrord_work;
@@ -106,12 +108,42 @@ struct region {
struct bio_list delayed_bios;
};
+
+/*-----------------------------------------------------------------
+ * Mirror set structures.
+ *---------------------------------------------------------------*/
+struct mirror {
+ atomic_t error_count;
+ struct dm_dev *dev;
+ sector_t offset;
+};
+
+struct mirror_set {
+ struct dm_target *ti;
+ struct list_head list;
+ struct region_hash rh;
+ struct kcopyd_client *kcopyd_client;
+
+ spinlock_t lock; /* protects the next two lists */
+ struct bio_list reads;
+ struct bio_list writes;
+
+ /* recovery */
+ region_t nr_regions;
+ int in_sync;
+
+ struct mirror *default_mirror; /* Default mirror */
+
+ unsigned int nr_mirrors;
+ struct mirror mirror[0];
+};
+
/*
* Conversion fns
*/
static inline region_t bio_to_region(struct region_hash *rh, struct bio *bio)
{
- return bio->bi_sector >> rh->region_shift;
+ return (bio->bi_sector - rh->ms->ti->begin) >> rh->region_shift;
}
static inline sector_t region_to_sector(struct region_hash *rh, region_t region)
@@ -458,11 +490,9 @@ static int __rh_recovery_prepare(struct region_hash *rh)
/* Already quiesced ? */
if (atomic_read(&reg->pending))
list_del_init(&reg->list);
+ else
+ list_move(&reg->list, &rh->quiesced_regions);
- else {
- list_del_init(&reg->list);
- list_add(&reg->list, &rh->quiesced_regions);
- }
spin_unlock_irq(&rh->region_lock);
return 1;
@@ -541,35 +571,6 @@ static void rh_start_recovery(struct region_hash *rh)
wake();
}
-/*-----------------------------------------------------------------
- * Mirror set structures.
- *---------------------------------------------------------------*/
-struct mirror {
- atomic_t error_count;
- struct dm_dev *dev;
- sector_t offset;
-};
-
-struct mirror_set {
- struct dm_target *ti;
- struct list_head list;
- struct region_hash rh;
- struct kcopyd_client *kcopyd_client;
-
- spinlock_t lock; /* protects the next two lists */
- struct bio_list reads;
- struct bio_list writes;
-
- /* recovery */
- region_t nr_regions;
- int in_sync;
-
- struct mirror *default_mirror; /* Default mirror */
-
- unsigned int nr_mirrors;
- struct mirror mirror[0];
-};
-
/*
* Every mirror should look like this one.
*/
@@ -603,7 +604,7 @@ static void recovery_complete(int read_err, unsigned int write_err,
struct region *reg = (struct region *) context;
/* FIXME: better error handling */
- rh_recovery_end(reg, read_err || write_err);
+ rh_recovery_end(reg, !(read_err || write_err));
}
static int recover(struct mirror_set *ms, struct region *reg)
@@ -893,7 +894,7 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
ms = kmalloc(len, GFP_KERNEL);
if (!ms) {
- ti->error = "dm-mirror: Cannot allocate mirror context";
+ ti->error = "Cannot allocate mirror context";
return NULL;
}
@@ -907,7 +908,7 @@ static struct mirror_set *alloc_context(unsigned int nr_mirrors,
ms->default_mirror = &ms->mirror[DEFAULT_MIRROR];
if (rh_init(&ms->rh, ms, dl, region_size, ms->nr_regions)) {
- ti->error = "dm-mirror: Error creating dirty region hash";
+ ti->error = "Error creating dirty region hash";
kfree(ms);
return NULL;
}
@@ -937,14 +938,14 @@ static int get_mirror(struct mirror_set *ms, struct dm_target *ti,
unsigned long long offset;
if (sscanf(argv[1], "%llu", &offset) != 1) {
- ti->error = "dm-mirror: Invalid offset";
+ ti->error = "Invalid offset";
return -EINVAL;
}
if (dm_get_device(ti, argv[0], offset, ti->len,
dm_table_get_mode(ti->table),
&ms->mirror[mirror].dev)) {
- ti->error = "dm-mirror: Device lookup failure";
+ ti->error = "Device lookup failure";
return -ENXIO;
}
@@ -981,30 +982,30 @@ static struct dirty_log *create_dirty_log(struct dm_target *ti,
struct dirty_log *dl;
if (argc < 2) {
- ti->error = "dm-mirror: Insufficient mirror log arguments";
+ ti->error = "Insufficient mirror log arguments";
return NULL;
}
if (sscanf(argv[1], "%u", &param_count) != 1) {
- ti->error = "dm-mirror: Invalid mirror log argument count";
+ ti->error = "Invalid mirror log argument count";
return NULL;
}
*args_used = 2 + param_count;
if (argc < *args_used) {
- ti->error = "dm-mirror: Insufficient mirror log arguments";
+ ti->error = "Insufficient mirror log arguments";
return NULL;
}
dl = dm_create_dirty_log(argv[0], ti, param_count, argv + 2);
if (!dl) {
- ti->error = "dm-mirror: Error creating mirror dirty log";
+ ti->error = "Error creating mirror dirty log";
return NULL;
}
if (!_check_region_size(ti, dl->type->get_region_size(dl))) {
- ti->error = "dm-mirror: Invalid region size";
+ ti->error = "Invalid region size";
dm_destroy_dirty_log(dl);
return NULL;
}
@@ -1038,7 +1039,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
if (!argc || sscanf(argv[0], "%u", &nr_mirrors) != 1 ||
nr_mirrors < 2 || nr_mirrors > KCOPYD_MAX_REGIONS + 1) {
- ti->error = "dm-mirror: Invalid number of mirrors";
+ ti->error = "Invalid number of mirrors";
dm_destroy_dirty_log(dl);
return -EINVAL;
}
@@ -1046,7 +1047,7 @@ static int mirror_ctr(struct dm_target *ti, unsigned int argc, char **argv)
argv++, argc--;
if (argc != nr_mirrors * 2) {
- ti->error = "dm-mirror: Wrong number of mirror arguments";
+ ti->error = "Wrong number of mirror arguments";
dm_destroy_dirty_log(dl);
return -EINVAL;
}
@@ -1115,7 +1116,7 @@ static int mirror_map(struct dm_target *ti, struct bio *bio,
struct mirror *m;
struct mirror_set *ms = ti->private;
- map_context->ll = bio->bi_sector >> ms->rh.region_shift;
+ map_context->ll = bio_to_region(&ms->rh, bio);
if (rw == WRITE) {
queue_bio(ms, bio, rw);
@@ -1221,7 +1222,7 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
static struct target_type mirror_target = {
.name = "mirror",
- .version = {1, 0, 1},
+ .version = {1, 0, 2},
.module = THIS_MODULE,
.ctr = mirror_ctr,
.dtr = mirror_dtr,
diff --git a/drivers/md/dm-round-robin.c b/drivers/md/dm-round-robin.c
index d0024865a789..c5a16c550122 100644
--- a/drivers/md/dm-round-robin.c
+++ b/drivers/md/dm-round-robin.c
@@ -14,6 +14,8 @@
#include <linux/slab.h>
+#define DM_MSG_PREFIX "multipath round-robin"
+
/*-----------------------------------------------------------------
* Path-handling code, paths are held in lists
*---------------------------------------------------------------*/
@@ -191,9 +193,9 @@ static int __init dm_rr_init(void)
int r = dm_register_path_selector(&rr_ps);
if (r < 0)
- DMERR("round-robin: register failed %d", r);
+ DMERR("register failed %d", r);
- DMINFO("dm-round-robin version 1.0.0 loaded");
+ DMINFO("version 1.0.0 loaded");
return r;
}
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 08312b46463a..8eea0ddbf5ec 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -23,6 +23,8 @@
#include "dm-bio-list.h"
#include "kcopyd.h"
+#define DM_MSG_PREFIX "snapshots"
+
/*
* The percentage increment we will wake up users at
*/
@@ -117,7 +119,7 @@ static int init_origin_hash(void)
_origins = kmalloc(ORIGIN_HASH_SIZE * sizeof(struct list_head),
GFP_KERNEL);
if (!_origins) {
- DMERR("Device mapper: Snapshot: unable to allocate memory");
+ DMERR("unable to allocate memory");
return -ENOMEM;
}
@@ -412,7 +414,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
int blocksize;
if (argc < 4) {
- ti->error = "dm-snapshot: requires exactly 4 arguments";
+ ti->error = "requires exactly 4 arguments";
r = -EINVAL;
goto bad1;
}
@@ -530,7 +532,7 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
ti->private = s;
- ti->split_io = chunk_size;
+ ti->split_io = s->chunk_size;
return 0;
@@ -1127,7 +1129,7 @@ static int origin_ctr(struct dm_target *ti, unsigned int argc, char **argv)
struct dm_dev *dev;
if (argc != 1) {
- ti->error = "dm-origin: incorrect number of arguments";
+ ti->error = "origin: incorrect number of arguments";
return -EINVAL;
}
@@ -1204,7 +1206,7 @@ static int origin_status(struct dm_target *ti, status_type_t type, char *result,
static struct target_type origin_target = {
.name = "snapshot-origin",
- .version = {1, 1, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = origin_ctr,
.dtr = origin_dtr,
@@ -1215,7 +1217,7 @@ static struct target_type origin_target = {
static struct target_type snapshot_target = {
.name = "snapshot",
- .version = {1, 1, 0},
+ .version = {1, 4, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
@@ -1236,7 +1238,7 @@ static int __init dm_snapshot_init(void)
r = dm_register_target(&origin_target);
if (r < 0) {
- DMERR("Device mapper: Origin: register failed %d\n", r);
+ DMERR("Origin target register failed %d", r);
goto bad1;
}
diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c
index 08328a8f5a3c..6c29fcecd892 100644
--- a/drivers/md/dm-stripe.c
+++ b/drivers/md/dm-stripe.c
@@ -12,6 +12,8 @@
#include <linux/bio.h>
#include <linux/slab.h>
+#define DM_MSG_PREFIX "striped"
+
struct stripe {
struct dm_dev *dev;
sector_t physical_start;
@@ -78,19 +80,19 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
unsigned int i;
if (argc < 2) {
- ti->error = "dm-stripe: Not enough arguments";
+ ti->error = "Not enough arguments";
return -EINVAL;
}
stripes = simple_strtoul(argv[0], &end, 10);
if (*end) {
- ti->error = "dm-stripe: Invalid stripe count";
+ ti->error = "Invalid stripe count";
return -EINVAL;
}
chunk_size = simple_strtoul(argv[1], &end, 10);
if (*end) {
- ti->error = "dm-stripe: Invalid chunk_size";
+ ti->error = "Invalid chunk_size";
return -EINVAL;
}
@@ -99,19 +101,19 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
*/
if (!chunk_size || (chunk_size & (chunk_size - 1)) ||
(chunk_size < (PAGE_SIZE >> SECTOR_SHIFT))) {
- ti->error = "dm-stripe: Invalid chunk size";
+ ti->error = "Invalid chunk size";
return -EINVAL;
}
if (ti->len & (chunk_size - 1)) {
- ti->error = "dm-stripe: Target length not divisible by "
+ ti->error = "Target length not divisible by "
"chunk size";
return -EINVAL;
}
width = ti->len;
if (sector_div(width, stripes)) {
- ti->error = "dm-stripe: Target length not divisible by "
+ ti->error = "Target length not divisible by "
"number of stripes";
return -EINVAL;
}
@@ -120,14 +122,14 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
* Do we have enough arguments for that many stripes ?
*/
if (argc != (2 + 2 * stripes)) {
- ti->error = "dm-stripe: Not enough destinations "
+ ti->error = "Not enough destinations "
"specified";
return -EINVAL;
}
sc = alloc_context(stripes);
if (!sc) {
- ti->error = "dm-stripe: Memory allocation for striped context "
+ ti->error = "Memory allocation for striped context "
"failed";
return -ENOMEM;
}
@@ -149,8 +151,7 @@ static int stripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
r = get_stripe(ti, sc, i, argv);
if (r < 0) {
- ti->error = "dm-stripe: Couldn't parse stripe "
- "destination";
+ ti->error = "Couldn't parse stripe destination";
while (i--)
dm_put_device(ti, sc->stripe[i].dev);
kfree(sc);
@@ -227,7 +228,7 @@ int __init dm_stripe_init(void)
r = dm_register_target(&stripe_target);
if (r < 0)
- DMWARN("striped target registration failed");
+ DMWARN("target registration failed");
return r;
}
@@ -235,7 +236,7 @@ int __init dm_stripe_init(void)
void dm_stripe_exit(void)
{
if (dm_unregister_target(&stripe_target))
- DMWARN("striped target unregistration failed");
+ DMWARN("target unregistration failed");
return;
}
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 8f56a54cf0ce..75fe9493e6af 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -17,6 +17,8 @@
#include <linux/mutex.h>
#include <asm/atomic.h>
+#define DM_MSG_PREFIX "table"
+
#define MAX_DEPTH 16
#define NODE_SIZE L1_CACHE_BYTES
#define KEYS_PER_NODE (NODE_SIZE / sizeof(sector_t))
@@ -237,6 +239,44 @@ int dm_table_create(struct dm_table **result, int mode,
return 0;
}
+int dm_create_error_table(struct dm_table **result, struct mapped_device *md)
+{
+ struct dm_table *t;
+ sector_t dev_size = 1;
+ int r;
+
+ /*
+ * Find current size of device.
+ * Default to 1 sector if inactive.
+ */
+ t = dm_get_table(md);
+ if (t) {
+ dev_size = dm_table_get_size(t);
+ dm_table_put(t);
+ }
+
+ r = dm_table_create(&t, FMODE_READ, 1, md);
+ if (r)
+ return r;
+
+ r = dm_table_add_target(t, "error", 0, dev_size, NULL);
+ if (r)
+ goto out;
+
+ r = dm_table_complete(t);
+ if (r)
+ goto out;
+
+ *result = t;
+
+out:
+ if (r)
+ dm_table_put(t);
+
+ return r;
+}
+EXPORT_SYMBOL_GPL(dm_create_error_table);
+
static void free_devices(struct list_head *devices)
{
struct list_head *tmp, *next;
@@ -590,6 +630,12 @@ int dm_split_args(int *argc, char ***argvp, char *input)
unsigned array_size = 0;
*argc = 0;
+
+ if (!input) {
+ *argvp = NULL;
+ return 0;
+ }
+
argv = realloc_argv(&array_size, argv);
if (!argv)
return -ENOMEM;
@@ -671,15 +717,14 @@ int dm_table_add_target(struct dm_table *t, const char *type,
memset(tgt, 0, sizeof(*tgt));
if (!len) {
- tgt->error = "zero-length target";
- DMERR("%s", tgt->error);
+ DMERR("%s: zero-length target", dm_device_name(t->md));
return -EINVAL;
}
tgt->type = dm_get_target_type(type);
if (!tgt->type) {
- tgt->error = "unknown target type";
- DMERR("%s", tgt->error);
+ DMERR("%s: %s: unknown target type", dm_device_name(t->md),
+ type);
return -EINVAL;
}
@@ -716,7 +761,7 @@ int dm_table_add_target(struct dm_table *t, const char *type,
return 0;
bad:
- DMERR("%s", tgt->error);
+ DMERR("%s: %s: %s", dm_device_name(t->md), type, tgt->error);
dm_put_target_type(tgt->type);
return r;
}
@@ -802,7 +847,7 @@ sector_t dm_table_get_size(struct dm_table *t)
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index)
{
- if (index > t->num_targets)
+ if (index >= t->num_targets)
return NULL;
return t->targets + index;
diff --git a/drivers/md/dm-target.c b/drivers/md/dm-target.c
index 64fd8e79ea4c..477a041a41cf 100644
--- a/drivers/md/dm-target.c
+++ b/drivers/md/dm-target.c
@@ -12,6 +12,8 @@
#include <linux/bio.h>
#include <linux/slab.h>
+#define DM_MSG_PREFIX "target"
+
struct tt_internal {
struct target_type tt;
diff --git a/drivers/md/dm-zero.c b/drivers/md/dm-zero.c
index 51c0639b2487..ea569f7348d2 100644
--- a/drivers/md/dm-zero.c
+++ b/drivers/md/dm-zero.c
@@ -10,13 +10,15 @@
#include <linux/init.h>
#include <linux/bio.h>
+#define DM_MSG_PREFIX "zero"
+
/*
* Construct a dummy mapping that only returns zeros
*/
static int zero_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
if (argc != 0) {
- ti->error = "dm-zero: No arguments required";
+ ti->error = "No arguments required";
return -EINVAL;
}
@@ -60,7 +62,7 @@ static int __init dm_zero_init(void)
int r = dm_register_target(&zero_target);
if (r < 0)
- DMERR("zero: register failed %d", r);
+ DMERR("register failed %d", r);
return r;
}
@@ -70,7 +72,7 @@ static void __exit dm_zero_exit(void)
int r = dm_unregister_target(&zero_target);
if (r < 0)
- DMERR("zero: unregister failed %d", r);
+ DMERR("unregister failed %d", r);
}
module_init(dm_zero_init)
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 4d710b7a133b..3ed2e53b9eb6 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001, 2002 Sistina Software (UK) Limited.
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -21,11 +21,14 @@
#include <linux/hdreg.h>
#include <linux/blktrace_api.h>
+#define DM_MSG_PREFIX "core"
+
static const char *_name = DM_NAME;
static unsigned int major = 0;
static unsigned int _major = 0;
+static DEFINE_SPINLOCK(_minor_lock);
/*
* One of these is allocated per bio.
*/
@@ -49,23 +52,28 @@ struct target_io {
union map_info *dm_get_mapinfo(struct bio *bio)
{
- if (bio && bio->bi_private)
- return &((struct target_io *)bio->bi_private)->info;
- return NULL;
+ if (bio && bio->bi_private)
+ return &((struct target_io *)bio->bi_private)->info;
+ return NULL;
}
+#define MINOR_ALLOCED ((void *)-1)
+
/*
* Bits for the md->flags field.
*/
#define DMF_BLOCK_IO 0
#define DMF_SUSPENDED 1
#define DMF_FROZEN 2
+#define DMF_FREEING 3
+#define DMF_DELETING 4
struct mapped_device {
struct rw_semaphore io_lock;
struct semaphore suspend_lock;
rwlock_t map_lock;
atomic_t holders;
+ atomic_t open_count;
unsigned long flags;
@@ -218,9 +226,25 @@ static int dm_blk_open(struct inode *inode, struct file *file)
{
struct mapped_device *md;
+ spin_lock(&_minor_lock);
+
md = inode->i_bdev->bd_disk->private_data;
+ if (!md)
+ goto out;
+
+ if (test_bit(DMF_FREEING, &md->flags) ||
+ test_bit(DMF_DELETING, &md->flags)) {
+ md = NULL;
+ goto out;
+ }
+
dm_get(md);
- return 0;
+ atomic_inc(&md->open_count);
+
+out:
+ spin_unlock(&_minor_lock);
+
+ return md ? 0 : -ENXIO;
}
static int dm_blk_close(struct inode *inode, struct file *file)
@@ -228,10 +252,35 @@ static int dm_blk_close(struct inode *inode, struct file *file)
struct mapped_device *md;
md = inode->i_bdev->bd_disk->private_data;
+ atomic_dec(&md->open_count);
dm_put(md);
return 0;
}
+int dm_open_count(struct mapped_device *md)
+{
+ return atomic_read(&md->open_count);
+}
+
+/*
+ * Guarantees nothing is using the device before it's deleted.
+ */
+int dm_lock_for_deletion(struct mapped_device *md)
+{
+ int r = 0;
+
+ spin_lock(&_minor_lock);
+
+ if (dm_open_count(md))
+ r = -EBUSY;
+ else
+ set_bit(DMF_DELETING, &md->flags);
+
+ spin_unlock(&_minor_lock);
+
+ return r;
+}
+
static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
{
struct mapped_device *md = bdev->bd_disk->private_data;
@@ -456,8 +505,8 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
if (r > 0) {
/* the bio has been remapped so dispatch it */
- blk_add_trace_remap(bdev_get_queue(clone->bi_bdev), clone,
- tio->io->bio->bi_bdev->bd_dev, sector,
+ blk_add_trace_remap(bdev_get_queue(clone->bi_bdev), clone,
+ tio->io->bio->bi_bdev->bd_dev, sector,
clone->bi_sector);
generic_make_request(clone);
@@ -744,43 +793,39 @@ static int dm_any_congested(void *congested_data, int bdi_bits)
/*-----------------------------------------------------------------
* An IDR is used to keep track of allocated minor numbers.
*---------------------------------------------------------------*/
-static DEFINE_MUTEX(_minor_lock);
static DEFINE_IDR(_minor_idr);
-static void free_minor(unsigned int minor)
+static void free_minor(int minor)
{
- mutex_lock(&_minor_lock);
+ spin_lock(&_minor_lock);
idr_remove(&_minor_idr, minor);
- mutex_unlock(&_minor_lock);
+ spin_unlock(&_minor_lock);
}
/*
* See if the device with a specific minor # is free.
*/
-static int specific_minor(struct mapped_device *md, unsigned int minor)
+static int specific_minor(struct mapped_device *md, int minor)
{
int r, m;
if (minor >= (1 << MINORBITS))
return -EINVAL;
- mutex_lock(&_minor_lock);
+ r = idr_pre_get(&_minor_idr, GFP_KERNEL);
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&_minor_lock);
if (idr_find(&_minor_idr, minor)) {
r = -EBUSY;
goto out;
}
- r = idr_pre_get(&_minor_idr, GFP_KERNEL);
- if (!r) {
- r = -ENOMEM;
- goto out;
- }
-
- r = idr_get_new_above(&_minor_idr, md, minor, &m);
- if (r) {
+ r = idr_get_new_above(&_minor_idr, MINOR_ALLOCED, minor, &m);
+ if (r)
goto out;
- }
if (m != minor) {
idr_remove(&_minor_idr, m);
@@ -789,24 +834,21 @@ static int specific_minor(struct mapped_device *md, unsigned int minor)
}
out:
- mutex_unlock(&_minor_lock);
+ spin_unlock(&_minor_lock);
return r;
}
-static int next_free_minor(struct mapped_device *md, unsigned int *minor)
+static int next_free_minor(struct mapped_device *md, int *minor)
{
- int r;
- unsigned int m;
-
- mutex_lock(&_minor_lock);
+ int r, m;
r = idr_pre_get(&_minor_idr, GFP_KERNEL);
- if (!r) {
- r = -ENOMEM;
- goto out;
- }
+ if (!r)
+ return -ENOMEM;
+
+ spin_lock(&_minor_lock);
- r = idr_get_new(&_minor_idr, md, &m);
+ r = idr_get_new(&_minor_idr, MINOR_ALLOCED, &m);
if (r) {
goto out;
}
@@ -820,7 +862,7 @@ static int next_free_minor(struct mapped_device *md, unsigned int *minor)
*minor = m;
out:
- mutex_unlock(&_minor_lock);
+ spin_unlock(&_minor_lock);
return r;
}
@@ -829,18 +871,25 @@ static struct block_device_operations dm_blk_dops;
/*
* Allocate and initialise a blank device with a given minor.
*/
-static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
+static struct mapped_device *alloc_dev(int minor)
{
int r;
struct mapped_device *md = kmalloc(sizeof(*md), GFP_KERNEL);
+ void *old_md;
if (!md) {
DMWARN("unable to allocate device, out of memory.");
return NULL;
}
+ if (!try_module_get(THIS_MODULE))
+ goto bad0;
+
/* get a minor number for the dev */
- r = persistent ? specific_minor(md, minor) : next_free_minor(md, &minor);
+ if (minor == DM_ANY_MINOR)
+ r = next_free_minor(md, &minor);
+ else
+ r = specific_minor(md, minor);
if (r < 0)
goto bad1;
@@ -849,6 +898,7 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
init_MUTEX(&md->suspend_lock);
rwlock_init(&md->map_lock);
atomic_set(&md->holders, 1);
+ atomic_set(&md->open_count, 0);
atomic_set(&md->event_nr, 0);
md->queue = blk_alloc_queue(GFP_KERNEL);
@@ -875,6 +925,10 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
if (!md->disk)
goto bad4;
+ atomic_set(&md->pending, 0);
+ init_waitqueue_head(&md->wait);
+ init_waitqueue_head(&md->eventq);
+
md->disk->major = _major;
md->disk->first_minor = minor;
md->disk->fops = &dm_blk_dops;
@@ -884,9 +938,12 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
add_disk(md->disk);
format_dev_t(md->name, MKDEV(_major, minor));
- atomic_set(&md->pending, 0);
- init_waitqueue_head(&md->wait);
- init_waitqueue_head(&md->eventq);
+ /* Populate the mapping, nobody knows we exist yet */
+ spin_lock(&_minor_lock);
+ old_md = idr_replace(&_minor_idr, md, minor);
+ spin_unlock(&_minor_lock);
+
+ BUG_ON(old_md != MINOR_ALLOCED);
return md;
@@ -898,13 +955,15 @@ static struct mapped_device *alloc_dev(unsigned int minor, int persistent)
blk_cleanup_queue(md->queue);
free_minor(minor);
bad1:
+ module_put(THIS_MODULE);
+ bad0:
kfree(md);
return NULL;
}
static void free_dev(struct mapped_device *md)
{
- unsigned int minor = md->disk->first_minor;
+ int minor = md->disk->first_minor;
if (md->suspended_bdev) {
thaw_bdev(md->suspended_bdev, NULL);
@@ -914,8 +973,14 @@ static void free_dev(struct mapped_device *md)
mempool_destroy(md->io_pool);
del_gendisk(md->disk);
free_minor(minor);
+
+ spin_lock(&_minor_lock);
+ md->disk->private_data = NULL;
+ spin_unlock(&_minor_lock);
+
put_disk(md->disk);
blk_cleanup_queue(md->queue);
+ module_put(THIS_MODULE);
kfree(md);
}
@@ -984,12 +1049,11 @@ static void __unbind(struct mapped_device *md)
/*
* Constructor for a new device.
*/
-static int create_aux(unsigned int minor, int persistent,
- struct mapped_device **result)
+int dm_create(int minor, struct mapped_device **result)
{
struct mapped_device *md;
- md = alloc_dev(minor, persistent);
+ md = alloc_dev(minor);
if (!md)
return -ENXIO;
@@ -997,16 +1061,6 @@ static int create_aux(unsigned int minor, int persistent,
return 0;
}
-int dm_create(struct mapped_device **result)
-{
- return create_aux(0, 0, result);
-}
-
-int dm_create_with_minor(unsigned int minor, struct mapped_device **result)
-{
- return create_aux(minor, 1, result);
-}
-
static struct mapped_device *dm_find_md(dev_t dev)
{
struct mapped_device *md;
@@ -1015,13 +1069,18 @@ static struct mapped_device *dm_find_md(dev_t dev)
if (MAJOR(dev) != _major || minor >= (1 << MINORBITS))
return NULL;
- mutex_lock(&_minor_lock);
+ spin_lock(&_minor_lock);
md = idr_find(&_minor_idr, minor);
- if (!md || (dm_disk(md)->first_minor != minor))
+ if (md && (md == MINOR_ALLOCED ||
+ (dm_disk(md)->first_minor != minor) ||
+ test_bit(DMF_FREEING, &md->flags))) {
md = NULL;
+ goto out;
+ }
- mutex_unlock(&_minor_lock);
+out:
+ spin_unlock(&_minor_lock);
return md;
}
@@ -1051,12 +1110,23 @@ void dm_get(struct mapped_device *md)
atomic_inc(&md->holders);
}
+const char *dm_device_name(struct mapped_device *md)
+{
+ return md->name;
+}
+EXPORT_SYMBOL_GPL(dm_device_name);
+
void dm_put(struct mapped_device *md)
{
struct dm_table *map;
- if (atomic_dec_and_test(&md->holders)) {
+ BUG_ON(test_bit(DMF_FREEING, &md->flags));
+
+ if (atomic_dec_and_lock(&md->holders, &_minor_lock)) {
map = dm_get_table(md);
+ idr_replace(&_minor_idr, MINOR_ALLOCED, dm_disk(md)->first_minor);
+ set_bit(DMF_FREEING, &md->flags);
+ spin_unlock(&_minor_lock);
if (!dm_suspended(md)) {
dm_table_presuspend_targets(map);
dm_table_postsuspend_targets(map);
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index fd90bc8f9e45..3c03c0ecab7e 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -2,7 +2,7 @@
* Internal header file for device mapper
*
* Copyright (C) 2001, 2002 Sistina Software
- * Copyright (C) 2004 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2004-2006 Red Hat, Inc. All rights reserved.
*
* This file is released under the LGPL.
*/
@@ -17,9 +17,10 @@
#include <linux/hdreg.h>
#define DM_NAME "device-mapper"
-#define DMWARN(f, x...) printk(KERN_WARNING DM_NAME ": " f "\n" , ## x)
-#define DMERR(f, x...) printk(KERN_ERR DM_NAME ": " f "\n" , ## x)
-#define DMINFO(f, x...) printk(KERN_INFO DM_NAME ": " f "\n" , ## x)
+
+#define DMERR(f, arg...) printk(KERN_ERR DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
+#define DMWARN(f, arg...) printk(KERN_WARNING DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
+#define DMINFO(f, arg...) printk(KERN_INFO DM_NAME ": " DM_MSG_PREFIX ": " f "\n", ## arg)
#define DMEMIT(x...) sz += ((sz >= maxlen) ? \
0 : scnprintf(result + sz, maxlen - sz, x))
@@ -39,83 +40,16 @@ struct dm_dev {
};
struct dm_table;
-struct mapped_device;
-
-/*-----------------------------------------------------------------
- * Functions for manipulating a struct mapped_device.
- * Drop the reference with dm_put when you finish with the object.
- *---------------------------------------------------------------*/
-int dm_create(struct mapped_device **md);
-int dm_create_with_minor(unsigned int minor, struct mapped_device **md);
-void dm_set_mdptr(struct mapped_device *md, void *ptr);
-void *dm_get_mdptr(struct mapped_device *md);
-struct mapped_device *dm_get_md(dev_t dev);
-
-/*
- * Reference counting for md.
- */
-void dm_get(struct mapped_device *md);
-void dm_put(struct mapped_device *md);
-
-/*
- * A device can still be used while suspended, but I/O is deferred.
- */
-int dm_suspend(struct mapped_device *md, int with_lockfs);
-int dm_resume(struct mapped_device *md);
-
-/*
- * The device must be suspended before calling this method.
- */
-int dm_swap_table(struct mapped_device *md, struct dm_table *t);
-
-/*
- * Drop a reference on the table when you've finished with the
- * result.
- */
-struct dm_table *dm_get_table(struct mapped_device *md);
-
-/*
- * Event functions.
- */
-uint32_t dm_get_event_nr(struct mapped_device *md);
-int dm_wait_event(struct mapped_device *md, int event_nr);
-
-/*
- * Info functions.
- */
-struct gendisk *dm_disk(struct mapped_device *md);
-int dm_suspended(struct mapped_device *md);
-
-/*
- * Geometry functions.
- */
-int dm_get_geometry(struct mapped_device *md, struct hd_geometry *geo);
-int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo);
/*-----------------------------------------------------------------
- * Functions for manipulating a table. Tables are also reference
- * counted.
+ * Internal table functions.
*---------------------------------------------------------------*/
-int dm_table_create(struct dm_table **result, int mode,
- unsigned num_targets, struct mapped_device *md);
-
-void dm_table_get(struct dm_table *t);
-void dm_table_put(struct dm_table *t);
-
-int dm_table_add_target(struct dm_table *t, const char *type,
- sector_t start, sector_t len, char *params);
-int dm_table_complete(struct dm_table *t);
void dm_table_event_callback(struct dm_table *t,
void (*fn)(void *), void *context);
-void dm_table_event(struct dm_table *t);
-sector_t dm_table_get_size(struct dm_table *t);
struct dm_target *dm_table_get_target(struct dm_table *t, unsigned int index);
struct dm_target *dm_table_find_target(struct dm_table *t, sector_t sector);
void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q);
-unsigned int dm_table_get_num_targets(struct dm_table *t);
struct list_head *dm_table_get_devices(struct dm_table *t);
-int dm_table_get_mode(struct dm_table *t);
-struct mapped_device *dm_table_get_md(struct dm_table *t);
void dm_table_presuspend_targets(struct dm_table *t);
void dm_table_postsuspend_targets(struct dm_table *t);
void dm_table_resume_targets(struct dm_table *t);
@@ -133,7 +67,6 @@ void dm_put_target_type(struct target_type *t);
int dm_target_iterate(void (*iter_func)(struct target_type *tt,
void *param), void *param);
-
/*-----------------------------------------------------------------
* Useful inlines.
*---------------------------------------------------------------*/
@@ -191,5 +124,7 @@ void dm_stripe_exit(void);
void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size);
union map_info *dm_get_mapinfo(struct bio *bio);
+int dm_open_count(struct mapped_device *md);
+int dm_lock_for_deletion(struct mapped_device *md);
#endif
diff --git a/drivers/md/kcopyd.c b/drivers/md/kcopyd.c
index 72480a48d88b..73ab875fb158 100644
--- a/drivers/md/kcopyd.c
+++ b/drivers/md/kcopyd.c
@@ -314,7 +314,7 @@ static void complete_io(unsigned long error, void *context)
if (error) {
if (job->rw == WRITE)
- job->write_err &= error;
+ job->write_err |= error;
else
job->read_err = 1;
@@ -460,7 +460,7 @@ static void segment_complete(int read_err,
job->read_err = 1;
if (write_err)
- job->write_err &= write_err;
+ job->write_err |= write_err;
/*
* Only dispatch more work if there hasn't been an error.
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index 777585458c85..ff83c9b5979e 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -111,7 +111,7 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
-static int linear_run (mddev_t *mddev)
+static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
{
linear_conf_t *conf;
dev_info_t **table;
@@ -121,20 +121,21 @@ static int linear_run (mddev_t *mddev)
sector_t curr_offset;
struct list_head *tmp;
- conf = kzalloc (sizeof (*conf) + mddev->raid_disks*sizeof(dev_info_t),
+ conf = kzalloc (sizeof (*conf) + raid_disks*sizeof(dev_info_t),
GFP_KERNEL);
if (!conf)
- goto out;
+ return NULL;
+
mddev->private = conf;
cnt = 0;
- mddev->array_size = 0;
+ conf->array_size = 0;
ITERATE_RDEV(mddev,rdev,tmp) {
int j = rdev->raid_disk;
dev_info_t *disk = conf->disks + j;
- if (j < 0 || j > mddev->raid_disks || disk->rdev) {
+ if (j < 0 || j > raid_disks || disk->rdev) {
printk("linear: disk numbering problem. Aborting!\n");
goto out;
}
@@ -152,11 +153,11 @@ static int linear_run (mddev_t *mddev)
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->size = rdev->size;
- mddev->array_size += rdev->size;
+ conf->array_size += rdev->size;
cnt++;
}
- if (cnt != mddev->raid_disks) {
+ if (cnt != raid_disks) {
printk("linear: not enough drives present. Aborting!\n");
goto out;
}
@@ -200,7 +201,7 @@ static int linear_run (mddev_t *mddev)
unsigned round;
unsigned long base;
- sz = mddev->array_size >> conf->preshift;
+ sz = conf->array_size >> conf->preshift;
sz += 1; /* force round-up */
base = conf->hash_spacing >> conf->preshift;
round = sector_div(sz, base);
@@ -247,14 +248,56 @@ static int linear_run (mddev_t *mddev)
BUG_ON(table - conf->hash_table > nb_zone);
+ return conf;
+
+out:
+ kfree(conf);
+ return NULL;
+}
+
+static int linear_run (mddev_t *mddev)
+{
+ linear_conf_t *conf;
+
+ conf = linear_conf(mddev, mddev->raid_disks);
+
+ if (!conf)
+ return 1;
+ mddev->private = conf;
+ mddev->array_size = conf->array_size;
+
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
mddev->queue->unplug_fn = linear_unplug;
mddev->queue->issue_flush_fn = linear_issue_flush;
return 0;
+}
-out:
- kfree(conf);
- return 1;
+static int linear_add(mddev_t *mddev, mdk_rdev_t *rdev)
+{
+ /* Adding a drive to a linear array allows the array to grow.
+ * It is permitted if the new drive has a matching superblock
+ * already on it, with raid_disk equal to raid_disks.
+ * It is achieved by creating a new linear_private_data structure
+ * and swapping it in in-place of the current one.
+ * The current one is never freed until the array is stopped.
+ * This avoids races.
+ */
+ linear_conf_t *newconf;
+
+ if (rdev->raid_disk != mddev->raid_disks)
+ return -EINVAL;
+
+ newconf = linear_conf(mddev,mddev->raid_disks+1);
+
+ if (!newconf)
+ return -ENOMEM;
+
+ newconf->prev = mddev_to_conf(mddev);
+ mddev->private = newconf;
+ mddev->raid_disks++;
+ mddev->array_size = newconf->array_size;
+ set_capacity(mddev->gendisk, mddev->array_size << 1);
+ return 0;
}
static int linear_stop (mddev_t *mddev)
@@ -262,8 +305,12 @@ static int linear_stop (mddev_t *mddev)
linear_conf_t *conf = mddev_to_conf(mddev);
blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
- kfree(conf->hash_table);
- kfree(conf);
+ do {
+ linear_conf_t *t = conf->prev;
+ kfree(conf->hash_table);
+ kfree(conf);
+ conf = t;
+ } while (conf);
return 0;
}
@@ -360,6 +407,7 @@ static struct mdk_personality linear_personality =
.run = linear_run,
.stop = linear_stop,
.status = linear_status,
+ .hot_add_disk = linear_add,
};
static int __init linear_init (void)
diff --git a/drivers/md/md.c b/drivers/md/md.c
index f19b874753a9..306268ec99ff 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -44,6 +44,7 @@
#include <linux/suspend.h>
#include <linux/poll.h>
#include <linux/mutex.h>
+#include <linux/ctype.h>
#include <linux/init.h>
@@ -72,6 +73,10 @@ static void autostart_arrays (int part);
static LIST_HEAD(pers_list);
static DEFINE_SPINLOCK(pers_lock);
+static void md_print_devices(void);
+
+#define MD_BUG(x...) { printk("md: bug in file %s, line %d\n", __FILE__, __LINE__); md_print_devices(); }
+
/*
* Current RAID-1,4,5 parallel reconstruction 'guaranteed speed limit'
* is 1000 KB/sec, so the extra system load does not show up that much.
@@ -170,7 +175,7 @@ EXPORT_SYMBOL_GPL(md_new_event);
/* Alternate version that can be called from interrupts
* when calling sysfs_notify isn't needed.
*/
-void md_new_event_inintr(mddev_t *mddev)
+static void md_new_event_inintr(mddev_t *mddev)
{
atomic_inc(&md_event_count);
wake_up(&md_event_waiters);
@@ -732,6 +737,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
{
mdp_disk_t *desc;
mdp_super_t *sb = (mdp_super_t *)page_address(rdev->sb_page);
+ __u64 ev1 = md_event(sb);
rdev->raid_disk = -1;
rdev->flags = 0;
@@ -748,7 +754,7 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->layout = sb->layout;
mddev->raid_disks = sb->raid_disks;
mddev->size = sb->size;
- mddev->events = md_event(sb);
+ mddev->events = ev1;
mddev->bitmap_offset = 0;
mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
@@ -797,7 +803,6 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
} else if (mddev->pers == NULL) {
/* Insist on good event counter while assembling */
- __u64 ev1 = md_event(sb);
++ev1;
if (ev1 < mddev->events)
return -EINVAL;
@@ -805,19 +810,21 @@ static int super_90_validate(mddev_t *mddev, mdk_rdev_t *rdev)
/* if adding to array with a bitmap, then we can accept an
* older device ... but not too old.
*/
- __u64 ev1 = md_event(sb);
if (ev1 < mddev->bitmap->events_cleared)
return 0;
- } else /* just a hot-add of a new device, leave raid_disk at -1 */
- return 0;
+ } else {
+ if (ev1 < mddev->events)
+ /* just a hot-add of a new device, leave raid_disk at -1 */
+ return 0;
+ }
if (mddev->level != LEVEL_MULTIPATH) {
desc = sb->disks + rdev->desc_nr;
if (desc->state & (1<<MD_DISK_FAULTY))
set_bit(Faulty, &rdev->flags);
- else if (desc->state & (1<<MD_DISK_SYNC) &&
- desc->raid_disk < mddev->raid_disks) {
+ else if (desc->state & (1<<MD_DISK_SYNC) /* &&
+ desc->raid_disk < mddev->raid_disks */) {
set_bit(In_sync, &rdev->flags);
rdev->raid_disk = desc->raid_disk;
}
@@ -1100,6 +1107,7 @@ static int super_1_load(mdk_rdev_t *rdev, mdk_rdev_t *refdev, int minor_version)
static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
{
struct mdp_superblock_1 *sb = (struct mdp_superblock_1*)page_address(rdev->sb_page);
+ __u64 ev1 = le64_to_cpu(sb->events);
rdev->raid_disk = -1;
rdev->flags = 0;
@@ -1115,7 +1123,7 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
mddev->layout = le32_to_cpu(sb->layout);
mddev->raid_disks = le32_to_cpu(sb->raid_disks);
mddev->size = le64_to_cpu(sb->size)/2;
- mddev->events = le64_to_cpu(sb->events);
+ mddev->events = ev1;
mddev->bitmap_offset = 0;
mddev->default_bitmap_offset = 1024 >> 9;
@@ -1149,7 +1157,6 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
} else if (mddev->pers == NULL) {
/* Insist of good event counter while assembling */
- __u64 ev1 = le64_to_cpu(sb->events);
++ev1;
if (ev1 < mddev->events)
return -EINVAL;
@@ -1157,12 +1164,13 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
/* If adding to array with a bitmap, then we can accept an
* older device, but not too old.
*/
- __u64 ev1 = le64_to_cpu(sb->events);
if (ev1 < mddev->bitmap->events_cleared)
return 0;
- } else /* just a hot-add of a new device, leave raid_disk at -1 */
- return 0;
-
+ } else {
+ if (ev1 < mddev->events)
+ /* just a hot-add of a new device, leave raid_disk at -1 */
+ return 0;
+ }
if (mddev->level != LEVEL_MULTIPATH) {
int role;
rdev->desc_nr = le32_to_cpu(sb->dev_number);
@@ -1174,7 +1182,11 @@ static int super_1_validate(mddev_t *mddev, mdk_rdev_t *rdev)
set_bit(Faulty, &rdev->flags);
break;
default:
- set_bit(In_sync, &rdev->flags);
+ if ((le32_to_cpu(sb->feature_map) &
+ MD_FEATURE_RECOVERY_OFFSET))
+ rdev->recovery_offset = le64_to_cpu(sb->recovery_offset);
+ else
+ set_bit(In_sync, &rdev->flags);
rdev->raid_disk = role;
break;
}
@@ -1198,6 +1210,7 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->feature_map = 0;
sb->pad0 = 0;
+ sb->recovery_offset = cpu_to_le64(0);
memset(sb->pad1, 0, sizeof(sb->pad1));
memset(sb->pad2, 0, sizeof(sb->pad2));
memset(sb->pad3, 0, sizeof(sb->pad3));
@@ -1218,6 +1231,14 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->bitmap_offset = cpu_to_le32((__u32)mddev->bitmap_offset);
sb->feature_map = cpu_to_le32(MD_FEATURE_BITMAP_OFFSET);
}
+
+ if (rdev->raid_disk >= 0 &&
+ !test_bit(In_sync, &rdev->flags) &&
+ rdev->recovery_offset > 0) {
+ sb->feature_map |= cpu_to_le32(MD_FEATURE_RECOVERY_OFFSET);
+ sb->recovery_offset = cpu_to_le64(rdev->recovery_offset);
+ }
+
if (mddev->reshape_position != MaxSector) {
sb->feature_map |= cpu_to_le32(MD_FEATURE_RESHAPE_ACTIVE);
sb->reshape_position = cpu_to_le64(mddev->reshape_position);
@@ -1242,11 +1263,12 @@ static void super_1_sync(mddev_t *mddev, mdk_rdev_t *rdev)
sb->dev_roles[i] = cpu_to_le16(0xfffe);
else if (test_bit(In_sync, &rdev2->flags))
sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
+ else if (rdev2->raid_disk >= 0 && rdev2->recovery_offset > 0)
+ sb->dev_roles[i] = cpu_to_le16(rdev2->raid_disk);
else
sb->dev_roles[i] = cpu_to_le16(0xffff);
}
- sb->recovery_offset = cpu_to_le64(0); /* not supported yet */
sb->sb_csum = calc_sb_1_csum(sb);
}
@@ -1507,7 +1529,7 @@ static void print_rdev(mdk_rdev_t *rdev)
printk(KERN_INFO "md: no rdev superblock!\n");
}
-void md_print_devices(void)
+static void md_print_devices(void)
{
struct list_head *tmp, *tmp2;
mdk_rdev_t *rdev;
@@ -1536,15 +1558,30 @@ void md_print_devices(void)
}
-static void sync_sbs(mddev_t * mddev)
+static void sync_sbs(mddev_t * mddev, int nospares)
{
+ /* Update each superblock (in-memory image), but
+ * if we are allowed to, skip spares which already
+ * have the right event counter, or have one earlier
+ * (which would mean they aren't being marked as dirty
+ * with the rest of the array)
+ */
mdk_rdev_t *rdev;
struct list_head *tmp;
ITERATE_RDEV(mddev,rdev,tmp) {
- super_types[mddev->major_version].
- sync_super(mddev, rdev);
- rdev->sb_loaded = 1;
+ if (rdev->sb_events == mddev->events ||
+ (nospares &&
+ rdev->raid_disk < 0 &&
+ (rdev->sb_events&1)==0 &&
+ rdev->sb_events+1 == mddev->events)) {
+ /* Don't update this superblock */
+ rdev->sb_loaded = 2;
+ } else {
+ super_types[mddev->major_version].
+ sync_super(mddev, rdev);
+ rdev->sb_loaded = 1;
+ }
}
}
@@ -1554,12 +1591,42 @@ void md_update_sb(mddev_t * mddev)
struct list_head *tmp;
mdk_rdev_t *rdev;
int sync_req;
+ int nospares = 0;
repeat:
spin_lock_irq(&mddev->write_lock);
sync_req = mddev->in_sync;
mddev->utime = get_seconds();
- mddev->events ++;
+ if (mddev->sb_dirty == 3)
+ /* just a clean<-> dirty transition, possibly leave spares alone,
+ * though if events isn't the right even/odd, we will have to do
+ * spares after all
+ */
+ nospares = 1;
+
+ /* If this is just a dirty<->clean transition, and the array is clean
+ * and 'events' is odd, we can roll back to the previous clean state */
+ if (mddev->sb_dirty == 3
+ && (mddev->in_sync && mddev->recovery_cp == MaxSector)
+ && (mddev->events & 1))
+ mddev->events--;
+ else {
+ /* otherwise we have to go forward and ... */
+ mddev->events ++;
+ if (!mddev->in_sync || mddev->recovery_cp != MaxSector) { /* not clean */
+ /* .. if the array isn't clean, insist on an odd 'events' */
+ if ((mddev->events&1)==0) {
+ mddev->events++;
+ nospares = 0;
+ }
+ } else {
+ /* otherwise insist on an even 'events' (for clean states) */
+ if ((mddev->events&1)) {
+ mddev->events++;
+ nospares = 0;
+ }
+ }
+ }
if (!mddev->events) {
/*
@@ -1571,7 +1638,7 @@ repeat:
mddev->events --;
}
mddev->sb_dirty = 2;
- sync_sbs(mddev);
+ sync_sbs(mddev, nospares);
/*
* do not write anything to disk if using
@@ -1593,6 +1660,8 @@ repeat:
ITERATE_RDEV(mddev,rdev,tmp) {
char b[BDEVNAME_SIZE];
dprintk(KERN_INFO "md: ");
+ if (rdev->sb_loaded != 1)
+ continue; /* no noise on spare devices */
if (test_bit(Faulty, &rdev->flags))
dprintk("(skipping faulty ");
@@ -1604,6 +1673,7 @@ repeat:
dprintk(KERN_INFO "(write) %s's sb offset: %llu\n",
bdevname(rdev->bdev,b),
(unsigned long long)rdev->sb_offset);
+ rdev->sb_events = mddev->events;
} else
dprintk(")\n");
@@ -1667,6 +1737,10 @@ state_show(mdk_rdev_t *rdev, char *page)
len += sprintf(page+len, "%sin_sync",sep);
sep = ",";
}
+ if (test_bit(WriteMostly, &rdev->flags)) {
+ len += sprintf(page+len, "%swrite_mostly",sep);
+ sep = ",";
+ }
if (!test_bit(Faulty, &rdev->flags) &&
!test_bit(In_sync, &rdev->flags)) {
len += sprintf(page+len, "%sspare", sep);
@@ -1675,8 +1749,40 @@ state_show(mdk_rdev_t *rdev, char *page)
return len+sprintf(page+len, "\n");
}
+static ssize_t
+state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
+{
+ /* can write
+ * faulty - simulates and error
+ * remove - disconnects the device
+ * writemostly - sets write_mostly
+ * -writemostly - clears write_mostly
+ */
+ int err = -EINVAL;
+ if (cmd_match(buf, "faulty") && rdev->mddev->pers) {
+ md_error(rdev->mddev, rdev);
+ err = 0;
+ } else if (cmd_match(buf, "remove")) {
+ if (rdev->raid_disk >= 0)
+ err = -EBUSY;
+ else {
+ mddev_t *mddev = rdev->mddev;
+ kick_rdev_from_array(rdev);
+ md_update_sb(mddev);
+ md_new_event(mddev);
+ err = 0;
+ }
+ } else if (cmd_match(buf, "writemostly")) {
+ set_bit(WriteMostly, &rdev->flags);
+ err = 0;
+ } else if (cmd_match(buf, "-writemostly")) {
+ clear_bit(WriteMostly, &rdev->flags);
+ err = 0;
+ }
+ return err ? err : len;
+}
static struct rdev_sysfs_entry
-rdev_state = __ATTR_RO(state);
+rdev_state = __ATTR(state, 0644, state_show, state_store);
static ssize_t
super_show(mdk_rdev_t *rdev, char *page)
@@ -1873,6 +1979,7 @@ static mdk_rdev_t *md_import_device(dev_t newdev, int super_format, int super_mi
rdev->desc_nr = -1;
rdev->flags = 0;
rdev->data_offset = 0;
+ rdev->sb_events = 0;
atomic_set(&rdev->nr_pending, 0);
atomic_set(&rdev->read_errors, 0);
atomic_set(&rdev->corrected_errors, 0);
@@ -1978,6 +2085,54 @@ static void analyze_sbs(mddev_t * mddev)
}
static ssize_t
+safe_delay_show(mddev_t *mddev, char *page)
+{
+ int msec = (mddev->safemode_delay*1000)/HZ;
+ return sprintf(page, "%d.%03d\n", msec/1000, msec%1000);
+}
+static ssize_t
+safe_delay_store(mddev_t *mddev, const char *cbuf, size_t len)
+{
+ int scale=1;
+ int dot=0;
+ int i;
+ unsigned long msec;
+ char buf[30];
+ char *e;
+ /* remove a period, and count digits after it */
+ if (len >= sizeof(buf))
+ return -EINVAL;
+ strlcpy(buf, cbuf, len);
+ buf[len] = 0;
+ for (i=0; i<len; i++) {
+ if (dot) {
+ if (isdigit(buf[i])) {
+ buf[i-1] = buf[i];
+ scale *= 10;
+ }
+ buf[i] = 0;
+ } else if (buf[i] == '.') {
+ dot=1;
+ buf[i] = 0;
+ }
+ }
+ msec = simple_strtoul(buf, &e, 10);
+ if (e == buf || (*e && *e != '\n'))
+ return -EINVAL;
+ msec = (msec * 1000) / scale;
+ if (msec == 0)
+ mddev->safemode_delay = 0;
+ else {
+ mddev->safemode_delay = (msec*HZ)/1000;
+ if (mddev->safemode_delay == 0)
+ mddev->safemode_delay = 1;
+ }
+ return len;
+}
+static struct md_sysfs_entry md_safe_delay =
+__ATTR(safe_mode_delay, 0644,safe_delay_show, safe_delay_store);
+
+static ssize_t
level_show(mddev_t *mddev, char *page)
{
struct mdk_personality *p = mddev->pers;
@@ -2012,6 +2167,32 @@ level_store(mddev_t *mddev, const char *buf, size_t len)
static struct md_sysfs_entry md_level =
__ATTR(level, 0644, level_show, level_store);
+
+static ssize_t
+layout_show(mddev_t *mddev, char *page)
+{
+ /* just a number, not meaningful for all levels */
+ return sprintf(page, "%d\n", mddev->layout);
+}
+
+static ssize_t
+layout_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ char *e;
+ unsigned long n = simple_strtoul(buf, &e, 10);
+ if (mddev->pers)
+ return -EBUSY;
+
+ if (!*buf || (*e && *e != '\n'))
+ return -EINVAL;
+
+ mddev->layout = n;
+ return len;
+}
+static struct md_sysfs_entry md_layout =
+__ATTR(layout, 0655, layout_show, layout_store);
+
+
static ssize_t
raid_disks_show(mddev_t *mddev, char *page)
{
@@ -2067,6 +2248,200 @@ static struct md_sysfs_entry md_chunk_size =
__ATTR(chunk_size, 0644, chunk_size_show, chunk_size_store);
static ssize_t
+resync_start_show(mddev_t *mddev, char *page)
+{
+ return sprintf(page, "%llu\n", (unsigned long long)mddev->recovery_cp);
+}
+
+static ssize_t
+resync_start_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ /* can only set chunk_size if array is not yet active */
+ char *e;
+ unsigned long long n = simple_strtoull(buf, &e, 10);
+
+ if (mddev->pers)
+ return -EBUSY;
+ if (!*buf || (*e && *e != '\n'))
+ return -EINVAL;
+
+ mddev->recovery_cp = n;
+ return len;
+}
+static struct md_sysfs_entry md_resync_start =
+__ATTR(resync_start, 0644, resync_start_show, resync_start_store);
+
+/*
+ * The array state can be:
+ *
+ * clear
+ * No devices, no size, no level
+ * Equivalent to STOP_ARRAY ioctl
+ * inactive
+ * May have some settings, but array is not active
+ * all IO results in error
+ * When written, doesn't tear down array, but just stops it
+ * suspended (not supported yet)
+ * All IO requests will block. The array can be reconfigured.
+ * Writing this, if accepted, will block until array is quiessent
+ * readonly
+ * no resync can happen. no superblocks get written.
+ * write requests fail
+ * read-auto
+ * like readonly, but behaves like 'clean' on a write request.
+ *
+ * clean - no pending writes, but otherwise active.
+ * When written to inactive array, starts without resync
+ * If a write request arrives then
+ * if metadata is known, mark 'dirty' and switch to 'active'.
+ * if not known, block and switch to write-pending
+ * If written to an active array that has pending writes, then fails.
+ * active
+ * fully active: IO and resync can be happening.
+ * When written to inactive array, starts with resync
+ *
+ * write-pending
+ * clean, but writes are blocked waiting for 'active' to be written.
+ *
+ * active-idle
+ * like active, but no writes have been seen for a while (100msec).
+ *
+ */
+enum array_state { clear, inactive, suspended, readonly, read_auto, clean, active,
+ write_pending, active_idle, bad_word};
+static char *array_states[] = {
+ "clear", "inactive", "suspended", "readonly", "read-auto", "clean", "active",
+ "write-pending", "active-idle", NULL };
+
+static int match_word(const char *word, char **list)
+{
+ int n;
+ for (n=0; list[n]; n++)
+ if (cmd_match(word, list[n]))
+ break;
+ return n;
+}
+
+static ssize_t
+array_state_show(mddev_t *mddev, char *page)
+{
+ enum array_state st = inactive;
+
+ if (mddev->pers)
+ switch(mddev->ro) {
+ case 1:
+ st = readonly;
+ break;
+ case 2:
+ st = read_auto;
+ break;
+ case 0:
+ if (mddev->in_sync)
+ st = clean;
+ else if (mddev->safemode)
+ st = active_idle;
+ else
+ st = active;
+ }
+ else {
+ if (list_empty(&mddev->disks) &&
+ mddev->raid_disks == 0 &&
+ mddev->size == 0)
+ st = clear;
+ else
+ st = inactive;
+ }
+ return sprintf(page, "%s\n", array_states[st]);
+}
+
+static int do_md_stop(mddev_t * mddev, int ro);
+static int do_md_run(mddev_t * mddev);
+static int restart_array(mddev_t *mddev);
+
+static ssize_t
+array_state_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ int err = -EINVAL;
+ enum array_state st = match_word(buf, array_states);
+ switch(st) {
+ case bad_word:
+ break;
+ case clear:
+ /* stopping an active array */
+ if (mddev->pers) {
+ if (atomic_read(&mddev->active) > 1)
+ return -EBUSY;
+ err = do_md_stop(mddev, 0);
+ }
+ break;
+ case inactive:
+ /* stopping an active array */
+ if (mddev->pers) {
+ if (atomic_read(&mddev->active) > 1)
+ return -EBUSY;
+ err = do_md_stop(mddev, 2);
+ }
+ break;
+ case suspended:
+ break; /* not supported yet */
+ case readonly:
+ if (mddev->pers)
+ err = do_md_stop(mddev, 1);
+ else {
+ mddev->ro = 1;
+ err = do_md_run(mddev);
+ }
+ break;
+ case read_auto:
+ /* stopping an active array */
+ if (mddev->pers) {
+ err = do_md_stop(mddev, 1);
+ if (err == 0)
+ mddev->ro = 2; /* FIXME mark devices writable */
+ } else {
+ mddev->ro = 2;
+ err = do_md_run(mddev);
+ }
+ break;
+ case clean:
+ if (mddev->pers) {
+ restart_array(mddev);
+ spin_lock_irq(&mddev->write_lock);
+ if (atomic_read(&mddev->writes_pending) == 0) {
+ mddev->in_sync = 1;
+ mddev->sb_dirty = 1;
+ }
+ spin_unlock_irq(&mddev->write_lock);
+ } else {
+ mddev->ro = 0;
+ mddev->recovery_cp = MaxSector;
+ err = do_md_run(mddev);
+ }
+ break;
+ case active:
+ if (mddev->pers) {
+ restart_array(mddev);
+ mddev->sb_dirty = 0;
+ wake_up(&mddev->sb_wait);
+ err = 0;
+ } else {
+ mddev->ro = 0;
+ err = do_md_run(mddev);
+ }
+ break;
+ case write_pending:
+ case active_idle:
+ /* these cannot be set */
+ break;
+ }
+ if (err)
+ return err;
+ else
+ return len;
+}
+static struct md_sysfs_entry md_array_state = __ATTR(array_state, 0644, array_state_show, array_state_store);
+
+static ssize_t
null_show(mddev_t *mddev, char *page)
{
return -EINVAL;
@@ -2428,11 +2803,15 @@ __ATTR(suspend_hi, S_IRUGO|S_IWUSR, suspend_hi_show, suspend_hi_store);
static struct attribute *md_default_attrs[] = {
&md_level.attr,
+ &md_layout.attr,
&md_raid_disks.attr,
&md_chunk_size.attr,
&md_size.attr,
+ &md_resync_start.attr,
&md_metadata.attr,
&md_new_device.attr,
+ &md_safe_delay.attr,
+ &md_array_state.attr,
NULL,
};
@@ -2553,8 +2932,6 @@ static struct kobject *md_probe(dev_t dev, int *part, void *data)
return NULL;
}
-void md_wakeup_thread(mdk_thread_t *thread);
-
static void md_safemode_timeout(unsigned long data)
{
mddev_t *mddev = (mddev_t *) data;
@@ -2708,7 +3085,7 @@ static int do_md_run(mddev_t * mddev)
mddev->safemode = 0;
mddev->safemode_timer.function = md_safemode_timeout;
mddev->safemode_timer.data = (unsigned long) mddev;
- mddev->safemode_delay = (20 * HZ)/1000 +1; /* 20 msec delay */
+ mddev->safemode_delay = (200 * HZ)/1000 +1; /* 200 msec delay */
mddev->in_sync = 1;
ITERATE_RDEV(mddev,rdev,tmp)
@@ -2736,6 +3113,36 @@ static int do_md_run(mddev_t * mddev)
mddev->queue->queuedata = mddev;
mddev->queue->make_request_fn = mddev->pers->make_request;
+ /* If there is a partially-recovered drive we need to
+ * start recovery here. If we leave it to md_check_recovery,
+ * it will remove the drives and not do the right thing
+ */
+ if (mddev->degraded) {
+ struct list_head *rtmp;
+ int spares = 0;
+ ITERATE_RDEV(mddev,rdev,rtmp)
+ if (rdev->raid_disk >= 0 &&
+ !test_bit(In_sync, &rdev->flags) &&
+ !test_bit(Faulty, &rdev->flags))
+ /* complete an interrupted recovery */
+ spares++;
+ if (spares && mddev->pers->sync_request) {
+ mddev->recovery = 0;
+ set_bit(MD_RECOVERY_RUNNING, &mddev->recovery);
+ mddev->sync_thread = md_register_thread(md_do_sync,
+ mddev,
+ "%s_resync");
+ if (!mddev->sync_thread) {
+ printk(KERN_ERR "%s: could not start resync"
+ " thread...\n",
+ mdname(mddev));
+ /* leave the spares where they are, it shouldn't hurt */
+ mddev->recovery = 0;
+ } else
+ md_wakeup_thread(mddev->sync_thread);
+ }
+ }
+
mddev->changed = 1;
md_new_event(mddev);
return 0;
@@ -2769,18 +3176,47 @@ static int restart_array(mddev_t *mddev)
*/
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
md_wakeup_thread(mddev->thread);
+ md_wakeup_thread(mddev->sync_thread);
err = 0;
- } else {
- printk(KERN_ERR "md: %s has no personality assigned.\n",
- mdname(mddev));
+ } else
err = -EINVAL;
- }
out:
return err;
}
-static int do_md_stop(mddev_t * mddev, int ro)
+/* similar to deny_write_access, but accounts for our holding a reference
+ * to the file ourselves */
+static int deny_bitmap_write_access(struct file * file)
+{
+ struct inode *inode = file->f_mapping->host;
+
+ spin_lock(&inode->i_lock);
+ if (atomic_read(&inode->i_writecount) > 1) {
+ spin_unlock(&inode->i_lock);
+ return -ETXTBSY;
+ }
+ atomic_set(&inode->i_writecount, -1);
+ spin_unlock(&inode->i_lock);
+
+ return 0;
+}
+
+static void restore_bitmap_write_access(struct file *file)
+{
+ struct inode *inode = file->f_mapping->host;
+
+ spin_lock(&inode->i_lock);
+ atomic_set(&inode->i_writecount, 1);
+ spin_unlock(&inode->i_lock);
+}
+
+/* mode:
+ * 0 - completely stop and dis-assemble array
+ * 1 - switch to readonly
+ * 2 - stop but do not disassemble array
+ */
+static int do_md_stop(mddev_t * mddev, int mode)
{
int err = 0;
struct gendisk *disk = mddev->gendisk;
@@ -2792,6 +3228,7 @@ static int do_md_stop(mddev_t * mddev, int ro)
}
if (mddev->sync_thread) {
+ set_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
set_bit(MD_RECOVERY_INTR, &mddev->recovery);
md_unregister_thread(mddev->sync_thread);
mddev->sync_thread = NULL;
@@ -2801,12 +3238,15 @@ static int do_md_stop(mddev_t * mddev, int ro)
invalidate_partition(disk, 0);
- if (ro) {
+ switch(mode) {
+ case 1: /* readonly */
err = -ENXIO;
if (mddev->ro==1)
goto out;
mddev->ro = 1;
- } else {
+ break;
+ case 0: /* disassemble */
+ case 2: /* stop */
bitmap_flush(mddev);
md_super_wait(mddev);
if (mddev->ro)
@@ -2821,19 +3261,20 @@ static int do_md_stop(mddev_t * mddev, int ro)
if (mddev->ro)
mddev->ro = 0;
}
- if (!mddev->in_sync) {
+ if (!mddev->in_sync || mddev->sb_dirty) {
/* mark array as shutdown cleanly */
mddev->in_sync = 1;
md_update_sb(mddev);
}
- if (ro)
+ if (mode == 1)
set_disk_ro(disk, 1);
+ clear_bit(MD_RECOVERY_FROZEN, &mddev->recovery);
}
/*
* Free resources if final stop
*/
- if (!ro) {
+ if (mode == 0) {
mdk_rdev_t *rdev;
struct list_head *tmp;
struct gendisk *disk;
@@ -2841,7 +3282,7 @@ static int do_md_stop(mddev_t * mddev, int ro)
bitmap_destroy(mddev);
if (mddev->bitmap_file) {
- atomic_set(&mddev->bitmap_file->f_dentry->d_inode->i_writecount, 1);
+ restore_bitmap_write_access(mddev->bitmap_file);
fput(mddev->bitmap_file);
mddev->bitmap_file = NULL;
}
@@ -2857,11 +3298,15 @@ static int do_md_stop(mddev_t * mddev, int ro)
export_array(mddev);
mddev->array_size = 0;
+ mddev->size = 0;
+ mddev->raid_disks = 0;
+ mddev->recovery_cp = 0;
+
disk = mddev->gendisk;
if (disk)
set_capacity(disk, 0);
mddev->changed = 1;
- } else
+ } else if (mddev->pers)
printk(KERN_INFO "md: %s switched to read-only mode.\n",
mdname(mddev));
err = 0;
@@ -3264,6 +3709,17 @@ static int add_new_disk(mddev_t * mddev, mdu_disk_info_t *info)
rdev->raid_disk = -1;
err = bind_rdev_to_array(rdev, mddev);
+ if (!err && !mddev->pers->hot_remove_disk) {
+ /* If there is hot_add_disk but no hot_remove_disk
+ * then added disks for geometry changes,
+ * and should be added immediately.
+ */
+ super_types[mddev->major_version].
+ validate_super(mddev, rdev);
+ err = mddev->pers->hot_add_disk(mddev, rdev);
+ if (err)
+ unbind_rdev_from_array(rdev);
+ }
if (err)
export_rdev(rdev);
@@ -3434,23 +3890,6 @@ abort_export:
return err;
}
-/* similar to deny_write_access, but accounts for our holding a reference
- * to the file ourselves */
-static int deny_bitmap_write_access(struct file * file)
-{
- struct inode *inode = file->f_mapping->host;
-
- spin_lock(&inode->i_lock);
- if (atomic_read(&inode->i_writecount) > 1) {
- spin_unlock(&inode->i_lock);
- return -ETXTBSY;
- }
- atomic_set(&inode->i_writecount, -1);
- spin_unlock(&inode->i_lock);
-
- return 0;
-}
-
static int set_bitmap_file(mddev_t *mddev, int fd)
{
int err;
@@ -3491,12 +3930,17 @@ static int set_bitmap_file(mddev_t *mddev, int fd)
mddev->pers->quiesce(mddev, 1);
if (fd >= 0)
err = bitmap_create(mddev);
- if (fd < 0 || err)
+ if (fd < 0 || err) {
bitmap_destroy(mddev);
+ fd = -1; /* make sure to put the file */
+ }
mddev->pers->quiesce(mddev, 0);
- } else if (fd < 0) {
- if (mddev->bitmap_file)
+ }
+ if (fd < 0) {
+ if (mddev->bitmap_file) {
+ restore_bitmap_write_access(mddev->bitmap_file);
fput(mddev->bitmap_file);
+ }
mddev->bitmap_file = NULL;
}
@@ -3977,11 +4421,6 @@ static int md_ioctl(struct inode *inode, struct file *file,
goto done_unlock;
default:
- if (_IOC_TYPE(cmd) == MD_MAJOR)
- printk(KERN_WARNING "md: %s(pid %d) used"
- " obsolete MD ioctl, upgrade your"
- " software to use new ictls.\n",
- current->comm, current->pid);
err = -EINVAL;
goto abort_unlock;
}
@@ -4586,7 +5025,7 @@ void md_write_start(mddev_t *mddev, struct bio *bi)
spin_lock_irq(&mddev->write_lock);
if (mddev->in_sync) {
mddev->in_sync = 0;
- mddev->sb_dirty = 1;
+ mddev->sb_dirty = 3;
md_wakeup_thread(mddev->thread);
}
spin_unlock_irq(&mddev->write_lock);
@@ -4599,7 +5038,7 @@ void md_write_end(mddev_t *mddev)
if (atomic_dec_and_test(&mddev->writes_pending)) {
if (mddev->safemode == 2)
md_wakeup_thread(mddev->thread);
- else
+ else if (mddev->safemode_delay)
mod_timer(&mddev->safemode_timer, jiffies + mddev->safemode_delay);
}
}
@@ -4620,10 +5059,14 @@ void md_do_sync(mddev_t *mddev)
struct list_head *tmp;
sector_t last_check;
int skipped = 0;
+ struct list_head *rtmp;
+ mdk_rdev_t *rdev;
/* just incase thread restarts... */
if (test_bit(MD_RECOVERY_DONE, &mddev->recovery))
return;
+ if (mddev->ro) /* never try to sync a read-only array */
+ return;
/* we overload curr_resync somewhat here.
* 0 == not engaged in resync at all
@@ -4682,17 +5125,30 @@ void md_do_sync(mddev_t *mddev)
}
} while (mddev->curr_resync < 2);
+ j = 0;
if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
/* resync follows the size requested by the personality,
* which defaults to physical size, but can be virtual size
*/
max_sectors = mddev->resync_max_sectors;
mddev->resync_mismatches = 0;
+ /* we don't use the checkpoint if there's a bitmap */
+ if (!mddev->bitmap &&
+ !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+ j = mddev->recovery_cp;
} else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
max_sectors = mddev->size << 1;
- else
+ else {
/* recovery follows the physical size of devices */
max_sectors = mddev->size << 1;
+ j = MaxSector;
+ ITERATE_RDEV(mddev,rdev,rtmp)
+ if (rdev->raid_disk >= 0 &&
+ !test_bit(Faulty, &rdev->flags) &&
+ !test_bit(In_sync, &rdev->flags) &&
+ rdev->recovery_offset < j)
+ j = rdev->recovery_offset;
+ }
printk(KERN_INFO "md: syncing RAID array %s\n", mdname(mddev));
printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed:"
@@ -4702,12 +5158,7 @@ void md_do_sync(mddev_t *mddev)
speed_max(mddev));
is_mddev_idle(mddev); /* this also initializes IO event counters */
- /* we don't use the checkpoint if there's a bitmap */
- if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) && !mddev->bitmap
- && ! test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
- j = mddev->recovery_cp;
- else
- j = 0;
+
io_sectors = 0;
for (m = 0; m < SYNC_MARKS; m++) {
mark[m] = jiffies;
@@ -4828,15 +5279,28 @@ void md_do_sync(mddev_t *mddev)
if (!test_bit(MD_RECOVERY_ERR, &mddev->recovery) &&
test_bit(MD_RECOVERY_SYNC, &mddev->recovery) &&
!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) &&
- mddev->curr_resync > 2 &&
- mddev->curr_resync >= mddev->recovery_cp) {
- if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
- printk(KERN_INFO
- "md: checkpointing recovery of %s.\n",
- mdname(mddev));
- mddev->recovery_cp = mddev->curr_resync;
- } else
- mddev->recovery_cp = MaxSector;
+ mddev->curr_resync > 2) {
+ if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+ if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) {
+ if (mddev->curr_resync >= mddev->recovery_cp) {
+ printk(KERN_INFO
+ "md: checkpointing recovery of %s.\n",
+ mdname(mddev));
+ mddev->recovery_cp = mddev->curr_resync;
+ }
+ } else
+ mddev->recovery_cp = MaxSector;
+ } else {
+ if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery))
+ mddev->curr_resync = MaxSector;
+ ITERATE_RDEV(mddev,rdev,rtmp)
+ if (rdev->raid_disk >= 0 &&
+ !test_bit(Faulty, &rdev->flags) &&
+ !test_bit(In_sync, &rdev->flags) &&
+ rdev->recovery_offset < mddev->curr_resync)
+ rdev->recovery_offset = mddev->curr_resync;
+ mddev->sb_dirty = 1;
+ }
}
skip:
@@ -4908,7 +5372,7 @@ void md_check_recovery(mddev_t *mddev)
if (mddev->safemode && !atomic_read(&mddev->writes_pending) &&
!mddev->in_sync && mddev->recovery_cp == MaxSector) {
mddev->in_sync = 1;
- mddev->sb_dirty = 1;
+ mddev->sb_dirty = 3;
}
if (mddev->safemode == 1)
mddev->safemode = 0;
@@ -4957,6 +5421,8 @@ void md_check_recovery(mddev_t *mddev)
clear_bit(MD_RECOVERY_INTR, &mddev->recovery);
clear_bit(MD_RECOVERY_DONE, &mddev->recovery);
+ if (test_bit(MD_RECOVERY_FROZEN, &mddev->recovery))
+ goto unlock;
/* no recovery is running.
* remove any failed drives, then
* add spares if possible.
@@ -4979,6 +5445,7 @@ void md_check_recovery(mddev_t *mddev)
ITERATE_RDEV(mddev,rdev,rtmp)
if (rdev->raid_disk < 0
&& !test_bit(Faulty, &rdev->flags)) {
+ rdev->recovery_offset = 0;
if (mddev->pers->hot_add_disk(mddev,rdev)) {
char nm[20];
sprintf(nm, "rd%d", rdev->raid_disk);
@@ -5216,7 +5683,6 @@ EXPORT_SYMBOL(md_write_end);
EXPORT_SYMBOL(md_register_thread);
EXPORT_SYMBOL(md_unregister_thread);
EXPORT_SYMBOL(md_wakeup_thread);
-EXPORT_SYMBOL(md_print_devices);
EXPORT_SYMBOL(md_check_recovery);
MODULE_LICENSE("GPL");
MODULE_ALIAS("md");
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 4070eff6f0f8..cead918578a7 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -374,26 +374,26 @@ static int raid1_end_write_request(struct bio *bio, unsigned int bytes_done, int
* already.
*/
if (atomic_dec_and_test(&r1_bio->remaining)) {
- if (test_bit(R1BIO_BarrierRetry, &r1_bio->state)) {
+ if (test_bit(R1BIO_BarrierRetry, &r1_bio->state))
reschedule_retry(r1_bio);
- goto out;
- }
- /* it really is the end of this request */
- if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
- /* free extra copy of the data pages */
- int i = bio->bi_vcnt;
- while (i--)
- safe_put_page(bio->bi_io_vec[i].bv_page);
+ else {
+ /* it really is the end of this request */
+ if (test_bit(R1BIO_BehindIO, &r1_bio->state)) {
+ /* free extra copy of the data pages */
+ int i = bio->bi_vcnt;
+ while (i--)
+ safe_put_page(bio->bi_io_vec[i].bv_page);
+ }
+ /* clear the bitmap if all writes complete successfully */
+ bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
+ r1_bio->sectors,
+ !test_bit(R1BIO_Degraded, &r1_bio->state),
+ behind);
+ md_write_end(r1_bio->mddev);
+ raid_end_bio_io(r1_bio);
}
- /* clear the bitmap if all writes complete successfully */
- bitmap_endwrite(r1_bio->mddev->bitmap, r1_bio->sector,
- r1_bio->sectors,
- !test_bit(R1BIO_Degraded, &r1_bio->state),
- behind);
- md_write_end(r1_bio->mddev);
- raid_end_bio_io(r1_bio);
}
- out:
+
if (to_put)
bio_put(to_put);
@@ -1625,6 +1625,12 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
/* before building a request, check if we can skip these blocks..
* This call the bitmap_start_sync doesn't actually record anything
*/
+ if (mddev->bitmap == NULL &&
+ mddev->recovery_cp == MaxSector &&
+ conf->fullsync == 0) {
+ *skipped = 1;
+ return max_sector - sector_nr;
+ }
if (!bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, 1) &&
!conf->fullsync && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) {
/* We can skip this block, and probably several more */
@@ -1888,7 +1894,8 @@ static int run(mddev_t *mddev)
disk = conf->mirrors + i;
- if (!disk->rdev) {
+ if (!disk->rdev ||
+ !test_bit(In_sync, &disk->rdev->flags)) {
disk->head_position = 0;
mddev->degraded++;
}
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 1440935414e6..7f636283a1ba 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -29,6 +29,7 @@
* raid_disks
* near_copies (stored in low byte of layout)
* far_copies (stored in second byte of layout)
+ * far_offset (stored in bit 16 of layout )
*
* The data to be stored is divided into chunks using chunksize.
* Each device is divided into far_copies sections.
@@ -36,10 +37,14 @@
* near_copies copies of each chunk is stored (each on a different drive).
* The starting device for each section is offset near_copies from the starting
* device of the previous section.
- * Thus there are (near_copies*far_copies) of each chunk, and each is on a different
+ * Thus they are (near_copies*far_copies) of each chunk, and each is on a different
* drive.
* near_copies and far_copies must be at least one, and their product is at most
* raid_disks.
+ *
+ * If far_offset is true, then the far_copies are handled a bit differently.
+ * The copies are still in different stripes, but instead of be very far apart
+ * on disk, there are adjacent stripes.
*/
/*
@@ -357,8 +362,7 @@ static int raid10_end_write_request(struct bio *bio, unsigned int bytes_done, in
* With this layout, and block is never stored twice on the one device.
*
* raid10_find_phys finds the sector offset of a given virtual sector
- * on each device that it is on. If a block isn't on a device,
- * that entry in the array is set to MaxSector.
+ * on each device that it is on.
*
* raid10_find_virt does the reverse mapping, from a device and a
* sector offset to a virtual address
@@ -381,6 +385,8 @@ static void raid10_find_phys(conf_t *conf, r10bio_t *r10bio)
chunk *= conf->near_copies;
stripe = chunk;
dev = sector_div(stripe, conf->raid_disks);
+ if (conf->far_offset)
+ stripe *= conf->far_copies;
sector += stripe << conf->chunk_shift;
@@ -414,16 +420,24 @@ static sector_t raid10_find_virt(conf_t *conf, sector_t sector, int dev)
{
sector_t offset, chunk, vchunk;
- while (sector > conf->stride) {
- sector -= conf->stride;
- if (dev < conf->near_copies)
- dev += conf->raid_disks - conf->near_copies;
- else
- dev -= conf->near_copies;
- }
-
offset = sector & conf->chunk_mask;
- chunk = sector >> conf->chunk_shift;
+ if (conf->far_offset) {
+ int fc;
+ chunk = sector >> conf->chunk_shift;
+ fc = sector_div(chunk, conf->far_copies);
+ dev -= fc * conf->near_copies;
+ if (dev < 0)
+ dev += conf->raid_disks;
+ } else {
+ while (sector > conf->stride) {
+ sector -= conf->stride;
+ if (dev < conf->near_copies)
+ dev += conf->raid_disks - conf->near_copies;
+ else
+ dev -= conf->near_copies;
+ }
+ chunk = sector >> conf->chunk_shift;
+ }
vchunk = chunk * conf->raid_disks + dev;
sector_div(vchunk, conf->near_copies);
return (vchunk << conf->chunk_shift) + offset;
@@ -900,9 +914,12 @@ static void status(struct seq_file *seq, mddev_t *mddev)
seq_printf(seq, " %dK chunks", mddev->chunk_size/1024);
if (conf->near_copies > 1)
seq_printf(seq, " %d near-copies", conf->near_copies);
- if (conf->far_copies > 1)
- seq_printf(seq, " %d far-copies", conf->far_copies);
-
+ if (conf->far_copies > 1) {
+ if (conf->far_offset)
+ seq_printf(seq, " %d offset-copies", conf->far_copies);
+ else
+ seq_printf(seq, " %d far-copies", conf->far_copies);
+ }
seq_printf(seq, " [%d/%d] [", conf->raid_disks,
conf->working_disks);
for (i = 0; i < conf->raid_disks; i++)
@@ -1915,7 +1932,7 @@ static int run(mddev_t *mddev)
mirror_info_t *disk;
mdk_rdev_t *rdev;
struct list_head *tmp;
- int nc, fc;
+ int nc, fc, fo;
sector_t stride, size;
if (mddev->chunk_size == 0) {
@@ -1925,8 +1942,9 @@ static int run(mddev_t *mddev)
nc = mddev->layout & 255;
fc = (mddev->layout >> 8) & 255;
+ fo = mddev->layout & (1<<16);
if ((nc*fc) <2 || (nc*fc) > mddev->raid_disks ||
- (mddev->layout >> 16)) {
+ (mddev->layout >> 17)) {
printk(KERN_ERR "raid10: %s: unsupported raid10 layout: 0x%8x\n",
mdname(mddev), mddev->layout);
goto out;
@@ -1958,12 +1976,16 @@ static int run(mddev_t *mddev)
conf->near_copies = nc;
conf->far_copies = fc;
conf->copies = nc*fc;
+ conf->far_offset = fo;
conf->chunk_mask = (sector_t)(mddev->chunk_size>>9)-1;
conf->chunk_shift = ffz(~mddev->chunk_size) - 9;
- stride = mddev->size >> (conf->chunk_shift-1);
- sector_div(stride, fc);
- conf->stride = stride << conf->chunk_shift;
-
+ if (fo)
+ conf->stride = 1 << conf->chunk_shift;
+ else {
+ stride = mddev->size >> (conf->chunk_shift-1);
+ sector_div(stride, fc);
+ conf->stride = stride << conf->chunk_shift;
+ }
conf->r10bio_pool = mempool_create(NR_RAID10_BIOS, r10bio_pool_alloc,
r10bio_pool_free, conf);
if (!conf->r10bio_pool) {
@@ -2015,7 +2037,8 @@ static int run(mddev_t *mddev)
disk = conf->mirrors + i;
- if (!disk->rdev) {
+ if (!disk->rdev ||
+ !test_bit(In_sync, &rdev->flags)) {
disk->head_position = 0;
mddev->degraded++;
}
@@ -2037,7 +2060,13 @@ static int run(mddev_t *mddev)
/*
* Ok, everything is just fine now
*/
- size = conf->stride * conf->raid_disks;
+ if (conf->far_offset) {
+ size = mddev->size >> (conf->chunk_shift-1);
+ size *= conf->raid_disks;
+ size <<= conf->chunk_shift;
+ sector_div(size, conf->far_copies);
+ } else
+ size = conf->stride * conf->raid_disks;
sector_div(size, conf->near_copies);
mddev->array_size = size/2;
mddev->resync_max_sectors = size;
@@ -2050,7 +2079,7 @@ static int run(mddev_t *mddev)
* maybe...
*/
{
- int stripe = conf->raid_disks * mddev->chunk_size / PAGE_SIZE;
+ int stripe = conf->raid_disks * (mddev->chunk_size / PAGE_SIZE);
stripe /= conf->near_copies;
if (mddev->queue->backing_dev_info.ra_pages < 2* stripe)
mddev->queue->backing_dev_info.ra_pages = 2* stripe;
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 31843604049c..837ec4eb3d60 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -2,8 +2,11 @@
* raid5.c : Multiple Devices driver for Linux
* Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
* Copyright (C) 1999, 2000 Ingo Molnar
+ * Copyright (C) 2002, 2003 H. Peter Anvin
*
- * RAID-5 management functions.
+ * RAID-4/5/6 management functions.
+ * Thanks to Penguin Computing for making the RAID-6 development possible
+ * by donating a test server!
*
* 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
@@ -19,11 +22,11 @@
#include <linux/config.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/raid/raid5.h>
#include <linux/highmem.h>
#include <linux/bitops.h>
#include <linux/kthread.h>
#include <asm/atomic.h>
+#include "raid6.h"
#include <linux/raid/bitmap.h>
@@ -68,6 +71,16 @@
#define __inline__
#endif
+#if !RAID6_USE_EMPTY_ZERO_PAGE
+/* In .bss so it's zeroed */
+const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
+#endif
+
+static inline int raid6_next_disk(int disk, int raid_disks)
+{
+ disk++;
+ return (disk < raid_disks) ? disk : 0;
+}
static void print_raid5_conf (raid5_conf_t *conf);
static void __release_stripe(raid5_conf_t *conf, struct stripe_head *sh)
@@ -104,7 +117,7 @@ static void release_stripe(struct stripe_head *sh)
{
raid5_conf_t *conf = sh->raid_conf;
unsigned long flags;
-
+
spin_lock_irqsave(&conf->device_lock, flags);
__release_stripe(conf, sh);
spin_unlock_irqrestore(&conf->device_lock, flags);
@@ -117,7 +130,7 @@ static inline void remove_hash(struct stripe_head *sh)
hlist_del_init(&sh->hash);
}
-static void insert_hash(raid5_conf_t *conf, struct stripe_head *sh)
+static inline void insert_hash(raid5_conf_t *conf, struct stripe_head *sh)
{
struct hlist_head *hp = stripe_hash(conf, sh->sector);
@@ -190,7 +203,7 @@ static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx, int
(unsigned long long)sh->sector);
remove_hash(sh);
-
+
sh->sector = sector;
sh->pd_idx = pd_idx;
sh->state = 0;
@@ -269,8 +282,9 @@ static struct stripe_head *get_active_stripe(raid5_conf_t *conf, sector_t sector
} else {
if (!test_bit(STRIPE_HANDLE, &sh->state))
atomic_inc(&conf->active_stripes);
- if (!list_empty(&sh->lru))
- list_del_init(&sh->lru);
+ if (list_empty(&sh->lru))
+ BUG();
+ list_del_init(&sh->lru);
}
}
} while (sh == NULL);
@@ -321,10 +335,9 @@ static int grow_stripes(raid5_conf_t *conf, int num)
return 1;
conf->slab_cache = sc;
conf->pool_size = devs;
- while (num--) {
+ while (num--)
if (!grow_one_stripe(conf))
return 1;
- }
return 0;
}
@@ -631,8 +644,7 @@ static void raid5_build_block (struct stripe_head *sh, int i)
dev->req.bi_private = sh;
dev->flags = 0;
- if (i != sh->pd_idx)
- dev->sector = compute_blocknr(sh, i);
+ dev->sector = compute_blocknr(sh, i);
}
static void error(mddev_t *mddev, mdk_rdev_t *rdev)
@@ -659,7 +671,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
" Operation continuing on %d devices\n",
bdevname(rdev->bdev,b), conf->working_disks);
}
-}
+}
/*
* Input: a 'big' sector number,
@@ -697,9 +709,12 @@ static sector_t raid5_compute_sector(sector_t r_sector, unsigned int raid_disks,
/*
* Select the parity disk based on the user selected algorithm.
*/
- if (conf->level == 4)
+ switch(conf->level) {
+ case 4:
*pd_idx = data_disks;
- else switch (conf->algorithm) {
+ break;
+ case 5:
+ switch (conf->algorithm) {
case ALGORITHM_LEFT_ASYMMETRIC:
*pd_idx = data_disks - stripe % raid_disks;
if (*dd_idx >= *pd_idx)
@@ -721,6 +736,39 @@ static sector_t raid5_compute_sector(sector_t r_sector, unsigned int raid_disks,
default:
printk(KERN_ERR "raid5: unsupported algorithm %d\n",
conf->algorithm);
+ }
+ break;
+ case 6:
+
+ /**** FIX THIS ****/
+ switch (conf->algorithm) {
+ case ALGORITHM_LEFT_ASYMMETRIC:
+ *pd_idx = raid_disks - 1 - (stripe % raid_disks);
+ if (*pd_idx == raid_disks-1)
+ (*dd_idx)++; /* Q D D D P */
+ else if (*dd_idx >= *pd_idx)
+ (*dd_idx) += 2; /* D D P Q D */
+ break;
+ case ALGORITHM_RIGHT_ASYMMETRIC:
+ *pd_idx = stripe % raid_disks;
+ if (*pd_idx == raid_disks-1)
+ (*dd_idx)++; /* Q D D D P */
+ else if (*dd_idx >= *pd_idx)
+ (*dd_idx) += 2; /* D D P Q D */
+ break;
+ case ALGORITHM_LEFT_SYMMETRIC:
+ *pd_idx = raid_disks - 1 - (stripe % raid_disks);
+ *dd_idx = (*pd_idx + 2 + *dd_idx) % raid_disks;
+ break;
+ case ALGORITHM_RIGHT_SYMMETRIC:
+ *pd_idx = stripe % raid_disks;
+ *dd_idx = (*pd_idx + 2 + *dd_idx) % raid_disks;
+ break;
+ default:
+ printk (KERN_CRIT "raid6: unsupported algorithm %d\n",
+ conf->algorithm);
+ }
+ break;
}
/*
@@ -742,12 +790,17 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i)
int chunk_number, dummy1, dummy2, dd_idx = i;
sector_t r_sector;
+
chunk_offset = sector_div(new_sector, sectors_per_chunk);
stripe = new_sector;
BUG_ON(new_sector != stripe);
-
- switch (conf->algorithm) {
+ if (i == sh->pd_idx)
+ return 0;
+ switch(conf->level) {
+ case 4: break;
+ case 5:
+ switch (conf->algorithm) {
case ALGORITHM_LEFT_ASYMMETRIC:
case ALGORITHM_RIGHT_ASYMMETRIC:
if (i > sh->pd_idx)
@@ -761,7 +814,37 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i)
break;
default:
printk(KERN_ERR "raid5: unsupported algorithm %d\n",
+ conf->algorithm);
+ }
+ break;
+ case 6:
+ data_disks = raid_disks - 2;
+ if (i == raid6_next_disk(sh->pd_idx, raid_disks))
+ return 0; /* It is the Q disk */
+ switch (conf->algorithm) {
+ case ALGORITHM_LEFT_ASYMMETRIC:
+ case ALGORITHM_RIGHT_ASYMMETRIC:
+ if (sh->pd_idx == raid_disks-1)
+ i--; /* Q D D D P */
+ else if (i > sh->pd_idx)
+ i -= 2; /* D D P Q D */
+ break;
+ case ALGORITHM_LEFT_SYMMETRIC:
+ case ALGORITHM_RIGHT_SYMMETRIC:
+ if (sh->pd_idx == raid_disks-1)
+ i--; /* Q D D D P */
+ else {
+ /* D D P Q D */
+ if (i < sh->pd_idx)
+ i += raid_disks;
+ i -= (sh->pd_idx + 2);
+ }
+ break;
+ default:
+ printk (KERN_CRIT "raid6: unsupported algorithm %d\n",
conf->algorithm);
+ }
+ break;
}
chunk_number = stripe * data_disks + i;
@@ -778,10 +861,11 @@ static sector_t compute_blocknr(struct stripe_head *sh, int i)
/*
- * Copy data between a page in the stripe cache, and a bio.
- * There are no alignment or size guarantees between the page or the
- * bio except that there is some overlap.
- * All iovecs in the bio must be considered.
+ * Copy data between a page in the stripe cache, and one or more bion
+ * The page could align with the middle of the bio, or there could be
+ * several bion, each with several bio_vecs, which cover part of the page
+ * Multiple bion are linked together on bi_next. There may be extras
+ * at the end of this list. We ignore them.
*/
static void copy_data(int frombio, struct bio *bio,
struct page *page,
@@ -810,7 +894,7 @@ static void copy_data(int frombio, struct bio *bio,
if (len > 0 && page_offset + len > STRIPE_SIZE)
clen = STRIPE_SIZE - page_offset;
else clen = len;
-
+
if (clen > 0) {
char *ba = __bio_kmap_atomic(bio, i, KM_USER0);
if (frombio)
@@ -862,14 +946,14 @@ static void compute_block(struct stripe_head *sh, int dd_idx)
set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
}
-static void compute_parity(struct stripe_head *sh, int method)
+static void compute_parity5(struct stripe_head *sh, int method)
{
raid5_conf_t *conf = sh->raid_conf;
int i, pd_idx = sh->pd_idx, disks = sh->disks, count;
void *ptr[MAX_XOR_BLOCKS];
struct bio *chosen;
- PRINTK("compute_parity, stripe %llu, method %d\n",
+ PRINTK("compute_parity5, stripe %llu, method %d\n",
(unsigned long long)sh->sector, method);
count = 1;
@@ -956,9 +1040,195 @@ static void compute_parity(struct stripe_head *sh, int method)
clear_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
}
+static void compute_parity6(struct stripe_head *sh, int method)
+{
+ raid6_conf_t *conf = sh->raid_conf;
+ int i, pd_idx = sh->pd_idx, qd_idx, d0_idx, disks = conf->raid_disks, count;
+ struct bio *chosen;
+ /**** FIX THIS: This could be very bad if disks is close to 256 ****/
+ void *ptrs[disks];
+
+ qd_idx = raid6_next_disk(pd_idx, disks);
+ d0_idx = raid6_next_disk(qd_idx, disks);
+
+ PRINTK("compute_parity, stripe %llu, method %d\n",
+ (unsigned long long)sh->sector, method);
+
+ switch(method) {
+ case READ_MODIFY_WRITE:
+ BUG(); /* READ_MODIFY_WRITE N/A for RAID-6 */
+ case RECONSTRUCT_WRITE:
+ for (i= disks; i-- ;)
+ if ( i != pd_idx && i != qd_idx && sh->dev[i].towrite ) {
+ chosen = sh->dev[i].towrite;
+ sh->dev[i].towrite = NULL;
+
+ if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
+ wake_up(&conf->wait_for_overlap);
+
+ if (sh->dev[i].written) BUG();
+ sh->dev[i].written = chosen;
+ }
+ break;
+ case CHECK_PARITY:
+ BUG(); /* Not implemented yet */
+ }
+
+ for (i = disks; i--;)
+ if (sh->dev[i].written) {
+ sector_t sector = sh->dev[i].sector;
+ struct bio *wbi = sh->dev[i].written;
+ while (wbi && wbi->bi_sector < sector + STRIPE_SECTORS) {
+ copy_data(1, wbi, sh->dev[i].page, sector);
+ wbi = r5_next_bio(wbi, sector);
+ }
+
+ set_bit(R5_LOCKED, &sh->dev[i].flags);
+ set_bit(R5_UPTODATE, &sh->dev[i].flags);
+ }
+
+// switch(method) {
+// case RECONSTRUCT_WRITE:
+// case CHECK_PARITY:
+// case UPDATE_PARITY:
+ /* Note that unlike RAID-5, the ordering of the disks matters greatly. */
+ /* FIX: Is this ordering of drives even remotely optimal? */
+ count = 0;
+ i = d0_idx;
+ do {
+ ptrs[count++] = page_address(sh->dev[i].page);
+ if (count <= disks-2 && !test_bit(R5_UPTODATE, &sh->dev[i].flags))
+ printk("block %d/%d not uptodate on parity calc\n", i,count);
+ i = raid6_next_disk(i, disks);
+ } while ( i != d0_idx );
+// break;
+// }
+
+ raid6_call.gen_syndrome(disks, STRIPE_SIZE, ptrs);
+
+ switch(method) {
+ case RECONSTRUCT_WRITE:
+ set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
+ set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags);
+ set_bit(R5_LOCKED, &sh->dev[pd_idx].flags);
+ set_bit(R5_LOCKED, &sh->dev[qd_idx].flags);
+ break;
+ case UPDATE_PARITY:
+ set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
+ set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags);
+ break;
+ }
+}
+
+
+/* Compute one missing block */
+static void compute_block_1(struct stripe_head *sh, int dd_idx, int nozero)
+{
+ raid6_conf_t *conf = sh->raid_conf;
+ int i, count, disks = conf->raid_disks;
+ void *ptr[MAX_XOR_BLOCKS], *p;
+ int pd_idx = sh->pd_idx;
+ int qd_idx = raid6_next_disk(pd_idx, disks);
+
+ PRINTK("compute_block_1, stripe %llu, idx %d\n",
+ (unsigned long long)sh->sector, dd_idx);
+
+ if ( dd_idx == qd_idx ) {
+ /* We're actually computing the Q drive */
+ compute_parity6(sh, UPDATE_PARITY);
+ } else {
+ ptr[0] = page_address(sh->dev[dd_idx].page);
+ if (!nozero) memset(ptr[0], 0, STRIPE_SIZE);
+ count = 1;
+ for (i = disks ; i--; ) {
+ if (i == dd_idx || i == qd_idx)
+ continue;
+ p = page_address(sh->dev[i].page);
+ if (test_bit(R5_UPTODATE, &sh->dev[i].flags))
+ ptr[count++] = p;
+ else
+ printk("compute_block() %d, stripe %llu, %d"
+ " not present\n", dd_idx,
+ (unsigned long long)sh->sector, i);
+
+ check_xor();
+ }
+ if (count != 1)
+ xor_block(count, STRIPE_SIZE, ptr);
+ if (!nozero) set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
+ else clear_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
+ }
+}
+
+/* Compute two missing blocks */
+static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2)
+{
+ raid6_conf_t *conf = sh->raid_conf;
+ int i, count, disks = conf->raid_disks;
+ int pd_idx = sh->pd_idx;
+ int qd_idx = raid6_next_disk(pd_idx, disks);
+ int d0_idx = raid6_next_disk(qd_idx, disks);
+ int faila, failb;
+
+ /* faila and failb are disk numbers relative to d0_idx */
+ /* pd_idx become disks-2 and qd_idx become disks-1 */
+ faila = (dd_idx1 < d0_idx) ? dd_idx1+(disks-d0_idx) : dd_idx1-d0_idx;
+ failb = (dd_idx2 < d0_idx) ? dd_idx2+(disks-d0_idx) : dd_idx2-d0_idx;
+
+ BUG_ON(faila == failb);
+ if ( failb < faila ) { int tmp = faila; faila = failb; failb = tmp; }
+
+ PRINTK("compute_block_2, stripe %llu, idx %d,%d (%d,%d)\n",
+ (unsigned long long)sh->sector, dd_idx1, dd_idx2, faila, failb);
+
+ if ( failb == disks-1 ) {
+ /* Q disk is one of the missing disks */
+ if ( faila == disks-2 ) {
+ /* Missing P+Q, just recompute */
+ compute_parity6(sh, UPDATE_PARITY);
+ return;
+ } else {
+ /* We're missing D+Q; recompute D from P */
+ compute_block_1(sh, (dd_idx1 == qd_idx) ? dd_idx2 : dd_idx1, 0);
+ compute_parity6(sh, UPDATE_PARITY); /* Is this necessary? */
+ return;
+ }
+ }
+
+ /* We're missing D+P or D+D; build pointer table */
+ {
+ /**** FIX THIS: This could be very bad if disks is close to 256 ****/
+ void *ptrs[disks];
+
+ count = 0;
+ i = d0_idx;
+ do {
+ ptrs[count++] = page_address(sh->dev[i].page);
+ i = raid6_next_disk(i, disks);
+ if (i != dd_idx1 && i != dd_idx2 &&
+ !test_bit(R5_UPTODATE, &sh->dev[i].flags))
+ printk("compute_2 with missing block %d/%d\n", count, i);
+ } while ( i != d0_idx );
+
+ if ( failb == disks-2 ) {
+ /* We're missing D+P. */
+ raid6_datap_recov(disks, STRIPE_SIZE, faila, ptrs);
+ } else {
+ /* We're missing D+D. */
+ raid6_2data_recov(disks, STRIPE_SIZE, faila, failb, ptrs);
+ }
+
+ /* Both the above update both missing blocks */
+ set_bit(R5_UPTODATE, &sh->dev[dd_idx1].flags);
+ set_bit(R5_UPTODATE, &sh->dev[dd_idx2].flags);
+ }
+}
+
+
+
/*
* Each stripe/dev can have one or more bion attached.
- * toread/towrite point to the first in a chain.
+ * toread/towrite point to the first in a chain.
* The bi_next chain must be in order.
*/
static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, int forwrite)
@@ -1031,6 +1301,13 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, in
static void end_reshape(raid5_conf_t *conf);
+static int page_is_zero(struct page *p)
+{
+ char *a = page_address(p);
+ return ((*(u32*)a) == 0 &&
+ memcmp(a, a+4, STRIPE_SIZE-4)==0);
+}
+
static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks)
{
int sectors_per_chunk = conf->chunk_size >> 9;
@@ -1062,7 +1339,7 @@ static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks)
*
*/
-static void handle_stripe(struct stripe_head *sh)
+static void handle_stripe5(struct stripe_head *sh)
{
raid5_conf_t *conf = sh->raid_conf;
int disks = sh->disks;
@@ -1394,7 +1671,7 @@ static void handle_stripe(struct stripe_head *sh)
if (locked == 0 && (rcw == 0 ||rmw == 0) &&
!test_bit(STRIPE_BIT_DELAY, &sh->state)) {
PRINTK("Computing parity...\n");
- compute_parity(sh, rcw==0 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
+ compute_parity5(sh, rcw==0 ? RECONSTRUCT_WRITE : READ_MODIFY_WRITE);
/* now every locked buffer is ready to be written */
for (i=disks; i--;)
if (test_bit(R5_LOCKED, &sh->dev[i].flags)) {
@@ -1421,13 +1698,10 @@ static void handle_stripe(struct stripe_head *sh)
!test_bit(STRIPE_INSYNC, &sh->state)) {
set_bit(STRIPE_HANDLE, &sh->state);
if (failed == 0) {
- char *pagea;
BUG_ON(uptodate != disks);
- compute_parity(sh, CHECK_PARITY);
+ compute_parity5(sh, CHECK_PARITY);
uptodate--;
- pagea = page_address(sh->dev[sh->pd_idx].page);
- if ((*(u32*)pagea) == 0 &&
- !memcmp(pagea, pagea+4, STRIPE_SIZE-4)) {
+ if (page_is_zero(sh->dev[sh->pd_idx].page)) {
/* parity is correct (on disc, not in buffer any more) */
set_bit(STRIPE_INSYNC, &sh->state);
} else {
@@ -1487,7 +1761,7 @@ static void handle_stripe(struct stripe_head *sh)
/* Need to write out all blocks after computing parity */
sh->disks = conf->raid_disks;
sh->pd_idx = stripe_to_pdidx(sh->sector, conf, conf->raid_disks);
- compute_parity(sh, RECONSTRUCT_WRITE);
+ compute_parity5(sh, RECONSTRUCT_WRITE);
for (i= conf->raid_disks; i--;) {
set_bit(R5_LOCKED, &sh->dev[i].flags);
locked++;
@@ -1615,6 +1889,569 @@ static void handle_stripe(struct stripe_head *sh)
}
}
+static void handle_stripe6(struct stripe_head *sh, struct page *tmp_page)
+{
+ raid6_conf_t *conf = sh->raid_conf;
+ int disks = conf->raid_disks;
+ struct bio *return_bi= NULL;
+ struct bio *bi;
+ int i;
+ int syncing;
+ int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0;
+ int non_overwrite = 0;
+ int failed_num[2] = {0, 0};
+ struct r5dev *dev, *pdev, *qdev;
+ int pd_idx = sh->pd_idx;
+ int qd_idx = raid6_next_disk(pd_idx, disks);
+ int p_failed, q_failed;
+
+ PRINTK("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d, qd_idx=%d\n",
+ (unsigned long long)sh->sector, sh->state, atomic_read(&sh->count),
+ pd_idx, qd_idx);
+
+ spin_lock(&sh->lock);
+ clear_bit(STRIPE_HANDLE, &sh->state);
+ clear_bit(STRIPE_DELAYED, &sh->state);
+
+ syncing = test_bit(STRIPE_SYNCING, &sh->state);
+ /* Now to look around and see what can be done */
+
+ rcu_read_lock();
+ for (i=disks; i--; ) {
+ mdk_rdev_t *rdev;
+ dev = &sh->dev[i];
+ clear_bit(R5_Insync, &dev->flags);
+
+ PRINTK("check %d: state 0x%lx read %p write %p written %p\n",
+ i, dev->flags, dev->toread, dev->towrite, dev->written);
+ /* maybe we can reply to a read */
+ if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread) {
+ struct bio *rbi, *rbi2;
+ PRINTK("Return read for disc %d\n", i);
+ spin_lock_irq(&conf->device_lock);
+ rbi = dev->toread;
+ dev->toread = NULL;
+ if (test_and_clear_bit(R5_Overlap, &dev->flags))
+ wake_up(&conf->wait_for_overlap);
+ spin_unlock_irq(&conf->device_lock);
+ while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) {
+ copy_data(0, rbi, dev->page, dev->sector);
+ rbi2 = r5_next_bio(rbi, dev->sector);
+ spin_lock_irq(&conf->device_lock);
+ if (--rbi->bi_phys_segments == 0) {
+ rbi->bi_next = return_bi;
+ return_bi = rbi;
+ }
+ spin_unlock_irq(&conf->device_lock);
+ rbi = rbi2;
+ }
+ }
+
+ /* now count some things */
+ if (test_bit(R5_LOCKED, &dev->flags)) locked++;
+ if (test_bit(R5_UPTODATE, &dev->flags)) uptodate++;
+
+
+ if (dev->toread) to_read++;
+ if (dev->towrite) {
+ to_write++;
+ if (!test_bit(R5_OVERWRITE, &dev->flags))
+ non_overwrite++;
+ }
+ if (dev->written) written++;
+ rdev = rcu_dereference(conf->disks[i].rdev);
+ if (!rdev || !test_bit(In_sync, &rdev->flags)) {
+ /* The ReadError flag will just be confusing now */
+ clear_bit(R5_ReadError, &dev->flags);
+ clear_bit(R5_ReWrite, &dev->flags);
+ }
+ if (!rdev || !test_bit(In_sync, &rdev->flags)
+ || test_bit(R5_ReadError, &dev->flags)) {
+ if ( failed < 2 )
+ failed_num[failed] = i;
+ failed++;
+ } else
+ set_bit(R5_Insync, &dev->flags);
+ }
+ rcu_read_unlock();
+ PRINTK("locked=%d uptodate=%d to_read=%d"
+ " to_write=%d failed=%d failed_num=%d,%d\n",
+ locked, uptodate, to_read, to_write, failed,
+ failed_num[0], failed_num[1]);
+ /* check if the array has lost >2 devices and, if so, some requests might
+ * need to be failed
+ */
+ if (failed > 2 && to_read+to_write+written) {
+ for (i=disks; i--; ) {
+ int bitmap_end = 0;
+
+ if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
+ mdk_rdev_t *rdev;
+ rcu_read_lock();
+ rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(In_sync, &rdev->flags))
+ /* multiple read failures in one stripe */
+ md_error(conf->mddev, rdev);
+ rcu_read_unlock();
+ }
+
+ spin_lock_irq(&conf->device_lock);
+ /* fail all writes first */
+ bi = sh->dev[i].towrite;
+ sh->dev[i].towrite = NULL;
+ if (bi) { to_write--; bitmap_end = 1; }
+
+ if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
+ wake_up(&conf->wait_for_overlap);
+
+ while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS){
+ struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
+ clear_bit(BIO_UPTODATE, &bi->bi_flags);
+ if (--bi->bi_phys_segments == 0) {
+ md_write_end(conf->mddev);
+ bi->bi_next = return_bi;
+ return_bi = bi;
+ }
+ bi = nextbi;
+ }
+ /* and fail all 'written' */
+ bi = sh->dev[i].written;
+ sh->dev[i].written = NULL;
+ if (bi) bitmap_end = 1;
+ while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS) {
+ struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector);
+ clear_bit(BIO_UPTODATE, &bi->bi_flags);
+ if (--bi->bi_phys_segments == 0) {
+ md_write_end(conf->mddev);
+ bi->bi_next = return_bi;
+ return_bi = bi;
+ }
+ bi = bi2;
+ }
+
+ /* fail any reads if this device is non-operational */
+ if (!test_bit(R5_Insync, &sh->dev[i].flags) ||
+ test_bit(R5_ReadError, &sh->dev[i].flags)) {
+ bi = sh->dev[i].toread;
+ sh->dev[i].toread = NULL;
+ if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
+ wake_up(&conf->wait_for_overlap);
+ if (bi) to_read--;
+ while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS){
+ struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
+ clear_bit(BIO_UPTODATE, &bi->bi_flags);
+ if (--bi->bi_phys_segments == 0) {
+ bi->bi_next = return_bi;
+ return_bi = bi;
+ }
+ bi = nextbi;
+ }
+ }
+ spin_unlock_irq(&conf->device_lock);
+ if (bitmap_end)
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS, 0, 0);
+ }
+ }
+ if (failed > 2 && syncing) {
+ md_done_sync(conf->mddev, STRIPE_SECTORS,0);
+ clear_bit(STRIPE_SYNCING, &sh->state);
+ syncing = 0;
+ }
+
+ /*
+ * might be able to return some write requests if the parity blocks
+ * are safe, or on a failed drive
+ */
+ pdev = &sh->dev[pd_idx];
+ p_failed = (failed >= 1 && failed_num[0] == pd_idx)
+ || (failed >= 2 && failed_num[1] == pd_idx);
+ qdev = &sh->dev[qd_idx];
+ q_failed = (failed >= 1 && failed_num[0] == qd_idx)
+ || (failed >= 2 && failed_num[1] == qd_idx);
+
+ if ( written &&
+ ( p_failed || ((test_bit(R5_Insync, &pdev->flags)
+ && !test_bit(R5_LOCKED, &pdev->flags)
+ && test_bit(R5_UPTODATE, &pdev->flags))) ) &&
+ ( q_failed || ((test_bit(R5_Insync, &qdev->flags)
+ && !test_bit(R5_LOCKED, &qdev->flags)
+ && test_bit(R5_UPTODATE, &qdev->flags))) ) ) {
+ /* any written block on an uptodate or failed drive can be
+ * returned. Note that if we 'wrote' to a failed drive,
+ * it will be UPTODATE, but never LOCKED, so we don't need
+ * to test 'failed' directly.
+ */
+ for (i=disks; i--; )
+ if (sh->dev[i].written) {
+ dev = &sh->dev[i];
+ if (!test_bit(R5_LOCKED, &dev->flags) &&
+ test_bit(R5_UPTODATE, &dev->flags) ) {
+ /* We can return any write requests */
+ int bitmap_end = 0;
+ struct bio *wbi, *wbi2;
+ PRINTK("Return write for stripe %llu disc %d\n",
+ (unsigned long long)sh->sector, i);
+ spin_lock_irq(&conf->device_lock);
+ wbi = dev->written;
+ dev->written = NULL;
+ while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) {
+ wbi2 = r5_next_bio(wbi, dev->sector);
+ if (--wbi->bi_phys_segments == 0) {
+ md_write_end(conf->mddev);
+ wbi->bi_next = return_bi;
+ return_bi = wbi;
+ }
+ wbi = wbi2;
+ }
+ if (dev->towrite == NULL)
+ bitmap_end = 1;
+ spin_unlock_irq(&conf->device_lock);
+ if (bitmap_end)
+ bitmap_endwrite(conf->mddev->bitmap, sh->sector,
+ STRIPE_SECTORS,
+ !test_bit(STRIPE_DEGRADED, &sh->state), 0);
+ }
+ }
+ }
+
+ /* Now we might consider reading some blocks, either to check/generate
+ * parity, or to satisfy requests
+ * or to load a block that is being partially written.
+ */
+ if (to_read || non_overwrite || (to_write && failed) || (syncing && (uptodate < disks))) {
+ for (i=disks; i--;) {
+ dev = &sh->dev[i];
+ if (!test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) &&
+ (dev->toread ||
+ (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
+ syncing ||
+ (failed >= 1 && (sh->dev[failed_num[0]].toread || to_write)) ||
+ (failed >= 2 && (sh->dev[failed_num[1]].toread || to_write))
+ )
+ ) {
+ /* we would like to get this block, possibly
+ * by computing it, but we might not be able to
+ */
+ if (uptodate == disks-1) {
+ PRINTK("Computing stripe %llu block %d\n",
+ (unsigned long long)sh->sector, i);
+ compute_block_1(sh, i, 0);
+ uptodate++;
+ } else if ( uptodate == disks-2 && failed >= 2 ) {
+ /* Computing 2-failure is *very* expensive; only do it if failed >= 2 */
+ int other;
+ for (other=disks; other--;) {
+ if ( other == i )
+ continue;
+ if ( !test_bit(R5_UPTODATE, &sh->dev[other].flags) )
+ break;
+ }
+ BUG_ON(other < 0);
+ PRINTK("Computing stripe %llu blocks %d,%d\n",
+ (unsigned long long)sh->sector, i, other);
+ compute_block_2(sh, i, other);
+ uptodate += 2;
+ } else if (test_bit(R5_Insync, &dev->flags)) {
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantread, &dev->flags);
+#if 0
+ /* if I am just reading this block and we don't have
+ a failed drive, or any pending writes then sidestep the cache */
+ if (sh->bh_read[i] && !sh->bh_read[i]->b_reqnext &&
+ ! syncing && !failed && !to_write) {
+ sh->bh_cache[i]->b_page = sh->bh_read[i]->b_page;
+ sh->bh_cache[i]->b_data = sh->bh_read[i]->b_data;
+ }
+#endif
+ locked++;
+ PRINTK("Reading block %d (sync=%d)\n",
+ i, syncing);
+ }
+ }
+ }
+ set_bit(STRIPE_HANDLE, &sh->state);
+ }
+
+ /* now to consider writing and what else, if anything should be read */
+ if (to_write) {
+ int rcw=0, must_compute=0;
+ for (i=disks ; i--;) {
+ dev = &sh->dev[i];
+ /* Would I have to read this buffer for reconstruct_write */
+ if (!test_bit(R5_OVERWRITE, &dev->flags)
+ && i != pd_idx && i != qd_idx
+ && (!test_bit(R5_LOCKED, &dev->flags)
+#if 0
+ || sh->bh_page[i] != bh->b_page
+#endif
+ ) &&
+ !test_bit(R5_UPTODATE, &dev->flags)) {
+ if (test_bit(R5_Insync, &dev->flags)) rcw++;
+ else {
+ PRINTK("raid6: must_compute: disk %d flags=%#lx\n", i, dev->flags);
+ must_compute++;
+ }
+ }
+ }
+ PRINTK("for sector %llu, rcw=%d, must_compute=%d\n",
+ (unsigned long long)sh->sector, rcw, must_compute);
+ set_bit(STRIPE_HANDLE, &sh->state);
+
+ if (rcw > 0)
+ /* want reconstruct write, but need to get some data */
+ for (i=disks; i--;) {
+ dev = &sh->dev[i];
+ if (!test_bit(R5_OVERWRITE, &dev->flags)
+ && !(failed == 0 && (i == pd_idx || i == qd_idx))
+ && !test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) &&
+ test_bit(R5_Insync, &dev->flags)) {
+ if (test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
+ {
+ PRINTK("Read_old stripe %llu block %d for Reconstruct\n",
+ (unsigned long long)sh->sector, i);
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantread, &dev->flags);
+ locked++;
+ } else {
+ PRINTK("Request delayed stripe %llu block %d for Reconstruct\n",
+ (unsigned long long)sh->sector, i);
+ set_bit(STRIPE_DELAYED, &sh->state);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ }
+ }
+ }
+ /* now if nothing is locked, and if we have enough data, we can start a write request */
+ if (locked == 0 && rcw == 0 &&
+ !test_bit(STRIPE_BIT_DELAY, &sh->state)) {
+ if ( must_compute > 0 ) {
+ /* We have failed blocks and need to compute them */
+ switch ( failed ) {
+ case 0: BUG();
+ case 1: compute_block_1(sh, failed_num[0], 0); break;
+ case 2: compute_block_2(sh, failed_num[0], failed_num[1]); break;
+ default: BUG(); /* This request should have been failed? */
+ }
+ }
+
+ PRINTK("Computing parity for stripe %llu\n", (unsigned long long)sh->sector);
+ compute_parity6(sh, RECONSTRUCT_WRITE);
+ /* now every locked buffer is ready to be written */
+ for (i=disks; i--;)
+ if (test_bit(R5_LOCKED, &sh->dev[i].flags)) {
+ PRINTK("Writing stripe %llu block %d\n",
+ (unsigned long long)sh->sector, i);
+ locked++;
+ set_bit(R5_Wantwrite, &sh->dev[i].flags);
+ }
+ /* after a RECONSTRUCT_WRITE, the stripe MUST be in-sync */
+ set_bit(STRIPE_INSYNC, &sh->state);
+
+ if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
+ atomic_dec(&conf->preread_active_stripes);
+ if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD)
+ md_wakeup_thread(conf->mddev->thread);
+ }
+ }
+ }
+
+ /* maybe we need to check and possibly fix the parity for this stripe
+ * Any reads will already have been scheduled, so we just see if enough data
+ * is available
+ */
+ if (syncing && locked == 0 && !test_bit(STRIPE_INSYNC, &sh->state)) {
+ int update_p = 0, update_q = 0;
+ struct r5dev *dev;
+
+ set_bit(STRIPE_HANDLE, &sh->state);
+
+ BUG_ON(failed>2);
+ BUG_ON(uptodate < disks);
+ /* Want to check and possibly repair P and Q.
+ * However there could be one 'failed' device, in which
+ * case we can only check one of them, possibly using the
+ * other to generate missing data
+ */
+
+ /* If !tmp_page, we cannot do the calculations,
+ * but as we have set STRIPE_HANDLE, we will soon be called
+ * by stripe_handle with a tmp_page - just wait until then.
+ */
+ if (tmp_page) {
+ if (failed == q_failed) {
+ /* The only possible failed device holds 'Q', so it makes
+ * sense to check P (If anything else were failed, we would
+ * have used P to recreate it).
+ */
+ compute_block_1(sh, pd_idx, 1);
+ if (!page_is_zero(sh->dev[pd_idx].page)) {
+ compute_block_1(sh,pd_idx,0);
+ update_p = 1;
+ }
+ }
+ if (!q_failed && failed < 2) {
+ /* q is not failed, and we didn't use it to generate
+ * anything, so it makes sense to check it
+ */
+ memcpy(page_address(tmp_page),
+ page_address(sh->dev[qd_idx].page),
+ STRIPE_SIZE);
+ compute_parity6(sh, UPDATE_PARITY);
+ if (memcmp(page_address(tmp_page),
+ page_address(sh->dev[qd_idx].page),
+ STRIPE_SIZE)!= 0) {
+ clear_bit(STRIPE_INSYNC, &sh->state);
+ update_q = 1;
+ }
+ }
+ if (update_p || update_q) {
+ conf->mddev->resync_mismatches += STRIPE_SECTORS;
+ if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery))
+ /* don't try to repair!! */
+ update_p = update_q = 0;
+ }
+
+ /* now write out any block on a failed drive,
+ * or P or Q if they need it
+ */
+
+ if (failed == 2) {
+ dev = &sh->dev[failed_num[1]];
+ locked++;
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantwrite, &dev->flags);
+ }
+ if (failed >= 1) {
+ dev = &sh->dev[failed_num[0]];
+ locked++;
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantwrite, &dev->flags);
+ }
+
+ if (update_p) {
+ dev = &sh->dev[pd_idx];
+ locked ++;
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantwrite, &dev->flags);
+ }
+ if (update_q) {
+ dev = &sh->dev[qd_idx];
+ locked++;
+ set_bit(R5_LOCKED, &dev->flags);
+ set_bit(R5_Wantwrite, &dev->flags);
+ }
+ clear_bit(STRIPE_DEGRADED, &sh->state);
+
+ set_bit(STRIPE_INSYNC, &sh->state);
+ }
+ }
+
+ if (syncing && locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
+ md_done_sync(conf->mddev, STRIPE_SECTORS,1);
+ clear_bit(STRIPE_SYNCING, &sh->state);
+ }
+
+ /* If the failed drives are just a ReadError, then we might need
+ * to progress the repair/check process
+ */
+ if (failed <= 2 && ! conf->mddev->ro)
+ for (i=0; i<failed;i++) {
+ dev = &sh->dev[failed_num[i]];
+ if (test_bit(R5_ReadError, &dev->flags)
+ && !test_bit(R5_LOCKED, &dev->flags)
+ && test_bit(R5_UPTODATE, &dev->flags)
+ ) {
+ if (!test_bit(R5_ReWrite, &dev->flags)) {
+ set_bit(R5_Wantwrite, &dev->flags);
+ set_bit(R5_ReWrite, &dev->flags);
+ set_bit(R5_LOCKED, &dev->flags);
+ } else {
+ /* let's read it back */
+ set_bit(R5_Wantread, &dev->flags);
+ set_bit(R5_LOCKED, &dev->flags);
+ }
+ }
+ }
+ spin_unlock(&sh->lock);
+
+ while ((bi=return_bi)) {
+ int bytes = bi->bi_size;
+
+ return_bi = bi->bi_next;
+ bi->bi_next = NULL;
+ bi->bi_size = 0;
+ bi->bi_end_io(bi, bytes, 0);
+ }
+ for (i=disks; i-- ;) {
+ int rw;
+ struct bio *bi;
+ mdk_rdev_t *rdev;
+ if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags))
+ rw = 1;
+ else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags))
+ rw = 0;
+ else
+ continue;
+
+ bi = &sh->dev[i].req;
+
+ bi->bi_rw = rw;
+ if (rw)
+ bi->bi_end_io = raid5_end_write_request;
+ else
+ bi->bi_end_io = raid5_end_read_request;
+
+ rcu_read_lock();
+ rdev = rcu_dereference(conf->disks[i].rdev);
+ if (rdev && test_bit(Faulty, &rdev->flags))
+ rdev = NULL;
+ if (rdev)
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+
+ if (rdev) {
+ if (syncing)
+ md_sync_acct(rdev->bdev, STRIPE_SECTORS);
+
+ bi->bi_bdev = rdev->bdev;
+ PRINTK("for %llu schedule op %ld on disc %d\n",
+ (unsigned long long)sh->sector, bi->bi_rw, i);
+ atomic_inc(&sh->count);
+ bi->bi_sector = sh->sector + rdev->data_offset;
+ bi->bi_flags = 1 << BIO_UPTODATE;
+ bi->bi_vcnt = 1;
+ bi->bi_max_vecs = 1;
+ bi->bi_idx = 0;
+ bi->bi_io_vec = &sh->dev[i].vec;
+ bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
+ bi->bi_io_vec[0].bv_offset = 0;
+ bi->bi_size = STRIPE_SIZE;
+ bi->bi_next = NULL;
+ if (rw == WRITE &&
+ test_bit(R5_ReWrite, &sh->dev[i].flags))
+ atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
+ generic_make_request(bi);
+ } else {
+ if (rw == 1)
+ set_bit(STRIPE_DEGRADED, &sh->state);
+ PRINTK("skip op %ld on disc %d for sector %llu\n",
+ bi->bi_rw, i, (unsigned long long)sh->sector);
+ clear_bit(R5_LOCKED, &sh->dev[i].flags);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ }
+ }
+}
+
+static void handle_stripe(struct stripe_head *sh, struct page *tmp_page)
+{
+ if (sh->raid_conf->level == 6)
+ handle_stripe6(sh, tmp_page);
+ else
+ handle_stripe5(sh);
+}
+
+
+
static void raid5_activate_delayed(raid5_conf_t *conf)
{
if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) {
@@ -1753,7 +2590,7 @@ static int make_request(request_queue_t *q, struct bio * bi)
for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
DEFINE_WAIT(w);
- int disks;
+ int disks, data_disks;
retry:
prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE);
@@ -1781,7 +2618,9 @@ static int make_request(request_queue_t *q, struct bio * bi)
}
spin_unlock_irq(&conf->device_lock);
}
- new_sector = raid5_compute_sector(logical_sector, disks, disks - 1,
+ data_disks = disks - conf->max_degraded;
+
+ new_sector = raid5_compute_sector(logical_sector, disks, data_disks,
&dd_idx, &pd_idx, conf);
PRINTK("raid5: make_request, sector %llu logical %llu\n",
(unsigned long long)new_sector,
@@ -1833,7 +2672,7 @@ static int make_request(request_queue_t *q, struct bio * bi)
}
finish_wait(&conf->wait_for_overlap, &w);
raid5_plug_device(conf);
- handle_stripe(sh);
+ handle_stripe(sh, NULL);
release_stripe(sh);
} else {
/* cannot get stripe for read-ahead, just give-up */
@@ -1849,7 +2688,7 @@ static int make_request(request_queue_t *q, struct bio * bi)
if (remaining == 0) {
int bytes = bi->bi_size;
- if ( bio_data_dir(bi) == WRITE )
+ if ( rw == WRITE )
md_write_end(mddev);
bi->bi_size = 0;
bi->bi_end_io(bi, bytes, 0);
@@ -1857,17 +2696,141 @@ static int make_request(request_queue_t *q, struct bio * bi)
return 0;
}
-/* FIXME go_faster isn't used */
-static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster)
+static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped)
{
+ /* reshaping is quite different to recovery/resync so it is
+ * handled quite separately ... here.
+ *
+ * On each call to sync_request, we gather one chunk worth of
+ * destination stripes and flag them as expanding.
+ * Then we find all the source stripes and request reads.
+ * As the reads complete, handle_stripe will copy the data
+ * into the destination stripe and release that stripe.
+ */
raid5_conf_t *conf = (raid5_conf_t *) mddev->private;
struct stripe_head *sh;
int pd_idx;
sector_t first_sector, last_sector;
+ int raid_disks;
+ int data_disks;
+ int i;
+ int dd_idx;
+ sector_t writepos, safepos, gap;
+
+ if (sector_nr == 0 &&
+ conf->expand_progress != 0) {
+ /* restarting in the middle, skip the initial sectors */
+ sector_nr = conf->expand_progress;
+ sector_div(sector_nr, conf->raid_disks-1);
+ *skipped = 1;
+ return sector_nr;
+ }
+
+ /* we update the metadata when there is more than 3Meg
+ * in the block range (that is rather arbitrary, should
+ * probably be time based) or when the data about to be
+ * copied would over-write the source of the data at
+ * the front of the range.
+ * i.e. one new_stripe forward from expand_progress new_maps
+ * to after where expand_lo old_maps to
+ */
+ writepos = conf->expand_progress +
+ conf->chunk_size/512*(conf->raid_disks-1);
+ sector_div(writepos, conf->raid_disks-1);
+ safepos = conf->expand_lo;
+ sector_div(safepos, conf->previous_raid_disks-1);
+ gap = conf->expand_progress - conf->expand_lo;
+
+ if (writepos >= safepos ||
+ gap > (conf->raid_disks-1)*3000*2 /*3Meg*/) {
+ /* Cannot proceed until we've updated the superblock... */
+ wait_event(conf->wait_for_overlap,
+ atomic_read(&conf->reshape_stripes)==0);
+ mddev->reshape_position = conf->expand_progress;
+ mddev->sb_dirty = 1;
+ md_wakeup_thread(mddev->thread);
+ wait_event(mddev->sb_wait, mddev->sb_dirty == 0 ||
+ kthread_should_stop());
+ spin_lock_irq(&conf->device_lock);
+ conf->expand_lo = mddev->reshape_position;
+ spin_unlock_irq(&conf->device_lock);
+ wake_up(&conf->wait_for_overlap);
+ }
+
+ for (i=0; i < conf->chunk_size/512; i+= STRIPE_SECTORS) {
+ int j;
+ int skipped = 0;
+ pd_idx = stripe_to_pdidx(sector_nr+i, conf, conf->raid_disks);
+ sh = get_active_stripe(conf, sector_nr+i,
+ conf->raid_disks, pd_idx, 0);
+ set_bit(STRIPE_EXPANDING, &sh->state);
+ atomic_inc(&conf->reshape_stripes);
+ /* If any of this stripe is beyond the end of the old
+ * array, then we need to zero those blocks
+ */
+ for (j=sh->disks; j--;) {
+ sector_t s;
+ if (j == sh->pd_idx)
+ continue;
+ s = compute_blocknr(sh, j);
+ if (s < (mddev->array_size<<1)) {
+ skipped = 1;
+ continue;
+ }
+ memset(page_address(sh->dev[j].page), 0, STRIPE_SIZE);
+ set_bit(R5_Expanded, &sh->dev[j].flags);
+ set_bit(R5_UPTODATE, &sh->dev[j].flags);
+ }
+ if (!skipped) {
+ set_bit(STRIPE_EXPAND_READY, &sh->state);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ }
+ release_stripe(sh);
+ }
+ spin_lock_irq(&conf->device_lock);
+ conf->expand_progress = (sector_nr + i)*(conf->raid_disks-1);
+ spin_unlock_irq(&conf->device_lock);
+ /* Ok, those stripe are ready. We can start scheduling
+ * reads on the source stripes.
+ * The source stripes are determined by mapping the first and last
+ * block on the destination stripes.
+ */
+ raid_disks = conf->previous_raid_disks;
+ data_disks = raid_disks - 1;
+ first_sector =
+ raid5_compute_sector(sector_nr*(conf->raid_disks-1),
+ raid_disks, data_disks,
+ &dd_idx, &pd_idx, conf);
+ last_sector =
+ raid5_compute_sector((sector_nr+conf->chunk_size/512)
+ *(conf->raid_disks-1) -1,
+ raid_disks, data_disks,
+ &dd_idx, &pd_idx, conf);
+ if (last_sector >= (mddev->size<<1))
+ last_sector = (mddev->size<<1)-1;
+ while (first_sector <= last_sector) {
+ pd_idx = stripe_to_pdidx(first_sector, conf, conf->previous_raid_disks);
+ sh = get_active_stripe(conf, first_sector,
+ conf->previous_raid_disks, pd_idx, 0);
+ set_bit(STRIPE_EXPAND_SOURCE, &sh->state);
+ set_bit(STRIPE_HANDLE, &sh->state);
+ release_stripe(sh);
+ first_sector += STRIPE_SECTORS;
+ }
+ return conf->chunk_size>>9;
+}
+
+/* FIXME go_faster isn't used */
+static inline sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster)
+{
+ raid5_conf_t *conf = (raid5_conf_t *) mddev->private;
+ struct stripe_head *sh;
+ int pd_idx;
int raid_disks = conf->raid_disks;
- int data_disks = raid_disks-1;
sector_t max_sector = mddev->size << 1;
int sync_blocks;
+ int still_degraded = 0;
+ int i;
if (sector_nr >= max_sector) {
/* just being told to finish up .. nothing much to do */
@@ -1880,134 +2843,22 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
if (mddev->curr_resync < max_sector) /* aborted */
bitmap_end_sync(mddev->bitmap, mddev->curr_resync,
&sync_blocks, 1);
- else /* compelted sync */
+ else /* completed sync */
conf->fullsync = 0;
bitmap_close_sync(mddev->bitmap);
return 0;
}
- if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)) {
- /* reshaping is quite different to recovery/resync so it is
- * handled quite separately ... here.
- *
- * On each call to sync_request, we gather one chunk worth of
- * destination stripes and flag them as expanding.
- * Then we find all the source stripes and request reads.
- * As the reads complete, handle_stripe will copy the data
- * into the destination stripe and release that stripe.
- */
- int i;
- int dd_idx;
- sector_t writepos, safepos, gap;
-
- if (sector_nr == 0 &&
- conf->expand_progress != 0) {
- /* restarting in the middle, skip the initial sectors */
- sector_nr = conf->expand_progress;
- sector_div(sector_nr, conf->raid_disks-1);
- *skipped = 1;
- return sector_nr;
- }
-
- /* we update the metadata when there is more than 3Meg
- * in the block range (that is rather arbitrary, should
- * probably be time based) or when the data about to be
- * copied would over-write the source of the data at
- * the front of the range.
- * i.e. one new_stripe forward from expand_progress new_maps
- * to after where expand_lo old_maps to
- */
- writepos = conf->expand_progress +
- conf->chunk_size/512*(conf->raid_disks-1);
- sector_div(writepos, conf->raid_disks-1);
- safepos = conf->expand_lo;
- sector_div(safepos, conf->previous_raid_disks-1);
- gap = conf->expand_progress - conf->expand_lo;
-
- if (writepos >= safepos ||
- gap > (conf->raid_disks-1)*3000*2 /*3Meg*/) {
- /* Cannot proceed until we've updated the superblock... */
- wait_event(conf->wait_for_overlap,
- atomic_read(&conf->reshape_stripes)==0);
- mddev->reshape_position = conf->expand_progress;
- mddev->sb_dirty = 1;
- md_wakeup_thread(mddev->thread);
- wait_event(mddev->sb_wait, mddev->sb_dirty == 0 ||
- kthread_should_stop());
- spin_lock_irq(&conf->device_lock);
- conf->expand_lo = mddev->reshape_position;
- spin_unlock_irq(&conf->device_lock);
- wake_up(&conf->wait_for_overlap);
- }
-
- for (i=0; i < conf->chunk_size/512; i+= STRIPE_SECTORS) {
- int j;
- int skipped = 0;
- pd_idx = stripe_to_pdidx(sector_nr+i, conf, conf->raid_disks);
- sh = get_active_stripe(conf, sector_nr+i,
- conf->raid_disks, pd_idx, 0);
- set_bit(STRIPE_EXPANDING, &sh->state);
- atomic_inc(&conf->reshape_stripes);
- /* If any of this stripe is beyond the end of the old
- * array, then we need to zero those blocks
- */
- for (j=sh->disks; j--;) {
- sector_t s;
- if (j == sh->pd_idx)
- continue;
- s = compute_blocknr(sh, j);
- if (s < (mddev->array_size<<1)) {
- skipped = 1;
- continue;
- }
- memset(page_address(sh->dev[j].page), 0, STRIPE_SIZE);
- set_bit(R5_Expanded, &sh->dev[j].flags);
- set_bit(R5_UPTODATE, &sh->dev[j].flags);
- }
- if (!skipped) {
- set_bit(STRIPE_EXPAND_READY, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- }
- release_stripe(sh);
- }
- spin_lock_irq(&conf->device_lock);
- conf->expand_progress = (sector_nr + i)*(conf->raid_disks-1);
- spin_unlock_irq(&conf->device_lock);
- /* Ok, those stripe are ready. We can start scheduling
- * reads on the source stripes.
- * The source stripes are determined by mapping the first and last
- * block on the destination stripes.
- */
- raid_disks = conf->previous_raid_disks;
- data_disks = raid_disks - 1;
- first_sector =
- raid5_compute_sector(sector_nr*(conf->raid_disks-1),
- raid_disks, data_disks,
- &dd_idx, &pd_idx, conf);
- last_sector =
- raid5_compute_sector((sector_nr+conf->chunk_size/512)
- *(conf->raid_disks-1) -1,
- raid_disks, data_disks,
- &dd_idx, &pd_idx, conf);
- if (last_sector >= (mddev->size<<1))
- last_sector = (mddev->size<<1)-1;
- while (first_sector <= last_sector) {
- pd_idx = stripe_to_pdidx(first_sector, conf, conf->previous_raid_disks);
- sh = get_active_stripe(conf, first_sector,
- conf->previous_raid_disks, pd_idx, 0);
- set_bit(STRIPE_EXPAND_SOURCE, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- release_stripe(sh);
- first_sector += STRIPE_SECTORS;
- }
- return conf->chunk_size>>9;
- }
- /* if there is 1 or more failed drives and we are trying
+ if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+ return reshape_request(mddev, sector_nr, skipped);
+
+ /* if there is too many failed drives and we are trying
* to resync, then assert that we are finished, because there is
* nothing we can do.
*/
- if (mddev->degraded >= 1 && test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+ if (mddev->degraded >= conf->max_degraded &&
+ test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
sector_t rv = (mddev->size << 1) - sector_nr;
*skipped = 1;
return rv;
@@ -2026,17 +2877,26 @@ static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, i
if (sh == NULL) {
sh = get_active_stripe(conf, sector_nr, raid_disks, pd_idx, 0);
/* make sure we don't swamp the stripe cache if someone else
- * is trying to get access
+ * is trying to get access
*/
schedule_timeout_uninterruptible(1);
}
- bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, 0);
- spin_lock(&sh->lock);
+ /* Need to check if array will still be degraded after recovery/resync
+ * We don't need to check the 'failed' flag as when that gets set,
+ * recovery aborts.
+ */
+ for (i=0; i<mddev->raid_disks; i++)
+ if (conf->disks[i].rdev == NULL)
+ still_degraded = 1;
+
+ bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
+
+ spin_lock(&sh->lock);
set_bit(STRIPE_SYNCING, &sh->state);
clear_bit(STRIPE_INSYNC, &sh->state);
spin_unlock(&sh->lock);
- handle_stripe(sh);
+ handle_stripe(sh, NULL);
release_stripe(sh);
return STRIPE_SECTORS;
@@ -2091,7 +2951,7 @@ static void raid5d (mddev_t *mddev)
spin_unlock_irq(&conf->device_lock);
handled++;
- handle_stripe(sh);
+ handle_stripe(sh, conf->spare_page);
release_stripe(sh);
spin_lock_irq(&conf->device_lock);
@@ -2181,8 +3041,8 @@ static int run(mddev_t *mddev)
struct disk_info *disk;
struct list_head *tmp;
- if (mddev->level != 5 && mddev->level != 4) {
- printk(KERN_ERR "raid5: %s: raid level not set to 4/5 (%d)\n",
+ if (mddev->level != 5 && mddev->level != 4 && mddev->level != 6) {
+ printk(KERN_ERR "raid5: %s: raid level not set to 4/5/6 (%d)\n",
mdname(mddev), mddev->level);
return -EIO;
}
@@ -2251,6 +3111,11 @@ static int run(mddev_t *mddev)
if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL)
goto abort;
+ if (mddev->level == 6) {
+ conf->spare_page = alloc_page(GFP_KERNEL);
+ if (!conf->spare_page)
+ goto abort;
+ }
spin_lock_init(&conf->device_lock);
init_waitqueue_head(&conf->wait_for_stripe);
init_waitqueue_head(&conf->wait_for_overlap);
@@ -2282,12 +3147,16 @@ static int run(mddev_t *mddev)
}
/*
- * 0 for a fully functional array, 1 for a degraded array.
+ * 0 for a fully functional array, 1 or 2 for a degraded array.
*/
mddev->degraded = conf->failed_disks = conf->raid_disks - conf->working_disks;
conf->mddev = mddev;
conf->chunk_size = mddev->chunk_size;
conf->level = mddev->level;
+ if (conf->level == 6)
+ conf->max_degraded = 2;
+ else
+ conf->max_degraded = 1;
conf->algorithm = mddev->layout;
conf->max_nr_stripes = NR_STRIPES;
conf->expand_progress = mddev->reshape_position;
@@ -2296,6 +3165,11 @@ static int run(mddev_t *mddev)
mddev->size &= ~(mddev->chunk_size/1024 -1);
mddev->resync_max_sectors = mddev->size << 1;
+ if (conf->level == 6 && conf->raid_disks < 4) {
+ printk(KERN_ERR "raid6: not enough configured devices for %s (%d, minimum 4)\n",
+ mdname(mddev), conf->raid_disks);
+ goto abort;
+ }
if (!conf->chunk_size || conf->chunk_size % 4) {
printk(KERN_ERR "raid5: invalid chunk size %d for %s\n",
conf->chunk_size, mdname(mddev));
@@ -2307,14 +3181,14 @@ static int run(mddev_t *mddev)
conf->algorithm, mdname(mddev));
goto abort;
}
- if (mddev->degraded > 1) {
+ if (mddev->degraded > conf->max_degraded) {
printk(KERN_ERR "raid5: not enough operational devices for %s"
" (%d/%d failed)\n",
mdname(mddev), conf->failed_disks, conf->raid_disks);
goto abort;
}
- if (mddev->degraded == 1 &&
+ if (mddev->degraded > 0 &&
mddev->recovery_cp != MaxSector) {
if (mddev->ok_start_degraded)
printk(KERN_WARNING
@@ -2379,11 +3253,12 @@ static int run(mddev_t *mddev)
}
/* read-ahead size must cover two whole stripes, which is
- * 2 * (n-1) * chunksize where 'n' is the number of raid devices
+ * 2 * (datadisks) * chunksize where 'n' is the number of raid devices
*/
{
- int stripe = (mddev->raid_disks-1) * mddev->chunk_size
- / PAGE_SIZE;
+ int data_disks = conf->previous_raid_disks - conf->max_degraded;
+ int stripe = data_disks *
+ (mddev->chunk_size / PAGE_SIZE);
if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
}
@@ -2393,12 +3268,14 @@ static int run(mddev_t *mddev)
mddev->queue->unplug_fn = raid5_unplug_device;
mddev->queue->issue_flush_fn = raid5_issue_flush;
- mddev->array_size = mddev->size * (conf->previous_raid_disks - 1);
+ mddev->array_size = mddev->size * (conf->previous_raid_disks -
+ conf->max_degraded);
return 0;
abort:
if (conf) {
print_raid5_conf(conf);
+ safe_put_page(conf->spare_page);
kfree(conf->disks);
kfree(conf->stripe_hashtbl);
kfree(conf);
@@ -2427,23 +3304,23 @@ static int stop(mddev_t *mddev)
}
#if RAID5_DEBUG
-static void print_sh (struct stripe_head *sh)
+static void print_sh (struct seq_file *seq, struct stripe_head *sh)
{
int i;
- printk("sh %llu, pd_idx %d, state %ld.\n",
- (unsigned long long)sh->sector, sh->pd_idx, sh->state);
- printk("sh %llu, count %d.\n",
- (unsigned long long)sh->sector, atomic_read(&sh->count));
- printk("sh %llu, ", (unsigned long long)sh->sector);
+ seq_printf(seq, "sh %llu, pd_idx %d, state %ld.\n",
+ (unsigned long long)sh->sector, sh->pd_idx, sh->state);
+ seq_printf(seq, "sh %llu, count %d.\n",
+ (unsigned long long)sh->sector, atomic_read(&sh->count));
+ seq_printf(seq, "sh %llu, ", (unsigned long long)sh->sector);
for (i = 0; i < sh->disks; i++) {
- printk("(cache%d: %p %ld) ",
- i, sh->dev[i].page, sh->dev[i].flags);
+ seq_printf(seq, "(cache%d: %p %ld) ",
+ i, sh->dev[i].page, sh->dev[i].flags);
}
- printk("\n");
+ seq_printf(seq, "\n");
}
-static void printall (raid5_conf_t *conf)
+static void printall (struct seq_file *seq, raid5_conf_t *conf)
{
struct stripe_head *sh;
struct hlist_node *hn;
@@ -2454,7 +3331,7 @@ static void printall (raid5_conf_t *conf)
hlist_for_each_entry(sh, hn, &conf->stripe_hashtbl[i], hash) {
if (sh->raid_conf != conf)
continue;
- print_sh(sh);
+ print_sh(seq, sh);
}
}
spin_unlock_irq(&conf->device_lock);
@@ -2474,9 +3351,8 @@ static void status (struct seq_file *seq, mddev_t *mddev)
test_bit(In_sync, &conf->disks[i].rdev->flags) ? "U" : "_");
seq_printf (seq, "]");
#if RAID5_DEBUG
-#define D(x) \
- seq_printf (seq, "<"#x":%d>", atomic_read(&conf->x))
- printall(conf);
+ seq_printf (seq, "\n");
+ printall(seq, conf);
#endif
}
@@ -2560,14 +3436,20 @@ static int raid5_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
int disk;
struct disk_info *p;
- if (mddev->degraded > 1)
+ if (mddev->degraded > conf->max_degraded)
/* no point adding a device */
return 0;
/*
- * find the disk ...
+ * find the disk ... but prefer rdev->saved_raid_disk
+ * if possible.
*/
- for (disk=0; disk < conf->raid_disks; disk++)
+ if (rdev->saved_raid_disk >= 0 &&
+ conf->disks[rdev->saved_raid_disk].rdev == NULL)
+ disk = rdev->saved_raid_disk;
+ else
+ disk = 0;
+ for ( ; disk < conf->raid_disks; disk++)
if ((p=conf->disks + disk)->rdev == NULL) {
clear_bit(In_sync, &rdev->flags);
rdev->raid_disk = disk;
@@ -2590,8 +3472,10 @@ static int raid5_resize(mddev_t *mddev, sector_t sectors)
* any io in the removed space completes, but it hardly seems
* worth it.
*/
+ raid5_conf_t *conf = mddev_to_conf(mddev);
+
sectors &= ~((sector_t)mddev->chunk_size/512 - 1);
- mddev->array_size = (sectors * (mddev->raid_disks-1))>>1;
+ mddev->array_size = (sectors * (mddev->raid_disks-conf->max_degraded))>>1;
set_capacity(mddev->gendisk, mddev->array_size << 1);
mddev->changed = 1;
if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) {
@@ -2680,6 +3564,7 @@ static int raid5_start_reshape(mddev_t *mddev)
set_bit(In_sync, &rdev->flags);
conf->working_disks++;
added_devices++;
+ rdev->recovery_offset = 0;
sprintf(nm, "rd%d", rdev->raid_disk);
sysfs_create_link(&mddev->kobj, &rdev->kobj, nm);
} else
@@ -2731,6 +3616,17 @@ static void end_reshape(raid5_conf_t *conf)
conf->expand_progress = MaxSector;
spin_unlock_irq(&conf->device_lock);
conf->mddev->reshape_position = MaxSector;
+
+ /* read-ahead size must cover two whole stripes, which is
+ * 2 * (datadisks) * chunksize where 'n' is the number of raid devices
+ */
+ {
+ int data_disks = conf->previous_raid_disks - conf->max_degraded;
+ int stripe = data_disks *
+ (conf->mddev->chunk_size / PAGE_SIZE);
+ if (conf->mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
+ conf->mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
+ }
}
}
@@ -2762,6 +3658,23 @@ static void raid5_quiesce(mddev_t *mddev, int state)
}
}
+static struct mdk_personality raid6_personality =
+{
+ .name = "raid6",
+ .level = 6,
+ .owner = THIS_MODULE,
+ .make_request = make_request,
+ .run = run,
+ .stop = stop,
+ .status = status,
+ .error_handler = error,
+ .hot_add_disk = raid5_add_disk,
+ .hot_remove_disk= raid5_remove_disk,
+ .spare_active = raid5_spare_active,
+ .sync_request = sync_request,
+ .resize = raid5_resize,
+ .quiesce = raid5_quiesce,
+};
static struct mdk_personality raid5_personality =
{
.name = "raid5",
@@ -2804,6 +3717,12 @@ static struct mdk_personality raid4_personality =
static int __init raid5_init(void)
{
+ int e;
+
+ e = raid6_select_algo();
+ if ( e )
+ return e;
+ register_md_personality(&raid6_personality);
register_md_personality(&raid5_personality);
register_md_personality(&raid4_personality);
return 0;
@@ -2811,6 +3730,7 @@ static int __init raid5_init(void)
static void raid5_exit(void)
{
+ unregister_md_personality(&raid6_personality);
unregister_md_personality(&raid5_personality);
unregister_md_personality(&raid4_personality);
}
@@ -2823,3 +3743,10 @@ MODULE_ALIAS("md-raid5");
MODULE_ALIAS("md-raid4");
MODULE_ALIAS("md-level-5");
MODULE_ALIAS("md-level-4");
+MODULE_ALIAS("md-personality-8"); /* RAID6 */
+MODULE_ALIAS("md-raid6");
+MODULE_ALIAS("md-level-6");
+
+/* This used to be two separate modules, they were: */
+MODULE_ALIAS("raid5");
+MODULE_ALIAS("raid6");
diff --git a/drivers/md/raid6main.c b/drivers/md/raid6main.c
deleted file mode 100644
index bc69355e0100..000000000000
--- a/drivers/md/raid6main.c
+++ /dev/null
@@ -1,2427 +0,0 @@
-/*
- * raid6main.c : Multiple Devices driver for Linux
- * Copyright (C) 1996, 1997 Ingo Molnar, Miguel de Icaza, Gadi Oxman
- * Copyright (C) 1999, 2000 Ingo Molnar
- * Copyright (C) 2002, 2003 H. Peter Anvin
- *
- * RAID-6 management functions. This code is derived from raid5.c.
- * Last merge from raid5.c bkcvs version 1.79 (kernel 2.6.1).
- *
- * Thanks to Penguin Computing for making the RAID-6 development possible
- * by donating a test server!
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * (for example /usr/src/linux/COPYING); if not, write to the Free
- * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/highmem.h>
-#include <linux/bitops.h>
-#include <asm/atomic.h>
-#include "raid6.h"
-
-#include <linux/raid/bitmap.h>
-
-/*
- * Stripe cache
- */
-
-#define NR_STRIPES 256
-#define STRIPE_SIZE PAGE_SIZE
-#define STRIPE_SHIFT (PAGE_SHIFT - 9)
-#define STRIPE_SECTORS (STRIPE_SIZE>>9)
-#define IO_THRESHOLD 1
-#define NR_HASH (PAGE_SIZE / sizeof(struct hlist_head))
-#define HASH_MASK (NR_HASH - 1)
-
-#define stripe_hash(conf, sect) (&((conf)->stripe_hashtbl[((sect) >> STRIPE_SHIFT) & HASH_MASK]))
-
-/* bio's attached to a stripe+device for I/O are linked together in bi_sector
- * order without overlap. There may be several bio's per stripe+device, and
- * a bio could span several devices.
- * When walking this list for a particular stripe+device, we must never proceed
- * beyond a bio that extends past this device, as the next bio might no longer
- * be valid.
- * This macro is used to determine the 'next' bio in the list, given the sector
- * of the current stripe+device
- */
-#define r5_next_bio(bio, sect) ( ( (bio)->bi_sector + ((bio)->bi_size>>9) < sect + STRIPE_SECTORS) ? (bio)->bi_next : NULL)
-/*
- * The following can be used to debug the driver
- */
-#define RAID6_DEBUG 0 /* Extremely verbose printk */
-#define RAID6_PARANOIA 1 /* Check spinlocks */
-#define RAID6_DUMPSTATE 0 /* Include stripe cache state in /proc/mdstat */
-#if RAID6_PARANOIA && defined(CONFIG_SMP)
-# define CHECK_DEVLOCK() assert_spin_locked(&conf->device_lock)
-#else
-# define CHECK_DEVLOCK()
-#endif
-
-#define PRINTK(x...) ((void)(RAID6_DEBUG && printk(KERN_DEBUG x)))
-#if RAID6_DEBUG
-#undef inline
-#undef __inline__
-#define inline
-#define __inline__
-#endif
-
-#if !RAID6_USE_EMPTY_ZERO_PAGE
-/* In .bss so it's zeroed */
-const char raid6_empty_zero_page[PAGE_SIZE] __attribute__((aligned(256)));
-#endif
-
-static inline int raid6_next_disk(int disk, int raid_disks)
-{
- disk++;
- return (disk < raid_disks) ? disk : 0;
-}
-
-static void print_raid6_conf (raid6_conf_t *conf);
-
-static void __release_stripe(raid6_conf_t *conf, struct stripe_head *sh)
-{
- if (atomic_dec_and_test(&sh->count)) {
- BUG_ON(!list_empty(&sh->lru));
- BUG_ON(atomic_read(&conf->active_stripes)==0);
- if (test_bit(STRIPE_HANDLE, &sh->state)) {
- if (test_bit(STRIPE_DELAYED, &sh->state))
- list_add_tail(&sh->lru, &conf->delayed_list);
- else if (test_bit(STRIPE_BIT_DELAY, &sh->state) &&
- conf->seq_write == sh->bm_seq)
- list_add_tail(&sh->lru, &conf->bitmap_list);
- else {
- clear_bit(STRIPE_BIT_DELAY, &sh->state);
- list_add_tail(&sh->lru, &conf->handle_list);
- }
- md_wakeup_thread(conf->mddev->thread);
- } else {
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- }
- list_add_tail(&sh->lru, &conf->inactive_list);
- atomic_dec(&conf->active_stripes);
- if (!conf->inactive_blocked ||
- atomic_read(&conf->active_stripes) < (conf->max_nr_stripes*3/4))
- wake_up(&conf->wait_for_stripe);
- }
- }
-}
-static void release_stripe(struct stripe_head *sh)
-{
- raid6_conf_t *conf = sh->raid_conf;
- unsigned long flags;
-
- spin_lock_irqsave(&conf->device_lock, flags);
- __release_stripe(conf, sh);
- spin_unlock_irqrestore(&conf->device_lock, flags);
-}
-
-static inline void remove_hash(struct stripe_head *sh)
-{
- PRINTK("remove_hash(), stripe %llu\n", (unsigned long long)sh->sector);
-
- hlist_del_init(&sh->hash);
-}
-
-static inline void insert_hash(raid6_conf_t *conf, struct stripe_head *sh)
-{
- struct hlist_head *hp = stripe_hash(conf, sh->sector);
-
- PRINTK("insert_hash(), stripe %llu\n", (unsigned long long)sh->sector);
-
- CHECK_DEVLOCK();
- hlist_add_head(&sh->hash, hp);
-}
-
-
-/* find an idle stripe, make sure it is unhashed, and return it. */
-static struct stripe_head *get_free_stripe(raid6_conf_t *conf)
-{
- struct stripe_head *sh = NULL;
- struct list_head *first;
-
- CHECK_DEVLOCK();
- if (list_empty(&conf->inactive_list))
- goto out;
- first = conf->inactive_list.next;
- sh = list_entry(first, struct stripe_head, lru);
- list_del_init(first);
- remove_hash(sh);
- atomic_inc(&conf->active_stripes);
-out:
- return sh;
-}
-
-static void shrink_buffers(struct stripe_head *sh, int num)
-{
- struct page *p;
- int i;
-
- for (i=0; i<num ; i++) {
- p = sh->dev[i].page;
- if (!p)
- continue;
- sh->dev[i].page = NULL;
- put_page(p);
- }
-}
-
-static int grow_buffers(struct stripe_head *sh, int num)
-{
- int i;
-
- for (i=0; i<num; i++) {
- struct page *page;
-
- if (!(page = alloc_page(GFP_KERNEL))) {
- return 1;
- }
- sh->dev[i].page = page;
- }
- return 0;
-}
-
-static void raid6_build_block (struct stripe_head *sh, int i);
-
-static void init_stripe(struct stripe_head *sh, sector_t sector, int pd_idx)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int disks = conf->raid_disks, i;
-
- BUG_ON(atomic_read(&sh->count) != 0);
- BUG_ON(test_bit(STRIPE_HANDLE, &sh->state));
-
- CHECK_DEVLOCK();
- PRINTK("init_stripe called, stripe %llu\n",
- (unsigned long long)sh->sector);
-
- remove_hash(sh);
-
- sh->sector = sector;
- sh->pd_idx = pd_idx;
- sh->state = 0;
-
- for (i=disks; i--; ) {
- struct r5dev *dev = &sh->dev[i];
-
- if (dev->toread || dev->towrite || dev->written ||
- test_bit(R5_LOCKED, &dev->flags)) {
- PRINTK("sector=%llx i=%d %p %p %p %d\n",
- (unsigned long long)sh->sector, i, dev->toread,
- dev->towrite, dev->written,
- test_bit(R5_LOCKED, &dev->flags));
- BUG();
- }
- dev->flags = 0;
- raid6_build_block(sh, i);
- }
- insert_hash(conf, sh);
-}
-
-static struct stripe_head *__find_stripe(raid6_conf_t *conf, sector_t sector)
-{
- struct stripe_head *sh;
- struct hlist_node *hn;
-
- CHECK_DEVLOCK();
- PRINTK("__find_stripe, sector %llu\n", (unsigned long long)sector);
- hlist_for_each_entry (sh, hn, stripe_hash(conf, sector), hash)
- if (sh->sector == sector)
- return sh;
- PRINTK("__stripe %llu not in cache\n", (unsigned long long)sector);
- return NULL;
-}
-
-static void unplug_slaves(mddev_t *mddev);
-
-static struct stripe_head *get_active_stripe(raid6_conf_t *conf, sector_t sector,
- int pd_idx, int noblock)
-{
- struct stripe_head *sh;
-
- PRINTK("get_stripe, sector %llu\n", (unsigned long long)sector);
-
- spin_lock_irq(&conf->device_lock);
-
- do {
- wait_event_lock_irq(conf->wait_for_stripe,
- conf->quiesce == 0,
- conf->device_lock, /* nothing */);
- sh = __find_stripe(conf, sector);
- if (!sh) {
- if (!conf->inactive_blocked)
- sh = get_free_stripe(conf);
- if (noblock && sh == NULL)
- break;
- if (!sh) {
- conf->inactive_blocked = 1;
- wait_event_lock_irq(conf->wait_for_stripe,
- !list_empty(&conf->inactive_list) &&
- (atomic_read(&conf->active_stripes)
- < (conf->max_nr_stripes *3/4)
- || !conf->inactive_blocked),
- conf->device_lock,
- unplug_slaves(conf->mddev);
- );
- conf->inactive_blocked = 0;
- } else
- init_stripe(sh, sector, pd_idx);
- } else {
- if (atomic_read(&sh->count)) {
- BUG_ON(!list_empty(&sh->lru));
- } else {
- if (!test_bit(STRIPE_HANDLE, &sh->state))
- atomic_inc(&conf->active_stripes);
- BUG_ON(list_empty(&sh->lru));
- list_del_init(&sh->lru);
- }
- }
- } while (sh == NULL);
-
- if (sh)
- atomic_inc(&sh->count);
-
- spin_unlock_irq(&conf->device_lock);
- return sh;
-}
-
-static int grow_one_stripe(raid6_conf_t *conf)
-{
- struct stripe_head *sh;
- sh = kmem_cache_alloc(conf->slab_cache, GFP_KERNEL);
- if (!sh)
- return 0;
- memset(sh, 0, sizeof(*sh) + (conf->raid_disks-1)*sizeof(struct r5dev));
- sh->raid_conf = conf;
- spin_lock_init(&sh->lock);
-
- if (grow_buffers(sh, conf->raid_disks)) {
- shrink_buffers(sh, conf->raid_disks);
- kmem_cache_free(conf->slab_cache, sh);
- return 0;
- }
- /* we just created an active stripe so... */
- atomic_set(&sh->count, 1);
- atomic_inc(&conf->active_stripes);
- INIT_LIST_HEAD(&sh->lru);
- release_stripe(sh);
- return 1;
-}
-
-static int grow_stripes(raid6_conf_t *conf, int num)
-{
- kmem_cache_t *sc;
- int devs = conf->raid_disks;
-
- sprintf(conf->cache_name[0], "raid6/%s", mdname(conf->mddev));
-
- sc = kmem_cache_create(conf->cache_name[0],
- sizeof(struct stripe_head)+(devs-1)*sizeof(struct r5dev),
- 0, 0, NULL, NULL);
- if (!sc)
- return 1;
- conf->slab_cache = sc;
- while (num--)
- if (!grow_one_stripe(conf))
- return 1;
- return 0;
-}
-
-static int drop_one_stripe(raid6_conf_t *conf)
-{
- struct stripe_head *sh;
- spin_lock_irq(&conf->device_lock);
- sh = get_free_stripe(conf);
- spin_unlock_irq(&conf->device_lock);
- if (!sh)
- return 0;
- BUG_ON(atomic_read(&sh->count));
- shrink_buffers(sh, conf->raid_disks);
- kmem_cache_free(conf->slab_cache, sh);
- atomic_dec(&conf->active_stripes);
- return 1;
-}
-
-static void shrink_stripes(raid6_conf_t *conf)
-{
- while (drop_one_stripe(conf))
- ;
-
- if (conf->slab_cache)
- kmem_cache_destroy(conf->slab_cache);
- conf->slab_cache = NULL;
-}
-
-static int raid6_end_read_request(struct bio * bi, unsigned int bytes_done,
- int error)
-{
- struct stripe_head *sh = bi->bi_private;
- raid6_conf_t *conf = sh->raid_conf;
- int disks = conf->raid_disks, i;
- int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
-
- if (bi->bi_size)
- return 1;
-
- for (i=0 ; i<disks; i++)
- if (bi == &sh->dev[i].req)
- break;
-
- PRINTK("end_read_request %llu/%d, count: %d, uptodate %d.\n",
- (unsigned long long)sh->sector, i, atomic_read(&sh->count),
- uptodate);
- if (i == disks) {
- BUG();
- return 0;
- }
-
- if (uptodate) {
-#if 0
- struct bio *bio;
- unsigned long flags;
- spin_lock_irqsave(&conf->device_lock, flags);
- /* we can return a buffer if we bypassed the cache or
- * if the top buffer is not in highmem. If there are
- * multiple buffers, leave the extra work to
- * handle_stripe
- */
- buffer = sh->bh_read[i];
- if (buffer &&
- (!PageHighMem(buffer->b_page)
- || buffer->b_page == bh->b_page )
- ) {
- sh->bh_read[i] = buffer->b_reqnext;
- buffer->b_reqnext = NULL;
- } else
- buffer = NULL;
- spin_unlock_irqrestore(&conf->device_lock, flags);
- if (sh->bh_page[i]==bh->b_page)
- set_buffer_uptodate(bh);
- if (buffer) {
- if (buffer->b_page != bh->b_page)
- memcpy(buffer->b_data, bh->b_data, bh->b_size);
- buffer->b_end_io(buffer, 1);
- }
-#else
- set_bit(R5_UPTODATE, &sh->dev[i].flags);
-#endif
- if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
- printk(KERN_INFO "raid6: read error corrected!!\n");
- clear_bit(R5_ReadError, &sh->dev[i].flags);
- clear_bit(R5_ReWrite, &sh->dev[i].flags);
- }
- if (atomic_read(&conf->disks[i].rdev->read_errors))
- atomic_set(&conf->disks[i].rdev->read_errors, 0);
- } else {
- int retry = 0;
- clear_bit(R5_UPTODATE, &sh->dev[i].flags);
- atomic_inc(&conf->disks[i].rdev->read_errors);
- if (conf->mddev->degraded)
- printk(KERN_WARNING "raid6: read error not correctable.\n");
- else if (test_bit(R5_ReWrite, &sh->dev[i].flags))
- /* Oh, no!!! */
- printk(KERN_WARNING "raid6: read error NOT corrected!!\n");
- else if (atomic_read(&conf->disks[i].rdev->read_errors)
- > conf->max_nr_stripes)
- printk(KERN_WARNING
- "raid6: Too many read errors, failing device.\n");
- else
- retry = 1;
- if (retry)
- set_bit(R5_ReadError, &sh->dev[i].flags);
- else {
- clear_bit(R5_ReadError, &sh->dev[i].flags);
- clear_bit(R5_ReWrite, &sh->dev[i].flags);
- md_error(conf->mddev, conf->disks[i].rdev);
- }
- }
- rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
-#if 0
- /* must restore b_page before unlocking buffer... */
- if (sh->bh_page[i] != bh->b_page) {
- bh->b_page = sh->bh_page[i];
- bh->b_data = page_address(bh->b_page);
- clear_buffer_uptodate(bh);
- }
-#endif
- clear_bit(R5_LOCKED, &sh->dev[i].flags);
- set_bit(STRIPE_HANDLE, &sh->state);
- release_stripe(sh);
- return 0;
-}
-
-static int raid6_end_write_request (struct bio *bi, unsigned int bytes_done,
- int error)
-{
- struct stripe_head *sh = bi->bi_private;
- raid6_conf_t *conf = sh->raid_conf;
- int disks = conf->raid_disks, i;
- unsigned long flags;
- int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
-
- if (bi->bi_size)
- return 1;
-
- for (i=0 ; i<disks; i++)
- if (bi == &sh->dev[i].req)
- break;
-
- PRINTK("end_write_request %llu/%d, count %d, uptodate: %d.\n",
- (unsigned long long)sh->sector, i, atomic_read(&sh->count),
- uptodate);
- if (i == disks) {
- BUG();
- return 0;
- }
-
- spin_lock_irqsave(&conf->device_lock, flags);
- if (!uptodate)
- md_error(conf->mddev, conf->disks[i].rdev);
-
- rdev_dec_pending(conf->disks[i].rdev, conf->mddev);
-
- clear_bit(R5_LOCKED, &sh->dev[i].flags);
- set_bit(STRIPE_HANDLE, &sh->state);
- __release_stripe(conf, sh);
- spin_unlock_irqrestore(&conf->device_lock, flags);
- return 0;
-}
-
-
-static sector_t compute_blocknr(struct stripe_head *sh, int i);
-
-static void raid6_build_block (struct stripe_head *sh, int i)
-{
- struct r5dev *dev = &sh->dev[i];
- int pd_idx = sh->pd_idx;
- int qd_idx = raid6_next_disk(pd_idx, sh->raid_conf->raid_disks);
-
- bio_init(&dev->req);
- dev->req.bi_io_vec = &dev->vec;
- dev->req.bi_vcnt++;
- dev->req.bi_max_vecs++;
- dev->vec.bv_page = dev->page;
- dev->vec.bv_len = STRIPE_SIZE;
- dev->vec.bv_offset = 0;
-
- dev->req.bi_sector = sh->sector;
- dev->req.bi_private = sh;
-
- dev->flags = 0;
- if (i != pd_idx && i != qd_idx)
- dev->sector = compute_blocknr(sh, i);
-}
-
-static void error(mddev_t *mddev, mdk_rdev_t *rdev)
-{
- char b[BDEVNAME_SIZE];
- raid6_conf_t *conf = (raid6_conf_t *) mddev->private;
- PRINTK("raid6: error called\n");
-
- if (!test_bit(Faulty, &rdev->flags)) {
- mddev->sb_dirty = 1;
- if (test_bit(In_sync, &rdev->flags)) {
- conf->working_disks--;
- mddev->degraded++;
- conf->failed_disks++;
- clear_bit(In_sync, &rdev->flags);
- /*
- * if recovery was running, make sure it aborts.
- */
- set_bit(MD_RECOVERY_ERR, &mddev->recovery);
- }
- set_bit(Faulty, &rdev->flags);
- printk (KERN_ALERT
- "raid6: Disk failure on %s, disabling device."
- " Operation continuing on %d devices\n",
- bdevname(rdev->bdev,b), conf->working_disks);
- }
-}
-
-/*
- * Input: a 'big' sector number,
- * Output: index of the data and parity disk, and the sector # in them.
- */
-static sector_t raid6_compute_sector(sector_t r_sector, unsigned int raid_disks,
- unsigned int data_disks, unsigned int * dd_idx,
- unsigned int * pd_idx, raid6_conf_t *conf)
-{
- long stripe;
- unsigned long chunk_number;
- unsigned int chunk_offset;
- sector_t new_sector;
- int sectors_per_chunk = conf->chunk_size >> 9;
-
- /* First compute the information on this sector */
-
- /*
- * Compute the chunk number and the sector offset inside the chunk
- */
- chunk_offset = sector_div(r_sector, sectors_per_chunk);
- chunk_number = r_sector;
- if ( r_sector != chunk_number ) {
- printk(KERN_CRIT "raid6: ERROR: r_sector = %llu, chunk_number = %lu\n",
- (unsigned long long)r_sector, (unsigned long)chunk_number);
- BUG();
- }
-
- /*
- * Compute the stripe number
- */
- stripe = chunk_number / data_disks;
-
- /*
- * Compute the data disk and parity disk indexes inside the stripe
- */
- *dd_idx = chunk_number % data_disks;
-
- /*
- * Select the parity disk based on the user selected algorithm.
- */
-
- /**** FIX THIS ****/
- switch (conf->algorithm) {
- case ALGORITHM_LEFT_ASYMMETRIC:
- *pd_idx = raid_disks - 1 - (stripe % raid_disks);
- if (*pd_idx == raid_disks-1)
- (*dd_idx)++; /* Q D D D P */
- else if (*dd_idx >= *pd_idx)
- (*dd_idx) += 2; /* D D P Q D */
- break;
- case ALGORITHM_RIGHT_ASYMMETRIC:
- *pd_idx = stripe % raid_disks;
- if (*pd_idx == raid_disks-1)
- (*dd_idx)++; /* Q D D D P */
- else if (*dd_idx >= *pd_idx)
- (*dd_idx) += 2; /* D D P Q D */
- break;
- case ALGORITHM_LEFT_SYMMETRIC:
- *pd_idx = raid_disks - 1 - (stripe % raid_disks);
- *dd_idx = (*pd_idx + 2 + *dd_idx) % raid_disks;
- break;
- case ALGORITHM_RIGHT_SYMMETRIC:
- *pd_idx = stripe % raid_disks;
- *dd_idx = (*pd_idx + 2 + *dd_idx) % raid_disks;
- break;
- default:
- printk (KERN_CRIT "raid6: unsupported algorithm %d\n",
- conf->algorithm);
- }
-
- PRINTK("raid6: chunk_number = %lu, pd_idx = %u, dd_idx = %u\n",
- chunk_number, *pd_idx, *dd_idx);
-
- /*
- * Finally, compute the new sector number
- */
- new_sector = (sector_t) stripe * sectors_per_chunk + chunk_offset;
- return new_sector;
-}
-
-
-static sector_t compute_blocknr(struct stripe_head *sh, int i)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int raid_disks = conf->raid_disks, data_disks = raid_disks - 2;
- sector_t new_sector = sh->sector, check;
- int sectors_per_chunk = conf->chunk_size >> 9;
- sector_t stripe;
- int chunk_offset;
- int chunk_number, dummy1, dummy2, dd_idx = i;
- sector_t r_sector;
- int i0 = i;
-
- chunk_offset = sector_div(new_sector, sectors_per_chunk);
- stripe = new_sector;
- if ( new_sector != stripe ) {
- printk(KERN_CRIT "raid6: ERROR: new_sector = %llu, stripe = %lu\n",
- (unsigned long long)new_sector, (unsigned long)stripe);
- BUG();
- }
-
- switch (conf->algorithm) {
- case ALGORITHM_LEFT_ASYMMETRIC:
- case ALGORITHM_RIGHT_ASYMMETRIC:
- if (sh->pd_idx == raid_disks-1)
- i--; /* Q D D D P */
- else if (i > sh->pd_idx)
- i -= 2; /* D D P Q D */
- break;
- case ALGORITHM_LEFT_SYMMETRIC:
- case ALGORITHM_RIGHT_SYMMETRIC:
- if (sh->pd_idx == raid_disks-1)
- i--; /* Q D D D P */
- else {
- /* D D P Q D */
- if (i < sh->pd_idx)
- i += raid_disks;
- i -= (sh->pd_idx + 2);
- }
- break;
- default:
- printk (KERN_CRIT "raid6: unsupported algorithm %d\n",
- conf->algorithm);
- }
-
- PRINTK("raid6: compute_blocknr: pd_idx = %u, i0 = %u, i = %u\n", sh->pd_idx, i0, i);
-
- chunk_number = stripe * data_disks + i;
- r_sector = (sector_t)chunk_number * sectors_per_chunk + chunk_offset;
-
- check = raid6_compute_sector (r_sector, raid_disks, data_disks, &dummy1, &dummy2, conf);
- if (check != sh->sector || dummy1 != dd_idx || dummy2 != sh->pd_idx) {
- printk(KERN_CRIT "raid6: compute_blocknr: map not correct\n");
- return 0;
- }
- return r_sector;
-}
-
-
-
-/*
- * Copy data between a page in the stripe cache, and one or more bion
- * The page could align with the middle of the bio, or there could be
- * several bion, each with several bio_vecs, which cover part of the page
- * Multiple bion are linked together on bi_next. There may be extras
- * at the end of this list. We ignore them.
- */
-static void copy_data(int frombio, struct bio *bio,
- struct page *page,
- sector_t sector)
-{
- char *pa = page_address(page);
- struct bio_vec *bvl;
- int i;
- int page_offset;
-
- if (bio->bi_sector >= sector)
- page_offset = (signed)(bio->bi_sector - sector) * 512;
- else
- page_offset = (signed)(sector - bio->bi_sector) * -512;
- bio_for_each_segment(bvl, bio, i) {
- int len = bio_iovec_idx(bio,i)->bv_len;
- int clen;
- int b_offset = 0;
-
- if (page_offset < 0) {
- b_offset = -page_offset;
- page_offset += b_offset;
- len -= b_offset;
- }
-
- if (len > 0 && page_offset + len > STRIPE_SIZE)
- clen = STRIPE_SIZE - page_offset;
- else clen = len;
-
- if (clen > 0) {
- char *ba = __bio_kmap_atomic(bio, i, KM_USER0);
- if (frombio)
- memcpy(pa+page_offset, ba+b_offset, clen);
- else
- memcpy(ba+b_offset, pa+page_offset, clen);
- __bio_kunmap_atomic(ba, KM_USER0);
- }
- if (clen < len) /* hit end of page */
- break;
- page_offset += len;
- }
-}
-
-#define check_xor() do { \
- if (count == MAX_XOR_BLOCKS) { \
- xor_block(count, STRIPE_SIZE, ptr); \
- count = 1; \
- } \
- } while(0)
-
-/* Compute P and Q syndromes */
-static void compute_parity(struct stripe_head *sh, int method)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int i, pd_idx = sh->pd_idx, qd_idx, d0_idx, disks = conf->raid_disks, count;
- struct bio *chosen;
- /**** FIX THIS: This could be very bad if disks is close to 256 ****/
- void *ptrs[disks];
-
- qd_idx = raid6_next_disk(pd_idx, disks);
- d0_idx = raid6_next_disk(qd_idx, disks);
-
- PRINTK("compute_parity, stripe %llu, method %d\n",
- (unsigned long long)sh->sector, method);
-
- switch(method) {
- case READ_MODIFY_WRITE:
- BUG(); /* READ_MODIFY_WRITE N/A for RAID-6 */
- case RECONSTRUCT_WRITE:
- for (i= disks; i-- ;)
- if ( i != pd_idx && i != qd_idx && sh->dev[i].towrite ) {
- chosen = sh->dev[i].towrite;
- sh->dev[i].towrite = NULL;
-
- if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
- wake_up(&conf->wait_for_overlap);
-
- BUG_ON(sh->dev[i].written);
- sh->dev[i].written = chosen;
- }
- break;
- case CHECK_PARITY:
- BUG(); /* Not implemented yet */
- }
-
- for (i = disks; i--;)
- if (sh->dev[i].written) {
- sector_t sector = sh->dev[i].sector;
- struct bio *wbi = sh->dev[i].written;
- while (wbi && wbi->bi_sector < sector + STRIPE_SECTORS) {
- copy_data(1, wbi, sh->dev[i].page, sector);
- wbi = r5_next_bio(wbi, sector);
- }
-
- set_bit(R5_LOCKED, &sh->dev[i].flags);
- set_bit(R5_UPTODATE, &sh->dev[i].flags);
- }
-
-// switch(method) {
-// case RECONSTRUCT_WRITE:
-// case CHECK_PARITY:
-// case UPDATE_PARITY:
- /* Note that unlike RAID-5, the ordering of the disks matters greatly. */
- /* FIX: Is this ordering of drives even remotely optimal? */
- count = 0;
- i = d0_idx;
- do {
- ptrs[count++] = page_address(sh->dev[i].page);
- if (count <= disks-2 && !test_bit(R5_UPTODATE, &sh->dev[i].flags))
- printk("block %d/%d not uptodate on parity calc\n", i,count);
- i = raid6_next_disk(i, disks);
- } while ( i != d0_idx );
-// break;
-// }
-
- raid6_call.gen_syndrome(disks, STRIPE_SIZE, ptrs);
-
- switch(method) {
- case RECONSTRUCT_WRITE:
- set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
- set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags);
- set_bit(R5_LOCKED, &sh->dev[pd_idx].flags);
- set_bit(R5_LOCKED, &sh->dev[qd_idx].flags);
- break;
- case UPDATE_PARITY:
- set_bit(R5_UPTODATE, &sh->dev[pd_idx].flags);
- set_bit(R5_UPTODATE, &sh->dev[qd_idx].flags);
- break;
- }
-}
-
-/* Compute one missing block */
-static void compute_block_1(struct stripe_head *sh, int dd_idx, int nozero)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int i, count, disks = conf->raid_disks;
- void *ptr[MAX_XOR_BLOCKS], *p;
- int pd_idx = sh->pd_idx;
- int qd_idx = raid6_next_disk(pd_idx, disks);
-
- PRINTK("compute_block_1, stripe %llu, idx %d\n",
- (unsigned long long)sh->sector, dd_idx);
-
- if ( dd_idx == qd_idx ) {
- /* We're actually computing the Q drive */
- compute_parity(sh, UPDATE_PARITY);
- } else {
- ptr[0] = page_address(sh->dev[dd_idx].page);
- if (!nozero) memset(ptr[0], 0, STRIPE_SIZE);
- count = 1;
- for (i = disks ; i--; ) {
- if (i == dd_idx || i == qd_idx)
- continue;
- p = page_address(sh->dev[i].page);
- if (test_bit(R5_UPTODATE, &sh->dev[i].flags))
- ptr[count++] = p;
- else
- printk("compute_block() %d, stripe %llu, %d"
- " not present\n", dd_idx,
- (unsigned long long)sh->sector, i);
-
- check_xor();
- }
- if (count != 1)
- xor_block(count, STRIPE_SIZE, ptr);
- if (!nozero) set_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
- else clear_bit(R5_UPTODATE, &sh->dev[dd_idx].flags);
- }
-}
-
-/* Compute two missing blocks */
-static void compute_block_2(struct stripe_head *sh, int dd_idx1, int dd_idx2)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int i, count, disks = conf->raid_disks;
- int pd_idx = sh->pd_idx;
- int qd_idx = raid6_next_disk(pd_idx, disks);
- int d0_idx = raid6_next_disk(qd_idx, disks);
- int faila, failb;
-
- /* faila and failb are disk numbers relative to d0_idx */
- /* pd_idx become disks-2 and qd_idx become disks-1 */
- faila = (dd_idx1 < d0_idx) ? dd_idx1+(disks-d0_idx) : dd_idx1-d0_idx;
- failb = (dd_idx2 < d0_idx) ? dd_idx2+(disks-d0_idx) : dd_idx2-d0_idx;
-
- BUG_ON(faila == failb);
- if ( failb < faila ) { int tmp = faila; faila = failb; failb = tmp; }
-
- PRINTK("compute_block_2, stripe %llu, idx %d,%d (%d,%d)\n",
- (unsigned long long)sh->sector, dd_idx1, dd_idx2, faila, failb);
-
- if ( failb == disks-1 ) {
- /* Q disk is one of the missing disks */
- if ( faila == disks-2 ) {
- /* Missing P+Q, just recompute */
- compute_parity(sh, UPDATE_PARITY);
- return;
- } else {
- /* We're missing D+Q; recompute D from P */
- compute_block_1(sh, (dd_idx1 == qd_idx) ? dd_idx2 : dd_idx1, 0);
- compute_parity(sh, UPDATE_PARITY); /* Is this necessary? */
- return;
- }
- }
-
- /* We're missing D+P or D+D; build pointer table */
- {
- /**** FIX THIS: This could be very bad if disks is close to 256 ****/
- void *ptrs[disks];
-
- count = 0;
- i = d0_idx;
- do {
- ptrs[count++] = page_address(sh->dev[i].page);
- i = raid6_next_disk(i, disks);
- if (i != dd_idx1 && i != dd_idx2 &&
- !test_bit(R5_UPTODATE, &sh->dev[i].flags))
- printk("compute_2 with missing block %d/%d\n", count, i);
- } while ( i != d0_idx );
-
- if ( failb == disks-2 ) {
- /* We're missing D+P. */
- raid6_datap_recov(disks, STRIPE_SIZE, faila, ptrs);
- } else {
- /* We're missing D+D. */
- raid6_2data_recov(disks, STRIPE_SIZE, faila, failb, ptrs);
- }
-
- /* Both the above update both missing blocks */
- set_bit(R5_UPTODATE, &sh->dev[dd_idx1].flags);
- set_bit(R5_UPTODATE, &sh->dev[dd_idx2].flags);
- }
-}
-
-
-/*
- * Each stripe/dev can have one or more bion attached.
- * toread/towrite point to the first in a chain.
- * The bi_next chain must be in order.
- */
-static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, int forwrite)
-{
- struct bio **bip;
- raid6_conf_t *conf = sh->raid_conf;
- int firstwrite=0;
-
- PRINTK("adding bh b#%llu to stripe s#%llu\n",
- (unsigned long long)bi->bi_sector,
- (unsigned long long)sh->sector);
-
-
- spin_lock(&sh->lock);
- spin_lock_irq(&conf->device_lock);
- if (forwrite) {
- bip = &sh->dev[dd_idx].towrite;
- if (*bip == NULL && sh->dev[dd_idx].written == NULL)
- firstwrite = 1;
- } else
- bip = &sh->dev[dd_idx].toread;
- while (*bip && (*bip)->bi_sector < bi->bi_sector) {
- if ((*bip)->bi_sector + ((*bip)->bi_size >> 9) > bi->bi_sector)
- goto overlap;
- bip = &(*bip)->bi_next;
- }
- if (*bip && (*bip)->bi_sector < bi->bi_sector + ((bi->bi_size)>>9))
- goto overlap;
-
- BUG_ON(*bip && bi->bi_next && (*bip) != bi->bi_next);
- if (*bip)
- bi->bi_next = *bip;
- *bip = bi;
- bi->bi_phys_segments ++;
- spin_unlock_irq(&conf->device_lock);
- spin_unlock(&sh->lock);
-
- PRINTK("added bi b#%llu to stripe s#%llu, disk %d.\n",
- (unsigned long long)bi->bi_sector,
- (unsigned long long)sh->sector, dd_idx);
-
- if (conf->mddev->bitmap && firstwrite) {
- sh->bm_seq = conf->seq_write;
- bitmap_startwrite(conf->mddev->bitmap, sh->sector,
- STRIPE_SECTORS, 0);
- set_bit(STRIPE_BIT_DELAY, &sh->state);
- }
-
- if (forwrite) {
- /* check if page is covered */
- sector_t sector = sh->dev[dd_idx].sector;
- for (bi=sh->dev[dd_idx].towrite;
- sector < sh->dev[dd_idx].sector + STRIPE_SECTORS &&
- bi && bi->bi_sector <= sector;
- bi = r5_next_bio(bi, sh->dev[dd_idx].sector)) {
- if (bi->bi_sector + (bi->bi_size>>9) >= sector)
- sector = bi->bi_sector + (bi->bi_size>>9);
- }
- if (sector >= sh->dev[dd_idx].sector + STRIPE_SECTORS)
- set_bit(R5_OVERWRITE, &sh->dev[dd_idx].flags);
- }
- return 1;
-
- overlap:
- set_bit(R5_Overlap, &sh->dev[dd_idx].flags);
- spin_unlock_irq(&conf->device_lock);
- spin_unlock(&sh->lock);
- return 0;
-}
-
-
-static int page_is_zero(struct page *p)
-{
- char *a = page_address(p);
- return ((*(u32*)a) == 0 &&
- memcmp(a, a+4, STRIPE_SIZE-4)==0);
-}
-/*
- * handle_stripe - do things to a stripe.
- *
- * We lock the stripe and then examine the state of various bits
- * to see what needs to be done.
- * Possible results:
- * return some read request which now have data
- * return some write requests which are safely on disc
- * schedule a read on some buffers
- * schedule a write of some buffers
- * return confirmation of parity correctness
- *
- * Parity calculations are done inside the stripe lock
- * buffers are taken off read_list or write_list, and bh_cache buffers
- * get BH_Lock set before the stripe lock is released.
- *
- */
-
-static void handle_stripe(struct stripe_head *sh, struct page *tmp_page)
-{
- raid6_conf_t *conf = sh->raid_conf;
- int disks = conf->raid_disks;
- struct bio *return_bi= NULL;
- struct bio *bi;
- int i;
- int syncing;
- int locked=0, uptodate=0, to_read=0, to_write=0, failed=0, written=0;
- int non_overwrite = 0;
- int failed_num[2] = {0, 0};
- struct r5dev *dev, *pdev, *qdev;
- int pd_idx = sh->pd_idx;
- int qd_idx = raid6_next_disk(pd_idx, disks);
- int p_failed, q_failed;
-
- PRINTK("handling stripe %llu, state=%#lx cnt=%d, pd_idx=%d, qd_idx=%d\n",
- (unsigned long long)sh->sector, sh->state, atomic_read(&sh->count),
- pd_idx, qd_idx);
-
- spin_lock(&sh->lock);
- clear_bit(STRIPE_HANDLE, &sh->state);
- clear_bit(STRIPE_DELAYED, &sh->state);
-
- syncing = test_bit(STRIPE_SYNCING, &sh->state);
- /* Now to look around and see what can be done */
-
- rcu_read_lock();
- for (i=disks; i--; ) {
- mdk_rdev_t *rdev;
- dev = &sh->dev[i];
- clear_bit(R5_Insync, &dev->flags);
-
- PRINTK("check %d: state 0x%lx read %p write %p written %p\n",
- i, dev->flags, dev->toread, dev->towrite, dev->written);
- /* maybe we can reply to a read */
- if (test_bit(R5_UPTODATE, &dev->flags) && dev->toread) {
- struct bio *rbi, *rbi2;
- PRINTK("Return read for disc %d\n", i);
- spin_lock_irq(&conf->device_lock);
- rbi = dev->toread;
- dev->toread = NULL;
- if (test_and_clear_bit(R5_Overlap, &dev->flags))
- wake_up(&conf->wait_for_overlap);
- spin_unlock_irq(&conf->device_lock);
- while (rbi && rbi->bi_sector < dev->sector + STRIPE_SECTORS) {
- copy_data(0, rbi, dev->page, dev->sector);
- rbi2 = r5_next_bio(rbi, dev->sector);
- spin_lock_irq(&conf->device_lock);
- if (--rbi->bi_phys_segments == 0) {
- rbi->bi_next = return_bi;
- return_bi = rbi;
- }
- spin_unlock_irq(&conf->device_lock);
- rbi = rbi2;
- }
- }
-
- /* now count some things */
- if (test_bit(R5_LOCKED, &dev->flags)) locked++;
- if (test_bit(R5_UPTODATE, &dev->flags)) uptodate++;
-
-
- if (dev->toread) to_read++;
- if (dev->towrite) {
- to_write++;
- if (!test_bit(R5_OVERWRITE, &dev->flags))
- non_overwrite++;
- }
- if (dev->written) written++;
- rdev = rcu_dereference(conf->disks[i].rdev);
- if (!rdev || !test_bit(In_sync, &rdev->flags)) {
- /* The ReadError flag will just be confusing now */
- clear_bit(R5_ReadError, &dev->flags);
- clear_bit(R5_ReWrite, &dev->flags);
- }
- if (!rdev || !test_bit(In_sync, &rdev->flags)
- || test_bit(R5_ReadError, &dev->flags)) {
- if ( failed < 2 )
- failed_num[failed] = i;
- failed++;
- } else
- set_bit(R5_Insync, &dev->flags);
- }
- rcu_read_unlock();
- PRINTK("locked=%d uptodate=%d to_read=%d"
- " to_write=%d failed=%d failed_num=%d,%d\n",
- locked, uptodate, to_read, to_write, failed,
- failed_num[0], failed_num[1]);
- /* check if the array has lost >2 devices and, if so, some requests might
- * need to be failed
- */
- if (failed > 2 && to_read+to_write+written) {
- for (i=disks; i--; ) {
- int bitmap_end = 0;
-
- if (test_bit(R5_ReadError, &sh->dev[i].flags)) {
- mdk_rdev_t *rdev;
- rcu_read_lock();
- rdev = rcu_dereference(conf->disks[i].rdev);
- if (rdev && test_bit(In_sync, &rdev->flags))
- /* multiple read failures in one stripe */
- md_error(conf->mddev, rdev);
- rcu_read_unlock();
- }
-
- spin_lock_irq(&conf->device_lock);
- /* fail all writes first */
- bi = sh->dev[i].towrite;
- sh->dev[i].towrite = NULL;
- if (bi) { to_write--; bitmap_end = 1; }
-
- if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
- wake_up(&conf->wait_for_overlap);
-
- while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS){
- struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
- clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (--bi->bi_phys_segments == 0) {
- md_write_end(conf->mddev);
- bi->bi_next = return_bi;
- return_bi = bi;
- }
- bi = nextbi;
- }
- /* and fail all 'written' */
- bi = sh->dev[i].written;
- sh->dev[i].written = NULL;
- if (bi) bitmap_end = 1;
- while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS) {
- struct bio *bi2 = r5_next_bio(bi, sh->dev[i].sector);
- clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (--bi->bi_phys_segments == 0) {
- md_write_end(conf->mddev);
- bi->bi_next = return_bi;
- return_bi = bi;
- }
- bi = bi2;
- }
-
- /* fail any reads if this device is non-operational */
- if (!test_bit(R5_Insync, &sh->dev[i].flags) ||
- test_bit(R5_ReadError, &sh->dev[i].flags)) {
- bi = sh->dev[i].toread;
- sh->dev[i].toread = NULL;
- if (test_and_clear_bit(R5_Overlap, &sh->dev[i].flags))
- wake_up(&conf->wait_for_overlap);
- if (bi) to_read--;
- while (bi && bi->bi_sector < sh->dev[i].sector + STRIPE_SECTORS){
- struct bio *nextbi = r5_next_bio(bi, sh->dev[i].sector);
- clear_bit(BIO_UPTODATE, &bi->bi_flags);
- if (--bi->bi_phys_segments == 0) {
- bi->bi_next = return_bi;
- return_bi = bi;
- }
- bi = nextbi;
- }
- }
- spin_unlock_irq(&conf->device_lock);
- if (bitmap_end)
- bitmap_endwrite(conf->mddev->bitmap, sh->sector,
- STRIPE_SECTORS, 0, 0);
- }
- }
- if (failed > 2 && syncing) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,0);
- clear_bit(STRIPE_SYNCING, &sh->state);
- syncing = 0;
- }
-
- /*
- * might be able to return some write requests if the parity blocks
- * are safe, or on a failed drive
- */
- pdev = &sh->dev[pd_idx];
- p_failed = (failed >= 1 && failed_num[0] == pd_idx)
- || (failed >= 2 && failed_num[1] == pd_idx);
- qdev = &sh->dev[qd_idx];
- q_failed = (failed >= 1 && failed_num[0] == qd_idx)
- || (failed >= 2 && failed_num[1] == qd_idx);
-
- if ( written &&
- ( p_failed || ((test_bit(R5_Insync, &pdev->flags)
- && !test_bit(R5_LOCKED, &pdev->flags)
- && test_bit(R5_UPTODATE, &pdev->flags))) ) &&
- ( q_failed || ((test_bit(R5_Insync, &qdev->flags)
- && !test_bit(R5_LOCKED, &qdev->flags)
- && test_bit(R5_UPTODATE, &qdev->flags))) ) ) {
- /* any written block on an uptodate or failed drive can be
- * returned. Note that if we 'wrote' to a failed drive,
- * it will be UPTODATE, but never LOCKED, so we don't need
- * to test 'failed' directly.
- */
- for (i=disks; i--; )
- if (sh->dev[i].written) {
- dev = &sh->dev[i];
- if (!test_bit(R5_LOCKED, &dev->flags) &&
- test_bit(R5_UPTODATE, &dev->flags) ) {
- /* We can return any write requests */
- int bitmap_end = 0;
- struct bio *wbi, *wbi2;
- PRINTK("Return write for stripe %llu disc %d\n",
- (unsigned long long)sh->sector, i);
- spin_lock_irq(&conf->device_lock);
- wbi = dev->written;
- dev->written = NULL;
- while (wbi && wbi->bi_sector < dev->sector + STRIPE_SECTORS) {
- wbi2 = r5_next_bio(wbi, dev->sector);
- if (--wbi->bi_phys_segments == 0) {
- md_write_end(conf->mddev);
- wbi->bi_next = return_bi;
- return_bi = wbi;
- }
- wbi = wbi2;
- }
- if (dev->towrite == NULL)
- bitmap_end = 1;
- spin_unlock_irq(&conf->device_lock);
- if (bitmap_end)
- bitmap_endwrite(conf->mddev->bitmap, sh->sector,
- STRIPE_SECTORS,
- !test_bit(STRIPE_DEGRADED, &sh->state), 0);
- }
- }
- }
-
- /* Now we might consider reading some blocks, either to check/generate
- * parity, or to satisfy requests
- * or to load a block that is being partially written.
- */
- if (to_read || non_overwrite || (to_write && failed) || (syncing && (uptodate < disks))) {
- for (i=disks; i--;) {
- dev = &sh->dev[i];
- if (!test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) &&
- (dev->toread ||
- (dev->towrite && !test_bit(R5_OVERWRITE, &dev->flags)) ||
- syncing ||
- (failed >= 1 && (sh->dev[failed_num[0]].toread || to_write)) ||
- (failed >= 2 && (sh->dev[failed_num[1]].toread || to_write))
- )
- ) {
- /* we would like to get this block, possibly
- * by computing it, but we might not be able to
- */
- if (uptodate == disks-1) {
- PRINTK("Computing stripe %llu block %d\n",
- (unsigned long long)sh->sector, i);
- compute_block_1(sh, i, 0);
- uptodate++;
- } else if ( uptodate == disks-2 && failed >= 2 ) {
- /* Computing 2-failure is *very* expensive; only do it if failed >= 2 */
- int other;
- for (other=disks; other--;) {
- if ( other == i )
- continue;
- if ( !test_bit(R5_UPTODATE, &sh->dev[other].flags) )
- break;
- }
- BUG_ON(other < 0);
- PRINTK("Computing stripe %llu blocks %d,%d\n",
- (unsigned long long)sh->sector, i, other);
- compute_block_2(sh, i, other);
- uptodate += 2;
- } else if (test_bit(R5_Insync, &dev->flags)) {
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantread, &dev->flags);
-#if 0
- /* if I am just reading this block and we don't have
- a failed drive, or any pending writes then sidestep the cache */
- if (sh->bh_read[i] && !sh->bh_read[i]->b_reqnext &&
- ! syncing && !failed && !to_write) {
- sh->bh_cache[i]->b_page = sh->bh_read[i]->b_page;
- sh->bh_cache[i]->b_data = sh->bh_read[i]->b_data;
- }
-#endif
- locked++;
- PRINTK("Reading block %d (sync=%d)\n",
- i, syncing);
- }
- }
- }
- set_bit(STRIPE_HANDLE, &sh->state);
- }
-
- /* now to consider writing and what else, if anything should be read */
- if (to_write) {
- int rcw=0, must_compute=0;
- for (i=disks ; i--;) {
- dev = &sh->dev[i];
- /* Would I have to read this buffer for reconstruct_write */
- if (!test_bit(R5_OVERWRITE, &dev->flags)
- && i != pd_idx && i != qd_idx
- && (!test_bit(R5_LOCKED, &dev->flags)
-#if 0
- || sh->bh_page[i] != bh->b_page
-#endif
- ) &&
- !test_bit(R5_UPTODATE, &dev->flags)) {
- if (test_bit(R5_Insync, &dev->flags)) rcw++;
- else {
- PRINTK("raid6: must_compute: disk %d flags=%#lx\n", i, dev->flags);
- must_compute++;
- }
- }
- }
- PRINTK("for sector %llu, rcw=%d, must_compute=%d\n",
- (unsigned long long)sh->sector, rcw, must_compute);
- set_bit(STRIPE_HANDLE, &sh->state);
-
- if (rcw > 0)
- /* want reconstruct write, but need to get some data */
- for (i=disks; i--;) {
- dev = &sh->dev[i];
- if (!test_bit(R5_OVERWRITE, &dev->flags)
- && !(failed == 0 && (i == pd_idx || i == qd_idx))
- && !test_bit(R5_LOCKED, &dev->flags) && !test_bit(R5_UPTODATE, &dev->flags) &&
- test_bit(R5_Insync, &dev->flags)) {
- if (test_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- {
- PRINTK("Read_old stripe %llu block %d for Reconstruct\n",
- (unsigned long long)sh->sector, i);
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantread, &dev->flags);
- locked++;
- } else {
- PRINTK("Request delayed stripe %llu block %d for Reconstruct\n",
- (unsigned long long)sh->sector, i);
- set_bit(STRIPE_DELAYED, &sh->state);
- set_bit(STRIPE_HANDLE, &sh->state);
- }
- }
- }
- /* now if nothing is locked, and if we have enough data, we can start a write request */
- if (locked == 0 && rcw == 0 &&
- !test_bit(STRIPE_BIT_DELAY, &sh->state)) {
- if ( must_compute > 0 ) {
- /* We have failed blocks and need to compute them */
- switch ( failed ) {
- case 0: BUG();
- case 1: compute_block_1(sh, failed_num[0], 0); break;
- case 2: compute_block_2(sh, failed_num[0], failed_num[1]); break;
- default: BUG(); /* This request should have been failed? */
- }
- }
-
- PRINTK("Computing parity for stripe %llu\n", (unsigned long long)sh->sector);
- compute_parity(sh, RECONSTRUCT_WRITE);
- /* now every locked buffer is ready to be written */
- for (i=disks; i--;)
- if (test_bit(R5_LOCKED, &sh->dev[i].flags)) {
- PRINTK("Writing stripe %llu block %d\n",
- (unsigned long long)sh->sector, i);
- locked++;
- set_bit(R5_Wantwrite, &sh->dev[i].flags);
- }
- /* after a RECONSTRUCT_WRITE, the stripe MUST be in-sync */
- set_bit(STRIPE_INSYNC, &sh->state);
-
- if (test_and_clear_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) {
- atomic_dec(&conf->preread_active_stripes);
- if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD)
- md_wakeup_thread(conf->mddev->thread);
- }
- }
- }
-
- /* maybe we need to check and possibly fix the parity for this stripe
- * Any reads will already have been scheduled, so we just see if enough data
- * is available
- */
- if (syncing && locked == 0 && !test_bit(STRIPE_INSYNC, &sh->state)) {
- int update_p = 0, update_q = 0;
- struct r5dev *dev;
-
- set_bit(STRIPE_HANDLE, &sh->state);
-
- BUG_ON(failed>2);
- BUG_ON(uptodate < disks);
- /* Want to check and possibly repair P and Q.
- * However there could be one 'failed' device, in which
- * case we can only check one of them, possibly using the
- * other to generate missing data
- */
-
- /* If !tmp_page, we cannot do the calculations,
- * but as we have set STRIPE_HANDLE, we will soon be called
- * by stripe_handle with a tmp_page - just wait until then.
- */
- if (tmp_page) {
- if (failed == q_failed) {
- /* The only possible failed device holds 'Q', so it makes
- * sense to check P (If anything else were failed, we would
- * have used P to recreate it).
- */
- compute_block_1(sh, pd_idx, 1);
- if (!page_is_zero(sh->dev[pd_idx].page)) {
- compute_block_1(sh,pd_idx,0);
- update_p = 1;
- }
- }
- if (!q_failed && failed < 2) {
- /* q is not failed, and we didn't use it to generate
- * anything, so it makes sense to check it
- */
- memcpy(page_address(tmp_page),
- page_address(sh->dev[qd_idx].page),
- STRIPE_SIZE);
- compute_parity(sh, UPDATE_PARITY);
- if (memcmp(page_address(tmp_page),
- page_address(sh->dev[qd_idx].page),
- STRIPE_SIZE)!= 0) {
- clear_bit(STRIPE_INSYNC, &sh->state);
- update_q = 1;
- }
- }
- if (update_p || update_q) {
- conf->mddev->resync_mismatches += STRIPE_SECTORS;
- if (test_bit(MD_RECOVERY_CHECK, &conf->mddev->recovery))
- /* don't try to repair!! */
- update_p = update_q = 0;
- }
-
- /* now write out any block on a failed drive,
- * or P or Q if they need it
- */
-
- if (failed == 2) {
- dev = &sh->dev[failed_num[1]];
- locked++;
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantwrite, &dev->flags);
- }
- if (failed >= 1) {
- dev = &sh->dev[failed_num[0]];
- locked++;
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantwrite, &dev->flags);
- }
-
- if (update_p) {
- dev = &sh->dev[pd_idx];
- locked ++;
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantwrite, &dev->flags);
- }
- if (update_q) {
- dev = &sh->dev[qd_idx];
- locked++;
- set_bit(R5_LOCKED, &dev->flags);
- set_bit(R5_Wantwrite, &dev->flags);
- }
- clear_bit(STRIPE_DEGRADED, &sh->state);
-
- set_bit(STRIPE_INSYNC, &sh->state);
- }
- }
-
- if (syncing && locked == 0 && test_bit(STRIPE_INSYNC, &sh->state)) {
- md_done_sync(conf->mddev, STRIPE_SECTORS,1);
- clear_bit(STRIPE_SYNCING, &sh->state);
- }
-
- /* If the failed drives are just a ReadError, then we might need
- * to progress the repair/check process
- */
- if (failed <= 2 && ! conf->mddev->ro)
- for (i=0; i<failed;i++) {
- dev = &sh->dev[failed_num[i]];
- if (test_bit(R5_ReadError, &dev->flags)
- && !test_bit(R5_LOCKED, &dev->flags)
- && test_bit(R5_UPTODATE, &dev->flags)
- ) {
- if (!test_bit(R5_ReWrite, &dev->flags)) {
- set_bit(R5_Wantwrite, &dev->flags);
- set_bit(R5_ReWrite, &dev->flags);
- set_bit(R5_LOCKED, &dev->flags);
- } else {
- /* let's read it back */
- set_bit(R5_Wantread, &dev->flags);
- set_bit(R5_LOCKED, &dev->flags);
- }
- }
- }
- spin_unlock(&sh->lock);
-
- while ((bi=return_bi)) {
- int bytes = bi->bi_size;
-
- return_bi = bi->bi_next;
- bi->bi_next = NULL;
- bi->bi_size = 0;
- bi->bi_end_io(bi, bytes, 0);
- }
- for (i=disks; i-- ;) {
- int rw;
- struct bio *bi;
- mdk_rdev_t *rdev;
- if (test_and_clear_bit(R5_Wantwrite, &sh->dev[i].flags))
- rw = 1;
- else if (test_and_clear_bit(R5_Wantread, &sh->dev[i].flags))
- rw = 0;
- else
- continue;
-
- bi = &sh->dev[i].req;
-
- bi->bi_rw = rw;
- if (rw)
- bi->bi_end_io = raid6_end_write_request;
- else
- bi->bi_end_io = raid6_end_read_request;
-
- rcu_read_lock();
- rdev = rcu_dereference(conf->disks[i].rdev);
- if (rdev && test_bit(Faulty, &rdev->flags))
- rdev = NULL;
- if (rdev)
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
-
- if (rdev) {
- if (syncing)
- md_sync_acct(rdev->bdev, STRIPE_SECTORS);
-
- bi->bi_bdev = rdev->bdev;
- PRINTK("for %llu schedule op %ld on disc %d\n",
- (unsigned long long)sh->sector, bi->bi_rw, i);
- atomic_inc(&sh->count);
- bi->bi_sector = sh->sector + rdev->data_offset;
- bi->bi_flags = 1 << BIO_UPTODATE;
- bi->bi_vcnt = 1;
- bi->bi_max_vecs = 1;
- bi->bi_idx = 0;
- bi->bi_io_vec = &sh->dev[i].vec;
- bi->bi_io_vec[0].bv_len = STRIPE_SIZE;
- bi->bi_io_vec[0].bv_offset = 0;
- bi->bi_size = STRIPE_SIZE;
- bi->bi_next = NULL;
- if (rw == WRITE &&
- test_bit(R5_ReWrite, &sh->dev[i].flags))
- atomic_add(STRIPE_SECTORS, &rdev->corrected_errors);
- generic_make_request(bi);
- } else {
- if (rw == 1)
- set_bit(STRIPE_DEGRADED, &sh->state);
- PRINTK("skip op %ld on disc %d for sector %llu\n",
- bi->bi_rw, i, (unsigned long long)sh->sector);
- clear_bit(R5_LOCKED, &sh->dev[i].flags);
- set_bit(STRIPE_HANDLE, &sh->state);
- }
- }
-}
-
-static void raid6_activate_delayed(raid6_conf_t *conf)
-{
- if (atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD) {
- while (!list_empty(&conf->delayed_list)) {
- struct list_head *l = conf->delayed_list.next;
- struct stripe_head *sh;
- sh = list_entry(l, struct stripe_head, lru);
- list_del_init(l);
- clear_bit(STRIPE_DELAYED, &sh->state);
- if (!test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state))
- atomic_inc(&conf->preread_active_stripes);
- list_add_tail(&sh->lru, &conf->handle_list);
- }
- }
-}
-
-static void activate_bit_delay(raid6_conf_t *conf)
-{
- /* device_lock is held */
- struct list_head head;
- list_add(&head, &conf->bitmap_list);
- list_del_init(&conf->bitmap_list);
- while (!list_empty(&head)) {
- struct stripe_head *sh = list_entry(head.next, struct stripe_head, lru);
- list_del_init(&sh->lru);
- atomic_inc(&sh->count);
- __release_stripe(conf, sh);
- }
-}
-
-static void unplug_slaves(mddev_t *mddev)
-{
- raid6_conf_t *conf = mddev_to_conf(mddev);
- int i;
-
- rcu_read_lock();
- for (i=0; i<mddev->raid_disks; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev);
- if (rdev && !test_bit(Faulty, &rdev->flags) && atomic_read(&rdev->nr_pending)) {
- request_queue_t *r_queue = bdev_get_queue(rdev->bdev);
-
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
-
- if (r_queue->unplug_fn)
- r_queue->unplug_fn(r_queue);
-
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
- }
- }
- rcu_read_unlock();
-}
-
-static void raid6_unplug_device(request_queue_t *q)
-{
- mddev_t *mddev = q->queuedata;
- raid6_conf_t *conf = mddev_to_conf(mddev);
- unsigned long flags;
-
- spin_lock_irqsave(&conf->device_lock, flags);
-
- if (blk_remove_plug(q)) {
- conf->seq_flush++;
- raid6_activate_delayed(conf);
- }
- md_wakeup_thread(mddev->thread);
-
- spin_unlock_irqrestore(&conf->device_lock, flags);
-
- unplug_slaves(mddev);
-}
-
-static int raid6_issue_flush(request_queue_t *q, struct gendisk *disk,
- sector_t *error_sector)
-{
- mddev_t *mddev = q->queuedata;
- raid6_conf_t *conf = mddev_to_conf(mddev);
- int i, ret = 0;
-
- rcu_read_lock();
- for (i=0; i<mddev->raid_disks && ret == 0; i++) {
- mdk_rdev_t *rdev = rcu_dereference(conf->disks[i].rdev);
- if (rdev && !test_bit(Faulty, &rdev->flags)) {
- struct block_device *bdev = rdev->bdev;
- request_queue_t *r_queue = bdev_get_queue(bdev);
-
- if (!r_queue->issue_flush_fn)
- ret = -EOPNOTSUPP;
- else {
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- ret = r_queue->issue_flush_fn(r_queue, bdev->bd_disk,
- error_sector);
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
- }
- }
- }
- rcu_read_unlock();
- return ret;
-}
-
-static inline void raid6_plug_device(raid6_conf_t *conf)
-{
- spin_lock_irq(&conf->device_lock);
- blk_plug_device(conf->mddev->queue);
- spin_unlock_irq(&conf->device_lock);
-}
-
-static int make_request (request_queue_t *q, struct bio * bi)
-{
- mddev_t *mddev = q->queuedata;
- raid6_conf_t *conf = mddev_to_conf(mddev);
- const unsigned int raid_disks = conf->raid_disks;
- const unsigned int data_disks = raid_disks - 2;
- unsigned int dd_idx, pd_idx;
- sector_t new_sector;
- sector_t logical_sector, last_sector;
- struct stripe_head *sh;
- const int rw = bio_data_dir(bi);
-
- if (unlikely(bio_barrier(bi))) {
- bio_endio(bi, bi->bi_size, -EOPNOTSUPP);
- return 0;
- }
-
- md_write_start(mddev, bi);
-
- disk_stat_inc(mddev->gendisk, ios[rw]);
- disk_stat_add(mddev->gendisk, sectors[rw], bio_sectors(bi));
-
- logical_sector = bi->bi_sector & ~((sector_t)STRIPE_SECTORS-1);
- last_sector = bi->bi_sector + (bi->bi_size>>9);
-
- bi->bi_next = NULL;
- bi->bi_phys_segments = 1; /* over-loaded to count active stripes */
-
- for (;logical_sector < last_sector; logical_sector += STRIPE_SECTORS) {
- DEFINE_WAIT(w);
-
- new_sector = raid6_compute_sector(logical_sector,
- raid_disks, data_disks, &dd_idx, &pd_idx, conf);
-
- PRINTK("raid6: make_request, sector %llu logical %llu\n",
- (unsigned long long)new_sector,
- (unsigned long long)logical_sector);
-
- retry:
- prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE);
- sh = get_active_stripe(conf, new_sector, pd_idx, (bi->bi_rw&RWA_MASK));
- if (sh) {
- if (!add_stripe_bio(sh, bi, dd_idx, (bi->bi_rw&RW_MASK))) {
- /* Add failed due to overlap. Flush everything
- * and wait a while
- */
- raid6_unplug_device(mddev->queue);
- release_stripe(sh);
- schedule();
- goto retry;
- }
- finish_wait(&conf->wait_for_overlap, &w);
- raid6_plug_device(conf);
- handle_stripe(sh, NULL);
- release_stripe(sh);
- } else {
- /* cannot get stripe for read-ahead, just give-up */
- clear_bit(BIO_UPTODATE, &bi->bi_flags);
- finish_wait(&conf->wait_for_overlap, &w);
- break;
- }
-
- }
- spin_lock_irq(&conf->device_lock);
- if (--bi->bi_phys_segments == 0) {
- int bytes = bi->bi_size;
-
- if (rw == WRITE )
- md_write_end(mddev);
- bi->bi_size = 0;
- bi->bi_end_io(bi, bytes, 0);
- }
- spin_unlock_irq(&conf->device_lock);
- return 0;
-}
-
-/* FIXME go_faster isn't used */
-static sector_t sync_request(mddev_t *mddev, sector_t sector_nr, int *skipped, int go_faster)
-{
- raid6_conf_t *conf = (raid6_conf_t *) mddev->private;
- struct stripe_head *sh;
- int sectors_per_chunk = conf->chunk_size >> 9;
- sector_t x;
- unsigned long stripe;
- int chunk_offset;
- int dd_idx, pd_idx;
- sector_t first_sector;
- int raid_disks = conf->raid_disks;
- int data_disks = raid_disks - 2;
- sector_t max_sector = mddev->size << 1;
- int sync_blocks;
- int still_degraded = 0;
- int i;
-
- if (sector_nr >= max_sector) {
- /* just being told to finish up .. nothing much to do */
- unplug_slaves(mddev);
-
- if (mddev->curr_resync < max_sector) /* aborted */
- bitmap_end_sync(mddev->bitmap, mddev->curr_resync,
- &sync_blocks, 1);
- else /* completed sync */
- conf->fullsync = 0;
- bitmap_close_sync(mddev->bitmap);
-
- return 0;
- }
- /* if there are 2 or more failed drives and we are trying
- * to resync, then assert that we are finished, because there is
- * nothing we can do.
- */
- if (mddev->degraded >= 2 && test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
- sector_t rv = (mddev->size << 1) - sector_nr;
- *skipped = 1;
- return rv;
- }
- if (!bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, 1) &&
- !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) &&
- !conf->fullsync && sync_blocks >= STRIPE_SECTORS) {
- /* we can skip this block, and probably more */
- sync_blocks /= STRIPE_SECTORS;
- *skipped = 1;
- return sync_blocks * STRIPE_SECTORS; /* keep things rounded to whole stripes */
- }
-
- x = sector_nr;
- chunk_offset = sector_div(x, sectors_per_chunk);
- stripe = x;
- BUG_ON(x != stripe);
-
- first_sector = raid6_compute_sector((sector_t)stripe*data_disks*sectors_per_chunk
- + chunk_offset, raid_disks, data_disks, &dd_idx, &pd_idx, conf);
- sh = get_active_stripe(conf, sector_nr, pd_idx, 1);
- if (sh == NULL) {
- sh = get_active_stripe(conf, sector_nr, pd_idx, 0);
- /* make sure we don't swamp the stripe cache if someone else
- * is trying to get access
- */
- schedule_timeout_uninterruptible(1);
- }
- /* Need to check if array will still be degraded after recovery/resync
- * We don't need to check the 'failed' flag as when that gets set,
- * recovery aborts.
- */
- for (i=0; i<mddev->raid_disks; i++)
- if (conf->disks[i].rdev == NULL)
- still_degraded = 1;
-
- bitmap_start_sync(mddev->bitmap, sector_nr, &sync_blocks, still_degraded);
-
- spin_lock(&sh->lock);
- set_bit(STRIPE_SYNCING, &sh->state);
- clear_bit(STRIPE_INSYNC, &sh->state);
- spin_unlock(&sh->lock);
-
- handle_stripe(sh, NULL);
- release_stripe(sh);
-
- return STRIPE_SECTORS;
-}
-
-/*
- * This is our raid6 kernel thread.
- *
- * We scan the hash table for stripes which can be handled now.
- * During the scan, completed stripes are saved for us by the interrupt
- * handler, so that they will not have to wait for our next wakeup.
- */
-static void raid6d (mddev_t *mddev)
-{
- struct stripe_head *sh;
- raid6_conf_t *conf = mddev_to_conf(mddev);
- int handled;
-
- PRINTK("+++ raid6d active\n");
-
- md_check_recovery(mddev);
-
- handled = 0;
- spin_lock_irq(&conf->device_lock);
- while (1) {
- struct list_head *first;
-
- if (conf->seq_flush - conf->seq_write > 0) {
- int seq = conf->seq_flush;
- spin_unlock_irq(&conf->device_lock);
- bitmap_unplug(mddev->bitmap);
- spin_lock_irq(&conf->device_lock);
- conf->seq_write = seq;
- activate_bit_delay(conf);
- }
-
- if (list_empty(&conf->handle_list) &&
- atomic_read(&conf->preread_active_stripes) < IO_THRESHOLD &&
- !blk_queue_plugged(mddev->queue) &&
- !list_empty(&conf->delayed_list))
- raid6_activate_delayed(conf);
-
- if (list_empty(&conf->handle_list))
- break;
-
- first = conf->handle_list.next;
- sh = list_entry(first, struct stripe_head, lru);
-
- list_del_init(first);
- atomic_inc(&sh->count);
- BUG_ON(atomic_read(&sh->count)!= 1);
- spin_unlock_irq(&conf->device_lock);
-
- handled++;
- handle_stripe(sh, conf->spare_page);
- release_stripe(sh);
-
- spin_lock_irq(&conf->device_lock);
- }
- PRINTK("%d stripes handled\n", handled);
-
- spin_unlock_irq(&conf->device_lock);
-
- unplug_slaves(mddev);
-
- PRINTK("--- raid6d inactive\n");
-}
-
-static ssize_t
-raid6_show_stripe_cache_size(mddev_t *mddev, char *page)
-{
- raid6_conf_t *conf = mddev_to_conf(mddev);
- if (conf)
- return sprintf(page, "%d\n", conf->max_nr_stripes);
- else
- return 0;
-}
-
-static ssize_t
-raid6_store_stripe_cache_size(mddev_t *mddev, const char *page, size_t len)
-{
- raid6_conf_t *conf = mddev_to_conf(mddev);
- char *end;
- int new;
- if (len >= PAGE_SIZE)
- return -EINVAL;
- if (!conf)
- return -ENODEV;
-
- new = simple_strtoul(page, &end, 10);
- if (!*page || (*end && *end != '\n') )
- return -EINVAL;
- if (new <= 16 || new > 32768)
- return -EINVAL;
- while (new < conf->max_nr_stripes) {
- if (drop_one_stripe(conf))
- conf->max_nr_stripes--;
- else
- break;
- }
- while (new > conf->max_nr_stripes) {
- if (grow_one_stripe(conf))
- conf->max_nr_stripes++;
- else break;
- }
- return len;
-}
-
-static struct md_sysfs_entry
-raid6_stripecache_size = __ATTR(stripe_cache_size, S_IRUGO | S_IWUSR,
- raid6_show_stripe_cache_size,
- raid6_store_stripe_cache_size);
-
-static ssize_t
-stripe_cache_active_show(mddev_t *mddev, char *page)
-{
- raid6_conf_t *conf = mddev_to_conf(mddev);
- if (conf)
- return sprintf(page, "%d\n", atomic_read(&conf->active_stripes));
- else
- return 0;
-}
-
-static struct md_sysfs_entry
-raid6_stripecache_active = __ATTR_RO(stripe_cache_active);
-
-static struct attribute *raid6_attrs[] = {
- &raid6_stripecache_size.attr,
- &raid6_stripecache_active.attr,
- NULL,
-};
-static struct attribute_group raid6_attrs_group = {
- .name = NULL,
- .attrs = raid6_attrs,
-};
-
-static int run(mddev_t *mddev)
-{
- raid6_conf_t *conf;
- int raid_disk, memory;
- mdk_rdev_t *rdev;
- struct disk_info *disk;
- struct list_head *tmp;
-
- if (mddev->level != 6) {
- PRINTK("raid6: %s: raid level not set to 6 (%d)\n", mdname(mddev), mddev->level);
- return -EIO;
- }
-
- mddev->private = kzalloc(sizeof (raid6_conf_t), GFP_KERNEL);
- if ((conf = mddev->private) == NULL)
- goto abort;
- conf->disks = kzalloc(mddev->raid_disks * sizeof(struct disk_info),
- GFP_KERNEL);
- if (!conf->disks)
- goto abort;
-
- conf->mddev = mddev;
-
- if ((conf->stripe_hashtbl = kzalloc(PAGE_SIZE, GFP_KERNEL)) == NULL)
- goto abort;
-
- conf->spare_page = alloc_page(GFP_KERNEL);
- if (!conf->spare_page)
- goto abort;
-
- spin_lock_init(&conf->device_lock);
- init_waitqueue_head(&conf->wait_for_stripe);
- init_waitqueue_head(&conf->wait_for_overlap);
- INIT_LIST_HEAD(&conf->handle_list);
- INIT_LIST_HEAD(&conf->delayed_list);
- INIT_LIST_HEAD(&conf->bitmap_list);
- INIT_LIST_HEAD(&conf->inactive_list);
- atomic_set(&conf->active_stripes, 0);
- atomic_set(&conf->preread_active_stripes, 0);
-
- PRINTK("raid6: run(%s) called.\n", mdname(mddev));
-
- ITERATE_RDEV(mddev,rdev,tmp) {
- raid_disk = rdev->raid_disk;
- if (raid_disk >= mddev->raid_disks
- || raid_disk < 0)
- continue;
- disk = conf->disks + raid_disk;
-
- disk->rdev = rdev;
-
- if (test_bit(In_sync, &rdev->flags)) {
- char b[BDEVNAME_SIZE];
- printk(KERN_INFO "raid6: device %s operational as raid"
- " disk %d\n", bdevname(rdev->bdev,b),
- raid_disk);
- conf->working_disks++;
- }
- }
-
- conf->raid_disks = mddev->raid_disks;
-
- /*
- * 0 for a fully functional array, 1 or 2 for a degraded array.
- */
- mddev->degraded = conf->failed_disks = conf->raid_disks - conf->working_disks;
- conf->mddev = mddev;
- conf->chunk_size = mddev->chunk_size;
- conf->level = mddev->level;
- conf->algorithm = mddev->layout;
- conf->max_nr_stripes = NR_STRIPES;
-
- /* device size must be a multiple of chunk size */
- mddev->size &= ~(mddev->chunk_size/1024 -1);
- mddev->resync_max_sectors = mddev->size << 1;
-
- if (conf->raid_disks < 4) {
- printk(KERN_ERR "raid6: not enough configured devices for %s (%d, minimum 4)\n",
- mdname(mddev), conf->raid_disks);
- goto abort;
- }
- if (!conf->chunk_size || conf->chunk_size % 4) {
- printk(KERN_ERR "raid6: invalid chunk size %d for %s\n",
- conf->chunk_size, mdname(mddev));
- goto abort;
- }
- if (conf->algorithm > ALGORITHM_RIGHT_SYMMETRIC) {
- printk(KERN_ERR
- "raid6: unsupported parity algorithm %d for %s\n",
- conf->algorithm, mdname(mddev));
- goto abort;
- }
- if (mddev->degraded > 2) {
- printk(KERN_ERR "raid6: not enough operational devices for %s"
- " (%d/%d failed)\n",
- mdname(mddev), conf->failed_disks, conf->raid_disks);
- goto abort;
- }
-
- if (mddev->degraded > 0 &&
- mddev->recovery_cp != MaxSector) {
- if (mddev->ok_start_degraded)
- printk(KERN_WARNING "raid6: starting dirty degraded array:%s"
- "- data corruption possible.\n",
- mdname(mddev));
- else {
- printk(KERN_ERR "raid6: cannot start dirty degraded array"
- " for %s\n", mdname(mddev));
- goto abort;
- }
- }
-
- {
- mddev->thread = md_register_thread(raid6d, mddev, "%s_raid6");
- if (!mddev->thread) {
- printk(KERN_ERR
- "raid6: couldn't allocate thread for %s\n",
- mdname(mddev));
- goto abort;
- }
- }
-
- memory = conf->max_nr_stripes * (sizeof(struct stripe_head) +
- conf->raid_disks * ((sizeof(struct bio) + PAGE_SIZE))) / 1024;
- if (grow_stripes(conf, conf->max_nr_stripes)) {
- printk(KERN_ERR
- "raid6: couldn't allocate %dkB for buffers\n", memory);
- shrink_stripes(conf);
- md_unregister_thread(mddev->thread);
- goto abort;
- } else
- printk(KERN_INFO "raid6: allocated %dkB for %s\n",
- memory, mdname(mddev));
-
- if (mddev->degraded == 0)
- printk(KERN_INFO "raid6: raid level %d set %s active with %d out of %d"
- " devices, algorithm %d\n", conf->level, mdname(mddev),
- mddev->raid_disks-mddev->degraded, mddev->raid_disks,
- conf->algorithm);
- else
- printk(KERN_ALERT "raid6: raid level %d set %s active with %d"
- " out of %d devices, algorithm %d\n", conf->level,
- mdname(mddev), mddev->raid_disks - mddev->degraded,
- mddev->raid_disks, conf->algorithm);
-
- print_raid6_conf(conf);
-
- /* read-ahead size must cover two whole stripes, which is
- * 2 * (n-2) * chunksize where 'n' is the number of raid devices
- */
- {
- int stripe = (mddev->raid_disks-2) * mddev->chunk_size
- / PAGE_SIZE;
- if (mddev->queue->backing_dev_info.ra_pages < 2 * stripe)
- mddev->queue->backing_dev_info.ra_pages = 2 * stripe;
- }
-
- /* Ok, everything is just fine now */
- sysfs_create_group(&mddev->kobj, &raid6_attrs_group);
-
- mddev->array_size = mddev->size * (mddev->raid_disks - 2);
-
- mddev->queue->unplug_fn = raid6_unplug_device;
- mddev->queue->issue_flush_fn = raid6_issue_flush;
- return 0;
-abort:
- if (conf) {
- print_raid6_conf(conf);
- safe_put_page(conf->spare_page);
- kfree(conf->stripe_hashtbl);
- kfree(conf->disks);
- kfree(conf);
- }
- mddev->private = NULL;
- printk(KERN_ALERT "raid6: failed to run raid set %s\n", mdname(mddev));
- return -EIO;
-}
-
-
-
-static int stop (mddev_t *mddev)
-{
- raid6_conf_t *conf = (raid6_conf_t *) mddev->private;
-
- md_unregister_thread(mddev->thread);
- mddev->thread = NULL;
- shrink_stripes(conf);
- kfree(conf->stripe_hashtbl);
- blk_sync_queue(mddev->queue); /* the unplug fn references 'conf'*/
- sysfs_remove_group(&mddev->kobj, &raid6_attrs_group);
- kfree(conf);
- mddev->private = NULL;
- return 0;
-}
-
-#if RAID6_DUMPSTATE
-static void print_sh (struct seq_file *seq, struct stripe_head *sh)
-{
- int i;
-
- seq_printf(seq, "sh %llu, pd_idx %d, state %ld.\n",
- (unsigned long long)sh->sector, sh->pd_idx, sh->state);
- seq_printf(seq, "sh %llu, count %d.\n",
- (unsigned long long)sh->sector, atomic_read(&sh->count));
- seq_printf(seq, "sh %llu, ", (unsigned long long)sh->sector);
- for (i = 0; i < sh->raid_conf->raid_disks; i++) {
- seq_printf(seq, "(cache%d: %p %ld) ",
- i, sh->dev[i].page, sh->dev[i].flags);
- }
- seq_printf(seq, "\n");
-}
-
-static void printall (struct seq_file *seq, raid6_conf_t *conf)
-{
- struct stripe_head *sh;
- struct hlist_node *hn;
- int i;
-
- spin_lock_irq(&conf->device_lock);
- for (i = 0; i < NR_HASH; i++) {
- sh = conf->stripe_hashtbl[i];
- hlist_for_each_entry(sh, hn, &conf->stripe_hashtbl[i], hash) {
- if (sh->raid_conf != conf)
- continue;
- print_sh(seq, sh);
- }
- }
- spin_unlock_irq(&conf->device_lock);
-}
-#endif
-
-static void status (struct seq_file *seq, mddev_t *mddev)
-{
- raid6_conf_t *conf = (raid6_conf_t *) mddev->private;
- int i;
-
- seq_printf (seq, " level %d, %dk chunk, algorithm %d", mddev->level, mddev->chunk_size >> 10, mddev->layout);
- seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->working_disks);
- for (i = 0; i < conf->raid_disks; i++)
- seq_printf (seq, "%s",
- conf->disks[i].rdev &&
- test_bit(In_sync, &conf->disks[i].rdev->flags) ? "U" : "_");
- seq_printf (seq, "]");
-#if RAID6_DUMPSTATE
- seq_printf (seq, "\n");
- printall(seq, conf);
-#endif
-}
-
-static void print_raid6_conf (raid6_conf_t *conf)
-{
- int i;
- struct disk_info *tmp;
-
- printk("RAID6 conf printout:\n");
- if (!conf) {
- printk("(conf==NULL)\n");
- return;
- }
- printk(" --- rd:%d wd:%d fd:%d\n", conf->raid_disks,
- conf->working_disks, conf->failed_disks);
-
- for (i = 0; i < conf->raid_disks; i++) {
- char b[BDEVNAME_SIZE];
- tmp = conf->disks + i;
- if (tmp->rdev)
- printk(" disk %d, o:%d, dev:%s\n",
- i, !test_bit(Faulty, &tmp->rdev->flags),
- bdevname(tmp->rdev->bdev,b));
- }
-}
-
-static int raid6_spare_active(mddev_t *mddev)
-{
- int i;
- raid6_conf_t *conf = mddev->private;
- struct disk_info *tmp;
-
- for (i = 0; i < conf->raid_disks; i++) {
- tmp = conf->disks + i;
- if (tmp->rdev
- && !test_bit(Faulty, &tmp->rdev->flags)
- && !test_bit(In_sync, &tmp->rdev->flags)) {
- mddev->degraded--;
- conf->failed_disks--;
- conf->working_disks++;
- set_bit(In_sync, &tmp->rdev->flags);
- }
- }
- print_raid6_conf(conf);
- return 0;
-}
-
-static int raid6_remove_disk(mddev_t *mddev, int number)
-{
- raid6_conf_t *conf = mddev->private;
- int err = 0;
- mdk_rdev_t *rdev;
- struct disk_info *p = conf->disks + number;
-
- print_raid6_conf(conf);
- rdev = p->rdev;
- if (rdev) {
- if (test_bit(In_sync, &rdev->flags) ||
- atomic_read(&rdev->nr_pending)) {
- err = -EBUSY;
- goto abort;
- }
- p->rdev = NULL;
- synchronize_rcu();
- if (atomic_read(&rdev->nr_pending)) {
- /* lost the race, try later */
- err = -EBUSY;
- p->rdev = rdev;
- }
- }
-
-abort:
-
- print_raid6_conf(conf);
- return err;
-}
-
-static int raid6_add_disk(mddev_t *mddev, mdk_rdev_t *rdev)
-{
- raid6_conf_t *conf = mddev->private;
- int found = 0;
- int disk;
- struct disk_info *p;
-
- if (mddev->degraded > 2)
- /* no point adding a device */
- return 0;
- /*
- * find the disk ... but prefer rdev->saved_raid_disk
- * if possible.
- */
- if (rdev->saved_raid_disk >= 0 &&
- conf->disks[rdev->saved_raid_disk].rdev == NULL)
- disk = rdev->saved_raid_disk;
- else
- disk = 0;
- for ( ; disk < mddev->raid_disks; disk++)
- if ((p=conf->disks + disk)->rdev == NULL) {
- clear_bit(In_sync, &rdev->flags);
- rdev->raid_disk = disk;
- found = 1;
- if (rdev->saved_raid_disk != disk)
- conf->fullsync = 1;
- rcu_assign_pointer(p->rdev, rdev);
- break;
- }
- print_raid6_conf(conf);
- return found;
-}
-
-static int raid6_resize(mddev_t *mddev, sector_t sectors)
-{
- /* no resync is happening, and there is enough space
- * on all devices, so we can resize.
- * We need to make sure resync covers any new space.
- * If the array is shrinking we should possibly wait until
- * any io in the removed space completes, but it hardly seems
- * worth it.
- */
- sectors &= ~((sector_t)mddev->chunk_size/512 - 1);
- mddev->array_size = (sectors * (mddev->raid_disks-2))>>1;
- set_capacity(mddev->gendisk, mddev->array_size << 1);
- mddev->changed = 1;
- if (sectors/2 > mddev->size && mddev->recovery_cp == MaxSector) {
- mddev->recovery_cp = mddev->size << 1;
- set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
- }
- mddev->size = sectors /2;
- mddev->resync_max_sectors = sectors;
- return 0;
-}
-
-static void raid6_quiesce(mddev_t *mddev, int state)
-{
- raid6_conf_t *conf = mddev_to_conf(mddev);
-
- switch(state) {
- case 1: /* stop all writes */
- spin_lock_irq(&conf->device_lock);
- conf->quiesce = 1;
- wait_event_lock_irq(conf->wait_for_stripe,
- atomic_read(&conf->active_stripes) == 0,
- conf->device_lock, /* nothing */);
- spin_unlock_irq(&conf->device_lock);
- break;
-
- case 0: /* re-enable writes */
- spin_lock_irq(&conf->device_lock);
- conf->quiesce = 0;
- wake_up(&conf->wait_for_stripe);
- spin_unlock_irq(&conf->device_lock);
- break;
- }
-}
-
-static struct mdk_personality raid6_personality =
-{
- .name = "raid6",
- .level = 6,
- .owner = THIS_MODULE,
- .make_request = make_request,
- .run = run,
- .stop = stop,
- .status = status,
- .error_handler = error,
- .hot_add_disk = raid6_add_disk,
- .hot_remove_disk= raid6_remove_disk,
- .spare_active = raid6_spare_active,
- .sync_request = sync_request,
- .resize = raid6_resize,
- .quiesce = raid6_quiesce,
-};
-
-static int __init raid6_init(void)
-{
- int e;
-
- e = raid6_select_algo();
- if ( e )
- return e;
-
- return register_md_personality(&raid6_personality);
-}
-
-static void raid6_exit (void)
-{
- unregister_md_personality(&raid6_personality);
-}
-
-module_init(raid6_init);
-module_exit(raid6_exit);
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("md-personality-8"); /* RAID6 */
-MODULE_ALIAS("md-raid6");
-MODULE_ALIAS("md-level-6");
diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig
index 583d151b7486..ef52e6da01ed 100644
--- a/drivers/media/Kconfig
+++ b/drivers/media/Kconfig
@@ -82,9 +82,6 @@ config VIDEO_IR
config VIDEO_TVEEPROM
tristate
-config VIDEO_CX2341X
- tristate
-
config USB_DABUSB
tristate "DABUSB driver"
depends on USB
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 3152a54a2539..5e8bb41a088b 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -556,22 +556,23 @@ static int dvb_frontend_thread(void *data)
}
/* do an iteration of the tuning loop */
- if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) {
- /* have we been asked to retune? */
- params = NULL;
- if (fepriv->state & FESTATE_RETUNE) {
- params = &fepriv->parameters;
- fepriv->state = FESTATE_TUNED;
- }
+ if (fe->ops.get_frontend_algo) {
+ if (fe->ops.get_frontend_algo(fe) == FE_ALGO_HW) {
+ /* have we been asked to retune? */
+ params = NULL;
+ if (fepriv->state & FESTATE_RETUNE) {
+ params = &fepriv->parameters;
+ fepriv->state = FESTATE_TUNED;
+ }
- fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
- if (s != fepriv->status) {
- dvb_frontend_add_event(fe, s);
- fepriv->status = s;
+ fe->ops.tune(fe, params, fepriv->tune_mode_flags, &fepriv->delay, &s);
+ if (s != fepriv->status) {
+ dvb_frontend_add_event(fe, s);
+ fepriv->status = s;
+ }
}
- } else {
+ } else
dvb_frontend_swzigzag(fe);
- }
}
if (dvb_shutdown_timeout) {
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 8832f80c05f7..7a5c99c200e8 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -152,13 +152,9 @@ static void init_av7110_av(struct av7110 *av7110)
/* remaining inits according to card and frontend type */
av7110->analog_tuner_flags = 0;
av7110->current_input = 0;
- if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a) {
- printk("dvb-ttpci: MSP3415 audio DAC @ card %d\n",
- av7110->dvb_adapter.num);
- av7110->adac_type = DVB_ADAC_MSP34x5;
+ if (dev->pci->subsystem_vendor == 0x13c2 && dev->pci->subsystem_device == 0x000a)
av7110_fw_cmd(av7110, COMTYPE_AUDIODAC, ADSwitch, 1, 0); // SPDIF on
- }
- else if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) {
+ if (i2c_writereg(av7110, 0x20, 0x00, 0x00) == 1) {
printk ("dvb-ttpci: Crystal audio DAC @ card %d detected\n",
av7110->dvb_adapter.num);
av7110->adac_type = DVB_ADAC_CRYSTAL;
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index 2eff09f638d3..0f3a044aeb17 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -318,7 +318,17 @@ int av7110_set_volume(struct av7110 *av7110, int volleft, int volright)
msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */
msp_writereg(av7110, MSP_WR_DSP, 0x0006, val); /* headphonesr */
return 0;
+
+ case DVB_ADAC_MSP34x5:
+ vol = (volleft > volright) ? volleft : volright;
+ val = (vol * 0x73 / 255) << 8;
+ if (vol > 0)
+ balance = ((volright - volleft) * 127) / vol;
+ msp_writereg(av7110, MSP_WR_DSP, 0x0001, balance << 8);
+ msp_writereg(av7110, MSP_WR_DSP, 0x0000, val); /* loudspeaker */
+ return 0;
}
+
return 0;
}
@@ -1267,23 +1277,32 @@ static int dvb_audio_ioctl(struct inode *inode, struct file *file,
switch(av7110->audiostate.channel_select) {
case AUDIO_STEREO:
ret = audcom(av7110, AUDIO_CMD_STEREO);
- if (!ret)
+ if (!ret) {
if (av7110->adac_type == DVB_ADAC_CRYSTAL)
i2c_writereg(av7110, 0x20, 0x02, 0x49);
+ else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0220);
+ }
break;
case AUDIO_MONO_LEFT:
ret = audcom(av7110, AUDIO_CMD_MONO_L);
- if (!ret)
+ if (!ret) {
if (av7110->adac_type == DVB_ADAC_CRYSTAL)
i2c_writereg(av7110, 0x20, 0x02, 0x4a);
+ else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0200);
+ }
break;
case AUDIO_MONO_RIGHT:
ret = audcom(av7110, AUDIO_CMD_MONO_R);
- if (!ret)
+ if (!ret) {
if (av7110->adac_type == DVB_ADAC_CRYSTAL)
i2c_writereg(av7110, 0x20, 0x02, 0x45);
+ else if (av7110->adac_type == DVB_ADAC_MSP34x5)
+ msp_writereg(av7110, MSP_WR_DSP, 0x0008, 0x0210);
+ }
break;
default:
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index 603a22e4bfe2..64055461559d 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -42,7 +42,18 @@
int msp_writereg(struct av7110 *av7110, u8 dev, u16 reg, u16 val)
{
u8 msg[5] = { dev, reg >> 8, reg & 0xff, val >> 8 , val & 0xff };
- struct i2c_msg msgs = { .flags = 0, .addr = 0x40, .len = 5, .buf = msg };
+ struct i2c_msg msgs = { .flags = 0, .len = 5, .buf = msg };
+
+ switch (av7110->adac_type) {
+ case DVB_ADAC_MSP34x0:
+ msgs.addr = 0x40;
+ break;
+ case DVB_ADAC_MSP34x5:
+ msgs.addr = 0x42;
+ break;
+ default:
+ return 0;
+ }
if (i2c_transfer(&av7110->i2c_adap, &msgs, 1) != 1) {
dprintk(1, "dvb-ttpci: failed @ card %d, %u = %u\n",
@@ -57,10 +68,23 @@ static int msp_readreg(struct av7110 *av7110, u8 dev, u16 reg, u16 *val)
u8 msg1[3] = { dev, reg >> 8, reg & 0xff };
u8 msg2[2];
struct i2c_msg msgs[2] = {
- { .flags = 0, .addr = 0x40, .len = 3, .buf = msg1 },
- { .flags = I2C_M_RD, .addr = 0x40, .len = 2, .buf = msg2 }
+ { .flags = 0 , .len = 3, .buf = msg1 },
+ { .flags = I2C_M_RD, .len = 2, .buf = msg2 }
};
+ switch (av7110->adac_type) {
+ case DVB_ADAC_MSP34x0:
+ msgs[0].addr = 0x40;
+ msgs[1].addr = 0x40;
+ break;
+ case DVB_ADAC_MSP34x5:
+ msgs[0].addr = 0x42;
+ msgs[1].addr = 0x42;
+ break;
+ default:
+ return 0;
+ }
+
if (i2c_transfer(&av7110->i2c_adap, &msgs[0], 2) != 2) {
dprintk(1, "dvb-ttpci: failed @ card %d, %u\n",
av7110->dvb_adapter.num, reg);
@@ -678,17 +702,23 @@ int av7110_init_analog_module(struct av7110 *av7110)
{
u16 version1, version2;
- if (i2c_writereg(av7110, 0x80, 0x0, 0x80) != 1
- || i2c_writereg(av7110, 0x80, 0x0, 0) != 1)
+ if (i2c_writereg(av7110, 0x80, 0x0, 0x80) == 1 &&
+ i2c_writereg(av7110, 0x80, 0x0, 0) == 1) {
+ printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n",
+ av7110->dvb_adapter.num);
+ av7110->adac_type = DVB_ADAC_MSP34x0;
+ } else if (i2c_writereg(av7110, 0x84, 0x0, 0x80) == 1 &&
+ i2c_writereg(av7110, 0x84, 0x0, 0) == 1) {
+ printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3415\n",
+ av7110->dvb_adapter.num);
+ av7110->adac_type = DVB_ADAC_MSP34x5;
+ } else
return -ENODEV;
- printk("dvb-ttpci: DVB-C analog module @ card %d detected, initializing MSP3400\n",
- av7110->dvb_adapter.num);
- av7110->adac_type = DVB_ADAC_MSP34x0;
msleep(100); // the probing above resets the msp...
msp_readreg(av7110, MSP_RD_DSP, 0x001e, &version1);
msp_readreg(av7110, MSP_RD_DSP, 0x001f, &version2);
- dprintk(1, "dvb-ttpci: @ card %d MSP3400 version 0x%04x 0x%04x\n",
+ dprintk(1, "dvb-ttpci: @ card %d MSP34xx version 0x%04x 0x%04x\n",
av7110->dvb_adapter.num, version1, version2);
msp_writereg(av7110, MSP_WR_DSP, 0x0013, 0x0c00);
msp_writereg(av7110, MSP_WR_DSP, 0x0000, 0x7f00); // loudspeaker + headphone
@@ -697,7 +727,7 @@ int av7110_init_analog_module(struct av7110 *av7110)
msp_writereg(av7110, MSP_WR_DSP, 0x0004, 0x7f00); // loudspeaker volume
msp_writereg(av7110, MSP_WR_DSP, 0x000a, 0x0220); // SCART 1 source
msp_writereg(av7110, MSP_WR_DSP, 0x0007, 0x7f00); // SCART 1 volume
- msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x4800); // prescale SCART
+ msp_writereg(av7110, MSP_WR_DSP, 0x000d, 0x1900); // prescale SCART
if (i2c_writereg(av7110, 0x48, 0x01, 0x00)!=1) {
INFO(("saa7113 not accessible.\n"));
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 824a63c92629..6d532f170ce5 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -381,6 +381,18 @@ config VIDEO_WM8739
To compile this driver as a module, choose M here: the
module will be called wm8739.
+config VIDEO_CX2341X
+ tristate "Conexant CX2341x MPEG encoders"
+ depends on VIDEO_V4L2 && EXPERIMENTAL
+ ---help---
+ Support for the Conexant CX23416 MPEG encoders
+ and CX23415 MPEG encoder/decoders.
+
+ This module currently supports the encoding functions only.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx2341x.
+
source "drivers/media/video/cx25840/Kconfig"
config VIDEO_SAA711X
@@ -433,6 +445,8 @@ endmenu # encoder / decoder chips
menu "V4L USB devices"
depends on USB && VIDEO_DEV
+source "drivers/media/video/pvrusb2/Kconfig"
+
source "drivers/media/video/em28xx/Kconfig"
config USB_DSBR
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index 6c401b46398a..353d61cfac1b 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_VIDEO_SAA7134) += ir-kbd-i2c.o saa7134/
obj-$(CONFIG_VIDEO_CX88) += cx88/
obj-$(CONFIG_VIDEO_EM28XX) += em28xx/
obj-$(CONFIG_VIDEO_EM28XX) += tvp5150.o
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
obj-$(CONFIG_VIDEO_TLV320AIC23B) += tlv320aic23b.o
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index 3116345c93b1..e68a6d2fff24 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -4848,7 +4848,7 @@ static void picolo_tetra_muxsel (struct bttv* btv, unsigned int input)
*
* The IVC120G security card has 4 i2c controlled TDA8540 matrix
* swichers to provide 16 channels to MUX0. The TDA8540's have
- * 4 indepedant outputs and as such the IVC120G also has the
+ * 4 independent outputs and as such the IVC120G also has the
* optional "Monitor Out" bus. This allows the card to be looking
* at one input while the monitor is looking at another.
*
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 423e954948be..aa3203ae670c 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -4019,8 +4019,9 @@ static int __devinit bttv_probe(struct pci_dev *dev,
if (!request_mem_region(pci_resource_start(dev,0),
pci_resource_len(dev,0),
btv->c.name)) {
- printk(KERN_WARNING "bttv%d: can't request iomem (0x%lx).\n",
- btv->c.nr, pci_resource_start(dev,0));
+ printk(KERN_WARNING "bttv%d: can't request iomem (0x%llx).\n",
+ btv->c.nr,
+ (unsigned long long)pci_resource_start(dev,0));
return -EBUSY;
}
pci_set_master(dev);
@@ -4031,8 +4032,9 @@ static int __devinit bttv_probe(struct pci_dev *dev,
pci_read_config_byte(dev, PCI_LATENCY_TIMER, &lat);
printk(KERN_INFO "bttv%d: Bt%d (rev %d) at %s, ",
bttv_num,btv->id, btv->revision, pci_name(dev));
- printk("irq: %d, latency: %d, mmio: 0x%lx\n",
- btv->c.pci->irq, lat, pci_resource_start(dev,0));
+ printk("irq: %d, latency: %d, mmio: 0x%llx\n",
+ btv->c.pci->irq, lat,
+ (unsigned long long)pci_resource_start(dev,0));
schedule();
btv->bt848_mmio=ioremap(pci_resource_start(dev,0), 0x1000);
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 554813e6f65d..65f00fc08fa9 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -43,6 +43,7 @@ MODULE_PARM_DESC(debug, "Debug level (0-1)");
const u32 cx2341x_mpeg_ctrls[] = {
V4L2_CID_MPEG_CLASS,
V4L2_CID_MPEG_STREAM_TYPE,
+ V4L2_CID_MPEG_STREAM_VBI_FMT,
V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
V4L2_CID_MPEG_AUDIO_ENCODING,
V4L2_CID_MPEG_AUDIO_L2_BITRATE,
@@ -135,6 +136,9 @@ static int cx2341x_get_ctrl(struct cx2341x_mpeg_params *params,
case V4L2_CID_MPEG_STREAM_TYPE:
ctrl->value = params->stream_type;
break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ ctrl->value = params->stream_vbi_fmt;
+ break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
ctrl->value = params->video_spatial_filter_mode;
break;
@@ -257,6 +261,9 @@ static int cx2341x_set_ctrl(struct cx2341x_mpeg_params *params,
params->video_bitrate_mode = V4L2_MPEG_VIDEO_BITRATE_MODE_CBR;
}
break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ params->stream_vbi_fmt = ctrl->value;
+ break;
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
params->video_spatial_filter_mode = ctrl->value;
break;
@@ -418,6 +425,14 @@ int cx2341x_ctrl_query(struct cx2341x_mpeg_params *params, struct v4l2_queryctrl
qctrl->flags |= V4L2_CTRL_FLAG_INACTIVE;
return err;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ if (params->capabilities & CX2341X_CAP_HAS_SLICED_VBI)
+ return v4l2_ctrl_query_fill_std(qctrl);
+ return cx2341x_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE, 1,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
+
/* CX23415/6 specific */
case V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE:
return cx2341x_ctrl_query_fill(qctrl,
@@ -586,7 +601,7 @@ static void cx2341x_calc_audio_properties(struct cx2341x_mpeg_params *params)
}
int cx2341x_ext_ctrls(struct cx2341x_mpeg_params *params,
- struct v4l2_ext_controls *ctrls, int cmd)
+ struct v4l2_ext_controls *ctrls, unsigned int cmd)
{
int err = 0;
int i;
@@ -639,6 +654,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
{
static struct cx2341x_mpeg_params default_params = {
/* misc */
+ .capabilities = 0,
.port = CX2341X_PORT_MEMORY,
.width = 720,
.height = 480,
@@ -646,6 +662,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
/* stream */
.stream_type = V4L2_MPEG_STREAM_TYPE_MPEG2_PS,
+ .stream_vbi_fmt = V4L2_MPEG_STREAM_VBI_FMT_NONE,
/* audio */
.audio_sampling_freq = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
@@ -830,22 +847,22 @@ invalid:
return "<invalid>";
}
-void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
+void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
{
int is_mpeg1 = p->video_encoding == V4L2_MPEG_VIDEO_ENCODING_MPEG_1;
/* Stream */
- printk(KERN_INFO "cx2341x-%d: Stream: %s\n",
- card_id,
+ printk(KERN_INFO "%s: Stream: %s\n",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
/* Video */
- printk(KERN_INFO "cx2341x-%d: Video: %dx%d, %d fps\n",
- card_id,
+ printk(KERN_INFO "%s: Video: %dx%d, %d fps\n",
+ prefix,
p->width / (is_mpeg1 ? 2 : 1), p->height / (is_mpeg1 ? 2 : 1),
p->is_50hz ? 25 : 30);
- printk(KERN_INFO "cx2341x-%d: Video: %s, %s, %s, %d",
- card_id,
+ printk(KERN_INFO "%s: Video: %s, %s, %s, %d",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ENCODING),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_ASPECT),
cx2341x_menu_item(p, V4L2_CID_MPEG_VIDEO_BITRATE_MODE),
@@ -854,19 +871,19 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
printk(", Peak %d", p->video_bitrate_peak);
}
printk("\n");
- printk(KERN_INFO "cx2341x-%d: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n",
- card_id,
+ printk(KERN_INFO "%s: Video: GOP Size %d, %d B-Frames, %sGOP Closure, %s3:2 Pulldown\n",
+ prefix,
p->video_gop_size, p->video_b_frames,
p->video_gop_closure ? "" : "No ",
p->video_pulldown ? "" : "No ");
if (p->video_temporal_decimation) {
- printk(KERN_INFO "cx2341x-%d: Video: Temporal Decimation %d\n",
- card_id, p->video_temporal_decimation);
+ printk(KERN_INFO "%s: Video: Temporal Decimation %d\n",
+ prefix, p->video_temporal_decimation);
}
/* Audio */
- printk(KERN_INFO "cx2341x-%d: Audio: %s, %s, %s, %s",
- card_id,
+ printk(KERN_INFO "%s: Audio: %s, %s, %s, %s",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_ENCODING),
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_L2_BITRATE),
@@ -880,18 +897,18 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, int card_id)
cx2341x_menu_item(p, V4L2_CID_MPEG_AUDIO_CRC));
/* Encoding filters */
- printk(KERN_INFO "cx2341x-%d: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
- card_id,
+ printk(KERN_INFO "%s: Spatial Filter: %s, Luma %s, Chroma %s, %d\n",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE),
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE),
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE),
p->video_spatial_filter);
- printk(KERN_INFO "cx2341x-%d: Temporal Filter: %s, %d\n",
- card_id,
+ printk(KERN_INFO "%s: Temporal Filter: %s, %d\n",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE),
p->video_temporal_filter);
- printk(KERN_INFO "cx2341x-%d: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
- card_id,
+ printk(KERN_INFO "%s: Median Filter: %s, Luma [%d, %d], Chroma [%d, %d]\n",
+ prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE),
p->video_luma_median_filter_bottom,
p->video_luma_median_filter_top,
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 91e1c481a164..80e23ee9801c 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -11,7 +11,6 @@ config VIDEO_CX88
select VIDEO_BUF
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_CX2341X
select VIDEO_IR
---help---
This is a video4linux driver for Conexant 2388x based
@@ -36,13 +35,25 @@ config VIDEO_CX88_ALSA
To compile this driver as a module, choose M here: the
module will be called cx88-alsa.
+config VIDEO_CX88_BLACKBIRD
+ tristate "Blackbird MPEG encoder support (cx2388x + cx23416)"
+ depends on VIDEO_CX88
+ select VIDEO_CX2341X
+ ---help---
+ This adds support for MPEG encoder cards based on the
+ Blackbird reference design, using the Conexant 2388x
+ and 23416 chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cx88-blackbird.
+
config VIDEO_CX88_DVB
tristate "DVB/ATSC Support for cx2388x based TV cards"
depends on VIDEO_CX88 && DVB_CORE
select VIDEO_BUF_DVB
---help---
This adds support for DVB/ATSC cards based on the
- Connexant 2388x chip.
+ Conexant 2388x chip.
To compile this driver as a module, choose M here: the
module will be called cx88-dvb.
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 0dcd09b9b727..352b919f30c4 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -3,9 +3,10 @@ cx88xx-objs := cx88-cards.o cx88-core.o cx88-i2c.o cx88-tvaudio.o \
cx8800-objs := cx88-video.o cx88-vbi.o
cx8802-objs := cx88-mpeg.o
-obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o cx88-blackbird.o
-obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
+obj-$(CONFIG_VIDEO_CX88) += cx88xx.o cx8800.o cx8802.o
obj-$(CONFIG_VIDEO_CX88_ALSA) += cx88-alsa.o
+obj-$(CONFIG_VIDEO_CX88_BLACKBIRD) += cx88-blackbird.o
+obj-$(CONFIG_VIDEO_CX88_DVB) += cx88-dvb.o
obj-$(CONFIG_VIDEO_CX88_VP3054) += cx88-vp3054-i2c.o
EXTRA_CFLAGS += -Idrivers/media/video
diff --git a/drivers/media/video/cx88/cx88-alsa.c b/drivers/media/video/cx88/cx88-alsa.c
index 2194cbeca33b..292a5e81eb75 100644
--- a/drivers/media/video/cx88/cx88-alsa.c
+++ b/drivers/media/video/cx88/cx88-alsa.c
@@ -712,9 +712,9 @@ static int __devinit snd_cx88_create(struct snd_card *card,
pci_read_config_byte(pci, PCI_LATENCY_TIMER, &chip->pci_lat);
dprintk(1,"ALSA %s/%i: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%lx\n", core->name, devno,
+ "latency: %d, mmio: 0x%llx\n", core->name, devno,
pci_name(pci), chip->pci_rev, pci->irq,
- chip->pci_lat,pci_resource_start(pci,0));
+ chip->pci_lat,(unsigned long long)pci_resource_start(pci,0));
chip->irq = pci->irq;
synchronize_irq(chip->irq);
@@ -766,8 +766,8 @@ static int __devinit cx88_audio_initdev(struct pci_dev *pci,
strcpy (card->driver, "CX88x");
sprintf(card->shortname, "Conexant CX%x", pci->device);
- sprintf(card->longname, "%s at %#lx",
- card->shortname, pci_resource_start(pci, 0));
+ sprintf(card->longname, "%s at %#llx",
+ card->shortname,(unsigned long long)pci_resource_start(pci, 0));
strcpy (card->mixername, "CX88");
dprintk (0, "%s/%i: ALSA support for cx2388x boards\n",
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index 67fd3302e8f2..4ff81582ec56 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -846,24 +846,33 @@ static int mpeg_do_ioctl(struct inode *inode, struct file *file,
BLACKBIRD_MPEG_CAPTURE,
BLACKBIRD_RAW_BITS_NONE);
- cx88_do_ioctl( inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook );
+ cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
blackbird_initialize_codec(dev);
cx88_set_scale(dev->core, dev->width, dev->height,
fh->mpegq.field);
return 0;
}
+ case VIDIOC_LOG_STATUS:
+ {
+ char name[32 + 2];
+
+ snprintf(name, sizeof(name), "%s/2", core->name);
+ printk("%s/2: ============ START LOG STATUS ============\n",
+ core->name);
+ cx88_call_i2c_clients(core, VIDIOC_LOG_STATUS, 0);
+ cx2341x_log_status(&dev->params, name);
+ printk("%s/2: ============= END LOG STATUS =============\n",
+ core->name);
+ return 0;
+ }
default:
- return cx88_do_ioctl( inode, file, 0, dev->core, cmd, arg, cx88_ioctl_hook );
+ return cx88_do_ioctl(inode, file, 0, dev->core, cmd, arg, mpeg_do_ioctl);
}
return 0;
}
-int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg);
-unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
-
static unsigned int mpeg_translate_ioctl(unsigned int cmd)
{
return cmd;
@@ -872,8 +881,8 @@ static unsigned int mpeg_translate_ioctl(unsigned int cmd)
static int mpeg_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- cmd = cx88_ioctl_translator( cmd );
- return video_usercopy(inode, file, cmd, arg, cx88_ioctl_hook);
+ cmd = mpeg_translate_ioctl( cmd );
+ return video_usercopy(inode, file, cmd, arg, mpeg_do_ioctl);
}
static int mpeg_open(struct inode *inode, struct file *file)
@@ -1119,8 +1128,6 @@ static int blackbird_init(void)
printk(KERN_INFO "cx2388x: snapshot date %04d-%02d-%02d\n",
SNAPSHOT/10000, (SNAPSHOT/100)%100, SNAPSHOT%100);
#endif
- cx88_ioctl_hook = mpeg_do_ioctl;
- cx88_ioctl_translator = mpeg_translate_ioctl;
return pci_register_driver(&blackbird_pci_driver);
}
@@ -1132,9 +1139,6 @@ static void blackbird_fini(void)
module_init(blackbird_init);
module_exit(blackbird_fini);
-EXPORT_SYMBOL(cx88_ioctl_hook);
-EXPORT_SYMBOL(cx88_ioctl_translator);
-
/* ----------------------------------------------------------- */
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 67cdd8270863..f9d68f20dc88 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1700,11 +1700,6 @@ void cx88_card_setup(struct cx88_core *core)
/* ------------------------------------------------------------------ */
EXPORT_SYMBOL(cx88_boards);
-EXPORT_SYMBOL(cx88_bcount);
-EXPORT_SYMBOL(cx88_subids);
-EXPORT_SYMBOL(cx88_idcount);
-EXPORT_SYMBOL(cx88_card_list);
-EXPORT_SYMBOL(cx88_card_setup);
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index c56292d8d93b..973d3f39b2d5 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -1031,8 +1031,8 @@ static int get_ressources(struct cx88_core *core, struct pci_dev *pci)
pci_resource_len(pci,0),
core->name))
return 0;
- printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n",
- core->name,pci_resource_start(pci,0));
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+ core->name,(unsigned long long)pci_resource_start(pci,0));
return -EBUSY;
}
@@ -1181,8 +1181,6 @@ EXPORT_SYMBOL(cx88_set_scale);
EXPORT_SYMBOL(cx88_vdev_init);
EXPORT_SYMBOL(cx88_core_get);
EXPORT_SYMBOL(cx88_core_put);
-EXPORT_SYMBOL(cx88_start_audio_dma);
-EXPORT_SYMBOL(cx88_stop_audio_dma);
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 7efa6def0bde..70663805cc30 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -234,7 +234,6 @@ int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
/* ----------------------------------------------------------------------- */
EXPORT_SYMBOL(cx88_call_i2c_clients);
-EXPORT_SYMBOL(cx88_i2c_init);
/*
* Local variables:
diff --git a/drivers/media/video/cx88/cx88-mpeg.c b/drivers/media/video/cx88/cx88-mpeg.c
index a9d7795a8e14..2c12aca1b6a3 100644
--- a/drivers/media/video/cx88/cx88-mpeg.c
+++ b/drivers/media/video/cx88/cx88-mpeg.c
@@ -420,9 +420,9 @@ int cx8802_init_common(struct cx8802_dev *dev)
pci_read_config_byte(dev->pci, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(dev->pci, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s/2: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%lx\n", dev->core->name,
+ "latency: %d, mmio: 0x%llx\n", dev->core->name,
pci_name(dev->pci), dev->pci_rev, dev->pci->irq,
- dev->pci_lat,pci_resource_start(dev->pci,0));
+ dev->pci_lat,(unsigned long long)pci_resource_start(dev->pci,0));
/* initialize driver struct */
spin_lock_init(&dev->slock);
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 1e4278b588d8..5785c3481579 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -726,7 +726,7 @@ static void set_audio_standard_FM(struct cx88_core *core,
/* ----------------------------------------------------------- */
-int cx88_detect_nicam(struct cx88_core *core)
+static int cx88_detect_nicam(struct cx88_core *core)
{
int i, j = 0;
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 694d1d80ff3f..8d5cf474b68e 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -494,8 +494,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
return 0;
buf = list_entry(q->queued.next, struct cx88_buffer, vb.queue);
if (NULL == prev) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
start_video_dma(dev, q, buf);
buf->vb.state = STATE_ACTIVE;
buf->count = q->count++;
@@ -506,8 +505,7 @@ static int restart_video_queue(struct cx8800_dev *dev,
} else if (prev->vb.width == buf->vb.width &&
prev->vb.height == buf->vb.height &&
prev->fmt == buf->fmt) {
- list_del(&buf->vb.queue);
- list_add_tail(&buf->vb.queue,&q->active);
+ list_move_tail(&buf->vb.queue, &q->active);
buf->vb.state = STATE_ACTIVE;
buf->count = q->count++;
prev->risc.jmp[1] = cpu_to_le32(buf->risc.dma);
@@ -1849,9 +1847,9 @@ static int __devinit cx8800_initdev(struct pci_dev *pci_dev,
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s/0: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%lx\n", core->name,
+ "latency: %d, mmio: 0x%llx\n", core->name,
pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
- dev->pci_lat,pci_resource_start(pci_dev,0));
+ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev,0xffffffff)) {
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index dc7bc35f18f4..9a9a0fc7a41a 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -563,7 +563,6 @@ void cx88_newstation(struct cx88_core *core);
void cx88_get_stereo(struct cx88_core *core, struct v4l2_tuner *t);
void cx88_set_stereo(struct cx88_core *core, u32 mode, int manual);
int cx88_audio_thread(void *data);
-int cx88_detect_nicam(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-input.c */
@@ -592,12 +591,6 @@ extern int cx88_do_ioctl(struct inode *inode, struct file *file, int radio,
struct cx88_core *core, unsigned int cmd,
void *arg, v4l2_kioctl driver_ioctl);
-/* ----------------------------------------------------------- */
-/* cx88-blackbird.c */
-extern int (*cx88_ioctl_hook)(struct inode *inode, struct file *file,
- unsigned int cmd, void *arg);
-extern unsigned int (*cx88_ioctl_translator)(unsigned int cmd);
-
/*
* Local variables:
* c-basic-offset: 8
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
new file mode 100644
index 000000000000..7e727fe14b32
--- /dev/null
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -0,0 +1,62 @@
+config VIDEO_PVRUSB2
+ tristate "Hauppauge WinTV-PVR USB2 support"
+ depends on VIDEO_V4L2 && USB && I2C && EXPERIMENTAL
+ select FW_LOADER
+ select VIDEO_TUNER
+ select VIDEO_TVEEPROM
+ select VIDEO_CX2341X
+ select VIDEO_SAA711X
+ select VIDEO_MSP3400
+ ---help---
+ This is a video4linux driver for Conexant 23416 based
+ usb2 personal video recorder devices.
+
+ To compile this driver as a module, choose M here: the
+ module will be called pvrusb2
+
+config VIDEO_PVRUSB2_24XXX
+ bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series"
+ depends on VIDEO_PVRUSB2 && EXPERIMENTAL
+ select VIDEO_CX25840
+ select VIDEO_WM8775
+ ---help---
+ This option enables inclusion of additional logic to operate
+ newer WinTV-PVR USB2 devices whose model number is of the
+ form "24xxx" (leading prefix of "24" followed by 3 digits).
+ To see if you may need this option, examine the white
+ sticker on the underside of your device. Enabling this
+ option will not harm support for older devices, however it
+ is a separate option because of the experimental nature of
+ this new feature.
+
+ If you are in doubt, say N.
+
+ Note: This feature is _very_ experimental. You have been
+ warned.
+
+config VIDEO_PVRUSB2_SYSFS
+ bool "pvrusb2 sysfs support (EXPERIMENTAL)"
+ default y
+ depends on VIDEO_PVRUSB2 && SYSFS && EXPERIMENTAL
+ ---help---
+ This option enables the operation of a sysfs based
+ interface for query and control of the pvrusb2 driver.
+
+ This is not generally needed for v4l applications,
+ although certain applications are optimized to take
+ advantage of this feature.
+
+ If you are in doubt, say Y.
+
+ Note: This feature is experimental and subject to change.
+
+config VIDEO_PVRUSB2_DEBUGIFC
+ bool "pvrusb2 debug interface"
+ depends on VIDEO_PVRUSB2_SYSFS
+ ---help---
+ This option enables the inclusion of a debug interface
+ in the pvrusb2 driver, hosted through sysfs.
+
+ You do not need to select this option unless you plan
+ on debugging the driver or performing a manual firmware
+ extraction.
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
new file mode 100644
index 000000000000..fed603ad0a67
--- /dev/null
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -0,0 +1,18 @@
+obj-pvrusb2-sysfs-$(CONFIG_VIDEO_PVRUSB2_SYSFS) := pvrusb2-sysfs.o
+obj-pvrusb2-debugifc-$(CONFIG_VIDEO_PVRUSB2_DEBUGIFC) := pvrusb2-debugifc.o
+
+obj-pvrusb2-24xxx-$(CONFIG_VIDEO_PVRUSB2_24XXX) := \
+ pvrusb2-cx2584x-v4l.o \
+ pvrusb2-wm8775.o
+
+pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.o \
+ pvrusb2-audio.o pvrusb2-i2c-chips-v4l2.o \
+ pvrusb2-encoder.o pvrusb2-video-v4l.o \
+ pvrusb2-eeprom.o pvrusb2-tuner.o pvrusb2-demod.o \
+ pvrusb2-main.o pvrusb2-hdw.o pvrusb2-v4l2.o \
+ pvrusb2-ctrl.o pvrusb2-std.o \
+ pvrusb2-context.o pvrusb2-io.o pvrusb2-ioread.o \
+ $(obj-pvrusb2-24xxx-y) \
+ $(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
+
+obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.c b/drivers/media/video/pvrusb2/pvrusb2-audio.c
new file mode 100644
index 000000000000..313d2dcf9e4b
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.c
@@ -0,0 +1,204 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 "pvrusb2-audio.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/msp3400.h>
+#include <media/v4l2-common.h>
+
+struct pvr2_msp3400_handler {
+ struct pvr2_hdw *hdw;
+ struct pvr2_i2c_client *client;
+ struct pvr2_i2c_handler i2c_handler;
+ struct pvr2_audio_stat astat;
+ unsigned long stale_mask;
+};
+
+
+/* This function selects the correct audio input source */
+static void set_stereo(struct pvr2_msp3400_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ struct v4l2_routing route;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c msp3400 v4l2 set_stereo");
+
+ if (hdw->input_val == PVR2_CVAL_INPUT_TV) {
+ struct v4l2_tuner vt;
+ memset(&vt,0,sizeof(vt));
+ vt.audmode = hdw->audiomode_val;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_S_TUNER,&vt);
+ }
+
+ route.input = MSP_INPUT_DEFAULT;
+ route.output = MSP_OUTPUT(MSP_SC_IN_DSP_SCART1);
+ switch (hdw->input_val) {
+ case PVR2_CVAL_INPUT_TV:
+ break;
+ case PVR2_CVAL_INPUT_RADIO:
+ /* Assume that msp34xx also handle FM decoding, in which case
+ we're still using the tuner. */
+ /* HV: actually it is more likely to be the SCART2 input if
+ the ivtv experience is any indication. */
+ route.input = MSP_INPUT(MSP_IN_SCART2, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ /* SCART 1 input */
+ route.input = MSP_INPUT(MSP_IN_SCART1, MSP_IN_TUNER1,
+ MSP_DSP_IN_SCART, MSP_DSP_IN_SCART);
+ break;
+ }
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+}
+
+
+static int check_stereo(struct pvr2_msp3400_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return (hdw->input_dirty ||
+ hdw->audiomode_dirty);
+}
+
+
+struct pvr2_msp3400_ops {
+ void (*update)(struct pvr2_msp3400_handler *);
+ int (*check)(struct pvr2_msp3400_handler *);
+};
+
+
+static const struct pvr2_msp3400_ops msp3400_ops[] = {
+ { .update = set_stereo, .check = check_stereo},
+};
+
+
+static int msp3400_check(struct pvr2_msp3400_handler *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (ctxt->stale_mask & msk) continue;
+ if (msp3400_ops[idx].check(ctxt)) {
+ ctxt->stale_mask |= msk;
+ }
+ }
+ return ctxt->stale_mask != 0;
+}
+
+
+static void msp3400_update(struct pvr2_msp3400_handler *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(msp3400_ops)/sizeof(msp3400_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (!(ctxt->stale_mask & msk)) continue;
+ ctxt->stale_mask &= ~msk;
+ msp3400_ops[idx].update(ctxt);
+ }
+}
+
+
+/* This reads back the current signal type */
+static int get_audio_status(struct pvr2_msp3400_handler *ctxt)
+{
+ struct v4l2_tuner vt;
+ int stat;
+
+ memset(&vt,0,sizeof(vt));
+ stat = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
+ if (stat < 0) return stat;
+
+ ctxt->hdw->flag_stereo = (vt.audmode & V4L2_TUNER_MODE_STEREO) != 0;
+ ctxt->hdw->flag_bilingual =
+ (vt.audmode & V4L2_TUNER_MODE_LANG2) != 0;
+ return 0;
+}
+
+
+static void pvr2_msp3400_detach(struct pvr2_msp3400_handler *ctxt)
+{
+ ctxt->client->handler = 0;
+ ctxt->hdw->audio_stat = 0;
+ kfree(ctxt);
+}
+
+
+static unsigned int pvr2_msp3400_describe(struct pvr2_msp3400_handler *ctxt,
+ char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-audio v4l2");
+}
+
+
+const static struct pvr2_i2c_handler_functions msp3400_funcs = {
+ .detach = (void (*)(void *))pvr2_msp3400_detach,
+ .check = (int (*)(void *))msp3400_check,
+ .update = (void (*)(void *))msp3400_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_msp3400_describe,
+};
+
+
+int pvr2_i2c_msp3400_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+{
+ struct pvr2_msp3400_handler *ctxt;
+ if (hdw->audio_stat) return 0;
+ if (cp->handler) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->i2c_handler.func_data = ctxt;
+ ctxt->i2c_handler.func_table = &msp3400_funcs;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ ctxt->astat.ctxt = ctxt;
+ ctxt->astat.status = (int (*)(void *))get_audio_status;
+ ctxt->astat.detach = (void (*)(void *))pvr2_msp3400_detach;
+ ctxt->stale_mask = (1 << (sizeof(msp3400_ops)/
+ sizeof(msp3400_ops[0]))) - 1;
+ cp->handler = &ctxt->i2c_handler;
+ hdw->audio_stat = &ctxt->astat;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x msp3400 V4L2 handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-audio.h b/drivers/media/video/pvrusb2/pvrusb2-audio.h
new file mode 100644
index 000000000000..536339b68843
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-audio.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_AUDIO_H
+#define __PVRUSB2_AUDIO_H
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_msp3400_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+#endif /* __PVRUSB2_AUDIO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.c b/drivers/media/video/pvrusb2/pvrusb2-context.c
new file mode 100644
index 000000000000..40dc59871a45
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.c
@@ -0,0 +1,230 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-context.h"
+#include "pvrusb2-io.h"
+#include "pvrusb2-ioread.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+
+
+static void pvr2_context_destroy(struct pvr2_context *mp)
+{
+ if (mp->hdw) pvr2_hdw_destroy(mp->hdw);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr_main id=%p",mp);
+ flush_workqueue(mp->workqueue);
+ destroy_workqueue(mp->workqueue);
+ kfree(mp);
+}
+
+
+static void pvr2_context_trigger_poll(struct pvr2_context *mp)
+{
+ queue_work(mp->workqueue,&mp->workpoll);
+}
+
+
+static void pvr2_context_poll(struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp); do {
+ pvr2_hdw_poll(mp->hdw);
+ } while (0); pvr2_context_exit(mp);
+}
+
+
+static void pvr2_context_setup(struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp); do {
+ if (!pvr2_hdw_dev_ok(mp->hdw)) break;
+ pvr2_hdw_setup(mp->hdw);
+ pvr2_hdw_setup_poll_trigger(
+ mp->hdw,
+ (void (*)(void *))pvr2_context_trigger_poll,
+ mp);
+ if (!pvr2_hdw_dev_ok(mp->hdw)) break;
+ if (!pvr2_hdw_init_ok(mp->hdw)) break;
+ mp->video_stream.stream = pvr2_hdw_get_video_stream(mp->hdw);
+ if (mp->setup_func) {
+ mp->setup_func(mp);
+ }
+ } while (0); pvr2_context_exit(mp);
+}
+
+
+struct pvr2_context *pvr2_context_create(
+ struct usb_interface *intf,
+ const struct usb_device_id *devid,
+ void (*setup_func)(struct pvr2_context *))
+{
+ struct pvr2_context *mp = 0;
+ mp = kmalloc(sizeof(*mp),GFP_KERNEL);
+ if (!mp) goto done;
+ memset(mp,0,sizeof(*mp));
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_main id=%p",mp);
+ mp->setup_func = setup_func;
+ mutex_init(&mp->mutex);
+ mp->hdw = pvr2_hdw_create(intf,devid);
+ if (!mp->hdw) {
+ pvr2_context_destroy(mp);
+ mp = 0;
+ goto done;
+ }
+
+ mp->workqueue = create_singlethread_workqueue("pvrusb2");
+ INIT_WORK(&mp->workinit,(void (*)(void*))pvr2_context_setup,mp);
+ INIT_WORK(&mp->workpoll,(void (*)(void*))pvr2_context_poll,mp);
+ queue_work(mp->workqueue,&mp->workinit);
+ done:
+ return mp;
+}
+
+
+void pvr2_context_enter(struct pvr2_context *mp)
+{
+ mutex_lock(&mp->mutex);
+ pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_enter(id=%p)",mp);
+}
+
+
+void pvr2_context_exit(struct pvr2_context *mp)
+{
+ int destroy_flag = 0;
+ if (!(mp->mc_first || !mp->disconnect_flag)) {
+ destroy_flag = !0;
+ }
+ pvr2_trace(PVR2_TRACE_CREG,"pvr2_context_exit(id=%p) outside",mp);
+ mutex_unlock(&mp->mutex);
+ if (destroy_flag) pvr2_context_destroy(mp);
+}
+
+
+static void pvr2_context_run_checks(struct pvr2_context *mp)
+{
+ struct pvr2_channel *ch1,*ch2;
+ for (ch1 = mp->mc_first; ch1; ch1 = ch2) {
+ ch2 = ch1->mc_next;
+ if (ch1->check_func) {
+ ch1->check_func(ch1);
+ }
+ }
+}
+
+
+void pvr2_context_disconnect(struct pvr2_context *mp)
+{
+ pvr2_context_enter(mp); do {
+ pvr2_hdw_disconnect(mp->hdw);
+ mp->disconnect_flag = !0;
+ pvr2_context_run_checks(mp);
+ } while (0); pvr2_context_exit(mp);
+}
+
+
+void pvr2_channel_init(struct pvr2_channel *cp,struct pvr2_context *mp)
+{
+ cp->hdw = mp->hdw;
+ cp->mc_head = mp;
+ cp->mc_next = 0;
+ cp->mc_prev = mp->mc_last;
+ if (mp->mc_last) {
+ mp->mc_last->mc_next = cp;
+ } else {
+ mp->mc_first = cp;
+ }
+ mp->mc_last = cp;
+}
+
+
+static void pvr2_channel_disclaim_stream(struct pvr2_channel *cp)
+{
+ if (!cp->stream) return;
+ pvr2_stream_kill(cp->stream->stream);
+ cp->stream->user = 0;
+ cp->stream = 0;
+}
+
+
+void pvr2_channel_done(struct pvr2_channel *cp)
+{
+ struct pvr2_context *mp = cp->mc_head;
+ pvr2_channel_disclaim_stream(cp);
+ if (cp->mc_next) {
+ cp->mc_next->mc_prev = cp->mc_prev;
+ } else {
+ mp->mc_last = cp->mc_prev;
+ }
+ if (cp->mc_prev) {
+ cp->mc_prev->mc_next = cp->mc_next;
+ } else {
+ mp->mc_first = cp->mc_next;
+ }
+ cp->hdw = 0;
+}
+
+
+int pvr2_channel_claim_stream(struct pvr2_channel *cp,
+ struct pvr2_context_stream *sp)
+{
+ int code = 0;
+ pvr2_context_enter(cp->mc_head); do {
+ if (sp == cp->stream) break;
+ if (sp->user) {
+ code = -EBUSY;
+ break;
+ }
+ pvr2_channel_disclaim_stream(cp);
+ if (!sp) break;
+ sp->user = cp;
+ cp->stream = sp;
+ } while (0); pvr2_context_exit(cp->mc_head);
+ return code;
+}
+
+
+// This is the marker for the real beginning of a legitimate mpeg2 stream.
+static char stream_sync_key[] = {
+ 0x00, 0x00, 0x01, 0xba,
+};
+
+struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
+ struct pvr2_context_stream *sp)
+{
+ struct pvr2_ioread *cp;
+ cp = pvr2_ioread_create();
+ if (!cp) return 0;
+ pvr2_ioread_setup(cp,sp->stream);
+ pvr2_ioread_set_sync_key(cp,stream_sync_key,sizeof(stream_sync_key));
+ return cp;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-context.h b/drivers/media/video/pvrusb2/pvrusb2-context.h
new file mode 100644
index 000000000000..6327fa1f7e4f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-context.h
@@ -0,0 +1,92 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_BASE_H
+#define __PVRUSB2_BASE_H
+
+#include <linux/mutex.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+
+struct pvr2_hdw; /* hardware interface - defined elsewhere */
+struct pvr2_stream; /* stream interface - defined elsewhere */
+
+struct pvr2_context; /* All central state */
+struct pvr2_channel; /* One I/O pathway to a user */
+struct pvr2_context_stream; /* Wrapper for a stream */
+struct pvr2_crit_reg; /* Critical region pointer */
+struct pvr2_ioread; /* Low level stream structure */
+
+struct pvr2_context_stream {
+ struct pvr2_channel *user;
+ struct pvr2_stream *stream;
+};
+
+struct pvr2_context {
+ struct pvr2_channel *mc_first;
+ struct pvr2_channel *mc_last;
+ struct pvr2_hdw *hdw;
+ struct pvr2_context_stream video_stream;
+ struct mutex mutex;
+ int disconnect_flag;
+
+ /* Called after pvr2_context initialization is complete */
+ void (*setup_func)(struct pvr2_context *);
+
+ /* Work queue overhead for out-of-line processing */
+ struct workqueue_struct *workqueue;
+ struct work_struct workinit;
+ struct work_struct workpoll;
+};
+
+struct pvr2_channel {
+ struct pvr2_context *mc_head;
+ struct pvr2_channel *mc_next;
+ struct pvr2_channel *mc_prev;
+ struct pvr2_context_stream *stream;
+ struct pvr2_hdw *hdw;
+ void (*check_func)(struct pvr2_channel *);
+};
+
+void pvr2_context_enter(struct pvr2_context *);
+void pvr2_context_exit(struct pvr2_context *);
+
+struct pvr2_context *pvr2_context_create(struct usb_interface *intf,
+ const struct usb_device_id *devid,
+ void (*setup_func)(struct pvr2_context *));
+void pvr2_context_disconnect(struct pvr2_context *);
+
+void pvr2_channel_init(struct pvr2_channel *,struct pvr2_context *);
+void pvr2_channel_done(struct pvr2_channel *);
+int pvr2_channel_claim_stream(struct pvr2_channel *,
+ struct pvr2_context_stream *);
+struct pvr2_ioread *pvr2_channel_create_mpeg_stream(
+ struct pvr2_context_stream *);
+
+
+#endif /* __PVRUSB2_CONTEXT_H */
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
new file mode 100644
index 000000000000..d5df9fbeba2f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -0,0 +1,593 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-ctrl.h"
+#include "pvrusb2-hdw-internal.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mutex.h>
+
+
+/* Set the given control. */
+int pvr2_ctrl_set_value(struct pvr2_ctrl *cptr,int val)
+{
+ return pvr2_ctrl_set_mask_value(cptr,~0,val);
+}
+
+
+/* Set/clear specific bits of the given control. */
+int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
+{
+ int ret = 0;
+ if (!cptr) return -EINVAL;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->set_value != 0) {
+ if (cptr->info->type == pvr2_ctl_bitmask) {
+ mask &= cptr->info->def.type_bitmask.valid_bits;
+ } else if (cptr->info->type == pvr2_ctl_int) {
+ if (val < cptr->info->def.type_int.min_value) {
+ break;
+ }
+ if (val > cptr->info->def.type_int.max_value) {
+ break;
+ }
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ if (val >= cptr->info->def.type_enum.count) {
+ break;
+ }
+ } else if (cptr->info->type != pvr2_ctl_bool) {
+ break;
+ }
+ ret = cptr->info->set_value(cptr,mask,val);
+ } else {
+ ret = -EPERM;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Get the current value of the given control. */
+int pvr2_ctrl_get_value(struct pvr2_ctrl *cptr,int *valptr)
+{
+ int ret = 0;
+ if (!cptr) return -EINVAL;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ ret = cptr->info->get_value(cptr,valptr);
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's type */
+enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return pvr2_ctl_int;
+ return cptr->info->type;
+}
+
+
+/* Retrieve control's maximum value (int type) */
+int pvr2_ctrl_get_max(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_int) {
+ ret = cptr->info->def.type_int.max_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's minimum value (int type) */
+int pvr2_ctrl_get_min(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_int) {
+ ret = cptr->info->def.type_int.min_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's default value (any type) */
+int pvr2_ctrl_get_def(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_int) {
+ ret = cptr->info->default_value;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's enumeration count (enum only) */
+int pvr2_ctrl_get_cnt(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_enum) {
+ ret = cptr->info->def.type_enum.count;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve control's valid mask bits (bit mask only) */
+int pvr2_ctrl_get_mask(struct pvr2_ctrl *cptr)
+{
+ int ret = 0;
+ if (!cptr) return 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_bitmask) {
+ ret = cptr->info->def.type_bitmask.valid_bits;
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Retrieve the control's name */
+const char *pvr2_ctrl_get_name(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->name;
+}
+
+
+/* Retrieve the control's desc */
+const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->desc;
+}
+
+
+/* Retrieve a control enumeration or bit mask value */
+int pvr2_ctrl_get_valname(struct pvr2_ctrl *cptr,int val,
+ char *bptr,unsigned int bmax,
+ unsigned int *blen)
+{
+ int ret = -EINVAL;
+ if (!cptr) return 0;
+ *blen = 0;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_enum) {
+ const char **names;
+ names = cptr->info->def.type_enum.value_names;
+ if ((val >= 0) &&
+ (val < cptr->info->def.type_enum.count)) {
+ if (names[val]) {
+ *blen = scnprintf(
+ bptr,bmax,"%s",
+ names[val]);
+ } else {
+ *blen = 0;
+ }
+ ret = 0;
+ }
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ const char **names;
+ unsigned int idx;
+ int msk;
+ names = cptr->info->def.type_bitmask.bit_names;
+ val &= cptr->info->def.type_bitmask.valid_bits;
+ for (idx = 0, msk = 1; val; idx++, msk <<= 1) {
+ if (val & msk) {
+ *blen = scnprintf(bptr,bmax,"%s",
+ names[idx]);
+ ret = 0;
+ break;
+ }
+ }
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Return V4L ID for this control or zero if none */
+int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->v4l_id;
+}
+
+
+unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *cptr)
+{
+ unsigned int flags = 0;
+
+ if (cptr->info->get_v4lflags) {
+ flags = cptr->info->get_v4lflags(cptr);
+ }
+
+ if (cptr->info->set_value) {
+ flags &= ~V4L2_CTRL_FLAG_READ_ONLY;
+ } else {
+ flags |= V4L2_CTRL_FLAG_READ_ONLY;
+ }
+
+ return flags;
+}
+
+
+/* Return true if control is writable */
+int pvr2_ctrl_is_writable(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ return cptr->info->set_value != 0;
+}
+
+
+/* Return true if control has custom symbolic representation */
+int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *cptr)
+{
+ if (!cptr) return 0;
+ if (!cptr->info->val_to_sym) return 0;
+ if (!cptr->info->sym_to_val) return 0;
+ return !0;
+}
+
+
+/* Convert a given mask/val to a custom symbolic value */
+int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ if (!cptr) return -EINVAL;
+ if (!cptr->info->val_to_sym) return -EINVAL;
+ return cptr->info->val_to_sym(cptr,mask,val,buf,maxlen,len);
+}
+
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *cptr,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr)
+{
+ if (!cptr) return -EINVAL;
+ if (!cptr->info->sym_to_val) return -EINVAL;
+ return cptr->info->sym_to_val(cptr,buf,len,maskptr,valptr);
+}
+
+
+static unsigned int gen_bitmask_string(int msk,int val,int msk_only,
+ const char **names,
+ char *ptr,unsigned int len)
+{
+ unsigned int idx;
+ long sm,um;
+ int spcFl;
+ unsigned int uc,cnt;
+ const char *idStr;
+
+ spcFl = 0;
+ uc = 0;
+ um = 0;
+ for (idx = 0, sm = 1; msk; idx++, sm <<= 1) {
+ if (sm & msk) {
+ msk &= ~sm;
+ idStr = names[idx];
+ if (idStr) {
+ cnt = scnprintf(ptr,len,"%s%s%s",
+ (spcFl ? " " : ""),
+ (msk_only ? "" :
+ ((val & sm) ? "+" : "-")),
+ idStr);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else {
+ um |= sm;
+ }
+ }
+ }
+ if (um) {
+ if (msk_only) {
+ cnt = scnprintf(ptr,len,"%s0x%lx",
+ (spcFl ? " " : ""),
+ um);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else if (um & val) {
+ cnt = scnprintf(ptr,len,"%s+0x%lx",
+ (spcFl ? " " : ""),
+ um & val);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ } else if (um & ~val) {
+ cnt = scnprintf(ptr,len,"%s+0x%lx",
+ (spcFl ? " " : ""),
+ um & ~val);
+ ptr += cnt; len -= cnt; uc += cnt;
+ spcFl = !0;
+ }
+ }
+ return uc;
+}
+
+
+static const char *boolNames[] = {
+ "false",
+ "true",
+ "no",
+ "yes",
+};
+
+
+static int parse_token(const char *ptr,unsigned int len,
+ int *valptr,
+ const char **names,unsigned int namecnt)
+{
+ char buf[33];
+ unsigned int slen;
+ unsigned int idx;
+ int negfl;
+ char *p2;
+ *valptr = 0;
+ if (!names) namecnt = 0;
+ for (idx = 0; idx < namecnt; idx++) {
+ if (!names[idx]) continue;
+ slen = strlen(names[idx]);
+ if (slen != len) continue;
+ if (memcmp(names[idx],ptr,slen)) continue;
+ *valptr = idx;
+ return 0;
+ }
+ negfl = 0;
+ if ((*ptr == '-') || (*ptr == '+')) {
+ negfl = (*ptr == '-');
+ ptr++; len--;
+ }
+ if (len >= sizeof(buf)) return -EINVAL;
+ memcpy(buf,ptr,len);
+ buf[len] = 0;
+ *valptr = simple_strtol(buf,&p2,0);
+ if (negfl) *valptr = -(*valptr);
+ if (*p2) return -EINVAL;
+ return 1;
+}
+
+
+static int parse_mtoken(const char *ptr,unsigned int len,
+ int *valptr,
+ const char **names,int valid_bits)
+{
+ char buf[33];
+ unsigned int slen;
+ unsigned int idx;
+ char *p2;
+ int msk;
+ *valptr = 0;
+ for (idx = 0, msk = 1; valid_bits; idx++, msk <<= 1) {
+ if (!msk & valid_bits) continue;
+ valid_bits &= ~msk;
+ if (!names[idx]) continue;
+ slen = strlen(names[idx]);
+ if (slen != len) continue;
+ if (memcmp(names[idx],ptr,slen)) continue;
+ *valptr = msk;
+ return 0;
+ }
+ if (len >= sizeof(buf)) return -EINVAL;
+ memcpy(buf,ptr,len);
+ buf[len] = 0;
+ *valptr = simple_strtol(buf,&p2,0);
+ if (*p2) return -EINVAL;
+ return 0;
+}
+
+
+static int parse_tlist(const char *ptr,unsigned int len,
+ int *maskptr,int *valptr,
+ const char **names,int valid_bits)
+{
+ unsigned int cnt;
+ int mask,val,kv,mode,ret;
+ mask = 0;
+ val = 0;
+ ret = 0;
+ while (len) {
+ cnt = 0;
+ while ((cnt < len) &&
+ ((ptr[cnt] <= 32) ||
+ (ptr[cnt] >= 127))) cnt++;
+ ptr += cnt;
+ len -= cnt;
+ mode = 0;
+ if ((*ptr == '-') || (*ptr == '+')) {
+ mode = (*ptr == '-') ? -1 : 1;
+ ptr++;
+ len--;
+ }
+ cnt = 0;
+ while (cnt < len) {
+ if (ptr[cnt] <= 32) break;
+ if (ptr[cnt] >= 127) break;
+ cnt++;
+ }
+ if (!cnt) break;
+ if (parse_mtoken(ptr,cnt,&kv,names,valid_bits)) {
+ ret = -EINVAL;
+ break;
+ }
+ ptr += cnt;
+ len -= cnt;
+ switch (mode) {
+ case 0:
+ mask = valid_bits;
+ val |= kv;
+ break;
+ case -1:
+ mask |= kv;
+ val &= ~kv;
+ break;
+ case 1:
+ mask |= kv;
+ val |= kv;
+ break;
+ default:
+ break;
+ }
+ }
+ *maskptr = mask;
+ *valptr = val;
+ return ret;
+}
+
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *cptr,
+ const char *ptr,unsigned int len,
+ int *maskptr,int *valptr)
+{
+ int ret = -EINVAL;
+ unsigned int cnt;
+
+ *maskptr = 0;
+ *valptr = 0;
+
+ cnt = 0;
+ while ((cnt < len) && ((ptr[cnt] <= 32) || (ptr[cnt] >= 127))) cnt++;
+ len -= cnt; ptr += cnt;
+ cnt = 0;
+ while ((cnt < len) && ((ptr[len-(cnt+1)] <= 32) ||
+ (ptr[len-(cnt+1)] >= 127))) cnt++;
+ len -= cnt;
+
+ if (!len) return -EINVAL;
+
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ if (cptr->info->type == pvr2_ctl_int) {
+ ret = parse_token(ptr,len,valptr,0,0);
+ if ((ret >= 0) &&
+ ((*valptr < cptr->info->def.type_int.min_value) ||
+ (*valptr > cptr->info->def.type_int.max_value))) {
+ ret = -ERANGE;
+ }
+ if (maskptr) *maskptr = ~0;
+ } else if (cptr->info->type == pvr2_ctl_bool) {
+ ret = parse_token(
+ ptr,len,valptr,boolNames,
+ sizeof(boolNames)/sizeof(boolNames[0]));
+ if (ret == 1) {
+ *valptr = *valptr ? !0 : 0;
+ } else if (ret == 0) {
+ *valptr = (*valptr & 1) ? !0 : 0;
+ }
+ if (maskptr) *maskptr = 1;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ ret = parse_token(
+ ptr,len,valptr,
+ cptr->info->def.type_enum.value_names,
+ cptr->info->def.type_enum.count);
+ if ((ret >= 0) &&
+ ((*valptr < 0) ||
+ (*valptr >= cptr->info->def.type_enum.count))) {
+ ret = -ERANGE;
+ }
+ if (maskptr) *maskptr = ~0;
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ ret = parse_tlist(
+ ptr,len,maskptr,valptr,
+ cptr->info->def.type_bitmask.bit_names,
+ cptr->info->def.type_bitmask.valid_bits);
+ }
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ int ret = -EINVAL;
+
+ *len = 0;
+ if (cptr->info->type == pvr2_ctl_int) {
+ *len = scnprintf(buf,maxlen,"%d",val);
+ ret = 0;
+ } else if (cptr->info->type == pvr2_ctl_bool) {
+ *len = scnprintf(buf,maxlen,"%s",val ? "true" : "false");
+ ret = 0;
+ } else if (cptr->info->type == pvr2_ctl_enum) {
+ const char **names;
+ names = cptr->info->def.type_enum.value_names;
+ if ((val >= 0) &&
+ (val < cptr->info->def.type_enum.count)) {
+ if (names[val]) {
+ *len = scnprintf(
+ buf,maxlen,"%s",
+ names[val]);
+ } else {
+ *len = 0;
+ }
+ ret = 0;
+ }
+ } else if (cptr->info->type == pvr2_ctl_bitmask) {
+ *len = gen_bitmask_string(
+ val & mask & cptr->info->def.type_bitmask.valid_bits,
+ ~0,!0,
+ cptr->info->def.type_bitmask.bit_names,
+ buf,maxlen);
+ }
+ return ret;
+}
+
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *cptr,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len)
+{
+ int ret;
+ LOCK_TAKE(cptr->hdw->big_lock); do {
+ ret = pvr2_ctrl_value_to_sym_internal(cptr,mask,val,
+ buf,maxlen,len);
+ } while(0); LOCK_GIVE(cptr->hdw->big_lock);
+ return ret;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.h b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
new file mode 100644
index 000000000000..c1680053cd64
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.h
@@ -0,0 +1,123 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_CTRL_H
+#define __PVRUSB2_CTRL_H
+
+struct pvr2_ctrl;
+
+enum pvr2_ctl_type {
+ pvr2_ctl_int = 0,
+ pvr2_ctl_enum = 1,
+ pvr2_ctl_bitmask = 2,
+ pvr2_ctl_bool = 3,
+};
+
+
+/* Set the given control. */
+int pvr2_ctrl_set_value(struct pvr2_ctrl *,int val);
+
+/* Set/clear specific bits of the given control. */
+int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *,int mask,int val);
+
+/* Get the current value of the given control. */
+int pvr2_ctrl_get_value(struct pvr2_ctrl *,int *valptr);
+
+/* Retrieve control's type */
+enum pvr2_ctl_type pvr2_ctrl_get_type(struct pvr2_ctrl *);
+
+/* Retrieve control's maximum value (int type) */
+int pvr2_ctrl_get_max(struct pvr2_ctrl *);
+
+/* Retrieve control's minimum value (int type) */
+int pvr2_ctrl_get_min(struct pvr2_ctrl *);
+
+/* Retrieve control's default value (any type) */
+int pvr2_ctrl_get_def(struct pvr2_ctrl *);
+
+/* Retrieve control's enumeration count (enum only) */
+int pvr2_ctrl_get_cnt(struct pvr2_ctrl *);
+
+/* Retrieve control's valid mask bits (bit mask only) */
+int pvr2_ctrl_get_mask(struct pvr2_ctrl *);
+
+/* Retrieve the control's name */
+const char *pvr2_ctrl_get_name(struct pvr2_ctrl *);
+
+/* Retrieve the control's desc */
+const char *pvr2_ctrl_get_desc(struct pvr2_ctrl *);
+
+/* Retrieve a control enumeration or bit mask value */
+int pvr2_ctrl_get_valname(struct pvr2_ctrl *,int,char *,unsigned int,
+ unsigned int *);
+
+/* Return true if control is writable */
+int pvr2_ctrl_is_writable(struct pvr2_ctrl *);
+
+/* Return V4L flags value for control (or zero if there is no v4l control
+ actually under this control) */
+unsigned int pvr2_ctrl_get_v4lflags(struct pvr2_ctrl *);
+
+/* Return V4L ID for this control or zero if none */
+int pvr2_ctrl_get_v4lid(struct pvr2_ctrl *);
+
+/* Return true if control has custom symbolic representation */
+int pvr2_ctrl_has_custom_symbols(struct pvr2_ctrl *);
+
+/* Convert a given mask/val to a custom symbolic value */
+int pvr2_ctrl_custom_value_to_sym(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_custom_sym_to_value(struct pvr2_ctrl *,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr);
+
+/* Convert a given mask/val to a symbolic value */
+int pvr2_ctrl_value_to_sym(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+/* Convert a symbolic value to a mask/value pair */
+int pvr2_ctrl_sym_to_value(struct pvr2_ctrl *,
+ const char *buf,unsigned int len,
+ int *maskptr,int *valptr);
+
+/* Convert a given mask/val to a symbolic value - must already be
+ inside of critical region. */
+int pvr2_ctrl_value_to_sym_internal(struct pvr2_ctrl *,
+ int mask,int val,
+ char *buf,unsigned int maxlen,
+ unsigned int *len);
+
+#endif /* __PVRUSB2_CTRL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
new file mode 100644
index 000000000000..27eadaff75a0
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -0,0 +1,279 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ cx2584x, in kernels 2.6.16 or newer.
+
+*/
+
+#include "pvrusb2-cx2584x-v4l.h"
+#include "pvrusb2-video-v4l.h"
+#include "pvrusb2-i2c-cmd-v4l2.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <media/cx25840.h>
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+struct pvr2_v4l_cx2584x {
+ struct pvr2_i2c_handler handler;
+ struct pvr2_decoder_ctrl ctrl;
+ struct pvr2_i2c_client *client;
+ struct pvr2_hdw *hdw;
+ unsigned long stale_mask;
+};
+
+
+static void set_input(struct pvr2_v4l_cx2584x *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ struct v4l2_routing route;
+ enum cx25840_video_input vid_input;
+ enum cx25840_audio_input aud_input;
+
+ memset(&route,0,sizeof(route));
+
+ switch(hdw->input_val) {
+ case PVR2_CVAL_INPUT_TV:
+ vid_input = CX25840_COMPOSITE7;
+ aud_input = CX25840_AUDIO8;
+ break;
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ vid_input = CX25840_COMPOSITE3;
+ aud_input = CX25840_AUDIO_SERIAL;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ vid_input = CX25840_SVIDEO1;
+ aud_input = CX25840_AUDIO_SERIAL;
+ break;
+ case PVR2_CVAL_INPUT_RADIO:
+ default:
+ // Just set it to be composite input for now...
+ vid_input = CX25840_COMPOSITE3;
+ aud_input = CX25840_AUDIO_SERIAL;
+ break;
+ }
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_input vid=0x%x aud=0x%x",
+ vid_input,aud_input);
+ route.input = (u32)vid_input;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
+ route.input = (u32)aud_input;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+}
+
+
+static int check_input(struct pvr2_v4l_cx2584x *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return hdw->input_dirty != 0;
+}
+
+
+static void set_audio(struct pvr2_v4l_cx2584x *ctxt)
+{
+ u32 val;
+ struct pvr2_hdw *hdw = ctxt->hdw;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx2584x set_audio %d",
+ hdw->srate_val);
+ switch (hdw->srate_val) {
+ default:
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
+ val = 48000;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
+ val = 44100;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+ val = 32000;
+ break;
+ }
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
+}
+
+
+static int check_audio(struct pvr2_v4l_cx2584x *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return hdw->srate_dirty != 0;
+}
+
+
+struct pvr2_v4l_cx2584x_ops {
+ void (*update)(struct pvr2_v4l_cx2584x *);
+ int (*check)(struct pvr2_v4l_cx2584x *);
+};
+
+
+static const struct pvr2_v4l_cx2584x_ops decoder_ops[] = {
+ { .update = set_input, .check = check_input},
+ { .update = set_audio, .check = check_audio},
+};
+
+
+static void decoder_detach(struct pvr2_v4l_cx2584x *ctxt)
+{
+ ctxt->client->handler = 0;
+ ctxt->hdw->decoder_ctrl = 0;
+ kfree(ctxt);
+}
+
+
+static int decoder_check(struct pvr2_v4l_cx2584x *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (ctxt->stale_mask & msk) continue;
+ if (decoder_ops[idx].check(ctxt)) {
+ ctxt->stale_mask |= msk;
+ }
+ }
+ return ctxt->stale_mask != 0;
+}
+
+
+static void decoder_update(struct pvr2_v4l_cx2584x *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (!(ctxt->stale_mask & msk)) continue;
+ ctxt->stale_mask &= ~msk;
+ decoder_ops[idx].update(ctxt);
+ }
+}
+
+
+static void decoder_enable(struct pvr2_v4l_cx2584x *ctxt,int fl)
+{
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_enable(%d)",fl);
+ pvr2_v4l2_cmd_stream(ctxt->client,fl);
+}
+
+
+static int decoder_detect(struct pvr2_i2c_client *cp)
+{
+ int ret;
+ /* Attempt to query the decoder - let's see if it will answer */
+ struct v4l2_queryctrl qc;
+
+ memset(&qc,0,sizeof(qc));
+
+ qc.id = V4L2_CID_BRIGHTNESS;
+
+ ret = pvr2_i2c_client_cmd(cp,VIDIOC_QUERYCTRL,&qc);
+ return ret == 0; /* Return true if it answered */
+}
+
+
+static int decoder_is_tuned(struct pvr2_v4l_cx2584x *ctxt)
+{
+ struct v4l2_tuner vt;
+ int ret;
+
+ memset(&vt,0,sizeof(vt));
+ ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
+ if (ret < 0) return -EINVAL;
+ return vt.signal ? 1 : 0;
+}
+
+
+static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt,
+ char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-cx2584x-v4l");
+}
+
+
+static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt)
+{
+ int ret;
+ ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0);
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret);
+}
+
+
+const static struct pvr2_i2c_handler_functions hfuncs = {
+ .detach = (void (*)(void *))decoder_detach,
+ .check = (int (*)(void *))decoder_check,
+ .update = (void (*)(void *))decoder_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
+};
+
+
+int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *hdw,
+ struct pvr2_i2c_client *cp)
+{
+ struct pvr2_v4l_cx2584x *ctxt;
+
+ if (hdw->decoder_ctrl) return 0;
+ if (cp->handler) return 0;
+ if (!decoder_detect(cp)) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->handler.func_data = ctxt;
+ ctxt->handler.func_table = &hfuncs;
+ ctxt->ctrl.ctxt = ctxt;
+ ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
+ ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
+ ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned;
+ ctxt->ctrl.force_reset = (void (*)(void*))decoder_reset;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ ctxt->stale_mask = (1 << (sizeof(decoder_ops)/
+ sizeof(decoder_ops[0]))) - 1;
+ hdw->decoder_ctrl = &ctxt->ctrl;
+ cp->handler = &ctxt->handler;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x cx2584x V4L2 handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
new file mode 100644
index 000000000000..54b2844e7a71
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_CX2584X_V4L_H
+#define __PVRUSB2_CX2584X_V4L_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles combined device audio & video processing.
+ This interface is used internally by the driver; higher level code
+ should only interact through the interface provided by
+ pvrusb2-hdw.h.
+
+*/
+
+
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_cx2584x_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+
+#endif /* __PVRUSB2_CX2584X_V4L_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debug.h b/drivers/media/video/pvrusb2/pvrusb2-debug.h
new file mode 100644
index 000000000000..d95a8588e4f8
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debug.h
@@ -0,0 +1,67 @@
+/*
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEBUG_H
+#define __PVRUSB2_DEBUG_H
+
+extern int pvrusb2_debug;
+
+#define pvr2_trace(msk, fmt, arg...) do {if(msk & pvrusb2_debug) printk(KERN_INFO "pvrusb2: " fmt "\n", ##arg); } while (0)
+
+/* These are listed in *rough* order of decreasing usefulness and
+ increasing noise level. */
+#define PVR2_TRACE_INFO (1 << 0) // Normal messages
+#define PVR2_TRACE_ERROR_LEGS (1 << 1) // error messages
+#define PVR2_TRACE_TOLERANCE (1 << 2) // track tolerance-affected errors
+#define PVR2_TRACE_TRAP (1 << 3) // Trap & report misbehavior from app
+#define PVR2_TRACE_INIT (1 << 4) // misc initialization steps
+#define PVR2_TRACE_START_STOP (1 << 5) // Streaming start / stop
+#define PVR2_TRACE_CTL (1 << 6) // commit of control changes
+#define PVR2_TRACE_DEBUG (1 << 7) // Temporary debug code
+#define PVR2_TRACE_EEPROM (1 << 8) // eeprom parsing / report
+#define PVR2_TRACE_STRUCT (1 << 9) // internal struct creation
+#define PVR2_TRACE_OPEN_CLOSE (1 << 10) // application open / close
+#define PVR2_TRACE_CREG (1 << 11) // Main critical region entry / exit
+#define PVR2_TRACE_SYSFS (1 << 12) // Sysfs driven I/O
+#define PVR2_TRACE_FIRMWARE (1 << 13) // firmware upload actions
+#define PVR2_TRACE_CHIPS (1 << 14) // chip broadcast operation
+#define PVR2_TRACE_I2C (1 << 15) // I2C related stuff
+#define PVR2_TRACE_I2C_CMD (1 << 16) // Software commands to I2C modules
+#define PVR2_TRACE_I2C_CORE (1 << 17) // I2C core debugging
+#define PVR2_TRACE_I2C_TRAF (1 << 18) // I2C traffic through the adapter
+#define PVR2_TRACE_V4LIOCTL (1 << 19) // v4l ioctl details
+#define PVR2_TRACE_ENCODER (1 << 20) // mpeg2 encoder operation
+#define PVR2_TRACE_BUF_POOL (1 << 21) // Track buffer pool management
+#define PVR2_TRACE_BUF_FLOW (1 << 22) // Track buffer flow in system
+#define PVR2_TRACE_DATA_FLOW (1 << 23) // Track data flow
+#define PVR2_TRACE_DEBUGIFC (1 << 24) // Debug interface actions
+#define PVR2_TRACE_GPIO (1 << 25) // GPIO state bit changes
+
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.c b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
new file mode 100644
index 000000000000..586900e365ff
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.c
@@ -0,0 +1,478 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 <linux/string.h>
+#include <linux/slab.h>
+#include "pvrusb2-debugifc.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-i2c-core.h"
+
+struct debugifc_mask_item {
+ const char *name;
+ unsigned long msk;
+};
+
+static struct debugifc_mask_item mask_items[] = {
+ {"ENC_FIRMWARE",(1<<PVR2_SUBSYS_B_ENC_FIRMWARE)},
+ {"ENC_CFG",(1<<PVR2_SUBSYS_B_ENC_CFG)},
+ {"DIG_RUN",(1<<PVR2_SUBSYS_B_DIGITIZER_RUN)},
+ {"USB_RUN",(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)},
+ {"ENC_RUN",(1<<PVR2_SUBSYS_B_ENC_RUN)},
+};
+
+
+static unsigned int debugifc_count_whitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') continue;
+ if (ch == '\t') continue;
+ if (ch == '\n') continue;
+ break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_count_nonwhitespace(const char *buf,
+ unsigned int count)
+{
+ unsigned int scnt;
+ char ch;
+
+ for (scnt = 0; scnt < count; scnt++) {
+ ch = buf[scnt];
+ if (ch == ' ') break;
+ if (ch == '\t') break;
+ if (ch == '\n') break;
+ }
+ return scnt;
+}
+
+
+static unsigned int debugifc_isolate_word(const char *buf,unsigned int count,
+ const char **wstrPtr,
+ unsigned int *wlenPtr)
+{
+ const char *wptr;
+ unsigned int consume_cnt = 0;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ wptr = 0;
+ wlen = 0;
+ scnt = debugifc_count_whitespace(buf,count);
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+ if (!count) goto done;
+
+ scnt = debugifc_count_nonwhitespace(buf,count);
+ if (!scnt) goto done;
+ wptr = buf;
+ wlen = scnt;
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+
+ done:
+ *wstrPtr = wptr;
+ *wlenPtr = wlen;
+ return consume_cnt;
+}
+
+
+static int debugifc_parse_unsigned_number(const char *buf,unsigned int count,
+ u32 *num_ptr)
+{
+ u32 result = 0;
+ u32 val;
+ int ch;
+ int radix = 10;
+ if ((count >= 2) && (buf[0] == '0') &&
+ ((buf[1] == 'x') || (buf[1] == 'X'))) {
+ radix = 16;
+ count -= 2;
+ buf += 2;
+ } else if ((count >= 1) && (buf[0] == '0')) {
+ radix = 8;
+ }
+
+ while (count--) {
+ ch = *buf++;
+ if ((ch >= '0') && (ch <= '9')) {
+ val = ch - '0';
+ } else if ((ch >= 'a') && (ch <= 'f')) {
+ val = ch - 'a' + 10;
+ } else if ((ch >= 'A') && (ch <= 'F')) {
+ val = ch - 'A' + 10;
+ } else {
+ return -EINVAL;
+ }
+ if (val >= radix) return -EINVAL;
+ result *= radix;
+ result += val;
+ }
+ *num_ptr = result;
+ return 0;
+}
+
+
+static int debugifc_match_keyword(const char *buf,unsigned int count,
+ const char *keyword)
+{
+ unsigned int kl;
+ if (!keyword) return 0;
+ kl = strlen(keyword);
+ if (kl != count) return 0;
+ return !memcmp(buf,keyword,kl);
+}
+
+
+static unsigned long debugifc_find_mask(const char *buf,unsigned int count)
+{
+ struct debugifc_mask_item *mip;
+ unsigned int idx;
+ for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) {
+ mip = mask_items + idx;
+ if (debugifc_match_keyword(buf,count,mip->name)) {
+ return mip->msk;
+ }
+ }
+ return 0;
+}
+
+
+static int debugifc_print_mask(char *buf,unsigned int sz,
+ unsigned long msk,unsigned long val)
+{
+ struct debugifc_mask_item *mip;
+ unsigned int idx;
+ int bcnt = 0;
+ int ccnt;
+ for (idx = 0; idx < sizeof(mask_items)/sizeof(mask_items[0]); idx++) {
+ mip = mask_items + idx;
+ if (!(mip->msk & msk)) continue;
+ ccnt = scnprintf(buf,sz,"%s%c%s",
+ (bcnt ? " " : ""),
+ ((mip->msk & val) ? '+' : '-'),
+ mip->name);
+ sz -= ccnt;
+ buf += ccnt;
+ bcnt += ccnt;
+ }
+ return bcnt;
+}
+
+static unsigned int debugifc_parse_subsys_mask(const char *buf,
+ unsigned int count,
+ unsigned long *mskPtr,
+ unsigned long *valPtr)
+{
+ const char *wptr;
+ unsigned int consume_cnt = 0;
+ unsigned int scnt;
+ unsigned int wlen;
+ int mode;
+ unsigned long m1,msk,val;
+
+ msk = 0;
+ val = 0;
+
+ while (count) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) break;
+ consume_cnt += scnt; count -= scnt; buf += scnt;
+ if (!wptr) break;
+
+ mode = 0;
+ if (wlen) switch (wptr[0]) {
+ case '+':
+ wptr++;
+ wlen--;
+ break;
+ case '-':
+ mode = 1;
+ wptr++;
+ wlen--;
+ break;
+ }
+ if (!wlen) continue;
+ m1 = debugifc_find_mask(wptr,wlen);
+ if (!m1) break;
+ msk |= m1;
+ if (!mode) val |= m1;
+ }
+ *mskPtr = msk;
+ *valPtr = val;
+ return consume_cnt;
+}
+
+
+int pvr2_debugifc_print_info(struct pvr2_hdw *hdw,char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ struct pvr2_hdw_debug_info dbg;
+
+ pvr2_hdw_get_debug_info(hdw,&dbg);
+
+ ccnt = scnprintf(buf,acnt,"big lock %s; ctl lock %s",
+ (dbg.big_lock_held ? "held" : "free"),
+ (dbg.ctl_lock_held ? "held" : "free"));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ if (dbg.ctl_lock_held) {
+ ccnt = scnprintf(buf,acnt,"; cmd_state=%d cmd_code=%d"
+ " cmd_wlen=%d cmd_rlen=%d"
+ " wpend=%d rpend=%d tmout=%d rstatus=%d"
+ " wstatus=%d",
+ dbg.cmd_debug_state,dbg.cmd_code,
+ dbg.cmd_debug_write_len,
+ dbg.cmd_debug_read_len,
+ dbg.cmd_debug_write_pend,
+ dbg.cmd_debug_read_pend,
+ dbg.cmd_debug_timeout,
+ dbg.cmd_debug_rstatus,
+ dbg.cmd_debug_wstatus);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ }
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(
+ buf,acnt,"driver flags: %s %s %s\n",
+ (dbg.flag_init_ok ? "initialized" : "uninitialized"),
+ (dbg.flag_ok ? "ok" : "fail"),
+ (dbg.flag_disconnected ? "disconnected" : "connected"));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,dbg.subsys_flags,dbg.subsys_flags);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,~dbg.subsys_flags,dbg.subsys_flags);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ ccnt = scnprintf(buf,acnt,"Attached I2C modules:\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = pvr2_i2c_report(hdw,buf,acnt);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+}
+
+
+int pvr2_debugifc_print_status(struct pvr2_hdw *hdw,
+ char *buf,unsigned int acnt)
+{
+ int bcnt = 0;
+ int ccnt;
+ unsigned long msk;
+ int ret;
+ u32 gpio_dir,gpio_in,gpio_out;
+
+ ret = pvr2_hdw_is_hsm(hdw);
+ ccnt = scnprintf(buf,acnt,"USB link speed: %s\n",
+ (ret < 0 ? "FAIL" : (ret ? "high" : "full")));
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ gpio_dir = 0; gpio_in = 0; gpio_out = 0;
+ pvr2_hdw_gpio_get_dir(hdw,&gpio_dir);
+ pvr2_hdw_gpio_get_out(hdw,&gpio_out);
+ pvr2_hdw_gpio_get_in(hdw,&gpio_in);
+ ccnt = scnprintf(buf,acnt,"GPIO state: dir=0x%x in=0x%x out=0x%x\n",
+ gpio_dir,gpio_in,gpio_out);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ ccnt = scnprintf(buf,acnt,"Streaming is %s\n",
+ pvr2_hdw_get_streaming(hdw) ? "on" : "off");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ msk = pvr2_hdw_subsys_get(hdw);
+ ccnt = scnprintf(buf,acnt,"Subsystems enabled / configured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"Subsystems disabled / unconfigured: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,~msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ msk = pvr2_hdw_subsys_stream_get(hdw);
+ ccnt = scnprintf(buf,acnt,"Subsystems stopped on stream shutdown: ");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = debugifc_print_mask(buf,acnt,msk,msk);
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+ ccnt = scnprintf(buf,acnt,"\n");
+ bcnt += ccnt; acnt -= ccnt; buf += ccnt;
+
+ return bcnt;
+}
+
+
+int pvr2_debugifc_do1cmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ const char *wptr;
+ unsigned int wlen;
+ unsigned int scnt;
+
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return 0;
+ count -= scnt; buf += scnt;
+ if (!wptr) return 0;
+
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,"debugifc cmd: \"%.*s\"",wlen,wptr);
+ if (debugifc_match_keyword(wptr,wlen,"reset")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"cpu")) {
+ pvr2_hdw_cpureset_assert(hdw,!0);
+ pvr2_hdw_cpureset_assert(hdw,0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"bus")) {
+ pvr2_hdw_device_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"soft")) {
+ return pvr2_hdw_cmd_powerup(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"deep")) {
+ return pvr2_hdw_cmd_deep_reset(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"firmware")) {
+ return pvr2_upload_firmware2(hdw);
+ } else if (debugifc_match_keyword(wptr,wlen,"decoder")) {
+ return pvr2_hdw_cmd_decoder_reset(hdw);
+ }
+ return -EINVAL;
+ } else if (debugifc_match_keyword(wptr,wlen,"subsys_flags")) {
+ unsigned long msk = 0;
+ unsigned long val = 0;
+ if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc parse error on subsys mask");
+ return -EINVAL;
+ }
+ pvr2_hdw_subsys_bit_chg(hdw,msk,val);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"stream_flags")) {
+ unsigned long msk = 0;
+ unsigned long val = 0;
+ if (debugifc_parse_subsys_mask(buf,count,&msk,&val) != count) {
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc parse error on stream mask");
+ return -EINVAL;
+ }
+ pvr2_hdw_subsys_stream_bit_chg(hdw,msk,val);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"cpufw")) {
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"fetch")) {
+ pvr2_hdw_cpufw_set_enabled(hdw,!0);
+ return 0;
+ } else if (debugifc_match_keyword(wptr,wlen,"done")) {
+ pvr2_hdw_cpufw_set_enabled(hdw,0);
+ return 0;
+ } else {
+ return -EINVAL;
+ }
+ } else if (debugifc_match_keyword(wptr,wlen,"gpio")) {
+ int dir_fl = 0;
+ int ret;
+ u32 msk,val;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ if (debugifc_match_keyword(wptr,wlen,"dir")) {
+ dir_fl = !0;
+ } else if (!debugifc_match_keyword(wptr,wlen,"out")) {
+ return -EINVAL;
+ }
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (!scnt) return -EINVAL;
+ count -= scnt; buf += scnt;
+ if (!wptr) return -EINVAL;
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&msk);
+ if (ret) return ret;
+ scnt = debugifc_isolate_word(buf,count,&wptr,&wlen);
+ if (wptr) {
+ ret = debugifc_parse_unsigned_number(wptr,wlen,&val);
+ if (ret) return ret;
+ } else {
+ val = msk;
+ msk = 0xffffffff;
+ }
+ if (dir_fl) {
+ ret = pvr2_hdw_gpio_chg_dir(hdw,msk,val);
+ } else {
+ ret = pvr2_hdw_gpio_chg_out(hdw,msk,val);
+ }
+ return ret;
+ }
+ pvr2_trace(PVR2_TRACE_DEBUGIFC,
+ "debugifc failed to recognize cmd: \"%.*s\"",wlen,wptr);
+ return -EINVAL;
+}
+
+
+int pvr2_debugifc_docmd(struct pvr2_hdw *hdw,const char *buf,
+ unsigned int count)
+{
+ unsigned int bcnt = 0;
+ int ret;
+
+ while (count) {
+ for (bcnt = 0; bcnt < count; bcnt++) {
+ if (buf[bcnt] == '\n') break;
+ }
+
+ ret = pvr2_debugifc_do1cmd(hdw,buf,bcnt);
+ if (ret < 0) return ret;
+ if (bcnt < count) bcnt++;
+ buf += bcnt;
+ count -= bcnt;
+ }
+
+ return 0;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-debugifc.h b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
new file mode 100644
index 000000000000..990b02d35d36
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-debugifc.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEBUGIFC_H
+#define __PVRUSB2_DEBUGIFC_H
+
+struct pvr2_hdw;
+
+/* Non-intrusively print some useful debugging info from inside the
+ driver. This should work even if the driver appears to be
+ wedged. */
+int pvr2_debugifc_print_info(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Print general status of driver. This will also trigger a probe of
+ the USB link. Unlike print_info(), this one synchronizes with the
+ driver so the information should be self-consistent (but it will
+ hang if the driver is wedged). */
+int pvr2_debugifc_print_status(struct pvr2_hdw *,
+ char *buf_ptr,unsigned int buf_size);
+
+/* Parse a string command into a driver action. */
+int pvr2_debugifc_docmd(struct pvr2_hdw *,
+ const char *buf_ptr,unsigned int buf_size);
+
+#endif /* __PVRUSB2_DEBUGIFC_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-demod.c b/drivers/media/video/pvrusb2/pvrusb2-demod.c
new file mode 100644
index 000000000000..9686569a11f6
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-demod.c
@@ -0,0 +1,126 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 "pvrusb2.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-demod.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+
+
+struct pvr2_demod_handler {
+ struct pvr2_hdw *hdw;
+ struct pvr2_i2c_client *client;
+ struct pvr2_i2c_handler i2c_handler;
+ int type_update_fl;
+};
+
+
+static void set_config(struct pvr2_demod_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ int cfg = 0;
+
+ switch (hdw->tuner_type) {
+ case TUNER_PHILIPS_FM1216ME_MK3:
+ case TUNER_PHILIPS_FM1236_MK3:
+ cfg = TDA9887_PORT1_ACTIVE|TDA9887_PORT2_ACTIVE;
+ break;
+ default:
+ break;
+ }
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c demod set_config(0x%x)",cfg);
+ pvr2_i2c_client_cmd(ctxt->client,TDA9887_SET_CONFIG,&cfg);
+ ctxt->type_update_fl = 0;
+}
+
+
+static int demod_check(struct pvr2_demod_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ if (hdw->tuner_updated) ctxt->type_update_fl = !0;
+ return ctxt->type_update_fl != 0;
+}
+
+
+static void demod_update(struct pvr2_demod_handler *ctxt)
+{
+ if (ctxt->type_update_fl) set_config(ctxt);
+}
+
+
+static void demod_detach(struct pvr2_demod_handler *ctxt)
+{
+ ctxt->client->handler = 0;
+ kfree(ctxt);
+}
+
+
+static unsigned int demod_describe(struct pvr2_demod_handler *ctxt,char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-demod");
+}
+
+
+const static struct pvr2_i2c_handler_functions tuner_funcs = {
+ .detach = (void (*)(void *))demod_detach,
+ .check = (int (*)(void *))demod_check,
+ .update = (void (*)(void *))demod_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))demod_describe,
+};
+
+
+int pvr2_i2c_demod_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+{
+ struct pvr2_demod_handler *ctxt;
+ if (cp->handler) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->i2c_handler.func_data = ctxt;
+ ctxt->i2c_handler.func_table = &tuner_funcs;
+ ctxt->type_update_fl = !0;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ cp->handler = &ctxt->i2c_handler;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tda9887 V4L2 handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-demod.h b/drivers/media/video/pvrusb2/pvrusb2-demod.h
new file mode 100644
index 000000000000..4c4e40ffbf03
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-demod.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_DEMOD_H
+#define __PVRUSB2_DEMOD_H
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_demod_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+#endif /* __PVRUSB2_DEMOD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.c b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
new file mode 100644
index 000000000000..94d383ff9889
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+
+#define trace_eeprom(...) pvr2_trace(PVR2_TRACE_EEPROM,__VA_ARGS__)
+
+
+
+/*
+
+ Read and analyze data in the eeprom. Use tveeprom to figure out
+ the packet structure, since this is another Hauppauge device and
+ internally it has a family resemblence to ivtv-type devices
+
+*/
+
+#include <media/tveeprom.h>
+
+/* We seem to only be interested in the last 128 bytes of the EEPROM */
+#define EEPROM_SIZE 128
+
+/* Grab EEPROM contents, needed for direct method. */
+static u8 *pvr2_eeprom_fetch(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[2];
+ u8 *eeprom;
+ u8 iadd[2];
+ u8 addr;
+ u16 eepromSize;
+ unsigned int offs;
+ int ret;
+ int mode16 = 0;
+ unsigned pcnt,tcnt;
+ eeprom = kmalloc(EEPROM_SIZE,GFP_KERNEL);
+ if (!eeprom) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to allocate memory"
+ " required to read eeprom");
+ return 0;
+ }
+
+ trace_eeprom("Value for eeprom addr from controller was 0x%x",
+ hdw->eeprom_addr);
+ addr = hdw->eeprom_addr;
+ /* Seems that if the high bit is set, then the *real* eeprom
+ address is shifted right now bit position (noticed this in
+ newer PVR USB2 hardware) */
+ if (addr & 0x80) addr >>= 1;
+
+ /* FX2 documentation states that a 16bit-addressed eeprom is
+ expected if the I2C address is an odd number (yeah, this is
+ strange but it's what they do) */
+ mode16 = (addr & 1);
+ eepromSize = (mode16 ? 4096 : 256);
+ trace_eeprom("Examining %d byte eeprom at location 0x%x"
+ " using %d bit addressing",eepromSize,addr,
+ mode16 ? 16 : 8);
+
+ msg[0].addr = addr;
+ msg[0].flags = 0;
+ msg[0].len = mode16 ? 2 : 1;
+ msg[0].buf = iadd;
+ msg[1].addr = addr;
+ msg[1].flags = I2C_M_RD;
+
+ /* We have to do the actual eeprom data fetch ourselves, because
+ (1) we're only fetching part of the eeprom, and (2) if we were
+ getting the whole thing our I2C driver can't grab it in one
+ pass - which is what tveeprom is otherwise going to attempt */
+ memset(eeprom,0,EEPROM_SIZE);
+ for (tcnt = 0; tcnt < EEPROM_SIZE; tcnt += pcnt) {
+ pcnt = 16;
+ if (pcnt + tcnt > EEPROM_SIZE) pcnt = EEPROM_SIZE-tcnt;
+ offs = tcnt + (eepromSize - EEPROM_SIZE);
+ if (mode16) {
+ iadd[0] = offs >> 8;
+ iadd[1] = offs;
+ } else {
+ iadd[0] = offs;
+ }
+ msg[1].len = pcnt;
+ msg[1].buf = eeprom+tcnt;
+ if ((ret = i2c_transfer(
+ &hdw->i2c_adap,
+ msg,sizeof(msg)/sizeof(msg[0]))) != 2) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "eeprom fetch set offs err=%d",ret);
+ kfree(eeprom);
+ return 0;
+ }
+ }
+ return eeprom;
+}
+
+
+/* Directly call eeprom analysis function within tveeprom. */
+int pvr2_eeprom_analyze(struct pvr2_hdw *hdw)
+{
+ u8 *eeprom;
+ struct tveeprom tvdata;
+
+ memset(&tvdata,0,sizeof(tvdata));
+
+ eeprom = pvr2_eeprom_fetch(hdw);
+ if (!eeprom) return -EINVAL;
+
+ {
+ struct i2c_client fake_client;
+ /* Newer version expects a useless client interface */
+ fake_client.addr = hdw->eeprom_addr;
+ fake_client.adapter = &hdw->i2c_adap;
+ tveeprom_hauppauge_analog(&fake_client,&tvdata,eeprom);
+ }
+
+ trace_eeprom("eeprom assumed v4l tveeprom module");
+ trace_eeprom("eeprom direct call results:");
+ trace_eeprom("has_radio=%d",tvdata.has_radio);
+ trace_eeprom("tuner_type=%d",tvdata.tuner_type);
+ trace_eeprom("tuner_formats=0x%x",tvdata.tuner_formats);
+ trace_eeprom("audio_processor=%d",tvdata.audio_processor);
+ trace_eeprom("model=%d",tvdata.model);
+ trace_eeprom("revision=%d",tvdata.revision);
+ trace_eeprom("serial_number=%d",tvdata.serial_number);
+ trace_eeprom("rev_str=%s",tvdata.rev_str);
+ hdw->tuner_type = tvdata.tuner_type;
+ hdw->serial_number = tvdata.serial_number;
+ hdw->std_mask_eeprom = tvdata.tuner_formats;
+
+ kfree(eeprom);
+
+ return 0;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-eeprom.h b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
new file mode 100644
index 000000000000..84242975dea7
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-eeprom.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_EEPROM_H
+#define __PVRUSB2_EEPROM_H
+
+struct pvr2_hdw;
+
+int pvr2_eeprom_analyze(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_EEPROM_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
new file mode 100644
index 000000000000..2cc31695b435
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -0,0 +1,418 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 <linux/device.h> // for linux/firmware.h
+#include <linux/firmware.h>
+#include "pvrusb2-util.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+
+
+
+/* Firmware mailbox flags - definitions found from ivtv */
+#define IVTV_MBOX_FIRMWARE_DONE 0x00000004
+#define IVTV_MBOX_DRIVER_DONE 0x00000002
+#define IVTV_MBOX_DRIVER_BUSY 0x00000001
+
+
+static int pvr2_encoder_write_words(struct pvr2_hdw *hdw,
+ const u32 *data, unsigned int dlen)
+{
+ unsigned int idx;
+ int ret;
+ unsigned int offs = 0;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x01. Remaining 32 bit words are
+ spread out into chunks of 7 bytes each, little-endian ordered,
+ offset at zero within each 2 blank bytes following and a
+ single byte that is 0x44 plus the offset of the word. Repeat
+ request for additional words, with offset adjusted
+ accordingly.
+
+ */
+ while (dlen) {
+ chunkCnt = 8;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
+ hdw->cmd_buffer[0] = 0x01;
+ for (idx = 0; idx < chunkCnt; idx++) {
+ hdw->cmd_buffer[1+(idx*7)+6] = 0x44 + idx + offs;
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer, 1+(idx*7),
+ data[idx]);
+ }
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1+(chunkCnt*7),
+ 0,0);
+ if (ret) return ret;
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+static int pvr2_encoder_read_words(struct pvr2_hdw *hdw,int statusFl,
+ u32 *data, unsigned int dlen)
+{
+ unsigned int idx;
+ int ret;
+ unsigned int offs = 0;
+ unsigned int chunkCnt;
+
+ /*
+
+ Format: First byte must be 0x02 (status check) or 0x28 (read
+ back block of 32 bit words). Next 6 bytes must be zero,
+ followed by a single byte of 0x44+offset for portion to be
+ read. Returned data is packed set of 32 bits words that were
+ read.
+
+ */
+
+ while (dlen) {
+ chunkCnt = 16;
+ if (chunkCnt > dlen) chunkCnt = dlen;
+ memset(hdw->cmd_buffer,0,sizeof(hdw->cmd_buffer));
+ hdw->cmd_buffer[0] = statusFl ? 0x02 : 0x28;
+ hdw->cmd_buffer[7] = 0x44 + offs;
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,8,
+ hdw->cmd_buffer,chunkCnt * 4);
+ if (ret) return ret;
+
+ for (idx = 0; idx < chunkCnt; idx++) {
+ data[idx] = PVR2_COMPOSE_LE(hdw->cmd_buffer,idx*4);
+ }
+ data += chunkCnt;
+ dlen -= chunkCnt;
+ offs += chunkCnt;
+ }
+
+ return 0;
+}
+
+
+/* This prototype is set up to be compatible with the
+ cx2341x_mbox_func prototype in cx2341x.h, which should be in
+ kernels 2.6.18 or later. We do this so that we can enable
+ cx2341x.ko to write to our encoder (by handing it a pointer to this
+ function). For earlier kernels this doesn't really matter. */
+static int pvr2_encoder_cmd(void *ctxt,
+ int cmd,
+ int arg_cnt_send,
+ int arg_cnt_recv,
+ u32 *argp)
+{
+ unsigned int poll_count;
+ int ret = 0;
+ unsigned int idx;
+ /* These sizes look to be limited by the FX2 firmware implementation */
+ u32 wrData[16];
+ u32 rdData[16];
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)ctxt;
+
+
+ /*
+
+ The encoder seems to speak entirely using blocks 32 bit words.
+ In ivtv driver terms, this is a mailbox which we populate with
+ data and watch what the hardware does with it. The first word
+ is a set of flags used to control the transaction, the second
+ word is the command to execute, the third byte is zero (ivtv
+ driver suggests that this is some kind of return value), and
+ the fourth byte is a specified timeout (windows driver always
+ uses 0x00060000 except for one case when it is zero). All
+ successive words are the argument words for the command.
+
+ First, write out the entire set of words, with the first word
+ being zero.
+
+ Next, write out just the first word again, but set it to
+ IVTV_MBOX_DRIVER_DONE | IVTV_DRIVER_BUSY this time (which
+ probably means "go").
+
+ Next, read back 16 words as status. Check the first word,
+ which should have IVTV_MBOX_FIRMWARE_DONE set. If however
+ that bit is not set, then the command isn't done so repeat the
+ read.
+
+ Next, read back 32 words and compare with the original
+ arugments. Hopefully they will match.
+
+ Finally, write out just the first word again, but set it to
+ 0x0 this time (which probably means "idle").
+
+ */
+
+ if (arg_cnt_send > (sizeof(wrData)/sizeof(wrData[0]))-4) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many input arguments"
+ " (was given %u limit %u)",
+ arg_cnt_send,
+ (unsigned int)(sizeof(wrData)/sizeof(wrData[0])) - 4);
+ return -EINVAL;
+ }
+
+ if (arg_cnt_recv > (sizeof(rdData)/sizeof(rdData[0]))-4) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many return arguments"
+ " (was given %u limit %u)",
+ arg_cnt_recv,
+ (unsigned int)(sizeof(rdData)/sizeof(rdData[0])) - 4);
+ return -EINVAL;
+ }
+
+
+ LOCK_TAKE(hdw->ctl_lock); do {
+
+ wrData[0] = 0;
+ wrData[1] = cmd;
+ wrData[2] = 0;
+ wrData[3] = 0x00060000;
+ for (idx = 0; idx < arg_cnt_send; idx++) {
+ wrData[idx+4] = argp[idx];
+ }
+ for (; idx < (sizeof(wrData)/sizeof(wrData[0]))-4; idx++) {
+ wrData[idx+4] = 0;
+ }
+
+ ret = pvr2_encoder_write_words(hdw,wrData,idx);
+ if (ret) break;
+ wrData[0] = IVTV_MBOX_DRIVER_DONE|IVTV_MBOX_DRIVER_BUSY;
+ ret = pvr2_encoder_write_words(hdw,wrData,1);
+ if (ret) break;
+ poll_count = 0;
+ while (1) {
+ if (poll_count < 10000000) poll_count++;
+ ret = pvr2_encoder_read_words(hdw,!0,rdData,1);
+ if (ret) break;
+ if (rdData[0] & IVTV_MBOX_FIRMWARE_DONE) {
+ break;
+ }
+ if (poll_count == 100) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** device's encoder"
+ " appears to be stuck"
+ " (status=0%08x)",rdData[0]);
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder command: 0x%02x",cmd);
+ for (idx = 4; idx < arg_cnt_send; idx++) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Encoder arg%d: 0x%08x",
+ idx-3,wrData[idx]);
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Giving up waiting."
+ " It is likely that"
+ " this is a bad idea...");
+ ret = -EBUSY;
+ break;
+ }
+ }
+ if (ret) break;
+ wrData[0] = 0x7;
+ ret = pvr2_encoder_read_words(
+ hdw,0,rdData,
+ sizeof(rdData)/sizeof(rdData[0]));
+ if (ret) break;
+ for (idx = 0; idx < arg_cnt_recv; idx++) {
+ argp[idx] = rdData[idx+4];
+ }
+
+ wrData[0] = 0x0;
+ ret = pvr2_encoder_write_words(hdw,wrData,1);
+ if (ret) break;
+
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+static int pvr2_encoder_vcmd(struct pvr2_hdw *hdw, int cmd,
+ int args, ...)
+{
+ va_list vl;
+ unsigned int idx;
+ u32 data[12];
+
+ if (args > sizeof(data)/sizeof(data[0])) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Failed to write cx23416 command"
+ " - too many arguments"
+ " (was given %u limit %u)",
+ args,(unsigned int)(sizeof(data)/sizeof(data[0])));
+ return -EINVAL;
+ }
+
+ va_start(vl, args);
+ for (idx = 0; idx < args; idx++) {
+ data[idx] = va_arg(vl, u32);
+ }
+ va_end(vl);
+
+ return pvr2_encoder_cmd(hdw,cmd,args,0,data);
+}
+
+int pvr2_encoder_configure(struct pvr2_hdw *hdw)
+{
+ int ret;
+ pvr2_trace(PVR2_TRACE_ENCODER,"pvr2_encoder_configure"
+ " (cx2341x module)");
+ hdw->enc_ctl_state.port = CX2341X_PORT_STREAMING;
+ hdw->enc_ctl_state.width = hdw->res_hor_val;
+ hdw->enc_ctl_state.height = hdw->res_ver_val;
+ hdw->enc_ctl_state.is_50hz = ((hdw->std_mask_cur &
+ (V4L2_STD_NTSC|V4L2_STD_PAL_M)) ?
+ 0 : 1);
+
+ ret = 0;
+
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_NUM_VSYNC_LINES, 2,
+ 0xf0, 0xf0);
+
+ /* setup firmware to notify us about some events (don't know why...) */
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_EVENT_NOTIFICATION, 4,
+ 0, 0, 0x10000000, 0xffffffff);
+
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw,CX2341X_ENC_SET_VBI_LINE, 5,
+ 0xffffffff,0,0,0,0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to configure cx32416");
+ return ret;
+ }
+
+ ret = cx2341x_update(hdw,pvr2_encoder_cmd,
+ (hdw->enc_cur_valid ? &hdw->enc_cur_state : 0),
+ &hdw->enc_ctl_state);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error from cx2341x module code=%d",ret);
+ return ret;
+ }
+
+ ret = 0;
+
+ if (!ret) ret = pvr2_encoder_vcmd(
+ hdw, CX2341X_ENC_INITIALIZE_INPUT, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to initialize cx32416 video input");
+ return ret;
+ }
+
+ hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
+ memcpy(&hdw->enc_cur_state,&hdw->enc_ctl_state,
+ sizeof(struct cx2341x_mpeg_params));
+ hdw->enc_cur_valid = !0;
+ return 0;
+}
+
+
+int pvr2_encoder_start(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* unmask some interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xbfffffff);
+
+ /* change some GPIO data */
+ pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000481);
+ pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
+
+ if (hdw->config == pvr2_config_vbi) {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0x01,0x14);
+ } else if (hdw->config == pvr2_config_mpeg) {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0,0x13);
+ } else {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_START_CAPTURE,2,
+ 0,0x13);
+ }
+ if (!status) {
+ hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_RUN);
+ }
+ return status;
+}
+
+int pvr2_encoder_stop(struct pvr2_hdw *hdw)
+{
+ int status;
+
+ /* mask all interrupts */
+ pvr2_write_register(hdw, 0x0048, 0xffffffff);
+
+ if (hdw->config == pvr2_config_vbi) {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0x01,0x14);
+ } else if (hdw->config == pvr2_config_mpeg) {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0,0x13);
+ } else {
+ status = pvr2_encoder_vcmd(hdw,CX2341X_ENC_STOP_CAPTURE,3,
+ 0x01,0,0x13);
+ }
+
+ /* change some GPIO data */
+ /* Note: Bit d7 of dir appears to control the LED. So we shut it
+ off here. */
+ pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000401);
+ pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000000);
+
+ if (!status) {
+ hdw->subsys_enabled_mask &= ~(1<<PVR2_SUBSYS_B_ENC_RUN);
+ }
+ return status;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.h b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
new file mode 100644
index 000000000000..01b5a0b89c03
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.h
@@ -0,0 +1,42 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_ENCODER_H
+#define __PVRUSB2_ENCODER_H
+
+struct pvr2_hdw;
+
+int pvr2_encoder_configure(struct pvr2_hdw *);
+int pvr2_encoder_start(struct pvr2_hdw *);
+int pvr2_encoder_stop(struct pvr2_hdw *);
+
+#endif /* __PVRUSB2_ENCODER_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
new file mode 100644
index 000000000000..ba2afbfe32c5
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -0,0 +1,384 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_HDW_INTERNAL_H
+#define __PVRUSB2_HDW_INTERNAL_H
+
+/*
+
+ This header sets up all the internal structures and definitions needed to
+ track and coordinate the driver's interaction with the hardware. ONLY
+ source files which actually implement part of that whole circus should be
+ including this header. Higher levels, like the external layers to the
+ various public APIs (V4L, sysfs, etc) should NOT ever include this
+ private, internal header. This means that pvrusb2-hdw, pvrusb2-encoder,
+ etc will include this, but pvrusb2-v4l should not.
+
+*/
+
+#include <linux/config.h>
+#include <linux/videodev2.h>
+#include <linux/i2c.h>
+#include <linux/mutex.h>
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-io.h"
+#include <media/cx2341x.h>
+
+/* Legal values for the SRATE state variable */
+#define PVR2_CVAL_SRATE_48 0
+#define PVR2_CVAL_SRATE_44_1 1
+
+/* Legal values for the AUDIOBITRATE state variable */
+#define PVR2_CVAL_AUDIOBITRATE_384 0
+#define PVR2_CVAL_AUDIOBITRATE_320 1
+#define PVR2_CVAL_AUDIOBITRATE_256 2
+#define PVR2_CVAL_AUDIOBITRATE_224 3
+#define PVR2_CVAL_AUDIOBITRATE_192 4
+#define PVR2_CVAL_AUDIOBITRATE_160 5
+#define PVR2_CVAL_AUDIOBITRATE_128 6
+#define PVR2_CVAL_AUDIOBITRATE_112 7
+#define PVR2_CVAL_AUDIOBITRATE_96 8
+#define PVR2_CVAL_AUDIOBITRATE_80 9
+#define PVR2_CVAL_AUDIOBITRATE_64 10
+#define PVR2_CVAL_AUDIOBITRATE_56 11
+#define PVR2_CVAL_AUDIOBITRATE_48 12
+#define PVR2_CVAL_AUDIOBITRATE_32 13
+#define PVR2_CVAL_AUDIOBITRATE_VBR 14
+
+/* Legal values for the AUDIOEMPHASIS state variable */
+#define PVR2_CVAL_AUDIOEMPHASIS_NONE 0
+#define PVR2_CVAL_AUDIOEMPHASIS_50_15 1
+#define PVR2_CVAL_AUDIOEMPHASIS_CCITT 2
+
+/* Legal values for PVR2_CID_HSM */
+#define PVR2_CVAL_HSM_FAIL 0
+#define PVR2_CVAL_HSM_FULL 1
+#define PVR2_CVAL_HSM_HIGH 2
+
+#define PVR2_VID_ENDPOINT 0x84
+#define PVR2_UNK_ENDPOINT 0x86 /* maybe raw yuv ? */
+#define PVR2_VBI_ENDPOINT 0x88
+
+#define PVR2_CTL_BUFFSIZE 64
+
+#define FREQTABLE_SIZE 500
+
+#define LOCK_TAKE(x) do { mutex_lock(&x##_mutex); x##_held = !0; } while (0)
+#define LOCK_GIVE(x) do { x##_held = 0; mutex_unlock(&x##_mutex); } while (0)
+
+struct pvr2_decoder;
+
+typedef int (*pvr2_ctlf_is_dirty)(struct pvr2_ctrl *);
+typedef void (*pvr2_ctlf_clear_dirty)(struct pvr2_ctrl *);
+typedef int (*pvr2_ctlf_get_value)(struct pvr2_ctrl *,int *);
+typedef int (*pvr2_ctlf_set_value)(struct pvr2_ctrl *,int msk,int val);
+typedef int (*pvr2_ctlf_val_to_sym)(struct pvr2_ctrl *,int msk,int val,
+ char *,unsigned int,unsigned int *);
+typedef int (*pvr2_ctlf_sym_to_val)(struct pvr2_ctrl *,
+ const char *,unsigned int,
+ int *mskp,int *valp);
+typedef unsigned int (*pvr2_ctlf_get_v4lflags)(struct pvr2_ctrl *);
+
+/* This structure describes a specific control. A table of these is set up
+ in pvrusb2-hdw.c. */
+struct pvr2_ctl_info {
+ /* Control's name suitable for use as an identifier */
+ const char *name;
+
+ /* Short description of control */
+ const char *desc;
+
+ /* Control's implementation */
+ pvr2_ctlf_get_value get_value; /* Get its value */
+ pvr2_ctlf_set_value set_value; /* Set its value */
+ pvr2_ctlf_val_to_sym val_to_sym; /* Custom convert value->symbol */
+ pvr2_ctlf_sym_to_val sym_to_val; /* Custom convert symbol->value */
+ pvr2_ctlf_is_dirty is_dirty; /* Return true if dirty */
+ pvr2_ctlf_clear_dirty clear_dirty; /* Clear dirty state */
+ pvr2_ctlf_get_v4lflags get_v4lflags;/* Retrieve v4l flags */
+
+ /* Control's type (int, enum, bitmask) */
+ enum pvr2_ctl_type type;
+
+ /* Associated V4L control ID, if any */
+ int v4l_id;
+
+ /* Associated driver internal ID, if any */
+ int internal_id;
+
+ /* Don't implicitly initialize this control's value */
+ int skip_init;
+
+ /* Starting value for this control */
+ int default_value;
+
+ /* Type-specific control information */
+ union {
+ struct { /* Integer control */
+ long min_value; /* lower limit */
+ long max_value; /* upper limit */
+ } type_int;
+ struct { /* enumerated control */
+ unsigned int count; /* enum value count */
+ const char **value_names; /* symbol names */
+ } type_enum;
+ struct { /* bitmask control */
+ unsigned int valid_bits; /* bits in use */
+ const char **bit_names; /* symbol name/bit */
+ } type_bitmask;
+ } def;
+};
+
+
+/* Same as pvr2_ctl_info, but includes storage for the control description */
+#define PVR2_CTLD_INFO_DESC_SIZE 32
+struct pvr2_ctld_info {
+ struct pvr2_ctl_info info;
+ char desc[PVR2_CTLD_INFO_DESC_SIZE];
+};
+
+struct pvr2_ctrl {
+ const struct pvr2_ctl_info *info;
+ struct pvr2_hdw *hdw;
+};
+
+
+struct pvr2_audio_stat {
+ void *ctxt;
+ void (*detach)(void *);
+ int (*status)(void *);
+};
+
+struct pvr2_decoder_ctrl {
+ void *ctxt;
+ void (*detach)(void *);
+ void (*enable)(void *,int);
+ int (*tuned)(void *);
+ void (*force_reset)(void *);
+};
+
+#define PVR2_I2C_PEND_DETECT 0x01 /* Need to detect a client type */
+#define PVR2_I2C_PEND_CLIENT 0x02 /* Client needs a specific update */
+#define PVR2_I2C_PEND_REFRESH 0x04 /* Client has specific pending bits */
+#define PVR2_I2C_PEND_STALE 0x08 /* Broadcast pending bits */
+
+#define PVR2_I2C_PEND_ALL (PVR2_I2C_PEND_DETECT |\
+ PVR2_I2C_PEND_CLIENT |\
+ PVR2_I2C_PEND_REFRESH |\
+ PVR2_I2C_PEND_STALE)
+
+/* Disposition of firmware1 loading situation */
+#define FW1_STATE_UNKNOWN 0
+#define FW1_STATE_MISSING 1
+#define FW1_STATE_FAILED 2
+#define FW1_STATE_RELOAD 3
+#define FW1_STATE_OK 4
+
+/* Known major hardware variants, keyed from device ID */
+#define PVR2_HDW_TYPE_29XXX 0
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+#define PVR2_HDW_TYPE_24XXX 1
+#endif
+
+typedef int (*pvr2_i2c_func)(struct pvr2_hdw *,u8,u8 *,u16,u8 *, u16);
+#define PVR2_I2C_FUNC_CNT 128
+
+/* This structure contains all state data directly needed to
+ manipulate the hardware (as opposed to complying with a kernel
+ interface) */
+struct pvr2_hdw {
+ /* Underlying USB device handle */
+ struct usb_device *usb_dev;
+ struct usb_interface *usb_intf;
+
+ /* Device type, one of PVR2_HDW_TYPE_xxxxx */
+ unsigned int hdw_type;
+
+ /* Video spigot */
+ struct pvr2_stream *vid_stream;
+
+ /* Mutex for all hardware state control */
+ struct mutex big_lock_mutex;
+ int big_lock_held; /* For debugging */
+
+ void (*poll_trigger_func)(void *);
+ void *poll_trigger_data;
+
+ char name[32];
+
+ /* I2C stuff */
+ struct i2c_adapter i2c_adap;
+ struct i2c_algorithm i2c_algo;
+ pvr2_i2c_func i2c_func[PVR2_I2C_FUNC_CNT];
+ int i2c_cx25840_hack_state;
+ int i2c_linked;
+ unsigned int i2c_pend_types; /* Which types of update are needed */
+ unsigned long i2c_pend_mask; /* Change bits we need to scan */
+ unsigned long i2c_stale_mask; /* Pending broadcast change bits */
+ unsigned long i2c_active_mask; /* All change bits currently in use */
+ struct list_head i2c_clients;
+ struct mutex i2c_list_lock;
+
+ /* Frequency table */
+ unsigned int freqTable[FREQTABLE_SIZE];
+ unsigned int freqProgSlot;
+ unsigned int freqSlot;
+
+ /* Stuff for handling low level control interaction with device */
+ struct mutex ctl_lock_mutex;
+ int ctl_lock_held; /* For debugging */
+ struct urb *ctl_write_urb;
+ struct urb *ctl_read_urb;
+ unsigned char *ctl_write_buffer;
+ unsigned char *ctl_read_buffer;
+ volatile int ctl_write_pend_flag;
+ volatile int ctl_read_pend_flag;
+ volatile int ctl_timeout_flag;
+ struct completion ctl_done;
+ unsigned char cmd_buffer[PVR2_CTL_BUFFSIZE];
+ int cmd_debug_state; // Low level command debugging info
+ unsigned char cmd_debug_code; //
+ unsigned int cmd_debug_write_len; //
+ unsigned int cmd_debug_read_len; //
+
+ int flag_ok; // device in known good state
+ int flag_disconnected; // flag_ok == 0 due to disconnect
+ int flag_init_ok; // true if structure is fully initialized
+ int flag_streaming_enabled; // true if streaming should be on
+ int fw1_state; // current situation with fw1
+
+ int flag_decoder_is_tuned;
+
+ struct pvr2_decoder_ctrl *decoder_ctrl;
+
+ // CPU firmware info (used to help find / save firmware data)
+ char *fw_buffer;
+ unsigned int fw_size;
+
+ // Which subsystem pieces have been enabled / configured
+ unsigned long subsys_enabled_mask;
+
+ // Which subsystems are manipulated to enable streaming
+ unsigned long subsys_stream_mask;
+
+ // True if there is a request to trigger logging of state in each
+ // module.
+ int log_requested;
+
+ /* Tuner / frequency control stuff */
+ unsigned int tuner_type;
+ int tuner_updated;
+ unsigned int freqVal;
+ int freqDirty;
+
+ /* Video standard handling */
+ v4l2_std_id std_mask_eeprom; // Hardware supported selections
+ v4l2_std_id std_mask_avail; // Which standards we may select from
+ v4l2_std_id std_mask_cur; // Currently selected standard(s)
+ unsigned int std_enum_cnt; // # of enumerated standards
+ int std_enum_cur; // selected standard enumeration value
+ int std_dirty; // True if std_mask_cur has changed
+ struct pvr2_ctl_info std_info_enum;
+ struct pvr2_ctl_info std_info_avail;
+ struct pvr2_ctl_info std_info_cur;
+ struct v4l2_standard *std_defs;
+ const char **std_enum_names;
+
+ // Generated string names, one per actual V4L2 standard
+ const char *std_mask_ptrs[32];
+ char std_mask_names[32][10];
+
+ int unit_number; /* ID for driver instance */
+ unsigned long serial_number; /* ID for hardware itself */
+
+ /* Minor number used by v4l logic (yes, this is a hack, as there should
+ be no v4l junk here). Probably a better way to do this. */
+ int v4l_minor_number;
+
+ /* Location of eeprom or a negative number if none */
+ int eeprom_addr;
+
+ enum pvr2_config config;
+
+ /* Information about what audio signal we're hearing */
+ int flag_stereo;
+ int flag_bilingual;
+ struct pvr2_audio_stat *audio_stat;
+
+ /* Control state needed for cx2341x module */
+ struct cx2341x_mpeg_params enc_cur_state;
+ struct cx2341x_mpeg_params enc_ctl_state;
+ /* True if an encoder attribute has changed */
+ int enc_stale;
+ /* True if enc_cur_state is valid */
+ int enc_cur_valid;
+
+ /* Control state */
+#define VCREATE_DATA(lab) int lab##_val; int lab##_dirty
+ VCREATE_DATA(brightness);
+ VCREATE_DATA(contrast);
+ VCREATE_DATA(saturation);
+ VCREATE_DATA(hue);
+ VCREATE_DATA(volume);
+ VCREATE_DATA(balance);
+ VCREATE_DATA(bass);
+ VCREATE_DATA(treble);
+ VCREATE_DATA(mute);
+ VCREATE_DATA(input);
+ VCREATE_DATA(audiomode);
+ VCREATE_DATA(res_hor);
+ VCREATE_DATA(res_ver);
+ VCREATE_DATA(srate);
+#undef VCREATE_DATA
+
+ struct pvr2_ctld_info *mpeg_ctrl_info;
+
+ struct pvr2_ctrl *controls;
+ unsigned int control_cnt;
+};
+
+int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw);
+
+unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *);
+
+void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val);
+void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,
+ unsigned long val);
+
+void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw);
+void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw);
+
+int pvr2_i2c_basic_op(struct pvr2_hdw *,u8 i2c_addr,
+ u8 *wdata,u16 wlen,
+ u8 *rdata,u16 rlen);
+
+#endif /* __PVRUSB2_HDW_INTERNAL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
new file mode 100644
index 000000000000..643c471375da
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -0,0 +1,3120 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/firmware.h>
+#include <linux/videodev2.h>
+#include <asm/semaphore.h>
+#include "pvrusb2.h"
+#include "pvrusb2-std.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-i2c-core.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-eeprom.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-encoder.h"
+#include "pvrusb2-debug.h"
+
+struct usb_device_id pvr2_device_table[] = {
+ [PVR2_HDW_TYPE_29XXX] = { USB_DEVICE(0x2040, 0x2900) },
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ [PVR2_HDW_TYPE_24XXX] = { USB_DEVICE(0x2040, 0x2400) },
+#endif
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, pvr2_device_table);
+
+static const char *pvr2_device_names[] = {
+ [PVR2_HDW_TYPE_29XXX] = "WinTV PVR USB2 Model Category 29xxxx",
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ [PVR2_HDW_TYPE_24XXX] = "WinTV PVR USB2 Model Category 24xxxx",
+#endif
+};
+
+struct pvr2_string_table {
+ const char **lst;
+ unsigned int cnt;
+};
+
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+// Names of other client modules to request for 24xxx model hardware
+static const char *pvr2_client_24xxx[] = {
+ "cx25840",
+ "tuner",
+ "tda9887",
+ "wm8775",
+};
+#endif
+
+// Names of other client modules to request for 29xxx model hardware
+static const char *pvr2_client_29xxx[] = {
+ "msp3400",
+ "saa7115",
+ "tuner",
+ "tda9887",
+};
+
+static struct pvr2_string_table pvr2_client_lists[] = {
+ [PVR2_HDW_TYPE_29XXX] = {
+ pvr2_client_29xxx,
+ sizeof(pvr2_client_29xxx)/sizeof(pvr2_client_29xxx[0]),
+ },
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ [PVR2_HDW_TYPE_24XXX] = {
+ pvr2_client_24xxx,
+ sizeof(pvr2_client_24xxx)/sizeof(pvr2_client_24xxx[0]),
+ },
+#endif
+};
+
+static struct pvr2_hdw *unit_pointers[PVR_NUM] = {[ 0 ... PVR_NUM-1 ] = 0};
+DECLARE_MUTEX(pvr2_unit_sem);
+
+static int ctlchg = 0;
+static int initusbreset = 1;
+static int procreload = 0;
+static int tuner[PVR_NUM] = { [0 ... PVR_NUM-1] = -1 };
+static int tolerance[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
+static int video_std[PVR_NUM] = { [0 ... PVR_NUM-1] = 0 };
+static int init_pause_msec = 0;
+
+module_param(ctlchg, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ctlchg, "0=optimize ctl change 1=always accept new ctl value");
+module_param(init_pause_msec, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(init_pause_msec, "hardware initialization settling delay");
+module_param(initusbreset, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(initusbreset, "Do USB reset device on probe");
+module_param(procreload, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(procreload,
+ "Attempt init failure recovery with firmware reload");
+module_param_array(tuner, int, NULL, 0444);
+MODULE_PARM_DESC(tuner,"specify installed tuner type");
+module_param_array(video_std, int, NULL, 0444);
+MODULE_PARM_DESC(video_std,"specify initial video standard");
+module_param_array(tolerance, int, NULL, 0444);
+MODULE_PARM_DESC(tolerance,"specify stream error tolerance");
+
+#define PVR2_CTL_WRITE_ENDPOINT 0x01
+#define PVR2_CTL_READ_ENDPOINT 0x81
+
+#define PVR2_GPIO_IN 0x9008
+#define PVR2_GPIO_OUT 0x900c
+#define PVR2_GPIO_DIR 0x9020
+
+#define trace_firmware(...) pvr2_trace(PVR2_TRACE_FIRMWARE,__VA_ARGS__)
+
+#define PVR2_FIRMWARE_ENDPOINT 0x02
+
+/* size of a firmware chunk */
+#define FIRMWARE_CHUNK_SIZE 0x2000
+
+/* Define the list of additional controls we'll dynamically construct based
+ on query of the cx2341x module. */
+struct pvr2_mpeg_ids {
+ const char *strid;
+ int id;
+};
+static const struct pvr2_mpeg_ids mpeg_ids[] = {
+ {
+ .strid = "audio_layer",
+ .id = V4L2_CID_MPEG_AUDIO_ENCODING,
+ },{
+ .strid = "audio_bitrate",
+ .id = V4L2_CID_MPEG_AUDIO_L2_BITRATE,
+ },{
+ /* Already using audio_mode elsewhere :-( */
+ .strid = "mpeg_audio_mode",
+ .id = V4L2_CID_MPEG_AUDIO_MODE,
+ },{
+ .strid = "mpeg_audio_mode_extension",
+ .id = V4L2_CID_MPEG_AUDIO_MODE_EXTENSION,
+ },{
+ .strid = "audio_emphasis",
+ .id = V4L2_CID_MPEG_AUDIO_EMPHASIS,
+ },{
+ .strid = "audio_crc",
+ .id = V4L2_CID_MPEG_AUDIO_CRC,
+ },{
+ .strid = "video_aspect",
+ .id = V4L2_CID_MPEG_VIDEO_ASPECT,
+ },{
+ .strid = "video_b_frames",
+ .id = V4L2_CID_MPEG_VIDEO_B_FRAMES,
+ },{
+ .strid = "video_gop_size",
+ .id = V4L2_CID_MPEG_VIDEO_GOP_SIZE,
+ },{
+ .strid = "video_gop_closure",
+ .id = V4L2_CID_MPEG_VIDEO_GOP_CLOSURE,
+ },{
+ .strid = "video_pulldown",
+ .id = V4L2_CID_MPEG_VIDEO_PULLDOWN,
+ },{
+ .strid = "video_bitrate_mode",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_MODE,
+ },{
+ .strid = "video_bitrate",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE,
+ },{
+ .strid = "video_bitrate_peak",
+ .id = V4L2_CID_MPEG_VIDEO_BITRATE_PEAK,
+ },{
+ .strid = "video_temporal_decimation",
+ .id = V4L2_CID_MPEG_VIDEO_TEMPORAL_DECIMATION,
+ },{
+ .strid = "stream_type",
+ .id = V4L2_CID_MPEG_STREAM_TYPE,
+ },{
+ .strid = "video_spatial_filter_mode",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER_MODE,
+ },{
+ .strid = "video_spatial_filter",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_SPATIAL_FILTER,
+ },{
+ .strid = "video_luma_spatial_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE,
+ },{
+ .strid = "video_chroma_spatial_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE,
+ },{
+ .strid = "video_temporal_filter_mode",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE,
+ },{
+ .strid = "video_temporal_filter",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER,
+ },{
+ .strid = "video_median_filter_type",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE,
+ },{
+ .strid = "video_luma_median_filter_top",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_TOP,
+ },{
+ .strid = "video_luma_median_filter_bottom",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_LUMA_MEDIAN_FILTER_BOTTOM,
+ },{
+ .strid = "video_chroma_median_filter_top",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_TOP,
+ },{
+ .strid = "video_chroma_median_filter_bottom",
+ .id = V4L2_CID_MPEG_CX2341X_VIDEO_CHROMA_MEDIAN_FILTER_BOTTOM,
+ }
+};
+#define MPEGDEF_COUNT (sizeof(mpeg_ids)/sizeof(mpeg_ids[0]))
+
+static const char *control_values_srate[] = {
+ [PVR2_CVAL_SRATE_48] = "48KHz",
+ [PVR2_CVAL_SRATE_44_1] = "44.1KHz",
+};
+
+
+
+
+static const char *control_values_input[] = {
+ [PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/
+ [PVR2_CVAL_INPUT_RADIO] = "radio",
+ [PVR2_CVAL_INPUT_SVIDEO] = "s-video",
+ [PVR2_CVAL_INPUT_COMPOSITE] = "composite",
+};
+
+
+static const char *control_values_audiomode[] = {
+ [V4L2_TUNER_MODE_MONO] = "Mono",
+ [V4L2_TUNER_MODE_STEREO] = "Stereo",
+ [V4L2_TUNER_MODE_LANG1] = "Lang1",
+ [V4L2_TUNER_MODE_LANG2] = "Lang2",
+ [V4L2_TUNER_MODE_LANG1_LANG2] = "Lang1+Lang2",
+};
+
+
+static const char *control_values_hsm[] = {
+ [PVR2_CVAL_HSM_FAIL] = "Fail",
+ [PVR2_CVAL_HSM_HIGH] = "High",
+ [PVR2_CVAL_HSM_FULL] = "Full",
+};
+
+
+static const char *control_values_subsystem[] = {
+ [PVR2_SUBSYS_B_ENC_FIRMWARE] = "enc_firmware",
+ [PVR2_SUBSYS_B_ENC_CFG] = "enc_config",
+ [PVR2_SUBSYS_B_DIGITIZER_RUN] = "digitizer_run",
+ [PVR2_SUBSYS_B_USBSTREAM_RUN] = "usbstream_run",
+ [PVR2_SUBSYS_B_ENC_RUN] = "enc_run",
+};
+
+
+static int ctrl_channelfreq_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
+ *vp = hdw->freqTable[hdw->freqProgSlot-1];
+ } else {
+ *vp = 0;
+ }
+ return 0;
+}
+
+static int ctrl_channelfreq_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((hdw->freqProgSlot > 0) && (hdw->freqProgSlot <= FREQTABLE_SIZE)) {
+ hdw->freqTable[hdw->freqProgSlot-1] = v;
+ }
+ return 0;
+}
+
+static int ctrl_channelprog_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->freqProgSlot;
+ return 0;
+}
+
+static int ctrl_channelprog_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if ((v >= 0) && (v <= FREQTABLE_SIZE)) {
+ hdw->freqProgSlot = v;
+ }
+ return 0;
+}
+
+static int ctrl_channel_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->freqSlot;
+ return 0;
+}
+
+static int ctrl_channel_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ unsigned freq = 0;
+ struct pvr2_hdw *hdw = cptr->hdw;
+ hdw->freqSlot = v;
+ if ((hdw->freqSlot > 0) && (hdw->freqSlot <= FREQTABLE_SIZE)) {
+ freq = hdw->freqTable[hdw->freqSlot-1];
+ }
+ if (freq && (freq != hdw->freqVal)) {
+ hdw->freqVal = freq;
+ hdw->freqDirty = !0;
+ }
+ return 0;
+}
+
+static int ctrl_freq_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->freqVal;
+ return 0;
+}
+
+static int ctrl_freq_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->freqDirty != 0;
+}
+
+static void ctrl_freq_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->freqDirty = 0;
+}
+
+static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ hdw->freqVal = v;
+ hdw->freqDirty = !0;
+ hdw->freqSlot = 0;
+ return 0;
+}
+
+static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->enc_stale != 0;
+}
+
+static void ctrl_cx2341x_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->enc_stale = 0;
+}
+
+static int ctrl_cx2341x_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ int ret;
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = cptr->info->v4l_id;
+ ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs,
+ VIDIOC_G_EXT_CTRLS);
+ if (ret) return ret;
+ *vp = c1.value;
+ return 0;
+}
+
+static int ctrl_cx2341x_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ int ret;
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = cptr->info->v4l_id;
+ c1.value = v;
+ ret = cx2341x_ext_ctrls(&cptr->hdw->enc_ctl_state,&cs,
+ VIDIOC_S_EXT_CTRLS);
+ if (ret) return ret;
+ cptr->hdw->enc_stale = !0;
+ return 0;
+}
+
+static unsigned int ctrl_cx2341x_getv4lflags(struct pvr2_ctrl *cptr)
+{
+ struct v4l2_queryctrl qctrl;
+ struct pvr2_ctl_info *info;
+ qctrl.id = cptr->info->v4l_id;
+ cx2341x_ctrl_query(&cptr->hdw->enc_ctl_state,&qctrl);
+ /* Strip out the const so we can adjust a function pointer. It's
+ OK to do this here because we know this is a dynamically created
+ control, so the underlying storage for the info pointer is (a)
+ private to us, and (b) not in read-only storage. Either we do
+ this or we significantly complicate the underlying control
+ implementation. */
+ info = (struct pvr2_ctl_info *)(cptr->info);
+ if (qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY) {
+ if (info->set_value) {
+ info->set_value = 0;
+ }
+ } else {
+ if (!(info->set_value)) {
+ info->set_value = ctrl_cx2341x_set;
+ }
+ }
+ return qctrl.flags;
+}
+
+static int ctrl_streamingenabled_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->flag_streaming_enabled;
+ return 0;
+}
+
+static int ctrl_hsm_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ int result = pvr2_hdw_is_hsm(cptr->hdw);
+ *vp = PVR2_CVAL_HSM_FULL;
+ if (result < 0) *vp = PVR2_CVAL_HSM_FAIL;
+ if (result) *vp = PVR2_CVAL_HSM_HIGH;
+ return 0;
+}
+
+static int ctrl_stdavail_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_mask_avail;
+ return 0;
+}
+
+static int ctrl_stdavail_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ v4l2_std_id ns;
+ ns = hdw->std_mask_avail;
+ ns = (ns & ~m) | (v & m);
+ if (ns == hdw->std_mask_avail) return 0;
+ hdw->std_mask_avail = ns;
+ pvr2_hdw_internal_set_std_avail(hdw);
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return 0;
+}
+
+static int ctrl_std_val_to_sym(struct pvr2_ctrl *cptr,int msk,int val,
+ char *bufPtr,unsigned int bufSize,
+ unsigned int *len)
+{
+ *len = pvr2_std_id_to_str(bufPtr,bufSize,msk & val);
+ return 0;
+}
+
+static int ctrl_std_sym_to_val(struct pvr2_ctrl *cptr,
+ const char *bufPtr,unsigned int bufSize,
+ int *mskp,int *valp)
+{
+ int ret;
+ v4l2_std_id id;
+ ret = pvr2_std_str_to_id(&id,bufPtr,bufSize);
+ if (ret < 0) return ret;
+ if (mskp) *mskp = id;
+ if (valp) *valp = id;
+ return 0;
+}
+
+static int ctrl_stdcur_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_mask_cur;
+ return 0;
+}
+
+static int ctrl_stdcur_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ v4l2_std_id ns;
+ ns = hdw->std_mask_cur;
+ ns = (ns & ~m) | (v & m);
+ if (ns == hdw->std_mask_cur) return 0;
+ hdw->std_mask_cur = ns;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return 0;
+}
+
+static int ctrl_stdcur_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->std_dirty != 0;
+}
+
+static void ctrl_stdcur_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->std_dirty = 0;
+}
+
+static int ctrl_signal_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = ((pvr2_hdw_get_signal_status_internal(cptr->hdw) &
+ PVR2_SIGNAL_OK) ? 1 : 0);
+ return 0;
+}
+
+static int ctrl_subsys_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->subsys_enabled_mask;
+ return 0;
+}
+
+static int ctrl_subsys_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ pvr2_hdw_subsys_bit_chg_no_lock(cptr->hdw,m,v);
+ return 0;
+}
+
+static int ctrl_subsys_stream_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->subsys_stream_mask;
+ return 0;
+}
+
+static int ctrl_subsys_stream_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ pvr2_hdw_subsys_stream_bit_chg_no_lock(cptr->hdw,m,v);
+ return 0;
+}
+
+static int ctrl_stdenumcur_set(struct pvr2_ctrl *cptr,int m,int v)
+{
+ struct pvr2_hdw *hdw = cptr->hdw;
+ if (v < 0) return -EINVAL;
+ if (v > hdw->std_enum_cnt) return -EINVAL;
+ hdw->std_enum_cur = v;
+ if (!v) return 0;
+ v--;
+ if (hdw->std_mask_cur == hdw->std_defs[v].id) return 0;
+ hdw->std_mask_cur = hdw->std_defs[v].id;
+ hdw->std_dirty = !0;
+ return 0;
+}
+
+
+static int ctrl_stdenumcur_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ *vp = cptr->hdw->std_enum_cur;
+ return 0;
+}
+
+
+static int ctrl_stdenumcur_is_dirty(struct pvr2_ctrl *cptr)
+{
+ return cptr->hdw->std_dirty != 0;
+}
+
+
+static void ctrl_stdenumcur_clear_dirty(struct pvr2_ctrl *cptr)
+{
+ cptr->hdw->std_dirty = 0;
+}
+
+
+#define DEFINT(vmin,vmax) \
+ .type = pvr2_ctl_int, \
+ .def.type_int.min_value = vmin, \
+ .def.type_int.max_value = vmax
+
+#define DEFENUM(tab) \
+ .type = pvr2_ctl_enum, \
+ .def.type_enum.count = (sizeof(tab)/sizeof((tab)[0])), \
+ .def.type_enum.value_names = tab
+
+#define DEFBOOL \
+ .type = pvr2_ctl_bool
+
+#define DEFMASK(msk,tab) \
+ .type = pvr2_ctl_bitmask, \
+ .def.type_bitmask.valid_bits = msk, \
+ .def.type_bitmask.bit_names = tab
+
+#define DEFREF(vname) \
+ .set_value = ctrl_set_##vname, \
+ .get_value = ctrl_get_##vname, \
+ .is_dirty = ctrl_isdirty_##vname, \
+ .clear_dirty = ctrl_cleardirty_##vname
+
+
+#define VCREATE_FUNCS(vname) \
+static int ctrl_get_##vname(struct pvr2_ctrl *cptr,int *vp) \
+{*vp = cptr->hdw->vname##_val; return 0;} \
+static int ctrl_set_##vname(struct pvr2_ctrl *cptr,int m,int v) \
+{cptr->hdw->vname##_val = v; cptr->hdw->vname##_dirty = !0; return 0;} \
+static int ctrl_isdirty_##vname(struct pvr2_ctrl *cptr) \
+{return cptr->hdw->vname##_dirty != 0;} \
+static void ctrl_cleardirty_##vname(struct pvr2_ctrl *cptr) \
+{cptr->hdw->vname##_dirty = 0;}
+
+VCREATE_FUNCS(brightness)
+VCREATE_FUNCS(contrast)
+VCREATE_FUNCS(saturation)
+VCREATE_FUNCS(hue)
+VCREATE_FUNCS(volume)
+VCREATE_FUNCS(balance)
+VCREATE_FUNCS(bass)
+VCREATE_FUNCS(treble)
+VCREATE_FUNCS(mute)
+VCREATE_FUNCS(input)
+VCREATE_FUNCS(audiomode)
+VCREATE_FUNCS(res_hor)
+VCREATE_FUNCS(res_ver)
+VCREATE_FUNCS(srate)
+
+#define MIN_FREQ 55250000L
+#define MAX_FREQ 850000000L
+
+/* Table definition of all controls which can be manipulated */
+static const struct pvr2_ctl_info control_defs[] = {
+ {
+ .v4l_id = V4L2_CID_BRIGHTNESS,
+ .desc = "Brightness",
+ .name = "brightness",
+ .default_value = 128,
+ DEFREF(brightness),
+ DEFINT(0,255),
+ },{
+ .v4l_id = V4L2_CID_CONTRAST,
+ .desc = "Contrast",
+ .name = "contrast",
+ .default_value = 68,
+ DEFREF(contrast),
+ DEFINT(0,127),
+ },{
+ .v4l_id = V4L2_CID_SATURATION,
+ .desc = "Saturation",
+ .name = "saturation",
+ .default_value = 64,
+ DEFREF(saturation),
+ DEFINT(0,127),
+ },{
+ .v4l_id = V4L2_CID_HUE,
+ .desc = "Hue",
+ .name = "hue",
+ .default_value = 0,
+ DEFREF(hue),
+ DEFINT(-128,127),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_VOLUME,
+ .desc = "Volume",
+ .name = "volume",
+ .default_value = 65535,
+ DEFREF(volume),
+ DEFINT(0,65535),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_BALANCE,
+ .desc = "Balance",
+ .name = "balance",
+ .default_value = 0,
+ DEFREF(balance),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_BASS,
+ .desc = "Bass",
+ .name = "bass",
+ .default_value = 0,
+ DEFREF(bass),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_TREBLE,
+ .desc = "Treble",
+ .name = "treble",
+ .default_value = 0,
+ DEFREF(treble),
+ DEFINT(-32768,32767),
+ },{
+ .v4l_id = V4L2_CID_AUDIO_MUTE,
+ .desc = "Mute",
+ .name = "mute",
+ .default_value = 0,
+ DEFREF(mute),
+ DEFBOOL,
+ },{
+ .desc = "Video Source",
+ .name = "input",
+ .internal_id = PVR2_CID_INPUT,
+ .default_value = PVR2_CVAL_INPUT_TV,
+ DEFREF(input),
+ DEFENUM(control_values_input),
+ },{
+ .desc = "Audio Mode",
+ .name = "audio_mode",
+ .internal_id = PVR2_CID_AUDIOMODE,
+ .default_value = V4L2_TUNER_MODE_STEREO,
+ DEFREF(audiomode),
+ DEFENUM(control_values_audiomode),
+ },{
+ .desc = "Horizontal capture resolution",
+ .name = "resolution_hor",
+ .internal_id = PVR2_CID_HRES,
+ .default_value = 720,
+ DEFREF(res_hor),
+ DEFINT(320,720),
+ },{
+ .desc = "Vertical capture resolution",
+ .name = "resolution_ver",
+ .internal_id = PVR2_CID_VRES,
+ .default_value = 480,
+ DEFREF(res_ver),
+ DEFINT(200,625),
+ },{
+ .v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
+ .desc = "Sample rate",
+ .name = "srate",
+ .default_value = PVR2_CVAL_SRATE_48,
+ DEFREF(srate),
+ DEFENUM(control_values_srate),
+ },{
+ .desc = "Tuner Frequency (Hz)",
+ .name = "frequency",
+ .internal_id = PVR2_CID_FREQUENCY,
+ .default_value = 175250000L,
+ .set_value = ctrl_freq_set,
+ .get_value = ctrl_freq_get,
+ .is_dirty = ctrl_freq_is_dirty,
+ .clear_dirty = ctrl_freq_clear_dirty,
+ DEFINT(MIN_FREQ,MAX_FREQ),
+ },{
+ .desc = "Channel",
+ .name = "channel",
+ .set_value = ctrl_channel_set,
+ .get_value = ctrl_channel_get,
+ DEFINT(0,FREQTABLE_SIZE),
+ },{
+ .desc = "Channel Program Frequency",
+ .name = "freq_table_value",
+ .set_value = ctrl_channelfreq_set,
+ .get_value = ctrl_channelfreq_get,
+ DEFINT(MIN_FREQ,MAX_FREQ),
+ },{
+ .desc = "Channel Program ID",
+ .name = "freq_table_channel",
+ .set_value = ctrl_channelprog_set,
+ .get_value = ctrl_channelprog_get,
+ DEFINT(0,FREQTABLE_SIZE),
+ },{
+ .desc = "Streaming Enabled",
+ .name = "streaming_enabled",
+ .get_value = ctrl_streamingenabled_get,
+ DEFBOOL,
+ },{
+ .desc = "USB Speed",
+ .name = "usb_speed",
+ .get_value = ctrl_hsm_get,
+ DEFENUM(control_values_hsm),
+ },{
+ .desc = "Signal Present",
+ .name = "signal_present",
+ .get_value = ctrl_signal_get,
+ DEFBOOL,
+ },{
+ .desc = "Video Standards Available Mask",
+ .name = "video_standard_mask_available",
+ .internal_id = PVR2_CID_STDAVAIL,
+ .skip_init = !0,
+ .get_value = ctrl_stdavail_get,
+ .set_value = ctrl_stdavail_set,
+ .val_to_sym = ctrl_std_val_to_sym,
+ .sym_to_val = ctrl_std_sym_to_val,
+ .type = pvr2_ctl_bitmask,
+ },{
+ .desc = "Video Standards In Use Mask",
+ .name = "video_standard_mask_active",
+ .internal_id = PVR2_CID_STDCUR,
+ .skip_init = !0,
+ .get_value = ctrl_stdcur_get,
+ .set_value = ctrl_stdcur_set,
+ .is_dirty = ctrl_stdcur_is_dirty,
+ .clear_dirty = ctrl_stdcur_clear_dirty,
+ .val_to_sym = ctrl_std_val_to_sym,
+ .sym_to_val = ctrl_std_sym_to_val,
+ .type = pvr2_ctl_bitmask,
+ },{
+ .desc = "Subsystem enabled mask",
+ .name = "debug_subsys_mask",
+ .skip_init = !0,
+ .get_value = ctrl_subsys_get,
+ .set_value = ctrl_subsys_set,
+ DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
+ },{
+ .desc = "Subsystem stream mask",
+ .name = "debug_subsys_stream_mask",
+ .skip_init = !0,
+ .get_value = ctrl_subsys_stream_get,
+ .set_value = ctrl_subsys_stream_set,
+ DEFMASK(PVR2_SUBSYS_ALL,control_values_subsystem),
+ },{
+ .desc = "Video Standard Name",
+ .name = "video_standard",
+ .internal_id = PVR2_CID_STDENUM,
+ .skip_init = !0,
+ .get_value = ctrl_stdenumcur_get,
+ .set_value = ctrl_stdenumcur_set,
+ .is_dirty = ctrl_stdenumcur_is_dirty,
+ .clear_dirty = ctrl_stdenumcur_clear_dirty,
+ .type = pvr2_ctl_enum,
+ }
+};
+
+#define CTRLDEF_COUNT (sizeof(control_defs)/sizeof(control_defs[0]))
+
+
+const char *pvr2_config_get_name(enum pvr2_config cfg)
+{
+ switch (cfg) {
+ case pvr2_config_empty: return "empty";
+ case pvr2_config_mpeg: return "mpeg";
+ case pvr2_config_vbi: return "vbi";
+ case pvr2_config_radio: return "radio";
+ }
+ return "<unknown>";
+}
+
+
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *hdw)
+{
+ return hdw->usb_dev;
+}
+
+
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *hdw)
+{
+ return hdw->serial_number;
+}
+
+
+struct pvr2_hdw *pvr2_hdw_find(int unit_number)
+{
+ if (unit_number < 0) return 0;
+ if (unit_number >= PVR_NUM) return 0;
+ return unit_pointers[unit_number];
+}
+
+
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *hdw)
+{
+ return hdw->unit_number;
+}
+
+
+/* Attempt to locate one of the given set of files. Messages are logged
+ appropriate to what has been found. The return value will be 0 or
+ greater on success (it will be the index of the file name found) and
+ fw_entry will be filled in. Otherwise a negative error is returned on
+ failure. If the return value is -ENOENT then no viable firmware file
+ could be located. */
+static int pvr2_locate_firmware(struct pvr2_hdw *hdw,
+ const struct firmware **fw_entry,
+ const char *fwtypename,
+ unsigned int fwcount,
+ const char *fwnames[])
+{
+ unsigned int idx;
+ int ret = -EINVAL;
+ for (idx = 0; idx < fwcount; idx++) {
+ ret = request_firmware(fw_entry,
+ fwnames[idx],
+ &hdw->usb_dev->dev);
+ if (!ret) {
+ trace_firmware("Located %s firmware: %s;"
+ " uploading...",
+ fwtypename,
+ fwnames[idx]);
+ return idx;
+ }
+ if (ret == -ENOENT) continue;
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware fatal error with code=%d",ret);
+ return ret;
+ }
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "***WARNING***"
+ " Device %s firmware"
+ " seems to be missing.",
+ fwtypename);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Did you install the pvrusb2 firmware files"
+ " in their proper location?");
+ if (fwcount == 1) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware unable to locate %s file %s",
+ fwtypename,fwnames[0]);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware unable to locate"
+ " one of the following %s files:",
+ fwtypename);
+ for (idx = 0; idx < fwcount; idx++) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "request_firmware: Failed to find %s",
+ fwnames[idx]);
+ }
+ }
+ return ret;
+}
+
+
+/*
+ * pvr2_upload_firmware1().
+ *
+ * Send the 8051 firmware to the device. After the upload, arrange for
+ * device to re-enumerate.
+ *
+ * NOTE : the pointer to the firmware data given by request_firmware()
+ * is not suitable for an usb transaction.
+ *
+ */
+int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = 0;
+ void *fw_ptr;
+ unsigned int pipe;
+ int ret;
+ u16 address;
+ static const char *fw_files_29xxx[] = {
+ "v4l-pvrusb2-29xxx-01.fw",
+ };
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ static const char *fw_files_24xxx[] = {
+ "v4l-pvrusb2-24xxx-01.fw",
+ };
+#endif
+ static const struct pvr2_string_table fw_file_defs[] = {
+ [PVR2_HDW_TYPE_29XXX] = {
+ fw_files_29xxx,
+ sizeof(fw_files_29xxx)/sizeof(fw_files_29xxx[0]),
+ },
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ [PVR2_HDW_TYPE_24XXX] = {
+ fw_files_24xxx,
+ sizeof(fw_files_24xxx)/sizeof(fw_files_24xxx[0]),
+ },
+#endif
+ };
+ hdw->fw1_state = FW1_STATE_FAILED; // default result
+
+ trace_firmware("pvr2_upload_firmware1");
+
+ ret = pvr2_locate_firmware(hdw,&fw_entry,"fx2 controller",
+ fw_file_defs[hdw->hdw_type].cnt,
+ fw_file_defs[hdw->hdw_type].lst);
+ if (ret < 0) {
+ if (ret == -ENOENT) hdw->fw1_state = FW1_STATE_MISSING;
+ return ret;
+ }
+
+ usb_settoggle(hdw->usb_dev, 0 & 0xf, !(0 & USB_DIR_IN), 0);
+ usb_clear_halt(hdw->usb_dev, usb_sndbulkpipe(hdw->usb_dev, 0 & 0x7f));
+
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+
+ if (fw_entry->size != 0x2000){
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,"wrong fx2 firmware size");
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ fw_ptr = kmalloc(0x800, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ return -ENOMEM;
+ }
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* upload the firmware to address 0000-1fff in 2048 (=0x800) bytes
+ chunk. */
+
+ ret = 0;
+ for(address = 0; address < fw_entry->size; address += 0x800) {
+ memcpy(fw_ptr, fw_entry->data + address, 0x800);
+ ret += usb_control_msg(hdw->usb_dev, pipe, 0xa0, 0x40, address,
+ 0, fw_ptr, 0x800, HZ);
+ }
+
+ trace_firmware("Upload done, releasing device's CPU");
+
+ /* Now release the CPU. It will disconnect and reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ trace_firmware("Upload done (%d bytes sent)",ret);
+
+ /* We should have written 8192 bytes */
+ if (ret == 8192) {
+ hdw->fw1_state = FW1_STATE_RELOAD;
+ return 0;
+ }
+
+ return -EIO;
+}
+
+
+/*
+ * pvr2_upload_firmware2()
+ *
+ * This uploads encoder firmware on endpoint 2.
+ *
+ */
+
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw)
+{
+ const struct firmware *fw_entry = 0;
+ void *fw_ptr;
+ unsigned int pipe, fw_len, fw_done;
+ int actual_length;
+ int ret = 0;
+ int fwidx;
+ static const char *fw_files[] = {
+ CX2341X_FIRM_ENC_FILENAME,
+ };
+
+ trace_firmware("pvr2_upload_firmware2");
+
+ ret = pvr2_locate_firmware(hdw,&fw_entry,"encoder",
+ sizeof(fw_files)/sizeof(fw_files[0]),
+ fw_files);
+ if (ret < 0) return ret;
+ fwidx = ret;
+ ret = 0;
+ /* Since we're about to completely reinitialize the encoder,
+ invalidate our cached copy of its configuration state. Next
+ time we configure the encoder, then we'll fully configure it. */
+ hdw->enc_cur_valid = 0;
+
+ /* First prepare firmware loading */
+ ret |= pvr2_write_register(hdw, 0x0048, 0xffffffff); /*interrupt mask*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000088); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_hdw_cmd_deep_reset(hdw);
+ ret |= pvr2_write_register(hdw, 0xa064, 0x00000000); /*APU command*/
+ ret |= pvr2_hdw_gpio_chg_dir(hdw,0xffffffff,0x00000408); /*gpio dir*/
+ ret |= pvr2_hdw_gpio_chg_out(hdw,0xffffffff,0x00000008); /*gpio output state*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffed); /*VPU ctrl*/
+ ret |= pvr2_write_register(hdw, 0x9054, 0xfffffffd); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x07f8, 0x80000800); /*encoder SDRAM refresh*/
+ ret |= pvr2_write_register(hdw, 0x07fc, 0x0000001a); /*encoder SDRAM pre-charge*/
+ ret |= pvr2_write_register(hdw, 0x0700, 0x00000000); /*I2C clock*/
+ ret |= pvr2_write_register(hdw, 0xaa00, 0x00000000); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa04, 0x00057810); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa10, 0x00148500); /*unknown*/
+ ret |= pvr2_write_register(hdw, 0xaa18, 0x00840000); /*unknown*/
+ ret |= pvr2_write_u8(hdw, 0x52, 0);
+ ret |= pvr2_write_u16(hdw, 0x0600, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload prep failed, ret=%d",ret);
+ release_firmware(fw_entry);
+ return ret;
+ }
+
+ /* Now send firmware */
+
+ fw_len = fw_entry->size;
+
+ if (fw_len % FIRMWARE_CHUNK_SIZE) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "size of %s firmware"
+ " must be a multiple of 8192B",
+ fw_files[fwidx]);
+ release_firmware(fw_entry);
+ return -1;
+ }
+
+ fw_ptr = kmalloc(FIRMWARE_CHUNK_SIZE, GFP_KERNEL);
+ if (fw_ptr == NULL){
+ release_firmware(fw_entry);
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "failed to allocate memory for firmware2 upload");
+ return -ENOMEM;
+ }
+
+ pipe = usb_sndbulkpipe(hdw->usb_dev, PVR2_FIRMWARE_ENDPOINT);
+
+ for (fw_done = 0 ; (fw_done < fw_len) && !ret ;
+ fw_done += FIRMWARE_CHUNK_SIZE ) {
+ int i;
+ memcpy(fw_ptr, fw_entry->data + fw_done, FIRMWARE_CHUNK_SIZE);
+ /* Usbsnoop log shows that we must swap bytes... */
+ for (i = 0; i < FIRMWARE_CHUNK_SIZE/4 ; i++)
+ ((u32 *)fw_ptr)[i] = ___swab32(((u32 *)fw_ptr)[i]);
+
+ ret |= usb_bulk_msg(hdw->usb_dev, pipe, fw_ptr,
+ FIRMWARE_CHUNK_SIZE,
+ &actual_length, HZ);
+ ret |= (actual_length != FIRMWARE_CHUNK_SIZE);
+ }
+
+ trace_firmware("upload of %s : %i / %i ",
+ fw_files[fwidx],fw_done,fw_len);
+
+ kfree(fw_ptr);
+ release_firmware(fw_entry);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload transfer failure");
+ return ret;
+ }
+
+ /* Finish upload */
+
+ ret |= pvr2_write_register(hdw, 0x9054, 0xffffffff); /*reset hw blocks*/
+ ret |= pvr2_write_register(hdw, 0x9058, 0xffffffe8); /*VPU ctrl*/
+ ret |= pvr2_write_u16(hdw, 0x0600, 0);
+
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "firmware2 upload post-proc failure");
+ } else {
+ hdw->subsys_enabled_mask |= (1<<PVR2_SUBSYS_B_ENC_FIRMWARE);
+ }
+ return ret;
+}
+
+
+#define FIRMWARE_RECOVERY_BITS \
+ ((1<<PVR2_SUBSYS_B_ENC_CFG) | \
+ (1<<PVR2_SUBSYS_B_ENC_RUN) | \
+ (1<<PVR2_SUBSYS_B_ENC_FIRMWARE) | \
+ (1<<PVR2_SUBSYS_B_USBSTREAM_RUN))
+
+/*
+
+ This single function is key to pretty much everything. The pvrusb2
+ device can logically be viewed as a series of subsystems which can be
+ stopped / started or unconfigured / configured. To get things streaming,
+ one must configure everything and start everything, but there may be
+ various reasons over time to deconfigure something or stop something.
+ This function handles all of this activity. Everything EVERYWHERE that
+ must affect a subsystem eventually comes here to do the work.
+
+ The current state of all subsystems is represented by a single bit mask,
+ known as subsys_enabled_mask. The bit positions are defined by the
+ PVR2_SUBSYS_xxxx macros, with one subsystem per bit position. At any
+ time the set of configured or active subsystems can be queried just by
+ looking at that mask. To change bits in that mask, this function here
+ must be called. The "msk" argument indicates which bit positions to
+ change, and the "val" argument defines the new values for the positions
+ defined by "msk".
+
+ There is a priority ordering of starting / stopping things, and for
+ multiple requested changes, this function implements that ordering.
+ (Thus we will act on a request to load encoder firmware before we
+ configure the encoder.) In addition to priority ordering, there is a
+ recovery strategy implemented here. If a particular step fails and we
+ detect that failure, this function will clear the affected subsystem bits
+ and restart. Thus we have a means for recovering from a dead encoder:
+ Clear all bits that correspond to subsystems that we need to restart /
+ reconfigure and start over.
+
+*/
+void pvr2_hdw_subsys_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val)
+{
+ unsigned long nmsk;
+ unsigned long vmsk;
+ int ret;
+ unsigned int tryCount = 0;
+
+ if (!hdw->flag_ok) return;
+
+ msk &= PVR2_SUBSYS_ALL;
+ nmsk = (hdw->subsys_enabled_mask & ~msk) | (val & msk);
+ nmsk &= PVR2_SUBSYS_ALL;
+
+ for (;;) {
+ tryCount++;
+ if (!((nmsk ^ hdw->subsys_enabled_mask) &
+ PVR2_SUBSYS_ALL)) break;
+ if (tryCount > 4) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Too many retries when configuring device;"
+ " giving up");
+ pvr2_hdw_render_useless(hdw);
+ break;
+ }
+ if (tryCount > 1) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Retrying device reconfiguration");
+ }
+ pvr2_trace(PVR2_TRACE_INIT,
+ "subsys mask changing 0x%lx:0x%lx"
+ " from 0x%lx to 0x%lx",
+ msk,val,hdw->subsys_enabled_mask,nmsk);
+
+ vmsk = (nmsk ^ hdw->subsys_enabled_mask) &
+ hdw->subsys_enabled_mask;
+ if (vmsk) {
+ if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_stop");
+ ret = pvr2_encoder_stop(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_hdw_cmd_usbstream(0)");
+ pvr2_hdw_cmd_usbstream(hdw,0);
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " decoder disable");
+ if (hdw->decoder_ctrl) {
+ hdw->decoder_ctrl->enable(
+ hdw->decoder_ctrl->ctxt,0);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING:"
+ " No decoder present");
+ }
+ hdw->subsys_enabled_mask &=
+ ~(1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
+ }
+ if (vmsk & PVR2_SUBSYS_CFG_ALL) {
+ hdw->subsys_enabled_mask &=
+ ~(vmsk & PVR2_SUBSYS_CFG_ALL);
+ }
+ }
+ vmsk = (nmsk ^ hdw->subsys_enabled_mask) & nmsk;
+ if (vmsk) {
+ if (vmsk & (1<<PVR2_SUBSYS_B_ENC_FIRMWARE)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_upload_firmware2");
+ ret = pvr2_upload_firmware2(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failure uploading encoder"
+ " firmware");
+ pvr2_hdw_render_useless(hdw);
+ break;
+ }
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_ENC_CFG)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_configure");
+ ret = pvr2_encoder_configure(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_DIGITIZER_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " decoder enable");
+ if (hdw->decoder_ctrl) {
+ hdw->decoder_ctrl->enable(
+ hdw->decoder_ctrl->ctxt,!0);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING:"
+ " No decoder present");
+ }
+ hdw->subsys_enabled_mask |=
+ (1<<PVR2_SUBSYS_B_DIGITIZER_RUN);
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_hdw_cmd_usbstream(1)");
+ pvr2_hdw_cmd_usbstream(hdw,!0);
+ }
+ if (vmsk & (1<<PVR2_SUBSYS_B_ENC_RUN)) {
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*---TRACE_CTL----*/"
+ " pvr2_encoder_start");
+ ret = pvr2_encoder_start(hdw);
+ if (ret) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Error recovery initiated");
+ hdw->subsys_enabled_mask &=
+ ~FIRMWARE_RECOVERY_BITS;
+ continue;
+ }
+ }
+ }
+ }
+}
+
+
+void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,msk,val);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk)
+{
+ pvr2_hdw_subsys_bit_chg(hdw,msk,msk);
+}
+
+
+void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk)
+{
+ pvr2_hdw_subsys_bit_chg(hdw,msk,0);
+}
+
+
+unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *hdw)
+{
+ return hdw->subsys_enabled_mask;
+}
+
+
+unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *hdw)
+{
+ return hdw->subsys_stream_mask;
+}
+
+
+void pvr2_hdw_subsys_stream_bit_chg_no_lock(struct pvr2_hdw *hdw,
+ unsigned long msk,
+ unsigned long val)
+{
+ unsigned long val2;
+ msk &= PVR2_SUBSYS_ALL;
+ val2 = ((hdw->subsys_stream_mask & ~msk) | (val & msk));
+ pvr2_trace(PVR2_TRACE_INIT,
+ "stream mask changing 0x%lx:0x%lx from 0x%lx to 0x%lx",
+ msk,val,hdw->subsys_stream_mask,val2);
+ hdw->subsys_stream_mask = val2;
+}
+
+
+void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,
+ unsigned long val)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_subsys_stream_bit_chg_no_lock(hdw,msk,val);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+int pvr2_hdw_set_streaming_no_lock(struct pvr2_hdw *hdw,int enableFl)
+{
+ if ((!enableFl) == !(hdw->flag_streaming_enabled)) return 0;
+ if (enableFl) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ enable");
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,~0);
+ } else {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*--TRACE_STREAM--*/ disable");
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
+ }
+ if (!hdw->flag_ok) return -EIO;
+ hdw->flag_streaming_enabled = enableFl != 0;
+ return 0;
+}
+
+
+int pvr2_hdw_get_streaming(struct pvr2_hdw *hdw)
+{
+ return hdw->flag_streaming_enabled != 0;
+}
+
+
+int pvr2_hdw_set_streaming(struct pvr2_hdw *hdw,int enable_flag)
+{
+ int ret;
+ LOCK_TAKE(hdw->big_lock); do {
+ ret = pvr2_hdw_set_streaming_no_lock(hdw,enable_flag);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+int pvr2_hdw_set_stream_type_no_lock(struct pvr2_hdw *hdw,
+ enum pvr2_config config)
+{
+ unsigned long sm = hdw->subsys_enabled_mask;
+ if (!hdw->flag_ok) return -EIO;
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,hdw->subsys_stream_mask,0);
+ hdw->config = config;
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,sm);
+ return 0;
+}
+
+
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *hdw,enum pvr2_config config)
+{
+ int ret;
+ if (!hdw->flag_ok) return -EIO;
+ LOCK_TAKE(hdw->big_lock);
+ ret = pvr2_hdw_set_stream_type_no_lock(hdw,config);
+ LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+static int get_default_tuner_type(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = -1;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = tuner[unit_number];
+ }
+ if (tp < 0) return -EINVAL;
+ hdw->tuner_type = tp;
+ return 0;
+}
+
+
+static v4l2_std_id get_default_standard(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = 0;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = video_std[unit_number];
+ }
+ return tp;
+}
+
+
+static unsigned int get_default_error_tolerance(struct pvr2_hdw *hdw)
+{
+ int unit_number = hdw->unit_number;
+ int tp = 0;
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ tp = tolerance[unit_number];
+ }
+ return tp;
+}
+
+
+static int pvr2_hdw_check_firmware(struct pvr2_hdw *hdw)
+{
+ /* Try a harmless request to fetch the eeprom's address over
+ endpoint 1. See what happens. Only the full FX2 image can
+ respond to this. If this probe fails then likely the FX2
+ firmware needs be loaded. */
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = 0xeb;
+ result = pvr2_send_request_ex(hdw,HZ*1,!0,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ if (result) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Probe of device endpoint 1 result status %d",
+ result);
+ } else {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Probe of device endpoint 1 succeeded");
+ }
+ return result == 0;
+}
+
+static void pvr2_hdw_setup_std(struct pvr2_hdw *hdw)
+{
+ char buf[40];
+ unsigned int bcnt;
+ v4l2_std_id std1,std2;
+
+ std1 = get_default_standard(hdw);
+
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),hdw->std_mask_eeprom);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Supported video standard(s) reported by eeprom: %.*s",
+ bcnt,buf);
+
+ hdw->std_mask_avail = hdw->std_mask_eeprom;
+
+ std2 = std1 & ~hdw->std_mask_avail;
+ if (std2) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std2);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Expanding supported video standards"
+ " to include: %.*s",
+ bcnt,buf);
+ hdw->std_mask_avail |= std2;
+ }
+
+ pvr2_hdw_internal_set_std_avail(hdw);
+
+ if (std1) {
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),std1);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Initial video standard forced to %.*s",
+ bcnt,buf);
+ hdw->std_mask_cur = std1;
+ hdw->std_dirty = !0;
+ pvr2_hdw_internal_find_stdenum(hdw);
+ return;
+ }
+
+ if (hdw->std_enum_cnt > 1) {
+ // Autoselect the first listed standard
+ hdw->std_enum_cur = 1;
+ hdw->std_mask_cur = hdw->std_defs[hdw->std_enum_cur-1].id;
+ hdw->std_dirty = !0;
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Initial video standard auto-selected to %s",
+ hdw->std_defs[hdw->std_enum_cur-1].name);
+ return;
+ }
+
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to select a viable initial video standard");
+}
+
+
+static void pvr2_hdw_setup_low(struct pvr2_hdw *hdw)
+{
+ int ret;
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int reloadFl = 0;
+ if (!reloadFl) {
+ reloadFl = (hdw->usb_intf->cur_altsetting->desc.bNumEndpoints
+ == 0);
+ if (reloadFl) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "USB endpoint config looks strange"
+ "; possibly firmware needs to be loaded");
+ }
+ }
+ if (!reloadFl) {
+ reloadFl = !pvr2_hdw_check_firmware(hdw);
+ if (reloadFl) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Check for FX2 firmware failed"
+ "; possibly firmware needs to be loaded");
+ }
+ }
+ if (reloadFl) {
+ if (pvr2_upload_firmware1(hdw) != 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failure uploading firmware1");
+ }
+ return;
+ }
+ hdw->fw1_state = FW1_STATE_OK;
+
+ if (initusbreset) {
+ pvr2_hdw_device_reset(hdw);
+ }
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ for (idx = 0; idx < pvr2_client_lists[hdw->hdw_type].cnt; idx++) {
+ request_module(pvr2_client_lists[hdw->hdw_type].lst[idx]);
+ }
+
+ pvr2_hdw_cmd_powerup(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ if (pvr2_upload_firmware2(hdw)){
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,"device unstable!!");
+ pvr2_hdw_render_useless(hdw);
+ return;
+ }
+
+ // This step MUST happen after the earlier powerup step.
+ pvr2_i2c_core_init(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx;
+ if (cptr->info->skip_init) continue;
+ if (!cptr->info->set_value) continue;
+ cptr->info->set_value(cptr,~0,cptr->info->default_value);
+ }
+
+ // Do not use pvr2_reset_ctl_endpoints() here. It is not
+ // thread-safe against the normal pvr2_send_request() mechanism.
+ // (We should make it thread safe).
+
+ ret = pvr2_hdw_get_eeprom_addr(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Unable to determine location of eeprom, skipping");
+ } else {
+ hdw->eeprom_addr = ret;
+ pvr2_eeprom_analyze(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ }
+
+ pvr2_hdw_setup_std(hdw);
+
+ if (!get_default_tuner_type(hdw)) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: Tuner type overridden to %d",
+ hdw->tuner_type);
+ }
+
+ hdw->tuner_updated = !0;
+ pvr2_i2c_core_check_stale(hdw);
+ hdw->tuner_updated = 0;
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ pvr2_hdw_commit_ctl_internal(hdw);
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ hdw->vid_stream = pvr2_stream_create();
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: video stream is %p",hdw->vid_stream);
+ if (hdw->vid_stream) {
+ idx = get_default_error_tolerance(hdw);
+ if (idx) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup: video stream %p"
+ " setting tolerance %u",
+ hdw->vid_stream,idx);
+ }
+ pvr2_stream_setup(hdw->vid_stream,hdw->usb_dev,
+ PVR2_VID_ENDPOINT,idx);
+ }
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ /* Make sure everything is up to date */
+ pvr2_i2c_core_sync(hdw);
+
+ if (!pvr2_hdw_dev_ok(hdw)) return;
+
+ hdw->flag_init_ok = !0;
+}
+
+
+int pvr2_hdw_setup(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) begin",hdw);
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_setup_low(hdw);
+ pvr2_trace(PVR2_TRACE_INIT,
+ "pvr2_hdw_setup(hdw=%p) done, ok=%d init_ok=%d",
+ hdw,hdw->flag_ok,hdw->flag_init_ok);
+ if (pvr2_hdw_dev_ok(hdw)) {
+ if (pvr2_hdw_init_ok(hdw)) {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device initialization"
+ " completed successfully.");
+ break;
+ }
+ if (hdw->fw1_state == FW1_STATE_RELOAD) {
+ pvr2_trace(
+ PVR2_TRACE_INFO,
+ "Device microcontroller firmware"
+ " (re)loaded; it should now reset"
+ " and reconnect.");
+ break;
+ }
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Device initialization was not successful.");
+ if (hdw->fw1_state == FW1_STATE_MISSING) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Giving up since device"
+ " microcontroller firmware"
+ " appears to be missing.");
+ break;
+ }
+ }
+ if (procreload) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempting pvrusb2 recovery by reloading"
+ " primary firmware.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "If this works, device should disconnect"
+ " and reconnect in a sane state.");
+ hdw->fw1_state = FW1_STATE_UNKNOWN;
+ pvr2_upload_firmware1(hdw);
+ } else {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "***WARNING*** pvrusb2 device hardware"
+ " appears to be jammed"
+ " and I can't clear it.");
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "You might need to power cycle"
+ " the pvrusb2 device"
+ " in order to recover.");
+ }
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_setup(hdw=%p) end",hdw);
+ return hdw->flag_init_ok;
+}
+
+
+/* Create and return a structure for interacting with the underlying
+ hardware */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ unsigned int idx,cnt1,cnt2;
+ struct pvr2_hdw *hdw;
+ unsigned int hdw_type;
+ int valid_std_mask;
+ struct pvr2_ctrl *cptr;
+ __u8 ifnum;
+ struct v4l2_queryctrl qctrl;
+ struct pvr2_ctl_info *ciptr;
+
+ hdw_type = devid - pvr2_device_table;
+ if (hdw_type >=
+ sizeof(pvr2_device_names)/sizeof(pvr2_device_names[0])) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Bogus device type of %u reported",hdw_type);
+ return 0;
+ }
+
+ hdw = kmalloc(sizeof(*hdw),GFP_KERNEL);
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_create: hdw=%p, type \"%s\"",
+ hdw,pvr2_device_names[hdw_type]);
+ if (!hdw) goto fail;
+ memset(hdw,0,sizeof(*hdw));
+ cx2341x_fill_defaults(&hdw->enc_ctl_state);
+
+ hdw->control_cnt = CTRLDEF_COUNT;
+ hdw->control_cnt += MPEGDEF_COUNT;
+ hdw->controls = kmalloc(sizeof(struct pvr2_ctrl) * hdw->control_cnt,
+ GFP_KERNEL);
+ if (!hdw->controls) goto fail;
+ memset(hdw->controls,0,sizeof(struct pvr2_ctrl) * hdw->control_cnt);
+ hdw->hdw_type = hdw_type;
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ cptr->hdw = hdw;
+ }
+ for (idx = 0; idx < 32; idx++) {
+ hdw->std_mask_ptrs[idx] = hdw->std_mask_names[idx];
+ }
+ for (idx = 0; idx < CTRLDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx;
+ cptr->info = control_defs+idx;
+ }
+ /* Define and configure additional controls from cx2341x module. */
+ hdw->mpeg_ctrl_info = kmalloc(
+ sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT, GFP_KERNEL);
+ if (!hdw->mpeg_ctrl_info) goto fail;
+ memset(hdw->mpeg_ctrl_info,0,
+ sizeof(*(hdw->mpeg_ctrl_info)) * MPEGDEF_COUNT);
+ for (idx = 0; idx < MPEGDEF_COUNT; idx++) {
+ cptr = hdw->controls + idx + CTRLDEF_COUNT;
+ ciptr = &(hdw->mpeg_ctrl_info[idx].info);
+ ciptr->desc = hdw->mpeg_ctrl_info[idx].desc;
+ ciptr->name = mpeg_ids[idx].strid;
+ ciptr->v4l_id = mpeg_ids[idx].id;
+ ciptr->skip_init = !0;
+ ciptr->get_value = ctrl_cx2341x_get;
+ ciptr->get_v4lflags = ctrl_cx2341x_getv4lflags;
+ ciptr->is_dirty = ctrl_cx2341x_is_dirty;
+ if (!idx) ciptr->clear_dirty = ctrl_cx2341x_clear_dirty;
+ qctrl.id = ciptr->v4l_id;
+ cx2341x_ctrl_query(&hdw->enc_ctl_state,&qctrl);
+ if (!(qctrl.flags & V4L2_CTRL_FLAG_READ_ONLY)) {
+ ciptr->set_value = ctrl_cx2341x_set;
+ }
+ strncpy(hdw->mpeg_ctrl_info[idx].desc,qctrl.name,
+ PVR2_CTLD_INFO_DESC_SIZE);
+ hdw->mpeg_ctrl_info[idx].desc[PVR2_CTLD_INFO_DESC_SIZE-1] = 0;
+ ciptr->default_value = qctrl.default_value;
+ switch (qctrl.type) {
+ default:
+ case V4L2_CTRL_TYPE_INTEGER:
+ ciptr->type = pvr2_ctl_int;
+ ciptr->def.type_int.min_value = qctrl.minimum;
+ ciptr->def.type_int.max_value = qctrl.maximum;
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ ciptr->type = pvr2_ctl_bool;
+ break;
+ case V4L2_CTRL_TYPE_MENU:
+ ciptr->type = pvr2_ctl_enum;
+ ciptr->def.type_enum.value_names =
+ cx2341x_ctrl_get_menu(ciptr->v4l_id);
+ for (cnt1 = 0;
+ ciptr->def.type_enum.value_names[cnt1] != NULL;
+ cnt1++) { }
+ ciptr->def.type_enum.count = cnt1;
+ break;
+ }
+ cptr->info = ciptr;
+ }
+
+ // Initialize video standard enum dynamic control
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDENUM);
+ if (cptr) {
+ memcpy(&hdw->std_info_enum,cptr->info,
+ sizeof(hdw->std_info_enum));
+ cptr->info = &hdw->std_info_enum;
+
+ }
+ // Initialize control data regarding video standard masks
+ valid_std_mask = pvr2_std_get_usable();
+ for (idx = 0; idx < 32; idx++) {
+ if (!(valid_std_mask & (1 << idx))) continue;
+ cnt1 = pvr2_std_id_to_str(
+ hdw->std_mask_names[idx],
+ sizeof(hdw->std_mask_names[idx])-1,
+ 1 << idx);
+ hdw->std_mask_names[idx][cnt1] = 0;
+ }
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDAVAIL);
+ if (cptr) {
+ memcpy(&hdw->std_info_avail,cptr->info,
+ sizeof(hdw->std_info_avail));
+ cptr->info = &hdw->std_info_avail;
+ hdw->std_info_avail.def.type_bitmask.bit_names =
+ hdw->std_mask_ptrs;
+ hdw->std_info_avail.def.type_bitmask.valid_bits =
+ valid_std_mask;
+ }
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR);
+ if (cptr) {
+ memcpy(&hdw->std_info_cur,cptr->info,
+ sizeof(hdw->std_info_cur));
+ cptr->info = &hdw->std_info_cur;
+ hdw->std_info_cur.def.type_bitmask.bit_names =
+ hdw->std_mask_ptrs;
+ hdw->std_info_avail.def.type_bitmask.valid_bits =
+ valid_std_mask;
+ }
+
+ hdw->eeprom_addr = -1;
+ hdw->unit_number = -1;
+ hdw->v4l_minor_number = -1;
+ hdw->ctl_write_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_write_buffer) goto fail;
+ hdw->ctl_read_buffer = kmalloc(PVR2_CTL_BUFFSIZE,GFP_KERNEL);
+ if (!hdw->ctl_read_buffer) goto fail;
+ hdw->ctl_write_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_write_urb) goto fail;
+ hdw->ctl_read_urb = usb_alloc_urb(0,GFP_KERNEL);
+ if (!hdw->ctl_read_urb) goto fail;
+
+ down(&pvr2_unit_sem); do {
+ for (idx = 0; idx < PVR_NUM; idx++) {
+ if (unit_pointers[idx]) continue;
+ hdw->unit_number = idx;
+ unit_pointers[idx] = hdw;
+ break;
+ }
+ } while (0); up(&pvr2_unit_sem);
+
+ cnt1 = 0;
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"pvrusb2");
+ cnt1 += cnt2;
+ if (hdw->unit_number >= 0) {
+ cnt2 = scnprintf(hdw->name+cnt1,sizeof(hdw->name)-cnt1,"_%c",
+ ('a' + hdw->unit_number));
+ cnt1 += cnt2;
+ }
+ if (cnt1 >= sizeof(hdw->name)) cnt1 = sizeof(hdw->name)-1;
+ hdw->name[cnt1] = 0;
+
+ pvr2_trace(PVR2_TRACE_INIT,"Driver unit number is %d, name is %s",
+ hdw->unit_number,hdw->name);
+
+ hdw->tuner_type = -1;
+ hdw->flag_ok = !0;
+ /* Initialize the mask of subsystems that we will shut down when we
+ stop streaming. */
+ hdw->subsys_stream_mask = PVR2_SUBSYS_RUN_ALL;
+ hdw->subsys_stream_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
+
+ pvr2_trace(PVR2_TRACE_INIT,"subsys_stream_mask: 0x%lx",
+ hdw->subsys_stream_mask);
+
+ hdw->usb_intf = intf;
+ hdw->usb_dev = interface_to_usbdev(intf);
+
+ ifnum = hdw->usb_intf->cur_altsetting->desc.bInterfaceNumber;
+ usb_set_interface(hdw->usb_dev,ifnum,0);
+
+ mutex_init(&hdw->ctl_lock_mutex);
+ mutex_init(&hdw->big_lock_mutex);
+
+ return hdw;
+ fail:
+ if (hdw) {
+ if (hdw->ctl_read_urb) usb_free_urb(hdw->ctl_read_urb);
+ if (hdw->ctl_write_urb) usb_free_urb(hdw->ctl_write_urb);
+ if (hdw->ctl_read_buffer) kfree(hdw->ctl_read_buffer);
+ if (hdw->ctl_write_buffer) kfree(hdw->ctl_write_buffer);
+ if (hdw->controls) kfree(hdw->controls);
+ if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info);
+ kfree(hdw);
+ }
+ return 0;
+}
+
+
+/* Remove _all_ associations between this driver and the underlying USB
+ layer. */
+void pvr2_hdw_remove_usb_stuff(struct pvr2_hdw *hdw)
+{
+ if (hdw->flag_disconnected) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_remove_usb_stuff: hdw=%p",hdw);
+ if (hdw->ctl_read_urb) {
+ usb_kill_urb(hdw->ctl_read_urb);
+ usb_free_urb(hdw->ctl_read_urb);
+ hdw->ctl_read_urb = 0;
+ }
+ if (hdw->ctl_write_urb) {
+ usb_kill_urb(hdw->ctl_write_urb);
+ usb_free_urb(hdw->ctl_write_urb);
+ hdw->ctl_write_urb = 0;
+ }
+ if (hdw->ctl_read_buffer) {
+ kfree(hdw->ctl_read_buffer);
+ hdw->ctl_read_buffer = 0;
+ }
+ if (hdw->ctl_write_buffer) {
+ kfree(hdw->ctl_write_buffer);
+ hdw->ctl_write_buffer = 0;
+ }
+ pvr2_hdw_render_useless_unlocked(hdw);
+ hdw->flag_disconnected = !0;
+ hdw->usb_dev = 0;
+ hdw->usb_intf = 0;
+}
+
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_destroy: hdw=%p",hdw);
+ if (hdw->fw_buffer) {
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = 0;
+ }
+ if (hdw->vid_stream) {
+ pvr2_stream_destroy(hdw->vid_stream);
+ hdw->vid_stream = 0;
+ }
+ if (hdw->audio_stat) {
+ hdw->audio_stat->detach(hdw->audio_stat->ctxt);
+ }
+ if (hdw->decoder_ctrl) {
+ hdw->decoder_ctrl->detach(hdw->decoder_ctrl->ctxt);
+ }
+ pvr2_i2c_core_done(hdw);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ down(&pvr2_unit_sem); do {
+ if ((hdw->unit_number >= 0) &&
+ (hdw->unit_number < PVR_NUM) &&
+ (unit_pointers[hdw->unit_number] == hdw)) {
+ unit_pointers[hdw->unit_number] = 0;
+ }
+ } while (0); up(&pvr2_unit_sem);
+ if (hdw->controls) kfree(hdw->controls);
+ if (hdw->mpeg_ctrl_info) kfree(hdw->mpeg_ctrl_info);
+ if (hdw->std_defs) kfree(hdw->std_defs);
+ if (hdw->std_enum_names) kfree(hdw->std_enum_names);
+ kfree(hdw);
+}
+
+
+int pvr2_hdw_init_ok(struct pvr2_hdw *hdw)
+{
+ return hdw->flag_init_ok;
+}
+
+
+int pvr2_hdw_dev_ok(struct pvr2_hdw *hdw)
+{
+ return (hdw && hdw->flag_ok);
+}
+
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_hdw_disconnect(hdw=%p)",hdw);
+ LOCK_TAKE(hdw->big_lock);
+ LOCK_TAKE(hdw->ctl_lock);
+ pvr2_hdw_remove_usb_stuff(hdw);
+ LOCK_GIVE(hdw->ctl_lock);
+ LOCK_GIVE(hdw->big_lock);
+}
+
+
+// Attempt to autoselect an appropriate value for std_enum_cur given
+// whatever is currently in std_mask_cur
+void pvr2_hdw_internal_find_stdenum(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+ for (idx = 1; idx < hdw->std_enum_cnt; idx++) {
+ if (hdw->std_defs[idx-1].id == hdw->std_mask_cur) {
+ hdw->std_enum_cur = idx;
+ return;
+ }
+ }
+ hdw->std_enum_cur = 0;
+}
+
+
+// Calculate correct set of enumerated standards based on currently known
+// set of available standards bits.
+void pvr2_hdw_internal_set_std_avail(struct pvr2_hdw *hdw)
+{
+ struct v4l2_standard *newstd;
+ unsigned int std_cnt;
+ unsigned int idx;
+
+ newstd = pvr2_std_create_enum(&std_cnt,hdw->std_mask_avail);
+
+ if (hdw->std_defs) {
+ kfree(hdw->std_defs);
+ hdw->std_defs = 0;
+ }
+ hdw->std_enum_cnt = 0;
+ if (hdw->std_enum_names) {
+ kfree(hdw->std_enum_names);
+ hdw->std_enum_names = 0;
+ }
+
+ if (!std_cnt) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Failed to identify any viable standards");
+ }
+ hdw->std_enum_names = kmalloc(sizeof(char *)*(std_cnt+1),GFP_KERNEL);
+ hdw->std_enum_names[0] = "none";
+ for (idx = 0; idx < std_cnt; idx++) {
+ hdw->std_enum_names[idx+1] =
+ newstd[idx].name;
+ }
+ // Set up the dynamic control for this standard
+ hdw->std_info_enum.def.type_enum.value_names = hdw->std_enum_names;
+ hdw->std_info_enum.def.type_enum.count = std_cnt+1;
+ hdw->std_defs = newstd;
+ hdw->std_enum_cnt = std_cnt+1;
+ hdw->std_enum_cur = 0;
+ hdw->std_info_cur.def.type_bitmask.valid_bits = hdw->std_mask_avail;
+}
+
+
+int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,
+ struct v4l2_standard *std,
+ unsigned int idx)
+{
+ int ret = -EINVAL;
+ if (!idx) return ret;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (idx >= hdw->std_enum_cnt) break;
+ idx--;
+ memcpy(std,hdw->std_defs+idx,sizeof(*std));
+ ret = 0;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return ret;
+}
+
+
+/* Get the number of defined controls */
+unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *hdw)
+{
+ return hdw->control_cnt;
+}
+
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *hdw,
+ unsigned int idx)
+{
+ if (idx >= hdw->control_cnt) return 0;
+ return hdw->controls + idx;
+}
+
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *hdw,
+ unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->internal_id;
+ if (i && (i == ctl_id)) return cptr;
+ }
+ return 0;
+}
+
+
+/* Given a V4L ID, retrieve the control structure associated with it. */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *hdw,unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->v4l_id;
+ if (i && (i == ctl_id)) return cptr;
+ }
+ return 0;
+}
+
+
+/* Given a V4L ID for its immediate predecessor, retrieve the control
+ structure associated with it. */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *hdw,
+ unsigned int ctl_id)
+{
+ struct pvr2_ctrl *cptr,*cp2;
+ unsigned int idx;
+ int i;
+
+ /* This could be made a lot more efficient, but for now... */
+ cp2 = 0;
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ i = cptr->info->v4l_id;
+ if (!i) continue;
+ if (i <= ctl_id) continue;
+ if (cp2 && (cp2->info->v4l_id < i)) continue;
+ cp2 = cptr;
+ }
+ return cp2;
+ return 0;
+}
+
+
+static const char *get_ctrl_typename(enum pvr2_ctl_type tp)
+{
+ switch (tp) {
+ case pvr2_ctl_int: return "integer";
+ case pvr2_ctl_enum: return "enum";
+ case pvr2_ctl_bool: return "boolean";
+ case pvr2_ctl_bitmask: return "bitmask";
+ }
+ return "";
+}
+
+
+/* Commit all control changes made up to this point. Subsystems can be
+ indirectly affected by these changes. For a given set of things being
+ committed, we'll clear the affected subsystem bits and then once we're
+ done committing everything we'll make a request to restore the subsystem
+ state(s) back to their previous value before this function was called.
+ Thus we can automatically reconfigure affected pieces of the driver as
+ controls are changed. */
+int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
+{
+ unsigned long saved_subsys_mask = hdw->subsys_enabled_mask;
+ unsigned long stale_subsys_mask = 0;
+ unsigned int idx;
+ struct pvr2_ctrl *cptr;
+ int value;
+ int commit_flag = 0;
+ char buf[100];
+ unsigned int bcnt,ccnt;
+
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ if (cptr->info->is_dirty == 0) continue;
+ if (!cptr->info->is_dirty(cptr)) continue;
+ if (!commit_flag) {
+ commit_flag = !0;
+ }
+
+ bcnt = scnprintf(buf,sizeof(buf),"\"%s\" <-- ",
+ cptr->info->name);
+ value = 0;
+ cptr->info->get_value(cptr,&value);
+ pvr2_ctrl_value_to_sym_internal(cptr,~0,value,
+ buf+bcnt,
+ sizeof(buf)-bcnt,&ccnt);
+ bcnt += ccnt;
+ bcnt += scnprintf(buf+bcnt,sizeof(buf)-bcnt," <%s>",
+ get_ctrl_typename(cptr->info->type));
+ pvr2_trace(PVR2_TRACE_CTL,
+ "/*--TRACE_COMMIT--*/ %.*s",
+ bcnt,buf);
+ }
+
+ if (!commit_flag) {
+ /* Nothing has changed */
+ return 0;
+ }
+
+ /* When video standard changes, reset the hres and vres values -
+ but if the user has pending changes there, then let the changes
+ take priority. */
+ if (hdw->std_dirty) {
+ /* Rewrite the vertical resolution to be appropriate to the
+ video standard that has been selected. */
+ int nvres;
+ if (hdw->std_mask_cur & V4L2_STD_525_60) {
+ nvres = 480;
+ } else {
+ nvres = 576;
+ }
+ if (nvres != hdw->res_ver_val) {
+ hdw->res_ver_val = nvres;
+ hdw->res_ver_dirty = !0;
+ }
+ }
+
+ if (hdw->std_dirty ||
+ 0) {
+ /* If any of this changes, then the encoder needs to be
+ reconfigured, and we need to reset the stream. */
+ stale_subsys_mask |= (1<<PVR2_SUBSYS_B_ENC_CFG);
+ stale_subsys_mask |= hdw->subsys_stream_mask;
+ }
+
+ if (hdw->srate_dirty) {
+ /* Write new sample rate into control structure since
+ * the master copy is stale. We must track srate
+ * separate from the mpeg control structure because
+ * other logic also uses this value. */
+ struct v4l2_ext_controls cs;
+ struct v4l2_ext_control c1;
+ memset(&cs,0,sizeof(cs));
+ memset(&c1,0,sizeof(c1));
+ cs.controls = &c1;
+ cs.count = 1;
+ c1.id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ;
+ c1.value = hdw->srate_val;
+ cx2341x_ext_ctrls(&hdw->enc_ctl_state,&cs,VIDIOC_S_EXT_CTRLS);
+ }
+
+ /* Scan i2c core at this point - before we clear all the dirty
+ bits. Various parts of the i2c core will notice dirty bits as
+ appropriate and arrange to broadcast or directly send updates to
+ the client drivers in order to keep everything in sync */
+ pvr2_i2c_core_check_stale(hdw);
+
+ for (idx = 0; idx < hdw->control_cnt; idx++) {
+ cptr = hdw->controls + idx;
+ if (!cptr->info->clear_dirty) continue;
+ cptr->info->clear_dirty(cptr);
+ }
+
+ /* Now execute i2c core update */
+ pvr2_i2c_core_sync(hdw);
+
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,stale_subsys_mask,0);
+ pvr2_hdw_subsys_bit_chg_no_lock(hdw,~0,saved_subsys_mask);
+
+ return 0;
+}
+
+
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_commit_ctl_internal(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return 0;
+}
+
+
+void pvr2_hdw_poll(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_i2c_core_sync(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *hdw,
+ void (*func)(void *),
+ void *data)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ hdw->poll_trigger_func = func;
+ hdw->poll_trigger_data = data;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *hdw)
+{
+ if (hdw->poll_trigger_func) {
+ hdw->poll_trigger_func(hdw->poll_trigger_data);
+ }
+}
+
+
+void pvr2_hdw_poll_trigger(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->big_lock); do {
+ pvr2_hdw_poll_trigger_unlocked(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+/* Return name for this driver instance */
+const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *hdw)
+{
+ return hdw->name;
+}
+
+
+/* Return bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status_internal(struct pvr2_hdw *hdw)
+{
+ unsigned int msk = 0;
+ switch (hdw->input_val) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_RADIO:
+ if (hdw->decoder_ctrl &&
+ hdw->decoder_ctrl->tuned(hdw->decoder_ctrl->ctxt)) {
+ msk |= PVR2_SIGNAL_OK;
+ if (hdw->audio_stat &&
+ hdw->audio_stat->status(hdw->audio_stat->ctxt)) {
+ if (hdw->flag_stereo) {
+ msk |= PVR2_SIGNAL_STEREO;
+ }
+ if (hdw->flag_bilingual) {
+ msk |= PVR2_SIGNAL_SAP;
+ }
+ }
+ }
+ break;
+ default:
+ msk |= PVR2_SIGNAL_OK | PVR2_SIGNAL_STEREO;
+ }
+ return msk;
+}
+
+
+int pvr2_hdw_is_hsm(struct pvr2_hdw *hdw)
+{
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = 0x0b;
+ result = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ result = (hdw->cmd_buffer[0] != 0);
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ return result;
+}
+
+
+/* Return bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *hdw)
+{
+ unsigned int msk = 0;
+ LOCK_TAKE(hdw->big_lock); do {
+ msk = pvr2_hdw_get_signal_status_internal(hdw);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+ return msk;
+}
+
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *hp)
+{
+ return hp->vid_stream;
+}
+
+
+void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw)
+{
+ int nr = pvr2_hdw_get_unit_number(hdw);
+ LOCK_TAKE(hdw->big_lock); do {
+ hdw->log_requested = !0;
+ printk(KERN_INFO "pvrusb2: ================= START STATUS CARD #%d =================\n", nr);
+ pvr2_i2c_core_check_stale(hdw);
+ hdw->log_requested = 0;
+ pvr2_i2c_core_sync(hdw);
+ pvr2_trace(PVR2_TRACE_INFO,"cx2341x config:");
+ cx2341x_log_status(&hdw->enc_ctl_state, "pvrusb2");
+ printk(KERN_INFO "pvrusb2: ================== END STATUS CARD #%d ==================\n", nr);
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *hdw, int enable_flag)
+{
+ int ret;
+ u16 address;
+ unsigned int pipe;
+ LOCK_TAKE(hdw->big_lock); do {
+ if ((hdw->fw_buffer == 0) == !enable_flag) break;
+
+ if (!enable_flag) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Cleaning up after CPU firmware fetch");
+ kfree(hdw->fw_buffer);
+ hdw->fw_buffer = 0;
+ hdw->fw_size = 0;
+ /* Now release the CPU. It will disconnect and
+ reconnect later. */
+ pvr2_hdw_cpureset_assert(hdw,0);
+ break;
+ }
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Preparing to suck out CPU firmware");
+ hdw->fw_size = 0x2000;
+ hdw->fw_buffer = kmalloc(hdw->fw_size,GFP_KERNEL);
+ if (!hdw->fw_buffer) {
+ hdw->fw_size = 0;
+ break;
+ }
+
+ memset(hdw->fw_buffer,0,hdw->fw_size);
+
+ /* We have to hold the CPU during firmware upload. */
+ pvr2_hdw_cpureset_assert(hdw,1);
+
+ /* download the firmware from address 0000-1fff in 2048
+ (=0x800) bytes chunk. */
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,"Grabbing CPU firmware");
+ pipe = usb_rcvctrlpipe(hdw->usb_dev, 0);
+ for(address = 0; address < hdw->fw_size; address += 0x800) {
+ ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0xc0,
+ address,0,
+ hdw->fw_buffer+address,0x800,HZ);
+ if (ret < 0) break;
+ }
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,"Done grabbing CPU firmware");
+
+ } while (0); LOCK_GIVE(hdw->big_lock);
+}
+
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *hdw)
+{
+ return hdw->fw_buffer != 0;
+}
+
+
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *hdw,unsigned int offs,
+ char *buf,unsigned int cnt)
+{
+ int ret = -EINVAL;
+ LOCK_TAKE(hdw->big_lock); do {
+ if (!buf) break;
+ if (!cnt) break;
+
+ if (!hdw->fw_buffer) {
+ ret = -EIO;
+ break;
+ }
+
+ if (offs >= hdw->fw_size) {
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d EOF",
+ offs);
+ ret = 0;
+ break;
+ }
+
+ if (offs + cnt > hdw->fw_size) cnt = hdw->fw_size - offs;
+
+ memcpy(buf,hdw->fw_buffer+offs,cnt);
+
+ pvr2_trace(PVR2_TRACE_FIRMWARE,
+ "Read firmware data offs=%d cnt=%d",
+ offs,cnt);
+ ret = cnt;
+ } while (0); LOCK_GIVE(hdw->big_lock);
+
+ return ret;
+}
+
+
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *hdw)
+{
+ return hdw->v4l_minor_number;
+}
+
+
+/* Store the v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *hdw,int v)
+{
+ hdw->v4l_minor_number = v;
+}
+
+
+void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw)
+{
+ if (!hdw->usb_dev) return;
+ usb_settoggle(hdw->usb_dev, PVR2_CTL_WRITE_ENDPOINT & 0xf,
+ !(PVR2_CTL_WRITE_ENDPOINT & USB_DIR_IN), 0);
+ usb_settoggle(hdw->usb_dev, PVR2_CTL_READ_ENDPOINT & 0xf,
+ !(PVR2_CTL_READ_ENDPOINT & USB_DIR_IN), 0);
+ usb_clear_halt(hdw->usb_dev,
+ usb_rcvbulkpipe(hdw->usb_dev,
+ PVR2_CTL_READ_ENDPOINT & 0x7f));
+ usb_clear_halt(hdw->usb_dev,
+ usb_sndbulkpipe(hdw->usb_dev,
+ PVR2_CTL_WRITE_ENDPOINT & 0x7f));
+}
+
+
+static void pvr2_ctl_write_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_write_pend_flag = 0;
+ if (hdw->ctl_read_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_read_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_hdw *hdw = urb->context;
+ hdw->ctl_read_pend_flag = 0;
+ if (hdw->ctl_write_pend_flag) return;
+ complete(&hdw->ctl_done);
+}
+
+
+static void pvr2_ctl_timeout(unsigned long data)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)data;
+ if (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ hdw->ctl_timeout_flag = !0;
+ if (hdw->ctl_write_pend_flag && hdw->ctl_write_urb) {
+ usb_unlink_urb(hdw->ctl_write_urb);
+ }
+ if (hdw->ctl_read_pend_flag && hdw->ctl_read_urb) {
+ usb_unlink_urb(hdw->ctl_read_urb);
+ }
+ }
+}
+
+
+int pvr2_send_request_ex(struct pvr2_hdw *hdw,
+ unsigned int timeout,int probe_fl,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len)
+{
+ unsigned int idx;
+ int status = 0;
+ struct timer_list timer;
+ if (!hdw->ctl_lock_held) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " without lock!!");
+ return -EDEADLK;
+ }
+ if ((!hdw->flag_ok) && !probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when device not ok");
+ return -EIO;
+ }
+ if (!(hdw->ctl_read_urb && hdw->ctl_write_urb)) {
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute control transfer"
+ " when USB is disconnected");
+ }
+ return -ENOTTY;
+ }
+
+ /* Ensure that we have sane parameters */
+ if (!write_data) write_len = 0;
+ if (!read_data) read_len = 0;
+ if (write_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-write transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if (read_len > PVR2_CTL_BUFFSIZE) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute %d byte"
+ " control-read transfer (limit=%d)",
+ write_len,PVR2_CTL_BUFFSIZE);
+ return -EINVAL;
+ }
+ if ((!write_len) && (!read_len)) {
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "Attempted to execute null control transfer?");
+ return -EINVAL;
+ }
+
+
+ hdw->cmd_debug_state = 1;
+ if (write_len) {
+ hdw->cmd_debug_code = ((unsigned char *)write_data)[0];
+ } else {
+ hdw->cmd_debug_code = 0;
+ }
+ hdw->cmd_debug_write_len = write_len;
+ hdw->cmd_debug_read_len = read_len;
+
+ /* Initialize common stuff */
+ init_completion(&hdw->ctl_done);
+ hdw->ctl_timeout_flag = 0;
+ hdw->ctl_write_pend_flag = 0;
+ hdw->ctl_read_pend_flag = 0;
+ init_timer(&timer);
+ timer.expires = jiffies + timeout;
+ timer.data = (unsigned long)hdw;
+ timer.function = pvr2_ctl_timeout;
+
+ if (write_len) {
+ hdw->cmd_debug_state = 2;
+ /* Transfer write data to internal buffer */
+ for (idx = 0; idx < write_len; idx++) {
+ hdw->ctl_write_buffer[idx] =
+ ((unsigned char *)write_data)[idx];
+ }
+ /* Initiate a write request */
+ usb_fill_bulk_urb(hdw->ctl_write_urb,
+ hdw->usb_dev,
+ usb_sndbulkpipe(hdw->usb_dev,
+ PVR2_CTL_WRITE_ENDPOINT),
+ hdw->ctl_write_buffer,
+ write_len,
+ pvr2_ctl_write_complete,
+ hdw);
+ hdw->ctl_write_urb->actual_length = 0;
+ hdw->ctl_write_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_write_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit write-control"
+ " URB status=%d",status);
+ hdw->ctl_write_pend_flag = 0;
+ goto done;
+ }
+ }
+
+ if (read_len) {
+ hdw->cmd_debug_state = 3;
+ memset(hdw->ctl_read_buffer,0x43,read_len);
+ /* Initiate a read request */
+ usb_fill_bulk_urb(hdw->ctl_read_urb,
+ hdw->usb_dev,
+ usb_rcvbulkpipe(hdw->usb_dev,
+ PVR2_CTL_READ_ENDPOINT),
+ hdw->ctl_read_buffer,
+ read_len,
+ pvr2_ctl_read_complete,
+ hdw);
+ hdw->ctl_read_urb->actual_length = 0;
+ hdw->ctl_read_pend_flag = !0;
+ status = usb_submit_urb(hdw->ctl_read_urb,GFP_KERNEL);
+ if (status < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to submit read-control"
+ " URB status=%d",status);
+ hdw->ctl_read_pend_flag = 0;
+ goto done;
+ }
+ }
+
+ /* Start timer */
+ add_timer(&timer);
+
+ /* Now wait for all I/O to complete */
+ hdw->cmd_debug_state = 4;
+ while (hdw->ctl_write_pend_flag || hdw->ctl_read_pend_flag) {
+ wait_for_completion(&hdw->ctl_done);
+ }
+ hdw->cmd_debug_state = 5;
+
+ /* Stop timer */
+ del_timer_sync(&timer);
+
+ hdw->cmd_debug_state = 6;
+ status = 0;
+
+ if (hdw->ctl_timeout_flag) {
+ status = -ETIMEDOUT;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Timed out control-write");
+ }
+ goto done;
+ }
+
+ if (write_len) {
+ /* Validate results of write request */
+ if ((hdw->ctl_write_urb->status != 0) &&
+ (hdw->ctl_write_urb->status != -ENOENT) &&
+ (hdw->ctl_write_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_write_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the write */
+ status = hdw->ctl_write_urb->status;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB failure,"
+ " status=%d",
+ status);
+ }
+ goto done;
+ }
+ if (hdw->ctl_write_urb->actual_length < write_len) {
+ /* Failed to write enough data */
+ status = -EIO;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-write URB short,"
+ " expected=%d got=%d",
+ write_len,
+ hdw->ctl_write_urb->actual_length);
+ }
+ goto done;
+ }
+ }
+ if (read_len) {
+ /* Validate results of read request */
+ if ((hdw->ctl_read_urb->status != 0) &&
+ (hdw->ctl_read_urb->status != -ENOENT) &&
+ (hdw->ctl_read_urb->status != -ESHUTDOWN) &&
+ (hdw->ctl_read_urb->status != -ECONNRESET)) {
+ /* USB subsystem is reporting some kind of failure
+ on the read */
+ status = hdw->ctl_read_urb->status;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB failure,"
+ " status=%d",
+ status);
+ }
+ goto done;
+ }
+ if (hdw->ctl_read_urb->actual_length < read_len) {
+ /* Failed to read enough data */
+ status = -EIO;
+ if (!probe_fl) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "control-read URB short,"
+ " expected=%d got=%d",
+ read_len,
+ hdw->ctl_read_urb->actual_length);
+ }
+ goto done;
+ }
+ /* Transfer retrieved data out from internal buffer */
+ for (idx = 0; idx < read_len; idx++) {
+ ((unsigned char *)read_data)[idx] =
+ hdw->ctl_read_buffer[idx];
+ }
+ }
+
+ done:
+
+ hdw->cmd_debug_state = 0;
+ if ((status < 0) && (!probe_fl)) {
+ pvr2_hdw_render_useless_unlocked(hdw);
+ }
+ return status;
+}
+
+
+int pvr2_send_request(struct pvr2_hdw *hdw,
+ void *write_data,unsigned int write_len,
+ void *read_data,unsigned int read_len)
+{
+ return pvr2_send_request_ex(hdw,HZ*4,0,
+ write_data,write_len,
+ read_data,read_len);
+}
+
+int pvr2_write_register(struct pvr2_hdw *hdw, u16 reg, u32 data)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = 0x04; /* write register prefix */
+ PVR2_DECOMPOSE_LE(hdw->cmd_buffer,1,data);
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_read_register(struct pvr2_hdw *hdw, u16 reg, u32 *data)
+{
+ int ret = 0;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = 0x05; /* read register prefix */
+ hdw->cmd_buffer[1] = 0;
+ hdw->cmd_buffer[2] = 0;
+ hdw->cmd_buffer[3] = 0;
+ hdw->cmd_buffer[4] = 0;
+ hdw->cmd_buffer[5] = 0;
+ hdw->cmd_buffer[6] = (reg >> 8) & 0xff;
+ hdw->cmd_buffer[7] = reg & 0xff;
+
+ ret |= pvr2_send_request(hdw, hdw->cmd_buffer, 8, hdw->cmd_buffer, 4);
+ *data = PVR2_COMPOSE_LE(hdw->cmd_buffer,0);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_write_u16(struct pvr2_hdw *hdw, u16 data, int res)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = (data >> 8) & 0xff;
+ hdw->cmd_buffer[1] = data & 0xff;
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 2, hdw->cmd_buffer, res);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+int pvr2_write_u8(struct pvr2_hdw *hdw, u8 data, int res)
+{
+ int ret;
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ hdw->cmd_buffer[0] = data;
+
+ ret = pvr2_send_request(hdw, hdw->cmd_buffer, 1, hdw->cmd_buffer, res);
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+
+void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *hdw)
+{
+ if (!hdw->flag_ok) return;
+ pvr2_trace(PVR2_TRACE_INIT,"render_useless");
+ hdw->flag_ok = 0;
+ if (hdw->vid_stream) {
+ pvr2_stream_setup(hdw->vid_stream,0,0,0);
+ }
+ hdw->flag_streaming_enabled = 0;
+ hdw->subsys_enabled_mask = 0;
+}
+
+
+void pvr2_hdw_render_useless(struct pvr2_hdw *hdw)
+{
+ LOCK_TAKE(hdw->ctl_lock);
+ pvr2_hdw_render_useless_unlocked(hdw);
+ LOCK_GIVE(hdw->ctl_lock);
+}
+
+
+void pvr2_hdw_device_reset(struct pvr2_hdw *hdw)
+{
+ int ret;
+ pvr2_trace(PVR2_TRACE_INIT,"Performing a device reset...");
+ ret = usb_lock_device_for_reset(hdw->usb_dev,0);
+ if (ret == 1) {
+ ret = usb_reset_device(hdw->usb_dev);
+ usb_unlock_device(hdw->usb_dev);
+ } else {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to lock USB device ret=%d",ret);
+ }
+ if (init_pause_msec) {
+ pvr2_trace(PVR2_TRACE_INFO,
+ "Waiting %u msec for hardware to settle",
+ init_pause_msec);
+ msleep(init_pause_msec);
+ }
+
+}
+
+
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *hdw,int val)
+{
+ char da[1];
+ unsigned int pipe;
+ int ret;
+
+ if (!hdw->usb_dev) return;
+
+ pvr2_trace(PVR2_TRACE_INIT,"cpureset_assert(%d)",val);
+
+ da[0] = val ? 0x01 : 0x00;
+
+ /* Write the CPUCS register on the 8051. The lsb of the register
+ is the reset bit; a 1 asserts reset while a 0 clears it. */
+ pipe = usb_sndctrlpipe(hdw->usb_dev, 0);
+ ret = usb_control_msg(hdw->usb_dev,pipe,0xa0,0x40,0xe600,0,da,1,HZ);
+ if (ret < 0) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "cpureset_assert(%d) error=%d",val,ret);
+ pvr2_hdw_render_useless(hdw);
+ }
+}
+
+
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *hdw)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_trace(PVR2_TRACE_INIT,"Requesting uproc hard reset");
+ hdw->flag_ok = !0;
+ hdw->cmd_buffer[0] = 0xdd;
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ return status;
+}
+
+
+int pvr2_hdw_cmd_powerup(struct pvr2_hdw *hdw)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ pvr2_trace(PVR2_TRACE_INIT,"Requesting powerup");
+ hdw->cmd_buffer[0] = 0xde;
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ return status;
+}
+
+
+int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *hdw)
+{
+ if (!hdw->decoder_ctrl) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Unable to reset decoder: nothing attached");
+ return -ENOTTY;
+ }
+
+ if (!hdw->decoder_ctrl->force_reset) {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Unable to reset decoder: not implemented");
+ return -ENOTTY;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,
+ "Requesting decoder reset");
+ hdw->decoder_ctrl->force_reset(hdw->decoder_ctrl->ctxt);
+ return 0;
+}
+
+
+int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl)
+{
+ int status;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = (runFl ? 0x36 : 0x37);
+ status = pvr2_send_request(hdw,hdw->cmd_buffer,1,0,0);
+ } while (0); LOCK_GIVE(hdw->ctl_lock);
+ if (!status) {
+ hdw->subsys_enabled_mask =
+ ((hdw->subsys_enabled_mask &
+ ~(1<<PVR2_SUBSYS_B_USBSTREAM_RUN)) |
+ (runFl ? (1<<PVR2_SUBSYS_B_USBSTREAM_RUN) : 0));
+ }
+ return status;
+}
+
+
+void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *ptr)
+{
+ ptr->big_lock_held = hdw->big_lock_held;
+ ptr->ctl_lock_held = hdw->ctl_lock_held;
+ ptr->flag_ok = hdw->flag_ok;
+ ptr->flag_disconnected = hdw->flag_disconnected;
+ ptr->flag_init_ok = hdw->flag_init_ok;
+ ptr->flag_streaming_enabled = hdw->flag_streaming_enabled;
+ ptr->subsys_flags = hdw->subsys_enabled_mask;
+ ptr->cmd_debug_state = hdw->cmd_debug_state;
+ ptr->cmd_code = hdw->cmd_debug_code;
+ ptr->cmd_debug_write_len = hdw->cmd_debug_write_len;
+ ptr->cmd_debug_read_len = hdw->cmd_debug_read_len;
+ ptr->cmd_debug_timeout = hdw->ctl_timeout_flag;
+ ptr->cmd_debug_write_pend = hdw->ctl_write_pend_flag;
+ ptr->cmd_debug_read_pend = hdw->ctl_read_pend_flag;
+ ptr->cmd_debug_rstatus = hdw->ctl_read_urb->status;
+ ptr->cmd_debug_wstatus = hdw->ctl_read_urb->status;
+}
+
+
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_DIR,dp);
+}
+
+
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_OUT,dp);
+}
+
+
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *dp)
+{
+ return pvr2_read_register(hdw,PVR2_GPIO_IN,dp);
+}
+
+
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_DIR,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing 0x%x:0x%x"
+ " from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO direction changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_DIR,nval);
+}
+
+
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val)
+{
+ u32 cval,nval;
+ int ret;
+ if (~msk) {
+ ret = pvr2_read_register(hdw,PVR2_GPIO_OUT,&cval);
+ if (ret) return ret;
+ nval = (cval & ~msk) | (val & msk);
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing 0x%x:0x%x from 0x%x to 0x%x",
+ msk,val,cval,nval);
+ } else {
+ nval = val;
+ pvr2_trace(PVR2_TRACE_GPIO,
+ "GPIO output changing to 0x%x",nval);
+ }
+ return pvr2_write_register(hdw,PVR2_GPIO_OUT,nval);
+}
+
+
+int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *hdw)
+{
+ int result;
+ LOCK_TAKE(hdw->ctl_lock); do {
+ hdw->cmd_buffer[0] = 0xeb;
+ result = pvr2_send_request(hdw,
+ hdw->cmd_buffer,1,
+ hdw->cmd_buffer,1);
+ if (result < 0) break;
+ result = hdw->cmd_buffer[0];
+ } while(0); LOCK_GIVE(hdw->ctl_lock);
+ return result;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.h b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
new file mode 100644
index 000000000000..63f529154431
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.h
@@ -0,0 +1,335 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_HDW_H
+#define __PVRUSB2_HDW_H
+
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+#include "pvrusb2-io.h"
+#include "pvrusb2-ctrl.h"
+
+
+/* Private internal control ids, look these up with
+ pvr2_hdw_get_ctrl_by_id() - these are NOT visible in V4L */
+#define PVR2_CID_STDENUM 1
+#define PVR2_CID_STDCUR 2
+#define PVR2_CID_STDAVAIL 3
+#define PVR2_CID_INPUT 4
+#define PVR2_CID_AUDIOMODE 5
+#define PVR2_CID_FREQUENCY 6
+#define PVR2_CID_HRES 7
+#define PVR2_CID_VRES 8
+
+/* Legal values for the INPUT state variable */
+#define PVR2_CVAL_INPUT_TV 0
+#define PVR2_CVAL_INPUT_SVIDEO 1
+#define PVR2_CVAL_INPUT_COMPOSITE 2
+#define PVR2_CVAL_INPUT_RADIO 3
+
+/* Values that pvr2_hdw_get_signal_status() returns */
+#define PVR2_SIGNAL_OK 0x0001
+#define PVR2_SIGNAL_STEREO 0x0002
+#define PVR2_SIGNAL_SAP 0x0004
+
+
+/* Subsystem definitions - these are various pieces that can be
+ independently stopped / started. Usually you don't want to mess with
+ this directly (let the driver handle things itself), but it is useful
+ for debugging. */
+#define PVR2_SUBSYS_B_ENC_FIRMWARE 0
+#define PVR2_SUBSYS_B_ENC_CFG 1
+#define PVR2_SUBSYS_B_DIGITIZER_RUN 2
+#define PVR2_SUBSYS_B_USBSTREAM_RUN 3
+#define PVR2_SUBSYS_B_ENC_RUN 4
+
+#define PVR2_SUBSYS_CFG_ALL ( \
+ (1 << PVR2_SUBSYS_B_ENC_FIRMWARE) | \
+ (1 << PVR2_SUBSYS_B_ENC_CFG) )
+#define PVR2_SUBSYS_RUN_ALL ( \
+ (1 << PVR2_SUBSYS_B_DIGITIZER_RUN) | \
+ (1 << PVR2_SUBSYS_B_USBSTREAM_RUN) | \
+ (1 << PVR2_SUBSYS_B_ENC_RUN) )
+#define PVR2_SUBSYS_ALL ( \
+ PVR2_SUBSYS_CFG_ALL | \
+ PVR2_SUBSYS_RUN_ALL )
+
+enum pvr2_config {
+ pvr2_config_empty,
+ pvr2_config_mpeg,
+ pvr2_config_vbi,
+ pvr2_config_radio,
+};
+
+const char *pvr2_config_get_name(enum pvr2_config);
+
+struct pvr2_hdw;
+
+/* Create and return a structure for interacting with the underlying
+ hardware */
+struct pvr2_hdw *pvr2_hdw_create(struct usb_interface *intf,
+ const struct usb_device_id *devid);
+
+/* Poll for background activity (if any) */
+void pvr2_hdw_poll(struct pvr2_hdw *);
+
+/* Trigger a poll to take place later at a convenient time */
+void pvr2_hdw_poll_trigger(struct pvr2_hdw *);
+void pvr2_hdw_poll_trigger_unlocked(struct pvr2_hdw *);
+
+/* Register a callback used to trigger a future poll */
+void pvr2_hdw_setup_poll_trigger(struct pvr2_hdw *,
+ void (*func)(void *),
+ void *data);
+
+/* Get pointer to structure given unit number */
+struct pvr2_hdw *pvr2_hdw_find(int unit_number);
+
+/* Destroy hardware interaction structure */
+void pvr2_hdw_destroy(struct pvr2_hdw *);
+
+/* Set up the structure and attempt to put the device into a usable state.
+ This can be a time-consuming operation, which is why it is not done
+ internally as part of the create() step. Return value is exactly the
+ same as pvr2_hdw_init_ok(). */
+int pvr2_hdw_setup(struct pvr2_hdw *);
+
+/* Initialization succeeded */
+int pvr2_hdw_init_ok(struct pvr2_hdw *);
+
+/* Return true if in the ready (normal) state */
+int pvr2_hdw_dev_ok(struct pvr2_hdw *);
+
+/* Return small integer number [1..N] for logical instance number of this
+ device. This is useful for indexing array-valued module parameters. */
+int pvr2_hdw_get_unit_number(struct pvr2_hdw *);
+
+/* Get pointer to underlying USB device */
+struct usb_device *pvr2_hdw_get_dev(struct pvr2_hdw *);
+
+/* Retrieve serial number of device */
+unsigned long pvr2_hdw_get_sn(struct pvr2_hdw *);
+
+/* Called when hardware has been unplugged */
+void pvr2_hdw_disconnect(struct pvr2_hdw *);
+
+/* Get the number of defined controls */
+unsigned int pvr2_hdw_get_ctrl_count(struct pvr2_hdw *);
+
+/* Retrieve a control handle given its index (0..count-1) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_index(struct pvr2_hdw *,unsigned int);
+
+/* Retrieve a control handle given its internal ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_by_id(struct pvr2_hdw *,unsigned int);
+
+/* Retrieve a control handle given its V4L ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_v4l(struct pvr2_hdw *,unsigned int ctl_id);
+
+/* Retrieve a control handle given its immediate predecessor V4L ID (if any) */
+struct pvr2_ctrl *pvr2_hdw_get_ctrl_nextv4l(struct pvr2_hdw *,
+ unsigned int ctl_id);
+
+/* Commit all control changes made up to this point */
+int pvr2_hdw_commit_ctl(struct pvr2_hdw *);
+
+/* Return name for this driver instance */
+const char *pvr2_hdw_get_driver_name(struct pvr2_hdw *);
+
+/* Return PVR2_SIGNAL_XXXX bit mask indicating signal status */
+unsigned int pvr2_hdw_get_signal_status(struct pvr2_hdw *);
+
+/* Query device and see if it thinks it is on a high-speed USB link */
+int pvr2_hdw_is_hsm(struct pvr2_hdw *);
+
+/* Turn streaming on/off */
+int pvr2_hdw_set_streaming(struct pvr2_hdw *,int);
+
+/* Find out if streaming is on */
+int pvr2_hdw_get_streaming(struct pvr2_hdw *);
+
+/* Configure the type of stream to generate */
+int pvr2_hdw_set_stream_type(struct pvr2_hdw *, enum pvr2_config);
+
+/* Get handle to video output stream */
+struct pvr2_stream *pvr2_hdw_get_video_stream(struct pvr2_hdw *);
+
+/* Emit a video standard struct */
+int pvr2_hdw_get_stdenum_value(struct pvr2_hdw *hdw,struct v4l2_standard *std,
+ unsigned int idx);
+
+/* Enable / disable various pieces of hardware. Items to change are
+ identified by bit positions within msk, and new state for each item is
+ identified by corresponding bit positions within val. */
+void pvr2_hdw_subsys_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val);
+
+/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,msk) */
+void pvr2_hdw_subsys_bit_set(struct pvr2_hdw *hdw,unsigned long msk);
+
+/* Shortcut for pvr2_hdw_subsys_bit_chg(hdw,msk,0) */
+void pvr2_hdw_subsys_bit_clr(struct pvr2_hdw *hdw,unsigned long msk);
+
+/* Retrieve mask indicating which pieces of hardware are currently enabled
+ / configured. */
+unsigned long pvr2_hdw_subsys_get(struct pvr2_hdw *);
+
+/* Adjust mask of what get shut down when streaming is stopped. This is a
+ debugging aid. */
+void pvr2_hdw_subsys_stream_bit_chg(struct pvr2_hdw *hdw,
+ unsigned long msk,unsigned long val);
+
+/* Retrieve mask indicating which pieces of hardware are disabled when
+ streaming is turned off. */
+unsigned long pvr2_hdw_subsys_stream_get(struct pvr2_hdw *);
+
+
+/* Enable / disable retrieval of CPU firmware. This must be enabled before
+ pvr2_hdw_cpufw_get() will function. Note that doing this may prevent
+ the device from running (and leaving this mode may imply a device
+ reset). */
+void pvr2_hdw_cpufw_set_enabled(struct pvr2_hdw *, int enable_flag);
+
+/* Return true if we're in a mode for retrieval CPU firmware */
+int pvr2_hdw_cpufw_get_enabled(struct pvr2_hdw *);
+
+/* Retrieve a piece of the CPU's firmware at the given offset. Return
+ value is the number of bytes retrieved or zero if we're past the end or
+ an error otherwise (e.g. if firmware retrieval is not enabled). */
+int pvr2_hdw_cpufw_get(struct pvr2_hdw *,unsigned int offs,
+ char *buf,unsigned int cnt);
+
+/* Retrieve previously stored v4l minor device number */
+int pvr2_hdw_v4l_get_minor_number(struct pvr2_hdw *);
+
+/* Store the v4l minor device number */
+void pvr2_hdw_v4l_store_minor_number(struct pvr2_hdw *,int);
+
+
+/* The following entry points are all lower level things you normally don't
+ want to worry about. */
+
+/* Attempt to recover from a USB foul-up (in practice I find that if you
+ have to do this, then it's already too late). */
+void pvr2_reset_ctl_endpoints(struct pvr2_hdw *hdw);
+
+/* Issue a command and get a response from the device. LOTS of higher
+ level stuff is built on this. */
+int pvr2_send_request(struct pvr2_hdw *,
+ void *write_ptr,unsigned int write_len,
+ void *read_ptr,unsigned int read_len);
+
+/* Issue a command and get a response from the device. This extended
+ version includes a probe flag (which if set means that device errors
+ should not be logged or treated as fatal) and a timeout in jiffies.
+ This can be used to non-lethally probe the health of endpoint 1. */
+int pvr2_send_request_ex(struct pvr2_hdw *,unsigned int timeout,int probe_fl,
+ void *write_ptr,unsigned int write_len,
+ void *read_ptr,unsigned int read_len);
+
+/* Slightly higher level device communication functions. */
+int pvr2_write_register(struct pvr2_hdw *, u16, u32);
+int pvr2_read_register(struct pvr2_hdw *, u16, u32 *);
+int pvr2_write_u16(struct pvr2_hdw *, u16, int);
+int pvr2_write_u8(struct pvr2_hdw *, u8, int);
+
+/* Call if for any reason we can't talk to the hardware anymore - this will
+ cause the driver to stop flailing on the device. */
+void pvr2_hdw_render_useless(struct pvr2_hdw *);
+void pvr2_hdw_render_useless_unlocked(struct pvr2_hdw *);
+
+/* Set / clear 8051's reset bit */
+void pvr2_hdw_cpureset_assert(struct pvr2_hdw *,int);
+
+/* Execute a USB-commanded device reset */
+void pvr2_hdw_device_reset(struct pvr2_hdw *);
+
+/* Execute hard reset command (after this point it's likely that the
+ encoder will have to be reconfigured). This also clears the "useless"
+ state. */
+int pvr2_hdw_cmd_deep_reset(struct pvr2_hdw *);
+
+/* Execute simple reset command */
+int pvr2_hdw_cmd_powerup(struct pvr2_hdw *);
+
+/* Order decoder to reset */
+int pvr2_hdw_cmd_decoder_reset(struct pvr2_hdw *);
+
+/* Stop / start video stream transport */
+int pvr2_hdw_cmd_usbstream(struct pvr2_hdw *hdw,int runFl);
+
+/* Find I2C address of eeprom */
+int pvr2_hdw_get_eeprom_addr(struct pvr2_hdw *);
+
+/* Direct manipulation of GPIO bits */
+int pvr2_hdw_gpio_get_dir(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_out(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_get_in(struct pvr2_hdw *hdw,u32 *);
+int pvr2_hdw_gpio_chg_dir(struct pvr2_hdw *hdw,u32 msk,u32 val);
+int pvr2_hdw_gpio_chg_out(struct pvr2_hdw *hdw,u32 msk,u32 val);
+
+/* This data structure is specifically for the next function... */
+struct pvr2_hdw_debug_info {
+ int big_lock_held;
+ int ctl_lock_held;
+ int flag_ok;
+ int flag_disconnected;
+ int flag_init_ok;
+ int flag_streaming_enabled;
+ unsigned long subsys_flags;
+ int cmd_debug_state;
+ int cmd_debug_write_len;
+ int cmd_debug_read_len;
+ int cmd_debug_write_pend;
+ int cmd_debug_read_pend;
+ int cmd_debug_timeout;
+ int cmd_debug_rstatus;
+ int cmd_debug_wstatus;
+ unsigned char cmd_code;
+};
+
+/* Non-intrusively retrieve internal state info - this is useful for
+ diagnosing lockups. Note that this operation is completed without any
+ kind of locking and so it is not atomic and may yield inconsistent
+ results. This is *purely* a debugging aid. */
+void pvr2_hdw_get_debug_info(const struct pvr2_hdw *hdw,
+ struct pvr2_hdw_debug_info *);
+
+/* Cause modules to log their state once */
+void pvr2_hdw_trigger_module_log(struct pvr2_hdw *hdw);
+
+/* Cause encoder firmware to be uploaded into the device. This is normally
+ done autonomously, but the interface is exported here because it is also
+ a debugging aid. */
+int pvr2_upload_firmware2(struct pvr2_hdw *hdw);
+
+/* List of device types that we can match */
+extern struct usb_device_id pvr2_device_table[];
+
+#endif /* __PVRUSB2_HDW_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
new file mode 100644
index 000000000000..1dd4f6249b99
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
@@ -0,0 +1,115 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-i2c-core.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-i2c-cmd-v4l2.h"
+#include "pvrusb2-audio.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-demod.h"
+#include "pvrusb2-video-v4l.h"
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+#include "pvrusb2-cx2584x-v4l.h"
+#include "pvrusb2-wm8775.h"
+#endif
+
+#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
+
+#define OP_STANDARD 0
+#define OP_BCSH 1
+#define OP_VOLUME 2
+#define OP_FREQ 3
+#define OP_AUDIORATE 4
+#define OP_SIZE 5
+#define OP_LOG 6
+
+static const struct pvr2_i2c_op * const ops[] = {
+ [OP_STANDARD] = &pvr2_i2c_op_v4l2_standard,
+ [OP_BCSH] = &pvr2_i2c_op_v4l2_bcsh,
+ [OP_VOLUME] = &pvr2_i2c_op_v4l2_volume,
+ [OP_FREQ] = &pvr2_i2c_op_v4l2_frequency,
+ [OP_SIZE] = &pvr2_i2c_op_v4l2_size,
+ [OP_LOG] = &pvr2_i2c_op_v4l2_log,
+};
+
+void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+{
+ int id;
+ id = cp->client->driver->id;
+ cp->ctl_mask = ((1 << OP_STANDARD) |
+ (1 << OP_BCSH) |
+ (1 << OP_VOLUME) |
+ (1 << OP_FREQ) |
+ (1 << OP_SIZE) |
+ (1 << OP_LOG));
+
+ if (id == I2C_DRIVERID_MSP3400) {
+ if (pvr2_i2c_msp3400_setup(hdw,cp)) {
+ return;
+ }
+ }
+ if (id == I2C_DRIVERID_TUNER) {
+ if (pvr2_i2c_tuner_setup(hdw,cp)) {
+ return;
+ }
+ }
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ if (id == I2C_DRIVERID_CX25840) {
+ if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) {
+ return;
+ }
+ }
+ if (id == I2C_DRIVERID_WM8775) {
+ if (pvr2_i2c_wm8775_setup(hdw,cp)) {
+ return;
+ }
+ }
+#endif
+ if (id == I2C_DRIVERID_SAA711X) {
+ if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) {
+ return;
+ }
+ }
+ if (id == I2C_DRIVERID_TDA9887) {
+ if (pvr2_i2c_demod_setup(hdw,cp)) {
+ return;
+ }
+ }
+}
+
+
+const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx)
+{
+ if (idx >= sizeof(ops)/sizeof(ops[0])) return 0;
+ return ops[idx];
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
new file mode 100644
index 000000000000..9f81aff2b38a
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
@@ -0,0 +1,232 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 "pvrusb2-i2c-cmd-v4l2.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+
+
+static void set_standard(struct pvr2_hdw *hdw)
+{
+ v4l2_std_id vs;
+ vs = hdw->std_mask_cur;
+ pvr2_trace(PVR2_TRACE_CHIPS,
+ "i2c v4l2 set_standard(0x%llx)",(__u64)vs);
+
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
+}
+
+
+static int check_standard(struct pvr2_hdw *hdw)
+{
+ return hdw->std_dirty != 0;
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard = {
+ .check = check_standard,
+ .update = set_standard,
+ .name = "v4l2_standard",
+};
+
+
+static void set_bcsh(struct pvr2_hdw *hdw)
+{
+ struct v4l2_control ctrl;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_bcsh"
+ " b=%d c=%d s=%d h=%d",
+ hdw->brightness_val,hdw->contrast_val,
+ hdw->saturation_val,hdw->hue_val);
+ memset(&ctrl,0,sizeof(ctrl));
+ ctrl.id = V4L2_CID_BRIGHTNESS;
+ ctrl.value = hdw->brightness_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_CONTRAST;
+ ctrl.value = hdw->contrast_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_SATURATION;
+ ctrl.value = hdw->saturation_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_HUE;
+ ctrl.value = hdw->hue_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+}
+
+
+static int check_bcsh(struct pvr2_hdw *hdw)
+{
+ return (hdw->brightness_dirty ||
+ hdw->contrast_dirty ||
+ hdw->saturation_dirty ||
+ hdw->hue_dirty);
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh = {
+ .check = check_bcsh,
+ .update = set_bcsh,
+ .name = "v4l2_bcsh",
+};
+
+
+static void set_volume(struct pvr2_hdw *hdw)
+{
+ struct v4l2_control ctrl;
+ pvr2_trace(PVR2_TRACE_CHIPS,
+ "i2c v4l2 set_volume"
+ "(vol=%d bal=%d bas=%d treb=%d mute=%d)",
+ hdw->volume_val,
+ hdw->balance_val,
+ hdw->bass_val,
+ hdw->treble_val,
+ hdw->mute_val);
+ memset(&ctrl,0,sizeof(ctrl));
+ ctrl.id = V4L2_CID_AUDIO_MUTE;
+ ctrl.value = hdw->mute_val ? 1 : 0;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_AUDIO_VOLUME;
+ ctrl.value = hdw->volume_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_AUDIO_BALANCE;
+ ctrl.value = hdw->balance_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_AUDIO_BASS;
+ ctrl.value = hdw->bass_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+ ctrl.id = V4L2_CID_AUDIO_TREBLE;
+ ctrl.value = hdw->treble_val;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_CTRL,&ctrl);
+}
+
+
+static int check_volume(struct pvr2_hdw *hdw)
+{
+ return (hdw->volume_dirty ||
+ hdw->balance_dirty ||
+ hdw->bass_dirty ||
+ hdw->treble_dirty ||
+ hdw->mute_dirty);
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume = {
+ .check = check_volume,
+ .update = set_volume,
+ .name = "v4l2_volume",
+};
+
+
+static void set_frequency(struct pvr2_hdw *hdw)
+{
+ unsigned long fv;
+ struct v4l2_frequency freq;
+ fv = hdw->freqVal;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_freq(%lu)",fv);
+ memset(&freq,0,sizeof(freq));
+ freq.frequency = fv / 62500;
+ freq.tuner = 0;
+ freq.type = V4L2_TUNER_ANALOG_TV;
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_FREQUENCY,&freq);
+}
+
+
+static int check_frequency(struct pvr2_hdw *hdw)
+{
+ return hdw->freqDirty != 0;
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency = {
+ .check = check_frequency,
+ .update = set_frequency,
+ .name = "v4l2_freq",
+};
+
+
+static void set_size(struct pvr2_hdw *hdw)
+{
+ struct v4l2_format fmt;
+
+ memset(&fmt,0,sizeof(fmt));
+
+ fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt.fmt.pix.width = hdw->res_hor_val;
+ fmt.fmt.pix.height = hdw->res_ver_val;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_size(%dx%d)",
+ fmt.fmt.pix.width,fmt.fmt.pix.height);
+
+ pvr2_i2c_core_cmd(hdw,VIDIOC_S_FMT,&fmt);
+}
+
+
+static int check_size(struct pvr2_hdw *hdw)
+{
+ return (hdw->res_hor_dirty || hdw->res_ver_dirty);
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size = {
+ .check = check_size,
+ .update = set_size,
+ .name = "v4l2_size",
+};
+
+
+static void do_log(struct pvr2_hdw *hdw)
+{
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 do_log()");
+ pvr2_i2c_core_cmd(hdw,VIDIOC_LOG_STATUS,0);
+
+}
+
+
+static int check_log(struct pvr2_hdw *hdw)
+{
+ return hdw->log_requested != 0;
+}
+
+
+const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log = {
+ .check = check_log,
+ .update = do_log,
+ .name = "v4l2_log",
+};
+
+
+void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *cp,int fl)
+{
+ pvr2_i2c_client_cmd(cp,
+ (fl ? VIDIOC_STREAMON : VIDIOC_STREAMOFF),0);
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
new file mode 100644
index 000000000000..ecabddba1ec5
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_CMD_V4L2_H
+#define __PVRUSB2_CMD_V4L2_H
+
+#include "pvrusb2-i2c-core.h"
+
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_standard;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_bcsh;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_volume;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_frequency;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_size;
+extern const struct pvr2_i2c_op pvr2_i2c_op_v4l2_log;
+
+void pvr2_v4l2_cmd_stream(struct pvr2_i2c_client *,int);
+
+#endif /* __PVRUSB2_CMD_V4L2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
new file mode 100644
index 000000000000..c8d0bdee3ff1
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -0,0 +1,937 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-i2c-core.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+
+#define trace_i2c(...) pvr2_trace(PVR2_TRACE_I2C,__VA_ARGS__)
+
+/*
+
+ This module attempts to implement a compliant I2C adapter for the pvrusb2
+ device. By doing this we can then make use of existing functionality in
+ V4L (e.g. tuner.c) rather than rolling our own.
+
+*/
+
+static unsigned int i2c_scan = 0;
+module_param(i2c_scan, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+
+static int pvr2_i2c_write(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 length) /* Size of data to write */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+
+ if (!data) length = 0;
+ if (length > (sizeof(hdw->cmd_buffer) - 3)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C write to %u that is too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ length,(unsigned int)(sizeof(hdw->cmd_buffer) - 3));
+ return -ENOTSUPP;
+ }
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write */
+ hdw->cmd_buffer[0] = 0x08; /* write prefix */
+ hdw->cmd_buffer[1] = i2c_addr; /* i2c addr of chip */
+ hdw->cmd_buffer[2] = length; /* length of what follows */
+ if (length) memcpy(hdw->cmd_buffer + 3, data, length);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ length + 3,
+ hdw->cmd_buffer,
+ 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_write[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+static int pvr2_i2c_read(struct pvr2_hdw *hdw, /* Context */
+ u8 i2c_addr, /* I2C address we're talking to */
+ u8 *data, /* Data to write */
+ u16 dlen, /* Size of data to write */
+ u8 *res, /* Where to put data we read */
+ u16 rlen) /* Amount of data to read */
+{
+ /* Return value - default 0 means success */
+ int ret;
+
+
+ if (!data) dlen = 0;
+ if (dlen > (sizeof(hdw->cmd_buffer) - 4)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C read to %u that has wlen too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ dlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 4));
+ return -ENOTSUPP;
+ }
+ if (res && (rlen > (sizeof(hdw->cmd_buffer) - 1))) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Killing an I2C read to %u that has rlen too large"
+ " (desired=%u limit=%u)",
+ i2c_addr,
+ rlen,(unsigned int)(sizeof(hdw->cmd_buffer) - 1));
+ return -ENOTSUPP;
+ }
+
+ LOCK_TAKE(hdw->ctl_lock);
+
+ /* Clear the command buffer (likely to be paranoia) */
+ memset(hdw->cmd_buffer, 0, sizeof(hdw->cmd_buffer));
+
+ /* Set up command buffer for an I2C write followed by a read */
+ hdw->cmd_buffer[0] = 0x09; /* read prefix */
+ hdw->cmd_buffer[1] = dlen; /* arg length */
+ hdw->cmd_buffer[2] = rlen; /* answer length. Device will send one
+ more byte (status). */
+ hdw->cmd_buffer[3] = i2c_addr; /* i2c addr of chip */
+ if (dlen) memcpy(hdw->cmd_buffer + 4, data, dlen);
+
+ /* Do the operation */
+ ret = pvr2_send_request(hdw,
+ hdw->cmd_buffer,
+ 4 + dlen,
+ hdw->cmd_buffer,
+ rlen + 1);
+ if (!ret) {
+ if (hdw->cmd_buffer[0] != 8) {
+ ret = -EIO;
+ if (hdw->cmd_buffer[0] != 7) {
+ trace_i2c("unexpected status"
+ " from i2_read[%d]: %d",
+ i2c_addr,hdw->cmd_buffer[0]);
+ }
+ }
+ }
+
+ /* Copy back the result */
+ if (res && rlen) {
+ if (ret) {
+ /* Error, just blank out the return buffer */
+ memset(res, 0, rlen);
+ } else {
+ memcpy(res, hdw->cmd_buffer + 1, rlen);
+ }
+ }
+
+ LOCK_GIVE(hdw->ctl_lock);
+
+ return ret;
+}
+
+/* This is the common low level entry point for doing I2C operations to the
+ hardware. */
+int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
+ u8 i2c_addr,
+ u8 *wdata,
+ u16 wlen,
+ u8 *rdata,
+ u16 rlen)
+{
+ if (!rdata) rlen = 0;
+ if (!wdata) wlen = 0;
+ if (rlen || !wlen) {
+ return pvr2_i2c_read(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+ } else {
+ return pvr2_i2c_write(hdw,i2c_addr,wdata,wlen);
+ }
+}
+
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+
+/* This is a special entry point that is entered if an I2C operation is
+ attempted to a wm8775 chip on model 24xxx hardware. Autodetect of this
+ part doesn't work, but we know it is really there. So let's look for
+ the autodetect attempt and just return success if we see that. */
+static int i2c_hack_wm8775(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ if (!(rlen || wlen)) {
+ // This is a probe attempt. Just let it succeed.
+ return 0;
+ }
+ return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+}
+
+/* This is a special entry point that is entered if an I2C operation is
+ attempted to a cx25840 chip on model 24xxx hardware. This chip can
+ sometimes wedge itself. Worse still, when this happens msp3400 can
+ falsely detect this part and then the system gets hosed up after msp3400
+ gets confused and dies. What we want to do here is try to keep msp3400
+ away and also try to notice if the chip is wedged and send a warning to
+ the system log. */
+static int i2c_hack_cx25840(struct pvr2_hdw *hdw,
+ u8 i2c_addr,u8 *wdata,u16 wlen,u8 *rdata,u16 rlen)
+{
+ int ret;
+ unsigned int subaddr;
+ u8 wbuf[2];
+ int state = hdw->i2c_cx25840_hack_state;
+
+ if (!(rlen || wlen)) {
+ // Probe attempt - always just succeed and don't bother the
+ // hardware (this helps to make the state machine further
+ // down somewhat easier).
+ return 0;
+ }
+
+ if (state == 3) {
+ return pvr2_i2c_basic_op(hdw,i2c_addr,wdata,wlen,rdata,rlen);
+ }
+
+ /* We're looking for the exact pattern where the revision register
+ is being read. The cx25840 module will always look at the
+ revision register first. Any other pattern of access therefore
+ has to be a probe attempt from somebody else so we'll reject it.
+ Normally we could just let each client just probe the part
+ anyway, but when the cx25840 is wedged, msp3400 will get a false
+ positive and that just screws things up... */
+
+ if (wlen == 0) {
+ switch (state) {
+ case 1: subaddr = 0x0100; break;
+ case 2: subaddr = 0x0101; break;
+ default: goto fail;
+ }
+ } else if (wlen == 2) {
+ subaddr = (wdata[0] << 8) | wdata[1];
+ switch (subaddr) {
+ case 0x0100: state = 1; break;
+ case 0x0101: state = 2; break;
+ default: goto fail;
+ }
+ } else {
+ goto fail;
+ }
+ if (!rlen) goto success;
+ state = 0;
+ if (rlen != 1) goto fail;
+
+ /* If we get to here then we have a legitimate read for one of the
+ two revision bytes, so pass it through. */
+ wbuf[0] = subaddr >> 8;
+ wbuf[1] = subaddr;
+ ret = pvr2_i2c_basic_op(hdw,i2c_addr,wbuf,2,rdata,rlen);
+
+ if ((ret != 0) || (*rdata == 0x04) || (*rdata == 0x0a)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Detected a wedged cx25840 chip;"
+ " the device will not work.");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Try power cycling the pvrusb2 device.");
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "WARNING: Disabling further access to the device"
+ " to prevent other foul-ups.");
+ // This blocks all further communication with the part.
+ hdw->i2c_func[0x44] = 0;
+ pvr2_hdw_render_useless(hdw);
+ goto fail;
+ }
+
+ /* Success! */
+ pvr2_trace(PVR2_TRACE_CHIPS,"cx25840 appears to be OK.");
+ state = 3;
+
+ success:
+ hdw->i2c_cx25840_hack_state = state;
+ return 0;
+
+ fail:
+ hdw->i2c_cx25840_hack_state = state;
+ return -EIO;
+}
+
+#endif /* CONFIG_VIDEO_PVRUSB2_24XXX */
+
+/* This is a very, very limited I2C adapter implementation. We can only
+ support what we actually know will work on the device... */
+static int pvr2_i2c_xfer(struct i2c_adapter *i2c_adap,
+ struct i2c_msg msgs[],
+ int num)
+{
+ int ret = -ENOTSUPP;
+ pvr2_i2c_func funcp = 0;
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(i2c_adap->algo_data);
+
+ if (!num) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if ((msgs[0].flags & I2C_M_NOSTART)) {
+ trace_i2c("i2c refusing I2C_M_NOSTART");
+ goto done;
+ }
+ if (msgs[0].addr < PVR2_I2C_FUNC_CNT) {
+ funcp = hdw->i2c_func[msgs[0].addr];
+ }
+ if (!funcp) {
+ ret = -EIO;
+ goto done;
+ }
+
+ if (num == 1) {
+ if (msgs[0].flags & I2C_M_RD) {
+ /* Simple read */
+ u16 tcnt,bcnt,offs;
+ if (!msgs[0].len) {
+ /* Length == 0 read. This is a probe. */
+ if (funcp(hdw,msgs[0].addr,0,0,0,0)) {
+ ret = -EIO;
+ goto done;
+ }
+ ret = 1;
+ goto done;
+ }
+ /* If the read is short enough we'll do the whole
+ thing atomically. Otherwise we have no choice
+ but to break apart the reads. */
+ tcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (funcp(hdw,msgs[0].addr,0,0,
+ msgs[0].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ }
+ ret = 1;
+ goto done;
+ } else {
+ /* Simple write */
+ ret = 1;
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,msgs[0].len,0,0)) {
+ ret = -EIO;
+ }
+ goto done;
+ }
+ } else if (num == 2) {
+ if (msgs[0].addr != msgs[1].addr) {
+ trace_i2c("i2c refusing 2 phase transfer with"
+ " conflicting target addresses");
+ ret = -ENOTSUPP;
+ goto done;
+ }
+ if ((!((msgs[0].flags & I2C_M_RD))) &&
+ (msgs[1].flags & I2C_M_RD)) {
+ u16 tcnt,bcnt,wcnt,offs;
+ /* Write followed by atomic read. If the read
+ portion is short enough we'll do the whole thing
+ atomically. Otherwise we have no choice but to
+ break apart the reads. */
+ tcnt = msgs[1].len;
+ wcnt = msgs[0].len;
+ offs = 0;
+ while (tcnt || wcnt) {
+ bcnt = tcnt;
+ if (bcnt > sizeof(hdw->cmd_buffer)-1) {
+ bcnt = sizeof(hdw->cmd_buffer)-1;
+ }
+ if (funcp(hdw,msgs[0].addr,
+ msgs[0].buf,wcnt,
+ msgs[1].buf+offs,bcnt)) {
+ ret = -EIO;
+ goto done;
+ }
+ offs += bcnt;
+ tcnt -= bcnt;
+ wcnt = 0;
+ }
+ ret = 2;
+ goto done;
+ } else {
+ trace_i2c("i2c refusing complex transfer"
+ " read0=%d read1=%d",
+ (msgs[0].flags & I2C_M_RD),
+ (msgs[1].flags & I2C_M_RD));
+ }
+ } else {
+ trace_i2c("i2c refusing %d phase transfer",num);
+ }
+
+ done:
+ if (pvrusb2_debug & PVR2_TRACE_I2C_TRAF) {
+ unsigned int idx,offs,cnt;
+ for (idx = 0; idx < num; idx++) {
+ cnt = msgs[idx].len;
+ printk(KERN_INFO
+ "pvrusb2 i2c xfer %u/%u:"
+ " addr=0x%x len=%d %s%s",
+ idx+1,num,
+ msgs[idx].addr,
+ cnt,
+ (msgs[idx].flags & I2C_M_RD ?
+ "read" : "write"),
+ (msgs[idx].flags & I2C_M_NOSTART ?
+ " nostart" : ""));
+ if ((ret > 0) || !(msgs[idx].flags & I2C_M_RD)) {
+ if (cnt > 8) cnt = 8;
+ printk(" [");
+ for (offs = 0; offs < (cnt>8?8:cnt); offs++) {
+ if (offs) printk(" ");
+ printk("%02x",msgs[idx].buf[offs]);
+ }
+ if (offs < cnt) printk(" ...");
+ printk("]");
+ }
+ if (idx+1 == num) {
+ printk(" result=%d",ret);
+ }
+ printk("\n");
+ }
+ if (!num) {
+ printk(KERN_INFO
+ "pvrusb2 i2c xfer null transfer result=%d\n",
+ ret);
+ }
+ }
+ return ret;
+}
+
+static int pvr2_i2c_control(struct i2c_adapter *adapter,
+ unsigned int cmd, unsigned long arg)
+{
+ return 0;
+}
+
+static u32 pvr2_i2c_functionality(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_SMBUS_EMUL | I2C_FUNC_I2C | I2C_FUNC_SMBUS_BYTE_DATA;
+}
+
+static int pvr2_i2c_core_singleton(struct i2c_client *cp,
+ unsigned int cmd,void *arg)
+{
+ int stat;
+ if (!cp) return -EINVAL;
+ if (!(cp->driver)) return -EINVAL;
+ if (!(cp->driver->command)) return -EINVAL;
+ if (!try_module_get(cp->driver->driver.owner)) return -EAGAIN;
+ stat = cp->driver->command(cp,cmd,arg);
+ module_put(cp->driver->driver.owner);
+ return stat;
+}
+
+int pvr2_i2c_client_cmd(struct pvr2_i2c_client *cp,unsigned int cmd,void *arg)
+{
+ int stat;
+ if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
+ char buf[100];
+ unsigned int cnt;
+ cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
+ buf,sizeof(buf));
+ pvr2_trace(PVR2_TRACE_I2C_CMD,
+ "i2c COMMAND (code=%u 0x%x) to %.*s",
+ cmd,cmd,cnt,buf);
+ }
+ stat = pvr2_i2c_core_singleton(cp->client,cmd,arg);
+ if (pvrusb2_debug & PVR2_TRACE_I2C_CMD) {
+ char buf[100];
+ unsigned int cnt;
+ cnt = pvr2_i2c_client_describe(cp,PVR2_I2C_DETAIL_DEBUG,
+ buf,sizeof(buf));
+ pvr2_trace(PVR2_TRACE_I2C_CMD,
+ "i2c COMMAND to %.*s (ret=%d)",cnt,buf,stat);
+ }
+ return stat;
+}
+
+int pvr2_i2c_core_cmd(struct pvr2_hdw *hdw,unsigned int cmd,void *arg)
+{
+ struct list_head *item,*nc;
+ struct pvr2_i2c_client *cp;
+ int stat = -EINVAL;
+
+ if (!hdw) return stat;
+
+ mutex_lock(&hdw->i2c_list_lock);
+ list_for_each_safe(item,nc,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,list);
+ if (!cp->recv_enable) continue;
+ mutex_unlock(&hdw->i2c_list_lock);
+ stat = pvr2_i2c_client_cmd(cp,cmd,arg);
+ mutex_lock(&hdw->i2c_list_lock);
+ }
+ mutex_unlock(&hdw->i2c_list_lock);
+ return stat;
+}
+
+
+static int handler_check(struct pvr2_i2c_client *cp)
+{
+ struct pvr2_i2c_handler *hp = cp->handler;
+ if (!hp) return 0;
+ if (!hp->func_table->check) return 0;
+ return hp->func_table->check(hp->func_data) != 0;
+}
+
+#define BUFSIZE 500
+
+void pvr2_i2c_core_sync(struct pvr2_hdw *hdw)
+{
+ unsigned long msk;
+ unsigned int idx;
+ struct list_head *item,*nc;
+ struct pvr2_i2c_client *cp;
+
+ if (!hdw->i2c_linked) return;
+ if (!(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL)) {
+ return;
+ }
+ mutex_lock(&hdw->i2c_list_lock); do {
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync BEGIN");
+ if (hdw->i2c_pend_types & PVR2_I2C_PEND_DETECT) {
+ /* One or more I2C clients have attached since we
+ last synced. So scan the list and identify the
+ new clients. */
+ char *buf;
+ unsigned int cnt;
+ unsigned long amask = 0;
+ buf = kmalloc(BUFSIZE,GFP_KERNEL);
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_DETECT");
+ hdw->i2c_pend_types &= ~PVR2_I2C_PEND_DETECT;
+ list_for_each(item,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,
+ list);
+ if (!cp->detected_flag) {
+ cp->ctl_mask = 0;
+ pvr2_i2c_probe(hdw,cp);
+ cp->detected_flag = !0;
+ msk = cp->ctl_mask;
+ cnt = 0;
+ if (buf) {
+ cnt = pvr2_i2c_client_describe(
+ cp,
+ PVR2_I2C_DETAIL_ALL,
+ buf,BUFSIZE);
+ }
+ trace_i2c("Probed: %.*s",cnt,buf);
+ if (handler_check(cp)) {
+ hdw->i2c_pend_types |=
+ PVR2_I2C_PEND_CLIENT;
+ }
+ cp->pend_mask = msk;
+ hdw->i2c_pend_mask |= msk;
+ hdw->i2c_pend_types |=
+ PVR2_I2C_PEND_REFRESH;
+ }
+ amask |= cp->ctl_mask;
+ }
+ hdw->i2c_active_mask = amask;
+ if (buf) kfree(buf);
+ }
+ if (hdw->i2c_pend_types & PVR2_I2C_PEND_STALE) {
+ /* Need to do one or more global updates. Arrange
+ for this to happen. */
+ unsigned long m2;
+ pvr2_trace(PVR2_TRACE_I2C_CORE,
+ "i2c: PEND_STALE (0x%lx)",
+ hdw->i2c_stale_mask);
+ hdw->i2c_pend_types &= ~PVR2_I2C_PEND_STALE;
+ list_for_each(item,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,
+ list);
+ m2 = hdw->i2c_stale_mask;
+ m2 &= cp->ctl_mask;
+ m2 &= ~cp->pend_mask;
+ if (m2) {
+ pvr2_trace(PVR2_TRACE_I2C_CORE,
+ "i2c: cp=%p setting 0x%lx",
+ cp,m2);
+ cp->pend_mask |= m2;
+ }
+ }
+ hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
+ hdw->i2c_stale_mask = 0;
+ hdw->i2c_pend_types |= PVR2_I2C_PEND_REFRESH;
+ }
+ if (hdw->i2c_pend_types & PVR2_I2C_PEND_CLIENT) {
+ /* One or more client handlers are asking for an
+ update. Run through the list of known clients
+ and update each one. */
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_CLIENT");
+ hdw->i2c_pend_types &= ~PVR2_I2C_PEND_CLIENT;
+ list_for_each_safe(item,nc,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,
+ list);
+ if (!cp->handler) continue;
+ if (!cp->handler->func_table->update) continue;
+ pvr2_trace(PVR2_TRACE_I2C_CORE,
+ "i2c: cp=%p update",cp);
+ mutex_unlock(&hdw->i2c_list_lock);
+ cp->handler->func_table->update(
+ cp->handler->func_data);
+ mutex_lock(&hdw->i2c_list_lock);
+ /* If client's update function set some
+ additional pending bits, account for that
+ here. */
+ if (cp->pend_mask & ~hdw->i2c_pend_mask) {
+ hdw->i2c_pend_mask |= cp->pend_mask;
+ hdw->i2c_pend_types |=
+ PVR2_I2C_PEND_REFRESH;
+ }
+ }
+ }
+ if (hdw->i2c_pend_types & PVR2_I2C_PEND_REFRESH) {
+ const struct pvr2_i2c_op *opf;
+ unsigned long pm;
+ /* Some actual updates are pending. Walk through
+ each update type and perform it. */
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: PEND_REFRESH"
+ " (0x%lx)",hdw->i2c_pend_mask);
+ hdw->i2c_pend_types &= ~PVR2_I2C_PEND_REFRESH;
+ pm = hdw->i2c_pend_mask;
+ hdw->i2c_pend_mask = 0;
+ for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
+ if (!(pm & msk)) continue;
+ pm &= ~msk;
+ list_for_each(item,&hdw->i2c_clients) {
+ cp = list_entry(item,
+ struct pvr2_i2c_client,
+ list);
+ if (cp->pend_mask & msk) {
+ cp->pend_mask &= ~msk;
+ cp->recv_enable = !0;
+ } else {
+ cp->recv_enable = 0;
+ }
+ }
+ opf = pvr2_i2c_get_op(idx);
+ if (!opf) continue;
+ mutex_unlock(&hdw->i2c_list_lock);
+ opf->update(hdw);
+ mutex_lock(&hdw->i2c_list_lock);
+ }
+ }
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"i2c: core_sync END");
+ } while (0); mutex_unlock(&hdw->i2c_list_lock);
+}
+
+int pvr2_i2c_core_check_stale(struct pvr2_hdw *hdw)
+{
+ unsigned long msk,sm,pm;
+ unsigned int idx;
+ const struct pvr2_i2c_op *opf;
+ struct list_head *item;
+ struct pvr2_i2c_client *cp;
+ unsigned int pt = 0;
+
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale BEGIN");
+
+ pm = hdw->i2c_active_mask;
+ sm = 0;
+ for (idx = 0, msk = 1; pm; idx++, msk <<= 1) {
+ if (!(msk & pm)) continue;
+ pm &= ~msk;
+ opf = pvr2_i2c_get_op(idx);
+ if (!opf) continue;
+ if (opf->check(hdw)) {
+ sm |= msk;
+ }
+ }
+ if (sm) pt |= PVR2_I2C_PEND_STALE;
+
+ list_for_each(item,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,list);
+ if (!handler_check(cp)) continue;
+ pt |= PVR2_I2C_PEND_CLIENT;
+ }
+
+ if (pt) {
+ mutex_lock(&hdw->i2c_list_lock); do {
+ hdw->i2c_pend_types |= pt;
+ hdw->i2c_stale_mask |= sm;
+ hdw->i2c_pend_mask |= hdw->i2c_stale_mask;
+ } while (0); mutex_unlock(&hdw->i2c_list_lock);
+ }
+
+ pvr2_trace(PVR2_TRACE_I2C_CORE,
+ "i2c: types=0x%x stale=0x%lx pend=0x%lx",
+ hdw->i2c_pend_types,
+ hdw->i2c_stale_mask,
+ hdw->i2c_pend_mask);
+ pvr2_trace(PVR2_TRACE_I2C_CORE,"pvr2_i2c_core_check_stale END");
+
+ return (hdw->i2c_pend_types & PVR2_I2C_PEND_ALL) != 0;
+}
+
+unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *cp,
+ unsigned int detail,
+ char *buf,unsigned int maxlen)
+{
+ unsigned int ccnt,bcnt;
+ int spcfl = 0;
+ const struct pvr2_i2c_op *opf;
+
+ ccnt = 0;
+ if (detail & PVR2_I2C_DETAIL_DEBUG) {
+ bcnt = scnprintf(buf,maxlen,
+ "ctxt=%p ctl_mask=0x%lx",
+ cp,cp->ctl_mask);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ spcfl = !0;
+ }
+ bcnt = scnprintf(buf,maxlen,
+ "%s%s @ 0x%x",
+ (spcfl ? " " : ""),
+ cp->client->name,
+ cp->client->addr);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ if ((detail & PVR2_I2C_DETAIL_HANDLER) &&
+ cp->handler && cp->handler->func_table->describe) {
+ bcnt = scnprintf(buf,maxlen," (");
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ bcnt = cp->handler->func_table->describe(
+ cp->handler->func_data,buf,maxlen);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ bcnt = scnprintf(buf,maxlen,")");
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ }
+ if ((detail & PVR2_I2C_DETAIL_CTLMASK) && cp->ctl_mask) {
+ unsigned int idx;
+ unsigned long msk,sm;
+ int spcfl;
+ bcnt = scnprintf(buf,maxlen," [");
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ sm = 0;
+ spcfl = 0;
+ for (idx = 0, msk = 1; msk; idx++, msk <<= 1) {
+ if (!(cp->ctl_mask & msk)) continue;
+ opf = pvr2_i2c_get_op(idx);
+ if (opf) {
+ bcnt = scnprintf(buf,maxlen,"%s%s",
+ spcfl ? " " : "",
+ opf->name);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ spcfl = !0;
+ } else {
+ sm |= msk;
+ }
+ }
+ if (sm) {
+ bcnt = scnprintf(buf,maxlen,"%s%lx",
+ idx != 0 ? " " : "",sm);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ }
+ bcnt = scnprintf(buf,maxlen,"]");
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ }
+ return ccnt;
+}
+
+unsigned int pvr2_i2c_report(struct pvr2_hdw *hdw,
+ char *buf,unsigned int maxlen)
+{
+ unsigned int ccnt,bcnt;
+ struct list_head *item;
+ struct pvr2_i2c_client *cp;
+ ccnt = 0;
+ mutex_lock(&hdw->i2c_list_lock); do {
+ list_for_each(item,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,list);
+ bcnt = pvr2_i2c_client_describe(
+ cp,
+ (PVR2_I2C_DETAIL_HANDLER|
+ PVR2_I2C_DETAIL_CTLMASK),
+ buf,maxlen);
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ bcnt = scnprintf(buf,maxlen,"\n");
+ ccnt += bcnt; buf += bcnt; maxlen -= bcnt;
+ }
+ } while (0); mutex_unlock(&hdw->i2c_list_lock);
+ return ccnt;
+}
+
+static int pvr2_i2c_attach_inform(struct i2c_client *client)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+ struct pvr2_i2c_client *cp;
+ int fl = !(hdw->i2c_pend_types & PVR2_I2C_PEND_ALL);
+ cp = kmalloc(sizeof(*cp),GFP_KERNEL);
+ trace_i2c("i2c_attach [client=%s @ 0x%x ctxt=%p]",
+ client->name,
+ client->addr,cp);
+ if (!cp) return -ENOMEM;
+ memset(cp,0,sizeof(*cp));
+ INIT_LIST_HEAD(&cp->list);
+ cp->client = client;
+ mutex_lock(&hdw->i2c_list_lock); do {
+ list_add_tail(&cp->list,&hdw->i2c_clients);
+ hdw->i2c_pend_types |= PVR2_I2C_PEND_DETECT;
+ } while (0); mutex_unlock(&hdw->i2c_list_lock);
+ if (fl) pvr2_hdw_poll_trigger_unlocked(hdw);
+ return 0;
+}
+
+static int pvr2_i2c_detach_inform(struct i2c_client *client)
+{
+ struct pvr2_hdw *hdw = (struct pvr2_hdw *)(client->adapter->algo_data);
+ struct pvr2_i2c_client *cp;
+ struct list_head *item,*nc;
+ unsigned long amask = 0;
+ int foundfl = 0;
+ mutex_lock(&hdw->i2c_list_lock); do {
+ list_for_each_safe(item,nc,&hdw->i2c_clients) {
+ cp = list_entry(item,struct pvr2_i2c_client,list);
+ if (cp->client == client) {
+ trace_i2c("pvr2_i2c_detach"
+ " [client=%s @ 0x%x ctxt=%p]",
+ client->name,
+ client->addr,cp);
+ if (cp->handler &&
+ cp->handler->func_table->detach) {
+ cp->handler->func_table->detach(
+ cp->handler->func_data);
+ }
+ list_del(&cp->list);
+ kfree(cp);
+ foundfl = !0;
+ continue;
+ }
+ amask |= cp->ctl_mask;
+ }
+ hdw->i2c_active_mask = amask;
+ } while (0); mutex_unlock(&hdw->i2c_list_lock);
+ if (!foundfl) {
+ trace_i2c("pvr2_i2c_detach [client=%s @ 0x%x ctxt=<unknown>]",
+ client->name,
+ client->addr);
+ }
+ return 0;
+}
+
+static struct i2c_algorithm pvr2_i2c_algo_template = {
+ .master_xfer = pvr2_i2c_xfer,
+ .algo_control = pvr2_i2c_control,
+ .functionality = pvr2_i2c_functionality,
+};
+
+static struct i2c_adapter pvr2_i2c_adap_template = {
+ .owner = THIS_MODULE,
+ .class = I2C_CLASS_TV_ANALOG,
+ .id = I2C_HW_B_BT848,
+ .client_register = pvr2_i2c_attach_inform,
+ .client_unregister = pvr2_i2c_detach_inform,
+};
+
+static void do_i2c_scan(struct pvr2_hdw *hdw)
+{
+ struct i2c_msg msg[1];
+ int i,rc;
+ msg[0].addr = 0;
+ msg[0].flags = I2C_M_RD;
+ msg[0].len = 0;
+ msg[0].buf = 0;
+ printk("%s: i2c scan beginning\n",hdw->name);
+ for (i = 0; i < 128; i++) {
+ msg[0].addr = i;
+ rc = i2c_transfer(&hdw->i2c_adap,msg,
+ sizeof(msg)/sizeof(msg[0]));
+ if (rc != 1) continue;
+ printk("%s: i2c scan: found device @ 0x%x\n",hdw->name,i);
+ }
+ printk("%s: i2c scan done.\n",hdw->name);
+}
+
+void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
+{
+ unsigned int idx;
+
+ // The default action for all possible I2C addresses is just to do
+ // the transfer normally.
+ for (idx = 0; idx < PVR2_I2C_FUNC_CNT; idx++) {
+ hdw->i2c_func[idx] = pvr2_i2c_basic_op;
+ }
+
+#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
+ // If however we're dealing with new hardware, insert some hacks in
+ // the I2C transfer stack to let things work better.
+ if (hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ hdw->i2c_func[0x1b] = i2c_hack_wm8775;
+ hdw->i2c_func[0x44] = i2c_hack_cx25840;
+ }
+#endif
+
+ // Configure the adapter and set up everything else related to it.
+ memcpy(&hdw->i2c_adap,&pvr2_i2c_adap_template,sizeof(hdw->i2c_adap));
+ memcpy(&hdw->i2c_algo,&pvr2_i2c_algo_template,sizeof(hdw->i2c_algo));
+ strlcpy(hdw->i2c_adap.name,hdw->name,sizeof(hdw->i2c_adap.name));
+ hdw->i2c_adap.algo = &hdw->i2c_algo;
+ hdw->i2c_adap.algo_data = hdw;
+ hdw->i2c_pend_mask = 0;
+ hdw->i2c_stale_mask = 0;
+ hdw->i2c_active_mask = 0;
+ INIT_LIST_HEAD(&hdw->i2c_clients);
+ mutex_init(&hdw->i2c_list_lock);
+ hdw->i2c_linked = !0;
+ i2c_add_adapter(&hdw->i2c_adap);
+ if (i2c_scan) do_i2c_scan(hdw);
+}
+
+void pvr2_i2c_core_done(struct pvr2_hdw *hdw)
+{
+ if (hdw->i2c_linked) {
+ i2c_del_adapter(&hdw->i2c_adap);
+ hdw->i2c_linked = 0;
+ }
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
new file mode 100644
index 000000000000..e8af5b0ed3ce
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.h
@@ -0,0 +1,96 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_I2C_CORE_H
+#define __PVRUSB2_I2C_CORE_H
+
+#include <linux/list.h>
+#include <linux/i2c.h>
+
+struct pvr2_hdw;
+struct pvr2_i2c_client;
+struct pvr2_i2c_handler;
+struct pvr2_i2c_handler_functions;
+struct pvr2_i2c_op;
+struct pvr2_i2c_op_functions;
+
+struct pvr2_i2c_client {
+ struct i2c_client *client;
+ struct pvr2_i2c_handler *handler;
+ struct list_head list;
+ int detected_flag;
+ int recv_enable;
+ unsigned long pend_mask;
+ unsigned long ctl_mask;
+};
+
+struct pvr2_i2c_handler {
+ void *func_data;
+ const struct pvr2_i2c_handler_functions *func_table;
+};
+
+struct pvr2_i2c_handler_functions {
+ void (*detach)(void *);
+ int (*check)(void *);
+ void (*update)(void *);
+ unsigned int (*describe)(void *,char *,unsigned int);
+};
+
+struct pvr2_i2c_op {
+ int (*check)(struct pvr2_hdw *);
+ void (*update)(struct pvr2_hdw *);
+ const char *name;
+};
+
+void pvr2_i2c_core_init(struct pvr2_hdw *);
+void pvr2_i2c_core_done(struct pvr2_hdw *);
+
+int pvr2_i2c_client_cmd(struct pvr2_i2c_client *,unsigned int cmd,void *arg);
+int pvr2_i2c_core_cmd(struct pvr2_hdw *,unsigned int cmd,void *arg);
+
+int pvr2_i2c_core_check_stale(struct pvr2_hdw *);
+void pvr2_i2c_core_sync(struct pvr2_hdw *);
+unsigned int pvr2_i2c_report(struct pvr2_hdw *,char *buf,unsigned int maxlen);
+#define PVR2_I2C_DETAIL_DEBUG 0x0001
+#define PVR2_I2C_DETAIL_HANDLER 0x0002
+#define PVR2_I2C_DETAIL_CTLMASK 0x0004
+#define PVR2_I2C_DETAIL_ALL (\
+ PVR2_I2C_DETAIL_DEBUG |\
+ PVR2_I2C_DETAIL_HANDLER |\
+ PVR2_I2C_DETAIL_CTLMASK)
+unsigned int pvr2_i2c_client_describe(struct pvr2_i2c_client *,
+ unsigned int detail_mask,
+ char *buf,unsigned int maxlen);
+
+void pvr2_i2c_probe(struct pvr2_hdw *,struct pvr2_i2c_client *);
+const struct pvr2_i2c_op *pvr2_i2c_get_op(unsigned int idx);
+
+#endif /* __PVRUSB2_I2C_CORE_H */
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.c b/drivers/media/video/pvrusb2/pvrusb2-io.c
new file mode 100644
index 000000000000..a984c91f571c
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.c
@@ -0,0 +1,695 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-io.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+
+#define BUFFER_SIG 0x47653271
+
+// #define SANITY_CHECK_BUFFERS
+
+
+#ifdef SANITY_CHECK_BUFFERS
+#define BUFFER_CHECK(bp) do { \
+ if ((bp)->signature != BUFFER_SIG) { \
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS, \
+ "Buffer %p is bad at %s:%d", \
+ (bp),__FILE__,__LINE__); \
+ pvr2_buffer_describe(bp,"BadSig"); \
+ BUG(); \
+ } \
+} while (0)
+#else
+#define BUFFER_CHECK(bp) do {} while(0)
+#endif
+
+struct pvr2_stream {
+ /* Buffers queued for reading */
+ struct list_head queued_list;
+ unsigned int q_count;
+ unsigned int q_bcount;
+ /* Buffers with retrieved data */
+ struct list_head ready_list;
+ unsigned int r_count;
+ unsigned int r_bcount;
+ /* Buffers available for use */
+ struct list_head idle_list;
+ unsigned int i_count;
+ unsigned int i_bcount;
+ /* Pointers to all buffers */
+ struct pvr2_buffer **buffers;
+ /* Array size of buffers */
+ unsigned int buffer_slot_count;
+ /* Total buffers actually in circulation */
+ unsigned int buffer_total_count;
+ /* Designed number of buffers to be in circulation */
+ unsigned int buffer_target_count;
+ /* Executed when ready list become non-empty */
+ pvr2_stream_callback callback_func;
+ void *callback_data;
+ /* Context for transfer endpoint */
+ struct usb_device *dev;
+ int endpoint;
+ /* Overhead for mutex enforcement */
+ spinlock_t list_lock;
+ struct mutex mutex;
+ /* Tracking state for tolerating errors */
+ unsigned int fail_count;
+ unsigned int fail_tolerance;
+};
+
+struct pvr2_buffer {
+ int id;
+ int signature;
+ enum pvr2_buffer_state state;
+ void *ptr; /* Pointer to storage area */
+ unsigned int max_count; /* Size of storage area */
+ unsigned int used_count; /* Amount of valid data in storage area */
+ int status; /* Transfer result status */
+ struct pvr2_stream *stream;
+ struct list_head list_overhead;
+ struct urb *purb;
+};
+
+const char *pvr2_buffer_state_decode(enum pvr2_buffer_state st)
+{
+ switch (st) {
+ case pvr2_buffer_state_none: return "none";
+ case pvr2_buffer_state_idle: return "idle";
+ case pvr2_buffer_state_queued: return "queued";
+ case pvr2_buffer_state_ready: return "ready";
+ }
+ return "unknown";
+}
+
+void pvr2_buffer_describe(struct pvr2_buffer *bp,const char *msg)
+{
+ pvr2_trace(PVR2_TRACE_INFO,
+ "buffer%s%s %p state=%s id=%d status=%d"
+ " stream=%p purb=%p sig=0x%x",
+ (msg ? " " : ""),
+ (msg ? msg : ""),
+ bp,
+ (bp ? pvr2_buffer_state_decode(bp->state) : "(invalid)"),
+ (bp ? bp->id : 0),
+ (bp ? bp->status : 0),
+ (bp ? bp->stream : 0),
+ (bp ? bp->purb : 0),
+ (bp ? bp->signature : 0));
+}
+
+static void pvr2_buffer_remove(struct pvr2_buffer *bp)
+{
+ unsigned int *cnt;
+ unsigned int *bcnt;
+ unsigned int ccnt;
+ struct pvr2_stream *sp = bp->stream;
+ switch (bp->state) {
+ case pvr2_buffer_state_idle:
+ cnt = &sp->i_count;
+ bcnt = &sp->i_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_queued:
+ cnt = &sp->q_count;
+ bcnt = &sp->q_bcount;
+ ccnt = bp->max_count;
+ break;
+ case pvr2_buffer_state_ready:
+ cnt = &sp->r_count;
+ bcnt = &sp->r_bcount;
+ ccnt = bp->used_count;
+ break;
+ default:
+ return;
+ }
+ list_del_init(&bp->list_overhead);
+ (*cnt)--;
+ (*bcnt) -= ccnt;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s dec cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),*bcnt,*cnt);
+ bp->state = pvr2_buffer_state_none;
+}
+
+static void pvr2_buffer_set_none(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_none));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static int pvr2_buffer_set_ready(struct pvr2_buffer *bp)
+{
+ int fl;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_ready));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ fl = (sp->r_count == 0);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->ready_list);
+ bp->state = pvr2_buffer_state_ready;
+ (sp->r_count)++;
+ sp->r_bcount += bp->used_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->r_bcount,sp->r_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ return fl;
+}
+
+static void pvr2_buffer_set_idle(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_idle));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->idle_list);
+ bp->state = pvr2_buffer_state_idle;
+ (sp->i_count)++;
+ sp->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->i_bcount,sp->i_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_set_queued(struct pvr2_buffer *bp)
+{
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferState %p %6s --> %6s",
+ bp,
+ pvr2_buffer_state_decode(bp->state),
+ pvr2_buffer_state_decode(pvr2_buffer_state_queued));
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ pvr2_buffer_remove(bp);
+ list_add_tail(&bp->list_overhead,&sp->queued_list);
+ bp->state = pvr2_buffer_state_queued;
+ (sp->q_count)++;
+ sp->q_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/"
+ " bufferPool %8s inc cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(bp->state),
+ sp->q_bcount,sp->q_count);
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+}
+
+static void pvr2_buffer_wipe(struct pvr2_buffer *bp)
+{
+ if (bp->state == pvr2_buffer_state_queued) {
+ usb_kill_urb(bp->purb);
+ }
+}
+
+static int pvr2_buffer_init(struct pvr2_buffer *bp,
+ struct pvr2_stream *sp,
+ unsigned int id)
+{
+ memset(bp,0,sizeof(*bp));
+ bp->signature = BUFFER_SIG;
+ bp->id = id;
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ bufferInit %p stream=%p",bp,sp);
+ bp->stream = sp;
+ bp->state = pvr2_buffer_state_none;
+ INIT_LIST_HEAD(&bp->list_overhead);
+ bp->purb = usb_alloc_urb(0,GFP_KERNEL);
+ if (! bp->purb) return -ENOMEM;
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"create");
+#endif
+ return 0;
+}
+
+static void pvr2_buffer_done(struct pvr2_buffer *bp)
+{
+#ifdef SANITY_CHECK_BUFFERS
+ pvr2_buffer_describe(bp,"delete");
+#endif
+ pvr2_buffer_wipe(bp);
+ pvr2_buffer_set_none(bp);
+ bp->signature = 0;
+ bp->stream = 0;
+ if (bp->purb) usb_free_urb(bp->purb);
+ pvr2_trace(PVR2_TRACE_BUF_POOL,"/*---TRACE_FLOW---*/"
+ " bufferDone %p",bp);
+}
+
+static int pvr2_stream_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ unsigned int scnt;
+
+ /* Allocate buffers pointer array in multiples of 32 entries */
+ if (cnt == sp->buffer_total_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/ poolResize "
+ " stream=%p cur=%d adj=%+d",
+ sp,
+ sp->buffer_total_count,
+ cnt-sp->buffer_total_count);
+
+ scnt = cnt & ~0x1f;
+ if (cnt > scnt) scnt += 0x20;
+
+ if (cnt > sp->buffer_total_count) {
+ if (scnt > sp->buffer_slot_count) {
+ struct pvr2_buffer **nb;
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ if (sp->buffer_slot_count) {
+ memcpy(nb,sp->buffers,
+ sp->buffer_slot_count * sizeof(*nb));
+ kfree(sp->buffers);
+ }
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ while (sp->buffer_total_count < cnt) {
+ struct pvr2_buffer *bp;
+ bp = kmalloc(sizeof(*bp),GFP_KERNEL);
+ if (!bp) return -ENOMEM;
+ ret = pvr2_buffer_init(bp,sp,sp->buffer_total_count);
+ if (ret) {
+ kfree(bp);
+ return -ENOMEM;
+ }
+ sp->buffers[sp->buffer_total_count] = bp;
+ (sp->buffer_total_count)++;
+ pvr2_buffer_set_idle(bp);
+ }
+ } else {
+ while (sp->buffer_total_count > cnt) {
+ struct pvr2_buffer *bp;
+ bp = sp->buffers[sp->buffer_total_count - 1];
+ /* Paranoia */
+ sp->buffers[sp->buffer_total_count - 1] = 0;
+ (sp->buffer_total_count)--;
+ pvr2_buffer_done(bp);
+ kfree(bp);
+ }
+ if (scnt < sp->buffer_slot_count) {
+ struct pvr2_buffer **nb = 0;
+ if (scnt) {
+ nb = kmalloc(scnt * sizeof(*nb),GFP_KERNEL);
+ if (!nb) return -ENOMEM;
+ memcpy(nb,sp->buffers,scnt * sizeof(*nb));
+ }
+ kfree(sp->buffers);
+ sp->buffers = nb;
+ sp->buffer_slot_count = scnt;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_stream_achieve_buffer_count(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ unsigned int cnt;
+
+ if (sp->buffer_total_count == sp->buffer_target_count) return 0;
+
+ pvr2_trace(PVR2_TRACE_BUF_POOL,
+ "/*---TRACE_FLOW---*/"
+ " poolCheck stream=%p cur=%d tgt=%d",
+ sp,sp->buffer_total_count,sp->buffer_target_count);
+
+ if (sp->buffer_total_count < sp->buffer_target_count) {
+ return pvr2_stream_buffer_count(sp,sp->buffer_target_count);
+ }
+
+ cnt = 0;
+ while ((sp->buffer_total_count - cnt) > sp->buffer_target_count) {
+ bp = sp->buffers[sp->buffer_total_count - (cnt + 1)];
+ if (bp->state != pvr2_buffer_state_idle) break;
+ cnt++;
+ }
+ if (cnt) {
+ pvr2_stream_buffer_count(sp,sp->buffer_total_count - cnt);
+ }
+
+ return 0;
+}
+
+static void pvr2_stream_internal_flush(struct pvr2_stream *sp)
+{
+ struct list_head *lp;
+ struct pvr2_buffer *bp1;
+ while ((lp = sp->queued_list.next) != &sp->queued_list) {
+ bp1 = list_entry(lp,struct pvr2_buffer,list_overhead);
+ pvr2_buffer_wipe(bp1);
+ /* At this point, we should be guaranteed that no
+ completion callback may happen on this buffer. But it's
+ possible that it might have completed after we noticed
+ it but before we wiped it. So double check its status
+ here first. */
+ if (bp1->state != pvr2_buffer_state_queued) continue;
+ pvr2_buffer_set_idle(bp1);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+}
+
+static void pvr2_stream_init(struct pvr2_stream *sp)
+{
+ spin_lock_init(&sp->list_lock);
+ mutex_init(&sp->mutex);
+ INIT_LIST_HEAD(&sp->queued_list);
+ INIT_LIST_HEAD(&sp->ready_list);
+ INIT_LIST_HEAD(&sp->idle_list);
+}
+
+static void pvr2_stream_done(struct pvr2_stream *sp)
+{
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ pvr2_stream_buffer_count(sp,0);
+ } while (0); mutex_unlock(&sp->mutex);
+}
+
+static void buffer_complete(struct urb *urb, struct pt_regs *regs)
+{
+ struct pvr2_buffer *bp = urb->context;
+ struct pvr2_stream *sp;
+ unsigned long irq_flags;
+ BUFFER_CHECK(bp);
+ sp = bp->stream;
+ bp->used_count = 0;
+ bp->status = 0;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferComplete %p stat=%d cnt=%d",
+ bp,urb->status,urb->actual_length);
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if ((!(urb->status)) ||
+ (urb->status == -ENOENT) ||
+ (urb->status == -ECONNRESET) ||
+ (urb->status == -ESHUTDOWN)) {
+ bp->used_count = urb->actual_length;
+ if (sp->fail_count) {
+ pvr2_trace(PVR2_TRACE_TOLERANCE,
+ "stream %p transfer ok"
+ " - fail count reset",sp);
+ sp->fail_count = 0;
+ }
+ } else if (sp->fail_count < sp->fail_tolerance) {
+ // We can tolerate this error, because we're below the
+ // threshold...
+ (sp->fail_count)++;
+ pvr2_trace(PVR2_TRACE_TOLERANCE,
+ "stream %p ignoring error %d"
+ " - fail count increased to %u",
+ sp,urb->status,sp->fail_count);
+ } else {
+ bp->status = urb->status;
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ pvr2_buffer_set_ready(bp);
+ if (sp && sp->callback_func) {
+ sp->callback_func(sp->callback_data);
+ }
+}
+
+struct pvr2_stream *pvr2_stream_create(void)
+{
+ struct pvr2_stream *sp;
+ sp = kmalloc(sizeof(*sp),GFP_KERNEL);
+ if (!sp) return sp;
+ memset(sp,0,sizeof(*sp));
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_create: sp=%p",sp);
+ pvr2_stream_init(sp);
+ return sp;
+}
+
+void pvr2_stream_destroy(struct pvr2_stream *sp)
+{
+ if (!sp) return;
+ pvr2_trace(PVR2_TRACE_INIT,"pvr2_stream_destroy: sp=%p",sp);
+ pvr2_stream_done(sp);
+ kfree(sp);
+}
+
+void pvr2_stream_setup(struct pvr2_stream *sp,
+ struct usb_device *dev,
+ int endpoint,
+ unsigned int tolerance)
+{
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ sp->dev = dev;
+ sp->endpoint = endpoint;
+ sp->fail_tolerance = tolerance;
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+void pvr2_stream_set_callback(struct pvr2_stream *sp,
+ pvr2_stream_callback func,
+ void *data)
+{
+ unsigned long irq_flags;
+ mutex_lock(&sp->mutex); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ sp->callback_data = data;
+ sp->callback_func = func;
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *sp)
+{
+ return sp->buffer_target_count;
+}
+
+int pvr2_stream_set_buffer_count(struct pvr2_stream *sp,unsigned int cnt)
+{
+ int ret;
+ if (sp->buffer_target_count == cnt) return 0;
+ mutex_lock(&sp->mutex); do {
+ sp->buffer_target_count = cnt;
+ ret = pvr2_stream_achieve_buffer_count(sp);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->idle_list.next;
+ if (lp == &sp->idle_list) return 0;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *sp)
+{
+ struct list_head *lp = sp->ready_list.next;
+ if (lp == &sp->ready_list) return 0;
+ return list_entry(lp,struct pvr2_buffer,list_overhead);
+}
+
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id)
+{
+ if (id < 0) return 0;
+ if (id >= sp->buffer_total_count) return 0;
+ return sp->buffers[id];
+}
+
+int pvr2_stream_get_ready_count(struct pvr2_stream *sp)
+{
+ return sp->r_count;
+}
+
+int pvr2_stream_get_idle_count(struct pvr2_stream *sp)
+{
+ return sp->i_count;
+}
+
+void pvr2_stream_flush(struct pvr2_stream *sp)
+{
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+void pvr2_stream_kill(struct pvr2_stream *sp)
+{
+ struct pvr2_buffer *bp;
+ mutex_lock(&sp->mutex); do {
+ pvr2_stream_internal_flush(sp);
+ while ((bp = pvr2_stream_get_ready_buffer(sp)) != 0) {
+ pvr2_buffer_set_idle(bp);
+ }
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+ } while(0); mutex_unlock(&sp->mutex);
+}
+
+int pvr2_buffer_queue(struct pvr2_buffer *bp)
+{
+#undef SEED_BUFFER
+#ifdef SEED_BUFFER
+ unsigned int idx;
+ unsigned int val;
+#endif
+ int ret = 0;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ mutex_lock(&sp->mutex); do {
+ pvr2_buffer_wipe(bp);
+ if (!sp->dev) {
+ ret = -EIO;
+ break;
+ }
+ pvr2_buffer_set_queued(bp);
+#ifdef SEED_BUFFER
+ for (idx = 0; idx < (bp->max_count) / 4; idx++) {
+ val = bp->id << 24;
+ val |= idx;
+ ((unsigned int *)(bp->ptr))[idx] = val;
+ }
+#endif
+ bp->status = -EINPROGRESS;
+ usb_fill_bulk_urb(bp->purb, // struct urb *urb
+ sp->dev, // struct usb_device *dev
+ // endpoint (below)
+ usb_rcvbulkpipe(sp->dev,sp->endpoint),
+ bp->ptr, // void *transfer_buffer
+ bp->max_count, // int buffer_length
+ buffer_complete,
+ bp);
+ usb_submit_urb(bp->purb,GFP_KERNEL);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+int pvr2_buffer_idle(struct pvr2_buffer *bp)
+{
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ mutex_lock(&sp->mutex); do {
+ pvr2_buffer_wipe(bp);
+ pvr2_buffer_set_idle(bp);
+ if (sp->buffer_total_count != sp->buffer_target_count) {
+ pvr2_stream_achieve_buffer_count(sp);
+ }
+ } while(0); mutex_unlock(&sp->mutex);
+ return 0;
+}
+
+int pvr2_buffer_set_buffer(struct pvr2_buffer *bp,void *ptr,unsigned int cnt)
+{
+ int ret = 0;
+ unsigned long irq_flags;
+ struct pvr2_stream *sp;
+ if (!bp) return -EINVAL;
+ sp = bp->stream;
+ mutex_lock(&sp->mutex); do {
+ spin_lock_irqsave(&sp->list_lock,irq_flags);
+ if (bp->state != pvr2_buffer_state_idle) {
+ ret = -EPERM;
+ } else {
+ bp->ptr = ptr;
+ bp->stream->i_bcount -= bp->max_count;
+ bp->max_count = cnt;
+ bp->stream->i_bcount += bp->max_count;
+ pvr2_trace(PVR2_TRACE_BUF_FLOW,
+ "/*---TRACE_FLOW---*/ bufferPool "
+ " %8s cap cap=%07d cnt=%02d",
+ pvr2_buffer_state_decode(
+ pvr2_buffer_state_idle),
+ bp->stream->i_bcount,bp->stream->i_count);
+ }
+ spin_unlock_irqrestore(&sp->list_lock,irq_flags);
+ } while(0); mutex_unlock(&sp->mutex);
+ return ret;
+}
+
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *bp)
+{
+ return bp->used_count;
+}
+
+int pvr2_buffer_get_status(struct pvr2_buffer *bp)
+{
+ return bp->status;
+}
+
+enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *bp)
+{
+ return bp->state;
+}
+
+int pvr2_buffer_get_id(struct pvr2_buffer *bp)
+{
+ return bp->id;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-io.h b/drivers/media/video/pvrusb2/pvrusb2-io.h
new file mode 100644
index 000000000000..65e11385b2b3
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-io.h
@@ -0,0 +1,102 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_IO_H
+#define __PVRUSB2_IO_H
+
+#include <linux/usb.h>
+#include <linux/list.h>
+
+typedef void (*pvr2_stream_callback)(void *);
+
+enum pvr2_buffer_state {
+ pvr2_buffer_state_none = 0, // Not on any list
+ pvr2_buffer_state_idle = 1, // Buffer is ready to be used again
+ pvr2_buffer_state_queued = 2, // Buffer has been queued for filling
+ pvr2_buffer_state_ready = 3, // Buffer has data available
+};
+
+struct pvr2_stream;
+struct pvr2_buffer;
+
+const char *pvr2_buffer_state_decode(enum pvr2_buffer_state);
+
+/* Initialize / tear down stream structure */
+struct pvr2_stream *pvr2_stream_create(void);
+void pvr2_stream_destroy(struct pvr2_stream *);
+void pvr2_stream_setup(struct pvr2_stream *,
+ struct usb_device *dev,int endpoint,
+ unsigned int tolerance);
+void pvr2_stream_set_callback(struct pvr2_stream *,
+ pvr2_stream_callback func,
+ void *data);
+
+/* Query / set the nominal buffer count */
+int pvr2_stream_get_buffer_count(struct pvr2_stream *);
+int pvr2_stream_set_buffer_count(struct pvr2_stream *,unsigned int);
+
+/* Get a pointer to a buffer that is either idle, ready, or is specified
+ named. */
+struct pvr2_buffer *pvr2_stream_get_idle_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_ready_buffer(struct pvr2_stream *);
+struct pvr2_buffer *pvr2_stream_get_buffer(struct pvr2_stream *sp,int id);
+
+/* Find out how many buffers are idle or ready */
+int pvr2_stream_get_idle_count(struct pvr2_stream *);
+int pvr2_stream_get_ready_count(struct pvr2_stream *);
+
+/* Kill all pending operations */
+void pvr2_stream_flush(struct pvr2_stream *);
+
+/* Kill all pending buffers and throw away any ready buffers as well */
+void pvr2_stream_kill(struct pvr2_stream *);
+
+/* Set up the actual storage for a buffer */
+int pvr2_buffer_set_buffer(struct pvr2_buffer *,void *ptr,unsigned int cnt);
+
+/* Find out size of data in the given ready buffer */
+unsigned int pvr2_buffer_get_count(struct pvr2_buffer *);
+
+/* Retrieve completion code for given ready buffer */
+int pvr2_buffer_get_status(struct pvr2_buffer *);
+
+/* Retrieve state of given buffer */
+enum pvr2_buffer_state pvr2_buffer_get_state(struct pvr2_buffer *);
+
+/* Retrieve ID of given buffer */
+int pvr2_buffer_get_id(struct pvr2_buffer *);
+
+/* Start reading into given buffer (kill it if needed) */
+int pvr2_buffer_queue(struct pvr2_buffer *);
+
+/* Move buffer back to idle pool (kill it if needed) */
+int pvr2_buffer_idle(struct pvr2_buffer *);
+
+#endif /* __PVRUSB2_IO_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.c b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
new file mode 100644
index 000000000000..49da062e3271
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.c
@@ -0,0 +1,513 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-ioread.h"
+#include "pvrusb2-debug.h"
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <asm/uaccess.h>
+
+#define BUFFER_COUNT 32
+#define BUFFER_SIZE PAGE_ALIGN(0x4000)
+
+struct pvr2_ioread {
+ struct pvr2_stream *stream;
+ char *buffer_storage[BUFFER_COUNT];
+ char *sync_key_ptr;
+ unsigned int sync_key_len;
+ unsigned int sync_buf_offs;
+ unsigned int sync_state;
+ unsigned int sync_trashed_count;
+ int enabled; // Streaming is on
+ int spigot_open; // OK to pass data to client
+ int stream_running; // Passing data to client now
+
+ /* State relevant to current buffer being read */
+ struct pvr2_buffer *c_buf;
+ char *c_data_ptr;
+ unsigned int c_data_len;
+ unsigned int c_data_offs;
+ struct mutex mutex;
+};
+
+static int pvr2_ioread_init(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ cp->stream = 0;
+ mutex_init(&cp->mutex);
+
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ cp->buffer_storage[idx] = kmalloc(BUFFER_SIZE,GFP_KERNEL);
+ if (!(cp->buffer_storage[idx])) break;
+ }
+
+ if (idx < BUFFER_COUNT) {
+ // An allocation appears to have failed
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+ return -ENOMEM;
+ }
+ return 0;
+}
+
+static void pvr2_ioread_done(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+
+ pvr2_ioread_setup(cp,0);
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ if (!(cp->buffer_storage[idx])) continue;
+ kfree(cp->buffer_storage[idx]);
+ }
+}
+
+struct pvr2_ioread *pvr2_ioread_create(void)
+{
+ struct pvr2_ioread *cp;
+ cp = kmalloc(sizeof(*cp),GFP_KERNEL);
+ if (!cp) return 0;
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_create id=%p",cp);
+ memset(cp,0,sizeof(*cp));
+ if (pvr2_ioread_init(cp) < 0) {
+ kfree(cp);
+ return 0;
+ }
+ return cp;
+}
+
+void pvr2_ioread_destroy(struct pvr2_ioread *cp)
+{
+ if (!cp) return;
+ pvr2_ioread_done(cp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"pvr2_ioread_destroy id=%p",cp);
+ if (cp->sync_key_ptr) {
+ kfree(cp->sync_key_ptr);
+ cp->sync_key_ptr = 0;
+ }
+ kfree(cp);
+}
+
+void pvr2_ioread_set_sync_key(struct pvr2_ioread *cp,
+ const char *sync_key_ptr,
+ unsigned int sync_key_len)
+{
+ if (!cp) return;
+
+ if (!sync_key_ptr) sync_key_len = 0;
+ if ((sync_key_len == cp->sync_key_len) &&
+ ((!sync_key_len) ||
+ (!memcmp(sync_key_ptr,cp->sync_key_ptr,sync_key_len)))) return;
+
+ if (sync_key_len != cp->sync_key_len) {
+ if (cp->sync_key_ptr) {
+ kfree(cp->sync_key_ptr);
+ cp->sync_key_ptr = 0;
+ }
+ cp->sync_key_len = 0;
+ if (sync_key_len) {
+ cp->sync_key_ptr = kmalloc(sync_key_len,GFP_KERNEL);
+ if (cp->sync_key_ptr) {
+ cp->sync_key_len = sync_key_len;
+ }
+ }
+ }
+ if (!cp->sync_key_len) return;
+ memcpy(cp->sync_key_ptr,sync_key_ptr,cp->sync_key_len);
+}
+
+static void pvr2_ioread_stop(struct pvr2_ioread *cp)
+{
+ if (!(cp->enabled)) return;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_stop id=%p",cp);
+ pvr2_stream_kill(cp->stream);
+ cp->c_buf = 0;
+ cp->c_data_ptr = 0;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ cp->enabled = 0;
+ cp->stream_running = 0;
+ cp->spigot_open = 0;
+ if (cp->sync_state) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ sync_state <== 0");
+ cp->sync_state = 0;
+ }
+}
+
+static int pvr2_ioread_start(struct pvr2_ioread *cp)
+{
+ int stat;
+ struct pvr2_buffer *bp;
+ if (cp->enabled) return 0;
+ if (!(cp->stream)) return 0;
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/ pvr2_ioread_start id=%p",cp);
+ while ((bp = pvr2_stream_get_idle_buffer(cp->stream)) != 0) {
+ stat = pvr2_buffer_queue(bp);
+ if (stat < 0) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_start id=%p"
+ " error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ return stat;
+ }
+ }
+ cp->enabled = !0;
+ cp->c_buf = 0;
+ cp->c_data_ptr = 0;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ cp->stream_running = 0;
+ if (cp->sync_key_len) {
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ sync_state <== 1");
+ cp->sync_state = 1;
+ cp->sync_trashed_count = 0;
+ cp->sync_buf_offs = 0;
+ }
+ cp->spigot_open = 0;
+ return 0;
+}
+
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *cp)
+{
+ return cp->stream;
+}
+
+int pvr2_ioread_setup(struct pvr2_ioread *cp,struct pvr2_stream *sp)
+{
+ int ret;
+ unsigned int idx;
+ struct pvr2_buffer *bp;
+
+ mutex_lock(&cp->mutex); do {
+ if (cp->stream) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (tear-down) id=%p",cp);
+ pvr2_ioread_stop(cp);
+ pvr2_stream_kill(cp->stream);
+ pvr2_stream_set_buffer_count(cp->stream,0);
+ cp->stream = 0;
+ }
+ if (sp) {
+ pvr2_trace(PVR2_TRACE_START_STOP,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_setup (setup) id=%p",cp);
+ pvr2_stream_kill(sp);
+ ret = pvr2_stream_set_buffer_count(sp,BUFFER_COUNT);
+ if (ret < 0) return ret;
+ for (idx = 0; idx < BUFFER_COUNT; idx++) {
+ bp = pvr2_stream_get_buffer(sp,idx);
+ pvr2_buffer_set_buffer(bp,
+ cp->buffer_storage[idx],
+ BUFFER_SIZE);
+ }
+ cp->stream = sp;
+ }
+ } while (0); mutex_unlock(&cp->mutex);
+
+ return 0;
+}
+
+int pvr2_ioread_set_enabled(struct pvr2_ioread *cp,int fl)
+{
+ int ret = 0;
+ if ((!fl) == (!(cp->enabled))) return ret;
+
+ mutex_lock(&cp->mutex); do {
+ if (fl) {
+ ret = pvr2_ioread_start(cp);
+ } else {
+ pvr2_ioread_stop(cp);
+ }
+ } while (0); mutex_unlock(&cp->mutex);
+ return ret;
+}
+
+int pvr2_ioread_get_enabled(struct pvr2_ioread *cp)
+{
+ return cp->enabled != 0;
+}
+
+int pvr2_ioread_get_buffer(struct pvr2_ioread *cp)
+{
+ int stat;
+
+ while (cp->c_data_len <= cp->c_data_offs) {
+ if (cp->c_buf) {
+ // Flush out current buffer first.
+ stat = pvr2_buffer_queue(cp->c_buf);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " queue_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ return 0;
+ }
+ cp->c_buf = 0;
+ cp->c_data_ptr = 0;
+ cp->c_data_len = 0;
+ cp->c_data_offs = 0;
+ }
+ // Now get a freshly filled buffer.
+ cp->c_buf = pvr2_stream_get_ready_buffer(cp->stream);
+ if (!cp->c_buf) break; // Nothing ready; done.
+ cp->c_data_len = pvr2_buffer_get_count(cp->c_buf);
+ if (!cp->c_data_len) {
+ // Nothing transferred. Was there an error?
+ stat = pvr2_buffer_get_status(cp->c_buf);
+ if (stat < 0) {
+ // Streaming error...
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " pvr2_ioread_read id=%p"
+ " buffer_error=%d",
+ cp,stat);
+ pvr2_ioread_stop(cp);
+ // Give up.
+ return 0;
+ }
+ // Start over...
+ continue;
+ }
+ cp->c_data_offs = 0;
+ cp->c_data_ptr = cp->buffer_storage[
+ pvr2_buffer_get_id(cp->c_buf)];
+ }
+ return !0;
+}
+
+void pvr2_ioread_filter(struct pvr2_ioread *cp)
+{
+ unsigned int idx;
+ if (!cp->enabled) return;
+ if (cp->sync_state != 1) return;
+
+ // Search the stream for our synchronization key. This is made
+ // complicated by the fact that in order to be honest with
+ // ourselves here we must search across buffer boundaries...
+ mutex_lock(&cp->mutex); while (1) {
+ // Ensure we have a buffer
+ if (!pvr2_ioread_get_buffer(cp)) break;
+ if (!cp->c_data_len) break;
+
+ // Now walk the buffer contents until we match the key or
+ // run out of buffer data.
+ for (idx = cp->c_data_offs; idx < cp->c_data_len; idx++) {
+ if (cp->sync_buf_offs >= cp->sync_key_len) break;
+ if (cp->c_data_ptr[idx] ==
+ cp->sync_key_ptr[cp->sync_buf_offs]) {
+ // Found the next key byte
+ (cp->sync_buf_offs)++;
+ } else {
+ // Whoops, mismatched. Start key over...
+ cp->sync_buf_offs = 0;
+ }
+ }
+
+ // Consume what we've walked through
+ cp->c_data_offs += idx;
+ cp->sync_trashed_count += idx;
+
+ // If we've found the key, then update state and get out.
+ if (cp->sync_buf_offs >= cp->sync_key_len) {
+ cp->sync_trashed_count -= cp->sync_key_len;
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " sync_state <== 2 (skipped %u bytes)",
+ cp->sync_trashed_count);
+ cp->sync_state = 2;
+ cp->sync_buf_offs = 0;
+ break;
+ }
+
+ if (cp->c_data_offs < cp->c_data_len) {
+ // Sanity check - should NEVER get here
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ERROR: pvr2_ioread filter sync problem"
+ " len=%u offs=%u",
+ cp->c_data_len,cp->c_data_offs);
+ // Get out so we don't get stuck in an infinite
+ // loop.
+ break;
+ }
+
+ continue; // (for clarity)
+ } mutex_unlock(&cp->mutex);
+}
+
+int pvr2_ioread_avail(struct pvr2_ioread *cp)
+{
+ int ret;
+ if (!(cp->enabled)) {
+ // Stream is not enabled; so this is an I/O error
+ return -EIO;
+ }
+
+ if (cp->sync_state == 1) {
+ pvr2_ioread_filter(cp);
+ if (cp->sync_state == 1) return -EAGAIN;
+ }
+
+ ret = 0;
+ if (cp->stream_running) {
+ if (!pvr2_stream_get_ready_count(cp->stream)) {
+ // No data available at all right now.
+ ret = -EAGAIN;
+ }
+ } else {
+ if (pvr2_stream_get_ready_count(cp->stream) < BUFFER_COUNT/2) {
+ // Haven't buffered up enough yet; try again later
+ ret = -EAGAIN;
+ }
+ }
+
+ if ((!(cp->spigot_open)) != (!(ret == 0))) {
+ cp->spigot_open = (ret == 0);
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ data is %s",
+ cp->spigot_open ? "available" : "pending");
+ }
+
+ return ret;
+}
+
+int pvr2_ioread_read(struct pvr2_ioread *cp,void __user *buf,unsigned int cnt)
+{
+ unsigned int copied_cnt;
+ unsigned int bcnt;
+ const char *src;
+ int stat;
+ int ret = 0;
+ unsigned int req_cnt = cnt;
+
+ if (!cnt) {
+ pvr2_trace(PVR2_TRACE_TRAP,
+ "/*---TRACE_READ---*/ pvr2_ioread_read id=%p"
+ " ZERO Request? Returning zero.",cp);
+ return 0;
+ }
+
+ stat = pvr2_ioread_avail(cp);
+ if (stat < 0) return stat;
+
+ cp->stream_running = !0;
+
+ mutex_lock(&cp->mutex); do {
+
+ // Suck data out of the buffers and copy to the user
+ copied_cnt = 0;
+ if (!buf) cnt = 0;
+ while (1) {
+ if (!pvr2_ioread_get_buffer(cp)) {
+ ret = -EIO;
+ break;
+ }
+
+ if (!cnt) break;
+
+ if (cp->sync_state == 2) {
+ // We're repeating the sync key data into
+ // the stream.
+ src = cp->sync_key_ptr + cp->sync_buf_offs;
+ bcnt = cp->sync_key_len - cp->sync_buf_offs;
+ } else {
+ // Normal buffer copy
+ src = cp->c_data_ptr + cp->c_data_offs;
+ bcnt = cp->c_data_len - cp->c_data_offs;
+ }
+
+ if (!bcnt) break;
+
+ // Don't run past user's buffer
+ if (bcnt > cnt) bcnt = cnt;
+
+ if (copy_to_user(buf,src,bcnt)) {
+ // User supplied a bad pointer?
+ // Give up - this *will* cause data
+ // to be lost.
+ ret = -EFAULT;
+ break;
+ }
+ cnt -= bcnt;
+ buf += bcnt;
+ copied_cnt += bcnt;
+
+ if (cp->sync_state == 2) {
+ // Update offset inside sync key that we're
+ // repeating back out.
+ cp->sync_buf_offs += bcnt;
+ if (cp->sync_buf_offs >= cp->sync_key_len) {
+ // Consumed entire key; switch mode
+ // to normal.
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/"
+ " sync_state <== 0");
+ cp->sync_state = 0;
+ }
+ } else {
+ // Update buffer offset.
+ cp->c_data_offs += bcnt;
+ }
+ }
+
+ } while (0); mutex_unlock(&cp->mutex);
+
+ if (!ret) {
+ if (copied_cnt) {
+ // If anything was copied, return that count
+ ret = copied_cnt;
+ } else {
+ // Nothing copied; suggest to caller that another
+ // attempt should be tried again later
+ ret = -EAGAIN;
+ }
+ }
+
+ pvr2_trace(PVR2_TRACE_DATA_FLOW,
+ "/*---TRACE_READ---*/ pvr2_ioread_read"
+ " id=%p request=%d result=%d",
+ cp,req_cnt,ret);
+ return ret;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ioread.h b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
new file mode 100644
index 000000000000..6b002597f5de
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-ioread.h
@@ -0,0 +1,50 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_IOREAD_H
+#define __PVRUSB2_IOREAD_H
+
+#include "pvrusb2-io.h"
+
+struct pvr2_ioread;
+
+struct pvr2_ioread *pvr2_ioread_create(void);
+void pvr2_ioread_destroy(struct pvr2_ioread *);
+int pvr2_ioread_setup(struct pvr2_ioread *,struct pvr2_stream *);
+struct pvr2_stream *pvr2_ioread_get_stream(struct pvr2_ioread *);
+void pvr2_ioread_set_sync_key(struct pvr2_ioread *,
+ const char *sync_key_ptr,
+ unsigned int sync_key_len);
+int pvr2_ioread_set_enabled(struct pvr2_ioread *,int fl);
+int pvr2_ioread_get_enabled(struct pvr2_ioread *);
+int pvr2_ioread_read(struct pvr2_ioread *,void __user *buf,unsigned int cnt);
+int pvr2_ioread_avail(struct pvr2_ioread *);
+
+#endif /* __PVRUSB2_IOREAD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
new file mode 100644
index 000000000000..b95248274ed0
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -0,0 +1,172 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/smp_lock.h>
+#include <linux/usb.h>
+#include <linux/videodev2.h>
+
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-context.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+#include "pvrusb2-sysfs.h"
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+#define DRIVER_AUTHOR "Mike Isely <isely@pobox.com>"
+#define DRIVER_DESC "Hauppauge WinTV-PVR-USB2 MPEG2 Encoder/Tuner"
+#define DRIVER_VERSION "V4L in-tree version"
+
+#define DEFAULT_DEBUG_MASK (PVR2_TRACE_ERROR_LEGS| \
+ PVR2_TRACE_INFO| \
+ PVR2_TRACE_TOLERANCE| \
+ PVR2_TRACE_TRAP| \
+ 0)
+
+int pvrusb2_debug = DEFAULT_DEBUG_MASK;
+
+module_param_named(debug,pvrusb2_debug,int,S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug trace mask");
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+static struct pvr2_sysfs_class *class_ptr = 0;
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+static void pvr_setup_attach(struct pvr2_context *pvr)
+{
+ /* Create association with v4l layer */
+ pvr2_v4l2_create(pvr);
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ pvr2_sysfs_create(pvr,class_ptr);
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+}
+
+static int pvr_probe(struct usb_interface *intf,
+ const struct usb_device_id *devid)
+{
+ struct pvr2_context *pvr;
+
+ /* Create underlying hardware interface */
+ pvr = pvr2_context_create(intf,devid,pvr_setup_attach);
+ if (!pvr) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "Failed to create hdw handler");
+ return -ENOMEM;
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_probe(pvr=%p)",pvr);
+
+ usb_set_intfdata(intf, pvr);
+
+ return 0;
+}
+
+/*
+ * pvr_disconnect()
+ *
+ */
+static void pvr_disconnect(struct usb_interface *intf)
+{
+ struct pvr2_context *pvr = usb_get_intfdata(intf);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) BEGIN",pvr);
+
+ usb_set_intfdata (intf, NULL);
+ pvr2_context_disconnect(pvr);
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_disconnect(pvr=%p) DONE",pvr);
+
+}
+
+static struct usb_driver pvr_driver = {
+ name: "pvrusb2",
+ id_table: pvr2_device_table,
+ probe: pvr_probe,
+ disconnect: pvr_disconnect
+};
+
+/*
+ * pvr_init() / pvr_exit()
+ *
+ * This code is run to initialize/exit the driver.
+ *
+ */
+static int __init pvr_init(void)
+{
+ int ret;
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_init");
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ class_ptr = pvr2_sysfs_class_create();
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+ ret = usb_register(&pvr_driver);
+
+ if (ret == 0)
+ info(DRIVER_DESC " : " DRIVER_VERSION);
+ if (pvrusb2_debug) info("Debug mask is %d (0x%x)",
+ pvrusb2_debug,pvrusb2_debug);
+
+ return ret;
+}
+
+static void __exit pvr_exit(void)
+{
+
+ pvr2_trace(PVR2_TRACE_INIT,"pvr_exit");
+
+#ifdef CONFIG_VIDEO_PVRUSB2_SYSFS
+ pvr2_sysfs_class_destroy(class_ptr);
+#endif /* CONFIG_VIDEO_PVRUSB2_SYSFS */
+
+ usb_deregister(&pvr_driver);
+}
+
+module_init(pvr_init);
+module_exit(pvr_exit);
+
+/* Mike Isely <mcisely@pobox.com> 11-Mar-2006: See pvrusb2-hdw.c for
+ MODULE_DEVICE_TABLE(). We have to declare that attribute there
+ because that's where the device table actually is now and it seems
+ that certain gcc configurations get angry if MODULE_DEVICE_TABLE()
+ is used on what ends up being an external symbol. */
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.c b/drivers/media/video/pvrusb2/pvrusb2-std.c
new file mode 100644
index 000000000000..134063693643
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.c
@@ -0,0 +1,408 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 "pvrusb2-std.h"
+#include "pvrusb2-debug.h"
+#include <asm/string.h>
+#include <linux/slab.h>
+
+struct std_name {
+ const char *name;
+ v4l2_std_id id;
+};
+
+
+#define CSTD_PAL \
+ (V4L2_STD_PAL_B| \
+ V4L2_STD_PAL_B1| \
+ V4L2_STD_PAL_G| \
+ V4L2_STD_PAL_H| \
+ V4L2_STD_PAL_I| \
+ V4L2_STD_PAL_D| \
+ V4L2_STD_PAL_D1| \
+ V4L2_STD_PAL_K| \
+ V4L2_STD_PAL_M| \
+ V4L2_STD_PAL_N| \
+ V4L2_STD_PAL_Nc| \
+ V4L2_STD_PAL_60)
+
+#define CSTD_NTSC \
+ (V4L2_STD_NTSC_M| \
+ V4L2_STD_NTSC_M_JP| \
+ V4L2_STD_NTSC_M_KR| \
+ V4L2_STD_NTSC_443)
+
+#define CSTD_SECAM \
+ (V4L2_STD_SECAM_B| \
+ V4L2_STD_SECAM_D| \
+ V4L2_STD_SECAM_G| \
+ V4L2_STD_SECAM_H| \
+ V4L2_STD_SECAM_K| \
+ V4L2_STD_SECAM_K1| \
+ V4L2_STD_SECAM_L| \
+ V4L2_STD_SECAM_LC)
+
+#define TSTD_B (V4L2_STD_PAL_B|V4L2_STD_SECAM_B)
+#define TSTD_B1 (V4L2_STD_PAL_B1)
+#define TSTD_D (V4L2_STD_PAL_D|V4L2_STD_SECAM_D)
+#define TSTD_D1 (V4L2_STD_PAL_D1)
+#define TSTD_G (V4L2_STD_PAL_G|V4L2_STD_SECAM_G)
+#define TSTD_H (V4L2_STD_PAL_H|V4L2_STD_SECAM_H)
+#define TSTD_I (V4L2_STD_PAL_I)
+#define TSTD_K (V4L2_STD_PAL_K|V4L2_STD_SECAM_K)
+#define TSTD_K1 (V4L2_STD_SECAM_K1)
+#define TSTD_L (V4L2_STD_SECAM_L)
+#define TSTD_M (V4L2_STD_PAL_M|V4L2_STD_NTSC_M)
+#define TSTD_N (V4L2_STD_PAL_N)
+#define TSTD_Nc (V4L2_STD_PAL_Nc)
+#define TSTD_60 (V4L2_STD_PAL_60)
+
+#define CSTD_ALL (CSTD_PAL|CSTD_NTSC|CSTD_SECAM)
+
+/* Mapping of standard bits to color system */
+const static struct std_name std_groups[] = {
+ {"PAL",CSTD_PAL},
+ {"NTSC",CSTD_NTSC},
+ {"SECAM",CSTD_SECAM},
+};
+
+/* Mapping of standard bits to modulation system */
+const static struct std_name std_items[] = {
+ {"B",TSTD_B},
+ {"B1",TSTD_B1},
+ {"D",TSTD_D},
+ {"D1",TSTD_D1},
+ {"G",TSTD_G},
+ {"H",TSTD_H},
+ {"I",TSTD_I},
+ {"K",TSTD_K},
+ {"K1",TSTD_K1},
+ {"L",TSTD_L},
+ {"LC",V4L2_STD_SECAM_LC},
+ {"M",TSTD_M},
+ {"Mj",V4L2_STD_NTSC_M_JP},
+ {"443",V4L2_STD_NTSC_443},
+ {"Mk",V4L2_STD_NTSC_M_KR},
+ {"N",TSTD_N},
+ {"Nc",TSTD_Nc},
+ {"60",TSTD_60},
+};
+
+
+// Search an array of std_name structures and return a pointer to the
+// element with the matching name.
+static const struct std_name *find_std_name(const struct std_name *arrPtr,
+ unsigned int arrSize,
+ const char *bufPtr,
+ unsigned int bufSize)
+{
+ unsigned int idx;
+ const struct std_name *p;
+ for (idx = 0; idx < arrSize; idx++) {
+ p = arrPtr + idx;
+ if (strlen(p->name) != bufSize) continue;
+ if (!memcmp(bufPtr,p->name,bufSize)) return p;
+ }
+ return 0;
+}
+
+
+int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
+ unsigned int bufSize)
+{
+ v4l2_std_id id = 0;
+ v4l2_std_id cmsk = 0;
+ v4l2_std_id t;
+ int mMode = 0;
+ unsigned int cnt;
+ char ch;
+ const struct std_name *sp;
+
+ while (bufSize) {
+ if (!mMode) {
+ cnt = 0;
+ while ((cnt < bufSize) && (bufPtr[cnt] != '-')) cnt++;
+ if (cnt >= bufSize) return 0; // No more characters
+ sp = find_std_name(
+ std_groups,
+ sizeof(std_groups)/sizeof(std_groups[0]),
+ bufPtr,cnt);
+ if (!sp) return 0; // Illegal color system name
+ cnt++;
+ bufPtr += cnt;
+ bufSize -= cnt;
+ mMode = !0;
+ cmsk = sp->id;
+ continue;
+ }
+ cnt = 0;
+ while (cnt < bufSize) {
+ ch = bufPtr[cnt];
+ if (ch == ';') {
+ mMode = 0;
+ break;
+ }
+ if (ch == '/') break;
+ cnt++;
+ }
+ sp = find_std_name(std_items,
+ sizeof(std_items)/sizeof(std_items[0]),
+ bufPtr,cnt);
+ if (!sp) return 0; // Illegal modulation system ID
+ t = sp->id & cmsk;
+ if (!t) return 0; // Specific color + modulation system illegal
+ id |= t;
+ if (cnt < bufSize) cnt++;
+ bufPtr += cnt;
+ bufSize -= cnt;
+ }
+
+ if (idPtr) *idPtr = id;
+ return !0;
+}
+
+
+unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
+ v4l2_std_id id)
+{
+ unsigned int idx1,idx2;
+ const struct std_name *ip,*gp;
+ int gfl,cfl;
+ unsigned int c1,c2;
+ cfl = 0;
+ c1 = 0;
+ for (idx1 = 0;
+ idx1 < sizeof(std_groups)/sizeof(std_groups[0]);
+ idx1++) {
+ gp = std_groups + idx1;
+ gfl = 0;
+ for (idx2 = 0;
+ idx2 < sizeof(std_items)/sizeof(std_items[0]);
+ idx2++) {
+ ip = std_items + idx2;
+ if (!(gp->id & ip->id & id)) continue;
+ if (!gfl) {
+ if (cfl) {
+ c2 = scnprintf(bufPtr,bufSize,";");
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ }
+ cfl = !0;
+ c2 = scnprintf(bufPtr,bufSize,
+ "%s-",gp->name);
+ gfl = !0;
+ } else {
+ c2 = scnprintf(bufPtr,bufSize,"/");
+ }
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ c2 = scnprintf(bufPtr,bufSize,
+ ip->name);
+ c1 += c2;
+ bufSize -= c2;
+ bufPtr += c2;
+ }
+ }
+ return c1;
+}
+
+
+// Template data for possible enumerated video standards. Here we group
+// standards which share common frame rates and resolution.
+static struct v4l2_standard generic_standards[] = {
+ {
+ .id = (TSTD_B|TSTD_B1|
+ TSTD_D|TSTD_D1|
+ TSTD_G|
+ TSTD_H|
+ TSTD_I|
+ TSTD_K|TSTD_K1|
+ TSTD_L|
+ V4L2_STD_SECAM_LC |
+ TSTD_N|TSTD_Nc),
+ .frameperiod =
+ {
+ .numerator = 1,
+ .denominator= 25
+ },
+ .framelines = 625,
+ .reserved = {0,0,0,0}
+ }, {
+ .id = (TSTD_M|
+ V4L2_STD_NTSC_M_JP|
+ V4L2_STD_NTSC_M_KR),
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }, { // This is a total wild guess
+ .id = (TSTD_60),
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }, { // This is total wild guess
+ .id = V4L2_STD_NTSC_443,
+ .frameperiod =
+ {
+ .numerator = 1001,
+ .denominator= 30000
+ },
+ .framelines = 525,
+ .reserved = {0,0,0,0}
+ }
+};
+
+#define generic_standards_cnt (sizeof(generic_standards)/sizeof(generic_standards[0]))
+
+static struct v4l2_standard *match_std(v4l2_std_id id)
+{
+ unsigned int idx;
+ for (idx = 0; idx < generic_standards_cnt; idx++) {
+ if (generic_standards[idx].id & id) {
+ return generic_standards + idx;
+ }
+ }
+ return 0;
+}
+
+static int pvr2_std_fill(struct v4l2_standard *std,v4l2_std_id id)
+{
+ struct v4l2_standard *template;
+ int idx;
+ unsigned int bcnt;
+ template = match_std(id);
+ if (!template) return 0;
+ idx = std->index;
+ memcpy(std,template,sizeof(*template));
+ std->index = idx;
+ std->id = id;
+ bcnt = pvr2_std_id_to_str(std->name,sizeof(std->name)-1,id);
+ std->name[bcnt] = 0;
+ pvr2_trace(PVR2_TRACE_INIT,"Set up standard idx=%u name=%s",
+ std->index,std->name);
+ return !0;
+}
+
+/* These are special cases of combined standards that we should enumerate
+ separately if the component pieces are present. */
+static v4l2_std_id std_mixes[] = {
+ V4L2_STD_PAL_B | V4L2_STD_PAL_G,
+ V4L2_STD_PAL_D | V4L2_STD_PAL_K,
+ V4L2_STD_SECAM_B | V4L2_STD_SECAM_G,
+ V4L2_STD_SECAM_D | V4L2_STD_SECAM_K,
+};
+
+struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
+ v4l2_std_id id)
+{
+ unsigned int std_cnt = 0;
+ unsigned int idx,bcnt,idx2;
+ v4l2_std_id idmsk,cmsk,fmsk;
+ struct v4l2_standard *stddefs;
+
+ if (pvrusb2_debug & PVR2_TRACE_INIT) {
+ char buf[50];
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),id);
+ pvr2_trace(
+ PVR2_TRACE_INIT,"Mapping standards mask=0x%x (%.*s)",
+ (int)id,bcnt,buf);
+ }
+
+ *countptr = 0;
+ std_cnt = 0;
+ fmsk = 0;
+ for (idmsk = 1, cmsk = id; cmsk; idmsk <<= 1) {
+ if (!(idmsk & cmsk)) continue;
+ cmsk &= ~idmsk;
+ if (match_std(idmsk)) {
+ std_cnt++;
+ continue;
+ }
+ fmsk |= idmsk;
+ }
+
+ for (idx2 = 0; idx2 < sizeof(std_mixes)/sizeof(std_mixes[0]); idx2++) {
+ if ((id & std_mixes[idx2]) == std_mixes[idx2]) std_cnt++;
+ }
+
+ if (fmsk) {
+ char buf[50];
+ bcnt = pvr2_std_id_to_str(buf,sizeof(buf),fmsk);
+ pvr2_trace(
+ PVR2_TRACE_ERROR_LEGS,
+ "WARNING:"
+ " Failed to classify the following standard(s): %.*s",
+ bcnt,buf);
+ }
+
+ pvr2_trace(PVR2_TRACE_INIT,"Setting up %u unique standard(s)",
+ std_cnt);
+ if (!std_cnt) return 0; // paranoia
+
+ stddefs = kmalloc(sizeof(struct v4l2_standard) * std_cnt,
+ GFP_KERNEL);
+ memset(stddefs,0,sizeof(struct v4l2_standard) * std_cnt);
+ for (idx = 0; idx < std_cnt; idx++) stddefs[idx].index = idx;
+
+ idx = 0;
+
+ /* Enumerate potential special cases */
+ for (idx2 = 0; ((idx2 < sizeof(std_mixes)/sizeof(std_mixes[0])) &&
+ (idx < std_cnt)); idx2++) {
+ if (!(id & std_mixes[idx2])) continue;
+ if (pvr2_std_fill(stddefs+idx,std_mixes[idx2])) idx++;
+ }
+ /* Now enumerate individual pieces */
+ for (idmsk = 1, cmsk = id; cmsk && (idx < std_cnt); idmsk <<= 1) {
+ if (!(idmsk & cmsk)) continue;
+ cmsk &= ~idmsk;
+ if (!pvr2_std_fill(stddefs+idx,idmsk)) continue;
+ idx++;
+ }
+
+ *countptr = std_cnt;
+ return stddefs;
+}
+
+v4l2_std_id pvr2_std_get_usable(void)
+{
+ return CSTD_ALL;
+}
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-std.h b/drivers/media/video/pvrusb2/pvrusb2-std.h
new file mode 100644
index 000000000000..07c399375341
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-std.h
@@ -0,0 +1,60 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_STD_H
+#define __PVRUSB2_STD_H
+
+#include <linux/videodev2.h>
+
+// Convert string describing one or more video standards into a mask of V4L
+// standard bits. Return true if conversion succeeds otherwise return
+// false. String is expected to be of the form: C1-x/y;C2-a/b where C1 and
+// C2 are color system names (e.g. "PAL", "NTSC") and x, y, a, and b are
+// modulation schemes (e.g. "M", "B", "G", etc).
+int pvr2_std_str_to_id(v4l2_std_id *idPtr,const char *bufPtr,
+ unsigned int bufSize);
+
+// Convert any arbitrary set of video standard bits into an unambiguous
+// readable string. Return value is the number of bytes consumed in the
+// buffer. The formatted string is of a form that can be parsed by our
+// sibling std_std_to_id() function.
+unsigned int pvr2_std_id_to_str(char *bufPtr, unsigned int bufSize,
+ v4l2_std_id id);
+
+// Create an array of suitable v4l2_standard structures given a bit mask of
+// video standards to support. The array is allocated from the heap, and
+// the number of elements is returned in the first argument.
+struct v4l2_standard *pvr2_std_create_enum(unsigned int *countptr,
+ v4l2_std_id id);
+
+// Return mask of which video standard bits are valid
+v4l2_std_id pvr2_std_get_usable(void);
+
+#endif /* __PVRUSB2_STD_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
new file mode 100644
index 000000000000..c6e6523d74b4
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -0,0 +1,865 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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 <linux/config.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+#include "pvrusb2-sysfs.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2-debug.h"
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+#include "pvrusb2-debugifc.h"
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+#define pvr2_sysfs_trace(...) pvr2_trace(PVR2_TRACE_SYSFS,__VA_ARGS__)
+
+struct pvr2_sysfs {
+ struct pvr2_channel channel;
+ struct class_device *class_dev;
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ struct pvr2_sysfs_debugifc *debugifc;
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+ struct pvr2_sysfs_ctl_item *item_first;
+ struct pvr2_sysfs_ctl_item *item_last;
+ struct sysfs_ops kops;
+ struct kobj_type ktype;
+ struct class_device_attribute attr_v4l_minor_number;
+ struct class_device_attribute attr_unit_number;
+};
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+struct pvr2_sysfs_debugifc {
+ struct class_device_attribute attr_debugcmd;
+ struct class_device_attribute attr_debuginfo;
+};
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+struct pvr2_sysfs_ctl_item {
+ struct class_device_attribute attr_name;
+ struct class_device_attribute attr_type;
+ struct class_device_attribute attr_min;
+ struct class_device_attribute attr_max;
+ struct class_device_attribute attr_enum;
+ struct class_device_attribute attr_bits;
+ struct class_device_attribute attr_val;
+ struct class_device_attribute attr_custom;
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *chptr;
+ struct pvr2_sysfs_ctl_item *item_next;
+ struct attribute *attr_gen[7];
+ struct attribute_group grp;
+ char name[80];
+};
+
+struct pvr2_sysfs_class {
+ struct class class;
+};
+
+static ssize_t show_name(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ const char *name;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+
+ name = pvr2_ctrl_get_desc(cptr);
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_name(cid=%d) is %s",sfp,id,name);
+
+ if (!name) return -EINVAL;
+
+ return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+}
+
+static ssize_t show_type(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ const char *name;
+ enum pvr2_ctl_type tp;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+
+ tp = pvr2_ctrl_get_type(cptr);
+ switch (tp) {
+ case pvr2_ctl_int: name = "integer"; break;
+ case pvr2_ctl_enum: name = "enum"; break;
+ case pvr2_ctl_bitmask: name = "bitmask"; break;
+ case pvr2_ctl_bool: name = "boolean"; break;
+ default: name = "?"; break;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_type(cid=%d) is %s",sfp,id,name);
+
+ if (!name) return -EINVAL;
+
+ return scnprintf(buf,PAGE_SIZE,"%s\n",name);
+}
+
+static ssize_t show_min(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ long val;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+ val = pvr2_ctrl_get_min(cptr);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_min(cid=%d) is %ld",sfp,id,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+}
+
+static ssize_t show_max(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ long val;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+ val = pvr2_ctrl_get_max(cptr);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_max(cid=%d) is %ld",sfp,id,val);
+
+ return scnprintf(buf,PAGE_SIZE,"%ld\n",val);
+}
+
+static ssize_t show_val_norm(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ int val,ret;
+ unsigned int cnt = 0;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+
+ ret = pvr2_ctrl_get_value(cptr,&val);
+ if (ret < 0) return ret;
+
+ ret = pvr2_ctrl_value_to_sym(cptr,~0,val,
+ buf,PAGE_SIZE-1,&cnt);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_norm(cid=%d) is %.*s (%d)",
+ sfp,id,cnt,buf,val);
+ buf[cnt] = '\n';
+ return cnt+1;
+}
+
+static ssize_t show_val_custom(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ int val,ret;
+ unsigned int cnt = 0;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+
+ ret = pvr2_ctrl_get_value(cptr,&val);
+ if (ret < 0) return ret;
+
+ ret = pvr2_ctrl_custom_value_to_sym(cptr,~0,val,
+ buf,PAGE_SIZE-1,&cnt);
+
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_val_custom(cid=%d) is %.*s (%d)",
+ sfp,id,cnt,buf,val);
+ buf[cnt] = '\n';
+ return cnt+1;
+}
+
+static ssize_t show_enum(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ long val;
+ unsigned int bcnt,ccnt,ecnt;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+ ecnt = pvr2_ctrl_get_cnt(cptr);
+ bcnt = 0;
+ for (val = 0; val < ecnt; val++) {
+ pvr2_ctrl_get_valname(cptr,val,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+ if (!ccnt) continue;
+ bcnt += ccnt;
+ if (bcnt >= PAGE_SIZE) break;
+ buf[bcnt] = '\n';
+ bcnt++;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_enum(cid=%d)",sfp,id);
+ return bcnt;
+}
+
+static ssize_t show_bits(int id,struct class_device *class_dev,char *buf)
+{
+ struct pvr2_ctrl *cptr;
+ struct pvr2_sysfs *sfp;
+ int valid_bits,msk;
+ unsigned int bcnt,ccnt;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (!cptr) return -EINVAL;
+ valid_bits = pvr2_ctrl_get_mask(cptr);
+ bcnt = 0;
+ for (msk = 1; valid_bits; msk <<= 1) {
+ if (!(msk & valid_bits)) continue;
+ valid_bits &= ~msk;
+ pvr2_ctrl_get_valname(cptr,msk,buf+bcnt,PAGE_SIZE-bcnt,&ccnt);
+ bcnt += ccnt;
+ if (bcnt >= PAGE_SIZE) break;
+ buf[bcnt] = '\n';
+ bcnt++;
+ }
+ pvr2_sysfs_trace("pvr2_sysfs(%p) show_bits(cid=%d)",sfp,id);
+ return bcnt;
+}
+
+static int store_val_any(int id,int customfl,struct pvr2_sysfs *sfp,
+ const char *buf,unsigned int count)
+{
+ struct pvr2_ctrl *cptr;
+ int ret;
+ int mask,val;
+
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,id);
+ if (customfl) {
+ ret = pvr2_ctrl_custom_sym_to_value(cptr,buf,count,&mask,&val);
+ } else {
+ ret = pvr2_ctrl_sym_to_value(cptr,buf,count,&mask,&val);
+ }
+ if (ret < 0) return ret;
+ ret = pvr2_ctrl_set_mask_value(cptr,mask,val);
+ pvr2_hdw_commit_ctl(sfp->channel.hdw);
+ return ret;
+}
+
+static ssize_t store_val_norm(int id,struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ ret = store_val_any(id,0,sfp,buf,count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+static ssize_t store_val_custom(int id,struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ ret = store_val_any(id,1,sfp,buf,count);
+ if (!ret) ret = count;
+ return ret;
+}
+
+/*
+ Mike Isely <isely@pobox.com> 30-April-2005
+
+ This next batch of horrible preprocessor hackery is needed because the
+ kernel's class_device_attribute mechanism fails to pass the actual
+ attribute through to the show / store functions, which means we have no
+ way to package up any attribute-specific parameters, like for example the
+ control id. So we work around this brain-damage by encoding the control
+ id into the show / store functions themselves and pick the function based
+ on the control id we're setting up. These macros try to ease the pain.
+ Yuck.
+*/
+
+#define CREATE_SHOW_INSTANCE(sf_name,ctl_id) \
+static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,char *buf) \
+{ return sf_name(ctl_id,class_dev,buf); }
+
+#define CREATE_STORE_INSTANCE(sf_name,ctl_id) \
+static ssize_t sf_name##_##ctl_id(struct class_device *class_dev,const char *buf,size_t count) \
+{ return sf_name(ctl_id,class_dev,buf,count); }
+
+#define CREATE_BATCH(ctl_id) \
+CREATE_SHOW_INSTANCE(show_name,ctl_id) \
+CREATE_SHOW_INSTANCE(show_type,ctl_id) \
+CREATE_SHOW_INSTANCE(show_min,ctl_id) \
+CREATE_SHOW_INSTANCE(show_max,ctl_id) \
+CREATE_SHOW_INSTANCE(show_val_norm,ctl_id) \
+CREATE_SHOW_INSTANCE(show_val_custom,ctl_id) \
+CREATE_SHOW_INSTANCE(show_enum,ctl_id) \
+CREATE_SHOW_INSTANCE(show_bits,ctl_id) \
+CREATE_STORE_INSTANCE(store_val_norm,ctl_id) \
+CREATE_STORE_INSTANCE(store_val_custom,ctl_id) \
+
+CREATE_BATCH(0)
+CREATE_BATCH(1)
+CREATE_BATCH(2)
+CREATE_BATCH(3)
+CREATE_BATCH(4)
+CREATE_BATCH(5)
+CREATE_BATCH(6)
+CREATE_BATCH(7)
+CREATE_BATCH(8)
+CREATE_BATCH(9)
+CREATE_BATCH(10)
+CREATE_BATCH(11)
+CREATE_BATCH(12)
+CREATE_BATCH(13)
+CREATE_BATCH(14)
+CREATE_BATCH(15)
+CREATE_BATCH(16)
+CREATE_BATCH(17)
+CREATE_BATCH(18)
+CREATE_BATCH(19)
+CREATE_BATCH(20)
+CREATE_BATCH(21)
+CREATE_BATCH(22)
+CREATE_BATCH(23)
+CREATE_BATCH(24)
+CREATE_BATCH(25)
+CREATE_BATCH(26)
+CREATE_BATCH(27)
+CREATE_BATCH(28)
+CREATE_BATCH(29)
+CREATE_BATCH(30)
+CREATE_BATCH(31)
+CREATE_BATCH(32)
+CREATE_BATCH(33)
+CREATE_BATCH(34)
+CREATE_BATCH(35)
+CREATE_BATCH(36)
+CREATE_BATCH(37)
+CREATE_BATCH(38)
+CREATE_BATCH(39)
+CREATE_BATCH(40)
+CREATE_BATCH(41)
+CREATE_BATCH(42)
+CREATE_BATCH(43)
+CREATE_BATCH(44)
+CREATE_BATCH(45)
+CREATE_BATCH(46)
+CREATE_BATCH(47)
+CREATE_BATCH(48)
+CREATE_BATCH(49)
+CREATE_BATCH(50)
+CREATE_BATCH(51)
+CREATE_BATCH(52)
+CREATE_BATCH(53)
+CREATE_BATCH(54)
+CREATE_BATCH(55)
+CREATE_BATCH(56)
+CREATE_BATCH(57)
+CREATE_BATCH(58)
+CREATE_BATCH(59)
+
+struct pvr2_sysfs_func_set {
+ ssize_t (*show_name)(struct class_device *,char *);
+ ssize_t (*show_type)(struct class_device *,char *);
+ ssize_t (*show_min)(struct class_device *,char *);
+ ssize_t (*show_max)(struct class_device *,char *);
+ ssize_t (*show_enum)(struct class_device *,char *);
+ ssize_t (*show_bits)(struct class_device *,char *);
+ ssize_t (*show_val_norm)(struct class_device *,char *);
+ ssize_t (*store_val_norm)(struct class_device *,
+ const char *,size_t);
+ ssize_t (*show_val_custom)(struct class_device *,char *);
+ ssize_t (*store_val_custom)(struct class_device *,
+ const char *,size_t);
+};
+
+#define INIT_BATCH(ctl_id) \
+[ctl_id] = { \
+ .show_name = show_name_##ctl_id, \
+ .show_type = show_type_##ctl_id, \
+ .show_min = show_min_##ctl_id, \
+ .show_max = show_max_##ctl_id, \
+ .show_enum = show_enum_##ctl_id, \
+ .show_bits = show_bits_##ctl_id, \
+ .show_val_norm = show_val_norm_##ctl_id, \
+ .store_val_norm = store_val_norm_##ctl_id, \
+ .show_val_custom = show_val_custom_##ctl_id, \
+ .store_val_custom = store_val_custom_##ctl_id, \
+} \
+
+static struct pvr2_sysfs_func_set funcs[] = {
+ INIT_BATCH(0),
+ INIT_BATCH(1),
+ INIT_BATCH(2),
+ INIT_BATCH(3),
+ INIT_BATCH(4),
+ INIT_BATCH(5),
+ INIT_BATCH(6),
+ INIT_BATCH(7),
+ INIT_BATCH(8),
+ INIT_BATCH(9),
+ INIT_BATCH(10),
+ INIT_BATCH(11),
+ INIT_BATCH(12),
+ INIT_BATCH(13),
+ INIT_BATCH(14),
+ INIT_BATCH(15),
+ INIT_BATCH(16),
+ INIT_BATCH(17),
+ INIT_BATCH(18),
+ INIT_BATCH(19),
+ INIT_BATCH(20),
+ INIT_BATCH(21),
+ INIT_BATCH(22),
+ INIT_BATCH(23),
+ INIT_BATCH(24),
+ INIT_BATCH(25),
+ INIT_BATCH(26),
+ INIT_BATCH(27),
+ INIT_BATCH(28),
+ INIT_BATCH(29),
+ INIT_BATCH(30),
+ INIT_BATCH(31),
+ INIT_BATCH(32),
+ INIT_BATCH(33),
+ INIT_BATCH(34),
+ INIT_BATCH(35),
+ INIT_BATCH(36),
+ INIT_BATCH(37),
+ INIT_BATCH(38),
+ INIT_BATCH(39),
+ INIT_BATCH(40),
+ INIT_BATCH(41),
+ INIT_BATCH(42),
+ INIT_BATCH(43),
+ INIT_BATCH(44),
+ INIT_BATCH(45),
+ INIT_BATCH(46),
+ INIT_BATCH(47),
+ INIT_BATCH(48),
+ INIT_BATCH(49),
+ INIT_BATCH(50),
+ INIT_BATCH(51),
+ INIT_BATCH(52),
+ INIT_BATCH(53),
+ INIT_BATCH(54),
+ INIT_BATCH(55),
+ INIT_BATCH(56),
+ INIT_BATCH(57),
+ INIT_BATCH(58),
+ INIT_BATCH(59),
+};
+
+
+static void pvr2_sysfs_add_control(struct pvr2_sysfs *sfp,int ctl_id)
+{
+ struct pvr2_sysfs_ctl_item *cip;
+ struct pvr2_sysfs_func_set *fp;
+ struct pvr2_ctrl *cptr;
+ unsigned int cnt,acnt;
+
+ if ((ctl_id < 0) || (ctl_id >= (sizeof(funcs)/sizeof(funcs[0])))) {
+ return;
+ }
+
+ fp = funcs + ctl_id;
+ cptr = pvr2_hdw_get_ctrl_by_index(sfp->channel.hdw,ctl_id);
+ if (!cptr) return;
+
+ cip = kmalloc(sizeof(*cip),GFP_KERNEL);
+ if (!cip) return;
+ memset(cip,0,sizeof(*cip));
+ pvr2_sysfs_trace("Creating pvr2_sysfs_ctl_item id=%p",cip);
+
+ cip->cptr = cptr;
+
+ cip->chptr = sfp;
+ cip->item_next = 0;
+ if (sfp->item_last) {
+ sfp->item_last->item_next = cip;
+ } else {
+ sfp->item_first = cip;
+ }
+ sfp->item_last = cip;
+
+ cip->attr_name.attr.owner = THIS_MODULE;
+ cip->attr_name.attr.name = "name";
+ cip->attr_name.attr.mode = S_IRUGO;
+ cip->attr_name.show = fp->show_name;
+
+ cip->attr_type.attr.owner = THIS_MODULE;
+ cip->attr_type.attr.name = "type";
+ cip->attr_type.attr.mode = S_IRUGO;
+ cip->attr_type.show = fp->show_type;
+
+ cip->attr_min.attr.owner = THIS_MODULE;
+ cip->attr_min.attr.name = "min_val";
+ cip->attr_min.attr.mode = S_IRUGO;
+ cip->attr_min.show = fp->show_min;
+
+ cip->attr_max.attr.owner = THIS_MODULE;
+ cip->attr_max.attr.name = "max_val";
+ cip->attr_max.attr.mode = S_IRUGO;
+ cip->attr_max.show = fp->show_max;
+
+ cip->attr_val.attr.owner = THIS_MODULE;
+ cip->attr_val.attr.name = "cur_val";
+ cip->attr_val.attr.mode = S_IRUGO;
+
+ cip->attr_custom.attr.owner = THIS_MODULE;
+ cip->attr_custom.attr.name = "custom_val";
+ cip->attr_custom.attr.mode = S_IRUGO;
+
+ cip->attr_enum.attr.owner = THIS_MODULE;
+ cip->attr_enum.attr.name = "enum_val";
+ cip->attr_enum.attr.mode = S_IRUGO;
+ cip->attr_enum.show = fp->show_enum;
+
+ cip->attr_bits.attr.owner = THIS_MODULE;
+ cip->attr_bits.attr.name = "bit_val";
+ cip->attr_bits.attr.mode = S_IRUGO;
+ cip->attr_bits.show = fp->show_bits;
+
+ if (pvr2_ctrl_is_writable(cptr)) {
+ cip->attr_val.attr.mode |= S_IWUSR|S_IWGRP;
+ cip->attr_custom.attr.mode |= S_IWUSR|S_IWGRP;
+ }
+
+ acnt = 0;
+ cip->attr_gen[acnt++] = &cip->attr_name.attr;
+ cip->attr_gen[acnt++] = &cip->attr_type.attr;
+ cip->attr_gen[acnt++] = &cip->attr_val.attr;
+ cip->attr_val.show = fp->show_val_norm;
+ cip->attr_val.store = fp->store_val_norm;
+ if (pvr2_ctrl_has_custom_symbols(cptr)) {
+ cip->attr_gen[acnt++] = &cip->attr_custom.attr;
+ cip->attr_custom.show = fp->show_val_custom;
+ cip->attr_custom.store = fp->store_val_custom;
+ }
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ // Control is an enumeration
+ cip->attr_gen[acnt++] = &cip->attr_enum.attr;
+ break;
+ case pvr2_ctl_int:
+ // Control is an integer
+ cip->attr_gen[acnt++] = &cip->attr_min.attr;
+ cip->attr_gen[acnt++] = &cip->attr_max.attr;
+ break;
+ case pvr2_ctl_bitmask:
+ // Control is an bitmask
+ cip->attr_gen[acnt++] = &cip->attr_bits.attr;
+ break;
+ default: break;
+ }
+
+ cnt = scnprintf(cip->name,sizeof(cip->name)-1,"ctl_%s",
+ pvr2_ctrl_get_name(cptr));
+ cip->name[cnt] = 0;
+ cip->grp.name = cip->name;
+ cip->grp.attrs = cip->attr_gen;
+
+ sysfs_create_group(&sfp->class_dev->kobj,&cip->grp);
+}
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+static ssize_t debuginfo_show(struct class_device *,char *);
+static ssize_t debugcmd_show(struct class_device *,char *);
+static ssize_t debugcmd_store(struct class_device *,const char *,size_t count);
+
+static void pvr2_sysfs_add_debugifc(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_debugifc *dip;
+ dip = kmalloc(sizeof(*dip),GFP_KERNEL);
+ if (!dip) return;
+ memset(dip,0,sizeof(*dip));
+ dip->attr_debugcmd.attr.owner = THIS_MODULE;
+ dip->attr_debugcmd.attr.name = "debugcmd";
+ dip->attr_debugcmd.attr.mode = S_IRUGO|S_IWUSR|S_IWGRP;
+ dip->attr_debugcmd.show = debugcmd_show;
+ dip->attr_debugcmd.store = debugcmd_store;
+ dip->attr_debuginfo.attr.owner = THIS_MODULE;
+ dip->attr_debuginfo.attr.name = "debuginfo";
+ dip->attr_debuginfo.attr.mode = S_IRUGO;
+ dip->attr_debuginfo.show = debuginfo_show;
+ sfp->debugifc = dip;
+ class_device_create_file(sfp->class_dev,&dip->attr_debugcmd);
+ class_device_create_file(sfp->class_dev,&dip->attr_debuginfo);
+}
+
+
+static void pvr2_sysfs_tear_down_debugifc(struct pvr2_sysfs *sfp)
+{
+ if (!sfp->debugifc) return;
+ class_device_remove_file(sfp->class_dev,
+ &sfp->debugifc->attr_debuginfo);
+ class_device_remove_file(sfp->class_dev,&sfp->debugifc->attr_debugcmd);
+ kfree(sfp->debugifc);
+ sfp->debugifc = 0;
+}
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+
+static void pvr2_sysfs_add_controls(struct pvr2_sysfs *sfp)
+{
+ unsigned int idx,cnt;
+ cnt = pvr2_hdw_get_ctrl_count(sfp->channel.hdw);
+ for (idx = 0; idx < cnt; idx++) {
+ pvr2_sysfs_add_control(sfp,idx);
+ }
+}
+
+
+static void pvr2_sysfs_tear_down_controls(struct pvr2_sysfs *sfp)
+{
+ struct pvr2_sysfs_ctl_item *cip1,*cip2;
+ for (cip1 = sfp->item_first; cip1; cip1 = cip2) {
+ cip2 = cip1->item_next;
+ sysfs_remove_group(&sfp->class_dev->kobj,&cip1->grp);
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_ctl_item id=%p",cip1);
+ kfree(cip1);
+ }
+}
+
+
+static void pvr2_sysfs_class_release(struct class *class)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = container_of(class,struct pvr2_sysfs_class,class);
+ pvr2_sysfs_trace("Destroying pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+}
+
+
+static void pvr2_sysfs_release(struct class_device *class_dev)
+{
+ pvr2_sysfs_trace("Releasing class_dev id=%p",class_dev);
+ kfree(class_dev);
+}
+
+
+static void class_dev_destroy(struct pvr2_sysfs *sfp)
+{
+ if (!sfp->class_dev) return;
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ pvr2_sysfs_tear_down_debugifc(sfp);
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+ pvr2_sysfs_tear_down_controls(sfp);
+ class_device_remove_file(sfp->class_dev,&sfp->attr_v4l_minor_number);
+ class_device_remove_file(sfp->class_dev,&sfp->attr_unit_number);
+ pvr2_sysfs_trace("Destroying class_dev id=%p",sfp->class_dev);
+ sfp->class_dev->class_data = 0;
+ class_device_unregister(sfp->class_dev);
+ sfp->class_dev = 0;
+}
+
+
+static ssize_t v4l_minor_number_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_v4l_get_minor_number(sfp->channel.hdw));
+}
+
+
+static ssize_t unit_number_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return scnprintf(buf,PAGE_SIZE,"%d\n",
+ pvr2_hdw_get_unit_number(sfp->channel.hdw));
+}
+
+
+static void class_dev_create(struct pvr2_sysfs *sfp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct usb_device *usb_dev;
+ struct class_device *class_dev;
+ usb_dev = pvr2_hdw_get_dev(sfp->channel.hdw);
+ if (!usb_dev) return;
+ class_dev = kmalloc(sizeof(*class_dev),GFP_KERNEL);
+ if (!class_dev) return;
+ memset(class_dev,0,sizeof(*class_dev));
+
+ pvr2_sysfs_trace("Creating class_dev id=%p",class_dev);
+
+ class_dev->class = &class_ptr->class;
+ if (pvr2_hdw_get_sn(sfp->channel.hdw)) {
+ snprintf(class_dev->class_id,BUS_ID_SIZE,"sn-%lu",
+ pvr2_hdw_get_sn(sfp->channel.hdw));
+ } else if (pvr2_hdw_get_unit_number(sfp->channel.hdw) >= 0) {
+ snprintf(class_dev->class_id,BUS_ID_SIZE,"unit-%c",
+ pvr2_hdw_get_unit_number(sfp->channel.hdw) + 'a');
+ } else {
+ kfree(class_dev);
+ return;
+ }
+
+ class_dev->dev = &usb_dev->dev;
+
+ sfp->class_dev = class_dev;
+ class_dev->class_data = sfp;
+ class_device_register(class_dev);
+
+ sfp->attr_v4l_minor_number.attr.owner = THIS_MODULE;
+ sfp->attr_v4l_minor_number.attr.name = "v4l_minor_number";
+ sfp->attr_v4l_minor_number.attr.mode = S_IRUGO;
+ sfp->attr_v4l_minor_number.show = v4l_minor_number_show;
+ sfp->attr_v4l_minor_number.store = 0;
+ class_device_create_file(sfp->class_dev,&sfp->attr_v4l_minor_number);
+ sfp->attr_unit_number.attr.owner = THIS_MODULE;
+ sfp->attr_unit_number.attr.name = "unit_number";
+ sfp->attr_unit_number.attr.mode = S_IRUGO;
+ sfp->attr_unit_number.show = unit_number_show;
+ sfp->attr_unit_number.store = 0;
+ class_device_create_file(sfp->class_dev,&sfp->attr_unit_number);
+
+ pvr2_sysfs_add_controls(sfp);
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+ pvr2_sysfs_add_debugifc(sfp);
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+}
+
+
+static void pvr2_sysfs_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = container_of(chp,struct pvr2_sysfs,channel);
+ if (!sfp->channel.mc_head->disconnect_flag) return;
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_sysfs id=%p",sfp);
+ class_dev_destroy(sfp);
+ pvr2_channel_done(&sfp->channel);
+ kfree(sfp);
+}
+
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *mp,
+ struct pvr2_sysfs_class *class_ptr)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = kmalloc(sizeof(*sfp),GFP_KERNEL);
+ if (!sfp) return sfp;
+ memset(sfp,0,sizeof(*sfp));
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_sysfs id=%p",sfp);
+ pvr2_channel_init(&sfp->channel,mp);
+ sfp->channel.check_func = pvr2_sysfs_internal_check;
+
+ class_dev_create(sfp,class_ptr);
+ return sfp;
+}
+
+
+static int pvr2_sysfs_hotplug(struct class_device *cd,char **envp,
+ int numenvp,char *buf,int size)
+{
+ /* Even though we don't do anything here, we still need this function
+ because sysfs will still try to call it. */
+ return 0;
+}
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void)
+{
+ struct pvr2_sysfs_class *clp;
+ clp = kmalloc(sizeof(*clp),GFP_KERNEL);
+ if (!clp) return clp;
+ memset(clp,0,sizeof(*clp));
+ pvr2_sysfs_trace("Creating pvr2_sysfs_class id=%p",clp);
+ clp->class.name = "pvrusb2";
+ clp->class.class_release = pvr2_sysfs_class_release;
+ clp->class.release = pvr2_sysfs_release;
+ clp->class.uevent = pvr2_sysfs_hotplug;
+ if (class_register(&clp->class)) {
+ pvr2_sysfs_trace(
+ "Registration failed for pvr2_sysfs_class id=%p",clp);
+ kfree(clp);
+ clp = 0;
+ }
+ return clp;
+}
+
+
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *clp)
+{
+ class_unregister(&clp->class);
+}
+
+
+#ifdef CONFIG_VIDEO_PVRUSB2_DEBUGIFC
+static ssize_t debuginfo_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ pvr2_hdw_trigger_module_log(sfp->channel.hdw);
+ return pvr2_debugifc_print_info(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_show(struct class_device *class_dev,char *buf)
+{
+ struct pvr2_sysfs *sfp;
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+ return pvr2_debugifc_print_status(sfp->channel.hdw,buf,PAGE_SIZE);
+}
+
+
+static ssize_t debugcmd_store(struct class_device *class_dev,
+ const char *buf,size_t count)
+{
+ struct pvr2_sysfs *sfp;
+ int ret;
+
+ sfp = (struct pvr2_sysfs *)class_dev->class_data;
+ if (!sfp) return -EINVAL;
+
+ ret = pvr2_debugifc_docmd(sfp->channel.hdw,buf,count);
+ if (ret < 0) return ret;
+ return count;
+}
+#endif /* CONFIG_VIDEO_PVRUSB2_DEBUGIFC */
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.h b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
new file mode 100644
index 000000000000..ff9373b47f8f
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.h
@@ -0,0 +1,47 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_SYSFS_H
+#define __PVRUSB2_SYSFS_H
+
+#include <linux/list.h>
+#include <linux/sysfs.h>
+#include "pvrusb2-context.h"
+
+struct pvr2_sysfs;
+struct pvr2_sysfs_class;
+
+struct pvr2_sysfs_class *pvr2_sysfs_class_create(void);
+void pvr2_sysfs_class_destroy(struct pvr2_sysfs_class *);
+
+struct pvr2_sysfs *pvr2_sysfs_create(struct pvr2_context *,
+ struct pvr2_sysfs_class *);
+
+#endif /* __PVRUSB2_SYSFS_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.c b/drivers/media/video/pvrusb2/pvrusb2-tuner.c
new file mode 100644
index 000000000000..f4aba8144ce0
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.c
@@ -0,0 +1,122 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 "pvrusb2.h"
+#include "pvrusb2-util.h"
+#include "pvrusb2-tuner.h"
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/tuner.h>
+#include <media/v4l2-common.h>
+
+struct pvr2_tuner_handler {
+ struct pvr2_hdw *hdw;
+ struct pvr2_i2c_client *client;
+ struct pvr2_i2c_handler i2c_handler;
+ int type_update_fl;
+};
+
+
+static void set_type(struct pvr2_tuner_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ struct tuner_setup setup;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c tuner set_type(%d)",hdw->tuner_type);
+ if (((int)(hdw->tuner_type)) < 0) return;
+
+ setup.addr = ADDR_UNSET;
+ setup.type = hdw->tuner_type;
+ setup.mode_mask = T_RADIO | T_ANALOG_TV;
+ /* We may really want mode_mask to be T_ANALOG_TV for now */
+ pvr2_i2c_client_cmd(ctxt->client,TUNER_SET_TYPE_ADDR,&setup);
+ ctxt->type_update_fl = 0;
+}
+
+
+static int tuner_check(struct pvr2_tuner_handler *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ if (hdw->tuner_updated) ctxt->type_update_fl = !0;
+ return ctxt->type_update_fl != 0;
+}
+
+
+static void tuner_update(struct pvr2_tuner_handler *ctxt)
+{
+ if (ctxt->type_update_fl) set_type(ctxt);
+}
+
+
+static void pvr2_tuner_detach(struct pvr2_tuner_handler *ctxt)
+{
+ ctxt->client->handler = 0;
+ kfree(ctxt);
+}
+
+
+static unsigned int pvr2_tuner_describe(struct pvr2_tuner_handler *ctxt,char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-tuner");
+}
+
+
+const static struct pvr2_i2c_handler_functions tuner_funcs = {
+ .detach = (void (*)(void *))pvr2_tuner_detach,
+ .check = (int (*)(void *))tuner_check,
+ .update = (void (*)(void *))tuner_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))pvr2_tuner_describe,
+};
+
+
+int pvr2_i2c_tuner_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+{
+ struct pvr2_tuner_handler *ctxt;
+ if (cp->handler) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->i2c_handler.func_data = ctxt;
+ ctxt->i2c_handler.func_table = &tuner_funcs;
+ ctxt->type_update_fl = !0;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ cp->handler = &ctxt->i2c_handler;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x tuner handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-tuner.h b/drivers/media/video/pvrusb2/pvrusb2-tuner.h
new file mode 100644
index 000000000000..556f12aa9160
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-tuner.h
@@ -0,0 +1,38 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_TUNER_H
+#define __PVRUSB2_TUNER_H
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_tuner_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+#endif /* __PVRUSB2_TUNER_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-util.h b/drivers/media/video/pvrusb2/pvrusb2-util.h
new file mode 100644
index 000000000000..e53aee416f56
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-util.h
@@ -0,0 +1,63 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_UTIL_H
+#define __PVRUSB2_UTIL_H
+
+#define PVR2_DECOMPOSE_LE(t,i,d) \
+ do { \
+ (t)[i] = (d) & 0xff;\
+ (t)[i+1] = ((d) >> 8) & 0xff;\
+ (t)[i+2] = ((d) >> 16) & 0xff;\
+ (t)[i+3] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_DECOMPOSE_BE(t,i,d) \
+ do { \
+ (t)[i+3] = (d) & 0xff;\
+ (t)[i+2] = ((d) >> 8) & 0xff;\
+ (t)[i+1] = ((d) >> 16) & 0xff;\
+ (t)[i] = ((d) >> 24) & 0xff;\
+ } while(0)
+
+#define PVR2_COMPOSE_LE(t,i) \
+ ((((u32)((t)[i+3])) << 24) | \
+ (((u32)((t)[i+2])) << 16) | \
+ (((u32)((t)[i+1])) << 8) | \
+ ((u32)((t)[i])))
+
+#define PVR2_COMPOSE_BE(t,i) \
+ ((((u32)((t)[i])) << 24) | \
+ (((u32)((t)[i+1])) << 16) | \
+ (((u32)((t)[i+2])) << 8) | \
+ ((u32)((t)[i+3])))
+
+
+#endif /* __PVRUSB2_UTIL_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
new file mode 100644
index 000000000000..961951010c27
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -0,0 +1,1126 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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 <linux/kernel.h>
+#include <linux/version.h>
+#include "pvrusb2-context.h"
+#include "pvrusb2-hdw.h"
+#include "pvrusb2.h"
+#include "pvrusb2-debug.h"
+#include "pvrusb2-v4l2.h"
+#include "pvrusb2-ioread.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+
+struct pvr2_v4l2_dev;
+struct pvr2_v4l2_fh;
+struct pvr2_v4l2;
+
+/* V4L no longer provide the ability to set / get a private context pointer
+ (i.e. video_get_drvdata / video_set_drvdata), which means we have to
+ concoct our own context locating mechanism. Supposedly this is intended
+ to simplify driver implementation. It's not clear to me how that can
+ possibly be true. Our solution here is to maintain a lookup table of
+ our context instances, indexed by the minor device number of the V4L
+ device. See pvr2_v4l2_open() for some implications of this approach. */
+static struct pvr2_v4l2_dev *devices[256];
+static DEFINE_MUTEX(device_lock);
+
+struct pvr2_v4l2_dev {
+ struct pvr2_v4l2 *v4lp;
+ struct video_device *vdev;
+ struct pvr2_context_stream *stream;
+ int ctxt_idx;
+ enum pvr2_config config;
+};
+
+struct pvr2_v4l2_fh {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_dev *dev_info;
+ enum v4l2_priority prio;
+ struct pvr2_ioread *rhp;
+ struct file *file;
+ struct pvr2_v4l2 *vhead;
+ struct pvr2_v4l2_fh *vnext;
+ struct pvr2_v4l2_fh *vprev;
+ wait_queue_head_t wait_data;
+ int fw_mode_flag;
+};
+
+struct pvr2_v4l2 {
+ struct pvr2_channel channel;
+ struct pvr2_v4l2_fh *vfirst;
+ struct pvr2_v4l2_fh *vlast;
+
+ struct v4l2_prio_state prio;
+
+ /* streams */
+ struct pvr2_v4l2_dev video_dev;
+};
+
+static int video_nr[PVR_NUM] = {[0 ... PVR_NUM-1] = -1};
+module_param_array(video_nr, int, NULL, 0444);
+MODULE_PARM_DESC(video_nr, "Offset for device's minor");
+
+struct v4l2_capability pvr_capability ={
+ .driver = "pvrusb2",
+ .card = "Hauppauge WinTV pvr-usb2",
+ .bus_info = "usb",
+ .version = KERNEL_VERSION(0,8,0),
+ .capabilities = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE |
+ V4L2_CAP_TUNER | V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE),
+ .reserved = {0,0,0,0}
+};
+
+static struct v4l2_tuner pvr_v4l2_tuners[]= {
+ {
+ .index = 0,
+ .name = "TV Tuner",
+ .type = V4L2_TUNER_ANALOG_TV,
+ .capability = (V4L2_TUNER_CAP_NORM |
+ V4L2_TUNER_CAP_STEREO |
+ V4L2_TUNER_CAP_LANG1 |
+ V4L2_TUNER_CAP_LANG2),
+ .rangelow = 0,
+ .rangehigh = 0,
+ .rxsubchans = V4L2_TUNER_SUB_STEREO,
+ .audmode = V4L2_TUNER_MODE_STEREO,
+ .signal = 0,
+ .afc = 0,
+ .reserved = {0,0,0,0}
+ }
+};
+
+struct v4l2_fmtdesc pvr_fmtdesc [] = {
+ {
+ .index = 0,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .flags = V4L2_FMT_FLAG_COMPRESSED,
+ .description = "MPEG1/2",
+ // This should really be V4L2_PIX_FMT_MPEG, but xawtv
+ // breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ .reserved = { 0, 0, 0, 0 }
+ }
+};
+
+#define PVR_FORMAT_PIX 0
+#define PVR_FORMAT_VBI 1
+
+struct v4l2_format pvr_format [] = {
+ [PVR_FORMAT_PIX] = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt = {
+ .pix = {
+ .width = 720,
+ .height = 576,
+ // This should really be V4L2_PIX_FMT_MPEG,
+ // but xawtv breaks when I do that.
+ .pixelformat = 0, // V4L2_PIX_FMT_MPEG,
+ .field = V4L2_FIELD_INTERLACED,
+ .bytesperline = 0, // doesn't make sense
+ // here
+ //FIXME : Don't know what to put here...
+ .sizeimage = (32*1024),
+ .colorspace = 0, // doesn't make sense here
+ .priv = 0
+ }
+ }
+ },
+ [PVR_FORMAT_VBI] = {
+ .type = V4L2_BUF_TYPE_VBI_CAPTURE,
+ .fmt = {
+ .vbi = {
+ .sampling_rate = 27000000,
+ .offset = 248,
+ .samples_per_line = 1443,
+ .sample_format = V4L2_PIX_FMT_GREY,
+ .start = { 0, 0 },
+ .count = { 0, 0 },
+ .flags = 0,
+ .reserved = { 0, 0 }
+ }
+ }
+ }
+};
+
+/*
+ * pvr_ioctl()
+ *
+ * This is part of Video 4 Linux API. The procedure handles ioctl() calls.
+ *
+ */
+static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, void *arg)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ struct pvr2_v4l2 *vp = fh->vhead;
+ struct pvr2_v4l2_dev *dev_info = fh->dev_info;
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ int ret = -EINVAL;
+
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),cmd);
+ }
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_ERROR_LEGS,
+ "ioctl failed - bad or no context");
+ return -EFAULT;
+ }
+
+ /* check priority */
+ switch (cmd) {
+ case VIDIOC_S_CTRL:
+ case VIDIOC_S_STD:
+ case VIDIOC_S_INPUT:
+ case VIDIOC_S_TUNER:
+ case VIDIOC_S_FREQUENCY:
+ ret = v4l2_prio_check(&vp->prio, &fh->prio);
+ if (ret)
+ return ret;
+ }
+
+ switch (cmd) {
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *cap = arg;
+
+ memcpy(cap, &pvr_capability, sizeof(struct v4l2_capability));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_PRIORITY:
+ {
+ enum v4l2_priority *p = arg;
+
+ *p = v4l2_prio_max(&vp->prio);
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_S_PRIORITY:
+ {
+ enum v4l2_priority *prio = arg;
+
+ ret = v4l2_prio_change(&vp->prio, &fh->prio, *prio);
+ break;
+ }
+
+ case VIDIOC_ENUMSTD:
+ {
+ struct v4l2_standard *vs = (struct v4l2_standard *)arg;
+ int idx = vs->index;
+ ret = pvr2_hdw_get_stdenum_value(hdw,vs,idx+1);
+ break;
+ }
+
+ case VIDIOC_G_STD:
+ {
+ int val = 0;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),&val);
+ *(v4l2_std_id *)arg = val;
+ break;
+ }
+
+ case VIDIOC_S_STD:
+ {
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_STDCUR),
+ *(v4l2_std_id *)arg);
+ break;
+ }
+
+ case VIDIOC_ENUMINPUT:
+ {
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ struct v4l2_input tmp;
+ unsigned int cnt;
+
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+
+ memset(&tmp,0,sizeof(tmp));
+ tmp.index = vi->index;
+ ret = 0;
+ switch (vi->index) {
+ case PVR2_CVAL_INPUT_TV:
+ case PVR2_CVAL_INPUT_RADIO:
+ tmp.type = V4L2_INPUT_TYPE_TUNER;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ tmp.type = V4L2_INPUT_TYPE_CAMERA;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ if (ret < 0) break;
+
+ cnt = 0;
+ pvr2_ctrl_get_valname(cptr,vi->index,
+ tmp.name,sizeof(tmp.name)-1,&cnt);
+ tmp.name[cnt] = 0;
+
+ /* Don't bother with audioset, since this driver currently
+ always switches the audio whenever the video is
+ switched. */
+
+ /* Handling std is a tougher problem. It doesn't make
+ sense in cases where a device might be multi-standard.
+ We could just copy out the current value for the
+ standard, but it can change over time. For now just
+ leave it zero. */
+
+ memcpy(vi, &tmp, sizeof(tmp));
+
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_INPUT:
+ {
+ struct pvr2_ctrl *cptr;
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ int val;
+ cptr = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT);
+ val = 0;
+ ret = pvr2_ctrl_get_value(cptr,&val);
+ vi->index = val;
+ break;
+ }
+
+ case VIDIOC_S_INPUT:
+ {
+ struct v4l2_input *vi = (struct v4l2_input *)arg;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_INPUT),
+ vi->index);
+ break;
+ }
+
+ case VIDIOC_ENUMAUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOC_G_AUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+
+ case VIDIOC_S_AUDIO:
+ {
+ ret = -EINVAL;
+ break;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *vt = (struct v4l2_tuner *)arg;
+ unsigned int status_mask;
+ int val;
+ if (vt->index !=0) break;
+
+ status_mask = pvr2_hdw_get_signal_status(hdw);
+
+ memcpy(vt, &pvr_v4l2_tuners[vt->index],
+ sizeof(struct v4l2_tuner));
+
+ vt->signal = 0;
+ if (status_mask & PVR2_SIGNAL_OK) {
+ if (status_mask & PVR2_SIGNAL_STEREO) {
+ vt->rxsubchans = V4L2_TUNER_SUB_STEREO;
+ } else {
+ vt->rxsubchans = V4L2_TUNER_SUB_MONO;
+ }
+ if (status_mask & PVR2_SIGNAL_SAP) {
+ vt->rxsubchans |= (V4L2_TUNER_SUB_LANG1 |
+ V4L2_TUNER_SUB_LANG2);
+ }
+ vt->signal = 65535;
+ }
+
+ val = 0;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE),
+ &val);
+ vt->audmode = val;
+ break;
+ }
+
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *vt=(struct v4l2_tuner *)arg;
+
+ if (vt->index != 0)
+ break;
+
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_AUDIOMODE),
+ vt->audmode);
+ }
+
+ case VIDIOC_S_FREQUENCY:
+ {
+ const struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),
+ vf->frequency * 62500);
+ break;
+ }
+
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *vf = (struct v4l2_frequency *)arg;
+ int val = 0;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_FREQUENCY),
+ &val);
+ val /= 62500;
+ vf->frequency = val;
+ break;
+ }
+
+ case VIDIOC_ENUM_FMT:
+ {
+ struct v4l2_fmtdesc *fd = (struct v4l2_fmtdesc *)arg;
+
+ /* Only one format is supported : mpeg.*/
+ if (fd->index != 0)
+ break;
+
+ memcpy(fd, pvr_fmtdesc, sizeof(struct v4l2_fmtdesc));
+ ret = 0;
+ break;
+ }
+
+ case VIDIOC_G_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+ int val;
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE:
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES),
+ &val);
+ vf->fmt.pix.width = val;
+ val = 0;
+ pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES),
+ &val);
+ vf->fmt.pix.height = val;
+ ret = 0;
+ break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT:
+ {
+ struct v4l2_format *vf = (struct v4l2_format *)arg;
+
+ ret = 0;
+ switch(vf->type) {
+ case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ int h = vf->fmt.pix.height;
+ int w = vf->fmt.pix.width;
+
+ if (h < 200) {
+ h = 200;
+ } else if (h > 625) {
+ h = 625;
+ }
+ if (w < 320) {
+ w = 320;
+ } else if (w > 720) {
+ w = 720;
+ }
+
+ memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
+ sizeof(struct v4l2_format));
+ vf->fmt.pix.width = w;
+ vf->fmt.pix.height = h;
+
+ if (cmd == VIDIOC_S_FMT) {
+ pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,
+ PVR2_CID_HRES),
+ vf->fmt.pix.width);
+ pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_by_id(hdw,
+ PVR2_CID_VRES),
+ vf->fmt.pix.height);
+ }
+ } break;
+ case V4L2_BUF_TYPE_VBI_CAPTURE:
+ // ????? Still need to figure out to do VBI correctly
+ ret = -EINVAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_STREAMON:
+ {
+ ret = pvr2_hdw_set_stream_type(hdw,dev_info->config);
+ if (ret < 0) return ret;
+ ret = pvr2_hdw_set_streaming(hdw,!0);
+ break;
+ }
+
+ case VIDIOC_STREAMOFF:
+ {
+ ret = pvr2_hdw_set_streaming(hdw,0);
+ break;
+ }
+
+ case VIDIOC_QUERYCTRL:
+ {
+ struct pvr2_ctrl *cptr;
+ struct v4l2_queryctrl *vc = (struct v4l2_queryctrl *)arg;
+ ret = 0;
+ if (vc->id & V4L2_CTRL_FLAG_NEXT_CTRL) {
+ cptr = pvr2_hdw_get_ctrl_nextv4l(
+ hdw,(vc->id & ~V4L2_CTRL_FLAG_NEXT_CTRL));
+ if (cptr) vc->id = pvr2_ctrl_get_v4lid(cptr);
+ } else {
+ cptr = pvr2_hdw_get_ctrl_v4l(hdw,vc->id);
+ }
+ if (!cptr) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x not implemented here",
+ vc->id);
+ ret = -EINVAL;
+ break;
+ }
+
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x mapping name=%s (%s)",
+ vc->id,pvr2_ctrl_get_name(cptr),
+ pvr2_ctrl_get_desc(cptr));
+ strlcpy(vc->name,pvr2_ctrl_get_desc(cptr),sizeof(vc->name));
+ vc->flags = pvr2_ctrl_get_v4lflags(cptr);
+ vc->default_value = pvr2_ctrl_get_def(cptr);
+ switch (pvr2_ctrl_get_type(cptr)) {
+ case pvr2_ctl_enum:
+ vc->type = V4L2_CTRL_TYPE_MENU;
+ vc->minimum = 0;
+ vc->maximum = pvr2_ctrl_get_cnt(cptr) - 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_bool:
+ vc->type = V4L2_CTRL_TYPE_BOOLEAN;
+ vc->minimum = 0;
+ vc->maximum = 1;
+ vc->step = 1;
+ break;
+ case pvr2_ctl_int:
+ vc->type = V4L2_CTRL_TYPE_INTEGER;
+ vc->minimum = pvr2_ctrl_get_min(cptr);
+ vc->maximum = pvr2_ctrl_get_max(cptr);
+ vc->step = 1;
+ break;
+ default:
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "QUERYCTRL id=0x%x name=%s not mappable",
+ vc->id,pvr2_ctrl_get_name(cptr));
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+
+ case VIDIOC_QUERYMENU:
+ {
+ struct v4l2_querymenu *vm = (struct v4l2_querymenu *)arg;
+ unsigned int cnt = 0;
+ ret = pvr2_ctrl_get_valname(pvr2_hdw_get_ctrl_v4l(hdw,vm->id),
+ vm->index,
+ vm->name,sizeof(vm->name)-1,
+ &cnt);
+ vm->name[cnt] = 0;
+ break;
+ }
+
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ int val = 0;
+ ret = pvr2_ctrl_get_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
+ &val);
+ vc->value = val;
+ break;
+ }
+
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *vc = (struct v4l2_control *)arg;
+ ret = pvr2_ctrl_set_value(pvr2_hdw_get_ctrl_v4l(hdw,vc->id),
+ vc->value);
+ break;
+ }
+
+ case VIDIOC_G_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ int val;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_get_value(
+ pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),&val);
+ if (ret) {
+ ctls->error_idx = idx;
+ break;
+ }
+ /* Ensure that if read as a 64 bit value, the user
+ will still get a hopefully sane value */
+ ctrl->value64 = 0;
+ ctrl->value = val;
+ }
+ break;
+ }
+
+ case VIDIOC_S_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ unsigned int idx;
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ ret = pvr2_ctrl_set_value(
+ pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id),
+ ctrl->value);
+ if (ret) {
+ ctls->error_idx = idx;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_TRY_EXT_CTRLS:
+ {
+ struct v4l2_ext_controls *ctls =
+ (struct v4l2_ext_controls *)arg;
+ struct v4l2_ext_control *ctrl;
+ struct pvr2_ctrl *pctl;
+ unsigned int idx;
+ /* For the moment just validate that the requested control
+ actually exists. */
+ for (idx = 0; idx < ctls->count; idx++) {
+ ctrl = ctls->controls + idx;
+ pctl = pvr2_hdw_get_ctrl_v4l(hdw,ctrl->id);
+ if (!pctl) {
+ ret = -EINVAL;
+ ctls->error_idx = idx;
+ break;
+ }
+ }
+ break;
+ }
+
+ case VIDIOC_LOG_STATUS:
+ {
+ pvr2_hdw_trigger_module_log(hdw);
+ ret = 0;
+ break;
+ }
+
+ default :
+ ret = v4l_compat_translate_ioctl(inode,file,cmd,
+ arg,pvr2_v4l2_do_ioctl);
+ }
+
+ pvr2_hdw_commit_ctl(hdw);
+
+ if (ret < 0) {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%d",ret);
+ } else {
+ if (pvrusb2_debug & PVR2_TRACE_V4LIOCTL) {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl failure, ret=%d"
+ " command was:",ret);
+ v4l_print_ioctl(pvr2_hdw_get_driver_name(hdw),
+ cmd);
+ }
+ }
+ } else {
+ pvr2_trace(PVR2_TRACE_V4LIOCTL,
+ "pvr2_v4l2_do_ioctl complete, ret=%d (0x%x)",
+ ret,ret);
+ }
+ return ret;
+}
+
+
+static void pvr2_v4l2_dev_destroy(struct pvr2_v4l2_dev *dip)
+{
+ pvr2_trace(PVR2_TRACE_INIT,
+ "unregistering device video%d [%s]",
+ dip->vdev->minor,pvr2_config_get_name(dip->config));
+ if (dip->ctxt_idx >= 0) {
+ mutex_lock(&device_lock);
+ devices[dip->ctxt_idx] = NULL;
+ dip->ctxt_idx = -1;
+ mutex_unlock(&device_lock);
+ }
+ video_unregister_device(dip->vdev);
+}
+
+
+static void pvr2_v4l2_destroy_no_lock(struct pvr2_v4l2 *vp)
+{
+ pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,-1);
+ pvr2_v4l2_dev_destroy(&vp->video_dev);
+
+ pvr2_trace(PVR2_TRACE_STRUCT,"Destroying pvr2_v4l2 id=%p",vp);
+ pvr2_channel_done(&vp->channel);
+ kfree(vp);
+}
+
+
+void pvr2_v4l2_internal_check(struct pvr2_channel *chp)
+{
+ struct pvr2_v4l2 *vp;
+ vp = container_of(chp,struct pvr2_v4l2,channel);
+ if (!vp->channel.mc_head->disconnect_flag) return;
+ if (vp->vfirst) return;
+ pvr2_v4l2_destroy_no_lock(vp);
+}
+
+
+int pvr2_v4l2_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+
+/* Temporary hack : use ivtv api until a v4l2 one is available. */
+#define IVTV_IOC_G_CODEC 0xFFEE7703
+#define IVTV_IOC_S_CODEC 0xFFEE7704
+ if (cmd == IVTV_IOC_G_CODEC || cmd == IVTV_IOC_S_CODEC) return 0;
+ return video_usercopy(inode, file, cmd, arg, pvr2_v4l2_do_ioctl);
+}
+
+
+int pvr2_v4l2_release(struct inode *inode, struct file *file)
+{
+ struct pvr2_v4l2_fh *fhp = file->private_data;
+ struct pvr2_v4l2 *vp = fhp->vhead;
+ struct pvr2_context *mp = fhp->vhead->channel.mc_head;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_release");
+
+ if (fhp->rhp) {
+ struct pvr2_stream *sp;
+ struct pvr2_hdw *hdw;
+ hdw = fhp->channel.mc_head->hdw;
+ pvr2_hdw_set_streaming(hdw,0);
+ sp = pvr2_ioread_get_stream(fhp->rhp);
+ if (sp) pvr2_stream_set_callback(sp,0,0);
+ pvr2_ioread_destroy(fhp->rhp);
+ fhp->rhp = 0;
+ }
+ v4l2_prio_close(&vp->prio, &fhp->prio);
+ file->private_data = NULL;
+
+ pvr2_context_enter(mp); do {
+ if (fhp->vnext) {
+ fhp->vnext->vprev = fhp->vprev;
+ } else {
+ vp->vlast = fhp->vprev;
+ }
+ if (fhp->vprev) {
+ fhp->vprev->vnext = fhp->vnext;
+ } else {
+ vp->vfirst = fhp->vnext;
+ }
+ fhp->vnext = 0;
+ fhp->vprev = 0;
+ fhp->vhead = 0;
+ pvr2_channel_done(&fhp->channel);
+ pvr2_trace(PVR2_TRACE_STRUCT,
+ "Destroying pvr_v4l2_fh id=%p",fhp);
+ kfree(fhp);
+ if (vp->channel.mc_head->disconnect_flag && !vp->vfirst) {
+ pvr2_v4l2_destroy_no_lock(vp);
+ }
+ } while (0); pvr2_context_exit(mp);
+ return 0;
+}
+
+
+int pvr2_v4l2_open(struct inode *inode, struct file *file)
+{
+ struct pvr2_v4l2_dev *dip = 0; /* Our own context pointer */
+ struct pvr2_v4l2_fh *fhp;
+ struct pvr2_v4l2 *vp;
+ struct pvr2_hdw *hdw;
+
+ mutex_lock(&device_lock);
+ /* MCI 7-Jun-2006 Even though we're just doing what amounts to an
+ atomic read of the device mapping array here, we still need the
+ mutex. The problem is that there is a tiny race possible when
+ we register the device. We can't update the device mapping
+ array until after the device has been registered, owing to the
+ fact that we can't know the minor device number until after the
+ registration succeeds. And if another thread tries to open the
+ device in the window of time after registration but before the
+ map is updated, then it will get back an erroneous null pointer
+ and the open will result in a spurious failure. The only way to
+ prevent that is to (a) be inside the mutex here before we access
+ the array, and (b) cover the entire registration process later
+ on with this same mutex. Thus if we get inside the mutex here,
+ then we can be assured that the registration process actually
+ completed correctly. This is an unhappy complication from the
+ use of global data in a driver that lives in a preemptible
+ environment. It sure would be nice if the video device itself
+ had a means for storing and retrieving a local context pointer.
+ Oh wait. It did. But now it's gone. Silly me. */
+ {
+ unsigned int midx = iminor(file->f_dentry->d_inode);
+ if (midx < sizeof(devices)/sizeof(devices[0])) {
+ dip = devices[midx];
+ }
+ }
+ mutex_unlock(&device_lock);
+
+ if (!dip) return -ENODEV; /* Should be impossible but I'm paranoid */
+
+ vp = dip->v4lp;
+ hdw = vp->channel.hdw;
+
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,"pvr2_v4l2_open");
+
+ if (!pvr2_hdw_dev_ok(hdw)) {
+ pvr2_trace(PVR2_TRACE_OPEN_CLOSE,
+ "pvr2_v4l2_open: hardware not ready");
+ return -EIO;
+ }
+
+ fhp = kmalloc(sizeof(*fhp),GFP_KERNEL);
+ if (!fhp) {
+ return -ENOMEM;
+ }
+ memset(fhp,0,sizeof(*fhp));
+
+ init_waitqueue_head(&fhp->wait_data);
+ fhp->dev_info = dip;
+
+ pvr2_context_enter(vp->channel.mc_head); do {
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr_v4l2_fh id=%p",fhp);
+ pvr2_channel_init(&fhp->channel,vp->channel.mc_head);
+ fhp->vnext = 0;
+ fhp->vprev = vp->vlast;
+ if (vp->vlast) {
+ vp->vlast->vnext = fhp;
+ } else {
+ vp->vfirst = fhp;
+ }
+ vp->vlast = fhp;
+ fhp->vhead = vp;
+ } while (0); pvr2_context_exit(vp->channel.mc_head);
+
+ fhp->file = file;
+ file->private_data = fhp;
+ v4l2_prio_open(&vp->prio,&fhp->prio);
+
+ fhp->fw_mode_flag = pvr2_hdw_cpufw_get_enabled(hdw);
+
+ return 0;
+}
+
+
+static void pvr2_v4l2_notify(struct pvr2_v4l2_fh *fhp)
+{
+ wake_up(&fhp->wait_data);
+}
+
+static int pvr2_v4l2_iosetup(struct pvr2_v4l2_fh *fh)
+{
+ int ret;
+ struct pvr2_stream *sp;
+ struct pvr2_hdw *hdw;
+ if (fh->rhp) return 0;
+
+ /* First read() attempt. Try to claim the stream and start
+ it... */
+ if ((ret = pvr2_channel_claim_stream(&fh->channel,
+ fh->dev_info->stream)) != 0) {
+ /* Someone else must already have it */
+ return ret;
+ }
+
+ fh->rhp = pvr2_channel_create_mpeg_stream(fh->dev_info->stream);
+ if (!fh->rhp) {
+ pvr2_channel_claim_stream(&fh->channel,0);
+ return -ENOMEM;
+ }
+
+ hdw = fh->channel.mc_head->hdw;
+ sp = fh->dev_info->stream->stream;
+ pvr2_stream_set_callback(sp,(pvr2_stream_callback)pvr2_v4l2_notify,fh);
+ pvr2_hdw_set_stream_type(hdw,fh->dev_info->config);
+ pvr2_hdw_set_streaming(hdw,!0);
+ ret = pvr2_ioread_set_enabled(fh->rhp,!0);
+
+ return ret;
+}
+
+
+static ssize_t pvr2_v4l2_read(struct file *file,
+ char __user *buff, size_t count, loff_t *ppos)
+{
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ struct pvr2_hdw *hdw = fh->channel.mc_head->hdw;
+ char *tbuf;
+ int c1,c2;
+ int tcnt = 0;
+ unsigned int offs = *ppos;
+
+ tbuf = kmalloc(PAGE_SIZE,GFP_KERNEL);
+ if (!tbuf) return -ENOMEM;
+
+ while (count) {
+ c1 = count;
+ if (c1 > PAGE_SIZE) c1 = PAGE_SIZE;
+ c2 = pvr2_hdw_cpufw_get(hdw,offs,tbuf,c1);
+ if (c2 < 0) {
+ tcnt = c2;
+ break;
+ }
+ if (!c2) break;
+ if (copy_to_user(buff,tbuf,c2)) {
+ tcnt = -EFAULT;
+ break;
+ }
+ offs += c2;
+ tcnt += c2;
+ buff += c2;
+ count -= c2;
+ *ppos += c2;
+ }
+ kfree(tbuf);
+ return tcnt;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) {
+ return ret;
+ }
+ }
+
+ for (;;) {
+ ret = pvr2_ioread_read(fh->rhp,buff,count);
+ if (ret >= 0) break;
+ if (ret != -EAGAIN) break;
+ if (file->f_flags & O_NONBLOCK) break;
+ /* Doing blocking I/O. Wait here. */
+ ret = wait_event_interruptible(
+ fh->wait_data,
+ pvr2_ioread_avail(fh->rhp) >= 0);
+ if (ret < 0) break;
+ }
+
+ return ret;
+}
+
+
+static unsigned int pvr2_v4l2_poll(struct file *file, poll_table *wait)
+{
+ unsigned int mask = 0;
+ struct pvr2_v4l2_fh *fh = file->private_data;
+ int ret;
+
+ if (fh->fw_mode_flag) {
+ mask |= POLLIN | POLLRDNORM;
+ return mask;
+ }
+
+ if (!fh->rhp) {
+ ret = pvr2_v4l2_iosetup(fh);
+ if (ret) return POLLERR;
+ }
+
+ poll_wait(file,&fh->wait_data,wait);
+
+ if (pvr2_ioread_avail(fh->rhp) >= 0) {
+ mask |= POLLIN | POLLRDNORM;
+ }
+
+ return mask;
+}
+
+
+static struct file_operations vdev_fops = {
+ .owner = THIS_MODULE,
+ .open = pvr2_v4l2_open,
+ .release = pvr2_v4l2_release,
+ .read = pvr2_v4l2_read,
+ .ioctl = pvr2_v4l2_ioctl,
+ .llseek = no_llseek,
+ .poll = pvr2_v4l2_poll,
+};
+
+
+#define VID_HARDWARE_PVRUSB2 38 /* FIXME : need a good value */
+
+static struct video_device vdev_template = {
+ .owner = THIS_MODULE,
+ .type = VID_TYPE_CAPTURE | VID_TYPE_TUNER,
+ .type2 = (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VBI_CAPTURE
+ | V4L2_CAP_TUNER | V4L2_CAP_AUDIO
+ | V4L2_CAP_READWRITE),
+ .hardware = VID_HARDWARE_PVRUSB2,
+ .fops = &vdev_fops,
+};
+
+
+static void pvr2_v4l2_dev_init(struct pvr2_v4l2_dev *dip,
+ struct pvr2_v4l2 *vp,
+ enum pvr2_config cfg)
+{
+ int mindevnum;
+ int unit_number;
+ int v4l_type;
+ dip->v4lp = vp;
+ dip->config = cfg;
+
+
+ switch (cfg) {
+ case pvr2_config_mpeg:
+ v4l_type = VFL_TYPE_GRABBER;
+ dip->stream = &vp->channel.mc_head->video_stream;
+ break;
+ case pvr2_config_vbi:
+ v4l_type = VFL_TYPE_VBI;
+ break;
+ case pvr2_config_radio:
+ v4l_type = VFL_TYPE_RADIO;
+ break;
+ default:
+ /* Bail out (this should be impossible) */
+ err("Failed to set up pvrusb2 v4l dev"
+ " due to unrecognized config");
+ return;
+ }
+
+ if (!dip->stream) {
+ err("Failed to set up pvrusb2 v4l dev"
+ " due to missing stream instance");
+ return;
+ }
+
+ dip->vdev = video_device_alloc();
+ if (!dip->vdev) {
+ err("Alloc of pvrusb2 v4l video device failed");
+ return;
+ }
+
+ memcpy(dip->vdev,&vdev_template,sizeof(vdev_template));
+ dip->vdev->release = video_device_release;
+ mutex_lock(&device_lock);
+
+ mindevnum = -1;
+ unit_number = pvr2_hdw_get_unit_number(vp->channel.mc_head->hdw);
+ if ((unit_number >= 0) && (unit_number < PVR_NUM)) {
+ mindevnum = video_nr[unit_number];
+ }
+ if ((video_register_device(dip->vdev, v4l_type, mindevnum) < 0) &&
+ (video_register_device(dip->vdev, v4l_type, -1) < 0)) {
+ err("Failed to register pvrusb2 v4l video device");
+ } else {
+ pvr2_trace(PVR2_TRACE_INIT,
+ "registered device video%d [%s]",
+ dip->vdev->minor,pvr2_config_get_name(dip->config));
+ }
+
+ if ((dip->vdev->minor < sizeof(devices)/sizeof(devices[0])) &&
+ (devices[dip->vdev->minor] == NULL)) {
+ dip->ctxt_idx = dip->vdev->minor;
+ devices[dip->ctxt_idx] = dip;
+ }
+ mutex_unlock(&device_lock);
+
+ pvr2_hdw_v4l_store_minor_number(vp->channel.mc_head->hdw,
+ dip->vdev->minor);
+}
+
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *mnp)
+{
+ struct pvr2_v4l2 *vp;
+
+ vp = kmalloc(sizeof(*vp),GFP_KERNEL);
+ if (!vp) return vp;
+ memset(vp,0,sizeof(*vp));
+ vp->video_dev.ctxt_idx = -1;
+ pvr2_channel_init(&vp->channel,mnp);
+ pvr2_trace(PVR2_TRACE_STRUCT,"Creating pvr2_v4l2 id=%p",vp);
+
+ vp->channel.check_func = pvr2_v4l2_internal_check;
+
+ /* register streams */
+ pvr2_v4l2_dev_init(&vp->video_dev,vp,pvr2_config_mpeg);
+
+
+ return vp;
+}
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.h b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
new file mode 100644
index 000000000000..9a995e2d2256
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.h
@@ -0,0 +1,40 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.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
+ *
+ * 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
+ *
+ */
+#ifndef __PVRUSB2_V4L2_H
+#define __PVRUSB2_V4L2_H
+
+#include "pvrusb2-context.h"
+
+struct pvr2_v4l2;
+
+struct pvr2_v4l2 *pvr2_v4l2_create(struct pvr2_context *);
+
+#endif /* __PVRUSB2_V4L2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 75 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
new file mode 100644
index 000000000000..e4ec7f25194c
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.c
@@ -0,0 +1,253 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ saa711x support that is available in the v4l available starting
+ with linux 2.6.15.
+
+*/
+
+#include "pvrusb2-video-v4l.h"
+#include "pvrusb2-i2c-cmd-v4l2.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <media/saa7115.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+struct pvr2_v4l_decoder {
+ struct pvr2_i2c_handler handler;
+ struct pvr2_decoder_ctrl ctrl;
+ struct pvr2_i2c_client *client;
+ struct pvr2_hdw *hdw;
+ unsigned long stale_mask;
+};
+
+
+static void set_input(struct pvr2_v4l_decoder *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ struct v4l2_routing route;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_input(%d)",hdw->input_val);
+ switch(hdw->input_val) {
+ case PVR2_CVAL_INPUT_TV:
+ route.input = SAA7115_COMPOSITE4;
+ break;
+ case PVR2_CVAL_INPUT_COMPOSITE:
+ route.input = SAA7115_COMPOSITE5;
+ break;
+ case PVR2_CVAL_INPUT_SVIDEO:
+ route.input = SAA7115_SVIDEO2;
+ break;
+ case PVR2_CVAL_INPUT_RADIO:
+ // ????? No idea yet what to do here
+ default:
+ return;
+ }
+ route.output = 0;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_VIDEO_ROUTING,&route);
+}
+
+
+static int check_input(struct pvr2_v4l_decoder *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return hdw->input_dirty != 0;
+}
+
+
+static void set_audio(struct pvr2_v4l_decoder *ctxt)
+{
+ u32 val;
+ struct pvr2_hdw *hdw = ctxt->hdw;
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 set_audio %d",
+ hdw->srate_val);
+ switch (hdw->srate_val) {
+ default:
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000:
+ val = 48000;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100:
+ val = 44100;
+ break;
+ case V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000:
+ val = 32000;
+ break;
+ }
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_AUDIO_CLOCK_FREQ,&val);
+}
+
+
+static int check_audio(struct pvr2_v4l_decoder *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return hdw->srate_dirty != 0;
+}
+
+
+struct pvr2_v4l_decoder_ops {
+ void (*update)(struct pvr2_v4l_decoder *);
+ int (*check)(struct pvr2_v4l_decoder *);
+};
+
+
+static const struct pvr2_v4l_decoder_ops decoder_ops[] = {
+ { .update = set_input, .check = check_input},
+ { .update = set_audio, .check = check_audio},
+};
+
+
+static void decoder_detach(struct pvr2_v4l_decoder *ctxt)
+{
+ ctxt->client->handler = 0;
+ ctxt->hdw->decoder_ctrl = 0;
+ kfree(ctxt);
+}
+
+
+static int decoder_check(struct pvr2_v4l_decoder *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (ctxt->stale_mask & msk) continue;
+ if (decoder_ops[idx].check(ctxt)) {
+ ctxt->stale_mask |= msk;
+ }
+ }
+ return ctxt->stale_mask != 0;
+}
+
+
+static void decoder_update(struct pvr2_v4l_decoder *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(decoder_ops)/sizeof(decoder_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (!(ctxt->stale_mask & msk)) continue;
+ ctxt->stale_mask &= ~msk;
+ decoder_ops[idx].update(ctxt);
+ }
+}
+
+
+static int decoder_detect(struct pvr2_i2c_client *cp)
+{
+ /* Attempt to query the decoder - let's see if it will answer */
+ struct v4l2_tuner vt;
+ int ret;
+
+ memset(&vt,0,sizeof(vt));
+ ret = pvr2_i2c_client_cmd(cp,VIDIOC_G_TUNER,&vt);
+ return ret == 0; /* Return true if it answered */
+}
+
+
+static void decoder_enable(struct pvr2_v4l_decoder *ctxt,int fl)
+{
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c v4l2 decoder_enable(%d)",fl);
+ pvr2_v4l2_cmd_stream(ctxt->client,fl);
+}
+
+
+static int decoder_is_tuned(struct pvr2_v4l_decoder *ctxt)
+{
+ struct v4l2_tuner vt;
+ int ret;
+
+ memset(&vt,0,sizeof(vt));
+ ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_G_TUNER,&vt);
+ if (ret < 0) return -EINVAL;
+ return vt.signal ? 1 : 0;
+}
+
+
+static unsigned int decoder_describe(struct pvr2_v4l_decoder *ctxt,char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-video-v4l");
+}
+
+
+const static struct pvr2_i2c_handler_functions hfuncs = {
+ .detach = (void (*)(void *))decoder_detach,
+ .check = (int (*)(void *))decoder_check,
+ .update = (void (*)(void *))decoder_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))decoder_describe,
+};
+
+
+int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *hdw,
+ struct pvr2_i2c_client *cp)
+{
+ struct pvr2_v4l_decoder *ctxt;
+
+ if (hdw->decoder_ctrl) return 0;
+ if (cp->handler) return 0;
+ if (!decoder_detect(cp)) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->handler.func_data = ctxt;
+ ctxt->handler.func_table = &hfuncs;
+ ctxt->ctrl.ctxt = ctxt;
+ ctxt->ctrl.detach = (void (*)(void *))decoder_detach;
+ ctxt->ctrl.enable = (void (*)(void *,int))decoder_enable;
+ ctxt->ctrl.tuned = (int (*)(void *))decoder_is_tuned;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ ctxt->stale_mask = (1 << (sizeof(decoder_ops)/
+ sizeof(decoder_ops[0]))) - 1;
+ hdw->decoder_ctrl = &ctxt->ctrl;
+ cp->handler = &ctxt->handler;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x saa711x V4L2 handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
new file mode 100644
index 000000000000..2b917fda02e4
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-video-v4l.h
@@ -0,0 +1,52 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_VIDEO_V4L_H
+#define __PVRUSB2_VIDEO_V4L_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which handles device video processing. This interface is
+ used internally by the driver; higher level code should only
+ interact through the interface provided by pvrusb2-hdw.h.
+
+*/
+
+
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_decoder_v4l_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+
+#endif /* __PVRUSB2_VIDEO_V4L_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.c b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
new file mode 100644
index 000000000000..fcad346e3955
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.c
@@ -0,0 +1,170 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+/*
+
+ This source file is specifically designed to interface with the
+ wm8775.
+
+*/
+
+#include "pvrusb2-wm8775.h"
+#include "pvrusb2-i2c-cmd-v4l2.h"
+
+
+#include "pvrusb2-hdw-internal.h"
+#include "pvrusb2-debug.h"
+#include <linux/videodev2.h>
+#include <media/v4l2-common.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+struct pvr2_v4l_wm8775 {
+ struct pvr2_i2c_handler handler;
+ struct pvr2_i2c_client *client;
+ struct pvr2_hdw *hdw;
+ unsigned long stale_mask;
+};
+
+
+static void set_input(struct pvr2_v4l_wm8775 *ctxt)
+{
+ struct v4l2_routing route;
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ int msk = 0;
+
+ memset(&route,0,sizeof(route));
+
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c wm8775 set_input(val=%d msk=0x%x)",
+ hdw->input_val,msk);
+
+ // Always point to input #1 no matter what
+ route.input = 2;
+ pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_S_AUDIO_ROUTING,&route);
+}
+
+static int check_input(struct pvr2_v4l_wm8775 *ctxt)
+{
+ struct pvr2_hdw *hdw = ctxt->hdw;
+ return hdw->input_dirty != 0;
+}
+
+
+struct pvr2_v4l_wm8775_ops {
+ void (*update)(struct pvr2_v4l_wm8775 *);
+ int (*check)(struct pvr2_v4l_wm8775 *);
+};
+
+
+static const struct pvr2_v4l_wm8775_ops wm8775_ops[] = {
+ { .update = set_input, .check = check_input},
+};
+
+
+static unsigned int wm8775_describe(struct pvr2_v4l_wm8775 *ctxt,
+ char *buf,unsigned int cnt)
+{
+ return scnprintf(buf,cnt,"handler: pvrusb2-wm8775");
+}
+
+
+static void wm8775_detach(struct pvr2_v4l_wm8775 *ctxt)
+{
+ ctxt->client->handler = 0;
+ kfree(ctxt);
+}
+
+
+static int wm8775_check(struct pvr2_v4l_wm8775 *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (ctxt->stale_mask & msk) continue;
+ if (wm8775_ops[idx].check(ctxt)) {
+ ctxt->stale_mask |= msk;
+ }
+ }
+ return ctxt->stale_mask != 0;
+}
+
+
+static void wm8775_update(struct pvr2_v4l_wm8775 *ctxt)
+{
+ unsigned long msk;
+ unsigned int idx;
+
+ for (idx = 0; idx < sizeof(wm8775_ops)/sizeof(wm8775_ops[0]);
+ idx++) {
+ msk = 1 << idx;
+ if (!(ctxt->stale_mask & msk)) continue;
+ ctxt->stale_mask &= ~msk;
+ wm8775_ops[idx].update(ctxt);
+ }
+}
+
+
+const static struct pvr2_i2c_handler_functions hfuncs = {
+ .detach = (void (*)(void *))wm8775_detach,
+ .check = (int (*)(void *))wm8775_check,
+ .update = (void (*)(void *))wm8775_update,
+ .describe = (unsigned int (*)(void *,char *,unsigned int))wm8775_describe,
+};
+
+
+int pvr2_i2c_wm8775_setup(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
+{
+ struct pvr2_v4l_wm8775 *ctxt;
+
+ if (cp->handler) return 0;
+
+ ctxt = kmalloc(sizeof(*ctxt),GFP_KERNEL);
+ if (!ctxt) return 0;
+ memset(ctxt,0,sizeof(*ctxt));
+
+ ctxt->handler.func_data = ctxt;
+ ctxt->handler.func_table = &hfuncs;
+ ctxt->client = cp;
+ ctxt->hdw = hdw;
+ ctxt->stale_mask = (1 << (sizeof(wm8775_ops)/
+ sizeof(wm8775_ops[0]))) - 1;
+ cp->handler = &ctxt->handler;
+ pvr2_trace(PVR2_TRACE_CHIPS,"i2c 0x%x wm8775 V4L2 handler set up",
+ cp->client->addr);
+ return !0;
+}
+
+
+
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2-wm8775.h b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
new file mode 100644
index 000000000000..8aaeff4e1e20
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2-wm8775.h
@@ -0,0 +1,53 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_WM8775_H
+#define __PVRUSB2_WM8775_H
+
+/*
+
+ This module connects the pvrusb2 driver to the I2C chip level
+ driver which performs analog -> digital audio conversion for
+ external audio inputs. This interface is used internally by the
+ driver; higher level code should only interact through the
+ interface provided by pvrusb2-hdw.h.
+
+*/
+
+
+
+#include "pvrusb2-i2c-core.h"
+
+int pvr2_i2c_wm8775_setup(struct pvr2_hdw *,struct pvr2_i2c_client *);
+
+
+#endif /* __PVRUSB2_WM8775_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/pvrusb2/pvrusb2.h b/drivers/media/video/pvrusb2/pvrusb2.h
new file mode 100644
index 000000000000..074533e9c21e
--- /dev/null
+++ b/drivers/media/video/pvrusb2/pvrusb2.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * $Id$
+ *
+ * Copyright (C) 2005 Mike Isely <isely@pobox.com>
+ * Copyright (C) 2004 Aurelien Alleaume <slts@free.fr>
+ *
+ * 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
+ *
+ * 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
+ *
+ */
+
+#ifndef __PVRUSB2_H
+#define __PVRUSB2_H
+
+/* Maximum number of pvrusb2 instances we can track at once. You
+ might want to increase this - however the driver operation will not
+ be impaired if it is too small. Instead additional units just
+ won't have an ID assigned and it might not be possible to specify
+ module paramters for those extra units. */
+#define PVR_NUM 20
+
+#endif /* __PVRUSB2_H */
+
+/*
+ Stuff for Emacs to see, in order to encourage consistent editing style:
+ *** Local Variables: ***
+ *** mode: c ***
+ *** fill-column: 70 ***
+ *** tab-width: 8 ***
+ *** c-basic-offset: 8 ***
+ *** End: ***
+ */
diff --git a/drivers/media/video/saa7134/saa6752hs.c b/drivers/media/video/saa7134/saa6752hs.c
index de7b9e6e932a..afc8f352b8e7 100644
--- a/drivers/media/video/saa7134/saa6752hs.c
+++ b/drivers/media/video/saa7134/saa6752hs.c
@@ -432,10 +432,10 @@ static void saa6752hs_old_set_params(struct i2c_client* client,
}
static int handle_ctrl(struct saa6752hs_mpeg_params *params,
- struct v4l2_ext_control *ctrl, int cmd)
+ struct v4l2_ext_control *ctrl, unsigned int cmd)
{
int old = 0, new;
- int set = cmd == VIDIOC_S_EXT_CTRLS;
+ int set = (cmd == VIDIOC_S_EXT_CTRLS);
new = ctrl->value;
switch (ctrl->id) {
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index f0c2111f14ad..da3007d2f411 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -871,9 +871,9 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
pci_read_config_byte(pci_dev, PCI_CLASS_REVISION, &dev->pci_rev);
pci_read_config_byte(pci_dev, PCI_LATENCY_TIMER, &dev->pci_lat);
printk(KERN_INFO "%s: found at %s, rev: %d, irq: %d, "
- "latency: %d, mmio: 0x%lx\n", dev->name,
+ "latency: %d, mmio: 0x%llx\n", dev->name,
pci_name(pci_dev), dev->pci_rev, pci_dev->irq,
- dev->pci_lat,pci_resource_start(pci_dev,0));
+ dev->pci_lat,(unsigned long long)pci_resource_start(pci_dev,0));
pci_set_master(pci_dev);
if (!pci_dma_supported(pci_dev, DMA_32BIT_MASK)) {
printk("%s: Oops: no 32bit PCI DMA ???\n",dev->name);
@@ -905,8 +905,8 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
pci_resource_len(pci_dev,0),
dev->name)) {
err = -EBUSY;
- printk(KERN_ERR "%s: can't get MMIO memory @ 0x%lx\n",
- dev->name,pci_resource_start(pci_dev,0));
+ printk(KERN_ERR "%s: can't get MMIO memory @ 0x%llx\n",
+ dev->name,(unsigned long long)pci_resource_start(pci_dev,0));
goto fail1;
}
dev->lmmio = ioremap(pci_resource_start(pci_dev,0), 0x1000);
diff --git a/drivers/media/video/stradis.c b/drivers/media/video/stradis.c
index 6be9c1131e1f..c18b31d9928c 100644
--- a/drivers/media/video/stradis.c
+++ b/drivers/media/video/stradis.c
@@ -2190,7 +2190,7 @@ static struct pci_driver stradis_driver = {
.remove = __devexit_p(stradis_remove)
};
-int __init stradis_init(void)
+static int __init stradis_init(void)
{
int retval;
@@ -2203,7 +2203,7 @@ int __init stradis_init(void)
return retval;
}
-void __exit stradis_exit(void)
+static void __exit stradis_exit(void)
{
pci_unregister_driver(&stradis_driver);
printk(KERN_INFO "stradis: module cleanup complete\n");
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index b6ae969563b2..2fadabf99688 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -22,11 +22,11 @@
*/
#define tda9887_info(fmt, arg...) do {\
- printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \
+ printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
#define tda9887_dbg(fmt, arg...) do {\
if (tuner_debug) \
- printk(KERN_INFO "%s %d-%04x (tda9887): " fmt, t->i2c.name, \
+ printk(KERN_INFO "%s %d-%04x: " fmt, t->i2c.name, \
i2c_adapter_id(t->i2c.adapter), t->i2c.addr , ##arg); } while (0)
@@ -84,8 +84,7 @@ struct tvnorm {
#define cAudioGain6 0x80 // bit c7
#define cTopMask 0x1f // bit c0:4
-#define cTopPalSecamDefault 0x14 // bit c0:4
-#define cTopNtscRadioDefault 0x10 // bit c0:4
+#define cTopDefault 0x10 // bit c0:4
//// third reg (e)
#define cAudioIF_4_5 0x00 // bit e0:1
@@ -123,7 +122,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
- cTopPalSecamDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_5_5 |
cVideoIF_38_90 ),
@@ -134,7 +133,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
- cTopPalSecamDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_0 |
cVideoIF_38_90 ),
@@ -145,7 +144,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
- cTopPalSecamDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@@ -156,7 +155,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
- cTopNtscRadioDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ),
@@ -165,7 +164,7 @@ static struct tvnorm tvnorms[] = {
.name = "SECAM-BGH",
.b = ( cPositiveAmTV |
cQSS ),
- .c = ( cTopPalSecamDefault),
+ .c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_5_5 |
cVideoIF_38_90 ),
@@ -174,7 +173,7 @@ static struct tvnorm tvnorms[] = {
.name = "SECAM-L",
.b = ( cPositiveAmTV |
cQSS ),
- .c = ( cTopPalSecamDefault),
+ .c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@@ -184,7 +183,7 @@ static struct tvnorm tvnorms[] = {
.b = ( cOutputPort2Inactive |
cPositiveAmTV |
cQSS ),
- .c = ( cTopPalSecamDefault),
+ .c = ( cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_33_90 ),
@@ -195,7 +194,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
- cTopPalSecamDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_6_5 |
cVideoIF_38_90 ),
@@ -206,7 +205,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
- cTopNtscRadioDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_45_75 ),
@@ -217,7 +216,7 @@ static struct tvnorm tvnorms[] = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis50 |
- cTopNtscRadioDefault),
+ cTopDefault),
.e = ( cGating_36 |
cAudioIF_4_5 |
cVideoIF_58_75 ),
@@ -230,7 +229,7 @@ static struct tvnorm radio_stereo = {
cQSS ),
.c = ( cDeemphasisOFF |
cAudioGain6 |
- cTopNtscRadioDefault),
+ cTopDefault),
.e = ( cTunerGainLow |
cAudioIF_5_5 |
cRadioIF_38_90 ),
@@ -242,7 +241,7 @@ static struct tvnorm radio_mono = {
cQSS ),
.c = ( cDeemphasisON |
cDeemphasis75 |
- cTopNtscRadioDefault),
+ cTopDefault),
.e = ( cTunerGainLow |
cAudioIF_5_5 |
cRadioIF_38_90 ),
diff --git a/drivers/media/video/tuner-core.c b/drivers/media/video/tuner-core.c
index e95792fd70f8..011413cf34a8 100644
--- a/drivers/media/video/tuner-core.c
+++ b/drivers/media/video/tuner-core.c
@@ -40,7 +40,6 @@ static unsigned int no_autodetect = 0;
static unsigned int show_i2c = 0;
/* insmod options used at runtime => read/write */
-static unsigned int tuner_debug_old = 0;
int tuner_debug = 0;
static unsigned int tv_range[2] = { 44, 958 };
@@ -54,8 +53,6 @@ static char ntsc[] = "-";
module_param(addr, int, 0444);
module_param(no_autodetect, int, 0444);
module_param(show_i2c, int, 0444);
-/* Note: tuner_debug is deprecated and will be removed in 2.6.17 */
-module_param_named(tuner_debug,tuner_debug_old, int, 0444);
module_param_named(debug,tuner_debug, int, 0644);
module_param_string(pal, pal, sizeof(pal), 0644);
module_param_string(secam, secam, sizeof(secam), 0644);
@@ -442,11 +439,6 @@ static int tuner_attach(struct i2c_adapter *adap, int addr, int kind)
t->audmode = V4L2_TUNER_MODE_STEREO;
t->mode_mask = T_UNINITIALIZED;
t->tuner_status = tuner_status;
- if (tuner_debug_old) {
- tuner_debug = tuner_debug_old;
- printk(KERN_ERR "tuner: tuner_debug is deprecated and will be removed in 2.6.17.\n");
- printk(KERN_ERR "tuner: use the debug option instead.\n");
- }
if (show_i2c) {
unsigned char buffer[16];
@@ -730,14 +722,10 @@ static int tuner_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
struct v4l2_frequency *f = arg;
+ if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
+ == EINVAL)
+ return 0;
switch_v4l2();
- if ((V4L2_TUNER_RADIO == f->type && V4L2_TUNER_RADIO != t->mode)
- || (V4L2_TUNER_DIGITAL_TV == f->type
- && V4L2_TUNER_DIGITAL_TV != t->mode)) {
- if (set_mode (client, t, f->type, "VIDIOC_S_FREQUENCY")
- == EINVAL)
- return 0;
- }
set_freq(client,f->frequency);
break;
diff --git a/drivers/media/video/usbvideo/quickcam_messenger.c b/drivers/media/video/usbvideo/quickcam_messenger.c
index 3f3182a24da1..56e01b622417 100644
--- a/drivers/media/video/usbvideo/quickcam_messenger.c
+++ b/drivers/media/video/usbvideo/quickcam_messenger.c
@@ -33,7 +33,7 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/input.h>
-#include <linux/usb_input.h>
+#include <linux/usb/input.h>
#include "usbvideo.h"
#include "quickcam_messenger.h"
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 14e523471354..97f946db8597 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -1101,6 +1101,11 @@ const char **v4l2_ctrl_get_menu(u32 id)
"MPEG-2 SVCD-compatible Stream",
NULL
};
+ static const char *mpeg_stream_vbi_fmt[] = {
+ "No VBI",
+ "Private packet, IVTV format",
+ NULL
+ };
switch (id) {
case V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ:
@@ -1129,6 +1134,8 @@ const char **v4l2_ctrl_get_menu(u32 id)
return mpeg_video_bitrate_mode;
case V4L2_CID_MPEG_STREAM_TYPE:
return mpeg_stream_type;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return mpeg_stream_vbi_fmt;
default:
return NULL;
}
@@ -1182,6 +1189,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_STREAM_PID_PCR: name = "Stream PCR Program ID"; break;
case V4L2_CID_MPEG_STREAM_PES_ID_AUDIO: name = "Stream PES Audio ID"; break;
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO: name = "Stream PES Video ID"; break;
+ case V4L2_CID_MPEG_STREAM_VBI_FMT: name = "Stream VBI Format"; break;
default:
return -EINVAL;
@@ -1208,6 +1216,7 @@ int v4l2_ctrl_query_fill(struct v4l2_queryctrl *qctrl, s32 min, s32 max, s32 ste
case V4L2_CID_MPEG_VIDEO_ASPECT:
case V4L2_CID_MPEG_VIDEO_BITRATE_MODE:
case V4L2_CID_MPEG_STREAM_TYPE:
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
qctrl->type = V4L2_CTRL_TYPE_MENU;
step = 1;
break;
@@ -1367,6 +1376,11 @@ int v4l2_ctrl_query_fill_std(struct v4l2_queryctrl *qctrl)
return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
case V4L2_CID_MPEG_STREAM_PES_ID_VIDEO:
return v4l2_ctrl_query_fill(qctrl, 0, 255, 1, 0);
+ case V4L2_CID_MPEG_STREAM_VBI_FMT:
+ return v4l2_ctrl_query_fill(qctrl,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE,
+ V4L2_MPEG_STREAM_VBI_FMT_IVTV, 1,
+ V4L2_MPEG_STREAM_VBI_FMT_NONE);
default:
return -EINVAL;
}
diff --git a/drivers/message/fusion/mptfc.c b/drivers/message/fusion/mptfc.c
index 74714e5bcf03..3ff8378ea660 100644
--- a/drivers/message/fusion/mptfc.c
+++ b/drivers/message/fusion/mptfc.c
@@ -305,10 +305,8 @@ mptfc_GetFcDevPage0(MPT_ADAPTER *ioc, int ioc_port,
}
out:
- if (pp0_array)
- kfree(pp0_array);
- if (p0_array)
- kfree(p0_array);
+ kfree(pp0_array);
+ kfree(p0_array);
return rc;
}
diff --git a/drivers/message/fusion/mptsas.c b/drivers/message/fusion/mptsas.c
index af6ec553ff7c..85689ab46cbc 100644
--- a/drivers/message/fusion/mptsas.c
+++ b/drivers/message/fusion/mptsas.c
@@ -1378,8 +1378,7 @@ mptsas_probe_hba_phys(MPT_ADAPTER *ioc)
return 0;
out_free_port_info:
- if (hba)
- kfree(hba);
+ kfree(hba);
out:
return error;
}
diff --git a/drivers/message/i2o/iop.c b/drivers/message/i2o/iop.c
index febbdd4e0605..3305c12372a2 100644
--- a/drivers/message/i2o/iop.c
+++ b/drivers/message/i2o/iop.c
@@ -683,9 +683,10 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
c->mem_alloc = 1;
sb->current_mem_size = 1 + res->end - res->start;
sb->current_mem_base = res->start;
- osm_info("%s: allocated %ld bytes of PCI memory at "
- "0x%08lX.\n", c->name,
- 1 + res->end - res->start, res->start);
+ osm_info("%s: allocated %llu bytes of PCI memory at "
+ "0x%016llX.\n", c->name,
+ (unsigned long long)(1 + res->end - res->start),
+ (unsigned long long)res->start);
}
}
@@ -704,9 +705,10 @@ static int i2o_iop_systab_set(struct i2o_controller *c)
c->io_alloc = 1;
sb->current_io_size = 1 + res->end - res->start;
sb->current_mem_base = res->start;
- osm_info("%s: allocated %ld bytes of PCI I/O at 0x%08lX"
- ".\n", c->name, 1 + res->end - res->start,
- res->start);
+ osm_info("%s: allocated %llu bytes of PCI I/O at "
+ "0x%016llX.\n", c->name,
+ (unsigned long long)(1 + res->end - res->start),
+ (unsigned long long)res->start);
}
}
@@ -1239,7 +1241,6 @@ EXPORT_SYMBOL(i2o_cntxt_list_remove);
EXPORT_SYMBOL(i2o_cntxt_list_get_ptr);
#endif
EXPORT_SYMBOL(i2o_msg_get_wait);
-EXPORT_SYMBOL(i2o_msg_nop);
EXPORT_SYMBOL(i2o_find_iop);
EXPORT_SYMBOL(i2o_iop_find_device);
EXPORT_SYMBOL(i2o_event_register);
diff --git a/drivers/mfd/ucb1x00-core.c b/drivers/mfd/ucb1x00-core.c
index aff83f966803..c8426a9bf273 100644
--- a/drivers/mfd/ucb1x00-core.c
+++ b/drivers/mfd/ucb1x00-core.c
@@ -420,8 +420,10 @@ static int ucb1x00_detect_irq(struct ucb1x00 *ucb)
unsigned long mask;
mask = probe_irq_on();
- if (!mask)
+ if (!mask) {
+ probe_irq_off(mask);
return NO_IRQ;
+ }
/*
* Enable the ADC interrupt.
diff --git a/drivers/misc/ibmasm/module.c b/drivers/misc/ibmasm/module.c
index 1fdf03fd2da7..9706cc19134a 100644
--- a/drivers/misc/ibmasm/module.c
+++ b/drivers/misc/ibmasm/module.c
@@ -85,7 +85,7 @@ static int __devinit ibmasm_init_one(struct pci_dev *pdev, const struct pci_devi
}
memset(sp, 0, sizeof(struct service_processor));
- sp->lock = SPIN_LOCK_UNLOCKED;
+ spin_lock_init(&sp->lock);
INIT_LIST_HEAD(&sp->command_queue);
pci_set_drvdata(pdev, (void *)sp);
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index da8e4d7339cc..8576a65ca1c3 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -546,9 +546,9 @@ static int mmci_probe(struct amba_device *dev, void *id)
mmc_add_host(mmc);
- printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%08lx irq %d,%d\n",
+ printk(KERN_INFO "%s: MMCI rev %x cfg %02x at 0x%016llx irq %d,%d\n",
mmc_hostname(mmc), amba_rev(dev), amba_config(dev),
- dev->res.start, dev->irq[0], dev->irq[1]);
+ (unsigned long long)dev->res.start, dev->irq[0], dev->irq[1]);
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index 5ac265dde423..1344ad7a4b14 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -86,14 +86,14 @@ config MTD_REDBOOT_DIRECTORY_BLOCK
block and "-2" means the penultimate block.
config MTD_REDBOOT_PARTS_UNALLOCATED
- bool " Include unallocated flash regions"
+ bool "Include unallocated flash regions"
depends on MTD_REDBOOT_PARTS
help
If you need to register each unallocated flash region as a MTD
'partition', enable this option.
config MTD_REDBOOT_PARTS_READONLY
- bool " Force read-only for RedBoot system images"
+ bool "Force read-only for RedBoot system images"
depends on MTD_REDBOOT_PARTS
help
If you need to force read-only for 'RedBoot', 'RedBoot Config' and
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c
index 0d435814aaa1..39edb8250fbc 100644
--- a/drivers/mtd/chips/cfi_cmdset_0001.c
+++ b/drivers/mtd/chips/cfi_cmdset_0001.c
@@ -357,6 +357,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
mtd->resume = cfi_intelext_resume;
mtd->flags = MTD_CAP_NORFLASH;
mtd->name = map->name;
+ mtd->writesize = 1;
mtd->reboot_notifier.notifier_call = cfi_intelext_reboot;
diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c
index c40b48dabed3..2c3f019197c1 100644
--- a/drivers/mtd/chips/jedec.c
+++ b/drivers/mtd/chips/jedec.c
@@ -256,6 +256,7 @@ static struct mtd_info *jedec_probe(struct map_info *map)
MTD->name = map->name;
MTD->type = MTD_NORFLASH;
MTD->flags = MTD_CAP_NORFLASH;
+ MTD->writesize = 1;
MTD->erasesize = SectorSize*(map->buswidth);
// printk("MTD->erasesize is %x\n",(unsigned int)MTD->erasesize);
MTD->size = priv->size;
diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c
index a611de9b1515..ac01a949b687 100644
--- a/drivers/mtd/chips/map_absent.c
+++ b/drivers/mtd/chips/map_absent.c
@@ -64,7 +64,8 @@ static struct mtd_info *map_absent_probe(struct map_info *map)
mtd->write = map_absent_write;
mtd->sync = map_absent_sync;
mtd->flags = 0;
- mtd->erasesize = PAGE_SIZE;
+ mtd->erasesize = PAGE_SIZE;
+ mtd->writesize = 1;
__module_get(THIS_MODULE);
return mtd;
diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c
index 763925747db6..3a66680abfd0 100644
--- a/drivers/mtd/chips/map_ram.c
+++ b/drivers/mtd/chips/map_ram.c
@@ -71,6 +71,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
mtd->write = mapram_write;
mtd->sync = mapram_nop;
mtd->flags = MTD_CAP_RAM;
+ mtd->writesize = 1;
mtd->erasesize = PAGE_SIZE;
while(mtd->size & (mtd->erasesize - 1))
diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c
index bc6ee9ef8a31..1b328b1378fd 100644
--- a/drivers/mtd/chips/map_rom.c
+++ b/drivers/mtd/chips/map_rom.c
@@ -47,6 +47,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
mtd->sync = maprom_nop;
mtd->flags = MTD_CAP_ROM;
mtd->erasesize = map->size;
+ mtd->writesize = 1;
__module_get(THIS_MODULE);
return mtd;
diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c
index 0d98c223c5fc..be3f1c136d02 100644
--- a/drivers/mtd/devices/block2mtd.c
+++ b/drivers/mtd/devices/block2mtd.c
@@ -324,6 +324,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size)
dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
dev->mtd.erasesize = erase_size;
+ dev->mtd.writesize = 1;
dev->mtd.type = MTD_RAM;
dev->mtd.flags = MTD_CAP_RAM;
dev->mtd.erase = block2mtd_erase;
diff --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c
index 4ab7670770e4..08dfb899b272 100644
--- a/drivers/mtd/devices/ms02-nv.c
+++ b/drivers/mtd/devices/ms02-nv.c
@@ -225,6 +225,7 @@ static int __init ms02nv_init_one(ulong addr)
mtd->owner = THIS_MODULE;
mtd->read = ms02nv_read;
mtd->write = ms02nv_write;
+ mtd->writesize = 1;
ret = -EIO;
if (add_mtd_device(mtd)) {
diff --git a/drivers/mtd/devices/mtd_dataflash.c b/drivers/mtd/devices/mtd_dataflash.c
index a19480d07888..04271d02b6b6 100644
--- a/drivers/mtd/devices/mtd_dataflash.c
+++ b/drivers/mtd/devices/mtd_dataflash.c
@@ -478,6 +478,7 @@ add_dataflash(struct spi_device *spi, char *name,
device->name = (pdata && pdata->name) ? pdata->name : priv->name;
device->size = nr_pages * pagesize;
device->erasesize = pagesize;
+ device->writesize = pagesize;
device->owner = THIS_MODULE;
device->type = MTD_DATAFLASH;
device->flags = MTD_CAP_NORFLASH;
diff --git a/drivers/mtd/devices/phram.c b/drivers/mtd/devices/phram.c
index e09e416667d3..6c7337f9ebbb 100644
--- a/drivers/mtd/devices/phram.c
+++ b/drivers/mtd/devices/phram.c
@@ -151,6 +151,7 @@ static int register_device(char *name, unsigned long start, unsigned long len)
new->mtd.owner = THIS_MODULE;
new->mtd.type = MTD_RAM;
new->mtd.erasesize = PAGE_SIZE;
+ new->mtd.writesize = 1;
ret = -EAGAIN;
if (add_mtd_device(&new->mtd)) {
diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c
index 666cce1bf60c..30f07b473ae2 100644
--- a/drivers/mtd/devices/pmc551.c
+++ b/drivers/mtd/devices/pmc551.c
@@ -551,11 +551,11 @@ static u32 fixup_pmc551 (struct pci_dev *dev)
/*
* Some screen fun
*/
- printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%lx\n",
+ printk(KERN_DEBUG "pmc551: %d%c (0x%x) of %sprefetchable memory at 0x%llx\n",
(size<1024)?size:(size<1048576)?size>>10:size>>20,
(size<1024)?'B':(size<1048576)?'K':'M',
size, ((dcmd&(0x1<<3)) == 0)?"non-":"",
- (dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK );
+ (unsigned long long)((dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK));
/*
* Check to see the state of the memory
@@ -685,8 +685,8 @@ static int __init init_pmc551(void)
break;
}
- printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%lX\n",
- PCI_Device->resource[0].start);
+ printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%llx\n",
+ (unsigned long long)PCI_Device->resource[0].start);
/*
* The PMC551 device acts VERY weird if you don't init it
@@ -778,7 +778,8 @@ static int __init init_pmc551(void)
mtd->type = MTD_RAM;
mtd->name = "PMC551 RAM board";
mtd->erasesize = 0x10000;
- mtd->owner = THIS_MODULE;
+ mtd->writesize = 1;
+ mtd->owner = THIS_MODULE;
if (add_mtd_device(mtd)) {
printk(KERN_NOTICE "pmc551: Failed to register new device\n");
diff --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c
index b3f665e3c38b..542a0c009006 100644
--- a/drivers/mtd/devices/slram.c
+++ b/drivers/mtd/devices/slram.c
@@ -209,6 +209,7 @@ static int register_device(char *name, unsigned long start, unsigned long length
(*curmtd)->mtdinfo->owner = THIS_MODULE;
(*curmtd)->mtdinfo->type = MTD_RAM;
(*curmtd)->mtdinfo->erasesize = SLRAM_BLK_SZ;
+ (*curmtd)->mtdinfo->writesize = 1;
if (add_mtd_device((*curmtd)->mtdinfo)) {
E("slram: Failed to register new device\n");
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig
index 6bdaacc6d6f9..83d0b2a52527 100644
--- a/drivers/mtd/maps/Kconfig
+++ b/drivers/mtd/maps/Kconfig
@@ -212,7 +212,7 @@ config MTD_NETtel
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
config MTD_ALCHEMY
- tristate ' AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support'
+ tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support"
depends on SOC_AU1X00
help
Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards
diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c
index c350878d4592..a50587005263 100644
--- a/drivers/mtd/maps/amd76xrom.c
+++ b/drivers/mtd/maps/amd76xrom.c
@@ -123,9 +123,10 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
window->rsrc.parent = NULL;
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource"
- " 0x%.08lx-0x%.08lx - kernel bug?\n",
+ " 0x%.16llx-0x%.16llx - kernel bug?\n",
__func__,
- window->rsrc.start, window->rsrc.end);
+ (unsigned long long)window->rsrc.start,
+ (unsigned long long)window->rsrc.end);
}
#if 0
diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c
index ea5073781b3a..16732794edf3 100644
--- a/drivers/mtd/maps/ichxrom.c
+++ b/drivers/mtd/maps/ichxrom.c
@@ -177,9 +177,10 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev,
window->rsrc.parent = NULL;
printk(KERN_DEBUG MOD_NAME
": %s(): Unable to register resource"
- " 0x%.08lx-0x%.08lx - kernel bug?\n",
+ " 0x%.16llx-0x%.16llx - kernel bug?\n",
__func__,
- window->rsrc.start, window->rsrc.end);
+ (unsigned long long)window->rsrc.start,
+ (unsigned long long)window->rsrc.end);
}
/* Map the firmware hub into my address space. */
diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c
index 2c9cc7f37e92..c26488a1793a 100644
--- a/drivers/mtd/maps/ixp2000.c
+++ b/drivers/mtd/maps/ixp2000.c
@@ -42,7 +42,6 @@ struct ixp2000_flash_info {
struct map_info map;
struct mtd_partition *partitions;
struct resource *res;
- int nr_banks;
};
static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs)
@@ -183,7 +182,6 @@ static int ixp2000_flash_probe(struct platform_device *dev)
*/
info->map.phys = NO_XIP;
- info->nr_banks = ixp_data->nr_banks;
info->map.size = ixp_data->nr_banks * window_size;
info->map.bankwidth = 1;
diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c
index 433c3cac3ca9..d6301f08906d 100644
--- a/drivers/mtd/maps/physmap.c
+++ b/drivers/mtd/maps/physmap.c
@@ -182,7 +182,7 @@ static struct physmap_flash_data physmap_flash_data = {
static struct resource physmap_flash_resource = {
.start = CONFIG_MTD_PHYSMAP_START,
- .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN,
+ .end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
.flags = IORESOURCE_MEM,
};
diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c
index 28b8a571a91a..331a15859d71 100644
--- a/drivers/mtd/maps/scx200_docflash.c
+++ b/drivers/mtd/maps/scx200_docflash.c
@@ -164,8 +164,9 @@ static int __init init_scx200_docflash(void)
outl(pmr, scx200_cb_base + SCx200_PMR);
}
- printk(KERN_INFO NAME ": DOCCS mapped at 0x%lx-0x%lx, width %d\n",
- docmem.start, docmem.end, width);
+ printk(KERN_INFO NAME ": DOCCS mapped at 0x%llx-0x%llx, width %d\n",
+ (unsigned long long)docmem.start,
+ (unsigned long long)docmem.end, width);
scx200_docflash_map.size = size;
if (width == 8)
diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c
index 0758cb1d0105..4db2055cee31 100644
--- a/drivers/mtd/maps/sun_uflash.c
+++ b/drivers/mtd/maps/sun_uflash.c
@@ -18,6 +18,7 @@
#include <linux/ioport.h>
#include <asm/ebus.h>
#include <asm/oplib.h>
+#include <asm/prom.h>
#include <asm/uaccess.h>
#include <asm/io.h>
@@ -30,146 +31,141 @@
#define UFLASH_WINDOW_SIZE 0x200000
#define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */
-MODULE_AUTHOR
- ("Eric Brower <ebrower@usa.net>");
-MODULE_DESCRIPTION
- ("User-programmable flash device on Sun Microsystems boardsets");
-MODULE_SUPPORTED_DEVICE
- ("userflash");
-MODULE_LICENSE
- ("GPL");
+MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
+MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets");
+MODULE_SUPPORTED_DEVICE("userflash");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("2.0");
static LIST_HEAD(device_list);
struct uflash_dev {
- char * name; /* device name */
+ char *name; /* device name */
struct map_info map; /* mtd map info */
- struct mtd_info * mtd; /* mtd info */
- struct list_head list;
+ struct mtd_info *mtd; /* mtd info */
};
struct map_info uflash_map_templ = {
- .name = "SUNW,???-????",
- .size = UFLASH_WINDOW_SIZE,
- .bankwidth = UFLASH_BUSWIDTH,
+ .name = "SUNW,???-????",
+ .size = UFLASH_WINDOW_SIZE,
+ .bankwidth = UFLASH_BUSWIDTH,
};
-int uflash_devinit(struct linux_ebus_device* edev)
+int uflash_devinit(struct linux_ebus_device *edev, struct device_node *dp)
{
- int iTmp, nregs;
- struct linux_prom_registers regs[2];
- struct uflash_dev *pdev;
-
- iTmp = prom_getproperty(
- edev->prom_node, "reg", (void *)regs, sizeof(regs));
- if ((iTmp % sizeof(regs[0])) != 0) {
- printk("%s: Strange reg property size %d\n",
- UFLASH_DEVNAME, iTmp);
- return -ENODEV;
- }
+ struct uflash_dev *up;
+ struct resource *res;
- nregs = iTmp / sizeof(regs[0]);
+ res = &edev->resource[0];
- if (nregs != 1) {
+ if (edev->num_addrs != 1) {
/* Non-CFI userflash device-- once I find one we
* can work on supporting it.
*/
- printk("%s: unsupported device at 0x%lx (%d regs): " \
+ printk("%s: unsupported device at 0x%llx (%d regs): " \
"email ebrower@usa.net\n",
- UFLASH_DEVNAME, edev->resource[0].start, nregs);
+ dp->full_name, (unsigned long long)res->start,
+ edev->num_addrs);
+
return -ENODEV;
}
- if(0 == (pdev = kmalloc(sizeof(struct uflash_dev), GFP_KERNEL))) {
- printk("%s: unable to kmalloc new device\n", UFLASH_DEVNAME);
- return(-ENOMEM);
- }
+ up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL);
+ if (!up)
+ return -ENOMEM;
/* copy defaults and tweak parameters */
- memcpy(&pdev->map, &uflash_map_templ, sizeof(uflash_map_templ));
- pdev->map.size = regs[0].reg_size;
-
- iTmp = prom_getproplen(edev->prom_node, "model");
- pdev->name = kmalloc(iTmp, GFP_KERNEL);
- prom_getstring(edev->prom_node, "model", pdev->name, iTmp);
- if(0 != pdev->name && 0 < strlen(pdev->name)) {
- pdev->map.name = pdev->name;
- }
- pdev->map.phys = edev->resource[0].start;
- pdev->map.virt = ioremap_nocache(edev->resource[0].start, pdev->map.size);
- if(0 == pdev->map.virt) {
- printk("%s: failed to map device\n", __FUNCTION__);
- kfree(pdev->name);
- kfree(pdev);
- return(-1);
+ memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ));
+ up->map.size = (res->end - res->start) + 1UL;
+
+ up->name = of_get_property(dp, "model", NULL);
+ if (up->name && 0 < strlen(up->name))
+ up->map.name = up->name;
+
+ up->map.phys = res->start;
+
+ up->map.virt = ioremap_nocache(res->start, up->map.size);
+ if (!up->map.virt) {
+ printk("%s: Failed to map device.\n", dp->full_name);
+ kfree(up);
+
+ return -EINVAL;
}
- simple_map_init(&pdev->map);
+ simple_map_init(&up->map);
/* MTD registration */
- pdev->mtd = do_map_probe("cfi_probe", &pdev->map);
- if(0 == pdev->mtd) {
- iounmap(pdev->map.virt);
- kfree(pdev->name);
- kfree(pdev);
- return(-ENXIO);
+ up->mtd = do_map_probe("cfi_probe", &up->map);
+ if (!up->mtd) {
+ iounmap(up->map.virt);
+ kfree(up);
+
+ return -ENXIO;
}
- list_add(&pdev->list, &device_list);
+ up->mtd->owner = THIS_MODULE;
- pdev->mtd->owner = THIS_MODULE;
+ add_mtd_device(up->mtd);
- add_mtd_device(pdev->mtd);
- return(0);
+ dev_set_drvdata(&edev->ofdev.dev, up);
+
+ return 0;
}
-static int __init uflash_init(void)
+static int __devinit uflash_probe(struct of_device *dev, const struct of_device_id *match)
{
- struct linux_ebus *ebus = NULL;
- struct linux_ebus_device *edev = NULL;
-
- for_each_ebus(ebus) {
- for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, UFLASH_OBPNAME)) {
- if(0 > prom_getproplen(edev->prom_node, "user")) {
- DEBUG(2, "%s: ignoring device at 0x%lx\n",
- UFLASH_DEVNAME, edev->resource[0].start);
- } else {
- uflash_devinit(edev);
- }
- }
- }
- }
+ struct linux_ebus_device *edev = to_ebus_device(&dev->dev);
+ struct device_node *dp = dev->node;
- if(list_empty(&device_list)) {
- printk("%s: unable to locate device\n", UFLASH_DEVNAME);
+ if (of_find_property(dp, "user", NULL))
return -ENODEV;
- }
- return(0);
+
+ return uflash_devinit(edev, dp);
}
-static void __exit uflash_cleanup(void)
+static int __devexit uflash_remove(struct of_device *dev)
{
- struct list_head *udevlist;
- struct uflash_dev *udev;
-
- list_for_each(udevlist, &device_list) {
- udev = list_entry(udevlist, struct uflash_dev, list);
- DEBUG(2, "%s: removing device %s\n",
- UFLASH_DEVNAME, udev->name);
-
- if(0 != udev->mtd) {
- del_mtd_device(udev->mtd);
- map_destroy(udev->mtd);
- }
- if(0 != udev->map.virt) {
- iounmap(udev->map.virt);
- udev->map.virt = NULL;
- }
- kfree(udev->name);
- kfree(udev);
+ struct uflash_dev *up = dev_get_drvdata(&dev->dev);
+
+ if (up->mtd) {
+ del_mtd_device(up->mtd);
+ map_destroy(up->mtd);
}
+ if (up->map.virt) {
+ iounmap(up->map.virt);
+ up->map.virt = NULL;
+ }
+
+ kfree(up);
+
+ return 0;
+}
+
+static struct of_device_id uflash_match[] = {
+ {
+ .name = UFLASH_OBPNAME,
+ },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, uflash_match);
+
+static struct of_platform_driver uflash_driver = {
+ .name = UFLASH_DEVNAME,
+ .match_table = uflash_match,
+ .probe = uflash_probe,
+ .remove = __devexit_p(uflash_remove),
+};
+
+static int __init uflash_init(void)
+{
+ return of_register_driver(&uflash_driver, &ebus_bus_type);
+}
+
+static void __exit uflash_exit(void)
+{
+ of_unregister_driver(&uflash_driver);
}
module_init(uflash_init);
-module_exit(uflash_cleanup);
+module_exit(uflash_exit);
diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c
index aa18d45b264b..9a4b59d92525 100644
--- a/drivers/mtd/mtdchar.c
+++ b/drivers/mtd/mtdchar.c
@@ -78,7 +78,7 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
return -EINVAL;
}
- if (offset >= 0 && offset < mtd->size)
+ if (offset >= 0 && offset <= mtd->size)
return file->f_pos = offset;
return -EINVAL;
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 27083ed0a017..80a76654d963 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1176,7 +1176,7 @@ static int nand_write_oob_std(struct mtd_info *mtd, struct nand_chip *chip,
status = chip->waitfunc(mtd, chip);
- return status;
+ return status & NAND_STATUS_FAIL ? -EIO : 0;
}
/**
@@ -1271,10 +1271,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
buf = nand_transfer_oob(chip, buf, ops);
- readlen -= ops->ooblen;
- if (!readlen)
- break;
-
if (!(chip->options & NAND_NO_READRDY)) {
/*
* Apply delay or wait for ready/busy pin. Do this
@@ -1288,6 +1284,10 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
nand_wait_ready(mtd);
}
+ readlen -= ops->ooblen;
+ if (!readlen)
+ break;
+
/* Increment page address */
realpage++;
@@ -1610,13 +1610,13 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (!writelen)
return 0;
+ chipnr = (int)(to >> chip->chip_shift);
+ chip->select_chip(mtd, chipnr);
+
/* Check, if it is write protected */
if (nand_check_wp(mtd))
return -EIO;
- chipnr = (int)(to >> chip->chip_shift);
- chip->select_chip(mtd, chipnr);
-
realpage = (int)(to >> chip->page_shift);
page = realpage & chip->pagemask;
blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
diff --git a/drivers/mtd/nand/ndfc.c b/drivers/mtd/nand/ndfc.c
index fe8d38514ba6..e5bd88f2d560 100644
--- a/drivers/mtd/nand/ndfc.c
+++ b/drivers/mtd/nand/ndfc.c
@@ -61,15 +61,15 @@ static void ndfc_select_chip(struct mtd_info *mtd, int chip)
static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
- struct nand_chip *chip = mtd->priv;
+ struct ndfc_controller *ndfc = &ndfc_ctrl;
if (cmd == NAND_CMD_NONE)
return;
if (ctrl & NAND_CLE)
- writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_CMD);
+ writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_CMD);
else
- writel(cmd & 0xFF, chip->IO_ADDR_W + NDFC_ALE);
+ writel(cmd & 0xFF, ndfc->ndfcbase + NDFC_ALE);
}
static int ndfc_ready(struct mtd_info *mtd)
diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c
index 2c262fe03d8a..ff5cef24d5bb 100644
--- a/drivers/mtd/nand/s3c2410.c
+++ b/drivers/mtd/nand/s3c2410.c
@@ -63,8 +63,6 @@
#include <asm/arch/regs-nand.h>
#include <asm/arch/nand.h>
-#define PFX "s3c2410-nand: "
-
#ifdef CONFIG_MTD_NAND_S3C2410_HWECC
static int hardware_ecc = 1;
#else
@@ -99,6 +97,12 @@ struct s3c2410_nand_mtd {
int scan_res;
};
+enum s3c_cpu_type {
+ TYPE_S3C2410,
+ TYPE_S3C2412,
+ TYPE_S3C2440,
+};
+
/* overview of the s3c2410 nand state */
struct s3c2410_nand_info {
@@ -112,9 +116,11 @@ struct s3c2410_nand_info {
struct resource *area;
struct clk *clk;
void __iomem *regs;
+ void __iomem *sel_reg;
+ int sel_bit;
int mtd_count;
- unsigned char is_s3c2440;
+ enum s3c_cpu_type cpu_type;
};
/* conversion functions */
@@ -148,7 +154,7 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info)
#define NS_IN_KHZ 1000000
-static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
+static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max)
{
int result;
@@ -172,53 +178,58 @@ static int s3c2410_nand_calc_rate(int wanted, unsigned long clk, int max)
/* controller setup */
-static int s3c2410_nand_inithw(struct s3c2410_nand_info *info, struct platform_device *pdev)
+static int s3c2410_nand_inithw(struct s3c2410_nand_info *info,
+ struct platform_device *pdev)
{
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
unsigned long clkrate = clk_get_rate(info->clk);
+ int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4;
int tacls, twrph0, twrph1;
- unsigned long cfg;
+ unsigned long cfg = 0;
/* calculate the timing information for the controller */
clkrate /= 1000; /* turn clock into kHz for ease of use */
if (plat != NULL) {
- tacls = s3c2410_nand_calc_rate(plat->tacls, clkrate, 4);
- twrph0 = s3c2410_nand_calc_rate(plat->twrph0, clkrate, 8);
- twrph1 = s3c2410_nand_calc_rate(plat->twrph1, clkrate, 8);
+ tacls = s3c_nand_calc_rate(plat->tacls, clkrate, tacls_max);
+ twrph0 = s3c_nand_calc_rate(plat->twrph0, clkrate, 8);
+ twrph1 = s3c_nand_calc_rate(plat->twrph1, clkrate, 8);
} else {
/* default timings */
- tacls = 4;
+ tacls = tacls_max;
twrph0 = 8;
twrph1 = 8;
}
if (tacls < 0 || twrph0 < 0 || twrph1 < 0) {
- printk(KERN_ERR PFX "cannot get timings suitable for board\n");
+ dev_err(info->device, "cannot get suitable timings\n");
return -EINVAL;
}
- printk(KERN_INFO PFX "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
+ dev_info(info->device, "Tacls=%d, %dns Twrph0=%d %dns, Twrph1=%d %dns\n",
tacls, to_ns(tacls, clkrate), twrph0, to_ns(twrph0, clkrate), twrph1, to_ns(twrph1, clkrate));
- if (!info->is_s3c2440) {
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
cfg = S3C2410_NFCONF_EN;
cfg |= S3C2410_NFCONF_TACLS(tacls - 1);
cfg |= S3C2410_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2410_NFCONF_TWRPH1(twrph1 - 1);
- } else {
+ break;
+
+ case TYPE_S3C2440:
+ case TYPE_S3C2412:
cfg = S3C2440_NFCONF_TACLS(tacls - 1);
cfg |= S3C2440_NFCONF_TWRPH0(twrph0 - 1);
cfg |= S3C2440_NFCONF_TWRPH1(twrph1 - 1);
/* enable the controller and de-assert nFCE */
- writel(S3C2440_NFCONT_ENABLE | S3C2440_NFCONT_ENABLE,
- info->regs + S3C2440_NFCONT);
+ writel(S3C2440_NFCONT_ENABLE, info->regs + S3C2440_NFCONT);
}
- pr_debug(PFX "NF_CONF is 0x%lx\n", cfg);
+ dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg);
writel(cfg, info->regs + S3C2410_NFCONF);
return 0;
@@ -231,26 +242,21 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
struct s3c2410_nand_info *info;
struct s3c2410_nand_mtd *nmtd;
struct nand_chip *this = mtd->priv;
- void __iomem *reg;
unsigned long cur;
- unsigned long bit;
nmtd = this->priv;
info = nmtd->info;
- bit = (info->is_s3c2440) ? S3C2440_NFCONT_nFCE : S3C2410_NFCONF_nFCE;
- reg = info->regs + ((info->is_s3c2440) ? S3C2440_NFCONT : S3C2410_NFCONF);
-
if (chip != -1 && allow_clk_stop(info))
clk_enable(info->clk);
- cur = readl(reg);
+ cur = readl(info->sel_reg);
if (chip == -1) {
- cur |= bit;
+ cur |= info->sel_bit;
} else {
if (nmtd->set != NULL && chip > nmtd->set->nr_chips) {
- printk(KERN_ERR PFX "chip %d out of range\n", chip);
+ dev_err(info->device, "invalid chip %d\n", chip);
return;
}
@@ -259,10 +265,10 @@ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip)
(info->platform->select_chip) (nmtd->set, chip);
}
- cur &= ~bit;
+ cur &= ~info->sel_bit;
}
- writel(cur, reg);
+ writel(cur, info->sel_reg);
if (chip == -1 && allow_clk_stop(info))
clk_disable(info->clk);
@@ -311,15 +317,25 @@ static void s3c2440_nand_hwcontrol(struct mtd_info *mtd, int cmd,
static int s3c2410_nand_devready(struct mtd_info *mtd)
{
struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
-
- if (info->is_s3c2440)
- return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
return readb(info->regs + S3C2410_NFSTAT) & S3C2410_NFSTAT_BUSY;
}
+static int s3c2440_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ return readb(info->regs + S3C2440_NFSTAT) & S3C2440_NFSTAT_READY;
+}
+
+static int s3c2412_nand_devready(struct mtd_info *mtd)
+{
+ struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd);
+ return readb(info->regs + S3C2412_NFSTAT) & S3C2412_NFSTAT_READY;
+}
+
/* ECC handling functions */
-static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat,
+ u_char *read_ecc, u_char *calc_ecc)
{
pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc);
@@ -487,11 +503,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
struct s3c2410_nand_set *set)
{
struct nand_chip *chip = &nmtd->chip;
+ void __iomem *regs = info->regs;
- chip->IO_ADDR_R = info->regs + S3C2410_NFDATA;
- chip->IO_ADDR_W = info->regs + S3C2410_NFDATA;
- chip->cmd_ctrl = s3c2410_nand_hwcontrol;
- chip->dev_ready = s3c2410_nand_devready;
chip->write_buf = s3c2410_nand_write_buf;
chip->read_buf = s3c2410_nand_read_buf;
chip->select_chip = s3c2410_nand_select_chip;
@@ -500,11 +513,37 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
chip->options = 0;
chip->controller = &info->controller;
- if (info->is_s3c2440) {
- chip->IO_ADDR_R = info->regs + S3C2440_NFDATA;
- chip->IO_ADDR_W = info->regs + S3C2440_NFDATA;
- chip->cmd_ctrl = s3c2440_nand_hwcontrol;
- }
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ chip->IO_ADDR_W = regs + S3C2410_NFDATA;
+ info->sel_reg = regs + S3C2410_NFCONF;
+ info->sel_bit = S3C2410_NFCONF_nFCE;
+ chip->cmd_ctrl = s3c2410_nand_hwcontrol;
+ chip->dev_ready = s3c2410_nand_devready;
+ break;
+
+ case TYPE_S3C2440:
+ chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+ info->sel_reg = regs + S3C2440_NFCONT;
+ info->sel_bit = S3C2440_NFCONT_nFCE;
+ chip->cmd_ctrl = s3c2440_nand_hwcontrol;
+ chip->dev_ready = s3c2440_nand_devready;
+ break;
+
+ case TYPE_S3C2412:
+ chip->IO_ADDR_W = regs + S3C2440_NFDATA;
+ info->sel_reg = regs + S3C2440_NFCONT;
+ info->sel_bit = S3C2412_NFCONT_nFCE0;
+ chip->cmd_ctrl = s3c2440_nand_hwcontrol;
+ chip->dev_ready = s3c2412_nand_devready;
+
+ if (readl(regs + S3C2410_NFCONF) & S3C2412_NFCONF_NANDBOOT)
+ dev_info(info->device, "System booted from NAND\n");
+
+ break;
+ }
+
+ chip->IO_ADDR_R = chip->IO_ADDR_W;
nmtd->info = info;
nmtd->mtd.priv = chip;
@@ -512,17 +551,25 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
nmtd->set = set;
if (hardware_ecc) {
- chip->ecc.correct = s3c2410_nand_correct_data;
- chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ chip->ecc.correct = s3c2410_nand_correct_data;
chip->ecc.mode = NAND_ECC_HW;
chip->ecc.size = 512;
chip->ecc.bytes = 3;
chip->ecc.layout = &nand_hw_eccoob;
- if (info->is_s3c2440) {
- chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
- chip->ecc.calculate = s3c2440_nand_calculate_ecc;
+ switch (info->cpu_type) {
+ case TYPE_S3C2410:
+ chip->ecc.hwctl = s3c2410_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2410_nand_calculate_ecc;
+ break;
+
+ case TYPE_S3C2412:
+ case TYPE_S3C2440:
+ chip->ecc.hwctl = s3c2440_nand_enable_hwecc;
+ chip->ecc.calculate = s3c2440_nand_calculate_ecc;
+ break;
+
}
} else {
chip->ecc.mode = NAND_ECC_SOFT;
@@ -537,7 +584,8 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info,
* nand layer to look for devices
*/
-static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
+static int s3c24xx_nand_probe(struct platform_device *pdev,
+ enum s3c_cpu_type cpu_type)
{
struct s3c2410_platform_nand *plat = to_nand_plat(pdev);
struct s3c2410_nand_info *info;
@@ -592,7 +640,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int is_s3c2440)
info->device = &pdev->dev;
info->platform = plat;
info->regs = ioremap(res->start, size);
- info->is_s3c2440 = is_s3c2440;
+ info->cpu_type = cpu_type;
if (info->regs == NULL) {
dev_err(&pdev->dev, "cannot reserve register region\n");
@@ -699,12 +747,17 @@ static int s3c24xx_nand_resume(struct platform_device *dev)
static int s3c2410_nand_probe(struct platform_device *dev)
{
- return s3c24xx_nand_probe(dev, 0);
+ return s3c24xx_nand_probe(dev, TYPE_S3C2410);
}
static int s3c2440_nand_probe(struct platform_device *dev)
{
- return s3c24xx_nand_probe(dev, 1);
+ return s3c24xx_nand_probe(dev, TYPE_S3C2440);
+}
+
+static int s3c2412_nand_probe(struct platform_device *dev)
+{
+ return s3c24xx_nand_probe(dev, TYPE_S3C2412);
}
static struct platform_driver s3c2410_nand_driver = {
@@ -729,16 +782,29 @@ static struct platform_driver s3c2440_nand_driver = {
},
};
+static struct platform_driver s3c2412_nand_driver = {
+ .probe = s3c2412_nand_probe,
+ .remove = s3c2410_nand_remove,
+ .suspend = s3c24xx_nand_suspend,
+ .resume = s3c24xx_nand_resume,
+ .driver = {
+ .name = "s3c2412-nand",
+ .owner = THIS_MODULE,
+ },
+};
+
static int __init s3c2410_nand_init(void)
{
printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n");
+ platform_driver_register(&s3c2412_nand_driver);
platform_driver_register(&s3c2440_nand_driver);
return platform_driver_register(&s3c2410_nand_driver);
}
static void __exit s3c2410_nand_exit(void)
{
+ platform_driver_unregister(&s3c2412_nand_driver);
platform_driver_unregister(&s3c2440_nand_driver);
platform_driver_unregister(&s3c2410_nand_driver);
}
diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c
index a0b4b1edcb0d..f40081069ab2 100644
--- a/drivers/mtd/nand/ts7250.c
+++ b/drivers/mtd/nand/ts7250.c
@@ -97,7 +97,7 @@ static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE;
unsigned char bits;
- bits = (ctrl & NAND_CNE) << 2;
+ bits = (ctrl & NAND_NCE) << 2;
bits |= ctrl & NAND_CLE;
bits |= (ctrl & NAND_ALE) >> 2;
diff --git a/drivers/net/3c501.c b/drivers/net/3c501.c
index bb44509fd404..07136ec423bd 100644
--- a/drivers/net/3c501.c
+++ b/drivers/net/3c501.c
@@ -508,11 +508,11 @@ static int el_start_xmit(struct sk_buff *skb, struct net_device *dev)
* speak of. We simply pull the packet out of its PIO buffer (which is slow)
* and queue it for the kernel. Then we reset the card for the next packet.
*
- * We sometimes get suprise interrupts late both because the SMP IRQ delivery
+ * We sometimes get surprise interrupts late both because the SMP IRQ delivery
* is message passing and because the card sometimes seems to deliver late. I
* think if it is part way through a receive and the mode is changed it carries
* on receiving and sends us an interrupt. We have to band aid all these cases
- * to get a sensible 150kbytes/second performance. Even then you want a small
+ * to get a sensible 150kBytes/second performance. Even then you want a small
* TCP window.
*/
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index e27778926eba..d2f808979a2b 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -375,8 +375,7 @@ limit of 4K.
of the drivers, and will likely be provided by some future kernel.
*/
enum pci_flags_bit {
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
- PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
+ PCI_USES_MASTER=4,
};
enum { IS_VORTEX=1, IS_BOOMERANG=2, IS_CYCLONE=4, IS_TORNADO=8,
@@ -446,95 +445,95 @@ static struct vortex_chip_info {
int io_size;
} vortex_info_tbl[] __devinitdata = {
{"3c590 Vortex 10Mbps",
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c592 EISA 10Mbps Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c597 EISA Fast Demon/Vortex", /* AKPM: from Don's 3c59x_cb.c 0.49H */
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c595 Vortex 100baseTx",
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c595 Vortex 100baseT4",
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c595 Vortex 100base-MII",
- PCI_USES_IO|PCI_USES_MASTER, IS_VORTEX, 32, },
+ PCI_USES_MASTER, IS_VORTEX, 32, },
{"3c900 Boomerang 10baseT",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
+ PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
{"3c900 Boomerang 10Mbps Combo",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
+ PCI_USES_MASTER, IS_BOOMERANG|EEPROM_RESET, 64, },
{"3c900 Cyclone 10Mbps TPO", /* AKPM: from Don's 0.99M */
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c900 Cyclone 10Mbps Combo",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c900 Cyclone 10Mbps TPC", /* AKPM: from Don's 0.99M */
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c900B-FL Cyclone 10base-FL",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c905 Boomerang 100baseTx",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
{"3c905 Boomerang 100baseT4",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_RESET, 64, },
{"3c905B Cyclone 100baseTx",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
{"3c905B Cyclone 10/100/BNC",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
{"3c905B-FX Cyclone 100baseFx",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c905C Tornado",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
{"3c920B-EMB-WNM (ATI Radeon 9100 IGP)",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_MII|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_MII|HAS_HWCKSM, 128, },
{"3c980 Cyclone",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_HWCKSM, 128, },
{"3c980C Python-T",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
{"3cSOHO100-TX Hurricane",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM, 128, },
{"3c555 Laptop Hurricane",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|EEPROM_8BIT|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|EEPROM_8BIT|HAS_HWCKSM, 128, },
{"3c556 Laptop Tornado",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_8BIT|HAS_CB_FNS|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_8BIT|HAS_CB_FNS|INVERT_MII_PWR|
HAS_HWCKSM, 128, },
{"3c556B Laptop Hurricane",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|EEPROM_OFFSET|HAS_CB_FNS|INVERT_MII_PWR|
WNO_XCVR_PWR|HAS_HWCKSM, 128, },
{"3c575 [Megahertz] 10/100 LAN CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
{"3c575 Boomerang CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
+ PCI_USES_MASTER, IS_BOOMERANG|HAS_MII|EEPROM_8BIT, 128, },
{"3CCFE575BT Cyclone CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|
INVERT_LED_PWR|HAS_HWCKSM, 128, },
{"3CCFE575CT Tornado CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
{"3CCFE656 Cyclone CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
INVERT_LED_PWR|HAS_HWCKSM, 128, },
{"3CCFEM656B Cyclone+Winmodem CardBus",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
INVERT_LED_PWR|HAS_HWCKSM, 128, },
{"3CXFEM656C Tornado+Winmodem CardBus", /* From pcmcia-cs-3.1.5 */
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_CB_FNS|EEPROM_8BIT|INVERT_MII_PWR|
MAX_COLLISION_RESET|HAS_HWCKSM, 128, },
{"3c450 HomePNA Tornado", /* AKPM: from Don's 0.99Q */
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
{"3c920 Tornado",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
{"3c982 Hydra Dual Port A",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
{"3c982 Hydra Dual Port B",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_HWCKSM|HAS_NWAY, 128, },
{"3c905B-T4",
- PCI_USES_IO|PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
+ PCI_USES_MASTER, IS_CYCLONE|HAS_NWAY|HAS_HWCKSM|EXTRA_PREAMBLE, 128, },
{"3c920B-EMB-WNM Tornado",
- PCI_USES_IO|PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
+ PCI_USES_MASTER, IS_TORNADO|HAS_NWAY|HAS_HWCKSM, 128, },
{NULL,}, /* NULL terminated list. */
};
@@ -1408,8 +1407,10 @@ static int __devinit vortex_probe1(struct device *gendev,
}
if (print_info) {
- printk(KERN_INFO "%s: CardBus functions mapped %8.8lx->%p\n",
- print_name, pci_resource_start(pdev, 2),
+ printk(KERN_INFO "%s: CardBus functions mapped "
+ "%16.16llx->%p\n",
+ print_name,
+ (unsigned long long)pci_resource_start(pdev, 2),
vp->cb_fn_base);
}
EL3WINDOW(2);
diff --git a/drivers/net/8139cp.c b/drivers/net/8139cp.c
index 0cdc830449d8..d26dd6a7062d 100644
--- a/drivers/net/8139cp.c
+++ b/drivers/net/8139cp.c
@@ -1823,7 +1823,7 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
struct cp_private *cp;
int rc;
void __iomem *regs;
- long pciaddr;
+ resource_size_t pciaddr;
unsigned int addr_len, i, pci_using_dac;
u8 pci_rev;
@@ -1883,8 +1883,8 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
}
if (pci_resource_len(pdev, 1) < CP_REGS_SIZE) {
rc = -EIO;
- printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n",
- pci_resource_len(pdev, 1), pci_name(pdev));
+ printk(KERN_ERR PFX "MMIO resource (%llx) too small on pci dev %s\n",
+ (unsigned long long)pci_resource_len(pdev, 1), pci_name(pdev));
goto err_out_res;
}
@@ -1916,8 +1916,9 @@ static int cp_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
regs = ioremap(pciaddr, CP_REGS_SIZE);
if (!regs) {
rc = -EIO;
- printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n",
- pci_resource_len(pdev, 1), pciaddr, pci_name(pdev));
+ printk(KERN_ERR PFX "Cannot map PCI MMIO (%llx@%llx) on pci dev %s\n",
+ (unsigned long long)pci_resource_len(pdev, 1),
+ (unsigned long long)pciaddr, pci_name(pdev));
goto err_out_res;
}
dev->base_addr = (unsigned long) regs;
diff --git a/drivers/net/8139too.c b/drivers/net/8139too.c
index abd6261465f1..ed2e3c03bc88 100644
--- a/drivers/net/8139too.c
+++ b/drivers/net/8139too.c
@@ -1341,9 +1341,9 @@ static int rtl8139_open (struct net_device *dev)
netif_start_queue (dev);
if (netif_msg_ifup(tp))
- printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#lx IRQ %d"
- " GP Pins %2.2x %s-duplex.\n",
- dev->name, pci_resource_start (tp->pci_dev, 1),
+ printk(KERN_DEBUG "%s: rtl8139_open() ioaddr %#llx IRQ %d"
+ " GP Pins %2.2x %s-duplex.\n", dev->name,
+ (unsigned long long)pci_resource_start (tp->pci_dev, 1),
dev->irq, RTL_R8 (MediaStatus),
tp->mii.full_duplex ? "full" : "half");
diff --git a/drivers/net/dl2k.h b/drivers/net/dl2k.h
index 6e75482d75f2..53449207e53b 100644
--- a/drivers/net/dl2k.h
+++ b/drivers/net/dl2k.h
@@ -683,11 +683,6 @@ struct netdev_private {
};
/* The station address location in the EEPROM. */
-#ifdef MEM_MAPPING
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
-#else
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
-#endif
/* The struct pci_device_id consist of:
vendor, device Vendor and device ID to match (or PCI_ANY_ID)
subvendor, subdevice Subsystem vendor and device ID to match (or PCI_ANY_ID)
@@ -695,9 +690,10 @@ struct netdev_private {
class_mask of the class are honored during the comparison.
driver_data Data private to the driver.
*/
-static struct pci_device_id rio_pci_tbl[] = {
- {0x1186, 0x4000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,}
+
+static const struct pci_device_id rio_pci_tbl[] = {
+ {0x1186, 0x4000, PCI_ANY_ID, PCI_ANY_ID, },
+ { }
};
MODULE_DEVICE_TABLE (pci, rio_pci_tbl);
#define TX_TIMEOUT (4*HZ)
diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c
index 24996da4c1c4..7965a9b08e79 100644
--- a/drivers/net/dm9000.c
+++ b/drivers/net/dm9000.c
@@ -410,10 +410,7 @@ dm9000_probe(struct platform_device *pdev)
if (pdev->num_resources < 2) {
ret = -ENODEV;
goto out;
- }
-
- switch (pdev->num_resources) {
- case 2:
+ } else if (pdev->num_resources == 2) {
base = pdev->resource[0].start;
if (!request_mem_region(base, 4, ndev->name)) {
@@ -423,17 +420,16 @@ dm9000_probe(struct platform_device *pdev)
ndev->base_addr = base;
ndev->irq = pdev->resource[1].start;
- db->io_addr = (void *)base;
- db->io_data = (void *)(base + 4);
-
- break;
+ db->io_addr = (void __iomem *)base;
+ db->io_data = (void __iomem *)(base + 4);
- case 3:
+ } else {
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
- if (db->addr_res == NULL || db->data_res == NULL) {
+ if (db->addr_res == NULL || db->data_res == NULL ||
+ db->irq_res == NULL) {
printk(KERN_ERR PFX "insufficient resources\n");
ret = -ENOENT;
goto out;
@@ -482,7 +478,6 @@ dm9000_probe(struct platform_device *pdev)
/* ensure at least we have a default set of IO routines */
dm9000_set_io(db, iosize);
-
}
/* check to see if anything is being over-ridden */
@@ -564,6 +559,13 @@ dm9000_probe(struct platform_device *pdev)
for (i = 0; i < 6; i++)
ndev->dev_addr[i] = db->srom[i];
+ if (!is_valid_ether_addr(ndev->dev_addr)) {
+ /* try reading from mac */
+
+ for (i = 0; i < 6; i++)
+ ndev->dev_addr[i] = ior(db, i+DM9000_PAR);
+ }
+
if (!is_valid_ether_addr(ndev->dev_addr))
printk("%s: Invalid ethernet MAC address. Please "
"set using ifconfig\n", ndev->name);
@@ -663,7 +665,6 @@ dm9000_init_dm9000(struct net_device *dev)
db->tx_pkt_cnt = 0;
db->queue_pkt_len = 0;
dev->trans_start = 0;
- spin_lock_init(&db->lock);
}
/*
@@ -767,7 +768,7 @@ dm9000_stop(struct net_device *ndev)
* receive the packet to upper layer, free the transmitted packet
*/
-void
+static void
dm9000_tx_done(struct net_device *dev, board_info_t * db)
{
int tx_status = ior(db, DM9000_NSR); /* Got TX status */
@@ -1187,13 +1188,14 @@ dm9000_drv_remove(struct platform_device *pdev)
}
static struct platform_driver dm9000_driver = {
+ .driver = {
+ .name = "dm9000",
+ .owner = THIS_MODULE,
+ },
.probe = dm9000_probe,
.remove = dm9000_drv_remove,
.suspend = dm9000_drv_suspend,
.resume = dm9000_drv_resume,
- .driver = {
- .name = "dm9000",
- },
};
static int __init
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index f37170cc1a37..93a286570923 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -2678,9 +2678,9 @@ static int __devinit e100_probe(struct pci_dev *pdev,
goto err_out_free;
}
- DPRINTK(PROBE, INFO, "addr 0x%lx, irq %d, "
+ DPRINTK(PROBE, INFO, "addr 0x%llx, irq %d, "
"MAC addr %02X:%02X:%02X:%02X:%02X:%02X\n",
- pci_resource_start(pdev, 0), pdev->irq,
+ (unsigned long long)pci_resource_start(pdev, 0), pdev->irq,
netdev->dev_addr[0], netdev->dev_addr[1], netdev->dev_addr[2],
netdev->dev_addr[3], netdev->dev_addr[4], netdev->dev_addr[5]);
diff --git a/drivers/net/eepro100.c b/drivers/net/eepro100.c
index 467fc861360d..ecf5ad85a684 100644
--- a/drivers/net/eepro100.c
+++ b/drivers/net/eepro100.c
@@ -278,11 +278,6 @@ having to sign an Intel NDA when I'm helping Intel sell their own product!
static int speedo_found1(struct pci_dev *pdev, void __iomem *ioaddr, int fnd_cnt, int acpi_idle_state);
-enum pci_flags_bit {
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
- PCI_ADDR0=0x10<<0, PCI_ADDR1=0x10<<1, PCI_ADDR2=0x10<<2, PCI_ADDR3=0x10<<3,
-};
-
/* Offsets to the various registers.
All accesses need not be longword aligned. */
enum speedo_offsets {
diff --git a/drivers/net/epic100.c b/drivers/net/epic100.c
index 724d7dc35fa3..ee34a16eb4e2 100644
--- a/drivers/net/epic100.c
+++ b/drivers/net/epic100.c
@@ -191,23 +191,10 @@ IVc. Errata
*/
-enum pci_id_flags_bits {
- /* Set PCI command register bits before calling probe1(). */
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
- /* Read and map the single following PCI BAR. */
- PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
- PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
-};
-
enum chip_capability_flags { MII_PWRDWN=1, TYPE2_INTR=2, NO_MII=4 };
#define EPIC_TOTAL_SIZE 0x100
#define USE_IO_OPS 1
-#ifdef USE_IO_OPS
-#define EPIC_IOTYPE PCI_USES_MASTER|PCI_USES_IO|PCI_ADDR0
-#else
-#define EPIC_IOTYPE PCI_USES_MASTER|PCI_USES_MEM|PCI_ADDR1
-#endif
typedef enum {
SMSC_83C170_0,
@@ -218,7 +205,6 @@ typedef enum {
struct epic_chip_info {
const char *name;
- enum pci_id_flags_bits pci_flags;
int io_size; /* Needed for I/O region check or ioremap(). */
int drv_flags; /* Driver use, intended as capability flags. */
};
@@ -227,11 +213,11 @@ struct epic_chip_info {
/* indexed by chip_t */
static const struct epic_chip_info pci_id_tbl[] = {
{ "SMSC EPIC/100 83c170",
- EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | NO_MII | MII_PWRDWN },
+ EPIC_TOTAL_SIZE, TYPE2_INTR | NO_MII | MII_PWRDWN },
{ "SMSC EPIC/100 83c170",
- EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR },
+ EPIC_TOTAL_SIZE, TYPE2_INTR },
{ "SMSC EPIC/C 83c175",
- EPIC_IOTYPE, EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN },
+ EPIC_TOTAL_SIZE, TYPE2_INTR | MII_PWRDWN },
};
diff --git a/drivers/net/fealnx.c b/drivers/net/fealnx.c
index a8449265e5fd..13eca7ede2af 100644
--- a/drivers/net/fealnx.c
+++ b/drivers/net/fealnx.c
@@ -126,16 +126,6 @@ MODULE_PARM_DESC(full_duplex, "fealnx full duplex setting(s) (1)");
#define MIN_REGION_SIZE 136
-enum pci_flags_bit {
- PCI_USES_IO = 1,
- PCI_USES_MEM = 2,
- PCI_USES_MASTER = 4,
- PCI_ADDR0 = 0x10 << 0,
- PCI_ADDR1 = 0x10 << 1,
- PCI_ADDR2 = 0x10 << 2,
- PCI_ADDR3 = 0x10 << 3,
-};
-
/* A chip capabilities table, matching the entries in pci_tbl[] above. */
enum chip_capability_flags {
HAS_MII_XCVR,
diff --git a/drivers/net/fec.c b/drivers/net/fec.c
index bd6983d1afba..db694c832989 100644
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -22,7 +22,7 @@
* Copyright (c) 2001-2005 Greg Ungerer (gerg@snapgear.com)
*
* Bug fixes and cleanup by Philippe De Muyter (phdm@macqel.be)
- * Copyright (c) 2004-2005 Macq Electronique SA.
+ * Copyright (c) 2004-2006 Macq Electronique SA.
*/
#include <linux/config.h>
@@ -51,7 +51,7 @@
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || \
defined(CONFIG_M5272) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
#include <asm/coldfire.h>
#include <asm/mcfsim.h>
#include "fec.h"
@@ -80,6 +80,8 @@ static unsigned int fec_hw[] = {
(MCF_MBAR + 0x1000),
#elif defined(CONFIG_M520x)
(MCF_MBAR+0x30000),
+#elif defined(CONFIG_M532x)
+ (MCF_MBAR+0xfc030000),
#else
&(((immap_t *)IMAP_ADDR)->im_cpm.cp_fec),
#endif
@@ -143,7 +145,7 @@ typedef struct {
#define TX_RING_MOD_MASK 15 /* for this to work */
#if (((RX_RING_SIZE + TX_RING_SIZE) * 8) > PAGE_SIZE)
-#error "FEC: descriptor ring size contants too large"
+#error "FEC: descriptor ring size constants too large"
#endif
/* Interrupt events/masks.
@@ -167,12 +169,12 @@ typedef struct {
/*
- * The 5270/5271/5280/5282 RX control register also contains maximum frame
+ * The 5270/5271/5280/5282/532x RX control register also contains maximum frame
* size bits. Other FEC hardware does not, so we need to take that into
* account when setting it.
*/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
#define OPT_FRAME_SIZE (PKT_MAXBUF_SIZE << 16)
#else
#define OPT_FRAME_SIZE 0
@@ -308,6 +310,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
struct fec_enet_private *fep;
volatile fec_t *fecp;
volatile cbd_t *bdp;
+ unsigned short status;
fep = netdev_priv(dev);
fecp = (volatile fec_t*)dev->base_addr;
@@ -320,8 +323,9 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Fill in a Tx ring entry */
bdp = fep->cur_tx;
+ status = bdp->cbd_sc;
#ifndef final_version
- if (bdp->cbd_sc & BD_ENET_TX_READY) {
+ if (status & BD_ENET_TX_READY) {
/* Ooops. All transmit buffers are full. Bail out.
* This should not happen, since dev->tbusy should be set.
*/
@@ -332,7 +336,7 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
/* Clear all of the status flags.
*/
- bdp->cbd_sc &= ~BD_ENET_TX_STATS;
+ status &= ~BD_ENET_TX_STATS;
/* Set buffer length and buffer pointer.
*/
@@ -366,21 +370,22 @@ fec_enet_start_xmit(struct sk_buff *skb, struct net_device *dev)
spin_lock_irq(&fep->lock);
- /* Send it on its way. Tell FEC its ready, interrupt when done,
- * its the last BD of the frame, and to put the CRC on the end.
+ /* Send it on its way. Tell FEC it's ready, interrupt when done,
+ * it's the last BD of the frame, and to put the CRC on the end.
*/
- bdp->cbd_sc |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
+ status |= (BD_ENET_TX_READY | BD_ENET_TX_INTR
| BD_ENET_TX_LAST | BD_ENET_TX_TC);
+ bdp->cbd_sc = status;
dev->trans_start = jiffies;
/* Trigger transmission start */
- fecp->fec_x_des_active = 0x01000000;
+ fecp->fec_x_des_active = 0;
/* If this was the last BD in the ring, start at the beginning again.
*/
- if (bdp->cbd_sc & BD_ENET_TX_WRAP) {
+ if (status & BD_ENET_TX_WRAP) {
bdp = fep->tx_bd_base;
} else {
bdp++;
@@ -491,43 +496,44 @@ fec_enet_tx(struct net_device *dev)
{
struct fec_enet_private *fep;
volatile cbd_t *bdp;
+ unsigned short status;
struct sk_buff *skb;
fep = netdev_priv(dev);
spin_lock(&fep->lock);
bdp = fep->dirty_tx;
- while ((bdp->cbd_sc&BD_ENET_TX_READY) == 0) {
+ while (((status = bdp->cbd_sc) & BD_ENET_TX_READY) == 0) {
if (bdp == fep->cur_tx && fep->tx_full == 0) break;
skb = fep->tx_skbuff[fep->skb_dirty];
/* Check for errors. */
- if (bdp->cbd_sc & (BD_ENET_TX_HB | BD_ENET_TX_LC |
+ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC |
BD_ENET_TX_RL | BD_ENET_TX_UN |
BD_ENET_TX_CSL)) {
fep->stats.tx_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_HB) /* No heartbeat */
+ if (status & BD_ENET_TX_HB) /* No heartbeat */
fep->stats.tx_heartbeat_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_LC) /* Late collision */
+ if (status & BD_ENET_TX_LC) /* Late collision */
fep->stats.tx_window_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_RL) /* Retrans limit */
+ if (status & BD_ENET_TX_RL) /* Retrans limit */
fep->stats.tx_aborted_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_UN) /* Underrun */
+ if (status & BD_ENET_TX_UN) /* Underrun */
fep->stats.tx_fifo_errors++;
- if (bdp->cbd_sc & BD_ENET_TX_CSL) /* Carrier lost */
+ if (status & BD_ENET_TX_CSL) /* Carrier lost */
fep->stats.tx_carrier_errors++;
} else {
fep->stats.tx_packets++;
}
#ifndef final_version
- if (bdp->cbd_sc & BD_ENET_TX_READY)
+ if (status & BD_ENET_TX_READY)
printk("HEY! Enet xmit interrupt and TX_READY.\n");
#endif
/* Deferred means some collisions occurred during transmit,
* but we eventually sent the packet OK.
*/
- if (bdp->cbd_sc & BD_ENET_TX_DEF)
+ if (status & BD_ENET_TX_DEF)
fep->stats.collisions++;
/* Free the sk buffer associated with this last transmit.
@@ -538,7 +544,7 @@ fec_enet_tx(struct net_device *dev)
/* Update pointer to next buffer descriptor to be transmitted.
*/
- if (bdp->cbd_sc & BD_ENET_TX_WRAP)
+ if (status & BD_ENET_TX_WRAP)
bdp = fep->tx_bd_base;
else
bdp++;
@@ -568,9 +574,14 @@ fec_enet_rx(struct net_device *dev)
struct fec_enet_private *fep;
volatile fec_t *fecp;
volatile cbd_t *bdp;
+ unsigned short status;
struct sk_buff *skb;
ushort pkt_len;
__u8 *data;
+
+#ifdef CONFIG_M532x
+ flush_cache_all();
+#endif
fep = netdev_priv(dev);
fecp = (volatile fec_t*)dev->base_addr;
@@ -580,13 +591,13 @@ fec_enet_rx(struct net_device *dev)
*/
bdp = fep->cur_rx;
-while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
+while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) {
#ifndef final_version
/* Since we have allocated space to hold a complete frame,
* the last indicator should be set.
*/
- if ((bdp->cbd_sc & BD_ENET_RX_LAST) == 0)
+ if ((status & BD_ENET_RX_LAST) == 0)
printk("FEC ENET: rcv is not +last\n");
#endif
@@ -594,26 +605,26 @@ while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
goto rx_processing_done;
/* Check for errors. */
- if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
+ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH | BD_ENET_RX_NO |
BD_ENET_RX_CR | BD_ENET_RX_OV)) {
fep->stats.rx_errors++;
- if (bdp->cbd_sc & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
+ if (status & (BD_ENET_RX_LG | BD_ENET_RX_SH)) {
/* Frame too long or too short. */
fep->stats.rx_length_errors++;
}
- if (bdp->cbd_sc & BD_ENET_RX_NO) /* Frame alignment */
+ if (status & BD_ENET_RX_NO) /* Frame alignment */
fep->stats.rx_frame_errors++;
- if (bdp->cbd_sc & BD_ENET_RX_CR) /* CRC Error */
- fep->stats.rx_crc_errors++;
- if (bdp->cbd_sc & BD_ENET_RX_OV) /* FIFO overrun */
+ if (status & BD_ENET_RX_CR) /* CRC Error */
fep->stats.rx_crc_errors++;
+ if (status & BD_ENET_RX_OV) /* FIFO overrun */
+ fep->stats.rx_fifo_errors++;
}
/* Report late collisions as a frame error.
* On this error, the BD is closed, but we don't know what we
* have in the buffer. So, just drop this frame on the floor.
*/
- if (bdp->cbd_sc & BD_ENET_RX_CL) {
+ if (status & BD_ENET_RX_CL) {
fep->stats.rx_errors++;
fep->stats.rx_frame_errors++;
goto rx_processing_done;
@@ -639,9 +650,7 @@ while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
} else {
skb->dev = dev;
skb_put(skb,pkt_len-4); /* Make room */
- eth_copy_and_sum(skb,
- (unsigned char *)__va(bdp->cbd_bufaddr),
- pkt_len-4, 0);
+ eth_copy_and_sum(skb, data, pkt_len-4, 0);
skb->protocol=eth_type_trans(skb,dev);
netif_rx(skb);
}
@@ -649,15 +658,16 @@ while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
/* Clear the status flags for this buffer.
*/
- bdp->cbd_sc &= ~BD_ENET_RX_STATS;
+ status &= ~BD_ENET_RX_STATS;
/* Mark the buffer empty.
*/
- bdp->cbd_sc |= BD_ENET_RX_EMPTY;
+ status |= BD_ENET_RX_EMPTY;
+ bdp->cbd_sc = status;
/* Update BD pointer to next entry.
*/
- if (bdp->cbd_sc & BD_ENET_RX_WRAP)
+ if (status & BD_ENET_RX_WRAP)
bdp = fep->rx_bd_base;
else
bdp++;
@@ -667,9 +677,9 @@ while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
* incoming frames. On a heavily loaded network, we should be
* able to keep up at the expense of system resources.
*/
- fecp->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0;
#endif
- } /* while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) */
+ } /* while (!((status = bdp->cbd_sc) & BD_ENET_RX_EMPTY)) */
fep->cur_rx = (cbd_t *)bdp;
#if 0
@@ -680,11 +690,12 @@ while (!(bdp->cbd_sc & BD_ENET_RX_EMPTY)) {
* our way back to the interrupt return only to come right back
* here.
*/
- fecp->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0;
#endif
}
+/* called from interrupt context */
static void
fec_enet_mii(struct net_device *dev)
{
@@ -696,10 +707,12 @@ fec_enet_mii(struct net_device *dev)
fep = netdev_priv(dev);
ep = fep->hwp;
mii_reg = ep->fec_mii_data;
+
+ spin_lock(&fep->lock);
if ((mip = mii_head) == NULL) {
printk("MII and no head!\n");
- return;
+ goto unlock;
}
if (mip->mii_func != NULL)
@@ -711,6 +724,9 @@ fec_enet_mii(struct net_device *dev)
if ((mip = mii_head) != NULL)
ep->fec_mii_data = mip->mii_regval;
+
+unlock:
+ spin_unlock(&fep->lock);
}
static int
@@ -728,8 +744,7 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi
retval = 0;
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&fep->lock,flags);
if ((mip = mii_free) != NULL) {
mii_free = mip->mii_next;
@@ -749,7 +764,7 @@ mii_queue(struct net_device *dev, int regval, void (*func)(uint, struct net_devi
retval = 1;
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&fep->lock,flags);
return(retval);
}
@@ -1216,7 +1231,7 @@ static phy_info_t const * const phy_info[] = {
};
/* ------------------------------------------------------------------------- */
-
+#if !defined(CONFIG_M532x)
#ifdef CONFIG_RPXCLASSIC
static void
mii_link_interrupt(void *dev_id);
@@ -1224,6 +1239,7 @@ mii_link_interrupt(void *dev_id);
static irqreturn_t
mii_link_interrupt(int irq, void * dev_id, struct pt_regs * regs);
#endif
+#endif
#if defined(CONFIG_M5272)
@@ -1384,13 +1400,13 @@ static void __inline__ fec_request_intrs(struct net_device *dev)
{
volatile unsigned char *icrp;
volatile unsigned long *imrp;
- int i;
+ int i, ilip;
b = (fep->index) ? MCFICM_INTC1 : MCFICM_INTC0;
icrp = (volatile unsigned char *) (MCF_IPSBAR + b +
MCFINTC_ICR0);
- for (i = 23; (i < 36); i++)
- icrp[i] = 0x23;
+ for (i = 23, ilip = 0x28; (i < 36); i++)
+ icrp[i] = ilip--;
imrp = (volatile unsigned long *) (MCF_IPSBAR + b +
MCFINTC_IMRH);
@@ -1618,6 +1634,159 @@ static void __inline__ fec_uncache(unsigned long addr)
/* ------------------------------------------------------------------------- */
+#elif defined(CONFIG_M532x)
+/*
+ * Code specific for M532x
+ */
+static void __inline__ fec_request_intrs(struct net_device *dev)
+{
+ struct fec_enet_private *fep;
+ int b;
+ static const struct idesc {
+ char *name;
+ unsigned short irq;
+ } *idp, id[] = {
+ { "fec(TXF)", 36 },
+ { "fec(TXB)", 37 },
+ { "fec(TXFIFO)", 38 },
+ { "fec(TXCR)", 39 },
+ { "fec(RXF)", 40 },
+ { "fec(RXB)", 41 },
+ { "fec(MII)", 42 },
+ { "fec(LC)", 43 },
+ { "fec(HBERR)", 44 },
+ { "fec(GRA)", 45 },
+ { "fec(EBERR)", 46 },
+ { "fec(BABT)", 47 },
+ { "fec(BABR)", 48 },
+ { NULL },
+ };
+
+ fep = netdev_priv(dev);
+ b = (fep->index) ? 128 : 64;
+
+ /* Setup interrupt handlers. */
+ for (idp = id; idp->name; idp++) {
+ if (request_irq(b+idp->irq,fec_enet_interrupt,0,idp->name,dev)!=0)
+ printk("FEC: Could not allocate %s IRQ(%d)!\n",
+ idp->name, b+idp->irq);
+ }
+
+ /* Unmask interrupts */
+ MCF_INTC0_ICR36 = 0x2;
+ MCF_INTC0_ICR37 = 0x2;
+ MCF_INTC0_ICR38 = 0x2;
+ MCF_INTC0_ICR39 = 0x2;
+ MCF_INTC0_ICR40 = 0x2;
+ MCF_INTC0_ICR41 = 0x2;
+ MCF_INTC0_ICR42 = 0x2;
+ MCF_INTC0_ICR43 = 0x2;
+ MCF_INTC0_ICR44 = 0x2;
+ MCF_INTC0_ICR45 = 0x2;
+ MCF_INTC0_ICR46 = 0x2;
+ MCF_INTC0_ICR47 = 0x2;
+ MCF_INTC0_ICR48 = 0x2;
+
+ MCF_INTC0_IMRH &= ~(
+ MCF_INTC_IMRH_INT_MASK36 |
+ MCF_INTC_IMRH_INT_MASK37 |
+ MCF_INTC_IMRH_INT_MASK38 |
+ MCF_INTC_IMRH_INT_MASK39 |
+ MCF_INTC_IMRH_INT_MASK40 |
+ MCF_INTC_IMRH_INT_MASK41 |
+ MCF_INTC_IMRH_INT_MASK42 |
+ MCF_INTC_IMRH_INT_MASK43 |
+ MCF_INTC_IMRH_INT_MASK44 |
+ MCF_INTC_IMRH_INT_MASK45 |
+ MCF_INTC_IMRH_INT_MASK46 |
+ MCF_INTC_IMRH_INT_MASK47 |
+ MCF_INTC_IMRH_INT_MASK48 );
+
+ /* Set up gpio outputs for MII lines */
+ MCF_GPIO_PAR_FECI2C |= (0 |
+ MCF_GPIO_PAR_FECI2C_PAR_MDC_EMDC |
+ MCF_GPIO_PAR_FECI2C_PAR_MDIO_EMDIO);
+ MCF_GPIO_PAR_FEC = (0 |
+ MCF_GPIO_PAR_FEC_PAR_FEC_7W_FEC |
+ MCF_GPIO_PAR_FEC_PAR_FEC_MII_FEC);
+}
+
+static void __inline__ fec_set_mii(struct net_device *dev, struct fec_enet_private *fep)
+{
+ volatile fec_t *fecp;
+
+ fecp = fep->hwp;
+ fecp->fec_r_cntrl = OPT_FRAME_SIZE | 0x04;
+ fecp->fec_x_cntrl = 0x00;
+
+ /*
+ * Set MII speed to 2.5 MHz
+ */
+ fep->phy_speed = ((((MCF_CLK / 2) / (2500000 / 10)) + 5) / 10) * 2;
+ fecp->fec_mii_speed = fep->phy_speed;
+
+ fec_restart(dev, 0);
+}
+
+static void __inline__ fec_get_mac(struct net_device *dev)
+{
+ struct fec_enet_private *fep = netdev_priv(dev);
+ volatile fec_t *fecp;
+ unsigned char *iap, tmpaddr[ETH_ALEN];
+
+ fecp = fep->hwp;
+
+ if (FEC_FLASHMAC) {
+ /*
+ * Get MAC address from FLASH.
+ * If it is all 1's or 0's, use the default.
+ */
+ iap = FEC_FLASHMAC;
+ if ((iap[0] == 0) && (iap[1] == 0) && (iap[2] == 0) &&
+ (iap[3] == 0) && (iap[4] == 0) && (iap[5] == 0))
+ iap = fec_mac_default;
+ if ((iap[0] == 0xff) && (iap[1] == 0xff) && (iap[2] == 0xff) &&
+ (iap[3] == 0xff) && (iap[4] == 0xff) && (iap[5] == 0xff))
+ iap = fec_mac_default;
+ } else {
+ *((unsigned long *) &tmpaddr[0]) = fecp->fec_addr_low;
+ *((unsigned short *) &tmpaddr[4]) = (fecp->fec_addr_high >> 16);
+ iap = &tmpaddr[0];
+ }
+
+ memcpy(dev->dev_addr, iap, ETH_ALEN);
+
+ /* Adjust MAC if using default MAC address */
+ if (iap == fec_mac_default)
+ dev->dev_addr[ETH_ALEN-1] = fec_mac_default[ETH_ALEN-1] + fep->index;
+}
+
+static void __inline__ fec_enable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_disable_phy_intr(void)
+{
+}
+
+static void __inline__ fec_phy_ack_intr(void)
+{
+}
+
+static void __inline__ fec_localhw_setup(void)
+{
+}
+
+/*
+ * Do not need to make region uncached on 532x.
+ */
+static void __inline__ fec_uncache(unsigned long addr)
+{
+}
+
+/* ------------------------------------------------------------------------- */
+
+
#else
/*
@@ -1985,9 +2154,12 @@ fec_enet_open(struct net_device *dev)
mii_do_cmd(dev, fep->phy->config);
mii_do_cmd(dev, phy_cmd_config); /* display configuration */
- /* FIXME: use netif_carrier_{on,off} ; this polls
- * until link is up which is wrong... could be
- * 30 seconds or more we are trapped in here. -jgarzik
+ /* Poll until the PHY tells us its configuration
+ * (not link state).
+ * Request is initiated by mii_do_cmd above, but answer
+ * comes by interrupt.
+ * This should take about 25 usec per register at 2.5 MHz,
+ * and we read approximately 5 registers.
*/
while(!fep->sequence_done)
schedule();
@@ -2253,15 +2425,11 @@ int __init fec_enet_init(struct net_device *dev)
*/
fec_request_intrs(dev);
- /* Clear and enable interrupts */
- fecp->fec_ievent = 0xffc00000;
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
fecp->fec_hash_table_high = 0;
fecp->fec_hash_table_low = 0;
fecp->fec_r_buff_size = PKT_MAXBLR_SIZE;
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0;
dev->base_addr = (unsigned long)fecp;
@@ -2281,6 +2449,11 @@ int __init fec_enet_init(struct net_device *dev)
/* setup MII interface */
fec_set_mii(dev, fep);
+ /* Clear and enable interrupts */
+ fecp->fec_ievent = 0xffc00000;
+ fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
+ FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
+
/* Queue up command to detect the PHY and initialize the
* remainder of the interface.
*/
@@ -2312,11 +2485,6 @@ fec_restart(struct net_device *dev, int duplex)
fecp->fec_ecntrl = 1;
udelay(10);
- /* Enable interrupts we wish to service.
- */
- fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
- FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
-
/* Clear any outstanding interrupt.
*/
fecp->fec_ievent = 0xffc00000;
@@ -2408,7 +2576,12 @@ fec_restart(struct net_device *dev, int duplex)
/* And last, enable the transmit and receive processing.
*/
fecp->fec_ecntrl = 2;
- fecp->fec_r_des_active = 0x01000000;
+ fecp->fec_r_des_active = 0;
+
+ /* Enable interrupts we wish to service.
+ */
+ fecp->fec_imask = (FEC_ENET_TXF | FEC_ENET_TXB |
+ FEC_ENET_RXF | FEC_ENET_RXB | FEC_ENET_MII);
}
static void
@@ -2420,9 +2593,16 @@ fec_stop(struct net_device *dev)
fep = netdev_priv(dev);
fecp = fep->hwp;
- fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */
-
- while(!(fecp->fec_ievent & FEC_ENET_GRA));
+ /*
+ ** We cannot expect a graceful transmit stop without link !!!
+ */
+ if (fep->link)
+ {
+ fecp->fec_x_cntrl = 0x01; /* Graceful transmit stop */
+ udelay(10);
+ if (!(fecp->fec_ievent & FEC_ENET_GRA))
+ printk("fec_stop : Graceful transmit stop did not complete !\n");
+ }
/* Whack a reset. We should wait for this.
*/
diff --git a/drivers/net/fec.h b/drivers/net/fec.h
index 965c5c49fcdc..1d421606984f 100644
--- a/drivers/net/fec.h
+++ b/drivers/net/fec.h
@@ -14,7 +14,7 @@
/****************************************************************************/
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
/*
* Just figures, Motorola would have to change the offsets for
* registers in the same peripheral device on different models
diff --git a/drivers/net/fs_enet/fs_enet-mii.c b/drivers/net/fs_enet/fs_enet-mii.c
index c6770377ef87..0cd07150bf4a 100644
--- a/drivers/net/fs_enet/fs_enet-mii.c
+++ b/drivers/net/fs_enet/fs_enet-mii.c
@@ -431,8 +431,7 @@ static struct fs_enet_mii_bus *create_bus(const struct fs_mii_bus_info *bi)
return bus;
err:
- if (bus)
- kfree(bus);
+ kfree(bus);
return ERR_PTR(ret);
}
diff --git a/drivers/net/hamradio/dmascc.c b/drivers/net/hamradio/dmascc.c
index 0d5fccc984bb..c9a46b89942a 100644
--- a/drivers/net/hamradio/dmascc.c
+++ b/drivers/net/hamradio/dmascc.c
@@ -436,7 +436,7 @@ static int __init dmascc_init(void)
module_init(dmascc_init);
module_exit(dmascc_exit);
-static void dev_setup(struct net_device *dev)
+static void __init dev_setup(struct net_device *dev)
{
dev->type = ARPHRD_AX25;
dev->hard_header_len = AX25_MAX_HEADER_LEN;
diff --git a/drivers/net/irda/irport.c b/drivers/net/irda/irport.c
index 98fa5319e5cc..44efd49bf4a9 100644
--- a/drivers/net/irda/irport.c
+++ b/drivers/net/irda/irport.c
@@ -386,7 +386,7 @@ static int __irport_change_speed(struct irda_task *task)
/* Locking notes : this function may be called from irq context with
* spinlock, via irport_write_wakeup(), or from non-interrupt without
* spinlock (from the task timer). Yuck !
- * This is ugly, and unsafe is the spinlock is not already aquired.
+ * This is ugly, and unsafe is the spinlock is not already acquired.
* This will be fixed when irda-task get rewritten.
* Jean II */
if (!spin_is_locked(&self->lock)) {
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index cc7ff8f00e42..cb62f2a9676a 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -115,8 +115,12 @@ static nsc_chip_t chips[] = {
/* Contributed by Jan Frey - IBM A30/A31 */
{ "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff,
nsc_ircc_probe_39x, nsc_ircc_init_39x },
- { "IBM", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
- nsc_ircc_probe_39x, nsc_ircc_init_39x },
+ /* IBM ThinkPads using PC8738x (T60/X60/Z60) */
+ { "IBM-PC8738x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
+ nsc_ircc_probe_39x, nsc_ircc_init_39x },
+ /* IBM ThinkPads using PC8394T (T43/R52/?) */
+ { "IBM-PC8394T", { 0x2e, 0x4e, 0x0 }, 0x20, 0xf9, 0xff,
+ nsc_ircc_probe_39x, nsc_ircc_init_39x },
{ NULL }
};
diff --git a/drivers/net/natsemi.c b/drivers/net/natsemi.c
index 2e4ecedba057..5657049c2160 100644
--- a/drivers/net/natsemi.c
+++ b/drivers/net/natsemi.c
@@ -226,7 +226,6 @@ static int full_duplex[MAX_UNITS];
NATSEMI_PG1_NREGS)
#define NATSEMI_REGS_VER 1 /* v1 added RFDR registers */
#define NATSEMI_REGS_SIZE (NATSEMI_NREGS * sizeof(u32))
-#define NATSEMI_DEF_EEPROM_SIZE 24 /* 12 16-bit values */
/* Buffer sizes:
* The nic writes 32-bit values, even if the upper bytes of
@@ -344,18 +343,6 @@ None characterised.
-enum pcistuff {
- PCI_USES_IO = 0x01,
- PCI_USES_MEM = 0x02,
- PCI_USES_MASTER = 0x04,
- PCI_ADDR0 = 0x08,
- PCI_ADDR1 = 0x10,
-};
-
-/* MMIO operations required */
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
-
-
/*
* Support for fibre connections on Am79C874:
* This phy needs a special setup when connected to a fibre cable.
@@ -363,22 +350,25 @@ enum pcistuff {
*/
#define PHYID_AM79C874 0x0022561b
-#define MII_MCTRL 0x15 /* mode control register */
-#define MII_FX_SEL 0x0001 /* 100BASE-FX (fiber) */
-#define MII_EN_SCRM 0x0004 /* enable scrambler (tp) */
+enum {
+ MII_MCTRL = 0x15, /* mode control register */
+ MII_FX_SEL = 0x0001, /* 100BASE-FX (fiber) */
+ MII_EN_SCRM = 0x0004, /* enable scrambler (tp) */
+};
/* array of board data directly indexed by pci_tbl[x].driver_data */
static const struct {
const char *name;
unsigned long flags;
+ unsigned int eeprom_size;
} natsemi_pci_info[] __devinitdata = {
- { "NatSemi DP8381[56]", PCI_IOTYPE },
+ { "NatSemi DP8381[56]", 0, 24 },
};
-static struct pci_device_id natsemi_pci_tbl[] = {
- { PCI_VENDOR_ID_NS, PCI_DEVICE_ID_NS_83815, PCI_ANY_ID, PCI_ANY_ID, },
- { 0, },
+static const struct pci_device_id natsemi_pci_tbl[] __devinitdata = {
+ { PCI_VENDOR_ID_NS, 0x0020, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+ { } /* terminate list */
};
MODULE_DEVICE_TABLE(pci, natsemi_pci_tbl);
@@ -813,6 +803,42 @@ static void move_int_phy(struct net_device *dev, int addr)
udelay(1);
}
+static void __devinit natsemi_init_media (struct net_device *dev)
+{
+ struct netdev_private *np = netdev_priv(dev);
+ u32 tmp;
+
+ netif_carrier_off(dev);
+
+ /* get the initial settings from hardware */
+ tmp = mdio_read(dev, MII_BMCR);
+ np->speed = (tmp & BMCR_SPEED100)? SPEED_100 : SPEED_10;
+ np->duplex = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL : DUPLEX_HALF;
+ np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
+ np->advertising= mdio_read(dev, MII_ADVERTISE);
+
+ if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
+ && netif_msg_probe(np)) {
+ printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
+ "10%s %s duplex.\n",
+ pci_name(np->pci_dev),
+ (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?
+ "enabled, advertise" : "disabled, force",
+ (np->advertising &
+ (ADVERTISE_100FULL|ADVERTISE_100HALF))?
+ "0" : "",
+ (np->advertising &
+ (ADVERTISE_100FULL|ADVERTISE_10FULL))?
+ "full" : "half");
+ }
+ if (netif_msg_probe(np))
+ printk(KERN_INFO
+ "natsemi %s: Transceiver status %#04x advertising %#04x.\n",
+ pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),
+ np->advertising);
+
+}
+
static int __devinit natsemi_probe1 (struct pci_dev *pdev,
const struct pci_device_id *ent)
{
@@ -852,8 +878,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
iosize = pci_resource_len(pdev, pcibar);
irq = pdev->irq;
- if (natsemi_pci_info[chip_idx].flags & PCI_USES_MASTER)
- pci_set_master(pdev);
+ pci_set_master(pdev);
dev = alloc_etherdev(sizeof (struct netdev_private));
if (!dev)
@@ -892,7 +917,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
np->msg_enable = (debug >= 0) ? (1<<debug)-1 : NATSEMI_DEF_MSG;
np->hands_off = 0;
np->intr_status = 0;
- np->eeprom_size = NATSEMI_DEF_EEPROM_SIZE;
+ np->eeprom_size = natsemi_pci_info[chip_idx].eeprom_size;
/* Initial port:
* - If the nic was configured to use an external phy and if find_mii
@@ -957,34 +982,7 @@ static int __devinit natsemi_probe1 (struct pci_dev *pdev,
if (mtu)
dev->mtu = mtu;
- netif_carrier_off(dev);
-
- /* get the initial settings from hardware */
- tmp = mdio_read(dev, MII_BMCR);
- np->speed = (tmp & BMCR_SPEED100)? SPEED_100 : SPEED_10;
- np->duplex = (tmp & BMCR_FULLDPLX)? DUPLEX_FULL : DUPLEX_HALF;
- np->autoneg = (tmp & BMCR_ANENABLE)? AUTONEG_ENABLE: AUTONEG_DISABLE;
- np->advertising= mdio_read(dev, MII_ADVERTISE);
-
- if ((np->advertising & ADVERTISE_ALL) != ADVERTISE_ALL
- && netif_msg_probe(np)) {
- printk(KERN_INFO "natsemi %s: Transceiver default autonegotiation %s "
- "10%s %s duplex.\n",
- pci_name(np->pci_dev),
- (mdio_read(dev, MII_BMCR) & BMCR_ANENABLE)?
- "enabled, advertise" : "disabled, force",
- (np->advertising &
- (ADVERTISE_100FULL|ADVERTISE_100HALF))?
- "0" : "",
- (np->advertising &
- (ADVERTISE_100FULL|ADVERTISE_10FULL))?
- "full" : "half");
- }
- if (netif_msg_probe(np))
- printk(KERN_INFO
- "natsemi %s: Transceiver status %#04x advertising %#04x.\n",
- pci_name(np->pci_dev), mdio_read(dev, MII_BMSR),
- np->advertising);
+ natsemi_init_media(dev);
/* save the silicon revision for later querying */
np->srr = readl(ioaddr + SiliconRev);
diff --git a/drivers/net/pcmcia/smc91c92_cs.c b/drivers/net/pcmcia/smc91c92_cs.c
index e74bf5014ef6..a73d54553030 100644
--- a/drivers/net/pcmcia/smc91c92_cs.c
+++ b/drivers/net/pcmcia/smc91c92_cs.c
@@ -1883,7 +1883,7 @@ static void smc_reset(struct net_device *dev)
/* Set the Window 1 control, configuration and station addr registers.
No point in writing the I/O base register ;-> */
SMC_SELECT_BANK(1);
- /* Automatically release succesfully transmitted packets,
+ /* Automatically release successfully transmitted packets,
Accept link errors, counter and Tx error interrupts. */
outw(CTL_AUTO_RELEASE | CTL_TE_ENABLE | CTL_CR_ENABLE,
ioaddr + CONTROL);
diff --git a/drivers/net/pcnet32.c b/drivers/net/pcnet32.c
index fc08c4af506c..0e01c75da429 100644
--- a/drivers/net/pcnet32.c
+++ b/drivers/net/pcnet32.c
@@ -309,12 +309,6 @@ static int pcnet32_alloc_ring(struct net_device *dev, char *name);
static void pcnet32_free_ring(struct net_device *dev);
static void pcnet32_check_media(struct net_device *dev, int verbose);
-enum pci_flags_bit {
- PCI_USES_IO = 1, PCI_USES_MEM = 2, PCI_USES_MASTER = 4,
- PCI_ADDR0 = 0x10 << 0, PCI_ADDR1 = 0x10 << 1, PCI_ADDR2 =
- 0x10 << 2, PCI_ADDR3 = 0x10 << 3,
-};
-
static u16 pcnet32_wio_read_csr(unsigned long addr, int index)
{
outw(index, addr + PCNET32_WIO_RAP);
diff --git a/drivers/net/phy/lxt.c b/drivers/net/phy/lxt.c
index bef79e454c33..3f702c503afe 100644
--- a/drivers/net/phy/lxt.c
+++ b/drivers/net/phy/lxt.c
@@ -123,9 +123,9 @@ static int lxt971_config_intr(struct phy_device *phydev)
}
static struct phy_driver lxt970_driver = {
- .phy_id = 0x07810000,
+ .phy_id = 0x78100000,
.name = "LXT970",
- .phy_id_mask = 0x0fffffff,
+ .phy_id_mask = 0xfffffff0,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_init = lxt970_config_init,
@@ -137,9 +137,9 @@ static struct phy_driver lxt970_driver = {
};
static struct phy_driver lxt971_driver = {
- .phy_id = 0x0001378e,
+ .phy_id = 0x001378e0,
.name = "LXT971",
- .phy_id_mask = 0x0fffffff,
+ .phy_id_mask = 0xfffffff0,
.features = PHY_BASIC_FEATURES,
.flags = PHY_HAS_INTERRUPT,
.config_aneg = genphy_config_aneg,
diff --git a/drivers/net/ppp_generic.c b/drivers/net/ppp_generic.c
index 01cd8ec751ea..d643a097faa5 100644
--- a/drivers/net/ppp_generic.c
+++ b/drivers/net/ppp_generic.c
@@ -2578,8 +2578,7 @@ ppp_find_channel(int unit)
list_for_each_entry(pch, &new_channels, list) {
if (pch->file.index == unit) {
- list_del(&pch->list);
- list_add(&pch->list, &all_channels);
+ list_move(&pch->list, &all_channels);
return pch;
}
}
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 19a4a16055dc..1608efab4e3d 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -3354,8 +3354,8 @@ static int __devinit skge_probe(struct pci_dev *pdev,
if (err)
goto err_out_free_irq;
- printk(KERN_INFO PFX DRV_VERSION " addr 0x%lx irq %d chip %s rev %d\n",
- pci_resource_start(pdev, 0), pdev->irq,
+ printk(KERN_INFO PFX DRV_VERSION " addr 0x%llx irq %d chip %s rev %d\n",
+ (unsigned long long)pci_resource_start(pdev, 0), pdev->irq,
skge_board_name(hw), hw->chip_rev);
if ((dev = skge_devinit(hw, 0, using_dac)) == NULL)
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index d3577871be28..e122007e16da 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -3311,9 +3311,9 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
if (err)
goto err_out_iounmap;
- printk(KERN_INFO PFX "v%s addr 0x%lx irq %d Yukon-%s (0x%x) rev %d\n",
- DRV_VERSION, pci_resource_start(pdev, 0), pdev->irq,
- yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL],
+ printk(KERN_INFO PFX "v%s addr 0x%llx irq %d Yukon-%s (0x%x) rev %d\n",
+ DRV_VERSION, (unsigned long long)pci_resource_start(pdev, 0),
+ pdev->irq, yukon2_name[hw->chip_id - CHIP_ID_YUKON_XL],
hw->chip_id, hw->chip_rev);
dev = sky2_init_netdev(hw, 0, using_dac);
diff --git a/drivers/net/tulip/de2104x.c b/drivers/net/tulip/de2104x.c
index 5f743b972949..fc2468ecce0b 100644
--- a/drivers/net/tulip/de2104x.c
+++ b/drivers/net/tulip/de2104x.c
@@ -2007,8 +2007,8 @@ static int __init de_init_one (struct pci_dev *pdev,
}
if (pci_resource_len(pdev, 1) < DE_REGS_SIZE) {
rc = -EIO;
- printk(KERN_ERR PFX "MMIO resource (%lx) too small on pci dev %s\n",
- pci_resource_len(pdev, 1), pci_name(pdev));
+ printk(KERN_ERR PFX "MMIO resource (%llx) too small on pci dev %s\n",
+ (unsigned long long)pci_resource_len(pdev, 1), pci_name(pdev));
goto err_out_res;
}
@@ -2016,8 +2016,9 @@ static int __init de_init_one (struct pci_dev *pdev,
regs = ioremap_nocache(pciaddr, DE_REGS_SIZE);
if (!regs) {
rc = -EIO;
- printk(KERN_ERR PFX "Cannot map PCI MMIO (%lx@%lx) on pci dev %s\n",
- pci_resource_len(pdev, 1), pciaddr, pci_name(pdev));
+ printk(KERN_ERR PFX "Cannot map PCI MMIO (%llx@%lx) on pci dev %s\n",
+ (unsigned long long)pci_resource_len(pdev, 1),
+ pciaddr, pci_name(pdev));
goto err_out_res;
}
dev->base_addr = (unsigned long) regs;
diff --git a/drivers/net/tulip/tulip_core.c b/drivers/net/tulip/tulip_core.c
index e0de66739a42..53fd9b56d0bd 100644
--- a/drivers/net/tulip/tulip_core.c
+++ b/drivers/net/tulip/tulip_core.c
@@ -1350,10 +1350,10 @@ static int __devinit tulip_init_one (struct pci_dev *pdev,
SET_MODULE_OWNER(dev);
SET_NETDEV_DEV(dev, &pdev->dev);
if (pci_resource_len (pdev, 0) < tulip_tbl[chip_idx].io_size) {
- printk (KERN_ERR PFX "%s: I/O region (0x%lx@0x%lx) too small, "
+ printk (KERN_ERR PFX "%s: I/O region (0x%llx@0x%llx) too small, "
"aborting\n", pci_name(pdev),
- pci_resource_len (pdev, 0),
- pci_resource_start (pdev, 0));
+ (unsigned long long)pci_resource_len (pdev, 0),
+ (unsigned long long)pci_resource_start (pdev, 0));
goto err_out_free_netdev;
}
diff --git a/drivers/net/tulip/winbond-840.c b/drivers/net/tulip/winbond-840.c
index 8fea2aa455d4..602a6e5002a0 100644
--- a/drivers/net/tulip/winbond-840.c
+++ b/drivers/net/tulip/winbond-840.c
@@ -212,26 +212,15 @@ Test with 'ping -s 10000' on a fast computer.
/*
PCI probe table.
*/
-enum pci_id_flags_bits {
- /* Set PCI command register bits before calling probe1(). */
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
- /* Read and map the single following PCI BAR. */
- PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
- PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
-};
enum chip_capability_flags {
- CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,};
-#ifdef USE_IO_OPS
-#define W840_FLAGS (PCI_USES_IO | PCI_ADDR0 | PCI_USES_MASTER)
-#else
-#define W840_FLAGS (PCI_USES_MEM | PCI_ADDR1 | PCI_USES_MASTER)
-#endif
+ CanHaveMII=1, HasBrokenTx=2, AlwaysFDX=4, FDXOnNoMII=8,
+};
-static struct pci_device_id w840_pci_tbl[] = {
+static const struct pci_device_id w840_pci_tbl[] = {
{ 0x1050, 0x0840, PCI_ANY_ID, 0x8153, 0, 0, 0 },
{ 0x1050, 0x0840, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
{ 0x11f6, 0x2011, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 2 },
- { 0, }
+ { }
};
MODULE_DEVICE_TABLE(pci, w840_pci_tbl);
@@ -241,18 +230,17 @@ struct pci_id_info {
int pci, pci_mask, subsystem, subsystem_mask;
int revision, revision_mask; /* Only 8 bits. */
} id;
- enum pci_id_flags_bits pci_flags;
int io_size; /* Needed for I/O region check or ioremap(). */
int drv_flags; /* Driver use, intended as capability flags. */
};
static struct pci_id_info pci_id_tbl[] = {
{"Winbond W89c840", /* Sometime a Level-One switch card. */
{ 0x08401050, 0xffffffff, 0x81530000, 0xffff0000 },
- W840_FLAGS, 128, CanHaveMII | HasBrokenTx | FDXOnNoMII},
+ 128, CanHaveMII | HasBrokenTx | FDXOnNoMII},
{"Winbond W89c840", { 0x08401050, 0xffffffff, },
- W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+ 128, CanHaveMII | HasBrokenTx},
{"Compex RL100-ATX", { 0x201111F6, 0xffffffff,},
- W840_FLAGS, 128, CanHaveMII | HasBrokenTx},
+ 128, CanHaveMII | HasBrokenTx},
{NULL,}, /* 0 terminated list. */
};
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index e49e8b520c28..e24d2dafcf6c 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -2568,9 +2568,10 @@ typhoon_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
pci_set_drvdata(pdev, dev);
- printk(KERN_INFO "%s: %s at %s 0x%lx, ",
+ printk(KERN_INFO "%s: %s at %s 0x%llx, ",
dev->name, typhoon_card_info[card_id].name,
- use_mmio ? "MMIO" : "IO", pci_resource_start(pdev, use_mmio));
+ use_mmio ? "MMIO" : "IO",
+ (unsigned long long)pci_resource_start(pdev, use_mmio));
for(i = 0; i < 5; i++)
printk("%2.2x:", dev->dev_addr[i]);
printk("%2.2x\n", dev->dev_addr[i]);
diff --git a/drivers/net/wan/c101.c b/drivers/net/wan/c101.c
index b60ef02db7b0..c92ac9fde083 100644
--- a/drivers/net/wan/c101.c
+++ b/drivers/net/wan/c101.c
@@ -7,7 +7,7 @@
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
- * For information see http://hq.pm.waw.pl/hdlc/
+ * For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
*
* Sources of information:
* Hitachi HD64570 SCA User's Manual
diff --git a/drivers/net/wan/dscc4.c b/drivers/net/wan/dscc4.c
index 4505540e3c59..04a376ec0ed8 100644
--- a/drivers/net/wan/dscc4.c
+++ b/drivers/net/wan/dscc4.c
@@ -732,15 +732,15 @@ static int __devinit dscc4_init_one(struct pci_dev *pdev,
ioaddr = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (!ioaddr) {
- printk(KERN_ERR "%s: cannot remap MMIO region %lx @ %lx\n",
- DRV_NAME, pci_resource_len(pdev, 0),
- pci_resource_start(pdev, 0));
+ printk(KERN_ERR "%s: cannot remap MMIO region %llx @ %llx\n",
+ DRV_NAME, (unsigned long long)pci_resource_len(pdev, 0),
+ (unsigned long long)pci_resource_start(pdev, 0));
rc = -EIO;
goto err_free_mmio_regions_2;
}
- printk(KERN_DEBUG "Siemens DSCC4, MMIO at %#lx (regs), %#lx (lbi), IRQ %d\n",
- pci_resource_start(pdev, 0),
- pci_resource_start(pdev, 1), pdev->irq);
+ printk(KERN_DEBUG "Siemens DSCC4, MMIO at %#llx (regs), %#llx (lbi), IRQ %d\n",
+ (unsigned long long)pci_resource_start(pdev, 0),
+ (unsigned long long)pci_resource_start(pdev, 1), pdev->irq);
/* Cf errata DS5 p.2 */
pci_write_config_byte(pdev, PCI_LATENCY_TIMER, 0xf8);
diff --git a/drivers/net/wan/n2.c b/drivers/net/wan/n2.c
index b7d88db89a5c..e013b817cab8 100644
--- a/drivers/net/wan/n2.c
+++ b/drivers/net/wan/n2.c
@@ -7,7 +7,7 @@
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
- * For information see http://hq.pm.waw.pl/hdlc/
+ * For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
*
* Note: integrated CSU/DSU/DDS are not supported by this driver
*
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index a3e65d1bc19b..d7897ae89f90 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -3445,9 +3445,9 @@ cpc_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
card = (pc300_t *) kmalloc(sizeof(pc300_t), GFP_KERNEL);
if (card == NULL) {
- printk("PC300 found at RAM 0x%08lx, "
+ printk("PC300 found at RAM 0x%016llx, "
"but could not allocate card structure.\n",
- pci_resource_start(pdev, 3));
+ (unsigned long long)pci_resource_start(pdev, 3));
err = -ENOMEM;
goto err_disable_dev;
}
diff --git a/drivers/net/wan/pci200syn.c b/drivers/net/wan/pci200syn.c
index 670e8bd2245c..24c3c57c13c9 100644
--- a/drivers/net/wan/pci200syn.c
+++ b/drivers/net/wan/pci200syn.c
@@ -7,7 +7,7 @@
* under the terms of version 2 of the GNU General Public License
* as published by the Free Software Foundation.
*
- * For information see http://hq.pm.waw.pl/hdlc/
+ * For information see <http://www.kernel.org/pub/linux/utils/net/hdlc/>
*
* Sources of information:
* Hitachi HD64572 SCA-II User's Manual
diff --git a/drivers/net/wireless/bcm43xx/Kconfig b/drivers/net/wireless/bcm43xx/Kconfig
index 25ea4748f0b9..533993f538fc 100644
--- a/drivers/net/wireless/bcm43xx/Kconfig
+++ b/drivers/net/wireless/bcm43xx/Kconfig
@@ -2,6 +2,7 @@ config BCM43XX
tristate "Broadcom BCM43xx wireless support"
depends on PCI && IEEE80211 && IEEE80211_SOFTMAC && NET_RADIO && EXPERIMENTAL
select FW_LOADER
+ select HW_RANDOM
---help---
This is an experimental driver for the Broadcom 43xx wireless chip,
found in the Apple Airport Extreme and various other devices.
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index d8f917c21ea4..17a56828e232 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -1,6 +1,7 @@
#ifndef BCM43xx_H_
#define BCM43xx_H_
+#include <linux/hw_random.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/spinlock.h>
@@ -82,6 +83,7 @@
#define BCM43xx_MMIO_TSF_1 0x634 /* core rev < 3 only */
#define BCM43xx_MMIO_TSF_2 0x636 /* core rev < 3 only */
#define BCM43xx_MMIO_TSF_3 0x638 /* core rev < 3 only */
+#define BCM43xx_MMIO_RNG 0x65A
#define BCM43xx_MMIO_POWERUP_DELAY 0x6A8
/* SPROM offsets. */
@@ -750,6 +752,10 @@ struct bcm43xx_private {
const struct firmware *initvals0;
const struct firmware *initvals1;
+ /* Random Number Generator. */
+ struct hwrng rng;
+ char rng_name[20 + 1];
+
/* Debugging stuff follows. */
#ifdef CONFIG_BCM43XX_DEBUG
struct bcm43xx_dfsentry *dfsentry;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index 085d7857fe31..27bcf47228e2 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -3237,6 +3237,39 @@ static void bcm43xx_security_init(struct bcm43xx_private *bcm)
bcm43xx_clear_keys(bcm);
}
+static int bcm43xx_rng_read(struct hwrng *rng, u32 *data)
+{
+ struct bcm43xx_private *bcm = (struct bcm43xx_private *)rng->priv;
+ unsigned long flags;
+
+ bcm43xx_lock_irqonly(bcm, flags);
+ *data = bcm43xx_read16(bcm, BCM43xx_MMIO_RNG);
+ bcm43xx_unlock_irqonly(bcm, flags);
+
+ return (sizeof(u16));
+}
+
+static void bcm43xx_rng_exit(struct bcm43xx_private *bcm)
+{
+ hwrng_unregister(&bcm->rng);
+}
+
+static int bcm43xx_rng_init(struct bcm43xx_private *bcm)
+{
+ int err;
+
+ snprintf(bcm->rng_name, ARRAY_SIZE(bcm->rng_name),
+ "%s_%s", KBUILD_MODNAME, bcm->net_dev->name);
+ bcm->rng.name = bcm->rng_name;
+ bcm->rng.data_read = bcm43xx_rng_read;
+ bcm->rng.priv = (unsigned long)bcm;
+ err = hwrng_register(&bcm->rng);
+ if (err)
+ printk(KERN_ERR PFX "RNG init failed (%d)\n", err);
+
+ return err;
+}
+
/* This is the opposite of bcm43xx_init_board() */
static void bcm43xx_free_board(struct bcm43xx_private *bcm)
{
@@ -3248,6 +3281,7 @@ static void bcm43xx_free_board(struct bcm43xx_private *bcm)
bcm43xx_set_status(bcm, BCM43xx_STAT_SHUTTINGDOWN);
+ bcm43xx_rng_exit(bcm);
for (i = 0; i < BCM43xx_MAX_80211_CORES; i++) {
if (!bcm->core_80211[i].available)
continue;
@@ -3325,6 +3359,9 @@ static int bcm43xx_init_board(struct bcm43xx_private *bcm)
bcm43xx_switch_core(bcm, &bcm->core_80211[0]);
bcm43xx_mac_enable(bcm);
}
+ err = bcm43xx_rng_init(bcm);
+ if (err)
+ goto err_80211_unwind;
bcm43xx_macfilter_clear(bcm, BCM43xx_MACFILTER_ASSOC);
bcm43xx_macfilter_set(bcm, BCM43xx_MACFILTER_SELF, (u8 *)(bcm->net_dev->dev_addr));
dprintk(KERN_INFO PFX "80211 cores initialized\n");
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index 72335c8eb97f..94aeb23a7729 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -1485,7 +1485,7 @@ static int ipw2100_hw_stop_adapter(struct ipw2100_priv *priv)
*
* Sending the PREPARE_FOR_POWER_DOWN will restrict the
* hardware from going into standby mode and will transition
- * out of D0-standy if it is already in that state.
+ * out of D0-standby if it is already in that state.
*
* STATUS_PREPARE_POWER_DOWN_COMPLETE will be sent by the
* driver upon completion. Once received, the driver can
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 081a8999666e..a8a8f975432f 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -1229,12 +1229,6 @@ static struct ipw_fw_error *ipw_alloc_error_log(struct ipw_priv *priv)
return error;
}
-static void ipw_free_error_log(struct ipw_fw_error *error)
-{
- if (error)
- kfree(error);
-}
-
static ssize_t show_event_log(struct device *d,
struct device_attribute *attr, char *buf)
{
@@ -1296,10 +1290,9 @@ static ssize_t clear_error(struct device *d,
const char *buf, size_t count)
{
struct ipw_priv *priv = dev_get_drvdata(d);
- if (priv->error) {
- ipw_free_error_log(priv->error);
- priv->error = NULL;
- }
+
+ kfree(priv->error);
+ priv->error = NULL;
return count;
}
@@ -1970,8 +1963,7 @@ static void ipw_irq_tasklet(struct ipw_priv *priv)
struct ipw_fw_error *error =
ipw_alloc_error_log(priv);
ipw_dump_error_log(priv, error);
- if (error)
- ipw_free_error_log(error);
+ kfree(error);
}
#endif
} else {
@@ -11693,10 +11685,8 @@ static void ipw_pci_remove(struct pci_dev *pdev)
}
}
- if (priv->error) {
- ipw_free_error_log(priv->error);
- priv->error = NULL;
- }
+ kfree(priv->error);
+ priv->error = NULL;
#ifdef CONFIG_IPW2200_PROMISCUOUS
ipw_prom_free(priv);
diff --git a/drivers/net/yellowfin.c b/drivers/net/yellowfin.c
index ecec8e5db786..569305f57561 100644
--- a/drivers/net/yellowfin.c
+++ b/drivers/net/yellowfin.c
@@ -234,14 +234,6 @@ See Packet Engines confidential appendix (prototype chips only).
-enum pci_id_flags_bits {
- /* Set PCI command register bits before calling probe1(). */
- PCI_USES_IO=1, PCI_USES_MEM=2, PCI_USES_MASTER=4,
- /* Read and map the single following PCI BAR. */
- PCI_ADDR0=0<<4, PCI_ADDR1=1<<4, PCI_ADDR2=2<<4, PCI_ADDR3=3<<4,
- PCI_ADDR_64BITS=0x100, PCI_NO_ACPI_WAKE=0x200, PCI_NO_MIN_LATENCY=0x400,
- PCI_UNUSED_IRQ=0x800,
-};
enum capability_flags {
HasMII=1, FullTxStatus=2, IsGigabit=4, HasMulticastBug=8, FullRxStatus=16,
HasMACAddrBug=32, /* Only on early revs. */
@@ -249,11 +241,6 @@ enum capability_flags {
};
/* The PCI I/O space extent. */
#define YELLOWFIN_SIZE 0x100
-#ifdef USE_IO_OPS
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_IO | PCI_ADDR0)
-#else
-#define PCI_IOTYPE (PCI_USES_MASTER | PCI_USES_MEM | PCI_ADDR1)
-#endif
struct pci_id_info {
const char *name;
@@ -261,24 +248,23 @@ struct pci_id_info {
int pci, pci_mask, subsystem, subsystem_mask;
int revision, revision_mask; /* Only 8 bits. */
} id;
- enum pci_id_flags_bits pci_flags;
int io_size; /* Needed for I/O region check or ioremap(). */
int drv_flags; /* Driver use, intended as capability flags. */
};
static const struct pci_id_info pci_id_tbl[] = {
{"Yellowfin G-NIC Gigabit Ethernet", { 0x07021000, 0xffffffff},
- PCI_IOTYPE, YELLOWFIN_SIZE,
+ YELLOWFIN_SIZE,
FullTxStatus | IsGigabit | HasMulticastBug | HasMACAddrBug | DontUseEeprom},
{"Symbios SYM83C885", { 0x07011000, 0xffffffff},
- PCI_IOTYPE, YELLOWFIN_SIZE, HasMII | DontUseEeprom },
- {NULL,},
+ YELLOWFIN_SIZE, HasMII | DontUseEeprom },
+ { }
};
-static struct pci_device_id yellowfin_pci_tbl[] = {
+static const struct pci_device_id yellowfin_pci_tbl[] = {
{ 0x1000, 0x0702, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
{ 0x1000, 0x0701, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 1 },
- { 0, }
+ { }
};
MODULE_DEVICE_TABLE (pci, yellowfin_pci_tbl);
diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c
index 6e8ed0c81a6c..ce0a6ebcff15 100644
--- a/drivers/parisc/dino.c
+++ b/drivers/parisc/dino.c
@@ -299,7 +299,7 @@ struct pci_port_ops dino_port_ops = {
static void dino_disable_irq(unsigned int irq)
{
- struct dino_device *dino_dev = irq_desc[irq].handler_data;
+ struct dino_device *dino_dev = irq_desc[irq].chip_data;
int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, DINO_LOCAL_IRQS);
DBG(KERN_WARNING "%s(0x%p, %d)\n", __FUNCTION__, dino_dev, irq);
@@ -311,7 +311,7 @@ static void dino_disable_irq(unsigned int irq)
static void dino_enable_irq(unsigned int irq)
{
- struct dino_device *dino_dev = irq_desc[irq].handler_data;
+ struct dino_device *dino_dev = irq_desc[irq].chip_data;
int local_irq = gsc_find_local_irq(irq, dino_dev->global_irq, DINO_LOCAL_IRQS);
u32 tmp;
diff --git a/drivers/parisc/eisa.c b/drivers/parisc/eisa.c
index 9d3bd15bf53b..58f0ce8d78e0 100644
--- a/drivers/parisc/eisa.c
+++ b/drivers/parisc/eisa.c
@@ -350,7 +350,7 @@ static int __devinit eisa_probe(struct parisc_device *dev)
irq_desc[2].action = &irq2_action;
for (i = 0; i < 16; i++) {
- irq_desc[i].handler = &eisa_interrupt_type;
+ irq_desc[i].chip = &eisa_interrupt_type;
}
EISA_bus = 1;
diff --git a/drivers/parisc/gsc.c b/drivers/parisc/gsc.c
index 16d40f95978d..5476ba7709b3 100644
--- a/drivers/parisc/gsc.c
+++ b/drivers/parisc/gsc.c
@@ -109,7 +109,7 @@ int gsc_find_local_irq(unsigned int irq, int *global_irqs, int limit)
static void gsc_asic_disable_irq(unsigned int irq)
{
- struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+ struct gsc_asic *irq_dev = irq_desc[irq].chip_data;
int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
u32 imr;
@@ -124,7 +124,7 @@ static void gsc_asic_disable_irq(unsigned int irq)
static void gsc_asic_enable_irq(unsigned int irq)
{
- struct gsc_asic *irq_dev = irq_desc[irq].handler_data;
+ struct gsc_asic *irq_dev = irq_desc[irq].chip_data;
int local_irq = gsc_find_local_irq(irq, irq_dev->global_irq, 32);
u32 imr;
@@ -164,8 +164,8 @@ int gsc_assign_irq(struct hw_interrupt_type *type, void *data)
if (irq > GSC_IRQ_MAX)
return NO_IRQ;
- irq_desc[irq].handler = type;
- irq_desc[irq].handler_data = data;
+ irq_desc[irq].chip = type;
+ irq_desc[irq].chip_data = data;
return irq++;
}
diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c
index 7a458d5bc751..1fbda77cefc2 100644
--- a/drivers/parisc/iosapic.c
+++ b/drivers/parisc/iosapic.c
@@ -619,7 +619,7 @@ iosapic_set_irt_data( struct vector_info *vi, u32 *dp0, u32 *dp1)
static struct vector_info *iosapic_get_vector(unsigned int irq)
{
- return irq_desc[irq].handler_data;
+ return irq_desc[irq].chip_data;
}
static void iosapic_disable_irq(unsigned int irq)
diff --git a/drivers/parisc/superio.c b/drivers/parisc/superio.c
index 828eb45062de..a988dc7a9abd 100644
--- a/drivers/parisc/superio.c
+++ b/drivers/parisc/superio.c
@@ -360,7 +360,7 @@ int superio_fixup_irq(struct pci_dev *pcidev)
#endif
for (i = 0; i < 16; i++) {
- irq_desc[i].handler = &superio_interrupt_type;
+ irq_desc[i].chip = &superio_interrupt_type;
}
/*
diff --git a/drivers/parport/parport_gsc.c b/drivers/parport/parport_gsc.c
index 1de52d9febf9..7352104f7b30 100644
--- a/drivers/parport/parport_gsc.c
+++ b/drivers/parport/parport_gsc.c
@@ -15,7 +15,7 @@
* Phil Blundell <philb@gnu.org>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
- * David Campbell <campbell@torque.net>
+ * David Campbell
* Andrea Arcangeli
*/
diff --git a/drivers/parport/parport_gsc.h b/drivers/parport/parport_gsc.h
index 662f6c1fee5d..fc9c37c54022 100644
--- a/drivers/parport/parport_gsc.h
+++ b/drivers/parport/parport_gsc.h
@@ -24,7 +24,7 @@
* Phil Blundell <Philip.Blundell@pobox.com>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
- * David Campbell <campbell@torque.net>
+ * David Campbell
* Andrea Arcangeli
*/
diff --git a/drivers/parport/parport_pc.c b/drivers/parport/parport_pc.c
index 48bbf32fd980..7318e4a9e436 100644
--- a/drivers/parport/parport_pc.c
+++ b/drivers/parport/parport_pc.c
@@ -3,7 +3,7 @@
* Authors: Phil Blundell <philb@gnu.org>
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Jose Renau <renau@acm.org>
- * David Campbell <campbell@torque.net>
+ * David Campbell
* Andrea Arcangeli
*
* based on work by Grant Guenther <grant@torque.net> and Phil Blundell.
diff --git a/drivers/parport/parport_sunbpp.c b/drivers/parport/parport_sunbpp.c
index 69a4bbd4cbee..7c43c5392bed 100644
--- a/drivers/parport/parport_sunbpp.c
+++ b/drivers/parport/parport_sunbpp.c
@@ -389,7 +389,7 @@ static struct of_device_id bpp_match[] = {
{},
};
-MODULE_DEVICE_TABLE(of, qec_sbus_match);
+MODULE_DEVICE_TABLE(of, bpp_match);
static struct of_platform_driver bpp_sbus_driver = {
.name = "bpp",
diff --git a/drivers/parport/procfs.c b/drivers/parport/procfs.c
index cbe17184b9c7..8610ae88b92d 100644
--- a/drivers/parport/procfs.c
+++ b/drivers/parport/procfs.c
@@ -1,6 +1,6 @@
/* Sysctl interface for parport devices.
*
- * Authors: David Campbell <campbell@torque.net>
+ * Authors: David Campbell
* Tim Waugh <tim@cyberelk.demon.co.uk>
* Philip Blundell <philb@gnu.org>
* Andrea Arcangeli
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 723092682023..5f7db9d2436e 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -34,11 +34,11 @@
*/
int
pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
- unsigned long size, unsigned long align, unsigned long min,
- unsigned int type_mask,
- void (*alignf)(void *, struct resource *,
- unsigned long, unsigned long),
- void *alignf_data)
+ resource_size_t size, resource_size_t align,
+ resource_size_t min, unsigned int type_mask,
+ void (*alignf)(void *, struct resource *, resource_size_t,
+ resource_size_t),
+ void *alignf_data)
{
int i, ret = -ENOMEM;
diff --git a/drivers/pci/hotplug/cpcihp_zt5550.c b/drivers/pci/hotplug/cpcihp_zt5550.c
index f7cb00da38df..1ec165df8522 100644
--- a/drivers/pci/hotplug/cpcihp_zt5550.c
+++ b/drivers/pci/hotplug/cpcihp_zt5550.c
@@ -95,8 +95,8 @@ static int zt5550_hc_config(struct pci_dev *pdev)
hc_dev = pdev;
dbg("hc_dev = %p", hc_dev);
- dbg("pci resource start %lx", pci_resource_start(hc_dev, 1));
- dbg("pci resource len %lx", pci_resource_len(hc_dev, 1));
+ dbg("pci resource start %llx", (unsigned long long)pci_resource_start(hc_dev, 1));
+ dbg("pci resource len %llx", (unsigned long long)pci_resource_len(hc_dev, 1));
if(!request_mem_region(pci_resource_start(hc_dev, 1),
pci_resource_len(hc_dev, 1), MY_NAME)) {
@@ -108,8 +108,9 @@ static int zt5550_hc_config(struct pci_dev *pdev)
hc_registers =
ioremap(pci_resource_start(hc_dev, 1), pci_resource_len(hc_dev, 1));
if(!hc_registers) {
- err("cannot remap MMIO region %lx @ %lx",
- pci_resource_len(hc_dev, 1), pci_resource_start(hc_dev, 1));
+ err("cannot remap MMIO region %llx @ %llx",
+ (unsigned long long)pci_resource_len(hc_dev, 1),
+ (unsigned long long)pci_resource_start(hc_dev, 1));
ret = -ENODEV;
goto exit_release_region;
}
diff --git a/drivers/pci/hotplug/cpqphp_core.c b/drivers/pci/hotplug/cpqphp_core.c
index 9bc1deb8df52..f8658d63f077 100644
--- a/drivers/pci/hotplug/cpqphp_core.c
+++ b/drivers/pci/hotplug/cpqphp_core.c
@@ -1089,8 +1089,8 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
dbg("pdev = %p\n", pdev);
- dbg("pci resource start %lx\n", pci_resource_start(pdev, 0));
- dbg("pci resource len %lx\n", pci_resource_len(pdev, 0));
+ dbg("pci resource start %llx\n", (unsigned long long)pci_resource_start(pdev, 0));
+ dbg("pci resource len %llx\n", (unsigned long long)pci_resource_len(pdev, 0));
if (!request_mem_region(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0), MY_NAME)) {
@@ -1102,9 +1102,9 @@ static int cpqhpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ctrl->hpc_reg = ioremap(pci_resource_start(pdev, 0),
pci_resource_len(pdev, 0));
if (!ctrl->hpc_reg) {
- err("cannot remap MMIO region %lx @ %lx\n",
- pci_resource_len(pdev, 0),
- pci_resource_start(pdev, 0));
+ err("cannot remap MMIO region %llx @ %llx\n",
+ (unsigned long long)pci_resource_len(pdev, 0),
+ (unsigned long long)pci_resource_start(pdev, 0));
rc = -ENODEV;
goto err_free_mem_region;
}
diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c
index d77138ecb098..11f7858f0064 100644
--- a/drivers/pci/hotplug/pciehp_hpc.c
+++ b/drivers/pci/hotplug/pciehp_hpc.c
@@ -1398,8 +1398,9 @@ int pcie_init(struct controller * ctrl, struct pcie_device *dev)
for ( rc = 0; rc < DEVICE_COUNT_RESOURCE; rc++)
if (pci_resource_len(pdev, rc) > 0)
- dbg("pci resource[%d] start=0x%lx(len=0x%lx)\n", rc,
- pci_resource_start(pdev, rc), pci_resource_len(pdev, rc));
+ dbg("pci resource[%d] start=0x%llx(len=0x%llx)\n", rc,
+ (unsigned long long)pci_resource_start(pdev, rc),
+ (unsigned long long)pci_resource_len(pdev, rc));
info("HPC vendor_id %x device_id %x ss_vid %x ss_did %x\n", pdev->vendor, pdev->device,
pdev->subsystem_vendor, pdev->subsystem_device);
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index f5cfbf2c047c..620e1139e607 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -51,8 +51,10 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
res = bus->resource[index];
if (res && (res->flags & IORESOURCE_MEM) &&
!(res->flags & IORESOURCE_PREFETCH)) {
- out += sprintf(out, "start = %8.8lx, length = %8.8lx\n",
- res->start, (res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, "
+ "length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)(res->end - res->start));
}
}
out += sprintf(out, "Free resources: prefetchable memory\n");
@@ -60,16 +62,20 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
res = bus->resource[index];
if (res && (res->flags & IORESOURCE_MEM) &&
(res->flags & IORESOURCE_PREFETCH)) {
- out += sprintf(out, "start = %8.8lx, length = %8.8lx\n",
- res->start, (res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, "
+ "length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)(res->end - res->start));
}
}
out += sprintf(out, "Free resources: IO\n");
for (index = 0; index < PCI_BUS_NUM_RESOURCES; index++) {
res = bus->resource[index];
if (res && (res->flags & IORESOURCE_IO)) {
- out += sprintf(out, "start = %8.8lx, length = %8.8lx\n",
- res->start, (res->end - res->start));
+ out += sprintf(out, "start = %8.8llx, "
+ "length = %8.8llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)(res->end - res->start));
}
}
out += sprintf(out, "Free resources: bus numbers\n");
diff --git a/drivers/pci/msi-apic.c b/drivers/pci/msi-apic.c
index 0eb5fe9003a2..5ed798b319c7 100644
--- a/drivers/pci/msi-apic.c
+++ b/drivers/pci/msi-apic.c
@@ -4,6 +4,7 @@
#include <linux/pci.h>
#include <linux/irq.h>
+#include <asm/smp.h>
#include "msi.h"
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index 7f8429284fab..76d023d8a33b 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -429,12 +429,12 @@ static void irq_handler_init(int cap_id, int pos, int mask)
spin_lock_irqsave(&irq_desc[pos].lock, flags);
if (cap_id == PCI_CAP_ID_MSIX)
- irq_desc[pos].handler = &msix_irq_type;
+ irq_desc[pos].chip = &msix_irq_type;
else {
if (!mask)
- irq_desc[pos].handler = &msi_irq_wo_maskbit_type;
+ irq_desc[pos].chip = &msi_irq_wo_maskbit_type;
else
- irq_desc[pos].handler = &msi_irq_w_maskbit_type;
+ irq_desc[pos].chip = &msi_irq_w_maskbit_type;
}
spin_unlock_irqrestore(&irq_desc[pos].lock, flags);
}
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index bc405c035ce3..606f9b6f70eb 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -87,7 +87,7 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf)
char * str = buf;
int i;
int max = 7;
- u64 start, end;
+ resource_size_t start, end;
if (pci_dev->subordinate)
max = DEVICE_COUNT_RESOURCE;
@@ -365,7 +365,7 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
struct device, kobj));
struct resource *res = (struct resource *)attr->private;
enum pci_mmap_state mmap_type;
- u64 start, end;
+ resource_size_t start, end;
int i;
for (i = 0; i < PCI_ROM_RESOURCE; i++)
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index d408a3c30426..cf57d7de3765 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -369,7 +369,7 @@ pci_set_power_state(struct pci_dev *dev, pci_power_t state)
/*
* Give firmware a chance to be called, such as ACPI _PRx, _PSx
- * Firmware method after natice method ?
+ * Firmware method after native method ?
*/
if (platform_pci_set_power_state)
platform_pci_set_power_state(dev, state);
@@ -691,10 +691,12 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name)
return 0;
err_out:
- printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%lx@%lx for device %s\n",
+ printk (KERN_WARNING "PCI: Unable to reserve %s region #%d:%llx@%llx "
+ "for device %s\n",
pci_resource_flags(pdev, bar) & IORESOURCE_IO ? "I/O" : "mem",
bar + 1, /* PCI BAR # */
- pci_resource_len(pdev, bar), pci_resource_start(pdev, bar),
+ (unsigned long long)pci_resource_len(pdev, bar),
+ (unsigned long long)pci_resource_start(pdev, bar),
pci_name(pdev));
return -EBUSY;
}
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 29bdeca031a8..9cc842b666eb 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -6,10 +6,10 @@ extern int pci_create_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev);
extern void pci_cleanup_rom(struct pci_dev *dev);
extern int pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
- unsigned long size, unsigned long align,
- unsigned long min, unsigned int type_mask,
+ resource_size_t size, resource_size_t align,
+ resource_size_t min, unsigned int type_mask,
void (*alignf)(void *, struct resource *,
- unsigned long, unsigned long),
+ resource_size_t, resource_size_t),
void *alignf_data);
/* Firmware callbacks */
extern int (*platform_pci_choose_state)(struct pci_dev *dev, pm_message_t state);
diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c
index 54b2ebc9c91a..99cf33379769 100644
--- a/drivers/pci/proc.c
+++ b/drivers/pci/proc.c
@@ -302,12 +302,6 @@ static struct file_operations proc_bus_pci_operations = {
#endif /* HAVE_PCI_MMAP */
};
-#if BITS_PER_LONG == 32
-#define LONG_FORMAT "\t%08lx"
-#else
-#define LONG_FORMAT "\t%16lx"
-#endif
-
/* iterator */
static void *pci_seq_start(struct seq_file *m, loff_t *pos)
{
@@ -356,18 +350,18 @@ static int show_device(struct seq_file *m, void *v)
dev->irq);
/* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */
for (i=0; i<7; i++) {
- u64 start, end;
+ resource_size_t start, end;
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
- seq_printf(m, LONG_FORMAT,
- ((unsigned long)start) |
- (dev->resource[i].flags & PCI_REGION_FLAG_MASK));
+ seq_printf(m, "\t%16llx",
+ (unsigned long long)(start |
+ (dev->resource[i].flags & PCI_REGION_FLAG_MASK)));
}
for (i=0; i<7; i++) {
- u64 start, end;
+ resource_size_t start, end;
pci_resource_to_user(dev, i, &dev->resource[i], &start, &end);
- seq_printf(m, LONG_FORMAT,
+ seq_printf(m, "\t%16llx",
dev->resource[i].start < dev->resource[i].end ?
- (unsigned long)(end - start) + 1 : 0);
+ (unsigned long long)(end - start) + 1 : 0);
}
seq_putc(m, '\t');
if (drv)
diff --git a/drivers/pci/rom.c b/drivers/pci/rom.c
index 598a115cd00e..cbb69cf41311 100644
--- a/drivers/pci/rom.c
+++ b/drivers/pci/rom.c
@@ -80,8 +80,8 @@ void __iomem *pci_map_rom(struct pci_dev *pdev, size_t *size)
} else {
if (res->flags & IORESOURCE_ROM_COPY) {
*size = pci_resource_len(pdev, PCI_ROM_RESOURCE);
- return (void __iomem *)pci_resource_start(pdev,
- PCI_ROM_RESOURCE);
+ return (void __iomem *)(unsigned long)
+ pci_resource_start(pdev, PCI_ROM_RESOURCE);
} else {
/* assign the ROM an address if it doesn't have one */
if (res->parent == NULL &&
@@ -170,11 +170,11 @@ void __iomem *pci_map_rom_copy(struct pci_dev *pdev, size_t *size)
return rom;
res->end = res->start + *size;
- memcpy_fromio((void*)res->start, rom, *size);
+ memcpy_fromio((void*)(unsigned long)res->start, rom, *size);
pci_unmap_rom(pdev, rom);
res->flags |= IORESOURCE_ROM_COPY;
- return (void __iomem *)res->start;
+ return (void __iomem *)(unsigned long)res->start;
}
/**
@@ -227,7 +227,7 @@ void pci_cleanup_rom(struct pci_dev *pdev)
{
struct resource *res = &pdev->resource[PCI_ROM_RESOURCE];
if (res->flags & IORESOURCE_ROM_COPY) {
- kfree((void*)res->start);
+ kfree((void*)(unsigned long)res->start);
res->flags &= ~IORESOURCE_ROM_COPY;
res->start = 0;
res->end = 0;
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 35086e80faa9..47c1071ad84e 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -357,8 +357,10 @@ pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type)
order = __ffs(align) - 20;
if (order > 11) {
printk(KERN_WARNING "PCI: region %s/%d "
- "too large: %lx-%lx\n",
- pci_name(dev), i, r->start, r->end);
+ "too large: %llx-%llx\n",
+ pci_name(dev), i,
+ (unsigned long long)r->start,
+ (unsigned long long)r->end);
r->flags = 0;
continue;
}
diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c
index 577f4b55c46d..ab78e4bbdd83 100644
--- a/drivers/pci/setup-res.c
+++ b/drivers/pci/setup-res.c
@@ -40,8 +40,9 @@ pci_update_resource(struct pci_dev *dev, struct resource *res, int resno)
pcibios_resource_to_bus(dev, &region, res);
- pr_debug(" got res [%lx:%lx] bus [%lx:%lx] flags %lx for "
- "BAR %d of %s\n", res->start, res->end,
+ pr_debug(" got res [%llx:%llx] bus [%lx:%lx] flags %lx for "
+ "BAR %d of %s\n", (unsigned long long)res->start,
+ (unsigned long long)res->end,
region.start, region.end, res->flags, resno, pci_name(dev));
new = region.start | (res->flags & PCI_REGION_FLAG_MASK);
@@ -104,10 +105,12 @@ pci_claim_resource(struct pci_dev *dev, int resource)
err = insert_resource(root, res);
if (err) {
- printk(KERN_ERR "PCI: %s region %d of %s %s [%lx:%lx]\n",
- root ? "Address space collision on" :
- "No parent found for",
- resource, dtype, pci_name(dev), res->start, res->end);
+ printk(KERN_ERR "PCI: %s region %d of %s %s [%llx:%llx]\n",
+ root ? "Address space collision on" :
+ "No parent found for",
+ resource, dtype, pci_name(dev),
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
}
return err;
@@ -118,7 +121,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
{
struct pci_bus *bus = dev->bus;
struct resource *res = dev->resource + resno;
- unsigned long size, min, align;
+ resource_size_t size, min, align;
int ret;
size = res->end - res->start + 1;
@@ -145,9 +148,11 @@ int pci_assign_resource(struct pci_dev *dev, int resno)
}
if (ret) {
- printk(KERN_ERR "PCI: Failed to allocate %s resource #%d:%lx@%lx for %s\n",
- res->flags & IORESOURCE_IO ? "I/O" : "mem",
- resno, size, res->start, pci_name(dev));
+ printk(KERN_ERR "PCI: Failed to allocate %s resource "
+ "#%d:%llx@%llx for %s\n",
+ res->flags & IORESOURCE_IO ? "I/O" : "mem",
+ resno, (unsigned long long)size,
+ (unsigned long long)res->start, pci_name(dev));
} else if (resno < PCI_BRIDGE_RESOURCES) {
pci_update_resource(dev, res, resno);
}
@@ -204,7 +209,7 @@ pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
for (i = 0; i < PCI_NUM_RESOURCES; i++) {
struct resource *r;
struct resource_list *list, *tmp;
- unsigned long r_align;
+ resource_size_t r_align;
r = &dev->resource[i];
r_align = r->end - r->start;
@@ -213,13 +218,14 @@ pdev_sort_resources(struct pci_dev *dev, struct resource_list *head)
continue;
if (!r_align) {
printk(KERN_WARNING "PCI: Ignore bogus resource %d "
- "[%lx:%lx] of %s\n",
- i, r->start, r->end, pci_name(dev));
+ "[%llx:%llx] of %s\n",
+ i, (unsigned long long)r->start,
+ (unsigned long long)r->end, pci_name(dev));
continue;
}
r_align = (i < PCI_BRIDGE_RESOURCES) ? r_align + 1 : r->start;
for (list = head; ; list = list->next) {
- unsigned long align = 0;
+ resource_size_t align = 0;
struct resource_list *ln = list->next;
int idx;
diff --git a/drivers/pcmcia/hd64465_ss.c b/drivers/pcmcia/hd64465_ss.c
index b39435bbfaeb..c662e4f89d46 100644
--- a/drivers/pcmcia/hd64465_ss.c
+++ b/drivers/pcmcia/hd64465_ss.c
@@ -244,8 +244,8 @@ static void hs_map_irq(hs_socket_t *sp, unsigned int irq)
hs_mapped_irq[irq].sock = sp;
/* insert ourselves as the irq controller */
- hs_mapped_irq[irq].old_handler = irq_desc[irq].handler;
- irq_desc[irq].handler = &hd64465_ss_irq_type;
+ hs_mapped_irq[irq].old_handler = irq_desc[irq].chip;
+ irq_desc[irq].chip = &hd64465_ss_irq_type;
}
@@ -260,7 +260,7 @@ static void hs_unmap_irq(hs_socket_t *sp, unsigned int irq)
return;
/* restore the original irq controller */
- irq_desc[irq].handler = hs_mapped_irq[irq].old_handler;
+ irq_desc[irq].chip = hs_mapped_irq[irq].old_handler;
}
/*============================================================*/
diff --git a/drivers/pcmcia/i82365.c b/drivers/pcmcia/i82365.c
index a2f05f485156..ff51a65d9433 100644
--- a/drivers/pcmcia/i82365.c
+++ b/drivers/pcmcia/i82365.c
@@ -1084,9 +1084,10 @@ static int i365_set_mem_map(u_short sock, struct pccard_mem_map *mem)
u_short base, i;
u_char map;
- debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#lx-%#lx, "
+ debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, %#llx-%#llx, "
"%#x)\n", sock, mem->map, mem->flags, mem->speed,
- mem->res->start, mem->res->end, mem->card_start);
+ (unsigned long long)mem->res->start,
+ (unsigned long long)mem->res->end, mem->card_start);
map = mem->map;
if ((map > 4) || (mem->card_start > 0x3ffffff) ||
diff --git a/drivers/pcmcia/m8xx_pcmcia.c b/drivers/pcmcia/m8xx_pcmcia.c
index 0e07d9535116..d0f68ab8f041 100644
--- a/drivers/pcmcia/m8xx_pcmcia.c
+++ b/drivers/pcmcia/m8xx_pcmcia.c
@@ -157,7 +157,7 @@ MODULE_LICENSE("Dual MPL/GPL");
static int pcmcia_schlvl = PCMCIA_SCHLVL;
-static spinlock_t events_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(events_lock);
#define PCMCIA_SOCKET_KEY_5V 1
@@ -644,7 +644,7 @@ static struct platform_device m8xx_device = {
};
static u32 pending_events[PCMCIA_SOCKETS_NO];
-static spinlock_t pending_event_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(pending_event_lock);
static irqreturn_t m8xx_interrupt(int irq, void *dev, struct pt_regs *regs)
{
diff --git a/drivers/pcmcia/pd6729.c b/drivers/pcmcia/pd6729.c
index 247ab837f841..9ee26c1b8635 100644
--- a/drivers/pcmcia/pd6729.c
+++ b/drivers/pcmcia/pd6729.c
@@ -642,7 +642,8 @@ static int __devinit pd6729_pci_probe(struct pci_dev *dev,
goto err_out_free_mem;
printk(KERN_INFO "pd6729: Cirrus PD6729 PCI to PCMCIA Bridge "
- "at 0x%lx on irq %d\n", pci_resource_start(dev, 0), dev->irq);
+ "at 0x%llx on irq %d\n",
+ (unsigned long long)pci_resource_start(dev, 0), dev->irq);
/*
* Since we have no memory BARs some firmware may not
* have had PCI_COMMAND_MEMORY enabled, yet the device needs it.
diff --git a/drivers/pcmcia/rsrc_nonstatic.c b/drivers/pcmcia/rsrc_nonstatic.c
index 0f8b157c9717..c3176b16b7be 100644
--- a/drivers/pcmcia/rsrc_nonstatic.c
+++ b/drivers/pcmcia/rsrc_nonstatic.c
@@ -72,7 +72,7 @@ static DEFINE_MUTEX(rsrc_mutex);
======================================================================*/
static struct resource *
-make_resource(unsigned long b, unsigned long n, int flags, char *name)
+make_resource(resource_size_t b, resource_size_t n, int flags, char *name)
{
struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL);
@@ -86,8 +86,8 @@ make_resource(unsigned long b, unsigned long n, int flags, char *name)
}
static struct resource *
-claim_region(struct pcmcia_socket *s, unsigned long base, unsigned long size,
- int type, char *name)
+claim_region(struct pcmcia_socket *s, resource_size_t base,
+ resource_size_t size, int type, char *name)
{
struct resource *res, *parent;
@@ -519,10 +519,10 @@ struct pcmcia_align_data {
static void
pcmcia_common_align(void *align_data, struct resource *res,
- unsigned long size, unsigned long align)
+ resource_size_t size, resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
- unsigned long start;
+ resource_size_t start;
/*
* Ensure that we have the correct start address
*/
@@ -533,8 +533,8 @@ pcmcia_common_align(void *align_data, struct resource *res,
}
static void
-pcmcia_align(void *align_data, struct resource *res,
- unsigned long size, unsigned long align)
+pcmcia_align(void *align_data, struct resource *res, resource_size_t size,
+ resource_size_t align)
{
struct pcmcia_align_data *data = align_data;
struct resource_map *m;
@@ -808,8 +808,10 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
if (res->flags & IORESOURCE_IO) {
if (res == &ioport_resource)
continue;
- printk(KERN_INFO "pcmcia: parent PCI bridge I/O window: 0x%lx - 0x%lx\n",
- res->start, res->end);
+ printk(KERN_INFO "pcmcia: parent PCI bridge I/O "
+ "window: 0x%llx - 0x%llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
if (!adjust_io(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_IO;
@@ -818,8 +820,10 @@ static int nonstatic_autoadd_resources(struct pcmcia_socket *s)
if (res->flags & IORESOURCE_MEM) {
if (res == &iomem_resource)
continue;
- printk(KERN_INFO "pcmcia: parent PCI bridge Memory window: 0x%lx - 0x%lx\n",
- res->start, res->end);
+ printk(KERN_INFO "pcmcia: parent PCI bridge Memory "
+ "window: 0x%llx - 0x%llx\n",
+ (unsigned long long)res->start,
+ (unsigned long long)res->end);
if (!adjust_memory(s, ADD_MANAGED_RESOURCE, res->start, res->end))
done |= IORESOURCE_MEM;
}
diff --git a/drivers/pcmcia/tcic.c b/drivers/pcmcia/tcic.c
index 73bad1d5cb23..65a60671659f 100644
--- a/drivers/pcmcia/tcic.c
+++ b/drivers/pcmcia/tcic.c
@@ -756,8 +756,9 @@ static int tcic_set_mem_map(struct pcmcia_socket *sock, struct pccard_mem_map *m
u_long base, len, mmap;
debug(1, "SetMemMap(%d, %d, %#2.2x, %d ns, "
- "%#lx-%#lx, %#x)\n", psock, mem->map, mem->flags,
- mem->speed, mem->res->start, mem->res->end, mem->card_start);
+ "%#llx-%#llx, %#x)\n", psock, mem->map, mem->flags,
+ mem->speed, (unsigned long long)mem->res->start,
+ (unsigned long long)mem->res->end, mem->card_start);
if ((mem->map > 3) || (mem->card_start > 0x3ffffff) ||
(mem->res->start > 0xffffff) || (mem->res->end > 0xffffff) ||
(mem->res->start > mem->res->end) || (mem->speed > 1000))
diff --git a/drivers/pnp/interface.c b/drivers/pnp/interface.c
index a2d8ce7fef9c..3163e3d73da1 100644
--- a/drivers/pnp/interface.c
+++ b/drivers/pnp/interface.c
@@ -264,7 +264,7 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, struct device_at
if (pnp_port_flags(dev, i) & IORESOURCE_DISABLED)
pnp_printf(buffer," disabled\n");
else
- pnp_printf(buffer," 0x%lx-0x%lx\n",
+ pnp_printf(buffer," 0x%llx-0x%llx\n",
pnp_port_start(dev, i),
pnp_port_end(dev, i));
}
@@ -275,7 +275,7 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, struct device_at
if (pnp_mem_flags(dev, i) & IORESOURCE_DISABLED)
pnp_printf(buffer," disabled\n");
else
- pnp_printf(buffer," 0x%lx-0x%lx\n",
+ pnp_printf(buffer," 0x%llx-0x%llx\n",
pnp_mem_start(dev, i),
pnp_mem_end(dev, i));
}
@@ -286,7 +286,7 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, struct device_at
if (pnp_irq_flags(dev, i) & IORESOURCE_DISABLED)
pnp_printf(buffer," disabled\n");
else
- pnp_printf(buffer," %ld\n",
+ pnp_printf(buffer," %lld\n",
pnp_irq(dev, i));
}
}
@@ -296,7 +296,7 @@ static ssize_t pnp_show_current_resources(struct device *dmdev, struct device_at
if (pnp_dma_flags(dev, i) & IORESOURCE_DISABLED)
pnp_printf(buffer," disabled\n");
else
- pnp_printf(buffer," %ld\n",
+ pnp_printf(buffer," %lld\n",
pnp_dma(dev, i));
}
}
diff --git a/drivers/pnp/manager.c b/drivers/pnp/manager.c
index 6fff109bdab6..1d7a5b87f4cb 100644
--- a/drivers/pnp/manager.c
+++ b/drivers/pnp/manager.c
@@ -20,7 +20,8 @@ DECLARE_MUTEX(pnp_res_mutex);
static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
{
- unsigned long *start, *end, *flags;
+ resource_size_t *start, *end;
+ unsigned long *flags;
if (!dev || !rule)
return -EINVAL;
@@ -63,7 +64,8 @@ static int pnp_assign_port(struct pnp_dev *dev, struct pnp_port *rule, int idx)
static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
{
- unsigned long *start, *end, *flags;
+ resource_size_t *start, *end;
+ unsigned long *flags;
if (!dev || !rule)
return -EINVAL;
@@ -116,7 +118,8 @@ static int pnp_assign_mem(struct pnp_dev *dev, struct pnp_mem *rule, int idx)
static int pnp_assign_irq(struct pnp_dev * dev, struct pnp_irq *rule, int idx)
{
- unsigned long *start, *end, *flags;
+ resource_size_t *start, *end;
+ unsigned long *flags;
int i;
/* IRQ priority: this table is good for i386 */
@@ -168,7 +171,8 @@ static int pnp_assign_irq(struct pnp_dev * dev, struct pnp_irq *rule, int idx)
static int pnp_assign_dma(struct pnp_dev *dev, struct pnp_dma *rule, int idx)
{
- unsigned long *start, *end, *flags;
+ resource_size_t *start, *end;
+ unsigned long *flags;
int i;
/* DMA priority: this table is good for i386 */
@@ -582,7 +586,8 @@ int pnp_disable_dev(struct pnp_dev *dev)
* @size: size of region
*
*/
-void pnp_resource_change(struct resource *resource, unsigned long start, unsigned long size)
+void pnp_resource_change(struct resource *resource, resource_size_t start,
+ resource_size_t size)
{
if (resource == NULL)
return;
diff --git a/drivers/pnp/resource.c b/drivers/pnp/resource.c
index 6ded527169f4..7bb892f58cc0 100644
--- a/drivers/pnp/resource.c
+++ b/drivers/pnp/resource.c
@@ -241,7 +241,7 @@ int pnp_check_port(struct pnp_dev * dev, int idx)
{
int tmp;
struct pnp_dev *tdev;
- unsigned long *port, *end, *tport, *tend;
+ resource_size_t *port, *end, *tport, *tend;
port = &dev->res.port_resource[idx].start;
end = &dev->res.port_resource[idx].end;
@@ -297,7 +297,7 @@ int pnp_check_mem(struct pnp_dev * dev, int idx)
{
int tmp;
struct pnp_dev *tdev;
- unsigned long *addr, *end, *taddr, *tend;
+ resource_size_t *addr, *end, *taddr, *tend;
addr = &dev->res.mem_resource[idx].start;
end = &dev->res.mem_resource[idx].end;
@@ -358,7 +358,7 @@ int pnp_check_irq(struct pnp_dev * dev, int idx)
{
int tmp;
struct pnp_dev *tdev;
- unsigned long * irq = &dev->res.irq_resource[idx].start;
+ resource_size_t * irq = &dev->res.irq_resource[idx].start;
/* if the resource doesn't exist, don't complain about it */
if (cannot_compare(dev->res.irq_resource[idx].flags))
@@ -423,7 +423,7 @@ int pnp_check_dma(struct pnp_dev * dev, int idx)
#ifndef CONFIG_IA64
int tmp;
struct pnp_dev *tdev;
- unsigned long * dma = &dev->res.dma_resource[idx].start;
+ resource_size_t * dma = &dev->res.dma_resource[idx].start;
/* if the resource doesn't exist, don't complain about it */
if (cannot_compare(dev->res.dma_resource[idx].flags))
diff --git a/drivers/rapidio/rio-access.c b/drivers/rapidio/rio-access.c
index b9fab2ae3a36..8b56bbdd011e 100644
--- a/drivers/rapidio/rio-access.c
+++ b/drivers/rapidio/rio-access.c
@@ -17,8 +17,8 @@
* These interrupt-safe spinlocks protect all accesses to RIO
* configuration space and doorbell access.
*/
-static spinlock_t rio_config_lock = SPIN_LOCK_UNLOCKED;
-static spinlock_t rio_doorbell_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(rio_config_lock);
+static DEFINE_SPINLOCK(rio_doorbell_lock);
/*
* Wrappers for all RIO configuration access functions. They just check
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index bccff400b198..f2fc81a9074d 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -162,6 +162,16 @@ config RTC_DRV_PCF8583
This driver can also be built as a module. If so, the module
will be called rtc-pcf8583.
+config RTC_DRV_RS5C348
+ tristate "Ricoh RS5C348A/B"
+ depends on RTC_CLASS && SPI
+ help
+ If you say yes here you get support for the
+ Ricoh RS5C348A and RS5C348B RTC chips.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-rs5c348.
+
config RTC_DRV_RS5C372
tristate "Ricoh RS5C372A/B"
depends on RTC_CLASS && I2C
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 900d210dd1a2..da5e38774e13 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
+obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 5396beec30d0..1cb61a761cb2 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -94,7 +94,9 @@ exit_kfree:
kfree(rtc);
exit_idr:
+ mutex_lock(&idr_lock);
idr_remove(&rtc_idr, id);
+ mutex_unlock(&idr_lock);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index ecafbad41a24..762521a1419c 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -226,7 +226,7 @@ static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq < 0)
- return -ENOIOCTLCMD;
+ return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
switch (cmd) {
case RTC_AIE_OFF:
pdata->irqen &= ~RTC_AF;
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
new file mode 100644
index 000000000000..0964d1dba925
--- /dev/null
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -0,0 +1,246 @@
+/*
+ * A SPI driver for the Ricoh RS5C348 RTC
+ *
+ * Copyright (C) 2006 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * The board specific init code should provide characteristics of this
+ * device:
+ * Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
+ */
+
+#include <linux/bcd.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/rtc.h>
+#include <linux/workqueue.h>
+#include <linux/spi/spi.h>
+
+#define DRV_VERSION "0.1"
+
+#define RS5C348_REG_SECS 0
+#define RS5C348_REG_MINS 1
+#define RS5C348_REG_HOURS 2
+#define RS5C348_REG_WDAY 3
+#define RS5C348_REG_DAY 4
+#define RS5C348_REG_MONTH 5
+#define RS5C348_REG_YEAR 6
+#define RS5C348_REG_CTL1 14
+#define RS5C348_REG_CTL2 15
+
+#define RS5C348_SECS_MASK 0x7f
+#define RS5C348_MINS_MASK 0x7f
+#define RS5C348_HOURS_MASK 0x3f
+#define RS5C348_WDAY_MASK 0x03
+#define RS5C348_DAY_MASK 0x3f
+#define RS5C348_MONTH_MASK 0x1f
+
+#define RS5C348_BIT_PM 0x20 /* REG_HOURS */
+#define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */
+#define RS5C348_BIT_24H 0x20 /* REG_CTL1 */
+#define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */
+#define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */
+
+#define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */
+#define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */
+#define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */
+#define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */
+
+struct rs5c348_plat_data {
+ struct rtc_device *rtc;
+ int rtc_24h;
+};
+
+static int
+rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ u8 txbuf[5+7], *txp;
+ int ret;
+
+ /* Transfer 5 bytes before writing SEC. This gives 31us for carry. */
+ txp = txbuf;
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */
+ txp = &txbuf[5];
+ txp[RS5C348_REG_SECS] = BIN2BCD(tm->tm_sec);
+ txp[RS5C348_REG_MINS] = BIN2BCD(tm->tm_min);
+ if (pdata->rtc_24h) {
+ txp[RS5C348_REG_HOURS] = BIN2BCD(tm->tm_hour);
+ } else {
+ /* hour 0 is AM12, noon is PM12 */
+ txp[RS5C348_REG_HOURS] = BIN2BCD((tm->tm_hour + 11) % 12 + 1) |
+ (tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0);
+ }
+ txp[RS5C348_REG_WDAY] = BIN2BCD(tm->tm_wday);
+ txp[RS5C348_REG_DAY] = BIN2BCD(tm->tm_mday);
+ txp[RS5C348_REG_MONTH] = BIN2BCD(tm->tm_mon + 1) |
+ (tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0);
+ txp[RS5C348_REG_YEAR] = BIN2BCD(tm->tm_year % 100);
+ /* write in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0);
+ udelay(62); /* Tcsr 62us */
+ return ret;
+}
+
+static int
+rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ u8 txbuf[5], rxbuf[7];
+ int ret;
+
+ /* Transfer 5 byte befores reading SEC. This gives 31us for carry. */
+ txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[1] = 0; /* dummy */
+ txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
+ txbuf[3] = 0; /* dummy */
+ txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */
+
+ /* read in one transfer to avoid data inconsistency */
+ ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
+ rxbuf, sizeof(rxbuf));
+ udelay(62); /* Tcsr 62us */
+ if (ret < 0)
+ return ret;
+
+ tm->tm_sec = BCD2BIN(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK);
+ tm->tm_min = BCD2BIN(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
+ tm->tm_hour = BCD2BIN(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
+ if (!pdata->rtc_24h) {
+ tm->tm_hour %= 12;
+ if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM)
+ tm->tm_hour += 12;
+ }
+ tm->tm_wday = BCD2BIN(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
+ tm->tm_mday = BCD2BIN(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
+ tm->tm_mon =
+ BCD2BIN(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1;
+ /* year is 1900 + tm->tm_year */
+ tm->tm_year = BCD2BIN(rxbuf[RS5C348_REG_YEAR]) +
+ ((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
+
+ if (rtc_valid_tm(tm) < 0) {
+ dev_err(&spi->dev, "retrieved date/time is not valid.\n");
+ rtc_time_to_tm(0, tm);
+ }
+
+ return 0;
+}
+
+static struct rtc_class_ops rs5c348_rtc_ops = {
+ .read_time = rs5c348_rtc_read_time,
+ .set_time = rs5c348_rtc_set_time,
+};
+
+static struct spi_driver rs5c348_driver;
+
+static int __devinit rs5c348_probe(struct spi_device *spi)
+{
+ int ret;
+ struct rtc_device *rtc;
+ struct rs5c348_plat_data *pdata;
+
+ pdata = kzalloc(sizeof(struct rs5c348_plat_data), GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ spi->dev.platform_data = pdata;
+
+ /* Check D7 of SECOND register */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
+ if (ret < 0 || (ret & 0x80)) {
+ dev_err(&spi->dev, "not found.\n");
+ goto kfree_exit;
+ }
+
+ dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
+ dev_info(&spi->dev, "spiclk %u KHz.\n",
+ (spi->max_speed_hz + 500) / 1000);
+
+ /* turn RTC on if it was not on */
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
+ u8 buf[2];
+ if (ret & RS5C348_BIT_VDET)
+ dev_warn(&spi->dev, "voltage-low detected.\n");
+ buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
+ buf[1] = 0;
+ ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
+ if (ret < 0)
+ goto kfree_exit;
+ }
+
+ ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
+ if (ret < 0)
+ goto kfree_exit;
+ if (ret & RS5C348_BIT_24H)
+ pdata->rtc_24h = 1;
+
+ rtc = rtc_device_register(rs5c348_driver.driver.name, &spi->dev,
+ &rs5c348_rtc_ops, THIS_MODULE);
+
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc);
+ goto kfree_exit;
+ }
+
+ pdata->rtc = rtc;
+
+ return 0;
+ kfree_exit:
+ kfree(pdata);
+ return ret;
+}
+
+static int __devexit rs5c348_remove(struct spi_device *spi)
+{
+ struct rs5c348_plat_data *pdata = spi->dev.platform_data;
+ struct rtc_device *rtc = pdata->rtc;
+
+ if (rtc)
+ rtc_device_unregister(rtc);
+ kfree(pdata);
+ return 0;
+}
+
+static struct spi_driver rs5c348_driver = {
+ .driver = {
+ .name = "rs5c348",
+ .bus = &spi_bus_type,
+ .owner = THIS_MODULE,
+ },
+ .probe = rs5c348_probe,
+ .remove = __devexit_p(rs5c348_remove),
+};
+
+static __init int rs5c348_init(void)
+{
+ return spi_register_driver(&rs5c348_driver);
+}
+
+static __exit void rs5c348_exit(void)
+{
+ spi_unregister_driver(&rs5c348_driver);
+}
+
+module_init(rs5c348_init);
+module_exit(rs5c348_exit);
+
+MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
+MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(DRV_VERSION);
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index ab486fbc828d..9cd1cb304bb2 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -45,7 +45,7 @@
static unsigned long rtc_freq = 1024;
static struct rtc_time rtc_alarm;
-static spinlock_t sa1100_rtc_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(sa1100_rtc_lock);
static int rtc_update_alarm(struct rtc_time *alrm)
{
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 33e029207e26..4b9291dd4443 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -93,7 +93,7 @@ static void __iomem *rtc2_base;
static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
-static spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(rtc_lock);
static char rtc_name[] = "RTC";
static unsigned long periodic_frequency;
static unsigned long periodic_count;
diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c
index cfb1fff3787c..bafcd2f20ae2 100644
--- a/drivers/s390/block/dasd.c
+++ b/drivers/s390/block/dasd.c
@@ -95,7 +95,7 @@ dasd_alloc_device(void)
spin_lock_init(&device->mem_lock);
spin_lock_init(&device->request_queue_lock);
atomic_set (&device->tasklet_scheduled, 0);
- tasklet_init(&device->tasklet,
+ tasklet_init(&device->tasklet,
(void (*)(unsigned long)) dasd_tasklet,
(unsigned long) device);
INIT_LIST_HEAD(&device->ccw_queue);
@@ -128,7 +128,7 @@ dasd_state_new_to_known(struct dasd_device *device)
int rc;
/*
- * As long as the device is not in state DASD_STATE_NEW we want to
+ * As long as the device is not in state DASD_STATE_NEW we want to
* keep the reference count > 0.
*/
dasd_get_device(device);
@@ -336,7 +336,7 @@ dasd_decrease_state(struct dasd_device *device)
if (device->state == DASD_STATE_ONLINE &&
device->target <= DASD_STATE_READY)
dasd_state_online_to_ready(device);
-
+
if (device->state == DASD_STATE_READY &&
device->target <= DASD_STATE_BASIC)
dasd_state_ready_to_basic(device);
@@ -348,7 +348,7 @@ dasd_decrease_state(struct dasd_device *device)
if (device->state == DASD_STATE_BASIC &&
device->target <= DASD_STATE_KNOWN)
dasd_state_basic_to_known(device);
-
+
if (device->state == DASD_STATE_KNOWN &&
device->target <= DASD_STATE_NEW)
dasd_state_known_to_new(device);
@@ -994,7 +994,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
((irb->scsw.cstat << 8) | irb->scsw.dstat), cqr);
/* Find out the appropriate era_action. */
- if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
+ if (irb->scsw.fctl & SCSW_FCTL_HALT_FUNC)
era = dasd_era_fatal;
else if (irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END) &&
irb->scsw.cstat == 0 &&
@@ -1004,7 +1004,7 @@ dasd_int_handler(struct ccw_device *cdev, unsigned long intparm,
era = dasd_era_fatal; /* don't recover this request */
else if (irb->esw.esw0.erw.cons)
era = device->discipline->examine_error(cqr, irb);
- else
+ else
era = dasd_era_recover;
DBF_DEV_EVENT(DBF_DEBUG, device, "era_code %d", era);
@@ -1287,7 +1287,7 @@ __dasd_start_head(struct dasd_device * device)
}
/*
- * Remove requests from the ccw queue.
+ * Remove requests from the ccw queue.
*/
static void
dasd_flush_ccw_queue(struct dasd_device * device, int all)
@@ -1450,23 +1450,23 @@ dasd_sleep_on(struct dasd_ccw_req * cqr)
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
-
+
device = cqr->device;
spin_lock_irq(get_ccwdev_lock(device->cdev));
-
+
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED;
list_add_tail(&cqr->list, &device->ccw_queue);
-
+
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
-
+
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
-
+
/* Request status is either done or failed. */
rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
return rc;
@@ -1568,7 +1568,7 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
wait_queue_head_t wait_q;
struct dasd_device *device;
int rc;
-
+
device = cqr->device;
spin_lock_irq(get_ccwdev_lock(device->cdev));
rc = _dasd_term_running_cqr(device);
@@ -1576,20 +1576,20 @@ dasd_sleep_on_immediatly(struct dasd_ccw_req * cqr)
spin_unlock_irq(get_ccwdev_lock(device->cdev));
return rc;
}
-
+
init_waitqueue_head (&wait_q);
cqr->callback = dasd_wakeup_cb;
cqr->callback_data = (void *) &wait_q;
cqr->status = DASD_CQR_QUEUED;
list_add(&cqr->list, &device->ccw_queue);
-
+
/* let the bh start the request to keep them in order */
dasd_schedule_bh(device);
-
+
spin_unlock_irq(get_ccwdev_lock(device->cdev));
wait_event(wait_q, _wait_for_wakeup(cqr));
-
+
/* Request status is either done or failed. */
rc = (cqr->status == DASD_CQR_FAILED) ? -EIO : 0;
return rc;
@@ -1725,7 +1725,7 @@ dasd_flush_request_queue(struct dasd_device * device)
if (!device->request_queue)
return;
-
+
spin_lock_irq(&device->request_queue_lock);
while (!list_empty(&device->request_queue->queue_head)) {
req = elv_next_request(device->request_queue);
@@ -1855,15 +1855,34 @@ dasd_generic_probe (struct ccw_device *cdev,
{
int ret;
+ ret = ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
+ if (ret) {
+ printk(KERN_WARNING
+ "dasd_generic_probe: could not set ccw-device options "
+ "for %s\n", cdev->dev.bus_id);
+ return ret;
+ }
ret = dasd_add_sysfs_files(cdev);
if (ret) {
printk(KERN_WARNING
"dasd_generic_probe: could not add sysfs entries "
"for %s\n", cdev->dev.bus_id);
- } else {
- cdev->handler = &dasd_int_handler;
+ return ret;
}
+ cdev->handler = &dasd_int_handler;
+ /*
+ * Automatically online either all dasd devices (dasd_autodetect)
+ * or all devices specified with dasd= parameters during
+ * initial probe.
+ */
+ if ((dasd_get_feature(cdev, DASD_FEATURE_INITIAL_ONLINE) > 0 ) ||
+ (dasd_autodetect && dasd_busid_known(cdev->dev.bus_id) != 0))
+ ret = ccw_device_set_online(cdev);
+ if (ret)
+ printk(KERN_WARNING
+ "dasd_generic_probe: could not initially online "
+ "ccw-device %s\n", cdev->dev.bus_id);
return ret;
}
@@ -1911,6 +1930,8 @@ dasd_generic_set_online (struct ccw_device *cdev,
struct dasd_device *device;
int rc;
+ /* first online clears initial online feature flag */
+ dasd_set_feature(cdev, DASD_FEATURE_INITIAL_ONLINE, 0);
device = dasd_create_device(cdev);
if (IS_ERR(device))
return PTR_ERR(device);
@@ -2065,31 +2086,6 @@ dasd_generic_notify(struct ccw_device *cdev, int event)
return ret;
}
-/*
- * Automatically online either all dasd devices (dasd_autodetect) or
- * all devices specified with dasd= parameters.
- */
-static int
-__dasd_auto_online(struct device *dev, void *data)
-{
- struct ccw_device *cdev;
-
- cdev = to_ccwdev(dev);
- if (dasd_autodetect || dasd_busid_known(cdev->dev.bus_id) == 0)
- ccw_device_set_online(cdev);
- return 0;
-}
-
-void
-dasd_generic_auto_online (struct ccw_driver *dasd_discipline_driver)
-{
- struct device_driver *drv;
-
- drv = get_driver(&dasd_discipline_driver->driver);
- driver_for_each_device(drv, NULL, NULL, __dasd_auto_online);
- put_driver(drv);
-}
-
static int __init
dasd_init(void)
@@ -2170,23 +2166,4 @@ EXPORT_SYMBOL_GPL(dasd_generic_remove);
EXPORT_SYMBOL_GPL(dasd_generic_notify);
EXPORT_SYMBOL_GPL(dasd_generic_set_online);
EXPORT_SYMBOL_GPL(dasd_generic_set_offline);
-EXPORT_SYMBOL_GPL(dasd_generic_auto_online);
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_3370_erp.c b/drivers/s390/block/dasd_3370_erp.c
index 1d11c2a9525d..1ddab8991d92 100644
--- a/drivers/s390/block/dasd_3370_erp.c
+++ b/drivers/s390/block/dasd_3370_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_3370_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -12,10 +12,10 @@
/*
- * DASD_3370_ERP_EXAMINE
+ * DASD_3370_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -23,7 +23,7 @@
* 'Chapter 7. 3370 Sense Data'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -82,22 +82,3 @@ dasd_3370_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
return dasd_era_recover;
} /* END dasd_3370_erp_examine */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_3990_erp.c b/drivers/s390/block/dasd_3990_erp.c
index 2ed51562319e..669805d4402d 100644
--- a/drivers/s390/block/dasd_3990_erp.c
+++ b/drivers/s390/block/dasd_3990_erp.c
@@ -1,6 +1,6 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_3990_erp.c
- * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Author(s)......: Horst Hummel <Horst.Hummel@de.ibm.com>
* Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 2000, 2001
@@ -25,23 +25,23 @@ struct DCTL_data {
} __attribute__ ((packed));
/*
- *****************************************************************************
+ *****************************************************************************
* SECTION ERP EXAMINATION
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_EXAMINE_24
+ * DASD_3990_ERP_EXAMINE_24
*
* DESCRIPTION
- * Checks only for fatal (unrecoverable) error.
+ * Checks only for fatal (unrecoverable) error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* Each bit configuration leading to an action code 2 (Exit with
* programming error or unusual condition indication)
* are handled as fatal error´s.
- *
+ *
* All other configurations are handled as recoverable errors.
*
* RETURN VALUES
@@ -93,15 +93,15 @@ dasd_3990_erp_examine_24(struct dasd_ccw_req * cqr, char *sense)
} /* END dasd_3990_erp_examine_24 */
/*
- * DASD_3990_ERP_EXAMINE_32
+ * DASD_3990_ERP_EXAMINE_32
*
* DESCRIPTION
- * Checks only for fatal/no/recoverable error.
+ * Checks only for fatal/no/recoverable error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for recoverable others.
*/
@@ -128,10 +128,10 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
} /* end dasd_3990_erp_examine_32 */
/*
- * DASD_3990_ERP_EXAMINE
+ * DASD_3990_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -139,7 +139,7 @@ dasd_3990_erp_examine_32(struct dasd_ccw_req * cqr, char *sense)
* 'Chapter 7. Error Recovery Procedures'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -178,18 +178,18 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
} /* END dasd_3990_erp_examine */
/*
- *****************************************************************************
+ *****************************************************************************
* SECTION ERP HANDLING
- *****************************************************************************
+ *****************************************************************************
*/
/*
- *****************************************************************************
+ *****************************************************************************
* 24 and 32 byte sense ERP functions
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_CLEANUP
+ * DASD_3990_ERP_CLEANUP
*
* DESCRIPTION
* Removes the already build but not necessary ERP request and sets
@@ -197,10 +197,10 @@ dasd_3990_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
*
* PARAMETER
* erp request to be blocked
- * final_status either DASD_CQR_DONE or DASD_CQR_FAILED
+ * final_status either DASD_CQR_DONE or DASD_CQR_FAILED
*
* RETURN VALUES
- * cqr original cqr
+ * cqr original cqr
*/
static struct dasd_ccw_req *
dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
@@ -214,7 +214,7 @@ dasd_3990_erp_cleanup(struct dasd_ccw_req * erp, char final_status)
} /* end dasd_3990_erp_cleanup */
/*
- * DASD_3990_ERP_BLOCK_QUEUE
+ * DASD_3990_ERP_BLOCK_QUEUE
*
* DESCRIPTION
* Block the given device request queue to prevent from further
@@ -237,7 +237,7 @@ dasd_3990_erp_block_queue(struct dasd_ccw_req * erp, int expires)
}
/*
- * DASD_3990_ERP_INT_REQ
+ * DASD_3990_ERP_INT_REQ
*
* DESCRIPTION
* Handles 'Intervention Required' error.
@@ -277,7 +277,7 @@ dasd_3990_erp_int_req(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_int_req */
/*
- * DASD_3990_ERP_ALTERNATE_PATH
+ * DASD_3990_ERP_ALTERNATE_PATH
*
* DESCRIPTION
* Repeat the operation on a different channel path.
@@ -330,15 +330,15 @@ dasd_3990_erp_alternate_path(struct dasd_ccw_req * erp)
* DASD_3990_ERP_DCTL
*
* DESCRIPTION
- * Setup cqr to do the Diagnostic Control (DCTL) command with an
+ * Setup cqr to do the Diagnostic Control (DCTL) command with an
* Inhibit Write subcommand (0x20) and the given modifier.
*
* PARAMETER
* erp pointer to the current (failed) ERP
* modifier subcommand modifier
- *
+ *
* RETURN VALUES
- * dctl_cqr pointer to NEW dctl_cqr
+ * dctl_cqr pointer to NEW dctl_cqr
*
*/
static struct dasd_ccw_req *
@@ -386,7 +386,7 @@ dasd_3990_erp_DCTL(struct dasd_ccw_req * erp, char modifier)
} /* end dasd_3990_erp_DCTL */
/*
- * DASD_3990_ERP_ACTION_1
+ * DASD_3990_ERP_ACTION_1
*
* DESCRIPTION
* Setup ERP to do the ERP action 1 (see Reference manual).
@@ -415,7 +415,7 @@ dasd_3990_erp_action_1(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_action_1 */
/*
- * DASD_3990_ERP_ACTION_4
+ * DASD_3990_ERP_ACTION_4
*
* DESCRIPTION
* Setup ERP to do the ERP action 4 (see Reference manual).
@@ -453,11 +453,11 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
if (sense[25] == 0x1D) { /* state change pending */
- DEV_MESSAGE(KERN_INFO, device,
+ DEV_MESSAGE(KERN_INFO, device,
"waiting for state change pending "
"interrupt, %d retries left",
erp->retries);
-
+
dasd_3990_erp_block_queue(erp, 30*HZ);
} else if (sense[25] == 0x1E) { /* busy */
@@ -469,9 +469,9 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
} else {
/* no state change pending - retry */
- DEV_MESSAGE (KERN_INFO, device,
+ DEV_MESSAGE (KERN_INFO, device,
"redriving request immediately, "
- "%d retries left",
+ "%d retries left",
erp->retries);
erp->status = DASD_CQR_QUEUED;
}
@@ -482,13 +482,13 @@ dasd_3990_erp_action_4(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_action_4 */
/*
- *****************************************************************************
+ *****************************************************************************
* 24 byte sense ERP functions (only)
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERP_ACTION_5
+ * DASD_3990_ERP_ACTION_5
*
* DESCRIPTION
* Setup ERP to do the ERP action 5 (see Reference manual).
@@ -523,7 +523,7 @@ dasd_3990_erp_action_5(struct dasd_ccw_req * erp)
*
* PARAMETER
* sense current sense data
- *
+ *
* RETURN VALUES
* void
*/
@@ -1150,9 +1150,9 @@ dasd_3990_handle_env_data(struct dasd_ccw_req * erp, char *sense)
* PARAMETER
* erp current erp_head
* sense current sense data
- *
+ *
* RETURN VALUES
- * erp 'new' erp_head - pointer to new ERP
+ * erp 'new' erp_head - pointer to new ERP
*/
static struct dasd_ccw_req *
dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
@@ -1185,7 +1185,7 @@ dasd_3990_erp_com_rej(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_com_rej */
/*
- * DASD_3990_ERP_BUS_OUT
+ * DASD_3990_ERP_BUS_OUT
*
* DESCRIPTION
* Handles 24 byte 'Bus Out Parity Check' error.
@@ -1483,7 +1483,7 @@ dasd_3990_erp_env_data(struct dasd_ccw_req * erp, char *sense)
*
* PARAMETER
* erp already added default ERP
- *
+ *
* RETURN VALUES
* erp new erp_head - pointer to new ERP
*/
@@ -1527,11 +1527,11 @@ dasd_3990_erp_file_prot(struct dasd_ccw_req * erp)
} /* end dasd_3990_erp_file_prot */
/*
- * DASD_3990_ERP_INSPECT_24
+ * DASD_3990_ERP_INSPECT_24
*
* DESCRIPTION
* Does a detailed inspection of the 24 byte sense data
- * and sets up a related error recovery action.
+ * and sets up a related error recovery action.
*
* PARAMETER
* sense sense data of the actual error
@@ -1602,13 +1602,13 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense)
} /* END dasd_3990_erp_inspect_24 */
/*
- *****************************************************************************
+ *****************************************************************************
* 32 byte sense ERP functions (only)
- *****************************************************************************
+ *****************************************************************************
*/
/*
- * DASD_3990_ERPACTION_10_32
+ * DASD_3990_ERPACTION_10_32
*
* DESCRIPTION
* Handles 32 byte 'Action 10' of Single Program Action Codes.
@@ -1616,7 +1616,7 @@ dasd_3990_erp_inspect_24(struct dasd_ccw_req * erp, char *sense)
*
* PARAMETER
* erp current erp_head
- * sense current sense data
+ * sense current sense data
* RETURN VALUES
* erp modified erp_head
*/
@@ -1640,18 +1640,18 @@ dasd_3990_erp_action_10_32(struct dasd_ccw_req * erp, char *sense)
*
* DESCRIPTION
* Handles 32 byte 'Action 1B' of Single Program Action Codes.
- * A write operation could not be finished because of an unexpected
+ * A write operation could not be finished because of an unexpected
* condition.
- * The already created 'default erp' is used to get the link to
- * the erp chain, but it can not be used for this recovery
+ * The already created 'default erp' is used to get the link to
+ * the erp chain, but it can not be used for this recovery
* action because it contains no DE/LO data space.
*
* PARAMETER
* default_erp already added default erp.
- * sense current sense data
+ * sense current sense data
*
* RETURN VALUES
- * erp new erp or
+ * erp new erp or
* default_erp in case of imprecise ending or error
*/
static struct dasd_ccw_req *
@@ -1789,16 +1789,16 @@ dasd_3990_erp_action_1B_32(struct dasd_ccw_req * default_erp, char *sense)
* DASD_3990_UPDATE_1B
*
* DESCRIPTION
- * Handles the update to the 32 byte 'Action 1B' of Single Program
+ * Handles the update to the 32 byte 'Action 1B' of Single Program
* Action Codes in case the first action was not successful.
* The already created 'previous_erp' is the currently not successful
- * ERP.
+ * ERP.
*
* PARAMETER
* previous_erp already created previous erp.
- * sense current sense data
+ * sense current sense data
* RETURN VALUES
- * erp modified erp
+ * erp modified erp
*/
static struct dasd_ccw_req *
dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
@@ -1897,7 +1897,7 @@ dasd_3990_update_1B(struct dasd_ccw_req * previous_erp, char *sense)
} /* end dasd_3990_update_1B */
/*
- * DASD_3990_ERP_COMPOUND_RETRY
+ * DASD_3990_ERP_COMPOUND_RETRY
*
* DESCRIPTION
* Handles the compound ERP action retry code.
@@ -1943,7 +1943,7 @@ dasd_3990_erp_compound_retry(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_retry */
/*
- * DASD_3990_ERP_COMPOUND_PATH
+ * DASD_3990_ERP_COMPOUND_PATH
*
* DESCRIPTION
* Handles the compound ERP action for retry on alternate
@@ -1965,7 +1965,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
dasd_3990_erp_alternate_path(erp);
if (erp->status == DASD_CQR_FAILED) {
- /* reset the lpm and the status to be able to
+ /* reset the lpm and the status to be able to
* try further actions. */
erp->lpm = 0;
@@ -1980,7 +1980,7 @@ dasd_3990_erp_compound_path(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_path */
/*
- * DASD_3990_ERP_COMPOUND_CODE
+ * DASD_3990_ERP_COMPOUND_CODE
*
* DESCRIPTION
* Handles the compound ERP action for retry code.
@@ -2001,18 +2001,18 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense)
switch (sense[28]) {
case 0x17:
- /* issue a Diagnostic Control command with an
+ /* issue a Diagnostic Control command with an
* Inhibit Write subcommand and controler modifier */
erp = dasd_3990_erp_DCTL(erp, 0x20);
break;
-
+
case 0x25:
/* wait for 5 seconds and retry again */
erp->retries = 1;
-
+
dasd_3990_erp_block_queue (erp, 5*HZ);
break;
-
+
default:
/* should not happen - continue */
break;
@@ -2026,7 +2026,7 @@ dasd_3990_erp_compound_code(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_code */
/*
- * DASD_3990_ERP_COMPOUND_CONFIG
+ * DASD_3990_ERP_COMPOUND_CONFIG
*
* DESCRIPTION
* Handles the compound ERP action for configruation
@@ -2063,10 +2063,10 @@ dasd_3990_erp_compound_config(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound_config */
/*
- * DASD_3990_ERP_COMPOUND
+ * DASD_3990_ERP_COMPOUND
*
* DESCRIPTION
- * Does the further compound program action if
+ * Does the further compound program action if
* compound retry was not successful.
*
* PARAMETER
@@ -2110,11 +2110,11 @@ dasd_3990_erp_compound(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_compound */
/*
- * DASD_3990_ERP_INSPECT_32
+ * DASD_3990_ERP_INSPECT_32
*
* DESCRIPTION
* Does a detailed inspection of the 32 byte sense data
- * and sets up a related error recovery action.
+ * and sets up a related error recovery action.
*
* PARAMETER
* sense sense data of the actual error
@@ -2228,9 +2228,9 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
} /* end dasd_3990_erp_inspect_32 */
/*
- *****************************************************************************
+ *****************************************************************************
* main ERP control fuctions (24 and 32 byte sense)
- *****************************************************************************
+ *****************************************************************************
*/
/*
@@ -2243,7 +2243,7 @@ dasd_3990_erp_inspect_32(struct dasd_ccw_req * erp, char *sense)
* PARAMETER
* erp pointer to the currently created default ERP
* RETURN VALUES
- * erp_new contens was possibly modified
+ * erp_new contens was possibly modified
*/
static struct dasd_ccw_req *
dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
@@ -2272,14 +2272,14 @@ dasd_3990_erp_inspect(struct dasd_ccw_req * erp)
/*
* DASD_3990_ERP_ADD_ERP
- *
+ *
* DESCRIPTION
* This funtion adds an additional request block (ERP) to the head of
* the given cqr (or erp).
* This erp is initialized as an default erp (retry TIC)
*
* PARAMETER
- * cqr head of the current ERP-chain (or single cqr if
+ * cqr head of the current ERP-chain (or single cqr if
* first error)
* RETURN VALUES
* erp pointer to new ERP-chain head
@@ -2332,15 +2332,15 @@ dasd_3990_erp_add_erp(struct dasd_ccw_req * cqr)
}
/*
- * DASD_3990_ERP_ADDITIONAL_ERP
- *
+ * DASD_3990_ERP_ADDITIONAL_ERP
+ *
* DESCRIPTION
* An additional ERP is needed to handle the current error.
* Add ERP to the head of the ERP-chain containing the ERP processing
* determined based on the sense data.
*
* PARAMETER
- * cqr head of the current ERP-chain (or single cqr if
+ * cqr head of the current ERP-chain (or single cqr if
* first error)
*
* RETURN VALUES
@@ -2376,7 +2376,7 @@ dasd_3990_erp_additional_erp(struct dasd_ccw_req * cqr)
* 24 byte sense byte 25 and 27 is set as well.
*
* PARAMETER
- * cqr1 first cqr, which will be compared with the
+ * cqr1 first cqr, which will be compared with the
* cqr2 second cqr.
*
* RETURN VALUES
@@ -2415,7 +2415,7 @@ dasd_3990_erp_error_match(struct dasd_ccw_req *cqr1, struct dasd_ccw_req *cqr2)
* cqr failed cqr (either original cqr or already an erp)
*
* RETURN VALUES
- * erp erp-pointer to the already defined error
+ * erp erp-pointer to the already defined error
* recovery procedure OR
* NULL if a 'new' error occurred.
*/
@@ -2451,10 +2451,10 @@ dasd_3990_erp_in_erp(struct dasd_ccw_req *cqr)
* DASD_3990_ERP_FURTHER_ERP (24 & 32 byte sense)
*
* DESCRIPTION
- * No retry is left for the current ERP. Check what has to be done
+ * No retry is left for the current ERP. Check what has to be done
* with the ERP.
* - do further defined ERP action or
- * - wait for interrupt or
+ * - wait for interrupt or
* - exit with permanent error
*
* PARAMETER
@@ -2485,7 +2485,7 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
if (!(sense[2] & DASD_SENSE_BIT_0)) {
- /* issue a Diagnostic Control command with an
+ /* issue a Diagnostic Control command with an
* Inhibit Write subcommand */
switch (sense[25]) {
@@ -2535,14 +2535,14 @@ dasd_3990_erp_further_erp(struct dasd_ccw_req *erp)
} /* end dasd_3990_erp_further_erp */
/*
- * DASD_3990_ERP_HANDLE_MATCH_ERP
+ * DASD_3990_ERP_HANDLE_MATCH_ERP
*
* DESCRIPTION
* An error occurred again and an ERP has been detected which is already
- * used to handle this error (e.g. retries).
+ * used to handle this error (e.g. retries).
* All prior ERP's are asumed to be successful and therefore removed
* from queue.
- * If retry counter of matching erp is already 0, it is checked if further
+ * If retry counter of matching erp is already 0, it is checked if further
* action is needed (besides retry) or if the ERP has failed.
*
* PARAMETER
@@ -2631,7 +2631,7 @@ dasd_3990_erp_handle_match_erp(struct dasd_ccw_req *erp_head,
* erp erp-pointer to the head of the ERP action chain.
* This means:
* - either a ptr to an additional ERP cqr or
- * - the original given cqr (which's status might
+ * - the original given cqr (which's status might
* be modified)
*/
struct dasd_ccw_req *
@@ -2723,22 +2723,3 @@ dasd_3990_erp_action(struct dasd_ccw_req * cqr)
return erp;
} /* end dasd_3990_erp_action */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_9336_erp.c b/drivers/s390/block/dasd_9336_erp.c
index dc861446d056..6e082688475a 100644
--- a/drivers/s390/block/dasd_9336_erp.c
+++ b/drivers/s390/block/dasd_9336_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_9336_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -12,10 +12,10 @@
/*
- * DASD_9336_ERP_EXAMINE
+ * DASD_9336_ERP_EXAMINE
*
* DESCRIPTION
- * Checks only for fatal/no/recover error.
+ * Checks only for fatal/no/recover error.
* A detailed examination of the sense data is done later outside
* the interrupt handler.
*
@@ -23,7 +23,7 @@
* 'Chapter 7. 9336 Sense Data'.
*
* RETURN VALUES
- * dasd_era_none no error
+ * dasd_era_none no error
* dasd_era_fatal for all fatal (unrecoverable errors)
* dasd_era_recover for all others.
*/
@@ -39,22 +39,3 @@ dasd_9336_erp_examine(struct dasd_ccw_req * cqr, struct irb * irb)
return dasd_era_recover;
} /* END dasd_9336_erp_examine */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_9343_erp.c b/drivers/s390/block/dasd_9343_erp.c
index 4a5b79569aaa..ddecb9808ed4 100644
--- a/drivers/s390/block/dasd_9343_erp.c
+++ b/drivers/s390/block/dasd_9343_erp.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_9345_erp.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
diff --git a/drivers/s390/block/dasd_devmap.c b/drivers/s390/block/dasd_devmap.c
index 216bc4fba199..9e9ae7179602 100644
--- a/drivers/s390/block/dasd_devmap.c
+++ b/drivers/s390/block/dasd_devmap.c
@@ -27,7 +27,7 @@
#include "dasd_int.h"
kmem_cache_t *dasd_page_cache;
-EXPORT_SYMBOL(dasd_page_cache);
+EXPORT_SYMBOL_GPL(dasd_page_cache);
/*
* dasd_devmap_t is used to store the features and the relation
@@ -49,6 +49,20 @@ struct dasd_devmap {
};
/*
+ * dasd_servermap is used to store the server_id of all storage servers
+ * accessed by DASD device driver.
+ */
+struct dasd_servermap {
+ struct list_head list;
+ struct server_id {
+ char vendor[4];
+ char serial[15];
+ } sid;
+};
+
+static struct list_head dasd_serverlist;
+
+/*
* Parameter parsing functions for dasd= parameter. The syntax is:
* <devno> : (0x)?[0-9a-fA-F]+
* <busid> : [0-0a-f]\.[0-9a-f]\.(0x)?[0-9a-fA-F]+
@@ -64,6 +78,8 @@ struct dasd_devmap {
int dasd_probeonly = 0; /* is true, when probeonly mode is active */
int dasd_autodetect = 0; /* is true, when autodetection is active */
+int dasd_nopav = 0; /* is true, when PAV is disabled */
+EXPORT_SYMBOL_GPL(dasd_nopav);
/*
* char *dasd[] is intended to hold the ranges supplied by the dasd= statement
@@ -123,7 +139,7 @@ static inline int
dasd_busid(char **str, int *id0, int *id1, int *devno)
{
int val, old_style;
-
+
/* check for leading '0x' */
old_style = 0;
if ((*str)[0] == '0' && (*str)[1] == 'x') {
@@ -179,7 +195,7 @@ dasd_feature_list(char *str, char **endp)
features = 0;
while (1) {
- for (len = 0;
+ for (len = 0;
str[len] && str[len] != ':' && str[len] != ')'; len++);
if (len == 2 && !strncmp(str, "ro", 2))
features |= DASD_FEATURE_READONLY;
@@ -228,19 +244,24 @@ dasd_parse_keyword( char *parsestring ) {
length = strlen(parsestring);
residual_str = parsestring + length;
}
- if (strncmp ("autodetect", parsestring, length) == 0) {
+ if (strncmp("autodetect", parsestring, length) == 0) {
dasd_autodetect = 1;
MESSAGE (KERN_INFO, "%s",
"turning to autodetection mode");
return residual_str;
}
- if (strncmp ("probeonly", parsestring, length) == 0) {
+ if (strncmp("probeonly", parsestring, length) == 0) {
dasd_probeonly = 1;
MESSAGE(KERN_INFO, "%s",
"turning to probeonly mode");
return residual_str;
}
- if (strncmp ("fixedbuffers", parsestring, length) == 0) {
+ if (strncmp("nopav", parsestring, length) == 0) {
+ dasd_nopav = 1;
+ MESSAGE(KERN_INFO, "%s", "disable PAV mode");
+ return residual_str;
+ }
+ if (strncmp("fixedbuffers", parsestring, length) == 0) {
if (dasd_page_cache)
return residual_str;
dasd_page_cache =
@@ -294,6 +315,8 @@ dasd_parse_range( char *parsestring ) {
features = dasd_feature_list(str, &str);
if (features < 0)
return ERR_PTR(-EINVAL);
+ /* each device in dasd= parameter should be set initially online */
+ features |= DASD_FEATURE_INITIAL_ONLINE;
while (from <= to) {
sprintf(bus_id, "%01x.%01x.%04x",
from_id0, from_id1, from++);
@@ -359,7 +382,7 @@ dasd_parse(void)
* Add a devmap for the device specified by busid. It is possible that
* the devmap already exists (dasd= parameter). The order of the devices
* added through this function will define the kdevs for the individual
- * devices.
+ * devices.
*/
static struct dasd_devmap *
dasd_add_busid(char *bus_id, int features)
@@ -368,7 +391,7 @@ dasd_add_busid(char *bus_id, int features)
int hash;
new = (struct dasd_devmap *)
- kmalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
+ kzalloc(sizeof(struct dasd_devmap), GFP_KERNEL);
if (!new)
return ERR_PTR(-ENOMEM);
spin_lock(&dasd_devmap_lock);
@@ -630,7 +653,8 @@ dasd_ro_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-dasd_ro_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+dasd_ro_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct dasd_devmap *devmap;
int ro_flag;
@@ -658,7 +682,7 @@ static DEVICE_ATTR(readonly, 0644, dasd_ro_show, dasd_ro_store);
* use_diag controls whether the driver should use diag rather than ssch
* to talk to the device
*/
-static ssize_t
+static ssize_t
dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
{
struct dasd_devmap *devmap;
@@ -673,7 +697,8 @@ dasd_use_diag_show(struct device *dev, struct device_attribute *attr, char *buf)
}
static ssize_t
-dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+dasd_use_diag_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
{
struct dasd_devmap *devmap;
ssize_t rc;
@@ -697,11 +722,11 @@ dasd_use_diag_store(struct device *dev, struct device_attribute *attr, const cha
return rc;
}
-static
-DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
+static DEVICE_ATTR(use_diag, 0644, dasd_use_diag_show, dasd_use_diag_store);
static ssize_t
-dasd_discipline_show(struct device *dev, struct device_attribute *attr, char *buf)
+dasd_discipline_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
{
struct dasd_devmap *devmap;
char *dname;
@@ -834,6 +859,38 @@ static struct attribute_group dasd_attr_group = {
.attrs = dasd_attrs,
};
+/*
+ * Check if the related storage server is already contained in the
+ * dasd_serverlist. If server is not contained, create new entry.
+ * Return 0 if server was already in serverlist,
+ * 1 if the server was added successfully
+ * <0 in case of error.
+ */
+static int
+dasd_add_server(struct dasd_uid *uid)
+{
+ struct dasd_servermap *new, *tmp;
+
+ /* check if server is already contained */
+ list_for_each_entry(tmp, &dasd_serverlist, list)
+ // normale cmp?
+ if (strncmp(tmp->sid.vendor, uid->vendor,
+ sizeof(tmp->sid.vendor)) == 0
+ && strncmp(tmp->sid.serial, uid->serial,
+ sizeof(tmp->sid.serial)) == 0)
+ return 0;
+
+ new = (struct dasd_servermap *)
+ kzalloc(sizeof(struct dasd_servermap), GFP_KERNEL);
+ if (!new)
+ return -ENOMEM;
+
+ strncpy(new->sid.vendor, uid->vendor, sizeof(new->sid.vendor));
+ strncpy(new->sid.serial, uid->serial, sizeof(new->sid.serial));
+ list_add(&new->list, &dasd_serverlist);
+ return 1;
+}
+
/*
* Return copy of the device unique identifier.
@@ -854,21 +911,26 @@ dasd_get_uid(struct ccw_device *cdev, struct dasd_uid *uid)
/*
* Register the given device unique identifier into devmap struct.
+ * Return 0 if server was already in serverlist,
+ * 1 if the server was added successful
+ * <0 in case of error.
*/
int
dasd_set_uid(struct ccw_device *cdev, struct dasd_uid *uid)
{
struct dasd_devmap *devmap;
+ int rc;
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
return PTR_ERR(devmap);
spin_lock(&dasd_devmap_lock);
devmap->uid = *uid;
+ rc = dasd_add_server(uid);
spin_unlock(&dasd_devmap_lock);
- return 0;
+ return rc;
}
-EXPORT_SYMBOL(dasd_set_uid);
+EXPORT_SYMBOL_GPL(dasd_set_uid);
/*
* Return value of the specified feature.
@@ -880,7 +942,7 @@ dasd_get_feature(struct ccw_device *cdev, int feature)
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
- return (int) PTR_ERR(devmap);
+ return PTR_ERR(devmap);
return ((devmap->features & feature) != 0);
}
@@ -896,7 +958,7 @@ dasd_set_feature(struct ccw_device *cdev, int feature, int flag)
devmap = dasd_find_busid(cdev->dev.bus_id);
if (IS_ERR(devmap))
- return (int) PTR_ERR(devmap);
+ return PTR_ERR(devmap);
spin_lock(&dasd_devmap_lock);
if (flag)
@@ -932,8 +994,10 @@ dasd_devmap_init(void)
dasd_max_devindex = 0;
for (i = 0; i < 256; i++)
INIT_LIST_HEAD(&dasd_hashlists[i]);
- return 0;
+ /* Initialize servermap structure. */
+ INIT_LIST_HEAD(&dasd_serverlist);
+ return 0;
}
void
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 3f9d704d2657..4002f6c1c1b3 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_diag.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Based on.......: linux/drivers/s390/block/mdisk.c
@@ -336,7 +336,7 @@ dasd_diag_check_device(struct dasd_device *device)
private = (struct dasd_diag_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
+ private = kzalloc(sizeof(struct dasd_diag_private),GFP_KERNEL);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"memory allocation failed for private data");
@@ -527,7 +527,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
datasize, device);
if (IS_ERR(cqr))
return cqr;
-
+
dreq = (struct dasd_diag_req *) cqr->data;
dreq->block_count = count;
dbio = dreq->bio;
diff --git a/drivers/s390/block/dasd_diag.h b/drivers/s390/block/dasd_diag.h
index 38a4e55f8953..b8c78267ff3e 100644
--- a/drivers/s390/block/dasd_diag.h
+++ b/drivers/s390/block/dasd_diag.h
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_diag.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Based on.......: linux/drivers/s390/block/mdisk.h
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index 7d5a6cee4bd8..0dfab30e8089 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_eckd.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Carsten Otte <Cotte@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -24,6 +24,7 @@
#include <asm/io.h>
#include <asm/todclk.h>
#include <asm/uaccess.h>
+#include <asm/cio.h>
#include <asm/ccwdev.h>
#include "dasd_int.h"
@@ -89,17 +90,22 @@ dasd_eckd_probe (struct ccw_device *cdev)
{
int ret;
- ret = dasd_generic_probe (cdev, &dasd_eckd_discipline);
- if (ret)
+ /* set ECKD specific ccw-device options */
+ ret = ccw_device_set_options(cdev, CCWDEV_ALLOW_FORCE);
+ if (ret) {
+ printk(KERN_WARNING
+ "dasd_eckd_probe: could not set ccw-device options "
+ "for %s\n", cdev->dev.bus_id);
return ret;
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP | CCWDEV_ALLOW_FORCE);
- return 0;
+ }
+ ret = dasd_generic_probe(cdev, &dasd_eckd_discipline);
+ return ret;
}
static int
dasd_eckd_set_online(struct ccw_device *cdev)
{
- return dasd_generic_set_online (cdev, &dasd_eckd_discipline);
+ return dasd_generic_set_online(cdev, &dasd_eckd_discipline);
}
static struct ccw_driver dasd_eckd_driver = {
@@ -210,14 +216,14 @@ check_XRC (struct ccw1 *de_ccw,
/* switch on System Time Stamp - needed for XRC Support */
if (private->rdc_data.facilities.XRC_supported) {
-
+
data->ga_extended |= 0x08; /* switch on 'Time Stamp Valid' */
data->ga_extended |= 0x02; /* switch on 'Extended Parameter' */
-
+
data->ep_sys_time = get_clock ();
-
+
de_ccw->count = sizeof (struct DE_eckd_data);
- de_ccw->flags |= CCW_FLAG_SLI;
+ de_ccw->flags |= CCW_FLAG_SLI;
}
return;
@@ -296,8 +302,8 @@ define_extent(struct ccw1 * ccw, struct DE_eckd_data * data, int trk,
/* check for sequential prestage - enhance cylinder range */
if (data->attributes.operation == DASD_SEQ_PRESTAGE ||
data->attributes.operation == DASD_SEQ_ACCESS) {
-
- if (end.cyl + private->attrib.nr_cyl < geo.cyl)
+
+ if (end.cyl + private->attrib.nr_cyl < geo.cyl)
end.cyl += private->attrib.nr_cyl;
else
end.cyl = (geo.cyl - 1);
@@ -317,7 +323,7 @@ locate_record(struct ccw1 *ccw, struct LO_eckd_data *data, int trk,
struct dasd_eckd_private *private;
int sector;
int dn, d;
-
+
private = (struct dasd_eckd_private *) device->private;
DBF_DEV_EVENT(DBF_INFO, device,
@@ -541,6 +547,86 @@ dasd_eckd_read_conf(struct dasd_device *device)
}
/*
+ * Build CP for Perform Subsystem Function - SSC.
+ */
+struct dasd_ccw_req *
+dasd_eckd_build_psf_ssc(struct dasd_device *device)
+{
+ struct dasd_ccw_req *cqr;
+ struct dasd_psf_ssc_data *psf_ssc_data;
+ struct ccw1 *ccw;
+
+ cqr = dasd_smalloc_request("ECKD", 1 /* PSF */ ,
+ sizeof(struct dasd_psf_ssc_data),
+ device);
+
+ if (IS_ERR(cqr)) {
+ DEV_MESSAGE(KERN_WARNING, device, "%s",
+ "Could not allocate PSF-SSC request");
+ return cqr;
+ }
+ psf_ssc_data = (struct dasd_psf_ssc_data *)cqr->data;
+ psf_ssc_data->order = PSF_ORDER_SSC;
+ psf_ssc_data->suborder = 0x08;
+
+ ccw = cqr->cpaddr;
+ ccw->cmd_code = DASD_ECKD_CCW_PSF;
+ ccw->cda = (__u32)(addr_t)psf_ssc_data;
+ ccw->count = 66;
+
+ cqr->device = device;
+ cqr->expires = 10*HZ;
+ cqr->buildclk = get_clock();
+ cqr->status = DASD_CQR_FILLED;
+ return cqr;
+}
+
+/*
+ * Perform Subsystem Function.
+ * It is necessary to trigger CIO for channel revalidation since this
+ * call might change behaviour of DASD devices.
+ */
+static int
+dasd_eckd_psf_ssc(struct dasd_device *device)
+{
+ struct dasd_ccw_req *cqr;
+ int rc;
+
+ cqr = dasd_eckd_build_psf_ssc(device);
+ if (IS_ERR(cqr))
+ return PTR_ERR(cqr);
+
+ rc = dasd_sleep_on(cqr);
+ if (!rc)
+ /* trigger CIO to reprobe devices */
+ css_schedule_reprobe();
+ dasd_sfree_request(cqr, cqr->device);
+ return rc;
+}
+
+/*
+ * Valide storage server of current device.
+ */
+static int
+dasd_eckd_validate_server(struct dasd_device *device)
+{
+ int rc;
+
+ /* Currently PAV is the only reason to 'validate' server on LPAR */
+ if (dasd_nopav || MACHINE_IS_VM)
+ return 0;
+
+ rc = dasd_eckd_psf_ssc(device);
+ if (rc)
+ /* may be requested feature is not available on server,
+ * therefore just report error and go ahead */
+ DEV_MESSAGE(KERN_INFO, device,
+ "Perform Subsystem Function returned rc=%d", rc);
+ /* RE-Read Configuration Data */
+ return dasd_eckd_read_conf(device);
+}
+
+/*
* Check device characteristics.
* If the device is accessible using ECKD discipline, the device is enabled.
*/
@@ -554,7 +640,7 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private = (struct dasd_eckd_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_eckd_private),
+ private = kzalloc(sizeof(struct dasd_eckd_private),
GFP_KERNEL | GFP_DMA);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
@@ -562,7 +648,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
"data");
return -ENOMEM;
}
- memset(private, 0, sizeof(struct dasd_eckd_private));
device->private = (void *) private;
}
/* Invalidate status of initial analysis. */
@@ -571,16 +656,29 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->attrib.operation = DASD_NORMAL_CACHE;
private->attrib.nr_cyl = 0;
+ /* Read Configuration Data */
+ rc = dasd_eckd_read_conf(device);
+ if (rc)
+ return rc;
+
+ /* Generate device unique id and register in devmap */
+ rc = dasd_eckd_generate_uid(device, &uid);
+ if (rc)
+ return rc;
+ rc = dasd_set_uid(device->cdev, &uid);
+ if (rc == 1) /* new server found */
+ rc = dasd_eckd_validate_server(device);
+ if (rc)
+ return rc;
+
/* Read Device Characteristics */
rdc_data = (void *) &(private->rdc_data);
memset(rdc_data, 0, sizeof(rdc_data));
rc = read_dev_chars(device->cdev, &rdc_data, 64);
- if (rc) {
+ if (rc)
DEV_MESSAGE(KERN_WARNING, device,
- "Read device characteristics returned error %d",
- rc);
- return rc;
- }
+ "Read device characteristics returned "
+ "rc=%d", rc);
DEV_MESSAGE(KERN_INFO, device,
"%04X/%02X(CU:%04X/%02X) Cyl:%d Head:%d Sec:%d",
@@ -591,19 +689,6 @@ dasd_eckd_check_characteristics(struct dasd_device *device)
private->rdc_data.no_cyl,
private->rdc_data.trk_per_cyl,
private->rdc_data.sec_per_trk);
-
- /* Read Configuration Data */
- rc = dasd_eckd_read_conf (device);
- if (rc)
- return rc;
-
- /* Generate device unique id and register in devmap */
- rc = dasd_eckd_generate_uid(device, &uid);
- if (rc)
- return rc;
-
- rc = dasd_set_uid(device->cdev, &uid);
-
return rc;
}
@@ -773,7 +858,7 @@ dasd_eckd_end_analysis(struct dasd_device *device)
((private->rdc_data.no_cyl *
private->rdc_data.trk_per_cyl *
blk_per_trk * (device->bp_block >> 9)) >> 1),
- ((blk_per_trk * device->bp_block) >> 10),
+ ((blk_per_trk * device->bp_block) >> 10),
private->uses_cdl ?
"compatible disk layout" : "linux disk layout");
@@ -970,7 +1055,7 @@ dasd_eckd_format_device(struct dasd_device * device,
if (i < 3) {
ect->kl = 4;
ect->dl = sizes_trk0[i] - 4;
- }
+ }
}
if ((fdata->intensity & 0x08) &&
fdata->start_unit == 1) {
@@ -1270,7 +1355,7 @@ dasd_eckd_fill_info(struct dasd_device * device,
/*
* Release device ioctl.
- * Buils a channel programm to releases a prior reserved
+ * Buils a channel programm to releases a prior reserved
* (see dasd_eckd_reserve) device.
*/
static int
@@ -1310,8 +1395,8 @@ dasd_eckd_release(struct dasd_device *device)
/*
* Reserve device ioctl.
* Options are set to 'synchronous wait for interrupt' and
- * 'timeout the request'. This leads to a terminate IO if
- * the interrupt is outstanding for a certain time.
+ * 'timeout the request'. This leads to a terminate IO if
+ * the interrupt is outstanding for a certain time.
*/
static int
dasd_eckd_reserve(struct dasd_device *device)
@@ -1349,7 +1434,7 @@ dasd_eckd_reserve(struct dasd_device *device)
/*
* Steal lock ioctl - unconditional reserve device.
- * Buils a channel programm to break a device's reservation.
+ * Buils a channel programm to break a device's reservation.
* (unconditional reserve)
*/
static int
@@ -1522,6 +1607,40 @@ dasd_eckd_ioctl(struct dasd_device *device, unsigned int cmd, void __user *argp)
}
/*
+ * Dump the range of CCWs into 'page' buffer
+ * and return number of printed chars.
+ */
+static inline int
+dasd_eckd_dump_ccw_range(struct ccw1 *from, struct ccw1 *to, char *page)
+{
+ int len, count;
+ char *datap;
+
+ len = 0;
+ while (from <= to) {
+ len += sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " CCW %p: %08X %08X DAT:",
+ from, ((int *) from)[0], ((int *) from)[1]);
+
+ /* get pointer to data (consider IDALs) */
+ if (from->flags & CCW_FLAG_IDA)
+ datap = (char *) *((addr_t *) (addr_t) from->cda);
+ else
+ datap = (char *) ((addr_t) from->cda);
+
+ /* dump data (max 32 bytes) */
+ for (count = 0; count < from->count && count < 32; count++) {
+ if (count % 8 == 0) len += sprintf(page + len, " ");
+ if (count % 4 == 0) len += sprintf(page + len, " ");
+ len += sprintf(page + len, "%02x", datap[count]);
+ }
+ len += sprintf(page + len, "\n");
+ from++;
+ }
+ return len;
+}
+
+/*
* Print sense data and related channel program.
* Parts are printed because printk buffer is only 1024 bytes.
*/
@@ -1530,8 +1649,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
struct irb *irb)
{
char *page;
- struct ccw1 *act, *end, *last;
- int len, sl, sct, count;
+ struct ccw1 *first, *last, *fail, *from, *to;
+ int len, sl, sct;
page = (char *) get_zeroed_page(GFP_ATOMIC);
if (page == NULL) {
@@ -1539,7 +1658,8 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
"No memory to dump sense data");
return;
}
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ /* dump the sense data */
+ len = sprintf(page, KERN_ERR PRINTK_HEADER
" I/O status report for device %s:\n",
device->cdev->dev.bus_id);
len += sprintf(page + len, KERN_ERR PRINTK_HEADER
@@ -1564,87 +1684,55 @@ dasd_eckd_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
if (irb->ecw[27] & DASD_SENSE_BIT_0) {
/* 24 Byte Sense Data */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " 24 Byte: %x MSG %x, "
- "%s MSGb to SYSOP\n",
- irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
- irb->ecw[1] & 0x10 ? "" : "no");
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " 24 Byte: %x MSG %x, "
+ "%s MSGb to SYSOP\n",
+ irb->ecw[7] >> 4, irb->ecw[7] & 0x0f,
+ irb->ecw[1] & 0x10 ? "" : "no");
} else {
/* 32 Byte Sense Data */
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " 32 Byte: Format: %x "
- "Exception class %x\n",
- irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " 32 Byte: Format: %x "
+ "Exception class %x\n",
+ irb->ecw[6] & 0x0f, irb->ecw[22] >> 4);
}
} else {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " SORRY - NO VALID SENSE AVAILABLE\n");
+ sprintf(page + len, KERN_ERR PRINTK_HEADER
+ " SORRY - NO VALID SENSE AVAILABLE\n");
}
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
-
- /* dump the Channel Program */
- /* print first CCWs (maximum 8) */
- act = req->cpaddr;
- for (last = act; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
- end = min(act + 8, last);
- len = sprintf(page, KERN_ERR PRINTK_HEADER
+ printk("%s", page);
+
+ /* dump the Channel Program (max 140 Bytes per line) */
+ /* Count CCW and print first CCWs (maximum 1024 % 140 = 7) */
+ first = req->cpaddr;
+ for (last = first; last->flags & (CCW_FLAG_CC | CCW_FLAG_DC); last++);
+ to = min(first + 6, last);
+ len = sprintf(page, KERN_ERR PRINTK_HEADER
" Related CP in req: %p\n", req);
- while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
- }
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
+ dasd_eckd_dump_ccw_range(first, to, page + len);
+ printk("%s", page);
- /* print failing CCW area */
+ /* print failing CCW area (maximum 4) */
+ /* scsw->cda is either valid or zero */
len = 0;
- if (act < ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2) {
- act = ((struct ccw1 *)(addr_t) irb->scsw.cpa) - 2;
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
- }
- end = min((struct ccw1 *)(addr_t) irb->scsw.cpa + 2, last);
- while (act <= end) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
+ from = ++to;
+ fail = (struct ccw1 *)(addr_t) irb->scsw.cpa; /* failing CCW */
+ if (from < fail - 2) {
+ from = fail - 2; /* there is a gap - print header */
+ len += sprintf(page, KERN_ERR PRINTK_HEADER "......\n");
}
+ to = min(fail + 1, last);
+ len += dasd_eckd_dump_ccw_range(from, to, page + len);
- /* print last CCWs */
- if (act < last - 2) {
- act = last - 2;
+ /* print last CCWs (maximum 2) */
+ from = max(from, ++to);
+ if (from < last - 1) {
+ from = last - 1; /* there is a gap - print header */
len += sprintf(page + len, KERN_ERR PRINTK_HEADER "......\n");
}
- while (act <= last) {
- len += sprintf(page + len, KERN_ERR PRINTK_HEADER
- " CCW %p: %08X %08X DAT:",
- act, ((int *) act)[0], ((int *) act)[1]);
- for (count = 0; count < 32 && count < act->count;
- count += sizeof(int))
- len += sprintf(page + len, " %08X",
- ((int *) (addr_t) act->cda)
- [(count>>2)]);
- len += sprintf(page + len, "\n");
- act++;
- }
+ len += dasd_eckd_dump_ccw_range(from, last, page + len);
if (len > 0)
- MESSAGE_LOG(KERN_ERR, "%s",
- page + sizeof(KERN_ERR PRINTK_HEADER));
+ printk("%s", page);
free_page((unsigned long) page);
}
@@ -1685,14 +1773,8 @@ static struct dasd_discipline dasd_eckd_discipline = {
static int __init
dasd_eckd_init(void)
{
- int ret;
-
ASCEBC(dasd_eckd_discipline.ebcname, 4);
-
- ret = ccw_driver_register(&dasd_eckd_driver);
- if (!ret)
- dasd_generic_auto_online(&dasd_eckd_driver);
- return ret;
+ return ccw_driver_register(&dasd_eckd_driver);
}
static void __exit
@@ -1703,22 +1785,3 @@ dasd_eckd_cleanup(void)
module_init(dasd_eckd_init);
module_exit(dasd_eckd_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_eckd.h b/drivers/s390/block/dasd_eckd.h
index d5734e976e1c..712ff1650134 100644
--- a/drivers/s390/block/dasd_eckd.h
+++ b/drivers/s390/block/dasd_eckd.h
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_eckd.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
*
@@ -41,9 +41,10 @@
#define DASD_ECKD_CCW_RESERVE 0xB4
/*
- *Perform Subsystem Function / Sub-Orders
+ * Perform Subsystem Function / Sub-Orders
*/
-#define PSF_ORDER_PRSSD 0x18
+#define PSF_ORDER_PRSSD 0x18
+#define PSF_ORDER_SSC 0x1D
/*****************************************************************************
* SECTION: Type Definitions
@@ -155,7 +156,7 @@ struct dasd_eckd_characteristics {
unsigned char reserved2:4;
unsigned char reserved3:8;
unsigned char defect_wr:1;
- unsigned char XRC_supported:1;
+ unsigned char XRC_supported:1;
unsigned char reserved4:1;
unsigned char striping:1;
unsigned char reserved5:4;
@@ -343,7 +344,7 @@ struct dasd_eckd_path {
};
/*
- * Perform Subsystem Function - Prepare for Read Subsystem Data
+ * Perform Subsystem Function - Prepare for Read Subsystem Data
*/
struct dasd_psf_prssd_data {
unsigned char order;
@@ -353,4 +354,15 @@ struct dasd_psf_prssd_data {
unsigned char varies[9];
} __attribute__ ((packed));
+/*
+ * Perform Subsystem Function - Set Subsystem Characteristics
+ */
+struct dasd_psf_ssc_data {
+ unsigned char order;
+ unsigned char flags;
+ unsigned char cu_type[4];
+ unsigned char suborder;
+ unsigned char reserved[59];
+} __attribute__((packed));
+
#endif /* DASD_ECKD_H */
diff --git a/drivers/s390/block/dasd_eer.c b/drivers/s390/block/dasd_eer.c
index 2d946b6ca074..da65f1b032f5 100644
--- a/drivers/s390/block/dasd_eer.c
+++ b/drivers/s390/block/dasd_eer.c
@@ -89,7 +89,7 @@ struct eerbuffer {
};
static LIST_HEAD(bufferlist);
-static spinlock_t bufferlock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(bufferlock);
static DECLARE_WAIT_QUEUE_HEAD(dasd_eer_read_wait_queue);
/*
@@ -276,7 +276,7 @@ struct dasd_eer_header {
__u64 tv_sec;
__u64 tv_usec;
char busid[DASD_EER_BUSID_SIZE];
-};
+} __attribute__ ((packed));
/*
* The following function can be used for those triggers that have
@@ -521,6 +521,8 @@ static int dasd_eer_open(struct inode *inp, struct file *filp)
unsigned long flags;
eerb = kzalloc(sizeof(struct eerbuffer), GFP_KERNEL);
+ if (!eerb)
+ return -ENOMEM;
eerb->buffer_page_count = eer_pages;
if (eerb->buffer_page_count < 1 ||
eerb->buffer_page_count > INT_MAX / PAGE_SIZE) {
diff --git a/drivers/s390/block/dasd_erp.c b/drivers/s390/block/dasd_erp.c
index b842377cb0c6..4108d96f6a5a 100644
--- a/drivers/s390/block/dasd_erp.c
+++ b/drivers/s390/block/dasd_erp.c
@@ -90,7 +90,7 @@ dasd_default_erp_action(struct dasd_ccw_req * cqr)
/* just retry - there is nothing to save ... I got no sense data.... */
if (cqr->retries > 0) {
- DEV_MESSAGE (KERN_DEBUG, device,
+ DEV_MESSAGE (KERN_DEBUG, device,
"default ERP called (%i retries left)",
cqr->retries);
cqr->lpm = LPM_ANYPATH;
@@ -155,7 +155,7 @@ dasd_default_erp_postaction(struct dasd_ccw_req * cqr)
/*
* Print the hex dump of the memory used by a request. This includes
- * all error recovery ccws that have been chained in from of the
+ * all error recovery ccws that have been chained in from of the
* real request.
*/
static inline void
@@ -227,12 +227,12 @@ dasd_log_ccw(struct dasd_ccw_req * cqr, int caller, __u32 cpa)
/*
* Log bytes arround failed CCW but only if we did
* not log the whole CP of the CCW is outside the
- * logged CP.
+ * logged CP.
*/
if (cplength > 40 ||
((addr_t) cpa < (addr_t) lcqr->cpaddr &&
(addr_t) cpa > (addr_t) (lcqr->cpaddr + cplength + 4))) {
-
+
DEV_MESSAGE(KERN_ERR, device,
"Failed CCW (%p) (area):",
(void *) (long) cpa);
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index 91145698f8e9..bb7755b9b19d 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_fba.c
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
@@ -56,19 +56,13 @@ static struct ccw_driver dasd_fba_driver; /* see below */
static int
dasd_fba_probe(struct ccw_device *cdev)
{
- int ret;
-
- ret = dasd_generic_probe (cdev, &dasd_fba_discipline);
- if (ret)
- return ret;
- ccw_device_set_options(cdev, CCWDEV_DO_PATHGROUP);
- return 0;
+ return dasd_generic_probe(cdev, &dasd_fba_discipline);
}
static int
dasd_fba_set_online(struct ccw_device *cdev)
{
- return dasd_generic_set_online (cdev, &dasd_fba_discipline);
+ return dasd_generic_set_online(cdev, &dasd_fba_discipline);
}
static struct ccw_driver dasd_fba_driver = {
@@ -125,13 +119,13 @@ static int
dasd_fba_check_characteristics(struct dasd_device *device)
{
struct dasd_fba_private *private;
- struct ccw_device *cdev = device->cdev;
+ struct ccw_device *cdev = device->cdev;
void *rdc_data;
int rc;
private = (struct dasd_fba_private *) device->private;
if (private == NULL) {
- private = kmalloc(sizeof(struct dasd_fba_private), GFP_KERNEL);
+ private = kzalloc(sizeof(struct dasd_fba_private), GFP_KERNEL);
if (private == NULL) {
DEV_MESSAGE(KERN_WARNING, device, "%s",
"memory allocation failed for private "
@@ -204,7 +198,7 @@ dasd_fba_examine_error(struct dasd_ccw_req * cqr, struct irb * irb)
if (irb->scsw.cstat == 0x00 &&
irb->scsw.dstat == (DEV_STAT_CHN_END | DEV_STAT_DEV_END))
return dasd_era_none;
-
+
cdev = device->cdev;
switch (cdev->id.dev_type) {
case 0x3370:
@@ -539,7 +533,7 @@ dasd_fba_dump_sense(struct dasd_device *device, struct dasd_ccw_req * req,
* 8192 bytes (=2 pages). For 64 bit one dasd_mchunkt_t structure has
* 24 bytes, the struct dasd_ccw_req has 136 bytes and each block can use
* up to 16 bytes (8 for the ccw and 8 for the idal pointer). In
- * addition we have one define extent ccw + 16 bytes of data and a
+ * addition we have one define extent ccw + 16 bytes of data and a
* locate record ccw for each block (stupid devices!) + 16 bytes of data.
* That makes:
* (8192 - 24 - 136 - 8 - 16) / 40 = 200.2 blocks at maximum.
@@ -569,16 +563,8 @@ static struct dasd_discipline dasd_fba_discipline = {
static int __init
dasd_fba_init(void)
{
- int ret;
-
ASCEBC(dasd_fba_discipline.ebcname, 4);
-
- ret = ccw_driver_register(&dasd_fba_driver);
- if (ret)
- return ret;
-
- dasd_generic_auto_online(&dasd_fba_driver);
- return 0;
+ return ccw_driver_register(&dasd_fba_driver);
}
static void __exit
@@ -589,22 +575,3 @@ dasd_fba_cleanup(void)
module_init(dasd_fba_init);
module_exit(dasd_fba_cleanup);
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_fba.h b/drivers/s390/block/dasd_fba.h
index da1fa91fc01d..14c910baa5fe 100644
--- a/drivers/s390/block/dasd_fba.h
+++ b/drivers/s390/block/dasd_fba.h
@@ -1,4 +1,4 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_fba.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
diff --git a/drivers/s390/block/dasd_int.h b/drivers/s390/block/dasd_int.h
index d4b13e300a76..03a83efc34c4 100644
--- a/drivers/s390/block/dasd_int.h
+++ b/drivers/s390/block/dasd_int.h
@@ -1,7 +1,7 @@
-/*
+/*
* File...........: linux/drivers/s390/block/dasd_int.h
* Author(s)......: Holger Smolinski <Holger.Smolinski@de.ibm.com>
- * Horst Hummel <Horst.Hummel@de.ibm.com>
+ * Horst Hummel <Horst.Hummel@de.ibm.com>
* Martin Schwidefsky <schwidefsky@de.ibm.com>
* Bugreports.to..: <Linux390@de.ibm.com>
* (C) IBM Corporation, IBM Deutschland Entwicklung GmbH, 1999,2000
@@ -186,7 +186,7 @@ struct dasd_ccw_req {
void *callback_data;
};
-/*
+/*
* dasd_ccw_req -> status can be:
*/
#define DASD_CQR_FILLED 0x00 /* request is ready to be processed */
@@ -248,7 +248,7 @@ struct dasd_discipline {
/*
* Error recovery functions. examine_error() returns a value that
* indicates what to do for an error condition. If examine_error()
- * returns 'dasd_era_recover' erp_action() is called to create a
+ * returns 'dasd_era_recover' erp_action() is called to create a
* special error recovery ccw. erp_postaction() is called after
* an error recovery ccw has finished its execution. dump_sense
* is called for every error condition to print the sense data
@@ -302,11 +302,11 @@ struct dasd_device {
spinlock_t request_queue_lock;
struct block_device *bdev;
unsigned int devindex;
- unsigned long blocks; /* size of volume in blocks */
- unsigned int bp_block; /* bytes per block */
- unsigned int s2b_shift; /* log2 (bp_block/512) */
- unsigned long flags; /* per device flags */
- unsigned short features; /* copy of devmap-features (read-only!) */
+ unsigned long blocks; /* size of volume in blocks */
+ unsigned int bp_block; /* bytes per block */
+ unsigned int s2b_shift; /* log2 (bp_block/512) */
+ unsigned long flags; /* per device flags */
+ unsigned short features; /* copy of devmap-features (read-only!) */
/* extended error reporting stuff (eer) */
struct dasd_ccw_req *eer_cqr;
@@ -513,12 +513,12 @@ void dasd_generic_remove (struct ccw_device *cdev);
int dasd_generic_set_online(struct ccw_device *, struct dasd_discipline *);
int dasd_generic_set_offline (struct ccw_device *cdev);
int dasd_generic_notify(struct ccw_device *, int);
-void dasd_generic_auto_online (struct ccw_driver *);
/* externals in dasd_devmap.c */
extern int dasd_max_devindex;
extern int dasd_probeonly;
extern int dasd_autodetect;
+extern int dasd_nopav;
int dasd_devmap_init(void);
void dasd_devmap_exit(void);
@@ -606,22 +606,3 @@ static inline int dasd_eer_enabled(struct dasd_device *device)
#endif /* __KERNEL__ */
#endif /* DASD_H */
-
-/*
- * Overrides for Emacs so that we follow Linus's tabbing style.
- * Emacs will notice this stuff at the end of the file and automatically
- * adjust the settings for this buffer only. This must remain at the end
- * of the file.
- * ---------------------------------------------------------------------------
- * Local variables:
- * c-indent-level: 4
- * c-brace-imaginary-offset: 0
- * c-brace-offset: -4
- * c-argdecl-indent: 4
- * c-label-offset: -4
- * c-continued-statement-offset: 4
- * c-continued-brace-offset: 0
- * indent-tabs-mode: 1
- * tab-width: 8
- * End:
- */
diff --git a/drivers/s390/block/dasd_ioctl.c b/drivers/s390/block/dasd_ioctl.c
index b8c80d28df41..302bcd0f28be 100644
--- a/drivers/s390/block/dasd_ioctl.c
+++ b/drivers/s390/block/dasd_ioctl.c
@@ -90,10 +90,10 @@ static int
dasd_ioctl_quiesce(struct dasd_device *device)
{
unsigned long flags;
-
+
if (!capable (CAP_SYS_ADMIN))
return -EACCES;
-
+
DEV_MESSAGE (KERN_DEBUG, device, "%s",
"Quiesce IO on device");
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
@@ -110,13 +110,13 @@ static int
dasd_ioctl_resume(struct dasd_device *device)
{
unsigned long flags;
-
- if (!capable (CAP_SYS_ADMIN))
+
+ if (!capable (CAP_SYS_ADMIN))
return -EACCES;
DEV_MESSAGE (KERN_DEBUG, device, "%s",
"resume IO on device");
-
+
spin_lock_irqsave(get_ccwdev_lock(device->cdev), flags);
device->stopped &= ~DASD_STOPPED_QUIESCE;
spin_unlock_irqrestore(get_ccwdev_lock(device->cdev), flags);
@@ -287,7 +287,7 @@ dasd_ioctl_information(struct dasd_device *device,
dasd_info->open_count = atomic_read(&device->open_count);
if (!device->bdev)
dasd_info->open_count++;
-
+
/*
* check if device is really formatted
* LDL / CDL was returned by 'fill_info'
diff --git a/drivers/s390/char/raw3270.c b/drivers/s390/char/raw3270.c
index eecb2afad5c2..3c1314b7391b 100644
--- a/drivers/s390/char/raw3270.c
+++ b/drivers/s390/char/raw3270.c
@@ -50,6 +50,9 @@ struct raw3270 {
unsigned char *ascebc; /* ascii -> ebcdic table */
struct class_device *clttydev; /* 3270-class tty device ptr */
struct class_device *cltubdev; /* 3270-class tub device ptr */
+
+ struct raw3270_request init_request;
+ unsigned char init_data[256];
};
/* raw3270->flags */
@@ -484,8 +487,6 @@ struct raw3270_ua { /* Query Reply structure for Usable Area */
} __attribute__ ((packed)) aua;
} __attribute__ ((packed));
-static unsigned char raw3270_init_data[256];
-static struct raw3270_request raw3270_init_request;
static struct diag210 raw3270_init_diag210;
static DECLARE_MUTEX(raw3270_init_sem);
@@ -644,17 +645,17 @@ __raw3270_size_device(struct raw3270 *rp)
* required (3270 device switched to 'stand-by') and command
* rejects (old devices that can't do 'read partition').
*/
- memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
- memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
- /* Store 'read partition' data stream to raw3270_init_data */
- memcpy(raw3270_init_data, wbuf, sizeof(wbuf));
- INIT_LIST_HEAD(&raw3270_init_request.list);
- raw3270_init_request.ccw.cmd_code = TC_WRITESF;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = sizeof(wbuf);
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
-
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+ memset(&rp->init_request, 0, sizeof(rp->init_request));
+ memset(&rp->init_data, 0, 256);
+ /* Store 'read partition' data stream to init_data */
+ memcpy(&rp->init_data, wbuf, sizeof(wbuf));
+ INIT_LIST_HEAD(&rp->init_request.list);
+ rp->init_request.ccw.cmd_code = TC_WRITESF;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = sizeof(wbuf);
+ rp->init_request.ccw.cda = (__u32) __pa(&rp->init_data);
+
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
/* Check error cases: -ERESTARTSYS, -EIO and -EOPNOTSUPP */
return rc;
@@ -679,18 +680,18 @@ __raw3270_size_device(struct raw3270 *rp)
* The device accepted the 'read partition' command. Now
* set up a read ccw and issue it.
*/
- raw3270_init_request.ccw.cmd_code = TC_READMOD;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = sizeof(raw3270_init_data);
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+ rp->init_request.ccw.cmd_code = TC_READMOD;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = sizeof(rp->init_data);
+ rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
if (rc)
return rc;
/* Got a Query Reply */
- count = sizeof(raw3270_init_data) - raw3270_init_request.rescnt;
- uap = (struct raw3270_ua *) (raw3270_init_data + 1);
+ count = sizeof(rp->init_data) - rp->init_request.rescnt;
+ uap = (struct raw3270_ua *) (rp->init_data + 1);
/* Paranoia check. */
- if (raw3270_init_data[0] != 0x88 || uap->uab.qcode != 0x81)
+ if (rp->init_data[0] != 0x88 || uap->uab.qcode != 0x81)
return -EOPNOTSUPP;
/* Copy rows/columns of default Usable Area */
rp->rows = uap->uab.h;
@@ -749,18 +750,18 @@ raw3270_reset_device(struct raw3270 *rp)
int rc;
down(&raw3270_init_sem);
- memset(&raw3270_init_request, 0, sizeof(raw3270_init_request));
- memset(raw3270_init_data, 0, sizeof(raw3270_init_data));
- /* Store reset data stream to raw3270_init_data/raw3270_init_request */
- raw3270_init_data[0] = TW_KR;
- INIT_LIST_HEAD(&raw3270_init_request.list);
- raw3270_init_request.ccw.cmd_code = TC_EWRITEA;
- raw3270_init_request.ccw.flags = CCW_FLAG_SLI;
- raw3270_init_request.ccw.count = 1;
- raw3270_init_request.ccw.cda = (__u32) __pa(raw3270_init_data);
+ memset(&rp->init_request, 0, sizeof(rp->init_request));
+ memset(&rp->init_data, 0, sizeof(rp->init_data));
+ /* Store reset data stream to init_data/init_request */
+ rp->init_data[0] = TW_KR;
+ INIT_LIST_HEAD(&rp->init_request.list);
+ rp->init_request.ccw.cmd_code = TC_EWRITEA;
+ rp->init_request.ccw.flags = CCW_FLAG_SLI;
+ rp->init_request.ccw.count = 1;
+ rp->init_request.ccw.cda = (__u32) __pa(rp->init_data);
rp->view = &raw3270_init_view;
raw3270_init_view.dev = rp;
- rc = raw3270_start_init(rp, &raw3270_init_view, &raw3270_init_request);
+ rc = raw3270_start_init(rp, &raw3270_init_view, &rp->init_request);
raw3270_init_view.dev = 0;
rp->view = 0;
up(&raw3270_init_sem);
@@ -854,7 +855,7 @@ raw3270_setup_console(struct ccw_device *cdev)
char *ascebc;
int rc;
- rp = (struct raw3270 *) alloc_bootmem(sizeof(struct raw3270));
+ rp = (struct raw3270 *) alloc_bootmem_low(sizeof(struct raw3270));
ascebc = (char *) alloc_bootmem(256);
rc = raw3270_setup_device(cdev, rp, ascebc);
if (rc)
@@ -895,7 +896,7 @@ raw3270_create_device(struct ccw_device *cdev)
char *ascebc;
int rc;
- rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL);
+ rp = kmalloc(sizeof(struct raw3270), GFP_KERNEL | GFP_DMA);
if (!rp)
return ERR_PTR(-ENOMEM);
ascebc = kmalloc(256, GFP_KERNEL);
diff --git a/drivers/s390/cio/blacklist.c b/drivers/s390/cio/blacklist.c
index 0960bef7b199..15b895496a45 100644
--- a/drivers/s390/cio/blacklist.c
+++ b/drivers/s390/cio/blacklist.c
@@ -224,39 +224,6 @@ is_blacklisted (int ssid, int devno)
}
#ifdef CONFIG_PROC_FS
-static int
-__s390_redo_validation(struct subchannel_id schid, void *data)
-{
- int ret;
- struct subchannel *sch;
-
- sch = get_subchannel_by_schid(schid);
- if (sch) {
- /* Already known. */
- put_device(&sch->dev);
- return 0;
- }
- ret = css_probe_device(schid);
- if (ret == -ENXIO)
- return ret; /* We're through. */
- if (ret == -ENOMEM)
- /* Stop validation for now. Bad, but no need for a panic. */
- return ret;
- return 0;
-}
-
-/*
- * Function: s390_redo_validation
- * Look for no longer blacklisted devices
- * FIXME: there must be a better way to do this */
-static inline void
-s390_redo_validation (void)
-{
- CIO_TRACE_EVENT (0, "redoval");
-
- for_each_subchannel(__s390_redo_validation, NULL);
-}
-
/*
* Function: blacklist_parse_proc_parameters
* parse the stuff which is piped to /proc/cio_ignore
@@ -281,7 +248,7 @@ blacklist_parse_proc_parameters (char *buf)
return;
}
- s390_redo_validation ();
+ css_schedule_reprobe();
}
/* Iterator struct for all devices. */
diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c
index bdfee7fbaa2e..c7319a07ba35 100644
--- a/drivers/s390/cio/ccwgroup.c
+++ b/drivers/s390/cio/ccwgroup.c
@@ -404,21 +404,24 @@ ccwgroup_driver_register (struct ccwgroup_driver *cdriver)
}
static int
-__ccwgroup_driver_unregister_device(struct device *dev, void *data)
+__ccwgroup_match_all(struct device *dev, void *data)
{
- __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
- device_unregister(dev);
- put_device(dev);
- return 0;
+ return 1;
}
void
ccwgroup_driver_unregister (struct ccwgroup_driver *cdriver)
{
+ struct device *dev;
+
/* We don't want ccwgroup devices to live longer than their driver. */
get_driver(&cdriver->driver);
- driver_for_each_device(&cdriver->driver, NULL, NULL,
- __ccwgroup_driver_unregister_device);
+ while ((dev = driver_find_device(&cdriver->driver, NULL, NULL,
+ __ccwgroup_match_all))) {
+ __ccwgroup_remove_symlinks(to_ccwgroupdev(dev));
+ device_unregister(dev);
+ put_device(dev);
+ }
put_driver(&cdriver->driver);
driver_unregister(&cdriver->driver);
}
diff --git a/drivers/s390/cio/chsc.c b/drivers/s390/cio/chsc.c
index 72187e54dcac..b00f3ed051a0 100644
--- a/drivers/s390/cio/chsc.c
+++ b/drivers/s390/cio/chsc.c
@@ -244,8 +244,7 @@ s390_subchannel_remove_chpid(struct device *dev, void *data)
if ((sch->schib.scsw.actl & SCSW_ACTL_DEVACT) &&
(sch->schib.scsw.actl & SCSW_ACTL_SCHACT) &&
- (sch->schib.pmcw.lpum == mask) &&
- (sch->vpm == 0)) {
+ (sch->schib.pmcw.lpum == mask)) {
int cc;
cc = cio_clear(sch);
@@ -918,12 +917,13 @@ chp_measurement_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
chp = to_channelpath(container_of(kobj, struct device, kobj));
css = to_css(chp->dev.parent);
- size = sizeof(struct cmg_chars);
+ size = sizeof(struct cmg_entry);
/* Only allow single reads. */
if (off || count < size)
return 0;
chp_measurement_copy_block((struct cmg_entry *)buf, css, chp->id);
+ count = size;
return count;
}
diff --git a/drivers/s390/cio/cmf.c b/drivers/s390/cio/cmf.c
index 07ef3f640f4a..1c3e8e9012b0 100644
--- a/drivers/s390/cio/cmf.c
+++ b/drivers/s390/cio/cmf.c
@@ -3,9 +3,10 @@
*
* Linux on zSeries Channel Measurement Facility support
*
- * Copyright 2000,2003 IBM Corporation
+ * Copyright 2000,2006 IBM Corporation
*
- * Author: Arnd Bergmann <arndb@de.ibm.com>
+ * Authors: Arnd Bergmann <arndb@de.ibm.com>
+ * Cornelia Huck <cornelia.huck@de.ibm.com>
*
* original idea from Natarajan Krishnaswami <nkrishna@us.ibm.com>
*
@@ -96,9 +97,9 @@ module_param(format, bool, 0444);
/**
* struct cmb_operations - functions to use depending on cmb_format
*
- * all these functions operate on a struct cmf_device. There is only
- * one instance of struct cmb_operations because all cmf_device
- * objects are guaranteed to be of the same type.
+ * Most of these functions operate on a struct ccw_device. There is only
+ * one instance of struct cmb_operations because the format of the measurement
+ * data is guaranteed to be the same for every ccw_device.
*
* @alloc: allocate memory for a channel measurement block,
* either with the help of a special pool or with kmalloc
@@ -107,6 +108,7 @@ module_param(format, bool, 0444);
* @readall: read a measurement block in a common format
* @reset: clear the data in the associated measurement block and
* reset its time stamp
+ * @align: align an allocated block so that the hardware can use it
*/
struct cmb_operations {
int (*alloc) (struct ccw_device*);
@@ -115,11 +117,19 @@ struct cmb_operations {
u64 (*read) (struct ccw_device*, int);
int (*readall)(struct ccw_device*, struct cmbdata *);
void (*reset) (struct ccw_device*);
+ void * (*align) (void *);
struct attribute_group *attr_group;
};
static struct cmb_operations *cmbops;
+struct cmb_data {
+ void *hw_block; /* Pointer to block updated by hardware */
+ void *last_block; /* Last changed block copied from hardware block */
+ int size; /* Size of hw_block and last_block */
+ unsigned long long last_update; /* when last_block was updated */
+};
+
/* our user interface is designed in terms of nanoseconds,
* while the hardware measures total times in its own
* unit.*/
@@ -226,63 +236,229 @@ struct set_schib_struct {
unsigned long address;
wait_queue_head_t wait;
int ret;
+ struct kref kref;
};
+static void cmf_set_schib_release(struct kref *kref)
+{
+ struct set_schib_struct *set_data;
+
+ set_data = container_of(kref, struct set_schib_struct, kref);
+ kfree(set_data);
+}
+
+#define CMF_PENDING 1
+
static int set_schib_wait(struct ccw_device *cdev, u32 mme,
int mbfc, unsigned long address)
{
- struct set_schib_struct s = {
- .mme = mme,
- .mbfc = mbfc,
- .address = address,
- .wait = __WAIT_QUEUE_HEAD_INITIALIZER(s.wait),
- };
+ struct set_schib_struct *set_data;
+ int ret;
spin_lock_irq(cdev->ccwlock);
- s.ret = set_schib(cdev, mme, mbfc, address);
- if (s.ret != -EBUSY) {
- goto out_nowait;
+ if (!cdev->private->cmb) {
+ ret = -ENODEV;
+ goto out;
}
+ set_data = kzalloc(sizeof(struct set_schib_struct), GFP_ATOMIC);
+ if (!set_data) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(&set_data->wait);
+ kref_init(&set_data->kref);
+ set_data->mme = mme;
+ set_data->mbfc = mbfc;
+ set_data->address = address;
+
+ ret = set_schib(cdev, mme, mbfc, address);
+ if (ret != -EBUSY)
+ goto out_put;
if (cdev->private->state != DEV_STATE_ONLINE) {
- s.ret = -EBUSY;
/* if the device is not online, don't even try again */
- goto out_nowait;
+ ret = -EBUSY;
+ goto out_put;
}
+
cdev->private->state = DEV_STATE_CMFCHANGE;
- cdev->private->cmb_wait = &s;
- s.ret = 1;
+ set_data->ret = CMF_PENDING;
+ cdev->private->cmb_wait = set_data;
spin_unlock_irq(cdev->ccwlock);
- if (wait_event_interruptible(s.wait, s.ret != 1)) {
+ if (wait_event_interruptible(set_data->wait,
+ set_data->ret != CMF_PENDING)) {
spin_lock_irq(cdev->ccwlock);
- if (s.ret == 1) {
- s.ret = -ERESTARTSYS;
- cdev->private->cmb_wait = 0;
+ if (set_data->ret == CMF_PENDING) {
+ set_data->ret = -ERESTARTSYS;
if (cdev->private->state == DEV_STATE_CMFCHANGE)
cdev->private->state = DEV_STATE_ONLINE;
}
spin_unlock_irq(cdev->ccwlock);
}
- return s.ret;
-
-out_nowait:
+ spin_lock_irq(cdev->ccwlock);
+ cdev->private->cmb_wait = NULL;
+ ret = set_data->ret;
+out_put:
+ kref_put(&set_data->kref, cmf_set_schib_release);
+out:
spin_unlock_irq(cdev->ccwlock);
- return s.ret;
+ return ret;
}
void retry_set_schib(struct ccw_device *cdev)
{
- struct set_schib_struct *s;
+ struct set_schib_struct *set_data;
+
+ set_data = cdev->private->cmb_wait;
+ if (!set_data) {
+ WARN_ON(1);
+ return;
+ }
+ kref_get(&set_data->kref);
+ set_data->ret = set_schib(cdev, set_data->mme, set_data->mbfc,
+ set_data->address);
+ wake_up(&set_data->wait);
+ kref_put(&set_data->kref, cmf_set_schib_release);
+}
+
+static int cmf_copy_block(struct ccw_device *cdev)
+{
+ struct subchannel *sch;
+ void *reference_buf;
+ void *hw_block;
+ struct cmb_data *cmb_data;
+
+ sch = to_subchannel(cdev->dev.parent);
+
+ if (stsch(sch->schid, &sch->schib))
+ return -ENODEV;
+
+ if (sch->schib.scsw.fctl & SCSW_FCTL_START_FUNC) {
+ /* Don't copy if a start function is in progress. */
+ if ((!sch->schib.scsw.actl & SCSW_ACTL_SUSPENDED) &&
+ (sch->schib.scsw.actl &
+ (SCSW_ACTL_DEVACT | SCSW_ACTL_SCHACT)) &&
+ (!sch->schib.scsw.stctl & SCSW_STCTL_SEC_STATUS))
+ return -EBUSY;
+ }
+ cmb_data = cdev->private->cmb;
+ hw_block = cmbops->align(cmb_data->hw_block);
+ if (!memcmp(cmb_data->last_block, hw_block, cmb_data->size))
+ /* No need to copy. */
+ return 0;
+ reference_buf = kzalloc(cmb_data->size, GFP_ATOMIC);
+ if (!reference_buf)
+ return -ENOMEM;
+ /* Ensure consistency of block copied from hardware. */
+ do {
+ memcpy(cmb_data->last_block, hw_block, cmb_data->size);
+ memcpy(reference_buf, hw_block, cmb_data->size);
+ } while (memcmp(cmb_data->last_block, reference_buf, cmb_data->size));
+ cmb_data->last_update = get_clock();
+ kfree(reference_buf);
+ return 0;
+}
+
+struct copy_block_struct {
+ wait_queue_head_t wait;
+ int ret;
+ struct kref kref;
+};
+
+static void cmf_copy_block_release(struct kref *kref)
+{
+ struct copy_block_struct *copy_block;
+
+ copy_block = container_of(kref, struct copy_block_struct, kref);
+ kfree(copy_block);
+}
+
+static int cmf_cmb_copy_wait(struct ccw_device *cdev)
+{
+ struct copy_block_struct *copy_block;
+ int ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ ret = -ENODEV;
+ goto out;
+ }
+ copy_block = kzalloc(sizeof(struct copy_block_struct), GFP_ATOMIC);
+ if (!copy_block) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ init_waitqueue_head(&copy_block->wait);
+ kref_init(&copy_block->kref);
+
+ ret = cmf_copy_block(cdev);
+ if (ret != -EBUSY)
+ goto out_put;
+
+ if (cdev->private->state != DEV_STATE_ONLINE) {
+ ret = -EBUSY;
+ goto out_put;
+ }
+
+ cdev->private->state = DEV_STATE_CMFUPDATE;
+ copy_block->ret = CMF_PENDING;
+ cdev->private->cmb_wait = copy_block;
+
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (wait_event_interruptible(copy_block->wait,
+ copy_block->ret != CMF_PENDING)) {
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (copy_block->ret == CMF_PENDING) {
+ copy_block->ret = -ERESTARTSYS;
+ if (cdev->private->state == DEV_STATE_CMFUPDATE)
+ cdev->private->state = DEV_STATE_ONLINE;
+ }
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ }
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ cdev->private->cmb_wait = NULL;
+ ret = copy_block->ret;
+out_put:
+ kref_put(&copy_block->kref, cmf_copy_block_release);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
+}
+
+void cmf_retry_copy_block(struct ccw_device *cdev)
+{
+ struct copy_block_struct *copy_block;
- s = cdev->private->cmb_wait;
- cdev->private->cmb_wait = 0;
- if (!s) {
+ copy_block = cdev->private->cmb_wait;
+ if (!copy_block) {
WARN_ON(1);
return;
}
- s->ret = set_schib(cdev, s->mme, s->mbfc, s->address);
- wake_up(&s->wait);
+ kref_get(&copy_block->kref);
+ copy_block->ret = cmf_copy_block(cdev);
+ wake_up(&copy_block->wait);
+ kref_put(&copy_block->kref, cmf_copy_block_release);
+}
+
+static void cmf_generic_reset(struct ccw_device *cdev)
+{
+ struct cmb_data *cmb_data;
+
+ spin_lock_irq(cdev->ccwlock);
+ cmb_data = cdev->private->cmb;
+ if (cmb_data) {
+ memset(cmb_data->last_block, 0, cmb_data->size);
+ /*
+ * Need to reset hw block as well to make the hardware start
+ * from 0 again.
+ */
+ memset(cmbops->align(cmb_data->hw_block), 0, cmb_data->size);
+ cmb_data->last_update = 0;
+ }
+ cdev->private->cmb_start_time = get_clock();
+ spin_unlock_irq(cdev->ccwlock);
}
/**
@@ -343,8 +519,8 @@ struct cmb {
/* insert a single device into the cmb_area list
* called with cmb_area.lock held from alloc_cmb
*/
-static inline int
-alloc_cmb_single (struct ccw_device *cdev)
+static inline int alloc_cmb_single (struct ccw_device *cdev,
+ struct cmb_data *cmb_data)
{
struct cmb *cmb;
struct ccw_device_private *node;
@@ -358,10 +534,12 @@ alloc_cmb_single (struct ccw_device *cdev)
/* find first unused cmb in cmb_area.mem.
* this is a little tricky: cmb_area.list
- * remains sorted by ->cmb pointers */
+ * remains sorted by ->cmb->hw_data pointers */
cmb = cmb_area.mem;
list_for_each_entry(node, &cmb_area.list, cmb_list) {
- if ((struct cmb*)node->cmb > cmb)
+ struct cmb_data *data;
+ data = node->cmb;
+ if ((struct cmb*)data->hw_block > cmb)
break;
cmb++;
}
@@ -372,7 +550,8 @@ alloc_cmb_single (struct ccw_device *cdev)
/* insert new cmb */
list_add_tail(&cdev->private->cmb_list, &node->cmb_list);
- cdev->private->cmb = cmb;
+ cmb_data->hw_block = cmb;
+ cdev->private->cmb = cmb_data;
ret = 0;
out:
spin_unlock_irq(cdev->ccwlock);
@@ -385,7 +564,19 @@ alloc_cmb (struct ccw_device *cdev)
int ret;
struct cmb *mem;
ssize_t size;
+ struct cmb_data *cmb_data;
+
+ /* Allocate private cmb_data. */
+ cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
+ if (!cmb_data)
+ return -ENOMEM;
+ cmb_data->last_block = kzalloc(sizeof(struct cmb), GFP_KERNEL);
+ if (!cmb_data->last_block) {
+ kfree(cmb_data);
+ return -ENOMEM;
+ }
+ cmb_data->size = sizeof(struct cmb);
spin_lock(&cmb_area.lock);
if (!cmb_area.mem) {
@@ -414,29 +605,36 @@ alloc_cmb (struct ccw_device *cdev)
}
/* do the actual allocation */
- ret = alloc_cmb_single(cdev);
+ ret = alloc_cmb_single(cdev, cmb_data);
out:
spin_unlock(&cmb_area.lock);
-
+ if (ret) {
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
+ }
return ret;
}
-static void
-free_cmb(struct ccw_device *cdev)
+static void free_cmb(struct ccw_device *cdev)
{
struct ccw_device_private *priv;
-
- priv = cdev->private;
+ struct cmb_data *cmb_data;
spin_lock(&cmb_area.lock);
spin_lock_irq(cdev->ccwlock);
+ priv = cdev->private;
+
if (list_empty(&priv->cmb_list)) {
/* already freed */
goto out;
}
+ cmb_data = priv->cmb;
priv->cmb = NULL;
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
list_del_init(&priv->cmb_list);
if (list_empty(&cmb_area.list)) {
@@ -451,83 +649,97 @@ out:
spin_unlock(&cmb_area.lock);
}
-static int
-set_cmb(struct ccw_device *cdev, u32 mme)
+static int set_cmb(struct ccw_device *cdev, u32 mme)
{
u16 offset;
+ struct cmb_data *cmb_data;
+ unsigned long flags;
- if (!cdev->private->cmb)
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return -EINVAL;
-
- offset = mme ? (struct cmb *)cdev->private->cmb - cmb_area.mem : 0;
+ }
+ cmb_data = cdev->private->cmb;
+ offset = mme ? (struct cmb *)cmb_data->hw_block - cmb_area.mem : 0;
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 0, offset);
}
-static u64
-read_cmb (struct ccw_device *cdev, int index)
+static u64 read_cmb (struct ccw_device *cdev, int index)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmb cmb;
- unsigned long flags;
+ struct cmb *cmb;
u32 val;
+ int ret;
+ unsigned long flags;
+
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return 0;
spin_lock_irqsave(cdev->ccwlock, flags);
if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return 0;
+ ret = 0;
+ goto out;
}
-
- cmb = *(struct cmb*)cdev->private->cmb;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ cmb = ((struct cmb_data *)cdev->private->cmb)->last_block;
switch (index) {
case cmb_ssch_rsch_count:
- return cmb.ssch_rsch_count;
+ ret = cmb->ssch_rsch_count;
+ goto out;
case cmb_sample_count:
- return cmb.sample_count;
+ ret = cmb->sample_count;
+ goto out;
case cmb_device_connect_time:
- val = cmb.device_connect_time;
+ val = cmb->device_connect_time;
break;
case cmb_function_pending_time:
- val = cmb.function_pending_time;
+ val = cmb->function_pending_time;
break;
case cmb_device_disconnect_time:
- val = cmb.device_disconnect_time;
+ val = cmb->device_disconnect_time;
break;
case cmb_control_unit_queuing_time:
- val = cmb.control_unit_queuing_time;
+ val = cmb->control_unit_queuing_time;
break;
case cmb_device_active_only_time:
- val = cmb.device_active_only_time;
+ val = cmb->device_active_only_time;
break;
default:
- return 0;
+ ret = 0;
+ goto out;
}
- return time_to_avg_nsec(val, cmb.sample_count);
+ ret = time_to_avg_nsec(val, cmb->sample_count);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static int
-readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
+static int readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmb cmb;
- unsigned long flags;
+ struct cmb *cmb;
+ struct cmb_data *cmb_data;
u64 time;
+ unsigned long flags;
+ int ret;
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return ret;
spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return -ENODEV;
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = -ENODEV;
+ goto out;
}
-
- cmb = *(struct cmb*)cdev->private->cmb;
- time = get_clock() - cdev->private->cmb_start_time;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (cmb_data->last_update == 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ cmb = cmb_data->last_block;
+ time = cmb_data->last_update - cdev->private->cmb_start_time;
memset(data, 0, sizeof(struct cmbdata));
@@ -538,31 +750,32 @@ readall_cmb (struct ccw_device *cdev, struct cmbdata *data)
data->elapsed_time = (time * 1000) >> 12;
/* copy data to new structure */
- data->ssch_rsch_count = cmb.ssch_rsch_count;
- data->sample_count = cmb.sample_count;
+ data->ssch_rsch_count = cmb->ssch_rsch_count;
+ data->sample_count = cmb->sample_count;
/* time fields are converted to nanoseconds while copying */
- data->device_connect_time = time_to_nsec(cmb.device_connect_time);
- data->function_pending_time = time_to_nsec(cmb.function_pending_time);
- data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
+ data->device_connect_time = time_to_nsec(cmb->device_connect_time);
+ data->function_pending_time = time_to_nsec(cmb->function_pending_time);
+ data->device_disconnect_time =
+ time_to_nsec(cmb->device_disconnect_time);
data->control_unit_queuing_time
- = time_to_nsec(cmb.control_unit_queuing_time);
+ = time_to_nsec(cmb->control_unit_queuing_time);
data->device_active_only_time
- = time_to_nsec(cmb.device_active_only_time);
+ = time_to_nsec(cmb->device_active_only_time);
+ ret = 0;
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
+}
- return 0;
+static void reset_cmb(struct ccw_device *cdev)
+{
+ cmf_generic_reset(cdev);
}
-static void
-reset_cmb(struct ccw_device *cdev)
+static void * align_cmb(void *area)
{
- struct cmb *cmb;
- spin_lock_irq(cdev->ccwlock);
- cmb = cdev->private->cmb;
- if (cmb)
- memset (cmb, 0, sizeof (*cmb));
- cdev->private->cmb_start_time = get_clock();
- spin_unlock_irq(cdev->ccwlock);
+ return area;
}
static struct attribute_group cmf_attr_group;
@@ -574,6 +787,7 @@ static struct cmb_operations cmbops_basic = {
.read = read_cmb,
.readall = readall_cmb,
.reset = reset_cmb,
+ .align = align_cmb,
.attr_group = &cmf_attr_group,
};
@@ -610,22 +824,34 @@ static inline struct cmbe* cmbe_align(struct cmbe *c)
return (struct cmbe*)addr;
}
-static int
-alloc_cmbe (struct ccw_device *cdev)
+static int alloc_cmbe (struct ccw_device *cdev)
{
struct cmbe *cmbe;
- cmbe = kmalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
+ struct cmb_data *cmb_data;
+ int ret;
+
+ cmbe = kzalloc (sizeof (*cmbe) * 2, GFP_KERNEL);
if (!cmbe)
return -ENOMEM;
-
+ cmb_data = kzalloc(sizeof(struct cmb_data), GFP_KERNEL);
+ if (!cmb_data) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ cmb_data->last_block = kzalloc(sizeof(struct cmbe), GFP_KERNEL);
+ if (!cmb_data->last_block) {
+ ret = -ENOMEM;
+ goto out_free;
+ }
+ cmb_data->size = sizeof(struct cmbe);
spin_lock_irq(cdev->ccwlock);
if (cdev->private->cmb) {
- kfree(cmbe);
spin_unlock_irq(cdev->ccwlock);
- return -EBUSY;
+ ret = -EBUSY;
+ goto out_free;
}
-
- cdev->private->cmb = cmbe;
+ cmb_data->hw_block = cmbe;
+ cdev->private->cmb = cmb_data;
spin_unlock_irq(cdev->ccwlock);
/* activate global measurement if this is the first channel */
@@ -636,14 +862,24 @@ alloc_cmbe (struct ccw_device *cdev)
spin_unlock(&cmb_area.lock);
return 0;
+out_free:
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
+ kfree(cmbe);
+ return ret;
}
-static void
-free_cmbe (struct ccw_device *cdev)
+static void free_cmbe (struct ccw_device *cdev)
{
+ struct cmb_data *cmb_data;
+
spin_lock_irq(cdev->ccwlock);
- kfree(cdev->private->cmb);
+ cmb_data = cdev->private->cmb;
cdev->private->cmb = NULL;
+ if (cmb_data)
+ kfree(cmb_data->last_block);
+ kfree(cmb_data);
spin_unlock_irq(cdev->ccwlock);
/* deactivate global measurement if this is the last channel */
@@ -654,89 +890,105 @@ free_cmbe (struct ccw_device *cdev)
spin_unlock(&cmb_area.lock);
}
-static int
-set_cmbe(struct ccw_device *cdev, u32 mme)
+static int set_cmbe(struct ccw_device *cdev, u32 mme)
{
unsigned long mba;
+ struct cmb_data *cmb_data;
+ unsigned long flags;
- if (!cdev->private->cmb)
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ if (!cdev->private->cmb) {
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return -EINVAL;
- mba = mme ? (unsigned long) cmbe_align(cdev->private->cmb) : 0;
+ }
+ cmb_data = cdev->private->cmb;
+ mba = mme ? (unsigned long) cmbe_align(cmb_data->hw_block) : 0;
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
return set_schib_wait(cdev, mme, 1, mba);
}
-u64
-read_cmbe (struct ccw_device *cdev, int index)
+static u64 read_cmbe (struct ccw_device *cdev, int index)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmbe cmb;
- unsigned long flags;
+ struct cmbe *cmb;
+ struct cmb_data *cmb_data;
u32 val;
+ int ret;
+ unsigned long flags;
- spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
return 0;
- }
- cmb = *cmbe_align(cdev->private->cmb);
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ spin_lock_irqsave(cdev->ccwlock, flags);
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = 0;
+ goto out;
+ }
+ cmb = cmb_data->last_block;
switch (index) {
case cmb_ssch_rsch_count:
- return cmb.ssch_rsch_count;
+ ret = cmb->ssch_rsch_count;
+ goto out;
case cmb_sample_count:
- return cmb.sample_count;
+ ret = cmb->sample_count;
+ goto out;
case cmb_device_connect_time:
- val = cmb.device_connect_time;
+ val = cmb->device_connect_time;
break;
case cmb_function_pending_time:
- val = cmb.function_pending_time;
+ val = cmb->function_pending_time;
break;
case cmb_device_disconnect_time:
- val = cmb.device_disconnect_time;
+ val = cmb->device_disconnect_time;
break;
case cmb_control_unit_queuing_time:
- val = cmb.control_unit_queuing_time;
+ val = cmb->control_unit_queuing_time;
break;
case cmb_device_active_only_time:
- val = cmb.device_active_only_time;
+ val = cmb->device_active_only_time;
break;
case cmb_device_busy_time:
- val = cmb.device_busy_time;
+ val = cmb->device_busy_time;
break;
case cmb_initial_command_response_time:
- val = cmb.initial_command_response_time;
+ val = cmb->initial_command_response_time;
break;
default:
- return 0;
+ ret = 0;
+ goto out;
}
- return time_to_avg_nsec(val, cmb.sample_count);
+ ret = time_to_avg_nsec(val, cmb->sample_count);
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static int
-readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
+static int readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
{
- /* yes, we have to put it on the stack
- * because the cmb must only be accessed
- * atomically, e.g. with mvc */
- struct cmbe cmb;
- unsigned long flags;
+ struct cmbe *cmb;
+ struct cmb_data *cmb_data;
u64 time;
+ unsigned long flags;
+ int ret;
+ ret = cmf_cmb_copy_wait(cdev);
+ if (ret < 0)
+ return ret;
spin_lock_irqsave(cdev->ccwlock, flags);
- if (!cdev->private->cmb) {
- spin_unlock_irqrestore(cdev->ccwlock, flags);
- return -ENODEV;
+ cmb_data = cdev->private->cmb;
+ if (!cmb_data) {
+ ret = -ENODEV;
+ goto out;
}
-
- cmb = *cmbe_align(cdev->private->cmb);
- time = get_clock() - cdev->private->cmb_start_time;
- spin_unlock_irqrestore(cdev->ccwlock, flags);
+ if (cmb_data->last_update == 0) {
+ ret = -EAGAIN;
+ goto out;
+ }
+ time = cmb_data->last_update - cdev->private->cmb_start_time;
memset (data, 0, sizeof(struct cmbdata));
@@ -746,35 +998,38 @@ readall_cmbe (struct ccw_device *cdev, struct cmbdata *data)
/* conver to nanoseconds */
data->elapsed_time = (time * 1000) >> 12;
+ cmb = cmb_data->last_block;
/* copy data to new structure */
- data->ssch_rsch_count = cmb.ssch_rsch_count;
- data->sample_count = cmb.sample_count;
+ data->ssch_rsch_count = cmb->ssch_rsch_count;
+ data->sample_count = cmb->sample_count;
/* time fields are converted to nanoseconds while copying */
- data->device_connect_time = time_to_nsec(cmb.device_connect_time);
- data->function_pending_time = time_to_nsec(cmb.function_pending_time);
- data->device_disconnect_time = time_to_nsec(cmb.device_disconnect_time);
+ data->device_connect_time = time_to_nsec(cmb->device_connect_time);
+ data->function_pending_time = time_to_nsec(cmb->function_pending_time);
+ data->device_disconnect_time =
+ time_to_nsec(cmb->device_disconnect_time);
data->control_unit_queuing_time
- = time_to_nsec(cmb.control_unit_queuing_time);
+ = time_to_nsec(cmb->control_unit_queuing_time);
data->device_active_only_time
- = time_to_nsec(cmb.device_active_only_time);
- data->device_busy_time = time_to_nsec(cmb.device_busy_time);
+ = time_to_nsec(cmb->device_active_only_time);
+ data->device_busy_time = time_to_nsec(cmb->device_busy_time);
data->initial_command_response_time
- = time_to_nsec(cmb.initial_command_response_time);
+ = time_to_nsec(cmb->initial_command_response_time);
- return 0;
+ ret = 0;
+out:
+ spin_unlock_irqrestore(cdev->ccwlock, flags);
+ return ret;
}
-static void
-reset_cmbe(struct ccw_device *cdev)
+static void reset_cmbe(struct ccw_device *cdev)
{
- struct cmbe *cmb;
- spin_lock_irq(cdev->ccwlock);
- cmb = cmbe_align(cdev->private->cmb);
- if (cmb)
- memset (cmb, 0, sizeof (*cmb));
- cdev->private->cmb_start_time = get_clock();
- spin_unlock_irq(cdev->ccwlock);
+ cmf_generic_reset(cdev);
+}
+
+static void * align_cmbe(void *area)
+{
+ return cmbe_align(area);
}
static struct attribute_group cmf_attr_group_ext;
@@ -786,6 +1041,7 @@ static struct cmb_operations cmbops_extended = {
.read = read_cmbe,
.readall = readall_cmbe,
.reset = reset_cmbe,
+ .align = align_cmbe,
.attr_group = &cmf_attr_group_ext,
};
@@ -803,14 +1059,19 @@ cmb_show_avg_sample_interval(struct device *dev, struct device_attribute *attr,
struct ccw_device *cdev;
long interval;
unsigned long count;
+ struct cmb_data *cmb_data;
cdev = to_ccwdev(dev);
- interval = get_clock() - cdev->private->cmb_start_time;
count = cmf_read(cdev, cmb_sample_count);
- if (count)
+ spin_lock_irq(cdev->ccwlock);
+ cmb_data = cdev->private->cmb;
+ if (count) {
+ interval = cmb_data->last_update -
+ cdev->private->cmb_start_time;
interval /= count;
- else
+ } else
interval = -1;
+ spin_unlock_irq(cdev->ccwlock);
return sprintf(buf, "%ld\n", interval);
}
@@ -823,7 +1084,10 @@ cmb_show_avg_utilization(struct device *dev, struct device_attribute *attr, char
int ret;
ret = cmf_readall(to_ccwdev(dev), &data);
- if (ret)
+ if (ret == -EAGAIN || ret == -ENODEV)
+ /* No data (yet/currently) available to use for calculation. */
+ return sprintf(buf, "n/a\n");
+ else if (ret)
return ret;
utilization = data.device_connect_time +
@@ -982,6 +1246,13 @@ cmf_readall(struct ccw_device *cdev, struct cmbdata *data)
return cmbops->readall(cdev, data);
}
+/* Reenable cmf when a disconnected device becomes available again. */
+int cmf_reenable(struct ccw_device *cdev)
+{
+ cmbops->reset(cdev);
+ return cmbops->set(cdev, 2);
+}
+
static int __init
init_cmf(void)
{
diff --git a/drivers/s390/cio/css.c b/drivers/s390/cio/css.c
index 74ea8aac4b7d..1d3be80797f8 100644
--- a/drivers/s390/cio/css.c
+++ b/drivers/s390/cio/css.c
@@ -19,9 +19,11 @@
#include "cio_debug.h"
#include "ioasm.h"
#include "chsc.h"
+#include "device.h"
int need_rescan = 0;
int css_init_done = 0;
+static int need_reprobe = 0;
static int max_ssid = 0;
struct channel_subsystem *css[__MAX_CSSID + 1];
@@ -339,6 +341,67 @@ typedef void (*workfunc)(void *);
DECLARE_WORK(slow_path_work, (workfunc)css_trigger_slow_path, NULL);
struct workqueue_struct *slow_path_wq;
+/* Reprobe subchannel if unregistered. */
+static int reprobe_subchannel(struct subchannel_id schid, void *data)
+{
+ struct subchannel *sch;
+ int ret;
+
+ CIO_DEBUG(KERN_INFO, 6, "cio: reprobe 0.%x.%04x\n",
+ schid.ssid, schid.sch_no);
+ if (need_reprobe)
+ return -EAGAIN;
+
+ sch = get_subchannel_by_schid(schid);
+ if (sch) {
+ /* Already known. */
+ put_device(&sch->dev);
+ return 0;
+ }
+
+ ret = css_probe_device(schid);
+ switch (ret) {
+ case 0:
+ break;
+ case -ENXIO:
+ case -ENOMEM:
+ /* These should abort looping */
+ break;
+ default:
+ ret = 0;
+ }
+
+ return ret;
+}
+
+/* Work function used to reprobe all unregistered subchannels. */
+static void reprobe_all(void *data)
+{
+ int ret;
+
+ CIO_MSG_EVENT(2, "reprobe start\n");
+
+ need_reprobe = 0;
+ /* Make sure initial subchannel scan is done. */
+ wait_event(ccw_device_init_wq,
+ atomic_read(&ccw_device_init_count) == 0);
+ ret = for_each_subchannel(reprobe_subchannel, NULL);
+
+ CIO_MSG_EVENT(2, "reprobe done (rc=%d, need_reprobe=%d)\n", ret,
+ need_reprobe);
+}
+
+DECLARE_WORK(css_reprobe_work, reprobe_all, NULL);
+
+/* Schedule reprobing of all unregistered subchannels. */
+void css_schedule_reprobe(void)
+{
+ need_reprobe = 1;
+ queue_work(ccw_device_work, &css_reprobe_work);
+}
+
+EXPORT_SYMBOL_GPL(css_schedule_reprobe);
+
/*
* Rescan for new devices. FIXME: This is slow.
* This function is called when we have lost CRWs due to overflows and we have
diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c
index 8e3053c2a451..eafde43e8410 100644
--- a/drivers/s390/cio/device.c
+++ b/drivers/s390/cio/device.c
@@ -133,8 +133,8 @@ struct css_driver io_subchannel_driver = {
struct workqueue_struct *ccw_device_work;
struct workqueue_struct *ccw_device_notify_work;
-static wait_queue_head_t ccw_device_init_wq;
-static atomic_t ccw_device_init_count;
+wait_queue_head_t ccw_device_init_wq;
+atomic_t ccw_device_init_count;
static int __init
init_ccw_bus_type (void)
diff --git a/drivers/s390/cio/device.h b/drivers/s390/cio/device.h
index 11587ebb7289..00be9a5b4acd 100644
--- a/drivers/s390/cio/device.h
+++ b/drivers/s390/cio/device.h
@@ -1,6 +1,10 @@
#ifndef S390_DEVICE_H
#define S390_DEVICE_H
+#include <asm/ccwdev.h>
+#include <asm/atomic.h>
+#include <linux/wait.h>
+
/*
* states of the device statemachine
*/
@@ -23,6 +27,7 @@ enum dev_state {
DEV_STATE_DISCONNECTED,
DEV_STATE_DISCONNECTED_SENSE_ID,
DEV_STATE_CMFCHANGE,
+ DEV_STATE_CMFUPDATE,
/* last element! */
NR_DEV_STATES
};
@@ -67,6 +72,8 @@ dev_fsm_final_state(struct ccw_device *cdev)
extern struct workqueue_struct *ccw_device_work;
extern struct workqueue_struct *ccw_device_notify_work;
+extern wait_queue_head_t ccw_device_init_wq;
+extern atomic_t ccw_device_init_count;
void io_subchannel_recog_done(struct ccw_device *cdev);
@@ -112,5 +119,8 @@ int ccw_device_stlck(struct ccw_device *);
void ccw_device_set_timeout(struct ccw_device *, int);
extern struct subchannel_id ccw_device_get_subchannel_id(struct ccw_device *);
+/* Channel measurement facility related */
void retry_set_schib(struct ccw_device *cdev);
+void cmf_retry_copy_block(struct ccw_device *);
+int cmf_reenable(struct ccw_device *);
#endif
diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c
index 49ec562d7f60..7d0dd72635eb 100644
--- a/drivers/s390/cio/device_fsm.c
+++ b/drivers/s390/cio/device_fsm.c
@@ -336,8 +336,11 @@ ccw_device_oper_notify(void *data)
if (!ret)
/* Driver doesn't want device back. */
ccw_device_do_unreg_rereg((void *)cdev);
- else
+ else {
+ /* Reenable channel measurements, if needed. */
+ cmf_reenable(cdev);
wake_up(&cdev->private->wait_q);
+ }
}
/*
@@ -861,6 +864,8 @@ ccw_device_clear_verify(struct ccw_device *cdev, enum dev_event dev_event)
irb = (struct irb *) __LC_IRB;
/* Accumulate status. We don't do basic sense. */
ccw_device_accumulate_irb(cdev, irb);
+ /* Remember to clear irb to avoid residuals. */
+ memset(&cdev->private->irb, 0, sizeof(struct irb));
/* Try to start delayed device verification. */
ccw_device_online_verify(cdev, 0);
/* Note: Don't call handler for cio initiated clear! */
@@ -1093,6 +1098,13 @@ ccw_device_change_cmfstate(struct ccw_device *cdev, enum dev_event dev_event)
dev_fsm_event(cdev, dev_event);
}
+static void ccw_device_update_cmfblock(struct ccw_device *cdev,
+ enum dev_event dev_event)
+{
+ cmf_retry_copy_block(cdev);
+ cdev->private->state = DEV_STATE_ONLINE;
+ dev_fsm_event(cdev, dev_event);
+}
static void
ccw_device_quiesce_done(struct ccw_device *cdev, enum dev_event dev_event)
@@ -1247,6 +1259,12 @@ fsm_func_t *dev_jumptable[NR_DEV_STATES][NR_DEV_EVENTS] = {
[DEV_EVENT_TIMEOUT] = ccw_device_change_cmfstate,
[DEV_EVENT_VERIFY] = ccw_device_change_cmfstate,
},
+ [DEV_STATE_CMFUPDATE] = {
+ [DEV_EVENT_NOTOPER] = ccw_device_update_cmfblock,
+ [DEV_EVENT_INTERRUPT] = ccw_device_update_cmfblock,
+ [DEV_EVENT_TIMEOUT] = ccw_device_update_cmfblock,
+ [DEV_EVENT_VERIFY] = ccw_device_update_cmfblock,
+ },
};
/*
diff --git a/drivers/s390/cio/device_ops.c b/drivers/s390/cio/device_ops.c
index 795abb5a65ba..b266ad8e14ff 100644
--- a/drivers/s390/cio/device_ops.c
+++ b/drivers/s390/cio/device_ops.c
@@ -78,7 +78,8 @@ ccw_device_start_key(struct ccw_device *cdev, struct ccw1 *cpa,
return -ENODEV;
if (cdev->private->state == DEV_STATE_NOT_OPER)
return -ENODEV;
- if (cdev->private->state == DEV_STATE_VERIFY) {
+ if (cdev->private->state == DEV_STATE_VERIFY ||
+ cdev->private->state == DEV_STATE_CLEAR_VERIFY) {
/* Remember to fake irb when finished. */
if (!cdev->private->flags.fake_irb) {
cdev->private->flags.fake_irb = 1;
@@ -270,7 +271,8 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
* We didn't get channel end / device end. Check if path
* verification has been started; we can retry after it has
* finished. We also retry unit checks except for command reject
- * or intervention required.
+ * or intervention required. Also check for long busy
+ * conditions.
*/
if (cdev->private->flags.doverify ||
cdev->private->state == DEV_STATE_VERIFY)
@@ -279,6 +281,10 @@ ccw_device_wake_up(struct ccw_device *cdev, unsigned long ip, struct irb *irb)
!(irb->ecw[0] &
(SNS0_CMD_REJECT | SNS0_INTERVENTION_REQ)))
cdev->private->intparm = -EAGAIN;
+ else if ((irb->scsw.dstat & DEV_STAT_ATTENTION) &&
+ (irb->scsw.dstat & DEV_STAT_DEV_END) &&
+ (irb->scsw.dstat & DEV_STAT_UNIT_EXCEP))
+ cdev->private->intparm = -EAGAIN;
else
cdev->private->intparm = -EIO;
diff --git a/drivers/s390/net/lcs.c b/drivers/s390/net/lcs.c
index f94419b334f7..2eded55ae88d 100644
--- a/drivers/s390/net/lcs.c
+++ b/drivers/s390/net/lcs.c
@@ -1140,10 +1140,9 @@ list_modified:
}
}
/* re-insert all entries from the failed_list into ipm_list */
- list_for_each_entry_safe(ipm, tmp, &failed_list, list) {
- list_del_init(&ipm->list);
- list_add_tail(&ipm->list, &card->ipm_list);
- }
+ list_for_each_entry_safe(ipm, tmp, &failed_list, list)
+ list_move_tail(&ipm->list, &card->ipm_list);
+
spin_unlock_irqrestore(&card->ipm_lock, flags);
}
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index f99e55308b32..8dc75002acbe 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -14,6 +14,7 @@
#include <linux/errno.h>
#include <linux/workqueue.h>
#include <linux/time.h>
+#include <linux/kthread.h>
#include <asm/lowcore.h>
@@ -56,8 +57,6 @@ s390_collect_crw_info(void *param)
unsigned int chain;
sem = (struct semaphore *)param;
- /* Set a nice name. */
- daemonize("kmcheck");
repeat:
down_interruptible(sem);
slow = 0;
@@ -516,7 +515,7 @@ arch_initcall(machine_check_init);
static int __init
machine_check_crw_init (void)
{
- kernel_thread(s390_collect_crw_info, &m_sem, CLONE_FS|CLONE_FILES);
+ kthread_run(s390_collect_crw_info, &m_sem, "kmcheck");
ctl_set_bit(14, 28); /* enable channel report MCH */
return 0;
}
diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c
index 4682c8b8bd24..909731b99d26 100644
--- a/drivers/s390/scsi/zfcp_erp.c
+++ b/drivers/s390/scsi/zfcp_erp.c
@@ -167,7 +167,7 @@ zfcp_fsf_scsi_er_timeout_handler(unsigned long data)
* initiates adapter recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -203,7 +203,7 @@ zfcp_erp_adapter_reopen_internal(struct zfcp_adapter *adapter, int clear_mask)
* purpose: Wrappper for zfcp_erp_adapter_reopen_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -469,7 +469,7 @@ zfcp_test_link(struct zfcp_port *port)
* initiates Forced Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -509,7 +509,7 @@ zfcp_erp_port_forced_reopen_internal(struct zfcp_port *port, int clear_mask)
* purpose: Wrappper for zfcp_erp_port_forced_reopen_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
@@ -536,7 +536,7 @@ zfcp_erp_port_forced_reopen(struct zfcp_port *port, int clear_mask)
* initiates Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -605,7 +605,7 @@ zfcp_erp_port_reopen(struct zfcp_port *port, int clear_mask)
* initiates Reopen recovery which is done
* asynchronously
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
static int
@@ -1805,7 +1805,7 @@ zfcp_erp_modify_unit_status(struct zfcp_unit *unit, u32 mask, int set_or_clear)
* purpose: Wrappper for zfcp_erp_port_reopen_all_internal
* used to ensure the correct locking
*
- * returns: 0 - initiated action succesfully
+ * returns: 0 - initiated action successfully
* <0 - failed to initiate action
*/
int
diff --git a/drivers/sbus/char/cpwatchdog.c b/drivers/sbus/char/cpwatchdog.c
index 5bf3dd901b65..21737b7e86a1 100644
--- a/drivers/sbus/char/cpwatchdog.c
+++ b/drivers/sbus/char/cpwatchdog.c
@@ -755,7 +755,7 @@ static int __init wd_init(void)
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, WD_OBPNAME))
+ if (!strcmp(edev->ofdev.node->name, WD_OBPNAME))
goto ebus_done;
}
}
diff --git a/drivers/sbus/char/openprom.c b/drivers/sbus/char/openprom.c
index cf5b476b5496..d7e4bb41bd79 100644
--- a/drivers/sbus/char/openprom.c
+++ b/drivers/sbus/char/openprom.c
@@ -29,8 +29,6 @@
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#define PROMLIB_INTERNAL
-
#include <linux/config.h>
#include <linux/module.h>
#include <linux/kernel.h>
@@ -39,10 +37,10 @@
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/miscdevice.h>
-#include <linux/smp_lock.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/oplib.h>
+#include <asm/prom.h>
#include <asm/system.h>
#include <asm/uaccess.h>
#include <asm/openpromio.h>
@@ -51,15 +49,20 @@
#include <asm/pbm.h>
#endif
+MODULE_AUTHOR("Thomas K. Dyas (tdyas@noc.rutgers.edu) and Eddie C. Dost (ecd@skynet.be)");
+MODULE_DESCRIPTION("OPENPROM Configuration Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
+
/* Private data kept by the driver for each descriptor. */
typedef struct openprom_private_data
{
- int current_node; /* Current node for SunOS ioctls. */
- int lastnode; /* Last valid node used by BSD ioctls. */
+ struct device_node *current_node; /* Current node for SunOS ioctls. */
+ struct device_node *lastnode; /* Last valid node used by BSD ioctls. */
} DATA;
/* ID of the PROM node containing all of the EEPROM options. */
-static int options_node = 0;
+static struct device_node *options_node;
/*
* Copy an openpromio structure into kernel space from user space.
@@ -87,9 +90,8 @@ static int copyin(struct openpromio __user *info, struct openpromio **opp_p)
if (bufsize > OPROMMAXPARAM)
bufsize = OPROMMAXPARAM;
- if (!(*opp_p = kmalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
+ if (!(*opp_p = kzalloc(sizeof(int) + bufsize + 1, GFP_KERNEL)))
return -ENOMEM;
- memset(*opp_p, 0, sizeof(int) + bufsize + 1);
if (copy_from_user(&(*opp_p)->oprom_array,
&info->oprom_array, bufsize)) {
@@ -107,10 +109,9 @@ static int getstrings(struct openpromio __user *info, struct openpromio **opp_p)
if (!info || !opp_p)
return -EFAULT;
- if (!(*opp_p = kmalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
+ if (!(*opp_p = kzalloc(sizeof(int) + OPROMMAXPARAM + 1, GFP_KERNEL)))
return -ENOMEM;
- memset(*opp_p, 0, sizeof(int) + OPROMMAXPARAM + 1);
(*opp_p)->oprom_size = 0;
n = bufsize = 0;
@@ -140,16 +141,164 @@ static int copyout(void __user *info, struct openpromio *opp, int len)
return 0;
}
+static int opromgetprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
+{
+ void *pval;
+ int len;
+
+ pval = of_get_property(dp, op->oprom_array, &len);
+ if (!pval || len <= 0 || len > bufsize)
+ return copyout(argp, op, sizeof(int));
+
+ memcpy(op->oprom_array, pval, len);
+ op->oprom_array[len] = '\0';
+ op->oprom_size = len;
+
+ return copyout(argp, op, sizeof(int) + bufsize);
+}
+
+static int opromnxtprop(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize)
+{
+ struct property *prop;
+ int len;
+
+ if (op->oprom_array[0] == '\0') {
+ prop = dp->properties;
+ if (!prop)
+ return copyout(argp, op, sizeof(int));
+ len = strlen(prop->name);
+ } else {
+ prop = of_find_property(dp, op->oprom_array, NULL);
+
+ if (!prop ||
+ !prop->next ||
+ (len = strlen(prop->next->name)) + 1 > bufsize)
+ return copyout(argp, op, sizeof(int));
+
+ prop = prop->next;
+ }
+
+ memcpy(op->oprom_array, prop->name, len);
+ op->oprom_array[len] = '\0';
+ op->oprom_size = ++len;
+
+ return copyout(argp, op, sizeof(int) + bufsize);
+}
+
+static int opromsetopt(struct device_node *dp, struct openpromio *op, int bufsize)
+{
+ char *buf = op->oprom_array + strlen(op->oprom_array) + 1;
+ int len = op->oprom_array + bufsize - buf;
+
+ return of_set_property(options_node, op->oprom_array, buf, len);
+}
+
+static int opromnext(void __user *argp, unsigned int cmd, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
+{
+ phandle ph;
+
+ BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
+
+ if (bufsize < sizeof(phandle))
+ return -EINVAL;
+
+ ph = *((int *) op->oprom_array);
+ if (ph) {
+ dp = of_find_node_by_phandle(ph);
+ if (!dp)
+ return -EINVAL;
+
+ switch (cmd) {
+ case OPROMNEXT:
+ dp = dp->sibling;
+ break;
+
+ case OPROMCHILD:
+ dp = dp->child;
+ break;
+
+ case OPROMSETCUR:
+ default:
+ break;
+ };
+ } else {
+ /* Sibling of node zero is the root node. */
+ if (cmd != OPROMNEXT)
+ return -EINVAL;
+
+ dp = of_find_node_by_path("/");
+ }
+
+ ph = 0;
+ if (dp)
+ ph = dp->node;
+
+ data->current_node = dp;
+ *((int *) op->oprom_array) = ph;
+ op->oprom_size = sizeof(phandle);
+
+ return copyout(argp, op, bufsize + sizeof(int));
+}
+
+static int oprompci2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
+{
+ int err = -EINVAL;
+
+ if (bufsize >= 2*sizeof(int)) {
+#ifdef CONFIG_PCI
+ struct pci_dev *pdev;
+ struct pcidev_cookie *pcp;
+ pdev = pci_find_slot (((int *) op->oprom_array)[0],
+ ((int *) op->oprom_array)[1]);
+
+ pcp = pdev->sysdata;
+ if (pcp != NULL) {
+ dp = pcp->prom_node;
+ data->current_node = dp;
+ *((int *)op->oprom_array) = dp->node;
+ op->oprom_size = sizeof(int);
+ err = copyout(argp, op, bufsize + sizeof(int));
+ }
+#endif
+ }
+
+ return err;
+}
+
+static int oprompath2node(void __user *argp, struct device_node *dp, struct openpromio *op, int bufsize, DATA *data)
+{
+ dp = of_find_node_by_path(op->oprom_array);
+ data->current_node = dp;
+ *((int *)op->oprom_array) = dp->node;
+ op->oprom_size = sizeof(int);
+
+ return copyout(argp, op, bufsize + sizeof(int));
+}
+
+static int opromgetbootargs(void __user *argp, struct openpromio *op, int bufsize)
+{
+ char *buf = saved_command_line;
+ int len = strlen(buf);
+
+ if (len > bufsize)
+ return -EINVAL;
+
+ strcpy(op->oprom_array, buf);
+ op->oprom_size = len;
+
+ return copyout(argp, op, bufsize + sizeof(int));
+}
+
/*
* SunOS and Solaris /dev/openprom ioctl calls.
*/
static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg, int node)
+ unsigned int cmd, unsigned long arg,
+ struct device_node *dp)
{
- DATA *data = (DATA *) file->private_data;
- char buffer[OPROMMAXPARAM+1], *buf;
+ DATA *data = file->private_data;
struct openpromio *opp;
- int bufsize, len, error = 0;
+ int bufsize, error = 0;
static int cnt;
void __user *argp = (void __user *)arg;
@@ -164,119 +313,35 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
switch (cmd) {
case OPROMGETOPT:
case OPROMGETPROP:
- len = prom_getproplen(node, opp->oprom_array);
-
- if (len <= 0 || len > bufsize) {
- error = copyout(argp, opp, sizeof(int));
- break;
- }
-
- len = prom_getproperty(node, opp->oprom_array, buffer, bufsize);
-
- memcpy(opp->oprom_array, buffer, len);
- opp->oprom_array[len] = '\0';
- opp->oprom_size = len;
-
- error = copyout(argp, opp, sizeof(int) + bufsize);
+ error = opromgetprop(argp, dp, opp, bufsize);
break;
case OPROMNXTOPT:
case OPROMNXTPROP:
- buf = prom_nextprop(node, opp->oprom_array, buffer);
-
- len = strlen(buf);
- if (len == 0 || len + 1 > bufsize) {
- error = copyout(argp, opp, sizeof(int));
- break;
- }
-
- memcpy(opp->oprom_array, buf, len);
- opp->oprom_array[len] = '\0';
- opp->oprom_size = ++len;
-
- error = copyout(argp, opp, sizeof(int) + bufsize);
+ error = opromnxtprop(argp, dp, opp, bufsize);
break;
case OPROMSETOPT:
case OPROMSETOPT2:
- buf = opp->oprom_array + strlen(opp->oprom_array) + 1;
- len = opp->oprom_array + bufsize - buf;
-
- error = prom_setprop(options_node, opp->oprom_array,
- buf, len);
-
- if (error < 0)
- error = -EINVAL;
+ error = opromsetopt(dp, opp, bufsize);
break;
case OPROMNEXT:
case OPROMCHILD:
case OPROMSETCUR:
- if (bufsize < sizeof(int)) {
- error = -EINVAL;
- break;
- }
-
- node = *((int *) opp->oprom_array);
-
- switch (cmd) {
- case OPROMNEXT: node = __prom_getsibling(node); break;
- case OPROMCHILD: node = __prom_getchild(node); break;
- case OPROMSETCUR: break;
- }
-
- data->current_node = node;
- *((int *)opp->oprom_array) = node;
- opp->oprom_size = sizeof(int);
-
- error = copyout(argp, opp, bufsize + sizeof(int));
+ error = opromnext(argp, cmd, dp, opp, bufsize, data);
break;
case OPROMPCI2NODE:
- error = -EINVAL;
-
- if (bufsize >= 2*sizeof(int)) {
-#ifdef CONFIG_PCI
- struct pci_dev *pdev;
- struct pcidev_cookie *pcp;
- pdev = pci_find_slot (((int *) opp->oprom_array)[0],
- ((int *) opp->oprom_array)[1]);
-
- pcp = pdev->sysdata;
- if (pcp != NULL) {
- node = pcp->prom_node->node;
- data->current_node = node;
- *((int *)opp->oprom_array) = node;
- opp->oprom_size = sizeof(int);
- error = copyout(argp, opp, bufsize + sizeof(int));
- }
-#endif
- }
+ error = oprompci2node(argp, dp, opp, bufsize, data);
break;
case OPROMPATH2NODE:
- node = prom_finddevice(opp->oprom_array);
- data->current_node = node;
- *((int *)opp->oprom_array) = node;
- opp->oprom_size = sizeof(int);
-
- error = copyout(argp, opp, bufsize + sizeof(int));
+ error = oprompath2node(argp, dp, opp, bufsize, data);
break;
case OPROMGETBOOTARGS:
- buf = saved_command_line;
-
- len = strlen(buf);
-
- if (len > bufsize) {
- error = -EINVAL;
- break;
- }
-
- strcpy(opp->oprom_array, buf);
- opp->oprom_size = len;
-
- error = copyout(argp, opp, bufsize + sizeof(int));
+ error = opromgetbootargs(argp, opp, bufsize);
break;
case OPROMU2P:
@@ -297,25 +362,14 @@ static int openprom_sunos_ioctl(struct inode * inode, struct file * file,
return error;
}
-
-/* Return nonzero if a specific node is in the PROM device tree. */
-static int intree(int root, int node)
+static struct device_node *get_node(phandle n, DATA *data)
{
- for (; root != 0; root = prom_getsibling(root))
- if (root == node || intree(prom_getchild(root),node))
- return 1;
- return 0;
-}
+ struct device_node *dp = of_find_node_by_phandle(n);
-/* Return nonzero if a specific node is "valid". */
-static int goodnode(int n, DATA *data)
-{
- if (n == data->lastnode || n == prom_root_node || n == options_node)
- return 1;
- if (n == 0 || n == -1 || !intree(prom_root_node,n))
- return 0;
- data->lastnode = n;
- return 1;
+ if (dp)
+ data->lastnode = dp;
+
+ return dp;
}
/* Copy in a whole string from userspace into kernelspace. */
@@ -330,7 +384,7 @@ static int copyin_string(char __user *user, size_t len, char **ptr)
if (!tmp)
return -ENOMEM;
- if(copy_from_user(tmp, user, len)) {
+ if (copy_from_user(tmp, user, len)) {
kfree(tmp);
return -EFAULT;
}
@@ -345,162 +399,187 @@ static int copyin_string(char __user *user, size_t len, char **ptr)
/*
* NetBSD /dev/openprom ioctl calls.
*/
-static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
- unsigned int cmd, unsigned long arg)
+static int opiocget(void __user *argp, DATA *data)
{
- DATA *data = (DATA *) file->private_data;
- void __user *argp = (void __user *)arg;
struct opiocdesc op;
- int error, node, len;
- char *str, *tmp;
- char buffer[64];
- static int cnt;
-
- switch (cmd) {
- case OPIOCGET:
- if (copy_from_user(&op, argp, sizeof(op)))
- return -EFAULT;
-
- if (!goodnode(op.op_nodeid,data))
- return -EINVAL;
+ struct device_node *dp;
+ char *str;
+ void *pval;
+ int err, len;
- error = copyin_string(op.op_name, op.op_namelen, &str);
- if (error)
- return error;
+ if (copy_from_user(&op, argp, sizeof(op)))
+ return -EFAULT;
- len = prom_getproplen(op.op_nodeid,str);
+ dp = get_node(op.op_nodeid, data);
- if (len > op.op_buflen) {
- kfree(str);
- return -ENOMEM;
- }
+ err = copyin_string(op.op_name, op.op_namelen, &str);
+ if (err)
+ return err;
+ pval = of_get_property(dp, str, &len);
+ err = 0;
+ if (!pval || len > op.op_buflen) {
+ err = -EINVAL;
+ } else {
op.op_buflen = len;
+ if (copy_to_user(argp, &op, sizeof(op)) ||
+ copy_to_user(op.op_buf, pval, len))
+ err = -EFAULT;
+ }
+ kfree(str);
- if (len <= 0) {
- kfree(str);
- /* Verified by the above copy_from_user */
- if (__copy_to_user(argp, &op,
- sizeof(op)))
- return -EFAULT;
- return 0;
- }
+ return err;
+}
- tmp = kmalloc(len + 1, GFP_KERNEL);
- if (!tmp) {
- kfree(str);
- return -ENOMEM;
- }
+static int opiocnextprop(void __user *argp, DATA *data)
+{
+ struct opiocdesc op;
+ struct device_node *dp;
+ struct property *prop;
+ char *str;
+ int err, len;
- cnt = prom_getproperty(op.op_nodeid, str, tmp, len);
- if (cnt <= 0) {
- error = -EINVAL;
- } else {
- tmp[len] = '\0';
+ if (copy_from_user(&op, argp, sizeof(op)))
+ return -EFAULT;
- if (__copy_to_user(argp, &op, sizeof(op)) != 0 ||
- copy_to_user(op.op_buf, tmp, len) != 0)
- error = -EFAULT;
- }
+ dp = get_node(op.op_nodeid, data);
+ if (!dp)
+ return -EINVAL;
- kfree(tmp);
- kfree(str);
+ err = copyin_string(op.op_name, op.op_namelen, &str);
+ if (err)
+ return err;
- return error;
+ if (str[0] == '\0') {
+ prop = dp->properties;
+ } else {
+ prop = of_find_property(dp, str, NULL);
+ if (prop)
+ prop = prop->next;
+ }
+ kfree(str);
- case OPIOCNEXTPROP:
- if (copy_from_user(&op, argp, sizeof(op)))
- return -EFAULT;
+ if (!prop)
+ len = 0;
+ else
+ len = prop->length;
- if (!goodnode(op.op_nodeid,data))
- return -EINVAL;
+ if (len > op.op_buflen)
+ len = op.op_buflen;
- error = copyin_string(op.op_name, op.op_namelen, &str);
- if (error)
- return error;
+ if (copy_to_user(argp, &op, sizeof(op)))
+ return -EFAULT;
- tmp = prom_nextprop(op.op_nodeid,str,buffer);
+ if (len &&
+ copy_to_user(op.op_buf, prop->value, len))
+ return -EFAULT;
- if (tmp) {
- len = strlen(tmp);
- if (len > op.op_buflen)
- len = op.op_buflen;
- else
- op.op_buflen = len;
- } else {
- len = op.op_buflen = 0;
- }
+ return 0;
+}
- if (!access_ok(VERIFY_WRITE, argp, sizeof(op))) {
- kfree(str);
- return -EFAULT;
- }
+static int opiocset(void __user *argp, DATA *data)
+{
+ struct opiocdesc op;
+ struct device_node *dp;
+ char *str, *tmp;
+ int err;
- if (!access_ok(VERIFY_WRITE, op.op_buf, len)) {
- kfree(str);
- return -EFAULT;
- }
+ if (copy_from_user(&op, argp, sizeof(op)))
+ return -EFAULT;
+
+ dp = get_node(op.op_nodeid, data);
+ if (!dp)
+ return -EINVAL;
- error = __copy_to_user(argp, &op, sizeof(op));
- if (!error) error = __copy_to_user(op.op_buf, tmp, len);
+ err = copyin_string(op.op_name, op.op_namelen, &str);
+ if (err)
+ return err;
+ err = copyin_string(op.op_buf, op.op_buflen, &tmp);
+ if (err) {
kfree(str);
+ return err;
+ }
- return error;
+ err = of_set_property(dp, str, tmp, op.op_buflen);
- case OPIOCSET:
- if (copy_from_user(&op, argp, sizeof(op)))
- return -EFAULT;
+ kfree(str);
+ kfree(tmp);
- if (!goodnode(op.op_nodeid,data))
- return -EINVAL;
+ return err;
+}
- error = copyin_string(op.op_name, op.op_namelen, &str);
- if (error)
- return error;
+static int opiocgetnext(unsigned int cmd, void __user *argp)
+{
+ struct device_node *dp;
+ phandle nd;
- error = copyin_string(op.op_buf, op.op_buflen, &tmp);
- if (error) {
- kfree(str);
- return error;
- }
+ BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
- len = prom_setprop(op.op_nodeid,str,tmp,op.op_buflen+1);
+ if (copy_from_user(&nd, argp, sizeof(phandle)))
+ return -EFAULT;
- if (len != op.op_buflen)
+ if (nd == 0) {
+ if (cmd != OPIOCGETNEXT)
return -EINVAL;
+ dp = of_find_node_by_path("/");
+ } else {
+ dp = of_find_node_by_phandle(nd);
+ nd = 0;
+ if (dp) {
+ if (cmd == OPIOCGETNEXT)
+ dp = dp->sibling;
+ else
+ dp = dp->child;
+ }
+ }
+ if (dp)
+ nd = dp->node;
+ if (copy_to_user(argp, &nd, sizeof(phandle)))
+ return -EFAULT;
- kfree(str);
- kfree(tmp);
+ return 0;
+}
- return 0;
+static int openprom_bsd_ioctl(struct inode * inode, struct file * file,
+ unsigned int cmd, unsigned long arg)
+{
+ DATA *data = (DATA *) file->private_data;
+ void __user *argp = (void __user *)arg;
+ int err;
- case OPIOCGETOPTNODE:
- if (copy_to_user(argp, &options_node, sizeof(int)))
- return -EFAULT;
- return 0;
+ switch (cmd) {
+ case OPIOCGET:
+ err = opiocget(argp, data);
+ break;
- case OPIOCGETNEXT:
- case OPIOCGETCHILD:
- if (copy_from_user(&node, argp, sizeof(int)))
- return -EFAULT;
+ case OPIOCNEXTPROP:
+ err = opiocnextprop(argp, data);
+ break;
- if (cmd == OPIOCGETNEXT)
- node = __prom_getsibling(node);
- else
- node = __prom_getchild(node);
+ case OPIOCSET:
+ err = opiocset(argp, data);
+ break;
+
+ case OPIOCGETOPTNODE:
+ BUILD_BUG_ON(sizeof(phandle) != sizeof(int));
- if (__copy_to_user(argp, &node, sizeof(int)))
+ if (copy_to_user(argp, &options_node->node, sizeof(phandle)))
return -EFAULT;
return 0;
+ case OPIOCGETNEXT:
+ case OPIOCGETCHILD:
+ err = opiocgetnext(cmd, argp);
+ break;
+
default:
- if (cnt++ < 10)
- printk(KERN_INFO "openprom_bsd_ioctl: cmd 0x%X\n", cmd);
return -EINVAL;
- }
+ };
+
+ return err;
}
@@ -511,7 +590,6 @@ static int openprom_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
DATA *data = (DATA *) file->private_data;
- static int cnt;
switch (cmd) {
case OPROMGETOPT:
@@ -563,10 +641,8 @@ static int openprom_ioctl(struct inode * inode, struct file * file,
return openprom_bsd_ioctl(inode,file,cmd,arg);
default:
- if (cnt++ < 10)
- printk("openprom_ioctl: cmd 0x%X, arg 0x%lX\n", cmd, arg);
return -EINVAL;
- }
+ };
}
static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
@@ -594,9 +670,7 @@ static long openprom_compat_ioctl(struct file *file, unsigned int cmd,
case OPROMSETCUR:
case OPROMPCI2NODE:
case OPROMPATH2NODE:
- lock_kernel();
rval = openprom_ioctl(file->f_dentry->d_inode, file, cmd, arg);
- lock_kernel();
break;
}
@@ -607,13 +681,13 @@ static int openprom_open(struct inode * inode, struct file * file)
{
DATA *data;
- data = (DATA *) kmalloc(sizeof(DATA), GFP_KERNEL);
+ data = kmalloc(sizeof(DATA), GFP_KERNEL);
if (!data)
return -ENOMEM;
- data->current_node = prom_root_node;
- data->lastnode = prom_root_node;
- file->private_data = (void *)data;
+ data->current_node = of_find_node_by_path("/");
+ data->lastnode = data->current_node;
+ file->private_data = (void *) data;
return 0;
}
@@ -634,24 +708,30 @@ static struct file_operations openprom_fops = {
};
static struct miscdevice openprom_dev = {
- SUN_OPENPROM_MINOR, "openprom", &openprom_fops
+ .minor = SUN_OPENPROM_MINOR,
+ .name = "openprom",
+ .fops = &openprom_fops,
};
static int __init openprom_init(void)
{
- int error;
+ struct device_node *dp;
+ int err;
- error = misc_register(&openprom_dev);
- if (error) {
- printk(KERN_ERR "openprom: unable to get misc minor\n");
- return error;
- }
+ err = misc_register(&openprom_dev);
+ if (err)
+ return err;
- options_node = prom_getchild(prom_root_node);
- options_node = prom_searchsiblings(options_node,"options");
+ dp = of_find_node_by_path("/");
+ dp = dp->child;
+ while (dp) {
+ if (!strcmp(dp->name, "options"))
+ break;
+ dp = dp->sibling;
+ }
+ options_node = dp;
- if (options_node == 0 || options_node == -1) {
- printk(KERN_ERR "openprom: unable to find options node\n");
+ if (!options_node) {
misc_deregister(&openprom_dev);
return -EIO;
}
@@ -666,4 +746,3 @@ static void __exit openprom_cleanup(void)
module_init(openprom_init);
module_exit(openprom_cleanup);
-MODULE_LICENSE("GPL");
diff --git a/drivers/sbus/char/riowatchdog.c b/drivers/sbus/char/riowatchdog.c
index d1babff6a535..2a9cc8204429 100644
--- a/drivers/sbus/char/riowatchdog.c
+++ b/drivers/sbus/char/riowatchdog.c
@@ -211,7 +211,7 @@ static int __init riowd_bbc_init(void)
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, "bbc"))
+ if (!strcmp(edev->ofdev.node->name, "bbc"))
goto found_bbc;
}
}
@@ -238,7 +238,7 @@ static int __init riowd_init(void)
for_each_ebus(ebus) {
for_each_ebusdev(edev, ebus) {
- if (!strcmp(edev->prom_name, RIOWD_NAME))
+ if (!strcmp(edev->ofdev.node->name, RIOWD_NAME))
goto ebus_done;
}
}
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 44728ae3fe77..96a81cd17617 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -501,7 +501,7 @@ config SCSI_ATA_PIIX
tristate "Intel PIIX/ICH SATA support"
depends on SCSI_SATA && PCI
help
- This option enables support for ICH5 Serial ATA.
+ This option enables support for ICH5/6/7/8 Serial ATA.
If PATA support was enabled previously, this enables
support for select Intel PIIX/ICH PATA host controllers.
@@ -1169,7 +1169,7 @@ config SCSI_NCR_Q720
you do not have this SCSI card, so say N.
config SCSI_NCR53C8XX_DEFAULT_TAGS
- int " default tagged command queue depth"
+ int "default tagged command queue depth"
depends on SCSI_ZALON || SCSI_NCR_Q720
default "8"
---help---
@@ -1195,7 +1195,7 @@ config SCSI_NCR53C8XX_DEFAULT_TAGS
There is no safe option other than using good SCSI devices.
config SCSI_NCR53C8XX_MAX_TAGS
- int " maximum number of queued commands"
+ int "maximum number of queued commands"
depends on SCSI_ZALON || SCSI_NCR_Q720
default "32"
---help---
@@ -1212,7 +1212,7 @@ config SCSI_NCR53C8XX_MAX_TAGS
There is no safe option and the default answer is recommended.
config SCSI_NCR53C8XX_SYNC
- int " synchronous transfers frequency in MHz"
+ int "synchronous transfers frequency in MHz"
depends on SCSI_ZALON || SCSI_NCR_Q720
default "20"
---help---
@@ -1246,7 +1246,7 @@ config SCSI_NCR53C8XX_SYNC
terminations and SCSI conformant devices.
config SCSI_NCR53C8XX_PROFILE
- bool " enable profiling"
+ bool "enable profiling"
depends on SCSI_ZALON || SCSI_NCR_Q720
help
This option allows you to enable profiling information gathering.
@@ -1257,7 +1257,7 @@ config SCSI_NCR53C8XX_PROFILE
The normal answer therefore is N.
config SCSI_NCR53C8XX_NO_DISCONNECT
- bool " not allow targets to disconnect"
+ bool "not allow targets to disconnect"
depends on (SCSI_ZALON || SCSI_NCR_Q720) && SCSI_NCR53C8XX_DEFAULT_TAGS=0
help
This option is only provided for safety if you suspect some SCSI
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 84d546323dc7..ebd0cf00bf3e 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SCSI_ISCSI_ATTRS) += scsi_transport_iscsi.o
obj-$(CONFIG_SCSI_SAS_ATTRS) += scsi_transport_sas.o
obj-$(CONFIG_ISCSI_TCP) += libiscsi.o iscsi_tcp.o
+obj-$(CONFIG_INFINIBAND_ISER) += libiscsi.o
obj-$(CONFIG_SCSI_AMIGA7XX) += amiga7xx.o 53c7xx.o
obj-$(CONFIG_A3000_SCSI) += a3000.o wd33c93.o
obj-$(CONFIG_A2091_SCSI) += a2091.o wd33c93.o
diff --git a/drivers/scsi/NCR5380.c b/drivers/scsi/NCR5380.c
index fa57e0b4a5fd..75f2f7ae2a8e 100644
--- a/drivers/scsi/NCR5380.c
+++ b/drivers/scsi/NCR5380.c
@@ -500,7 +500,7 @@ static void NCR5380_print_phase(struct Scsi_Host *instance)
/*
* Function : int should_disconnect (unsigned char cmd)
*
- * Purpose : decide weather a command would normally disconnect or
+ * Purpose : decide whether a command would normally disconnect or
* not, since if it won't disconnect we should go to sleep.
*
* Input : cmd - opcode of SCSI command
diff --git a/drivers/scsi/aacraid/comminit.c b/drivers/scsi/aacraid/comminit.c
index 35b0a6ebd3f5..7cea514e810a 100644
--- a/drivers/scsi/aacraid/comminit.c
+++ b/drivers/scsi/aacraid/comminit.c
@@ -104,8 +104,11 @@ static int aac_alloc_comm(struct aac_dev *dev, void **commaddr, unsigned long co
* always true on real computers. It also has some slight problems
* with the GART on x86-64. I've btw never tried DMA from PCI space
* on this platform but don't be surprised if its problematic.
+ * [AK: something is very very wrong when a driver tests this symbol.
+ * Someone should figure out what the comment writer really meant here and fix
+ * the code. Or just remove that bad code. ]
*/
-#ifndef CONFIG_GART_IOMMU
+#ifndef CONFIG_IOMMU
if ((num_physpages << (PAGE_SHIFT - 12)) <= AAC_MAX_HOSTPHYSMEMPAGES) {
init->HostPhysMemPages =
cpu_to_le32(num_physpages << (PAGE_SHIFT-12));
diff --git a/drivers/scsi/advansys.c b/drivers/scsi/advansys.c
index 5ee47555a8af..dd9fb3d91000 100644
--- a/drivers/scsi/advansys.c
+++ b/drivers/scsi/advansys.c
@@ -12374,7 +12374,7 @@ AscInitFromEEP(ASC_DVC_VAR *asc_dvc)
ASC_PRINT1(
"AscInitFromEEP: Failed to re-write EEPROM with %d errors.\n", i);
} else {
- ASC_PRINT("AscInitFromEEP: Succesfully re-wrote EEPROM.");
+ ASC_PRINT("AscInitFromEEP: Successfully re-wrote EEPROM.\n");
}
}
return (warn_code);
diff --git a/drivers/scsi/ahci.c b/drivers/scsi/ahci.c
index 4bb77f62b3b9..f05946777718 100644
--- a/drivers/scsi/ahci.c
+++ b/drivers/scsi/ahci.c
@@ -48,7 +48,7 @@
#include <asm/io.h>
#define DRV_NAME "ahci"
-#define DRV_VERSION "1.3"
+#define DRV_VERSION "2.0"
enum {
diff --git a/drivers/scsi/ata_piix.c b/drivers/scsi/ata_piix.c
index 521b718763f6..94b1261a259d 100644
--- a/drivers/scsi/ata_piix.c
+++ b/drivers/scsi/ata_piix.c
@@ -93,7 +93,7 @@
#include <linux/libata.h>
#define DRV_NAME "ata_piix"
-#define DRV_VERSION "1.10"
+#define DRV_VERSION "2.00"
enum {
PIIX_IOCFG = 0x54, /* IDE I/O configuration register */
diff --git a/drivers/scsi/dc395x.c b/drivers/scsi/dc395x.c
index 183245254931..58b0748045ee 100644
--- a/drivers/scsi/dc395x.c
+++ b/drivers/scsi/dc395x.c
@@ -3771,7 +3771,7 @@ static void request_sense(struct AdapterCtlBlk *acb, struct DeviceCtlBlk *dcb,
* @target: The target for the new device.
* @lun: The lun for the new device.
*
- * Return the new device if succesfull or NULL on failure.
+ * Return the new device if successful or NULL on failure.
**/
static struct DeviceCtlBlk *device_alloc(struct AdapterCtlBlk *acb,
u8 target, u8 lun)
diff --git a/drivers/scsi/ibmmca.c b/drivers/scsi/ibmmca.c
index 115f55471ed3..497f6642b2dc 100644
--- a/drivers/scsi/ibmmca.c
+++ b/drivers/scsi/ibmmca.c
@@ -760,7 +760,7 @@ static int device_inquiry(int host_index, int ldn)
while (!got_interrupt(host_index))
barrier();
- /*if command succesful, break */
+ /*if command successful, break */
if ((stat_result(host_index) == IM_SCB_CMD_COMPLETED) || (stat_result(host_index) == IM_SCB_CMD_COMPLETED_WITH_RETRIES))
return 1;
}
@@ -885,7 +885,7 @@ static int immediate_assign(int host_index, unsigned int pun, unsigned int lun,
while (!got_interrupt(host_index))
barrier();
- /*if command succesful, break */
+ /*if command successful, break */
if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
return 1;
}
@@ -921,7 +921,7 @@ static int immediate_feature(int host_index, unsigned int speed, unsigned int ti
return 2;
} else
global_command_error_excuse = 0;
- /*if command succesful, break */
+ /*if command successful, break */
if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
return 1;
}
@@ -959,7 +959,7 @@ static int immediate_reset(int host_index, unsigned int ldn)
/* did not work, finish */
return 1;
}
- /*if command succesful, break */
+ /*if command successful, break */
if (stat_result(host_index) == IM_IMMEDIATE_CMD_COMPLETED)
return 1;
}
diff --git a/drivers/scsi/imm.c b/drivers/scsi/imm.c
index cd2dffdab77a..681bd18493f3 100644
--- a/drivers/scsi/imm.c
+++ b/drivers/scsi/imm.c
@@ -3,9 +3,6 @@
*
* (The IMM is the embedded controller in the ZIP Plus drive.)
*
- * Current Maintainer: David Campbell (Perth, Western Australia)
- * campbell@torque.net
- *
* My unoffical company acronym list is 21 pages long:
* FLA: Four letter acronym with built in facility for
* future expansion to five letters.
diff --git a/drivers/scsi/imm.h b/drivers/scsi/imm.h
index dc3aebf0e365..ece936ac29c7 100644
--- a/drivers/scsi/imm.h
+++ b/drivers/scsi/imm.h
@@ -2,7 +2,7 @@
/* Driver for the Iomega MatchMaker parallel port SCSI HBA embedded in
* the Iomega ZIP Plus drive
*
- * (c) 1998 David Campbell campbell@torque.net
+ * (c) 1998 David Campbell
*
* Please note that I live in Perth, Western Australia. GMT+0800
*/
diff --git a/drivers/scsi/ips.c b/drivers/scsi/ips.c
index 5353b28b2939..78f2ff736c3e 100644
--- a/drivers/scsi/ips.c
+++ b/drivers/scsi/ips.c
@@ -6438,7 +6438,7 @@ ips_erase_bios(ips_ha_t * ha)
/* VPP failure */
return (1);
- /* check for succesful flash */
+ /* check for successful flash */
if (status & 0x30)
/* sequence error */
return (1);
@@ -6550,7 +6550,7 @@ ips_erase_bios_memio(ips_ha_t * ha)
/* VPP failure */
return (1);
- /* check for succesful flash */
+ /* check for successful flash */
if (status & 0x30)
/* sequence error */
return (1);
diff --git a/drivers/scsi/libata-core.c b/drivers/scsi/libata-core.c
index 6c66877be2bf..d1c1c30d123f 100644
--- a/drivers/scsi/libata-core.c
+++ b/drivers/scsi/libata-core.c
@@ -88,6 +88,10 @@ int libata_fua = 0;
module_param_named(fua, libata_fua, int, 0444);
MODULE_PARM_DESC(fua, "FUA support (0=off, 1=on)");
+static int ata_probe_timeout = ATA_TMOUT_INTERNAL / HZ;
+module_param(ata_probe_timeout, int, 0444);
+MODULE_PARM_DESC(ata_probe_timeout, "Set ATA probing timeout (seconds)");
+
MODULE_AUTHOR("Jeff Garzik");
MODULE_DESCRIPTION("Library module for ATA devices");
MODULE_LICENSE("GPL");
@@ -777,11 +781,9 @@ void ata_std_dev_select (struct ata_port *ap, unsigned int device)
void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep)
{
- if (ata_msg_probe(ap)) {
+ if (ata_msg_probe(ap))
ata_port_printk(ap, KERN_INFO, "ata_dev_select: ENTER, ata%u: "
- "device %u, wait %u\n",
- ap->id, device, wait);
- }
+ "device %u, wait %u\n", ap->id, device, wait);
if (wait)
ata_wait_idle(ap);
@@ -950,7 +952,8 @@ void ata_port_flush_task(struct ata_port *ap)
*/
if (!cancel_delayed_work(&ap->port_task)) {
if (ata_msg_ctl(ap))
- ata_port_printk(ap, KERN_DEBUG, "%s: flush #2\n", __FUNCTION__);
+ ata_port_printk(ap, KERN_DEBUG, "%s: flush #2\n",
+ __FUNCTION__);
flush_workqueue(ata_wq);
}
@@ -1059,7 +1062,7 @@ unsigned ata_exec_internal(struct ata_device *dev,
spin_unlock_irqrestore(ap->lock, flags);
- rc = wait_for_completion_timeout(&wait, ATA_TMOUT_INTERNAL);
+ rc = wait_for_completion_timeout(&wait, ata_probe_timeout);
ata_port_flush_task(ap);
@@ -1081,7 +1084,7 @@ unsigned ata_exec_internal(struct ata_device *dev,
if (ata_msg_warn(ap))
ata_dev_printk(dev, KERN_WARNING,
- "qc timeout (cmd 0x%x)\n", command);
+ "qc timeout (cmd 0x%x)\n", command);
}
spin_unlock_irqrestore(ap->lock, flags);
@@ -1093,9 +1096,9 @@ unsigned ata_exec_internal(struct ata_device *dev,
if (qc->flags & ATA_QCFLAG_FAILED && !qc->err_mask) {
if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING,
+ ata_dev_printk(dev, KERN_WARNING,
"zero err_mask for failed "
- "internal command, assuming AC_ERR_OTHER\n");
+ "internal command, assuming AC_ERR_OTHER\n");
qc->err_mask |= AC_ERR_OTHER;
}
@@ -1132,6 +1135,33 @@ unsigned ata_exec_internal(struct ata_device *dev,
}
/**
+ * ata_do_simple_cmd - execute simple internal command
+ * @dev: Device to which the command is sent
+ * @cmd: Opcode to execute
+ *
+ * Execute a 'simple' command, that only consists of the opcode
+ * 'cmd' itself, without filling any other registers
+ *
+ * LOCKING:
+ * Kernel thread context (may sleep).
+ *
+ * RETURNS:
+ * Zero on success, AC_ERR_* mask on failure
+ */
+unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd)
+{
+ struct ata_taskfile tf;
+
+ ata_tf_init(dev, &tf);
+
+ tf.command = cmd;
+ tf.flags |= ATA_TFLAG_DEVICE;
+ tf.protocol = ATA_PROT_NODATA;
+
+ return ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
+}
+
+/**
* ata_pio_need_iordy - check if iordy needed
* @adev: ATA device
*
@@ -1193,8 +1223,8 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
int rc;
if (ata_msg_ctl(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n",
- __FUNCTION__, ap->id, dev->devno);
+ ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n",
+ __FUNCTION__, ap->id, dev->devno);
ata_dev_select(ap, dev->devno, 1, 1); /* select device 0/1 */
@@ -1263,9 +1293,9 @@ int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
return 0;
err_out:
- if (ata_msg_warn(ap))
+ if (ata_msg_warn(ap))
ata_dev_printk(dev, KERN_WARNING, "failed to IDENTIFY "
- "(%s, err_mask=0x%x)\n", reason, err_mask);
+ "(%s, err_mask=0x%x)\n", reason, err_mask);
return rc;
}
@@ -1318,19 +1348,21 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
int i, rc;
if (!ata_dev_enabled(dev) && ata_msg_info(ap)) {
- ata_dev_printk(dev, KERN_INFO, "%s: ENTER/EXIT (host %u, dev %u) -- nodev\n",
- __FUNCTION__, ap->id, dev->devno);
+ ata_dev_printk(dev, KERN_INFO,
+ "%s: ENTER/EXIT (host %u, dev %u) -- nodev\n",
+ __FUNCTION__, ap->id, dev->devno);
return 0;
}
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n",
- __FUNCTION__, ap->id, dev->devno);
+ ata_dev_printk(dev, KERN_DEBUG, "%s: ENTER, host %u, dev %u\n",
+ __FUNCTION__, ap->id, dev->devno);
/* print device capabilities */
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG, "%s: cfg 49:%04x 82:%04x 83:%04x "
- "84:%04x 85:%04x 86:%04x 87:%04x 88:%04x\n",
+ ata_dev_printk(dev, KERN_DEBUG,
+ "%s: cfg 49:%04x 82:%04x 83:%04x 84:%04x "
+ "85:%04x 86:%04x 87:%04x 88:%04x\n",
__FUNCTION__,
id[49], id[82], id[83], id[84],
id[85], id[86], id[87], id[88]);
@@ -1402,14 +1434,16 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
ata_id_major_version(id),
ata_mode_string(xfer_mask),
(unsigned long long)dev->n_sectors,
- dev->cylinders, dev->heads, dev->sectors);
+ dev->cylinders, dev->heads,
+ dev->sectors);
}
if (dev->id[59] & 0x100) {
dev->multi_count = dev->id[59] & 0xff;
if (ata_msg_info(ap))
- ata_dev_printk(dev, KERN_INFO, "ata%u: dev %u multi count %u\n",
- ap->id, dev->devno, dev->multi_count);
+ ata_dev_printk(dev, KERN_INFO,
+ "ata%u: dev %u multi count %u\n",
+ ap->id, dev->devno, dev->multi_count);
}
dev->cdb_len = 16;
@@ -1422,8 +1456,8 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
rc = atapi_cdb_len(id);
if ((rc < 12) || (rc > ATAPI_CDB_LEN)) {
if (ata_msg_warn(ap))
- ata_dev_printk(dev, KERN_WARNING,
- "unsupported CDB len\n");
+ ata_dev_printk(dev, KERN_WARNING,
+ "unsupported CDB len\n");
rc = -EINVAL;
goto err_out_nosup;
}
@@ -1466,8 +1500,8 @@ int ata_dev_configure(struct ata_device *dev, int print_info)
err_out_nosup:
if (ata_msg_probe(ap))
- ata_dev_printk(dev, KERN_DEBUG,
- "%s: EXIT, err\n", __FUNCTION__);
+ ata_dev_printk(dev, KERN_DEBUG,
+ "%s: EXIT, err\n", __FUNCTION__);
return rc;
}
@@ -3527,7 +3561,7 @@ void swap_buf_le16(u16 *buf, unsigned int buf_words)
* Inherited from caller.
*/
-void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
+void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
unsigned int buflen, int write_data)
{
struct ata_port *ap = adev->ap;
@@ -3573,7 +3607,7 @@ void ata_mmio_data_xfer(struct ata_device *adev, unsigned char *buf,
* Inherited from caller.
*/
-void ata_pio_data_xfer(struct ata_device *adev, unsigned char *buf,
+void ata_pio_data_xfer(struct ata_device *adev, unsigned char *buf,
unsigned int buflen, int write_data)
{
struct ata_port *ap = adev->ap;
@@ -3607,7 +3641,7 @@ void ata_pio_data_xfer(struct ata_device *adev, unsigned char *buf,
* @buflen: buffer length
* @write_data: read/write
*
- * Transfer data from/to the device data register by PIO. Do the
+ * Transfer data from/to the device data register by PIO. Do the
* transfer with interrupts disabled.
*
* LOCKING:
@@ -4946,31 +4980,9 @@ int ata_port_offline(struct ata_port *ap)
return 0;
}
-/*
- * Execute a 'simple' command, that only consists of the opcode 'cmd' itself,
- * without filling any other registers
- */
-static int ata_do_simple_cmd(struct ata_device *dev, u8 cmd)
-{
- struct ata_taskfile tf;
- int err;
-
- ata_tf_init(dev, &tf);
-
- tf.command = cmd;
- tf.flags |= ATA_TFLAG_DEVICE;
- tf.protocol = ATA_PROT_NODATA;
-
- err = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0);
- if (err)
- ata_dev_printk(dev, KERN_ERR, "%s: ata command failed: %d\n",
- __FUNCTION__, err);
-
- return err;
-}
-
-static int ata_flush_cache(struct ata_device *dev)
+int ata_flush_cache(struct ata_device *dev)
{
+ unsigned int err_mask;
u8 cmd;
if (!ata_try_flush_cache(dev))
@@ -4981,17 +4993,41 @@ static int ata_flush_cache(struct ata_device *dev)
else
cmd = ATA_CMD_FLUSH;
- return ata_do_simple_cmd(dev, cmd);
+ err_mask = ata_do_simple_cmd(dev, cmd);
+ if (err_mask) {
+ ata_dev_printk(dev, KERN_ERR, "failed to flush cache\n");
+ return -EIO;
+ }
+
+ return 0;
}
static int ata_standby_drive(struct ata_device *dev)
{
- return ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
+ unsigned int err_mask;
+
+ err_mask = ata_do_simple_cmd(dev, ATA_CMD_STANDBYNOW1);
+ if (err_mask) {
+ ata_dev_printk(dev, KERN_ERR, "failed to standby drive "
+ "(err_mask=0x%x)\n", err_mask);
+ return -EIO;
+ }
+
+ return 0;
}
static int ata_start_drive(struct ata_device *dev)
{
- return ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE);
+ unsigned int err_mask;
+
+ err_mask = ata_do_simple_cmd(dev, ATA_CMD_IDLEIMMEDIATE);
+ if (err_mask) {
+ ata_dev_printk(dev, KERN_ERR, "failed to start drive "
+ "(err_mask=0x%x)\n", err_mask);
+ return -EIO;
+ }
+
+ return 0;
}
/**
@@ -5212,7 +5248,7 @@ static void ata_host_init(struct ata_port *ap, struct Scsi_Host *host,
ap->msg_enable = 0x00FF;
#elif defined(ATA_DEBUG)
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_INFO | ATA_MSG_CTL | ATA_MSG_WARN | ATA_MSG_ERR;
-#else
+#else
ap->msg_enable = ATA_MSG_DRV | ATA_MSG_ERR | ATA_MSG_WARN;
#endif
@@ -5709,6 +5745,7 @@ int ata_pci_device_resume(struct pci_dev *pdev)
static int __init ata_init(void)
{
+ ata_probe_timeout *= HZ;
ata_wq = create_workqueue("ata");
if (!ata_wq)
return -ENOMEM;
@@ -5733,7 +5770,7 @@ module_init(ata_init);
module_exit(ata_exit);
static unsigned long ratelimit_time;
-static spinlock_t ata_ratelimit_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(ata_ratelimit_lock);
int ata_ratelimit(void)
{
diff --git a/drivers/scsi/libata-eh.c b/drivers/scsi/libata-eh.c
index 823385981a7a..bf5a72aca8a4 100644
--- a/drivers/scsi/libata-eh.c
+++ b/drivers/scsi/libata-eh.c
@@ -93,6 +93,38 @@ static int ata_ering_map(struct ata_ering *ering,
return rc;
}
+static unsigned int ata_eh_dev_action(struct ata_device *dev)
+{
+ struct ata_eh_context *ehc = &dev->ap->eh_context;
+
+ return ehc->i.action | ehc->i.dev_action[dev->devno];
+}
+
+static void ata_eh_clear_action(struct ata_device *dev,
+ struct ata_eh_info *ehi, unsigned int action)
+{
+ int i;
+
+ if (!dev) {
+ ehi->action &= ~action;
+ for (i = 0; i < ATA_MAX_DEVICES; i++)
+ ehi->dev_action[i] &= ~action;
+ } else {
+ /* doesn't make sense for port-wide EH actions */
+ WARN_ON(!(action & ATA_EH_PERDEV_MASK));
+
+ /* break ehi->action into ehi->dev_action */
+ if (ehi->action & action) {
+ for (i = 0; i < ATA_MAX_DEVICES; i++)
+ ehi->dev_action[i] |= ehi->action & action;
+ ehi->action &= ~action;
+ }
+
+ /* turn off the specified per-dev action */
+ ehi->dev_action[dev->devno] &= ~action;
+ }
+}
+
/**
* ata_scsi_timed_out - SCSI layer time out callback
* @cmd: timed out SCSI command
@@ -702,32 +734,11 @@ static void ata_eh_detach_dev(struct ata_device *dev)
ap->flags |= ATA_FLAG_SCSI_HOTPLUG;
}
- spin_unlock_irqrestore(ap->lock, flags);
-}
-
-static void ata_eh_clear_action(struct ata_device *dev,
- struct ata_eh_info *ehi, unsigned int action)
-{
- int i;
+ /* clear per-dev EH actions */
+ ata_eh_clear_action(dev, &ap->eh_info, ATA_EH_PERDEV_MASK);
+ ata_eh_clear_action(dev, &ap->eh_context.i, ATA_EH_PERDEV_MASK);
- if (!dev) {
- ehi->action &= ~action;
- for (i = 0; i < ATA_MAX_DEVICES; i++)
- ehi->dev_action[i] &= ~action;
- } else {
- /* doesn't make sense for port-wide EH actions */
- WARN_ON(!(action & ATA_EH_PERDEV_MASK));
-
- /* break ehi->action into ehi->dev_action */
- if (ehi->action & action) {
- for (i = 0; i < ATA_MAX_DEVICES; i++)
- ehi->dev_action[i] |= ehi->action & action;
- ehi->action &= ~action;
- }
-
- /* turn off the specified per-dev action */
- ehi->dev_action[dev->devno] &= ~action;
- }
+ spin_unlock_irqrestore(ap->lock, flags);
}
/**
@@ -1592,7 +1603,7 @@ static int ata_eh_revalidate_and_attach(struct ata_port *ap,
unsigned int action;
dev = &ap->device[i];
- action = ehc->i.action | ehc->i.dev_action[dev->devno];
+ action = ata_eh_dev_action(dev);
if (action & ATA_EH_REVALIDATE && ata_dev_enabled(dev)) {
if (ata_port_offline(ap)) {
diff --git a/drivers/scsi/libata-scsi.c b/drivers/scsi/libata-scsi.c
index 93d18a74c401..2915bca691e8 100644
--- a/drivers/scsi/libata-scsi.c
+++ b/drivers/scsi/libata-scsi.c
@@ -222,9 +222,7 @@ int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg)
&& copy_to_user(arg + sizeof(args), argbuf, argsize))
rc = -EFAULT;
error:
- if (argbuf)
- kfree(argbuf);
-
+ kfree(argbuf);
return rc;
}
diff --git a/drivers/scsi/libata.h b/drivers/scsi/libata.h
index bdd488897096..c325679d9b54 100644
--- a/drivers/scsi/libata.h
+++ b/drivers/scsi/libata.h
@@ -29,7 +29,7 @@
#define __LIBATA_H__
#define DRV_NAME "libata"
-#define DRV_VERSION "1.30" /* must be exactly four chars */
+#define DRV_VERSION "2.00" /* must be exactly four chars */
struct ata_scsi_args {
struct ata_device *dev;
@@ -50,6 +50,7 @@ extern void ata_port_flush_task(struct ata_port *ap);
extern unsigned ata_exec_internal(struct ata_device *dev,
struct ata_taskfile *tf, const u8 *cdb,
int dma_dir, void *buf, unsigned int buflen);
+extern unsigned int ata_do_simple_cmd(struct ata_device *dev, u8 cmd);
extern int ata_dev_read_id(struct ata_device *dev, unsigned int *p_class,
int post_reset, u16 *id);
extern int ata_dev_configure(struct ata_device *dev, int print_info);
@@ -64,6 +65,7 @@ extern int ata_check_atapi_dma(struct ata_queued_cmd *qc);
extern void ata_dev_select(struct ata_port *ap, unsigned int device,
unsigned int wait, unsigned int can_sleep);
extern void swap_buf_le16(u16 *buf, unsigned int buf_words);
+extern int ata_flush_cache(struct ata_device *dev);
extern void ata_dev_init(struct ata_device *dev);
extern int ata_task_ioctl(struct scsi_device *scsidev, void __user *arg);
extern int ata_cmd_ioctl(struct scsi_device *scsidev, void __user *arg);
diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c
index 6ab035590ee6..b28712df0b77 100644
--- a/drivers/scsi/ncr53c8xx.c
+++ b/drivers/scsi/ncr53c8xx.c
@@ -5118,8 +5118,7 @@ static void ncr_ccb_skipped(struct ncb *np, struct ccb *cp)
cp->host_status &= ~HS_SKIPMASK;
cp->start.schedule.l_paddr =
cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
- list_del(&cp->link_ccbq);
- list_add_tail(&cp->link_ccbq, &lp->skip_ccbq);
+ list_move_tail(&cp->link_ccbq, &lp->skip_ccbq);
if (cp->queued) {
--lp->queuedccbs;
}
diff --git a/drivers/scsi/ppa.c b/drivers/scsi/ppa.c
index 108910f512e4..d58ac5ad509d 100644
--- a/drivers/scsi/ppa.c
+++ b/drivers/scsi/ppa.c
@@ -6,8 +6,6 @@
* (c) 1995,1996 Grant R. Guenther, grant@torque.net,
* under the terms of the GNU General Public License.
*
- * Current Maintainer: David Campbell (Perth, Western Australia, GMT+0800)
- * campbell@torque.net
*/
#include <linux/config.h>
diff --git a/drivers/scsi/ppa.h b/drivers/scsi/ppa.h
index f6e1a1574bb8..7511df3588e4 100644
--- a/drivers/scsi/ppa.h
+++ b/drivers/scsi/ppa.h
@@ -2,7 +2,7 @@
* the Iomega ZIP drive
*
* (c) 1996 Grant R. Guenther grant@torque.net
- * David Campbell campbell@torque.net
+ * David Campbell
*
* All comments to David.
*/
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
index aef093db597e..3d4487eac9b7 100644
--- a/drivers/scsi/qla2xxx/qla_init.c
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -2258,8 +2258,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *ha)
}
/* Remove device from the new list and add it to DB */
- list_del(&fcport->list);
- list_add_tail(&fcport->list, &ha->fcports);
+ list_move_tail(&fcport->list, &ha->fcports);
/* Login and update database */
qla2x00_fabric_dev_login(ha, fcport, &next_loopid);
diff --git a/drivers/scsi/sata_nv.c b/drivers/scsi/sata_nv.c
index d18e7e0932ef..5cc42c6054eb 100644
--- a/drivers/scsi/sata_nv.c
+++ b/drivers/scsi/sata_nv.c
@@ -44,7 +44,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_nv"
-#define DRV_VERSION "0.9"
+#define DRV_VERSION "2.0"
enum {
NV_PORTS = 2,
diff --git a/drivers/scsi/sata_sil.c b/drivers/scsi/sata_sil.c
index bc9f918a7f28..51d86d750e84 100644
--- a/drivers/scsi/sata_sil.c
+++ b/drivers/scsi/sata_sil.c
@@ -46,12 +46,13 @@
#include <linux/libata.h>
#define DRV_NAME "sata_sil"
-#define DRV_VERSION "1.0"
+#define DRV_VERSION "2.0"
enum {
/*
* host flags
*/
+ SIL_FLAG_NO_SATA_IRQ = (1 << 28),
SIL_FLAG_RERR_ON_DMA_ACT = (1 << 29),
SIL_FLAG_MOD15WRITE = (1 << 30),
@@ -62,8 +63,9 @@ enum {
* Controller IDs
*/
sil_3112 = 0,
- sil_3512 = 1,
- sil_3114 = 2,
+ sil_3112_no_sata_irq = 1,
+ sil_3512 = 2,
+ sil_3114 = 3,
/*
* Register offsets
@@ -123,8 +125,8 @@ static const struct pci_device_id sil_pci_tbl[] = {
{ 0x1095, 0x3512, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3512 },
{ 0x1095, 0x3114, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3114 },
{ 0x1002, 0x436e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
- { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
- { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112 },
+ { 0x1002, 0x4379, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq },
+ { 0x1002, 0x437a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, sil_3112_no_sata_irq },
{ } /* terminate list */
};
@@ -217,6 +219,16 @@ static const struct ata_port_info sil_port_info[] = {
.udma_mask = 0x3f, /* udma0-5 */
.port_ops = &sil_ops,
},
+ /* sil_3112_no_sata_irq */
+ {
+ .sht = &sil_sht,
+ .host_flags = SIL_DFL_HOST_FLAGS | SIL_FLAG_MOD15WRITE |
+ SIL_FLAG_NO_SATA_IRQ,
+ .pio_mask = 0x1f, /* pio0-4 */
+ .mwdma_mask = 0x07, /* mwdma0-2 */
+ .udma_mask = 0x3f, /* udma0-5 */
+ .port_ops = &sil_ops,
+ },
/* sil_3512 */
{
.sht = &sil_sht,
@@ -437,6 +449,10 @@ static irqreturn_t sil_interrupt(int irq, void *dev_instance,
if (unlikely(!ap || ap->flags & ATA_FLAG_DISABLED))
continue;
+ /* turn off SATA_IRQ if not supported */
+ if (ap->flags & SIL_FLAG_NO_SATA_IRQ)
+ bmdma2 &= ~SIL_DMA_SATA_IRQ;
+
if (bmdma2 == 0xffffffff ||
!(bmdma2 & (SIL_DMA_COMPLETE | SIL_DMA_SATA_IRQ)))
continue;
@@ -474,8 +490,9 @@ static void sil_thaw(struct ata_port *ap)
ata_chk_status(ap);
ata_bmdma_irq_clear(ap);
- /* turn on SATA IRQ */
- writel(SIL_SIEN_N, mmio_base + sil_port[ap->port_no].sien);
+ /* turn on SATA IRQ if supported */
+ if (!(ap->flags & SIL_FLAG_NO_SATA_IRQ))
+ writel(SIL_SIEN_N, mmio_base + sil_port[ap->port_no].sien);
/* turn on IRQ */
tmp = readl(mmio_base + SIL_SYSCFG);
diff --git a/drivers/scsi/sata_sil24.c b/drivers/scsi/sata_sil24.c
index c8b477c67247..b5f8fa955679 100644
--- a/drivers/scsi/sata_sil24.c
+++ b/drivers/scsi/sata_sil24.c
@@ -31,7 +31,7 @@
#include <asm/io.h>
#define DRV_NAME "sata_sil24"
-#define DRV_VERSION "0.24"
+#define DRV_VERSION "0.3"
/*
* Port request block (PRB) 32 bytes
diff --git a/drivers/scsi/sata_svw.c b/drivers/scsi/sata_svw.c
index c94b870cf378..7566c2cabaf7 100644
--- a/drivers/scsi/sata_svw.c
+++ b/drivers/scsi/sata_svw.c
@@ -54,7 +54,7 @@
#endif /* CONFIG_PPC_OF */
#define DRV_NAME "sata_svw"
-#define DRV_VERSION "1.8"
+#define DRV_VERSION "2.0"
enum {
/* Taskfile registers offsets */
diff --git a/drivers/scsi/sata_uli.c b/drivers/scsi/sata_uli.c
index f668c997e9af..64f3c1aeed21 100644
--- a/drivers/scsi/sata_uli.c
+++ b/drivers/scsi/sata_uli.c
@@ -37,7 +37,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_uli"
-#define DRV_VERSION "0.6"
+#define DRV_VERSION "1.0"
enum {
uli_5289 = 0,
diff --git a/drivers/scsi/sata_via.c b/drivers/scsi/sata_via.c
index 322890b400a6..501ce1791782 100644
--- a/drivers/scsi/sata_via.c
+++ b/drivers/scsi/sata_via.c
@@ -47,7 +47,7 @@
#include <asm/io.h>
#define DRV_NAME "sata_via"
-#define DRV_VERSION "1.2"
+#define DRV_VERSION "2.0"
enum board_ids_enum {
vt6420,
@@ -335,10 +335,10 @@ static int svia_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
if ((pci_resource_start(pdev, i) == 0) ||
(pci_resource_len(pdev, i) < bar_sizes[i])) {
dev_printk(KERN_ERR, &pdev->dev,
- "invalid PCI BAR %u (sz 0x%lx, val 0x%lx)\n",
- i,
- pci_resource_start(pdev, i),
- pci_resource_len(pdev, i));
+ "invalid PCI BAR %u (sz 0x%llx, val 0x%llx)\n",
+ i,
+ (unsigned long long)pci_resource_start(pdev, i),
+ (unsigned long long)pci_resource_len(pdev, i));
rc = -ENODEV;
goto err_out_regions;
}
diff --git a/drivers/scsi/sata_vsc.c b/drivers/scsi/sata_vsc.c
index 6d0c4f18e652..616fd9634b4b 100644
--- a/drivers/scsi/sata_vsc.c
+++ b/drivers/scsi/sata_vsc.c
@@ -47,7 +47,7 @@
#include <linux/libata.h>
#define DRV_NAME "sata_vsc"
-#define DRV_VERSION "1.2"
+#define DRV_VERSION "2.0"
enum {
/* Interrupt register offsets (from chip base address) */
@@ -443,16 +443,12 @@ err_out:
}
-/*
- * Intel 31244 is supposed to be identical.
- * Compatibility is untested as of yet.
- */
static const struct pci_device_id vsc_sata_pci_tbl[] = {
- { PCI_VENDOR_ID_VITESSE, PCI_DEVICE_ID_VITESSE_VSC7174,
+ { PCI_VENDOR_ID_VITESSE, 0x7174,
PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_GD31244,
+ { PCI_VENDOR_ID_INTEL, 0x3200,
PCI_ANY_ID, PCI_ANY_ID, 0x10600, 0xFFFFFF, 0 },
- { }
+ { } /* terminate list */
};
diff --git a/drivers/scsi/st.c b/drivers/scsi/st.c
index 1272dd249af3..b5218fc0ac86 100644
--- a/drivers/scsi/st.c
+++ b/drivers/scsi/st.c
@@ -2818,7 +2818,7 @@ static int st_int_ioctl(struct scsi_tape *STp, unsigned int cmd_in, unsigned lon
(cmdstatp->sense_hdr.sense_key == NO_SENSE ||
cmdstatp->sense_hdr.sense_key == RECOVERED_ERROR) &&
undone == 0) {
- ioctl_result = 0; /* EOF written succesfully at EOM */
+ ioctl_result = 0; /* EOF written successfully at EOM */
if (fileno >= 0)
fileno++;
STps->drv_file = fileno;
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c
index b88a7c1158af..bff94541991c 100644
--- a/drivers/serial/68328serial.c
+++ b/drivers/serial/68328serial.c
@@ -131,17 +131,6 @@ static int m68328_console_baud = CONSOLE_BAUD_RATE;
static int m68328_console_cbaud = DEFAULT_CBAUD;
-/*
- * tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char tmp_buf[SERIAL_XMIT_SIZE]; /* This is cheating */
-
static inline int serial_paranoia_check(struct m68k_serial *info,
char *name, const char *routine)
{
@@ -211,16 +200,16 @@ static void rs_stop(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->name, "rs_stop"))
return;
- save_flags(flags); cli();
+ local_irq_save(flags);
uart->ustcnt &= ~USTCNT_TXEN;
- restore_flags(flags);
+ local_irq_restore(flags);
}
static void rs_put_char(char ch)
{
int flags, loops = 0;
- save_flags(flags); cli();
+ local_irq_save(flags);
while (!(UTX & UTX_TX_AVAIL) && (loops < 1000)) {
loops++;
@@ -229,7 +218,7 @@ static void rs_put_char(char ch)
UTX_TXDATA = ch;
udelay(5);
- restore_flags(flags);
+ local_irq_restore(flags);
}
static void rs_start(struct tty_struct *tty)
@@ -241,7 +230,7 @@ static void rs_start(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->name, "rs_start"))
return;
- save_flags(flags); cli();
+ local_irq_save(flags);
if (info->xmit_cnt && info->xmit_buf && !(uart->ustcnt & USTCNT_TXEN)) {
#ifdef USE_INTS
uart->ustcnt |= USTCNT_TXEN | USTCNT_TX_INTR_MASK;
@@ -249,7 +238,7 @@ static void rs_start(struct tty_struct *tty)
uart->ustcnt |= USTCNT_TXEN;
#endif
}
- restore_flags(flags);
+ local_irq_restore(flags);
}
/* Drop into either the boot monitor or kadb upon receiving a break
@@ -327,14 +316,6 @@ static void receive_chars(struct m68k_serial *info, struct pt_regs *regs,
if(!tty)
goto clear_and_exit;
- /*
- * Make sure that we do not overflow the buffer
- */
- if (tty_request_buffer_room(tty, 1) == 0) {
- tty_schedule_flip(tty);
- return;
- }
-
flag = TTY_NORMAL;
if(rx & URX_PARITY_ERROR) {
@@ -473,7 +454,7 @@ static int startup(struct m68k_serial * info)
return -ENOMEM;
}
- save_flags(flags); cli();
+ local_irq_save(flags);
/*
* Clear the FIFO buffers and disable them
@@ -506,7 +487,7 @@ static int startup(struct m68k_serial * info)
change_speed(info);
info->flags |= S_INITIALIZED;
- restore_flags(flags);
+ local_irq_restore(flags);
return 0;
}
@@ -523,7 +504,7 @@ static void shutdown(struct m68k_serial * info)
if (!(info->flags & S_INITIALIZED))
return;
- save_flags(flags); cli(); /* Disable interrupts */
+ local_irq_save(flags);
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
@@ -534,7 +515,7 @@ static void shutdown(struct m68k_serial * info)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~S_INITIALIZED;
- restore_flags(flags);
+ local_irq_restore(flags);
}
struct {
@@ -655,24 +636,24 @@ static void rs_fair_output(void)
if (info == 0) return;
if (info->xmit_buf == 0) return;
- save_flags(flags); cli();
+ local_irq_save(flags);
left = info->xmit_cnt;
while (left != 0) {
c = info->xmit_buf[info->xmit_tail];
info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
- restore_flags(flags);
+ local_irq_restore(flags);
rs_put_char(c);
- save_flags(flags); cli();
+ local_irq_save(flags);
left = min(info->xmit_cnt, left-1);
}
/* Last character is being transmitted now (hopefully). */
udelay(5);
- restore_flags(flags);
+ local_irq_restore(flags);
return;
}
@@ -720,11 +701,11 @@ static void rs_flush_chars(struct tty_struct *tty)
#endif
/* Enable transmitter */
- save_flags(flags); cli();
+ local_irq_save(flags);
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!info->xmit_buf) {
- restore_flags(flags);
+ local_irq_restore(flags);
return;
}
@@ -749,7 +730,7 @@ static void rs_flush_chars(struct tty_struct *tty)
while (!(uart->utx.w & UTX_TX_AVAIL)) udelay(5);
}
#endif
- restore_flags(flags);
+ local_irq_restore(flags);
}
extern void console_printn(const char * b, int count);
@@ -768,18 +749,22 @@ static int rs_write(struct tty_struct * tty,
if (!tty || !info->xmit_buf)
return 0;
- save_flags(flags);
+ local_save_flags(flags);
while (1) {
- cli();
+ local_irq_disable();
c = min_t(int, count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
+ local_irq_restore(flags);
+
if (c <= 0)
break;
memcpy(info->xmit_buf + info->xmit_head, buf, c);
+
+ local_irq_disable();
info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt += c;
- restore_flags(flags);
+ local_irq_restore(flags);
buf += c;
count -= c;
total += c;
@@ -787,7 +772,7 @@ static int rs_write(struct tty_struct * tty,
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) {
/* Enable transmitter */
- cli();
+ local_irq_disable();
#ifndef USE_INTS
while(info->xmit_cnt) {
#endif
@@ -807,9 +792,9 @@ static int rs_write(struct tty_struct * tty,
#ifndef USE_INTS
}
#endif
- restore_flags(flags);
+ local_irq_restore(flags);
}
- restore_flags(flags);
+
return total;
}
@@ -838,12 +823,13 @@ static int rs_chars_in_buffer(struct tty_struct *tty)
static void rs_flush_buffer(struct tty_struct *tty)
{
struct m68k_serial *info = (struct m68k_serial *)tty->driver_data;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
return;
- cli();
+ local_irq_save(flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
+ local_irq_restore(flags);
tty_wakeup(tty);
}
@@ -973,14 +959,15 @@ static int get_lsr_info(struct m68k_serial * info, unsigned int *value)
m68328_uart *uart = &uart_addr[info->line];
#endif
unsigned char status;
+ unsigned long flags;
- cli();
+ local_irq_save(flags);
#ifdef CONFIG_SERIAL_68328_RTS_CTS
status = (uart->utx.w & UTX_CTS_STAT) ? 1 : 0;
#else
status = 0;
#endif
- sti();
+ local_irq_restore(flags);
put_user(status,value);
return 0;
}
@@ -994,14 +981,13 @@ static void send_break(struct m68k_serial * info, unsigned int duration)
unsigned long flags;
if (!info->port)
return;
- save_flags(flags);
- cli();
+ local_irq_save(flags);
#ifdef USE_INTS
uart->utx.w |= UTX_SEND_BREAK;
msleep_interruptible(duration);
uart->utx.w &= ~UTX_SEND_BREAK;
#endif
- restore_flags(flags);
+ local_irq_restore(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
@@ -1060,7 +1046,7 @@ static int rs_ioctl(struct tty_struct *tty, struct file * file,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
if (access_ok(VERIFY_WRITE, (void *) arg,
- sizeof(unsigned int));
+ sizeof(unsigned int)))
return get_lsr_info(info, (unsigned int *) arg);
return -EFAULT;
case TIOCSERGSTRUCT:
@@ -1113,10 +1099,10 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
return;
- save_flags(flags); cli();
+ local_irq_save(flags);
if (tty_hung_up_p(filp)) {
- restore_flags(flags);
+ local_irq_restore(flags);
return;
}
@@ -1138,7 +1124,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
info->count = 0;
}
if (info->count) {
- restore_flags(flags);
+ local_irq_restore(flags);
return;
}
info->flags |= S_CLOSING;
@@ -1186,7 +1172,7 @@ static void rs_close(struct tty_struct *tty, struct file * filp)
}
info->flags &= ~(S_NORMAL_ACTIVE|S_CLOSING);
wake_up_interruptible(&info->close_wait);
- restore_flags(flags);
+ local_irq_restore(flags);
}
/*
@@ -1262,9 +1248,9 @@ static int block_til_ready(struct tty_struct *tty, struct file * filp,
info->count--;
info->blocked_open++;
while (1) {
- cli();
+ local_irq_disable();
m68k_rtsdtr(info, 1);
- sti();
+ local_irq_enable();
current->state = TASK_INTERRUPTIBLE;
if (tty_hung_up_p(filp) ||
!(info->flags & S_INITIALIZED)) {
@@ -1444,7 +1430,7 @@ rs68328_init(void)
return -ENOMEM;
}
- save_flags(flags); cli();
+ local_irq_save(flags);
for(i=0;i<NR_PORTS;i++) {
@@ -1489,7 +1475,7 @@ rs68328_init(void)
serial_pm[i]->data = info;
#endif
}
- restore_flags(flags);
+ local_irq_restore(flags);
return 0;
}
diff --git a/drivers/serial/8250_pci.c b/drivers/serial/8250_pci.c
index 94886c000d2a..864ef859be56 100644
--- a/drivers/serial/8250_pci.c
+++ b/drivers/serial/8250_pci.c
@@ -594,8 +594,8 @@ pci_default_setup(struct serial_private *priv, struct pciserial_board *board,
else
offset += idx * board->uart_offset;
- maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) /
- (8 << board->reg_shift);
+ maxnr = (pci_resource_len(priv->dev, bar) - board->first_offset) >>
+ (board->reg_shift + 3);
if (board->flags & FL_REGION_SZ_CAP && idx >= maxnr)
return 1;
diff --git a/drivers/serial/8250_pnp.c b/drivers/serial/8250_pnp.c
index b79ed0665d51..739bc84f91e9 100644
--- a/drivers/serial/8250_pnp.c
+++ b/drivers/serial/8250_pnp.c
@@ -323,8 +323,10 @@ static const struct pnp_device_id pnp_dev_table[] = {
{ "USR9180", 0 },
/* U.S. Robotics 56K Voice INT PnP*/
{ "USR9190", 0 },
- /* HP Compaq Tablet PC tc1100 Wacom tablet */
+ /* Wacom tablets */
+ { "WACF004", 0 },
{ "WACF005", 0 },
+ { "WACF006", 0 },
/* Rockwell's (PORALiNK) 33600 INT PNP */
{ "WCI0003", 0 },
/* Unkown PnP modems */
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
index 89700141f87e..5cacc5e74a92 100644
--- a/drivers/serial/crisv10.c
+++ b/drivers/serial/crisv10.c
@@ -2573,12 +2573,6 @@ static void flush_to_flip_buffer(struct e100_serial *info)
DFLIP(
if (1) {
-
- if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
- DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count);
- DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt);
- } else {
- }
DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx);
DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty));
DEBUG_LOG(info->line, "room %lu\n", tty->ldisc.receive_room(tty));
diff --git a/drivers/serial/jsm/jsm_tty.c b/drivers/serial/jsm/jsm_tty.c
index 7d823705193c..f8262e6ad8d3 100644
--- a/drivers/serial/jsm/jsm_tty.c
+++ b/drivers/serial/jsm/jsm_tty.c
@@ -589,13 +589,6 @@ void jsm_input(struct jsm_channel *ch)
ld = tty_ldisc_ref(tp);
/*
- * If the DONT_FLIP flag is on, don't flush our buffer, and act
- * like the ld doesn't have any space to put the data right now.
- */
- if (test_bit(TTY_DONT_FLIP, &tp->flags))
- len = 0;
-
- /*
* If we were unable to get a reference to the ld,
* don't flush our buffer, and act like the ld doesn't
* have any space to put the data right now.
diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c
index 8cbbb954df2c..8ad242934368 100644
--- a/drivers/serial/mcfserial.c
+++ b/drivers/serial/mcfserial.c
@@ -60,11 +60,11 @@ struct timer_list mcfrs_timer_struct;
#if defined(CONFIG_HW_FEITH)
#define CONSOLE_BAUD_RATE 38400
#define DEFAULT_CBAUD B38400
-#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB)
+#elif defined(CONFIG_MOD5272) || defined(CONFIG_M5208EVB) || defined(CONFIG_M5329EVB)
#define CONSOLE_BAUD_RATE 115200
#define DEFAULT_CBAUD B115200
#elif defined(CONFIG_ARNEWSH) || defined(CONFIG_FREESCALE) || \
- defined(CONFIG_senTec) || defined(CONFIG_SNEHA)
+ defined(CONFIG_senTec) || defined(CONFIG_SNEHA) || defined(CONFIG_AVNET)
#define CONSOLE_BAUD_RATE 19200
#define DEFAULT_CBAUD B19200
#endif
@@ -93,7 +93,7 @@ static struct tty_driver *mcfrs_serial_driver;
#undef SERIAL_DEBUG_FLOW
#if defined(CONFIG_M523x) || defined(CONFIG_M527x) || defined(CONFIG_M528x) || \
- defined(CONFIG_M520x)
+ defined(CONFIG_M520x) || defined(CONFIG_M532x)
#define IRQBASE (MCFINT_VECBASE+MCFINT_UART0)
#else
#define IRQBASE 73
@@ -1545,6 +1545,28 @@ static void mcfrs_irqinit(struct mcf_serial *info)
*feci2c_par |= MCF_GPIO_PAR_FECI2C_PAR_SCL_UTXD2
| MCF_GPIO_PAR_FECI2C_PAR_SDA_URXD2;
}
+#elif defined(CONFIG_M532x)
+ volatile unsigned char *uartp;
+ uartp = info->addr;
+ switch (info->line) {
+ case 0:
+ MCF_INTC0_ICR26 = 0x3;
+ MCF_INTC0_CIMR = 26;
+ /* GPIO initialization */
+ MCF_GPIO_PAR_UART |= 0x000F;
+ break;
+ case 1:
+ MCF_INTC0_ICR27 = 0x3;
+ MCF_INTC0_CIMR = 27;
+ /* GPIO initialization */
+ MCF_GPIO_PAR_UART |= 0x0FF0;
+ break;
+ case 2:
+ MCF_INTC0_ICR28 = 0x3;
+ MCF_INTC0_CIMR = 28;
+ /* GPIOs also must be initalized, depends on board */
+ break;
+ }
#else
volatile unsigned char *icrp, *uartp;
diff --git a/drivers/sn/ioc3.c b/drivers/sn/ioc3.c
index 501316b198e5..ed946311d3a4 100644
--- a/drivers/sn/ioc3.c
+++ b/drivers/sn/ioc3.c
@@ -26,7 +26,7 @@ static DECLARE_RWSEM(ioc3_devices_rwsem);
static struct ioc3_submodule *ioc3_submodules[IOC3_MAX_SUBMODULES];
static struct ioc3_submodule *ioc3_ethernet;
-static rwlock_t ioc3_submodules_lock = RW_LOCK_UNLOCKED;
+static DEFINE_RWLOCK(ioc3_submodules_lock);
/* NIC probing code */
diff --git a/drivers/sn/ioc4.c b/drivers/sn/ioc4.c
index 8256a97eb508..8562821e6498 100644
--- a/drivers/sn/ioc4.c
+++ b/drivers/sn/ioc4.c
@@ -438,7 +438,7 @@ static struct pci_device_id ioc4_id_table[] = {
{0}
};
-static struct pci_driver __devinitdata ioc4_driver = {
+static struct pci_driver ioc4_driver = {
.name = "IOC4",
.id_table = ioc4_id_table,
.probe = ioc4_probe,
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index 1cea4a6799fe..ed1cdf6ac8f3 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -210,6 +210,7 @@ spi_new_device(struct spi_master *master, struct spi_board_info *chip)
proxy->master = master;
proxy->chip_select = chip->chip_select;
proxy->max_speed_hz = chip->max_speed_hz;
+ proxy->mode = chip->mode;
proxy->irq = chip->irq;
proxy->modalias = chip->modalias;
diff --git a/drivers/telephony/ixj.c b/drivers/telephony/ixj.c
index 5578a9dd04e8..f6b2948ab288 100644
--- a/drivers/telephony/ixj.c
+++ b/drivers/telephony/ixj.c
@@ -5712,7 +5712,7 @@ static int ixj_daa_write(IXJ *j)
return 1;
}
-int ixj_set_tone_off(unsigned short arg, IXJ *j)
+static int ixj_set_tone_off(unsigned short arg, IXJ *j)
{
j->tone_off_time = arg;
if (ixj_WriteDSPCommand(0x6E05, j)) /* Set Tone Off Period */
diff --git a/drivers/usb/host/hc_crisv10.c b/drivers/usb/host/hc_crisv10.c
index 2fe7fd19437b..4a22909518f5 100644
--- a/drivers/usb/host/hc_crisv10.c
+++ b/drivers/usb/host/hc_crisv10.c
@@ -411,8 +411,7 @@ static inline void urb_list_move_last(struct urb *urb, int epid)
urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
assert(urb_entry);
- list_del(&urb_entry->list);
- list_add_tail(&urb_entry->list, &urb_list[epid]);
+ list_move_tail(&urb_entry->list, &urb_list[epid]);
}
/* Get the next urb in the list. */
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index 6b4bc3f2bd86..89bcda5a3298 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -1684,9 +1684,13 @@ sl811h_probe(struct platform_device *dev)
if (!addr || !data)
return -ENODEV;
ioaddr = 1;
-
- addr_reg = (void __iomem *) addr->start;
- data_reg = (void __iomem *) data->start;
+ /*
+ * NOTE: 64-bit resource->start is getting truncated
+ * to avoid compiler warning, assuming that ->start
+ * is always 32-bit for this case
+ */
+ addr_reg = (void __iomem *) (unsigned long) addr->start;
+ data_reg = (void __iomem *) (unsigned long) data->start;
} else {
addr_reg = ioremap(addr->start, 1);
if (addr_reg == NULL) {
diff --git a/drivers/usb/input/fixp-arith.h b/drivers/usb/input/fixp-arith.h
index b44d398de071..ed3d2da0c485 100644
--- a/drivers/usb/input/fixp-arith.h
+++ b/drivers/usb/input/fixp-arith.h
@@ -2,8 +2,6 @@
#define _FIXP_ARITH_H
/*
- * $$
- *
* Simplistic fixed-point arithmetics.
* Hmm, I'm probably duplicating some code :(
*
@@ -31,20 +29,20 @@
#include <linux/types.h>
-// The type representing fixed-point values
+/* The type representing fixed-point values */
typedef s16 fixp_t;
#define FRAC_N 8
#define FRAC_MASK ((1<<FRAC_N)-1)
-// Not to be used directly. Use fixp_{cos,sin}
-static const fixp_t cos_table[45] = {
+/* Not to be used directly. Use fixp_{cos,sin} */
+static const fixp_t cos_table[46] = {
0x0100, 0x00FF, 0x00FF, 0x00FE, 0x00FD, 0x00FC, 0x00FA, 0x00F8,
0x00F6, 0x00F3, 0x00F0, 0x00ED, 0x00E9, 0x00E6, 0x00E2, 0x00DD,
0x00D9, 0x00D4, 0x00CF, 0x00C9, 0x00C4, 0x00BE, 0x00B8, 0x00B1,
0x00AB, 0x00A4, 0x009D, 0x0096, 0x008F, 0x0087, 0x0080, 0x0078,
0x0070, 0x0068, 0x005F, 0x0057, 0x004F, 0x0046, 0x003D, 0x0035,
- 0x002C, 0x0023, 0x001A, 0x0011, 0x0008
+ 0x002C, 0x0023, 0x001A, 0x0011, 0x0008, 0x0000
};
@@ -68,9 +66,8 @@ static inline fixp_t fixp_cos(unsigned int degrees)
int quadrant = (degrees / 90) & 3;
unsigned int i = degrees % 90;
- if (quadrant == 1 || quadrant == 3) {
- i = 89 - i;
- }
+ if (quadrant == 1 || quadrant == 3)
+ i = 90 - i;
i >>= 1;
diff --git a/drivers/usb/input/hid-debug.h b/drivers/usb/input/hid-debug.h
index 702c48c2f81b..f04d6d75c098 100644
--- a/drivers/usb/input/hid-debug.h
+++ b/drivers/usb/input/hid-debug.h
@@ -563,7 +563,7 @@ static char *keys[KEY_MAX + 1] = {
[KEY_VOLUMEUP] = "VolumeUp", [KEY_POWER] = "Power",
[KEY_KPEQUAL] = "KPEqual", [KEY_KPPLUSMINUS] = "KPPlusMinus",
[KEY_PAUSE] = "Pause", [KEY_KPCOMMA] = "KPComma",
- [KEY_HANGUEL] = "Hanguel", [KEY_HANJA] = "Hanja",
+ [KEY_HANGUEL] = "Hangeul", [KEY_HANJA] = "Hanja",
[KEY_YEN] = "Yen", [KEY_LEFTMETA] = "LeftMeta",
[KEY_RIGHTMETA] = "RightMeta", [KEY_COMPOSE] = "Compose",
[KEY_STOP] = "Stop", [KEY_AGAIN] = "Again",
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 9432c7302275..d7f3f736a692 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -453,8 +453,7 @@ static void ir_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
tty = port->tty;
/*
- * FIXME: must not do this in IRQ context,
- * must honour TTY_DONT_FLIP
+ * FIXME: must not do this in IRQ context
*/
tty->ldisc.receive_buf(
tty,
diff --git a/drivers/usb/serial/whiteheat.c b/drivers/usb/serial/whiteheat.c
index 5b06fa366098..56ffc81302fc 100644
--- a/drivers/usb/serial/whiteheat.c
+++ b/drivers/usb/serial/whiteheat.c
@@ -686,19 +686,16 @@ static void whiteheat_close(struct usb_serial_port *port, struct file * filp)
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
usb_kill_urb(urb);
- list_del(tmp);
- list_add(tmp, &info->rx_urbs_free);
- }
- list_for_each_safe(tmp, tmp2, &info->rx_urb_q) {
- list_del(tmp);
- list_add(tmp, &info->rx_urbs_free);
+ list_move(tmp, &info->rx_urbs_free);
}
+ list_for_each_safe(tmp, tmp2, &info->rx_urb_q)
+ list_move(tmp, &info->rx_urbs_free);
+
list_for_each_safe(tmp, tmp2, &info->tx_urbs_submitted) {
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
usb_kill_urb(urb);
- list_del(tmp);
- list_add(tmp, &info->tx_urbs_free);
+ list_move(tmp, &info->tx_urbs_free);
}
spin_unlock_irqrestore(&info->lock, flags);
@@ -1080,8 +1077,7 @@ static void whiteheat_write_callback(struct urb *urb, struct pt_regs *regs)
err("%s - Not my urb!", __FUNCTION__);
return;
}
- list_del(&wrap->list);
- list_add(&wrap->list, &info->tx_urbs_free);
+ list_move(&wrap->list, &info->tx_urbs_free);
spin_unlock(&info->lock);
if (urb->status) {
@@ -1371,8 +1367,7 @@ static int start_port_read(struct usb_serial_port *port)
wrap = list_entry(tmp, struct whiteheat_urb_wrap, list);
urb = wrap->urb;
usb_kill_urb(urb);
- list_del(tmp);
- list_add(tmp, &info->rx_urbs_free);
+ list_move(tmp, &info->rx_urbs_free);
}
break;
}
diff --git a/drivers/usb/storage/usb.h b/drivers/usb/storage/usb.h
index 009fb0953a56..5284abe1b5eb 100644
--- a/drivers/usb/storage/usb.h
+++ b/drivers/usb/storage/usb.h
@@ -160,10 +160,10 @@ struct us_data {
};
/* Convert between us_data and the corresponding Scsi_Host */
-static struct Scsi_Host inline *us_to_host(struct us_data *us) {
+static inline struct Scsi_Host *us_to_host(struct us_data *us) {
return container_of((void *) us, struct Scsi_Host, hostdata);
}
-static struct us_data inline *host_to_us(struct Scsi_Host *host) {
+static inline struct us_data *host_to_us(struct Scsi_Host *host) {
return (struct us_data *) host->hostdata;
}
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 168ede7902bd..17de4c84db69 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -4,6 +4,21 @@
menu "Graphics support"
+config FIRMWARE_EDID
+ bool "Enable firmware EDID"
+ default y
+ ---help---
+ This enables access to the EDID transferred from the firmware.
+ On the i386, this is from the Video BIOS. Enable this if DDC/I2C
+ transfers do not work for your driver and if you are using
+ nvidiafb, i810fb or savagefb.
+
+ In general, choosing Y for this option is safe. If you
+ experience extremely long delays while booting before you get
+ something on your display, try setting this to N. Matrox cards in
+ combination with certain motherboards and monitors are known to
+ suffer from this problem.
+
config FB
tristate "Support for frame buffer devices"
---help---
@@ -70,22 +85,6 @@ config FB_MACMODES
depends on FB
default n
-config FB_FIRMWARE_EDID
- bool "Enable firmware EDID"
- depends on FB
- default y
- ---help---
- This enables access to the EDID transferred from the firmware.
- On the i386, this is from the Video BIOS. Enable this if DDC/I2C
- transfers do not work for your driver and if you are using
- nvidiafb, i810fb or savagefb.
-
- In general, choosing Y for this option is safe. If you
- experience extremely long delays while booting before you get
- something on your display, try setting this to N. Matrox cards in
- combination with certain motherboards and monitors are known to
- suffer from this problem.
-
config FB_BACKLIGHT
bool
depends on FB
@@ -551,10 +550,14 @@ config FB_VESA
You will get a boot time penguin logo at no additional cost. Please
read <file:Documentation/fb/vesafb.txt>. If unsure, say Y.
-config VIDEO_SELECT
- bool
- depends on FB_VESA
- default y
+config FB_IMAC
+ bool "Intel-based Macintosh Framebuffer Support"
+ depends on (FB = y) && X86
+ select FB_CFB_FILLRECT
+ select FB_CFB_COPYAREA
+ select FB_CFB_IMAGEBLIT
+ help
+ This is the frame buffer device driver for the Intel-based Macintosh
config FB_HGA
tristate "Hercules mono graphics support"
@@ -578,12 +581,6 @@ config FB_HGA_ACCEL
This will compile the Hercules mono graphics with
acceleration functions.
-
-config VIDEO_SELECT
- bool
- depends on (FB = y) && X86
- default y
-
config FB_SGIVW
tristate "SGI Visual Workstation framebuffer support"
depends on FB && X86_VISWS
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 23de3b2c7856..c335e9bc3b20 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -4,15 +4,15 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_VT) += console/
-obj-$(CONFIG_LOGO) += logo/
-obj-$(CONFIG_SYSFS) += backlight/
-
obj-$(CONFIG_FB) += fb.o
fb-y := fbmem.o fbmon.o fbcmap.o fbsysfs.o \
modedb.o fbcvt.o
fb-objs := $(fb-y)
+obj-$(CONFIG_VT) += console/
+obj-$(CONFIG_LOGO) += logo/
+obj-$(CONFIG_SYSFS) += backlight/
+
obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_S3C2410) += s3c2410fb.o
# Platform or fallback drivers go here
obj-$(CONFIG_FB_VESA) += vesafb.o
+obj-$(CONFIG_FB_IMAC) += imacfb.o
obj-$(CONFIG_FB_VGA16) += vga16fb.o vgastate.o
obj-$(CONFIG_FB_OF) += offb.o
diff --git a/drivers/video/aty/aty128fb.c b/drivers/video/aty/aty128fb.c
index db878fd55fb2..11cf7fcb1d55 100644
--- a/drivers/video/aty/aty128fb.c
+++ b/drivers/video/aty/aty128fb.c
@@ -100,7 +100,7 @@
#ifndef CONFIG_PPC_PMAC
/* default mode */
-static struct fb_var_screeninfo default_var __initdata = {
+static struct fb_var_screeninfo default_var __devinitdata = {
/* 640x480, 60 Hz, Non-Interlaced (25.175 MHz dotclock) */
640, 480, 640, 480, 0, 0, 8, 0,
{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -123,7 +123,7 @@ static struct fb_var_screeninfo default_var = {
/* default modedb mode */
/* 640x480, 60 Hz, Non-Interlaced (25.172 MHz dotclock) */
-static struct fb_videomode defaultmode __initdata = {
+static struct fb_videomode defaultmode __devinitdata = {
.refresh = 60,
.xres = 640,
.yres = 480,
@@ -335,7 +335,7 @@ static const struct aty128_meminfo sdr_sgram =
static const struct aty128_meminfo ddr_sgram =
{ 4, 4, 3, 3, 2, 3, 1, 16, 31, 16, "64-bit DDR SGRAM" };
-static struct fb_fix_screeninfo aty128fb_fix __initdata = {
+static struct fb_fix_screeninfo aty128fb_fix __devinitdata = {
.id = "ATY Rage128",
.type = FB_TYPE_PACKED_PIXELS,
.visual = FB_VISUAL_PSEUDOCOLOR,
@@ -345,15 +345,15 @@ static struct fb_fix_screeninfo aty128fb_fix __initdata = {
.accel = FB_ACCEL_ATI_RAGE128,
};
-static char *mode_option __initdata = NULL;
+static char *mode_option __devinitdata = NULL;
#ifdef CONFIG_PPC_PMAC
-static int default_vmode __initdata = VMODE_1024_768_60;
-static int default_cmode __initdata = CMODE_8;
+static int default_vmode __devinitdata = VMODE_1024_768_60;
+static int default_cmode __devinitdata = CMODE_8;
#endif
-static int default_crt_on __initdata = 0;
-static int default_lcd_on __initdata = 1;
+static int default_crt_on __devinitdata = 0;
+static int default_lcd_on __devinitdata = 1;
#ifdef CONFIG_MTRR
static int mtrr = 1;
@@ -445,9 +445,9 @@ static int aty128_encode_var(struct fb_var_screeninfo *var,
static int aty128_decode_var(struct fb_var_screeninfo *var,
struct aty128fb_par *par);
#if 0
-static void __init aty128_get_pllinfo(struct aty128fb_par *par,
+static void __devinit aty128_get_pllinfo(struct aty128fb_par *par,
void __iomem *bios);
-static void __init __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
+static void __devinit __iomem *aty128_map_ROM(struct pci_dev *pdev, const struct aty128fb_par *par);
#endif
static void aty128_timings(struct aty128fb_par *par);
static void aty128_init_engine(struct aty128fb_par *par);
@@ -573,7 +573,7 @@ static void aty_pll_writeupdate(const struct aty128fb_par *par)
/* write to the scratch register to test r/w functionality */
-static int __init register_test(const struct aty128fb_par *par)
+static int __devinit register_test(const struct aty128fb_par *par)
{
u32 val;
int flag = 0;
@@ -772,7 +772,7 @@ static u32 depth_to_dst(u32 depth)
#ifndef __sparc__
-static void __iomem * __init aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
+static void __iomem * __devinit aty128_map_ROM(const struct aty128fb_par *par, struct pci_dev *dev)
{
u16 dptr;
u8 rom_type;
@@ -856,7 +856,7 @@ static void __iomem * __init aty128_map_ROM(const struct aty128fb_par *par, stru
return NULL;
}
-static void __init aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
+static void __devinit aty128_get_pllinfo(struct aty128fb_par *par, unsigned char __iomem *bios)
{
unsigned int bios_hdr;
unsigned int bios_pll;
@@ -903,7 +903,7 @@ static void __iomem * __devinit aty128_find_mem_vbios(struct aty128fb_par *par)
#endif /* ndef(__sparc__) */
/* fill in known card constants if pll_block is not available */
-static void __init aty128_timings(struct aty128fb_par *par)
+static void __devinit aty128_timings(struct aty128fb_par *par)
{
#ifdef CONFIG_PPC_OF
/* instead of a table lookup, assume OF has properly
@@ -1645,7 +1645,7 @@ static int aty128fb_sync(struct fb_info *info)
}
#ifndef MODULE
-static int __init aty128fb_setup(char *options)
+static int __devinit aty128fb_setup(char *options)
{
char *this_opt;
@@ -1893,7 +1893,7 @@ static void aty128_early_resume(void *data)
}
#endif /* CONFIG_PPC_PMAC */
-static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_init(struct pci_dev *pdev, const struct pci_device_id *ent)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct aty128fb_par *par = info->par;
@@ -2037,7 +2037,7 @@ static int __init aty128_init(struct pci_dev *pdev, const struct pci_device_id *
#ifdef CONFIG_PCI
/* register a card ++ajoshi */
-static int __init aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+static int __devinit aty128_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
unsigned long fb_addr, reg_addr;
struct aty128fb_par *par;
@@ -2556,7 +2556,7 @@ static int aty128_pci_resume(struct pci_dev *pdev)
}
-static int __init aty128fb_init(void)
+static int __devinit aty128fb_init(void)
{
#ifndef MODULE
char *option = NULL;
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index c5185f7cf4ba..22e720611bf6 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -316,12 +316,12 @@ static int vram;
static int pll;
static int mclk;
static int xclk;
-static int comp_sync __initdata = -1;
+static int comp_sync __devinitdata = -1;
static char *mode;
#ifdef CONFIG_PPC
-static int default_vmode __initdata = VMODE_CHOOSE;
-static int default_cmode __initdata = CMODE_CHOOSE;
+static int default_vmode __devinitdata = VMODE_CHOOSE;
+static int default_cmode __devinitdata = CMODE_CHOOSE;
module_param_named(vmode, default_vmode, int, 0);
MODULE_PARM_DESC(vmode, "int: video mode for mac");
@@ -330,10 +330,10 @@ MODULE_PARM_DESC(cmode, "int: color mode for mac");
#endif
#ifdef CONFIG_ATARI
-static unsigned int mach64_count __initdata = 0;
-static unsigned long phys_vmembase[FB_MAX] __initdata = { 0, };
-static unsigned long phys_size[FB_MAX] __initdata = { 0, };
-static unsigned long phys_guiregbase[FB_MAX] __initdata = { 0, };
+static unsigned int mach64_count __devinitdata = 0;
+static unsigned long phys_vmembase[FB_MAX] __devinitdata = { 0, };
+static unsigned long phys_size[FB_MAX] __devinitdata = { 0, };
+static unsigned long phys_guiregbase[FB_MAX] __devinitdata = { 0, };
#endif
/* top -> down is an evolution of mach64 chipset, any corrections? */
@@ -583,7 +583,7 @@ static u32 atyfb_get_pixclock(struct fb_var_screeninfo *var, struct atyfb_par *p
* Apple monitor sense
*/
-static int __init read_aty_sense(const struct atyfb_par *par)
+static int __devinit read_aty_sense(const struct atyfb_par *par)
{
int sense, i;
@@ -1281,6 +1281,14 @@ static int atyfb_set_par(struct fb_info *info)
par->accel_flags = var->accel_flags; /* hack */
+ if (var->accel_flags) {
+ info->fbops->fb_sync = atyfb_sync;
+ info->flags &= ~FBINFO_HWACCEL_DISABLED;
+ } else {
+ info->fbops->fb_sync = NULL;
+ info->flags |= FBINFO_HWACCEL_DISABLED;
+ }
+
if (par->blitter_may_be_busy)
wait_for_idle(par);
@@ -2253,7 +2261,7 @@ static void aty_bl_exit(struct atyfb_par *par)
#endif /* CONFIG_FB_ATY_BACKLIGHT */
-static void __init aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
+static void __devinit aty_calc_mem_refresh(struct atyfb_par *par, int xclk)
{
const int ragepro_tbl[] = {
44, 50, 55, 66, 75, 80, 100
@@ -2313,7 +2321,7 @@ static int __devinit atyfb_get_timings_from_lcd(struct atyfb_par *par,
}
#endif /* defined(__i386__) && defined(CONFIG_FB_ATY_GENERIC_LCD) */
-static int __init aty_init(struct fb_info *info, const char *name)
+static int __devinit aty_init(struct fb_info *info, const char *name)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
const char *ramname = NULL, *xtal;
@@ -2394,12 +2402,15 @@ static int __init aty_init(struct fb_info *info, const char *name)
break;
}
switch (clk_type) {
+#ifdef CONFIG_ATARI
case CLK_ATI18818_1:
par->pll_ops = &aty_pll_ati18818_1;
break;
+#else
case CLK_IBMRGB514:
par->pll_ops = &aty_pll_ibm514;
break;
+#endif
#if 0 /* dead code */
case CLK_STG1703:
par->pll_ops = &aty_pll_stg1703;
@@ -2604,7 +2615,11 @@ static int __init aty_init(struct fb_info *info, const char *name)
info->fbops = &atyfb_ops;
info->pseudo_palette = pseudo_palette;
- info->flags = FBINFO_FLAG_DEFAULT;
+ info->flags = FBINFO_DEFAULT |
+ FBINFO_HWACCEL_IMAGEBLIT |
+ FBINFO_HWACCEL_FILLRECT |
+ FBINFO_HWACCEL_COPYAREA |
+ FBINFO_HWACCEL_YPAN;
#ifdef CONFIG_PMAC_BACKLIGHT
if (M64_HAS(G3_PB_1_1) && machine_is_compatible("PowerBook1,1")) {
@@ -2733,7 +2748,7 @@ aty_init_exit:
}
#ifdef CONFIG_ATARI
-static int __init store_video_par(char *video_str, unsigned char m64_num)
+static int __devinit store_video_par(char *video_str, unsigned char m64_num)
{
char *p;
unsigned long vmembase, size, guiregbase;
@@ -3764,7 +3779,7 @@ static struct pci_driver atyfb_driver = {
#endif /* CONFIG_PCI */
#ifndef MODULE
-static int __init atyfb_setup(char *options)
+static int __devinit atyfb_setup(char *options)
{
char *this_opt;
@@ -3836,7 +3851,7 @@ static int __init atyfb_setup(char *options)
}
#endif /* MODULE */
-static int __init atyfb_init(void)
+static int __devinit atyfb_init(void)
{
#ifndef MODULE
char *option = NULL;
diff --git a/drivers/video/aty/mach64_accel.c b/drivers/video/aty/mach64_accel.c
index c98f4a442134..1490e5e1c232 100644
--- a/drivers/video/aty/mach64_accel.c
+++ b/drivers/video/aty/mach64_accel.c
@@ -200,8 +200,6 @@ void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area)
if (!area->width || !area->height)
return;
if (!par->accel_flags) {
- if (par->blitter_may_be_busy)
- wait_for_idle(par);
cfb_copyarea(info, area);
return;
}
@@ -248,8 +246,6 @@ void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect)
if (!rect->width || !rect->height)
return;
if (!par->accel_flags) {
- if (par->blitter_may_be_busy)
- wait_for_idle(par);
cfb_fillrect(info, rect);
return;
}
@@ -288,14 +284,10 @@ void atyfb_imageblit(struct fb_info *info, const struct fb_image *image)
return;
if (!par->accel_flags ||
(image->depth != 1 && info->var.bits_per_pixel != image->depth)) {
- if (par->blitter_may_be_busy)
- wait_for_idle(par);
-
cfb_imageblit(info, image);
return;
}
- wait_for_idle(par);
pix_width = pix_width_save = aty_ld_le32(DP_PIX_WIDTH, par);
host_cntl = aty_ld_le32(HOST_CNTL, par) | HOST_BYTE_ALIGN;
@@ -425,8 +417,6 @@ void atyfb_imageblit(struct fb_info *info, const struct fb_image *image)
}
}
- wait_for_idle(par);
-
/* restore pix_width */
wait_for_fifo(1, par);
aty_st_le32(DP_PIX_WIDTH, pix_width_save, par);
diff --git a/drivers/video/aty/mach64_cursor.c b/drivers/video/aty/mach64_cursor.c
index ad8b7496f853..2a7f381c330f 100644
--- a/drivers/video/aty/mach64_cursor.c
+++ b/drivers/video/aty/mach64_cursor.c
@@ -66,11 +66,6 @@ static const u8 cursor_bits_lookup[16] = {
0x01, 0x41, 0x11, 0x51, 0x05, 0x45, 0x15, 0x55
};
-static const u8 cursor_mask_lookup[16] = {
- 0xaa, 0x2a, 0x8a, 0x0a, 0xa2, 0x22, 0x82, 0x02,
- 0xa8, 0x28, 0x88, 0x08, 0xa0, 0x20, 0x80, 0x00
-};
-
static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
struct atyfb_par *par = (struct atyfb_par *) info->par;
@@ -130,13 +125,13 @@ static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
fg_idx = cursor->image.fg_color;
bg_idx = cursor->image.bg_color;
- fg = (info->cmap.red[fg_idx] << 24) |
- (info->cmap.green[fg_idx] << 16) |
- (info->cmap.blue[fg_idx] << 8) | 15;
+ fg = ((info->cmap.red[fg_idx] & 0xff) << 24) |
+ ((info->cmap.green[fg_idx] & 0xff) << 16) |
+ ((info->cmap.blue[fg_idx] & 0xff) << 8) | 0xff;
- bg = (info->cmap.red[bg_idx] << 24) |
- (info->cmap.green[bg_idx] << 16) |
- (info->cmap.blue[bg_idx] << 8);
+ bg = ((info->cmap.red[bg_idx] & 0xff) << 24) |
+ ((info->cmap.green[bg_idx] & 0xff) << 16) |
+ ((info->cmap.blue[bg_idx] & 0xff) << 8);
wait_for_fifo(2, par);
aty_st_le32(CUR_CLR0, bg, par);
@@ -166,19 +161,17 @@ static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
switch (cursor->rop) {
case ROP_XOR:
// Upper 4 bits of mask data
- fb_writeb(cursor_mask_lookup[m >> 4 ] |
- cursor_bits_lookup[(b ^ m) >> 4], dst++);
+ fb_writeb(cursor_bits_lookup[(b ^ m) >> 4], dst++);
// Lower 4 bits of mask
- fb_writeb(cursor_mask_lookup[m & 0x0f ] |
- cursor_bits_lookup[(b ^ m) & 0x0f], dst++);
+ fb_writeb(cursor_bits_lookup[(b ^ m) & 0x0f],
+ dst++);
break;
case ROP_COPY:
// Upper 4 bits of mask data
- fb_writeb(cursor_mask_lookup[m >> 4 ] |
- cursor_bits_lookup[(b & m) >> 4], dst++);
+ fb_writeb(cursor_bits_lookup[(b & m) >> 4], dst++);
// Lower 4 bits of mask
- fb_writeb(cursor_mask_lookup[m & 0x0f ] |
- cursor_bits_lookup[(b & m) & 0x0f], dst++);
+ fb_writeb(cursor_bits_lookup[(b & m) & 0x0f],
+ dst++);
break;
}
}
@@ -194,7 +187,7 @@ static int atyfb_cursor(struct fb_info *info, struct fb_cursor *cursor)
return 0;
}
-int __init aty_init_cursor(struct fb_info *info)
+int __devinit aty_init_cursor(struct fb_info *info)
{
unsigned long addr;
diff --git a/drivers/video/aty/radeon_backlight.c b/drivers/video/aty/radeon_backlight.c
index 7de66b855d4e..1755dddf1899 100644
--- a/drivers/video/aty/radeon_backlight.c
+++ b/drivers/video/aty/radeon_backlight.c
@@ -40,14 +40,14 @@ static int radeon_bl_get_level_brightness(struct radeon_bl_privdata *pdata,
mutex_unlock(&info->bl_mutex);
- if (pdata->negative)
- rlevel = MAX_RADEON_LEVEL - rlevel;
-
if (rlevel < 0)
rlevel = 0;
else if (rlevel > MAX_RADEON_LEVEL)
rlevel = MAX_RADEON_LEVEL;
+ if (pdata->negative)
+ rlevel = MAX_RADEON_LEVEL - rlevel;
+
return rlevel;
}
diff --git a/drivers/video/aty/radeon_base.c b/drivers/video/aty/radeon_base.c
index c5ecbb02e01d..68b15645b893 100644
--- a/drivers/video/aty/radeon_base.c
+++ b/drivers/video/aty/radeon_base.c
@@ -2379,7 +2379,6 @@ err_release_pci0:
err_release_fb:
framebuffer_release(info);
err_disable:
- pci_disable_device(pdev);
err_out:
return ret;
}
@@ -2436,7 +2435,6 @@ static void __devexit radeonfb_pci_unregister (struct pci_dev *pdev)
#endif
fb_dealloc_cmap(&info->cmap);
framebuffer_release(info);
- pci_disable_device(pdev);
}
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index 789450bb0bc9..9ef68cd83bb4 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -7,6 +7,8 @@
* Karl Lessard <klessard@sunrisetelecom.com>
* <c.pellegrin@exadron.com>
*
+ * PM support added by Rodolfo Giometti <giometti@linux.it>
+ *
* Copyright 2002 MontaVista Software
* Author: MontaVista Software, Inc.
* ppopov@mvista.com or source@mvista.com
@@ -602,17 +604,52 @@ int au1100fb_drv_remove(struct device *dev)
return 0;
}
+#ifdef CONFIG_PM
+static u32 sys_clksrc;
+static struct au1100fb_regs fbregs;
+
int au1100fb_drv_suspend(struct device *dev, pm_message_t state)
{
- /* TODO */
+ struct au1100fb_device *fbdev = dev_get_drvdata(dev);
+
+ if (!fbdev)
+ return 0;
+
+ /* Save the clock source state */
+ sys_clksrc = au_readl(SYS_CLKSRC);
+
+ /* Blank the LCD */
+ au1100fb_fb_blank(VESA_POWERDOWN, &fbdev->info);
+
+ /* Stop LCD clocking */
+ au_writel(sys_clksrc & ~SYS_CS_ML_MASK, SYS_CLKSRC);
+
+ memcpy(&fbregs, fbdev->regs, sizeof(struct au1100fb_regs));
+
return 0;
}
int au1100fb_drv_resume(struct device *dev)
{
- /* TODO */
+ struct au1100fb_device *fbdev = dev_get_drvdata(dev);
+
+ if (!fbdev)
+ return 0;
+
+ memcpy(fbdev->regs, &fbregs, sizeof(struct au1100fb_regs));
+
+ /* Restart LCD clocking */
+ au_writel(sys_clksrc, SYS_CLKSRC);
+
+ /* Unblank the LCD */
+ au1100fb_fb_blank(VESA_NO_BLANKING, &fbdev->info);
+
return 0;
}
+#else
+#define au1100fb_drv_suspend NULL
+#define au1100fb_drv_resume NULL
+#endif
static struct device_driver au1100fb_driver = {
.name = "au1100-lcd",
@@ -706,8 +743,7 @@ void __exit au1100fb_cleanup(void)
{
driver_unregister(&au1100fb_driver);
- if (drv_info.opt_mode)
- kfree(drv_info.opt_mode);
+ kfree(drv_info.opt_mode);
}
module_init(au1100fb_init);
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index b895eaaa73fd..022f9d3473f5 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -10,7 +10,7 @@ menuconfig BACKLIGHT_LCD_SUPPORT
config BACKLIGHT_CLASS_DEVICE
tristate "Lowlevel Backlight controls"
- depends on BACKLIGHT_LCD_SUPPORT
+ depends on BACKLIGHT_LCD_SUPPORT && FB
default m
help
This framework adds support for low-level control of the LCD
@@ -26,7 +26,7 @@ config BACKLIGHT_DEVICE
config LCD_CLASS_DEVICE
tristate "Lowlevel LCD controls"
- depends on BACKLIGHT_LCD_SUPPORT
+ depends on BACKLIGHT_LCD_SUPPORT && FB
default m
help
This framework adds support for low-level control of LCD.
@@ -50,6 +50,14 @@ config BACKLIGHT_CORGI
If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the
backlight driver.
+config BACKLIGHT_LOCOMO
+ tristate "Sharp LOCOMO LCD/Backlight Driver"
+ depends on BACKLIGHT_DEVICE && SHARP_LOCOMO
+ default y
+ help
+ If you have a Sharp Zaurus SL-5500 (Collie) or SL-5600 (Poodle) say y to
+ enable the LCD/backlight driver.
+
config BACKLIGHT_HP680
tristate "HP Jornada 680 Backlight Driver"
depends on BACKLIGHT_DEVICE && SH_HP6XX
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index 744210c38e74..65e5553fc849 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -4,4 +4,4 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o
obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o
obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o
obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o
-obj-$(CONFIG_SHARP_LOCOMO) += locomolcd.o
+obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index a71e984c93d4..ffc72ae3ada8 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -27,7 +27,7 @@
static int hp680bl_suspended;
static int current_intensity = 0;
-static spinlock_t bl_lock = SPIN_LOCK_UNLOCKED;
+static DEFINE_SPINLOCK(bl_lock);
static struct backlight_device *hp680_backlight_device;
static void hp680bl_send_intensity(struct backlight_device *bd)
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index 60831bb23685..bd879b7ec119 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -17,6 +17,8 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/interrupt.h>
+#include <linux/fb.h>
+#include <linux/backlight.h>
#include <asm/hardware/locomo.h>
#include <asm/irq.h>
@@ -25,7 +27,10 @@
#include "../../../arch/arm/mach-sa1100/generic.h"
+static struct backlight_device *locomolcd_bl_device;
static struct locomo_dev *locomolcd_dev;
+static unsigned long locomolcd_flags;
+#define LOCOMOLCD_SUSPENDED 0x01
static void locomolcd_on(int comadj)
{
@@ -89,12 +94,10 @@ void locomolcd_power(int on)
}
/* read comadj */
- if (comadj == -1) {
- if (machine_is_poodle())
- comadj = 118;
- if (machine_is_collie())
- comadj = 128;
- }
+ if (comadj == -1 && machine_is_collie())
+ comadj = 128;
+ if (comadj == -1 && machine_is_poodle())
+ comadj = 118;
if (on)
locomolcd_on(comadj);
@@ -105,26 +108,100 @@ void locomolcd_power(int on)
}
EXPORT_SYMBOL(locomolcd_power);
-static int poodle_lcd_probe(struct locomo_dev *dev)
+
+static int current_intensity;
+
+static int locomolcd_set_intensity(struct backlight_device *bd)
+{
+ int intensity = bd->props->brightness;
+
+ if (bd->props->power != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (bd->props->fb_blank != FB_BLANK_UNBLANK)
+ intensity = 0;
+ if (locomolcd_flags & LOCOMOLCD_SUSPENDED)
+ intensity = 0;
+
+ switch (intensity) {
+ /* AC and non-AC are handled differently, but produce same results in sharp code? */
+ case 0: locomo_frontlight_set(locomolcd_dev, 0, 0, 161); break;
+ case 1: locomo_frontlight_set(locomolcd_dev, 117, 0, 161); break;
+ case 2: locomo_frontlight_set(locomolcd_dev, 163, 0, 148); break;
+ case 3: locomo_frontlight_set(locomolcd_dev, 194, 0, 161); break;
+ case 4: locomo_frontlight_set(locomolcd_dev, 194, 1, 161); break;
+
+ default:
+ return -ENODEV;
+ }
+ current_intensity = intensity;
+ return 0;
+}
+
+static int locomolcd_get_intensity(struct backlight_device *bd)
+{
+ return current_intensity;
+}
+
+static struct backlight_properties locomobl_data = {
+ .owner = THIS_MODULE,
+ .get_brightness = locomolcd_get_intensity,
+ .update_status = locomolcd_set_intensity,
+ .max_brightness = 4,
+};
+
+#ifdef CONFIG_PM
+static int locomolcd_suspend(struct locomo_dev *dev, pm_message_t state)
+{
+ locomolcd_flags |= LOCOMOLCD_SUSPENDED;
+ locomolcd_set_intensity(locomolcd_bl_device);
+ return 0;
+}
+
+static int locomolcd_resume(struct locomo_dev *dev)
+{
+ locomolcd_flags &= ~LOCOMOLCD_SUSPENDED;
+ locomolcd_set_intensity(locomolcd_bl_device);
+ return 0;
+}
+#else
+#define locomolcd_suspend NULL
+#define locomolcd_resume NULL
+#endif
+
+static int locomolcd_probe(struct locomo_dev *dev)
{
unsigned long flags;
local_irq_save(flags);
locomolcd_dev = dev;
+ locomo_gpio_set_dir(dev, LOCOMO_GPIO_FL_VR, 0);
+
/* the poodle_lcd_power function is called for the first time
* from fs_initcall, which is before locomo is activated.
* We need to recall poodle_lcd_power here*/
-#ifdef CONFIG_MACH_POODLE
- locomolcd_power(1);
-#endif
+ if (machine_is_poodle())
+ locomolcd_power(1);
+
local_irq_restore(flags);
+
+ locomolcd_bl_device = backlight_device_register("locomo-bl", NULL, &locomobl_data);
+
+ if (IS_ERR (locomolcd_bl_device))
+ return PTR_ERR (locomolcd_bl_device);
+
+ /* Set up frontlight so that screen is readable */
+ locomobl_data.brightness = 2;
+ locomolcd_set_intensity(locomolcd_bl_device);
+
return 0;
}
-static int poodle_lcd_remove(struct locomo_dev *dev)
+static int locomolcd_remove(struct locomo_dev *dev)
{
unsigned long flags;
+
+ backlight_device_unregister(locomolcd_bl_device);
local_irq_save(flags);
locomolcd_dev = NULL;
local_irq_restore(flags);
@@ -136,19 +213,33 @@ static struct locomo_driver poodle_lcd_driver = {
.name = "locomo-backlight",
},
.devid = LOCOMO_DEVID_BACKLIGHT,
- .probe = poodle_lcd_probe,
- .remove = poodle_lcd_remove,
+ .probe = locomolcd_probe,
+ .remove = locomolcd_remove,
+ .suspend = locomolcd_suspend,
+ .resume = locomolcd_resume,
};
-static int __init poodle_lcd_init(void)
+
+static int __init locomolcd_init(void)
{
int ret = locomo_driver_register(&poodle_lcd_driver);
- if (ret) return ret;
+ if (ret)
+ return ret;
#ifdef CONFIG_SA1100_COLLIE
sa1100fb_lcd_power = locomolcd_power;
#endif
return 0;
}
-device_initcall(poodle_lcd_init);
+static void __exit locomolcd_exit(void)
+{
+ locomo_driver_unregister(&poodle_lcd_driver);
+}
+
+module_init(locomolcd_init);
+module_exit(locomolcd_exit);
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>, Pavel Machek <pavel@suse.cz>");
+MODULE_DESCRIPTION("Collie LCD driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/cfbimgblt.c b/drivers/video/cfbimgblt.c
index 8ba6152db2fd..ad8a89bf8eae 100644
--- a/drivers/video/cfbimgblt.c
+++ b/drivers/video/cfbimgblt.c
@@ -230,6 +230,7 @@ static inline void fast_imageblit(const struct fb_image *image, struct fb_info *
tab = cfb_tab16;
break;
case 32:
+ default:
tab = cfb_tab32;
break;
}
diff --git a/drivers/video/cirrusfb.c b/drivers/video/cirrusfb.c
index 1103010af54a..dda240eb7360 100644
--- a/drivers/video/cirrusfb.c
+++ b/drivers/video/cirrusfb.c
@@ -2227,7 +2227,6 @@ static void cirrusfb_pci_unmap (struct cirrusfb_info *cinfo)
release_region(0x3C0, 32);
pci_release_regions(pdev);
framebuffer_release(cinfo->info);
- pci_disable_device(pdev);
}
#endif /* CONFIG_PCI */
@@ -2458,7 +2457,6 @@ err_release_regions:
err_release_fb:
framebuffer_release(info);
err_disable:
- pci_disable_device(pdev);
err_out:
return ret;
}
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 47ba1a79adcd..5dc4083552d8 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -125,6 +125,8 @@ static int softback_lines;
static int first_fb_vc;
static int last_fb_vc = MAX_NR_CONSOLES - 1;
static int fbcon_is_default = 1;
+static int fbcon_has_exited;
+
/* font data */
static char fontname[40];
@@ -140,7 +142,6 @@ static const struct consw fb_con;
#define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row)
-static void fbcon_free_font(struct display *);
static int fbcon_set_origin(struct vc_data *);
#define CURSOR_DRAW_DELAY (1)
@@ -194,6 +195,9 @@ static void fbcon_redraw_move(struct vc_data *vc, struct display *p,
int line, int count, int dy);
static void fbcon_modechanged(struct fb_info *info);
static void fbcon_set_all_vcs(struct fb_info *info);
+static void fbcon_start(void);
+static void fbcon_exit(void);
+static struct class_device *fbcon_class_device;
#ifdef CONFIG_MAC
/*
@@ -252,7 +256,7 @@ static void fbcon_rotate_all(struct fb_info *info, u32 rotate)
if (!ops || ops->currcon < 0 || rotate > 3)
return;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
vc = vc_cons[i].d;
if (!vc || vc->vc_mode != KD_TEXT ||
registered_fb[con2fb_map[i]] != info)
@@ -389,15 +393,18 @@ static void fb_flashcursor(void *private)
int c;
int mode;
- if (ops->currcon != -1)
+ acquire_console_sem();
+ if (ops && ops->currcon != -1)
vc = vc_cons[ops->currcon].d;
if (!vc || !CON_IS_VISIBLE(vc) ||
fbcon_is_inactive(vc, info) ||
registered_fb[con2fb_map[vc->vc_num]] != info ||
- vc_cons[ops->currcon].d->vc_deccm != 1)
+ vc_cons[ops->currcon].d->vc_deccm != 1) {
+ release_console_sem();
return;
- acquire_console_sem();
+ }
+
p = &fb_display[vc->vc_num];
c = scr_readw((u16 *) vc->vc_pos);
mode = (!ops->cursor_flash || ops->cursor_state.enable) ?
@@ -528,7 +535,7 @@ static int search_fb_in_map(int idx)
{
int i, retval = 0;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] == idx)
retval = 1;
}
@@ -539,7 +546,7 @@ static int search_for_mapped_con(void)
{
int i, retval = 0;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map[i] != -1)
retval = 1;
}
@@ -561,6 +568,7 @@ static int fbcon_takeover(int show_logo)
err = take_over_console(&fb_con, first_fb_vc, last_fb_vc,
fbcon_is_default);
+
if (err) {
for (i = first_fb_vc; i <= last_fb_vc; i++) {
con2fb_map[i] = -1;
@@ -795,8 +803,8 @@ static int set_con2fb_map(int unit, int newidx, int user)
if (oldidx == newidx)
return 0;
- if (!info)
- err = -EINVAL;
+ if (!info || fbcon_has_exited)
+ return -EINVAL;
if (!err && !search_for_mapped_con()) {
info_idx = newidx;
@@ -832,6 +840,9 @@ static int set_con2fb_map(int unit, int newidx, int user)
con2fb_init_display(vc, info, unit, show_logo);
}
+ if (!search_fb_in_map(info_idx))
+ info_idx = newidx;
+
release_console_sem();
return err;
}
@@ -1034,6 +1045,7 @@ static const char *fbcon_startup(void)
#endif /* CONFIG_MAC */
fbcon_add_cursor_timer(info);
+ fbcon_has_exited = 0;
return display_desc;
}
@@ -1061,17 +1073,36 @@ static void fbcon_init(struct vc_data *vc, int init)
/* If we are not the first console on this
fb, copy the font from that console */
- t = &fb_display[svc->vc_num];
- if (!vc->vc_font.data) {
- vc->vc_font.data = (void *)(p->fontdata = t->fontdata);
- vc->vc_font.width = (*default_mode)->vc_font.width;
- vc->vc_font.height = (*default_mode)->vc_font.height;
- p->userfont = t->userfont;
- if (p->userfont)
- REFCOUNT(p->fontdata)++;
+ t = &fb_display[fg_console];
+ if (!p->fontdata) {
+ if (t->fontdata) {
+ struct vc_data *fvc = vc_cons[fg_console].d;
+
+ vc->vc_font.data = (void *)(p->fontdata =
+ fvc->vc_font.data);
+ vc->vc_font.width = fvc->vc_font.width;
+ vc->vc_font.height = fvc->vc_font.height;
+ p->userfont = t->userfont;
+
+ if (p->userfont)
+ REFCOUNT(p->fontdata)++;
+ } else {
+ const struct font_desc *font = NULL;
+
+ if (!fontname[0] || !(font = find_font(fontname)))
+ font = get_default_font(info->var.xres,
+ info->var.yres);
+ vc->vc_font.width = font->width;
+ vc->vc_font.height = font->height;
+ vc->vc_font.data = (void *)(p->fontdata = font->data);
+ vc->vc_font.charcount = 256; /* FIXME Need to
+ support more fonts */
+ }
}
+
if (p->userfont)
charcnt = FNTCHARCNT(p->fontdata);
+
vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1);
vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800;
if (charcnt == 256) {
@@ -1145,13 +1176,47 @@ static void fbcon_init(struct vc_data *vc, int init)
ops->p = &fb_display[fg_console];
}
+static void fbcon_free_font(struct display *p)
+{
+ if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
+ kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
+ p->fontdata = NULL;
+ p->userfont = 0;
+}
+
static void fbcon_deinit(struct vc_data *vc)
{
struct display *p = &fb_display[vc->vc_num];
+ struct fb_info *info;
+ struct fbcon_ops *ops;
+ int idx;
- if (info_idx != -1)
- return;
fbcon_free_font(p);
+ idx = con2fb_map[vc->vc_num];
+
+ if (idx == -1)
+ goto finished;
+
+ info = registered_fb[idx];
+
+ if (!info)
+ goto finished;
+
+ ops = info->fbcon_par;
+
+ if (!ops)
+ goto finished;
+
+ if (CON_IS_VISIBLE(vc))
+ fbcon_del_cursor_timer(info);
+
+ ops->flags &= ~FBCON_FLAGS_INIT;
+finished:
+
+ if (!con_is_bound(&fb_con))
+ fbcon_exit();
+
+ return;
}
/* ====================================================================== */
@@ -2099,12 +2164,11 @@ static int fbcon_switch(struct vc_data *vc)
if (info->fbops->fb_set_par)
info->fbops->fb_set_par(info);
- if (old_info != info) {
+ if (old_info != info)
fbcon_del_cursor_timer(old_info);
- fbcon_add_cursor_timer(info);
- }
}
+ fbcon_add_cursor_timer(info);
set_blitting_type(vc, info);
ops->cursor_reset = 1;
@@ -2222,14 +2286,6 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
return 0;
}
-static void fbcon_free_font(struct display *p)
-{
- if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0))
- kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int));
- p->fontdata = NULL;
- p->userfont = 0;
-}
-
static int fbcon_get_font(struct vc_data *vc, struct console_font *font)
{
u8 *fontdata = vc->vc_font.data;
@@ -2443,7 +2499,7 @@ static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigne
FNTSUM(new_data) = csum;
/* Check if the same font is on some other console already */
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
struct vc_data *tmp = vc_cons[i].d;
if (fb_display[i].userfont &&
@@ -2768,7 +2824,7 @@ static void fbcon_set_all_vcs(struct fb_info *info)
if (!ops || ops->currcon < 0)
return;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
vc = vc_cons[i].d;
if (!vc || vc->vc_mode != KD_TEXT ||
registered_fb[con2fb_map[i]] != info)
@@ -2830,22 +2886,57 @@ static int fbcon_mode_deleted(struct fb_info *info,
return found;
}
+static int fbcon_fb_unregistered(int idx)
+{
+ int i;
+
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+ if (con2fb_map[i] == idx)
+ con2fb_map[i] = -1;
+ }
+
+ if (idx == info_idx) {
+ info_idx = -1;
+
+ for (i = 0; i < FB_MAX; i++) {
+ if (registered_fb[i] != NULL) {
+ info_idx = i;
+ break;
+ }
+ }
+ }
+
+ if (info_idx != -1) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+ if (con2fb_map[i] == -1)
+ con2fb_map[i] = info_idx;
+ }
+ }
+
+ if (!num_registered_fb)
+ unregister_con_driver(&fb_con);
+
+ return 0;
+}
+
static int fbcon_fb_registered(int idx)
{
int ret = 0, i;
if (info_idx == -1) {
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (con2fb_map_boot[i] == idx) {
info_idx = idx;
break;
}
}
+
if (info_idx != -1)
ret = fbcon_takeover(1);
} else {
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- if (con2fb_map_boot[i] == idx)
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
+ if (con2fb_map_boot[i] == idx &&
+ con2fb_map[i] == -1)
set_con2fb_map(i, idx, 0);
}
}
@@ -2882,7 +2973,7 @@ static void fbcon_new_modelist(struct fb_info *info)
struct fb_var_screeninfo var;
struct fb_videomode *mode;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
+ for (i = first_fb_vc; i <= last_fb_vc; i++) {
if (registered_fb[con2fb_map[i]] != info)
continue;
if (!fb_display[i].mode)
@@ -2910,6 +3001,14 @@ static int fbcon_event_notify(struct notifier_block *self,
struct fb_con2fbmap *con2fb;
int ret = 0;
+ /*
+ * ignore all events except driver registration and deregistration
+ * if fbcon is not active
+ */
+ if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED ||
+ action == FB_EVENT_FB_UNREGISTERED))
+ goto done;
+
switch(action) {
case FB_EVENT_SUSPEND:
fbcon_suspended(info);
@@ -2930,6 +3029,9 @@ static int fbcon_event_notify(struct notifier_block *self,
case FB_EVENT_FB_REGISTERED:
ret = fbcon_fb_registered(info->node);
break;
+ case FB_EVENT_FB_UNREGISTERED:
+ ret = fbcon_fb_unregistered(info->node);
+ break;
case FB_EVENT_SET_CONSOLE_MAP:
con2fb = event->data;
ret = set_con2fb_map(con2fb->console - 1,
@@ -2945,16 +3047,9 @@ static int fbcon_event_notify(struct notifier_block *self,
case FB_EVENT_NEW_MODELIST:
fbcon_new_modelist(info);
break;
- case FB_EVENT_SET_CON_ROTATE:
- fbcon_rotate(info, *(int *)event->data);
- break;
- case FB_EVENT_GET_CON_ROTATE:
- ret = fbcon_get_rotate(info);
- break;
- case FB_EVENT_SET_CON_ROTATE_ALL:
- fbcon_rotate_all(info, *(int *)event->data);
}
+done:
return ret;
}
@@ -2992,27 +3087,181 @@ static struct notifier_block fbcon_event_notifier = {
.notifier_call = fbcon_event_notify,
};
-static int __init fb_console_init(void)
+static ssize_t store_rotate(struct class_device *class_device,
+ const char *buf, size_t count)
{
- int i;
+ struct fb_info *info;
+ int rotate, idx;
+ char **last = NULL;
+
+ if (fbcon_has_exited)
+ return count;
acquire_console_sem();
- fb_register_client(&fbcon_event_notifier);
+ idx = con2fb_map[fg_console];
+
+ if (idx == -1 || registered_fb[idx] == NULL)
+ goto err;
+
+ info = registered_fb[idx];
+ rotate = simple_strtoul(buf, last, 0);
+ fbcon_rotate(info, rotate);
+err:
release_console_sem();
+ return count;
+}
- for (i = 0; i < MAX_NR_CONSOLES; i++)
- con2fb_map[i] = -1;
+static ssize_t store_rotate_all(struct class_device *class_device,
+ const char *buf, size_t count)
+{
+ struct fb_info *info;
+ int rotate, idx;
+ char **last = NULL;
+
+ if (fbcon_has_exited)
+ return count;
+
+ acquire_console_sem();
+ idx = con2fb_map[fg_console];
+
+ if (idx == -1 || registered_fb[idx] == NULL)
+ goto err;
+ info = registered_fb[idx];
+ rotate = simple_strtoul(buf, last, 0);
+ fbcon_rotate_all(info, rotate);
+err:
+ release_console_sem();
+ return count;
+}
+
+static ssize_t show_rotate(struct class_device *class_device, char *buf)
+{
+ struct fb_info *info;
+ int rotate = 0, idx;
+
+ if (fbcon_has_exited)
+ return 0;
+
+ acquire_console_sem();
+ idx = con2fb_map[fg_console];
+
+ if (idx == -1 || registered_fb[idx] == NULL)
+ goto err;
+
+ info = registered_fb[idx];
+ rotate = fbcon_get_rotate(info);
+err:
+ release_console_sem();
+ return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
+}
+
+static struct class_device_attribute class_device_attrs[] = {
+ __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
+ __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all),
+};
+
+static int fbcon_init_class_device(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_create_file(fbcon_class_device,
+ &class_device_attrs[i]);
+ return 0;
+}
+
+static void fbcon_start(void)
+{
if (num_registered_fb) {
+ int i;
+
+ acquire_console_sem();
+
for (i = 0; i < FB_MAX; i++) {
if (registered_fb[i] != NULL) {
info_idx = i;
break;
}
}
+
+ release_console_sem();
fbcon_takeover(0);
}
+}
+
+static void fbcon_exit(void)
+{
+ struct fb_info *info;
+ int i, j, mapped;
+
+ if (fbcon_has_exited)
+ return;
+
+#ifdef CONFIG_ATARI
+ free_irq(IRQ_AUTO_4, fbcon_vbl_handler);
+#endif
+#ifdef CONFIG_MAC
+ if (MACH_IS_MAC && vbl_detected)
+ free_irq(IRQ_MAC_VBL, fbcon_vbl_handler);
+#endif
+
+ kfree((void *)softback_buf);
+ softback_buf = 0UL;
+
+ for (i = 0; i < FB_MAX; i++) {
+ mapped = 0;
+ info = registered_fb[i];
+
+ if (info == NULL)
+ continue;
+
+ for (j = first_fb_vc; j <= last_fb_vc; j++) {
+ if (con2fb_map[j] == i)
+ mapped = 1;
+ }
+
+ if (mapped) {
+ if (info->fbops->fb_release)
+ info->fbops->fb_release(info, 0);
+ module_put(info->fbops->owner);
+
+ if (info->fbcon_par) {
+ fbcon_del_cursor_timer(info);
+ kfree(info->fbcon_par);
+ info->fbcon_par = NULL;
+ }
+ if (info->queue.func == fb_flashcursor)
+ info->queue.func = NULL;
+ }
+ }
+
+ fbcon_has_exited = 1;
+}
+
+static int __init fb_console_init(void)
+{
+ int i;
+
+ acquire_console_sem();
+ fb_register_client(&fbcon_event_notifier);
+ fbcon_class_device =
+ class_device_create(fb_class, NULL, MKDEV(0, 0), NULL, "fbcon");
+
+ if (IS_ERR(fbcon_class_device)) {
+ printk(KERN_WARNING "Unable to create class_device "
+ "for fbcon; errno = %ld\n",
+ PTR_ERR(fbcon_class_device));
+ fbcon_class_device = NULL;
+ } else
+ fbcon_init_class_device();
+
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ con2fb_map[i] = -1;
+
+ release_console_sem();
+ fbcon_start();
return 0;
}
@@ -3020,12 +3269,24 @@ module_init(fb_console_init);
#ifdef MODULE
+static void __exit fbcon_deinit_class_device(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_remove_file(fbcon_class_device,
+ &class_device_attrs[i]);
+}
+
static void __exit fb_console_exit(void)
{
acquire_console_sem();
fb_unregister_client(&fbcon_event_notifier);
+ fbcon_deinit_class_device();
+ class_device_destroy(fb_class, MKDEV(0, 0));
+ fbcon_exit();
release_console_sem();
- give_up_console(&fb_con);
+ unregister_con_driver(&fb_con);
}
module_exit(fb_console_exit);
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index c38c3d8e7a74..3487a636370a 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -175,6 +175,7 @@ extern void fbcon_set_tileops(struct vc_data *vc, struct fb_info *info);
#endif
extern void fbcon_set_bitops(struct fbcon_ops *ops);
extern int soft_cursor(struct fb_info *info, struct fb_cursor *cursor);
+extern struct class *fb_class;
#define FBCON_ATTRIBUTE_UNDERLINE 1
#define FBCON_ATTRIBUTE_REVERSE 2
diff --git a/drivers/video/console/mdacon.c b/drivers/video/console/mdacon.c
index 7f939d066a5a..c89f90edf8ac 100644
--- a/drivers/video/console/mdacon.c
+++ b/drivers/video/console/mdacon.c
@@ -308,7 +308,7 @@ static void __init mda_initialize(void)
outb_p(0x00, mda_gfx_port);
}
-static const char __init *mdacon_startup(void)
+static const char *mdacon_startup(void)
{
mda_num_columns = 80;
mda_num_lines = 25;
diff --git a/drivers/video/console/newport_con.c b/drivers/video/console/newport_con.c
index e99fe30e568c..03041311711b 100644
--- a/drivers/video/console/newport_con.c
+++ b/drivers/video/console/newport_con.c
@@ -51,6 +51,7 @@ static int topscan;
static int xcurs_correction = 29;
static int newport_xsize;
static int newport_ysize;
+static int newport_has_init;
static int newport_set_def_font(int unit, struct console_font *op);
@@ -283,6 +284,15 @@ static void newport_get_revisions(void)
xcurs_correction = 21;
}
+static void newport_exit(void)
+{
+ int i;
+
+ /* free memory used by user font */
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ newport_set_def_font(i, NULL);
+}
+
/* Can't be __init, take_over_console may call it later */
static const char *newport_startup(void)
{
@@ -290,8 +300,10 @@ static const char *newport_startup(void)
if (!sgi_gfxaddr)
return NULL;
- npregs = (struct newport_regs *) /* ioremap cannot fail */
- ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
+
+ if (!npregs)
+ npregs = (struct newport_regs *)/* ioremap cannot fail */
+ ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
npregs->cset.config = NPORT_CFG_GD0;
if (newport_wait(npregs))
@@ -307,11 +319,11 @@ static const char *newport_startup(void)
newport_reset();
newport_get_revisions();
newport_get_screensize();
+ newport_has_init = 1;
return "SGI Newport";
out_unmap:
- iounmap((void *)npregs);
return NULL;
}
@@ -324,11 +336,10 @@ static void newport_init(struct vc_data *vc, int init)
static void newport_deinit(struct vc_data *c)
{
- int i;
-
- /* free memory used by user font */
- for (i = 0; i < MAX_NR_CONSOLES; i++)
- newport_set_def_font(i, NULL);
+ if (!con_is_bound(&newport_con) && newport_has_init) {
+ newport_exit();
+ newport_has_init = 0;
+ }
}
static void newport_clear(struct vc_data *vc, int sy, int sx, int height,
@@ -728,16 +739,23 @@ const struct consw newport_con = {
#ifdef MODULE
static int __init newport_console_init(void)
{
+
+ if (!sgi_gfxaddr)
+ return NULL;
+
+ if (!npregs)
+ npregs = (struct newport_regs *)/* ioremap cannot fail */
+ ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
+
return take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
}
+module_init(newport_console_init);
static void __exit newport_console_exit(void)
{
give_up_console(&newport_con);
iounmap((void *)npregs);
}
-
-module_init(newport_console_init);
module_exit(newport_console_exit);
#endif
diff --git a/drivers/video/console/promcon.c b/drivers/video/console/promcon.c
index 04f42fcaac59..d6e6ad537f9f 100644
--- a/drivers/video/console/promcon.c
+++ b/drivers/video/console/promcon.c
@@ -109,7 +109,7 @@ promcon_end(struct vc_data *conp, char *b)
return b - p;
}
-const char __init *promcon_startup(void)
+const char *promcon_startup(void)
{
const char *display_desc = "PROM";
int node;
@@ -133,7 +133,7 @@ const char __init *promcon_startup(void)
return display_desc;
}
-static void __init
+static void
promcon_init_unimap(struct vc_data *conp)
{
mm_segment_t old_fs = get_fs();
diff --git a/drivers/video/console/sticon.c b/drivers/video/console/sticon.c
index fd5940f41271..45c4f227e56e 100644
--- a/drivers/video/console/sticon.c
+++ b/drivers/video/console/sticon.c
@@ -75,7 +75,7 @@ static inline void cursor_undrawn(void)
cursor_drawn = 0;
}
-static const char *__init sticon_startup(void)
+static const char *sticon_startup(void)
{
return "STI console";
}
diff --git a/drivers/video/console/vgacon.c b/drivers/video/console/vgacon.c
index e64d42e2449e..01401cd63ac0 100644
--- a/drivers/video/console/vgacon.c
+++ b/drivers/video/console/vgacon.c
@@ -114,6 +114,7 @@ static int vga_512_chars;
static int vga_video_font_height;
static int vga_scan_lines;
static unsigned int vga_rolled_over = 0;
+static int vga_init_done;
static int __init no_scroll(char *str)
{
@@ -190,7 +191,7 @@ static void vgacon_scrollback_init(int pitch)
}
}
-static void __init vgacon_scrollback_startup(void)
+static void vgacon_scrollback_startup(void)
{
vgacon_scrollback = alloc_bootmem(CONFIG_VGACON_SOFT_SCROLLBACK_SIZE
* 1024);
@@ -355,7 +356,7 @@ static int vgacon_scrolldelta(struct vc_data *c, int lines)
}
#endif /* CONFIG_VGACON_SOFT_SCROLLBACK */
-static const char __init *vgacon_startup(void)
+static const char *vgacon_startup(void)
{
const char *display_desc = NULL;
u16 saved1, saved2;
@@ -389,7 +390,7 @@ static const char __init *vgacon_startup(void)
vga_video_port_val = VGA_CRT_DM;
if ((ORIG_VIDEO_EGA_BX & 0xff) != 0x10) {
static struct resource ega_console_resource =
- { "ega", 0x3B0, 0x3BF };
+ { .name = "ega", .start = 0x3B0, .end = 0x3BF };
vga_video_type = VIDEO_TYPE_EGAM;
vga_vram_size = 0x8000;
display_desc = "EGA+";
@@ -397,9 +398,9 @@ static const char __init *vgacon_startup(void)
&ega_console_resource);
} else {
static struct resource mda1_console_resource =
- { "mda", 0x3B0, 0x3BB };
+ { .name = "mda", .start = 0x3B0, .end = 0x3BB };
static struct resource mda2_console_resource =
- { "mda", 0x3BF, 0x3BF };
+ { .name = "mda", .start = 0x3BF, .end = 0x3BF };
vga_video_type = VIDEO_TYPE_MDA;
vga_vram_size = 0x2000;
display_desc = "*MDA";
@@ -422,14 +423,14 @@ static const char __init *vgacon_startup(void)
if (!ORIG_VIDEO_ISVGA) {
static struct resource ega_console_resource
- = { "ega", 0x3C0, 0x3DF };
+ = { .name = "ega", .start = 0x3C0, .end = 0x3DF };
vga_video_type = VIDEO_TYPE_EGAC;
display_desc = "EGA";
request_resource(&ioport_resource,
&ega_console_resource);
} else {
static struct resource vga_console_resource
- = { "vga+", 0x3C0, 0x3DF };
+ = { .name = "vga+", .start = 0x3C0, .end = 0x3DF };
vga_video_type = VIDEO_TYPE_VGAC;
display_desc = "VGA+";
request_resource(&ioport_resource,
@@ -473,7 +474,7 @@ static const char __init *vgacon_startup(void)
}
} else {
static struct resource cga_console_resource =
- { "cga", 0x3D4, 0x3D5 };
+ { .name = "cga", .start = 0x3D4, .end = 0x3D5 };
vga_video_type = VIDEO_TYPE_CGA;
vga_vram_size = 0x2000;
display_desc = "*CGA";
@@ -523,7 +524,12 @@ static const char __init *vgacon_startup(void)
vgacon_xres = ORIG_VIDEO_COLS * VGA_FONTWIDTH;
vgacon_yres = vga_scan_lines;
- vgacon_scrollback_startup();
+
+ if (!vga_init_done) {
+ vgacon_scrollback_startup();
+ vga_init_done = 1;
+ }
+
return display_desc;
}
@@ -531,10 +537,20 @@ static void vgacon_init(struct vc_data *c, int init)
{
unsigned long p;
- /* We cannot be loaded as a module, therefore init is always 1 */
+ /*
+ * We cannot be loaded as a module, therefore init is always 1,
+ * but vgacon_init can be called more than once, and init will
+ * not be 1.
+ */
c->vc_can_do_color = vga_can_do_color;
- c->vc_cols = vga_video_num_columns;
- c->vc_rows = vga_video_num_lines;
+
+ /* set dimensions manually if init != 0 since vc_resize() will fail */
+ if (init) {
+ c->vc_cols = vga_video_num_columns;
+ c->vc_rows = vga_video_num_lines;
+ } else
+ vc_resize(c, vga_video_num_columns, vga_video_num_lines);
+
c->vc_scan_lines = vga_scan_lines;
c->vc_font.height = vga_video_font_height;
c->vc_complement_mask = 0x7700;
diff --git a/drivers/video/epson1355fb.c b/drivers/video/epson1355fb.c
index 082759447bf6..f0a621ecc288 100644
--- a/drivers/video/epson1355fb.c
+++ b/drivers/video/epson1355fb.c
@@ -605,11 +605,6 @@ static void clearfb16(struct fb_info *info)
fb_writeb(0, dst);
}
-static void epson1355fb_platform_release(struct device *device)
-{
- dev_err(device, "This driver is broken, please bug the authors so they will fix it.\n");
-}
-
static int epson1355fb_remove(struct platform_device *dev)
{
struct fb_info *info = platform_get_drvdata(dev);
@@ -733,13 +728,7 @@ static struct platform_driver epson1355fb_driver = {
},
};
-static struct platform_device epson1355fb_device = {
- .name = "epson1355fb",
- .id = 0,
- .dev = {
- .release = epson1355fb_platform_release,
- }
-};
+static struct platform_device *epson1355fb_device;
int __init epson1355fb_init(void)
{
@@ -749,11 +738,21 @@ int __init epson1355fb_init(void)
return -ENODEV;
ret = platform_driver_register(&epson1355fb_driver);
+
if (!ret) {
- ret = platform_device_register(&epson1355fb_device);
- if (ret)
+ epson1355fb_device = platform_device_alloc("epson1355fb", 0);
+
+ if (epson1355fb_device)
+ ret = platform_device_add(epson1355fb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(epson1355fb_device);
platform_driver_unregister(&epson1355fb_driver);
+ }
}
+
return ret;
}
@@ -762,7 +761,7 @@ module_init(epson1355fb_init);
#ifdef MODULE
static void __exit epson1355fb_exit(void)
{
- platform_device_unregister(&epson1355fb_device);
+ platform_device_unregister(epson1355fb_device);
platform_driver_unregister(&epson1355fb_driver);
}
diff --git a/drivers/video/fbcvt.c b/drivers/video/fbcvt.c
index ac90883dc3aa..b5498999c4ec 100644
--- a/drivers/video/fbcvt.c
+++ b/drivers/video/fbcvt.c
@@ -376,4 +376,3 @@ int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb)
return 0;
}
-EXPORT_SYMBOL(fb_find_mode_cvt);
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 372aa1776827..31143afe7c95 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -34,7 +34,6 @@
#endif
#include <linux/devfs_fs_kernel.h>
#include <linux/err.h>
-#include <linux/kernel.h>
#include <linux/device.h>
#include <linux/efi.h>
@@ -162,7 +161,6 @@ char* fb_get_buffer_offset(struct fb_info *info, struct fb_pixmap *buf, u32 size
}
#ifdef CONFIG_LOGO
-#include <linux/linux_logo.h>
static inline unsigned safe_shift(unsigned d, int n)
{
@@ -336,11 +334,11 @@ static void fb_rotate_logo_ud(const u8 *in, u8 *out, u32 width, u32 height)
static void fb_rotate_logo_cw(const u8 *in, u8 *out, u32 width, u32 height)
{
- int i, j, w = width - 1;
+ int i, j, h = height - 1;
for (i = 0; i < height; i++)
for (j = 0; j < width; j++)
- out[height * j + w - i] = *in++;
+ out[height * j + h - i] = *in++;
}
static void fb_rotate_logo_ccw(const u8 *in, u8 *out, u32 width, u32 height)
@@ -358,24 +356,24 @@ static void fb_rotate_logo(struct fb_info *info, u8 *dst,
u32 tmp;
if (rotate == FB_ROTATE_UD) {
- image->dx = info->var.xres - image->width;
- image->dy = info->var.yres - image->height;
fb_rotate_logo_ud(image->data, dst, image->width,
image->height);
+ image->dx = info->var.xres - image->width;
+ image->dy = info->var.yres - image->height;
} else if (rotate == FB_ROTATE_CW) {
- tmp = image->width;
- image->width = image->height;
- image->height = tmp;
- image->dx = info->var.xres - image->height;
fb_rotate_logo_cw(image->data, dst, image->width,
image->height);
- } else if (rotate == FB_ROTATE_CCW) {
tmp = image->width;
image->width = image->height;
image->height = tmp;
- image->dy = info->var.yres - image->width;
+ image->dx = info->var.xres - image->width;
+ } else if (rotate == FB_ROTATE_CCW) {
fb_rotate_logo_ccw(image->data, dst, image->width,
image->height);
+ tmp = image->width;
+ image->width = image->height;
+ image->height = tmp;
+ image->dy = info->var.yres - image->height;
}
image->data = dst;
@@ -435,7 +433,7 @@ int fb_prepare_logo(struct fb_info *info, int rotate)
depth = info->var.green.length;
}
- if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR) {
+ if (info->fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR && depth > 4) {
/* assume console colormap */
depth = 4;
}
@@ -1278,8 +1276,8 @@ static struct file_operations fb_fops = {
#endif
};
-static struct class *fb_class;
-
+struct class *fb_class;
+EXPORT_SYMBOL(fb_class);
/**
* register_framebuffer - registers a frame buffer device
* @fb_info: frame buffer info structure
@@ -1355,6 +1353,7 @@ register_framebuffer(struct fb_info *fb_info)
int
unregister_framebuffer(struct fb_info *fb_info)
{
+ struct fb_event event;
int i;
i = fb_info->node;
@@ -1362,13 +1361,17 @@ unregister_framebuffer(struct fb_info *fb_info)
return -EINVAL;
devfs_remove("fb/%d", i);
- if (fb_info->pixmap.addr && (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
+ if (fb_info->pixmap.addr &&
+ (fb_info->pixmap.flags & FB_PIXMAP_DEFAULT))
kfree(fb_info->pixmap.addr);
fb_destroy_modelist(&fb_info->modelist);
registered_fb[i]=NULL;
num_registered_fb--;
fb_cleanup_class_device(fb_info);
class_device_destroy(fb_class, MKDEV(FB_MAJOR, i));
+ event.info = fb_info;
+ blocking_notifier_call_chain(&fb_notifier_list,
+ FB_EVENT_FB_UNREGISTERED, &event);
return 0;
}
@@ -1491,28 +1494,6 @@ int fb_new_modelist(struct fb_info *info)
return err;
}
-/**
- * fb_con_duit - user<->fbcon passthrough
- * @info: struct fb_info
- * @event: notification event to be passed to fbcon
- * @data: private data
- *
- * DESCRIPTION
- * This function is an fbcon-user event passing channel
- * which bypasses fbdev. This is hopefully temporary
- * until a user interface for fbcon is created
- */
-int fb_con_duit(struct fb_info *info, int event, void *data)
-{
- struct fb_event evnt;
-
- evnt.info = info;
- evnt.data = data;
-
- return blocking_notifier_call_chain(&fb_notifier_list, event, &evnt);
-}
-EXPORT_SYMBOL(fb_con_duit);
-
static char *video_options[FB_MAX];
static int ofonly;
@@ -1622,6 +1603,5 @@ EXPORT_SYMBOL(fb_set_suspend);
EXPORT_SYMBOL(fb_register_client);
EXPORT_SYMBOL(fb_unregister_client);
EXPORT_SYMBOL(fb_get_options);
-EXPORT_SYMBOL(fb_new_modelist);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c
index 53beeb4a9998..3ccfff715a51 100644
--- a/drivers/video/fbmon.c
+++ b/drivers/video/fbmon.c
@@ -29,9 +29,9 @@
#include <linux/tty.h>
#include <linux/fb.h>
#include <linux/module.h>
+#include <linux/pci.h>
#include <video/edid.h>
#ifdef CONFIG_PPC_OF
-#include <linux/pci.h>
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
@@ -605,6 +605,7 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
block = edid + DETAILED_TIMING_DESCRIPTIONS_START;
DPRINTK(" Monitor Operating Limits: ");
+
for (i = 0; i < 4; i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) {
if (edid_is_limits_block(block)) {
specs->hfmin = H_MIN_RATE * 1000;
@@ -618,11 +619,12 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
break;
}
}
-
+
/* estimate monitor limits based on modes supported */
if (retval) {
- struct fb_videomode *modes;
+ struct fb_videomode *modes, *mode;
int num_modes, i, hz, hscan, pixclock;
+ int vtotal, htotal;
modes = fb_create_modedb(edid, &num_modes);
if (!modes) {
@@ -632,20 +634,38 @@ static int fb_get_monitor_limits(unsigned char *edid, struct fb_monspecs *specs)
retval = 0;
for (i = 0; i < num_modes; i++) {
- hz = modes[i].refresh;
+ mode = &modes[i];
pixclock = PICOS2KHZ(modes[i].pixclock) * 1000;
- hscan = (modes[i].yres * 105 * hz + 5000)/100;
+ htotal = mode->xres + mode->right_margin + mode->hsync_len
+ + mode->left_margin;
+ vtotal = mode->yres + mode->lower_margin + mode->vsync_len
+ + mode->upper_margin;
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ vtotal /= 2;
+
+ if (mode->vmode & FB_VMODE_DOUBLE)
+ vtotal *= 2;
+
+ hscan = (pixclock + htotal / 2) / htotal;
+ hscan = (hscan + 500) / 1000 * 1000;
+ hz = (hscan + vtotal / 2) / vtotal;
if (specs->dclkmax == 0 || specs->dclkmax < pixclock)
specs->dclkmax = pixclock;
+
if (specs->dclkmin == 0 || specs->dclkmin > pixclock)
specs->dclkmin = pixclock;
+
if (specs->hfmax == 0 || specs->hfmax < hscan)
specs->hfmax = hscan;
+
if (specs->hfmin == 0 || specs->hfmin > hscan)
specs->hfmin = hscan;
+
if (specs->vfmax == 0 || specs->vfmax < hz)
specs->vfmax = hz;
+
if (specs->vfmin == 0 || specs->vfmin > hz)
specs->vfmin = hz;
}
@@ -1281,8 +1301,7 @@ int fb_validate_mode(const struct fb_var_screeninfo *var, struct fb_info *info)
-EINVAL : 0;
}
-#if defined(CONFIG_FB_FIRMWARE_EDID) && defined(__i386__)
-#include <linux/pci.h>
+#if defined(CONFIG_FIRMWARE_EDID) && defined(CONFIG_X86)
/*
* We need to ensure that the EDID block is only returned for
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
index 3ceb8c1b392e..4f78f234473d 100644
--- a/drivers/video/fbsysfs.c
+++ b/drivers/video/fbsysfs.c
@@ -100,13 +100,22 @@ static int mode_string(char *buf, unsigned int offset,
const struct fb_videomode *mode)
{
char m = 'U';
+ char v = 'p';
+
if (mode->flag & FB_MODE_IS_DETAILED)
m = 'D';
if (mode->flag & FB_MODE_IS_VESA)
m = 'V';
if (mode->flag & FB_MODE_IS_STANDARD)
m = 'S';
- return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d-%d\n", m, mode->xres, mode->yres, mode->refresh);
+
+ if (mode->vmode & FB_VMODE_INTERLACED)
+ v = 'i';
+ if (mode->vmode & FB_VMODE_DOUBLE)
+ v = 'd';
+
+ return snprintf(&buf[offset], PAGE_SIZE - offset, "%c:%dx%d%c-%d\n",
+ m, mode->xres, mode->yres, v, mode->refresh);
}
static ssize_t store_mode(struct class_device *class_device, const char * buf,
@@ -238,45 +247,6 @@ static ssize_t show_rotate(struct class_device *class_device, char *buf)
return snprintf(buf, PAGE_SIZE, "%d\n", fb_info->var.rotate);
}
-static ssize_t store_con_rotate(struct class_device *class_device,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = class_get_devdata(class_device);
- int rotate;
- char **last = NULL;
-
- acquire_console_sem();
- rotate = simple_strtoul(buf, last, 0);
- fb_con_duit(fb_info, FB_EVENT_SET_CON_ROTATE, &rotate);
- release_console_sem();
- return count;
-}
-
-static ssize_t store_con_rotate_all(struct class_device *class_device,
- const char *buf, size_t count)
-{
- struct fb_info *fb_info = class_get_devdata(class_device);
- int rotate;
- char **last = NULL;
-
- acquire_console_sem();
- rotate = simple_strtoul(buf, last, 0);
- fb_con_duit(fb_info, FB_EVENT_SET_CON_ROTATE_ALL, &rotate);
- release_console_sem();
- return count;
-}
-
-static ssize_t show_con_rotate(struct class_device *class_device, char *buf)
-{
- struct fb_info *fb_info = class_get_devdata(class_device);
- int rotate;
-
- acquire_console_sem();
- rotate = fb_con_duit(fb_info, FB_EVENT_GET_CON_ROTATE, NULL);
- release_console_sem();
- return snprintf(buf, PAGE_SIZE, "%d\n", rotate);
-}
-
static ssize_t store_virtual(struct class_device *class_device,
const char * buf, size_t count)
{
@@ -493,8 +463,6 @@ static struct class_device_attribute class_device_attrs[] = {
__ATTR(name, S_IRUGO, show_name, NULL),
__ATTR(stride, S_IRUGO, show_stride, NULL),
__ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate),
- __ATTR(con_rotate, S_IRUGO|S_IWUSR, show_con_rotate, store_con_rotate),
- __ATTR(con_rotate_all, S_IWUSR, NULL, store_con_rotate_all),
__ATTR(state, S_IRUGO|S_IWUSR, show_fbstate, store_fbstate),
#ifdef CONFIG_FB_BACKLIGHT
__ATTR(bl_curve, S_IRUGO|S_IWUSR, show_bl_curve, store_bl_curve),
diff --git a/drivers/video/geode/gx1fb_core.c b/drivers/video/geode/gx1fb_core.c
index 20e69156d728..4d3a8871d3d1 100644
--- a/drivers/video/geode/gx1fb_core.c
+++ b/drivers/video/geode/gx1fb_core.c
@@ -376,8 +376,6 @@ static int __init gx1fb_probe(struct pci_dev *pdev, const struct pci_device_id *
release_mem_region(gx1_gx_base() + 0x8300, 0x100);
}
- pci_disable_device(pdev);
-
if (info)
framebuffer_release(info);
return ret;
@@ -399,7 +397,6 @@ static void gx1fb_remove(struct pci_dev *pdev)
iounmap(par->dc_regs);
release_mem_region(gx1_gx_base() + 0x8300, 0x100);
- pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
framebuffer_release(info);
diff --git a/drivers/video/geode/gxfb_core.c b/drivers/video/geode/gxfb_core.c
index 89c34b15f5d4..5ef12a3dfa50 100644
--- a/drivers/video/geode/gxfb_core.c
+++ b/drivers/video/geode/gxfb_core.c
@@ -354,8 +354,6 @@ static int __init gxfb_probe(struct pci_dev *pdev, const struct pci_device_id *i
pci_release_region(pdev, 2);
}
- pci_disable_device(pdev);
-
if (info)
framebuffer_release(info);
return ret;
@@ -377,7 +375,6 @@ static void gxfb_remove(struct pci_dev *pdev)
iounmap(par->dc_regs);
pci_release_region(pdev, 2);
- pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
framebuffer_release(info);
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
index 44aa2ffff973..a1f7d80f0ac1 100644
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -2110,9 +2110,6 @@ static void i810fb_release_resource(struct fb_info *info,
if (par->res_flags & MMIO_REQ)
release_mem_region(par->mmio_start_phys, MMIO_SIZE);
- if (par->res_flags & PCI_DEVICE_ENABLED)
- pci_disable_device(par->dev);
-
framebuffer_release(info);
}
diff --git a/drivers/video/imacfb.c b/drivers/video/imacfb.c
new file mode 100644
index 000000000000..7b1c168c834d
--- /dev/null
+++ b/drivers/video/imacfb.c
@@ -0,0 +1,345 @@
+/*
+ * framebuffer driver for Intel Based Mac's
+ *
+ * (c) 2006 Edgar Hucek <gimli@dark-green.com>
+ * Original imac driver written by Gerd Knorr <kraxel@goldbach.in-berlin.de>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/fb.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+
+#include <asm/io.h>
+
+#include <video/vga.h>
+
+typedef enum _MAC_TYPE {
+ M_I17,
+ M_I20,
+ M_MINI,
+ M_MACBOOK,
+ M_NEW
+} MAC_TYPE;
+
+/* --------------------------------------------------------------------- */
+
+static struct fb_var_screeninfo imacfb_defined __initdata = {
+ .activate = FB_ACTIVATE_NOW,
+ .height = -1,
+ .width = -1,
+ .right_margin = 32,
+ .upper_margin = 16,
+ .lower_margin = 4,
+ .vsync_len = 4,
+ .vmode = FB_VMODE_NONINTERLACED,
+};
+
+static struct fb_fix_screeninfo imacfb_fix __initdata = {
+ .id = "IMAC VGA",
+ .type = FB_TYPE_PACKED_PIXELS,
+ .accel = FB_ACCEL_NONE,
+ .visual = FB_VISUAL_TRUECOLOR,
+};
+
+static int inverse;
+static int model = M_NEW;
+static int manual_height;
+static int manual_width;
+
+#define DEFAULT_FB_MEM 1024*1024*16
+
+/* --------------------------------------------------------------------- */
+
+static int imacfb_setcolreg(unsigned regno, unsigned red, unsigned green,
+ unsigned blue, unsigned transp,
+ struct fb_info *info)
+{
+ /*
+ * Set a single color register. The values supplied are
+ * already rounded down to the hardware's capabilities
+ * (according to the entries in the `var' structure). Return
+ * != 0 for invalid regno.
+ */
+
+ if (regno >= info->cmap.len)
+ return 1;
+
+ if (regno < 16) {
+ red >>= 8;
+ green >>= 8;
+ blue >>= 8;
+ ((u32 *)(info->pseudo_palette))[regno] =
+ (red << info->var.red.offset) |
+ (green << info->var.green.offset) |
+ (blue << info->var.blue.offset);
+ }
+ return 0;
+}
+
+static struct fb_ops imacfb_ops = {
+ .owner = THIS_MODULE,
+ .fb_setcolreg = imacfb_setcolreg,
+ .fb_fillrect = cfb_fillrect,
+ .fb_copyarea = cfb_copyarea,
+ .fb_imageblit = cfb_imageblit,
+};
+
+static int __init imacfb_setup(char *options)
+{
+ char *this_opt;
+
+ if (!options || !*options)
+ return 0;
+
+ while ((this_opt = strsep(&options, ",")) != NULL) {
+ if (!*this_opt) continue;
+
+ if (!strcmp(this_opt, "inverse"))
+ inverse = 1;
+ else if (!strcmp(this_opt, "i17"))
+ model = M_I17;
+ else if (!strcmp(this_opt, "i20"))
+ model = M_I20;
+ else if (!strcmp(this_opt, "mini"))
+ model = M_MINI;
+ else if (!strcmp(this_opt, "macbook"))
+ model = M_MACBOOK;
+ else if (!strncmp(this_opt, "height:", 7))
+ manual_height = simple_strtoul(this_opt+7, NULL, 0);
+ else if (!strncmp(this_opt, "width:", 6))
+ manual_width = simple_strtoul(this_opt+6, NULL, 0);
+ }
+ return 0;
+}
+
+static int __init imacfb_probe(struct platform_device *dev)
+{
+ struct fb_info *info;
+ int err;
+ unsigned int size_vmode;
+ unsigned int size_remap;
+ unsigned int size_total;
+
+ screen_info.lfb_depth = 32;
+ screen_info.lfb_size = DEFAULT_FB_MEM / 0x10000;
+ screen_info.pages=1;
+ screen_info.blue_size = 8;
+ screen_info.blue_pos = 0;
+ screen_info.green_size = 8;
+ screen_info.green_pos = 8;
+ screen_info.red_size = 8;
+ screen_info.red_pos = 16;
+ screen_info.rsvd_size = 8;
+ screen_info.rsvd_pos = 24;
+
+ switch (model) {
+ case M_I17:
+ screen_info.lfb_width = 1440;
+ screen_info.lfb_height = 900;
+ screen_info.lfb_linelength = 1472 * 4;
+ screen_info.lfb_base = 0x80010000;
+ break;
+ case M_NEW:
+ case M_I20:
+ screen_info.lfb_width = 1680;
+ screen_info.lfb_height = 1050;
+ screen_info.lfb_linelength = 1728 * 4;
+ screen_info.lfb_base = 0x80010000;
+ break;
+ case M_MINI:
+ screen_info.lfb_width = 1024;
+ screen_info.lfb_height = 768;
+ screen_info.lfb_linelength = 2048 * 4;
+ screen_info.lfb_base = 0x80000000;
+ break;
+ case M_MACBOOK:
+ screen_info.lfb_width = 1280;
+ screen_info.lfb_height = 800;
+ screen_info.lfb_linelength = 2048 * 4;
+ screen_info.lfb_base = 0x80000000;
+ break;
+ }
+
+ /* if the user wants to manually specify height/width,
+ we will override the defaults */
+ /* TODO: eventually get auto-detection working */
+ if (manual_height > 0)
+ screen_info.lfb_height = manual_height;
+ if (manual_width > 0)
+ screen_info.lfb_width = manual_width;
+
+ imacfb_fix.smem_start = screen_info.lfb_base;
+ imacfb_defined.bits_per_pixel = screen_info.lfb_depth;
+ imacfb_defined.xres = screen_info.lfb_width;
+ imacfb_defined.yres = screen_info.lfb_height;
+ imacfb_fix.line_length = screen_info.lfb_linelength;
+
+ /* size_vmode -- that is the amount of memory needed for the
+ * used video mode, i.e. the minimum amount of
+ * memory we need. */
+ size_vmode = imacfb_defined.yres * imacfb_fix.line_length;
+
+ /* size_total -- all video memory we have. Used for
+ * entries, ressource allocation and bounds
+ * checking. */
+ size_total = screen_info.lfb_size * 65536;
+ if (size_total < size_vmode)
+ size_total = size_vmode;
+
+ /* size_remap -- the amount of video memory we are going to
+ * use for imacfb. With modern cards it is no
+ * option to simply use size_total as that
+ * wastes plenty of kernel address space. */
+ size_remap = size_vmode * 2;
+ if (size_remap < size_vmode)
+ size_remap = size_vmode;
+ if (size_remap > size_total)
+ size_remap = size_total;
+ imacfb_fix.smem_len = size_remap;
+
+#ifndef __i386__
+ screen_info.imacpm_seg = 0;
+#endif
+
+ if (!request_mem_region(imacfb_fix.smem_start, size_total, "imacfb")) {
+ printk(KERN_WARNING
+ "imacfb: cannot reserve video memory at 0x%lx\n",
+ imacfb_fix.smem_start);
+ /* We cannot make this fatal. Sometimes this comes from magic
+ spaces our resource handlers simply don't know about */
+ }
+
+ info = framebuffer_alloc(sizeof(u32) * 16, &dev->dev);
+ if (!info) {
+ err = -ENOMEM;
+ goto err_release_mem;
+ }
+ info->pseudo_palette = info->par;
+ info->par = NULL;
+
+ info->screen_base = ioremap(imacfb_fix.smem_start, imacfb_fix.smem_len);
+ if (!info->screen_base) {
+ printk(KERN_ERR "imacfb: abort, cannot ioremap video memory "
+ "0x%x @ 0x%lx\n",
+ imacfb_fix.smem_len, imacfb_fix.smem_start);
+ err = -EIO;
+ goto err_unmap;
+ }
+
+ printk(KERN_INFO "imacfb: framebuffer at 0x%lx, mapped to 0x%p, "
+ "using %dk, total %dk\n",
+ imacfb_fix.smem_start, info->screen_base,
+ size_remap/1024, size_total/1024);
+ printk(KERN_INFO "imacfb: mode is %dx%dx%d, linelength=%d, pages=%d\n",
+ imacfb_defined.xres, imacfb_defined.yres,
+ imacfb_defined.bits_per_pixel, imacfb_fix.line_length,
+ screen_info.pages);
+
+ imacfb_defined.xres_virtual = imacfb_defined.xres;
+ imacfb_defined.yres_virtual = imacfb_fix.smem_len /
+ imacfb_fix.line_length;
+ printk(KERN_INFO "imacfb: scrolling: redraw\n");
+ imacfb_defined.yres_virtual = imacfb_defined.yres;
+
+ /* some dummy values for timing to make fbset happy */
+ imacfb_defined.pixclock = 10000000 / imacfb_defined.xres *
+ 1000 / imacfb_defined.yres;
+ imacfb_defined.left_margin = (imacfb_defined.xres / 8) & 0xf8;
+ imacfb_defined.hsync_len = (imacfb_defined.xres / 8) & 0xf8;
+
+ imacfb_defined.red.offset = screen_info.red_pos;
+ imacfb_defined.red.length = screen_info.red_size;
+ imacfb_defined.green.offset = screen_info.green_pos;
+ imacfb_defined.green.length = screen_info.green_size;
+ imacfb_defined.blue.offset = screen_info.blue_pos;
+ imacfb_defined.blue.length = screen_info.blue_size;
+ imacfb_defined.transp.offset = screen_info.rsvd_pos;
+ imacfb_defined.transp.length = screen_info.rsvd_size;
+
+ printk(KERN_INFO "imacfb: %s: "
+ "size=%d:%d:%d:%d, shift=%d:%d:%d:%d\n",
+ "Truecolor",
+ screen_info.rsvd_size,
+ screen_info.red_size,
+ screen_info.green_size,
+ screen_info.blue_size,
+ screen_info.rsvd_pos,
+ screen_info.red_pos,
+ screen_info.green_pos,
+ screen_info.blue_pos);
+
+ imacfb_fix.ypanstep = 0;
+ imacfb_fix.ywrapstep = 0;
+
+ /* request failure does not faze us, as vgacon probably has this
+ * region already (FIXME) */
+ request_region(0x3c0, 32, "imacfb");
+
+ info->fbops = &imacfb_ops;
+ info->var = imacfb_defined;
+ info->fix = imacfb_fix;
+ info->flags = FBINFO_FLAG_DEFAULT;
+
+ if (fb_alloc_cmap(&info->cmap, 256, 0) < 0) {
+ err = -ENOMEM;
+ goto err_unmap;
+ }
+ if (register_framebuffer(info)<0) {
+ err = -EINVAL;
+ goto err_fb_dealoc;
+ }
+ printk(KERN_INFO "fb%d: %s frame buffer device\n",
+ info->node, info->fix.id);
+ return 0;
+
+err_fb_dealoc:
+ fb_dealloc_cmap(&info->cmap);
+err_unmap:
+ iounmap(info->screen_base);
+ framebuffer_release(info);
+err_release_mem:
+ release_mem_region(imacfb_fix.smem_start, size_total);
+ return err;
+}
+
+static struct platform_driver imacfb_driver = {
+ .probe = imacfb_probe,
+ .driver = {
+ .name = "imacfb",
+ },
+};
+
+static struct platform_device imacfb_device = {
+ .name = "imacfb",
+};
+
+static int __init imacfb_init(void)
+{
+ int ret;
+ char *option = NULL;
+
+ /* ignore error return of fb_get_options */
+ fb_get_options("imacfb", &option);
+ imacfb_setup(option);
+ ret = platform_driver_register(&imacfb_driver);
+
+ if (!ret) {
+ ret = platform_device_register(&imacfb_device);
+ if (ret)
+ platform_driver_unregister(&imacfb_driver);
+ }
+ return ret;
+}
+module_init(imacfb_init);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/macmodes.c b/drivers/video/macmodes.c
index c0385c6f7db5..d21321ca7c39 100644
--- a/drivers/video/macmodes.c
+++ b/drivers/video/macmodes.c
@@ -327,7 +327,6 @@ int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
}
return -EINVAL;
}
-EXPORT_SYMBOL(mac_var_to_vmode);
/**
* mac_map_monitor_sense - Convert monitor sense to vmode
@@ -371,8 +370,9 @@ EXPORT_SYMBOL(mac_map_monitor_sense);
*
*/
-int __init mac_find_mode(struct fb_var_screeninfo *var, struct fb_info *info,
- const char *mode_option, unsigned int default_bpp)
+int __devinit mac_find_mode(struct fb_var_screeninfo *var,
+ struct fb_info *info, const char *mode_option,
+ unsigned int default_bpp)
{
const struct fb_videomode *db = NULL;
unsigned int dbsize = 0;
diff --git a/drivers/video/macmodes.h b/drivers/video/macmodes.h
index 232f5a09a499..babeb81f467d 100644
--- a/drivers/video/macmodes.h
+++ b/drivers/video/macmodes.h
@@ -55,9 +55,10 @@ extern int mac_vmode_to_var(int vmode, int cmode,
extern int mac_var_to_vmode(const struct fb_var_screeninfo *var, int *vmode,
int *cmode);
extern int mac_map_monitor_sense(int sense);
-extern int __init mac_find_mode(struct fb_var_screeninfo *var,
- struct fb_info *info, const char *mode_option,
- unsigned int default_bpp);
+extern int __devinit mac_find_mode(struct fb_var_screeninfo *var,
+ struct fb_info *info,
+ const char *mode_option,
+ unsigned int default_bpp);
/*
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c
index 26a1c618a205..ff5454601e22 100644
--- a/drivers/video/modedb.c
+++ b/drivers/video/modedb.c
@@ -259,6 +259,10 @@ static const struct fb_videomode modedb[] = {
/* 1152x768, 60 Hz, PowerBook G4 Titanium I and II */
NULL, 60, 1152, 768, 15386, 158, 26, 29, 3, 136, 6,
FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED
+ }, {
+ /* 1366x768, 60 Hz, 47.403 kHz hsync, WXGA 16:9 aspect ratio */
+ NULL, 60, 1366, 768, 13806, 120, 10, 14, 3, 32, 5,
+ 0, FB_VMODE_NONINTERLACED
},
};
@@ -787,8 +791,9 @@ struct fb_videomode *fb_find_best_mode(struct fb_var_screeninfo *var,
if (diff > d) {
diff = d;
best = mode;
- } else if (diff == d && mode->refresh > best->refresh)
- best = mode;
+ } else if (diff == d && best &&
+ mode->refresh > best->refresh)
+ best = mode;
}
}
return best;
@@ -1016,8 +1021,6 @@ EXPORT_SYMBOL(fb_videomode_to_var);
EXPORT_SYMBOL(fb_var_to_videomode);
EXPORT_SYMBOL(fb_mode_is_equal);
EXPORT_SYMBOL(fb_add_videomode);
-EXPORT_SYMBOL(fb_delete_videomode);
-EXPORT_SYMBOL(fb_destroy_modelist);
EXPORT_SYMBOL(fb_match_mode);
EXPORT_SYMBOL(fb_find_best_mode);
EXPORT_SYMBOL(fb_find_nearest_mode);
diff --git a/drivers/video/neofb.c b/drivers/video/neofb.c
index 24b12f71d5a8..2f156b724d1c 100644
--- a/drivers/video/neofb.c
+++ b/drivers/video/neofb.c
@@ -1333,17 +1333,22 @@ static int neofb_blank(int blank_mode, struct fb_info *info)
* run "setterm -powersave powerdown" to take advantage
*/
struct neofb_par *par = info->par;
- int seqflags, lcdflags, dpmsflags, reg;
-
+ int seqflags, lcdflags, dpmsflags, reg, tmpdisp;
/*
- * Reload the value stored in the register, if sensible. It might have
- * been changed via FN keystroke.
+ * Read back the register bits related to display configuration. They might
+ * have been changed underneath the driver via Fn key stroke.
+ */
+ neoUnlock();
+ tmpdisp = vga_rgfx(NULL, 0x20) & 0x03;
+ neoLock(&par->state);
+
+ /* In case we blank the screen, we want to store the possibly new
+ * configuration in the driver. During un-blank, we re-apply this setting,
+ * since the LCD bit will be cleared in order to switch off the backlight.
*/
if (par->PanelDispCntlRegRead) {
- neoUnlock();
- par->PanelDispCntlReg1 = vga_rgfx(NULL, 0x20) & 0x03;
- neoLock(&par->state);
+ par->PanelDispCntlReg1 = tmpdisp;
}
par->PanelDispCntlRegRead = !blank_mode;
@@ -1378,12 +1383,21 @@ static int neofb_blank(int blank_mode, struct fb_info *info)
break;
case FB_BLANK_NORMAL: /* just blank screen (backlight stays on) */
seqflags = VGA_SR01_SCREEN_OFF; /* Disable sequencer */
- lcdflags = par->PanelDispCntlReg1 & 0x02; /* LCD normal */
+ /*
+ * During a blank operation with the LID shut, we might store "LCD off"
+ * by mistake. Due to timing issues, the BIOS may switch the lights
+ * back on, and we turn it back off once we "unblank".
+ *
+ * So here is an attempt to implement ">=" - if we are in the process
+ * of unblanking, and the LCD bit is unset in the driver but set in the
+ * register, we must keep it.
+ */
+ lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
dpmsflags = 0x00; /* no hsync/vsync suppression */
break;
case FB_BLANK_UNBLANK: /* unblank */
seqflags = 0; /* Enable sequencer */
- lcdflags = par->PanelDispCntlReg1 & 0x02; /* LCD normal */
+ lcdflags = ((par->PanelDispCntlReg1 | tmpdisp) & 0x02); /* LCD normal */
dpmsflags = 0x00; /* no hsync/vsync suppression */
#ifdef CONFIG_TOSHIBA
/* Do we still need this ? */
diff --git a/drivers/video/nvidia/nv_hw.c b/drivers/video/nvidia/nv_hw.c
index 99c3a8e6a237..9ed640d35728 100644
--- a/drivers/video/nvidia/nv_hw.c
+++ b/drivers/video/nvidia/nv_hw.c
@@ -886,7 +886,10 @@ void NVCalcStateExt(struct nvidia_par *par,
case NV_ARCH_20:
case NV_ARCH_30:
default:
- if (((par->Chipset & 0xffff) == 0x01A0) ||
+ if ((par->Chipset & 0xfff0) == 0x0240) {
+ state->arbitration0 = 256;
+ state->arbitration1 = 0x0480;
+ } else if (((par->Chipset & 0xffff) == 0x01A0) ||
((par->Chipset & 0xffff) == 0x01f0)) {
nForceUpdateArbitrationSettings(VClk,
pixelDepth * 8,
@@ -1235,6 +1238,7 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state)
break;
case 0x0160:
case 0x01D0:
+ case 0x0240:
NV_WR32(par->PMC, 0x1700,
NV_RD32(par->PFB, 0x020C));
NV_WR32(par->PMC, 0x1704, 0);
@@ -1359,7 +1363,9 @@ void NVLoadStateExt(struct nvidia_par *par, RIVA_HW_STATE * state)
if(((par->Chipset & 0xfff0)
!= 0x0160) &&
((par->Chipset & 0xfff0)
- != 0x0220))
+ != 0x0220) &&
+ ((par->Chipset & 0xfff0)
+ != 0x240))
NV_WR32(par->PGRAPH,
0x6900 + i*4,
NV_RD32(par->PFB,
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index 03a7c1e9ce38..7b5cffb27851 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -67,359 +67,10 @@
#define MAX_CURS 32
static struct pci_device_id nvidiafb_pci_tbl[] = {
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UTNT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_TNT_UNKNOWN,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_VTNT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_UVTNT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_ITNT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_SDR,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_DDR,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_MX2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_MXR,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_GTS2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE2_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO2_PRO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_460,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_SE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_460_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_420_GO_M32,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_440_GO_M64,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_550XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_500_GOGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_410_GO_M16,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440_8X,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_440SE_8X,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_420_8X,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_4000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_448_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_488_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_580_XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_MX_MAC,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_280_NVS,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_380_XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_IGEFORCE2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE3_2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_DDC,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4600,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4400,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_900XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_750XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800_8X,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_TI_4800SE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE4_4200_GO,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_980_XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_780_XGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO4_700_GOGL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5800,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_2000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5600SE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5600,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5650,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO700,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5200SE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5250_32,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO_5200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_NVS_280_PCI,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_500,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5300,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5100,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900XT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5950_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_3000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700LE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5700VE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5500,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5100,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_700,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_5900ZT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_LE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800_GT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_4000,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6600_GT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6600,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6610_XL,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_QUADRO_FX_540,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_ALT1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6600_ALT2,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6200_ALT1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_GEFORCE_6800_GT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCIE_DEVICE_ID_NVIDIA_QUADRO_NVS280,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0252,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0313,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0316,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0317,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x031D,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x031E,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x031F,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0329,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x032F,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0345,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0349,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x034B,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x034F,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x00c0,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_6800A,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_6800A_LE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_GO_6800,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_GEFORCE_GO_6800_ULTRA,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_QUADRO_FX_GO1400,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x00cd,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_QUADRO_FX_1400,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0142,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0143,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0144,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0145,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0146,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0147,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0148,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0149,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x014b,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x14c,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x014d,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0160,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6200_TURBOCACHE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0162,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0163,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0165,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6200_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_6250_1,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0169,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x016b,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x016c,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x016d,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x016e,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0210,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_LE,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_6800B_GT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GT,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_7800_GTX,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_GEFORCE_GO_7800_GTX,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x021d,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x021e,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0220,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0221,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0222,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {PCI_VENDOR_ID_NVIDIA, 0x0228,
- PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
- {0,} /* terminate list */
+ {PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
+ PCI_BASE_CLASS_DISPLAY << 16, 0xff0000, 0},
+ { 0, }
};
-
MODULE_DEVICE_TABLE(pci, nvidiafb_pci_tbl);
/* command line data, set in nvidiafb_setup() */
@@ -1465,10 +1116,10 @@ static u32 __devinit nvidia_get_chipset(struct fb_info *info)
struct nvidia_par *par = info->par;
u32 id = (par->pci_dev->vendor << 16) | par->pci_dev->device;
- printk("nvidiafb: PCI id - %x\n", id);
+ printk(KERN_INFO PFX "Device ID: %x \n", id);
+
if ((id & 0xfff0) == 0x00f0) {
/* pci-e */
- printk("nvidiafb: PCI-E card\n");
id = NV_RD32(par->REGS, 0x1800);
if ((id & 0x0000ffff) == 0x000010DE)
@@ -1476,9 +1127,9 @@ static u32 __devinit nvidia_get_chipset(struct fb_info *info)
else if ((id & 0xffff0000) == 0xDE100000) /* wrong endian */
id = 0x10DE0000 | ((id << 8) & 0x0000ff00) |
((id >> 8) & 0x000000ff);
+ printk(KERN_INFO PFX "Subsystem ID: %x \n", id);
}
- printk("nvidiafb: Actual id - %x\n", id);
return id;
}
@@ -1520,6 +1171,7 @@ static u32 __devinit nvidia_get_arch(struct fb_info *info)
case 0x0210:
case 0x0220:
case 0x0230:
+ case 0x0240:
case 0x0290:
case 0x0390:
arch = NV_ARCH_40;
@@ -1567,7 +1219,7 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd,
if (pci_request_regions(pd, "nvidiafb")) {
printk(KERN_ERR PFX "cannot request PCI regions\n");
- goto err_out_request;
+ goto err_out_enable;
}
par->FlatPanel = flatpanel;
@@ -1596,7 +1248,6 @@ static int __devinit nvidiafb_probe(struct pci_dev *pd,
}
par->Chipset = nvidia_get_chipset(info);
- printk(KERN_INFO PFX "nVidia device/chipset %X\n", par->Chipset);
par->Architecture = nvidia_get_arch(info);
if (par->Architecture == 0) {
@@ -1687,10 +1338,8 @@ err_out_free_base1:
nvidia_delete_i2c_busses(par);
err_out_arch:
iounmap(par->REGS);
-err_out_free_base0:
+ err_out_free_base0:
pci_release_regions(pd);
-err_out_request:
- pci_disable_device(pd);
err_out_enable:
kfree(info->pixmap.addr);
err_out_kfree:
@@ -1720,7 +1369,6 @@ static void __exit nvidiafb_remove(struct pci_dev *pd)
nvidia_delete_i2c_busses(par);
iounmap(par->REGS);
pci_release_regions(pd);
- pci_disable_device(pd);
kfree(info->pixmap.addr);
framebuffer_release(info);
pci_set_drvdata(pd, NULL);
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
index d4384ab1df65..12af58c5cf1f 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -2152,7 +2152,6 @@ err_iounmap_ctrl_base:
err_release_region:
pci_release_regions(pd);
err_disable_device:
- pci_disable_device(pd);
err_free_pixmap:
kfree(info->pixmap.addr);
err_framebuffer_release:
@@ -2187,7 +2186,6 @@ static void __exit rivafb_remove(struct pci_dev *pd)
if (par->riva.Architecture == NV_ARCH_03)
iounmap(par->riva.PRAMIN);
pci_release_regions(pd);
- pci_disable_device(pd);
kfree(info->pixmap.addr);
framebuffer_release(info);
pci_set_drvdata(pd, NULL);
diff --git a/drivers/video/s3c2410fb.c b/drivers/video/s3c2410fb.c
index 9451932fbaf2..fbc411850686 100644
--- a/drivers/video/s3c2410fb.c
+++ b/drivers/video/s3c2410fb.c
@@ -641,6 +641,7 @@ static int __init s3c2410fb_probe(struct platform_device *pdev)
int ret;
int irq;
int i;
+ u32 lcdcon1;
mach_info = pdev->dev.platform_data;
if (mach_info == NULL) {
@@ -672,6 +673,11 @@ static int __init s3c2410fb_probe(struct platform_device *pdev)
memcpy(&info->regs, &mach_info->regs, sizeof(info->regs));
+ /* Stop the video and unset ENVID if set */
+ info->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
+ lcdcon1 = readl(S3C2410_LCDCON1);
+ writel(lcdcon1 & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
+
info->mach_info = pdev->dev.platform_data;
fbinfo->fix.type = FB_TYPE_PACKED_PIXELS;
@@ -794,15 +800,14 @@ dealloc_fb:
* shutdown the lcd controller
*/
-static void s3c2410fb_stop_lcd(void)
+static void s3c2410fb_stop_lcd(struct s3c2410fb_info *fbi)
{
unsigned long flags;
- unsigned long tmp;
local_irq_save(flags);
- tmp = readl(S3C2410_LCDCON1);
- writel(tmp & ~S3C2410_LCDCON1_ENVID, S3C2410_LCDCON1);
+ fbi->regs.lcdcon1 &= ~S3C2410_LCDCON1_ENVID;
+ writel(fbi->regs.lcdcon1, S3C2410_LCDCON1);
local_irq_restore(flags);
}
@@ -816,7 +821,7 @@ static int s3c2410fb_remove(struct platform_device *pdev)
struct s3c2410fb_info *info = fbinfo->par;
int irq;
- s3c2410fb_stop_lcd();
+ s3c2410fb_stop_lcd(info);
msleep(1);
s3c2410fb_unmap_video_memory(info);
@@ -844,7 +849,7 @@ static int s3c2410fb_suspend(struct platform_device *dev, pm_message_t state)
struct fb_info *fbinfo = platform_get_drvdata(dev);
struct s3c2410fb_info *info = fbinfo->par;
- s3c2410fb_stop_lcd();
+ s3c2410fb_stop_lcd(info);
/* sleep before disabling the clock, we need to ensure
* the LCD DMA engine is not going to get back on the bus
diff --git a/drivers/video/savage/savagefb.h b/drivers/video/savage/savagefb.h
index 58cfdfb41833..e648a6c0f6d9 100644
--- a/drivers/video/savage/savagefb.h
+++ b/drivers/video/savage/savagefb.h
@@ -147,7 +147,27 @@ struct xtimings {
int interlaced;
};
+struct savage_reg {
+ unsigned char MiscOutReg; /* Misc */
+ unsigned char CRTC[25]; /* Crtc Controller */
+ unsigned char Sequencer[5]; /* Video Sequencer */
+ unsigned char Graphics[9]; /* Video Graphics */
+ unsigned char Attribute[21]; /* Video Atribute */
+ unsigned int mode, refresh;
+ unsigned char SR08, SR0E, SR0F;
+ unsigned char SR10, SR11, SR12, SR13, SR15, SR18, SR29, SR30;
+ unsigned char SR54[8];
+ unsigned char Clock;
+ unsigned char CR31, CR32, CR33, CR34, CR36, CR3A, CR3B, CR3C;
+ unsigned char CR40, CR41, CR42, CR43, CR45;
+ unsigned char CR50, CR51, CR53, CR55, CR58, CR5B, CR5D, CR5E;
+ unsigned char CR60, CR63, CR65, CR66, CR67, CR68, CR69, CR6D, CR6F;
+ unsigned char CR86, CR88;
+ unsigned char CR90, CR91, CRB0;
+ unsigned int STREAMS[22]; /* yuck, streams regs */
+ unsigned int MMPR0, MMPR1, MMPR2, MMPR3;
+};
/* --------------------------------------------------------------------- */
#define NR_PALETTE 256
@@ -167,6 +187,8 @@ struct savagefb_par {
struct pci_dev *pcidev;
savage_chipset chip;
struct savagefb_i2c_chan chan;
+ struct savage_reg state;
+ struct savage_reg save;
unsigned char *edid;
u32 pseudo_palette[16];
int paletteEnabled;
@@ -179,6 +201,7 @@ struct savagefb_par {
int minClock;
int numClocks;
int clock[4];
+ int MCLK, REFCLK, LCDclk;
struct {
u8 __iomem *vbase;
u32 pbase;
@@ -196,7 +219,6 @@ struct savagefb_par {
volatile u32 __iomem *bci_base;
unsigned int bci_ptr;
-
u32 cob_offset;
u32 cob_size;
int cob_index;
@@ -204,7 +226,6 @@ struct savagefb_par {
void (*SavageWaitIdle) (struct savagefb_par *par);
void (*SavageWaitFifo) (struct savagefb_par *par, int space);
- int MCLK, REFCLK, LCDclk;
int HorizScaleFactor;
/* Panels size */
@@ -217,26 +238,6 @@ struct savagefb_par {
int depth;
int vwidth;
-
- unsigned char MiscOutReg; /* Misc */
- unsigned char CRTC[25]; /* Crtc Controller */
- unsigned char Sequencer[5]; /* Video Sequencer */
- unsigned char Graphics[9]; /* Video Graphics */
- unsigned char Attribute[21]; /* Video Atribute */
-
- unsigned int mode, refresh;
- unsigned char SR08, SR0E, SR0F;
- unsigned char SR10, SR11, SR12, SR13, SR15, SR18, SR29, SR30;
- unsigned char SR54[8];
- unsigned char Clock;
- unsigned char CR31, CR32, CR33, CR34, CR36, CR3A, CR3B, CR3C;
- unsigned char CR40, CR41, CR42, CR43, CR45;
- unsigned char CR50, CR51, CR53, CR55, CR58, CR5B, CR5D, CR5E;
- unsigned char CR60, CR63, CR65, CR66, CR67, CR68, CR69, CR6D, CR6F;
- unsigned char CR86, CR88;
- unsigned char CR90, CR91, CRB0;
- unsigned int STREAMS[22]; /* yuck, streams regs */
- unsigned int MMPR0, MMPR1, MMPR2, MMPR3;
};
#define BCI_BD_BW_DISABLE 0x10000000
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index 0da624e6524f..78883cf66a4d 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -86,15 +86,15 @@ MODULE_DESCRIPTION("FBDev driver for S3 Savage PCI/AGP Chips");
/* --------------------------------------------------------------------- */
-static void vgaHWSeqReset (struct savagefb_par *par, int start)
+static void vgaHWSeqReset(struct savagefb_par *par, int start)
{
if (start)
- VGAwSEQ (0x00, 0x01, par); /* Synchronous Reset */
+ VGAwSEQ(0x00, 0x01, par); /* Synchronous Reset */
else
- VGAwSEQ (0x00, 0x03, par); /* End Reset */
+ VGAwSEQ(0x00, 0x03, par); /* End Reset */
}
-static void vgaHWProtect (struct savagefb_par *par, int on)
+static void vgaHWProtect(struct savagefb_par *par, int on)
{
unsigned char tmp;
@@ -102,10 +102,10 @@ static void vgaHWProtect (struct savagefb_par *par, int on)
/*
* Turn off screen and disable sequencer.
*/
- tmp = VGArSEQ (0x01, par);
+ tmp = VGArSEQ(0x01, par);
- vgaHWSeqReset (par, 1); /* start synchronous reset */
- VGAwSEQ (0x01, tmp | 0x20, par);/* disable the display */
+ vgaHWSeqReset(par, 1); /* start synchronous reset */
+ VGAwSEQ(0x01, tmp | 0x20, par);/* disable the display */
VGAenablePalette(par);
} else {
@@ -113,75 +113,76 @@ static void vgaHWProtect (struct savagefb_par *par, int on)
* Reenable sequencer, then turn on screen.
*/
- tmp = VGArSEQ (0x01, par);
+ tmp = VGArSEQ(0x01, par);
- VGAwSEQ (0x01, tmp & ~0x20, par);/* reenable display */
- vgaHWSeqReset (par, 0); /* clear synchronous reset */
+ VGAwSEQ(0x01, tmp & ~0x20, par);/* reenable display */
+ vgaHWSeqReset(par, 0); /* clear synchronous reset */
VGAdisablePalette(par);
}
}
-static void vgaHWRestore (struct savagefb_par *par)
+static void vgaHWRestore(struct savagefb_par *par, struct savage_reg *reg)
{
int i;
- VGAwMISC (par->MiscOutReg, par);
+ VGAwMISC(reg->MiscOutReg, par);
for (i = 1; i < 5; i++)
- VGAwSEQ (i, par->Sequencer[i], par);
+ VGAwSEQ(i, reg->Sequencer[i], par);
/* Ensure CRTC registers 0-7 are unlocked by clearing bit 7 or
CRTC[17] */
- VGAwCR (17, par->CRTC[17] & ~0x80, par);
+ VGAwCR(17, reg->CRTC[17] & ~0x80, par);
for (i = 0; i < 25; i++)
- VGAwCR (i, par->CRTC[i], par);
+ VGAwCR(i, reg->CRTC[i], par);
for (i = 0; i < 9; i++)
- VGAwGR (i, par->Graphics[i], par);
+ VGAwGR(i, reg->Graphics[i], par);
VGAenablePalette(par);
for (i = 0; i < 21; i++)
- VGAwATTR (i, par->Attribute[i], par);
+ VGAwATTR(i, reg->Attribute[i], par);
VGAdisablePalette(par);
}
-static void vgaHWInit (struct fb_var_screeninfo *var,
- struct savagefb_par *par,
- struct xtimings *timings)
+static void vgaHWInit(struct fb_var_screeninfo *var,
+ struct savagefb_par *par,
+ struct xtimings *timings,
+ struct savage_reg *reg)
{
- par->MiscOutReg = 0x23;
+ reg->MiscOutReg = 0x23;
if (!(timings->sync & FB_SYNC_HOR_HIGH_ACT))
- par->MiscOutReg |= 0x40;
+ reg->MiscOutReg |= 0x40;
if (!(timings->sync & FB_SYNC_VERT_HIGH_ACT))
- par->MiscOutReg |= 0x80;
+ reg->MiscOutReg |= 0x80;
/*
* Time Sequencer
*/
- par->Sequencer[0x00] = 0x00;
- par->Sequencer[0x01] = 0x01;
- par->Sequencer[0x02] = 0x0F;
- par->Sequencer[0x03] = 0x00; /* Font select */
- par->Sequencer[0x04] = 0x0E; /* Misc */
+ reg->Sequencer[0x00] = 0x00;
+ reg->Sequencer[0x01] = 0x01;
+ reg->Sequencer[0x02] = 0x0F;
+ reg->Sequencer[0x03] = 0x00; /* Font select */
+ reg->Sequencer[0x04] = 0x0E; /* Misc */
/*
* CRTC Controller
*/
- par->CRTC[0x00] = (timings->HTotal >> 3) - 5;
- par->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
- par->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
- par->CRTC[0x03] = (((timings->HSyncEnd >> 3) - 1) & 0x1f) | 0x80;
- par->CRTC[0x04] = (timings->HSyncStart >> 3);
- par->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
+ reg->CRTC[0x00] = (timings->HTotal >> 3) - 5;
+ reg->CRTC[0x01] = (timings->HDisplay >> 3) - 1;
+ reg->CRTC[0x02] = (timings->HSyncStart >> 3) - 1;
+ reg->CRTC[0x03] = (((timings->HSyncEnd >> 3) - 1) & 0x1f) | 0x80;
+ reg->CRTC[0x04] = (timings->HSyncStart >> 3);
+ reg->CRTC[0x05] = ((((timings->HSyncEnd >> 3) - 1) & 0x20) << 2) |
(((timings->HSyncEnd >> 3)) & 0x1f);
- par->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
- par->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
+ reg->CRTC[0x06] = (timings->VTotal - 2) & 0xFF;
+ reg->CRTC[0x07] = (((timings->VTotal - 2) & 0x100) >> 8) |
(((timings->VDisplay - 1) & 0x100) >> 7) |
((timings->VSyncStart & 0x100) >> 6) |
(((timings->VSyncStart - 1) & 0x100) >> 5) |
@@ -189,27 +190,27 @@ static void vgaHWInit (struct fb_var_screeninfo *var,
(((timings->VTotal - 2) & 0x200) >> 4) |
(((timings->VDisplay - 1) & 0x200) >> 3) |
((timings->VSyncStart & 0x200) >> 2);
- par->CRTC[0x08] = 0x00;
- par->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
+ reg->CRTC[0x08] = 0x00;
+ reg->CRTC[0x09] = (((timings->VSyncStart - 1) & 0x200) >> 4) | 0x40;
if (timings->dblscan)
- par->CRTC[0x09] |= 0x80;
-
- par->CRTC[0x0a] = 0x00;
- par->CRTC[0x0b] = 0x00;
- par->CRTC[0x0c] = 0x00;
- par->CRTC[0x0d] = 0x00;
- par->CRTC[0x0e] = 0x00;
- par->CRTC[0x0f] = 0x00;
- par->CRTC[0x10] = timings->VSyncStart & 0xff;
- par->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
- par->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
- par->CRTC[0x13] = var->xres_virtual >> 4;
- par->CRTC[0x14] = 0x00;
- par->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
- par->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
- par->CRTC[0x17] = 0xc3;
- par->CRTC[0x18] = 0xff;
+ reg->CRTC[0x09] |= 0x80;
+
+ reg->CRTC[0x0a] = 0x00;
+ reg->CRTC[0x0b] = 0x00;
+ reg->CRTC[0x0c] = 0x00;
+ reg->CRTC[0x0d] = 0x00;
+ reg->CRTC[0x0e] = 0x00;
+ reg->CRTC[0x0f] = 0x00;
+ reg->CRTC[0x10] = timings->VSyncStart & 0xff;
+ reg->CRTC[0x11] = (timings->VSyncEnd & 0x0f) | 0x20;
+ reg->CRTC[0x12] = (timings->VDisplay - 1) & 0xff;
+ reg->CRTC[0x13] = var->xres_virtual >> 4;
+ reg->CRTC[0x14] = 0x00;
+ reg->CRTC[0x15] = (timings->VSyncStart - 1) & 0xff;
+ reg->CRTC[0x16] = (timings->VSyncEnd - 1) & 0xff;
+ reg->CRTC[0x17] = 0xc3;
+ reg->CRTC[0x18] = 0xff;
/*
* are these unnecessary?
@@ -220,38 +221,38 @@ static void vgaHWInit (struct fb_var_screeninfo *var,
/*
* Graphics Display Controller
*/
- par->Graphics[0x00] = 0x00;
- par->Graphics[0x01] = 0x00;
- par->Graphics[0x02] = 0x00;
- par->Graphics[0x03] = 0x00;
- par->Graphics[0x04] = 0x00;
- par->Graphics[0x05] = 0x40;
- par->Graphics[0x06] = 0x05; /* only map 64k VGA memory !!!! */
- par->Graphics[0x07] = 0x0F;
- par->Graphics[0x08] = 0xFF;
-
-
- par->Attribute[0x00] = 0x00; /* standard colormap translation */
- par->Attribute[0x01] = 0x01;
- par->Attribute[0x02] = 0x02;
- par->Attribute[0x03] = 0x03;
- par->Attribute[0x04] = 0x04;
- par->Attribute[0x05] = 0x05;
- par->Attribute[0x06] = 0x06;
- par->Attribute[0x07] = 0x07;
- par->Attribute[0x08] = 0x08;
- par->Attribute[0x09] = 0x09;
- par->Attribute[0x0a] = 0x0A;
- par->Attribute[0x0b] = 0x0B;
- par->Attribute[0x0c] = 0x0C;
- par->Attribute[0x0d] = 0x0D;
- par->Attribute[0x0e] = 0x0E;
- par->Attribute[0x0f] = 0x0F;
- par->Attribute[0x10] = 0x41;
- par->Attribute[0x11] = 0xFF;
- par->Attribute[0x12] = 0x0F;
- par->Attribute[0x13] = 0x00;
- par->Attribute[0x14] = 0x00;
+ reg->Graphics[0x00] = 0x00;
+ reg->Graphics[0x01] = 0x00;
+ reg->Graphics[0x02] = 0x00;
+ reg->Graphics[0x03] = 0x00;
+ reg->Graphics[0x04] = 0x00;
+ reg->Graphics[0x05] = 0x40;
+ reg->Graphics[0x06] = 0x05; /* only map 64k VGA memory !!!! */
+ reg->Graphics[0x07] = 0x0F;
+ reg->Graphics[0x08] = 0xFF;
+
+
+ reg->Attribute[0x00] = 0x00; /* standard colormap translation */
+ reg->Attribute[0x01] = 0x01;
+ reg->Attribute[0x02] = 0x02;
+ reg->Attribute[0x03] = 0x03;
+ reg->Attribute[0x04] = 0x04;
+ reg->Attribute[0x05] = 0x05;
+ reg->Attribute[0x06] = 0x06;
+ reg->Attribute[0x07] = 0x07;
+ reg->Attribute[0x08] = 0x08;
+ reg->Attribute[0x09] = 0x09;
+ reg->Attribute[0x0a] = 0x0A;
+ reg->Attribute[0x0b] = 0x0B;
+ reg->Attribute[0x0c] = 0x0C;
+ reg->Attribute[0x0d] = 0x0D;
+ reg->Attribute[0x0e] = 0x0E;
+ reg->Attribute[0x0f] = 0x0F;
+ reg->Attribute[0x10] = 0x41;
+ reg->Attribute[0x11] = 0xFF;
+ reg->Attribute[0x12] = 0x0F;
+ reg->Attribute[0x13] = 0x00;
+ reg->Attribute[0x14] = 0x00;
}
/* -------------------- Hardware specific routines ------------------------- */
@@ -304,15 +305,15 @@ savage2000_waitidle(struct savagefb_par *par)
while ((savage_in32(0x48C60, par) & 0x009fffff));
}
-
+#ifdef CONFIG_FB_SAVAGE_ACCEL
static void
-SavageSetup2DEngine (struct savagefb_par *par)
+SavageSetup2DEngine(struct savagefb_par *par)
{
unsigned long GlobalBitmapDescriptor;
GlobalBitmapDescriptor = 1 | 8 | BCI_BD_BW_DISABLE;
- BCI_BD_SET_BPP (GlobalBitmapDescriptor, par->depth);
- BCI_BD_SET_STRIDE (GlobalBitmapDescriptor, par->vwidth);
+ BCI_BD_SET_BPP(GlobalBitmapDescriptor, par->depth);
+ BCI_BD_SET_STRIDE(GlobalBitmapDescriptor, par->vwidth);
switch(par->chip) {
case S3_SAVAGE3D:
@@ -361,32 +362,48 @@ SavageSetup2DEngine (struct savagefb_par *par)
vga_out8(0x3d5, 0x0c, par);
/* Set stride to use GBD. */
- vga_out8 (0x3d4, 0x50, par);
- vga_out8 (0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
+ vga_out8(0x3d4, 0x50, par);
+ vga_out8(0x3d5, vga_in8(0x3d5, par) | 0xC1, par);
/* Enable 2D engine. */
- vga_out8 (0x3d4, 0x40, par);
- vga_out8 (0x3d5, 0x01, par);
+ vga_out8(0x3d4, 0x40, par);
+ vga_out8(0x3d5, 0x01, par);
- savage_out32 (MONO_PAT_0, ~0, par);
- savage_out32 (MONO_PAT_1, ~0, par);
+ savage_out32(MONO_PAT_0, ~0, par);
+ savage_out32(MONO_PAT_1, ~0, par);
/* Setup plane masks */
- savage_out32 (0x8128, ~0, par); /* enable all write planes */
- savage_out32 (0x812C, ~0, par); /* enable all read planes */
- savage_out16 (0x8134, 0x27, par);
- savage_out16 (0x8136, 0x07, par);
+ savage_out32(0x8128, ~0, par); /* enable all write planes */
+ savage_out32(0x812C, ~0, par); /* enable all read planes */
+ savage_out16(0x8134, 0x27, par);
+ savage_out16(0x8136, 0x07, par);
/* Now set the GBD */
par->bci_ptr = 0;
- par->SavageWaitFifo (par, 4);
+ par->SavageWaitFifo(par, 4);
- BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD1 );
- BCI_SEND( 0 );
- BCI_SEND( BCI_CMD_SETREG | (1 << 16) | BCI_GBD2 );
- BCI_SEND( GlobalBitmapDescriptor );
+ BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD1);
+ BCI_SEND(0);
+ BCI_SEND(BCI_CMD_SETREG | (1 << 16) | BCI_GBD2);
+ BCI_SEND(GlobalBitmapDescriptor);
}
+static void savagefb_set_clip(struct fb_info *info)
+{
+ struct savagefb_par *par = info->par;
+ int cmd;
+
+ cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
+ par->bci_ptr = 0;
+ par->SavageWaitFifo(par,3);
+ BCI_SEND(cmd);
+ BCI_SEND(BCI_CLIP_TL(0, 0));
+ BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
+}
+#else
+static void SavageSetup2DEngine(struct savagefb_par *par) {}
+
+#endif
static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
int min_n2, int max_n2, long freq_min,
@@ -398,11 +415,11 @@ static void SavageCalcClock(long freq, int min_m, int min_n1, int max_n1,
unsigned char n1, n2, best_n1=16+2, best_n2=2, best_m=125+2;
if (freq < freq_min / (1 << max_n2)) {
- printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
+ printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
freq = freq_min / (1 << max_n2);
}
if (freq > freq_max / (1 << min_n2)) {
- printk (KERN_ERR "invalid frequency %ld Khz\n", freq);
+ printk(KERN_ERR "invalid frequency %ld Khz\n", freq);
freq = freq_max / (1 << min_n2);
}
@@ -453,12 +470,12 @@ static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
BASE_FREQ;
if (m < min_m + 2 || m > 127+2)
continue;
- if((m * BASE_FREQ >= freq_min * n1) &&
- (m * BASE_FREQ <= freq_max * n1)) {
+ if ((m * BASE_FREQ >= freq_min * n1) &&
+ (m * BASE_FREQ <= freq_max * n1)) {
diff = freq * (1 << n2) * n1 - BASE_FREQ * m;
- if(diff < 0)
+ if (diff < 0)
diff = -diff;
- if(diff < best_diff) {
+ if (diff < best_diff) {
best_diff = diff;
best_m = m;
best_n1 = n1;
@@ -468,7 +485,7 @@ static int common_calc_clock(long freq, int min_m, int min_n1, int max_n1,
}
}
- if(max_n1 == 63)
+ if (max_n1 == 63)
*ndiv = (best_n1 - 2) | (best_n2 << 6);
else
*ndiv = (best_n1 - 2) | (best_n2 << 5);
@@ -488,23 +505,23 @@ static void SavagePrintRegs(void)
int vgaCRReg = 0x3d5;
printk(KERN_DEBUG "SR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE "
- "xF" );
+ "xF");
- for( i = 0; i < 0x70; i++ ) {
- if( !(i % 16) )
- printk(KERN_DEBUG "\nSR%xx ", i >> 4 );
- vga_out8( 0x3c4, i, par);
- printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par) );
+ for (i = 0; i < 0x70; i++) {
+ if (!(i % 16))
+ printk(KERN_DEBUG "\nSR%xx ", i >> 4);
+ vga_out8(0x3c4, i, par);
+ printk(KERN_DEBUG " %02x", vga_in8(0x3c5, par));
}
printk(KERN_DEBUG "\n\nCR x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC "
- "xD xE xF" );
+ "xD xE xF");
- for( i = 0; i < 0xB7; i++ ) {
- if( !(i % 16) )
- printk(KERN_DEBUG "\nCR%xx ", i >> 4 );
- vga_out8( vgaCRIndex, i, par);
- printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par) );
+ for (i = 0; i < 0xB7; i++) {
+ if (!(i % 16))
+ printk(KERN_DEBUG "\nCR%xx ", i >> 4);
+ vga_out8(vgaCRIndex, i, par);
+ printk(KERN_DEBUG " %02x", vga_in8(vgaCRReg, par));
}
printk(KERN_DEBUG "\n\n");
@@ -513,156 +530,309 @@ static void SavagePrintRegs(void)
/* --------------------------------------------------------------------- */
-static void savage_get_default_par(struct savagefb_par *par)
+static void savage_get_default_par(struct savagefb_par *par, struct savage_reg *reg)
{
unsigned char cr3a, cr53, cr66;
- vga_out16 (0x3d4, 0x4838, par);
- vga_out16 (0x3d4, 0xa039, par);
- vga_out16 (0x3c4, 0x0608, par);
-
- vga_out8 (0x3d4, 0x66, par);
- cr66 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr66 | 0x80, par);
- vga_out8 (0x3d4, 0x3a, par);
- cr3a = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr3a | 0x80, par);
- vga_out8 (0x3d4, 0x53, par);
- cr53 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr53 & 0x7f, par);
-
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, cr66, par);
- vga_out8 (0x3d4, 0x3a, par);
- vga_out8 (0x3d5, cr3a, par);
-
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, cr66, par);
- vga_out8 (0x3d4, 0x3a, par);
- vga_out8 (0x3d5, cr3a, par);
+ vga_out16(0x3d4, 0x4838, par);
+ vga_out16(0x3d4, 0xa039, par);
+ vga_out16(0x3c4, 0x0608, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x80, par);
+ vga_out8(0x3d4, 0x3a, par);
+ cr3a = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3a | 0x80, par);
+ vga_out8(0x3d4, 0x53, par);
+ cr53 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr53 & 0x7f, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
/* unlock extended seq regs */
- vga_out8 (0x3c4, 0x08, par);
- par->SR08 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c5, 0x06, par);
+ vga_out8(0x3c4, 0x08, par);
+ reg->SR08 = vga_in8(0x3c5, par);
+ vga_out8(0x3c5, 0x06, par);
/* now save all the extended regs we need */
- vga_out8 (0x3d4, 0x31, par);
- par->CR31 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x32, par);
- par->CR32 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x34, par);
- par->CR34 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x36, par);
- par->CR36 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x3a, par);
- par->CR3A = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x40, par);
- par->CR40 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x42, par);
- par->CR42 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x45, par);
- par->CR45 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x50, par);
- par->CR50 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x51, par);
- par->CR51 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x53, par);
- par->CR53 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x58, par);
- par->CR58 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x60, par);
- par->CR60 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x66, par);
- par->CR66 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x67, par);
- par->CR67 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x68, par);
- par->CR68 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x69, par);
- par->CR69 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x6f, par);
- par->CR6F = vga_in8 (0x3d5, par);
-
- vga_out8 (0x3d4, 0x33, par);
- par->CR33 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x86, par);
- par->CR86 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x88, par);
- par->CR88 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x90, par);
- par->CR90 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x91, par);
- par->CR91 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0xb0, par);
- par->CRB0 = vga_in8 (0x3d5, par) | 0x80;
+ vga_out8(0x3d4, 0x31, par);
+ reg->CR31 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x32, par);
+ reg->CR32 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x34, par);
+ reg->CR34 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x36, par);
+ reg->CR36 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x3a, par);
+ reg->CR3A = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x40, par);
+ reg->CR40 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x42, par);
+ reg->CR42 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x45, par);
+ reg->CR45 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x50, par);
+ reg->CR50 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x51, par);
+ reg->CR51 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x53, par);
+ reg->CR53 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x58, par);
+ reg->CR58 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x60, par);
+ reg->CR60 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x66, par);
+ reg->CR66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x67, par);
+ reg->CR67 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x68, par);
+ reg->CR68 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x69, par);
+ reg->CR69 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x6f, par);
+ reg->CR6F = vga_in8(0x3d5, par);
+
+ vga_out8(0x3d4, 0x33, par);
+ reg->CR33 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x86, par);
+ reg->CR86 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x88, par);
+ reg->CR88 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x90, par);
+ reg->CR90 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x91, par);
+ reg->CR91 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0xb0, par);
+ reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
+
+ /* extended mode timing regs */
+ vga_out8(0x3d4, 0x3b, par);
+ reg->CR3B = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x3c, par);
+ reg->CR3C = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x43, par);
+ reg->CR43 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x5d, par);
+ reg->CR5D = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x5e, par);
+ reg->CR5E = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x65, par);
+ reg->CR65 = vga_in8(0x3d5, par);
+
+ /* save seq extended regs for DCLK PLL programming */
+ vga_out8(0x3c4, 0x0e, par);
+ reg->SR0E = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x0f, par);
+ reg->SR0F = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x10, par);
+ reg->SR10 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x11, par);
+ reg->SR11 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x12, par);
+ reg->SR12 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x13, par);
+ reg->SR13 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x29, par);
+ reg->SR29 = vga_in8(0x3c5, par);
+
+ vga_out8(0x3c4, 0x15, par);
+ reg->SR15 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x30, par);
+ reg->SR30 = vga_in8(0x3c5, par);
+ vga_out8(0x3c4, 0x18, par);
+ reg->SR18 = vga_in8(0x3c5, par);
+
+ /* Save flat panel expansion regsters. */
+ if (par->chip == S3_SAVAGE_MX) {
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ vga_out8(0x3c4, 0x54+i, par);
+ reg->SR54[i] = vga_in8(0x3c5, par);
+ }
+ }
+
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x80, par);
+ vga_out8(0x3d4, 0x3a, par);
+ cr3a = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3a | 0x80, par);
+
+ /* now save MIU regs */
+ if (par->chip != S3_SAVAGE_MX) {
+ reg->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
+ reg->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
+ reg->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
+ reg->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
+ }
+
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+}
+
+static void savage_set_default_par(struct savagefb_par *par,
+ struct savage_reg *reg)
+{
+ unsigned char cr3a, cr53, cr66;
+
+ vga_out16(0x3d4, 0x4838, par);
+ vga_out16(0x3d4, 0xa039, par);
+ vga_out16(0x3c4, 0x0608, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x80, par);
+ vga_out8(0x3d4, 0x3a, par);
+ cr3a = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3a | 0x80, par);
+ vga_out8(0x3d4, 0x53, par);
+ cr53 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr53 & 0x7f, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
+
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
+
+ /* unlock extended seq regs */
+ vga_out8(0x3c4, 0x08, par);
+ vga_out8(0x3c5, reg->SR08, par);
+ vga_out8(0x3c5, 0x06, par);
+
+ /* now restore all the extended regs we need */
+ vga_out8(0x3d4, 0x31, par);
+ vga_out8(0x3d5, reg->CR31, par);
+ vga_out8(0x3d4, 0x32, par);
+ vga_out8(0x3d5, reg->CR32, par);
+ vga_out8(0x3d4, 0x34, par);
+ vga_out8(0x3d5, reg->CR34, par);
+ vga_out8(0x3d4, 0x36, par);
+ vga_out8(0x3d5,reg->CR36, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, reg->CR3A, par);
+ vga_out8(0x3d4, 0x40, par);
+ vga_out8(0x3d5, reg->CR40, par);
+ vga_out8(0x3d4, 0x42, par);
+ vga_out8(0x3d5, reg->CR42, par);
+ vga_out8(0x3d4, 0x45, par);
+ vga_out8(0x3d5, reg->CR45, par);
+ vga_out8(0x3d4, 0x50, par);
+ vga_out8(0x3d5, reg->CR50, par);
+ vga_out8(0x3d4, 0x51, par);
+ vga_out8(0x3d5, reg->CR51, par);
+ vga_out8(0x3d4, 0x53, par);
+ vga_out8(0x3d5, reg->CR53, par);
+ vga_out8(0x3d4, 0x58, par);
+ vga_out8(0x3d5, reg->CR58, par);
+ vga_out8(0x3d4, 0x60, par);
+ vga_out8(0x3d5, reg->CR60, par);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, reg->CR66, par);
+ vga_out8(0x3d4, 0x67, par);
+ vga_out8(0x3d5, reg->CR67, par);
+ vga_out8(0x3d4, 0x68, par);
+ vga_out8(0x3d5, reg->CR68, par);
+ vga_out8(0x3d4, 0x69, par);
+ vga_out8(0x3d5, reg->CR69, par);
+ vga_out8(0x3d4, 0x6f, par);
+ vga_out8(0x3d5, reg->CR6F, par);
+
+ vga_out8(0x3d4, 0x33, par);
+ vga_out8(0x3d5, reg->CR33, par);
+ vga_out8(0x3d4, 0x86, par);
+ vga_out8(0x3d5, reg->CR86, par);
+ vga_out8(0x3d4, 0x88, par);
+ vga_out8(0x3d5, reg->CR88, par);
+ vga_out8(0x3d4, 0x90, par);
+ vga_out8(0x3d5, reg->CR90, par);
+ vga_out8(0x3d4, 0x91, par);
+ vga_out8(0x3d5, reg->CR91, par);
+ vga_out8(0x3d4, 0xb0, par);
+ vga_out8(0x3d5, reg->CRB0, par);
/* extended mode timing regs */
- vga_out8 (0x3d4, 0x3b, par);
- par->CR3B = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x3c, par);
- par->CR3C = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x43, par);
- par->CR43 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x5d, par);
- par->CR5D = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x5e, par);
- par->CR5E = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x65, par);
- par->CR65 = vga_in8 (0x3d5, par);
+ vga_out8(0x3d4, 0x3b, par);
+ vga_out8(0x3d5, reg->CR3B, par);
+ vga_out8(0x3d4, 0x3c, par);
+ vga_out8(0x3d5, reg->CR3C, par);
+ vga_out8(0x3d4, 0x43, par);
+ vga_out8(0x3d5, reg->CR43, par);
+ vga_out8(0x3d4, 0x5d, par);
+ vga_out8(0x3d5, reg->CR5D, par);
+ vga_out8(0x3d4, 0x5e, par);
+ vga_out8(0x3d5, reg->CR5E, par);
+ vga_out8(0x3d4, 0x65, par);
+ vga_out8(0x3d5, reg->CR65, par);
/* save seq extended regs for DCLK PLL programming */
- vga_out8 (0x3c4, 0x0e, par);
- par->SR0E = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x0f, par);
- par->SR0F = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x10, par);
- par->SR10 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x11, par);
- par->SR11 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x12, par);
- par->SR12 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x13, par);
- par->SR13 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x29, par);
- par->SR29 = vga_in8 (0x3c5, par);
-
- vga_out8 (0x3c4, 0x15, par);
- par->SR15 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x30, par);
- par->SR30 = vga_in8 (0x3c5, par);
- vga_out8 (0x3c4, 0x18, par);
- par->SR18 = vga_in8 (0x3c5, par);
+ vga_out8(0x3c4, 0x0e, par);
+ vga_out8(0x3c5, reg->SR0E, par);
+ vga_out8(0x3c4, 0x0f, par);
+ vga_out8(0x3c5, reg->SR0F, par);
+ vga_out8(0x3c4, 0x10, par);
+ vga_out8(0x3c5, reg->SR10, par);
+ vga_out8(0x3c4, 0x11, par);
+ vga_out8(0x3c5, reg->SR11, par);
+ vga_out8(0x3c4, 0x12, par);
+ vga_out8(0x3c5, reg->SR12, par);
+ vga_out8(0x3c4, 0x13, par);
+ vga_out8(0x3c5, reg->SR13, par);
+ vga_out8(0x3c4, 0x29, par);
+ vga_out8(0x3c5, reg->SR29, par);
+
+ vga_out8(0x3c4, 0x15, par);
+ vga_out8(0x3c5, reg->SR15, par);
+ vga_out8(0x3c4, 0x30, par);
+ vga_out8(0x3c5, reg->SR30, par);
+ vga_out8(0x3c4, 0x18, par);
+ vga_out8(0x3c5, reg->SR18, par);
/* Save flat panel expansion regsters. */
if (par->chip == S3_SAVAGE_MX) {
int i;
for (i = 0; i < 8; i++) {
- vga_out8 (0x3c4, 0x54+i, par);
- par->SR54[i] = vga_in8 (0x3c5, par);
+ vga_out8(0x3c4, 0x54+i, par);
+ vga_out8(0x3c5, reg->SR54[i], par);
}
}
- vga_out8 (0x3d4, 0x66, par);
- cr66 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr66 | 0x80, par);
- vga_out8 (0x3d4, 0x3a, par);
- cr3a = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr3a | 0x80, par);
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x80, par);
+ vga_out8(0x3d4, 0x3a, par);
+ cr3a = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3a | 0x80, par);
/* now save MIU regs */
if (par->chip != S3_SAVAGE_MX) {
- par->MMPR0 = savage_in32(FIFO_CONTROL_REG, par);
- par->MMPR1 = savage_in32(MIU_CONTROL_REG, par);
- par->MMPR2 = savage_in32(STREAMS_TIMEOUT_REG, par);
- par->MMPR3 = savage_in32(MISC_TIMEOUT_REG, par);
+ savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
+ savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
+ savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
+ savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
}
- vga_out8 (0x3d4, 0x3a, par);
- vga_out8 (0x3d5, cr3a, par);
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
}
static void savage_update_var(struct fb_var_screeninfo *var, struct fb_videomode *modedb)
@@ -683,8 +853,8 @@ static void savage_update_var(struct fb_var_screeninfo *var, struct fb_videomode
var->vmode = modedb->vmode;
}
-static int savagefb_check_var (struct fb_var_screeninfo *var,
- struct fb_info *info)
+static int savagefb_check_var(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct savagefb_par *par = info->par;
int memlen, vramlen, mode_valid = 0;
@@ -750,10 +920,10 @@ static int savagefb_check_var (struct fb_var_screeninfo *var,
if (par->SavagePanelWidth &&
(var->xres > par->SavagePanelWidth ||
var->yres > par->SavagePanelHeight)) {
- printk (KERN_INFO "Mode (%dx%d) larger than the LCD panel "
- "(%dx%d)\n", var->xres, var->yres,
- par->SavagePanelWidth,
- par->SavagePanelHeight);
+ printk(KERN_INFO "Mode (%dx%d) larger than the LCD panel "
+ "(%dx%d)\n", var->xres, var->yres,
+ par->SavagePanelWidth,
+ par->SavagePanelHeight);
return -1;
}
@@ -788,8 +958,9 @@ static int savagefb_check_var (struct fb_var_screeninfo *var,
}
-static int savagefb_decode_var (struct fb_var_screeninfo *var,
- struct savagefb_par *par)
+static int savagefb_decode_var(struct fb_var_screeninfo *var,
+ struct savagefb_par *par,
+ struct savage_reg *reg)
{
struct xtimings timings;
int width, dclk, i, j; /*, refresh; */
@@ -799,7 +970,7 @@ static int savagefb_decode_var (struct fb_var_screeninfo *var,
DBG("savagefb_decode_var");
- memset (&timings, 0, sizeof(timings));
+ memset(&timings, 0, sizeof(timings));
if (!pixclock) pixclock = 10000; /* 10ns = 100MHz */
timings.Clock = 1000000000 / pixclock;
@@ -831,39 +1002,39 @@ static int savagefb_decode_var (struct fb_var_screeninfo *var,
* This will allocate the datastructure and initialize all of the
* generic VGA registers.
*/
- vgaHWInit (var, par, &timings);
+ vgaHWInit(var, par, &timings, reg);
/* We need to set CR67 whether or not we use the BIOS. */
dclk = timings.Clock;
- par->CR67 = 0x00;
+ reg->CR67 = 0x00;
- switch( var->bits_per_pixel ) {
+ switch(var->bits_per_pixel) {
case 8:
- if( (par->chip == S3_SAVAGE2000) && (dclk >= 230000) )
- par->CR67 = 0x10; /* 8bpp, 2 pixels/clock */
+ if ((par->chip == S3_SAVAGE2000) && (dclk >= 230000))
+ reg->CR67 = 0x10; /* 8bpp, 2 pixels/clock */
else
- par->CR67 = 0x00; /* 8bpp, 1 pixel/clock */
+ reg->CR67 = 0x00; /* 8bpp, 1 pixel/clock */
break;
case 15:
- if ( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
- ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
- par->CR67 = 0x30; /* 15bpp, 2 pixel/clock */
+ if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
+ ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
+ reg->CR67 = 0x30; /* 15bpp, 2 pixel/clock */
else
- par->CR67 = 0x20; /* 15bpp, 1 pixels/clock */
+ reg->CR67 = 0x20; /* 15bpp, 1 pixels/clock */
break;
case 16:
- if( S3_SAVAGE_MOBILE_SERIES(par->chip) ||
- ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)) )
- par->CR67 = 0x50; /* 16bpp, 2 pixel/clock */
+ if (S3_SAVAGE_MOBILE_SERIES(par->chip) ||
+ ((par->chip == S3_SAVAGE2000) && (dclk >= 230000)))
+ reg->CR67 = 0x50; /* 16bpp, 2 pixel/clock */
else
- par->CR67 = 0x40; /* 16bpp, 1 pixels/clock */
+ reg->CR67 = 0x40; /* 16bpp, 1 pixels/clock */
break;
case 24:
- par->CR67 = 0x70;
+ reg->CR67 = 0x70;
break;
case 32:
- par->CR67 = 0xd0;
+ reg->CR67 = 0xd0;
break;
}
@@ -872,61 +1043,61 @@ static int savagefb_decode_var (struct fb_var_screeninfo *var,
* match. Fall back to traditional register-crunching.
*/
- vga_out8 (0x3d4, 0x3a, par);
- tmp = vga_in8 (0x3d5, par);
+ vga_out8(0x3d4, 0x3a, par);
+ tmp = vga_in8(0x3d5, par);
if (1 /*FIXME:psav->pci_burst*/)
- par->CR3A = (tmp & 0x7f) | 0x15;
+ reg->CR3A = (tmp & 0x7f) | 0x15;
else
- par->CR3A = tmp | 0x95;
+ reg->CR3A = tmp | 0x95;
- par->CR53 = 0x00;
- par->CR31 = 0x8c;
- par->CR66 = 0x89;
+ reg->CR53 = 0x00;
+ reg->CR31 = 0x8c;
+ reg->CR66 = 0x89;
- vga_out8 (0x3d4, 0x58, par);
- par->CR58 = vga_in8 (0x3d5, par) & 0x80;
- par->CR58 |= 0x13;
+ vga_out8(0x3d4, 0x58, par);
+ reg->CR58 = vga_in8(0x3d5, par) & 0x80;
+ reg->CR58 |= 0x13;
- par->SR15 = 0x03 | 0x80;
- par->SR18 = 0x00;
- par->CR43 = par->CR45 = par->CR65 = 0x00;
+ reg->SR15 = 0x03 | 0x80;
+ reg->SR18 = 0x00;
+ reg->CR43 = reg->CR45 = reg->CR65 = 0x00;
- vga_out8 (0x3d4, 0x40, par);
- par->CR40 = vga_in8 (0x3d5, par) & ~0x01;
+ vga_out8(0x3d4, 0x40, par);
+ reg->CR40 = vga_in8(0x3d5, par) & ~0x01;
- par->MMPR0 = 0x010400;
- par->MMPR1 = 0x00;
- par->MMPR2 = 0x0808;
- par->MMPR3 = 0x08080810;
+ reg->MMPR0 = 0x010400;
+ reg->MMPR1 = 0x00;
+ reg->MMPR2 = 0x0808;
+ reg->MMPR3 = 0x08080810;
- SavageCalcClock (dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
+ SavageCalcClock(dclk, 1, 1, 127, 0, 4, 180000, 360000, &m, &n, &r);
/* m = 107; n = 4; r = 2; */
if (par->MCLK <= 0) {
- par->SR10 = 255;
- par->SR11 = 255;
+ reg->SR10 = 255;
+ reg->SR11 = 255;
} else {
- common_calc_clock (par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
- &par->SR11, &par->SR10);
- /* par->SR10 = 80; // MCLK == 286000 */
- /* par->SR11 = 125; */
+ common_calc_clock(par->MCLK, 1, 1, 31, 0, 3, 135000, 270000,
+ &reg->SR11, &reg->SR10);
+ /* reg->SR10 = 80; // MCLK == 286000 */
+ /* reg->SR11 = 125; */
}
- par->SR12 = (r << 6) | (n & 0x3f);
- par->SR13 = m & 0xff;
- par->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
+ reg->SR12 = (r << 6) | (n & 0x3f);
+ reg->SR13 = m & 0xff;
+ reg->SR29 = (r & 4) | (m & 0x100) >> 5 | (n & 0x40) >> 2;
if (var->bits_per_pixel < 24)
- par->MMPR0 -= 0x8000;
+ reg->MMPR0 -= 0x8000;
else
- par->MMPR0 -= 0x4000;
+ reg->MMPR0 -= 0x4000;
if (timings.interlaced)
- par->CR42 = 0x20;
+ reg->CR42 = 0x20;
else
- par->CR42 = 0x00;
+ reg->CR42 = 0x00;
- par->CR34 = 0x10; /* display fifo */
+ reg->CR34 = 0x10; /* display fifo */
i = ((((timings.HTotal >> 3) - 5) & 0x100) >> 8) |
((((timings.HDisplay >> 3) - 1) & 0x100) >> 7) |
@@ -938,77 +1109,77 @@ static int savagefb_decode_var (struct fb_var_screeninfo *var,
if ((timings.HSyncEnd >> 3) - (timings.HSyncStart >> 3) > 32)
i |= 0x20;
- j = (par->CRTC[0] + ((i & 0x01) << 8) +
- par->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
+ j = (reg->CRTC[0] + ((i & 0x01) << 8) +
+ reg->CRTC[4] + ((i & 0x10) << 4) + 1) / 2;
- if (j - (par->CRTC[4] + ((i & 0x10) << 4)) < 4) {
- if (par->CRTC[4] + ((i & 0x10) << 4) + 4 <=
- par->CRTC[0] + ((i & 0x01) << 8))
- j = par->CRTC[4] + ((i & 0x10) << 4) + 4;
+ if (j - (reg->CRTC[4] + ((i & 0x10) << 4)) < 4) {
+ if (reg->CRTC[4] + ((i & 0x10) << 4) + 4 <=
+ reg->CRTC[0] + ((i & 0x01) << 8))
+ j = reg->CRTC[4] + ((i & 0x10) << 4) + 4;
else
- j = par->CRTC[0] + ((i & 0x01) << 8) + 1;
+ j = reg->CRTC[0] + ((i & 0x01) << 8) + 1;
}
- par->CR3B = j & 0xff;
+ reg->CR3B = j & 0xff;
i |= (j & 0x100) >> 2;
- par->CR3C = (par->CRTC[0] + ((i & 0x01) << 8)) / 2;
- par->CR5D = i;
- par->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
+ reg->CR3C = (reg->CRTC[0] + ((i & 0x01) << 8)) / 2;
+ reg->CR5D = i;
+ reg->CR5E = (((timings.VTotal - 2) & 0x400) >> 10) |
(((timings.VDisplay - 1) & 0x400) >> 9) |
(((timings.VSyncStart) & 0x400) >> 8) |
(((timings.VSyncStart) & 0x400) >> 6) | 0x40;
width = (var->xres_virtual * ((var->bits_per_pixel+7) / 8)) >> 3;
- par->CR91 = par->CRTC[19] = 0xff & width;
- par->CR51 = (0x300 & width) >> 4;
- par->CR90 = 0x80 | (width >> 8);
- par->MiscOutReg |= 0x0c;
+ reg->CR91 = reg->CRTC[19] = 0xff & width;
+ reg->CR51 = (0x300 & width) >> 4;
+ reg->CR90 = 0x80 | (width >> 8);
+ reg->MiscOutReg |= 0x0c;
/* Set frame buffer description. */
if (var->bits_per_pixel <= 8)
- par->CR50 = 0;
+ reg->CR50 = 0;
else if (var->bits_per_pixel <= 16)
- par->CR50 = 0x10;
+ reg->CR50 = 0x10;
else
- par->CR50 = 0x30;
+ reg->CR50 = 0x30;
if (var->xres_virtual <= 640)
- par->CR50 |= 0x40;
+ reg->CR50 |= 0x40;
else if (var->xres_virtual == 800)
- par->CR50 |= 0x80;
+ reg->CR50 |= 0x80;
else if (var->xres_virtual == 1024)
- par->CR50 |= 0x00;
+ reg->CR50 |= 0x00;
else if (var->xres_virtual == 1152)
- par->CR50 |= 0x01;
+ reg->CR50 |= 0x01;
else if (var->xres_virtual == 1280)
- par->CR50 |= 0xc0;
+ reg->CR50 |= 0xc0;
else if (var->xres_virtual == 1600)
- par->CR50 |= 0x81;
+ reg->CR50 |= 0x81;
else
- par->CR50 |= 0xc1; /* Use GBD */
+ reg->CR50 |= 0xc1; /* Use GBD */
- if( par->chip == S3_SAVAGE2000 )
- par->CR33 = 0x08;
+ if (par->chip == S3_SAVAGE2000)
+ reg->CR33 = 0x08;
else
- par->CR33 = 0x20;
+ reg->CR33 = 0x20;
- par->CRTC[0x17] = 0xeb;
+ reg->CRTC[0x17] = 0xeb;
- par->CR67 |= 1;
+ reg->CR67 |= 1;
vga_out8(0x3d4, 0x36, par);
- par->CR36 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x68, par);
- par->CR68 = vga_in8 (0x3d5, par);
- par->CR69 = 0;
- vga_out8 (0x3d4, 0x6f, par);
- par->CR6F = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x86, par);
- par->CR86 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d4, 0x88, par);
- par->CR88 = vga_in8 (0x3d5, par) | 0x08;
- vga_out8 (0x3d4, 0xb0, par);
- par->CRB0 = vga_in8 (0x3d5, par) | 0x80;
+ reg->CR36 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x68, par);
+ reg->CR68 = vga_in8(0x3d5, par);
+ reg->CR69 = 0;
+ vga_out8(0x3d4, 0x6f, par);
+ reg->CR6F = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x86, par);
+ reg->CR86 = vga_in8(0x3d5, par);
+ vga_out8(0x3d4, 0x88, par);
+ reg->CR88 = vga_in8(0x3d5, par) | 0x08;
+ vga_out8(0x3d4, 0xb0, par);
+ reg->CRB0 = vga_in8(0x3d5, par) | 0x80;
return 0;
}
@@ -1037,11 +1208,11 @@ static int savagefb_setcolreg(unsigned regno,
switch (info->var.bits_per_pixel) {
case 8:
- vga_out8 (0x3c8, regno, par);
+ vga_out8(0x3c8, regno, par);
- vga_out8 (0x3c9, red >> 10, par);
- vga_out8 (0x3c9, green >> 10, par);
- vga_out8 (0x3c9, blue >> 10, par);
+ vga_out8(0x3c9, red >> 10, par);
+ vga_out8(0x3c9, green >> 10, par);
+ vga_out8(0x3c9, blue >> 10, par);
break;
case 16:
@@ -1075,21 +1246,21 @@ static int savagefb_setcolreg(unsigned regno,
return 0;
}
-static void savagefb_set_par_int (struct savagefb_par *par)
+static void savagefb_set_par_int(struct savagefb_par *par, struct savage_reg *reg)
{
unsigned char tmp, cr3a, cr66, cr67;
- DBG ("savagefb_set_par_int");
+ DBG("savagefb_set_par_int");
- par->SavageWaitIdle (par);
+ par->SavageWaitIdle(par);
- vga_out8 (0x3c2, 0x23, par);
+ vga_out8(0x3c2, 0x23, par);
- vga_out16 (0x3d4, 0x4838, par);
- vga_out16 (0x3d4, 0xa539, par);
- vga_out16 (0x3c4, 0x0608, par);
+ vga_out16(0x3d4, 0x4838, par);
+ vga_out16(0x3d4, 0xa539, par);
+ vga_out16(0x3c4, 0x0608, par);
- vgaHWProtect (par, 1);
+ vgaHWProtect(par, 1);
/*
* Some Savage/MX and /IX systems go nuts when trying to exit the
@@ -1099,203 +1270,202 @@ static void savagefb_set_par_int (struct savagefb_par *par)
*/
VerticalRetraceWait(par);
- vga_out8 (0x3d4, 0x67, par);
- cr67 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
+ vga_out8(0x3d4, 0x67, par);
+ cr67 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr67/*par->CR67*/ & ~0x0c, par); /* no STREAMS yet */
- vga_out8 (0x3d4, 0x23, par);
- vga_out8 (0x3d5, 0x00, par);
- vga_out8 (0x3d4, 0x26, par);
- vga_out8 (0x3d5, 0x00, par);
+ vga_out8(0x3d4, 0x23, par);
+ vga_out8(0x3d5, 0x00, par);
+ vga_out8(0x3d4, 0x26, par);
+ vga_out8(0x3d5, 0x00, par);
/* restore extended regs */
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, par->CR66, par);
- vga_out8 (0x3d4, 0x3a, par);
- vga_out8 (0x3d5, par->CR3A, par);
- vga_out8 (0x3d4, 0x31, par);
- vga_out8 (0x3d5, par->CR31, par);
- vga_out8 (0x3d4, 0x32, par);
- vga_out8 (0x3d5, par->CR32, par);
- vga_out8 (0x3d4, 0x58, par);
- vga_out8 (0x3d5, par->CR58, par);
- vga_out8 (0x3d4, 0x53, par);
- vga_out8 (0x3d5, par->CR53 & 0x7f, par);
-
- vga_out16 (0x3c4, 0x0608, par);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, reg->CR66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, reg->CR3A, par);
+ vga_out8(0x3d4, 0x31, par);
+ vga_out8(0x3d5, reg->CR31, par);
+ vga_out8(0x3d4, 0x32, par);
+ vga_out8(0x3d5, reg->CR32, par);
+ vga_out8(0x3d4, 0x58, par);
+ vga_out8(0x3d5, reg->CR58, par);
+ vga_out8(0x3d4, 0x53, par);
+ vga_out8(0x3d5, reg->CR53 & 0x7f, par);
+
+ vga_out16(0x3c4, 0x0608, par);
/* Restore DCLK registers. */
- vga_out8 (0x3c4, 0x0e, par);
- vga_out8 (0x3c5, par->SR0E, par);
- vga_out8 (0x3c4, 0x0f, par);
- vga_out8 (0x3c5, par->SR0F, par);
- vga_out8 (0x3c4, 0x29, par);
- vga_out8 (0x3c5, par->SR29, par);
- vga_out8 (0x3c4, 0x15, par);
- vga_out8 (0x3c5, par->SR15, par);
+ vga_out8(0x3c4, 0x0e, par);
+ vga_out8(0x3c5, reg->SR0E, par);
+ vga_out8(0x3c4, 0x0f, par);
+ vga_out8(0x3c5, reg->SR0F, par);
+ vga_out8(0x3c4, 0x29, par);
+ vga_out8(0x3c5, reg->SR29, par);
+ vga_out8(0x3c4, 0x15, par);
+ vga_out8(0x3c5, reg->SR15, par);
/* Restore flat panel expansion regsters. */
- if( par->chip == S3_SAVAGE_MX ) {
+ if (par->chip == S3_SAVAGE_MX) {
int i;
- for( i = 0; i < 8; i++ ) {
- vga_out8 (0x3c4, 0x54+i, par);
- vga_out8 (0x3c5, par->SR54[i], par);
+ for (i = 0; i < 8; i++) {
+ vga_out8(0x3c4, 0x54+i, par);
+ vga_out8(0x3c5, reg->SR54[i], par);
}
}
- vgaHWRestore (par);
+ vgaHWRestore (par, reg);
/* extended mode timing registers */
- vga_out8 (0x3d4, 0x53, par);
- vga_out8 (0x3d5, par->CR53, par);
- vga_out8 (0x3d4, 0x5d, par);
- vga_out8 (0x3d5, par->CR5D, par);
- vga_out8 (0x3d4, 0x5e, par);
- vga_out8 (0x3d5, par->CR5E, par);
- vga_out8 (0x3d4, 0x3b, par);
- vga_out8 (0x3d5, par->CR3B, par);
- vga_out8 (0x3d4, 0x3c, par);
- vga_out8 (0x3d5, par->CR3C, par);
- vga_out8 (0x3d4, 0x43, par);
- vga_out8 (0x3d5, par->CR43, par);
- vga_out8 (0x3d4, 0x65, par);
- vga_out8 (0x3d5, par->CR65, par);
+ vga_out8(0x3d4, 0x53, par);
+ vga_out8(0x3d5, reg->CR53, par);
+ vga_out8(0x3d4, 0x5d, par);
+ vga_out8(0x3d5, reg->CR5D, par);
+ vga_out8(0x3d4, 0x5e, par);
+ vga_out8(0x3d5, reg->CR5E, par);
+ vga_out8(0x3d4, 0x3b, par);
+ vga_out8(0x3d5, reg->CR3B, par);
+ vga_out8(0x3d4, 0x3c, par);
+ vga_out8(0x3d5, reg->CR3C, par);
+ vga_out8(0x3d4, 0x43, par);
+ vga_out8(0x3d5, reg->CR43, par);
+ vga_out8(0x3d4, 0x65, par);
+ vga_out8(0x3d5, reg->CR65, par);
/* restore the desired video mode with cr67 */
- vga_out8 (0x3d4, 0x67, par);
+ vga_out8(0x3d4, 0x67, par);
/* following part not present in X11 driver */
- cr67 = vga_in8 (0x3d5, par) & 0xf;
- vga_out8 (0x3d5, 0x50 | cr67, par);
- udelay (10000);
- vga_out8 (0x3d4, 0x67, par);
+ cr67 = vga_in8(0x3d5, par) & 0xf;
+ vga_out8(0x3d5, 0x50 | cr67, par);
+ udelay(10000);
+ vga_out8(0x3d4, 0x67, par);
/* end of part */
- vga_out8 (0x3d5, par->CR67 & ~0x0c, par);
+ vga_out8(0x3d5, reg->CR67 & ~0x0c, par);
/* other mode timing and extended regs */
- vga_out8 (0x3d4, 0x34, par);
- vga_out8 (0x3d5, par->CR34, par);
- vga_out8 (0x3d4, 0x40, par);
- vga_out8 (0x3d5, par->CR40, par);
- vga_out8 (0x3d4, 0x42, par);
- vga_out8 (0x3d5, par->CR42, par);
- vga_out8 (0x3d4, 0x45, par);
- vga_out8 (0x3d5, par->CR45, par);
- vga_out8 (0x3d4, 0x50, par);
- vga_out8 (0x3d5, par->CR50, par);
- vga_out8 (0x3d4, 0x51, par);
- vga_out8 (0x3d5, par->CR51, par);
+ vga_out8(0x3d4, 0x34, par);
+ vga_out8(0x3d5, reg->CR34, par);
+ vga_out8(0x3d4, 0x40, par);
+ vga_out8(0x3d5, reg->CR40, par);
+ vga_out8(0x3d4, 0x42, par);
+ vga_out8(0x3d5, reg->CR42, par);
+ vga_out8(0x3d4, 0x45, par);
+ vga_out8(0x3d5, reg->CR45, par);
+ vga_out8(0x3d4, 0x50, par);
+ vga_out8(0x3d5, reg->CR50, par);
+ vga_out8(0x3d4, 0x51, par);
+ vga_out8(0x3d5, reg->CR51, par);
/* memory timings */
- vga_out8 (0x3d4, 0x36, par);
- vga_out8 (0x3d5, par->CR36, par);
- vga_out8 (0x3d4, 0x60, par);
- vga_out8 (0x3d5, par->CR60, par);
- vga_out8 (0x3d4, 0x68, par);
- vga_out8 (0x3d5, par->CR68, par);
- vga_out8 (0x3d4, 0x69, par);
- vga_out8 (0x3d5, par->CR69, par);
- vga_out8 (0x3d4, 0x6f, par);
- vga_out8 (0x3d5, par->CR6F, par);
-
- vga_out8 (0x3d4, 0x33, par);
- vga_out8 (0x3d5, par->CR33, par);
- vga_out8 (0x3d4, 0x86, par);
- vga_out8 (0x3d5, par->CR86, par);
- vga_out8 (0x3d4, 0x88, par);
- vga_out8 (0x3d5, par->CR88, par);
- vga_out8 (0x3d4, 0x90, par);
- vga_out8 (0x3d5, par->CR90, par);
- vga_out8 (0x3d4, 0x91, par);
- vga_out8 (0x3d5, par->CR91, par);
+ vga_out8(0x3d4, 0x36, par);
+ vga_out8(0x3d5, reg->CR36, par);
+ vga_out8(0x3d4, 0x60, par);
+ vga_out8(0x3d5, reg->CR60, par);
+ vga_out8(0x3d4, 0x68, par);
+ vga_out8(0x3d5, reg->CR68, par);
+ vga_out8(0x3d4, 0x69, par);
+ vga_out8(0x3d5, reg->CR69, par);
+ vga_out8(0x3d4, 0x6f, par);
+ vga_out8(0x3d5, reg->CR6F, par);
+
+ vga_out8(0x3d4, 0x33, par);
+ vga_out8(0x3d5, reg->CR33, par);
+ vga_out8(0x3d4, 0x86, par);
+ vga_out8(0x3d5, reg->CR86, par);
+ vga_out8(0x3d4, 0x88, par);
+ vga_out8(0x3d5, reg->CR88, par);
+ vga_out8(0x3d4, 0x90, par);
+ vga_out8(0x3d5, reg->CR90, par);
+ vga_out8(0x3d4, 0x91, par);
+ vga_out8(0x3d5, reg->CR91, par);
if (par->chip == S3_SAVAGE4) {
- vga_out8 (0x3d4, 0xb0, par);
- vga_out8 (0x3d5, par->CRB0, par);
+ vga_out8(0x3d4, 0xb0, par);
+ vga_out8(0x3d5, reg->CRB0, par);
}
- vga_out8 (0x3d4, 0x32, par);
- vga_out8 (0x3d5, par->CR32, par);
+ vga_out8(0x3d4, 0x32, par);
+ vga_out8(0x3d5, reg->CR32, par);
/* unlock extended seq regs */
- vga_out8 (0x3c4, 0x08, par);
- vga_out8 (0x3c5, 0x06, par);
+ vga_out8(0x3c4, 0x08, par);
+ vga_out8(0x3c5, 0x06, par);
/* Restore extended sequencer regs for MCLK. SR10 == 255 indicates
* that we should leave the default SR10 and SR11 values there.
*/
- if (par->SR10 != 255) {
- vga_out8 (0x3c4, 0x10, par);
- vga_out8 (0x3c5, par->SR10, par);
- vga_out8 (0x3c4, 0x11, par);
- vga_out8 (0x3c5, par->SR11, par);
+ if (reg->SR10 != 255) {
+ vga_out8(0x3c4, 0x10, par);
+ vga_out8(0x3c5, reg->SR10, par);
+ vga_out8(0x3c4, 0x11, par);
+ vga_out8(0x3c5, reg->SR11, par);
}
/* restore extended seq regs for dclk */
- vga_out8 (0x3c4, 0x0e, par);
- vga_out8 (0x3c5, par->SR0E, par);
- vga_out8 (0x3c4, 0x0f, par);
- vga_out8 (0x3c5, par->SR0F, par);
- vga_out8 (0x3c4, 0x12, par);
- vga_out8 (0x3c5, par->SR12, par);
- vga_out8 (0x3c4, 0x13, par);
- vga_out8 (0x3c5, par->SR13, par);
- vga_out8 (0x3c4, 0x29, par);
- vga_out8 (0x3c5, par->SR29, par);
-
- vga_out8 (0x3c4, 0x18, par);
- vga_out8 (0x3c5, par->SR18, par);
+ vga_out8(0x3c4, 0x0e, par);
+ vga_out8(0x3c5, reg->SR0E, par);
+ vga_out8(0x3c4, 0x0f, par);
+ vga_out8(0x3c5, reg->SR0F, par);
+ vga_out8(0x3c4, 0x12, par);
+ vga_out8(0x3c5, reg->SR12, par);
+ vga_out8(0x3c4, 0x13, par);
+ vga_out8(0x3c5, reg->SR13, par);
+ vga_out8(0x3c4, 0x29, par);
+ vga_out8(0x3c5, reg->SR29, par);
+ vga_out8(0x3c4, 0x18, par);
+ vga_out8(0x3c5, reg->SR18, par);
/* load new m, n pll values for dclk & mclk */
- vga_out8 (0x3c4, 0x15, par);
- tmp = vga_in8 (0x3c5, par) & ~0x21;
+ vga_out8(0x3c4, 0x15, par);
+ tmp = vga_in8(0x3c5, par) & ~0x21;
- vga_out8 (0x3c5, tmp | 0x03, par);
- vga_out8 (0x3c5, tmp | 0x23, par);
- vga_out8 (0x3c5, tmp | 0x03, par);
- vga_out8 (0x3c5, par->SR15, par);
- udelay (100);
+ vga_out8(0x3c5, tmp | 0x03, par);
+ vga_out8(0x3c5, tmp | 0x23, par);
+ vga_out8(0x3c5, tmp | 0x03, par);
+ vga_out8(0x3c5, reg->SR15, par);
+ udelay(100);
- vga_out8 (0x3c4, 0x30, par);
- vga_out8 (0x3c5, par->SR30, par);
- vga_out8 (0x3c4, 0x08, par);
- vga_out8 (0x3c5, par->SR08, par);
+ vga_out8(0x3c4, 0x30, par);
+ vga_out8(0x3c5, reg->SR30, par);
+ vga_out8(0x3c4, 0x08, par);
+ vga_out8(0x3c5, reg->SR08, par);
/* now write out cr67 in full, possibly starting STREAMS */
VerticalRetraceWait(par);
- vga_out8 (0x3d4, 0x67, par);
- vga_out8 (0x3d5, par->CR67, par);
+ vga_out8(0x3d4, 0x67, par);
+ vga_out8(0x3d5, reg->CR67, par);
- vga_out8 (0x3d4, 0x66, par);
- cr66 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr66 | 0x80, par);
- vga_out8 (0x3d4, 0x3a, par);
- cr3a = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr3a | 0x80, par);
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x80, par);
+ vga_out8(0x3d4, 0x3a, par);
+ cr3a = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3a | 0x80, par);
if (par->chip != S3_SAVAGE_MX) {
VerticalRetraceWait(par);
- savage_out32 (FIFO_CONTROL_REG, par->MMPR0, par);
- par->SavageWaitIdle (par);
- savage_out32 (MIU_CONTROL_REG, par->MMPR1, par);
- par->SavageWaitIdle (par);
- savage_out32 (STREAMS_TIMEOUT_REG, par->MMPR2, par);
- par->SavageWaitIdle (par);
- savage_out32 (MISC_TIMEOUT_REG, par->MMPR3, par);
+ savage_out32(FIFO_CONTROL_REG, reg->MMPR0, par);
+ par->SavageWaitIdle(par);
+ savage_out32(MIU_CONTROL_REG, reg->MMPR1, par);
+ par->SavageWaitIdle(par);
+ savage_out32(STREAMS_TIMEOUT_REG, reg->MMPR2, par);
+ par->SavageWaitIdle(par);
+ savage_out32(MISC_TIMEOUT_REG, reg->MMPR3, par);
}
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, cr66, par);
- vga_out8 (0x3d4, 0x3a, par);
- vga_out8 (0x3d5, cr3a, par);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66, par);
+ vga_out8(0x3d4, 0x3a, par);
+ vga_out8(0x3d5, cr3a, par);
- SavageSetup2DEngine (par);
- vgaHWProtect (par, 0);
+ SavageSetup2DEngine(par);
+ vgaHWProtect(par, 0);
}
-static void savagefb_update_start (struct savagefb_par *par,
- struct fb_var_screeninfo *var)
+static void savagefb_update_start(struct savagefb_par *par,
+ struct fb_var_screeninfo *var)
{
int base;
@@ -1305,8 +1475,8 @@ static void savagefb_update_start (struct savagefb_par *par,
/* now program the start address registers */
vga_out16(0x3d4, (base & 0x00ff00) | 0x0c, par);
vga_out16(0x3d4, ((base & 0x00ff) << 8) | 0x0d, par);
- vga_out8 (0x3d4, 0x69, par);
- vga_out8 (0x3d5, (base & 0x7f0000) >> 16, par);
+ vga_out8(0x3d4, 0x69, par);
+ vga_out8(0x3d5, (base & 0x7f0000) >> 16, par);
}
@@ -1325,29 +1495,14 @@ static void savagefb_set_fix(struct fb_info *info)
}
-#if defined(CONFIG_FB_SAVAGE_ACCEL)
-static void savagefb_set_clip(struct fb_info *info)
-{
- struct savagefb_par *par = info->par;
- int cmd;
-
- cmd = BCI_CMD_NOP | BCI_CMD_CLIP_NEW;
- par->bci_ptr = 0;
- par->SavageWaitFifo(par,3);
- BCI_SEND(cmd);
- BCI_SEND(BCI_CLIP_TL(0, 0));
- BCI_SEND(BCI_CLIP_BR(0xfff, 0xfff));
-}
-#endif
-
-static int savagefb_set_par (struct fb_info *info)
+static int savagefb_set_par(struct fb_info *info)
{
struct savagefb_par *par = info->par;
struct fb_var_screeninfo *var = &info->var;
int err;
DBG("savagefb_set_par");
- err = savagefb_decode_var (var, par);
+ err = savagefb_decode_var(var, par, &par->state);
if (err)
return err;
@@ -1366,8 +1521,8 @@ static int savagefb_set_par (struct fb_info *info)
par->maxClock = par->dacSpeedBpp;
par->minClock = 10000;
- savagefb_set_par_int (par);
- fb_set_cmap (&info->cmap, info);
+ savagefb_set_par_int(par, &par->state);
+ fb_set_cmap(&info->cmap, info);
savagefb_set_fix(info);
savagefb_set_clip(info);
@@ -1378,12 +1533,12 @@ static int savagefb_set_par (struct fb_info *info)
/*
* Pan or Wrap the Display
*/
-static int savagefb_pan_display (struct fb_var_screeninfo *var,
- struct fb_info *info)
+static int savagefb_pan_display(struct fb_var_screeninfo *var,
+ struct fb_info *info)
{
struct savagefb_par *par = info->par;
- savagefb_update_start (par, var);
+ savagefb_update_start(par, var);
return 0;
}
@@ -1440,6 +1595,22 @@ static int savagefb_blank(int blank, struct fb_info *info)
return (blank == FB_BLANK_NORMAL) ? 1 : 0;
}
+static void savagefb_save_state(struct fb_info *info)
+{
+ struct savagefb_par *par = info->par;
+
+ savage_get_default_par(par, &par->save);
+}
+
+static void savagefb_restore_state(struct fb_info *info)
+{
+ struct savagefb_par *par = info->par;
+
+ savagefb_blank(FB_BLANK_POWERDOWN, info);
+ savage_set_default_par(par, &par->save);
+ savagefb_blank(FB_BLANK_UNBLANK, info);
+}
+
static struct fb_ops savagefb_ops = {
.owner = THIS_MODULE,
.fb_check_var = savagefb_check_var,
@@ -1447,6 +1618,8 @@ static struct fb_ops savagefb_ops = {
.fb_setcolreg = savagefb_setcolreg,
.fb_pan_display = savagefb_pan_display,
.fb_blank = savagefb_blank,
+ .fb_save_state = savagefb_save_state,
+ .fb_restore_state = savagefb_restore_state,
#if defined(CONFIG_FB_SAVAGE_ACCEL)
.fb_fillrect = savagefb_fillrect,
.fb_copyarea = savagefb_copyarea,
@@ -1479,59 +1652,59 @@ static struct fb_var_screeninfo __devinitdata savagefb_var800x600x8 = {
.vmode = FB_VMODE_NONINTERLACED
};
-static void savage_enable_mmio (struct savagefb_par *par)
+static void savage_enable_mmio(struct savagefb_par *par)
{
unsigned char val;
- DBG ("savage_enable_mmio\n");
+ DBG("savage_enable_mmio\n");
- val = vga_in8 (0x3c3, par);
- vga_out8 (0x3c3, val | 0x01, par);
- val = vga_in8 (0x3cc, par);
- vga_out8 (0x3c2, val | 0x01, par);
+ val = vga_in8(0x3c3, par);
+ vga_out8(0x3c3, val | 0x01, par);
+ val = vga_in8(0x3cc, par);
+ vga_out8(0x3c2, val | 0x01, par);
if (par->chip >= S3_SAVAGE4) {
- vga_out8 (0x3d4, 0x40, par);
- val = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, val | 1, par);
+ vga_out8(0x3d4, 0x40, par);
+ val = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, val | 1, par);
}
}
-static void savage_disable_mmio (struct savagefb_par *par)
+static void savage_disable_mmio(struct savagefb_par *par)
{
unsigned char val;
- DBG ("savage_disable_mmio\n");
+ DBG("savage_disable_mmio\n");
- if(par->chip >= S3_SAVAGE4 ) {
- vga_out8 (0x3d4, 0x40, par);
- val = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, val | 1, par);
+ if (par->chip >= S3_SAVAGE4) {
+ vga_out8(0x3d4, 0x40, par);
+ val = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, val | 1, par);
}
}
-static int __devinit savage_map_mmio (struct fb_info *info)
+static int __devinit savage_map_mmio(struct fb_info *info)
{
struct savagefb_par *par = info->par;
- DBG ("savage_map_mmio");
+ DBG("savage_map_mmio");
- if (S3_SAVAGE3D_SERIES (par->chip))
- par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
+ if (S3_SAVAGE3D_SERIES(par->chip))
+ par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
SAVAGE_NEWMMIO_REGBASE_S3;
else
- par->mmio.pbase = pci_resource_start (par->pcidev, 0) +
+ par->mmio.pbase = pci_resource_start(par->pcidev, 0) +
SAVAGE_NEWMMIO_REGBASE_S4;
par->mmio.len = SAVAGE_NEWMMIO_REGSIZE;
- par->mmio.vbase = ioremap (par->mmio.pbase, par->mmio.len);
+ par->mmio.vbase = ioremap(par->mmio.pbase, par->mmio.len);
if (!par->mmio.vbase) {
- printk ("savagefb: unable to map memory mapped IO\n");
+ printk("savagefb: unable to map memory mapped IO\n");
return -ENOMEM;
} else
- printk (KERN_INFO "savagefb: mapped io at %p\n",
+ printk(KERN_INFO "savagefb: mapped io at %p\n",
par->mmio.vbase);
info->fix.mmio_start = par->mmio.pbase;
@@ -1540,15 +1713,15 @@ static int __devinit savage_map_mmio (struct fb_info *info)
par->bci_base = (u32 __iomem *)(par->mmio.vbase + BCI_BUFFER_OFFSET);
par->bci_ptr = 0;
- savage_enable_mmio (par);
+ savage_enable_mmio(par);
return 0;
}
-static void savage_unmap_mmio (struct fb_info *info)
+static void savage_unmap_mmio(struct fb_info *info)
{
struct savagefb_par *par = info->par;
- DBG ("savage_unmap_mmio");
+ DBG("savage_unmap_mmio");
savage_disable_mmio(par);
@@ -1558,46 +1731,46 @@ static void savage_unmap_mmio (struct fb_info *info)
}
}
-static int __devinit savage_map_video (struct fb_info *info,
- int video_len)
+static int __devinit savage_map_video(struct fb_info *info,
+ int video_len)
{
struct savagefb_par *par = info->par;
int resource;
DBG("savage_map_video");
- if (S3_SAVAGE3D_SERIES (par->chip))
+ if (S3_SAVAGE3D_SERIES(par->chip))
resource = 0;
else
resource = 1;
- par->video.pbase = pci_resource_start (par->pcidev, resource);
+ par->video.pbase = pci_resource_start(par->pcidev, resource);
par->video.len = video_len;
- par->video.vbase = ioremap (par->video.pbase, par->video.len);
+ par->video.vbase = ioremap(par->video.pbase, par->video.len);
if (!par->video.vbase) {
- printk ("savagefb: unable to map screen memory\n");
+ printk("savagefb: unable to map screen memory\n");
return -ENOMEM;
} else
- printk (KERN_INFO "savagefb: mapped framebuffer at %p, "
- "pbase == %x\n", par->video.vbase, par->video.pbase);
+ printk(KERN_INFO "savagefb: mapped framebuffer at %p, "
+ "pbase == %x\n", par->video.vbase, par->video.pbase);
info->fix.smem_start = par->video.pbase;
info->fix.smem_len = par->video.len - par->cob_size;
info->screen_base = par->video.vbase;
#ifdef CONFIG_MTRR
- par->video.mtrr = mtrr_add (par->video.pbase, video_len,
- MTRR_TYPE_WRCOMB, 1);
+ par->video.mtrr = mtrr_add(par->video.pbase, video_len,
+ MTRR_TYPE_WRCOMB, 1);
#endif
/* Clear framebuffer, it's all white in memory after boot */
- memset_io (par->video.vbase, 0, par->video.len);
+ memset_io(par->video.vbase, 0, par->video.len);
return 0;
}
-static void savage_unmap_video (struct fb_info *info)
+static void savage_unmap_video(struct fb_info *info)
{
struct savagefb_par *par = info->par;
@@ -1605,16 +1778,16 @@ static void savage_unmap_video (struct fb_info *info)
if (par->video.vbase) {
#ifdef CONFIG_MTRR
- mtrr_del (par->video.mtrr, par->video.pbase, par->video.len);
+ mtrr_del(par->video.mtrr, par->video.pbase, par->video.len);
#endif
- iounmap (par->video.vbase);
+ iounmap(par->video.vbase);
par->video.vbase = NULL;
info->screen_base = NULL;
}
}
-static int savage_init_hw (struct savagefb_par *par)
+static int savage_init_hw(struct savagefb_par *par)
{
unsigned char config1, m, n, n1, n2, sr8, cr3f, cr66 = 0, tmp;
@@ -1656,7 +1829,7 @@ static int savage_init_hw (struct savagefb_par *par)
switch (par->chip) {
case S3_SAVAGE3D:
- videoRam = RamSavage3D[ (config1 & 0xC0) >> 6 ] * 1024;
+ videoRam = RamSavage3D[(config1 & 0xC0) >> 6 ] * 1024;
break;
case S3_SAVAGE4:
@@ -1667,22 +1840,22 @@ static int savage_init_hw (struct savagefb_par *par)
* can do it different...
*/
vga_out8(0x3d4, 0x68, par); /* memory control 1 */
- if( (vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6) )
+ if ((vga_in8(0x3d5, par) & 0xC0) == (0x01 << 6))
RamSavage4[1] = 8;
/*FALLTHROUGH*/
case S3_SAVAGE2000:
- videoRam = RamSavage4[ (config1 & 0xE0) >> 5 ] * 1024;
+ videoRam = RamSavage4[(config1 & 0xE0) >> 5] * 1024;
break;
case S3_SAVAGE_MX:
case S3_SUPERSAVAGE:
- videoRam = RamSavageMX[ (config1 & 0x0E) >> 1 ] * 1024;
+ videoRam = RamSavageMX[(config1 & 0x0E) >> 1] * 1024;
break;
case S3_PROSAVAGE:
- videoRam = RamSavageNB[ (config1 & 0xE0) >> 5 ] * 1024;
+ videoRam = RamSavageNB[(config1 & 0xE0) >> 5] * 1024;
break;
default:
@@ -1693,31 +1866,31 @@ static int savage_init_hw (struct savagefb_par *par)
videoRambytes = videoRam * 1024;
- printk (KERN_INFO "savagefb: probed videoram: %dk\n", videoRam);
+ printk(KERN_INFO "savagefb: probed videoram: %dk\n", videoRam);
/* reset graphics engine to avoid memory corruption */
- vga_out8 (0x3d4, 0x66, par);
- cr66 = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr66 | 0x02, par);
- udelay (10000);
+ vga_out8(0x3d4, 0x66, par);
+ cr66 = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr66 | 0x02, par);
+ udelay(10000);
- vga_out8 (0x3d4, 0x66, par);
- vga_out8 (0x3d5, cr66 & ~0x02, par); /* clear reset flag */
- udelay (10000);
+ vga_out8(0x3d4, 0x66, par);
+ vga_out8(0x3d5, cr66 & ~0x02, par); /* clear reset flag */
+ udelay(10000);
/*
* reset memory interface, 3D engine, AGP master, PCI master,
* master engine unit, motion compensation/LPB
*/
- vga_out8 (0x3d4, 0x3f, par);
- cr3f = vga_in8 (0x3d5, par);
- vga_out8 (0x3d5, cr3f | 0x08, par);
- udelay (10000);
+ vga_out8(0x3d4, 0x3f, par);
+ cr3f = vga_in8(0x3d5, par);
+ vga_out8(0x3d5, cr3f | 0x08, par);
+ udelay(10000);
- vga_out8 (0x3d4, 0x3f, par);
- vga_out8 (0x3d5, cr3f & ~0x08, par); /* clear reset flags */
- udelay (10000);
+ vga_out8(0x3d4, 0x3f, par);
+ vga_out8(0x3d5, cr3f & ~0x08, par); /* clear reset flags */
+ udelay(10000);
/* Savage ramdac speeds */
par->numClocks = 4;
@@ -1740,7 +1913,7 @@ static int savage_init_hw (struct savagefb_par *par)
n1 = n & 0x1f;
n2 = (n >> 5) & 0x03;
par->MCLK = ((1431818 * (m+2)) / (n1+2) / (1 << n2) + 50) / 100;
- printk (KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
+ printk(KERN_INFO "savagefb: Detected current MCLK value of %d kHz\n",
par->MCLK);
/* check for DVI/flat panel */
@@ -1769,12 +1942,12 @@ static int savage_init_hw (struct savagefb_par *par)
/* Check LCD panel parrmation */
if (par->display_type == DISP_LCD) {
- unsigned char cr6b = VGArCR( 0x6b, par);
+ unsigned char cr6b = VGArCR(0x6b, par);
- int panelX = (VGArSEQ (0x61, par) +
- ((VGArSEQ (0x66, par) & 0x02) << 7) + 1) * 8;
- int panelY = (VGArSEQ (0x69, par) +
- ((VGArSEQ (0x6e, par) & 0x70) << 4) + 1);
+ int panelX = (VGArSEQ(0x61, par) +
+ ((VGArSEQ(0x66, par) & 0x02) << 7) + 1) * 8;
+ int panelY = (VGArSEQ(0x69, par) +
+ ((VGArSEQ(0x6e, par) & 0x70) << 4) + 1);
char * sTechnology = "Unknown";
@@ -1796,26 +1969,26 @@ static int savage_init_hw (struct savagefb_par *par)
ActiveDUO = 0x80
};
- if ((VGArSEQ (0x39, par) & 0x03) == 0) {
+ if ((VGArSEQ(0x39, par) & 0x03) == 0) {
sTechnology = "TFT";
- } else if ((VGArSEQ (0x30, par) & 0x01) == 0) {
+ } else if ((VGArSEQ(0x30, par) & 0x01) == 0) {
sTechnology = "DSTN";
} else {
sTechnology = "STN";
}
- printk (KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
- panelX, panelY, sTechnology,
- cr6b & ActiveLCD ? "and active" : "but not active");
+ printk(KERN_INFO "savagefb: %dx%d %s LCD panel detected %s\n",
+ panelX, panelY, sTechnology,
+ cr6b & ActiveLCD ? "and active" : "but not active");
- if( cr6b & ActiveLCD ) {
+ if (cr6b & ActiveLCD) {
/*
* If the LCD is active and panel expansion is enabled,
* we probably want to kill the HW cursor.
*/
- printk (KERN_INFO "savagefb: Limiting video mode to "
- "%dx%d\n", panelX, panelY );
+ printk(KERN_INFO "savagefb: Limiting video mode to "
+ "%dx%d\n", panelX, panelY);
par->SavagePanelWidth = panelX;
par->SavagePanelHeight = panelY;
@@ -1824,9 +1997,10 @@ static int savage_init_hw (struct savagefb_par *par)
par->display_type = DISP_CRT;
}
- savage_get_default_par (par);
+ savage_get_default_par(par, &par->state);
+ par->save = par->state;
- if( S3_SAVAGE4_SERIES(par->chip) ) {
+ if (S3_SAVAGE4_SERIES(par->chip)) {
/*
* The Savage4 and ProSavage have COB coherency bugs which
* render the buffer useless. We disable it.
@@ -1845,9 +2019,9 @@ static int savage_init_hw (struct savagefb_par *par)
return videoRambytes;
}
-static int __devinit savage_init_fb_info (struct fb_info *info,
- struct pci_dev *dev,
- const struct pci_device_id *id)
+static int __devinit savage_init_fb_info(struct fb_info *info,
+ struct pci_dev *dev,
+ const struct pci_device_id *id)
{
struct savagefb_par *par = info->par;
int err = 0;
@@ -1863,63 +2037,63 @@ static int __devinit savage_init_fb_info (struct fb_info *info,
switch (info->fix.accel) {
case FB_ACCEL_SUPERSAVAGE:
par->chip = S3_SUPERSAVAGE;
- snprintf (info->fix.id, 16, "SuperSavage");
+ snprintf(info->fix.id, 16, "SuperSavage");
break;
case FB_ACCEL_SAVAGE4:
par->chip = S3_SAVAGE4;
- snprintf (info->fix.id, 16, "Savage4");
+ snprintf(info->fix.id, 16, "Savage4");
break;
case FB_ACCEL_SAVAGE3D:
par->chip = S3_SAVAGE3D;
- snprintf (info->fix.id, 16, "Savage3D");
+ snprintf(info->fix.id, 16, "Savage3D");
break;
case FB_ACCEL_SAVAGE3D_MV:
par->chip = S3_SAVAGE3D;
- snprintf (info->fix.id, 16, "Savage3D-MV");
+ snprintf(info->fix.id, 16, "Savage3D-MV");
break;
case FB_ACCEL_SAVAGE2000:
par->chip = S3_SAVAGE2000;
- snprintf (info->fix.id, 16, "Savage2000");
+ snprintf(info->fix.id, 16, "Savage2000");
break;
case FB_ACCEL_SAVAGE_MX_MV:
par->chip = S3_SAVAGE_MX;
- snprintf (info->fix.id, 16, "Savage/MX-MV");
+ snprintf(info->fix.id, 16, "Savage/MX-MV");
break;
case FB_ACCEL_SAVAGE_MX:
par->chip = S3_SAVAGE_MX;
- snprintf (info->fix.id, 16, "Savage/MX");
+ snprintf(info->fix.id, 16, "Savage/MX");
break;
case FB_ACCEL_SAVAGE_IX_MV:
par->chip = S3_SAVAGE_MX;
- snprintf (info->fix.id, 16, "Savage/IX-MV");
+ snprintf(info->fix.id, 16, "Savage/IX-MV");
break;
case FB_ACCEL_SAVAGE_IX:
par->chip = S3_SAVAGE_MX;
- snprintf (info->fix.id, 16, "Savage/IX");
+ snprintf(info->fix.id, 16, "Savage/IX");
break;
case FB_ACCEL_PROSAVAGE_PM:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "ProSavagePM");
+ snprintf(info->fix.id, 16, "ProSavagePM");
break;
case FB_ACCEL_PROSAVAGE_KM:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "ProSavageKM");
+ snprintf(info->fix.id, 16, "ProSavageKM");
break;
case FB_ACCEL_S3TWISTER_P:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "TwisterP");
+ snprintf(info->fix.id, 16, "TwisterP");
break;
case FB_ACCEL_S3TWISTER_K:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "TwisterK");
+ snprintf(info->fix.id, 16, "TwisterK");
break;
case FB_ACCEL_PROSAVAGE_DDR:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "ProSavageDDR");
+ snprintf(info->fix.id, 16, "ProSavageDDR");
break;
case FB_ACCEL_PROSAVAGE_DDRK:
par->chip = S3_PROSAVAGE;
- snprintf (info->fix.id, 16, "ProSavage8");
+ snprintf(info->fix.id, 16, "ProSavage8");
break;
}
@@ -1960,7 +2134,7 @@ static int __devinit savage_init_fb_info (struct fb_info *info,
info->pixmap.buf_align = 4;
info->pixmap.access_align = 32;
- err = fb_alloc_cmap (&info->cmap, NR_PALETTE, 0);
+ err = fb_alloc_cmap(&info->cmap, NR_PALETTE, 0);
if (!err)
info->flags |= FBINFO_HWACCEL_COPYAREA |
FBINFO_HWACCEL_FILLRECT |
@@ -1972,8 +2146,8 @@ static int __devinit savage_init_fb_info (struct fb_info *info,
/* --------------------------------------------------------------------- */
-static int __devinit savagefb_probe (struct pci_dev* dev,
- const struct pci_device_id* id)
+static int __devinit savagefb_probe(struct pci_dev* dev,
+ const struct pci_device_id* id)
{
struct fb_info *info;
struct savagefb_par *par;
@@ -2085,12 +2259,12 @@ static int __devinit savagefb_probe (struct pci_dev* dev,
fb_destroy_modedb(info->monspecs.modedb);
info->monspecs.modedb = NULL;
- err = register_framebuffer (info);
+ err = register_framebuffer(info);
if (err < 0)
goto failed;
- printk (KERN_INFO "fb: S3 %s frame buffer device\n",
- info->fix.id);
+ printk(KERN_INFO "fb: S3 %s frame buffer device\n",
+ info->fix.id);
/*
* Our driver data
@@ -2103,10 +2277,10 @@ static int __devinit savagefb_probe (struct pci_dev* dev,
#ifdef CONFIG_FB_SAVAGE_I2C
savagefb_delete_i2c_busses(info);
#endif
- fb_alloc_cmap (&info->cmap, 0, 0);
+ fb_alloc_cmap(&info->cmap, 0, 0);
savage_unmap_video(info);
failed_video:
- savage_unmap_mmio (info);
+ savage_unmap_mmio(info);
failed_mmio:
kfree(info->pixmap.addr);
failed_init:
@@ -2117,7 +2291,7 @@ static int __devinit savagefb_probe (struct pci_dev* dev,
return err;
}
-static void __devexit savagefb_remove (struct pci_dev *dev)
+static void __devexit savagefb_remove(struct pci_dev *dev)
{
struct fb_info *info = pci_get_drvdata(dev);
@@ -2129,16 +2303,16 @@ static void __devexit savagefb_remove (struct pci_dev *dev)
* we will be leaving hooks that could cause
* oopsen laying around.
*/
- if (unregister_framebuffer (info))
- printk (KERN_WARNING "savagefb: danger danger! "
- "Oopsen imminent!\n");
+ if (unregister_framebuffer(info))
+ printk(KERN_WARNING "savagefb: danger danger! "
+ "Oopsen imminent!\n");
#ifdef CONFIG_FB_SAVAGE_I2C
savagefb_delete_i2c_busses(info);
#endif
- fb_alloc_cmap (&info->cmap, 0, 0);
- savage_unmap_video (info);
- savage_unmap_mmio (info);
+ fb_alloc_cmap(&info->cmap, 0, 0);
+ savage_unmap_video(info);
+ savage_unmap_mmio(info);
kfree(info->pixmap.addr);
pci_release_regions(dev);
framebuffer_release(info);
@@ -2151,7 +2325,7 @@ static void __devexit savagefb_remove (struct pci_dev *dev)
}
}
-static int savagefb_suspend (struct pci_dev* dev, pm_message_t state)
+static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
{
struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par;
@@ -2177,6 +2351,7 @@ static int savagefb_suspend (struct pci_dev* dev, pm_message_t state)
info->fbops->fb_sync(info);
savagefb_blank(FB_BLANK_POWERDOWN, info);
+ savage_set_default_par(par, &par->save);
savage_disable_mmio(par);
pci_save_state(dev);
pci_disable_device(dev);
@@ -2186,7 +2361,7 @@ static int savagefb_suspend (struct pci_dev* dev, pm_message_t state)
return 0;
}
-static int savagefb_resume (struct pci_dev* dev)
+static int savagefb_resume(struct pci_dev* dev)
{
struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par;
@@ -2210,15 +2385,15 @@ static int savagefb_resume (struct pci_dev* dev)
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
- if(pci_enable_device(dev))
+ if (pci_enable_device(dev))
DBG("err");
pci_set_master(dev);
savage_enable_mmio(par);
savage_init_hw(par);
- savagefb_set_par (info);
+ savagefb_set_par(info);
+ fb_set_suspend(info, 0);
savagefb_blank(FB_BLANK_UNBLANK, info);
- fb_set_suspend (info, 0);
release_console_sem();
return 0;
@@ -2311,10 +2486,10 @@ static struct pci_driver savagefb_driver = {
/* **************************** exit-time only **************************** */
-static void __exit savage_done (void)
+static void __exit savage_done(void)
{
DBG("savage_done");
- pci_unregister_driver (&savagefb_driver);
+ pci_unregister_driver(&savagefb_driver);
}
@@ -2345,7 +2520,7 @@ static int __init savagefb_init(void)
return -ENODEV;
savagefb_setup(option);
- return pci_register_driver (&savagefb_driver);
+ return pci_register_driver(&savagefb_driver);
}
diff --git a/drivers/video/sgivwfb.c b/drivers/video/sgivwfb.c
index 2e6df1fcb2b9..c0cc5e3ba7b5 100644
--- a/drivers/video/sgivwfb.c
+++ b/drivers/video/sgivwfb.c
@@ -23,6 +23,8 @@
#include <asm/io.h>
#include <asm/mtrr.h>
+#include <setup_arch.h>
+
#define INCLUDE_TIMING_TABLE_DATA
#define DBE_REG_BASE par->regs
#include <video/sgivw.h>
@@ -42,10 +44,6 @@ struct sgivw_par {
* The default can be overridden if the driver is compiled as a module
*/
-/* set by arch/i386/kernel/setup.c */
-extern unsigned long sgivwfb_mem_phys;
-extern unsigned long sgivwfb_mem_size;
-
static int ypan = 0;
static int ywrap = 0;
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index 8adf5bf91eee..c63c0e721b82 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -275,7 +275,7 @@ sisfb_search_mode(char *name, BOOLEAN quiet)
static void __devinit
sisfb_get_vga_mode_from_kernel(void)
{
-#if (defined(__i386__) || defined(__x86_64__)) && defined(CONFIG_VIDEO_SELECT)
+#ifdef CONFIG_X86
char mymode[32];
int mydepth = screen_info.lfb_depth;
diff --git a/drivers/video/skeletonfb.c b/drivers/video/skeletonfb.c
index 9b707771d757..67f429e93189 100644
--- a/drivers/video/skeletonfb.c
+++ b/drivers/video/skeletonfb.c
@@ -906,11 +906,6 @@ static void __exit xxxfb_exit(void)
}
#endif
-MODULE_LICENSE("GPL");
-module_init(xxxfb_init);
-module_exit(xxxfb_exit);
-
-
/*
* Setup
*/
diff --git a/drivers/video/tgafb.c b/drivers/video/tgafb.c
index 7398bd48ba6c..6c2c78ab9827 100644
--- a/drivers/video/tgafb.c
+++ b/drivers/video/tgafb.c
@@ -26,7 +26,6 @@
#include <linux/selection.h>
#include <asm/io.h>
#include <video/tgafb.h>
-#include <linux/selection.h>
/*
* Local functions.
diff --git a/drivers/video/vesafb.c b/drivers/video/vesafb.c
index b0b9acfdd430..5718924b677f 100644
--- a/drivers/video/vesafb.c
+++ b/drivers/video/vesafb.c
@@ -51,7 +51,7 @@ static int inverse = 0;
static int mtrr = 0; /* disable mtrr */
static int vram_remap __initdata = 0; /* Set amount of memory to be used */
static int vram_total __initdata = 0; /* Set total amount of memory */
-static int pmi_setpal = 0; /* pmi for palette changes ??? */
+static int pmi_setpal = 1; /* pmi for palette changes ??? */
static int ypan = 0; /* 0..nothing, 1..ypan, 2..ywrap */
static unsigned short *pmi_base = NULL;
static void (*pmi_start)(void);
@@ -80,15 +80,30 @@ static int vesafb_pan_display(struct fb_var_screeninfo *var,
return 0;
}
-static void vesa_setpalette(int regno, unsigned red, unsigned green,
+static int vesa_setpalette(int regno, unsigned red, unsigned green,
unsigned blue)
{
int shift = 16 - depth;
+ int err = -EINVAL;
+
+/*
+ * Try VGA registers first...
+ */
+ if (vga_compat) {
+ outb_p(regno, dac_reg);
+ outb_p(red >> shift, dac_val);
+ outb_p(green >> shift, dac_val);
+ outb_p(blue >> shift, dac_val);
+ err = 0;
+ }
#ifdef __i386__
- struct { u_char blue, green, red, pad; } entry;
+/*
+ * Fallback to the PMI....
+ */
+ if (err && pmi_setpal) {
+ struct { u_char blue, green, red, pad; } entry;
- if (pmi_setpal) {
entry.red = red >> shift;
entry.green = green >> shift;
entry.blue = blue >> shift;
@@ -102,26 +117,19 @@ static void vesa_setpalette(int regno, unsigned red, unsigned green,
"d" (regno), /* EDX */
"D" (&entry), /* EDI */
"S" (&pmi_pal)); /* ESI */
- return;
+ err = 0;
}
#endif
-/*
- * without protected mode interface and if VGA compatible,
- * try VGA registers...
- */
- if (vga_compat) {
- outb_p(regno, dac_reg);
- outb_p(red >> shift, dac_val);
- outb_p(green >> shift, dac_val);
- outb_p(blue >> shift, dac_val);
- }
+ return err;
}
static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp,
struct fb_info *info)
{
+ int err = 0;
+
/*
* Set a single color register. The values supplied are
* already rounded down to the hardware's capabilities
@@ -133,7 +141,7 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 1;
if (info->var.bits_per_pixel == 8)
- vesa_setpalette(regno,red,green,blue);
+ err = vesa_setpalette(regno,red,green,blue);
else if (regno < 16) {
switch (info->var.bits_per_pixel) {
case 16:
@@ -164,7 +172,7 @@ static int vesafb_setcolreg(unsigned regno, unsigned red, unsigned green,
}
}
- return 0;
+ return err;
}
static struct fb_ops vesafb_ops = {
@@ -460,9 +468,7 @@ static struct platform_driver vesafb_driver = {
},
};
-static struct platform_device vesafb_device = {
- .name = "vesafb",
-};
+static struct platform_device *vesafb_device;
static int __init vesafb_init(void)
{
@@ -475,10 +481,19 @@ static int __init vesafb_init(void)
ret = platform_driver_register(&vesafb_driver);
if (!ret) {
- ret = platform_device_register(&vesafb_device);
- if (ret)
+ vesafb_device = platform_device_alloc("vesafb", 0);
+
+ if (vesafb_device)
+ ret = platform_device_add(vesafb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(vesafb_device);
platform_driver_unregister(&vesafb_driver);
+ }
}
+
return ret;
}
module_init(vesafb_init);
diff --git a/drivers/video/vfb.c b/drivers/video/vfb.c
index 77eed1fd9943..d073ffb6e1f9 100644
--- a/drivers/video/vfb.c
+++ b/drivers/video/vfb.c
@@ -398,12 +398,6 @@ static int __init vfb_setup(char *options)
* Initialisation
*/
-static void vfb_platform_release(struct device *device)
-{
- // This is called when the reference count goes to zero.
- dev_err(device, "This driver is broken, please bug the authors so they will fix it.\n");
-}
-
static int __init vfb_probe(struct platform_device *dev)
{
struct fb_info *info;
@@ -482,13 +476,7 @@ static struct platform_driver vfb_driver = {
},
};
-static struct platform_device vfb_device = {
- .name = "vfb",
- .id = 0,
- .dev = {
- .release = vfb_platform_release,
- }
-};
+static struct platform_device *vfb_device;
static int __init vfb_init(void)
{
@@ -508,10 +496,19 @@ static int __init vfb_init(void)
ret = platform_driver_register(&vfb_driver);
if (!ret) {
- ret = platform_device_register(&vfb_device);
- if (ret)
+ vfb_device = platform_device_alloc("vfb", 0);
+
+ if (vfb_device)
+ ret = platform_device_add(vfb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(vfb_device);
platform_driver_unregister(&vfb_driver);
+ }
}
+
return ret;
}
@@ -520,7 +517,7 @@ module_init(vfb_init);
#ifdef MODULE
static void __exit vfb_exit(void)
{
- platform_device_unregister(&vfb_device);
+ platform_device_unregister(vfb_device);
platform_driver_unregister(&vfb_driver);
}
diff --git a/drivers/video/vga16fb.c b/drivers/video/vga16fb.c
index 4fd2a272e03d..3c404c9bd36c 100644
--- a/drivers/video/vga16fb.c
+++ b/drivers/video/vga16fb.c
@@ -1334,9 +1334,8 @@ static int vga16fb_setup(char *options)
}
#endif
-static int __init vga16fb_probe(struct device *device)
+static int __init vga16fb_probe(struct platform_device *dev)
{
- struct platform_device *dev = to_platform_device(device);
struct fb_info *info;
struct vga16fb_par *par;
int i;
@@ -1403,7 +1402,7 @@ static int __init vga16fb_probe(struct device *device)
printk(KERN_INFO "fb%d: %s frame buffer device\n",
info->node, info->fix.id);
- dev_set_drvdata(device, info);
+ platform_set_drvdata(dev, info);
return 0;
@@ -1417,9 +1416,9 @@ static int __init vga16fb_probe(struct device *device)
return ret;
}
-static int vga16fb_remove(struct device *device)
+static int vga16fb_remove(struct platform_device *dev)
{
- struct fb_info *info = dev_get_drvdata(device);
+ struct fb_info *info = platform_get_drvdata(dev);
if (info) {
unregister_framebuffer(info);
@@ -1432,16 +1431,15 @@ static int vga16fb_remove(struct device *device)
return 0;
}
-static struct device_driver vga16fb_driver = {
- .name = "vga16fb",
- .bus = &platform_bus_type,
+static struct platform_driver vga16fb_driver = {
.probe = vga16fb_probe,
.remove = vga16fb_remove,
+ .driver = {
+ .name = "vga16fb",
+ },
};
-static struct platform_device vga16fb_device = {
- .name = "vga16fb",
-};
+static struct platform_device *vga16fb_device;
static int __init vga16fb_init(void)
{
@@ -1454,12 +1452,20 @@ static int __init vga16fb_init(void)
vga16fb_setup(option);
#endif
- ret = driver_register(&vga16fb_driver);
+ ret = platform_driver_register(&vga16fb_driver);
if (!ret) {
- ret = platform_device_register(&vga16fb_device);
- if (ret)
- driver_unregister(&vga16fb_driver);
+ vga16fb_device = platform_device_alloc("vga16fb", 0);
+
+ if (vga16fb_device)
+ ret = platform_device_add(vga16fb_device);
+ else
+ ret = -ENOMEM;
+
+ if (ret) {
+ platform_device_put(vga16fb_device);
+ platform_driver_unregister(&vga16fb_driver);
+ }
}
return ret;
@@ -1467,8 +1473,8 @@ static int __init vga16fb_init(void)
static void __exit vga16fb_exit(void)
{
- platform_device_unregister(&vga16fb_device);
- driver_unregister(&vga16fb_driver);
+ platform_device_unregister(vga16fb_device);
+ platform_driver_unregister(&vga16fb_driver);
}
MODULE_LICENSE("GPL");