aboutsummaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/acorn/char/i2c.c1
-rw-r--r--drivers/acpi/acpi_memhotplug.c4
-rw-r--r--drivers/acpi/i2c_ec.c2
-rw-r--r--drivers/acpi/osl.c2
-rw-r--r--drivers/acpi/processor_idle.c38
-rw-r--r--drivers/ata/Kconfig3
-rw-r--r--drivers/ata/ata_generic.c2
-rw-r--r--drivers/ata/ata_piix.c10
-rw-r--r--drivers/ata/libata-core.c5
-rw-r--r--drivers/ata/libata-eh.c6
-rw-r--r--drivers/ata/libata-sff.c6
-rw-r--r--drivers/ata/pata_ali.c8
-rw-r--r--drivers/ata/pata_amd.c37
-rw-r--r--drivers/ata/pata_artop.c22
-rw-r--r--drivers/ata/pata_atiixp.c12
-rw-r--r--drivers/ata/pata_cmd64x.c6
-rw-r--r--drivers/ata/pata_cs5520.c2
-rw-r--r--drivers/ata/pata_cs5530.c2
-rw-r--r--drivers/ata/pata_cs5535.c2
-rw-r--r--drivers/ata/pata_cypress.c2
-rw-r--r--drivers/ata/pata_efar.c12
-rw-r--r--drivers/ata/pata_hpt366.c4
-rw-r--r--drivers/ata/pata_hpt37x.c8
-rw-r--r--drivers/ata/pata_hpt3x2n.c2
-rw-r--r--drivers/ata/pata_hpt3x3.c2
-rw-r--r--drivers/ata/pata_isapnp.c2
-rw-r--r--drivers/ata/pata_it821x.c4
-rw-r--r--drivers/ata/pata_jmicron.c5
-rw-r--r--drivers/ata/pata_legacy.c14
-rw-r--r--drivers/ata/pata_mpiix.c11
-rw-r--r--drivers/ata/pata_netcell.c3
-rw-r--r--drivers/ata/pata_ns87410.c9
-rw-r--r--drivers/ata/pata_oldpiix.c9
-rw-r--r--drivers/ata/pata_opti.c12
-rw-r--r--drivers/ata/pata_optidma.c14
-rw-r--r--drivers/ata/pata_pcmcia.c2
-rw-r--r--drivers/ata/pata_pdc2027x.c8
-rw-r--r--drivers/ata/pata_qdi.c4
-rw-r--r--drivers/ata/pata_radisys.c2
-rw-r--r--drivers/ata/pata_rz1000.c2
-rw-r--r--drivers/ata/pata_sc1200.c2
-rw-r--r--drivers/ata/pata_serverworks.c12
-rw-r--r--drivers/ata/pata_sil680.c2
-rw-r--r--drivers/ata/pata_sis.c20
-rw-r--r--drivers/ata/pata_sl82c105.c11
-rw-r--r--drivers/ata/pata_triflex.c15
-rw-r--r--drivers/ata/pata_via.c13
-rw-r--r--drivers/ata/sata_mv.c1
-rw-r--r--drivers/ata/sata_nv.c6
-rw-r--r--drivers/ata/sata_sis.c6
-rw-r--r--drivers/ata/sata_uli.c6
-rw-r--r--drivers/ata/sata_via.c7
-rw-r--r--drivers/base/Makefile2
-rw-r--r--drivers/base/base.h2
-rw-r--r--drivers/base/bus.c133
-rw-r--r--drivers/base/class.c42
-rw-r--r--drivers/base/core.c230
-rw-r--r--drivers/base/dd.c147
-rw-r--r--drivers/base/driver.c16
-rw-r--r--drivers/base/firmware_class.c14
-rw-r--r--drivers/base/node.c13
-rw-r--r--drivers/base/platform.c30
-rw-r--r--drivers/base/power/resume.c37
-rw-r--r--drivers/base/power/suspend.c92
-rw-r--r--drivers/base/power/sysfs.c35
-rw-r--r--drivers/block/DAC960.c4
-rw-r--r--drivers/block/DAC960.h2
-rw-r--r--drivers/block/Kconfig4
-rw-r--r--drivers/block/cciss.c238
-rw-r--r--drivers/block/cciss.h3
-rw-r--r--drivers/block/cciss_cmd.h33
-rw-r--r--drivers/block/cciss_scsi.c2
-rw-r--r--drivers/block/cpqarray.c1
-rw-r--r--drivers/block/floppy.c4
-rw-r--r--drivers/block/loop.c247
-rw-r--r--drivers/block/nbd.c8
-rw-r--r--drivers/block/paride/pd.c8
-rw-r--r--drivers/block/pktcdvd.c10
-rw-r--r--drivers/block/swim3.c4
-rw-r--r--drivers/block/swim_iop.c4
-rw-r--r--drivers/block/ub.c38
-rw-r--r--drivers/block/umem.c3
-rw-r--r--drivers/block/xd.c2
-rw-r--r--drivers/bluetooth/bfusb.c316
-rw-r--r--drivers/bluetooth/hci_ldisc.c13
-rw-r--r--drivers/bluetooth/hci_usb.c3
-rw-r--r--drivers/bluetooth/hci_vhci.c99
-rw-r--r--drivers/cdrom/Kconfig2
-rw-r--r--drivers/cdrom/cdrom.c2
-rw-r--r--drivers/cdrom/cdu31a.c4
-rw-r--r--drivers/char/Kconfig19
-rw-r--r--drivers/char/Makefile2
-rw-r--r--drivers/char/agp/amd64-agp.c8
-rw-r--r--drivers/char/agp/generic.c9
-rw-r--r--drivers/char/amiserial.c2
-rw-r--r--drivers/char/cyclades.c2
-rw-r--r--drivers/char/drm/Kconfig9
-rw-r--r--drivers/char/drm/Makefile6
-rw-r--r--drivers/char/drm/drmP.h68
-rw-r--r--drivers/char/drm/drm_auth.c64
-rw-r--r--drivers/char/drm/drm_bufs.c74
-rw-r--r--drivers/char/drm/drm_drv.c12
-rw-r--r--drivers/char/drm/drm_fops.c10
-rw-r--r--drivers/char/drm/drm_hashtab.c190
-rw-r--r--drivers/char/drm/drm_hashtab.h67
-rw-r--r--drivers/char/drm/drm_ioc32.c2
-rw-r--r--drivers/char/drm/drm_ioctl.c34
-rw-r--r--drivers/char/drm/drm_irq.c12
-rw-r--r--drivers/char/drm/drm_mm.c201
-rw-r--r--drivers/char/drm/drm_pciids.h187
-rw-r--r--drivers/char/drm/drm_proc.c2
-rw-r--r--drivers/char/drm/drm_sman.c352
-rw-r--r--drivers/char/drm/drm_sman.h176
-rw-r--r--drivers/char/drm/drm_stub.c12
-rw-r--r--drivers/char/drm/drm_vm.c45
-rw-r--r--drivers/char/drm/i810_dma.c10
-rw-r--r--drivers/char/drm/i830_dma.c4
-rw-r--r--drivers/char/drm/i915_dma.c45
-rw-r--r--drivers/char/drm/i915_drm.h6
-rw-r--r--drivers/char/drm/i915_drv.h10
-rw-r--r--drivers/char/drm/i915_irq.c16
-rw-r--r--drivers/char/drm/radeon_cp.c72
-rw-r--r--drivers/char/drm/radeon_drv.c2
-rw-r--r--drivers/char/drm/radeon_drv.h36
-rw-r--r--drivers/char/drm/radeon_state.c48
-rw-r--r--drivers/char/drm/sis_drv.c39
-rw-r--r--drivers/char/drm/sis_drv.h34
-rw-r--r--drivers/char/drm/sis_ds.c299
-rw-r--r--drivers/char/drm/sis_ds.h146
-rw-r--r--drivers/char/drm/sis_mm.c504
-rw-r--r--drivers/char/drm/via_dmablit.c68
-rw-r--r--drivers/char/drm/via_drm.h8
-rw-r--r--drivers/char/drm/via_drv.c3
-rw-r--r--drivers/char/drm/via_drv.h16
-rw-r--r--drivers/char/drm/via_ds.c273
-rw-r--r--drivers/char/drm/via_ds.h104
-rw-r--r--drivers/char/drm/via_map.c9
-rw-r--r--drivers/char/drm/via_mm.c375
-rw-r--r--drivers/char/ds1286.c15
-rw-r--r--drivers/char/epca.c2
-rw-r--r--drivers/char/esp.c2
-rw-r--r--drivers/char/generic_serial.c11
-rw-r--r--drivers/char/hpet.c4
-rw-r--r--drivers/char/hvc_console.c2
-rw-r--r--drivers/char/hvcs.c2
-rw-r--r--drivers/char/hvsi.c2
-rw-r--r--drivers/char/hw_random/intel-rng.c186
-rw-r--r--drivers/char/ip2/ip2main.c14
-rw-r--r--drivers/char/ipmi/ipmi_devintf.c34
-rw-r--r--drivers/char/ipmi/ipmi_msghandler.c75
-rw-r--r--drivers/char/ipmi/ipmi_si_intf.c26
-rw-r--r--drivers/char/isicom.c37
-rw-r--r--drivers/char/istallion.c22
-rw-r--r--drivers/char/keyboard.c25
-rw-r--r--drivers/char/lp.c4
-rw-r--r--drivers/char/mbcs.c7
-rw-r--r--drivers/char/mem.c47
-rw-r--r--drivers/char/moxa.c82
-rw-r--r--drivers/char/mspec.c421
-rw-r--r--drivers/char/mwave/mwavedd.c4
-rw-r--r--drivers/char/mxser.c68
-rw-r--r--drivers/char/nwbutton.c5
-rw-r--r--drivers/char/pc8736x_gpio.c10
-rw-r--r--drivers/char/pcmcia/synclink_cs.c2
-rw-r--r--drivers/char/pty.c2
-rw-r--r--drivers/char/random.c8
-rw-r--r--drivers/char/raw.c50
-rw-r--r--drivers/char/rio/rio_linux.c2
-rw-r--r--drivers/char/riscom8.c24
-rw-r--r--drivers/char/rocket.c2
-rw-r--r--drivers/char/rtc.c4
-rw-r--r--drivers/char/s3c2410-rtc.c591
-rw-r--r--drivers/char/scx200_gpio.c4
-rw-r--r--drivers/char/selection.c2
-rw-r--r--drivers/char/ser_a2232.c2
-rw-r--r--drivers/char/serial167.c2
-rw-r--r--drivers/char/snsc_event.c2
-rw-r--r--drivers/char/specialix.c29
-rw-r--r--drivers/char/stallion.c2
-rw-r--r--drivers/char/sx.c2
-rw-r--r--drivers/char/synclink.c2
-rw-r--r--drivers/char/synclink_gt.c96
-rw-r--r--drivers/char/synclinkmp.c2
-rw-r--r--drivers/char/sysrq.c3
-rw-r--r--drivers/char/tipar.c6
-rw-r--r--drivers/char/tty_io.c107
-rw-r--r--drivers/char/tty_ioctl.c35
-rw-r--r--drivers/char/vc_screen.c5
-rw-r--r--drivers/char/viocons.c2
-rw-r--r--drivers/char/vme_scc.c2
-rw-r--r--drivers/char/vt.c146
-rw-r--r--drivers/char/vt_ioctl.c34
-rw-r--r--drivers/char/watchdog/Kconfig26
-rw-r--r--drivers/char/watchdog/Makefile2
-rw-r--r--drivers/char/watchdog/acquirewdt.c2
-rw-r--r--drivers/char/watchdog/advantechwdt.c2
-rw-r--r--drivers/char/watchdog/alim1535_wdt.c12
-rw-r--r--drivers/char/watchdog/alim7101_wdt.c17
-rw-r--r--drivers/char/watchdog/at91_wdt.c2
-rw-r--r--drivers/char/watchdog/booke_wdt.c2
-rw-r--r--drivers/char/watchdog/cpu5wdt.c2
-rw-r--r--drivers/char/watchdog/ep93xx_wdt.c2
-rw-r--r--drivers/char/watchdog/eurotechwdt.c2
-rw-r--r--drivers/char/watchdog/i6300esb.c2
-rw-r--r--drivers/char/watchdog/i8xx_tco.c35
-rw-r--r--drivers/char/watchdog/ib700wdt.c2
-rw-r--r--drivers/char/watchdog/ibmasr.c2
-rw-r--r--drivers/char/watchdog/indydog.c2
-rw-r--r--drivers/char/watchdog/ixp2000_wdt.c2
-rw-r--r--drivers/char/watchdog/ixp4xx_wdt.c2
-rw-r--r--drivers/char/watchdog/machzwd.c5
-rw-r--r--drivers/char/watchdog/mixcomwd.c2
-rw-r--r--drivers/char/watchdog/mpc83xx_wdt.c2
-rw-r--r--drivers/char/watchdog/mpc8xx_wdt.c2
-rw-r--r--drivers/char/watchdog/mpcore_wdt.c4
-rw-r--r--drivers/char/watchdog/mv64x60_wdt.c2
-rw-r--r--drivers/char/watchdog/omap_wdt.c391
-rw-r--r--drivers/char/watchdog/omap_wdt.h64
-rw-r--r--drivers/char/watchdog/pcwd.c2
-rw-r--r--drivers/char/watchdog/pcwd_pci.c2
-rw-r--r--drivers/char/watchdog/pcwd_usb.c2
-rw-r--r--drivers/char/watchdog/pnx4008_wdt.c362
-rw-r--r--drivers/char/watchdog/s3c2410_wdt.c12
-rw-r--r--drivers/char/watchdog/sa1100_wdt.c2
-rw-r--r--drivers/char/watchdog/sbc60xxwdt.c2
-rw-r--r--drivers/char/watchdog/sbc_epx_c3.c2
-rw-r--r--drivers/char/watchdog/sc1200wdt.c2
-rw-r--r--drivers/char/watchdog/sc520_wdt.c2
-rw-r--r--drivers/char/watchdog/scx200_wdt.c2
-rw-r--r--drivers/char/watchdog/shwdt.c112
-rw-r--r--drivers/char/watchdog/softdog.c2
-rw-r--r--drivers/char/watchdog/w83627hf_wdt.c2
-rw-r--r--drivers/char/watchdog/w83877f_wdt.c2
-rw-r--r--drivers/char/watchdog/w83977f_wdt.c2
-rw-r--r--drivers/char/watchdog/wafer5823wdt.c2
-rw-r--r--drivers/char/watchdog/wdrtas.c2
-rw-r--r--drivers/char/watchdog/wdt.c2
-rw-r--r--drivers/char/watchdog/wdt285.c2
-rw-r--r--drivers/char/watchdog/wdt977.c2
-rw-r--r--drivers/char/watchdog/wdt_pci.c2
-rw-r--r--drivers/cpufreq/cpufreq.c2
-rw-r--r--drivers/eisa/eisa-bus.c23
-rw-r--r--drivers/fc4/fc.c1
-rw-r--r--drivers/firmware/dell_rbu.c4
-rw-r--r--drivers/firmware/dmi_scan.c23
-rw-r--r--drivers/hwmon/Kconfig51
-rw-r--r--drivers/hwmon/Makefile2
-rw-r--r--drivers/hwmon/abituguru.c30
-rw-r--r--drivers/hwmon/adm1021.c31
-rw-r--r--drivers/hwmon/adm1025.c94
-rw-r--r--drivers/hwmon/adm1026.c286
-rw-r--r--drivers/hwmon/adm1031.c114
-rw-r--r--drivers/hwmon/adm9240.c105
-rw-r--r--drivers/hwmon/asb100.c122
-rw-r--r--drivers/hwmon/atxp1.c28
-rw-r--r--drivers/hwmon/ds1621.c28
-rw-r--r--drivers/hwmon/f71805f.c336
-rw-r--r--drivers/hwmon/fscher.c106
-rw-r--r--drivers/hwmon/fscpos.c75
-rw-r--r--drivers/hwmon/gl518sm.c74
-rw-r--r--drivers/hwmon/gl520sm.c128
-rw-r--r--drivers/hwmon/hdaps.c7
-rw-r--r--drivers/hwmon/it87.c470
-rw-r--r--drivers/hwmon/k8temp.c294
-rw-r--r--drivers/hwmon/lm63.c96
-rw-r--r--drivers/hwmon/lm75.c24
-rw-r--r--drivers/hwmon/lm77.c33
-rw-r--r--drivers/hwmon/lm78.c89
-rw-r--r--drivers/hwmon/lm80.c85
-rw-r--r--drivers/hwmon/lm83.c128
-rw-r--r--drivers/hwmon/lm85.c173
-rw-r--r--drivers/hwmon/lm87.c191
-rw-r--r--drivers/hwmon/lm90.c90
-rw-r--r--drivers/hwmon/lm92.c34
-rw-r--r--drivers/hwmon/max1619.c33
-rw-r--r--drivers/hwmon/pc87360.c231
-rw-r--r--drivers/hwmon/sis5595.c102
-rw-r--r--drivers/hwmon/smsc47b397.c40
-rw-r--r--drivers/hwmon/smsc47m1.c82
-rw-r--r--drivers/hwmon/smsc47m192.c150
-rw-r--r--drivers/hwmon/via686a.c84
-rw-r--r--drivers/hwmon/vt1211.c1355
-rw-r--r--drivers/hwmon/vt8231.c187
-rw-r--r--drivers/hwmon/w83627ehf.c486
-rw-r--r--drivers/hwmon/w83627hf.c233
-rw-r--r--drivers/hwmon/w83781d.c278
-rw-r--r--drivers/hwmon/w83791d.c7
-rw-r--r--drivers/hwmon/w83792d.c554
-rw-r--r--drivers/hwmon/w83l785ts.c28
-rw-r--r--drivers/i2c/Kconfig2
-rw-r--r--drivers/i2c/algos/Kconfig6
-rw-r--r--drivers/i2c/algos/Makefile1
-rw-r--r--drivers/i2c/algos/i2c-algo-bit.c23
-rw-r--r--drivers/i2c/algos/i2c-algo-pca.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-pcf.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-sgi.c2
-rw-r--r--drivers/i2c/algos/i2c-algo-sibyte.c215
-rw-r--r--drivers/i2c/busses/Kconfig36
-rw-r--r--drivers/i2c/busses/Makefile1
-rw-r--r--drivers/i2c/busses/i2c-ali1535.c2
-rw-r--r--drivers/i2c/busses/i2c-ali1563.c2
-rw-r--r--drivers/i2c/busses/i2c-ali15x3.c2
-rw-r--r--drivers/i2c/busses/i2c-amd756.c2
-rw-r--r--drivers/i2c/busses/i2c-amd8111.c2
-rw-r--r--drivers/i2c/busses/i2c-au1550.c21
-rw-r--r--drivers/i2c/busses/i2c-elektor.c1
-rw-r--r--drivers/i2c/busses/i2c-hydra.c1
-rw-r--r--drivers/i2c/busses/i2c-i801.c2
-rw-r--r--drivers/i2c/busses/i2c-i810.c2
-rw-r--r--drivers/i2c/busses/i2c-ibm_iic.c2
-rw-r--r--drivers/i2c/busses/i2c-iop3xx.c18
-rw-r--r--drivers/i2c/busses/i2c-isa.c42
-rw-r--r--drivers/i2c/busses/i2c-ite.c2
-rw-r--r--drivers/i2c/busses/i2c-ixp2000.c1
-rw-r--r--drivers/i2c/busses/i2c-ixp4xx.c1
-rw-r--r--drivers/i2c/busses/i2c-mpc.c2
-rw-r--r--drivers/i2c/busses/i2c-mv64xxx.c2
-rw-r--r--drivers/i2c/busses/i2c-nforce2.c2
-rw-r--r--drivers/i2c/busses/i2c-ocores.c2
-rw-r--r--drivers/i2c/busses/i2c-omap.c676
-rw-r--r--drivers/i2c/busses/i2c-parport-light.c1
-rw-r--r--drivers/i2c/busses/i2c-parport.c1
-rw-r--r--drivers/i2c/busses/i2c-piix4.c2
-rw-r--r--drivers/i2c/busses/i2c-powermac.c2
-rw-r--r--drivers/i2c/busses/i2c-prosavage.c1
-rw-r--r--drivers/i2c/busses/i2c-pxa.c2
-rw-r--r--drivers/i2c/busses/i2c-s3c2410.c2
-rw-r--r--drivers/i2c/busses/i2c-savage4.c1
-rw-r--r--drivers/i2c/busses/i2c-sibyte.c160
-rw-r--r--drivers/i2c/busses/i2c-sis5595.c2
-rw-r--r--drivers/i2c/busses/i2c-sis630.c2
-rw-r--r--drivers/i2c/busses/i2c-sis96x.c2
-rw-r--r--drivers/i2c/busses/i2c-stub.c21
-rw-r--r--drivers/i2c/busses/i2c-via.c1
-rw-r--r--drivers/i2c/busses/i2c-viapro.c10
-rw-r--r--drivers/i2c/busses/i2c-voodoo3.c2
-rw-r--r--drivers/i2c/busses/scx200_acb.c2
-rw-r--r--drivers/i2c/busses/scx200_i2c.c12
-rw-r--r--drivers/i2c/chips/eeprom.c8
-rw-r--r--drivers/i2c/chips/isp1301_omap.c2
-rw-r--r--drivers/i2c/chips/max6875.c25
-rw-r--r--drivers/i2c/chips/pca9539.c11
-rw-r--r--drivers/i2c/chips/pcf8574.c22
-rw-r--r--drivers/i2c/chips/pcf8591.c58
-rw-r--r--drivers/i2c/chips/tps65010.c2
-rw-r--r--drivers/i2c/i2c-core.c87
-rw-r--r--drivers/i2c/i2c-dev.c109
-rw-r--r--drivers/ide/Kconfig12
-rw-r--r--drivers/ide/ide-cd.c69
-rw-r--r--drivers/ide/ide-disk.c5
-rw-r--r--drivers/ide/ide-dma.c64
-rw-r--r--drivers/ide/ide-floppy.c17
-rw-r--r--drivers/ide/ide-io.c82
-rw-r--r--drivers/ide/ide-iops.c4
-rw-r--r--drivers/ide/ide-lib.c130
-rw-r--r--drivers/ide/ide-probe.c25
-rw-r--r--drivers/ide/ide-proc.c22
-rw-r--r--drivers/ide/ide-tape.c16
-rw-r--r--drivers/ide/ide-taskfile.c8
-rw-r--r--drivers/ide/ide.c41
-rw-r--r--drivers/ide/legacy/hd.c2
-rw-r--r--drivers/ide/legacy/ide-cs.c7
-rw-r--r--drivers/ide/mips/au1xxx-ide.c4
-rw-r--r--drivers/ide/pci/Makefile1
-rw-r--r--drivers/ide/pci/atiixp.c21
-rw-r--r--drivers/ide/pci/cs5530.c13
-rw-r--r--drivers/ide/pci/cy82c693.c5
-rw-r--r--drivers/ide/pci/generic.c11
-rw-r--r--drivers/ide/pci/jmicron.c269
-rw-r--r--drivers/ide/pci/pdc202xx_old.c22
-rw-r--r--drivers/ide/pci/piix.c33
-rw-r--r--drivers/ide/pci/sc1200.c4
-rw-r--r--drivers/ide/pci/serverworks.c8
-rw-r--r--drivers/ide/pci/sgiioc4.c20
-rw-r--r--drivers/ide/pci/siimage.c1
-rw-r--r--drivers/ide/pci/sis5513.c3
-rw-r--r--drivers/ide/pci/via82cxxx.c5
-rw-r--r--drivers/ide/ppc/pmac.c14
-rw-r--r--drivers/ide/setup-pci.c20
-rw-r--r--drivers/ieee1394/Kconfig11
-rw-r--r--drivers/ieee1394/csr.c31
-rw-r--r--drivers/ieee1394/csr.h109
-rw-r--r--drivers/ieee1394/dma.c7
-rw-r--r--drivers/ieee1394/dma.h90
-rw-r--r--drivers/ieee1394/dv1394-private.h6
-rw-r--r--drivers/ieee1394/dv1394.c47
-rw-r--r--drivers/ieee1394/eth1394.c12
-rw-r--r--drivers/ieee1394/highlevel.h201
-rw-r--r--drivers/ieee1394/hosts.c23
-rw-r--r--drivers/ieee1394/hosts.h51
-rw-r--r--drivers/ieee1394/ieee1394-ioctl.h9
-rw-r--r--drivers/ieee1394/ieee1394.h316
-rw-r--r--drivers/ieee1394/ieee1394_core.c9
-rw-r--r--drivers/ieee1394/ieee1394_core.h28
-rw-r--r--drivers/ieee1394/ieee1394_hotplug.h30
-rw-r--r--drivers/ieee1394/ieee1394_transactions.c114
-rw-r--r--drivers/ieee1394/ieee1394_transactions.h41
-rw-r--r--drivers/ieee1394/ieee1394_types.h68
-rw-r--r--drivers/ieee1394/iso.c5
-rw-r--r--drivers/ieee1394/iso.h87
-rw-r--r--drivers/ieee1394/nodemgr.c227
-rw-r--r--drivers/ieee1394/nodemgr.h27
-rw-r--r--drivers/ieee1394/ohci1394.c73
-rw-r--r--drivers/ieee1394/pcilynx.c1
-rw-r--r--drivers/ieee1394/raw1394-private.h3
-rw-r--r--drivers/ieee1394/raw1394.c138
-rw-r--r--drivers/ieee1394/sbp2.c481
-rw-r--r--drivers/ieee1394/sbp2.h37
-rw-r--r--drivers/ieee1394/video1394.c52
-rw-r--r--drivers/infiniband/core/addr.c4
-rw-r--r--drivers/infiniband/core/cma.c47
-rw-r--r--drivers/infiniband/core/mad.c5
-rw-r--r--drivers/infiniband/hw/amso1100/c2_ae.c2
-rw-r--r--drivers/infiniband/hw/amso1100/c2_alloc.c2
-rw-r--r--drivers/infiniband/hw/amso1100/c2_cm.c15
-rw-r--r--drivers/infiniband/hw/amso1100/c2_provider.c8
-rw-r--r--drivers/infiniband/hw/amso1100/c2_rnic.c4
-rw-r--r--drivers/infiniband/hw/ehca/ehca_main.c36
-rw-r--r--drivers/infiniband/hw/ehca/ehca_tools.h2
-rw-r--r--drivers/infiniband/hw/ipath/ipath_common.h54
-rw-r--r--drivers/infiniband/hw/ipath/ipath_cq.c48
-rw-r--r--drivers/infiniband/hw/ipath/ipath_driver.c359
-rw-r--r--drivers/infiniband/hw/ipath/ipath_eeprom.c17
-rw-r--r--drivers/infiniband/hw/ipath/ipath_file_ops.c974
-rw-r--r--drivers/infiniband/hw/ipath/ipath_fs.c26
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6110.c137
-rw-r--r--drivers/infiniband/hw/ipath/ipath_iba6120.c263
-rw-r--r--drivers/infiniband/hw/ipath/ipath_init_chip.c56
-rw-r--r--drivers/infiniband/hw/ipath/ipath_intr.c280
-rw-r--r--drivers/infiniband/hw/ipath/ipath_kernel.h116
-rw-r--r--drivers/infiniband/hw/ipath/ipath_keys.c12
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mad.c16
-rw-r--r--drivers/infiniband/hw/ipath/ipath_mr.c3
-rw-r--r--drivers/infiniband/hw/ipath/ipath_qp.c16
-rw-r--r--drivers/infiniband/hw/ipath/ipath_rc.c136
-rw-r--r--drivers/infiniband/hw/ipath/ipath_registers.h40
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ruc.c14
-rw-r--r--drivers/infiniband/hw/ipath/ipath_srq.c23
-rw-r--r--drivers/infiniband/hw/ipath/ipath_sysfs.c21
-rw-r--r--drivers/infiniband/hw/ipath/ipath_uc.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_ud.c6
-rw-r--r--drivers/infiniband/hw/ipath/ipath_user_pages.c56
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.c45
-rw-r--r--drivers/infiniband/hw/ipath/ipath_verbs.h18
-rw-r--r--drivers/infiniband/hw/ipath/ipath_wc_ppc64.c20
-rw-r--r--drivers/infiniband/hw/ipath/ipath_wc_x86_64.c13
-rw-r--r--drivers/infiniband/ulp/ipoib/ipoib_fs.c4
-rw-r--r--drivers/infiniband/ulp/iser/Kconfig13
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.c2
-rw-r--r--drivers/infiniband/ulp/iser/iscsi_iser.h9
-rw-r--r--drivers/infiniband/ulp/iser/iser_initiator.c60
-rw-r--r--drivers/infiniband/ulp/iser/iser_memory.c42
-rw-r--r--drivers/infiniband/ulp/iser/iser_verbs.c8
-rw-r--r--drivers/input/Kconfig14
-rw-r--r--drivers/input/Makefile6
-rw-r--r--drivers/input/evbug.c11
-rw-r--r--drivers/input/evdev.c42
-rw-r--r--drivers/input/ff-core.c367
-rw-r--r--drivers/input/ff-memless.c515
-rw-r--r--drivers/input/fixp-arith.h (renamed from drivers/usb/input/fixp-arith.h)0
-rw-r--r--drivers/input/input.c46
-rw-r--r--drivers/input/joydev.c12
-rw-r--r--drivers/input/joystick/iforce/iforce-ff.c118
-rw-r--r--drivers/input/joystick/iforce/iforce-main.c226
-rw-r--r--drivers/input/joystick/iforce/iforce-packets.c15
-rw-r--r--drivers/input/joystick/iforce/iforce.h26
-rw-r--r--drivers/input/keyboard/Kconfig20
-rw-r--r--drivers/input/keyboard/Makefile2
-rw-r--r--drivers/input/keyboard/atkbd.c4
-rw-r--r--drivers/input/keyboard/omap-keypad.c492
-rw-r--r--drivers/input/keyboard/stowaway.c187
-rw-r--r--drivers/input/misc/uinput.c67
-rw-r--r--drivers/input/misc/wistron_btns.c18
-rw-r--r--drivers/input/mouse/alps.c8
-rw-r--r--drivers/input/mouse/alps.h2
-rw-r--r--drivers/input/mouse/lifebook.c8
-rw-r--r--drivers/input/mouse/logips2pp.c23
-rw-r--r--drivers/input/mouse/psmouse-base.c42
-rw-r--r--drivers/input/mouse/sermouse.c2
-rw-r--r--drivers/input/mouse/synaptics.c10
-rw-r--r--drivers/input/mousedev.c28
-rw-r--r--drivers/input/power.c7
-rw-r--r--drivers/input/serio/i8042-io.h15
-rw-r--r--drivers/input/serio/i8042-x86ia64io.h14
-rw-r--r--drivers/input/serio/i8042.c741
-rw-r--r--drivers/input/serio/i8042.h9
-rw-r--r--drivers/input/serio/libps2.c36
-rw-r--r--drivers/input/touchscreen/Kconfig36
-rw-r--r--drivers/input/touchscreen/Makefile3
-rw-r--r--drivers/input/touchscreen/elo.c167
-rw-r--r--drivers/input/touchscreen/hp680_ts_input.c14
-rw-r--r--drivers/input/touchscreen/penmount.c185
-rw-r--r--drivers/input/touchscreen/touchright.c196
-rw-r--r--drivers/input/touchscreen/touchwin.c203
-rw-r--r--drivers/input/tsdev.c12
-rw-r--r--drivers/isdn/capi/capi.c2
-rw-r--r--drivers/isdn/capi/capifs.c2
-rw-r--r--drivers/isdn/gigaset/bas-gigaset.c2
-rw-r--r--drivers/isdn/gigaset/interface.c2
-rw-r--r--drivers/isdn/gigaset/proc.c3
-rw-r--r--drivers/isdn/hardware/eicon/dsp_defs.h3
-rw-r--r--drivers/isdn/hisax/config.c6
-rw-r--r--drivers/isdn/hisax/hfc4s8s_l1.c7
-rw-r--r--drivers/isdn/hisax/hfc_sx.c4
-rw-r--r--drivers/isdn/hisax/hfc_usb.c4
-rw-r--r--drivers/isdn/hisax/hfc_usb.h6
-rw-r--r--drivers/isdn/hisax/hisax.h13
-rw-r--r--drivers/isdn/hisax/hisax_fcpcipnp.c17
-rw-r--r--drivers/isdn/hisax/st5481_b.c8
-rw-r--r--drivers/isdn/hisax/st5481_d.c4
-rw-r--r--drivers/isdn/i4l/isdn_net.c4
-rw-r--r--drivers/isdn/i4l/isdn_tty.c2
-rw-r--r--drivers/isdn/sc/command.c22
-rw-r--r--drivers/isdn/sc/event.c2
-rw-r--r--drivers/isdn/sc/interrupt.c2
-rw-r--r--drivers/isdn/sc/timer.c2
-rw-r--r--drivers/leds/led-triggers.c1
-rw-r--r--drivers/leds/leds-net48xx.c9
-rw-r--r--drivers/macintosh/smu.c4
-rw-r--r--drivers/macintosh/via-pmu-backlight.c2
-rw-r--r--drivers/macintosh/via-pmu.c14
-rw-r--r--drivers/macintosh/via-pmu68k.c6
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c2
-rw-r--r--drivers/macintosh/windfarm_smu_sat.c7
-rw-r--r--drivers/macintosh/windfarm_smu_sensors.c2
-rw-r--r--drivers/md/Kconfig21
-rw-r--r--drivers/md/bitmap.c17
-rw-r--r--drivers/md/dm-crypt.c506
-rw-r--r--drivers/md/dm-emc.c3
-rw-r--r--drivers/md/dm-exception-store.c176
-rw-r--r--drivers/md/dm-linear.c19
-rw-r--r--drivers/md/dm-mpath.c83
-rw-r--r--drivers/md/dm-raid1.c4
-rw-r--r--drivers/md/dm-snap.c351
-rw-r--r--drivers/md/dm-snap.h17
-rw-r--r--drivers/md/dm-table.c109
-rw-r--r--drivers/md/dm.c113
-rw-r--r--drivers/md/dm.h7
-rw-r--r--drivers/md/linear.c15
-rw-r--r--drivers/md/md.c275
-rw-r--r--drivers/md/multipath.c27
-rw-r--r--drivers/md/raid0.c17
-rw-r--r--drivers/md/raid1.c247
-rw-r--r--drivers/md/raid10.c261
-rw-r--r--drivers/md/raid5.c74
-rw-r--r--drivers/media/common/Kconfig1
-rw-r--r--drivers/media/common/ir-keymaps.c79
-rw-r--r--drivers/media/common/saa7146_fops.c1
-rw-r--r--drivers/media/dvb/b2c2/Kconfig14
-rw-r--r--drivers/media/dvb/b2c2/flexcop-fe-tuner.c24
-rw-r--r--drivers/media/dvb/bt8xx/Kconfig14
-rw-r--r--drivers/media/dvb/bt8xx/dst.c9
-rw-r--r--drivers/media/dvb/bt8xx/dst_ca.c11
-rw-r--r--drivers/media/dvb/bt8xx/dst_common.h3
-rw-r--r--drivers/media/dvb/bt8xx/dvb-bt8xx.c37
-rw-r--r--drivers/media/dvb/cinergyT2/cinergyT2.c2
-rw-r--r--drivers/media/dvb/dvb-core/Kconfig13
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.c42
-rw-r--r--drivers/media/dvb/dvb-core/dvb_frontend.h7
-rw-r--r--drivers/media/dvb/dvb-core/dvb_ringbuffer.c1
-rw-r--r--drivers/media/dvb/dvb-core/dvbdev.h22
-rw-r--r--drivers/media/dvb/dvb-usb/Kconfig17
-rw-r--r--drivers/media/dvb/dvb-usb/a800.c8
-rw-r--r--drivers/media/dvb/dvb-usb/cxusb.c11
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-common.c200
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mb.c18
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb-mc.c41
-rw-r--r--drivers/media/dvb/dvb-usb/dibusb.h3
-rw-r--r--drivers/media/dvb/dvb-usb/digitv.c86
-rw-r--r--drivers/media/dvb/dvb-usb/dtt200u.c50
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-dvb.c20
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-ids.h124
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-remote.c5
-rw-r--r--drivers/media/dvb/dvb-usb/dvb-usb-urb.c1
-rw-r--r--drivers/media/dvb/dvb-usb/nova-t-usb2.c2
-rw-r--r--drivers/media/dvb/dvb-usb/umt-010.c2
-rw-r--r--drivers/media/dvb/frontends/Kconfig71
-rw-r--r--drivers/media/dvb/frontends/Makefile8
-rw-r--r--drivers/media/dvb/frontends/bcm3510.h9
-rw-r--r--drivers/media/dvb/frontends/cx22700.h9
-rw-r--r--drivers/media/dvb/frontends/cx22702.c4
-rw-r--r--drivers/media/dvb/frontends/cx22702.h9
-rw-r--r--drivers/media/dvb/frontends/cx24110.c17
-rw-r--r--drivers/media/dvb/frontends/cx24110.h19
-rw-r--r--drivers/media/dvb/frontends/cx24123.c98
-rw-r--r--drivers/media/dvb/frontends/cx24123.h12
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.c83
-rw-r--r--drivers/media/dvb/frontends/dib3000-common.h135
-rw-r--r--drivers/media/dvb/frontends/dib3000.h11
-rw-r--r--drivers/media/dvb/frontends/dib3000mb.c76
-rw-r--r--drivers/media/dvb/frontends/dib3000mb_priv.h93
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.c1432
-rw-r--r--drivers/media/dvb/frontends/dib3000mc.h58
-rw-r--r--drivers/media/dvb/frontends/dib3000mc_priv.h428
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.c152
-rw-r--r--drivers/media/dvb/frontends/dibx000_common.h166
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.c11
-rw-r--r--drivers/media/dvb/frontends/dvb-pll.h4
-rw-r--r--drivers/media/dvb/frontends/isl6421.c30
-rw-r--r--drivers/media/dvb/frontends/isl6421.h11
-rw-r--r--drivers/media/dvb/frontends/l64781.h10
-rw-r--r--drivers/media/dvb/frontends/lgdt330x.h9
-rw-r--r--drivers/media/dvb/frontends/lnbp21.c30
-rw-r--r--drivers/media/dvb/frontends/lnbp21.h12
-rw-r--r--drivers/media/dvb/frontends/mt2060.c367
-rw-r--r--drivers/media/dvb/frontends/mt2060.h35
-rw-r--r--drivers/media/dvb/frontends/mt2060_priv.h105
-rw-r--r--drivers/media/dvb/frontends/mt312.h10
-rw-r--r--drivers/media/dvb/frontends/mt352.c16
-rw-r--r--drivers/media/dvb/frontends/mt352.h16
-rw-r--r--drivers/media/dvb/frontends/nxt200x.h9
-rw-r--r--drivers/media/dvb/frontends/nxt6000.h9
-rw-r--r--drivers/media/dvb/frontends/or51132.h9
-rw-r--r--drivers/media/dvb/frontends/or51211.h9
-rw-r--r--drivers/media/dvb/frontends/s5h1420.h9
-rw-r--r--drivers/media/dvb/frontends/sp8870.h9
-rw-r--r--drivers/media/dvb/frontends/sp887x.h9
-rw-r--r--drivers/media/dvb/frontends/stv0297.h9
-rw-r--r--drivers/media/dvb/frontends/stv0299.c9
-rw-r--r--drivers/media/dvb/frontends/stv0299.h19
-rw-r--r--drivers/media/dvb/frontends/tda10021.c63
-rw-r--r--drivers/media/dvb/frontends/tda10021.h19
-rw-r--r--drivers/media/dvb/frontends/tda1004x.c10
-rw-r--r--drivers/media/dvb/frontends/tda1004x.h25
-rw-r--r--drivers/media/dvb/frontends/tda10086.c740
-rw-r--r--drivers/media/dvb/frontends/tda10086.h41
-rw-r--r--drivers/media/dvb/frontends/tda8083.h9
-rw-r--r--drivers/media/dvb/frontends/tda826x.c173
-rw-r--r--drivers/media/dvb/frontends/tda826x.h40
-rw-r--r--drivers/media/dvb/frontends/tua6100.c205
-rw-r--r--drivers/media/dvb/frontends/tua6100.h47
-rw-r--r--drivers/media/dvb/frontends/ves1820.h9
-rw-r--r--drivers/media/dvb/frontends/ves1x93.h9
-rw-r--r--drivers/media/dvb/frontends/zl10353.c11
-rw-r--r--drivers/media/dvb/frontends/zl10353.h14
-rw-r--r--drivers/media/dvb/ttpci/Kconfig57
-rw-r--r--drivers/media/dvb/ttpci/av7110.c58
-rw-r--r--drivers/media/dvb/ttpci/av7110_av.c1
-rw-r--r--drivers/media/dvb/ttpci/av7110_ca.c1
-rw-r--r--drivers/media/dvb/ttpci/av7110_hw.c1
-rw-r--r--drivers/media/dvb/ttpci/av7110_v4l.c3
-rw-r--r--drivers/media/dvb/ttpci/budget-av.c184
-rw-r--r--drivers/media/dvb/ttpci/budget-ci.c32
-rw-r--r--drivers/media/dvb/ttpci/budget-patch.c17
-rw-r--r--drivers/media/dvb/ttpci/budget.c53
-rw-r--r--drivers/media/dvb/ttusb-budget/Kconfig14
-rw-r--r--drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c28
-rw-r--r--drivers/media/dvb/ttusb-dec/ttusb_dec.c8
-rw-r--r--drivers/media/radio/Kconfig30
-rw-r--r--drivers/media/radio/dsbr100.c198
-rw-r--r--drivers/media/radio/radio-aimslab.c151
-rw-r--r--drivers/media/radio/radio-aztech.c166
-rw-r--r--drivers/media/radio/radio-cadet.c250
-rw-r--r--drivers/media/radio/radio-gemtek-pci.c170
-rw-r--r--drivers/media/radio/radio-gemtek.c165
-rw-r--r--drivers/media/radio/radio-maestro.c197
-rw-r--r--drivers/media/radio/radio-maxiradio.c166
-rw-r--r--drivers/media/radio/radio-rtrack2.c163
-rw-r--r--drivers/media/radio/radio-sf16fmi.c158
-rw-r--r--drivers/media/radio/radio-sf16fmr2.c223
-rw-r--r--drivers/media/radio/radio-terratec.c154
-rw-r--r--drivers/media/radio/radio-trust.c188
-rw-r--r--drivers/media/radio/radio-typhoon.c171
-rw-r--r--drivers/media/radio/radio-zoltrix.c183
-rw-r--r--drivers/media/video/Kconfig476
-rw-r--r--drivers/media/video/Makefile45
-rw-r--r--drivers/media/video/bt866.c2
-rw-r--r--drivers/media/video/bt8xx/Kconfig5
-rw-r--r--drivers/media/video/bt8xx/bttv-cards.c2
-rw-r--r--drivers/media/video/bt8xx/bttv-driver.c12
-rw-r--r--drivers/media/video/bt8xx/bttv-i2c.c17
-rw-r--r--drivers/media/video/compat_ioctl32.c59
-rw-r--r--drivers/media/video/cpia2/cpia2.h4
-rw-r--r--drivers/media/video/cx2341x.c25
-rw-r--r--drivers/media/video/cx25840/Kconfig2
-rw-r--r--drivers/media/video/cx25840/cx25840-vbi.c4
-rw-r--r--drivers/media/video/cx88/Kconfig109
-rw-r--r--drivers/media/video/cx88/Makefile7
-rw-r--r--drivers/media/video/cx88/cx88-blackbird.c2
-rw-r--r--drivers/media/video/cx88/cx88-cards.c161
-rw-r--r--drivers/media/video/cx88/cx88-core.c13
-rw-r--r--drivers/media/video/cx88/cx88-dvb.c330
-rw-r--r--drivers/media/video/cx88/cx88-i2c.c14
-rw-r--r--drivers/media/video/cx88/cx88-input.c19
-rw-r--r--drivers/media/video/cx88/cx88-tvaudio.c5
-rw-r--r--drivers/media/video/cx88/cx88-video.c7
-rw-r--r--drivers/media/video/cx88/cx88-vp3054-i2c.c1
-rw-r--r--drivers/media/video/cx88/cx88.h5
-rw-r--r--drivers/media/video/em28xx/Kconfig3
-rw-r--r--drivers/media/video/em28xx/em28xx-video.c2
-rw-r--r--drivers/media/video/ks0127.c3
-rw-r--r--drivers/media/video/ov511.c7
-rw-r--r--drivers/media/video/pvrusb2/Kconfig18
-rw-r--r--drivers/media/video/pvrusb2/Makefile6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-ctrl.c21
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-encoder.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h31
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-hdw.c84
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c4
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c2
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-i2c-core.c6
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-main.c1
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-sysfs.c3
-rw-r--r--drivers/media/video/pvrusb2/pvrusb2-v4l2.c36
-rw-r--r--drivers/media/video/pwc/pwc-if.c2
-rw-r--r--drivers/media/video/saa5246a.c1
-rw-r--r--drivers/media/video/saa5249.c1
-rw-r--r--drivers/media/video/saa7115.c1250
-rw-r--r--drivers/media/video/saa711x_regs.h549
-rw-r--r--drivers/media/video/saa7134/Kconfig50
-rw-r--r--drivers/media/video/saa7134/Makefile3
-rw-r--r--drivers/media/video/saa7134/saa7134-alsa.c8
-rw-r--r--drivers/media/video/saa7134/saa7134-cards.c127
-rw-r--r--drivers/media/video/saa7134/saa7134-core.c2
-rw-r--r--drivers/media/video/saa7134/saa7134-dvb.c197
-rw-r--r--drivers/media/video/saa7134/saa7134-input.c6
-rw-r--r--drivers/media/video/saa7134/saa7134-tvaudio.c1
-rw-r--r--drivers/media/video/saa7134/saa7134.h3
-rw-r--r--drivers/media/video/tda9887.c2
-rw-r--r--drivers/media/video/tuner-simple.c6
-rw-r--r--drivers/media/video/tuner-types.c1
-rw-r--r--drivers/media/video/tvaudio.c42
-rw-r--r--drivers/media/video/tveeprom.c2
-rw-r--r--drivers/media/video/tvp5150.c7
-rw-r--r--drivers/media/video/usbvideo/konicawc.c9
-rw-r--r--drivers/media/video/usbvideo/vicam.c4
-rw-r--r--drivers/media/video/v4l1-compat.c12
-rw-r--r--drivers/media/video/v4l2-common.c1
-rw-r--r--drivers/media/video/video-buf-dvb.c2
-rw-r--r--drivers/media/video/videodev.c32
-rw-r--r--drivers/media/video/vino.c1
-rw-r--r--drivers/media/video/vivi.c5
-rw-r--r--drivers/media/video/vpx3220.c2
-rw-r--r--drivers/media/video/w9968cf.c7
-rw-r--r--drivers/media/video/zoran_card.c7
-rw-r--r--drivers/media/video/zoran_driver.c7
-rw-r--r--drivers/media/video/zr36120.c6
-rw-r--r--drivers/message/i2o/Kconfig2
-rw-r--r--drivers/message/i2o/i2o_block.c7
-rw-r--r--drivers/message/i2o/pci.c7
-rw-r--r--drivers/mfd/ucb1x00-ts.c45
-rw-r--r--drivers/misc/Makefile3
-rw-r--r--drivers/misc/ibmasm/ibmasmfs.c17
-rw-r--r--drivers/misc/lkdtm.c342
-rw-r--r--drivers/mmc/Kconfig2
-rw-r--r--drivers/mmc/Makefile3
-rw-r--r--drivers/mmc/at91_mci.c5
-rw-r--r--drivers/mmc/au1xmmc.c2
-rw-r--r--drivers/mmc/imxmmc.c2
-rw-r--r--drivers/mmc/mmc.c1
-rw-r--r--drivers/mmc/mmc_block.c66
-rw-r--r--drivers/mmc/mmc_queue.c6
-rw-r--r--drivers/mmc/mmci.c13
-rw-r--r--drivers/mmc/omap.c7
-rw-r--r--drivers/mmc/sdhci.c7
-rw-r--r--drivers/mmc/sdhci.h5
-rw-r--r--drivers/mmc/wbsd.c7
-rw-r--r--drivers/mmc/wbsd.h5
-rw-r--r--drivers/mtd/Kconfig12
-rw-r--r--drivers/mtd/devices/Kconfig2
-rw-r--r--drivers/mtd/maps/arctic-mtd.c6
-rw-r--r--drivers/mtd/maps/beech-mtd.c6
-rw-r--r--drivers/mtd/maps/cstm_mips_ixx.c8
-rw-r--r--drivers/mtd/maps/nettel.c2
-rw-r--r--drivers/mtd/maps/redwood.c2
-rw-r--r--drivers/mtd/mtd_blkdevs.c4
-rw-r--r--drivers/mtd/nand/edb7312.c2
-rw-r--r--drivers/mtd/nand/nand_base.c172
-rw-r--r--drivers/mtd/nand/nand_bbt.c2
-rw-r--r--drivers/mtd/nftlcore.c4
-rw-r--r--drivers/mtd/onenand/Kconfig6
-rw-r--r--drivers/mtd/onenand/onenand_base.c154
-rw-r--r--drivers/net/3c509.c1
-rw-r--r--drivers/net/3c59x.c1
-rw-r--r--drivers/net/8390.c6
-rw-r--r--drivers/net/Kconfig5
-rw-r--r--drivers/net/Space.c2
-rw-r--r--drivers/net/acenic.c4
-rw-r--r--drivers/net/appletalk/ipddp.c5
-rw-r--r--drivers/net/arm/at91_ether.c2
-rw-r--r--drivers/net/bnx2.c32
-rw-r--r--drivers/net/bonding/bond_3ad.c70
-rw-r--r--drivers/net/bonding/bond_main.c230
-rw-r--r--drivers/net/bonding/bond_sysfs.c56
-rw-r--r--drivers/net/bonding/bonding.h34
-rw-r--r--drivers/net/dgrs.c1
-rw-r--r--drivers/net/e100.c82
-rw-r--r--drivers/net/e1000/LICENSE339
-rw-r--r--drivers/net/e1000/Makefile35
-rw-r--r--drivers/net/e1000/e1000.h59
-rw-r--r--drivers/net/e1000/e1000_ethtool.c150
-rw-r--r--drivers/net/e1000/e1000_hw.c1074
-rw-r--r--drivers/net/e1000/e1000_hw.h90
-rw-r--r--drivers/net/e1000/e1000_main.c271
-rw-r--r--drivers/net/e1000/e1000_osdep.h35
-rw-r--r--drivers/net/e1000/e1000_param.c47
-rw-r--r--drivers/net/fec_8xx/fec_main.c2
-rw-r--r--drivers/net/forcedeth.c8
-rw-r--r--drivers/net/fs_enet/fs_enet.h3
-rw-r--r--drivers/net/gt64240eth.h402
-rw-r--r--drivers/net/hp100.c9
-rw-r--r--drivers/net/ifb.c4
-rw-r--r--drivers/net/irda/Kconfig1
-rw-r--r--drivers/net/irda/irda-usb.c18
-rw-r--r--drivers/net/irda/nsc-ircc.c2
-rw-r--r--drivers/net/irda/smsc-ircc2.c38
-rw-r--r--drivers/net/irda/stir4200.c15
-rw-r--r--drivers/net/irda/via-ircc.c7
-rw-r--r--drivers/net/irda/vlsi_ir.h2
-rw-r--r--drivers/net/ixgb/Makefile38
-rw-r--r--drivers/net/ixgb/ixgb.h38
-rw-r--r--drivers/net/ixgb/ixgb_ee.c36
-rw-r--r--drivers/net/ixgb/ixgb_ee.h36
-rw-r--r--drivers/net/ixgb/ixgb_ethtool.c36
-rw-r--r--drivers/net/ixgb/ixgb_hw.c36
-rw-r--r--drivers/net/ixgb/ixgb_hw.h36
-rw-r--r--drivers/net/ixgb/ixgb_ids.h36
-rw-r--r--drivers/net/ixgb/ixgb_main.c46
-rw-r--r--drivers/net/ixgb/ixgb_osdep.h36
-rw-r--r--drivers/net/ixgb/ixgb_param.c36
-rw-r--r--drivers/net/loopback.c31
-rw-r--r--drivers/net/ne3210.c1
-rw-r--r--drivers/net/phy/fixed.c6
-rw-r--r--drivers/net/phy/phy_device.c10
-rw-r--r--drivers/net/pppoe.c1
-rw-r--r--drivers/net/s2io.c10
-rw-r--r--drivers/net/skge.c357
-rw-r--r--drivers/net/skge.h37
-rw-r--r--drivers/net/sky2.c548
-rw-r--r--drivers/net/sky2.h62
-rw-r--r--drivers/net/smc91x.h19
-rw-r--r--drivers/net/spider_net.c6
-rw-r--r--drivers/net/stnic.c2
-rw-r--r--drivers/net/tg3.c480
-rw-r--r--drivers/net/tg3.h48
-rw-r--r--drivers/net/tokenring/lanstreamer.c59
-rw-r--r--drivers/net/tokenring/lanstreamer.h12
-rw-r--r--drivers/net/tulip/de4x5.c1
-rw-r--r--drivers/net/tun.c39
-rw-r--r--drivers/net/typhoon.c4
-rw-r--r--drivers/net/wan/Kconfig12
-rw-r--r--drivers/net/wan/Makefile19
-rw-r--r--drivers/net/wan/hdlc.c (renamed from drivers/net/wan/hdlc_generic.c)169
-rw-r--r--drivers/net/wan/hdlc_cisco.c200
-rw-r--r--drivers/net/wan/hdlc_fr.c389
-rw-r--r--drivers/net/wan/hdlc_ppp.c77
-rw-r--r--drivers/net/wan/hdlc_raw.c50
-rw-r--r--drivers/net/wan/hdlc_raw_eth.c49
-rw-r--r--drivers/net/wan/hdlc_x25.c54
-rw-r--r--drivers/net/wan/pc300.h1
-rw-r--r--drivers/net/wan/pc300_drv.c30
-rw-r--r--drivers/net/wan/syncppp.c4
-rw-r--r--drivers/net/wireless/airo.c42
-rw-r--r--drivers/net/wireless/atmel.c18
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx.h1
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_main.c10
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_phy.c15
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_wx.c2
-rw-r--r--drivers/net/wireless/bcm43xx/bcm43xx_xmit.c5
-rw-r--r--drivers/net/wireless/hostap/hostap_ioctl.c10
-rw-r--r--drivers/net/wireless/ipw2100.c36
-rw-r--r--drivers/net/wireless/ipw2200.c16
-rw-r--r--drivers/net/wireless/orinoco.c10
-rw-r--r--drivers/net/wireless/prism54/isl_ioctl.c16
-rw-r--r--drivers/net/wireless/ray_cs.c2
-rw-r--r--drivers/net/wireless/strip.c4
-rw-r--r--drivers/net/wireless/wl3501_cs.c6
-rw-r--r--drivers/net/wireless/zd1201.c6
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.c137
-rw-r--r--drivers/net/wireless/zd1211rw/zd_chip.h23
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.c50
-rw-r--r--drivers/net/wireless/zd1211rw/zd_mac.h5
-rw-r--r--drivers/net/wireless/zd1211rw/zd_netdev.c2
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.c10
-rw-r--r--drivers/net/wireless/zd1211rw/zd_usb.h2
-rw-r--r--drivers/oprofile/oprofilefs.c11
-rw-r--r--drivers/parisc/led.c2
-rw-r--r--drivers/parisc/power.c3
-rw-r--r--drivers/parport/parport_serial.c4
-rw-r--r--drivers/pci/Kconfig25
-rw-r--r--drivers/pci/bus.c22
-rw-r--r--drivers/pci/hotplug/acpiphp.h5
-rw-r--r--drivers/pci/hotplug/acpiphp_glue.c127
-rw-r--r--drivers/pci/hotplug/acpiphp_ibm.c4
-rw-r--r--drivers/pci/hotplug/cpqphp_sysfs.c2
-rw-r--r--drivers/pci/hotplug/fakephp.c18
-rw-r--r--drivers/pci/hotplug/pci_hotplug.h4
-rw-r--r--drivers/pci/hotplug/pci_hotplug_core.c157
-rw-r--r--drivers/pci/hotplug/pciehp_ctrl.c12
-rw-r--r--drivers/pci/hotplug/pcihp_skeleton.c9
-rw-r--r--drivers/pci/hotplug/shpchp.h2
-rw-r--r--drivers/pci/hotplug/shpchp_core.c6
-rw-r--r--drivers/pci/hotplug/shpchp_sysfs.c4
-rw-r--r--drivers/pci/msi.c64
-rw-r--r--drivers/pci/pci-driver.c47
-rw-r--r--drivers/pci/pci-sysfs.c153
-rw-r--r--drivers/pci/pci.c59
-rw-r--r--drivers/pci/pci.h2
-rw-r--r--drivers/pci/pcie/Kconfig1
-rw-r--r--drivers/pci/pcie/Makefile3
-rw-r--r--drivers/pci/pcie/aer/Kconfig12
-rw-r--r--drivers/pci/pcie/aer/Makefile8
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c346
-rw-r--r--drivers/pci/pcie/aer/aerdrv.h125
-rw-r--r--drivers/pci/pcie/aer/aerdrv_acpi.c68
-rw-r--r--drivers/pci/pcie/aer/aerdrv_core.c758
-rw-r--r--drivers/pci/pcie/aer/aerdrv_errprint.c248
-rw-r--r--drivers/pci/pcie/portdrv.h2
-rw-r--r--drivers/pci/pcie/portdrv_bus.c1
-rw-r--r--drivers/pci/pcie/portdrv_core.c11
-rw-r--r--drivers/pci/pcie/portdrv_pci.c211
-rw-r--r--drivers/pci/probe.c16
-rw-r--r--drivers/pci/quirks.c121
-rw-r--r--drivers/pci/remove.c37
-rw-r--r--drivers/pci/setup-bus.c13
-rw-r--r--drivers/pcmcia/cardbus.c5
-rw-r--r--drivers/pcmcia/omap_cf.c25
-rw-r--r--drivers/pcmcia/socket_sysfs.c27
-rw-r--r--drivers/pnp/pnpbios/core.c8
-rw-r--r--drivers/rtc/Kconfig38
-rw-r--r--drivers/rtc/Makefile5
-rw-r--r--drivers/rtc/class.c6
-rw-r--r--drivers/rtc/rtc-at91.c24
-rw-r--r--drivers/rtc/rtc-dev.c7
-rw-r--r--drivers/rtc/rtc-ds1307.c2
-rw-r--r--drivers/rtc/rtc-ds1553.c10
-rw-r--r--drivers/rtc/rtc-ds1672.c2
-rw-r--r--drivers/rtc/rtc-ds1742.c12
-rw-r--r--drivers/rtc/rtc-ep93xx.c2
-rw-r--r--drivers/rtc/rtc-isl1208.c2
-rw-r--r--drivers/rtc/rtc-lib.c8
-rw-r--r--drivers/rtc/rtc-m48t86.c2
-rw-r--r--drivers/rtc/rtc-max6902.c2
-rw-r--r--drivers/rtc/rtc-pcf8563.c6
-rw-r--r--drivers/rtc/rtc-pcf8583.c2
-rw-r--r--drivers/rtc/rtc-pl031.c2
-rw-r--r--drivers/rtc/rtc-proc.c6
-rw-r--r--drivers/rtc/rtc-rs5c348.c11
-rw-r--r--drivers/rtc/rtc-rs5c372.c2
-rw-r--r--drivers/rtc/rtc-s3c.c2
-rw-r--r--drivers/rtc/rtc-sa1100.c2
-rw-r--r--drivers/rtc/rtc-sh.c467
-rw-r--r--drivers/rtc/rtc-sysfs.c2
-rw-r--r--drivers/rtc/rtc-test.c2
-rw-r--r--drivers/rtc/rtc-v3020.c5
-rw-r--r--drivers/rtc/rtc-vr41xx.c2
-rw-r--r--drivers/rtc/rtc-x1205.c2
-rw-r--r--drivers/s390/block/Kconfig2
-rw-r--r--drivers/s390/block/dasd_diag.c36
-rw-r--r--drivers/s390/block/dasd_eckd.c2
-rw-r--r--drivers/s390/block/dasd_fba.c2
-rw-r--r--drivers/s390/block/xpram.c54
-rw-r--r--drivers/s390/char/con3215.c2
-rw-r--r--drivers/s390/char/fs3270.c12
-rw-r--r--drivers/s390/char/sclp.c31
-rw-r--r--drivers/s390/char/sclp_tty.c2
-rw-r--r--drivers/s390/char/sclp_vt220.c2
-rw-r--r--drivers/s390/char/tty3270.c3
-rw-r--r--drivers/s390/char/vmwatchdog.c52
-rw-r--r--drivers/s390/cio/device_id.c38
-rw-r--r--drivers/s390/cio/ioasm.h220
-rw-r--r--drivers/s390/cio/qdio.h192
-rw-r--r--drivers/s390/net/iucv.c39
-rw-r--r--drivers/s390/net/qeth_main.c2
-rw-r--r--drivers/s390/s390mach.c95
-rw-r--r--drivers/s390/scsi/zfcp_scsi.c2
-rw-r--r--drivers/sbus/char/aurora.c2
-rw-r--r--drivers/sbus/char/bbc_envctrl.c5
-rw-r--r--drivers/sbus/char/envctrl.c7
-rw-r--r--drivers/scsi/53c700.c2
-rw-r--r--drivers/scsi/BusLogic.h5
-rw-r--r--drivers/scsi/Kconfig2
-rw-r--r--drivers/scsi/aha1740.c1
-rw-r--r--drivers/scsi/aic7xxx/aic7770_osm.c3
-rw-r--r--drivers/scsi/aic7xxx/aic79xx_osm.c4
-rw-r--r--drivers/scsi/aic7xxx/aic7xxx_osm.c2
-rw-r--r--drivers/scsi/aic7xxx_old.c4
-rw-r--r--drivers/scsi/gdth.c4
-rw-r--r--drivers/scsi/ide-scsi.c16
-rw-r--r--drivers/scsi/libsas/sas_scsi_host.c2
-rw-r--r--drivers/scsi/lpfc/lpfc_ct.c8
-rw-r--r--drivers/scsi/mesh.c15
-rw-r--r--drivers/scsi/pluto.c6
-rw-r--r--drivers/scsi/qla1280.c4
-rw-r--r--drivers/scsi/scsi.c13
-rw-r--r--drivers/scsi/scsi_lib.c37
-rw-r--r--drivers/scsi/sd.c5
-rw-r--r--drivers/scsi/sim710.c1
-rw-r--r--drivers/scsi/sun3_NCR5380.c2
-rw-r--r--drivers/scsi/sun3_scsi.c2
-rw-r--r--drivers/scsi/sun3_scsi_vme.c2
-rw-r--r--drivers/serial/68328serial.c2
-rw-r--r--drivers/serial/68360serial.c2
-rw-r--r--drivers/serial/8250_acorn.c9
-rw-r--r--drivers/serial/8250_gsc.c1
-rw-r--r--drivers/serial/Kconfig11
-rw-r--r--drivers/serial/at91_serial.c2
-rw-r--r--drivers/serial/crisv10.c2
-rw-r--r--drivers/serial/ioc4_serial.c3
-rw-r--r--drivers/serial/ip22zilog.c16
-rw-r--r--drivers/serial/mcfserial.c2
-rw-r--r--drivers/serial/mpc52xx_uart.c11
-rw-r--r--drivers/serial/mpsc.c12
-rw-r--r--drivers/serial/mux.c2
-rw-r--r--drivers/serial/serial_core.c16
-rw-r--r--drivers/serial/sh-sci.c1146
-rw-r--r--drivers/serial/sh-sci.h90
-rw-r--r--drivers/serial/sunsu.c3
-rw-r--r--drivers/serial/sunzilog.c2
-rw-r--r--drivers/tc/zs.c2
-rw-r--r--drivers/usb/Kconfig1
-rw-r--r--drivers/usb/Makefile12
-rw-r--r--drivers/usb/atm/ueagle-atm.c74
-rw-r--r--drivers/usb/class/cdc-acm.c2
-rw-r--r--drivers/usb/class/usblp.c15
-rw-r--r--drivers/usb/core/Makefile2
-rw-r--r--drivers/usb/core/buffer.c4
-rw-r--r--drivers/usb/core/config.c4
-rw-r--r--drivers/usb/core/devices.c6
-rw-r--r--drivers/usb/core/devio.c78
-rw-r--r--drivers/usb/core/driver.c1012
-rw-r--r--drivers/usb/core/endpoint.c30
-rw-r--r--drivers/usb/core/file.c2
-rw-r--r--drivers/usb/core/generic.c208
-rw-r--r--drivers/usb/core/hcd-pci.c18
-rw-r--r--drivers/usb/core/hcd.c254
-rw-r--r--drivers/usb/core/hcd.h60
-rw-r--r--drivers/usb/core/hub.c555
-rw-r--r--drivers/usb/core/hub.h3
-rw-r--r--drivers/usb/core/inode.c26
-rw-r--r--drivers/usb/core/message.c148
-rw-r--r--drivers/usb/core/notify.c3
-rw-r--r--drivers/usb/core/sysfs.c60
-rw-r--r--drivers/usb/core/urb.c15
-rw-r--r--drivers/usb/core/usb.c569
-rw-r--r--drivers/usb/core/usb.h95
-rw-r--r--drivers/usb/gadget/Kconfig16
-rw-r--r--drivers/usb/gadget/Makefile2
-rw-r--r--drivers/usb/gadget/at91_udc.c8
-rw-r--r--drivers/usb/gadget/dummy_hcd.c41
-rw-r--r--drivers/usb/gadget/ether.c11
-rw-r--r--drivers/usb/gadget/file_storage.c37
-rw-r--r--drivers/usb/gadget/gmidi.c1337
-rw-r--r--drivers/usb/gadget/inode.c149
-rw-r--r--drivers/usb/gadget/net2280.c156
-rw-r--r--drivers/usb/gadget/omap_udc.c6
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.c70
-rw-r--r--drivers/usb/gadget/pxa2xx_udc.h24
-rw-r--r--drivers/usb/gadget/serial.c7
-rw-r--r--drivers/usb/gadget/zero.c2
-rw-r--r--drivers/usb/host/Kconfig29
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-au1xxx.c2
-rw-r--r--drivers/usb/host/ehci-dbg.c31
-rw-r--r--drivers/usb/host/ehci-fsl.c2
-rw-r--r--drivers/usb/host/ehci-hcd.c95
-rw-r--r--drivers/usb/host/ehci-hub.c14
-rw-r--r--drivers/usb/host/ehci-mem.c14
-rw-r--r--drivers/usb/host/ehci-pci.c10
-rw-r--r--drivers/usb/host/ehci-q.c26
-rw-r--r--drivers/usb/host/ehci-sched.c26
-rw-r--r--drivers/usb/host/ehci.h59
-rw-r--r--drivers/usb/host/isp116x-hcd.c4
-rw-r--r--drivers/usb/host/isp116x.h2
-rw-r--r--drivers/usb/host/ohci-at91.c7
-rw-r--r--drivers/usb/host/ohci-au1xxx.c7
-rw-r--r--drivers/usb/host/ohci-dbg.c18
-rw-r--r--drivers/usb/host/ohci-ep93xx.c3
-rw-r--r--drivers/usb/host/ohci-hcd.c64
-rw-r--r--drivers/usb/host/ohci-hub.c273
-rw-r--r--drivers/usb/host/ohci-lh7a404.c7
-rw-r--r--drivers/usb/host/ohci-mem.c1
-rw-r--r--drivers/usb/host/ohci-omap.c120
-rw-r--r--drivers/usb/host/ohci-pci.c16
-rw-r--r--drivers/usb/host/ohci-pnx4008.c476
-rw-r--r--drivers/usb/host/ohci-ppc-soc.c3
-rw-r--r--drivers/usb/host/ohci-pxa27x.c3
-rw-r--r--drivers/usb/host/ohci-s3c2410.c5
-rw-r--r--drivers/usb/host/ohci-sa1111.c5
-rw-r--r--drivers/usb/host/ohci.h5
-rw-r--r--drivers/usb/host/sl811-hcd.c13
-rw-r--r--drivers/usb/host/u132-hcd.c3295
-rw-r--r--drivers/usb/host/uhci-debug.c6
-rw-r--r--drivers/usb/host/uhci-hcd.c12
-rw-r--r--drivers/usb/host/uhci-hub.c12
-rw-r--r--drivers/usb/image/mdc800.c4
-rw-r--r--drivers/usb/image/microtek.c18
-rw-r--r--drivers/usb/image/microtek.h4
-rw-r--r--drivers/usb/input/Kconfig48
-rw-r--r--drivers/usb/input/Makefile7
-rw-r--r--drivers/usb/input/acecad.c5
-rw-r--r--drivers/usb/input/appletouch.c5
-rw-r--r--drivers/usb/input/ati_remote.c8
-rw-r--r--drivers/usb/input/hid-core.c33
-rw-r--r--drivers/usb/input/hid-ff.c49
-rw-r--r--drivers/usb/input/hid-input.c23
-rw-r--r--drivers/usb/input/hid-lgff.c523
-rw-r--r--drivers/usb/input/hid-pidff.c1330
-rw-r--r--drivers/usb/input/hid-tmff.c399
-rw-r--r--drivers/usb/input/hid-zpff.c110
-rw-r--r--drivers/usb/input/hid.h32
-rw-r--r--drivers/usb/input/hiddev.c2
-rw-r--r--drivers/usb/input/itmtouch.c2
-rw-r--r--drivers/usb/input/keyspan_remote.c3
-rw-r--r--drivers/usb/input/mtouchusb.c2
-rw-r--r--drivers/usb/input/pid.c295
-rw-r--r--drivers/usb/input/pid.h62
-rw-r--r--drivers/usb/input/powermate.c4
-rw-r--r--drivers/usb/input/touchkitusb.c2
-rw-r--r--drivers/usb/input/trancevibrator.c159
-rw-r--r--drivers/usb/input/usbmouse.c2
-rw-r--r--drivers/usb/input/usbtouchscreen.c285
-rw-r--r--drivers/usb/input/wacom.c1003
-rw-r--r--drivers/usb/input/wacom.h132
-rw-r--r--drivers/usb/input/wacom_sys.c315
-rw-r--r--drivers/usb/input/wacom_wac.c646
-rw-r--r--drivers/usb/input/wacom_wac.h48
-rw-r--r--drivers/usb/input/yealink.c2
-rw-r--r--drivers/usb/misc/Kconfig61
-rw-r--r--drivers/usb/misc/Makefile5
-rw-r--r--drivers/usb/misc/adutux.c900
-rw-r--r--drivers/usb/misc/auerswald.c6
-rw-r--r--drivers/usb/misc/cypress_cy7c63.c19
-rw-r--r--drivers/usb/misc/cytherm.c35
-rw-r--r--drivers/usb/misc/ftdi-elan.c2809
-rw-r--r--drivers/usb/misc/idmouse.c2
-rw-r--r--drivers/usb/misc/ldusb.c10
-rw-r--r--drivers/usb/misc/legousbtower.c2
-rw-r--r--drivers/usb/misc/phidget.c43
-rw-r--r--drivers/usb/misc/phidget.h12
-rw-r--r--drivers/usb/misc/phidgetkit.c372
-rw-r--r--drivers/usb/misc/phidgetmotorcontrol.c466
-rw-r--r--drivers/usb/misc/phidgetservo.c117
-rw-r--r--drivers/usb/misc/sisusbvga/sisusb.c2
-rw-r--r--drivers/usb/misc/usb_u132.h97
-rw-r--r--drivers/usb/misc/usblcd.c10
-rw-r--r--drivers/usb/misc/usbled.c20
-rw-r--r--drivers/usb/mon/mon_main.c7
-rw-r--r--drivers/usb/mon/mon_stat.c4
-rw-r--r--drivers/usb/mon/mon_text.c19
-rw-r--r--drivers/usb/mon/usb_mon.h5
-rw-r--r--drivers/usb/net/asix.c1005
-rw-r--r--drivers/usb/net/kaweth.c1
-rw-r--r--drivers/usb/net/net1080.c15
-rw-r--r--drivers/usb/net/pegasus.c20
-rw-r--r--drivers/usb/net/rtl8150.c2
-rw-r--r--drivers/usb/net/usbnet.c44
-rw-r--r--drivers/usb/net/usbnet.h1
-rw-r--r--drivers/usb/serial/Kconfig25
-rw-r--r--drivers/usb/serial/Makefile2
-rw-r--r--drivers/usb/serial/aircable.c625
-rw-r--r--drivers/usb/serial/airprime.c261
-rw-r--r--drivers/usb/serial/ark3116.c233
-rw-r--r--drivers/usb/serial/cyberjack.c6
-rw-r--r--drivers/usb/serial/cypress_m8.c129
-rw-r--r--drivers/usb/serial/ftdi_sio.c34
-rw-r--r--drivers/usb/serial/ftdi_sio.h10
-rw-r--r--drivers/usb/serial/garmin_gps.c219
-rw-r--r--drivers/usb/serial/generic.c6
-rw-r--r--drivers/usb/serial/ipaq.c39
-rw-r--r--drivers/usb/serial/ipw.c6
-rw-r--r--drivers/usb/serial/ir-usb.c6
-rw-r--r--drivers/usb/serial/keyspan_pda.c6
-rw-r--r--drivers/usb/serial/mos7840.c2962
-rw-r--r--drivers/usb/serial/omninet.c6
-rw-r--r--drivers/usb/serial/pl2303.c828
-rw-r--r--drivers/usb/serial/pl2303.h8
-rw-r--r--drivers/usb/serial/safe_serial.c6
-rw-r--r--drivers/usb/serial/usb-serial.c30
-rw-r--r--drivers/usb/storage/Kconfig17
-rw-r--r--drivers/usb/storage/Makefile1
-rw-r--r--drivers/usb/storage/initializers.c73
-rw-r--r--drivers/usb/storage/initializers.h1
-rw-r--r--drivers/usb/storage/karma.c155
-rw-r--r--drivers/usb/storage/karma.h7
-rw-r--r--drivers/usb/storage/libusual.c10
-rw-r--r--drivers/usb/storage/onetouch.c8
-rw-r--r--drivers/usb/storage/scsiglue.c15
-rw-r--r--drivers/usb/storage/transport.c5
-rw-r--r--drivers/usb/storage/unusual_devs.h32
-rw-r--r--drivers/usb/storage/usb.c11
-rw-r--r--drivers/usb/usb-skeleton.c101
-rw-r--r--drivers/video/Kconfig39
-rw-r--r--drivers/video/Makefile1
-rw-r--r--drivers/video/aty/atyfb.h6
-rw-r--r--drivers/video/aty/atyfb_base.c11
-rw-r--r--drivers/video/aty/mach64_ct.c2
-rw-r--r--drivers/video/aty/radeon_i2c.c106
-rw-r--r--drivers/video/aty/radeon_pm.c18
-rw-r--r--drivers/video/au1100fb.c30
-rw-r--r--drivers/video/backlight/hp680_bl.c4
-rw-r--r--drivers/video/backlight/locomolcd.c30
-rw-r--r--drivers/video/console/Kconfig2
-rw-r--r--drivers/video/console/fbcon.c54
-rw-r--r--drivers/video/console/fbcon.h2
-rw-r--r--drivers/video/console/fbcon_ccw.c2
-rw-r--r--drivers/video/console/fbcon_cw.c2
-rw-r--r--drivers/video/console/fbcon_ud.c2
-rw-r--r--drivers/video/console/softcursor.c31
-rw-r--r--drivers/video/fb_ddc.c116
-rw-r--r--drivers/video/fbmem.c3
-rw-r--r--drivers/video/fbsysfs.c35
-rw-r--r--drivers/video/hitfb.c229
-rw-r--r--drivers/video/i810/i810-i2c.c43
-rw-r--r--drivers/video/i810/i810_main.c18
-rw-r--r--drivers/video/intelfb/Makefile4
-rw-r--r--drivers/video/intelfb/intelfb.h79
-rw-r--r--drivers/video/intelfb/intelfb_i2c.c200
-rw-r--r--drivers/video/intelfb/intelfbdrv.c64
-rw-r--r--drivers/video/intelfb/intelfbhw.c136
-rw-r--r--drivers/video/intelfb/intelfbhw.h25
-rw-r--r--drivers/video/matrox/i2c-matroxfb.c12
-rw-r--r--drivers/video/matrox/matroxfb_base.c12
-rw-r--r--drivers/video/mbx/mbxfb.c21
-rw-r--r--drivers/video/nvidia/nv_i2c.c45
-rw-r--r--drivers/video/nvidia/nvidia.c29
-rw-r--r--drivers/video/pvr2fb.c22
-rw-r--r--drivers/video/riva/fbdev.c8
-rw-r--r--drivers/video/riva/rivafb-i2c.c44
-rw-r--r--drivers/video/savage/savagefb-i2c.c50
-rw-r--r--drivers/video/savage/savagefb_driver.c14
-rw-r--r--drivers/video/sis/init.h7
-rw-r--r--drivers/video/sis/init301.h7
-rw-r--r--drivers/video/sis/initextlfb.c4
-rw-r--r--drivers/video/sis/osdef.h4
-rw-r--r--drivers/video/sis/sis_accel.c26
-rw-r--r--drivers/video/sis/sis_accel.h14
-rw-r--r--drivers/video/sis/sis_main.c232
-rw-r--r--drivers/video/sis/sis_main.h65
-rw-r--r--drivers/video/sis/vgatypes.h2
-rw-r--r--drivers/video/sstfb.c170
1233 files changed, 60921 insertions, 25923 deletions
diff --git a/drivers/acorn/char/i2c.c b/drivers/acorn/char/i2c.c
index c26c08b3682..bdb9c8b78ed 100644
--- a/drivers/acorn/char/i2c.c
+++ b/drivers/acorn/char/i2c.c
@@ -308,7 +308,6 @@ static struct i2c_algo_bit_data ioc_data = {
.getsda = ioc_getsda,
.getscl = ioc_getscl,
.udelay = 80,
- .mdelay = 80,
.timeout = 100
};
diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c
index 1dda370f402..98099de59b4 100644
--- a/drivers/acpi/acpi_memhotplug.c
+++ b/drivers/acpi/acpi_memhotplug.c
@@ -238,6 +238,10 @@ static int acpi_memory_enable_device(struct acpi_memory_device *mem_device)
num_enabled++;
continue;
}
+
+ if (node < 0)
+ node = memory_add_physaddr_to_nid(info->start_addr);
+
result = add_memory(node, info->start_addr, info->length);
if (result)
continue;
diff --git a/drivers/acpi/i2c_ec.c b/drivers/acpi/i2c_ec.c
index 6809c283ec5..6342e612c20 100644
--- a/drivers/acpi/i2c_ec.c
+++ b/drivers/acpi/i2c_ec.c
@@ -293,7 +293,7 @@ static u32 acpi_ec_smb_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC);
}
-static struct i2c_algorithm acpi_ec_smbus_algorithm = {
+static const struct i2c_algorithm acpi_ec_smbus_algorithm = {
.smbus_xfer = acpi_ec_smb_access,
.functionality = acpi_ec_smb_func,
};
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c
index 507f051d1ce..20beea778ea 100644
--- a/drivers/acpi/osl.c
+++ b/drivers/acpi/osl.c
@@ -1079,7 +1079,7 @@ acpi_status acpi_os_purge_cache(acpi_cache_t * cache)
acpi_status acpi_os_delete_cache(acpi_cache_t * cache)
{
- (void)kmem_cache_destroy(cache);
+ kmem_cache_destroy(cache);
return (AE_OK);
}
diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c
index 71066066d62..0a395fca843 100644
--- a/drivers/acpi/processor_idle.c
+++ b/drivers/acpi/processor_idle.c
@@ -38,6 +38,7 @@
#include <linux/dmi.h>
#include <linux/moduleparam.h>
#include <linux/sched.h> /* need_resched() */
+#include <linux/latency.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -453,7 +454,8 @@ static void acpi_processor_idle(void)
*/
if (cx->promotion.state &&
((cx->promotion.state - pr->power.states) <= max_cstate)) {
- if (sleep_ticks > cx->promotion.threshold.ticks) {
+ if (sleep_ticks > cx->promotion.threshold.ticks &&
+ cx->promotion.state->latency <= system_latency_constraint()) {
cx->promotion.count++;
cx->demotion.count = 0;
if (cx->promotion.count >=
@@ -494,8 +496,10 @@ static void acpi_processor_idle(void)
end:
/*
* Demote if current state exceeds max_cstate
+ * or if the latency of the current state is unacceptable
*/
- if ((pr->power.state - pr->power.states) > max_cstate) {
+ if ((pr->power.state - pr->power.states) > max_cstate ||
+ pr->power.state->latency > system_latency_constraint()) {
if (cx->demotion.state)
next_state = cx->demotion.state;
}
@@ -1009,9 +1013,11 @@ static int acpi_processor_power_seq_show(struct seq_file *seq, void *offset)
seq_printf(seq, "active state: C%zd\n"
"max_cstate: C%d\n"
- "bus master activity: %08x\n",
+ "bus master activity: %08x\n"
+ "maximum allowed latency: %d usec\n",
pr->power.state ? pr->power.state - pr->power.states : 0,
- max_cstate, (unsigned)pr->power.bm_activity);
+ max_cstate, (unsigned)pr->power.bm_activity,
+ system_latency_constraint());
seq_puts(seq, "states:\n");
@@ -1077,6 +1083,28 @@ static const struct file_operations acpi_processor_power_fops = {
.release = single_release,
};
+static void smp_callback(void *v)
+{
+ /* we already woke the CPU up, nothing more to do */
+}
+
+/*
+ * This function gets called when a part of the kernel has a new latency
+ * requirement. This means we need to get all processors out of their C-state,
+ * and then recalculate a new suitable C-state. Just do a cross-cpu IPI; that
+ * wakes them all right up.
+ */
+static int acpi_processor_latency_notify(struct notifier_block *b,
+ unsigned long l, void *v)
+{
+ smp_call_function(smp_callback, NULL, 0, 1);
+ return NOTIFY_OK;
+}
+
+static struct notifier_block acpi_processor_latency_notifier = {
+ .notifier_call = acpi_processor_latency_notify,
+};
+
int acpi_processor_power_init(struct acpi_processor *pr,
struct acpi_device *device)
{
@@ -1093,6 +1121,7 @@ int acpi_processor_power_init(struct acpi_processor *pr,
"ACPI: processor limited to max C-state %d\n",
max_cstate);
first_run++;
+ register_latency_notifier(&acpi_processor_latency_notifier);
}
if (!pr)
@@ -1164,6 +1193,7 @@ int acpi_processor_power_exit(struct acpi_processor *pr,
* copies of pm_idle before proceeding.
*/
cpu_idle_wait();
+ unregister_latency_notifier(&acpi_processor_latency_notifier);
}
return 0;
diff --git a/drivers/ata/Kconfig b/drivers/ata/Kconfig
index 99837d932f3..3f4aa0c99ee 100644
--- a/drivers/ata/Kconfig
+++ b/drivers/ata/Kconfig
@@ -311,7 +311,7 @@ config PATA_JMICRON
config PATA_LEGACY
tristate "Legacy ISA PATA support (Experimental)"
- depends on PCI && EXPERIMENTAL
+ depends on ISA && EXPERIMENTAL
help
This option enables support for ISA/VLB bus legacy PATA
ports and allows them to be accessed via the new ATA layer.
@@ -400,6 +400,7 @@ config PATA_PDC_OLD
config PATA_QDI
tristate "QDI VLB PATA support"
+ depends on ISA
help
Support for QDI 6500 and 6580 PATA controllers on VESA local bus.
diff --git a/drivers/ata/ata_generic.c b/drivers/ata/ata_generic.c
index 1d1c30a2fcd..377425e7139 100644
--- a/drivers/ata/ata_generic.c
+++ b/drivers/ata/ata_generic.c
@@ -143,7 +143,7 @@ static struct ata_port_operations generic_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/ata_piix.c b/drivers/ata/ata_piix.c
index ab2ecccf779..5719704eb0e 100644
--- a/drivers/ata/ata_piix.c
+++ b/drivers/ata/ata_piix.c
@@ -643,11 +643,9 @@ static int piix_pata_prereset(struct ata_port *ap)
{
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no])) {
- ata_port_printk(ap, KERN_INFO, "port disabled. ignoring.\n");
- ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &piix_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -851,7 +849,7 @@ static void piix_set_piomode (struct ata_port *ap, struct ata_device *adev)
* @ap: Port whose timings we are configuring
* @adev: Drive in question
* @udma: udma mode, 0 - 6
- * @is_ich: set if the chip is an ICH device
+ * @isich: set if the chip is an ICH device
*
* Set UDMA mode for device, in host controller PCI config space.
*
diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c
index 753b0152afd..b4abd685036 100644
--- a/drivers/ata/libata-core.c
+++ b/drivers/ata/libata-core.c
@@ -5453,6 +5453,11 @@ int ata_device_add(const struct ata_probe_ent *ent)
int rc;
DPRINTK("ENTER\n");
+
+ if (ent->irq == 0) {
+ dev_printk(KERN_ERR, dev, "is not available: No interrupt assigned.\n");
+ return 0;
+ }
/* alloc a container for our list of ATA ports (buses) */
host = kzalloc(sizeof(struct ata_host) +
(ent->n_ports * sizeof(void *)), GFP_KERNEL);
diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c
index 3fa80f09f2a..02b2b2787d9 100644
--- a/drivers/ata/libata-eh.c
+++ b/drivers/ata/libata-eh.c
@@ -1515,7 +1515,11 @@ static int ata_eh_reset(struct ata_port *ap, int classify,
if (prereset) {
rc = prereset(ap);
if (rc) {
- ata_port_printk(ap, KERN_ERR,
+ if (rc == -ENOENT) {
+ ata_port_printk(ap, KERN_DEBUG, "port disabled. ignoring.\n");
+ ap->eh_context.i.action &= ~ATA_EH_RESET_MASK;
+ } else
+ ata_port_printk(ap, KERN_ERR,
"prereset failed (errno=%d)\n", rc);
return rc;
}
diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c
index 688bb55e197..08b3a407473 100644
--- a/drivers/ata/libata-sff.c
+++ b/drivers/ata/libata-sff.c
@@ -881,7 +881,7 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
probe_ent->private_data = port[0]->private_data;
if (port_mask & ATA_PORT_PRIMARY) {
- probe_ent->irq = 14;
+ probe_ent->irq = ATA_PRIMARY_IRQ;
probe_ent->port[0].cmd_addr = ATA_PRIMARY_CMD;
probe_ent->port[0].altstatus_addr =
probe_ent->port[0].ctl_addr = ATA_PRIMARY_CTL;
@@ -896,9 +896,9 @@ static struct ata_probe_ent *ata_pci_init_legacy_port(struct pci_dev *pdev,
if (port_mask & ATA_PORT_SECONDARY) {
if (probe_ent->irq)
- probe_ent->irq2 = 15;
+ probe_ent->irq2 = ATA_SECONDARY_IRQ;
else
- probe_ent->irq = 15;
+ probe_ent->irq = ATA_SECONDARY_IRQ;
probe_ent->port[1].cmd_addr = ATA_SECONDARY_CMD;
probe_ent->port[1].altstatus_addr =
probe_ent->port[1].ctl_addr = ATA_SECONDARY_CTL;
diff --git a/drivers/ata/pata_ali.c b/drivers/ata/pata_ali.c
index 8448ee6e0ee..87af3b5861a 100644
--- a/drivers/ata/pata_ali.c
+++ b/drivers/ata/pata_ali.c
@@ -369,7 +369,7 @@ static struct ata_port_operations ali_early_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -410,7 +410,7 @@ static struct ata_port_operations ali_20_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -448,7 +448,7 @@ static struct ata_port_operations ali_c2_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -485,7 +485,7 @@ static struct ata_port_operations ali_c5_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_amd.c b/drivers/ata/pata_amd.c
index 3293cf9a7eb..599ee266722 100644
--- a/drivers/ata/pata_amd.c
+++ b/drivers/ata/pata_amd.c
@@ -25,7 +25,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_amd"
-#define DRV_VERSION "0.2.3"
+#define DRV_VERSION "0.2.4"
/**
* timing_setup - shared timing computation and load
@@ -137,11 +137,8 @@ static int amd_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 ata66;
- if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
+ return -ENOENT;
pci_read_config_byte(pdev, 0x42, &ata66);
if (ata66 & bitmask[ap->port_no])
@@ -167,11 +164,9 @@ static int amd_early_pre_reset(struct ata_port *ap)
{ 0x40, 1, 0x01, 0x01 }
};
- if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &amd_enable_bits[ap->port_no]))
+ return -ENOENT;
+
/* No host side cable detection */
ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
@@ -262,12 +257,8 @@ static int nv_pre_reset(struct ata_port *ap) {
u8 ata66;
u16 udma;
- if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
-
+ if (!pci_test_config_bits(pdev, &nv_enable_bits[ap->port_no]))
+ return -ENOENT;
pci_read_config_byte(pdev, 0x52, &ata66);
if (ata66 & bitmask[ap->port_no])
@@ -368,7 +359,7 @@ static struct ata_port_operations amd33_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -402,7 +393,7 @@ static struct ata_port_operations amd66_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -436,7 +427,7 @@ static struct ata_port_operations amd100_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -470,7 +461,7 @@ static struct ata_port_operations amd133_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -504,7 +495,7 @@ static struct ata_port_operations nv100_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -538,7 +529,7 @@ static struct ata_port_operations nv133_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_artop.c b/drivers/ata/pata_artop.c
index d6ef3bf1bac..c4ccb75a4f1 100644
--- a/drivers/ata/pata_artop.c
+++ b/drivers/ata/pata_artop.c
@@ -28,7 +28,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_artop"
-#define DRV_VERSION "0.4.1"
+#define DRV_VERSION "0.4.2"
/*
* The ARTOP has 33 Mhz and "over clocked" timing tables. Until we
@@ -47,11 +47,9 @@ static int artop6210_pre_reset(struct ata_port *ap)
{ 0x4AU, 1U, 0x04UL, 0x04UL }, /* port 1 */
};
- if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -90,11 +88,9 @@ static int artop6260_pre_reset(struct ata_port *ap)
u8 tmp;
/* Odd numbered device ids are the units with enable bits (the -R cards) */
- if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (pdev->device % 1 && !pci_test_config_bits(pdev, &artop_enable_bits[ap->port_no]))
+ return -ENOENT;
+
pci_read_config_byte(pdev, 0x49, &tmp);
if (tmp & (1 >> ap->port_no))
ap->cbl = ATA_CBL_PATA40;
@@ -344,7 +340,7 @@ static const struct ata_port_operations artop6210_ops = {
.bmdma_status = ata_bmdma_status,
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -379,8 +375,6 @@ static const struct ata_port_operations artop6260_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_atiixp.c b/drivers/ata/pata_atiixp.c
index 3f78a1e54a7..6c2269b6bd3 100644
--- a/drivers/ata/pata_atiixp.c
+++ b/drivers/ata/pata_atiixp.c
@@ -22,7 +22,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_atiixp"
-#define DRV_VERSION "0.4.2"
+#define DRV_VERSION "0.4.3"
enum {
ATIIXP_IDE_PIO_TIMING = 0x40,
@@ -41,11 +41,9 @@ static int atiixp_pre_reset(struct ata_port *ap)
{ 0x48, 1, 0x08, 0x00 }
};
- if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &atiixp_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA80;
return ata_std_prereset(ap);
}
@@ -244,7 +242,7 @@ static struct ata_port_operations atiixp_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cmd64x.c b/drivers/ata/pata_cmd64x.c
index abf1bb7bd32..e92b0ef43ec 100644
--- a/drivers/ata/pata_cmd64x.c
+++ b/drivers/ata/pata_cmd64x.c
@@ -301,7 +301,7 @@ static struct ata_port_operations cmd64x_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -335,7 +335,7 @@ static struct ata_port_operations cmd646r1_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -369,7 +369,7 @@ static struct ata_port_operations cmd648_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cs5520.c b/drivers/ata/pata_cs5520.c
index 792ce482851..a6c6cebd0da 100644
--- a/drivers/ata/pata_cs5520.c
+++ b/drivers/ata/pata_cs5520.c
@@ -193,8 +193,6 @@ static struct ata_port_operations cs5520_port_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_cs5530.c b/drivers/ata/pata_cs5530.c
index f3d8a3bc1e7..7bba4d954e9 100644
--- a/drivers/ata/pata_cs5530.c
+++ b/drivers/ata/pata_cs5530.c
@@ -207,7 +207,7 @@ static struct ata_port_operations cs5530_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = cs5530_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cs5535.c b/drivers/ata/pata_cs5535.c
index 69d6b425872..d64fcdceaf0 100644
--- a/drivers/ata/pata_cs5535.c
+++ b/drivers/ata/pata_cs5535.c
@@ -211,7 +211,7 @@ static struct ata_port_operations cs5535_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_cypress.c b/drivers/ata/pata_cypress.c
index fd55474e0d1..dfa5ac53904 100644
--- a/drivers/ata/pata_cypress.c
+++ b/drivers/ata/pata_cypress.c
@@ -162,7 +162,7 @@ static struct ata_port_operations cy82c693_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_efar.c b/drivers/ata/pata_efar.c
index c30bc181304..95cd1ca181f 100644
--- a/drivers/ata/pata_efar.c
+++ b/drivers/ata/pata_efar.c
@@ -22,7 +22,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_efar"
-#define DRV_VERSION "0.4.1"
+#define DRV_VERSION "0.4.2"
/**
* efar_pre_reset - check for 40/80 pin
@@ -42,11 +42,9 @@ static int efar_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u8 tmp;
- if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &efar_enable_bits[ap->port_no]))
+ return -ENOENT;
+
pci_read_config_byte(pdev, 0x47, &tmp);
if (tmp & (2 >> ap->port_no))
ap->cbl = ATA_CBL_PATA40;
@@ -263,8 +261,6 @@ static const struct ata_port_operations efar_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_hpt366.c b/drivers/ata/pata_hpt366.c
index 94bb1dfc3f1..8c757438f35 100644
--- a/drivers/ata/pata_hpt366.c
+++ b/drivers/ata/pata_hpt366.c
@@ -360,7 +360,7 @@ static struct ata_port_operations hpt366_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -429,7 +429,7 @@ static int hpt36x_init_one(struct pci_dev *dev, const struct pci_device_id *id)
/* PCI clocking determines the ATA timing values to use */
/* info_hpt366 is safe against re-entry so we can scribble on it */
- switch(reg1 & 0x700) {
+ switch((reg1 & 0x700) >> 8) {
case 5:
info_hpt366.private_data = &hpt366_40;
break;
diff --git a/drivers/ata/pata_hpt37x.c b/drivers/ata/pata_hpt37x.c
index 532a7928f80..10318c0012e 100644
--- a/drivers/ata/pata_hpt37x.c
+++ b/drivers/ata/pata_hpt37x.c
@@ -793,7 +793,7 @@ static struct ata_port_operations hpt370_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -832,7 +832,7 @@ static struct ata_port_operations hpt370a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -872,7 +872,7 @@ static struct ata_port_operations hpt372_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -912,7 +912,7 @@ static struct ata_port_operations hpt374_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_hpt3x2n.c b/drivers/ata/pata_hpt3x2n.c
index 06c8db079b9..5c5d4f6ab90 100644
--- a/drivers/ata/pata_hpt3x2n.c
+++ b/drivers/ata/pata_hpt3x2n.c
@@ -372,7 +372,7 @@ static struct ata_port_operations hpt3x2n_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = hpt3x2n_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_hpt3x3.c b/drivers/ata/pata_hpt3x3.c
index 152770133ab..1f084ab1ccc 100644
--- a/drivers/ata/pata_hpt3x3.c
+++ b/drivers/ata/pata_hpt3x3.c
@@ -145,7 +145,7 @@ static struct ata_port_operations hpt3x3_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_isapnp.c b/drivers/ata/pata_isapnp.c
index 73948c8b727..640b8b0954f 100644
--- a/drivers/ata/pata_isapnp.c
+++ b/drivers/ata/pata_isapnp.c
@@ -52,7 +52,7 @@ static struct ata_port_operations isapnp_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_it821x.c b/drivers/ata/pata_it821x.c
index af39097d808..82a46ff4000 100644
--- a/drivers/ata/pata_it821x.c
+++ b/drivers/ata/pata_it821x.c
@@ -703,7 +703,7 @@ static struct ata_port_operations it821x_smart_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = it821x_smart_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -739,7 +739,7 @@ static struct ata_port_operations it821x_passthru_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = it821x_passthru_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_jmicron.c b/drivers/ata/pata_jmicron.c
index 6832a643a9e..be3a866b111 100644
--- a/drivers/ata/pata_jmicron.c
+++ b/drivers/ata/pata_jmicron.c
@@ -51,7 +51,7 @@ static int jmicron_pre_reset(struct ata_port *ap)
/* Check if our port is enabled */
pci_read_config_dword(pdev, 0x40, &control);
if ((control & port_mask) == 0)
- return 0;
+ return -ENOENT;
/* There are two basic mappings. One has the two SATA ports merged
as master/slave and the secondary as PATA, the other has only the
@@ -164,8 +164,7 @@ static const struct ata_port_operations jmicron_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- /* Timeout handling. Special recovery hooks here */
- .eng_timeout = ata_eng_timeout,
+ /* IRQ-related hooks */
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_legacy.c b/drivers/ata/pata_legacy.c
index ad37c220bb2..10231ef731d 100644
--- a/drivers/ata/pata_legacy.c
+++ b/drivers/ata/pata_legacy.c
@@ -161,7 +161,7 @@ static struct ata_port_operations simple_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
@@ -186,7 +186,7 @@ static struct ata_port_operations legacy_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
@@ -296,7 +296,7 @@ static struct ata_port_operations pdc20230_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = pdc_data_xfer_vlb,
.irq_handler = ata_interrupt,
@@ -348,7 +348,7 @@ static struct ata_port_operations ht6560a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer, /* Check vlb/noirq */
.irq_handler = ata_interrupt,
@@ -411,7 +411,7 @@ static struct ata_port_operations ht6560b_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer, /* FIXME: Check 32bit and noirq */
.irq_handler = ata_interrupt,
@@ -529,7 +529,7 @@ static struct ata_port_operations opti82c611a_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -659,7 +659,7 @@ static struct ata_port_operations opti82c46x_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = opti82c46x_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_mpiix.c b/drivers/ata/pata_mpiix.c
index 1958c4ed09a..3c65393c1f0 100644
--- a/drivers/ata/pata_mpiix.c
+++ b/drivers/ata/pata_mpiix.c
@@ -18,7 +18,7 @@
* The driver conciously keeps this logic internally to avoid pushing quirky
* PATA history into the clean libata layer.
*
- * Thinkpad specific note: If you boot an MPIIX using thinkpad with a PCMCIA
+ * Thinkpad specific note: If you boot an MPIIX using a thinkpad with a PCMCIA
* hard disk present this driver will not detect it. This is not a bug. In this
* configuration the secondary port of the MPIIX is disabled and the addresses
* are decoded by the PCMCIA bridge and therefore are for a generic IDE driver
@@ -35,7 +35,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_mpiix"
-#define DRV_VERSION "0.7.1"
+#define DRV_VERSION "0.7.2"
enum {
IDETIM = 0x6C, /* IDE control register */
@@ -54,11 +54,8 @@ static int mpiix_pre_reset(struct ata_port *ap)
{ 0x6F, 1, 0x80, 0x80 }
};
- if (!pci_test_config_bits(pdev, &mpiix_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &mpiix_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_netcell.c b/drivers/ata/pata_netcell.c
index 16cb254cb97..76eb9c90bee 100644
--- a/drivers/ata/pata_netcell.c
+++ b/drivers/ata/pata_netcell.c
@@ -90,8 +90,7 @@ static const struct ata_port_operations netcell_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- /* Timeout handling. Special recovery hooks here */
- .eng_timeout = ata_eng_timeout,
+ /* IRQ-related hooks */
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_ns87410.c b/drivers/ata/pata_ns87410.c
index 93d6646d295..2005a95f48f 100644
--- a/drivers/ata/pata_ns87410.c
+++ b/drivers/ata/pata_ns87410.c
@@ -45,11 +45,8 @@ static int ns87410_pre_reset(struct ata_port *ap)
{ 0x47, 1, 0x08, 0x08 }
};
- if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &ns87410_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -179,7 +176,7 @@ static struct ata_port_operations ns87410_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ns87410_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_oldpiix.c b/drivers/ata/pata_oldpiix.c
index 04c618a2664..31a285ca88d 100644
--- a/drivers/ata/pata_oldpiix.c
+++ b/drivers/ata/pata_oldpiix.c
@@ -25,7 +25,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_oldpiix"
-#define DRV_VERSION "0.5.1"
+#define DRV_VERSION "0.5.2"
/**
* oldpiix_pre_reset - probe begin
@@ -42,11 +42,8 @@ static int oldpiix_pre_reset(struct ata_port *ap)
{ 0x43U, 1U, 0x80UL, 0x80UL }, /* port 1 */
};
- if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &oldpiix_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_opti.c b/drivers/ata/pata_opti.c
index c3d01325e0e..57fe21f3a97 100644
--- a/drivers/ata/pata_opti.c
+++ b/drivers/ata/pata_opti.c
@@ -34,7 +34,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_opti"
-#define DRV_VERSION "0.2.4"
+#define DRV_VERSION "0.2.5"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -59,11 +59,9 @@ static int opti_pre_reset(struct ata_port *ap)
{ 0x40, 1, 0x08, 0x00 }
};
- if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &opti_enable_bits[ap->port_no]))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -229,7 +227,7 @@ static struct ata_port_operations opti_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_optidma.c b/drivers/ata/pata_optidma.c
index 177a455f425..7296a20cd10 100644
--- a/drivers/ata/pata_optidma.c
+++ b/drivers/ata/pata_optidma.c
@@ -33,7 +33,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_optidma"
-#define DRV_VERSION "0.2.1"
+#define DRV_VERSION "0.2.2"
enum {
READ_REG = 0, /* index of Read cycle timing register */
@@ -59,11 +59,9 @@ static int optidma_pre_reset(struct ata_port *ap)
0x40, 1, 0x08, 0x00
};
- if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits)) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (ap->port_no && !pci_test_config_bits(pdev, &optidma_enable_bits))
+ return -ENOENT;
+
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -388,7 +386,7 @@ static struct ata_port_operations optidma_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -423,7 +421,7 @@ static struct ata_port_operations optiplus_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_pcmcia.c b/drivers/ata/pata_pcmcia.c
index 62b25cda409..cb501e145a4 100644
--- a/drivers/ata/pata_pcmcia.c
+++ b/drivers/ata/pata_pcmcia.c
@@ -87,7 +87,7 @@ static struct ata_port_operations pcmcia_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_pdc2027x.c b/drivers/ata/pata_pdc2027x.c
index 31ab9c88620..bd4ed6734ed 100644
--- a/drivers/ata/pata_pdc2027x.c
+++ b/drivers/ata/pata_pdc2027x.c
@@ -36,7 +36,7 @@
#include <asm/io.h>
#define DRV_NAME "pata_pdc2027x"
-#define DRV_VERSION "0.74-ac3"
+#define DRV_VERSION "0.74-ac5"
#undef PDC_DEBUG
#ifdef PDC_DEBUG
@@ -311,10 +311,8 @@ static inline int pdc2027x_port_enabled(struct ata_port *ap)
static int pdc2027x_prereset(struct ata_port *ap)
{
/* Check whether port enabled */
- if (!pdc2027x_port_enabled(ap)) {
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pdc2027x_port_enabled(ap))
+ return -ENOENT;
pdc2027x_cbl_detect(ap);
return ata_std_prereset(ap);
}
diff --git a/drivers/ata/pata_qdi.c b/drivers/ata/pata_qdi.c
index 35cfdf0ac3f..7977f471d5e 100644
--- a/drivers/ata/pata_qdi.c
+++ b/drivers/ata/pata_qdi.c
@@ -184,7 +184,7 @@ static struct ata_port_operations qdi6500_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = qdi_data_xfer,
.irq_handler = ata_interrupt,
@@ -212,7 +212,7 @@ static struct ata_port_operations qdi6580_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = qdi_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = qdi_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_radisys.c b/drivers/ata/pata_radisys.c
index 277f8411b52..c20bcf43ed6 100644
--- a/drivers/ata/pata_radisys.c
+++ b/drivers/ata/pata_radisys.c
@@ -255,8 +255,6 @@ static const struct ata_port_operations radisys_pata_ops = {
.qc_issue = radisys_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_rz1000.c b/drivers/ata/pata_rz1000.c
index 3c6d84fd431..eccc6fd4503 100644
--- a/drivers/ata/pata_rz1000.c
+++ b/drivers/ata/pata_rz1000.c
@@ -112,7 +112,7 @@ static struct ata_port_operations rz1000_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.freeze = ata_bmdma_freeze,
diff --git a/drivers/ata/pata_sc1200.c b/drivers/ata/pata_sc1200.c
index 4166c1a8a9e..107e6cd3dc0 100644
--- a/drivers/ata/pata_sc1200.c
+++ b/drivers/ata/pata_sc1200.c
@@ -217,7 +217,7 @@ static struct ata_port_operations sc1200_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = sc1200_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_serverworks.c b/drivers/ata/pata_serverworks.c
index af456113c55..a5c8d7e121d 100644
--- a/drivers/ata/pata_serverworks.c
+++ b/drivers/ata/pata_serverworks.c
@@ -41,7 +41,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_serverworks"
-#define DRV_VERSION "0.3.6"
+#define DRV_VERSION "0.3.7"
#define SVWKS_CSB5_REVISION_NEW 0x92 /* min PCI_REVISION_ID for UDMA5 (A2.0) */
#define SVWKS_CSB6_REVISION 0xa0 /* min PCI_REVISION_ID for UDMA4 (A1.0) */
@@ -128,7 +128,7 @@ static struct sv_cable_table cable_detect[] = {
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_DELL, dell_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_VENDOR_ID_DELL, dell_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_VENDOR_ID_SUN, sun_cable },
- { PCI_DEVICE_ID_SERVERWORKS_OSB4, PCI_ANY_ID, osb4_cable },
+ { PCI_DEVICE_ID_SERVERWORKS_OSB4IDE, PCI_ANY_ID, osb4_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB5IDE, PCI_ANY_ID, csb_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE, PCI_ANY_ID, csb_cable },
{ PCI_DEVICE_ID_SERVERWORKS_CSB6IDE2, PCI_ANY_ID, csb_cable },
@@ -352,10 +352,12 @@ static struct ata_port_operations serverworks_osb4_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = ata_host_stop
@@ -385,10 +387,12 @@ static struct ata_port_operations serverworks_csb_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
+ .irq_clear = ata_bmdma_irq_clear,
+
.port_start = ata_port_start,
.port_stop = ata_port_stop,
.host_stop = ata_host_stop
diff --git a/drivers/ata/pata_sil680.c b/drivers/ata/pata_sil680.c
index 8f7db9638d0..c8b2e26db70 100644
--- a/drivers/ata/pata_sil680.c
+++ b/drivers/ata/pata_sil680.c
@@ -251,7 +251,7 @@ static struct ata_port_operations sil680_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_sis.c b/drivers/ata/pata_sis.c
index 2e555168b43..17791e2785f 100644
--- a/drivers/ata/pata_sis.c
+++ b/drivers/ata/pata_sis.c
@@ -34,7 +34,7 @@
#include <linux/ata.h>
#define DRV_NAME "pata_sis"
-#define DRV_VERSION "0.4.3"
+#define DRV_VERSION "0.4.4"
struct sis_chipset {
u16 device; /* PCI host ID */
@@ -74,11 +74,9 @@ static int sis_133_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
u16 tmp;
- if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &sis_enable_bits[ap->port_no]))
+ return -ENOENT;
+
/* The top bit of this register is the cable detect bit */
pci_read_config_word(pdev, 0x50 + 2 * ap->port_no, &tmp);
if (tmp & 0x8000)
@@ -575,8 +573,6 @@ static const struct ata_port_operations sis_133_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -610,8 +606,6 @@ static const struct ata_port_operations sis_133_early_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -646,8 +640,6 @@ static const struct ata_port_operations sis_100_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -681,8 +673,6 @@ static const struct ata_port_operations sis_66_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
@@ -716,8 +706,6 @@ static const struct ata_port_operations sis_old_ops = {
.qc_issue = ata_qc_issue_prot,
.data_xfer = ata_pio_data_xfer,
- .eng_timeout = ata_eng_timeout,
-
.irq_handler = ata_interrupt,
.irq_clear = ata_bmdma_irq_clear,
diff --git a/drivers/ata/pata_sl82c105.c b/drivers/ata/pata_sl82c105.c
index f8499786917..5b762acc568 100644
--- a/drivers/ata/pata_sl82c105.c
+++ b/drivers/ata/pata_sl82c105.c
@@ -19,7 +19,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_sl82c105"
-#define DRV_VERSION "0.2.2"
+#define DRV_VERSION "0.2.3"
enum {
/*
@@ -49,11 +49,8 @@ static int sl82c105_pre_reset(struct ata_port *ap)
};
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- dev_printk(KERN_INFO, &pdev->dev, "port disabled. ignoring.\n");
- return 0;
- }
+ if (ap->port_no && !pci_test_config_bits(pdev, &sl82c105_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -264,7 +261,7 @@ static struct ata_port_operations sl82c105_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_triflex.c b/drivers/ata/pata_triflex.c
index 36f788728f3..a954ed93a40 100644
--- a/drivers/ata/pata_triflex.c
+++ b/drivers/ata/pata_triflex.c
@@ -46,13 +46,13 @@
#define DRV_VERSION "0.2.5"
/**
- * triflex_probe_init - probe begin
+ * triflex_prereset - probe begin
* @ap: ATA port
*
* Set up cable type and use generic probe init
*/
-static int triflex_probe_init(struct ata_port *ap)
+static int triflex_prereset(struct ata_port *ap)
{
static const struct pci_bits triflex_enable_bits[] = {
{ 0x80, 1, 0x01, 0x01 },
@@ -61,11 +61,8 @@ static int triflex_probe_init(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &triflex_enable_bits[ap->port_no]))
+ return -ENOENT;
ap->cbl = ATA_CBL_PATA40;
return ata_std_prereset(ap);
}
@@ -74,7 +71,7 @@ static int triflex_probe_init(struct ata_port *ap)
static void triflex_error_handler(struct ata_port *ap)
{
- ata_bmdma_drive_eh(ap, triflex_probe_init, ata_std_softreset, NULL, ata_std_postreset);
+ ata_bmdma_drive_eh(ap, triflex_prereset, ata_std_softreset, NULL, ata_std_postreset);
}
/**
@@ -221,7 +218,7 @@ static struct ata_port_operations triflex_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/pata_via.c b/drivers/ata/pata_via.c
index 1b2ff133b16..7b5dd2343b9 100644
--- a/drivers/ata/pata_via.c
+++ b/drivers/ata/pata_via.c
@@ -60,7 +60,7 @@
#include <linux/libata.h>
#define DRV_NAME "pata_via"
-#define DRV_VERSION "0.1.13"
+#define DRV_VERSION "0.1.14"
/*
* The following comes directly from Vojtech Pavlik's ide/pci/via82cxxx
@@ -155,11 +155,8 @@ static int via_pre_reset(struct ata_port *ap)
struct pci_dev *pdev = to_pci_dev(ap->host->dev);
- if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no])) {
- ata_port_disable(ap);
- printk(KERN_INFO "ata%u: port disabled. ignoring.\n", ap->id);
- return 0;
- }
+ if (!pci_test_config_bits(pdev, &via_enable_bits[ap->port_no]))
+ return -ENOENT;
}
if ((config->flags & VIA_UDMA) >= VIA_UDMA_66)
@@ -325,7 +322,7 @@ static struct ata_port_operations via_port_ops = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer,
.irq_handler = ata_interrupt,
@@ -360,7 +357,7 @@ static struct ata_port_operations via_port_ops_noirq = {
.qc_prep = ata_qc_prep,
.qc_issue = ata_qc_issue_prot,
- .eng_timeout = ata_eng_timeout,
+
.data_xfer = ata_pio_data_xfer_noirq,
.irq_handler = ata_interrupt,
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index fdce6e07ecd..c01496df4a9 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -463,6 +463,7 @@ static const struct ata_port_operations mv_iie_ops = {
.qc_prep = mv_qc_prep_iie,
.qc_issue = mv_qc_issue,
+ .data_xfer = ata_mmio_data_xfer,
.eng_timeout = mv_eng_timeout,
diff --git a/drivers/ata/sata_nv.c b/drivers/ata/sata_nv.c
index 27c22feebf3..8cd730fe5dd 100644
--- a/drivers/ata/sata_nv.c
+++ b/drivers/ata/sata_nv.c
@@ -484,7 +484,7 @@ static void nv_error_handler(struct ata_port *ap)
static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version = 0;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
struct ata_probe_ent *probe_ent;
int pci_dev_busy = 0;
int rc;
@@ -520,8 +520,8 @@ static int nv_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
rc = -ENOMEM;
- ppi = &nv_port_info[ent->driver_data];
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &nv_port_info[ent->driver_data];
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
goto err_out_regions;
diff --git a/drivers/ata/sata_sis.c b/drivers/ata/sata_sis.c
index 9b17375d805..18d49fff8dc 100644
--- a/drivers/ata/sata_sis.c
+++ b/drivers/ata/sata_sis.c
@@ -240,7 +240,7 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
struct ata_probe_ent *probe_ent = NULL;
int rc;
u32 genctl;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
int pci_dev_busy = 0;
u8 pmr;
u8 port2_start;
@@ -265,8 +265,8 @@ static int sis_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
goto err_out_regions;
- ppi = &sis_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &sis_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent) {
rc = -ENOMEM;
goto err_out_regions;
diff --git a/drivers/ata/sata_uli.c b/drivers/ata/sata_uli.c
index 8fc6e800011..dd76f37be18 100644
--- a/drivers/ata/sata_uli.c
+++ b/drivers/ata/sata_uli.c
@@ -185,7 +185,7 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
{
static int printed_version;
struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi;
+ struct ata_port_info *ppi[2];
int rc;
unsigned int board_idx = (unsigned int) ent->driver_data;
int pci_dev_busy = 0;
@@ -211,8 +211,8 @@ static int uli_init_one (struct pci_dev *pdev, const struct pci_device_id *ent)
if (rc)
goto err_out_regions;
- ppi = &uli_port_info;
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ ppi[0] = ppi[1] = &uli_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent) {
rc = -ENOMEM;
goto err_out_regions;
diff --git a/drivers/ata/sata_via.c b/drivers/ata/sata_via.c
index 7f087aef99d..a72a2389a11 100644
--- a/drivers/ata/sata_via.c
+++ b/drivers/ata/sata_via.c
@@ -318,9 +318,10 @@ static void vt6421_init_addrs(struct ata_probe_ent *probe_ent,
static struct ata_probe_ent *vt6420_init_probe_ent(struct pci_dev *pdev)
{
struct ata_probe_ent *probe_ent;
- struct ata_port_info *ppi = &vt6420_port_info;
-
- probe_ent = ata_pci_init_native_mode(pdev, &ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
+ struct ata_port_info *ppi[2];
+
+ ppi[0] = ppi[1] = &vt6420_port_info;
+ probe_ent = ata_pci_init_native_mode(pdev, ppi, ATA_PORT_PRIMARY | ATA_PORT_SECONDARY);
if (!probe_ent)
return NULL;
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index b539e5e75b5..7bbb9eeda23 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -8,7 +8,7 @@ obj-y += power/
obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
-obj-$(CONFIG_MEMORY_HOTPLUG) += memory.o
+obj-$(CONFIG_MEMORY_HOTPLUG_SPARSE) += memory.o
obj-$(CONFIG_SMP) += topology.o
obj-$(CONFIG_SYS_HYPERVISOR) += hypervisor.o
diff --git a/drivers/base/base.h b/drivers/base/base.h
index c3b8dc98b8a..d26644a5953 100644
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -16,7 +16,7 @@ extern int cpu_dev_init(void);
extern int attribute_container_init(void);
extern int bus_add_device(struct device * dev);
-extern void bus_attach_device(struct device * dev);
+extern int bus_attach_device(struct device * dev);
extern void bus_remove_device(struct device * dev);
extern struct bus_type *get_bus(struct bus_type * bus);
extern void put_bus(struct bus_type * bus);
diff --git a/drivers/base/bus.c b/drivers/base/bus.c
index 2e954d07175..12173d16bea 100644
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -371,12 +371,20 @@ int bus_add_device(struct device * dev)
if (bus) {
pr_debug("bus %s: add device %s\n", bus->name, dev->bus_id);
error = device_add_attrs(bus, dev);
- if (!error) {
- sysfs_create_link(&bus->devices.kobj, &dev->kobj, dev->bus_id);
- sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "subsystem");
- sysfs_create_link(&dev->kobj, &dev->bus->subsys.kset.kobj, "bus");
- }
+ if (error)
+ goto out;
+ error = sysfs_create_link(&bus->devices.kobj,
+ &dev->kobj, dev->bus_id);
+ if (error)
+ goto out;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->subsys.kset.kobj, "subsystem");
+ if (error)
+ goto out;
+ error = sysfs_create_link(&dev->kobj,
+ &dev->bus->subsys.kset.kobj, "bus");
}
+out:
return error;
}
@@ -384,16 +392,24 @@ int bus_add_device(struct device * dev)
* bus_attach_device - add device to bus
* @dev: device tried to attach to a driver
*
+ * - Add device to bus's list of devices.
* - Try to attach to driver.
*/
-void bus_attach_device(struct device * dev)
+int bus_attach_device(struct device * dev)
{
- struct bus_type * bus = dev->bus;
+ struct bus_type *bus = dev->bus;
+ int ret = 0;
if (bus) {
- device_attach(dev);
- klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ dev->is_registered = 1;
+ ret = device_attach(dev);
+ if (ret >= 0) {
+ klist_add_tail(&dev->knode_bus, &bus->klist_devices);
+ ret = 0;
+ } else
+ dev->is_registered = 0;
}
+ return ret;
}
/**
@@ -412,7 +428,8 @@ void bus_remove_device(struct device * dev)
sysfs_remove_link(&dev->kobj, "bus");
sysfs_remove_link(&dev->bus->devices.kobj, dev->bus_id);
device_remove_attrs(dev->bus, dev);
- klist_remove(&dev->knode_bus);
+ dev->is_registered = 0;
+ klist_del(&dev->knode_bus);
pr_debug("bus %s: remove device %s\n", dev->bus->name, dev->bus_id);
device_release_driver(dev);
put_bus(dev->bus);
@@ -455,10 +472,17 @@ static void driver_remove_attrs(struct bus_type * bus, struct device_driver * dr
* Thanks to drivers making their tables __devinit, we can't allow manual
* bind and unbind from userspace unless CONFIG_HOTPLUG is enabled.
*/
-static void add_bind_files(struct device_driver *drv)
+static int __must_check add_bind_files(struct device_driver *drv)
{
- driver_create_file(drv, &driver_attr_unbind);
- driver_create_file(drv, &driver_attr_bind);
+ int ret;
+
+ ret = driver_create_file(drv, &driver_attr_unbind);
+ if (ret == 0) {
+ ret = driver_create_file(drv, &driver_attr_bind);
+ if (ret)
+ driver_remove_file(drv, &driver_attr_unbind);
+ }
+ return ret;
}
static void remove_bind_files(struct device_driver *drv)
@@ -467,7 +491,7 @@ static void remove_bind_files(struct device_driver *drv)
driver_remove_file(drv, &driver_attr_unbind);
}
#else
-static inline void add_bind_files(struct device_driver *drv) {}
+static inline int add_bind_files(struct device_driver *drv) { return 0; }
static inline void remove_bind_files(struct device_driver *drv) {}
#endif
@@ -476,7 +500,7 @@ static inline void remove_bind_files(struct device_driver *drv) {}
* @drv: driver.
*
*/
-int bus_add_driver(struct device_driver * drv)
+int bus_add_driver(struct device_driver *drv)
{
struct bus_type * bus = get_bus(drv->bus);
int error = 0;
@@ -484,27 +508,39 @@ int bus_add_driver(struct device_driver * drv)
if (bus) {
pr_debug("bus %s: add driver %s\n", bus->name, drv->name);
error = kobject_set_name(&drv->kobj, "%s", drv->name);
- if (error) {
- put_bus(bus);
- return error;
- }
+ if (error)
+ goto out_put_bus;
drv->kobj.kset = &bus->drivers;
- if ((error = kobject_register(&drv->kobj))) {
- put_bus(bus);
- return error;
- }
+ if ((error = kobject_register(&drv->kobj)))
+ goto out_put_bus;
- driver_attach(drv);
+ error = driver_attach(drv);
+ if (error)
+ goto out_unregister;
klist_add_tail(&drv->knode_bus, &bus->klist_drivers);
module_add_driver(drv->owner, drv);
- driver_add_attrs(bus, drv);
- add_bind_files(drv);
+ error = driver_add_attrs(bus, drv);
+ if (error) {
+ /* How the hell do we get out of this pickle? Give up */
+ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n",
+ __FUNCTION__, drv->name);
+ }
+ error = add_bind_files(drv);
+ if (error) {
+ /* Ditto */
+ printk(KERN_ERR "%s: add_bind_files(%s) failed\n",
+ __FUNCTION__, drv->name);
+ }
}
return error;
+out_unregister:
+ kobject_unregister(&drv->kobj);
+out_put_bus:
+ put_bus(bus);
+ return error;
}
-
/**
* bus_remove_driver - delete driver from bus's knowledge.
* @drv: driver.
@@ -530,16 +566,21 @@ void bus_remove_driver(struct device_driver * drv)
/* Helper for bus_rescan_devices's iter */
-static int bus_rescan_devices_helper(struct device *dev, void *data)
+static int __must_check bus_rescan_devices_helper(struct device *dev,
+ void *data)
{
+ int ret = 0;
+
if (!dev->driver) {
if (dev->parent) /* Needed for USB */
down(&dev->parent->sem);
- device_attach(dev);
+ ret = device_attach(dev);
if (dev->parent)
up(&dev->parent->sem);
+ if (ret > 0)
+ ret = 0;
}
- return 0;
+ return ret < 0 ? ret : 0;
}
/**
@@ -550,9 +591,9 @@ static int bus_rescan_devices_helper(struct device *dev, void *data)
* attached and rescan it against existing drivers to see if it matches
* any by calling device_attach() for the unbound devices.
*/
-void bus_rescan_devices(struct bus_type * bus)
+int bus_rescan_devices(struct bus_type * bus)
{
- bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
+ return bus_for_each_dev(bus, NULL, NULL, bus_rescan_devices_helper);
}
/**
@@ -564,7 +605,7 @@ void bus_rescan_devices(struct bus_type * bus)
* to use if probing criteria changed during a devices lifetime and
* driver attachment should change accordingly.
*/
-void device_reprobe(struct device *dev)
+int device_reprobe(struct device *dev)
{
if (dev->driver) {
if (dev->parent) /* Needed for USB */
@@ -573,14 +614,14 @@ void device_reprobe(struct device *dev)
if (dev->parent)
up(&dev->parent->sem);
}
-
- bus_rescan_devices_helper(dev, NULL);
+ return bus_rescan_devices_helper(dev, NULL);
}
EXPORT_SYMBOL_GPL(device_reprobe);
-struct bus_type * get_bus(struct bus_type * bus)
+struct bus_type *get_bus(struct bus_type *bus)
{
- return bus ? container_of(subsys_get(&bus->subsys), struct bus_type, subsys) : NULL;
+ return bus ? container_of(subsys_get(&bus->subsys),
+ struct bus_type, subsys) : NULL;
}
void put_bus(struct bus_type * bus)
@@ -655,22 +696,6 @@ static void klist_devices_put(struct klist_node *n)
put_device(dev);
}
-static void klist_drivers_get(struct klist_node *n)
-{
- struct device_driver *drv = container_of(n, struct device_driver,
- knode_bus);
-
- get_driver(drv);
-}
-
-static void klist_drivers_put(struct klist_node *n)
-{
- struct device_driver *drv = container_of(n, struct device_driver,
- knode_bus);
-
- put_driver(drv);
-}
-
/**
* bus_register - register a bus with the system.
* @bus: bus.
@@ -706,7 +731,7 @@ int bus_register(struct bus_type * bus)
goto bus_drivers_fail;
klist_init(&bus->klist_devices, klist_devices_get, klist_devices_put);
- klist_init(&bus->klist_drivers, klist_drivers_get, klist_drivers_put);
+ klist_init(&bus->klist_drivers, NULL, NULL);
bus_add_attrs(bus);
pr_debug("bus type '%s' registered\n", bus->name);
diff --git a/drivers/base/class.c b/drivers/base/class.c
index de8908320f2..b32b77ff2dc 100644
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -19,6 +19,8 @@
#include <linux/slab.h>
#include "base.h"
+extern struct subsystem devices_subsys;
+
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
@@ -197,7 +199,7 @@ static int class_device_create_uevent(struct class_device *class_dev,
* Note, the pointer created here is to be destroyed when finished by
* making a call to class_destroy().
*/
-struct class *class_create(struct module *owner, char *name)
+struct class *class_create(struct module *owner, const char *name)
{
struct class *cls;
int retval;
@@ -226,7 +228,7 @@ error:
/**
* class_destroy - destroys a struct class structure
- * @cs: pointer to the struct class that is to be destroyed
+ * @cls: pointer to the struct class that is to be destroyed
*
* Note, the pointer to be destroyed must have been created with a call
* to class_create().
@@ -361,7 +363,7 @@ static int class_uevent(struct kset *kset, struct kobject *kobj, char **envp,
pr_debug("%s - name = %s\n", __FUNCTION__, class_dev->class_id);
if (class_dev->dev) {
- /* add physical device, backing this device */
+ /* add device, backing this class device (deprecated) */
struct device *dev = class_dev->dev;
char *path = kobject_get_path(&dev->kobj, GFP_KERNEL);
@@ -656,9 +658,9 @@ int class_device_register(struct class_device *class_dev)
/**
* class_device_create - creates a class device and registers it with sysfs
- * @cs: pointer to the struct class that this device should be registered to.
+ * @cls: pointer to the struct class that this device should be registered to.
* @parent: pointer to the parent struct class_device of this new device, if any.
- * @dev: the dev_t for the char device to be added.
+ * @devt: the dev_t for the char device to be added.
* @device: a pointer to a struct device that is assiociated with this class device.
* @fmt: string for the class device's name
*
@@ -679,7 +681,8 @@ int class_device_register(struct class_device *class_dev)
struct class_device *class_device_create(struct class *cls,
struct class_device *parent,
dev_t devt,
- struct device *device, char *fmt, ...)
+ struct device *device,
+ const char *fmt, ...)
{
va_list args;
struct class_device *class_dev = NULL;
@@ -763,7 +766,7 @@ void class_device_unregister(struct class_device *class_dev)
/**
* class_device_destroy - removes a class device that was created with class_device_create()
* @cls: the pointer to the struct class that this device was registered * with.
- * @dev: the dev_t of the device that was previously registered.
+ * @devt: the dev_t of the device that was previously registered.
*
* This call unregisters and cleans up a class device that was created with a
* call to class_device_create()
@@ -839,6 +842,7 @@ int class_interface_register(struct class_interface *class_intf)
{
struct class *parent;
struct class_device *class_dev;
+ struct device *dev;
if (!class_intf || !class_intf->class)
return -ENODEV;
@@ -853,6 +857,10 @@ int class_interface_register(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
class_intf->add(class_dev, class_intf);
}
+ if (class_intf->add_dev) {
+ list_for_each_entry(dev, &parent->devices, node)
+ class_intf->add_dev(dev, class_intf);
+ }
up(&parent->sem);
return 0;
@@ -862,6 +870,7 @@ void class_interface_unregister(struct class_interface *class_intf)
{
struct class * parent = class_intf->class;
struct class_device *class_dev;
+ struct device *dev;
if (!parent)
return;
@@ -872,12 +881,31 @@ void class_interface_unregister(struct class_interface *class_intf)
list_for_each_entry(class_dev, &parent->children, node)
class_intf->remove(class_dev, class_intf);
}
+ if (class_intf->remove_dev) {
+ list_for_each_entry(dev, &parent->devices, node)
+ class_intf->remove_dev(dev, class_intf);
+ }
up(&parent->sem);
class_put(parent);
}
+int virtual_device_parent(struct device *dev)
+{
+ if (!dev->class)
+ return -ENODEV;
+
+ if (!dev->class->virtual_dir) {
+ static struct kobject *virtual_dir = NULL;
+
+ if (!virtual_dir)
+ virtual_dir = kobject_add_dir(&devices_subsys.kset.kobj, "virtual");
+ dev->class->virtual_dir = kobject_add_dir(virtual_dir, dev->class->name);
+ }
+ dev->kobj.parent = dev->class->virtual_dir;
+ return 0;
+}
int __init classes_init(void)
{
diff --git a/drivers/base/core.c b/drivers/base/core.c
index be6b5bc0677..b224bb43ff6 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -3,6 +3,8 @@
*
* Copyright (c) 2002-3 Patrick Mochel
* Copyright (c) 2002-3 Open Source Development Labs
+ * Copyright (c) 2006 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (c) 2006 Novell, Inc.
*
* This file is released under the GPLv2
*
@@ -92,6 +94,8 @@ static void device_release(struct kobject * kobj)
if (dev->release)
dev->release(dev);
+ else if (dev->class && dev->class->dev_release)
+ dev->class->dev_release(dev);
else {
printk(KERN_ERR "Device '%s' does not have a release() function, "
"it is broken and must be fixed.\n",
@@ -149,17 +153,21 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
"MINOR=%u", MINOR(dev->devt));
}
- /* add bus name of physical device */
+ /* add bus name (same as SUBSYSTEM, deprecated) */
if (dev->bus)
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVBUS=%s", dev->bus->name);
- /* add driver name of physical device */
- if (dev->driver)
+ /* add driver name (PHYSDEV* values are deprecated)*/
+ if (dev->driver) {
+ add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DRIVER=%s", dev->driver->name);
add_uevent_var(envp, num_envp, &i,
buffer, buffer_size, &length,
"PHYSDEVDRIVER=%s", dev->driver->name);
+ }
/* terminate, set to next free slot, shrink available space */
envp[i] = NULL;
@@ -177,6 +185,15 @@ static int dev_uevent(struct kset *kset, struct kobject *kobj, char **envp,
}
}
+ if (dev->class && dev->class->dev_uevent) {
+ /* have the class specific function add its stuff */
+ retval = dev->class->dev_uevent(dev, envp, num_envp, buffer, buffer_size);
+ if (retval) {
+ pr_debug("%s - dev_uevent() returned %d\n",
+ __FUNCTION__, retval);
+ }
+ }
+
return retval;
}
@@ -193,6 +210,72 @@ static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
return count;
}
+static int device_add_groups(struct device *dev)
+{
+ int i;
+ int error = 0;
+
+ if (dev->groups) {
+ for (i = 0; dev->groups[i]; i++) {
+ error = sysfs_create_group(&dev->kobj, dev->groups[i]);
+ if (error) {
+ while (--i >= 0)
+ sysfs_remove_group(&dev->kobj, dev->groups[i]);
+ goto out;
+ }
+ }
+ }
+out:
+ return error;
+}
+
+static void device_remove_groups(struct device *dev)
+{
+ int i;
+ if (dev->groups) {
+ for (i = 0; dev->groups[i]; i++) {
+ sysfs_remove_group(&dev->kobj, dev->groups[i]);
+ }
+ }
+}
+
+static int device_add_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ int error = 0;
+ int i;
+
+ if (!class)
+ return 0;
+
+ if (class->dev_attrs) {
+ for (i = 0; attr_name(class->dev_attrs[i]); i++) {
+ error = device_create_file(dev, &class->dev_attrs[i]);
+ if (error)
+ break;
+ }
+ }
+ if (error)
+ while (--i >= 0)
+ device_remove_file(dev, &class->dev_attrs[i]);
+ return error;
+}
+
+static void device_remove_attrs(struct device *dev)
+{
+ struct class *class = dev->class;
+ int i;
+
+ if (!class)
+ return;
+
+ if (class->dev_attrs) {
+ for (i = 0; attr_name(class->dev_attrs[i]); i++)
+ device_remove_file(dev, &class->dev_attrs[i]);
+ }
+}
+
+
static ssize_t show_dev(struct device *dev, struct device_attribute *attr,
char *buf)
{
@@ -236,6 +319,32 @@ void device_remove_file(struct device * dev, struct device_attribute * attr)
}
}
+/**
+ * device_create_bin_file - create sysfs binary attribute file for device.
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+int device_create_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ int error = -EINVAL;
+ if (dev)
+ error = sysfs_create_bin_file(&dev->kobj, attr);
+ return error;
+}
+EXPORT_SYMBOL_GPL(device_create_bin_file);
+
+/**
+ * device_remove_bin_file - remove sysfs binary attribute file
+ * @dev: device.
+ * @attr: device binary attribute descriptor.
+ */
+void device_remove_bin_file(struct device *dev, struct bin_attribute *attr)
+{
+ if (dev)
+ sysfs_remove_bin_file(&dev->kobj, attr);
+}
+EXPORT_SYMBOL_GPL(device_remove_bin_file);
+
static void klist_children_get(struct klist_node *n)
{
struct device *dev = container_of(n, struct device, knode_parent);
@@ -289,12 +398,20 @@ int device_add(struct device *dev)
{
struct device *parent = NULL;
char *class_name = NULL;
+ struct class_interface *class_intf;
int error = -EINVAL;
dev = get_device(dev);
if (!dev || !strlen(dev->bus_id))
goto Error;
+ /* if this is a class device, and has no parent, create one */
+ if ((dev->class) && (dev->parent == NULL)) {
+ error = virtual_device_parent(dev);
+ if (error)
+ goto Error;
+ }
+
parent = get_device(dev->parent);
pr_debug("DEV: registering device: ID = '%s'\n", dev->bus_id);
@@ -307,6 +424,10 @@ int device_add(struct device *dev)
if ((error = kobject_add(&dev->kobj)))
goto Error;
+ /* notify platform of device entry */
+ if (platform_notify)
+ platform_notify(dev);
+
dev->uevent_attr.attr.name = "uevent";
dev->uevent_attr.attr.mode = S_IWUSR;
if (dev->driver)
@@ -340,12 +461,17 @@ int device_add(struct device *dev)
"subsystem");
sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
dev->bus_id);
-
- sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
- class_name = make_class_name(dev->class->name, &dev->kobj);
- sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ if (parent) {
+ sysfs_create_link(&dev->kobj, &dev->parent->kobj, "device");
+ class_name = make_class_name(dev->class->name, &dev->kobj);
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj, class_name);
+ }
}
+ if ((error = device_add_attrs(dev)))
+ goto AttrsError;
+ if ((error = device_add_groups(dev)))
+ goto GroupError;
if ((error = device_pm_add(dev)))
goto PMError;
if ((error = bus_add_device(dev)))
@@ -356,15 +482,16 @@ int device_add(struct device *dev)
klist_add_tail(&dev->knode_parent, &parent->klist_children);
if (dev->class) {
- /* tie the class to the device */
down(&dev->class->sem);
+ /* tie the class to the device */
list_add_tail(&dev->node, &dev->class->devices);
+
+ /* notify any interfaces that the device is here */
+ list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ if (class_intf->add_dev)
+ class_intf->add_dev(dev, class_intf);
up(&dev->class->sem);
}
-
- /* notify platform of device entry */
- if (platform_notify)
- platform_notify(dev);
Done:
kfree(class_name);
put_device(dev);
@@ -372,6 +499,10 @@ int device_add(struct device *dev)
BusError:
device_pm_remove(dev);
PMError:
+ device_remove_groups(dev);
+ GroupError:
+ device_remove_attrs(dev);
+ AttrsError:
if (dev->devt_attr) {
device_remove_file(dev, dev->devt_attr);
kfree(dev->devt_attr);
@@ -449,6 +580,7 @@ void device_del(struct device * dev)
{
struct device * parent = dev->parent;
char *class_name = NULL;
+ struct class_interface *class_intf;
if (parent)
klist_del(&dev->knode_parent);
@@ -458,14 +590,23 @@ void device_del(struct device * dev)
sysfs_remove_link(&dev->kobj, "subsystem");
sysfs_remove_link(&dev->class->subsys.kset.kobj, dev->bus_id);
class_name = make_class_name(dev->class->name, &dev->kobj);
- sysfs_remove_link(&dev->kobj, "device");
- sysfs_remove_link(&dev->parent->kobj, class_name);
+ if (parent) {
+ sysfs_remove_link(&dev->kobj, "device");
+ sysfs_remove_link(&dev->parent->kobj, class_name);
+ }
kfree(class_name);
down(&dev->class->sem);
+ /* notify any interfaces that the device is now gone */
+ list_for_each_entry(class_intf, &dev->class->interfaces, node)
+ if (class_intf->remove_dev)
+ class_intf->remove_dev(dev, class_intf);
+ /* remove the device from the class list */
list_del_init(&dev->node);
up(&dev->class->sem);
}
device_remove_file(dev, &dev->uevent_attr);
+ device_remove_groups(dev);
+ device_remove_attrs(dev);
/* Notify the platform of the removal, in case they
* need to do anything...
@@ -579,7 +720,7 @@ static void device_create_release(struct device *dev)
* been created with a call to class_create().
*/
struct device *device_create(struct class *class, struct device *parent,
- dev_t devt, char *fmt, ...)
+ dev_t devt, const char *fmt, ...)
{
va_list args;
struct device *dev = NULL;
@@ -587,10 +728,6 @@ struct device *device_create(struct class *class, struct device *parent,
if (class == NULL || IS_ERR(class))
goto error;
- if (parent == NULL) {
- printk(KERN_WARNING "%s does not work yet for NULL parents\n", __FUNCTION__);
- goto error;
- }
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
@@ -644,3 +781,58 @@ void device_destroy(struct class *class, dev_t devt)
device_unregister(dev);
}
EXPORT_SYMBOL_GPL(device_destroy);
+
+/**
+ * device_rename - renames a device
+ * @dev: the pointer to the struct device to be renamed
+ * @new_name: the new name of the device
+ */
+int device_rename(struct device *dev, char *new_name)
+{
+ char *old_class_name = NULL;
+ char *new_class_name = NULL;
+ char *old_symlink_name = NULL;
+ int error;
+
+ dev = get_device(dev);
+ if (!dev)
+ return -EINVAL;
+
+ pr_debug("DEVICE: renaming '%s' to '%s'\n", dev->bus_id, new_name);
+
+ if ((dev->class) && (dev->parent))
+ old_class_name = make_class_name(dev->class->name, &dev->kobj);
+
+ if (dev->class) {
+ old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+ if (!old_symlink_name)
+ return -ENOMEM;
+ strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE);
+ }
+
+ strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
+
+ error = kobject_rename(&dev->kobj, new_name);
+
+ if (old_class_name) {
+ new_class_name = make_class_name(dev->class->name, &dev->kobj);
+ if (new_class_name) {
+ sysfs_create_link(&dev->parent->kobj, &dev->kobj,
+ new_class_name);
+ sysfs_remove_link(&dev->parent->kobj, old_class_name);
+ }
+ }
+ if (dev->class) {
+ sysfs_remove_link(&dev->class->subsys.kset.kobj,
+ old_symlink_name);
+ sysfs_create_link(&dev->class->subsys.kset.kobj, &dev->kobj,
+ dev->bus_id);
+ }
+ put_device(dev);
+
+ kfree(old_class_name);
+ kfree(new_class_name);
+ kfree(old_symlink_name);
+
+ return error;
+}
diff --git a/drivers/base/dd.c b/drivers/base/dd.c
index 889c7111123..b5f43c3e44f 100644
--- a/drivers/base/dd.c
+++ b/drivers/base/dd.c
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/module.h>
+#include <linux/kthread.h>
#include "base.h"
#include "power/power.h"
@@ -38,66 +39,73 @@
*
* This function must be called with @dev->sem held.
*/
-void device_bind_driver(struct device * dev)
+int device_bind_driver(struct device *dev)
{
- if (klist_node_attached(&dev->knode_driver))
- return;
+ int ret;
+
+ if (klist_node_attached(&dev->knode_driver)) {
+ printk(KERN_WARNING "%s: device %s already bound\n",
+ __FUNCTION__, kobject_name(&dev->kobj));
+ return 0;
+ }
pr_debug("bound device '%s' to driver '%s'\n",
dev->bus_id, dev->driver->name);
klist_add_tail(&dev->knode_driver, &dev->driver->klist_devices);
- sysfs_create_link(&dev->driver->kobj, &dev->kobj,
+ ret = sysfs_create_link(&dev->driver->kobj, &dev->kobj,
kobject_name(&dev->kobj));
- sysfs_create_link(&dev->kobj, &dev->driver->kobj, "driver");
+ if (ret == 0) {
+ ret = sysfs_create_link(&dev->kobj, &dev->driver->kobj,
+ "driver");
+ if (ret)
+ sysfs_remove_link(&dev->driver->kobj,
+ kobject_name(&dev->kobj));
+ }
+ return ret;
}
-/**
- * driver_probe_device - attempt to bind device & driver.
- * @drv: driver.
- * @dev: device.
- *
- * First, we call the bus's match function, if one present, which
- * should compare the device IDs the driver supports with the
- * device IDs of the device. Note we don't do this ourselves
- * because we don't know the format of the ID structures, nor what
- * is to be considered a match and what is not.
- *
- * This function returns 1 if a match is found, an error if one
- * occurs (that is not -ENODEV or -ENXIO), and 0 otherwise.
- *
- * This function must be called with @dev->sem held. When called
- * for a USB interface, @dev->parent->sem must be held as well.
- */
-int driver_probe_device(struct device_driver * drv, struct device * dev)
+struct stupid_thread_structure {
+ struct device_driver *drv;
+ struct device *dev;
+};
+
+static atomic_t probe_count = ATOMIC_INIT(0);
+static int really_probe(void *void_data)
{
+ struct stupid_thread_structure *data = void_data;
+ struct device_driver *drv = data->drv;
+ struct device *dev = data->dev;
int ret = 0;
- if (drv->bus->match && !drv->bus->match(dev, drv))
- goto Done;
+ atomic_inc(&probe_count);
+ pr_debug("%s: Probing driver %s with device %s\n",
+ drv->bus->name, drv->name, dev->bus_id);
- pr_debug("%s: Matched Device %s with Driver %s\n",
- drv->bus->name, dev->bus_id, drv->name);
dev->driver = drv;
if (dev->bus->probe) {
ret = dev->bus->probe(dev);
if (ret) {
dev->driver = NULL;
- goto ProbeFailed;
+ goto probe_failed;
}
} else if (drv->probe) {
ret = drv->probe(dev);
if (ret) {
dev->driver = NULL;
- goto ProbeFailed;
+ goto probe_failed;
}
}
- device_bind_driver(dev);
+ if (device_bind_driver(dev)) {
+ printk(KERN_ERR "%s: device_bind_driver(%s) failed\n",
+ __FUNCTION__, dev->bus_id);
+ /* How does undo a ->probe? We're screwed. */
+ }
ret = 1;
pr_debug("%s: Bound Device %s to Driver %s\n",
drv->bus->name, dev->bus_id, drv->name);
- goto Done;
+ goto done;
- ProbeFailed:
+probe_failed:
if (ret == -ENODEV || ret == -ENXIO) {
/* Driver matched, but didn't support device
* or device not found.
@@ -110,7 +118,71 @@ int driver_probe_device(struct device_driver * drv, struct device * dev)
"%s: probe of %s failed with error %d\n",
drv->name, dev->bus_id, ret);
}
- Done:
+done:
+ kfree(data);
+ atomic_dec(&probe_count);
+ return ret;
+}
+
+/**
+ * driver_probe_done
+ * Determine if the probe sequence is finished or not.
+ *
+ * Should somehow figure out how to use a semaphore, not an atomic variable...
+ */
+int driver_probe_done(void)
+{
+ pr_debug("%s: probe_count = %d\n", __FUNCTION__,
+ atomic_read(&probe_count));
+ if (atomic_read(&probe_count))
+ return -EBUSY;
+ return 0;
+}
+
+/**
+ * driver_probe_device - attempt to bind device & driver together
+ * @drv: driver to bind a device to
+ * @dev: device to try to bind to the driver
+ *
+ * First, we call the bus's match function, if one present, which should
+ * compare the device IDs the driver supports with the device IDs of the
+ * device. Note we don't do this ourselves because we don't know the
+ * format of the ID structures, nor what is to be considered a match and
+ * what is not.
+ *
+ * This function returns 1 if a match is found, an error if one occurs
+ * (that is not -ENODEV or -ENXIO), and 0 otherwise.
+ *
+ * This function must be called with @dev->sem held. When called for a
+ * USB interface, @dev->parent->sem must be held as well.
+ */
+int driver_probe_device(struct device_driver * drv, struct device * dev)
+{
+ struct stupid_thread_structure *data;
+ struct task_struct *probe_task;
+ int ret = 0;
+
+ if (!device_is_registered(dev))
+ return -ENODEV;
+ if (drv->bus->match && !drv->bus->match(dev, drv))
+ goto done;
+
+ pr_debug("%s: Matched Device %s with Driver %s\n",
+ drv->bus->name, dev->bus_id, drv->name);
+
+ data = kmalloc(sizeof(*data), GFP_KERNEL);
+ data->drv = drv;
+ data->dev = dev;
+
+ if (drv->multithread_probe) {
+ probe_task = kthread_run(really_probe, data,
+ "probe-%s", dev->bus_id);
+ if (IS_ERR(probe_task))
+ ret = PTR_ERR(probe_task);
+ } else
+ ret = really_probe(data);
+
+done:
return ret;
}
@@ -139,8 +211,9 @@ int device_attach(struct device * dev)
down(&dev->sem);
if (dev->driver) {
- device_bind_driver(dev);
- ret = 1;
+ ret = device_bind_driver(dev);
+ if (ret == 0)
+ ret = 1;
} else
ret = bus_for_each_drv(dev->bus, NULL, dev, __device_attach);
up(&dev->sem);
@@ -182,9 +255,9 @@ static int __driver_attach(struct device * dev, void * data)
* returns 0 and the @dev->driver is set, we've found a
* compatible pair.
*/
-void driver_attach(struct device_driver * drv)
+int driver_attach(struct device_driver * drv)
{
- bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
+ return bus_for_each_dev(drv->bus, NULL, drv, __driver_attach);
}
/**
diff --git a/drivers/base/driver.c b/drivers/base/driver.c
index 562600dd540..1214cbd17d8 100644
--- a/drivers/base/driver.c
+++ b/drivers/base/driver.c
@@ -142,20 +142,6 @@ void put_driver(struct device_driver * drv)
kobject_put(&drv->kobj);
}
-static void klist_devices_get(struct klist_node *n)
-{
- struct device *dev = container_of(n, struct device, knode_driver);
-
- get_device(dev);
-}
-
-static void klist_devices_put(struct klist_node *n)
-{
- struct device *dev = container_of(n, struct device, knode_driver);
-
- put_device(dev);
-}
-
/**
* driver_register - register driver with bus
* @drv: driver to register
@@ -175,7 +161,7 @@ int driver_register(struct device_driver * drv)
(drv->bus->shutdown && drv->shutdown)) {
printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name);
}
- klist_init(&drv->klist_devices, klist_devices_get, klist_devices_put);
+ klist_init(&drv->klist_devices, NULL, NULL);
init_completion(&drv->unloaded);
return bus_add_driver(drv);
}
diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c
index 5d6c011183f..14615694ae9 100644
--- a/drivers/base/firmware_class.c
+++ b/drivers/base/firmware_class.c
@@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/bitops.h>
#include <linux/mutex.h>
+#include <linux/kthread.h>
#include <linux/firmware.h>
#include "base.h"
@@ -511,7 +512,6 @@ request_firmware_work_func(void *arg)
WARN_ON(1);
return 0;
}
- daemonize("%s/%s", "firmware", fw_work->name);
ret = _request_firmware(&fw, fw_work->name, fw_work->device,
fw_work->uevent);
if (ret < 0)
@@ -546,9 +546,9 @@ request_firmware_nowait(
const char *name, struct device *device, void *context,
void (*cont)(const struct firmware *fw, void *context))
{
+ struct task_struct *task;
struct firmware_work *fw_work = kmalloc(sizeof (struct firmware_work),
GFP_ATOMIC);
- int ret;
if (!fw_work)
return -ENOMEM;
@@ -566,14 +566,14 @@ request_firmware_nowait(
.uevent = uevent,
};
- ret = kernel_thread(request_firmware_work_func, fw_work,
- CLONE_FS | CLONE_FILES);
+ task = kthread_run(request_firmware_work_func, fw_work,
+ "firmware/%s", name);
- if (ret < 0) {
+ if (IS_ERR(task)) {
fw_work->cont(NULL, fw_work->context);
module_put(fw_work->module);
kfree(fw_work);
- return ret;
+ return PTR_ERR(task);
}
return 0;
}
@@ -602,7 +602,7 @@ firmware_class_exit(void)
class_unregister(&firmware_class);
}
-module_init(firmware_class_init);
+fs_initcall(firmware_class_init);
module_exit(firmware_class_exit);
EXPORT_SYMBOL(release_firmware);
diff --git a/drivers/base/node.c b/drivers/base/node.c
index e9b0957f15d..001e6f6b9c1 100644
--- a/drivers/base/node.c
+++ b/drivers/base/node.c
@@ -54,10 +54,12 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
"Node %d MemUsed: %8lu kB\n"
"Node %d Active: %8lu kB\n"
"Node %d Inactive: %8lu kB\n"
+#ifdef CONFIG_HIGHMEM
"Node %d HighTotal: %8lu kB\n"
"Node %d HighFree: %8lu kB\n"
"Node %d LowTotal: %8lu kB\n"
"Node %d LowFree: %8lu kB\n"
+#endif
"Node %d Dirty: %8lu kB\n"
"Node %d Writeback: %8lu kB\n"
"Node %d FilePages: %8lu kB\n"
@@ -66,16 +68,20 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
"Node %d PageTables: %8lu kB\n"
"Node %d NFS_Unstable: %8lu kB\n"
"Node %d Bounce: %8lu kB\n"
- "Node %d Slab: %8lu kB\n",
+ "Node %d Slab: %8lu kB\n"
+ "Node %d SReclaimable: %8lu kB\n"
+ "Node %d SUnreclaim: %8lu kB\n",
nid, K(i.totalram),
nid, K(i.freeram),
nid, K(i.totalram - i.freeram),
nid, K(active),
nid, K(inactive),
+#ifdef CONFIG_HIGHMEM
nid, K(i.totalhigh),
nid, K(i.freehigh),
nid, K(i.totalram - i.totalhigh),
nid, K(i.freeram - i.freehigh),
+#endif
nid, K(node_page_state(nid, NR_FILE_DIRTY)),
nid, K(node_page_state(nid, NR_WRITEBACK)),
nid, K(node_page_state(nid, NR_FILE_PAGES)),
@@ -84,7 +90,10 @@ static ssize_t node_read_meminfo(struct sys_device * dev, char * buf)
nid, K(node_page_state(nid, NR_PAGETABLE)),
nid, K(node_page_state(nid, NR_UNSTABLE_NFS)),
nid, K(node_page_state(nid, NR_BOUNCE)),
- nid, K(node_page_state(nid, NR_SLAB)));
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE) +
+ node_page_state(nid, NR_SLAB_UNRECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_RECLAIMABLE)),
+ nid, K(node_page_state(nid, NR_SLAB_UNRECLAIMABLE)));
n += hugetlb_report_node_meminfo(nid, buf + n);
return n;
}
diff --git a/drivers/base/platform.c b/drivers/base/platform.c
index 2b8755db76c..940ce41f188 100644
--- a/drivers/base/platform.c
+++ b/drivers/base/platform.c
@@ -505,12 +505,36 @@ static int platform_match(struct device * dev, struct device_driver * drv)
return (strncmp(pdev->name, drv->name, BUS_ID_SIZE) == 0);
}
-static int platform_suspend(struct device * dev, pm_message_t state)
+static int platform_suspend(struct device *dev, pm_message_t mesg)
{
int ret = 0;
if (dev->driver && dev->driver->suspend)
- ret = dev->driver->suspend(dev, state);
+ ret = dev->driver->suspend(dev, mesg);
+
+ return ret;
+}
+
+static int platform_suspend_late(struct device *dev, pm_message_t mesg)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ int ret = 0;
+
+ if (dev->driver && drv->suspend_late)
+ ret = drv->suspend_late(pdev, mesg);
+
+ return ret;
+}
+
+static int platform_resume_early(struct device *dev)
+{
+ struct platform_driver *drv = to_platform_driver(dev->driver);
+ struct platform_device *pdev = container_of(dev, struct platform_device, dev);
+ int ret = 0;
+
+ if (dev->driver && drv->resume_early)
+ ret = drv->resume_early(pdev);
return ret;
}
@@ -531,6 +555,8 @@ struct bus_type platform_bus_type = {
.match = platform_match,
.uevent = platform_uevent,
.suspend = platform_suspend,
+ .suspend_late = platform_suspend_late,
+ .resume_early = platform_resume_early,
.resume = platform_resume,
};
EXPORT_SYMBOL_GPL(platform_bus_type);
diff --git a/drivers/base/power/resume.c b/drivers/base/power/resume.c
index 826093ef4c7..020be36705a 100644
--- a/drivers/base/power/resume.c
+++ b/drivers/base/power/resume.c
@@ -38,13 +38,35 @@ int resume_device(struct device * dev)
dev_dbg(dev,"resuming\n");
error = dev->bus->resume(dev);
}
+ if (dev->class && dev->class->resume) {
+ dev_dbg(dev,"class resume\n");
+ error = dev->class->resume(dev);
+ }
up(&dev->sem);
TRACE_RESUME(error);
return error;
}
+static int resume_device_early(struct device * dev)
+{
+ int error = 0;
+ TRACE_DEVICE(dev);
+ TRACE_RESUME(0);
+ if (dev->bus && dev->bus->resume_early) {
+ dev_dbg(dev,"EARLY resume\n");
+ error = dev->bus->resume_early(dev);
+ }
+ TRACE_RESUME(error);
+ return error;
+}
+
+/*
+ * Resume the devices that have either not gone through
+ * the late suspend, or that did go through it but also
+ * went through the early resume
+ */
void dpm_resume(void)
{
down(&dpm_list_sem);
@@ -74,6 +96,7 @@ void dpm_resume(void)
void device_resume(void)
{
+ might_sleep();
down(&dpm_sem);
dpm_resume();
up(&dpm_sem);
@@ -83,12 +106,12 @@ EXPORT_SYMBOL_GPL(device_resume);
/**
- * device_power_up_irq - Power on some devices.
+ * dpm_power_up - Power on some devices.
*
* Walk the dpm_off_irq list and power each device up. This
* is used for devices that required they be powered down with
- * interrupts disabled. As devices are powered on, they are moved to
- * the dpm_suspended list.
+ * interrupts disabled. As devices are powered on, they are moved
+ * to the dpm_active list.
*
* Interrupts must be disabled when calling this.
*/
@@ -99,16 +122,14 @@ void dpm_power_up(void)
struct list_head * entry = dpm_off_irq.next;
struct device * dev = to_device(entry);
- get_device(dev);
- list_move_tail(entry, &dpm_active);
- resume_device(dev);
- put_device(dev);
+ list_move_tail(entry, &dpm_off);
+ resume_device_early(dev);
}
}
/**
- * device_pm_power_up - Turn on all devices that need special attention.
+ * device_power_up - Turn on all devices that need special attention.
*
* Power on system devices then devices that required we shut them down
* with interrupts disabled.
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c
index 69509e02f70..ece136bf97e 100644
--- a/drivers/base/power/suspend.c
+++ b/drivers/base/power/suspend.c
@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event)
switch (event) {
case PM_EVENT_SUSPEND: return "suspend";
case PM_EVENT_FREEZE: return "freeze";
+ case PM_EVENT_PRETHAW: return "prethaw";
default: return "(unknown suspend event)";
}
}
@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state)
dev->power.prev_state = dev->power.power_state;
- if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
+ if (dev->class && dev->class->suspend && !dev->power.power_state.event) {
+ dev_dbg(dev, "class %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->class->suspend(dev, state);
+ suspend_report_result(dev->class->suspend, error);
+ }
+
+ if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) {
dev_dbg(dev, "%s%s\n",
suspend_verb(state.event),
((state.event == PM_EVENT_SUSPEND)
@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state)
}
+/*
+ * This is called with interrupts off, only a single CPU
+ * running. We can't do down() on a semaphore (and we don't
+ * need the protection)
+ */
+static int suspend_device_late(struct device *dev, pm_message_t state)
+{
+ int error = 0;
+
+ if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) {
+ dev_dbg(dev, "LATE %s%s\n",
+ suspend_verb(state.event),
+ ((state.event == PM_EVENT_SUSPEND)
+ && device_may_wakeup(dev))
+ ? ", may wakeup"
+ : ""
+ );
+ error = dev->bus->suspend_late(dev, state);
+ suspend_report_result(dev->bus->suspend_late, error);
+ }
+ return error;
+}
+
/**
* device_suspend - Save state and stop all devices in system.
* @state: Power state to put each device in.
*
* Walk the dpm_active list, call ->suspend() for each device, and move
- * it to dpm_off.
- * Check the return value for each. If it returns 0, then we move the
- * the device to the dpm_off list. If it returns -EAGAIN, we move it to
- * the dpm_off_irq list. If we get a different error, try and back out.
+ * it to the dpm_off list.
+ *
+ * (For historical reasons, if it returns -EAGAIN, that used to mean
+ * that the device would be called again with interrupts disabled.
+ * These days, we use the "suspend_late()" callback for that, so we
+ * print a warning and consider it an error).
+ *
+ * If we get a different error, try and back out.
*
* If we hit a failure with any of the devices, call device_resume()
* above to bring the suspended devices back to life.
@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state)
{
int error = 0;
+ might_sleep();
down(&dpm_sem);
down(&dpm_list_sem);
while (!list_empty(&dpm_active) && error == 0) {
@@ -115,39 +156,27 @@ 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 */
+ /* Move it to the dpm_off list */
if (!error)
list_move(&dev->power.entry, &dpm_off);
- else if (error == -EAGAIN) {
- list_move(&dev->power.entry, &dpm_off_irq);
- error = 0;
- }
}
if (error)
printk(KERN_ERR "Could not suspend device %s: "
- "error %d\n", kobject_name(&dev->kobj), error);
+ "error %d%s\n",
+ kobject_name(&dev->kobj), error,
+ error == -EAGAIN ? " (please convert to suspend_late)" : "");
put_device(dev);
}
up(&dpm_list_sem);
- if (error) {
- /* we failed... before resuming, bring back devices from
- * dpm_off_irq list back to main dpm_off list, we do want
- * to call resume() on them, in case they partially suspended
- * despite returning -EAGAIN
- */
- while (!list_empty(&dpm_off_irq)) {
- struct list_head * entry = dpm_off_irq.next;
- list_move(entry, &dpm_off);
- }
+ if (error)
dpm_resume();
- }
+
up(&dpm_sem);
return error;
}
EXPORT_SYMBOL_GPL(device_suspend);
-
/**
* device_power_down - Shut down special devices.
* @state: Power state to enter.
@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state)
int error = 0;
struct device * dev;
- list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) {
- if ((error = suspend_device(dev, state)))
- break;
+ while (!list_empty(&dpm_off)) {
+ struct list_head * entry = dpm_off.prev;
+
+ dev = to_device(entry);
+ error = suspend_device_late(dev, state);
+ if (error)
+ goto Error;
+ list_move(&dev->power.entry, &dpm_off_irq);
}
- if (error)
- goto Error;
- if ((error = sysdev_suspend(state)))
- goto Error;
+
+ error = sysdev_suspend(state);
Done:
return error;
Error:
diff --git a/drivers/base/power/sysfs.c b/drivers/base/power/sysfs.c
index 40d7242a07c..2d47517dbe3 100644
--- a/drivers/base/power/sysfs.c
+++ b/drivers/base/power/sysfs.c
@@ -7,22 +7,29 @@
#include "power.h"
+#ifdef CONFIG_PM_SYSFS_DEPRECATED
+
/**
* state - Control current power state of device
*
* show() returns the current power state of the device. '0' indicates
- * the device is on. Other values (1-3) indicate the device is in a low
+ * the device is on. Other values (2) indicate the device is in some low
* power state.
*
- * store() sets the current power state, which is an integer value
- * between 0-3. If the device is on ('0'), and the value written is
- * greater than 0, then the device is placed directly into the low-power
- * state (via its driver's ->suspend() method).
- * If the device is currently in a low-power state, and the value is 0,
- * the device is powered back on (via the ->resume() method).
- * If the device is in a low-power state, and a different low-power state
- * is requested, the device is first resumed, then suspended into the new
- * low-power state.
+ * store() sets the current power state, which is an integer valued
+ * 0, 2, or 3. Devices with bus.suspend_late(), or bus.resume_early()
+ * methods fail this operation; those methods couldn't be called.
+ * Otherwise,
+ *
+ * - If the recorded dev->power.power_state.event matches the
+ * target value, nothing is done.
+ * - If the recorded event code is nonzero, the device is reactivated
+ * by calling bus.resume() and/or class.resume().
+ * - If the target value is nonzero, the device is suspended by
+ * calling class.suspend() and/or bus.suspend() with event code
+ * PM_EVENT_SUSPEND.
+ *
+ * This mechanism is DEPRECATED and should only be used for testing.
*/
static ssize_t state_show(struct device * dev, struct device_attribute *attr, char * buf)
@@ -38,6 +45,10 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
pm_message_t state;
int error = -EINVAL;
+ /* disallow incomplete suspend sequences */
+ if (dev->bus && (dev->bus->suspend_late || dev->bus->resume_early))
+ return error;
+
state.event = PM_EVENT_SUSPEND;
/* Older apps expected to write "3" here - confused with PCI D3 */
if ((n == 1) && !strcmp(buf, "3"))
@@ -57,6 +68,8 @@ static ssize_t state_store(struct device * dev, struct device_attribute *attr, c
static DEVICE_ATTR(state, 0644, state_show, state_store);
+#endif /* CONFIG_PM_SYSFS_DEPRECATED */
+
/*
* wakeup - Report/change current wakeup option for device
*
@@ -130,7 +143,9 @@ static DEVICE_ATTR(wakeup, 0644, wake_show, wake_store);
static struct attribute * power_attrs[] = {
+#ifdef CONFIG_PM_SYSFS_DEPRECATED
&dev_attr_state.attr,
+#endif
&dev_attr_wakeup.attr,
NULL,
};
diff --git a/drivers/block/DAC960.c b/drivers/block/DAC960.c
index a360215dbce..b3f639fbf22 100644
--- a/drivers/block/DAC960.c
+++ b/drivers/block/DAC960.c
@@ -770,7 +770,7 @@ static void DAC960_P_QueueCommand(DAC960_Command_T *Command)
static void DAC960_ExecuteCommand(DAC960_Command_T *Command)
{
DAC960_Controller_T *Controller = Command->Controller;
- DECLARE_COMPLETION(Completion);
+ DECLARE_COMPLETION_ONSTACK(Completion);
unsigned long flags;
Command->Completion = &Completion;
@@ -3331,7 +3331,7 @@ static int DAC960_process_queue(DAC960_Controller_T *Controller, struct request_
Command->DmaDirection = PCI_DMA_TODEVICE;
Command->CommandType = DAC960_WriteCommand;
}
- Command->Completion = Request->waiting;
+ Command->Completion = Request->end_io_data;
Command->LogicalDriveNumber = (long)Request->rq_disk->private_data;
Command->BlockNumber = Request->sector;
Command->BlockCount = Request->nr_sectors;
diff --git a/drivers/block/DAC960.h b/drivers/block/DAC960.h
index a82f37f749a..f9217c34bc2 100644
--- a/drivers/block/DAC960.h
+++ b/drivers/block/DAC960.h
@@ -71,7 +71,7 @@
Define a Boolean data type.
*/
-typedef enum { false, true } __attribute__ ((packed)) boolean;
+typedef bool boolean;
/*
diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index b5382cedf0c..422e31d5f8e 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -2,6 +2,8 @@
# Block device driver configuration
#
+if BLOCK
+
menu "Block devices"
config BLK_DEV_FD
@@ -468,3 +470,5 @@ config ATA_OVER_ETH
devices like the Coraid EtherDrive (R) Storage Blade.
endmenu
+
+endif
diff --git a/drivers/block/cciss.c b/drivers/block/cciss.c
index 2cd3391ff87..99f87efe0f5 100644
--- a/drivers/block/cciss.c
+++ b/drivers/block/cciss.c
@@ -144,13 +144,13 @@ static int rebuild_lun_table(ctlr_info_t *h, struct gendisk *del_disk);
static int deregister_disk(struct gendisk *disk, drive_info_struct *drv,
int clear_all);
-static void cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf,
- int withirq, unsigned int *total_size,
- unsigned int *block_size);
-static void cciss_geometry_inquiry(int ctlr, int logvol, int withirq,
- unsigned int total_size,
- unsigned int block_size,
- InquiryData_struct *inq_buff,
+static void cciss_read_capacity(int ctlr, int logvol, int withirq,
+ sector_t *total_size, unsigned int *block_size);
+static void cciss_read_capacity_16(int ctlr, int logvol, int withirq,
+ sector_t *total_size, unsigned int *block_size);
+static void cciss_geometry_inquiry(int ctlr, int logvol,
+ int withirq, sector_t total_size,
+ unsigned int block_size, InquiryData_struct *inq_buff,
drive_info_struct *drv);
static void cciss_getgeometry(int cntl_num);
static void __devinit cciss_interrupt_mode(ctlr_info_t *, struct pci_dev *,
@@ -879,7 +879,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
char *buff = NULL;
u64bit temp64;
unsigned long flags;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
if (!arg)
return -EINVAL;
@@ -997,7 +997,7 @@ static int cciss_ioctl(struct inode *inode, struct file *filep,
BYTE sg_used = 0;
int status = 0;
int i;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
__u32 left;
__u32 sz;
BYTE __user *data_ptr;
@@ -1229,7 +1229,6 @@ static inline void complete_buffers(struct bio *bio, int status)
int nr_sectors = bio_sectors(bio);
bio->bi_next = NULL;
- blk_finished_io(len);
bio_endio(bio, nr_sectors << 9, status ? 0 : -EIO);
bio = xbh;
}
@@ -1326,10 +1325,9 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
{
ctlr_info_t *h = hba[ctlr];
struct gendisk *disk;
- ReadCapdata_struct *size_buff = NULL;
InquiryData_struct *inq_buff = NULL;
unsigned int block_size;
- unsigned int total_size;
+ sector_t total_size;
unsigned long flags = 0;
int ret = 0;
@@ -1348,15 +1346,25 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
return;
/* Get information about the disk and modify the driver structure */
- size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
- if (size_buff == NULL)
- goto mem_msg;
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL)
goto mem_msg;
- cciss_read_capacity(ctlr, drv_index, size_buff, 1,
+ cciss_read_capacity(ctlr, drv_index, 1,
&total_size, &block_size);
+
+ /* total size = last LBA + 1 */
+ /* FFFFFFFF + 1 = 0, cannot have a logical volume of size 0 */
+ /* so we assume this volume this must be >2TB in size */
+ if (total_size == (__u32) 0) {
+ cciss_read_capacity_16(ctlr, drv_index, 1,
+ &total_size, &block_size);
+ h->cciss_read = CCISS_READ_16;
+ h->cciss_write = CCISS_WRITE_16;
+ } else {
+ h->cciss_read = CCISS_READ_10;
+ h->cciss_write = CCISS_WRITE_10;
+ }
cciss_geometry_inquiry(ctlr, drv_index, 1, total_size, block_size,
inq_buff, &h->drv[drv_index]);
@@ -1392,7 +1400,6 @@ static void cciss_update_drive_info(int ctlr, int drv_index)
}
freeret:
- kfree(size_buff);
kfree(inq_buff);
return;
mem_msg:
@@ -1717,6 +1724,22 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
c->Request.Timeout = 0;
c->Request.CDB[0] = cmd;
break;
+ case CCISS_READ_CAPACITY_16:
+ c->Header.LUN.LogDev.VolId = h->drv[log_unit].LunID;
+ c->Header.LUN.LogDev.Mode = 1;
+ c->Request.CDBLen = 16;
+ c->Request.Type.Attribute = ATTR_SIMPLE;
+ c->Request.Type.Direction = XFER_READ;
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = cmd;
+ c->Request.CDB[1] = 0x10;
+ c->Request.CDB[10] = (size >> 24) & 0xFF;
+ c->Request.CDB[11] = (size >> 16) & 0xFF;
+ c->Request.CDB[12] = (size >> 8) & 0xFF;
+ c->Request.CDB[13] = size & 0xFF;
+ c->Request.Timeout = 0;
+ c->Request.CDB[0] = cmd;
+ break;
case CCISS_CACHE_FLUSH:
c->Request.CDBLen = 12;
c->Request.Type.Attribute = ATTR_SIMPLE;
@@ -1750,6 +1773,7 @@ static int fill_cmd(CommandList_struct *c, __u8 cmd, int ctlr, void *buff, size_
memset(&c->Request.CDB[0], 0, sizeof(c->Request.CDB));
c->Request.CDB[0] = cmd; /* reset */
c->Request.CDB[1] = 0x04; /* reset a LUN */
+ break;
case 3: /* No-Op message */
c->Request.CDBLen = 1;
c->Request.Type.Attribute = ATTR_SIMPLE;
@@ -1792,7 +1816,7 @@ static int sendcmd_withirq(__u8 cmd,
u64bit buff_dma_handle;
unsigned long flags;
int return_status;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
if ((c = cmd_alloc(h, 0)) == NULL)
return -ENOMEM;
@@ -1893,12 +1917,15 @@ static int sendcmd_withirq(__u8 cmd,
}
static void cciss_geometry_inquiry(int ctlr, int logvol,
- int withirq, unsigned int total_size,
+ int withirq, sector_t total_size,
unsigned int block_size,
InquiryData_struct *inq_buff,
drive_info_struct *drv)
{
int return_code;
+ unsigned long t;
+ unsigned long rem;
+
memset(inq_buff, 0, sizeof(InquiryData_struct));
if (withirq)
return_code = sendcmd_withirq(CISS_INQUIRY, ctlr,
@@ -1917,10 +1944,10 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
drv->nr_blocks = total_size;
drv->heads = 255;
drv->sectors = 32; // Sectors per track
- drv->cylinders = total_size / 255 / 32;
+ t = drv->heads * drv->sectors;
+ drv->cylinders = total_size;
+ rem = do_div(drv->cylinders, t);
} else {
- unsigned int t;
-
drv->block_size = block_size;
drv->nr_blocks = total_size;
drv->heads = inq_buff->data_byte[6];
@@ -1930,42 +1957,84 @@ static void cciss_geometry_inquiry(int ctlr, int logvol,
drv->raid_level = inq_buff->data_byte[8];
t = drv->heads * drv->sectors;
if (t > 1) {
- drv->cylinders = total_size / t;
+ drv->cylinders = total_size;
+ rem = do_div(drv->cylinders, t);
}
}
} else { /* Get geometry failed */
printk(KERN_WARNING "cciss: reading geometry failed\n");
}
- printk(KERN_INFO " heads= %d, sectors= %d, cylinders= %d\n\n",
+ printk(KERN_INFO " heads=%d, sectors=%d, cylinders=%d\n\n",
drv->heads, drv->sectors, drv->cylinders);
}
static void
-cciss_read_capacity(int ctlr, int logvol, ReadCapdata_struct *buf,
- int withirq, unsigned int *total_size,
+cciss_read_capacity(int ctlr, int logvol, int withirq, sector_t *total_size,
unsigned int *block_size)
{
+ ReadCapdata_struct *buf;
int return_code;
- memset(buf, 0, sizeof(*buf));
+ buf = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_WARNING "cciss: out of memory\n");
+ return;
+ }
+ memset(buf, 0, sizeof(ReadCapdata_struct));
if (withirq)
return_code = sendcmd_withirq(CCISS_READ_CAPACITY,
- ctlr, buf, sizeof(*buf), 1,
- logvol, 0, TYPE_CMD);
+ ctlr, buf, sizeof(ReadCapdata_struct),
+ 1, logvol, 0, TYPE_CMD);
else
return_code = sendcmd(CCISS_READ_CAPACITY,
- ctlr, buf, sizeof(*buf), 1, logvol, 0,
- NULL, TYPE_CMD);
+ ctlr, buf, sizeof(ReadCapdata_struct),
+ 1, logvol, 0, NULL, TYPE_CMD);
if (return_code == IO_OK) {
- *total_size =
- be32_to_cpu(*((__be32 *) & buf->total_size[0])) + 1;
- *block_size = be32_to_cpu(*((__be32 *) & buf->block_size[0]));
+ *total_size = be32_to_cpu(*(__u32 *) buf->total_size)+1;
+ *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
} else { /* read capacity command failed */
printk(KERN_WARNING "cciss: read capacity failed\n");
*total_size = 0;
*block_size = BLOCK_SIZE;
}
- printk(KERN_INFO " blocks= %u block_size= %d\n",
+ if (*total_size != (__u32) 0)
+ printk(KERN_INFO " blocks= %lld block_size= %d\n",
+ *total_size, *block_size);
+ kfree(buf);
+ return;
+}
+
+static void
+cciss_read_capacity_16(int ctlr, int logvol, int withirq, sector_t *total_size, unsigned int *block_size)
+{
+ ReadCapdata_struct_16 *buf;
+ int return_code;
+ buf = kmalloc(sizeof(ReadCapdata_struct_16), GFP_KERNEL);
+ if (buf == NULL) {
+ printk(KERN_WARNING "cciss: out of memory\n");
+ return;
+ }
+ memset(buf, 0, sizeof(ReadCapdata_struct_16));
+ if (withirq) {
+ return_code = sendcmd_withirq(CCISS_READ_CAPACITY_16,
+ ctlr, buf, sizeof(ReadCapdata_struct_16),
+ 1, logvol, 0, TYPE_CMD);
+ }
+ else {
+ return_code = sendcmd(CCISS_READ_CAPACITY_16,
+ ctlr, buf, sizeof(ReadCapdata_struct_16),
+ 1, logvol, 0, NULL, TYPE_CMD);
+ }
+ if (return_code == IO_OK) {
+ *total_size = be64_to_cpu(*(__u64 *) buf->total_size)+1;
+ *block_size = be32_to_cpu(*(__u32 *) buf->block_size);
+ } else { /* read capacity command failed */
+ printk(KERN_WARNING "cciss: read capacity failed\n");
+ *total_size = 0;
+ *block_size = BLOCK_SIZE;
+ }
+ printk(KERN_INFO " blocks= %lld block_size= %d\n",
*total_size, *block_size);
+ kfree(buf);
return;
}
@@ -1976,8 +2045,7 @@ static int cciss_revalidate(struct gendisk *disk)
int logvol;
int FOUND = 0;
unsigned int block_size;
- unsigned int total_size;
- ReadCapdata_struct *size_buff = NULL;
+ sector_t total_size;
InquiryData_struct *inq_buff = NULL;
for (logvol = 0; logvol < CISS_MAX_LUN; logvol++) {
@@ -1990,27 +2058,24 @@ static int cciss_revalidate(struct gendisk *disk)
if (!FOUND)
return 1;
- size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
- if (size_buff == NULL) {
- printk(KERN_WARNING "cciss: out of memory\n");
- return 1;
- }
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
printk(KERN_WARNING "cciss: out of memory\n");
- kfree(size_buff);
return 1;
}
-
- cciss_read_capacity(h->ctlr, logvol, size_buff, 1, &total_size,
- &block_size);
+ if (h->cciss_read == CCISS_READ_10) {
+ cciss_read_capacity(h->ctlr, logvol, 1,
+ &total_size, &block_size);
+ } else {
+ cciss_read_capacity_16(h->ctlr, logvol, 1,
+ &total_size, &block_size);
+ }
cciss_geometry_inquiry(h->ctlr, logvol, 1, total_size, block_size,
inq_buff, drv);
blk_queue_hardsect_size(drv->queue, drv->block_size);
set_capacity(disk, drv->nr_blocks);
- kfree(size_buff);
kfree(inq_buff);
return 0;
}
@@ -2419,7 +2484,8 @@ static void do_cciss_request(request_queue_t *q)
{
ctlr_info_t *h = q->queuedata;
CommandList_struct *c;
- int start_blk, seg;
+ sector_t start_blk;
+ int seg;
struct request *creq;
u64bit temp64;
struct scatterlist tmp_sg[MAXSGENTRIES];
@@ -2463,10 +2529,10 @@ static void do_cciss_request(request_queue_t *q)
c->Request.Type.Type = TYPE_CMD; // It is a command.
c->Request.Type.Attribute = ATTR_SIMPLE;
c->Request.Type.Direction =
- (rq_data_dir(creq) == READ) ? XFER_READ : XFER_WRITE;
+ (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
c->Request.Timeout = 0; // Don't time out
c->Request.CDB[0] =
- (rq_data_dir(creq) == READ) ? CCISS_READ : CCISS_WRITE;
+ (rq_data_dir(creq) == READ) ? h->cciss_read : h->cciss_write;
start_blk = creq->sector;
#ifdef CCISS_DEBUG
printk(KERN_DEBUG "ciss: sector =%d nr_sectors=%d\n", (int)creq->sector,
@@ -2500,15 +2566,33 @@ static void do_cciss_request(request_queue_t *q)
#endif /* CCISS_DEBUG */
c->Header.SGList = c->Header.SGTotal = seg;
- c->Request.CDB[1] = 0;
- c->Request.CDB[2] = (start_blk >> 24) & 0xff; //MSB
- c->Request.CDB[3] = (start_blk >> 16) & 0xff;
- c->Request.CDB[4] = (start_blk >> 8) & 0xff;
- c->Request.CDB[5] = start_blk & 0xff;
- c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
- c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
- c->Request.CDB[8] = creq->nr_sectors & 0xff;
- c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+ if(h->cciss_read == CCISS_READ_10) {
+ c->Request.CDB[1] = 0;
+ c->Request.CDB[2] = (start_blk >> 24) & 0xff; //MSB
+ c->Request.CDB[3] = (start_blk >> 16) & 0xff;
+ c->Request.CDB[4] = (start_blk >> 8) & 0xff;
+ c->Request.CDB[5] = start_blk & 0xff;
+ c->Request.CDB[6] = 0; // (sect >> 24) & 0xff; MSB
+ c->Request.CDB[7] = (creq->nr_sectors >> 8) & 0xff;
+ c->Request.CDB[8] = creq->nr_sectors & 0xff;
+ c->Request.CDB[9] = c->Request.CDB[11] = c->Request.CDB[12] = 0;
+ } else {
+ c->Request.CDBLen = 16;
+ c->Request.CDB[1]= 0;
+ c->Request.CDB[2]= (start_blk >> 56) & 0xff; //MSB
+ c->Request.CDB[3]= (start_blk >> 48) & 0xff;
+ c->Request.CDB[4]= (start_blk >> 40) & 0xff;
+ c->Request.CDB[5]= (start_blk >> 32) & 0xff;
+ c->Request.CDB[6]= (start_blk >> 24) & 0xff;
+ c->Request.CDB[7]= (start_blk >> 16) & 0xff;
+ c->Request.CDB[8]= (start_blk >> 8) & 0xff;
+ c->Request.CDB[9]= start_blk & 0xff;
+ c->Request.CDB[10]= (creq->nr_sectors >> 24) & 0xff;
+ c->Request.CDB[11]= (creq->nr_sectors >> 16) & 0xff;
+ c->Request.CDB[12]= (creq->nr_sectors >> 8) & 0xff;
+ c->Request.CDB[13]= creq->nr_sectors & 0xff;
+ c->Request.CDB[14] = c->Request.CDB[15] = 0;
+ }
spin_lock_irq(q->queue_lock);
@@ -2518,9 +2602,9 @@ static void do_cciss_request(request_queue_t *q)
h->maxQsinceinit = h->Qdepth;
goto queue;
- full:
+full:
blk_stop_queue(q);
- startio:
+startio:
/* We will already have the driver lock here so not need
* to lock it.
*/
@@ -2948,31 +3032,23 @@ static int cciss_pci_init(ctlr_info_t *c, struct pci_dev *pdev)
static void cciss_getgeometry(int cntl_num)
{
ReportLunData_struct *ld_buff;
- ReadCapdata_struct *size_buff;
InquiryData_struct *inq_buff;
int return_code;
int i;
int listlength = 0;
__u32 lunid = 0;
int block_size;
- int total_size;
+ sector_t total_size;
ld_buff = kzalloc(sizeof(ReportLunData_struct), GFP_KERNEL);
if (ld_buff == NULL) {
printk(KERN_ERR "cciss: out of memory\n");
return;
}
- size_buff = kmalloc(sizeof(ReadCapdata_struct), GFP_KERNEL);
- if (size_buff == NULL) {
- printk(KERN_ERR "cciss: out of memory\n");
- kfree(ld_buff);
- return;
- }
inq_buff = kmalloc(sizeof(InquiryData_struct), GFP_KERNEL);
if (inq_buff == NULL) {
printk(KERN_ERR "cciss: out of memory\n");
kfree(ld_buff);
- kfree(size_buff);
return;
}
/* Get the firmware version */
@@ -3027,7 +3103,6 @@ static void cciss_getgeometry(int cntl_num)
#endif /* CCISS_DEBUG */
hba[cntl_num]->highest_lun = hba[cntl_num]->num_luns - 1;
-// for(i=0; i< hba[cntl_num]->num_luns; i++)
for (i = 0; i < CISS_MAX_LUN; i++) {
if (i < hba[cntl_num]->num_luns) {
lunid = (0xff & (unsigned int)(ld_buff->LUN[i][3]))
@@ -3046,8 +3121,26 @@ static void cciss_getgeometry(int cntl_num)
ld_buff->LUN[i][2], ld_buff->LUN[i][3],
hba[cntl_num]->drv[i].LunID);
#endif /* CCISS_DEBUG */
- cciss_read_capacity(cntl_num, i, size_buff, 0,
+
+ /* testing to see if 16-byte CDBs are already being used */
+ if(hba[cntl_num]->cciss_read == CCISS_READ_16) {
+ cciss_read_capacity_16(cntl_num, i, 0,
&total_size, &block_size);
+ goto geo_inq;
+ }
+ cciss_read_capacity(cntl_num, i, 0, &total_size, &block_size);
+
+ /* total_size = last LBA + 1 */
+ if(total_size == (__u32) 0) {
+ cciss_read_capacity_16(cntl_num, i, 0,
+ &total_size, &block_size);
+ hba[cntl_num]->cciss_read = CCISS_READ_16;
+ hba[cntl_num]->cciss_write = CCISS_WRITE_16;
+ } else {
+ hba[cntl_num]->cciss_read = CCISS_READ_10;
+ hba[cntl_num]->cciss_write = CCISS_WRITE_10;
+ }
+geo_inq:
cciss_geometry_inquiry(cntl_num, i, 0, total_size,
block_size, inq_buff,
&hba[cntl_num]->drv[i]);
@@ -3057,7 +3150,6 @@ static void cciss_getgeometry(int cntl_num)
}
}
kfree(ld_buff);
- kfree(size_buff);
kfree(inq_buff);
}
diff --git a/drivers/block/cciss.h b/drivers/block/cciss.h
index 868e0d862b0..562235c1445 100644
--- a/drivers/block/cciss.h
+++ b/drivers/block/cciss.h
@@ -76,6 +76,9 @@ struct ctlr_info
unsigned int intr[4];
unsigned int msix_vector;
unsigned int msi_vector;
+ BYTE cciss_read;
+ BYTE cciss_write;
+ BYTE cciss_read_capacity;
// information about each logical volume
drive_info_struct drv[CISS_MAX_LUN];
diff --git a/drivers/block/cciss_cmd.h b/drivers/block/cciss_cmd.h
index 53fea549ba8..4af7c4c0c7a 100644
--- a/drivers/block/cciss_cmd.h
+++ b/drivers/block/cciss_cmd.h
@@ -118,11 +118,34 @@ typedef struct _ReadCapdata_struct
BYTE block_size[4]; // Size of blocks in bytes
} ReadCapdata_struct;
-// 12 byte commands not implemented in firmware yet.
-// #define CCISS_READ 0xa8 // Read(12)
-// #define CCISS_WRITE 0xaa // Write(12)
- #define CCISS_READ 0x28 // Read(10)
- #define CCISS_WRITE 0x2a // Write(10)
+#define CCISS_READ_CAPACITY_16 0x9e /* Read Capacity 16 */
+
+/* service action to differentiate a 16 byte read capacity from
+ other commands that use the 0x9e SCSI op code */
+
+#define CCISS_READ_CAPACITY_16_SERVICE_ACT 0x10
+
+typedef struct _ReadCapdata_struct_16
+{
+ BYTE total_size[8]; /* Total size in blocks */
+ BYTE block_size[4]; /* Size of blocks in bytes */
+ BYTE prot_en:1; /* protection enable bit */
+ BYTE rto_en:1; /* reference tag own enable bit */
+ BYTE reserved:6; /* reserved bits */
+ BYTE reserved2[18]; /* reserved bytes per spec */
+} ReadCapdata_struct_16;
+
+/* Define the supported read/write commands for cciss based controllers */
+
+#define CCISS_READ_10 0x28 /* Read(10) */
+#define CCISS_WRITE_10 0x2a /* Write(10) */
+#define CCISS_READ_16 0x88 /* Read(16) */
+#define CCISS_WRITE_16 0x8a /* Write(16) */
+
+/* Define the CDB lengths supported by cciss based controllers */
+
+#define CDB_LEN10 10
+#define CDB_LEN16 16
// BMIC commands
#define BMIC_READ 0x26
diff --git a/drivers/block/cciss_scsi.c b/drivers/block/cciss_scsi.c
index 05f79d7393f..bb15051ffbe 100644
--- a/drivers/block/cciss_scsi.c
+++ b/drivers/block/cciss_scsi.c
@@ -766,7 +766,7 @@ cciss_scsi_do_simple_cmd(ctlr_info_t *c,
int direction)
{
unsigned long flags;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
cp->cmd_type = CMD_IOCTL_PEND; // treat this like an ioctl
cp->scsi_cmd = NULL;
diff --git a/drivers/block/cpqarray.c b/drivers/block/cpqarray.c
index 78082edc14b..4abc193314e 100644
--- a/drivers/block/cpqarray.c
+++ b/drivers/block/cpqarray.c
@@ -989,7 +989,6 @@ static inline void complete_buffers(struct bio *bio, int ok)
xbh = bio->bi_next;
bio->bi_next = NULL;
- blk_finished_io(nr_sectors);
bio_endio(bio, nr_sectors << 9, ok ? 0 : -EIO);
bio = xbh;
diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c
index ad1d7065a1b..629c5769d99 100644
--- a/drivers/block/floppy.c
+++ b/drivers/block/floppy.c
@@ -2991,8 +2991,8 @@ static void do_fd_request(request_queue_t * q)
if (usage_count == 0) {
printk("warning: usage count=0, current_req=%p exiting\n",
current_req);
- printk("sect=%ld flags=%lx\n", (long)current_req->sector,
- current_req->flags);
+ printk("sect=%ld type=%x flags=%x\n", (long)current_req->sector,
+ current_req->cmd_type, current_req->cmd_flags);
return;
}
if (test_bit(0, &fdc_busy)) {
diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 7b3b94ddddc..d6bb8da955a 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -66,12 +66,14 @@
#include <linux/swap.h>
#include <linux/slab.h>
#include <linux/loop.h>
+#include <linux/compat.h>
#include <linux/suspend.h>
#include <linux/writeback.h>
#include <linux/buffer_head.h> /* for invalidate_bdev() */
#include <linux/completion.h>
#include <linux/highmem.h>
#include <linux/gfp.h>
+#include <linux/kthread.h>
#include <asm/uaccess.h>
@@ -522,15 +524,12 @@ static int loop_make_request(request_queue_t *q, struct bio *old_bio)
goto out;
if (unlikely(rw == WRITE && (lo->lo_flags & LO_FLAGS_READ_ONLY)))
goto out;
- lo->lo_pending++;
loop_add_bio(lo, old_bio);
+ wake_up(&lo->lo_event);
spin_unlock_irq(&lo->lo_lock);
- complete(&lo->lo_bh_done);
return 0;
out:
- if (lo->lo_pending == 0)
- complete(&lo->lo_bh_done);
spin_unlock_irq(&lo->lo_lock);
bio_io_error(old_bio, old_bio->bi_size);
return 0;
@@ -570,14 +569,18 @@ static inline void loop_handle_bio(struct loop_device *lo, struct bio *bio)
* to avoid blocking in our make_request_fn. it also does loop decrypting
* on reads for block backed loop, as that is too heavy to do from
* b_end_io context where irqs may be disabled.
+ *
+ * Loop explanation: loop_clr_fd() sets lo_state to Lo_rundown before
+ * calling kthread_stop(). Therefore once kthread_should_stop() is
+ * true, make_request will not place any more requests. Therefore
+ * once kthread_should_stop() is true and lo_bio is NULL, we are
+ * done with the loop.
*/
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
@@ -587,47 +590,21 @@ static int loop_thread(void *data)
set_user_nice(current, -20);
- lo->lo_state = Lo_bound;
- lo->lo_pending = 1;
+ while (!kthread_should_stop() || lo->lo_bio) {
- /*
- * complete it, we are running
- */
- complete(&lo->lo_done);
-
- for (;;) {
- int pending;
+ wait_event_interruptible(lo->lo_event,
+ lo->lo_bio || kthread_should_stop());
- if (wait_for_completion_interruptible(&lo->lo_bh_done))
+ if (!lo->lo_bio)
continue;
-
spin_lock_irq(&lo->lo_lock);
-
- /*
- * could be completed because of tear-down, not pending work
- */
- if (unlikely(!lo->lo_pending)) {
- spin_unlock_irq(&lo->lo_lock);
- break;
- }
-
bio = loop_get_bio(lo);
- lo->lo_pending--;
- pending = lo->lo_pending;
spin_unlock_irq(&lo->lo_lock);
BUG_ON(!bio);
loop_handle_bio(lo, bio);
-
- /*
- * upped both for pending work and tear-down, lo_pending
- * will hit zero then
- */
- if (unlikely(!pending))
- break;
}
- complete(&lo->lo_done);
return 0;
}
@@ -662,7 +639,8 @@ static void do_loop_switch(struct loop_device *lo, struct switch_request *p)
mapping_set_gfp_mask(old_file->f_mapping, lo->old_gfp_mask);
lo->lo_backing_file = file;
- lo->lo_blocksize = mapping->host->i_blksize;
+ lo->lo_blocksize = S_ISBLK(mapping->host->i_mode) ?
+ mapping->host->i_bdev->bd_block_size : PAGE_SIZE;
lo->old_gfp_mask = mapping_gfp_mask(mapping);
mapping_set_gfp_mask(mapping, lo->old_gfp_mask & ~(__GFP_IO|__GFP_FS));
complete(&p->wait);
@@ -794,7 +772,9 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
if (!(lo_flags & LO_FLAGS_USE_AOPS) && !file->f_op->write)
lo_flags |= LO_FLAGS_READ_ONLY;
- lo_blocksize = inode->i_blksize;
+ lo_blocksize = S_ISBLK(inode->i_mode) ?
+ inode->i_bdev->bd_block_size : PAGE_SIZE;
+
error = 0;
} else {
goto out_putf;
@@ -837,12 +817,26 @@ static int loop_set_fd(struct loop_device *lo, struct file *lo_file,
set_blocksize(bdev, lo_blocksize);
- error = kernel_thread(loop_thread, lo, CLONE_KERNEL);
- if (error < 0)
- goto out_putf;
- wait_for_completion(&lo->lo_done);
+ lo->lo_thread = kthread_create(loop_thread, lo, "loop%d",
+ lo->lo_number);
+ if (IS_ERR(lo->lo_thread)) {
+ error = PTR_ERR(lo->lo_thread);
+ goto out_clr;
+ }
+ lo->lo_state = Lo_bound;
+ wake_up_process(lo->lo_thread);
return 0;
+out_clr:
+ lo->lo_thread = NULL;
+ lo->lo_device = NULL;
+ lo->lo_backing_file = NULL;
+ lo->lo_flags = 0;
+ set_capacity(disks[lo->lo_number], 0);
+ invalidate_bdev(bdev, 0);
+ bd_set_size(bdev, 0);
+ mapping_set_gfp_mask(mapping, lo->old_gfp_mask);
+ lo->lo_state = Lo_unbound;
out_putf:
fput(file);
out:
@@ -904,12 +898,9 @@ static int loop_clr_fd(struct loop_device *lo, struct block_device *bdev)
spin_lock_irq(&lo->lo_lock);
lo->lo_state = Lo_rundown;
- lo->lo_pending--;
- if (!lo->lo_pending)
- complete(&lo->lo_bh_done);
spin_unlock_irq(&lo->lo_lock);
- wait_for_completion(&lo->lo_done);
+ kthread_stop(lo->lo_thread);
lo->lo_backing_file = NULL;
@@ -922,6 +913,7 @@ 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);
@@ -1174,6 +1166,162 @@ static int lo_ioctl(struct inode * inode, struct file * file,
return err;
}
+#ifdef CONFIG_COMPAT
+struct compat_loop_info {
+ compat_int_t lo_number; /* ioctl r/o */
+ compat_dev_t lo_device; /* ioctl r/o */
+ compat_ulong_t lo_inode; /* ioctl r/o */
+ compat_dev_t lo_rdevice; /* ioctl r/o */
+ compat_int_t lo_offset;
+ compat_int_t lo_encrypt_type;
+ compat_int_t lo_encrypt_key_size; /* ioctl w/o */
+ compat_int_t lo_flags; /* ioctl r/o */
+ char lo_name[LO_NAME_SIZE];
+ unsigned char lo_encrypt_key[LO_KEY_SIZE]; /* ioctl w/o */
+ compat_ulong_t lo_init[2];
+ char reserved[4];
+};
+
+/*
+ * Transfer 32-bit compatibility structure in userspace to 64-bit loop info
+ * - noinlined to reduce stack space usage in main part of driver
+ */
+static noinline int
+loop_info64_from_compat(const struct compat_loop_info *arg,
+ struct loop_info64 *info64)
+{
+ struct compat_loop_info info;
+
+ if (copy_from_user(&info, arg, sizeof(info)))
+ return -EFAULT;
+
+ memset(info64, 0, sizeof(*info64));
+ info64->lo_number = info.lo_number;
+ info64->lo_device = info.lo_device;
+ info64->lo_inode = info.lo_inode;
+ info64->lo_rdevice = info.lo_rdevice;
+ info64->lo_offset = info.lo_offset;
+ info64->lo_sizelimit = 0;
+ info64->lo_encrypt_type = info.lo_encrypt_type;
+ info64->lo_encrypt_key_size = info.lo_encrypt_key_size;
+ info64->lo_flags = info.lo_flags;
+ info64->lo_init[0] = info.lo_init[0];
+ info64->lo_init[1] = info.lo_init[1];
+ if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+ memcpy(info64->lo_crypt_name, info.lo_name, LO_NAME_SIZE);
+ else
+ memcpy(info64->lo_file_name, info.lo_name, LO_NAME_SIZE);
+ memcpy(info64->lo_encrypt_key, info.lo_encrypt_key, LO_KEY_SIZE);
+ return 0;
+}
+
+/*
+ * Transfer 64-bit loop info to 32-bit compatibility structure in userspace
+ * - noinlined to reduce stack space usage in main part of driver
+ */
+static noinline int
+loop_info64_to_compat(const struct loop_info64 *info64,
+ struct compat_loop_info __user *arg)
+{
+ struct compat_loop_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.lo_number = info64->lo_number;
+ info.lo_device = info64->lo_device;
+ info.lo_inode = info64->lo_inode;
+ info.lo_rdevice = info64->lo_rdevice;
+ info.lo_offset = info64->lo_offset;
+ info.lo_encrypt_type = info64->lo_encrypt_type;
+ info.lo_encrypt_key_size = info64->lo_encrypt_key_size;
+ info.lo_flags = info64->lo_flags;
+ info.lo_init[0] = info64->lo_init[0];
+ info.lo_init[1] = info64->lo_init[1];
+ if (info.lo_encrypt_type == LO_CRYPT_CRYPTOAPI)
+ memcpy(info.lo_name, info64->lo_crypt_name, LO_NAME_SIZE);
+ else
+ memcpy(info.lo_name, info64->lo_file_name, LO_NAME_SIZE);
+ memcpy(info.lo_encrypt_key, info64->lo_encrypt_key, LO_KEY_SIZE);
+
+ /* error in case values were truncated */
+ if (info.lo_device != info64->lo_device ||
+ info.lo_rdevice != info64->lo_rdevice ||
+ info.lo_inode != info64->lo_inode ||
+ info.lo_offset != info64->lo_offset ||
+ info.lo_init[0] != info64->lo_init[0] ||
+ info.lo_init[1] != info64->lo_init[1])
+ return -EOVERFLOW;
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+ return 0;
+}
+
+static int
+loop_set_status_compat(struct loop_device *lo,
+ const struct compat_loop_info __user *arg)
+{
+ struct loop_info64 info64;
+ int ret;
+
+ ret = loop_info64_from_compat(arg, &info64);
+ if (ret < 0)
+ return ret;
+ return loop_set_status(lo, &info64);
+}
+
+static int
+loop_get_status_compat(struct loop_device *lo,
+ struct compat_loop_info __user *arg)
+{
+ struct loop_info64 info64;
+ int err = 0;
+
+ if (!arg)
+ err = -EINVAL;
+ if (!err)
+ err = loop_get_status(lo, &info64);
+ if (!err)
+ err = loop_info64_to_compat(&info64, arg);
+ return err;
+}
+
+static long lo_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
+ int err;
+
+ lock_kernel();
+ switch(cmd) {
+ case LOOP_SET_STATUS:
+ mutex_lock(&lo->lo_ctl_mutex);
+ err = loop_set_status_compat(
+ lo, (const struct compat_loop_info __user *) arg);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ break;
+ case LOOP_GET_STATUS:
+ mutex_lock(&lo->lo_ctl_mutex);
+ err = loop_get_status_compat(
+ lo, (struct compat_loop_info __user *) arg);
+ mutex_unlock(&lo->lo_ctl_mutex);
+ break;
+ case LOOP_CLR_FD:
+ case LOOP_GET_STATUS64:
+ case LOOP_SET_STATUS64:
+ arg = (unsigned long) compat_ptr(arg);
+ case LOOP_SET_FD:
+ case LOOP_CHANGE_FD:
+ err = lo_ioctl(inode, file, cmd, arg);
+ break;
+ default:
+ err = -ENOIOCTLCMD;
+ break;
+ }
+ unlock_kernel();
+ return err;
+}
+#endif
+
static int lo_open(struct inode *inode, struct file *file)
{
struct loop_device *lo = inode->i_bdev->bd_disk->private_data;
@@ -1201,6 +1349,9 @@ static struct block_device_operations lo_fops = {
.open = lo_open,
.release = lo_release,
.ioctl = lo_ioctl,
+#ifdef CONFIG_COMPAT
+ .compat_ioctl = lo_compat_ioctl,
+#endif
};
/*
@@ -1284,9 +1435,9 @@ 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;
+ lo->lo_thread = NULL;
+ init_waitqueue_head(&lo->lo_event);
spin_lock_init(&lo->lo_lock);
disk->major = LOOP_MAJOR;
disk->first_minor = i;
diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c
index bdbade9a5cf..9d1035e8d9d 100644
--- a/drivers/block/nbd.c
+++ b/drivers/block/nbd.c
@@ -407,10 +407,10 @@ static void do_nbd_request(request_queue_t * q)
struct nbd_device *lo;
blkdev_dequeue_request(req);
- dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%lx)\n",
- req->rq_disk->disk_name, req, req->flags);
+ dprintk(DBG_BLKDEV, "%s: request %p: dequeued (flags=%x)\n",
+ req->rq_disk->disk_name, req, req->cmd_type);
- if (!(req->flags & REQ_CMD))
+ if (!blk_fs_request(req))
goto error_out;
lo = req->rq_disk->private_data;
@@ -489,7 +489,7 @@ static int nbd_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case NBD_DISCONNECT:
printk(KERN_INFO "%s: NBD_DISCONNECT\n", lo->disk->disk_name);
- sreq.flags = REQ_SPECIAL;
+ sreq.cmd_type = REQ_TYPE_SPECIAL;
nbd_cmd(&sreq) = NBD_CMD_DISC;
/*
* Set these to sane values in case server implementation
diff --git a/drivers/block/paride/pd.c b/drivers/block/paride/pd.c
index 2403721f9db..40a11e56797 100644
--- a/drivers/block/paride/pd.c
+++ b/drivers/block/paride/pd.c
@@ -437,7 +437,7 @@ static char *pd_buf; /* buffer for request in progress */
static enum action do_pd_io_start(void)
{
- if (pd_req->flags & REQ_SPECIAL) {
+ if (blk_special_request(pd_req)) {
phase = pd_special;
return pd_special();
}
@@ -713,20 +713,18 @@ static void do_pd_request(request_queue_t * q)
static int pd_special_command(struct pd_unit *disk,
enum action (*func)(struct pd_unit *disk))
{
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
struct request rq;
int err = 0;
memset(&rq, 0, sizeof(rq));
rq.errors = 0;
- rq.rq_status = RQ_ACTIVE;
rq.rq_disk = disk->gd;
rq.ref_count = 1;
- rq.waiting = &wait;
+ rq.end_io_data = &wait;
rq.end_io = blk_end_sync_rq;
blk_insert_request(disk->gd->queue, &rq, 0, func);
wait_for_completion(&wait);
- rq.waiting = NULL;
if (rq.errors)
err = -EIO;
blk_put_request(&rq);
diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c
index 451b996bba9..a6b2aa67c9b 100644
--- a/drivers/block/pktcdvd.c
+++ b/drivers/block/pktcdvd.c
@@ -348,7 +348,7 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *
char sense[SCSI_SENSE_BUFFERSIZE];
request_queue_t *q;
struct request *rq;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
int err = 0;
q = bdev_get_queue(pd->bdev);
@@ -365,17 +365,17 @@ static int pkt_generic_packet(struct pktcdvd_device *pd, struct packet_command *
rq->sense = sense;
memset(sense, 0, sizeof(sense));
rq->sense_len = 0;
- rq->flags |= REQ_BLOCK_PC | REQ_HARDBARRIER;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
+ rq->cmd_flags |= REQ_HARDBARRIER;
if (cgc->quiet)
- rq->flags |= REQ_QUIET;
+ rq->cmd_flags |= REQ_QUIET;
memcpy(rq->cmd, cgc->cmd, CDROM_PACKET_SIZE);
if (sizeof(rq->cmd) > CDROM_PACKET_SIZE)
memset(rq->cmd + CDROM_PACKET_SIZE, 0, sizeof(rq->cmd) - CDROM_PACKET_SIZE);
rq->cmd_len = COMMAND_SIZE(rq->cmd[0]);
rq->ref_count++;
- rq->flags |= REQ_NOMERGE;
- rq->waiting = &wait;
+ rq->end_io_data = &wait;
rq->end_io = blk_end_sync_rq;
elv_add_request(q, rq, ELEVATOR_INSERT_BACK, 1);
generic_unplug_device(q);
diff --git a/drivers/block/swim3.c b/drivers/block/swim3.c
index cc42e762396..f2305ee792a 100644
--- a/drivers/block/swim3.c
+++ b/drivers/block/swim3.c
@@ -319,8 +319,8 @@ static void start_request(struct floppy_state *fs)
printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
req->rq_disk->disk_name, req->cmd,
(long)req->sector, req->nr_sectors, req->buffer);
- printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n",
- req->rq_status, req->errors, req->current_nr_sectors);
+ printk(" errors=%d current_nr_sectors=%ld\n",
+ req->errors, req->current_nr_sectors);
#endif
if (req->sector < 0 || req->sector >= fs->total_secs) {
diff --git a/drivers/block/swim_iop.c b/drivers/block/swim_iop.c
index 89e3c2f8b77..dfda796eba5 100644
--- a/drivers/block/swim_iop.c
+++ b/drivers/block/swim_iop.c
@@ -529,8 +529,8 @@ static void start_request(struct floppy_state *fs)
printk("do_fd_req: dev=%s cmd=%d sec=%ld nr_sec=%ld buf=%p\n",
CURRENT->rq_disk->disk_name, CURRENT->cmd,
CURRENT->sector, CURRENT->nr_sectors, CURRENT->buffer);
- printk(" rq_status=%d errors=%d current_nr_sectors=%ld\n",
- CURRENT->rq_status, CURRENT->errors, CURRENT->current_nr_sectors);
+ printk(" errors=%d current_nr_sectors=%ld\n",
+ CURRENT->errors, CURRENT->current_nr_sectors);
#endif
if (CURRENT->sector < 0 || CURRENT->sector >= fs->total_secs) {
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index d62b49fbf10..45a8f402b07 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -358,7 +358,7 @@ static void ub_cmd_build_block(struct ub_dev *sc, struct ub_lun *lun,
static void ub_cmd_build_packet(struct ub_dev *sc, struct ub_lun *lun,
struct ub_scsi_cmd *cmd, struct ub_request *urq);
static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
-static void ub_end_rq(struct request *rq, int uptodate);
+static void ub_end_rq(struct request *rq, unsigned int status);
static int ub_rw_cmd_retry(struct ub_dev *sc, struct ub_lun *lun,
struct ub_request *urq, struct ub_scsi_cmd *cmd);
static int ub_submit_scsi(struct ub_dev *sc, struct ub_scsi_cmd *cmd);
@@ -639,9 +639,15 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
struct ub_request *urq;
int n_elem;
- if (atomic_read(&sc->poison) || lun->changed) {
+ if (atomic_read(&sc->poison)) {
+ blkdev_dequeue_request(rq);
+ ub_end_rq(rq, DID_NO_CONNECT << 16);
+ return 0;
+ }
+
+ if (lun->changed && !blk_pc_request(rq)) {
blkdev_dequeue_request(rq);
- ub_end_rq(rq, 0);
+ ub_end_rq(rq, SAM_STAT_CHECK_CONDITION);
return 0;
}
@@ -693,7 +699,7 @@ static int ub_request_fn_1(struct ub_lun *lun, struct request *rq)
drop:
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, 0);
+ ub_end_rq(rq, DID_ERROR << 16);
return 0;
}
@@ -761,47 +767,53 @@ static void ub_rw_cmd_done(struct ub_dev *sc, struct ub_scsi_cmd *cmd)
struct ub_lun *lun = cmd->lun;
struct ub_request *urq = cmd->back;
struct request *rq;
- int uptodate;
+ unsigned int scsi_status;
rq = urq->rq;
if (cmd->error == 0) {
- uptodate = 1;
-
if (blk_pc_request(rq)) {
if (cmd->act_len >= rq->data_len)
rq->data_len = 0;
else
rq->data_len -= cmd->act_len;
}
+ scsi_status = 0;
} else {
- uptodate = 0;
-
if (blk_pc_request(rq)) {
/* UB_SENSE_SIZE is smaller than SCSI_SENSE_BUFFERSIZE */
memcpy(rq->sense, sc->top_sense, UB_SENSE_SIZE);
rq->sense_len = UB_SENSE_SIZE;
if (sc->top_sense[0] != 0)
- rq->errors = SAM_STAT_CHECK_CONDITION;
+ scsi_status = SAM_STAT_CHECK_CONDITION;
else
- rq->errors = DID_ERROR << 16;
+ scsi_status = DID_ERROR << 16;
} else {
if (cmd->error == -EIO) {
if (ub_rw_cmd_retry(sc, lun, urq, cmd) == 0)
return;
}
+ scsi_status = SAM_STAT_CHECK_CONDITION;
}
}
urq->rq = NULL;
ub_put_cmd(lun, cmd);
- ub_end_rq(rq, uptodate);
+ ub_end_rq(rq, scsi_status);
blk_start_queue(lun->disk->queue);
}
-static void ub_end_rq(struct request *rq, int uptodate)
+static void ub_end_rq(struct request *rq, unsigned int scsi_status)
{
+ int uptodate;
+
+ if (scsi_status == 0) {
+ uptodate = 1;
+ } else {
+ uptodate = 0;
+ rq->errors = scsi_status;
+ }
end_that_request_first(rq, uptodate, rq->hard_nr_sectors);
end_that_request_last(rq, uptodate);
}
diff --git a/drivers/block/umem.c b/drivers/block/umem.c
index 5d8925bd904..cbb9d0f21ac 100644
--- a/drivers/block/umem.c
+++ b/drivers/block/umem.c
@@ -552,7 +552,8 @@ static void process_page(unsigned long data)
static int mm_make_request(request_queue_t *q, struct bio *bio)
{
struct cardinfo *card = q->queuedata;
- pr_debug("mm_make_request %ld %d\n", bh->b_rsector, bh->b_size);
+ pr_debug("mm_make_request %llu %u\n",
+ (unsigned long long)bio->bi_sector, bio->bi_size);
bio->bi_phys_segments = bio->bi_idx; /* count of completed segments*/
spin_lock_irq(&card->lock);
diff --git a/drivers/block/xd.c b/drivers/block/xd.c
index e828e4cbd3e..ebf3025721d 100644
--- a/drivers/block/xd.c
+++ b/drivers/block/xd.c
@@ -313,7 +313,7 @@ static void do_xd_request (request_queue_t * q)
int res = 0;
int retry;
- if (!(req->flags & REQ_CMD)) {
+ if (!blk_fs_request(req)) {
end_request(req, 0);
continue;
}
diff --git a/drivers/bluetooth/bfusb.c b/drivers/bluetooth/bfusb.c
index 23f96213f4a..efcc28ec9d9 100644
--- a/drivers/bluetooth/bfusb.c
+++ b/drivers/bluetooth/bfusb.c
@@ -2,7 +2,7 @@
*
* AVM BlueFRITZ! USB driver
*
- * Copyright (C) 2003 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2003-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -59,7 +59,6 @@ static struct usb_device_id bfusb_table[] = {
MODULE_DEVICE_TABLE(usb, bfusb_table);
-
#define BFUSB_MAX_BLOCK_SIZE 256
#define BFUSB_BLOCK_TIMEOUT 3000
@@ -70,7 +69,7 @@ MODULE_DEVICE_TABLE(usb, bfusb_table);
#define BFUSB_MAX_BULK_TX 2
#define BFUSB_MAX_BULK_RX 2
-struct bfusb {
+struct bfusb_data {
struct hci_dev *hdev;
unsigned long state;
@@ -92,137 +91,136 @@ struct bfusb {
struct sk_buff_head completed_q;
};
-struct bfusb_scb {
+struct bfusb_data_scb {
struct urb *urb;
};
static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs);
static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs);
-static struct urb *bfusb_get_completed(struct bfusb *bfusb)
+static struct urb *bfusb_get_completed(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb = NULL;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- skb = skb_dequeue(&bfusb->completed_q);
+ skb = skb_dequeue(&data->completed_q);
if (skb) {
- urb = ((struct bfusb_scb *) skb->cb)->urb;
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
kfree_skb(skb);
}
return urb;
}
-static void bfusb_unlink_urbs(struct bfusb *bfusb)
+static void bfusb_unlink_urbs(struct bfusb_data *data)
{
struct sk_buff *skb;
struct urb *urb;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- while ((skb = skb_dequeue(&bfusb->pending_q))) {
- urb = ((struct bfusb_scb *) skb->cb)->urb;
+ while ((skb = skb_dequeue(&data->pending_q))) {
+ urb = ((struct bfusb_data_scb *) skb->cb)->urb;
usb_kill_urb(urb);
- skb_queue_tail(&bfusb->completed_q, skb);
+ skb_queue_tail(&data->completed_q, skb);
}
- while ((urb = bfusb_get_completed(bfusb)))
+ while ((urb = bfusb_get_completed(data)))
usb_free_urb(urb);
}
-
-static int bfusb_send_bulk(struct bfusb *bfusb, struct sk_buff *skb)
+static int bfusb_send_bulk(struct bfusb_data *data, struct sk_buff *skb)
{
- struct bfusb_scb *scb = (void *) skb->cb;
- struct urb *urb = bfusb_get_completed(bfusb);
+ struct bfusb_data_scb *scb = (void *) skb->cb;
+ struct urb *urb = bfusb_get_completed(data);
int err, pipe;
- BT_DBG("bfusb %p skb %p len %d", bfusb, skb, skb->len);
+ BT_DBG("bfusb %p skb %p len %d", data, skb, skb->len);
if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
return -ENOMEM;
- pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
- usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, skb->len,
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, skb->len,
bfusb_tx_complete, skb);
scb->urb = urb;
- skb_queue_tail(&bfusb->pending_q, skb);
+ skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk tx submit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
- skb_unlink(skb, &bfusb->pending_q);
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
usb_free_urb(urb);
} else
- atomic_inc(&bfusb->pending_tx);
+ atomic_inc(&data->pending_tx);
return err;
}
-static void bfusb_tx_wakeup(struct bfusb *bfusb)
+static void bfusb_tx_wakeup(struct bfusb_data *data)
{
struct sk_buff *skb;
- BT_DBG("bfusb %p", bfusb);
+ BT_DBG("bfusb %p", data);
- if (test_and_set_bit(BFUSB_TX_PROCESS, &bfusb->state)) {
- set_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+ if (test_and_set_bit(BFUSB_TX_PROCESS, &data->state)) {
+ set_bit(BFUSB_TX_WAKEUP, &data->state);
return;
}
do {
- clear_bit(BFUSB_TX_WAKEUP, &bfusb->state);
+ clear_bit(BFUSB_TX_WAKEUP, &data->state);
- while ((atomic_read(&bfusb->pending_tx) < BFUSB_MAX_BULK_TX) &&
- (skb = skb_dequeue(&bfusb->transmit_q))) {
- if (bfusb_send_bulk(bfusb, skb) < 0) {
- skb_queue_head(&bfusb->transmit_q, skb);
+ while ((atomic_read(&data->pending_tx) < BFUSB_MAX_BULK_TX) &&
+ (skb = skb_dequeue(&data->transmit_q))) {
+ if (bfusb_send_bulk(data, skb) < 0) {
+ skb_queue_head(&data->transmit_q, skb);
break;
}
}
- } while (test_bit(BFUSB_TX_WAKEUP, &bfusb->state));
+ } while (test_bit(BFUSB_TX_WAKEUP, &data->state));
- clear_bit(BFUSB_TX_PROCESS, &bfusb->state);
+ clear_bit(BFUSB_TX_PROCESS, &data->state);
}
static void bfusb_tx_complete(struct urb *urb, struct pt_regs *regs)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct bfusb *bfusb = (struct bfusb *) skb->dev;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
- BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
+ BT_DBG("bfusb %p urb %p skb %p len %d", data, urb, skb, skb->len);
- atomic_dec(&bfusb->pending_tx);
+ atomic_dec(&data->pending_tx);
- if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags))
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
return;
if (!urb->status)
- bfusb->hdev->stat.byte_tx += skb->len;
+ data->hdev->stat.byte_tx += skb->len;
else
- bfusb->hdev->stat.err_tx++;
+ data->hdev->stat.err_tx++;
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- skb_unlink(skb, &bfusb->pending_q);
- skb_queue_tail(&bfusb->completed_q, skb);
+ skb_unlink(skb, &data->pending_q);
+ skb_queue_tail(&data->completed_q, skb);
- bfusb_tx_wakeup(bfusb);
+ bfusb_tx_wakeup(data);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
}
-static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
+static int bfusb_rx_submit(struct bfusb_data *data, struct urb *urb)
{
- struct bfusb_scb *scb;
+ struct bfusb_data_scb *scb;
struct sk_buff *skb;
int err, pipe, size = HCI_MAX_FRAME_SIZE + 32;
@@ -231,28 +229,29 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
if (!urb && !(urb = usb_alloc_urb(0, GFP_ATOMIC)))
return -ENOMEM;
- if (!(skb = bt_skb_alloc(size, GFP_ATOMIC))) {
+ skb = bt_skb_alloc(size, GFP_ATOMIC);
+ if (!skb) {
usb_free_urb(urb);
return -ENOMEM;
}
- skb->dev = (void *) bfusb;
+ skb->dev = (void *) data;
- scb = (struct bfusb_scb *) skb->cb;
+ scb = (struct bfusb_data_scb *) skb->cb;
scb->urb = urb;
- pipe = usb_rcvbulkpipe(bfusb->udev, bfusb->bulk_in_ep);
+ pipe = usb_rcvbulkpipe(data->udev, data->bulk_in_ep);
- usb_fill_bulk_urb(urb, bfusb->udev, pipe, skb->data, size,
+ usb_fill_bulk_urb(urb, data->udev, pipe, skb->data, size,
bfusb_rx_complete, skb);
- skb_queue_tail(&bfusb->pending_q, skb);
+ skb_queue_tail(&data->pending_q, skb);
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk rx submit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
- skb_unlink(skb, &bfusb->pending_q);
+ data->hdev->name, urb, err);
+ skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
usb_free_urb(urb);
}
@@ -260,15 +259,15 @@ static int bfusb_rx_submit(struct bfusb *bfusb, struct urb *urb)
return err;
}
-static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *data, int len)
+static inline int bfusb_recv_block(struct bfusb_data *data, int hdr, unsigned char *buf, int len)
{
- BT_DBG("bfusb %p hdr 0x%02x data %p len %d", bfusb, hdr, data, len);
+ BT_DBG("bfusb %p hdr 0x%02x data %p len %d", data, hdr, buf, len);
if (hdr & 0x10) {
- BT_ERR("%s error in block", bfusb->hdev->name);
- if (bfusb->reassembly)
- kfree_skb(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ BT_ERR("%s error in block", data->hdev->name);
+ if (data->reassembly)
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
return -EIO;
}
@@ -277,46 +276,46 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
unsigned char pkt_type;
int pkt_len = 0;
- if (bfusb->reassembly) {
- BT_ERR("%s unexpected start block", bfusb->hdev->name);
- kfree_skb(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ if (data->reassembly) {
+ BT_ERR("%s unexpected start block", data->hdev->name);
+ kfree_skb(data->reassembly);
+ data->reassembly = NULL;
}
if (len < 1) {
- BT_ERR("%s no packet type found", bfusb->hdev->name);
+ BT_ERR("%s no packet type found", data->hdev->name);
return -EPROTO;
}
- pkt_type = *data++; len--;
+ pkt_type = *buf++; len--;
switch (pkt_type) {
case HCI_EVENT_PKT:
if (len >= HCI_EVENT_HDR_SIZE) {
- struct hci_event_hdr *hdr = (struct hci_event_hdr *) data;
+ struct hci_event_hdr *hdr = (struct hci_event_hdr *) buf;
pkt_len = HCI_EVENT_HDR_SIZE + hdr->plen;
} else {
- BT_ERR("%s event block is too short", bfusb->hdev->name);
+ BT_ERR("%s event block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_ACLDATA_PKT:
if (len >= HCI_ACL_HDR_SIZE) {
- struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) data;
+ struct hci_acl_hdr *hdr = (struct hci_acl_hdr *) buf;
pkt_len = HCI_ACL_HDR_SIZE + __le16_to_cpu(hdr->dlen);
} else {
- BT_ERR("%s data block is too short", bfusb->hdev->name);
+ BT_ERR("%s data block is too short", data->hdev->name);
return -EILSEQ;
}
break;
case HCI_SCODATA_PKT:
if (len >= HCI_SCO_HDR_SIZE) {
- struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) data;
+ struct hci_sco_hdr *hdr = (struct hci_sco_hdr *) buf;
pkt_len = HCI_SCO_HDR_SIZE + hdr->dlen;
} else {
- BT_ERR("%s audio block is too short", bfusb->hdev->name);
+ BT_ERR("%s audio block is too short", data->hdev->name);
return -EILSEQ;
}
break;
@@ -324,27 +323,27 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
skb = bt_skb_alloc(pkt_len, GFP_ATOMIC);
if (!skb) {
- BT_ERR("%s no memory for the packet", bfusb->hdev->name);
+ BT_ERR("%s no memory for the packet", data->hdev->name);
return -ENOMEM;
}
- skb->dev = (void *) bfusb->hdev;
+ skb->dev = (void *) data->hdev;
bt_cb(skb)->pkt_type = pkt_type;
- bfusb->reassembly = skb;
+ data->reassembly = skb;
} else {
- if (!bfusb->reassembly) {
- BT_ERR("%s unexpected continuation block", bfusb->hdev->name);
+ if (!data->reassembly) {
+ BT_ERR("%s unexpected continuation block", data->hdev->name);
return -EIO;
}
}
if (len > 0)
- memcpy(skb_put(bfusb->reassembly, len), data, len);
+ memcpy(skb_put(data->reassembly, len), buf, len);
if (hdr & 0x08) {
- hci_recv_frame(bfusb->reassembly);
- bfusb->reassembly = NULL;
+ hci_recv_frame(data->reassembly);
+ data->reassembly = NULL;
}
return 0;
@@ -353,22 +352,22 @@ static inline int bfusb_recv_block(struct bfusb *bfusb, int hdr, unsigned char *
static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs)
{
struct sk_buff *skb = (struct sk_buff *) urb->context;
- struct bfusb *bfusb = (struct bfusb *) skb->dev;
+ struct bfusb_data *data = (struct bfusb_data *) skb->dev;
unsigned char *buf = urb->transfer_buffer;
int count = urb->actual_length;
int err, hdr, len;
BT_DBG("bfusb %p urb %p skb %p len %d", bfusb, urb, skb, skb->len);
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- if (!test_bit(HCI_RUNNING, &bfusb->hdev->flags))
+ if (!test_bit(HCI_RUNNING, &data->hdev->flags))
goto unlock;
if (urb->status || !count)
goto resubmit;
- bfusb->hdev->stat.byte_rx += count;
+ data->hdev->stat.byte_rx += count;
skb_put(skb, count);
@@ -387,90 +386,89 @@ static void bfusb_rx_complete(struct urb *urb, struct pt_regs *regs)
if (count < len) {
BT_ERR("%s block extends over URB buffer ranges",
- bfusb->hdev->name);
+ data->hdev->name);
}
if ((hdr & 0xe1) == 0xc1)
- bfusb_recv_block(bfusb, hdr, buf, len);
+ bfusb_recv_block(data, hdr, buf, len);
count -= len;
buf += len;
}
- skb_unlink(skb, &bfusb->pending_q);
+ skb_unlink(skb, &data->pending_q);
kfree_skb(skb);
- bfusb_rx_submit(bfusb, urb);
+ bfusb_rx_submit(data, urb);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
return;
resubmit:
- urb->dev = bfusb->udev;
+ urb->dev = data->udev;
err = usb_submit_urb(urb, GFP_ATOMIC);
if (err) {
BT_ERR("%s bulk resubmit failed urb %p err %d",
- bfusb->hdev->name, urb, err);
+ data->hdev->name, urb, err);
}
unlock:
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
}
-
static int bfusb_open(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
unsigned long flags;
int i, err;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
if (test_and_set_bit(HCI_RUNNING, &hdev->flags))
return 0;
- write_lock_irqsave(&bfusb->lock, flags);
+ write_lock_irqsave(&data->lock, flags);
- err = bfusb_rx_submit(bfusb, NULL);
+ err = bfusb_rx_submit(data, NULL);
if (!err) {
for (i = 1; i < BFUSB_MAX_BULK_RX; i++)
- bfusb_rx_submit(bfusb, NULL);
+ bfusb_rx_submit(data, NULL);
} else {
clear_bit(HCI_RUNNING, &hdev->flags);
}
- write_unlock_irqrestore(&bfusb->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
return err;
}
static int bfusb_flush(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
- skb_queue_purge(&bfusb->transmit_q);
+ skb_queue_purge(&data->transmit_q);
return 0;
}
static int bfusb_close(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
unsigned long flags;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
- write_lock_irqsave(&bfusb->lock, flags);
- write_unlock_irqrestore(&bfusb->lock, flags);
+ write_lock_irqsave(&data->lock, flags);
+ write_unlock_irqrestore(&data->lock, flags);
- bfusb_unlink_urbs(bfusb);
+ bfusb_unlink_urbs(data);
bfusb_flush(hdev);
return 0;
@@ -479,7 +477,7 @@ static int bfusb_close(struct hci_dev *hdev)
static int bfusb_send_frame(struct sk_buff *skb)
{
struct hci_dev *hdev = (struct hci_dev *) skb->dev;
- struct bfusb *bfusb;
+ struct bfusb_data *data;
struct sk_buff *nskb;
unsigned char buf[3];
int sent = 0, size, count;
@@ -494,7 +492,7 @@ static int bfusb_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
- bfusb = (struct bfusb *) hdev->driver_data;
+ data = hdev->driver_data;
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
@@ -514,12 +512,13 @@ static int bfusb_send_frame(struct sk_buff *skb)
count = skb->len;
/* Max HCI frame size seems to be 1511 + 1 */
- if (!(nskb = bt_skb_alloc(count + 32, GFP_ATOMIC))) {
+ nskb = bt_skb_alloc(count + 32, GFP_ATOMIC);
+ if (!nskb) {
BT_ERR("Can't allocate memory for new packet");
return -ENOMEM;
}
- nskb->dev = (void *) bfusb;
+ nskb->dev = (void *) data;
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE);
@@ -536,18 +535,18 @@ static int bfusb_send_frame(struct sk_buff *skb)
}
/* Don't send frame with multiple size of bulk max packet */
- if ((nskb->len % bfusb->bulk_pkt_size) == 0) {
+ if ((nskb->len % data->bulk_pkt_size) == 0) {
buf[0] = 0xdd;
buf[1] = 0x00;
memcpy(skb_put(nskb, 2), buf, 2);
}
- read_lock(&bfusb->lock);
+ read_lock(&data->lock);
- skb_queue_tail(&bfusb->transmit_q, nskb);
- bfusb_tx_wakeup(bfusb);
+ skb_queue_tail(&data->transmit_q, nskb);
+ bfusb_tx_wakeup(data);
- read_unlock(&bfusb->lock);
+ read_unlock(&data->lock);
kfree_skb(skb);
@@ -556,11 +555,11 @@ static int bfusb_send_frame(struct sk_buff *skb)
static void bfusb_destruct(struct hci_dev *hdev)
{
- struct bfusb *bfusb = (struct bfusb *) hdev->driver_data;
+ struct bfusb_data *data = hdev->driver_data;
- BT_DBG("hdev %p bfusb %p", hdev, bfusb);
+ BT_DBG("hdev %p bfusb %p", hdev, data);
- kfree(bfusb);
+ kfree(data);
}
static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg)
@@ -568,25 +567,24 @@ static int bfusb_ioctl(struct hci_dev *hdev, unsigned int cmd, unsigned long arg
return -ENOIOCTLCMD;
}
-
-static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int count)
+static int bfusb_load_firmware(struct bfusb_data *data, unsigned char *firmware, int count)
{
unsigned char *buf;
int err, pipe, len, size, sent = 0;
- BT_DBG("bfusb %p udev %p", bfusb, bfusb->udev);
+ BT_DBG("bfusb %p udev %p", data, data->udev);
BT_INFO("BlueFRITZ! USB loading firmware");
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- if (usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ if (usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 1, 0, NULL, 0, USB_CTRL_SET_TIMEOUT) < 0) {
BT_ERR("Can't change to loading configuration");
return -EBUSY;
}
- bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0;
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
buf = kmalloc(BFUSB_MAX_BLOCK_SIZE + 3, GFP_ATOMIC);
if (!buf) {
@@ -594,14 +592,14 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
return -ENOMEM;
}
- pipe = usb_sndbulkpipe(bfusb->udev, bfusb->bulk_out_ep);
+ pipe = usb_sndbulkpipe(data->udev, data->bulk_out_ep);
while (count) {
size = min_t(uint, count, BFUSB_MAX_BLOCK_SIZE + 3);
memcpy(buf, firmware + sent, size);
- err = usb_bulk_msg(bfusb->udev, pipe, buf, size,
+ err = usb_bulk_msg(data->udev, pipe, buf, size,
&len, BFUSB_BLOCK_TIMEOUT);
if (err || (len != size)) {
@@ -613,21 +611,23 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
count -= size;
}
- if ((err = usb_bulk_msg(bfusb->udev, pipe, NULL, 0,
- &len, BFUSB_BLOCK_TIMEOUT)) < 0) {
+ err = usb_bulk_msg(data->udev, pipe, NULL, 0,
+ &len, BFUSB_BLOCK_TIMEOUT);
+ if (err < 0) {
BT_ERR("Error in null packet request");
goto error;
}
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- if ((err = usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
- 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT)) < 0) {
+ err = usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ 0, 2, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ if (err < 0) {
BT_ERR("Can't change to running configuration");
goto error;
}
- bfusb->udev->toggle[0] = bfusb->udev->toggle[1] = 0;
+ data->udev->toggle[0] = data->udev->toggle[1] = 0;
BT_INFO("BlueFRITZ! USB device ready");
@@ -637,9 +637,9 @@ static int bfusb_load_firmware(struct bfusb *bfusb, unsigned char *firmware, int
error:
kfree(buf);
- pipe = usb_sndctrlpipe(bfusb->udev, 0);
+ pipe = usb_sndctrlpipe(data->udev, 0);
- usb_control_msg(bfusb->udev, pipe, USB_REQ_SET_CONFIGURATION,
+ usb_control_msg(data->udev, pipe, USB_REQ_SET_CONFIGURATION,
0, 0, 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
return err;
@@ -652,7 +652,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
struct usb_host_endpoint *bulk_out_ep;
struct usb_host_endpoint *bulk_in_ep;
struct hci_dev *hdev;
- struct bfusb *bfusb;
+ struct bfusb_data *data;
BT_DBG("intf %p id %p", intf, id);
@@ -672,23 +672,24 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
}
/* Initialize control structure and load firmware */
- if (!(bfusb = kzalloc(sizeof(struct bfusb), GFP_KERNEL))) {
+ data = kzalloc(sizeof(struct bfusb_data), GFP_KERNEL);
+ if (!data) {
BT_ERR("Can't allocate memory for control structure");
goto done;
}
- bfusb->udev = udev;
- bfusb->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
- bfusb->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
- bfusb->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
+ data->udev = udev;
+ data->bulk_in_ep = bulk_in_ep->desc.bEndpointAddress;
+ data->bulk_out_ep = bulk_out_ep->desc.bEndpointAddress;
+ data->bulk_pkt_size = le16_to_cpu(bulk_out_ep->desc.wMaxPacketSize);
- rwlock_init(&bfusb->lock);
+ rwlock_init(&data->lock);
- bfusb->reassembly = NULL;
+ data->reassembly = NULL;
- skb_queue_head_init(&bfusb->transmit_q);
- skb_queue_head_init(&bfusb->pending_q);
- skb_queue_head_init(&bfusb->completed_q);
+ skb_queue_head_init(&data->transmit_q);
+ skb_queue_head_init(&data->pending_q);
+ skb_queue_head_init(&data->completed_q);
if (request_firmware(&firmware, "bfubase.frm", &udev->dev) < 0) {
BT_ERR("Firmware request failed");
@@ -697,7 +698,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
BT_DBG("firmware data %p size %d", firmware->data, firmware->size);
- if (bfusb_load_firmware(bfusb, firmware->data, firmware->size) < 0) {
+ if (bfusb_load_firmware(data, firmware->data, firmware->size) < 0) {
BT_ERR("Firmware loading failed");
goto release;
}
@@ -711,10 +712,10 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
goto error;
}
- bfusb->hdev = hdev;
+ data->hdev = hdev;
hdev->type = HCI_USB;
- hdev->driver_data = bfusb;
+ hdev->driver_data = data;
SET_HCIDEV_DEV(hdev, &intf->dev);
hdev->open = bfusb_open;
@@ -732,7 +733,7 @@ static int bfusb_probe(struct usb_interface *intf, const struct usb_device_id *i
goto error;
}
- usb_set_intfdata(intf, bfusb);
+ usb_set_intfdata(intf, data);
return 0;
@@ -740,7 +741,7 @@ release:
release_firmware(firmware);
error:
- kfree(bfusb);
+ kfree(data);
done:
return -EIO;
@@ -748,8 +749,8 @@ done:
static void bfusb_disconnect(struct usb_interface *intf)
{
- struct bfusb *bfusb = usb_get_intfdata(intf);
- struct hci_dev *hdev = bfusb->hdev;
+ struct bfusb_data *data = usb_get_intfdata(intf);
+ struct hci_dev *hdev = data->hdev;
BT_DBG("intf %p", intf);
@@ -779,7 +780,8 @@ static int __init bfusb_init(void)
BT_INFO("BlueFRITZ! USB driver ver %s", VERSION);
- if ((err = usb_register(&bfusb_driver)) < 0)
+ err = usb_register(&bfusb_driver);
+ if (err < 0)
BT_ERR("Failed to register BlueFRITZ! USB driver");
return err;
diff --git a/drivers/bluetooth/hci_ldisc.c b/drivers/bluetooth/hci_ldisc.c
index 93ba25b7ea3..420b645c4c9 100644
--- a/drivers/bluetooth/hci_ldisc.c
+++ b/drivers/bluetooth/hci_ldisc.c
@@ -241,15 +241,11 @@ static int hci_uart_send_frame(struct sk_buff *skb)
static void hci_uart_destruct(struct hci_dev *hdev)
{
- struct hci_uart *hu;
-
if (!hdev)
return;
BT_DBG("%s", hdev->name);
-
- hu = (struct hci_uart *) hdev->driver_data;
- kfree(hu);
+ kfree(hdev->driver_data);
}
/* ------ LDISC part ------ */
@@ -272,7 +268,7 @@ static int hci_uart_tty_open(struct tty_struct *tty)
return -EEXIST;
if (!(hu = kzalloc(sizeof(struct hci_uart), GFP_KERNEL))) {
- BT_ERR("Can't allocate controll structure");
+ BT_ERR("Can't allocate control structure");
return -ENFILE;
}
@@ -360,7 +356,7 @@ static void hci_uart_tty_wakeup(struct tty_struct *tty)
*
* Return Value: None
*/
-static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char *flags, int count)
+static void hci_uart_tty_receive(struct tty_struct *tty, const u8 *data, char *flags, int count)
{
struct hci_uart *hu = (void *)tty->disc_data;
@@ -375,7 +371,8 @@ static void hci_uart_tty_receive(struct tty_struct *tty, const __u8 *data, char
hu->hdev->stat.byte_rx += count;
spin_unlock(&hu->rx_lock);
- if (test_and_clear_bit(TTY_THROTTLED,&tty->flags) && tty->driver->unthrottle)
+ if (test_and_clear_bit(TTY_THROTTLED, &tty->flags) &&
+ tty->driver->unthrottle)
tty->driver->unthrottle(tty);
}
diff --git a/drivers/bluetooth/hci_usb.c b/drivers/bluetooth/hci_usb.c
index e2d4beac742..0801af4ad2b 100644
--- a/drivers/bluetooth/hci_usb.c
+++ b/drivers/bluetooth/hci_usb.c
@@ -96,6 +96,9 @@ static struct usb_device_id bluetooth_ids[] = {
/* Ericsson with non-standard id */
{ USB_DEVICE(0x0bdb, 0x1002) },
+ /* Canyon CN-BTU1 with HID interfaces */
+ { USB_DEVICE(0x0c10, 0x0000), .driver_info = HCI_RESET },
+
{ } /* Terminating entry */
};
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c
index aac67a3a601..a278d98a915 100644
--- a/drivers/bluetooth/hci_vhci.c
+++ b/drivers/bluetooth/hci_vhci.c
@@ -2,9 +2,9 @@
*
* Bluetooth virtual HCI driver
*
- * Copyright (C) 2000-2001 Qualcomm Incorporated
- * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
- * Copyright (C) 2004-2005 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2000-2001 Qualcomm Incorporated
+ * Copyright (C) 2002-2003 Maxim Krasnyansky <maxk@qualcomm.com>
+ * Copyright (C) 2004-2006 Marcel Holtmann <marcel@holtmann.org>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -72,21 +72,21 @@ static int vhci_open_dev(struct hci_dev *hdev)
static int vhci_close_dev(struct hci_dev *hdev)
{
- struct vhci_data *vhci = hdev->driver_data;
+ struct vhci_data *data = hdev->driver_data;
if (!test_and_clear_bit(HCI_RUNNING, &hdev->flags))
return 0;
- skb_queue_purge(&vhci->readq);
+ skb_queue_purge(&data->readq);
return 0;
}
static int vhci_flush(struct hci_dev *hdev)
{
- struct vhci_data *vhci = hdev->driver_data;
+ struct vhci_data *data = hdev->driver_data;
- skb_queue_purge(&vhci->readq);
+ skb_queue_purge(&data->readq);
return 0;
}
@@ -94,7 +94,7 @@ static int vhci_flush(struct hci_dev *hdev)
static int vhci_send_frame(struct sk_buff *skb)
{
struct hci_dev* hdev = (struct hci_dev *) skb->dev;
- struct vhci_data *vhci;
+ struct vhci_data *data;
if (!hdev) {
BT_ERR("Frame for unknown HCI device (hdev=NULL)");
@@ -104,15 +104,15 @@ static int vhci_send_frame(struct sk_buff *skb)
if (!test_bit(HCI_RUNNING, &hdev->flags))
return -EBUSY;
- vhci = hdev->driver_data;
+ data = hdev->driver_data;
memcpy(skb_push(skb, 1), &bt_cb(skb)->pkt_type, 1);
- skb_queue_tail(&vhci->readq, skb);
+ skb_queue_tail(&data->readq, skb);
- if (vhci->flags & VHCI_FASYNC)
- kill_fasync(&vhci->fasync, SIGIO, POLL_IN);
+ if (data->flags & VHCI_FASYNC)
+ kill_fasync(&data->fasync, SIGIO, POLL_IN);
- wake_up_interruptible(&vhci->read_wait);
+ wake_up_interruptible(&data->read_wait);
return 0;
}
@@ -122,7 +122,7 @@ static void vhci_destruct(struct hci_dev *hdev)
kfree(hdev->driver_data);
}
-static inline ssize_t vhci_get_user(struct vhci_data *vhci,
+static inline ssize_t vhci_get_user(struct vhci_data *data,
const char __user *buf, size_t count)
{
struct sk_buff *skb;
@@ -139,7 +139,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci,
return -EFAULT;
}
- skb->dev = (void *) vhci->hdev;
+ skb->dev = (void *) data->hdev;
bt_cb(skb)->pkt_type = *((__u8 *) skb->data);
skb_pull(skb, 1);
@@ -148,7 +148,7 @@ static inline ssize_t vhci_get_user(struct vhci_data *vhci,
return count;
}
-static inline ssize_t vhci_put_user(struct vhci_data *vhci,
+static inline ssize_t vhci_put_user(struct vhci_data *data,
struct sk_buff *skb, char __user *buf, int count)
{
char __user *ptr = buf;
@@ -161,42 +161,43 @@ static inline ssize_t vhci_put_user(struct vhci_data *vhci,
total += len;
- vhci->hdev->stat.byte_tx += len;
+ data->hdev->stat.byte_tx += len;
switch (bt_cb(skb)->pkt_type) {
case HCI_COMMAND_PKT:
- vhci->hdev->stat.cmd_tx++;
+ data->hdev->stat.cmd_tx++;
break;
case HCI_ACLDATA_PKT:
- vhci->hdev->stat.acl_tx++;
+ data->hdev->stat.acl_tx++;
break;
case HCI_SCODATA_PKT:
- vhci->hdev->stat.cmd_tx++;
+ data->hdev->stat.cmd_tx++;
break;
};
return total;
}
-static loff_t vhci_llseek(struct file * file, loff_t offset, int origin)
+static loff_t vhci_llseek(struct file *file, loff_t offset, int origin)
{
return -ESPIPE;
}
-static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, loff_t *pos)
+static ssize_t vhci_read(struct file *file,
+ char __user *buf, size_t count, loff_t *pos)
{
DECLARE_WAITQUEUE(wait, current);
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
struct sk_buff *skb;
ssize_t ret = 0;
- add_wait_queue(&vhci->read_wait, &wait);
+ add_wait_queue(&data->read_wait, &wait);
while (count) {
set_current_state(TASK_INTERRUPTIBLE);
- skb = skb_dequeue(&vhci->readq);
+ skb = skb_dequeue(&data->readq);
if (!skb) {
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
@@ -213,7 +214,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
}
if (access_ok(VERIFY_WRITE, buf, count))
- ret = vhci_put_user(vhci, skb, buf, count);
+ ret = vhci_put_user(data, skb, buf, count);
else
ret = -EFAULT;
@@ -221,7 +222,7 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
break;
}
set_current_state(TASK_RUNNING);
- remove_wait_queue(&vhci->read_wait, &wait);
+ remove_wait_queue(&data->read_wait, &wait);
return ret;
}
@@ -229,21 +230,21 @@ static ssize_t vhci_read(struct file * file, char __user * buf, size_t count, lo
static ssize_t vhci_write(struct file *file,
const char __user *buf, size_t count, loff_t *pos)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
- return vhci_get_user(vhci, buf, count);
+ return vhci_get_user(data, buf, count);
}
static unsigned int vhci_poll(struct file *file, poll_table *wait)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
- poll_wait(file, &vhci->read_wait, wait);
+ poll_wait(file, &data->read_wait, wait);
- if (!skb_queue_empty(&vhci->readq))
+ if (!skb_queue_empty(&data->readq))
return POLLIN | POLLRDNORM;
return POLLOUT | POLLWRNORM;
@@ -257,26 +258,26 @@ static int vhci_ioctl(struct inode *inode, struct file *file,
static int vhci_open(struct inode *inode, struct file *file)
{
- struct vhci_data *vhci;
+ struct vhci_data *data;
struct hci_dev *hdev;
- vhci = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
- if (!vhci)
+ data = kzalloc(sizeof(struct vhci_data), GFP_KERNEL);
+ if (!data)
return -ENOMEM;
- skb_queue_head_init(&vhci->readq);
- init_waitqueue_head(&vhci->read_wait);
+ skb_queue_head_init(&data->readq);
+ init_waitqueue_head(&data->read_wait);
hdev = hci_alloc_dev();
if (!hdev) {
- kfree(vhci);
+ kfree(data);
return -ENOMEM;
}
- vhci->hdev = hdev;
+ data->hdev = hdev;
- hdev->type = HCI_VHCI;
- hdev->driver_data = vhci;
+ hdev->type = HCI_VIRTUAL;
+ hdev->driver_data = data;
hdev->open = vhci_open_dev;
hdev->close = vhci_close_dev;
@@ -288,20 +289,20 @@ static int vhci_open(struct inode *inode, struct file *file)
if (hci_register_dev(hdev) < 0) {
BT_ERR("Can't register HCI device");
- kfree(vhci);
+ kfree(data);
hci_free_dev(hdev);
return -EBUSY;
}
- file->private_data = vhci;
+ file->private_data = data;
return nonseekable_open(inode, file);
}
static int vhci_release(struct inode *inode, struct file *file)
{
- struct vhci_data *vhci = file->private_data;
- struct hci_dev *hdev = vhci->hdev;
+ struct vhci_data *data = file->private_data;
+ struct hci_dev *hdev = data->hdev;
if (hci_unregister_dev(hdev) < 0) {
BT_ERR("Can't unregister HCI device %s", hdev->name);
@@ -316,17 +317,17 @@ static int vhci_release(struct inode *inode, struct file *file)
static int vhci_fasync(int fd, struct file *file, int on)
{
- struct vhci_data *vhci = file->private_data;
+ struct vhci_data *data = file->private_data;
int err;
- err = fasync_helper(fd, file, on, &vhci->fasync);
+ err = fasync_helper(fd, file, on, &data->fasync);
if (err < 0)
return err;
if (on)
- vhci->flags |= VHCI_FASYNC;
+ data->flags |= VHCI_FASYNC;
else
- vhci->flags &= ~VHCI_FASYNC;
+ data->flags &= ~VHCI_FASYNC;
return 0;
}
diff --git a/drivers/cdrom/Kconfig b/drivers/cdrom/Kconfig
index ff5652d4061..4b12e9031fb 100644
--- a/drivers/cdrom/Kconfig
+++ b/drivers/cdrom/Kconfig
@@ -3,7 +3,7 @@
#
menu "Old CD-ROM drivers (not SCSI, not IDE)"
- depends on ISA
+ depends on ISA && BLOCK
config CD_NO_IDESCSI
bool "Support non-SCSI/IDE/ATAPI CDROM drives"
diff --git a/drivers/cdrom/cdrom.c b/drivers/cdrom/cdrom.c
index d239cf8b20b..b38c84a7a8e 100644
--- a/drivers/cdrom/cdrom.c
+++ b/drivers/cdrom/cdrom.c
@@ -2129,7 +2129,7 @@ static int cdrom_read_cdda_bpc(struct cdrom_device_info *cdi, __u8 __user *ubuf,
rq->cmd[9] = 0xf8;
rq->cmd_len = 12;
- rq->flags |= REQ_BLOCK_PC;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->timeout = 60 * HZ;
bio = rq->bio;
diff --git a/drivers/cdrom/cdu31a.c b/drivers/cdrom/cdu31a.c
index 37bdb0163f0..ccd91c1a84b 100644
--- a/drivers/cdrom/cdu31a.c
+++ b/drivers/cdrom/cdu31a.c
@@ -1338,8 +1338,10 @@ static void do_cdu31a_request(request_queue_t * q)
}
/* WTF??? */
- if (!(req->flags & REQ_CMD))
+ if (!blk_fs_request(req)) {
+ end_request(req, 0);
continue;
+ }
if (rq_data_dir(req) == WRITE) {
end_request(req, 0);
continue;
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 52ea94b891f..bde1c665d9f 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -439,6 +439,14 @@ config SGI_MBCS
If you have an SGI Altix with an attached SABrick
say Y or M here, otherwise say N.
+config MSPEC
+ tristate "Memory special operations driver"
+ depends on IA64
+ help
+ If you have an ia64 and you want to enable memory special
+ operations support (formerly known as fetchop), say Y here,
+ otherwise say N.
+
source "drivers/serial/Kconfig"
config UNIX98_PTYS
@@ -739,7 +747,7 @@ config NVRAM
config RTC
tristate "Enhanced Real Time Clock Support"
- depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM
+ depends on !PPC && !PARISC && !IA64 && !M68K && (!SPARC || PCI) && !FRV && !ARM && !SUPERH
---help---
If you say Y here and create a character special file /dev/rtc with
major number 10 and minor number 135 using mknod ("man mknod"), you
@@ -823,14 +831,6 @@ config DS1302
will get access to the real time clock (or hardware clock) built
into your computer.
-config S3C2410_RTC
- bool "S3C2410 RTC Driver"
- depends on ARCH_S3C2410
- help
- RTC (Realtime Clock) driver for the clock inbuilt into the
- Samsung S3C2410. This can provide periodic interrupt rates
- from 1Hz to 64Hz for user programs, and wakeup from Alarm.
-
config COBALT_LCD
bool "Support for Cobalt LCD"
depends on MIPS_COBALT
@@ -1006,6 +1006,7 @@ config GPIO_VR41XX
config RAW_DRIVER
tristate "RAW driver (/dev/raw/rawN) (OBSOLETE)"
+ depends on BLOCK
help
The raw driver permits block devices to be bound to /dev/raw/rawN.
Once bound, I/O against /dev/raw/rawN uses efficient zero-copy I/O.
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 8c6dfc62152..19114df59bb 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -47,6 +47,7 @@ obj-$(CONFIG_HVC_RTAS) += hvc_rtas.o
obj-$(CONFIG_HVC_DRIVER) += hvc_console.o
obj-$(CONFIG_RAW_DRIVER) += raw.o
obj-$(CONFIG_SGI_SNSC) += snsc.o snsc_event.o
+obj-$(CONFIG_MSPEC) += mspec.o
obj-$(CONFIG_MMTIMER) += mmtimer.o
obj-$(CONFIG_VIOCONS) += viocons.o
obj-$(CONFIG_VIOTAPE) += viotape.o
@@ -68,7 +69,6 @@ obj-$(CONFIG_EFI_RTC) += efirtc.o
obj-$(CONFIG_SGI_DS1286) += ds1286.o
obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
obj-$(CONFIG_DS1302) += ds1302.o
-obj-$(CONFIG_S3C2410_RTC) += s3c2410-rtc.o
ifeq ($(CONFIG_GENERIC_NVRAM),y)
obj-$(CONFIG_NVRAM) += generic_nvram.o
else
diff --git a/drivers/char/agp/amd64-agp.c b/drivers/char/agp/amd64-agp.c
index 8cd52984cda..00b17ae3973 100644
--- a/drivers/char/agp/amd64-agp.c
+++ b/drivers/char/agp/amd64-agp.c
@@ -409,7 +409,7 @@ static int __devinit uli_agp_init(struct pci_dev *pdev)
int i;
unsigned size = amd64_fetch_size();
printk(KERN_INFO "Setting up ULi AGP.\n");
- dev1 = pci_find_slot ((unsigned int)pdev->bus->number,PCI_DEVFN(0,0));
+ dev1 = pci_get_slot (pdev->bus,PCI_DEVFN(0,0));
if (dev1 == NULL) {
printk(KERN_INFO PFX "Detected a ULi chipset, "
"but could not fine the secondary device.\n");
@@ -442,6 +442,8 @@ static int __devinit uli_agp_init(struct pci_dev *pdev)
enuscr= httfea+ (size * 1024 * 1024) - 1;
pci_write_config_dword(dev1, ULI_X86_64_HTT_FEA_REG, httfea);
pci_write_config_dword(dev1, ULI_X86_64_ENU_SCR_REG, enuscr);
+
+ pci_dev_put(dev1);
return 0;
}
@@ -466,7 +468,7 @@ static int __devinit nforce3_agp_init(struct pci_dev *pdev)
printk(KERN_INFO PFX "Setting up Nforce3 AGP.\n");
- dev1 = pci_find_slot((unsigned int)pdev->bus->number, PCI_DEVFN(11, 0));
+ dev1 = pci_get_slot(pdev->bus, PCI_DEVFN(11, 0));
if (dev1 == NULL) {
printk(KERN_INFO PFX "agpgart: Detected an NVIDIA "
"nForce3 chipset, but could not find "
@@ -510,6 +512,8 @@ static int __devinit nforce3_agp_init(struct pci_dev *pdev)
pci_write_config_dword(dev1, NVIDIA_X86_64_1_APBASE2, apbase);
pci_write_config_dword(dev1, NVIDIA_X86_64_1_APLIMIT2, aplimit);
+ pci_dev_put(dev1);
+
return 0;
}
diff --git a/drivers/char/agp/generic.c b/drivers/char/agp/generic.c
index 0dcdb363923..c3920016168 100644
--- a/drivers/char/agp/generic.c
+++ b/drivers/char/agp/generic.c
@@ -581,18 +581,21 @@ static void agp_v3_parse_one(u32 *requested_mode, u32 *bridge_agpstat, u32 *vga_
* If not, we fall back to x4 mode.
*/
if ((*bridge_agpstat & AGPSTAT3_8X) && (*vga_agpstat & AGPSTAT3_8X)) {
- printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode supported by bridge & card (x8).\n");
+ printk(KERN_INFO PFX "No AGP mode specified. Setting to highest mode "
+ "supported by bridge & card (x8).\n");
*bridge_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
*vga_agpstat &= ~(AGPSTAT3_4X | AGPSTAT3_RSVD);
} else {
printk(KERN_INFO PFX "Fell back to AGPx4 mode because");
if (!(*bridge_agpstat & AGPSTAT3_8X)) {
- printk("bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n", *bridge_agpstat, origbridge);
+ printk(KERN_INFO PFX "bridge couldn't do x8. bridge_agpstat:%x (orig=%x)\n",
+ *bridge_agpstat, origbridge);
*bridge_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
*bridge_agpstat |= AGPSTAT3_4X;
}
if (!(*vga_agpstat & AGPSTAT3_8X)) {
- printk("graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n", *vga_agpstat, origvga);
+ printk(KERN_INFO PFX "graphics card couldn't do x8. vga_agpstat:%x (orig=%x)\n",
+ *vga_agpstat, origvga);
*vga_agpstat &= ~(AGPSTAT3_8X | AGPSTAT3_RSVD);
*vga_agpstat |= AGPSTAT3_4X;
}
diff --git a/drivers/char/amiserial.c b/drivers/char/amiserial.c
index 9d6713a93ed..d0e92ed0a36 100644
--- a/drivers/char/amiserial.c
+++ b/drivers/char/amiserial.c
@@ -1958,7 +1958,7 @@ static void show_serial_version(void)
}
-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
index c1c67281750..f85b4eb1661 100644
--- a/drivers/char/cyclades.c
+++ b/drivers/char/cyclades.c
@@ -5205,7 +5205,7 @@ done:
extra ports are ignored.
*/
-static struct tty_operations cy_ops = {
+static const struct tty_operations cy_ops = {
.open = cy_open,
.close = cy_close,
.write = cy_write,
diff --git a/drivers/char/drm/Kconfig b/drivers/char/drm/Kconfig
index 5278c388d3e..ef833a1c27e 100644
--- a/drivers/char/drm/Kconfig
+++ b/drivers/char/drm/Kconfig
@@ -60,7 +60,9 @@ config DRM_I830
Choose this option if you have a system that has Intel 830M, 845G,
852GM, 855GM or 865G integrated graphics. If M is selected, the
module will be called i830. AGP support is required for this driver
- to work. This driver will eventually be replaced by the i915 one.
+ to work. This driver is used by the older X releases X.org 6.7 and
+ XFree86 4.3. If unsure, build this and i915 as modules and the X server
+ will load the correct one.
config DRM_I915
tristate "i915 driver"
@@ -68,8 +70,9 @@ config DRM_I915
Choose this option if you have a system that has Intel 830M, 845G,
852GM, 855GM 865G or 915G integrated graphics. If M is selected, the
module will be called i915. AGP support is required for this driver
- to work. This driver will eventually replace the I830 driver, when
- later release of X start to use the new DDX and DRI.
+ to work. This driver is used by the Intel driver in X.org 6.8 and
+ XFree86 4.4 and above. If unsure, build this and i830 as modules and
+ the X server will load the correct one.
endchoice
diff --git a/drivers/char/drm/Makefile b/drivers/char/drm/Makefile
index 9d180c42816..3ad0f648c6b 100644
--- a/drivers/char/drm/Makefile
+++ b/drivers/char/drm/Makefile
@@ -6,7 +6,7 @@ drm-objs := drm_auth.o drm_bufs.o drm_context.o drm_dma.o drm_drawable.o \
drm_drv.o drm_fops.o drm_ioctl.o drm_irq.o \
drm_lock.o drm_memory.o drm_proc.o drm_stub.o drm_vm.o \
drm_agpsupport.o drm_scatter.o ati_pcigart.o drm_pci.o \
- drm_sysfs.o
+ drm_sysfs.o drm_hashtab.o drm_sman.o drm_mm.o
tdfx-objs := tdfx_drv.o
r128-objs := r128_drv.o r128_cce.o r128_state.o r128_irq.o
@@ -16,9 +16,9 @@ i830-objs := i830_drv.o i830_dma.o i830_irq.o
i915-objs := i915_drv.o i915_dma.o i915_irq.o i915_mem.o
radeon-objs := radeon_drv.o radeon_cp.o radeon_state.o radeon_mem.o radeon_irq.o r300_cmdbuf.o
ffb-objs := ffb_drv.o ffb_context.o
-sis-objs := sis_drv.o sis_ds.o sis_mm.o
+sis-objs := sis_drv.o sis_mm.o
savage-objs := savage_drv.o savage_bci.o savage_state.o
-via-objs := via_irq.o via_drv.o via_ds.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o
+via-objs := via_irq.o via_drv.o via_map.o via_mm.o via_dma.o via_verifier.o via_video.o via_dmablit.o
ifeq ($(CONFIG_COMPAT),y)
drm-objs += drm_ioc32.o
diff --git a/drivers/char/drm/drmP.h b/drivers/char/drm/drmP.h
index d2a56182bc3..7690a59ace0 100644
--- a/drivers/char/drm/drmP.h
+++ b/drivers/char/drm/drmP.h
@@ -79,6 +79,7 @@
#define __OS_HAS_MTRR (defined(CONFIG_MTRR))
#include "drm_os_linux.h"
+#include "drm_hashtab.h"
/***********************************************************************/
/** \name DRM template customization defaults */
@@ -104,7 +105,7 @@
#define DRM_DEBUG_CODE 2 /**< Include debugging code if > 1, then
also include looping detection. */
-#define DRM_HASH_SIZE 16 /**< Size of key hash table. Must be power of 2. */
+#define DRM_MAGIC_HASH_ORDER 4 /**< Size of key hash table. Must be power of 2. */
#define DRM_KERNEL_CONTEXT 0 /**< Change drm_resctx if changed */
#define DRM_RESERVED_CONTEXTS 1 /**< Change drm_resctx if changed */
#define DRM_LOOPING_LIMIT 5000000
@@ -134,19 +135,12 @@
#define DRM_MEM_CTXBITMAP 18
#define DRM_MEM_STUB 19
#define DRM_MEM_SGLISTS 20
-#define DRM_MEM_CTXLIST 21
+#define DRM_MEM_CTXLIST 21
+#define DRM_MEM_MM 22
+#define DRM_MEM_HASHTAB 23
#define DRM_MAX_CTXBITMAP (PAGE_SIZE * 8)
-
-/*@}*/
-
-/***********************************************************************/
-/** \name Backward compatibility section */
-/*@{*/
-
-#define DRM_RPR_ARG(vma) vma,
-
-#define VM_OFFSET(vma) ((vma)->vm_pgoff << PAGE_SHIFT)
+#define DRM_MAP_HASH_OFFSET 0x10000000
/*@}*/
@@ -211,8 +205,6 @@
/*@{*/
#define DRM_ARRAY_SIZE(x) ARRAY_SIZE(x)
-#define DRM_MIN(a,b) min(a,b)
-#define DRM_MAX(a,b) max(a,b)
#define DRM_LEFTCOUNT(x) (((x)->rp + (x)->count - (x)->wp) % ((x)->count + 1))
#define DRM_BUFCOUNT(x) ((x)->count - DRM_LEFTCOUNT(x))
@@ -286,7 +278,8 @@ typedef struct drm_devstate {
} drm_devstate_t;
typedef struct drm_magic_entry {
- drm_magic_t magic;
+ drm_hash_item_t hash_item;
+ struct list_head head;
struct drm_file *priv;
struct drm_magic_entry *next;
} drm_magic_entry_t;
@@ -493,6 +486,7 @@ typedef struct drm_sigdata {
*/
typedef struct drm_map_list {
struct list_head head; /**< list head */
+ drm_hash_item_t hash;
drm_map_t *map; /**< mapping */
unsigned int user_token;
} drm_map_list_t;
@@ -527,6 +521,22 @@ typedef struct ati_pcigart_info {
drm_local_map_t mapping;
} drm_ati_pcigart_info;
+/*
+ * Generic memory manager structs
+ */
+typedef struct drm_mm_node {
+ struct list_head fl_entry;
+ struct list_head ml_entry;
+ int free;
+ unsigned long start;
+ unsigned long size;
+ void *private;
+} drm_mm_node_t;
+
+typedef struct drm_mm {
+ drm_mm_node_t root_node;
+} drm_mm_t;
+
/**
* DRM driver structure. This structure represent the common code for
* a family of cards. There will one drm_device for each card present
@@ -646,13 +656,15 @@ typedef struct drm_device {
/*@{ */
drm_file_t *file_first; /**< file list head */
drm_file_t *file_last; /**< file list tail */
- drm_magic_head_t magiclist[DRM_HASH_SIZE]; /**< magic hash table */
+ drm_open_hash_t magiclist; /**< magic hash table */
+ struct list_head magicfree;
/*@} */
/** \name Memory management */
/*@{ */
drm_map_list_t *maplist; /**< Linked list of regions */
int map_count; /**< Number of mappable regions */
+ drm_open_hash_t map_hash; /**< User token hash table for maps */
/** \name Context handle management */
/*@{ */
@@ -711,10 +723,8 @@ typedef struct drm_device {
drm_agp_head_t *agp; /**< AGP data */
struct pci_dev *pdev; /**< PCI device structure */
- int pci_domain; /**< PCI bus domain number */
- int pci_bus; /**< PCI bus number */
- int pci_slot; /**< PCI slot number */
- int pci_func; /**< PCI function number */
+ int pci_vendor; /**< PCI vendor id */
+ int pci_device; /**< PCI device id */
#ifdef __alpha__
struct pci_controller *hose;
#endif
@@ -736,6 +746,12 @@ static __inline__ int drm_core_check_feature(struct drm_device *dev,
return ((dev->driver->driver_features & feature) ? 1 : 0);
}
+#ifdef __alpha__
+#define drm_get_pci_domain(dev) dev->hose->bus->number
+#else
+#define drm_get_pci_domain(dev) 0
+#endif
+
#if __OS_HAS_AGP
static inline int drm_core_has_AGP(struct drm_device *dev)
{
@@ -1011,6 +1027,18 @@ extern struct class_device *drm_sysfs_device_add(struct class *cs,
drm_head_t *head);
extern void drm_sysfs_device_remove(struct class_device *class_dev);
+/*
+ * Basic memory manager support (drm_mm.c)
+ */
+extern drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent,
+ unsigned long size,
+ unsigned alignment);
+extern void drm_mm_put_block(drm_mm_t *mm, drm_mm_node_t *cur);
+extern drm_mm_node_t *drm_mm_search_free(const drm_mm_t *mm, unsigned long size,
+ unsigned alignment, int best_match);
+extern int drm_mm_init(drm_mm_t *mm, unsigned long start, unsigned long size);
+extern void drm_mm_takedown(drm_mm_t *mm);
+
/* Inline replacements for DRM_IOREMAP macros */
static __inline__ void drm_core_ioremap(struct drm_map *map,
struct drm_device *dev)
diff --git a/drivers/char/drm/drm_auth.c b/drivers/char/drm/drm_auth.c
index 2a37586a7ee..c7b19d35bcd 100644
--- a/drivers/char/drm/drm_auth.c
+++ b/drivers/char/drm/drm_auth.c
@@ -36,20 +36,6 @@
#include "drmP.h"
/**
- * Generate a hash key from a magic.
- *
- * \param magic magic.
- * \return hash key.
- *
- * The key is the modulus of the hash table size, #DRM_HASH_SIZE, which must be
- * a power of 2.
- */
-static int drm_hash_magic(drm_magic_t magic)
-{
- return magic & (DRM_HASH_SIZE - 1);
-}
-
-/**
* Find the file with the given magic number.
*
* \param dev DRM device.
@@ -63,14 +49,12 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic)
{
drm_file_t *retval = NULL;
drm_magic_entry_t *pt;
- int hash = drm_hash_magic(magic);
+ drm_hash_item_t *hash;
mutex_lock(&dev->struct_mutex);
- for (pt = dev->magiclist[hash].head; pt; pt = pt->next) {
- if (pt->magic == magic) {
- retval = pt->priv;
- break;
- }
+ if (!drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item);
+ retval = pt->priv;
}
mutex_unlock(&dev->struct_mutex);
return retval;
@@ -90,28 +74,20 @@ static drm_file_t *drm_find_file(drm_device_t * dev, drm_magic_t magic)
static int drm_add_magic(drm_device_t * dev, drm_file_t * priv,
drm_magic_t magic)
{
- int hash;
drm_magic_entry_t *entry;
DRM_DEBUG("%d\n", magic);
- hash = drm_hash_magic(magic);
entry = drm_alloc(sizeof(*entry), DRM_MEM_MAGIC);
if (!entry)
return -ENOMEM;
memset(entry, 0, sizeof(*entry));
- entry->magic = magic;
entry->priv = priv;
- entry->next = NULL;
+ entry->hash_item.key = (unsigned long)magic;
mutex_lock(&dev->struct_mutex);
- if (dev->magiclist[hash].tail) {
- dev->magiclist[hash].tail->next = entry;
- dev->magiclist[hash].tail = entry;
- } else {
- dev->magiclist[hash].head = entry;
- dev->magiclist[hash].tail = entry;
- }
+ drm_ht_insert_item(&dev->magiclist, &entry->hash_item);
+ list_add_tail(&entry->head, &dev->magicfree);
mutex_unlock(&dev->struct_mutex);
return 0;
@@ -128,34 +104,24 @@ static int drm_add_magic(drm_device_t * dev, drm_file_t * priv,
*/
static int drm_remove_magic(drm_device_t * dev, drm_magic_t magic)
{
- drm_magic_entry_t *prev = NULL;
drm_magic_entry_t *pt;
- int hash;
+ drm_hash_item_t *hash;
DRM_DEBUG("%d\n", magic);
- hash = drm_hash_magic(magic);
mutex_lock(&dev->struct_mutex);
- for (pt = dev->magiclist[hash].head; pt; prev = pt, pt = pt->next) {
- if (pt->magic == magic) {
- if (dev->magiclist[hash].head == pt) {
- dev->magiclist[hash].head = pt->next;
- }
- if (dev->magiclist[hash].tail == pt) {
- dev->magiclist[hash].tail = prev;
- }
- if (prev) {
- prev->next = pt->next;
- }
- mutex_unlock(&dev->struct_mutex);
- return 0;
- }
+ if (drm_ht_find_item(&dev->magiclist, (unsigned long)magic, &hash)) {
+ mutex_unlock(&dev->struct_mutex);
+ return -EINVAL;
}
+ pt = drm_hash_entry(hash, drm_magic_entry_t, hash_item);
+ drm_ht_remove_item(&dev->magiclist, hash);
+ list_del(&pt->head);
mutex_unlock(&dev->struct_mutex);
drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
- return -EINVAL;
+ return 0;
}
/**
diff --git a/drivers/char/drm/drm_bufs.c b/drivers/char/drm/drm_bufs.c
index 006b06d2972..029baea33b6 100644
--- a/drivers/char/drm/drm_bufs.c
+++ b/drivers/char/drm/drm_bufs.c
@@ -65,43 +65,29 @@ static drm_map_list_t *drm_find_matching_map(drm_device_t *dev,
return NULL;
}
-/*
- * Used to allocate 32-bit handles for mappings.
- */
-#define START_RANGE 0x10000000
-#define END_RANGE 0x40000000
-
-#ifdef _LP64
-static __inline__ unsigned int HandleID(unsigned long lhandle,
- drm_device_t *dev)
+static int drm_map_handle(drm_device_t *dev, drm_hash_item_t *hash,
+ unsigned long user_token, int hashed_handle)
{
- static unsigned int map32_handle = START_RANGE;
- unsigned int hash;
-
- if (lhandle & 0xffffffff00000000) {
- hash = map32_handle;
- map32_handle += PAGE_SIZE;
- if (map32_handle > END_RANGE)
- map32_handle = START_RANGE;
- } else
- hash = lhandle;
-
- while (1) {
- drm_map_list_t *_entry;
- list_for_each_entry(_entry, &dev->maplist->head, head) {
- if (_entry->user_token == hash)
- break;
- }
- if (&_entry->head == &dev->maplist->head)
- return hash;
+ int use_hashed_handle;
+#if (BITS_PER_LONG == 64)
+ use_hashed_handle = ((user_token & 0xFFFFFFFF00000000UL) || hashed_handle);
+#elif (BITS_PER_LONG == 32)
+ use_hashed_handle = hashed_handle;
+#else
+#error Unsupported long size. Neither 64 nor 32 bits.
+#endif
- hash += PAGE_SIZE;
- map32_handle += PAGE_SIZE;
+ if (!use_hashed_handle) {
+ int ret;
+ hash->key = user_token;
+ ret = drm_ht_insert_item(&dev->map_hash, hash);
+ if (ret != -EINVAL)
+ return ret;
}
+ return drm_ht_just_insert_please(&dev->map_hash, hash,
+ user_token, 32 - PAGE_SHIFT - 3,
+ PAGE_SHIFT, DRM_MAP_HASH_OFFSET);
}
-#else
-# define HandleID(x,dev) (unsigned int)(x)
-#endif
/**
* Ioctl to specify a range of memory that is available for mapping by a non-root process.
@@ -123,6 +109,8 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset,
drm_map_t *map;
drm_map_list_t *list;
drm_dma_handle_t *dmah;
+ unsigned long user_token;
+ int ret;
map = drm_alloc(sizeof(*map), DRM_MEM_MAPS);
if (!map)
@@ -257,11 +245,20 @@ static int drm_addmap_core(drm_device_t * dev, unsigned int offset,
mutex_lock(&dev->struct_mutex);
list_add(&list->head, &dev->maplist->head);
+
/* Assign a 32-bit handle */
/* We do it here so that dev->struct_mutex protects the increment */
- list->user_token = HandleID(map->type == _DRM_SHM
- ? (unsigned long)map->handle
- : map->offset, dev);
+ user_token = (map->type == _DRM_SHM) ? (unsigned long)map->handle :
+ map->offset;
+ ret = drm_map_handle(dev, &list->hash, user_token, 0);
+ if (ret) {
+ drm_free(map, sizeof(*map), DRM_MEM_MAPS);
+ drm_free(list, sizeof(*list), DRM_MEM_MAPS);
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
+
+ list->user_token = list->hash.key;
mutex_unlock(&dev->struct_mutex);
*maplist = list;
@@ -346,6 +343,7 @@ int drm_rmmap_locked(drm_device_t *dev, drm_local_map_t *map)
if (r_list->map == map) {
list_del(list);
+ drm_ht_remove_key(&dev->map_hash, r_list->user_token);
drm_free(list, sizeof(*list), DRM_MEM_MAPS);
break;
}
@@ -441,8 +439,10 @@ int drm_rmmap_ioctl(struct inode *inode, struct file *filp,
return -EINVAL;
}
- if (!map)
+ if (!map) {
+ mutex_unlock(&dev->struct_mutex);
return -EINVAL;
+ }
/* Register and framebuffer maps are permanent */
if ((map->type == _DRM_REGISTERS) || (map->type == _DRM_FRAME_BUFFER)) {
diff --git a/drivers/char/drm/drm_drv.c b/drivers/char/drm/drm_drv.c
index 3c0b882a8e7..b366c5b1bd1 100644
--- a/drivers/char/drm/drm_drv.c
+++ b/drivers/char/drm/drm_drv.c
@@ -118,7 +118,7 @@ static drm_ioctl_desc_t drm_ioctls[] = {
[DRM_IOCTL_NR(DRM_IOCTL_WAIT_VBLANK)] = {drm_wait_vblank, 0},
};
-#define DRIVER_IOCTL_COUNT DRM_ARRAY_SIZE( drm_ioctls )
+#define DRIVER_IOCTL_COUNT ARRAY_SIZE( drm_ioctls )
/**
* Take down the DRM device.
@@ -155,12 +155,13 @@ int drm_lastclose(drm_device_t * dev)
del_timer(&dev->timer);
/* Clear pid list */
- for (i = 0; i < DRM_HASH_SIZE; i++) {
- for (pt = dev->magiclist[i].head; pt; pt = next) {
- next = pt->next;
+ if (dev->magicfree.next) {
+ list_for_each_entry_safe(pt, next, &dev->magicfree, head) {
+ list_del(&pt->head);
+ drm_ht_remove_item(&dev->magiclist, &pt->hash_item);
drm_free(pt, sizeof(*pt), DRM_MEM_MAGIC);
}
- dev->magiclist[i].head = dev->magiclist[i].tail = NULL;
+ drm_ht_remove(&dev->magiclist);
}
/* Clear AGP information */
@@ -299,6 +300,7 @@ static void drm_cleanup(drm_device_t * dev)
if (dev->maplist) {
drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
dev->maplist = NULL;
+ drm_ht_remove(&dev->map_hash);
}
drm_ctxbitmap_cleanup(dev);
diff --git a/drivers/char/drm/drm_fops.c b/drivers/char/drm/drm_fops.c
index b7f7951c458..898f47dafec 100644
--- a/drivers/char/drm/drm_fops.c
+++ b/drivers/char/drm/drm_fops.c
@@ -53,6 +53,8 @@ static int drm_setup(drm_device_t * dev)
return ret;
}
+ dev->magicfree.next = NULL;
+
/* prebuild the SAREA */
i = drm_addmap(dev, 0, SAREA_MAX, _DRM_SHM, _DRM_CONTAINS_LOCK, &map);
if (i != 0)
@@ -69,13 +71,11 @@ static int drm_setup(drm_device_t * dev)
return i;
}
- for (i = 0; i < DRM_ARRAY_SIZE(dev->counts); i++)
+ for (i = 0; i < ARRAY_SIZE(dev->counts); i++)
atomic_set(&dev->counts[i], 0);
- for (i = 0; i < DRM_HASH_SIZE; i++) {
- dev->magiclist[i].head = NULL;
- dev->magiclist[i].tail = NULL;
- }
+ drm_ht_create(&dev->magiclist, DRM_MAGIC_HASH_ORDER);
+ INIT_LIST_HEAD(&dev->magicfree);
dev->ctxlist = drm_alloc(sizeof(*dev->ctxlist), DRM_MEM_CTXLIST);
if (dev->ctxlist == NULL)
diff --git a/drivers/char/drm/drm_hashtab.c b/drivers/char/drm/drm_hashtab.c
new file mode 100644
index 00000000000..a0b2d6802ae
--- /dev/null
+++ b/drivers/char/drm/drm_hashtab.c
@@ -0,0 +1,190 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Simple open hash tab implementation.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "drmP.h"
+#include "drm_hashtab.h"
+#include <linux/hash.h>
+
+int drm_ht_create(drm_open_hash_t *ht, unsigned int order)
+{
+ unsigned int i;
+
+ ht->size = 1 << order;
+ ht->order = order;
+ ht->fill = 0;
+ ht->table = vmalloc(ht->size*sizeof(*ht->table));
+ if (!ht->table) {
+ DRM_ERROR("Out of memory for hash table\n");
+ return -ENOMEM;
+ }
+ for (i=0; i< ht->size; ++i) {
+ INIT_HLIST_HEAD(&ht->table[i]);
+ }
+ return 0;
+}
+
+void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key)
+{
+ drm_hash_item_t *entry;
+ struct hlist_head *h_list;
+ struct hlist_node *list;
+ unsigned int hashed_key;
+ int count = 0;
+
+ hashed_key = hash_long(key, ht->order);
+ DRM_DEBUG("Key is 0x%08lx, Hashed key is 0x%08x\n", key, hashed_key);
+ h_list = &ht->table[hashed_key];
+ hlist_for_each(list, h_list) {
+ entry = hlist_entry(list, drm_hash_item_t, head);
+ DRM_DEBUG("count %d, key: 0x%08lx\n", count++, entry->key);
+ }
+}
+
+static struct hlist_node *drm_ht_find_key(drm_open_hash_t *ht,
+ unsigned long key)
+{
+ drm_hash_item_t *entry;
+ struct hlist_head *h_list;
+ struct hlist_node *list;
+ unsigned int hashed_key;
+
+ hashed_key = hash_long(key, ht->order);
+ h_list = &ht->table[hashed_key];
+ hlist_for_each(list, h_list) {
+ entry = hlist_entry(list, drm_hash_item_t, head);
+ if (entry->key == key)
+ return list;
+ if (entry->key > key)
+ break;
+ }
+ return NULL;
+}
+
+
+int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item)
+{
+ drm_hash_item_t *entry;
+ struct hlist_head *h_list;
+ struct hlist_node *list, *parent;
+ unsigned int hashed_key;
+ unsigned long key = item->key;
+
+ hashed_key = hash_long(key, ht->order);
+ h_list = &ht->table[hashed_key];
+ parent = NULL;
+ hlist_for_each(list, h_list) {
+ entry = hlist_entry(list, drm_hash_item_t, head);
+ if (entry->key == key)
+ return -EINVAL;
+ if (entry->key > key)
+ break;
+ parent = list;
+ }
+ if (parent) {
+ hlist_add_after(parent, &item->head);
+ } else {
+ hlist_add_head(&item->head, h_list);
+ }
+ return 0;
+}
+
+/*
+ * Just insert an item and return any "bits" bit key that hasn't been
+ * used before.
+ */
+int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item,
+ unsigned long seed, int bits, int shift,
+ unsigned long add)
+{
+ int ret;
+ unsigned long mask = (1 << bits) - 1;
+ unsigned long first, unshifted_key;
+
+ unshifted_key = hash_long(seed, bits);
+ first = unshifted_key;
+ do {
+ item->key = (unshifted_key << shift) + add;
+ ret = drm_ht_insert_item(ht, item);
+ if (ret)
+ unshifted_key = (unshifted_key + 1) & mask;
+ } while(ret && (unshifted_key != first));
+
+ if (ret) {
+ DRM_ERROR("Available key bit space exhausted\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key,
+ drm_hash_item_t **item)
+{
+ struct hlist_node *list;
+
+ list = drm_ht_find_key(ht, key);
+ if (!list)
+ return -EINVAL;
+
+ *item = hlist_entry(list, drm_hash_item_t, head);
+ return 0;
+}
+
+int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key)
+{
+ struct hlist_node *list;
+
+ list = drm_ht_find_key(ht, key);
+ if (list) {
+ hlist_del_init(list);
+ ht->fill--;
+ return 0;
+ }
+ return -EINVAL;
+}
+
+int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item)
+{
+ hlist_del_init(&item->head);
+ ht->fill--;
+ return 0;
+}
+
+void drm_ht_remove(drm_open_hash_t *ht)
+{
+ if (ht->table) {
+ vfree(ht->table);
+ ht->table = NULL;
+ }
+}
+
diff --git a/drivers/char/drm/drm_hashtab.h b/drivers/char/drm/drm_hashtab.h
new file mode 100644
index 00000000000..40afec05bff
--- /dev/null
+++ b/drivers/char/drm/drm_hashtab.h
@@ -0,0 +1,67 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismack, ND. USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Simple open hash tab implementation.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef DRM_HASHTAB_H
+#define DRM_HASHTAB_H
+
+#define drm_hash_entry(_ptr, _type, _member) container_of(_ptr, _type, _member)
+
+typedef struct drm_hash_item{
+ struct hlist_node head;
+ unsigned long key;
+} drm_hash_item_t;
+
+typedef struct drm_open_hash{
+ unsigned int size;
+ unsigned int order;
+ unsigned int fill;
+ struct hlist_head *table;
+} drm_open_hash_t;
+
+
+extern int drm_ht_create(drm_open_hash_t *ht, unsigned int order);
+extern int drm_ht_insert_item(drm_open_hash_t *ht, drm_hash_item_t *item);
+extern int drm_ht_just_insert_please(drm_open_hash_t *ht, drm_hash_item_t *item,
+ unsigned long seed, int bits, int shift,
+ unsigned long add);
+extern int drm_ht_find_item(drm_open_hash_t *ht, unsigned long key, drm_hash_item_t **item);
+
+extern void drm_ht_verbose_list(drm_open_hash_t *ht, unsigned long key);
+extern int drm_ht_remove_key(drm_open_hash_t *ht, unsigned long key);
+extern int drm_ht_remove_item(drm_open_hash_t *ht, drm_hash_item_t *item);
+extern void drm_ht_remove(drm_open_hash_t *ht);
+
+
+#endif
+
diff --git a/drivers/char/drm/drm_ioc32.c b/drivers/char/drm/drm_ioc32.c
index e9e2db18952..d4f87452008 100644
--- a/drivers/char/drm/drm_ioc32.c
+++ b/drivers/char/drm/drm_ioc32.c
@@ -1051,7 +1051,7 @@ long drm_compat_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
drm_ioctl_compat_t *fn;
int ret;
- if (nr >= DRM_ARRAY_SIZE(drm_compat_ioctls))
+ if (nr >= ARRAY_SIZE(drm_compat_ioctls))
return -ENOTTY;
fn = drm_compat_ioctls[nr];
diff --git a/drivers/char/drm/drm_ioctl.c b/drivers/char/drm/drm_ioctl.c
index 555f323b8a3..565895547d7 100644
--- a/drivers/char/drm/drm_ioctl.c
+++ b/drivers/char/drm/drm_ioctl.c
@@ -127,9 +127,10 @@ int drm_setunique(struct inode *inode, struct file *filp,
domain = bus >> 8;
bus &= 0xff;
- if ((domain != dev->pci_domain) ||
- (bus != dev->pci_bus) ||
- (slot != dev->pci_slot) || (func != dev->pci_func))
+ if ((domain != drm_get_pci_domain(dev)) ||
+ (bus != dev->pdev->bus->number) ||
+ (slot != PCI_SLOT(dev->pdev->devfn)) ||
+ (func != PCI_FUNC(dev->pdev->devfn)))
return -EINVAL;
return 0;
@@ -140,15 +141,17 @@ static int drm_set_busid(drm_device_t * dev)
int len;
if (dev->unique != NULL)
- return EBUSY;
+ return 0;
dev->unique_len = 40;
dev->unique = drm_alloc(dev->unique_len + 1, DRM_MEM_DRIVER);
if (dev->unique == NULL)
- return ENOMEM;
+ return -ENOMEM;
len = snprintf(dev->unique, dev->unique_len, "pci:%04x:%02x:%02x.%d",
- dev->pci_domain, dev->pci_bus, dev->pci_slot, dev->pci_func);
+ drm_get_pci_domain(dev), dev->pdev->bus->number,
+ PCI_SLOT(dev->pdev->devfn),
+ PCI_FUNC(dev->pdev->devfn));
if (len > dev->unique_len)
DRM_ERROR("Unique buffer overflowed\n");
@@ -157,7 +160,7 @@ static int drm_set_busid(drm_device_t * dev)
drm_alloc(strlen(dev->driver->pci_driver.name) + dev->unique_len +
2, DRM_MEM_DRIVER);
if (dev->devname == NULL)
- return ENOMEM;
+ return -ENOMEM;
sprintf(dev->devname, "%s@%s", dev->driver->pci_driver.name,
dev->unique);
@@ -330,27 +333,32 @@ int drm_setversion(DRM_IOCTL_ARGS)
drm_set_version_t retv;
int if_version;
drm_set_version_t __user *argp = (void __user *)data;
+ int ret;
- DRM_COPY_FROM_USER_IOCTL(sv, argp, sizeof(sv));
+ if (copy_from_user(&sv, argp, sizeof(sv)))
+ return -EFAULT;
retv.drm_di_major = DRM_IF_MAJOR;
retv.drm_di_minor = DRM_IF_MINOR;
retv.drm_dd_major = dev->driver->major;
retv.drm_dd_minor = dev->driver->minor;
- DRM_COPY_TO_USER_IOCTL(argp, retv, sizeof(sv));
+ if (copy_to_user(argp, &retv, sizeof(retv)))
+ return -EFAULT;
if (sv.drm_di_major != -1) {
if (sv.drm_di_major != DRM_IF_MAJOR ||
sv.drm_di_minor < 0 || sv.drm_di_minor > DRM_IF_MINOR)
- return EINVAL;
+ return -EINVAL;
if_version = DRM_IF_VERSION(sv.drm_di_major, sv.drm_di_minor);
- dev->if_version = DRM_MAX(if_version, dev->if_version);
+ dev->if_version = max(if_version, dev->if_version);
if (sv.drm_di_minor >= 1) {
/*
* Version 1.1 includes tying of DRM to specific device
*/
- drm_set_busid(dev);
+ ret = drm_set_busid(dev);
+ if (ret)
+ return ret;
}
}
@@ -358,7 +366,7 @@ int drm_setversion(DRM_IOCTL_ARGS)
if (sv.drm_dd_major != dev->driver->major ||
sv.drm_dd_minor < 0
|| sv.drm_dd_minor > dev->driver->minor)
- return EINVAL;
+ return -EINVAL;
if (dev->driver->set_version)
dev->driver->set_version(dev, &sv);
diff --git a/drivers/char/drm/drm_irq.c b/drivers/char/drm/drm_irq.c
index ebdb7182c4f..4553a3a1e49 100644
--- a/drivers/char/drm/drm_irq.c
+++ b/drivers/char/drm/drm_irq.c
@@ -64,9 +64,9 @@ int drm_irq_by_busid(struct inode *inode, struct file *filp,
if (copy_from_user(&p, argp, sizeof(p)))
return -EFAULT;
- if ((p.busnum >> 8) != dev->pci_domain ||
- (p.busnum & 0xff) != dev->pci_bus ||
- p.devnum != dev->pci_slot || p.funcnum != dev->pci_func)
+ if ((p.busnum >> 8) != drm_get_pci_domain(dev) ||
+ (p.busnum & 0xff) != dev->pdev->bus->number ||
+ p.devnum != PCI_SLOT(dev->pdev->devfn) || p.funcnum != PCI_FUNC(dev->pdev->devfn))
return -EINVAL;
p.irq = dev->irq;
@@ -255,7 +255,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
if (!dev->irq)
return -EINVAL;
- DRM_COPY_FROM_USER_IOCTL(vblwait, argp, sizeof(vblwait));
+ if (copy_from_user(&vblwait, argp, sizeof(vblwait)))
+ return -EFAULT;
switch (vblwait.request.type & ~_DRM_VBLANK_FLAGS_MASK) {
case _DRM_VBLANK_RELATIVE:
@@ -329,7 +330,8 @@ int drm_wait_vblank(DRM_IOCTL_ARGS)
}
done:
- DRM_COPY_TO_USER_IOCTL(argp, vblwait, sizeof(vblwait));
+ if (copy_to_user(argp, &vblwait, sizeof(vblwait)))
+ return -EFAULT;
return ret;
}
diff --git a/drivers/char/drm/drm_mm.c b/drivers/char/drm/drm_mm.c
new file mode 100644
index 00000000000..617526bd5b0
--- /dev/null
+++ b/drivers/char/drm/drm_mm.c
@@ -0,0 +1,201 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+
+/*
+ * Generic simple memory manager implementation. Intended to be used as a base
+ * class implementation for more advanced memory managers.
+ *
+ * Note that the algorithm used is quite simple and there might be substantial
+ * performance gains if a smarter free list is implemented. Currently it is just an
+ * unordered stack of free regions. This could easily be improved if an RB-tree
+ * is used instead. At least if we expect heavy fragmentation.
+ *
+ * Aligned allocations can also see improvement.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "drmP.h"
+
+drm_mm_node_t *drm_mm_get_block(drm_mm_node_t * parent,
+ unsigned long size, unsigned alignment)
+{
+
+ drm_mm_node_t *child;
+
+ if (alignment)
+ size += alignment - 1;
+
+ if (parent->size == size) {
+ list_del_init(&parent->fl_entry);
+ parent->free = 0;
+ return parent;
+ } else {
+ child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM);
+ if (!child)
+ return NULL;
+
+ INIT_LIST_HEAD(&child->ml_entry);
+ INIT_LIST_HEAD(&child->fl_entry);
+
+ child->free = 0;
+ child->size = size;
+ child->start = parent->start;
+
+ list_add_tail(&child->ml_entry, &parent->ml_entry);
+ parent->size -= size;
+ parent->start += size;
+ }
+ return child;
+}
+
+/*
+ * Put a block. Merge with the previous and / or next block if they are free.
+ * Otherwise add to the free stack.
+ */
+
+void drm_mm_put_block(drm_mm_t * mm, drm_mm_node_t * cur)
+{
+
+ drm_mm_node_t *list_root = &mm->root_node;
+ struct list_head *cur_head = &cur->ml_entry;
+ struct list_head *root_head = &list_root->ml_entry;
+ drm_mm_node_t *prev_node = NULL;
+ drm_mm_node_t *next_node;
+
+ int merged = 0;
+
+ if (cur_head->prev != root_head) {
+ prev_node = list_entry(cur_head->prev, drm_mm_node_t, ml_entry);
+ if (prev_node->free) {
+ prev_node->size += cur->size;
+ merged = 1;
+ }
+ }
+ if (cur_head->next != root_head) {
+ next_node = list_entry(cur_head->next, drm_mm_node_t, ml_entry);
+ if (next_node->free) {
+ if (merged) {
+ prev_node->size += next_node->size;
+ list_del(&next_node->ml_entry);
+ list_del(&next_node->fl_entry);
+ drm_free(next_node, sizeof(*next_node),
+ DRM_MEM_MM);
+ } else {
+ next_node->size += cur->size;
+ next_node->start = cur->start;
+ merged = 1;
+ }
+ }
+ }
+ if (!merged) {
+ cur->free = 1;
+ list_add(&cur->fl_entry, &list_root->fl_entry);
+ } else {
+ list_del(&cur->ml_entry);
+ drm_free(cur, sizeof(*cur), DRM_MEM_MM);
+ }
+}
+
+drm_mm_node_t *drm_mm_search_free(const drm_mm_t * mm,
+ unsigned long size,
+ unsigned alignment, int best_match)
+{
+ struct list_head *list;
+ const struct list_head *free_stack = &mm->root_node.fl_entry;
+ drm_mm_node_t *entry;
+ drm_mm_node_t *best;
+ unsigned long best_size;
+
+ best = NULL;
+ best_size = ~0UL;
+
+ if (alignment)
+ size += alignment - 1;
+
+ list_for_each(list, free_stack) {
+ entry = list_entry(list, drm_mm_node_t, fl_entry);
+ if (entry->size >= size) {
+ if (!best_match)
+ return entry;
+ if (size < best_size) {
+ best = entry;
+ best_size = entry->size;
+ }
+ }
+ }
+
+ return best;
+}
+
+int drm_mm_init(drm_mm_t * mm, unsigned long start, unsigned long size)
+{
+ drm_mm_node_t *child;
+
+ INIT_LIST_HEAD(&mm->root_node.ml_entry);
+ INIT_LIST_HEAD(&mm->root_node.fl_entry);
+ child = (drm_mm_node_t *) drm_alloc(sizeof(*child), DRM_MEM_MM);
+ if (!child)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&child->ml_entry);
+ INIT_LIST_HEAD(&child->fl_entry);
+
+ child->start = start;
+ child->size = size;
+ child->free = 1;
+
+ list_add(&child->fl_entry, &mm->root_node.fl_entry);
+ list_add(&child->ml_entry, &mm->root_node.ml_entry);
+
+ return 0;
+}
+
+EXPORT_SYMBOL(drm_mm_init);
+
+void drm_mm_takedown(drm_mm_t * mm)
+{
+ struct list_head *bnode = mm->root_node.fl_entry.next;
+ drm_mm_node_t *entry;
+
+ entry = list_entry(bnode, drm_mm_node_t, fl_entry);
+
+ if (entry->ml_entry.next != &mm->root_node.ml_entry ||
+ entry->fl_entry.next != &mm->root_node.fl_entry) {
+ DRM_ERROR("Memory manager not clean. Delaying takedown\n");
+ return;
+ }
+
+ list_del(&entry->fl_entry);
+ list_del(&entry->ml_entry);
+
+ drm_free(entry, sizeof(*entry), DRM_MEM_MM);
+}
+
+EXPORT_SYMBOL(drm_mm_takedown);
diff --git a/drivers/char/drm/drm_pciids.h b/drivers/char/drm/drm_pciids.h
index b1bb3c7b568..09398d5fbd3 100644
--- a/drivers/char/drm/drm_pciids.h
+++ b/drivers/char/drm/drm_pciids.h
@@ -3,13 +3,13 @@
Please contact dri-devel@lists.sf.net to add new cards to this list
*/
#define radeon_PCI_IDS \
- {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
- {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP}, \
- {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \
+ {0x1002, 0x3150, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x3152, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x3154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x3E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x3E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4136, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP}, \
+ {0x1002, 0x4137, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
{0x1002, 0x4144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
@@ -25,35 +25,35 @@
{0x1002, 0x4154, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4155, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
{0x1002, 0x4156, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350}, \
- {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP}, \
+ {0x1002, 0x4237, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP}, \
{0x1002, 0x4242, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x4243, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
- {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4336, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS100|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4337, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4437, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS200|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x4966, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
{0x1002, 0x4967, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250}, \
- {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x4A48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A4F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4A54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4B49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4B4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4B4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4B4C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x4C57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C58, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV200|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C59, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C5A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C66, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4C67, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV250|RADEON_IS_MOBILITY}, \
{0x1002, 0x4E44, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E45, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
{0x1002, 0x4E46, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R300}, \
@@ -62,16 +62,16 @@
{0x1002, 0x4E49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E4A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
{0x1002, 0x4E4B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R350}, \
- {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
- {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
- {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
- {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|CHIP_SINGLE_CRTC}, \
+ {0x1002, 0x4E50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4E51, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4E52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4E53, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4E54, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x4E56, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV350|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5144, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+ {0x1002, 0x5145, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+ {0x1002, 0x5146, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
+ {0x1002, 0x5147, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R100|RADEON_SINGLE_CRTC}, \
{0x1002, 0x5148, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
{0x1002, 0x514D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R200}, \
@@ -80,59 +80,59 @@
{0x1002, 0x5159, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
{0x1002, 0x515E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
- {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP}, \
- {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY}, \
+ {0x1002, 0x5460, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5462, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5464, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5548, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5549, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554C, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554D, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554E, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x554F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5550, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5551, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5552, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5554, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x564A, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x564B, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x564F, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5652, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5653, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP}, \
+ {0x1002, 0x5835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY}, \
{0x1002, 0x5960, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5961, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5962, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5964, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5965, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280}, \
{0x1002, 0x5969, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV100}, \
- {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|CHIP_IS_MOBILITY}, \
- {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_NEW_MEMMAP}, \
- {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|CHIP_IS_IGP|CHIP_IS_MOBILITY|CHIP_NEW_MEMMAP}, \
+ {0x1002, 0x5b60, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5b62, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5b63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5b64, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5b65, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV380|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5c61, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5c63, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV280|RADEON_IS_MOBILITY}, \
+ {0x1002, 0x5d48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d49, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d4e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d50, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d52, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5d57, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_R420|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e48, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e4a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e4b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e4c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e4d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x5e4f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RV410|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x7834, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_NEW_MEMMAP}, \
+ {0x1002, 0x7835, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_RS300|RADEON_IS_IGP|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \
{0, 0, 0}
#define r128_PCI_IDS \
@@ -209,6 +209,7 @@
{0x1039, 0x0300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x1039, 0x5300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x1039, 0x6300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x1039, 0x6330, PCI_ANY_ID, PCI_ANY_ID, 0, 0, SIS_CHIP_315}, \
{0x1039, 0x7300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0, 0, 0}
@@ -227,6 +228,10 @@
{0x1106, 0x3122, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x1106, 0x7205, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x1106, 0x3108, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x1106, 0x3304, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x1106, 0x3157, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x1106, 0x3344, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x1106, 0x7204, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0, 0, 0}
#define i810_PCI_IDS \
@@ -285,5 +290,9 @@
{0x8086, 0x2592, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x8086, 0x2772, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0x8086, 0x27a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x8086, 0x2972, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x8086, 0x2982, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x8086, 0x2992, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
+ {0x8086, 0x29a2, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0}, \
{0, 0, 0}
diff --git a/drivers/char/drm/drm_proc.c b/drivers/char/drm/drm_proc.c
index 362a270af0f..62d5fe15f04 100644
--- a/drivers/char/drm/drm_proc.c
+++ b/drivers/char/drm/drm_proc.c
@@ -510,7 +510,7 @@ static int drm__vma_info(char *buf, char **start, off_t offset, int request,
vma->vm_flags & VM_MAYSHARE ? 's' : 'p',
vma->vm_flags & VM_LOCKED ? 'l' : '-',
vma->vm_flags & VM_IO ? 'i' : '-',
- VM_OFFSET(vma));
+ vma->vm_pgoff << PAGE_SHIFT);
#if defined(__i386__)
pgprot = pgprot_val(vma->vm_page_prot);
diff --git a/drivers/char/drm/drm_sman.c b/drivers/char/drm/drm_sman.c
new file mode 100644
index 00000000000..425c82336ee
--- /dev/null
+++ b/drivers/char/drm/drm_sman.c
@@ -0,0 +1,352 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck., ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * THE 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 NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ *
+ **************************************************************************/
+/*
+ * Simple memory manager interface that keeps track on allocate regions on a
+ * per "owner" basis. All regions associated with an "owner" can be released
+ * with a simple call. Typically if the "owner" exists. The owner is any
+ * "unsigned long" identifier. Can typically be a pointer to a file private
+ * struct or a context identifier.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#include "drm_sman.h"
+
+typedef struct drm_owner_item {
+ drm_hash_item_t owner_hash;
+ struct list_head sman_list;
+ struct list_head mem_blocks;
+} drm_owner_item_t;
+
+void drm_sman_takedown(drm_sman_t * sman)
+{
+ drm_ht_remove(&sman->user_hash_tab);
+ drm_ht_remove(&sman->owner_hash_tab);
+ if (sman->mm)
+ drm_free(sman->mm, sman->num_managers * sizeof(*sman->mm),
+ DRM_MEM_MM);
+}
+
+EXPORT_SYMBOL(drm_sman_takedown);
+
+int
+drm_sman_init(drm_sman_t * sman, unsigned int num_managers,
+ unsigned int user_order, unsigned int owner_order)
+{
+ int ret = 0;
+
+ sman->mm = (drm_sman_mm_t *) drm_calloc(num_managers, sizeof(*sman->mm),
+ DRM_MEM_MM);
+ if (!sman->mm) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ sman->num_managers = num_managers;
+ INIT_LIST_HEAD(&sman->owner_items);
+ ret = drm_ht_create(&sman->owner_hash_tab, owner_order);
+ if (ret)
+ goto out1;
+ ret = drm_ht_create(&sman->user_hash_tab, user_order);
+ if (!ret)
+ goto out;
+
+ drm_ht_remove(&sman->owner_hash_tab);
+out1:
+ drm_free(sman->mm, num_managers * sizeof(*sman->mm), DRM_MEM_MM);
+out:
+ return ret;
+}
+
+EXPORT_SYMBOL(drm_sman_init);
+
+static void *drm_sman_mm_allocate(void *private, unsigned long size,
+ unsigned alignment)
+{
+ drm_mm_t *mm = (drm_mm_t *) private;
+ drm_mm_node_t *tmp;
+
+ tmp = drm_mm_search_free(mm, size, alignment, 1);
+ if (!tmp) {
+ return NULL;
+ }
+ tmp = drm_mm_get_block(tmp, size, alignment);
+ return tmp;
+}
+
+static void drm_sman_mm_free(void *private, void *ref)
+{
+ drm_mm_t *mm = (drm_mm_t *) private;
+ drm_mm_node_t *node = (drm_mm_node_t *) ref;
+
+ drm_mm_put_block(mm, node);
+}
+
+static void drm_sman_mm_destroy(void *private)
+{
+ drm_mm_t *mm = (drm_mm_t *) private;
+ drm_mm_takedown(mm);
+ drm_free(mm, sizeof(*mm), DRM_MEM_MM);
+}
+
+static unsigned long drm_sman_mm_offset(void *private, void *ref)
+{
+ drm_mm_node_t *node = (drm_mm_node_t *) ref;
+ return node->start;
+}
+
+int
+drm_sman_set_range(drm_sman_t * sman, unsigned int manager,
+ unsigned long start, unsigned long size)
+{
+ drm_sman_mm_t *sman_mm;
+ drm_mm_t *mm;
+ int ret;
+
+ BUG_ON(manager >= sman->num_managers);
+
+ sman_mm = &sman->mm[manager];
+ mm = drm_calloc(1, sizeof(*mm), DRM_MEM_MM);
+ if (!mm) {
+ return -ENOMEM;
+ }
+ sman_mm->private = mm;
+ ret = drm_mm_init(mm, start, size);
+
+ if (ret) {
+ drm_free(mm, sizeof(*mm), DRM_MEM_MM);
+ return ret;
+ }
+
+ sman_mm->allocate = drm_sman_mm_allocate;
+ sman_mm->free = drm_sman_mm_free;
+ sman_mm->destroy = drm_sman_mm_destroy;
+ sman_mm->offset = drm_sman_mm_offset;
+
+ return 0;
+}
+
+EXPORT_SYMBOL(drm_sman_set_range);
+
+int
+drm_sman_set_manager(drm_sman_t * sman, unsigned int manager,
+ drm_sman_mm_t * allocator)
+{
+ BUG_ON(manager >= sman->num_managers);
+ sman->mm[manager] = *allocator;
+
+ return 0;
+}
+
+static drm_owner_item_t *drm_sman_get_owner_item(drm_sman_t * sman,
+ unsigned long owner)
+{
+ int ret;
+ drm_hash_item_t *owner_hash_item;
+ drm_owner_item_t *owner_item;
+
+ ret = drm_ht_find_item(&sman->owner_hash_tab, owner, &owner_hash_item);
+ if (!ret) {
+ return drm_hash_entry(owner_hash_item, drm_owner_item_t,
+ owner_hash);
+ }
+
+ owner_item = drm_calloc(1, sizeof(*owner_item), DRM_MEM_MM);
+ if (!owner_item)
+ goto out;
+
+ INIT_LIST_HEAD(&owner_item->mem_blocks);
+ owner_item->owner_hash.key = owner;
+ if (drm_ht_insert_item(&sman->owner_hash_tab, &owner_item->owner_hash))
+ goto out1;
+
+ list_add_tail(&owner_item->sman_list, &sman->owner_items);
+ return owner_item;
+
+out1:
+ drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
+out:
+ return NULL;
+}
+
+drm_memblock_item_t *drm_sman_alloc(drm_sman_t *sman, unsigned int manager,
+ unsigned long size, unsigned alignment,
+ unsigned long owner)
+{
+ void *tmp;
+ drm_sman_mm_t *sman_mm;
+ drm_owner_item_t *owner_item;
+ drm_memblock_item_t *memblock;
+
+ BUG_ON(manager >= sman->num_managers);
+
+ sman_mm = &sman->mm[manager];
+ tmp = sman_mm->allocate(sman_mm->private, size, alignment);
+
+ if (!tmp) {
+ return NULL;
+ }
+
+ memblock = drm_calloc(1, sizeof(*memblock), DRM_MEM_MM);
+
+ if (!memblock)
+ goto out;
+
+ memblock->mm_info = tmp;
+ memblock->mm = sman_mm;
+ memblock->sman = sman;
+
+ if (drm_ht_just_insert_please
+ (&sman->user_hash_tab, &memblock->user_hash,
+ (unsigned long)memblock, 32, 0, 0))
+ goto out1;
+
+ owner_item = drm_sman_get_owner_item(sman, owner);
+ if (!owner_item)
+ goto out2;
+
+ list_add_tail(&memblock->owner_list, &owner_item->mem_blocks);
+
+ return memblock;
+
+out2:
+ drm_ht_remove_item(&sman->user_hash_tab, &memblock->user_hash);
+out1:
+ drm_free(memblock, sizeof(*memblock), DRM_MEM_MM);
+out:
+ sman_mm->free(sman_mm->private, tmp);
+
+ return NULL;
+}
+
+EXPORT_SYMBOL(drm_sman_alloc);
+
+static void drm_sman_free(drm_memblock_item_t *item)
+{
+ drm_sman_t *sman = item->sman;
+
+ list_del(&item->owner_list);
+ drm_ht_remove_item(&sman->user_hash_tab, &item->user_hash);
+ item->mm->free(item->mm->private, item->mm_info);
+ drm_free(item, sizeof(*item), DRM_MEM_MM);
+}
+
+int drm_sman_free_key(drm_sman_t *sman, unsigned int key)
+{
+ drm_hash_item_t *hash_item;
+ drm_memblock_item_t *memblock_item;
+
+ if (drm_ht_find_item(&sman->user_hash_tab, key, &hash_item))
+ return -EINVAL;
+
+ memblock_item = drm_hash_entry(hash_item, drm_memblock_item_t, user_hash);
+ drm_sman_free(memblock_item);
+ return 0;
+}
+
+EXPORT_SYMBOL(drm_sman_free_key);
+
+static void drm_sman_remove_owner(drm_sman_t *sman,
+ drm_owner_item_t *owner_item)
+{
+ list_del(&owner_item->sman_list);
+ drm_ht_remove_item(&sman->owner_hash_tab, &owner_item->owner_hash);
+ drm_free(owner_item, sizeof(*owner_item), DRM_MEM_MM);
+}
+
+int drm_sman_owner_clean(drm_sman_t *sman, unsigned long owner)
+{
+
+ drm_hash_item_t *hash_item;
+ drm_owner_item_t *owner_item;
+
+ if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
+ return -1;
+ }
+
+ owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash);
+ if (owner_item->mem_blocks.next == &owner_item->mem_blocks) {
+ drm_sman_remove_owner(sman, owner_item);
+ return -1;
+ }
+
+ return 0;
+}
+
+EXPORT_SYMBOL(drm_sman_owner_clean);
+
+static void drm_sman_do_owner_cleanup(drm_sman_t *sman,
+ drm_owner_item_t *owner_item)
+{
+ drm_memblock_item_t *entry, *next;
+
+ list_for_each_entry_safe(entry, next, &owner_item->mem_blocks,
+ owner_list) {
+ drm_sman_free(entry);
+ }
+ drm_sman_remove_owner(sman, owner_item);
+}
+
+void drm_sman_owner_cleanup(drm_sman_t *sman, unsigned long owner)
+{
+
+ drm_hash_item_t *hash_item;
+ drm_owner_item_t *owner_item;
+
+ if (drm_ht_find_item(&sman->owner_hash_tab, owner, &hash_item)) {
+
+ return;
+ }
+
+ owner_item = drm_hash_entry(hash_item, drm_owner_item_t, owner_hash);
+ drm_sman_do_owner_cleanup(sman, owner_item);
+}
+
+EXPORT_SYMBOL(drm_sman_owner_cleanup);
+
+void drm_sman_cleanup(drm_sman_t *sman)
+{
+ drm_owner_item_t *entry, *next;
+ unsigned int i;
+ drm_sman_mm_t *sman_mm;
+
+ list_for_each_entry_safe(entry, next, &sman->owner_items, sman_list) {
+ drm_sman_do_owner_cleanup(sman, entry);
+ }
+ if (sman->mm) {
+ for (i = 0; i < sman->num_managers; ++i) {
+ sman_mm = &sman->mm[i];
+ if (sman_mm->private) {
+ sman_mm->destroy(sman_mm->private);
+ sman_mm->private = NULL;
+ }
+ }
+ }
+}
+
+EXPORT_SYMBOL(drm_sman_cleanup);
diff --git a/drivers/char/drm/drm_sman.h b/drivers/char/drm/drm_sman.h
new file mode 100644
index 00000000000..ddc732a1bf2
--- /dev/null
+++ b/drivers/char/drm/drm_sman.h
@@ -0,0 +1,176 @@
+/**************************************************************************
+ *
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
+ * All Rights Reserved.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ *
+ **************************************************************************/
+/*
+ * Simple memory MANager interface that keeps track on allocate regions on a
+ * per "owner" basis. All regions associated with an "owner" can be released
+ * with a simple call. Typically if the "owner" exists. The owner is any
+ * "unsigned long" identifier. Can typically be a pointer to a file private
+ * struct or a context identifier.
+ *
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
+#ifndef DRM_SMAN_H
+#define DRM_SMAN_H
+
+#include "drmP.h"
+#include "drm_hashtab.h"
+
+/*
+ * A class that is an abstration of a simple memory allocator.
+ * The sman implementation provides a default such allocator
+ * using the drm_mm.c implementation. But the user can replace it.
+ * See the SiS implementation, which may use the SiS FB kernel module
+ * for memory management.
+ */
+
+typedef struct drm_sman_mm {
+ /* private info. If allocated, needs to be destroyed by the destroy
+ function */
+ void *private;
+
+ /* Allocate a memory block with given size and alignment.
+ Return an opaque reference to the memory block */
+
+ void *(*allocate) (void *private, unsigned long size,
+ unsigned alignment);
+
+ /* Free a memory block. "ref" is the opaque reference that we got from
+ the "alloc" function */
+
+ void (*free) (void *private, void *ref);
+
+ /* Free all resources associated with this allocator */
+
+ void (*destroy) (void *private);
+
+ /* Return a memory offset from the opaque reference returned from the
+ "alloc" function */
+
+ unsigned long (*offset) (void *private, void *ref);
+} drm_sman_mm_t;
+
+typedef struct drm_memblock_item {
+ struct list_head owner_list;
+ drm_hash_item_t user_hash;
+ void *mm_info;
+ drm_sman_mm_t *mm;
+ struct drm_sman *sman;
+} drm_memblock_item_t;
+
+typedef struct drm_sman {
+ drm_sman_mm_t *mm;
+ int num_managers;
+ drm_open_hash_t owner_hash_tab;
+ drm_open_hash_t user_hash_tab;
+ struct list_head owner_items;
+} drm_sman_t;
+
+/*
+ * Take down a memory manager. This function should only be called after a
+ * successful init and after a call to drm_sman_cleanup.
+ */
+
+extern void drm_sman_takedown(drm_sman_t * sman);
+
+/*
+ * Allocate structures for a manager.
+ * num_managers are the number of memory pools to manage. (VRAM, AGP, ....)
+ * user_order is the log2 of the number of buckets in the user hash table.
+ * set this to approximately log2 of the max number of memory regions
+ * that will be allocated for _all_ pools together.
+ * owner_order is the log2 of the number of buckets in the owner hash table.
+ * set this to approximately log2 of
+ * the number of client file connections that will
+ * be using the manager.
+ *
+ */
+
+extern int drm_sman_init(drm_sman_t * sman, unsigned int num_managers,
+ unsigned int user_order, unsigned int owner_order);
+
+/*
+ * Initialize a drm_mm.c allocator. Should be called only once for each
+ * manager unless a customized allogator is used.
+ */
+
+extern int drm_sman_set_range(drm_sman_t * sman, unsigned int manager,
+ unsigned long start, unsigned long size);
+
+/*
+ * Initialize a customized allocator for one of the managers.
+ * (See the SiS module). The object pointed to by "allocator" is copied,
+ * so it can be destroyed after this call.
+ */
+
+extern int drm_sman_set_manager(drm_sman_t * sman, unsigned int mananger,
+ drm_sman_mm_t * allocator);
+
+/*
+ * Allocate a memory block. Aligment is not implemented yet.
+ */
+
+extern drm_memblock_item_t *drm_sman_alloc(drm_sman_t * sman,
+ unsigned int manager,
+ unsigned long size,
+ unsigned alignment,
+ unsigned long owner);
+/*
+ * Free a memory block identified by its user hash key.
+ */
+
+extern int drm_sman_free_key(drm_sman_t * sman, unsigned int key);
+
+/*
+ * returns 1 iff there are no stale memory blocks associated with this owner.
+ * Typically called to determine if we need to idle the hardware and call
+ * drm_sman_owner_cleanup. If there are no stale memory blocks, it removes all
+ * resources associated with owner.
+ */
+
+extern int drm_sman_owner_clean(drm_sman_t * sman, unsigned long owner);
+
+/*
+ * Frees all stale memory blocks associated with this owner. Note that this
+ * requires that the hardware is finished with all blocks, so the graphics engine
+ * should be idled before this call is made. This function also frees
+ * any resources associated with "owner" and should be called when owner
+ * is not going to be referenced anymore.
+ */
+
+extern void drm_sman_owner_cleanup(drm_sman_t * sman, unsigned long owner);
+
+/*
+ * Frees all stale memory blocks associated with the memory manager.
+ * See idling above.
+ */
+
+extern void drm_sman_cleanup(drm_sman_t * sman);
+
+#endif
diff --git a/drivers/char/drm/drm_stub.c b/drivers/char/drm/drm_stub.c
index 9a842a36bb2..7b1d4e8659b 100644
--- a/drivers/char/drm/drm_stub.c
+++ b/drivers/char/drm/drm_stub.c
@@ -65,22 +65,22 @@ static int drm_fill_in_dev(drm_device_t * dev, struct pci_dev *pdev,
mutex_init(&dev->ctxlist_mutex);
dev->pdev = pdev;
+ dev->pci_device = pdev->device;
+ dev->pci_vendor = pdev->vendor;
#ifdef __alpha__
dev->hose = pdev->sysdata;
- dev->pci_domain = dev->hose->bus->number;
-#else
- dev->pci_domain = 0;
#endif
- dev->pci_bus = pdev->bus->number;
- dev->pci_slot = PCI_SLOT(pdev->devfn);
- dev->pci_func = PCI_FUNC(pdev->devfn);
dev->irq = pdev->irq;
dev->maplist = drm_calloc(1, sizeof(*dev->maplist), DRM_MEM_MAPS);
if (dev->maplist == NULL)
return -ENOMEM;
INIT_LIST_HEAD(&dev->maplist->head);
+ if (drm_ht_create(&dev->map_hash, 12)) {
+ drm_free(dev->maplist, sizeof(*dev->maplist), DRM_MEM_MAPS);
+ return -ENOMEM;
+ }
/* the DRM has 6 basic counters */
dev->counters = 6;
diff --git a/drivers/char/drm/drm_vm.c b/drivers/char/drm/drm_vm.c
index ffd0800ed60..b40ae438f53 100644
--- a/drivers/char/drm/drm_vm.c
+++ b/drivers/char/drm/drm_vm.c
@@ -59,7 +59,7 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma,
drm_device_t *dev = priv->head->dev;
drm_map_t *map = NULL;
drm_map_list_t *r_list;
- struct list_head *list;
+ drm_hash_item_t *hash;
/*
* Find the right map
@@ -70,14 +70,11 @@ static __inline__ struct page *drm_do_vm_nopage(struct vm_area_struct *vma,
if (!dev->agp || !dev->agp->cant_use_aperture)
goto vm_nopage_error;
- list_for_each(list, &dev->maplist->head) {
- r_list = list_entry(list, drm_map_list_t, head);
- map = r_list->map;
- if (!map)
- continue;
- if (r_list->user_token == VM_OFFSET(vma))
- break;
- }
+ if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash))
+ goto vm_nopage_error;
+
+ r_list = drm_hash_entry(hash, drm_map_list_t, hash);
+ map = r_list->map;
if (map && map->type == _DRM_AGP) {
unsigned long offset = address - vma->vm_start;
@@ -467,7 +464,7 @@ static int drm_mmap_dma(struct file *filp, struct vm_area_struct *vma)
dev = priv->head->dev;
dma = dev->dma;
DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
- vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+ vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT);
/* Length must match exact page count */
if (!dma || (length >> PAGE_SHIFT) != dma->page_count) {
@@ -521,12 +518,11 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
drm_file_t *priv = filp->private_data;
drm_device_t *dev = priv->head->dev;
drm_map_t *map = NULL;
- drm_map_list_t *r_list;
unsigned long offset = 0;
- struct list_head *list;
+ drm_hash_item_t *hash;
DRM_DEBUG("start = 0x%lx, end = 0x%lx, offset = 0x%lx\n",
- vma->vm_start, vma->vm_end, VM_OFFSET(vma));
+ vma->vm_start, vma->vm_end, vma->vm_pgoff << PAGE_SHIFT);
if (!priv->authenticated)
return -EACCES;
@@ -535,7 +531,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
* the AGP mapped at physical address 0
* --BenH.
*/
- if (!VM_OFFSET(vma)
+ if (!(vma->vm_pgoff << PAGE_SHIFT)
#if __OS_HAS_AGP
&& (!dev->agp
|| dev->agp->agp_info.device->vendor != PCI_VENDOR_ID_APPLE)
@@ -543,23 +539,12 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
)
return drm_mmap_dma(filp, vma);
- /* A sequential search of a linked list is
- fine here because: 1) there will only be
- about 5-10 entries in the list and, 2) a
- DRI client only has to do this mapping
- once, so it doesn't have to be optimized
- for performance, even if the list was a
- bit longer. */
- list_for_each(list, &dev->maplist->head) {
-
- r_list = list_entry(list, drm_map_list_t, head);
- map = r_list->map;
- if (!map)
- continue;
- if (r_list->user_token == VM_OFFSET(vma))
- break;
+ if (drm_ht_find_item(&dev->map_hash, vma->vm_pgoff << PAGE_SHIFT, &hash)) {
+ DRM_ERROR("Could not find map\n");
+ return -EINVAL;
}
+ map = drm_hash_entry(hash, drm_map_list_t, hash)->map;
if (!map || ((map->flags & _DRM_RESTRICTED) && !capable(CAP_SYS_ADMIN)))
return -EPERM;
@@ -620,7 +605,7 @@ int drm_mmap(struct file *filp, struct vm_area_struct *vma)
offset = dev->driver->get_reg_ofs(dev);
#ifdef __sparc__
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
- if (io_remap_pfn_range(DRM_RPR_ARG(vma) vma->vm_start,
+ if (io_remap_pfn_range(vma, vma->vm_start,
(map->offset + offset) >> PAGE_SHIFT,
vma->vm_end - vma->vm_start,
vma->vm_page_prot))
diff --git a/drivers/char/drm/i810_dma.c b/drivers/char/drm/i810_dma.c
index c658dde3633..fa2de70f740 100644
--- a/drivers/char/drm/i810_dma.c
+++ b/drivers/char/drm/i810_dma.c
@@ -106,7 +106,7 @@ static int i810_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
unlock_kernel();
if (io_remap_pfn_range(vma, vma->vm_start,
- VM_OFFSET(vma) >> PAGE_SHIFT,
+ vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
@@ -141,10 +141,10 @@ static int i810_map_buffer(drm_buf_t * buf, struct file *filp)
MAP_SHARED, buf->bus_address);
dev_priv->mmap_buffer = NULL;
filp->f_op = old_fops;
- if ((unsigned long)buf_priv->virtual > -1024UL) {
+ if (IS_ERR(buf_priv->virtual)) {
/* Real error */
DRM_ERROR("mmap error\n");
- retcode = (signed int)buf_priv->virtual;
+ retcode = PTR_ERR(buf_priv->virtual);
buf_priv->virtual = NULL;
}
up_write(&current->mm->mmap_sem);
@@ -808,7 +808,7 @@ static void i810_dma_dispatch_vertex(drm_device_t * dev,
((GFX_OP_PRIMITIVE | prim | ((used / 4) - 2)));
if (used & 4) {
- *(u32 *) ((u32) buf_priv->kernel_virtual + used) = 0;
+ *(u32 *) ((char *) buf_priv->kernel_virtual + used) = 0;
used += 4;
}
@@ -1166,7 +1166,7 @@ static void i810_dma_dispatch_mc(drm_device_t * dev, drm_buf_t * buf, int used,
if (buf_priv->currently_mapped == I810_BUF_MAPPED) {
if (used & 4) {
- *(u32 *) ((u32) buf_priv->virtual + used) = 0;
+ *(u32 *) ((char *) buf_priv->virtual + used) = 0;
used += 4;
}
diff --git a/drivers/char/drm/i830_dma.c b/drivers/char/drm/i830_dma.c
index b0f815d8cea..4f0e5746ab3 100644
--- a/drivers/char/drm/i830_dma.c
+++ b/drivers/char/drm/i830_dma.c
@@ -108,7 +108,7 @@ static int i830_mmap_buffers(struct file *filp, struct vm_area_struct *vma)
unlock_kernel();
if (io_remap_pfn_range(vma, vma->vm_start,
- VM_OFFSET(vma) >> PAGE_SHIFT,
+ vma->vm_pgoff,
vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
@@ -146,7 +146,7 @@ static int i830_map_buffer(drm_buf_t * buf, struct file *filp)
if (IS_ERR((void *)virtual)) { /* ugh */
/* Real error */
DRM_ERROR("mmap error\n");
- retcode = virtual;
+ retcode = PTR_ERR((void *)virtual);
buf_priv->virtual = NULL;
} else {
buf_priv->virtual = (void __user *)virtual;
diff --git a/drivers/char/drm/i915_dma.c b/drivers/char/drm/i915_dma.c
index a94233bdbc0..fb7913ff528 100644
--- a/drivers/char/drm/i915_dma.c
+++ b/drivers/char/drm/i915_dma.c
@@ -31,6 +31,11 @@
#include "i915_drm.h"
#include "i915_drv.h"
+#define IS_I965G(dev) (dev->pci_device == 0x2972 || \
+ dev->pci_device == 0x2982 || \
+ dev->pci_device == 0x2992 || \
+ dev->pci_device == 0x29A2)
+
/* Really want an OS-independent resettable timer. Would like to have
* this loop run for (eg) 3 sec, but have the timer reset every time
* the head pointer changes, so that EBUSY only happens if the ring
@@ -255,7 +260,7 @@ static int i915_dma_init(DRM_IOCTL_ARGS)
retcode = i915_dma_resume(dev);
break;
default:
- retcode = -EINVAL;
+ retcode = DRM_ERR(EINVAL);
break;
}
@@ -347,7 +352,7 @@ static int i915_emit_cmds(drm_device_t * dev, int __user * buffer, int dwords)
if ((dwords+1) * sizeof(int) >= dev_priv->ring.Size - 8)
return DRM_ERR(EINVAL);
- BEGIN_LP_RING(((dwords+1)&~1));
+ BEGIN_LP_RING((dwords+1)&~1);
for (i = 0; i < dwords;) {
int cmd, sz;
@@ -386,7 +391,7 @@ static int i915_emit_box(drm_device_t * dev,
RING_LOCALS;
if (DRM_COPY_FROM_USER_UNCHECKED(&box, &boxes[i], sizeof(box))) {
- return EFAULT;
+ return DRM_ERR(EFAULT);
}
if (box.y2 <= box.y1 || box.x2 <= box.x1 || box.y2 <= 0 || box.x2 <= 0) {
@@ -395,24 +400,40 @@ static int i915_emit_box(drm_device_t * dev,
return DRM_ERR(EINVAL);
}
- BEGIN_LP_RING(6);
- OUT_RING(GFX_OP_DRAWRECT_INFO);
- OUT_RING(DR1);
- OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
- OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
- OUT_RING(DR4);
- OUT_RING(0);
- ADVANCE_LP_RING();
+ if (IS_I965G(dev)) {
+ BEGIN_LP_RING(4);
+ OUT_RING(GFX_OP_DRAWRECT_INFO_I965);
+ OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
+ OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
+ OUT_RING(DR4);
+ ADVANCE_LP_RING();
+ } else {
+ BEGIN_LP_RING(6);
+ OUT_RING(GFX_OP_DRAWRECT_INFO);
+ OUT_RING(DR1);
+ OUT_RING((box.x1 & 0xffff) | (box.y1 << 16));
+ OUT_RING(((box.x2 - 1) & 0xffff) | ((box.y2 - 1) << 16));
+ OUT_RING(DR4);
+ OUT_RING(0);
+ ADVANCE_LP_RING();
+ }
return 0;
}
+/* XXX: Emitting the counter should really be moved to part of the IRQ
+ * emit. For now, do it in both places:
+ */
+
static void i915_emit_breadcrumb(drm_device_t *dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
RING_LOCALS;
- dev_priv->sarea_priv->last_enqueue = dev_priv->counter++;
+ dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
+
+ if (dev_priv->counter > 0x7FFFFFFFUL)
+ dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
BEGIN_LP_RING(4);
OUT_RING(CMD_STORE_DWORD_IDX);
diff --git a/drivers/char/drm/i915_drm.h b/drivers/char/drm/i915_drm.h
index 5aa3e0e3bb4..6af83e613f2 100644
--- a/drivers/char/drm/i915_drm.h
+++ b/drivers/char/drm/i915_drm.h
@@ -98,6 +98,12 @@ typedef struct _drm_i915_sarea {
int rotated_size;
int rotated_pitch;
int virtualX, virtualY;
+
+ unsigned int front_tiled;
+ unsigned int back_tiled;
+ unsigned int depth_tiled;
+ unsigned int rotated_tiled;
+ unsigned int rotated2_tiled;
} drm_i915_sarea_t;
/* Flags for perf_boxes
diff --git a/drivers/char/drm/i915_drv.h b/drivers/char/drm/i915_drv.h
index 2d565031c00..fdc2bf19271 100644
--- a/drivers/char/drm/i915_drv.h
+++ b/drivers/char/drm/i915_drv.h
@@ -146,9 +146,9 @@ extern void i915_mem_release(drm_device_t * dev,
#define BEGIN_LP_RING(n) do { \
if (I915_VERBOSE) \
DRM_DEBUG("BEGIN_LP_RING(%d) in %s\n", \
- n, __FUNCTION__); \
- if (dev_priv->ring.space < n*4) \
- i915_wait_ring(dev, n*4, __FUNCTION__); \
+ (n), __FUNCTION__); \
+ if (dev_priv->ring.space < (n)*4) \
+ i915_wait_ring(dev, (n)*4, __FUNCTION__); \
outcount = 0; \
outring = dev_priv->ring.tail; \
ringmask = dev_priv->ring.tail_mask; \
@@ -157,7 +157,7 @@ extern void i915_mem_release(drm_device_t * dev,
#define OUT_RING(n) do { \
if (I915_VERBOSE) DRM_DEBUG(" OUT_RING %x\n", (int)(n)); \
- *(volatile unsigned int *)(virt + outring) = n; \
+ *(volatile unsigned int *)(virt + outring) = (n); \
outcount++; \
outring += 4; \
outring &= ringmask; \
@@ -254,6 +254,8 @@ extern int i915_wait_ring(drm_device_t * dev, int n, const char *caller);
#define GFX_OP_DESTBUFFER_VARS ((0x3<<29)|(0x1d<<24)|(0x85<<16)|0x0)
#define GFX_OP_DRAWRECT_INFO ((0x3<<29)|(0x1d<<24)|(0x80<<16)|(0x3))
+#define GFX_OP_DRAWRECT_INFO_I965 ((0x7900<<16)|0x2)
+
#define MI_BATCH_BUFFER ((0x30<<23)|1)
#define MI_BATCH_BUFFER_START (0x31<<23)
#define MI_BATCH_BUFFER_END (0xA<<23)
diff --git a/drivers/char/drm/i915_irq.c b/drivers/char/drm/i915_irq.c
index cd96cfa430d..0d4a162aa38 100644
--- a/drivers/char/drm/i915_irq.c
+++ b/drivers/char/drm/i915_irq.c
@@ -71,21 +71,27 @@ irqreturn_t i915_driver_irq_handler(DRM_IRQ_ARGS)
static int i915_emit_irq(drm_device_t * dev)
{
drm_i915_private_t *dev_priv = dev->dev_private;
- u32 ret;
RING_LOCALS;
i915_kernel_lost_context(dev);
DRM_DEBUG("%s\n", __FUNCTION__);
- ret = dev_priv->counter;
+ dev_priv->sarea_priv->last_enqueue = ++dev_priv->counter;
- BEGIN_LP_RING(2);
+ if (dev_priv->counter > 0x7FFFFFFFUL)
+ dev_priv->sarea_priv->last_enqueue = dev_priv->counter = 1;
+
+ BEGIN_LP_RING(6);
+ OUT_RING(CMD_STORE_DWORD_IDX);
+ OUT_RING(20);
+ OUT_RING(dev_priv->counter);
+ OUT_RING(0);
OUT_RING(0);
OUT_RING(GFX_OP_USER_INTERRUPT);
ADVANCE_LP_RING();
-
- return ret;
+
+ return dev_priv->counter;
}
static int i915_wait_irq(drm_device_t * dev, int irq_nr)
diff --git a/drivers/char/drm/radeon_cp.c b/drivers/char/drm/radeon_cp.c
index 5ad43ba7b5a..5ed96568829 100644
--- a/drivers/char/drm/radeon_cp.c
+++ b/drivers/char/drm/radeon_cp.c
@@ -864,13 +864,13 @@ static int radeon_do_pixcache_flush(drm_radeon_private_t * dev_priv)
dev_priv->stats.boxes |= RADEON_BOX_WAIT_IDLE;
- tmp = RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT);
- tmp |= RADEON_RB2D_DC_FLUSH_ALL;
- RADEON_WRITE(RADEON_RB2D_DSTCACHE_CTLSTAT, tmp);
+ tmp = RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT);
+ tmp |= RADEON_RB3D_DC_FLUSH_ALL;
+ RADEON_WRITE(RADEON_RB3D_DSTCACHE_CTLSTAT, tmp);
for (i = 0; i < dev_priv->usec_timeout; i++) {
- if (!(RADEON_READ(RADEON_RB2D_DSTCACHE_CTLSTAT)
- & RADEON_RB2D_DC_BUSY)) {
+ if (!(RADEON_READ(RADEON_RB3D_DSTCACHE_CTLSTAT)
+ & RADEON_RB3D_DC_BUSY)) {
return 0;
}
DRM_UDELAY(1);
@@ -1130,7 +1130,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
| (dev_priv->fb_location >> 16));
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
RADEON_WRITE(RADEON_AGP_BASE, (unsigned int)dev->agp->base);
RADEON_WRITE(RADEON_MC_AGP_LOCATION,
(((dev_priv->gart_vm_start - 1 +
@@ -1158,7 +1158,7 @@ static void radeon_cp_init_ring_buffer(drm_device_t * dev,
dev_priv->ring.tail = cur_read_ptr;
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
RADEON_WRITE(RADEON_CP_RB_RPTR_ADDR,
dev_priv->ring_rptr->offset
- dev->agp->base + dev_priv->gart_vm_start);
@@ -1258,6 +1258,13 @@ static void radeon_test_writeback(drm_radeon_private_t * dev_priv)
dev_priv->writeback_works = 0;
DRM_INFO("writeback forced off\n");
}
+
+ if (!dev_priv->writeback_works) {
+ /* Disable writeback to avoid unnecessary bus master transfer */
+ RADEON_WRITE(RADEON_CP_RB_CNTL, RADEON_READ(RADEON_CP_RB_CNTL) |
+ RADEON_RB_NO_UPDATE);
+ RADEON_WRITE(RADEON_SCRATCH_UMSK, 0);
+ }
}
/* Enable or disable PCI-E GART on the chip */
@@ -1295,7 +1302,7 @@ static void radeon_set_pcigart(drm_radeon_private_t * dev_priv, int on)
{
u32 tmp;
- if (dev_priv->flags & CHIP_IS_PCIE) {
+ if (dev_priv->flags & RADEON_IS_PCIE) {
radeon_set_pciegart(dev_priv, on);
return;
}
@@ -1333,20 +1340,22 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
DRM_DEBUG("\n");
/* if we require new memory map but we don't have it fail */
- if ((dev_priv->flags & CHIP_NEW_MEMMAP) && !dev_priv->new_memmap)
- {
- DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX\n");
+ if ((dev_priv->flags & RADEON_NEW_MEMMAP) && !dev_priv->new_memmap) {
+ DRM_ERROR("Cannot initialise DRM on this card\nThis card requires a new X.org DDX for 3D\n");
radeon_do_cleanup_cp(dev);
return DRM_ERR(EINVAL);
}
- if (init->is_pci && (dev_priv->flags & CHIP_IS_AGP))
- {
+ if (init->is_pci && (dev_priv->flags & RADEON_IS_AGP)) {
DRM_DEBUG("Forcing AGP card to PCI mode\n");
- dev_priv->flags &= ~CHIP_IS_AGP;
+ dev_priv->flags &= ~RADEON_IS_AGP;
+ } else if (!(dev_priv->flags & (RADEON_IS_AGP | RADEON_IS_PCI | RADEON_IS_PCIE))
+ && !init->is_pci) {
+ DRM_DEBUG("Restoring AGP flag\n");
+ dev_priv->flags |= RADEON_IS_AGP;
}
- if ((!(dev_priv->flags & CHIP_IS_AGP)) && !dev->sg) {
+ if ((!(dev_priv->flags & RADEON_IS_AGP)) && !dev->sg) {
DRM_ERROR("PCI GART memory not allocated!\n");
radeon_do_cleanup_cp(dev);
return DRM_ERR(EINVAL);
@@ -1489,7 +1498,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
init->sarea_priv_offset);
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
drm_core_ioremap(dev_priv->cp_ring, dev);
drm_core_ioremap(dev_priv->ring_rptr, dev);
drm_core_ioremap(dev->agp_buffer_map, dev);
@@ -1548,7 +1557,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
* align it down.
*/
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
base = dev->agp->base;
/* Check if valid */
if ((base + dev_priv->gart_size) > dev_priv->fb_location &&
@@ -1578,7 +1587,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
}
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP)
+ if (dev_priv->flags & RADEON_IS_AGP)
dev_priv->gart_buffers_offset = (dev->agp_buffer_map->offset
- dev->agp->base
+ dev_priv->gart_vm_start);
@@ -1604,7 +1613,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->ring.high_mark = RADEON_RING_HIGH_MARK;
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
/* Turn off PCI GART */
radeon_set_pcigart(dev_priv, 0);
} else
@@ -1624,7 +1633,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
dev_priv->gart_info.mapping.handle;
dev_priv->gart_info.is_pcie =
- !!(dev_priv->flags & CHIP_IS_PCIE);
+ !!(dev_priv->flags & RADEON_IS_PCIE);
dev_priv->gart_info.gart_table_location =
DRM_ATI_GART_FB;
@@ -1636,7 +1645,7 @@ static int radeon_do_init_cp(drm_device_t * dev, drm_radeon_init_t * init)
DRM_ATI_GART_MAIN;
dev_priv->gart_info.addr = NULL;
dev_priv->gart_info.bus_addr = 0;
- if (dev_priv->flags & CHIP_IS_PCIE) {
+ if (dev_priv->flags & RADEON_IS_PCIE) {
DRM_ERROR
("Cannot use PCI Express without GART in FB memory\n");
radeon_do_cleanup_cp(dev);
@@ -1678,7 +1687,7 @@ static int radeon_do_cleanup_cp(drm_device_t * dev)
drm_irq_uninstall(dev);
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
if (dev_priv->cp_ring != NULL) {
drm_core_ioremapfree(dev_priv->cp_ring, dev);
dev_priv->cp_ring = NULL;
@@ -1733,7 +1742,7 @@ static int radeon_do_resume_cp(drm_device_t * dev)
DRM_DEBUG("Starting radeon_do_resume_cp()\n");
#if __OS_HAS_AGP
- if (dev_priv->flags & CHIP_IS_AGP) {
+ if (dev_priv->flags & RADEON_IS_AGP) {
/* Turn off PCI GART */
radeon_set_pcigart(dev_priv, 0);
} else
@@ -2177,13 +2186,15 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
dev->dev_private = (void *)dev_priv;
dev_priv->flags = flags;
- switch (flags & CHIP_FAMILY_MASK) {
+ switch (flags & RADEON_FAMILY_MASK) {
case CHIP_R100:
case CHIP_RV200:
case CHIP_R200:
case CHIP_R300:
+ case CHIP_R350:
case CHIP_R420:
- dev_priv->flags |= CHIP_HAS_HIERZ;
+ case CHIP_RV410:
+ dev_priv->flags |= RADEON_HAS_HIERZ;
break;
default:
/* all other chips have no hierarchical z buffer */
@@ -2191,13 +2202,14 @@ int radeon_driver_load(struct drm_device *dev, unsigned long flags)
}
if (drm_device_is_agp(dev))
- dev_priv->flags |= CHIP_IS_AGP;
-
- if (drm_device_is_pcie(dev))
- dev_priv->flags |= CHIP_IS_PCIE;
+ dev_priv->flags |= RADEON_IS_AGP;
+ else if (drm_device_is_pcie(dev))
+ dev_priv->flags |= RADEON_IS_PCIE;
+ else
+ dev_priv->flags |= RADEON_IS_PCI;
DRM_DEBUG("%s card detected\n",
- ((dev_priv->flags & CHIP_IS_AGP) ? "AGP" : (((dev_priv->flags & CHIP_IS_PCIE) ? "PCIE" : "PCI"))));
+ ((dev_priv->flags & RADEON_IS_AGP) ? "AGP" : (((dev_priv->flags & RADEON_IS_PCIE) ? "PCIE" : "PCI"))));
return ret;
}
diff --git a/drivers/char/drm/radeon_drv.c b/drivers/char/drm/radeon_drv.c
index eb985c2a31e..2eb652ec674 100644
--- a/drivers/char/drm/radeon_drv.c
+++ b/drivers/char/drm/radeon_drv.c
@@ -44,7 +44,7 @@ module_param_named(no_wb, radeon_no_wb, int, 0444);
static int dri_library_name(struct drm_device *dev, char *buf)
{
drm_radeon_private_t *dev_priv = dev->dev_private;
- int family = dev_priv->flags & CHIP_FAMILY_MASK;
+ int family = dev_priv->flags & RADEON_FAMILY_MASK;
return snprintf(buf, PAGE_SIZE, "%s\n",
(family < CHIP_R200) ? "radeon" :
diff --git a/drivers/char/drm/radeon_drv.h b/drivers/char/drm/radeon_drv.h
index e5a256f5429..f45cd7f147a 100644
--- a/drivers/char/drm/radeon_drv.h
+++ b/drivers/char/drm/radeon_drv.h
@@ -133,15 +133,16 @@ enum radeon_cp_microcode_version {
* Chip flags
*/
enum radeon_chip_flags {
- CHIP_FAMILY_MASK = 0x0000ffffUL,
- CHIP_FLAGS_MASK = 0xffff0000UL,
- CHIP_IS_MOBILITY = 0x00010000UL,
- CHIP_IS_IGP = 0x00020000UL,
- CHIP_SINGLE_CRTC = 0x00040000UL,
- CHIP_IS_AGP = 0x00080000UL,
- CHIP_HAS_HIERZ = 0x00100000UL,
- CHIP_IS_PCIE = 0x00200000UL,
- CHIP_NEW_MEMMAP = 0x00400000UL,
+ RADEON_FAMILY_MASK = 0x0000ffffUL,
+ RADEON_FLAGS_MASK = 0xffff0000UL,
+ RADEON_IS_MOBILITY = 0x00010000UL,
+ RADEON_IS_IGP = 0x00020000UL,
+ RADEON_SINGLE_CRTC = 0x00040000UL,
+ RADEON_IS_AGP = 0x00080000UL,
+ RADEON_HAS_HIERZ = 0x00100000UL,
+ RADEON_IS_PCIE = 0x00200000UL,
+ RADEON_NEW_MEMMAP = 0x00400000UL,
+ RADEON_IS_PCI = 0x00800000UL,
};
#define GET_RING_HEAD(dev_priv) (dev_priv->writeback_works ? \
@@ -424,6 +425,8 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp,
#define RADEON_RB3D_COLOROFFSET 0x1c40
#define RADEON_RB3D_COLORPITCH 0x1c48
+#define RADEON_SRC_X_Y 0x1590
+
#define RADEON_DP_GUI_MASTER_CNTL 0x146c
# define RADEON_GMC_SRC_PITCH_OFFSET_CNTL (1 << 0)
# define RADEON_GMC_DST_PITCH_OFFSET_CNTL (1 << 1)
@@ -441,6 +444,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp,
# define RADEON_ROP3_S 0x00cc0000
# define RADEON_ROP3_P 0x00f00000
#define RADEON_DP_WRITE_MASK 0x16cc
+#define RADEON_SRC_PITCH_OFFSET 0x1428
#define RADEON_DST_PITCH_OFFSET 0x142c
#define RADEON_DST_PITCH_OFFSET_C 0x1c80
# define RADEON_DST_TILE_LINEAR (0 << 30)
@@ -545,6 +549,11 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp,
# define RADEON_RB3D_ZC_FREE (1 << 2)
# define RADEON_RB3D_ZC_FLUSH_ALL 0x5
# define RADEON_RB3D_ZC_BUSY (1 << 31)
+#define RADEON_RB3D_DSTCACHE_CTLSTAT 0x325c
+# define RADEON_RB3D_DC_FLUSH (3 << 0)
+# define RADEON_RB3D_DC_FREE (3 << 2)
+# define RADEON_RB3D_DC_FLUSH_ALL 0xf
+# define RADEON_RB3D_DC_BUSY (1 << 31)
#define RADEON_RB3D_ZSTENCILCNTL 0x1c2c
# define RADEON_Z_TEST_MASK (7 << 4)
# define RADEON_Z_TEST_ALWAYS (7 << 4)
@@ -681,6 +690,7 @@ extern int r300_do_cp_cmdbuf(drm_device_t * dev, DRMFILE filp,
#define RADEON_CP_RB_BASE 0x0700
#define RADEON_CP_RB_CNTL 0x0704
# define RADEON_BUF_SWAP_32BIT (2 << 16)
+# define RADEON_RB_NO_UPDATE (1 << 27)
#define RADEON_CP_RB_RPTR_ADDR 0x070c
#define RADEON_CP_RB_RPTR 0x0710
#define RADEON_CP_RB_WPTR 0x0714
@@ -986,13 +996,13 @@ do { \
} while (0)
#define RADEON_FLUSH_CACHE() do { \
- OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \
- OUT_RING( RADEON_RB2D_DC_FLUSH ); \
+ OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \
+ OUT_RING( RADEON_RB3D_DC_FLUSH ); \
} while (0)
#define RADEON_PURGE_CACHE() do { \
- OUT_RING( CP_PACKET0( RADEON_RB2D_DSTCACHE_CTLSTAT, 0 ) ); \
- OUT_RING( RADEON_RB2D_DC_FLUSH_ALL ); \
+ OUT_RING( CP_PACKET0( RADEON_RB3D_DSTCACHE_CTLSTAT, 0 ) ); \
+ OUT_RING( RADEON_RB3D_DC_FLUSH_ALL ); \
} while (0)
#define RADEON_FLUSH_ZCACHE() do { \
diff --git a/drivers/char/drm/radeon_state.c b/drivers/char/drm/radeon_state.c
index 39a7f685e3f..feac5f005d4 100644
--- a/drivers/char/drm/radeon_state.c
+++ b/drivers/char/drm/radeon_state.c
@@ -42,7 +42,11 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
drm_file_t * filp_priv,
u32 *offset)
{
- u32 off = *offset;
+ u64 off = *offset;
+ u32 fb_start = dev_priv->fb_location;
+ u32 fb_end = fb_start + dev_priv->fb_size - 1;
+ u32 gart_start = dev_priv->gart_vm_start;
+ u32 gart_end = gart_start + dev_priv->gart_size - 1;
struct drm_radeon_driver_file_fields *radeon_priv;
/* Hrm ... the story of the offset ... So this function converts
@@ -62,10 +66,8 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
/* First, the best case, the offset already lands in either the
* framebuffer or the GART mapped space
*/
- if ((off >= dev_priv->fb_location &&
- off < (dev_priv->fb_location + dev_priv->fb_size)) ||
- (off >= dev_priv->gart_vm_start &&
- off < (dev_priv->gart_vm_start + dev_priv->gart_size)))
+ if ((off >= fb_start && off <= fb_end) ||
+ (off >= gart_start && off <= gart_end))
return 0;
/* Ok, that didn't happen... now check if we have a zero based
@@ -78,16 +80,13 @@ static __inline__ int radeon_check_and_fixup_offset(drm_radeon_private_t *
}
/* Finally, assume we aimed at a GART offset if beyond the fb */
- if (off > (dev_priv->fb_location + dev_priv->fb_size))
- off = off - (dev_priv->fb_location + dev_priv->fb_size) +
- dev_priv->gart_vm_start;
+ if (off > fb_end)
+ off = off - fb_end - 1 + gart_start;
/* Now recheck and fail if out of bounds */
- if ((off >= dev_priv->fb_location &&
- off < (dev_priv->fb_location + dev_priv->fb_size)) ||
- (off >= dev_priv->gart_vm_start &&
- off < (dev_priv->gart_vm_start + dev_priv->gart_size))) {
- DRM_DEBUG("offset fixed up to 0x%x\n", off);
+ if ((off >= fb_start && off <= fb_end) ||
+ (off >= gart_start && off <= gart_end)) {
+ DRM_DEBUG("offset fixed up to 0x%x\n", (unsigned int)off);
*offset = off;
return 0;
}
@@ -869,7 +868,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev,
*/
dev_priv->sarea_priv->ctx_owner = 0;
- if ((dev_priv->flags & CHIP_HAS_HIERZ)
+ if ((dev_priv->flags & RADEON_HAS_HIERZ)
&& (flags & RADEON_USE_HIERZ)) {
/* FIXME : reverse engineer that for Rx00 cards */
/* FIXME : the mask supposedly contains low-res z values. So can't set
@@ -914,7 +913,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev,
for (i = 0; i < nbox; i++) {
int tileoffset, nrtilesx, nrtilesy, j;
/* it looks like r200 needs rv-style clears, at least if hierz is not enabled? */
- if ((dev_priv->flags & CHIP_HAS_HIERZ)
+ if ((dev_priv->flags & RADEON_HAS_HIERZ)
&& !(dev_priv->microcode_version == UCODE_R200)) {
/* FIXME : figure this out for r200 (when hierz is enabled). Or
maybe r200 actually doesn't need to put the low-res z value into
@@ -998,7 +997,7 @@ static void radeon_cp_dispatch_clear(drm_device_t * dev,
}
/* TODO don't always clear all hi-level z tiles */
- if ((dev_priv->flags & CHIP_HAS_HIERZ)
+ if ((dev_priv->flags & RADEON_HAS_HIERZ)
&& (dev_priv->microcode_version == UCODE_R200)
&& (flags & RADEON_USE_HIERZ))
/* r100 and cards without hierarchical z-buffer have no high-level z-buffer */
@@ -1270,9 +1269,9 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev)
DRM_DEBUG("dispatch swap %d,%d-%d,%d\n", x, y, w, h);
- BEGIN_RING(7);
+ BEGIN_RING(9);
- OUT_RING(CP_PACKET3(RADEON_CNTL_BITBLT_MULTI, 5));
+ OUT_RING(CP_PACKET0(RADEON_DP_GUI_MASTER_CNTL, 0));
OUT_RING(RADEON_GMC_SRC_PITCH_OFFSET_CNTL |
RADEON_GMC_DST_PITCH_OFFSET_CNTL |
RADEON_GMC_BRUSH_NONE |
@@ -1284,6 +1283,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev)
/* Make this work even if front & back are flipped:
*/
+ OUT_RING(CP_PACKET0(RADEON_SRC_PITCH_OFFSET, 1));
if (dev_priv->current_page == 0) {
OUT_RING(dev_priv->back_pitch_offset);
OUT_RING(dev_priv->front_pitch_offset);
@@ -1292,6 +1292,7 @@ static void radeon_cp_dispatch_swap(drm_device_t * dev)
OUT_RING(dev_priv->back_pitch_offset);
}
+ OUT_RING(CP_PACKET0(RADEON_SRC_X_Y, 2));
OUT_RING((x << 16) | y);
OUT_RING((x << 16) | y);
OUT_RING((w << 16) | h);
@@ -2987,16 +2988,21 @@ static int radeon_cp_getparam(DRM_IOCTL_ARGS)
case RADEON_PARAM_GART_TEX_HANDLE:
value = dev_priv->gart_textures_offset;
break;
-
+ case RADEON_PARAM_SCRATCH_OFFSET:
+ if (!dev_priv->writeback_works)
+ return DRM_ERR(EINVAL);
+ value = RADEON_SCRATCH_REG_OFFSET;
+ break;
case RADEON_PARAM_CARD_TYPE:
- if (dev_priv->flags & CHIP_IS_PCIE)
+ if (dev_priv->flags & RADEON_IS_PCIE)
value = RADEON_CARD_PCIE;
- else if (dev_priv->flags & CHIP_IS_AGP)
+ else if (dev_priv->flags & RADEON_IS_AGP)
value = RADEON_CARD_AGP;
else
value = RADEON_CARD_PCI;
break;
default:
+ DRM_DEBUG("Invalid parameter %d\n", param.param);
return DRM_ERR(EINVAL);
}
diff --git a/drivers/char/drm/sis_drv.c b/drivers/char/drm/sis_drv.c
index 5e9dc86f295..3d5b3218b6f 100644
--- a/drivers/char/drm/sis_drv.c
+++ b/drivers/char/drm/sis_drv.c
@@ -35,11 +35,44 @@ static struct pci_device_id pciidlist[] = {
sisdrv_PCI_IDS
};
+static int sis_driver_load(drm_device_t *dev, unsigned long chipset)
+{
+ drm_sis_private_t *dev_priv;
+ int ret;
+
+ dev_priv = drm_calloc(1, sizeof(drm_sis_private_t), DRM_MEM_DRIVER);
+ if (dev_priv == NULL)
+ return DRM_ERR(ENOMEM);
+
+ dev->dev_private = (void *)dev_priv;
+ dev_priv->chipset = chipset;
+ ret = drm_sman_init(&dev_priv->sman, 2, 12, 8);
+ if (ret) {
+ drm_free(dev_priv, sizeof(dev_priv), DRM_MEM_DRIVER);
+ }
+
+ return ret;
+}
+
+static int sis_driver_unload(drm_device_t *dev)
+{
+ drm_sis_private_t *dev_priv = dev->dev_private;
+
+ drm_sman_takedown(&dev_priv->sman);
+ drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+
+ return 0;
+}
+
static struct drm_driver driver = {
.driver_features = DRIVER_USE_AGP | DRIVER_USE_MTRR,
- .context_ctor = sis_init_context,
- .context_dtor = sis_final_context,
- .reclaim_buffers = drm_core_reclaim_buffers,
+ .load = sis_driver_load,
+ .unload = sis_driver_unload,
+ .context_dtor = NULL,
+ .dma_quiescent = sis_idle,
+ .reclaim_buffers = NULL,
+ .reclaim_buffers_locked = sis_reclaim_buffers_locked,
+ .lastclose = sis_lastclose,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = sis_ioctls,
diff --git a/drivers/char/drm/sis_drv.h b/drivers/char/drm/sis_drv.h
index e218e526950..2b8d6f6ed7c 100644
--- a/drivers/char/drm/sis_drv.h
+++ b/drivers/char/drm/sis_drv.h
@@ -31,23 +31,39 @@
/* General customization:
*/
-#define DRIVER_AUTHOR "SIS"
+#define DRIVER_AUTHOR "SIS, Tungsten Graphics"
#define DRIVER_NAME "sis"
#define DRIVER_DESC "SIS 300/630/540"
-#define DRIVER_DATE "20030826"
+#define DRIVER_DATE "20060704"
#define DRIVER_MAJOR 1
-#define DRIVER_MINOR 1
-#define DRIVER_PATCHLEVEL 0
+#define DRIVER_MINOR 2
+#define DRIVER_PATCHLEVEL 1
-#include "sis_ds.h"
+enum sis_family {
+ SIS_OTHER = 0,
+ SIS_CHIP_315 = 1,
+};
+
+#include "drm_sman.h"
+
+#define SIS_BASE (dev_priv->mmio)
+#define SIS_READ(reg) DRM_READ32(SIS_BASE, reg);
+#define SIS_WRITE(reg, val) DRM_WRITE32(SIS_BASE, reg, val);
typedef struct drm_sis_private {
- memHeap_t *AGPHeap;
- memHeap_t *FBHeap;
+ drm_local_map_t *mmio;
+ unsigned int idle_fault;
+ drm_sman_t sman;
+ unsigned int chipset;
+ int vram_initialized;
+ int agp_initialized;
+ unsigned long vram_offset;
+ unsigned long agp_offset;
} drm_sis_private_t;
-extern int sis_init_context(drm_device_t * dev, int context);
-extern int sis_final_context(drm_device_t * dev, int context);
+extern int sis_idle(drm_device_t *dev);
+extern void sis_reclaim_buffers_locked(drm_device_t *dev, struct file *filp);
+extern void sis_lastclose(drm_device_t *dev);
extern drm_ioctl_desc_t sis_ioctls[];
extern int sis_max_ioctl;
diff --git a/drivers/char/drm/sis_ds.c b/drivers/char/drm/sis_ds.c
deleted file mode 100644
index 2e485d48294..00000000000
--- a/drivers/char/drm/sis_ds.c
+++ /dev/null
@@ -1,299 +0,0 @@
-/* sis_ds.c -- Private header for Direct Rendering Manager -*- linux-c -*-
- * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw
- *
- * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sung-Ching Lin <sclin@sis.com.tw>
- *
- */
-
-#include "drmP.h"
-#include "drm.h"
-#include "sis_ds.h"
-
-/* Set Data Structure, not check repeated value
- * temporarily used
- */
-
-set_t *setInit(void)
-{
- int i;
- set_t *set;
-
- set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER);
- if (set != NULL) {
- for (i = 0; i < SET_SIZE; i++) {
- set->list[i].free_next = i + 1;
- set->list[i].alloc_next = -1;
- }
- set->list[SET_SIZE - 1].free_next = -1;
- set->free = 0;
- set->alloc = -1;
- set->trace = -1;
- }
- return set;
-}
-
-int setAdd(set_t * set, ITEM_TYPE item)
-{
- int free = set->free;
-
- if (free != -1) {
- set->list[free].val = item;
- set->free = set->list[free].free_next;
- } else {
- return 0;
- }
-
- set->list[free].alloc_next = set->alloc;
- set->alloc = free;
- set->list[free].free_next = -1;
-
- return 1;
-}
-
-int setDel(set_t * set, ITEM_TYPE item)
-{
- int alloc = set->alloc;
- int prev = -1;
-
- while (alloc != -1) {
- if (set->list[alloc].val == item) {
- if (prev != -1)
- set->list[prev].alloc_next =
- set->list[alloc].alloc_next;
- else
- set->alloc = set->list[alloc].alloc_next;
- break;
- }
- prev = alloc;
- alloc = set->list[alloc].alloc_next;
- }
-
- if (alloc == -1)
- return 0;
-
- set->list[alloc].free_next = set->free;
- set->free = alloc;
- set->list[alloc].alloc_next = -1;
-
- return 1;
-}
-
-/* setFirst -> setAdd -> setNext is wrong */
-
-int setFirst(set_t * set, ITEM_TYPE * item)
-{
- if (set->alloc == -1)
- return 0;
-
- *item = set->list[set->alloc].val;
- set->trace = set->list[set->alloc].alloc_next;
-
- return 1;
-}
-
-int setNext(set_t * set, ITEM_TYPE * item)
-{
- if (set->trace == -1)
- return 0;
-
- *item = set->list[set->trace].val;
- set->trace = set->list[set->trace].alloc_next;
-
- return 1;
-}
-
-int setDestroy(set_t * set)
-{
- drm_free(set, sizeof(set_t), DRM_MEM_DRIVER);
-
- return 1;
-}
-
-/*
- * GLX Hardware Device Driver common code
- * Copyright (C) 1999 Wittawat Yamwong
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-#define ISFREE(bptr) ((bptr)->free)
-
-memHeap_t *mmInit(int ofs, int size)
-{
- PMemBlock blocks;
-
- if (size <= 0)
- return NULL;
-
- blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER);
- if (blocks != NULL) {
- blocks->ofs = ofs;
- blocks->size = size;
- blocks->free = 1;
- return (memHeap_t *) blocks;
- } else
- return NULL;
-}
-
-/* Checks if a pointer 'b' is part of the heap 'heap' */
-int mmBlockInHeap(memHeap_t * heap, PMemBlock b)
-{
- TMemBlock *p;
-
- if (heap == NULL || b == NULL)
- return 0;
-
- p = heap;
- while (p != NULL && p != b) {
- p = p->next;
- }
- if (p == b)
- return 1;
- else
- return 0;
-}
-
-static TMemBlock *SliceBlock(TMemBlock * p,
- int startofs, int size,
- int reserved, int alignment)
-{
- TMemBlock *newblock;
-
- /* break left */
- if (startofs > p->ofs) {
- newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock),
- DRM_MEM_DRIVER);
- newblock->ofs = startofs;
- newblock->size = p->size - (startofs - p->ofs);
- newblock->free = 1;
- newblock->next = p->next;
- p->size -= newblock->size;
- p->next = newblock;
- p = newblock;
- }
-
- /* break right */
- if (size < p->size) {
- newblock = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock),
- DRM_MEM_DRIVER);
- newblock->ofs = startofs + size;
- newblock->size = p->size - size;
- newblock->free = 1;
- newblock->next = p->next;
- p->size = size;
- p->next = newblock;
- }
-
- /* p = middle block */
- p->align = alignment;
- p->free = 0;
- p->reserved = reserved;
- return p;
-}
-
-PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch)
-{
- int mask, startofs, endofs;
- TMemBlock *p;
-
- if (heap == NULL || align2 < 0 || size <= 0)
- return NULL;
-
- mask = (1 << align2) - 1;
- startofs = 0;
- p = (TMemBlock *) heap;
- while (p != NULL) {
- if (ISFREE(p)) {
- startofs = (p->ofs + mask) & ~mask;
- if (startofs < startSearch) {
- startofs = startSearch;
- }
- endofs = startofs + size;
- if (endofs <= (p->ofs + p->size))
- break;
- }
- p = p->next;
- }
- if (p == NULL)
- return NULL;
- p = SliceBlock(p, startofs, size, 0, mask + 1);
- p->heap = heap;
- return p;
-}
-
-static __inline__ int Join2Blocks(TMemBlock * p)
-{
- if (p->free && p->next && p->next->free) {
- TMemBlock *q = p->next;
- p->size += q->size;
- p->next = q->next;
- drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER);
- return 1;
- }
- return 0;
-}
-
-int mmFreeMem(PMemBlock b)
-{
- TMemBlock *p, *prev;
-
- if (b == NULL)
- return 0;
- if (b->heap == NULL)
- return -1;
-
- p = b->heap;
- prev = NULL;
- while (p != NULL && p != b) {
- prev = p;
- p = p->next;
- }
- if (p == NULL || p->free || p->reserved)
- return -1;
-
- p->free = 1;
- Join2Blocks(p);
- if (prev)
- Join2Blocks(prev);
- return 0;
-}
diff --git a/drivers/char/drm/sis_ds.h b/drivers/char/drm/sis_ds.h
deleted file mode 100644
index 94f2b4728b6..00000000000
--- a/drivers/char/drm/sis_ds.h
+++ /dev/null
@@ -1,146 +0,0 @@
-/* sis_ds.h -- Private header for Direct Rendering Manager -*- linux-c -*-
- * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw
- */
-/*
- * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- *
- * Authors:
- * Sung-Ching Lin <sclin@sis.com.tw>
- *
- */
-
-#ifndef __SIS_DS_H__
-#define __SIS_DS_H__
-
-/* Set Data Structure */
-
-#define SET_SIZE 5000
-
-typedef unsigned long ITEM_TYPE;
-
-typedef struct {
- ITEM_TYPE val;
- int alloc_next, free_next;
-} list_item_t;
-
-typedef struct {
- int alloc;
- int free;
- int trace;
- list_item_t list[SET_SIZE];
-} set_t;
-
-set_t *setInit(void);
-int setAdd(set_t * set, ITEM_TYPE item);
-int setDel(set_t * set, ITEM_TYPE item);
-int setFirst(set_t * set, ITEM_TYPE * item);
-int setNext(set_t * set, ITEM_TYPE * item);
-int setDestroy(set_t * set);
-
-/*
- * GLX Hardware Device Driver common code
- * Copyright (C) 1999 Wittawat Yamwong
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included
- * in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
- * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * WITTAWAT YAMWONG, OR ANY OTHER CONTRIBUTORS BE LIABLE FOR ANY CLAIM,
- * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
- * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
- * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-
-struct mem_block_t {
- struct mem_block_t *next;
- struct mem_block_t *heap;
- int ofs, size;
- int align;
- unsigned int free:1;
- unsigned int reserved:1;
-};
-typedef struct mem_block_t TMemBlock;
-typedef struct mem_block_t *PMemBlock;
-
-/* a heap is just the first block in a chain */
-typedef struct mem_block_t memHeap_t;
-
-static __inline__ int mmBlockSize(PMemBlock b)
-{
- return b->size;
-}
-
-static __inline__ int mmOffset(PMemBlock b)
-{
- return b->ofs;
-}
-
-static __inline__ void mmMarkReserved(PMemBlock b)
-{
- b->reserved = 1;
-}
-
-/*
- * input: total size in bytes
- * return: a heap pointer if OK, NULL if error
- */
-memHeap_t *mmInit(int ofs, int size);
-
-/*
- * Allocate 'size' bytes with 2^align2 bytes alignment,
- * restrict the search to free memory after 'startSearch'
- * depth and back buffers should be in different 4mb banks
- * to get better page hits if possible
- * input: size = size of block
- * align2 = 2^align2 bytes alignment
- * startSearch = linear offset from start of heap to begin search
- * return: pointer to the allocated block, 0 if error
- */
-PMemBlock mmAllocMem(memHeap_t * heap, int size, int align2, int startSearch);
-
-/*
- * Returns 1 if the block 'b' is part of the heap 'heap'
- */
-int mmBlockInHeap(PMemBlock heap, PMemBlock b);
-
-/*
- * Free block starts at offset
- * input: pointer to a block
- * return: 0 if OK, -1 if error
- */
-int mmFreeMem(PMemBlock b);
-
-/* For debuging purpose. */
-void mmDumpMemInfo(memHeap_t * mmInit);
-
-#endif /* __SIS_DS_H__ */
diff --git a/drivers/char/drm/sis_mm.c b/drivers/char/drm/sis_mm.c
index 5e9936bc307..d26f5dbb785 100644
--- a/drivers/char/drm/sis_mm.c
+++ b/drivers/char/drm/sis_mm.c
@@ -1,414 +1,348 @@
-/* sis_mm.c -- Private header for Direct Rendering Manager -*- linux-c -*-
- * Created: Mon Jan 4 10:05:05 1999 by sclin@sis.com.tw
+/**************************************************************************
*
- * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
- * All rights reserved.
+ * Copyright 2006 Tungsten Graphics, Inc., Bismarck, ND., USA.
+ * All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sublicense,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sub license, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
*
- * The above copyright notice and this permission notice (including the next
- * paragraph) shall be included in all copies or substantial portions of the
- * Software.
+ * The above copyright notice and this permission notice (including the
+ * next paragraph) shall be included in all copies or substantial portions
+ * of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
- * PRECISION INSIGHT AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE COPYRIGHT HOLDERS, AUTHORS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
*
- * Authors:
- * Sung-Ching Lin <sclin@sis.com.tw>
*
+ **************************************************************************/
+
+/*
+ * Authors:
+ * Thomas Hellström <thomas-at-tungstengraphics-dot-com>
*/
#include "drmP.h"
#include "sis_drm.h"
#include "sis_drv.h"
-#include "sis_ds.h"
-#if defined(__linux__) && defined(CONFIG_FB_SIS)
+
#include <video/sisfb.h>
-#endif
-#define MAX_CONTEXT 100
#define VIDEO_TYPE 0
#define AGP_TYPE 1
-typedef struct {
- int used;
- int context;
- set_t *sets[2]; /* 0 for video, 1 for AGP */
-} sis_context_t;
-static sis_context_t global_ppriv[MAX_CONTEXT];
+#if defined(CONFIG_FB_SIS)
+/* fb management via fb device */
-static int add_alloc_set(int context, int type, unsigned int val)
-{
- int i, retval = 0;
+#define SIS_MM_ALIGN_SHIFT 0
+#define SIS_MM_ALIGN_MASK 0
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (global_ppriv[i].used && global_ppriv[i].context == context) {
- retval = setAdd(global_ppriv[i].sets[type], val);
- break;
- }
- }
- return retval;
-}
-
-static int del_alloc_set(int context, int type, unsigned int val)
+static void *sis_sman_mm_allocate(void *private, unsigned long size,
+ unsigned alignment)
{
- int i, retval = 0;
+ struct sis_memreq req;
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (global_ppriv[i].used && global_ppriv[i].context == context) {
- retval = setDel(global_ppriv[i].sets[type], val);
- break;
- }
- }
- return retval;
+ req.size = size;
+ sis_malloc(&req);
+ if (req.size == 0)
+ return NULL;
+ else
+ return (void *)~req.offset;
}
-/* fb management via fb device */
-#if defined(__linux__) && defined(CONFIG_FB_SIS)
-
-static int sis_fb_init(DRM_IOCTL_ARGS)
+static void sis_sman_mm_free(void *private, void *ref)
{
- return 0;
+ sis_free(~((unsigned long)ref));
}
-static int sis_fb_alloc(DRM_IOCTL_ARGS)
+static void sis_sman_mm_destroy(void *private)
{
- drm_sis_mem_t fb;
- struct sis_memreq req;
- drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data;
- int retval = 0;
-
- DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb));
-
- req.size = fb.size;
- sis_malloc(&req);
- if (req.offset) {
- /* TODO */
- fb.offset = req.offset;
- fb.free = req.offset;
- if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) {
- DRM_DEBUG("adding to allocation set fails\n");
- sis_free(req.offset);
- retval = DRM_ERR(EINVAL);
- }
- } else {
- fb.offset = 0;
- fb.size = 0;
- fb.free = 0;
- }
-
- DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb));
-
- DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, req.offset);
-
- return retval;
+ ;
}
-static int sis_fb_free(DRM_IOCTL_ARGS)
+static unsigned long sis_sman_mm_offset(void *private, void *ref)
{
- drm_sis_mem_t fb;
- int retval = 0;
-
- DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb));
-
- if (!fb.free)
- return DRM_ERR(EINVAL);
+ return ~((unsigned long)ref);
+}
- if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free))
- retval = DRM_ERR(EINVAL);
- sis_free(fb.free);
+#else /* CONFIG_FB_SIS */
- DRM_DEBUG("free fb, offset = 0x%lx\n", fb.free);
+#define SIS_MM_ALIGN_SHIFT 4
+#define SIS_MM_ALIGN_MASK ( (1 << SIS_MM_ALIGN_SHIFT) - 1)
- return retval;
-}
+#endif /* CONFIG_FB_SIS */
-#else
-
-/* Called by the X Server to initialize the FB heap. Allocations will fail
- * unless this is called. Offset is the beginning of the heap from the
- * framebuffer offset (MaxXFBMem in XFree86).
- *
- * Memory layout according to Thomas Winischofer:
- * |------------------|DDDDDDDDDDDDDDDDDDDDDDDDDDDDD|HHHH|CCCCCCCCCCC|
- *
- * X driver/sisfb HW- Command-
- * framebuffer memory DRI heap Cursor queue
- */
static int sis_fb_init(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
drm_sis_private_t *dev_priv = dev->dev_private;
drm_sis_fb_t fb;
+ int ret;
DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_fb_t __user *) data, sizeof(fb));
- if (dev_priv == NULL) {
- dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t),
- DRM_MEM_DRIVER);
- dev_priv = dev->dev_private;
- if (dev_priv == NULL)
- return ENOMEM;
+ mutex_lock(&dev->struct_mutex);
+#if defined(CONFIG_FB_SIS)
+ {
+ drm_sman_mm_t sman_mm;
+ sman_mm.private = (void *)0xFFFFFFFF;
+ sman_mm.allocate = sis_sman_mm_allocate;
+ sman_mm.free = sis_sman_mm_free;
+ sman_mm.destroy = sis_sman_mm_destroy;
+ sman_mm.offset = sis_sman_mm_offset;
+ ret =
+ drm_sman_set_manager(&dev_priv->sman, VIDEO_TYPE, &sman_mm);
}
+#else
+ ret = drm_sman_set_range(&dev_priv->sman, VIDEO_TYPE, 0,
+ fb.size >> SIS_MM_ALIGN_SHIFT);
+#endif
- if (dev_priv->FBHeap != NULL)
- return DRM_ERR(EINVAL);
+ if (ret) {
+ DRM_ERROR("VRAM memory manager initialisation error\n");
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
- dev_priv->FBHeap = mmInit(fb.offset, fb.size);
+ dev_priv->vram_initialized = 1;
+ dev_priv->vram_offset = fb.offset;
+ mutex_unlock(&dev->struct_mutex);
DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size);
return 0;
}
-static int sis_fb_alloc(DRM_IOCTL_ARGS)
+static int sis_drm_alloc(drm_device_t * dev, drm_file_t * priv,
+ unsigned long data, int pool)
{
- DRM_DEVICE;
drm_sis_private_t *dev_priv = dev->dev_private;
- drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data;
- drm_sis_mem_t fb;
- PMemBlock block;
+ drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *) data;
+ drm_sis_mem_t mem;
int retval = 0;
+ drm_memblock_item_t *item;
+
+ DRM_COPY_FROM_USER_IOCTL(mem, argp, sizeof(mem));
- if (dev_priv == NULL || dev_priv->FBHeap == NULL)
+ mutex_lock(&dev->struct_mutex);
+
+ if (0 == ((pool == 0) ? dev_priv->vram_initialized :
+ dev_priv->agp_initialized)) {
+ DRM_ERROR
+ ("Attempt to allocate from uninitialized memory manager.\n");
return DRM_ERR(EINVAL);
+ }
- DRM_COPY_FROM_USER_IOCTL(fb, argp, sizeof(fb));
-
- block = mmAllocMem(dev_priv->FBHeap, fb.size, 0, 0);
- if (block) {
- /* TODO */
- fb.offset = block->ofs;
- fb.free = (unsigned long)block;
- if (!add_alloc_set(fb.context, VIDEO_TYPE, fb.free)) {
- DRM_DEBUG("adding to allocation set fails\n");
- mmFreeMem((PMemBlock) fb.free);
- retval = DRM_ERR(EINVAL);
- }
+ mem.size = (mem.size + SIS_MM_ALIGN_MASK) >> SIS_MM_ALIGN_SHIFT;
+ item = drm_sman_alloc(&dev_priv->sman, pool, mem.size, 0,
+ (unsigned long)priv);
+
+ mutex_unlock(&dev->struct_mutex);
+ if (item) {
+ mem.offset = ((pool == 0) ?
+ dev_priv->vram_offset : dev_priv->agp_offset) +
+ (item->mm->
+ offset(item->mm, item->mm_info) << SIS_MM_ALIGN_SHIFT);
+ mem.free = item->user_hash.key;
+ mem.size = mem.size << SIS_MM_ALIGN_SHIFT;
} else {
- fb.offset = 0;
- fb.size = 0;
- fb.free = 0;
+ mem.offset = 0;
+ mem.size = 0;
+ mem.free = 0;
+ retval = DRM_ERR(ENOMEM);
}
- DRM_COPY_TO_USER_IOCTL(argp, fb, sizeof(fb));
+ DRM_COPY_TO_USER_IOCTL(argp, mem, sizeof(mem));
- DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size, fb.offset);
+ DRM_DEBUG("alloc %d, size = %d, offset = %d\n", pool, mem.size,
+ mem.offset);
return retval;
}
-static int sis_fb_free(DRM_IOCTL_ARGS)
+static int sis_drm_free(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
drm_sis_private_t *dev_priv = dev->dev_private;
- drm_sis_mem_t fb;
+ drm_sis_mem_t mem;
+ int ret;
- if (dev_priv == NULL || dev_priv->FBHeap == NULL)
- return DRM_ERR(EINVAL);
+ DRM_COPY_FROM_USER_IOCTL(mem, (drm_sis_mem_t __user *) data,
+ sizeof(mem));
- DRM_COPY_FROM_USER_IOCTL(fb, (drm_sis_mem_t __user *) data, sizeof(fb));
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_sman_free_key(&dev_priv->sman, mem.free);
+ mutex_unlock(&dev->struct_mutex);
+ DRM_DEBUG("free = 0x%lx\n", mem.free);
- if (!mmBlockInHeap(dev_priv->FBHeap, (PMemBlock) fb.free))
- return DRM_ERR(EINVAL);
-
- if (!del_alloc_set(fb.context, VIDEO_TYPE, fb.free))
- return DRM_ERR(EINVAL);
- mmFreeMem((PMemBlock) fb.free);
-
- DRM_DEBUG("free fb, free = 0x%lx\n", fb.free);
-
- return 0;
+ return ret;
}
-#endif
-
-/* agp memory management */
+static int sis_fb_alloc(DRM_IOCTL_ARGS)
+{
+ DRM_DEVICE;
+ return sis_drm_alloc(dev, priv, data, VIDEO_TYPE);
+}
static int sis_ioctl_agp_init(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
drm_sis_private_t *dev_priv = dev->dev_private;
drm_sis_agp_t agp;
-
- if (dev_priv == NULL) {
- dev->dev_private = drm_calloc(1, sizeof(drm_sis_private_t),
- DRM_MEM_DRIVER);
- dev_priv = dev->dev_private;
- if (dev_priv == NULL)
- return ENOMEM;
- }
-
- if (dev_priv->AGPHeap != NULL)
- return DRM_ERR(EINVAL);
+ int ret;
+ dev_priv = dev->dev_private;
DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_agp_t __user *) data,
sizeof(agp));
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_sman_set_range(&dev_priv->sman, AGP_TYPE, 0,
+ agp.size >> SIS_MM_ALIGN_SHIFT);
+
+ if (ret) {
+ DRM_ERROR("AGP memory manager initialisation error\n");
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
- dev_priv->AGPHeap = mmInit(agp.offset, agp.size);
+ dev_priv->agp_initialized = 1;
+ dev_priv->agp_offset = agp.offset;
+ mutex_unlock(&dev->struct_mutex);
DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size);
-
return 0;
}
static int sis_ioctl_agp_alloc(DRM_IOCTL_ARGS)
{
DRM_DEVICE;
- drm_sis_private_t *dev_priv = dev->dev_private;
- drm_sis_mem_t __user *argp = (drm_sis_mem_t __user *)data;
- drm_sis_mem_t agp;
- PMemBlock block;
- int retval = 0;
- if (dev_priv == NULL || dev_priv->AGPHeap == NULL)
- return DRM_ERR(EINVAL);
+ return sis_drm_alloc(dev, priv, data, AGP_TYPE);
+}
- DRM_COPY_FROM_USER_IOCTL(agp, argp, sizeof(agp));
-
- block = mmAllocMem(dev_priv->AGPHeap, agp.size, 0, 0);
- if (block) {
- /* TODO */
- agp.offset = block->ofs;
- agp.free = (unsigned long)block;
- if (!add_alloc_set(agp.context, AGP_TYPE, agp.free)) {
- DRM_DEBUG("adding to allocation set fails\n");
- mmFreeMem((PMemBlock) agp.free);
- retval = -1;
+static drm_local_map_t *sis_reg_init(drm_device_t *dev)
+{
+ drm_map_list_t *entry;
+ drm_local_map_t *map;
+
+ list_for_each_entry(entry, &dev->maplist->head, head) {
+ map = entry->map;
+ if (!map)
+ continue;
+ if (map->type == _DRM_REGISTERS) {
+ return map;
}
- } else {
- agp.offset = 0;
- agp.size = 0;
- agp.free = 0;
}
-
- DRM_COPY_TO_USER_IOCTL(argp, agp, sizeof(agp));
-
- DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size, agp.offset);
-
- return retval;
+ return NULL;
}
-static int sis_ioctl_agp_free(DRM_IOCTL_ARGS)
+int sis_idle(drm_device_t *dev)
{
- DRM_DEVICE;
drm_sis_private_t *dev_priv = dev->dev_private;
- drm_sis_mem_t agp;
-
- if (dev_priv == NULL || dev_priv->AGPHeap == NULL)
- return DRM_ERR(EINVAL);
+ uint32_t idle_reg;
+ unsigned long end;
+ int i;
- DRM_COPY_FROM_USER_IOCTL(agp, (drm_sis_mem_t __user *) data,
- sizeof(agp));
+ if (dev_priv->idle_fault)
+ return 0;
- if (!mmBlockInHeap(dev_priv->AGPHeap, (PMemBlock) agp.free))
- return DRM_ERR(EINVAL);
+ if (dev_priv->mmio == NULL) {
+ dev_priv->mmio = sis_reg_init(dev);
+ if (dev_priv->mmio == NULL) {
+ DRM_ERROR("Could not find register map.\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Implement a device switch here if needed
+ */
+
+ if (dev_priv->chipset != SIS_CHIP_315)
+ return 0;
+
+ /*
+ * Timeout after 3 seconds. We cannot use DRM_WAIT_ON here
+ * because its polling frequency is too low.
+ */
+
+ end = jiffies + (DRM_HZ * 3);
+
+ for (i=0; i<4; ++i) {
+ do {
+ idle_reg = SIS_READ(0x85cc);
+ } while ( !time_after_eq(jiffies, end) &&
+ ((idle_reg & 0x80000000) != 0x80000000));
+ }
- mmFreeMem((PMemBlock) agp.free);
- if (!del_alloc_set(agp.context, AGP_TYPE, agp.free))
- return DRM_ERR(EINVAL);
+ if (time_after_eq(jiffies, end)) {
+ DRM_ERROR("Graphics engine idle timeout. "
+ "Disabling idle check\n");
+ dev_priv->idle_fault = 1;
+ }
- DRM_DEBUG("free agp, free = 0x%lx\n", agp.free);
+ /*
+ * The caller never sees an error code. It gets trapped
+ * in libdrm.
+ */
return 0;
}
-int sis_init_context(struct drm_device *dev, int context)
-{
- int i;
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (global_ppriv[i].used &&
- (global_ppriv[i].context == context))
- break;
- }
+void sis_lastclose(struct drm_device *dev)
+{
+ drm_sis_private_t *dev_priv = dev->dev_private;
- if (i >= MAX_CONTEXT) {
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (!global_ppriv[i].used) {
- global_ppriv[i].context = context;
- global_ppriv[i].used = 1;
- global_ppriv[i].sets[0] = setInit();
- global_ppriv[i].sets[1] = setInit();
- DRM_DEBUG("init allocation set, socket=%d, "
- "context = %d\n", i, context);
- break;
- }
- }
- if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) ||
- (global_ppriv[i].sets[1] == NULL)) {
- return 0;
- }
- }
+ if (!dev_priv)
+ return;
- return 1;
+ mutex_lock(&dev->struct_mutex);
+ drm_sman_cleanup(&dev_priv->sman);
+ dev_priv->vram_initialized = 0;
+ dev_priv->agp_initialized = 0;
+ dev_priv->mmio = NULL;
+ mutex_unlock(&dev->struct_mutex);
}
-int sis_final_context(struct drm_device *dev, int context)
+void sis_reclaim_buffers_locked(drm_device_t * dev, struct file *filp)
{
- int i;
+ drm_sis_private_t *dev_priv = dev->dev_private;
+ drm_file_t *priv = filp->private_data;
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (global_ppriv[i].used &&
- (global_ppriv[i].context == context))
- break;
+ mutex_lock(&dev->struct_mutex);
+ if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) {
+ mutex_unlock(&dev->struct_mutex);
+ return;
}
- if (i < MAX_CONTEXT) {
- set_t *set;
- ITEM_TYPE item;
- int retval;
-
- DRM_DEBUG("find socket %d, context = %d\n", i, context);
-
- /* Video Memory */
- set = global_ppriv[i].sets[0];
- retval = setFirst(set, &item);
- while (retval) {
- DRM_DEBUG("free video memory 0x%lx\n", item);
-#if defined(__linux__) && defined(CONFIG_FB_SIS)
- sis_free(item);
-#else
- mmFreeMem((PMemBlock) item);
-#endif
- retval = setNext(set, &item);
- }
- setDestroy(set);
-
- /* AGP Memory */
- set = global_ppriv[i].sets[1];
- retval = setFirst(set, &item);
- while (retval) {
- DRM_DEBUG("free agp memory 0x%lx\n", item);
- mmFreeMem((PMemBlock) item);
- retval = setNext(set, &item);
- }
- setDestroy(set);
-
- global_ppriv[i].used = 0;
+ if (dev->driver->dma_quiescent) {
+ dev->driver->dma_quiescent(dev);
}
- return 1;
+ drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv);
+ mutex_unlock(&dev->struct_mutex);
+ return;
}
drm_ioctl_desc_t sis_ioctls[] = {
[DRM_IOCTL_NR(DRM_SIS_FB_ALLOC)] = {sis_fb_alloc, DRM_AUTH},
- [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_fb_free, DRM_AUTH},
- [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] = {sis_ioctl_agp_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY},
+ [DRM_IOCTL_NR(DRM_SIS_FB_FREE)] = {sis_drm_free, DRM_AUTH},
+ [DRM_IOCTL_NR(DRM_SIS_AGP_INIT)] =
+ {sis_ioctl_agp_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY},
[DRM_IOCTL_NR(DRM_SIS_AGP_ALLOC)] = {sis_ioctl_agp_alloc, DRM_AUTH},
- [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_ioctl_agp_free, DRM_AUTH},
- [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] = {sis_fb_init, DRM_AUTH|DRM_MASTER|DRM_ROOT_ONLY}
+ [DRM_IOCTL_NR(DRM_SIS_AGP_FREE)] = {sis_drm_free, DRM_AUTH},
+ [DRM_IOCTL_NR(DRM_SIS_FB_INIT)] =
+ {sis_fb_init, DRM_AUTH | DRM_MASTER | DRM_ROOT_ONLY}
};
int sis_max_ioctl = DRM_ARRAY_SIZE(sis_ioctls);
diff --git a/drivers/char/drm/via_dmablit.c b/drivers/char/drm/via_dmablit.c
index 78a81a4a99c..60c1695db30 100644
--- a/drivers/char/drm/via_dmablit.c
+++ b/drivers/char/drm/via_dmablit.c
@@ -41,9 +41,9 @@
#include <linux/pagemap.h>
-#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK)
-#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK)
-#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT)
+#define VIA_PGDN(x) (((unsigned long)(x)) & PAGE_MASK)
+#define VIA_PGOFF(x) (((unsigned long)(x)) & ~PAGE_MASK)
+#define VIA_PFN(x) ((unsigned long)(x) >> PAGE_SHIFT)
typedef struct _drm_via_descriptor {
uint32_t mem_addr;
@@ -121,19 +121,19 @@ via_map_blit_for_device(struct pci_dev *pdev,
while (line_len > 0) {
- remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len);
+ remaining_len = min(PAGE_SIZE-VIA_PGOFF(cur_mem), line_len);
line_len -= remaining_len;
if (mode == 1) {
- desc_ptr->mem_addr =
+ desc_ptr->mem_addr =
dma_map_page(&pdev->dev,
vsg->pages[VIA_PFN(cur_mem) -
VIA_PFN(first_addr)],
VIA_PGOFF(cur_mem), remaining_len,
vsg->direction);
- desc_ptr->dev_addr = cur_fb;
+ desc_ptr->dev_addr = cur_fb;
- desc_ptr->size = remaining_len;
+ desc_ptr->size = remaining_len;
desc_ptr->next = (uint32_t) next;
next = dma_map_single(&pdev->dev, desc_ptr, sizeof(*desc_ptr),
DMA_TO_DEVICE);
@@ -162,7 +162,7 @@ via_map_blit_for_device(struct pci_dev *pdev,
/*
* Function that frees up all resources for a blit. It is usable even if the
- * blit info has only be partially built as long as the status enum is consistent
+ * blit info has only been partially built as long as the status enum is consistent
* with the actual status of the used resources.
*/
@@ -238,8 +238,11 @@ via_lock_all_dma_pages(drm_via_sg_info_t *vsg, drm_via_dmablit_t *xfer)
return DRM_ERR(ENOMEM);
memset(vsg->pages, 0, sizeof(struct page *) * vsg->num_pages);
down_read(&current->mm->mmap_sem);
- ret = get_user_pages(current, current->mm, (unsigned long) xfer->mem_addr,
- vsg->num_pages, vsg->direction, 0, vsg->pages, NULL);
+ ret = get_user_pages(current, current->mm,
+ (unsigned long)xfer->mem_addr,
+ vsg->num_pages,
+ (vsg->direction == DMA_FROM_DEVICE),
+ 0, vsg->pages, NULL);
up_read(&current->mm->mmap_sem);
if (ret != vsg->num_pages) {
@@ -475,9 +478,15 @@ via_dmablit_timer(unsigned long data)
if (!timer_pending(&blitq->poll_timer)) {
blitq->poll_timer.expires = jiffies+1;
add_timer(&blitq->poll_timer);
- }
- via_dmablit_handler(dev, engine, 0);
+ /*
+ * Rerun handler to delete timer if engines are off, and
+ * to shorten abort latency. This is a little nasty.
+ */
+
+ via_dmablit_handler(dev, engine, 0);
+
+ }
}
@@ -597,15 +606,27 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t *
* (Not a big limitation anyway.)
*/
- if (((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) ||
- (xfer->mem_stride > 2048*4)) {
+ if ((xfer->mem_stride - xfer->line_length) >= PAGE_SIZE) {
DRM_ERROR("Too large system memory stride. Stride: %d, "
"Length: %d\n", xfer->mem_stride, xfer->line_length);
return DRM_ERR(EINVAL);
}
- if (xfer->num_lines > 2048) {
- DRM_ERROR("Too many PCI DMA bitblt lines.\n");
+ if ((xfer->mem_stride == xfer->line_length) &&
+ (xfer->fb_stride == xfer->line_length)) {
+ xfer->mem_stride *= xfer->num_lines;
+ xfer->line_length = xfer->mem_stride;
+ xfer->fb_stride = xfer->mem_stride;
+ xfer->num_lines = 1;
+ }
+
+ /*
+ * Don't lock an arbitrary large number of pages, since that causes a
+ * DOS security hole.
+ */
+
+ if (xfer->num_lines > 2048 || (xfer->num_lines*xfer->mem_stride > (2048*2048*4))) {
+ DRM_ERROR("Too large PCI DMA bitblt.\n");
return DRM_ERR(EINVAL);
}
@@ -628,16 +649,17 @@ via_build_sg_info(drm_device_t *dev, drm_via_sg_info_t *vsg, drm_via_dmablit_t *
#ifdef VIA_BUGFREE
if ((((unsigned long)xfer->mem_addr & 3) != ((unsigned long)xfer->fb_addr & 3)) ||
- ((xfer->mem_stride & 3) != (xfer->fb_stride & 3))) {
+ ((xfer->num_lines > 1) && ((xfer->mem_stride & 3) != (xfer->fb_stride & 3)))) {
DRM_ERROR("Invalid DRM bitblt alignment.\n");
- return DRM_ERR(EINVAL);
+ return DRM_ERR(EINVAL);
}
#else
if ((((unsigned long)xfer->mem_addr & 15) ||
- ((unsigned long)xfer->fb_addr & 3)) || (xfer->mem_stride & 15) ||
- (xfer->fb_stride & 3)) {
+ ((unsigned long)xfer->fb_addr & 3)) ||
+ ((xfer->num_lines > 1) &&
+ ((xfer->mem_stride & 15) || (xfer->fb_stride & 3)))) {
DRM_ERROR("Invalid DRM bitblt alignment.\n");
- return DRM_ERR(EINVAL);
+ return DRM_ERR(EINVAL);
}
#endif
@@ -715,7 +737,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer)
drm_via_private_t *dev_priv = (drm_via_private_t *)dev->dev_private;
drm_via_sg_info_t *vsg;
drm_via_blitq_t *blitq;
- int ret;
+ int ret;
int engine;
unsigned long irqsave;
@@ -756,7 +778,7 @@ via_dmablit(drm_device_t *dev, drm_via_dmablit_t *xfer)
/*
* Sync on a previously submitted blit. Note that the X server use signals extensively, and
- * that there is a very big proability that this IOCTL will be interrupted by a signal. In that
+ * that there is a very big probability that this IOCTL will be interrupted by a signal. In that
* case it returns with -EAGAIN for the signal to be delivered.
* The caller should then reissue the IOCTL. This is similar to what is being done for drmGetLock().
*/
diff --git a/drivers/char/drm/via_drm.h b/drivers/char/drm/via_drm.h
index 47f0b5b2637..e4ee97d7156 100644
--- a/drivers/char/drm/via_drm.h
+++ b/drivers/char/drm/via_drm.h
@@ -250,6 +250,12 @@ typedef struct drm_via_blitsync {
unsigned engine;
} drm_via_blitsync_t;
+/* - * Below,"flags" is currently unused but will be used for possible future
+ * extensions like kernel space bounce buffers for bad alignments and
+ * blit engine busy-wait polling for better latency in the absence of
+ * interrupts.
+ */
+
typedef struct drm_via_dmablit {
uint32_t num_lines;
uint32_t line_length;
@@ -260,7 +266,7 @@ typedef struct drm_via_dmablit {
unsigned char *mem_addr;
uint32_t mem_stride;
- int bounce_buffer;
+ uint32_t flags;
int to_fb;
drm_via_blitsync_t sync;
diff --git a/drivers/char/drm/via_drv.c b/drivers/char/drm/via_drv.c
index b3d364d793d..bb9dde8b191 100644
--- a/drivers/char/drm/via_drv.c
+++ b/drivers/char/drm/via_drv.c
@@ -43,7 +43,6 @@ static struct drm_driver driver = {
DRIVER_IRQ_SHARED | DRIVER_IRQ_VBL,
.load = via_driver_load,
.unload = via_driver_unload,
- .context_ctor = via_init_context,
.context_dtor = via_final_context,
.vblank_wait = via_driver_vblank_wait,
.irq_preinstall = via_driver_irq_preinstall,
@@ -53,6 +52,8 @@ static struct drm_driver driver = {
.dma_quiescent = via_driver_dma_quiescent,
.dri_library_name = dri_library_name,
.reclaim_buffers = drm_core_reclaim_buffers,
+ .reclaim_buffers_locked = via_reclaim_buffers_locked,
+ .lastclose = via_lastclose,
.get_map_ofs = drm_core_get_map_ofs,
.get_reg_ofs = drm_core_get_reg_ofs,
.ioctls = via_ioctls,
diff --git a/drivers/char/drm/via_drv.h b/drivers/char/drm/via_drv.h
index 52bcc7b1ba4..d21b5b75da0 100644
--- a/drivers/char/drm/via_drv.h
+++ b/drivers/char/drm/via_drv.h
@@ -24,15 +24,16 @@
#ifndef _VIA_DRV_H_
#define _VIA_DRV_H_
+#include "drm_sman.h"
#define DRIVER_AUTHOR "Various"
#define DRIVER_NAME "via"
#define DRIVER_DESC "VIA Unichrome / Pro"
-#define DRIVER_DATE "20051116"
+#define DRIVER_DATE "20060529"
#define DRIVER_MAJOR 2
-#define DRIVER_MINOR 7
-#define DRIVER_PATCHLEVEL 4
+#define DRIVER_MINOR 10
+#define DRIVER_PATCHLEVEL 0
#include "via_verifier.h"
@@ -85,6 +86,12 @@ typedef struct drm_via_private {
uint32_t irq_enable_mask;
uint32_t irq_pending_mask;
int *irq_map;
+ unsigned int idle_fault;
+ drm_sman_t sman;
+ int vram_initialized;
+ int agp_initialized;
+ unsigned long vram_offset;
+ unsigned long agp_offset;
drm_via_blitq_t blit_queues[VIA_NUM_BLIT_ENGINES];
} drm_via_private_t;
@@ -135,6 +142,9 @@ extern void via_init_futex(drm_via_private_t * dev_priv);
extern void via_cleanup_futex(drm_via_private_t * dev_priv);
extern void via_release_futex(drm_via_private_t * dev_priv, int context);
+extern void via_reclaim_buffers_locked(drm_device_t *dev, struct file *filp);
+extern void via_lastclose(drm_device_t *dev);
+
extern void via_dmablit_handler(drm_device_t *dev, int engine, int from_irq);
extern void via_init_dmablit(drm_device_t *dev);
diff --git a/drivers/char/drm/via_ds.c b/drivers/char/drm/via_ds.c
deleted file mode 100644
index 9429736b3b9..00000000000
--- a/drivers/char/drm/via_ds.c
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
- * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sub license,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#include "drmP.h"
-
-#include "via_ds.h"
-extern unsigned int VIA_DEBUG;
-
-set_t *via_setInit(void)
-{
- int i;
- set_t *set;
- set = (set_t *) drm_alloc(sizeof(set_t), DRM_MEM_DRIVER);
- for (i = 0; i < SET_SIZE; i++) {
- set->list[i].free_next = i + 1;
- set->list[i].alloc_next = -1;
- }
- set->list[SET_SIZE - 1].free_next = -1;
- set->free = 0;
- set->alloc = -1;
- set->trace = -1;
- return set;
-}
-
-int via_setAdd(set_t * set, ITEM_TYPE item)
-{
- int free = set->free;
- if (free != -1) {
- set->list[free].val = item;
- set->free = set->list[free].free_next;
- } else {
- return 0;
- }
- set->list[free].alloc_next = set->alloc;
- set->alloc = free;
- set->list[free].free_next = -1;
- return 1;
-}
-
-int via_setDel(set_t * set, ITEM_TYPE item)
-{
- int alloc = set->alloc;
- int prev = -1;
-
- while (alloc != -1) {
- if (set->list[alloc].val == item) {
- if (prev != -1)
- set->list[prev].alloc_next =
- set->list[alloc].alloc_next;
- else
- set->alloc = set->list[alloc].alloc_next;
- break;
- }
- prev = alloc;
- alloc = set->list[alloc].alloc_next;
- }
-
- if (alloc == -1)
- return 0;
-
- set->list[alloc].free_next = set->free;
- set->free = alloc;
- set->list[alloc].alloc_next = -1;
-
- return 1;
-}
-
-/* setFirst -> setAdd -> setNext is wrong */
-
-int via_setFirst(set_t * set, ITEM_TYPE * item)
-{
- if (set->alloc == -1)
- return 0;
-
- *item = set->list[set->alloc].val;
- set->trace = set->list[set->alloc].alloc_next;
-
- return 1;
-}
-
-int via_setNext(set_t * set, ITEM_TYPE * item)
-{
- if (set->trace == -1)
- return 0;
-
- *item = set->list[set->trace].val;
- set->trace = set->list[set->trace].alloc_next;
-
- return 1;
-}
-
-int via_setDestroy(set_t * set)
-{
- drm_free(set, sizeof(set_t), DRM_MEM_DRIVER);
-
- return 1;
-}
-
-#define ISFREE(bptr) ((bptr)->free)
-
-#define fprintf(fmt, arg...) do{}while(0)
-
-memHeap_t *via_mmInit(int ofs, int size)
-{
- PMemBlock blocks;
-
- if (size <= 0)
- return NULL;
-
- blocks = (TMemBlock *) drm_calloc(1, sizeof(TMemBlock), DRM_MEM_DRIVER);
-
- if (blocks) {
- blocks->ofs = ofs;
- blocks->size = size;
- blocks->free = 1;
- return (memHeap_t *) blocks;
- } else
- return NULL;
-}
-
-static TMemBlock *SliceBlock(TMemBlock * p,
- int startofs, int size,
- int reserved, int alignment)
-{
- TMemBlock *newblock;
-
- /* break left */
- if (startofs > p->ofs) {
- newblock =
- (TMemBlock *) drm_calloc(1, sizeof(TMemBlock),
- DRM_MEM_DRIVER);
- newblock->ofs = startofs;
- newblock->size = p->size - (startofs - p->ofs);
- newblock->free = 1;
- newblock->next = p->next;
- p->size -= newblock->size;
- p->next = newblock;
- p = newblock;
- }
-
- /* break right */
- if (size < p->size) {
- newblock =
- (TMemBlock *) drm_calloc(1, sizeof(TMemBlock),
- DRM_MEM_DRIVER);
- newblock->ofs = startofs + size;
- newblock->size = p->size - size;
- newblock->free = 1;
- newblock->next = p->next;
- p->size = size;
- p->next = newblock;
- }
-
- /* p = middle block */
- p->align = alignment;
- p->free = 0;
- p->reserved = reserved;
- return p;
-}
-
-PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2,
- int startSearch)
-{
- int mask, startofs, endofs;
- TMemBlock *p;
-
- if (!heap || align2 < 0 || size <= 0)
- return NULL;
-
- mask = (1 << align2) - 1;
- startofs = 0;
- p = (TMemBlock *) heap;
-
- while (p) {
- if (ISFREE(p)) {
- startofs = (p->ofs + mask) & ~mask;
-
- if (startofs < startSearch)
- startofs = startSearch;
-
- endofs = startofs + size;
-
- if (endofs <= (p->ofs + p->size))
- break;
- }
-
- p = p->next;
- }
-
- if (!p)
- return NULL;
-
- p = SliceBlock(p, startofs, size, 0, mask + 1);
- p->heap = heap;
-
- return p;
-}
-
-static __inline__ int Join2Blocks(TMemBlock * p)
-{
- if (p->free && p->next && p->next->free) {
- TMemBlock *q = p->next;
- p->size += q->size;
- p->next = q->next;
- drm_free(q, sizeof(TMemBlock), DRM_MEM_DRIVER);
-
- return 1;
- }
-
- return 0;
-}
-
-int via_mmFreeMem(PMemBlock b)
-{
- TMemBlock *p, *prev;
-
- if (!b)
- return 0;
-
- if (!b->heap) {
- fprintf(stderr, "no heap\n");
-
- return -1;
- }
-
- p = b->heap;
- prev = NULL;
-
- while (p && p != b) {
- prev = p;
- p = p->next;
- }
-
- if (!p || p->free || p->reserved) {
- if (!p)
- fprintf(stderr, "block not found in heap\n");
- else if (p->free)
- fprintf(stderr, "block already free\n");
- else
- fprintf(stderr, "block is reserved\n");
-
- return -1;
- }
-
- p->free = 1;
- Join2Blocks(p);
-
- if (prev)
- Join2Blocks(prev);
-
- return 0;
-}
diff --git a/drivers/char/drm/via_ds.h b/drivers/char/drm/via_ds.h
deleted file mode 100644
index d2bb9f37ca3..00000000000
--- a/drivers/char/drm/via_ds.h
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
- * Copyright 2000 Silicon Integrated Systems Corp, Inc., HsinChu, Taiwan.
- * All rights reserved.
- *
- * Permission is hereby granted, free of charge, to any person obtaining a
- * copy of this software and associated documentation files (the "Software"),
- * to deal in the Software without restriction, including without limitation
- * the rights to use, copy, modify, merge, publish, distribute, sub license,
- * and/or sell copies of the Software, and to permit persons to whom the
- * Software is furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice (including the
- * next paragraph) shall be included in all copies or substantial portions
- * of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
- * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
- * DEALINGS IN THE SOFTWARE.
- */
-#ifndef _via_ds_h_
-#define _via_ds_h_
-
-#include "drmP.h"
-
-/* Set Data Structure */
-#define SET_SIZE 5000
-typedef unsigned long ITEM_TYPE;
-
-typedef struct {
- ITEM_TYPE val;
- int alloc_next, free_next;
-} list_item_t;
-
-typedef struct {
- int alloc;
- int free;
- int trace;
- list_item_t list[SET_SIZE];
-} set_t;
-
-set_t *via_setInit(void);
-int via_setAdd(set_t * set, ITEM_TYPE item);
-int via_setDel(set_t * set, ITEM_TYPE item);
-int via_setFirst(set_t * set, ITEM_TYPE * item);
-int via_setNext(set_t * set, ITEM_TYPE * item);
-int via_setDestroy(set_t * set);
-
-#endif
-
-#ifndef MM_INC
-#define MM_INC
-
-struct mem_block_t {
- struct mem_block_t *next;
- struct mem_block_t *heap;
- int ofs, size;
- int align;
- unsigned int free:1;
- unsigned int reserved:1;
-};
-typedef struct mem_block_t TMemBlock;
-typedef struct mem_block_t *PMemBlock;
-
-/* a heap is just the first block in a chain */
-typedef struct mem_block_t memHeap_t;
-
-static __inline__ int mmBlockSize(PMemBlock b)
-{
- return b->size;
-}
-
-static __inline__ int mmOffset(PMemBlock b)
-{
- return b->ofs;
-}
-
-static __inline__ void mmMarkReserved(PMemBlock b)
-{
- b->reserved = 1;
-}
-
-/*
- * input: total size in bytes
- * return: a heap pointer if OK, NULL if error
- */
-memHeap_t *via_mmInit(int ofs, int size);
-
-PMemBlock via_mmAllocMem(memHeap_t * heap, int size, int align2,
- int startSearch);
-
-/*
- * Free block starts at offset
- * input: pointer to a block
- * return: 0 if OK, -1 if error
- */
-int via_mmFreeMem(PMemBlock b);
-
-#endif
diff --git a/drivers/char/drm/via_map.c b/drivers/char/drm/via_map.c
index c6a08e96285..782011e0a58 100644
--- a/drivers/char/drm/via_map.c
+++ b/drivers/char/drm/via_map.c
@@ -98,6 +98,7 @@ int via_map_init(DRM_IOCTL_ARGS)
int via_driver_load(drm_device_t *dev, unsigned long chipset)
{
drm_via_private_t *dev_priv;
+ int ret = 0;
dev_priv = drm_calloc(1, sizeof(drm_via_private_t), DRM_MEM_DRIVER);
if (dev_priv == NULL)
@@ -108,13 +109,19 @@ int via_driver_load(drm_device_t *dev, unsigned long chipset)
if (chipset == VIA_PRO_GROUP_A)
dev_priv->pro_group_a = 1;
- return 0;
+ ret = drm_sman_init(&dev_priv->sman, 2, 12, 8);
+ if (ret) {
+ drm_free(dev_priv, sizeof(*dev_priv), DRM_MEM_DRIVER);
+ }
+ return ret;
}
int via_driver_unload(drm_device_t *dev)
{
drm_via_private_t *dev_priv = dev->dev_private;
+ drm_sman_takedown(&dev_priv->sman);
+
drm_free(dev_priv, sizeof(drm_via_private_t), DRM_MEM_DRIVER);
return 0;
diff --git a/drivers/char/drm/via_mm.c b/drivers/char/drm/via_mm.c
index 33e0cb12e4c..2fcf0577a7a 100644
--- a/drivers/char/drm/via_mm.c
+++ b/drivers/char/drm/via_mm.c
@@ -1,6 +1,6 @@
/*
- * Copyright 1998-2003 VIA Technologies, Inc. All Rights Reserved.
- * Copyright 2001-2003 S3 Graphics, Inc. All Rights Reserved.
+ * Copyright 2006 Tungsten Graphics Inc., Bismarck, ND., USA.
+ * All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
@@ -16,347 +16,194 @@
* 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 NON-INFRINGEMENT. IN NO EVENT SHALL
- * VIA, S3 GRAPHICS, AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
- * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * THE AUTHORS OR COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*/
+/*
+ * Authors: Thomas Hellström <thomas-at-tungstengraphics-dot-com>
+ */
+
#include "drmP.h"
#include "via_drm.h"
#include "via_drv.h"
-#include "via_ds.h"
-#include "via_mm.h"
-
-#define MAX_CONTEXT 100
-
-typedef struct {
- int used;
- int context;
- set_t *sets[2]; /* 0 for frame buffer, 1 for AGP , 2 for System */
-} via_context_t;
-
-static via_context_t global_ppriv[MAX_CONTEXT];
+#include "drm_sman.h"
-static int via_agp_alloc(drm_via_mem_t * mem);
-static int via_agp_free(drm_via_mem_t * mem);
-static int via_fb_alloc(drm_via_mem_t * mem);
-static int via_fb_free(drm_via_mem_t * mem);
-
-static int add_alloc_set(int context, int type, unsigned long val)
-{
- int i, retval = 0;
-
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (global_ppriv[i].used && global_ppriv[i].context == context) {
- retval = via_setAdd(global_ppriv[i].sets[type], val);
- break;
- }
- }
-
- return retval;
-}
-
-static int del_alloc_set(int context, int type, unsigned long val)
-{
- int i, retval = 0;
-
- for (i = 0; i < MAX_CONTEXT; i++)
- if (global_ppriv[i].used && global_ppriv[i].context == context) {
- retval = via_setDel(global_ppriv[i].sets[type], val);
- break;
- }
-
- return retval;
-}
-
-/* agp memory management */
-static memHeap_t *AgpHeap = NULL;
+#define VIA_MM_ALIGN_SHIFT 4
+#define VIA_MM_ALIGN_MASK ( (1 << VIA_MM_ALIGN_SHIFT) - 1)
int via_agp_init(DRM_IOCTL_ARGS)
{
+ DRM_DEVICE;
drm_via_agp_t agp;
+ drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+ int ret;
DRM_COPY_FROM_USER_IOCTL(agp, (drm_via_agp_t __user *) data,
sizeof(agp));
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_AGP, 0,
+ agp.size >> VIA_MM_ALIGN_SHIFT);
+
+ if (ret) {
+ DRM_ERROR("AGP memory manager initialisation error\n");
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
- AgpHeap = via_mmInit(agp.offset, agp.size);
-
- DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)agp.offset,
- (unsigned long)agp.size);
+ dev_priv->agp_initialized = 1;
+ dev_priv->agp_offset = agp.offset;
+ mutex_unlock(&dev->struct_mutex);
+ DRM_DEBUG("offset = %u, size = %u", agp.offset, agp.size);
return 0;
}
-/* fb memory management */
-static memHeap_t *FBHeap = NULL;
-
int via_fb_init(DRM_IOCTL_ARGS)
{
+ DRM_DEVICE;
drm_via_fb_t fb;
+ drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+ int ret;
DRM_COPY_FROM_USER_IOCTL(fb, (drm_via_fb_t __user *) data, sizeof(fb));
- FBHeap = via_mmInit(fb.offset, fb.size);
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_sman_set_range(&dev_priv->sman, VIA_MEM_VIDEO, 0,
+ fb.size >> VIA_MM_ALIGN_SHIFT);
- DRM_DEBUG("offset = %lu, size = %lu", (unsigned long)fb.offset,
- (unsigned long)fb.size);
+ if (ret) {
+ DRM_ERROR("VRAM memory manager initialisation error\n");
+ mutex_unlock(&dev->struct_mutex);
+ return ret;
+ }
- return 0;
-}
+ dev_priv->vram_initialized = 1;
+ dev_priv->vram_offset = fb.offset;
-int via_init_context(struct drm_device *dev, int context)
-{
- int i;
-
- for (i = 0; i < MAX_CONTEXT; i++)
- if (global_ppriv[i].used &&
- (global_ppriv[i].context == context))
- break;
-
- if (i >= MAX_CONTEXT) {
- for (i = 0; i < MAX_CONTEXT; i++) {
- if (!global_ppriv[i].used) {
- global_ppriv[i].context = context;
- global_ppriv[i].used = 1;
- global_ppriv[i].sets[0] = via_setInit();
- global_ppriv[i].sets[1] = via_setInit();
- DRM_DEBUG("init allocation set, socket=%d,"
- " context = %d\n", i, context);
- break;
- }
- }
-
- if ((i >= MAX_CONTEXT) || (global_ppriv[i].sets[0] == NULL) ||
- (global_ppriv[i].sets[1] == NULL)) {
- return 0;
- }
- }
+ mutex_unlock(&dev->struct_mutex);
+ DRM_DEBUG("offset = %u, size = %u", fb.offset, fb.size);
+
+ return 0;
- return 1;
}
int via_final_context(struct drm_device *dev, int context)
{
- int i;
drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
- for (i = 0; i < MAX_CONTEXT; i++)
- if (global_ppriv[i].used &&
- (global_ppriv[i].context == context))
- break;
-
- if (i < MAX_CONTEXT) {
- set_t *set;
- ITEM_TYPE item;
- int retval;
-
- DRM_DEBUG("find socket %d, context = %d\n", i, context);
-
- /* Video Memory */
- set = global_ppriv[i].sets[0];
- retval = via_setFirst(set, &item);
- while (retval) {
- DRM_DEBUG("free video memory 0x%lx\n", item);
- via_mmFreeMem((PMemBlock) item);
- retval = via_setNext(set, &item);
- }
- via_setDestroy(set);
-
- /* AGP Memory */
- set = global_ppriv[i].sets[1];
- retval = via_setFirst(set, &item);
- while (retval) {
- DRM_DEBUG("free agp memory 0x%lx\n", item);
- via_mmFreeMem((PMemBlock) item);
- retval = via_setNext(set, &item);
- }
- via_setDestroy(set);
- global_ppriv[i].used = 0;
- }
via_release_futex(dev_priv, context);
-#if defined(__linux__)
/* Linux specific until context tracking code gets ported to BSD */
/* Last context, perform cleanup */
if (dev->ctx_count == 1 && dev->dev_private) {
DRM_DEBUG("Last Context\n");
if (dev->irq)
drm_irq_uninstall(dev);
-
via_cleanup_futex(dev_priv);
via_do_cleanup_map(dev);
}
-#endif
-
return 1;
}
+void via_lastclose(struct drm_device *dev)
+{
+ drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+
+ if (!dev_priv)
+ return;
+
+ mutex_lock(&dev->struct_mutex);
+ drm_sman_cleanup(&dev_priv->sman);
+ dev_priv->vram_initialized = 0;
+ dev_priv->agp_initialized = 0;
+ mutex_unlock(&dev->struct_mutex);
+}
+
int via_mem_alloc(DRM_IOCTL_ARGS)
{
+ DRM_DEVICE;
+
drm_via_mem_t mem;
+ int retval = 0;
+ drm_memblock_item_t *item;
+ drm_via_private_t *dev_priv = (drm_via_private_t *) dev->dev_private;
+ unsigned long tmpSize;
DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data,
sizeof(mem));
- switch (mem.type) {
- case VIA_MEM_VIDEO:
- if (via_fb_alloc(&mem) < 0)
- return -EFAULT;
- DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem,
- sizeof(mem));
- return 0;
- case VIA_MEM_AGP:
- if (via_agp_alloc(&mem) < 0)
- return -EFAULT;
- DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem,
- sizeof(mem));
- return 0;
+ if (mem.type > VIA_MEM_AGP) {
+ DRM_ERROR("Unknown memory type allocation\n");
+ return DRM_ERR(EINVAL);
}
-
- return -EFAULT;
-}
-
-static int via_fb_alloc(drm_via_mem_t * mem)
-{
- drm_via_mm_t fb;
- PMemBlock block;
- int retval = 0;
-
- if (!FBHeap)
- return -1;
-
- fb.size = mem->size;
- fb.context = mem->context;
-
- block = via_mmAllocMem(FBHeap, fb.size, 5, 0);
- if (block) {
- fb.offset = block->ofs;
- fb.free = (unsigned long)block;
- if (!add_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) {
- DRM_DEBUG("adding to allocation set fails\n");
- via_mmFreeMem((PMemBlock) fb.free);
- retval = -1;
- }
- } else {
- fb.offset = 0;
- fb.size = 0;
- fb.free = 0;
- retval = -1;
+ mutex_lock(&dev->struct_mutex);
+ if (0 == ((mem.type == VIA_MEM_VIDEO) ? dev_priv->vram_initialized :
+ dev_priv->agp_initialized)) {
+ DRM_ERROR
+ ("Attempt to allocate from uninitialized memory manager.\n");
+ mutex_unlock(&dev->struct_mutex);
+ return DRM_ERR(EINVAL);
}
- mem->offset = fb.offset;
- mem->index = fb.free;
-
- DRM_DEBUG("alloc fb, size = %d, offset = %d\n", fb.size,
- (int)fb.offset);
-
- return retval;
-}
-
-static int via_agp_alloc(drm_via_mem_t * mem)
-{
- drm_via_mm_t agp;
- PMemBlock block;
- int retval = 0;
-
- if (!AgpHeap)
- return -1;
-
- agp.size = mem->size;
- agp.context = mem->context;
-
- block = via_mmAllocMem(AgpHeap, agp.size, 5, 0);
- if (block) {
- agp.offset = block->ofs;
- agp.free = (unsigned long)block;
- if (!add_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) {
- DRM_DEBUG("adding to allocation set fails\n");
- via_mmFreeMem((PMemBlock) agp.free);
- retval = -1;
- }
+ tmpSize = (mem.size + VIA_MM_ALIGN_MASK) >> VIA_MM_ALIGN_SHIFT;
+ item = drm_sman_alloc(&dev_priv->sman, mem.type, tmpSize, 0,
+ (unsigned long)priv);
+ mutex_unlock(&dev->struct_mutex);
+ if (item) {
+ mem.offset = ((mem.type == VIA_MEM_VIDEO) ?
+ dev_priv->vram_offset : dev_priv->agp_offset) +
+ (item->mm->
+ offset(item->mm, item->mm_info) << VIA_MM_ALIGN_SHIFT);
+ mem.index = item->user_hash.key;
} else {
- agp.offset = 0;
- agp.size = 0;
- agp.free = 0;
+ mem.offset = 0;
+ mem.size = 0;
+ mem.index = 0;
+ DRM_DEBUG("Video memory allocation failed\n");
+ retval = DRM_ERR(ENOMEM);
}
+ DRM_COPY_TO_USER_IOCTL((drm_via_mem_t __user *) data, mem, sizeof(mem));
- mem->offset = agp.offset;
- mem->index = agp.free;
-
- DRM_DEBUG("alloc agp, size = %d, offset = %d\n", agp.size,
- (unsigned int)agp.offset);
return retval;
}
int via_mem_free(DRM_IOCTL_ARGS)
{
+ DRM_DEVICE;
+ drm_via_private_t *dev_priv = dev->dev_private;
drm_via_mem_t mem;
+ int ret;
DRM_COPY_FROM_USER_IOCTL(mem, (drm_via_mem_t __user *) data,
sizeof(mem));
- switch (mem.type) {
+ mutex_lock(&dev->struct_mutex);
+ ret = drm_sman_free_key(&dev_priv->sman, mem.index);
+ mutex_unlock(&dev->struct_mutex);
+ DRM_DEBUG("free = 0x%lx\n", mem.index);
- case VIA_MEM_VIDEO:
- if (via_fb_free(&mem) == 0)
- return 0;
- break;
- case VIA_MEM_AGP:
- if (via_agp_free(&mem) == 0)
- return 0;
- break;
- }
-
- return -EFAULT;
+ return ret;
}
-static int via_fb_free(drm_via_mem_t * mem)
-{
- drm_via_mm_t fb;
- int retval = 0;
-
- if (!FBHeap) {
- return -1;
- }
-
- fb.free = mem->index;
- fb.context = mem->context;
-
- if (!fb.free) {
- return -1;
-
- }
-
- via_mmFreeMem((PMemBlock) fb.free);
-
- if (!del_alloc_set(fb.context, VIA_MEM_VIDEO, fb.free)) {
- retval = -1;
- }
-
- DRM_DEBUG("free fb, free = %ld\n", fb.free);
- return retval;
-}
-
-static int via_agp_free(drm_via_mem_t * mem)
+void via_reclaim_buffers_locked(drm_device_t * dev, struct file *filp)
{
- drm_via_mm_t agp;
-
- int retval = 0;
+ drm_via_private_t *dev_priv = dev->dev_private;
+ drm_file_t *priv = filp->private_data;
- agp.free = mem->index;
- agp.context = mem->context;
-
- if (!agp.free)
- return -1;
-
- via_mmFreeMem((PMemBlock) agp.free);
-
- if (!del_alloc_set(agp.context, VIA_MEM_AGP, agp.free)) {
- retval = -1;
+ mutex_lock(&dev->struct_mutex);
+ if (drm_sman_owner_clean(&dev_priv->sman, (unsigned long)priv)) {
+ mutex_unlock(&dev->struct_mutex);
+ return;
}
- DRM_DEBUG("free agp, free = %ld\n", agp.free);
+ if (dev->driver->dma_quiescent) {
+ dev->driver->dma_quiescent(dev);
+ }
- return retval;
+ drm_sman_owner_cleanup(&dev_priv->sman, (unsigned long)priv);
+ mutex_unlock(&dev->struct_mutex);
+ return;
}
diff --git a/drivers/char/ds1286.c b/drivers/char/ds1286.c
index 21c8229f544..6d58b037080 100644
--- a/drivers/char/ds1286.c
+++ b/drivers/char/ds1286.c
@@ -104,7 +104,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
case RTC_AIE_OFF: /* Mask alarm int. enab. bit */
{
- unsigned int flags;
+ unsigned long flags;
unsigned char val;
if (!capable(CAP_SYS_TIME))
@@ -120,7 +120,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file,
}
case RTC_AIE_ON: /* Allow alarm interrupts. */
{
- unsigned int flags;
+ unsigned long flags;
unsigned char val;
if (!capable(CAP_SYS_TIME))
@@ -136,7 +136,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file,
}
case RTC_WIE_OFF: /* Mask watchdog int. enab. bit */
{
- unsigned int flags;
+ unsigned long flags;
unsigned char val;
if (!capable(CAP_SYS_TIME))
@@ -152,7 +152,7 @@ static int ds1286_ioctl(struct inode *inode, struct file *file,
}
case RTC_WIE_ON: /* Allow watchdog interrupts. */
{
- unsigned int flags;
+ unsigned long flags;
unsigned char val;
if (!capable(CAP_SYS_TIME))
@@ -434,7 +434,7 @@ static inline unsigned char ds1286_is_updating(void)
static void ds1286_get_time(struct rtc_time *rtc_tm)
{
unsigned char save_control;
- unsigned int flags;
+ unsigned long flags;
unsigned long uip_watchdog = jiffies;
/*
@@ -494,7 +494,8 @@ static int ds1286_set_time(struct rtc_time *rtc_tm)
{
unsigned char mon, day, hrs, min, sec, leap_yr;
unsigned char save_control;
- unsigned int yrs, flags;
+ unsigned int yrs;
+ unsigned long flags;
yrs = rtc_tm->tm_year + 1900;
@@ -552,7 +553,7 @@ static int ds1286_set_time(struct rtc_time *rtc_tm)
static void ds1286_get_alm_time(struct rtc_time *alm_tm)
{
unsigned char cmd;
- unsigned int flags;
+ unsigned long flags;
/*
* Only the values that we read from the RTC are set. That
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index 86d290e9f30..3baa2ab8cbd 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -1125,7 +1125,7 @@ static void __exit epca_module_exit(void)
module_exit(epca_module_exit);
-static struct tty_operations pc_ops = {
+static const struct tty_operations pc_ops = {
.open = pc_open,
.close = pc_close,
.write = pc_write,
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
index afcd83d9984..05788c75d7f 100644
--- a/drivers/char/esp.c
+++ b/drivers/char/esp.c
@@ -2376,7 +2376,7 @@ static inline int autoconfig(struct esp_struct * info)
return (port_detected);
}
-static struct tty_operations esp_ops = {
+static const struct tty_operations esp_ops = {
.open = esp_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/char/generic_serial.c b/drivers/char/generic_serial.c
index 5e59c0b4273..4711d9b3a59 100644
--- a/drivers/char/generic_serial.c
+++ b/drivers/char/generic_serial.c
@@ -746,11 +746,9 @@ void gs_set_termios (struct tty_struct * tty,
gs_dprintk (GS_DEBUG_TERMIOS, "termios structure (%p):\n", tiosp);
}
-#if 0
/* This is an optimization that is only allowed for dumb cards */
/* Smart cards require knowledge of iflags and oflags too: that
might change hardware cooking mode.... */
-#endif
if (old_termios) {
if( (tiosp->c_iflag == old_termios->c_iflag)
&& (tiosp->c_oflag == old_termios->c_oflag)
@@ -774,14 +772,7 @@ void gs_set_termios (struct tty_struct * tty,
if(!memcmp(tiosp->c_cc, old_termios->c_cc, NCC)) printk("c_cc changed\n");
}
- baudrate = tiosp->c_cflag & CBAUD;
- if (baudrate & CBAUDEX) {
- baudrate &= ~CBAUDEX;
- if ((baudrate < 1) || (baudrate > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- baudrate += 15;
- }
+ baudrate = tty_get_baud_rate(tty);
baudrate = gs_baudrates[baudrate];
if ((tiosp->c_cflag & CBAUD) == B38400) {
diff --git a/drivers/char/hpet.c b/drivers/char/hpet.c
index 8afba339f05..58b0eb58111 100644
--- a/drivers/char/hpet.c
+++ b/drivers/char/hpet.c
@@ -868,8 +868,8 @@ int hpet_alloc(struct hpet_data *hdp)
do_div(temp, period);
hpetp->hp_tick_freq = temp; /* ticks per second */
- printk(KERN_INFO "hpet%d: at MMIO 0x%lx (virtual 0x%p), IRQ%s",
- hpetp->hp_which, hdp->hd_phys_address, hdp->hd_address,
+ printk(KERN_INFO "hpet%d: at MMIO 0x%lx, IRQ%s",
+ hpetp->hp_which, hdp->hd_phys_address,
hpetp->hp_ntimer > 1 ? "s" : "");
for (i = 0; i < hpetp->hp_ntimer; i++)
printk("%s %d", i > 0 ? "," : "", hdp->hd_irq[i]);
diff --git a/drivers/char/hvc_console.c b/drivers/char/hvc_console.c
index a76d2c40dd5..4053d1cd393 100644
--- a/drivers/char/hvc_console.c
+++ b/drivers/char/hvc_console.c
@@ -696,7 +696,7 @@ int khvcd(void *unused)
return 0;
}
-static struct tty_operations hvc_ops = {
+static const struct tty_operations hvc_ops = {
.open = hvc_open,
.close = hvc_close,
.write = hvc_write,
diff --git a/drivers/char/hvcs.c b/drivers/char/hvcs.c
index 4589ff302b0..0b89bcde8c5 100644
--- a/drivers/char/hvcs.c
+++ b/drivers/char/hvcs.c
@@ -1306,7 +1306,7 @@ static int hvcs_chars_in_buffer(struct tty_struct *tty)
return hvcsd->chars_in_buffer;
}
-static struct tty_operations hvcs_ops = {
+static const struct tty_operations hvcs_ops = {
.open = hvcs_open,
.close = hvcs_close,
.hangup = hvcs_hangup,
diff --git a/drivers/char/hvsi.c b/drivers/char/hvsi.c
index a89a95fb5e4..c07dc58d5c1 100644
--- a/drivers/char/hvsi.c
+++ b/drivers/char/hvsi.c
@@ -1130,7 +1130,7 @@ static int hvsi_tiocmset(struct tty_struct *tty, struct file *file,
}
-static struct tty_operations hvsi_ops = {
+static const struct tty_operations hvsi_ops = {
.open = hvsi_open,
.close = hvsi_close,
.write = hvsi_write,
diff --git a/drivers/char/hw_random/intel-rng.c b/drivers/char/hw_random/intel-rng.c
index ccd7e710223..8efbc9c0e54 100644
--- a/drivers/char/hw_random/intel-rng.c
+++ b/drivers/char/hw_random/intel-rng.c
@@ -50,6 +50,43 @@
#define INTEL_RNG_ADDR_LEN 3
/*
+ * LPC bridge PCI config space registers
+ */
+#define FWH_DEC_EN1_REG_OLD 0xe3
+#define FWH_DEC_EN1_REG_NEW 0xd9 /* high byte of 16-bit register */
+#define FWH_F8_EN_MASK 0x80
+
+#define BIOS_CNTL_REG_OLD 0x4e
+#define BIOS_CNTL_REG_NEW 0xdc
+#define BIOS_CNTL_WRITE_ENABLE_MASK 0x01
+#define BIOS_CNTL_LOCK_ENABLE_MASK 0x02
+
+/*
+ * Magic address at which Intel Firmware Hubs get accessed
+ */
+#define INTEL_FWH_ADDR 0xffff0000
+#define INTEL_FWH_ADDR_LEN 2
+
+/*
+ * Intel Firmware Hub command codes (write to any address inside the device)
+ */
+#define INTEL_FWH_RESET_CMD 0xff /* aka READ_ARRAY */
+#define INTEL_FWH_READ_ID_CMD 0x90
+
+/*
+ * Intel Firmware Hub Read ID command result addresses
+ */
+#define INTEL_FWH_MANUFACTURER_CODE_ADDRESS 0x000000
+#define INTEL_FWH_DEVICE_CODE_ADDRESS 0x000001
+
+/*
+ * Intel Firmware Hub Read ID command result values
+ */
+#define INTEL_FWH_MANUFACTURER_CODE 0x89
+#define INTEL_FWH_DEVICE_CODE_8M 0xac
+#define INTEL_FWH_DEVICE_CODE_4M 0xad
+
+/*
* Data for PCI driver interface
*
* This data only exists for exporting the supported
@@ -58,12 +95,50 @@
* 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, },
+/* AA
+ { 0x8086, 0x2418, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2410, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AA */
+/* AB
+ { 0x8086, 0x2428, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2420, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* AB */
+/* ??
+ { 0x8086, 0x2430, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+/* BAM, CAM, DBM, FBM, GxM
+ { 0x8086, 0x2448, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x244c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BAM */
+ { 0x8086, 0x248c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CAM */
+ { 0x8086, 0x24cc, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DBM */
+ { 0x8086, 0x2641, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* FBM */
+ { 0x8086, 0x27b9, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM */
+ { 0x8086, 0x27bd, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* GxM DH */
+/* BA, CA, DB, Ex, 6300, Fx, 631x/632x, Gx
+ { 0x8086, 0x244e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2440, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* BA */
+ { 0x8086, 0x2480, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* CA */
+ { 0x8086, 0x24c0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* DB */
+ { 0x8086, 0x24d0, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Ex */
+ { 0x8086, 0x25a1, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 6300 */
+ { 0x8086, 0x2640, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Fx */
+ { 0x8086, 0x2670, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2671, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2672, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2673, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2674, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2675, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2676, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2677, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2678, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x2679, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267a, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267b, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267c, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267d, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x267f, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* 631x/632x */
+ { 0x8086, 0x27b8, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* Gx */
+/* E
+ { 0x8086, 0x245e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, */
+ { 0x8086, 0x2450, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0, }, /* E */
{ 0, }, /* terminate list */
};
MODULE_DEVICE_TABLE(pci, pci_tbl);
@@ -138,22 +213,115 @@ static struct hwrng intel_rng = {
};
+#ifdef CONFIG_SMP
+static char __initdata waitflag;
+
+static void __init intel_init_wait(void *unused)
+{
+ while (waitflag)
+ cpu_relax();
+}
+#endif
+
static int __init mod_init(void)
{
int err = -ENODEV;
+ unsigned i;
+ struct pci_dev *dev = NULL;
void __iomem *mem;
- u8 hw_status;
+ unsigned long flags;
+ u8 bios_cntl_off, fwh_dec_en1_off;
+ u8 bios_cntl_val = 0xff, fwh_dec_en1_val = 0xff;
+ u8 hw_status, mfc, dvc;
- if (!pci_dev_present(pci_tbl))
+ for (i = 0; !dev && pci_tbl[i].vendor; ++i)
+ dev = pci_get_device(pci_tbl[i].vendor, pci_tbl[i].device, NULL);
+
+ if (!dev)
goto out; /* Device not found. */
+ /* Check for Intel 82802 */
+ if (dev->device < 0x2640) {
+ fwh_dec_en1_off = FWH_DEC_EN1_REG_OLD;
+ bios_cntl_off = BIOS_CNTL_REG_OLD;
+ } else {
+ fwh_dec_en1_off = FWH_DEC_EN1_REG_NEW;
+ bios_cntl_off = BIOS_CNTL_REG_NEW;
+ }
+
+ pci_read_config_byte(dev, fwh_dec_en1_off, &fwh_dec_en1_val);
+ pci_read_config_byte(dev, bios_cntl_off, &bios_cntl_val);
+
+ mem = ioremap_nocache(INTEL_FWH_ADDR, INTEL_FWH_ADDR_LEN);
+ if (mem == NULL) {
+ pci_dev_put(dev);
+ err = -EBUSY;
+ goto out;
+ }
+
+ /*
+ * Since the BIOS code/data is going to disappear from its normal
+ * location with the Read ID command, all activity on the system
+ * must be stopped until the state is back to normal.
+ */
+#ifdef CONFIG_SMP
+ set_mb(waitflag, 1);
+ if (smp_call_function(intel_init_wait, NULL, 1, 0) != 0) {
+ set_mb(waitflag, 0);
+ pci_dev_put(dev);
+ printk(KERN_ERR PFX "cannot run on all processors\n");
+ err = -EAGAIN;
+ goto err_unmap;
+ }
+#endif
+ local_irq_save(flags);
+
+ if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
+ pci_write_config_byte(dev,
+ fwh_dec_en1_off,
+ fwh_dec_en1_val | FWH_F8_EN_MASK);
+ if (!(bios_cntl_val &
+ (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
+ pci_write_config_byte(dev,
+ bios_cntl_off,
+ bios_cntl_val | BIOS_CNTL_WRITE_ENABLE_MASK);
+
+ writeb(INTEL_FWH_RESET_CMD, mem);
+ writeb(INTEL_FWH_READ_ID_CMD, mem);
+ mfc = readb(mem + INTEL_FWH_MANUFACTURER_CODE_ADDRESS);
+ dvc = readb(mem + INTEL_FWH_DEVICE_CODE_ADDRESS);
+ writeb(INTEL_FWH_RESET_CMD, mem);
+
+ if (!(bios_cntl_val &
+ (BIOS_CNTL_LOCK_ENABLE_MASK|BIOS_CNTL_WRITE_ENABLE_MASK)))
+ pci_write_config_byte(dev, bios_cntl_off, bios_cntl_val);
+ if (!(fwh_dec_en1_val & FWH_F8_EN_MASK))
+ pci_write_config_byte(dev, fwh_dec_en1_off, fwh_dec_en1_val);
+
+ local_irq_restore(flags);
+#ifdef CONFIG_SMP
+ /* Tell other CPUs to resume. */
+ set_mb(waitflag, 0);
+#endif
+
+ iounmap(mem);
+ pci_dev_put(dev);
+
+ if (mfc != INTEL_FWH_MANUFACTURER_CODE ||
+ (dvc != INTEL_FWH_DEVICE_CODE_8M &&
+ dvc != INTEL_FWH_DEVICE_CODE_4M)) {
+ printk(KERN_ERR PFX "FWH not detected\n");
+ err = -ENODEV;
+ goto out;
+ }
+
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 */
+ /* Check for Random Number Generator */
err = -ENODEV;
hw_status = hwstatus_get(mem);
if ((hw_status & INTEL_RNG_PRESENT) == 0)
diff --git a/drivers/char/ip2/ip2main.c b/drivers/char/ip2/ip2main.c
index 7907ae88c2f..62ef511d143 100644
--- a/drivers/char/ip2/ip2main.c
+++ b/drivers/char/ip2/ip2main.c
@@ -436,6 +436,7 @@ cleanup_module(void)
#ifdef CONFIG_PCI
if (ip2config.type[i] == PCI && ip2config.pci_dev[i]) {
pci_disable_device(ip2config.pci_dev[i]);
+ pci_dev_put(ip2config.pci_dev[i]);
ip2config.pci_dev[i] = NULL;
}
#endif
@@ -457,7 +458,7 @@ cleanup_module(void)
}
#endif /* MODULE */
-static struct tty_operations ip2_ops = {
+static const struct tty_operations ip2_ops = {
.open = ip2_open,
.close = ip2_close,
.write = ip2_write,
@@ -505,6 +506,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
static int loaded;
i2eBordStrPtr pB = NULL;
int rc = -1;
+ static struct pci_dev *pci_dev_i = NULL;
ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_ENTER, 0 );
@@ -588,8 +590,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
case PCI:
#ifdef CONFIG_PCI
{
- struct pci_dev *pci_dev_i = NULL;
- pci_dev_i = pci_find_device(PCI_VENDOR_ID_COMPUTONE,
+ pci_dev_i = pci_get_device(PCI_VENDOR_ID_COMPUTONE,
PCI_DEVICE_ID_COMPUTONE_IP2EX, pci_dev_i);
if (pci_dev_i != NULL) {
unsigned int addr;
@@ -600,7 +601,7 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
break;
}
ip2config.type[i] = PCI;
- ip2config.pci_dev[i] = pci_dev_i;
+ ip2config.pci_dev[i] = pci_dev_get(pci_dev_i);
status =
pci_read_config_dword(pci_dev_i, PCI_BASE_ADDRESS_1, &addr);
if ( addr & 1 ) {
@@ -641,6 +642,9 @@ ip2_loadmain(int *iop, int *irqp, unsigned char *firmware, int firmsize)
break;
} /* switch */
} /* for */
+ if (pci_dev_i)
+ pci_dev_put(pci_dev_i);
+
for ( i = 0; i < IP2_MAX_BOARDS; ++i ) {
if ( ip2config.addr[i] ) {
pB = kmalloc( sizeof(i2eBordStr), GFP_KERNEL);
@@ -775,8 +779,6 @@ retry:
ip2trace (ITRC_NO_PORT, ITRC_INIT, ITRC_RETURN, 0 );
goto out;
-out_class:
- class_destroy(ip2_class);
out_chrdev:
unregister_chrdev(IP2_IPL_MAJOR, "ip2");
out:
diff --git a/drivers/char/ipmi/ipmi_devintf.c b/drivers/char/ipmi/ipmi_devintf.c
index 68d7c61a864..81fcf0ce21d 100644
--- a/drivers/char/ipmi/ipmi_devintf.c
+++ b/drivers/char/ipmi/ipmi_devintf.c
@@ -377,7 +377,8 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
- rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd);
+ rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
+ IPMI_CHAN_ALL);
break;
}
@@ -390,7 +391,36 @@ static int ipmi_ioctl(struct inode *inode,
break;
}
- rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd);
+ rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
+ IPMI_CHAN_ALL);
+ break;
+ }
+
+ case IPMICTL_REGISTER_FOR_CMD_CHANS:
+ {
+ struct ipmi_cmdspec_chans val;
+
+ if (copy_from_user(&val, arg, sizeof(val))) {
+ rv = -EFAULT;
+ break;
+ }
+
+ rv = ipmi_register_for_cmd(priv->user, val.netfn, val.cmd,
+ val.chans);
+ break;
+ }
+
+ case IPMICTL_UNREGISTER_FOR_CMD_CHANS:
+ {
+ struct ipmi_cmdspec_chans val;
+
+ if (copy_from_user(&val, arg, sizeof(val))) {
+ rv = -EFAULT;
+ break;
+ }
+
+ rv = ipmi_unregister_for_cmd(priv->user, val.netfn, val.cmd,
+ val.chans);
break;
}
diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c
index 843d34c8627..2455e8d478a 100644
--- a/drivers/char/ipmi/ipmi_msghandler.c
+++ b/drivers/char/ipmi/ipmi_msghandler.c
@@ -96,6 +96,7 @@ struct cmd_rcvr
ipmi_user_t user;
unsigned char netfn;
unsigned char cmd;
+ unsigned int chans;
/*
* This is used to form a linked lised during mass deletion.
@@ -953,24 +954,41 @@ int ipmi_set_gets_events(ipmi_user_t user, int val)
static struct cmd_rcvr *find_cmd_rcvr(ipmi_smi_t intf,
unsigned char netfn,
- unsigned char cmd)
+ unsigned char cmd,
+ unsigned char chan)
{
struct cmd_rcvr *rcvr;
list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
- if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd))
+ if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
+ && (rcvr->chans & (1 << chan)))
return rcvr;
}
return NULL;
}
+static int is_cmd_rcvr_exclusive(ipmi_smi_t intf,
+ unsigned char netfn,
+ unsigned char cmd,
+ unsigned int chans)
+{
+ struct cmd_rcvr *rcvr;
+
+ list_for_each_entry_rcu(rcvr, &intf->cmd_rcvrs, link) {
+ if ((rcvr->netfn == netfn) && (rcvr->cmd == cmd)
+ && (rcvr->chans & chans))
+ return 0;
+ }
+ return 1;
+}
+
int ipmi_register_for_cmd(ipmi_user_t user,
unsigned char netfn,
- unsigned char cmd)
+ unsigned char cmd,
+ unsigned int chans)
{
ipmi_smi_t intf = user->intf;
struct cmd_rcvr *rcvr;
- struct cmd_rcvr *entry;
int rv = 0;
@@ -979,12 +997,12 @@ int ipmi_register_for_cmd(ipmi_user_t user,
return -ENOMEM;
rcvr->cmd = cmd;
rcvr->netfn = netfn;
+ rcvr->chans = chans;
rcvr->user = user;
mutex_lock(&intf->cmd_rcvrs_mutex);
/* Make sure the command/netfn is not already registered. */
- entry = find_cmd_rcvr(intf, netfn, cmd);
- if (entry) {
+ if (!is_cmd_rcvr_exclusive(intf, netfn, cmd, chans)) {
rv = -EBUSY;
goto out_unlock;
}
@@ -1001,24 +1019,39 @@ int ipmi_register_for_cmd(ipmi_user_t user,
int ipmi_unregister_for_cmd(ipmi_user_t user,
unsigned char netfn,
- unsigned char cmd)
+ unsigned char cmd,
+ unsigned int chans)
{
ipmi_smi_t intf = user->intf;
struct cmd_rcvr *rcvr;
+ struct cmd_rcvr *rcvrs = NULL;
+ int i, rv = -ENOENT;
mutex_lock(&intf->cmd_rcvrs_mutex);
- /* Make sure the command/netfn is not already registered. */
- rcvr = find_cmd_rcvr(intf, netfn, cmd);
- if ((rcvr) && (rcvr->user == user)) {
- list_del_rcu(&rcvr->link);
- mutex_unlock(&intf->cmd_rcvrs_mutex);
- synchronize_rcu();
+ for (i = 0; i < IPMI_NUM_CHANNELS; i++) {
+ if (((1 << i) & chans) == 0)
+ continue;
+ rcvr = find_cmd_rcvr(intf, netfn, cmd, i);
+ if (rcvr == NULL)
+ continue;
+ if (rcvr->user == user) {
+ rv = 0;
+ rcvr->chans &= ~chans;
+ if (rcvr->chans == 0) {
+ list_del_rcu(&rcvr->link);
+ rcvr->next = rcvrs;
+ rcvrs = rcvr;
+ }
+ }
+ }
+ mutex_unlock(&intf->cmd_rcvrs_mutex);
+ synchronize_rcu();
+ while (rcvrs) {
+ rcvr = rcvrs;
+ rcvrs = rcvr->next;
kfree(rcvr);
- return 0;
- } else {
- mutex_unlock(&intf->cmd_rcvrs_mutex);
- return -ENOENT;
}
+ return rv;
}
void ipmi_user_set_run_to_completion(ipmi_user_t user, int val)
@@ -2548,6 +2581,7 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
int rv = 0;
unsigned char netfn;
unsigned char cmd;
+ unsigned char chan;
ipmi_user_t user = NULL;
struct ipmi_ipmb_addr *ipmb_addr;
struct ipmi_recv_msg *recv_msg;
@@ -2568,9 +2602,10 @@ static int handle_ipmb_get_msg_cmd(ipmi_smi_t intf,
netfn = msg->rsp[4] >> 2;
cmd = msg->rsp[8];
+ chan = msg->rsp[3] & 0xf;
rcu_read_lock();
- rcvr = find_cmd_rcvr(intf, netfn, cmd);
+ rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) {
user = rcvr->user;
kref_get(&user->refcount);
@@ -2728,6 +2763,7 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
int rv = 0;
unsigned char netfn;
unsigned char cmd;
+ unsigned char chan;
ipmi_user_t user = NULL;
struct ipmi_lan_addr *lan_addr;
struct ipmi_recv_msg *recv_msg;
@@ -2748,9 +2784,10 @@ static int handle_lan_get_msg_cmd(ipmi_smi_t intf,
netfn = msg->rsp[6] >> 2;
cmd = msg->rsp[10];
+ chan = msg->rsp[3] & 0xf;
rcu_read_lock();
- rcvr = find_cmd_rcvr(intf, netfn, cmd);
+ rcvr = find_cmd_rcvr(intf, netfn, cmd, chan);
if (rcvr) {
user = rcvr->user;
kref_get(&user->refcount);
diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c
index abca98beac1..b106c45abfc 100644
--- a/drivers/char/ipmi/ipmi_si_intf.c
+++ b/drivers/char/ipmi/ipmi_si_intf.c
@@ -217,6 +217,11 @@ struct smi_info
struct list_head link;
};
+#define SI_MAX_PARMS 4
+
+static int force_kipmid[SI_MAX_PARMS];
+static int num_force_kipmid;
+
static int try_smi_init(struct smi_info *smi);
static ATOMIC_NOTIFIER_HEAD(xaction_notifier_list);
@@ -908,6 +913,7 @@ static int smi_start_processing(void *send_info,
ipmi_smi_t intf)
{
struct smi_info *new_smi = send_info;
+ int enable = 0;
new_smi->intf = intf;
@@ -916,7 +922,19 @@ static int smi_start_processing(void *send_info,
new_smi->last_timeout_jiffies = jiffies;
mod_timer(&new_smi->si_timer, jiffies + SI_TIMEOUT_JIFFIES);
- if (new_smi->si_type != SI_BT) {
+ /*
+ * Check if the user forcefully enabled the daemon.
+ */
+ if (new_smi->intf_num < num_force_kipmid)
+ enable = force_kipmid[new_smi->intf_num];
+ /*
+ * The BT interface is efficient enough to not need a thread,
+ * and there is no need for a thread if we have interrupts.
+ */
+ else if ((new_smi->si_type != SI_BT) && (!new_smi->irq))
+ enable = 1;
+
+ if (enable) {
new_smi->thread = kthread_run(ipmi_thread, new_smi,
"kipmi%d", new_smi->intf_num);
if (IS_ERR(new_smi->thread)) {
@@ -944,7 +962,6 @@ static struct ipmi_smi_handlers handlers =
/* There can be 4 IO ports passed in (with or without IRQs), 4 addresses,
a default IO port, and 1 ACPI/SPMI address. That sets SI_MAX_DRIVERS */
-#define SI_MAX_PARMS 4
static LIST_HEAD(smi_infos);
static DEFINE_MUTEX(smi_infos_lock);
static int smi_num; /* Used to sequence the SMIs */
@@ -1017,6 +1034,10 @@ MODULE_PARM_DESC(slave_addrs, "Set the default IPMB slave address for"
" the controller. Normally this is 0x20, but can be"
" overridden by this parm. This is an array indexed"
" by interface number.");
+module_param_array(force_kipmid, int, &num_force_kipmid, 0);
+MODULE_PARM_DESC(force_kipmid, "Force the kipmi daemon to be enabled (1) or"
+ " disabled(0). Normally the IPMI driver auto-detects"
+ " this, but the value may be overridden by this parm.");
#define IPMI_IO_ADDR_SPACE 0
@@ -1730,6 +1751,7 @@ static void __devinit dmi_find_bmc(void)
int rv;
while ((dev = dmi_find_device(DMI_DEV_TYPE_IPMI, NULL, dev))) {
+ memset(&data, 0, sizeof(data));
rv = decode_dmi((struct dmi_header *) dev->device_data, &data);
if (!rv)
try_init_dmi(&data);
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 913be23e0a2..ea2bbf80ad3 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -1550,7 +1550,7 @@ static void isicom_unregister_ioregion(struct pci_dev *pdev)
board->base = 0;
}
-static struct tty_operations isicom_ops = {
+static const struct tty_operations isicom_ops = {
.open = isicom_open,
.close = isicom_close,
.write = isicom_write,
@@ -1756,9 +1756,12 @@ static int __devinit load_firmware(struct pci_dev *pdev,
if (retval)
goto end;
+ retval = -EIO;
+
for (frame = (struct stframe *)fw->data;
frame < (struct stframe *)(fw->data + fw->size);
- frame++) {
+ frame = (struct stframe *)((u8 *)(frame + 1) +
+ frame->count)) {
if (WaitTillCardIsFree(base))
goto errrelfw;
@@ -1797,23 +1800,12 @@ static int __devinit load_firmware(struct pci_dev *pdev,
}
}
- retval = -EIO;
-
- if (WaitTillCardIsFree(base))
- goto errrelfw;
-
- outw(0xf2, base);
- outw(0x800, base);
- outw(0x0, base);
- outw(0x0, base);
- InterruptTheCard(base);
- outw(0x0, base + 0x4); /* for ISI4608 cards */
-
/* XXX: should we test it by reading it back and comparing with original like
* in load firmware package? */
- for (frame = (struct stframe*)fw->data;
- frame < (struct stframe*)(fw->data + fw->size);
- frame++) {
+ for (frame = (struct stframe *)fw->data;
+ frame < (struct stframe *)(fw->data + fw->size);
+ frame = (struct stframe *)((u8 *)(frame + 1) +
+ frame->count)) {
if (WaitTillCardIsFree(base))
goto errrelfw;
@@ -1863,6 +1855,17 @@ static int __devinit load_firmware(struct pci_dev *pdev,
}
}
+ /* xfer ctrl */
+ if (WaitTillCardIsFree(base))
+ goto errrelfw;
+
+ outw(0xf2, base);
+ outw(0x800, base);
+ outw(0x0, base);
+ outw(0x0, base);
+ InterruptTheCard(base);
+ outw(0x0, base + 0x4); /* for ISI4608 cards */
+
board->status |= FIRMWARE_LOADED;
retval = 0;
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 8c09997cc3d..d6e031542c6 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -612,16 +612,6 @@ MODULE_DEVICE_TABLE(pci, istallion_pci_tbl);
#define MINOR2BRD(min) (((min) & 0xc0) >> 6)
#define MINOR2PORT(min) ((min) & 0x3f)
-/*
- * Define a baud rate table that converts termios baud rate selector
- * into the actual baud rate value. All baud rate calculations are based
- * on the actual baud rate required.
- */
-static unsigned int stli_baudrates[] = {
- 0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
- 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600
-};
-
/*****************************************************************************/
/*
@@ -2747,15 +2737,7 @@ static void stli_mkasyport(stliport_t *portp, asyport_t *pp, struct termios *tio
/*
* Start of by setting the baud, char size, parity and stop bit info.
*/
- pp->baudout = tiosp->c_cflag & CBAUD;
- if (pp->baudout & CBAUDEX) {
- pp->baudout &= ~CBAUDEX;
- if ((pp->baudout < 1) || (pp->baudout > 4))
- tiosp->c_cflag &= ~CBAUDEX;
- else
- pp->baudout += 15;
- }
- pp->baudout = stli_baudrates[pp->baudout];
+ pp->baudout = tty_get_baud_rate(portp->tty);
if ((tiosp->c_cflag & CBAUD) == B38400) {
if ((portp->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
pp->baudout = 57600;
@@ -4654,7 +4636,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
return rc;
}
-static struct tty_operations stli_ops = {
+static const struct tty_operations stli_ops = {
.open = stli_open,
.close = stli_close,
.write = stli_write,
diff --git a/drivers/char/keyboard.c b/drivers/char/keyboard.c
index 3e90aac3751..e2011669c7b 100644
--- a/drivers/char/keyboard.c
+++ b/drivers/char/keyboard.c
@@ -108,7 +108,11 @@ const int NR_TYPES = ARRAY_SIZE(max_vals);
struct kbd_struct kbd_table[MAX_NR_CONSOLES];
static struct kbd_struct *kbd = kbd_table;
-int spawnpid, spawnsig;
+struct vt_spawn_console vt_spawn_con = {
+ .lock = SPIN_LOCK_UNLOCKED,
+ .pid = NULL,
+ .sig = 0,
+};
/*
* Variables exported for vt.c
@@ -578,9 +582,13 @@ static void fn_compose(struct vc_data *vc, struct pt_regs *regs)
static void fn_spawn_con(struct vc_data *vc, struct pt_regs *regs)
{
- if (spawnpid)
- if (kill_proc(spawnpid, spawnsig, 1))
- spawnpid = 0;
+ spin_lock(&vt_spawn_con.lock);
+ if (vt_spawn_con.pid)
+ if (kill_pid(vt_spawn_con.pid, vt_spawn_con.sig, 1)) {
+ put_pid(vt_spawn_con.pid);
+ vt_spawn_con.pid = NULL;
+ }
+ spin_unlock(&vt_spawn_con.lock);
}
static void fn_SAK(struct vc_data *vc, struct pt_regs *regs)
@@ -1285,7 +1293,7 @@ static void kbd_event(struct input_handle *handle, unsigned int event_type,
*/
static struct input_handle *kbd_connect(struct input_handler *handler,
struct input_dev *dev,
- struct input_device_id *id)
+ const struct input_device_id *id)
{
struct input_handle *handle;
int i;
@@ -1334,7 +1342,7 @@ static void kbd_start(struct input_handle *handle)
tasklet_enable(&keyboard_tasklet);
}
-static struct input_device_id kbd_ids[] = {
+static const struct input_device_id kbd_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT,
.evbit = { BIT(EV_KEY) },
@@ -1362,6 +1370,7 @@ static struct input_handler kbd_handler = {
int __init kbd_init(void)
{
int i;
+ int error;
for (i = 0; i < MAX_NR_CONSOLES; i++) {
kbd_table[i].ledflagstate = KBD_DEFLEDS;
@@ -1373,7 +1382,9 @@ int __init kbd_init(void)
kbd_table[i].kbdmode = VC_XLATE;
}
- input_register_handler(&kbd_handler);
+ error = input_register_handler(&kbd_handler);
+ if (error)
+ return error;
tasklet_enable(&keyboard_tasklet);
tasklet_schedule(&keyboard_tasklet);
diff --git a/drivers/char/lp.c b/drivers/char/lp.c
index f875fda3b08..1ecea7d448f 100644
--- a/drivers/char/lp.c
+++ b/drivers/char/lp.c
@@ -906,7 +906,7 @@ static int __init lp_init (void)
lp_class = class_create(THIS_MODULE, "printer");
if (IS_ERR(lp_class)) {
err = PTR_ERR(lp_class);
- goto out_devfs;
+ goto out_reg;
}
if (parport_register_driver (&lp_driver)) {
@@ -927,7 +927,7 @@ static int __init lp_init (void)
out_class:
class_destroy(lp_class);
-out_devfs:
+out_reg:
unregister_chrdev(LP_MAJOR, "lp");
return err;
}
diff --git a/drivers/char/mbcs.c b/drivers/char/mbcs.c
index 0385650f607..63635472265 100644
--- a/drivers/char/mbcs.c
+++ b/drivers/char/mbcs.c
@@ -22,6 +22,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/mm.h>
+#include <linux/fs.h>
#include <linux/uio.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -447,15 +448,15 @@ loff_t mbcs_sram_llseek(struct file * filp, loff_t off, int whence)
loff_t newpos;
switch (whence) {
- case 0: /* SEEK_SET */
+ case SEEK_SET:
newpos = off;
break;
- case 1: /* SEEK_CUR */
+ case SEEK_CUR:
newpos = filp->f_pos + off;
break;
- case 2: /* SEEK_END */
+ case SEEK_END:
newpos = MBCS_SRAM_SIZE + off;
break;
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 917b2040266..6511012cbdc 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -238,6 +238,32 @@ static pgprot_t phys_mem_access_prot(struct file *file, unsigned long pfn,
}
#endif
+#ifndef CONFIG_MMU
+static unsigned long get_unmapped_area_mem(struct file *file,
+ unsigned long addr,
+ unsigned long len,
+ unsigned long pgoff,
+ unsigned long flags)
+{
+ if (!valid_mmap_phys_addr_range(pgoff, len))
+ return (unsigned long) -EINVAL;
+ return pgoff;
+}
+
+/* can't do an in-place private mapping if there's no MMU */
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+ return vma->vm_flags & VM_MAYSHARE;
+}
+#else
+#define get_unmapped_area_mem NULL
+
+static inline int private_mapping_ok(struct vm_area_struct *vma)
+{
+ return 1;
+}
+#endif
+
static int mmap_mem(struct file * file, struct vm_area_struct * vma)
{
size_t size = vma->vm_end - vma->vm_start;
@@ -245,6 +271,9 @@ static int mmap_mem(struct file * file, struct vm_area_struct * vma)
if (!valid_mmap_phys_addr_range(vma->vm_pgoff, size))
return -EINVAL;
+ if (!private_mapping_ok(vma))
+ return -ENOSYS;
+
vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
size,
vma->vm_page_prot);
@@ -522,7 +551,7 @@ static ssize_t write_kmem(struct file * file, const char __user * buf,
return virtr + wrote;
}
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
static ssize_t read_port(struct file * file, char __user * buf,
size_t count, loff_t *ppos)
{
@@ -782,6 +811,7 @@ static const struct file_operations mem_fops = {
.write = write_mem,
.mmap = mmap_mem,
.open = open_mem,
+ .get_unmapped_area = get_unmapped_area_mem,
};
static const struct file_operations kmem_fops = {
@@ -790,6 +820,7 @@ static const struct file_operations kmem_fops = {
.write = write_kmem,
.mmap = mmap_kmem,
.open = open_kmem,
+ .get_unmapped_area = get_unmapped_area_mem,
};
static const struct file_operations null_fops = {
@@ -799,7 +830,7 @@ static const struct file_operations null_fops = {
.splice_write = splice_write_null,
};
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
static const struct file_operations port_fops = {
.llseek = memory_lseek,
.read = read_port,
@@ -815,6 +846,10 @@ static const struct file_operations zero_fops = {
.mmap = mmap_zero,
};
+/*
+ * capabilities for /dev/zero
+ * - permits private mappings, "copies" are taken of the source of zeros
+ */
static struct backing_dev_info zero_bdi = {
.capabilities = BDI_CAP_MAP_COPY,
};
@@ -862,14 +897,18 @@ static int memory_open(struct inode * inode, struct file * filp)
switch (iminor(inode)) {
case 1:
filp->f_op = &mem_fops;
+ filp->f_mapping->backing_dev_info =
+ &directly_mappable_cdev_bdi;
break;
case 2:
filp->f_op = &kmem_fops;
+ filp->f_mapping->backing_dev_info =
+ &directly_mappable_cdev_bdi;
break;
case 3:
filp->f_op = &null_fops;
break;
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
case 4:
filp->f_op = &port_fops;
break;
@@ -916,7 +955,7 @@ static const struct {
{1, "mem", S_IRUSR | S_IWUSR | S_IRGRP, &mem_fops},
{2, "kmem", S_IRUSR | S_IWUSR | S_IRGRP, &kmem_fops},
{3, "null", S_IRUGO | S_IWUGO, &null_fops},
-#if defined(CONFIG_ISA) || !defined(__mc68000__)
+#if (defined(CONFIG_ISA) || defined(CONFIG_PCI)) && !defined(__mc68000__)
{4, "port", S_IRUSR | S_IWUSR | S_IRGRP, &port_fops},
#endif
{5, "zero", S_IRUGO | S_IWUGO, &zero_fops},
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index a369dd6877d..b401383808c 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -260,7 +260,7 @@ static void MoxaPortEnable(int);
static void MoxaPortDisable(int);
static long MoxaPortGetMaxBaud(int);
static long MoxaPortSetBaud(int, long);
-static int MoxaPortSetTermio(int, struct termios *);
+static int MoxaPortSetTermio(int, struct termios *, speed_t);
static int MoxaPortGetLineOut(int, int *, int *);
static void MoxaPortLineCtrl(int, int, int);
static void MoxaPortFlowCtrl(int, int, int, int, int, int);
@@ -281,7 +281,7 @@ static int moxa_get_serial_info(struct moxa_str *, struct serial_struct __user *
static int moxa_set_serial_info(struct moxa_str *, struct serial_struct __user *);
static void MoxaSetFifo(int port, int enable);
-static struct tty_operations moxa_ops = {
+static const struct tty_operations moxa_ops = {
.open = moxa_open,
.close = moxa_close,
.write = moxa_write,
@@ -986,7 +986,7 @@ static void set_tty_param(struct tty_struct *tty)
if (ts->c_iflag & IXANY)
xany = 1;
MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany);
- MoxaPortSetTermio(ch->port, ts);
+ MoxaPortSetTermio(ch->port, ts, tty_get_baud_rate(tty));
}
static int block_till_ready(struct tty_struct *tty, struct file *filp,
@@ -1900,9 +1900,10 @@ int MoxaPortsOfCard(int cardno)
*
* Function 12: Configure the port.
* Syntax:
- * int MoxaPortSetTermio(int port, struct termios *termio);
+ * int MoxaPortSetTermio(int port, struct termios *termio, speed_t baud);
* int port : port number (0 - 127)
* struct termios * termio : termio structure pointer
+ * speed_t baud : baud rate
*
* return: -1 : this port is invalid or termio == NULL
* 0 : setting O.K.
@@ -2182,11 +2183,10 @@ long MoxaPortSetBaud(int port, long baud)
return (baud);
}
-int MoxaPortSetTermio(int port, struct termios *termio)
+int MoxaPortSetTermio(int port, struct termios *termio, speed_t baud)
{
void __iomem *ofsAddr;
tcflag_t cflag;
- long baud;
tcflag_t mode = 0;
if (moxaChkPort[port] == 0 || termio == 0)
@@ -2222,77 +2222,9 @@ int MoxaPortSetTermio(int port, struct termios *termio)
moxafunc(ofsAddr, FC_SetDataMode, (ushort) mode);
- cflag &= (CBAUD | CBAUDEX);
-#ifndef B921600
-#define B921600 (B460800+1)
-#endif
- switch (cflag) {
- case B921600:
- baud = 921600L;
- break;
- case B460800:
- baud = 460800L;
- break;
- case B230400:
- baud = 230400L;
- break;
- case B115200:
- baud = 115200L;
- break;
- case B57600:
- baud = 57600L;
- break;
- case B38400:
- baud = 38400L;
- break;
- case B19200:
- baud = 19200L;
- break;
- case B9600:
- baud = 9600L;
- break;
- case B4800:
- baud = 4800L;
- break;
- case B2400:
- baud = 2400L;
- break;
- case B1800:
- baud = 1800L;
- break;
- case B1200:
- baud = 1200L;
- break;
- case B600:
- baud = 600L;
- break;
- case B300:
- baud = 300L;
- break;
- case B200:
- baud = 200L;
- break;
- case B150:
- baud = 150L;
- break;
- case B134:
- baud = 134L;
- break;
- case B110:
- baud = 110L;
- break;
- case B75:
- baud = 75L;
- break;
- case B50:
- baud = 50L;
- break;
- default:
- baud = 0;
- }
if ((moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_ISA) ||
(moxa_boards[port / MAX_PORTS_PER_BOARD].boardType == MOXA_BOARD_C320_PCI)) {
- if (baud == 921600L)
+ if (baud >= 921600L)
return (-1);
}
MoxaPortSetBaud(port, baud);
diff --git a/drivers/char/mspec.c b/drivers/char/mspec.c
new file mode 100644
index 00000000000..5426b1e5595
--- /dev/null
+++ b/drivers/char/mspec.c
@@ -0,0 +1,421 @@
+/*
+ * Copyright (C) 2001-2006 Silicon Graphics, Inc. All rights
+ * reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of version 2 of the GNU General Public License
+ * as published by the Free Software Foundation.
+ */
+
+/*
+ * SN Platform Special Memory (mspec) Support
+ *
+ * This driver exports the SN special memory (mspec) facility to user
+ * processes.
+ * There are three types of memory made available thru this driver:
+ * fetchops, uncached and cached.
+ *
+ * Fetchops are atomic memory operations that are implemented in the
+ * memory controller on SGI SN hardware.
+ *
+ * Uncached are used for memory write combining feature of the ia64
+ * cpu.
+ *
+ * Cached are used for areas of memory that are used as cached addresses
+ * on our partition and used as uncached addresses from other partitions.
+ * Due to a design constraint of the SN2 Shub, you can not have processors
+ * on the same FSB perform both a cached and uncached reference to the
+ * same cache line. These special memory cached regions prevent the
+ * kernel from ever dropping in a TLB entry and therefore prevent the
+ * processor from ever speculating a cache line from this page.
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/miscdevice.h>
+#include <linux/spinlock.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/numa.h>
+#include <asm/page.h>
+#include <asm/system.h>
+#include <asm/pgtable.h>
+#include <asm/atomic.h>
+#include <asm/tlbflush.h>
+#include <asm/uncached.h>
+#include <asm/sn/addrs.h>
+#include <asm/sn/arch.h>
+#include <asm/sn/mspec.h>
+#include <asm/sn/sn_cpuid.h>
+#include <asm/sn/io.h>
+#include <asm/sn/bte.h>
+#include <asm/sn/shubio.h>
+
+
+#define FETCHOP_ID "SGI Fetchop,"
+#define CACHED_ID "Cached,"
+#define UNCACHED_ID "Uncached"
+#define REVISION "4.0"
+#define MSPEC_BASENAME "mspec"
+
+/*
+ * Page types allocated by the device.
+ */
+enum {
+ MSPEC_FETCHOP = 1,
+ MSPEC_CACHED,
+ MSPEC_UNCACHED
+};
+
+static int is_sn2;
+
+/*
+ * One of these structures is allocated when an mspec region is mmaped. The
+ * structure is pointed to by the vma->vm_private_data field in the vma struct.
+ * This structure is used to record the addresses of the mspec pages.
+ */
+struct vma_data {
+ atomic_t refcnt; /* Number of vmas sharing the data. */
+ spinlock_t lock; /* Serialize access to the vma. */
+ int count; /* Number of pages allocated. */
+ int type; /* Type of pages allocated. */
+ unsigned long maddr[0]; /* Array of MSPEC addresses. */
+};
+
+/* used on shub2 to clear FOP cache in the HUB */
+static unsigned long scratch_page[MAX_NUMNODES];
+#define SH2_AMO_CACHE_ENTRIES 4
+
+static inline int
+mspec_zero_block(unsigned long addr, int len)
+{
+ int status;
+
+ if (is_sn2) {
+ if (is_shub2()) {
+ int nid;
+ void *p;
+ int i;
+
+ nid = nasid_to_cnodeid(get_node_number(__pa(addr)));
+ p = (void *)TO_AMO(scratch_page[nid]);
+
+ for (i=0; i < SH2_AMO_CACHE_ENTRIES; i++) {
+ FETCHOP_LOAD_OP(p, FETCHOP_LOAD);
+ p += FETCHOP_VAR_SIZE;
+ }
+ }
+
+ status = bte_copy(0, addr & ~__IA64_UNCACHED_OFFSET, len,
+ BTE_WACQUIRE | BTE_ZERO_FILL, NULL);
+ } else {
+ memset((char *) addr, 0, len);
+ status = 0;
+ }
+ return status;
+}
+
+/*
+ * mspec_open
+ *
+ * Called when a device mapping is created by a means other than mmap
+ * (via fork, etc.). Increments the reference count on the underlying
+ * mspec data so it is not freed prematurely.
+ */
+static void
+mspec_open(struct vm_area_struct *vma)
+{
+ struct vma_data *vdata;
+
+ vdata = vma->vm_private_data;
+ atomic_inc(&vdata->refcnt);
+}
+
+/*
+ * mspec_close
+ *
+ * Called when unmapping a device mapping. Frees all mspec pages
+ * belonging to the vma.
+ */
+static void
+mspec_close(struct vm_area_struct *vma)
+{
+ struct vma_data *vdata;
+ int i, pages, result, vdata_size;
+
+ vdata = vma->vm_private_data;
+ if (!atomic_dec_and_test(&vdata->refcnt))
+ return;
+
+ pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
+ for (i = 0; i < pages; i++) {
+ if (vdata->maddr[i] == 0)
+ continue;
+ /*
+ * Clear the page before sticking it back
+ * into the pool.
+ */
+ result = mspec_zero_block(vdata->maddr[i], PAGE_SIZE);
+ if (!result)
+ uncached_free_page(vdata->maddr[i]);
+ else
+ printk(KERN_WARNING "mspec_close(): "
+ "failed to zero page %i\n",
+ result);
+ }
+
+ if (vdata_size <= PAGE_SIZE)
+ kfree(vdata);
+ else
+ vfree(vdata);
+}
+
+
+/*
+ * mspec_nopfn
+ *
+ * Creates a mspec page and maps it to user space.
+ */
+static unsigned long
+mspec_nopfn(struct vm_area_struct *vma, unsigned long address)
+{
+ unsigned long paddr, maddr;
+ unsigned long pfn;
+ int index;
+ struct vma_data *vdata = vma->vm_private_data;
+
+ index = (address - vma->vm_start) >> PAGE_SHIFT;
+ maddr = (volatile unsigned long) vdata->maddr[index];
+ if (maddr == 0) {
+ maddr = uncached_alloc_page(numa_node_id());
+ if (maddr == 0)
+ return NOPFN_OOM;
+
+ spin_lock(&vdata->lock);
+ if (vdata->maddr[index] == 0) {
+ vdata->count++;
+ vdata->maddr[index] = maddr;
+ } else {
+ uncached_free_page(maddr);
+ maddr = vdata->maddr[index];
+ }
+ spin_unlock(&vdata->lock);
+ }
+
+ if (vdata->type == MSPEC_FETCHOP)
+ paddr = TO_AMO(maddr);
+ else
+ paddr = __pa(TO_CAC(maddr));
+
+ pfn = paddr >> PAGE_SHIFT;
+
+ return pfn;
+}
+
+static struct vm_operations_struct mspec_vm_ops = {
+ .open = mspec_open,
+ .close = mspec_close,
+ .nopfn = mspec_nopfn
+};
+
+/*
+ * mspec_mmap
+ *
+ * Called when mmaping the device. Initializes the vma with a fault handler
+ * and private data structure necessary to allocate, track, and free the
+ * underlying pages.
+ */
+static int
+mspec_mmap(struct file *file, struct vm_area_struct *vma, int type)
+{
+ struct vma_data *vdata;
+ int pages, vdata_size;
+
+ if (vma->vm_pgoff != 0)
+ return -EINVAL;
+
+ if ((vma->vm_flags & VM_SHARED) == 0)
+ return -EINVAL;
+
+ if ((vma->vm_flags & VM_WRITE) == 0)
+ return -EPERM;
+
+ pages = (vma->vm_end - vma->vm_start) >> PAGE_SHIFT;
+ vdata_size = sizeof(struct vma_data) + pages * sizeof(long);
+ if (vdata_size <= PAGE_SIZE)
+ vdata = kmalloc(vdata_size, GFP_KERNEL);
+ else
+ vdata = vmalloc(vdata_size);
+ if (!vdata)
+ return -ENOMEM;
+ memset(vdata, 0, vdata_size);
+
+ vdata->type = type;
+ spin_lock_init(&vdata->lock);
+ vdata->refcnt = ATOMIC_INIT(1);
+ vma->vm_private_data = vdata;
+
+ vma->vm_flags |= (VM_IO | VM_LOCKED | VM_RESERVED | VM_PFNMAP);
+ if (vdata->type == MSPEC_FETCHOP || vdata->type == MSPEC_UNCACHED)
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+ vma->vm_ops = &mspec_vm_ops;
+
+ return 0;
+}
+
+static int
+fetchop_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_FETCHOP);
+}
+
+static int
+cached_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_CACHED);
+}
+
+static int
+uncached_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ return mspec_mmap(file, vma, MSPEC_UNCACHED);
+}
+
+static struct file_operations fetchop_fops = {
+ .owner = THIS_MODULE,
+ .mmap = fetchop_mmap
+};
+
+static struct miscdevice fetchop_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "sgi_fetchop",
+ .fops = &fetchop_fops
+};
+
+static struct file_operations cached_fops = {
+ .owner = THIS_MODULE,
+ .mmap = cached_mmap
+};
+
+static struct miscdevice cached_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mspec_cached",
+ .fops = &cached_fops
+};
+
+static struct file_operations uncached_fops = {
+ .owner = THIS_MODULE,
+ .mmap = uncached_mmap
+};
+
+static struct miscdevice uncached_miscdev = {
+ .minor = MISC_DYNAMIC_MINOR,
+ .name = "mspec_uncached",
+ .fops = &uncached_fops
+};
+
+/*
+ * mspec_init
+ *
+ * Called at boot time to initialize the mspec facility.
+ */
+static int __init
+mspec_init(void)
+{
+ int ret;
+ int nid;
+
+ /*
+ * The fetchop device only works on SN2 hardware, uncached and cached
+ * memory drivers should both be valid on all ia64 hardware
+ */
+ if (ia64_platform_is("sn2")) {
+ is_sn2 = 1;
+ if (is_shub2()) {
+ ret = -ENOMEM;
+ for_each_online_node(nid) {
+ int actual_nid;
+ int nasid;
+ unsigned long phys;
+
+ scratch_page[nid] = uncached_alloc_page(nid);
+ if (scratch_page[nid] == 0)
+ goto free_scratch_pages;
+ phys = __pa(scratch_page[nid]);
+ nasid = get_node_number(phys);
+ actual_nid = nasid_to_cnodeid(nasid);
+ if (actual_nid != nid)
+ goto free_scratch_pages;
+ }
+ }
+
+ ret = misc_register(&fetchop_miscdev);
+ if (ret) {
+ printk(KERN_ERR
+ "%s: failed to register device %i\n",
+ FETCHOP_ID, ret);
+ goto free_scratch_pages;
+ }
+ }
+ ret = misc_register(&cached_miscdev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register device %i\n",
+ CACHED_ID, ret);
+ if (is_sn2)
+ misc_deregister(&fetchop_miscdev);
+ goto free_scratch_pages;
+ }
+ ret = misc_register(&uncached_miscdev);
+ if (ret) {
+ printk(KERN_ERR "%s: failed to register device %i\n",
+ UNCACHED_ID, ret);
+ misc_deregister(&cached_miscdev);
+ if (is_sn2)
+ misc_deregister(&fetchop_miscdev);
+ goto free_scratch_pages;
+ }
+
+ printk(KERN_INFO "%s %s initialized devices: %s %s %s\n",
+ MSPEC_BASENAME, REVISION, is_sn2 ? FETCHOP_ID : "",
+ CACHED_ID, UNCACHED_ID);
+
+ return 0;
+
+ free_scratch_pages:
+ for_each_node(nid) {
+ if (scratch_page[nid] != 0)
+ uncached_free_page(scratch_page[nid]);
+ }
+ return ret;
+}
+
+static void __exit
+mspec_exit(void)
+{
+ int nid;
+
+ misc_deregister(&uncached_miscdev);
+ misc_deregister(&cached_miscdev);
+ if (is_sn2) {
+ misc_deregister(&fetchop_miscdev);
+
+ for_each_node(nid) {
+ if (scratch_page[nid] != 0)
+ uncached_free_page(scratch_page[nid]);
+ }
+ }
+}
+
+module_init(mspec_init);
+module_exit(mspec_exit);
+
+MODULE_AUTHOR("Silicon Graphics, Inc. <linux-altix@sgi.com>");
+MODULE_DESCRIPTION("Driver for SGI SN special memory operations");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c
index 39a2e661ff5..8d14823b051 100644
--- a/drivers/char/mwave/mwavedd.c
+++ b/drivers/char/mwave/mwavedd.c
@@ -297,7 +297,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
" ipcnum %x, usIntCount %x\n",
ipcnum,
pDrvData->IPCs[ipcnum].usIntCount);
- if (ipcnum > ARRAY_SIZE(pDrvData->IPCs)) {
+ if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
PRINTK_ERROR(KERN_ERR_MWAVE
"mwavedd::mwave_ioctl:"
" IOCTL_MW_GET_IPC: Error:"
@@ -355,7 +355,7 @@ static int mwave_ioctl(struct inode *inode, struct file *file,
"mwavedd::mwave_ioctl IOCTL_MW_UNREGISTER_IPC"
" ipcnum %x\n",
ipcnum);
- if (ipcnum > ARRAY_SIZE(pDrvData->IPCs)) {
+ if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) {
PRINTK_ERROR(KERN_ERR_MWAVE
"mwavedd::mwave_ioctl:"
" IOCTL_MW_UNREGISTER_IPC:"
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 556abd3e0d0..8253fca8efd 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -453,7 +453,7 @@ static int CheckIsMoxaMust(int io)
/* above is modified by Victor Yu. 08-15-2002 */
-static struct tty_operations mxser_ops = {
+static const struct tty_operations mxser_ops = {
.open = mxser_open,
.close = mxser_close,
.write = mxser_write,
@@ -2554,71 +2554,7 @@ static int mxser_change_speed(struct mxser_struct *info, struct termios *old_ter
#define B921600 (B460800 +1)
#endif
if (mxser_set_baud_method[info->port] == 0) {
- switch (cflag & (CBAUD | CBAUDEX)) {
- case B921600:
- baud = 921600;
- break;
- case B460800:
- baud = 460800;
- break;
- case B230400:
- baud = 230400;
- break;
- case B115200:
- baud = 115200;
- break;
- case B57600:
- baud = 57600;
- break;
- case B38400:
- baud = 38400;
- break;
- case B19200:
- baud = 19200;
- break;
- case B9600:
- baud = 9600;
- break;
- case B4800:
- baud = 4800;
- break;
- case B2400:
- baud = 2400;
- break;
- case B1800:
- baud = 1800;
- break;
- case B1200:
- baud = 1200;
- break;
- case B600:
- baud = 600;
- break;
- case B300:
- baud = 300;
- break;
- case B200:
- baud = 200;
- break;
- case B150:
- baud = 150;
- break;
- case B134:
- baud = 134;
- break;
- case B110:
- baud = 110;
- break;
- case B75:
- baud = 75;
- break;
- case B50:
- baud = 50;
- break;
- default:
- baud = 0;
- break;
- }
+ baud = tty_get_baud_rate(info->tty);
mxser_set_baud(info, baud);
}
diff --git a/drivers/char/nwbutton.c b/drivers/char/nwbutton.c
index 7c57ebfa864..ea1aa7764f8 100644
--- a/drivers/char/nwbutton.c
+++ b/drivers/char/nwbutton.c
@@ -127,9 +127,8 @@ static void button_consume_callbacks (int bpcount)
static void button_sequence_finished (unsigned long parameters)
{
#ifdef CONFIG_NWBUTTON_REBOOT /* Reboot using button is enabled */
- if (button_press_count == reboot_count) {
- kill_proc (1, SIGINT, 1); /* Ask init to reboot us */
- }
+ if (button_press_count == reboot_count)
+ kill_cad_pid(SIGINT, 1); /* Ask init to reboot us */
#endif /* CONFIG_NWBUTTON_REBOOT */
button_consume_callbacks (button_press_count);
bcount = sprintf (button_output_buffer, "%d\n", button_press_count);
diff --git a/drivers/char/pc8736x_gpio.c b/drivers/char/pc8736x_gpio.c
index 84e5a68635f..ecfaf180e5b 100644
--- a/drivers/char/pc8736x_gpio.c
+++ b/drivers/char/pc8736x_gpio.c
@@ -188,16 +188,6 @@ static void pc8736x_gpio_set(unsigned minor, int 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;
diff --git a/drivers/char/pcmcia/synclink_cs.c b/drivers/char/pcmcia/synclink_cs.c
index 00f574cbb0d..dd845cbefe9 100644
--- a/drivers/char/pcmcia/synclink_cs.c
+++ b/drivers/char/pcmcia/synclink_cs.c
@@ -3010,7 +3010,7 @@ static struct pcmcia_driver mgslpc_driver = {
.resume = mgslpc_resume,
};
-static struct tty_operations mgslpc_ops = {
+static const struct tty_operations mgslpc_ops = {
.open = mgslpc_open,
.close = mgslpc_close,
.write = mgslpc_write,
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index 34dd4c38110..80d3eedd7f9 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -224,7 +224,7 @@ static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios)
tty->termios->c_cflag |= (CS8 | CREAD);
}
-static struct tty_operations pty_ops = {
+static const struct tty_operations pty_ops = {
.open = pty_open,
.close = pty_close,
.write = pty_write,
diff --git a/drivers/char/random.c b/drivers/char/random.c
index 4c3a5ca9d8f..07f47a0208a 100644
--- a/drivers/char/random.c
+++ b/drivers/char/random.c
@@ -655,6 +655,7 @@ void add_interrupt_randomness(int irq)
add_timer_randomness(irq_timer_state[irq], 0x100 + irq);
}
+#ifdef CONFIG_BLOCK
void add_disk_randomness(struct gendisk *disk)
{
if (!disk || !disk->random)
@@ -667,6 +668,7 @@ void add_disk_randomness(struct gendisk *disk)
}
EXPORT_SYMBOL(add_disk_randomness);
+#endif
#define EXTRACT_SIZE 10
@@ -887,8 +889,8 @@ static void init_std_data(struct entropy_store *r)
do_gettimeofday(&tv);
add_entropy_words(r, (__u32 *)&tv, sizeof(tv)/4);
- add_entropy_words(r, (__u32 *)&system_utsname,
- sizeof(system_utsname)/4);
+ add_entropy_words(r, (__u32 *)utsname(),
+ sizeof(*(utsname()))/4);
}
static int __init rand_initialize(void)
@@ -918,6 +920,7 @@ void rand_initialize_irq(int irq)
}
}
+#ifdef CONFIG_BLOCK
void rand_initialize_disk(struct gendisk *disk)
{
struct timer_rand_state *state;
@@ -932,6 +935,7 @@ void rand_initialize_disk(struct gendisk *disk)
disk->random = state;
}
}
+#endif
static ssize_t
random_read(struct file * file, char __user * buf, size_t nbytes, loff_t *ppos)
diff --git a/drivers/char/raw.c b/drivers/char/raw.c
index 579868af4a5..89b718e326e 100644
--- a/drivers/char/raw.c
+++ b/drivers/char/raw.c
@@ -238,39 +238,14 @@ out:
return err;
}
-static ssize_t raw_file_write(struct file *file, const char __user *buf,
- size_t count, loff_t *ppos)
-{
- struct iovec local_iov = {
- .iov_base = (char __user *)buf,
- .iov_len = count
- };
-
- return generic_file_write_nolock(file, &local_iov, 1, ppos);
-}
-
-static ssize_t raw_file_aio_write(struct kiocb *iocb, const char __user *buf,
- size_t count, loff_t pos)
-{
- struct iovec local_iov = {
- .iov_base = (char __user *)buf,
- .iov_len = count
- };
-
- return generic_file_aio_write_nolock(iocb, &local_iov, 1, &iocb->ki_pos);
-}
-
-
static const struct file_operations raw_fops = {
- .read = generic_file_read,
+ .read = do_sync_read,
.aio_read = generic_file_aio_read,
- .write = raw_file_write,
- .aio_write = raw_file_aio_write,
+ .write = do_sync_write,
+ .aio_write = generic_file_aio_write_nolock,
.open = raw_open,
.release= raw_release,
.ioctl = raw_ioctl,
- .readv = generic_file_readv,
- .writev = generic_file_writev,
.owner = THIS_MODULE,
};
@@ -288,31 +263,34 @@ static struct cdev raw_cdev = {
static int __init raw_init(void)
{
dev_t dev = MKDEV(RAW_MAJOR, 0);
+ int ret;
- if (register_chrdev_region(dev, MAX_RAW_MINORS, "raw"))
+ ret = register_chrdev_region(dev, MAX_RAW_MINORS, "raw");
+ if (ret)
goto error;
cdev_init(&raw_cdev, &raw_fops);
- if (cdev_add(&raw_cdev, dev, MAX_RAW_MINORS)) {
+ ret = cdev_add(&raw_cdev, dev, MAX_RAW_MINORS);
+ if (ret) {
kobject_put(&raw_cdev.kobj);
- unregister_chrdev_region(dev, MAX_RAW_MINORS);
- goto error;
+ goto error_region;
}
raw_class = class_create(THIS_MODULE, "raw");
if (IS_ERR(raw_class)) {
printk(KERN_ERR "Error creating raw class.\n");
cdev_del(&raw_cdev);
- unregister_chrdev_region(dev, MAX_RAW_MINORS);
- goto error;
+ ret = PTR_ERR(raw_class);
+ goto error_region;
}
class_device_create(raw_class, NULL, MKDEV(RAW_MAJOR, 0), NULL, "rawctl");
return 0;
+error_region:
+ unregister_chrdev_region(dev, MAX_RAW_MINORS);
error:
- printk(KERN_ERR "error register raw device\n");
- return 1;
+ return ret;
}
static void __exit raw_exit(void)
diff --git a/drivers/char/rio/rio_linux.c b/drivers/char/rio/rio_linux.c
index 3fa80aaf452..202a3b0945b 100644
--- a/drivers/char/rio/rio_linux.c
+++ b/drivers/char/rio/rio_linux.c
@@ -727,7 +727,7 @@ static struct vpd_prom *get_VPD_PROM(struct Host *hp)
return &vpdp;
}
-static struct tty_operations rio_ops = {
+static const struct tty_operations rio_ops = {
.open = riotopen,
.close = gs_close,
.write = gs_write,
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index f1c94f771af..214d850112f 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -675,26 +675,12 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port)
port->COR2 = 0;
port->MSVR = MSVR_RTS;
- baud = C_BAUD(tty);
-
- if (baud & CBAUDEX) {
- baud &= ~CBAUDEX;
- if (baud < 1 || baud > 2)
- port->tty->termios->c_cflag &= ~CBAUDEX;
- else
- baud += 15;
- }
- if (baud == 15) {
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- baud ++;
- if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- baud += 2;
- }
+ baud = tty_get_baud_rate(tty);
/* Select port on the board */
rc_out(bp, CD180_CAR, port_No(port));
- if (!baud_table[baud]) {
+ if (!baud) {
/* Drop DTR & exit */
bp->DTR |= (1u << port_No(port));
rc_out(bp, RC_DTR, bp->DTR);
@@ -710,7 +696,7 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port)
*/
/* Set baud rate for port */
- tmp = (((RC_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+ tmp = (((RC_OSCFREQ + baud/2) / baud +
CD180_TPC/2) / CD180_TPC);
rc_out(bp, CD180_RBPRH, (tmp >> 8) & 0xff);
@@ -718,7 +704,7 @@ static void rc_change_speed(struct riscom_board *bp, struct riscom_port *port)
rc_out(bp, CD180_RBPRL, tmp & 0xff);
rc_out(bp, CD180_TBPRL, tmp & 0xff);
- baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */
+ baud = (baud + 5) / 10; /* Estimated CPS */
/* Two timer ticks seems enough to wakeup something like SLIP driver */
tmp = ((baud + HZ/2) / HZ) * 2 - CD180_NFIFO;
@@ -1597,7 +1583,7 @@ static void do_softint(void *private_)
}
}
-static struct tty_operations riscom_ops = {
+static const struct tty_operations riscom_ops = {
.open = rc_open,
.close = rc_close,
.write = rc_write,
diff --git a/drivers/char/rocket.c b/drivers/char/rocket.c
index 0ac13188132..bac80056f7e 100644
--- a/drivers/char/rocket.c
+++ b/drivers/char/rocket.c
@@ -2334,7 +2334,7 @@ static int __init init_ISA(int i)
return (1);
}
-static struct tty_operations rocket_ops = {
+static const struct tty_operations rocket_ops = {
.open = rp_open,
.close = rp_close,
.write = rp_write,
diff --git a/drivers/char/rtc.c b/drivers/char/rtc.c
index ab6429b4a84..656f8c0ca52 100644
--- a/drivers/char/rtc.c
+++ b/drivers/char/rtc.c
@@ -1262,10 +1262,8 @@ void rtc_get_rtc_time(struct rtc_time *rtc_tm)
* Once the read clears, read the RTC time (again via ioctl). Easy.
*/
- while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100) {
- barrier();
+ while (rtc_is_updating() != 0 && jiffies - uip_watchdog < 2*HZ/100)
cpu_relax();
- }
/*
* Only the values that we read from the RTC are set. We leave
diff --git a/drivers/char/s3c2410-rtc.c b/drivers/char/s3c2410-rtc.c
deleted file mode 100644
index 5458ef1634e..00000000000
--- a/drivers/char/s3c2410-rtc.c
+++ /dev/null
@@ -1,591 +0,0 @@
-/* drivers/char/s3c2410_rtc.c
- *
- * Copyright (c) 2004 Simtec Electronics <linux@simtec.co.uk>
- * http://www.simtec.co.uk/products/SWLINUX/
- *
- * 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.
- *
- * S3C2410 Internal RTC Driver
- *
- * Changelog:
- * 08-Nov-2004 BJD Initial creation
- * 12-Nov-2004 BJD Added periodic IRQ and PM code
- * 22-Nov-2004 BJD Sign-test on alarm code to check for <0
- * 10-Mar-2005 LCVR Changed S3C2410_VA_RTC to S3C24XX_VA_RTC
-*/
-
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/string.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/rtc.h>
-#include <linux/bcd.h>
-#include <linux/clk.h>
-
-#include <asm/hardware.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/rtc.h>
-
-#include <asm/mach/time.h>
-
-#include <asm/arch/regs-rtc.h>
-
-/* need this for the RTC_AF definitions */
-#include <linux/mc146818rtc.h>
-
-#undef S3C24XX_VA_RTC
-#define S3C24XX_VA_RTC s3c2410_rtc_base
-
-static struct resource *s3c2410_rtc_mem;
-
-static void __iomem *s3c2410_rtc_base;
-static int s3c2410_rtc_alarmno = NO_IRQ;
-static int s3c2410_rtc_tickno = NO_IRQ;
-static int s3c2410_rtc_freq = 1;
-
-static DEFINE_SPINLOCK(s3c2410_rtc_pie_lock);
-
-/* IRQ Handlers */
-
-static irqreturn_t s3c2410_rtc_alarmirq(int irq, void *id, struct pt_regs *r)
-{
- rtc_update(1, RTC_AF | RTC_IRQF);
- return IRQ_HANDLED;
-}
-
-static irqreturn_t s3c2410_rtc_tickirq(int irq, void *id, struct pt_regs *r)
-{
- rtc_update(1, RTC_PF | RTC_IRQF);
- return IRQ_HANDLED;
-}
-
-/* Update control registers */
-static void s3c2410_rtc_setaie(int to)
-{
- unsigned int tmp;
-
- pr_debug("%s: aie=%d\n", __FUNCTION__, to);
-
- tmp = readb(S3C2410_RTCALM);
-
- if (to)
- tmp |= S3C2410_RTCALM_ALMEN;
- else
- tmp &= ~S3C2410_RTCALM_ALMEN;
-
-
- writeb(tmp, S3C2410_RTCALM);
-}
-
-static void s3c2410_rtc_setpie(int to)
-{
- unsigned int tmp;
-
- pr_debug("%s: pie=%d\n", __FUNCTION__, to);
-
- spin_lock_irq(&s3c2410_rtc_pie_lock);
- tmp = readb(S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
-
- if (to)
- tmp |= S3C2410_TICNT_ENABLE;
-
- writeb(tmp, S3C2410_TICNT);
- spin_unlock_irq(&s3c2410_rtc_pie_lock);
-}
-
-static void s3c2410_rtc_setfreq(int freq)
-{
- unsigned int tmp;
-
- spin_lock_irq(&s3c2410_rtc_pie_lock);
- tmp = readb(S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
-
- s3c2410_rtc_freq = freq;
-
- tmp |= (128 / freq)-1;
-
- writeb(tmp, S3C2410_TICNT);
- spin_unlock_irq(&s3c2410_rtc_pie_lock);
-}
-
-/* Time read/write */
-
-static int s3c2410_rtc_gettime(struct rtc_time *rtc_tm)
-{
- unsigned int have_retried = 0;
-
- retry_get_time:
- rtc_tm->tm_min = readb(S3C2410_RTCMIN);
- rtc_tm->tm_hour = readb(S3C2410_RTCHOUR);
- rtc_tm->tm_mday = readb(S3C2410_RTCDATE);
- rtc_tm->tm_mon = readb(S3C2410_RTCMON);
- rtc_tm->tm_year = readb(S3C2410_RTCYEAR);
- rtc_tm->tm_sec = readb(S3C2410_RTCSEC);
-
- /* the only way to work out wether the system was mid-update
- * when we read it is to check the second counter, and if it
- * is zero, then we re-try the entire read
- */
-
- if (rtc_tm->tm_sec == 0 && !have_retried) {
- have_retried = 1;
- goto retry_get_time;
- }
-
- pr_debug("read time %02x.%02x.%02x %02x/%02x/%02x\n",
- rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
- rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
-
- BCD_TO_BIN(rtc_tm->tm_sec);
- BCD_TO_BIN(rtc_tm->tm_min);
- BCD_TO_BIN(rtc_tm->tm_hour);
- BCD_TO_BIN(rtc_tm->tm_mday);
- BCD_TO_BIN(rtc_tm->tm_mon);
- BCD_TO_BIN(rtc_tm->tm_year);
-
- rtc_tm->tm_year += 100;
- rtc_tm->tm_mon -= 1;
-
- return 0;
-}
-
-
-static int s3c2410_rtc_settime(struct rtc_time *tm)
-{
- /* the rtc gets round the y2k problem by just not supporting it */
-
- if (tm->tm_year < 100)
- return -EINVAL;
-
- writeb(BIN2BCD(tm->tm_sec), S3C2410_RTCSEC);
- writeb(BIN2BCD(tm->tm_min), S3C2410_RTCMIN);
- writeb(BIN2BCD(tm->tm_hour), S3C2410_RTCHOUR);
- writeb(BIN2BCD(tm->tm_mday), S3C2410_RTCDATE);
- writeb(BIN2BCD(tm->tm_mon + 1), S3C2410_RTCMON);
- writeb(BIN2BCD(tm->tm_year - 100), S3C2410_RTCYEAR);
-
- return 0;
-}
-
-static int s3c2410_rtc_getalarm(struct rtc_wkalrm *alrm)
-{
- struct rtc_time *alm_tm = &alrm->time;
- unsigned int alm_en;
-
- alm_tm->tm_sec = readb(S3C2410_ALMSEC);
- alm_tm->tm_min = readb(S3C2410_ALMMIN);
- alm_tm->tm_hour = readb(S3C2410_ALMHOUR);
- alm_tm->tm_mon = readb(S3C2410_ALMMON);
- alm_tm->tm_mday = readb(S3C2410_ALMDATE);
- alm_tm->tm_year = readb(S3C2410_ALMYEAR);
-
- alm_en = readb(S3C2410_RTCALM);
-
- pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
- alm_en,
- alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
- alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
-
-
- /* decode the alarm enable field */
-
- if (alm_en & S3C2410_RTCALM_SECEN) {
- BCD_TO_BIN(alm_tm->tm_sec);
- } else {
- alm_tm->tm_sec = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_MINEN) {
- BCD_TO_BIN(alm_tm->tm_min);
- } else {
- alm_tm->tm_min = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_HOUREN) {
- BCD_TO_BIN(alm_tm->tm_hour);
- } else {
- alm_tm->tm_hour = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_DAYEN) {
- BCD_TO_BIN(alm_tm->tm_mday);
- } else {
- alm_tm->tm_mday = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_MONEN) {
- BCD_TO_BIN(alm_tm->tm_mon);
- alm_tm->tm_mon -= 1;
- } else {
- alm_tm->tm_mon = 0xff;
- }
-
- if (alm_en & S3C2410_RTCALM_YEAREN) {
- BCD_TO_BIN(alm_tm->tm_year);
- } else {
- alm_tm->tm_year = 0xffff;
- }
-
- /* todo - set alrm->enabled ? */
-
- return 0;
-}
-
-static int s3c2410_rtc_setalarm(struct rtc_wkalrm *alrm)
-{
- struct rtc_time *tm = &alrm->time;
- unsigned int alrm_en;
-
- pr_debug("s3c2410_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
- alrm->enabled,
- tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
- tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
-
- if (alrm->enabled || 1) {
- alrm_en = readb(S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
- writeb(0x00, S3C2410_RTCALM);
-
- if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
- alrm_en |= S3C2410_RTCALM_SECEN;
- writeb(BIN2BCD(tm->tm_sec), S3C2410_ALMSEC);
- }
-
- if (tm->tm_min < 60 && tm->tm_min >= 0) {
- alrm_en |= S3C2410_RTCALM_MINEN;
- writeb(BIN2BCD(tm->tm_min), S3C2410_ALMMIN);
- }
-
- if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
- alrm_en |= S3C2410_RTCALM_HOUREN;
- writeb(BIN2BCD(tm->tm_hour), S3C2410_ALMHOUR);
- }
-
- pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
-
- writeb(alrm_en, S3C2410_RTCALM);
- enable_irq_wake(s3c2410_rtc_alarmno);
- } else {
- alrm_en = readb(S3C2410_RTCALM);
- alrm_en &= ~S3C2410_RTCALM_ALMEN;
- writeb(alrm_en, S3C2410_RTCALM);
- disable_irq_wake(s3c2410_rtc_alarmno);
- }
-
- return 0;
-}
-
-static int s3c2410_rtc_ioctl(unsigned int cmd, unsigned long arg)
-{
- switch (cmd) {
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- s3c2410_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
- return 0;
-
- case RTC_PIE_OFF:
- case RTC_PIE_ON:
- s3c2410_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
- return 0;
-
- case RTC_IRQP_READ:
- return put_user(s3c2410_rtc_freq, (unsigned long __user *)arg);
-
- case RTC_IRQP_SET:
- if (arg < 1 || arg > 64)
- return -EINVAL;
-
- if (!capable(CAP_SYS_RESOURCE))
- return -EACCES;
-
- /* check for power of 2 */
-
- if ((arg & (arg-1)) != 0)
- return -EINVAL;
-
- pr_debug("s3c2410_rtc: setting frequency %ld\n", arg);
-
- s3c2410_rtc_setfreq(arg);
- return 0;
-
- case RTC_UIE_ON:
- case RTC_UIE_OFF:
- return -EINVAL;
- }
-
- return -EINVAL;
-}
-
-static int s3c2410_rtc_proc(char *buf)
-{
- unsigned int rtcalm = readb(S3C2410_RTCALM);
- unsigned int ticnt = readb (S3C2410_TICNT);
- char *p = buf;
-
- p += sprintf(p, "alarm_IRQ\t: %s\n",
- (rtcalm & S3C2410_RTCALM_ALMEN) ? "yes" : "no" );
- p += sprintf(p, "periodic_IRQ\t: %s\n",
- (ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
- p += sprintf(p, "periodic_freq\t: %d\n", s3c2410_rtc_freq);
-
- return p - buf;
-}
-
-static int s3c2410_rtc_open(void)
-{
- int ret;
-
- ret = request_irq(s3c2410_rtc_alarmno, s3c2410_rtc_alarmirq,
- IRQF_DISABLED, "s3c2410-rtc alarm", NULL);
-
- if (ret)
- printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_alarmno);
-
- ret = request_irq(s3c2410_rtc_tickno, s3c2410_rtc_tickirq,
- IRQF_DISABLED, "s3c2410-rtc tick", NULL);
-
- if (ret) {
- printk(KERN_ERR "IRQ%d already in use\n", s3c2410_rtc_tickno);
- goto tick_err;
- }
-
- return ret;
-
- tick_err:
- free_irq(s3c2410_rtc_alarmno, NULL);
- return ret;
-}
-
-static void s3c2410_rtc_release(void)
-{
- /* do not clear AIE here, it may be needed for wake */
-
- s3c2410_rtc_setpie(0);
- free_irq(s3c2410_rtc_alarmno, NULL);
- free_irq(s3c2410_rtc_tickno, NULL);
-}
-
-static struct rtc_ops s3c2410_rtcops = {
- .owner = THIS_MODULE,
- .open = s3c2410_rtc_open,
- .release = s3c2410_rtc_release,
- .ioctl = s3c2410_rtc_ioctl,
- .read_time = s3c2410_rtc_gettime,
- .set_time = s3c2410_rtc_settime,
- .read_alarm = s3c2410_rtc_getalarm,
- .set_alarm = s3c2410_rtc_setalarm,
- .proc = s3c2410_rtc_proc,
-};
-
-static void s3c2410_rtc_enable(struct platform_device *pdev, int en)
-{
- unsigned int tmp;
-
- if (s3c2410_rtc_base == NULL)
- return;
-
- if (!en) {
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_RTCEN, S3C2410_RTCCON);
-
- tmp = readb(S3C2410_TICNT);
- writeb(tmp & ~S3C2410_TICNT_ENABLE, S3C2410_TICNT);
- } else {
- /* re-enable the device, and check it is ok */
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
- dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp | S3C2410_RTCCON_RTCEN , S3C2410_RTCCON);
- }
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
- dev_info(&pdev->dev, "removing S3C2410_RTCCON_CNTSEL\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp& ~S3C2410_RTCCON_CNTSEL , S3C2410_RTCCON);
- }
-
- if ((readb(S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
- dev_info(&pdev->dev, "removing S3C2410_RTCCON_CLKRST\n");
-
- tmp = readb(S3C2410_RTCCON);
- writeb(tmp & ~S3C2410_RTCCON_CLKRST, S3C2410_RTCCON);
- }
- }
-}
-
-static int s3c2410_rtc_remove(struct platform_device *dev)
-{
- unregister_rtc(&s3c2410_rtcops);
-
- s3c2410_rtc_setpie(0);
- s3c2410_rtc_setaie(0);
-
- if (s3c2410_rtc_mem != NULL) {
- pr_debug("s3c2410_rtc: releasing s3c2410_rtc_mem\n");
- iounmap(s3c2410_rtc_base);
- release_resource(s3c2410_rtc_mem);
- kfree(s3c2410_rtc_mem);
- }
-
- return 0;
-}
-
-static int s3c2410_rtc_probe(struct platform_device *pdev)
-{
- struct resource *res;
- int ret;
-
- pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);
-
- /* find the IRQs */
-
- s3c2410_rtc_tickno = platform_get_irq(pdev, 1);
- if (s3c2410_rtc_tickno < 0) {
- dev_err(&pdev->dev, "no irq for rtc tick\n");
- return -ENOENT;
- }
-
- s3c2410_rtc_alarmno = platform_get_irq(pdev, 0);
- if (s3c2410_rtc_alarmno < 0) {
- dev_err(&pdev->dev, "no irq for alarm\n");
- return -ENOENT;
- }
-
- pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
- s3c2410_rtc_tickno, s3c2410_rtc_alarmno);
-
- /* get the memory region */
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (res == NULL) {
- dev_err(&pdev->dev, "failed to get memory region resource\n");
- return -ENOENT;
- }
-
- s3c2410_rtc_mem = request_mem_region(res->start, res->end-res->start+1,
- pdev->name);
-
- if (s3c2410_rtc_mem == NULL) {
- dev_err(&pdev->dev, "failed to reserve memory region\n");
- ret = -ENOENT;
- goto exit_err;
- }
-
- s3c2410_rtc_base = ioremap(res->start, res->end - res->start + 1);
- if (s3c2410_rtc_base == NULL) {
- dev_err(&pdev->dev, "failed ioremap()\n");
- ret = -EINVAL;
- goto exit_err;
- }
-
- s3c2410_rtc_mem = res;
- pr_debug("s3c2410_rtc_base=%p\n", s3c2410_rtc_base);
-
- pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
-
- /* check to see if everything is setup correctly */
-
- s3c2410_rtc_enable(pdev, 1);
-
- pr_debug("s3c2410_rtc: RTCCON=%02x\n", readb(S3C2410_RTCCON));
-
- s3c2410_rtc_setfreq(s3c2410_rtc_freq);
-
- /* register RTC and exit */
-
- register_rtc(&s3c2410_rtcops);
- return 0;
-
- exit_err:
- dev_err(&pdev->dev, "error %d during initialisation\n", ret);
-
- return ret;
-}
-
-#ifdef CONFIG_PM
-
-/* S3C2410 RTC Power management control */
-
-static struct timespec s3c2410_rtc_delta;
-
-static int ticnt_save;
-
-static int s3c2410_rtc_suspend(struct platform_device *pdev, pm_message_t state)
-{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- /* save TICNT for anyone using periodic interrupts */
-
- ticnt_save = readb(S3C2410_TICNT);
-
- /* calculate time delta for suspend */
-
- s3c2410_rtc_gettime(&tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- save_time_delta(&s3c2410_rtc_delta, &time);
- s3c2410_rtc_enable(pdev, 0);
-
- return 0;
-}
-
-static int s3c2410_rtc_resume(struct platform_device *pdev)
-{
- struct rtc_time tm;
- struct timespec time;
-
- time.tv_nsec = 0;
-
- s3c2410_rtc_enable(pdev, 1);
- s3c2410_rtc_gettime(&tm);
- rtc_tm_to_time(&tm, &time.tv_sec);
- restore_time_delta(&s3c2410_rtc_delta, &time);
-
- writeb(ticnt_save, S3C2410_TICNT);
- return 0;
-}
-#else
-#define s3c2410_rtc_suspend NULL
-#define s3c2410_rtc_resume NULL
-#endif
-
-static struct platform_driver s3c2410_rtcdrv = {
- .probe = s3c2410_rtc_probe,
- .remove = s3c2410_rtc_remove,
- .suspend = s3c2410_rtc_suspend,
- .resume = s3c2410_rtc_resume,
- .driver = {
- .name = "s3c2410-rtc",
- .owner = THIS_MODULE,
- },
-};
-
-static char __initdata banner[] = "S3C2410 RTC, (c) 2004 Simtec Electronics\n";
-
-static int __init s3c2410_rtc_init(void)
-{
- printk(banner);
- return platform_driver_register(&s3c2410_rtcdrv);
-}
-
-static void __exit s3c2410_rtc_exit(void)
-{
- platform_driver_unregister(&s3c2410_rtcdrv);
-}
-
-module_init(s3c2410_rtc_init);
-module_exit(s3c2410_rtc_exit);
-
-MODULE_DESCRIPTION("S3C24XX RTC Driver");
-MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/scx200_gpio.c b/drivers/char/scx200_gpio.c
index b956c7babd1..99e5272e3c5 100644
--- a/drivers/char/scx200_gpio.c
+++ b/drivers/char/scx200_gpio.c
@@ -44,7 +44,7 @@ struct nsc_gpio_ops scx200_gpio_ops = {
.gpio_change = scx200_gpio_change,
.gpio_current = scx200_gpio_current
};
-EXPORT_SYMBOL(scx200_gpio_ops);
+EXPORT_SYMBOL_GPL(scx200_gpio_ops);
static int scx200_gpio_open(struct inode *inode, struct file *file)
{
@@ -69,7 +69,7 @@ static const struct file_operations scx200_gpio_fileops = {
.release = scx200_gpio_release,
};
-struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
+static struct cdev scx200_gpio_cdev; /* use 1 cdev for all pins */
static int __init scx200_gpio_init(void)
{
diff --git a/drivers/char/selection.c b/drivers/char/selection.c
index 71093a9fc46..74cff839c85 100644
--- a/drivers/char/selection.c
+++ b/drivers/char/selection.c
@@ -33,7 +33,7 @@ extern void poke_blanked_console(void);
/* Variables for selection control. */
/* Use a dynamic buffer, instead of static (Dec 1994) */
-struct vc_data *sel_cons; /* must not be disallocated */
+struct vc_data *sel_cons; /* must not be deallocated */
static volatile int sel_start = -1; /* cleared by clear_selection */
static int sel_end;
static int sel_buffer_lth;
diff --git a/drivers/char/ser_a2232.c b/drivers/char/ser_a2232.c
index 510bd3e0e88..65c751d0d64 100644
--- a/drivers/char/ser_a2232.c
+++ b/drivers/char/ser_a2232.c
@@ -661,7 +661,7 @@ static void a2232_init_portstructs(void)
}
}
-static struct tty_operations a2232_ops = {
+static const struct tty_operations a2232_ops = {
.open = a2232_open,
.close = gs_close,
.write = gs_write,
diff --git a/drivers/char/serial167.c b/drivers/char/serial167.c
index 21a710cb4bb..b4ea1266b66 100644
--- a/drivers/char/serial167.c
+++ b/drivers/char/serial167.c
@@ -2158,7 +2158,7 @@ mvme167_serial_console_setup(int cflag)
rcor >> 5, rbpr);
} /* serial_console_init */
-static struct tty_operations cy_ops = {
+static const struct tty_operations cy_ops = {
.open = cy_open,
.close = cy_close,
.write = cy_write,
diff --git a/drivers/char/snsc_event.c b/drivers/char/snsc_event.c
index d12d4f629ce..864854c5886 100644
--- a/drivers/char/snsc_event.c
+++ b/drivers/char/snsc_event.c
@@ -220,7 +220,7 @@ scdrv_dispatch_event(char *event, int len)
" Sending SIGPWR to init...\n");
/* give a SIGPWR signal to init proc */
- kill_proc(1, SIGPWR, 0);
+ kill_cad_pid(SIGPWR, 0);
} else {
/* print to system log */
printk("%s|$(0x%x)%s\n", severity, esp_code, desc);
diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c
index a1d303f9a33..902c48dca3b 100644
--- a/drivers/char/specialix.c
+++ b/drivers/char/specialix.c
@@ -182,7 +182,6 @@ static int sx_poll = HZ;
#define RS_EVENT_WRITE_WAKEUP 0
static struct tty_driver *specialix_driver;
-static unsigned char * tmp_buf;
static unsigned long baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
@@ -1087,24 +1086,16 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p
port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);
spin_unlock_irqrestore(&bp->lock, flags);
dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR);
- baud = C_BAUD(tty);
+ baud = tty_get_baud_rate(tty);
- if (baud & CBAUDEX) {
- baud &= ~CBAUDEX;
- if (baud < 1 || baud > 2)
- port->tty->termios->c_cflag &= ~CBAUDEX;
- else
- baud += 15;
- }
- if (baud == 15) {
+ if (baud == 38400) {
if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
baud ++;
if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
baud += 2;
}
-
- if (!baud_table[baud]) {
+ if (!baud) {
/* Drop DTR & exit */
dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n");
if (!SX_CRTSCTS (tty)) {
@@ -1134,7 +1125,7 @@ static void sx_change_speed(struct specialix_board *bp, struct specialix_port *p
"This is an untested option, please be carefull.\n",
port_No (port), tmp);
else
- tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] +
+ tmp = (((SX_OSCFREQ + baud/2) / baud +
CD186x_TPC/2) / CD186x_TPC);
if ((tmp < 0x10) && time_before(again, jiffies)) {
@@ -1682,7 +1673,7 @@ static int sx_write(struct tty_struct * tty,
bp = port_Board(port);
- if (!port->xmit_buf || !tmp_buf) {
+ if (!port->xmit_buf) {
func_exit();
return 0;
}
@@ -2372,7 +2363,7 @@ static void do_softint(void *private_)
func_exit();
}
-static struct tty_operations sx_ops = {
+static const struct tty_operations sx_ops = {
.open = sx_open,
.close = sx_close,
.write = sx_write,
@@ -2406,12 +2397,6 @@ static int sx_init_drivers(void)
return 1;
}
- if (!(tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL))) {
- printk(KERN_ERR "sx: Couldn't get free page.\n");
- put_tty_driver(specialix_driver);
- func_exit();
- return 1;
- }
specialix_driver->owner = THIS_MODULE;
specialix_driver->name = "ttyW";
specialix_driver->major = SPECIALIX_NORMAL_MAJOR;
@@ -2425,7 +2410,6 @@ static int sx_init_drivers(void)
if ((error = tty_register_driver(specialix_driver))) {
put_tty_driver(specialix_driver);
- free_page((unsigned long)tmp_buf);
printk(KERN_ERR "sx: Couldn't register specialix IO8+ driver, error = %d\n",
error);
func_exit();
@@ -2451,7 +2435,6 @@ static void sx_release_drivers(void)
{
func_enter();
- free_page((unsigned long)tmp_buf);
tty_unregister_driver(specialix_driver);
put_tty_driver(specialix_driver);
func_exit();
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index 3beb2203d24..bd711537ec4 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -2993,7 +2993,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns
return(rc);
}
-static struct tty_operations stl_ops = {
+static const struct tty_operations stl_ops = {
.open = stl_open,
.close = stl_close,
.write = stl_write,
diff --git a/drivers/char/sx.c b/drivers/char/sx.c
index e1cd2bc4b1e..57e31e5eaed 100644
--- a/drivers/char/sx.c
+++ b/drivers/char/sx.c
@@ -2226,7 +2226,7 @@ static int probe_si (struct sx_board *board)
return 1;
}
-static struct tty_operations sx_ops = {
+static const struct tty_operations sx_ops = {
.break_ctl = sx_break,
.open = sx_open,
.close = gs_close,
diff --git a/drivers/char/synclink.c b/drivers/char/synclink.c
index 78b1b1a2732..244dc308c77 100644
--- a/drivers/char/synclink.c
+++ b/drivers/char/synclink.c
@@ -4360,7 +4360,7 @@ static struct mgsl_struct* mgsl_allocate_device(void)
} /* end of mgsl_allocate_device()*/
-static struct tty_operations mgsl_ops = {
+static const struct tty_operations mgsl_ops = {
.open = mgsl_open,
.close = mgsl_close,
.write = mgsl_write,
diff --git a/drivers/char/synclink_gt.c b/drivers/char/synclink_gt.c
index 2f07b085536..bdc7cb248b8 100644
--- a/drivers/char/synclink_gt.c
+++ b/drivers/char/synclink_gt.c
@@ -1,5 +1,5 @@
/*
- * $Id: synclink_gt.c,v 4.25 2006/02/06 21:20:33 paulkf Exp $
+ * $Id: synclink_gt.c,v 4.36 2006/08/28 20:47:14 paulkf Exp $
*
* Device driver for Microgate SyncLink GT serial adapters.
*
@@ -91,12 +91,12 @@
* module identification
*/
static char *driver_name = "SyncLink GT";
-static char *driver_version = "$Revision: 4.25 $";
+static char *driver_version = "$Revision: 4.36 $";
static char *tty_driver_name = "synclink_gt";
static char *tty_dev_prefix = "ttySLG";
MODULE_LICENSE("GPL");
#define MGSL_MAGIC 0x5401
-#define MAX_DEVICES 12
+#define MAX_DEVICES 32
static struct pci_device_id pci_table[] = {
{PCI_VENDOR_ID_MICROGATE, SYNCLINK_GT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,},
@@ -461,7 +461,7 @@ static int adapter_test(struct slgt_info *info);
static void reset_adapter(struct slgt_info *info);
static void reset_port(struct slgt_info *info);
static void async_mode(struct slgt_info *info);
-static void hdlc_mode(struct slgt_info *info);
+static void sync_mode(struct slgt_info *info);
static void rx_stop(struct slgt_info *info);
static void rx_start(struct slgt_info *info);
@@ -881,7 +881,9 @@ static int write(struct tty_struct *tty,
if (!count)
goto cleanup;
- if (info->params.mode == MGSL_MODE_RAW) {
+ if (info->params.mode == MGSL_MODE_RAW ||
+ info->params.mode == MGSL_MODE_MONOSYNC ||
+ info->params.mode == MGSL_MODE_BISYNC) {
unsigned int bufs_needed = (count/DMABUFSIZE);
unsigned int bufs_free = free_tbuf_count(info);
if (count % DMABUFSIZE)
@@ -1897,6 +1899,8 @@ static void bh_handler(void* context)
while(rx_get_frame(info));
break;
case MGSL_MODE_RAW:
+ case MGSL_MODE_MONOSYNC:
+ case MGSL_MODE_BISYNC:
while(rx_get_buf(info));
break;
}
@@ -2362,10 +2366,9 @@ static void program_hw(struct slgt_info *info)
rx_stop(info);
tx_stop(info);
- if (info->params.mode == MGSL_MODE_HDLC ||
- info->params.mode == MGSL_MODE_RAW ||
+ if (info->params.mode != MGSL_MODE_ASYNC ||
info->netcount)
- hdlc_mode(info);
+ sync_mode(info);
else
async_mode(info);
@@ -2564,6 +2567,10 @@ static int rx_enable(struct slgt_info *info, int enable)
if (enable) {
if (!info->rx_enabled)
rx_start(info);
+ else if (enable == 2) {
+ /* force hunt mode (write 1 to RCR[3]) */
+ wr_reg16(info, RCR, rd_reg16(info, RCR) | BIT3);
+ }
} else {
if (info->rx_enabled)
rx_stop(info);
@@ -3434,7 +3441,7 @@ static void __devexit remove_one(struct pci_dev *dev)
{
}
-static struct tty_operations ops = {
+static const struct tty_operations ops = {
.open = open,
.close = close,
.write = write,
@@ -3748,7 +3755,7 @@ static void tx_start(struct slgt_info *info)
{
if (!info->tx_enabled) {
wr_reg16(info, TCR,
- (unsigned short)(rd_reg16(info, TCR) | BIT1));
+ (unsigned short)((rd_reg16(info, TCR) | BIT1) & ~BIT2));
info->tx_enabled = TRUE;
}
@@ -3775,13 +3782,18 @@ static void tx_start(struct slgt_info *info)
tdma_reset(info);
/* set 1st descriptor address */
wr_reg32(info, TDDAR, info->tbufs[info->tbuf_start].pdesc);
- if (info->params.mode == MGSL_MODE_RAW)
+ switch(info->params.mode) {
+ case MGSL_MODE_RAW:
+ case MGSL_MODE_MONOSYNC:
+ case MGSL_MODE_BISYNC:
wr_reg32(info, TDCSR, BIT2 + BIT0); /* IRQ + DMA enable */
- else
+ break;
+ default:
wr_reg32(info, TDCSR, BIT0); /* DMA enable */
+ }
}
- if (info->params.mode != MGSL_MODE_RAW) {
+ if (info->params.mode == MGSL_MODE_HDLC) {
info->tx_timer.expires = jiffies + msecs_to_jiffies(5000);
add_timer(&info->tx_timer);
}
@@ -3814,7 +3826,6 @@ static void tx_stop(struct slgt_info *info)
/* reset and disable transmitter */
val = rd_reg16(info, TCR) & ~BIT1; /* clear enable bit */
wr_reg16(info, TCR, (unsigned short)(val | BIT2)); /* set reset bit */
- wr_reg16(info, TCR, val); /* clear reset */
slgt_irq_off(info, IRQ_TXDATA + IRQ_TXIDLE + IRQ_TXUNDER);
@@ -3982,7 +3993,7 @@ static void async_mode(struct slgt_info *info)
enable_loopback(info);
}
-static void hdlc_mode(struct slgt_info *info)
+static void sync_mode(struct slgt_info *info)
{
unsigned short val;
@@ -3992,7 +4003,7 @@ static void hdlc_mode(struct slgt_info *info)
/* TCR (tx control)
*
- * 15..13 mode, 000=HDLC 001=raw sync
+ * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync
* 12..10 encoding
* 09 CRC enable
* 08 CRC32
@@ -4006,8 +4017,11 @@ static void hdlc_mode(struct slgt_info *info)
*/
val = 0;
- if (info->params.mode == MGSL_MODE_RAW)
- val |= BIT13;
+ switch(info->params.mode) {
+ case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
+ case MGSL_MODE_BISYNC: val |= BIT15; break;
+ case MGSL_MODE_RAW: val |= BIT13; break;
+ }
if (info->if_mode & MGSL_INTERFACE_RTS_EN)
val |= BIT7;
@@ -4058,7 +4072,7 @@ static void hdlc_mode(struct slgt_info *info)
/* RCR (rx control)
*
- * 15..13 mode, 000=HDLC 001=raw sync
+ * 15..13 mode, 000=HDLC 001=raw 010=async 011=monosync 100=bisync
* 12..10 encoding
* 09 CRC enable
* 08 CRC32
@@ -4069,8 +4083,11 @@ static void hdlc_mode(struct slgt_info *info)
*/
val = 0;
- if (info->params.mode == MGSL_MODE_RAW)
- val |= BIT13;
+ switch(info->params.mode) {
+ case MGSL_MODE_MONOSYNC: val |= BIT14 + BIT13; break;
+ case MGSL_MODE_BISYNC: val |= BIT15; break;
+ case MGSL_MODE_RAW: val |= BIT13; break;
+ }
switch(info->params.encoding)
{
@@ -4309,10 +4326,15 @@ static void free_rbufs(struct slgt_info *info, unsigned int i, unsigned int last
while(!done) {
/* reset current buffer for reuse */
info->rbufs[i].status = 0;
- if (info->params.mode == MGSL_MODE_RAW)
+ switch(info->params.mode) {
+ case MGSL_MODE_RAW:
+ case MGSL_MODE_MONOSYNC:
+ case MGSL_MODE_BISYNC:
set_desc_count(info->rbufs[i], info->raw_rx_size);
- else
+ break;
+ default:
set_desc_count(info->rbufs[i], DMABUFSIZE);
+ }
if (i == last)
done = 1;
@@ -4477,13 +4499,24 @@ cleanup:
static int rx_get_buf(struct slgt_info *info)
{
unsigned int i = info->rbuf_current;
+ unsigned int count;
if (!desc_complete(info->rbufs[i]))
return 0;
- DBGDATA(info, info->rbufs[i].buf, desc_count(info->rbufs[i]), "rx");
- DBGINFO(("rx_get_buf size=%d\n", desc_count(info->rbufs[i])));
- ldisc_receive_buf(info->tty, info->rbufs[i].buf,
- info->flag_buf, desc_count(info->rbufs[i]));
+ count = desc_count(info->rbufs[i]);
+ switch(info->params.mode) {
+ case MGSL_MODE_MONOSYNC:
+ case MGSL_MODE_BISYNC:
+ /* ignore residue in byte synchronous modes */
+ if (desc_residue(info->rbufs[i]))
+ count--;
+ break;
+ }
+ DBGDATA(info, info->rbufs[i].buf, count, "rx");
+ DBGINFO(("rx_get_buf size=%d\n", count));
+ if (count)
+ ldisc_receive_buf(info->tty, info->rbufs[i].buf,
+ info->flag_buf, count);
free_rbufs(info, i, i);
return 1;
}
@@ -4549,8 +4582,13 @@ static void tx_load(struct slgt_info *info, const char *buf, unsigned int size)
size -= count;
buf += count;
- if (!size && info->params.mode != MGSL_MODE_RAW)
- set_desc_eof(*d, 1); /* HDLC: set EOF of last desc */
+ /*
+ * set EOF bit for last buffer of HDLC frame or
+ * for every buffer in raw mode
+ */
+ if ((!size && info->params.mode == MGSL_MODE_HDLC) ||
+ info->params.mode == MGSL_MODE_RAW)
+ set_desc_eof(*d, 1);
else
set_desc_eof(*d, 0);
diff --git a/drivers/char/synclinkmp.c b/drivers/char/synclinkmp.c
index 66f3754fbbd..6eb75dcd796 100644
--- a/drivers/char/synclinkmp.c
+++ b/drivers/char/synclinkmp.c
@@ -3929,7 +3929,7 @@ void device_init(int adapter_num, struct pci_dev *pdev)
}
}
-static struct tty_operations ops = {
+static const struct tty_operations ops = {
.open = open,
.close = close,
.write = write,
diff --git a/drivers/char/sysrq.c b/drivers/char/sysrq.c
index ee3ca8f1768..6b4d4d1e343 100644
--- a/drivers/char/sysrq.c
+++ b/drivers/char/sysrq.c
@@ -113,6 +113,7 @@ static struct sysrq_key_op sysrq_crashdump_op = {
static void sysrq_handle_reboot(int key, struct pt_regs *pt_regs,
struct tty_struct *tty)
{
+ lockdep_off();
local_irq_enable();
emergency_restart();
}
@@ -208,7 +209,7 @@ static void send_sig_all(int sig)
struct task_struct *p;
for_each_process(p) {
- if (p->mm && p->pid != 1)
+ if (p->mm && !is_init(p))
/* Not swapper, init nor kernel thread */
force_sig(sig, p);
}
diff --git a/drivers/char/tipar.c b/drivers/char/tipar.c
index d30dc09dbbc..9df0ca1be0e 100644
--- a/drivers/char/tipar.c
+++ b/drivers/char/tipar.c
@@ -224,14 +224,16 @@ probe_ti_parallel(int minor)
{
int i;
int seq[] = { 0x00, 0x20, 0x10, 0x30 };
+ int data;
for (i = 3; i >= 0; i--) {
outbyte(3, minor);
outbyte(i, minor);
udelay(delay);
+ data = inbyte(minor) & 0x30;
pr_debug("tipar: Probing -> %i: 0x%02x 0x%02x\n", i,
- data & 0x30, seq[i]);
- if ((inbyte(minor) & 0x30) != seq[i]) {
+ data, seq[i]);
+ if (data != seq[i]) {
outbyte(3, minor);
return -1;
}
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index bb0d9199e99..e90ea39c7c4 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -129,6 +129,7 @@ LIST_HEAD(tty_drivers); /* linked list of tty drivers */
/* Semaphore to protect creating and releasing a tty. This is shared with
vt.c for deeply disgusting hack reasons */
DEFINE_MUTEX(tty_mutex);
+EXPORT_SYMBOL(tty_mutex);
#ifdef CONFIG_UNIX98_PTYS
extern struct tty_driver *ptm_driver; /* Unix98 pty masters; for /dev/ptmx */
@@ -160,17 +161,11 @@ static void release_mem(struct tty_struct *tty, int idx);
* been initialized in any way but has been zeroed
*
* Locking: none
- * FIXME: use kzalloc
*/
static struct tty_struct *alloc_tty_struct(void)
{
- struct tty_struct *tty;
-
- tty = kmalloc(sizeof(struct tty_struct), GFP_KERNEL);
- if (tty)
- memset(tty, 0, sizeof(struct tty_struct));
- return tty;
+ return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
static void tty_buffer_free_all(struct tty_struct *);
@@ -483,10 +478,9 @@ int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
tb->used += space;
copied += space;
chars += space;
- }
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- while (unlikely(size > copied));
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
return copied;
}
EXPORT_SYMBOL(tty_insert_flip_string);
@@ -521,10 +515,9 @@ int tty_insert_flip_string_flags(struct tty_struct *tty,
copied += space;
chars += space;
flags += space;
- }
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- while (unlikely(size > copied));
+ /* There is a small chance that we need to split the data over
+ several buffers. If this is the case we must loop */
+ } while (unlikely(size > copied));
return copied;
}
EXPORT_SYMBOL(tty_insert_flip_string_flags);
@@ -626,9 +619,9 @@ EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
{
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tty->termios->c_line = num;
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/*
@@ -1346,9 +1339,9 @@ static void do_tty_hangup(void *data)
*/
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
{
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
*tty->termios = tty->driver->init_termios;
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/* Defer ldisc switch */
@@ -2072,8 +2065,9 @@ fail_no_mem:
/* call the tty release_mem routine to clean out this slot */
release_mem_out:
- printk(KERN_INFO "init_dev: ldisc open failed, "
- "clearing slot %d\n", idx);
+ if (printk_ratelimit())
+ printk(KERN_INFO "init_dev: ldisc open failed, "
+ "clearing slot %d\n", idx);
release_mem(tty, idx);
goto end_init;
}
@@ -2726,6 +2720,8 @@ static int tty_fasync(int fd, struct file * filp, int on)
* Locking:
* Called functions take tty_ldisc_lock
* current->signal->tty check is safe without locks
+ *
+ * FIXME: may race normal receive processing
*/
static int tiocsti(struct tty_struct *tty, char __user *p)
@@ -2748,18 +2744,21 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
* @tty; tty
* @arg: user buffer for result
*
- * Copies the kernel idea of the window size into the user buffer. No
- * locking is done.
+ * Copies the kernel idea of the window size into the user buffer.
*
- * FIXME: Returning random values racing a window size set is wrong
- * should lock here against that
+ * Locking: tty->termios_sem is taken to ensure the winsize data
+ * is consistent.
*/
static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
{
- if (copy_to_user(arg, &tty->winsize, sizeof(*arg)))
- return -EFAULT;
- return 0;
+ int err;
+
+ mutex_lock(&tty->termios_mutex);
+ err = copy_to_user(arg, &tty->winsize, sizeof(*arg));
+ mutex_unlock(&tty->termios_mutex);
+
+ return err ? -EFAULT: 0;
}
/**
@@ -2772,12 +2771,11 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user * arg)
* actually has driver level meaning and triggers a VC resize.
*
* Locking:
- * The console_sem is used to ensure we do not try and resize
- * the console twice at once.
- * FIXME: Two racing size sets may leave the console and kernel
- * parameters disagreeing. Is this exploitable ?
- * FIXME: Random values racing a window size get is wrong
- * should lock here against that
+ * Called function use the console_sem is used to ensure we do
+ * not try and resize the console twice at once.
+ * The tty->termios_sem is used to ensure we don't double
+ * resize and get confused. Lock order - tty->termios.sem before
+ * console sem
*/
static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
@@ -2787,17 +2785,18 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
return -EFAULT;
+
+ mutex_lock(&tty->termios_mutex);
if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
- return 0;
+ goto done;
+
#ifdef CONFIG_VT
if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
- int rc;
-
- acquire_console_sem();
- rc = vc_resize(tty->driver_data, tmp_ws.ws_col, tmp_ws.ws_row);
- release_console_sem();
- if (rc)
- return -ENXIO;
+ if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col,
+ tmp_ws.ws_row)) {
+ mutex_unlock(&tty->termios_mutex);
+ return -ENXIO;
+ }
}
#endif
if (tty->pgrp > 0)
@@ -2806,6 +2805,8 @@ static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
kill_pg(real_tty->pgrp, SIGWINCH, 1);
tty->winsize = tmp_ws;
real_tty->winsize = tmp_ws;
+done:
+ mutex_unlock(&tty->termios_mutex);
return 0;
}
@@ -2880,9 +2881,7 @@ static int fionbio(struct file *file, int __user *p)
* Locking:
* Takes tasklist lock internally to walk sessions
* Takes task_lock() when updating signal->tty
- *
- * FIXME: tty_mutex is needed to protect signal->tty references.
- * FIXME: why task_lock on the signal->tty reference ??
+ * Takes tty_mutex() to protect tty instance
*
*/
@@ -2917,9 +2916,11 @@ static int tiocsctty(struct tty_struct *tty, int arg)
} else
return -EPERM;
}
+ mutex_lock(&tty_mutex);
task_lock(current);
current->signal->tty = tty;
task_unlock(current);
+ mutex_unlock(&tty_mutex);
current->signal->tty_old_pgrp = 0;
tty->session = current->signal->session;
tty->pgrp = process_group(current);
@@ -2959,8 +2960,6 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
* permitted where the tty session is our session.
*
* Locking: None
- *
- * FIXME: current->signal->tty referencing is unsafe.
*/
static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
@@ -3039,19 +3038,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
* timed break functionality.
*
* Locking:
- * None
+ * atomic_write_lock serializes
*
- * FIXME:
- * What if two overlap
*/
static int send_break(struct tty_struct *tty, unsigned int duration)
{
+ if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ return -EINTR;
tty->driver->break_ctl(tty, -1);
if (!signal_pending(current)) {
msleep_interruptible(duration);
}
tty->driver->break_ctl(tty, 0);
+ mutex_unlock(&tty->atomic_write_lock);
if (signal_pending(current))
return -EINTR;
return 0;
@@ -3144,6 +3144,8 @@ int tty_ioctl(struct inode * inode, struct file * file,
if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL;
+ /* CHECKME: is this safe as one end closes ? */
+
real_tty = tty;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER)
@@ -3580,7 +3582,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
tty_buffer_init(tty);
INIT_WORK(&tty->buf.work, flush_to_ldisc, tty);
init_MUTEX(&tty->buf.pty_sem);
- init_MUTEX(&tty->termios_sem);
+ mutex_init(&tty->termios_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
INIT_WORK(&tty->hangup_work, do_tty_hangup, tty);
@@ -3678,7 +3680,8 @@ void put_tty_driver(struct tty_driver *driver)
kfree(driver);
}
-void tty_set_operations(struct tty_driver *driver, struct tty_operations *op)
+void tty_set_operations(struct tty_driver *driver,
+ const struct tty_operations *op)
{
driver->open = op->open;
driver->close = op->close;
diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c
index 4ad47d321bd..3b6fa7b0be8 100644
--- a/drivers/char/tty_ioctl.c
+++ b/drivers/char/tty_ioctl.c
@@ -20,6 +20,7 @@
#include <linux/mm.h>
#include <linux/module.h>
#include <linux/bitops.h>
+#include <linux/mutex.h>
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -131,7 +132,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
/* FIXME: we need to decide on some locking/ordering semantics
for the set_termios notification eventually */
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
*tty->termios = *new_termios;
unset_locked_termios(tty->termios, &old_termios, tty->termios_locked);
@@ -176,7 +177,7 @@ static void change_termios(struct tty_struct * tty, struct termios * new_termios
(ld->set_termios)(tty, &old_termios);
tty_ldisc_deref(ld);
}
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
}
/**
@@ -284,13 +285,13 @@ static int get_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
{
struct sgttyb tmp;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tmp.sg_ispeed = 0;
tmp.sg_ospeed = 0;
tmp.sg_erase = tty->termios->c_cc[VERASE];
tmp.sg_kill = tty->termios->c_cc[VKILL];
tmp.sg_flags = get_sgflags(tty);
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
return copy_to_user(sgttyb, &tmp, sizeof(tmp)) ? -EFAULT : 0;
}
@@ -345,12 +346,12 @@ static int set_sgttyb(struct tty_struct * tty, struct sgttyb __user * sgttyb)
if (copy_from_user(&tmp, sgttyb, sizeof(tmp)))
return -EFAULT;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
termios = *tty->termios;
termios.c_cc[VERASE] = tmp.sg_erase;
termios.c_cc[VKILL] = tmp.sg_kill;
set_sgflags(&termios, tmp.sg_flags);
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
change_termios(tty, &termios);
return 0;
}
@@ -422,24 +423,28 @@ static int set_ltchars(struct tty_struct * tty, struct ltchars __user * ltchars)
*
* Send a high priority character to the tty even if stopped
*
- * Locking: none
- *
- * FIXME: overlapping calls with start/stop tty lose state of tty
+ * Locking: none for xchar method, write ordering for write method.
*/
-static void send_prio_char(struct tty_struct *tty, char ch)
+static int send_prio_char(struct tty_struct *tty, char ch)
{
int was_stopped = tty->stopped;
if (tty->driver->send_xchar) {
tty->driver->send_xchar(tty, ch);
- return;
+ return 0;
}
+
+ if (mutex_lock_interruptible(&tty->atomic_write_lock))
+ return -ERESTARTSYS;
+
if (was_stopped)
start_tty(tty);
tty->driver->write(tty, &ch, 1);
if (was_stopped)
stop_tty(tty);
+ mutex_unlock(&tty->atomic_write_lock);
+ return 0;
}
int n_tty_ioctl(struct tty_struct * tty, struct file * file,
@@ -513,11 +518,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
break;
case TCIOFF:
if (STOP_CHAR(tty) != __DISABLED_CHAR)
- send_prio_char(tty, STOP_CHAR(tty));
+ return send_prio_char(tty, STOP_CHAR(tty));
break;
case TCION:
if (START_CHAR(tty) != __DISABLED_CHAR)
- send_prio_char(tty, START_CHAR(tty));
+ return send_prio_char(tty, START_CHAR(tty));
break;
default:
return -EINVAL;
@@ -592,11 +597,11 @@ int n_tty_ioctl(struct tty_struct * tty, struct file * file,
case TIOCSSOFTCAR:
if (get_user(arg, (unsigned int __user *) arg))
return -EFAULT;
- down(&tty->termios_sem);
+ mutex_lock(&tty->termios_mutex);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
- up(&tty->termios_sem);
+ mutex_unlock(&tty->termios_mutex);
return 0;
default:
return -ENOIOCTLCMD;
diff --git a/drivers/char/vc_screen.c b/drivers/char/vc_screen.c
index a9247b5213d..bd7a98c6ea7 100644
--- a/drivers/char/vc_screen.c
+++ b/drivers/char/vc_screen.c
@@ -474,14 +474,15 @@ static const struct file_operations vcs_fops = {
static struct class *vc_class;
-void vcs_make_devfs(struct tty_struct *tty)
+void vcs_make_sysfs(struct tty_struct *tty)
{
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 1),
NULL, "vcs%u", tty->index + 1);
class_device_create(vc_class, NULL, MKDEV(VCS_MAJOR, tty->index + 129),
NULL, "vcsa%u", tty->index + 1);
}
-void vcs_remove_devfs(struct tty_struct *tty)
+
+void vcs_remove_sysfs(struct tty_struct *tty)
{
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 1));
class_device_destroy(vc_class, MKDEV(VCS_MAJOR, tty->index + 129));
diff --git a/drivers/char/viocons.c b/drivers/char/viocons.c
index f3efeaf2826..a362ee9c92d 100644
--- a/drivers/char/viocons.c
+++ b/drivers/char/viocons.c
@@ -1047,7 +1047,7 @@ static int send_open(HvLpIndex remoteLp, void *sem)
0, 0, 0, 0);
}
-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = viotty_open,
.close = viotty_close,
.write = viotty_write,
diff --git a/drivers/char/vme_scc.c b/drivers/char/vme_scc.c
index bfe5ea948f6..c2ca31eb850 100644
--- a/drivers/char/vme_scc.c
+++ b/drivers/char/vme_scc.c
@@ -113,7 +113,7 @@ static struct real_driver scc_real_driver = {
};
-static struct tty_operations scc_ops = {
+static const struct tty_operations scc_ops = {
.open = scc_open,
.close = gs_close,
.write = gs_write,
diff --git a/drivers/char/vt.c b/drivers/char/vt.c
index da7e66a2a38..8e4413f6fba 100644
--- a/drivers/char/vt.c
+++ b/drivers/char/vt.c
@@ -63,6 +63,13 @@
*
* Removed console_lock, enabled interrupts across all console operations
* 13 March 2001, Andrew Morton
+ *
+ * Fixed UTF-8 mode so alternate charset modes always work according
+ * to control sequences interpreted in do_con_trol function
+ * preserving backward VT100 semigraphics compatibility,
+ * malformed UTF sequences represented as sequences of replacement glyphs,
+ * original codes or '?' as a last resort if replacement glyph is undefined
+ * by Adam Tla/lka <atlka@pg.gda.pl>, Aug 2006
*/
#include <linux/module.h>
@@ -99,7 +106,8 @@
#define MAX_NR_CON_DRIVER 16
#define CON_DRIVER_FLAG_MODULE 1
-#define CON_DRIVER_FLAG_INIT 2
+#define CON_DRIVER_FLAG_INIT 2
+#define CON_DRIVER_FLAG_ATTR 4
struct con_driver {
const struct consw *con;
@@ -128,16 +136,8 @@ const struct consw *conswitchp;
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
-extern void vcs_make_devfs(struct tty_struct *tty);
-extern void vcs_remove_devfs(struct tty_struct *tty);
-
-extern void console_map_init(void);
-#ifdef CONFIG_PROM_CONSOLE
-extern void prom_con_init(void);
-#endif
-#ifdef CONFIG_MDA_CONSOLE
-extern int mda_console_init(void);
-#endif
+extern void vcs_make_sysfs(struct tty_struct *tty);
+extern void vcs_remove_sysfs(struct tty_struct *tty);
struct vc vc_cons [MAX_NR_CONSOLES];
@@ -730,7 +730,8 @@ int vc_allocate(unsigned int currcons) /* return 0 on success */
visual_init(vc, currcons, 1);
if (!*vc->vc_uni_pagedir_loc)
con_set_default_unimap(vc);
- vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
+ if (!vc->vc_kmalloced)
+ vc->vc_screenbuf = kmalloc(vc->vc_screenbuf_size, GFP_KERNEL);
if (!vc->vc_screenbuf) {
kfree(vc);
vc_cons[currcons].d = NULL;
@@ -878,14 +879,24 @@ int vc_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
return err;
}
+int vc_lock_resize(struct vc_data *vc, unsigned int cols, unsigned int lines)
+{
+ int rc;
+
+ acquire_console_sem();
+ rc = vc_resize(vc, cols, lines);
+ release_console_sem();
+ return rc;
+}
-void vc_disallocate(unsigned int currcons)
+void vc_deallocate(unsigned int currcons)
{
WARN_CONSOLE_UNLOCKED();
if (vc_cons_allocated(currcons)) {
struct vc_data *vc = vc_cons[currcons].d;
vc->vc_sw->con_deinit(vc);
+ put_pid(vc->vt_pid);
module_put(vc->vc_sw->owner);
if (vc->vc_kmalloced)
kfree(vc->vc_screenbuf);
@@ -2005,17 +2016,23 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
/* Do no translation at all in control states */
if (vc->vc_state != ESnormal) {
tc = c;
- } else if (vc->vc_utf) {
+ } else if (vc->vc_utf && !vc->vc_disp_ctrl) {
/* Combine UTF-8 into Unicode */
- /* Incomplete characters silently ignored */
+ /* Malformed sequences as sequences of replacement glyphs */
+rescan_last_byte:
if(c > 0x7f) {
- if (vc->vc_utf_count > 0 && (c & 0xc0) == 0x80) {
- vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
- vc->vc_utf_count--;
- if (vc->vc_utf_count == 0)
- tc = c = vc->vc_utf_char;
- else continue;
+ if (vc->vc_utf_count) {
+ if ((c & 0xc0) == 0x80) {
+ vc->vc_utf_char = (vc->vc_utf_char << 6) | (c & 0x3f);
+ if (--vc->vc_utf_count) {
+ vc->vc_npar++;
+ continue;
+ }
+ tc = c = vc->vc_utf_char;
+ } else
+ goto replacement_glyph;
} else {
+ vc->vc_npar = 0;
if ((c & 0xe0) == 0xc0) {
vc->vc_utf_count = 1;
vc->vc_utf_char = (c & 0x1f);
@@ -2032,14 +2049,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
vc->vc_utf_count = 5;
vc->vc_utf_char = (c & 0x01);
} else
- vc->vc_utf_count = 0;
+ goto replacement_glyph;
continue;
}
} else {
+ if (vc->vc_utf_count)
+ goto replacement_glyph;
tc = c;
- vc->vc_utf_count = 0;
}
- } else { /* no utf */
+ } else { /* no utf or alternate charset mode */
tc = vc->vc_translate[vc->vc_toggle_meta ? (c | 0x80) : c];
}
@@ -2054,31 +2072,33 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
* direct-to-font zone in UTF-8 mode.
*/
ok = tc && (c >= 32 ||
- (!vc->vc_utf && !(((vc->vc_disp_ctrl ? CTRL_ALWAYS
- : CTRL_ACTION) >> c) & 1)))
+ !(vc->vc_disp_ctrl ? (CTRL_ALWAYS >> c) & 1 :
+ vc->vc_utf || ((CTRL_ACTION >> c) & 1)))
&& (c != 127 || vc->vc_disp_ctrl)
&& (c != 128+27);
if (vc->vc_state == ESnormal && ok) {
/* Now try to find out how to display it */
tc = conv_uni_to_pc(vc, tc);
- if ( tc == -4 ) {
+ if (tc & ~charmask) {
+ if ( tc == -4 ) {
/* If we got -4 (not found) then see if we have
defined a replacement character (U+FFFD) */
- tc = conv_uni_to_pc(vc, 0xfffd);
-
- /* One reason for the -4 can be that we just
- did a clear_unimap();
- try at least to show something. */
- if (tc == -4)
- tc = c;
- } else if ( tc == -3 ) {
- /* Bad hash table -- hope for the best */
- tc = c;
- }
- if (tc & ~charmask)
- continue; /* Conversion failed */
+replacement_glyph:
+ tc = conv_uni_to_pc(vc, 0xfffd);
+ if (!(tc & ~charmask))
+ goto display_glyph;
+ } else if ( tc != -3 )
+ continue; /* nothing to display */
+ /* no hash table or no replacement --
+ * hope for the best */
+ if ( c & ~charmask )
+ tc = '?';
+ else
+ tc = c;
+ }
+display_glyph:
if (vc->vc_need_wrap || vc->vc_decim)
FLUSH
if (vc->vc_need_wrap) {
@@ -2102,6 +2122,15 @@ static int do_con_write(struct tty_struct *tty, const unsigned char *buf, int co
vc->vc_x++;
draw_to = (vc->vc_pos += 2);
}
+ if (vc->vc_utf_count) {
+ if (vc->vc_npar) {
+ vc->vc_npar--;
+ goto display_glyph;
+ }
+ vc->vc_utf_count = 0;
+ c = orig;
+ goto rescan_last_byte;
+ }
continue;
}
FLUSH
@@ -2498,7 +2527,7 @@ static int con_open(struct tty_struct *tty, struct file *filp)
tty->winsize.ws_col = vc_cons[currcons].d->vc_cols;
}
release_console_sem();
- vcs_make_devfs(tty);
+ vcs_make_sysfs(tty);
return ret;
}
}
@@ -2511,7 +2540,7 @@ static int con_open(struct tty_struct *tty, struct file *filp)
* and taking a ref against the tty while we're in the process of forgetting
* about it and cleaning things up.
*
- * This is because vcs_remove_devfs() can sleep and will drop the BKL.
+ * This is because vcs_remove_sysfs() can sleep and will drop the BKL.
*/
static void con_close(struct tty_struct *tty, struct file *filp)
{
@@ -2524,7 +2553,7 @@ static void con_close(struct tty_struct *tty, struct file *filp)
vc->vc_tty = NULL;
tty->driver_data = NULL;
release_console_sem();
- vcs_remove_devfs(tty);
+ vcs_remove_sysfs(tty);
mutex_unlock(&tty_mutex);
/*
* tty_mutex is released, but we still hold BKL, so there is
@@ -2639,7 +2668,7 @@ static int __init con_init(void)
}
console_initcall(con_init);
-static struct tty_operations con_ops = {
+static const struct tty_operations con_ops = {
.open = con_open,
.close = con_close,
.write = con_write,
@@ -3034,22 +3063,37 @@ static struct class_device_attribute class_device_attrs[] = {
static int vtconsole_init_class_device(struct con_driver *con)
{
int i;
+ int error = 0;
+ con->flag |= CON_DRIVER_FLAG_ATTR;
class_set_devdata(con->class_dev, con);
- for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
- class_device_create_file(con->class_dev,
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) {
+ error = class_device_create_file(con->class_dev,
&class_device_attrs[i]);
+ if (error)
+ break;
+ }
- return 0;
+ if (error) {
+ while (--i >= 0)
+ class_device_remove_file(con->class_dev,
+ &class_device_attrs[i]);
+ con->flag &= ~CON_DRIVER_FLAG_ATTR;
+ }
+
+ return error;
}
static void vtconsole_deinit_class_device(struct con_driver *con)
{
int i;
- for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
- class_device_remove_file(con->class_dev,
- &class_device_attrs[i]);
+ if (con->flag & CON_DRIVER_FLAG_ATTR) {
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_remove_file(con->class_dev,
+ &class_device_attrs[i]);
+ con->flag &= ~CON_DRIVER_FLAG_ATTR;
+ }
}
/**
@@ -3148,6 +3192,7 @@ int register_con_driver(const struct consw *csw, int first, int last)
} else {
vtconsole_init_class_device(con_driver);
}
+
err:
release_console_sem();
module_put(owner);
@@ -3765,6 +3810,7 @@ EXPORT_SYMBOL(default_blu);
EXPORT_SYMBOL(update_region);
EXPORT_SYMBOL(redraw_screen);
EXPORT_SYMBOL(vc_resize);
+EXPORT_SYMBOL(vc_lock_resize);
EXPORT_SYMBOL(fg_console);
EXPORT_SYMBOL(console_blank_hook);
EXPORT_SYMBOL(console_blanked);
diff --git a/drivers/char/vt_ioctl.c b/drivers/char/vt_ioctl.c
index a5628a8b662..ac5d60edbaf 100644
--- a/drivers/char/vt_ioctl.c
+++ b/drivers/char/vt_ioctl.c
@@ -96,7 +96,7 @@ do_kdsk_ioctl(int cmd, struct kbentry __user *user_kbe, int perm, struct kbd_str
if (!perm)
return -EPERM;
if (!i && v == K_NOSUCHMAP) {
- /* disallocate map */
+ /* deallocate map */
key_map = key_maps[s];
if (s && key_map) {
key_maps[s] = NULL;
@@ -645,13 +645,16 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
*/
case KDSIGACCEPT:
{
- extern int spawnpid, spawnsig;
if (!perm || !capable(CAP_KILL))
return -EPERM;
if (!valid_signal(arg) || arg < 1 || arg == SIGKILL)
return -EINVAL;
- spawnpid = current->pid;
- spawnsig = arg;
+
+ spin_lock_irq(&vt_spawn_con.lock);
+ put_pid(vt_spawn_con.pid);
+ vt_spawn_con.pid = get_pid(task_pid(current));
+ vt_spawn_con.sig = arg;
+ spin_unlock_irq(&vt_spawn_con.lock);
return 0;
}
@@ -669,7 +672,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
vc->vt_mode = tmp;
/* the frsig is ignored, so we set it to 0 */
vc->vt_mode.frsig = 0;
- vc->vt_pid = current->pid;
+ put_pid(xchg(&vc->vt_pid, get_pid(task_pid(current))));
/* no switch is required -- saw@shade.msu.ru */
vc->vt_newvt = -1;
release_console_sem();
@@ -819,20 +822,20 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (arg > MAX_NR_CONSOLES)
return -ENXIO;
if (arg == 0) {
- /* disallocate all unused consoles, but leave 0 */
+ /* deallocate all unused consoles, but leave 0 */
acquire_console_sem();
for (i=1; i<MAX_NR_CONSOLES; i++)
if (! VT_BUSY(i))
- vc_disallocate(i);
+ vc_deallocate(i);
release_console_sem();
} else {
- /* disallocate a single console, if possible */
+ /* deallocate a single console, if possible */
arg--;
if (VT_BUSY(arg))
return -EBUSY;
if (arg) { /* leave 0 */
acquire_console_sem();
- vc_disallocate(arg);
+ vc_deallocate(arg);
release_console_sem();
}
}
@@ -847,11 +850,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
if (get_user(ll, &vtsizes->v_rows) ||
get_user(cc, &vtsizes->v_cols))
return -EFAULT;
- for (i = 0; i < MAX_NR_CONSOLES; i++) {
- acquire_console_sem();
- vc_resize(vc_cons[i].d, cc, ll);
- release_console_sem();
- }
+ for (i = 0; i < MAX_NR_CONSOLES; i++)
+ vc_lock_resize(vc_cons[i].d, cc, ll);
return 0;
}
@@ -1063,7 +1063,7 @@ void reset_vc(struct vc_data *vc)
vc->vt_mode.relsig = 0;
vc->vt_mode.acqsig = 0;
vc->vt_mode.frsig = 0;
- vc->vt_pid = -1;
+ put_pid(xchg(&vc->vt_pid, NULL));
vc->vt_newvt = -1;
if (!in_interrupt()) /* Via keyboard.c:SAK() - akpm */
reset_palette(vc);
@@ -1114,7 +1114,7 @@ static void complete_change_console(struct vc_data *vc)
* tell us if the process has gone or something else
* is awry
*/
- if (kill_proc(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) {
+ if (kill_pid(vc->vt_pid, vc->vt_mode.acqsig, 1) != 0) {
/*
* The controlling process has died, so we revert back to
* normal operation. In this case, we'll also change back
@@ -1174,7 +1174,7 @@ void change_console(struct vc_data *new_vc)
* tell us if the process has gone or something else
* is awry
*/
- if (kill_proc(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) {
+ if (kill_pid(vc->vt_pid, vc->vt_mode.relsig, 1) == 0) {
/*
* It worked. Mark the vt to switch to and
* return. The process needs to send us a
diff --git a/drivers/char/watchdog/Kconfig b/drivers/char/watchdog/Kconfig
index fff89c2d88f..06f3fa2fe87 100644
--- a/drivers/char/watchdog/Kconfig
+++ b/drivers/char/watchdog/Kconfig
@@ -165,6 +165,24 @@ config EP93XX_WATCHDOG
To compile this driver as a module, choose M here: the
module will be called ep93xx_wdt.
+config OMAP_WATCHDOG
+ tristate "OMAP Watchdog"
+ depends on WATCHDOG && (ARCH_OMAP16XX || ARCH_OMAP24XX)
+ help
+ Support for TI OMAP1610/OMAP1710/OMAP2420 watchdog. Say 'Y' here to
+ enable the OMAP1610/OMAP1710 watchdog timer.
+
+config PNX4008_WATCHDOG
+ tristate "PNX4008 Watchdog"
+ depends on WATCHDOG && ARCH_PNX4008
+ help
+ Say Y here if to include support for the watchdog timer
+ in the PNX4008 processor.
+ This driver can be built as a module by choosing M. The module
+ will be called pnx4008_wdt.
+
+ Say N if you are unsure.
+
# X86 (i386 + ia64 + x86_64) Architecture
config ACQUIRE_WDT
@@ -510,6 +528,14 @@ config SH_WDT
To compile this driver as a module, choose M here: the
module will be called shwdt.
+config SH_WDT_MMAP
+ bool "Allow mmap of SH WDT"
+ default n
+ depends on SH_WDT
+ help
+ If you say Y here, user applications will be able to mmap the
+ WDT/CPG registers.
+#
# SPARC64 Architecture
config WATCHDOG_CP1XXX
diff --git a/drivers/char/watchdog/Makefile b/drivers/char/watchdog/Makefile
index 6ab77b61a64..6ffca7cb56a 100644
--- a/drivers/char/watchdog/Makefile
+++ b/drivers/char/watchdog/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
# ARM Architecture
obj-$(CONFIG_AT91_WATCHDOG) += at91_wdt.o
+obj-$(CONFIG_OMAP_WATCHDOG) += omap_wdt.o
obj-$(CONFIG_21285_WATCHDOG) += wdt285.o
obj-$(CONFIG_977_WATCHDOG) += wdt977.o
obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
@@ -32,6 +33,7 @@ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c2410_wdt.o
obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
+obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
# X86 (i386 + ia64 + x86_64) Architecture
obj-$(CONFIG_ACQUIRE_WDT) += acquirewdt.o
diff --git a/drivers/char/watchdog/acquirewdt.c b/drivers/char/watchdog/acquirewdt.c
index c77fe3cf285..154d67e591e 100644
--- a/drivers/char/watchdog/acquirewdt.c
+++ b/drivers/char/watchdog/acquirewdt.c
@@ -183,7 +183,7 @@ static int acq_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/advantechwdt.c b/drivers/char/watchdog/advantechwdt.c
index 8069be445ed..9d732769ba0 100644
--- a/drivers/char/watchdog/advantechwdt.c
+++ b/drivers/char/watchdog/advantechwdt.c
@@ -176,7 +176,7 @@ advwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/alim1535_wdt.c b/drivers/char/watchdog/alim1535_wdt.c
index c5c94e4c949..01b0d132ee4 100644
--- a/drivers/char/watchdog/alim1535_wdt.c
+++ b/drivers/char/watchdog/alim1535_wdt.c
@@ -236,7 +236,7 @@ static int ali_ioctl(struct inode *inode, struct file *file,
return put_user(timeout, p);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
@@ -330,17 +330,20 @@ static int __init ali_find_watchdog(void)
u32 wdog;
/* Check for a 1535 series bridge */
- pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
+ pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x1535, NULL);
if(pdev == NULL)
return -ENODEV;
+ pci_dev_put(pdev);
/* Check for the a 7101 PMU */
- pdev = pci_find_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
+ pdev = pci_get_device(PCI_VENDOR_ID_AL, 0x7101, NULL);
if(pdev == NULL)
return -ENODEV;
- if(pci_enable_device(pdev))
+ if(pci_enable_device(pdev)) {
+ pci_dev_put(pdev);
return -EIO;
+ }
ali_pci = pdev;
@@ -447,6 +450,7 @@ static void __exit watchdog_exit(void)
/* Deregister */
unregister_reboot_notifier(&ali_notifier);
misc_deregister(&ali_miscdev);
+ pci_dev_put(ali_pci);
}
module_init(watchdog_init);
diff --git a/drivers/char/watchdog/alim7101_wdt.c b/drivers/char/watchdog/alim7101_wdt.c
index ffd7684f999..5948863b592 100644
--- a/drivers/char/watchdog/alim7101_wdt.c
+++ b/drivers/char/watchdog/alim7101_wdt.c
@@ -277,7 +277,7 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd, u
case WDIOC_GETTIMEOUT:
return put_user(timeout, p);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
@@ -333,6 +333,7 @@ static void __exit alim7101_wdt_unload(void)
/* Deregister */
misc_deregister(&wdt_miscdev);
unregister_reboot_notifier(&wdt_notifier);
+ pci_dev_put(alim7101_pmu);
}
static int __init alim7101_wdt_init(void)
@@ -342,7 +343,8 @@ static int __init alim7101_wdt_init(void)
char tmp;
printk(KERN_INFO PFX "Steve Hill <steve@navaho.co.uk>.\n");
- alim7101_pmu = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,NULL);
+ alim7101_pmu = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M7101,
+ NULL);
if (!alim7101_pmu) {
printk(KERN_INFO PFX "ALi M7101 PMU not present - WDT not set\n");
return -EBUSY;
@@ -351,21 +353,23 @@ static int __init alim7101_wdt_init(void)
/* Set the WDT in the PMU to 1 second */
pci_write_config_byte(alim7101_pmu, ALI_7101_WDT, 0x02);
- ali1543_south = pci_find_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533, NULL);
+ ali1543_south = pci_get_device(PCI_VENDOR_ID_AL, PCI_DEVICE_ID_AL_M1533,
+ NULL);
if (!ali1543_south) {
printk(KERN_INFO PFX "ALi 1543 South-Bridge not present - WDT not set\n");
- return -EBUSY;
+ goto err_out;
}
pci_read_config_byte(ali1543_south, 0x5e, &tmp);
+ pci_dev_put(ali1543_south);
if ((tmp & 0x1e) == 0x00) {
if (!use_gpio) {
printk(KERN_INFO PFX "Detected old alim7101 revision 'a1d'. If this is a cobalt board, set the 'use_gpio' module parameter.\n");
- return -EBUSY;
+ goto err_out;
}
nowayout = 1;
} else if ((tmp & 0x1e) != 0x12 && (tmp & 0x1e) != 0x00) {
printk(KERN_INFO PFX "ALi 1543 South-Bridge does not have the correct revision number (???1001?) - WDT not set\n");
- return -EBUSY;
+ goto err_out;
}
if(timeout < 1 || timeout > 3600) /* arbitrary upper limit */
@@ -404,6 +408,7 @@ static int __init alim7101_wdt_init(void)
err_out_miscdev:
misc_deregister(&wdt_miscdev);
err_out:
+ pci_dev_put(alim7101_pmu);
return rc;
}
diff --git a/drivers/char/watchdog/at91_wdt.c b/drivers/char/watchdog/at91_wdt.c
index cc266715ea3..4e7a1145e78 100644
--- a/drivers/char/watchdog/at91_wdt.c
+++ b/drivers/char/watchdog/at91_wdt.c
@@ -168,7 +168,7 @@ static int at91_wdt_ioctl(struct inode *inode, struct file *file,
return 0;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/booke_wdt.c b/drivers/char/watchdog/booke_wdt.c
index e3cefc538b4..488902231cc 100644
--- a/drivers/char/watchdog/booke_wdt.c
+++ b/drivers/char/watchdog/booke_wdt.c
@@ -125,7 +125,7 @@ static int booke_wdt_ioctl (struct inode *inode, struct file *file,
return -EINVAL;
return 0;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
diff --git a/drivers/char/watchdog/cpu5wdt.c b/drivers/char/watchdog/cpu5wdt.c
index 04c7e49918d..00bdabb90f2 100644
--- a/drivers/char/watchdog/cpu5wdt.c
+++ b/drivers/char/watchdog/cpu5wdt.c
@@ -183,7 +183,7 @@ static int cpu5wdt_ioctl(struct inode *inode, struct file *file, unsigned int cm
}
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/ep93xx_wdt.c b/drivers/char/watchdog/ep93xx_wdt.c
index 77c8a955ae9..01cf123b161 100644
--- a/drivers/char/watchdog/ep93xx_wdt.c
+++ b/drivers/char/watchdog/ep93xx_wdt.c
@@ -144,7 +144,7 @@ static int
ep93xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
switch (cmd) {
case WDIOC_GETSUPPORT:
diff --git a/drivers/char/watchdog/eurotechwdt.c b/drivers/char/watchdog/eurotechwdt.c
index 62dbccb2f6d..4f4269754c4 100644
--- a/drivers/char/watchdog/eurotechwdt.c
+++ b/drivers/char/watchdog/eurotechwdt.c
@@ -240,7 +240,7 @@ static int eurwdt_ioctl(struct inode *inode, struct file *file,
switch(cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0;
diff --git a/drivers/char/watchdog/i6300esb.c b/drivers/char/watchdog/i6300esb.c
index 870539eabbf..fb64df4d7c8 100644
--- a/drivers/char/watchdog/i6300esb.c
+++ b/drivers/char/watchdog/i6300esb.c
@@ -315,7 +315,7 @@ static int esb_ioctl (struct inode *inode, struct file *file,
return put_user(heartbeat, p);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/i8xx_tco.c b/drivers/char/watchdog/i8xx_tco.c
index 8385dd36eef..e0627d79707 100644
--- a/drivers/char/watchdog/i8xx_tco.c
+++ b/drivers/char/watchdog/i8xx_tco.c
@@ -356,7 +356,7 @@ static int i8xx_tco_ioctl (struct inode *inode, struct file *file,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
@@ -406,18 +406,18 @@ static struct notifier_block i8xx_tco_notifier = {
* want to register another driver on the same PCI id.
*/
static struct pci_device_id i8xx_tco_pci_tbl[] = {
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, PCI_ANY_ID, PCI_ANY_ID, },
- { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, PCI_ANY_ID, PCI_ANY_ID, },
- { 0, }, /* End of list */
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AA_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801AB_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_10) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_12) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801E_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0) },
+ { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1) },
+ { }, /* End of list */
};
MODULE_DEVICE_TABLE (pci, i8xx_tco_pci_tbl);
@@ -434,12 +434,11 @@ static unsigned char __init i8xx_tco_getdevice (void)
* Find the PCI device
*/
- while ((dev = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) {
+ for_each_pci_dev(dev)
if (pci_match_id(i8xx_tco_pci_tbl, dev)) {
i8xx_tco_pci = dev;
break;
}
- }
if (i8xx_tco_pci) {
/*
@@ -454,6 +453,7 @@ static unsigned char __init i8xx_tco_getdevice (void)
/* Something's wrong here, ACPIBASE has to be set */
if (badr == 0x0001 || badr == 0x0000) {
printk (KERN_ERR PFX "failed to get TCOBASE address\n");
+ pci_dev_put(i8xx_tco_pci);
return 0;
}
@@ -465,6 +465,7 @@ static unsigned char __init i8xx_tco_getdevice (void)
pci_read_config_byte (i8xx_tco_pci, 0xd4, &val1);
if (val1 & 0x02) {
printk (KERN_ERR PFX "failed to reset NO_REBOOT flag, reboot disabled by hardware\n");
+ pci_dev_put(i8xx_tco_pci);
return 0; /* Cannot reset NO_REBOOT bit */
}
}
@@ -476,6 +477,7 @@ static unsigned char __init i8xx_tco_getdevice (void)
if (!request_region (SMI_EN + 1, 1, "i8xx TCO")) {
printk (KERN_ERR PFX "I/O address 0x%04x already in use\n",
SMI_EN + 1);
+ pci_dev_put(i8xx_tco_pci);
return 0;
}
val1 = inb (SMI_EN + 1);
@@ -542,6 +544,7 @@ unreg_notifier:
unreg_region:
release_region (TCOBASE, 0x10);
out:
+ pci_dev_put(i8xx_tco_pci);
return ret;
}
@@ -555,6 +558,8 @@ static void __exit watchdog_cleanup (void)
misc_deregister (&i8xx_tco_miscdev);
unregister_reboot_notifier(&i8xx_tco_notifier);
release_region (TCOBASE, 0x10);
+
+ pci_dev_put(i8xx_tco_pci);
}
module_init(watchdog_init);
diff --git a/drivers/char/watchdog/ib700wdt.c b/drivers/char/watchdog/ib700wdt.c
index fd95f732779..c1ed209a138 100644
--- a/drivers/char/watchdog/ib700wdt.c
+++ b/drivers/char/watchdog/ib700wdt.c
@@ -199,7 +199,7 @@ ibwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/ibmasr.c b/drivers/char/watchdog/ibmasr.c
index 26ceee7a4df..dd6760f1a23 100644
--- a/drivers/char/watchdog/ibmasr.c
+++ b/drivers/char/watchdog/ibmasr.c
@@ -295,7 +295,7 @@ static int asr_ioctl(struct inode *inode, struct file *file,
}
}
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
static int asr_open(struct inode *inode, struct file *file)
diff --git a/drivers/char/watchdog/indydog.c b/drivers/char/watchdog/indydog.c
index dacc1c20a31..0bc23930898 100644
--- a/drivers/char/watchdog/indydog.c
+++ b/drivers/char/watchdog/indydog.c
@@ -112,7 +112,7 @@ static int indydog_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
if (copy_to_user((struct watchdog_info *)arg,
&ident, sizeof(ident)))
diff --git a/drivers/char/watchdog/ixp2000_wdt.c b/drivers/char/watchdog/ixp2000_wdt.c
index 692908819e2..c91d9a660ec 100644
--- a/drivers/char/watchdog/ixp2000_wdt.c
+++ b/drivers/char/watchdog/ixp2000_wdt.c
@@ -107,7 +107,7 @@ static int
ixp2000_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
int time;
switch (cmd) {
diff --git a/drivers/char/watchdog/ixp4xx_wdt.c b/drivers/char/watchdog/ixp4xx_wdt.c
index 9db5cf2c38c..db477f71238 100644
--- a/drivers/char/watchdog/ixp4xx_wdt.c
+++ b/drivers/char/watchdog/ixp4xx_wdt.c
@@ -102,7 +102,7 @@ static int
ixp4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
int time;
switch (cmd) {
diff --git a/drivers/char/watchdog/machzwd.c b/drivers/char/watchdog/machzwd.c
index 23734e07fb2..276577d08fb 100644
--- a/drivers/char/watchdog/machzwd.c
+++ b/drivers/char/watchdog/machzwd.c
@@ -329,7 +329,7 @@ static int zf_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
@@ -426,8 +426,7 @@ static int __init zf_init(void)
printk(KERN_INFO PFX ": MachZ ZF-Logic Watchdog driver initializing.\n");
ret = zf_get_ZFL_version();
- printk("%#x\n", ret);
- if((!ret) || (ret != 0xffff)){
+ if ((!ret) || (ret == 0xffff)) {
printk(KERN_WARNING PFX ": no ZF-Logic found\n");
return -ENODEV;
}
diff --git a/drivers/char/watchdog/mixcomwd.c b/drivers/char/watchdog/mixcomwd.c
index ae943324d25..c2dac0aa1d6 100644
--- a/drivers/char/watchdog/mixcomwd.c
+++ b/drivers/char/watchdog/mixcomwd.c
@@ -185,7 +185,7 @@ static int mixcomwd_ioctl(struct inode *inode, struct file *file,
mixcomwd_ping();
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/mpc83xx_wdt.c b/drivers/char/watchdog/mpc83xx_wdt.c
index a480903ee1a..18ca752e2f9 100644
--- a/drivers/char/watchdog/mpc83xx_wdt.c
+++ b/drivers/char/watchdog/mpc83xx_wdt.c
@@ -125,7 +125,7 @@ static int mpc83xx_wdt_ioctl(struct inode *inode, struct file *file,
case WDIOC_GETTIMEOUT:
return put_user(timeout_sec, p);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/mpc8xx_wdt.c b/drivers/char/watchdog/mpc8xx_wdt.c
index 35dd9e6e114..8aaed10dd49 100644
--- a/drivers/char/watchdog/mpc8xx_wdt.c
+++ b/drivers/char/watchdog/mpc8xx_wdt.c
@@ -126,7 +126,7 @@ static int mpc8xx_wdt_ioctl(struct inode *inode, struct file *file,
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
diff --git a/drivers/char/watchdog/mpcore_wdt.c b/drivers/char/watchdog/mpcore_wdt.c
index 54b3c56ead0..02d336ace50 100644
--- a/drivers/char/watchdog/mpcore_wdt.c
+++ b/drivers/char/watchdog/mpcore_wdt.c
@@ -221,7 +221,7 @@ static int mpcore_wdt_ioctl(struct inode *inode, struct file *file,
} uarg;
if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
- return -ENOIOCTLCMD;
+ return -ENOTTY;
if (_IOC_DIR(cmd) & _IOC_WRITE) {
ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
@@ -271,7 +271,7 @@ static int mpcore_wdt_ioctl(struct inode *inode, struct file *file,
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
diff --git a/drivers/char/watchdog/mv64x60_wdt.c b/drivers/char/watchdog/mv64x60_wdt.c
index 5c8fab345b4..b887cdb0133 100644
--- a/drivers/char/watchdog/mv64x60_wdt.c
+++ b/drivers/char/watchdog/mv64x60_wdt.c
@@ -160,7 +160,7 @@ static int mv64x60_wdt_ioctl(struct inode *inode, struct file *file,
break;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
diff --git a/drivers/char/watchdog/omap_wdt.c b/drivers/char/watchdog/omap_wdt.c
new file mode 100644
index 00000000000..8f90b90a502
--- /dev/null
+++ b/drivers/char/watchdog/omap_wdt.c
@@ -0,0 +1,391 @@
+/*
+ * linux/drivers/char/watchdog/omap_wdt.c
+ *
+ * Watchdog driver for the TI OMAP 16xx & 24xx 32KHz (non-secure) watchdog
+ *
+ * Author: MontaVista Software, Inc.
+ * <gdavis@mvista.com> or <source@mvista.com>
+ *
+ * 2003 (c) MontaVista Software, Inc. 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.
+ *
+ * History:
+ *
+ * 20030527: George G. Davis <gdavis@mvista.com>
+ * Initially based on linux-2.4.19-rmk7-pxa1/drivers/char/sa1100_wdt.c
+ * (c) Copyright 2000 Oleg Drokin <green@crimea.edu>
+ * Based on SoftDog driver by Alan Cox <alan@redhat.com>
+ *
+ * Copyright (c) 2004 Texas Instruments.
+ * 1. Modified to support OMAP1610 32-KHz watchdog timer
+ * 2. Ported to 2.6 kernel
+ *
+ * Copyright (c) 2005 David Brownell
+ * Use the driver model and standard identifiers; handle bigger timeouts.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/reboot.h>
+#include <linux/smp_lock.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/moduleparam.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/hardware.h>
+#include <asm/bitops.h>
+
+#include <asm/arch/prcm.h>
+
+#include "omap_wdt.h"
+
+static unsigned timer_margin;
+module_param(timer_margin, uint, 0);
+MODULE_PARM_DESC(timer_margin, "initial watchdog timeout (in seconds)");
+
+static int omap_wdt_users;
+static struct clk *armwdt_ck = NULL;
+static struct clk *mpu_wdt_ick = NULL;
+static struct clk *mpu_wdt_fck = NULL;
+
+static unsigned int wdt_trgr_pattern = 0x1234;
+
+static void omap_wdt_ping(void)
+{
+ /* wait for posted write to complete */
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+ wdt_trgr_pattern = ~wdt_trgr_pattern;
+ omap_writel(wdt_trgr_pattern, (OMAP_WATCHDOG_TGR));
+ /* wait for posted write to complete */
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x08)
+ cpu_relax();
+ /* reloaded WCRR from WLDR */
+}
+
+static void omap_wdt_enable(void)
+{
+ /* Sequence to enable the watchdog */
+ omap_writel(0xBBBB, OMAP_WATCHDOG_SPR);
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+ omap_writel(0x4444, OMAP_WATCHDOG_SPR);
+ while ((omap_readl(OMAP_WATCHDOG_WPS)) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_disable(void)
+{
+ /* sequence required to disable watchdog */
+ omap_writel(0xAAAA, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+ omap_writel(0x5555, OMAP_WATCHDOG_SPR); /* TIMER_MODE */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x10)
+ cpu_relax();
+}
+
+static void omap_wdt_adjust_timeout(unsigned new_timeout)
+{
+ if (new_timeout < TIMER_MARGIN_MIN)
+ new_timeout = TIMER_MARGIN_DEFAULT;
+ if (new_timeout > TIMER_MARGIN_MAX)
+ new_timeout = TIMER_MARGIN_MAX;
+ timer_margin = new_timeout;
+}
+
+static void omap_wdt_set_timeout(void)
+{
+ u32 pre_margin = GET_WLDR_VAL(timer_margin);
+
+ /* just count up at 32 KHz */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+ omap_writel(pre_margin, OMAP_WATCHDOG_LDR);
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x04)
+ cpu_relax();
+}
+
+/*
+ * Allow only one task to hold it open
+ */
+
+static int omap_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(1, (unsigned long *)&omap_wdt_users))
+ return -EBUSY;
+
+ if (cpu_is_omap16xx())
+ clk_enable(armwdt_ck); /* Enable the clock */
+
+ if (cpu_is_omap24xx()) {
+ clk_enable(mpu_wdt_ick); /* Enable the interface clock */
+ clk_enable(mpu_wdt_fck); /* Enable the functional clock */
+ }
+
+ /* initialize prescaler */
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+ omap_writel((1 << 5) | (PTV << 2), OMAP_WATCHDOG_CNTRL);
+ while (omap_readl(OMAP_WATCHDOG_WPS) & 0x01)
+ cpu_relax();
+
+ omap_wdt_set_timeout();
+ omap_wdt_enable();
+ return 0;
+}
+
+static int omap_wdt_release(struct inode *inode, struct file *file)
+{
+ /*
+ * Shut off the timer unless NOWAYOUT is defined.
+ */
+#ifndef CONFIG_WATCHDOG_NOWAYOUT
+ omap_wdt_disable();
+
+ if (cpu_is_omap16xx()) {
+ clk_disable(armwdt_ck); /* Disable the clock */
+ clk_put(armwdt_ck);
+ armwdt_ck = NULL;
+ }
+
+ if (cpu_is_omap24xx()) {
+ clk_disable(mpu_wdt_ick); /* Disable the clock */
+ clk_disable(mpu_wdt_fck); /* Disable the clock */
+ clk_put(mpu_wdt_ick);
+ clk_put(mpu_wdt_fck);
+ mpu_wdt_ick = NULL;
+ mpu_wdt_fck = NULL;
+ }
+#else
+ printk(KERN_CRIT "omap_wdt: Unexpected close, not stopping!\n");
+#endif
+ omap_wdt_users = 0;
+ return 0;
+}
+
+static ssize_t
+omap_wdt_write(struct file *file, const char __user *data,
+ size_t len, loff_t *ppos)
+{
+ /* Refresh LOAD_TIME. */
+ if (len)
+ omap_wdt_ping();
+ return len;
+}
+
+static int
+omap_wdt_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ int new_margin;
+ static struct watchdog_info ident = {
+ .identity = "OMAP Watchdog",
+ .options = WDIOF_SETTIMEOUT,
+ .firmware_version = 0,
+ };
+
+ switch (cmd) {
+ default:
+ return -ENOIOCTLCMD;
+ case WDIOC_GETSUPPORT:
+ return copy_to_user((struct watchdog_info __user *)arg, &ident,
+ sizeof(ident));
+ case WDIOC_GETSTATUS:
+ return put_user(0, (int __user *)arg);
+ case WDIOC_GETBOOTSTATUS:
+ if (cpu_is_omap16xx())
+ return put_user(omap_readw(ARM_SYSST),
+ (int __user *)arg);
+ if (cpu_is_omap24xx())
+ return put_user(omap_prcm_get_reset_sources(),
+ (int __user *)arg);
+ case WDIOC_KEEPALIVE:
+ omap_wdt_ping();
+ return 0;
+ case WDIOC_SETTIMEOUT:
+ if (get_user(new_margin, (int __user *)arg))
+ return -EFAULT;
+ omap_wdt_adjust_timeout(new_margin);
+
+ omap_wdt_disable();
+ omap_wdt_set_timeout();
+ omap_wdt_enable();
+
+ omap_wdt_ping();
+ /* Fall */
+ case WDIOC_GETTIMEOUT:
+ return put_user(timer_margin, (int __user *)arg);
+ }
+}
+
+static struct file_operations omap_wdt_fops = {
+ .owner = THIS_MODULE,
+ .write = omap_wdt_write,
+ .ioctl = omap_wdt_ioctl,
+ .open = omap_wdt_open,
+ .release = omap_wdt_release,
+};
+
+static struct miscdevice omap_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &omap_wdt_fops
+};
+
+static int __init omap_wdt_probe(struct platform_device *pdev)
+{
+ struct resource *res, *mem;
+ int ret;
+
+ /* reserve static register mappings */
+ 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;
+
+ platform_set_drvdata(pdev, mem);
+
+ omap_wdt_users = 0;
+
+ if (cpu_is_omap16xx()) {
+ armwdt_ck = clk_get(&pdev->dev, "armwdt_ck");
+ if (IS_ERR(armwdt_ck)) {
+ ret = PTR_ERR(armwdt_ck);
+ armwdt_ck = NULL;
+ goto fail;
+ }
+ }
+
+ if (cpu_is_omap24xx()) {
+ mpu_wdt_ick = clk_get(&pdev->dev, "mpu_wdt_ick");
+ if (IS_ERR(mpu_wdt_ick)) {
+ ret = PTR_ERR(mpu_wdt_ick);
+ mpu_wdt_ick = NULL;
+ goto fail;
+ }
+ mpu_wdt_fck = clk_get(&pdev->dev, "mpu_wdt_fck");
+ if (IS_ERR(mpu_wdt_fck)) {
+ ret = PTR_ERR(mpu_wdt_fck);
+ mpu_wdt_fck = NULL;
+ goto fail;
+ }
+ }
+
+ omap_wdt_disable();
+ omap_wdt_adjust_timeout(timer_margin);
+
+ omap_wdt_miscdev.dev = &pdev->dev;
+ ret = misc_register(&omap_wdt_miscdev);
+ if (ret)
+ goto fail;
+
+ pr_info("OMAP Watchdog Timer: initial timeout %d sec\n", timer_margin);
+
+ /* autogate OCP interface clock */
+ omap_writel(0x01, OMAP_WATCHDOG_SYS_CONFIG);
+ return 0;
+
+fail:
+ if (armwdt_ck)
+ clk_put(armwdt_ck);
+ if (mpu_wdt_ick)
+ clk_put(mpu_wdt_ick);
+ if (mpu_wdt_fck)
+ clk_put(mpu_wdt_fck);
+ release_resource(mem);
+ return ret;
+}
+
+static void omap_wdt_shutdown(struct platform_device *pdev)
+{
+ omap_wdt_disable();
+}
+
+static int omap_wdt_remove(struct platform_device *pdev)
+{
+ struct resource *mem = platform_get_drvdata(pdev);
+ misc_deregister(&omap_wdt_miscdev);
+ release_resource(mem);
+ if (armwdt_ck)
+ clk_put(armwdt_ck);
+ if (mpu_wdt_ick)
+ clk_put(mpu_wdt_ick);
+ if (mpu_wdt_fck)
+ clk_put(mpu_wdt_fck);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+/* REVISIT ... not clear this is the best way to handle system suspend; and
+ * it's very inappropriate for selective device suspend (e.g. suspending this
+ * through sysfs rather than by stopping the watchdog daemon). Also, this
+ * may not play well enough with NOWAYOUT...
+ */
+
+static int omap_wdt_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ if (omap_wdt_users)
+ omap_wdt_disable();
+ return 0;
+}
+
+static int omap_wdt_resume(struct platform_device *pdev)
+{
+ if (omap_wdt_users) {
+ omap_wdt_enable();
+ omap_wdt_ping();
+ }
+ return 0;
+}
+
+#else
+#define omap_wdt_suspend NULL
+#define omap_wdt_resume NULL
+#endif
+
+static struct platform_driver omap_wdt_driver = {
+ .probe = omap_wdt_probe,
+ .remove = omap_wdt_remove,
+ .shutdown = omap_wdt_shutdown,
+ .suspend = omap_wdt_suspend,
+ .resume = omap_wdt_resume,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap_wdt",
+ },
+};
+
+static int __init omap_wdt_init(void)
+{
+ return platform_driver_register(&omap_wdt_driver);
+}
+
+static void __exit omap_wdt_exit(void)
+{
+ platform_driver_unregister(&omap_wdt_driver);
+}
+
+module_init(omap_wdt_init);
+module_exit(omap_wdt_exit);
+
+MODULE_AUTHOR("George G. Davis");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/char/watchdog/omap_wdt.h b/drivers/char/watchdog/omap_wdt.h
new file mode 100644
index 00000000000..52a532a5114
--- /dev/null
+++ b/drivers/char/watchdog/omap_wdt.h
@@ -0,0 +1,64 @@
+/*
+ * linux/drivers/char/watchdog/omap_wdt.h
+ *
+ * BRIEF MODULE DESCRIPTION
+ * OMAP Watchdog timer register definitions
+ *
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef _OMAP_WATCHDOG_H
+#define _OMAP_WATCHDOG_H
+
+#define OMAP1610_WATCHDOG_BASE 0xfffeb000
+#define OMAP2420_WATCHDOG_BASE 0x48022000 /*WDT Timer 2 */
+
+#ifdef CONFIG_ARCH_OMAP24XX
+#define OMAP_WATCHDOG_BASE OMAP2420_WATCHDOG_BASE
+#else
+#define OMAP_WATCHDOG_BASE OMAP1610_WATCHDOG_BASE
+#define RM_RSTST_WKUP 0
+#endif
+
+#define OMAP_WATCHDOG_REV (OMAP_WATCHDOG_BASE + 0x00)
+#define OMAP_WATCHDOG_SYS_CONFIG (OMAP_WATCHDOG_BASE + 0x10)
+#define OMAP_WATCHDOG_STATUS (OMAP_WATCHDOG_BASE + 0x14)
+#define OMAP_WATCHDOG_CNTRL (OMAP_WATCHDOG_BASE + 0x24)
+#define OMAP_WATCHDOG_CRR (OMAP_WATCHDOG_BASE + 0x28)
+#define OMAP_WATCHDOG_LDR (OMAP_WATCHDOG_BASE + 0x2c)
+#define OMAP_WATCHDOG_TGR (OMAP_WATCHDOG_BASE + 0x30)
+#define OMAP_WATCHDOG_WPS (OMAP_WATCHDOG_BASE + 0x34)
+#define OMAP_WATCHDOG_SPR (OMAP_WATCHDOG_BASE + 0x48)
+
+/* Using the prescaler, the OMAP watchdog could go for many
+ * months before firing. These limits work without scaling,
+ * with the 60 second default assumed by most tools and docs.
+ */
+#define TIMER_MARGIN_MAX (24 * 60 * 60) /* 1 day */
+#define TIMER_MARGIN_DEFAULT 60 /* 60 secs */
+#define TIMER_MARGIN_MIN 1
+
+#define PTV 0 /* prescale */
+#define GET_WLDR_VAL(secs) (0xffffffff - ((secs) * (32768/(1<<PTV))) + 1)
+
+#endif /* _OMAP_WATCHDOG_H */
diff --git a/drivers/char/watchdog/pcwd.c b/drivers/char/watchdog/pcwd.c
index cd7d1b6a5d9..6f8515db5b0 100644
--- a/drivers/char/watchdog/pcwd.c
+++ b/drivers/char/watchdog/pcwd.c
@@ -572,7 +572,7 @@ static int pcwd_ioctl(struct inode *inode, struct file *file,
switch(cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
if(copy_to_user(argp, &ident, sizeof(ident)))
diff --git a/drivers/char/watchdog/pcwd_pci.c b/drivers/char/watchdog/pcwd_pci.c
index c7cfd6dbfe1..2de6e497c14 100644
--- a/drivers/char/watchdog/pcwd_pci.c
+++ b/drivers/char/watchdog/pcwd_pci.c
@@ -541,7 +541,7 @@ static int pcipcwd_ioctl(struct inode *inode, struct file *file,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/pcwd_usb.c b/drivers/char/watchdog/pcwd_usb.c
index b7ae73dcdd0..77662cb0ac4 100644
--- a/drivers/char/watchdog/pcwd_usb.c
+++ b/drivers/char/watchdog/pcwd_usb.c
@@ -445,7 +445,7 @@ static int usb_pcwd_ioctl(struct inode *inode, struct file *file,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/pnx4008_wdt.c b/drivers/char/watchdog/pnx4008_wdt.c
new file mode 100644
index 00000000000..e7f0450a939
--- /dev/null
+++ b/drivers/char/watchdog/pnx4008_wdt.c
@@ -0,0 +1,362 @@
+/*
+ * drivers/char/watchdog/pnx4008_wdt.c
+ *
+ * Watchdog driver for PNX4008 board
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>,
+ * Vitaly Wool <vitalywool@gmail.com>
+ * Based on sa1100 driver,
+ * Copyright (C) 2000 Oleg Drokin <green@crimea.edu>
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. 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/config.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/init.h>
+#include <linux/bitops.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/spinlock.h>
+
+#include <asm/hardware.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+
+#define MODULE_NAME "PNX4008-WDT: "
+
+/* WatchDog Timer - Chapter 23 Page 207 */
+
+#define DEFAULT_HEARTBEAT 19
+#define MAX_HEARTBEAT 60
+
+/* Watchdog timer register set definition */
+#define WDTIM_INT(p) ((p) + 0x0)
+#define WDTIM_CTRL(p) ((p) + 0x4)
+#define WDTIM_COUNTER(p) ((p) + 0x8)
+#define WDTIM_MCTRL(p) ((p) + 0xC)
+#define WDTIM_MATCH0(p) ((p) + 0x10)
+#define WDTIM_EMR(p) ((p) + 0x14)
+#define WDTIM_PULSE(p) ((p) + 0x18)
+#define WDTIM_RES(p) ((p) + 0x1C)
+
+/* WDTIM_INT bit definitions */
+#define MATCH_INT 1
+
+/* WDTIM_CTRL bit definitions */
+#define COUNT_ENAB 1
+#define RESET_COUNT (1<<1)
+#define DEBUG_EN (1<<2)
+
+/* WDTIM_MCTRL bit definitions */
+#define MR0_INT 1
+#undef RESET_COUNT0
+#define RESET_COUNT0 (1<<2)
+#define STOP_COUNT0 (1<<2)
+#define M_RES1 (1<<3)
+#define M_RES2 (1<<4)
+#define RESFRC1 (1<<5)
+#define RESFRC2 (1<<6)
+
+/* WDTIM_EMR bit definitions */
+#define EXT_MATCH0 1
+#define MATCH_OUTPUT_HIGH (2<<4) /*a MATCH_CTRL setting */
+
+/* WDTIM_RES bit definitions */
+#define WDOG_RESET 1 /* read only */
+
+#define WDOG_COUNTER_RATE 13000000 /*the counter clock is 13 MHz fixed */
+
+static int nowayout = WATCHDOG_NOWAYOUT;
+static int heartbeat = DEFAULT_HEARTBEAT;
+
+static spinlock_t io_lock;
+static unsigned long wdt_status;
+#define WDT_IN_USE 0
+#define WDT_OK_TO_CLOSE 1
+#define WDT_REGION_INITED 2
+#define WDT_DEVICE_INITED 3
+
+static unsigned long boot_status;
+
+static struct resource *wdt_mem;
+static void __iomem *wdt_base;
+struct clk *wdt_clk;
+
+static void wdt_enable(void)
+{
+ spin_lock(&io_lock);
+
+ if (wdt_clk)
+ clk_set_rate(wdt_clk, 1);
+
+ /* stop counter, initiate counter reset */
+ __raw_writel(RESET_COUNT, WDTIM_CTRL(wdt_base));
+ /*wait for reset to complete. 100% guarantee event */
+ while (__raw_readl(WDTIM_COUNTER(wdt_base)))
+ cpu_relax();
+ /* internal and external reset, stop after that */
+ __raw_writel(M_RES2 | STOP_COUNT0 | RESET_COUNT0,
+ WDTIM_MCTRL(wdt_base));
+ /* configure match output */
+ __raw_writel(MATCH_OUTPUT_HIGH, WDTIM_EMR(wdt_base));
+ /* clear interrupt, just in case */
+ __raw_writel(MATCH_INT, WDTIM_INT(wdt_base));
+ /* the longest pulse period 65541/(13*10^6) seconds ~ 5 ms. */
+ __raw_writel(0xFFFF, WDTIM_PULSE(wdt_base));
+ __raw_writel(heartbeat * WDOG_COUNTER_RATE, WDTIM_MATCH0(wdt_base));
+ /*enable counter, stop when debugger active */
+ __raw_writel(COUNT_ENAB | DEBUG_EN, WDTIM_CTRL(wdt_base));
+
+ spin_unlock(&io_lock);
+}
+
+static void wdt_disable(void)
+{
+ spin_lock(&io_lock);
+
+ __raw_writel(0, WDTIM_CTRL(wdt_base)); /*stop counter */
+ if (wdt_clk)
+ clk_set_rate(wdt_clk, 0);
+
+ spin_unlock(&io_lock);
+}
+
+static int pnx4008_wdt_open(struct inode *inode, struct file *file)
+{
+ if (test_and_set_bit(WDT_IN_USE, &wdt_status))
+ return -EBUSY;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ wdt_enable();
+
+ return nonseekable_open(inode, file);
+}
+
+static ssize_t
+pnx4008_wdt_write(struct file *file, const char *data, size_t len,
+ loff_t * ppos)
+{
+ /* Can't seek (pwrite) on this device */
+ if (ppos != &file->f_pos)
+ return -ESPIPE;
+
+ if (len) {
+ if (!nowayout) {
+ size_t i;
+
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ for (i = 0; i != len; i++) {
+ char c;
+
+ if (get_user(c, data + i))
+ return -EFAULT;
+ if (c == 'V')
+ set_bit(WDT_OK_TO_CLOSE, &wdt_status);
+ }
+ }
+ wdt_enable();
+ }
+
+ return len;
+}
+
+static struct watchdog_info ident = {
+ .options = WDIOF_CARDRESET | WDIOF_MAGICCLOSE |
+ WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
+ .identity = "PNX4008 Watchdog",
+};
+
+static int
+pnx4008_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+ unsigned long arg)
+{
+ int ret = -ENOIOCTLCMD;
+ int time;
+
+ switch (cmd) {
+ case WDIOC_GETSUPPORT:
+ ret = copy_to_user((struct watchdog_info *)arg, &ident,
+ sizeof(ident)) ? -EFAULT : 0;
+ break;
+
+ case WDIOC_GETSTATUS:
+ ret = put_user(0, (int *)arg);
+ break;
+
+ case WDIOC_GETBOOTSTATUS:
+ ret = put_user(boot_status, (int *)arg);
+ break;
+
+ case WDIOC_SETTIMEOUT:
+ ret = get_user(time, (int *)arg);
+ if (ret)
+ break;
+
+ if (time <= 0 || time > MAX_HEARTBEAT) {
+ ret = -EINVAL;
+ break;
+ }
+
+ heartbeat = time;
+ wdt_enable();
+ /* Fall through */
+
+ case WDIOC_GETTIMEOUT:
+ ret = put_user(heartbeat, (int *)arg);
+ break;
+
+ case WDIOC_KEEPALIVE:
+ wdt_enable();
+ ret = 0;
+ break;
+ }
+ return ret;
+}
+
+static int pnx4008_wdt_release(struct inode *inode, struct file *file)
+{
+ if (!test_bit(WDT_OK_TO_CLOSE, &wdt_status))
+ printk(KERN_WARNING "WATCHDOG: Device closed unexpectdly\n");
+
+ wdt_disable();
+ clear_bit(WDT_IN_USE, &wdt_status);
+ clear_bit(WDT_OK_TO_CLOSE, &wdt_status);
+
+ return 0;
+}
+
+static struct file_operations pnx4008_wdt_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .write = pnx4008_wdt_write,
+ .ioctl = pnx4008_wdt_ioctl,
+ .open = pnx4008_wdt_open,
+ .release = pnx4008_wdt_release,
+};
+
+static struct miscdevice pnx4008_wdt_miscdev = {
+ .minor = WATCHDOG_MINOR,
+ .name = "watchdog",
+ .fops = &pnx4008_wdt_fops,
+};
+
+static int pnx4008_wdt_probe(struct platform_device *pdev)
+{
+ int ret = 0, size;
+ struct resource *res;
+
+ spin_lock_init(&io_lock);
+
+ if (heartbeat < 1 || heartbeat > MAX_HEARTBEAT)
+ heartbeat = DEFAULT_HEARTBEAT;
+
+ printk(KERN_INFO MODULE_NAME
+ "PNX4008 Watchdog Timer: heartbeat %d sec\n", heartbeat);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ printk(KERN_INFO MODULE_NAME
+ "failed to get memory region resouce\n");
+ return -ENOENT;
+ }
+
+ size = res->end - res->start + 1;
+ wdt_mem = request_mem_region(res->start, size, pdev->name);
+
+ if (wdt_mem == NULL) {
+ printk(KERN_INFO MODULE_NAME "failed to get memory region\n");
+ return -ENOENT;
+ }
+ wdt_base = (void __iomem *)IO_ADDRESS(res->start);
+
+ wdt_clk = clk_get(&pdev->dev, "wdt_ck");
+ if (!wdt_clk) {
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ goto out;
+ } else
+ clk_set_rate(wdt_clk, 1);
+
+ ret = misc_register(&pnx4008_wdt_miscdev);
+ if (ret < 0) {
+ printk(KERN_ERR MODULE_NAME "cannot register misc device\n");
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ clk_set_rate(wdt_clk, 0);
+ } else {
+ boot_status = (__raw_readl(WDTIM_RES(wdt_base)) & WDOG_RESET) ?
+ WDIOF_CARDRESET : 0;
+ wdt_disable(); /*disable for now */
+ set_bit(WDT_DEVICE_INITED, &wdt_status);
+ }
+
+out:
+ return ret;
+}
+
+static int pnx4008_wdt_remove(struct platform_device *pdev)
+{
+ misc_deregister(&pnx4008_wdt_miscdev);
+ if (wdt_clk) {
+ clk_set_rate(wdt_clk, 0);
+ clk_put(wdt_clk);
+ wdt_clk = NULL;
+ }
+ if (wdt_mem) {
+ release_resource(wdt_mem);
+ kfree(wdt_mem);
+ wdt_mem = NULL;
+ }
+ return 0;
+}
+
+static struct platform_driver platform_wdt_driver = {
+ .driver = {
+ .name = "watchdog",
+ },
+ .probe = pnx4008_wdt_probe,
+ .remove = pnx4008_wdt_remove,
+};
+
+static int __init pnx4008_wdt_init(void)
+{
+ return platform_driver_register(&platform_wdt_driver);
+}
+
+static void __exit pnx4008_wdt_exit(void)
+{
+ return platform_driver_unregister(&platform_wdt_driver);
+}
+
+module_init(pnx4008_wdt_init);
+module_exit(pnx4008_wdt_exit);
+
+MODULE_AUTHOR("MontaVista Software, Inc. <source@mvista.com>");
+MODULE_DESCRIPTION("PNX4008 Watchdog Driver");
+
+module_param(heartbeat, int, 0);
+MODULE_PARM_DESC(heartbeat,
+ "Watchdog heartbeat period in seconds from 1 to "
+ __MODULE_STRING(MAX_HEARTBEAT) ", default "
+ __MODULE_STRING(DEFAULT_HEARTBEAT));
+
+module_param(nowayout, int, 0);
+MODULE_PARM_DESC(nowayout,
+ "Set to 1 to keep watchdog running after device release");
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
diff --git a/drivers/char/watchdog/s3c2410_wdt.c b/drivers/char/watchdog/s3c2410_wdt.c
index be978e8ed75..b36a04ae9ab 100644
--- a/drivers/char/watchdog/s3c2410_wdt.c
+++ b/drivers/char/watchdog/s3c2410_wdt.c
@@ -62,7 +62,7 @@
#define CONFIG_S3C2410_WATCHDOG_ATBOOT (0)
#define CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME (15)
-static int nowayout = WATCHDOG_NOWAYOUT;
+static int nowayout = WATCHDOG_NOWAYOUT;
static int tmr_margin = CONFIG_S3C2410_WATCHDOG_DEFAULT_TIME;
static int tmr_atboot = CONFIG_S3C2410_WATCHDOG_ATBOOT;
static int soft_noboot = 0;
@@ -213,11 +213,10 @@ static int s3c2410wdt_open(struct inode *inode, struct file *file)
if(down_trylock(&open_lock))
return -EBUSY;
- if (nowayout) {
+ if (nowayout)
__module_get(THIS_MODULE);
- } else {
- allow_close = CLOSE_STATE_ALLOW;
- }
+
+ allow_close = CLOSE_STATE_NOT;
/* start the timer */
s3c2410wdt_start();
@@ -230,6 +229,7 @@ static int s3c2410wdt_release(struct inode *inode, struct file *file)
* Shut off the timer.
* Lock it in if it's a module and we set nowayout
*/
+
if (allow_close == CLOSE_STATE_ALLOW) {
s3c2410wdt_stop();
} else {
@@ -288,7 +288,7 @@ static int s3c2410wdt_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &s3c2410_wdt_ident,
diff --git a/drivers/char/watchdog/sa1100_wdt.c b/drivers/char/watchdog/sa1100_wdt.c
index 1fc16d99578..33c1137f17d 100644
--- a/drivers/char/watchdog/sa1100_wdt.c
+++ b/drivers/char/watchdog/sa1100_wdt.c
@@ -90,7 +90,7 @@ static struct watchdog_info ident = {
static int sa1100dog_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
int time;
void __user *argp = (void __user *)arg;
int __user *p = argp;
diff --git a/drivers/char/watchdog/sbc60xxwdt.c b/drivers/char/watchdog/sbc60xxwdt.c
index 4663c2fd53c..c7b2045bc76 100644
--- a/drivers/char/watchdog/sbc60xxwdt.c
+++ b/drivers/char/watchdog/sbc60xxwdt.c
@@ -235,7 +235,7 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_GETSTATUS:
diff --git a/drivers/char/watchdog/sbc_epx_c3.c b/drivers/char/watchdog/sbc_epx_c3.c
index bfc475dabe6..8882b427d24 100644
--- a/drivers/char/watchdog/sbc_epx_c3.c
+++ b/drivers/char/watchdog/sbc_epx_c3.c
@@ -141,7 +141,7 @@ static int epx_c3_ioctl(struct inode *inode, struct file *file,
return retval;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/sc1200wdt.c b/drivers/char/watchdog/sc1200wdt.c
index 7c3cf293a5a..d8d0f28e0ac 100644
--- a/drivers/char/watchdog/sc1200wdt.c
+++ b/drivers/char/watchdog/sc1200wdt.c
@@ -180,7 +180,7 @@ static int sc1200wdt_ioctl(struct inode *inode, struct file *file, unsigned int
switch (cmd) {
default:
- return -ENOIOCTLCMD; /* Keep Pavel Machek amused ;) */
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
if (copy_to_user(argp, &ident, sizeof ident))
diff --git a/drivers/char/watchdog/sc520_wdt.c b/drivers/char/watchdog/sc520_wdt.c
index 2c7c9db71be..caec37ba750 100644
--- a/drivers/char/watchdog/sc520_wdt.c
+++ b/drivers/char/watchdog/sc520_wdt.c
@@ -290,7 +290,7 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_GETSTATUS:
diff --git a/drivers/char/watchdog/scx200_wdt.c b/drivers/char/watchdog/scx200_wdt.c
index c561299a553..fc0e0347f9d 100644
--- a/drivers/char/watchdog/scx200_wdt.c
+++ b/drivers/char/watchdog/scx200_wdt.c
@@ -166,7 +166,7 @@ static int scx200_wdt_ioctl(struct inode *inode, struct file *file,
switch (cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
if(copy_to_user(argp, &ident, sizeof(ident)))
return -EFAULT;
diff --git a/drivers/char/watchdog/shwdt.c b/drivers/char/watchdog/shwdt.c
index 1355038f104..dc403629aeb 100644
--- a/drivers/char/watchdog/shwdt.c
+++ b/drivers/char/watchdog/shwdt.c
@@ -27,7 +27,7 @@
#include <linux/notifier.h>
#include <linux/ioport.h>
#include <linux/fs.h>
-
+#include <linux/mm.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/watchdog.h>
@@ -125,7 +125,6 @@ static void sh_wdt_start(void)
/**
* sh_wdt_stop - Stop the Watchdog
- *
* Stops the watchdog.
*/
static void sh_wdt_stop(void)
@@ -141,22 +140,20 @@ static void sh_wdt_stop(void)
/**
* sh_wdt_keepalive - Keep the Userspace Watchdog Alive
- *
* The Userspace watchdog got a KeepAlive: schedule the next heartbeat.
*/
-static void sh_wdt_keepalive(void)
+static inline void sh_wdt_keepalive(void)
{
next_heartbeat = jiffies + (heartbeat * HZ);
}
/**
* sh_wdt_set_heartbeat - Set the Userspace Watchdog heartbeat
- *
* Set the Userspace Watchdog heartbeat
*/
static int sh_wdt_set_heartbeat(int t)
{
- if ((t < 1) || (t > 3600)) /* arbitrary upper limit */
+ if (unlikely((t < 1) || (t > 3600))) /* arbitrary upper limit */
return -EINVAL;
heartbeat = t;
@@ -165,7 +162,6 @@ static int sh_wdt_set_heartbeat(int t)
/**
* sh_wdt_ping - Ping the Watchdog
- *
* @data: Unused
*
* Clears overflow bit, resets timer counter.
@@ -182,14 +178,13 @@ static void sh_wdt_ping(unsigned long data)
sh_wdt_write_cnt(0);
mod_timer(&timer, next_ping_period(clock_division_ratio));
- } else {
- printk(KERN_WARNING PFX "Heartbeat lost! Will not ping the watchdog\n");
- }
+ } else
+ printk(KERN_WARNING PFX "Heartbeat lost! Will not ping "
+ "the watchdog\n");
}
/**
* sh_wdt_open - Open the Device
- *
* @inode: inode of device
* @file: file handle of device
*
@@ -209,7 +204,6 @@ static int sh_wdt_open(struct inode *inode, struct file *file)
/**
* sh_wdt_close - Close the Device
- *
* @inode: inode of device
* @file: file handle of device
*
@@ -220,7 +214,8 @@ static int sh_wdt_close(struct inode *inode, struct file *file)
if (shwdt_expect_close == 42) {
sh_wdt_stop();
} else {
- printk(KERN_CRIT PFX "Unexpected close, not stopping watchdog!\n");
+ printk(KERN_CRIT PFX "Unexpected close, not "
+ "stopping watchdog!\n");
sh_wdt_keepalive();
}
@@ -232,7 +227,6 @@ static int sh_wdt_close(struct inode *inode, struct file *file)
/**
* sh_wdt_write - Write to Device
- *
* @file: file handle of device
* @buf: buffer to write
* @count: length of buffer
@@ -264,8 +258,56 @@ static ssize_t sh_wdt_write(struct file *file, const char *buf,
}
/**
- * sh_wdt_ioctl - Query Device
+ * sh_wdt_mmap - map WDT/CPG registers into userspace
+ * @file: file structure for the device
+ * @vma: VMA to map the registers into
+ *
+ * A simple mmap() implementation for the corner cases where the counter
+ * needs to be mapped in userspace directly. Due to the relatively small
+ * size of the area, neighbouring registers not necessarily tied to the
+ * CPG will also be accessible through the register page, so this remains
+ * configurable for users that really know what they're doing.
*
+ * Additionaly, the register page maps in the CPG register base relative
+ * to the nearest page-aligned boundary, which requires that userspace do
+ * the appropriate CPU subtype math for calculating the page offset for
+ * the counter value.
+ */
+static int sh_wdt_mmap(struct file *file, struct vm_area_struct *vma)
+{
+ int ret = -ENOSYS;
+
+#ifdef CONFIG_SH_WDT_MMAP
+ unsigned long addr;
+
+ /* Only support the simple cases where we map in a register page. */
+ if (((vma->vm_end - vma->vm_start) != PAGE_SIZE) || vma->vm_pgoff)
+ return -EINVAL;
+
+ /*
+ * Pick WTCNT as the start, it's usually the first register after the
+ * FRQCR, and neither one are generally page-aligned out of the box.
+ */
+ addr = WTCNT & ~(PAGE_SIZE - 1);
+
+ vma->vm_flags |= VM_IO;
+ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+
+ if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT,
+ PAGE_SIZE, vma->vm_page_prot)) {
+ printk(KERN_ERR PFX "%s: io_remap_pfn_range failed\n",
+ __FUNCTION__);
+ return -EAGAIN;
+ }
+
+ ret = 0;
+#endif
+
+ return ret;
+}
+
+/**
+ * sh_wdt_ioctl - Query Device
* @inode: inode of device
* @file: file handle of device
* @cmd: watchdog command
@@ -318,7 +360,7 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
return retval;
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
@@ -326,7 +368,6 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
/**
* sh_wdt_notify_sys - Notifier Handler
- *
* @this: notifier block
* @code: notifier event
* @unused: unused
@@ -337,9 +378,8 @@ static int sh_wdt_ioctl(struct inode *inode, struct file *file,
static int sh_wdt_notify_sys(struct notifier_block *this,
unsigned long code, void *unused)
{
- if (code == SYS_DOWN || code == SYS_HALT) {
+ if (code == SYS_DOWN || code == SYS_HALT)
sh_wdt_stop();
- }
return NOTIFY_DONE;
}
@@ -351,10 +391,12 @@ static const struct file_operations sh_wdt_fops = {
.ioctl = sh_wdt_ioctl,
.open = sh_wdt_open,
.release = sh_wdt_close,
+ .mmap = sh_wdt_mmap,
};
static struct watchdog_info sh_wdt_info = {
- .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE,
+ .options = WDIOF_KEEPALIVEPING | WDIOF_SETTIMEOUT |
+ WDIOF_MAGICCLOSE,
.firmware_version = 1,
.identity = "SH WDT",
};
@@ -371,7 +413,6 @@ static struct miscdevice sh_wdt_miscdev = {
/**
* sh_wdt_init - Initialize module
- *
* Registers the device and notifier handler. Actual device
* initialization is handled by sh_wdt_open().
*/
@@ -381,15 +422,15 @@ static int __init sh_wdt_init(void)
if ((clock_division_ratio < 0x5) || (clock_division_ratio > 0x7)) {
clock_division_ratio = WTCSR_CKS_4096;
- printk(KERN_INFO PFX "clock_division_ratio value must be 0x5<=x<=0x7, using %d\n",
- clock_division_ratio);
+ printk(KERN_INFO PFX "clock_division_ratio value must "
+ "be 0x5<=x<=0x7, using %d\n", clock_division_ratio);
}
- if (sh_wdt_set_heartbeat(heartbeat))
- {
+ rc = sh_wdt_set_heartbeat(heartbeat);
+ if (unlikely(rc)) {
heartbeat = WATCHDOG_HEARTBEAT;
- printk(KERN_INFO PFX "heartbeat value must be 1<=x<=3600, using %d\n",
- heartbeat);
+ printk(KERN_INFO PFX "heartbeat value must "
+ "be 1<=x<=3600, using %d\n", heartbeat);
}
init_timer(&timer);
@@ -397,15 +438,16 @@ static int __init sh_wdt_init(void)
timer.data = 0;
rc = register_reboot_notifier(&sh_wdt_notifier);
- if (rc) {
- printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n", rc);
+ if (unlikely(rc)) {
+ printk(KERN_ERR PFX "Can't register reboot notifier (err=%d)\n",
+ rc);
return rc;
}
rc = misc_register(&sh_wdt_miscdev);
- if (rc) {
- printk(KERN_ERR PFX "Can't register miscdev on minor=%d (err=%d)\n",
- sh_wdt_miscdev.minor, rc);
+ if (unlikely(rc)) {
+ printk(KERN_ERR PFX "Can't register miscdev on "
+ "minor=%d (err=%d)\n", sh_wdt_miscdev.minor, rc);
unregister_reboot_notifier(&sh_wdt_notifier);
return rc;
}
@@ -418,7 +460,6 @@ static int __init sh_wdt_init(void)
/**
* sh_wdt_exit - Deinitialize module
- *
* Unregisters the device and notifier handler. Actual device
* deinitialization is handled by sh_wdt_close().
*/
@@ -434,14 +475,13 @@ MODULE_LICENSE("GPL");
MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
module_param(clock_division_ratio, int, 0);
-MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). Defaults to 0x7.");
+MODULE_PARM_DESC(clock_division_ratio, "Clock division ratio. Valid ranges are from 0x5 (1.31ms) to 0x7 (5.25ms). (default=" __MODULE_STRING(clock_division_ratio) ")");
module_param(heartbeat, int, 0);
MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat in seconds. (1<=heartbeat<=3600, default=" __MODULE_STRING(WATCHDOG_HEARTBEAT) ")");
module_param(nowayout, int, 0);
-MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
+MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
module_init(sh_wdt_init);
module_exit(sh_wdt_exit);
-
diff --git a/drivers/char/watchdog/softdog.c b/drivers/char/watchdog/softdog.c
index ef8da517545..4067e1f8a36 100644
--- a/drivers/char/watchdog/softdog.c
+++ b/drivers/char/watchdog/softdog.c
@@ -203,7 +203,7 @@ static int softdog_ioctl(struct inode *inode, struct file *file,
};
switch (cmd) {
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident,
sizeof(ident)) ? -EFAULT : 0;
diff --git a/drivers/char/watchdog/w83627hf_wdt.c b/drivers/char/watchdog/w83627hf_wdt.c
index 13f16d41c2f..b4adc527e68 100644
--- a/drivers/char/watchdog/w83627hf_wdt.c
+++ b/drivers/char/watchdog/w83627hf_wdt.c
@@ -223,7 +223,7 @@ wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/w83877f_wdt.c b/drivers/char/watchdog/w83877f_wdt.c
index ccf6c091594..b0e5f84d6ba 100644
--- a/drivers/char/watchdog/w83877f_wdt.c
+++ b/drivers/char/watchdog/w83877f_wdt.c
@@ -252,7 +252,7 @@ static int fop_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
case WDIOC_GETSTATUS:
diff --git a/drivers/char/watchdog/w83977f_wdt.c b/drivers/char/watchdog/w83977f_wdt.c
index 98f4e17db70..2c8d5d8bd4e 100644
--- a/drivers/char/watchdog/w83977f_wdt.c
+++ b/drivers/char/watchdog/w83977f_wdt.c
@@ -393,7 +393,7 @@ static int wdt_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(uarg.ident, &ident, sizeof(ident)) ? -EFAULT : 0;
diff --git a/drivers/char/watchdog/wafer5823wdt.c b/drivers/char/watchdog/wafer5823wdt.c
index 2bb6a9d6ad2..163e028ef9e 100644
--- a/drivers/char/watchdog/wafer5823wdt.c
+++ b/drivers/char/watchdog/wafer5823wdt.c
@@ -174,7 +174,7 @@ static int wafwdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd
}
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
return 0;
}
diff --git a/drivers/char/watchdog/wdrtas.c b/drivers/char/watchdog/wdrtas.c
index 5c38cdf4173..1d64e277567 100644
--- a/drivers/char/watchdog/wdrtas.c
+++ b/drivers/char/watchdog/wdrtas.c
@@ -385,7 +385,7 @@ wdrtas_ioctl(struct inode *inode, struct file *file,
return put_user(wdrtas_interval, argp);
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
}
}
diff --git a/drivers/char/watchdog/wdt.c b/drivers/char/watchdog/wdt.c
index 70be81e39a6..13f23f4a223 100644
--- a/drivers/char/watchdog/wdt.c
+++ b/drivers/char/watchdog/wdt.c
@@ -341,7 +341,7 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
diff --git a/drivers/char/watchdog/wdt285.c b/drivers/char/watchdog/wdt285.c
index 6555fb844f2..89a249e23fd 100644
--- a/drivers/char/watchdog/wdt285.c
+++ b/drivers/char/watchdog/wdt285.c
@@ -137,7 +137,7 @@ watchdog_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg)
{
unsigned int new_margin;
- int ret = -ENOIOCTLCMD;
+ int ret = -ENOTTY;
switch(cmd) {
case WDIOC_GETSUPPORT:
diff --git a/drivers/char/watchdog/wdt977.c b/drivers/char/watchdog/wdt977.c
index a0935bc775f..6253041b235 100644
--- a/drivers/char/watchdog/wdt977.c
+++ b/drivers/char/watchdog/wdt977.c
@@ -361,7 +361,7 @@ static int wdt977_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(uarg.ident, &ident,
diff --git a/drivers/char/watchdog/wdt_pci.c b/drivers/char/watchdog/wdt_pci.c
index 5918ca2c9c3..74d8cf836e1 100644
--- a/drivers/char/watchdog/wdt_pci.c
+++ b/drivers/char/watchdog/wdt_pci.c
@@ -386,7 +386,7 @@ static int wdtpci_ioctl(struct inode *inode, struct file *file, unsigned int cmd
switch(cmd)
{
default:
- return -ENOIOCTLCMD;
+ return -ENOTTY;
case WDIOC_GETSUPPORT:
return copy_to_user(argp, &ident, sizeof(ident))?-EFAULT:0;
diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c
index d35a9f06ab7..2caaf71d80c 100644
--- a/drivers/cpufreq/cpufreq.c
+++ b/drivers/cpufreq/cpufreq.c
@@ -994,7 +994,7 @@ static int cpufreq_suspend(struct sys_device * sysdev, pm_message_t pmsg)
unsigned int cur_freq = 0;
struct cpufreq_policy *cpu_policy;
- dprintk("resuming cpu %u\n", cpu);
+ dprintk("suspending cpu %u\n", cpu);
if (!cpu_online(cpu))
return 0;
diff --git a/drivers/eisa/eisa-bus.c b/drivers/eisa/eisa-bus.c
index 6078e2f5881..3a365e159d8 100644
--- a/drivers/eisa/eisa-bus.c
+++ b/drivers/eisa/eisa-bus.c
@@ -128,9 +128,23 @@ static int eisa_bus_match (struct device *dev, struct device_driver *drv)
return 0;
}
+static int eisa_bus_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
+ struct eisa_device *edev = to_eisa_device(dev);
+ int i = 0;
+ int length = 0;
+
+ add_uevent_var(envp, num_envp, &i, buffer, buffer_size, &length,
+ "MODALIAS=" EISA_DEVICE_MODALIAS_FMT, edev->id.sig);
+ envp[i] = NULL;
+ return 0;
+}
+
struct bus_type eisa_bus_type = {
.name = "eisa",
.match = eisa_bus_match,
+ .uevent = eisa_bus_uevent,
};
int eisa_driver_register (struct eisa_driver *edrv)
@@ -160,6 +174,14 @@ static ssize_t eisa_show_state (struct device *dev, struct device_attribute *att
static DEVICE_ATTR(enabled, S_IRUGO, eisa_show_state, NULL);
+static ssize_t eisa_show_modalias (struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct eisa_device *edev = to_eisa_device (dev);
+ return sprintf (buf, EISA_DEVICE_MODALIAS_FMT "\n", edev->id.sig);
+}
+
+static DEVICE_ATTR(modalias, S_IRUGO, eisa_show_modalias, NULL);
+
static int __init eisa_init_device (struct eisa_root_device *root,
struct eisa_device *edev,
int slot)
@@ -209,6 +231,7 @@ static int __init eisa_register_device (struct eisa_device *edev)
device_create_file (&edev->dev, &dev_attr_signature);
device_create_file (&edev->dev, &dev_attr_enabled);
+ device_create_file (&edev->dev, &dev_attr_modalias);
return 0;
}
diff --git a/drivers/fc4/fc.c b/drivers/fc4/fc.c
index 1a159e8843c..22d17474755 100644
--- a/drivers/fc4/fc.c
+++ b/drivers/fc4/fc.c
@@ -974,7 +974,6 @@ int fcp_scsi_dev_reset(Scsi_Cmnd *SCpnt)
*/
fc->rst_pkt->device->host->eh_action = &sem;
- fc->rst_pkt->request->rq_status = RQ_SCSI_BUSY;
fc->rst_pkt->done = fcp_scsi_reset_done;
diff --git a/drivers/firmware/dell_rbu.c b/drivers/firmware/dell_rbu.c
index 23b08668545..fc17599c905 100644
--- a/drivers/firmware/dell_rbu.c
+++ b/drivers/firmware/dell_rbu.c
@@ -227,7 +227,7 @@ static int packetize_data(void *data, size_t length)
int packet_length;
u8 *temp;
u8 *end = (u8 *) data + length;
- pr_debug("packetize_data: data length %d\n", length);
+ pr_debug("packetize_data: data length %zd\n", length);
if (!rbu_data.packetsize) {
printk(KERN_WARNING
"dell_rbu: packetsize not specified\n");
@@ -249,7 +249,7 @@ static int packetize_data(void *data, size_t length)
if ((rc = create_packet(temp, packet_length)))
return rc;
- pr_debug("%lu:%lu\n", temp, (end - temp));
+ pr_debug("%p:%lu\n", temp, (end - temp));
temp += packet_length;
}
diff --git a/drivers/firmware/dmi_scan.c b/drivers/firmware/dmi_scan.c
index b9e3886d9e1..b8b596d5778 100644
--- a/drivers/firmware/dmi_scan.c
+++ b/drivers/firmware/dmi_scan.c
@@ -123,6 +123,26 @@ static void __init dmi_save_devices(struct dmi_header *dm)
dev->type = *d++ & 0x7f;
dev->name = dmi_string(dm, *d);
dev->device_data = NULL;
+ list_add(&dev->list, &dmi_devices);
+ }
+}
+
+static void __init dmi_save_oem_strings_devices(struct dmi_header *dm)
+{
+ int i, count = *(u8 *)(dm + 1);
+ struct dmi_device *dev;
+
+ for (i = 1; i <= count; i++) {
+ dev = dmi_alloc(sizeof(*dev));
+ if (!dev) {
+ printk(KERN_ERR
+ "dmi_save_oem_strings_devices: out of memory.\n");
+ break;
+ }
+
+ dev->type = DMI_DEV_TYPE_OEM_STRING;
+ dev->name = dmi_string(dm, i);
+ dev->device_data = NULL;
list_add(&dev->list, &dmi_devices);
}
@@ -181,6 +201,9 @@ static void __init dmi_decode(struct dmi_header *dm)
case 10: /* Onboard Devices Information */
dmi_save_devices(dm);
break;
+ case 11: /* OEM Strings */
+ dmi_save_oem_strings_devices(dm);
+ break;
case 38: /* IPMI Device Information */
dmi_save_ipmi_device(dm);
}
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 0e31a0c496e..9b88b25b6ed 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -53,7 +53,7 @@ config SENSORS_ADM1021
config SENSORS_ADM1025
tristate "Analog Devices ADM1025 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for Analog Devices ADM1025
@@ -94,6 +94,16 @@ config SENSORS_ADM9240
This driver can also be built as a module. If so, the module
will be called adm9240.
+config SENSORS_K8TEMP
+ tristate "AMD K8 processor sensor"
+ depends on HWMON && X86 && PCI && EXPERIMENTAL
+ help
+ If you say yes here you get support for the temperature
+ sensor(s) inside your AMD K8 CPU.
+
+ This driver can also be built as a module. If so, the module
+ will be called k8temp.
+
config SENSORS_ASB100
tristate "Asus ASB100 Bach"
depends on HWMON && I2C && EXPERIMENTAL
@@ -121,7 +131,7 @@ config SENSORS_ATXP1
config SENSORS_DS1621
tristate "Dallas Semiconductor DS1621 and DS1625"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Dallas Semiconductor
DS1621 and DS1625 sensor chips.
@@ -141,7 +151,7 @@ config SENSORS_F71805F
config SENSORS_FSCHER
tristate "FSC Hermes"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Fujitsu Siemens
Computers Hermes sensor chips.
@@ -151,7 +161,7 @@ config SENSORS_FSCHER
config SENSORS_FSCPOS
tristate "FSC Poseidon"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for Fujitsu Siemens
Computers Poseidon sensor chips.
@@ -171,7 +181,7 @@ config SENSORS_GL518SM
config SENSORS_GL520SM
tristate "Genesys Logic GL520SM"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for Genesys Logic GL520SM
@@ -186,15 +196,15 @@ config SENSORS_IT87
select I2C_ISA
select HWMON_VID
help
- If you say yes here you get support for ITE IT87xx sensor chips
- and clones: SiS960.
+ If you say yes here you get support for ITE IT8705F, IT8712F,
+ IT8716F and IT8718F sensor chips, and the SiS960 clone.
This driver can also be built as a module. If so, the module
will be called it87.
config SENSORS_LM63
tristate "National Semiconductor LM63"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for the National Semiconductor
LM63 remote diode digital temperature sensor with integrated fan
@@ -231,7 +241,7 @@ config SENSORS_LM75
config SENSORS_LM77
tristate "National Semiconductor LM77"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for National Semiconductor LM77
sensor chips.
@@ -241,7 +251,7 @@ config SENSORS_LM77
config SENSORS_LM78
tristate "National Semiconductor LM78 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select I2C_ISA
select HWMON_VID
help
@@ -284,7 +294,7 @@ config SENSORS_LM85
config SENSORS_LM87
tristate "National Semiconductor LM87"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select HWMON_VID
help
If you say yes here you get support for National Semiconductor LM87
@@ -309,7 +319,7 @@ config SENSORS_LM90
config SENSORS_LM92
tristate "National Semiconductor LM92 and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for National Semiconductor LM92
and Maxim MAX6635 sensor chips.
@@ -319,7 +329,7 @@ config SENSORS_LM92
config SENSORS_MAX1619
tristate "Maxim MAX1619 sensor chip"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
help
If you say yes here you get support for MAX1619 sensor chip.
@@ -354,7 +364,7 @@ config SENSORS_SIS5595
config SENSORS_SMSC47M1
tristate "SMSC LPC47M10x and compatibles"
- depends on HWMON && I2C && EXPERIMENTAL
+ depends on HWMON && I2C
select I2C_ISA
help
If you say yes here you get support for the integrated fan
@@ -407,8 +417,19 @@ config SENSORS_VIA686A
This driver can also be built as a module. If so, the module
will be called via686a.
+config SENSORS_VT1211
+ tristate "VIA VT1211"
+ depends on HWMON && EXPERIMENTAL
+ select HWMON_VID
+ help
+ If you say yes here then you get support for hardware monitoring
+ features of the VIA VT1211 Super-I/O chip.
+
+ This driver can also be built as a module. If so, the module
+ will be called vt1211.
+
config SENSORS_VT8231
- tristate "VT8231"
+ tristate "VIA VT8231"
depends on HWMON && I2C && PCI && EXPERIMENTAL
select HWMON_VID
select I2C_ISA
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 31415843a91..af01cc64f7d 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SENSORS_GL518SM) += gl518sm.o
obj-$(CONFIG_SENSORS_GL520SM) += gl520sm.o
obj-$(CONFIG_SENSORS_HDAPS) += hdaps.o
obj-$(CONFIG_SENSORS_IT87) += it87.o
+obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o
obj-$(CONFIG_SENSORS_LM63) += lm63.o
obj-$(CONFIG_SENSORS_LM70) += lm70.o
obj-$(CONFIG_SENSORS_LM75) += lm75.o
@@ -45,6 +46,7 @@ obj-$(CONFIG_SENSORS_SMSC47B397)+= smsc47b397.o
obj-$(CONFIG_SENSORS_SMSC47M1) += smsc47m1.o
obj-$(CONFIG_SENSORS_SMSC47M192)+= smsc47m192.o
obj-$(CONFIG_SENSORS_VIA686A) += via686a.o
+obj-$(CONFIG_SENSORS_VT1211) += vt1211.o
obj-$(CONFIG_SENSORS_VT8231) += vt8231.o
obj-$(CONFIG_SENSORS_W83627EHF) += w83627ehf.o
obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o
diff --git a/drivers/hwmon/abituguru.c b/drivers/hwmon/abituguru.c
index 35ad1b03272..e5cb0fdab9b 100644
--- a/drivers/hwmon/abituguru.c
+++ b/drivers/hwmon/abituguru.c
@@ -1354,13 +1354,39 @@ LEAVE_UPDATE:
return NULL;
}
+#ifdef CONFIG_PM
+static int abituguru_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct abituguru_data *data = platform_get_drvdata(pdev);
+ /* make sure all communications with the uguru are done and no new
+ ones are started */
+ mutex_lock(&data->update_lock);
+ return 0;
+}
+
+static int abituguru_resume(struct platform_device *pdev)
+{
+ struct abituguru_data *data = platform_get_drvdata(pdev);
+ /* See if the uGuru is still ready */
+ if (inb_p(data->addr + ABIT_UGURU_DATA) != ABIT_UGURU_STATUS_INPUT)
+ data->uguru_ready = 0;
+ mutex_unlock(&data->update_lock);
+ return 0;
+}
+#else
+#define abituguru_suspend NULL
+#define abituguru_resume NULL
+#endif /* CONFIG_PM */
+
static struct platform_driver abituguru_driver = {
.driver = {
.owner = THIS_MODULE,
.name = ABIT_UGURU_NAME,
},
- .probe = abituguru_probe,
- .remove = __devexit_p(abituguru_remove),
+ .probe = abituguru_probe,
+ .remove = __devexit_p(abituguru_remove),
+ .suspend = abituguru_suspend,
+ .resume = abituguru_resume,
};
static int __init abituguru_detect(void)
diff --git a/drivers/hwmon/adm1021.c b/drivers/hwmon/adm1021.c
index 2b6e74dd4a8..c466329b2ef 100644
--- a/drivers/hwmon/adm1021.c
+++ b/drivers/hwmon/adm1021.c
@@ -190,6 +190,21 @@ static int adm1021_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1021_detect);
}
+static struct attribute *adm1021_attributes[] = {
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group adm1021_group = {
+ .attrs = adm1021_attributes,
+};
+
static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i;
@@ -287,22 +302,19 @@ static int adm1021_detect(struct i2c_adapter *adapter, int address, int kind)
adm1021_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1021_group)))
+ goto error2;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error2;
+ goto error3;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+error3:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1021_group);
error2:
i2c_detach_client(new_client);
error1:
@@ -326,6 +338,7 @@ static int adm1021_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1021_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/adm1025.c b/drivers/hwmon/adm1025.c
index a4c859c9fbf..8c562885b54 100644
--- a/drivers/hwmon/adm1025.c
+++ b/drivers/hwmon/adm1025.c
@@ -315,6 +315,49 @@ static int adm1025_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1025_detect);
}
+static struct attribute *adm1025_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1025_group = {
+ .attrs = adm1025_attributes,
+};
+
+static struct attribute *adm1025_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ NULL
+};
+
+static const struct attribute_group adm1025_group_opt = {
+ .attrs = adm1025_attributes_opt,
+};
+
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
@@ -415,46 +458,31 @@ static int adm1025_detect(struct i2c_adapter *adapter, int address, int kind)
adm1025_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1025_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
/* Pin 11 is either in4 (+12V) or VID4 */
if (!(config & 0x20)) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
}
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1025_group);
+ sysfs_remove_group(&new_client->dev.kobj, &adm1025_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -511,6 +539,8 @@ static int adm1025_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1025_group);
+ sysfs_remove_group(&client->dev.kobj, &adm1025_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/adm1026.c b/drivers/hwmon/adm1026.c
index 6d4f8b8d358..b4618b2705f 100644
--- a/drivers/hwmon/adm1026.c
+++ b/drivers/hwmon/adm1026.c
@@ -323,15 +323,6 @@ static int adm1026_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1026_detect);
}
-static int adm1026_detach_client(struct i2c_client *client)
-{
- struct adm1026_data *data = i2c_get_clientdata(client);
- hwmon_device_unregister(data->class_dev);
- i2c_detach_client(client);
- kfree(data);
- return 0;
-}
-
static int adm1026_read_value(struct i2c_client *client, u8 reg)
{
int res;
@@ -1450,6 +1441,135 @@ static DEVICE_ATTR(temp1_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
static DEVICE_ATTR(temp2_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
static DEVICE_ATTR(temp3_auto_point2_pwm, S_IRUGO, show_auto_pwm_max, NULL);
+static struct attribute *adm1026_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_in9_input.dev_attr.attr,
+ &sensor_dev_attr_in9_max.dev_attr.attr,
+ &sensor_dev_attr_in9_min.dev_attr.attr,
+ &sensor_dev_attr_in10_input.dev_attr.attr,
+ &sensor_dev_attr_in10_max.dev_attr.attr,
+ &sensor_dev_attr_in10_min.dev_attr.attr,
+ &sensor_dev_attr_in11_input.dev_attr.attr,
+ &sensor_dev_attr_in11_max.dev_attr.attr,
+ &sensor_dev_attr_in11_min.dev_attr.attr,
+ &sensor_dev_attr_in12_input.dev_attr.attr,
+ &sensor_dev_attr_in12_max.dev_attr.attr,
+ &sensor_dev_attr_in12_min.dev_attr.attr,
+ &sensor_dev_attr_in13_input.dev_attr.attr,
+ &sensor_dev_attr_in13_max.dev_attr.attr,
+ &sensor_dev_attr_in13_min.dev_attr.attr,
+ &sensor_dev_attr_in14_input.dev_attr.attr,
+ &sensor_dev_attr_in14_max.dev_attr.attr,
+ &sensor_dev_attr_in14_min.dev_attr.attr,
+ &sensor_dev_attr_in15_input.dev_attr.attr,
+ &sensor_dev_attr_in15_max.dev_attr.attr,
+ &sensor_dev_attr_in15_min.dev_attr.attr,
+ &sensor_dev_attr_in16_input.dev_attr.attr,
+ &sensor_dev_attr_in16_max.dev_attr.attr,
+ &sensor_dev_attr_in16_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan8_input.dev_attr.attr,
+ &sensor_dev_attr_fan8_div.dev_attr.attr,
+ &sensor_dev_attr_fan8_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+ &dev_attr_temp1_crit_enable.attr,
+ &dev_attr_temp2_crit_enable.attr,
+ &dev_attr_temp3_crit_enable.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_alarm_mask.attr,
+ &dev_attr_gpio.attr,
+ &dev_attr_gpio_mask.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3_enable.attr,
+ &dev_attr_temp1_auto_point1_pwm.attr,
+ &dev_attr_temp2_auto_point1_pwm.attr,
+ &dev_attr_temp3_auto_point1_pwm.attr,
+ &dev_attr_temp1_auto_point2_pwm.attr,
+ &dev_attr_temp2_auto_point2_pwm.attr,
+ &dev_attr_temp3_auto_point2_pwm.attr,
+ &dev_attr_analog_out.attr,
+ NULL
+};
+
+static const struct attribute_group adm1026_group = {
+ .attrs = adm1026_attributes,
+};
+
static int adm1026_detect(struct i2c_adapter *adapter, int address,
int kind)
{
@@ -1554,145 +1674,20 @@ static int adm1026_detect(struct i2c_adapter *adapter, int address,
adm1026_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1026_group)))
+ goto exitdetach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exitdetach;
+ goto exitremove;
}
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in9_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in10_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in11_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in12_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in13_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in14_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in15_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in16_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan8_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_offset.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_offset.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_offset.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point1_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point1_temp_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_auto_point2_temp.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_crit.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit_enable);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_alarm_mask);
- device_create_file(&new_client->dev, &dev_attr_gpio);
- device_create_file(&new_client->dev, &dev_attr_gpio_mask);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm3);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm3_enable);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_point1_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_point2_pwm);
- device_create_file(&new_client->dev, &dev_attr_analog_out);
return 0;
/* Error out and cleanup code */
+exitremove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1026_group);
exitdetach:
i2c_detach_client(new_client);
exitfree:
@@ -1700,6 +1695,17 @@ exitfree:
exit:
return err;
}
+
+static int adm1026_detach_client(struct i2c_client *client)
+{
+ struct adm1026_data *data = i2c_get_clientdata(client);
+ hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1026_group);
+ i2c_detach_client(client);
+ kfree(data);
+ return 0;
+}
+
static int __init sm_adm1026_init(void)
{
return i2c_add_driver(&adm1026_driver);
diff --git a/drivers/hwmon/adm1031.c b/drivers/hwmon/adm1031.c
index 3bf2da621ae..122683fc91d 100644
--- a/drivers/hwmon/adm1031.c
+++ b/drivers/hwmon/adm1031.c
@@ -730,6 +730,61 @@ static int adm1031_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, adm1031_detect);
}
+static struct attribute *adm1031_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_auto_fan1_channel.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_crit.attr,
+
+ &dev_attr_auto_temp1_off.attr,
+ &dev_attr_auto_temp1_min.attr,
+ &dev_attr_auto_temp1_max.attr,
+
+ &dev_attr_auto_temp2_off.attr,
+ &dev_attr_auto_temp2_min.attr,
+ &dev_attr_auto_temp2_max.attr,
+
+ &dev_attr_auto_fan1_min_pwm.attr,
+
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group adm1031_group = {
+ .attrs = adm1031_attributes,
+};
+
+static struct attribute *adm1031_attributes_opt[] = {
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_auto_fan2_channel.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_crit.attr,
+ &dev_attr_auto_temp3_off.attr,
+ &dev_attr_auto_temp3_min.attr,
+ &dev_attr_auto_temp3_max.attr,
+ &dev_attr_auto_fan2_min_pwm.attr,
+ NULL
+};
+
+static const struct attribute_group adm1031_group_opt = {
+ .attrs = adm1031_attributes_opt,
+};
+
/* This function is called by i2c_probe */
static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -789,57 +844,26 @@ static int adm1031_detect(struct i2c_adapter *adapter, int address, int kind)
adm1031_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm1031_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_auto_fan1_channel);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
-
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp1_max);
-
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp2_max);
-
- device_create_file(&new_client->dev, &dev_attr_auto_fan1_min_pwm);
-
- device_create_file(&new_client->dev, &dev_attr_alarms);
if (kind == adm1031) {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev,
- &dev_attr_auto_fan2_channel);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_off);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_auto_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_auto_fan2_min_pwm);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &adm1031_group_opt)))
+ goto exit_remove;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
}
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm1031_group);
+ sysfs_remove_group(&new_client->dev.kobj, &adm1031_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -854,6 +878,8 @@ static int adm1031_detach_client(struct i2c_client *client)
int ret;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm1031_group);
+ sysfs_remove_group(&client->dev.kobj, &adm1031_group_opt);
if ((ret = i2c_detach_client(client)) != 0) {
return ret;
}
diff --git a/drivers/hwmon/adm9240.c b/drivers/hwmon/adm9240.c
index 43f6991b588..377961c4a41 100644
--- a/drivers/hwmon/adm9240.c
+++ b/drivers/hwmon/adm9240.c
@@ -465,6 +465,45 @@ static ssize_t chassis_clear(struct device *dev,
}
static DEVICE_ATTR(chassis_clear, S_IWUSR, NULL, chassis_clear);
+static struct attribute *adm9240_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &dev_attr_temp1_input.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_aout_output.attr,
+ &dev_attr_chassis_clear.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group adm9240_group = {
+ .attrs = adm9240_attributes,
+};
+
/*** sensor chip detect and driver install ***/
@@ -548,72 +587,19 @@ static int adm9240_detect(struct i2c_adapter *adapter, int address, int kind)
adm9240_init_client(new_client);
/* populate sysfs filesystem */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &adm9240_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_aout_output);
- device_create_file(&new_client->dev, &dev_attr_chassis_clear);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &adm9240_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -635,6 +621,7 @@ static int adm9240_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &adm9240_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/asb100.c b/drivers/hwmon/asb100.c
index facc1ccb833..57b1c7b7ac3 100644
--- a/drivers/hwmon/asb100.c
+++ b/drivers/hwmon/asb100.c
@@ -298,12 +298,6 @@ sysfs_in(4);
sysfs_in(5);
sysfs_in(6);
-#define device_create_file_in(client, offset) do { \
- device_create_file(&client->dev, &dev_attr_in##offset##_input); \
- device_create_file(&client->dev, &dev_attr_in##offset##_min); \
- device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
/* 3 Fans */
static ssize_t show_fan(struct device *dev, char *buf, int nr)
{
@@ -421,12 +415,6 @@ sysfs_fan(1);
sysfs_fan(2);
sysfs_fan(3);
-#define device_create_file_fan(client, offset) do { \
- device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
/* 4 Temp. Sensors */
static int sprintf_temp_from_reg(u16 reg, char *buf, int nr)
{
@@ -515,12 +503,6 @@ sysfs_temp(3);
sysfs_temp(4);
/* VID */
-#define device_create_file_temp(client, num) do { \
- device_create_file(&client->dev, &dev_attr_temp##num##_input); \
- device_create_file(&client->dev, &dev_attr_temp##num##_max); \
- device_create_file(&client->dev, &dev_attr_temp##num##_max_hyst); \
-} while (0)
-
static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
{
struct asb100_data *data = asb100_update_device(dev);
@@ -528,8 +510,6 @@ static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
/* VRM */
static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
@@ -549,8 +529,6 @@ static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const
/* Alarms */
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm);
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -559,8 +537,6 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms)
/* 1 PWM */
static ssize_t show_pwm1(struct device *dev, struct device_attribute *attr, char *buf)
@@ -607,10 +583,65 @@ static ssize_t set_pwm_enable1(struct device *dev, struct device_attribute *attr
static DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, show_pwm1, set_pwm1);
static DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
show_pwm_enable1, set_pwm_enable1);
-#define device_create_file_pwm1(client) do { \
- device_create_file(&new_client->dev, &dev_attr_pwm1); \
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable); \
-} while (0)
+
+static struct attribute *asb100_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_max_hyst.attr,
+ &dev_attr_temp4_input.attr,
+ &dev_attr_temp4_max.attr,
+ &dev_attr_temp4_max_hyst.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+
+ NULL
+};
+
+static const struct attribute_group asb100_group = {
+ .attrs = asb100_attributes,
+};
/* This function is called when:
asb100_driver is inserted (when this module is loaded), for each
@@ -810,38 +841,19 @@ static int asb100_detect(struct i2c_adapter *adapter, int address, int kind)
data->fan_min[2] = asb100_read_value(new_client, ASB100_REG_FAN_MIN(2));
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &asb100_group)))
+ goto ERROR3;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto ERROR3;
+ goto ERROR4;
}
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
- device_create_file_temp(new_client, 4);
-
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
-
- device_create_file_alarms(new_client);
-
- device_create_file_pwm1(new_client);
-
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &asb100_group);
ERROR3:
i2c_detach_client(data->lm75[1]);
i2c_detach_client(data->lm75[0]);
@@ -861,8 +873,10 @@ static int asb100_detach_client(struct i2c_client *client)
int err;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &asb100_group);
+ }
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/atxp1.c b/drivers/hwmon/atxp1.c
index 728a1e8b919..0ccdd0750c4 100644
--- a/drivers/hwmon/atxp1.c
+++ b/drivers/hwmon/atxp1.c
@@ -27,6 +27,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("System voltages control via Attansic ATXP1");
@@ -116,8 +117,7 @@ static ssize_t atxp1_storevcore(struct device *dev, struct device_attribute *att
{
struct atxp1_data *data;
struct i2c_client *client;
- char vid;
- char cvid;
+ int vid, cvid;
unsigned int vcore;
client = to_i2c_client(dev);
@@ -251,6 +251,17 @@ static ssize_t atxp1_storegpio2(struct device *dev, struct device_attribute *att
*/
static DEVICE_ATTR(gpio2, S_IRUGO | S_IWUSR, atxp1_showgpio2, atxp1_storegpio2);
+static struct attribute *atxp1_attributes[] = {
+ &dev_attr_gpio1.attr,
+ &dev_attr_gpio2.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group atxp1_group = {
+ .attrs = atxp1_attributes,
+};
+
static int atxp1_attach_adapter(struct i2c_adapter *adapter)
{
@@ -320,21 +331,23 @@ static int atxp1_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_free;
}
+ /* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &atxp1_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_gpio1);
- device_create_file(&new_client->dev, &dev_attr_gpio2);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
dev_info(&new_client->dev, "Using VRM: %d.%d\n",
data->vrm / 10, data->vrm % 10);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &atxp1_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -349,6 +362,7 @@ static int atxp1_detach_client(struct i2c_client * client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &atxp1_group);
err = i2c_detach_client(client);
diff --git a/drivers/hwmon/ds1621.c b/drivers/hwmon/ds1621.c
index 478eb4bb857..c849c0c6ee9 100644
--- a/drivers/hwmon/ds1621.c
+++ b/drivers/hwmon/ds1621.c
@@ -29,6 +29,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include "lm75.h"
/* Addresses to scan */
@@ -178,6 +179,18 @@ static DEVICE_ATTR(temp1_input, S_IRUGO , show_temp, NULL);
static DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO , show_temp_min, set_temp_min);
static DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, set_temp_max);
+static struct attribute *ds1621_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group ds1621_group = {
+ .attrs = ds1621_attributes,
+};
+
static int ds1621_attach_adapter(struct i2c_adapter *adapter)
{
@@ -253,21 +266,19 @@ static int ds1621_detect(struct i2c_adapter *adapter, int address,
ds1621_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &ds1621_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
-
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
+ exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &ds1621_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -282,6 +293,7 @@ static int ds1621_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &ds1621_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index fd72440faf7..de17a72149d 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -1,7 +1,7 @@
/*
* f71805f.c - driver for the Fintek F71805F/FG Super-I/O chip integrated
* hardware monitoring features
- * Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
*
* The F71805F/FG is a LPC Super-I/O chip made by Fintek. It integrates
* complete hardware monitoring features: voltage, fan and temperature
@@ -31,6 +31,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
static struct platform_device *pdev;
@@ -147,7 +148,7 @@ struct f71805f_data {
u8 temp_high[3];
u8 temp_hyst[3];
u8 temp_mode;
- u8 alarms[3];
+ unsigned long alarms;
};
static inline long in_from_reg(u8 reg)
@@ -311,10 +312,9 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
data->temp[nr] = f71805f_read8(data,
F71805F_REG_TEMP(nr));
}
- for (nr = 0; nr < 3; nr++) {
- data->alarms[nr] = f71805f_read8(data,
- F71805F_REG_STATUS(nr));
- }
+ data->alarms = f71805f_read8(data, F71805F_REG_STATUS(0))
+ + (f71805f_read8(data, F71805F_REG_STATUS(1)) << 8)
+ + (f71805f_read8(data, F71805F_REG_STATUS(2)) << 16);
data->last_updated = jiffies;
data->valid = 1;
@@ -557,8 +557,7 @@ static ssize_t show_alarms_in(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", data->alarms[0] |
- ((data->alarms[1] & 0x01) << 8));
+ return sprintf(buf, "%lu\n", data->alarms & 0x1ff);
}
static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
@@ -566,7 +565,7 @@ static ssize_t show_alarms_fan(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", data->alarms[2] & 0x07);
+ return sprintf(buf, "%lu\n", (data->alarms >> 16) & 0x07);
}
static ssize_t show_alarms_temp(struct device *dev, struct device_attribute
@@ -574,7 +573,17 @@ static ssize_t show_alarms_temp(struct device *dev, struct device_attribute
{
struct f71805f_data *data = f71805f_update_device(dev);
- return sprintf(buf, "%d\n", (data->alarms[1] >> 3) & 0x07);
+ return sprintf(buf, "%lu\n", (data->alarms >> 11) & 0x07);
+}
+
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct f71805f_data *data = f71805f_update_device(dev);
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%lu\n", (data->alarms >> bitnr) & 1);
}
static ssize_t show_name(struct device *dev, struct device_attribute
@@ -585,88 +594,189 @@ static ssize_t show_name(struct device *dev, struct device_attribute
return sprintf(buf, "%s\n", data->name);
}
-static struct device_attribute f71805f_dev_attr[] = {
- __ATTR(in0_input, S_IRUGO, show_in0, NULL),
- __ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max),
- __ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min),
- __ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL),
- __ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL),
- __ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL),
- __ATTR(name, S_IRUGO, show_name, NULL),
+static DEVICE_ATTR(in0_input, S_IRUGO, show_in0, NULL);
+static DEVICE_ATTR(in0_max, S_IRUGO| S_IWUSR, show_in0_max, set_in0_max);
+static DEVICE_ATTR(in0_min, S_IRUGO| S_IWUSR, show_in0_min, set_in0_min);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in1_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 1);
+static SENSOR_DEVICE_ATTR(in1_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in2_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 2);
+static SENSOR_DEVICE_ATTR(in2_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in3_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 3);
+static SENSOR_DEVICE_ATTR(in3_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in4_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 4);
+static SENSOR_DEVICE_ATTR(in4_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in5_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 5);
+static SENSOR_DEVICE_ATTR(in5_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in6_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 6);
+static SENSOR_DEVICE_ATTR(in6_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in7_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 7);
+static SENSOR_DEVICE_ATTR(in7_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in8_max, S_IRUGO | S_IWUSR,
+ show_in_max, set_in_max, 8);
+static SENSOR_DEVICE_ATTR(in8_min, S_IRUGO | S_IWUSR,
+ show_in_min, set_in_min, 8);
+
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 0);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IRUGO | S_IWUSR,
+ show_fan_min, set_fan_min, 2);
+
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 0);
+static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 0);
+static SENSOR_DEVICE_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 1);
+static SENSOR_DEVICE_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 1);
+static SENSOR_DEVICE_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO | S_IWUSR,
+ show_temp_max, set_temp_max, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp_hyst, set_temp_hyst, 2);
+static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
+
+static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, 11);
+static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, 16);
+static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, 17);
+static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, 18);
+static DEVICE_ATTR(alarms_in, S_IRUGO, show_alarms_in, NULL);
+static DEVICE_ATTR(alarms_fan, S_IRUGO, show_alarms_fan, NULL);
+static DEVICE_ATTR(alarms_temp, S_IRUGO, show_alarms_temp, NULL);
+
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct attribute *f71805f_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in0_min.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+ &sensor_dev_attr_in8_alarm.dev_attr.attr,
+ &dev_attr_alarms_in.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &dev_attr_alarms_temp.attr,
+ &dev_attr_alarms_fan.attr,
+
+ &dev_attr_name.attr,
+ NULL
};
-static struct sensor_device_attribute f71805f_sensor_attr[] = {
- SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
- SENSOR_ATTR(in1_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 1),
- SENSOR_ATTR(in1_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 1),
- SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
- SENSOR_ATTR(in2_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 2),
- SENSOR_ATTR(in2_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 2),
- SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
- SENSOR_ATTR(in3_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 3),
- SENSOR_ATTR(in3_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 3),
- SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
- SENSOR_ATTR(in4_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 4),
- SENSOR_ATTR(in4_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 4),
- SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
- SENSOR_ATTR(in5_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 5),
- SENSOR_ATTR(in5_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 5),
- SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
- SENSOR_ATTR(in6_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 6),
- SENSOR_ATTR(in6_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 6),
- SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
- SENSOR_ATTR(in7_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 7),
- SENSOR_ATTR(in7_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 7),
- SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
- SENSOR_ATTR(in8_max, S_IRUGO | S_IWUSR,
- show_in_max, set_in_max, 8),
- SENSOR_ATTR(in8_min, S_IRUGO | S_IWUSR,
- show_in_min, set_in_min, 8),
-
- SENSOR_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0),
- SENSOR_ATTR(temp1_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 0),
- SENSOR_ATTR(temp1_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 0),
- SENSOR_ATTR(temp1_type, S_IRUGO, show_temp_type, NULL, 0),
- SENSOR_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1),
- SENSOR_ATTR(temp2_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 1),
- SENSOR_ATTR(temp2_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 1),
- SENSOR_ATTR(temp2_type, S_IRUGO, show_temp_type, NULL, 1),
- SENSOR_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2),
- SENSOR_ATTR(temp3_max, S_IRUGO | S_IWUSR,
- show_temp_max, set_temp_max, 2),
- SENSOR_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
- show_temp_hyst, set_temp_hyst, 2),
- SENSOR_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2),
+static const struct attribute_group f71805f_group = {
+ .attrs = f71805f_attributes,
};
-static struct sensor_device_attribute f71805f_fan_attr[] = {
- SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0),
- SENSOR_ATTR(fan1_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 0),
- SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1),
- SENSOR_ATTR(fan2_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 1),
- SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2),
- SENSOR_ATTR(fan3_min, S_IRUGO | S_IWUSR,
- show_fan_min, set_fan_min, 2),
+static struct attribute *f71805f_attributes_fan[3][4] = {
+ {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_alarm.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_alarm.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group f71805f_group_fan[3] = {
+ { .attrs = f71805f_attributes_fan[0] },
+ { .attrs = f71805f_attributes_fan[1] },
+ { .attrs = f71805f_attributes_fan[2] },
};
/*
@@ -714,43 +824,35 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, data);
- data->class_dev = hwmon_device_register(&pdev->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
- goto exit_free;
- }
-
/* Initialize the F71805F chip */
f71805f_init_device(data);
/* Register sysfs interface files */
- for (i = 0; i < ARRAY_SIZE(f71805f_dev_attr); i++) {
- err = device_create_file(&pdev->dev, &f71805f_dev_attr[i]);
- if (err)
- goto exit_class;
- }
- for (i = 0; i < ARRAY_SIZE(f71805f_sensor_attr); i++) {
- err = device_create_file(&pdev->dev,
- &f71805f_sensor_attr[i].dev_attr);
- if (err)
- goto exit_class;
- }
- for (i = 0; i < ARRAY_SIZE(f71805f_fan_attr); i++) {
- if (!(data->fan_enabled & (1 << (i / 2))))
+ if ((err = sysfs_create_group(&pdev->dev.kobj, &f71805f_group)))
+ goto exit_free;
+ for (i = 0; i < 3; i++) {
+ if (!(data->fan_enabled & (1 << i)))
continue;
- err = device_create_file(&pdev->dev,
- &f71805f_fan_attr[i].dev_attr);
- if (err)
- goto exit_class;
+ if ((err = sysfs_create_group(&pdev->dev.kobj,
+ &f71805f_group_fan[i])))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&pdev->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ dev_err(&pdev->dev, "Class registration failed (%d)\n", err);
+ goto exit_remove_files;
}
return 0;
-exit_class:
- dev_err(&pdev->dev, "Sysfs interface creation failed\n");
- hwmon_device_unregister(data->class_dev);
+exit_remove_files:
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 3; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
exit_free:
+ platform_set_drvdata(pdev, NULL);
kfree(data);
exit:
return err;
@@ -759,9 +861,13 @@ exit:
static int __devexit f71805f_remove(struct platform_device *pdev)
{
struct f71805f_data *data = platform_get_drvdata(pdev);
+ int i;
platform_set_drvdata(pdev, NULL);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group);
+ for (i = 0; i < 3; i++)
+ sysfs_remove_group(&pdev->dev.kobj, &f71805f_group_fan[i]);
kfree(data);
return 0;
diff --git a/drivers/hwmon/fscher.c b/drivers/hwmon/fscher.c
index 6bc76b40763..19717752cfc 100644
--- a/drivers/hwmon/fscher.c
+++ b/drivers/hwmon/fscher.c
@@ -34,6 +34,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -240,47 +241,45 @@ sysfs_alarms(FSCHER_REG_EVENTS)
sysfs_control(FSCHER_REG_CONTROL)
sysfs_watchdog(FSCHER_REG_WDOG_CONTROL, FSCHER_REG_WDOG_STATE, FSCHER_REG_WDOG_PRESET)
-#define device_create_file_fan(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_fan##offset##_status); \
- device_create_file(&client->dev, &dev_attr_pwm##offset); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
- device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-} while (0)
-
-#define device_create_file_temp(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_temp##offset##_status); \
- device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-} while (0)
-
-#define device_create_file_in(client, offset) \
-do { \
- device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-} while (0)
-
-#define device_create_file_revision(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_revision); \
-} while (0)
-
-#define device_create_file_alarms(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_alarms); \
-} while (0)
-
-#define device_create_file_control(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_control); \
-} while (0)
-
-#define device_create_file_watchdog(client) \
-do { \
- device_create_file(&client->dev, &dev_attr_watchdog_status); \
- device_create_file(&client->dev, &dev_attr_watchdog_control); \
- device_create_file(&client->dev, &dev_attr_watchdog_preset); \
-} while (0)
-
+static struct attribute *fscher_attributes[] = {
+ &dev_attr_revision.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_control.attr,
+
+ &dev_attr_watchdog_status.attr,
+ &dev_attr_watchdog_control.attr,
+ &dev_attr_watchdog_preset.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+
+ &dev_attr_fan1_status.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan2_status.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_fan3_status.attr,
+ &dev_attr_fan3_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_pwm3.attr,
+
+ &dev_attr_temp1_status.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_status.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_status.attr,
+ &dev_attr_temp3_input.attr,
+ NULL
+};
+
+static const struct attribute_group fscher_group = {
+ .attrs = fscher_attributes,
+};
+
/*
* Real code
*/
@@ -342,31 +341,19 @@ static int fscher_detect(struct i2c_adapter *adapter, int address, int kind)
fscher_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &fscher_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file_revision(new_client);
- device_create_file_alarms(new_client);
- device_create_file_control(new_client);
- device_create_file_watchdog(new_client);
-
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &fscher_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -381,6 +368,7 @@ static int fscher_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &fscher_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/fscpos.c b/drivers/hwmon/fscpos.c
index 6dc4846b9ee..ea506a77f9c 100644
--- a/drivers/hwmon/fscpos.c
+++ b/drivers/hwmon/fscpos.c
@@ -38,6 +38,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -432,6 +433,44 @@ static DEVICE_ATTR(in0_input, S_IRUGO, show_volt_12, NULL);
static DEVICE_ATTR(in1_input, S_IRUGO, show_volt_5, NULL);
static DEVICE_ATTR(in2_input, S_IRUGO, show_volt_batt, NULL);
+static struct attribute *fscpos_attributes[] = {
+ &dev_attr_event.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+
+ &dev_attr_wdog_control.attr,
+ &dev_attr_wdog_preset.attr,
+ &dev_attr_wdog_state.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_status.attr,
+ &dev_attr_temp1_reset.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_status.attr,
+ &dev_attr_temp2_reset.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_status.attr,
+ &dev_attr_temp3_reset.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_status.attr,
+ &dev_attr_fan1_ripple.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_status.attr,
+ &dev_attr_fan2_ripple.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_status.attr,
+ &dev_attr_fan3_ripple.attr,
+ NULL
+};
+
+static const struct attribute_group fscpos_group = {
+ .attrs = fscpos_attributes,
+};
+
static int fscpos_attach_adapter(struct i2c_adapter *adapter)
{
if (!(adapter->class & I2C_CLASS_HWMON))
@@ -497,42 +536,19 @@ static int fscpos_detect(struct i2c_adapter *adapter, int address, int kind)
dev_info(&new_client->dev, "Found fscpos chip, rev %u\n", data->revision);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &fscpos_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_event);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_wdog_control);
- device_create_file(&new_client->dev, &dev_attr_wdog_preset);
- device_create_file(&new_client->dev, &dev_attr_wdog_state);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_status);
- device_create_file(&new_client->dev, &dev_attr_temp1_reset);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_status);
- device_create_file(&new_client->dev, &dev_attr_temp2_reset);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_status);
- device_create_file(&new_client->dev, &dev_attr_temp3_reset);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_status);
- device_create_file(&new_client->dev, &dev_attr_fan1_ripple);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_status);
- device_create_file(&new_client->dev, &dev_attr_fan2_ripple);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_status);
- device_create_file(&new_client->dev, &dev_attr_fan3_ripple);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &fscpos_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -547,6 +563,7 @@ static int fscpos_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &fscpos_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/gl518sm.c b/drivers/hwmon/gl518sm.c
index 6606aabdb49..c103640455a 100644
--- a/drivers/hwmon/gl518sm.c
+++ b/drivers/hwmon/gl518sm.c
@@ -44,6 +44,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
@@ -340,6 +341,42 @@ static DEVICE_ATTR(beep_enable, S_IWUSR|S_IRUGO,
static DEVICE_ATTR(beep_mask, S_IWUSR|S_IRUGO,
show_beep_mask, set_beep_mask);
+static struct attribute *gl518_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_auto.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl518_group = {
+ .attrs = gl518_attributes,
+};
+
/*
* Real code
*/
@@ -420,43 +457,19 @@ static int gl518_detect(struct i2c_adapter *adapter, int address, int kind)
gl518_init_client((struct i2c_client *) new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &gl518_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_fan1_auto);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_beep_enable);
- device_create_file(&new_client->dev, &dev_attr_beep_mask);
-
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
-
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &gl518_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -490,6 +503,7 @@ static int gl518_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &gl518_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/gl520sm.c b/drivers/hwmon/gl520sm.c
index 14e810f3c2c..ebe7b9aaa91 100644
--- a/drivers/hwmon/gl520sm.c
+++ b/drivers/hwmon/gl520sm.c
@@ -30,6 +30,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Type of the extra sensor */
static unsigned short extra_sensor_type;
@@ -190,55 +191,29 @@ static DEVICE_ATTR(type##item, S_IRUGO, get_##type##0##item, NULL);
#define sysfs_vid(n) \
sysfs_ro_n(cpu, n, _vid, GL520_REG_VID_INPUT)
-#define device_create_file_vid(client, n) \
-device_create_file(&client->dev, &dev_attr_cpu##n##_vid)
-
#define sysfs_in(n) \
sysfs_ro_n(in, n, _input, GL520_REG_IN##n##INPUT) \
sysfs_rw_n(in, n, _min, GL520_REG_IN##n##_MIN) \
sysfs_rw_n(in, n, _max, GL520_REG_IN##n##_MAX) \
-#define device_create_file_in(client, n) \
-({device_create_file(&client->dev, &dev_attr_in##n##_input); \
-device_create_file(&client->dev, &dev_attr_in##n##_min); \
-device_create_file(&client->dev, &dev_attr_in##n##_max);})
-
#define sysfs_fan(n) \
sysfs_ro_n(fan, n, _input, GL520_REG_FAN_INPUT) \
sysfs_rw_n(fan, n, _min, GL520_REG_FAN_MIN) \
sysfs_rw_n(fan, n, _div, GL520_REG_FAN_DIV)
-#define device_create_file_fan(client, n) \
-({device_create_file(&client->dev, &dev_attr_fan##n##_input); \
-device_create_file(&client->dev, &dev_attr_fan##n##_min); \
-device_create_file(&client->dev, &dev_attr_fan##n##_div);})
-
#define sysfs_fan_off(n) \
sysfs_rw_n(fan, n, _off, GL520_REG_FAN_OFF) \
-#define device_create_file_fan_off(client, n) \
-device_create_file(&client->dev, &dev_attr_fan##n##_off)
-
#define sysfs_temp(n) \
sysfs_ro_n(temp, n, _input, GL520_REG_TEMP##n##_INPUT) \
sysfs_rw_n(temp, n, _max, GL520_REG_TEMP##n##_MAX) \
sysfs_rw_n(temp, n, _max_hyst, GL520_REG_TEMP##n##_MAX_HYST)
-#define device_create_file_temp(client, n) \
-({device_create_file(&client->dev, &dev_attr_temp##n##_input); \
-device_create_file(&client->dev, &dev_attr_temp##n##_max); \
-device_create_file(&client->dev, &dev_attr_temp##n##_max_hyst);})
-
#define sysfs_alarms() \
sysfs_ro(alarms, , GL520_REG_ALARMS) \
sysfs_rw(beep_enable, , GL520_REG_BEEP_ENABLE) \
sysfs_rw(beep_mask, , GL520_REG_BEEP_MASK)
-#define device_create_file_alarms(client) \
-({device_create_file(&client->dev, &dev_attr_alarms); \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask);})
-
sysfs_vid(0)
@@ -511,6 +486,59 @@ static ssize_t set_beep_mask(struct i2c_client *client, struct gl520_data *data,
return count;
}
+static struct attribute *gl520_attributes[] = {
+ &dev_attr_cpu0_vid.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan1_off.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group = {
+ .attrs = gl520_attributes,
+};
+
+static struct attribute *gl520_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ NULL
+};
+
+static const struct attribute_group gl520_group_opt = {
+ .attrs = gl520_attributes_opt,
+};
+
/*
* Real code
@@ -572,33 +600,39 @@ static int gl520_detect(struct i2c_adapter *adapter, int address, int kind)
gl520_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &gl520_group)))
goto exit_detach;
- }
-
- device_create_file_vid(new_client, 0);
- device_create_file_in(new_client, 0);
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- if (!data->two_temps)
- device_create_file_in(new_client, 4);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan_off(new_client, 1);
+ if (data->two_temps) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp2_max_hyst)))
+ goto exit_remove_files;
+ } else {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove_files;
+ }
- device_create_file_temp(new_client, 1);
- if (data->two_temps)
- device_create_file_temp(new_client, 2);
- device_create_file_alarms(new_client);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
+ }
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &gl520_group);
+ sysfs_remove_group(&new_client->dev.kobj, &gl520_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -652,6 +686,8 @@ static int gl520_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &gl520_group);
+ sysfs_remove_group(&client->dev.kobj, &gl520_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/hdaps.c b/drivers/hwmon/hdaps.c
index 42b632889dd..26be4ea8a38 100644
--- a/drivers/hwmon/hdaps.c
+++ b/drivers/hwmon/hdaps.c
@@ -537,6 +537,7 @@ static int __init hdaps_init(void)
HDAPS_DMI_MATCH_NORMAL("ThinkPad T42"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad T43"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad T60p"),
+ HDAPS_DMI_MATCH_LENOVO("ThinkPad T60"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X40"),
HDAPS_DMI_MATCH_NORMAL("ThinkPad X41"),
HDAPS_DMI_MATCH_LENOVO("ThinkPad X60"),
@@ -587,7 +588,9 @@ static int __init hdaps_init(void)
input_set_abs_params(hdaps_idev, ABS_Y,
-256, 256, HDAPS_INPUT_FUZZ, HDAPS_INPUT_FLAT);
- input_register_device(hdaps_idev);
+ ret = input_register_device(hdaps_idev);
+ if (ret)
+ goto out_idev;
/* start up our timer for the input device */
init_timer(&hdaps_timer);
@@ -598,6 +601,8 @@ static int __init hdaps_init(void)
printk(KERN_INFO "hdaps: driver successfully loaded.\n");
return 0;
+out_idev:
+ input_free_device(hdaps_idev);
out_group:
sysfs_remove_group(&pdev->dev.kobj, &hdaps_attribute_group);
out_device:
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c
index 06df92b3ee4..323ef06719c 100644
--- a/drivers/hwmon/it87.c
+++ b/drivers/hwmon/it87.c
@@ -4,10 +4,12 @@
Supports: IT8705F Super I/O chip w/LPC interface
IT8712F Super I/O chip w/LPC interface & SMBus
+ IT8716F Super I/O chip w/LPC interface
+ IT8718F Super I/O chip w/LPC interface
Sis950 A clone of the IT8705F
Copyright (C) 2001 Chris Gauthron <chrisg@0-in.com>
- Largely inspired by lm78.c of the same package
+ Copyright (C) 2005-2006 Jean Delvare <khali@linux-fr.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -24,13 +26,6 @@
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-/*
- djg@pdp8.net David Gesswein 7/18/01
- Modified to fix bug with not all alarms enabled.
- Added ability to read battery voltage and select temperature sensor
- type at module load time.
-*/
-
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
@@ -42,6 +37,7 @@
#include <linux/hwmon-vid.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -50,12 +46,13 @@ static unsigned short normal_i2c[] = { 0x2d, I2C_CLIENT_END };
static unsigned short isa_address;
/* Insmod parameters */
-I2C_CLIENT_INSMOD_2(it87, it8712);
+I2C_CLIENT_INSMOD_4(it87, it8712, it8716, it8718);
#define REG 0x2e /* The register to read/write */
#define DEV 0x07 /* Register: Logical device select */
#define VAL 0x2f /* The value to read/write */
#define PME 0x04 /* The device with the fan registers in it */
+#define GPIO 0x07 /* The device with the IT8718F VID value in it */
#define DEVID 0x20 /* Register: Device ID */
#define DEVREV 0x22 /* Register: Device Revision */
@@ -77,10 +74,10 @@ static int superio_inw(int reg)
}
static inline void
-superio_select(void)
+superio_select(int ldn)
{
outb(DEV, REG);
- outb(PME, VAL);
+ outb(ldn, VAL);
}
static inline void
@@ -99,20 +96,27 @@ superio_exit(void)
outb(0x02, VAL);
}
+/* Logical device 4 registers */
#define IT8712F_DEVID 0x8712
#define IT8705F_DEVID 0x8705
+#define IT8716F_DEVID 0x8716
+#define IT8718F_DEVID 0x8718
#define IT87_ACT_REG 0x30
#define IT87_BASE_REG 0x60
+/* Logical device 7 registers (IT8712F and later) */
+#define IT87_SIO_PINX2_REG 0x2c /* Pin selection */
+#define IT87_SIO_VID_REG 0xfc /* VID value */
+
/* Update battery voltage after every reading if true */
static int update_vbat;
/* Not all BIOSes properly configure the PWM registers */
static int fix_pwm_polarity;
-/* Chip Type */
-
+/* Values read from Super-I/O config space */
static u16 chip_type;
+static u8 vid_value;
/* Many IT87 constants specified below */
@@ -131,13 +135,21 @@ static u16 chip_type;
#define IT87_REG_ALARM2 0x02
#define IT87_REG_ALARM3 0x03
+/* The IT8718F has the VID value in a different register, in Super-I/O
+ configuration space. */
#define IT87_REG_VID 0x0a
+/* Warning: register 0x0b is used for something completely different in
+ new chips/revisions. I suspect only 16-bit tachometer mode will work
+ for these. */
#define IT87_REG_FAN_DIV 0x0b
+#define IT87_REG_FAN_16BIT 0x0c
/* Monitors: 9 voltage (0 to 7, battery), 3 temp (1 to 3), 3 fan (1 to 3) */
#define IT87_REG_FAN(nr) (0x0d + (nr))
#define IT87_REG_FAN_MIN(nr) (0x10 + (nr))
+#define IT87_REG_FANX(nr) (0x18 + (nr))
+#define IT87_REG_FANX_MIN(nr) (0x1b + (nr))
#define IT87_REG_FAN_MAIN_CTRL 0x13
#define IT87_REG_FAN_CTL 0x14
#define IT87_REG_PWM(nr) (0x15 + (nr))
@@ -169,7 +181,16 @@ static inline u8 FAN_TO_REG(long rpm, int div)
254);
}
+static inline u16 FAN16_TO_REG(long rpm)
+{
+ if (rpm == 0)
+ return 0xffff;
+ return SENSORS_LIMIT((1350000 + rpm) / (rpm * 2), 1, 0xfffe);
+}
+
#define FAN_FROM_REG(val,div) ((val)==0?-1:(val)==255?0:1350000/((val)*(div)))
+/* The divider is fixed to 2 in 16-bit mode */
+#define FAN16_FROM_REG(val) ((val)==0?-1:(val)==0xffff?0:1350000/((val)*2))
#define TEMP_TO_REG(val) (SENSORS_LIMIT(((val)<0?(((val)-500)/1000):\
((val)+500)/1000),-128,127))
@@ -181,7 +202,7 @@ static inline u8 FAN_TO_REG(long rpm, int div)
static int DIV_TO_REG(int val)
{
int answer = 0;
- while ((val >>= 1) != 0)
+ while (answer < 7 && (val >>= 1))
answer++;
return answer;
}
@@ -203,10 +224,11 @@ struct it87_data {
unsigned long last_updated; /* In jiffies */
u8 in[9]; /* Register value */
- u8 in_max[9]; /* Register value */
- u8 in_min[9]; /* Register value */
- u8 fan[3]; /* Register value */
- u8 fan_min[3]; /* Register value */
+ u8 in_max[8]; /* Register value */
+ u8 in_min[8]; /* Register value */
+ u8 has_fan; /* Bitfield, fans enabled */
+ u16 fan[3]; /* Register values, possibly combined */
+ u16 fan_min[3]; /* Register values, possibly combined */
u8 temp[3]; /* Register value */
u8 temp_high[3]; /* Register value */
u8 temp_low[3]; /* Register value */
@@ -243,6 +265,7 @@ static struct i2c_driver it87_driver = {
static struct i2c_driver it87_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "it87-isa",
},
.attach_adapter = it87_isa_attach_adapter,
@@ -544,15 +567,15 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
struct i2c_client *client = to_i2c_client(dev);
struct it87_data *data = i2c_get_clientdata(client);
- int val = simple_strtol(buf, NULL, 10);
- int i, min[3];
+ unsigned long val = simple_strtoul(buf, NULL, 10);
+ int min;
u8 old;
mutex_lock(&data->update_lock);
old = it87_read_value(client, IT87_REG_FAN_DIV);
- for (i = 0; i < 3; i++)
- min[i] = FAN_FROM_REG(data->fan_min[i], DIV_FROM_REG(data->fan_div[i]));
+ /* Save fan min limit */
+ min = FAN_FROM_REG(data->fan_min[nr], DIV_FROM_REG(data->fan_div[nr]));
switch (nr) {
case 0:
@@ -572,10 +595,10 @@ static ssize_t set_fan_div(struct device *dev, struct device_attribute *attr,
val |= 0x1 << 6;
it87_write_value(client, IT87_REG_FAN_DIV, val);
- for (i = 0; i < 3; i++) {
- data->fan_min[i]=FAN_TO_REG(min[i], DIV_FROM_REG(data->fan_div[i]));
- it87_write_value(client, IT87_REG_FAN_MIN(i), data->fan_min[i]);
- }
+ /* Restore fan min limit */
+ data->fan_min[nr] = FAN_TO_REG(min, DIV_FROM_REG(data->fan_div[nr]));
+ it87_write_value(client, IT87_REG_FAN_MIN(nr), data->fan_min[nr]);
+
mutex_unlock(&data->update_lock);
return count;
}
@@ -656,6 +679,59 @@ show_pwm_offset(1);
show_pwm_offset(2);
show_pwm_offset(3);
+/* A different set of callbacks for 16-bit fans */
+static ssize_t show_fan16(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct it87_data *data = it87_update_device(dev);
+ return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan[nr]));
+}
+
+static ssize_t show_fan16_min(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct it87_data *data = it87_update_device(dev);
+ return sprintf(buf, "%d\n", FAN16_FROM_REG(data->fan_min[nr]));
+}
+
+static ssize_t set_fan16_min(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ struct i2c_client *client = to_i2c_client(dev);
+ struct it87_data *data = i2c_get_clientdata(client);
+ int val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ data->fan_min[nr] = FAN16_TO_REG(val);
+ it87_write_value(client, IT87_REG_FAN_MIN(nr),
+ data->fan_min[nr] & 0xff);
+ it87_write_value(client, IT87_REG_FANX_MIN(nr),
+ data->fan_min[nr] >> 8);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* We want to use the same sysfs file names as 8-bit fans, but we need
+ different variable names, so we have to use SENSOR_ATTR instead of
+ SENSOR_DEVICE_ATTR. */
+#define show_fan16_offset(offset) \
+static struct sensor_device_attribute sensor_dev_attr_fan##offset##_input16 \
+ = SENSOR_ATTR(fan##offset##_input, S_IRUGO, \
+ show_fan16, NULL, offset - 1); \
+static struct sensor_device_attribute sensor_dev_attr_fan##offset##_min16 \
+ = SENSOR_ATTR(fan##offset##_min, S_IRUGO | S_IWUSR, \
+ show_fan16_min, set_fan16_min, offset - 1)
+
+show_fan16_offset(1);
+show_fan16_offset(2);
+show_fan16_offset(3);
+
/* Alarms */
static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -683,8 +759,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm)
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -693,8 +767,88 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
+
+static struct attribute *it87_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_type.dev_attr.attr,
+ &sensor_dev_attr_temp2_type.dev_attr.attr,
+ &sensor_dev_attr_temp3_type.dev_attr.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group = {
+ .attrs = it87_attributes,
+};
+
+static struct attribute *it87_attributes_opt[] = {
+ &sensor_dev_attr_fan1_input16.dev_attr.attr,
+ &sensor_dev_attr_fan1_min16.dev_attr.attr,
+ &sensor_dev_attr_fan2_input16.dev_attr.attr,
+ &sensor_dev_attr_fan2_min16.dev_attr.attr,
+ &sensor_dev_attr_fan3_input16.dev_attr.attr,
+ &sensor_dev_attr_fan3_min16.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ NULL
+};
+
+static const struct attribute_group it87_group_opt = {
+ .attrs = it87_attributes_opt,
+};
/* This function is called when:
* it87_driver is inserted (when this module is loaded), for each
@@ -720,10 +874,12 @@ static int __init it87_find(unsigned short *address)
superio_enter();
chip_type = superio_inw(DEVID);
if (chip_type != IT8712F_DEVID
+ && chip_type != IT8716F_DEVID
+ && chip_type != IT8718F_DEVID
&& chip_type != IT8705F_DEVID)
goto exit;
- superio_select();
+ superio_select(PME);
if (!(superio_inb(IT87_ACT_REG) & 0x01)) {
pr_info("it87: Device not activated, skipping\n");
goto exit;
@@ -739,6 +895,21 @@ static int __init it87_find(unsigned short *address)
pr_info("it87: Found IT%04xF chip at 0x%x, revision %d\n",
chip_type, *address, superio_inb(DEVREV) & 0x0f);
+ /* Read GPIO config and VID value from LDN 7 (GPIO) */
+ if (chip_type != IT8705F_DEVID) {
+ int reg;
+
+ superio_select(GPIO);
+ if (chip_type == it8718)
+ vid_value = superio_inb(IT87_SIO_VID_REG);
+
+ reg = superio_inb(IT87_SIO_PINX2_REG);
+ if (reg & (1 << 0))
+ pr_info("it87: in3 is VCC (+5V)\n");
+ if (reg & (1 << 1))
+ pr_info("it87: in7 is VCCH (+5V Stand-By)\n");
+ }
+
exit:
superio_exit();
return err;
@@ -799,8 +970,19 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
i = it87_read_value(new_client, IT87_REG_CHIPID);
if (i == 0x90) {
kind = it87;
- if ((is_isa) && (chip_type == IT8712F_DEVID))
- kind = it8712;
+ if (is_isa) {
+ switch (chip_type) {
+ case IT8712F_DEVID:
+ kind = it8712;
+ break;
+ case IT8716F_DEVID:
+ kind = it8716;
+ break;
+ case IT8718F_DEVID:
+ kind = it8718;
+ break;
+ }
+ }
}
else {
if (kind == 0)
@@ -817,6 +999,10 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
name = "it87";
} else if (kind == it8712) {
name = "it8712";
+ } else if (kind == it8716) {
+ name = "it8716";
+ } else if (kind == it8718) {
+ name = "it8718";
}
/* Fill in the remaining client fields and put it into the global list */
@@ -841,76 +1027,103 @@ static int it87_detect(struct i2c_adapter *adapter, int address, int kind)
it87_init_client(new_client, data);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &it87_group)))
goto ERROR3;
+
+ /* Do not create fan files for disabled fans */
+ if (data->type == it8716 || data->type == it8718) {
+ /* 16-bit tachometers */
+ if (data->has_fan & (1 << 0)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_min16.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 1)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_min16.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 2)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_input16.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_min16.dev_attr)))
+ goto ERROR4;
+ }
+ } else {
+ /* 8-bit tachometers with clock divider */
+ if (data->has_fan & (1 << 0)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan1_div.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 1)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan2_div.dev_attr)))
+ goto ERROR4;
+ }
+ if (data->has_fan & (1 << 2)) {
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_input.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_min.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_fan3_div.dev_attr)))
+ goto ERROR4;
+ }
}
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in8_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp1_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp2_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_temp3_type.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_input.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_min.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan2_div.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_fan3_div.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
if (enable_pwm_interface) {
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm1_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm2_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm3_enable.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm1.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm2.dev_attr);
- device_create_file(&new_client->dev, &sensor_dev_attr_pwm3.dev_attr);
+ if ((err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm1_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm2_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm3_enable.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm1.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm2.dev_attr))
+ || (err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_pwm3.dev_attr)))
+ goto ERROR4;
}
- if (data->type == it8712) {
+ if (data->type == it8712 || data->type == it8716
+ || data->type == it8718) {
data->vrm = vid_which_vrm();
- device_create_file_vrm(new_client);
- device_create_file_vid(new_client);
+ /* VID reading from Super-I/O config space if available */
+ data->vid = vid_value;
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_vrm))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid)))
+ goto ERROR4;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
}
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &it87_group);
+ sysfs_remove_group(&new_client->dev.kobj, &it87_group_opt);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -928,6 +1141,8 @@ static int it87_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &it87_group);
+ sysfs_remove_group(&client->dev.kobj, &it87_group_opt);
if ((err = i2c_detach_client(client)))
return err;
@@ -1044,6 +1259,22 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
data->manual_pwm_ctl[i] = 0xff;
}
+ /* Some chips seem to have default value 0xff for all limit
+ * registers. For low voltage limits it makes no sense and triggers
+ * alarms, so change to 0 instead. For high temperature limits, it
+ * means -1 degree C, which surprisingly doesn't trigger an alarm,
+ * but is still confusing, so change to 127 degrees C. */
+ for (i = 0; i < 8; i++) {
+ tmp = it87_read_value(client, IT87_REG_VIN_MIN(i));
+ if (tmp == 0xff)
+ it87_write_value(client, IT87_REG_VIN_MIN(i), 0);
+ }
+ for (i = 0; i < 3; i++) {
+ tmp = it87_read_value(client, IT87_REG_TEMP_HIGH(i));
+ if (tmp == 0xff)
+ it87_write_value(client, IT87_REG_TEMP_HIGH(i), 127);
+ }
+
/* Check if temperature channnels are reset manually or by some reason */
tmp = it87_read_value(client, IT87_REG_TEMP_ENABLE);
if ((tmp & 0x3f) == 0) {
@@ -1067,6 +1298,18 @@ static void it87_init_client(struct i2c_client *client, struct it87_data *data)
data->fan_main_ctrl |= 0x70;
it87_write_value(client, IT87_REG_FAN_MAIN_CTRL, data->fan_main_ctrl);
}
+ data->has_fan = (data->fan_main_ctrl >> 4) & 0x07;
+
+ /* Set tachometers to 16-bit mode if needed */
+ if (data->type == it8716 || data->type == it8718) {
+ tmp = it87_read_value(client, IT87_REG_FAN_16BIT);
+ if (~tmp & 0x07 & data->has_fan) {
+ dev_dbg(&client->dev,
+ "Setting fan1-3 to 16-bit mode\n");
+ it87_write_value(client, IT87_REG_FAN_16BIT,
+ tmp | 0x07);
+ }
+ }
/* Set current fan mode registers and the default settings for the
* other mode registers */
@@ -1117,18 +1360,26 @@ static struct it87_data *it87_update_device(struct device *dev)
data->in_max[i] =
it87_read_value(client, IT87_REG_VIN_MAX(i));
}
+ /* in8 (battery) has no limit registers */
data->in[8] =
it87_read_value(client, IT87_REG_VIN(8));
- /* Temperature sensor doesn't have limit registers, set
- to min and max value */
- data->in_min[8] = 0;
- data->in_max[8] = 255;
for (i = 0; i < 3; i++) {
- data->fan[i] =
- it87_read_value(client, IT87_REG_FAN(i));
+ /* Skip disabled fans */
+ if (!(data->has_fan & (1 << i)))
+ continue;
+
data->fan_min[i] =
it87_read_value(client, IT87_REG_FAN_MIN(i));
+ data->fan[i] = it87_read_value(client,
+ IT87_REG_FAN(i));
+ /* Add high byte if in 16-bit mode */
+ if (data->type == it8716 || data->type == it8718) {
+ data->fan[i] |= it87_read_value(client,
+ IT87_REG_FANX(i)) << 8;
+ data->fan_min[i] |= it87_read_value(client,
+ IT87_REG_FANX_MIN(i)) << 8;
+ }
}
for (i = 0; i < 3; i++) {
data->temp[i] =
@@ -1139,10 +1390,14 @@ static struct it87_data *it87_update_device(struct device *dev)
it87_read_value(client, IT87_REG_TEMP_LOW(i));
}
- i = it87_read_value(client, IT87_REG_FAN_DIV);
- data->fan_div[0] = i & 0x07;
- data->fan_div[1] = (i >> 3) & 0x07;
- data->fan_div[2] = (i & 0x40) ? 3 : 1;
+ /* Newer chips don't have clock dividers */
+ if ((data->has_fan & 0x07) && data->type != it8716
+ && data->type != it8718) {
+ i = it87_read_value(client, IT87_REG_FAN_DIV);
+ data->fan_div[0] = i & 0x07;
+ data->fan_div[1] = (i >> 3) & 0x07;
+ data->fan_div[2] = (i & 0x40) ? 3 : 1;
+ }
data->alarms =
it87_read_value(client, IT87_REG_ALARM1) |
@@ -1152,9 +1407,11 @@ static struct it87_data *it87_update_device(struct device *dev)
data->sensor = it87_read_value(client, IT87_REG_TEMP_ENABLE);
/* The 8705 does not have VID capability */
- if (data->type == it8712) {
+ if (data->type == it8712 || data->type == it8716) {
data->vid = it87_read_value(client, IT87_REG_VID);
- data->vid &= 0x1f;
+ /* The older IT8712F revisions had only 5 VID pins,
+ but we assume it is always safe to read 6 bits. */
+ data->vid &= 0x3f;
}
data->last_updated = jiffies;
data->valid = 1;
@@ -1192,8 +1449,9 @@ static void __exit sm_it87_exit(void)
}
-MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>");
-MODULE_DESCRIPTION("IT8705F, IT8712F, Sis950 driver");
+MODULE_AUTHOR("Chris Gauthron <chrisg@0-in.com>, "
+ "Jean Delvare <khali@linux-fr.org>");
+MODULE_DESCRIPTION("IT8705F/8712F/8716F/8718F, SiS950 driver");
module_param(update_vbat, bool, 0);
MODULE_PARM_DESC(update_vbat, "Update vbat if set else return powerup value");
module_param(fix_pwm_polarity, bool, 0);
diff --git a/drivers/hwmon/k8temp.c b/drivers/hwmon/k8temp.c
new file mode 100644
index 00000000000..f58b64ed09e
--- /dev/null
+++ b/drivers/hwmon/k8temp.c
@@ -0,0 +1,294 @@
+/*
+ * k8temp.c - Linux kernel module for hardware monitoring
+ *
+ * Copyright (C) 2006 Rudolf Marek <r.marek@sh.cvut.cz>
+ *
+ * Inspired from the w83785 and amd756 drivers.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301 USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/pci.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+
+#define TEMP_FROM_REG(val) (((((val) >> 16) & 0xff) - 49) * 1000)
+#define REG_TEMP 0xe4
+#define SEL_PLACE 0x40
+#define SEL_CORE 0x04
+
+struct k8temp_data {
+ struct class_device *class_dev;
+ struct mutex update_lock;
+ const char *name;
+ char valid; /* zero until following fields are valid */
+ unsigned long last_updated; /* in jiffies */
+
+ /* registers values */
+ u8 sensorsp; /* sensor presence bits - SEL_CORE & SEL_PLACE */
+ u32 temp[2][2]; /* core, place */
+};
+
+static struct k8temp_data *k8temp_update_device(struct device *dev)
+{
+ struct k8temp_data *data = dev_get_drvdata(dev);
+ struct pci_dev *pdev = to_pci_dev(dev);
+ u8 tmp;
+
+ mutex_lock(&data->update_lock);
+
+ if (!data->valid
+ || time_after(jiffies, data->last_updated + HZ)) {
+ pci_read_config_byte(pdev, REG_TEMP, &tmp);
+ tmp &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP, &data->temp[0][0]);
+
+ if (data->sensorsp & SEL_PLACE) {
+ tmp |= SEL_PLACE; /* Select sensor 1, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[0][1]);
+ }
+
+ if (data->sensorsp & SEL_CORE) {
+ tmp &= ~SEL_PLACE; /* Select sensor 0, core1 */
+ tmp |= SEL_CORE;
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[1][0]);
+
+ if (data->sensorsp & SEL_PLACE) {
+ tmp |= SEL_PLACE; /* Select sensor 1, core1 */
+ pci_write_config_byte(pdev, REG_TEMP, tmp);
+ pci_read_config_dword(pdev, REG_TEMP,
+ &data->temp[1][1]);
+ }
+ }
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+ return data;
+}
+
+/*
+ * Sysfs stuff
+ */
+
+static ssize_t show_name(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct k8temp_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+
+static ssize_t show_temp(struct device *dev,
+ struct device_attribute *devattr, char *buf)
+{
+ struct sensor_device_attribute_2 *attr =
+ to_sensor_dev_attr_2(devattr);
+ int core = attr->nr;
+ int place = attr->index;
+ struct k8temp_data *data = k8temp_update_device(dev);
+
+ return sprintf(buf, "%d\n",
+ TEMP_FROM_REG(data->temp[core][place]));
+}
+
+/* core, place */
+
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp, NULL, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp4_input, S_IRUGO, show_temp, NULL, 1, 1);
+static DEVICE_ATTR(name, S_IRUGO, show_name, NULL);
+
+static struct pci_device_id k8temp_ids[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) },
+ { 0 },
+};
+
+MODULE_DEVICE_TABLE(pci, k8temp_ids);
+
+static int __devinit k8temp_probe(struct pci_dev *pdev,
+ const struct pci_device_id *id)
+{
+ int err;
+ u8 scfg;
+ u32 temp;
+ struct k8temp_data *data;
+ u32 cpuid = cpuid_eax(1);
+
+ /* this feature should be available since SH-C0 core */
+ if ((cpuid == 0xf40) || (cpuid == 0xf50) || (cpuid == 0xf51)) {
+ err = -ENODEV;
+ goto exit;
+ }
+
+ if (!(data = kzalloc(sizeof(struct k8temp_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ goto exit;
+ }
+
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+ scfg &= ~(SEL_PLACE | SEL_CORE); /* Select sensor 0, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_byte(pdev, REG_TEMP, &scfg);
+
+ if (scfg & (SEL_PLACE | SEL_CORE)) {
+ dev_err(&pdev->dev, "Configuration bit(s) stuck at 1!\n");
+ err = -ENODEV;
+ goto exit_free;
+ }
+
+ scfg |= (SEL_PLACE | SEL_CORE);
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+
+ /* now we know if we can change core and/or sensor */
+ pci_read_config_byte(pdev, REG_TEMP, &data->sensorsp);
+
+ if (data->sensorsp & SEL_PLACE) {
+ scfg &= ~SEL_CORE; /* Select sensor 1, core0 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ scfg |= SEL_CORE; /* prepare for next selection */
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */
+ data->sensorsp &= ~SEL_PLACE;
+ }
+
+ if (data->sensorsp & SEL_CORE) {
+ scfg &= ~SEL_PLACE; /* Select sensor 0, core1 */
+ pci_write_config_byte(pdev, REG_TEMP, scfg);
+ pci_read_config_dword(pdev, REG_TEMP, &temp);
+ if (!((temp >> 16) & 0xff)) /* if temp is 0 -49C is not likely */
+ data->sensorsp &= ~SEL_CORE;
+ }
+
+ data->name = "k8temp";
+ mutex_init(&data->update_lock);
+ dev_set_drvdata(&pdev->dev, data);
+
+ /* Register sysfs hooks */
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ /* sensor can be changed and reports something */
+ if (data->sensorsp & SEL_PLACE) {
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ /* core can be changed and reports something */
+ if (data->sensorsp & SEL_CORE) {
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ if (err)
+ goto exit_remove;
+ if (data->sensorsp & SEL_PLACE)
+ err = device_create_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.
+ dev_attr);
+ if (err)
+ goto exit_remove;
+ }
+
+ err = device_create_file(&pdev->dev, &dev_attr_name);
+ if (err)
+ goto exit_remove;
+
+ data->class_dev = hwmon_device_register(&pdev->dev);
+
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
+
+ return 0;
+
+exit_remove:
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.dev_attr);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+exit_free:
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(data);
+exit:
+ return err;
+}
+
+static void __devexit k8temp_remove(struct pci_dev *pdev)
+{
+ struct k8temp_data *data = dev_get_drvdata(&pdev->dev);
+
+ hwmon_device_unregister(data->class_dev);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp2_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp3_input.dev_attr);
+ device_remove_file(&pdev->dev,
+ &sensor_dev_attr_temp4_input.dev_attr);
+ device_remove_file(&pdev->dev, &dev_attr_name);
+ dev_set_drvdata(&pdev->dev, NULL);
+ kfree(data);
+}
+
+static struct pci_driver k8temp_driver = {
+ .name = "k8temp",
+ .id_table = k8temp_ids,
+ .probe = k8temp_probe,
+ .remove = __devexit_p(k8temp_remove),
+};
+
+static int __init k8temp_init(void)
+{
+ return pci_register_driver(&k8temp_driver);
+}
+
+static void __exit k8temp_exit(void)
+{
+ pci_unregister_driver(&k8temp_driver);
+}
+
+MODULE_AUTHOR("Rudolf Marek <r.marek@sh.cvut.cz>");
+MODULE_DESCRIPTION("AMD K8 core temperature monitor");
+MODULE_LICENSE("GPL");
+
+module_init(k8temp_init)
+module_exit(k8temp_exit)
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index 071f0fc6ade..d69f3cf0712 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -1,7 +1,7 @@
/*
* lm63.c - driver for the National Semiconductor LM63 temperature sensor
* with integrated fan control
- * Copyright (C) 2004-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2004-2006 Jean Delvare <khali@linux-fr.org>
* Based on the lm90 driver.
*
* The LM63 is a sensor chip made by National Semiconductor. It measures
@@ -46,6 +46,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -330,6 +331,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%u\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute *devattr,
+ char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm63_data *data = lm63_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%u\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
set_fan, 1);
@@ -350,8 +361,52 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp8, NULL, 2);
static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp2_crit_hyst,
set_temp2_crit_hyst);
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(fan1_min_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm63_attributes[] = {
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &dev_attr_temp2_crit_hyst.attr,
+
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group = {
+ .attrs = lm63_attributes,
+};
+
+static struct attribute *lm63_attributes_fan1[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+
+ &sensor_dev_attr_fan1_min_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm63_group_fan1 = {
+ .attrs = lm63_attributes_fan1,
+};
+
/*
* Real code
*/
@@ -438,37 +493,26 @@ static int lm63_detect(struct i2c_adapter *adapter, int address, int kind)
lm63_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm63_group)))
goto exit_detach;
+ if (data->config & 0x04) { /* tachometer enabled */
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm63_group_fan1)))
+ goto exit_remove_files;
}
- if (data->config & 0x04) { /* tachometer enabled */
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_fan1_min.dev_attr);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm63_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm63_group_fan1);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -518,6 +562,8 @@ static int lm63_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group);
+ sysfs_remove_group(&client->dev.kobj, &lm63_group_fan1);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm75.c b/drivers/hwmon/lm75.c
index fc25b90ec24..7c65b8bb6d7 100644
--- a/drivers/hwmon/lm75.c
+++ b/drivers/hwmon/lm75.c
@@ -112,6 +112,18 @@ static int lm75_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm75_detect);
}
+static struct attribute *lm75_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm75_group = {
+ .attrs = lm75_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -199,18 +211,19 @@ static int lm75_detect(struct i2c_adapter *adapter, int address, int kind)
lm75_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm75_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm75_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -223,6 +236,7 @@ static int lm75_detach_client(struct i2c_client *client)
{
struct lm75_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm75_group);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm77.c b/drivers/hwmon/lm77.c
index 459cc977380..dd969f1e841 100644
--- a/drivers/hwmon/lm77.c
+++ b/drivers/hwmon/lm77.c
@@ -212,6 +212,23 @@ static int lm77_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm77_detect);
}
+static struct attribute *lm77_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_temp1_min_hyst.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm77_group = {
+ .attrs = lm77_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -317,22 +334,19 @@ static int lm77_detect(struct i2c_adapter *adapter, int address, int kind)
lm77_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm77_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm77_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -345,6 +359,7 @@ static int lm77_detach_client(struct i2c_client *client)
{
struct lm77_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm77_group);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm78.c b/drivers/hwmon/lm78.c
index a6ce7abf860..ac1b746df6d 100644
--- a/drivers/hwmon/lm78.c
+++ b/drivers/hwmon/lm78.c
@@ -175,6 +175,7 @@ static struct i2c_driver lm78_driver = {
static struct i2c_driver lm78_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "lm78-isa",
},
.attach_adapter = lm78_isa_attach_adapter,
@@ -481,6 +482,50 @@ static int lm78_isa_attach_adapter(struct i2c_adapter *adapter)
return lm78_detect(adapter, isa_address, -1);
}
+static struct attribute *lm78_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_cpu0_vid.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm78_group = {
+ .attrs = lm78_attributes,
+};
+
/* This function is called by i2c_probe */
static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
{
@@ -615,50 +660,19 @@ static int lm78_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm78_group)))
+ goto ERROR3;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto ERROR3;
+ goto ERROR4;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_min);
- device_create_file(&new_client->dev, &dev_attr_fan3_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
-
return 0;
+ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &lm78_group);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -676,6 +690,7 @@ static int lm78_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm78_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm80.c b/drivers/hwmon/lm80.c
index b4ccdfc0120..064516d824a 100644
--- a/drivers/hwmon/lm80.c
+++ b/drivers/hwmon/lm80.c
@@ -394,6 +394,48 @@ static int lm80_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm80_detect);
}
+static struct attribute *lm80_attributes[] = {
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_max.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm80_group = {
+ .attrs = lm80_attributes,
+};
+
static int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i, cur;
@@ -452,48 +494,19 @@ static int lm80_detect(struct i2c_adapter *adapter, int address, int kind)
data->fan_min[1] = lm80_read_value(new_client, LM80_REG_FAN_MIN(2));
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm80_group)))
+ goto error_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error_detach;
+ goto error_remove;
}
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+error_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm80_group);
error_detach:
i2c_detach_client(new_client);
error_free:
@@ -508,7 +521,7 @@ static int lm80_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
-
+ sysfs_remove_group(&client->dev.kobj, &lm80_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm83.c b/drivers/hwmon/lm83.c
index 2137d7879df..feb87b41e98 100644
--- a/drivers/hwmon/lm83.c
+++ b/drivers/hwmon/lm83.c
@@ -1,7 +1,7 @@
/*
* lm83.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
*
* Heavily inspired from the lm78, lm75 and adm1021 drivers. The LM83 is
* a sensor chip made by National Semiconductor. It reports up to four
@@ -40,6 +40,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -191,6 +192,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%d\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm83_data *data = lm83_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2);
@@ -208,8 +219,64 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IRUGO, show_temp, NULL, 8);
static SENSOR_DEVICE_ATTR(temp3_crit, S_IWUSR | S_IRUGO, show_temp,
set_temp, 8);
static SENSOR_DEVICE_ATTR(temp4_crit, S_IRUGO, show_temp, NULL, 8);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp3_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp3_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp3_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 8);
+static SENSOR_DEVICE_ATTR(temp4_crit_alarm, S_IRUGO, show_alarm, NULL, 9);
+static SENSOR_DEVICE_ATTR(temp4_input_fault, S_IRUGO, show_alarm, NULL, 10);
+static SENSOR_DEVICE_ATTR(temp4_max_alarm, S_IRUGO, show_alarm, NULL, 12);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 13);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 15);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm83_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm83_group = {
+ .attrs = lm83_attributes,
+};
+
+static struct attribute *lm83_attributes_opt[] = {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit.dev_attr.attr,
+
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp4_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group lm83_group_opt = {
+ .attrs = lm83_attributes_opt,
+};
+
/*
* Real code
*/
@@ -318,59 +385,32 @@ static int lm83_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_free;
/*
- * Initialize the LM83 chip
- * (Nothing to do for this one.)
- */
-
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto exit_detach;
- }
-
- /*
+ * Register sysfs hooks
* The LM82 can only monitor one external diode which is
* at the same register as the LM83 temp3 entry - so we
* declare 1 and 3 common, and then 2 and 4 only for the LM83.
*/
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_input.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_max.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp3_crit.dev_attr);
-
- device_create_file(&new_client->dev, &dev_attr_alarms);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm83_group)))
+ goto exit_detach;
if (kind == lm83) {
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_input.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_max.dev_attr);
-
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp4_crit.dev_attr);
+ if ((err = sysfs_create_group(&new_client->dev.kobj,
+ &lm83_group_opt)))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm83_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm83_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -385,6 +425,8 @@ static int lm83_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm83_group);
+ sysfs_remove_group(&client->dev.kobj, &lm83_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c
index 342e9663119..2c3293cf69d 100644
--- a/drivers/hwmon/lm85.c
+++ b/drivers/hwmon/lm85.c
@@ -1025,6 +1025,89 @@ static int lm85_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm85_detect);
}
+static struct attribute *lm85_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan4_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan4_min.attr,
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3_enable.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_pwm1_auto_channels.attr,
+ &dev_attr_pwm2_auto_channels.attr,
+ &dev_attr_pwm3_auto_channels.attr,
+ &dev_attr_pwm1_auto_pwm_min.attr,
+ &dev_attr_pwm2_auto_pwm_min.attr,
+ &dev_attr_pwm3_auto_pwm_min.attr,
+ &dev_attr_pwm1_auto_pwm_minctl.attr,
+ &dev_attr_pwm2_auto_pwm_minctl.attr,
+ &dev_attr_pwm3_auto_pwm_minctl.attr,
+ &dev_attr_pwm1_auto_pwm_freq.attr,
+ &dev_attr_pwm2_auto_pwm_freq.attr,
+ &dev_attr_pwm3_auto_pwm_freq.attr,
+ &dev_attr_temp1_auto_temp_off.attr,
+ &dev_attr_temp2_auto_temp_off.attr,
+ &dev_attr_temp3_auto_temp_off.attr,
+ &dev_attr_temp1_auto_temp_min.attr,
+ &dev_attr_temp2_auto_temp_min.attr,
+ &dev_attr_temp3_auto_temp_min.attr,
+ &dev_attr_temp1_auto_temp_max.attr,
+ &dev_attr_temp2_auto_temp_max.attr,
+ &dev_attr_temp3_auto_temp_max.attr,
+ &dev_attr_temp1_auto_temp_crit.attr,
+ &dev_attr_temp2_auto_temp_crit.attr,
+ &dev_attr_temp3_auto_temp_crit.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm85_group = {
+ .attrs = lm85_attributes,
+};
+
+static struct attribute *lm85_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm85_group_opt = {
+ .attrs = lm85_attributes_opt,
+};
+
static int lm85_detect(struct i2c_adapter *adapter, int address,
int kind)
{
@@ -1163,87 +1246,33 @@ static int lm85_detect(struct i2c_adapter *adapter, int address,
lm85_init_client(new_client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm85_group)))
goto ERROR2;
- }
-
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan3_input);
- device_create_file(&new_client->dev, &dev_attr_fan4_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan3_min);
- device_create_file(&new_client->dev, &dev_attr_fan4_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm3);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
- device_create_file(&new_client->dev, &dev_attr_pwm3_enable);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_vrm);
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_channels);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_min);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_minctl);
- device_create_file(&new_client->dev, &dev_attr_pwm1_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_pwm2_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_pwm3_auto_pwm_freq);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_off);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_auto_temp_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_auto_temp_crit);
- device_create_file(&new_client->dev, &dev_attr_temp3_auto_temp_crit);
/* The ADT7463 has an optional VRM 10 mode where pin 21 is used
as a sixth digital VID input rather than an analog input. */
data->vid = lm85_read_value(new_client, LM85_REG_VID);
- if (!(kind == adt7463 && (data->vid & 0x80))) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
+ if (!(kind == adt7463 && (data->vid & 0x80)))
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto ERROR3;
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR3;
}
return 0;
/* Error out and cleanup code */
+ ERROR3:
+ sysfs_remove_group(&new_client->dev.kobj, &lm85_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm85_group_opt);
ERROR2:
i2c_detach_client(new_client);
ERROR1:
@@ -1256,6 +1285,8 @@ static int lm85_detach_client(struct i2c_client *client)
{
struct lm85_data *data = i2c_get_clientdata(client);
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm85_group);
+ sysfs_remove_group(&client->dev.kobj, &lm85_group_opt);
i2c_detach_client(client);
kfree(data);
return 0;
diff --git a/drivers/hwmon/lm87.c b/drivers/hwmon/lm87.c
index e6c1b638c97..3ce825489e3 100644
--- a/drivers/hwmon/lm87.c
+++ b/drivers/hwmon/lm87.c
@@ -542,6 +542,78 @@ static int lm87_attach_adapter(struct i2c_adapter *adapter)
return i2c_probe(adapter, &addr_data, lm87_detect);
}
+static struct attribute *lm87_attributes[] = {
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_crit.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_aout_output.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm87_group = {
+ .attrs = lm87_attributes,
+};
+
+static struct attribute *lm87_attributes_opt[] = {
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+
+ &dev_attr_in7_input.attr,
+ &dev_attr_in7_min.attr,
+ &dev_attr_in7_max.attr,
+
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_min.attr,
+ &dev_attr_temp3_crit.attr,
+
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm87_group_opt = {
+ .attrs = lm87_attributes_opt,
+};
+
/*
* The following function does more than just detection. If detection
* succeeds, it also registers the new chip.
@@ -609,77 +681,90 @@ static int lm87_detect(struct i2c_adapter *adapter, int address, int kind)
data->in_scale[7] = 1875;
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm87_group)))
goto exit_detach;
- }
-
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
if (data->channel & CHAN_NO_FAN(0)) {
- device_create_file(&new_client->dev, &dev_attr_in6_input);
- device_create_file(&new_client->dev, &dev_attr_in6_min);
- device_create_file(&new_client->dev, &dev_attr_in6_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in6_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_max)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_div)))
+ goto exit_remove;
}
+
if (data->channel & CHAN_NO_FAN(1)) {
- device_create_file(&new_client->dev, &dev_attr_in7_input);
- device_create_file(&new_client->dev, &dev_attr_in7_min);
- device_create_file(&new_client->dev, &dev_attr_in7_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in7_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in7_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in7_max)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_div)))
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
-
if (data->channel & CHAN_TEMP3) {
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_min);
- device_create_file(&new_client->dev, &dev_attr_temp3_crit);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_crit)))
+ goto exit_remove;
} else {
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in5_input);
- device_create_file(&new_client->dev, &dev_attr_in5_min);
- device_create_file(&new_client->dev, &dev_attr_in5_max);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in0_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in0_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in0_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_max)))
+ goto exit_remove;
}
if (!(data->channel & CHAN_NO_VID)) {
- device_create_file(&new_client->dev, &dev_attr_cpu0_vid);
- device_create_file(&new_client->dev, &dev_attr_vrm);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_vrm)))
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_alarms);
- device_create_file(&new_client->dev, &dev_attr_aout_output);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm87_group);
+ sysfs_remove_group(&new_client->dev.kobj, &lm87_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -732,6 +817,8 @@ static int lm87_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm87_group);
+ sysfs_remove_group(&client->dev.kobj, &lm87_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm90.c b/drivers/hwmon/lm90.c
index d9eeaf7585b..6882ce75fee 100644
--- a/drivers/hwmon/lm90.c
+++ b/drivers/hwmon/lm90.c
@@ -1,7 +1,7 @@
/*
* lm90.c - Part of lm_sensors, Linux kernel modules for hardware
* monitoring
- * Copyright (C) 2003-2005 Jean Delvare <khali@linux-fr.org>
+ * Copyright (C) 2003-2006 Jean Delvare <khali@linux-fr.org>
*
* Based on the lm83 driver. The LM90 is a sensor chip made by National
* Semiconductor. It reports up to two temperatures (its own plus up to
@@ -79,6 +79,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/*
* Addresses to scan
@@ -327,6 +328,16 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *dummy,
return sprintf(buf, "%d\n", data->alarms);
}
+static ssize_t show_alarm(struct device *dev, struct device_attribute
+ *devattr, char *buf)
+{
+ struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
+ struct lm90_data *data = lm90_update_device(dev);
+ int bitnr = attr->index;
+
+ return sprintf(buf, "%d\n", (data->alarms >> bitnr) & 1);
+}
+
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp8, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp11, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp8,
@@ -344,8 +355,45 @@ static SENSOR_DEVICE_ATTR(temp2_crit, S_IWUSR | S_IRUGO, show_temp8,
static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IWUSR | S_IRUGO, show_temphyst,
set_temphyst, 3);
static SENSOR_DEVICE_ATTR(temp2_crit_hyst, S_IRUGO, show_temphyst, NULL, 4);
+
+/* Individual alarm files */
+static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, show_alarm, NULL, 0);
+static SENSOR_DEVICE_ATTR(temp2_crit_alarm, S_IRUGO, show_alarm, NULL, 1);
+static SENSOR_DEVICE_ATTR(temp2_input_fault, S_IRUGO, show_alarm, NULL, 2);
+static SENSOR_DEVICE_ATTR(temp2_min_alarm, S_IRUGO, show_alarm, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp2_max_alarm, S_IRUGO, show_alarm, NULL, 4);
+static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 5);
+static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 6);
+/* Raw alarm file for compatibility */
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *lm90_attributes[] = {
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit.dev_attr.attr,
+ &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_hyst.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_crit_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp2_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_min_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_alarm.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group lm90_group = {
+ .attrs = lm90_attributes,
+};
+
/* pec used for ADM1032 only */
static ssize_t show_pec(struct device *dev, struct device_attribute *dummy,
char *buf)
@@ -569,39 +617,25 @@ static int lm90_detect(struct i2c_adapter *adapter, int address, int kind)
lm90_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm90_group)))
+ goto exit_detach;
+ if (new_client->flags & I2C_CLIENT_PEC) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pec)))
+ goto exit_remove_files;
+ }
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_crit_hyst.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp2_crit_hyst.dev_attr);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
- if (new_client->flags & I2C_CLIENT_PEC)
- device_create_file(&new_client->dev, &dev_attr_pec);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &lm90_group);
+ device_remove_file(&new_client->dev, &dev_attr_pec);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -634,6 +668,8 @@ static int lm90_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm90_group);
+ device_remove_file(&client->dev, &dev_attr_pec);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/lm92.c b/drivers/hwmon/lm92.c
index 197f77226dc..30b536333f1 100644
--- a/drivers/hwmon/lm92.c
+++ b/drivers/hwmon/lm92.c
@@ -288,6 +288,23 @@ static int max6635_check(struct i2c_client *client)
return 1;
}
+static struct attribute *lm92_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_crit.attr,
+ &dev_attr_temp1_crit_hyst.attr,
+ &dev_attr_temp1_min.attr,
+ &dev_attr_temp1_min_hyst.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_alarms.attr,
+
+ NULL
+};
+
+static const struct attribute_group lm92_group = {
+ .attrs = lm92_attributes,
+};
+
/* The following function does more than just detection. If detection
succeeds, it also registers the new chip. */
static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
@@ -359,23 +376,19 @@ static int lm92_detect(struct i2c_adapter *adapter, int address, int kind)
lm92_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm92_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit);
- device_create_file(&new_client->dev, &dev_attr_temp1_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_min);
- device_create_file(&new_client->dev, &dev_attr_temp1_min_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &lm92_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -397,6 +410,7 @@ static int lm92_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &lm92_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/max1619.c b/drivers/hwmon/max1619.c
index b4135b5971f..2f58f651f03 100644
--- a/drivers/hwmon/max1619.c
+++ b/drivers/hwmon/max1619.c
@@ -34,6 +34,7 @@
#include <linux/hwmon.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
static unsigned short normal_i2c[] = { 0x18, 0x19, 0x1a,
0x29, 0x2a, 0x2b,
@@ -172,6 +173,22 @@ static DEVICE_ATTR(temp2_crit_hyst, S_IWUSR | S_IRUGO, show_temp_hyst2,
set_temp_hyst2);
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *max1619_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_min.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_crit.attr,
+ &dev_attr_temp2_crit_hyst.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group max1619_group = {
+ .attrs = max1619_attributes,
+};
+
/*
* Real code
*/
@@ -273,22 +290,19 @@ static int max1619_detect(struct i2c_adapter *adapter, int address, int kind)
max1619_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &max1619_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_min);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit);
- device_create_file(&new_client->dev, &dev_attr_temp2_crit_hyst);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &max1619_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -318,6 +332,7 @@ static int max1619_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &max1619_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/pc87360.c b/drivers/hwmon/pc87360.c
index ae05e483a77..3b8b81984ad 100644
--- a/drivers/hwmon/pc87360.c
+++ b/drivers/hwmon/pc87360.c
@@ -238,6 +238,7 @@ static struct pc87360_data *pc87360_update_device(struct device *dev);
static struct i2c_driver pc87360_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "pc87360",
},
.attach_adapter = pc87360_detect,
@@ -327,6 +328,12 @@ static struct sensor_device_attribute fan_min[] = {
SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, set_fan_min, 2),
};
+#define FAN_UNIT_ATTRS(X) \
+ &fan_input[X].dev_attr.attr, \
+ &fan_status[X].dev_attr.attr, \
+ &fan_div[X].dev_attr.attr, \
+ &fan_min[X].dev_attr.attr
+
static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -359,6 +366,19 @@ static struct sensor_device_attribute pwm[] = {
SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2),
};
+static struct attribute * pc8736x_fan_attr_array[] = {
+ FAN_UNIT_ATTRS(0),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ &pwm[0].dev_attr.attr,
+ &pwm[1].dev_attr.attr,
+ &pwm[2].dev_attr.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_fan_group = {
+ .attrs = pc8736x_fan_attr_array,
+};
+
static ssize_t show_in_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -471,6 +491,61 @@ static struct sensor_device_attribute in_max[] = {
SENSOR_ATTR(in10_max, S_IWUSR | S_IRUGO, show_in_max, set_in_max, 10),
};
+#define VIN_UNIT_ATTRS(X) \
+ &in_input[X].dev_attr.attr, \
+ &in_status[X].dev_attr.attr, \
+ &in_min[X].dev_attr.attr, \
+ &in_max[X].dev_attr.attr
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
+}
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", data->vrm);
+}
+static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pc87360_data *data = i2c_get_clientdata(client);
+ data->vrm = simple_strtoul(buf, NULL, 10);
+ return count;
+}
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
+
+static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pc87360_data *data = pc87360_update_device(dev);
+ return sprintf(buf, "%u\n", data->in_alarms);
+}
+static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+
+static struct attribute *pc8736x_vin_attr_array[] = {
+ VIN_UNIT_ATTRS(0),
+ VIN_UNIT_ATTRS(1),
+ VIN_UNIT_ATTRS(2),
+ VIN_UNIT_ATTRS(3),
+ VIN_UNIT_ATTRS(4),
+ VIN_UNIT_ATTRS(5),
+ VIN_UNIT_ATTRS(6),
+ VIN_UNIT_ATTRS(7),
+ VIN_UNIT_ATTRS(8),
+ VIN_UNIT_ATTRS(9),
+ VIN_UNIT_ATTRS(10),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms_in.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_vin_group = {
+ .attrs = pc8736x_vin_attr_array,
+};
+
static ssize_t show_therm_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
@@ -589,33 +664,22 @@ static struct sensor_device_attribute therm_crit[] = {
show_therm_crit, set_therm_crit, 2+11),
};
-static ssize_t show_vid(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", vid_from_reg(data->vid, data->vrm));
-}
-static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid, NULL);
-
-static ssize_t show_vrm(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", data->vrm);
-}
-static ssize_t set_vrm(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
-{
- struct i2c_client *client = to_i2c_client(dev);
- struct pc87360_data *data = i2c_get_clientdata(client);
- data->vrm = simple_strtoul(buf, NULL, 10);
- return count;
-}
-static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm);
-
-static ssize_t show_in_alarms(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct pc87360_data *data = pc87360_update_device(dev);
- return sprintf(buf, "%u\n", data->in_alarms);
-}
-static DEVICE_ATTR(alarms_in, S_IRUGO, show_in_alarms, NULL);
+#define THERM_UNIT_ATTRS(X) \
+ &therm_input[X].dev_attr.attr, \
+ &therm_status[X].dev_attr.attr, \
+ &therm_min[X].dev_attr.attr, \
+ &therm_max[X].dev_attr.attr, \
+ &therm_crit[X].dev_attr.attr
+
+static struct attribute * pc8736x_therm_attr_array[] = {
+ THERM_UNIT_ATTRS(0),
+ THERM_UNIT_ATTRS(1),
+ THERM_UNIT_ATTRS(2),
+ NULL
+};
+static const struct attribute_group pc8736x_therm_group = {
+ .attrs = pc8736x_therm_attr_array,
+};
static ssize_t show_temp_input(struct device *dev, struct device_attribute *devattr, char *buf)
{
@@ -735,6 +799,25 @@ static ssize_t show_temp_alarms(struct device *dev, struct device_attribute *att
}
static DEVICE_ATTR(alarms_temp, S_IRUGO, show_temp_alarms, NULL);
+#define TEMP_UNIT_ATTRS(X) \
+ &temp_input[X].dev_attr.attr, \
+ &temp_status[X].dev_attr.attr, \
+ &temp_min[X].dev_attr.attr, \
+ &temp_max[X].dev_attr.attr, \
+ &temp_crit[X].dev_attr.attr
+
+static struct attribute * pc8736x_temp_attr_array[] = {
+ TEMP_UNIT_ATTRS(0),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ /* include the few miscellaneous atts here */
+ &dev_attr_alarms_temp.attr,
+ NULL
+};
+static const struct attribute_group pc8736x_temp_group = {
+ .attrs = pc8736x_temp_attr_array,
+};
+
/*
* Device detection, registration and update
*/
@@ -935,60 +1018,69 @@ static int pc87360_detect(struct i2c_adapter *adapter)
pc87360_init_client(client, use_thermistors);
}
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ /* Register all-or-nothing sysfs groups */
+
+ if (data->innr &&
+ (err = sysfs_create_group(&client->dev.kobj,
+ &pc8736x_vin_group)))
goto ERROR3;
- }
- if (data->innr) {
- for (i = 0; i < 11; i++) {
- device_create_file(dev, &in_input[i].dev_attr);
- device_create_file(dev, &in_min[i].dev_attr);
- device_create_file(dev, &in_max[i].dev_attr);
- device_create_file(dev, &in_status[i].dev_attr);
- }
- device_create_file(dev, &dev_attr_cpu0_vid);
- device_create_file(dev, &dev_attr_vrm);
- device_create_file(dev, &dev_attr_alarms_in);
- }
+ if (data->innr == 14 &&
+ (err = sysfs_create_group(&client->dev.kobj,
+ &pc8736x_therm_group)))
+ goto ERROR3;
+
+ /* create device attr-files for varying sysfs groups */
if (data->tempnr) {
for (i = 0; i < data->tempnr; i++) {
- device_create_file(dev, &temp_input[i].dev_attr);
- device_create_file(dev, &temp_min[i].dev_attr);
- device_create_file(dev, &temp_max[i].dev_attr);
- device_create_file(dev, &temp_crit[i].dev_attr);
- device_create_file(dev, &temp_status[i].dev_attr);
- }
- device_create_file(dev, &dev_attr_alarms_temp);
- }
-
- if (data->innr == 14) {
- for (i = 0; i < 3; i++) {
- device_create_file(dev, &therm_input[i].dev_attr);
- device_create_file(dev, &therm_min[i].dev_attr);
- device_create_file(dev, &therm_max[i].dev_attr);
- device_create_file(dev, &therm_crit[i].dev_attr);
- device_create_file(dev, &therm_status[i].dev_attr);
+ if ((err = device_create_file(dev,
+ &temp_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_max[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_crit[i].dev_attr))
+ || (err = device_create_file(dev,
+ &temp_status[i].dev_attr)))
+ goto ERROR3;
}
+ if ((err = device_create_file(dev, &dev_attr_alarms_temp)))
+ goto ERROR3;
}
for (i = 0; i < data->fannr; i++) {
- if (FAN_CONFIG_MONITOR(data->fan_conf, i)) {
- device_create_file(dev, &fan_input[i].dev_attr);
- device_create_file(dev, &fan_min[i].dev_attr);
- device_create_file(dev, &fan_div[i].dev_attr);
- device_create_file(dev, &fan_status[i].dev_attr);
- }
- if (FAN_CONFIG_CONTROL(data->fan_conf, i))
- device_create_file(dev, &pwm[i].dev_attr);
+ if (FAN_CONFIG_MONITOR(data->fan_conf, i)
+ && ((err = device_create_file(dev,
+ &fan_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_div[i].dev_attr))
+ || (err = device_create_file(dev,
+ &fan_status[i].dev_attr))))
+ goto ERROR3;
+
+ if (FAN_CONFIG_CONTROL(data->fan_conf, i)
+ && (err = device_create_file(dev, &pwm[i].dev_attr)))
+ goto ERROR3;
}
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR3;
+ }
return 0;
ERROR3:
+ /* can still remove groups whose members were added individually */
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
+
i2c_detach_client(client);
ERROR2:
for (i = 0; i < 3; i++) {
@@ -1008,6 +1100,11 @@ static int pc87360_detach_client(struct i2c_client *client)
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_temp_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_fan_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_therm_group);
+ sysfs_remove_group(&client->dev.kobj, &pc8736x_vin_group);
+
if ((i = i2c_detach_client(client)))
return i;
diff --git a/drivers/hwmon/sis5595.c b/drivers/hwmon/sis5595.c
index 063f71c5f07..95a4b5d9eaf 100644
--- a/drivers/hwmon/sis5595.c
+++ b/drivers/hwmon/sis5595.c
@@ -61,6 +61,7 @@
#include <linux/init.h>
#include <linux/jiffies.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -200,6 +201,7 @@ static void sis5595_init_client(struct i2c_client *client);
static struct i2c_driver sis5595_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "sis5595",
},
.attach_adapter = sis5595_detect,
@@ -472,6 +474,50 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
return sprintf(buf, "%d\n", data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+
+static struct attribute *sis5595_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group sis5595_group = {
+ .attrs = sis5595_attributes,
+};
+
+static struct attribute *sis5595_attributes_opt[] = {
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ NULL
+};
+
+static const struct attribute_group sis5595_group_opt = {
+ .attrs = sis5595_attributes_opt,
+};
/* This is called when the module is loaded */
static int sis5595_detect(struct i2c_adapter *adapter)
@@ -565,43 +611,37 @@ static int sis5595_detect(struct i2c_adapter *adapter)
}
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &sis5595_group)))
+ goto exit_detach;
+ if (data->maxins == 4) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in4_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in4_max)))
+ goto exit_remove_files;
+ } else {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp1_max_hyst)))
+ goto exit_remove_files;
+ }
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- if (data->maxins == 4) {
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- }
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
- if (data->maxins == 3) {
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- }
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&new_client->dev.kobj, &sis5595_group_opt);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -618,6 +658,8 @@ static int sis5595_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &sis5595_group);
+ sysfs_remove_group(&client->dev.kobj, &sis5595_group_opt);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/smsc47b397.c b/drivers/hwmon/smsc47b397.c
index b6086186d22..72b0e2d8650 100644
--- a/drivers/hwmon/smsc47b397.c
+++ b/drivers/hwmon/smsc47b397.c
@@ -176,9 +176,6 @@ sysfs_temp(2);
sysfs_temp(3);
sysfs_temp(4);
-#define device_create_file_temp(client, num) \
- device_create_file(&client->dev, &dev_attr_temp##num##_input)
-
/* FAN: 1 RPM/bit
REG: count of 90kHz pulses / revolution */
static int fan_from_reg(u16 reg)
@@ -205,8 +202,22 @@ sysfs_fan(2);
sysfs_fan(3);
sysfs_fan(4);
-#define device_create_file_fan(client, num) \
- device_create_file(&client->dev, &dev_attr_fan##num##_input)
+static struct attribute *smsc47b397_attributes[] = {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp4_input.attr,
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan4_input.attr,
+
+ NULL
+};
+
+static const struct attribute_group smsc47b397_group = {
+ .attrs = smsc47b397_attributes,
+};
static int smsc47b397_detach_client(struct i2c_client *client)
{
@@ -214,6 +225,7 @@ static int smsc47b397_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47b397_group);
if ((err = i2c_detach_client(client)))
return err;
@@ -228,6 +240,7 @@ static int smsc47b397_detect(struct i2c_adapter *adapter);
static struct i2c_driver smsc47b397_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "smsc47b397",
},
.attach_adapter = smsc47b397_detect,
@@ -267,24 +280,19 @@ static int smsc47b397_detect(struct i2c_adapter *adapter)
if ((err = i2c_attach_client(new_client)))
goto error_free;
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &smsc47b397_group)))
+ goto error_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto error_detach;
+ goto error_remove;
}
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- device_create_file_temp(new_client, 3);
- device_create_file_temp(new_client, 4);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
- device_create_file_fan(new_client, 4);
-
return 0;
+error_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &smsc47b397_group);
error_detach:
i2c_detach_client(new_client);
error_free:
diff --git a/drivers/hwmon/smsc47m1.c b/drivers/hwmon/smsc47m1.c
index 825e8f72698..47132fd26b1 100644
--- a/drivers/hwmon/smsc47m1.c
+++ b/drivers/hwmon/smsc47m1.c
@@ -35,6 +35,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
/* Address is autodetected, there is no default value */
@@ -128,6 +129,7 @@ static struct smsc47m1_data *smsc47m1_update_device(struct device *dev,
static struct i2c_driver smsc47m1_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "smsc47m1",
},
.attach_adapter = smsc47m1_detect,
@@ -346,6 +348,30 @@ fan_present(2);
static DEVICE_ATTR(alarms, S_IRUGO, get_alarms, NULL);
+/* Almost all sysfs files may or may not be created depending on the chip
+ setup so we create them individually. It is still convenient to define a
+ group to remove them all at once. */
+static struct attribute *smsc47m1_attributes[] = {
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm1_enable.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm2_enable.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m1_group = {
+ .attrs = smsc47m1_attributes,
+};
+
static int __init smsc47m1_find(unsigned short *addr)
{
u8 val;
@@ -428,7 +454,8 @@ static int smsc47m1_detect(struct i2c_adapter *adapter)
pwm2 = (smsc47m1_read_value(new_client, SMSC47M1_REG_PPIN(1)) & 0x05)
== 0x04;
if (!(fan1 || fan2 || pwm1 || pwm2)) {
- dev_warn(&new_client->dev, "Device is not configured, will not use\n");
+ dev_warn(&adapter->dev, "Device at 0x%x is not configured, "
+ "will not use\n", new_client->addr);
err = -ENODEV;
goto error_free;
}
@@ -445,46 +472,62 @@ static int smsc47m1_detect(struct i2c_adapter *adapter)
smsc47m1_update_device(&new_client->dev, 1);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto error_detach;
- }
-
if (fan1) {
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan1_div)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "Fan 1 not enabled by hardware, "
"skipping\n");
if (fan2) {
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan2_div)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "Fan 2 not enabled by hardware, "
"skipping\n");
if (pwm1) {
- device_create_file(&new_client->dev, &dev_attr_pwm1);
- device_create_file(&new_client->dev, &dev_attr_pwm1_enable);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm1))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm1_enable)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "PWM 1 not enabled by hardware, "
"skipping\n");
if (pwm2) {
- device_create_file(&new_client->dev, &dev_attr_pwm2);
- device_create_file(&new_client->dev, &dev_attr_pwm2_enable);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm2))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_pwm2_enable)))
+ goto error_remove_files;
} else
dev_dbg(&new_client->dev, "PWM 2 not enabled by hardware, "
"skipping\n");
- device_create_file(&new_client->dev, &dev_attr_alarms);
+ if ((err = device_create_file(&new_client->dev, &dev_attr_alarms)))
+ goto error_remove_files;
+
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto error_remove_files;
+ }
return 0;
-error_detach:
+error_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &smsc47m1_group);
i2c_detach_client(new_client);
error_free:
kfree(data);
@@ -499,6 +542,7 @@ static int smsc47m1_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m1_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/smsc47m192.c b/drivers/hwmon/smsc47m192.c
index bdc4570acf9..a6833f43739 100644
--- a/drivers/hwmon/smsc47m192.c
+++ b/drivers/hwmon/smsc47m192.c
@@ -30,6 +30,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/hwmon-vid.h>
#include <linux/err.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, I2C_CLIENT_END };
@@ -370,6 +371,75 @@ static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, 0x0200);
static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, 0x0400);
static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, 0x0800);
+static struct attribute *smsc47m192_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_alarm.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_alarm.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_alarm.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_alarm.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_alarm.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_alarm.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_alarm.dev_attr.attr,
+
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_offset.dev_attr.attr,
+ &sensor_dev_attr_temp1_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_min.dev_attr.attr,
+ &sensor_dev_attr_temp2_offset.dev_attr.attr,
+ &sensor_dev_attr_temp2_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp2_input_fault.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_min.dev_attr.attr,
+ &sensor_dev_attr_temp3_offset.dev_attr.attr,
+ &sensor_dev_attr_temp3_alarm.dev_attr.attr,
+ &sensor_dev_attr_temp3_input_fault.dev_attr.attr,
+
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group = {
+ .attrs = smsc47m192_attributes,
+};
+
+static struct attribute *smsc47m192_attributes_in4[] = {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_alarm.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group smsc47m192_group_in4 = {
+ .attrs = smsc47m192_attributes_in4,
+};
+
/* This function is called when:
* smsc47m192_driver is inserted (when this module is loaded), for each
available adapter
@@ -471,80 +541,28 @@ static int smsc47m192_detect(struct i2c_adapter *adapter, int address,
smsc47m192_init_client(client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&client->dev.kobj, &smsc47m192_group)))
goto exit_detach;
- }
-
- device_create_file(&client->dev, &sensor_dev_attr_in0_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in0_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in1_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in2_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in3_alarm.dev_attr);
/* Pin 110 is either in4 (+12V) or VID4 */
config = i2c_smbus_read_byte_data(client, SMSC47M192_REG_CONFIG);
if (!(config & 0x20)) {
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_input.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_max.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_in4_alarm.dev_attr);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &smsc47m192_group_in4)))
+ goto exit_remove_files;
+ }
+
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(&client->dev, &sensor_dev_attr_in5_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in5_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in6_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_in7_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp1_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp1_alarm.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp2_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp2_alarm.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp2_input_fault.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_max.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_min.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp3_offset.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_temp3_alarm.dev_attr);
- device_create_file(&client->dev,
- &sensor_dev_attr_temp3_input_fault.dev_attr);
- device_create_file(&client->dev, &dev_attr_cpu0_vid);
- device_create_file(&client->dev, &dev_attr_vrm);
return 0;
+exit_remove_files:
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4);
exit_detach:
i2c_detach_client(client);
exit_free:
@@ -559,6 +577,8 @@ static int smsc47m192_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group);
+ sysfs_remove_group(&client->dev.kobj, &smsc47m192_group_in4);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/via686a.c b/drivers/hwmon/via686a.c
index 166298f1f19..f8acada0537 100644
--- a/drivers/hwmon/via686a.c
+++ b/drivers/hwmon/via686a.c
@@ -40,6 +40,7 @@
#include <linux/err.h>
#include <linux/init.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
#include <asm/io.h>
@@ -570,10 +571,53 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr, ch
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *via686a_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_max.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp3_max_hyst.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group via686a_group = {
+ .attrs = via686a_attributes,
+};
+
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
static struct i2c_driver via686a_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "via686a",
},
.attach_adapter = via686a_detect,
@@ -649,46 +693,19 @@ static int via686a_detect(struct i2c_adapter *adapter)
via686a_init_client(new_client);
/* Register sysfs hooks */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &via686a_group)))
+ goto exit_detach;
+
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove_files;
}
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
- device_create_file(&new_client->dev, &dev_attr_in2_input);
- device_create_file(&new_client->dev, &dev_attr_in3_input);
- device_create_file(&new_client->dev, &dev_attr_in4_input);
- device_create_file(&new_client->dev, &dev_attr_in0_min);
- device_create_file(&new_client->dev, &dev_attr_in1_min);
- device_create_file(&new_client->dev, &dev_attr_in2_min);
- device_create_file(&new_client->dev, &dev_attr_in3_min);
- device_create_file(&new_client->dev, &dev_attr_in4_min);
- device_create_file(&new_client->dev, &dev_attr_in0_max);
- device_create_file(&new_client->dev, &dev_attr_in1_max);
- device_create_file(&new_client->dev, &dev_attr_in2_max);
- device_create_file(&new_client->dev, &dev_attr_in3_max);
- device_create_file(&new_client->dev, &dev_attr_in4_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_input);
- device_create_file(&new_client->dev, &dev_attr_temp2_input);
- device_create_file(&new_client->dev, &dev_attr_temp3_input);
- device_create_file(&new_client->dev, &dev_attr_temp1_max);
- device_create_file(&new_client->dev, &dev_attr_temp2_max);
- device_create_file(&new_client->dev, &dev_attr_temp3_max);
- device_create_file(&new_client->dev, &dev_attr_temp1_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp2_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_temp3_max_hyst);
- device_create_file(&new_client->dev, &dev_attr_fan1_input);
- device_create_file(&new_client->dev, &dev_attr_fan2_input);
- device_create_file(&new_client->dev, &dev_attr_fan1_min);
- device_create_file(&new_client->dev, &dev_attr_fan2_min);
- device_create_file(&new_client->dev, &dev_attr_fan1_div);
- device_create_file(&new_client->dev, &dev_attr_fan2_div);
- device_create_file(&new_client->dev, &dev_attr_alarms);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&new_client->dev.kobj, &via686a_group);
exit_detach:
i2c_detach_client(new_client);
exit_free:
@@ -704,6 +721,7 @@ static int via686a_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &via686a_group);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/vt1211.c b/drivers/hwmon/vt1211.c
new file mode 100644
index 00000000000..25cc56003d7
--- /dev/null
+++ b/drivers/hwmon/vt1211.c
@@ -0,0 +1,1355 @@
+/*
+ * vt1211.c - driver for the VIA VT1211 Super-I/O chip integrated hardware
+ * monitoring features
+ * Copyright (C) 2006 Juerg Haefliger <juergh@gmail.com>
+ *
+ * This driver is based on the driver for kernel 2.4 by Mark D. Studebaker
+ * and its port to kernel 2.6 by Lars Ekman.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/jiffies.h>
+#include <linux/platform_device.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/hwmon-vid.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <asm/io.h>
+
+static int uch_config = -1;
+module_param(uch_config, int, 0);
+MODULE_PARM_DESC(uch_config, "Initialize the universal channel configuration");
+
+static int int_mode = -1;
+module_param(int_mode, int, 0);
+MODULE_PARM_DESC(int_mode, "Force the temperature interrupt mode");
+
+static struct platform_device *pdev;
+
+#define DRVNAME "vt1211"
+
+/* ---------------------------------------------------------------------
+ * Registers
+ *
+ * The sensors are defined as follows.
+ *
+ * Sensor Voltage Mode Temp Mode Notes (from the datasheet)
+ * -------- ------------ --------- --------------------------
+ * Reading 1 temp1 Intel thermal diode
+ * Reading 3 temp2 Internal thermal diode
+ * UCH1/Reading2 in0 temp3 NTC type thermistor
+ * UCH2 in1 temp4 +2.5V
+ * UCH3 in2 temp5 VccP
+ * UCH4 in3 temp6 +5V
+ * UCH5 in4 temp7 +12V
+ * 3.3V in5 Internal VDD (+3.3V)
+ *
+ * --------------------------------------------------------------------- */
+
+/* Voltages (in) numbered 0-5 (ix) */
+#define VT1211_REG_IN(ix) (0x21 + (ix))
+#define VT1211_REG_IN_MIN(ix) ((ix) == 0 ? 0x3e : 0x2a + 2 * (ix))
+#define VT1211_REG_IN_MAX(ix) ((ix) == 0 ? 0x3d : 0x29 + 2 * (ix))
+
+/* Temperatures (temp) numbered 0-6 (ix) */
+static u8 regtemp[] = {0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25};
+static u8 regtempmax[] = {0x39, 0x1d, 0x3d, 0x2b, 0x2d, 0x2f, 0x31};
+static u8 regtemphyst[] = {0x3a, 0x1e, 0x3e, 0x2c, 0x2e, 0x30, 0x32};
+
+/* Fans numbered 0-1 (ix) */
+#define VT1211_REG_FAN(ix) (0x29 + (ix))
+#define VT1211_REG_FAN_MIN(ix) (0x3b + (ix))
+#define VT1211_REG_FAN_DIV 0x47
+
+/* PWMs numbered 0-1 (ix) */
+/* Auto points numbered 0-3 (ap) */
+#define VT1211_REG_PWM(ix) (0x60 + (ix))
+#define VT1211_REG_PWM_CLK 0x50
+#define VT1211_REG_PWM_CTL 0x51
+#define VT1211_REG_PWM_AUTO_TEMP(ap) (0x55 - (ap))
+#define VT1211_REG_PWM_AUTO_PWM(ix, ap) (0x58 + 2 * (ix) - (ap))
+
+/* Miscellaneous registers */
+#define VT1211_REG_CONFIG 0x40
+#define VT1211_REG_ALARM1 0x41
+#define VT1211_REG_ALARM2 0x42
+#define VT1211_REG_VID 0x45
+#define VT1211_REG_UCH_CONFIG 0x4a
+#define VT1211_REG_TEMP1_CONFIG 0x4b
+#define VT1211_REG_TEMP2_CONFIG 0x4c
+
+/* In, temp & fan alarm bits */
+static const u8 bitalarmin[] = {11, 0, 1, 3, 8, 2, 9};
+static const u8 bitalarmtemp[] = {4, 15, 11, 0, 1, 3, 8};
+static const u8 bitalarmfan[] = {6, 7};
+
+/* ---------------------------------------------------------------------
+ * Data structures and manipulation thereof
+ * --------------------------------------------------------------------- */
+
+struct vt1211_data {
+ unsigned short addr;
+ const char *name;
+ struct class_device *class_dev;
+
+ struct mutex update_lock;
+ char valid; /* !=0 if following fields are valid */
+ unsigned long last_updated; /* In jiffies */
+
+ /* Register values */
+ u8 in[6];
+ u8 in_max[6];
+ u8 in_min[6];
+ u8 temp[7];
+ u8 temp_max[7];
+ u8 temp_hyst[7];
+ u8 fan[2];
+ u8 fan_min[2];
+ u8 fan_div[2];
+ u8 fan_ctl;
+ u8 pwm[2];
+ u8 pwm_ctl[2];
+ u8 pwm_clk;
+ u8 pwm_auto_temp[4];
+ u8 pwm_auto_pwm[2][4];
+ u8 vid; /* Read once at init time */
+ u8 vrm;
+ u8 uch_config; /* Read once at init time */
+ u16 alarms;
+};
+
+/* ix = [0-5] */
+#define ISVOLT(ix, uch_config) ((ix) > 4 ? 1 : \
+ !(((uch_config) >> ((ix) + 2)) & 1))
+
+/* ix = [0-6] */
+#define ISTEMP(ix, uch_config) ((ix) < 2 ? 1 : \
+ ((uch_config) >> (ix)) & 1)
+
+/* in5 (ix = 5) is special. It's the internal 3.3V so it's scaled in the
+ driver according to the VT1211 BIOS porting guide */
+#define IN_FROM_REG(ix, reg) ((reg) < 3 ? 0 : (ix) == 5 ? \
+ (((reg) - 3) * 15882 + 479) / 958 : \
+ (((reg) - 3) * 10000 + 479) / 958)
+#define IN_TO_REG(ix, val) (SENSORS_LIMIT((ix) == 5 ? \
+ ((val) * 958 + 7941) / 15882 + 3 : \
+ ((val) * 958 + 5000) / 10000 + 3, 0, 255))
+
+/* temp1 (ix = 0) is an intel thermal diode which is scaled in user space.
+ temp2 (ix = 1) is the internal temp diode so it's scaled in the driver
+ according to some measurements that I took on an EPIA M10000.
+ temp3-7 are thermistor based so the driver returns the voltage measured at
+ the pin (range 0V - 2.2V). */
+#define TEMP_FROM_REG(ix, reg) ((ix) == 0 ? (reg) * 1000 : \
+ (ix) == 1 ? (reg) < 51 ? 0 : \
+ ((reg) - 51) * 1000 : \
+ ((253 - (reg)) * 2200 + 105) / 210)
+#define TEMP_TO_REG(ix, val) SENSORS_LIMIT( \
+ ((ix) == 0 ? ((val) + 500) / 1000 : \
+ (ix) == 1 ? ((val) + 500) / 1000 + 51 : \
+ 253 - ((val) * 210 + 1100) / 2200), 0, 255)
+
+#define DIV_FROM_REG(reg) (1 << (reg))
+
+#define RPM_FROM_REG(reg, div) (((reg) == 0) || ((reg) == 255) ? 0 : \
+ 1310720 / (reg) / DIV_FROM_REG(div))
+#define RPM_TO_REG(val, div) ((val) == 0 ? 255 : \
+ SENSORS_LIMIT((1310720 / (val) / \
+ DIV_FROM_REG(div)), 1, 254))
+
+/* ---------------------------------------------------------------------
+ * Super-I/O constants and functions
+ * --------------------------------------------------------------------- */
+
+/* Configuration & data index port registers */
+#define SIO_REG_CIP 0x2e
+#define SIO_REG_DIP 0x2f
+
+/* Configuration registers */
+#define SIO_VT1211_LDN 0x07 /* logical device number */
+#define SIO_VT1211_DEVID 0x20 /* device ID */
+#define SIO_VT1211_DEVREV 0x21 /* device revision */
+#define SIO_VT1211_ACTIVE 0x30 /* HW monitor active */
+#define SIO_VT1211_BADDR 0x60 /* base I/O address */
+#define SIO_VT1211_ID 0x3c /* VT1211 device ID */
+
+/* VT1211 logical device numbers */
+#define SIO_VT1211_LDN_HWMON 0x0b /* HW monitor */
+
+static inline void superio_outb(int reg, int val)
+{
+ outb(reg, SIO_REG_CIP);
+ outb(val, SIO_REG_DIP);
+}
+
+static inline int superio_inb(int reg)
+{
+ outb(reg, SIO_REG_CIP);
+ return inb(SIO_REG_DIP);
+}
+
+static inline void superio_select(int ldn)
+{
+ outb(SIO_VT1211_LDN, SIO_REG_CIP);
+ outb(ldn, SIO_REG_DIP);
+}
+
+static inline void superio_enter(void)
+{
+ outb(0x87, SIO_REG_CIP);
+ outb(0x87, SIO_REG_CIP);
+}
+
+static inline void superio_exit(void)
+{
+ outb(0xaa, SIO_REG_CIP);
+}
+
+/* ---------------------------------------------------------------------
+ * Device I/O access
+ * --------------------------------------------------------------------- */
+
+static inline u8 vt1211_read8(struct vt1211_data *data, u8 reg)
+{
+ return inb(data->addr + reg);
+}
+
+static inline void vt1211_write8(struct vt1211_data *data, u8 reg, u8 val)
+{
+ outb(val, data->addr + reg);
+}
+
+static struct vt1211_data *vt1211_update_device(struct device *dev)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ int ix, val;
+
+ mutex_lock(&data->update_lock);
+
+ /* registers cache is refreshed after 1 second */
+ if (time_after(jiffies, data->last_updated + HZ) || !data->valid) {
+ /* read VID */
+ data->vid = vt1211_read8(data, VT1211_REG_VID) & 0x1f;
+
+ /* voltage (in) registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->in); ix++) {
+ if (ISVOLT(ix, data->uch_config)) {
+ data->in[ix] = vt1211_read8(data,
+ VT1211_REG_IN(ix));
+ data->in_min[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MIN(ix));
+ data->in_max[ix] = vt1211_read8(data,
+ VT1211_REG_IN_MAX(ix));
+ }
+ }
+
+ /* temp registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->temp); ix++) {
+ if (ISTEMP(ix, data->uch_config)) {
+ data->temp[ix] = vt1211_read8(data,
+ regtemp[ix]);
+ data->temp_max[ix] = vt1211_read8(data,
+ regtempmax[ix]);
+ data->temp_hyst[ix] = vt1211_read8(data,
+ regtemphyst[ix]);
+ }
+ }
+
+ /* fan & pwm registers */
+ for (ix = 0; ix < ARRAY_SIZE(data->fan); ix++) {
+ data->fan[ix] = vt1211_read8(data,
+ VT1211_REG_FAN(ix));
+ data->fan_min[ix] = vt1211_read8(data,
+ VT1211_REG_FAN_MIN(ix));
+ data->pwm[ix] = vt1211_read8(data,
+ VT1211_REG_PWM(ix));
+ }
+ val = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (val >> 4) & 3;
+ data->fan_div[1] = (val >> 6) & 3;
+ data->fan_ctl = val & 0xf;
+
+ val = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = val & 0xf;
+ data->pwm_ctl[1] = (val >> 4) & 0xf;
+
+ data->pwm_clk = vt1211_read8(data, VT1211_REG_PWM_CLK);
+
+ /* pwm & temp auto point registers */
+ data->pwm_auto_pwm[0][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 1));
+ data->pwm_auto_pwm[0][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(0, 2));
+ data->pwm_auto_pwm[1][1] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 1));
+ data->pwm_auto_pwm[1][2] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_PWM(1, 2));
+ for (ix = 0; ix < ARRAY_SIZE(data->pwm_auto_temp); ix++) {
+ data->pwm_auto_temp[ix] = vt1211_read8(data,
+ VT1211_REG_PWM_AUTO_TEMP(ix));
+ }
+
+ /* alarm registers */
+ data->alarms = (vt1211_read8(data, VT1211_REG_ALARM2) << 8) |
+ vt1211_read8(data, VT1211_REG_ALARM1);
+
+ data->last_updated = jiffies;
+ data->valid = 1;
+ }
+
+ mutex_unlock(&data->update_lock);
+
+ return data;
+}
+
+/* ---------------------------------------------------------------------
+ * Voltage sysfs interfaces
+ * ix = [0-5]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_IN_INPUT 0
+#define SHOW_SET_IN_MIN 1
+#define SHOW_SET_IN_MAX 2
+#define SHOW_IN_ALARM 3
+
+static ssize_t show_in(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_IN_INPUT:
+ res = IN_FROM_REG(ix, data->in[ix]);
+ break;
+ case SHOW_SET_IN_MIN:
+ res = IN_FROM_REG(ix, data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ res = IN_FROM_REG(ix, data->in_max[ix]);
+ break;
+ case SHOW_IN_ALARM:
+ res = (data->alarms >> bitalarmin[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_in(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_IN_MIN:
+ data->in_min[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MIN(ix), data->in_min[ix]);
+ break;
+ case SHOW_SET_IN_MAX:
+ data->in_max[ix] = IN_TO_REG(ix, val);
+ vt1211_write8(data, VT1211_REG_IN_MAX(ix), data->in_max[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Temperature sysfs interfaces
+ * ix = [0-6]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_TEMP_INPUT 0
+#define SHOW_SET_TEMP_MAX 1
+#define SHOW_SET_TEMP_MAX_HYST 2
+#define SHOW_TEMP_ALARM 3
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_TEMP_INPUT:
+ res = TEMP_FROM_REG(ix, data->temp[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX:
+ res = TEMP_FROM_REG(ix, data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ res = TEMP_FROM_REG(ix, data->temp_hyst[ix]);
+ break;
+ case SHOW_TEMP_ALARM:
+ res = (data->alarms >> bitalarmtemp[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ mutex_lock(&data->update_lock);
+ switch (fn) {
+ case SHOW_SET_TEMP_MAX:
+ data->temp_max[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtempmax[ix],
+ data->temp_max[ix]);
+ break;
+ case SHOW_SET_TEMP_MAX_HYST:
+ data->temp_hyst[ix] = TEMP_TO_REG(ix, val);
+ vt1211_write8(data, regtemphyst[ix],
+ data->temp_hyst[ix]);
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Fan sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_FAN_INPUT 0
+#define SHOW_SET_FAN_MIN 1
+#define SHOW_SET_FAN_DIV 2
+#define SHOW_FAN_ALARM 3
+
+static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_FAN_INPUT:
+ res = RPM_FROM_REG(data->fan[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_MIN:
+ res = RPM_FROM_REG(data->fan_min[ix], data->fan_div[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ res = DIV_FROM_REG(data->fan_div[ix]);
+ break;
+ case SHOW_FAN_ALARM:
+ res = (data->alarms >> bitalarmfan[ix]) & 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_fan(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int reg;
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+
+ switch (fn) {
+ case SHOW_SET_FAN_MIN:
+ data->fan_min[ix] = RPM_TO_REG(val, data->fan_div[ix]);
+ vt1211_write8(data, VT1211_REG_FAN_MIN(ix),
+ data->fan_min[ix]);
+ break;
+ case SHOW_SET_FAN_DIV:
+ switch (val) {
+ case 1: data->fan_div[ix] = 0; break;
+ case 2: data->fan_div[ix] = 1; break;
+ case 4: data->fan_div[ix] = 2; break;
+ case 8: data->fan_div[ix] = 3; break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev, "fan div value %ld not "
+ "supported. Choose one of 1, 2, "
+ "4, or 8.\n", val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM sysfs interfaces
+ * ix = [0-1]
+ * --------------------------------------------------------------------- */
+
+#define SHOW_PWM 0
+#define SHOW_SET_PWM_ENABLE 1
+#define SHOW_SET_PWM_FREQ 2
+#define SHOW_SET_PWM_AUTO_CHANNELS_TEMP 3
+
+static ssize_t show_pwm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ int res;
+
+ switch (fn) {
+ case SHOW_PWM:
+ res = data->pwm[ix];
+ break;
+ case SHOW_SET_PWM_ENABLE:
+ res = ((data->pwm_ctl[ix] >> 3) & 1) ? 2 : 0;
+ break;
+ case SHOW_SET_PWM_FREQ:
+ res = 90000 >> (data->pwm_clk & 7);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ res = (data->pwm_ctl[ix] & 7) + 1;
+ break;
+ default:
+ res = 0;
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+ return sprintf(buf, "%d\n", res);
+}
+
+static ssize_t set_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int fn = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int tmp, reg;
+
+ mutex_lock(&data->update_lock);
+
+ switch (fn) {
+ case SHOW_SET_PWM_ENABLE:
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_FAN_DIV);
+ data->fan_div[0] = (reg >> 4) & 3;
+ data->fan_div[1] = (reg >> 6) & 3;
+ data->fan_ctl = reg & 0xf;
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ switch (val) {
+ case 0:
+ data->pwm_ctl[ix] &= 7;
+ /* disable SmartGuardian if both PWM outputs are
+ * disabled */
+ if ((data->pwm_ctl[ix ^ 1] & 1) == 0) {
+ data->fan_ctl &= 0xe;
+ }
+ break;
+ case 2:
+ data->pwm_ctl[ix] |= 8;
+ data->fan_ctl |= 1;
+ break;
+ default:
+ count = -EINVAL;
+ dev_warn(dev, "pwm mode %ld not supported. "
+ "Choose one of 0 or 2.\n", val);
+ goto EXIT;
+ }
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) |
+ data->pwm_ctl[0]));
+ vt1211_write8(data, VT1211_REG_FAN_DIV,
+ ((data->fan_div[1] << 6) |
+ (data->fan_div[0] << 4) |
+ data->fan_ctl));
+ break;
+ case SHOW_SET_PWM_FREQ:
+ val = 135000 / SENSORS_LIMIT(val, 135000 >> 7, 135000);
+ /* calculate tmp = log2(val) */
+ tmp = 0;
+ for (val >>= 1; val > 0; val >>= 1) {
+ tmp++;
+ }
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CLK);
+ data->pwm_clk = (reg & 0xf8) | tmp;
+ vt1211_write8(data, VT1211_REG_PWM_CLK, data->pwm_clk);
+ break;
+ case SHOW_SET_PWM_AUTO_CHANNELS_TEMP:
+ if ((val < 1) || (val > 7)) {
+ count = -EINVAL;
+ dev_warn(dev, "temp channel %ld not supported. "
+ "Choose a value between 1 and 7.\n", val);
+ goto EXIT;
+ }
+ if (!ISTEMP(val - 1, data->uch_config)) {
+ count = -EINVAL;
+ dev_warn(dev, "temp channel %ld is not available.\n",
+ val);
+ goto EXIT;
+ }
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+ data->pwm_ctl[ix] = (data->pwm_ctl[ix] & 8) | (val - 1);
+ vt1211_write8(data, VT1211_REG_PWM_CTL,
+ ((data->pwm_ctl[1] << 4) | data->pwm_ctl[0]));
+ break;
+ default:
+ dev_dbg(dev, "Unknown attr fetch (%d)\n", fn);
+ }
+
+EXIT:
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * PWM auto point definitions
+ * ix = [0-1]
+ * ap = [0-3]
+ * --------------------------------------------------------------------- */
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_temp mapping table:
+ * Note that there is only a single set of temp auto points that controls both
+ * PWM controllers. We still create 2 sets of sysfs files to make it look
+ * more consistent even though they map to the same registers.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 0 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 0 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 0 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ * 1 0 : pwm1/2 off temperature (pwm_auto_temp[0])
+ * 1 1 : pwm1/2 low speed temperature (pwm_auto_temp[1])
+ * 1 2 : pwm1/2 high speed temperature (pwm_auto_temp[2])
+ * 1 3 : pwm1/2 full speed temperature (pwm_auto_temp[3])
+ */
+
+static ssize_t show_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", TEMP_FROM_REG(data->pwm_ctl[ix] & 7,
+ data->pwm_auto_temp[ap]));
+}
+
+static ssize_t set_pwm_auto_point_temp(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+ int reg;
+
+ mutex_lock(&data->update_lock);
+
+ /* sync the data cache */
+ reg = vt1211_read8(data, VT1211_REG_PWM_CTL);
+ data->pwm_ctl[0] = reg & 0xf;
+ data->pwm_ctl[1] = (reg >> 4) & 0xf;
+
+ data->pwm_auto_temp[ap] = TEMP_TO_REG(data->pwm_ctl[ix] & 7, val);
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_TEMP(ap),
+ data->pwm_auto_temp[ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/*
+ * pwm[ix+1]_auto_point[ap+1]_pwm mapping table:
+ * Note that the PWM auto points 0 & 3 are hard-wired in the VT1211 and can't
+ * be changed.
+ *
+ * ix ap : description
+ * -------------------
+ * 0 0 : pwm1 off (pwm_auto_pwm[0][0], hard-wired to 0)
+ * 0 1 : pwm1 low speed duty cycle (pwm_auto_pwm[0][1])
+ * 0 2 : pwm1 high speed duty cycle (pwm_auto_pwm[0][2])
+ * 0 3 : pwm1 full speed (pwm_auto_pwm[0][3], hard-wired to 255)
+ * 1 0 : pwm2 off (pwm_auto_pwm[1][0], hard-wired to 0)
+ * 1 1 : pwm2 low speed duty cycle (pwm_auto_pwm[1][1])
+ * 1 2 : pwm2 high speed duty cycle (pwm_auto_pwm[1][2])
+ * 1 3 : pwm2 full speed (pwm_auto_pwm[1][3], hard-wired to 255)
+*/
+
+static ssize_t show_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+
+ return sprintf(buf, "%d\n", data->pwm_auto_pwm[ix][ap]);
+}
+
+static ssize_t set_pwm_auto_point_pwm(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ struct sensor_device_attribute_2 *sensor_attr_2 =
+ to_sensor_dev_attr_2(attr);
+ int ix = sensor_attr_2->index;
+ int ap = sensor_attr_2->nr;
+ long val = simple_strtol(buf, NULL, 10);
+
+ if ((val < 0) || (val > 255)) {
+ dev_err(dev, "pwm value %ld is out of range. "
+ "Choose a value between 0 and 255." , val);
+ return -EINVAL;
+ }
+
+ mutex_lock(&data->update_lock);
+ data->pwm_auto_pwm[ix][ap] = val;
+ vt1211_write8(data, VT1211_REG_PWM_AUTO_PWM(ix, ap),
+ data->pwm_auto_pwm[ix][ap]);
+ mutex_unlock(&data->update_lock);
+
+ return count;
+}
+
+/* ---------------------------------------------------------------------
+ * Miscellaneous sysfs interfaces (VRM, VID, name, and (legacy) alarms)
+ * --------------------------------------------------------------------- */
+
+static ssize_t show_vrm(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", data->vrm);
+}
+
+static ssize_t set_vrm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+ long val = simple_strtol(buf, NULL, 10);
+
+ data->vrm = val;
+
+ return count;
+}
+
+static ssize_t show_vid(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%d\n", vid_from_reg(data->vid, data->vrm));
+}
+
+static ssize_t show_name(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = dev_get_drvdata(dev);
+
+ return sprintf(buf, "%s\n", data->name);
+}
+
+static ssize_t show_alarms(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct vt1211_data *data = vt1211_update_device(dev);
+
+ return sprintf(buf, "%d\n", data->alarms);
+}
+
+/* ---------------------------------------------------------------------
+ * Device attribute structs
+ * --------------------------------------------------------------------- */
+
+#define SENSOR_ATTR_IN_INPUT(ix) \
+ SENSOR_ATTR_2(in##ix##_input, S_IRUGO, \
+ show_in, NULL, SHOW_IN_INPUT, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_input[] = {
+ SENSOR_ATTR_IN_INPUT(0),
+ SENSOR_ATTR_IN_INPUT(1),
+ SENSOR_ATTR_IN_INPUT(2),
+ SENSOR_ATTR_IN_INPUT(3),
+ SENSOR_ATTR_IN_INPUT(4),
+ SENSOR_ATTR_IN_INPUT(5),
+};
+
+#define SENSOR_ATTR_IN_MIN(ix) \
+ SENSOR_ATTR_2(in##ix##_min, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MIN, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_min[] = {
+ SENSOR_ATTR_IN_MIN(0),
+ SENSOR_ATTR_IN_MIN(1),
+ SENSOR_ATTR_IN_MIN(2),
+ SENSOR_ATTR_IN_MIN(3),
+ SENSOR_ATTR_IN_MIN(4),
+ SENSOR_ATTR_IN_MIN(5),
+};
+
+#define SENSOR_ATTR_IN_MAX(ix) \
+ SENSOR_ATTR_2(in##ix##_max, S_IRUGO | S_IWUSR, \
+ show_in, set_in, SHOW_SET_IN_MAX, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_max[] = {
+ SENSOR_ATTR_IN_MAX(0),
+ SENSOR_ATTR_IN_MAX(1),
+ SENSOR_ATTR_IN_MAX(2),
+ SENSOR_ATTR_IN_MAX(3),
+ SENSOR_ATTR_IN_MAX(4),
+ SENSOR_ATTR_IN_MAX(5),
+};
+
+#define SENSOR_ATTR_IN_ALARM(ix) \
+ SENSOR_ATTR_2(in##ix##_alarm, S_IRUGO, \
+ show_in, NULL, SHOW_IN_ALARM, ix)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_in_alarm[] = {
+ SENSOR_ATTR_IN_ALARM(0),
+ SENSOR_ATTR_IN_ALARM(1),
+ SENSOR_ATTR_IN_ALARM(2),
+ SENSOR_ATTR_IN_ALARM(3),
+ SENSOR_ATTR_IN_ALARM(4),
+ SENSOR_ATTR_IN_ALARM(5),
+};
+
+#define SENSOR_ATTR_TEMP_INPUT(ix) \
+ SENSOR_ATTR_2(temp##ix##_input, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_INPUT, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_input[] = {
+ SENSOR_ATTR_TEMP_INPUT(1),
+ SENSOR_ATTR_TEMP_INPUT(2),
+ SENSOR_ATTR_TEMP_INPUT(3),
+ SENSOR_ATTR_TEMP_INPUT(4),
+ SENSOR_ATTR_TEMP_INPUT(5),
+ SENSOR_ATTR_TEMP_INPUT(6),
+ SENSOR_ATTR_TEMP_INPUT(7),
+};
+
+#define SENSOR_ATTR_TEMP_MAX(ix) \
+ SENSOR_ATTR_2(temp##ix##_max, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_max[] = {
+ SENSOR_ATTR_TEMP_MAX(1),
+ SENSOR_ATTR_TEMP_MAX(2),
+ SENSOR_ATTR_TEMP_MAX(3),
+ SENSOR_ATTR_TEMP_MAX(4),
+ SENSOR_ATTR_TEMP_MAX(5),
+ SENSOR_ATTR_TEMP_MAX(6),
+ SENSOR_ATTR_TEMP_MAX(7),
+};
+
+#define SENSOR_ATTR_TEMP_MAX_HYST(ix) \
+ SENSOR_ATTR_2(temp##ix##_max_hyst, S_IRUGO | S_IWUSR, \
+ show_temp, set_temp, SHOW_SET_TEMP_MAX_HYST, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_max_hyst[] = {
+ SENSOR_ATTR_TEMP_MAX_HYST(1),
+ SENSOR_ATTR_TEMP_MAX_HYST(2),
+ SENSOR_ATTR_TEMP_MAX_HYST(3),
+ SENSOR_ATTR_TEMP_MAX_HYST(4),
+ SENSOR_ATTR_TEMP_MAX_HYST(5),
+ SENSOR_ATTR_TEMP_MAX_HYST(6),
+ SENSOR_ATTR_TEMP_MAX_HYST(7),
+};
+
+#define SENSOR_ATTR_TEMP_ALARM(ix) \
+ SENSOR_ATTR_2(temp##ix##_alarm, S_IRUGO, \
+ show_temp, NULL, SHOW_TEMP_ALARM, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_temp_alarm[] = {
+ SENSOR_ATTR_TEMP_ALARM(1),
+ SENSOR_ATTR_TEMP_ALARM(2),
+ SENSOR_ATTR_TEMP_ALARM(3),
+ SENSOR_ATTR_TEMP_ALARM(4),
+ SENSOR_ATTR_TEMP_ALARM(5),
+ SENSOR_ATTR_TEMP_ALARM(6),
+ SENSOR_ATTR_TEMP_ALARM(7),
+};
+
+#define SENSOR_ATTR_FAN(ix) \
+ SENSOR_ATTR_2(fan##ix##_input, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_INPUT, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_min, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_MIN, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_div, S_IRUGO | S_IWUSR, \
+ show_fan, set_fan, SHOW_SET_FAN_DIV, ix-1), \
+ SENSOR_ATTR_2(fan##ix##_alarm, S_IRUGO, \
+ show_fan, NULL, SHOW_FAN_ALARM, ix-1)
+
+#define SENSOR_ATTR_PWM(ix) \
+ SENSOR_ATTR_2(pwm##ix, S_IRUGO, \
+ show_pwm, NULL, SHOW_PWM, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_enable, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_ENABLE, ix-1), \
+ SENSOR_ATTR_2(pwm##ix##_auto_channels_temp, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_AUTO_CHANNELS_TEMP, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO | S_IWUSR, \
+ show_pwm, set_pwm, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_FREQ_RO(ix) \
+ SENSOR_ATTR_2(pwm##ix##_freq, S_IRUGO, \
+ show_pwm, NULL, SHOW_SET_PWM_FREQ, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_temp, set_pwm_auto_point_temp, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_temp, S_IRUGO, \
+ show_pwm_auto_point_temp, NULL, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO | S_IWUSR, \
+ show_pwm_auto_point_pwm, set_pwm_auto_point_pwm, \
+ ap-1, ix-1)
+
+#define SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(ix, ap) \
+ SENSOR_ATTR_2(pwm##ix##_auto_point##ap##_pwm, S_IRUGO, \
+ show_pwm_auto_point_pwm, NULL, \
+ ap-1, ix-1)
+
+static struct sensor_device_attribute_2 vt1211_sysfs_fan_pwm[] = {
+ SENSOR_ATTR_FAN(1),
+ SENSOR_ATTR_FAN(2),
+ SENSOR_ATTR_PWM(1),
+ SENSOR_ATTR_PWM(2),
+ SENSOR_ATTR_PWM_FREQ(1),
+ SENSOR_ATTR_PWM_FREQ_RO(2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_TEMP_RO(2, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(1, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(1, 4),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 1),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 2),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM(2, 3),
+ SENSOR_ATTR_PWM_AUTO_POINT_PWM_RO(2, 4),
+};
+
+static struct device_attribute vt1211_sysfs_misc[] = {
+ __ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm, set_vrm),
+ __ATTR(cpu0_vid, S_IRUGO, show_vid, NULL),
+ __ATTR(name, S_IRUGO, show_name, NULL),
+ __ATTR(alarms, S_IRUGO, show_alarms, NULL),
+};
+
+/* ---------------------------------------------------------------------
+ * Device registration and initialization
+ * --------------------------------------------------------------------- */
+
+static void __devinit vt1211_init_device(struct vt1211_data *data)
+{
+ /* set VRM */
+ data->vrm = vid_which_vrm();
+
+ /* Read (and initialize) UCH config */
+ data->uch_config = vt1211_read8(data, VT1211_REG_UCH_CONFIG);
+ if (uch_config > -1) {
+ data->uch_config = (data->uch_config & 0x83) |
+ (uch_config << 2);
+ vt1211_write8(data, VT1211_REG_UCH_CONFIG, data->uch_config);
+ }
+
+ /* Initialize the interrupt mode (if request at module load time).
+ * The VT1211 implements 3 different modes for clearing interrupts:
+ * 0: Clear INT when status register is read. Regenerate INT as long
+ * as temp stays above hysteresis limit.
+ * 1: Clear INT when status register is read. DON'T regenerate INT
+ * until temp falls below hysteresis limit and exceeds hot limit
+ * again.
+ * 2: Clear INT when temp falls below max limit.
+ *
+ * The driver only allows to force mode 0 since that's the only one
+ * that makes sense for 'sensors' */
+ if (int_mode == 0) {
+ vt1211_write8(data, VT1211_REG_TEMP1_CONFIG, 0);
+ vt1211_write8(data, VT1211_REG_TEMP2_CONFIG, 0);
+ }
+
+ /* Fill in some hard wired values into our data struct */
+ data->pwm_auto_pwm[0][3] = 255;
+ data->pwm_auto_pwm[1][3] = 255;
+}
+
+static void vt1211_remove_sysfs(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_in_input[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_min[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_max[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_in_alarm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_input[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_max[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_max_hyst[i].dev_attr);
+ device_remove_file(dev,
+ &vt1211_sysfs_temp_alarm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ device_remove_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
+ device_remove_file(dev, &vt1211_sysfs_misc[i]);
+ }
+}
+
+static int __devinit vt1211_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct vt1211_data *data;
+ struct resource *res;
+ int i, err;
+
+ if (!(data = kzalloc(sizeof(struct vt1211_data), GFP_KERNEL))) {
+ err = -ENOMEM;
+ dev_err(dev, "Out of memory\n");
+ goto EXIT;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ data->addr = res->start;
+ data->name = DRVNAME;
+ mutex_init(&data->update_lock);
+
+ platform_set_drvdata(pdev, data);
+
+ /* Initialize the VT1211 chip */
+ vt1211_init_device(data);
+
+ /* Create sysfs interface files */
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_in_input); i++) {
+ if (ISVOLT(i, data->uch_config)) {
+ if ((err = device_create_file(dev,
+ &vt1211_sysfs_in_input[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_min[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_max[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_in_alarm[i].dev_attr))) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_temp_input); i++) {
+ if (ISTEMP(i, data->uch_config)) {
+ if ((err = device_create_file(dev,
+ &vt1211_sysfs_temp_input[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_max[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_max_hyst[i].dev_attr)) ||
+ (err = device_create_file(dev,
+ &vt1211_sysfs_temp_alarm[i].dev_attr))) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_fan_pwm); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_fan_pwm[i].dev_attr);
+ if (err) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+ for (i = 0; i < ARRAY_SIZE(vt1211_sysfs_misc); i++) {
+ err = device_create_file(dev,
+ &vt1211_sysfs_misc[i]);
+ if (err) {
+ goto EXIT_DEV_REMOVE;
+ }
+ }
+
+ /* Register device */
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ dev_err(dev, "Class registration failed (%d)\n", err);
+ goto EXIT_DEV_REMOVE_SILENT;
+ }
+
+ return 0;
+
+EXIT_DEV_REMOVE:
+ dev_err(dev, "Sysfs interface creation failed (%d)\n", err);
+EXIT_DEV_REMOVE_SILENT:
+ vt1211_remove_sysfs(pdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+EXIT:
+ return err;
+}
+
+static int __devexit vt1211_remove(struct platform_device *pdev)
+{
+ struct vt1211_data *data = platform_get_drvdata(pdev);
+
+ hwmon_device_unregister(data->class_dev);
+ vt1211_remove_sysfs(pdev);
+ platform_set_drvdata(pdev, NULL);
+ kfree(data);
+
+ return 0;
+}
+
+static struct platform_driver vt1211_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = DRVNAME,
+ },
+ .probe = vt1211_probe,
+ .remove = __devexit_p(vt1211_remove),
+};
+
+static int __init vt1211_device_add(unsigned short address)
+{
+ struct resource res = {
+ .start = address,
+ .end = address + 0x7f,
+ .flags = IORESOURCE_IO,
+ };
+ int err;
+
+ pdev = platform_device_alloc(DRVNAME, address);
+ if (!pdev) {
+ err = -ENOMEM;
+ printk(KERN_ERR DRVNAME ": Device allocation failed (%d)\n",
+ err);
+ goto EXIT;
+ }
+
+ res.name = pdev->name;
+ err = platform_device_add_resources(pdev, &res, 1);
+ if (err) {
+ printk(KERN_ERR DRVNAME ": Device resource addition failed "
+ "(%d)\n", err);
+ goto EXIT_DEV_PUT;
+ }
+
+ err = platform_device_add(pdev);
+ if (err) {
+ printk(KERN_ERR DRVNAME ": Device addition failed (%d)\n",
+ err);
+ goto EXIT_DEV_PUT;
+ }
+
+ return 0;
+
+EXIT_DEV_PUT:
+ platform_device_put(pdev);
+EXIT:
+ return err;
+}
+
+static int __init vt1211_find(unsigned short *address)
+{
+ int err = -ENODEV;
+
+ superio_enter();
+
+ if (superio_inb(SIO_VT1211_DEVID) != SIO_VT1211_ID) {
+ goto EXIT;
+ }
+
+ superio_select(SIO_VT1211_LDN_HWMON);
+
+ if ((superio_inb(SIO_VT1211_ACTIVE) & 1) == 0) {
+ printk(KERN_WARNING DRVNAME ": HW monitor is disabled, "
+ "skipping\n");
+ goto EXIT;
+ }
+
+ *address = ((superio_inb(SIO_VT1211_BADDR) << 8) |
+ (superio_inb(SIO_VT1211_BADDR + 1))) & 0xff00;
+ if (*address == 0) {
+ printk(KERN_WARNING DRVNAME ": Base address is not set, "
+ "skipping\n");
+ goto EXIT;
+ }
+
+ err = 0;
+ printk(KERN_INFO DRVNAME ": Found VT1211 chip at 0x%04x, "
+ "revision %u\n", *address, superio_inb(SIO_VT1211_DEVREV));
+
+EXIT:
+ superio_exit();
+ return err;
+}
+
+static int __init vt1211_init(void)
+{
+ int err;
+ unsigned short address = 0;
+
+ err = vt1211_find(&address);
+ if (err) {
+ goto EXIT;
+ }
+
+ if ((uch_config < -1) || (uch_config > 31)) {
+ err = -EINVAL;
+ printk(KERN_WARNING DRVNAME ": Invalid UCH configuration %d. "
+ "Choose a value between 0 and 31.\n", uch_config);
+ goto EXIT;
+ }
+
+ if ((int_mode < -1) || (int_mode > 0)) {
+ err = -EINVAL;
+ printk(KERN_WARNING DRVNAME ": Invalid interrupt mode %d. "
+ "Only mode 0 is supported.\n", int_mode);
+ goto EXIT;
+ }
+
+ err = platform_driver_register(&vt1211_driver);
+ if (err) {
+ goto EXIT;
+ }
+
+ /* Sets global pdev as a side effect */
+ err = vt1211_device_add(address);
+ if (err) {
+ goto EXIT_DRV_UNREGISTER;
+ }
+
+ return 0;
+
+EXIT_DRV_UNREGISTER:
+ platform_driver_unregister(&vt1211_driver);
+EXIT:
+ return err;
+}
+
+static void __exit vt1211_exit(void)
+{
+ platform_device_unregister(pdev);
+ platform_driver_unregister(&vt1211_driver);
+}
+
+MODULE_AUTHOR("Juerg Haefliger <juergh@gmail.com>");
+MODULE_DESCRIPTION("VT1211 sensors");
+MODULE_LICENSE("GPL");
+
+module_init(vt1211_init);
+module_exit(vt1211_exit);
diff --git a/drivers/hwmon/vt8231.c b/drivers/hwmon/vt8231.c
index 686f3deb309..93f93d4fb8a 100644
--- a/drivers/hwmon/vt8231.c
+++ b/drivers/hwmon/vt8231.c
@@ -451,37 +451,6 @@ define_temperature_sysfs(4);
define_temperature_sysfs(5);
define_temperature_sysfs(6);
-#define CFG_INFO_TEMP(id) { &sensor_dev_attr_temp##id##_input.dev_attr, \
- &sensor_dev_attr_temp##id##_max_hyst.dev_attr, \
- &sensor_dev_attr_temp##id##_max.dev_attr }
-#define CFG_INFO_VOLT(id) { &sensor_dev_attr_in##id##_input.dev_attr, \
- &sensor_dev_attr_in##id##_min.dev_attr, \
- &sensor_dev_attr_in##id##_max.dev_attr }
-
-struct str_device_attr_table {
- struct device_attribute *input;
- struct device_attribute *min;
- struct device_attribute *max;
-};
-
-static struct str_device_attr_table cfg_info_temp[] = {
- { &dev_attr_temp1_input, &dev_attr_temp1_max_hyst, &dev_attr_temp1_max },
- CFG_INFO_TEMP(2),
- CFG_INFO_TEMP(3),
- CFG_INFO_TEMP(4),
- CFG_INFO_TEMP(5),
- CFG_INFO_TEMP(6)
-};
-
-static struct str_device_attr_table cfg_info_volt[] = {
- CFG_INFO_VOLT(0),
- CFG_INFO_VOLT(1),
- CFG_INFO_VOLT(2),
- CFG_INFO_VOLT(3),
- CFG_INFO_VOLT(4),
- { &dev_attr_in5_input, &dev_attr_in5_min, &dev_attr_in5_max }
-};
-
/* Fans */
static ssize_t show_fan(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -585,8 +554,110 @@ static ssize_t show_alarms(struct device *dev, struct device_attribute *attr,
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms, NULL);
+static struct attribute *vt8231_attributes_temps[6][4] = {
+ {
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_max.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp4_input.dev_attr.attr,
+ &sensor_dev_attr_temp4_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp4_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp5_input.dev_attr.attr,
+ &sensor_dev_attr_temp5_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp5_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_temp6_input.dev_attr.attr,
+ &sensor_dev_attr_temp6_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp6_max.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_temps[6] = {
+ { .attrs = vt8231_attributes_temps[0] },
+ { .attrs = vt8231_attributes_temps[1] },
+ { .attrs = vt8231_attributes_temps[2] },
+ { .attrs = vt8231_attributes_temps[3] },
+ { .attrs = vt8231_attributes_temps[4] },
+ { .attrs = vt8231_attributes_temps[5] },
+};
+
+static struct attribute *vt8231_attributes_volts[6][4] = {
+ {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ NULL
+ }, {
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group vt8231_group_volts[6] = {
+ { .attrs = vt8231_attributes_volts[0] },
+ { .attrs = vt8231_attributes_volts[1] },
+ { .attrs = vt8231_attributes_volts[2] },
+ { .attrs = vt8231_attributes_volts[3] },
+ { .attrs = vt8231_attributes_volts[4] },
+ { .attrs = vt8231_attributes_volts[5] },
+};
+
+static struct attribute *vt8231_attributes[] = {
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ NULL
+};
+
+static const struct attribute_group vt8231_group = {
+ .attrs = vt8231_attributes,
+};
+
static struct i2c_driver vt8231_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "vt8231",
},
.attach_adapter = vt8231_detect,
@@ -670,43 +741,43 @@ int vt8231_detect(struct i2c_adapter *adapter)
vt8231_init_client(client);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&client->dev.kobj, &vt8231_group)))
goto exit_detach;
- }
/* Must update device information to find out the config field */
data->uch_config = vt8231_read_value(client, VT8231_REG_UCH_CONFIG);
- for (i = 0; i < ARRAY_SIZE(cfg_info_temp); i++) {
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++) {
if (ISTEMP(i, data->uch_config)) {
- device_create_file(&client->dev,
- cfg_info_temp[i].input);
- device_create_file(&client->dev, cfg_info_temp[i].max);
- device_create_file(&client->dev, cfg_info_temp[i].min);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &vt8231_group_temps[i])))
+ goto exit_remove_files;
}
}
- for (i = 0; i < ARRAY_SIZE(cfg_info_volt); i++) {
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++) {
if (ISVOLT(i, data->uch_config)) {
- device_create_file(&client->dev,
- cfg_info_volt[i].input);
- device_create_file(&client->dev, cfg_info_volt[i].max);
- device_create_file(&client->dev, cfg_info_volt[i].min);
+ if ((err = sysfs_create_group(&client->dev.kobj,
+ &vt8231_group_volts[i])))
+ goto exit_remove_files;
}
}
- device_create_file(&client->dev, &sensor_dev_attr_fan1_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_input.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan1_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_min.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan1_div.dev_attr);
- device_create_file(&client->dev, &sensor_dev_attr_fan2_div.dev_attr);
-
- device_create_file(&client->dev, &dev_attr_alarms);
+ data->class_dev = hwmon_device_register(&client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
+ }
return 0;
+exit_remove_files:
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group);
exit_detach:
i2c_detach_client(client);
exit_free:
@@ -719,10 +790,18 @@ exit_release:
static int vt8231_detach_client(struct i2c_client *client)
{
struct vt8231_data *data = i2c_get_clientdata(client);
- int err;
+ int err, i;
hwmon_device_unregister(data->class_dev);
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_volts); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_volts[i]);
+
+ for (i = 0; i < ARRAY_SIZE(vt8231_group_temps); i++)
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group_temps[i]);
+
+ sysfs_remove_group(&client->dev.kobj, &vt8231_group);
+
if ((err = i2c_detach_client(client))) {
return err;
}
diff --git a/drivers/hwmon/w83627ehf.c b/drivers/hwmon/w83627ehf.c
index 40301bc6ce1..833faa275ff 100644
--- a/drivers/hwmon/w83627ehf.c
+++ b/drivers/hwmon/w83627ehf.c
@@ -2,6 +2,9 @@
w83627ehf - Driver for the hardware monitoring functionality of
the Winbond W83627EHF Super-I/O chip
Copyright (C) 2005 Jean Delvare <khali@linux-fr.org>
+ Copyright (C) 2006 Yuan Mu (Winbond),
+ Rudolf Marek <r.marek@sh.cvut.cz>
+ David Hubbard <david.c.hubbard@gmail.com>
Shamelessly ripped from the w83627hf driver
Copyright (C) 2003 Mark Studebaker
@@ -29,8 +32,8 @@
Supports the following chips:
- Chip #vin #fan #pwm #temp chip_id man_id
- w83627ehf 10 5 - 3 0x88 0x5ca3
+ Chip #vin #fan #pwm #temp chip_id man_id
+ w83627ehf 10 5 4 3 0x88,0xa1 0x5ca3
*/
#include <linux/module.h>
@@ -145,10 +148,44 @@ static const u16 W83627EHF_REG_TEMP_CONFIG[] = { 0x152, 0x252 };
#define W83627EHF_REG_ALARM2 0x45A
#define W83627EHF_REG_ALARM3 0x45B
+/* SmartFan registers */
+/* DC or PWM output fan configuration */
+static const u8 W83627EHF_REG_PWM_ENABLE[] = {
+ 0x04, /* SYS FAN0 output mode and PWM mode */
+ 0x04, /* CPU FAN0 output mode and PWM mode */
+ 0x12, /* AUX FAN mode */
+ 0x62, /* CPU fan1 mode */
+};
+
+static const u8 W83627EHF_PWM_MODE_SHIFT[] = { 0, 1, 0, 6 };
+static const u8 W83627EHF_PWM_ENABLE_SHIFT[] = { 2, 4, 1, 4 };
+
+/* FAN Duty Cycle, be used to control */
+static const u8 W83627EHF_REG_PWM[] = { 0x01, 0x03, 0x11, 0x61 };
+static const u8 W83627EHF_REG_TARGET[] = { 0x05, 0x06, 0x13, 0x63 };
+static const u8 W83627EHF_REG_TOLERANCE[] = { 0x07, 0x07, 0x14, 0x62 };
+
+
+/* Advanced Fan control, some values are common for all fans */
+static const u8 W83627EHF_REG_FAN_MIN_OUTPUT[] = { 0x08, 0x09, 0x15, 0x64 };
+static const u8 W83627EHF_REG_FAN_STOP_TIME[] = { 0x0C, 0x0D, 0x17, 0x66 };
+
/*
* Conversions
*/
+/* 1 is PWM mode, output in ms */
+static inline unsigned int step_time_from_reg(u8 reg, u8 mode)
+{
+ return mode ? 100 * reg : 400 * reg;
+}
+
+static inline u8 step_time_to_reg(unsigned int msec, u8 mode)
+{
+ return SENSORS_LIMIT((mode ? (msec + 50) / 100 :
+ (msec + 200) / 400), 1, 255);
+}
+
static inline unsigned int
fan_from_reg(u8 reg, unsigned int div)
{
@@ -170,12 +207,12 @@ temp1_from_reg(s8 reg)
}
static inline s8
-temp1_to_reg(int temp)
+temp1_to_reg(int temp, int min, int max)
{
- if (temp <= -128000)
- return -128;
- if (temp >= 127000)
- return 127;
+ if (temp <= min)
+ return min / 1000;
+ if (temp >= max)
+ return max / 1000;
if (temp < 0)
return (temp - 500) / 1000;
return (temp + 500) / 1000;
@@ -223,6 +260,16 @@ struct w83627ehf_data {
s16 temp_max[2];
s16 temp_max_hyst[2];
u32 alarms;
+
+ u8 pwm_mode[4]; /* 0->DC variable voltage, 1->PWM variable duty cycle */
+ u8 pwm_enable[4]; /* 1->manual
+ 2->thermal cruise (also called SmartFan I) */
+ u8 pwm[4];
+ u8 target_temp[4];
+ u8 tolerance[4];
+
+ u8 fan_min_output[4]; /* minimum fan speed */
+ u8 fan_stop_time[4];
};
static inline int is_word_sized(u16 reg)
@@ -349,6 +396,7 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
{
struct i2c_client *client = to_i2c_client(dev);
struct w83627ehf_data *data = i2c_get_clientdata(client);
+ int pwmcfg = 0, tolerance = 0; /* shut up the compiler */
int i;
mutex_lock(&data->update_lock);
@@ -416,6 +464,34 @@ static struct w83627ehf_data *w83627ehf_update_device(struct device *dev)
}
}
+ for (i = 0; i < 4; i++) {
+ /* pwmcfg, tolarance mapped for i=0, i=1 to same reg */
+ if (i != 1) {
+ pwmcfg = w83627ehf_read_value(client,
+ W83627EHF_REG_PWM_ENABLE[i]);
+ tolerance = w83627ehf_read_value(client,
+ W83627EHF_REG_TOLERANCE[i]);
+ }
+ data->pwm_mode[i] =
+ ((pwmcfg >> W83627EHF_PWM_MODE_SHIFT[i]) & 1)
+ ? 0 : 1;
+ data->pwm_enable[i] =
+ ((pwmcfg >> W83627EHF_PWM_ENABLE_SHIFT[i])
+ & 3) + 1;
+ data->pwm[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_PWM[i]);
+ data->fan_min_output[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_FAN_MIN_OUTPUT[i]);
+ data->fan_stop_time[i] = w83627ehf_read_value(client,
+ W83627EHF_REG_FAN_STOP_TIME[i]);
+ data->target_temp[i] =
+ w83627ehf_read_value(client,
+ W83627EHF_REG_TARGET[i]) &
+ (data->pwm_mode[i] == 1 ? 0x7f : 0xff);
+ data->tolerance[i] = (tolerance >> (i == 1 ? 4 : 0))
+ & 0x0f;
+ }
+
/* Measured temperatures and limits */
data->temp1 = w83627ehf_read_value(client,
W83627EHF_REG_TEMP1);
@@ -546,14 +622,6 @@ static struct sensor_device_attribute sda_in_max[] = {
SENSOR_ATTR(in9_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 9),
};
-static void device_create_file_in(struct device *dev, int i)
-{
- device_create_file(dev, &sda_in_input[i].dev_attr);
- device_create_file(dev, &sda_in_alarm[i].dev_attr);
- device_create_file(dev, &sda_in_min[i].dev_attr);
- device_create_file(dev, &sda_in_max[i].dev_attr);
-}
-
#define show_fan_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -681,14 +749,6 @@ static struct sensor_device_attribute sda_fan_div[] = {
SENSOR_ATTR(fan5_div, S_IRUGO, show_fan_div, NULL, 4),
};
-static void device_create_file_fan(struct device *dev, int i)
-{
- device_create_file(dev, &sda_fan_input[i].dev_attr);
- device_create_file(dev, &sda_fan_alarm[i].dev_attr);
- device_create_file(dev, &sda_fan_div[i].dev_attr);
- device_create_file(dev, &sda_fan_min[i].dev_attr);
-}
-
#define show_temp1_reg(reg) \
static ssize_t \
show_##reg(struct device *dev, struct device_attribute *attr, \
@@ -711,7 +771,7 @@ store_temp1_##reg(struct device *dev, struct device_attribute *attr, \
u32 val = simple_strtoul(buf, NULL, 10); \
\
mutex_lock(&data->update_lock); \
- data->temp1_##reg = temp1_to_reg(val); \
+ data->temp1_##reg = temp1_to_reg(val, -128000, 127000); \
w83627ehf_write_value(client, W83627EHF_REG_TEMP1_##REG, \
data->temp1_##reg); \
mutex_unlock(&data->update_lock); \
@@ -777,10 +837,309 @@ static struct sensor_device_attribute sda_temp[] = {
SENSOR_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, 13),
};
+#define show_pwm_reg(reg) \
+static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+}
+
+show_pwm_reg(pwm_mode)
+show_pwm_reg(pwm_enable)
+show_pwm_reg(pwm)
+
+static ssize_t
+store_pwm_mode(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = simple_strtoul(buf, NULL, 10);
+ u16 reg;
+
+ if (val > 1)
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+ data->pwm_mode[nr] = val;
+ reg &= ~(1 << W83627EHF_PWM_MODE_SHIFT[nr]);
+ if (!val)
+ reg |= 1 << W83627EHF_PWM_MODE_SHIFT[nr];
+ w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 255);
+
+ mutex_lock(&data->update_lock);
+ data->pwm[nr] = val;
+ w83627ehf_write_value(client, W83627EHF_REG_PWM[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_pwm_enable(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u32 val = simple_strtoul(buf, NULL, 10);
+ u16 reg;
+
+ if (!val || (val > 2)) /* only modes 1 and 2 are supported */
+ return -EINVAL;
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_PWM_ENABLE[nr]);
+ data->pwm_enable[nr] = val;
+ reg &= ~(0x03 << W83627EHF_PWM_ENABLE_SHIFT[nr]);
+ reg |= (val - 1) << W83627EHF_PWM_ENABLE_SHIFT[nr];
+ w83627ehf_write_value(client, W83627EHF_REG_PWM_ENABLE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+
+#define show_tol_temp(reg) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", temp1_from_reg(data->reg[nr])); \
+}
+
+show_tol_temp(tolerance)
+show_tol_temp(target_temp)
+
+static ssize_t
+store_target_temp(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 127000);
+
+ mutex_lock(&data->update_lock);
+ data->target_temp[nr] = val;
+ w83627ehf_write_value(client, W83627EHF_REG_TARGET[nr], val);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static ssize_t
+store_tolerance(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct w83627ehf_data *data = i2c_get_clientdata(client);
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
+ int nr = sensor_attr->index;
+ u16 reg;
+ /* Limit the temp to 0C - 15C */
+ u8 val = temp1_to_reg(simple_strtoul(buf, NULL, 10), 0, 15000);
+
+ mutex_lock(&data->update_lock);
+ reg = w83627ehf_read_value(client, W83627EHF_REG_TOLERANCE[nr]);
+ data->tolerance[nr] = val;
+ if (nr == 1)
+ reg = (reg & 0x0f) | (val << 4);
+ else
+ reg = (reg & 0xf0) | val;
+ w83627ehf_write_value(client, W83627EHF_REG_TOLERANCE[nr], reg);
+ mutex_unlock(&data->update_lock);
+ return count;
+}
+
+static struct sensor_device_attribute sda_pwm[] = {
+ SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
+ SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
+ SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
+ SENSOR_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_mode[] = {
+ SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 0),
+ SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 1),
+ SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 2),
+ SENSOR_ATTR(pwm4_mode, S_IWUSR | S_IRUGO, show_pwm_mode,
+ store_pwm_mode, 3),
+};
+
+static struct sensor_device_attribute sda_pwm_enable[] = {
+ SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 0),
+ SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 1),
+ SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 2),
+ SENSOR_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_enable,
+ store_pwm_enable, 3),
+};
+
+static struct sensor_device_attribute sda_target_temp[] = {
+ SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 0),
+ SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 1),
+ SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 2),
+ SENSOR_ATTR(pwm4_target, S_IWUSR | S_IRUGO, show_target_temp,
+ store_target_temp, 3),
+};
+
+static struct sensor_device_attribute sda_tolerance[] = {
+ SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 0),
+ SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 1),
+ SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 2),
+ SENSOR_ATTR(pwm4_tolerance, S_IWUSR | S_IRUGO, show_tolerance,
+ store_tolerance, 3),
+};
+
+/* Smart Fan registers */
+
+#define fan_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", data->reg[nr]); \
+}\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{\
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83627ehf_data *data = i2c_get_clientdata(client); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ u32 val = SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 1, 255); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+}
+
+fan_functions(fan_min_output, FAN_MIN_OUTPUT)
+
+#define fan_time_functions(reg, REG) \
+static ssize_t show_##reg(struct device *dev, struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct w83627ehf_data *data = w83627ehf_update_device(dev); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ return sprintf(buf, "%d\n", \
+ step_time_from_reg(data->reg[nr], data->pwm_mode[nr])); \
+} \
+\
+static ssize_t \
+store_##reg(struct device *dev, struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct i2c_client *client = to_i2c_client(dev); \
+ struct w83627ehf_data *data = i2c_get_clientdata(client); \
+ struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr); \
+ int nr = sensor_attr->index; \
+ u8 val = step_time_to_reg(simple_strtoul(buf, NULL, 10), \
+ data->pwm_mode[nr]); \
+ mutex_lock(&data->update_lock); \
+ data->reg[nr] = val; \
+ w83627ehf_write_value(client, W83627EHF_REG_##REG[nr], val); \
+ mutex_unlock(&data->update_lock); \
+ return count; \
+} \
+
+fan_time_functions(fan_stop_time, FAN_STOP_TIME)
+
+
+static struct sensor_device_attribute sda_sf3_arrays_fan4[] = {
+ SENSOR_ATTR(pwm4_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 3),
+ SENSOR_ATTR(pwm4_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 3),
+};
+
+static struct sensor_device_attribute sda_sf3_arrays[] = {
+ SENSOR_ATTR(pwm1_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 0),
+ SENSOR_ATTR(pwm2_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 1),
+ SENSOR_ATTR(pwm3_stop_time, S_IWUSR | S_IRUGO, show_fan_stop_time,
+ store_fan_stop_time, 2),
+ SENSOR_ATTR(pwm1_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 0),
+ SENSOR_ATTR(pwm2_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 1),
+ SENSOR_ATTR(pwm3_min_output, S_IWUSR | S_IRUGO, show_fan_min_output,
+ store_fan_min_output, 2),
+};
+
/*
* Driver and client management
*/
+static void w83627ehf_device_remove_files(struct device *dev)
+{
+ /* some entries in the following arrays may not have been used in
+ * device_create_file(), but device_remove_file() will ignore them */
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+ device_remove_file(dev, &sda_sf3_arrays[i].dev_attr);
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++)
+ device_remove_file(dev, &sda_sf3_arrays_fan4[i].dev_attr);
+ for (i = 0; i < 10; i++) {
+ device_remove_file(dev, &sda_in_input[i].dev_attr);
+ device_remove_file(dev, &sda_in_alarm[i].dev_attr);
+ device_remove_file(dev, &sda_in_min[i].dev_attr);
+ device_remove_file(dev, &sda_in_max[i].dev_attr);
+ }
+ for (i = 0; i < 5; i++) {
+ device_remove_file(dev, &sda_fan_input[i].dev_attr);
+ device_remove_file(dev, &sda_fan_alarm[i].dev_attr);
+ device_remove_file(dev, &sda_fan_div[i].dev_attr);
+ device_remove_file(dev, &sda_fan_min[i].dev_attr);
+ }
+ for (i = 0; i < 4; i++) {
+ device_remove_file(dev, &sda_pwm[i].dev_attr);
+ device_remove_file(dev, &sda_pwm_mode[i].dev_attr);
+ device_remove_file(dev, &sda_pwm_enable[i].dev_attr);
+ device_remove_file(dev, &sda_target_temp[i].dev_attr);
+ device_remove_file(dev, &sda_tolerance[i].dev_attr);
+ }
+ for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
+ device_remove_file(dev, &sda_temp[i].dev_attr);
+}
+
static struct i2c_driver w83627ehf_driver;
static void w83627ehf_init_client(struct i2c_client *client)
@@ -810,6 +1169,7 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
struct i2c_client *client;
struct w83627ehf_data *data;
struct device *dev;
+ u8 fan4pin, fan5pin;
int i, err = 0;
if (!request_region(address + REGION_OFFSET, REGION_LENGTH,
@@ -848,35 +1208,87 @@ static int w83627ehf_detect(struct i2c_adapter *adapter)
data->fan_min[i] = w83627ehf_read_value(client,
W83627EHF_REG_FAN_MIN[i]);
+ /* fan4 and fan5 share some pins with the GPIO and serial flash */
+
+ superio_enter();
+ fan5pin = superio_inb(0x24) & 0x2;
+ fan4pin = superio_inb(0x29) & 0x6;
+ superio_exit();
+
/* It looks like fan4 and fan5 pins can be alternatively used
as fan on/off switches */
+
data->has_fan = 0x07; /* fan1, fan2 and fan3 */
i = w83627ehf_read_value(client, W83627EHF_REG_FANDIV1);
- if (i & (1 << 2))
+ if ((i & (1 << 2)) && (!fan4pin))
data->has_fan |= (1 << 3);
- if (i & (1 << 0))
+ if ((i & (1 << 0)) && (!fan5pin))
data->has_fan |= (1 << 4);
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
- goto exit_detach;
- }
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays); i++)
+ if ((err = device_create_file(dev,
+ &sda_sf3_arrays[i].dev_attr)))
+ goto exit_remove;
+
+ /* if fan4 is enabled create the sf3 files for it */
+ if (data->has_fan & (1 << 3))
+ for (i = 0; i < ARRAY_SIZE(sda_sf3_arrays_fan4); i++) {
+ if ((err = device_create_file(dev,
+ &sda_sf3_arrays_fan4[i].dev_attr)))
+ goto exit_remove;
+ }
for (i = 0; i < 10; i++)
- device_create_file_in(dev, i);
+ if ((err = device_create_file(dev, &sda_in_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_alarm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_min[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_in_max[i].dev_attr)))
+ goto exit_remove;
for (i = 0; i < 5; i++) {
- if (data->has_fan & (1 << i))
- device_create_file_fan(dev, i);
+ if (data->has_fan & (1 << i)) {
+ if ((err = device_create_file(dev,
+ &sda_fan_input[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_alarm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_div[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_fan_min[i].dev_attr)))
+ goto exit_remove;
+ if (i < 4 && /* w83627ehf only has 4 pwm */
+ ((err = device_create_file(dev,
+ &sda_pwm[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_pwm_mode[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_pwm_enable[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_target_temp[i].dev_attr))
+ || (err = device_create_file(dev,
+ &sda_tolerance[i].dev_attr))))
+ goto exit_remove;
+ }
}
+
for (i = 0; i < ARRAY_SIZE(sda_temp); i++)
- device_create_file(dev, &sda_temp[i].dev_attr);
+ if ((err = device_create_file(dev, &sda_temp[i].dev_attr)))
+ goto exit_remove;
+
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove;
+ }
return 0;
-exit_detach:
+exit_remove:
+ w83627ehf_device_remove_files(dev);
i2c_detach_client(client);
exit_free:
kfree(data);
@@ -892,6 +1304,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
+ w83627ehf_device_remove_files(&client->dev);
if ((err = i2c_detach_client(client)))
return err;
@@ -903,6 +1316,7 @@ static int w83627ehf_detach_client(struct i2c_client *client)
static struct i2c_driver w83627ehf_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83627ehf",
},
.attach_adapter = w83627ehf_detect,
diff --git a/drivers/hwmon/w83627hf.c b/drivers/hwmon/w83627hf.c
index 79368d53c36..dfdc29c7712 100644
--- a/drivers/hwmon/w83627hf.c
+++ b/drivers/hwmon/w83627hf.c
@@ -339,6 +339,7 @@ static void w83627hf_init_client(struct i2c_client *client);
static struct i2c_driver w83627hf_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83627hf",
},
.attach_adapter = w83627hf_detect,
@@ -511,13 +512,6 @@ static DEVICE_ATTR(in0_min, S_IRUGO | S_IWUSR,
static DEVICE_ATTR(in0_max, S_IRUGO | S_IWUSR,
show_regs_in_max0, store_regs_in_max0);
-#define device_create_file_in(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-device_create_file(&client->dev, &dev_attr_in##offset##_min); \
-device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -575,12 +569,6 @@ sysfs_fan_min_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
-#define device_create_file_fan(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
-} while (0)
-
#define show_temp_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -655,13 +643,6 @@ sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
sysfs_temp_offsets(3);
-#define device_create_file_temp(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \
-} while (0)
-
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -669,8 +650,6 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid)
static ssize_t
show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -691,8 +670,6 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm)
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -701,8 +678,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) data->alarms);
}
static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms)
#define show_beep_reg(REG, reg) \
static ssize_t show_beep_##reg (struct device *dev, struct device_attribute *attr, char *buf) \
@@ -765,12 +740,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, \
sysfs_beep(ENABLE, enable);
sysfs_beep(MASK, mask);
-#define device_create_file_beep(client) \
-do { \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask); \
-} while (0)
-
static ssize_t
show_fan_div_reg(struct device *dev, char *buf, int nr)
{
@@ -836,11 +805,6 @@ sysfs_fan_div(1);
sysfs_fan_div(2);
sysfs_fan_div(3);
-#define device_create_file_fan_div(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
static ssize_t
show_pwm_reg(struct device *dev, char *buf, int nr)
{
@@ -895,11 +859,6 @@ sysfs_pwm(1);
sysfs_pwm(2);
sysfs_pwm(3);
-#define device_create_file_pwm(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset); \
-} while (0)
-
static ssize_t
show_sensor_reg(struct device *dev, char *buf, int nr)
{
@@ -971,12 +930,6 @@ sysfs_sensor(1);
sysfs_sensor(2);
sysfs_sensor(3);
-#define device_create_file_sensor(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_type); \
-} while (0)
-
-
static int __init w83627hf_find(int sioaddr, unsigned short *addr)
{
u16 val;
@@ -1008,6 +961,85 @@ static int __init w83627hf_find(int sioaddr, unsigned short *addr)
return 0;
}
+static struct attribute *w83627hf_attributes[] = {
+ &dev_attr_in0_input.attr,
+ &dev_attr_in0_min.attr,
+ &dev_attr_in0_max.attr,
+ &dev_attr_in2_input.attr,
+ &dev_attr_in2_min.attr,
+ &dev_attr_in2_max.attr,
+ &dev_attr_in3_input.attr,
+ &dev_attr_in3_min.attr,
+ &dev_attr_in3_max.attr,
+ &dev_attr_in4_input.attr,
+ &dev_attr_in4_min.attr,
+ &dev_attr_in4_max.attr,
+ &dev_attr_in7_input.attr,
+ &dev_attr_in7_min.attr,
+ &dev_attr_in7_max.attr,
+ &dev_attr_in8_input.attr,
+ &dev_attr_in8_min.attr,
+ &dev_attr_in8_max.attr,
+
+ &dev_attr_fan1_input.attr,
+ &dev_attr_fan1_min.attr,
+ &dev_attr_fan1_div.attr,
+ &dev_attr_fan2_input.attr,
+ &dev_attr_fan2_min.attr,
+ &dev_attr_fan2_div.attr,
+
+ &dev_attr_temp1_input.attr,
+ &dev_attr_temp1_max.attr,
+ &dev_attr_temp1_max_hyst.attr,
+ &dev_attr_temp1_type.attr,
+ &dev_attr_temp2_input.attr,
+ &dev_attr_temp2_max.attr,
+ &dev_attr_temp2_max_hyst.attr,
+ &dev_attr_temp2_type.attr,
+
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_enable.attr,
+ &dev_attr_beep_mask.attr,
+
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+
+ NULL
+};
+
+static const struct attribute_group w83627hf_group = {
+ .attrs = w83627hf_attributes,
+};
+
+static struct attribute *w83627hf_attributes_opt[] = {
+ &dev_attr_in1_input.attr,
+ &dev_attr_in1_min.attr,
+ &dev_attr_in1_max.attr,
+ &dev_attr_in5_input.attr,
+ &dev_attr_in5_min.attr,
+ &dev_attr_in5_max.attr,
+ &dev_attr_in6_input.attr,
+ &dev_attr_in6_min.attr,
+ &dev_attr_in6_max.attr,
+
+ &dev_attr_fan3_input.attr,
+ &dev_attr_fan3_min.attr,
+ &dev_attr_fan3_div.attr,
+
+ &dev_attr_temp3_input.attr,
+ &dev_attr_temp3_max.attr,
+ &dev_attr_temp3_max_hyst.attr,
+ &dev_attr_temp3_type.attr,
+
+ &dev_attr_pwm3.attr,
+
+ NULL
+};
+
+static const struct attribute_group w83627hf_group_opt = {
+ .attrs = w83627hf_attributes_opt,
+};
+
static int w83627hf_detect(struct i2c_adapter *adapter)
{
int val, kind;
@@ -1107,62 +1139,72 @@ static int w83627hf_detect(struct i2c_adapter *adapter)
data->fan_min[1] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(2));
data->fan_min[2] = w83627hf_read_value(new_client, W83781D_REG_FAN_MIN(3));
- /* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ /* Register common device attributes */
+ if ((err = sysfs_create_group(&new_client->dev.kobj, &w83627hf_group)))
goto ERROR3;
- }
-
- device_create_file_in(new_client, 0);
- if (kind != w83697hf)
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- if (kind == w83627hf || kind == w83697hf) {
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
- }
- device_create_file_in(new_client, 7);
- device_create_file_in(new_client, 8);
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- if (kind != w83697hf)
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- if (kind != w83697hf)
- device_create_file_temp(new_client, 3);
- if (kind != w83697hf && data->vid != 0xff) {
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
- }
+ /* Register chip-specific device attributes */
+ if (kind == w83627hf || kind == w83697hf)
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in5_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in5_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in6_max)))
+ goto ERROR4;
- device_create_file_fan_div(new_client, 1);
- device_create_file_fan_div(new_client, 2);
if (kind != w83697hf)
- device_create_file_fan_div(new_client, 3);
-
- device_create_file_alarms(new_client);
-
- device_create_file_beep(new_client);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in1_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in1_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_in1_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_min))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_fan3_div))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_input))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_max_hyst))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_temp3_type)))
+ goto ERROR4;
+
+ if (kind != w83697hf && data->vid != 0xff)
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_cpu0_vid))
+ || (err = device_create_file(&new_client->dev,
+ &dev_attr_vrm)))
+ goto ERROR4;
- device_create_file_pwm(new_client, 1);
- device_create_file_pwm(new_client, 2);
if (kind == w83627thf || kind == w83637hf || kind == w83687thf)
- device_create_file_pwm(new_client, 3);
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_pwm3)))
+ goto ERROR4;
- device_create_file_sensor(new_client, 1);
- device_create_file_sensor(new_client, 2);
- if (kind != w83697hf)
- device_create_file_sensor(new_client, 3);
+ data->class_dev = hwmon_device_register(&new_client->dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
+ }
return 0;
+ ERROR4:
+ sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group);
+ sysfs_remove_group(&new_client->dev.kobj, &w83627hf_group_opt);
ERROR3:
i2c_detach_client(new_client);
ERROR2:
@@ -1180,6 +1222,9 @@ static int w83627hf_detach_client(struct i2c_client *client)
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83627hf_group);
+ sysfs_remove_group(&client->dev.kobj, &w83627hf_group_opt);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/w83781d.c b/drivers/hwmon/w83781d.c
index 7be469ed0f8..a4584ec6984 100644
--- a/drivers/hwmon/w83781d.c
+++ b/drivers/hwmon/w83781d.c
@@ -41,6 +41,7 @@
#include <linux/i2c-isa.h>
#include <linux/hwmon.h>
#include <linux/hwmon-vid.h>
+#include <linux/sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
#include <asm/io.h>
@@ -288,6 +289,7 @@ static struct i2c_driver w83781d_driver = {
static struct i2c_driver w83781d_isa_driver = {
.driver = {
+ .owner = THIS_MODULE,
.name = "w83781d-isa",
},
.attach_adapter = w83781d_isa_attach_adapter,
@@ -359,13 +361,6 @@ sysfs_in_offsets(6);
sysfs_in_offsets(7);
sysfs_in_offsets(8);
-#define device_create_file_in(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_in##offset##_input); \
-device_create_file(&client->dev, &dev_attr_in##offset##_min); \
-device_create_file(&client->dev, &dev_attr_in##offset##_max); \
-} while (0)
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -420,12 +415,6 @@ sysfs_fan_min_offset(2);
sysfs_fan_offset(3);
sysfs_fan_min_offset(3);
-#define device_create_file_fan(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_input); \
-device_create_file(&client->dev, &dev_attr_fan##offset##_min); \
-} while (0)
-
#define show_temp_reg(reg) \
static ssize_t show_##reg (struct device *dev, char *buf, int nr) \
{ \
@@ -496,13 +485,6 @@ sysfs_temp_offsets(1);
sysfs_temp_offsets(2);
sysfs_temp_offsets(3);
-#define device_create_file_temp(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_input); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max); \
-device_create_file(&client->dev, &dev_attr_temp##offset##_max_hyst); \
-} while (0)
-
static ssize_t
show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -510,10 +492,8 @@ show_vid_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%ld\n", (long) vid_from_reg(data->vid, data->vrm));
}
-static
-DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
-#define device_create_file_vid(client) \
-device_create_file(&client->dev, &dev_attr_cpu0_vid);
+static DEVICE_ATTR(cpu0_vid, S_IRUGO, show_vid_reg, NULL);
+
static ssize_t
show_vrm_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -534,10 +514,8 @@ store_vrm_reg(struct device *dev, struct device_attribute *attr, const char *buf
return count;
}
-static
-DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
-#define device_create_file_vrm(client) \
-device_create_file(&client->dev, &dev_attr_vrm);
+static DEVICE_ATTR(vrm, S_IRUGO | S_IWUSR, show_vrm_reg, store_vrm_reg);
+
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -545,10 +523,8 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%u\n", data->alarms);
}
-static
-DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-#define device_create_file_alarms(client) \
-device_create_file(&client->dev, &dev_attr_alarms);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+
static ssize_t show_beep_mask (struct device *dev, struct device_attribute *attr, char *buf)
{
struct w83781d_data *data = w83781d_update_device(dev);
@@ -614,12 +590,6 @@ static DEVICE_ATTR(beep_##reg, S_IRUGO | S_IWUSR, show_regs_beep_##reg, store_re
sysfs_beep(ENABLE, enable);
sysfs_beep(MASK, mask);
-#define device_create_file_beep(client) \
-do { \
-device_create_file(&client->dev, &dev_attr_beep_enable); \
-device_create_file(&client->dev, &dev_attr_beep_mask); \
-} while (0)
-
static ssize_t
show_fan_div_reg(struct device *dev, char *buf, int nr)
{
@@ -685,11 +655,6 @@ sysfs_fan_div(1);
sysfs_fan_div(2);
sysfs_fan_div(3);
-#define device_create_file_fan_div(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_fan##offset##_div); \
-} while (0)
-
static ssize_t
show_pwm_reg(struct device *dev, char *buf, int nr)
{
@@ -786,16 +751,6 @@ sysfs_pwmenable(2); /* only PWM2 can be enabled/disabled */
sysfs_pwm(3);
sysfs_pwm(4);
-#define device_create_file_pwm(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset); \
-} while (0)
-
-#define device_create_file_pwmenable(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_pwm##offset##_enable); \
-} while (0)
-
static ssize_t
show_sensor_reg(struct device *dev, char *buf, int nr)
{
@@ -864,11 +819,6 @@ sysfs_sensor(1);
sysfs_sensor(2);
sysfs_sensor(3);
-#define device_create_file_sensor(client, offset) \
-do { \
-device_create_file(&client->dev, &dev_attr_temp##offset##_type); \
-} while (0)
-
/* This function is called when:
* w83781d_driver is inserted (when this module is loaded), for each
available adapter
@@ -993,11 +943,69 @@ ERROR_SC_0:
return err;
}
+#define IN_UNIT_ATTRS(X) \
+ &dev_attr_in##X##_input.attr, \
+ &dev_attr_in##X##_min.attr, \
+ &dev_attr_in##X##_max.attr
+
+#define FAN_UNIT_ATTRS(X) \
+ &dev_attr_fan##X##_input.attr, \
+ &dev_attr_fan##X##_min.attr, \
+ &dev_attr_fan##X##_div.attr
+
+#define TEMP_UNIT_ATTRS(X) \
+ &dev_attr_temp##X##_input.attr, \
+ &dev_attr_temp##X##_max.attr, \
+ &dev_attr_temp##X##_max_hyst.attr
+
+static struct attribute* w83781d_attributes[] = {
+ IN_UNIT_ATTRS(0),
+ IN_UNIT_ATTRS(2),
+ IN_UNIT_ATTRS(3),
+ IN_UNIT_ATTRS(4),
+ IN_UNIT_ATTRS(5),
+ IN_UNIT_ATTRS(6),
+ FAN_UNIT_ATTRS(1),
+ FAN_UNIT_ATTRS(2),
+ FAN_UNIT_ATTRS(3),
+ TEMP_UNIT_ATTRS(1),
+ TEMP_UNIT_ATTRS(2),
+ &dev_attr_cpu0_vid.attr,
+ &dev_attr_vrm.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_beep_mask.attr,
+ &dev_attr_beep_enable.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group = {
+ .attrs = w83781d_attributes,
+};
+
+static struct attribute *w83781d_attributes_opt[] = {
+ IN_UNIT_ATTRS(1),
+ IN_UNIT_ATTRS(7),
+ IN_UNIT_ATTRS(8),
+ TEMP_UNIT_ATTRS(3),
+ &dev_attr_pwm1.attr,
+ &dev_attr_pwm2.attr,
+ &dev_attr_pwm2_enable.attr,
+ &dev_attr_pwm3.attr,
+ &dev_attr_pwm4.attr,
+ &dev_attr_temp1_type.attr,
+ &dev_attr_temp2_type.attr,
+ &dev_attr_temp3_type.attr,
+ NULL
+};
+static const struct attribute_group w83781d_group_opt = {
+ .attrs = w83781d_attributes_opt,
+};
+
static int
w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
{
int i = 0, val1 = 0, val2;
- struct i2c_client *new_client;
+ struct i2c_client *client;
+ struct device *dev;
struct w83781d_data *data;
int err;
const char *client_name = "";
@@ -1074,13 +1082,14 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
goto ERROR1;
}
- new_client = &data->client;
- i2c_set_clientdata(new_client, data);
- new_client->addr = address;
+ client = &data->client;
+ i2c_set_clientdata(client, data);
+ client->addr = address;
mutex_init(&data->lock);
- new_client->adapter = adapter;
- new_client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver;
- new_client->flags = 0;
+ client->adapter = adapter;
+ client->driver = is_isa ? &w83781d_isa_driver : &w83781d_driver;
+ client->flags = 0;
+ dev = &client->dev;
/* Now, we do the remaining detection. */
@@ -1089,20 +1098,18 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
force_*=... parameter, and the Winbond will be reset to the right
bank. */
if (kind < 0) {
- if (w83781d_read_value(new_client, W83781D_REG_CONFIG) & 0x80) {
- dev_dbg(&new_client->dev, "Detection failed at step "
- "3\n");
+ if (w83781d_read_value(client, W83781D_REG_CONFIG) & 0x80) {
+ dev_dbg(dev, "Detection failed at step 3\n");
err = -ENODEV;
goto ERROR2;
}
- val1 = w83781d_read_value(new_client, W83781D_REG_BANK);
- val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ val1 = w83781d_read_value(client, W83781D_REG_BANK);
+ val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN);
/* Check for Winbond or Asus ID if in bank 0 */
if ((!(val1 & 0x07)) &&
(((!(val1 & 0x80)) && (val2 != 0xa3) && (val2 != 0xc3))
|| ((val1 & 0x80) && (val2 != 0x5c) && (val2 != 0x12)))) {
- dev_dbg(&new_client->dev, "Detection failed at step "
- "4\n");
+ dev_dbg(dev, "Detection failed at step 4\n");
err = -ENODEV;
goto ERROR2;
}
@@ -1111,9 +1118,8 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
if ((!is_isa) && (((!(val1 & 0x80)) && (val2 == 0xa3)) ||
((val1 & 0x80) && (val2 == 0x5c)))) {
if (w83781d_read_value
- (new_client, W83781D_REG_I2C_ADDR) != address) {
- dev_dbg(&new_client->dev, "Detection failed "
- "at step 5\n");
+ (client, W83781D_REG_I2C_ADDR) != address) {
+ dev_dbg(dev, "Detection failed at step 5\n");
err = -ENODEV;
goto ERROR2;
}
@@ -1122,27 +1128,26 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
/* We have either had a force parameter, or we have already detected the
Winbond. Put it now into bank 0 and Vendor ID High Byte */
- w83781d_write_value(new_client, W83781D_REG_BANK,
- (w83781d_read_value(new_client,
- W83781D_REG_BANK) & 0x78) |
- 0x80);
+ w83781d_write_value(client, W83781D_REG_BANK,
+ (w83781d_read_value(client, W83781D_REG_BANK)
+ & 0x78) | 0x80);
/* Determine the chip type. */
if (kind <= 0) {
/* get vendor ID */
- val2 = w83781d_read_value(new_client, W83781D_REG_CHIPMAN);
+ val2 = w83781d_read_value(client, W83781D_REG_CHIPMAN);
if (val2 == 0x5c)
vendid = winbond;
else if (val2 == 0x12)
vendid = asus;
else {
- dev_dbg(&new_client->dev, "Chip was made by neither "
+ dev_dbg(dev, "Chip was made by neither "
"Winbond nor Asus?\n");
err = -ENODEV;
goto ERROR2;
}
- val1 = w83781d_read_value(new_client, W83781D_REG_WCHIPID);
+ val1 = w83781d_read_value(client, W83781D_REG_WCHIPID);
if ((val1 == 0x10 || val1 == 0x11) && vendid == winbond)
kind = w83781d;
else if (val1 == 0x30 && vendid == winbond)
@@ -1156,7 +1161,7 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
kind = as99127f;
else {
if (kind == 0)
- dev_warn(&new_client->dev, "Ignoring 'force' "
+ dev_warn(dev, "Ignoring 'force' "
"parameter for unknown chip at "
"adapter %d, address 0x%02x\n",
i2c_adapter_id(adapter), address);
@@ -1178,20 +1183,20 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Fill in the remaining client fields and put into the global list */
- strlcpy(new_client->name, client_name, I2C_NAME_SIZE);
+ strlcpy(client->name, client_name, I2C_NAME_SIZE);
data->type = kind;
data->valid = 0;
mutex_init(&data->update_lock);
/* Tell the I2C layer a new client has arrived */
- if ((err = i2c_attach_client(new_client)))
+ if ((err = i2c_attach_client(client)))
goto ERROR2;
/* attach secondary i2c lm75-like clients */
if (!is_isa) {
if ((err = w83781d_detect_subclients(adapter, address,
- kind, new_client)))
+ kind, client)))
goto ERROR3;
} else {
data->lm75[0] = NULL;
@@ -1199,11 +1204,11 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Initialize the chip */
- w83781d_init_client(new_client);
+ w83781d_init_client(client);
/* A few vars need to be filled upon startup */
for (i = 1; i <= 3; i++) {
- data->fan_min[i - 1] = w83781d_read_value(new_client,
+ data->fan_min[i - 1] = w83781d_read_value(client,
W83781D_REG_FAN_MIN(i));
}
if (kind != w83781d && kind != as99127f)
@@ -1211,65 +1216,68 @@ w83781d_detect(struct i2c_adapter *adapter, int address, int kind)
data->pwmenable[i] = 1;
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(&new_client->dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&dev->kobj, &w83781d_group)))
goto ERROR4;
- }
- device_create_file_in(new_client, 0);
- if (kind != w83783s)
- device_create_file_in(new_client, 1);
- device_create_file_in(new_client, 2);
- device_create_file_in(new_client, 3);
- device_create_file_in(new_client, 4);
- device_create_file_in(new_client, 5);
- device_create_file_in(new_client, 6);
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev, &dev_attr_in1_input))
+ || (err = device_create_file(dev, &dev_attr_in1_min))
+ || (err = device_create_file(dev, &dev_attr_in1_max)))
+ goto ERROR4;
+ }
if (kind != as99127f && kind != w83781d && kind != w83783s) {
- device_create_file_in(new_client, 7);
- device_create_file_in(new_client, 8);
+ if ((err = device_create_file(dev, &dev_attr_in7_input))
+ || (err = device_create_file(dev, &dev_attr_in7_min))
+ || (err = device_create_file(dev, &dev_attr_in7_max))
+ || (err = device_create_file(dev, &dev_attr_in8_input))
+ || (err = device_create_file(dev, &dev_attr_in8_min))
+ || (err = device_create_file(dev, &dev_attr_in8_max)))
+ goto ERROR4;
+ }
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev, &dev_attr_temp3_input))
+ || (err = device_create_file(dev, &dev_attr_temp3_max))
+ || (err = device_create_file(dev,
+ &dev_attr_temp3_max_hyst)))
+ goto ERROR4;
}
-
- device_create_file_fan(new_client, 1);
- device_create_file_fan(new_client, 2);
- device_create_file_fan(new_client, 3);
-
- device_create_file_temp(new_client, 1);
- device_create_file_temp(new_client, 2);
- if (kind != w83783s)
- device_create_file_temp(new_client, 3);
-
- device_create_file_vid(new_client);
- device_create_file_vrm(new_client);
-
- device_create_file_fan_div(new_client, 1);
- device_create_file_fan_div(new_client, 2);
- device_create_file_fan_div(new_client, 3);
-
- device_create_file_alarms(new_client);
-
- device_create_file_beep(new_client);
if (kind != w83781d && kind != as99127f) {
- device_create_file_pwm(new_client, 1);
- device_create_file_pwm(new_client, 2);
- device_create_file_pwmenable(new_client, 2);
+ if ((err = device_create_file(dev, &dev_attr_pwm1))
+ || (err = device_create_file(dev, &dev_attr_pwm2))
+ || (err = device_create_file(dev, &dev_attr_pwm2_enable)))
+ goto ERROR4;
}
if (kind == w83782d && !is_isa) {
- device_create_file_pwm(new_client, 3);
- device_create_file_pwm(new_client, 4);
+ if ((err = device_create_file(dev, &dev_attr_pwm3))
+ || (err = device_create_file(dev, &dev_attr_pwm4)))
+ goto ERROR4;
}
if (kind != as99127f && kind != w83781d) {
- device_create_file_sensor(new_client, 1);
- device_create_file_sensor(new_client, 2);
- if (kind != w83783s)
- device_create_file_sensor(new_client, 3);
+ if ((err = device_create_file(dev, &dev_attr_temp1_type))
+ || (err = device_create_file(dev,
+ &dev_attr_temp2_type)))
+ goto ERROR4;
+ if (kind != w83783s) {
+ if ((err = device_create_file(dev,
+ &dev_attr_temp3_type)))
+ goto ERROR4;
+ }
+ }
+
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto ERROR4;
}
return 0;
ERROR4:
+ sysfs_remove_group(&dev->kobj, &w83781d_group);
+ sysfs_remove_group(&dev->kobj, &w83781d_group_opt);
+
if (data->lm75[1]) {
i2c_detach_client(data->lm75[1]);
kfree(data->lm75[1]);
@@ -1279,7 +1287,7 @@ ERROR4:
kfree(data->lm75[0]);
}
ERROR3:
- i2c_detach_client(new_client);
+ i2c_detach_client(client);
ERROR2:
kfree(data);
ERROR1:
@@ -1296,9 +1304,11 @@ w83781d_detach_client(struct i2c_client *client)
int err;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
-
+ sysfs_remove_group(&client->dev.kobj, &w83781d_group);
+ sysfs_remove_group(&client->dev.kobj, &w83781d_group_opt);
+ }
if (i2c_is_isa_client(client))
release_region(client->addr, W83781D_EXTENT);
diff --git a/drivers/hwmon/w83791d.c b/drivers/hwmon/w83791d.c
index eec43abd57f..d965d074cd6 100644
--- a/drivers/hwmon/w83791d.c
+++ b/drivers/hwmon/w83791d.c
@@ -27,9 +27,9 @@
The w83791d chip appears to be part way between the 83781d and the
83792d. Thus, this file is derived from both the w83792d.c and
- w83781d.c files, but its output is more along the lines of the
- 83781d (which means there are no changes to the user-mode sensors
- program which treats the 83791d as an 83781d).
+ w83781d.c files.
+
+ The w83791g chip is the same as the w83791d but lead-free.
*/
#include <linux/config.h>
@@ -1172,6 +1172,7 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
(w83791d_read(client, W83791D_REG_BEEP_CTRL[1]) << 8) +
(w83791d_read(client, W83791D_REG_BEEP_CTRL[2]) << 16);
+ /* Extract global beep enable flag */
data->beep_enable =
(data->beep_mask >> GLOBAL_BEEP_ENABLE_SHIFT) & 0x01;
diff --git a/drivers/hwmon/w83792d.c b/drivers/hwmon/w83792d.c
index 7576ec9426a..4e108262576 100644
--- a/drivers/hwmon/w83792d.c
+++ b/drivers/hwmon/w83792d.c
@@ -43,6 +43,7 @@
#include <linux/hwmon-sysfs.h>
#include <linux/err.h>
#include <linux/mutex.h>
+#include <linux/sysfs.h>
/* Addresses to scan */
static unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END };
@@ -381,41 +382,6 @@ static ssize_t store_in_##reg (struct device *dev, \
store_in_reg(MIN, min);
store_in_reg(MAX, max);
-static struct sensor_device_attribute sda_in_input[] = {
- SENSOR_ATTR(in0_input, S_IRUGO, show_in, NULL, 0),
- SENSOR_ATTR(in1_input, S_IRUGO, show_in, NULL, 1),
- SENSOR_ATTR(in2_input, S_IRUGO, show_in, NULL, 2),
- SENSOR_ATTR(in3_input, S_IRUGO, show_in, NULL, 3),
- SENSOR_ATTR(in4_input, S_IRUGO, show_in, NULL, 4),
- SENSOR_ATTR(in5_input, S_IRUGO, show_in, NULL, 5),
- SENSOR_ATTR(in6_input, S_IRUGO, show_in, NULL, 6),
- SENSOR_ATTR(in7_input, S_IRUGO, show_in, NULL, 7),
- SENSOR_ATTR(in8_input, S_IRUGO, show_in, NULL, 8),
-};
-static struct sensor_device_attribute sda_in_min[] = {
- SENSOR_ATTR(in0_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 0),
- SENSOR_ATTR(in1_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 1),
- SENSOR_ATTR(in2_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 2),
- SENSOR_ATTR(in3_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 3),
- SENSOR_ATTR(in4_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 4),
- SENSOR_ATTR(in5_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 5),
- SENSOR_ATTR(in6_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 6),
- SENSOR_ATTR(in7_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 7),
- SENSOR_ATTR(in8_min, S_IWUSR | S_IRUGO, show_in_min, store_in_min, 8),
-};
-static struct sensor_device_attribute sda_in_max[] = {
- SENSOR_ATTR(in0_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 0),
- SENSOR_ATTR(in1_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 1),
- SENSOR_ATTR(in2_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 2),
- SENSOR_ATTR(in3_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 3),
- SENSOR_ATTR(in4_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 4),
- SENSOR_ATTR(in5_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 5),
- SENSOR_ATTR(in6_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 6),
- SENSOR_ATTR(in7_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 7),
- SENSOR_ATTR(in8_max, S_IWUSR | S_IRUGO, show_in_max, store_in_max, 8),
-};
-
-
#define show_fan_reg(reg) \
static ssize_t show_##reg (struct device *dev, struct device_attribute *attr, \
char *buf) \
@@ -499,35 +465,6 @@ store_fan_div(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_fan_input[] = {
- SENSOR_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1),
- SENSOR_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2),
- SENSOR_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3),
- SENSOR_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4),
- SENSOR_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5),
- SENSOR_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6),
- SENSOR_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7),
-};
-static struct sensor_device_attribute sda_fan_min[] = {
- SENSOR_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 1),
- SENSOR_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 2),
- SENSOR_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 3),
- SENSOR_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 4),
- SENSOR_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 5),
- SENSOR_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 6),
- SENSOR_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, store_fan_min, 7),
-};
-static struct sensor_device_attribute sda_fan_div[] = {
- SENSOR_ATTR(fan1_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 1),
- SENSOR_ATTR(fan2_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 2),
- SENSOR_ATTR(fan3_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 3),
- SENSOR_ATTR(fan4_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 4),
- SENSOR_ATTR(fan5_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 5),
- SENSOR_ATTR(fan6_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 6),
- SENSOR_ATTR(fan7_div, S_IWUSR | S_IRUGO, show_fan_div, store_fan_div, 7),
-};
-
-
/* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *attr,
@@ -595,24 +532,6 @@ static ssize_t store_temp23(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_temp_input[] = {
- SENSOR_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0),
- SENSOR_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0),
- SENSOR_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0),
-};
-
-static struct sensor_device_attribute_2 sda_temp_max[] = {
- SENSOR_ATTR_2(temp1_max, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 1),
- SENSOR_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 2),
- SENSOR_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 2),
-};
-
-static struct sensor_device_attribute_2 sda_temp_max_hyst[] = {
- SENSOR_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR, show_temp1, store_temp1, 0, 2),
- SENSOR_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 0, 4),
- SENSOR_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR, show_temp23, store_temp23, 1, 4),
-};
-
/* get reatime status of all sensors items: voltage, temp, fan */
static ssize_t
show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
@@ -621,9 +540,6 @@ show_alarms_reg(struct device *dev, struct device_attribute *attr, char *buf)
return sprintf(buf, "%d\n", data->alarms);
}
-static
-DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
-
static ssize_t
show_pwm(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -715,21 +631,6 @@ store_pwmenable(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_pwm[] = {
- SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
- SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
- SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
-};
-static struct sensor_device_attribute sda_pwm_enable[] = {
- SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 1),
- SENSOR_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 2),
- SENSOR_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
- show_pwmenable, store_pwmenable, 3),
-};
-
-
static ssize_t
show_pwm_mode(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -767,16 +668,6 @@ store_pwm_mode(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_pwm_mode[] = {
- SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 0),
- SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 1),
- SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
- show_pwm_mode, store_pwm_mode, 2),
-};
-
-
static ssize_t
show_regs_chassis(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -785,8 +676,6 @@ show_regs_chassis(struct device *dev, struct device_attribute *attr,
return sprintf(buf, "%d\n", data->chassis);
}
-static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
-
static ssize_t
show_chassis_clear(struct device *dev, struct device_attribute *attr, char *buf)
{
@@ -815,9 +704,6 @@ store_chassis_clear(struct device *dev, struct device_attribute *attr,
return count;
}
-static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
- show_chassis_clear, store_chassis_clear);
-
/* For Smart Fan I / Thermal Cruise */
static ssize_t
show_thermal_cruise(struct device *dev, struct device_attribute *attr,
@@ -853,15 +739,6 @@ store_thermal_cruise(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_thermal_cruise[] = {
- SENSOR_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 1),
- SENSOR_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 2),
- SENSOR_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO,
- show_thermal_cruise, store_thermal_cruise, 3),
-};
-
/* For Smart Fan I/Thermal Cruise and Smart Fan II */
static ssize_t
show_tolerance(struct device *dev, struct device_attribute *attr,
@@ -901,15 +778,6 @@ store_tolerance(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute sda_tolerance[] = {
- SENSOR_ATTR(tolerance1, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 1),
- SENSOR_ATTR(tolerance2, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 2),
- SENSOR_ATTR(tolerance3, S_IWUSR | S_IRUGO,
- show_tolerance, store_tolerance, 3),
-};
-
/* For Smart Fan II */
static ssize_t
show_sf2_point(struct device *dev, struct device_attribute *attr,
@@ -946,36 +814,6 @@ store_sf2_point(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_sf2_point[] = {
- SENSOR_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 1),
- SENSOR_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 1),
- SENSOR_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 1),
- SENSOR_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 1),
-
- SENSOR_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 2),
- SENSOR_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 2),
- SENSOR_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 2),
- SENSOR_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 2),
-
- SENSOR_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 1, 3),
- SENSOR_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 2, 3),
- SENSOR_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 3, 3),
- SENSOR_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR,
- show_sf2_point, store_sf2_point, 4, 3),
-};
-
-
static ssize_t
show_sf2_level(struct device *dev, struct device_attribute *attr,
char *buf)
@@ -1016,29 +854,6 @@ store_sf2_level(struct device *dev, struct device_attribute *attr,
return count;
}
-static struct sensor_device_attribute_2 sda_sf2_level[] = {
- SENSOR_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 1),
- SENSOR_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 1),
- SENSOR_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 1),
-
- SENSOR_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 2),
- SENSOR_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 2),
- SENSOR_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 2),
-
- SENSOR_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 1, 3),
- SENSOR_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 2, 3),
- SENSOR_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR,
- show_sf2_level, store_sf2_level, 3, 3),
-};
-
/* This function is called when:
* w83792d_driver is inserted (when this module is loaded), for each
available adapter
@@ -1139,12 +954,297 @@ ERROR_SC_0:
return err;
}
-static void device_create_file_fan(struct device *dev, int i)
-{
- device_create_file(dev, &sda_fan_input[i].dev_attr);
- device_create_file(dev, &sda_fan_div[i].dev_attr);
- device_create_file(dev, &sda_fan_min[i].dev_attr);
-}
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_in, NULL, 4);
+static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_in, NULL, 5);
+static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_in, NULL, 6);
+static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_in, NULL, 7);
+static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_in, NULL, 8);
+static SENSOR_DEVICE_ATTR(in0_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 0);
+static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 1);
+static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 2);
+static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 3);
+static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 4);
+static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 5);
+static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 6);
+static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 7);
+static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO,
+ show_in_min, store_in_min, 8);
+static SENSOR_DEVICE_ATTR(in0_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 0);
+static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 1);
+static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 2);
+static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 3);
+static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 4);
+static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 5);
+static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 6);
+static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 7);
+static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO,
+ show_in_max, store_in_max, 8);
+static SENSOR_DEVICE_ATTR_2(temp1_input, S_IRUGO, show_temp1, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp2_input, S_IRUGO, show_temp23, NULL, 0, 0);
+static SENSOR_DEVICE_ATTR_2(temp3_input, S_IRUGO, show_temp23, NULL, 1, 0);
+static SENSOR_DEVICE_ATTR_2(temp1_max, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 1);
+static SENSOR_DEVICE_ATTR_2(temp2_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp3_max, S_IRUGO | S_IWUSR, show_temp23,
+ store_temp23, 1, 2);
+static SENSOR_DEVICE_ATTR_2(temp1_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp1, store_temp1, 0, 2);
+static SENSOR_DEVICE_ATTR_2(temp2_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 0, 4);
+static SENSOR_DEVICE_ATTR_2(temp3_max_hyst, S_IRUGO | S_IWUSR,
+ show_temp23, store_temp23, 1, 4);
+static DEVICE_ATTR(alarms, S_IRUGO, show_alarms_reg, NULL);
+static DEVICE_ATTR(chassis, S_IRUGO, show_regs_chassis, NULL);
+static DEVICE_ATTR(chassis_clear, S_IRUGO | S_IWUSR,
+ show_chassis_clear, store_chassis_clear);
+static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0);
+static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1);
+static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2);
+static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 1);
+static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 2);
+static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO,
+ show_pwmenable, store_pwmenable, 3);
+static SENSOR_DEVICE_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 0);
+static SENSOR_DEVICE_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 1);
+static SENSOR_DEVICE_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
+ show_pwm_mode, store_pwm_mode, 2);
+static SENSOR_DEVICE_ATTR(tolerance1, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 1);
+static SENSOR_DEVICE_ATTR(tolerance2, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 2);
+static SENSOR_DEVICE_ATTR(tolerance3, S_IWUSR | S_IRUGO,
+ show_tolerance, store_tolerance, 3);
+static SENSOR_DEVICE_ATTR(thermal_cruise1, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 1);
+static SENSOR_DEVICE_ATTR(thermal_cruise2, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 2);
+static SENSOR_DEVICE_ATTR(thermal_cruise3, S_IWUSR | S_IRUGO,
+ show_thermal_cruise, store_thermal_cruise, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_point1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 3, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_point4_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_point, store_sf2_point, 4, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan1, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 1);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan2, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 2);
+static SENSOR_DEVICE_ATTR_2(sf2_level1_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 1, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level2_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 2, 3);
+static SENSOR_DEVICE_ATTR_2(sf2_level3_fan3, S_IRUGO | S_IWUSR,
+ show_sf2_level, store_sf2_level, 3, 3);
+static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 1);
+static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 2);
+static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 3);
+static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 4);
+static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 5);
+static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 6);
+static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 7);
+static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 1);
+static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 2);
+static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 3);
+static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 4);
+static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 5);
+static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 6);
+static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO,
+ show_fan_min, store_fan_min, 7);
+static SENSOR_DEVICE_ATTR(fan1_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 1);
+static SENSOR_DEVICE_ATTR(fan2_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 2);
+static SENSOR_DEVICE_ATTR(fan3_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 3);
+static SENSOR_DEVICE_ATTR(fan4_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 4);
+static SENSOR_DEVICE_ATTR(fan5_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 5);
+static SENSOR_DEVICE_ATTR(fan6_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 6);
+static SENSOR_DEVICE_ATTR(fan7_div, S_IWUSR | S_IRUGO,
+ show_fan_div, store_fan_div, 7);
+
+static struct attribute *w83792d_attributes_fan[4][4] = {
+ {
+ &sensor_dev_attr_fan4_input.dev_attr.attr,
+ &sensor_dev_attr_fan4_min.dev_attr.attr,
+ &sensor_dev_attr_fan4_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan5_input.dev_attr.attr,
+ &sensor_dev_attr_fan5_min.dev_attr.attr,
+ &sensor_dev_attr_fan5_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan6_input.dev_attr.attr,
+ &sensor_dev_attr_fan6_min.dev_attr.attr,
+ &sensor_dev_attr_fan6_div.dev_attr.attr,
+ NULL
+ }, {
+ &sensor_dev_attr_fan7_input.dev_attr.attr,
+ &sensor_dev_attr_fan7_min.dev_attr.attr,
+ &sensor_dev_attr_fan7_div.dev_attr.attr,
+ NULL
+ }
+};
+
+static const struct attribute_group w83792d_group_fan[4] = {
+ { .attrs = w83792d_attributes_fan[0] },
+ { .attrs = w83792d_attributes_fan[1] },
+ { .attrs = w83792d_attributes_fan[2] },
+ { .attrs = w83792d_attributes_fan[3] },
+};
+
+static struct attribute *w83792d_attributes[] = {
+ &sensor_dev_attr_in0_input.dev_attr.attr,
+ &sensor_dev_attr_in0_max.dev_attr.attr,
+ &sensor_dev_attr_in0_min.dev_attr.attr,
+ &sensor_dev_attr_in1_input.dev_attr.attr,
+ &sensor_dev_attr_in1_max.dev_attr.attr,
+ &sensor_dev_attr_in1_min.dev_attr.attr,
+ &sensor_dev_attr_in2_input.dev_attr.attr,
+ &sensor_dev_attr_in2_max.dev_attr.attr,
+ &sensor_dev_attr_in2_min.dev_attr.attr,
+ &sensor_dev_attr_in3_input.dev_attr.attr,
+ &sensor_dev_attr_in3_max.dev_attr.attr,
+ &sensor_dev_attr_in3_min.dev_attr.attr,
+ &sensor_dev_attr_in4_input.dev_attr.attr,
+ &sensor_dev_attr_in4_max.dev_attr.attr,
+ &sensor_dev_attr_in4_min.dev_attr.attr,
+ &sensor_dev_attr_in5_input.dev_attr.attr,
+ &sensor_dev_attr_in5_max.dev_attr.attr,
+ &sensor_dev_attr_in5_min.dev_attr.attr,
+ &sensor_dev_attr_in6_input.dev_attr.attr,
+ &sensor_dev_attr_in6_max.dev_attr.attr,
+ &sensor_dev_attr_in6_min.dev_attr.attr,
+ &sensor_dev_attr_in7_input.dev_attr.attr,
+ &sensor_dev_attr_in7_max.dev_attr.attr,
+ &sensor_dev_attr_in7_min.dev_attr.attr,
+ &sensor_dev_attr_in8_input.dev_attr.attr,
+ &sensor_dev_attr_in8_max.dev_attr.attr,
+ &sensor_dev_attr_in8_min.dev_attr.attr,
+ &sensor_dev_attr_temp1_input.dev_attr.attr,
+ &sensor_dev_attr_temp1_max.dev_attr.attr,
+ &sensor_dev_attr_temp1_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp2_input.dev_attr.attr,
+ &sensor_dev_attr_temp2_max.dev_attr.attr,
+ &sensor_dev_attr_temp2_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_temp3_input.dev_attr.attr,
+ &sensor_dev_attr_temp3_max.dev_attr.attr,
+ &sensor_dev_attr_temp3_max_hyst.dev_attr.attr,
+ &sensor_dev_attr_pwm1.dev_attr.attr,
+ &sensor_dev_attr_pwm1_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm1_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm2.dev_attr.attr,
+ &sensor_dev_attr_pwm2_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm2_enable.dev_attr.attr,
+ &sensor_dev_attr_pwm3.dev_attr.attr,
+ &sensor_dev_attr_pwm3_mode.dev_attr.attr,
+ &sensor_dev_attr_pwm3_enable.dev_attr.attr,
+ &dev_attr_alarms.attr,
+ &dev_attr_chassis.attr,
+ &dev_attr_chassis_clear.attr,
+ &sensor_dev_attr_tolerance1.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise1.dev_attr.attr,
+ &sensor_dev_attr_tolerance2.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise2.dev_attr.attr,
+ &sensor_dev_attr_tolerance3.dev_attr.attr,
+ &sensor_dev_attr_thermal_cruise3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_point1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point3_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_point4_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan1.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan2.dev_attr.attr,
+ &sensor_dev_attr_sf2_level1_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level2_fan3.dev_attr.attr,
+ &sensor_dev_attr_sf2_level3_fan3.dev_attr.attr,
+ &sensor_dev_attr_fan1_input.dev_attr.attr,
+ &sensor_dev_attr_fan1_min.dev_attr.attr,
+ &sensor_dev_attr_fan1_div.dev_attr.attr,
+ &sensor_dev_attr_fan2_input.dev_attr.attr,
+ &sensor_dev_attr_fan2_min.dev_attr.attr,
+ &sensor_dev_attr_fan2_div.dev_attr.attr,
+ &sensor_dev_attr_fan3_input.dev_attr.attr,
+ &sensor_dev_attr_fan3_min.dev_attr.attr,
+ &sensor_dev_attr_fan3_div.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group w83792d_group = {
+ .attrs = w83792d_attributes,
+};
static int
w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
@@ -1268,59 +1368,46 @@ w83792d_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* Register sysfs hooks */
- data->class_dev = hwmon_device_register(dev);
- if (IS_ERR(data->class_dev)) {
- err = PTR_ERR(data->class_dev);
+ if ((err = sysfs_create_group(&dev->kobj, &w83792d_group)))
goto ERROR3;
- }
- for (i = 0; i < 9; i++) {
- device_create_file(dev, &sda_in_input[i].dev_attr);
- device_create_file(dev, &sda_in_max[i].dev_attr);
- device_create_file(dev, &sda_in_min[i].dev_attr);
- }
- for (i = 0; i < 3; i++)
- device_create_file_fan(dev, i);
/* Read GPIO enable register to check if pins for fan 4,5 are used as
GPIO */
val1 = w83792d_read_value(client, W83792D_REG_GPIO_EN);
+
if (!(val1 & 0x40))
- device_create_file_fan(dev, 3);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[0])))
+ goto exit_remove_files;
+
if (!(val1 & 0x20))
- device_create_file_fan(dev, 4);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[1])))
+ goto exit_remove_files;
val1 = w83792d_read_value(client, W83792D_REG_PIN);
if (val1 & 0x40)
- device_create_file_fan(dev, 5);
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[2])))
+ goto exit_remove_files;
+
if (val1 & 0x04)
- device_create_file_fan(dev, 6);
-
- for (i = 0; i < 3; i++) {
- device_create_file(dev, &sda_temp_input[i].dev_attr);
- device_create_file(dev, &sda_temp_max[i].dev_attr);
- device_create_file(dev, &sda_temp_max_hyst[i].dev_attr);
- device_create_file(dev, &sda_thermal_cruise[i].dev_attr);
- device_create_file(dev, &sda_tolerance[i].dev_attr);
- }
+ if ((err = sysfs_create_group(&dev->kobj,
+ &w83792d_group_fan[3])))
+ goto exit_remove_files;
- for (i = 0; i < ARRAY_SIZE(sda_pwm); i++) {
- device_create_file(dev, &sda_pwm[i].dev_attr);
- device_create_file(dev, &sda_pwm_enable[i].dev_attr);
- device_create_file(dev, &sda_pwm_mode[i].dev_attr);
+ data->class_dev = hwmon_device_register(dev);
+ if (IS_ERR(data->class_dev)) {
+ err = PTR_ERR(data->class_dev);
+ goto exit_remove_files;
}
- device_create_file(dev, &dev_attr_alarms);
- device_create_file(dev, &dev_attr_chassis);
- device_create_file(dev, &dev_attr_chassis_clear);
-
- for (i = 0; i < ARRAY_SIZE(sda_sf2_point); i++)
- device_create_file(dev, &sda_sf2_point[i].dev_attr);
-
- for (i = 0; i < ARRAY_SIZE(sda_sf2_level); i++)
- device_create_file(dev, &sda_sf2_level[i].dev_attr);
-
return 0;
+exit_remove_files:
+ sysfs_remove_group(&dev->kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&dev->kobj, &w83792d_group_fan[i]);
ERROR3:
if (data->lm75[0] != NULL) {
i2c_detach_client(data->lm75[0]);
@@ -1342,11 +1429,16 @@ static int
w83792d_detach_client(struct i2c_client *client)
{
struct w83792d_data *data = i2c_get_clientdata(client);
- int err;
+ int err, i;
/* main client */
- if (data)
+ if (data) {
hwmon_device_unregister(data->class_dev);
+ sysfs_remove_group(&client->dev.kobj, &w83792d_group);
+ for (i = 0; i < ARRAY_SIZE(w83792d_group_fan); i++)
+ sysfs_remove_group(&client->dev.kobj,
+ &w83792d_group_fan[i]);
+ }
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/hwmon/w83l785ts.c b/drivers/hwmon/w83l785ts.c
index 3f2bac125fb..a3fcace412f 100644
--- a/drivers/hwmon/w83l785ts.c
+++ b/drivers/hwmon/w83l785ts.c
@@ -236,21 +236,30 @@ static int w83l785ts_detect(struct i2c_adapter *adapter, int address, int kind)
* Nothing yet, assume it is already started.
*/
+ err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ if (err)
+ goto exit_remove;
+
+ err = device_create_file(&new_client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
+ if (err)
+ goto exit_remove;
+
/* Register sysfs hooks */
data->class_dev = hwmon_device_register(&new_client->dev);
if (IS_ERR(data->class_dev)) {
err = PTR_ERR(data->class_dev);
- goto exit_detach;
+ goto exit_remove;
}
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_input.dev_attr);
- device_create_file(&new_client->dev,
- &sensor_dev_attr_temp1_max.dev_attr);
-
return 0;
-exit_detach:
+exit_remove:
+ device_remove_file(&new_client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&new_client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
i2c_detach_client(new_client);
exit_free:
kfree(data);
@@ -264,7 +273,10 @@ static int w83l785ts_detach_client(struct i2c_client *client)
int err;
hwmon_device_unregister(data->class_dev);
-
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_input.dev_attr);
+ device_remove_file(&client->dev,
+ &sensor_dev_attr_temp1_max.dev_attr);
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/Kconfig b/drivers/i2c/Kconfig
index 24383afdda7..11935f66fcd 100644
--- a/drivers/i2c/Kconfig
+++ b/drivers/i2c/Kconfig
@@ -1,5 +1,5 @@
#
-# Character device configuration
+# I2C subsystem configuration
#
menu "I2C support"
diff --git a/drivers/i2c/algos/Kconfig b/drivers/i2c/algos/Kconfig
index 30408015d23..c034820615b 100644
--- a/drivers/i2c/algos/Kconfig
+++ b/drivers/i2c/algos/Kconfig
@@ -53,12 +53,6 @@ config I2C_ALGO8XX
tristate "MPC8xx CPM I2C interface"
depends on 8xx && I2C
-config I2C_ALGO_SIBYTE
- tristate "SiByte SMBus interface"
- depends on SIBYTE_SB1xxx_SOC && I2C
- help
- Supports the SiByte SOC on-chip I2C interfaces (2 channels).
-
config I2C_ALGO_SGI
tristate "I2C SGI interfaces"
depends on I2C && (SGI_IP22 || SGI_IP32 || X86_VISWS)
diff --git a/drivers/i2c/algos/Makefile b/drivers/i2c/algos/Makefile
index 867fe1f6740..208be04a3db 100644
--- a/drivers/i2c/algos/Makefile
+++ b/drivers/i2c/algos/Makefile
@@ -6,7 +6,6 @@ obj-$(CONFIG_I2C_ALGOBIT) += i2c-algo-bit.o
obj-$(CONFIG_I2C_ALGOPCF) += i2c-algo-pcf.o
obj-$(CONFIG_I2C_ALGOPCA) += i2c-algo-pca.o
obj-$(CONFIG_I2C_ALGOITE) += i2c-algo-ite.o
-obj-$(CONFIG_I2C_ALGO_SIBYTE) += i2c-algo-sibyte.o
obj-$(CONFIG_I2C_ALGO_SGI) += i2c-algo-sgi.o
ifeq ($(CONFIG_I2C_DEBUG_ALGO),y)
diff --git a/drivers/i2c/algos/i2c-algo-bit.c b/drivers/i2c/algos/i2c-algo-bit.c
index ab230c033f9..21c36bfb5e6 100644
--- a/drivers/i2c/algos/i2c-algo-bit.c
+++ b/drivers/i2c/algos/i2c-algo-bit.c
@@ -76,17 +76,15 @@ static inline void scllo(struct i2c_algo_bit_data *adap)
* Raise scl line, and do checking for delays. This is necessary for slower
* devices.
*/
-static inline int sclhi(struct i2c_algo_bit_data *adap)
+static int sclhi(struct i2c_algo_bit_data *adap)
{
unsigned long start;
setscl(adap,1);
/* Not all adapters have scl sense line... */
- if (adap->getscl == NULL ) {
- udelay(adap->udelay);
- return 0;
- }
+ if (!adap->getscl)
+ goto done;
start=jiffies;
while (! getscl(adap) ) {
@@ -101,6 +99,8 @@ static inline int sclhi(struct i2c_algo_bit_data *adap)
cond_resched();
}
DEBSTAT(printk(KERN_DEBUG "needed %ld jiffies\n", jiffies-start));
+
+done:
udelay(adap->udelay);
return 0;
}
@@ -121,7 +121,6 @@ static void i2c_repstart(struct i2c_algo_bit_data *adap)
DEBPROTO(printk(" Sr "));
setsda(adap,1);
sclhi(adap);
- udelay(adap->udelay);
sdalo(adap);
scllo(adap);
@@ -306,7 +305,7 @@ bailout:
* 0 chip did not answer
* -x transmission error
*/
-static inline int try_address(struct i2c_adapter *i2c_adap,
+static int try_address(struct i2c_adapter *i2c_adap,
unsigned char addr, int retries)
{
struct i2c_algo_bit_data *adap = i2c_adap->algo_data;
@@ -354,15 +353,11 @@ static int sendbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
return (retval<0)? retval : -EFAULT;
/* got a better one ?? */
}
-#if 0
- /* from asm/delay.h */
- __delay(adap->mdelay * (loops_per_sec / 1000) );
-#endif
}
return wrcount;
}
-static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
+static int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
int inval;
int rdcount=0; /* counts bytes read */
@@ -412,7 +407,7 @@ static inline int readbytes(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
* -x an error occurred (like: -EREMOTEIO if the device did not answer, or
* -ETIMEDOUT, for example if the lines are stuck...)
*/
-static inline int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
+static int bit_doAddress(struct i2c_adapter *i2c_adap, struct i2c_msg *msg)
{
unsigned short flags = msg->flags;
unsigned short nak_ok = msg->flags & I2C_M_IGNORE_NAK;
@@ -517,7 +512,7 @@ static u32 bit_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static struct i2c_algorithm i2c_bit_algo = {
+static const struct i2c_algorithm i2c_bit_algo = {
.master_xfer = bit_xfer,
.functionality = bit_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c
index b88a6fcf7bd..9081c9fbcd2 100644
--- a/drivers/i2c/algos/i2c-algo-pca.c
+++ b/drivers/i2c/algos/i2c-algo-pca.c
@@ -355,7 +355,7 @@ static int pca_init(struct i2c_algo_pca_data *adap)
return 0;
}
-static struct i2c_algorithm pca_algo = {
+static const struct i2c_algorithm pca_algo = {
.master_xfer = pca_xfer,
.functionality = pca_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-pcf.c b/drivers/i2c/algos/i2c-algo-pcf.c
index 5b24930adb5..3b200339896 100644
--- a/drivers/i2c/algos/i2c-algo-pcf.c
+++ b/drivers/i2c/algos/i2c-algo-pcf.c
@@ -458,7 +458,7 @@ static u32 pcf_func(struct i2c_adapter *adap)
/* -----exported algorithm data: ------------------------------------- */
-static struct i2c_algorithm pcf_algo = {
+static const struct i2c_algorithm pcf_algo = {
.master_xfer = pcf_xfer,
.functionality = pcf_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-sgi.c b/drivers/i2c/algos/i2c-algo-sgi.c
index 932c4fa86c7..490d99997fd 100644
--- a/drivers/i2c/algos/i2c-algo-sgi.c
+++ b/drivers/i2c/algos/i2c-algo-sgi.c
@@ -157,7 +157,7 @@ static u32 sgi_func(struct i2c_adapter *adap)
return I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm sgi_algo = {
+static const struct i2c_algorithm sgi_algo = {
.master_xfer = sgi_xfer,
.functionality = sgi_func,
};
diff --git a/drivers/i2c/algos/i2c-algo-sibyte.c b/drivers/i2c/algos/i2c-algo-sibyte.c
deleted file mode 100644
index 32d41c6fac0..00000000000
--- a/drivers/i2c/algos/i2c-algo-sibyte.c
+++ /dev/null
@@ -1,215 +0,0 @@
-/* ------------------------------------------------------------------------- */
-/* i2c-algo-sibyte.c i2c driver algorithms for bit-shift adapters */
-/* ------------------------------------------------------------------------- */
-/* Copyright (C) 2001,2002,2003 Broadcom Corporation
- Copyright (C) 1995-2000 Simon G. Vogl
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-/* ------------------------------------------------------------------------- */
-
-/* With some changes from Kyösti Mälkki <kmalkki@cc.hut.fi> and even
- Frodo Looijaard <frodol@dds.nl>. */
-
-/* Ported for SiByte SOCs by Broadcom Corporation. */
-
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-
-#include <asm/io.h>
-#include <asm/sibyte/sb1250_regs.h>
-#include <asm/sibyte/sb1250_smbus.h>
-
-#include <linux/i2c.h>
-#include <linux/i2c-algo-sibyte.h>
-
-/* ----- global defines ----------------------------------------------- */
-#define SMB_CSR(a,r) ((long)(a->reg_base + r))
-
-/* ----- global variables --------------------------------------------- */
-
-/* module parameters:
- */
-static int bit_scan; /* have a look at what's hanging 'round */
-
-
-static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
- unsigned short flags, char read_write,
- u8 command, int size, union i2c_smbus_data * data)
-{
- struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
- int data_bytes = 0;
- int error;
-
- while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
- ;
-
- switch (size) {
- case I2C_SMBUS_QUICK:
- csr_out32((V_SMB_ADDR(addr) | (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
- V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
- break;
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 1;
- } else {
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- case I2C_SMBUS_BYTE_DATA:
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 1;
- } else {
- csr_out32(V_SMB_LB(data->byte), SMB_CSR(adap, R_SMB_DATA));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- case I2C_SMBUS_WORD_DATA:
- csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
- if (read_write == I2C_SMBUS_READ) {
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
- SMB_CSR(adap, R_SMB_START));
- data_bytes = 2;
- } else {
- csr_out32(V_SMB_LB(data->word & 0xff), SMB_CSR(adap, R_SMB_DATA));
- csr_out32(V_SMB_MB(data->word >> 8), SMB_CSR(adap, R_SMB_DATA));
- csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
- SMB_CSR(adap, R_SMB_START));
- }
- break;
- default:
- return -1; /* XXXKW better error code? */
- }
-
- while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
- ;
-
- error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
- if (error & M_SMB_ERROR) {
- /* Clear error bit by writing a 1 */
- csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
- return -1; /* XXXKW better error code? */
- }
-
- if (data_bytes == 1)
- data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
- if (data_bytes == 2)
- data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
-
- return 0;
-}
-
-static int algo_control(struct i2c_adapter *adapter,
- unsigned int cmd, unsigned long arg)
-{
- return 0;
-}
-
-static u32 bit_func(struct i2c_adapter *adap)
-{
- return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
-}
-
-
-/* -----exported algorithm data: ------------------------------------- */
-
-static struct i2c_algorithm i2c_sibyte_algo = {
- .smbus_xfer = smbus_xfer,
- .algo_control = algo_control, /* ioctl */
- .functionality = bit_func,
-};
-
-/*
- * registering functions to load algorithms at runtime
- */
-int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
-{
- int i;
- struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
-
- /* register new adapter to i2c module... */
- i2c_adap->algo = &i2c_sibyte_algo;
-
- /* Set the frequency to 100 kHz */
- csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
- csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
-
- /* scan bus */
- if (bit_scan) {
- union i2c_smbus_data data;
- int rc;
- printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
- i2c_adap->name);
- for (i = 0x00; i < 0x7f; i++) {
- /* XXXKW is this a realistic probe? */
- rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
- I2C_SMBUS_BYTE_DATA, &data);
- if (!rc) {
- printk("(%02x)",i);
- } else
- printk(".");
- }
- printk("\n");
- }
-
- return i2c_add_adapter(i2c_adap);
-}
-
-
-int i2c_sibyte_del_bus(struct i2c_adapter *adap)
-{
- int res;
-
- if ((res = i2c_del_adapter(adap)) < 0)
- return res;
-
- return 0;
-}
-
-int __init i2c_algo_sibyte_init (void)
-{
- printk("i2c-algo-sibyte.o: i2c SiByte algorithm module\n");
- return 0;
-}
-
-
-EXPORT_SYMBOL(i2c_sibyte_add_bus);
-EXPORT_SYMBOL(i2c_sibyte_del_bus);
-
-#ifdef MODULE
-MODULE_AUTHOR("Kip Walker, Broadcom Corp.");
-MODULE_DESCRIPTION("SiByte I2C-Bus algorithm");
-module_param(bit_scan, int, 0);
-MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
-MODULE_LICENSE("GPL");
-
-int init_module(void)
-{
- return i2c_algo_sibyte_init();
-}
-
-void cleanup_module(void)
-{
-}
-#endif
diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig
index 884320e7040..0d9667921f6 100644
--- a/drivers/i2c/busses/Kconfig
+++ b/drivers/i2c/busses/Kconfig
@@ -75,11 +75,11 @@ config I2C_AMD8111
will be called i2c-amd8111.
config I2C_AU1550
- tristate "Au1550 SMBus interface"
- depends on I2C && SOC_AU1550
+ tristate "Au1550/Au1200 SMBus interface"
+ depends on I2C && (SOC_AU1550 || SOC_AU1200)
help
If you say yes to this option, support will be included for the
- Au1550 SMBus interface.
+ Au1550 and Au1200 SMBus interface.
This driver can also be built as a module. If so, the module
will be called i2c-au1550.
@@ -196,7 +196,7 @@ config I2C_IBM_IIC
config I2C_IOP3XX
tristate "Intel IOP3xx and IXP4xx on-chip I2C interface"
- depends on (ARCH_IOP3XX || ARCH_IXP4XX) && I2C
+ depends on (ARCH_IOP32X || ARCH_IOP33X || ARCH_IXP4XX) && I2C
help
Say Y here if you want to use the IIC bus controller on
the Intel IOP3xx I/O Processors or IXP4xx Network Processors.
@@ -287,6 +287,16 @@ config I2C_OCORES
This driver can also be built as a module. If so, the module
will be called i2c-ocores.
+config I2C_OMAP
+ tristate "OMAP I2C adapter"
+ depends on I2C && ARCH_OMAP
+ default y if MACH_OMAP_H3 || MACH_OMAP_OSK
+ help
+ If you say yes to this option, support will be included for the
+ I2C interface on the Texas Instruments OMAP1/2 family of processors.
+ Like OMAP1510/1610/1710/5912 and OMAP242x.
+ For details see http://www.ti.com/omap.
+
config I2C_PARPORT
tristate "Parallel port adapter"
depends on I2C && PARPORT
@@ -482,19 +492,19 @@ config I2C_VIA
will be called i2c-via.
config I2C_VIAPRO
- tristate "VIA 82C596/82C686/823x"
+ tristate "VIA 82C596/82C686/82xx"
depends on I2C && PCI
help
If you say yes to this option, support will be included for the VIA
- 82C596/82C686/823x I2C interfaces. Specifically, the following
+ 82C596/82C686/82xx I2C interfaces. Specifically, the following
chipsets are supported:
- 82C596A/B
- 82C686A/B
- 8231
- 8233
- 8233A
- 8235
- 8237
+ VT82C596A/B
+ VT82C686A/B
+ VT8231
+ VT8233/A
+ VT8235
+ VT8237R/A
+ VT8251
This driver can also be built as a module. If so, the module
will be called i2c-viapro.
diff --git a/drivers/i2c/busses/Makefile b/drivers/i2c/busses/Makefile
index ac56df53155..493c87289b6 100644
--- a/drivers/i2c/busses/Makefile
+++ b/drivers/i2c/busses/Makefile
@@ -24,6 +24,7 @@ obj-$(CONFIG_I2C_MPC) += i2c-mpc.o
obj-$(CONFIG_I2C_MV64XXX) += i2c-mv64xxx.o
obj-$(CONFIG_I2C_NFORCE2) += i2c-nforce2.o
obj-$(CONFIG_I2C_OCORES) += i2c-ocores.o
+obj-$(CONFIG_I2C_OMAP) += i2c-omap.o
obj-$(CONFIG_I2C_PARPORT) += i2c-parport.o
obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o
obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o
diff --git a/drivers/i2c/busses/i2c-ali1535.c b/drivers/i2c/busses/i2c-ali1535.c
index d3ef46aeeb3..e75d339a348 100644
--- a/drivers/i2c/busses/i2c-ali1535.c
+++ b/drivers/i2c/busses/i2c-ali1535.c
@@ -468,7 +468,7 @@ static u32 ali1535_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ali1535_access,
.functionality = ali1535_func,
};
diff --git a/drivers/i2c/busses/i2c-ali1563.c b/drivers/i2c/busses/i2c-ali1563.c
index e6f63208fc4..33fbb47100a 100644
--- a/drivers/i2c/busses/i2c-ali1563.c
+++ b/drivers/i2c/busses/i2c-ali1563.c
@@ -367,7 +367,7 @@ static void ali1563_shutdown(struct pci_dev *dev)
release_region(ali1563_smba,ALI1563_SMB_IOSIZE);
}
-static struct i2c_algorithm ali1563_algorithm = {
+static const struct i2c_algorithm ali1563_algorithm = {
.smbus_xfer = ali1563_access,
.functionality = ali1563_func,
};
diff --git a/drivers/i2c/busses/i2c-ali15x3.c b/drivers/i2c/busses/i2c-ali15x3.c
index 7a5c0941dbc..3f11b6e1a34 100644
--- a/drivers/i2c/busses/i2c-ali15x3.c
+++ b/drivers/i2c/busses/i2c-ali15x3.c
@@ -463,7 +463,7 @@ static u32 ali15x3_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = ali15x3_access,
.functionality = ali15x3_func,
};
diff --git a/drivers/i2c/busses/i2c-amd756.c b/drivers/i2c/busses/i2c-amd756.c
index 1750dedaf4b..2d21afdc5b1 100644
--- a/drivers/i2c/busses/i2c-amd756.c
+++ b/drivers/i2c/busses/i2c-amd756.c
@@ -294,7 +294,7 @@ static u32 amd756_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = amd756_access,
.functionality = amd756_func,
};
diff --git a/drivers/i2c/busses/i2c-amd8111.c b/drivers/i2c/busses/i2c-amd8111.c
index e5ef560e686..0fbc7186c91 100644
--- a/drivers/i2c/busses/i2c-amd8111.c
+++ b/drivers/i2c/busses/i2c-amd8111.c
@@ -316,7 +316,7 @@ static u32 amd8111_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_I2C_BLOCK | I2C_FUNC_SMBUS_HWPEC_CALC;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = amd8111_access,
.functionality = amd8111_func,
};
diff --git a/drivers/i2c/busses/i2c-au1550.c b/drivers/i2c/busses/i2c-au1550.c
index d06edce03bf..d7e7c359fc3 100644
--- a/drivers/i2c/busses/i2c-au1550.c
+++ b/drivers/i2c/busses/i2c-au1550.c
@@ -34,8 +34,7 @@
#include <linux/errno.h>
#include <linux/i2c.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-pb1x00/pb1550.h>
+#include <asm/mach-au1x00/au1xxx.h>
#include <asm/mach-au1x00/au1xxx_psc.h>
#include "i2c-au1550.h"
@@ -118,13 +117,19 @@ do_address(struct i2c_au1550_data *adap, unsigned int addr, int rd)
/* Reset the FIFOs, clear events.
*/
- sp->psc_smbpcr = PSC_SMBPCR_DC;
+ stat = sp->psc_smbstat;
sp->psc_smbevnt = PSC_SMBEVNT_ALLCLR;
au_sync();
- do {
- stat = sp->psc_smbpcr;
+
+ if (!(stat & PSC_SMBSTAT_TE) || !(stat & PSC_SMBSTAT_RE)) {
+ sp->psc_smbpcr = PSC_SMBPCR_DC;
au_sync();
- } while ((stat & PSC_SMBPCR_DC) != 0);
+ do {
+ stat = sp->psc_smbpcr;
+ au_sync();
+ } while ((stat & PSC_SMBPCR_DC) != 0);
+ udelay(50);
+ }
/* Write out the i2c chip address and specify operation
*/
@@ -279,10 +284,10 @@ au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg *msgs, int num)
static u32
au1550_func(struct i2c_adapter *adap)
{
- return I2C_FUNC_I2C;
+ return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm au1550_algo = {
+static const struct i2c_algorithm au1550_algo = {
.master_xfer = au1550_xfer,
.functionality = au1550_func,
};
diff --git a/drivers/i2c/busses/i2c-elektor.c b/drivers/i2c/busses/i2c-elektor.c
index 59f8308c235..caa8e5c8bfb 100644
--- a/drivers/i2c/busses/i2c-elektor.c
+++ b/drivers/i2c/busses/i2c-elektor.c
@@ -196,7 +196,6 @@ static struct i2c_algo_pcf_data pcf_isa_data = {
.getclock = pcf_isa_getclock,
.waitforpin = pcf_isa_waitforpin,
.udelay = 10,
- .mdelay = 10,
.timeout = 100,
};
diff --git a/drivers/i2c/busses/i2c-hydra.c b/drivers/i2c/busses/i2c-hydra.c
index e0cb3b0f92f..457d48a0ab9 100644
--- a/drivers/i2c/busses/i2c-hydra.c
+++ b/drivers/i2c/busses/i2c-hydra.c
@@ -99,7 +99,6 @@ static struct i2c_algo_bit_data hydra_bit_data = {
.getsda = hydra_bit_getsda,
.getscl = hydra_bit_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = HZ
};
diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c
index 7be1d0a3e8f..bbb2fbee836 100644
--- a/drivers/i2c/busses/i2c-i801.c
+++ b/drivers/i2c/busses/i2c-i801.c
@@ -434,7 +434,7 @@ static u32 i801_func(struct i2c_adapter *adapter)
| (isich4 ? I2C_FUNC_SMBUS_HWPEC_CALC : 0);
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = i801_access,
.functionality = i801_func,
};
diff --git a/drivers/i2c/busses/i2c-i810.c b/drivers/i2c/busses/i2c-i810.c
index 748be30f2ba..b66fb6bb187 100644
--- a/drivers/i2c/busses/i2c-i810.c
+++ b/drivers/i2c/busses/i2c-i810.c
@@ -166,7 +166,6 @@ static struct i2c_algo_bit_data i810_i2c_bit_data = {
.getsda = bit_i810i2c_getsda,
.getscl = bit_i810i2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
@@ -182,7 +181,6 @@ static struct i2c_algo_bit_data i810_ddc_bit_data = {
.getsda = bit_i810ddc_getsda,
.getscl = bit_i810ddc_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT,
};
diff --git a/drivers/i2c/busses/i2c-ibm_iic.c b/drivers/i2c/busses/i2c-ibm_iic.c
index 0599bbd65d9..5bccb5d6831 100644
--- a/drivers/i2c/busses/i2c-ibm_iic.c
+++ b/drivers/i2c/busses/i2c-ibm_iic.c
@@ -625,7 +625,7 @@ static u32 iic_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL | I2C_FUNC_10BIT_ADDR;
}
-static struct i2c_algorithm iic_algo = {
+static const struct i2c_algorithm iic_algo = {
.master_xfer = iic_xfer,
.functionality = iic_func
};
diff --git a/drivers/i2c/busses/i2c-iop3xx.c b/drivers/i2c/busses/i2c-iop3xx.c
index 48c56939c86..4436c89be58 100644
--- a/drivers/i2c/busses/i2c-iop3xx.c
+++ b/drivers/i2c/busses/i2c-iop3xx.c
@@ -82,14 +82,16 @@ iop3xx_i2c_enable(struct i2c_algo_iop3xx_data *iop3xx_adap)
/*
* Every time unit enable is asserted, GPOD needs to be cleared
- * on IOP321 to avoid data corruption on the bus.
+ * on IOP3XX to avoid data corruption on the bus.
*/
-#ifdef CONFIG_ARCH_IOP321
-#define IOP321_GPOD_I2C0 0x00c0 /* clear these bits to enable ch0 */
-#define IOP321_GPOD_I2C1 0x0030 /* clear these bits to enable ch1 */
-
- *IOP321_GPOD &= (iop3xx_adap->id == 0) ? ~IOP321_GPOD_I2C0 :
- ~IOP321_GPOD_I2C1;
+#ifdef CONFIG_PLAT_IOP
+ if (iop3xx_adap->id == 0) {
+ gpio_line_set(IOP3XX_GPIO_LINE(7), GPIO_LOW);
+ gpio_line_set(IOP3XX_GPIO_LINE(6), GPIO_LOW);
+ } else {
+ gpio_line_set(IOP3XX_GPIO_LINE(5), GPIO_LOW);
+ gpio_line_set(IOP3XX_GPIO_LINE(4), GPIO_LOW);
+ }
#endif
/* NB SR bits not same position as CR IE bits :-( */
iop3xx_adap->SR_enabled =
@@ -401,7 +403,7 @@ iop3xx_i2c_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm iop3xx_i2c_algo = {
+static const struct i2c_algorithm iop3xx_i2c_algo = {
.master_xfer = iop3xx_i2c_master_xfer,
.algo_control = iop3xx_i2c_algo_control,
.functionality = iop3xx_i2c_func,
diff --git a/drivers/i2c/busses/i2c-isa.c b/drivers/i2c/busses/i2c-isa.c
index c3e1d3e888d..4380653748a 100644
--- a/drivers/i2c/busses/i2c-isa.c
+++ b/drivers/i2c/busses/i2c-isa.c
@@ -43,7 +43,7 @@
static u32 isa_func(struct i2c_adapter *adapter);
/* This is the actual algorithm we define */
-static struct i2c_algorithm isa_algorithm = {
+static const struct i2c_algorithm isa_algorithm = {
.functionality = isa_func,
};
@@ -89,9 +89,14 @@ int i2c_isa_add_driver(struct i2c_driver *driver)
dev_dbg(&isa_adapter.dev, "Driver %s registered\n", driver->driver.name);
/* Now look for clients */
- driver->attach_adapter(&isa_adapter);
-
- return 0;
+ res = driver->attach_adapter(&isa_adapter);
+ if (res) {
+ dev_err(&isa_adapter.dev,
+ "Driver %s failed to attach adapter, unregistering\n",
+ driver->driver.name);
+ driver_unregister(&driver->driver);
+ }
+ return res;
}
int i2c_isa_del_driver(struct i2c_driver *driver)
@@ -125,6 +130,8 @@ int i2c_isa_del_driver(struct i2c_driver *driver)
static int __init i2c_isa_init(void)
{
+ int err;
+
mutex_init(&isa_adapter.clist_lock);
INIT_LIST_HEAD(&isa_adapter.clients);
@@ -133,8 +140,16 @@ static int __init i2c_isa_init(void)
sprintf(isa_adapter.dev.bus_id, "i2c-%d", isa_adapter.nr);
isa_adapter.dev.driver = &i2c_adapter_driver;
isa_adapter.dev.release = &i2c_adapter_dev_release;
- device_register(&isa_adapter.dev);
- device_create_file(&isa_adapter.dev, &dev_attr_name);
+ err = device_register(&isa_adapter.dev);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to register device\n");
+ goto exit;
+ }
+ err = device_create_file(&isa_adapter.dev, &dev_attr_name);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to create name file\n");
+ goto exit_unregister;
+ }
/* Add this adapter to the i2c_adapter class */
memset(&isa_adapter.class_dev, 0x00, sizeof(struct class_device));
@@ -142,11 +157,24 @@ static int __init i2c_isa_init(void)
isa_adapter.class_dev.class = &i2c_adapter_class;
strlcpy(isa_adapter.class_dev.class_id, isa_adapter.dev.bus_id,
BUS_ID_SIZE);
- class_device_register(&isa_adapter.class_dev);
+ err = class_device_register(&isa_adapter.class_dev);
+ if (err) {
+ printk(KERN_ERR "i2c-isa: Failed to register class device\n");
+ goto exit_remove_name;
+ }
dev_dbg(&isa_adapter.dev, "%s registered\n", isa_adapter.name);
return 0;
+
+exit_remove_name:
+ device_remove_file(&isa_adapter.dev, &dev_attr_name);
+exit_unregister:
+ init_completion(&isa_adapter.dev_released); /* Needed? */
+ device_unregister(&isa_adapter.dev);
+ wait_for_completion(&isa_adapter.dev_released);
+exit:
+ return err;
}
static void __exit i2c_isa_exit(void)
diff --git a/drivers/i2c/busses/i2c-ite.c b/drivers/i2c/busses/i2c-ite.c
index d82e6dae840..559a62b04ee 100644
--- a/drivers/i2c/busses/i2c-ite.c
+++ b/drivers/i2c/busses/i2c-ite.c
@@ -109,7 +109,7 @@ static int iic_ite_getclock(void *data)
static void iic_ite_waitforpin(void) {
DEFINE_WAIT(wait);
int timeout = 2;
- long flags;
+ unsigned long flags;
/* If interrupts are enabled (which they are), then put the process to
* sleep. This process will be awakened by two events -- either the
diff --git a/drivers/i2c/busses/i2c-ixp2000.c b/drivers/i2c/busses/i2c-ixp2000.c
index cd6f45d186a..dd3f4cd3aa6 100644
--- a/drivers/i2c/busses/i2c-ixp2000.c
+++ b/drivers/i2c/busses/i2c-ixp2000.c
@@ -114,7 +114,6 @@ static int ixp2000_i2c_probe(struct platform_device *plat_dev)
drv_data->algo_data.getsda = ixp2000_bit_getsda;
drv_data->algo_data.getscl = ixp2000_bit_getscl;
drv_data->algo_data.udelay = 6;
- drv_data->algo_data.mdelay = 6;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP2000,
diff --git a/drivers/i2c/busses/i2c-ixp4xx.c b/drivers/i2c/busses/i2c-ixp4xx.c
index 2ed07112d68..ab573254a8a 100644
--- a/drivers/i2c/busses/i2c-ixp4xx.c
+++ b/drivers/i2c/busses/i2c-ixp4xx.c
@@ -122,7 +122,6 @@ static int ixp4xx_i2c_probe(struct platform_device *plat_dev)
drv_data->algo_data.getsda = ixp4xx_bit_getsda;
drv_data->algo_data.getscl = ixp4xx_bit_getscl;
drv_data->algo_data.udelay = 10;
- drv_data->algo_data.mdelay = 10;
drv_data->algo_data.timeout = 100;
drv_data->adapter.id = I2C_HW_B_IXP4XX;
diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c
index 377ab40944b..155a986de51 100644
--- a/drivers/i2c/busses/i2c-mpc.c
+++ b/drivers/i2c/busses/i2c-mpc.c
@@ -272,7 +272,7 @@ static u32 mpc_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm mpc_algo = {
+static const struct i2c_algorithm mpc_algo = {
.master_xfer = mpc_xfer,
.functionality = mpc_functionality,
};
diff --git a/drivers/i2c/busses/i2c-mv64xxx.c b/drivers/i2c/busses/i2c-mv64xxx.c
index ac5cde1bbd2..eacbaf745b6 100644
--- a/drivers/i2c/busses/i2c-mv64xxx.c
+++ b/drivers/i2c/busses/i2c-mv64xxx.c
@@ -431,7 +431,7 @@ mv64xxx_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
return num;
}
-static struct i2c_algorithm mv64xxx_i2c_algo = {
+static const struct i2c_algorithm mv64xxx_i2c_algo = {
.master_xfer = mv64xxx_i2c_xfer,
.functionality = mv64xxx_i2c_functionality,
};
diff --git a/drivers/i2c/busses/i2c-nforce2.c b/drivers/i2c/busses/i2c-nforce2.c
index 604b49e22df..e0292e414ab 100644
--- a/drivers/i2c/busses/i2c-nforce2.c
+++ b/drivers/i2c/busses/i2c-nforce2.c
@@ -109,7 +109,7 @@ static s32 nforce2_access(struct i2c_adapter *adap, u16 addr,
static u32 nforce2_func(struct i2c_adapter *adapter);
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = nforce2_access,
.functionality = nforce2_func,
};
diff --git a/drivers/i2c/busses/i2c-ocores.c b/drivers/i2c/busses/i2c-ocores.c
index 592824087c4..952a28d485c 100644
--- a/drivers/i2c/busses/i2c-ocores.c
+++ b/drivers/i2c/busses/i2c-ocores.c
@@ -199,7 +199,7 @@ static u32 ocores_func(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm ocores_algorithm = {
+static const struct i2c_algorithm ocores_algorithm = {
.master_xfer = ocores_xfer,
.functionality = ocores_func,
};
diff --git a/drivers/i2c/busses/i2c-omap.c b/drivers/i2c/busses/i2c-omap.c
new file mode 100644
index 00000000000..81d87d2c2a2
--- /dev/null
+++ b/drivers/i2c/busses/i2c-omap.c
@@ -0,0 +1,676 @@
+/*
+ * TI OMAP I2C master mode driver
+ *
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ * Copyright (C) 2004 Texas Instruments.
+ *
+ * Updated to work with multiple I2C interfaces on 24xx by
+ * Tony Lindgren <tony@atomide.com> and Imre Deak <imre.deak@nokia.com>
+ * Copyright (C) 2005 Nokia Corporation
+ *
+ * Cleaned up by Juha Yrjölä <juha.yrjola@nokia.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <asm/io.h>
+
+/* timeout waiting for the controller to respond */
+#define OMAP_I2C_TIMEOUT (msecs_to_jiffies(1000))
+
+#define OMAP_I2C_REV_REG 0x00
+#define OMAP_I2C_IE_REG 0x04
+#define OMAP_I2C_STAT_REG 0x08
+#define OMAP_I2C_IV_REG 0x0c
+#define OMAP_I2C_SYSS_REG 0x10
+#define OMAP_I2C_BUF_REG 0x14
+#define OMAP_I2C_CNT_REG 0x18
+#define OMAP_I2C_DATA_REG 0x1c
+#define OMAP_I2C_SYSC_REG 0x20
+#define OMAP_I2C_CON_REG 0x24
+#define OMAP_I2C_OA_REG 0x28
+#define OMAP_I2C_SA_REG 0x2c
+#define OMAP_I2C_PSC_REG 0x30
+#define OMAP_I2C_SCLL_REG 0x34
+#define OMAP_I2C_SCLH_REG 0x38
+#define OMAP_I2C_SYSTEST_REG 0x3c
+
+/* I2C Interrupt Enable Register (OMAP_I2C_IE): */
+#define OMAP_I2C_IE_XRDY (1 << 4) /* TX data ready int enable */
+#define OMAP_I2C_IE_RRDY (1 << 3) /* RX data ready int enable */
+#define OMAP_I2C_IE_ARDY (1 << 2) /* Access ready int enable */
+#define OMAP_I2C_IE_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_IE_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Status Register (OMAP_I2C_STAT): */
+#define OMAP_I2C_STAT_SBD (1 << 15) /* Single byte data */
+#define OMAP_I2C_STAT_BB (1 << 12) /* Bus busy */
+#define OMAP_I2C_STAT_ROVR (1 << 11) /* Receive overrun */
+#define OMAP_I2C_STAT_XUDF (1 << 10) /* Transmit underflow */
+#define OMAP_I2C_STAT_AAS (1 << 9) /* Address as slave */
+#define OMAP_I2C_STAT_AD0 (1 << 8) /* Address zero */
+#define OMAP_I2C_STAT_XRDY (1 << 4) /* Transmit data ready */
+#define OMAP_I2C_STAT_RRDY (1 << 3) /* Receive data ready */
+#define OMAP_I2C_STAT_ARDY (1 << 2) /* Register access ready */
+#define OMAP_I2C_STAT_NACK (1 << 1) /* No ack interrupt enable */
+#define OMAP_I2C_STAT_AL (1 << 0) /* Arbitration lost int ena */
+
+/* I2C Buffer Configuration Register (OMAP_I2C_BUF): */
+#define OMAP_I2C_BUF_RDMA_EN (1 << 15) /* RX DMA channel enable */
+#define OMAP_I2C_BUF_XDMA_EN (1 << 7) /* TX DMA channel enable */
+
+/* I2C Configuration Register (OMAP_I2C_CON): */
+#define OMAP_I2C_CON_EN (1 << 15) /* I2C module enable */
+#define OMAP_I2C_CON_BE (1 << 14) /* Big endian mode */
+#define OMAP_I2C_CON_STB (1 << 11) /* Start byte mode (master) */
+#define OMAP_I2C_CON_MST (1 << 10) /* Master/slave mode */
+#define OMAP_I2C_CON_TRX (1 << 9) /* TX/RX mode (master only) */
+#define OMAP_I2C_CON_XA (1 << 8) /* Expand address */
+#define OMAP_I2C_CON_RM (1 << 2) /* Repeat mode (master only) */
+#define OMAP_I2C_CON_STP (1 << 1) /* Stop cond (master only) */
+#define OMAP_I2C_CON_STT (1 << 0) /* Start condition (master) */
+
+/* I2C System Test Register (OMAP_I2C_SYSTEST): */
+#ifdef DEBUG
+#define OMAP_I2C_SYSTEST_ST_EN (1 << 15) /* System test enable */
+#define OMAP_I2C_SYSTEST_FREE (1 << 14) /* Free running mode */
+#define OMAP_I2C_SYSTEST_TMODE_MASK (3 << 12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_TMODE_SHIFT (12) /* Test mode select */
+#define OMAP_I2C_SYSTEST_SCL_I (1 << 3) /* SCL line sense in */
+#define OMAP_I2C_SYSTEST_SCL_O (1 << 2) /* SCL line drive out */
+#define OMAP_I2C_SYSTEST_SDA_I (1 << 1) /* SDA line sense in */
+#define OMAP_I2C_SYSTEST_SDA_O (1 << 0) /* SDA line drive out */
+#endif
+
+/* I2C System Status register (OMAP_I2C_SYSS): */
+#define OMAP_I2C_SYSS_RDONE (1 << 0) /* Reset Done */
+
+/* I2C System Configuration Register (OMAP_I2C_SYSC): */
+#define OMAP_I2C_SYSC_SRST (1 << 1) /* Soft Reset */
+
+/* REVISIT: Use platform_data instead of module parameters */
+/* Fast Mode = 400 kHz, Standard = 100 kHz */
+static int clock = 100; /* Default: 100 kHz */
+module_param(clock, int, 0);
+MODULE_PARM_DESC(clock, "Set I2C clock in kHz: 400=fast mode (default == 100)");
+
+struct omap_i2c_dev {
+ struct device *dev;
+ void __iomem *base; /* virtual */
+ int irq;
+ struct clk *iclk; /* Interface clock */
+ struct clk *fclk; /* Functional clock */
+ struct completion cmd_complete;
+ struct resource *ioarea;
+ u16 cmd_err;
+ u8 *buf;
+ size_t buf_len;
+ struct i2c_adapter adapter;
+ unsigned rev1:1;
+};
+
+static inline void omap_i2c_write_reg(struct omap_i2c_dev *i2c_dev,
+ int reg, u16 val)
+{
+ __raw_writew(val, i2c_dev->base + reg);
+}
+
+static inline u16 omap_i2c_read_reg(struct omap_i2c_dev *i2c_dev, int reg)
+{
+ return __raw_readw(i2c_dev->base + reg);
+}
+
+static int omap_i2c_get_clocks(struct omap_i2c_dev *dev)
+{
+ if (cpu_is_omap16xx() || cpu_is_omap24xx()) {
+ dev->iclk = clk_get(dev->dev, "i2c_ick");
+ if (IS_ERR(dev->iclk)) {
+ dev->iclk = NULL;
+ return -ENODEV;
+ }
+ }
+
+ dev->fclk = clk_get(dev->dev, "i2c_fck");
+ if (IS_ERR(dev->fclk)) {
+ if (dev->iclk != NULL) {
+ clk_put(dev->iclk);
+ dev->iclk = NULL;
+ }
+ dev->fclk = NULL;
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static void omap_i2c_put_clocks(struct omap_i2c_dev *dev)
+{
+ clk_put(dev->fclk);
+ dev->fclk = NULL;
+ if (dev->iclk != NULL) {
+ clk_put(dev->iclk);
+ dev->iclk = NULL;
+ }
+}
+
+static void omap_i2c_enable_clocks(struct omap_i2c_dev *dev)
+{
+ if (dev->iclk != NULL)
+ clk_enable(dev->iclk);
+ clk_enable(dev->fclk);
+}
+
+static void omap_i2c_disable_clocks(struct omap_i2c_dev *dev)
+{
+ if (dev->iclk != NULL)
+ clk_disable(dev->iclk);
+ clk_disable(dev->fclk);
+}
+
+static int omap_i2c_init(struct omap_i2c_dev *dev)
+{
+ u16 psc = 0;
+ unsigned long fclk_rate = 12000000;
+ unsigned long timeout;
+
+ if (!dev->rev1) {
+ omap_i2c_write_reg(dev, OMAP_I2C_SYSC_REG, OMAP_I2C_SYSC_SRST);
+ /* For some reason we need to set the EN bit before the
+ * reset done bit gets set. */
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+ while (!(omap_i2c_read_reg(dev, OMAP_I2C_SYSS_REG) &
+ OMAP_I2C_SYSS_RDONE)) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev, "timeout waiting"
+ "for controller reset\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+
+ if (cpu_class_is_omap1()) {
+ struct clk *armxor_ck;
+
+ armxor_ck = clk_get(NULL, "armxor_ck");
+ if (IS_ERR(armxor_ck))
+ dev_warn(dev->dev, "Could not get armxor_ck\n");
+ else {
+ fclk_rate = clk_get_rate(armxor_ck);
+ clk_put(armxor_ck);
+ }
+ /* TRM for 5912 says the I2C clock must be prescaled to be
+ * between 7 - 12 MHz. The XOR input clock is typically
+ * 12, 13 or 19.2 MHz. So we should have code that produces:
+ *
+ * XOR MHz Divider Prescaler
+ * 12 1 0
+ * 13 2 1
+ * 19.2 2 1
+ */
+ if (fclk_rate > 16000000)
+ psc = (fclk_rate + 8000000) / 12000000;
+ }
+
+ /* Setup clock prescaler to obtain approx 12MHz I2C module clock: */
+ omap_i2c_write_reg(dev, OMAP_I2C_PSC_REG, psc);
+
+ /* Program desired operating rate */
+ fclk_rate /= (psc + 1) * 1000;
+ if (psc > 2)
+ psc = 2;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLL_REG,
+ fclk_rate / (clock * 2) - 7 + psc);
+ omap_i2c_write_reg(dev, OMAP_I2C_SCLH_REG,
+ fclk_rate / (clock * 2) - 7 + psc);
+
+ /* Take the I2C module out of reset: */
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_EN);
+
+ /* Enable interrupts */
+ omap_i2c_write_reg(dev, OMAP_I2C_IE_REG,
+ (OMAP_I2C_IE_XRDY | OMAP_I2C_IE_RRDY |
+ OMAP_I2C_IE_ARDY | OMAP_I2C_IE_NACK |
+ OMAP_I2C_IE_AL));
+ return 0;
+}
+
+/*
+ * Waiting on Bus Busy
+ */
+static int omap_i2c_wait_for_bb(struct omap_i2c_dev *dev)
+{
+ unsigned long timeout;
+
+ timeout = jiffies + OMAP_I2C_TIMEOUT;
+ while (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG) & OMAP_I2C_STAT_BB) {
+ if (time_after(jiffies, timeout)) {
+ dev_warn(dev->dev, "timeout waiting for bus ready\n");
+ return -ETIMEDOUT;
+ }
+ msleep(1);
+ }
+
+ return 0;
+}
+
+/*
+ * Low level master read/write transaction.
+ */
+static int omap_i2c_xfer_msg(struct i2c_adapter *adap,
+ struct i2c_msg *msg, int stop)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ int r;
+ u16 w;
+
+ dev_dbg(dev->dev, "addr: 0x%04x, len: %d, flags: 0x%x, stop: %d\n",
+ msg->addr, msg->len, msg->flags, stop);
+
+ if (msg->len == 0)
+ return -EINVAL;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_SA_REG, msg->addr);
+
+ /* REVISIT: Could the STB bit of I2C_CON be used with probing? */
+ dev->buf = msg->buf;
+ dev->buf_len = msg->len;
+
+ omap_i2c_write_reg(dev, OMAP_I2C_CNT_REG, dev->buf_len);
+
+ init_completion(&dev->cmd_complete);
+ dev->cmd_err = 0;
+
+ w = OMAP_I2C_CON_EN | OMAP_I2C_CON_MST | OMAP_I2C_CON_STT;
+ if (msg->flags & I2C_M_TEN)
+ w |= OMAP_I2C_CON_XA;
+ if (!(msg->flags & I2C_M_RD))
+ w |= OMAP_I2C_CON_TRX;
+ if (stop)
+ w |= OMAP_I2C_CON_STP;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+
+ r = wait_for_completion_interruptible_timeout(&dev->cmd_complete,
+ OMAP_I2C_TIMEOUT);
+ dev->buf_len = 0;
+ if (r < 0)
+ return r;
+ if (r == 0) {
+ dev_err(dev->dev, "controller timed out\n");
+ omap_i2c_init(dev);
+ return -ETIMEDOUT;
+ }
+
+ if (likely(!dev->cmd_err))
+ return 0;
+
+ /* We have an error */
+ if (dev->cmd_err & (OMAP_I2C_STAT_AL | OMAP_I2C_STAT_ROVR |
+ OMAP_I2C_STAT_XUDF)) {
+ omap_i2c_init(dev);
+ return -EIO;
+ }
+
+ if (dev->cmd_err & OMAP_I2C_STAT_NACK) {
+ if (msg->flags & I2C_M_IGNORE_NAK)
+ return 0;
+ if (stop) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_CON_REG);
+ w |= OMAP_I2C_CON_STP;
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, w);
+ }
+ return -EREMOTEIO;
+ }
+ return -EIO;
+}
+
+
+/*
+ * Prepare controller for a transaction and call omap_i2c_xfer_msg
+ * to do the work during IRQ processing.
+ */
+static int
+omap_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)
+{
+ struct omap_i2c_dev *dev = i2c_get_adapdata(adap);
+ int i;
+ int r;
+
+ omap_i2c_enable_clocks(dev);
+
+ /* REVISIT: initialize and use adap->retries. This is an optional
+ * feature */
+ if ((r = omap_i2c_wait_for_bb(dev)) < 0)
+ goto out;
+
+ for (i = 0; i < num; i++) {
+ r = omap_i2c_xfer_msg(adap, &msgs[i], (i == (num - 1)));
+ if (r != 0)
+ break;
+ }
+
+ if (r == 0)
+ r = num;
+out:
+ omap_i2c_disable_clocks(dev);
+ return r;
+}
+
+static u32
+omap_i2c_func(struct i2c_adapter *adap)
+{
+ return I2C_FUNC_I2C | (I2C_FUNC_SMBUS_EMUL & ~I2C_FUNC_SMBUS_QUICK);
+}
+
+static inline void
+omap_i2c_complete_cmd(struct omap_i2c_dev *dev, u16 err)
+{
+ dev->cmd_err |= err;
+ complete(&dev->cmd_complete);
+}
+
+static inline void
+omap_i2c_ack_stat(struct omap_i2c_dev *dev, u16 stat)
+{
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
+}
+
+static irqreturn_t
+omap_i2c_rev1_isr(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ struct omap_i2c_dev *dev = dev_id;
+ u16 iv, w;
+
+ iv = omap_i2c_read_reg(dev, OMAP_I2C_IV_REG);
+ switch (iv) {
+ case 0x00: /* None */
+ break;
+ case 0x01: /* Arbitration lost */
+ dev_err(dev->dev, "Arbitration lost\n");
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL);
+ break;
+ case 0x02: /* No acknowledgement */
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, OMAP_I2C_CON_STP);
+ break;
+ case 0x03: /* Register access ready */
+ omap_i2c_complete_cmd(dev, 0);
+ break;
+ case 0x04: /* Receive data ready */
+ if (dev->buf_len) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ *dev->buf++ = w;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "RRDY IRQ while no data requested\n");
+ break;
+ case 0x05: /* Transmit data ready */
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ } else
+ dev_err(dev->dev, "XRDY IRQ while no data to send\n");
+ break;
+ default:
+ return IRQ_NONE;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+omap_i2c_isr(int this_irq, void *dev_id, struct pt_regs *regs)
+{
+ struct omap_i2c_dev *dev = dev_id;
+ u16 bits;
+ u16 stat, w;
+ int count = 0;
+
+ bits = omap_i2c_read_reg(dev, OMAP_I2C_IE_REG);
+ while ((stat = (omap_i2c_read_reg(dev, OMAP_I2C_STAT_REG))) & bits) {
+ dev_dbg(dev->dev, "IRQ (ISR = 0x%04x)\n", stat);
+ if (count++ == 100) {
+ dev_warn(dev->dev, "Too much work in one IRQ\n");
+ break;
+ }
+
+ omap_i2c_write_reg(dev, OMAP_I2C_STAT_REG, stat);
+
+ if (stat & OMAP_I2C_STAT_ARDY) {
+ omap_i2c_complete_cmd(dev, 0);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_RRDY) {
+ w = omap_i2c_read_reg(dev, OMAP_I2C_DATA_REG);
+ if (dev->buf_len) {
+ *dev->buf++ = w;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ *dev->buf++ = w >> 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "RRDY IRQ while no data"
+ "requested\n");
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_RRDY);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_XRDY) {
+ w = 0;
+ if (dev->buf_len) {
+ w = *dev->buf++;
+ dev->buf_len--;
+ if (dev->buf_len) {
+ w |= *dev->buf++ << 8;
+ dev->buf_len--;
+ }
+ } else
+ dev_err(dev->dev, "XRDY IRQ while no"
+ "data to send\n");
+ omap_i2c_write_reg(dev, OMAP_I2C_DATA_REG, w);
+ omap_i2c_ack_stat(dev, OMAP_I2C_STAT_XRDY);
+ continue;
+ }
+ if (stat & OMAP_I2C_STAT_ROVR) {
+ dev_err(dev->dev, "Receive overrun\n");
+ dev->cmd_err |= OMAP_I2C_STAT_ROVR;
+ }
+ if (stat & OMAP_I2C_STAT_XUDF) {
+ dev_err(dev->dev, "Transmit overflow\n");
+ dev->cmd_err |= OMAP_I2C_STAT_XUDF;
+ }
+ if (stat & OMAP_I2C_STAT_NACK) {
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_NACK);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG,
+ OMAP_I2C_CON_STP);
+ }
+ if (stat & OMAP_I2C_STAT_AL) {
+ dev_err(dev->dev, "Arbitration lost\n");
+ omap_i2c_complete_cmd(dev, OMAP_I2C_STAT_AL);
+ }
+ }
+
+ return count ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static const struct i2c_algorithm omap_i2c_algo = {
+ .master_xfer = omap_i2c_xfer,
+ .functionality = omap_i2c_func,
+};
+
+static int
+omap_i2c_probe(struct platform_device *pdev)
+{
+ struct omap_i2c_dev *dev;
+ struct i2c_adapter *adap;
+ struct resource *mem, *irq, *ioarea;
+ int r;
+
+ /* NOTE: driver uses the static register mapping */
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!mem) {
+ dev_err(&pdev->dev, "no mem resource?\n");
+ return -ENODEV;
+ }
+ irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!irq) {
+ dev_err(&pdev->dev, "no irq resource?\n");
+ return -ENODEV;
+ }
+
+ ioarea = request_mem_region(mem->start, (mem->end - mem->start) + 1,
+ pdev->name);
+ if (!ioarea) {
+ dev_err(&pdev->dev, "I2C region already claimed\n");
+ return -EBUSY;
+ }
+
+ if (clock > 200)
+ clock = 400; /* Fast mode */
+ else
+ clock = 100; /* Standard mode */
+
+ dev = kzalloc(sizeof(struct omap_i2c_dev), GFP_KERNEL);
+ if (!dev) {
+ r = -ENOMEM;
+ goto err_release_region;
+ }
+
+ dev->dev = &pdev->dev;
+ dev->irq = irq->start;
+ dev->base = (void __iomem *) IO_ADDRESS(mem->start);
+ platform_set_drvdata(pdev, dev);
+
+ if ((r = omap_i2c_get_clocks(dev)) != 0)
+ goto err_free_mem;
+
+ omap_i2c_enable_clocks(dev);
+
+ if (cpu_is_omap15xx())
+ dev->rev1 = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) < 0x20;
+
+ /* reset ASAP, clearing any IRQs */
+ omap_i2c_init(dev);
+
+ r = request_irq(dev->irq, dev->rev1 ? omap_i2c_rev1_isr : omap_i2c_isr,
+ 0, pdev->name, dev);
+
+ if (r) {
+ dev_err(dev->dev, "failure requesting irq %i\n", dev->irq);
+ goto err_unuse_clocks;
+ }
+ r = omap_i2c_read_reg(dev, OMAP_I2C_REV_REG) & 0xff;
+ dev_info(dev->dev, "bus %d rev%d.%d at %d kHz\n",
+ pdev->id, r >> 4, r & 0xf, clock);
+
+ adap = &dev->adapter;
+ i2c_set_adapdata(adap, dev);
+ adap->owner = THIS_MODULE;
+ adap->class = I2C_CLASS_HWMON;
+ strncpy(adap->name, "OMAP I2C adapter", sizeof(adap->name));
+ adap->algo = &omap_i2c_algo;
+ adap->dev.parent = &pdev->dev;
+
+ /* i2c device drivers may be active on return from add_adapter() */
+ r = i2c_add_adapter(adap);
+ if (r) {
+ dev_err(dev->dev, "failure adding adapter\n");
+ goto err_free_irq;
+ }
+
+ omap_i2c_disable_clocks(dev);
+
+ return 0;
+
+err_free_irq:
+ free_irq(dev->irq, dev);
+err_unuse_clocks:
+ omap_i2c_disable_clocks(dev);
+ omap_i2c_put_clocks(dev);
+err_free_mem:
+ platform_set_drvdata(pdev, NULL);
+ kfree(dev);
+err_release_region:
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+
+ return r;
+}
+
+static int
+omap_i2c_remove(struct platform_device *pdev)
+{
+ struct omap_i2c_dev *dev = platform_get_drvdata(pdev);
+ struct resource *mem;
+
+ platform_set_drvdata(pdev, NULL);
+
+ free_irq(dev->irq, dev);
+ i2c_del_adapter(&dev->adapter);
+ omap_i2c_write_reg(dev, OMAP_I2C_CON_REG, 0);
+ omap_i2c_put_clocks(dev);
+ kfree(dev);
+ mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(mem->start, (mem->end - mem->start) + 1);
+ return 0;
+}
+
+static struct platform_driver omap_i2c_driver = {
+ .probe = omap_i2c_probe,
+ .remove = omap_i2c_remove,
+ .driver = {
+ .name = "i2c_omap",
+ .owner = THIS_MODULE,
+ },
+};
+
+/* I2C may be needed to bring up other drivers */
+static int __init
+omap_i2c_init_driver(void)
+{
+ return platform_driver_register(&omap_i2c_driver);
+}
+subsys_initcall(omap_i2c_init_driver);
+
+static void __exit omap_i2c_exit_driver(void)
+{
+ platform_driver_unregister(&omap_i2c_driver);
+}
+module_exit(omap_i2c_exit_driver);
+
+MODULE_AUTHOR("MontaVista Software, Inc. (and others)");
+MODULE_DESCRIPTION("TI OMAP I2C bus adapter");
+MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-parport-light.c b/drivers/i2c/busses/i2c-parport-light.c
index e09ebbb2f9f..5eb2bd294fd 100644
--- a/drivers/i2c/busses/i2c-parport-light.c
+++ b/drivers/i2c/busses/i2c-parport-light.c
@@ -103,7 +103,6 @@ static struct i2c_algo_bit_data parport_algo_data = {
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 50,
- .mdelay = 50,
.timeout = HZ,
};
diff --git a/drivers/i2c/busses/i2c-parport.c b/drivers/i2c/busses/i2c-parport.c
index 934bd55bae1..48a829431c7 100644
--- a/drivers/i2c/busses/i2c-parport.c
+++ b/drivers/i2c/busses/i2c-parport.c
@@ -138,7 +138,6 @@ static struct i2c_algo_bit_data parport_algo_data = {
.getsda = parport_getsda,
.getscl = parport_getscl,
.udelay = 60,
- .mdelay = 60,
.timeout = HZ,
};
diff --git a/drivers/i2c/busses/i2c-piix4.c b/drivers/i2c/busses/i2c-piix4.c
index 8f2f65b793b..30c7a1b38cb 100644
--- a/drivers/i2c/busses/i2c-piix4.c
+++ b/drivers/i2c/busses/i2c-piix4.c
@@ -376,7 +376,7 @@ static u32 piix4_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BLOCK_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = piix4_access,
.functionality = piix4_func,
};
diff --git a/drivers/i2c/busses/i2c-powermac.c b/drivers/i2c/busses/i2c-powermac.c
index d658d910795..a508cb962d2 100644
--- a/drivers/i2c/busses/i2c-powermac.c
+++ b/drivers/i2c/busses/i2c-powermac.c
@@ -175,7 +175,7 @@ static u32 i2c_powermac_func(struct i2c_adapter * adapter)
}
/* For now, we only handle smbus */
-static struct i2c_algorithm i2c_powermac_algorithm = {
+static const struct i2c_algorithm i2c_powermac_algorithm = {
.smbus_xfer = i2c_powermac_smbus_xfer,
.master_xfer = i2c_powermac_master_xfer,
.functionality = i2c_powermac_func,
diff --git a/drivers/i2c/busses/i2c-prosavage.c b/drivers/i2c/busses/i2c-prosavage.c
index 9479525892e..7745e21874a 100644
--- a/drivers/i2c/busses/i2c-prosavage.c
+++ b/drivers/i2c/busses/i2c-prosavage.c
@@ -180,7 +180,6 @@ static int i2c_register_bus(struct pci_dev *dev, struct s_i2c_bus *p, void __iom
p->algo.getsda = bit_s3via_getsda;
p->algo.getscl = bit_s3via_getscl;
p->algo.udelay = CYCLE_DELAY;
- p->algo.mdelay = CYCLE_DELAY;
p->algo.timeout = TIMEOUT;
p->algo.data = p;
p->mmvga = mmvga;
diff --git a/drivers/i2c/busses/i2c-pxa.c b/drivers/i2c/busses/i2c-pxa.c
index ee114b48fac..cd4ad98ad51 100644
--- a/drivers/i2c/busses/i2c-pxa.c
+++ b/drivers/i2c/busses/i2c-pxa.c
@@ -926,7 +926,7 @@ static u32 i2c_pxa_functionality(struct i2c_adapter *adap)
return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL;
}
-static struct i2c_algorithm i2c_pxa_algorithm = {
+static const struct i2c_algorithm i2c_pxa_algorithm = {
.master_xfer = i2c_pxa_xfer,
.functionality = i2c_pxa_functionality,
};
diff --git a/drivers/i2c/busses/i2c-s3c2410.c b/drivers/i2c/busses/i2c-s3c2410.c
index 5d2950e91fc..9ebe429a0a0 100644
--- a/drivers/i2c/busses/i2c-s3c2410.c
+++ b/drivers/i2c/busses/i2c-s3c2410.c
@@ -566,7 +566,7 @@ static u32 s3c24xx_i2c_func(struct i2c_adapter *adap)
/* i2c bus registration info */
-static struct i2c_algorithm s3c24xx_i2c_algorithm = {
+static const struct i2c_algorithm s3c24xx_i2c_algorithm = {
.master_xfer = s3c24xx_i2c_xfer,
.functionality = s3c24xx_i2c_func,
};
diff --git a/drivers/i2c/busses/i2c-savage4.c b/drivers/i2c/busses/i2c-savage4.c
index 0c8518298e4..209f47ea175 100644
--- a/drivers/i2c/busses/i2c-savage4.c
+++ b/drivers/i2c/busses/i2c-savage4.c
@@ -140,7 +140,6 @@ static struct i2c_algo_bit_data sav_i2c_bit_data = {
.getsda = bit_savi2c_getsda,
.getscl = bit_savi2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
diff --git a/drivers/i2c/busses/i2c-sibyte.c b/drivers/i2c/busses/i2c-sibyte.c
index fa503ed9f86..0ca599d3b40 100644
--- a/drivers/i2c/busses/i2c-sibyte.c
+++ b/drivers/i2c/busses/i2c-sibyte.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2004 Steven J. Hill
* Copyright (C) 2001,2002,2003 Broadcom Corporation
+ * Copyright (C) 1995-2000 Simon G. Vogl
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -17,11 +18,162 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
+#include <linux/kernel.h>
#include <linux/module.h>
-#include <linux/i2c-algo-sibyte.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <asm/io.h>
#include <asm/sibyte/sb1250_regs.h>
#include <asm/sibyte/sb1250_smbus.h>
+
+struct i2c_algo_sibyte_data {
+ void *data; /* private data */
+ int bus; /* which bus */
+ void *reg_base; /* CSR base */
+};
+
+/* ----- global defines ----------------------------------------------- */
+#define SMB_CSR(a,r) ((long)(a->reg_base + r))
+
+/* ----- global variables --------------------------------------------- */
+
+/* module parameters:
+ */
+static int bit_scan; /* have a look at what's hanging 'round */
+module_param(bit_scan, int, 0);
+MODULE_PARM_DESC(bit_scan, "Scan for active chips on the bus");
+
+
+static int smbus_xfer(struct i2c_adapter *i2c_adap, u16 addr,
+ unsigned short flags, char read_write,
+ u8 command, int size, union i2c_smbus_data * data)
+{
+ struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+ int data_bytes = 0;
+ int error;
+
+ while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ switch (size) {
+ case I2C_SMBUS_QUICK:
+ csr_out32((V_SMB_ADDR(addr) |
+ (read_write == I2C_SMBUS_READ ? M_SMB_QDATA : 0) |
+ V_SMB_TT_QUICKCMD), SMB_CSR(adap, R_SMB_START));
+ break;
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_RD1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 1;
+ } else {
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ case I2C_SMBUS_BYTE_DATA:
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD1BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 1;
+ } else {
+ csr_out32(V_SMB_LB(data->byte),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ case I2C_SMBUS_WORD_DATA:
+ csr_out32(V_SMB_CMD(command), SMB_CSR(adap, R_SMB_CMD));
+ if (read_write == I2C_SMBUS_READ) {
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_CMD_RD2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ data_bytes = 2;
+ } else {
+ csr_out32(V_SMB_LB(data->word & 0xff),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32(V_SMB_MB(data->word >> 8),
+ SMB_CSR(adap, R_SMB_DATA));
+ csr_out32((V_SMB_ADDR(addr) | V_SMB_TT_WR2BYTE),
+ SMB_CSR(adap, R_SMB_START));
+ }
+ break;
+ default:
+ return -1; /* XXXKW better error code? */
+ }
+
+ while (csr_in32(SMB_CSR(adap, R_SMB_STATUS)) & M_SMB_BUSY)
+ ;
+
+ error = csr_in32(SMB_CSR(adap, R_SMB_STATUS));
+ if (error & M_SMB_ERROR) {
+ /* Clear error bit by writing a 1 */
+ csr_out32(M_SMB_ERROR, SMB_CSR(adap, R_SMB_STATUS));
+ return -1; /* XXXKW better error code? */
+ }
+
+ if (data_bytes == 1)
+ data->byte = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xff;
+ if (data_bytes == 2)
+ data->word = csr_in32(SMB_CSR(adap, R_SMB_DATA)) & 0xffff;
+
+ return 0;
+}
+
+static u32 bit_func(struct i2c_adapter *adap)
+{
+ return (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA);
+}
+
+
+/* -----exported algorithm data: ------------------------------------- */
+
+static const struct i2c_algorithm i2c_sibyte_algo = {
+ .smbus_xfer = smbus_xfer,
+ .functionality = bit_func,
+};
+
+/*
+ * registering functions to load algorithms at runtime
+ */
+int i2c_sibyte_add_bus(struct i2c_adapter *i2c_adap, int speed)
+{
+ int i;
+ struct i2c_algo_sibyte_data *adap = i2c_adap->algo_data;
+
+ /* register new adapter to i2c module... */
+ i2c_adap->algo = &i2c_sibyte_algo;
+
+ /* Set the frequency to 100 kHz */
+ csr_out32(speed, SMB_CSR(adap,R_SMB_FREQ));
+ csr_out32(0, SMB_CSR(adap,R_SMB_CONTROL));
+
+ /* scan bus */
+ if (bit_scan) {
+ union i2c_smbus_data data;
+ int rc;
+ printk(KERN_INFO " i2c-algo-sibyte.o: scanning bus %s.\n",
+ i2c_adap->name);
+ for (i = 0x00; i < 0x7f; i++) {
+ /* XXXKW is this a realistic probe? */
+ rc = smbus_xfer(i2c_adap, i, 0, I2C_SMBUS_READ, 0,
+ I2C_SMBUS_BYTE_DATA, &data);
+ if (!rc) {
+ printk("(%02x)",i);
+ } else
+ printk(".");
+ }
+ printk("\n");
+ }
+
+ return i2c_add_adapter(i2c_adap);
+}
+
+
static struct i2c_algo_sibyte_data sibyte_board_data[2] = {
{ NULL, 0, (void *) (CKSEG1+A_SMB_BASE(0)) },
{ NULL, 1, (void *) (CKSEG1+A_SMB_BASE(1)) }
@@ -58,13 +210,13 @@ static int __init i2c_sibyte_init(void)
static void __exit i2c_sibyte_exit(void)
{
- i2c_sibyte_del_bus(&sibyte_board_adapter[0]);
- i2c_sibyte_del_bus(&sibyte_board_adapter[1]);
+ i2c_del_adapter(&sibyte_board_adapter[0]);
+ i2c_del_adapter(&sibyte_board_adapter[1]);
}
module_init(i2c_sibyte_init);
module_exit(i2c_sibyte_exit);
-MODULE_AUTHOR("Kip Walker <kwalker@broadcom.com>, Steven J. Hill <sjhill@realitydiluted.com>");
+MODULE_AUTHOR("Kip Walker (Broadcom Corp.), Steven J. Hill <sjhill@realitydiluted.com>");
MODULE_DESCRIPTION("SMBus adapter routines for SiByte boards");
MODULE_LICENSE("GPL");
diff --git a/drivers/i2c/busses/i2c-sis5595.c b/drivers/i2c/busses/i2c-sis5595.c
index b57ab74d23e..38bbfd840b6 100644
--- a/drivers/i2c/busses/i2c-sis5595.c
+++ b/drivers/i2c/busses/i2c-sis5595.c
@@ -358,7 +358,7 @@ static u32 sis5595_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis5595_access,
.functionality = sis5595_func,
};
diff --git a/drivers/i2c/busses/i2c-sis630.c b/drivers/i2c/busses/i2c-sis630.c
index acb75e28241..dec0bafb52a 100644
--- a/drivers/i2c/busses/i2c-sis630.c
+++ b/drivers/i2c/busses/i2c-sis630.c
@@ -450,7 +450,7 @@ exit:
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis630_access,
.functionality = sis630_func,
};
diff --git a/drivers/i2c/busses/i2c-sis96x.c b/drivers/i2c/busses/i2c-sis96x.c
index 1a73c0532fc..7fd07fbac33 100644
--- a/drivers/i2c/busses/i2c-sis96x.c
+++ b/drivers/i2c/busses/i2c-sis96x.c
@@ -242,7 +242,7 @@ static u32 sis96x_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_PROC_CALL;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = sis96x_access,
.functionality = sis96x_func,
};
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index 73f481e93a3..a54adc50d16 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -27,6 +27,10 @@
#include <linux/errno.h>
#include <linux/i2c.h>
+static unsigned short chip_addr;
+module_param(chip_addr, ushort, S_IRUGO);
+MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n");
+
static u8 stub_pointer;
static u8 stub_bytes[256];
static u16 stub_words[256];
@@ -37,6 +41,9 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
{
s32 ret;
+ if (addr != chip_addr)
+ return -ENODEV;
+
switch (size) {
case I2C_SMBUS_QUICK:
@@ -108,7 +115,7 @@ static u32 stub_func(struct i2c_adapter *adapter)
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.functionality = stub_func,
.smbus_xfer = stub_xfer,
};
@@ -122,7 +129,17 @@ static struct i2c_adapter stub_adapter = {
static int __init i2c_stub_init(void)
{
- printk(KERN_INFO "i2c-stub loaded\n");
+ if (!chip_addr) {
+ printk(KERN_ERR "i2c-stub: Please specify a chip address\n");
+ return -ENODEV;
+ }
+ if (chip_addr < 0x03 || chip_addr > 0x77) {
+ printk(KERN_ERR "i2c-stub: Invalid chip address 0x%02x\n",
+ chip_addr);
+ return -EINVAL;
+ }
+
+ printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", chip_addr);
return i2c_add_adapter(&stub_adapter);
}
diff --git a/drivers/i2c/busses/i2c-via.c b/drivers/i2c/busses/i2c-via.c
index 484bbacfce6..910e200ad50 100644
--- a/drivers/i2c/busses/i2c-via.c
+++ b/drivers/i2c/busses/i2c-via.c
@@ -81,7 +81,6 @@ static struct i2c_algo_bit_data bit_data = {
.getsda = bit_via_getsda,
.getscl = bit_via_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = HZ
};
diff --git a/drivers/i2c/busses/i2c-viapro.c b/drivers/i2c/busses/i2c-viapro.c
index 47e52bf2c5e..efc6bbf0cc0 100644
--- a/drivers/i2c/busses/i2c-viapro.c
+++ b/drivers/i2c/busses/i2c-viapro.c
@@ -34,6 +34,8 @@
VT8233A 0x3147 yes?
VT8235 0x3177 yes
VT8237R 0x3227 yes
+ VT8237A 0x3337 yes
+ VT8251 0x3287 yes
Note: we assume there can only be one device, with one SMBus interface.
*/
@@ -297,7 +299,7 @@ static u32 vt596_func(struct i2c_adapter *adapter)
return func;
}
-static struct i2c_algorithm smbus_algorithm = {
+static const struct i2c_algorithm smbus_algorithm = {
.smbus_xfer = vt596_access,
.functionality = vt596_func,
};
@@ -381,7 +383,9 @@ found:
dev_dbg(&pdev->dev, "VT596_smba = 0x%X\n", vt596_smba);
switch (pdev->device) {
+ case PCI_DEVICE_ID_VIA_8251:
case PCI_DEVICE_ID_VIA_8237:
+ case PCI_DEVICE_ID_VIA_8237A:
case PCI_DEVICE_ID_VIA_8235:
case PCI_DEVICE_ID_VIA_8233A:
case PCI_DEVICE_ID_VIA_8233_0:
@@ -432,8 +436,12 @@ static struct pci_device_id vt596_ids[] = {
.driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237),
.driver_data = SMBBA3 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237A),
+ .driver_data = SMBBA3 },
{ PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8231_4),
.driver_data = SMBBA1 },
+ { PCI_DEVICE(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8251),
+ .driver_data = SMBBA3 },
{ 0, }
};
diff --git a/drivers/i2c/busses/i2c-voodoo3.c b/drivers/i2c/busses/i2c-voodoo3.c
index b675773b0cc..6c8d2518338 100644
--- a/drivers/i2c/busses/i2c-voodoo3.c
+++ b/drivers/i2c/busses/i2c-voodoo3.c
@@ -160,7 +160,6 @@ static struct i2c_algo_bit_data voo_i2c_bit_data = {
.getsda = bit_vooi2c_getsda,
.getscl = bit_vooi2c_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
@@ -177,7 +176,6 @@ static struct i2c_algo_bit_data voo_ddc_bit_data = {
.getsda = bit_vooddc_getsda,
.getscl = bit_vooddc_getscl,
.udelay = CYCLE_DELAY,
- .mdelay = CYCLE_DELAY,
.timeout = TIMEOUT
};
diff --git a/drivers/i2c/busses/scx200_acb.c b/drivers/i2c/busses/scx200_acb.c
index eae9e81be37..32aab0d34ee 100644
--- a/drivers/i2c/busses/scx200_acb.c
+++ b/drivers/i2c/busses/scx200_acb.c
@@ -383,7 +383,7 @@ static u32 scx200_acb_func(struct i2c_adapter *adapter)
}
/* For now, we only handle combined mode (smbus) */
-static struct i2c_algorithm scx200_acb_algorithm = {
+static const struct i2c_algorithm scx200_acb_algorithm = {
.smbus_xfer = scx200_acb_smbus_xfer,
.functionality = scx200_acb_func,
};
diff --git a/drivers/i2c/busses/scx200_i2c.c b/drivers/i2c/busses/scx200_i2c.c
index cb3ef5ac99f..8b65a5cf825 100644
--- a/drivers/i2c/busses/scx200_i2c.c
+++ b/drivers/i2c/busses/scx200_i2c.c
@@ -71,12 +71,12 @@ static int scx200_i2c_getsda(void *data)
*/
static struct i2c_algo_bit_data scx200_i2c_data = {
- NULL,
- scx200_i2c_setsda,
- scx200_i2c_setscl,
- scx200_i2c_getsda,
- scx200_i2c_getscl,
- 10, 10, 100, /* waits, timeout */
+ .setsda = scx200_i2c_setsda,
+ .setscl = scx200_i2c_setscl,
+ .getsda = scx200_i2c_getsda,
+ .getscl = scx200_i2c_getscl,
+ .udelay = 10,
+ .timeout = 100,
};
static struct i2c_adapter scx200_i2c_ops = {
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
index 13c108269a6..cec3a0c3894 100644
--- a/drivers/i2c/chips/eeprom.c
+++ b/drivers/i2c/chips/eeprom.c
@@ -209,10 +209,14 @@ static int eeprom_detect(struct i2c_adapter *adapter, int address, int kind)
}
/* create the sysfs eeprom file */
- sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
+ err = sysfs_create_bin_file(&new_client->dev.kobj, &eeprom_attr);
+ if (err)
+ goto exit_detach;
return 0;
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -223,6 +227,8 @@ static int eeprom_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_bin_file(&client->dev.kobj, &eeprom_attr);
+
err = i2c_detach_client(client);
if (err)
return err;
diff --git a/drivers/i2c/chips/isp1301_omap.c b/drivers/i2c/chips/isp1301_omap.c
index f92505b94c6..182f0495346 100644
--- a/drivers/i2c/chips/isp1301_omap.c
+++ b/drivers/i2c/chips/isp1301_omap.c
@@ -30,7 +30,7 @@
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
#include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/i2c.h>
#include <linux/workqueue.h>
diff --git a/drivers/i2c/chips/max6875.c b/drivers/i2c/chips/max6875.c
index 88d2ddee449..76645c14297 100644
--- a/drivers/i2c/chips/max6875.c
+++ b/drivers/i2c/chips/max6875.c
@@ -199,8 +199,7 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind)
mutex_init(&data->update_lock);
/* Init fake client data */
- /* set the client data to the i2c_client so that it will get freed */
- i2c_set_clientdata(fake_client, fake_client);
+ i2c_set_clientdata(fake_client, NULL);
fake_client->addr = address | 1;
fake_client->adapter = adapter;
fake_client->driver = &max6875_driver;
@@ -214,13 +213,17 @@ static int max6875_detect(struct i2c_adapter *adapter, int address, int kind)
goto exit_kfree2;
if ((err = i2c_attach_client(fake_client)) != 0)
- goto exit_detach;
+ goto exit_detach1;
- sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr);
+ err = sysfs_create_bin_file(&real_client->dev.kobj, &user_eeprom_attr);
+ if (err)
+ goto exit_detach2;
return 0;
-exit_detach:
+exit_detach2:
+ i2c_detach_client(fake_client);
+exit_detach1:
i2c_detach_client(real_client);
exit_kfree2:
kfree(fake_client);
@@ -229,14 +232,24 @@ exit_kfree1:
return err;
}
+/* Will be called for both the real client and the fake client */
static int max6875_detach_client(struct i2c_client *client)
{
int err;
+ struct max6875_data *data = i2c_get_clientdata(client);
+
+ /* data is NULL for the fake client */
+ if (data)
+ sysfs_remove_bin_file(&client->dev.kobj, &user_eeprom_attr);
err = i2c_detach_client(client);
if (err)
return err;
- kfree(i2c_get_clientdata(client));
+
+ if (data) /* real client */
+ kfree(data);
+ else /* fake client */
+ kfree(client);
return 0;
}
diff --git a/drivers/i2c/chips/pca9539.c b/drivers/i2c/chips/pca9539.c
index cb22280cdd2..f43c4e79b55 100644
--- a/drivers/i2c/chips/pca9539.c
+++ b/drivers/i2c/chips/pca9539.c
@@ -148,11 +148,16 @@ static int pca9539_detect(struct i2c_adapter *adapter, int address, int kind)
if ((err = i2c_attach_client(new_client)))
goto exit_kfree;
- /* Register sysfs hooks (don't care about failure) */
- sysfs_create_group(&new_client->dev.kobj, &pca9539_defattr_group);
+ /* Register sysfs hooks */
+ err = sysfs_create_group(&new_client->dev.kobj,
+ &pca9539_defattr_group);
+ if (err)
+ goto exit_detach;
return 0;
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -163,6 +168,8 @@ static int pca9539_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pca9539_defattr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/pcf8574.c b/drivers/i2c/chips/pcf8574.c
index c3e6449c448..32b25427eab 100644
--- a/drivers/i2c/chips/pcf8574.c
+++ b/drivers/i2c/chips/pcf8574.c
@@ -105,6 +105,16 @@ static ssize_t set_write(struct device *dev, struct device_attribute *attr, cons
static DEVICE_ATTR(write, S_IWUSR | S_IRUGO, show_write, set_write);
+static struct attribute *pcf8574_attributes[] = {
+ &dev_attr_read.attr,
+ &dev_attr_write.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8574_attr_group = {
+ .attrs = pcf8574_attributes,
+};
+
/*
* Real code
*/
@@ -166,13 +176,13 @@ static int pcf8574_detect(struct i2c_adapter *adapter, int address, int kind)
pcf8574_init_client(new_client);
/* Register sysfs hooks */
- device_create_file(&new_client->dev, &dev_attr_read);
- device_create_file(&new_client->dev, &dev_attr_write);
+ err = sysfs_create_group(&new_client->dev.kobj, &pcf8574_attr_group);
+ if (err)
+ goto exit_detach;
return 0;
-/* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
-
+ exit_detach:
+ i2c_detach_client(new_client);
exit_free:
kfree(data);
exit:
@@ -183,6 +193,8 @@ static int pcf8574_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pcf8574_attr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/pcf8591.c b/drivers/i2c/chips/pcf8591.c
index 925a6b371fd..4dc36376eb3 100644
--- a/drivers/i2c/chips/pcf8591.c
+++ b/drivers/i2c/chips/pcf8591.c
@@ -158,6 +158,28 @@ static ssize_t set_out0_enable(struct device *dev, struct device_attribute *attr
static DEVICE_ATTR(out0_enable, S_IWUSR | S_IRUGO,
show_out0_enable, set_out0_enable);
+static struct attribute *pcf8591_attributes[] = {
+ &dev_attr_out0_enable.attr,
+ &dev_attr_out0_output.attr,
+ &dev_attr_in0_input.attr,
+ &dev_attr_in1_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group = {
+ .attrs = pcf8591_attributes,
+};
+
+static struct attribute *pcf8591_attributes_opt[] = {
+ &dev_attr_in2_input.attr,
+ &dev_attr_in3_input.attr,
+ NULL
+};
+
+static const struct attribute_group pcf8591_attr_group_opt = {
+ .attrs = pcf8591_attributes_opt,
+};
+
/*
* Real code
*/
@@ -211,24 +233,31 @@ static int pcf8591_detect(struct i2c_adapter *adapter, int address, int kind)
pcf8591_init_client(new_client);
/* Register sysfs hooks */
- device_create_file(&new_client->dev, &dev_attr_out0_enable);
- device_create_file(&new_client->dev, &dev_attr_out0_output);
- device_create_file(&new_client->dev, &dev_attr_in0_input);
- device_create_file(&new_client->dev, &dev_attr_in1_input);
+ err = sysfs_create_group(&new_client->dev.kobj, &pcf8591_attr_group);
+ if (err)
+ goto exit_detach;
/* Register input2 if not in "two differential inputs" mode */
- if (input_mode != 3 )
- device_create_file(&new_client->dev, &dev_attr_in2_input);
-
+ if (input_mode != 3) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in2_input)))
+ goto exit_sysfs_remove;
+ }
+
/* Register input3 only in "four single ended inputs" mode */
- if (input_mode == 0)
- device_create_file(&new_client->dev, &dev_attr_in3_input);
-
+ if (input_mode == 0) {
+ if ((err = device_create_file(&new_client->dev,
+ &dev_attr_in3_input)))
+ goto exit_sysfs_remove;
+ }
+
return 0;
-
- /* OK, this is not exactly good programming practice, usually. But it is
- very code-efficient in this case. */
+exit_sysfs_remove:
+ sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&new_client->dev.kobj, &pcf8591_attr_group);
+exit_detach:
+ i2c_detach_client(new_client);
exit_kfree:
kfree(data);
exit:
@@ -239,6 +268,9 @@ static int pcf8591_detach_client(struct i2c_client *client)
{
int err;
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group_opt);
+ sysfs_remove_group(&client->dev.kobj, &pcf8591_attr_group);
+
if ((err = i2c_detach_client(client)))
return err;
diff --git a/drivers/i2c/chips/tps65010.c b/drivers/i2c/chips/tps65010.c
index 0be6fd6a267..6a757821717 100644
--- a/drivers/i2c/chips/tps65010.c
+++ b/drivers/i2c/chips/tps65010.c
@@ -305,7 +305,7 @@ static int dbg_show(struct seq_file *s, void *_)
static int dbg_tps_open(struct inode *inode, struct file *file)
{
- return single_open(file, dbg_show, inode->u.generic_ip);
+ return single_open(file, dbg_show, inode->i_private);
}
static struct file_operations debug_fops = {
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index 9cb277d6aa4..7ca81f42d14 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -183,15 +183,21 @@ int i2c_add_adapter(struct i2c_adapter *adap)
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
- device_register(&adap->dev);
- device_create_file(&adap->dev, &dev_attr_name);
+ res = device_register(&adap->dev);
+ if (res)
+ goto out_list;
+ res = device_create_file(&adap->dev, &dev_attr_name);
+ if (res)
+ goto out_unregister;
/* Add this adapter to the i2c_adapter class */
memset(&adap->class_dev, 0x00, sizeof(struct class_device));
adap->class_dev.dev = &adap->dev;
adap->class_dev.class = &i2c_adapter_class;
strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
- class_device_register(&adap->class_dev);
+ res = class_device_register(&adap->class_dev);
+ if (res)
+ goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
@@ -206,6 +212,17 @@ int i2c_add_adapter(struct i2c_adapter *adap)
out_unlock:
mutex_unlock(&core_lists);
return res;
+
+out_remove_name:
+ device_remove_file(&adap->dev, &dev_attr_name);
+out_unregister:
+ init_completion(&adap->dev_released); /* Needed? */
+ device_unregister(&adap->dev);
+ wait_for_completion(&adap->dev_released);
+out_list:
+ list_del(&adap->list);
+ idr_remove(&i2c_adapter_idr, adap->nr);
+ goto out_unlock;
}
@@ -394,23 +411,15 @@ int i2c_check_addr(struct i2c_adapter *adapter, int addr)
int i2c_attach_client(struct i2c_client *client)
{
struct i2c_adapter *adapter = client->adapter;
+ int res = 0;
mutex_lock(&adapter->clist_lock);
if (__i2c_check_addr(client->adapter, client->addr)) {
- mutex_unlock(&adapter->clist_lock);
- return -EBUSY;
+ res = -EBUSY;
+ goto out_unlock;
}
list_add_tail(&client->list,&adapter->clients);
- mutex_unlock(&adapter->clist_lock);
- if (adapter->client_register) {
- if (adapter->client_register(client)) {
- dev_dbg(&adapter->dev, "client_register "
- "failed for client [%s] at 0x%02x\n",
- client->name, client->addr);
- }
- }
-
client->usage_count = 0;
client->dev.parent = &client->adapter->dev;
@@ -422,10 +431,35 @@ int i2c_attach_client(struct i2c_client *client)
"%d-%04x", i2c_adapter_id(adapter), client->addr);
dev_dbg(&adapter->dev, "client [%s] registered with bus id %s\n",
client->name, client->dev.bus_id);
- device_register(&client->dev);
- device_create_file(&client->dev, &dev_attr_client_name);
-
+ res = device_register(&client->dev);
+ if (res)
+ goto out_list;
+ res = device_create_file(&client->dev, &dev_attr_client_name);
+ if (res)
+ goto out_unregister;
+ mutex_unlock(&adapter->clist_lock);
+
+ if (adapter->client_register) {
+ if (adapter->client_register(client)) {
+ dev_dbg(&adapter->dev, "client_register "
+ "failed for client [%s] at 0x%02x\n",
+ client->name, client->addr);
+ }
+ }
+
return 0;
+
+out_unregister:
+ init_completion(&client->released); /* Needed? */
+ device_unregister(&client->dev);
+ wait_for_completion(&client->released);
+out_list:
+ list_del(&client->list);
+ dev_err(&adapter->dev, "Failed to attach i2c client %s at 0x%02x "
+ "(%d)\n", client->name, client->addr, res);
+out_unlock:
+ mutex_unlock(&adapter->clist_lock);
+ return res;
}
@@ -674,11 +708,16 @@ static int i2c_probe_address(struct i2c_adapter *adapter, int addr, int kind,
/* Finally call the custom detection function */
err = found_proc(adapter, addr, kind);
-
/* -ENODEV can be returned if there is a chip at the given address
but it isn't supported by this chip driver. We catch it here as
this isn't an error. */
- return (err == -ENODEV) ? 0 : err;
+ if (err == -ENODEV)
+ err = 0;
+
+ if (err)
+ dev_warn(&adapter->dev, "Client creation failed at 0x%x (%d)\n",
+ addr, err);
+ return err;
}
int i2c_probe(struct i2c_adapter *adapter,
@@ -868,7 +907,7 @@ s32 i2c_smbus_read_byte(struct i2c_client *client)
I2C_SMBUS_READ,0,I2C_SMBUS_BYTE, &data))
return -1;
else
- return 0x0FF & data.byte;
+ return data.byte;
}
s32 i2c_smbus_write_byte(struct i2c_client *client, u8 value)
@@ -884,7 +923,7 @@ s32 i2c_smbus_read_byte_data(struct i2c_client *client, u8 command)
I2C_SMBUS_READ,command, I2C_SMBUS_BYTE_DATA,&data))
return -1;
else
- return 0x0FF & data.byte;
+ return data.byte;
}
s32 i2c_smbus_write_byte_data(struct i2c_client *client, u8 command, u8 value)
@@ -903,7 +942,7 @@ s32 i2c_smbus_read_word_data(struct i2c_client *client, u8 command)
I2C_SMBUS_READ,command, I2C_SMBUS_WORD_DATA, &data))
return -1;
else
- return 0x0FFFF & data.word;
+ return data.word;
}
s32 i2c_smbus_write_word_data(struct i2c_client *client, u8 command, u16 value)
@@ -1006,7 +1045,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
else {
msg[0].len=3;
msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = (data->word >> 8) & 0xff;
+ msgbuf0[2] = data->word >> 8;
}
break;
case I2C_SMBUS_PROC_CALL:
@@ -1015,7 +1054,7 @@ static s32 i2c_smbus_xfer_emulated(struct i2c_adapter * adapter, u16 addr,
msg[0].len = 3;
msg[1].len = 2;
msgbuf0[1] = data->word & 0xff;
- msgbuf0[2] = (data->word >> 8) & 0xff;
+ msgbuf0[2] = data->word >> 8;
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c
index 58ccddd5c23..3f869033ed7 100644
--- a/drivers/i2c/i2c-dev.c
+++ b/drivers/i2c/i2c-dev.c
@@ -32,43 +32,35 @@
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/init.h>
+#include <linux/list.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
-#include <linux/platform_device.h>
#include <asm/uaccess.h>
-static struct i2c_client i2cdev_client_template;
+static struct i2c_driver i2cdev_driver;
struct i2c_dev {
- int minor;
+ struct list_head list;
struct i2c_adapter *adap;
struct class_device *class_dev;
};
-#define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev)
#define I2C_MINORS 256
-static struct i2c_dev *i2c_dev_array[I2C_MINORS];
-static DEFINE_SPINLOCK(i2c_dev_array_lock);
+static LIST_HEAD(i2c_dev_list);
+static DEFINE_SPINLOCK(i2c_dev_list_lock);
static struct i2c_dev *i2c_dev_get_by_minor(unsigned index)
{
struct i2c_dev *i2c_dev;
- spin_lock(&i2c_dev_array_lock);
- i2c_dev = i2c_dev_array[index];
- spin_unlock(&i2c_dev_array_lock);
- return i2c_dev;
-}
-
-static struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap)
-{
- struct i2c_dev *i2c_dev = NULL;
-
- spin_lock(&i2c_dev_array_lock);
- if ((i2c_dev_array[adap->nr]) &&
- (i2c_dev_array[adap->nr]->adap == adap))
- i2c_dev = i2c_dev_array[adap->nr];
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_for_each_entry(i2c_dev, &i2c_dev_list, list) {
+ if (i2c_dev->adap->nr == index)
+ goto found;
+ }
+ i2c_dev = NULL;
+found:
+ spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
}
@@ -76,30 +68,28 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
+ if (adap->nr >= I2C_MINORS) {
+ printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n",
+ adap->nr);
+ return ERR_PTR(-ENODEV);
+ }
+
i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL);
if (!i2c_dev)
return ERR_PTR(-ENOMEM);
+ i2c_dev->adap = adap;
- spin_lock(&i2c_dev_array_lock);
- if (i2c_dev_array[adap->nr]) {
- spin_unlock(&i2c_dev_array_lock);
- dev_err(&adap->dev, "i2c-dev already has a device assigned to this adapter\n");
- goto error;
- }
- i2c_dev->minor = adap->nr;
- i2c_dev_array[adap->nr] = i2c_dev;
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_add_tail(&i2c_dev->list, &i2c_dev_list);
+ spin_unlock(&i2c_dev_list_lock);
return i2c_dev;
-error:
- kfree(i2c_dev);
- return ERR_PTR(-ENODEV);
}
static void return_i2c_dev(struct i2c_dev *i2c_dev)
{
- spin_lock(&i2c_dev_array_lock);
- i2c_dev_array[i2c_dev->minor] = NULL;
- spin_unlock(&i2c_dev_array_lock);
+ spin_lock(&i2c_dev_list_lock);
+ list_del(&i2c_dev->list);
+ spin_unlock(&i2c_dev_list_lock);
}
static ssize_t show_adapter_name(struct class_device *class_dev, char *buf)
@@ -375,12 +365,13 @@ static int i2cdev_open(struct inode *inode, struct file *file)
if (!adap)
return -ENODEV;
- client = kmalloc(sizeof(*client), GFP_KERNEL);
+ client = kzalloc(sizeof(*client), GFP_KERNEL);
if (!client) {
i2c_put_adapter(adap);
return -ENOMEM;
}
- memcpy(client, &i2cdev_client_template, sizeof(*client));
+ snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);
+ client->driver = &i2cdev_driver;
/* registered with adapter, passed as client to user */
client->adapter = adap;
@@ -415,41 +406,47 @@ static struct class *i2c_dev_class;
static int i2cdev_attach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
- struct device *dev;
+ int res;
i2c_dev = get_free_i2c_dev(adap);
if (IS_ERR(i2c_dev))
return PTR_ERR(i2c_dev);
- pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
- adap->name, i2c_dev->minor);
-
/* register this i2c device with the driver core */
- i2c_dev->adap = adap;
- dev = &adap->dev;
i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL,
- MKDEV(I2C_MAJOR, i2c_dev->minor),
- dev, "i2c-%d", i2c_dev->minor);
- if (!i2c_dev->class_dev)
+ MKDEV(I2C_MAJOR, adap->nr),
+ &adap->dev, "i2c-%d",
+ adap->nr);
+ if (!i2c_dev->class_dev) {
+ res = -ENODEV;
goto error;
- class_device_create_file(i2c_dev->class_dev, &class_device_attr_name);
+ }
+ res = class_device_create_file(i2c_dev->class_dev, &class_device_attr_name);
+ if (res)
+ goto error_destroy;
+
+ pr_debug("i2c-dev: adapter [%s] registered as minor %d\n",
+ adap->name, adap->nr);
return 0;
+error_destroy:
+ class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
error:
return_i2c_dev(i2c_dev);
kfree(i2c_dev);
- return -ENODEV;
+ return res;
}
static int i2cdev_detach_adapter(struct i2c_adapter *adap)
{
struct i2c_dev *i2c_dev;
- i2c_dev = i2c_dev_get_by_adapter(adap);
- if (!i2c_dev)
- return -ENODEV;
+ i2c_dev = i2c_dev_get_by_minor(adap->nr);
+ if (!i2c_dev) /* attach_adapter must have failed */
+ return 0;
+ class_device_remove_file(i2c_dev->class_dev, &class_device_attr_name);
return_i2c_dev(i2c_dev);
- class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, i2c_dev->minor));
+ class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr));
kfree(i2c_dev);
pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name);
@@ -471,12 +468,6 @@ static struct i2c_driver i2cdev_driver = {
.detach_client = i2cdev_detach_client,
};
-static struct i2c_client i2cdev_client_template = {
- .name = "I2C /dev entry",
- .addr = -1,
- .driver = &i2cdev_driver,
-};
-
static int __init i2c_dev_init(void)
{
int res;
diff --git a/drivers/ide/Kconfig b/drivers/ide/Kconfig
index b6fb167e20f..abcabb29559 100644
--- a/drivers/ide/Kconfig
+++ b/drivers/ide/Kconfig
@@ -4,6 +4,8 @@
# Andre Hedrick <andre@linux-ide.org>
#
+if BLOCK
+
menu "ATA/ATAPI/MFM/RLL support"
config IDE
@@ -54,7 +56,7 @@ if IDE
config IDE_MAX_HWIFS
int "Max IDE interfaces"
- depends on ALPHA || SUPERH || IA64
+ depends on ALPHA || SUPERH || IA64 || EMBEDDED
default 4
help
This is the maximum number of IDE hardware interfaces that will
@@ -592,6 +594,12 @@ config BLK_DEV_HPT366
ide-probe at boot. It is reported to support DVD II drives, by the
manufacturer.
+config BLK_DEV_JMICRON
+ tristate "JMicron JMB36x support"
+ help
+ Basic support for the JMicron ATA controllers. For full support
+ use the libata drivers.
+
config BLK_DEV_SC1200
tristate "National SCx200 chipset support"
help
@@ -1082,3 +1090,5 @@ config BLK_DEV_HD
endif
endmenu
+
+endif
diff --git a/drivers/ide/ide-cd.c b/drivers/ide/ide-cd.c
index 654d4cd0984..69bbb6206a0 100644
--- a/drivers/ide/ide-cd.c
+++ b/drivers/ide/ide-cd.c
@@ -372,7 +372,7 @@ static int cdrom_log_sense(ide_drive_t *drive, struct request *rq,
{
int log = 0;
- if (!sense || !rq || (rq->flags & REQ_QUIET))
+ if (!sense || !rq || (rq->cmd_flags & REQ_QUIET))
return 0;
switch (sense->sense_key) {
@@ -597,7 +597,7 @@ static void cdrom_prepare_request(ide_drive_t *drive, struct request *rq)
struct cdrom_info *cd = drive->driver_data;
ide_init_drive_cmd(rq);
- rq->flags = REQ_PC;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->rq_disk = cd->disk;
}
@@ -617,7 +617,7 @@ static void cdrom_queue_request_sense(ide_drive_t *drive, void *sense,
rq->cmd[0] = GPCMD_REQUEST_SENSE;
rq->cmd[4] = rq->data_len = 18;
- rq->flags = REQ_SENSE;
+ rq->cmd_type = REQ_TYPE_SENSE;
/* NOTE! Save the failed command in "rq->buffer" */
rq->buffer = (void *) failed_command;
@@ -630,10 +630,10 @@ static void cdrom_end_request (ide_drive_t *drive, int uptodate)
struct request *rq = HWGROUP(drive)->rq;
int nsectors = rq->hard_cur_sectors;
- if ((rq->flags & REQ_SENSE) && uptodate) {
+ if (blk_sense_request(rq) && uptodate) {
/*
- * For REQ_SENSE, "rq->buffer" points to the original failed
- * request
+ * For REQ_TYPE_SENSE, "rq->buffer" points to the original
+ * failed request
*/
struct request *failed = (struct request *) rq->buffer;
struct cdrom_info *info = drive->driver_data;
@@ -706,17 +706,17 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
return 1;
}
- if (rq->flags & REQ_SENSE) {
+ if (blk_sense_request(rq)) {
/* We got an error trying to get sense info
from the drive (probably while trying
to recover from a former error). Just give up. */
- rq->flags |= REQ_FAILED;
+ rq->cmd_flags |= REQ_FAILED;
cdrom_end_request(drive, 0);
ide_error(drive, "request sense failure", stat);
return 1;
- } else if (rq->flags & (REQ_PC | REQ_BLOCK_PC)) {
+ } else if (blk_pc_request(rq)) {
/* All other functions, except for READ. */
unsigned long flags;
@@ -724,7 +724,7 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
* if we have an error, pass back CHECK_CONDITION as the
* scsi status byte
*/
- if ((rq->flags & REQ_BLOCK_PC) && !rq->errors)
+ if (!rq->errors)
rq->errors = SAM_STAT_CHECK_CONDITION;
/* Check for tray open. */
@@ -735,12 +735,12 @@ static int cdrom_decode_status(ide_drive_t *drive, int good_stat, int *stat_ret)
cdrom_saw_media_change (drive);
/*printk("%s: media changed\n",drive->name);*/
return 0;
- } else if (!(rq->flags & REQ_QUIET)) {
+ } else if (!(rq->cmd_flags & REQ_QUIET)) {
/* Otherwise, print an error. */
ide_dump_status(drive, "packet command error", stat);
}
- rq->flags |= REQ_FAILED;
+ rq->cmd_flags |= REQ_FAILED;
/*
* instead of playing games with moving completions around,
@@ -881,7 +881,7 @@ static int cdrom_timer_expiry(ide_drive_t *drive)
wait = ATAPI_WAIT_PC;
break;
default:
- if (!(rq->flags & REQ_QUIET))
+ if (!(rq->cmd_flags & REQ_QUIET))
printk(KERN_INFO "ide-cd: cmd 0x%x timed out\n", rq->cmd[0]);
wait = 0;
break;
@@ -1124,7 +1124,7 @@ static ide_startstop_t cdrom_read_intr (ide_drive_t *drive)
if (rq->current_nr_sectors > 0) {
printk (KERN_ERR "%s: cdrom_read_intr: data underrun (%d blocks)\n",
drive->name, rq->current_nr_sectors);
- rq->flags |= REQ_FAILED;
+ rq->cmd_flags |= REQ_FAILED;
cdrom_end_request(drive, 0);
} else
cdrom_end_request(drive, 1);
@@ -1456,7 +1456,7 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
printk ("%s: cdrom_pc_intr: data underrun %d\n",
drive->name, pc->buflen);
*/
- rq->flags |= REQ_FAILED;
+ rq->cmd_flags |= REQ_FAILED;
cdrom_end_request(drive, 0);
}
return ide_stopped;
@@ -1509,7 +1509,7 @@ static ide_startstop_t cdrom_pc_intr (ide_drive_t *drive)
rq->data += thislen;
rq->data_len -= thislen;
- if (rq->flags & REQ_SENSE)
+ if (blk_sense_request(rq))
rq->sense_len += thislen;
} else {
confused:
@@ -1517,7 +1517,7 @@ confused:
"appears confused (ireason = 0x%02x). "
"Trying to recover by ending request.\n",
drive->name, ireason);
- rq->flags |= REQ_FAILED;
+ rq->cmd_flags |= REQ_FAILED;
cdrom_end_request(drive, 0);
return ide_stopped;
}
@@ -1546,7 +1546,7 @@ static ide_startstop_t cdrom_do_packet_command (ide_drive_t *drive)
struct cdrom_info *info = drive->driver_data;
info->dma = 0;
- rq->flags &= ~REQ_FAILED;
+ rq->cmd_flags &= ~REQ_FAILED;
len = rq->data_len;
/* Start sending the command to the drive. */
@@ -1558,7 +1558,7 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
{
struct request_sense sense;
int retries = 10;
- unsigned int flags = rq->flags;
+ unsigned int flags = rq->cmd_flags;
if (rq->sense == NULL)
rq->sense = &sense;
@@ -1567,14 +1567,14 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
do {
int error;
unsigned long time = jiffies;
- rq->flags = flags;
+ rq->cmd_flags = flags;
error = ide_do_drive_cmd(drive, rq, ide_wait);
time = jiffies - time;
/* FIXME: we should probably abort/retry or something
* in case of failure */
- if (rq->flags & REQ_FAILED) {
+ if (rq->cmd_flags & REQ_FAILED) {
/* The request failed. Retry if it was due to a unit
attention status
(usually means media was changed). */
@@ -1596,10 +1596,10 @@ static int cdrom_queue_packet_command(ide_drive_t *drive, struct request *rq)
}
/* End of retry loop. */
- } while ((rq->flags & REQ_FAILED) && retries >= 0);
+ } while ((rq->cmd_flags & REQ_FAILED) && retries >= 0);
/* Return an error if the command failed. */
- return (rq->flags & REQ_FAILED) ? -EIO : 0;
+ return (rq->cmd_flags & REQ_FAILED) ? -EIO : 0;
}
/*
@@ -1963,7 +1963,7 @@ static ide_startstop_t cdrom_do_block_pc(ide_drive_t *drive, struct request *rq)
{
struct cdrom_info *info = drive->driver_data;
- rq->flags |= REQ_QUIET;
+ rq->cmd_flags |= REQ_QUIET;
info->dma = 0;
@@ -2023,11 +2023,11 @@ ide_do_rw_cdrom (ide_drive_t *drive, struct request *rq, sector_t block)
}
info->last_block = block;
return action;
- } else if (rq->flags & (REQ_PC | REQ_SENSE)) {
+ } else if (rq->cmd_type == REQ_TYPE_SENSE) {
return cdrom_do_packet_command(drive);
- } else if (rq->flags & REQ_BLOCK_PC) {
+ } else if (blk_pc_request(rq)) {
return cdrom_do_block_pc(drive, rq);
- } else if (rq->flags & REQ_SPECIAL) {
+ } else if (blk_special_request(rq)) {
/*
* right now this can only be a reset...
*/
@@ -2105,7 +2105,7 @@ static int cdrom_check_status(ide_drive_t *drive, struct request_sense *sense)
req.sense = sense;
req.cmd[0] = GPCMD_TEST_UNIT_READY;
- req.flags |= REQ_QUIET;
+ req.cmd_flags |= REQ_QUIET;
#if ! STANDARD_ATAPI
/* the Sanyo 3 CD changer uses byte 7 of TEST_UNIT_READY to
@@ -2207,7 +2207,7 @@ static int cdrom_read_capacity(ide_drive_t *drive, unsigned long *capacity,
req.cmd[0] = GPCMD_READ_CDVD_CAPACITY;
req.data = (char *)&capbuf;
req.data_len = sizeof(capbuf);
- req.flags |= REQ_QUIET;
+ req.cmd_flags |= REQ_QUIET;
stat = cdrom_queue_packet_command(drive, &req);
if (stat == 0) {
@@ -2230,7 +2230,7 @@ static int cdrom_read_tocentry(ide_drive_t *drive, int trackno, int msf_flag,
req.sense = sense;
req.data = buf;
req.data_len = buflen;
- req.flags |= REQ_QUIET;
+ req.cmd_flags |= REQ_QUIET;
req.cmd[0] = GPCMD_READ_TOC_PMA_ATIP;
req.cmd[6] = trackno;
req.cmd[7] = (buflen >> 8);
@@ -2531,7 +2531,7 @@ static int ide_cdrom_packet(struct cdrom_device_info *cdi,
req.timeout = cgc->timeout;
if (cgc->quiet)
- req.flags |= REQ_QUIET;
+ req.cmd_flags |= REQ_QUIET;
req.sense = cgc->sense;
cgc->stat = cdrom_queue_packet_command(drive, &req);
@@ -2629,7 +2629,8 @@ int ide_cdrom_reset (struct cdrom_device_info *cdi)
int ret;
cdrom_prepare_request(drive, &req);
- req.flags = REQ_SPECIAL | REQ_QUIET;
+ req.cmd_type = REQ_TYPE_SPECIAL;
+ req.cmd_flags = REQ_QUIET;
ret = ide_do_drive_cmd(drive, &req, ide_wait);
/*
@@ -3116,9 +3117,9 @@ static int ide_cdrom_prep_pc(struct request *rq)
static int ide_cdrom_prep_fn(request_queue_t *q, struct request *rq)
{
- if (rq->flags & REQ_CMD)
+ if (blk_fs_request(rq))
return ide_cdrom_prep_fs(q, rq);
- else if (rq->flags & REQ_BLOCK_PC)
+ else if (blk_pc_request(rq))
return ide_cdrom_prep_pc(rq);
return 0;
diff --git a/drivers/ide/ide-disk.c b/drivers/ide/ide-disk.c
index 7cf3eb02352..0a05a377d66 100644
--- a/drivers/ide/ide-disk.c
+++ b/drivers/ide/ide-disk.c
@@ -699,7 +699,8 @@ static void idedisk_prepare_flush(request_queue_t *q, struct request *rq)
rq->cmd[0] = WIN_FLUSH_CACHE;
- rq->flags |= REQ_DRIVE_TASK;
+ rq->cmd_type = REQ_TYPE_ATA_TASK;
+ rq->cmd_flags |= REQ_SOFTBARRIER;
rq->buffer = rq->cmd;
}
@@ -740,7 +741,7 @@ static int set_multcount(ide_drive_t *drive, int arg)
if (drive->special.b.set_multmode)
return -EBUSY;
ide_init_drive_cmd (&rq);
- rq.flags = REQ_DRIVE_CMD;
+ rq.cmd_type = REQ_TYPE_ATA_CMD;
drive->mult_req = arg;
drive->special.b.set_multmode = 1;
(void) ide_do_drive_cmd (drive, &rq, ide_wait);
diff --git a/drivers/ide/ide-dma.c b/drivers/ide/ide-dma.c
index 7c3a13e1cf6..56efed6742d 100644
--- a/drivers/ide/ide-dma.c
+++ b/drivers/ide/ide-dma.c
@@ -145,14 +145,12 @@ int ide_in_drive_list(struct hd_driveid *id, const struct drive_list_entry *driv
{
for ( ; drive_table->id_model ; drive_table++)
if ((!strcmp(drive_table->id_model, id->model)) &&
- ((strstr(drive_table->id_firmware, id->fw_rev)) ||
+ ((strstr(id->fw_rev, drive_table->id_firmware)) ||
(!strcmp(drive_table->id_firmware, "ALL"))))
return 1;
return 0;
}
-EXPORT_SYMBOL_GPL(ide_in_drive_list);
-
/**
* ide_dma_intr - IDE DMA interrupt handler
* @drive: the drive the interrupt is for
@@ -205,7 +203,7 @@ int ide_build_sglist(ide_drive_t *drive, struct request *rq)
ide_hwif_t *hwif = HWIF(drive);
struct scatterlist *sg = hwif->sg_table;
- BUG_ON((rq->flags & REQ_DRIVE_TASKFILE) && rq->nr_sectors > 256);
+ BUG_ON((rq->cmd_type == REQ_TYPE_ATA_TASKFILE) && rq->nr_sectors > 256);
ide_map_sg(drive, rq);
@@ -798,26 +796,23 @@ static int ide_release_dma_engine(ide_hwif_t *hwif)
static int ide_release_iomio_dma(ide_hwif_t *hwif)
{
- if ((hwif->dma_extra) && (hwif->channel == 0))
- release_region((hwif->dma_base + 16), hwif->dma_extra);
release_region(hwif->dma_base, 8);
- if (hwif->dma_base2)
- release_region(hwif->dma_base, 8);
+ if (hwif->extra_ports)
+ release_region(hwif->extra_base, hwif->extra_ports);
return 1;
}
/*
* Needed for allowing full modular support of ide-driver
*/
-int ide_release_dma (ide_hwif_t *hwif)
+int ide_release_dma(ide_hwif_t *hwif)
{
+ ide_release_dma_engine(hwif);
+
if (hwif->mmio == 2)
return 1;
- if (hwif->chipset == ide_etrax100)
- return 1;
-
- ide_release_dma_engine(hwif);
- return ide_release_iomio_dma(hwif);
+ else
+ return ide_release_iomio_dma(hwif);
}
static int ide_allocate_dma_engine(ide_hwif_t *hwif)
@@ -829,10 +824,9 @@ static int ide_allocate_dma_engine(ide_hwif_t *hwif)
if (hwif->dmatable_cpu)
return 0;
- printk(KERN_ERR "%s: -- Error, unable to allocate%s DMA table(s).\n",
- hwif->cds->name, !hwif->dmatable_cpu ? " CPU" : "");
+ printk(KERN_ERR "%s: -- Error, unable to allocate DMA table.\n",
+ hwif->cds->name);
- ide_release_dma_engine(hwif);
return 1;
}
@@ -840,9 +834,7 @@ static int ide_mapped_mmio_dma(ide_hwif_t *hwif, unsigned long base, unsigned in
{
printk(KERN_INFO " %s: MMIO-DMA ", hwif->name);
- hwif->dma_base = base;
- if (hwif->cds->extra && hwif->channel == 0)
- hwif->dma_extra = hwif->cds->extra;
+ hwif->dma_base = base;
if(hwif->mate)
hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base : base;
@@ -854,29 +846,33 @@ static int ide_mapped_mmio_dma(ide_hwif_t *hwif, unsigned long base, unsigned in
static int ide_iomio_dma(ide_hwif_t *hwif, unsigned long base, unsigned int ports)
{
printk(KERN_INFO " %s: BM-DMA at 0x%04lx-0x%04lx",
- hwif->name, base, base + ports - 1);
+ hwif->name, base, base + ports - 1);
+
if (!request_region(base, ports, hwif->name)) {
printk(" -- Error, ports in use.\n");
return 1;
}
+
hwif->dma_base = base;
- if ((hwif->cds->extra) && (hwif->channel == 0)) {
- request_region(base+16, hwif->cds->extra, hwif->cds->name);
- hwif->dma_extra = hwif->cds->extra;
+
+ if (hwif->cds->extra) {
+ hwif->extra_base = base + (hwif->channel ? 8 : 16);
+
+ if (!hwif->mate || !hwif->mate->extra_ports) {
+ if (!request_region(hwif->extra_base,
+ hwif->cds->extra, hwif->cds->name)) {
+ printk(" -- Error, extra ports in use.\n");
+ release_region(base, ports);
+ return 1;
+ }
+ hwif->extra_ports = hwif->cds->extra;
+ }
}
-
+
if(hwif->mate)
- hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base : base;
+ hwif->dma_master = (hwif->channel) ? hwif->mate->dma_base:base;
else
hwif->dma_master = base;
- if (hwif->dma_base2) {
- if (!request_region(hwif->dma_base2, ports, hwif->name))
- {
- printk(" -- Error, secondary ports in use.\n");
- release_region(base, ports);
- return 1;
- }
- }
return 0;
}
diff --git a/drivers/ide/ide-floppy.c b/drivers/ide/ide-floppy.c
index adbe9f76a50..8ccee9c769f 100644
--- a/drivers/ide/ide-floppy.c
+++ b/drivers/ide/ide-floppy.c
@@ -588,7 +588,7 @@ static int idefloppy_do_end_request(ide_drive_t *drive, int uptodate, int nsecs)
/* Why does this happen? */
if (!rq)
return 0;
- if (!(rq->flags & REQ_SPECIAL)) { //if (!IDEFLOPPY_RQ_CMD (rq->cmd)) {
+ if (!blk_special_request(rq)) {
/* our real local end request function */
ide_end_request(drive, uptodate, nsecs);
return 0;
@@ -689,7 +689,7 @@ static void idefloppy_queue_pc_head (ide_drive_t *drive,idefloppy_pc_t *pc,struc
ide_init_drive_cmd(rq);
rq->buffer = (char *) pc;
- rq->flags = REQ_SPECIAL; //rq->cmd = IDEFLOPPY_PC_RQ;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
rq->rq_disk = floppy->disk;
(void) ide_do_drive_cmd(drive, rq, ide_preempt);
}
@@ -1250,7 +1250,7 @@ static void idefloppy_create_rw_cmd (idefloppy_floppy_t *floppy, idefloppy_pc_t
pc->callback = &idefloppy_rw_callback;
pc->rq = rq;
pc->b_count = cmd == READ ? 0 : rq->bio->bi_size;
- if (rq->flags & REQ_RW)
+ if (rq->cmd_flags & REQ_RW)
set_bit(PC_WRITING, &pc->flags);
pc->buffer = NULL;
pc->request_transfer = pc->buffer_size = blocks * floppy->block_size;
@@ -1281,8 +1281,7 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request
idefloppy_pc_t *pc;
unsigned long block = (unsigned long)block_s;
- debug_log(KERN_INFO "rq_status: %d, dev: %s, flags: %lx, errors: %d\n",
- rq->rq_status,
+ debug_log(KERN_INFO "dev: %s, flags: %lx, errors: %d\n",
rq->rq_disk ? rq->rq_disk->disk_name : "?",
rq->flags, rq->errors);
debug_log(KERN_INFO "sector: %ld, nr_sectors: %ld, "
@@ -1303,7 +1302,7 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request
idefloppy_do_end_request(drive, 0, 0);
return ide_stopped;
}
- if (rq->flags & REQ_CMD) {
+ if (blk_fs_request(rq)) {
if (((long)rq->sector % floppy->bs_factor) ||
(rq->nr_sectors % floppy->bs_factor)) {
printk("%s: unsupported r/w request size\n",
@@ -1313,9 +1312,9 @@ static ide_startstop_t idefloppy_do_request (ide_drive_t *drive, struct request
}
pc = idefloppy_next_pc_storage(drive);
idefloppy_create_rw_cmd(floppy, pc, rq, block);
- } else if (rq->flags & REQ_SPECIAL) {
+ } else if (blk_special_request(rq)) {
pc = (idefloppy_pc_t *) rq->buffer;
- } else if (rq->flags & REQ_BLOCK_PC) {
+ } else if (blk_pc_request(rq)) {
pc = idefloppy_next_pc_storage(drive);
if (idefloppy_blockpc_cmd(floppy, pc, rq)) {
idefloppy_do_end_request(drive, 0, 0);
@@ -1343,7 +1342,7 @@ static int idefloppy_queue_pc_tail (ide_drive_t *drive,idefloppy_pc_t *pc)
ide_init_drive_cmd (&rq);
rq.buffer = (char *) pc;
- rq.flags = REQ_SPECIAL; // rq.cmd = IDEFLOPPY_PC_RQ;
+ rq.cmd_type = REQ_TYPE_SPECIAL;
rq.rq_disk = floppy->disk;
return ide_do_drive_cmd(drive, &rq, ide_wait);
diff --git a/drivers/ide/ide-io.c b/drivers/ide/ide-io.c
index fb6795236e7..ba6039b55b4 100644
--- a/drivers/ide/ide-io.c
+++ b/drivers/ide/ide-io.c
@@ -59,8 +59,6 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
{
int ret = 1;
- BUG_ON(!(rq->flags & REQ_STARTED));
-
/*
* if failfast is set on a request, override number of sectors and
* complete the whole request right now
@@ -82,7 +80,8 @@ static int __ide_end_request(ide_drive_t *drive, struct request *rq,
if (!end_that_request_first(rq, uptodate, nr_sectors)) {
add_disk_randomness(rq->rq_disk);
- blkdev_dequeue_request(rq);
+ if (!list_empty(&rq->queuelist))
+ blkdev_dequeue_request(rq);
HWGROUP(drive)->rq = NULL;
end_that_request_last(rq, uptodate);
ret = 0;
@@ -135,13 +134,14 @@ enum {
ide_pm_flush_cache = ide_pm_state_start_suspend,
idedisk_pm_standby,
- idedisk_pm_idle = ide_pm_state_start_resume,
+ idedisk_pm_restore_pio = ide_pm_state_start_resume,
+ idedisk_pm_idle,
ide_pm_restore_dma,
};
static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 stat, u8 error)
{
- struct request_pm_state *pm = rq->end_io_data;
+ struct request_pm_state *pm = rq->data;
if (drive->media != ide_disk)
return;
@@ -156,7 +156,10 @@ static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 s
case idedisk_pm_standby: /* Suspend step 2 (standby) complete */
pm->pm_step = ide_pm_state_completed;
break;
- case idedisk_pm_idle: /* Resume step 1 (idle) complete */
+ case idedisk_pm_restore_pio: /* Resume step 1 complete */
+ pm->pm_step = idedisk_pm_idle;
+ break;
+ case idedisk_pm_idle: /* Resume step 2 (idle) complete */
pm->pm_step = ide_pm_restore_dma;
break;
}
@@ -164,14 +167,17 @@ static void ide_complete_power_step(ide_drive_t *drive, struct request *rq, u8 s
static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->end_io_data;
+ struct request_pm_state *pm = rq->data;
ide_task_t *args = rq->special;
memset(args, 0, sizeof(*args));
if (drive->media != ide_disk) {
- /* skip idedisk_pm_idle for ATAPI devices */
- if (pm->pm_step == idedisk_pm_idle)
+ /*
+ * skip idedisk_pm_restore_pio and idedisk_pm_idle for ATAPI
+ * devices
+ */
+ if (pm->pm_step == idedisk_pm_restore_pio)
pm->pm_step = ide_pm_restore_dma;
}
@@ -198,13 +204,19 @@ static ide_startstop_t ide_start_power_step(ide_drive_t *drive, struct request *
args->handler = &task_no_data_intr;
return do_rw_taskfile(drive, args);
- case idedisk_pm_idle: /* Resume step 1 (idle) */
+ case idedisk_pm_restore_pio: /* Resume step 1 (restore PIO) */
+ if (drive->hwif->tuneproc != NULL)
+ drive->hwif->tuneproc(drive, 255);
+ ide_complete_power_step(drive, rq, 0, 0);
+ return ide_stopped;
+
+ case idedisk_pm_idle: /* Resume step 2 (idle) */
args->tfRegister[IDE_COMMAND_OFFSET] = WIN_IDLEIMMEDIATE;
args->command_type = IDE_DRIVE_TASK_NO_DATA;
args->handler = task_no_data_intr;
return do_rw_taskfile(drive, args);
- case ide_pm_restore_dma: /* Resume step 2 (restore DMA) */
+ case ide_pm_restore_dma: /* Resume step 3 (restore DMA) */
/*
* Right now, all we do is call hwif->ide_dma_check(drive),
* we could be smarter and check for current xfer_speed
@@ -244,7 +256,7 @@ int ide_end_dequeued_request(ide_drive_t *drive, struct request *rq,
spin_lock_irqsave(&ide_lock, flags);
- BUG_ON(!(rq->flags & REQ_STARTED));
+ BUG_ON(!blk_rq_started(rq));
/*
* if failfast is set on a request, override number of sectors and
@@ -366,7 +378,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
rq = HWGROUP(drive)->rq;
spin_unlock_irqrestore(&ide_lock, flags);
- if (rq->flags & REQ_DRIVE_CMD) {
+ if (rq->cmd_type == REQ_TYPE_ATA_CMD) {
u8 *args = (u8 *) rq->buffer;
if (rq->errors == 0)
rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
@@ -376,7 +388,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
args[1] = err;
args[2] = hwif->INB(IDE_NSECTOR_REG);
}
- } else if (rq->flags & REQ_DRIVE_TASK) {
+ } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) {
u8 *args = (u8 *) rq->buffer;
if (rq->errors == 0)
rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
@@ -390,7 +402,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
args[5] = hwif->INB(IDE_HCYL_REG);
args[6] = hwif->INB(IDE_SELECT_REG);
}
- } else if (rq->flags & REQ_DRIVE_TASKFILE) {
+ } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *args = (ide_task_t *) rq->special;
if (rq->errors == 0)
rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
@@ -421,7 +433,7 @@ void ide_end_drive_cmd (ide_drive_t *drive, u8 stat, u8 err)
}
}
} else if (blk_pm_request(rq)) {
- struct request_pm_state *pm = rq->end_io_data;
+ struct request_pm_state *pm = rq->data;
#ifdef DEBUG_PM
printk("%s: complete_power_step(step: %d, stat: %x, err: %x)\n",
drive->name, rq->pm->pm_step, stat, err);
@@ -587,7 +599,7 @@ ide_startstop_t ide_error (ide_drive_t *drive, const char *msg, u8 stat)
return ide_stopped;
/* retry only "normal" I/O: */
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {
+ if (!blk_fs_request(rq)) {
rq->errors = 1;
ide_end_drive_cmd(drive, stat, err);
return ide_stopped;
@@ -638,7 +650,7 @@ ide_startstop_t ide_abort(ide_drive_t *drive, const char *msg)
return ide_stopped;
/* retry only "normal" I/O: */
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK | REQ_DRIVE_TASKFILE)) {
+ if (!blk_fs_request(rq)) {
rq->errors = 1;
ide_end_drive_cmd(drive, BUSY_STAT, 0);
return ide_stopped;
@@ -808,7 +820,7 @@ void ide_map_sg(ide_drive_t *drive, struct request *rq)
if (hwif->sg_mapped) /* needed by ide-scsi */
return;
- if ((rq->flags & REQ_DRIVE_TASKFILE) == 0) {
+ if (rq->cmd_type != REQ_TYPE_ATA_TASKFILE) {
hwif->sg_nents = blk_rq_map_sg(drive->queue, rq, sg);
} else {
sg_init_one(sg, rq->buffer, rq->nr_sectors * SECTOR_SIZE);
@@ -844,7 +856,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
struct request *rq)
{
ide_hwif_t *hwif = HWIF(drive);
- if (rq->flags & REQ_DRIVE_TASKFILE) {
+ if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *args = rq->special;
if (!args)
@@ -866,7 +878,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
if (args->tf_out_flags.all != 0)
return flagged_taskfile(drive, args);
return do_rw_taskfile(drive, args);
- } else if (rq->flags & REQ_DRIVE_TASK) {
+ } else if (rq->cmd_type == REQ_TYPE_ATA_TASK) {
u8 *args = rq->buffer;
u8 sel;
@@ -892,7 +904,7 @@ static ide_startstop_t execute_drive_cmd (ide_drive_t *drive,
hwif->OUTB(sel, IDE_SELECT_REG);
ide_cmd(drive, args[0], args[2], &drive_cmd_intr);
return ide_started;
- } else if (rq->flags & REQ_DRIVE_CMD) {
+ } else if (rq->cmd_type == REQ_TYPE_ATA_CMD) {
u8 *args = rq->buffer;
if (!args)
@@ -933,7 +945,7 @@ done:
static void ide_check_pm_state(ide_drive_t *drive, struct request *rq)
{
- struct request_pm_state *pm = rq->end_io_data;
+ struct request_pm_state *pm = rq->data;
if (blk_pm_suspend_request(rq) &&
pm->pm_step == ide_pm_state_start_suspend)
@@ -980,7 +992,7 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
ide_startstop_t startstop;
sector_t block;
- BUG_ON(!(rq->flags & REQ_STARTED));
+ BUG_ON(!blk_rq_started(rq));
#ifdef DEBUG
printk("%s: start_request: current=0x%08lx\n",
@@ -1013,12 +1025,12 @@ static ide_startstop_t start_request (ide_drive_t *drive, struct request *rq)
if (!drive->special.all) {
ide_driver_t *drv;
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK))
- return execute_drive_cmd(drive, rq);
- else if (rq->flags & REQ_DRIVE_TASKFILE)
+ if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
+ rq->cmd_type == REQ_TYPE_ATA_TASK ||
+ rq->cmd_type == REQ_TYPE_ATA_TASKFILE)
return execute_drive_cmd(drive, rq);
else if (blk_pm_request(rq)) {
- struct request_pm_state *pm = rq->end_io_data;
+ struct request_pm_state *pm = rq->data;
#ifdef DEBUG_PM
printk("%s: start_power_step(step: %d)\n",
drive->name, rq->pm->pm_step);
@@ -1264,7 +1276,7 @@ static void ide_do_request (ide_hwgroup_t *hwgroup, int masked_irq)
* We count how many times we loop here to make sure we service
* all drives in the hwgroup without looping for ever
*/
- if (drive->blocked && !blk_pm_request(rq) && !(rq->flags & REQ_PREEMPT)) {
+ if (drive->blocked && !blk_pm_request(rq) && !(rq->cmd_flags & REQ_PREEMPT)) {
drive = drive->next ? drive->next : hwgroup->drive;
if (loops++ < 4 && !blk_queue_plugged(drive->queue))
goto again;
@@ -1346,6 +1358,10 @@ static ide_startstop_t ide_dma_timeout_retry(ide_drive_t *drive, int error)
* make sure request is sane
*/
rq = HWGROUP(drive)->rq;
+
+ if (!rq)
+ goto out;
+
HWGROUP(drive)->rq = NULL;
rq->errors = 0;
@@ -1670,7 +1686,7 @@ irqreturn_t ide_intr (int irq, void *dev_id, struct pt_regs *regs)
void ide_init_drive_cmd (struct request *rq)
{
memset(rq, 0, sizeof(*rq));
- rq->flags = REQ_DRIVE_CMD;
+ rq->cmd_type = REQ_TYPE_ATA_CMD;
rq->ref_count = 1;
}
@@ -1710,7 +1726,6 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
int must_wait = (action == ide_wait || action == ide_head_wait);
rq->errors = 0;
- rq->rq_status = RQ_ACTIVE;
/*
* we need to hold an extra reference to request for safe inspection
@@ -1718,7 +1733,7 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
*/
if (must_wait) {
rq->ref_count++;
- rq->waiting = &wait;
+ rq->end_io_data = &wait;
rq->end_io = blk_end_sync_rq;
}
@@ -1727,7 +1742,7 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
hwgroup->rq = NULL;
if (action == ide_preempt || action == ide_head_wait) {
where = ELEVATOR_INSERT_FRONT;
- rq->flags |= REQ_PREEMPT;
+ rq->cmd_flags |= REQ_PREEMPT;
}
__elv_add_request(drive->queue, rq, where, 0);
ide_do_request(hwgroup, IDE_NO_IRQ);
@@ -1736,7 +1751,6 @@ int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t actio
err = 0;
if (must_wait) {
wait_for_completion(&wait);
- rq->waiting = NULL;
if (rq->errors)
err = -EIO;
diff --git a/drivers/ide/ide-iops.c b/drivers/ide/ide-iops.c
index 77703acaec1..badde633177 100644
--- a/drivers/ide/ide-iops.c
+++ b/drivers/ide/ide-iops.c
@@ -998,6 +998,7 @@ static ide_startstop_t atapi_reset_pollfunc (ide_drive_t *drive)
}
/* done polling */
hwgroup->polling = 0;
+ hwgroup->resetting = 0;
return ide_stopped;
}
@@ -1057,6 +1058,7 @@ static ide_startstop_t reset_pollfunc (ide_drive_t *drive)
}
}
hwgroup->polling = 0; /* done polling */
+ hwgroup->resetting = 0; /* done reset attempt */
return ide_stopped;
}
@@ -1143,6 +1145,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
/* For an ATAPI device, first try an ATAPI SRST. */
if (drive->media != ide_disk && !do_not_try_atapi) {
+ hwgroup->resetting = 1;
pre_reset(drive);
SELECT_DRIVE(drive);
udelay (20);
@@ -1168,6 +1171,7 @@ static ide_startstop_t do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
return ide_stopped;
}
+ hwgroup->resetting = 1;
/*
* Note that we also set nIEN while resetting the device,
* to mask unwanted interrupts from the interface during the reset.
diff --git a/drivers/ide/ide-lib.c b/drivers/ide/ide-lib.c
index 1feff23487d..8237d89eec6 100644
--- a/drivers/ide/ide-lib.c
+++ b/drivers/ide/ide-lib.c
@@ -71,75 +71,96 @@ EXPORT_SYMBOL(ide_xfer_verbose);
/**
* ide_dma_speed - compute DMA speed
* @drive: drive
- * @mode; intended mode
+ * @mode: modes available
*
* Checks the drive capabilities and returns the speed to use
- * for the transfer. Returns -1 if the requested mode is unknown
- * (eg PIO)
+ * for the DMA transfer. Returns 0 if the drive is incapable
+ * of DMA transfers.
*/
u8 ide_dma_speed(ide_drive_t *drive, u8 mode)
{
struct hd_driveid *id = drive->id;
ide_hwif_t *hwif = HWIF(drive);
+ u8 ultra_mask, mwdma_mask, swdma_mask;
u8 speed = 0;
if (drive->media != ide_disk && hwif->atapi_dma == 0)
return 0;
- switch(mode) {
- case 0x04:
- if ((id->dma_ultra & 0x0040) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_6; break; }
- case 0x03:
- if ((id->dma_ultra & 0x0020) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_5; break; }
- case 0x02:
- if ((id->dma_ultra & 0x0010) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_4; break; }
- if ((id->dma_ultra & 0x0008) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_3; break; }
- case 0x01:
- if ((id->dma_ultra & 0x0004) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_2; break; }
- if ((id->dma_ultra & 0x0002) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_1; break; }
- if ((id->dma_ultra & 0x0001) &&
- (id->dma_ultra & hwif->ultra_mask))
- { speed = XFER_UDMA_0; break; }
- case 0x00:
- if ((id->dma_mword & 0x0004) &&
- (id->dma_mword & hwif->mwdma_mask))
- { speed = XFER_MW_DMA_2; break; }
- if ((id->dma_mword & 0x0002) &&
- (id->dma_mword & hwif->mwdma_mask))
- { speed = XFER_MW_DMA_1; break; }
- if ((id->dma_mword & 0x0001) &&
- (id->dma_mword & hwif->mwdma_mask))
- { speed = XFER_MW_DMA_0; break; }
- if ((id->dma_1word & 0x0004) &&
- (id->dma_1word & hwif->swdma_mask))
- { speed = XFER_SW_DMA_2; break; }
- if ((id->dma_1word & 0x0002) &&
- (id->dma_1word & hwif->swdma_mask))
- { speed = XFER_SW_DMA_1; break; }
- if ((id->dma_1word & 0x0001) &&
- (id->dma_1word & hwif->swdma_mask))
- { speed = XFER_SW_DMA_0; break; }
- }
+ /* Capable of UltraDMA modes? */
+ ultra_mask = id->dma_ultra & hwif->ultra_mask;
+
+ if (!(id->field_valid & 4))
+ mode = 0; /* fallback to MW/SW DMA if no UltraDMA */
+
+ switch (mode) {
+ case 4:
+ if (ultra_mask & 0x40) {
+ speed = XFER_UDMA_6;
+ break;
+ }
+ case 3:
+ if (ultra_mask & 0x20) {
+ speed = XFER_UDMA_5;
+ break;
+ }
+ case 2:
+ if (ultra_mask & 0x10) {
+ speed = XFER_UDMA_4;
+ break;
+ }
+ if (ultra_mask & 0x08) {
+ speed = XFER_UDMA_3;
+ break;
+ }
+ case 1:
+ if (ultra_mask & 0x04) {
+ speed = XFER_UDMA_2;
+ break;
+ }
+ if (ultra_mask & 0x02) {
+ speed = XFER_UDMA_1;
+ break;
+ }
+ if (ultra_mask & 0x01) {
+ speed = XFER_UDMA_0;
+ break;
+ }
+ case 0:
+ mwdma_mask = id->dma_mword & hwif->mwdma_mask;
-// printk("%s: %s: mode 0x%02x, speed 0x%02x\n",
-// __FUNCTION__, drive->name, mode, speed);
+ if (mwdma_mask & 0x04) {
+ speed = XFER_MW_DMA_2;
+ break;
+ }
+ if (mwdma_mask & 0x02) {
+ speed = XFER_MW_DMA_1;
+ break;
+ }
+ if (mwdma_mask & 0x01) {
+ speed = XFER_MW_DMA_0;
+ break;
+ }
+
+ swdma_mask = id->dma_1word & hwif->swdma_mask;
+
+ if (swdma_mask & 0x04) {
+ speed = XFER_SW_DMA_2;
+ break;
+ }
+ if (swdma_mask & 0x02) {
+ speed = XFER_SW_DMA_1;
+ break;
+ }
+ if (swdma_mask & 0x01) {
+ speed = XFER_SW_DMA_0;
+ break;
+ }
+ }
return speed;
}
-
EXPORT_SYMBOL(ide_dma_speed);
@@ -456,13 +477,14 @@ static void ide_dump_opcode(ide_drive_t *drive)
spin_unlock(&ide_lock);
if (!rq)
return;
- if (rq->flags & (REQ_DRIVE_CMD | REQ_DRIVE_TASK)) {
+ if (rq->cmd_type == REQ_TYPE_ATA_CMD ||
+ rq->cmd_type == REQ_TYPE_ATA_TASK) {
char *args = rq->buffer;
if (args) {
opcode = args[0];
found = 1;
}
- } else if (rq->flags & REQ_DRIVE_TASKFILE) {
+ } else if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *args = rq->special;
if (args) {
task_struct_t *tf = (task_struct_t *) args->tfRegister;
diff --git a/drivers/ide/ide-probe.c b/drivers/ide/ide-probe.c
index 9cadf0106c6..dad9c47ebb6 100644
--- a/drivers/ide/ide-probe.c
+++ b/drivers/ide/ide-probe.c
@@ -623,6 +623,8 @@ static void hwif_release_dev (struct device *dev)
static void hwif_register (ide_hwif_t *hwif)
{
+ int ret;
+
/* register with global device tree */
strlcpy(hwif->gendev.bus_id,hwif->name,BUS_ID_SIZE);
hwif->gendev.driver_data = hwif;
@@ -634,7 +636,10 @@ static void hwif_register (ide_hwif_t *hwif)
hwif->gendev.parent = NULL;
}
hwif->gendev.release = hwif_release_dev;
- device_register(&hwif->gendev);
+ ret = device_register(&hwif->gendev);
+ if (ret < 0)
+ printk(KERN_WARNING "IDE: %s: device_register error: %d\n",
+ __FUNCTION__, ret);
}
static int wait_hwif_ready(ide_hwif_t *hwif)
@@ -884,13 +889,19 @@ int probe_hwif_init_with_fixup(ide_hwif_t *hwif, void (*fixup)(ide_hwif_t *hwif)
if (hwif->present) {
u16 unit = 0;
+ int ret;
+
for (unit = 0; unit < MAX_DRIVES; ++unit) {
ide_drive_t *drive = &hwif->drives[unit];
/* For now don't attach absent drives, we may
want them on default or a new "empty" class
for hotplug reprobing ? */
if (drive->present) {
- device_register(&drive->gendev);
+ ret = device_register(&drive->gendev);
+ if (ret < 0)
+ printk(KERN_WARNING "IDE: %s: "
+ "device_register error: %d\n",
+ __FUNCTION__, ret);
}
}
}
@@ -1409,8 +1420,14 @@ int ideprobe_init (void)
if (hwif->chipset == ide_unknown || hwif->chipset == ide_forced)
hwif->chipset = ide_generic;
for (unit = 0; unit < MAX_DRIVES; ++unit)
- if (hwif->drives[unit].present)
- device_register(&hwif->drives[unit].gendev);
+ if (hwif->drives[unit].present) {
+ int ret = device_register(
+ &hwif->drives[unit].gendev);
+ if (ret < 0)
+ printk(KERN_WARNING "IDE: %s: "
+ "device_register error: %d\n",
+ __FUNCTION__, ret);
+ }
}
}
return 0;
diff --git a/drivers/ide/ide-proc.c b/drivers/ide/ide-proc.c
index 41b74b13a00..aa049dab3d9 100644
--- a/drivers/ide/ide-proc.c
+++ b/drivers/ide/ide-proc.c
@@ -326,15 +326,24 @@ static int ide_replace_subdriver(ide_drive_t *drive, const char *driver)
{
struct device *dev = &drive->gendev;
int ret = 1;
+ int err;
down_write(&dev->bus->subsys.rwsem);
device_release_driver(dev);
/* FIXME: device can still be in use by previous driver */
strlcpy(drive->driver_req, driver, sizeof(drive->driver_req));
- device_attach(dev);
+ err = device_attach(dev);
+ if (err < 0)
+ printk(KERN_WARNING "IDE: %s: device_attach error: %d\n",
+ __FUNCTION__, err);
drive->driver_req[0] = 0;
- if (dev->driver == NULL)
- device_attach(dev);
+ if (dev->driver == NULL) {
+ err = device_attach(dev);
+ if (err < 0)
+ printk(KERN_WARNING
+ "IDE: %s: device_attach(2) error: %d\n",
+ __FUNCTION__, err);
+ }
if (dev->driver && !strcmp(dev->driver->name, driver))
ret = 0;
up_write(&dev->bus->subsys.rwsem);
@@ -526,7 +535,12 @@ static int proc_print_driver(struct device_driver *drv, void *data)
static int ide_drivers_show(struct seq_file *s, void *p)
{
- bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
+ int err;
+
+ err = bus_for_each_drv(&ide_bus_type, NULL, s, proc_print_driver);
+ if (err < 0)
+ printk(KERN_WARNING "IDE: %s: bus_for_each_drv error: %d\n",
+ __FUNCTION__, err);
return 0;
}
diff --git a/drivers/ide/ide-tape.c b/drivers/ide/ide-tape.c
index 7067ab99792..e2f4bb54906 100644
--- a/drivers/ide/ide-tape.c
+++ b/drivers/ide/ide-tape.c
@@ -1776,7 +1776,7 @@ static void idetape_create_request_sense_cmd (idetape_pc_t *pc)
static void idetape_init_rq(struct request *rq, u8 cmd)
{
memset(rq, 0, sizeof(*rq));
- rq->flags = REQ_SPECIAL;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
rq->cmd[0] = cmd;
}
@@ -2423,8 +2423,8 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
#if IDETAPE_DEBUG_LOG
#if 0
if (tape->debug_level >= 5)
- printk(KERN_INFO "ide-tape: rq_status: %d, "
- "dev: %s, cmd: %ld, errors: %d\n", rq->rq_status,
+ printk(KERN_INFO "ide-tape: %d, "
+ "dev: %s, cmd: %ld, errors: %d\n",
rq->rq_disk->disk_name, rq->cmd[0], rq->errors);
#endif
if (tape->debug_level >= 2)
@@ -2433,12 +2433,12 @@ static ide_startstop_t idetape_do_request(ide_drive_t *drive,
rq->sector, rq->nr_sectors, rq->current_nr_sectors);
#endif /* IDETAPE_DEBUG_LOG */
- if ((rq->flags & REQ_SPECIAL) == 0) {
+ if (!blk_special_request(rq)) {
/*
* We do not support buffer cache originated requests.
*/
printk(KERN_NOTICE "ide-tape: %s: Unsupported request in "
- "request queue (%ld)\n", drive->name, rq->flags);
+ "request queue (%d)\n", drive->name, rq->cmd_type);
ide_end_request(drive, 0, 0);
return ide_stopped;
}
@@ -2764,16 +2764,16 @@ static void idetape_add_stage_tail (ide_drive_t *drive,idetape_stage_t *stage)
*/
static void idetape_wait_for_request (ide_drive_t *drive, struct request *rq)
{
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
idetape_tape_t *tape = drive->driver_data;
#if IDETAPE_DEBUG_BUGS
- if (rq == NULL || (rq->flags & REQ_SPECIAL) == 0) {
+ if (rq == NULL || !blk_special_request(rq)) {
printk (KERN_ERR "ide-tape: bug: Trying to sleep on non-valid request\n");
return;
}
#endif /* IDETAPE_DEBUG_BUGS */
- rq->waiting = &wait;
+ rq->end_io_data = &wait;
rq->end_io = blk_end_sync_rq;
spin_unlock_irq(&tape->spinlock);
wait_for_completion(&wait);
diff --git a/drivers/ide/ide-taskfile.c b/drivers/ide/ide-taskfile.c
index 97a9244312f..1d0470c1f95 100644
--- a/drivers/ide/ide-taskfile.c
+++ b/drivers/ide/ide-taskfile.c
@@ -363,7 +363,7 @@ static ide_startstop_t task_error(ide_drive_t *drive, struct request *rq,
static void task_end_request(ide_drive_t *drive, struct request *rq, u8 stat)
{
- if (rq->flags & REQ_DRIVE_TASKFILE) {
+ if (rq->cmd_type == REQ_TYPE_ATA_TASKFILE) {
ide_task_t *task = rq->special;
if (task->tf_out_flags.all) {
@@ -474,7 +474,7 @@ static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long
struct request rq;
memset(&rq, 0, sizeof(rq));
- rq.flags = REQ_DRIVE_TASKFILE;
+ rq.cmd_type = REQ_TYPE_ATA_TASKFILE;
rq.buffer = buf;
/*
@@ -499,7 +499,7 @@ static int ide_diag_taskfile(ide_drive_t *drive, ide_task_t *args, unsigned long
rq.hard_cur_sectors = rq.current_nr_sectors = rq.nr_sectors;
if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
- rq.flags |= REQ_RW;
+ rq.cmd_flags |= REQ_RW;
}
rq.special = args;
@@ -737,7 +737,7 @@ static int ide_wait_cmd_task(ide_drive_t *drive, u8 *buf)
struct request rq;
ide_init_drive_cmd(&rq);
- rq.flags = REQ_DRIVE_TASK;
+ rq.cmd_type = REQ_TYPE_ATA_TASK;
rq.buffer = buf;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
diff --git a/drivers/ide/ide.c b/drivers/ide/ide.c
index defd4b4bd37..287a6620115 100644
--- a/drivers/ide/ide.c
+++ b/drivers/ide/ide.c
@@ -450,7 +450,7 @@ void ide_hwif_release_regions(ide_hwif_t *hwif)
* @hwif: hwif to update
* @tmp_hwif: template
*
- * Restore hwif to a previous state by copying most settngs
+ * Restore hwif to a previous state by copying most settings
* from the template.
*/
@@ -539,9 +539,10 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
hwif->dma_vendor3 = tmp_hwif->dma_vendor3;
hwif->dma_prdtable = tmp_hwif->dma_prdtable;
- hwif->dma_extra = tmp_hwif->dma_extra;
hwif->config_data = tmp_hwif->config_data;
hwif->select_data = tmp_hwif->select_data;
+ hwif->extra_base = tmp_hwif->extra_base;
+ hwif->extra_ports = tmp_hwif->extra_ports;
hwif->autodma = tmp_hwif->autodma;
hwif->udma_four = tmp_hwif->udma_four;
hwif->no_dsc = tmp_hwif->no_dsc;
@@ -550,7 +551,7 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
}
/**
- * ide_unregister - free an ide interface
+ * ide_unregister - free an IDE interface
* @index: index of interface (will change soon to a pointer)
*
* Perform the final unregister of an IDE interface. At the moment
@@ -563,8 +564,8 @@ static void ide_hwif_restore(ide_hwif_t *hwif, ide_hwif_t *tmp_hwif)
* deadlocking the IDE layer. The shutdown callback is called
* before we take the lock and free resources. It is up to the
* caller to be sure there is no pending I/O here, and that
- * the interfce will not be reopened (present/vanishing locking
- * isnt yet done btw). After we commit to the final kill we
+ * the interface will not be reopened (present/vanishing locking
+ * isn't yet done BTW). After we commit to the final kill we
* call the cleanup callback with the ide locks held.
*
* Unregister restores the hwif structures to the default state.
@@ -674,6 +675,9 @@ void ide_unregister(unsigned int index)
hwif->dma_status = 0;
hwif->dma_vendor3 = 0;
hwif->dma_prdtable = 0;
+
+ hwif->extra_base = 0;
+ hwif->extra_ports = 0;
}
/* copy original settings */
@@ -1207,7 +1211,7 @@ int system_bus_clock (void)
EXPORT_SYMBOL(system_bus_clock);
-static int generic_ide_suspend(struct device *dev, pm_message_t state)
+static int generic_ide_suspend(struct device *dev, pm_message_t mesg)
{
ide_drive_t *drive = dev->driver_data;
struct request rq;
@@ -1217,11 +1221,13 @@ static int generic_ide_suspend(struct device *dev, pm_message_t state)
memset(&rq, 0, sizeof(rq));
memset(&rqpm, 0, sizeof(rqpm));
memset(&args, 0, sizeof(args));
- rq.flags = REQ_PM_SUSPEND;
+ rq.cmd_type = REQ_TYPE_PM_SUSPEND;
rq.special = &args;
- rq.end_io_data = &rqpm;
+ rq.data = &rqpm;
rqpm.pm_step = ide_pm_state_start_suspend;
- rqpm.pm_state = state.event;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ rqpm.pm_state = mesg.event;
return ide_do_drive_cmd(drive, &rq, ide_wait);
}
@@ -1236,9 +1242,9 @@ static int generic_ide_resume(struct device *dev)
memset(&rq, 0, sizeof(rq));
memset(&rqpm, 0, sizeof(rqpm));
memset(&args, 0, sizeof(args));
- rq.flags = REQ_PM_RESUME;
+ rq.cmd_type = REQ_TYPE_PM_RESUME;
rq.special = &args;
- rq.end_io_data = &rqpm;
+ rq.data = &rqpm;
rqpm.pm_step = ide_pm_state_start_resume;
rqpm.pm_state = PM_EVENT_ON;
@@ -1358,6 +1364,11 @@ int generic_ide_ioctl(ide_drive_t *drive, struct file *file, struct block_device
spin_lock_irqsave(&ide_lock, flags);
+ if (HWGROUP(drive)->resetting) {
+ spin_unlock_irqrestore(&ide_lock, flags);
+ return -EBUSY;
+ }
+
ide_abort(drive, "drive reset");
BUG_ON(HWGROUP(drive)->handler);
@@ -1991,10 +2002,16 @@ EXPORT_SYMBOL_GPL(ide_bus_type);
*/
static int __init ide_init(void)
{
+ int ret;
+
printk(KERN_INFO "Uniform Multi-Platform E-IDE driver " REVISION "\n");
system_bus_speed = ide_system_bus_speed();
- bus_register(&ide_bus_type);
+ ret = bus_register(&ide_bus_type);
+ if (ret < 0) {
+ printk(KERN_WARNING "IDE: bus_register error: %d\n", ret);
+ return ret;
+ }
init_ide_data();
diff --git a/drivers/ide/legacy/hd.c b/drivers/ide/legacy/hd.c
index aebecd8f51c..4ab93114567 100644
--- a/drivers/ide/legacy/hd.c
+++ b/drivers/ide/legacy/hd.c
@@ -626,7 +626,7 @@ repeat:
req->rq_disk->disk_name, (req->cmd == READ)?"read":"writ",
cyl, head, sec, nsect, req->buffer);
#endif
- if (req->flags & REQ_CMD) {
+ if (blk_fs_request(req)) {
switch (rq_data_dir(req)) {
case READ:
hd_out(disk,nsect,sec,head,cyl,WIN_READ,&read_intr);
diff --git a/drivers/ide/legacy/ide-cs.c b/drivers/ide/legacy/ide-cs.c
index 602797a4420..bef4759f70e 100644
--- a/drivers/ide/legacy/ide-cs.c
+++ b/drivers/ide/legacy/ide-cs.c
@@ -120,7 +120,7 @@ static int ide_probe(struct pcmcia_device *link)
link->io.Attributes1 = IO_DATA_PATH_WIDTH_AUTO;
link->io.Attributes2 = IO_DATA_PATH_WIDTH_8;
link->io.IOAddrLines = 3;
- link->irq.Attributes = IRQ_TYPE_EXCLUSIVE;
+ link->irq.Attributes = IRQ_TYPE_DYNAMIC_SHARING;
link->irq.IRQInfo1 = IRQ_LEVEL_ID;
link->conf.Attributes = CONF_ENABLE_IRQ;
link->conf.IntType = INT_MEMORY_AND_IO;
@@ -398,12 +398,17 @@ static struct pcmcia_device_id ide_ids[] = {
PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDE", 0x547e66dc, 0x5c5ab149),
PCMCIA_DEVICE_PROD_ID12("IO DATA", "PCIDEII", 0x547e66dc, 0xb3662674),
PCMCIA_DEVICE_PROD_ID12("LOOKMEET", "CBIDE2 ", 0xe37be2b5, 0x8671043b),
+ PCMCIA_DEVICE_PROD_ID12("M-Systems", "CF500", 0x7ed2ad87, 0x7a13045c),
PCMCIA_DEVICE_PROD_ID2("NinjaATA-", 0xebe0bd79),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "CD-ROM", 0x281f1c5d, 0x66536591),
PCMCIA_DEVICE_PROD_ID12("PCMCIA", "PnPIDE", 0x281f1c5d, 0x0c694728),
PCMCIA_DEVICE_PROD_ID12("SHUTTLE TECHNOLOGY LTD.", "PCCARD-IDE/ATAPI Adapter", 0x4a3f0ba0, 0x322560e1),
+ PCMCIA_DEVICE_PROD_ID12("SEAGATE", "ST1", 0x87c1b330, 0xe1f30883),
+ PCMCIA_DEVICE_PROD_ID12("SAMSUNG", "04/05/06", 0x43d74cb4, 0x6a22777d),
+ PCMCIA_DEVICE_PROD_ID12("SMI VENDOR", "SMI PRODUCT", 0x30896c92, 0x703cc5f6),
PCMCIA_DEVICE_PROD_ID12("TOSHIBA", "MK2001MPL", 0xb4585a1a, 0x3489e003),
PCMCIA_DEVICE_PROD_ID1("TRANSCEND 512M ", 0xd0909443),
+ PCMCIA_DEVICE_PROD_ID12("TRANSCEND", "TS4GCF120", 0x709b1bf1, 0xf54a91c8),
PCMCIA_DEVICE_PROD_ID12("WIT", "IDE16", 0x244e5994, 0x3e232852),
PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209),
PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e),
diff --git a/drivers/ide/mips/au1xxx-ide.c b/drivers/ide/mips/au1xxx-ide.c
index 71f27e955d8..c7854ea57b5 100644
--- a/drivers/ide/mips/au1xxx-ide.c
+++ b/drivers/ide/mips/au1xxx-ide.c
@@ -476,13 +476,13 @@ static int auide_dma_lostirq(ide_drive_t *drive)
return 0;
}
-static void auide_ddma_tx_callback(int irq, void *param, struct pt_regs *regs)
+static void auide_ddma_tx_callback(int irq, void *param)
{
_auide_hwif *ahwif = (_auide_hwif*)param;
ahwif->drive->waiting_for_dma = 0;
}
-static void auide_ddma_rx_callback(int irq, void *param, struct pt_regs *regs)
+static void auide_ddma_rx_callback(int irq, void *param)
{
_auide_hwif *ahwif = (_auide_hwif*)param;
ahwif->drive->waiting_for_dma = 0;
diff --git a/drivers/ide/pci/Makefile b/drivers/ide/pci/Makefile
index f35d684edc2..640a54b09b5 100644
--- a/drivers/ide/pci/Makefile
+++ b/drivers/ide/pci/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_BLK_DEV_HPT366) += hpt366.o
#obj-$(CONFIG_BLK_DEV_HPT37X) += hpt37x.o
obj-$(CONFIG_BLK_DEV_IT8172) += it8172.o
obj-$(CONFIG_BLK_DEV_IT821X) += it821x.o
+obj-$(CONFIG_BLK_DEV_JMICRON) += jmicron.o
obj-$(CONFIG_BLK_DEV_NS87415) += ns87415.o
obj-$(CONFIG_BLK_DEV_OPTI621) += opti621.o
obj-$(CONFIG_BLK_DEV_PDC202XX_OLD) += pdc202xx_old.o
diff --git a/drivers/ide/pci/atiixp.c b/drivers/ide/pci/atiixp.c
index a574de5f083..d55b938b1ae 100644
--- a/drivers/ide/pci/atiixp.c
+++ b/drivers/ide/pci/atiixp.c
@@ -318,6 +318,20 @@ static void __devinit init_hwif_atiixp(ide_hwif_t *hwif)
hwif->drives[0].autodma = hwif->autodma;
}
+static void __devinit init_hwif_sb600_legacy(ide_hwif_t *hwif)
+{
+
+ hwif->atapi_dma = 1;
+ hwif->ultra_mask = 0x7f;
+ hwif->mwdma_mask = 0x07;
+ hwif->swdma_mask = 0x07;
+
+ if (!noautodma)
+ hwif->autodma = 1;
+ hwif->drives[0].autodma = hwif->autodma;
+ hwif->drives[1].autodma = hwif->autodma;
+}
+
static ide_pci_device_t atiixp_pci_info[] __devinitdata = {
{ /* 0 */
.name = "ATIIXP",
@@ -326,6 +340,12 @@ static ide_pci_device_t atiixp_pci_info[] __devinitdata = {
.autodma = AUTODMA,
.enablebits = {{0x48,0x01,0x00}, {0x48,0x08,0x00}},
.bootable = ON_BOARD,
+ },{ /* 1 */
+ .name = "ATI SB600 SATA Legacy IDE",
+ .init_hwif = init_hwif_sb600_legacy,
+ .channels = 2,
+ .autodma = AUTODMA,
+ .bootable = ON_BOARD,
}
};
@@ -348,6 +368,7 @@ static struct pci_device_id atiixp_pci_tbl[] = {
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP300_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP400_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
{ PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_IDE, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+ { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, PCI_ANY_ID, PCI_ANY_ID, (PCI_CLASS_STORAGE_IDE<<8)|0x8a, 0xffff05, 1},
{ 0, },
};
MODULE_DEVICE_TABLE(pci, atiixp_pci_tbl);
diff --git a/drivers/ide/pci/cs5530.c b/drivers/ide/pci/cs5530.c
index 380bb28c7c5..ae405fa3223 100644
--- a/drivers/ide/pci/cs5530.c
+++ b/drivers/ide/pci/cs5530.c
@@ -222,23 +222,23 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch
unsigned long flags;
dev = NULL;
- while ((dev = pci_find_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) {
+ while ((dev = pci_get_device(PCI_VENDOR_ID_CYRIX, PCI_ANY_ID, dev)) != NULL) {
switch (dev->device) {
case PCI_DEVICE_ID_CYRIX_PCI_MASTER:
- master_0 = dev;
+ master_0 = pci_dev_get(dev);
break;
case PCI_DEVICE_ID_CYRIX_5530_LEGACY:
- cs5530_0 = dev;
+ cs5530_0 = pci_dev_get(dev);
break;
}
}
if (!master_0) {
printk(KERN_ERR "%s: unable to locate PCI MASTER function\n", name);
- return 0;
+ goto out;
}
if (!cs5530_0) {
printk(KERN_ERR "%s: unable to locate CS5530 LEGACY function\n", name);
- return 0;
+ goto out;
}
spin_lock_irqsave(&ide_lock, flags);
@@ -296,6 +296,9 @@ static unsigned int __devinit init_chipset_cs5530 (struct pci_dev *dev, const ch
spin_unlock_irqrestore(&ide_lock, flags);
+out:
+ pci_dev_put(master_0);
+ pci_dev_put(cs5530_0);
return 0;
}
diff --git a/drivers/ide/pci/cy82c693.c b/drivers/ide/pci/cy82c693.c
index 120929fbe7a..64330c459bd 100644
--- a/drivers/ide/pci/cy82c693.c
+++ b/drivers/ide/pci/cy82c693.c
@@ -281,7 +281,7 @@ static void cy82c693_tune_drive (ide_drive_t *drive, u8 pio)
/* select primary or secondary channel */
if (hwif->index > 0) { /* drive is on the secondary channel */
- dev = pci_find_slot(dev->bus->number, dev->devfn+1);
+ dev = pci_get_slot(dev->bus, dev->devfn+1);
if (!dev) {
printk(KERN_ERR "%s: tune_drive: "
"Cannot find secondary interface!\n",
@@ -500,8 +500,9 @@ static int __devinit cy82c693_init_one(struct pci_dev *dev, const struct pci_dev
Function 1 is primary IDE channel, function 2 - secondary. */
if ((dev->class >> 8) == PCI_CLASS_STORAGE_IDE &&
PCI_FUNC(dev->devfn) == 1) {
- dev2 = pci_find_slot(dev->bus->number, dev->devfn + 1);
+ dev2 = pci_get_slot(dev->bus, dev->devfn + 1);
ret = ide_setup_pci_devices(dev, dev2, d);
+ /* We leak pci refs here but thats ok - we can't be unloaded */
}
return ret;
}
diff --git a/drivers/ide/pci/generic.c b/drivers/ide/pci/generic.c
index 78810ba982e..0cb7b9b520e 100644
--- a/drivers/ide/pci/generic.c
+++ b/drivers/ide/pci/generic.c
@@ -41,15 +41,8 @@
static int ide_generic_all; /* Set to claim all devices */
-#ifndef MODULE
-static int __init ide_generic_all_on(char *unused)
-{
- ide_generic_all = 1;
- printk(KERN_INFO "IDE generic will claim all unknown PCI IDE storage controllers.\n");
- return 1;
-}
-__setup("all-generic-ide", ide_generic_all_on);
-#endif
+module_param_named(all_generic_ide, ide_generic_all, bool, 0444);
+MODULE_PARM_DESC(all_generic_ide, "IDE generic will claim all unknown PCI IDE storage controllers.");
static void __devinit init_hwif_generic (ide_hwif_t *hwif)
{
diff --git a/drivers/ide/pci/jmicron.c b/drivers/ide/pci/jmicron.c
new file mode 100644
index 00000000000..68c74bbf8b0
--- /dev/null
+++ b/drivers/ide/pci/jmicron.c
@@ -0,0 +1,269 @@
+
+/*
+ * Copyright (C) 2006 Red Hat <alan@redhat.com>
+ *
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/hdreg.h>
+#include <linux/ide.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+
+typedef enum {
+ PORT_PATA0 = 0,
+ PORT_PATA1 = 1,
+ PORT_SATA = 2,
+} port_type;
+
+/**
+ * jmicron_ratemask - Compute available modes
+ * @drive: IDE drive
+ *
+ * Compute the available speeds for the devices on the interface. This
+ * is all modes to ATA133 clipped by drive cable setup.
+ */
+
+static u8 jmicron_ratemask(ide_drive_t *drive)
+{
+ u8 mode = 4;
+ if (!eighty_ninty_three(drive))
+ mode = min(mode, (u8)1);
+ return mode;
+}
+
+/**
+ * ata66_jmicron - Cable check
+ * @hwif: IDE port
+ *
+ * Return 1 if the cable is 80pin
+ */
+
+static int __devinit ata66_jmicron(ide_hwif_t *hwif)
+{
+ struct pci_dev *pdev = hwif->pci_dev;
+
+ u32 control;
+ u32 control5;
+
+ int port = hwif->channel;
+ port_type port_map[2];
+
+ pci_read_config_dword(pdev, 0x40, &control);
+
+ /* There are two basic mappings. One has the two SATA ports merged
+ as master/slave and the secondary as PATA, the other has only the
+ SATA port mapped */
+ if (control & (1 << 23)) {
+ port_map[0] = PORT_SATA;
+ port_map[1] = PORT_PATA0;
+ } else {
+ port_map[0] = PORT_SATA;
+ port_map[1] = PORT_SATA;
+ }
+
+ /* The 365/366 may have this bit set to map the second PATA port
+ as the internal primary channel */
+ pci_read_config_dword(pdev, 0x80, &control5);
+ if (control5 & (1<<24))
+ port_map[0] = PORT_PATA1;
+
+ /* The two ports may then be logically swapped by the firmware */
+ if (control & (1 << 22))
+ port = port ^ 1;
+
+ /*
+ * Now we know which physical port we are talking about we can
+ * actually do our cable checking etc. Thankfully we don't need
+ * to do the plumbing for other cases.
+ */
+ switch (port_map[port])
+ {
+ case PORT_PATA0:
+ if (control & (1 << 3)) /* 40/80 pin primary */
+ return 1;
+ return 0;
+ case PORT_PATA1:
+ if (control5 & (1 << 19)) /* 40/80 pin secondary */
+ return 0;
+ return 1;
+ case PORT_SATA:
+ return 1;
+ }
+}
+
+static void jmicron_tuneproc (ide_drive_t *drive, byte mode_wanted)
+{
+ return;
+}
+
+/**
+ * config_jmicron_chipset_for_pio - set drive timings
+ * @drive: drive to tune
+ * @speed we want
+ *
+ */
+
+static void config_jmicron_chipset_for_pio (ide_drive_t *drive, byte set_speed)
+{
+ u8 speed = XFER_PIO_0 + ide_get_best_pio_mode(drive, 255, 5, NULL);
+ if (set_speed)
+ (void) ide_config_drive_speed(drive, speed);
+}
+
+/**
+ * jmicron_tune_chipset - set controller timings
+ * @drive: Drive to set up
+ * @xferspeed: speed we want to achieve
+ *
+ * As the JMicron snoops for timings all we actually need to do is
+ * make sure we don't set an invalid mode. We do need to honour
+ * the cable detect here.
+ */
+
+static int jmicron_tune_chipset (ide_drive_t *drive, byte xferspeed)
+{
+
+ u8 speed = ide_rate_filter(jmicron_ratemask(drive), xferspeed);
+
+ return ide_config_drive_speed(drive, speed);
+}
+
+/**
+ * config_chipset_for_dma - configure for DMA
+ * @drive: drive to configure
+ *
+ * As the JMicron snoops for timings all we actually need to do is
+ * make sure we don't set an invalid mode.
+ */
+
+static int config_chipset_for_dma (ide_drive_t *drive)
+{
+ u8 speed = ide_dma_speed(drive, jmicron_ratemask(drive));
+
+ config_jmicron_chipset_for_pio(drive, !speed);
+ jmicron_tune_chipset(drive, speed);
+ return ide_dma_enable(drive);
+}
+
+/**
+ * jmicron_configure_drive_for_dma - set up for DMA transfers
+ * @drive: drive we are going to set up
+ *
+ * As the JMicron snoops for timings all we actually need to do is
+ * make sure we don't set an invalid mode.
+ */
+
+static int jmicron_config_drive_for_dma (ide_drive_t *drive)
+{
+ ide_hwif_t *hwif = drive->hwif;
+
+ if (ide_use_dma(drive)) {
+ if (config_chipset_for_dma(drive))
+ return hwif->ide_dma_on(drive);
+ }
+ config_jmicron_chipset_for_pio(drive, 1);
+ return hwif->ide_dma_off_quietly(drive);
+}
+
+/**
+ * init_hwif_jmicron - set up hwif structs
+ * @hwif: interface to set up
+ *
+ * Minimal set up is required for the Jmicron hardware.
+ */
+
+static void __devinit init_hwif_jmicron(ide_hwif_t *hwif)
+{
+ hwif->speedproc = &jmicron_tune_chipset;
+ hwif->tuneproc = &jmicron_tuneproc;
+
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+
+ if (!hwif->dma_base)
+ goto fallback;
+
+ hwif->atapi_dma = 1;
+ hwif->ultra_mask = 0x7f;
+ hwif->mwdma_mask = 0x07;
+
+ hwif->ide_dma_check = &jmicron_config_drive_for_dma;
+ if (!(hwif->udma_four))
+ hwif->udma_four = ata66_jmicron(hwif);
+
+ hwif->autodma = 1;
+ hwif->drives[0].autodma = hwif->autodma;
+ hwif->drives[1].autodma = hwif->autodma;
+ return;
+fallback:
+ hwif->autodma = 0;
+ return;
+}
+
+#define DECLARE_JMB_DEV(name_str) \
+ { \
+ .name = name_str, \
+ .init_hwif = init_hwif_jmicron, \
+ .channels = 2, \
+ .autodma = AUTODMA, \
+ .bootable = ON_BOARD, \
+ .enablebits = { {0x40, 1, 1}, {0x40, 0x10, 0x10} }, \
+ }
+
+static ide_pci_device_t jmicron_chipsets[] __devinitdata = {
+ /* 0 */ DECLARE_JMB_DEV("JMB361"),
+ /* 1 */ DECLARE_JMB_DEV("JMB363"),
+ /* 2 */ DECLARE_JMB_DEV("JMB365"),
+ /* 3 */ DECLARE_JMB_DEV("JMB366"),
+ /* 4 */ DECLARE_JMB_DEV("JMB368"),
+};
+
+/**
+ * jmicron_init_one - pci layer discovery entry
+ * @dev: PCI device
+ * @id: ident table entry
+ *
+ * Called by the PCI code when it finds a Jmicron controller.
+ * We then use the IDE PCI generic helper to do most of the work.
+ */
+
+static int __devinit jmicron_init_one(struct pci_dev *dev, const struct pci_device_id *id)
+{
+ ide_setup_pci_device(dev, &jmicron_chipsets[id->driver_data]);
+ return 0;
+}
+
+static struct pci_device_id jmicron_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB361), 0},
+ { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB363), 1},
+ { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB365), 2},
+ { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB366), 3},
+ { PCI_DEVICE(PCI_VENDOR_ID_JMICRON, PCI_DEVICE_ID_JMICRON_JMB368), 4},
+ { 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, jmicron_pci_tbl);
+
+static struct pci_driver driver = {
+ .name = "JMicron IDE",
+ .id_table = jmicron_pci_tbl,
+ .probe = jmicron_init_one,
+};
+
+static int __init jmicron_ide_init(void)
+{
+ return ide_pci_register_driver(&driver);
+}
+
+module_init(jmicron_ide_init);
+
+MODULE_AUTHOR("Alan Cox");
+MODULE_DESCRIPTION("PCI driver module for the JMicron in legacy modes");
+MODULE_LICENSE("GPL");
diff --git a/drivers/ide/pci/pdc202xx_old.c b/drivers/ide/pci/pdc202xx_old.c
index b46022a11be..184cdacddeb 100644
--- a/drivers/ide/pci/pdc202xx_old.c
+++ b/drivers/ide/pci/pdc202xx_old.c
@@ -154,7 +154,8 @@ static int pdc202xx_tune_chipset (ide_drive_t *drive, u8 xferspeed)
u8 AP, BP, CP, DP;
u8 TA = 0, TB = 0, TC = 0;
- if ((drive->media != ide_disk) && (speed < XFER_SW_DMA_0))
+ if (drive->media != ide_disk &&
+ drive->media != ide_cdrom && speed < XFER_SW_DMA_0)
return -1;
pci_read_config_dword(dev, drive_pci, &drive_conf);
@@ -330,14 +331,12 @@ static int config_chipset_for_dma (ide_drive_t *drive)
chipset_is_set:
- if (drive->media == ide_disk) {
- pci_read_config_byte(dev, (drive_pci), &AP);
- if (id->capability & 4) /* IORDY_EN */
- pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
- pci_read_config_byte(dev, (drive_pci), &AP);
- if (drive->media == ide_disk) /* PREFETCH_EN */
- pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
- }
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ if (id->capability & 4) /* IORDY_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|IORDY_EN);
+ pci_read_config_byte(dev, (drive_pci), &AP);
+ if (drive->media == ide_disk) /* PREFETCH_EN */
+ pci_write_config_byte(dev, (drive_pci), AP|PREFETCH_EN);
speed = ide_dma_speed(drive, pdc202xx_ratemask(drive));
@@ -385,7 +384,7 @@ static void pdc202xx_old_ide_dma_start(ide_drive_t *drive)
{
if (drive->current_speed > XFER_UDMA_2)
pdc_old_enable_66MHz_clock(drive->hwif);
- if (drive->addressing == 1) {
+ if (drive->media != ide_disk || drive->addressing == 1) {
struct request *rq = HWGROUP(drive)->rq;
ide_hwif_t *hwif = HWIF(drive);
unsigned long high_16 = hwif->dma_master;
@@ -405,7 +404,7 @@ static void pdc202xx_old_ide_dma_start(ide_drive_t *drive)
static int pdc202xx_old_ide_dma_end(ide_drive_t *drive)
{
- if (drive->addressing == 1) {
+ if (drive->media != ide_disk || drive->addressing == 1) {
ide_hwif_t *hwif = HWIF(drive);
unsigned long high_16 = hwif->dma_master;
unsigned long atapi_reg = high_16 + (hwif->channel ? 0x24 : 0x20);
@@ -519,6 +518,7 @@ static void __devinit init_hwif_pdc202xx(ide_hwif_t *hwif)
hwif->ultra_mask = 0x3f;
hwif->mwdma_mask = 0x07;
hwif->swdma_mask = 0x07;
+ hwif->atapi_dma = 1;
hwif->err_stops_fifo = 1;
diff --git a/drivers/ide/pci/piix.c b/drivers/ide/pci/piix.c
index 50332ddd5dd..cdc3aab9ebc 100644
--- a/drivers/ide/pci/piix.c
+++ b/drivers/ide/pci/piix.c
@@ -222,13 +222,15 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio)
u16 master_data;
u8 slave_data;
static DEFINE_SPINLOCK(tune_lock);
+ int control = 0;
/* ISP RTC */
- u8 timings[][2] = { { 0, 0 },
- { 0, 0 },
- { 1, 0 },
- { 2, 1 },
- { 2, 3 }, };
+ static const u8 timings[][2]= {
+ { 0, 0 },
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 1 },
+ { 2, 3 }, };
pio = ide_get_best_pio_mode(drive, pio, 5, NULL);
@@ -239,19 +241,30 @@ static void piix_tune_drive (ide_drive_t *drive, u8 pio)
*/
spin_lock_irqsave(&tune_lock, flags);
pci_read_config_word(dev, master_port, &master_data);
+
+ if (pio >= 2)
+ control |= 1; /* Programmable timing on */
+ if (drive->media == ide_disk)
+ control |= 4; /* Prefetch, post write */
+ if (pio >= 3)
+ control |= 2; /* IORDY */
if (is_slave) {
master_data = master_data | 0x4000;
- if (pio > 1)
+ if (pio > 1) {
/* enable PPE, IE and TIME */
- master_data = master_data | 0x0070;
+ master_data = master_data | (control << 4);
+ } else {
+ master_data &= ~0x0070;
+ }
pci_read_config_byte(dev, slave_port, &slave_data);
slave_data = slave_data & (hwif->channel ? 0x0f : 0xf0);
slave_data = slave_data | (((timings[pio][0] << 2) | timings[pio][1]) << (hwif->channel ? 4 : 0));
} else {
master_data = master_data & 0xccf8;
- if (pio > 1)
+ if (pio > 1) {
/* enable PPE, IE and TIME */
- master_data = master_data | 0x0007;
+ master_data = master_data | control;
+ }
master_data = master_data | (timings[pio][0] << 12) | (timings[pio][1] << 8);
}
pci_write_config_word(dev, master_port, master_data);
@@ -615,7 +628,7 @@ static void __devinit piix_check_450nx(void)
struct pci_dev *pdev = NULL;
u16 cfg;
u8 rev;
- while((pdev=pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev))!=NULL)
+ while((pdev=pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, pdev))!=NULL)
{
/* Look for 450NX PXB. Check for problem configurations
A PCI quirk checks bit 6 already */
diff --git a/drivers/ide/pci/sc1200.c b/drivers/ide/pci/sc1200.c
index fc2b5496b6d..ff80937d94d 100644
--- a/drivers/ide/pci/sc1200.c
+++ b/drivers/ide/pci/sc1200.c
@@ -323,6 +323,7 @@ static void sc1200_tuneproc (ide_drive_t *drive, byte pio) /* mode=255 means "au
}
}
+#ifdef CONFIG_PM
static ide_hwif_t *lookup_pci_dev (ide_hwif_t *prev, struct pci_dev *dev)
{
int h;
@@ -451,6 +452,7 @@ static int sc1200_resume (struct pci_dev *dev)
}
return 0;
}
+#endif
/*
* This gets invoked by the IDE driver once for each channel,
@@ -499,8 +501,10 @@ static struct pci_driver driver = {
.name = "SC1200_IDE",
.id_table = sc1200_pci_tbl,
.probe = sc1200_init_one,
+#ifdef CONFIG_PM
.suspend = sc1200_suspend,
.resume = sc1200_resume,
+#endif
};
static int sc1200_ide_init(void)
diff --git a/drivers/ide/pci/serverworks.c b/drivers/ide/pci/serverworks.c
index f063d954236..057548d0720 100644
--- a/drivers/ide/pci/serverworks.c
+++ b/drivers/ide/pci/serverworks.c
@@ -359,7 +359,7 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
/* OSB4 : South Bridge and IDE */
if (dev->device == PCI_DEVICE_ID_SERVERWORKS_OSB4IDE) {
- isa_dev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+ isa_dev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_OSB4, NULL);
if (isa_dev) {
pci_read_config_dword(isa_dev, 0x64, &reg);
@@ -380,7 +380,7 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
if (!(PCI_FUNC(dev->devfn) & 1)) {
struct pci_dev * findev = NULL;
u32 reg4c = 0;
- findev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+ findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_CSB5, NULL);
if (findev) {
pci_read_config_dword(findev, 0x4C, &reg4c);
@@ -388,6 +388,7 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
reg4c |= 0x00000040;
reg4c |= 0x00000020;
pci_write_config_dword(findev, 0x4C, reg4c);
+ pci_dev_put(findev);
}
outb_p(0x06, 0x0c00);
dev->irq = inb_p(0x0c01);
@@ -395,12 +396,13 @@ static unsigned int __devinit init_chipset_svwks (struct pci_dev *dev, const cha
struct pci_dev * findev = NULL;
u8 reg41 = 0;
- findev = pci_find_device(PCI_VENDOR_ID_SERVERWORKS,
+ findev = pci_get_device(PCI_VENDOR_ID_SERVERWORKS,
PCI_DEVICE_ID_SERVERWORKS_CSB6, NULL);
if (findev) {
pci_read_config_byte(findev, 0x41, &reg41);
reg41 &= ~0x40;
pci_write_config_byte(findev, 0x41, reg41);
+ pci_dev_put(findev);
}
/*
* This is a device pin issue on CSB6.
diff --git a/drivers/ide/pci/sgiioc4.c b/drivers/ide/pci/sgiioc4.c
index d8a0d87df73..f3fe287fbd8 100644
--- a/drivers/ide/pci/sgiioc4.c
+++ b/drivers/ide/pci/sgiioc4.c
@@ -220,7 +220,7 @@ sgiioc4_ide_dma_end(ide_drive_t * drive)
ide_hwif_t *hwif = HWIF(drive);
u64 dma_base = hwif->dma_base;
int dma_stat = 0;
- unsigned long *ending_dma = (unsigned long *) hwif->dma_base2;
+ unsigned long *ending_dma = ide_get_hwifdata(hwif);
hwif->OUTL(IOC4_S_DMA_STOP, dma_base + IOC4_DMA_CTRL * 4);
@@ -369,6 +369,7 @@ ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base)
{
void __iomem *virt_dma_base;
int num_ports = sizeof (ioc4_dma_regs_t);
+ void *pad;
printk(KERN_INFO "%s: BM-DMA at 0x%04lx-0x%04lx\n", hwif->name,
dma_base, dma_base + num_ports - 1);
@@ -400,17 +401,14 @@ ide_dma_sgiioc4(ide_hwif_t * hwif, unsigned long dma_base)
hwif->sg_max_nents = IOC4_PRD_ENTRIES;
- hwif->dma_base2 = (unsigned long)
- pci_alloc_consistent(hwif->pci_dev,
- IOC4_IDE_CACHELINE_SIZE,
- (dma_addr_t *) &(hwif->dma_status));
+ pad = pci_alloc_consistent(hwif->pci_dev, IOC4_IDE_CACHELINE_SIZE,
+ (dma_addr_t *) &(hwif->dma_status));
- if (!hwif->dma_base2)
- goto dma_base2alloc_failure;
-
- return;
+ if (pad) {
+ ide_set_hwifdata(hwif, pad);
+ return;
+ }
-dma_base2alloc_failure:
pci_free_consistent(hwif->pci_dev,
IOC4_PRD_ENTRIES * IOC4_PRD_BYTES,
hwif->dmatable_cpu, hwif->dmatable_dma);
@@ -476,7 +474,7 @@ sgiioc4_configure_for_dma(int dma_direction, ide_drive_t * drive)
hwif->OUTL(dma_addr, dma_base + IOC4_DMA_PTR_L * 4);
/* Address of the Ending DMA */
- memset((unsigned int *) hwif->dma_base2, 0, IOC4_IDE_CACHELINE_SIZE);
+ memset(ide_get_hwifdata(hwif), 0, IOC4_IDE_CACHELINE_SIZE);
ending_dma_addr = cpu_to_le32(hwif->dma_status);
hwif->OUTL(ending_dma_addr, dma_base + IOC4_DMA_END_ADDR * 4);
diff --git a/drivers/ide/pci/siimage.c b/drivers/ide/pci/siimage.c
index 20b392948f3..697f566fb90 100644
--- a/drivers/ide/pci/siimage.c
+++ b/drivers/ide/pci/siimage.c
@@ -898,7 +898,6 @@ static void __devinit init_mmio_iops_siimage(ide_hwif_t *hwif)
base = (unsigned long) addr;
hwif->dma_base = base + (ch ? 0x08 : 0x00);
- hwif->dma_base2 = base + (ch ? 0x18 : 0x10);
hwif->mmio = 2;
}
diff --git a/drivers/ide/pci/sis5513.c b/drivers/ide/pci/sis5513.c
index f03196c5db3..92edf76bd7a 100644
--- a/drivers/ide/pci/sis5513.c
+++ b/drivers/ide/pci/sis5513.c
@@ -739,7 +739,7 @@ static unsigned int __devinit init_chipset_sis5513 (struct pci_dev *dev, const c
for (i = 0; i < ARRAY_SIZE(SiSHostChipInfo) && !chipset_family; i++) {
- host = pci_find_device(PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, NULL);
+ host = pci_get_device(PCI_VENDOR_ID_SI, SiSHostChipInfo[i].host_id, NULL);
if (!host)
continue;
@@ -753,6 +753,7 @@ static unsigned int __devinit init_chipset_sis5513 (struct pci_dev *dev, const c
if (hostrev >= 0x30)
chipset_family = ATA_100a;
}
+ pci_dev_put(host);
printk(KERN_INFO "SIS5513: %s %s controller\n",
SiSHostChipInfo[i].name, chipset_capability[chipset_family]);
diff --git a/drivers/ide/pci/via82cxxx.c b/drivers/ide/pci/via82cxxx.c
index 9b7589e8e93..2af634d7acf 100644
--- a/drivers/ide/pci/via82cxxx.c
+++ b/drivers/ide/pci/via82cxxx.c
@@ -248,7 +248,7 @@ static struct via_isa_bridge *via_config_find(struct pci_dev **isa)
u8 t;
for (via_config = via_isa_bridges; via_config->id; via_config++)
- if ((*isa = pci_find_device(PCI_VENDOR_ID_VIA +
+ if ((*isa = pci_get_device(PCI_VENDOR_ID_VIA +
!!(via_config->flags & VIA_BAD_ID),
via_config->id, NULL))) {
@@ -256,6 +256,7 @@ static struct via_isa_bridge *via_config_find(struct pci_dev **isa)
if (t >= via_config->rev_min &&
t <= via_config->rev_max)
break;
+ pci_dev_put(*isa);
}
return via_config;
@@ -283,6 +284,7 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const
via_config = via_config_find(&isa);
if (!via_config->id) {
printk(KERN_WARNING "VP_IDE: Unknown VIA SouthBridge, disabling DMA.\n");
+ pci_dev_put(isa);
return -ENODEV;
}
@@ -361,6 +363,7 @@ static unsigned int __devinit init_chipset_via82cxxx(struct pci_dev *dev, const
via_dma[via_config->flags & VIA_UDMA],
pci_name(dev));
+ pci_dev_put(isa);
return 0;
}
diff --git a/drivers/ide/ppc/pmac.c b/drivers/ide/ppc/pmac.c
index 996c694341b..31ad79f52df 100644
--- a/drivers/ide/ppc/pmac.c
+++ b/drivers/ide/ppc/pmac.c
@@ -1369,15 +1369,16 @@ pmac_ide_macio_attach(struct macio_dev *mdev, const struct of_device_id *match)
}
static int
-pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t state)
+pmac_ide_macio_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
ide_hwif_t *hwif = (ide_hwif_t *)dev_get_drvdata(&mdev->ofdev.dev);
int rc = 0;
- if (state.event != mdev->ofdev.dev.power.power_state.event && state.event >= PM_EVENT_SUSPEND) {
+ if (mesg.event != mdev->ofdev.dev.power.power_state.event
+ && mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif);
if (rc == 0)
- mdev->ofdev.dev.power.power_state = state;
+ mdev->ofdev.dev.power.power_state = mesg;
}
return rc;
@@ -1473,15 +1474,16 @@ pmac_ide_pci_attach(struct pci_dev *pdev, const struct pci_device_id *id)
}
static int
-pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+pmac_ide_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
ide_hwif_t *hwif = (ide_hwif_t *)pci_get_drvdata(pdev);
int rc = 0;
- if (state.event != pdev->dev.power.power_state.event && state.event >= 2) {
+ if (mesg.event != pdev->dev.power.power_state.event
+ && mesg.event == PM_EVENT_SUSPEND) {
rc = pmac_ide_do_suspend(hwif);
if (rc == 0)
- pdev->dev.power.power_state = state;
+ pdev->dev.power.power_state = mesg;
}
return rc;
diff --git a/drivers/ide/setup-pci.c b/drivers/ide/setup-pci.c
index eb0945284ac..0719b648482 100644
--- a/drivers/ide/setup-pci.c
+++ b/drivers/ide/setup-pci.c
@@ -101,7 +101,7 @@ static ide_hwif_t *ide_match_hwif(unsigned long io_base, u8 bootable, const char
return hwif; /* pick an unused entry */
}
}
- for (h = 0; h < 2; ++h) {
+ for (h = 0; h < 2 && h < MAX_HWIFS; ++h) {
hwif = ide_hwifs + h;
if (hwif->chipset == ide_unknown)
return hwif; /* pick an unused entry */
@@ -795,24 +795,6 @@ int __ide_pci_register_driver(struct pci_driver *driver, struct module *module)
EXPORT_SYMBOL_GPL(__ide_pci_register_driver);
/**
- * ide_unregister_pci_driver - unregister an IDE driver
- * @driver: driver to remove
- *
- * Unregister a currently installed IDE driver. Returns are the same
- * as for pci_unregister_driver
- */
-
-void ide_pci_unregister_driver(struct pci_driver *driver)
-{
- if(!pre_init)
- pci_unregister_driver(driver);
- else
- list_del(&driver->node);
-}
-
-EXPORT_SYMBOL_GPL(ide_pci_unregister_driver);
-
-/**
* ide_scan_pcidev - find an IDE driver for a device
* @dev: PCI device to check
*
diff --git a/drivers/ieee1394/Kconfig b/drivers/ieee1394/Kconfig
index 186737539cf..2769e505f05 100644
--- a/drivers/ieee1394/Kconfig
+++ b/drivers/ieee1394/Kconfig
@@ -120,12 +120,19 @@ config IEEE1394_VIDEO1394
this option only if you have an IEEE 1394 video device connected to
an OHCI-1394 card.
+comment "SBP-2 support (for storage devices) requires SCSI"
+ depends on IEEE1394 && SCSI=n
+
config IEEE1394_SBP2
tristate "SBP-2 support (Harddisks etc.)"
depends on IEEE1394 && SCSI && (PCI || BROKEN)
help
- This option enables you to use SBP-2 devices connected to your IEEE
- 1394 bus. SBP-2 devices include harddrives and DVD devices.
+ This option enables you to use SBP-2 devices connected to an IEEE
+ 1394 bus. SBP-2 devices include storage devices like harddisks and
+ DVD drives, also some other FireWire devices like scanners.
+
+ You should also enable support for disks, CD-ROMs, etc. in the SCSI
+ configuration section.
config IEEE1394_SBP2_PHYS_DMA
bool "Enable replacement for physical DMA in SBP2"
diff --git a/drivers/ieee1394/csr.c b/drivers/ieee1394/csr.c
index 149573db91c..ab0c80f61b9 100644
--- a/drivers/ieee1394/csr.c
+++ b/drivers/ieee1394/csr.c
@@ -17,11 +17,13 @@
*
*/
-#include <linux/string.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/param.h>
#include <linux/spinlock.h>
+#include <linux/string.h>
#include "csr1212.h"
#include "ieee1394_types.h"
@@ -149,31 +151,18 @@ static void host_reset(struct hpsb_host *host)
/*
* HI == seconds (bits 0:2)
- * LO == fraction units of 1/8000 of a second, as per 1394 (bits 19:31)
- *
- * Convert to units and then to HZ, for comparison to jiffies.
- *
- * By default this will end up being 800 units, or 100ms (125usec per
- * unit).
+ * LO == fractions of a second in units of 125usec (bits 19:31)
*
- * NOTE: The spec says 1/8000, but also says we can compute based on 1/8192
- * like CSR specifies. Should make our math less complex.
+ * Convert SPLIT_TIMEOUT to jiffies.
+ * The default and minimum as per 1394a-2000 clause 8.3.2.2.6 is 100ms.
*/
static inline void calculate_expire(struct csr_control *csr)
{
- unsigned long units;
-
- /* Take the seconds, and convert to units */
- units = (unsigned long)(csr->split_timeout_hi & 0x07) << 13;
-
- /* Add in the fractional units */
- units += (unsigned long)(csr->split_timeout_lo >> 19);
-
- /* Convert to jiffies */
- csr->expire = (unsigned long)(units * HZ) >> 13UL;
+ unsigned long usecs =
+ (csr->split_timeout_hi & 0x07) * USEC_PER_SEC +
+ (csr->split_timeout_lo >> 19) * 125L;
- /* Just to keep from rounding low */
- csr->expire++;
+ csr->expire = usecs_to_jiffies(usecs > 100000L ? usecs : 100000L);
HPSB_VERBOSE("CSR: setting expire to %lu, HZ=%u", csr->expire, HZ);
}
diff --git a/drivers/ieee1394/csr.h b/drivers/ieee1394/csr.h
index ea9aa4f53ab..f11546550d8 100644
--- a/drivers/ieee1394/csr.h
+++ b/drivers/ieee1394/csr.h
@@ -1,75 +1,73 @@
-
#ifndef _IEEE1394_CSR_H
#define _IEEE1394_CSR_H
-#ifdef CONFIG_PREEMPT
-#include <linux/sched.h>
-#endif
+#include <linux/spinlock_types.h>
#include "csr1212.h"
+#include "ieee1394_types.h"
-#define CSR_REGISTER_BASE 0xfffff0000000ULL
+#define CSR_REGISTER_BASE 0xfffff0000000ULL
/* register offsets relative to CSR_REGISTER_BASE */
-#define CSR_STATE_CLEAR 0x0
-#define CSR_STATE_SET 0x4
-#define CSR_NODE_IDS 0x8
-#define CSR_RESET_START 0xc
-#define CSR_SPLIT_TIMEOUT_HI 0x18
-#define CSR_SPLIT_TIMEOUT_LO 0x1c
-#define CSR_CYCLE_TIME 0x200
-#define CSR_BUS_TIME 0x204
-#define CSR_BUSY_TIMEOUT 0x210
-#define CSR_BUS_MANAGER_ID 0x21c
-#define CSR_BANDWIDTH_AVAILABLE 0x220
-#define CSR_CHANNELS_AVAILABLE 0x224
-#define CSR_CHANNELS_AVAILABLE_HI 0x224
-#define CSR_CHANNELS_AVAILABLE_LO 0x228
-#define CSR_BROADCAST_CHANNEL 0x234
-#define CSR_CONFIG_ROM 0x400
-#define CSR_CONFIG_ROM_END 0x800
-#define CSR_FCP_COMMAND 0xB00
-#define CSR_FCP_RESPONSE 0xD00
-#define CSR_FCP_END 0xF00
-#define CSR_TOPOLOGY_MAP 0x1000
-#define CSR_TOPOLOGY_MAP_END 0x1400
-#define CSR_SPEED_MAP 0x2000
-#define CSR_SPEED_MAP_END 0x3000
+#define CSR_STATE_CLEAR 0x0
+#define CSR_STATE_SET 0x4
+#define CSR_NODE_IDS 0x8
+#define CSR_RESET_START 0xc
+#define CSR_SPLIT_TIMEOUT_HI 0x18
+#define CSR_SPLIT_TIMEOUT_LO 0x1c
+#define CSR_CYCLE_TIME 0x200
+#define CSR_BUS_TIME 0x204
+#define CSR_BUSY_TIMEOUT 0x210
+#define CSR_BUS_MANAGER_ID 0x21c
+#define CSR_BANDWIDTH_AVAILABLE 0x220
+#define CSR_CHANNELS_AVAILABLE 0x224
+#define CSR_CHANNELS_AVAILABLE_HI 0x224
+#define CSR_CHANNELS_AVAILABLE_LO 0x228
+#define CSR_BROADCAST_CHANNEL 0x234
+#define CSR_CONFIG_ROM 0x400
+#define CSR_CONFIG_ROM_END 0x800
+#define CSR_FCP_COMMAND 0xB00
+#define CSR_FCP_RESPONSE 0xD00
+#define CSR_FCP_END 0xF00
+#define CSR_TOPOLOGY_MAP 0x1000
+#define CSR_TOPOLOGY_MAP_END 0x1400
+#define CSR_SPEED_MAP 0x2000
+#define CSR_SPEED_MAP_END 0x3000
/* IEEE 1394 bus specific Configuration ROM Key IDs */
#define IEEE1394_KV_ID_POWER_REQUIREMENTS (0x30)
-/* IEEE 1394 Bus Inforamation Block specifics */
+/* IEEE 1394 Bus Information Block specifics */
#define CSR_BUS_INFO_SIZE (5 * sizeof(quadlet_t))
-#define CSR_IRMC_SHIFT 31
-#define CSR_CMC_SHIFT 30
-#define CSR_ISC_SHIFT 29
-#define CSR_BMC_SHIFT 28
-#define CSR_PMC_SHIFT 27
-#define CSR_CYC_CLK_ACC_SHIFT 16
-#define CSR_MAX_REC_SHIFT 12
-#define CSR_MAX_ROM_SHIFT 8
-#define CSR_GENERATION_SHIFT 4
+#define CSR_IRMC_SHIFT 31
+#define CSR_CMC_SHIFT 30
+#define CSR_ISC_SHIFT 29
+#define CSR_BMC_SHIFT 28
+#define CSR_PMC_SHIFT 27
+#define CSR_CYC_CLK_ACC_SHIFT 16
+#define CSR_MAX_REC_SHIFT 12
+#define CSR_MAX_ROM_SHIFT 8
+#define CSR_GENERATION_SHIFT 4
#define CSR_SET_BUS_INFO_GENERATION(csr, gen) \
((csr)->bus_info_data[2] = \
cpu_to_be32((be32_to_cpu((csr)->bus_info_data[2]) & \
- ~(0xf << CSR_GENERATION_SHIFT)) | \
+ ~(0xf << CSR_GENERATION_SHIFT)) | \
(gen) << CSR_GENERATION_SHIFT))
struct csr_control {
- spinlock_t lock;
-
- quadlet_t state;
- quadlet_t node_ids;
- quadlet_t split_timeout_hi, split_timeout_lo;
- unsigned long expire; // Calculated from split_timeout
- quadlet_t cycle_time;
- quadlet_t bus_time;
- quadlet_t bus_manager_id;
- quadlet_t bandwidth_available;
- quadlet_t channels_available_hi, channels_available_lo;
+ spinlock_t lock;
+
+ quadlet_t state;
+ quadlet_t node_ids;
+ quadlet_t split_timeout_hi, split_timeout_lo;
+ unsigned long expire; /* Calculated from split_timeout */
+ quadlet_t cycle_time;
+ quadlet_t bus_time;
+ quadlet_t bus_manager_id;
+ quadlet_t bandwidth_available;
+ quadlet_t channels_available_hi, channels_available_lo;
quadlet_t broadcast_channel;
/* Bus Info */
@@ -84,8 +82,8 @@ struct csr_control {
struct csr1212_csr *rom;
- quadlet_t topology_map[256];
- quadlet_t speed_map[1024];
+ quadlet_t topology_map[256];
+ quadlet_t speed_map[1024];
};
extern struct csr1212_bus_ops csr_bus_ops;
@@ -93,4 +91,9 @@ extern struct csr1212_bus_ops csr_bus_ops;
int init_csr(void);
void cleanup_csr(void);
+/* hpsb_update_config_rom() is deprecated */
+struct hpsb_host;
+int hpsb_update_config_rom(struct hpsb_host *host, const quadlet_t *new_rom,
+ size_t size, unsigned char rom_version);
+
#endif /* _IEEE1394_CSR_H */
diff --git a/drivers/ieee1394/dma.c b/drivers/ieee1394/dma.c
index ca5167de707..c68f328e1a2 100644
--- a/drivers/ieee1394/dma.c
+++ b/drivers/ieee1394/dma.c
@@ -7,10 +7,13 @@
* directory of the kernel sources for details.
*/
+#include <linux/mm.h>
#include <linux/module.h>
-#include <linux/vmalloc.h>
+#include <linux/pci.h>
#include <linux/slab.h>
-#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <asm/scatterlist.h>
+
#include "dma.h"
/* dma_prog_region */
diff --git a/drivers/ieee1394/dma.h b/drivers/ieee1394/dma.h
index 061550a6fb9..a1682aba71c 100644
--- a/drivers/ieee1394/dma.h
+++ b/drivers/ieee1394/dma.h
@@ -10,69 +10,91 @@
#ifndef IEEE1394_DMA_H
#define IEEE1394_DMA_H
-#include <linux/pci.h>
-#include <asm/scatterlist.h>
-
-/* struct dma_prog_region
-
- a small, physically-contiguous DMA buffer with random-access,
- synchronous usage characteristics
-*/
-
+#include <asm/types.h>
+
+struct pci_dev;
+struct scatterlist;
+struct vm_area_struct;
+
+/**
+ * struct dma_prog_region - small contiguous DMA buffer
+ * @kvirt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @bus_addr: base bus address
+ *
+ * a small, physically contiguous DMA buffer with random-access, synchronous
+ * usage characteristics
+ */
struct dma_prog_region {
- unsigned char *kvirt; /* kernel virtual address */
- struct pci_dev *dev; /* PCI device */
- unsigned int n_pages; /* # of kernel pages */
- dma_addr_t bus_addr; /* base bus address */
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ dma_addr_t bus_addr;
};
/* clear out all fields but do not allocate any memory */
void dma_prog_region_init(struct dma_prog_region *prog);
-int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes, struct pci_dev *dev);
+int dma_prog_region_alloc(struct dma_prog_region *prog, unsigned long n_bytes,
+ struct pci_dev *dev);
void dma_prog_region_free(struct dma_prog_region *prog);
-static inline dma_addr_t dma_prog_region_offset_to_bus(struct dma_prog_region *prog, unsigned long offset)
+static inline dma_addr_t dma_prog_region_offset_to_bus(
+ struct dma_prog_region *prog, unsigned long offset)
{
return prog->bus_addr + offset;
}
-/* struct dma_region
-
- a large, non-physically-contiguous DMA buffer with streaming,
- asynchronous usage characteristics
-*/
-
+/**
+ * struct dma_region - large non-contiguous DMA buffer
+ * @virt: kernel virtual address
+ * @dev: PCI device
+ * @n_pages: number of kernel pages
+ * @n_dma_pages: number of IOMMU pages
+ * @sglist: IOMMU mapping
+ * @direction: PCI_DMA_TODEVICE, etc.
+ *
+ * a large, non-physically-contiguous DMA buffer with streaming, asynchronous
+ * usage characteristics
+ */
struct dma_region {
- unsigned char *kvirt; /* kernel virtual address */
- struct pci_dev *dev; /* PCI device */
- unsigned int n_pages; /* # of kernel pages */
- unsigned int n_dma_pages; /* # of IOMMU pages */
- struct scatterlist *sglist; /* IOMMU mapping */
- int direction; /* PCI_DMA_TODEVICE, etc */
+ unsigned char *kvirt;
+ struct pci_dev *dev;
+ unsigned int n_pages;
+ unsigned int n_dma_pages;
+ struct scatterlist *sglist;
+ int direction;
};
/* clear out all fields but do not allocate anything */
void dma_region_init(struct dma_region *dma);
/* allocate the buffer and map it to the IOMMU */
-int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes, struct pci_dev *dev, int direction);
+int dma_region_alloc(struct dma_region *dma, unsigned long n_bytes,
+ struct pci_dev *dev, int direction);
/* unmap and free the buffer */
void dma_region_free(struct dma_region *dma);
/* sync the CPU's view of the buffer */
-void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset, unsigned long len);
+void dma_region_sync_for_cpu(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
+
/* sync the IO bus' view of the buffer */
-void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset, unsigned long len);
+void dma_region_sync_for_device(struct dma_region *dma, unsigned long offset,
+ unsigned long len);
/* map the buffer into a user space process */
-int dma_region_mmap(struct dma_region *dma, struct file *file, struct vm_area_struct *vma);
+int dma_region_mmap(struct dma_region *dma, struct file *file,
+ struct vm_area_struct *vma);
/* macro to index into a DMA region (or dma_prog_region) */
-#define dma_region_i(_dma, _type, _index) ( ((_type*) ((_dma)->kvirt)) + (_index) )
+#define dma_region_i(_dma, _type, _index) \
+ ( ((_type*) ((_dma)->kvirt)) + (_index) )
/* return the DMA bus address of the byte with the given offset
- relative to the beginning of the dma_region */
-dma_addr_t dma_region_offset_to_bus(struct dma_region *dma, unsigned long offset);
+ * relative to the beginning of the dma_region */
+dma_addr_t dma_region_offset_to_bus(struct dma_region *dma,
+ unsigned long offset);
#endif /* IEEE1394_DMA_H */
diff --git a/drivers/ieee1394/dv1394-private.h b/drivers/ieee1394/dv1394-private.h
index 80b5ac7fe38..7d1d2845b42 100644
--- a/drivers/ieee1394/dv1394-private.h
+++ b/drivers/ieee1394/dv1394-private.h
@@ -460,7 +460,7 @@ struct video_card {
int dma_running;
/*
- 3) the sleeping semaphore 'sem' - this is used from process context only,
+ 3) the sleeping mutex 'mtx' - this is used from process context only,
to serialize various operations on the video_card. Even though only one
open() is allowed, we still need to prevent multiple threads of execution
from entering calls like read, write, ioctl, etc.
@@ -468,9 +468,9 @@ struct video_card {
I honestly can't think of a good reason to use dv1394 from several threads
at once, but we need to serialize anyway to prevent oopses =).
- NOTE: if you need both spinlock and sem, take sem first to avoid deadlock!
+ NOTE: if you need both spinlock and mtx, take mtx first to avoid deadlock!
*/
- struct semaphore sem;
+ struct mutex mtx;
/* people waiting for buffer space, please form a line here... */
wait_queue_head_t waitq;
diff --git a/drivers/ieee1394/dv1394.c b/drivers/ieee1394/dv1394.c
index 87532dd4337..6c72f04b2b5 100644
--- a/drivers/ieee1394/dv1394.c
+++ b/drivers/ieee1394/dv1394.c
@@ -95,6 +95,7 @@
#include <linux/fs.h>
#include <linux/poll.h>
#include <linux/smp_lock.h>
+#include <linux/mutex.h>
#include <linux/bitops.h>
#include <asm/byteorder.h>
#include <asm/atomic.h>
@@ -110,15 +111,15 @@
#include <linux/compat.h>
#include <linux/cdev.h>
+#include "dv1394.h"
+#include "dv1394-private.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
+#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_types.h"
#include "nodemgr.h"
-#include "hosts.h"
-#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "dv1394.h"
-#include "dv1394-private.h"
-
#include "ohci1394.h"
/* DEBUG LEVELS:
@@ -136,13 +137,13 @@
#if DV1394_DEBUG_LEVEL >= 2
#define irq_printk( args... ) printk( args )
#else
-#define irq_printk( args... )
+#define irq_printk( args... ) do {} while (0)
#endif
#if DV1394_DEBUG_LEVEL >= 1
#define debug_printk( args... ) printk( args)
#else
-#define debug_printk( args... )
+#define debug_printk( args... ) do {} while (0)
#endif
/* issue a dummy PCI read to force the preceding write
@@ -247,7 +248,7 @@ static void frame_delete(struct frame *f)
Frame_prepare() must be called OUTSIDE the video->spinlock.
However, frame_prepare() must still be serialized, so
- it should be called WITH the video->sem taken.
+ it should be called WITH the video->mtx taken.
*/
static void frame_prepare(struct video_card *video, unsigned int this_frame)
@@ -1271,7 +1272,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
int retval = -EINVAL;
/* serialize mmap */
- down(&video->sem);
+ mutex_lock(&video->mtx);
if ( ! video_card_initialized(video) ) {
retval = do_dv1394_init_default(video);
@@ -1281,7 +1282,7 @@ static int dv1394_mmap(struct file *file, struct vm_area_struct *vma)
retval = dma_region_mmap(&video->dv_buf, file, vma);
out:
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return retval;
}
@@ -1337,17 +1338,17 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem))
+ if (!mutex_trylock(&video->mtx))
return -EAGAIN;
} else {
- if (down_interruptible(&video->sem))
+ if (mutex_lock_interruptible(&video->mtx))
return -ERESTARTSYS;
}
if ( !video_card_initialized(video) ) {
ret = do_dv1394_init_default(video);
if (ret) {
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
}
@@ -1418,7 +1419,7 @@ static ssize_t dv1394_write(struct file *file, const char __user *buffer, size_t
remove_wait_queue(&video->waitq, &wait);
set_current_state(TASK_RUNNING);
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
@@ -1434,17 +1435,17 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem))
+ if (!mutex_trylock(&video->mtx))
return -EAGAIN;
} else {
- if (down_interruptible(&video->sem))
+ if (mutex_lock_interruptible(&video->mtx))
return -ERESTARTSYS;
}
if ( !video_card_initialized(video) ) {
ret = do_dv1394_init_default(video);
if (ret) {
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
video->continuity_counter = -1;
@@ -1526,7 +1527,7 @@ static ssize_t dv1394_read(struct file *file, char __user *buffer, size_t count
remove_wait_queue(&video->waitq, &wait);
set_current_state(TASK_RUNNING);
- up(&video->sem);
+ mutex_unlock(&video->mtx);
return ret;
}
@@ -1547,12 +1548,12 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
/* serialize this to prevent multi-threaded mayhem */
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&video->sem)) {
+ if (!mutex_trylock(&video->mtx)) {
unlock_kernel();
return -EAGAIN;
}
} else {
- if (down_interruptible(&video->sem)) {
+ if (mutex_lock_interruptible(&video->mtx)) {
unlock_kernel();
return -ERESTARTSYS;
}
@@ -1778,7 +1779,7 @@ static long dv1394_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
}
out:
- up(&video->sem);
+ mutex_unlock(&video->mtx);
unlock_kernel();
return ret;
}
@@ -2253,7 +2254,7 @@ static int dv1394_init(struct ti_ohci *ohci, enum pal_or_ntsc format, enum modes
clear_bit(0, &video->open);
spin_lock_init(&video->spinlock);
video->dma_running = 0;
- init_MUTEX(&video->sem);
+ mutex_init(&video->mtx);
init_waitqueue_head(&video->waitq);
video->fasync = NULL;
diff --git a/drivers/ieee1394/eth1394.c b/drivers/ieee1394/eth1394.c
index 2d5b57be98c..8a7b8fab623 100644
--- a/drivers/ieee1394/eth1394.c
+++ b/drivers/ieee1394/eth1394.c
@@ -64,19 +64,19 @@
#include <linux/ethtool.h>
#include <asm/uaccess.h>
#include <asm/delay.h>
-#include <asm/semaphore.h>
#include <net/arp.h>
+#include "config_roms.h"
#include "csr1212.h"
-#include "ieee1394_types.h"
+#include "eth1394.h"
+#include "highlevel.h"
+#include "ieee1394.h"
#include "ieee1394_core.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
-#include "ieee1394.h"
-#include "highlevel.h"
+#include "ieee1394_types.h"
#include "iso.h"
#include "nodemgr.h"
-#include "eth1394.h"
-#include "config_roms.h"
#define ETH1394_PRINT_G(level, fmt, args...) \
printk(level "%s: " fmt, driver_name, ## args)
diff --git a/drivers/ieee1394/highlevel.h b/drivers/ieee1394/highlevel.h
index e119fb87e5b..50f2dd2c7e2 100644
--- a/drivers/ieee1394/highlevel.h
+++ b/drivers/ieee1394/highlevel.h
@@ -1,60 +1,61 @@
-
#ifndef IEEE1394_HIGHLEVEL_H
#define IEEE1394_HIGHLEVEL_H
+#include <linux/list.h>
+#include <linux/spinlock_types.h>
+#include <linux/types.h>
-struct hpsb_address_serve {
- struct list_head host_list; /* per host list */
+struct module;
- struct list_head hl_list; /* hpsb_highlevel list */
+#include "ieee1394_types.h"
- struct hpsb_address_ops *op;
+struct hpsb_host;
+/* internal to ieee1394 core */
+struct hpsb_address_serve {
+ struct list_head host_list; /* per host list */
+ struct list_head hl_list; /* hpsb_highlevel list */
+ struct hpsb_address_ops *op;
struct hpsb_host *host;
-
- /* first address handled and first address behind, quadlet aligned */
- u64 start, end;
+ u64 start; /* first address handled, quadlet aligned */
+ u64 end; /* first address behind, quadlet aligned */
};
-
-/*
- * The above structs are internal to highlevel driver handling. Only the
- * following structures are of interest to actual highlevel drivers.
- */
+/* Only the following structures are of interest to actual highlevel drivers. */
struct hpsb_highlevel {
struct module *owner;
const char *name;
- /* Any of the following pointers can legally be NULL, except for
- * iso_receive which can only be NULL when you don't request
- * channels. */
+ /* Any of the following pointers can legally be NULL, except for
+ * iso_receive which can only be NULL when you don't request
+ * channels. */
- /* New host initialized. Will also be called during
- * hpsb_register_highlevel for all hosts already installed. */
- void (*add_host) (struct hpsb_host *host);
+ /* New host initialized. Will also be called during
+ * hpsb_register_highlevel for all hosts already installed. */
+ void (*add_host)(struct hpsb_host *host);
- /* Host about to be removed. Will also be called during
- * hpsb_unregister_highlevel once for each host. */
- void (*remove_host) (struct hpsb_host *host);
+ /* Host about to be removed. Will also be called during
+ * hpsb_unregister_highlevel once for each host. */
+ void (*remove_host)(struct hpsb_host *host);
- /* Host experienced bus reset with possible configuration changes.
+ /* Host experienced bus reset with possible configuration changes.
* Note that this one may occur during interrupt/bottom half handling.
* You can not expect to be able to do stock hpsb_reads. */
- void (*host_reset) (struct hpsb_host *host);
+ void (*host_reset)(struct hpsb_host *host);
- /* An isochronous packet was received. Channel contains the channel
- * number for your convenience, it is also contained in the included
- * packet header (first quadlet, CRCs are missing). You may get called
- * for channel/host combinations you did not request. */
- void (*iso_receive) (struct hpsb_host *host, int channel,
- quadlet_t *data, size_t length);
+ /* An isochronous packet was received. Channel contains the channel
+ * number for your convenience, it is also contained in the included
+ * packet header (first quadlet, CRCs are missing). You may get called
+ * for channel/host combinations you did not request. */
+ void (*iso_receive)(struct hpsb_host *host, int channel,
+ quadlet_t *data, size_t length);
- /* A write request was received on either the FCP_COMMAND (direction =
- * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
- * contains the cts field (first byte of data). */
- void (*fcp_request) (struct hpsb_host *host, int nodeid, int direction,
- int cts, u8 *data, size_t length);
+ /* A write request was received on either the FCP_COMMAND (direction =
+ * 0) or the FCP_RESPONSE (direction = 1) register. The cts arg
+ * contains the cts field (first byte of data). */
+ void (*fcp_request)(struct hpsb_host *host, int nodeid, int direction,
+ int cts, u8 *data, size_t length);
/* These are initialized by the subsystem when the
* hpsb_higlevel is registered. */
@@ -67,61 +68,62 @@ struct hpsb_highlevel {
};
struct hpsb_address_ops {
- /*
- * Null function pointers will make the respective operation complete
- * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
- * registers (just leave everything but read NULL).
- *
- * All functions shall return appropriate IEEE 1394 rcodes.
- */
-
- /* These functions have to implement block reads for themselves. */
- /* These functions either return a response code
- or a negative number. In the first case a response will be generated; in the
- later case, no response will be sent and the driver, that handled the request
- will send the response itself
- */
- int (*read) (struct hpsb_host *host, int nodeid, quadlet_t *buffer,
- u64 addr, size_t length, u16 flags);
- int (*write) (struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 flags);
-
- /* Lock transactions: write results of ext_tcode operation into
- * *store. */
- int (*lock) (struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags);
- int (*lock64) (struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
+ /*
+ * Null function pointers will make the respective operation complete
+ * with RCODE_TYPE_ERROR. Makes for easy to implement read-only
+ * registers (just leave everything but read NULL).
+ *
+ * All functions shall return appropriate IEEE 1394 rcodes.
+ */
+
+ /* These functions have to implement block reads for themselves.
+ *
+ * These functions either return a response code or a negative number.
+ * In the first case a response will be generated. In the latter case,
+ * no response will be sent and the driver which handled the request
+ * will send the response itself. */
+ int (*read)(struct hpsb_host *host, int nodeid, quadlet_t *buffer,
+ u64 addr, size_t length, u16 flags);
+ int (*write)(struct hpsb_host *host, int nodeid, int destid,
+ quadlet_t *data, u64 addr, size_t length, u16 flags);
+
+ /* Lock transactions: write results of ext_tcode operation into
+ * *store. */
+ int (*lock)(struct hpsb_host *host, int nodeid, quadlet_t *store,
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
+ int (*lock64)(struct hpsb_host *host, int nodeid, octlet_t *store,
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
};
-
void highlevel_add_host(struct hpsb_host *host);
void highlevel_remove_host(struct hpsb_host *host);
void highlevel_host_reset(struct hpsb_host *host);
-
-/* these functions are called to handle transactions. They are called, when
- a packet arrives. The flags argument contains the second word of the first header
- quadlet of the incoming packet (containing transaction label, retry code,
- transaction code and priority). These functions either return a response code
- or a negative number. In the first case a response will be generated; in the
- later case, no response will be sent and the driver, that handled the request
- will send the response itself.
-*/
-int highlevel_read(struct hpsb_host *host, int nodeid, void *data,
- u64 addr, unsigned int length, u16 flags);
-int highlevel_write(struct hpsb_host *host, int nodeid, int destid,
- void *data, u64 addr, unsigned int length, u16 flags);
+/*
+ * These functions are called to handle transactions. They are called when a
+ * packet arrives. The flags argument contains the second word of the first
+ * header quadlet of the incoming packet (containing transaction label, retry
+ * code, transaction code and priority). These functions either return a
+ * response code or a negative number. In the first case a response will be
+ * generated. In the latter case, no response will be sent and the driver which
+ * handled the request will send the response itself.
+ */
+int highlevel_read(struct hpsb_host *host, int nodeid, void *data, u64 addr,
+ unsigned int length, u16 flags);
+int highlevel_write(struct hpsb_host *host, int nodeid, int destid, void *data,
+ u64 addr, unsigned int length, u16 flags);
int highlevel_lock(struct hpsb_host *host, int nodeid, quadlet_t *store,
- u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode, u16 flags);
+ u64 addr, quadlet_t data, quadlet_t arg, int ext_tcode,
+ u16 flags);
int highlevel_lock64(struct hpsb_host *host, int nodeid, octlet_t *store,
- u64 addr, octlet_t data, octlet_t arg, int ext_tcode, u16 flags);
+ u64 addr, octlet_t data, octlet_t arg, int ext_tcode,
+ u16 flags);
-void highlevel_iso_receive(struct hpsb_host *host, void *data,
- size_t length);
+void highlevel_iso_receive(struct hpsb_host *host, void *data, size_t length);
void highlevel_fcp_request(struct hpsb_host *host, int nodeid, int direction,
- void *data, size_t length);
-
+ void *data, size_t length);
/*
* Register highlevel driver. The name pointer has to stay valid at all times
@@ -132,13 +134,15 @@ void hpsb_unregister_highlevel(struct hpsb_highlevel *hl);
/*
* Register handlers for host address spaces. Start and end are 48 bit pointers
- * and have to be quadlet aligned (end points to the first address behind the
- * handled addresses. This function can be called multiple times for a single
- * hpsb_highlevel to implement sparse register sets. The requested region must
- * not overlap any previously allocated region, otherwise registering will fail.
+ * and have to be quadlet aligned. Argument "end" points to the first address
+ * behind the handled addresses. This function can be called multiple times for
+ * a single hpsb_highlevel to implement sparse register sets. The requested
+ * region must not overlap any previously allocated region, otherwise
+ * registering will fail.
*
- * It returns true for successful allocation. There is no unregister function,
- * all address spaces are deallocated together with the hpsb_highlevel.
+ * It returns true for successful allocation. Address spaces can be
+ * unregistered with hpsb_unregister_addrspace. All remaining address spaces
+ * are automatically deallocated together with the hpsb_highlevel.
*/
u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
struct hpsb_host *host,
@@ -146,20 +150,18 @@ u64 hpsb_allocate_and_register_addrspace(struct hpsb_highlevel *hl,
u64 size, u64 alignment,
u64 start, u64 end);
int hpsb_register_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- struct hpsb_address_ops *ops, u64 start, u64 end);
-
+ struct hpsb_address_ops *ops, u64 start, u64 end);
int hpsb_unregister_addrspace(struct hpsb_highlevel *hl, struct hpsb_host *host,
- u64 start);
+ u64 start);
/*
* Enable or disable receving a certain isochronous channel through the
* iso_receive op.
*/
int hpsb_listen_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned int channel);
+ unsigned int channel);
void hpsb_unlisten_channel(struct hpsb_highlevel *hl, struct hpsb_host *host,
- unsigned int channel);
-
+ unsigned int channel);
/* Retrieve a hostinfo pointer bound to this driver/host */
void *hpsb_get_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
@@ -172,19 +174,24 @@ void *hpsb_create_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
void hpsb_destroy_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host);
/* Set an alternate lookup key for the hostinfo bound to this driver/host */
-void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host, unsigned long key);
+void hpsb_set_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ unsigned long key);
-/* Retrieve the alternate lookup key for the hostinfo bound to this driver/host */
-unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl, struct hpsb_host *host);
+/* Retrieve the alternate lookup key for the hostinfo bound to this
+ * driver/host */
+unsigned long hpsb_get_hostinfo_key(struct hpsb_highlevel *hl,
+ struct hpsb_host *host);
/* Retrieve a hostinfo pointer bound to this driver using its alternate key */
void *hpsb_get_hostinfo_bykey(struct hpsb_highlevel *hl, unsigned long key);
/* Set the hostinfo pointer to something useful. Usually follows a call to
* hpsb_create_hostinfo, where the size is 0. */
-int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host, void *data);
+int hpsb_set_hostinfo(struct hpsb_highlevel *hl, struct hpsb_host *host,
+ void *data);
/* Retrieve hpsb_host using a highlevel handle and a key */
-struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl, unsigned long key);
+struct hpsb_host *hpsb_get_host_bykey(struct hpsb_highlevel *hl,
+ unsigned long key);
#endif /* IEEE1394_HIGHLEVEL_H */
diff --git a/drivers/ieee1394/hosts.c b/drivers/ieee1394/hosts.c
index 4feead4a35c..d90a3a1898c 100644
--- a/drivers/ieee1394/hosts.c
+++ b/drivers/ieee1394/hosts.c
@@ -90,6 +90,16 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
return 0;
}
+/*
+ * The pending_packet_queue is special in that it's processed
+ * from hardirq context too (such as hpsb_bus_reset()). Hence
+ * split the lock class from the usual networking skb-head
+ * lock class by using a separate key for it:
+ */
+static struct lock_class_key pending_packet_queue_key;
+
+static DEFINE_MUTEX(host_num_alloc);
+
/**
* hpsb_alloc_host - allocate a new host controller.
* @drv: the driver that will manage the host controller
@@ -105,16 +115,6 @@ static int alloc_hostnum_cb(struct hpsb_host *host, void *__data)
* Return Value: a pointer to the &hpsb_host if successful, %NULL if
* no memory was available.
*/
-static DEFINE_MUTEX(host_num_alloc);
-
-/*
- * The pending_packet_queue is special in that it's processed
- * from hardirq context too (such as hpsb_bus_reset()). Hence
- * split the lock class from the usual networking skb-head
- * lock class by using a separate key for it:
- */
-static struct lock_class_key pending_packet_queue_key;
-
struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
struct device *dev)
{
@@ -143,9 +143,6 @@ struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
for (i = 2; i < 16; i++)
h->csr.gen_timestamp[i] = jiffies - 60 * HZ;
- for (i = 0; i < ARRAY_SIZE(h->tpool); i++)
- HPSB_TPOOL_INIT(&h->tpool[i]);
-
atomic_set(&h->generation, 0);
INIT_WORK(&h->delayed_reset, delayed_reset_bus, h);
diff --git a/drivers/ieee1394/hosts.h b/drivers/ieee1394/hosts.h
index 9ad4b246307..bc6dbfadb89 100644
--- a/drivers/ieee1394/hosts.h
+++ b/drivers/ieee1394/hosts.h
@@ -2,17 +2,19 @@
#define _IEEE1394_HOSTS_H
#include <linux/device.h>
-#include <linux/wait.h>
#include <linux/list.h>
-#include <linux/timer.h>
#include <linux/skbuff.h>
+#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/workqueue.h>
+#include <asm/atomic.h>
-#include <asm/semaphore.h>
+struct pci_dev;
+struct module;
#include "ieee1394_types.h"
#include "csr.h"
-
struct hpsb_packet;
struct hpsb_iso;
@@ -33,7 +35,6 @@ struct hpsb_host {
int node_count; /* number of identified nodes on this bus */
int selfid_count; /* total number of SelfIDs received */
int nodes_active; /* number of nodes with active link layer */
- u8 speed[ALL_NODES]; /* speed between each node and local node */
nodeid_t node_id; /* node ID of this host */
nodeid_t irm_id; /* ID of this bus' isochronous resource manager */
@@ -53,31 +54,29 @@ struct hpsb_host {
int reset_retries;
quadlet_t *topology_map;
u8 *speed_map;
- struct csr_control csr;
-
- /* Per node tlabel pool allocation */
- struct hpsb_tlabel_pool tpool[ALL_NODES];
+ int id;
struct hpsb_host_driver *driver;
-
struct pci_dev *pdev;
-
- int id;
-
struct device device;
struct class_device class_dev;
int update_config_rom;
struct work_struct delayed_reset;
-
unsigned int config_roms;
struct list_head addr_space;
u64 low_addr_space; /* upper bound of physical DMA area */
u64 middle_addr_space; /* upper bound of posted write area */
-};
+ u8 speed[ALL_NODES]; /* speed between each node and local node */
+
+ /* per node tlabel allocation */
+ u8 next_tl[ALL_NODES];
+ struct { DECLARE_BITMAP(map, 64); } tl_pool[ALL_NODES];
+ struct csr_control csr;
+};
enum devctl_cmd {
/* Host is requested to reset its bus and cancel all outstanding async
@@ -112,7 +111,7 @@ enum devctl_cmd {
enum isoctl_cmd {
/* rawiso API - see iso.h for the meanings of these commands
- (they correspond exactly to the hpsb_iso_* API functions)
+ * (they correspond exactly to the hpsb_iso_* API functions)
* INIT = allocate resources
* START = begin transmission/reception
* STOP = halt transmission/reception
@@ -160,7 +159,8 @@ struct hpsb_host_driver {
/* The hardware driver may optionally support a function that is used
* to set the hardware ConfigROM if the hardware supports handling
* reads to the ConfigROM on its own. */
- void (*set_hw_config_rom) (struct hpsb_host *host, quadlet_t *config_rom);
+ void (*set_hw_config_rom)(struct hpsb_host *host,
+ quadlet_t *config_rom);
/* This function shall implement packet transmission based on
* packet->type. It shall CRC both parts of the packet (unless
@@ -170,20 +170,21 @@ struct hpsb_host_driver {
* called. Return 0 on success, negative errno on failure.
* NOTE: The function must be callable in interrupt context.
*/
- int (*transmit_packet) (struct hpsb_host *host,
- struct hpsb_packet *packet);
+ int (*transmit_packet)(struct hpsb_host *host,
+ struct hpsb_packet *packet);
/* This function requests miscellanous services from the driver, see
* above for command codes and expected actions. Return -1 for unknown
* command, though that should never happen.
*/
- int (*devctl) (struct hpsb_host *host, enum devctl_cmd command, int arg);
+ int (*devctl)(struct hpsb_host *host, enum devctl_cmd command, int arg);
/* ISO transmission/reception functions. Return 0 on success, -1
* (or -EXXX errno code) on failure. If the low-level driver does not
* support the new ISO API, set isoctl to NULL.
*/
- int (*isoctl) (struct hpsb_iso *iso, enum isoctl_cmd command, unsigned long arg);
+ int (*isoctl)(struct hpsb_iso *iso, enum isoctl_cmd command,
+ unsigned long arg);
/* This function is mainly to redirect local CSR reads/locks to the iso
* management registers (bus manager id, bandwidth available, channels
@@ -196,19 +197,11 @@ struct hpsb_host_driver {
quadlet_t data, quadlet_t compare);
};
-
struct hpsb_host *hpsb_alloc_host(struct hpsb_host_driver *drv, size_t extra,
struct device *dev);
int hpsb_add_host(struct hpsb_host *host);
void hpsb_remove_host(struct hpsb_host *h);
-/* The following 2 functions are deprecated and will be removed when the
- * raw1394/libraw1394 update is complete. */
-int hpsb_update_config_rom(struct hpsb_host *host,
- const quadlet_t *new_rom, size_t size, unsigned char rom_version);
-int hpsb_get_config_rom(struct hpsb_host *host, quadlet_t *buffer,
- size_t buffersize, size_t *rom_size, unsigned char *rom_version);
-
/* Updates the configuration rom image of a host. rom_version must be the
* current version, otherwise it will fail with return value -1. If this
* host does not support config-rom-update, it will return -EINVAL.
diff --git a/drivers/ieee1394/ieee1394-ioctl.h b/drivers/ieee1394/ieee1394-ioctl.h
index 15670398634..8f207508ed1 100644
--- a/drivers/ieee1394/ieee1394-ioctl.h
+++ b/drivers/ieee1394/ieee1394-ioctl.h
@@ -1,5 +1,7 @@
-/* Base file for all ieee1394 ioctl's. Linux-1394 has allocated base '#'
- * with a range of 0x00-0x3f. */
+/*
+ * Base file for all ieee1394 ioctl's.
+ * Linux-1394 has allocated base '#' with a range of 0x00-0x3f.
+ */
#ifndef __IEEE1394_IOCTL_H
#define __IEEE1394_IOCTL_H
@@ -96,8 +98,7 @@
_IOW ('#', 0x27, struct raw1394_iso_packets)
#define RAW1394_IOC_ISO_XMIT_SYNC \
_IO ('#', 0x28)
-#define RAW1394_IOC_ISO_RECV_FLUSH \
+#define RAW1394_IOC_ISO_RECV_FLUSH \
_IO ('#', 0x29)
-
#endif /* __IEEE1394_IOCTL_H */
diff --git a/drivers/ieee1394/ieee1394.h b/drivers/ieee1394/ieee1394.h
index 936d776de00..40492074c01 100644
--- a/drivers/ieee1394/ieee1394.h
+++ b/drivers/ieee1394/ieee1394.h
@@ -5,77 +5,78 @@
#ifndef _IEEE1394_IEEE1394_H
#define _IEEE1394_IEEE1394_H
-#define TCODE_WRITEQ 0x0
-#define TCODE_WRITEB 0x1
-#define TCODE_WRITE_RESPONSE 0x2
-#define TCODE_READQ 0x4
-#define TCODE_READB 0x5
-#define TCODE_READQ_RESPONSE 0x6
-#define TCODE_READB_RESPONSE 0x7
-#define TCODE_CYCLE_START 0x8
-#define TCODE_LOCK_REQUEST 0x9
-#define TCODE_ISO_DATA 0xa
-#define TCODE_STREAM_DATA 0xa
-#define TCODE_LOCK_RESPONSE 0xb
-
-#define RCODE_COMPLETE 0x0
-#define RCODE_CONFLICT_ERROR 0x4
-#define RCODE_DATA_ERROR 0x5
-#define RCODE_TYPE_ERROR 0x6
-#define RCODE_ADDRESS_ERROR 0x7
-
-#define EXTCODE_MASK_SWAP 0x1
-#define EXTCODE_COMPARE_SWAP 0x2
-#define EXTCODE_FETCH_ADD 0x3
-#define EXTCODE_LITTLE_ADD 0x4
-#define EXTCODE_BOUNDED_ADD 0x5
-#define EXTCODE_WRAP_ADD 0x6
-
-#define ACK_COMPLETE 0x1
-#define ACK_PENDING 0x2
-#define ACK_BUSY_X 0x4
-#define ACK_BUSY_A 0x5
-#define ACK_BUSY_B 0x6
-#define ACK_TARDY 0xb
-#define ACK_CONFLICT_ERROR 0xc
-#define ACK_DATA_ERROR 0xd
-#define ACK_TYPE_ERROR 0xe
-#define ACK_ADDRESS_ERROR 0xf
+#define TCODE_WRITEQ 0x0
+#define TCODE_WRITEB 0x1
+#define TCODE_WRITE_RESPONSE 0x2
+#define TCODE_READQ 0x4
+#define TCODE_READB 0x5
+#define TCODE_READQ_RESPONSE 0x6
+#define TCODE_READB_RESPONSE 0x7
+#define TCODE_CYCLE_START 0x8
+#define TCODE_LOCK_REQUEST 0x9
+#define TCODE_ISO_DATA 0xa
+#define TCODE_STREAM_DATA 0xa
+#define TCODE_LOCK_RESPONSE 0xb
+
+#define RCODE_COMPLETE 0x0
+#define RCODE_CONFLICT_ERROR 0x4
+#define RCODE_DATA_ERROR 0x5
+#define RCODE_TYPE_ERROR 0x6
+#define RCODE_ADDRESS_ERROR 0x7
+
+#define EXTCODE_MASK_SWAP 0x1
+#define EXTCODE_COMPARE_SWAP 0x2
+#define EXTCODE_FETCH_ADD 0x3
+#define EXTCODE_LITTLE_ADD 0x4
+#define EXTCODE_BOUNDED_ADD 0x5
+#define EXTCODE_WRAP_ADD 0x6
+
+#define ACK_COMPLETE 0x1
+#define ACK_PENDING 0x2
+#define ACK_BUSY_X 0x4
+#define ACK_BUSY_A 0x5
+#define ACK_BUSY_B 0x6
+#define ACK_TARDY 0xb
+#define ACK_CONFLICT_ERROR 0xc
+#define ACK_DATA_ERROR 0xd
+#define ACK_TYPE_ERROR 0xe
+#define ACK_ADDRESS_ERROR 0xf
/* Non-standard "ACK codes" for internal use */
-#define ACKX_NONE (-1)
-#define ACKX_SEND_ERROR (-2)
-#define ACKX_ABORTED (-3)
-#define ACKX_TIMEOUT (-4)
-
-
-#define IEEE1394_SPEED_100 0x00
-#define IEEE1394_SPEED_200 0x01
-#define IEEE1394_SPEED_400 0x02
-#define IEEE1394_SPEED_800 0x03
-#define IEEE1394_SPEED_1600 0x04
-#define IEEE1394_SPEED_3200 0x05
+#define ACKX_NONE (-1)
+#define ACKX_SEND_ERROR (-2)
+#define ACKX_ABORTED (-3)
+#define ACKX_TIMEOUT (-4)
+
+#define IEEE1394_SPEED_100 0x00
+#define IEEE1394_SPEED_200 0x01
+#define IEEE1394_SPEED_400 0x02
+#define IEEE1394_SPEED_800 0x03
+#define IEEE1394_SPEED_1600 0x04
+#define IEEE1394_SPEED_3200 0x05
+
/* The current highest tested speed supported by the subsystem */
-#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800
+#define IEEE1394_SPEED_MAX IEEE1394_SPEED_800
/* Maps speed values above to a string representation */
extern const char *hpsb_speedto_str[];
-
/* 1394a cable PHY packets */
-#define SELFID_PWRCL_NO_POWER 0x0
-#define SELFID_PWRCL_PROVIDE_15W 0x1
-#define SELFID_PWRCL_PROVIDE_30W 0x2
-#define SELFID_PWRCL_PROVIDE_45W 0x3
-#define SELFID_PWRCL_USE_1W 0x4
-#define SELFID_PWRCL_USE_3W 0x5
-#define SELFID_PWRCL_USE_6W 0x6
-#define SELFID_PWRCL_USE_10W 0x7
-
-#define SELFID_PORT_CHILD 0x3
-#define SELFID_PORT_PARENT 0x2
-#define SELFID_PORT_NCONN 0x1
-#define SELFID_PORT_NONE 0x0
+#define SELFID_PWRCL_NO_POWER 0x0
+#define SELFID_PWRCL_PROVIDE_15W 0x1
+#define SELFID_PWRCL_PROVIDE_30W 0x2
+#define SELFID_PWRCL_PROVIDE_45W 0x3
+#define SELFID_PWRCL_USE_1W 0x4
+#define SELFID_PWRCL_USE_3W 0x5
+#define SELFID_PWRCL_USE_6W 0x6
+#define SELFID_PWRCL_USE_10W 0x7
+
+#define SELFID_PORT_CHILD 0x3
+#define SELFID_PORT_PARENT 0x2
+#define SELFID_PORT_NCONN 0x1
+#define SELFID_PORT_NONE 0x0
+
+#define SELFID_SPEED_UNKNOWN 0x3 /* 1394b PHY */
#define PHYPACKET_LINKON 0x40000000
#define PHYPACKET_PHYCONFIG_R 0x00800000
@@ -91,76 +92,76 @@ extern const char *hpsb_speedto_str[];
#define EXTPHYPACKET_TYPEMASK 0xC0FC0000
-#define PHYPACKET_PORT_SHIFT 24
-#define PHYPACKET_GAPCOUNT_SHIFT 16
+#define PHYPACKET_PORT_SHIFT 24
+#define PHYPACKET_GAPCOUNT_SHIFT 16
/* 1394a PHY register map bitmasks */
-#define PHY_00_PHYSICAL_ID 0xFC
-#define PHY_00_R 0x02 /* Root */
-#define PHY_00_PS 0x01 /* Power Status*/
-#define PHY_01_RHB 0x80 /* Root Hold-Off */
-#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
-#define PHY_01_GAP_COUNT 0x3F
-#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
-#define PHY_02_TOTAL_PORTS 0x1F
-#define PHY_03_MAX_SPEED 0xE0
-#define PHY_03_DELAY 0x0F
-#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
-#define PHY_04_CONTENDER 0x40
-#define PHY_04_JITTER 0x38
-#define PHY_04_PWR_CLASS 0x07 /* Power Class */
-#define PHY_05_WATCHDOG 0x80
-#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
-#define PHY_05_LOOP 0x20 /* Loop Detect */
-#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
-#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
-#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
-#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
-#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
+#define PHY_00_PHYSICAL_ID 0xFC
+#define PHY_00_R 0x02 /* Root */
+#define PHY_00_PS 0x01 /* Power Status*/
+#define PHY_01_RHB 0x80 /* Root Hold-Off */
+#define PHY_01_IBR 0x80 /* Initiate Bus Reset */
+#define PHY_01_GAP_COUNT 0x3F
+#define PHY_02_EXTENDED 0xE0 /* 0x7 for 1394a-compliant PHY */
+#define PHY_02_TOTAL_PORTS 0x1F
+#define PHY_03_MAX_SPEED 0xE0
+#define PHY_03_DELAY 0x0F
+#define PHY_04_LCTRL 0x80 /* Link Active Report Control */
+#define PHY_04_CONTENDER 0x40
+#define PHY_04_JITTER 0x38
+#define PHY_04_PWR_CLASS 0x07 /* Power Class */
+#define PHY_05_WATCHDOG 0x80
+#define PHY_05_ISBR 0x40 /* Initiate Short Bus Reset */
+#define PHY_05_LOOP 0x20 /* Loop Detect */
+#define PHY_05_PWR_FAIL 0x10 /* Cable Power Failure Detect */
+#define PHY_05_TIMEOUT 0x08 /* Arbitration State Machine Timeout */
+#define PHY_05_PORT_EVENT 0x04 /* Port Event Detect */
+#define PHY_05_ENAB_ACCEL 0x02 /* Enable Arbitration Acceleration */
+#define PHY_05_ENAB_MULTI 0x01 /* Ena. Multispeed Packet Concatenation */
#include <asm/byteorder.h>
#ifdef __BIG_ENDIAN_BITFIELD
struct selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if true is struct ext_selfid */
- u32 link_active:1;
- u32 gap_count:6;
- /* byte */
- u32 speed:2;
- u32 phy_delay:2;
- u32 contender:1;
- u32 power_class:3;
- /* byte */
- u32 port0:2;
- u32 port1:2;
- u32 port2:2;
- u32 initiated_reset:1;
- u32 more_packets:1;
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if true is struct ext_selfid */
+ u32 link_active:1;
+ u32 gap_count:6;
+ /* byte */
+ u32 speed:2;
+ u32 phy_delay:2;
+ u32 contender:1;
+ u32 power_class:3;
+ /* byte */
+ u32 port0:2;
+ u32 port1:2;
+ u32 port2:2;
+ u32 initiated_reset:1;
+ u32 more_packets:1;
} __attribute__((packed));
struct ext_selfid {
- u32 packet_identifier:2; /* always binary 10 */
- u32 phy_id:6;
- /* byte */
- u32 extended:1; /* if false is struct selfid */
- u32 seq_nr:3;
- u32 reserved:2;
- u32 porta:2;
- /* byte */
- u32 portb:2;
- u32 portc:2;
- u32 portd:2;
- u32 porte:2;
- /* byte */
- u32 portf:2;
- u32 portg:2;
- u32 porth:2;
- u32 reserved2:1;
- u32 more_packets:1;
+ u32 packet_identifier:2; /* always binary 10 */
+ u32 phy_id:6;
+ /* byte */
+ u32 extended:1; /* if false is struct selfid */
+ u32 seq_nr:3;
+ u32 reserved:2;
+ u32 porta:2;
+ /* byte */
+ u32 portb:2;
+ u32 portc:2;
+ u32 portd:2;
+ u32 porte:2;
+ /* byte */
+ u32 portf:2;
+ u32 portg:2;
+ u32 porth:2;
+ u32 reserved2:1;
+ u32 more_packets:1;
} __attribute__((packed));
#elif defined __LITTLE_ENDIAN_BITFIELD /* __BIG_ENDIAN_BITFIELD */
@@ -171,49 +172,48 @@ struct ext_selfid {
*/
struct selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 gap_count:6;
- u32 link_active:1;
- u32 extended:1; /* if true is struct ext_selfid */
- /* byte */
- u32 power_class:3;
- u32 contender:1;
- u32 phy_delay:2;
- u32 speed:2;
- /* byte */
- u32 more_packets:1;
- u32 initiated_reset:1;
- u32 port2:2;
- u32 port1:2;
- u32 port0:2;
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 gap_count:6;
+ u32 link_active:1;
+ u32 extended:1; /* if true is struct ext_selfid */
+ /* byte */
+ u32 power_class:3;
+ u32 contender:1;
+ u32 phy_delay:2;
+ u32 speed:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 initiated_reset:1;
+ u32 port2:2;
+ u32 port1:2;
+ u32 port0:2;
} __attribute__((packed));
struct ext_selfid {
- u32 phy_id:6;
- u32 packet_identifier:2; /* always binary 10 */
- /* byte */
- u32 porta:2;
- u32 reserved:2;
- u32 seq_nr:3;
- u32 extended:1; /* if false is struct selfid */
- /* byte */
- u32 porte:2;
- u32 portd:2;
- u32 portc:2;
- u32 portb:2;
- /* byte */
- u32 more_packets:1;
- u32 reserved2:1;
- u32 porth:2;
- u32 portg:2;
- u32 portf:2;
+ u32 phy_id:6;
+ u32 packet_identifier:2; /* always binary 10 */
+ /* byte */
+ u32 porta:2;
+ u32 reserved:2;
+ u32 seq_nr:3;
+ u32 extended:1; /* if false is struct selfid */
+ /* byte */
+ u32 porte:2;
+ u32 portd:2;
+ u32 portc:2;
+ u32 portb:2;
+ /* byte */
+ u32 more_packets:1;
+ u32 reserved2:1;
+ u32 porth:2;
+ u32 portg:2;
+ u32 portf:2;
} __attribute__((packed));
#else
#error What? PDP endian?
#endif /* __BIG_ENDIAN_BITFIELD */
-
#endif /* _IEEE1394_IEEE1394_H */
diff --git a/drivers/ieee1394/ieee1394_core.c b/drivers/ieee1394/ieee1394_core.c
index f43739c5cab..5fccf9f7a1d 100644
--- a/drivers/ieee1394/ieee1394_core.c
+++ b/drivers/ieee1394/ieee1394_core.c
@@ -35,7 +35,6 @@
#include <linux/kthread.h>
#include <asm/byteorder.h>
-#include <asm/semaphore.h>
#include "ieee1394_types.h"
#include "ieee1394.h"
@@ -86,7 +85,7 @@ static void dump_packet(const char *text, quadlet_t *data, int size, int speed)
printk("\n");
}
#else
-#define dump_packet(a,b,c,d)
+#define dump_packet(a,b,c,d) do {} while (0)
#endif
static void abort_requests(struct hpsb_host *host);
@@ -355,10 +354,12 @@ static void build_speed_map(struct hpsb_host *host, int nodecount)
}
}
+#if SELFID_SPEED_UNKNOWN != IEEE1394_SPEED_MAX
/* assume maximum speed for 1394b PHYs, nodemgr will correct it */
for (n = 0; n < nodecount; n++)
- if (speedcap[n] == 3)
+ if (speedcap[n] == SELFID_SPEED_UNKNOWN)
speedcap[n] = IEEE1394_SPEED_MAX;
+#endif
}
@@ -1169,7 +1170,7 @@ static void __exit ieee1394_cleanup(void)
unregister_chrdev_region(IEEE1394_CORE_DEV, 256);
}
-module_init(ieee1394_init);
+fs_initcall(ieee1394_init); /* same as ohci1394 */
module_exit(ieee1394_cleanup);
/* Exported symbols */
diff --git a/drivers/ieee1394/ieee1394_core.h b/drivers/ieee1394/ieee1394_core.h
index 0ecbf335c64..af4a78a8ef3 100644
--- a/drivers/ieee1394/ieee1394_core.h
+++ b/drivers/ieee1394/ieee1394_core.h
@@ -1,12 +1,15 @@
-
#ifndef _IEEE1394_CORE_H
#define _IEEE1394_CORE_H
-#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/skbuff.h>
+#include <linux/types.h>
#include <asm/atomic.h>
-#include <asm/semaphore.h>
-#include "hosts.h"
+#include "hosts.h"
+#include "ieee1394_types.h"
struct hpsb_packet {
/* This struct is basically read-only for hosts with the exception of
@@ -58,7 +61,6 @@ struct hpsb_packet {
size_t header_size;
size_t data_size;
-
struct hpsb_host *host;
unsigned int generation;
@@ -80,7 +82,7 @@ struct hpsb_packet {
/* Set a task for when a packet completes */
void hpsb_set_packet_complete_task(struct hpsb_packet *packet,
- void (*routine)(void *), void *data);
+ void (*routine)(void *), void *data);
static inline struct hpsb_packet *driver_packet(struct list_head *l)
{
@@ -92,7 +94,6 @@ void abort_timedouts(unsigned long __opaque);
struct hpsb_packet *hpsb_alloc_packet(size_t data_size);
void hpsb_free_packet(struct hpsb_packet *packet);
-
/*
* Generation counter for the complete 1394 subsystem. Generation gets
* incremented on every change in the subsystem (e.g. bus reset).
@@ -204,10 +205,14 @@ void hpsb_packet_received(struct hpsb_host *host, quadlet_t *data, size_t size,
#define IEEE1394_MINOR_BLOCK_EXPERIMENTAL 15
#define IEEE1394_CORE_DEV MKDEV(IEEE1394_MAJOR, 0)
-#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_RAW1394 * 16)
-#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
-#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_DV1394 * 16)
-#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
+#define IEEE1394_RAW1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_RAW1394 * 16)
+#define IEEE1394_VIDEO1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_VIDEO1394 * 16)
+#define IEEE1394_DV1394_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_DV1394 * 16)
+#define IEEE1394_EXPERIMENTAL_DEV MKDEV(IEEE1394_MAJOR, \
+ IEEE1394_MINOR_BLOCK_EXPERIMENTAL * 16)
/* return the index (within a minor number block) of a file */
static inline unsigned char ieee1394_file_to_instance(struct file *file)
@@ -223,4 +228,3 @@ extern struct class hpsb_host_class;
extern struct class *hpsb_protocol_class;
#endif /* _IEEE1394_CORE_H */
-
diff --git a/drivers/ieee1394/ieee1394_hotplug.h b/drivers/ieee1394/ieee1394_hotplug.h
index 5be70d31b00..dd5500ed832 100644
--- a/drivers/ieee1394/ieee1394_hotplug.h
+++ b/drivers/ieee1394/ieee1394_hotplug.h
@@ -1,33 +1,19 @@
#ifndef _IEEE1394_HOTPLUG_H
#define _IEEE1394_HOTPLUG_H
-#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/mod_devicetable.h>
-
/* Unit spec id and sw version entry for some protocols */
#define AVC_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define AVC_SW_VERSION_ENTRY 0x00010001
#define CAMERA_UNIT_SPEC_ID_ENTRY 0x0000A02D
#define CAMERA_SW_VERSION_ENTRY 0x00000100
-/* Check to make sure this all isn't already defined */
-#ifndef IEEE1394_MATCH_VENDOR_ID
-
-#define IEEE1394_MATCH_VENDOR_ID 0x0001
-#define IEEE1394_MATCH_MODEL_ID 0x0002
-#define IEEE1394_MATCH_SPECIFIER_ID 0x0004
-#define IEEE1394_MATCH_VERSION 0x0008
-
-struct ieee1394_device_id {
- u32 match_flags;
- u32 vendor_id;
- u32 model_id;
- u32 specifier_id;
- u32 version;
- void *driver_data;
-};
-
-#endif
+/* /include/linux/mod_devicetable.h defines:
+ * IEEE1394_MATCH_VENDOR_ID
+ * IEEE1394_MATCH_MODEL_ID
+ * IEEE1394_MATCH_SPECIFIER_ID
+ * IEEE1394_MATCH_VERSION
+ * struct ieee1394_device_id
+ */
+#include <linux/mod_devicetable.h>
#endif /* _IEEE1394_HOTPLUG_H */
diff --git a/drivers/ieee1394/ieee1394_transactions.c b/drivers/ieee1394/ieee1394_transactions.c
index a114b91d606..0833fc9f50c 100644
--- a/drivers/ieee1394/ieee1394_transactions.c
+++ b/drivers/ieee1394/ieee1394_transactions.c
@@ -9,19 +9,17 @@
* directory of the kernel sources for details.
*/
-#include <linux/sched.h>
#include <linux/bitops.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <asm/bug.h>
#include <asm/errno.h>
#include "ieee1394.h"
#include "ieee1394_types.h"
#include "hosts.h"
#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "nodemgr.h"
#include "ieee1394_transactions.h"
#define PREP_ASYNC_HEAD_ADDRESS(tc) \
@@ -31,6 +29,13 @@
packet->header[1] = (packet->host->node_id << 16) | (addr >> 32); \
packet->header[2] = addr & 0xffffffff
+#ifndef HPSB_DEBUG_TLABELS
+static
+#endif
+spinlock_t hpsb_tlabel_lock = SPIN_LOCK_UNLOCKED;
+
+static DECLARE_WAIT_QUEUE_HEAD(tlabel_wq);
+
static void fill_async_readquad(struct hpsb_packet *packet, u64 addr)
{
PREP_ASYNC_HEAD_ADDRESS(TCODE_READQ);
@@ -114,9 +119,41 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
packet->tcode = TCODE_ISO_DATA;
}
+/* same as hpsb_get_tlabel, except that it returns immediately */
+static int hpsb_get_tlabel_atomic(struct hpsb_packet *packet)
+{
+ unsigned long flags, *tp;
+ u8 *next;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
+
+ /* Broadcast transactions are complete once the request has been sent.
+ * Use the same transaction label for all broadcast transactions. */
+ if (unlikely(n == ALL_NODES)) {
+ packet->tlabel = 0;
+ return 0;
+ }
+ tp = packet->host->tl_pool[n].map;
+ next = &packet->host->next_tl[n];
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tlabel = find_next_zero_bit(tp, 64, *next);
+ if (tlabel > 63)
+ tlabel = find_first_zero_bit(tp, 64);
+ if (tlabel > 63) {
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+ return -EAGAIN;
+ }
+ __set_bit(tlabel, tp);
+ *next = (tlabel + 1) & 63;
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ packet->tlabel = tlabel;
+ return 0;
+}
+
/**
* hpsb_get_tlabel - allocate a transaction label
- * @packet: the packet who's tlabel/tpool we set
+ * @packet: the packet whose tlabel and tl_pool we set
*
* Every asynchronous transaction on the 1394 bus needs a transaction
* label to match the response to the request. This label has to be
@@ -130,42 +167,25 @@ static void fill_async_stream_packet(struct hpsb_packet *packet, int length,
* Return value: Zero on success, otherwise non-zero. A non-zero return
* generally means there are no available tlabels. If this is called out
* of interrupt or atomic context, then it will sleep until can return a
- * tlabel.
+ * tlabel or a signal is received.
*/
int hpsb_get_tlabel(struct hpsb_packet *packet)
{
- unsigned long flags;
- struct hpsb_tlabel_pool *tp;
- int n = NODEID_TO_NODE(packet->node_id);
-
- if (unlikely(n == ALL_NODES))
- return 0;
- tp = &packet->host->tpool[n];
-
- if (irqs_disabled() || in_atomic()) {
- if (down_trylock(&tp->count))
- return 1;
- } else {
- down(&tp->count);
- }
-
- spin_lock_irqsave(&tp->lock, flags);
-
- packet->tlabel = find_next_zero_bit(tp->pool, 64, tp->next);
- if (packet->tlabel > 63)
- packet->tlabel = find_first_zero_bit(tp->pool, 64);
- tp->next = (packet->tlabel + 1) % 64;
- /* Should _never_ happen */
- BUG_ON(test_and_set_bit(packet->tlabel, tp->pool));
- tp->allocations++;
- spin_unlock_irqrestore(&tp->lock, flags);
-
- return 0;
+ if (irqs_disabled() || in_atomic())
+ return hpsb_get_tlabel_atomic(packet);
+
+ /* NB: The macro wait_event_interruptible() is called with a condition
+ * argument with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event_interruptible() won't evaluate the condition again after
+ * that. */
+ return wait_event_interruptible(tlabel_wq,
+ !hpsb_get_tlabel_atomic(packet));
}
/**
* hpsb_free_tlabel - free an allocated transaction label
- * @packet: packet whos tlabel/tpool needs to be cleared
+ * @packet: packet whose tlabel and tl_pool needs to be cleared
*
* Frees the transaction label allocated with hpsb_get_tlabel(). The
* tlabel has to be freed after the transaction is complete (i.e. response
@@ -176,21 +196,20 @@ int hpsb_get_tlabel(struct hpsb_packet *packet)
*/
void hpsb_free_tlabel(struct hpsb_packet *packet)
{
- unsigned long flags;
- struct hpsb_tlabel_pool *tp;
- int n = NODEID_TO_NODE(packet->node_id);
+ unsigned long flags, *tp;
+ int tlabel, n = NODEID_TO_NODE(packet->node_id);
if (unlikely(n == ALL_NODES))
return;
- tp = &packet->host->tpool[n];
+ tp = packet->host->tl_pool[n].map;
+ tlabel = packet->tlabel;
+ BUG_ON(tlabel > 63 || tlabel < 0);
- BUG_ON(packet->tlabel > 63 || packet->tlabel < 0);
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ BUG_ON(!__test_and_clear_bit(tlabel, tp));
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
- spin_lock_irqsave(&tp->lock, flags);
- BUG_ON(!test_and_clear_bit(packet->tlabel, tp->pool));
- spin_unlock_irqrestore(&tp->lock, flags);
-
- up(&tp->count);
+ wake_up_interruptible(&tlabel_wq);
}
int hpsb_packet_success(struct hpsb_packet *packet)
@@ -214,7 +233,7 @@ int hpsb_packet_success(struct hpsb_packet *packet)
packet->node_id);
return -EAGAIN;
}
- HPSB_PANIC("reached unreachable code 1 in %s", __FUNCTION__);
+ BUG();
case ACK_BUSY_X:
case ACK_BUSY_A:
@@ -261,8 +280,7 @@ int hpsb_packet_success(struct hpsb_packet *packet)
packet->ack_code, packet->node_id, packet->tcode);
return -EAGAIN;
}
-
- HPSB_PANIC("reached unreachable code 2 in %s", __FUNCTION__);
+ BUG();
}
struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
diff --git a/drivers/ieee1394/ieee1394_transactions.h b/drivers/ieee1394/ieee1394_transactions.h
index 45ba784fe6d..c1369c41469 100644
--- a/drivers/ieee1394/ieee1394_transactions.h
+++ b/drivers/ieee1394/ieee1394_transactions.h
@@ -1,32 +1,32 @@
#ifndef _IEEE1394_TRANSACTIONS_H
#define _IEEE1394_TRANSACTIONS_H
-#include "ieee1394_core.h"
+#include <linux/types.h>
+#include "ieee1394_types.h"
+
+struct hpsb_packet;
+struct hpsb_host;
-/*
- * Get and free transaction labels.
- */
int hpsb_get_tlabel(struct hpsb_packet *packet);
void hpsb_free_tlabel(struct hpsb_packet *packet);
-
struct hpsb_packet *hpsb_make_readpacket(struct hpsb_host *host, nodeid_t node,
u64 addr, size_t length);
struct hpsb_packet *hpsb_make_lockpacket(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode, quadlet_t *data,
+ u64 addr, int extcode, quadlet_t *data,
quadlet_t arg);
-struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host, nodeid_t node,
- u64 addr, int extcode, octlet_t *data,
- octlet_t arg);
-struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host,
- quadlet_t data) ;
-struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host,
- int length, int channel,
- int tag, int sync);
-struct hpsb_packet *hpsb_make_writepacket (struct hpsb_host *host, nodeid_t node,
- u64 addr, quadlet_t *buffer, size_t length);
+struct hpsb_packet *hpsb_make_lock64packet(struct hpsb_host *host,
+ nodeid_t node, u64 addr, int extcode,
+ octlet_t *data, octlet_t arg);
+struct hpsb_packet *hpsb_make_phypacket(struct hpsb_host *host, quadlet_t data);
+struct hpsb_packet *hpsb_make_isopacket(struct hpsb_host *host, int length,
+ int channel, int tag, int sync);
+struct hpsb_packet *hpsb_make_writepacket(struct hpsb_host *host,
+ nodeid_t node, u64 addr,
+ quadlet_t *buffer, size_t length);
struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
- int length, int channel, int tag, int sync);
+ int length, int channel, int tag,
+ int sync);
/*
* hpsb_packet_success - Make sense of the ack and reply codes and
@@ -40,9 +40,8 @@ struct hpsb_packet *hpsb_make_streampacket(struct hpsb_host *host, u8 *buffer,
*/
int hpsb_packet_success(struct hpsb_packet *packet);
-
/*
- * The generic read, write and lock functions. All recognize the local node ID
+ * The generic read and write functions. All recognize the local node ID
* and act accordingly. Read and write automatically use quadlet commands if
* length == 4 and and block commands otherwise (however, they do not yet
* support lengths that are not a multiple of 4). You must explicitly specifiy
@@ -54,4 +53,8 @@ int hpsb_read(struct hpsb_host *host, nodeid_t node, unsigned int generation,
int hpsb_write(struct hpsb_host *host, nodeid_t node, unsigned int generation,
u64 addr, quadlet_t *buffer, size_t length);
+#ifdef HPSB_DEBUG_TLABELS
+extern spinlock_t hpsb_tlabel_lock;
+#endif
+
#endif /* _IEEE1394_TRANSACTIONS_H */
diff --git a/drivers/ieee1394/ieee1394_types.h b/drivers/ieee1394/ieee1394_types.h
index 3165609ec1e..9803aaa15be 100644
--- a/drivers/ieee1394/ieee1394_types.h
+++ b/drivers/ieee1394/ieee1394_types.h
@@ -1,37 +1,11 @@
-
#ifndef _IEEE1394_TYPES_H
#define _IEEE1394_TYPES_H
#include <linux/kernel.h>
-#include <linux/types.h>
-#include <linux/list.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
#include <linux/string.h>
-
-#include <asm/semaphore.h>
+#include <linux/types.h>
#include <asm/byteorder.h>
-
-/* Transaction Label handling */
-struct hpsb_tlabel_pool {
- DECLARE_BITMAP(pool, 64);
- spinlock_t lock;
- u8 next;
- u32 allocations;
- struct semaphore count;
-};
-
-#define HPSB_TPOOL_INIT(_tp) \
-do { \
- bitmap_zero((_tp)->pool, 64); \
- spin_lock_init(&(_tp)->lock); \
- (_tp)->next = 0; \
- (_tp)->allocations = 0; \
- sema_init(&(_tp)->count, 63); \
-} while (0)
-
-
typedef u32 quadlet_t;
typedef u64 octlet_t;
typedef u16 nodeid_t;
@@ -54,46 +28,40 @@ typedef u16 arm_length_t;
#define NODE_BUS_ARGS(__host, __nodeid) \
__host->id, NODEID_TO_NODE(__nodeid), NODEID_TO_BUS(__nodeid)
-#define HPSB_PRINT(level, fmt, args...) printk(level "ieee1394: " fmt "\n" , ## args)
+#define HPSB_PRINT(level, fmt, args...) \
+ printk(level "ieee1394: " fmt "\n" , ## args)
-#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
-#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
-#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
-#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
-#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
+#define HPSB_DEBUG(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_INFO(fmt, args...) HPSB_PRINT(KERN_INFO, fmt , ## args)
+#define HPSB_NOTICE(fmt, args...) HPSB_PRINT(KERN_NOTICE, fmt , ## args)
+#define HPSB_WARN(fmt, args...) HPSB_PRINT(KERN_WARNING, fmt , ## args)
+#define HPSB_ERR(fmt, args...) HPSB_PRINT(KERN_ERR, fmt , ## args)
#ifdef CONFIG_IEEE1394_VERBOSEDEBUG
-#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_VERBOSE(fmt, args...) HPSB_PRINT(KERN_DEBUG, fmt , ## args)
+#define HPSB_DEBUG_TLABELS
#else
-#define HPSB_VERBOSE(fmt, args...)
+#define HPSB_VERBOSE(fmt, args...) do {} while (0)
#endif
-#define HPSB_PANIC(fmt, args...) panic("ieee1394: " fmt "\n" , ## args)
-
-#define HPSB_TRACE() HPSB_PRINT(KERN_INFO, "TRACE - %s, %s(), line %d", __FILE__, __FUNCTION__, __LINE__)
-
-
#ifdef __BIG_ENDIAN
-static __inline__ void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
+static inline void *memcpy_le32(u32 *dest, const u32 *__src, size_t count)
{
- void *tmp = dest;
+ void *tmp = dest;
u32 *src = (u32 *)__src;
- count /= 4;
-
- while (count--) {
- *dest++ = swab32p(src++);
- }
-
- return tmp;
+ count /= 4;
+ while (count--)
+ *dest++ = swab32p(src++);
+ return tmp;
}
#else
static __inline__ void *memcpy_le32(u32 *dest, const u32 *src, size_t count)
{
- return memcpy(dest, src, count);
+ return memcpy(dest, src, count);
}
#endif /* __BIG_ENDIAN */
diff --git a/drivers/ieee1394/iso.c b/drivers/ieee1394/iso.c
index f26680ebef7..08bd15d2a7b 100644
--- a/drivers/ieee1394/iso.c
+++ b/drivers/ieee1394/iso.c
@@ -9,8 +9,11 @@
* directory of the kernel sources for details.
*/
-#include <linux/slab.h>
+#include <linux/pci.h>
#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include "hosts.h"
#include "iso.h"
void hpsb_iso_stop(struct hpsb_iso *iso)
diff --git a/drivers/ieee1394/iso.h b/drivers/ieee1394/iso.h
index 3efc60b33a8..1210a97e868 100644
--- a/drivers/ieee1394/iso.h
+++ b/drivers/ieee1394/iso.h
@@ -12,33 +12,40 @@
#ifndef IEEE1394_ISO_H
#define IEEE1394_ISO_H
-#include "hosts.h"
+#include <linux/spinlock_types.h>
+#include <asm/atomic.h>
+#include <asm/types.h>
+
#include "dma.h"
-/* high-level ISO interface */
+struct hpsb_host;
-/* This API sends and receives isochronous packets on a large,
- virtually-contiguous kernel memory buffer. The buffer may be mapped
- into a user-space process for zero-copy transmission and reception.
+/* high-level ISO interface */
- There are no explicit boundaries between packets in the buffer. A
- packet may be transmitted or received at any location. However,
- low-level drivers may impose certain restrictions on alignment or
- size of packets. (e.g. in OHCI no packet may cross a page boundary,
- and packets should be quadlet-aligned)
-*/
+/*
+ * This API sends and receives isochronous packets on a large,
+ * virtually-contiguous kernel memory buffer. The buffer may be mapped
+ * into a user-space process for zero-copy transmission and reception.
+ *
+ * There are no explicit boundaries between packets in the buffer. A
+ * packet may be transmitted or received at any location. However,
+ * low-level drivers may impose certain restrictions on alignment or
+ * size of packets. (e.g. in OHCI no packet may cross a page boundary,
+ * and packets should be quadlet-aligned)
+ */
/* Packet descriptor - the API maintains a ring buffer of these packet
- descriptors in kernel memory (hpsb_iso.infos[]). */
-
+ * descriptors in kernel memory (hpsb_iso.infos[]). */
struct hpsb_iso_packet_info {
/* offset of data payload relative to the first byte of the buffer */
__u32 offset;
- /* length of the data payload, in bytes (not including the isochronous header) */
+ /* length of the data payload, in bytes (not including the isochronous
+ * header) */
__u16 len;
- /* (recv only) the cycle number (mod 8000) on which the packet was received */
+ /* (recv only) the cycle number (mod 8000) on which the packet was
+ * received */
__u16 cycle;
/* (recv only) channel on which the packet was received */
@@ -48,12 +55,10 @@ struct hpsb_iso_packet_info {
__u8 tag;
__u8 sy;
- /*
- * length in bytes of the packet including header/trailer.
- * MUST be at structure end, since the first part of this structure is also
- * defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is copied to
- * userspace and is accessed there through libraw1394.
- */
+ /* length in bytes of the packet including header/trailer.
+ * MUST be at structure end, since the first part of this structure is
+ * also defined in raw1394.h (i.e. struct raw1394_iso_packet_info), is
+ * copied to userspace and is accessed there through libraw1394. */
__u16 total_len;
};
@@ -75,8 +80,8 @@ struct hpsb_iso {
void *hostdata;
/* a function to be called (from interrupt context) after
- outgoing packets have been sent, or incoming packets have
- arrived */
+ * outgoing packets have been sent, or incoming packets have
+ * arrived */
void (*callback)(struct hpsb_iso*);
/* wait for buffer space */
@@ -88,7 +93,7 @@ struct hpsb_iso {
/* greatest # of packets between interrupts - controls
- the maximum latency of the buffer */
+ * the maximum latency of the buffer */
int irq_interval;
/* the buffer for packet data payloads */
@@ -112,8 +117,8 @@ struct hpsb_iso {
int pkt_dma;
/* how many packets, starting at first_packet:
- (transmit) are ready to be filled with data
- (receive) contain received data */
+ * (transmit) are ready to be filled with data
+ * (receive) contain received data */
int n_ready_packets;
/* how many times the buffer has overflowed or underflowed */
@@ -134,7 +139,7 @@ struct hpsb_iso {
int start_cycle;
/* cycle at which next packet will be transmitted,
- -1 if not known */
+ * -1 if not known */
int xmit_cycle;
/* ringbuffer of packet descriptors in regular kernel memory
@@ -170,25 +175,30 @@ int hpsb_iso_recv_unlisten_channel(struct hpsb_iso *iso, unsigned char channel);
int hpsb_iso_recv_set_channel_mask(struct hpsb_iso *iso, u64 mask);
/* start/stop DMA */
-int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle, int prebuffer);
-int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle, int tag_mask, int sync);
+int hpsb_iso_xmit_start(struct hpsb_iso *iso, int start_on_cycle,
+ int prebuffer);
+int hpsb_iso_recv_start(struct hpsb_iso *iso, int start_on_cycle,
+ int tag_mask, int sync);
void hpsb_iso_stop(struct hpsb_iso *iso);
/* deallocate buffer and DMA context */
void hpsb_iso_shutdown(struct hpsb_iso *iso);
-/* queue a packet for transmission. 'offset' is relative to the beginning of the
- DMA buffer, where the packet's data payload should already have been placed */
-int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len, u8 tag, u8 sy);
+/* queue a packet for transmission.
+ * 'offset' is relative to the beginning of the DMA buffer, where the packet's
+ * data payload should already have been placed. */
+int hpsb_iso_xmit_queue_packet(struct hpsb_iso *iso, u32 offset, u16 len,
+ u8 tag, u8 sy);
/* wait until all queued packets have been transmitted to the bus */
int hpsb_iso_xmit_sync(struct hpsb_iso *iso);
/* N packets have been read out of the buffer, re-use the buffer space */
-int hpsb_iso_recv_release_packets(struct hpsb_iso *recv, unsigned int n_packets);
+int hpsb_iso_recv_release_packets(struct hpsb_iso *recv,
+ unsigned int n_packets);
/* check for arrival of new packets immediately (even if irq_interval
- has not yet been reached) */
+ * has not yet been reached) */
int hpsb_iso_recv_flush(struct hpsb_iso *iso);
/* returns # of packets ready to send or receive */
@@ -197,14 +207,15 @@ int hpsb_iso_n_ready(struct hpsb_iso *iso);
/* the following are callbacks available to low-level drivers */
/* call after a packet has been transmitted to the bus (interrupt context is OK)
- 'cycle' is the _exact_ cycle the packet was sent on
- 'error' should be non-zero if some sort of error occurred when sending the packet
-*/
+ * 'cycle' is the _exact_ cycle the packet was sent on
+ * 'error' should be non-zero if some sort of error occurred when sending the
+ * packet */
void hpsb_iso_packet_sent(struct hpsb_iso *iso, int cycle, int error);
/* call after a packet has been received (interrupt context OK) */
void hpsb_iso_packet_received(struct hpsb_iso *iso, u32 offset, u16 len,
- u16 total_len, u16 cycle, u8 channel, u8 tag, u8 sy);
+ u16 total_len, u16 cycle, u8 channel, u8 tag,
+ u8 sy);
/* call to wake waiting processes after buffer space has opened up. */
void hpsb_iso_wake(struct hpsb_iso *iso);
diff --git a/drivers/ieee1394/nodemgr.c b/drivers/ieee1394/nodemgr.c
index d541b508a15..3e7974c5744 100644
--- a/drivers/ieee1394/nodemgr.c
+++ b/drivers/ieee1394/nodemgr.c
@@ -12,26 +12,23 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/slab.h>
-#include <linux/smp_lock.h>
-#include <linux/interrupt.h>
-#include <linux/kmod.h>
-#include <linux/completion.h>
#include <linux/delay.h>
-#include <linux/pci.h>
+#include <linux/kthread.h>
#include <linux/moduleparam.h>
#include <asm/atomic.h>
-#include "ieee1394_types.h"
+#include "csr.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
#include "ieee1394_core.h"
-#include "hosts.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
#include "ieee1394_transactions.h"
-#include "highlevel.h"
-#include "csr.h"
#include "nodemgr.h"
static int ignore_drivers;
-module_param(ignore_drivers, int, 0444);
+module_param(ignore_drivers, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(ignore_drivers, "Disable automatic probing for drivers.");
struct nodemgr_csr_info {
@@ -71,7 +68,7 @@ static int nodemgr_check_speed(struct nodemgr_csr_info *ci, u64 addr,
u8 i, *speed, old_speed, good_speed;
int ret;
- speed = ci->host->speed + NODEID_TO_NODE(ci->nodeid);
+ speed = &(ci->host->speed[NODEID_TO_NODE(ci->nodeid)]);
old_speed = *speed;
good_speed = IEEE1394_SPEED_MAX + 1;
@@ -161,16 +158,12 @@ static struct csr1212_bus_ops nodemgr_csr_ops = {
* but now we are much simpler because of the LDM.
*/
-static DECLARE_MUTEX(nodemgr_serialize);
+static DEFINE_MUTEX(nodemgr_serialize);
struct host_info {
struct hpsb_host *host;
struct list_head list;
- struct completion exited;
- struct semaphore reset_sem;
- int pid;
- char daemon_name[15];
- int kill_me;
+ struct task_struct *thread;
};
static int nodemgr_bus_match(struct device * dev, struct device_driver * drv);
@@ -334,34 +327,44 @@ static ssize_t fw_show_ne_bus_options(struct device *dev, struct device_attribut
static DEVICE_ATTR(bus_options,S_IRUGO,fw_show_ne_bus_options,NULL);
-/* tlabels_free, tlabels_allocations, tlabels_mask are read non-atomically
- * here, therefore displayed values may be occasionally wrong. */
-static ssize_t fw_show_ne_tlabels_free(struct device *dev, struct device_attribute *attr, char *buf)
+#ifdef HPSB_DEBUG_TLABELS
+static ssize_t fw_show_ne_tlabels_free(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%d\n", 64 - bitmap_weight(ne->tpool->pool, 64));
-}
-static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ int tf;
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
+ tf = 64 - bitmap_weight(tp, 64);
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
-static ssize_t fw_show_ne_tlabels_allocations(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct node_entry *ne = container_of(dev, struct node_entry, device);
- return sprintf(buf, "%u\n", ne->tpool->allocations);
+ return sprintf(buf, "%d\n", tf);
}
-static DEVICE_ATTR(tlabels_allocations,S_IRUGO,fw_show_ne_tlabels_allocations,NULL);
+static DEVICE_ATTR(tlabels_free,S_IRUGO,fw_show_ne_tlabels_free,NULL);
-static ssize_t fw_show_ne_tlabels_mask(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t fw_show_ne_tlabels_mask(struct device *dev,
+ struct device_attribute *attr, char *buf)
{
struct node_entry *ne = container_of(dev, struct node_entry, device);
+ unsigned long flags;
+ unsigned long *tp = ne->host->tl_pool[NODEID_TO_NODE(ne->nodeid)].map;
+ u64 tm;
+
+ spin_lock_irqsave(&hpsb_tlabel_lock, flags);
#if (BITS_PER_LONG <= 32)
- return sprintf(buf, "0x%08lx%08lx\n", ne->tpool->pool[0], ne->tpool->pool[1]);
+ tm = ((u64)tp[0] << 32) + tp[1];
#else
- return sprintf(buf, "0x%016lx\n", ne->tpool->pool[0]);
+ tm = tp[0];
#endif
+ spin_unlock_irqrestore(&hpsb_tlabel_lock, flags);
+
+ return sprintf(buf, "0x%016llx\n", tm);
}
static DEVICE_ATTR(tlabels_mask, S_IRUGO, fw_show_ne_tlabels_mask, NULL);
+#endif /* HPSB_DEBUG_TLABELS */
static ssize_t fw_set_ignore_driver(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
@@ -408,26 +411,11 @@ static ssize_t fw_get_destroy_node(struct bus_type *bus, char *buf)
}
static BUS_ATTR(destroy_node, S_IWUSR | S_IRUGO, fw_get_destroy_node, fw_set_destroy_node);
-static int nodemgr_rescan_bus_thread(void *__unused)
-{
- /* No userlevel access needed */
- daemonize("kfwrescan");
-
- bus_rescan_devices(&ieee1394_bus_type);
-
- return 0;
-}
static ssize_t fw_set_rescan(struct bus_type *bus, const char *buf, size_t count)
{
- int state = simple_strtoul(buf, NULL, 10);
-
- /* Don't wait for this, or care about errors. Root could do
- * something stupid and spawn this a lot of times, but that's
- * root's fault. */
- if (state == 1)
- kernel_thread(nodemgr_rescan_bus_thread, NULL, CLONE_KERNEL);
-
+ if (simple_strtoul(buf, NULL, 10) == 1)
+ bus_rescan_devices(&ieee1394_bus_type);
return count;
}
static ssize_t fw_get_rescan(struct bus_type *bus, char *buf)
@@ -483,9 +471,10 @@ static struct device_attribute *const fw_ne_attrs[] = {
&dev_attr_ne_vendor_id,
&dev_attr_ne_nodeid,
&dev_attr_bus_options,
+#ifdef HPSB_DEBUG_TLABELS
&dev_attr_tlabels_free,
- &dev_attr_tlabels_allocations,
&dev_attr_tlabels_mask,
+#endif
};
@@ -804,8 +793,6 @@ static struct node_entry *nodemgr_create_node(octlet_t guid, struct csr1212_csr
if (!ne)
return NULL;
- ne->tpool = &host->tpool[nodeid & NODE_MASK];
-
ne->host = host;
ne->nodeid = nodeid;
ne->generation = generation;
@@ -1251,6 +1238,7 @@ static void nodemgr_node_scan_one(struct host_info *hi,
octlet_t guid;
struct csr1212_csr *csr;
struct nodemgr_csr_info *ci;
+ u8 *speed;
ci = kmalloc(sizeof(*ci), GFP_KERNEL);
if (!ci)
@@ -1259,8 +1247,12 @@ static void nodemgr_node_scan_one(struct host_info *hi,
ci->host = host;
ci->nodeid = nodeid;
ci->generation = generation;
- ci->speed_unverified =
- host->speed[NODEID_TO_NODE(nodeid)] > IEEE1394_SPEED_100;
+
+ /* Prepare for speed probe which occurs when reading the ROM */
+ speed = &(host->speed[NODEID_TO_NODE(nodeid)]);
+ if (*speed > host->csr.lnk_spd)
+ *speed = host->csr.lnk_spd;
+ ci->speed_unverified = *speed > IEEE1394_SPEED_100;
/* We need to detect when the ConfigROM's generation has changed,
* so we only update the node's info when it needs to be. */
@@ -1300,8 +1292,6 @@ static void nodemgr_node_scan_one(struct host_info *hi,
nodemgr_create_node(guid, csr, hi, nodeid, generation);
else
nodemgr_update_node(ne, csr, hi, nodeid, generation);
-
- return;
}
@@ -1326,6 +1316,7 @@ static void nodemgr_node_scan(struct host_info *hi, int generation)
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_suspend_ne(struct node_entry *ne)
{
struct class_device *cdev;
@@ -1361,6 +1352,7 @@ static void nodemgr_resume_ne(struct node_entry *ne)
ne->in_limbo = 0;
device_remove_file(&ne->device, &dev_attr_ne_in_limbo);
+ down_read(&nodemgr_ud_class.subsys.rwsem);
down_read(&ne->device.bus->subsys.rwsem);
list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
@@ -1372,21 +1364,21 @@ static void nodemgr_resume_ne(struct node_entry *ne)
ud->device.driver->resume(&ud->device);
}
up_read(&ne->device.bus->subsys.rwsem);
+ up_read(&nodemgr_ud_class.subsys.rwsem);
HPSB_DEBUG("Node resumed: ID:BUS[" NODE_BUS_FMT "] GUID[%016Lx]",
NODE_BUS_ARGS(ne->host, ne->nodeid), (unsigned long long)ne->guid);
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader. */
static void nodemgr_update_pdrv(struct node_entry *ne)
{
struct unit_directory *ud;
struct hpsb_protocol_driver *pdrv;
- struct class *class = &nodemgr_ud_class;
struct class_device *cdev;
- down_read(&class->subsys.rwsem);
- list_for_each_entry(cdev, &class->children, node) {
+ list_for_each_entry(cdev, &nodemgr_ud_class.children, node) {
ud = container_of(cdev, struct unit_directory, class_dev);
if (ud->ne != ne || !ud->device.driver)
continue;
@@ -1399,7 +1391,6 @@ static void nodemgr_update_pdrv(struct node_entry *ne)
up_write(&ud->device.bus->subsys.rwsem);
}
}
- up_read(&class->subsys.rwsem);
}
@@ -1430,6 +1421,8 @@ static void nodemgr_irm_write_bc(struct node_entry *ne, int generation)
}
+/* Caller needs to hold nodemgr_ud_class.subsys.rwsem as reader because the
+ * calls to nodemgr_update_pdrv() and nodemgr_suspend_ne() here require it. */
static void nodemgr_probe_ne(struct host_info *hi, struct node_entry *ne, int generation)
{
struct device *dev;
@@ -1492,9 +1485,8 @@ static void nodemgr_node_probe(struct host_info *hi, int generation)
/* If we had a bus reset while we were scanning the bus, it is
* possible that we did not probe all nodes. In that case, we
* skip the clean up for now, since we could remove nodes that
- * were still on the bus. The bus reset increased hi->reset_sem,
- * so there's a bus scan pending which will do the clean up
- * eventually.
+ * were still on the bus. Another bus scan is pending which will
+ * do the clean up eventually.
*
* Now let's tell the bus to rescan our devices. This may seem
* like overhead, but the driver-model core will only scan a
@@ -1622,41 +1614,37 @@ static int nodemgr_host_thread(void *__hi)
{
struct host_info *hi = (struct host_info *)__hi;
struct hpsb_host *host = hi->host;
- int reset_cycles = 0;
-
- /* No userlevel access needed */
- daemonize(hi->daemon_name);
+ unsigned int g, generation = get_hpsb_generation(host) - 1;
+ int i, reset_cycles = 0;
/* Setup our device-model entries */
nodemgr_create_host_dev_files(host);
- /* Sit and wait for a signal to probe the nodes on the bus. This
- * happens when we get a bus reset. */
- while (1) {
- unsigned int generation = 0;
- int i;
+ for (;;) {
+ /* Sleep until next bus reset */
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (get_hpsb_generation(host) == generation)
+ schedule();
+ __set_current_state(TASK_RUNNING);
+
+ /* Thread may have been woken up to freeze or to exit */
+ if (try_to_freeze())
+ continue;
+ if (kthread_should_stop())
+ goto exit;
- if (down_interruptible(&hi->reset_sem) ||
- down_interruptible(&nodemgr_serialize)) {
+ if (mutex_lock_interruptible(&nodemgr_serialize)) {
if (try_to_freeze())
continue;
- printk("NodeMgr: received unexpected signal?!\n" );
- break;
- }
-
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- break;
+ goto exit;
}
/* Pause for 1/4 second in 1/16 second intervals,
* to make sure things settle down. */
+ g = get_hpsb_generation(host);
for (i = 0; i < 4 ; i++) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (msleep_interruptible(63)) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (msleep_interruptible(63) || kthread_should_stop())
+ goto unlock_exit;
/* Now get the generation in which the node ID's we collect
* are valid. During the bus scan we will use this generation
@@ -1667,20 +1655,14 @@ static int nodemgr_host_thread(void *__hi)
/* If we get a reset before we are done waiting, then
* start the the waiting over again */
- while (!down_trylock(&hi->reset_sem))
- i = 0;
-
- /* Check the kill_me again */
- if (hi->kill_me) {
- up(&nodemgr_serialize);
- goto caught_signal;
- }
+ if (generation != g)
+ g = generation, i = 0;
}
if (!nodemgr_check_irm_capability(host, reset_cycles) ||
!nodemgr_do_irm_duties(host, reset_cycles)) {
reset_cycles++;
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
continue;
}
reset_cycles = 0;
@@ -1698,13 +1680,13 @@ static int nodemgr_host_thread(void *__hi)
/* Update some of our sysfs symlinks */
nodemgr_update_host_dev_links(host);
- up(&nodemgr_serialize);
+ mutex_unlock(&nodemgr_serialize);
}
-
-caught_signal:
+unlock_exit:
+ mutex_unlock(&nodemgr_serialize);
+exit:
HPSB_VERBOSE("NodeMgr: Exiting thread");
-
- complete_and_exit(&hi->exited, 0);
+ return 0;
}
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *))
@@ -1764,41 +1746,27 @@ static void nodemgr_add_host(struct hpsb_host *host)
struct host_info *hi;
hi = hpsb_create_hostinfo(&nodemgr_highlevel, host, sizeof(*hi));
-
if (!hi) {
- HPSB_ERR ("NodeMgr: out of memory in add host");
+ HPSB_ERR("NodeMgr: out of memory in add host");
return;
}
-
hi->host = host;
- init_completion(&hi->exited);
- sema_init(&hi->reset_sem, 0);
-
- sprintf(hi->daemon_name, "knodemgrd_%d", host->id);
-
- hi->pid = kernel_thread(nodemgr_host_thread, hi, CLONE_KERNEL);
-
- if (hi->pid < 0) {
- HPSB_ERR ("NodeMgr: failed to start %s thread for %s",
- hi->daemon_name, host->driver->name);
+ hi->thread = kthread_run(nodemgr_host_thread, hi, "knodemgrd_%d",
+ host->id);
+ if (IS_ERR(hi->thread)) {
+ HPSB_ERR("NodeMgr: cannot start thread for host %d", host->id);
hpsb_destroy_hostinfo(&nodemgr_highlevel, host);
- return;
}
-
- return;
}
static void nodemgr_host_reset(struct hpsb_host *host)
{
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
- if (hi != NULL) {
- HPSB_VERBOSE("NodeMgr: Processing host reset for %s", hi->daemon_name);
- up(&hi->reset_sem);
- } else
- HPSB_ERR ("NodeMgr: could not process reset of unused host");
-
- return;
+ if (hi) {
+ HPSB_VERBOSE("NodeMgr: Processing reset for host %d", host->id);
+ wake_up_process(hi->thread);
+ }
}
static void nodemgr_remove_host(struct hpsb_host *host)
@@ -1806,18 +1774,9 @@ static void nodemgr_remove_host(struct hpsb_host *host)
struct host_info *hi = hpsb_get_hostinfo(&nodemgr_highlevel, host);
if (hi) {
- if (hi->pid >= 0) {
- hi->kill_me = 1;
- mb();
- up(&hi->reset_sem);
- wait_for_completion(&hi->exited);
- nodemgr_remove_host_dev(&host->device);
- }
- } else
- HPSB_ERR("NodeMgr: host %s does not exist, cannot remove",
- host->driver->name);
-
- return;
+ kthread_stop(hi->thread);
+ nodemgr_remove_host_dev(&host->device);
+ }
}
static struct hpsb_highlevel nodemgr_highlevel = {
diff --git a/drivers/ieee1394/nodemgr.h b/drivers/ieee1394/nodemgr.h
index 0b26616e16c..0e1e7d93078 100644
--- a/drivers/ieee1394/nodemgr.h
+++ b/drivers/ieee1394/nodemgr.h
@@ -21,9 +21,15 @@
#define _IEEE1394_NODEMGR_H
#include <linux/device.h>
-#include "csr1212.h"
+#include <asm/types.h>
+
#include "ieee1394_core.h"
-#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
+
+struct csr1212_csr;
+struct csr1212_keyval;
+struct hpsb_host;
+struct ieee1394_device_id;
/* '1' '3' '9' '4' in ASCII */
#define IEEE1394_BUSID_MAGIC __constant_cpu_to_be32(0x31333934)
@@ -44,7 +50,6 @@ struct bus_options {
u16 max_rec; /* Maximum packet size node can receive */
};
-
#define UNIT_DIRECTORY_VENDOR_ID 0x01
#define UNIT_DIRECTORY_MODEL_ID 0x02
#define UNIT_DIRECTORY_SPECIFIER_ID 0x04
@@ -59,8 +64,8 @@ struct bus_options {
* unit directory for each of these protocols.
*/
struct unit_directory {
- struct node_entry *ne; /* The node which this directory belongs to */
- octlet_t address; /* Address of the unit directory on the node */
+ struct node_entry *ne; /* The node which this directory belongs to */
+ octlet_t address; /* Address of the unit directory on the node */
u8 flags; /* Indicates which entries were read */
quadlet_t vendor_id;
@@ -79,11 +84,10 @@ struct unit_directory {
int length; /* Number of quadlets */
struct device device;
-
struct class_device class_dev;
struct csr1212_keyval *ud_kv;
- u32 lun; /* logical unit number immediate value */
+ u32 lun; /* logical unit number immediate value */
};
struct node_entry {
@@ -103,10 +107,8 @@ struct node_entry {
const char *vendor_oui;
u32 capabilities;
- struct hpsb_tlabel_pool *tpool;
struct device device;
-
struct class_device class_dev;
/* Means this node is not attached anymore */
@@ -153,8 +155,8 @@ static inline int hpsb_node_entry_valid(struct node_entry *ne)
/*
* This will fill in the given, pre-initialised hpsb_packet with the current
* information from the node entry (host, node ID, generation number). It will
- * return false if the node owning the GUID is not accessible (and not modify the
- * hpsb_packet) and return true otherwise.
+ * return false if the node owning the GUID is not accessible (and not modify
+ * the hpsb_packet) and return true otherwise.
*
* Note that packet sending may still fail in hpsb_send_packet if a bus reset
* happens while you are trying to set up the packet (due to obsolete generation
@@ -170,16 +172,13 @@ int hpsb_node_write(struct node_entry *ne, u64 addr,
int hpsb_node_lock(struct node_entry *ne, u64 addr,
int extcode, quadlet_t *data, quadlet_t arg);
-
/* Iterate the hosts, calling a given function with supplied data for each
* host. */
int nodemgr_for_each_host(void *__data, int (*cb)(struct hpsb_host *, void *));
-
int init_ieee1394_nodemgr(void);
void cleanup_ieee1394_nodemgr(void);
-
/* The template for a host device */
extern struct device nodemgr_dev_template_host;
diff --git a/drivers/ieee1394/ohci1394.c b/drivers/ieee1394/ohci1394.c
index 448df277337..8fd0030475b 100644
--- a/drivers/ieee1394/ohci1394.c
+++ b/drivers/ieee1394/ohci1394.c
@@ -136,7 +136,7 @@
#define DBGMSG(fmt, args...) \
printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->id , ## args)
#else
-#define DBGMSG(fmt, args...)
+#define DBGMSG(fmt, args...) do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_OHCI_DMA_DEBUG
@@ -148,8 +148,8 @@ printk(KERN_INFO "%s: fw-host%d: " fmt "\n" , OHCI1394_DRIVER_NAME, ohci->host->
--global_outstanding_dmas, ## args)
static int global_outstanding_dmas = 0;
#else
-#define OHCI_DMA_ALLOC(fmt, args...)
-#define OHCI_DMA_FREE(fmt, args...)
+#define OHCI_DMA_ALLOC(fmt, args...) do {} while (0)
+#define OHCI_DMA_FREE(fmt, args...) do {} while (0)
#endif
/* print general (card independent) information */
@@ -181,36 +181,35 @@ static int alloc_dma_trm_ctx(struct ti_ohci *ohci, struct dma_trm_ctx *d,
static void ohci1394_pci_remove(struct pci_dev *pdev);
#ifndef __LITTLE_ENDIAN
-static unsigned hdr_sizes[] =
-{
+const static size_t hdr_sizes[] = {
3, /* TCODE_WRITEQ */
4, /* TCODE_WRITEB */
3, /* TCODE_WRITE_RESPONSE */
- 0, /* ??? */
+ 0, /* reserved */
3, /* TCODE_READQ */
4, /* TCODE_READB */
3, /* TCODE_READQ_RESPONSE */
4, /* TCODE_READB_RESPONSE */
- 1, /* TCODE_CYCLE_START (???) */
+ 1, /* TCODE_CYCLE_START */
4, /* TCODE_LOCK_REQUEST */
2, /* TCODE_ISO_DATA */
4, /* TCODE_LOCK_RESPONSE */
+ /* rest is reserved or link-internal */
};
-/* Swap headers */
-static inline void packet_swab(quadlet_t *data, int tcode)
+static inline void header_le32_to_cpu(quadlet_t *data, unsigned char tcode)
{
- size_t size = hdr_sizes[tcode];
+ size_t size;
- if (tcode > TCODE_LOCK_RESPONSE || hdr_sizes[tcode] == 0)
+ if (unlikely(tcode >= ARRAY_SIZE(hdr_sizes)))
return;
+ size = hdr_sizes[tcode];
while (size--)
- data[size] = swab32(data[size]);
+ data[size] = le32_to_cpu(data[size]);
}
#else
-/* Don't waste cycles on same sex byte swaps */
-#define packet_swab(w,x)
+#define header_le32_to_cpu(w,x) do {} while (0)
#endif /* !LITTLE_ENDIAN */
/***********************************
@@ -701,7 +700,7 @@ static void insert_packet(struct ti_ohci *ohci,
d->prg_cpu[idx]->data[2] = packet->header[2];
d->prg_cpu[idx]->data[3] = packet->header[3];
}
- packet_swab(d->prg_cpu[idx]->data, packet->tcode);
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
}
if (packet->data_size) { /* block transmit */
@@ -777,7 +776,7 @@ static void insert_packet(struct ti_ohci *ohci,
d->prg_cpu[idx]->data[0] = packet->speed_code<<16 |
(packet->header[0] & 0xFFFF);
d->prg_cpu[idx]->data[1] = packet->header[0] & 0xFFFF0000;
- packet_swab(d->prg_cpu[idx]->data, packet->tcode);
+ header_le32_to_cpu(d->prg_cpu[idx]->data, packet->tcode);
d->prg_cpu[idx]->begin.control =
cpu_to_le32(DMA_CTL_OUTPUT_MORE |
@@ -2598,8 +2597,9 @@ static const int TCODE_SIZE[16] = {20, 0, 16, -1, 16, 20, 20, 0,
* Determine the length of a packet in the buffer
* Optimization suggested by Pascal Drolet <pascal.drolet@informission.ca>
*/
-static __inline__ int packet_length(struct dma_rcv_ctx *d, int idx, quadlet_t *buf_ptr,
- int offset, unsigned char tcode, int noswap)
+static inline int packet_length(struct dma_rcv_ctx *d, int idx,
+ quadlet_t *buf_ptr, int offset,
+ unsigned char tcode, int noswap)
{
int length = -1;
@@ -2730,7 +2730,7 @@ static void dma_rcv_tasklet (unsigned long data)
* bus reset. We always ignore it. */
if (tcode != OHCI1394_TCODE_PHY) {
if (!ohci->no_swap_incoming)
- packet_swab(d->spb, tcode);
+ header_le32_to_cpu(d->spb, tcode);
DBGMSG("Packet received from node"
" %d ack=0x%02X spd=%d tcode=0x%X"
" length=%d ctx=%d tlabel=%d",
@@ -2738,7 +2738,7 @@ static void dma_rcv_tasklet (unsigned long data)
(cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f,
(cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>21)&0x3,
tcode, length, d->ctx,
- (cond_le32_to_cpu(d->spb[0], ohci->no_swap_incoming)>>10)&0x3f);
+ (d->spb[0]>>10)&0x3f);
ack = (((cond_le32_to_cpu(d->spb[length/4-1], ohci->no_swap_incoming)>>16)&0x1f)
== 0x11) ? 1 : 0;
@@ -3529,9 +3529,10 @@ static void ohci1394_pci_remove(struct pci_dev *pdev)
put_device(dev);
}
-
+#ifdef CONFIG_PM
static int ohci1394_pci_resume (struct pci_dev *pdev)
{
+/* PowerMac resume code comes first */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *of_node;
@@ -3543,17 +3544,23 @@ static int ohci1394_pci_resume (struct pci_dev *pdev)
}
#endif /* CONFIG_PPC_PMAC */
+ pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
- pci_enable_device(pdev);
-
- return 0;
+ return pci_enable_device(pdev);
}
-
static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state)
{
- pci_save_state(pdev);
+ int err;
+
+ err = pci_save_state(pdev);
+ if (err)
+ goto out;
+ err = pci_set_power_state(pdev, pci_choose_state(pdev, state));
+ if (err)
+ goto out;
+/* PowerMac suspend code comes last */
#ifdef CONFIG_PPC_PMAC
if (machine_is(powermac)) {
struct device_node *of_node;
@@ -3563,11 +3570,11 @@ static int ohci1394_pci_suspend (struct pci_dev *pdev, pm_message_t state)
if (of_node)
pmac_call_feature(PMAC_FTR_1394_ENABLE, of_node, 0, 0);
}
-#endif
-
- return 0;
+#endif /* CONFIG_PPC_PMAC */
+out:
+ return err;
}
-
+#endif /* CONFIG_PM */
#define PCI_CLASS_FIREWIRE_OHCI ((PCI_CLASS_SERIAL_FIREWIRE << 8) | 0x10)
@@ -3590,8 +3597,10 @@ static struct pci_driver ohci1394_pci_driver = {
.id_table = ohci1394_pci_tbl,
.probe = ohci1394_pci_probe,
.remove = ohci1394_pci_remove,
+#ifdef CONFIG_PM
.resume = ohci1394_pci_resume,
.suspend = ohci1394_pci_suspend,
+#endif
};
/***********************************
@@ -3718,5 +3727,7 @@ static int __init ohci1394_init(void)
return pci_register_driver(&ohci1394_pci_driver);
}
-module_init(ohci1394_init);
+/* Register before most other device drivers.
+ * Useful for remote debugging via physical DMA, e.g. using firescope. */
+fs_initcall(ohci1394_init);
module_exit(ohci1394_cleanup);
diff --git a/drivers/ieee1394/pcilynx.c b/drivers/ieee1394/pcilynx.c
index e6f41238f5e..b4f146f2c95 100644
--- a/drivers/ieee1394/pcilynx.c
+++ b/drivers/ieee1394/pcilynx.c
@@ -137,7 +137,6 @@ static struct i2c_algo_bit_data bit_data = {
.getsda = bit_getsda,
.getscl = bit_getscl,
.udelay = 5,
- .mdelay = 5,
.timeout = 100,
};
diff --git a/drivers/ieee1394/raw1394-private.h b/drivers/ieee1394/raw1394-private.h
index c93587be9ca..c7731d1bcd8 100644
--- a/drivers/ieee1394/raw1394-private.h
+++ b/drivers/ieee1394/raw1394-private.h
@@ -29,9 +29,8 @@ struct file_info {
struct list_head req_pending;
struct list_head req_complete;
- struct semaphore complete_sem;
spinlock_t reqlists_lock;
- wait_queue_head_t poll_wait_complete;
+ wait_queue_head_t wait_complete;
struct list_head addr_list;
diff --git a/drivers/ieee1394/raw1394.c b/drivers/ieee1394/raw1394.c
index 571ea68c0cf..5ec4f5eb6b1 100644
--- a/drivers/ieee1394/raw1394.c
+++ b/drivers/ieee1394/raw1394.c
@@ -44,14 +44,15 @@
#include <linux/compat.h>
#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
-#include "ieee1394_types.h"
#include "ieee1394_core.h"
-#include "nodemgr.h"
-#include "hosts.h"
-#include "highlevel.h"
-#include "iso.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "iso.h"
+#include "nodemgr.h"
#include "raw1394.h"
#include "raw1394-private.h"
@@ -66,7 +67,7 @@
#define DBGMSG(fmt, args...) \
printk(KERN_INFO "raw1394:" fmt "\n" , ## args)
#else
-#define DBGMSG(fmt, args...)
+#define DBGMSG(fmt, args...) do {} while (0)
#endif
static LIST_HEAD(host_info_list);
@@ -132,10 +133,9 @@ 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_move_tail(&req->list, &fi->req_complete);
- up(&fi->complete_sem);
- wake_up_interruptible(&fi->poll_wait_complete);
+ list_move_tail(&req->list, &fi->req_complete);
+ wake_up(&fi->wait_complete);
}
static void queue_complete_req(struct pending_request *req)
@@ -463,13 +463,36 @@ raw1394_compat_read(const char __user *buf, struct raw1394_request *r)
#endif
+/* get next completed request (caller must hold fi->reqlists_lock) */
+static inline struct pending_request *__next_complete_req(struct file_info *fi)
+{
+ struct list_head *lh;
+ struct pending_request *req = NULL;
+
+ if (!list_empty(&fi->req_complete)) {
+ lh = fi->req_complete.next;
+ list_del(lh);
+ req = list_entry(lh, struct pending_request, list);
+ }
+ return req;
+}
+
+/* atomically get next completed request */
+static struct pending_request *next_complete_req(struct file_info *fi)
+{
+ unsigned long flags;
+ struct pending_request *req;
+
+ spin_lock_irqsave(&fi->reqlists_lock, flags);
+ req = __next_complete_req(fi);
+ spin_unlock_irqrestore(&fi->reqlists_lock, flags);
+ return req;
+}
static ssize_t raw1394_read(struct file *file, char __user * buffer,
size_t count, loff_t * offset_is_ignored)
{
- unsigned long flags;
struct file_info *fi = (struct file_info *)file->private_data;
- struct list_head *lh;
struct pending_request *req;
ssize_t ret;
@@ -487,22 +510,21 @@ static ssize_t raw1394_read(struct file *file, char __user * buffer,
}
if (file->f_flags & O_NONBLOCK) {
- if (down_trylock(&fi->complete_sem)) {
+ if (!(req = next_complete_req(fi)))
return -EAGAIN;
- }
} else {
- if (down_interruptible(&fi->complete_sem)) {
+ /*
+ * NB: We call the macro wait_event_interruptible() with a
+ * condition argument with side effect. This is only possible
+ * because the side effect does not occur until the condition
+ * became true, and wait_event_interruptible() won't evaluate
+ * the condition again after that.
+ */
+ if (wait_event_interruptible(fi->wait_complete,
+ (req = next_complete_req(fi))))
return -ERESTARTSYS;
- }
}
- spin_lock_irqsave(&fi->reqlists_lock, flags);
- lh = fi->req_complete.next;
- list_del(lh);
- spin_unlock_irqrestore(&fi->reqlists_lock, flags);
-
- req = list_entry(lh, struct pending_request, list);
-
if (req->req.length) {
if (copy_to_user(int2ptr(req->req.recvb), req->data,
req->req.length)) {
@@ -1752,6 +1774,7 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
addr->notification_options |= addr->client_transactions;
addr->recvb = req->req.recvb;
addr->rec_length = (u16) ((req->req.misc >> 16) & 0xFFFF);
+
spin_lock_irqsave(&host_info_lock, flags);
hi = find_host_info(fi->host);
same_host = 0;
@@ -1777,9 +1800,9 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
}
if (same_host) {
/* addressrange occupied by same host */
+ spin_unlock_irqrestore(&host_info_lock, flags);
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EALREADY);
}
/* another host with valid address-entry containing same addressrange */
@@ -1807,6 +1830,8 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
}
}
}
+ spin_unlock_irqrestore(&host_info_lock, flags);
+
if (another_host) {
DBGMSG("another hosts entry is valid -> SUCCESS");
if (copy_to_user(int2ptr(req->req.recvb),
@@ -1815,11 +1840,11 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
" address-range-entry is invalid -> EFAULT !!!\n");
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EFAULT);
}
free_pending_request(req); /* immediate success or fail */
/* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
list_add_tail(&addr->addr_list, &fi->addr_list);
spin_unlock_irqrestore(&host_info_lock, flags);
return sizeof(struct raw1394_request);
@@ -1830,15 +1855,15 @@ static int arm_register(struct file_info *fi, struct pending_request *req)
req->req.address + req->req.length);
if (retval) {
/* INSERT ENTRY */
+ spin_lock_irqsave(&host_info_lock, flags);
list_add_tail(&addr->addr_list, &fi->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
} else {
DBGMSG("arm_register failed errno: %d \n", retval);
vfree(addr->addr_space_buffer);
kfree(addr);
- spin_unlock_irqrestore(&host_info_lock, flags);
return (-EALREADY);
}
- spin_unlock_irqrestore(&host_info_lock, flags);
free_pending_request(req); /* immediate success or fail */
return sizeof(struct raw1394_request);
}
@@ -1904,10 +1929,10 @@ static int arm_unregister(struct file_info *fi, struct pending_request *req)
if (another_host) {
DBGMSG("delete entry from list -> success");
list_del(&addr->addr_list);
+ spin_unlock_irqrestore(&host_info_lock, flags);
vfree(addr->addr_space_buffer);
kfree(addr);
free_pending_request(req); /* immediate success or fail */
- spin_unlock_irqrestore(&host_info_lock, flags);
return sizeof(struct raw1394_request);
}
retval =
@@ -1949,23 +1974,19 @@ static int arm_get_buf(struct file_info *fi, struct pending_request *req)
(arm_addr->end > req->req.address)) {
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
DBGMSG
("arm_get_buf copy_to_user( %08X, %p, %u )",
(u32) req->req.recvb,
arm_addr->addr_space_buffer + offset,
(u32) req->req.length);
-
if (copy_to_user
(int2ptr(req->req.recvb),
arm_addr->addr_space_buffer + offset,
- req->req.length)) {
- spin_unlock_irqrestore(&host_info_lock,
- flags);
+ req->req.length))
return (-EFAULT);
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
/* We have to free the request, because we
* queue no response, and therefore nobody
* will free it. */
@@ -2005,24 +2026,23 @@ static int arm_set_buf(struct file_info *fi, struct pending_request *req)
(arm_addr->end > req->req.address)) {
if (req->req.address + req->req.length <= arm_addr->end) {
offset = req->req.address - arm_addr->start;
+ spin_unlock_irqrestore(&host_info_lock, flags);
DBGMSG
("arm_set_buf copy_from_user( %p, %08X, %u )",
arm_addr->addr_space_buffer + offset,
(u32) req->req.sendb,
(u32) req->req.length);
-
if (copy_from_user
(arm_addr->addr_space_buffer + offset,
int2ptr(req->req.sendb),
- req->req.length)) {
- spin_unlock_irqrestore(&host_info_lock,
- flags);
+ req->req.length))
return (-EFAULT);
- }
- spin_unlock_irqrestore(&host_info_lock, flags);
- free_pending_request(req); /* we have to free the request, because we queue no response, and therefore nobody will free it */
+ /* We have to free the request, because we
+ * queue no response, and therefore nobody
+ * will free it. */
+ free_pending_request(req);
return sizeof(struct raw1394_request);
} else {
DBGMSG("arm_set_buf request exceeded mapping");
@@ -2744,7 +2764,7 @@ static unsigned int raw1394_poll(struct file *file, poll_table * pt)
unsigned int mask = POLLOUT | POLLWRNORM;
unsigned long flags;
- poll_wait(file, &fi->poll_wait_complete, pt);
+ poll_wait(file, &fi->wait_complete, pt);
spin_lock_irqsave(&fi->reqlists_lock, flags);
if (!list_empty(&fi->req_complete)) {
@@ -2769,9 +2789,8 @@ static int raw1394_open(struct inode *inode, struct file *file)
fi->state = opened;
INIT_LIST_HEAD(&fi->req_pending);
INIT_LIST_HEAD(&fi->req_complete);
- sema_init(&fi->complete_sem, 0);
spin_lock_init(&fi->reqlists_lock);
- init_waitqueue_head(&fi->poll_wait_complete);
+ init_waitqueue_head(&fi->wait_complete);
INIT_LIST_HEAD(&fi->addr_list);
file->private_data = fi;
@@ -2784,7 +2803,7 @@ static int raw1394_release(struct inode *inode, struct file *file)
struct file_info *fi = file->private_data;
struct list_head *lh;
struct pending_request *req;
- int done = 0, i, fail = 0;
+ int i, fail;
int retval = 0;
struct list_head *entry;
struct arm_addr *addr = NULL;
@@ -2864,25 +2883,28 @@ static int raw1394_release(struct inode *inode, struct file *file)
"error(s) occurred \n");
}
- while (!done) {
+ for (;;) {
+ /* This locked section guarantees that neither
+ * complete nor pending requests exist once i!=0 */
spin_lock_irqsave(&fi->reqlists_lock, flags);
-
- while (!list_empty(&fi->req_complete)) {
- lh = fi->req_complete.next;
- list_del(lh);
-
- req = list_entry(lh, struct pending_request, list);
-
+ while ((req = __next_complete_req(fi)))
free_pending_request(req);
- }
-
- if (list_empty(&fi->req_pending))
- done = 1;
+ i = list_empty(&fi->req_pending);
spin_unlock_irqrestore(&fi->reqlists_lock, flags);
- if (!done)
- down_interruptible(&fi->complete_sem);
+ if (i)
+ break;
+ /*
+ * Sleep until more requests can be freed.
+ *
+ * NB: We call the macro wait_event() with a condition argument
+ * with side effect. This is only possible because the side
+ * effect does not occur until the condition became true, and
+ * wait_event() won't evaluate the condition again after that.
+ */
+ wait_event(fi->wait_complete, (req = next_complete_req(fi)));
+ free_pending_request(req);
}
/* Remove any sub-trees left by user space programs */
diff --git a/drivers/ieee1394/sbp2.c b/drivers/ieee1394/sbp2.c
index b08755e2e68..6986ac18828 100644
--- a/drivers/ieee1394/sbp2.c
+++ b/drivers/ieee1394/sbp2.c
@@ -38,31 +38,36 @@
* but the code needs additional debugging.
*/
+#include <linux/blkdev.h>
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/gfp.h>
+#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
-#include <linux/string.h>
-#include <linux/stringify.h>
-#include <linux/slab.h>
-#include <linux/interrupt.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
-#include <linux/types.h>
-#include <linux/delay.h>
-#include <linux/sched.h>
-#include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <linux/init.h>
#include <linux/pci.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/stat.h>
+#include <linux/string.h>
+#include <linux/stringify.h>
+#include <linux/types.h>
+#include <linux/wait.h>
-#include <asm/current.h>
-#include <asm/uaccess.h>
-#include <asm/io.h>
#include <asm/byteorder.h>
-#include <asm/atomic.h>
-#include <asm/system.h>
+#include <asm/errno.h>
+#include <asm/param.h>
#include <asm/scatterlist.h>
+#include <asm/system.h>
+#include <asm/types.h>
+
+#ifdef CONFIG_IEEE1394_SBP2_PHYS_DMA
+#include <asm/io.h> /* for bus_to_virt */
+#endif
#include <scsi/scsi.h>
#include <scsi/scsi_cmnd.h>
@@ -71,13 +76,14 @@
#include <scsi/scsi_host.h>
#include "csr1212.h"
+#include "highlevel.h"
+#include "hosts.h"
#include "ieee1394.h"
-#include "ieee1394_types.h"
#include "ieee1394_core.h"
-#include "nodemgr.h"
-#include "hosts.h"
-#include "highlevel.h"
+#include "ieee1394_hotplug.h"
#include "ieee1394_transactions.h"
+#include "ieee1394_types.h"
+#include "nodemgr.h"
#include "sbp2.h"
/*
@@ -173,11 +179,6 @@ MODULE_PARM_DESC(workarounds, "Work around device bugs (default = 0"
", override internal blacklist = " __stringify(SBP2_WORKAROUND_OVERRIDE)
", or a combination)");
-/* legacy parameter */
-static int force_inquiry_hack;
-module_param(force_inquiry_hack, int, 0644);
-MODULE_PARM_DESC(force_inquiry_hack, "Deprecated, use 'workarounds'");
-
/*
* Export information about protocols/devices supported by this driver.
*/
@@ -208,9 +209,9 @@ static u32 global_outstanding_command_orbs = 0;
#define outstanding_orb_incr global_outstanding_command_orbs++
#define outstanding_orb_decr global_outstanding_command_orbs--
#else
-#define SBP2_ORB_DEBUG(fmt, args...)
-#define outstanding_orb_incr
-#define outstanding_orb_decr
+#define SBP2_ORB_DEBUG(fmt, args...) do {} while (0)
+#define outstanding_orb_incr do {} while (0)
+#define outstanding_orb_decr do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_SBP2_DEBUG_DMA
@@ -222,8 +223,8 @@ static u32 global_outstanding_command_orbs = 0;
--global_outstanding_dmas, ## args)
static u32 global_outstanding_dmas = 0;
#else
-#define SBP2_DMA_ALLOC(fmt, args...)
-#define SBP2_DMA_FREE(fmt, args...)
+#define SBP2_DMA_ALLOC(fmt, args...) do {} while (0)
+#define SBP2_DMA_FREE(fmt, args...) do {} while (0)
#endif
#if CONFIG_IEEE1394_SBP2_DEBUG >= 2
@@ -237,7 +238,7 @@ static u32 global_outstanding_dmas = 0;
#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args)
#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args)
#else
-#define SBP2_DEBUG(fmt, args...)
+#define SBP2_DEBUG(fmt, args...) do {} while (0)
#define SBP2_INFO(fmt, args...) HPSB_INFO("sbp2: "fmt, ## args)
#define SBP2_NOTICE(fmt, args...) HPSB_NOTICE("sbp2: "fmt, ## args)
#define SBP2_WARN(fmt, args...) HPSB_WARN("sbp2: "fmt, ## args)
@@ -356,7 +357,7 @@ static const struct {
/*
* Converts a buffer from be32 to cpu byte ordering. Length is in bytes.
*/
-static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
+static inline void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
{
u32 *temp = buffer;
@@ -369,7 +370,7 @@ static __inline__ void sbp2util_be32_to_cpu_buffer(void *buffer, int length)
/*
* Converts a buffer from cpu to be32 byte ordering. Length is in bytes.
*/
-static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
+static inline void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
{
u32 *temp = buffer;
@@ -380,8 +381,8 @@ static __inline__ void sbp2util_cpu_to_be32_buffer(void *buffer, int length)
}
#else /* BIG_ENDIAN */
/* Why waste the cpu cycles? */
-#define sbp2util_be32_to_cpu_buffer(x,y)
-#define sbp2util_cpu_to_be32_buffer(x,y)
+#define sbp2util_be32_to_cpu_buffer(x,y) do {} while (0)
+#define sbp2util_cpu_to_be32_buffer(x,y) do {} while (0)
#endif
#ifdef CONFIG_IEEE1394_SBP2_PACKET_DUMP
@@ -417,24 +418,26 @@ static void sbp2util_packet_dump(void *buffer, int length, char *dump_name,
return;
}
#else
-#define sbp2util_packet_dump(w,x,y,z)
+#define sbp2util_packet_dump(w,x,y,z) do {} while (0)
#endif
+static DECLARE_WAIT_QUEUE_HEAD(access_wq);
+
/*
- * Goofy routine that basically does a down_timeout function.
+ * Waits for completion of an SBP-2 access request.
+ * Returns nonzero if timed out or prematurely interrupted.
*/
-static int sbp2util_down_timeout(atomic_t *done, int timeout)
+static int sbp2util_access_timeout(struct scsi_id_instance_data *scsi_id,
+ int timeout)
{
- int i;
+ long leftover = wait_event_interruptible_timeout(
+ access_wq, scsi_id->access_complete, timeout);
- for (i = timeout; (i > 0 && atomic_read(done) == 0); i-= HZ/10) {
- if (msleep_interruptible(100)) /* 100ms */
- return 1;
- }
- return (i > 0) ? 0 : 1;
+ scsi_id->access_complete = 0;
+ return leftover <= 0;
}
-/* Free's an allocated packet */
+/* Frees an allocated packet */
static void sbp2_free_packet(struct hpsb_packet *packet)
{
hpsb_free_tlabel(packet);
@@ -468,6 +471,44 @@ static int sbp2util_node_write_no_wait(struct node_entry *ne, u64 addr,
return 0;
}
+static void sbp2util_notify_fetch_agent(struct scsi_id_instance_data *scsi_id,
+ u64 offset, quadlet_t *data, size_t len)
+{
+ /*
+ * There is a small window after a bus reset within which the node
+ * entry's generation is current but the reconnect wasn't completed.
+ */
+ if (unlikely(atomic_read(&scsi_id->state) == SBP2LU_STATE_IN_RESET))
+ return;
+
+ if (hpsb_node_write(scsi_id->ne,
+ scsi_id->sbp2_command_block_agent_addr + offset,
+ data, len))
+ SBP2_ERR("sbp2util_notify_fetch_agent failed.");
+ /*
+ * Now accept new SCSI commands, unless a bus reset happended during
+ * hpsb_node_write.
+ */
+ if (likely(atomic_read(&scsi_id->state) != SBP2LU_STATE_IN_RESET))
+ scsi_unblock_requests(scsi_id->scsi_host);
+}
+
+static void sbp2util_write_orb_pointer(void *p)
+{
+ quadlet_t data[2];
+
+ data[0] = ORB_SET_NODE_ID(
+ ((struct scsi_id_instance_data *)p)->hi->host->node_id);
+ data[1] = ((struct scsi_id_instance_data *)p)->last_orb_dma;
+ sbp2util_cpu_to_be32_buffer(data, 8);
+ sbp2util_notify_fetch_agent(p, SBP2_ORB_POINTER_OFFSET, data, 8);
+}
+
+static void sbp2util_write_doorbell(void *p)
+{
+ sbp2util_notify_fetch_agent(p, SBP2_DOORBELL_OFFSET, NULL, 4);
+}
+
/*
* This function is called to create a pool of command orbs used for
* command processing. It is called when a new sbp2 device is detected.
@@ -492,7 +533,7 @@ static int sbp2util_create_command_orb_pool(struct scsi_id_instance_data *scsi_i
command->command_orb_dma =
pci_map_single(hi->host->pdev, &command->command_orb,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
SBP2_DMA_ALLOC("single command orb DMA");
command->sge_dma =
pci_map_single(hi->host->pdev,
@@ -525,7 +566,7 @@ static void sbp2util_remove_command_orb_pool(struct scsi_id_instance_data *scsi_
/* Release our generic DMA's */
pci_unmap_single(host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
SBP2_DMA_FREE("single command orb DMA");
pci_unmap_single(host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
@@ -715,6 +756,7 @@ static int sbp2_remove(struct device *dev)
sbp2scsi_complete_all_commands(scsi_id, DID_NO_CONNECT);
/* scsi_remove_device() will trigger shutdown functions of SCSI
* highlevel drivers which would deadlock if blocked. */
+ atomic_set(&scsi_id->state, SBP2LU_STATE_IN_SHUTDOWN);
scsi_unblock_requests(scsi_id->scsi_host);
}
sdev = scsi_id->sdev;
@@ -766,10 +808,12 @@ static int sbp2_update(struct unit_directory *ud)
*/
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
- /* Make sure we unblock requests (since this is likely after a bus
- * reset). */
- scsi_unblock_requests(scsi_id->scsi_host);
-
+ /* Accept new commands unless there was another bus reset in the
+ * meantime. */
+ if (hpsb_node_entry_valid(scsi_id->ne)) {
+ atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING);
+ scsi_unblock_requests(scsi_id->scsi_host);
+ }
return 0;
}
@@ -794,11 +838,12 @@ static struct scsi_id_instance_data *sbp2_alloc_device(struct unit_directory *ud
scsi_id->speed_code = IEEE1394_SPEED_100;
scsi_id->max_payload_size = sbp2_speedto_max_payload[IEEE1394_SPEED_100];
scsi_id->status_fifo_addr = CSR1212_INVALID_ADDR_SPACE;
- atomic_set(&scsi_id->sbp2_login_complete, 0);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_inuse);
INIT_LIST_HEAD(&scsi_id->sbp2_command_orb_completed);
INIT_LIST_HEAD(&scsi_id->scsi_list);
spin_lock_init(&scsi_id->sbp2_command_orb_lock);
+ atomic_set(&scsi_id->state, SBP2LU_STATE_RUNNING);
+ INIT_WORK(&scsi_id->protocol_work, NULL, NULL);
ud->device.driver_data = scsi_id;
@@ -881,11 +926,14 @@ static void sbp2_host_reset(struct hpsb_host *host)
struct scsi_id_instance_data *scsi_id;
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
-
- if (hi) {
- list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+ if (!hi)
+ return;
+ list_for_each_entry(scsi_id, &hi->scsi_ids, scsi_list)
+ if (likely(atomic_read(&scsi_id->state) !=
+ SBP2LU_STATE_IN_SHUTDOWN)) {
+ atomic_set(&scsi_id->state, SBP2LU_STATE_IN_RESET);
scsi_block_requests(scsi_id->scsi_host);
- }
+ }
}
/*
@@ -970,8 +1018,7 @@ static int sbp2_start_device(struct scsi_id_instance_data *scsi_id)
* connected to the sbp2 device being removed. That host would
* have a certain amount of time to relogin before the sbp2 device
* allows someone else to login instead. One second makes sense. */
- msleep_interruptible(1000);
- if (signal_pending(current)) {
+ if (msleep_interruptible(1000)) {
sbp2_remove_device(scsi_id);
return -EINTR;
}
@@ -1036,7 +1083,7 @@ static void sbp2_remove_device(struct scsi_id_instance_data *scsi_id)
scsi_remove_host(scsi_id->scsi_host);
scsi_host_put(scsi_id->scsi_host);
}
-
+ flush_scheduled_work();
sbp2util_remove_command_orb_pool(scsi_id);
list_del(&scsi_id->scsi_list);
@@ -1182,17 +1229,14 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
"sbp2 query logins orb", scsi_id->query_logins_orb_dma);
memset(scsi_id->query_logins_response, 0, sizeof(struct sbp2_query_logins_response));
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->query_logins_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8);
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 2*HZ)) {
+ if (sbp2util_access_timeout(scsi_id, 2*HZ)) {
SBP2_INFO("Error querying logins to SBP-2 device - timed out");
return -EIO;
}
@@ -1202,11 +1246,8 @@ static int sbp2_query_logins(struct scsi_id_instance_data *scsi_id)
return -EIO;
}
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_INFO("Error querying logins to SBP-2 device - timed out");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_INFO("Error querying logins to SBP-2 device - failed");
return -EIO;
}
@@ -1278,21 +1319,18 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
"sbp2 login orb", scsi_id->login_orb_dma);
memset(scsi_id->login_response, 0, sizeof(struct sbp2_login_response));
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->login_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
hpsb_node_write(scsi_id->ne, scsi_id->sbp2_management_agent_addr, data, 8);
/*
* Wait for login status (up to 20 seconds)...
*/
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, 20*HZ)) {
- SBP2_ERR("Error logging into SBP-2 device - login timed-out");
+ if (sbp2util_access_timeout(scsi_id, 20*HZ)) {
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
return -EIO;
}
@@ -1300,18 +1338,12 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
* Sanity. Make sure status returned matches login orb.
*/
if (scsi_id->status_block.ORB_offset_lo != scsi_id->login_orb_dma) {
- SBP2_ERR("Error logging into SBP-2 device - login timed-out");
+ SBP2_ERR("Error logging into SBP-2 device - timed out");
return -EIO;
}
- /*
- * Check status
- */
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_ERR("Error logging into SBP-2 device - login failed");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error logging into SBP-2 device - failed");
return -EIO;
}
@@ -1335,9 +1367,7 @@ static int sbp2_login_device(struct scsi_id_instance_data *scsi_id)
scsi_id->sbp2_command_block_agent_addr &= 0x0000ffffffffffffULL;
SBP2_INFO("Logged into SBP-2 device");
-
return 0;
-
}
/*
@@ -1387,21 +1417,17 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id)
data[1] = scsi_id->logout_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
error = hpsb_node_write(scsi_id->ne,
scsi_id->sbp2_management_agent_addr, data, 8);
if (error)
return error;
/* Wait for device to logout...1 second. */
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ))
+ if (sbp2util_access_timeout(scsi_id, HZ))
return -EIO;
SBP2_INFO("Logged out of SBP-2 device");
-
return 0;
-
}
/*
@@ -1445,20 +1471,10 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
sbp2util_packet_dump(scsi_id->reconnect_orb, sizeof(struct sbp2_reconnect_orb),
"sbp2 reconnect orb", scsi_id->reconnect_orb_dma);
- /*
- * Initialize status fifo
- */
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
-
- /*
- * Ok, let's write to the target's management agent register
- */
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = scsi_id->reconnect_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
- atomic_set(&scsi_id->sbp2_login_complete, 0);
-
error = hpsb_node_write(scsi_id->ne,
scsi_id->sbp2_management_agent_addr, data, 8);
if (error)
@@ -1467,8 +1483,8 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
/*
* Wait for reconnect status (up to 1 second)...
*/
- if (sbp2util_down_timeout(&scsi_id->sbp2_login_complete, HZ)) {
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out");
+ if (sbp2util_access_timeout(scsi_id, HZ)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
return -EIO;
}
@@ -1476,25 +1492,17 @@ static int sbp2_reconnect_device(struct scsi_id_instance_data *scsi_id)
* Sanity. Make sure status returned matches reconnect orb.
*/
if (scsi_id->status_block.ORB_offset_lo != scsi_id->reconnect_orb_dma) {
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect timed-out");
+ SBP2_ERR("Error reconnecting to SBP-2 device - timed out");
return -EIO;
}
- /*
- * Check status
- */
- if (STATUS_GET_RESP(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc) ||
- STATUS_GET_SBP_STATUS(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- SBP2_ERR("Error reconnecting to SBP-2 device - reconnect failed");
+ if (STATUS_TEST_RDS(scsi_id->status_block.ORB_offset_hi_misc)) {
+ SBP2_ERR("Error reconnecting to SBP-2 device - failed");
return -EIO;
}
HPSB_DEBUG("Reconnected to SBP-2 device");
-
return 0;
-
}
/*
@@ -1592,11 +1600,6 @@ static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
}
workarounds = sbp2_default_workarounds;
- if (force_inquiry_hack) {
- SBP2_WARN("force_inquiry_hack is deprecated. "
- "Use parameter 'workarounds' instead.");
- workarounds |= SBP2_WORKAROUND_INQUIRY_36;
- }
if (!(workarounds & SBP2_WORKAROUND_OVERRIDE))
for (i = 0; i < ARRAY_SIZE(sbp2_workarounds_table); i++) {
@@ -1705,9 +1708,14 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait)
quadlet_t data;
u64 addr;
int retval;
+ unsigned long flags;
SBP2_DEBUG_ENTER();
+ cancel_delayed_work(&scsi_id->protocol_work);
+ if (wait)
+ flush_scheduled_work();
+
data = ntohl(SBP2_AGENT_RESET_DATA);
addr = scsi_id->sbp2_command_block_agent_addr + SBP2_AGENT_RESET_OFFSET;
@@ -1724,7 +1732,9 @@ static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait)
/*
* Need to make sure orb pointer is written on next command
*/
+ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
scsi_id->last_orb = NULL;
+ spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
return 0;
}
@@ -1961,13 +1971,17 @@ static void sbp2_create_command_orb(struct scsi_id_instance_data *scsi_id,
/*
* This function is called in order to begin a regular SBP-2 command.
*/
-static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
+static void sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
struct sbp2_command_info *command)
{
struct sbp2scsi_host_info *hi = scsi_id->hi;
struct sbp2_command_orb *command_orb = &command->command_orb;
- struct node_entry *ne = scsi_id->ne;
- u64 addr;
+ struct sbp2_command_orb *last_orb;
+ dma_addr_t last_orb_dma;
+ u64 addr = scsi_id->sbp2_command_block_agent_addr;
+ quadlet_t data[2];
+ size_t length;
+ unsigned long flags;
outstanding_orb_incr;
SBP2_ORB_DEBUG("sending command orb %p, total orbs = %x",
@@ -1975,73 +1989,70 @@ static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
pci_dma_sync_single_for_device(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_device(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
/*
* Check to see if there are any previous orbs to use
*/
- if (scsi_id->last_orb == NULL) {
- quadlet_t data[2];
-
+ spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
+ last_orb = scsi_id->last_orb;
+ last_orb_dma = scsi_id->last_orb_dma;
+ if (!last_orb) {
/*
- * Ok, let's write to the target's management agent register
+ * last_orb == NULL means: We know that the target's fetch agent
+ * is not active right now.
*/
- addr = scsi_id->sbp2_command_block_agent_addr + SBP2_ORB_POINTER_OFFSET;
+ addr += SBP2_ORB_POINTER_OFFSET;
data[0] = ORB_SET_NODE_ID(hi->host->node_id);
data[1] = command->command_orb_dma;
sbp2util_cpu_to_be32_buffer(data, 8);
-
- SBP2_ORB_DEBUG("write command agent, command orb %p", command_orb);
-
- if (sbp2util_node_write_no_wait(ne, addr, data, 8) < 0) {
- SBP2_ERR("sbp2util_node_write_no_wait failed.\n");
- return -EIO;
- }
-
- SBP2_ORB_DEBUG("write command agent complete");
-
- scsi_id->last_orb = command_orb;
- scsi_id->last_orb_dma = command->command_orb_dma;
-
+ length = 8;
} else {
- quadlet_t data;
-
/*
- * We have an orb already sent (maybe or maybe not
- * processed) that we can append this orb to. So do so,
- * and ring the doorbell. Have to be very careful
- * modifying these next orb pointers, as they are accessed
- * both by the sbp2 device and us.
+ * last_orb != NULL means: We know that the target's fetch agent
+ * is (very probably) not dead or in reset state right now.
+ * We have an ORB already sent that we can append a new one to.
+ * The target's fetch agent may or may not have read this
+ * previous ORB yet.
*/
- scsi_id->last_orb->next_ORB_lo =
- cpu_to_be32(command->command_orb_dma);
+ pci_dma_sync_single_for_cpu(hi->host->pdev, last_orb_dma,
+ sizeof(struct sbp2_command_orb),
+ PCI_DMA_TODEVICE);
+ last_orb->next_ORB_lo = cpu_to_be32(command->command_orb_dma);
+ wmb();
/* Tells hardware that this pointer is valid */
- scsi_id->last_orb->next_ORB_hi = 0x0;
- pci_dma_sync_single_for_device(hi->host->pdev,
- scsi_id->last_orb_dma,
+ last_orb->next_ORB_hi = 0;
+ pci_dma_sync_single_for_device(hi->host->pdev, last_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
+ addr += SBP2_DOORBELL_OFFSET;
+ data[0] = 0;
+ length = 4;
+ }
+ scsi_id->last_orb = command_orb;
+ scsi_id->last_orb_dma = command->command_orb_dma;
+ spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
+ SBP2_ORB_DEBUG("write to %s register, command orb %p",
+ last_orb ? "DOORBELL" : "ORB_POINTER", command_orb);
+ if (sbp2util_node_write_no_wait(scsi_id->ne, addr, data, length)) {
/*
- * Ring the doorbell
+ * sbp2util_node_write_no_wait failed. We certainly ran out
+ * of transaction labels, perhaps just because there were no
+ * context switches which gave khpsbpkt a chance to collect
+ * free tlabels. Try again in non-atomic context. If necessary,
+ * the workqueue job will sleep to guaranteedly get a tlabel.
+ * We do not accept new commands until the job is over.
*/
- data = cpu_to_be32(command->command_orb_dma);
- addr = scsi_id->sbp2_command_block_agent_addr + SBP2_DOORBELL_OFFSET;
-
- SBP2_ORB_DEBUG("ring doorbell, command orb %p", command_orb);
-
- if (sbp2util_node_write_no_wait(ne, addr, &data, 4) < 0) {
- SBP2_ERR("sbp2util_node_write_no_wait failed");
- return -EIO;
- }
-
- scsi_id->last_orb = command_orb;
- scsi_id->last_orb_dma = command->command_orb_dma;
-
+ scsi_block_requests(scsi_id->scsi_host);
+ PREPARE_WORK(&scsi_id->protocol_work,
+ last_orb ? sbp2util_write_doorbell:
+ sbp2util_write_orb_pointer,
+ scsi_id);
+ schedule_work(&scsi_id->protocol_work);
}
- return 0;
}
/*
@@ -2078,11 +2089,6 @@ static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
"sbp2 command orb", command->command_orb_dma);
/*
- * Initialize status fifo
- */
- memset(&scsi_id->status_block, 0, sizeof(struct sbp2_status_block));
-
- /*
* Link up the orb, and ring the doorbell if needed
*/
sbp2_link_orb_command(scsi_id, command);
@@ -2123,12 +2129,14 @@ static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status, unchar *sense
/*
* This function deals with status writes from the SBP-2 device
*/
-static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
- quadlet_t *data, u64 addr, size_t length, u16 fl)
+static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid,
+ int destid, quadlet_t *data, u64 addr,
+ size_t length, u16 fl)
{
struct sbp2scsi_host_info *hi;
struct scsi_id_instance_data *scsi_id = NULL, *scsi_id_tmp;
struct scsi_cmnd *SCpnt = NULL;
+ struct sbp2_status_block *sb;
u32 scsi_status = SBP2_SCSI_STATUS_GOOD;
struct sbp2_command_info *command;
unsigned long flags;
@@ -2137,18 +2145,19 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
sbp2util_packet_dump(data, length, "sbp2 status write by device", (u32)addr);
- if (!host) {
+ if (unlikely(length < 8 || length > sizeof(struct sbp2_status_block))) {
+ SBP2_ERR("Wrong size of status block");
+ return RCODE_ADDRESS_ERROR;
+ }
+ if (unlikely(!host)) {
SBP2_ERR("host is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
-
hi = hpsb_get_hostinfo(&sbp2_highlevel, host);
-
- if (!hi) {
+ if (unlikely(!hi)) {
SBP2_ERR("host info is NULL - this is bad!");
return RCODE_ADDRESS_ERROR;
}
-
/*
* Find our scsi_id structure by looking at the status fifo address
* written to by the sbp2 device.
@@ -2160,32 +2169,35 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
break;
}
}
-
- if (!scsi_id) {
+ if (unlikely(!scsi_id)) {
SBP2_ERR("scsi_id is NULL - device is gone?");
return RCODE_ADDRESS_ERROR;
}
/*
- * Put response into scsi_id status fifo...
+ * Put response into scsi_id status fifo buffer. The first two bytes
+ * come in big endian bit order. Often the target writes only a
+ * truncated status block, minimally the first two quadlets. The rest
+ * is implied to be zeros.
*/
- memcpy(&scsi_id->status_block, data, length);
+ sb = &scsi_id->status_block;
+ memset(sb->command_set_dependent, 0, sizeof(sb->command_set_dependent));
+ memcpy(sb, data, length);
+ sbp2util_be32_to_cpu_buffer(sb, 8);
/*
- * Byte swap first two quadlets (8 bytes) of status for processing
+ * Ignore unsolicited status. Handle command ORB status.
*/
- sbp2util_be32_to_cpu_buffer(&scsi_id->status_block, 8);
-
- /*
- * Handle command ORB status here if necessary. First, need to match status with command.
- */
- command = sbp2util_find_command_for_orb(scsi_id, scsi_id->status_block.ORB_offset_lo);
+ if (unlikely(STATUS_GET_SRC(sb->ORB_offset_hi_misc) == 2))
+ command = NULL;
+ else
+ command = sbp2util_find_command_for_orb(scsi_id,
+ sb->ORB_offset_lo);
if (command) {
-
SBP2_DEBUG("Found status for command ORB");
pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
@@ -2194,7 +2206,12 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
outstanding_orb_decr;
/*
- * Matched status with command, now grab scsi command pointers and check status
+ * Matched status with command, now grab scsi command pointers
+ * and check status.
+ */
+ /*
+ * FIXME: If the src field in the status is 1, the ORB DMA must
+ * not be reused until status for a subsequent ORB is received.
*/
SCpnt = command->Current_SCpnt;
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
@@ -2202,61 +2219,64 @@ static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int dest
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
if (SCpnt) {
-
+ u32 h = sb->ORB_offset_hi_misc;
+ u32 r = STATUS_GET_RESP(h);
+
+ if (r != RESP_STATUS_REQUEST_COMPLETE) {
+ SBP2_WARN("resp 0x%x, sbp_status 0x%x",
+ r, STATUS_GET_SBP_STATUS(h));
+ scsi_status =
+ r == RESP_STATUS_TRANSPORT_FAILURE ?
+ SBP2_SCSI_STATUS_BUSY :
+ SBP2_SCSI_STATUS_COMMAND_TERMINATED;
+ }
/*
- * See if the target stored any scsi status information
+ * See if the target stored any scsi status information.
*/
- if (STATUS_GET_LENGTH(scsi_id->status_block.ORB_offset_hi_misc) > 1) {
- /*
- * Translate SBP-2 status to SCSI sense data
- */
+ if (STATUS_GET_LEN(h) > 1) {
SBP2_DEBUG("CHECK CONDITION");
- scsi_status = sbp2_status_to_sense_data((unchar *)&scsi_id->status_block, SCpnt->sense_buffer);
+ scsi_status = sbp2_status_to_sense_data(
+ (unchar *)sb, SCpnt->sense_buffer);
}
-
/*
- * Check to see if the dead bit is set. If so, we'll have to initiate
- * a fetch agent reset.
+ * Check to see if the dead bit is set. If so, we'll
+ * have to initiate a fetch agent reset.
*/
- if (STATUS_GET_DEAD_BIT(scsi_id->status_block.ORB_offset_hi_misc)) {
-
- /*
- * Initiate a fetch agent reset.
- */
- SBP2_DEBUG("Dead bit set - initiating fetch agent reset");
+ if (STATUS_TEST_DEAD(h)) {
+ SBP2_DEBUG("Dead bit set - "
+ "initiating fetch agent reset");
sbp2_agent_reset(scsi_id, 0);
}
-
SBP2_ORB_DEBUG("completing command orb %p", &command->command_orb);
}
/*
- * Check here to see if there are no commands in-use. If there are none, we can
- * null out last orb so that next time around we write directly to the orb pointer...
- * Quick start saves one 1394 bus transaction.
+ * Check here to see if there are no commands in-use. If there
+ * are none, we know that the fetch agent left the active state
+ * _and_ that we did not reactivate it yet. Therefore clear
+ * last_orb so that next time we write directly to the
+ * ORB_POINTER register. That way the fetch agent does not need
+ * to refetch the next_ORB.
*/
spin_lock_irqsave(&scsi_id->sbp2_command_orb_lock, flags);
- if (list_empty(&scsi_id->sbp2_command_orb_inuse)) {
+ if (list_empty(&scsi_id->sbp2_command_orb_inuse))
scsi_id->last_orb = NULL;
- }
spin_unlock_irqrestore(&scsi_id->sbp2_command_orb_lock, flags);
} else {
-
/*
* It's probably a login/logout/reconnect status.
*/
- if ((scsi_id->login_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->query_logins_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->reconnect_orb_dma == scsi_id->status_block.ORB_offset_lo) ||
- (scsi_id->logout_orb_dma == scsi_id->status_block.ORB_offset_lo)) {
- atomic_set(&scsi_id->sbp2_login_complete, 1);
+ if ((sb->ORB_offset_lo == scsi_id->reconnect_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->login_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->query_logins_orb_dma) ||
+ (sb->ORB_offset_lo == scsi_id->logout_orb_dma)) {
+ scsi_id->access_complete = 1;
+ wake_up_interruptible(&access_wq);
}
}
if (SCpnt) {
-
- /* Complete the SCSI command. */
SBP2_DEBUG("Completing SCSI command");
sbp2scsi_complete_command(scsi_id, scsi_status, SCpnt,
command->Current_done);
@@ -2372,7 +2392,7 @@ static void sbp2scsi_complete_all_commands(struct scsi_id_instance_data *scsi_id
command = list_entry(lh, struct sbp2_command_info, list);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev, command->sge_dma,
sizeof(command->scatter_gather_element),
PCI_DMA_BIDIRECTIONAL);
@@ -2495,6 +2515,7 @@ static int sbp2scsi_slave_alloc(struct scsi_device *sdev)
(struct scsi_id_instance_data *)sdev->host->hostdata[0];
scsi_id->sdev = sdev;
+ sdev->allow_restart = 1;
if (scsi_id->workarounds & SBP2_WORKAROUND_INQUIRY_36)
sdev->inquiry_len = 36;
@@ -2508,16 +2529,12 @@ static int sbp2scsi_slave_configure(struct scsi_device *sdev)
blk_queue_dma_alignment(sdev->request_queue, (512 - 1));
sdev->use_10_for_rw = 1;
- sdev->use_10_for_ms = 1;
if (sdev->type == TYPE_DISK &&
scsi_id->workarounds & SBP2_WORKAROUND_MODE_SENSE_8)
sdev->skip_ms_page_8 = 1;
if (scsi_id->workarounds & SBP2_WORKAROUND_FIX_CAPACITY)
sdev->fix_capacity = 1;
- if (scsi_id->ne->guid_vendor_id == 0x0010b9 && /* Maxtor's OUI */
- (sdev->type == TYPE_DISK || sdev->type == TYPE_RBC))
- sdev->allow_restart = 1;
return 0;
}
@@ -2555,7 +2572,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
pci_dma_sync_single_for_cpu(hi->host->pdev,
command->command_orb_dma,
sizeof(struct sbp2_command_orb),
- PCI_DMA_BIDIRECTIONAL);
+ PCI_DMA_TODEVICE);
pci_dma_sync_single_for_cpu(hi->host->pdev,
command->sge_dma,
sizeof(command->scatter_gather_element),
@@ -2571,7 +2588,7 @@ static int sbp2scsi_abort(struct scsi_cmnd *SCpnt)
/*
* Initiate a fetch agent reset.
*/
- sbp2_agent_reset(scsi_id, 0);
+ sbp2_agent_reset(scsi_id, 1);
sbp2scsi_complete_all_commands(scsi_id, DID_BUS_BUSY);
}
@@ -2590,7 +2607,7 @@ static int sbp2scsi_reset(struct scsi_cmnd *SCpnt)
if (sbp2util_node_is_available(scsi_id)) {
SBP2_ERR("Generating sbp2 fetch agent reset");
- sbp2_agent_reset(scsi_id, 0);
+ sbp2_agent_reset(scsi_id, 1);
}
return SUCCESS;
diff --git a/drivers/ieee1394/sbp2.h b/drivers/ieee1394/sbp2.h
index b22ce1aa8fe..abbe48e646c 100644
--- a/drivers/ieee1394/sbp2.h
+++ b/drivers/ieee1394/sbp2.h
@@ -46,8 +46,8 @@
#define ORB_SET_DIRECTION(value) ((value & 0x1) << 27)
struct sbp2_command_orb {
- volatile u32 next_ORB_hi;
- volatile u32 next_ORB_lo;
+ u32 next_ORB_hi;
+ u32 next_ORB_lo;
u32 data_descriptor_hi;
u32 data_descriptor_lo;
u32 misc;
@@ -180,12 +180,14 @@ struct sbp2_unrestricted_page_table {
#define SBP2_SCSI_STATUS_SELECTION_TIMEOUT 0xff
-#define STATUS_GET_ORB_OFFSET_HI(value) (value & 0xffff)
-#define STATUS_GET_SBP_STATUS(value) ((value >> 16) & 0xff)
-#define STATUS_GET_LENGTH(value) ((value >> 24) & 0x7)
-#define STATUS_GET_DEAD_BIT(value) ((value >> 27) & 0x1)
-#define STATUS_GET_RESP(value) ((value >> 28) & 0x3)
-#define STATUS_GET_SRC(value) ((value >> 30) & 0x3)
+#define STATUS_GET_SRC(value) (((value) >> 30) & 0x3)
+#define STATUS_GET_RESP(value) (((value) >> 28) & 0x3)
+#define STATUS_GET_LEN(value) (((value) >> 24) & 0x7)
+#define STATUS_GET_SBP_STATUS(value) (((value) >> 16) & 0xff)
+#define STATUS_GET_ORB_OFFSET_HI(value) ((value) & 0x0000ffff)
+#define STATUS_TEST_DEAD(value) ((value) & 0x08000000)
+/* test 'resp' | 'dead' | 'sbp2_status' */
+#define STATUS_TEST_RDS(value) ((value) & 0x38ff0000)
struct sbp2_status_block {
u32 ORB_offset_hi_misc;
@@ -318,9 +320,9 @@ struct scsi_id_instance_data {
u64 status_fifo_addr;
/*
- * Variable used for logins, reconnects, logouts, query logins
+ * Waitqueue flag for logins, reconnects, logouts, query logins
*/
- atomic_t sbp2_login_complete;
+ int access_complete:1;
/*
* Pool of command orbs, so we can have more than overlapped command per id
@@ -344,6 +346,16 @@ struct scsi_id_instance_data {
/* Device specific workarounds/brokeness */
unsigned workarounds;
+
+ atomic_t state;
+ struct work_struct protocol_work;
+};
+
+/* For use in scsi_id_instance_data.state */
+enum sbp2lu_state_types {
+ SBP2LU_STATE_RUNNING, /* all normal */
+ SBP2LU_STATE_IN_RESET, /* between bus reset and reconnect */
+ SBP2LU_STATE_IN_SHUTDOWN /* when sbp2_remove was called */
};
/* Sbp2 host data structure (one per IEEE1394 host) */
@@ -390,11 +402,6 @@ static int sbp2_logout_device(struct scsi_id_instance_data *scsi_id);
static int sbp2_handle_status_write(struct hpsb_host *host, int nodeid, int destid,
quadlet_t *data, u64 addr, size_t length, u16 flags);
static int sbp2_agent_reset(struct scsi_id_instance_data *scsi_id, int wait);
-static int sbp2_link_orb_command(struct scsi_id_instance_data *scsi_id,
- struct sbp2_command_info *command);
-static int sbp2_send_command(struct scsi_id_instance_data *scsi_id,
- struct scsi_cmnd *SCpnt,
- void (*done)(struct scsi_cmnd *));
static unsigned int sbp2_status_to_sense_data(unchar *sbp2_status,
unchar *sense_data);
static void sbp2_parse_unit_directory(struct scsi_id_instance_data *scsi_id,
diff --git a/drivers/ieee1394/video1394.c b/drivers/ieee1394/video1394.c
index c6e3f02bc6d..9bc65059cc6 100644
--- a/drivers/ieee1394/video1394.c
+++ b/drivers/ieee1394/video1394.c
@@ -49,16 +49,16 @@
#include <linux/compat.h>
#include <linux/cdev.h>
-#include "ieee1394.h"
-#include "ieee1394_types.h"
+#include "dma.h"
+#include "highlevel.h"
#include "hosts.h"
+#include "ieee1394.h"
#include "ieee1394_core.h"
-#include "highlevel.h"
-#include "video1394.h"
+#include "ieee1394_hotplug.h"
+#include "ieee1394_types.h"
#include "nodemgr.h"
-#include "dma.h"
-
#include "ohci1394.h"
+#include "video1394.h"
#define ISO_CHANNELS 64
@@ -129,7 +129,7 @@ struct file_ctx {
#define DBGMSG(card, fmt, args...) \
printk(KERN_INFO "video1394_%d: " fmt "\n" , card , ## args)
#else
-#define DBGMSG(card, fmt, args...)
+#define DBGMSG(card, fmt, args...) do {} while (0)
#endif
/* print general (card independent) information */
@@ -1181,7 +1181,8 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
lock_kernel();
if (ctx->current_ctx == NULL) {
- PRINT(KERN_ERR, ctx->ohci->host->id, "Current iso context not set");
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
} else
res = dma_region_mmap(&ctx->current_ctx->dma, file, vma);
unlock_kernel();
@@ -1189,6 +1190,40 @@ static int video1394_mmap(struct file *file, struct vm_area_struct *vma)
return res;
}
+static unsigned int video1394_poll(struct file *file, poll_table *pt)
+{
+ struct file_ctx *ctx;
+ unsigned int mask = 0;
+ unsigned long flags;
+ struct dma_iso_ctx *d;
+ int i;
+
+ lock_kernel();
+ ctx = file->private_data;
+ d = ctx->current_ctx;
+ if (d == NULL) {
+ PRINT(KERN_ERR, ctx->ohci->host->id,
+ "Current iso context not set");
+ mask = POLLERR;
+ goto done;
+ }
+
+ poll_wait(file, &d->waitq, pt);
+
+ spin_lock_irqsave(&d->lock, flags);
+ for (i = 0; i < d->num_desc; i++) {
+ if (d->buffer_status[i] == VIDEO1394_BUFFER_READY) {
+ mask |= POLLIN | POLLRDNORM;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&d->lock, flags);
+done:
+ unlock_kernel();
+
+ return mask;
+}
+
static int video1394_open(struct inode *inode, struct file *file)
{
int i = ieee1394_file_to_instance(file);
@@ -1257,6 +1292,7 @@ static struct file_operations video1394_fops=
#ifdef CONFIG_COMPAT
.compat_ioctl = video1394_compat_ioctl,
#endif
+ .poll = video1394_poll,
.mmap = video1394_mmap,
.open = video1394_open,
.release = video1394_release
diff --git a/drivers/infiniband/core/addr.c b/drivers/infiniband/core/addr.c
index 9cbf09e2052..60d3fbdd216 100644
--- a/drivers/infiniband/core/addr.c
+++ b/drivers/infiniband/core/addr.c
@@ -86,7 +86,7 @@ EXPORT_SYMBOL(rdma_copy_addr);
int rdma_translate_ip(struct sockaddr *addr, struct rdma_dev_addr *dev_addr)
{
struct net_device *dev;
- u32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
+ __be32 ip = ((struct sockaddr_in *) addr)->sin_addr.s_addr;
int ret;
dev = ip_dev_find(ip);
@@ -239,7 +239,7 @@ static int addr_resolve_local(struct sockaddr_in *src_in,
{
struct net_device *dev;
u32 src_ip = src_in->sin_addr.s_addr;
- u32 dst_ip = dst_in->sin_addr.s_addr;
+ __be32 dst_ip = dst_in->sin_addr.s_addr;
int ret;
dev = ip_dev_find(dst_ip);
diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c
index 1178bd434d1..9ae4f3a67c7 100644
--- a/drivers/infiniband/core/cma.c
+++ b/drivers/infiniband/core/cma.c
@@ -874,23 +874,25 @@ static struct rdma_id_private *cma_new_id(struct rdma_cm_id *listen_id,
__u16 port;
u8 ip_ver;
+ if (cma_get_net_info(ib_event->private_data, listen_id->ps,
+ &ip_ver, &port, &src, &dst))
+ goto err;
+
id = rdma_create_id(listen_id->event_handler, listen_id->context,
listen_id->ps);
if (IS_ERR(id))
- return NULL;
+ goto err;
+
+ cma_save_net_info(&id->route.addr, &listen_id->route.addr,
+ ip_ver, port, src, dst);
rt = &id->route;
rt->num_paths = ib_event->param.req_rcvd.alternate_path ? 2 : 1;
- rt->path_rec = kmalloc(sizeof *rt->path_rec * rt->num_paths, GFP_KERNEL);
+ rt->path_rec = kmalloc(sizeof *rt->path_rec * rt->num_paths,
+ GFP_KERNEL);
if (!rt->path_rec)
- goto err;
+ goto destroy_id;
- if (cma_get_net_info(ib_event->private_data, listen_id->ps,
- &ip_ver, &port, &src, &dst))
- goto err;
-
- cma_save_net_info(&id->route.addr, &listen_id->route.addr,
- ip_ver, port, src, dst);
rt->path_rec[0] = *ib_event->param.req_rcvd.primary_path;
if (rt->num_paths == 2)
rt->path_rec[1] = *ib_event->param.req_rcvd.alternate_path;
@@ -903,8 +905,10 @@ static struct rdma_id_private *cma_new_id(struct rdma_cm_id *listen_id,
id_priv = container_of(id, struct rdma_id_private, id);
id_priv->state = CMA_CONNECT;
return id_priv;
-err:
+
+destroy_id:
rdma_destroy_id(id);
+err:
return NULL;
}
@@ -932,6 +936,7 @@ static int cma_req_handler(struct ib_cm_id *cm_id, struct ib_cm_event *ib_event)
mutex_unlock(&lock);
if (ret) {
ret = -ENODEV;
+ cma_exch(conn_id, CMA_DESTROYING);
cma_release_remove(conn_id);
rdma_destroy_id(&conn_id->id);
goto out;
@@ -1307,6 +1312,7 @@ static void cma_query_handler(int status, struct ib_sa_path_rec *path_rec,
work->old_state = CMA_ROUTE_QUERY;
work->new_state = CMA_ADDR_RESOLVED;
work->event.event = RDMA_CM_EVENT_ROUTE_ERROR;
+ work->event.status = status;
}
queue_work(cma_wq, &work->work);
@@ -1862,6 +1868,11 @@ static int cma_connect_ib(struct rdma_id_private *id_priv,
ret = ib_send_cm_req(id_priv->cm_id.ib, &req);
out:
+ if (ret && !IS_ERR(id_priv->cm_id.ib)) {
+ ib_destroy_cm_id(id_priv->cm_id.ib);
+ id_priv->cm_id.ib = NULL;
+ }
+
kfree(private_data);
return ret;
}
@@ -1889,10 +1900,8 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
cm_id->remote_addr = *sin;
ret = cma_modify_qp_rtr(&id_priv->id);
- if (ret) {
- iw_destroy_cm_id(cm_id);
- return ret;
- }
+ if (ret)
+ goto out;
iw_param.ord = conn_param->initiator_depth;
iw_param.ird = conn_param->responder_resources;
@@ -1904,6 +1913,10 @@ static int cma_connect_iw(struct rdma_id_private *id_priv,
iw_param.qpn = conn_param->qp_num;
ret = iw_cm_connect(cm_id, &iw_param);
out:
+ if (ret && !IS_ERR(cm_id)) {
+ iw_destroy_cm_id(cm_id);
+ id_priv->cm_id.iw = NULL;
+ }
return ret;
}
@@ -2142,12 +2155,9 @@ static int cma_remove_id_dev(struct rdma_id_private *id_priv)
static void cma_process_remove(struct cma_device *cma_dev)
{
- struct list_head remove_list;
struct rdma_id_private *id_priv;
int ret;
- INIT_LIST_HEAD(&remove_list);
-
mutex_lock(&lock);
while (!list_empty(&cma_dev->id_list)) {
id_priv = list_entry(cma_dev->id_list.next,
@@ -2158,8 +2168,7 @@ static void cma_process_remove(struct cma_device *cma_dev)
continue;
}
- list_del(&id_priv->list);
- list_add_tail(&id_priv->list, &remove_list);
+ list_del_init(&id_priv->list);
atomic_inc(&id_priv->refcount);
mutex_unlock(&lock);
diff --git a/drivers/infiniband/core/mad.c b/drivers/infiniband/core/mad.c
index 082f03c158f..493f4c65c7a 100644
--- a/drivers/infiniband/core/mad.c
+++ b/drivers/infiniband/core/mad.c
@@ -2987,10 +2987,7 @@ error1:
static void __exit ib_mad_cleanup_module(void)
{
ib_unregister_client(&mad_client);
-
- if (kmem_cache_destroy(ib_mad_cache)) {
- printk(KERN_DEBUG PFX "Failed to destroy ib_mad cache\n");
- }
+ kmem_cache_destroy(ib_mad_cache);
}
module_init(ib_mad_init_module);
diff --git a/drivers/infiniband/hw/amso1100/c2_ae.c b/drivers/infiniband/hw/amso1100/c2_ae.c
index 08f46c83a3a..3aae4978e1c 100644
--- a/drivers/infiniband/hw/amso1100/c2_ae.c
+++ b/drivers/infiniband/hw/amso1100/c2_ae.c
@@ -197,7 +197,7 @@ void c2_ae_event(struct c2_dev *c2dev, u32 mq_index)
"resource=%x, qp_state=%s\n",
__FUNCTION__,
to_event_str(event_id),
- be64_to_cpu(wr->ae.ae_generic.user_context),
+ (unsigned long long) be64_to_cpu(wr->ae.ae_generic.user_context),
be32_to_cpu(wr->ae.ae_generic.resource_type),
be32_to_cpu(wr->ae.ae_generic.resource),
to_qp_state_str(be32_to_cpu(wr->ae.ae_generic.qp_state)));
diff --git a/drivers/infiniband/hw/amso1100/c2_alloc.c b/drivers/infiniband/hw/amso1100/c2_alloc.c
index 1d2529992c0..028a60bbfca 100644
--- a/drivers/infiniband/hw/amso1100/c2_alloc.c
+++ b/drivers/infiniband/hw/amso1100/c2_alloc.c
@@ -115,7 +115,7 @@ u16 *c2_alloc_mqsp(struct c2_dev *c2dev, struct sp_chunk *head,
((unsigned long) &(head->shared_ptr[mqsp]) -
(unsigned long) head);
pr_debug("%s addr %p dma_addr %llx\n", __FUNCTION__,
- &(head->shared_ptr[mqsp]), (u64)*dma_addr);
+ &(head->shared_ptr[mqsp]), (unsigned long long) *dma_addr);
return &(head->shared_ptr[mqsp]);
}
return NULL;
diff --git a/drivers/infiniband/hw/amso1100/c2_cm.c b/drivers/infiniband/hw/amso1100/c2_cm.c
index 485254efdd1..75b93e9b881 100644
--- a/drivers/infiniband/hw/amso1100/c2_cm.c
+++ b/drivers/infiniband/hw/amso1100/c2_cm.c
@@ -302,7 +302,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
vq_req = vq_req_alloc(c2dev);
if (!vq_req) {
err = -ENOMEM;
- goto bail1;
+ goto bail0;
}
vq_req->qp = qp;
vq_req->cm_id = cm_id;
@@ -311,7 +311,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
wr = kmalloc(c2dev->req_vq.msg_size, GFP_KERNEL);
if (!wr) {
err = -ENOMEM;
- goto bail2;
+ goto bail1;
}
/* Build the WR */
@@ -331,7 +331,7 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
/* Validate private_data length */
if (iw_param->private_data_len > C2_MAX_PRIVATE_DATA_SIZE) {
err = -EINVAL;
- goto bail2;
+ goto bail1;
}
if (iw_param->private_data) {
@@ -348,19 +348,19 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
err = vq_send_wr(c2dev, (union c2wr *) wr);
if (err) {
vq_req_put(c2dev, vq_req);
- goto bail2;
+ goto bail1;
}
/* Wait for reply from adapter */
err = vq_wait_for_reply(c2dev, vq_req);
if (err)
- goto bail2;
+ goto bail1;
/* Check that reply is present */
reply = (struct c2wr_cr_accept_rep *) (unsigned long) vq_req->reply_msg;
if (!reply) {
err = -ENOMEM;
- goto bail2;
+ goto bail1;
}
err = c2_errno(reply);
@@ -368,9 +368,8 @@ int c2_llp_accept(struct iw_cm_id *cm_id, struct iw_cm_conn_param *iw_param)
if (!err)
c2_set_qp_state(qp, C2_QP_STATE_RTS);
- bail2:
- kfree(wr);
bail1:
+ kfree(wr);
vq_req_free(c2dev, vq_req);
bail0:
if (err) {
diff --git a/drivers/infiniband/hw/amso1100/c2_provider.c b/drivers/infiniband/hw/amso1100/c2_provider.c
index dd6af551108..da98d9f7142 100644
--- a/drivers/infiniband/hw/amso1100/c2_provider.c
+++ b/drivers/infiniband/hw/amso1100/c2_provider.c
@@ -390,14 +390,18 @@ static struct ib_mr *c2_reg_phys_mr(struct ib_pd *ib_pd,
}
mr = kmalloc(sizeof(*mr), GFP_KERNEL);
- if (!mr)
+ if (!mr) {
+ vfree(page_list);
return ERR_PTR(-ENOMEM);
+ }
mr->pd = to_c2pd(ib_pd);
pr_debug("%s - page shift %d, pbl_depth %d, total_len %u, "
"*iova_start %llx, first pa %llx, last pa %llx\n",
__FUNCTION__, page_shift, pbl_depth, total_len,
- *iova_start, page_list[0], page_list[pbl_depth-1]);
+ (unsigned long long) *iova_start,
+ (unsigned long long) page_list[0],
+ (unsigned long long) page_list[pbl_depth-1]);
err = c2_nsmr_register_phys_kern(to_c2dev(ib_pd->device), page_list,
(1 << page_shift), pbl_depth,
total_len, 0, iova_start,
diff --git a/drivers/infiniband/hw/amso1100/c2_rnic.c b/drivers/infiniband/hw/amso1100/c2_rnic.c
index f49a32b7a8f..e37c5688c21 100644
--- a/drivers/infiniband/hw/amso1100/c2_rnic.c
+++ b/drivers/infiniband/hw/amso1100/c2_rnic.c
@@ -527,7 +527,7 @@ int c2_rnic_init(struct c2_dev *c2dev)
DMA_FROM_DEVICE);
pci_unmap_addr_set(&c2dev->rep_vq, mapping, c2dev->rep_vq.host_dma);
pr_debug("%s rep_vq va %p dma %llx\n", __FUNCTION__, q1_pages,
- (u64)c2dev->rep_vq.host_dma);
+ (unsigned long long) c2dev->rep_vq.host_dma);
c2_mq_rep_init(&c2dev->rep_vq,
1,
qsize,
@@ -550,7 +550,7 @@ int c2_rnic_init(struct c2_dev *c2dev)
DMA_FROM_DEVICE);
pci_unmap_addr_set(&c2dev->aeq, mapping, c2dev->aeq.host_dma);
pr_debug("%s aeq va %p dma %llx\n", __FUNCTION__, q1_pages,
- (u64)c2dev->rep_vq.host_dma);
+ (unsigned long long) c2dev->rep_vq.host_dma);
c2_mq_rep_init(&c2dev->aeq,
2,
qsize,
diff --git a/drivers/infiniband/hw/ehca/ehca_main.c b/drivers/infiniband/hw/ehca/ehca_main.c
index 2380994418a..024d511c4b5 100644
--- a/drivers/infiniband/hw/ehca/ehca_main.c
+++ b/drivers/infiniband/hw/ehca/ehca_main.c
@@ -49,7 +49,7 @@
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Christoph Raisch <raisch@de.ibm.com>");
MODULE_DESCRIPTION("IBM eServer HCA InfiniBand Device Driver");
-MODULE_VERSION("SVNEHCA_0016");
+MODULE_VERSION("SVNEHCA_0017");
int ehca_open_aqp1 = 0;
int ehca_debug_level = 0;
@@ -239,7 +239,7 @@ init_node_guid1:
return ret;
}
-int ehca_register_device(struct ehca_shca *shca)
+int ehca_init_device(struct ehca_shca *shca)
{
int ret;
@@ -317,11 +317,6 @@ int ehca_register_device(struct ehca_shca *shca)
/* shca->ib_device.process_mad = ehca_process_mad; */
shca->ib_device.mmap = ehca_mmap;
- ret = ib_register_device(&shca->ib_device);
- if (ret)
- ehca_err(&shca->ib_device,
- "ib_register_device() failed ret=%x", ret);
-
return ret;
}
@@ -561,9 +556,9 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
goto probe1;
}
- ret = ehca_register_device(shca);
+ ret = ehca_init_device(shca);
if (ret) {
- ehca_gen_err("Cannot register Infiniband device");
+ ehca_gen_err("Cannot init ehca device struct");
goto probe1;
}
@@ -571,7 +566,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
ret = ehca_create_eq(shca, &shca->eq, EHCA_EQ, 2048);
if (ret) {
ehca_err(&shca->ib_device, "Cannot create EQ.");
- goto probe2;
+ goto probe1;
}
ret = ehca_create_eq(shca, &shca->neq, EHCA_NEQ, 513);
@@ -600,6 +595,13 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
goto probe5;
}
+ ret = ib_register_device(&shca->ib_device);
+ if (ret) {
+ ehca_err(&shca->ib_device,
+ "ib_register_device() failed ret=%x", ret);
+ goto probe6;
+ }
+
/* create AQP1 for port 1 */
if (ehca_open_aqp1 == 1) {
shca->sport[0].port_state = IB_PORT_DOWN;
@@ -607,7 +609,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
if (ret) {
ehca_err(&shca->ib_device,
"Cannot create AQP1 for port 1.");
- goto probe6;
+ goto probe7;
}
}
@@ -618,7 +620,7 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
if (ret) {
ehca_err(&shca->ib_device,
"Cannot create AQP1 for port 2.");
- goto probe7;
+ goto probe8;
}
}
@@ -630,12 +632,15 @@ static int __devinit ehca_probe(struct ibmebus_dev *dev,
return 0;
-probe7:
+probe8:
ret = ehca_destroy_aqp1(&shca->sport[0]);
if (ret)
ehca_err(&shca->ib_device,
"Cannot destroy AQP1 for port 1. ret=%x", ret);
+probe7:
+ ib_unregister_device(&shca->ib_device);
+
probe6:
ret = ehca_dereg_internal_maxmr(shca);
if (ret)
@@ -660,9 +665,6 @@ probe3:
ehca_err(&shca->ib_device,
"Cannot destroy EQ. ret=%x", ret);
-probe2:
- ib_unregister_device(&shca->ib_device);
-
probe1:
ib_dealloc_device(&shca->ib_device);
@@ -750,7 +752,7 @@ int __init ehca_module_init(void)
int ret;
printk(KERN_INFO "eHCA Infiniband Device Driver "
- "(Rel.: SVNEHCA_0016)\n");
+ "(Rel.: SVNEHCA_0017)\n");
idr_init(&ehca_qp_idr);
idr_init(&ehca_cq_idr);
spin_lock_init(&ehca_qp_idr_lock);
diff --git a/drivers/infiniband/hw/ehca/ehca_tools.h b/drivers/infiniband/hw/ehca/ehca_tools.h
index 9f56bb846d9..809da3ef706 100644
--- a/drivers/infiniband/hw/ehca/ehca_tools.h
+++ b/drivers/infiniband/hw/ehca/ehca_tools.h
@@ -117,7 +117,7 @@ extern int ehca_debug_level;
unsigned int l = (unsigned int)(len); \
unsigned char *deb = (unsigned char*)(adr); \
for (x = 0; x < l; x += 16) { \
- printk("EHCA_DMP:%s" format \
+ printk("EHCA_DMP:%s " format \
" adr=%p ofs=%04x %016lx %016lx\n", \
__FUNCTION__, ##args, deb, x, \
*((u64 *)&deb[0]), *((u64 *)&deb[8])); \
diff --git a/drivers/infiniband/hw/ipath/ipath_common.h b/drivers/infiniband/hw/ipath/ipath_common.h
index f577905e3ac..54139d39818 100644
--- a/drivers/infiniband/hw/ipath/ipath_common.h
+++ b/drivers/infiniband/hw/ipath/ipath_common.h
@@ -141,8 +141,9 @@ struct infinipath_stats {
* packets if ipath not configured, etc.)
*/
__u64 sps_krdrops;
+ __u64 sps_txeparity; /* PIO buffer parity error, recovered */
/* pad for future growth */
- __u64 __sps_pad[46];
+ __u64 __sps_pad[45];
};
/*
@@ -185,6 +186,9 @@ typedef enum _ipath_ureg {
#define IPATH_RUNTIME_PCIE 0x2
#define IPATH_RUNTIME_FORCE_WC_ORDER 0x4
#define IPATH_RUNTIME_RCVHDR_COPY 0x8
+#define IPATH_RUNTIME_MASTER 0x10
+#define IPATH_RUNTIME_PBC_REWRITE 0x20
+#define IPATH_RUNTIME_LOOSE_DMA_ALIGN 0x40
/*
* This structure is returned by ipath_userinit() immediately after
@@ -202,7 +206,8 @@ struct ipath_base_info {
/* version of software, for feature checking. */
__u32 spi_sw_version;
/* InfiniPath port assigned, goes into sent packets */
- __u32 spi_port;
+ __u16 spi_port;
+ __u16 spi_subport;
/*
* IB MTU, packets IB data must be less than this.
* The MTU is in bytes, and will be a multiple of 4 bytes.
@@ -218,7 +223,7 @@ struct ipath_base_info {
__u32 spi_tidcnt;
/* size of the TID Eager list in infinipath, in entries */
__u32 spi_tidegrcnt;
- /* size of a single receive header queue entry. */
+ /* size of a single receive header queue entry in words. */
__u32 spi_rcvhdrent_size;
/*
* Count of receive header queue entries allocated.
@@ -310,6 +315,12 @@ struct ipath_base_info {
__u32 spi_filler_for_align;
/* address of readonly memory copy of the rcvhdrq tail register. */
__u64 spi_rcvhdr_tailaddr;
+
+ /* shared memory pages for subports if IPATH_RUNTIME_MASTER is set */
+ __u64 spi_subport_uregbase;
+ __u64 spi_subport_rcvegrbuf;
+ __u64 spi_subport_rcvhdr_base;
+
} __attribute__ ((aligned(8)));
@@ -328,12 +339,12 @@ struct ipath_base_info {
/*
* Minor version differences are always compatible
- * a within a major version, however if if user software is larger
+ * a within a major version, however if user software is larger
* than driver software, some new features and/or structure fields
* may not be implemented; the user code must deal with this if it
- * cares, or it must abort after initialization reports the difference
+ * cares, or it must abort after initialization reports the difference.
*/
-#define IPATH_USER_SWMINOR 2
+#define IPATH_USER_SWMINOR 3
#define IPATH_USER_SWVERSION ((IPATH_USER_SWMAJOR<<16) | IPATH_USER_SWMINOR)
@@ -379,7 +390,16 @@ struct ipath_user_info {
*/
__u32 spu_rcvhdrsize;
- __u64 spu_unused; /* kept for compatible layout */
+ /*
+ * If two or more processes wish to share a port, each process
+ * must set the spu_subport_cnt and spu_subport_id to the same
+ * values. The only restriction on the spu_subport_id is that
+ * it be unique for a given node.
+ */
+ __u16 spu_subport_cnt;
+ __u16 spu_subport_id;
+
+ __u32 spu_unused; /* kept for compatible layout */
/*
* address of struct base_info to write to
@@ -392,19 +412,25 @@ struct ipath_user_info {
#define IPATH_CMD_MIN 16
-#define IPATH_CMD_USER_INIT 16 /* set up userspace */
+#define __IPATH_CMD_USER_INIT 16 /* old set up userspace (for old user code) */
#define IPATH_CMD_PORT_INFO 17 /* find out what resources we got */
#define IPATH_CMD_RECV_CTRL 18 /* control receipt of packets */
#define IPATH_CMD_TID_UPDATE 19 /* update expected TID entries */
#define IPATH_CMD_TID_FREE 20 /* free expected TID entries */
#define IPATH_CMD_SET_PART_KEY 21 /* add partition key */
+#define IPATH_CMD_SLAVE_INFO 22 /* return info on slave processes */
+#define IPATH_CMD_ASSIGN_PORT 23 /* allocate HCA and port */
+#define IPATH_CMD_USER_INIT 24 /* set up userspace */
-#define IPATH_CMD_MAX 21
+#define IPATH_CMD_MAX 24
struct ipath_port_info {
__u32 num_active; /* number of active units */
__u32 unit; /* unit (chip) assigned to caller */
- __u32 port; /* port on unit assigned to caller */
+ __u16 port; /* port on unit assigned to caller */
+ __u16 subport; /* subport on unit assigned to caller */
+ __u16 num_ports; /* number of ports available on unit */
+ __u16 num_subports; /* number of subport slaves opened on port */
};
struct ipath_tid_info {
@@ -435,6 +461,8 @@ struct ipath_cmd {
__u32 recv_ctrl;
/* partition key to set */
__u16 part_key;
+ /* user address of __u32 bitmask of active slaves */
+ __u64 slave_mask_addr;
} cmd;
};
@@ -596,6 +624,10 @@ struct infinipath_counters {
/* K_PktFlags bits */
#define INFINIPATH_KPF_INTR 0x1
+#define INFINIPATH_KPF_SUBPORT_MASK 0x3
+#define INFINIPATH_KPF_SUBPORT_SHIFT 1
+
+#define INFINIPATH_MAX_SUBPORT 4
/* SendPIO per-buffer control */
#define INFINIPATH_SP_TEST 0x40
@@ -610,7 +642,7 @@ struct ipath_header {
/*
* Version - 4 bits, Port - 4 bits, TID - 10 bits and Offset -
* 14 bits before ECO change ~28 Dec 03. After that, Vers 4,
- * Port 3, TID 11, offset 14.
+ * Port 4, TID 11, offset 13.
*/
__le32 ver_port_tid_offset;
__le16 chksum;
diff --git a/drivers/infiniband/hw/ipath/ipath_cq.c b/drivers/infiniband/hw/ipath/ipath_cq.c
index 049221bc590..87462e0cb4d 100644
--- a/drivers/infiniband/hw/ipath/ipath_cq.c
+++ b/drivers/infiniband/hw/ipath/ipath_cq.c
@@ -46,7 +46,7 @@
*/
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
{
- struct ipath_cq_wc *wc = cq->queue;
+ struct ipath_cq_wc *wc;
unsigned long flags;
u32 head;
u32 next;
@@ -57,6 +57,7 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
* Note that the head pointer might be writable by user processes.
* Take care to verify it is a sane value.
*/
+ wc = cq->queue;
head = wc->head;
if (head >= (unsigned) cq->ibcq.cqe) {
head = cq->ibcq.cqe;
@@ -109,21 +110,27 @@ void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int solicited)
int ipath_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry)
{
struct ipath_cq *cq = to_icq(ibcq);
- struct ipath_cq_wc *wc = cq->queue;
+ struct ipath_cq_wc *wc;
unsigned long flags;
int npolled;
+ u32 tail;
spin_lock_irqsave(&cq->lock, flags);
+ wc = cq->queue;
+ tail = wc->tail;
+ if (tail > (u32) cq->ibcq.cqe)
+ tail = (u32) cq->ibcq.cqe;
for (npolled = 0; npolled < num_entries; ++npolled, ++entry) {
- if (wc->tail == wc->head)
+ if (tail == wc->head)
break;
- *entry = wc->queue[wc->tail];
- if (wc->tail >= cq->ibcq.cqe)
- wc->tail = 0;
+ *entry = wc->queue[tail];
+ if (tail >= cq->ibcq.cqe)
+ tail = 0;
else
- wc->tail++;
+ tail++;
}
+ wc->tail = tail;
spin_unlock_irqrestore(&cq->lock, flags);
@@ -177,11 +184,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
goto done;
}
- if (dev->n_cqs_allocated == ib_ipath_max_cqs) {
- ret = ERR_PTR(-ENOMEM);
- goto done;
- }
-
/* Allocate the completion queue structure. */
cq = kmalloc(sizeof(*cq), GFP_KERNEL);
if (!cq) {
@@ -237,6 +239,16 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
} else
cq->ip = NULL;
+ spin_lock(&dev->n_cqs_lock);
+ if (dev->n_cqs_allocated == ib_ipath_max_cqs) {
+ spin_unlock(&dev->n_cqs_lock);
+ ret = ERR_PTR(-ENOMEM);
+ goto bail_wc;
+ }
+
+ dev->n_cqs_allocated++;
+ spin_unlock(&dev->n_cqs_lock);
+
/*
* ib_create_cq() will initialize cq->ibcq except for cq->ibcq.cqe.
* The number of entries should be >= the number requested or return
@@ -253,7 +265,6 @@ struct ib_cq *ipath_create_cq(struct ib_device *ibdev, int entries,
ret = &cq->ibcq;
- dev->n_cqs_allocated++;
goto done;
bail_wc:
@@ -280,7 +291,9 @@ int ipath_destroy_cq(struct ib_cq *ibcq)
struct ipath_cq *cq = to_icq(ibcq);
tasklet_kill(&cq->comptask);
+ spin_lock(&dev->n_cqs_lock);
dev->n_cqs_allocated--;
+ spin_unlock(&dev->n_cqs_lock);
if (cq->ip)
kref_put(&cq->ip->ref, ipath_release_mmap_info);
else
@@ -316,10 +329,16 @@ int ipath_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify notify)
return 0;
}
+/**
+ * ipath_resize_cq - change the size of the CQ
+ * @ibcq: the completion queue
+ *
+ * Returns 0 for success.
+ */
int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
{
struct ipath_cq *cq = to_icq(ibcq);
- struct ipath_cq_wc *old_wc = cq->queue;
+ struct ipath_cq_wc *old_wc;
struct ipath_cq_wc *wc;
u32 head, tail, n;
int ret;
@@ -355,6 +374,7 @@ int ipath_resize_cq(struct ib_cq *ibcq, int cqe, struct ib_udata *udata)
* Make sure head and tail are sane since they
* might be user writable.
*/
+ old_wc = cq->queue;
head = old_wc->head;
if (head > (u32) cq->ibcq.cqe)
head = (u32) cq->ibcq.cqe;
diff --git a/drivers/infiniband/hw/ipath/ipath_driver.c b/drivers/infiniband/hw/ipath/ipath_driver.c
index 2108466c7e3..12cefa658f3 100644
--- a/drivers/infiniband/hw/ipath/ipath_driver.c
+++ b/drivers/infiniband/hw/ipath/ipath_driver.c
@@ -95,16 +95,6 @@ const char *ipath_ibcstatus_str[] = {
"RecovIdle",
};
-/*
- * These variables are initialized in the chip-specific files
- * but are defined here.
- */
-u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
-u64 ipath_gpio_sda, ipath_gpio_scl;
-u64 infinipath_i_bitsextant;
-ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
-u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
-
static void __devexit ipath_remove_one(struct pci_dev *);
static int __devinit ipath_init_one(struct pci_dev *,
const struct pci_device_id *);
@@ -527,28 +517,146 @@ bail:
return ret;
}
+static void __devexit cleanup_device(struct ipath_devdata *dd)
+{
+ int port;
+
+ ipath_shutdown_device(dd);
+
+ if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
+ /* can't do anything more with chip; needs re-init */
+ *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
+ if (dd->ipath_kregbase) {
+ /*
+ * if we haven't already cleaned up before these are
+ * to ensure any register reads/writes "fail" until
+ * re-init
+ */
+ dd->ipath_kregbase = NULL;
+ dd->ipath_uregbase = 0;
+ dd->ipath_sregbase = 0;
+ dd->ipath_cregbase = 0;
+ dd->ipath_kregsize = 0;
+ }
+ ipath_disable_wc(dd);
+ }
+
+ if (dd->ipath_pioavailregs_dma) {
+ dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
+ (void *) dd->ipath_pioavailregs_dma,
+ dd->ipath_pioavailregs_phys);
+ dd->ipath_pioavailregs_dma = NULL;
+ }
+ if (dd->ipath_dummy_hdrq) {
+ dma_free_coherent(&dd->pcidev->dev,
+ dd->ipath_pd[0]->port_rcvhdrq_size,
+ dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
+ dd->ipath_dummy_hdrq = NULL;
+ }
+
+ if (dd->ipath_pageshadow) {
+ struct page **tmpp = dd->ipath_pageshadow;
+ dma_addr_t *tmpd = dd->ipath_physshadow;
+ int i, cnt = 0;
+
+ ipath_cdbg(VERBOSE, "Unlocking any expTID pages still "
+ "locked\n");
+ for (port = 0; port < dd->ipath_cfgports; port++) {
+ int port_tidbase = port * dd->ipath_rcvtidcnt;
+ int maxtid = port_tidbase + dd->ipath_rcvtidcnt;
+ for (i = port_tidbase; i < maxtid; i++) {
+ if (!tmpp[i])
+ continue;
+ pci_unmap_page(dd->pcidev, tmpd[i],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
+ ipath_release_user_pages(&tmpp[i], 1);
+ tmpp[i] = NULL;
+ cnt++;
+ }
+ }
+ if (cnt) {
+ ipath_stats.sps_pageunlocks += cnt;
+ ipath_cdbg(VERBOSE, "There were still %u expTID "
+ "entries locked\n", cnt);
+ }
+ if (ipath_stats.sps_pagelocks ||
+ ipath_stats.sps_pageunlocks)
+ ipath_cdbg(VERBOSE, "%llu pages locked, %llu "
+ "unlocked via ipath_m{un}lock\n",
+ (unsigned long long)
+ ipath_stats.sps_pagelocks,
+ (unsigned long long)
+ ipath_stats.sps_pageunlocks);
+
+ ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
+ dd->ipath_pageshadow);
+ vfree(dd->ipath_pageshadow);
+ dd->ipath_pageshadow = NULL;
+ }
+
+ /*
+ * free any resources still in use (usually just kernel ports)
+ * at unload; we do for portcnt, not cfgports, because cfgports
+ * could have changed while we were loaded.
+ */
+ for (port = 0; port < dd->ipath_portcnt; port++) {
+ struct ipath_portdata *pd = dd->ipath_pd[port];
+ dd->ipath_pd[port] = NULL;
+ ipath_free_pddata(dd, pd);
+ }
+ kfree(dd->ipath_pd);
+ /*
+ * debuggability, in case some cleanup path tries to use it
+ * after this
+ */
+ dd->ipath_pd = NULL;
+}
+
static void __devexit ipath_remove_one(struct pci_dev *pdev)
{
- struct ipath_devdata *dd;
+ struct ipath_devdata *dd = pci_get_drvdata(pdev);
- ipath_cdbg(VERBOSE, "removing, pdev=%p\n", pdev);
- if (!pdev)
- return;
+ ipath_cdbg(VERBOSE, "removing, pdev=%p, dd=%p\n", pdev, dd);
+
+ if (dd->verbs_dev)
+ ipath_unregister_ib_device(dd->verbs_dev);
- dd = pci_get_drvdata(pdev);
- ipath_unregister_ib_device(dd->verbs_dev);
ipath_diag_remove(dd);
ipath_user_remove(dd);
ipathfs_remove_device(dd);
ipath_device_remove_group(&pdev->dev, dd);
+
ipath_cdbg(VERBOSE, "Releasing pci memory regions, dd %p, "
"unit %u\n", dd, (u32) dd->ipath_unit);
- if (dd->ipath_kregbase) {
- ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n",
- dd->ipath_kregbase);
- iounmap((volatile void __iomem *) dd->ipath_kregbase);
- dd->ipath_kregbase = NULL;
- }
+
+ cleanup_device(dd);
+
+ /*
+ * turn off rcv, send, and interrupts for all ports, all drivers
+ * should also hard reset the chip here?
+ * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs
+ * for all versions of the driver, if they were allocated
+ */
+ if (pdev->irq) {
+ ipath_cdbg(VERBOSE,
+ "unit %u free_irq of irq %x\n",
+ dd->ipath_unit, pdev->irq);
+ free_irq(pdev->irq, dd);
+ } else
+ ipath_dbg("irq is 0, not doing free_irq "
+ "for unit %u\n", dd->ipath_unit);
+ /*
+ * we check for NULL here, because it's outside
+ * the kregbase check, and we need to call it
+ * after the free_irq. Thus it's possible that
+ * the function pointers were never initialized.
+ */
+ if (dd->ipath_f_cleanup)
+ /* clean up chip-specific stuff */
+ dd->ipath_f_cleanup(dd);
+
+ ipath_cdbg(VERBOSE, "Unmapping kregbase %p\n", dd->ipath_kregbase);
+ iounmap((volatile void __iomem *) dd->ipath_kregbase);
pci_release_regions(pdev);
ipath_cdbg(VERBOSE, "calling pci_disable_device\n");
pci_disable_device(pdev);
@@ -760,8 +868,8 @@ static void get_rhf_errstring(u32 err, char *msg, size_t len)
static inline void *ipath_get_egrbuf(struct ipath_devdata *dd, u32 bufnum,
int err)
{
- return dd->ipath_port0_skbs ?
- (void *)dd->ipath_port0_skbs[bufnum]->data : NULL;
+ return dd->ipath_port0_skbinfo ?
+ (void *) dd->ipath_port0_skbinfo[bufnum].skb->data : NULL;
}
/**
@@ -783,31 +891,34 @@ struct sk_buff *ipath_alloc_skb(struct ipath_devdata *dd,
*/
/*
- * We need 4 extra bytes for unaligned transfer copying
+ * We need 2 extra bytes for ipath_ether data sent in the
+ * key header. In order to keep everything dword aligned,
+ * we'll reserve 4 bytes.
*/
+ len = dd->ipath_ibmaxlen + 4;
+
if (dd->ipath_flags & IPATH_4BYTE_TID) {
- /* we need a 4KB multiple alignment, and there is no way
+ /* We need a 2KB multiple alignment, and there is no way
* to do it except to allocate extra and then skb_reserve
* enough to bring it up to the right alignment.
*/
- len = dd->ipath_ibmaxlen + 4 + (1 << 11) - 1;
+ len += 2047;
}
- else
- len = dd->ipath_ibmaxlen + 4;
+
skb = __dev_alloc_skb(len, gfp_mask);
if (!skb) {
ipath_dev_err(dd, "Failed to allocate skbuff, length %u\n",
len);
goto bail;
}
+
+ skb_reserve(skb, 4);
+
if (dd->ipath_flags & IPATH_4BYTE_TID) {
- u32 una = ((1 << 11) - 1) & (unsigned long)(skb->data + 4);
+ u32 una = (unsigned long)skb->data & 2047;
if (una)
- skb_reserve(skb, 4 + (1 << 11) - una);
- else
- skb_reserve(skb, 4);
- } else
- skb_reserve(skb, 4);
+ skb_reserve(skb, 2048 - una);
+ }
bail:
return skb;
@@ -1326,6 +1437,9 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
"for port %u rcvhdrqtailaddr failed\n",
pd->port_port);
ret = -ENOMEM;
+ dma_free_coherent(&dd->pcidev->dev, amt,
+ pd->port_rcvhdrq, pd->port_rcvhdrq_phys);
+ pd->port_rcvhdrq = NULL;
goto bail;
}
pd->port_rcvhdrqtailaddr_phys = phys_hdrqtail;
@@ -1347,12 +1461,13 @@ int ipath_create_rcvhdrq(struct ipath_devdata *dd,
ipath_cdbg(VERBOSE, "reuse port %d rcvhdrq @%p %llx phys; "
"hdrtailaddr@%p %llx physical\n",
pd->port_port, pd->port_rcvhdrq,
- pd->port_rcvhdrq_phys, pd->port_rcvhdrtail_kvaddr,
- (unsigned long long)pd->port_rcvhdrqtailaddr_phys);
+ (unsigned long long) pd->port_rcvhdrq_phys,
+ pd->port_rcvhdrtail_kvaddr, (unsigned long long)
+ pd->port_rcvhdrqtailaddr_phys);
/* clear for security and sanity on each use */
memset(pd->port_rcvhdrq, 0, pd->port_rcvhdrq_size);
- memset((void *)pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
+ memset(pd->port_rcvhdrtail_kvaddr, 0, PAGE_SIZE);
/*
* tell chip each time we init it, even if we are re-using previous
@@ -1805,7 +1920,7 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
pd->port_rcvhdrq = NULL;
if (pd->port_rcvhdrtail_kvaddr) {
dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
- (void *)pd->port_rcvhdrtail_kvaddr,
+ pd->port_rcvhdrtail_kvaddr,
pd->port_rcvhdrqtailaddr_phys);
pd->port_rcvhdrtail_kvaddr = NULL;
}
@@ -1824,24 +1939,32 @@ void ipath_free_pddata(struct ipath_devdata *dd, struct ipath_portdata *pd)
dma_free_coherent(&dd->pcidev->dev, size,
base, pd->port_rcvegrbuf_phys[e]);
}
- vfree(pd->port_rcvegrbuf);
+ kfree(pd->port_rcvegrbuf);
pd->port_rcvegrbuf = NULL;
- vfree(pd->port_rcvegrbuf_phys);
+ kfree(pd->port_rcvegrbuf_phys);
pd->port_rcvegrbuf_phys = NULL;
pd->port_rcvegrbuf_chunks = 0;
- } else if (pd->port_port == 0 && dd->ipath_port0_skbs) {
+ } else if (pd->port_port == 0 && dd->ipath_port0_skbinfo) {
unsigned e;
- struct sk_buff **skbs = dd->ipath_port0_skbs;
+ struct ipath_skbinfo *skbinfo = dd->ipath_port0_skbinfo;
- dd->ipath_port0_skbs = NULL;
- ipath_cdbg(VERBOSE, "free closed port %d ipath_port0_skbs "
- "@ %p\n", pd->port_port, skbs);
+ dd->ipath_port0_skbinfo = NULL;
+ ipath_cdbg(VERBOSE, "free closed port %d "
+ "ipath_port0_skbinfo @ %p\n", pd->port_port,
+ skbinfo);
for (e = 0; e < dd->ipath_rcvegrcnt; e++)
- if (skbs[e])
- dev_kfree_skb(skbs[e]);
- vfree(skbs);
+ if (skbinfo[e].skb) {
+ pci_unmap_single(dd->pcidev, skbinfo[e].phys,
+ dd->ipath_ibmaxlen,
+ PCI_DMA_FROMDEVICE);
+ dev_kfree_skb(skbinfo[e].skb);
+ }
+ vfree(skbinfo);
}
kfree(pd->port_tid_pg_list);
+ vfree(pd->subport_uregbase);
+ vfree(pd->subport_rcvegrbuf);
+ vfree(pd->subport_rcvhdr_base);
kfree(pd);
}
@@ -1907,150 +2030,12 @@ bail:
return ret;
}
-static void cleanup_device(struct ipath_devdata *dd)
-{
- int port;
-
- ipath_shutdown_device(dd);
-
- if (*dd->ipath_statusp & IPATH_STATUS_CHIP_PRESENT) {
- /* can't do anything more with chip; needs re-init */
- *dd->ipath_statusp &= ~IPATH_STATUS_CHIP_PRESENT;
- if (dd->ipath_kregbase) {
- /*
- * if we haven't already cleaned up before these are
- * to ensure any register reads/writes "fail" until
- * re-init
- */
- dd->ipath_kregbase = NULL;
- dd->ipath_uregbase = 0;
- dd->ipath_sregbase = 0;
- dd->ipath_cregbase = 0;
- dd->ipath_kregsize = 0;
- }
- ipath_disable_wc(dd);
- }
-
- if (dd->ipath_pioavailregs_dma) {
- dma_free_coherent(&dd->pcidev->dev, PAGE_SIZE,
- (void *) dd->ipath_pioavailregs_dma,
- dd->ipath_pioavailregs_phys);
- dd->ipath_pioavailregs_dma = NULL;
- }
- if (dd->ipath_dummy_hdrq) {
- dma_free_coherent(&dd->pcidev->dev,
- dd->ipath_pd[0]->port_rcvhdrq_size,
- dd->ipath_dummy_hdrq, dd->ipath_dummy_hdrq_phys);
- dd->ipath_dummy_hdrq = NULL;
- }
-
- if (dd->ipath_pageshadow) {
- struct page **tmpp = dd->ipath_pageshadow;
- int i, cnt = 0;
-
- ipath_cdbg(VERBOSE, "Unlocking any expTID pages still "
- "locked\n");
- for (port = 0; port < dd->ipath_cfgports; port++) {
- int port_tidbase = port * dd->ipath_rcvtidcnt;
- int maxtid = port_tidbase + dd->ipath_rcvtidcnt;
- for (i = port_tidbase; i < maxtid; i++) {
- if (!tmpp[i])
- continue;
- ipath_release_user_pages(&tmpp[i], 1);
- tmpp[i] = NULL;
- cnt++;
- }
- }
- if (cnt) {
- ipath_stats.sps_pageunlocks += cnt;
- ipath_cdbg(VERBOSE, "There were still %u expTID "
- "entries locked\n", cnt);
- }
- if (ipath_stats.sps_pagelocks ||
- ipath_stats.sps_pageunlocks)
- ipath_cdbg(VERBOSE, "%llu pages locked, %llu "
- "unlocked via ipath_m{un}lock\n",
- (unsigned long long)
- ipath_stats.sps_pagelocks,
- (unsigned long long)
- ipath_stats.sps_pageunlocks);
-
- ipath_cdbg(VERBOSE, "Free shadow page tid array at %p\n",
- dd->ipath_pageshadow);
- vfree(dd->ipath_pageshadow);
- dd->ipath_pageshadow = NULL;
- }
-
- /*
- * free any resources still in use (usually just kernel ports)
- * at unload; we do for portcnt, not cfgports, because cfgports
- * could have changed while we were loaded.
- */
- for (port = 0; port < dd->ipath_portcnt; port++) {
- struct ipath_portdata *pd = dd->ipath_pd[port];
- dd->ipath_pd[port] = NULL;
- ipath_free_pddata(dd, pd);
- }
- kfree(dd->ipath_pd);
- /*
- * debuggability, in case some cleanup path tries to use it
- * after this
- */
- dd->ipath_pd = NULL;
-}
-
static void __exit infinipath_cleanup(void)
{
- struct ipath_devdata *dd, *tmp;
- unsigned long flags;
-
- ipath_diagpkt_remove();
-
ipath_exit_ipathfs();
ipath_driver_remove_group(&ipath_driver.driver);
- spin_lock_irqsave(&ipath_devs_lock, flags);
-
- /*
- * turn off rcv, send, and interrupts for all ports, all drivers
- * should also hard reset the chip here?
- * free up port 0 (kernel) rcvhdr, egr bufs, and eventually tid bufs
- * for all versions of the driver, if they were allocated
- */
- list_for_each_entry_safe(dd, tmp, &ipath_dev_list, ipath_list) {
- spin_unlock_irqrestore(&ipath_devs_lock, flags);
-
- if (dd->ipath_kregbase)
- cleanup_device(dd);
-
- if (dd->pcidev) {
- if (dd->pcidev->irq) {
- ipath_cdbg(VERBOSE,
- "unit %u free_irq of irq %x\n",
- dd->ipath_unit, dd->pcidev->irq);
- free_irq(dd->pcidev->irq, dd);
- } else
- ipath_dbg("irq is 0, not doing free_irq "
- "for unit %u\n", dd->ipath_unit);
-
- /*
- * we check for NULL here, because it's outside
- * the kregbase check, and we need to call it
- * after the free_irq. Thus it's possible that
- * the function pointers were never initialized.
- */
- if (dd->ipath_f_cleanup)
- /* clean up chip-specific stuff */
- dd->ipath_f_cleanup(dd);
-
- dd->pcidev = NULL;
- }
- spin_lock_irqsave(&ipath_devs_lock, flags);
- }
-
- spin_unlock_irqrestore(&ipath_devs_lock, flags);
-
ipath_cdbg(VERBOSE, "Unregistering pci driver\n");
pci_unregister_driver(&ipath_driver);
diff --git a/drivers/infiniband/hw/ipath/ipath_eeprom.c b/drivers/infiniband/hw/ipath/ipath_eeprom.c
index 3313356ab93..a4019a6b756 100644
--- a/drivers/infiniband/hw/ipath/ipath_eeprom.c
+++ b/drivers/infiniband/hw/ipath/ipath_eeprom.c
@@ -100,9 +100,9 @@ static int i2c_gpio_set(struct ipath_devdata *dd,
gpioval = &dd->ipath_gpio_out;
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
if (line == i2c_line_scl)
- mask = ipath_gpio_scl;
+ mask = dd->ipath_gpio_scl;
else
- mask = ipath_gpio_sda;
+ mask = dd->ipath_gpio_sda;
if (new_line_state == i2c_line_high)
/* tri-state the output rather than force high */
@@ -119,12 +119,12 @@ static int i2c_gpio_set(struct ipath_devdata *dd,
write_val = 0x0UL;
if (line == i2c_line_scl) {
- write_val <<= ipath_gpio_scl_num;
- *gpioval = *gpioval & ~(1UL << ipath_gpio_scl_num);
+ write_val <<= dd->ipath_gpio_scl_num;
+ *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_scl_num);
*gpioval |= write_val;
} else {
- write_val <<= ipath_gpio_sda_num;
- *gpioval = *gpioval & ~(1UL << ipath_gpio_sda_num);
+ write_val <<= dd->ipath_gpio_sda_num;
+ *gpioval = *gpioval & ~(1UL << dd->ipath_gpio_sda_num);
*gpioval |= write_val;
}
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_out, *gpioval);
@@ -157,9 +157,9 @@ static int i2c_gpio_get(struct ipath_devdata *dd,
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extctrl);
/* config line to be an input */
if (line == i2c_line_scl)
- mask = ipath_gpio_scl;
+ mask = dd->ipath_gpio_scl;
else
- mask = ipath_gpio_sda;
+ mask = dd->ipath_gpio_sda;
write_val = read_val & ~mask;
ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, write_val);
read_val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_extstatus);
@@ -187,6 +187,7 @@ bail:
static void i2c_wait_for_writes(struct ipath_devdata *dd)
{
(void)ipath_read_kreg32(dd, dd->ipath_kregs->kr_scratch);
+ rmb();
}
static void scl_out(struct ipath_devdata *dd, u8 bit)
diff --git a/drivers/infiniband/hw/ipath/ipath_file_ops.c b/drivers/infiniband/hw/ipath/ipath_file_ops.c
index 29930e22318..a9ddc6911f6 100644
--- a/drivers/infiniband/hw/ipath/ipath_file_ops.c
+++ b/drivers/infiniband/hw/ipath/ipath_file_ops.c
@@ -41,6 +41,12 @@
#include "ipath_kernel.h"
#include "ipath_common.h"
+/*
+ * mmap64 doesn't allow all 64 bits for 32-bit applications
+ * so only use the low 43 bits.
+ */
+#define MMAP64_MASK 0x7FFFFFFFFFFUL
+
static int ipath_open(struct inode *, struct file *);
static int ipath_close(struct inode *, struct file *);
static ssize_t ipath_write(struct file *, const char __user *, size_t,
@@ -57,18 +63,35 @@ static struct file_operations ipath_file_ops = {
.mmap = ipath_mmap
};
-static int ipath_get_base_info(struct ipath_portdata *pd,
+static int ipath_get_base_info(struct file *fp,
void __user *ubase, size_t ubase_size)
{
+ struct ipath_portdata *pd = port_fp(fp);
int ret = 0;
struct ipath_base_info *kinfo = NULL;
struct ipath_devdata *dd = pd->port_dd;
+ unsigned subport_cnt;
+ int shared, master;
+ size_t sz;
+
+ subport_cnt = pd->port_subport_cnt;
+ if (!subport_cnt) {
+ shared = 0;
+ master = 0;
+ subport_cnt = 1;
+ } else {
+ shared = 1;
+ master = !subport_fp(fp);
+ }
- if (ubase_size < sizeof(*kinfo)) {
+ sz = sizeof(*kinfo);
+ /* If port sharing is not requested, allow the old size structure */
+ if (!shared)
+ sz -= 3 * sizeof(u64);
+ if (ubase_size < sz) {
ipath_cdbg(PROC,
- "Base size %lu, need %lu (version mismatch?)\n",
- (unsigned long) ubase_size,
- (unsigned long) sizeof(*kinfo));
+ "Base size %zu, need %zu (version mismatch?)\n",
+ ubase_size, sz);
ret = -EINVAL;
goto bail;
}
@@ -95,7 +118,9 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
kinfo->spi_rcv_egrperchunk = pd->port_rcvegrbufs_perchunk;
kinfo->spi_rcv_egrchunksize = kinfo->spi_rcv_egrbuftotlen /
pd->port_rcvegrbuf_chunks;
- kinfo->spi_tidcnt = dd->ipath_rcvtidcnt;
+ kinfo->spi_tidcnt = dd->ipath_rcvtidcnt / subport_cnt;
+ if (master)
+ kinfo->spi_tidcnt += dd->ipath_rcvtidcnt % subport_cnt;
/*
* for this use, may be ipath_cfgports summed over all chips that
* are are configured and present
@@ -118,31 +143,75 @@ static int ipath_get_base_info(struct ipath_portdata *pd,
* page_address() macro worked, but in 2.6.11, even that returns the
* full 64 bit address (upper bits all 1's). So far, using the
* physical addresses (or chip offsets, for chip mapping) works, but
- * no doubt some future kernel release will chang that, and we'll be
- * on to yet another method of dealing with this
+ * no doubt some future kernel release will change that, and we'll be
+ * on to yet another method of dealing with this.
*/
kinfo->spi_rcvhdr_base = (u64) pd->port_rcvhdrq_phys;
- kinfo->spi_rcvhdr_tailaddr = (u64)pd->port_rcvhdrqtailaddr_phys;
+ kinfo->spi_rcvhdr_tailaddr = (u64) pd->port_rcvhdrqtailaddr_phys;
kinfo->spi_rcv_egrbufs = (u64) pd->port_rcvegr_phys;
kinfo->spi_pioavailaddr = (u64) dd->ipath_pioavailregs_phys;
kinfo->spi_status = (u64) kinfo->spi_pioavailaddr +
(void *) dd->ipath_statusp -
(void *) dd->ipath_pioavailregs_dma;
- kinfo->spi_piobufbase = (u64) pd->port_piobufs;
- kinfo->__spi_uregbase =
- dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ if (!shared) {
+ kinfo->spi_piocnt = dd->ipath_pbufsport;
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs;
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
+ dd->ipath_palign * pd->port_port;
+ } else if (master) {
+ kinfo->spi_piocnt = (dd->ipath_pbufsport / subport_cnt) +
+ (dd->ipath_pbufsport % subport_cnt);
+ /* Master's PIO buffers are after all the slave's */
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
+ dd->ipath_palign *
+ (dd->ipath_pbufsport - kinfo->spi_piocnt);
+ kinfo->__spi_uregbase = (u64) dd->ipath_uregbase +
+ dd->ipath_palign * pd->port_port;
+ } else {
+ unsigned slave = subport_fp(fp) - 1;
+
+ kinfo->spi_piocnt = dd->ipath_pbufsport / subport_cnt;
+ kinfo->spi_piobufbase = (u64) pd->port_piobufs +
+ dd->ipath_palign * kinfo->spi_piocnt * slave;
+ kinfo->__spi_uregbase = ((u64) pd->subport_uregbase +
+ PAGE_SIZE * slave) & MMAP64_MASK;
- kinfo->spi_pioindex = dd->ipath_pbufsport * (pd->port_port - 1);
- kinfo->spi_piocnt = dd->ipath_pbufsport;
+ kinfo->spi_rcvhdr_base = ((u64) pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * slave) & MMAP64_MASK;
+ kinfo->spi_rcvhdr_tailaddr =
+ (u64) pd->port_rcvhdrqtailaddr_phys & MMAP64_MASK;
+ kinfo->spi_rcv_egrbufs = ((u64) pd->subport_rcvegrbuf +
+ dd->ipath_rcvegrcnt * dd->ipath_rcvegrbufsize * slave) &
+ MMAP64_MASK;
+ }
+
+ kinfo->spi_pioindex = (kinfo->spi_piobufbase - dd->ipath_piobufbase) /
+ dd->ipath_palign;
kinfo->spi_pioalign = dd->ipath_palign;
kinfo->spi_qpair = IPATH_KD_QP;
kinfo->spi_piosize = dd->ipath_ibmaxlen;
kinfo->spi_mtu = dd->ipath_ibmaxlen; /* maxlen, not ibmtu */
kinfo->spi_port = pd->port_port;
+ kinfo->spi_subport = subport_fp(fp);
kinfo->spi_sw_version = IPATH_KERN_SWVERSION;
kinfo->spi_hw_version = dd->ipath_revision;
+ if (master) {
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_MASTER;
+ kinfo->spi_subport_uregbase =
+ (u64) pd->subport_uregbase & MMAP64_MASK;
+ kinfo->spi_subport_rcvegrbuf =
+ (u64) pd->subport_rcvegrbuf & MMAP64_MASK;
+ kinfo->spi_subport_rcvhdr_base =
+ (u64) pd->subport_rcvhdr_base & MMAP64_MASK;
+ ipath_cdbg(PROC, "port %u flags %x %llx %llx %llx\n",
+ kinfo->spi_port, kinfo->spi_runtime_flags,
+ (unsigned long long) kinfo->spi_subport_uregbase,
+ (unsigned long long) kinfo->spi_subport_rcvegrbuf,
+ (unsigned long long) kinfo->spi_subport_rcvhdr_base);
+ }
+
if (copy_to_user(ubase, kinfo, sizeof(*kinfo)))
ret = -EFAULT;
@@ -154,6 +223,7 @@ bail:
/**
* ipath_tid_update - update a port TID
* @pd: the port
+ * @fp: the ipath device file
* @ti: the TID information
*
* The new implementation as of Oct 2004 is that the driver assigns
@@ -176,11 +246,11 @@ bail:
* virtually contiguous pages, that should change to improve
* performance.
*/
-static int ipath_tid_update(struct ipath_portdata *pd,
+static int ipath_tid_update(struct ipath_portdata *pd, struct file *fp,
const struct ipath_tid_info *ti)
{
int ret = 0, ntids;
- u32 tid, porttid, cnt, i, tidcnt;
+ u32 tid, porttid, cnt, i, tidcnt, tidoff;
u16 *tidlist;
struct ipath_devdata *dd = pd->port_dd;
u64 physaddr;
@@ -188,6 +258,7 @@ static int ipath_tid_update(struct ipath_portdata *pd,
u64 __iomem *tidbase;
unsigned long tidmap[8];
struct page **pagep = NULL;
+ unsigned subport = subport_fp(fp);
if (!dd->ipath_pageshadow) {
ret = -ENOMEM;
@@ -204,20 +275,34 @@ static int ipath_tid_update(struct ipath_portdata *pd,
ret = -EFAULT;
goto done;
}
- tidcnt = dd->ipath_rcvtidcnt;
- if (cnt >= tidcnt) {
+ porttid = pd->port_port * dd->ipath_rcvtidcnt;
+ if (!pd->port_subport_cnt) {
+ tidcnt = dd->ipath_rcvtidcnt;
+ tid = pd->port_tidcursor;
+ tidoff = 0;
+ } else if (!subport) {
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
+ tidoff = dd->ipath_rcvtidcnt - tidcnt;
+ porttid += tidoff;
+ tid = tidcursor_fp(fp);
+ } else {
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
+ tidoff = tidcnt * (subport - 1);
+ porttid += tidoff;
+ tid = tidcursor_fp(fp);
+ }
+ if (cnt > tidcnt) {
/* make sure it all fits in port_tid_pg_list */
dev_info(&dd->pcidev->dev, "Process tried to allocate %u "
"TIDs, only trying max (%u)\n", cnt, tidcnt);
cnt = tidcnt;
}
- pagep = (struct page **)pd->port_tid_pg_list;
- tidlist = (u16 *) (&pagep[cnt]);
+ pagep = &((struct page **) pd->port_tid_pg_list)[tidoff];
+ tidlist = &((u16 *) &pagep[dd->ipath_rcvtidcnt])[tidoff];
memset(tidmap, 0, sizeof(tidmap));
- tid = pd->port_tidcursor;
/* before decrement; chip actual # */
- porttid = pd->port_port * tidcnt;
ntids = tidcnt;
tidbase = (u64 __iomem *) (((char __iomem *) dd->ipath_kregbase) +
dd->ipath_rcvtidbase +
@@ -274,16 +359,19 @@ static int ipath_tid_update(struct ipath_portdata *pd,
ret = -ENOMEM;
break;
}
- tidlist[i] = tid;
+ tidlist[i] = tid + tidoff;
ipath_cdbg(VERBOSE, "Updating idx %u to TID %u, "
- "vaddr %lx\n", i, tid, vaddr);
+ "vaddr %lx\n", i, tid + tidoff, vaddr);
/* we "know" system pages and TID pages are same size */
dd->ipath_pageshadow[porttid + tid] = pagep[i];
+ dd->ipath_physshadow[porttid + tid] = ipath_map_page(
+ dd->pcidev, pagep[i], 0, PAGE_SIZE,
+ PCI_DMA_FROMDEVICE);
/*
* don't need atomic or it's overhead
*/
__set_bit(tid, tidmap);
- physaddr = page_to_phys(pagep[i]);
+ physaddr = dd->ipath_physshadow[porttid + tid];
ipath_stats.sps_pagelocks++;
ipath_cdbg(VERBOSE,
"TID %u, vaddr %lx, physaddr %llx pgp %p\n",
@@ -317,6 +405,9 @@ static int ipath_tid_update(struct ipath_portdata *pd,
tid);
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
dd->ipath_tidinvalid);
+ pci_unmap_page(dd->pcidev,
+ dd->ipath_physshadow[porttid + tid],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
dd->ipath_pageshadow[porttid + tid] = NULL;
ipath_stats.sps_pageunlocks++;
}
@@ -341,7 +432,10 @@ static int ipath_tid_update(struct ipath_portdata *pd,
}
if (tid == tidcnt)
tid = 0;
- pd->port_tidcursor = tid;
+ if (!pd->port_subport_cnt)
+ pd->port_tidcursor = tid;
+ else
+ tidcursor_fp(fp) = tid;
}
done:
@@ -354,6 +448,7 @@ done:
/**
* ipath_tid_free - free a port TID
* @pd: the port
+ * @subport: the subport
* @ti: the TID info
*
* right now we are unlocking one page at a time, but since
@@ -367,7 +462,7 @@ done:
* they pass in to us.
*/
-static int ipath_tid_free(struct ipath_portdata *pd,
+static int ipath_tid_free(struct ipath_portdata *pd, unsigned subport,
const struct ipath_tid_info *ti)
{
int ret = 0;
@@ -388,11 +483,20 @@ static int ipath_tid_free(struct ipath_portdata *pd,
}
porttid = pd->port_port * dd->ipath_rcvtidcnt;
+ if (!pd->port_subport_cnt)
+ tidcnt = dd->ipath_rcvtidcnt;
+ else if (!subport) {
+ tidcnt = (dd->ipath_rcvtidcnt / pd->port_subport_cnt) +
+ (dd->ipath_rcvtidcnt % pd->port_subport_cnt);
+ porttid += dd->ipath_rcvtidcnt - tidcnt;
+ } else {
+ tidcnt = dd->ipath_rcvtidcnt / pd->port_subport_cnt;
+ porttid += tidcnt * (subport - 1);
+ }
tidbase = (u64 __iomem *) ((char __iomem *)(dd->ipath_kregbase) +
dd->ipath_rcvtidbase +
porttid * sizeof(*tidbase));
- tidcnt = dd->ipath_rcvtidcnt;
limit = sizeof(tidmap) * BITS_PER_BYTE;
if (limit > tidcnt)
/* just in case size changes in future */
@@ -417,6 +521,9 @@ static int ipath_tid_free(struct ipath_portdata *pd,
pd->port_pid, tid);
dd->ipath_f_put_tid(dd, &tidbase[tid], 1,
dd->ipath_tidinvalid);
+ pci_unmap_page(dd->pcidev,
+ dd->ipath_physshadow[porttid + tid],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
ipath_release_user_pages(
&dd->ipath_pageshadow[porttid + tid], 1);
dd->ipath_pageshadow[porttid + tid] = NULL;
@@ -581,20 +688,24 @@ bail:
/**
* ipath_manage_rcvq - manage a port's receive queue
* @pd: the port
+ * @subport: the subport
* @start_stop: action to carry out
*
* start_stop == 0 disables receive on the port, for use in queue
* overflow conditions. start_stop==1 re-enables, to be used to
* re-init the software copy of the head register
*/
-static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
+static int ipath_manage_rcvq(struct ipath_portdata *pd, unsigned subport,
+ int start_stop)
{
struct ipath_devdata *dd = pd->port_dd;
u64 tval;
- ipath_cdbg(PROC, "%sabling rcv for unit %u port %u\n",
+ ipath_cdbg(PROC, "%sabling rcv for unit %u port %u:%u\n",
start_stop ? "en" : "dis", dd->ipath_unit,
- pd->port_port);
+ pd->port_port, subport);
+ if (subport)
+ goto bail;
/* atomically clear receive enable port. */
if (start_stop) {
/*
@@ -609,7 +720,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
* updated and correct itself, even in the face of software
* bugs.
*/
- *pd->port_rcvhdrtail_kvaddr = 0;
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0;
set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
&dd->ipath_rcvctrl);
} else
@@ -630,6 +741,7 @@ static int ipath_manage_rcvq(struct ipath_portdata *pd, int start_stop)
tval = ipath_read_ureg32(dd, ur_rcvhdrtail, pd->port_port);
}
/* always; new head should be equal to new tail; see above */
+bail:
return 0;
}
@@ -687,6 +799,36 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
}
}
+/*
+ * Initialize the port data with the receive buffer sizes
+ * so this can be done while the master port is locked.
+ * Otherwise, there is a race with a slave opening the port
+ * and seeing these fields uninitialized.
+ */
+static void init_user_egr_sizes(struct ipath_portdata *pd)
+{
+ struct ipath_devdata *dd = pd->port_dd;
+ unsigned egrperchunk, egrcnt, size;
+
+ /*
+ * to avoid wasting a lot of memory, we allocate 32KB chunks of
+ * physically contiguous memory, advance through it until used up
+ * and then allocate more. Of course, we need memory to store those
+ * extra pointers, now. Started out with 256KB, but under heavy
+ * memory pressure (creating large files and then copying them over
+ * NFS while doing lots of MPI jobs), we hit some allocation
+ * failures, even though we can sleep... (2.6.10) Still get
+ * failures at 64K. 32K is the lowest we can go without wasting
+ * additional memory.
+ */
+ size = 0x8000;
+ egrperchunk = size / dd->ipath_rcvegrbufsize;
+ egrcnt = dd->ipath_rcvegrcnt;
+ pd->port_rcvegrbuf_chunks = (egrcnt + egrperchunk - 1) / egrperchunk;
+ pd->port_rcvegrbufs_perchunk = egrperchunk;
+ pd->port_rcvegrbuf_size = size;
+}
+
/**
* ipath_create_user_egr - allocate eager TID buffers
* @pd: the port to allocate TID buffers for
@@ -702,7 +844,7 @@ static void ipath_clean_part_key(struct ipath_portdata *pd,
static int ipath_create_user_egr(struct ipath_portdata *pd)
{
struct ipath_devdata *dd = pd->port_dd;
- unsigned e, egrcnt, alloced, egrperchunk, chunk, egrsize, egroff;
+ unsigned e, egrcnt, egrperchunk, chunk, egrsize, egroff;
size_t size;
int ret;
gfp_t gfp_flags;
@@ -722,31 +864,18 @@ static int ipath_create_user_egr(struct ipath_portdata *pd)
ipath_cdbg(VERBOSE, "Allocating %d egr buffers, at egrtid "
"offset %x, egrsize %u\n", egrcnt, egroff, egrsize);
- /*
- * to avoid wasting a lot of memory, we allocate 32KB chunks of
- * physically contiguous memory, advance through it until used up
- * and then allocate more. Of course, we need memory to store those
- * extra pointers, now. Started out with 256KB, but under heavy
- * memory pressure (creating large files and then copying them over
- * NFS while doing lots of MPI jobs), we hit some allocation
- * failures, even though we can sleep... (2.6.10) Still get
- * failures at 64K. 32K is the lowest we can go without wasting
- * additional memory.
- */
- size = 0x8000;
- alloced = ALIGN(egrsize * egrcnt, size);
- egrperchunk = size / egrsize;
- chunk = (egrcnt + egrperchunk - 1) / egrperchunk;
- pd->port_rcvegrbuf_chunks = chunk;
- pd->port_rcvegrbufs_perchunk = egrperchunk;
- pd->port_rcvegrbuf_size = size;
- pd->port_rcvegrbuf = vmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]));
+ chunk = pd->port_rcvegrbuf_chunks;
+ egrperchunk = pd->port_rcvegrbufs_perchunk;
+ size = pd->port_rcvegrbuf_size;
+ pd->port_rcvegrbuf = kmalloc(chunk * sizeof(pd->port_rcvegrbuf[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf) {
ret = -ENOMEM;
goto bail;
}
pd->port_rcvegrbuf_phys =
- vmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]));
+ kmalloc(chunk * sizeof(pd->port_rcvegrbuf_phys[0]),
+ GFP_KERNEL);
if (!pd->port_rcvegrbuf_phys) {
ret = -ENOMEM;
goto bail_rcvegrbuf;
@@ -791,105 +920,23 @@ bail_rcvegrbuf_phys:
pd->port_rcvegrbuf_phys[e]);
}
- vfree(pd->port_rcvegrbuf_phys);
+ kfree(pd->port_rcvegrbuf_phys);
pd->port_rcvegrbuf_phys = NULL;
bail_rcvegrbuf:
- vfree(pd->port_rcvegrbuf);
+ kfree(pd->port_rcvegrbuf);
pd->port_rcvegrbuf = NULL;
bail:
return ret;
}
-static int ipath_do_user_init(struct ipath_portdata *pd,
- const struct ipath_user_info *uinfo)
-{
- int ret = 0;
- struct ipath_devdata *dd = pd->port_dd;
- u32 head32;
-
- /* for now, if major version is different, bail */
- if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
- dev_info(&dd->pcidev->dev,
- "User major version %d not same as driver "
- "major %d\n", uinfo->spu_userversion >> 16,
- IPATH_USER_SWMAJOR);
- ret = -ENODEV;
- goto done;
- }
-
- if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
- ipath_dbg("User minor version %d not same as driver "
- "minor %d\n", uinfo->spu_userversion & 0xffff,
- IPATH_USER_SWMINOR);
-
- if (uinfo->spu_rcvhdrsize) {
- ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
- if (ret)
- goto done;
- }
-
- /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
-
- /* for right now, kernel piobufs are at end, so port 1 is at 0 */
- pd->port_piobufs = dd->ipath_piobufbase +
- dd->ipath_pbufsport * (pd->port_port -
- 1) * dd->ipath_palign;
- ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
- pd->port_port, pd->port_piobufs);
-
- /*
- * Now allocate the rcvhdr Q and eager TIDs; skip the TID
- * array for time being. If pd->port_port > chip-supported,
- * we need to do extra stuff here to handle by handling overflow
- * through port 0, someday
- */
- ret = ipath_create_rcvhdrq(dd, pd);
- if (!ret)
- ret = ipath_create_user_egr(pd);
- if (ret)
- goto done;
-
- /*
- * set the eager head register for this port to the current values
- * of the tail pointers, since we don't know if they were
- * updated on last use of the port.
- */
- head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
- ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
- dd->ipath_lastegrheads[pd->port_port] = -1;
- dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
- ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
- pd->port_port, head32);
- pd->port_tidcursor = 0; /* start at beginning after open */
- /*
- * now enable the port; the tail registers will be written to memory
- * by the chip as soon as it sees the write to
- * dd->ipath_kregs->kr_rcvctrl. The update only happens on
- * transition from 0 to 1, so clear it first, then set it as part of
- * enabling the port. This will (very briefly) affect any other
- * open ports, but it shouldn't be long enough to be an issue.
- * We explictly set the in-memory copy to 0 beforehand, so we don't
- * have to wait to be sure the DMA update has happened.
- */
- *pd->port_rcvhdrtail_kvaddr = 0ULL;
- set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
- &dd->ipath_rcvctrl);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
- dd->ipath_rcvctrl);
-done:
- return ret;
-}
-
/* common code for the mappings on dma_alloc_coherent mem */
static int ipath_mmap_mem(struct vm_area_struct *vma,
- struct ipath_portdata *pd, unsigned len,
- int write_ok, dma_addr_t addr, char *what)
+ struct ipath_portdata *pd, unsigned len, int write_ok,
+ void *kvaddr, char *what)
{
struct ipath_devdata *dd = pd->port_dd;
- unsigned pfn = (unsigned long)addr >> PAGE_SHIFT;
+ unsigned long pfn;
int ret;
if ((vma->vm_end - vma->vm_start) > len) {
@@ -912,17 +959,17 @@ static int ipath_mmap_mem(struct vm_area_struct *vma,
vma->vm_flags &= ~VM_MAYWRITE;
}
+ pfn = virt_to_phys(kvaddr) >> PAGE_SHIFT;
ret = remap_pfn_range(vma, vma->vm_start, pfn,
len, vma->vm_page_prot);
if (ret)
- dev_info(&dd->pcidev->dev,
- "%s port%u mmap of %lx, %x bytes r%c failed: %d\n",
- what, pd->port_port, (unsigned long)addr, len,
- write_ok?'w':'o', ret);
+ dev_info(&dd->pcidev->dev, "%s port%u mmap of %lx, %x "
+ "bytes r%c failed: %d\n", what, pd->port_port,
+ pfn, len, write_ok?'w':'o', ret);
else
- ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes r%c\n",
- what, pd->port_port, (unsigned long)addr, len,
- write_ok?'w':'o');
+ ipath_cdbg(VERBOSE, "%s port%u mmaped %lx, %x bytes "
+ "r%c\n", what, pd->port_port, pfn, len,
+ write_ok?'w':'o');
bail:
return ret;
}
@@ -957,7 +1004,8 @@ static int mmap_ureg(struct vm_area_struct *vma, struct ipath_devdata *dd,
static int mmap_piobufs(struct vm_area_struct *vma,
struct ipath_devdata *dd,
- struct ipath_portdata *pd)
+ struct ipath_portdata *pd,
+ unsigned piobufs, unsigned piocnt)
{
unsigned long phys;
int ret;
@@ -968,16 +1016,15 @@ static int mmap_piobufs(struct vm_area_struct *vma,
* process data, and catches users who might try to read the i/o
* space due to a bug.
*/
- if ((vma->vm_end - vma->vm_start) >
- (dd->ipath_pbufsport * dd->ipath_palign)) {
+ if ((vma->vm_end - vma->vm_start) > (piocnt * dd->ipath_palign)) {
dev_info(&dd->pcidev->dev, "FAIL mmap piobufs: "
"reqlen %lx > PAGE\n",
vma->vm_end - vma->vm_start);
- ret = -EFAULT;
+ ret = -EINVAL;
goto bail;
}
- phys = dd->ipath_physaddr + pd->port_piobufs;
+ phys = dd->ipath_physaddr + piobufs;
/*
* Don't mark this as non-cached, or we don't get the
@@ -1011,7 +1058,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
struct ipath_devdata *dd = pd->port_dd;
unsigned long start, size;
size_t total_size, i;
- dma_addr_t *phys;
+ unsigned long pfn;
int ret;
size = pd->port_rcvegrbuf_size;
@@ -1021,7 +1068,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
"reqlen %lx > actual %lx\n",
vma->vm_end - vma->vm_start,
(unsigned long) total_size);
- ret = -EFAULT;
+ ret = -EINVAL;
goto bail;
}
@@ -1035,11 +1082,11 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma,
vma->vm_flags &= ~VM_MAYWRITE;
start = vma->vm_start;
- phys = pd->port_rcvegrbuf_phys;
for (i = 0; i < pd->port_rcvegrbuf_chunks; i++, start += size) {
- ret = remap_pfn_range(vma, start, phys[i] >> PAGE_SHIFT,
- size, vma->vm_page_prot);
+ pfn = virt_to_phys(pd->port_rcvegrbuf[i]) >> PAGE_SHIFT;
+ ret = remap_pfn_range(vma, start, pfn, size,
+ vma->vm_page_prot);
if (ret < 0)
goto bail;
}
@@ -1049,6 +1096,122 @@ bail:
return ret;
}
+/*
+ * ipath_file_vma_nopage - handle a VMA page fault.
+ */
+static struct page *ipath_file_vma_nopage(struct vm_area_struct *vma,
+ unsigned long address, int *type)
+{
+ unsigned long offset = address - vma->vm_start;
+ struct page *page = NOPAGE_SIGBUS;
+ void *pageptr;
+
+ /*
+ * Convert the vmalloc address into a struct page.
+ */
+ pageptr = (void *)(offset + (vma->vm_pgoff << PAGE_SHIFT));
+ page = vmalloc_to_page(pageptr);
+ if (!page)
+ goto out;
+
+ /* Increment the reference count. */
+ get_page(page);
+ if (type)
+ *type = VM_FAULT_MINOR;
+out:
+ return page;
+}
+
+static struct vm_operations_struct ipath_file_vm_ops = {
+ .nopage = ipath_file_vma_nopage,
+};
+
+static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr,
+ struct ipath_portdata *pd, unsigned subport)
+{
+ unsigned long len;
+ struct ipath_devdata *dd;
+ void *addr;
+ size_t size;
+ int ret;
+
+ /* If the port is not shared, all addresses should be physical */
+ if (!pd->port_subport_cnt) {
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ dd = pd->port_dd;
+ size = pd->port_rcvegrbuf_chunks * pd->port_rcvegrbuf_size;
+
+ /*
+ * Master has all the slave uregbase, rcvhdrq, and
+ * rcvegrbufs mmapped.
+ */
+ if (subport == 0) {
+ unsigned num_slaves = pd->port_subport_cnt - 1;
+
+ if (pgaddr == ((u64) pd->subport_uregbase & MMAP64_MASK)) {
+ addr = pd->subport_uregbase;
+ size = PAGE_SIZE * num_slaves;
+ } else if (pgaddr == ((u64) pd->subport_rcvhdr_base &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvhdr_base;
+ size = pd->port_rcvhdrq_size * num_slaves;
+ } else if (pgaddr == ((u64) pd->subport_rcvegrbuf &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvegrbuf;
+ size *= num_slaves;
+ } else {
+ ret = -EINVAL;
+ goto bail;
+ }
+ } else if (pgaddr == (((u64) pd->subport_uregbase +
+ PAGE_SIZE * (subport - 1)) & MMAP64_MASK)) {
+ addr = pd->subport_uregbase + PAGE_SIZE * (subport - 1);
+ size = PAGE_SIZE;
+ } else if (pgaddr == (((u64) pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * (subport - 1)) &
+ MMAP64_MASK)) {
+ addr = pd->subport_rcvhdr_base +
+ pd->port_rcvhdrq_size * (subport - 1);
+ size = pd->port_rcvhdrq_size;
+ } else if (pgaddr == (((u64) pd->subport_rcvegrbuf +
+ size * (subport - 1)) & MMAP64_MASK)) {
+ addr = pd->subport_rcvegrbuf + size * (subport - 1);
+ /* rcvegrbufs are read-only on the slave */
+ if (vma->vm_flags & VM_WRITE) {
+ dev_info(&dd->pcidev->dev,
+ "Can't map eager buffers as "
+ "writable (flags=%lx)\n", vma->vm_flags);
+ ret = -EPERM;
+ goto bail;
+ }
+ /*
+ * Don't allow permission to later change to writeable
+ * with mprotect.
+ */
+ vma->vm_flags &= ~VM_MAYWRITE;
+ } else {
+ ret = -EINVAL;
+ goto bail;
+ }
+ len = vma->vm_end - vma->vm_start;
+ if (len > size) {
+ ipath_cdbg(MM, "FAIL: reqlen %lx > %zx\n", len, size);
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ vma->vm_pgoff = (unsigned long) addr >> PAGE_SHIFT;
+ vma->vm_ops = &ipath_file_vm_ops;
+ vma->vm_flags |= VM_RESERVED | VM_DONTEXPAND;
+ ret = 0;
+
+bail:
+ return ret;
+}
+
/**
* ipath_mmap - mmap various structures into user space
* @fp: the file pointer
@@ -1064,73 +1227,99 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
struct ipath_portdata *pd;
struct ipath_devdata *dd;
u64 pgaddr, ureg;
+ unsigned piobufs, piocnt;
int ret;
pd = port_fp(fp);
+ if (!pd) {
+ ret = -EINVAL;
+ goto bail;
+ }
dd = pd->port_dd;
/*
* This is the ipath_do_user_init() code, mapping the shared buffers
* into the user process. The address referred to by vm_pgoff is the
- * virtual, not physical, address; we only do one mmap for each
- * space mapped.
+ * file offset passed via mmap(). For shared ports, this is the
+ * kernel vmalloc() address of the pages to share with the master.
+ * For non-shared or master ports, this is a physical address.
+ * We only do one mmap for each space mapped.
*/
pgaddr = vma->vm_pgoff << PAGE_SHIFT;
/*
- * Must fit in 40 bits for our hardware; some checked elsewhere,
- * but we'll be paranoid. Check for 0 is mostly in case one of the
- * allocations failed, but user called mmap anyway. We want to catch
- * that before it can match.
+ * Check for 0 in case one of the allocations failed, but user
+ * called mmap anyway.
*/
- if (!pgaddr || pgaddr >= (1ULL<<40)) {
- ipath_dev_err(dd, "Bad phys addr %llx, start %lx, end %lx\n",
- (unsigned long long)pgaddr, vma->vm_start, vma->vm_end);
- return -EINVAL;
+ if (!pgaddr) {
+ ret = -EINVAL;
+ goto bail;
}
- /* just the offset of the port user registers, not physical addr */
- ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
-
- ipath_cdbg(MM, "ushare: pgaddr %llx vm_start=%lx, vmlen %lx\n",
+ ipath_cdbg(MM, "pgaddr %llx vm_start=%lx len %lx port %u:%u:%u\n",
(unsigned long long) pgaddr, vma->vm_start,
- vma->vm_end - vma->vm_start);
+ vma->vm_end - vma->vm_start, dd->ipath_unit,
+ pd->port_port, subport_fp(fp));
- if (vma->vm_start & (PAGE_SIZE-1)) {
- ipath_dev_err(dd,
- "vm_start not aligned: %lx, end=%lx phys %lx\n",
- vma->vm_start, vma->vm_end, (unsigned long)pgaddr);
- ret = -EINVAL;
+ /*
+ * Physical addresses must fit in 40 bits for our hardware.
+ * Check for kernel virtual addresses first, anything else must
+ * match a HW or memory address.
+ */
+ if (pgaddr >= (1ULL<<40)) {
+ ret = mmap_kvaddr(vma, pgaddr, pd, subport_fp(fp));
+ goto bail;
}
- else if (pgaddr == ureg)
+
+ if (!pd->port_subport_cnt) {
+ /* port is not shared */
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ piocnt = dd->ipath_pbufsport;
+ piobufs = pd->port_piobufs;
+ } else if (!subport_fp(fp)) {
+ /* caller is the master */
+ ureg = dd->ipath_uregbase + dd->ipath_palign * pd->port_port;
+ piocnt = (dd->ipath_pbufsport / pd->port_subport_cnt) +
+ (dd->ipath_pbufsport % pd->port_subport_cnt);
+ piobufs = pd->port_piobufs +
+ dd->ipath_palign * (dd->ipath_pbufsport - piocnt);
+ } else {
+ unsigned slave = subport_fp(fp) - 1;
+
+ /* caller is a slave */
+ ureg = 0;
+ piocnt = dd->ipath_pbufsport / pd->port_subport_cnt;
+ piobufs = pd->port_piobufs + dd->ipath_palign * piocnt * slave;
+ }
+
+ if (pgaddr == ureg)
ret = mmap_ureg(vma, dd, ureg);
- else if (pgaddr == pd->port_piobufs)
- ret = mmap_piobufs(vma, dd, pd);
- else if (pgaddr == (u64) pd->port_rcvegr_phys)
+ else if (pgaddr == piobufs)
+ ret = mmap_piobufs(vma, dd, pd, piobufs, piocnt);
+ else if (pgaddr == dd->ipath_pioavailregs_phys)
+ /* in-memory copy of pioavail registers */
+ ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
+ (void *) dd->ipath_pioavailregs_dma,
+ "pioavail registers");
+ else if (subport_fp(fp))
+ /* Subports don't mmap the physical receive buffers */
+ ret = -EINVAL;
+ else if (pgaddr == pd->port_rcvegr_phys)
ret = mmap_rcvegrbufs(vma, pd);
- else if (pgaddr == (u64) pd->port_rcvhdrq_phys) {
+ else if (pgaddr == (u64) pd->port_rcvhdrq_phys)
/*
* The rcvhdrq itself; readonly except on HT (so have
* to allow writable mapping), multiple pages, contiguous
* from an i/o perspective.
*/
- unsigned total_size =
- ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize
- * sizeof(u32), PAGE_SIZE);
- ret = ipath_mmap_mem(vma, pd, total_size, 1,
- pd->port_rcvhdrq_phys,
+ ret = ipath_mmap_mem(vma, pd, pd->port_rcvhdrq_size, 1,
+ pd->port_rcvhdrq,
"rcvhdrq");
- }
- else if (pgaddr == (u64)pd->port_rcvhdrqtailaddr_phys)
+ else if (pgaddr == (u64) pd->port_rcvhdrqtailaddr_phys)
/* in-memory copy of rcvhdrq tail register */
ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
- pd->port_rcvhdrqtailaddr_phys,
+ pd->port_rcvhdrtail_kvaddr,
"rcvhdrq tail");
- else if (pgaddr == dd->ipath_pioavailregs_phys)
- /* in-memory copy of pioavail registers */
- ret = ipath_mmap_mem(vma, pd, PAGE_SIZE, 0,
- dd->ipath_pioavailregs_phys,
- "pioavail registers");
else
ret = -EINVAL;
@@ -1138,9 +1327,10 @@ static int ipath_mmap(struct file *fp, struct vm_area_struct *vma)
if (ret < 0)
dev_info(&dd->pcidev->dev,
- "Failure %d on addr %lx, off %lx\n",
- -ret, vma->vm_start, vma->vm_pgoff);
-
+ "Failure %d on off %llx len %lx\n",
+ -ret, (unsigned long long)pgaddr,
+ vma->vm_end - vma->vm_start);
+bail:
return ret;
}
@@ -1154,6 +1344,8 @@ static unsigned int ipath_poll(struct file *fp,
struct ipath_devdata *dd;
pd = port_fp(fp);
+ if (!pd)
+ goto bail;
dd = pd->port_dd;
bit = pd->port_port + INFINIPATH_R_INTRAVAIL_SHIFT;
@@ -1176,7 +1368,7 @@ static unsigned int ipath_poll(struct file *fp,
if (tail == head) {
set_bit(IPATH_PORT_WAITING_RCV, &pd->port_flag);
- if(dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
+ if (dd->ipath_rhdrhead_intr_off) /* arm rcv interrupt */
(void)ipath_write_ureg(dd, ur_rcvhdrhead,
dd->ipath_rhdrhead_intr_off
| head, pd->port_port);
@@ -1200,18 +1392,80 @@ static unsigned int ipath_poll(struct file *fp,
ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
dd->ipath_rcvctrl);
+bail:
return pollflag;
}
+static int init_subports(struct ipath_devdata *dd,
+ struct ipath_portdata *pd,
+ const struct ipath_user_info *uinfo)
+{
+ int ret = 0;
+ unsigned num_slaves;
+ size_t size;
+
+ /* Old user binaries don't know about subports */
+ if ((uinfo->spu_userversion & 0xffff) != IPATH_USER_SWMINOR)
+ goto bail;
+ /*
+ * If the user is requesting zero or one port,
+ * skip the subport allocation.
+ */
+ if (uinfo->spu_subport_cnt <= 1)
+ goto bail;
+ if (uinfo->spu_subport_cnt > 4) {
+ ret = -EINVAL;
+ goto bail;
+ }
+
+ num_slaves = uinfo->spu_subport_cnt - 1;
+ pd->subport_uregbase = vmalloc(PAGE_SIZE * num_slaves);
+ if (!pd->subport_uregbase) {
+ ret = -ENOMEM;
+ goto bail;
+ }
+ /* Note: pd->port_rcvhdrq_size isn't initialized yet. */
+ size = ALIGN(dd->ipath_rcvhdrcnt * dd->ipath_rcvhdrentsize *
+ sizeof(u32), PAGE_SIZE) * num_slaves;
+ pd->subport_rcvhdr_base = vmalloc(size);
+ if (!pd->subport_rcvhdr_base) {
+ ret = -ENOMEM;
+ goto bail_ureg;
+ }
+
+ pd->subport_rcvegrbuf = vmalloc(pd->port_rcvegrbuf_chunks *
+ pd->port_rcvegrbuf_size *
+ num_slaves);
+ if (!pd->subport_rcvegrbuf) {
+ ret = -ENOMEM;
+ goto bail_rhdr;
+ }
+
+ pd->port_subport_cnt = uinfo->spu_subport_cnt;
+ pd->port_subport_id = uinfo->spu_subport_id;
+ pd->active_slaves = 1;
+ goto bail;
+
+bail_rhdr:
+ vfree(pd->subport_rcvhdr_base);
+bail_ureg:
+ vfree(pd->subport_uregbase);
+ pd->subport_uregbase = NULL;
+bail:
+ return ret;
+}
+
static int try_alloc_port(struct ipath_devdata *dd, int port,
- struct file *fp)
+ struct file *fp,
+ const struct ipath_user_info *uinfo)
{
+ struct ipath_portdata *pd;
int ret;
- if (!dd->ipath_pd[port]) {
- void *p, *ptmp;
+ if (!(pd = dd->ipath_pd[port])) {
+ void *ptmp;
- p = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
+ pd = kzalloc(sizeof(struct ipath_portdata), GFP_KERNEL);
/*
* Allocate memory for use in ipath_tid_update() just once
@@ -1221,34 +1475,36 @@ static int try_alloc_port(struct ipath_devdata *dd, int port,
ptmp = kmalloc(dd->ipath_rcvtidcnt * sizeof(u16) +
dd->ipath_rcvtidcnt * sizeof(struct page **),
GFP_KERNEL);
- if (!p || !ptmp) {
+ if (!pd || !ptmp) {
ipath_dev_err(dd, "Unable to allocate portdata "
"memory, failing open\n");
ret = -ENOMEM;
- kfree(p);
+ kfree(pd);
kfree(ptmp);
goto bail;
}
- dd->ipath_pd[port] = p;
+ dd->ipath_pd[port] = pd;
dd->ipath_pd[port]->port_port = port;
dd->ipath_pd[port]->port_dd = dd;
dd->ipath_pd[port]->port_tid_pg_list = ptmp;
init_waitqueue_head(&dd->ipath_pd[port]->port_wait);
}
- if (!dd->ipath_pd[port]->port_cnt) {
- dd->ipath_pd[port]->port_cnt = 1;
- fp->private_data = (void *) dd->ipath_pd[port];
+ if (!pd->port_cnt) {
+ pd->userversion = uinfo->spu_userversion;
+ init_user_egr_sizes(pd);
+ if ((ret = init_subports(dd, pd, uinfo)) != 0)
+ goto bail;
ipath_cdbg(PROC, "%s[%u] opened unit:port %u:%u\n",
current->comm, current->pid, dd->ipath_unit,
port);
- dd->ipath_pd[port]->port_pid = current->pid;
- strncpy(dd->ipath_pd[port]->port_comm, current->comm,
- sizeof(dd->ipath_pd[port]->port_comm));
+ pd->port_cnt = 1;
+ port_fp(fp) = pd;
+ pd->port_pid = current->pid;
+ strncpy(pd->port_comm, current->comm, sizeof(pd->port_comm));
ipath_stats.sps_ports++;
ret = 0;
- goto bail;
- }
- ret = -EBUSY;
+ } else
+ ret = -EBUSY;
bail:
return ret;
@@ -1264,7 +1520,8 @@ static inline int usable(struct ipath_devdata *dd)
| IPATH_LINKUNK));
}
-static int find_free_port(int unit, struct file *fp)
+static int find_free_port(int unit, struct file *fp,
+ const struct ipath_user_info *uinfo)
{
struct ipath_devdata *dd = ipath_lookup(unit);
int ret, i;
@@ -1279,8 +1536,8 @@ static int find_free_port(int unit, struct file *fp)
goto bail;
}
- for (i = 0; i < dd->ipath_cfgports; i++) {
- ret = try_alloc_port(dd, i, fp);
+ for (i = 1; i < dd->ipath_cfgports; i++) {
+ ret = try_alloc_port(dd, i, fp, uinfo);
if (ret != -EBUSY)
goto bail;
}
@@ -1290,13 +1547,14 @@ bail:
return ret;
}
-static int find_best_unit(struct file *fp)
+static int find_best_unit(struct file *fp,
+ const struct ipath_user_info *uinfo)
{
int ret = 0, i, prefunit = -1, devmax;
int maxofallports, npresent, nup;
int ndev;
- (void) ipath_count_units(&npresent, &nup, &maxofallports);
+ devmax = ipath_count_units(&npresent, &nup, &maxofallports);
/*
* This code is present to allow a knowledgeable person to
@@ -1343,8 +1601,6 @@ static int find_best_unit(struct file *fp)
if (prefunit != -1)
devmax = prefunit + 1;
- else
- devmax = ipath_count_units(NULL, NULL, NULL);
recheck:
for (i = 1; i < maxofallports; i++) {
for (ndev = prefunit != -1 ? prefunit : 0; ndev < devmax;
@@ -1359,7 +1615,7 @@ recheck:
* next.
*/
continue;
- ret = try_alloc_port(dd, i, fp);
+ ret = try_alloc_port(dd, i, fp, uinfo);
if (!ret)
goto done;
}
@@ -1395,22 +1651,183 @@ done:
return ret;
}
+static int find_shared_port(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int devmax, ndev, i;
+ int ret = 0;
+
+ devmax = ipath_count_units(NULL, NULL, NULL);
+
+ for (ndev = 0; ndev < devmax; ndev++) {
+ struct ipath_devdata *dd = ipath_lookup(ndev);
+
+ if (!dd)
+ continue;
+ for (i = 1; i < dd->ipath_cfgports; i++) {
+ struct ipath_portdata *pd = dd->ipath_pd[i];
+
+ /* Skip ports which are not yet open */
+ if (!pd || !pd->port_cnt)
+ continue;
+ /* Skip port if it doesn't match the requested one */
+ if (pd->port_subport_id != uinfo->spu_subport_id)
+ continue;
+ /* Verify the sharing process matches the master */
+ if (pd->port_subport_cnt != uinfo->spu_subport_cnt ||
+ pd->userversion != uinfo->spu_userversion ||
+ pd->port_cnt >= pd->port_subport_cnt) {
+ ret = -EINVAL;
+ goto done;
+ }
+ port_fp(fp) = pd;
+ subport_fp(fp) = pd->port_cnt++;
+ tidcursor_fp(fp) = 0;
+ pd->active_slaves |= 1 << subport_fp(fp);
+ ipath_cdbg(PROC,
+ "%s[%u] %u sharing %s[%u] unit:port %u:%u\n",
+ current->comm, current->pid,
+ subport_fp(fp),
+ pd->port_comm, pd->port_pid,
+ dd->ipath_unit, pd->port_port);
+ ret = 1;
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
static int ipath_open(struct inode *in, struct file *fp)
{
- int ret, user_minor;
+ /* The real work is performed later in ipath_assign_port() */
+ fp->private_data = kzalloc(sizeof(struct ipath_filedata), GFP_KERNEL);
+ return fp->private_data ? 0 : -ENOMEM;
+}
+
+
+/* Get port early, so can set affinity prior to memory allocation */
+static int ipath_assign_port(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int ret;
+ int i_minor;
+ unsigned swminor;
+
+ /* Check to be sure we haven't already initialized this file */
+ if (port_fp(fp)) {
+ ret = -EINVAL;
+ goto done;
+ }
+
+ /* for now, if major version is different, bail */
+ if ((uinfo->spu_userversion >> 16) != IPATH_USER_SWMAJOR) {
+ ipath_dbg("User major version %d not same as driver "
+ "major %d\n", uinfo->spu_userversion >> 16,
+ IPATH_USER_SWMAJOR);
+ ret = -ENODEV;
+ goto done;
+ }
+
+ swminor = uinfo->spu_userversion & 0xffff;
+ if (swminor != IPATH_USER_SWMINOR)
+ ipath_dbg("User minor version %d not same as driver "
+ "minor %d\n", swminor, IPATH_USER_SWMINOR);
mutex_lock(&ipath_mutex);
- user_minor = iminor(in) - IPATH_USER_MINOR_BASE;
+ if (swminor == IPATH_USER_SWMINOR && uinfo->spu_subport_cnt &&
+ (ret = find_shared_port(fp, uinfo))) {
+ mutex_unlock(&ipath_mutex);
+ if (ret > 0)
+ ret = 0;
+ goto done;
+ }
+
+ i_minor = iminor(fp->f_dentry->d_inode) - IPATH_USER_MINOR_BASE;
ipath_cdbg(VERBOSE, "open on dev %lx (minor %d)\n",
- (long)in->i_rdev, user_minor);
+ (long)fp->f_dentry->d_inode->i_rdev, i_minor);
- if (user_minor)
- ret = find_free_port(user_minor - 1, fp);
+ if (i_minor)
+ ret = find_free_port(i_minor - 1, fp, uinfo);
else
- ret = find_best_unit(fp);
+ ret = find_best_unit(fp, uinfo);
mutex_unlock(&ipath_mutex);
+
+done:
+ return ret;
+}
+
+
+static int ipath_do_user_init(struct file *fp,
+ const struct ipath_user_info *uinfo)
+{
+ int ret;
+ struct ipath_portdata *pd;
+ struct ipath_devdata *dd;
+ u32 head32;
+
+ pd = port_fp(fp);
+ dd = pd->port_dd;
+
+ if (uinfo->spu_rcvhdrsize) {
+ ret = ipath_setrcvhdrsize(dd, uinfo->spu_rcvhdrsize);
+ if (ret)
+ goto done;
+ }
+
+ /* for now we do nothing with rcvhdrcnt: uinfo->spu_rcvhdrcnt */
+
+ /* for right now, kernel piobufs are at end, so port 1 is at 0 */
+ pd->port_piobufs = dd->ipath_piobufbase +
+ dd->ipath_pbufsport * (pd->port_port - 1) * dd->ipath_palign;
+ ipath_cdbg(VERBOSE, "Set base of piobufs for port %u to 0x%x\n",
+ pd->port_port, pd->port_piobufs);
+
+ /*
+ * Now allocate the rcvhdr Q and eager TIDs; skip the TID
+ * array for time being. If pd->port_port > chip-supported,
+ * we need to do extra stuff here to handle by handling overflow
+ * through port 0, someday
+ */
+ ret = ipath_create_rcvhdrq(dd, pd);
+ if (!ret)
+ ret = ipath_create_user_egr(pd);
+ if (ret)
+ goto done;
+
+ /*
+ * set the eager head register for this port to the current values
+ * of the tail pointers, since we don't know if they were
+ * updated on last use of the port.
+ */
+ head32 = ipath_read_ureg32(dd, ur_rcvegrindextail, pd->port_port);
+ ipath_write_ureg(dd, ur_rcvegrindexhead, head32, pd->port_port);
+ dd->ipath_lastegrheads[pd->port_port] = -1;
+ dd->ipath_lastrcvhdrqtails[pd->port_port] = -1;
+ ipath_cdbg(VERBOSE, "Wrote port%d egrhead %x from tail regs\n",
+ pd->port_port, head32);
+ pd->port_tidcursor = 0; /* start at beginning after open */
+ /*
+ * now enable the port; the tail registers will be written to memory
+ * by the chip as soon as it sees the write to
+ * dd->ipath_kregs->kr_rcvctrl. The update only happens on
+ * transition from 0 to 1, so clear it first, then set it as part of
+ * enabling the port. This will (very briefly) affect any other
+ * open ports, but it shouldn't be long enough to be an issue.
+ * We explictly set the in-memory copy to 0 beforehand, so we don't
+ * have to wait to be sure the DMA update has happened.
+ */
+ *(volatile u64 *)pd->port_rcvhdrtail_kvaddr = 0ULL;
+ set_bit(INFINIPATH_R_PORTENABLE_SHIFT + pd->port_port,
+ &dd->ipath_rcvctrl);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+ dd->ipath_rcvctrl & ~INFINIPATH_R_TAILUPD);
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_rcvctrl,
+ dd->ipath_rcvctrl);
+done:
return ret;
}
@@ -1433,6 +1850,8 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
if (!dd->ipath_pageshadow[i])
continue;
+ pci_unmap_page(dd->pcidev, dd->ipath_physshadow[i],
+ PAGE_SIZE, PCI_DMA_FROMDEVICE);
ipath_release_user_pages_on_close(&dd->ipath_pageshadow[i],
1);
dd->ipath_pageshadow[i] = NULL;
@@ -1453,6 +1872,7 @@ static void unlock_expected_tids(struct ipath_portdata *pd)
static int ipath_close(struct inode *in, struct file *fp)
{
int ret = 0;
+ struct ipath_filedata *fd;
struct ipath_portdata *pd;
struct ipath_devdata *dd;
unsigned port;
@@ -1462,9 +1882,24 @@ static int ipath_close(struct inode *in, struct file *fp)
mutex_lock(&ipath_mutex);
- pd = port_fp(fp);
- port = pd->port_port;
+ fd = (struct ipath_filedata *) fp->private_data;
fp->private_data = NULL;
+ pd = fd->pd;
+ if (!pd) {
+ mutex_unlock(&ipath_mutex);
+ goto bail;
+ }
+ if (--pd->port_cnt) {
+ /*
+ * XXX If the master closes the port before the slave(s),
+ * revoke the mmap for the eager receive queue so
+ * the slave(s) don't wait for receive data forever.
+ */
+ pd->active_slaves &= ~(1 << fd->subport);
+ mutex_unlock(&ipath_mutex);
+ goto bail;
+ }
+ port = pd->port_port;
dd = pd->port_dd;
if (pd->port_hdrqfull) {
@@ -1503,8 +1938,6 @@ static int ipath_close(struct inode *in, struct file *fp)
/* clean up the pkeys for this port user */
ipath_clean_part_key(pd, dd);
-
-
/*
* be paranoid, and never write 0's to these, just use an
* unused part of the port 0 tail page. Of course,
@@ -1523,39 +1956,49 @@ static int ipath_close(struct inode *in, struct file *fp)
i = dd->ipath_pbufsport * (port - 1);
ipath_disarm_piobufs(dd, i, dd->ipath_pbufsport);
+ dd->ipath_f_clear_tids(dd, pd->port_port);
+
if (dd->ipath_pageshadow)
unlock_expected_tids(pd);
ipath_stats.sps_ports--;
ipath_cdbg(PROC, "%s[%u] closed port %u:%u\n",
pd->port_comm, pd->port_pid,
dd->ipath_unit, port);
-
- dd->ipath_f_clear_tids(dd, pd->port_port);
}
- pd->port_cnt = 0;
pd->port_pid = 0;
-
dd->ipath_pd[pd->port_port] = NULL; /* before releasing mutex */
mutex_unlock(&ipath_mutex);
ipath_free_pddata(dd, pd); /* after releasing the mutex */
+bail:
+ kfree(fd);
return ret;
}
-static int ipath_port_info(struct ipath_portdata *pd,
+static int ipath_port_info(struct ipath_portdata *pd, u16 subport,
struct ipath_port_info __user *uinfo)
{
struct ipath_port_info info;
int nup;
int ret;
+ size_t sz;
(void) ipath_count_units(NULL, &nup, NULL);
info.num_active = nup;
info.unit = pd->port_dd->ipath_unit;
info.port = pd->port_port;
+ info.subport = subport;
+ /* Don't return new fields if old library opened the port. */
+ if ((pd->userversion & 0xffff) == IPATH_USER_SWMINOR) {
+ /* Number of user ports available for this device. */
+ info.num_ports = pd->port_dd->ipath_cfgports - 1;
+ info.num_subports = pd->port_subport_cnt;
+ sz = sizeof(info);
+ } else
+ sz = sizeof(info) - 2 * sizeof(u16);
- if (copy_to_user(uinfo, &info, sizeof(info))) {
+ if (copy_to_user(uinfo, &info, sz)) {
ret = -EFAULT;
goto bail;
}
@@ -1565,6 +2008,16 @@ bail:
return ret;
}
+static int ipath_get_slave_info(struct ipath_portdata *pd,
+ void __user *slave_mask_addr)
+{
+ int ret = 0;
+
+ if (copy_to_user(slave_mask_addr, &pd->active_slaves, sizeof(u32)))
+ ret = -EFAULT;
+ return ret;
+}
+
static ssize_t ipath_write(struct file *fp, const char __user *data,
size_t count, loff_t *off)
{
@@ -1591,6 +2044,8 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
consumed = sizeof(cmd.type);
switch (cmd.type) {
+ case IPATH_CMD_ASSIGN_PORT:
+ case __IPATH_CMD_USER_INIT:
case IPATH_CMD_USER_INIT:
copy = sizeof(cmd.cmd.user_info);
dest = &cmd.cmd.user_info;
@@ -1617,6 +2072,11 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
dest = &cmd.cmd.part_key;
src = &ucmd->cmd.part_key;
break;
+ case IPATH_CMD_SLAVE_INFO:
+ copy = sizeof(cmd.cmd.slave_mask_addr);
+ dest = &cmd.cmd.slave_mask_addr;
+ src = &ucmd->cmd.slave_mask_addr;
+ break;
default:
ret = -EINVAL;
goto bail;
@@ -1634,34 +2094,55 @@ static ssize_t ipath_write(struct file *fp, const char __user *data,
consumed += copy;
pd = port_fp(fp);
+ if (!pd && cmd.type != __IPATH_CMD_USER_INIT &&
+ cmd.type != IPATH_CMD_ASSIGN_PORT) {
+ ret = -EINVAL;
+ goto bail;
+ }
switch (cmd.type) {
+ case IPATH_CMD_ASSIGN_PORT:
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
+ if (ret)
+ goto bail;
+ break;
+ case __IPATH_CMD_USER_INIT:
+ /* backwards compatibility, get port first */
+ ret = ipath_assign_port(fp, &cmd.cmd.user_info);
+ if (ret)
+ goto bail;
+ /* and fall through to current version. */
case IPATH_CMD_USER_INIT:
- ret = ipath_do_user_init(pd, &cmd.cmd.user_info);
- if (ret < 0)
+ ret = ipath_do_user_init(fp, &cmd.cmd.user_info);
+ if (ret)
goto bail;
ret = ipath_get_base_info(
- pd, (void __user *) (unsigned long)
+ fp, (void __user *) (unsigned long)
cmd.cmd.user_info.spu_base_info,
cmd.cmd.user_info.spu_base_info_size);
break;
case IPATH_CMD_RECV_CTRL:
- ret = ipath_manage_rcvq(pd, cmd.cmd.recv_ctrl);
+ ret = ipath_manage_rcvq(pd, subport_fp(fp), cmd.cmd.recv_ctrl);
break;
case IPATH_CMD_PORT_INFO:
- ret = ipath_port_info(pd,
+ ret = ipath_port_info(pd, subport_fp(fp),
(struct ipath_port_info __user *)
(unsigned long) cmd.cmd.port_info);
break;
case IPATH_CMD_TID_UPDATE:
- ret = ipath_tid_update(pd, &cmd.cmd.tid_info);
+ ret = ipath_tid_update(pd, fp, &cmd.cmd.tid_info);
break;
case IPATH_CMD_TID_FREE:
- ret = ipath_tid_free(pd, &cmd.cmd.tid_info);
+ ret = ipath_tid_free(pd, subport_fp(fp), &cmd.cmd.tid_info);
break;
case IPATH_CMD_SET_PART_KEY:
ret = ipath_set_part_key(pd, cmd.cmd.part_key);
break;
+ case IPATH_CMD_SLAVE_INFO:
+ ret = ipath_get_slave_info(pd,
+ (void __user *) (unsigned long)
+ cmd.cmd.slave_mask_addr);
+ break;
}
if (ret >= 0)
@@ -1858,4 +2339,3 @@ void ipath_user_remove(struct ipath_devdata *dd)
bail:
return;
}
-
diff --git a/drivers/infiniband/hw/ipath/ipath_fs.c b/drivers/infiniband/hw/ipath/ipath_fs.c
index a5eb30a06a5..d9ff283f725 100644
--- a/drivers/infiniband/hw/ipath/ipath_fs.c
+++ b/drivers/infiniband/hw/ipath/ipath_fs.c
@@ -61,14 +61,13 @@ static int ipathfs_mknod(struct inode *dir, struct dentry *dentry,
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
- inode->u.generic_ip = data;
+ inode->i_private = data;
if ((mode & S_IFMT) == S_IFDIR) {
inode->i_op = &simple_dir_inode_operations;
- inode->i_nlink++;
- dir->i_nlink++;
+ inc_nlink(inode);
+ inc_nlink(dir);
}
inode->i_fop = fops;
@@ -119,7 +118,7 @@ static ssize_t atomic_counters_read(struct file *file, char __user *buf,
u16 i;
struct ipath_devdata *dd;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
for (i = 0; i < NUM_COUNTERS; i++)
counters[i] = ipath_snap_cntr(dd, i);
@@ -139,7 +138,7 @@ static ssize_t atomic_node_info_read(struct file *file, char __user *buf,
struct ipath_devdata *dd;
u64 guid;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
guid = be64_to_cpu(dd->ipath_guid);
@@ -178,7 +177,7 @@ static ssize_t atomic_port_info_read(struct file *file, char __user *buf,
u32 tmp, tmp2;
struct ipath_devdata *dd;
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
/* so we only initialize non-zero fields. */
memset(portinfo, 0, sizeof portinfo);
@@ -325,7 +324,7 @@ static ssize_t flash_read(struct file *file, char __user *buf,
goto bail;
}
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
if (ipath_eeprom_read(dd, pos, tmp, count)) {
ipath_dev_err(dd, "failed to read from flash\n");
ret = -ENXIO;
@@ -357,19 +356,16 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
pos = *ppos;
- if ( pos < 0) {
+ if (pos != 0) {
ret = -EINVAL;
goto bail;
}
- if (pos >= sizeof(struct ipath_flash)) {
- ret = 0;
+ if (count != sizeof(struct ipath_flash)) {
+ ret = -EINVAL;
goto bail;
}
- if (count > sizeof(struct ipath_flash) - pos)
- count = sizeof(struct ipath_flash) - pos;
-
tmp = kmalloc(count, GFP_KERNEL);
if (!tmp) {
ret = -ENOMEM;
@@ -381,7 +377,7 @@ static ssize_t flash_write(struct file *file, const char __user *buf,
goto bail_tmp;
}
- dd = file->f_dentry->d_inode->u.generic_ip;
+ dd = file->f_dentry->d_inode->i_private;
if (ipath_eeprom_write(dd, pos, tmp, count)) {
ret = -ENXIO;
ipath_dev_err(dd, "failed to write to flash\n");
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6110.c b/drivers/infiniband/hw/ipath/ipath_iba6110.c
index bf2455a6d56..9e4e8d4c6e2 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6110.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6110.c
@@ -252,8 +252,8 @@ static const struct ipath_cregs ipath_ht_cregs = {
};
/* kr_intstatus, kr_intclear, kr_intmask bits */
-#define INFINIPATH_I_RCVURG_MASK 0x1FF
-#define INFINIPATH_I_RCVAVAIL_MASK 0x1FF
+#define INFINIPATH_I_RCVURG_MASK ((1U<<9)-1)
+#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<9)-1)
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
#define INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT 0
@@ -338,7 +338,7 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs,
if (crcbits) {
u16 ctrl0, ctrl1;
snprintf(bitsmsg, sizeof bitsmsg,
- "[HT%s lane %s CRC (%llx); ignore till reload]",
+ "[HT%s lane %s CRC (%llx); powercycle to completely clear]",
!(crcbits & _IPATH_HTLINK1_CRCBITS) ?
"0 (A)" : (!(crcbits & _IPATH_HTLINK0_CRCBITS)
? "1 (B)" : "0+1 (A+B)"),
@@ -389,17 +389,28 @@ static void hwerr_crcbits(struct ipath_devdata *dd, ipath_err_t hwerrs,
_IPATH_HTLINK1_CRCBITS)));
}
+/* 6110 specific hardware errors... */
+static const struct ipath_hwerror_msgs ipath_6110_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(HTCBUSIREQPARITYERR, "HTC Ireq Parity"),
+ INFINIPATH_HWE_MSG(HTCBUSTREQPARITYERR, "HTC Treq Parity"),
+ INFINIPATH_HWE_MSG(HTCBUSTRESPPARITYERR, "HTC Tresp Parity"),
+ INFINIPATH_HWE_MSG(HTCMISCERR5, "HT core Misc5"),
+ INFINIPATH_HWE_MSG(HTCMISCERR6, "HT core Misc6"),
+ INFINIPATH_HWE_MSG(HTCMISCERR7, "HT core Misc7"),
+ INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"),
+ INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
+};
+
/**
- * ipath_ht_handle_hwerrors - display hardware errors
+ * ipath_ht_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
* @msg: the output buffer
* @msgl: the size of the output buffer
*
- * Use same msg buffer as regular errors to avoid
- * excessive stack use. Most hardware errors are catastrophic, but for
- * right now, we'll print them and continue.
- * We reuse the same message buffer as ipath_handle_errors() to avoid
- * excessive stack usage.
+ * Use same msg buffer as regular errors to avoid excessive stack
+ * use. Most hardware errors are catastrophic, but for right now,
+ * we'll print them and continue. We reuse the same message buffer as
+ * ipath_handle_errors() to avoid excessive stack usage.
*/
static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
size_t msgl)
@@ -440,19 +451,49 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* make sure we get this much out, unless told to be quiet,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~dd->ipath_lasthwerror) ||
+ if ((hwerrs & ~(dd->ipath_lasthwerror |
+ ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
(ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
dd->ipath_lasthwerror |= hwerrs;
- if (hwerrs & ~infinipath_hwe_bitsextant)
+ if (hwerrs & ~dd->ipath_hwe_bitsextant)
ipath_dev_err(dd, "hwerror interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (hwerrs & ~infinipath_hwe_bitsextant));
+ (hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ /*
+ * parity errors in send memory are recoverable,
+ * just cancel the send (if indicated in * sendbuffererror),
+ * count the occurrence, unfreeze (if no other handled
+ * hardware error bits are set), and continue. They can
+ * occur if a processor speculative read is done to the PIO
+ * buffer while we are sending a packet, for example.
+ */
+ if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+ ipath_stats.sps_txeparity++;
+ ipath_dbg("Recovering from TXE parity error (%llu), "
+ "hwerrstatus=%llx\n",
+ (unsigned long long) ipath_stats.sps_txeparity,
+ (unsigned long long) hwerrs);
+ ipath_disarm_senderrbufs(dd);
+ hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
+ if (!hwerrs) { /* else leave in freeze mode */
+ ipath_write_kreg(dd,
+ dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+ return;
+ }
+ }
if (hwerrs) {
/*
* if any set that we aren't ignoring; only
@@ -499,44 +540,16 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
bits);
strlcat(msg, bitsmsg, msgl);
}
- if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
- strlcat(msg, "[IB2IPATH Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
- strlcat(msg, "[IPATH2IB Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSIREQPARITYERR)
- strlcat(msg, "[HTC Ireq Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSTREQPARITYERR)
- strlcat(msg, "[HTC Treq Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCBUSTRESPPARITYERR)
- strlcat(msg, "[HTC Tresp Parity]", msgl);
+
+ ipath_format_hwerrors(hwerrs,
+ ipath_6110_hwerror_msgs,
+ sizeof(ipath_6110_hwerror_msgs) /
+ sizeof(ipath_6110_hwerror_msgs[0]),
+ msg, msgl);
if (hwerrs & (_IPATH_HTLINK0_CRCBITS | _IPATH_HTLINK1_CRCBITS))
hwerr_crcbits(dd, hwerrs, msg, msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR5)
- strlcat(msg, "[HT core Misc5]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR6)
- strlcat(msg, "[HT core Misc6]", msgl);
- if (hwerrs & INFINIPATH_HWE_HTCMISCERR7)
- strlcat(msg, "[HT core Misc7]", msgl);
if (hwerrs & INFINIPATH_HWE_MEMBISTFAILED) {
strlcat(msg, "[Memory BIST test failed, InfiniPath hardware unusable]",
msgl);
@@ -573,11 +586,6 @@ static void ipath_ht_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
- strlcat(msg, "[Rx Dsync]", msgl);
- if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
- strlcat(msg, "[SerDes PLL]", msgl);
-
ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg)
/*
@@ -742,7 +750,6 @@ static int ipath_setup_ht_reset(struct ipath_devdata *dd)
return 0;
}
-#define HT_CAPABILITY_ID 0x08 /* HT capabilities not defined in kernel */
#define HT_INTR_DISC_CONFIG 0x80 /* HT interrupt and discovery cap */
#define HT_INTR_REG_INDEX 2 /* intconfig requires indirect accesses */
@@ -973,7 +980,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
* do this early, before we ever enable errors or hardware errors,
* mostly to avoid causing the chip to enter freeze mode.
*/
- pos = pci_find_capability(pdev, HT_CAPABILITY_ID);
+ pos = pci_find_capability(pdev, PCI_CAP_ID_HT);
if (!pos) {
ipath_dev_err(dd, "Couldn't find HyperTransport "
"capability; no interrupts\n");
@@ -996,7 +1003,7 @@ static int ipath_setup_ht_config(struct ipath_devdata *dd,
else if (cap_type == HT_INTR_DISC_CONFIG)
ihandler = set_int_handler(dd, pdev, pos);
} while ((pos = pci_find_next_capability(pdev, pos,
- HT_CAPABILITY_ID)));
+ PCI_CAP_ID_HT)));
if (!ihandler) {
ipath_dev_err(dd, "Couldn't find interrupt handler in "
@@ -1081,21 +1088,21 @@ static void ipath_setup_ht_setextled(struct ipath_devdata *dd,
ipath_write_kreg(dd, dd->ipath_kregs->kr_extctrl, extctl);
}
-static void ipath_init_ht_variables(void)
+static void ipath_init_ht_variables(struct ipath_devdata *dd)
{
- ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
- ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
- ipath_gpio_sda = IPATH_GPIO_SDA;
- ipath_gpio_scl = IPATH_GPIO_SCL;
+ dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+ dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+ dd->ipath_gpio_sda = IPATH_GPIO_SDA;
+ dd->ipath_gpio_scl = IPATH_GPIO_SCL;
- infinipath_i_bitsextant =
+ dd->ipath_i_bitsextant =
(INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
(INFINIPATH_I_RCVAVAIL_MASK <<
INFINIPATH_I_RCVAVAIL_SHIFT) |
INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
- infinipath_e_bitsextant =
+ dd->ipath_e_bitsextant =
INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
@@ -1113,7 +1120,7 @@ static void ipath_init_ht_variables(void)
INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
INFINIPATH_E_HARDWARE;
- infinipath_hwe_bitsextant =
+ dd->ipath_hwe_bitsextant =
(INFINIPATH_HWE_HTCMEMPARITYERR_MASK <<
INFINIPATH_HWE_HTCMEMPARITYERR_SHIFT) |
(INFINIPATH_HWE_TXEMEMPARITYERR_MASK <<
@@ -1142,8 +1149,8 @@ static void ipath_init_ht_variables(void)
INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
- infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
- infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+ dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
}
/**
@@ -1608,5 +1615,5 @@ void ipath_init_iba6110_funcs(struct ipath_devdata *dd)
* do very early init that is needed before ipath_f_bus is
* called
*/
- ipath_init_ht_variables();
+ ipath_init_ht_variables(dd);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_iba6120.c b/drivers/infiniband/hw/ipath/ipath_iba6120.c
index d86516d23df..a72ab9de386 100644
--- a/drivers/infiniband/hw/ipath/ipath_iba6120.c
+++ b/drivers/infiniband/hw/ipath/ipath_iba6120.c
@@ -263,8 +263,8 @@ static const struct ipath_cregs ipath_pe_cregs = {
};
/* kr_intstatus, kr_intclear, kr_intmask bits */
-#define INFINIPATH_I_RCVURG_MASK 0x1F
-#define INFINIPATH_I_RCVAVAIL_MASK 0x1F
+#define INFINIPATH_I_RCVURG_MASK ((1U<<5)-1)
+#define INFINIPATH_I_RCVAVAIL_MASK ((1U<<5)-1)
/* kr_hwerrclear, kr_hwerrmask, kr_hwerrstatus, bits */
#define INFINIPATH_HWE_PCIEMEMPARITYERR_MASK 0x000000000000003fULL
@@ -294,6 +294,33 @@ static const struct ipath_cregs ipath_pe_cregs = {
#define IPATH_GPIO_SCL (1ULL << \
(_IPATH_GPIO_SCL_NUM+INFINIPATH_EXTC_GPIOOE_SHIFT))
+/*
+ * Rev2 silicon allows suppressing check for ArmLaunch errors.
+ * this can speed up short packet sends on systems that do
+ * not guaranteee write-order.
+ */
+#define INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR (1ULL<<63)
+
+/* 6120 specific hardware errors... */
+static const struct ipath_hwerror_msgs ipath_6120_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(PCIEPOISONEDTLP, "PCIe Poisoned TLP"),
+ INFINIPATH_HWE_MSG(PCIECPLTIMEOUT, "PCIe completion timeout"),
+ /*
+ * In practice, it's unlikely wthat we'll see PCIe PLL, or bus
+ * parity or memory parity error failures, because most likely we
+ * won't be able to talk to the core of the chip. Nonetheless, we
+ * might see them, if they are in parts of the PCIe core that aren't
+ * essential.
+ */
+ INFINIPATH_HWE_MSG(PCIE1PLLFAILED, "PCIePLL1"),
+ INFINIPATH_HWE_MSG(PCIE0PLLFAILED, "PCIePLL0"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYXTLH, "PCIe XTLH core parity"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYXADM, "PCIe ADM TX core parity"),
+ INFINIPATH_HWE_MSG(PCIEBUSPARITYRADM, "PCIe ADM RX core parity"),
+ INFINIPATH_HWE_MSG(RXDSYNCMEMPARITYERR, "Rx Dsync"),
+ INFINIPATH_HWE_MSG(SERDESPLLFAILED, "SerDes PLL"),
+};
+
/**
* ipath_pe_handle_hwerrors - display hardware errors.
* @dd: the infinipath device
@@ -343,19 +370,49 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
* make sure we get this much out, unless told to be quiet,
* or it's occurred within the last 5 seconds
*/
- if ((hwerrs & ~dd->ipath_lasthwerror) ||
+ if ((hwerrs & ~(dd->ipath_lasthwerror |
+ ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT))) ||
(ipath_debug & __IPATH_VERBDBG))
dev_info(&dd->pcidev->dev, "Hardware error: hwerr=0x%llx "
"(cleared)\n", (unsigned long long) hwerrs);
dd->ipath_lasthwerror |= hwerrs;
- if (hwerrs & ~infinipath_hwe_bitsextant)
+ if (hwerrs & ~dd->ipath_hwe_bitsextant)
ipath_dev_err(dd, "hwerror interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (hwerrs & ~infinipath_hwe_bitsextant));
+ (hwerrs & ~dd->ipath_hwe_bitsextant));
ctrl = ipath_read_kreg32(dd, dd->ipath_kregs->kr_control);
if (ctrl & INFINIPATH_C_FREEZEMODE) {
+ /*
+ * parity errors in send memory are recoverable,
+ * just cancel the send (if indicated in * sendbuffererror),
+ * count the occurrence, unfreeze (if no other handled
+ * hardware error bits are set), and continue. They can
+ * occur if a processor speculative read is done to the PIO
+ * buffer while we are sending a packet, for example.
+ */
+ if (hwerrs & ((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
+ ipath_stats.sps_txeparity++;
+ ipath_dbg("Recovering from TXE parity error (%llu), "
+ "hwerrstatus=%llx\n",
+ (unsigned long long) ipath_stats.sps_txeparity,
+ (unsigned long long) hwerrs);
+ ipath_disarm_senderrbufs(dd);
+ hwerrs &= ~((INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF |
+ INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC)
+ << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT);
+ if (!hwerrs) { /* else leave in freeze mode */
+ ipath_write_kreg(dd,
+ dd->ipath_kregs->kr_control,
+ dd->ipath_control);
+ return;
+ }
+ }
if (hwerrs) {
/*
* if any set that we aren't ignoring only make the
@@ -379,9 +436,8 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
} else {
ipath_dbg("Clearing freezemode on ignored hardware "
"error\n");
- ctrl &= ~INFINIPATH_C_FREEZEMODE;
ipath_write_kreg(dd, dd->ipath_kregs->kr_control,
- ctrl);
+ dd->ipath_control);
}
}
@@ -396,24 +452,13 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
ipath_write_kreg(dd, dd->ipath_kregs->kr_hwerrmask,
dd->ipath_hwerrmask);
}
- if (hwerrs & (INFINIPATH_HWE_RXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_RXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[RXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
- if (hwerrs & (INFINIPATH_HWE_TXEMEMPARITYERR_MASK
- << INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT)) {
- bits = (u32) ((hwerrs >>
- INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) &
- INFINIPATH_HWE_TXEMEMPARITYERR_MASK);
- snprintf(bitsmsg, sizeof bitsmsg, "[TXE Parity Errs %x] ",
- bits);
- strlcat(msg, bitsmsg, msgl);
- }
+
+ ipath_format_hwerrors(hwerrs,
+ ipath_6120_hwerror_msgs,
+ sizeof(ipath_6120_hwerror_msgs)/
+ sizeof(ipath_6120_hwerror_msgs[0]),
+ msg, msgl);
+
if (hwerrs & (INFINIPATH_HWE_PCIEMEMPARITYERR_MASK
<< INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT)) {
bits = (u32) ((hwerrs >>
@@ -423,10 +468,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
"[PCIe Mem Parity Errs %x] ", bits);
strlcat(msg, bitsmsg, msgl);
}
- if (hwerrs & INFINIPATH_HWE_IBCBUSTOSPCPARITYERR)
- strlcat(msg, "[IB2IPATH Parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_IBCBUSFRSPCPARITYERR)
- strlcat(msg, "[IPATH2IB Parity]", msgl);
#define _IPATH_PLL_FAIL (INFINIPATH_HWE_COREPLL_FBSLIP | \
INFINIPATH_HWE_COREPLL_RFSLIP )
@@ -452,34 +493,6 @@ static void ipath_pe_handle_hwerrors(struct ipath_devdata *dd, char *msg,
dd->ipath_hwerrmask);
}
- if (hwerrs & INFINIPATH_HWE_PCIEPOISONEDTLP)
- strlcat(msg, "[PCIe Poisoned TLP]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIECPLTIMEOUT)
- strlcat(msg, "[PCIe completion timeout]", msgl);
-
- /*
- * In practice, it's unlikely wthat we'll see PCIe PLL, or bus
- * parity or memory parity error failures, because most likely we
- * won't be able to talk to the core of the chip. Nonetheless, we
- * might see them, if they are in parts of the PCIe core that aren't
- * essential.
- */
- if (hwerrs & INFINIPATH_HWE_PCIE1PLLFAILED)
- strlcat(msg, "[PCIePLL1]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIE0PLLFAILED)
- strlcat(msg, "[PCIePLL0]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXTLH)
- strlcat(msg, "[PCIe XTLH core parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYXADM)
- strlcat(msg, "[PCIe ADM TX core parity]", msgl);
- if (hwerrs & INFINIPATH_HWE_PCIEBUSPARITYRADM)
- strlcat(msg, "[PCIe ADM RX core parity]", msgl);
-
- if (hwerrs & INFINIPATH_HWE_RXDSYNCMEMPARITYERR)
- strlcat(msg, "[Rx Dsync]", msgl);
- if (hwerrs & INFINIPATH_HWE_SERDESPLLFAILED)
- strlcat(msg, "[SerDes PLL]", msgl);
-
ipath_dev_err(dd, "%s hardware error\n", msg);
if (isfatal && !ipath_diag_inuse && dd->ipath_freezemsg) {
/*
@@ -525,6 +538,9 @@ static int ipath_pe_boardname(struct ipath_devdata *dd, char *name,
case 5:
n = "InfiniPath_QMH7140";
break;
+ case 6:
+ n = "InfiniPath_QLE7142";
+ break;
default:
ipath_dev_err(dd,
"Don't yet know about board with ID %u\n",
@@ -571,9 +587,12 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
if (!dd->ipath_boardrev) // no PLL for Emulator
val &= ~INFINIPATH_HWE_SERDESPLLFAILED;
- /* workaround bug 9460 in internal interface bus parity checking */
- val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM;
-
+ if (dd->ipath_minrev < 2) {
+ /* workaround bug 9460 in internal interface bus parity
+ * checking. Fixed (HW bug 9490) in Rev2.
+ */
+ val &= ~INFINIPATH_HWE_PCIEBUSPARITYRADM;
+ }
dd->ipath_hwerrmask = val;
}
@@ -583,8 +602,8 @@ static void ipath_pe_init_hwerrors(struct ipath_devdata *dd)
*/
static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
{
- u64 val, tmp, config1;
- int ret = 0, change = 0;
+ u64 val, tmp, config1, prev_val;
+ int ret = 0;
ipath_dbg("Trying to bringup serdes\n");
@@ -641,6 +660,7 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_scratch);
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_xgxsconfig);
+ prev_val = val;
if (((val >> INFINIPATH_XGXS_MDIOADDR_SHIFT) &
INFINIPATH_XGXS_MDIOADDR_MASK) != 3) {
val &=
@@ -648,11 +668,9 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_XGXS_MDIOADDR_SHIFT);
/* MDIO address 3 */
val |= 3ULL << INFINIPATH_XGXS_MDIOADDR_SHIFT;
- change = 1;
}
if (val & INFINIPATH_XGXS_RESET) {
val &= ~INFINIPATH_XGXS_RESET;
- change = 1;
}
if (((val >> INFINIPATH_XGXS_RX_POL_SHIFT) &
INFINIPATH_XGXS_RX_POL_MASK) != dd->ipath_rx_pol_inv ) {
@@ -661,9 +679,19 @@ static int ipath_pe_bringup_serdes(struct ipath_devdata *dd)
INFINIPATH_XGXS_RX_POL_SHIFT);
val |= dd->ipath_rx_pol_inv <<
INFINIPATH_XGXS_RX_POL_SHIFT;
- change = 1;
}
- if (change)
+ if (dd->ipath_minrev >= 2) {
+ /* Rev 2. can tolerate multiple writes to PBC, and
+ * allowing them can provide lower latency on some
+ * CPUs, but this feature is off by default, only
+ * turned on by setting D63 of XGXSconfig reg.
+ * May want to make this conditional more
+ * fine-grained in future. This is not exactly
+ * related to XGXS, but where the bit ended up.
+ */
+ val |= INFINIPATH_XGXS_SUPPRESS_ARMLAUNCH_ERR;
+ }
+ if (val != prev_val)
ipath_write_kreg(dd, dd->ipath_kregs->kr_xgxsconfig, val);
val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_serdesconfig0);
@@ -717,9 +745,25 @@ static void ipath_pe_quiet_serdes(struct ipath_devdata *dd)
ipath_write_kreg(dd, dd->ipath_kregs->kr_serdesconfig0, val);
}
-/* this is not yet needed on this chip, so just return 0. */
static int ipath_pe_intconfig(struct ipath_devdata *dd)
{
+ u64 val;
+ u32 chiprev;
+
+ /*
+ * If the chip supports added error indication via GPIO pins,
+ * enable interrupts on those bits so the interrupt routine
+ * can count the events. Also set flag so interrupt routine
+ * can know they are expected.
+ */
+ chiprev = dd->ipath_revision >> INFINIPATH_R_CHIPREVMINOR_SHIFT;
+ if ((chiprev & INFINIPATH_R_CHIPREVMINOR_MASK) > 1) {
+ /* Rev2+ reports extra errors via internal GPIO pins */
+ dd->ipath_flags |= IPATH_GPIO_ERRINTRS;
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val |= IPATH_GPIO_ERRINTR_MASK;
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
+ }
return 0;
}
@@ -853,21 +897,23 @@ static int ipath_setup_pe_config(struct ipath_devdata *dd,
return 0;
}
-static void ipath_init_pe_variables(void)
+static void ipath_init_pe_variables(struct ipath_devdata *dd)
{
/*
* bits for selecting i2c direction and values,
* used for I2C serial flash
*/
- ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
- ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
- ipath_gpio_sda = IPATH_GPIO_SDA;
- ipath_gpio_scl = IPATH_GPIO_SCL;
+ dd->ipath_gpio_sda_num = _IPATH_GPIO_SDA_NUM;
+ dd->ipath_gpio_scl_num = _IPATH_GPIO_SCL_NUM;
+ dd->ipath_gpio_sda = IPATH_GPIO_SDA;
+ dd->ipath_gpio_scl = IPATH_GPIO_SCL;
/* variables for sanity checking interrupt and errors */
- infinipath_hwe_bitsextant =
+ dd->ipath_hwe_bitsextant =
(INFINIPATH_HWE_RXEMEMPARITYERR_MASK <<
INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) |
+ (INFINIPATH_HWE_TXEMEMPARITYERR_MASK <<
+ INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) |
(INFINIPATH_HWE_PCIEMEMPARITYERR_MASK <<
INFINIPATH_HWE_PCIEMEMPARITYERR_SHIFT) |
INFINIPATH_HWE_PCIE1PLLFAILED |
@@ -883,13 +929,13 @@ static void ipath_init_pe_variables(void)
INFINIPATH_HWE_SERDESPLLFAILED |
INFINIPATH_HWE_IBCBUSTOSPCPARITYERR |
INFINIPATH_HWE_IBCBUSFRSPCPARITYERR;
- infinipath_i_bitsextant =
+ dd->ipath_i_bitsextant =
(INFINIPATH_I_RCVURG_MASK << INFINIPATH_I_RCVURG_SHIFT) |
(INFINIPATH_I_RCVAVAIL_MASK <<
INFINIPATH_I_RCVAVAIL_SHIFT) |
INFINIPATH_I_ERROR | INFINIPATH_I_SPIOSENT |
INFINIPATH_I_SPIOBUFAVAIL | INFINIPATH_I_GPIO;
- infinipath_e_bitsextant =
+ dd->ipath_e_bitsextant =
INFINIPATH_E_RFORMATERR | INFINIPATH_E_RVCRC |
INFINIPATH_E_RICRC | INFINIPATH_E_RMINPKTLEN |
INFINIPATH_E_RMAXPKTLEN | INFINIPATH_E_RLONGPKTLEN |
@@ -907,8 +953,8 @@ static void ipath_init_pe_variables(void)
INFINIPATH_E_INVALIDADDR | INFINIPATH_E_RESET |
INFINIPATH_E_HARDWARE;
- infinipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
- infinipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
+ dd->ipath_i_rcvavail_mask = INFINIPATH_I_RCVAVAIL_MASK;
+ dd->ipath_i_rcvurg_mask = INFINIPATH_I_RCVURG_MASK;
}
/* setup the MSI stuff again after a reset. I'd like to just call
@@ -1082,6 +1128,45 @@ static void ipath_pe_put_tid(struct ipath_devdata *dd, u64 __iomem *tidptr,
mmiowb();
spin_unlock_irqrestore(&dd->ipath_tid_lock, flags);
}
+/**
+ * ipath_pe_put_tid_2 - write a TID in chip, Revision 2 or higher
+ * @dd: the infinipath device
+ * @tidptr: pointer to the expected TID (in chip) to udpate
+ * @tidtype: 0 for eager, 1 for expected
+ * @pa: physical address of in memory buffer; ipath_tidinvalid if freeing
+ *
+ * This exists as a separate routine to allow for selection of the
+ * appropriate "flavor". The static calls in cleanup just use the
+ * revision-agnostic form, as they are not performance critical.
+ */
+static void ipath_pe_put_tid_2(struct ipath_devdata *dd, u64 __iomem *tidptr,
+ u32 type, unsigned long pa)
+{
+ u32 __iomem *tidp32 = (u32 __iomem *)tidptr;
+
+ if (pa != dd->ipath_tidinvalid) {
+ if (pa & ((1U << 11) - 1)) {
+ dev_info(&dd->pcidev->dev, "BUG: physaddr %lx "
+ "not 2KB aligned!\n", pa);
+ return;
+ }
+ pa >>= 11;
+ /* paranoia check */
+ if (pa & (7<<29))
+ ipath_dev_err(dd,
+ "BUG: Physical page address 0x%lx "
+ "has bits set in 31-29\n", pa);
+
+ if (type == 0)
+ pa |= dd->ipath_tidtemplate;
+ else /* for now, always full 4KB page */
+ pa |= 2 << 29;
+ }
+ if (dd->ipath_kregbase)
+ writel(pa, tidp32);
+ mmiowb();
+}
+
/**
* ipath_pe_clear_tid - clear all TID entries for a port, expected and eager
@@ -1203,7 +1288,7 @@ int __attribute__((weak)) ipath_unordered_wc(void)
/**
* ipath_init_pe_get_base_info - set chip-specific flags for user code
- * @dd: the infinipath device
+ * @pd: the infinipath port
* @kbase: ipath_base_info pointer
*
* We set the PCIE flag because the lower bandwidth on PCIe vs
@@ -1212,6 +1297,7 @@ int __attribute__((weak)) ipath_unordered_wc(void)
static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)
{
struct ipath_base_info *kinfo = kbase;
+ struct ipath_devdata *dd;
if (ipath_unordered_wc()) {
kinfo->spi_runtime_flags |= IPATH_RUNTIME_FORCE_WC_ORDER;
@@ -1220,8 +1306,20 @@ static int ipath_pe_get_base_info(struct ipath_portdata *pd, void *kbase)
else
ipath_cdbg(PROC, "Not Intel processor, WC ordered\n");
- kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE;
+ if (pd == NULL)
+ goto done;
+ dd = pd->port_dd;
+
+ if (dd != NULL && dd->ipath_minrev >= 2) {
+ ipath_cdbg(PROC, "IBA6120 Rev2, allow multiple PBC write\n");
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_PBC_REWRITE;
+ ipath_cdbg(PROC, "IBA6120 Rev2, allow loose DMA alignment\n");
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_LOOSE_DMA_ALIGN;
+ }
+
+done:
+ kinfo->spi_runtime_flags |= IPATH_RUNTIME_PCIE;
return 0;
}
@@ -1244,7 +1342,10 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_f_quiet_serdes = ipath_pe_quiet_serdes;
dd->ipath_f_bringup_serdes = ipath_pe_bringup_serdes;
dd->ipath_f_clear_tids = ipath_pe_clear_tids;
- dd->ipath_f_put_tid = ipath_pe_put_tid;
+ if (dd->ipath_minrev >= 2)
+ dd->ipath_f_put_tid = ipath_pe_put_tid_2;
+ else
+ dd->ipath_f_put_tid = ipath_pe_put_tid;
dd->ipath_f_cleanup = ipath_setup_pe_cleanup;
dd->ipath_f_setextled = ipath_setup_pe_setextled;
dd->ipath_f_get_base_info = ipath_pe_get_base_info;
@@ -1259,6 +1360,6 @@ void ipath_init_iba6120_funcs(struct ipath_devdata *dd)
dd->ipath_kregs = &ipath_pe_kregs;
dd->ipath_cregs = &ipath_pe_cregs;
- ipath_init_pe_variables();
+ ipath_init_pe_variables(dd);
}
diff --git a/drivers/infiniband/hw/ipath/ipath_init_chip.c b/drivers/infiniband/hw/ipath/ipath_init_chip.c
index 44669dc2e22..d819cca524c 100644
--- a/drivers/infiniband/hw/ipath/ipath_init_chip.c
+++ b/drivers/infiniband/hw/ipath/ipath_init_chip.c
@@ -88,13 +88,13 @@ MODULE_PARM_DESC(kpiobufs, "Set number of PIO buffers for driver");
static int create_port0_egr(struct ipath_devdata *dd)
{
unsigned e, egrcnt;
- struct sk_buff **skbs;
+ struct ipath_skbinfo *skbinfo;
int ret;
egrcnt = dd->ipath_rcvegrcnt;
- skbs = vmalloc(sizeof(*dd->ipath_port0_skbs) * egrcnt);
- if (skbs == NULL) {
+ skbinfo = vmalloc(sizeof(*dd->ipath_port0_skbinfo) * egrcnt);
+ if (skbinfo == NULL) {
ipath_dev_err(dd, "allocation error for eager TID "
"skb array\n");
ret = -ENOMEM;
@@ -109,13 +109,13 @@ static int create_port0_egr(struct ipath_devdata *dd)
* 4 bytes so that the data buffer stays word aligned.
* See ipath_kreceive() for more details.
*/
- skbs[e] = ipath_alloc_skb(dd, GFP_KERNEL);
- if (!skbs[e]) {
+ skbinfo[e].skb = ipath_alloc_skb(dd, GFP_KERNEL);
+ if (!skbinfo[e].skb) {
ipath_dev_err(dd, "SKB allocation error for "
"eager TID %u\n", e);
while (e != 0)
- dev_kfree_skb(skbs[--e]);
- vfree(skbs);
+ dev_kfree_skb(skbinfo[--e].skb);
+ vfree(skbinfo);
ret = -ENOMEM;
goto bail;
}
@@ -124,14 +124,17 @@ static int create_port0_egr(struct ipath_devdata *dd)
* After loop above, so we can test non-NULL to see if ready
* to use at receive, etc.
*/
- dd->ipath_port0_skbs = skbs;
+ dd->ipath_port0_skbinfo = skbinfo;
for (e = 0; e < egrcnt; e++) {
- unsigned long phys =
- virt_to_phys(dd->ipath_port0_skbs[e]->data);
+ dd->ipath_port0_skbinfo[e].phys =
+ ipath_map_single(dd->pcidev,
+ dd->ipath_port0_skbinfo[e].skb->data,
+ dd->ipath_ibmaxlen, PCI_DMA_FROMDEVICE);
dd->ipath_f_put_tid(dd, e + (u64 __iomem *)
((char __iomem *) dd->ipath_kregbase +
- dd->ipath_rcvegrbase), 0, phys);
+ dd->ipath_rcvegrbase), 0,
+ dd->ipath_port0_skbinfo[e].phys);
}
ret = 0;
@@ -432,16 +435,33 @@ done:
*/
static void init_shadow_tids(struct ipath_devdata *dd)
{
- dd->ipath_pageshadow = (struct page **)
- vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ struct page **pages;
+ dma_addr_t *addrs;
+
+ pages = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
sizeof(struct page *));
- if (!dd->ipath_pageshadow)
+ if (!pages) {
ipath_dev_err(dd, "failed to allocate shadow page * "
"array, no expected sends!\n");
- else
- memset(dd->ipath_pageshadow, 0,
- dd->ipath_cfgports * dd->ipath_rcvtidcnt *
- sizeof(struct page *));
+ dd->ipath_pageshadow = NULL;
+ return;
+ }
+
+ addrs = vmalloc(dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ sizeof(dma_addr_t));
+ if (!addrs) {
+ ipath_dev_err(dd, "failed to allocate shadow dma handle "
+ "array, no expected sends!\n");
+ vfree(dd->ipath_pageshadow);
+ dd->ipath_pageshadow = NULL;
+ return;
+ }
+
+ memset(pages, 0, dd->ipath_cfgports * dd->ipath_rcvtidcnt *
+ sizeof(struct page *));
+
+ dd->ipath_pageshadow = pages;
+ dd->ipath_physshadow = addrs;
}
static void enable_chip(struct ipath_devdata *dd,
diff --git a/drivers/infiniband/hw/ipath/ipath_intr.c b/drivers/infiniband/hw/ipath/ipath_intr.c
index 49bf7bb15b0..6bee53ce5f3 100644
--- a/drivers/infiniband/hw/ipath/ipath_intr.c
+++ b/drivers/infiniband/hw/ipath/ipath_intr.c
@@ -37,6 +37,50 @@
#include "ipath_verbs.h"
#include "ipath_common.h"
+/*
+ * Called when we might have an error that is specific to a particular
+ * PIO buffer, and may need to cancel that buffer, so it can be re-used.
+ */
+void ipath_disarm_senderrbufs(struct ipath_devdata *dd)
+{
+ u32 piobcnt;
+ unsigned long sbuf[4];
+ /*
+ * it's possible that sendbuffererror could have bits set; might
+ * have already done this as a result of hardware error handling
+ */
+ piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
+ /* read these before writing errorclear */
+ sbuf[0] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror);
+ sbuf[1] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 1);
+ if (piobcnt > 128) {
+ sbuf[2] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 2);
+ sbuf[3] = ipath_read_kreg64(
+ dd, dd->ipath_kregs->kr_sendbuffererror + 3);
+ }
+
+ if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
+ int i;
+ if (ipath_debug & (__IPATH_PKTDBG|__IPATH_DBG)) {
+ __IPATH_DBG_WHICH(__IPATH_PKTDBG|__IPATH_DBG,
+ "SendbufErrs %lx %lx", sbuf[0],
+ sbuf[1]);
+ if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)
+ printk(" %lx %lx ", sbuf[2], sbuf[3]);
+ printk("\n");
+ }
+
+ for (i = 0; i < piobcnt; i++)
+ if (test_bit(i, sbuf))
+ ipath_disarm_piobufs(dd, i, 1);
+ dd->ipath_lastcancel = jiffies+3; /* no armlaunch for a bit */
+ }
+}
+
+
/* These are all rcv-related errors which we want to count for stats */
#define E_SUM_PKTERRS \
(INFINIPATH_E_RHDRLEN | INFINIPATH_E_RBADTID | \
@@ -68,53 +112,9 @@
static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
{
- unsigned long sbuf[4];
u64 ignore_this_time = 0;
- u32 piobcnt;
- /* if possible that sendbuffererror could be valid */
- piobcnt = dd->ipath_piobcnt2k + dd->ipath_piobcnt4k;
- /* read these before writing errorclear */
- sbuf[0] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror);
- sbuf[1] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 1);
- if (piobcnt > 128) {
- sbuf[2] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 2);
- sbuf[3] = ipath_read_kreg64(
- dd, dd->ipath_kregs->kr_sendbuffererror + 3);
- }
-
- if (sbuf[0] || sbuf[1] || (piobcnt > 128 && (sbuf[2] || sbuf[3]))) {
- int i;
-
- ipath_cdbg(PKT, "SendbufErrs %lx %lx ", sbuf[0], sbuf[1]);
- if (ipath_debug & __IPATH_PKTDBG && piobcnt > 128)
- printk("%lx %lx ", sbuf[2], sbuf[3]);
- for (i = 0; i < piobcnt; i++) {
- if (test_bit(i, sbuf)) {
- u32 __iomem *piobuf;
- if (i < dd->ipath_piobcnt2k)
- piobuf = (u32 __iomem *)
- (dd->ipath_pio2kbase +
- i * dd->ipath_palign);
- else
- piobuf = (u32 __iomem *)
- (dd->ipath_pio4kbase +
- (i - dd->ipath_piobcnt2k) *
- dd->ipath_4kalign);
-
- ipath_cdbg(PKT,
- "PIObuf[%u] @%p pbc is %x; ",
- i, piobuf, readl(piobuf));
-
- ipath_disarm_piobufs(dd, i, 1);
- }
- }
- if (ipath_debug & __IPATH_PKTDBG)
- printk("\n");
- }
+ ipath_disarm_senderrbufs(dd);
if ((errs & E_SUM_LINK_PKTERRS) &&
!(dd->ipath_flags & IPATH_LINKACTIVE)) {
/*
@@ -132,6 +132,82 @@ static u64 handle_e_sum_errs(struct ipath_devdata *dd, ipath_err_t errs)
return ignore_this_time;
}
+/* generic hw error messages... */
+#define INFINIPATH_HWE_TXEMEMPARITYERR_MSG(a) \
+ { \
+ .mask = ( INFINIPATH_HWE_TXEMEMPARITYERR_##a << \
+ INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT ), \
+ .msg = "TXE " #a " Memory Parity" \
+ }
+#define INFINIPATH_HWE_RXEMEMPARITYERR_MSG(a) \
+ { \
+ .mask = ( INFINIPATH_HWE_RXEMEMPARITYERR_##a << \
+ INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT ), \
+ .msg = "RXE " #a " Memory Parity" \
+ }
+
+static const struct ipath_hwerror_msgs ipath_generic_hwerror_msgs[] = {
+ INFINIPATH_HWE_MSG(IBCBUSFRSPCPARITYERR, "IPATH2IB Parity"),
+ INFINIPATH_HWE_MSG(IBCBUSTOSPCPARITYERR, "IB2IPATH Parity"),
+
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOBUF),
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOPBC),
+ INFINIPATH_HWE_TXEMEMPARITYERR_MSG(PIOLAUNCHFIFO),
+
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(RCVBUF),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(LOOKUPQ),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EAGERTID),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(EXPTID),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(FLAGBUF),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(DATAINFO),
+ INFINIPATH_HWE_RXEMEMPARITYERR_MSG(HDRINFO),
+};
+
+/**
+ * ipath_format_hwmsg - format a single hwerror message
+ * @msg message buffer
+ * @msgl length of message buffer
+ * @hwmsg message to add to message buffer
+ */
+static void ipath_format_hwmsg(char *msg, size_t msgl, const char *hwmsg)
+{
+ strlcat(msg, "[", msgl);
+ strlcat(msg, hwmsg, msgl);
+ strlcat(msg, "]", msgl);
+}
+
+/**
+ * ipath_format_hwerrors - format hardware error messages for display
+ * @hwerrs hardware errors bit vector
+ * @hwerrmsgs hardware error descriptions
+ * @nhwerrmsgs number of hwerrmsgs
+ * @msg message buffer
+ * @msgl message buffer length
+ */
+void ipath_format_hwerrors(u64 hwerrs,
+ const struct ipath_hwerror_msgs *hwerrmsgs,
+ size_t nhwerrmsgs,
+ char *msg, size_t msgl)
+{
+ int i;
+ const int glen =
+ sizeof(ipath_generic_hwerror_msgs) /
+ sizeof(ipath_generic_hwerror_msgs[0]);
+
+ for (i=0; i<glen; i++) {
+ if (hwerrs & ipath_generic_hwerror_msgs[i].mask) {
+ ipath_format_hwmsg(msg, msgl,
+ ipath_generic_hwerror_msgs[i].msg);
+ }
+ }
+
+ for (i=0; i<nhwerrmsgs; i++) {
+ if (hwerrs & hwerrmsgs[i].mask) {
+ ipath_format_hwmsg(msg, msgl, hwerrmsgs[i].msg);
+ }
+ }
+}
+
/* return the strings for the most common link states */
static char *ib_linkstate(u32 linkstate)
{
@@ -404,10 +480,10 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
dd->ipath_f_handle_hwerrors(dd, msg, sizeof msg);
}
- if (!noprint && (errs & ~infinipath_e_bitsextant))
+ if (!noprint && (errs & ~dd->ipath_e_bitsextant))
ipath_dev_err(dd, "error interrupt with unknown errors "
"%llx set\n", (unsigned long long)
- (errs & ~infinipath_e_bitsextant));
+ (errs & ~dd->ipath_e_bitsextant));
if (errs & E_SUM_ERRS)
ignore_this_time = handle_e_sum_errs(dd, errs);
@@ -478,6 +554,14 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
~(INFINIPATH_E_HARDWARE |
INFINIPATH_E_IBSTATUSCHANGED);
}
+
+ /* likely due to cancel, so suppress */
+ if ((errs & (INFINIPATH_E_SPKTLEN | INFINIPATH_E_SPIOARMLAUNCH)) &&
+ dd->ipath_lastcancel > jiffies) {
+ ipath_dbg("Suppressed armlaunch/spktlen after error send cancel\n");
+ errs &= ~(INFINIPATH_E_SPIOARMLAUNCH | INFINIPATH_E_SPKTLEN);
+ }
+
if (!errs)
return 0;
@@ -529,7 +613,7 @@ static int handle_errors(struct ipath_devdata *dd, ipath_err_t errs)
* don't report same point multiple times,
* except kernel
*/
- tl = (u32) * pd->port_rcvhdrtail_kvaddr;
+ tl = *(u64 *) pd->port_rcvhdrtail_kvaddr;
if (tl == dd->ipath_lastrcvhdrqtails[i])
continue;
hd = ipath_read_ureg32(dd, ur_rcvhdrhead,
@@ -729,9 +813,9 @@ static void handle_urcv(struct ipath_devdata *dd, u32 istat)
int rcvdint = 0;
portr = ((istat >> INFINIPATH_I_RCVAVAIL_SHIFT) &
- infinipath_i_rcvavail_mask)
+ dd->ipath_i_rcvavail_mask)
| ((istat >> INFINIPATH_I_RCVURG_SHIFT) &
- infinipath_i_rcvurg_mask);
+ dd->ipath_i_rcvurg_mask);
for (i = 1; i < dd->ipath_cfgports; i++) {
struct ipath_portdata *pd = dd->ipath_pd[i];
if (portr & (1 << i) && pd && pd->port_cnt &&
@@ -808,7 +892,7 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (oldhead != curtail) {
if (dd->ipath_flags & IPATH_GPIO_INTR) {
ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- (u64) (1 << 2));
+ (u64) (1 << IPATH_GPIO_PORT0_BIT));
istat = port0rbits | INFINIPATH_I_GPIO;
}
else
@@ -838,10 +922,10 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (unexpected)
unexpected = 0;
- if (unlikely(istat & ~infinipath_i_bitsextant))
+ if (unlikely(istat & ~dd->ipath_i_bitsextant))
ipath_dev_err(dd,
"interrupt with unknown interrupts %x set\n",
- istat & (u32) ~ infinipath_i_bitsextant);
+ istat & (u32) ~ dd->ipath_i_bitsextant);
else
ipath_cdbg(VERBOSE, "intr stat=0x%x\n", istat);
@@ -867,26 +951,80 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
if (istat & INFINIPATH_I_GPIO) {
/*
- * Packets are available in the port 0 rcv queue.
- * Eventually this needs to be generalized to check
- * IPATH_GPIO_INTR, and the specific GPIO bit, if
- * GPIO interrupts are used for anything else.
+ * GPIO interrupts fall in two broad classes:
+ * GPIO_2 indicates (on some HT4xx boards) that a packet
+ * has arrived for Port 0. Checking for this
+ * is controlled by flag IPATH_GPIO_INTR.
+ * GPIO_3..5 on IBA6120 Rev2 chips indicate errors
+ * that we need to count. Checking for this
+ * is controlled by flag IPATH_GPIO_ERRINTRS.
*/
- if (unlikely(!(dd->ipath_flags & IPATH_GPIO_INTR))) {
- u32 gpiostatus;
- gpiostatus = ipath_read_kreg32(
- dd, dd->ipath_kregs->kr_gpio_status);
- ipath_dbg("Unexpected GPIO interrupt bits %x\n",
- gpiostatus);
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- gpiostatus);
+ u32 gpiostatus;
+ u32 to_clear = 0;
+
+ gpiostatus = ipath_read_kreg32(
+ dd, dd->ipath_kregs->kr_gpio_status);
+ /* First the error-counter case.
+ */
+ if ((gpiostatus & IPATH_GPIO_ERRINTR_MASK) &&
+ (dd->ipath_flags & IPATH_GPIO_ERRINTRS)) {
+ /* want to clear the bits we see asserted. */
+ to_clear |= (gpiostatus & IPATH_GPIO_ERRINTR_MASK);
+
+ /*
+ * Count appropriately, clear bits out of our copy,
+ * as they have been "handled".
+ */
+ if (gpiostatus & (1 << IPATH_GPIO_RXUVL_BIT)) {
+ ipath_dbg("FlowCtl on UnsupVL\n");
+ dd->ipath_rxfc_unsupvl_errs++;
+ }
+ if (gpiostatus & (1 << IPATH_GPIO_OVRUN_BIT)) {
+ ipath_dbg("Overrun Threshold exceeded\n");
+ dd->ipath_overrun_thresh_errs++;
+ }
+ if (gpiostatus & (1 << IPATH_GPIO_LLI_BIT)) {
+ ipath_dbg("Local Link Integrity error\n");
+ dd->ipath_lli_errs++;
+ }
+ gpiostatus &= ~IPATH_GPIO_ERRINTR_MASK;
}
- else {
- /* Clear GPIO status bit 2 */
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
- (u64) (1 << 2));
+ /* Now the Port0 Receive case */
+ if ((gpiostatus & (1 << IPATH_GPIO_PORT0_BIT)) &&
+ (dd->ipath_flags & IPATH_GPIO_INTR)) {
+ /*
+ * GPIO status bit 2 is set, and we expected it.
+ * clear it and indicate in p0bits.
+ * This probably only happens if a Port0 pkt
+ * arrives at _just_ the wrong time, and we
+ * handle that by seting chk0rcv;
+ */
+ to_clear |= (1 << IPATH_GPIO_PORT0_BIT);
+ gpiostatus &= ~(1 << IPATH_GPIO_PORT0_BIT);
chk0rcv = 1;
}
+ if (unlikely(gpiostatus)) {
+ /*
+ * Some unexpected bits remain. If they could have
+ * caused the interrupt, complain and clear.
+ * MEA: this is almost certainly non-ideal.
+ * we should look into auto-disable of unexpected
+ * GPIO interrupts, possibly on a "three strikes"
+ * basis.
+ */
+ u32 mask;
+ mask = ipath_read_kreg32(
+ dd, dd->ipath_kregs->kr_gpio_mask);
+ if (mask & gpiostatus) {
+ ipath_dbg("Unexpected GPIO IRQ bits %x\n",
+ gpiostatus & mask);
+ to_clear |= (gpiostatus & mask);
+ }
+ }
+ if (to_clear) {
+ ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_clear,
+ (u64) to_clear);
+ }
}
chk0rcv |= istat & port0rbits;
@@ -911,9 +1049,9 @@ irqreturn_t ipath_intr(int irq, void *data, struct pt_regs *regs)
istat &= ~port0rbits;
}
- if (istat & ((infinipath_i_rcvavail_mask <<
+ if (istat & ((dd->ipath_i_rcvavail_mask <<
INFINIPATH_I_RCVAVAIL_SHIFT)
- | (infinipath_i_rcvurg_mask <<
+ | (dd->ipath_i_rcvurg_mask <<
INFINIPATH_I_RCVURG_SHIFT)))
handle_urcv(dd, istat);
diff --git a/drivers/infiniband/hw/ipath/ipath_kernel.h b/drivers/infiniband/hw/ipath/ipath_kernel.h
index a8a56276ff1..d7540b71b45 100644
--- a/drivers/infiniband/hw/ipath/ipath_kernel.h
+++ b/drivers/infiniband/hw/ipath/ipath_kernel.h
@@ -39,6 +39,8 @@
*/
#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
#include <asm/io.h>
#include "ipath_common.h"
@@ -62,7 +64,7 @@ struct ipath_portdata {
/* rcvhdrq base, needs mmap before useful */
void *port_rcvhdrq;
/* kernel virtual address where hdrqtail is updated */
- volatile __le64 *port_rcvhdrtail_kvaddr;
+ void *port_rcvhdrtail_kvaddr;
/*
* temp buffer for expected send setup, allocated at open, instead
* of each setup call
@@ -79,8 +81,8 @@ struct ipath_portdata {
dma_addr_t port_rcvhdrq_phys;
dma_addr_t port_rcvhdrqtailaddr_phys;
/*
- * number of opens on this instance (0 or 1; ignoring forks, dup,
- * etc. for now)
+ * number of opens (including slave subports) on this instance
+ * (ignoring forks, dup, etc. for now)
*/
int port_cnt;
/*
@@ -89,6 +91,10 @@ struct ipath_portdata {
*/
/* instead of calculating it */
unsigned port_port;
+ /* non-zero if port is being shared. */
+ u16 port_subport_cnt;
+ /* non-zero if port is being shared. */
+ u16 port_subport_id;
/* chip offset of PIO buffers for this port */
u32 port_piobufs;
/* how many alloc_pages() chunks in port_rcvegrbuf_pages */
@@ -121,6 +127,16 @@ struct ipath_portdata {
u16 port_pkeys[4];
/* so file ops can get at unit */
struct ipath_devdata *port_dd;
+ /* A page of memory for rcvhdrhead, rcvegrhead, rcvegrtail * N */
+ void *subport_uregbase;
+ /* An array of pages for the eager receive buffers * N */
+ void *subport_rcvegrbuf;
+ /* An array of pages for the eager header queue entries * N */
+ void *subport_rcvhdr_base;
+ /* The version of the library which opened this port */
+ u32 userversion;
+ /* Bitmask of active slaves */
+ u32 active_slaves;
};
struct sk_buff;
@@ -132,6 +148,11 @@ struct _ipath_layer {
void *l_arg;
};
+struct ipath_skbinfo {
+ struct sk_buff *skb;
+ dma_addr_t phys;
+};
+
struct ipath_devdata {
struct list_head ipath_list;
@@ -154,7 +175,7 @@ struct ipath_devdata {
/* ipath_cfgports pointers */
struct ipath_portdata **ipath_pd;
/* sk_buffs used by port 0 eager receive queue */
- struct sk_buff **ipath_port0_skbs;
+ struct ipath_skbinfo *ipath_port0_skbinfo;
/* kvirt address of 1st 2k pio buffer */
void __iomem *ipath_pio2kbase;
/* kvirt address of 1st 4k pio buffer */
@@ -315,12 +336,16 @@ struct ipath_devdata {
u8 ipath_ht_slave_off;
/* for write combining settings */
unsigned long ipath_wc_cookie;
+ unsigned long ipath_wc_base;
+ unsigned long ipath_wc_len;
/* ref count for each pkey */
atomic_t ipath_pkeyrefs[4];
/* shadow copy of all exptids physaddr; used only by funcsim */
u64 *ipath_tidsimshadow;
/* shadow copy of struct page *'s for exp tid pages */
struct page **ipath_pageshadow;
+ /* shadow copy of dma handles for exp tid pages */
+ dma_addr_t *ipath_physshadow;
/* lock to workaround chip bug 9437 */
spinlock_t ipath_tid_lock;
@@ -402,6 +427,9 @@ struct ipath_devdata {
unsigned long ipath_rcvctrl;
/* shadow kr_sendctrl */
unsigned long ipath_sendctrl;
+ /* ports waiting for PIOavail intr */
+ unsigned long ipath_portpiowait;
+ unsigned long ipath_lastcancel; /* to not count armlaunch after cancel */
/* value we put in kr_rcvhdrcnt */
u32 ipath_rcvhdrcnt;
@@ -465,8 +493,6 @@ struct ipath_devdata {
u32 ipath_htwidth;
/* HT speed (200,400,800,1000) from HT config */
u32 ipath_htspeed;
- /* ports waiting for PIOavail intr */
- unsigned long ipath_portpiowait;
/*
* number of sequential ibcstatus change for polling active/quiet
* (i.e., link not coming up).
@@ -510,8 +536,47 @@ struct ipath_devdata {
u32 ipath_lli_counter;
/* local link integrity errors */
u32 ipath_lli_errors;
+ /*
+ * Above counts only cases where _successive_ LocalLinkIntegrity
+ * errors were seen in the receive headers of kern-packets.
+ * Below are the three (monotonically increasing) counters
+ * maintained via GPIO interrupts on iba6120-rev2.
+ */
+ u32 ipath_rxfc_unsupvl_errs;
+ u32 ipath_overrun_thresh_errs;
+ u32 ipath_lli_errs;
+
+ /*
+ * Not all devices managed by a driver instance are the same
+ * type, so these fields must be per-device.
+ */
+ u64 ipath_i_bitsextant;
+ ipath_err_t ipath_e_bitsextant;
+ ipath_err_t ipath_hwe_bitsextant;
+
+ /*
+ * Below should be computable from number of ports,
+ * since they are never modified.
+ */
+ u32 ipath_i_rcvavail_mask;
+ u32 ipath_i_rcvurg_mask;
+
+ /*
+ * Register bits for selecting i2c direction and values, used for
+ * I2C serial flash.
+ */
+ u16 ipath_gpio_sda_num;
+ u16 ipath_gpio_scl_num;
+ u64 ipath_gpio_sda;
+ u64 ipath_gpio_scl;
};
+/* Private data for file operations */
+struct ipath_filedata {
+ struct ipath_portdata *pd;
+ unsigned subport;
+ unsigned tidcursor;
+};
extern struct list_head ipath_dev_list;
extern spinlock_t ipath_devs_lock;
extern struct ipath_devdata *ipath_lookup(int unit);
@@ -521,6 +586,7 @@ int ipath_enable_wc(struct ipath_devdata *dd);
void ipath_disable_wc(struct ipath_devdata *dd);
int ipath_count_units(int *npresentp, int *nupp, u32 *maxportsp);
void ipath_shutdown_device(struct ipath_devdata *);
+void ipath_disarm_senderrbufs(struct ipath_devdata *);
struct file_operations;
int ipath_cdev_init(int minor, char *name, struct file_operations *fops,
@@ -572,7 +638,11 @@ int ipath_set_lid(struct ipath_devdata *, u32, u8);
int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* for use in system calls, where we want to know device type, etc. */
-#define port_fp(fp) ((struct ipath_portdata *) (fp)->private_data)
+#define port_fp(fp) ((struct ipath_filedata *)(fp)->private_data)->pd
+#define subport_fp(fp) \
+ ((struct ipath_filedata *)(fp)->private_data)->subport
+#define tidcursor_fp(fp) \
+ ((struct ipath_filedata *)(fp)->private_data)->tidcursor
/*
* values for ipath_flags
@@ -612,6 +682,15 @@ int ipath_set_rx_pol_inv(struct ipath_devdata *dd, u8 new_pol_inv);
/* can miss port0 rx interrupts */
#define IPATH_POLL_RX_INTR 0x40000
#define IPATH_DISABLED 0x80000 /* administratively disabled */
+ /* Use GPIO interrupts for new counters */
+#define IPATH_GPIO_ERRINTRS 0x100000
+
+/* Bits in GPIO for the added interrupts */
+#define IPATH_GPIO_PORT0_BIT 2
+#define IPATH_GPIO_RXUVL_BIT 3
+#define IPATH_GPIO_OVRUN_BIT 4
+#define IPATH_GPIO_LLI_BIT 5
+#define IPATH_GPIO_ERRINTR_MASK 0x38
/* portdata flag bit offsets */
/* waiting for a packet to arrive */
@@ -799,6 +878,13 @@ int ipathfs_add_device(struct ipath_devdata *);
int ipathfs_remove_device(struct ipath_devdata *);
/*
+ * dma_addr wrappers - all 0's invalid for hw
+ */
+dma_addr_t ipath_map_page(struct pci_dev *, struct page *, unsigned long,
+ size_t, int);
+dma_addr_t ipath_map_single(struct pci_dev *, void *, size_t, int);
+
+/*
* Flush write combining store buffers (if present) and perform a write
* barrier.
*/
@@ -855,4 +941,20 @@ extern struct mutex ipath_mutex;
#endif /* _IPATH_DEBUGGING */
+/*
+ * this is used for formatting hw error messages...
+ */
+struct ipath_hwerror_msgs {
+ u64 mask;
+ const char *msg;
+};
+
+#define INFINIPATH_HWE_MSG(a, b) { .mask = INFINIPATH_HWE_##a, .msg = b }
+
+/* in ipath_intr.c... */
+void ipath_format_hwerrors(u64 hwerrs,
+ const struct ipath_hwerror_msgs *hwerrmsgs,
+ size_t nhwerrmsgs,
+ char *msg, size_t lmsg);
+
#endif /* _IPATH_KERNEL_H */
diff --git a/drivers/infiniband/hw/ipath/ipath_keys.c b/drivers/infiniband/hw/ipath/ipath_keys.c
index ba1b93226ca..9a6cbd05adc 100644
--- a/drivers/infiniband/hw/ipath/ipath_keys.c
+++ b/drivers/infiniband/hw/ipath/ipath_keys.c
@@ -118,9 +118,10 @@ void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey)
* Check the IB SGE for validity and initialize our internal version
* of it.
*/
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
struct ib_sge *sge, int acc)
{
+ struct ipath_lkey_table *rkt = &to_idev(qp->ibqp.device)->lk_table;
struct ipath_mregion *mr;
unsigned n, m;
size_t off;
@@ -140,7 +141,8 @@ int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
goto bail;
}
mr = rkt->table[(sge->lkey >> (32 - ib_ipath_lkey_table_size))];
- if (unlikely(mr == NULL || mr->lkey != sge->lkey)) {
+ if (unlikely(mr == NULL || mr->lkey != sge->lkey ||
+ qp->ibqp.pd != mr->pd)) {
ret = 0;
goto bail;
}
@@ -188,9 +190,10 @@ bail:
*
* Return 1 if successful, otherwise 0.
*/
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
u32 len, u64 vaddr, u32 rkey, int acc)
{
+ struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
struct ipath_lkey_table *rkt = &dev->lk_table;
struct ipath_sge *sge = &ss->sge;
struct ipath_mregion *mr;
@@ -214,7 +217,8 @@ int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
}
mr = rkt->table[(rkey >> (32 - ib_ipath_lkey_table_size))];
- if (unlikely(mr == NULL || mr->lkey != rkey)) {
+ if (unlikely(mr == NULL || mr->lkey != rkey ||
+ qp->ibqp.pd != mr->pd)) {
ret = 0;
goto bail;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_mad.c b/drivers/infiniband/hw/ipath/ipath_mad.c
index 72d1db89db8..25908b02fbe 100644
--- a/drivers/infiniband/hw/ipath/ipath_mad.c
+++ b/drivers/infiniband/hw/ipath/ipath_mad.c
@@ -87,7 +87,8 @@ static int recv_subn_get_nodeinfo(struct ib_smp *smp,
struct ipath_devdata *dd = to_idev(ibdev)->dd;
u32 vendor, majrev, minrev;
- if (smp->attr_mod)
+ /* GUID 0 is illegal */
+ if (smp->attr_mod || (dd->ipath_guid == 0))
smp->status |= IB_SMP_INVALID_FIELD;
nip->base_version = 1;
@@ -131,10 +132,15 @@ static int recv_subn_get_guidinfo(struct ib_smp *smp,
* We only support one GUID for now. If this changes, the
* portinfo.guid_cap field needs to be updated too.
*/
- if (startgx == 0)
- /* The first is a copy of the read-only HW GUID. */
- *p = to_idev(ibdev)->dd->ipath_guid;
- else
+ if (startgx == 0) {
+ __be64 g = to_idev(ibdev)->dd->ipath_guid;
+ if (g == 0)
+ /* GUID 0 is illegal */
+ smp->status |= IB_SMP_INVALID_FIELD;
+ else
+ /* The first is a copy of the read-only HW GUID. */
+ *p = g;
+ } else
smp->status |= IB_SMP_INVALID_FIELD;
return reply(smp);
diff --git a/drivers/infiniband/hw/ipath/ipath_mr.c b/drivers/infiniband/hw/ipath/ipath_mr.c
index b36f6fb3e37..a0673c1eef7 100644
--- a/drivers/infiniband/hw/ipath/ipath_mr.c
+++ b/drivers/infiniband/hw/ipath/ipath_mr.c
@@ -138,6 +138,7 @@ struct ib_mr *ipath_reg_phys_mr(struct ib_pd *pd,
goto bail;
}
+ mr->mr.pd = pd;
mr->mr.user_base = *iova_start;
mr->mr.iova = *iova_start;
mr->mr.length = 0;
@@ -197,6 +198,7 @@ struct ib_mr *ipath_reg_user_mr(struct ib_pd *pd, struct ib_umem *region,
goto bail;
}
+ mr->mr.pd = pd;
mr->mr.user_base = region->user_base;
mr->mr.iova = region->virt_base;
mr->mr.length = region->length;
@@ -289,6 +291,7 @@ struct ib_fmr *ipath_alloc_fmr(struct ib_pd *pd, int mr_access_flags,
* Resources are allocated but no valid mapping (RKEY can't be
* used).
*/
+ fmr->mr.pd = pd;
fmr->mr.user_base = 0;
fmr->mr.iova = 0;
fmr->mr.length = 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_qp.c b/drivers/infiniband/hw/ipath/ipath_qp.c
index 224b0f40767..46c1c89bf6a 100644
--- a/drivers/infiniband/hw/ipath/ipath_qp.c
+++ b/drivers/infiniband/hw/ipath/ipath_qp.c
@@ -335,6 +335,7 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->s_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
qp->r_ack_state = IB_OPCODE_RC_ACKNOWLEDGE;
qp->r_nak_state = 0;
+ qp->r_wrid_valid = 0;
qp->s_rnr_timeout = 0;
qp->s_head = 0;
qp->s_tail = 0;
@@ -342,6 +343,7 @@ static void ipath_reset_qp(struct ipath_qp *qp)
qp->s_last = 0;
qp->s_ssn = 1;
qp->s_lsn = 0;
+ qp->s_wait_credit = 0;
if (qp->r_rq.wq) {
qp->r_rq.wq->head = 0;
qp->r_rq.wq->tail = 0;
@@ -352,12 +354,13 @@ static void ipath_reset_qp(struct ipath_qp *qp)
/**
* ipath_error_qp - put a QP into an error state
* @qp: the QP to put into an error state
+ * @err: the receive completion error to signal if a RWQE is active
*
* Flushes both send and receive work queues.
* QP s_lock should be held and interrupts disabled.
*/
-void ipath_error_qp(struct ipath_qp *qp)
+void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err)
{
struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
struct ib_wc wc;
@@ -373,7 +376,6 @@ void ipath_error_qp(struct ipath_qp *qp)
list_del_init(&qp->piowait);
spin_unlock(&dev->pending_lock);
- wc.status = IB_WC_WR_FLUSH_ERR;
wc.vendor_err = 0;
wc.byte_len = 0;
wc.imm_data = 0;
@@ -385,6 +387,12 @@ void ipath_error_qp(struct ipath_qp *qp)
wc.sl = 0;
wc.dlid_path_bits = 0;
wc.port_num = 0;
+ if (qp->r_wrid_valid) {
+ qp->r_wrid_valid = 0;
+ wc.status = err;
+ ipath_cq_enter(to_icq(qp->ibqp.send_cq), &wc, 1);
+ }
+ wc.status = IB_WC_WR_FLUSH_ERR;
while (qp->s_last != qp->s_head) {
struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
@@ -501,7 +509,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
break;
case IB_QPS_ERR:
- ipath_error_qp(qp);
+ ipath_error_qp(qp, IB_WC_GENERAL_ERR);
break;
default:
@@ -516,7 +524,7 @@ int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
qp->remote_qpn = attr->dest_qp_num;
if (attr_mask & IB_QP_SQ_PSN) {
- qp->s_next_psn = attr->sq_psn;
+ qp->s_psn = qp->s_next_psn = attr->sq_psn;
qp->s_last_psn = qp->s_next_psn - 1;
}
diff --git a/drivers/infiniband/hw/ipath/ipath_rc.c b/drivers/infiniband/hw/ipath/ipath_rc.c
index a08654042c0..ce6038743c5 100644
--- a/drivers/infiniband/hw/ipath/ipath_rc.c
+++ b/drivers/infiniband/hw/ipath/ipath_rc.c
@@ -201,6 +201,18 @@ int ipath_make_rc_req(struct ipath_qp *qp,
qp->s_rnr_timeout)
goto done;
+ /* Limit the number of packets sent without an ACK. */
+ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT) > 0) {
+ qp->s_wait_credit = 1;
+ dev->n_rc_stalls++;
+ spin_lock(&dev->pending_lock);
+ if (list_empty(&qp->timerwait))
+ list_add_tail(&qp->timerwait,
+ &dev->pending[dev->pending_index]);
+ spin_unlock(&dev->pending_lock);
+ goto done;
+ }
+
/* header size in 32-bit words LRH+BTH = (8+12)/4. */
hwords = 5;
bth0 = 0;
@@ -221,7 +233,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
/* Check if send work queue is empty. */
if (qp->s_tail == qp->s_head)
goto done;
- qp->s_psn = wqe->psn = qp->s_next_psn;
+ wqe->psn = qp->s_next_psn;
newreq = 1;
}
/*
@@ -229,10 +241,7 @@ int ipath_make_rc_req(struct ipath_qp *qp,
* original work request since we may need to resend
* it.
*/
- qp->s_sge.sge = wqe->sg_list[0];
- qp->s_sge.sg_list = wqe->sg_list + 1;
- qp->s_sge.num_sge = wqe->wr.num_sge;
- qp->s_len = len = wqe->length;
+ len = wqe->length;
ss = &qp->s_sge;
bth2 = 0;
switch (wqe->wr.opcode) {
@@ -356,14 +365,23 @@ int ipath_make_rc_req(struct ipath_qp *qp,
default:
goto done;
}
+ qp->s_sge.sge = wqe->sg_list[0];
+ qp->s_sge.sg_list = wqe->sg_list + 1;
+ qp->s_sge.num_sge = wqe->wr.num_sge;
+ qp->s_len = wqe->length;
if (newreq) {
qp->s_tail++;
if (qp->s_tail >= qp->s_size)
qp->s_tail = 0;
}
- bth2 |= qp->s_psn++ & IPATH_PSN_MASK;
- if ((int)(qp->s_psn - qp->s_next_psn) > 0)
- qp->s_next_psn = qp->s_psn;
+ bth2 |= qp->s_psn & IPATH_PSN_MASK;
+ if (wqe->wr.opcode == IB_WR_RDMA_READ)
+ qp->s_psn = wqe->lpsn + 1;
+ else {
+ qp->s_psn++;
+ if ((int)(qp->s_psn - qp->s_next_psn) > 0)
+ qp->s_next_psn = qp->s_psn;
+ }
/*
* Put the QP on the pending list so lost ACKs will cause
* a retry. More than one request can be pending so the
@@ -393,12 +411,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
ss = &qp->s_sge;
len = qp->s_len;
if (len > pmtu) {
- /*
- * Request an ACK every 1/2 MB to avoid retransmit
- * timeouts.
- */
- if (((wqe->length - len) % (512 * 1024)) == 0)
- bth2 |= 1 << 31;
len = pmtu;
break;
}
@@ -435,12 +447,6 @@ int ipath_make_rc_req(struct ipath_qp *qp,
ss = &qp->s_sge;
len = qp->s_len;
if (len > pmtu) {
- /*
- * Request an ACK every 1/2 MB to avoid retransmit
- * timeouts.
- */
- if (((wqe->length - len) % (512 * 1024)) == 0)
- bth2 |= 1 << 31;
len = pmtu;
break;
}
@@ -498,6 +504,8 @@ int ipath_make_rc_req(struct ipath_qp *qp,
*/
goto done;
}
+ if (ipath_cmp24(qp->s_psn, qp->s_last_psn + IPATH_PSN_CREDIT - 1) >= 0)
+ bth2 |= 1 << 31; /* Request ACK. */
qp->s_len -= len;
qp->s_hdrwords = hwords;
qp->s_cur_sge = ss;
@@ -688,13 +696,6 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
struct ipath_swqe *wqe = get_swqe_ptr(qp, qp->s_last);
struct ipath_ibdev *dev;
- /*
- * If there are no requests pending, we are done.
- */
- if (ipath_cmp24(psn, qp->s_next_psn) >= 0 ||
- qp->s_last == qp->s_tail)
- goto done;
-
if (qp->s_retry == 0) {
wc->wr_id = wqe->wr.wr_id;
wc->status = IB_WC_RETRY_EXC_ERR;
@@ -729,14 +730,21 @@ void ipath_restart_rc(struct ipath_qp *qp, u32 psn, struct ib_wc *wc)
dev->n_rc_resends += (int)qp->s_psn - (int)psn;
reset_psn(qp, psn);
-
-done:
tasklet_hi_schedule(&qp->s_task);
bail:
return;
}
+static inline void update_last_psn(struct ipath_qp *qp, u32 psn)
+{
+ if (qp->s_wait_credit) {
+ qp->s_wait_credit = 0;
+ tasklet_hi_schedule(&qp->s_task);
+ }
+ qp->s_last_psn = psn;
+}
+
/**
* do_rc_ack - process an incoming RC ACK
* @qp: the QP the ACK came in on
@@ -754,6 +762,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
struct ib_wc wc;
struct ipath_swqe *wqe;
int ret = 0;
+ u32 ack_psn;
/*
* Remove the QP from the timeout queue (or RNR timeout queue).
@@ -766,26 +775,26 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
list_del_init(&qp->timerwait);
spin_unlock(&dev->pending_lock);
+ /* Nothing is pending to ACK/NAK. */
+ if (unlikely(qp->s_last == qp->s_tail))
+ goto bail;
+
/*
* Note that NAKs implicitly ACK outstanding SEND and RDMA write
* requests and implicitly NAK RDMA read and atomic requests issued
* before the NAK'ed request. The MSN won't include the NAK'ed
* request but will include an ACK'ed request(s).
*/
+ ack_psn = psn;
+ if (aeth >> 29)
+ ack_psn--;
wqe = get_swqe_ptr(qp, qp->s_last);
- /* Nothing is pending to ACK/NAK. */
- if (qp->s_last == qp->s_tail)
- goto bail;
-
/*
* The MSN might be for a later WQE than the PSN indicates so
* only complete WQEs that the PSN finishes.
*/
- while (ipath_cmp24(psn, wqe->lpsn) >= 0) {
- /* If we are ACKing a WQE, the MSN should be >= the SSN. */
- if (ipath_cmp24(aeth, wqe->ssn) < 0)
- break;
+ while (ipath_cmp24(ack_psn, wqe->lpsn) >= 0) {
/*
* If this request is a RDMA read or atomic, and the ACK is
* for a later operation, this ACK NAKs the RDMA read or
@@ -796,7 +805,8 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
* is sent but before the response is received.
*/
if ((wqe->wr.opcode == IB_WR_RDMA_READ &&
- opcode != OP(RDMA_READ_RESPONSE_LAST)) ||
+ (opcode != OP(RDMA_READ_RESPONSE_LAST) ||
+ ipath_cmp24(ack_psn, wqe->lpsn) != 0)) ||
((wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD) &&
(opcode != OP(ATOMIC_ACKNOWLEDGE) ||
@@ -805,7 +815,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
* The last valid PSN seen is the previous
* request's.
*/
- qp->s_last_psn = wqe->psn - 1;
+ update_last_psn(qp, wqe->psn - 1);
/* Retry this request. */
ipath_restart_rc(qp, wqe->psn, &wc);
/*
@@ -814,6 +824,10 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
*/
goto bail;
}
+ if (wqe->wr.opcode == IB_WR_RDMA_READ ||
+ wqe->wr.opcode == IB_WR_ATOMIC_CMP_AND_SWP ||
+ wqe->wr.opcode == IB_WR_ATOMIC_FETCH_AND_ADD)
+ tasklet_hi_schedule(&qp->s_task);
/* Post a send completion queue entry if requested. */
if (!test_bit(IPATH_S_SIGNAL_REQ_WR, &qp->s_flags) ||
(wqe->wr.send_flags & IB_SEND_SIGNALED)) {
@@ -864,7 +878,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
ipath_get_credit(qp, aeth);
qp->s_rnr_retry = qp->s_rnr_retry_cnt;
qp->s_retry = qp->s_retry_cnt;
- qp->s_last_psn = psn;
+ update_last_psn(qp, psn);
ret = 1;
goto bail;
@@ -883,7 +897,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
goto bail;
/* The last valid PSN is the previous PSN. */
- qp->s_last_psn = psn - 1;
+ update_last_psn(qp, psn - 1);
dev->n_rc_resends += (int)qp->s_psn - (int)psn;
@@ -898,7 +912,7 @@ static int do_rc_ack(struct ipath_qp *qp, u32 aeth, u32 psn, int opcode)
case 3: /* NAK */
/* The last valid PSN seen is the previous request's. */
if (qp->s_last != qp->s_tail)
- qp->s_last_psn = wqe->psn - 1;
+ update_last_psn(qp, wqe->psn - 1);
switch ((aeth >> IPATH_AETH_CREDIT_SHIFT) &
IPATH_AETH_CREDIT_MASK) {
case 0: /* PSN sequence error */
@@ -1044,7 +1058,8 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
/* no AETH, no ACK */
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
dev->n_rdma_seq++;
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+ if (qp->s_last != qp->s_tail)
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
goto ack_done;
}
rdma_read:
@@ -1071,7 +1086,7 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
* since we don't want s_sge modified.
*/
qp->s_len -= pmtu;
- qp->s_last_psn = psn;
+ update_last_psn(qp, psn);
spin_unlock_irqrestore(&qp->s_lock, flags);
ipath_copy_sge(&qp->s_sge, data, pmtu);
goto bail;
@@ -1080,7 +1095,8 @@ static inline void ipath_rc_rcv_resp(struct ipath_ibdev *dev,
/* ACKs READ req. */
if (unlikely(ipath_cmp24(psn, qp->s_last_psn + 1))) {
dev->n_rdma_seq++;
- ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
+ if (qp->s_last != qp->s_tail)
+ ipath_restart_rc(qp, qp->s_last_psn + 1, &wc);
goto ack_done;
}
/* FALLTHROUGH */
@@ -1223,7 +1239,7 @@ static inline int ipath_rc_rcv_error(struct ipath_ibdev *dev,
* Address range must be a subset of the original
* request and start on pmtu boundaries.
*/
- ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+ ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
qp->s_rdma_len, vaddr, rkey,
IB_ACCESS_REMOTE_READ);
if (unlikely(!ok)) {
@@ -1282,6 +1298,14 @@ done:
return 1;
}
+static void ipath_rc_error(struct ipath_qp *qp, enum ib_wc_status err)
+{
+ spin_lock_irq(&qp->s_lock);
+ qp->state = IB_QPS_ERR;
+ ipath_error_qp(qp, err);
+ spin_unlock_irq(&qp->s_lock);
+}
+
/**
* ipath_rc_rcv - process an incoming RC packet
* @dev: the device this packet came in on
@@ -1309,6 +1333,10 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
struct ib_reth *reth;
int header_in_data;
+ /* Validate the SLID. See Ch. 9.6.1.5 */
+ if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
+ goto done;
+
/* Check for GRH */
if (!has_grh) {
ohdr = &hdr->u.oth;
@@ -1370,8 +1398,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
*/
if (qp->r_ack_state >= OP(COMPARE_SWAP))
goto send_ack;
- /* XXX Flush WQEs */
- qp->state = IB_QPS_ERR;
+ ipath_rc_error(qp, IB_WC_REM_INV_REQ_ERR);
qp->r_ack_state = OP(SEND_ONLY);
qp->r_nak_state = IB_NAK_INVALID_REQUEST;
qp->r_ack_psn = qp->r_psn;
@@ -1477,9 +1504,9 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
goto nack_inv;
ipath_copy_sge(&qp->r_sge, data, tlen);
qp->r_msn++;
- if (opcode == OP(RDMA_WRITE_LAST) ||
- opcode == OP(RDMA_WRITE_ONLY))
+ if (!qp->r_wrid_valid)
break;
+ qp->r_wrid_valid = 0;
wc.wr_id = qp->r_wr_id;
wc.status = IB_WC_SUCCESS;
wc.opcode = IB_WC_RECV;
@@ -1517,7 +1544,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey & NAK */
- ok = ipath_rkey_ok(dev, &qp->r_sge,
+ ok = ipath_rkey_ok(qp, &qp->r_sge,
qp->r_len, vaddr, rkey,
IB_ACCESS_REMOTE_WRITE);
if (unlikely(!ok))
@@ -1559,7 +1586,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey & NAK */
- ok = ipath_rkey_ok(dev, &qp->s_rdma_sge,
+ ok = ipath_rkey_ok(qp, &qp->s_rdma_sge,
qp->s_rdma_len, vaddr, rkey,
IB_ACCESS_REMOTE_READ);
if (unlikely(!ok)) {
@@ -1618,7 +1645,7 @@ void ipath_rc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
goto nack_inv;
rkey = be32_to_cpu(ateth->rkey);
/* Check rkey & NAK */
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge,
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge,
sizeof(u64), vaddr, rkey,
IB_ACCESS_REMOTE_ATOMIC)))
goto nack_acc;
@@ -1670,8 +1697,7 @@ nack_acc:
* is pending though.
*/
if (qp->r_ack_state < OP(COMPARE_SWAP)) {
- /* XXX Flush WQEs */
- qp->state = IB_QPS_ERR;
+ ipath_rc_error(qp, IB_WC_REM_ACCESS_ERR);
qp->r_ack_state = OP(RDMA_WRITE_ONLY);
qp->r_nak_state = IB_NAK_REMOTE_ACCESS_ERROR;
qp->r_ack_psn = qp->r_psn;
diff --git a/drivers/infiniband/hw/ipath/ipath_registers.h b/drivers/infiniband/hw/ipath/ipath_registers.h
index 6e23b3d632b..dffc76016d3 100644
--- a/drivers/infiniband/hw/ipath/ipath_registers.h
+++ b/drivers/infiniband/hw/ipath/ipath_registers.h
@@ -134,10 +134,24 @@
#define INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT 40
#define INFINIPATH_HWE_RXEMEMPARITYERR_MASK 0x7FULL
#define INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT 44
-#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL
-#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL
#define INFINIPATH_HWE_IBCBUSTOSPCPARITYERR 0x4000000000000000ULL
#define INFINIPATH_HWE_IBCBUSFRSPCPARITYERR 0x8000000000000000ULL
+/* txe mem parity errors (shift by INFINIPATH_HWE_TXEMEMPARITYERR_SHIFT) */
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOBUF 0x1ULL
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOPBC 0x2ULL
+#define INFINIPATH_HWE_TXEMEMPARITYERR_PIOLAUNCHFIFO 0x4ULL
+/* rxe mem parity errors (shift by INFINIPATH_HWE_RXEMEMPARITYERR_SHIFT) */
+#define INFINIPATH_HWE_RXEMEMPARITYERR_RCVBUF 0x01ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_LOOKUPQ 0x02ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EAGERTID 0x04ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_EXPTID 0x08ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_FLAGBUF 0x10ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_DATAINFO 0x20ULL
+#define INFINIPATH_HWE_RXEMEMPARITYERR_HDRINFO 0x40ULL
+/* waldo specific -- find the rest in ipath_6110.c */
+#define INFINIPATH_HWE_RXDSYNCMEMPARITYERR 0x0000000400000000ULL
+/* monty specific -- find the rest in ipath_6120.c */
+#define INFINIPATH_HWE_MEMBISTFAILED 0x0040000000000000ULL
/* kr_hwdiagctrl bits */
#define INFINIPATH_DC_FORCETXEMEMPARITYERR_MASK 0xFULL
@@ -209,9 +223,9 @@
/* combination link status states that we use with some frequency */
#define IPATH_IBSTATE_MASK ((INFINIPATH_IBCS_LINKTRAININGSTATE_MASK \
- << INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
+ << INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT) | \
(INFINIPATH_IBCS_LINKSTATE_MASK \
- <<INFINIPATH_IBCS_LINKTRAININGSTATE_SHIFT))
+ <<INFINIPATH_IBCS_LINKSTATE_SHIFT))
#define IPATH_IBSTATE_INIT ((INFINIPATH_IBCS_L_STATE_INIT \
<< INFINIPATH_IBCS_LINKSTATE_SHIFT) | \
(INFINIPATH_IBCS_LT_STATE_LINKUP \
@@ -302,6 +316,17 @@
typedef u64 ipath_err_t;
+/* The following change with the type of device, so
+ * need to be part of the ipath_devdata struct, or
+ * we could have problems plugging in devices of
+ * different types (e.g. one HT, one PCIE)
+ * in one system, to be managed by one driver.
+ * On the other hand, this file is may also be included
+ * by other code, so leave the declarations here
+ * temporarily. Minor footprint issue if common-model
+ * linker used, none if C89+ linker used.
+ */
+
/* mask of defined bits for various registers */
extern u64 infinipath_i_bitsextant;
extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
@@ -310,13 +335,6 @@ extern ipath_err_t infinipath_e_bitsextant, infinipath_hwe_bitsextant;
extern u32 infinipath_i_rcvavail_mask, infinipath_i_rcvurg_mask;
/*
- * register bits for selecting i2c direction and values, used for I2C serial
- * flash
- */
-extern u16 ipath_gpio_sda_num, ipath_gpio_scl_num;
-extern u64 ipath_gpio_sda, ipath_gpio_scl;
-
-/*
* These are the infinipath general register numbers (not offsets).
* The kernel registers are used directly, those beyond the kernel
* registers are calculated from one of the base registers. The use of
diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c
index 5c1da2d25e0..f7530512045 100644
--- a/drivers/infiniband/hw/ipath/ipath_ruc.c
+++ b/drivers/infiniband/hw/ipath/ipath_ruc.c
@@ -108,7 +108,6 @@ void ipath_insert_rnr_queue(struct ipath_qp *qp)
static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe)
{
- struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
int user = to_ipd(qp->ibqp.pd)->user;
int i, j, ret;
struct ib_wc wc;
@@ -119,8 +118,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe)
continue;
/* Check LKEY */
if ((user && wqe->sg_list[i].lkey == 0) ||
- !ipath_lkey_ok(&dev->lk_table,
- &qp->r_sg_list[j], &wqe->sg_list[i],
+ !ipath_lkey_ok(qp, &qp->r_sg_list[j], &wqe->sg_list[i],
IB_ACCESS_LOCAL_WRITE))
goto bad_lkey;
qp->r_len += wqe->sg_list[i].length;
@@ -231,6 +229,7 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only)
}
}
spin_unlock_irqrestore(&rq->lock, flags);
+ qp->r_wrid_valid = 1;
bail:
return ret;
@@ -326,7 +325,7 @@ again:
case IB_WR_RDMA_WRITE:
if (wqe->length == 0)
break;
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, wqe->length,
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, wqe->length,
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_WRITE))) {
@@ -350,7 +349,7 @@ again:
break;
case IB_WR_RDMA_READ:
- if (unlikely(!ipath_rkey_ok(dev, &sqp->s_sge, wqe->length,
+ if (unlikely(!ipath_rkey_ok(qp, &sqp->s_sge, wqe->length,
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_READ)))
@@ -365,7 +364,7 @@ again:
case IB_WR_ATOMIC_CMP_AND_SWP:
case IB_WR_ATOMIC_FETCH_AND_ADD:
- if (unlikely(!ipath_rkey_ok(dev, &qp->r_sge, sizeof(u64),
+ if (unlikely(!ipath_rkey_ok(qp, &qp->r_sge, sizeof(u64),
wqe->wr.wr.rdma.remote_addr,
wqe->wr.wr.rdma.rkey,
IB_ACCESS_REMOTE_ATOMIC)))
@@ -575,8 +574,7 @@ int ipath_post_ruc_send(struct ipath_qp *qp, struct ib_send_wr *wr)
}
if (wr->sg_list[i].length == 0)
continue;
- if (!ipath_lkey_ok(&to_idev(qp->ibqp.device)->lk_table,
- &wqe->sg_list[j], &wr->sg_list[i],
+ if (!ipath_lkey_ok(qp, &wqe->sg_list[j], &wr->sg_list[i],
acc)) {
spin_unlock_irqrestore(&qp->s_lock, flags);
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/ipath/ipath_srq.c b/drivers/infiniband/hw/ipath/ipath_srq.c
index 941e866d951..94033503400 100644
--- a/drivers/infiniband/hw/ipath/ipath_srq.c
+++ b/drivers/infiniband/hw/ipath/ipath_srq.c
@@ -104,11 +104,6 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
u32 sz;
struct ib_srq *ret;
- if (dev->n_srqs_allocated == ib_ipath_max_srqs) {
- ret = ERR_PTR(-ENOMEM);
- goto done;
- }
-
if (srq_init_attr->attr.max_wr == 0) {
ret = ERR_PTR(-EINVAL);
goto done;
@@ -180,10 +175,17 @@ struct ib_srq *ipath_create_srq(struct ib_pd *ibpd,
spin_lock_init(&srq->rq.lock);
srq->rq.wq->head = 0;
srq->rq.wq->tail = 0;
- srq->rq.max_sge = srq_init_attr->attr.max_sge;
srq->limit = srq_init_attr->attr.srq_limit;
- dev->n_srqs_allocated++;
+ spin_lock(&dev->n_srqs_lock);
+ if (dev->n_srqs_allocated == ib_ipath_max_srqs) {
+ spin_unlock(&dev->n_srqs_lock);
+ ret = ERR_PTR(-ENOMEM);
+ goto bail_wq;
+ }
+
+ dev->n_srqs_allocated++;
+ spin_unlock(&dev->n_srqs_lock);
ret = &srq->ibsrq;
goto done;
@@ -351,8 +353,13 @@ int ipath_destroy_srq(struct ib_srq *ibsrq)
struct ipath_srq *srq = to_isrq(ibsrq);
struct ipath_ibdev *dev = to_idev(ibsrq->device);
+ spin_lock(&dev->n_srqs_lock);
dev->n_srqs_allocated--;
- vfree(srq->rq.wq);
+ spin_unlock(&dev->n_srqs_lock);
+ if (srq->ip)
+ kref_put(&srq->ip->ref, ipath_release_mmap_info);
+ else
+ vfree(srq->rq.wq);
kfree(srq);
return 0;
diff --git a/drivers/infiniband/hw/ipath/ipath_sysfs.c b/drivers/infiniband/hw/ipath/ipath_sysfs.c
index e299148c4b6..182de34f9f4 100644
--- a/drivers/infiniband/hw/ipath/ipath_sysfs.c
+++ b/drivers/infiniband/hw/ipath/ipath_sysfs.c
@@ -257,7 +257,7 @@ static ssize_t store_guid(struct device *dev,
struct ipath_devdata *dd = dev_get_drvdata(dev);
ssize_t ret;
unsigned short guid[8];
- __be64 nguid;
+ __be64 new_guid;
u8 *ng;
int i;
@@ -266,7 +266,7 @@ static ssize_t store_guid(struct device *dev,
&guid[4], &guid[5], &guid[6], &guid[7]) != 8)
goto invalid;
- ng = (u8 *) &nguid;
+ ng = (u8 *) &new_guid;
for (i = 0; i < 8; i++) {
if (guid[i] > 0xff)
@@ -274,7 +274,10 @@ static ssize_t store_guid(struct device *dev,
ng[i] = guid[i];
}
- dd->ipath_guid = nguid;
+ if (new_guid == 0)
+ goto invalid;
+
+ dd->ipath_guid = new_guid;
dd->ipath_nguid = 1;
ret = strlen(buf);
@@ -297,6 +300,16 @@ static ssize_t show_nguid(struct device *dev,
return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_nguid);
}
+static ssize_t show_nports(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct ipath_devdata *dd = dev_get_drvdata(dev);
+
+ /* Return the number of user ports available. */
+ return scnprintf(buf, PAGE_SIZE, "%u\n", dd->ipath_cfgports - 1);
+}
+
static ssize_t show_serial(struct device *dev,
struct device_attribute *attr,
char *buf)
@@ -608,6 +621,7 @@ static DEVICE_ATTR(mlid, S_IWUSR | S_IRUGO, show_mlid, store_mlid);
static DEVICE_ATTR(mtu, S_IWUSR | S_IRUGO, show_mtu, store_mtu);
static DEVICE_ATTR(enabled, S_IWUSR | S_IRUGO, show_enabled, store_enabled);
static DEVICE_ATTR(nguid, S_IRUGO, show_nguid, NULL);
+static DEVICE_ATTR(nports, S_IRUGO, show_nports, NULL);
static DEVICE_ATTR(reset, S_IWUSR, NULL, store_reset);
static DEVICE_ATTR(serial, S_IRUGO, show_serial, NULL);
static DEVICE_ATTR(status, S_IRUGO, show_status, NULL);
@@ -623,6 +637,7 @@ static struct attribute *dev_attributes[] = {
&dev_attr_mlid.attr,
&dev_attr_mtu.attr,
&dev_attr_nguid.attr,
+ &dev_attr_nports.attr,
&dev_attr_serial.attr,
&dev_attr_status.attr,
&dev_attr_status_str.attr,
diff --git a/drivers/infiniband/hw/ipath/ipath_uc.c b/drivers/infiniband/hw/ipath/ipath_uc.c
index 0fd3cded16b..e636cfd67a8 100644
--- a/drivers/infiniband/hw/ipath/ipath_uc.c
+++ b/drivers/infiniband/hw/ipath/ipath_uc.c
@@ -246,6 +246,10 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
struct ib_reth *reth;
int header_in_data;
+ /* Validate the SLID. See Ch. 9.6.1.5 */
+ if (unlikely(be16_to_cpu(hdr->lrh[3]) != qp->remote_ah_attr.dlid))
+ goto done;
+
/* Check for GRH */
if (!has_grh) {
ohdr = &hdr->u.oth;
@@ -440,7 +444,7 @@ void ipath_uc_rcv(struct ipath_ibdev *dev, struct ipath_ib_header *hdr,
int ok;
/* Check rkey */
- ok = ipath_rkey_ok(dev, &qp->r_sge, qp->r_len,
+ ok = ipath_rkey_ok(qp, &qp->r_sge, qp->r_len,
vaddr, rkey,
IB_ACCESS_REMOTE_WRITE);
if (unlikely(!ok)) {
diff --git a/drivers/infiniband/hw/ipath/ipath_ud.c b/drivers/infiniband/hw/ipath/ipath_ud.c
index 6991d1d74e3..49f1102af8b 100644
--- a/drivers/infiniband/hw/ipath/ipath_ud.c
+++ b/drivers/infiniband/hw/ipath/ipath_ud.c
@@ -39,7 +39,6 @@
static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
u32 *lengthp, struct ipath_sge_state *ss)
{
- struct ipath_ibdev *dev = to_idev(qp->ibqp.device);
int user = to_ipd(qp->ibqp.pd)->user;
int i, j, ret;
struct ib_wc wc;
@@ -50,8 +49,7 @@ static int init_sge(struct ipath_qp *qp, struct ipath_rwqe *wqe,
continue;
/* Check LKEY */
if ((user && wqe->sg_list[i].lkey == 0) ||
- !ipath_lkey_ok(&dev->lk_table,
- j ? &ss->sg_list[j - 1] : &ss->sge,
+ !ipath_lkey_ok(qp, j ? &ss->sg_list[j - 1] : &ss->sge,
&wqe->sg_list[i], IB_ACCESS_LOCAL_WRITE))
goto bad_lkey;
*lengthp += wqe->sg_list[i].length;
@@ -343,7 +341,7 @@ int ipath_post_ud_send(struct ipath_qp *qp, struct ib_send_wr *wr)
if (wr->sg_list[i].length == 0)
continue;
- if (!ipath_lkey_ok(&dev->lk_table, ss.num_sge ?
+ if (!ipath_lkey_ok(qp, ss.num_sge ?
sg_list + ss.num_sge - 1 : &ss.sge,
&wr->sg_list[i], 0)) {
ret = -EINVAL;
diff --git a/drivers/infiniband/hw/ipath/ipath_user_pages.c b/drivers/infiniband/hw/ipath/ipath_user_pages.c
index e32fca9faf8..413754b1d8a 100644
--- a/drivers/infiniband/hw/ipath/ipath_user_pages.c
+++ b/drivers/infiniband/hw/ipath/ipath_user_pages.c
@@ -90,6 +90,62 @@ bail:
}
/**
+ * ipath_map_page - a safety wrapper around pci_map_page()
+ *
+ * A dma_addr of all 0's is interpreted by the chip as "disabled".
+ * Unfortunately, it can also be a valid dma_addr returned on some
+ * architectures.
+ *
+ * The powerpc iommu assigns dma_addrs in ascending order, so we don't
+ * have to bother with retries or mapping a dummy page to insure we
+ * don't just get the same mapping again.
+ *
+ * I'm sure we won't be so lucky with other iommu's, so FIXME.
+ */
+dma_addr_t ipath_map_page(struct pci_dev *hwdev, struct page *page,
+ unsigned long offset, size_t size, int direction)
+{
+ dma_addr_t phys;
+
+ phys = pci_map_page(hwdev, page, offset, size, direction);
+
+ if (phys == 0) {
+ pci_unmap_page(hwdev, phys, size, direction);
+ phys = pci_map_page(hwdev, page, offset, size, direction);
+ /*
+ * FIXME: If we get 0 again, we should keep this page,
+ * map another, then free the 0 page.
+ */
+ }
+
+ return phys;
+}
+
+/**
+ * ipath_map_single - a safety wrapper around pci_map_single()
+ *
+ * Same idea as ipath_map_page().
+ */
+dma_addr_t ipath_map_single(struct pci_dev *hwdev, void *ptr, size_t size,
+ int direction)
+{
+ dma_addr_t phys;
+
+ phys = pci_map_single(hwdev, ptr, size, direction);
+
+ if (phys == 0) {
+ pci_unmap_single(hwdev, phys, size, direction);
+ phys = pci_map_single(hwdev, ptr, size, direction);
+ /*
+ * FIXME: If we get 0 again, we should keep this page,
+ * map another, then free the 0 page.
+ */
+ }
+
+ return phys;
+}
+
+/**
* ipath_get_user_pages - lock user pages into memory
* @start_page: the start page
* @num_pages: the number of pages
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.c b/drivers/infiniband/hw/ipath/ipath_verbs.c
index b8381c5e72b..a5456108dba 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.c
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.c
@@ -898,7 +898,8 @@ int ipath_get_counters(struct ipath_devdata *dd,
ipath_snap_cntr(dd, dd->ipath_cregs->cr_erricrccnt) +
ipath_snap_cntr(dd, dd->ipath_cregs->cr_errvcrccnt) +
ipath_snap_cntr(dd, dd->ipath_cregs->cr_errlpcrccnt) +
- ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt);
+ ipath_snap_cntr(dd, dd->ipath_cregs->cr_badformatcnt) +
+ dd->ipath_rxfc_unsupvl_errs;
cntrs->port_rcv_remphys_errors =
ipath_snap_cntr(dd, dd->ipath_cregs->cr_rcvebpcnt);
cntrs->port_xmit_discards =
@@ -911,8 +912,10 @@ int ipath_get_counters(struct ipath_devdata *dd,
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktsendcnt);
cntrs->port_rcv_packets =
ipath_snap_cntr(dd, dd->ipath_cregs->cr_pktrcvcnt);
- cntrs->local_link_integrity_errors = dd->ipath_lli_errors;
- cntrs->excessive_buffer_overrun_errors = 0; /* XXX */
+ cntrs->local_link_integrity_errors =
+ (dd->ipath_flags & IPATH_GPIO_ERRINTRS) ?
+ dd->ipath_lli_errs : dd->ipath_lli_errors;
+ cntrs->excessive_buffer_overrun_errors = dd->ipath_overrun_thresh_errs;
ret = 0;
@@ -1199,6 +1202,7 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd,
struct ipath_ah *ah;
struct ib_ah *ret;
struct ipath_ibdev *dev = to_idev(pd->device);
+ unsigned long flags;
/* A multicast address requires a GRH (see ch. 8.4.1). */
if (ah_attr->dlid >= IPATH_MULTICAST_LID_BASE &&
@@ -1225,16 +1229,16 @@ static struct ib_ah *ipath_create_ah(struct ib_pd *pd,
goto bail;
}
- spin_lock(&dev->n_ahs_lock);
+ spin_lock_irqsave(&dev->n_ahs_lock, flags);
if (dev->n_ahs_allocated == ib_ipath_max_ahs) {
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
ret = ERR_PTR(-ENOMEM);
goto bail;
}
dev->n_ahs_allocated++;
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
/* ib_create_ah() will initialize ah->ibah. */
ah->attr = *ah_attr;
@@ -1255,10 +1259,11 @@ static int ipath_destroy_ah(struct ib_ah *ibah)
{
struct ipath_ibdev *dev = to_idev(ibah->device);
struct ipath_ah *ah = to_iah(ibah);
+ unsigned long flags;
- spin_lock(&dev->n_ahs_lock);
+ spin_lock_irqsave(&dev->n_ahs_lock, flags);
dev->n_ahs_allocated--;
- spin_unlock(&dev->n_ahs_lock);
+ spin_unlock_irqrestore(&dev->n_ahs_lock, flags);
kfree(ah);
@@ -1380,11 +1385,13 @@ static int enable_timer(struct ipath_devdata *dd)
* processing.
*/
if (dd->ipath_flags & IPATH_GPIO_INTR) {
+ u64 val;
ipath_write_kreg(dd, dd->ipath_kregs->kr_debugportselect,
0x2074076542310ULL);
/* Enable GPIO bit 2 interrupt */
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask,
- (u64) (1 << 2));
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val |= (u64) (1 << IPATH_GPIO_PORT0_BIT);
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
}
init_timer(&dd->verbs_timer);
@@ -1399,8 +1406,17 @@ static int enable_timer(struct ipath_devdata *dd)
static int disable_timer(struct ipath_devdata *dd)
{
/* Disable GPIO bit 2 interrupt */
- if (dd->ipath_flags & IPATH_GPIO_INTR)
- ipath_write_kreg(dd, dd->ipath_kregs->kr_gpio_mask, 0);
+ if (dd->ipath_flags & IPATH_GPIO_INTR) {
+ u64 val;
+ /* Disable GPIO bit 2 interrupt */
+ val = ipath_read_kreg64(dd, dd->ipath_kregs->kr_gpio_mask);
+ val &= ~((u64) (1 << IPATH_GPIO_PORT0_BIT));
+ ipath_write_kreg( dd, dd->ipath_kregs->kr_gpio_mask, val);
+ /*
+ * We might want to undo changes to debugportselect,
+ * but how?
+ */
+ }
del_timer_sync(&dd->verbs_timer);
@@ -1585,7 +1601,7 @@ int ipath_register_ib_device(struct ipath_devdata *dd)
dev->mmap = ipath_mmap;
snprintf(dev->node_desc, sizeof(dev->node_desc),
- IPATH_IDSTR " %s", system_utsname.nodename);
+ IPATH_IDSTR " %s", init_utsname()->nodename);
ret = ib_register_device(dev);
if (ret)
@@ -1683,6 +1699,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf)
"RC OTH NAKs %d\n"
"RC timeouts %d\n"
"RC RDMA dup %d\n"
+ "RC stalls %d\n"
"piobuf wait %d\n"
"no piobuf %d\n"
"PKT drops %d\n"
@@ -1690,7 +1707,7 @@ static ssize_t show_stats(struct class_device *cdev, char *buf)
dev->n_rc_resends, dev->n_rc_qacks, dev->n_rc_acks,
dev->n_seq_naks, dev->n_rdma_seq, dev->n_rnr_naks,
dev->n_other_naks, dev->n_timeouts,
- dev->n_rdma_dup_busy, dev->n_piowait,
+ dev->n_rdma_dup_busy, dev->n_rc_stalls, dev->n_piowait,
dev->n_no_piobuf, dev->n_pkt_drops, dev->n_wqe_errs);
for (i = 0; i < ARRAY_SIZE(dev->opstats); i++) {
const struct ipath_opcode_stats *si = &dev->opstats[i];
diff --git a/drivers/infiniband/hw/ipath/ipath_verbs.h b/drivers/infiniband/hw/ipath/ipath_verbs.h
index 09bbb3f9a21..8039f6e5f0c 100644
--- a/drivers/infiniband/hw/ipath/ipath_verbs.h
+++ b/drivers/infiniband/hw/ipath/ipath_verbs.h
@@ -220,6 +220,7 @@ struct ipath_segarray {
};
struct ipath_mregion {
+ struct ib_pd *pd; /* shares refcnt of ibmr.pd */
u64 user_base; /* User's address for this region */
u64 iova; /* IB start address of this region */
size_t length;
@@ -364,12 +365,14 @@ struct ipath_qp {
u8 r_min_rnr_timer; /* retry timeout value for RNR NAKs */
u8 r_reuse_sge; /* for UC receive errors */
u8 r_sge_inx; /* current index into sg_list */
+ u8 r_wrid_valid; /* r_wrid set but CQ entry not yet made */
u8 qp_access_flags;
u8 s_max_sge; /* size of s_wq->sg_list */
u8 s_retry_cnt; /* number of times to retry */
u8 s_rnr_retry_cnt;
u8 s_retry; /* requester retry counter */
u8 s_rnr_retry; /* requester RNR retry counter */
+ u8 s_wait_credit; /* limit number of unacked packets sent */
u8 s_pkey_index; /* PKEY index to use */
u8 timeout; /* Timeout for this QP */
enum ib_mtu path_mtu;
@@ -393,6 +396,8 @@ struct ipath_qp {
#define IPATH_S_BUSY 0
#define IPATH_S_SIGNAL_REQ_WR 1
+#define IPATH_PSN_CREDIT 2048
+
/*
* Since struct ipath_swqe is not a fixed size, we can't simply index into
* struct ipath_qp.s_wq. This function does the array index computation.
@@ -521,6 +526,7 @@ struct ipath_ibdev {
u32 n_rnr_naks;
u32 n_other_naks;
u32 n_timeouts;
+ u32 n_rc_stalls;
u32 n_pkt_drops;
u32 n_vl15_dropped;
u32 n_wqe_errs;
@@ -634,6 +640,8 @@ struct ib_qp *ipath_create_qp(struct ib_pd *ibpd,
int ipath_destroy_qp(struct ib_qp *ibqp);
+void ipath_error_qp(struct ipath_qp *qp, enum ib_wc_status err);
+
int ipath_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr,
int attr_mask, struct ib_udata *udata);
@@ -653,12 +661,6 @@ int ipath_verbs_send(struct ipath_devdata *dd, u32 hdrwords,
void ipath_cq_enter(struct ipath_cq *cq, struct ib_wc *entry, int sig);
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
- u32 len, u64 vaddr, u32 rkey, int acc);
-
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
- struct ib_sge *sge, int acc);
-
void ipath_copy_sge(struct ipath_sge_state *ss, void *data, u32 length);
void ipath_skip_sge(struct ipath_sge_state *ss, u32 length);
@@ -683,10 +685,10 @@ int ipath_alloc_lkey(struct ipath_lkey_table *rkt,
void ipath_free_lkey(struct ipath_lkey_table *rkt, u32 lkey);
-int ipath_lkey_ok(struct ipath_lkey_table *rkt, struct ipath_sge *isge,
+int ipath_lkey_ok(struct ipath_qp *qp, struct ipath_sge *isge,
struct ib_sge *sge, int acc);
-int ipath_rkey_ok(struct ipath_ibdev *dev, struct ipath_sge_state *ss,
+int ipath_rkey_ok(struct ipath_qp *qp, struct ipath_sge_state *ss,
u32 len, u64 vaddr, u32 rkey, int acc);
int ipath_post_srq_receive(struct ib_srq *ibsrq, struct ib_recv_wr *wr,
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
index 036fde662aa..0095bb70f34 100644
--- a/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
+++ b/drivers/infiniband/hw/ipath/ipath_wc_ppc64.c
@@ -38,13 +38,23 @@
#include "ipath_kernel.h"
/**
- * ipath_unordered_wc - indicate whether write combining is ordered
+ * ipath_enable_wc - enable write combining for MMIO writes to the device
+ * @dd: infinipath device
*
- * PowerPC systems (at least those in the 970 processor family)
- * write partially filled store buffers in address order, but will write
- * completely filled store buffers in "random" order, and therefore must
- * have serialization for correctness with current InfiniPath chips.
+ * Nothing to do on PowerPC, so just return without error.
+ */
+int ipath_enable_wc(struct ipath_devdata *dd)
+{
+ return 0;
+}
+
+/**
+ * ipath_unordered_wc - indicate whether write combining is unordered
*
+ * Because our performance depends on our ability to do write
+ * combining mmio writes in the most efficient way, we need to
+ * know if we are on a processor that may reorder stores when
+ * write combining.
*/
int ipath_unordered_wc(void)
{
diff --git a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
index f8f9e2e8cbd..04696e62da8 100644
--- a/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
+++ b/drivers/infiniband/hw/ipath/ipath_wc_x86_64.c
@@ -123,6 +123,8 @@ int ipath_enable_wc(struct ipath_devdata *dd)
ipath_cdbg(VERBOSE, "Set mtrr for chip to WC, "
"cookie is %d\n", cookie);
dd->ipath_wc_cookie = cookie;
+ dd->ipath_wc_base = (unsigned long) pioaddr;
+ dd->ipath_wc_len = (unsigned long) piolen;
}
}
@@ -136,9 +138,16 @@ int ipath_enable_wc(struct ipath_devdata *dd)
void ipath_disable_wc(struct ipath_devdata *dd)
{
if (dd->ipath_wc_cookie) {
+ int r;
ipath_cdbg(VERBOSE, "undoing WCCOMB on pio buffers\n");
- mtrr_del(dd->ipath_wc_cookie, 0, 0);
- dd->ipath_wc_cookie = 0;
+ r = mtrr_del(dd->ipath_wc_cookie, dd->ipath_wc_base,
+ dd->ipath_wc_len);
+ if (r < 0)
+ dev_info(&dd->pcidev->dev,
+ "mtrr_del(%lx, %lx, %lx) failed: %d\n",
+ dd->ipath_wc_cookie, dd->ipath_wc_base,
+ dd->ipath_wc_len, r);
+ dd->ipath_wc_cookie = 0; /* even on failure */
}
}
diff --git a/drivers/infiniband/ulp/ipoib/ipoib_fs.c b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
index 5dde380e8db..f1cb83688b3 100644
--- a/drivers/infiniband/ulp/ipoib/ipoib_fs.c
+++ b/drivers/infiniband/ulp/ipoib/ipoib_fs.c
@@ -141,7 +141,7 @@ static int ipoib_mcg_open(struct inode *inode, struct file *file)
return ret;
seq = file->private_data;
- seq->private = inode->u.generic_ip;
+ seq->private = inode->i_private;
return 0;
}
@@ -247,7 +247,7 @@ static int ipoib_path_open(struct inode *inode, struct file *file)
return ret;
seq = file->private_data;
- seq->private = inode->u.generic_ip;
+ seq->private = inode->i_private;
return 0;
}
diff --git a/drivers/infiniband/ulp/iser/Kconfig b/drivers/infiniband/ulp/iser/Kconfig
index 365a1b5f19e..aecbb9083f0 100644
--- a/drivers/infiniband/ulp/iser/Kconfig
+++ b/drivers/infiniband/ulp/iser/Kconfig
@@ -1,11 +1,12 @@
config INFINIBAND_ISER
- tristate "ISCSI RDMA Protocol"
+ tristate "iSCSI Extensions for RDMA (iSER)"
depends on INFINIBAND && SCSI && INET
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.
+ Support for the iSCSI Extensions for RDMA (iSER) Protocol
+ over InfiniBand. This allows you to access storage devices
+ that speak iSCSI over iSER over InfiniBand.
- The ISER protocol is defined by IETF.
- See <http://www.ietf.org/>.
+ The iSER protocol is defined by IETF.
+ See <http://www.ietf.org/internet-drafts/draft-ietf-ips-iser-05.txt>
+ and <http://www.infinibandta.org/members/spec/iser_annex_060418.pdf>
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c
index 2a14fe2e322..eb6f98d8228 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.c
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.c
@@ -317,6 +317,8 @@ iscsi_iser_conn_destroy(struct iscsi_cls_conn *cls_conn)
struct iscsi_iser_conn *iser_conn = conn->dd_data;
iscsi_conn_teardown(cls_conn);
+ if (iser_conn->ib_conn)
+ iser_conn->ib_conn->iser_conn = NULL;
kfree(iser_conn);
}
diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.h b/drivers/infiniband/ulp/iser/iscsi_iser.h
index 2cf9ae0def1..9c53916f28c 100644
--- a/drivers/infiniband/ulp/iser/iscsi_iser.h
+++ b/drivers/infiniband/ulp/iser/iscsi_iser.h
@@ -192,7 +192,7 @@ struct iser_regd_buf {
struct iser_dto {
struct iscsi_iser_cmd_task *ctask;
- struct iscsi_iser_conn *conn;
+ struct iser_conn *ib_conn;
int notify_enable;
/* vector of registered buffers */
@@ -355,4 +355,11 @@ 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);
+
+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);
+
+void iser_dma_unmap_task_data(struct iscsi_iser_cmd_task *iser_ctask);
#endif
diff --git a/drivers/infiniband/ulp/iser/iser_initiator.c b/drivers/infiniband/ulp/iser/iser_initiator.c
index ccf56f6f723..9b3d79c796c 100644
--- a/drivers/infiniband/ulp/iser/iser_initiator.c
+++ b/drivers/infiniband/ulp/iser/iser_initiator.c
@@ -66,42 +66,6 @@ static void iser_dto_add_regd_buff(struct iser_dto *dto,
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
@@ -249,7 +213,7 @@ static int iser_post_receive_control(struct iscsi_conn *conn)
}
recv_dto = &rx_desc->dto;
- recv_dto->conn = iser_conn;
+ recv_dto->ib_conn = iser_conn->ib_conn;
recv_dto->regd_vector_len = 0;
regd_hdr = &rx_desc->hdr_regd_buf;
@@ -296,7 +260,7 @@ static void iser_create_send_desc(struct iscsi_iser_conn *iser_conn,
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->ib_conn = iser_conn->ib_conn;
send_dto->notify_enable = 1;
send_dto->regd_vector_len = 0;
@@ -588,7 +552,7 @@ 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_iser_conn *conn = dto->ib_conn->iser_conn;
struct iscsi_session *session = conn->iscsi_conn->session;
struct iscsi_cmd_task *ctask;
struct iscsi_iser_cmd_task *iser_ctask;
@@ -641,7 +605,8 @@ void iser_rcv_completion(struct iser_desc *rx_desc,
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 iser_conn *ib_conn = dto->ib_conn;
+ struct iscsi_iser_conn *iser_conn = ib_conn->iser_conn;
struct iscsi_conn *conn = iser_conn->iscsi_conn;
struct iscsi_mgmt_task *mtask;
@@ -652,7 +617,7 @@ void iser_snd_completion(struct iser_desc *tx_desc)
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);
+ atomic_dec(&ib_conn->post_send_buf_count);
write_lock(conn->recv_lock);
if (conn->suspend_tx) {
@@ -698,14 +663,19 @@ void iser_ctask_rdma_init(struct iscsi_iser_cmd_task *iser_ctask)
void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
{
int deferred;
+ int is_rdma_aligned = 1;
/* 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)
+ if (iser_ctask->data_copy[ISER_DIR_IN].copy_buf != NULL) {
+ is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_IN);
- if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL)
+ }
+ if (iser_ctask->data_copy[ISER_DIR_OUT].copy_buf != NULL) {
+ is_rdma_aligned = 0;
iser_finalize_rdma_unaligned_sg(iser_ctask, ISER_DIR_OUT);
+ }
if (iser_ctask->dir[ISER_DIR_IN]) {
deferred = iser_regd_buff_release
@@ -725,7 +695,9 @@ void iser_ctask_rdma_finalize(struct iscsi_iser_cmd_task *iser_ctask)
}
}
- iser_dma_unmap_task_data(iser_ctask);
+ /* if the data was unaligned, it was already unmapped and then copied */
+ if (is_rdma_aligned)
+ iser_dma_unmap_task_data(iser_ctask);
}
void iser_dto_buffs_release(struct iser_dto *dto)
diff --git a/drivers/infiniband/ulp/iser/iser_memory.c b/drivers/infiniband/ulp/iser/iser_memory.c
index d0b03f42658..0606744c3f8 100644
--- a/drivers/infiniband/ulp/iser/iser_memory.c
+++ b/drivers/infiniband/ulp/iser/iser_memory.c
@@ -369,6 +369,44 @@ static void iser_page_vec_build(struct iser_data_buf *data,
}
}
+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;
+}
+
+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);
+ }
+}
+
/**
* iser_reg_rdma_mem - Registers memory intended for RDMA,
* obtaining rkey and va
@@ -394,6 +432,10 @@ int iser_reg_rdma_mem(struct iscsi_iser_cmd_task *iser_ctask,
iser_err("rdma alignment violation %d/%d aligned\n",
aligned_len, mem->size);
iser_data_buf_dump(mem);
+
+ /* unmap the command data before accessing it */
+ iser_dma_unmap_task_data(iser_ctask);
+
/* 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)
diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c
index ecdca7fc1e4..18a00003499 100644
--- a/drivers/infiniband/ulp/iser/iser_verbs.c
+++ b/drivers/infiniband/ulp/iser/iser_verbs.c
@@ -571,6 +571,8 @@ void iser_conn_release(struct iser_conn *ib_conn)
/* on EVENT_ADDR_ERROR there's no device yet for this conn */
if (device != NULL)
iser_device_try_release(device);
+ if (ib_conn->iser_conn)
+ ib_conn->iser_conn->ib_conn = NULL;
kfree(ib_conn);
}
@@ -694,7 +696,7 @@ int iser_post_recv(struct iser_desc *rx_desc)
struct iser_dto *recv_dto = &rx_desc->dto;
/* Retrieve conn */
- ib_conn = recv_dto->conn->ib_conn;
+ ib_conn = recv_dto->ib_conn;
iser_dto_to_iov(recv_dto, iov, 2);
@@ -727,7 +729,7 @@ int iser_post_send(struct iser_desc *tx_desc)
struct iser_conn *ib_conn;
struct iser_dto *dto = &tx_desc->dto;
- ib_conn = dto->conn->ib_conn;
+ ib_conn = dto->ib_conn;
iser_dto_to_iov(dto, iov, MAX_REGD_BUF_VECTOR_LEN);
@@ -774,7 +776,7 @@ static void iser_comp_error_worker(void *data)
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;
+ struct iser_conn *ib_conn = dto->ib_conn;
iser_dto_buffs_release(dto);
diff --git a/drivers/input/Kconfig b/drivers/input/Kconfig
index 58223b5d842..96232313b1b 100644
--- a/drivers/input/Kconfig
+++ b/drivers/input/Kconfig
@@ -24,6 +24,20 @@ config INPUT
if INPUT
+config INPUT_FF_MEMLESS
+ tristate "Support for memoryless force-feedback devices"
+ default n
+ ---help---
+ Say Y here if you have memoryless force-feedback input device
+ such as Logitech WingMan Force 3D, ThrustMaster FireStorm Dual
+ Power 2, or similar. You will also need to enable hardware-specific
+ driver.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ff-memless.
+
comment "Userland interfaces"
config INPUT_MOUSEDEV
diff --git a/drivers/input/Makefile b/drivers/input/Makefile
index 1a6ff4982f3..a005b1df5f1 100644
--- a/drivers/input/Makefile
+++ b/drivers/input/Makefile
@@ -4,7 +4,11 @@
# Each configuration option enables a list of files.
-obj-$(CONFIG_INPUT) += input.o
+obj-$(CONFIG_INPUT) += input-core.o
+input-core-objs := input.o ff-core.o
+
+obj-$(CONFIG_INPUT_FF_MEMLESS) += ff-memless.o
+
obj-$(CONFIG_INPUT_MOUSEDEV) += mousedev.o
obj-$(CONFIG_INPUT_JOYDEV) += joydev.o
obj-$(CONFIG_INPUT_EVDEV) += evdev.o
diff --git a/drivers/input/evbug.c b/drivers/input/evbug.c
index 07358fb51b8..5a9653c3128 100644
--- a/drivers/input/evbug.c
+++ b/drivers/input/evbug.c
@@ -42,10 +42,12 @@ static char evbug_name[] = "evbug";
static void evbug_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
- printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n", handle->dev->phys, type, code, value);
+ printk(KERN_DEBUG "evbug.c: Event. Dev: %s, Type: %d, Code: %d, Value: %d\n",
+ handle->dev->phys, type, code, value);
}
-static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *evbug_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct input_handle *handle;
@@ -72,7 +74,7 @@ static void evbug_disconnect(struct input_handle *handle)
kfree(handle);
}
-static struct input_device_id evbug_ids[] = {
+static const struct input_device_id evbug_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
@@ -89,8 +91,7 @@ static struct input_handler evbug_handler = {
static int __init evbug_init(void)
{
- input_register_handler(&evbug_handler);
- return 0;
+ return input_register_handler(&evbug_handler);
}
static void __exit evbug_exit(void)
diff --git a/drivers/input/evdev.c b/drivers/input/evdev.c
index 4bf48188cc9..6439f378f6c 100644
--- a/drivers/input/evdev.c
+++ b/drivers/input/evdev.c
@@ -391,8 +391,10 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
struct evdev *evdev = list->evdev;
struct input_dev *dev = evdev->handle.dev;
struct input_absinfo abs;
+ struct ff_effect effect;
int __user *ip = (int __user *)p;
int i, t, u, v;
+ int error;
if (!evdev->exist)
return -ENODEV;
@@ -460,27 +462,22 @@ static long evdev_ioctl_handler(struct file *file, unsigned int cmd,
return 0;
case EVIOCSFF:
- if (dev->upload_effect) {
- struct ff_effect effect;
- int err;
-
- if (copy_from_user(&effect, p, sizeof(effect)))
- return -EFAULT;
- err = dev->upload_effect(dev, &effect);
- if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
- return -EFAULT;
- return err;
- } else
- return -ENOSYS;
+ if (copy_from_user(&effect, p, sizeof(effect)))
+ return -EFAULT;
- case EVIOCRMFF:
- if (!dev->erase_effect)
- return -ENOSYS;
+ error = input_ff_upload(dev, &effect, file);
- return dev->erase_effect(dev, (int)(unsigned long) p);
+ if (put_user(effect.id, &(((struct ff_effect __user *)p)->id)))
+ return -EFAULT;
+
+ return error;
+
+ case EVIOCRMFF:
+ return input_ff_erase(dev, (int)(unsigned long) p, file);
case EVIOCGEFFECTS:
- if (put_user(dev->ff_effects_max, ip))
+ i = test_bit(EV_FF, dev->evbit) ? dev->ff->max_effects : 0;
+ if (put_user(i, ip))
return -EFAULT;
return 0;
@@ -604,7 +601,7 @@ static long evdev_ioctl_compat(struct file *file, unsigned int cmd, unsigned lon
}
#endif
-static struct file_operations evdev_fops = {
+static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
@@ -619,7 +616,8 @@ static struct file_operations evdev_fops = {
.flush = evdev_flush
};
-static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *evdev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
@@ -669,6 +667,7 @@ static void evdev_disconnect(struct input_handle *handle)
evdev->exist = 0;
if (evdev->open) {
+ input_flush_device(handle, NULL);
input_close_device(handle);
wake_up_interruptible(&evdev->wait);
list_for_each_entry(list, &evdev->list, node)
@@ -677,7 +676,7 @@ static void evdev_disconnect(struct input_handle *handle)
evdev_free(evdev);
}
-static struct input_device_id evdev_ids[] = {
+static const struct input_device_id evdev_ids[] = {
{ .driver_info = 1 }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
@@ -696,8 +695,7 @@ static struct input_handler evdev_handler = {
static int __init evdev_init(void)
{
- input_register_handler(&evdev_handler);
- return 0;
+ return input_register_handler(&evdev_handler);
}
static void __exit evdev_exit(void)
diff --git a/drivers/input/ff-core.c b/drivers/input/ff-core.c
new file mode 100644
index 00000000000..35656cadc91
--- /dev/null
+++ b/drivers/input/ff-core.c
@@ -0,0 +1,367 @@
+/*
+ * Force feedback support for Linux input subsystem
+ *
+ * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("ff-core: " format "\n", ## arg)
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+/*
+ * Check that the effect_id is a valid effect and whether the user
+ * is the owner
+ */
+static int check_effect_access(struct ff_device *ff, int effect_id,
+ struct file *file)
+{
+ if (effect_id < 0 || effect_id >= ff->max_effects ||
+ !ff->effect_owners[effect_id])
+ return -EINVAL;
+
+ if (file && ff->effect_owners[effect_id] != file)
+ return -EACCES;
+
+ return 0;
+}
+
+/*
+ * Checks whether 2 effects can be combined together
+ */
+static inline int check_effects_compatible(struct ff_effect *e1,
+ struct ff_effect *e2)
+{
+ return e1->type == e2->type &&
+ (e1->type != FF_PERIODIC ||
+ e1->u.periodic.waveform == e2->u.periodic.waveform);
+}
+
+/*
+ * Convert an effect into compatible one
+ */
+static int compat_effect(struct ff_device *ff, struct ff_effect *effect)
+{
+ int magnitude;
+
+ switch (effect->type) {
+ case FF_RUMBLE:
+ if (!test_bit(FF_PERIODIC, ff->ffbit))
+ return -EINVAL;
+
+ /*
+ * calculate manginude of sine wave as average of rumble's
+ * 2/3 of strong magnitude and 1/3 of weak magnitude
+ */
+ magnitude = effect->u.rumble.strong_magnitude / 3 +
+ effect->u.rumble.weak_magnitude / 6;
+
+ effect->type = FF_PERIODIC;
+ effect->u.periodic.waveform = FF_SINE;
+ effect->u.periodic.period = 50;
+ effect->u.periodic.magnitude = max(magnitude, 0x7fff);
+ effect->u.periodic.offset = 0;
+ effect->u.periodic.phase = 0;
+ effect->u.periodic.envelope.attack_length = 0;
+ effect->u.periodic.envelope.attack_level = 0;
+ effect->u.periodic.envelope.fade_length = 0;
+ effect->u.periodic.envelope.fade_level = 0;
+
+ return 0;
+
+ default:
+ /* Let driver handle conversion */
+ return 0;
+ }
+}
+
+/**
+ * input_ff_upload() - upload effect into force-feedback device
+ * @dev: input device
+ * @effect: effect to be uploaded
+ * @file: owner of the effect
+ */
+int input_ff_upload(struct input_dev *dev, struct ff_effect *effect,
+ struct file *file)
+{
+ struct ff_device *ff = dev->ff;
+ struct ff_effect *old;
+ int ret = 0;
+ int id;
+
+ if (!test_bit(EV_FF, dev->evbit))
+ return -ENOSYS;
+
+ if (effect->type < FF_EFFECT_MIN || effect->type > FF_EFFECT_MAX ||
+ !test_bit(effect->type, dev->ffbit)) {
+ debug("invalid or not supported effect type in upload");
+ return -EINVAL;
+ }
+
+ if (effect->type == FF_PERIODIC &&
+ (effect->u.periodic.waveform < FF_WAVEFORM_MIN ||
+ effect->u.periodic.waveform > FF_WAVEFORM_MAX ||
+ !test_bit(effect->u.periodic.waveform, dev->ffbit))) {
+ debug("invalid or not supported wave form in upload");
+ return -EINVAL;
+ }
+
+ if (!test_bit(effect->type, ff->ffbit)) {
+ ret = compat_effect(ff, effect);
+ if (ret)
+ return ret;
+ }
+
+ mutex_lock(&ff->mutex);
+
+ if (effect->id == -1) {
+ for (id = 0; id < ff->max_effects; id++)
+ if (!ff->effect_owners[id])
+ break;
+
+ if (id >= ff->max_effects) {
+ ret = -ENOSPC;
+ goto out;
+ }
+
+ effect->id = id;
+ old = NULL;
+
+ } else {
+ id = effect->id;
+
+ ret = check_effect_access(ff, id, file);
+ if (ret)
+ goto out;
+
+ old = &ff->effects[id];
+
+ if (!check_effects_compatible(effect, old)) {
+ ret = -EINVAL;
+ goto out;
+ }
+ }
+
+ ret = ff->upload(dev, effect, old);
+ if (ret)
+ goto out;
+
+ ff->effects[id] = *effect;
+ ff->effect_owners[id] = file;
+
+ out:
+ mutex_unlock(&ff->mutex);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_upload);
+
+/*
+ * Erases the effect if the requester is also the effect owner. The mutex
+ * should already be locked before calling this function.
+ */
+static int erase_effect(struct input_dev *dev, int effect_id,
+ struct file *file)
+{
+ struct ff_device *ff = dev->ff;
+ int error;
+
+ error = check_effect_access(ff, effect_id, file);
+ if (error)
+ return error;
+
+ ff->playback(dev, effect_id, 0);
+
+ if (ff->erase) {
+ error = ff->erase(dev, effect_id);
+ if (error)
+ return error;
+ }
+
+ ff->effect_owners[effect_id] = NULL;
+
+ return 0;
+}
+
+/**
+ * input_ff_erase - erase an effect from device
+ * @dev: input device to erase effect from
+ * @effect_id: id of the ffect to be erased
+ * @file: purported owner of the request
+ *
+ * This function erases a force-feedback effect from specified device.
+ * The effect will only be erased if it was uploaded through the same
+ * file handle that is requesting erase.
+ */
+int input_ff_erase(struct input_dev *dev, int effect_id, struct file *file)
+{
+ struct ff_device *ff = dev->ff;
+ int ret;
+
+ if (!test_bit(EV_FF, dev->evbit))
+ return -ENOSYS;
+
+ mutex_lock(&ff->mutex);
+ ret = erase_effect(dev, effect_id, file);
+ mutex_unlock(&ff->mutex);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(input_ff_erase);
+
+/*
+ * flush_effects - erase all effects owned by a file handle
+ */
+static int flush_effects(struct input_dev *dev, struct file *file)
+{
+ struct ff_device *ff = dev->ff;
+ int i;
+
+ debug("flushing now");
+
+ mutex_lock(&ff->mutex);
+
+ for (i = 0; i < ff->max_effects; i++)
+ erase_effect(dev, i, file);
+
+ mutex_unlock(&ff->mutex);
+
+ return 0;
+}
+
+/**
+ * input_ff_event() - generic handler for force-feedback events
+ * @dev: input device to send the effect to
+ * @type: event type (anything but EV_FF is ignored)
+ * @code: event code
+ * @value: event value
+ */
+int input_ff_event(struct input_dev *dev, unsigned int type,
+ unsigned int code, int value)
+{
+ struct ff_device *ff = dev->ff;
+
+ if (type != EV_FF)
+ return 0;
+
+ mutex_lock(&ff->mutex);
+
+ switch (code) {
+ case FF_GAIN:
+ if (!test_bit(FF_GAIN, dev->ffbit) || value > 0xffff)
+ break;
+
+ ff->set_gain(dev, value);
+ break;
+
+ case FF_AUTOCENTER:
+ if (!test_bit(FF_AUTOCENTER, dev->ffbit) || value > 0xffff)
+ break;
+
+ ff->set_autocenter(dev, value);
+ break;
+
+ default:
+ ff->playback(dev, code, value);
+ break;
+ }
+
+ mutex_unlock(&ff->mutex);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_event);
+
+/**
+ * input_ff_create() - create force-feedback device
+ * @dev: input device supporting force-feedback
+ * @max_effects: maximum number of effects supported by the device
+ *
+ * This function allocates all necessary memory for a force feedback
+ * portion of an input device and installs all default handlers.
+ * @dev->ffbit should be already set up before calling this function.
+ * Once ff device is created you need to setup its upload, erase,
+ * playback and other handlers before registering input device
+ */
+int input_ff_create(struct input_dev *dev, int max_effects)
+{
+ struct ff_device *ff;
+ int i;
+
+ if (!max_effects) {
+ printk(KERN_ERR
+ "ff-core: cannot allocate device without any effects\n");
+ return -EINVAL;
+ }
+
+ ff = kzalloc(sizeof(struct ff_device) +
+ max_effects * sizeof(struct file *), GFP_KERNEL);
+ if (!ff)
+ return -ENOMEM;
+
+ ff->effects = kcalloc(max_effects, sizeof(struct ff_effect),
+ GFP_KERNEL);
+ if (!ff->effects) {
+ kfree(ff);
+ return -ENOMEM;
+ }
+
+ ff->max_effects = max_effects;
+ mutex_init(&ff->mutex);
+
+ dev->ff = ff;
+ dev->flush = flush_effects;
+ dev->event = input_ff_event;
+ set_bit(EV_FF, dev->evbit);
+
+ /* Copy "true" bits into ff device bitmap */
+ for (i = 0; i <= FF_MAX; i++)
+ if (test_bit(i, dev->ffbit))
+ set_bit(i, ff->ffbit);
+
+ /* we can emulate RUMBLE with periodic effects */
+ if (test_bit(FF_PERIODIC, ff->ffbit))
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create);
+
+/**
+ * input_ff_free() - frees force feedback portion of input device
+ * @dev: input device supporintg force feedback
+ *
+ * This function is only needed in error path as input core will
+ * automatically free force feedback structures when device is
+ * destroyed.
+ */
+void input_ff_destroy(struct input_dev *dev)
+{
+ clear_bit(EV_FF, dev->evbit);
+ if (dev->ff) {
+ if (dev->ff->destroy)
+ dev->ff->destroy(dev->ff);
+ kfree(dev->ff->private);
+ kfree(dev->ff);
+ dev->ff = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(input_ff_destroy);
diff --git a/drivers/input/ff-memless.c b/drivers/input/ff-memless.c
new file mode 100644
index 00000000000..cd8b7297e6d
--- /dev/null
+++ b/drivers/input/ff-memless.c
@@ -0,0 +1,515 @@
+/*
+ * Force feedback support for memoryless devices
+ *
+ * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ * Copyright (c) 2006 Dmitry Torokhov <dtor@mail.ru>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("ff-memless: " format "\n", ## arg)
+
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+
+#include "fixp-arith.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Anssi Hannula <anssi.hannula@gmail.com>");
+MODULE_DESCRIPTION("Force feedback support for memoryless devices");
+
+/* Number of effects handled with memoryless devices */
+#define FF_MEMLESS_EFFECTS 16
+
+/* Envelope update interval in ms */
+#define FF_ENVELOPE_INTERVAL 50
+
+#define FF_EFFECT_STARTED 0
+#define FF_EFFECT_PLAYING 1
+#define FF_EFFECT_ABORTING 2
+
+struct ml_effect_state {
+ struct ff_effect *effect;
+ unsigned long flags; /* effect state (STARTED, PLAYING, etc) */
+ int count; /* loop count of the effect */
+ unsigned long play_at; /* start time */
+ unsigned long stop_at; /* stop time */
+ unsigned long adj_at; /* last time the effect was sent */
+};
+
+struct ml_device {
+ void *private;
+ struct ml_effect_state states[FF_MEMLESS_EFFECTS];
+ int gain;
+ struct timer_list timer;
+ spinlock_t timer_lock;
+ struct input_dev *dev;
+
+ int (*play_effect)(struct input_dev *dev, void *data,
+ struct ff_effect *effect);
+};
+
+static const struct ff_envelope *get_envelope(const struct ff_effect *effect)
+{
+ static const struct ff_envelope empty_envelope;
+
+ switch (effect->type) {
+ case FF_PERIODIC:
+ return &effect->u.periodic.envelope;
+ case FF_CONSTANT:
+ return &effect->u.constant.envelope;
+ default:
+ return &empty_envelope;
+ }
+}
+
+/*
+ * Check for the next time envelope requires an update on memoryless devices
+ */
+static unsigned long calculate_next_time(struct ml_effect_state *state)
+{
+ const struct ff_envelope *envelope = get_envelope(state->effect);
+ unsigned long attack_stop, fade_start, next_fade;
+
+ if (envelope->attack_length) {
+ attack_stop = state->play_at +
+ msecs_to_jiffies(envelope->attack_length);
+ if (time_before(state->adj_at, attack_stop))
+ return state->adj_at +
+ msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+ }
+
+ if (state->effect->replay.length) {
+ if (envelope->fade_length) {
+ /* check when fading should start */
+ fade_start = state->stop_at -
+ msecs_to_jiffies(envelope->fade_length);
+
+ if (time_before(state->adj_at, fade_start))
+ return fade_start;
+
+ /* already fading, advance to next checkpoint */
+ next_fade = state->adj_at +
+ msecs_to_jiffies(FF_ENVELOPE_INTERVAL);
+ if (time_before(next_fade, state->stop_at))
+ return next_fade;
+ }
+
+ return state->stop_at;
+ }
+
+ return state->play_at;
+}
+
+static void ml_schedule_timer(struct ml_device *ml)
+{
+ struct ml_effect_state *state;
+ unsigned long now = jiffies;
+ unsigned long earliest = 0;
+ unsigned long next_at;
+ int events = 0;
+ int i;
+
+ debug("calculating next timer");
+
+ for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+
+ state = &ml->states[i];
+
+ if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+ continue;
+
+ if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+ next_at = calculate_next_time(state);
+ else
+ next_at = state->play_at;
+
+ if (time_before_eq(now, next_at) &&
+ (++events == 1 || time_before(next_at, earliest)))
+ earliest = next_at;
+ }
+
+ if (!events) {
+ debug("no actions");
+ del_timer(&ml->timer);
+ } else {
+ debug("timer set");
+ mod_timer(&ml->timer, earliest);
+ }
+}
+
+/*
+ * Apply an envelope to a value
+ */
+static int apply_envelope(struct ml_effect_state *state, int value,
+ struct ff_envelope *envelope)
+{
+ struct ff_effect *effect = state->effect;
+ unsigned long now = jiffies;
+ int time_from_level;
+ int time_of_envelope;
+ int envelope_level;
+ int difference;
+
+ if (envelope->attack_length &&
+ time_before(now,
+ state->play_at + msecs_to_jiffies(envelope->attack_length))) {
+ debug("value = 0x%x, attack_level = 0x%x", value,
+ envelope->attack_level);
+ time_from_level = jiffies_to_msecs(now - state->play_at);
+ time_of_envelope = envelope->attack_length;
+ envelope_level = min_t(__s16, envelope->attack_level, 0x7fff);
+
+ } else if (envelope->fade_length && effect->replay.length &&
+ time_after(now,
+ state->stop_at - msecs_to_jiffies(envelope->fade_length)) &&
+ time_before(now, state->stop_at)) {
+ time_from_level = jiffies_to_msecs(state->stop_at - now);
+ time_of_envelope = envelope->fade_length;
+ envelope_level = min_t(__s16, envelope->fade_level, 0x7fff);
+ } else
+ return value;
+
+ difference = abs(value) - envelope_level;
+
+ debug("difference = %d", difference);
+ debug("time_from_level = 0x%x", time_from_level);
+ debug("time_of_envelope = 0x%x", time_of_envelope);
+
+ difference = difference * time_from_level / time_of_envelope;
+
+ debug("difference = %d", difference);
+
+ return value < 0 ?
+ -(difference + envelope_level) : (difference + envelope_level);
+}
+
+/*
+ * Return the type the effect has to be converted into (memless devices)
+ */
+static int get_compatible_type(struct ff_device *ff, int effect_type)
+{
+
+ if (test_bit(effect_type, ff->ffbit))
+ return effect_type;
+
+ if (effect_type == FF_PERIODIC && test_bit(FF_RUMBLE, ff->ffbit))
+ return FF_RUMBLE;
+
+ printk(KERN_ERR
+ "ff-memless: invalid type in get_compatible_type()\n");
+
+ return 0;
+}
+
+/*
+ * Combine two effects and apply gain.
+ */
+static void ml_combine_effects(struct ff_effect *effect,
+ struct ml_effect_state *state,
+ int gain)
+{
+ struct ff_effect *new = state->effect;
+ unsigned int strong, weak, i;
+ int x, y;
+ fixp_t level;
+
+ switch (new->type) {
+ case FF_CONSTANT:
+ i = new->direction * 360 / 0xffff;
+ level = fixp_new16(apply_envelope(state,
+ new->u.constant.level,
+ &new->u.constant.envelope));
+ x = fixp_mult(fixp_sin(i), level) * gain / 0xffff;
+ y = fixp_mult(-fixp_cos(i), level) * gain / 0xffff;
+ /*
+ * here we abuse ff_ramp to hold x and y of constant force
+ * If in future any driver wants something else than x and y
+ * in s8, this should be changed to something more generic
+ */
+ effect->u.ramp.start_level =
+ max(min(effect->u.ramp.start_level + x, 0x7f), -0x80);
+ effect->u.ramp.end_level =
+ max(min(effect->u.ramp.end_level + y, 0x7f), -0x80);
+ break;
+
+ case FF_RUMBLE:
+ strong = new->u.rumble.strong_magnitude * gain / 0xffff;
+ weak = new->u.rumble.weak_magnitude * gain / 0xffff;
+ effect->u.rumble.strong_magnitude =
+ min(strong + effect->u.rumble.strong_magnitude,
+ 0xffffU);
+ effect->u.rumble.weak_magnitude =
+ min(weak + effect->u.rumble.weak_magnitude, 0xffffU);
+ break;
+
+ case FF_PERIODIC:
+ i = apply_envelope(state, abs(new->u.periodic.magnitude),
+ &new->u.periodic.envelope);
+
+ /* here we also scale it 0x7fff => 0xffff */
+ i = i * gain / 0x7fff;
+
+ effect->u.rumble.strong_magnitude =
+ min(i + effect->u.rumble.strong_magnitude, 0xffffU);
+ effect->u.rumble.weak_magnitude =
+ min(i + effect->u.rumble.weak_magnitude, 0xffffU);
+ break;
+
+ default:
+ printk(KERN_ERR "ff-memless: invalid type in ml_combine_effects()\n");
+ break;
+ }
+
+}
+
+
+/*
+ * Because memoryless devices have only one effect per effect type active
+ * at one time we have to combine multiple effects into one
+ */
+static int ml_get_combo_effect(struct ml_device *ml,
+ unsigned long *effect_handled,
+ struct ff_effect *combo_effect)
+{
+ struct ff_effect *effect;
+ struct ml_effect_state *state;
+ int effect_type;
+ int i;
+
+ memset(combo_effect, 0, sizeof(struct ff_effect));
+
+ for (i = 0; i < FF_MEMLESS_EFFECTS; i++) {
+ if (__test_and_set_bit(i, effect_handled))
+ continue;
+
+ state = &ml->states[i];
+ effect = state->effect;
+
+ if (!test_bit(FF_EFFECT_STARTED, &state->flags))
+ continue;
+
+ if (time_before(jiffies, state->play_at))
+ continue;
+
+ /*
+ * here we have started effects that are either
+ * currently playing (and may need be aborted)
+ * or need to start playing.
+ */
+ effect_type = get_compatible_type(ml->dev->ff, effect->type);
+ if (combo_effect->type != effect_type) {
+ if (combo_effect->type != 0) {
+ __clear_bit(i, effect_handled);
+ continue;
+ }
+ combo_effect->type = effect_type;
+ }
+
+ if (__test_and_clear_bit(FF_EFFECT_ABORTING, &state->flags)) {
+ __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+ __clear_bit(FF_EFFECT_STARTED, &state->flags);
+ } else if (effect->replay.length &&
+ time_after_eq(jiffies, state->stop_at)) {
+
+ __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+
+ if (--state->count <= 0) {
+ __clear_bit(FF_EFFECT_STARTED, &state->flags);
+ } else {
+ state->play_at = jiffies +
+ msecs_to_jiffies(effect->replay.delay);
+ state->stop_at = state->play_at +
+ msecs_to_jiffies(effect->replay.length);
+ }
+ } else {
+ __set_bit(FF_EFFECT_PLAYING, &state->flags);
+ state->adj_at = jiffies;
+ ml_combine_effects(combo_effect, state, ml->gain);
+ }
+ }
+
+ return combo_effect->type != 0;
+}
+
+static void ml_play_effects(struct ml_device *ml)
+{
+ struct ff_effect effect;
+ DECLARE_BITMAP(handled_bm, FF_MEMLESS_EFFECTS);
+
+ memset(handled_bm, 0, sizeof(handled_bm));
+
+ while (ml_get_combo_effect(ml, handled_bm, &effect))
+ ml->play_effect(ml->dev, ml->private, &effect);
+
+ ml_schedule_timer(ml);
+}
+
+static void ml_effect_timer(unsigned long timer_data)
+{
+ struct input_dev *dev = (struct input_dev *)timer_data;
+ struct ml_device *ml = dev->ff->private;
+
+ debug("timer: updating effects");
+
+ spin_lock(&ml->timer_lock);
+ ml_play_effects(ml);
+ spin_unlock(&ml->timer_lock);
+}
+
+static void ml_ff_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct ml_device *ml = dev->ff->private;
+ int i;
+
+ spin_lock_bh(&ml->timer_lock);
+
+ ml->gain = gain;
+
+ for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+ __clear_bit(FF_EFFECT_PLAYING, &ml->states[i].flags);
+
+ ml_play_effects(ml);
+
+ spin_unlock_bh(&ml->timer_lock);
+}
+
+static int ml_ff_playback(struct input_dev *dev, int effect_id, int value)
+{
+ struct ml_device *ml = dev->ff->private;
+ struct ml_effect_state *state = &ml->states[effect_id];
+
+ spin_lock_bh(&ml->timer_lock);
+
+ if (value > 0) {
+ debug("initiated play");
+
+ __set_bit(FF_EFFECT_STARTED, &state->flags);
+ state->count = value;
+ state->play_at = jiffies +
+ msecs_to_jiffies(state->effect->replay.delay);
+ state->stop_at = state->play_at +
+ msecs_to_jiffies(state->effect->replay.length);
+ state->adj_at = state->play_at;
+
+ ml_schedule_timer(ml);
+
+ } else {
+ debug("initiated stop");
+
+ if (test_bit(FF_EFFECT_PLAYING, &state->flags))
+ __set_bit(FF_EFFECT_ABORTING, &state->flags);
+ else
+ __clear_bit(FF_EFFECT_STARTED, &state->flags);
+
+ ml_play_effects(ml);
+ }
+
+ spin_unlock_bh(&ml->timer_lock);
+
+ return 0;
+}
+
+static int ml_ff_upload(struct input_dev *dev,
+ struct ff_effect *effect, struct ff_effect *old)
+{
+ struct ml_device *ml = dev->ff->private;
+ struct ml_effect_state *state = &ml->states[effect->id];
+
+ spin_lock_bh(&ml->timer_lock);
+
+ if (test_bit(FF_EFFECT_STARTED, &state->flags)) {
+ __clear_bit(FF_EFFECT_PLAYING, &state->flags);
+ state->play_at = jiffies +
+ msecs_to_jiffies(state->effect->replay.delay);
+ state->stop_at = state->play_at +
+ msecs_to_jiffies(state->effect->replay.length);
+ state->adj_at = state->play_at;
+ ml_schedule_timer(ml);
+ }
+
+ spin_unlock_bh(&ml->timer_lock);
+
+ return 0;
+}
+
+static void ml_ff_destroy(struct ff_device *ff)
+{
+ struct ml_device *ml = ff->private;
+
+ kfree(ml->private);
+}
+
+/**
+ * input_ff_create_memless() - create memoryless FF device
+ * @dev: input device supporting force-feedback
+ * @data: driver-specific data to be passed into @play_effect
+ * @play_effect: driver-specific method for playing FF effect
+ */
+int input_ff_create_memless(struct input_dev *dev, void *data,
+ int (*play_effect)(struct input_dev *, void *, struct ff_effect *))
+{
+ struct ml_device *ml;
+ struct ff_device *ff;
+ int error;
+ int i;
+
+ ml = kzalloc(sizeof(struct ml_device), GFP_KERNEL);
+ if (!ml)
+ return -ENOMEM;
+
+ ml->dev = dev;
+ ml->private = data;
+ ml->play_effect = play_effect;
+ ml->gain = 0xffff;
+ spin_lock_init(&ml->timer_lock);
+ setup_timer(&ml->timer, ml_effect_timer, (unsigned long)dev);
+
+ set_bit(FF_GAIN, dev->ffbit);
+
+ error = input_ff_create(dev, FF_MEMLESS_EFFECTS);
+ if (error) {
+ kfree(ml);
+ return error;
+ }
+
+ ff = dev->ff;
+ ff->private = ml;
+ ff->upload = ml_ff_upload;
+ ff->playback = ml_ff_playback;
+ ff->set_gain = ml_ff_set_gain;
+ ff->destroy = ml_ff_destroy;
+
+ /* we can emulate periodic effects with RUMBLE */
+ if (test_bit(FF_RUMBLE, ff->ffbit)) {
+ set_bit(FF_PERIODIC, dev->ffbit);
+ set_bit(FF_SINE, dev->ffbit);
+ set_bit(FF_TRIANGLE, dev->ffbit);
+ set_bit(FF_SQUARE, dev->ffbit);
+ }
+
+ for (i = 0; i < FF_MEMLESS_EFFECTS; i++)
+ ml->states[i].effect = &ff->effects[i];
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(input_ff_create_memless);
diff --git a/drivers/usb/input/fixp-arith.h b/drivers/input/fixp-arith.h
index ed3d2da0c48..ed3d2da0c48 100644
--- a/drivers/usb/input/fixp-arith.h
+++ b/drivers/input/fixp-arith.h
diff --git a/drivers/input/input.c b/drivers/input/input.c
index 9cb4b9a54f0..1c8c8a5bc4a 100644
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -176,6 +176,10 @@ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, in
break;
case EV_FF:
+
+ if (value < 0)
+ return;
+
if (dev->event)
dev->event(dev, type, code, value);
break;
@@ -309,7 +313,8 @@ static void input_link_handle(struct input_handle *handle)
if (i != NBITS(max)) \
continue;
-static struct input_device_id *input_match_device(struct input_device_id *id, struct input_dev *dev)
+static const struct input_device_id *input_match_device(const struct input_device_id *id,
+ struct input_dev *dev)
{
int i;
@@ -762,7 +767,9 @@ static void input_dev_release(struct class_device *class_dev)
{
struct input_dev *dev = to_input_dev(class_dev);
+ input_ff_destroy(dev);
kfree(dev);
+
module_put(THIS_MODULE);
}
@@ -899,12 +906,13 @@ struct input_dev *input_allocate_device(void)
dev = kzalloc(sizeof(struct input_dev), GFP_KERNEL);
if (dev) {
- 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);
+
+ __module_get(THIS_MODULE);
}
return dev;
@@ -929,17 +937,10 @@ int input_register_device(struct input_dev *dev)
static atomic_t input_no = ATOMIC_INIT(0);
struct input_handle *handle;
struct input_handler *handler;
- struct input_device_id *id;
+ const struct input_device_id *id;
const char *path;
int error;
- if (!dev->dynalloc) {
- printk(KERN_WARNING "input: device %s is statically allocated, will not register\n"
- "Please convert to input_allocate_device() or contact dtor_core@ameritech.net\n",
- dev->name ? dev->name : "<Unknown>");
- return -EINVAL;
- }
-
set_bit(EV_SYN, dev->evbit);
/*
@@ -955,10 +956,8 @@ int input_register_device(struct input_dev *dev)
dev->rep[REP_PERIOD] = 33;
}
- INIT_LIST_HEAD(&dev->h_list);
list_add_tail(&dev->node, &input_dev_list);
- dev->cdev.class = &input_class;
snprintf(dev->cdev.class_id, sizeof(dev->cdev.class_id),
"input%ld", (unsigned long) atomic_inc_return(&input_no) - 1);
@@ -978,8 +977,6 @@ int input_register_device(struct input_dev *dev)
if (error)
goto fail3;
- __module_get(THIS_MODULE);
-
path = kobject_get_path(&dev->cdev.kobj, GFP_KERNEL);
printk(KERN_INFO "input: %s as %s\n",
dev->name ? dev->name : "Unspecified device", path ? path : "N/A");
@@ -1008,9 +1005,12 @@ EXPORT_SYMBOL(input_register_device);
void input_unregister_device(struct input_dev *dev)
{
struct list_head *node, *next;
+ int code;
- if (!dev)
- return;
+ for (code = 0; code <= KEY_MAX; code++)
+ if (test_bit(code, dev->key))
+ input_report_key(dev, code, 0);
+ input_sync(dev);
del_timer_sync(&dev->timer);
@@ -1037,19 +1037,20 @@ void input_unregister_device(struct input_dev *dev)
}
EXPORT_SYMBOL(input_unregister_device);
-void input_register_handler(struct input_handler *handler)
+int input_register_handler(struct input_handler *handler)
{
struct input_dev *dev;
struct input_handle *handle;
- struct input_device_id *id;
-
- if (!handler)
- return;
+ const struct input_device_id *id;
INIT_LIST_HEAD(&handler->h_list);
- if (handler->fops != NULL)
+ if (handler->fops != NULL) {
+ if (input_table[handler->minor >> 5])
+ return -EBUSY;
+
input_table[handler->minor >> 5] = handler;
+ }
list_add_tail(&handler->node, &input_handler_list);
@@ -1063,6 +1064,7 @@ void input_register_handler(struct input_handler *handler)
}
input_wakeup_procfs_readers();
+ return 0;
}
EXPORT_SYMBOL(input_register_handler);
diff --git a/drivers/input/joydev.c b/drivers/input/joydev.c
index d67157513bf..9f3529ad3fd 100644
--- a/drivers/input/joydev.c
+++ b/drivers/input/joydev.c
@@ -451,7 +451,7 @@ static int joydev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
}
}
-static struct file_operations joydev_fops = {
+static const struct file_operations joydev_fops = {
.owner = THIS_MODULE,
.read = joydev_read,
.write = joydev_write,
@@ -465,7 +465,8 @@ static struct file_operations joydev_fops = {
.fasync = joydev_fasync,
};
-static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *joydev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct joydev *joydev;
struct class_device *cdev;
@@ -562,7 +563,7 @@ static void joydev_disconnect(struct input_handle *handle)
joydev_free(joydev);
}
-static struct input_device_id joydev_blacklist[] = {
+static const struct input_device_id joydev_blacklist[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT(EV_KEY) },
@@ -571,7 +572,7 @@ static struct input_device_id joydev_blacklist[] = {
{ } /* Terminating entry */
};
-static struct input_device_id joydev_ids[] = {
+static const struct input_device_id joydev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_ABSBIT,
.evbit = { BIT(EV_ABS) },
@@ -605,8 +606,7 @@ static struct input_handler joydev_handler = {
static int __init joydev_init(void)
{
- input_register_handler(&joydev_handler);
- return 0;
+ return input_register_handler(&joydev_handler);
}
static void __exit joydev_exit(void)
diff --git a/drivers/input/joystick/iforce/iforce-ff.c b/drivers/input/joystick/iforce/iforce-ff.c
index 50c90765aee..8fb0c19cc60 100644
--- a/drivers/input/joystick/iforce/iforce-ff.c
+++ b/drivers/input/joystick/iforce/iforce-ff.c
@@ -165,19 +165,19 @@ static int make_condition_modifier(struct iforce* iforce,
data[0] = LO(mod_chunk->start);
data[1] = HI(mod_chunk->start);
- data[2] = (100*rk)>>15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
- data[3] = (100*lk)>>15; /* This code is incorrect on cpus lacking arith shift */
+ data[2] = (100 * rk) >> 15; /* Dangerous: the sign is extended by gcc on plateforms providing an arith shift */
+ data[3] = (100 * lk) >> 15; /* This code is incorrect on cpus lacking arith shift */
- center = (500*center)>>15;
+ center = (500 * center) >> 15;
data[4] = LO(center);
data[5] = HI(center);
- db = (1000*db)>>16;
+ db = (1000 * db) >> 16;
data[6] = LO(db);
data[7] = HI(db);
- data[8] = (100*rsat)>>16;
- data[9] = (100*lsat)>>16;
+ data[8] = (100 * rsat) >> 16;
+ data[9] = (100 * lsat) >> 16;
iforce_send_packet(iforce, FF_CMD_CONDITION, data);
iforce_dump_packet("condition", FF_CMD_CONDITION, data);
@@ -188,6 +188,7 @@ static int make_condition_modifier(struct iforce* iforce,
static unsigned char find_button(struct iforce *iforce, signed short button)
{
int i;
+
for (i = 1; iforce->type->btn[i] >= 0; i++)
if (iforce->type->btn[i] == button)
return i + 1;
@@ -198,19 +199,17 @@ static unsigned char find_button(struct iforce *iforce, signed short button)
* Analyse the changes in an effect, and tell if we need to send an condition
* parameter packet
*/
-static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
+static int need_condition_modifier(struct ff_effect *old, struct ff_effect *new)
{
- int id = new->id;
- struct ff_effect* old = &iforce->core_effects[id].effect;
- int ret=0;
+ int ret = 0;
int i;
if (new->type != FF_SPRING && new->type != FF_FRICTION) {
printk(KERN_WARNING "iforce.c: bad effect type in need_condition_modifier\n");
- return FALSE;
+ return 0;
}
- for(i=0; i<2; i++) {
+ for (i = 0; i < 2; i++) {
ret |= old->u.condition[i].right_saturation != new->u.condition[i].right_saturation
|| old->u.condition[i].left_saturation != new->u.condition[i].left_saturation
|| old->u.condition[i].right_coeff != new->u.condition[i].right_coeff
@@ -225,35 +224,29 @@ static int need_condition_modifier(struct iforce* iforce, struct ff_effect* new)
* Analyse the changes in an effect, and tell if we need to send a magnitude
* parameter packet
*/
-static int need_magnitude_modifier(struct iforce* iforce, struct ff_effect* effect)
+static int need_magnitude_modifier(struct ff_effect *old, struct ff_effect *effect)
{
- int id = effect->id;
- struct ff_effect* old = &iforce->core_effects[id].effect;
-
if (effect->type != FF_CONSTANT) {
printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
- return FALSE;
+ return 0;
}
- return (old->u.constant.level != effect->u.constant.level);
+ return old->u.constant.level != effect->u.constant.level;
}
/*
* Analyse the changes in an effect, and tell if we need to send an envelope
* parameter packet
*/
-static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effect)
+static int need_envelope_modifier(struct ff_effect *old, struct ff_effect *effect)
{
- int id = effect->id;
- struct ff_effect* old = &iforce->core_effects[id].effect;
-
switch (effect->type) {
case FF_CONSTANT:
if (old->u.constant.envelope.attack_length != effect->u.constant.envelope.attack_length
|| old->u.constant.envelope.attack_level != effect->u.constant.envelope.attack_level
|| old->u.constant.envelope.fade_length != effect->u.constant.envelope.fade_length
|| old->u.constant.envelope.fade_level != effect->u.constant.envelope.fade_level)
- return TRUE;
+ return 1;
break;
case FF_PERIODIC:
@@ -261,30 +254,26 @@ static int need_envelope_modifier(struct iforce* iforce, struct ff_effect* effec
|| old->u.periodic.envelope.attack_level != effect->u.periodic.envelope.attack_level
|| old->u.periodic.envelope.fade_length != effect->u.periodic.envelope.fade_length
|| old->u.periodic.envelope.fade_level != effect->u.periodic.envelope.fade_level)
- return TRUE;
+ return 1;
break;
default:
printk(KERN_WARNING "iforce.c: bad effect type in need_envelope_modifier\n");
}
- return FALSE;
+ return 0;
}
/*
* Analyse the changes in an effect, and tell if we need to send a periodic
* parameter effect
*/
-static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
+static int need_period_modifier(struct ff_effect *old, struct ff_effect *new)
{
- int id = new->id;
- struct ff_effect* old = &iforce->core_effects[id].effect;
-
if (new->type != FF_PERIODIC) {
- printk(KERN_WARNING "iforce.c: bad effect type in need_periodic_modifier\n");
- return FALSE;
+ printk(KERN_WARNING "iforce.c: bad effect type in need_period_modifier\n");
+ return 0;
}
-
return (old->u.periodic.period != new->u.periodic.period
|| old->u.periodic.magnitude != new->u.periodic.magnitude
|| old->u.periodic.offset != new->u.periodic.offset
@@ -295,19 +284,16 @@ static int need_period_modifier(struct iforce* iforce, struct ff_effect* new)
* Analyse the changes in an effect, and tell if we need to send an effect
* packet
*/
-static int need_core(struct iforce* iforce, struct ff_effect* new)
+static int need_core(struct ff_effect *old, struct ff_effect *new)
{
- int id = new->id;
- struct ff_effect* old = &iforce->core_effects[id].effect;
-
if (old->direction != new->direction
|| old->trigger.button != new->trigger.button
|| old->trigger.interval != new->trigger.interval
|| old->replay.length != new->replay.length
|| old->replay.delay != new->replay.delay)
- return TRUE;
+ return 1;
- return FALSE;
+ return 0;
}
/*
* Send the part common to all effects to the device
@@ -360,7 +346,7 @@ static int make_core(struct iforce* iforce, u16 id, u16 mod_id1, u16 mod_id2,
* Upload a periodic effect to the device
* See also iforce_upload_constant.
*/
-int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int is_update)
+int iforce_upload_periodic(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
{
u8 wave_code;
int core_id = effect->id;
@@ -371,23 +357,25 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
int param2_err = 1;
int core_err = 0;
- if (!is_update || need_period_modifier(iforce, effect)) {
+ if (!old || need_period_modifier(old, effect)) {
param1_err = make_period_modifier(iforce, mod1_chunk,
- is_update,
+ old != NULL,
effect->u.periodic.magnitude, effect->u.periodic.offset,
effect->u.periodic.period, effect->u.periodic.phase);
- if (param1_err) return param1_err;
+ if (param1_err)
+ return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
- if (!is_update || need_envelope_modifier(iforce, effect)) {
+ if (!old || need_envelope_modifier(old, effect)) {
param2_err = make_envelope_modifier(iforce, mod2_chunk,
- is_update,
+ old !=NULL,
effect->u.periodic.envelope.attack_length,
effect->u.periodic.envelope.attack_level,
effect->u.periodic.envelope.fade_length,
effect->u.periodic.envelope.fade_level);
- if (param2_err) return param2_err;
+ if (param2_err)
+ return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
@@ -400,7 +388,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
default: wave_code = 0x20; break;
}
- if (!is_update || need_core(iforce, effect)) {
+ if (!old || need_core(old, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
@@ -429,7 +417,7 @@ int iforce_upload_periodic(struct iforce* iforce, struct ff_effect* effect, int
* 0 Ok, effect created or updated
* 1 effect did not change since last upload, and no packet was therefore sent
*/
-int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int is_update)
+int iforce_upload_constant(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
@@ -439,26 +427,28 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int
int param2_err = 1;
int core_err = 0;
- if (!is_update || need_magnitude_modifier(iforce, effect)) {
+ if (!old || need_magnitude_modifier(old, effect)) {
param1_err = make_magnitude_modifier(iforce, mod1_chunk,
- is_update,
+ old != NULL,
effect->u.constant.level);
- if (param1_err) return param1_err;
+ if (param1_err)
+ return param1_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
}
- if (!is_update || need_envelope_modifier(iforce, effect)) {
+ if (!old || need_envelope_modifier(old, effect)) {
param2_err = make_envelope_modifier(iforce, mod2_chunk,
- is_update,
+ old != NULL,
effect->u.constant.envelope.attack_length,
effect->u.constant.envelope.attack_level,
effect->u.constant.envelope.fade_length,
effect->u.constant.envelope.fade_level);
- if (param2_err) return param2_err;
+ if (param2_err)
+ return param2_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
- if (!is_update || need_core(iforce, effect)) {
+ if (!old || need_core(old, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start,
mod2_chunk->start,
@@ -483,7 +473,7 @@ int iforce_upload_constant(struct iforce* iforce, struct ff_effect* effect, int
/*
* Upload an condition effect. Those are for example friction, inertia, springs...
*/
-int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int is_update)
+int iforce_upload_condition(struct iforce *iforce, struct ff_effect *effect, struct ff_effect *old)
{
int core_id = effect->id;
struct iforce_core_effect* core_effect = iforce->core_effects + core_id;
@@ -494,37 +484,39 @@ int iforce_upload_condition(struct iforce* iforce, struct ff_effect* effect, int
int core_err = 0;
switch (effect->type) {
- case FF_SPRING: type = 0x40; break;
- case FF_DAMPER: type = 0x41; break;
+ case FF_SPRING: type = 0x40; break;
+ case FF_DAMPER: type = 0x41; break;
default: return -1;
}
- if (!is_update || need_condition_modifier(iforce, effect)) {
+ if (!old || need_condition_modifier(old, effect)) {
param_err = make_condition_modifier(iforce, mod1_chunk,
- is_update,
+ old != NULL,
effect->u.condition[0].right_saturation,
effect->u.condition[0].left_saturation,
effect->u.condition[0].right_coeff,
effect->u.condition[0].left_coeff,
effect->u.condition[0].deadband,
effect->u.condition[0].center);
- if (param_err) return param_err;
+ if (param_err)
+ return param_err;
set_bit(FF_MOD1_IS_USED, core_effect->flags);
param_err = make_condition_modifier(iforce, mod2_chunk,
- is_update,
+ old != NULL,
effect->u.condition[1].right_saturation,
effect->u.condition[1].left_saturation,
effect->u.condition[1].right_coeff,
effect->u.condition[1].left_coeff,
effect->u.condition[1].deadband,
effect->u.condition[1].center);
- if (param_err) return param_err;
+ if (param_err)
+ return param_err;
set_bit(FF_MOD2_IS_USED, core_effect->flags);
}
- if (!is_update || need_core(iforce, effect)) {
+ if (!old || need_core(old, effect)) {
core_err = make_core(iforce, effect->id,
mod1_chunk->start, mod2_chunk->start,
type, 0xc0,
diff --git a/drivers/input/joystick/iforce/iforce-main.c b/drivers/input/joystick/iforce/iforce-main.c
index b4914e7231f..24c684bc633 100644
--- a/drivers/input/joystick/iforce/iforce-main.c
+++ b/drivers/input/joystick/iforce/iforce-main.c
@@ -83,103 +83,57 @@ static struct iforce_device iforce_device[] = {
{ 0x0000, 0x0000, "Unknown I-Force Device [%04x:%04x]", btn_joystick, abs_joystick, ff_iforce }
};
-
-
-static int iforce_input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+static int iforce_playback(struct input_dev *dev, int effect_id, int value)
{
struct iforce* iforce = dev->private;
- unsigned char data[3];
-
- if (type != EV_FF)
- return -1;
-
- switch (code) {
-
- case FF_GAIN:
-
- data[0] = value >> 9;
- iforce_send_packet(iforce, FF_CMD_GAIN, data);
-
- return 0;
-
- case FF_AUTOCENTER:
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
- data[0] = 0x03;
- data[1] = value >> 9;
- iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+ if (value > 0)
+ set_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
+ else
+ clear_bit(FF_CORE_SHOULD_PLAY, core_effect->flags);
- data[0] = 0x04;
- data[1] = 0x01;
- iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
+ iforce_control_playback(iforce, effect_id, value);
+ return 0;
+}
- return 0;
+static void iforce_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct iforce* iforce = dev->private;
+ unsigned char data[3];
- default: /* Play or stop an effect */
+ data[0] = gain >> 9;
+ iforce_send_packet(iforce, FF_CMD_GAIN, data);
+}
- if (!CHECK_OWNERSHIP(code, iforce)) {
- return -1;
- }
- if (value > 0) {
- set_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
- }
- else {
- clear_bit(FF_CORE_SHOULD_PLAY, iforce->core_effects[code].flags);
- }
+static void iforce_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ struct iforce* iforce = dev->private;
+ unsigned char data[3];
- iforce_control_playback(iforce, code, value);
- return 0;
- }
+ data[0] = 0x03;
+ data[1] = magnitude >> 9;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
- return -1;
+ data[0] = 0x04;
+ data[1] = 0x01;
+ iforce_send_packet(iforce, FF_CMD_AUTOCENTER, data);
}
/*
* Function called when an ioctl is performed on the event dev entry.
* It uploads an effect to the device
*/
-static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct iforce* iforce = dev->private;
- int id;
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect->id];
int ret;
- int is_update;
-
-/* Check this effect type is supported by this device */
- if (!test_bit(effect->type, iforce->dev->ffbit))
- return -EINVAL;
-
-/*
- * If we want to create a new effect, get a free id
- */
- if (effect->id == -1) {
-
- for (id = 0; id < FF_EFFECTS_MAX; ++id)
- if (!test_and_set_bit(FF_CORE_IS_USED, iforce->core_effects[id].flags))
- break;
-
- if (id == FF_EFFECTS_MAX || id >= iforce->dev->ff_effects_max)
- return -ENOMEM;
-
- effect->id = id;
- iforce->core_effects[id].owner = current->pid;
- iforce->core_effects[id].flags[0] = (1 << FF_CORE_IS_USED); /* Only IS_USED bit must be set */
-
- is_update = FALSE;
- }
- else {
- /* We want to update an effect */
- if (!CHECK_OWNERSHIP(effect->id, iforce))
- return -EACCES;
-
- /* Parameter type cannot be updated */
- if (effect->type != iforce->core_effects[effect->id].effect.type)
- return -EINVAL;
+ if (__test_and_set_bit(FF_CORE_IS_USED, core_effect->flags)) {
/* Check the effect is not already being updated */
- if (test_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags))
+ if (test_bit(FF_CORE_UPDATE, core_effect->flags))
return -EAGAIN;
-
- is_update = TRUE;
}
/*
@@ -188,28 +142,28 @@ static int iforce_upload_effect(struct input_dev *dev, struct ff_effect *effect)
switch (effect->type) {
case FF_PERIODIC:
- ret = iforce_upload_periodic(iforce, effect, is_update);
+ ret = iforce_upload_periodic(iforce, effect, old);
break;
case FF_CONSTANT:
- ret = iforce_upload_constant(iforce, effect, is_update);
+ ret = iforce_upload_constant(iforce, effect, old);
break;
case FF_SPRING:
case FF_DAMPER:
- ret = iforce_upload_condition(iforce, effect, is_update);
+ ret = iforce_upload_condition(iforce, effect, old);
break;
default:
return -EINVAL;
}
+
if (ret == 0) {
/* A packet was sent, forbid new updates until we are notified
* that the packet was updated
*/
- set_bit(FF_CORE_UPDATE, iforce->core_effects[effect->id].flags);
+ set_bit(FF_CORE_UPDATE, core_effect->flags);
}
- iforce->core_effects[effect->id].effect = *effect;
return ret;
}
@@ -219,20 +173,9 @@ 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 = dev->private;
+ struct iforce *iforce = dev->private;
+ struct iforce_core_effect *core_effect = &iforce->core_effects[effect_id];
int err = 0;
- struct iforce_core_effect* core_effect;
-
- if (effect_id < 0 || effect_id >= FF_EFFECTS_MAX)
- return -EINVAL;
-
- core_effect = &iforce->core_effects[effect_id];
-
- /* Check who is trying to erase this effect */
- if (core_effect->owner != current->pid) {
- printk(KERN_WARNING "iforce-main.c: %d tried to erase an effect belonging to %d\n", current->pid, core_effect->owner);
- return -EACCES;
- }
if (test_bit(FF_MOD1_IS_USED, core_effect->flags))
err = release_resource(&core_effect->mod1_chunk);
@@ -240,7 +183,7 @@ static int iforce_erase_effect(struct input_dev *dev, int effect_id)
if (!err && test_bit(FF_MOD2_IS_USED, core_effect->flags))
err = release_resource(&core_effect->mod2_chunk);
- /*TODO: remember to change that if more FF_MOD* bits are added */
+ /* TODO: remember to change that if more FF_MOD* bits are added */
core_effect->flags[0] = 0;
return err;
@@ -260,52 +203,31 @@ static int iforce_open(struct input_dev *dev)
#endif
}
- /* Enable force feedback */
- iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+ if (test_bit(EV_FF, dev->evbit)) {
+ /* Enable force feedback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\004");
+ }
return 0;
}
-static int iforce_flush(struct input_dev *dev, struct file *file)
+static void iforce_release(struct input_dev *dev)
{
struct iforce *iforce = dev->private;
int i;
- /* Erase all effects this process owns */
- for (i=0; i<dev->ff_effects_max; ++i) {
-
- if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
- current->pid == iforce->core_effects[i].owner) {
-
- /* Stop effect */
- input_report_ff(dev, i, 0);
-
- /* Free ressources assigned to effect */
- if (iforce_erase_effect(dev, i)) {
- printk(KERN_WARNING "iforce_flush: erase effect %d failed\n", i);
+ if (test_bit(EV_FF, dev->evbit)) {
+ /* Check: no effects should be present in memory */
+ for (i = 0; i < dev->ff->max_effects; i++) {
+ if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags)) {
+ printk(KERN_WARNING "iforce_release: Device still owns effects\n");
+ break;
}
}
+ /* Disable force feedback playback */
+ iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
}
- return 0;
-}
-
-static void iforce_release(struct input_dev *dev)
-{
- struct iforce *iforce = dev->private;
- int i;
-
- /* Check: no effect should be present in memory */
- for (i=0; i<dev->ff_effects_max; ++i) {
- if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags))
- break;
- }
- if (i<dev->ff_effects_max) {
- printk(KERN_WARNING "iforce_release: Device still owns effects\n");
- }
-
- /* Disable force feedback playback */
- iforce_send_packet(iforce, FF_CMD_ENABLE, "\001");
switch (iforce->bus) {
#ifdef CONFIG_JOYSTICK_IFORCE_USB
@@ -342,8 +264,10 @@ void iforce_delete_device(struct iforce *iforce)
int iforce_init_device(struct iforce *iforce)
{
struct input_dev *input_dev;
+ struct ff_device *ff;
unsigned char c[] = "CEOV";
- int i;
+ int i, error;
+ int ff_effects = 0;
input_dev = input_allocate_device();
if (!input_dev)
@@ -378,11 +302,6 @@ int iforce_init_device(struct iforce *iforce)
input_dev->name = "Unknown I-Force device";
input_dev->open = iforce_open;
input_dev->close = iforce_release;
- input_dev->flush = iforce_flush;
- input_dev->event = iforce_input_event;
- input_dev->upload_effect = iforce_upload_effect;
- input_dev->erase_effect = iforce_erase_effect;
- input_dev->ff_effects_max = 10;
/*
* On-device memory allocation.
@@ -430,15 +349,15 @@ int iforce_init_device(struct iforce *iforce)
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet B\n");
if (!iforce_get_id_packet(iforce, "N"))
- iforce->dev->ff_effects_max = iforce->edata[1];
+ ff_effects = iforce->edata[1];
else
printk(KERN_WARNING "iforce-main.c: Device does not respond to id packet N\n");
/* Check if the device can store more effects than the driver can really handle */
- if (iforce->dev->ff_effects_max > FF_EFFECTS_MAX) {
- printk(KERN_WARNING "input??: Device can handle %d effects, but N_EFFECTS_MAX is set to %d in iforce.h\n",
- iforce->dev->ff_effects_max, FF_EFFECTS_MAX);
- iforce->dev->ff_effects_max = FF_EFFECTS_MAX;
+ if (ff_effects > IFORCE_EFFECTS_MAX) {
+ printk(KERN_WARNING "iforce: Limiting number of effects to %d (device reports %d)\n",
+ IFORCE_EFFECTS_MAX, ff_effects);
+ ff_effects = IFORCE_EFFECTS_MAX;
}
/*
@@ -472,12 +391,10 @@ int iforce_init_device(struct iforce *iforce)
* Set input device bitfields and ranges.
*/
- input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF) | BIT(EV_FF_STATUS);
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS) | BIT(EV_FF_STATUS);
- for (i = 0; iforce->type->btn[i] >= 0; i++) {
- signed short t = iforce->type->btn[i];
- set_bit(t, input_dev->keybit);
- }
+ for (i = 0; iforce->type->btn[i] >= 0; i++)
+ set_bit(iforce->type->btn[i], input_dev->keybit);
set_bit(BTN_DEAD, input_dev->keybit);
for (i = 0; iforce->type->abs[i] >= 0; i++) {
@@ -516,9 +433,24 @@ int iforce_init_device(struct iforce *iforce)
}
}
- for (i = 0; iforce->type->ff[i] >= 0; i++)
- set_bit(iforce->type->ff[i], input_dev->ffbit);
+ if (ff_effects) {
+ for (i = 0; iforce->type->ff[i] >= 0; i++)
+ set_bit(iforce->type->ff[i], input_dev->ffbit);
+
+ error = input_ff_create(input_dev, ff_effects);
+ if (error) {
+ input_free_device(input_dev);
+ return error;
+ }
+
+ ff = input_dev->ff;
+ ff->upload = iforce_upload_effect;
+ ff->erase = iforce_erase_effect;
+ ff->set_gain = iforce_set_gain;
+ ff->set_autocenter = iforce_set_autocenter;
+ ff->playback = iforce_playback;
+ }
/*
* Register input device.
*/
diff --git a/drivers/input/joystick/iforce/iforce-packets.c b/drivers/input/joystick/iforce/iforce-packets.c
index 76cb1f88f4e..8632d47a7fb 100644
--- a/drivers/input/joystick/iforce/iforce-packets.c
+++ b/drivers/input/joystick/iforce/iforce-packets.c
@@ -140,7 +140,10 @@ static int mark_core_as_ready(struct iforce *iforce, unsigned short addr)
{
int i;
- for (i = 0; i < iforce->dev->ff_effects_max; ++i) {
+ if (!iforce->dev->ff)
+ return 0;
+
+ for (i = 0; i < iforce->dev->ff->max_effects; ++i) {
if (test_bit(FF_CORE_IS_USED, iforce->core_effects[i].flags) &&
(iforce->core_effects[i].mod1_chunk.start == addr ||
iforce->core_effects[i].mod2_chunk.start == addr)) {
@@ -229,19 +232,17 @@ void iforce_process_packet(struct iforce *iforce, u16 cmd, unsigned char *data,
i = data[1] & 0x7f;
if (data[1] & 0x80) {
if (!test_and_set_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
- /* Report play event */
- input_report_ff_status(dev, i, FF_STATUS_PLAYING);
+ /* Report play event */
+ input_report_ff_status(dev, i, FF_STATUS_PLAYING);
}
- }
- else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
+ } else if (test_and_clear_bit(FF_CORE_IS_PLAYED, iforce->core_effects[i].flags)) {
/* Report stop event */
input_report_ff_status(dev, i, FF_STATUS_STOPPED);
}
if (LO(cmd) > 3) {
int j;
- for (j=3; j<LO(cmd); j+=2) {
+ for (j = 3; j < LO(cmd); j += 2)
mark_core_as_ready(iforce, data[j] | (data[j+1]<<8));
- }
}
break;
}
diff --git a/drivers/input/joystick/iforce/iforce.h b/drivers/input/joystick/iforce/iforce.h
index e9924d6f01b..947df273984 100644
--- a/drivers/input/joystick/iforce/iforce.h
+++ b/drivers/input/joystick/iforce/iforce.h
@@ -51,10 +51,7 @@
#define IFORCE_232 1
#define IFORCE_USB 2
-#define FALSE 0
-#define TRUE 1
-
-#define FF_EFFECTS_MAX 32
+#define IFORCE_EFFECTS_MAX 32
/* Each force feedback effect is made of one core effect, which can be
* associated to at most to effect modifiers
@@ -67,24 +64,11 @@
#define FF_CORE_UPDATE 5 /* Effect is being updated */
#define FF_MODCORE_MAX 5
-#define CHECK_OWNERSHIP(i, iforce) \
- ((i) < FF_EFFECTS_MAX && i >= 0 && \
- test_bit(FF_CORE_IS_USED, (iforce)->core_effects[(i)].flags) && \
- (current->pid == 0 || \
- (iforce)->core_effects[(i)].owner == current->pid))
-
struct iforce_core_effect {
/* Information about where modifiers are stored in the device's memory */
struct resource mod1_chunk;
struct resource mod2_chunk;
unsigned long flags[NBITS(FF_MODCORE_MAX)];
- pid_t owner;
- /* Used to keep track of parameters of an effect. They are needed
- * to know what parts of an effect changed in an update operation.
- * We try to send only parameter packets if possible, as sending
- * effect parameter requires the effect to be stoped and restarted
- */
- struct ff_effect effect;
};
#define FF_CMD_EFFECT 0x010e
@@ -145,7 +129,7 @@ struct iforce {
/* Force Feedback */
wait_queue_head_t wait;
struct resource device_memory;
- struct iforce_core_effect core_effects[FF_EFFECTS_MAX];
+ struct iforce_core_effect core_effects[IFORCE_EFFECTS_MAX];
struct mutex mem_mutex;
};
@@ -182,9 +166,9 @@ void iforce_dump_packet(char *msg, u16 cmd, unsigned char *data) ;
int iforce_get_id_packet(struct iforce *iforce, char *packet);
/* iforce-ff.c */
-int iforce_upload_periodic(struct iforce*, struct ff_effect*, int is_update);
-int iforce_upload_constant(struct iforce*, struct ff_effect*, int is_update);
-int iforce_upload_condition(struct iforce*, struct ff_effect*, int is_update);
+int iforce_upload_periodic(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_constant(struct iforce *, struct ff_effect *, struct ff_effect *);
+int iforce_upload_condition(struct iforce *, struct ff_effect *, struct ff_effect *);
/* Public variables */
extern struct serio_driver iforce_serio_drv;
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a9dda56f62c..c62e00c79de 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -121,6 +121,17 @@ config KEYBOARD_NEWTON
To compile this driver as a module, choose M here: the
module will be called newtonkbd.
+config KEYBOARD_STOWAWAY
+ tristate "Stowaway keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Stowaway keyboard on a serial port.
+ Stowaway compatible keyboards like Dicota Input-PDA keyboard
+ are also supported by this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stowaway.
+
config KEYBOARD_CORGI
tristate "Corgi keyboard"
depends on PXA_SHARPSL
@@ -183,4 +194,13 @@ config KEYBOARD_HIL
This driver implements support for HIL-keyboards attached
to your machine, so normally you should say Y here.
+config KEYBOARD_OMAP
+ tristate "TI OMAP keypad support"
+ depends on (ARCH_OMAP1 || ARCH_OMAP2)
+ help
+ Say Y here if you want to use the OMAP keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap-keypad.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 2708167ba17..4c79e7bc9d0 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -11,8 +11,10 @@ obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
index a86afd0a5ef..40244d4ce0f 100644
--- a/drivers/input/keyboard/atkbd.c
+++ b/drivers/input/keyboard/atkbd.c
@@ -652,9 +652,7 @@ static int atkbd_probe(struct atkbd *atkbd)
return 0;
}
- if (param[0] != 0xab && param[0] != 0xac && /* Regular and NCD Sun keyboards */
- param[0] != 0x2b && param[0] != 0x5d && /* Trust keyboard, raw and translated */
- param[0] != 0x60 && param[0] != 0x47) /* NMB SGI keyboard, raw and translated */
+ if (!ps2_is_keyboard_id(param[0]))
return -1;
atkbd->id = (param[0] << 8) | param[1];
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
new file mode 100644
index 00000000000..d436287d1d2
--- /dev/null
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -0,0 +1,492 @@
+/*
+ * linux/drivers/input/keyboard/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/keypad.h>
+#include <asm/arch/menelaus.h>
+#include <asm/irq.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/arch/mux.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static unsigned char keypad_state[8];
+static DEFINE_MUTEX(kp_enable_mutex);
+static int kp_enable = 1;
+static int kp_cur_group = -1;
+
+struct omap_kp {
+ struct input_dev *input;
+ struct timer_list timer;
+ int irq;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned long delay;
+ unsigned int debounce;
+};
+
+DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+static int *keymap;
+static unsigned int *row_gpios;
+static unsigned int *col_gpios;
+
+#ifdef CONFIG_ARCH_OMAP2
+static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
+{
+ int col;
+ for (col = 0; col < omap_kp->cols; col++) {
+ if (value & (1 << col))
+ omap_set_gpio_dataout(col_gpios[col], 1);
+ else
+ omap_set_gpio_dataout(col_gpios[col], 0);
+ }
+}
+
+static u8 get_row_gpio_val(struct omap_kp *omap_kp)
+{
+ int row;
+ u8 value = 0;
+
+ for (row = 0; row < omap_kp->rows; row++) {
+ if (omap_get_gpio_datain(row_gpios[row]))
+ value |= (1 << row);
+ }
+ return value;
+}
+#else
+#define set_col_gpio_val(x, y) do {} while (0)
+#define get_row_gpio_val(x) 0
+#endif
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id,
+ struct pt_regs *regs)
+{
+ struct omap_kp *omap_kp = dev_id;
+
+ /* disable keyboard interrupt and schedule for handling */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ tasklet_schedule(&kp_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+ tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
+{
+ int col = 0;
+
+ /* read the keypad status */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+
+ /* read the keypad status */
+ for (col = 0; col < omap_kp->cols; col++) {
+ set_col_gpio_val(omap_kp, ~(1 << col));
+ state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f;
+ }
+ set_col_gpio_val(omap_kp, 0);
+
+ } else {
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ /* read the keypad status */
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ for (col = 0; col < omap_kp->cols; col++) {
+ omap_writew(~(1 << col) & 0xff,
+ OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+ udelay(omap_kp->delay);
+
+ state[col] = ~omap_readw(OMAP_MPUIO_BASE +
+ OMAP_MPUIO_KBR_LATCH) & 0xff;
+ }
+ omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ udelay(2);
+ }
+}
+
+static inline int omap_kp_find_key(int col, int row)
+{
+ int i, key;
+
+ key = KEY(col, row, 0);
+ for (i = 0; keymap[i] != 0; i++)
+ if ((keymap[i] & 0xff000000) == key)
+ return keymap[i] & 0x00ffffff;
+ return -1;
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+ struct omap_kp *omap_kp_data = (struct omap_kp *) data;
+ unsigned char new_state[8], changed, key_down = 0;
+ int col, row;
+ int spurious = 0;
+
+ /* check for any changes */
+ omap_kp_scan_keypad(omap_kp_data, new_state);
+
+ /* check for changes and print those */
+ for (col = 0; col < omap_kp_data->cols; col++) {
+ changed = new_state[col] ^ keypad_state[col];
+ key_down |= new_state[col];
+ if (changed == 0)
+ continue;
+
+ for (row = 0; row < omap_kp_data->rows; row++) {
+ int key;
+ if (!(changed & (1 << row)))
+ continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+ printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
+ row, (new_state[col] & (1 << row)) ?
+ "pressed" : "released");
+#else
+ key = omap_kp_find_key(col, row);
+ if (key < 0) {
+ printk(KERN_WARNING
+ "omap-keypad: Spurious key event %d-%d\n",
+ col, row);
+ /* We scan again after a couple of seconds */
+ spurious = 1;
+ continue;
+ }
+
+ if (!(kp_cur_group == (key & GROUP_MASK) ||
+ kp_cur_group == -1))
+ continue;
+
+ kp_cur_group = key & GROUP_MASK;
+ input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
+ new_state[col] & (1 << row));
+#endif
+ }
+ }
+ memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+ if (key_down) {
+ int delay = HZ / 20;
+ /* some key is pressed - keep irq disabled and use timer
+ * to poll the keypad */
+ if (spurious)
+ delay = 2 * HZ;
+ mod_timer(&omap_kp_data->timer, jiffies + delay);
+ } else {
+ /* enable interrupts */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp_data->rows; i++)
+ enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else {
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ kp_cur_group = -1;
+ }
+ }
+}
+
+static ssize_t omap_kp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", kp_enable);
+}
+
+static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state;
+
+ if (sscanf(buf, "%u", &state) != 1)
+ return -EINVAL;
+
+ if ((state != 1) && (state != 0))
+ return -EINVAL;
+
+ mutex_lock(&kp_enable_mutex);
+ if (state != kp_enable) {
+ if (state)
+ enable_irq(INT_KEYBOARD);
+ else
+ disable_irq(INT_KEYBOARD);
+ kp_enable = state;
+ }
+ mutex_unlock(&kp_enable_mutex);
+
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
+
+#ifdef CONFIG_PM
+static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static int omap_kp_resume(struct platform_device *dev)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+#else
+#define omap_kp_suspend NULL
+#define omap_kp_resume NULL
+#endif
+
+static int __init omap_kp_probe(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp;
+ struct input_dev *input_dev;
+ struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
+ int i, col_idx, row_idx, irq_idx, ret;
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ printk(KERN_ERR "No rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!omap_kp || !input_dev) {
+ kfree(omap_kp);
+ input_free_device(input_dev);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, omap_kp);
+
+ omap_kp->input = input_dev;
+
+ /* Disable the interrupt for the MPUIO keyboard */
+ if (!cpu_is_omap24xx())
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ keymap = pdata->keymap;
+
+ if (pdata->rep)
+ set_bit(EV_REP, input_dev->evbit);
+
+ if (pdata->delay)
+ omap_kp->delay = pdata->delay;
+
+ if (pdata->row_gpios && pdata->col_gpios) {
+ row_gpios = pdata->row_gpios;
+ col_gpios = pdata->col_gpios;
+ }
+
+ omap_kp->rows = pdata->rows;
+ omap_kp->cols = pdata->cols;
+
+ if (cpu_is_omap24xx()) {
+ /* Cols: outputs */
+ for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) {
+ if (omap_request_gpio(col_gpios[col_idx]) < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ col_gpios[col_idx]);
+ goto err1;
+ }
+ omap_set_gpio_direction(col_gpios[col_idx], 0);
+ }
+ /* Rows: inputs */
+ for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) {
+ if (omap_request_gpio(row_gpios[row_idx]) < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ row_gpios[row_idx]);
+ goto err2;
+ }
+ omap_set_gpio_direction(row_gpios[row_idx], 1);
+ }
+ }
+
+ setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
+
+ /* get the irq and init timer*/
+ tasklet_enable(&kp_tasklet);
+ kp_tasklet.data = (unsigned long) omap_kp;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_enable);
+ if (ret < 0)
+ goto err2;
+
+ /* setup input device */
+ set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; keymap[i] != 0; i++)
+ set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
+ input_dev->name = "omap-keypad";
+ input_dev->phys = "omap-keypad/input0";
+ input_dev->cdev.dev = &pdev->dev;
+ input_dev->private = omap_kp;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->keycode = keymap;
+ input_dev->keycodesize = sizeof(unsigned int);
+ input_dev->keycodemax = pdata->keymapsize;
+
+ ret = input_register_device(omap_kp->input);
+ if (ret < 0) {
+ printk(KERN_ERR "Unable to register omap-keypad input device\n");
+ goto err3;
+ }
+
+ if (pdata->dbounce)
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+
+ /* scan current status and enable interrupt */
+ omap_kp_scan_keypad(omap_kp, keypad_state);
+ if (!cpu_is_omap24xx()) {
+ omap_kp->irq = platform_get_irq(pdev, 0);
+ if (omap_kp->irq >= 0) {
+ if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
+ "omap-keypad", omap_kp) < 0)
+ goto err4;
+ }
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ } else {
+ for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
+ if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
+ omap_kp_interrupt,
+ IRQF_TRIGGER_FALLING,
+ "omap-keypad", omap_kp) < 0)
+ goto err5;
+ }
+ }
+ return 0;
+err5:
+ for (i = irq_idx-1; i >=0; i--)
+ free_irq(row_gpios[i], 0);
+err4:
+ input_unregister_device(omap_kp->input);
+ input_dev = NULL;
+err3:
+ device_remove_file(&pdev->dev, &dev_attr_enable);
+err2:
+ for (i = row_idx-1; i >=0; i--)
+ omap_free_gpio(row_gpios[i]);
+err1:
+ for (i = col_idx-1; i >=0; i--)
+ omap_free_gpio(col_gpios[i]);
+
+ kfree(omap_kp);
+ input_free_device(input_dev);
+
+ return -EINVAL;
+}
+
+static int omap_kp_remove(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp = platform_get_drvdata(pdev);
+
+ /* disable keypad interrupt handling */
+ tasklet_disable(&kp_tasklet);
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->cols; i++)
+ omap_free_gpio(col_gpios[i]);
+ for (i = 0; i < omap_kp->rows; i++) {
+ omap_free_gpio(row_gpios[i]);
+ free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
+ }
+ } else {
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ free_irq(omap_kp->irq, 0);
+ }
+
+ del_timer_sync(&omap_kp->timer);
+ tasklet_kill(&kp_tasklet);
+
+ /* unregister everything */
+ input_unregister_device(omap_kp->input);
+
+ kfree(omap_kp);
+
+ return 0;
+}
+
+static struct platform_driver omap_kp_driver = {
+ .probe = omap_kp_probe,
+ .remove = omap_kp_remove,
+ .suspend = omap_kp_suspend,
+ .resume = omap_kp_resume,
+ .driver = {
+ .name = "omap-keypad",
+ },
+};
+
+static int __devinit omap_kp_init(void)
+{
+ printk(KERN_INFO "OMAP Keypad Driver\n");
+ return platform_driver_register(&omap_kp_driver);
+}
+
+static void __exit omap_kp_exit(void)
+{
+ platform_driver_unregister(&omap_kp_driver);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c
new file mode 100644
index 00000000000..04c54c57f25
--- /dev/null
+++ b/drivers/input/keyboard/stowaway.c
@@ -0,0 +1,187 @@
+/*
+ * Stowaway keyboard driver for Linux
+ */
+
+/*
+ * Copyright (c) 2006 Marek Vasut
+ *
+ * Based on Newton keyboard driver for Linux
+ * by Justin Cormack
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail:
+ * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Stowaway keyboard driver"
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SKBD_KEY_MASK 0x7f
+#define SKBD_RELEASE 0x80
+
+static unsigned char skbd_keycode[128] = {
+ KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7,
+ 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE,
+ KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE,
+ KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0,
+ 0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0,
+ 0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N,
+ KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC,
+ KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P,
+ KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
+ KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT,
+ KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0,
+ KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
+ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0
+};
+
+struct skbd {
+ unsigned char keycode[128];
+ struct input_dev *dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags, struct pt_regs *regs)
+{
+ struct skbd *skbd = serio_get_drvdata(serio);
+ struct input_dev *dev = skbd->dev;
+
+ if (skbd->keycode[data & SKBD_KEY_MASK]) {
+ input_regs(dev, regs);
+ input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK],
+ !(data & SKBD_RELEASE));
+ input_sync(dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int skbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct skbd *skbd;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!skbd || !input_dev)
+ goto fail1;
+
+ skbd->serio = serio;
+ skbd->dev = input_dev;
+ snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys);
+ memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode));
+
+ input_dev->name = "Stowaway Keyboard";
+ input_dev->phys = skbd->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_STOWAWAY;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->cdev.dev = &serio->dev;
+ input_dev->private = skbd;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->keycode = skbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(skbd_keycode);
+ for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++)
+ set_bit(skbd_keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ serio_set_drvdata(serio, skbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(skbd->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(skbd);
+ return err;
+}
+
+static void skbd_disconnect(struct serio *serio)
+{
+ struct skbd *skbd = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(skbd->dev);
+ kfree(skbd);
+}
+
+static struct serio_device_id skbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_STOWAWAY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, skbd_serio_ids);
+
+static struct serio_driver skbd_drv = {
+ .driver = {
+ .name = "stowaway",
+ },
+ .description = DRIVER_DESC,
+ .id_table = skbd_serio_ids,
+ .interrupt = skbd_interrupt,
+ .connect = skbd_connect,
+ .disconnect = skbd_disconnect,
+};
+
+static int __init skbd_init(void)
+{
+ serio_register_driver(&skbd_drv);
+ return 0;
+}
+
+static void __exit skbd_exit(void)
+{
+ serio_unregister_driver(&skbd_drv);
+}
+
+module_init(skbd_init);
+module_exit(skbd_exit);
diff --git a/drivers/input/misc/uinput.c b/drivers/input/misc/uinput.c
index d723e9ad7c4..9516439b7c7 100644
--- a/drivers/input/misc/uinput.c
+++ b/drivers/input/misc/uinput.c
@@ -20,6 +20,9 @@
* Author: Aristeu Sergio Rozanski Filho <aris@cathedrallabs.org>
*
* Changes/Revisions:
+ * 0.3 09/04/2006 (Anssi Hannula <anssi.hannula@gmail.com>)
+ * - updated ff support for the changes in kernel interface
+ * - added MODULE_VERSION
* 0.2 16/10/2004 (Micah Dowty <micah@navi.cx>)
* - added force feedback support
* - added UI_SET_PHYS
@@ -107,18 +110,31 @@ static int uinput_request_submit(struct input_dev *dev, struct uinput_request *r
return request->retval;
}
-static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect)
+static void uinput_dev_set_gain(struct input_dev *dev, u16 gain)
+{
+ uinput_dev_event(dev, EV_FF, FF_GAIN, gain);
+}
+
+static void uinput_dev_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ uinput_dev_event(dev, EV_FF, FF_AUTOCENTER, magnitude);
+}
+
+static int uinput_dev_playback(struct input_dev *dev, int effect_id, int value)
+{
+ return uinput_dev_event(dev, EV_FF, effect_id, value);
+}
+
+static int uinput_dev_upload_effect(struct input_dev *dev, struct ff_effect *effect, struct ff_effect *old)
{
struct uinput_request request;
int retval;
- if (!test_bit(EV_FF, dev->evbit))
- return -ENOSYS;
-
request.id = -1;
init_completion(&request.done);
request.code = UI_FF_UPLOAD;
- request.u.effect = effect;
+ request.u.upload.effect = effect;
+ request.u.upload.old = old;
retval = uinput_request_reserve_slot(dev->private, &request);
if (!retval)
@@ -168,6 +184,7 @@ static void uinput_destroy_device(struct uinput_device *udev)
static int uinput_create_device(struct uinput_device *udev)
{
+ struct input_dev *dev = udev->dev;
int error;
if (udev->state != UIST_SETUP_COMPLETE) {
@@ -175,15 +192,29 @@ static int uinput_create_device(struct uinput_device *udev)
return -EINVAL;
}
- error = input_register_device(udev->dev);
- if (error) {
- uinput_destroy_device(udev);
- return error;
+ if (udev->ff_effects_max) {
+ error = input_ff_create(dev, udev->ff_effects_max);
+ if (error)
+ goto fail1;
+
+ dev->ff->upload = uinput_dev_upload_effect;
+ dev->ff->erase = uinput_dev_erase_effect;
+ dev->ff->playback = uinput_dev_playback;
+ dev->ff->set_gain = uinput_dev_set_gain;
+ dev->ff->set_autocenter = uinput_dev_set_autocenter;
}
+ error = input_register_device(udev->dev);
+ if (error)
+ goto fail2;
+
udev->state = UIST_CREATED;
return 0;
+
+ fail2: input_ff_destroy(dev);
+ fail1: uinput_destroy_device(udev);
+ return error;
}
static int uinput_open(struct inode *inode, struct file *file)
@@ -243,8 +274,6 @@ static int uinput_allocate_device(struct uinput_device *udev)
return -ENOMEM;
udev->dev->event = uinput_dev_event;
- udev->dev->upload_effect = uinput_dev_upload_effect;
- udev->dev->erase_effect = uinput_dev_erase_effect;
udev->dev->private = udev;
return 0;
@@ -278,6 +307,8 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
goto exit;
}
+ udev->ff_effects_max = user_dev->ff_effects_max;
+
size = strnlen(user_dev->name, UINPUT_MAX_NAME_SIZE) + 1;
if (!size) {
retval = -EINVAL;
@@ -296,7 +327,6 @@ static int uinput_setup_device(struct uinput_device *udev, const char __user *bu
dev->id.vendor = user_dev->id.vendor;
dev->id.product = user_dev->id.product;
dev->id.version = user_dev->id.version;
- dev->ff_effects_max = user_dev->ff_effects_max;
size = sizeof(int) * (ABS_MAX + 1);
memcpy(dev->absmax, user_dev->absmax, size);
@@ -525,12 +555,17 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
req = uinput_request_find(udev, ff_up.request_id);
- if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) {
+ if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
retval = -EINVAL;
break;
}
ff_up.retval = 0;
- memcpy(&ff_up.effect, req->u.effect, sizeof(struct ff_effect));
+ memcpy(&ff_up.effect, req->u.upload.effect, sizeof(struct ff_effect));
+ if (req->u.upload.old)
+ memcpy(&ff_up.old, req->u.upload.old, sizeof(struct ff_effect));
+ else
+ memset(&ff_up.old, 0, sizeof(struct ff_effect));
+
if (copy_to_user(p, &ff_up, sizeof(ff_up))) {
retval = -EFAULT;
break;
@@ -561,12 +596,11 @@ static long uinput_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
break;
}
req = uinput_request_find(udev, ff_up.request_id);
- if (!(req && req->code == UI_FF_UPLOAD && req->u.effect)) {
+ if (!(req && req->code == UI_FF_UPLOAD && req->u.upload.effect)) {
retval = -EINVAL;
break;
}
req->retval = ff_up.retval;
- memcpy(req->u.effect, &ff_up.effect, sizeof(struct ff_effect));
uinput_request_done(udev, req);
break;
@@ -622,6 +656,7 @@ static void __exit uinput_exit(void)
MODULE_AUTHOR("Aristeu Sergio Rozanski Filho");
MODULE_DESCRIPTION("User level driver support for input subsystem");
MODULE_LICENSE("GPL");
+MODULE_VERSION("0.3");
module_init(uinput_init);
module_exit(uinput_exit);
diff --git a/drivers/input/misc/wistron_btns.c b/drivers/input/misc/wistron_btns.c
index de0f46dd969..4639537336f 100644
--- a/drivers/input/misc/wistron_btns.c
+++ b/drivers/input/misc/wistron_btns.c
@@ -248,13 +248,10 @@ static int __init dmi_matched(struct dmi_system_id *dmi)
keymap = dmi->driver_data;
for (key = keymap; key->type != KE_END; key++) {
- if (key->type == KE_WIFI) {
+ if (key->type == KE_WIFI)
have_wifi = 1;
- break;
- } else if (key->type == KE_BLUETOOTH) {
+ else if (key->type == KE_BLUETOOTH)
have_bluetooth = 1;
- break;
- }
}
return 1;
}
@@ -389,7 +386,16 @@ static struct dmi_system_id dmi_ids[] __initdata = {
},
.driver_data = keymap_acer_travelmate_240
},
- {
+ {
+ .callback = dmi_matched,
+ .ident = "Acer TravelMate 2424NWXCi",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "TravelMate 2420"),
+ },
+ .driver_data = keymap_acer_travelmate_240
+ },
+ {
.callback = dmi_matched,
.ident = "AOpen 1559AS",
.matches = {
diff --git a/drivers/input/mouse/alps.c b/drivers/input/mouse/alps.c
index 070d75330af..450b68a619f 100644
--- a/drivers/input/mouse/alps.c
+++ b/drivers/input/mouse/alps.c
@@ -36,7 +36,7 @@
#define ALPS_PASS 0x20
#define ALPS_FW_BK_2 0x40
-static struct alps_model_info alps_model_data[] = {
+static const struct alps_model_info alps_model_data[] = {
{ { 0x33, 0x02, 0x0a }, 0x88, 0xf8, ALPS_OLDPROTO }, /* UMAX-530T */
{ { 0x53, 0x02, 0x0a }, 0xf8, 0xf8, 0 },
{ { 0x53, 0x02, 0x14 }, 0xf8, 0xf8, 0 },
@@ -209,10 +209,10 @@ static psmouse_ret_t alps_process_byte(struct psmouse *psmouse, struct pt_regs *
return PSMOUSE_GOOD_DATA;
}
-static struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
+static const struct alps_model_info *alps_get_model(struct psmouse *psmouse, int *version)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
- unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
+ static const unsigned char rates[] = { 0, 10, 20, 40, 60, 80, 100, 200 };
unsigned char param[4];
int i;
@@ -504,7 +504,7 @@ init_fail:
int alps_detect(struct psmouse *psmouse, int set_properties)
{
int version;
- struct alps_model_info *model;
+ const struct alps_model_info *model;
if (!(model = alps_get_model(psmouse, &version)))
return -1;
diff --git a/drivers/input/mouse/alps.h b/drivers/input/mouse/alps.h
index e428f8d5d12..69db7325a49 100644
--- a/drivers/input/mouse/alps.h
+++ b/drivers/input/mouse/alps.h
@@ -25,7 +25,7 @@ struct alps_data {
struct input_dev *dev2; /* Relative device */
char name[32]; /* Name */
char phys[32]; /* Phys */
- struct alps_model_info *i; /* Info */
+ const struct alps_model_info *i;/* Info */
int prev_fin; /* Finger bit from previous packet */
};
diff --git a/drivers/input/mouse/lifebook.c b/drivers/input/mouse/lifebook.c
index c14395ba798..5e9d2506751 100644
--- a/drivers/input/mouse/lifebook.c
+++ b/drivers/input/mouse/lifebook.c
@@ -115,13 +115,15 @@ static int lifebook_absolute_mode(struct psmouse *psmouse)
static void lifebook_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
- unsigned char params[] = { 0, 1, 2, 2, 3 };
+ static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+ unsigned char p;
if (resolution == 0 || resolution > 400)
resolution = 400;
- ps2_command(&psmouse->ps2dev, &params[resolution / 100], PSMOUSE_CMD_SETRES);
- psmouse->resolution = 50 << params[resolution / 100];
+ p = params[resolution / 100];
+ ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+ psmouse->resolution = 50 << p;
}
static void lifebook_disconnect(struct psmouse *psmouse)
diff --git a/drivers/input/mouse/logips2pp.c b/drivers/input/mouse/logips2pp.c
index 54b696cfe1e..7972eecbcfe 100644
--- a/drivers/input/mouse/logips2pp.c
+++ b/drivers/input/mouse/logips2pp.c
@@ -30,9 +30,9 @@
#define PS2PP_NAV_BTN 0x20
struct ps2pp_info {
- const int model;
- unsigned const int kind;
- unsigned const int features;
+ u8 model;
+ u8 kind;
+ u16 features;
};
/*
@@ -199,9 +199,9 @@ static void ps2pp_disconnect(struct psmouse *psmouse)
device_remove_file(&psmouse->ps2dev.serio->dev, &psmouse_attr_smartscroll.dattr);
}
-static struct ps2pp_info *get_model_info(unsigned char model)
+static const struct ps2pp_info *get_model_info(unsigned char model)
{
- static struct ps2pp_info ps2pp_list[] = {
+ static const struct ps2pp_info ps2pp_list[] = {
{ 12, 0, PS2PP_SIDE_BTN},
{ 13, 0, 0 },
{ 15, PS2PP_KIND_MX, /* MX1000 */
@@ -215,6 +215,7 @@ static struct ps2pp_info *get_model_info(unsigned char model)
{ 51, 0, 0 },
{ 52, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL },
{ 53, PS2PP_KIND_WHEEL, PS2PP_WHEEL },
+ { 56, PS2PP_KIND_WHEEL, PS2PP_SIDE_BTN | PS2PP_WHEEL }, /* Cordless MouseMan Wheel */
{ 61, PS2PP_KIND_MX, /* MX700 */
PS2PP_WHEEL | PS2PP_SIDE_BTN | PS2PP_TASK_BTN |
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
@@ -244,12 +245,11 @@ static struct ps2pp_info *get_model_info(unsigned char model)
PS2PP_EXTRA_BTN | PS2PP_NAV_BTN },
{ 114, PS2PP_KIND_MX, /* MX310 */
PS2PP_WHEEL | PS2PP_SIDE_BTN |
- PS2PP_TASK_BTN | PS2PP_EXTRA_BTN },
- { }
+ PS2PP_TASK_BTN | PS2PP_EXTRA_BTN }
};
int i;
- for (i = 0; ps2pp_list[i].model; i++)
+ for (i = 0; i < ARRAY_SIZE(ps2pp_list); i++)
if (model == ps2pp_list[i].model)
return &ps2pp_list[i];
@@ -261,7 +261,8 @@ static struct ps2pp_info *get_model_info(unsigned char model)
* Set up input device's properties based on the detected mouse model.
*/
-static void ps2pp_set_model_properties(struct psmouse *psmouse, struct ps2pp_info *model_info,
+static void ps2pp_set_model_properties(struct psmouse *psmouse,
+ const struct ps2pp_info *model_info,
int using_ps2pp)
{
struct input_dev *input_dev = psmouse->dev;
@@ -327,7 +328,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[4];
unsigned char model, buttons;
- struct ps2pp_info *model_info;
+ const struct ps2pp_info *model_info;
int use_ps2pp = 0;
param[0] = 0;
@@ -349,7 +350,7 @@ int ps2pp_init(struct psmouse *psmouse, int set_properties)
/*
* Do Logitech PS2++ / PS2T++ magic init.
*/
- if (model == 97) { /* Touch Pad 3 */
+ if (model_info->kind == PS2PP_KIND_TP3) { /* Touch Pad 3 */
/* Unprotect RAM */
param[0] = 0x11; param[1] = 0x04; param[2] = 0x68;
diff --git a/drivers/input/mouse/psmouse-base.c b/drivers/input/mouse/psmouse-base.c
index 343afa38f4c..9fb7eb6b0f7 100644
--- a/drivers/input/mouse/psmouse-base.c
+++ b/drivers/input/mouse/psmouse-base.c
@@ -112,8 +112,8 @@ static struct workqueue_struct *kpsmoused_wq;
struct psmouse_protocol {
enum psmouse_type type;
- char *name;
- char *alias;
+ const char *name;
+ const char *alias;
int maxproto;
int (*detect)(struct psmouse *, int);
int (*init)(struct psmouse *);
@@ -507,15 +507,17 @@ static int thinking_detect(struct psmouse *psmouse, int set_properties)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[2];
- unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20, 0 };
+ static const unsigned char seq[] = { 20, 60, 40, 20, 20, 60, 40, 20, 20 };
int i;
param[0] = 10;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
param[0] = 0;
ps2_command(ps2dev, param, PSMOUSE_CMD_SETRES);
- for (i = 0; seq[i]; i++)
- ps2_command(ps2dev, seq + i, PSMOUSE_CMD_SETRATE);
+ for (i = 0; i < ARRAY_SIZE(seq); i++) {
+ param[0] = seq[i];
+ ps2_command(ps2dev, param, PSMOUSE_CMD_SETRATE);
+ }
ps2_command(ps2dev, param, PSMOUSE_CMD_GETID);
if (param[0] != 2)
@@ -652,7 +654,7 @@ static int psmouse_extensions(struct psmouse *psmouse,
return PSMOUSE_PS2;
}
-static struct psmouse_protocol psmouse_protocols[] = {
+static const struct psmouse_protocol psmouse_protocols[] = {
{
.type = PSMOUSE_PS2,
.name = "PS/2",
@@ -726,7 +728,7 @@ static struct psmouse_protocol psmouse_protocols[] = {
},
};
-static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
+static const struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
{
int i;
@@ -738,9 +740,9 @@ static struct psmouse_protocol *psmouse_protocol_by_type(enum psmouse_type type)
return &psmouse_protocols[0];
}
-static struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
+static const struct psmouse_protocol *psmouse_protocol_by_name(const char *name, size_t len)
{
- struct psmouse_protocol *p;
+ const struct psmouse_protocol *p;
int i;
for (i = 0; i < ARRAY_SIZE(psmouse_protocols); i++) {
@@ -795,13 +797,15 @@ static int psmouse_probe(struct psmouse *psmouse)
void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
{
- unsigned char params[] = { 0, 1, 2, 2, 3 };
+ static const unsigned char params[] = { 0, 1, 2, 2, 3 };
+ unsigned char p;
if (resolution == 0 || resolution > 200)
resolution = 200;
- ps2_command(&psmouse->ps2dev, &params[resolution / 50], PSMOUSE_CMD_SETRES);
- psmouse->resolution = 25 << params[resolution / 50];
+ p = params[resolution / 50];
+ ps2_command(&psmouse->ps2dev, &p, PSMOUSE_CMD_SETRES);
+ psmouse->resolution = 25 << p;
}
/*
@@ -810,12 +814,14 @@ void psmouse_set_resolution(struct psmouse *psmouse, unsigned int resolution)
static void psmouse_set_rate(struct psmouse *psmouse, unsigned int rate)
{
- unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+ static const unsigned char rates[] = { 200, 100, 80, 60, 40, 20, 10, 0 };
+ unsigned char r;
int i = 0;
while (rates[i] > rate) i++;
- ps2_command(&psmouse->ps2dev, &rates[i], PSMOUSE_CMD_SETRATE);
- psmouse->rate = rates[i];
+ r = rates[i];
+ ps2_command(&psmouse->ps2dev, &r, PSMOUSE_CMD_SETRATE);
+ psmouse->rate = r;
}
/*
@@ -1031,7 +1037,7 @@ static void psmouse_disconnect(struct serio *serio)
mutex_unlock(&psmouse_mutex);
}
-static int psmouse_switch_protocol(struct psmouse *psmouse, struct psmouse_protocol *proto)
+static int psmouse_switch_protocol(struct psmouse *psmouse, const struct psmouse_protocol *proto)
{
struct input_dev *input_dev = psmouse->dev;
@@ -1362,7 +1368,7 @@ static ssize_t psmouse_attr_set_protocol(struct psmouse *psmouse, void *data, co
struct serio *serio = psmouse->ps2dev.serio;
struct psmouse *parent = NULL;
struct input_dev *new_dev;
- struct psmouse_protocol *proto;
+ const struct psmouse_protocol *proto;
int retry = 0;
if (!(proto = psmouse_protocol_by_name(buf, count)))
@@ -1459,7 +1465,7 @@ static ssize_t psmouse_attr_set_resolution(struct psmouse *psmouse, void *data,
static int psmouse_set_maxproto(const char *val, struct kernel_param *kp)
{
- struct psmouse_protocol *proto;
+ const struct psmouse_protocol *proto;
if (!val)
return -EINVAL;
diff --git a/drivers/input/mouse/sermouse.c b/drivers/input/mouse/sermouse.c
index 0023501a5b6..680b3235388 100644
--- a/drivers/input/mouse/sermouse.c
+++ b/drivers/input/mouse/sermouse.c
@@ -42,7 +42,7 @@ MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
-static char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
+static const char *sermouse_protocols[] = { "None", "Mouse Systems Mouse", "Sun Mouse", "Microsoft Mouse",
"Logitech M+ Mouse", "Microsoft MZ Mouse", "Logitech MZ+ Mouse",
"Logitech MZ++ Mouse"};
diff --git a/drivers/input/mouse/synaptics.c b/drivers/input/mouse/synaptics.c
index ad5d0a85e96..392108c436b 100644
--- a/drivers/input/mouse/synaptics.c
+++ b/drivers/input/mouse/synaptics.c
@@ -430,11 +430,11 @@ static void synaptics_process_packet(struct psmouse *psmouse)
static int synaptics_validate_byte(unsigned char packet[], int idx, unsigned char pkt_type)
{
- static unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
- static unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
- static unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
- static unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
- static unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
+ static const unsigned char newabs_mask[] = { 0xC8, 0x00, 0x00, 0xC8, 0x00 };
+ static const unsigned char newabs_rel_mask[] = { 0xC0, 0x00, 0x00, 0xC0, 0x00 };
+ static const unsigned char newabs_rslt[] = { 0x80, 0x00, 0x00, 0xC0, 0x00 };
+ static const unsigned char oldabs_mask[] = { 0xC0, 0x60, 0x00, 0xC0, 0x60 };
+ static const unsigned char oldabs_rslt[] = { 0xC0, 0x00, 0x00, 0x80, 0x00 };
if (idx < 0 || idx > 4)
return 0;
diff --git a/drivers/input/mousedev.c b/drivers/input/mousedev.c
index 1f851acab30..a22a74a2a3d 100644
--- a/drivers/input/mousedev.c
+++ b/drivers/input/mousedev.c
@@ -614,7 +614,7 @@ static unsigned int mousedev_poll(struct file *file, poll_table *wait)
(list->mousedev->exist ? 0 : (POLLHUP | POLLERR));
}
-static struct file_operations mousedev_fops = {
+static const struct file_operations mousedev_fops = {
.owner = THIS_MODULE,
.read = mousedev_read,
.write = mousedev_write,
@@ -624,7 +624,8 @@ static struct file_operations mousedev_fops = {
.fasync = mousedev_fasync,
};
-static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev, struct input_device_id *id)
+static struct input_handle *mousedev_connect(struct input_handler *handler, struct input_dev *dev,
+ const struct input_device_id *id)
{
struct mousedev *mousedev;
struct class_device *cdev;
@@ -688,7 +689,7 @@ static void mousedev_disconnect(struct input_handle *handle)
}
}
-static struct input_device_id mousedev_ids[] = {
+static const struct input_device_id mousedev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
@@ -737,7 +738,12 @@ static int psaux_registered;
static int __init mousedev_init(void)
{
- input_register_handler(&mousedev_handler);
+ struct class_device *cdev;
+ int error;
+
+ error = input_register_handler(&mousedev_handler);
+ if (error)
+ return error;
memset(&mousedev_mix, 0, sizeof(struct mousedev));
INIT_LIST_HEAD(&mousedev_mix.list);
@@ -746,12 +752,20 @@ static int __init mousedev_init(void)
mousedev_mix.exist = 1;
mousedev_mix.minor = MOUSEDEV_MIX;
- class_device_create(&input_class, NULL,
+ cdev = class_device_create(&input_class, NULL,
MKDEV(INPUT_MAJOR, MOUSEDEV_MINOR_BASE + MOUSEDEV_MIX), NULL, "mice");
+ if (IS_ERR(cdev)) {
+ input_unregister_handler(&mousedev_handler);
+ return PTR_ERR(cdev);
+ }
#ifdef CONFIG_INPUT_MOUSEDEV_PSAUX
- if (!(psaux_registered = !misc_register(&psaux_mouse)))
- printk(KERN_WARNING "mice: could not misc_register the device\n");
+ error = misc_register(&psaux_mouse);
+ if (error)
+ printk(KERN_WARNING "mice: could not register psaux device, "
+ "error: %d\n", error);
+ else
+ psaux_registered = 1;
#endif
printk(KERN_INFO "mice: PS/2 mouse device common for all mice\n");
diff --git a/drivers/input/power.c b/drivers/input/power.c
index 51a519e24b6..ee82464a2fa 100644
--- a/drivers/input/power.c
+++ b/drivers/input/power.c
@@ -98,7 +98,7 @@ static void power_event(struct input_handle *handle, unsigned int type,
static struct input_handle *power_connect(struct input_handler *handler,
struct input_dev *dev,
- struct input_device_id *id)
+ const struct input_device_id *id)
{
struct input_handle *handle;
@@ -120,7 +120,7 @@ static void power_disconnect(struct input_handle *handle)
kfree(handle);
}
-static struct input_device_id power_ids[] = {
+static const struct input_device_id power_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT,
.evbit = { BIT(EV_KEY) },
@@ -150,8 +150,7 @@ static struct input_handler power_handler = {
static int __init power_init(void)
{
- input_register_handler(&power_handler);
- return 0;
+ return input_register_handler(&power_handler);
}
static void __exit power_exit(void)
diff --git a/drivers/input/serio/i8042-io.h b/drivers/input/serio/i8042-io.h
index cc21914fbc7..3b4e13b9ce1 100644
--- a/drivers/input/serio/i8042-io.h
+++ b/drivers/input/serio/i8042-io.h
@@ -67,25 +67,22 @@ static inline int i8042_platform_init(void)
* On some platforms touching the i8042 data register region can do really
* bad things. Because of this the region is always reserved on such boxes.
*/
-#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__) && !defined(CONFIG_PPC_MERGE)
- if (!request_region(I8042_DATA_REG, 16, "i8042"))
- return -EBUSY;
-#endif
-
- i8042_reset = 1;
-
#if defined(CONFIG_PPC_MERGE)
if (check_legacy_ioport(I8042_DATA_REG))
- return -EBUSY;
+ return -ENODEV;
+#endif
+#if !defined(__sh__) && !defined(__alpha__) && !defined(__mips__)
if (!request_region(I8042_DATA_REG, 16, "i8042"))
return -EBUSY;
#endif
+
+ i8042_reset = 1;
return 0;
}
static inline void i8042_platform_exit(void)
{
-#if !defined(__sh__) && !defined(__alpha__) && !defined(CONFIG_PPC64)
+#if !defined(__sh__) && !defined(__alpha__)
release_region(I8042_DATA_REG, 16);
#endif
}
diff --git a/drivers/input/serio/i8042-x86ia64io.h b/drivers/input/serio/i8042-x86ia64io.h
index f606e96bc2f..8738edda661 100644
--- a/drivers/input/serio/i8042-x86ia64io.h
+++ b/drivers/input/serio/i8042-x86ia64io.h
@@ -160,6 +160,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
},
},
{
+ .ident = "Toshiba Equium A110",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "EQUIUM A110"),
+ },
+ },
+ {
.ident = "Alienware Sentia",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ALIENWARE"),
@@ -180,6 +187,13 @@ static struct dmi_system_id __initdata i8042_dmi_nomux_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "VGN-FS115B"),
},
},
+ {
+ .ident = "Amoi M636/A737",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Amoi Electronics CO.,LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "M636/A737 platform"),
+ },
+ },
{ }
};
diff --git a/drivers/input/serio/i8042.c b/drivers/input/serio/i8042.c
index 06a3f25657d..1bb0c76a925 100644
--- a/drivers/input/serio/i8042.c
+++ b/drivers/input/serio/i8042.c
@@ -90,46 +90,24 @@ static DEFINE_SPINLOCK(i8042_lock);
struct i8042_port {
struct serio *serio;
int irq;
- unsigned char disable;
- unsigned char irqen;
unsigned char exists;
signed char mux;
- char name[8];
};
#define I8042_KBD_PORT_NO 0
#define I8042_AUX_PORT_NO 1
#define I8042_MUX_PORT_NO 2
#define I8042_NUM_PORTS (I8042_NUM_MUX_PORTS + 2)
-static struct i8042_port i8042_ports[I8042_NUM_PORTS] = {
- {
- .disable = I8042_CTR_KBDDIS,
- .irqen = I8042_CTR_KBDINT,
- .mux = -1,
- .name = "KBD",
- },
- {
- .disable = I8042_CTR_AUXDIS,
- .irqen = I8042_CTR_AUXINT,
- .mux = -1,
- .name = "AUX",
- }
-};
+
+static struct i8042_port i8042_ports[I8042_NUM_PORTS];
static unsigned char i8042_initial_ctr;
static unsigned char i8042_ctr;
-static unsigned char i8042_mux_open;
static unsigned char i8042_mux_present;
-static struct timer_list i8042_timer;
+static unsigned char i8042_kbd_irq_registered;
+static unsigned char i8042_aux_irq_registered;
static struct platform_device *i8042_platform_device;
-
-/*
- * Shared IRQ's require a device pointer, but this driver doesn't support
- * multiple devices
- */
-#define i8042_request_irq_cookie (&i8042_timer)
-
static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
/*
@@ -141,6 +119,7 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs);
static int i8042_wait_read(void)
{
int i = 0;
+
while ((~i8042_read_status() & I8042_STR_OBF) && (i < I8042_CTL_TIMEOUT)) {
udelay(50);
i++;
@@ -151,6 +130,7 @@ static int i8042_wait_read(void)
static int i8042_wait_write(void)
{
int i = 0;
+
while ((i8042_read_status() & I8042_STR_IBF) && (i < I8042_CTL_TIMEOUT)) {
udelay(50);
i++;
@@ -192,48 +172,57 @@ static int i8042_flush(void)
* encoded in bits 8-11 of the command number.
*/
-static int i8042_command(unsigned char *param, int command)
+static int __i8042_command(unsigned char *param, int command)
{
- unsigned long flags;
- int i, retval, auxerr = 0;
+ int i, error;
if (i8042_noloop && command == I8042_CMD_AUX_LOOP)
return -1;
- spin_lock_irqsave(&i8042_lock, flags);
-
- if ((retval = i8042_wait_write()))
- goto out;
+ error = i8042_wait_write();
+ if (error)
+ return error;
dbg("%02x -> i8042 (command)", command & 0xff);
i8042_write_command(command & 0xff);
for (i = 0; i < ((command >> 12) & 0xf); i++) {
- if ((retval = i8042_wait_write()))
- goto out;
+ error = i8042_wait_write();
+ if (error)
+ return error;
dbg("%02x -> i8042 (parameter)", param[i]);
i8042_write_data(param[i]);
}
for (i = 0; i < ((command >> 8) & 0xf); i++) {
- if ((retval = i8042_wait_read()))
- goto out;
+ error = i8042_wait_read();
+ if (error) {
+ dbg(" -- i8042 (timeout)");
+ return error;
+ }
if (command == I8042_CMD_AUX_LOOP &&
!(i8042_read_status() & I8042_STR_AUXDATA)) {
- retval = auxerr = -1;
- goto out;
+ dbg(" -- i8042 (auxerr)");
+ return -1;
}
param[i] = i8042_read_data();
dbg("%02x <- i8042 (return)", param[i]);
}
- if (retval)
- dbg(" -- i8042 (%s)", auxerr ? "auxerr" : "timeout");
+ return 0;
+}
- out:
+static int i8042_command(unsigned char *param, int command)
+{
+ unsigned long flags;
+ int retval;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+ retval = __i8042_command(param, command);
spin_unlock_irqrestore(&i8042_lock, flags);
+
return retval;
}
@@ -248,7 +237,7 @@ static int i8042_kbd_write(struct serio *port, unsigned char c)
spin_lock_irqsave(&i8042_lock, flags);
- if(!(retval = i8042_wait_write())) {
+ if (!(retval = i8042_wait_write())) {
dbg("%02x -> i8042 (kbd-data)", c);
i8042_write_data(c);
}
@@ -287,100 +276,6 @@ static int i8042_aux_write(struct serio *serio, unsigned char c)
}
/*
- * i8042_activate_port() enables port on a chip.
- */
-
-static int i8042_activate_port(struct i8042_port *port)
-{
- if (!port->serio)
- return -1;
-
- i8042_flush();
-
- /*
- * Enable port again here because it is disabled if we are
- * resuming (normally it is enabled already).
- */
- i8042_ctr &= ~port->disable;
-
- i8042_ctr |= port->irqen;
-
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- i8042_ctr &= ~port->irqen;
- return -1;
- }
-
- return 0;
-}
-
-
-/*
- * i8042_open() is called when a port is open by the higher layer.
- * It allocates the interrupt and calls i8042_enable_port.
- */
-
-static int i8042_open(struct serio *serio)
-{
- struct i8042_port *port = serio->port_data;
-
- if (port->mux != -1)
- if (i8042_mux_open++)
- return 0;
-
- if (request_irq(port->irq, i8042_interrupt,
- IRQF_SHARED, "i8042", i8042_request_irq_cookie)) {
- printk(KERN_ERR "i8042.c: Can't get irq %d for %s, unregistering the port.\n", port->irq, port->name);
- goto irq_fail;
- }
-
- if (i8042_activate_port(port)) {
- printk(KERN_ERR "i8042.c: Can't activate %s, unregistering the port\n", port->name);
- goto activate_fail;
- }
-
- i8042_interrupt(0, NULL, NULL);
-
- return 0;
-
- activate_fail:
- free_irq(port->irq, i8042_request_irq_cookie);
-
- irq_fail:
- serio_unregister_port_delayed(serio);
-
- return -1;
-}
-
-/*
- * i8042_close() frees the interrupt, so that it can possibly be used
- * by another driver. We never know - if the user doesn't have a mouse,
- * the BIOS could have used the AUX interrupt for PCI.
- */
-
-static void i8042_close(struct serio *serio)
-{
- struct i8042_port *port = serio->port_data;
-
- if (port->mux != -1)
- if (--i8042_mux_open)
- return;
-
- i8042_ctr &= ~port->irqen;
-
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_WARNING "i8042.c: Can't write CTR while closing %s.\n", port->name);
-/*
- * We still want to continue and free IRQ so if more data keeps coming in
- * kernel will just ignore the irq.
- */
- }
-
- free_irq(port->irq, i8042_request_irq_cookie);
-
- i8042_flush();
-}
-
-/*
* i8042_start() is called by serio core when port is about to finish
* registering. It will mark port as existing so i8042_interrupt can
* start sending data through it.
@@ -423,8 +318,6 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
unsigned int port_no;
int ret;
- mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
-
spin_lock_irqsave(&i8042_lock, flags);
str = i8042_read_status();
if (unlikely(~str & I8042_STR_OBF)) {
@@ -480,8 +373,8 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
port = &i8042_ports[port_no];
- dbg("%02x <- i8042 (interrupt, %s, %d%s%s)",
- data, port->name, irq,
+ dbg("%02x <- i8042 (interrupt, %d, %d%s%s)",
+ data, port_no, irq,
dfl & SERIO_PARITY ? ", bad parity" : "",
dfl & SERIO_TIMEOUT ? ", timeout" : "");
@@ -494,6 +387,58 @@ static irqreturn_t i8042_interrupt(int irq, void *dev_id, struct pt_regs *regs)
}
/*
+ * i8042_enable_kbd_port enables keybaord port on chip
+ */
+
+static int i8042_enable_kbd_port(void)
+{
+ i8042_ctr &= ~I8042_CTR_KBDDIS;
+ i8042_ctr |= I8042_CTR_KBDINT;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042.c: Failed to enable KBD port.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * i8042_enable_aux_port enables AUX (mouse) port on chip
+ */
+
+static int i8042_enable_aux_port(void)
+{
+ i8042_ctr &= ~I8042_CTR_AUXDIS;
+ i8042_ctr |= I8042_CTR_AUXINT;
+
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042.c: Failed to enable AUX port.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * i8042_enable_mux_ports enables 4 individual AUX ports after
+ * the controller has been switched into Multiplexed mode
+ */
+
+static int i8042_enable_mux_ports(void)
+{
+ unsigned char param;
+ int i;
+
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+ i8042_command(&param, I8042_CMD_MUX_PFX + i);
+ i8042_command(&param, I8042_CMD_AUX_ENABLE);
+ }
+
+ return i8042_enable_aux_port();
+}
+
+/*
* i8042_set_mux_mode checks whether the controller has an active
* multiplexor and puts the chip into Multiplexed (1) or Legacy (0) mode.
*/
@@ -510,8 +455,7 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
/*
* Internal loopback test - send three bytes, they should come back from the
- * mouse interface, the last should be version. Note that we negate mouseport
- * command responses for the i8042_check_aux() routine.
+ * mouse interface, the last should be version.
*/
param = 0xf0;
@@ -530,67 +474,67 @@ static int i8042_set_mux_mode(unsigned int mode, unsigned char *mux_version)
return 0;
}
-
/*
- * i8042_enable_mux_ports enables 4 individual AUX ports after
- * the controller has been switched into Multiplexed mode
+ * i8042_check_mux() checks whether the controller supports the PS/2 Active
+ * Multiplexing specification by Synaptics, Phoenix, Insyde and
+ * LCS/Telegraphics.
*/
-static int i8042_enable_mux_ports(void)
+static int __devinit i8042_check_mux(void)
{
- unsigned char param;
- int i;
+ unsigned char mux_version;
+
+ if (i8042_set_mux_mode(1, &mux_version))
+ return -1;
+
/*
- * Disable all muxed ports by disabling AUX.
+ * Workaround for interference with USB Legacy emulation
+ * that causes a v10.12 MUX to be found.
*/
+ if (mux_version == 0xAC)
+ return -1;
+
+ printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
+ (mux_version >> 4) & 0xf, mux_version & 0xf);
+/*
+ * Disable all muxed ports by disabling AUX.
+ */
i8042_ctr |= I8042_CTR_AUXDIS;
i8042_ctr &= ~I8042_CTR_AUXINT;
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Failed to disable AUX port, can't use MUX.\n");
- return -1;
+ return -EIO;
}
-/*
- * Enable all muxed ports.
- */
-
- for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
- i8042_command(&param, I8042_CMD_MUX_PFX + i);
- i8042_command(&param, I8042_CMD_AUX_ENABLE);
- }
+ i8042_mux_present = 1;
return 0;
}
-
/*
- * i8042_check_mux() checks whether the controller supports the PS/2 Active
- * Multiplexing specification by Synaptics, Phoenix, Insyde and
- * LCS/Telegraphics.
+ * The following is used to test AUX IRQ delivery.
*/
+static struct completion i8042_aux_irq_delivered __devinitdata;
+static int i8042_irq_being_tested __devinitdata;
-static int __devinit i8042_check_mux(void)
+static irqreturn_t __devinit i8042_aux_test_irq(int irq, void *dev_id, struct pt_regs *regs)
{
- unsigned char mux_version;
-
- if (i8042_set_mux_mode(1, &mux_version))
- return -1;
-
- /* Workaround for interference with USB Legacy emulation */
- /* that causes a v10.12 MUX to be found. */
- if (mux_version == 0xAC)
- return -1;
-
- printk(KERN_INFO "i8042.c: Detected active multiplexing controller, rev %d.%d.\n",
- (mux_version >> 4) & 0xf, mux_version & 0xf);
+ unsigned long flags;
+ unsigned char str, data;
- if (i8042_enable_mux_ports())
- return -1;
+ spin_lock_irqsave(&i8042_lock, flags);
+ str = i8042_read_status();
+ if (str & I8042_STR_OBF) {
+ data = i8042_read_data();
+ if (i8042_irq_being_tested &&
+ data == 0xa5 && (str & I8042_STR_AUXDATA))
+ complete(&i8042_aux_irq_delivered);
+ }
+ spin_unlock_irqrestore(&i8042_lock, flags);
- i8042_mux_present = 1;
- return 0;
+ return IRQ_HANDLED;
}
@@ -601,18 +545,10 @@ static int __devinit i8042_check_mux(void)
static int __devinit i8042_check_aux(void)
{
+ int retval = -1;
+ int irq_registered = 0;
+ unsigned long flags;
unsigned char param;
- static int i8042_check_aux_cookie;
-
-/*
- * Check if AUX irq is available. If it isn't, then there is no point
- * in trying to detect AUX presence.
- */
-
- if (request_irq(i8042_ports[I8042_AUX_PORT_NO].irq, i8042_interrupt,
- IRQF_SHARED, "i8042", &i8042_check_aux_cookie))
- return -1;
- free_irq(i8042_ports[I8042_AUX_PORT_NO].irq, &i8042_check_aux_cookie);
/*
* Get rid of bytes in the queue.
@@ -637,9 +573,9 @@ static int __devinit i8042_check_aux(void)
* AUX ports, we test for this only when the LOOP command failed.
*/
- if (i8042_command(&param, I8042_CMD_AUX_TEST)
- || (param && param != 0xfa && param != 0xff))
- return -1;
+ if (i8042_command(&param, I8042_CMD_AUX_TEST) ||
+ (param && param != 0xfa && param != 0xff))
+ return -1;
}
/*
@@ -659,54 +595,80 @@ static int __devinit i8042_check_aux(void)
return -1;
/*
- * Disable the interface.
+ * Test AUX IRQ delivery to make sure BIOS did not grab the IRQ and
+ * used it for a PCI card or somethig else.
*/
- i8042_ctr |= I8042_CTR_AUXDIS;
- i8042_ctr &= ~I8042_CTR_AUXINT;
+ if (i8042_noloop) {
+/*
+ * Without LOOP command we can't test AUX IRQ delivery. Assume the port
+ * is working and hope we are right.
+ */
+ retval = 0;
+ goto out;
+ }
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
- return -1;
+ if (request_irq(I8042_AUX_IRQ, i8042_aux_test_irq, IRQF_SHARED,
+ "i8042", i8042_platform_device))
+ goto out;
- return 0;
-}
+ irq_registered = 1;
+
+ if (i8042_enable_aux_port())
+ goto out;
+
+ spin_lock_irqsave(&i8042_lock, flags);
+ init_completion(&i8042_aux_irq_delivered);
+ i8042_irq_being_tested = 1;
+
+ param = 0xa5;
+ retval = __i8042_command(&param, I8042_CMD_AUX_LOOP & 0xf0ff);
+
+ spin_unlock_irqrestore(&i8042_lock, flags);
+
+ if (retval)
+ goto out;
+ if (wait_for_completion_timeout(&i8042_aux_irq_delivered,
+ msecs_to_jiffies(250)) == 0) {
/*
- * i8042_port_register() marks the device as existing,
- * registers it, and reports to the user.
+ * AUX IRQ was never delivered so we need to flush the controller to
+ * get rid of the byte we put there; otherwise keyboard may not work.
*/
+ i8042_flush();
+ retval = -1;
+ }
-static int __devinit i8042_port_register(struct i8042_port *port)
-{
- i8042_ctr &= ~port->disable;
+ out:
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_WARNING "i8042.c: Can't write CTR while registering.\n");
- kfree(port->serio);
- port->serio = NULL;
- i8042_ctr |= port->disable;
- return -EIO;
- }
+/*
+ * Disable the interface.
+ */
- printk(KERN_INFO "serio: i8042 %s port at %#lx,%#lx irq %d\n",
- port->name,
- (unsigned long) I8042_DATA_REG,
- (unsigned long) I8042_COMMAND_REG,
- port->irq);
+ i8042_ctr |= I8042_CTR_AUXDIS;
+ i8042_ctr &= ~I8042_CTR_AUXINT;
- serio_register_port(port->serio);
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+ retval = -1;
- return 0;
-}
+ if (irq_registered)
+ free_irq(I8042_AUX_IRQ, i8042_platform_device);
+ return retval;
+}
-static void i8042_timer_func(unsigned long data)
+static int i8042_controller_check(void)
{
- i8042_interrupt(0, NULL, NULL);
+ if (i8042_flush() == I8042_BUFFER_SIZE) {
+ printk(KERN_ERR "i8042.c: No controller found.\n");
+ return -ENODEV;
+ }
+
+ return 0;
}
-static int i8042_ctl_test(void)
+static int i8042_controller_selftest(void)
{
unsigned char param;
@@ -715,13 +677,13 @@ static int i8042_ctl_test(void)
if (i8042_command(&param, I8042_CMD_CTL_TEST)) {
printk(KERN_ERR "i8042.c: i8042 controller self test timeout.\n");
- return -1;
+ return -ENODEV;
}
if (param != I8042_RET_CTL_TEST) {
printk(KERN_ERR "i8042.c: i8042 controller selftest failed. (%#x != %#x)\n",
param, I8042_RET_CTL_TEST);
- return -1;
+ return -EIO;
}
return 0;
@@ -738,25 +700,12 @@ static int i8042_controller_init(void)
unsigned long flags;
/*
- * Test the i8042. We need to know if it thinks it's working correctly
- * before doing anything else.
- */
-
- if (i8042_flush() == I8042_BUFFER_SIZE) {
- printk(KERN_ERR "i8042.c: No controller found.\n");
- return -1;
- }
-
- if (i8042_ctl_test())
- return -1;
-
-/*
* Save the CTR for restoral on unload / reboot.
*/
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_RCTR)) {
printk(KERN_ERR "i8042.c: Can't read CTR while initializing i8042.\n");
- return -1;
+ return -EIO;
}
i8042_initial_ctr = i8042_ctr;
@@ -805,7 +754,7 @@ static int i8042_controller_init(void)
if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
printk(KERN_ERR "i8042.c: Can't write CTR while initializing i8042.\n");
- return -1;
+ return -EIO;
}
return 0;
@@ -813,15 +762,12 @@ static int i8042_controller_init(void)
/*
- * Reset the controller.
+ * Reset the controller and reset CRT to the original value set by BIOS.
*/
+
static void i8042_controller_reset(void)
{
-/*
- * Reset the controller if requested.
- */
-
- i8042_ctl_test();
+ i8042_flush();
/*
* Disable MUX mode if present.
@@ -831,12 +777,16 @@ static void i8042_controller_reset(void)
i8042_set_mux_mode(0, NULL);
/*
- * Restore the original control register setting.
+ * Reset the controller if requested.
*/
- i8042_ctr = i8042_initial_ctr;
+ i8042_controller_selftest();
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR))
+/*
+ * Restore the original control register setting.
+ */
+
+ if (i8042_command(&i8042_initial_ctr, I8042_CMD_CTL_WCTR))
printk(KERN_WARNING "i8042.c: Can't restore CTR.\n");
}
@@ -850,14 +800,12 @@ static void i8042_controller_cleanup(void)
{
int i;
- i8042_flush();
-
/*
* Reset anything that is connected to the ports.
*/
for (i = 0; i < I8042_NUM_PORTS; i++)
- if (i8042_ports[i].exists)
+ if (i8042_ports[i].serio)
serio_cleanup(i8042_ports[i].serio);
i8042_controller_reset();
@@ -913,8 +861,7 @@ static long i8042_panic_blink(long count)
static int i8042_suspend(struct platform_device *dev, pm_message_t state)
{
- del_timer_sync(&i8042_timer);
- i8042_controller_reset();
+ i8042_controller_cleanup();
return 0;
}
@@ -926,33 +873,39 @@ static int i8042_suspend(struct platform_device *dev, pm_message_t state)
static int i8042_resume(struct platform_device *dev)
{
- int i;
+ int error;
- if (i8042_ctl_test())
- return -1;
+ error = i8042_controller_check();
+ if (error)
+ return error;
- if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
- printk(KERN_ERR "i8042: Can't write CTR\n");
- return -1;
- }
-
- if (i8042_mux_present)
- if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
- printk(KERN_WARNING "i8042: failed to resume active multiplexor, mouse won't work.\n");
+ error = i8042_controller_selftest();
+ if (error)
+ return error;
/*
- * Activate all ports.
+ * Restore pre-resume CTR value and disable all ports
*/
- for (i = 0; i < I8042_NUM_PORTS; i++)
- i8042_activate_port(&i8042_ports[i]);
+ i8042_ctr |= I8042_CTR_AUXDIS | I8042_CTR_KBDDIS;
+ i8042_ctr &= ~(I8042_CTR_AUXINT | I8042_CTR_KBDINT);
+ if (i8042_command(&i8042_ctr, I8042_CMD_CTL_WCTR)) {
+ printk(KERN_ERR "i8042: Can't write CTR to resume\n");
+ return -EIO;
+ }
-/*
- * Restart timer (for polling "stuck" data)
- */
- mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+ if (i8042_mux_present) {
+ if (i8042_set_mux_mode(1, NULL) || i8042_enable_mux_ports())
+ printk(KERN_WARNING
+ "i8042: failed to resume active multiplexor, "
+ "mouse won't work.\n");
+ } else if (i8042_ports[I8042_AUX_PORT_NO].serio)
+ i8042_enable_aux_port();
- panic_blink = i8042_panic_blink;
+ if (i8042_ports[I8042_KBD_PORT_NO].serio)
+ i8042_enable_kbd_port();
+
+ i8042_interrupt(0, NULL, NULL);
return 0;
}
@@ -978,24 +931,24 @@ static int __devinit i8042_create_kbd_port(void)
serio->id.type = i8042_direct ? SERIO_8042 : SERIO_8042_XL;
serio->write = i8042_dumbkbd ? NULL : i8042_kbd_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
serio->start = i8042_start;
serio->stop = i8042_stop;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
- strlcpy(serio->name, "i8042 Kbd Port", sizeof(serio->name));
+ strlcpy(serio->name, "i8042 KBD port", sizeof(serio->name));
strlcpy(serio->phys, I8042_KBD_PHYS_DESC, sizeof(serio->phys));
port->serio = serio;
+ port->irq = I8042_KBD_IRQ;
- return i8042_port_register(port);
+ return 0;
}
-static int __devinit i8042_create_aux_port(void)
+static int __devinit i8042_create_aux_port(int idx)
{
struct serio *serio;
- struct i8042_port *port = &i8042_ports[I8042_AUX_PORT_NO];
+ int port_no = idx < 0 ? I8042_AUX_PORT_NO : I8042_MUX_PORT_NO + idx;
+ struct i8042_port *port = &i8042_ports[port_no];
serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
if (!serio)
@@ -1003,111 +956,191 @@ static int __devinit i8042_create_aux_port(void)
serio->id.type = SERIO_8042;
serio->write = i8042_aux_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
serio->start = i8042_start;
serio->stop = i8042_stop;
serio->port_data = port;
serio->dev.parent = &i8042_platform_device->dev;
- strlcpy(serio->name, "i8042 Aux Port", sizeof(serio->name));
- strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+ if (idx < 0) {
+ strlcpy(serio->name, "i8042 AUX port", sizeof(serio->name));
+ strlcpy(serio->phys, I8042_AUX_PHYS_DESC, sizeof(serio->phys));
+ } else {
+ snprintf(serio->name, sizeof(serio->name), "i8042 AUX%d port", idx);
+ snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, idx + 1);
+ }
port->serio = serio;
+ port->mux = idx;
+ port->irq = I8042_AUX_IRQ;
- return i8042_port_register(port);
+ return 0;
}
-static int __devinit i8042_create_mux_port(int index)
+static void __devinit i8042_free_kbd_port(void)
{
- struct serio *serio;
- struct i8042_port *port = &i8042_ports[I8042_MUX_PORT_NO + index];
+ kfree(i8042_ports[I8042_KBD_PORT_NO].serio);
+ i8042_ports[I8042_KBD_PORT_NO].serio = NULL;
+}
- serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
- if (!serio)
- return -ENOMEM;
+static void __devinit i8042_free_aux_ports(void)
+{
+ int i;
- serio->id.type = SERIO_8042;
- serio->write = i8042_aux_write;
- serio->open = i8042_open;
- serio->close = i8042_close;
- serio->start = i8042_start;
- serio->stop = i8042_stop;
- serio->port_data = port;
- serio->dev.parent = &i8042_platform_device->dev;
- snprintf(serio->name, sizeof(serio->name), "i8042 Aux-%d Port", index);
- snprintf(serio->phys, sizeof(serio->phys), I8042_MUX_PHYS_DESC, index + 1);
+ for (i = I8042_AUX_PORT_NO; i < I8042_NUM_PORTS; i++) {
+ kfree(i8042_ports[i].serio);
+ i8042_ports[i].serio = NULL;
+ }
+}
- *port = i8042_ports[I8042_AUX_PORT_NO];
- port->exists = 0;
- snprintf(port->name, sizeof(port->name), "AUX%d", index);
- port->mux = index;
- port->serio = serio;
+static void __devinit i8042_register_ports(void)
+{
+ int i;
- return i8042_port_register(port);
+ for (i = 0; i < I8042_NUM_PORTS; i++) {
+ if (i8042_ports[i].serio) {
+ printk(KERN_INFO "serio: %s at %#lx,%#lx irq %d\n",
+ i8042_ports[i].serio->name,
+ (unsigned long) I8042_DATA_REG,
+ (unsigned long) I8042_COMMAND_REG,
+ i8042_ports[i].irq);
+ serio_register_port(i8042_ports[i].serio);
+ }
+ }
}
-static int __devinit i8042_probe(struct platform_device *dev)
+static void __devinit i8042_unregister_ports(void)
{
- int i, have_ports = 0;
- int err;
+ int i;
- init_timer(&i8042_timer);
- i8042_timer.function = i8042_timer_func;
+ for (i = 0; i < I8042_NUM_PORTS; i++) {
+ if (i8042_ports[i].serio) {
+ serio_unregister_port(i8042_ports[i].serio);
+ i8042_ports[i].serio = NULL;
+ }
+ }
+}
+
+static void i8042_free_irqs(void)
+{
+ if (i8042_aux_irq_registered)
+ free_irq(I8042_AUX_IRQ, i8042_platform_device);
+ if (i8042_kbd_irq_registered)
+ free_irq(I8042_KBD_IRQ, i8042_platform_device);
+
+ i8042_aux_irq_registered = i8042_kbd_irq_registered = 0;
+}
+
+static int __devinit i8042_setup_aux(void)
+{
+ int (*aux_enable)(void);
+ int error;
+ int i;
- if (i8042_controller_init())
+ if (i8042_check_aux())
return -ENODEV;
- if (!i8042_noaux && !i8042_check_aux()) {
- if (!i8042_nomux && !i8042_check_mux()) {
- for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
- err = i8042_create_mux_port(i);
- if (err)
- goto err_unregister_ports;
- }
- } else {
- err = i8042_create_aux_port();
- if (err)
- goto err_unregister_ports;
+ if (i8042_nomux || i8042_check_mux()) {
+ error = i8042_create_aux_port(-1);
+ if (error)
+ goto err_free_ports;
+ aux_enable = i8042_enable_aux_port;
+ } else {
+ for (i = 0; i < I8042_NUM_MUX_PORTS; i++) {
+ error = i8042_create_aux_port(i);
+ if (error)
+ goto err_free_ports;
}
- have_ports = 1;
+ aux_enable = i8042_enable_mux_ports;
}
- if (!i8042_nokbd) {
- err = i8042_create_kbd_port();
- if (err)
- goto err_unregister_ports;
- have_ports = 1;
- }
+ error = request_irq(I8042_AUX_IRQ, i8042_interrupt, IRQF_SHARED,
+ "i8042", i8042_platform_device);
+ if (error)
+ goto err_free_ports;
- if (!have_ports) {
- err = -ENODEV;
- goto err_controller_cleanup;
- }
+ if (aux_enable())
+ goto err_free_irq;
- mod_timer(&i8042_timer, jiffies + I8042_POLL_PERIOD);
+ i8042_aux_irq_registered = 1;
return 0;
- err_unregister_ports:
- for (i = 0; i < I8042_NUM_PORTS; i++)
- if (i8042_ports[i].serio)
- serio_unregister_port(i8042_ports[i].serio);
- err_controller_cleanup:
- i8042_controller_cleanup();
+ err_free_irq:
+ free_irq(I8042_AUX_IRQ, i8042_platform_device);
+ err_free_ports:
+ i8042_free_aux_ports();
+ return error;
+}
- return err;
+static int __devinit i8042_setup_kbd(void)
+{
+ int error;
+
+ error = i8042_create_kbd_port();
+ if (error)
+ return error;
+
+ error = request_irq(I8042_KBD_IRQ, i8042_interrupt, IRQF_SHARED,
+ "i8042", i8042_platform_device);
+ if (error)
+ goto err_free_port;
+
+ error = i8042_enable_kbd_port();
+ if (error)
+ goto err_free_irq;
+
+ i8042_kbd_irq_registered = 1;
+ return 0;
+
+ err_free_irq:
+ free_irq(I8042_KBD_IRQ, i8042_platform_device);
+ err_free_port:
+ i8042_free_kbd_port();
+ return error;
}
-static int __devexit i8042_remove(struct platform_device *dev)
+static int __devinit i8042_probe(struct platform_device *dev)
{
- int i;
+ int error;
- i8042_controller_cleanup();
+ error = i8042_controller_selftest();
+ if (error)
+ return error;
- for (i = 0; i < I8042_NUM_PORTS; i++)
- if (i8042_ports[i].exists)
- serio_unregister_port(i8042_ports[i].serio);
+ error = i8042_controller_init();
+ if (error)
+ return error;
+
+ if (!i8042_noaux) {
+ error = i8042_setup_aux();
+ if (error && error != -ENODEV && error != -EBUSY)
+ goto out_fail;
+ }
+
+ if (!i8042_nokbd) {
+ error = i8042_setup_kbd();
+ if (error)
+ goto out_fail;
+ }
- del_timer_sync(&i8042_timer);
+/*
+ * Ok, everything is ready, let's register all serio ports
+ */
+ i8042_register_ports();
+
+ return 0;
+
+ out_fail:
+ i8042_free_aux_ports(); /* in case KBD failed but AUX not */
+ i8042_free_irqs();
+ i8042_controller_reset();
+
+ return error;
+}
+
+static int __devexit i8042_remove(struct platform_device *dev)
+{
+ i8042_unregister_ports();
+ i8042_free_irqs();
+ i8042_controller_reset();
return 0;
}
@@ -1134,8 +1167,9 @@ static int __init i8042_init(void)
if (err)
return err;
- i8042_ports[I8042_AUX_PORT_NO].irq = I8042_AUX_IRQ;
- i8042_ports[I8042_KBD_PORT_NO].irq = I8042_KBD_IRQ;
+ err = i8042_controller_check();
+ if (err)
+ goto err_platform_exit;
err = platform_driver_register(&i8042_driver);
if (err)
@@ -1151,6 +1185,8 @@ static int __init i8042_init(void)
if (err)
goto err_free_device;
+ panic_blink = i8042_panic_blink;
+
return 0;
err_free_device:
@@ -1167,7 +1203,6 @@ static void __exit i8042_exit(void)
{
platform_device_unregister(i8042_platform_device);
platform_driver_unregister(&i8042_driver);
-
i8042_platform_exit();
panic_blink = NULL;
diff --git a/drivers/input/serio/i8042.h b/drivers/input/serio/i8042.h
index af526ab9ec0..b3eb7a72d96 100644
--- a/drivers/input/serio/i8042.h
+++ b/drivers/input/serio/i8042.h
@@ -37,15 +37,6 @@
#define I8042_CTL_TIMEOUT 10000
/*
- * When the device isn't opened and it's interrupts aren't used, we poll it at
- * regular intervals to see if any characters arrived. If yes, we can start
- * probing for any mouse / keyboard connected. This is the period of the
- * polling.
- */
-
-#define I8042_POLL_PERIOD HZ/20
-
-/*
* Status register bits.
*/
diff --git a/drivers/input/serio/libps2.c b/drivers/input/serio/libps2.c
index ed202f2f251..dcb16b5cbec 100644
--- a/drivers/input/serio/libps2.c
+++ b/drivers/input/serio/libps2.c
@@ -27,15 +27,6 @@ MODULE_AUTHOR("Dmitry Torokhov <dtor@mail.ru>");
MODULE_DESCRIPTION("PS/2 driver library");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(ps2_init);
-EXPORT_SYMBOL(ps2_sendbyte);
-EXPORT_SYMBOL(ps2_drain);
-EXPORT_SYMBOL(ps2_command);
-EXPORT_SYMBOL(ps2_schedule_command);
-EXPORT_SYMBOL(ps2_handle_ack);
-EXPORT_SYMBOL(ps2_handle_response);
-EXPORT_SYMBOL(ps2_cmd_aborted);
-
/* Work structure to schedule execution of a command */
struct ps2work {
struct work_struct work;
@@ -71,6 +62,7 @@ int ps2_sendbyte(struct ps2dev *ps2dev, unsigned char byte, int timeout)
return -ps2dev->nak;
}
+EXPORT_SYMBOL(ps2_sendbyte);
/*
* ps2_drain() waits for device to transmit requested number of bytes
@@ -96,15 +88,16 @@ void ps2_drain(struct ps2dev *ps2dev, int maxbytes, int timeout)
msecs_to_jiffies(timeout));
mutex_unlock(&ps2dev->cmd_mutex);
}
+EXPORT_SYMBOL(ps2_drain);
/*
* ps2_is_keyboard_id() checks received ID byte against the list of
* known keyboard IDs.
*/
-static inline int ps2_is_keyboard_id(char id_byte)
+int ps2_is_keyboard_id(char id_byte)
{
- static char keyboard_ids[] = {
+ const static char keyboard_ids[] = {
0xab, /* Regular keyboards */
0xac, /* NCD Sun keyboard */
0x2b, /* Trust keyboard, translated */
@@ -115,6 +108,7 @@ static inline int ps2_is_keyboard_id(char id_byte)
return memchr(keyboard_ids, id_byte, sizeof(keyboard_ids)) != NULL;
}
+EXPORT_SYMBOL(ps2_is_keyboard_id);
/*
* ps2_adjust_timeout() is called after receiving 1st byte of command
@@ -139,6 +133,19 @@ static int ps2_adjust_timeout(struct ps2dev *ps2dev, int command, int timeout)
case PS2_CMD_GETID:
/*
+ * Microsoft Natural Elite keyboard responds to
+ * the GET ID command as it were a mouse, with
+ * a single byte. Fail the command so atkbd will
+ * use alternative probe to detect it.
+ */
+ if (ps2dev->cmdbuf[1] == 0xaa) {
+ serio_pause_rx(ps2dev->serio);
+ ps2dev->flags = 0;
+ serio_continue_rx(ps2dev->serio);
+ timeout = 0;
+ }
+
+ /*
* If device behind the port is not a keyboard there
* won't be 2nd byte of ID response.
*/
@@ -237,6 +244,7 @@ int ps2_command(struct ps2dev *ps2dev, unsigned char *param, int command)
mutex_unlock(&ps2dev->cmd_mutex);
return rc;
}
+EXPORT_SYMBOL(ps2_command);
/*
* ps2_execute_scheduled_command() sends a command, previously scheduled by
@@ -279,6 +287,7 @@ int ps2_schedule_command(struct ps2dev *ps2dev, unsigned char *param, int comman
return 0;
}
+EXPORT_SYMBOL(ps2_schedule_command);
/*
* ps2_init() initializes ps2dev structure
@@ -290,6 +299,7 @@ void ps2_init(struct ps2dev *ps2dev, struct serio *serio)
init_waitqueue_head(&ps2dev->wait);
ps2dev->serio = serio;
}
+EXPORT_SYMBOL(ps2_init);
/*
* ps2_handle_ack() is supposed to be used in interrupt handler
@@ -335,6 +345,7 @@ int ps2_handle_ack(struct ps2dev *ps2dev, unsigned char data)
return 1;
}
+EXPORT_SYMBOL(ps2_handle_ack);
/*
* ps2_handle_response() is supposed to be used in interrupt handler
@@ -360,6 +371,7 @@ int ps2_handle_response(struct ps2dev *ps2dev, unsigned char data)
return 1;
}
+EXPORT_SYMBOL(ps2_handle_response);
void ps2_cmd_aborted(struct ps2dev *ps2dev)
{
@@ -371,4 +383,4 @@ void ps2_cmd_aborted(struct ps2dev *ps2dev)
ps2dev->flags = 0;
}
-
+EXPORT_SYMBOL(ps2_cmd_aborted);
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index b1b14f8d4dd..9418bbe4707 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -108,4 +108,40 @@ config TOUCHSCREEN_HP600
To compile this driver as a module, choose M here: the
module will be called hp680_ts_input.
+config TOUCHSCREEN_PENMOUNT
+ tristate "Penmount serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Penmount serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called penmount.
+
+config TOUCHSCREEN_TOUCHRIGHT
+ tristate "Touchright serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchright serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchright.
+
+config TOUCHSCREEN_TOUCHWIN
+ tristate "Touchwin serial touchscreen"
+ select SERIO
+ help
+ Say Y here if you have a Touchwin serial touchscreen connected to
+ your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called touchwin.
+
endif
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 5e5557c4312..1abb8f10d60 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -12,3 +12,6 @@ obj-$(CONFIG_TOUCHSCREEN_ELO) += elo.o
obj-$(CONFIG_TOUCHSCREEN_MTOUCH) += mtouch.o
obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
+obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o
+obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o
diff --git a/drivers/input/touchscreen/elo.c b/drivers/input/touchscreen/elo.c
index c86a2eb310f..ab565335ee4 100644
--- a/drivers/input/touchscreen/elo.c
+++ b/drivers/input/touchscreen/elo.c
@@ -23,6 +23,7 @@
#include <linux/input.h>
#include <linux/serio.h>
#include <linux/init.h>
+#include <linux/ctype.h>
#define DRIVER_DESC "Elo serial touchscreen driver"
@@ -34,7 +35,19 @@ MODULE_LICENSE("GPL");
* Definitions & global arrays.
*/
-#define ELO_MAX_LENGTH 10
+#define ELO_MAX_LENGTH 10
+
+#define ELO10_PACKET_LEN 8
+#define ELO10_TOUCH 0x03
+#define ELO10_PRESSURE 0x80
+
+#define ELO10_LEAD_BYTE 'U'
+
+#define ELO10_ID_CMD 'i'
+
+#define ELO10_TOUCH_PACKET 'T'
+#define ELO10_ACK_PACKET 'A'
+#define ELI10_ID_PACKET 'I'
/*
* Per-touchscreen data.
@@ -43,51 +56,67 @@ MODULE_LICENSE("GPL");
struct elo {
struct input_dev *dev;
struct serio *serio;
+ struct mutex cmd_mutex;
+ struct completion cmd_done;
int id;
int idx;
+ unsigned char expected_packet;
unsigned char csum;
unsigned char data[ELO_MAX_LENGTH];
+ unsigned char response[ELO10_PACKET_LEN];
char phys[32];
};
-static void elo_process_data_10(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_10(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
- elo->csum += elo->data[elo->idx] = data;
-
+ elo->data[elo->idx] = data;
switch (elo->idx++) {
-
case 0:
- if (data != 'U') {
+ elo->csum = 0xaa;
+ if (data != ELO10_LEAD_BYTE) {
+ pr_debug("elo: unsynchronized data: 0x%02x\n", data);
elo->idx = 0;
- elo->csum = 0;
- }
- break;
-
- case 1:
- if (data != 'T') {
- elo->idx = 0;
- elo->csum = 0;
}
break;
case 9:
- if (elo->csum) {
+ elo->idx = 0;
+ if (data != elo->csum) {
+ pr_debug("elo: bad checksum: 0x%02x, expected 0x%02x\n",
+ data, elo->csum);
+ break;
+ }
+ if (elo->data[1] != elo->expected_packet) {
+ if (elo->data[1] != ELO10_TOUCH_PACKET)
+ pr_debug("elo: unexpected packet: 0x%02x\n",
+ elo->data[1]);
+ break;
+ }
+ if (likely(elo->data[1] == ELO10_TOUCH_PACKET)) {
input_regs(dev, regs);
input_report_abs(dev, ABS_X, (elo->data[4] << 8) | elo->data[3]);
input_report_abs(dev, ABS_Y, (elo->data[6] << 8) | elo->data[5]);
- input_report_abs(dev, ABS_PRESSURE, (elo->data[8] << 8) | elo->data[7]);
- input_report_key(dev, BTN_TOUCH, elo->data[8] || elo->data[7]);
+ if (elo->data[2] & ELO10_PRESSURE)
+ input_report_abs(dev, ABS_PRESSURE,
+ (elo->data[8] << 8) | elo->data[7]);
+ input_report_key(dev, BTN_TOUCH, elo->data[2] & ELO10_TOUCH);
input_sync(dev);
+ } else if (elo->data[1] == ELO10_ACK_PACKET) {
+ if (elo->data[2] == '0')
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ complete(&elo->cmd_done);
+ } else {
+ memcpy(elo->response, &elo->data[1], ELO10_PACKET_LEN);
+ elo->expected_packet = ELO10_ACK_PACKET;
}
- elo->idx = 0;
- elo->csum = 0;
break;
}
+ elo->csum += data;
}
-static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_6(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
@@ -135,7 +164,7 @@ static void elo_process_data_6(struct elo* elo, unsigned char data, struct pt_re
}
}
-static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_regs *regs)
+static void elo_process_data_3(struct elo *elo, unsigned char data, struct pt_regs *regs)
{
struct input_dev *dev = elo->dev;
@@ -161,7 +190,7 @@ static void elo_process_data_3(struct elo* elo, unsigned char data, struct pt_re
static irqreturn_t elo_interrupt(struct serio *serio,
unsigned char data, unsigned int flags, struct pt_regs *regs)
{
- struct elo* elo = serio_get_drvdata(serio);
+ struct elo *elo = serio_get_drvdata(serio);
switch(elo->id) {
case 0:
@@ -181,17 +210,81 @@ static irqreturn_t elo_interrupt(struct serio *serio,
return IRQ_HANDLED;
}
+static int elo_command_10(struct elo *elo, unsigned char *packet)
+{
+ int rc = -1;
+ int i;
+ unsigned char csum = 0xaa + ELO10_LEAD_BYTE;
+
+ mutex_lock(&elo->cmd_mutex);
+
+ serio_pause_rx(elo->serio);
+ elo->expected_packet = toupper(packet[0]);
+ init_completion(&elo->cmd_done);
+ serio_continue_rx(elo->serio);
+
+ if (serio_write(elo->serio, ELO10_LEAD_BYTE))
+ goto out;
+
+ for (i = 0; i < ELO10_PACKET_LEN; i++) {
+ csum += packet[i];
+ if (serio_write(elo->serio, packet[i]))
+ goto out;
+ }
+
+ if (serio_write(elo->serio, csum))
+ goto out;
+
+ wait_for_completion_timeout(&elo->cmd_done, HZ);
+
+ if (elo->expected_packet == ELO10_TOUCH_PACKET) {
+ /* We are back in reporting mode, the command was ACKed */
+ memcpy(packet, elo->response, ELO10_PACKET_LEN);
+ rc = 0;
+ }
+
+ out:
+ mutex_unlock(&elo->cmd_mutex);
+ return rc;
+}
+
+static int elo_setup_10(struct elo *elo)
+{
+ static const char *elo_types[] = { "Accu", "Dura", "Intelli", "Carroll" };
+ struct input_dev *dev = elo->dev;
+ unsigned char packet[ELO10_PACKET_LEN] = { ELO10_ID_CMD };
+
+ if (elo_command_10(elo, packet))
+ return -1;
+
+ dev->id.version = (packet[5] << 8) | packet[4];
+
+ input_set_abs_params(dev, ABS_X, 96, 4000, 0, 0);
+ input_set_abs_params(dev, ABS_Y, 96, 4000, 0, 0);
+ if (packet[3] & ELO10_PRESSURE)
+ input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0);
+
+ printk(KERN_INFO "elo: %sTouch touchscreen, fw: %02x.%02x, "
+ "features: %x02x, controller: 0x%02x\n",
+ elo_types[(packet[1] -'0') & 0x03],
+ packet[5], packet[4], packet[3], packet[7]);
+
+ return 0;
+}
+
/*
* elo_disconnect() is the opposite of elo_connect()
*/
static void elo_disconnect(struct serio *serio)
{
- struct elo* elo = serio_get_drvdata(serio);
+ struct elo *elo = serio_get_drvdata(serio);
+ input_get_device(elo->dev);
input_unregister_device(elo->dev);
serio_close(serio);
serio_set_drvdata(serio, NULL);
+ input_put_device(elo->dev);
kfree(elo);
}
@@ -211,12 +304,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
input_dev = input_allocate_device();
if (!elo || !input_dev) {
err = -ENOMEM;
- goto fail;
+ goto fail1;
}
elo->serio = serio;
elo->id = serio->id.id;
elo->dev = input_dev;
+ elo->expected_packet = ELO10_TOUCH_PACKET;
+ mutex_init(&elo->cmd_mutex);
+ init_completion(&elo->cmd_done);
snprintf(elo->phys, sizeof(elo->phys), "%s/input0", serio->phys);
input_dev->private = elo;
@@ -231,12 +327,17 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ serio_set_drvdata(serio, elo);
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
switch (elo->id) {
case 0: /* 10-byte protocol */
- input_set_abs_params(input_dev, ABS_X, 96, 4000, 0, 0);
- input_set_abs_params(input_dev, ABS_Y, 96, 4000, 0, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, 255, 0, 0);
+ if (elo_setup_10(elo))
+ goto fail3;
+
break;
case 1: /* 6-byte protocol */
@@ -253,17 +354,15 @@ static int elo_connect(struct serio *serio, struct serio_driver *drv)
break;
}
- serio_set_drvdata(serio, elo);
-
- err = serio_open(serio, drv);
+ err = input_register_device(elo->dev);
if (err)
- goto fail;
+ goto fail3;
- input_register_device(elo->dev);
return 0;
- fail: serio_set_drvdata(serio, NULL);
- input_free_device(input_dev);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
kfree(elo);
return err;
}
diff --git a/drivers/input/touchscreen/hp680_ts_input.c b/drivers/input/touchscreen/hp680_ts_input.c
index fa97e0f79e7..ee6c2f40cdf 100644
--- a/drivers/input/touchscreen/hp680_ts_input.c
+++ b/drivers/input/touchscreen/hp680_ts_input.c
@@ -15,7 +15,6 @@
#define HP680_TS_ABS_Y_MIN 80
#define HP680_TS_ABS_Y_MAX 910
-#define SCPCR 0xa4000116
#define PHDR 0xa400012e
#define SCPDR 0xa4000136
@@ -77,19 +76,6 @@ static irqreturn_t hp680_ts_interrupt(int irq, void *dev, struct pt_regs *regs)
static int __init hp680_ts_init(void)
{
- u8 scpdr;
- u16 scpcr;
-
- scpdr = ctrl_inb(SCPDR);
- scpdr |= SCPDR_TS_SCAN_X | SCPDR_TS_SCAN_Y;
- scpdr &= ~SCPDR_TS_SCAN_ENABLE;
- ctrl_outb(scpdr, SCPDR);
-
- scpcr = ctrl_inw(SCPCR);
- scpcr &= ~SCPCR_TS_MASK;
- scpcr |= SCPCR_TS_ENABLE;
- ctrl_outw(scpcr, SCPCR);
-
hp680_ts_dev = input_allocate_device();
if (!hp680_ts_dev)
return -ENOMEM;
diff --git a/drivers/input/touchscreen/penmount.c b/drivers/input/touchscreen/penmount.c
new file mode 100644
index 00000000000..f7370109d43
--- /dev/null
+++ b/drivers/input/touchscreen/penmount.c
@@ -0,0 +1,185 @@
+/*
+ * Penmount serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on ELO driver (drivers/input/touchscreen/elo.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Penmount serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define PM_MAX_LENGTH 5
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct pm {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[PM_MAX_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t pm_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+ struct input_dev *dev = pm->dev;
+
+ pm->data[pm->idx] = data;
+
+ if (pm->data[0] & 0x80) {
+ if (PM_MAX_LENGTH == ++pm->idx) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X, pm->data[2] * 128 + pm->data[1]);
+ input_report_abs(dev, ABS_Y, pm->data[4] * 128 + pm->data[3]);
+ input_report_key(dev, BTN_TOUCH, !!(pm->data[0] & 0x40));
+ input_sync(dev);
+ pm->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * pm_disconnect() is the opposite of pm_connect()
+ */
+
+static void pm_disconnect(struct serio *serio)
+{
+ struct pm *pm = serio_get_drvdata(serio);
+
+ input_get_device(pm->dev);
+ input_unregister_device(pm->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(pm->dev);
+ kfree(pm);
+}
+
+/*
+ * pm_connect() is the routine that is called when someone adds a
+ * new serio device that supports Gunze protocol and registers it as
+ * an input device.
+ */
+
+static int pm_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct pm *pm;
+ struct input_dev *input_dev;
+ int err;
+
+ pm = kzalloc(sizeof(struct pm), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!pm || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ pm->serio = serio;
+ pm->dev = input_dev;
+ snprintf(pm->phys, sizeof(pm->phys), "%s/input0", serio->phys);
+
+ input_dev->private = pm;
+ input_dev->name = "Penmount Serial TouchScreen";
+ input_dev->phys = pm->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_PENMOUNT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->cdev.dev = &serio->dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(pm->dev, ABS_X, 0, 0x3ff, 0, 0);
+ input_set_abs_params(pm->dev, ABS_Y, 0, 0x3ff, 0, 0);
+
+ serio_set_drvdata(serio, pm);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(pm->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(pm);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id pm_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PENMOUNT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, pm_serio_ids);
+
+static struct serio_driver pm_drv = {
+ .driver = {
+ .name = "penmountlpc",
+ },
+ .description = DRIVER_DESC,
+ .id_table = pm_serio_ids,
+ .interrupt = pm_interrupt,
+ .connect = pm_connect,
+ .disconnect = pm_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init pm_init(void)
+{
+ serio_register_driver(&pm_drv);
+ return 0;
+}
+
+static void __exit pm_exit(void)
+{
+ serio_unregister_driver(&pm_drv);
+}
+
+module_init(pm_init);
+module_exit(pm_exit);
diff --git a/drivers/input/touchscreen/touchright.c b/drivers/input/touchscreen/touchright.c
new file mode 100644
index 00000000000..1c89fa53865
--- /dev/null
+++ b/drivers/input/touchscreen/touchright.c
@@ -0,0 +1,196 @@
+/*
+ * Touchright serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchright serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TR_FORMAT_TOUCH_BIT 0x01
+#define TR_FORMAT_STATUS_BYTE 0x40
+#define TR_FORMAT_STATUS_MASK ~TR_FORMAT_TOUCH_BIT
+
+#define TR_LENGTH 5
+
+#define TR_MIN_XC 0
+#define TR_MAX_XC 0x1ff
+#define TR_MIN_YC 0
+#define TR_MAX_YC 0x1ff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tr {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ unsigned char data[TR_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tr_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+ struct input_dev *dev = tr->dev;
+
+ tr->data[tr->idx] = data;
+
+ if ((tr->data[0] & TR_FORMAT_STATUS_MASK) == TR_FORMAT_STATUS_BYTE) {
+ if (++tr->idx == TR_LENGTH) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X,
+ (tr->data[1] << 5) | (tr->data[2] >> 1));
+ input_report_abs(dev, ABS_Y,
+ (tr->data[3] << 5) | (tr->data[4] >> 1));
+ input_report_key(dev, BTN_TOUCH,
+ tr->data[0] & TR_FORMAT_TOUCH_BIT);
+ input_sync(dev);
+ tr->idx = 0;
+ }
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tr_disconnect() is the opposite of tr_connect()
+ */
+
+static void tr_disconnect(struct serio *serio)
+{
+ struct tr *tr = serio_get_drvdata(serio);
+
+ input_get_device(tr->dev);
+ input_unregister_device(tr->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tr->dev);
+ kfree(tr);
+}
+
+/*
+ * tr_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchright protocol and registers it as
+ * an input device.
+ */
+
+static int tr_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tr *tr;
+ struct input_dev *input_dev;
+ int err;
+
+ tr = kzalloc(sizeof(struct tr), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tr || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tr->serio = serio;
+ tr->dev = input_dev;
+ snprintf(tr->phys, sizeof(tr->phys), "%s/input0", serio->phys);
+
+ input_dev->private = tr;
+ input_dev->name = "Touchright Serial TouchScreen";
+ input_dev->phys = tr->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHRIGHT;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(tr->dev, ABS_X, TR_MIN_XC, TR_MAX_XC, 0, 0);
+ input_set_abs_params(tr->dev, ABS_Y, TR_MIN_YC, TR_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tr);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tr->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tr);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tr_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHRIGHT,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tr_serio_ids);
+
+static struct serio_driver tr_drv = {
+ .driver = {
+ .name = "touchright",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tr_serio_ids,
+ .interrupt = tr_interrupt,
+ .connect = tr_connect,
+ .disconnect = tr_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tr_init(void)
+{
+ serio_register_driver(&tr_drv);
+ return 0;
+}
+
+static void __exit tr_exit(void)
+{
+ serio_unregister_driver(&tr_drv);
+}
+
+module_init(tr_init);
+module_exit(tr_exit);
diff --git a/drivers/input/touchscreen/touchwin.c b/drivers/input/touchscreen/touchwin.c
new file mode 100644
index 00000000000..a7b4c755958
--- /dev/null
+++ b/drivers/input/touchscreen/touchwin.c
@@ -0,0 +1,203 @@
+/*
+ * Touchwindow serial touchscreen driver
+ *
+ * Copyright (c) 2006 Rick Koch <n1gp@hotmail.com>
+ *
+ * Based on MicroTouch driver (drivers/input/touchscreen/mtouch.c)
+ * Copyright (c) 2004 Vojtech Pavlik
+ * and Dan Streetman <ddstreet@ieee.org>
+ */
+
+/*
+ * 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.
+ */
+
+/*
+ * 2005/02/19 Rick Koch:
+ * The Touchwindow I used is made by Edmark Corp. and
+ * constantly outputs a stream of 0's unless it is touched.
+ * It then outputs 3 bytes: X, Y, and a copy of Y.
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/init.h>
+
+#define DRIVER_DESC "Touchwindow serial touchscreen driver"
+
+MODULE_AUTHOR("Rick Koch <n1gp@hotmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+/*
+ * Definitions & global arrays.
+ */
+
+#define TW_LENGTH 3
+
+#define TW_MIN_XC 0
+#define TW_MAX_XC 0xff
+#define TW_MIN_YC 0
+#define TW_MAX_YC 0xff
+
+/*
+ * Per-touchscreen data.
+ */
+
+struct tw {
+ struct input_dev *dev;
+ struct serio *serio;
+ int idx;
+ int touched;
+ unsigned char data[TW_LENGTH];
+ char phys[32];
+};
+
+static irqreturn_t tw_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags, struct pt_regs *regs)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+ struct input_dev *dev = tw->dev;
+
+ if (data) { /* touch */
+ tw->touched = 1;
+ tw->data[tw->idx++] = data;
+ /* verify length and that the two Y's are the same */
+ if (tw->idx == TW_LENGTH && tw->data[1] == tw->data[2]) {
+ input_regs(dev, regs);
+ input_report_abs(dev, ABS_X, tw->data[0]);
+ input_report_abs(dev, ABS_Y, tw->data[1]);
+ input_report_key(dev, BTN_TOUCH, 1);
+ input_sync(dev);
+ tw->idx = 0;
+ }
+ } else if (tw->touched) { /* untouch */
+ input_report_key(dev, BTN_TOUCH, 0);
+ input_sync(dev);
+ tw->idx = 0;
+ tw->touched = 0;
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tw_disconnect() is the opposite of tw_connect()
+ */
+
+static void tw_disconnect(struct serio *serio)
+{
+ struct tw *tw = serio_get_drvdata(serio);
+
+ input_get_device(tw->dev);
+ input_unregister_device(tw->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_put_device(tw->dev);
+ kfree(tw);
+}
+
+/*
+ * tw_connect() is the routine that is called when someone adds a
+ * new serio device that supports the Touchwin protocol and registers it as
+ * an input device.
+ */
+
+static int tw_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct tw *tw;
+ struct input_dev *input_dev;
+ int err;
+
+ tw = kzalloc(sizeof(struct tw), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!tw || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ tw->serio = serio;
+ tw->dev = input_dev;
+ snprintf(tw->phys, sizeof(tw->phys), "%s/input0", serio->phys);
+
+ input_dev->private = tw;
+ input_dev->name = "Touchwindow Serial TouchScreen";
+ input_dev->phys = tw->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_TOUCHWIN;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_TOUCH)] = BIT(BTN_TOUCH);
+ input_set_abs_params(tw->dev, ABS_X, TW_MIN_XC, TW_MAX_XC, 0, 0);
+ input_set_abs_params(tw->dev, ABS_Y, TW_MIN_YC, TW_MAX_YC, 0, 0);
+
+ serio_set_drvdata(serio, tw);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(tw->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(tw);
+ return err;
+}
+
+/*
+ * The serio driver structure.
+ */
+
+static struct serio_device_id tw_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_TOUCHWIN,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, tw_serio_ids);
+
+static struct serio_driver tw_drv = {
+ .driver = {
+ .name = "touchwin",
+ },
+ .description = DRIVER_DESC,
+ .id_table = tw_serio_ids,
+ .interrupt = tw_interrupt,
+ .connect = tw_connect,
+ .disconnect = tw_disconnect,
+};
+
+/*
+ * The functions for inserting/removing us as a module.
+ */
+
+static int __init tw_init(void)
+{
+ serio_register_driver(&tw_drv);
+ return 0;
+}
+
+static void __exit tw_exit(void)
+{
+ serio_unregister_driver(&tw_drv);
+}
+
+module_init(tw_init);
+module_exit(tw_exit);
diff --git a/drivers/input/tsdev.c b/drivers/input/tsdev.c
index 00e3929c628..a730c461227 100644
--- a/drivers/input/tsdev.c
+++ b/drivers/input/tsdev.c
@@ -135,8 +135,6 @@ struct tsdev_list {
#define TS_GET_CAL _IOR(IOC_H3600_TS_MAGIC, 10, struct ts_calibration)
#define TS_SET_CAL _IOW(IOC_H3600_TS_MAGIC, 11, struct ts_calibration)
-static struct input_handler tsdev_handler;
-
static struct tsdev *tsdev_table[TSDEV_MINORS/2];
static int tsdev_fasync(int fd, struct file *file, int on)
@@ -263,7 +261,7 @@ static int tsdev_ioctl(struct inode *inode, struct file *file,
return retval;
}
-static struct file_operations tsdev_fops = {
+static const struct file_operations tsdev_fops = {
.owner = THIS_MODULE,
.open = tsdev_open,
.release = tsdev_release,
@@ -370,7 +368,7 @@ static void tsdev_event(struct input_handle *handle, unsigned int type,
static struct input_handle *tsdev_connect(struct input_handler *handler,
struct input_dev *dev,
- struct input_device_id *id)
+ const struct input_device_id *id)
{
struct tsdev *tsdev;
struct class_device *cdev;
@@ -443,7 +441,7 @@ static void tsdev_disconnect(struct input_handle *handle)
tsdev_free(tsdev);
}
-static struct input_device_id tsdev_ids[] = {
+static const struct input_device_id tsdev_ids[] = {
{
.flags = INPUT_DEVICE_ID_MATCH_EVBIT | INPUT_DEVICE_ID_MATCH_KEYBIT | INPUT_DEVICE_ID_MATCH_RELBIT,
.evbit = { BIT(EV_KEY) | BIT(EV_REL) },
@@ -481,9 +479,7 @@ static struct input_handler tsdev_handler = {
static int __init tsdev_init(void)
{
- input_register_handler(&tsdev_handler);
- printk(KERN_INFO "ts: Compaq touchscreen protocol output\n");
- return 0;
+ return input_register_handler(&tsdev_handler);
}
static void __exit tsdev_exit(void)
diff --git a/drivers/isdn/capi/capi.c b/drivers/isdn/capi/capi.c
index 669f76393b5..11844bbfe93 100644
--- a/drivers/isdn/capi/capi.c
+++ b/drivers/isdn/capi/capi.c
@@ -1298,7 +1298,7 @@ static int capinc_tty_read_proc(char *page, char **start, off_t off,
static struct tty_driver *capinc_tty_driver;
-static struct tty_operations capinc_ops = {
+static const struct tty_operations capinc_ops = {
.open = capinc_tty_open,
.close = capinc_tty_close,
.write = capinc_tty_write,
diff --git a/drivers/isdn/capi/capifs.c b/drivers/isdn/capi/capifs.c
index 9ea6bd0ddc3..2dd1b57b0ba 100644
--- a/drivers/isdn/capi/capifs.c
+++ b/drivers/isdn/capi/capifs.c
@@ -104,7 +104,6 @@ capifs_fill_super(struct super_block *s, void *data, int silent)
inode->i_ino = 1;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
inode->i_blocks = 0;
- inode->i_blksize = 1024;
inode->i_uid = inode->i_gid = 0;
inode->i_mode = S_IFDIR | S_IRUGO | S_IXUGO | S_IWUSR;
inode->i_op = &simple_dir_inode_operations;
@@ -149,7 +148,6 @@ void capifs_new_ncci(unsigned int number, dev_t device)
if (!inode)
return;
inode->i_ino = number+2;
- inode->i_blksize = 1024;
inode->i_uid = config.setuid ? config.uid : current->fsuid;
inode->i_gid = config.setgid ? config.gid : current->fsgid;
inode->i_mtime = inode->i_atime = inode->i_ctime = CURRENT_TIME;
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 3845defd490..5cfbe6a3801 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -192,7 +192,7 @@ static char *get_usb_statmsg(int status)
return "bit stuffing error, timeout, or unknown USB error";
case -EILSEQ:
return "CRC mismatch, timeout, or unknown USB error";
- case -ETIMEDOUT:
+ case -ETIME:
return "timed out";
case -EPIPE:
return "endpoint stalled";
diff --git a/drivers/isdn/gigaset/interface.c b/drivers/isdn/gigaset/interface.c
index bd2e4267528..596f3aebe2f 100644
--- a/drivers/isdn/gigaset/interface.c
+++ b/drivers/isdn/gigaset/interface.c
@@ -134,7 +134,7 @@ static int if_tiocmset(struct tty_struct *tty, struct file *file,
static int if_write(struct tty_struct *tty,
const unsigned char *buf, int count);
-static struct tty_operations if_ops = {
+static const struct tty_operations if_ops = {
.open = if_open,
.close = if_close,
.ioctl = if_ioctl,
diff --git a/drivers/isdn/gigaset/proc.c b/drivers/isdn/gigaset/proc.c
index 9ae3a7f3e7b..9ad840e95db 100644
--- a/drivers/isdn/gigaset/proc.c
+++ b/drivers/isdn/gigaset/proc.c
@@ -83,5 +83,6 @@ void gigaset_init_dev_sysfs(struct cardstate *cs)
return;
gig_dbg(DEBUG_INIT, "setting up sysfs");
- class_device_create_file(cs->class, &class_device_attr_cidmode);
+ if (class_device_create_file(cs->class, &class_device_attr_cidmode))
+ dev_err(cs->dev, "could not create sysfs attribute\n");
}
diff --git a/drivers/isdn/hardware/eicon/dsp_defs.h b/drivers/isdn/hardware/eicon/dsp_defs.h
index b44950e06f3..fec1e381a68 100644
--- a/drivers/isdn/hardware/eicon/dsp_defs.h
+++ b/drivers/isdn/hardware/eicon/dsp_defs.h
@@ -34,9 +34,6 @@
*
* I/O functions returns -1 on error, 0 on EOF
*/
-#define OS_SEEK_SET 0
-#define OS_SEEK_CUR 1
-#define OS_SEEK_END 2
struct _OsFileHandle_;
typedef long ( * OsFileIo) (struct _OsFileHandle_ *handle,
void *buffer,
diff --git a/drivers/isdn/hisax/config.c b/drivers/isdn/hisax/config.c
index e10350360f2..e4823ab2b12 100644
--- a/drivers/isdn/hisax/config.c
+++ b/drivers/isdn/hisax/config.c
@@ -1721,11 +1721,11 @@ static void hisax_b_l1l2(struct hisax_if *ifc, int pr, void *arg)
hisax_b_sched_event(bcs, B_RCVBUFREADY);
break;
case PH_DATA | CONFIRM:
- bcs->tx_cnt -= (int) arg;
+ bcs->tx_cnt -= (long)arg;
if (test_bit(FLG_LLI_L1WAKEUP,&bcs->st->lli.flag)) {
u_long flags;
spin_lock_irqsave(&bcs->aclock, flags);
- bcs->ackcnt += (int) arg;
+ bcs->ackcnt += (long)arg;
spin_unlock_irqrestore(&bcs->aclock, flags);
schedule_event(bcs, B_ACKPENDING);
}
@@ -1789,7 +1789,7 @@ static void hisax_b_l2l1(struct PStack *st, int pr, void *arg)
switch (pr) {
case PH_ACTIVATE | REQUEST:
- B_L2L1(b_if, pr, (void *) st->l1.mode);
+ B_L2L1(b_if, pr, (void *)(unsigned long)st->l1.mode);
break;
case PH_DATA | REQUEST:
case PH_PULL | INDICATION:
diff --git a/drivers/isdn/hisax/hfc4s8s_l1.c b/drivers/isdn/hisax/hfc4s8s_l1.c
index 3a5ca8a68fc..0ca5e66d2f5 100644
--- a/drivers/isdn/hisax/hfc4s8s_l1.c
+++ b/drivers/isdn/hisax/hfc4s8s_l1.c
@@ -424,7 +424,7 @@ bch_l2l1(struct hisax_if *ifc, int pr, void *arg)
struct hfc4s8s_btype *bch = ifc->priv;
struct hfc4s8s_l1 *l1 = bch->l1p;
struct sk_buff *skb = (struct sk_buff *) arg;
- int mode = (int) arg;
+ long mode = (long) arg;
u_long flags;
switch (pr) {
@@ -914,7 +914,7 @@ tx_d_frame(struct hfc4s8s_l1 *l1p)
struct sk_buff *skb;
u_char f1, f2;
u_char *cp;
- int cnt;
+ long cnt;
if (l1p->l1_state != 7)
return;
@@ -980,7 +980,8 @@ tx_b_frame(struct hfc4s8s_btype *bch)
struct sk_buff *skb;
struct hfc4s8s_l1 *l1 = bch->l1p;
u_char *cp;
- int cnt, max, hdlc_num, ack_len = 0;
+ int cnt, max, hdlc_num;
+ long ack_len = 0;
if (!l1->enabled || (bch->mode == L1_MODE_NULL))
return;
diff --git a/drivers/isdn/hisax/hfc_sx.c b/drivers/isdn/hisax/hfc_sx.c
index f27c1608a3a..b7e8e23be33 100644
--- a/drivers/isdn/hisax/hfc_sx.c
+++ b/drivers/isdn/hisax/hfc_sx.c
@@ -970,7 +970,7 @@ HFCSX_l1hw(struct PStack *st, int pr, void *arg)
break;
case (HW_TESTLOOP | REQUEST):
spin_lock_irqsave(&cs->lock, flags);
- switch ((int) arg) {
+ switch ((long) arg) {
case (1):
Write_hfc(cs, HFCSX_B1_SSL, 0x80); /* tx slot */
Write_hfc(cs, HFCSX_B1_RSL, 0x80); /* rx slot */
@@ -986,7 +986,7 @@ HFCSX_l1hw(struct PStack *st, int pr, void *arg)
default:
spin_unlock_irqrestore(&cs->lock, flags);
if (cs->debug & L1_DEB_WARN)
- debugl1(cs, "hfcsx_l1hw loop invalid %4x", (int) arg);
+ debugl1(cs, "hfcsx_l1hw loop invalid %4lx", arg);
return;
}
cs->hw.hfcsx.trm |= 0x80; /* enable IOM-loop */
diff --git a/drivers/isdn/hisax/hfc_usb.c b/drivers/isdn/hisax/hfc_usb.c
index b5e571a5269..6b88ecb5047 100644
--- a/drivers/isdn/hisax/hfc_usb.c
+++ b/drivers/isdn/hisax/hfc_usb.c
@@ -696,7 +696,7 @@ tx_iso_complete(struct urb *urb, struct pt_regs *regs)
fifo->delete_flg = TRUE;
fifo->hif->l1l2(fifo->hif,
PH_DATA | CONFIRM,
- (void *) fifo->skbuff->
+ (void *) (unsigned long) fifo->skbuff->
truesize);
if (fifo->skbuff && fifo->delete_flg) {
dev_kfree_skb_any(fifo->skbuff);
@@ -1144,7 +1144,7 @@ hfc_usb_l2l1(struct hisax_if *my_hisax_if, int pr, void *arg)
set_hfcmode(hfc,
(fifo->fifonum ==
HFCUSB_B1_TX) ? 0 : 1,
- (int) arg);
+ (long) arg);
fifo->hif->l1l2(fifo->hif,
PH_ACTIVATE | INDICATION,
NULL);
diff --git a/drivers/isdn/hisax/hfc_usb.h b/drivers/isdn/hisax/hfc_usb.h
index ec52c1a7c22..6349367ed48 100644
--- a/drivers/isdn/hisax/hfc_usb.h
+++ b/drivers/isdn/hisax/hfc_usb.h
@@ -137,11 +137,11 @@ static struct hfcusb_symbolic_list urb_errlist[] = {
{-ENXIO, "URB already queued"},
{-EFBIG, "Too much ISO frames requested"},
{-ENOSR, "Buffer error (overrun)"},
- {-EPIPE, "Specified endpoint is stalled (device not responding)"},
+ {-EPIPE, "Specified endpoint is stalled"},
{-EOVERFLOW, "Babble (bad cable?)"},
{-EPROTO, "Bit-stuff error (bad cable?)"},
- {-EILSEQ, "CRC/Timeout"},
- {-ETIMEDOUT, "NAK (device does not respond)"},
+ {-EILSEQ, "CRC or missing token"},
+ {-ETIME, "Device did not respond"},
{-ESHUTDOWN, "Device unplugged"},
{-1, NULL}
};
diff --git a/drivers/isdn/hisax/hisax.h b/drivers/isdn/hisax/hisax.h
index 75920aa0a3c..2f9d5118cea 100644
--- a/drivers/isdn/hisax/hisax.h
+++ b/drivers/isdn/hisax/hisax.h
@@ -1316,7 +1316,18 @@ void dlogframe(struct IsdnCardState *cs, struct sk_buff *skb, int dir);
void iecpy(u_char * dest, u_char * iestart, int ieoffset);
#endif /* __KERNEL__ */
-#define HZDELAY(jiffs) {int tout = jiffs; while (tout--) udelay(1000000/HZ);}
+/*
+ * Busywait delay for `jiffs' jiffies
+ */
+#define HZDELAY(jiffs) do { \
+ int tout = jiffs; \
+ \
+ while (tout--) { \
+ int loops = USEC_PER_SEC / HZ; \
+ while (loops--) \
+ udelay(1); \
+ } \
+ } while (0)
int ll_run(struct IsdnCardState *cs, int addfeatures);
int CallcNew(void);
diff --git a/drivers/isdn/hisax/hisax_fcpcipnp.c b/drivers/isdn/hisax/hisax_fcpcipnp.c
index 1d7cf3bd6aa..881a4165cfb 100644
--- a/drivers/isdn/hisax/hisax_fcpcipnp.c
+++ b/drivers/isdn/hisax/hisax_fcpcipnp.c
@@ -546,7 +546,7 @@ static inline void hdlc_xpr_irq(struct fritz_bcs *bcs)
}
bcs->tx_cnt = 0;
bcs->tx_skb = NULL;
- B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+ B_L1L2(bcs, PH_DATA | CONFIRM, (void *)(unsigned long)skb->truesize);
dev_kfree_skb_irq(skb);
}
@@ -635,7 +635,7 @@ static void fritz_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
hdlc_fill_fifo(bcs);
break;
case PH_ACTIVATE | REQUEST:
- mode = (int) arg;
+ mode = (long) arg;
DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
modehdlc(bcs, mode);
B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
@@ -998,18 +998,15 @@ static int __init hisax_fcpcipnp_init(void)
retval = pci_register_driver(&fcpci_driver);
if (retval)
- goto out;
+ return retval;
#ifdef __ISAPNP__
retval = pnp_register_driver(&fcpnp_driver);
- if (retval < 0)
- goto out_unregister_pci;
+ if (retval < 0) {
+ pci_unregister_driver(&fcpci_driver);
+ return retval;
+ }
#endif
return 0;
-
- out_unregister_pci:
- pci_unregister_driver(&fcpci_driver);
- out:
- return retval;
}
static void __exit hisax_fcpcipnp_exit(void)
diff --git a/drivers/isdn/hisax/st5481_b.c b/drivers/isdn/hisax/st5481_b.c
index 22fd5db18d4..aca2a3954b1 100644
--- a/drivers/isdn/hisax/st5481_b.c
+++ b/drivers/isdn/hisax/st5481_b.c
@@ -86,7 +86,7 @@ static void usb_b_out(struct st5481_bcs *bcs,int buf_nr)
if (!skb->len) {
// Frame sent
b_out->tx_skb = NULL;
- B_L1L2(bcs, PH_DATA | CONFIRM, (void *) skb->truesize);
+ B_L1L2(bcs, PH_DATA | CONFIRM, (void *)(unsigned long) skb->truesize);
dev_kfree_skb_any(skb);
/* if (!(bcs->tx_skb = skb_dequeue(&bcs->sq))) { */
@@ -350,7 +350,7 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
{
struct st5481_bcs *bcs = ifc->priv;
struct sk_buff *skb = arg;
- int mode;
+ long mode;
DBG(4, "");
@@ -360,8 +360,8 @@ void st5481_b_l2l1(struct hisax_if *ifc, int pr, void *arg)
bcs->b_out.tx_skb = skb;
break;
case PH_ACTIVATE | REQUEST:
- mode = (int) arg;
- DBG(4,"B%d,PH_ACTIVATE_REQUEST %d", bcs->channel + 1, mode);
+ mode = (long) arg;
+ DBG(4,"B%d,PH_ACTIVATE_REQUEST %ld", bcs->channel + 1, mode);
st5481B_mode(bcs, mode);
B_L1L2(bcs, PH_ACTIVATE | INDICATION, NULL);
break;
diff --git a/drivers/isdn/hisax/st5481_d.c b/drivers/isdn/hisax/st5481_d.c
index 493dc94992e..98adec44059 100644
--- a/drivers/isdn/hisax/st5481_d.c
+++ b/drivers/isdn/hisax/st5481_d.c
@@ -374,7 +374,7 @@ static void usb_d_out_complete(struct urb *urb, struct pt_regs *regs)
{
struct st5481_adapter *adapter = urb->context;
struct st5481_d_out *d_out = &adapter->d_out;
- int buf_nr;
+ long buf_nr;
DBG(2, "");
@@ -546,7 +546,7 @@ static void dout_reseted(struct FsmInst *fsm, int event, void *arg)
static void dout_complete(struct FsmInst *fsm, int event, void *arg)
{
struct st5481_adapter *adapter = fsm->userdata;
- int buf_nr = (int) arg;
+ long buf_nr = (long) arg;
usb_d_out(adapter, buf_nr);
}
diff --git a/drivers/isdn/i4l/isdn_net.c b/drivers/isdn/i4l/isdn_net.c
index 43da8ae1b2a..1f8d6ae66b4 100644
--- a/drivers/isdn/i4l/isdn_net.c
+++ b/drivers/isdn/i4l/isdn_net.c
@@ -1614,8 +1614,8 @@ isdn_net_ciscohdlck_slarp_send_reply(isdn_net_local *lp)
struct sk_buff *skb;
unsigned char *p;
struct in_device *in_dev = NULL;
- u32 addr = 0; /* local ipv4 address */
- u32 mask = 0; /* local netmask */
+ __be32 addr = 0; /* local ipv4 address */
+ __be32 mask = 0; /* local netmask */
if ((in_dev = lp->netdev->dev.ip_ptr) != NULL) {
/* take primary(first) address of interface */
diff --git a/drivers/isdn/i4l/isdn_tty.c b/drivers/isdn/i4l/isdn_tty.c
index 9ab66e8960d..2b91bb07fc7 100644
--- a/drivers/isdn/i4l/isdn_tty.c
+++ b/drivers/isdn/i4l/isdn_tty.c
@@ -1860,7 +1860,7 @@ modem_write_profile(atemu * m)
send_sig(SIGIO, dev->profd, 1);
}
-static struct tty_operations modem_ops = {
+static const struct tty_operations modem_ops = {
.open = isdn_tty_open,
.close = isdn_tty_close,
.write = isdn_tty_write,
diff --git a/drivers/isdn/sc/command.c b/drivers/isdn/sc/command.c
index b4b24335f71..04b8a58f03b 100644
--- a/drivers/isdn/sc/command.c
+++ b/drivers/isdn/sc/command.c
@@ -103,9 +103,6 @@ int command(isdn_ctrl *cmd)
return -ENODEV;
}
- pr_debug("%s: Received %s command from Link Layer\n",
- sc_adapter[card]->devicename, commands[cmd->command]);
-
/*
* Dispatch the command
*/
@@ -118,7 +115,7 @@ int command(isdn_ctrl *cmd)
memcpy(&cmdptr, cmd->parm.num, sizeof(unsigned long));
if (copy_from_user(&ioc, (scs_ioctl __user *)cmdptr,
sizeof(scs_ioctl))) {
- pr_debug("%s: Failed to verify user space 0x%x\n",
+ pr_debug("%s: Failed to verify user space 0x%lx\n",
sc_adapter[card]->devicename, cmdptr);
return -EFAULT;
}
@@ -195,7 +192,7 @@ static int dial(int card, unsigned long channel, setup_parm setup)
strlen(Phone),
(unsigned int *) Phone);
- pr_debug("%s: Dialing %s on channel %d\n",
+ pr_debug("%s: Dialing %s on channel %lu\n",
sc_adapter[card]->devicename, Phone, channel+1);
return status;
@@ -217,7 +214,7 @@ static int answer(int card, unsigned long channel)
}
indicate_status(card, ISDN_STAT_BCONN,channel,NULL);
- pr_debug("%s: Answered incoming call on channel %s\n",
+ pr_debug("%s: Answered incoming call on channel %lu\n",
sc_adapter[card]->devicename, channel+1);
return 0;
}
@@ -240,7 +237,7 @@ static int hangup(int card, unsigned long channel)
(unsigned char) channel+1,
0,
NULL);
- pr_debug("%s: Sent HANGUP message to channel %d\n",
+ pr_debug("%s: Sent HANGUP message to channel %lu\n",
sc_adapter[card]->devicename, channel+1);
return status;
}
@@ -260,9 +257,6 @@ static int setl2(int card, unsigned long arg)
protocol = arg >> 8;
channel = arg & 0xff;
sc_adapter[card]->channel[channel].l2_proto = protocol;
- pr_debug("%s: Level 2 protocol for channel %d set to %s from %d\n",
- sc_adapter[card]->devicename, channel+1,
- l2protos[sc_adapter[card]->channel[channel].l2_proto],protocol);
/*
* check that the adapter is also set to the correct protocol
@@ -293,8 +287,6 @@ static int setl3(int card, unsigned long channel)
}
sc_adapter[card]->channel[channel].l3_proto = protocol;
- pr_debug("%s: Level 3 protocol for channel %d set to %s\n",
- sc_adapter[card]->devicename, channel+1, l3protos[protocol]);
return 0;
}
@@ -311,7 +303,7 @@ static int acceptb(int card, unsigned long channel)
return -ENOBUFS;
}
- pr_debug("%s: B-Channel connection accepted on channel %d\n",
+ pr_debug("%s: B-Channel connection accepted on channel %lu\n",
sc_adapter[card]->devicename, channel+1);
indicate_status(card, ISDN_STAT_BCONN, channel, NULL);
return 0;
@@ -326,7 +318,7 @@ static int clreaz(int card, unsigned long arg)
strcpy(sc_adapter[card]->channel[arg].eazlist, "");
sc_adapter[card]->channel[arg].eazclear = 1;
- pr_debug("%s: EAZ List cleared for channel %d\n",
+ pr_debug("%s: EAZ List cleared for channel %lu\n",
sc_adapter[card]->devicename, arg+1);
return 0;
}
@@ -340,7 +332,7 @@ static int seteaz(int card, unsigned long arg, char *num)
strcpy(sc_adapter[card]->channel[arg].eazlist, num);
sc_adapter[card]->channel[arg].eazclear = 0;
- pr_debug("%s: EAZ list for channel %d set to: %s\n",
+ pr_debug("%s: EAZ list for channel %lu set to: %s\n",
sc_adapter[card]->devicename, arg+1,
sc_adapter[card]->channel[arg].eazlist);
return 0;
diff --git a/drivers/isdn/sc/event.c b/drivers/isdn/sc/event.c
index 5b8c7c1a766..57367325ef0 100644
--- a/drivers/isdn/sc/event.c
+++ b/drivers/isdn/sc/event.c
@@ -45,8 +45,10 @@ int indicate_status(int card, int event,ulong Channel,char *Data)
{
isdn_ctrl cmd;
+#ifdef DEBUG
pr_debug("%s: Indicating event %s on Channel %d\n",
sc_adapter[card]->devicename, events[event-256], Channel);
+#endif
if (Data != NULL){
pr_debug("%s: Event data: %s\n", sc_adapter[card]->devicename,
Data);
diff --git a/drivers/isdn/sc/interrupt.c b/drivers/isdn/sc/interrupt.c
index 8631d338d69..ae6263125ac 100644
--- a/drivers/isdn/sc/interrupt.c
+++ b/drivers/isdn/sc/interrupt.c
@@ -91,7 +91,7 @@ irqreturn_t interrupt_handler(int interrupt, void *cardptr, struct pt_regs *regs
*/
if (IS_CE_MESSAGE(rcvmsg, Lnk, 1, Read))
{
- pr_debug("%s: Received packet 0x%x bytes long at 0x%x\n",
+ pr_debug("%s: Received packet 0x%x bytes long at 0x%lx\n",
sc_adapter[card]->devicename,
rcvmsg.msg_data.response.msg_len,
rcvmsg.msg_data.response.buff_offset);
diff --git a/drivers/isdn/sc/timer.c b/drivers/isdn/sc/timer.c
index aced19aac5a..f43282be0ad 100644
--- a/drivers/isdn/sc/timer.c
+++ b/drivers/isdn/sc/timer.c
@@ -76,7 +76,7 @@ void check_reset(unsigned long data)
if (sc_adapter[card]->StartOnReset)
startproc(card);
} else {
- pr_debug("%s: No signature yet, waiting another %d jiffies.\n",
+ pr_debug("%s: No signature yet, waiting another %lu jiffies.\n",
sc_adapter[card]->devicename, CHECKRESET_TIME);
mod_timer(&sc_adapter[card]->reset_timer, jiffies+CHECKRESET_TIME);
spin_unlock_irqrestore(&sc_adapter[card]->lock, flags);
diff --git a/drivers/leds/led-triggers.c b/drivers/leds/led-triggers.c
index 47f0ff19632..454fb0901f8 100644
--- a/drivers/leds/led-triggers.c
+++ b/drivers/leds/led-triggers.c
@@ -125,6 +125,7 @@ void led_trigger_set(struct led_classdev *led_cdev, struct led_trigger *trigger)
write_unlock_irqrestore(&led_cdev->trigger->leddev_list_lock, flags);
if (led_cdev->trigger->deactivate)
led_cdev->trigger->deactivate(led_cdev);
+ led_set_brightness(led_cdev, LED_OFF);
}
if (trigger) {
write_lock_irqsave(&trigger->leddev_list_lock, flags);
diff --git a/drivers/leds/leds-net48xx.c b/drivers/leds/leds-net48xx.c
index 713c4a8aa77..45ba3d45bcb 100644
--- a/drivers/leds/leds-net48xx.c
+++ b/drivers/leds/leds-net48xx.c
@@ -16,6 +16,7 @@
#include <linux/leds.h>
#include <linux/err.h>
#include <asm/io.h>
+#include <linux/nsc_gpio.h>
#include <linux/scx200_gpio.h>
#define DRVNAME "net48xx-led"
@@ -26,10 +27,7 @@ static struct platform_device *pdev;
static void net48xx_error_led_set(struct led_classdev *led_cdev,
enum led_brightness value)
{
- if (value)
- scx200_gpio_set_high(NET48XX_ERROR_LED_GPIO);
- else
- scx200_gpio_set_low(NET48XX_ERROR_LED_GPIO);
+ scx200_gpio_ops.gpio_set(NET48XX_ERROR_LED_GPIO, value ? 1 : 0);
}
static struct led_classdev net48xx_error_led = {
@@ -81,7 +79,8 @@ static int __init net48xx_led_init(void)
{
int ret;
- if (!scx200_gpio_present()) {
+ /* small hack, but scx200_gpio doesn't set .dev if the probe fails */
+ if (!scx200_gpio_ops.dev) {
ret = -ENODEV;
goto out;
}
diff --git a/drivers/macintosh/smu.c b/drivers/macintosh/smu.c
index 090e40fc501..c0f9d82e466 100644
--- a/drivers/macintosh/smu.c
+++ b/drivers/macintosh/smu.c
@@ -870,7 +870,7 @@ int smu_queue_i2c(struct smu_i2c_cmd *cmd)
static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
{
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
unsigned int chunk;
struct smu_cmd cmd;
int rc;
@@ -917,7 +917,7 @@ static int smu_read_datablock(u8 *dest, unsigned int addr, unsigned int len)
static struct smu_sdbp_header *smu_create_sdb_partition(int id)
{
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
struct smu_simple_cmd cmd;
unsigned int addr, len, tlen;
struct smu_sdbp_header *hdr;
diff --git a/drivers/macintosh/via-pmu-backlight.c b/drivers/macintosh/via-pmu-backlight.c
index a82f313d9dc..6c29fe727c0 100644
--- a/drivers/macintosh/via-pmu-backlight.c
+++ b/drivers/macintosh/via-pmu-backlight.c
@@ -16,7 +16,7 @@
#define MAX_PMU_LEVEL 0xFF
static struct backlight_properties pmu_backlight_data;
-static spinlock_t pmu_backlight_lock;
+static DEFINE_SPINLOCK(pmu_backlight_lock);
static int sleeping;
static u8 bl_curve[FB_BACKLIGHT_LEVELS];
diff --git a/drivers/macintosh/via-pmu.c b/drivers/macintosh/via-pmu.c
index 5710e01cef1..4f04fd0956a 100644
--- a/drivers/macintosh/via-pmu.c
+++ b/drivers/macintosh/via-pmu.c
@@ -1832,7 +1832,7 @@ pbook_alloc_pci_save(void)
struct pci_dev *pd = NULL;
npci = 0;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
++npci;
}
if (npci == 0)
@@ -1862,9 +1862,11 @@ pbook_pci_save(void)
if (ps == NULL)
return;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
- if (npci-- == 0)
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ if (npci-- == 0) {
+ pci_dev_put(pd);
return;
+ }
#ifndef HACKED_PCI_SAVE
pci_read_config_word(pd, PCI_COMMAND, &ps->command);
pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
@@ -1892,11 +1894,13 @@ pbook_pci_restore(void)
int npci = pbook_npci_saves;
int j;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
#ifdef HACKED_PCI_SAVE
int i;
- if (npci-- == 0)
+ if (npci-- == 0) {
+ pci_dev_put(pd);
return;
+ }
ps++;
for (i=2;i<16;i++)
pci_write_config_dword(pd, i<<4, ps->config[i]);
diff --git a/drivers/macintosh/via-pmu68k.c b/drivers/macintosh/via-pmu68k.c
index 35b70323e7e..9f4eff1d1a0 100644
--- a/drivers/macintosh/via-pmu68k.c
+++ b/drivers/macintosh/via-pmu68k.c
@@ -843,7 +843,7 @@ pbook_pci_save(void)
struct pci_save *ps;
npci = 0;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL)
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL)
++npci;
n_pbook_pci_saves = npci;
if (npci == 0)
@@ -854,7 +854,7 @@ pbook_pci_save(void)
return;
pd = NULL;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
pci_read_config_word(pd, PCI_COMMAND, &ps->command);
pci_read_config_word(pd, PCI_CACHE_LINE_SIZE, &ps->cache_lat);
pci_read_config_word(pd, PCI_INTERRUPT_LINE, &ps->intr);
@@ -871,7 +871,7 @@ pbook_pci_restore(void)
struct pci_dev *pd = NULL;
int j;
- while ((pd = pci_find_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
+ while ((pd = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, pd)) != NULL) {
if (ps->command == 0)
continue;
pci_read_config_word(pd, PCI_COMMAND, &cmd);
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index bff1f372f18..31b750d6120 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -56,7 +56,7 @@ static int smu_set_fan(int pwm, u8 id, u16 value)
{
struct smu_cmd cmd;
u8 buffer[16];
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
int rc;
/* Fill SMU command structure */
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c
index aceb61d9fbc..83f79de7174 100644
--- a/drivers/macintosh/windfarm_smu_sat.c
+++ b/drivers/macintosh/windfarm_smu_sat.c
@@ -397,12 +397,7 @@ static int wf_sat_detach(struct i2c_client *client)
static int __init sat_sensors_init(void)
{
- int err;
-
- err = i2c_add_driver(&wf_sat_driver);
- if (err < 0)
- return err;
- return 0;
+ return i2c_add_driver(&wf_sat_driver);
}
static void __exit sat_sensors_exit(void)
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c
index defe9922ebd..01b4c50143d 100644
--- a/drivers/macintosh/windfarm_smu_sensors.c
+++ b/drivers/macintosh/windfarm_smu_sensors.c
@@ -67,7 +67,7 @@ static void smu_ads_release(struct wf_sensor *sr)
static int smu_read_adc(u8 id, s32 *value)
{
struct smu_simple_cmd cmd;
- DECLARE_COMPLETION(comp);
+ DECLARE_COMPLETION_ONSTACK(comp);
int rc;
rc = smu_queue_simple(&cmd, SMU_CMD_READ_ADC, 1,
diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index bf869ed03ee..c92c1521546 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -2,6 +2,8 @@
# Block device driver configuration
#
+if BLOCK
+
menu "Multi-device support (RAID and LVM)"
config MD
@@ -136,16 +138,16 @@ config MD_RAID456
If unsure, say Y.
config MD_RAID5_RESHAPE
- bool "Support adding drives to a raid-5 array (experimental)"
- depends on MD_RAID456 && EXPERIMENTAL
+ bool "Support adding drives to a raid-5 array"
+ depends on MD_RAID456
+ default y
---help---
A RAID-5 set can be expanded by adding extra drives. This
requires "restriping" the array which means (almost) every
block must be written to a different place.
This option allows such restriping to be done while the array
- is online. However it is still EXPERIMENTAL code. It should
- work, but please be sure that you have backups.
+ is online.
You will need mdadm version 2.4.1 or later to use this
feature safely. During the early stage of reshape there is
@@ -162,6 +164,8 @@ config MD_RAID5_RESHAPE
There should be enough spares already present to make the new
array workable.
+ If unsure, say Y.
+
config MD_MULTIPATH
tristate "Multipath I/O support"
depends on BLK_DEV_MD
@@ -199,6 +203,14 @@ config BLK_DEV_DM
If unsure, say N.
+config DM_DEBUG
+ boolean "Device mapper debugging support"
+ depends on BLK_DEV_DM && EXPERIMENTAL
+ ---help---
+ Enable this for messages that may help debug device-mapper problems.
+
+ If unsure, say N.
+
config DM_CRYPT
tristate "Crypt target support"
depends on BLK_DEV_DM && EXPERIMENTAL
@@ -251,3 +263,4 @@ config DM_MULTIPATH_EMC
endmenu
+endif
diff --git a/drivers/md/bitmap.c b/drivers/md/bitmap.c
index ecc56765d94..8e67634e79a 100644
--- a/drivers/md/bitmap.c
+++ b/drivers/md/bitmap.c
@@ -613,6 +613,7 @@ static inline unsigned long file_page_offset(unsigned long chunk)
static inline struct page *filemap_get_page(struct bitmap *bitmap,
unsigned long chunk)
{
+ if (file_page_index(chunk) >= bitmap->file_pages) return NULL;
return bitmap->filemap[file_page_index(chunk) - file_page_index(0)];
}
@@ -739,6 +740,7 @@ static void bitmap_file_set_bit(struct bitmap *bitmap, sector_t block)
}
page = filemap_get_page(bitmap, chunk);
+ if (!page) return;
bit = file_page_offset(chunk);
/* set the bit */
@@ -1322,6 +1324,18 @@ static void bitmap_set_memory_bits(struct bitmap *bitmap, sector_t offset, int n
}
+/* dirty the memory and file bits for bitmap chunks "s" to "e" */
+void bitmap_dirty_bits(struct bitmap *bitmap, unsigned long s, unsigned long e)
+{
+ unsigned long chunk;
+
+ for (chunk = s; chunk <= e; chunk++) {
+ sector_t sec = chunk << CHUNK_BLOCK_SHIFT(bitmap);
+ bitmap_set_memory_bits(bitmap, sec, 1);
+ bitmap_file_set_bit(bitmap, sec);
+ }
+}
+
/*
* flush out any pending updates
*/
@@ -1430,8 +1444,7 @@ int bitmap_create(mddev_t *mddev)
if (err)
goto error;
- bitmap->chunkshift = find_first_bit(&bitmap->chunksize,
- sizeof(bitmap->chunksize));
+ bitmap->chunkshift = ffz(~bitmap->chunksize);
/* now that chunksize and chunkshift are set, we can use these macros */
chunks = (blocks + CHUNK_BLOCK_RATIO(bitmap) - 1) /
diff --git a/drivers/md/dm-crypt.c b/drivers/md/dm-crypt.c
index bdbd34993a8..655d816760e 100644
--- a/drivers/md/dm-crypt.c
+++ b/drivers/md/dm-crypt.c
@@ -1,6 +1,7 @@
/*
* Copyright (C) 2003 Christophe Saout <christophe@saout.de>
* Copyright (C) 2004 Clemens Fruhwirth <clemens@endorphin.org>
+ * Copyright (C) 2006 Red Hat, Inc. All rights reserved.
*
* This file is released under the GPL.
*/
@@ -22,17 +23,19 @@
#include "dm.h"
#define DM_MSG_PREFIX "crypt"
+#define MESG_STR(x) x, sizeof(x)
/*
* per bio private data
*/
struct crypt_io {
struct dm_target *target;
- struct bio *bio;
+ struct bio *base_bio;
struct bio *first_clone;
struct work_struct work;
atomic_t pending;
int error;
+ int post_process;
};
/*
@@ -63,6 +66,7 @@ struct crypt_iv_operations {
* Crypt: maps a linear range of a block device
* and encrypts / decrypts at the same time.
*/
+enum flags { DM_CRYPT_SUSPENDED, DM_CRYPT_KEY_VALID };
struct crypt_config {
struct dm_dev *dev;
sector_t start;
@@ -73,6 +77,7 @@ struct crypt_config {
*/
mempool_t *io_pool;
mempool_t *page_pool;
+ struct bio_set *bs;
/*
* crypto related data
@@ -86,11 +91,12 @@ struct crypt_config {
char cipher[CRYPTO_MAX_ALG_NAME];
char chainmode[CRYPTO_MAX_ALG_NAME];
struct crypto_blkcipher *tfm;
+ unsigned long flags;
unsigned int key_size;
u8 key[0];
};
-#define MIN_IOS 256
+#define MIN_IOS 16
#define MIN_POOL_PAGES 32
#define MIN_BIO_PAGES 8
@@ -306,6 +312,14 @@ static int crypt_convert(struct crypt_config *cc,
return r;
}
+ static void dm_crypt_bio_destructor(struct bio *bio)
+ {
+ struct crypt_io *io = bio->bi_private;
+ struct crypt_config *cc = io->target->private;
+
+ bio_free(bio, cc->bs);
+ }
+
/*
* Generate a new unfragmented bio with the given size
* This should never violate the device limitations
@@ -315,34 +329,33 @@ static struct bio *
crypt_alloc_buffer(struct crypt_config *cc, unsigned int size,
struct bio *base_bio, unsigned int *bio_vec_idx)
{
- struct bio *bio;
+ struct bio *clone;
unsigned int nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
gfp_t gfp_mask = GFP_NOIO | __GFP_HIGHMEM;
unsigned int i;
- /*
- * Use __GFP_NOMEMALLOC to tell the VM to act less aggressively and
- * to fail earlier. This is not necessary but increases throughput.
- * FIXME: Is this really intelligent?
- */
- if (base_bio)
- bio = bio_clone(base_bio, GFP_NOIO|__GFP_NOMEMALLOC);
- else
- bio = bio_alloc(GFP_NOIO|__GFP_NOMEMALLOC, nr_iovecs);
- if (!bio)
+ if (base_bio) {
+ clone = bio_alloc_bioset(GFP_NOIO, base_bio->bi_max_vecs, cc->bs);
+ __bio_clone(clone, base_bio);
+ } else
+ clone = bio_alloc_bioset(GFP_NOIO, nr_iovecs, cc->bs);
+
+ if (!clone)
return NULL;
+ clone->bi_destructor = dm_crypt_bio_destructor;
+
/* if the last bio was not complete, continue where that one ended */
- bio->bi_idx = *bio_vec_idx;
- bio->bi_vcnt = *bio_vec_idx;
- bio->bi_size = 0;
- bio->bi_flags &= ~(1 << BIO_SEG_VALID);
+ clone->bi_idx = *bio_vec_idx;
+ clone->bi_vcnt = *bio_vec_idx;
+ clone->bi_size = 0;
+ clone->bi_flags &= ~(1 << BIO_SEG_VALID);
- /* bio->bi_idx pages have already been allocated */
- size -= bio->bi_idx * PAGE_SIZE;
+ /* clone->bi_idx pages have already been allocated */
+ size -= clone->bi_idx * PAGE_SIZE;
- for(i = bio->bi_idx; i < nr_iovecs; i++) {
- struct bio_vec *bv = bio_iovec_idx(bio, i);
+ for (i = clone->bi_idx; i < nr_iovecs; i++) {
+ struct bio_vec *bv = bio_iovec_idx(clone, i);
bv->bv_page = mempool_alloc(cc->page_pool, gfp_mask);
if (!bv->bv_page)
@@ -353,7 +366,7 @@ crypt_alloc_buffer(struct crypt_config *cc, unsigned int size,
* return a partially allocated bio, the caller will then try
* to allocate additional bios while submitting this partial bio
*/
- if ((i - bio->bi_idx) == (MIN_BIO_PAGES - 1))
+ if ((i - clone->bi_idx) == (MIN_BIO_PAGES - 1))
gfp_mask = (gfp_mask | __GFP_NOWARN) & ~__GFP_WAIT;
bv->bv_offset = 0;
@@ -362,13 +375,13 @@ crypt_alloc_buffer(struct crypt_config *cc, unsigned int size,
else
bv->bv_len = size;
- bio->bi_size += bv->bv_len;
- bio->bi_vcnt++;
+ clone->bi_size += bv->bv_len;
+ clone->bi_vcnt++;
size -= bv->bv_len;
}
- if (!bio->bi_size) {
- bio_put(bio);
+ if (!clone->bi_size) {
+ bio_put(clone);
return NULL;
}
@@ -376,13 +389,13 @@ crypt_alloc_buffer(struct crypt_config *cc, unsigned int size,
* Remember the last bio_vec allocated to be able
* to correctly continue after the splitting.
*/
- *bio_vec_idx = bio->bi_vcnt;
+ *bio_vec_idx = clone->bi_vcnt;
- return bio;
+ return clone;
}
static void crypt_free_buffer_pages(struct crypt_config *cc,
- struct bio *bio, unsigned int bytes)
+ struct bio *clone, unsigned int bytes)
{
unsigned int i, start, end;
struct bio_vec *bv;
@@ -396,19 +409,19 @@ static void crypt_free_buffer_pages(struct crypt_config *cc,
* A fix to the bi_idx issue in the kernel is in the works, so
* we will hopefully be able to revert to the cleaner solution soon.
*/
- i = bio->bi_vcnt - 1;
- bv = bio_iovec_idx(bio, i);
- end = (i << PAGE_SHIFT) + (bv->bv_offset + bv->bv_len) - bio->bi_size;
+ i = clone->bi_vcnt - 1;
+ bv = bio_iovec_idx(clone, i);
+ end = (i << PAGE_SHIFT) + (bv->bv_offset + bv->bv_len) - clone->bi_size;
start = end - bytes;
start >>= PAGE_SHIFT;
- if (!bio->bi_size)
- end = bio->bi_vcnt;
+ if (!clone->bi_size)
+ end = clone->bi_vcnt;
else
end >>= PAGE_SHIFT;
- for(i = start; i < end; i++) {
- bv = bio_iovec_idx(bio, i);
+ for (i = start; i < end; i++) {
+ bv = bio_iovec_idx(clone, i);
BUG_ON(!bv->bv_page);
mempool_free(bv->bv_page, cc->page_pool);
bv->bv_page = NULL;
@@ -432,7 +445,7 @@ static void dec_pending(struct crypt_io *io, int error)
if (io->first_clone)
bio_put(io->first_clone);
- bio_endio(io->bio, io->bio->bi_size, io->error);
+ bio_endio(io->base_bio, io->base_bio->bi_size, io->error);
mempool_free(io, cc->io_pool);
}
@@ -441,29 +454,179 @@ static void dec_pending(struct crypt_io *io, int error)
* kcryptd:
*
* Needed because it would be very unwise to do decryption in an
- * interrupt context, so bios returning from read requests get
- * queued here.
+ * interrupt context.
*/
static struct workqueue_struct *_kcryptd_workqueue;
+static void kcryptd_do_work(void *data);
-static void kcryptd_do_work(void *data)
+static void kcryptd_queue_io(struct crypt_io *io)
{
- struct crypt_io *io = (struct crypt_io *) data;
- struct crypt_config *cc = (struct crypt_config *) io->target->private;
+ INIT_WORK(&io->work, kcryptd_do_work, io);
+ queue_work(_kcryptd_workqueue, &io->work);
+}
+
+static int crypt_endio(struct bio *clone, unsigned int done, int error)
+{
+ struct crypt_io *io = clone->bi_private;
+ struct crypt_config *cc = io->target->private;
+ unsigned read_io = bio_data_dir(clone) == READ;
+
+ /*
+ * free the processed pages, even if
+ * it's only a partially completed write
+ */
+ if (!read_io)
+ crypt_free_buffer_pages(cc, clone, done);
+
+ /* keep going - not finished yet */
+ if (unlikely(clone->bi_size))
+ return 1;
+
+ if (!read_io)
+ goto out;
+
+ if (unlikely(!bio_flagged(clone, BIO_UPTODATE))) {
+ error = -EIO;
+ goto out;
+ }
+
+ bio_put(clone);
+ io->post_process = 1;
+ kcryptd_queue_io(io);
+ return 0;
+
+out:
+ bio_put(clone);
+ dec_pending(io, error);
+ return error;
+}
+
+static void clone_init(struct crypt_io *io, struct bio *clone)
+{
+ struct crypt_config *cc = io->target->private;
+
+ clone->bi_private = io;
+ clone->bi_end_io = crypt_endio;
+ clone->bi_bdev = cc->dev->bdev;
+ clone->bi_rw = io->base_bio->bi_rw;
+}
+
+static void process_read(struct crypt_io *io)
+{
+ struct crypt_config *cc = io->target->private;
+ struct bio *base_bio = io->base_bio;
+ struct bio *clone;
+ sector_t sector = base_bio->bi_sector - io->target->begin;
+
+ atomic_inc(&io->pending);
+
+ /*
+ * The block layer might modify the bvec array, so always
+ * copy the required bvecs because we need the original
+ * one in order to decrypt the whole bio data *afterwards*.
+ */
+ clone = bio_alloc_bioset(GFP_NOIO, bio_segments(base_bio), cc->bs);
+ if (unlikely(!clone)) {
+ dec_pending(io, -ENOMEM);
+ return;
+ }
+
+ clone_init(io, clone);
+ clone->bi_destructor = dm_crypt_bio_destructor;
+ clone->bi_idx = 0;
+ clone->bi_vcnt = bio_segments(base_bio);
+ clone->bi_size = base_bio->bi_size;
+ clone->bi_sector = cc->start + sector;
+ memcpy(clone->bi_io_vec, bio_iovec(base_bio),
+ sizeof(struct bio_vec) * clone->bi_vcnt);
+
+ generic_make_request(clone);
+}
+
+static void process_write(struct crypt_io *io)
+{
+ struct crypt_config *cc = io->target->private;
+ struct bio *base_bio = io->base_bio;
+ struct bio *clone;
struct convert_context ctx;
- int r;
+ unsigned remaining = base_bio->bi_size;
+ sector_t sector = base_bio->bi_sector - io->target->begin;
+ unsigned bvec_idx = 0;
+
+ atomic_inc(&io->pending);
+
+ crypt_convert_init(cc, &ctx, NULL, base_bio, sector, 1);
+
+ /*
+ * The allocated buffers can be smaller than the whole bio,
+ * so repeat the whole process until all the data can be handled.
+ */
+ while (remaining) {
+ clone = crypt_alloc_buffer(cc, base_bio->bi_size,
+ io->first_clone, &bvec_idx);
+ if (unlikely(!clone)) {
+ dec_pending(io, -ENOMEM);
+ return;
+ }
+
+ ctx.bio_out = clone;
+
+ if (unlikely(crypt_convert(cc, &ctx) < 0)) {
+ crypt_free_buffer_pages(cc, clone, clone->bi_size);
+ bio_put(clone);
+ dec_pending(io, -EIO);
+ return;
+ }
+
+ clone_init(io, clone);
+ clone->bi_sector = cc->start + sector;
+
+ if (!io->first_clone) {
+ /*
+ * hold a reference to the first clone, because it
+ * holds the bio_vec array and that can't be freed
+ * before all other clones are released
+ */
+ bio_get(clone);
+ io->first_clone = clone;
+ }
+
+ remaining -= clone->bi_size;
+ sector += bio_sectors(clone);
+
+ /* prevent bio_put of first_clone */
+ if (remaining)
+ atomic_inc(&io->pending);
- crypt_convert_init(cc, &ctx, io->bio, io->bio,
- io->bio->bi_sector - io->target->begin, 0);
- r = crypt_convert(cc, &ctx);
+ generic_make_request(clone);
- dec_pending(io, r);
+ /* out of memory -> run queues */
+ if (remaining)
+ blk_congestion_wait(bio_data_dir(clone), HZ/100);
+ }
}
-static void kcryptd_queue_io(struct crypt_io *io)
+static void process_read_endio(struct crypt_io *io)
{
- INIT_WORK(&io->work, kcryptd_do_work, io);
- queue_work(_kcryptd_workqueue, &io->work);
+ struct crypt_config *cc = io->target->private;
+ struct convert_context ctx;
+
+ crypt_convert_init(cc, &ctx, io->base_bio, io->base_bio,
+ io->base_bio->bi_sector - io->target->begin, 0);
+
+ dec_pending(io, crypt_convert(cc, &ctx));
+}
+
+static void kcryptd_do_work(void *data)
+{
+ struct crypt_io *io = data;
+
+ if (io->post_process)
+ process_read_endio(io);
+ else if (bio_data_dir(io->base_bio) == READ)
+ process_read(io);
+ else
+ process_write(io);
}
/*
@@ -477,7 +640,7 @@ static int crypt_decode_key(u8 *key, char *hex, unsigned int size)
buffer[2] = '\0';
- for(i = 0; i < size; i++) {
+ for (i = 0; i < size; i++) {
buffer[0] = *hex++;
buffer[1] = *hex++;
@@ -500,13 +663,38 @@ static void crypt_encode_key(char *hex, u8 *key, unsigned int size)
{
unsigned int i;
- for(i = 0; i < size; i++) {
+ for (i = 0; i < size; i++) {
sprintf(hex, "%02x", *key);
hex += 2;
key++;
}
}
+static int crypt_set_key(struct crypt_config *cc, char *key)
+{
+ unsigned key_size = strlen(key) >> 1;
+
+ if (cc->key_size && cc->key_size != key_size)
+ return -EINVAL;
+
+ cc->key_size = key_size; /* initial settings */
+
+ if ((!key_size && strcmp(key, "-")) ||
+ (key_size && crypt_decode_key(cc->key, key, key_size) < 0))
+ return -EINVAL;
+
+ set_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+
+ return 0;
+}
+
+static int crypt_wipe_key(struct crypt_config *cc)
+{
+ clear_bit(DM_CRYPT_KEY_VALID, &cc->flags);
+ memset(&cc->key, 0, cc->key_size * sizeof(u8));
+ return 0;
+}
+
/*
* Construct an encryption mapping:
* <cipher> <key> <iv_offset> <dev_path> <start>
@@ -539,16 +727,14 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
key_size = strlen(argv[1]) >> 1;
- cc = kmalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);
+ cc = kzalloc(sizeof(*cc) + key_size * sizeof(u8), GFP_KERNEL);
if (cc == NULL) {
ti->error =
"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)) {
+ if (crypt_set_key(cc, argv[1])) {
ti->error = "Error decoding key";
goto bad1;
}
@@ -626,6 +812,12 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad4;
}
+ cc->bs = bioset_create(MIN_IOS, MIN_IOS, 4);
+ if (!cc->bs) {
+ ti->error = "Cannot allocate crypt bioset";
+ goto bad_bs;
+ }
+
if (crypto_blkcipher_setkey(tfm, cc->key, key_size) < 0) {
ti->error = "Error setting key";
goto bad5;
@@ -665,6 +857,8 @@ static int crypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)
return 0;
bad5:
+ bioset_free(cc->bs);
+bad_bs:
mempool_destroy(cc->page_pool);
bad4:
mempool_destroy(cc->io_pool);
@@ -684,6 +878,7 @@ static void crypt_dtr(struct dm_target *ti)
{
struct crypt_config *cc = (struct crypt_config *) ti->private;
+ bioset_free(cc->bs);
mempool_destroy(cc->page_pool);
mempool_destroy(cc->io_pool);
@@ -698,147 +893,21 @@ static void crypt_dtr(struct dm_target *ti)
kfree(cc);
}
-static int crypt_endio(struct bio *bio, unsigned int done, int error)
-{
- struct crypt_io *io = (struct crypt_io *) bio->bi_private;
- struct crypt_config *cc = (struct crypt_config *) io->target->private;
-
- if (bio_data_dir(bio) == WRITE) {
- /*
- * free the processed pages, even if
- * it's only a partially completed write
- */
- crypt_free_buffer_pages(cc, bio, done);
- }
-
- if (bio->bi_size)
- return 1;
-
- bio_put(bio);
-
- /*
- * successful reads are decrypted by the worker thread
- */
- if ((bio_data_dir(bio) == READ)
- && bio_flagged(bio, BIO_UPTODATE)) {
- kcryptd_queue_io(io);
- return 0;
- }
-
- dec_pending(io, error);
- return error;
-}
-
-static inline struct bio *
-crypt_clone(struct crypt_config *cc, struct crypt_io *io, struct bio *bio,
- sector_t sector, unsigned int *bvec_idx,
- struct convert_context *ctx)
-{
- struct bio *clone;
-
- if (bio_data_dir(bio) == WRITE) {
- clone = crypt_alloc_buffer(cc, bio->bi_size,
- io->first_clone, bvec_idx);
- if (clone) {
- ctx->bio_out = clone;
- if (crypt_convert(cc, ctx) < 0) {
- crypt_free_buffer_pages(cc, clone,
- clone->bi_size);
- bio_put(clone);
- return NULL;
- }
- }
- } else {
- /*
- * The block layer might modify the bvec array, so always
- * copy the required bvecs because we need the original
- * one in order to decrypt the whole bio data *afterwards*.
- */
- clone = bio_alloc(GFP_NOIO, bio_segments(bio));
- if (clone) {
- clone->bi_idx = 0;
- clone->bi_vcnt = bio_segments(bio);
- clone->bi_size = bio->bi_size;
- memcpy(clone->bi_io_vec, bio_iovec(bio),
- sizeof(struct bio_vec) * clone->bi_vcnt);
- }
- }
-
- if (!clone)
- return NULL;
-
- clone->bi_private = io;
- clone->bi_end_io = crypt_endio;
- clone->bi_bdev = cc->dev->bdev;
- clone->bi_sector = cc->start + sector;
- clone->bi_rw = bio->bi_rw;
-
- return clone;
-}
-
static int crypt_map(struct dm_target *ti, struct bio *bio,
union map_info *map_context)
{
- struct crypt_config *cc = (struct crypt_config *) ti->private;
- struct crypt_io *io = mempool_alloc(cc->io_pool, GFP_NOIO);
- struct convert_context ctx;
- struct bio *clone;
- unsigned int remaining = bio->bi_size;
- sector_t sector = bio->bi_sector - ti->begin;
- unsigned int bvec_idx = 0;
+ struct crypt_config *cc = ti->private;
+ struct crypt_io *io;
+ io = mempool_alloc(cc->io_pool, GFP_NOIO);
io->target = ti;
- io->bio = bio;
+ io->base_bio = bio;
io->first_clone = NULL;
- io->error = 0;
- atomic_set(&io->pending, 1); /* hold a reference */
-
- if (bio_data_dir(bio) == WRITE)
- crypt_convert_init(cc, &ctx, NULL, bio, sector, 1);
-
- /*
- * The allocated buffers can be smaller than the whole bio,
- * so repeat the whole process until all the data can be handled.
- */
- while (remaining) {
- clone = crypt_clone(cc, io, bio, sector, &bvec_idx, &ctx);
- if (!clone)
- goto cleanup;
-
- if (!io->first_clone) {
- /*
- * hold a reference to the first clone, because it
- * holds the bio_vec array and that can't be freed
- * before all other clones are released
- */
- bio_get(clone);
- io->first_clone = clone;
- }
- atomic_inc(&io->pending);
+ io->error = io->post_process = 0;
+ atomic_set(&io->pending, 0);
+ kcryptd_queue_io(io);
- remaining -= clone->bi_size;
- sector += bio_sectors(clone);
-
- generic_make_request(clone);
-
- /* out of memory -> run queues */
- if (remaining)
- blk_congestion_wait(bio_data_dir(clone), HZ/100);
- }
-
- /* drop reference, clones could have returned before we reach this */
- dec_pending(io, 0);
return 0;
-
-cleanup:
- if (io->first_clone) {
- dec_pending(io, -ENOMEM);
- return 0;
- }
-
- /* if no bio has been dispatched yet, we can directly return the error */
- mempool_free(io, cc->io_pool);
- return -ENOMEM;
}
static int crypt_status(struct dm_target *ti, status_type_t type,
@@ -883,14 +952,71 @@ static int crypt_status(struct dm_target *ti, status_type_t type,
return 0;
}
+static void crypt_postsuspend(struct dm_target *ti)
+{
+ struct crypt_config *cc = ti->private;
+
+ set_bit(DM_CRYPT_SUSPENDED, &cc->flags);
+}
+
+static int crypt_preresume(struct dm_target *ti)
+{
+ struct crypt_config *cc = ti->private;
+
+ if (!test_bit(DM_CRYPT_KEY_VALID, &cc->flags)) {
+ DMERR("aborting resume - crypt key is not set.");
+ return -EAGAIN;
+ }
+
+ return 0;
+}
+
+static void crypt_resume(struct dm_target *ti)
+{
+ struct crypt_config *cc = ti->private;
+
+ clear_bit(DM_CRYPT_SUSPENDED, &cc->flags);
+}
+
+/* Message interface
+ * key set <key>
+ * key wipe
+ */
+static int crypt_message(struct dm_target *ti, unsigned argc, char **argv)
+{
+ struct crypt_config *cc = ti->private;
+
+ if (argc < 2)
+ goto error;
+
+ if (!strnicmp(argv[0], MESG_STR("key"))) {
+ if (!test_bit(DM_CRYPT_SUSPENDED, &cc->flags)) {
+ DMWARN("not suspended during key manipulation.");
+ return -EINVAL;
+ }
+ if (argc == 3 && !strnicmp(argv[1], MESG_STR("set")))
+ return crypt_set_key(cc, argv[2]);
+ if (argc == 2 && !strnicmp(argv[1], MESG_STR("wipe")))
+ return crypt_wipe_key(cc);
+ }
+
+error:
+ DMWARN("unrecognised message received.");
+ return -EINVAL;
+}
+
static struct target_type crypt_target = {
.name = "crypt",
- .version= {1, 1, 0},
+ .version= {1, 3, 0},
.module = THIS_MODULE,
.ctr = crypt_ctr,
.dtr = crypt_dtr,
.map = crypt_map,
.status = crypt_status,
+ .postsuspend = crypt_postsuspend,
+ .preresume = crypt_preresume,
+ .resume = crypt_resume,
+ .message = crypt_message,
};
static int __init dm_crypt_init(void)
diff --git a/drivers/md/dm-emc.c b/drivers/md/dm-emc.c
index 2a374ccb30d..2b2d45d7baa 100644
--- a/drivers/md/dm-emc.c
+++ b/drivers/md/dm-emc.c
@@ -126,7 +126,8 @@ static struct request *get_failover_req(struct emc_handler *h,
memset(&rq->cmd, 0, BLK_MAX_CDB);
rq->timeout = EMC_FAILOVER_TIMEOUT;
- rq->flags |= (REQ_BLOCK_PC | REQ_FAILFAST | REQ_NOMERGE);
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
+ rq->cmd_flags |= REQ_FAILFAST | REQ_NOMERGE;
return rq;
}
diff --git a/drivers/md/dm-exception-store.c b/drivers/md/dm-exception-store.c
index d12379b5cdb..99cdffa7fbf 100644
--- a/drivers/md/dm-exception-store.c
+++ b/drivers/md/dm-exception-store.c
@@ -17,6 +17,7 @@
#include <linux/slab.h>
#define DM_MSG_PREFIX "snapshots"
+#define DM_CHUNK_SIZE_DEFAULT_SECTORS 32 /* 16KB */
/*-----------------------------------------------------------------
* Persistent snapshots, by persistent we mean that the snapshot
@@ -150,6 +151,7 @@ static int alloc_area(struct pstore *ps)
static void free_area(struct pstore *ps)
{
vfree(ps->area);
+ ps->area = NULL;
}
/*
@@ -198,48 +200,79 @@ static int read_header(struct pstore *ps, int *new_snapshot)
int r;
struct disk_header *dh;
chunk_t chunk_size;
+ int chunk_size_supplied = 1;
- r = chunk_io(ps, 0, READ);
+ /*
+ * Use default chunk size (or hardsect_size, if larger) if none supplied
+ */
+ if (!ps->snap->chunk_size) {
+ ps->snap->chunk_size = max(DM_CHUNK_SIZE_DEFAULT_SECTORS,
+ bdev_hardsect_size(ps->snap->cow->bdev) >> 9);
+ ps->snap->chunk_mask = ps->snap->chunk_size - 1;
+ ps->snap->chunk_shift = ffs(ps->snap->chunk_size) - 1;
+ chunk_size_supplied = 0;
+ }
+
+ r = dm_io_get(sectors_to_pages(ps->snap->chunk_size));
if (r)
return r;
+ r = alloc_area(ps);
+ if (r)
+ goto bad1;
+
+ r = chunk_io(ps, 0, READ);
+ if (r)
+ goto bad2;
+
dh = (struct disk_header *) ps->area;
if (le32_to_cpu(dh->magic) == 0) {
*new_snapshot = 1;
+ return 0;
+ }
- } else if (le32_to_cpu(dh->magic) == SNAP_MAGIC) {
- *new_snapshot = 0;
- ps->valid = le32_to_cpu(dh->valid);
- ps->version = le32_to_cpu(dh->version);
- 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");
+ if (le32_to_cpu(dh->magic) != SNAP_MAGIC) {
+ DMWARN("Invalid or corrupt snapshot");
r = -ENXIO;
+ goto bad2;
}
+ *new_snapshot = 0;
+ ps->valid = le32_to_cpu(dh->valid);
+ ps->version = le32_to_cpu(dh->version);
+ chunk_size = le32_to_cpu(dh->chunk_size);
+
+ if (!chunk_size_supplied || ps->snap->chunk_size == chunk_size)
+ return 0;
+
+ 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 = dm_io_get(sectors_to_pages(chunk_size));
+ if (r)
+ return r;
+
+ r = alloc_area(ps);
+ if (r)
+ goto bad1;
+
+ return 0;
+
+bad2:
+ free_area(ps);
+bad1:
+ dm_io_put(sectors_to_pages(ps->snap->chunk_size));
return r;
}
@@ -263,42 +296,29 @@ static int write_header(struct pstore *ps)
*/
static struct disk_exception *get_exception(struct pstore *ps, uint32_t index)
{
- if (index >= ps->exceptions_per_area)
- return NULL;
+ BUG_ON(index >= ps->exceptions_per_area);
return ((struct disk_exception *) ps->area) + index;
}
-static int read_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *result)
+static void read_exception(struct pstore *ps,
+ uint32_t index, struct disk_exception *result)
{
- struct disk_exception *e;
-
- e = get_exception(ps, index);
- if (!e)
- return -EINVAL;
+ struct disk_exception *e = get_exception(ps, index);
/* copy it */
result->old_chunk = le64_to_cpu(e->old_chunk);
result->new_chunk = le64_to_cpu(e->new_chunk);
-
- return 0;
}
-static int write_exception(struct pstore *ps,
- uint32_t index, struct disk_exception *de)
+static void write_exception(struct pstore *ps,
+ uint32_t index, struct disk_exception *de)
{
- struct disk_exception *e;
-
- e = get_exception(ps, index);
- if (!e)
- return -EINVAL;
+ struct disk_exception *e = get_exception(ps, index);
/* copy it */
e->old_chunk = cpu_to_le64(de->old_chunk);
e->new_chunk = cpu_to_le64(de->new_chunk);
-
- return 0;
}
/*
@@ -316,10 +336,7 @@ static int insert_exceptions(struct pstore *ps, int *full)
*full = 1;
for (i = 0; i < ps->exceptions_per_area; i++) {
- r = read_exception(ps, i, &de);
-
- if (r)
- return r;
+ read_exception(ps, i, &de);
/*
* If the new_chunk is pointing at the start of
@@ -519,6 +536,16 @@ static void persistent_commit(struct exception_store *store,
if (r)
ps->valid = 0;
+ /*
+ * Have we completely filled the current area ?
+ */
+ if (ps->current_committed == ps->exceptions_per_area) {
+ ps->current_committed = 0;
+ r = zero_area(ps, ps->current_area + 1);
+ if (r)
+ ps->valid = 0;
+ }
+
for (i = 0; i < ps->callback_count; i++) {
cb = ps->callbacks + i;
cb->callback(cb->context, r == 0 ? 1 : 0);
@@ -526,16 +553,6 @@ static void persistent_commit(struct exception_store *store,
ps->callback_count = 0;
}
-
- /*
- * Have we completely filled the current area ?
- */
- if (ps->current_committed == ps->exceptions_per_area) {
- ps->current_committed = 0;
- r = zero_area(ps, ps->current_area + 1);
- if (r)
- ps->valid = 0;
- }
}
static void persistent_drop(struct exception_store *store)
@@ -547,32 +564,22 @@ static void persistent_drop(struct exception_store *store)
DMWARN("write header failed");
}
-int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
+int dm_create_persistent(struct exception_store *store)
{
- int r;
struct pstore *ps;
- r = dm_io_get(sectors_to_pages(chunk_size));
- if (r)
- return r;
-
/* allocate the pstore */
ps = kmalloc(sizeof(*ps), GFP_KERNEL);
- if (!ps) {
- r = -ENOMEM;
- goto bad;
- }
+ if (!ps)
+ return -ENOMEM;
ps->snap = store->snap;
ps->valid = 1;
ps->version = SNAPSHOT_DISK_VERSION;
+ ps->area = NULL;
ps->next_free = 2; /* skipping the header and first area */
ps->current_committed = 0;
- r = alloc_area(ps);
- if (r)
- goto bad;
-
ps->callback_count = 0;
atomic_set(&ps->pending_count, 0);
ps->callbacks = NULL;
@@ -586,13 +593,6 @@ int dm_create_persistent(struct exception_store *store, uint32_t chunk_size)
store->context = ps;
return 0;
-
- bad:
- dm_io_put(sectors_to_pages(chunk_size));
- if (ps && ps->area)
- free_area(ps);
- kfree(ps);
- return r;
}
/*-----------------------------------------------------------------
@@ -642,18 +642,16 @@ static void transient_fraction_full(struct exception_store *store,
*denominator = get_dev_size(store->snap->cow->bdev);
}
-int dm_create_transient(struct exception_store *store,
- struct dm_snapshot *s, int blocksize)
+int dm_create_transient(struct exception_store *store)
{
struct transient_c *tc;
- memset(store, 0, sizeof(*store));
store->destroy = transient_destroy;
store->read_metadata = transient_read_metadata;
store->prepare_exception = transient_prepare;
store->commit_exception = transient_commit;
+ store->drop_snapshot = NULL;
store->fraction_full = transient_fraction_full;
- store->snap = s;
tc = kmalloc(sizeof(struct transient_c), GFP_KERNEL);
if (!tc)
diff --git a/drivers/md/dm-linear.c b/drivers/md/dm-linear.c
index 47b3c62bbdb..00234909b3d 100644
--- a/drivers/md/dm-linear.c
+++ b/drivers/md/dm-linear.c
@@ -98,14 +98,31 @@ static int linear_status(struct dm_target *ti, status_type_t type,
return 0;
}
+static int linear_ioctl(struct dm_target *ti, struct inode *inode,
+ struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct linear_c *lc = (struct linear_c *) ti->private;
+ struct block_device *bdev = lc->dev->bdev;
+ struct file fake_file = {};
+ struct dentry fake_dentry = {};
+
+ fake_file.f_mode = lc->dev->mode;
+ fake_file.f_dentry = &fake_dentry;
+ fake_dentry.d_inode = bdev->bd_inode;
+
+ return blkdev_driver_ioctl(bdev->bd_inode, &fake_file, bdev->bd_disk, cmd, arg);
+}
+
static struct target_type linear_target = {
.name = "linear",
- .version= {1, 0, 1},
+ .version= {1, 0, 2},
.module = THIS_MODULE,
.ctr = linear_ctr,
.dtr = linear_dtr,
.map = linear_map,
.status = linear_status,
+ .ioctl = linear_ioctl,
};
int __init dm_linear_init(void)
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index 93f701ea87b..d754e0bc6e9 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -114,12 +114,10 @@ static void trigger_event(void *data);
static struct pgpath *alloc_pgpath(void)
{
- struct pgpath *pgpath = kmalloc(sizeof(*pgpath), GFP_KERNEL);
+ struct pgpath *pgpath = kzalloc(sizeof(*pgpath), GFP_KERNEL);
- if (pgpath) {
- memset(pgpath, 0, sizeof(*pgpath));
+ if (pgpath)
pgpath->path.is_active = 1;
- }
return pgpath;
}
@@ -133,12 +131,10 @@ static struct priority_group *alloc_priority_group(void)
{
struct priority_group *pg;
- pg = kmalloc(sizeof(*pg), GFP_KERNEL);
- if (!pg)
- return NULL;
+ pg = kzalloc(sizeof(*pg), GFP_KERNEL);
- memset(pg, 0, sizeof(*pg));
- INIT_LIST_HEAD(&pg->pgpaths);
+ if (pg)
+ INIT_LIST_HEAD(&pg->pgpaths);
return pg;
}
@@ -168,13 +164,12 @@ static void free_priority_group(struct priority_group *pg,
kfree(pg);
}
-static struct multipath *alloc_multipath(void)
+static struct multipath *alloc_multipath(struct dm_target *ti)
{
struct multipath *m;
- m = kmalloc(sizeof(*m), GFP_KERNEL);
+ m = kzalloc(sizeof(*m), GFP_KERNEL);
if (m) {
- memset(m, 0, sizeof(*m));
INIT_LIST_HEAD(&m->priority_groups);
spin_lock_init(&m->lock);
m->queue_io = 1;
@@ -185,6 +180,8 @@ static struct multipath *alloc_multipath(void)
kfree(m);
return NULL;
}
+ m->ti = ti;
+ ti->private = m;
}
return m;
@@ -557,8 +554,7 @@ static struct pgpath *parse_path(struct arg_set *as, struct path_selector *ps,
}
static struct priority_group *parse_priority_group(struct arg_set *as,
- struct multipath *m,
- struct dm_target *ti)
+ struct multipath *m)
{
static struct param _params[] = {
{1, 1024, "invalid number of paths"},
@@ -568,6 +564,7 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
int r;
unsigned i, nr_selector_args, nr_params;
struct priority_group *pg;
+ struct dm_target *ti = m->ti;
if (as->argc < 2) {
as->argc = 0;
@@ -624,12 +621,12 @@ static struct priority_group *parse_priority_group(struct arg_set *as,
return NULL;
}
-static int parse_hw_handler(struct arg_set *as, struct multipath *m,
- struct dm_target *ti)
+static int parse_hw_handler(struct arg_set *as, struct multipath *m)
{
int r;
struct hw_handler_type *hwht;
unsigned hw_argc;
+ struct dm_target *ti = m->ti;
static struct param _params[] = {
{0, 1024, "invalid number of hardware handler args"},
@@ -661,11 +658,11 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m,
return 0;
}
-static int parse_features(struct arg_set *as, struct multipath *m,
- struct dm_target *ti)
+static int parse_features(struct arg_set *as, struct multipath *m)
{
int r;
unsigned argc;
+ struct dm_target *ti = m->ti;
static struct param _params[] = {
{0, 1, "invalid number of feature args"},
@@ -704,19 +701,17 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
as.argc = argc;
as.argv = argv;
- m = alloc_multipath();
+ m = alloc_multipath(ti);
if (!m) {
ti->error = "can't allocate multipath";
return -EINVAL;
}
- m->ti = ti;
-
- r = parse_features(&as, m, ti);
+ r = parse_features(&as, m);
if (r)
goto bad;
- r = parse_hw_handler(&as, m, ti);
+ r = parse_hw_handler(&as, m);
if (r)
goto bad;
@@ -732,7 +727,7 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
while (as.argc) {
struct priority_group *pg;
- pg = parse_priority_group(&as, m, ti);
+ pg = parse_priority_group(&as, m);
if (!pg) {
r = -EINVAL;
goto bad;
@@ -752,8 +747,6 @@ static int multipath_ctr(struct dm_target *ti, unsigned int argc,
goto bad;
}
- ti->private = m;
-
return 0;
bad:
@@ -1266,12 +1259,47 @@ error:
return -EINVAL;
}
+static int multipath_ioctl(struct dm_target *ti, struct inode *inode,
+ struct file *filp, unsigned int cmd,
+ unsigned long arg)
+{
+ struct multipath *m = (struct multipath *) ti->private;
+ struct block_device *bdev = NULL;
+ unsigned long flags;
+ struct file fake_file = {};
+ struct dentry fake_dentry = {};
+ int r = 0;
+
+ fake_file.f_dentry = &fake_dentry;
+
+ spin_lock_irqsave(&m->lock, flags);
+
+ if (!m->current_pgpath)
+ __choose_pgpath(m);
+
+ if (m->current_pgpath) {
+ bdev = m->current_pgpath->path.dev->bdev;
+ fake_dentry.d_inode = bdev->bd_inode;
+ fake_file.f_mode = m->current_pgpath->path.dev->mode;
+ }
+
+ if (m->queue_io)
+ r = -EAGAIN;
+ else if (!bdev)
+ r = -EIO;
+
+ spin_unlock_irqrestore(&m->lock, flags);
+
+ return r ? : blkdev_driver_ioctl(bdev->bd_inode, &fake_file,
+ bdev->bd_disk, cmd, arg);
+}
+
/*-----------------------------------------------------------------
* Module setup
*---------------------------------------------------------------*/
static struct target_type multipath_target = {
.name = "multipath",
- .version = {1, 0, 4},
+ .version = {1, 0, 5},
.module = THIS_MODULE,
.ctr = multipath_ctr,
.dtr = multipath_dtr,
@@ -1281,6 +1309,7 @@ static struct target_type multipath_target = {
.resume = multipath_resume,
.status = multipath_status,
.message = multipath_message,
+ .ioctl = multipath_ioctl,
};
static int __init dm_multipath_init(void)
diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c
index c54de989eb0..659224cb7c5 100644
--- a/drivers/md/dm-raid1.c
+++ b/drivers/md/dm-raid1.c
@@ -1213,9 +1213,9 @@ static int mirror_status(struct dm_target *ti, status_type_t type,
break;
case STATUSTYPE_TABLE:
- DMEMIT("%d ", ms->nr_mirrors);
+ DMEMIT("%d", ms->nr_mirrors);
for (m = 0; m < ms->nr_mirrors; m++)
- DMEMIT("%s %llu ", ms->mirror[m].dev->name,
+ DMEMIT(" %s %llu", ms->mirror[m].dev->name,
(unsigned long long)ms->mirror[m].offset);
}
diff --git a/drivers/md/dm-snap.c b/drivers/md/dm-snap.c
index 1d0fafda0f7..5281e009407 100644
--- a/drivers/md/dm-snap.c
+++ b/drivers/md/dm-snap.c
@@ -39,6 +39,9 @@
*/
#define SNAPSHOT_PAGES 256
+struct workqueue_struct *ksnapd;
+static void flush_queued_bios(void *data);
+
struct pending_exception {
struct exception e;
@@ -56,7 +59,7 @@ struct pending_exception {
/*
* The primary pending_exception is the one that holds
- * the sibling_count and the list of origin_bios for a
+ * the ref_count and the list of origin_bios for a
* group of pending_exceptions. It is always last to get freed.
* These fields get set up when writing to the origin.
*/
@@ -69,7 +72,7 @@ struct pending_exception {
* the sibling concerned and not pe->primary_pe->snap->lock unless
* they are the same.
*/
- atomic_t sibling_count;
+ atomic_t ref_count;
/* Pointer back to snapshot context */
struct dm_snapshot *snap;
@@ -387,15 +390,46 @@ static inline ulong round_up(ulong n, ulong size)
return (n + size) & ~size;
}
-static void read_snapshot_metadata(struct dm_snapshot *s)
+static int set_chunk_size(struct dm_snapshot *s, const char *chunk_size_arg,
+ char **error)
{
- if (s->store.read_metadata(&s->store)) {
- down_write(&s->lock);
- s->valid = 0;
- up_write(&s->lock);
+ unsigned long chunk_size;
+ char *value;
+
+ chunk_size = simple_strtoul(chunk_size_arg, &value, 10);
+ if (*chunk_size_arg == '\0' || *value != '\0') {
+ *error = "Invalid chunk size";
+ return -EINVAL;
+ }
+
+ if (!chunk_size) {
+ s->chunk_size = s->chunk_mask = s->chunk_shift = 0;
+ return 0;
+ }
+
+ /*
+ * Chunk size must be multiple of page size. Silently
+ * round up if it's not.
+ */
+ chunk_size = round_up(chunk_size, PAGE_SIZE >> 9);
+
+ /* Check chunk_size is a power of 2 */
+ if (chunk_size & (chunk_size - 1)) {
+ *error = "Chunk size is not a power of 2";
+ return -EINVAL;
+ }
- dm_table_event(s->table);
+ /* Validate the chunk size against the device block size */
+ if (chunk_size % (bdev_hardsect_size(s->cow->bdev) >> 9)) {
+ *error = "Chunk size is not a multiple of device blocksize";
+ return -EINVAL;
}
+
+ s->chunk_size = chunk_size;
+ s->chunk_mask = chunk_size - 1;
+ s->chunk_shift = ffs(chunk_size) - 1;
+
+ return 0;
}
/*
@@ -404,15 +438,12 @@ static void read_snapshot_metadata(struct dm_snapshot *s)
static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
struct dm_snapshot *s;
- unsigned long chunk_size;
int r = -EINVAL;
char persistent;
char *origin_path;
char *cow_path;
- char *value;
- int blocksize;
- if (argc < 4) {
+ if (argc != 4) {
ti->error = "requires exactly 4 arguments";
r = -EINVAL;
goto bad1;
@@ -428,13 +459,6 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad1;
}
- chunk_size = simple_strtoul(argv[3], &value, 10);
- if (chunk_size == 0 || value == NULL) {
- ti->error = "Invalid chunk size";
- r = -EINVAL;
- goto bad1;
- }
-
s = kmalloc(sizeof(*s), GFP_KERNEL);
if (s == NULL) {
ti->error = "Cannot allocate snapshot context private "
@@ -457,36 +481,17 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad2;
}
- /*
- * Chunk size must be multiple of page size. Silently
- * round up if it's not.
- */
- chunk_size = round_up(chunk_size, PAGE_SIZE >> 9);
-
- /* Validate the chunk size against the device block size */
- blocksize = s->cow->bdev->bd_disk->queue->hardsect_size;
- if (chunk_size % (blocksize >> 9)) {
- ti->error = "Chunk size is not a multiple of device blocksize";
- r = -EINVAL;
- goto bad3;
- }
-
- /* Check chunk_size is a power of 2 */
- if (chunk_size & (chunk_size - 1)) {
- ti->error = "Chunk size is not a power of 2";
- r = -EINVAL;
+ r = set_chunk_size(s, argv[3], &ti->error);
+ if (r)
goto bad3;
- }
- s->chunk_size = chunk_size;
- s->chunk_mask = chunk_size - 1;
s->type = persistent;
- s->chunk_shift = ffs(chunk_size) - 1;
s->valid = 1;
s->active = 0;
s->last_percent = 0;
init_rwsem(&s->lock);
+ spin_lock_init(&s->pe_lock);
s->table = ti->table;
/* Allocate hash table for COW data */
@@ -496,16 +501,12 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
goto bad3;
}
- /*
- * Check the persistent flag - done here because we need the iobuf
- * to check the LV header
- */
s->store.snap = s;
if (persistent == 'P')
- r = dm_create_persistent(&s->store, chunk_size);
+ r = dm_create_persistent(&s->store);
else
- r = dm_create_transient(&s->store, s, blocksize);
+ r = dm_create_transient(&s->store);
if (r) {
ti->error = "Couldn't create exception store";
@@ -520,7 +521,14 @@ static int snapshot_ctr(struct dm_target *ti, unsigned int argc, char **argv)
}
/* Metadata must only be loaded into one table at once */
- read_snapshot_metadata(s);
+ r = s->store.read_metadata(&s->store);
+ if (r) {
+ ti->error = "Failed to read snapshot metadata";
+ goto bad6;
+ }
+
+ bio_list_init(&s->queued_bios);
+ INIT_WORK(&s->queued_bios_work, flush_queued_bios, s);
/* Add snapshot to the list of snapshots for this origin */
/* Exceptions aren't triggered till snapshot_resume() is called */
@@ -560,6 +568,8 @@ static void snapshot_dtr(struct dm_target *ti)
{
struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
+ flush_workqueue(ksnapd);
+
/* Prevent further origin writes from using this snapshot. */
/* After this returns there can be no new kcopyd jobs. */
unregister_snapshot(s);
@@ -593,6 +603,19 @@ static void flush_bios(struct bio *bio)
}
}
+static void flush_queued_bios(void *data)
+{
+ struct dm_snapshot *s = (struct dm_snapshot *) data;
+ struct bio *queued_bios;
+ unsigned long flags;
+
+ spin_lock_irqsave(&s->pe_lock, flags);
+ queued_bios = bio_list_get(&s->queued_bios);
+ spin_unlock_irqrestore(&s->pe_lock, flags);
+
+ flush_bios(queued_bios);
+}
+
/*
* Error a list of buffers.
*/
@@ -608,28 +631,7 @@ static void error_bios(struct bio *bio)
}
}
-static inline void error_snapshot_bios(struct pending_exception *pe)
-{
- error_bios(bio_list_get(&pe->snapshot_bios));
-}
-
-static struct bio *__flush_bios(struct pending_exception *pe)
-{
- /*
- * If this pe is involved in a write to the origin and
- * it is the last sibling to complete then release
- * the bios for the original write to the origin.
- */
-
- if (pe->primary_pe &&
- atomic_dec_and_test(&pe->primary_pe->sibling_count))
- return bio_list_get(&pe->primary_pe->origin_bios);
-
- return NULL;
-}
-
-static void __invalidate_snapshot(struct dm_snapshot *s,
- struct pending_exception *pe, int err)
+static void __invalidate_snapshot(struct dm_snapshot *s, int err)
{
if (!s->valid)
return;
@@ -639,9 +641,6 @@ static void __invalidate_snapshot(struct dm_snapshot *s,
else if (err == -ENOMEM)
DMERR("Invalidating snapshot: Unable to allocate exception.");
- if (pe)
- remove_exception(&pe->e);
-
if (s->store.drop_snapshot)
s->store.drop_snapshot(&s->store);
@@ -650,78 +649,95 @@ static void __invalidate_snapshot(struct dm_snapshot *s,
dm_table_event(s->table);
}
+static void get_pending_exception(struct pending_exception *pe)
+{
+ atomic_inc(&pe->ref_count);
+}
+
+static struct bio *put_pending_exception(struct pending_exception *pe)
+{
+ struct pending_exception *primary_pe;
+ struct bio *origin_bios = NULL;
+
+ primary_pe = pe->primary_pe;
+
+ /*
+ * If this pe is involved in a write to the origin and
+ * it is the last sibling to complete then release
+ * the bios for the original write to the origin.
+ */
+ if (primary_pe &&
+ atomic_dec_and_test(&primary_pe->ref_count))
+ origin_bios = bio_list_get(&primary_pe->origin_bios);
+
+ /*
+ * Free the pe if it's not linked to an origin write or if
+ * it's not itself a primary pe.
+ */
+ if (!primary_pe || primary_pe != pe)
+ free_pending_exception(pe);
+
+ /*
+ * Free the primary pe if nothing references it.
+ */
+ if (primary_pe && !atomic_read(&primary_pe->ref_count))
+ free_pending_exception(primary_pe);
+
+ return origin_bios;
+}
+
static void pending_complete(struct pending_exception *pe, int success)
{
struct exception *e;
- struct pending_exception *primary_pe;
struct dm_snapshot *s = pe->snap;
- struct bio *flush = NULL;
+ struct bio *origin_bios = NULL;
+ struct bio *snapshot_bios = NULL;
+ int error = 0;
if (!success) {
/* Read/write error - snapshot is unusable */
down_write(&s->lock);
- __invalidate_snapshot(s, pe, -EIO);
- flush = __flush_bios(pe);
- up_write(&s->lock);
-
- error_snapshot_bios(pe);
+ __invalidate_snapshot(s, -EIO);
+ error = 1;
goto out;
}
e = alloc_exception();
if (!e) {
down_write(&s->lock);
- __invalidate_snapshot(s, pe, -ENOMEM);
- flush = __flush_bios(pe);
- up_write(&s->lock);
-
- error_snapshot_bios(pe);
+ __invalidate_snapshot(s, -ENOMEM);
+ error = 1;
goto out;
}
*e = pe->e;
- /*
- * Add a proper exception, and remove the
- * in-flight exception from the list.
- */
down_write(&s->lock);
if (!s->valid) {
- flush = __flush_bios(pe);
- up_write(&s->lock);
-
free_exception(e);
-
- error_snapshot_bios(pe);
+ error = 1;
goto out;
}
+ /*
+ * Add a proper exception, and remove the
+ * in-flight exception from the list.
+ */
insert_exception(&s->complete, e);
+
+ out:
remove_exception(&pe->e);
- flush = __flush_bios(pe);
+ snapshot_bios = bio_list_get(&pe->snapshot_bios);
+ origin_bios = put_pending_exception(pe);
up_write(&s->lock);
/* Submit any pending write bios */
- flush_bios(bio_list_get(&pe->snapshot_bios));
-
- out:
- primary_pe = pe->primary_pe;
-
- /*
- * Free the pe if it's not linked to an origin write or if
- * it's not itself a primary pe.
- */
- if (!primary_pe || primary_pe != pe)
- free_pending_exception(pe);
-
- /*
- * Free the primary pe if nothing references it.
- */
- if (primary_pe && !atomic_read(&primary_pe->sibling_count))
- free_pending_exception(primary_pe);
+ if (error)
+ error_bios(snapshot_bios);
+ else
+ flush_bios(snapshot_bios);
- if (flush)
- flush_bios(flush);
+ flush_bios(origin_bios);
}
static void commit_callback(void *context, int success)
@@ -822,7 +838,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
bio_list_init(&pe->origin_bios);
bio_list_init(&pe->snapshot_bios);
pe->primary_pe = NULL;
- atomic_set(&pe->sibling_count, 1);
+ atomic_set(&pe->ref_count, 0);
pe->snap = s;
pe->started = 0;
@@ -831,6 +847,7 @@ __find_pending_exception(struct dm_snapshot *s, struct bio *bio)
return NULL;
}
+ get_pending_exception(pe);
insert_exception(&s->pending, &pe->e);
out:
@@ -850,7 +867,6 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
{
struct exception *e;
struct dm_snapshot *s = (struct dm_snapshot *) ti->private;
- int copy_needed = 0;
int r = 1;
chunk_t chunk;
struct pending_exception *pe = NULL;
@@ -865,32 +881,31 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
if (unlikely(bio_barrier(bio)))
return -EOPNOTSUPP;
+ /* FIXME: should only take write lock if we need
+ * to copy an exception */
+ down_write(&s->lock);
+
+ if (!s->valid) {
+ r = -EIO;
+ goto out_unlock;
+ }
+
+ /* If the block is already remapped - use that, else remap it */
+ e = lookup_exception(&s->complete, chunk);
+ if (e) {
+ remap_exception(s, e, bio);
+ goto out_unlock;
+ }
+
/*
* Write to snapshot - higher level takes care of RW/RO
* flags so we should only get this if we are
* writeable.
*/
if (bio_rw(bio) == WRITE) {
-
- /* FIXME: should only take write lock if we need
- * to copy an exception */
- down_write(&s->lock);
-
- if (!s->valid) {
- r = -EIO;
- goto out_unlock;
- }
-
- /* If the block is already remapped - use that, else remap it */
- e = lookup_exception(&s->complete, chunk);
- if (e) {
- remap_exception(s, e, bio);
- goto out_unlock;
- }
-
pe = __find_pending_exception(s, bio);
if (!pe) {
- __invalidate_snapshot(s, pe, -ENOMEM);
+ __invalidate_snapshot(s, -ENOMEM);
r = -EIO;
goto out_unlock;
}
@@ -898,45 +913,27 @@ static int snapshot_map(struct dm_target *ti, struct bio *bio,
remap_exception(s, &pe->e, bio);
bio_list_add(&pe->snapshot_bios, bio);
+ r = 0;
+
if (!pe->started) {
/* this is protected by snap->lock */
pe->started = 1;
- copy_needed = 1;
- }
-
- r = 0;
-
- out_unlock:
- up_write(&s->lock);
-
- if (copy_needed)
+ up_write(&s->lock);
start_copy(pe);
- } else {
+ goto out;
+ }
+ } else
/*
* FIXME: this read path scares me because we
* always use the origin when we have a pending
* exception. However I can't think of a
* situation where this is wrong - ejt.
*/
+ bio->bi_bdev = s->origin->bdev;
- /* Do reads */
- down_read(&s->lock);
-
- if (!s->valid) {
- up_read(&s->lock);
- return -EIO;
- }
-
- /* See if it it has been remapped */
- e = lookup_exception(&s->complete, chunk);
- if (e)
- remap_exception(s, e, bio);
- else
- bio->bi_bdev = s->origin->bdev;
-
- up_read(&s->lock);
- }
-
+ out_unlock:
+ up_write(&s->lock);
+ out:
return r;
}
@@ -1025,7 +1022,7 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
* is already remapped in this snapshot
* and trigger an exception if not.
*
- * sibling_count is initialised to 1 so pending_complete()
+ * ref_count is initialised to 1 so pending_complete()
* won't destroy the primary_pe while we're inside this loop.
*/
e = lookup_exception(&snap->complete, chunk);
@@ -1034,7 +1031,7 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
pe = __find_pending_exception(snap, bio);
if (!pe) {
- __invalidate_snapshot(snap, pe, ENOMEM);
+ __invalidate_snapshot(snap, -ENOMEM);
goto next_snapshot;
}
@@ -1056,8 +1053,8 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
}
if (!pe->primary_pe) {
- atomic_inc(&primary_pe->sibling_count);
pe->primary_pe = primary_pe;
+ get_pending_exception(primary_pe);
}
if (!pe->started) {
@@ -1070,20 +1067,20 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
}
if (!primary_pe)
- goto out;
+ return r;
/*
* If this is the first time we're processing this chunk and
- * sibling_count is now 1 it means all the pending exceptions
+ * ref_count is now 1 it means all the pending exceptions
* got completed while we were in the loop above, so it falls to
* us here to remove the primary_pe and submit any origin_bios.
*/
- if (first && atomic_dec_and_test(&primary_pe->sibling_count)) {
+ if (first && atomic_dec_and_test(&primary_pe->ref_count)) {
flush_bios(bio_list_get(&primary_pe->origin_bios));
free_pending_exception(primary_pe);
/* If we got here, pe_queue is necessarily empty. */
- goto out;
+ return r;
}
/*
@@ -1092,7 +1089,6 @@ static int __origin_write(struct list_head *snapshots, struct bio *bio)
list_for_each_entry_safe(pe, next_pe, &pe_queue, list)
start_copy(pe);
- out:
return r;
}
@@ -1205,7 +1201,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, 4, 0},
+ .version = {1, 5, 0},
.module = THIS_MODULE,
.ctr = origin_ctr,
.dtr = origin_dtr,
@@ -1216,7 +1212,7 @@ static struct target_type origin_target = {
static struct target_type snapshot_target = {
.name = "snapshot",
- .version = {1, 4, 0},
+ .version = {1, 5, 0},
.module = THIS_MODULE,
.ctr = snapshot_ctr,
.dtr = snapshot_dtr,
@@ -1275,8 +1271,17 @@ static int __init dm_snapshot_init(void)
goto bad5;
}
+ ksnapd = create_singlethread_workqueue("ksnapd");
+ if (!ksnapd) {
+ DMERR("Failed to create ksnapd workqueue.");
+ r = -ENOMEM;
+ goto bad6;
+ }
+
return 0;
+ bad6:
+ mempool_destroy(pending_pool);
bad5:
kmem_cache_destroy(pending_cache);
bad4:
@@ -1294,6 +1299,8 @@ static void __exit dm_snapshot_exit(void)
{
int r;
+ destroy_workqueue(ksnapd);
+
r = dm_unregister_target(&snapshot_target);
if (r)
DMERR("snapshot unregister failed %d", r);
diff --git a/drivers/md/dm-snap.h b/drivers/md/dm-snap.h
index fdec1e2dc87..15fa2ae6cdc 100644
--- a/drivers/md/dm-snap.h
+++ b/drivers/md/dm-snap.h
@@ -10,7 +10,9 @@
#define DM_SNAPSHOT_H
#include "dm.h"
+#include "dm-bio-list.h"
#include <linux/blkdev.h>
+#include <linux/workqueue.h>
struct exception_table {
uint32_t hash_mask;
@@ -112,10 +114,20 @@ struct dm_snapshot {
struct exception_table pending;
struct exception_table complete;
+ /*
+ * pe_lock protects all pending_exception operations and access
+ * as well as the snapshot_bios list.
+ */
+ spinlock_t pe_lock;
+
/* The on disk metadata handler */
struct exception_store store;
struct kcopyd_client *kcopyd_client;
+
+ /* Queue of snapshot writes for ksnapd to flush */
+ struct bio_list queued_bios;
+ struct work_struct queued_bios_work;
};
/*
@@ -128,10 +140,9 @@ int dm_add_exception(struct dm_snapshot *s, chunk_t old, chunk_t new);
* Constructor and destructor for the default persistent
* store.
*/
-int dm_create_persistent(struct exception_store *store, uint32_t chunk_size);
+int dm_create_persistent(struct exception_store *store);
-int dm_create_transient(struct exception_store *store,
- struct dm_snapshot *s, int blocksize);
+int dm_create_transient(struct exception_store *store);
/*
* Return the number of sectors in the device.
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 75fe9493e6a..05befa91807 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -522,56 +522,61 @@ static int __table_get_device(struct dm_table *t, struct dm_target *ti,
return 0;
}
-
-int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
- sector_t len, int mode, struct dm_dev **result)
+void dm_set_device_limits(struct dm_target *ti, struct block_device *bdev)
{
- int r = __table_get_device(ti->table, ti, path,
- start, len, mode, result);
- if (!r) {
- request_queue_t *q = bdev_get_queue((*result)->bdev);
- struct io_restrictions *rs = &ti->limits;
-
- /*
- * Combine the device limits low.
- *
- * FIXME: if we move an io_restriction struct
- * into q this would just be a call to
- * combine_restrictions_low()
- */
+ request_queue_t *q = bdev_get_queue(bdev);
+ struct io_restrictions *rs = &ti->limits;
+
+ /*
+ * Combine the device limits low.
+ *
+ * FIXME: if we move an io_restriction struct
+ * into q this would just be a call to
+ * combine_restrictions_low()
+ */
+ rs->max_sectors =
+ min_not_zero(rs->max_sectors, q->max_sectors);
+
+ /* FIXME: Device-Mapper on top of RAID-0 breaks because DM
+ * currently doesn't honor MD's merge_bvec_fn routine.
+ * In this case, we'll force DM to use PAGE_SIZE or
+ * smaller I/O, just to be safe. A better fix is in the
+ * works, but add this for the time being so it will at
+ * least operate correctly.
+ */
+ if (q->merge_bvec_fn)
rs->max_sectors =
- min_not_zero(rs->max_sectors, q->max_sectors);
+ min_not_zero(rs->max_sectors,
+ (unsigned int) (PAGE_SIZE >> 9));
- /* FIXME: Device-Mapper on top of RAID-0 breaks because DM
- * currently doesn't honor MD's merge_bvec_fn routine.
- * In this case, we'll force DM to use PAGE_SIZE or
- * smaller I/O, just to be safe. A better fix is in the
- * works, but add this for the time being so it will at
- * least operate correctly.
- */
- if (q->merge_bvec_fn)
- rs->max_sectors =
- min_not_zero(rs->max_sectors,
- (unsigned int) (PAGE_SIZE >> 9));
+ rs->max_phys_segments =
+ min_not_zero(rs->max_phys_segments,
+ q->max_phys_segments);
- rs->max_phys_segments =
- min_not_zero(rs->max_phys_segments,
- q->max_phys_segments);
+ rs->max_hw_segments =
+ min_not_zero(rs->max_hw_segments, q->max_hw_segments);
- rs->max_hw_segments =
- min_not_zero(rs->max_hw_segments, q->max_hw_segments);
+ rs->hardsect_size = max(rs->hardsect_size, q->hardsect_size);
- rs->hardsect_size = max(rs->hardsect_size, q->hardsect_size);
+ rs->max_segment_size =
+ min_not_zero(rs->max_segment_size, q->max_segment_size);
- rs->max_segment_size =
- min_not_zero(rs->max_segment_size, q->max_segment_size);
+ rs->seg_boundary_mask =
+ min_not_zero(rs->seg_boundary_mask,
+ q->seg_boundary_mask);
- rs->seg_boundary_mask =
- min_not_zero(rs->seg_boundary_mask,
- q->seg_boundary_mask);
+ rs->no_cluster |= !test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
+}
+EXPORT_SYMBOL_GPL(dm_set_device_limits);
- rs->no_cluster |= !test_bit(QUEUE_FLAG_CLUSTER, &q->queue_flags);
- }
+int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
+ sector_t len, int mode, struct dm_dev **result)
+{
+ int r = __table_get_device(ti->table, ti, path,
+ start, len, mode, result);
+
+ if (!r)
+ dm_set_device_limits(ti, (*result)->bdev);
return r;
}
@@ -939,9 +944,20 @@ void dm_table_postsuspend_targets(struct dm_table *t)
return suspend_targets(t, 1);
}
-void dm_table_resume_targets(struct dm_table *t)
+int dm_table_resume_targets(struct dm_table *t)
{
- int i;
+ int i, r = 0;
+
+ for (i = 0; i < t->num_targets; i++) {
+ struct dm_target *ti = t->targets + i;
+
+ if (!ti->type->preresume)
+ continue;
+
+ r = ti->type->preresume(ti);
+ if (r)
+ return r;
+ }
for (i = 0; i < t->num_targets; i++) {
struct dm_target *ti = t->targets + i;
@@ -949,6 +965,8 @@ void dm_table_resume_targets(struct dm_table *t)
if (ti->type->resume)
ti->type->resume(ti);
}
+
+ return 0;
}
int dm_table_any_congested(struct dm_table *t, int bdi_bits)
@@ -983,6 +1001,11 @@ int dm_table_flush_all(struct dm_table *t)
{
struct list_head *d, *devices = dm_table_get_devices(t);
int ret = 0;
+ unsigned i;
+
+ for (i = 0; i < t->num_targets; i++)
+ if (t->targets[i].type->flush)
+ t->targets[i].type->flush(&t->targets[i]);
for (d = devices->next; d != devices; d = d->next) {
struct dm_dev *dd = list_entry(d, struct dm_dev, list);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index c99bf9f0175..b5764a86c8b 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -20,6 +20,7 @@
#include <linux/idr.h>
#include <linux/hdreg.h>
#include <linux/blktrace_api.h>
+#include <linux/smp_lock.h>
#define DM_MSG_PREFIX "core"
@@ -101,6 +102,8 @@ struct mapped_device {
mempool_t *io_pool;
mempool_t *tio_pool;
+ struct bio_set *bs;
+
/*
* Event handling.
*/
@@ -121,16 +124,10 @@ struct mapped_device {
static kmem_cache_t *_io_cache;
static kmem_cache_t *_tio_cache;
-static struct bio_set *dm_set;
-
static int __init local_init(void)
{
int r;
- dm_set = bioset_create(16, 16, 4);
- if (!dm_set)
- return -ENOMEM;
-
/* allocate a slab for the dm_ios */
_io_cache = kmem_cache_create("dm_io",
sizeof(struct dm_io), 0, 0, NULL, NULL);
@@ -164,8 +161,6 @@ static void local_exit(void)
kmem_cache_destroy(_tio_cache);
kmem_cache_destroy(_io_cache);
- bioset_free(dm_set);
-
if (unregister_blkdev(_major, _name) < 0)
DMERR("unregister_blkdev failed");
@@ -288,6 +283,45 @@ static int dm_blk_getgeo(struct block_device *bdev, struct hd_geometry *geo)
return dm_get_geometry(md, geo);
}
+static int dm_blk_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct mapped_device *md;
+ struct dm_table *map;
+ struct dm_target *tgt;
+ int r = -ENOTTY;
+
+ /* We don't really need this lock, but we do need 'inode'. */
+ unlock_kernel();
+
+ md = inode->i_bdev->bd_disk->private_data;
+
+ map = dm_get_table(md);
+
+ if (!map || !dm_table_get_size(map))
+ goto out;
+
+ /* We only support devices that have a single target */
+ if (dm_table_get_num_targets(map) != 1)
+ goto out;
+
+ tgt = dm_table_get_target(map, 0);
+
+ if (dm_suspended(md)) {
+ r = -EAGAIN;
+ goto out;
+ }
+
+ if (tgt->type->ioctl)
+ r = tgt->type->ioctl(tgt, inode, file, cmd, arg);
+
+out:
+ dm_table_put(map);
+
+ lock_kernel();
+ return r;
+}
+
static inline struct dm_io *alloc_io(struct mapped_device *md)
{
return mempool_alloc(md->io_pool, GFP_NOIO);
@@ -435,7 +469,7 @@ static int clone_endio(struct bio *bio, unsigned int done, int error)
{
int r = 0;
struct target_io *tio = bio->bi_private;
- struct dm_io *io = tio->io;
+ struct mapped_device *md = tio->io->md;
dm_endio_fn endio = tio->ti->type->end_io;
if (bio->bi_size)
@@ -454,9 +488,15 @@ static int clone_endio(struct bio *bio, unsigned int done, int error)
return 1;
}
- free_tio(io->md, tio);
- dec_pending(io, error);
+ dec_pending(tio->io, error);
+
+ /*
+ * Store md for cleanup instead of tio which is about to get freed.
+ */
+ bio->bi_private = md->bs;
+
bio_put(bio);
+ free_tio(md, tio);
return r;
}
@@ -485,6 +525,7 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
{
int r;
sector_t sector;
+ struct mapped_device *md;
/*
* Sanity checks.
@@ -514,10 +555,14 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
else if (r < 0) {
/* error the io and bail out */
- struct dm_io *io = tio->io;
- free_tio(tio->io->md, tio);
- dec_pending(io, r);
+ md = tio->io->md;
+ dec_pending(tio->io, r);
+ /*
+ * Store bio_set for cleanup.
+ */
+ clone->bi_private = md->bs;
bio_put(clone);
+ free_tio(md, tio);
}
}
@@ -533,7 +578,9 @@ struct clone_info {
static void dm_bio_destructor(struct bio *bio)
{
- bio_free(bio, dm_set);
+ struct bio_set *bs = bio->bi_private;
+
+ bio_free(bio, bs);
}
/*
@@ -541,12 +588,12 @@ static void dm_bio_destructor(struct bio *bio)
*/
static struct bio *split_bvec(struct bio *bio, sector_t sector,
unsigned short idx, unsigned int offset,
- unsigned int len)
+ unsigned int len, struct bio_set *bs)
{
struct bio *clone;
struct bio_vec *bv = bio->bi_io_vec + idx;
- clone = bio_alloc_bioset(GFP_NOIO, 1, dm_set);
+ clone = bio_alloc_bioset(GFP_NOIO, 1, bs);
clone->bi_destructor = dm_bio_destructor;
*clone->bi_io_vec = *bv;
@@ -566,11 +613,13 @@ static struct bio *split_bvec(struct bio *bio, sector_t sector,
*/
static struct bio *clone_bio(struct bio *bio, sector_t sector,
unsigned short idx, unsigned short bv_count,
- unsigned int len)
+ unsigned int len, struct bio_set *bs)
{
struct bio *clone;
- clone = bio_clone(bio, GFP_NOIO);
+ clone = bio_alloc_bioset(GFP_NOIO, bio->bi_max_vecs, bs);
+ __bio_clone(clone, bio);
+ clone->bi_destructor = dm_bio_destructor;
clone->bi_sector = sector;
clone->bi_idx = idx;
clone->bi_vcnt = idx + bv_count;
@@ -601,7 +650,8 @@ static void __clone_and_map(struct clone_info *ci)
* the remaining io with a single clone.
*/
clone = clone_bio(bio, ci->sector, ci->idx,
- bio->bi_vcnt - ci->idx, ci->sector_count);
+ bio->bi_vcnt - ci->idx, ci->sector_count,
+ ci->md->bs);
__map_bio(ti, clone, tio);
ci->sector_count = 0;
@@ -624,7 +674,8 @@ static void __clone_and_map(struct clone_info *ci)
len += bv_len;
}
- clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len);
+ clone = clone_bio(bio, ci->sector, ci->idx, i - ci->idx, len,
+ ci->md->bs);
__map_bio(ti, clone, tio);
ci->sector += len;
@@ -653,7 +704,8 @@ static void __clone_and_map(struct clone_info *ci)
len = min(remaining, max);
clone = split_bvec(bio, ci->sector, ci->idx,
- bv->bv_offset + offset, len);
+ bv->bv_offset + offset, len,
+ ci->md->bs);
__map_bio(ti, clone, tio);
@@ -903,7 +955,7 @@ static struct mapped_device *alloc_dev(int minor)
md->queue = blk_alloc_queue(GFP_KERNEL);
if (!md->queue)
- goto bad1;
+ goto bad1_free_minor;
md->queue->queuedata = md;
md->queue->backing_dev_info.congested_fn = dm_any_congested;
@@ -921,6 +973,10 @@ static struct mapped_device *alloc_dev(int minor)
if (!md->tio_pool)
goto bad3;
+ md->bs = bioset_create(16, 16, 4);
+ if (!md->bs)
+ goto bad_no_bioset;
+
md->disk = alloc_disk(1);
if (!md->disk)
goto bad4;
@@ -948,11 +1004,14 @@ static struct mapped_device *alloc_dev(int minor)
return md;
bad4:
+ bioset_free(md->bs);
+ bad_no_bioset:
mempool_destroy(md->tio_pool);
bad3:
mempool_destroy(md->io_pool);
bad2:
blk_cleanup_queue(md->queue);
+ bad1_free_minor:
free_minor(minor);
bad1:
module_put(THIS_MODULE);
@@ -971,6 +1030,7 @@ static void free_dev(struct mapped_device *md)
}
mempool_destroy(md->tio_pool);
mempool_destroy(md->io_pool);
+ bioset_free(md->bs);
del_gendisk(md->disk);
free_minor(minor);
@@ -1319,7 +1379,9 @@ int dm_resume(struct mapped_device *md)
if (!map || !dm_table_get_size(map))
goto out;
- dm_table_resume_targets(map);
+ r = dm_table_resume_targets(map);
+ if (r)
+ goto out;
down_write(&md->io_lock);
clear_bit(DMF_BLOCK_IO, &md->flags);
@@ -1337,6 +1399,8 @@ int dm_resume(struct mapped_device *md)
dm_table_unplug_all(map);
+ kobject_uevent(&md->disk->kobj, KOBJ_CHANGE);
+
r = 0;
out:
@@ -1377,6 +1441,7 @@ int dm_suspended(struct mapped_device *md)
static struct block_device_operations dm_blk_dops = {
.open = dm_blk_open,
.release = dm_blk_close,
+ .ioctl = dm_blk_ioctl,
.getgeo = dm_blk_getgeo,
.owner = THIS_MODULE
};
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 3c03c0ecab7..a48ec5e3c1f 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -21,6 +21,11 @@
#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)
+#ifdef CONFIG_DM_DEBUG
+# define DMDEBUG(f, arg...) printk(KERN_DEBUG DM_NAME ": " DM_MSG_PREFIX " DEBUG: " f "\n", ## arg)
+#else
+# define DMDEBUG(f, arg...) do {} while (0)
+#endif
#define DMEMIT(x...) sz += ((sz >= maxlen) ? \
0 : scnprintf(result + sz, maxlen - sz, x))
@@ -52,7 +57,7 @@ void dm_table_set_restrictions(struct dm_table *t, struct request_queue *q);
struct list_head *dm_table_get_devices(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);
+int dm_table_resume_targets(struct dm_table *t);
int dm_table_any_congested(struct dm_table *t, int bdi_bits);
void dm_table_unplug_all(struct dm_table *t);
int dm_table_flush_all(struct dm_table *t);
diff --git a/drivers/md/linear.c b/drivers/md/linear.c
index b99c19c7eb2..c625ddb8833 100644
--- a/drivers/md/linear.c
+++ b/drivers/md/linear.c
@@ -111,6 +111,19 @@ static int linear_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
+static int linear_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ linear_conf_t *conf = mddev_to_conf(mddev);
+ int i, ret = 0;
+
+ for (i = 0; i < mddev->raid_disks && !ret ; i++) {
+ request_queue_t *q = bdev_get_queue(conf->disks[i].rdev->bdev);
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ }
+ return ret;
+}
+
static linear_conf_t *linear_conf(mddev_t *mddev, int raid_disks)
{
linear_conf_t *conf;
@@ -269,6 +282,8 @@ static int linear_run (mddev_t *mddev)
blk_queue_merge_bvec(mddev->queue, linear_mergeable_bvec);
mddev->queue->unplug_fn = linear_unplug;
mddev->queue->issue_flush_fn = linear_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = linear_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
return 0;
}
diff --git a/drivers/md/md.c b/drivers/md/md.c
index 8dbab2ef388..38a0a5741d5 100644
--- a/drivers/md/md.c
+++ b/drivers/md/md.c
@@ -389,8 +389,12 @@ static int super_written(struct bio *bio, unsigned int bytes_done, int error)
if (bio->bi_size)
return 1;
- if (error || !test_bit(BIO_UPTODATE, &bio->bi_flags))
+ if (error || !test_bit(BIO_UPTODATE, &bio->bi_flags)) {
+ printk("md: super_written gets error=%d, uptodate=%d\n",
+ error, test_bit(BIO_UPTODATE, &bio->bi_flags));
+ WARN_ON(test_bit(BIO_UPTODATE, &bio->bi_flags));
md_error(mddev, rdev);
+ }
if (atomic_dec_and_test(&mddev->pending_writes))
wake_up(&mddev->sb_wait);
@@ -1587,7 +1591,7 @@ static void sync_sbs(mddev_t * mddev, int nospares)
}
}
-void md_update_sb(mddev_t * mddev)
+static void md_update_sb(mddev_t * mddev, int force_change)
{
int err;
struct list_head *tmp;
@@ -1598,7 +1602,18 @@ void md_update_sb(mddev_t * mddev)
repeat:
spin_lock_irq(&mddev->write_lock);
- if (mddev->degraded && mddev->sb_dirty == 3)
+ set_bit(MD_CHANGE_PENDING, &mddev->flags);
+ if (test_and_clear_bit(MD_CHANGE_DEVS, &mddev->flags))
+ force_change = 1;
+ if (test_and_clear_bit(MD_CHANGE_CLEAN, &mddev->flags))
+ /* 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 (force_change)
+ nospares = 0;
+ if (mddev->degraded)
/* If the array is degraded, then skipping spares is both
* dangerous and fairly pointless.
* Dangerous because a device that was removed from the array
@@ -1608,20 +1623,14 @@ repeat:
* then a recovery will happen and soon that array won't
* be degraded any more and the spare can go back to sleep then.
*/
- mddev->sb_dirty = 1;
+ nospares = 0;
sync_req = mddev->in_sync;
mddev->utime = get_seconds();
- 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
+ if (nospares
&& (mddev->in_sync && mddev->recovery_cp == MaxSector)
&& (mddev->events & 1))
mddev->events--;
@@ -1652,7 +1661,6 @@ repeat:
MD_BUG();
mddev->events --;
}
- mddev->sb_dirty = 2;
sync_sbs(mddev, nospares);
/*
@@ -1660,7 +1668,7 @@ repeat:
* nonpersistent superblocks
*/
if (!mddev->persistent) {
- mddev->sb_dirty = 0;
+ clear_bit(MD_CHANGE_PENDING, &mddev->flags);
spin_unlock_irq(&mddev->write_lock);
wake_up(&mddev->sb_wait);
return;
@@ -1697,20 +1705,20 @@ repeat:
break;
}
md_super_wait(mddev);
- /* if there was a failure, sb_dirty was set to 1, and we re-write super */
+ /* if there was a failure, MD_CHANGE_DEVS was set, and we re-write super */
spin_lock_irq(&mddev->write_lock);
- if (mddev->in_sync != sync_req|| mddev->sb_dirty == 1) {
+ if (mddev->in_sync != sync_req ||
+ test_bit(MD_CHANGE_DEVS, &mddev->flags)) {
/* have to write it out again */
spin_unlock_irq(&mddev->write_lock);
goto repeat;
}
- mddev->sb_dirty = 0;
+ clear_bit(MD_CHANGE_PENDING, &mddev->flags);
spin_unlock_irq(&mddev->write_lock);
wake_up(&mddev->sb_wait);
}
-EXPORT_SYMBOL_GPL(md_update_sb);
/* words written to sysfs files may, or my not, be \n terminated.
* We want to accept with case. For this we use cmd_match.
@@ -1783,7 +1791,7 @@ state_store(mdk_rdev_t *rdev, const char *buf, size_t len)
else {
mddev_t *mddev = rdev->mddev;
kick_rdev_from_array(rdev);
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
md_new_event(mddev);
err = 0;
}
@@ -2426,7 +2434,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
spin_lock_irq(&mddev->write_lock);
if (atomic_read(&mddev->writes_pending) == 0) {
mddev->in_sync = 1;
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_CLEAN, &mddev->flags);
}
spin_unlock_irq(&mddev->write_lock);
} else {
@@ -2438,7 +2446,7 @@ array_state_store(mddev_t *mddev, const char *buf, size_t len)
case active:
if (mddev->pers) {
restart_array(mddev);
- mddev->sb_dirty = 0;
+ clear_bit(MD_CHANGE_CLEAN, &mddev->flags);
wake_up(&mddev->sb_wait);
err = 0;
} else {
@@ -2520,6 +2528,36 @@ static struct md_sysfs_entry md_new_device =
__ATTR(new_dev, S_IWUSR, null_show, new_dev_store);
static ssize_t
+bitmap_store(mddev_t *mddev, const char *buf, size_t len)
+{
+ char *end;
+ unsigned long chunk, end_chunk;
+
+ if (!mddev->bitmap)
+ goto out;
+ /* buf should be <chunk> <chunk> ... or <chunk>-<chunk> ... (range) */
+ while (*buf) {
+ chunk = end_chunk = simple_strtoul(buf, &end, 0);
+ if (buf == end) break;
+ if (*end == '-') { /* range */
+ buf = end + 1;
+ end_chunk = simple_strtoul(buf, &end, 0);
+ if (buf == end) break;
+ }
+ if (*end && !isspace(*end)) break;
+ bitmap_dirty_bits(mddev->bitmap, chunk, end_chunk);
+ buf = end;
+ while (isspace(*buf)) buf++;
+ }
+ bitmap_unplug(mddev->bitmap); /* flush the bits to disk */
+out:
+ return len;
+}
+
+static struct md_sysfs_entry md_bitmap =
+__ATTR(bitmap_set_bits, S_IWUSR, null_show, bitmap_store);
+
+static ssize_t
size_show(mddev_t *mddev, char *page)
{
return sprintf(page, "%llu\n", (unsigned long long)mddev->size);
@@ -2543,7 +2581,7 @@ size_store(mddev_t *mddev, const char *buf, size_t len)
if (mddev->pers) {
err = update_size(mddev, size);
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
} else {
if (mddev->size == 0 ||
mddev->size > size)
@@ -2839,6 +2877,7 @@ static struct attribute *md_redundancy_attrs[] = {
&md_sync_completed.attr,
&md_suspend_lo.attr,
&md_suspend_hi.attr,
+ &md_bitmap.attr,
NULL,
};
static struct attribute_group md_redundancy_group = {
@@ -3111,8 +3150,8 @@ static int do_md_run(mddev_t * mddev)
set_bit(MD_RECOVERY_NEEDED, &mddev->recovery);
- if (mddev->sb_dirty)
- md_update_sb(mddev);
+ if (mddev->flags)
+ md_update_sb(mddev, 0);
set_capacity(disk, mddev->array_size<<1);
@@ -3275,10 +3314,10 @@ static int do_md_stop(mddev_t * mddev, int mode)
if (mddev->ro)
mddev->ro = 0;
}
- if (!mddev->in_sync || mddev->sb_dirty) {
+ if (!mddev->in_sync || mddev->flags) {
/* mark array as shutdown cleanly */
mddev->in_sync = 1;
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
}
if (mode == 1)
set_disk_ro(disk, 1);
@@ -3374,6 +3413,7 @@ static void autorun_devices(int part)
printk(KERN_INFO "md: autorun ...\n");
while (!list_empty(&pending_raid_disks)) {
+ int unit;
dev_t dev;
LIST_HEAD(candidates);
rdev0 = list_entry(pending_raid_disks.next,
@@ -3393,16 +3433,19 @@ static void autorun_devices(int part)
* mostly sane superblocks. It's time to allocate the
* mddev.
*/
- if (rdev0->preferred_minor < 0 || rdev0->preferred_minor >= MAX_MD_DEVS) {
+ if (part) {
+ dev = MKDEV(mdp_major,
+ rdev0->preferred_minor << MdpMinorShift);
+ unit = MINOR(dev) >> MdpMinorShift;
+ } else {
+ dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
+ unit = MINOR(dev);
+ }
+ if (rdev0->preferred_minor != unit) {
printk(KERN_INFO "md: unit number in %s is bad: %d\n",
bdevname(rdev0->bdev, b), rdev0->preferred_minor);
break;
}
- if (part)
- dev = MKDEV(mdp_major,
- rdev0->preferred_minor << MdpMinorShift);
- else
- dev = MKDEV(MD_MAJOR, rdev0->preferred_minor);
md_probe(dev, NULL, NULL);
mddev = mddev_find(dev);
@@ -3440,67 +3483,6 @@ static void autorun_devices(int part)
printk(KERN_INFO "md: ... autorun DONE.\n");
}
-/*
- * import RAID devices based on one partition
- * if possible, the array gets run as well.
- */
-
-static int autostart_array(dev_t startdev)
-{
- char b[BDEVNAME_SIZE];
- int err = -EINVAL, i;
- mdp_super_t *sb = NULL;
- mdk_rdev_t *start_rdev = NULL, *rdev;
-
- start_rdev = md_import_device(startdev, 0, 0);
- if (IS_ERR(start_rdev))
- return err;
-
-
- /* NOTE: this can only work for 0.90.0 superblocks */
- sb = (mdp_super_t*)page_address(start_rdev->sb_page);
- if (sb->major_version != 0 ||
- sb->minor_version != 90 ) {
- printk(KERN_WARNING "md: can only autostart 0.90.0 arrays\n");
- export_rdev(start_rdev);
- return err;
- }
-
- if (test_bit(Faulty, &start_rdev->flags)) {
- printk(KERN_WARNING
- "md: can not autostart based on faulty %s!\n",
- bdevname(start_rdev->bdev,b));
- export_rdev(start_rdev);
- return err;
- }
- list_add(&start_rdev->same_set, &pending_raid_disks);
-
- for (i = 0; i < MD_SB_DISKS; i++) {
- mdp_disk_t *desc = sb->disks + i;
- dev_t dev = MKDEV(desc->major, desc->minor);
-
- if (!dev)
- continue;
- if (dev == startdev)
- continue;
- if (MAJOR(dev) != desc->major || MINOR(dev) != desc->minor)
- continue;
- rdev = md_import_device(dev, 0, 0);
- if (IS_ERR(rdev))
- continue;
-
- list_add(&rdev->same_set, &pending_raid_disks);
- }
-
- /*
- * possibly return codes
- */
- autorun_devices(0);
- return 0;
-
-}
-
-
static int get_version(void __user * arg)
{
mdu_version_t ver;
@@ -3808,7 +3790,7 @@ static int hot_remove_disk(mddev_t * mddev, dev_t dev)
goto busy;
kick_rdev_from_array(rdev);
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
md_new_event(mddev);
return 0;
@@ -3885,7 +3867,7 @@ static int hot_add_disk(mddev_t * mddev, dev_t dev)
rdev->raid_disk = -1;
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
/*
* Kick recovery, maybe this spare has to be added to the
@@ -4016,7 +3998,8 @@ static int set_array_info(mddev_t * mddev, mdu_array_info_t *info)
mddev->max_disks = MD_SB_DISKS;
- mddev->sb_dirty = 1;
+ mddev->flags = 0;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
mddev->default_bitmap_offset = MD_SB_BYTES >> 9;
mddev->bitmap_offset = 0;
@@ -4185,7 +4168,7 @@ static int update_array_info(mddev_t *mddev, mdu_array_info_t *info)
mddev->bitmap_offset = 0;
}
}
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
return rv;
}
@@ -4259,27 +4242,6 @@ static int md_ioctl(struct inode *inode, struct file *file,
goto abort;
}
-
- if (cmd == START_ARRAY) {
- /* START_ARRAY doesn't need to lock the array as autostart_array
- * does the locking, and it could even be a different array
- */
- static int cnt = 3;
- if (cnt > 0 ) {
- printk(KERN_WARNING
- "md: %s(pid %d) used deprecated START_ARRAY ioctl. "
- "This will not be supported beyond July 2006\n",
- current->comm, current->pid);
- cnt--;
- }
- err = autostart_array(new_decode_dev(arg));
- if (err) {
- printk(KERN_WARNING "md: autostart failed!\n");
- goto abort;
- }
- goto done;
- }
-
err = mddev_lock(mddev);
if (err) {
printk(KERN_INFO
@@ -4687,9 +4649,11 @@ static void status_resync(struct seq_file *seq, mddev_t * mddev)
seq_printf(seq, " %s =%3u.%u%% (%llu/%llu)",
(test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery)?
"reshape" :
- (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
- "resync" : "recovery")),
- per_milli/10, per_milli % 10,
+ (test_bit(MD_RECOVERY_CHECK, &mddev->recovery)?
+ "check" :
+ (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) ?
+ "resync" : "recovery"))),
+ per_milli/10, per_milli % 10,
(unsigned long long) resync,
(unsigned long long) max_blocks);
@@ -5042,12 +5006,12 @@ 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 = 3;
+ set_bit(MD_CHANGE_CLEAN, &mddev->flags);
md_wakeup_thread(mddev->thread);
}
spin_unlock_irq(&mddev->write_lock);
}
- wait_event(mddev->sb_wait, mddev->sb_dirty==0);
+ wait_event(mddev->sb_wait, mddev->flags==0);
}
void md_write_end(mddev_t *mddev)
@@ -5078,6 +5042,7 @@ void md_do_sync(mddev_t *mddev)
int skipped = 0;
struct list_head *rtmp;
mdk_rdev_t *rdev;
+ char *desc;
/* just incase thread restarts... */
if (test_bit(MD_RECOVERY_DONE, &mddev->recovery))
@@ -5085,6 +5050,18 @@ void md_do_sync(mddev_t *mddev)
if (mddev->ro) /* never try to sync a read-only array */
return;
+ if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) {
+ if (test_bit(MD_RECOVERY_CHECK, &mddev->recovery))
+ desc = "data-check";
+ else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery))
+ desc = "requested-resync";
+ else
+ desc = "resync";
+ } else if (test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery))
+ desc = "reshape";
+ else
+ desc = "recovery";
+
/* we overload curr_resync somewhat here.
* 0 == not engaged in resync at all
* 2 == checking that there is no conflict with another sync
@@ -5128,10 +5105,10 @@ void md_do_sync(mddev_t *mddev)
prepare_to_wait(&resync_wait, &wq, TASK_UNINTERRUPTIBLE);
if (!kthread_should_stop() &&
mddev2->curr_resync >= mddev->curr_resync) {
- printk(KERN_INFO "md: delaying resync of %s"
- " until %s has finished resync (they"
+ printk(KERN_INFO "md: delaying %s of %s"
+ " until %s has finished (they"
" share one or more physical units)\n",
- mdname(mddev), mdname(mddev2));
+ desc, mdname(mddev), mdname(mddev2));
mddev_put(mddev2);
schedule();
finish_wait(&resync_wait, &wq);
@@ -5167,12 +5144,12 @@ void md_do_sync(mddev_t *mddev)
j = rdev->recovery_offset;
}
- printk(KERN_INFO "md: syncing RAID array %s\n", mdname(mddev));
- printk(KERN_INFO "md: minimum _guaranteed_ reconstruction speed:"
- " %d KB/sec/disc.\n", speed_min(mddev));
+ printk(KERN_INFO "md: %s of RAID array %s\n", desc, mdname(mddev));
+ printk(KERN_INFO "md: minimum _guaranteed_ speed:"
+ " %d KB/sec/disk.\n", speed_min(mddev));
printk(KERN_INFO "md: using maximum available idle IO bandwidth "
- "(but not more than %d KB/sec) for reconstruction.\n",
- speed_max(mddev));
+ "(but not more than %d KB/sec) for %s.\n",
+ speed_max(mddev), desc);
is_mddev_idle(mddev); /* this also initializes IO event counters */
@@ -5198,8 +5175,8 @@ void md_do_sync(mddev_t *mddev)
if (j>2) {
printk(KERN_INFO
- "md: resuming recovery of %s from checkpoint.\n",
- mdname(mddev));
+ "md: resuming %s of %s from checkpoint.\n",
+ desc, mdname(mddev));
mddev->curr_resync = j;
}
@@ -5282,7 +5259,7 @@ void md_do_sync(mddev_t *mddev)
}
}
}
- printk(KERN_INFO "md: %s: sync done.\n",mdname(mddev));
+ printk(KERN_INFO "md: %s: %s done.\n",mdname(mddev), desc);
/*
* this also signals 'finished resyncing' to md_stop
*/
@@ -5302,8 +5279,8 @@ void md_do_sync(mddev_t *mddev)
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));
+ "md: checkpointing %s of %s.\n",
+ desc, mdname(mddev));
mddev->recovery_cp = mddev->curr_resync;
}
} else
@@ -5317,7 +5294,6 @@ void md_do_sync(mddev_t *mddev)
!test_bit(In_sync, &rdev->flags) &&
rdev->recovery_offset < mddev->curr_resync)
rdev->recovery_offset = mddev->curr_resync;
- mddev->sb_dirty = 1;
}
}
@@ -5374,7 +5350,7 @@ void md_check_recovery(mddev_t *mddev)
}
if ( ! (
- mddev->sb_dirty ||
+ mddev->flags ||
test_bit(MD_RECOVERY_NEEDED, &mddev->recovery) ||
test_bit(MD_RECOVERY_DONE, &mddev->recovery) ||
(mddev->safemode == 1) ||
@@ -5390,14 +5366,14 @@ 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 = 3;
+ set_bit(MD_CHANGE_CLEAN, &mddev->flags);
}
if (mddev->safemode == 1)
mddev->safemode = 0;
spin_unlock_irq(&mddev->write_lock);
- if (mddev->sb_dirty)
- md_update_sb(mddev);
+ if (mddev->flags)
+ md_update_sb(mddev, 0);
if (test_bit(MD_RECOVERY_RUNNING, &mddev->recovery) &&
@@ -5416,7 +5392,7 @@ void md_check_recovery(mddev_t *mddev)
/* activate any spares */
mddev->pers->spare_active(mddev);
}
- md_update_sb(mddev);
+ md_update_sb(mddev, 1);
/* if array is no-longer degraded, then any saved_raid_disk
* information must be scrapped
@@ -5556,22 +5532,15 @@ static void md_geninit(void)
static int __init md_init(void)
{
- printk(KERN_INFO "md: md driver %d.%d.%d MAX_MD_DEVS=%d,"
- " MD_SB_DISKS=%d\n",
- MD_MAJOR_VERSION, MD_MINOR_VERSION,
- MD_PATCHLEVEL_VERSION, MAX_MD_DEVS, MD_SB_DISKS);
- printk(KERN_INFO "md: bitmap version %d.%d\n", BITMAP_MAJOR_HI,
- BITMAP_MINOR);
-
if (register_blkdev(MAJOR_NR, "md"))
return -1;
if ((mdp_major=register_blkdev(0, "mdp"))<=0) {
unregister_blkdev(MAJOR_NR, "md");
return -1;
}
- blk_register_region(MKDEV(MAJOR_NR, 0), MAX_MD_DEVS, THIS_MODULE,
- md_probe, NULL, NULL);
- blk_register_region(MKDEV(mdp_major, 0), MAX_MD_DEVS<<MdpMinorShift, THIS_MODULE,
+ blk_register_region(MKDEV(MAJOR_NR, 0), 1UL<<MINORBITS, THIS_MODULE,
+ md_probe, NULL, NULL);
+ blk_register_region(MKDEV(mdp_major, 0), 1UL<<MINORBITS, THIS_MODULE,
md_probe, NULL, NULL);
register_reboot_notifier(&md_notifier);
@@ -5630,8 +5599,8 @@ static __exit void md_exit(void)
mddev_t *mddev;
struct list_head *tmp;
- blk_unregister_region(MKDEV(MAJOR_NR,0), MAX_MD_DEVS);
- blk_unregister_region(MKDEV(mdp_major,0), MAX_MD_DEVS << MdpMinorShift);
+ blk_unregister_region(MKDEV(MAJOR_NR,0), 1U << MINORBITS);
+ blk_unregister_region(MKDEV(mdp_major,0), 1U << MINORBITS);
unregister_blkdev(MAJOR_NR,"md");
unregister_blkdev(mdp_major, "mdp");
diff --git a/drivers/md/multipath.c b/drivers/md/multipath.c
index 1cc9de44ce8..171ff41b52b 100644
--- a/drivers/md/multipath.c
+++ b/drivers/md/multipath.c
@@ -228,6 +228,28 @@ static int multipath_issue_flush(request_queue_t *q, struct gendisk *disk,
rcu_read_unlock();
return ret;
}
+static int multipath_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ multipath_conf_t *conf = mddev_to_conf(mddev);
+ int i, ret = 0;
+
+ rcu_read_lock();
+ for (i = 0; i < mddev->raid_disks ; i++) {
+ mdk_rdev_t *rdev = rcu_dereference(conf->multipaths[i].rdev);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ request_queue_t *q = bdev_get_queue(rdev->bdev);
+
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ /* Just like multipath_map, we just check the
+ * first available device
+ */
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
/*
* Careful, this can execute in IRQ contexts as well!
@@ -253,7 +275,7 @@ static void multipath_error (mddev_t *mddev, mdk_rdev_t *rdev)
char b[BDEVNAME_SIZE];
clear_bit(In_sync, &rdev->flags);
set_bit(Faulty, &rdev->flags);
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
conf->working_disks--;
printk(KERN_ALERT "multipath: IO failure on %s,"
" disabling IO path. \n Operation continuing"
@@ -470,7 +492,6 @@ static int multipath_run (mddev_t *mddev)
}
conf->raid_disks = mddev->raid_disks;
- mddev->sb_dirty = 1;
conf->mddev = mddev;
spin_lock_init(&conf->device_lock);
INIT_LIST_HEAD(&conf->retry_list);
@@ -510,6 +531,8 @@ static int multipath_run (mddev_t *mddev)
mddev->queue->unplug_fn = multipath_unplug;
mddev->queue->issue_flush_fn = multipath_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = multipath_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
return 0;
diff --git a/drivers/md/raid0.c b/drivers/md/raid0.c
index cb8c6317e4e..dfe32149ad3 100644
--- a/drivers/md/raid0.c
+++ b/drivers/md/raid0.c
@@ -60,6 +60,21 @@ static int raid0_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
+static int raid0_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ raid0_conf_t *conf = mddev_to_conf(mddev);
+ mdk_rdev_t **devlist = conf->strip_zone[0].dev;
+ int i, ret = 0;
+
+ for (i = 0; i < mddev->raid_disks && !ret ; i++) {
+ request_queue_t *q = bdev_get_queue(devlist[i]->bdev);
+
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ }
+ return ret;
+}
+
static int create_strip_zones (mddev_t *mddev)
{
@@ -236,6 +251,8 @@ static int create_strip_zones (mddev_t *mddev)
mddev->queue->unplug_fn = raid0_unplug;
mddev->queue->issue_flush_fn = raid0_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = raid0_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
printk("raid0: done.\n");
return 0;
diff --git a/drivers/md/raid1.c b/drivers/md/raid1.c
index 3b4d69c0562..dc9d2def027 100644
--- a/drivers/md/raid1.c
+++ b/drivers/md/raid1.c
@@ -271,7 +271,7 @@ static int raid1_end_read_request(struct bio *bio, unsigned int bytes_done, int
*/
update_head_pos(mirror, r1_bio);
- if (uptodate || conf->working_disks <= 1) {
+ if (uptodate || (conf->raid_disks - conf->mddev->degraded) <= 1) {
/*
* Set R1BIO_Uptodate in our master bio, so that
* we will return a good error code for to the higher
@@ -601,6 +601,32 @@ static int raid1_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
+static int raid1_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ conf_t *conf = mddev_to_conf(mddev);
+ int i, ret = 0;
+
+ rcu_read_lock();
+ for (i = 0; i < mddev->raid_disks; i++) {
+ mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ request_queue_t *q = bdev_get_queue(rdev->bdev);
+
+ /* Note the '|| 1' - when read_balance prefers
+ * non-congested targets, it can be removed
+ */
+ if ((bits & (1<<BDI_write_congested)) || 1)
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ else
+ ret &= bdi_congested(&q->backing_dev_info, bits);
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
@@ -929,7 +955,7 @@ static void status(struct seq_file *seq, mddev_t *mddev)
int i;
seq_printf(seq, " [%d/%d] [", conf->raid_disks,
- conf->working_disks);
+ conf->raid_disks - mddev->degraded);
rcu_read_lock();
for (i = 0; i < conf->raid_disks; i++) {
mdk_rdev_t *rdev = rcu_dereference(conf->mirrors[i].rdev);
@@ -953,26 +979,27 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* else mark the drive as failed
*/
if (test_bit(In_sync, &rdev->flags)
- && conf->working_disks == 1)
+ && (conf->raid_disks - mddev->degraded) == 1)
/*
* Don't fail the drive, act as though we were just a
* normal single drive
*/
return;
- if (test_bit(In_sync, &rdev->flags)) {
+ if (test_and_clear_bit(In_sync, &rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded++;
- conf->working_disks--;
+ spin_unlock_irqrestore(&conf->device_lock, flags);
/*
* if recovery is running, make sure it aborts.
*/
set_bit(MD_RECOVERY_ERR, &mddev->recovery);
}
- clear_bit(In_sync, &rdev->flags);
set_bit(Faulty, &rdev->flags);
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
printk(KERN_ALERT "raid1: Disk failure on %s, disabling device. \n"
" Operation continuing on %d devices\n",
- bdevname(rdev->bdev,b), conf->working_disks);
+ bdevname(rdev->bdev,b), conf->raid_disks - mddev->degraded);
}
static void print_conf(conf_t *conf)
@@ -984,7 +1011,7 @@ static void print_conf(conf_t *conf)
printk("(!conf)\n");
return;
}
- printk(" --- wd:%d rd:%d\n", conf->working_disks,
+ printk(" --- wd:%d rd:%d\n", conf->raid_disks - conf->mddev->degraded,
conf->raid_disks);
rcu_read_lock();
@@ -1023,10 +1050,11 @@ static int raid1_spare_active(mddev_t *mddev)
mdk_rdev_t *rdev = conf->mirrors[i].rdev;
if (rdev
&& !test_bit(Faulty, &rdev->flags)
- && !test_bit(In_sync, &rdev->flags)) {
- conf->working_disks++;
+ && !test_and_set_bit(In_sync, &rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded--;
- set_bit(In_sync, &rdev->flags);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
}
}
@@ -1368,6 +1396,95 @@ static void sync_request_write(mddev_t *mddev, r1bio_t *r1_bio)
* 3. Performs writes following reads for array syncronising.
*/
+static void fix_read_error(conf_t *conf, int read_disk,
+ sector_t sect, int sectors)
+{
+ mddev_t *mddev = conf->mddev;
+ while(sectors) {
+ int s = sectors;
+ int d = read_disk;
+ int success = 0;
+ int start;
+ mdk_rdev_t *rdev;
+
+ if (s > (PAGE_SIZE>>9))
+ s = PAGE_SIZE >> 9;
+
+ do {
+ /* Note: no rcu protection needed here
+ * as this is synchronous in the raid1d thread
+ * which is the thread that might remove
+ * a device. If raid1d ever becomes multi-threaded....
+ */
+ rdev = conf->mirrors[d].rdev;
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags) &&
+ sync_page_io(rdev->bdev,
+ sect + rdev->data_offset,
+ s<<9,
+ conf->tmppage, READ))
+ success = 1;
+ else {
+ d++;
+ if (d == conf->raid_disks)
+ d = 0;
+ }
+ } while (!success && d != read_disk);
+
+ if (!success) {
+ /* Cannot read from anywhere -- bye bye array */
+ md_error(mddev, conf->mirrors[read_disk].rdev);
+ break;
+ }
+ /* write it back and re-read */
+ start = d;
+ while (d != read_disk) {
+ if (d==0)
+ d = conf->raid_disks;
+ d--;
+ rdev = conf->mirrors[d].rdev;
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags)) {
+ if (sync_page_io(rdev->bdev,
+ sect + rdev->data_offset,
+ s<<9, conf->tmppage, WRITE)
+ == 0)
+ /* Well, this device is dead */
+ md_error(mddev, rdev);
+ }
+ }
+ d = start;
+ while (d != read_disk) {
+ char b[BDEVNAME_SIZE];
+ if (d==0)
+ d = conf->raid_disks;
+ d--;
+ rdev = conf->mirrors[d].rdev;
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags)) {
+ if (sync_page_io(rdev->bdev,
+ sect + rdev->data_offset,
+ s<<9, conf->tmppage, READ)
+ == 0)
+ /* Well, this device is dead */
+ md_error(mddev, rdev);
+ else {
+ atomic_add(s, &rdev->corrected_errors);
+ printk(KERN_INFO
+ "raid1:%s: read error corrected "
+ "(%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)sect +
+ rdev->data_offset,
+ bdevname(rdev->bdev, b));
+ }
+ }
+ }
+ sectors -= s;
+ sect += s;
+ }
+}
+
static void raid1d(mddev_t *mddev)
{
r1bio_t *r1_bio;
@@ -1460,86 +1577,14 @@ static void raid1d(mddev_t *mddev)
* This is all done synchronously while the array is
* frozen
*/
- sector_t sect = r1_bio->sector;
- int sectors = r1_bio->sectors;
- freeze_array(conf);
- if (mddev->ro == 0) while(sectors) {
- int s = sectors;
- int d = r1_bio->read_disk;
- int success = 0;
-
- if (s > (PAGE_SIZE>>9))
- s = PAGE_SIZE >> 9;
-
- do {
- /* Note: no rcu protection needed here
- * as this is synchronous in the raid1d thread
- * which is the thread that might remove
- * a device. If raid1d ever becomes multi-threaded....
- */
- rdev = conf->mirrors[d].rdev;
- if (rdev &&
- test_bit(In_sync, &rdev->flags) &&
- sync_page_io(rdev->bdev,
- sect + rdev->data_offset,
- s<<9,
- conf->tmppage, READ))
- success = 1;
- else {
- d++;
- if (d == conf->raid_disks)
- d = 0;
- }
- } while (!success && d != r1_bio->read_disk);
-
- if (success) {
- /* write it back and re-read */
- int start = d;
- while (d != r1_bio->read_disk) {
- if (d==0)
- d = conf->raid_disks;
- d--;
- rdev = conf->mirrors[d].rdev;
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- if (sync_page_io(rdev->bdev,
- sect + rdev->data_offset,
- s<<9, conf->tmppage, WRITE) == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- }
- }
- d = start;
- while (d != r1_bio->read_disk) {
- if (d==0)
- d = conf->raid_disks;
- d--;
- rdev = conf->mirrors[d].rdev;
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- if (sync_page_io(rdev->bdev,
- sect + rdev->data_offset,
- s<<9, conf->tmppage, READ) == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- else {
- atomic_add(s, &rdev->corrected_errors);
- printk(KERN_INFO "raid1:%s: read error corrected (%d sectors at %llu on %s)\n",
- mdname(mddev), s, (unsigned long long)(sect + rdev->data_offset), bdevname(rdev->bdev, b));
- }
- }
- }
- } else {
- /* Cannot read from anywhere -- bye bye array */
- md_error(mddev, conf->mirrors[r1_bio->read_disk].rdev);
- break;
- }
- sectors -= s;
- sect += s;
+ if (mddev->ro == 0) {
+ freeze_array(conf);
+ fix_read_error(conf, r1_bio->read_disk,
+ r1_bio->sector,
+ r1_bio->sectors);
+ unfreeze_array(conf);
}
- unfreeze_array(conf);
-
bio = r1_bio->bios[r1_bio->read_disk];
if ((disk=read_balance(conf, r1_bio)) == -1) {
printk(KERN_ALERT "raid1: %s: unrecoverable I/O"
@@ -1884,15 +1929,11 @@ static int run(mddev_t *mddev)
blk_queue_max_sectors(mddev->queue, PAGE_SIZE>>9);
disk->head_position = 0;
- if (!test_bit(Faulty, &rdev->flags) && test_bit(In_sync, &rdev->flags))
- conf->working_disks++;
}
conf->raid_disks = mddev->raid_disks;
conf->mddev = mddev;
spin_lock_init(&conf->device_lock);
INIT_LIST_HEAD(&conf->retry_list);
- if (conf->working_disks == 1)
- mddev->recovery_cp = MaxSector;
spin_lock_init(&conf->resync_lock);
init_waitqueue_head(&conf->wait_barrier);
@@ -1900,11 +1941,6 @@ static int run(mddev_t *mddev)
bio_list_init(&conf->pending_bio_list);
bio_list_init(&conf->flushing_bio_list);
- if (!conf->working_disks) {
- printk(KERN_ERR "raid1: no operational mirrors for %s\n",
- mdname(mddev));
- goto out_free_conf;
- }
mddev->degraded = 0;
for (i = 0; i < conf->raid_disks; i++) {
@@ -1917,6 +1953,13 @@ static int run(mddev_t *mddev)
mddev->degraded++;
}
}
+ if (mddev->degraded == conf->raid_disks) {
+ printk(KERN_ERR "raid1: no operational mirrors for %s\n",
+ mdname(mddev));
+ goto out_free_conf;
+ }
+ if (conf->raid_disks - mddev->degraded == 1)
+ mddev->recovery_cp = MaxSector;
/*
* find the first working one and use it as a starting point
@@ -1948,6 +1991,8 @@ static int run(mddev_t *mddev)
mddev->queue->unplug_fn = raid1_unplug;
mddev->queue->issue_flush_fn = raid1_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = raid1_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
return 0;
@@ -2035,7 +2080,7 @@ static int raid1_reshape(mddev_t *mddev)
mirror_info_t *newmirrors;
conf_t *conf = mddev_to_conf(mddev);
int cnt, raid_disks;
-
+ unsigned long flags;
int d, d2;
/* Cannot change chunk_size, layout, or level */
@@ -2094,7 +2139,9 @@ static int raid1_reshape(mddev_t *mddev)
kfree(conf->poolinfo);
conf->poolinfo = newpoolinfo;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded += (raid_disks - conf->raid_disks);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
conf->raid_disks = mddev->raid_disks = raid_disks;
mddev->delta_disks = 0;
diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c
index 016ddb831c9..1250f0eab4a 100644
--- a/drivers/md/raid10.c
+++ b/drivers/md/raid10.c
@@ -648,6 +648,26 @@ static int raid10_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
+static int raid10_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ 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->mirrors[i].rdev);
+ if (rdev && !test_bit(Faulty, &rdev->flags)) {
+ request_queue_t *q = bdev_get_queue(rdev->bdev);
+
+ ret |= bdi_congested(&q->backing_dev_info, bits);
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+
/* Barriers....
* Sometimes we need to suspend IO while we do something else,
* either some resync/recovery, or reconfigure the array.
@@ -921,7 +941,7 @@ static void status(struct seq_file *seq, mddev_t *mddev)
seq_printf(seq, " %d far-copies", conf->far_copies);
}
seq_printf(seq, " [%d/%d] [", conf->raid_disks,
- conf->working_disks);
+ conf->raid_disks - mddev->degraded);
for (i = 0; i < conf->raid_disks; i++)
seq_printf(seq, "%s",
conf->mirrors[i].rdev &&
@@ -941,7 +961,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* else mark the drive as failed
*/
if (test_bit(In_sync, &rdev->flags)
- && conf->working_disks == 1)
+ && conf->raid_disks-mddev->degraded == 1)
/*
* Don't fail the drive, just return an IO error.
* The test should really be more sophisticated than
@@ -950,20 +970,21 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
* really dead" tests...
*/
return;
- if (test_bit(In_sync, &rdev->flags)) {
+ if (test_and_clear_bit(In_sync, &rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded++;
- conf->working_disks--;
+ spin_unlock_irqrestore(&conf->device_lock, flags);
/*
* if recovery is running, make sure it aborts.
*/
set_bit(MD_RECOVERY_ERR, &mddev->recovery);
}
- clear_bit(In_sync, &rdev->flags);
set_bit(Faulty, &rdev->flags);
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
printk(KERN_ALERT "raid10: Disk failure on %s, disabling device. \n"
" Operation continuing on %d devices\n",
- bdevname(rdev->bdev,b), conf->working_disks);
+ bdevname(rdev->bdev,b), conf->raid_disks - mddev->degraded);
}
static void print_conf(conf_t *conf)
@@ -976,7 +997,7 @@ static void print_conf(conf_t *conf)
printk("(!conf)\n");
return;
}
- printk(" --- wd:%d rd:%d\n", conf->working_disks,
+ printk(" --- wd:%d rd:%d\n", conf->raid_disks - conf->mddev->degraded,
conf->raid_disks);
for (i = 0; i < conf->raid_disks; i++) {
@@ -1034,10 +1055,11 @@ static int raid10_spare_active(mddev_t *mddev)
tmp = conf->mirrors + i;
if (tmp->rdev
&& !test_bit(Faulty, &tmp->rdev->flags)
- && !test_bit(In_sync, &tmp->rdev->flags)) {
- conf->working_disks++;
+ && !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded--;
- set_bit(In_sync, &tmp->rdev->flags);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
}
}
@@ -1350,9 +1372,119 @@ static void recovery_request_write(mddev_t *mddev, r10bio_t *r10_bio)
*
* 1. Retries failed read operations on working mirrors.
* 2. Updates the raid superblock when problems encounter.
- * 3. Performs writes following reads for array syncronising.
+ * 3. Performs writes following reads for array synchronising.
*/
+static void fix_read_error(conf_t *conf, mddev_t *mddev, r10bio_t *r10_bio)
+{
+ int sect = 0; /* Offset from r10_bio->sector */
+ int sectors = r10_bio->sectors;
+ mdk_rdev_t*rdev;
+ while(sectors) {
+ int s = sectors;
+ int sl = r10_bio->read_slot;
+ int success = 0;
+ int start;
+
+ if (s > (PAGE_SIZE>>9))
+ s = PAGE_SIZE >> 9;
+
+ rcu_read_lock();
+ do {
+ int d = r10_bio->devs[sl].devnum;
+ rdev = rcu_dereference(conf->mirrors[d].rdev);
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags)) {
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ success = sync_page_io(rdev->bdev,
+ r10_bio->devs[sl].addr +
+ sect + rdev->data_offset,
+ s<<9,
+ conf->tmppage, READ);
+ rdev_dec_pending(rdev, mddev);
+ rcu_read_lock();
+ if (success)
+ break;
+ }
+ sl++;
+ if (sl == conf->copies)
+ sl = 0;
+ } while (!success && sl != r10_bio->read_slot);
+ rcu_read_unlock();
+
+ if (!success) {
+ /* Cannot read from anywhere -- bye bye array */
+ int dn = r10_bio->devs[r10_bio->read_slot].devnum;
+ md_error(mddev, conf->mirrors[dn].rdev);
+ break;
+ }
+
+ start = sl;
+ /* write it back and re-read */
+ rcu_read_lock();
+ while (sl != r10_bio->read_slot) {
+ int d;
+ if (sl==0)
+ sl = conf->copies;
+ sl--;
+ d = r10_bio->devs[sl].devnum;
+ rdev = rcu_dereference(conf->mirrors[d].rdev);
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags)) {
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ atomic_add(s, &rdev->corrected_errors);
+ if (sync_page_io(rdev->bdev,
+ r10_bio->devs[sl].addr +
+ sect + rdev->data_offset,
+ s<<9, conf->tmppage, WRITE)
+ == 0)
+ /* Well, this device is dead */
+ md_error(mddev, rdev);
+ rdev_dec_pending(rdev, mddev);
+ rcu_read_lock();
+ }
+ }
+ sl = start;
+ while (sl != r10_bio->read_slot) {
+ int d;
+ if (sl==0)
+ sl = conf->copies;
+ sl--;
+ d = r10_bio->devs[sl].devnum;
+ rdev = rcu_dereference(conf->mirrors[d].rdev);
+ if (rdev &&
+ test_bit(In_sync, &rdev->flags)) {
+ char b[BDEVNAME_SIZE];
+ atomic_inc(&rdev->nr_pending);
+ rcu_read_unlock();
+ if (sync_page_io(rdev->bdev,
+ r10_bio->devs[sl].addr +
+ sect + rdev->data_offset,
+ s<<9, conf->tmppage, READ) == 0)
+ /* Well, this device is dead */
+ md_error(mddev, rdev);
+ else
+ printk(KERN_INFO
+ "raid10:%s: read error corrected"
+ " (%d sectors at %llu on %s)\n",
+ mdname(mddev), s,
+ (unsigned long long)sect+
+ rdev->data_offset,
+ bdevname(rdev->bdev, b));
+
+ rdev_dec_pending(rdev, mddev);
+ rcu_read_lock();
+ }
+ }
+ rcu_read_unlock();
+
+ sectors -= s;
+ sect += s;
+ }
+}
+
static void raid10d(mddev_t *mddev)
{
r10bio_t *r10_bio;
@@ -1413,105 +1545,12 @@ static void raid10d(mddev_t *mddev)
* This is all done synchronously while the array is
* frozen.
*/
- int sect = 0; /* Offset from r10_bio->sector */
- int sectors = r10_bio->sectors;
- freeze_array(conf);
- if (mddev->ro == 0) while(sectors) {
- int s = sectors;
- int sl = r10_bio->read_slot;
- int success = 0;
-
- if (s > (PAGE_SIZE>>9))
- s = PAGE_SIZE >> 9;
-
- rcu_read_lock();
- do {
- int d = r10_bio->devs[sl].devnum;
- rdev = rcu_dereference(conf->mirrors[d].rdev);
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- success = sync_page_io(rdev->bdev,
- r10_bio->devs[sl].addr +
- sect + rdev->data_offset,
- s<<9,
- conf->tmppage, READ);
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
- if (success)
- break;
- }
- sl++;
- if (sl == conf->copies)
- sl = 0;
- } while (!success && sl != r10_bio->read_slot);
- rcu_read_unlock();
-
- if (success) {
- int start = sl;
- /* write it back and re-read */
- rcu_read_lock();
- while (sl != r10_bio->read_slot) {
- int d;
- if (sl==0)
- sl = conf->copies;
- sl--;
- d = r10_bio->devs[sl].devnum;
- rdev = rcu_dereference(conf->mirrors[d].rdev);
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- atomic_add(s, &rdev->corrected_errors);
- if (sync_page_io(rdev->bdev,
- r10_bio->devs[sl].addr +
- sect + rdev->data_offset,
- s<<9, conf->tmppage, WRITE) == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
- }
- }
- sl = start;
- while (sl != r10_bio->read_slot) {
- int d;
- if (sl==0)
- sl = conf->copies;
- sl--;
- d = r10_bio->devs[sl].devnum;
- rdev = rcu_dereference(conf->mirrors[d].rdev);
- if (rdev &&
- test_bit(In_sync, &rdev->flags)) {
- atomic_inc(&rdev->nr_pending);
- rcu_read_unlock();
- if (sync_page_io(rdev->bdev,
- r10_bio->devs[sl].addr +
- sect + rdev->data_offset,
- s<<9, conf->tmppage, READ) == 0)
- /* Well, this device is dead */
- md_error(mddev, rdev);
- else
- printk(KERN_INFO "raid10:%s: read error corrected (%d sectors at %llu on %s)\n",
- mdname(mddev), s, (unsigned long long)(sect+rdev->data_offset), bdevname(rdev->bdev, b));
-
- rdev_dec_pending(rdev, mddev);
- rcu_read_lock();
- }
- }
- rcu_read_unlock();
- } else {
- /* Cannot read from anywhere -- bye bye array */
- md_error(mddev, conf->mirrors[r10_bio->devs[r10_bio->read_slot].devnum].rdev);
- break;
- }
- sectors -= s;
- sect += s;
+ if (mddev->ro == 0) {
+ freeze_array(conf);
+ fix_read_error(conf, mddev, r10_bio);
+ unfreeze_array(conf);
}
- unfreeze_array(conf);
-
bio = r10_bio->devs[r10_bio->read_slot].bio;
r10_bio->devs[r10_bio->read_slot].bio =
mddev->ro ? IO_BLOCKED : NULL;
@@ -2018,8 +2057,6 @@ static int run(mddev_t *mddev)
mddev->queue->max_sectors = (PAGE_SIZE>>9);
disk->head_position = 0;
- if (!test_bit(Faulty, &rdev->flags) && test_bit(In_sync, &rdev->flags))
- conf->working_disks++;
}
conf->raid_disks = mddev->raid_disks;
conf->mddev = mddev;
@@ -2077,6 +2114,8 @@ static int run(mddev_t *mddev)
mddev->queue->unplug_fn = raid10_unplug;
mddev->queue->issue_flush_fn = raid10_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = raid10_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
/* Calculate max read-ahead size.
* We need to readahead at least twice a whole stripe....
diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c
index 45006600716..37e4ff661b6 100644
--- a/drivers/md/raid5.c
+++ b/drivers/md/raid5.c
@@ -636,7 +636,6 @@ static int raid5_end_write_request (struct bio *bi, unsigned int bytes_done,
struct stripe_head *sh = bi->bi_private;
raid5_conf_t *conf = sh->raid_conf;
int disks = sh->disks, i;
- unsigned long flags;
int uptodate = test_bit(BIO_UPTODATE, &bi->bi_flags);
if (bi->bi_size)
@@ -654,7 +653,6 @@ static int raid5_end_write_request (struct bio *bi, unsigned int bytes_done,
return 0;
}
- spin_lock_irqsave(&conf->device_lock, flags);
if (!uptodate)
md_error(conf->mddev, conf->disks[i].rdev);
@@ -662,8 +660,7 @@ static int raid5_end_write_request (struct bio *bi, unsigned int bytes_done,
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);
+ release_stripe(sh);
return 0;
}
@@ -696,12 +693,12 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
PRINTK("raid5: error called\n");
if (!test_bit(Faulty, &rdev->flags)) {
- mddev->sb_dirty = 1;
- if (test_bit(In_sync, &rdev->flags)) {
- conf->working_disks--;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
+ if (test_and_clear_bit(In_sync, &rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded++;
- conf->failed_disks++;
- clear_bit(In_sync, &rdev->flags);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
/*
* if recovery was running, make sure it aborts.
*/
@@ -711,7 +708,7 @@ static void error(mddev_t *mddev, mdk_rdev_t *rdev)
printk (KERN_ALERT
"raid5: Disk failure on %s, disabling device."
" Operation continuing on %d devices\n",
- bdevname(rdev->bdev,b), conf->working_disks);
+ bdevname(rdev->bdev,b), conf->raid_disks - mddev->degraded);
}
}
@@ -1353,10 +1350,9 @@ static int page_is_zero(struct page *p)
static int stripe_to_pdidx(sector_t stripe, raid5_conf_t *conf, int disks)
{
int sectors_per_chunk = conf->chunk_size >> 9;
- sector_t x = stripe;
int pd_idx, dd_idx;
- int chunk_offset = sector_div(x, sectors_per_chunk);
- stripe = x;
+ int chunk_offset = sector_div(stripe, sectors_per_chunk);
+
raid5_compute_sector(stripe*(disks-1)*sectors_per_chunk
+ chunk_offset, disks, disks-1, &dd_idx, &pd_idx, conf);
return pd_idx;
@@ -2597,6 +2593,24 @@ static int raid5_issue_flush(request_queue_t *q, struct gendisk *disk,
return ret;
}
+static int raid5_congested(void *data, int bits)
+{
+ mddev_t *mddev = data;
+ raid5_conf_t *conf = mddev_to_conf(mddev);
+
+ /* No difference between reads and writes. Just check
+ * how busy the stripe_cache is
+ */
+ if (conf->inactive_blocked)
+ return 1;
+ if (conf->quiesce)
+ return 1;
+ if (list_empty_careful(&conf->inactive_list))
+ return 1;
+
+ return 0;
+}
+
static int make_request(request_queue_t *q, struct bio * bi)
{
mddev_t *mddev = q->queuedata;
@@ -2781,9 +2795,9 @@ static sector_t reshape_request(mddev_t *mddev, sector_t sector_nr, int *skipped
wait_event(conf->wait_for_overlap,
atomic_read(&conf->reshape_stripes)==0);
mddev->reshape_position = conf->expand_progress;
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
md_wakeup_thread(mddev->thread);
- wait_event(mddev->sb_wait, mddev->sb_dirty == 0 ||
+ wait_event(mddev->sb_wait, mddev->flags == 0 ||
kthread_should_stop());
spin_lock_irq(&conf->device_lock);
conf->expand_lo = mddev->reshape_position;
@@ -3074,6 +3088,7 @@ static int run(mddev_t *mddev)
mdk_rdev_t *rdev;
struct disk_info *disk;
struct list_head *tmp;
+ int working_disks = 0;
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",
@@ -3176,14 +3191,14 @@ static int run(mddev_t *mddev)
printk(KERN_INFO "raid5: device %s operational as raid"
" disk %d\n", bdevname(rdev->bdev,b),
raid_disk);
- conf->working_disks++;
+ working_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;
+ mddev->degraded = conf->raid_disks - working_disks;
conf->mddev = mddev;
conf->chunk_size = mddev->chunk_size;
conf->level = mddev->level;
@@ -3218,7 +3233,7 @@ static int run(mddev_t *mddev)
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);
+ mdname(mddev), mddev->degraded, conf->raid_disks);
goto abort;
}
@@ -3299,6 +3314,9 @@ static int run(mddev_t *mddev)
mddev->queue->unplug_fn = raid5_unplug_device;
mddev->queue->issue_flush_fn = raid5_issue_flush;
+ mddev->queue->backing_dev_info.congested_fn = raid5_congested;
+ mddev->queue->backing_dev_info.congested_data = mddev;
+
mddev->array_size = mddev->size * (conf->previous_raid_disks -
conf->max_degraded);
@@ -3375,7 +3393,7 @@ static void status (struct seq_file *seq, mddev_t *mddev)
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);
+ seq_printf (seq, " [%d/%d] [", conf->raid_disks, conf->raid_disks - mddev->degraded);
for (i = 0; i < conf->raid_disks; i++)
seq_printf (seq, "%s",
conf->disks[i].rdev &&
@@ -3397,8 +3415,8 @@ static void print_raid5_conf (raid5_conf_t *conf)
printk("(conf==NULL)\n");
return;
}
- printk(" --- rd:%d wd:%d fd:%d\n", conf->raid_disks,
- conf->working_disks, conf->failed_disks);
+ printk(" --- rd:%d wd:%d\n", conf->raid_disks,
+ conf->raid_disks - conf->mddev->degraded);
for (i = 0; i < conf->raid_disks; i++) {
char b[BDEVNAME_SIZE];
@@ -3420,11 +3438,11 @@ static int raid5_spare_active(mddev_t *mddev)
tmp = conf->disks + i;
if (tmp->rdev
&& !test_bit(Faulty, &tmp->rdev->flags)
- && !test_bit(In_sync, &tmp->rdev->flags)) {
+ && !test_and_set_bit(In_sync, &tmp->rdev->flags)) {
+ unsigned long flags;
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded--;
- conf->failed_disks--;
- conf->working_disks++;
- set_bit(In_sync, &tmp->rdev->flags);
+ spin_unlock_irqrestore(&conf->device_lock, flags);
}
}
print_raid5_conf(conf);
@@ -3560,6 +3578,7 @@ static int raid5_start_reshape(mddev_t *mddev)
struct list_head *rtmp;
int spares = 0;
int added_devices = 0;
+ unsigned long flags;
if (mddev->degraded ||
test_bit(MD_RECOVERY_RUNNING, &mddev->recovery))
@@ -3593,7 +3612,6 @@ static int raid5_start_reshape(mddev_t *mddev)
if (raid5_add_disk(mddev, rdev)) {
char nm[20];
set_bit(In_sync, &rdev->flags);
- conf->working_disks++;
added_devices++;
rdev->recovery_offset = 0;
sprintf(nm, "rd%d", rdev->raid_disk);
@@ -3602,10 +3620,12 @@ static int raid5_start_reshape(mddev_t *mddev)
break;
}
+ spin_lock_irqsave(&conf->device_lock, flags);
mddev->degraded = (conf->raid_disks - conf->previous_raid_disks) - added_devices;
+ spin_unlock_irqrestore(&conf->device_lock, flags);
mddev->raid_disks = conf->raid_disks;
mddev->reshape_position = 0;
- mddev->sb_dirty = 1;
+ set_bit(MD_CHANGE_DEVS, &mddev->flags);
clear_bit(MD_RECOVERY_SYNC, &mddev->recovery);
clear_bit(MD_RECOVERY_CHECK, &mddev->recovery);
diff --git a/drivers/media/common/Kconfig b/drivers/media/common/Kconfig
index 1a04db4552d..f33e5d97341 100644
--- a/drivers/media/common/Kconfig
+++ b/drivers/media/common/Kconfig
@@ -4,7 +4,6 @@ config VIDEO_SAA7146
config VIDEO_SAA7146_VV
tristate
- select VIDEO_V4L2
select VIDEO_BUF
select VIDEO_VIDEOBUF
select VIDEO_SAA7146
diff --git a/drivers/media/common/ir-keymaps.c b/drivers/media/common/ir-keymaps.c
index ca98d947894..db753443587 100644
--- a/drivers/media/common/ir-keymaps.c
+++ b/drivers/media/common/ir-keymaps.c
@@ -32,6 +32,37 @@ IR_KEYTAB_TYPE ir_codes_empty[IR_KEYTAB_SIZE] = {
EXPORT_SYMBOL_GPL(ir_codes_empty);
+/* Michal Majchrowicz <mmajchrowicz@gmail.com> */
+IR_KEYTAB_TYPE ir_codes_proteus_2309[IR_KEYTAB_SIZE] = {
+ /* numeric */
+ [ 0x00 ] = KEY_0,
+ [ 0x01 ] = KEY_1,
+ [ 0x02 ] = KEY_2,
+ [ 0x03 ] = KEY_3,
+ [ 0x04 ] = KEY_4,
+ [ 0x05 ] = KEY_5,
+ [ 0x06 ] = KEY_6,
+ [ 0x07 ] = KEY_7,
+ [ 0x08 ] = KEY_8,
+ [ 0x09 ] = KEY_9,
+
+ [ 0x5c ] = KEY_POWER, /* power */
+ [ 0x20 ] = KEY_F, /* full screen */
+ [ 0x0f ] = KEY_BACKSPACE, /* recall */
+ [ 0x1b ] = KEY_ENTER, /* mute */
+ [ 0x41 ] = KEY_RECORD, /* record */
+ [ 0x43 ] = KEY_STOP, /* stop */
+ [ 0x16 ] = KEY_S,
+ [ 0x1a ] = KEY_Q, /* off */
+ [ 0x2e ] = KEY_RED,
+ [ 0x1f ] = KEY_DOWN, /* channel - */
+ [ 0x1c ] = KEY_UP, /* channel + */
+ [ 0x10 ] = KEY_LEFT, /* volume - */
+ [ 0x1e ] = KEY_RIGHT, /* volume + */
+ [ 0x14 ] = KEY_F1,
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_proteus_2309);
/* Matt Jesson <dvb@jesson.eclipse.co.uk */
IR_KEYTAB_TYPE ir_codes_avermedia_dvbt[IR_KEYTAB_SIZE] = {
[ 0x28 ] = KEY_0, //'0' / 'enter'
@@ -1473,3 +1504,51 @@ IR_KEYTAB_TYPE ir_codes_npgtech[IR_KEYTAB_SIZE] = {
};
EXPORT_SYMBOL_GPL(ir_codes_npgtech);
+
+/* Norwood Micro (non-Pro) TV Tuner
+ By Peter Naulls <peter@chocky.org>
+ Key comments are the functions given in the manual */
+IR_KEYTAB_TYPE ir_codes_norwood[IR_KEYTAB_SIZE] = {
+ /* Keys 0 to 9 */
+ [ 0x20 ] = KEY_0,
+ [ 0x21 ] = KEY_1,
+ [ 0x22 ] = KEY_2,
+ [ 0x23 ] = KEY_3,
+ [ 0x24 ] = KEY_4,
+ [ 0x25 ] = KEY_5,
+ [ 0x26 ] = KEY_6,
+ [ 0x27 ] = KEY_7,
+ [ 0x28 ] = KEY_8,
+ [ 0x29 ] = KEY_9,
+
+ [ 0x78 ] = KEY_TUNER, /* Video Source */
+ [ 0x2c ] = KEY_EXIT, /* Open/Close software */
+ [ 0x2a ] = KEY_SELECT, /* 2 Digit Select */
+ [ 0x69 ] = KEY_AGAIN, /* Recall */
+
+ [ 0x32 ] = KEY_BRIGHTNESSUP, /* Brightness increase */
+ [ 0x33 ] = KEY_BRIGHTNESSDOWN, /* Brightness decrease */
+ [ 0x6b ] = KEY_KPPLUS, /* (not named >>>>>) */
+ [ 0x6c ] = KEY_KPMINUS, /* (not named <<<<<) */
+
+ [ 0x2d ] = KEY_MUTE, /* Mute */
+ [ 0x30 ] = KEY_VOLUMEUP, /* Volume up */
+ [ 0x31 ] = KEY_VOLUMEDOWN, /* Volume down */
+ [ 0x60 ] = KEY_CHANNELUP, /* Channel up */
+ [ 0x61 ] = KEY_CHANNELDOWN, /* Channel down */
+
+ [ 0x3f ] = KEY_RECORD, /* Record */
+ [ 0x37 ] = KEY_PLAY, /* Play */
+ [ 0x36 ] = KEY_PAUSE, /* Pause */
+ [ 0x2b ] = KEY_STOP, /* Stop */
+ [ 0x67 ] = KEY_FASTFORWARD, /* Foward */
+ [ 0x66 ] = KEY_REWIND, /* Rewind */
+ [ 0x3e ] = KEY_SEARCH, /* Auto Scan */
+ [ 0x2e ] = KEY_CAMERA, /* Capture Video */
+ [ 0x6d ] = KEY_MENU, /* Show/Hide Control */
+ [ 0x2f ] = KEY_ZOOM, /* Full Screen */
+ [ 0x34 ] = KEY_RADIO, /* FM */
+ [ 0x65 ] = KEY_POWER, /* Computer power */
+};
+
+EXPORT_SYMBOL_GPL(ir_codes_norwood);
diff --git a/drivers/media/common/saa7146_fops.c b/drivers/media/common/saa7146_fops.c
index 0027acc5b8e..d867a6a9e43 100644
--- a/drivers/media/common/saa7146_fops.c
+++ b/drivers/media/common/saa7146_fops.c
@@ -455,7 +455,6 @@ static void vv_callback(struct saa7146_dev *dev, unsigned long status)
static struct video_device device_template =
{
- .hardware = VID_HARDWARE_SAA7146,
.fops = &video_fops,
.minor = -1,
};
diff --git a/drivers/media/dvb/b2c2/Kconfig b/drivers/media/dvb/b2c2/Kconfig
index 49a06fc54c5..a0dcd59da76 100644
--- a/drivers/media/dvb/b2c2/Kconfig
+++ b/drivers/media/dvb/b2c2/Kconfig
@@ -2,13 +2,13 @@ config DVB_B2C2_FLEXCOP
tristate "Technisat/B2C2 FlexCopII(b) and FlexCopIII adapters"
depends on DVB_CORE && I2C
select DVB_PLL
- select DVB_STV0299
- select DVB_MT352
- select DVB_MT312
- select DVB_NXT200X
- select DVB_STV0297
- select DVB_BCM3510
- select DVB_LGDT330X
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_MT312 if !DVB_FE_CUSTOMISE
+ select DVB_NXT200X if !DVB_FE_CUSTOMISE
+ select DVB_STV0297 if !DVB_FE_CUSTOMISE
+ select DVB_BCM3510 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
help
Support for the digital TV receiver chip made by B2C2 Inc. included in
Technisats PCI cards and USB boxes.
diff --git a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
index 3be87c72e37..b8ba8786345 100644
--- a/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
+++ b/drivers/media/dvb/b2c2/flexcop-fe-tuner.c
@@ -505,7 +505,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
struct dvb_frontend_ops *ops;
/* try the sky v2.6 (stv0299/Samsung tbmu24112(sl1935)) */
- if ((fc->fe = stv0299_attach(&samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(stv0299_attach, &samsung_tbmu24112_config, &fc->i2c_adap)) != NULL) {
ops = &fc->fe->ops;
ops->tuner_ops.set_params = samsung_tbmu24112_tuner_set_params;
@@ -519,36 +519,36 @@ int flexcop_frontend_init(struct flexcop_device *fc)
info("found the stv0299 at i2c address: 0x%02x",samsung_tbmu24112_config.demod_address);
} else
/* try the air dvb-t (mt352/Samsung tdtc9251dh0(??)) */
- if ((fc->fe = mt352_attach(&samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
+ if ((fc->fe = dvb_attach(mt352_attach, &samsung_tdtc9251dh0_config, &fc->i2c_adap)) != NULL ) {
fc->dev_type = FC_AIR_DVB;
fc->fe->ops.tuner_ops.calc_regs = samsung_tdtc9251dh0_calc_regs;
info("found the mt352 at i2c address: 0x%02x",samsung_tdtc9251dh0_config.demod_address);
} else
/* try the air atsc 2nd generation (nxt2002) */
- if ((fc->fe = nxt200x_attach(&samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(nxt200x_attach, &samsung_tbmv_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC2;
- dvb_pll_attach(fc->fe, 0x61, &fc->i2c_adap, &dvb_pll_samsung_tbmv);
+ dvb_attach(dvb_pll_attach, fc->fe, 0x61, NULL, &dvb_pll_samsung_tbmv);
info("found the nxt2002 at i2c address: 0x%02x",samsung_tbmv_config.demod_address);
} else
/* try the air atsc 3nd generation (lgdt3303) */
- if ((fc->fe = lgdt330x_attach(&air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(lgdt330x_attach, &air2pc_atsc_hd5000_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC3;
fc->fe->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
info("found the lgdt3303 at i2c address: 0x%02x",air2pc_atsc_hd5000_config.demod_address);
} else
/* try the air atsc 1nd generation (bcm3510)/panasonic ct10s */
- if ((fc->fe = bcm3510_attach(&air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(bcm3510_attach, &air2pc_atsc_first_gen_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_AIR_ATSC1;
info("found the bcm3510 at i2c address: 0x%02x",air2pc_atsc_first_gen_config.demod_address);
} else
/* try the cable dvb (stv0297) */
- if ((fc->fe = stv0297_attach(&alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(stv0297_attach, &alps_tdee4_stv0297_config, &fc->i2c_adap)) != NULL) {
fc->dev_type = FC_CABLE;
fc->fe->ops.tuner_ops.set_params = alps_tdee4_stv0297_tuner_set_params;
info("found the stv0297 at i2c address: 0x%02x",alps_tdee4_stv0297_config.demod_address);
} else
/* try the sky v2.3 (vp310/Samsung tbdu18132(tsa5059)) */
- if ((fc->fe = vp310_mt312_attach(&skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
+ if ((fc->fe = dvb_attach(vp310_mt312_attach, &skystar23_samsung_tbdu18132_config, &fc->i2c_adap)) != NULL) {
ops = &fc->fe->ops;
ops->tuner_ops.set_params = skystar23_samsung_tbdu18132_tuner_set_params;
@@ -571,9 +571,7 @@ int flexcop_frontend_init(struct flexcop_device *fc)
} else {
if (dvb_register_frontend(&fc->dvb_adapter, fc->fe)) {
err("frontend registration failed!");
- ops = &fc->fe->ops;
- if (ops->release != NULL)
- ops->release(fc->fe);
+ dvb_frontend_detach(fc->fe);
fc->fe = NULL;
return -EINVAL;
}
@@ -584,8 +582,10 @@ int flexcop_frontend_init(struct flexcop_device *fc)
void flexcop_frontend_exit(struct flexcop_device *fc)
{
- if (fc->init_state & FC_STATE_FE_INIT)
+ if (fc->init_state & FC_STATE_FE_INIT) {
dvb_unregister_frontend(fc->fe);
+ dvb_frontend_detach(fc->fe);
+ }
fc->init_state &= ~FC_STATE_FE_INIT;
}
diff --git a/drivers/media/dvb/bt8xx/Kconfig b/drivers/media/dvb/bt8xx/Kconfig
index 7d0ee1ab290..ae2ff5dc238 100644
--- a/drivers/media/dvb/bt8xx/Kconfig
+++ b/drivers/media/dvb/bt8xx/Kconfig
@@ -2,13 +2,13 @@ config DVB_BT8XX
tristate "BT8xx based PCI cards"
depends on DVB_CORE && PCI && I2C && VIDEO_BT848
select DVB_PLL
- select DVB_MT352
- select DVB_SP887X
- select DVB_NXT6000
- select DVB_CX24110
- select DVB_OR51211
- select DVB_LGDT330X
- select DVB_ZL10353
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_SP887X if !DVB_FE_CUSTOMISE
+ select DVB_NXT6000 if !DVB_FE_CUSTOMISE
+ select DVB_CX24110 if !DVB_FE_CUSTOMISE
+ select DVB_OR51211 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
Support for PCI cards based on the Bt8xx PCI bridge. Examples are
diff --git a/drivers/media/dvb/bt8xx/dst.c b/drivers/media/dvb/bt8xx/dst.c
index 06ac899a9a2..9f72b7000c0 100644
--- a/drivers/media/dvb/bt8xx/dst.c
+++ b/drivers/media/dvb/bt8xx/dst.c
@@ -1715,6 +1715,15 @@ static int dst_get_frontend(struct dvb_frontend *fe, struct dvb_frontend_paramet
static void dst_release(struct dvb_frontend *fe)
{
struct dst_state *state = fe->demodulator_priv;
+ if (state->dst_ca) {
+ dvb_unregister_device(state->dst_ca);
+#ifdef CONFIG_DVB_CORE_ATTACH
+ symbol_put(dst_ca_attach);
+#endif
+ }
+#ifdef CONFIG_DVB_CORE_ATTACH
+ symbol_put(dst_attach);
+#endif
kfree(state);
}
diff --git a/drivers/media/dvb/bt8xx/dst_ca.c b/drivers/media/dvb/bt8xx/dst_ca.c
index fa923b9b346..240ad084fa7 100644
--- a/drivers/media/dvb/bt8xx/dst_ca.c
+++ b/drivers/media/dvb/bt8xx/dst_ca.c
@@ -699,12 +699,17 @@ static struct dvb_device dvbdev_ca = {
.fops = &dst_ca_fops
};
-int dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
+struct dvb_device *dst_ca_attach(struct dst_state *dst, struct dvb_adapter *dvb_adapter)
{
struct dvb_device *dvbdev;
+
dprintk(verbose, DST_CA_ERROR, 1, "registering DST-CA device");
- dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA);
- return 0;
+ if (dvb_register_device(dvb_adapter, &dvbdev, &dvbdev_ca, dst, DVB_DEVICE_CA) == 0) {
+ dst->dst_ca = dvbdev;
+ return dst->dst_ca;
+ }
+
+ return NULL;
}
EXPORT_SYMBOL(dst_ca_attach);
diff --git a/drivers/media/dvb/bt8xx/dst_common.h b/drivers/media/dvb/bt8xx/dst_common.h
index 0677b047b3a..3bf084f2e52 100644
--- a/drivers/media/dvb/bt8xx/dst_common.h
+++ b/drivers/media/dvb/bt8xx/dst_common.h
@@ -140,6 +140,7 @@ struct dst_state {
char *tuner_name;
struct mutex dst_mutex;
u8 fw_name[8];
+ struct dvb_device *dst_ca;
};
struct tuner_types {
@@ -178,7 +179,7 @@ int write_dst(struct dst_state *state, u8 * data, u8 len);
int read_dst(struct dst_state *state, u8 * ret, u8 len);
u8 dst_check_sum(u8 * buf, u32 len);
struct dst_state* dst_attach(struct dst_state* state, struct dvb_adapter *dvb_adapter);
-int dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
+struct dvb_device *dst_ca_attach(struct dst_state *state, struct dvb_adapter *dvb_adapter);
int dst_gpio_outb(struct dst_state* state, u32 mask, u32 enbb, u32 outhigh, int delay);
int dst_command(struct dst_state* state, u8 * data, u8 len);
diff --git a/drivers/media/dvb/bt8xx/dvb-bt8xx.c b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
index b715b972d2f..fb6c4cc8477 100644
--- a/drivers/media/dvb/bt8xx/dvb-bt8xx.c
+++ b/drivers/media/dvb/bt8xx/dvb-bt8xx.c
@@ -67,7 +67,7 @@ static void dvb_bt8xx_task(unsigned long data)
static int dvb_bt8xx_start_feed(struct dvb_demux_feed *dvbdmxfeed)
{
- struct dvb_demux *dvbdmx = dvbdmxfeed->demux;
+ struct dvb_demux*dvbdmx = dvbdmxfeed->demux;
struct dvb_bt8xx_card *card = dvbdmx->priv;
int rc;
@@ -595,15 +595,14 @@ static void lgdt330x_reset(struct dvb_bt8xx_card *bt)
static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
{
- int ret;
struct dst_state* state = NULL;
switch(type) {
case BTTV_BOARD_DVICO_DVBT_LITE:
- card->fe = mt352_attach(&thomson_dtt7579_config, card->i2c_adapter);
+ card->fe = dvb_attach(mt352_attach, &thomson_dtt7579_config, card->i2c_adapter);
if (card->fe == NULL)
- card->fe = zl10353_attach(&thomson_dtt7579_zl10353_config,
+ card->fe = dvb_attach(zl10353_attach, &thomson_dtt7579_zl10353_config,
card->i2c_adapter);
if (card->fe != NULL) {
@@ -615,7 +614,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
case BTTV_BOARD_DVICO_FUSIONHDTV_5_LITE:
lgdt330x_reset(card);
- card->fe = lgdt330x_attach(&tdvs_tua6034_config, card->i2c_adapter);
+ card->fe = dvb_attach(lgdt330x_attach, &tdvs_tua6034_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.set_params = tdvs_tua6034_tuner_set_params;
dprintk ("dvb_bt8xx: lgdt330x detected\n");
@@ -630,7 +629,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
/* Old Nebula (marked (c)2003 on high profile pci card) has nxt6000 demod */
digitv_alps_tded4_reset(card);
- card->fe = nxt6000_attach(&vp3021_alps_tded4_config, card->i2c_adapter);
+ card->fe = dvb_attach(nxt6000_attach, &vp3021_alps_tded4_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.set_params = vp3021_alps_tded4_tuner_set_params;
dprintk ("dvb_bt8xx: an nxt6000 was detected on your digitv card\n");
@@ -639,7 +638,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
/* New Nebula (marked (c)2005 on low profile pci card) has mt352 demod */
digitv_alps_tded4_reset(card);
- card->fe = mt352_attach(&digitv_alps_tded4_config, card->i2c_adapter);
+ card->fe = dvb_attach(mt352_attach, &digitv_alps_tded4_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.calc_regs = digitv_alps_tded4_tuner_calc_regs;
@@ -648,14 +647,14 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
break;
case BTTV_BOARD_AVDVBT_761:
- card->fe = sp887x_attach(&microtune_mt7202dtf_config, card->i2c_adapter);
+ card->fe = dvb_attach(sp887x_attach, &microtune_mt7202dtf_config, card->i2c_adapter);
if (card->fe) {
card->fe->ops.tuner_ops.set_params = microtune_mt7202dtf_tuner_set_params;
}
break;
case BTTV_BOARD_AVDVBT_771:
- card->fe = mt352_attach(&advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
+ card->fe = dvb_attach(mt352_attach, &advbt771_samsung_tdtc9251dh0_config, card->i2c_adapter);
if (card->fe != NULL) {
card->fe->ops.tuner_ops.calc_regs = advbt771_samsung_tdtc9251dh0_tuner_calc_regs;
card->fe->ops.info.frequency_min = 174000000;
@@ -670,22 +669,21 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
state->config = &dst_config;
state->i2c = card->i2c_adapter;
state->bt = card->bt;
-
+ state->dst_ca = NULL;
/* DST is not a frontend, attaching the ASIC */
- if ((dst_attach(state, &card->dvb_adapter)) == NULL) {
+ if (dvb_attach(dst_attach, state, &card->dvb_adapter) == NULL) {
printk("%s: Could not find a Twinhan DST.\n", __FUNCTION__);
break;
}
- card->fe = &state->frontend;
-
/* Attach other DST peripherals if any */
/* Conditional Access device */
+ card->fe = &state->frontend;
if (state->dst_hw_cap & DST_TYPE_HAS_CA)
- ret = dst_ca_attach(state, &card->dvb_adapter);
+ dvb_attach(dst_ca_attach, state, &card->dvb_adapter);
break;
case BTTV_BOARD_PINNACLESAT:
- card->fe = cx24110_attach(&pctvsat_config, card->i2c_adapter);
+ card->fe = dvb_attach(cx24110_attach, &pctvsat_config, card->i2c_adapter);
if (card->fe) {
card->fe->ops.tuner_ops.init = pinnsat_tuner_init;
card->fe->ops.tuner_ops.sleep = pinnsat_tuner_sleep;
@@ -694,7 +692,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
break;
case BTTV_BOARD_PC_HDTV:
- card->fe = or51211_attach(&or51211_config, card->i2c_adapter);
+ card->fe = dvb_attach(or51211_attach, &or51211_config, card->i2c_adapter);
break;
}
@@ -707,8 +705,7 @@ static void frontend_init(struct dvb_bt8xx_card *card, u32 type)
else
if (dvb_register_frontend(&card->dvb_adapter, card->fe)) {
printk("dvb-bt8xx: Frontend registration failed!\n");
- if (card->fe->ops.release)
- card->fe->ops.release(card->fe);
+ dvb_frontend_detach(card->fe);
card->fe = NULL;
}
}
@@ -925,8 +922,10 @@ static void dvb_bt8xx_remove(struct bttv_sub_device *sub)
card->demux.dmx.remove_frontend(&card->demux.dmx, &card->fe_hw);
dvb_dmxdev_release(&card->dmxdev);
dvb_dmx_release(&card->demux);
- if (card->fe)
+ if (card->fe) {
dvb_unregister_frontend(card->fe);
+ dvb_frontend_detach(card->fe);
+ }
dvb_unregister_adapter(&card->dvb_adapter);
kfree(card);
diff --git a/drivers/media/dvb/cinergyT2/cinergyT2.c b/drivers/media/dvb/cinergyT2/cinergyT2.c
index 001c71b6be6..410fa6d620f 100644
--- a/drivers/media/dvb/cinergyT2/cinergyT2.c
+++ b/drivers/media/dvb/cinergyT2/cinergyT2.c
@@ -981,7 +981,7 @@ static int cinergyt2_suspend (struct usb_interface *intf, pm_message_t state)
if (cinergyt2->disconnect_pending || mutex_lock_interruptible(&cinergyt2->sem))
return -ERESTARTSYS;
- if (state.event > PM_EVENT_ON) {
+ if (1) {
struct cinergyt2 *cinergyt2 = usb_get_intfdata (intf);
cinergyt2_suspend_rc(cinergyt2);
diff --git a/drivers/media/dvb/dvb-core/Kconfig b/drivers/media/dvb/dvb-core/Kconfig
index 12ee912a570..e46eae3b9be 100644
--- a/drivers/media/dvb/dvb-core/Kconfig
+++ b/drivers/media/dvb/dvb-core/Kconfig
@@ -9,3 +9,16 @@ config DVB_CORE
in-kernel drivers will select this automatically if needed.
If unsure say N.
+config DVB_CORE_ATTACH
+ bool "Load and attach frontend modules as needed"
+ depends on DVB_CORE
+ depends on MODULES
+ help
+ Remove the static dependency of DVB card drivers on all
+ frontend modules for all possible card variants. Instead,
+ allow the card drivers to only load the frontend modules
+ they require. This saves several KBytes of memory.
+
+ Note: You will need moudule-init-tools v3.2 or later for this feature.
+
+ If unsure say Y.
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.c b/drivers/media/dvb/dvb-core/dvb_frontend.c
index 57b34cda99f..3dd5dbafb33 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.c
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.c
@@ -1105,18 +1105,42 @@ int dvb_unregister_frontend(struct dvb_frontend* fe)
mutex_lock(&frontend_mutex);
dvb_unregister_device (fepriv->dvbdev);
dvb_frontend_stop (fe);
- if (fe->ops.tuner_ops.release) {
- fe->ops.tuner_ops.release(fe);
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 0);
- }
- if (fe->ops.release)
- fe->ops.release(fe);
- else
- printk("dvb_frontend: Demodulator (%s) does not have a release callback!\n", fe->ops.info.name);
+
/* fe is invalid now */
kfree(fepriv);
mutex_unlock(&frontend_mutex);
return 0;
}
EXPORT_SYMBOL(dvb_unregister_frontend);
+
+#ifdef CONFIG_DVB_CORE_ATTACH
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ void *ptr;
+
+ if (fe->ops.release_sec) {
+ fe->ops.release_sec(fe);
+ symbol_put_addr(fe->ops.release_sec);
+ }
+ if (fe->ops.tuner_ops.release) {
+ fe->ops.tuner_ops.release(fe);
+ symbol_put_addr(fe->ops.tuner_ops.release);
+ }
+ ptr = (void*)fe->ops.release;
+ if (ptr) {
+ fe->ops.release(fe);
+ symbol_put_addr(ptr);
+ }
+}
+#else
+void dvb_frontend_detach(struct dvb_frontend* fe)
+{
+ if (fe->ops.release_sec)
+ fe->ops.release_sec(fe);
+ if (fe->ops.tuner_ops.release)
+ fe->ops.tuner_ops.release(fe);
+ if (fe->ops.release)
+ fe->ops.release(fe);
+}
+#endif
+EXPORT_SYMBOL(dvb_frontend_detach);
diff --git a/drivers/media/dvb/dvb-core/dvb_frontend.h b/drivers/media/dvb/dvb-core/dvb_frontend.h
index 2887e2b862a..e5d5028b369 100644
--- a/drivers/media/dvb/dvb-core/dvb_frontend.h
+++ b/drivers/media/dvb/dvb-core/dvb_frontend.h
@@ -92,10 +92,13 @@ struct dvb_frontend_ops {
struct dvb_frontend_info info;
void (*release)(struct dvb_frontend* fe);
+ void (*release_sec)(struct dvb_frontend* fe);
int (*init)(struct dvb_frontend* fe);
int (*sleep)(struct dvb_frontend* fe);
+ int (*write)(struct dvb_frontend* fe, u8* buf, int len);
+
/* if this is set, it overrides the default swzigzag */
int (*tune)(struct dvb_frontend* fe,
struct dvb_frontend_parameters* params,
@@ -147,7 +150,7 @@ struct dvb_frontend {
void* demodulator_priv;
void* tuner_priv;
void* frontend_priv;
- void* misc_priv;
+ void* sec_priv;
};
extern int dvb_register_frontend(struct dvb_adapter* dvb,
@@ -155,6 +158,8 @@ extern int dvb_register_frontend(struct dvb_adapter* dvb,
extern int dvb_unregister_frontend(struct dvb_frontend* fe);
+extern void dvb_frontend_detach(struct dvb_frontend* fe);
+
extern void dvb_frontend_reinitialise(struct dvb_frontend *fe);
extern void dvb_frontend_sleep_until(struct timeval *waketime, u32 add_usec);
diff --git a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
index c972fe014c5..9878183ba3f 100644
--- a/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
+++ b/drivers/media/dvb/dvb-core/dvb_ringbuffer.c
@@ -26,7 +26,6 @@
-#define __KERNEL_SYSCALLS__
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/media/dvb/dvb-core/dvbdev.h b/drivers/media/dvb/dvb-core/dvbdev.h
index 7a7f75fd168..620e7887b3d 100644
--- a/drivers/media/dvb/dvb-core/dvbdev.h
+++ b/drivers/media/dvb/dvb-core/dvbdev.h
@@ -102,4 +102,26 @@ extern int dvb_usercopy(struct inode *inode, struct file *file,
int (*func)(struct inode *inode, struct file *file,
unsigned int cmd, void *arg));
+/** generic DVB attach function. */
+#ifdef CONFIG_DVB_CORE_ATTACH
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ void *__r = NULL; \
+ typeof(&FUNCTION) __a = symbol_request(FUNCTION); \
+ if (__a) { \
+ __r = (void *) __a(ARGS); \
+ if (__r == NULL) \
+ symbol_put(FUNCTION); \
+ } else { \
+ printk(KERN_ERR "DVB: Unable to find symbol "#FUNCTION"()\n"); \
+ } \
+ __r; \
+})
+
+#else
+#define dvb_attach(FUNCTION, ARGS...) ({ \
+ FUNCTION(ARGS); \
+})
+
+#endif
+
#endif /* #ifndef _DVBDEV_H_ */
diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig
index 75824b77198..0a3c35399be 100644
--- a/drivers/media/dvb/dvb-usb/Kconfig
+++ b/drivers/media/dvb/dvb-usb/Kconfig
@@ -26,6 +26,7 @@ config DVB_USB_A800
tristate "AVerMedia AverTV DVB-T USB 2.0 (A800)"
depends on DVB_USB
select DVB_DIB3000MC
+ select DVB_TUNER_MT2060
help
Say Y here to support the AVerMedia AverTV DVB-T USB 2.0 (A800) receiver.
@@ -33,6 +34,7 @@ config DVB_USB_DIBUSB_MB
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-B) (see help for device list)"
depends on DVB_USB
select DVB_DIB3000MB
+ select DVB_TUNER_MT2060
help
Support for USB 1.1 and 2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-B demodulator.
@@ -65,6 +67,7 @@ config DVB_USB_DIBUSB_MC
tristate "DiBcom USB DVB-T devices (based on the DiB3000M-C/P) (see help for device list)"
depends on DVB_USB
select DVB_DIB3000MC
+ select DVB_TUNER_MT2060
help
Support for 2.0 DVB-T receivers based on reference designs made by
DiBcom (<http://www.dibcom.fr>) equipped with a DiB3000M-C/P demodulator.
@@ -80,16 +83,17 @@ config DVB_USB_UMT_010
tristate "HanfTek UMT-010 DVB-T USB2.0 support"
depends on DVB_USB
select DVB_DIB3000MC
+ select DVB_TUNER_MT2060
help
Say Y here to support the HanfTek UMT-010 USB2.0 stick-sized DVB-T receiver.
config DVB_USB_CXUSB
tristate "Conexant USB2.0 hybrid reference design support"
depends on DVB_USB
- select DVB_CX22702
- select DVB_LGDT330X
- select DVB_MT352
- select DVB_ZL10353
+ select DVB_CX22702 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Conexant USB2.0 hybrid reference design.
Currently, only DVB and ATSC modes are supported, analog mode
@@ -101,8 +105,8 @@ config DVB_USB_CXUSB
config DVB_USB_DIGITV
tristate "Nebula Electronics uDigiTV DVB-T USB2.0 support"
depends on DVB_USB
- select DVB_NXT6000
- select DVB_MT352
+ select DVB_NXT6000 if !DVB_FE_CUSTOMISE
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
help
Say Y here to support the Nebula Electronics uDigitV USB2.0 DVB-T receiver.
@@ -145,6 +149,7 @@ config DVB_USB_NOVA_T_USB2
tristate "Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 support"
depends on DVB_USB
select DVB_DIB3000MC
+ select DVB_TUNER_MT2060
help
Say Y here to support the Hauppauge WinTV-NOVA-T usb2 DVB-T USB2.0 receiver.
diff --git a/drivers/media/dvb/dvb-usb/a800.c b/drivers/media/dvb/dvb-usb/a800.c
index ce44aa6bbb8..df0c384bd4c 100644
--- a/drivers/media/dvb/dvb-usb/a800.c
+++ b/drivers/media/dvb/dvb-usb/a800.c
@@ -26,6 +26,13 @@ static int a800_power_ctrl(struct dvb_usb_device *d, int onoff)
return 0;
}
+/* assure to put cold to 0 for iManufacturer == 1 */
+static int a800_identify_state(struct usb_device *udev, struct dvb_usb_properties *props,struct dvb_usb_device_description **desc, int *cold)
+{
+ *cold = udev->descriptor.iManufacturer != 1;
+ return 0;
+}
+
static struct dvb_usb_rc_key a800_rc_keys[] = {
{ 0x02, 0x01, KEY_PROG1 }, /* SOURCE */
{ 0x02, 0x00, KEY_POWER }, /* POWER */
@@ -113,6 +120,7 @@ static struct dvb_usb_properties a800_properties = {
.power_ctrl = a800_power_ctrl,
.frontend_attach = dibusb_dib3000mc_frontend_attach,
.tuner_attach = dibusb_dib3000mc_tuner_attach,
+ .identify_state = a800_identify_state,
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = a800_rc_keys,
diff --git a/drivers/media/dvb/dvb-usb/cxusb.c b/drivers/media/dvb/dvb-usb/cxusb.c
index ae23bdde42a..c710c0176e0 100644
--- a/drivers/media/dvb/dvb-usb/cxusb.c
+++ b/drivers/media/dvb/dvb-usb/cxusb.c
@@ -349,6 +349,7 @@ static struct mt352_config cxusb_dee1601_config = {
static struct zl10353_config cxusb_zl10353_dee1601_config = {
.demod_address = 0x0f,
+ .parallel_ts = 1,
};
static struct mt352_config cxusb_mt352_config = {
@@ -409,7 +410,7 @@ static int cxusb_cx22702_frontend_attach(struct dvb_usb_device *d)
cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, &b, 1);
- if ((d->fe = cx22702_attach(&cxusb_cx22702_config, &d->i2c_adap)) != NULL)
+ if ((d->fe = dvb_attach(cx22702_attach, &cxusb_cx22702_config, &d->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -422,7 +423,7 @@ static int cxusb_lgdt3303_frontend_attach(struct dvb_usb_device *d)
cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((d->fe = lgdt330x_attach(&cxusb_lgdt3303_config, &d->i2c_adap)) != NULL)
+ if ((d->fe = dvb_attach(lgdt330x_attach, &cxusb_lgdt3303_config, &d->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -435,7 +436,7 @@ static int cxusb_mt352_frontend_attach(struct dvb_usb_device *d)
cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0);
- if ((d->fe = mt352_attach(&cxusb_mt352_config, &d->i2c_adap)) != NULL)
+ if ((d->fe = dvb_attach(mt352_attach, &cxusb_mt352_config, &d->i2c_adap)) != NULL)
return 0;
return -EIO;
@@ -448,8 +449,8 @@ static int cxusb_dee1601_frontend_attach(struct dvb_usb_device *d)
cxusb_ctrl_msg(d,CMD_DIGITAL, NULL, 0, NULL, 0);
- if (((d->fe = mt352_attach(&cxusb_dee1601_config, &d->i2c_adap)) != NULL) ||
- ((d->fe = zl10353_attach(&cxusb_zl10353_dee1601_config, &d->i2c_adap)) != NULL))
+ if (((d->fe = dvb_attach(mt352_attach, &cxusb_dee1601_config, &d->i2c_adap)) != NULL) ||
+ ((d->fe = dvb_attach(zl10353_attach, &cxusb_zl10353_dee1601_config, &d->i2c_adap)) != NULL))
return 0;
return -EIO;
diff --git a/drivers/media/dvb/dvb-usb/dibusb-common.c b/drivers/media/dvb/dvb-usb/dibusb-common.c
index abd75b4a350..124e25ac53b 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-common.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-common.c
@@ -131,9 +131,6 @@ static int dibusb_i2c_xfer(struct i2c_adapter *adap,struct i2c_msg msg[],int num
if (mutex_lock_interruptible(&d->i2c_mutex) < 0)
return -EAGAIN;
- if (num > 2)
- warn("more than 2 i2c messages at a time is not handled yet. TODO.");
-
for (i = 0; i < num; i++) {
/* write/read request */
if (i+1 < num && (msg[i+1].flags & I2C_M_RD)) {
@@ -168,31 +165,137 @@ int dibusb_read_eeprom_byte(struct dvb_usb_device *d, u8 offs, u8 *val)
}
EXPORT_SYMBOL(dibusb_read_eeprom_byte);
+/* 3000MC/P stuff */
+// Config Adjacent channels Perf -cal22
+static struct dibx000_agc_config dib3000p_mt2060_agc_config = {
+ .band_caps = BAND_VHF | BAND_UHF,
+ .setup = (0 << 15) | (0 << 14) | (1 << 13) | (1 << 12) | (29 << 0),
+
+ .agc1_max = 48497,
+ .agc1_min = 23593,
+ .agc2_max = 46531,
+ .agc2_min = 24904,
+
+ .agc1_pt1 = 0x65,
+ .agc1_pt2 = 0x69,
+
+ .agc1_slope1 = 0x51,
+ .agc1_slope2 = 0x27,
+
+ .agc2_pt1 = 0,
+ .agc2_pt2 = 0x33,
+
+ .agc2_slope1 = 0x35,
+ .agc2_slope2 = 0x37,
+};
+
+static struct dib3000mc_config stk3000p_dib3000p_config = {
+ &dib3000p_mt2060_agc_config,
+
+ .max_time = 0x196,
+ .ln_adc_level = 0x1cc7,
+
+ .output_mpeg2_in_188_bytes = 1,
+};
+
+static struct dibx000_agc_config dib3000p_panasonic_agc_config = {
+ .setup = (0 << 15) | (0 << 14) | (1 << 13) | (1 << 12) | (29 << 0),
+
+ .agc1_max = 56361,
+ .agc1_min = 22282,
+ .agc2_max = 47841,
+ .agc2_min = 36045,
+
+ .agc1_pt1 = 0x3b,
+ .agc1_pt2 = 0x6b,
+
+ .agc1_slope1 = 0x55,
+ .agc1_slope2 = 0x1d,
+
+ .agc2_pt1 = 0,
+ .agc2_pt2 = 0x0a,
+
+ .agc2_slope1 = 0x95,
+ .agc2_slope2 = 0x1e,
+};
+
+static struct dib3000mc_config mod3000p_dib3000p_config = {
+ &dib3000p_panasonic_agc_config,
+
+ .max_time = 0x51,
+ .ln_adc_level = 0x1cc7,
+
+ .output_mpeg2_in_188_bytes = 1,
+};
+
int dibusb_dib3000mc_frontend_attach(struct dvb_usb_device *d)
{
- struct dib3000_config demod_cfg;
- struct dibusb_state *st = d->priv;
-
- for (demod_cfg.demod_address = 0x8; demod_cfg.demod_address < 0xd; demod_cfg.demod_address++)
- if ((d->fe = dib3000mc_attach(&demod_cfg,&d->i2c_adap,&st->ops)) != NULL) {
- d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
- d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
- d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl;
- return 0;
+ if (dib3000mc_attach(&d->i2c_adap, 1, DEFAULT_DIB3000P_I2C_ADDRESS, 0, &mod3000p_dib3000p_config, &d->fe) == 0 ||
+ dib3000mc_attach(&d->i2c_adap, 1, DEFAULT_DIB3000MC_I2C_ADDRESS, 0, &mod3000p_dib3000p_config, &d->fe) == 0) {
+ if (d->priv != NULL) {
+ struct dibusb_state *st = d->priv;
+ st->ops.pid_parse = dib3000mc_pid_parse;
+ st->ops.pid_ctrl = dib3000mc_pid_control;
}
-
+ return 0;
+ }
return -ENODEV;
}
EXPORT_SYMBOL(dibusb_dib3000mc_frontend_attach);
+static struct mt2060_config stk3000p_mt2060_config = {
+ 0x60
+};
+
int dibusb_dib3000mc_tuner_attach (struct dvb_usb_device *d)
{
- d->pll_addr = 0x60;
- d->pll_desc = &dvb_pll_env57h1xd5;
-
- d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
- d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
+ struct dibusb_state *st = d->priv;
+ int ret;
+ u8 a,b;
+ u16 if1 = 1220;
+ struct i2c_adapter *tun_i2c;
+
+ // First IF calibration for Liteon Sticks
+ if (d->udev->descriptor.idVendor == USB_VID_LITEON &&
+ d->udev->descriptor.idProduct == USB_PID_LITEON_DVB_T_WARM) {
+
+ dibusb_read_eeprom_byte(d,0x7E,&a);
+ dibusb_read_eeprom_byte(d,0x7F,&b);
+
+ if (a == 0x00)
+ if1 += b;
+ else if (a == 0x80)
+ if1 -= b;
+ else
+ warn("LITE-ON DVB-T: Strange IF1 calibration :%2X %2X\n", a, b);
+
+ } else if (d->udev->descriptor.idVendor == USB_VID_DIBCOM &&
+ d->udev->descriptor.idProduct == USB_PID_DIBCOM_MOD3001_WARM) {
+ u8 desc;
+ dibusb_read_eeprom_byte(d, 7, &desc);
+ if (desc == 2) {
+ a = 127;
+ do {
+ dibusb_read_eeprom_byte(d, a, &desc);
+ a--;
+ } while (a > 7 && (desc == 0xff || desc == 0x00));
+ if (desc & 0x80)
+ if1 -= (0xff - desc);
+ else
+ if1 += desc;
+ }
+ }
+ tun_i2c = dib3000mc_get_tuner_i2c_master(d->fe, 1);
+ if ((ret = mt2060_attach(d->fe, tun_i2c, &stk3000p_mt2060_config, if1)) != 0) {
+ /* not found - use panasonic pll parameters */
+ if (dvb_pll_attach(d->fe, 0x60, tun_i2c, &dvb_pll_env57h1xd5) == NULL)
+ return -ENOMEM;
+ } else {
+ st->mt2060_present = 1;
+ /* set the correct parameters for the dib3000p */
+ dib3000mc_set_config(d->fe, &stk3000p_dib3000p_config);
+ }
return 0;
}
EXPORT_SYMBOL(dibusb_dib3000mc_tuner_attach);
@@ -267,6 +370,67 @@ struct dvb_usb_rc_key dibusb_rc_keys[] = {
{ 0x86, 0x1e, KEY_DOWN },
{ 0x86, 0x1f, KEY_LEFT },
{ 0x86, 0x1b, KEY_RIGHT },
+
+ /* Key codes for the DiBcom MOD3000 remote. */
+ { 0x80, 0x00, KEY_MUTE },
+ { 0x80, 0x01, KEY_TEXT },
+ { 0x80, 0x02, KEY_HOME },
+ { 0x80, 0x03, KEY_POWER },
+
+ { 0x80, 0x04, KEY_RED },
+ { 0x80, 0x05, KEY_GREEN },
+ { 0x80, 0x06, KEY_YELLOW },
+ { 0x80, 0x07, KEY_BLUE },
+
+ { 0x80, 0x08, KEY_DVD },
+ { 0x80, 0x09, KEY_AUDIO },
+ { 0x80, 0x0a, KEY_MEDIA }, /* Pictures */
+ { 0x80, 0x0b, KEY_VIDEO },
+
+ { 0x80, 0x0c, KEY_BACK },
+ { 0x80, 0x0d, KEY_UP },
+ { 0x80, 0x0e, KEY_RADIO },
+ { 0x80, 0x0f, KEY_EPG },
+
+ { 0x80, 0x10, KEY_LEFT },
+ { 0x80, 0x11, KEY_OK },
+ { 0x80, 0x12, KEY_RIGHT },
+ { 0x80, 0x13, KEY_UNKNOWN }, /* SAP */
+
+ { 0x80, 0x14, KEY_TV },
+ { 0x80, 0x15, KEY_DOWN },
+ { 0x80, 0x16, KEY_MENU }, /* DVD Menu */
+ { 0x80, 0x17, KEY_LAST },
+
+ { 0x80, 0x18, KEY_RECORD },
+ { 0x80, 0x19, KEY_STOP },
+ { 0x80, 0x1a, KEY_PAUSE },
+ { 0x80, 0x1b, KEY_PLAY },
+
+ { 0x80, 0x1c, KEY_PREVIOUS },
+ { 0x80, 0x1d, KEY_REWIND },
+ { 0x80, 0x1e, KEY_FASTFORWARD },
+ { 0x80, 0x1f, KEY_NEXT},
+
+ { 0x80, 0x40, KEY_1 },
+ { 0x80, 0x41, KEY_2 },
+ { 0x80, 0x42, KEY_3 },
+ { 0x80, 0x43, KEY_CHANNELUP },
+
+ { 0x80, 0x44, KEY_4 },
+ { 0x80, 0x45, KEY_5 },
+ { 0x80, 0x46, KEY_6 },
+ { 0x80, 0x47, KEY_CHANNELDOWN },
+
+ { 0x80, 0x48, KEY_7 },
+ { 0x80, 0x49, KEY_8 },
+ { 0x80, 0x4a, KEY_9 },
+ { 0x80, 0x4b, KEY_VOLUMEUP },
+
+ { 0x80, 0x4c, KEY_CLEAR },
+ { 0x80, 0x4d, KEY_0 },
+ { 0x80, 0x4e, KEY_ENTER },
+ { 0x80, 0x4f, KEY_VOLUMEDOWN },
};
EXPORT_SYMBOL(dibusb_rc_keys);
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mb.c b/drivers/media/dvb/dvb-usb/dibusb-mb.c
index f4c45f386eb..effd34cc4b0 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mb.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mb.c
@@ -21,11 +21,11 @@ static int dibusb_dib3000mb_frontend_attach(struct dvb_usb_device *d)
demod_cfg.demod_address = 0x8;
- if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL) {
- d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
- d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
+ if ((d->fe = dib3000mb_attach(&demod_cfg,&d->i2c_adap,&st->ops)) == NULL)
return -ENODEV;
- }
+
+ d->fe->ops.tuner_ops.init = dvb_usb_tuner_init_i2c;
+ d->fe->ops.tuner_ops.set_params = dvb_usb_tuner_set_params_i2c;
d->tuner_pass_ctrl = st->ops.tuner_pass_ctrl;
@@ -169,7 +169,7 @@ static struct dvb_usb_properties dibusb1_1_properties = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
- .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */
+ .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
@@ -247,7 +247,7 @@ static struct dvb_usb_properties dibusb1_1_an2235_properties = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
- .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */
+ .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
@@ -272,8 +272,8 @@ static struct dvb_usb_properties dibusb1_1_an2235_properties = {
#endif
.devices = {
{ "Artec T1 USB1.1 TVBOX with AN2235",
- { &dibusb_dib3000mb_table[20], NULL },
{ &dibusb_dib3000mb_table[21], NULL },
+ { &dibusb_dib3000mb_table[22], NULL },
},
#ifdef CONFIG_DVB_USB_DIBUSB_MB_FAULTY
{ "Artec T1 USB1.1 TVBOX with AN2235 (faulty USB IDs)",
@@ -304,7 +304,7 @@ static struct dvb_usb_properties dibusb2_0b_properties = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
- .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */
+ .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
@@ -355,7 +355,7 @@ static struct dvb_usb_properties artec_t1_usb2_properties = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
- .rc_key_map_size = 63, /* wow, that is ugly ... I want to load it to the driver dynamically */
+ .rc_key_map_size = 111, /* wow, that is ugly ... I want to load it to the driver dynamically */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
diff --git a/drivers/media/dvb/dvb-usb/dibusb-mc.c b/drivers/media/dvb/dvb-usb/dibusb-mc.c
index 55802fba3c2..eca4082a61a 100644
--- a/drivers/media/dvb/dvb-usb/dibusb-mc.c
+++ b/drivers/media/dvb/dvb-usb/dibusb-mc.c
@@ -28,6 +28,17 @@ static struct usb_device_id dibusb_dib3000mc_table [] = {
/* 00 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_COLD) },
/* 01 */ { USB_DEVICE(USB_VID_DIBCOM, USB_PID_DIBCOM_MOD3001_WARM) },
/* 02 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_COLD) },
+/* 03 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ULTIMA_TVBOX_USB2_WARM) }, // ( ? )
+/* 04 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_COLD) },
+/* 05 */ { USB_DEVICE(USB_VID_LITEON, USB_PID_LITEON_DVB_T_WARM) },
+/* 06 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_COLD) },
+/* 07 */ { USB_DEVICE(USB_VID_EMPIA, USB_PID_DIGIVOX_MINI_SL_WARM) },
+/* 08 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_COLD) },
+/* 09 */ { USB_DEVICE(USB_VID_GRANDTEC, USB_PID_GRANDTEC_DVBT_USB2_WARM) },
+/* 10 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_COLD) },
+/* 11 */ { USB_DEVICE(USB_VID_ULTIMA_ELECTRONIC, USB_PID_ARTEC_T14_WARM) },
+/* 12 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_COLD) },
+/* 13 */ { USB_DEVICE(USB_VID_LEADTEK, USB_PID_WINFAST_DTV_DONGLE_WARM) },
{ } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, dibusb_dib3000mc_table);
@@ -50,7 +61,7 @@ static struct dvb_usb_properties dibusb_mc_properties = {
.rc_interval = DEFAULT_RC_INTERVAL,
.rc_key_map = dibusb_rc_keys,
- .rc_key_map_size = 63, /* FIXME */
+ .rc_key_map_size = 111, /* FIXME */
.rc_query = dibusb_rc_query,
.i2c_algo = &dibusb_i2c_algo,
@@ -68,16 +79,38 @@ static struct dvb_usb_properties dibusb_mc_properties = {
}
},
- .num_device_descs = 2,
+ .num_device_descs = 7,
.devices = {
{ "DiBcom USB2.0 DVB-T reference design (MOD3000P)",
{ &dibusb_dib3000mc_table[0], NULL },
{ &dibusb_dib3000mc_table[1], NULL },
},
- { "Artec T1 USB2.0 TVBOX (please report the warm ID)",
+ { "Artec T1 USB2.0 TVBOX (please check the warm ID)",
{ &dibusb_dib3000mc_table[2], NULL },
- { NULL },
+ { &dibusb_dib3000mc_table[3], NULL },
},
+ { "LITE-ON USB2.0 DVB-T Tuner",
+ /* Also rebranded as Intuix S800, Toshiba */
+ { &dibusb_dib3000mc_table[4], NULL },
+ { &dibusb_dib3000mc_table[5], NULL },
+ },
+ { "MSI Digivox Mini SL",
+ { &dibusb_dib3000mc_table[6], NULL },
+ { &dibusb_dib3000mc_table[7], NULL },
+ },
+ { "GRAND - USB2.0 DVB-T adapter",
+ { &dibusb_dib3000mc_table[8], NULL },
+ { &dibusb_dib3000mc_table[9], NULL },
+ },
+ { "Artec T14 - USB2.0 DVB-T",
+ { &dibusb_dib3000mc_table[10], NULL },
+ { &dibusb_dib3000mc_table[11], NULL },
+ },
+ { "Leadtek - USB2.0 Winfast DTV dongle",
+ { &dibusb_dib3000mc_table[12], NULL },
+ { &dibusb_dib3000mc_table[13], NULL },
+ },
+ { NULL },
}
};
diff --git a/drivers/media/dvb/dvb-usb/dibusb.h b/drivers/media/dvb/dvb-usb/dibusb.h
index 2d99d05c7ea..a43f87480cf 100644
--- a/drivers/media/dvb/dvb-usb/dibusb.h
+++ b/drivers/media/dvb/dvb-usb/dibusb.h
@@ -17,6 +17,8 @@
#include "dvb-usb.h"
#include "dib3000.h"
+#include "dib3000mc.h"
+#include "mt2060.h"
/*
* protocol of all dibusb related devices
@@ -96,6 +98,7 @@
struct dibusb_state {
struct dib_fe_xfer_ops ops;
+ int mt2060_present;
/* for RC5 remote control */
int old_toggle;
diff --git a/drivers/media/dvb/dvb-usb/digitv.c b/drivers/media/dvb/dvb-usb/digitv.c
index c14d9efb48f..01585448730 100644
--- a/drivers/media/dvb/dvb-usb/digitv.c
+++ b/drivers/media/dvb/dvb-usb/digitv.c
@@ -128,11 +128,11 @@ static struct nxt6000_config digitv_nxt6000_config = {
static int digitv_frontend_attach(struct dvb_usb_device *d)
{
- if ((d->fe = mt352_attach(&digitv_mt352_config, &d->i2c_adap)) != NULL) {
+ if ((d->fe = dvb_attach(mt352_attach, &digitv_mt352_config, &d->i2c_adap)) != NULL) {
d->fe->ops.tuner_ops.calc_regs = dvb_usb_tuner_calc_regs;
return 0;
}
- if ((d->fe = nxt6000_attach(&digitv_nxt6000_config, &d->i2c_adap)) != NULL) {
+ if ((d->fe = dvb_attach(nxt6000_attach, &digitv_nxt6000_config, &d->i2c_adap)) != NULL) {
d->fe->ops.tuner_ops.set_params = digitv_nxt6000_tuner_set_params;
return 0;
}
@@ -147,21 +147,91 @@ static int digitv_tuner_attach(struct dvb_usb_device *d)
}
static struct dvb_usb_rc_key digitv_rc_keys[] = {
- { 0x00, 0x16, KEY_POWER }, /* dummy key */
+ { 0x5f, 0x55, KEY_0 },
+ { 0x6f, 0x55, KEY_1 },
+ { 0x9f, 0x55, KEY_2 },
+ { 0xaf, 0x55, KEY_3 },
+ { 0x5f, 0x56, KEY_4 },
+ { 0x6f, 0x56, KEY_5 },
+ { 0x9f, 0x56, KEY_6 },
+ { 0xaf, 0x56, KEY_7 },
+ { 0x5f, 0x59, KEY_8 },
+ { 0x6f, 0x59, KEY_9 },
+ { 0x9f, 0x59, KEY_TV },
+ { 0xaf, 0x59, KEY_AUX },
+ { 0x5f, 0x5a, KEY_DVD },
+ { 0x6f, 0x5a, KEY_POWER },
+ { 0x9f, 0x5a, KEY_MHP }, /* labelled 'Picture' */
+ { 0xaf, 0x5a, KEY_AUDIO },
+ { 0x5f, 0x65, KEY_INFO },
+ { 0x6f, 0x65, KEY_F13 }, /* 16:9 */
+ { 0x9f, 0x65, KEY_F14 }, /* 14:9 */
+ { 0xaf, 0x65, KEY_EPG },
+ { 0x5f, 0x66, KEY_EXIT },
+ { 0x6f, 0x66, KEY_MENU },
+ { 0x9f, 0x66, KEY_UP },
+ { 0xaf, 0x66, KEY_DOWN },
+ { 0x5f, 0x69, KEY_LEFT },
+ { 0x6f, 0x69, KEY_RIGHT },
+ { 0x9f, 0x69, KEY_ENTER },
+ { 0xaf, 0x69, KEY_CHANNELUP },
+ { 0x5f, 0x6a, KEY_CHANNELDOWN },
+ { 0x6f, 0x6a, KEY_VOLUMEUP },
+ { 0x9f, 0x6a, KEY_VOLUMEDOWN },
+ { 0xaf, 0x6a, KEY_RED },
+ { 0x5f, 0x95, KEY_GREEN },
+ { 0x6f, 0x95, KEY_YELLOW },
+ { 0x9f, 0x95, KEY_BLUE },
+ { 0xaf, 0x95, KEY_SUBTITLE },
+ { 0x5f, 0x96, KEY_F15 }, /* AD */
+ { 0x6f, 0x96, KEY_TEXT },
+ { 0x9f, 0x96, KEY_MUTE },
+ { 0xaf, 0x96, KEY_REWIND },
+ { 0x5f, 0x99, KEY_STOP },
+ { 0x6f, 0x99, KEY_PLAY },
+ { 0x9f, 0x99, KEY_FASTFORWARD },
+ { 0xaf, 0x99, KEY_F16 }, /* chapter */
+ { 0x5f, 0x9a, KEY_PAUSE },
+ { 0x6f, 0x9a, KEY_PLAY },
+ { 0x9f, 0x9a, KEY_RECORD },
+ { 0xaf, 0x9a, KEY_F17 }, /* picture in picture */
+ { 0x5f, 0xa5, KEY_KPPLUS }, /* zoom in */
+ { 0x6f, 0xa5, KEY_KPMINUS }, /* zoom out */
+ { 0x9f, 0xa5, KEY_F18 }, /* capture */
+ { 0xaf, 0xa5, KEY_F19 }, /* web */
+ { 0x5f, 0xa6, KEY_EMAIL },
+ { 0x6f, 0xa6, KEY_PHONE },
+ { 0x9f, 0xa6, KEY_PC },
};
-/* TODO is it really the NEC protocol ? */
static int digitv_rc_query(struct dvb_usb_device *d, u32 *event, int *state)
{
+ int i;
u8 key[5];
+ u8 b[4] = { 0 };
+
+ *event = 0;
+ *state = REMOTE_NO_KEY_PRESSED;
digitv_ctrl_msg(d,USB_READ_REMOTE,0,NULL,0,&key[1],4);
- /* TODO state, maybe it is VV ? */
+
+ /* Tell the device we've read the remote. Not sure how necessary
+ this is, but the Nebula SDK does it. */
+ digitv_ctrl_msg(d,USB_WRITE_REMOTE,0,b,4,NULL,0);
+
+ /* if something is inside the buffer, simulate key press */
if (key[1] != 0)
- key[0] = 0x01; /* if something is inside the buffer, simulate key press */
+ {
+ for (i = 0; i < d->props.rc_key_map_size; i++) {
+ if (d->props.rc_key_map[i].custom == key[1] &&
+ d->props.rc_key_map[i].data == key[2]) {
+ *event = d->props.rc_key_map[i].event;
+ *state = REMOTE_KEY_PRESSED;
+ return 0;
+ }
+ }
+ }
- /* call the universal NEC remote processor, to find out the key's state and event */
- dvb_usb_nec_rc_key_to_event(d,key,event,state);
if (key[0] != 0)
deb_rc("key: %x %x %x %x %x\n",key[0],key[1],key[2],key[3],key[4]);
return 0;
diff --git a/drivers/media/dvb/dvb-usb/dtt200u.c b/drivers/media/dvb/dvb-usb/dtt200u.c
index 70afcfd141c..27af4e43647 100644
--- a/drivers/media/dvb/dvb-usb/dtt200u.c
+++ b/drivers/media/dvb/dvb-usb/dtt200u.c
@@ -93,6 +93,7 @@ static int dtt200u_frontend_attach(struct dvb_usb_device *d)
}
static struct dvb_usb_properties dtt200u_properties;
+static struct dvb_usb_properties wt220u_fc_properties;
static struct dvb_usb_properties wt220u_properties;
static struct dvb_usb_properties wt220u_zl0353_properties;
@@ -101,6 +102,7 @@ static int dtt200u_usb_probe(struct usb_interface *intf,
{
if (dvb_usb_device_init(intf,&dtt200u_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&wt220u_properties,THIS_MODULE,NULL) == 0 ||
+ dvb_usb_device_init(intf,&wt220u_fc_properties,THIS_MODULE,NULL) == 0 ||
dvb_usb_device_init(intf,&wt220u_zl0353_properties,THIS_MODULE,NULL) == 0)
return 0;
@@ -114,6 +116,9 @@ static struct usb_device_id dtt200u_usb_table [] = {
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_WARM) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_COLD) },
{ USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZL0353_WARM) },
+ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_COLD) },
+ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_FC_WARM) },
+ { USB_DEVICE(USB_VID_WIDEVIEW, USB_PID_WT220U_ZAP250_COLD) },
{ 0 },
};
MODULE_DEVICE_TABLE(usb, dtt200u_usb_table);
@@ -193,13 +198,54 @@ static struct dvb_usb_properties wt220u_properties = {
.num_device_descs = 1,
.devices = {
{ .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
- .cold_ids = { &dtt200u_usb_table[2], NULL },
+ .cold_ids = { &dtt200u_usb_table[2], &dtt200u_usb_table[8], NULL },
.warm_ids = { &dtt200u_usb_table[3], NULL },
},
{ NULL },
}
};
+static struct dvb_usb_properties wt220u_fc_properties = {
+ .caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING,
+ .pid_filter_count = 15,
+
+ .usb_ctrl = CYPRESS_FX2,
+ .firmware = "dvb-usb-wt220u-fc03.fw",
+
+ .power_ctrl = dtt200u_power_ctrl,
+ .streaming_ctrl = dtt200u_streaming_ctrl,
+ .pid_filter = dtt200u_pid_filter,
+ .frontend_attach = dtt200u_frontend_attach,
+
+ .rc_interval = 300,
+ .rc_key_map = dtt200u_rc_keys,
+ .rc_key_map_size = ARRAY_SIZE(dtt200u_rc_keys),
+ .rc_query = dtt200u_rc_query,
+
+ .generic_bulk_ctrl_endpoint = 0x01,
+
+ /* parameter for the MPEG2-data transfer */
+ .urb = {
+ .type = DVB_USB_BULK,
+ .count = 7,
+ .endpoint = 0x86,
+ .u = {
+ .bulk = {
+ .buffersize = 4096,
+ }
+ }
+ },
+
+ .num_device_descs = 1,
+ .devices = {
+ { .name = "WideView WT-220U PenType Receiver (Typhoon/Freecom)",
+ .cold_ids = { &dtt200u_usb_table[6], NULL },
+ .warm_ids = { &dtt200u_usb_table[7], NULL },
+ },
+ { NULL },
+ }
+};
+
static struct dvb_usb_properties wt220u_zl0353_properties = {
.caps = DVB_USB_HAS_PID_FILTER | DVB_USB_NEED_PID_FILTERING,
.pid_filter_count = 15,
@@ -271,6 +317,6 @@ module_init(dtt200u_usb_module_init);
module_exit(dtt200u_usb_module_exit);
MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de>");
-MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon DVB-T USB2.0 devices");
+MODULE_DESCRIPTION("Driver for the WideView/Yakumo/Hama/Typhoon/Club3D DVB-T USB2.0 devices");
MODULE_VERSION("1.0");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
index ec631708c39..fe6208ada90 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-dvb.c
@@ -175,36 +175,36 @@ static int dvb_usb_fe_sleep(struct dvb_frontend *fe)
int dvb_usb_fe_init(struct dvb_usb_device* d)
{
if (d->props.frontend_attach == NULL) {
- err("strange '%s' doesn't want to attach a frontend.",d->desc->name);
+ err("strange: '%s' doesn't want to attach a frontend.",d->desc->name);
return 0;
}
- d->props.frontend_attach(d);
-
/* re-assign sleep and wakeup functions */
- if (d->fe != NULL) {
+ if (d->props.frontend_attach(d) == 0 && d->fe != NULL) {
d->fe_init = d->fe->ops.init; d->fe->ops.init = dvb_usb_fe_wakeup;
d->fe_sleep = d->fe->ops.sleep; d->fe->ops.sleep = dvb_usb_fe_sleep;
if (dvb_register_frontend(&d->dvb_adap, d->fe)) {
err("Frontend registration failed.");
- if (d->fe->ops.release)
- d->fe->ops.release(d->fe);
+ dvb_frontend_detach(d->fe);
d->fe = NULL;
return -ENODEV;
}
+
+ /* only attach the tuner if the demod is there */
+ if (d->props.tuner_attach != NULL)
+ d->props.tuner_attach(d);
} else
err("no frontend was attached by '%s'",d->desc->name);
- if (d->props.tuner_attach != NULL)
- d->props.tuner_attach(d);
-
return 0;
}
int dvb_usb_fe_exit(struct dvb_usb_device *d)
{
- if (d->fe != NULL)
+ if (d->fe != NULL) {
dvb_unregister_frontend(d->fe);
+ dvb_frontend_detach(d->fe);
+ }
return 0;
}
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
index 95698918bc1..57a10de1d3d 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-ids.h
@@ -10,51 +10,53 @@
#define _DVB_USB_IDS_H_
/* Vendor IDs */
-#define USB_VID_ADSTECH 0x06e1
-#define USB_VID_ANCHOR 0x0547
-#define USB_VID_WIDEVIEW 0x14aa
-#define USB_VID_AVERMEDIA 0x07ca
-#define USB_VID_COMPRO 0x185b
-#define USB_VID_COMPRO_UNK 0x145f
-#define USB_VID_CYPRESS 0x04b4
-#define USB_VID_DIBCOM 0x10b8
-#define USB_VID_DVICO 0x0fe9
-#define USB_VID_EMPIA 0xeb1a
-#define USB_VID_GRANDTEC 0x5032
-#define USB_VID_HANFTEK 0x15f4
-#define USB_VID_HAUPPAUGE 0x2040
-#define USB_VID_HYPER_PALTEK 0x1025
-#define USB_VID_KWORLD 0xeb2a
-#define USB_VID_KYE 0x0458
-#define USB_VID_MEDION 0x1660
-#define USB_VID_PINNACLE 0x2304
-#define USB_VID_VISIONPLUS 0x13d3
-#define USB_VID_TWINHAN 0x1822
-#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
-#define USB_VID_GENPIX 0x09c0
+#define USB_VID_ADSTECH 0x06e1
+#define USB_VID_ANCHOR 0x0547
+#define USB_VID_AVERMEDIA 0x07ca
+#define USB_VID_COMPRO 0x185b
+#define USB_VID_COMPRO_UNK 0x145f
+#define USB_VID_CYPRESS 0x04b4
+#define USB_VID_DIBCOM 0x10b8
+#define USB_VID_DVICO 0x0fe9
+#define USB_VID_EMPIA 0xeb1a
+#define USB_VID_GENPIX 0x09c0
+#define USB_VID_GRANDTEC 0x5032
+#define USB_VID_HANFTEK 0x15f4
+#define USB_VID_HAUPPAUGE 0x2040
+#define USB_VID_HYPER_PALTEK 0x1025
+#define USB_VID_KWORLD 0xeb2a
+#define USB_VID_KYE 0x0458
+#define USB_VID_LEADTEK 0x0413
+#define USB_VID_LITEON 0x04ca
+#define USB_VID_MEDION 0x1660
+#define USB_VID_PINNACLE 0x2304
+#define USB_VID_VISIONPLUS 0x13d3
+#define USB_VID_TWINHAN 0x1822
+#define USB_VID_ULTIMA_ELECTRONIC 0x05d8
+#define USB_VID_WIDEVIEW 0x14aa
/* Product IDs */
#define USB_PID_ADSTECH_USB2_COLD 0xa333
#define USB_PID_ADSTECH_USB2_WARM 0xa334
-#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
-#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
-#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
-#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
-#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
-#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
-#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
-#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
+#define USB_PID_AVERMEDIA_DVBT_USB_COLD 0x0001
+#define USB_PID_AVERMEDIA_DVBT_USB_WARM 0x0002
+#define USB_PID_AVERMEDIA_DVBT_USB2_COLD 0xa800
+#define USB_PID_AVERMEDIA_DVBT_USB2_WARM 0xa801
+#define USB_PID_COMPRO_DVBU2000_COLD 0xd000
+#define USB_PID_COMPRO_DVBU2000_WARM 0xd001
+#define USB_PID_COMPRO_DVBU2000_UNK_COLD 0x010c
+#define USB_PID_COMPRO_DVBU2000_UNK_WARM 0x010d
#define USB_PID_DIBCOM_HOOK_DEFAULT 0x0064
-#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
+#define USB_PID_DIBCOM_HOOK_DEFAULT_REENUM 0x0065
#define USB_PID_DIBCOM_MOD3000_COLD 0x0bb8
#define USB_PID_DIBCOM_MOD3000_WARM 0x0bb9
#define USB_PID_DIBCOM_MOD3001_COLD 0x0bc6
#define USB_PID_DIBCOM_MOD3001_WARM 0x0bc7
#define USB_PID_DIBCOM_STK7700 0x1e14
-#define USB_PID_DIBCOM_STK7700_REENUM 0x1e15
-#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
-#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
-#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
+#define USB_PID_DIBCOM_STK7700_REENUM 0x1e15
+#define USB_PID_DIBCOM_ANCHOR_2135_COLD 0x2131
+#define USB_PID_GRANDTEC_DVBT_USB_COLD 0x0fa0
+#define USB_PID_GRANDTEC_DVBT_USB_WARM 0x0fa1
#define USB_PID_KWORLD_VSTREAM_COLD 0x17de
#define USB_PID_KWORLD_VSTREAM_WARM 0x17df
#define USB_PID_TWINHAN_VP7041_COLD 0x3201
@@ -69,25 +71,30 @@
#define USB_PID_DNTV_TINYUSB2_WARM 0x3224
#define USB_PID_ULTIMA_TVBOX_COLD 0x8105
#define USB_PID_ULTIMA_TVBOX_WARM 0x8106
-#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
-#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
-#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
-#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
-#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
-#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
-#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
-#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
-#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
-#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
-#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
+#define USB_PID_ULTIMA_TVBOX_AN2235_COLD 0x8107
+#define USB_PID_ULTIMA_TVBOX_AN2235_WARM 0x8108
+#define USB_PID_ULTIMA_TVBOX_ANCHOR_COLD 0x2235
+#define USB_PID_ULTIMA_TVBOX_USB2_COLD 0x8109
+#define USB_PID_ULTIMA_TVBOX_USB2_WARM 0x810a
+#define USB_PID_ARTEC_T14_COLD 0x810b
+#define USB_PID_ARTEC_T14_WARM 0x810c
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_COLD 0x8613
+#define USB_PID_ULTIMA_TVBOX_USB2_FX_WARM 0x1002
+#define USB_PID_UNK_HYPER_PALTEK_COLD 0x005e
+#define USB_PID_UNK_HYPER_PALTEK_WARM 0x005f
+#define USB_PID_HANFTEK_UMT_010_COLD 0x0001
+#define USB_PID_HANFTEK_UMT_010_WARM 0x0015
#define USB_PID_DTT200U_COLD 0x0201
#define USB_PID_DTT200U_WARM 0x0301
-#define USB_PID_WT220U_COLD 0x0222
-#define USB_PID_WT220U_WARM 0x0221
+#define USB_PID_WT220U_ZAP250_COLD 0x0220
+#define USB_PID_WT220U_COLD 0x0222
+#define USB_PID_WT220U_WARM 0x0221
+#define USB_PID_WT220U_FC_COLD 0x0225
+#define USB_PID_WT220U_FC_WARM 0x0226
#define USB_PID_WT220U_ZL0353_COLD 0x022a
#define USB_PID_WT220U_ZL0353_WARM 0x022b
-#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
-#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
+#define USB_PID_WINTV_NOVA_T_USB2_COLD 0x9300
+#define USB_PID_WINTV_NOVA_T_USB2_WARM 0x9301
#define USB_PID_NEBULA_DIGITV 0x0201
#define USB_PID_DVICO_BLUEBIRD_LGDT 0xd820
#define USB_PID_DVICO_BLUEBIRD_LG064F_COLD 0xd500
@@ -103,8 +110,17 @@
#define USB_PID_MEDION_MD95700 0x0932
#define USB_PID_KYE_DVB_T_COLD 0x701e
#define USB_PID_KYE_DVB_T_WARM 0x701f
-#define USB_PID_PCTV_200E 0x020e
-#define USB_PID_PCTV_400E 0x020f
-#define USB_PID_GENPIX_8PSK_COLD 0x0200
-#define USB_PID_GENPIX_8PSK_WARM 0x0201
+#define USB_PID_PCTV_200E 0x020e
+#define USB_PID_PCTV_400E 0x020f
+#define USB_PID_LITEON_DVB_T_COLD 0xf000
+#define USB_PID_LITEON_DVB_T_WARM 0xf001
+#define USB_PID_DIGIVOX_MINI_SL_COLD 0xe360
+#define USB_PID_DIGIVOX_MINI_SL_WARM 0xe361
+#define USB_PID_GRANDTEC_DVBT_USB2_COLD 0x0bc6
+#define USB_PID_GRANDTEC_DVBT_USB2_WARM 0x0bc7
+#define USB_PID_WINFAST_DTV_DONGLE_COLD 0x6025
+#define USB_PID_WINFAST_DTV_DONGLE_WARM 0x6026
+#define USB_PID_GENPIX_8PSK_COLD 0x0200
+#define USB_PID_GENPIX_8PSK_WARM 0x0201
+
#endif
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
index e5c6d9835e0..380b2a45ee4 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-remote.c
@@ -6,6 +6,7 @@
* This file contains functions for initializing the the input-device and for handling remote-control-queries.
*/
#include "dvb-usb-common.h"
+#include <linux/usb/input.h>
/* Remote-control poll function - called every dib->rc_query_interval ms to see
* whether the remote control has received anything.
@@ -96,7 +97,7 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
return 0;
usb_make_path(d->udev, d->rc_phys, sizeof(d->rc_phys));
- strlcpy(d->rc_phys, "/ir0", sizeof(d->rc_phys));
+ strlcat(d->rc_phys, "/ir0", sizeof(d->rc_phys));
d->rc_input_dev = input_allocate_device();
if (!d->rc_input_dev)
@@ -107,6 +108,8 @@ int dvb_usb_remote_init(struct dvb_usb_device *d)
d->rc_input_dev->keycodemax = KEY_MAX;
d->rc_input_dev->name = "IR-receiver inside an USB DVB receiver";
d->rc_input_dev->phys = d->rc_phys;
+ usb_to_input_id(d->udev, &d->rc_input_dev->id);
+ d->rc_input_dev->cdev.dev = &d->udev->dev;
/* set the bits for the keys */
deb_rc("key map size: %d\n", d->props.rc_key_map_size);
diff --git a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
index 9002f35aa95..88b283731bb 100644
--- a/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
+++ b/drivers/media/dvb/dvb-usb/dvb-usb-urb.c
@@ -80,7 +80,6 @@ static void dvb_usb_urb_complete(struct urb *urb, struct pt_regs *ptregs)
switch (urb->status) {
case 0: /* success */
- case -ETIMEDOUT: /* NAK */
break;
case -ECONNRESET: /* kill */
case -ENOENT:
diff --git a/drivers/media/dvb/dvb-usb/nova-t-usb2.c b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
index 412039d8dba..79f0a02ce98 100644
--- a/drivers/media/dvb/dvb-usb/nova-t-usb2.c
+++ b/drivers/media/dvb/dvb-usb/nova-t-usb2.c
@@ -156,7 +156,7 @@ static struct dvb_usb_properties nova_t_properties = {
.pid_filter_count = 32,
.usb_ctrl = CYPRESS_FX2,
- .firmware = "dvb-usb-nova-t-usb2-01.fw",
+ .firmware = "dvb-usb-nova-t-usb2-02.fw",
.size_of_priv = sizeof(struct dibusb_state),
diff --git a/drivers/media/dvb/dvb-usb/umt-010.c b/drivers/media/dvb/dvb-usb/umt-010.c
index 97d74da0dad..418a0b70715 100644
--- a/drivers/media/dvb/dvb-usb/umt-010.c
+++ b/drivers/media/dvb/dvb-usb/umt-010.c
@@ -58,7 +58,7 @@ static int umt_mt352_frontend_attach(struct dvb_usb_device *d)
umt_config.demod_init = umt_mt352_demod_init;
umt_config.demod_address = 0xf;
- d->fe = mt352_attach(&umt_config, &d->i2c_adap);
+ d->fe = dvb_attach(mt352_attach, &umt_config, &d->i2c_adap);
return 0;
}
diff --git a/drivers/media/dvb/frontends/Kconfig b/drivers/media/dvb/frontends/Kconfig
index db978555b1e..080fa257a0b 100644
--- a/drivers/media/dvb/frontends/Kconfig
+++ b/drivers/media/dvb/frontends/Kconfig
@@ -1,48 +1,73 @@
menu "Customise DVB Frontends"
depends on DVB_CORE
+config DVB_FE_CUSTOMISE
+ bool "Customise the frontend modules to build"
+ default N
+ help
+ This allows the user to deselect frontend drivers unnecessary
+ for their hardware from the build. Use this option with care
+ as deselecting frontends which are in fact necessary will result
+ in DVB devices which cannot be tuned due to lack of driver support.
+
+ If unsure say N.
+
comment "DVB-S (satellite) frontends"
depends on DVB_CORE
config DVB_STV0299
tristate "ST STV0299 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_CX24110
tristate "Conexant CX24110 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_CX24123
tristate "Conexant CX24123 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_TDA8083
tristate "Philips TDA8083 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_MT312
tristate "Zarlink VP310/MT312 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_VES1X93
tristate "VLSI VES1893 or VES1993 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
config DVB_S5H1420
tristate "Samsung S5H1420 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S tuner module. Say Y when you want to support this frontend.
+
+config DVB_TDA10086
+ tristate "Philips TDA10086 based"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-S tuner module. Say Y when you want to support this frontend.
@@ -52,6 +77,7 @@ comment "DVB-T (terrestrial) frontends"
config DVB_SP8870
tristate "Spase sp8870 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -64,6 +90,7 @@ config DVB_SP8870
config DVB_SP887X
tristate "Spase sp887x based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -76,24 +103,28 @@ config DVB_SP887X
config DVB_CX22700
tristate "Conexant CX22700 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_CX22702
tristate "Conexant cx22702 demodulator (OFDM)"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_L64781
tristate "LSI L64781"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_TDA1004X
tristate "Philips TDA10045H/TDA10046H based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
A DVB-T tuner module. Say Y when you want to support this frontend.
@@ -107,24 +138,28 @@ config DVB_TDA1004X
config DVB_NXT6000
tristate "NxtWave Communications NXT6000 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_MT352
tristate "Zarlink MT352 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_ZL10353
tristate "Zarlink ZL10353 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Say Y when you want to support this frontend.
config DVB_DIB3000MB
tristate "DiBcom 3000M-B"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
@@ -132,6 +167,7 @@ config DVB_DIB3000MB
config DVB_DIB3000MC
tristate "DiBcom 3000P/M-C"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-T tuner module. Designed for mobile usage. Say Y when you want
to support this frontend.
@@ -142,18 +178,21 @@ comment "DVB-C (cable) frontends"
config DVB_VES1820
tristate "VLSI VES1820 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
config DVB_TDA10021
tristate "Philips TDA10021 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
config DVB_STV0297
tristate "ST STV0297 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
A DVB-C tuner module. Say Y when you want to support this frontend.
@@ -163,6 +202,7 @@ comment "ATSC (North American/Korean Terrestrial/Cable DTV) frontends"
config DVB_NXT200X
tristate "NxtWave Communications NXT2002/NXT2004 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
@@ -177,6 +217,7 @@ config DVB_NXT200X
config DVB_OR51211
tristate "Oren OR51211 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB tuner module. Say Y when you want to support this frontend.
@@ -189,6 +230,7 @@ config DVB_OR51211
config DVB_OR51132
tristate "Oren OR51132 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
@@ -204,6 +246,7 @@ config DVB_OR51132
config DVB_BCM3510
tristate "Broadcom BCM3510"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
select FW_LOADER
help
An ATSC 8VSB/16VSB and QAM64/256 tuner module. Say Y when you want to
@@ -212,28 +255,52 @@ config DVB_BCM3510
config DVB_LGDT330X
tristate "LG Electronics LGDT3302/LGDT3303 based"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An ATSC 8VSB and QAM64/256 tuner module. Say Y when you want
to support this frontend.
-
-comment "Miscellaneous devices"
+comment "Tuners/PLL support"
depends on DVB_CORE
config DVB_PLL
tristate
depends on DVB_CORE && I2C
+config DVB_TDA826X
+ tristate "Philips TDA826X silicon tuner"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVB-S silicon tuner module. Say Y when you want to support this tuner.
+
+config DVB_TUNER_MT2060
+ tristate "Microtune MT2060 silicon IF tuner"
+ help
+ A driver for the silicon IF tuner MT2060 from Microtune.
+
+comment "Miscellaneous devices"
+ depends on DVB_CORE
+
config DVB_LNBP21
tristate "LNBP21 SEC controller"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An SEC control chip.
config DVB_ISL6421
tristate "ISL6421 SEC controller"
depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
help
An SEC control chip.
+config DVB_TUA6100
+ tristate "TUA6100 PLL"
+ depends on DVB_CORE && I2C
+ default m if DVB_FE_CUSTOMISE
+ help
+ A DVBS PLL chip.
+
endmenu
diff --git a/drivers/media/dvb/frontends/Makefile b/drivers/media/dvb/frontends/Makefile
index 0e4880b6db1..dce9cf0c75c 100644
--- a/drivers/media/dvb/frontends/Makefile
+++ b/drivers/media/dvb/frontends/Makefile
@@ -11,8 +11,8 @@ obj-$(CONFIG_DVB_CX22700) += cx22700.o
obj-$(CONFIG_DVB_CX24110) += cx24110.o
obj-$(CONFIG_DVB_TDA8083) += tda8083.o
obj-$(CONFIG_DVB_L64781) += l64781.o
-obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o dib3000-common.o
-obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dib3000-common.o
+obj-$(CONFIG_DVB_DIB3000MB) += dib3000mb.o
+obj-$(CONFIG_DVB_DIB3000MC) += dib3000mc.o dibx000_common.o
obj-$(CONFIG_DVB_MT312) += mt312.o
obj-$(CONFIG_DVB_VES1820) += ves1820.o
obj-$(CONFIG_DVB_VES1X93) += ves1x93.o
@@ -33,3 +33,7 @@ obj-$(CONFIG_DVB_LGDT330X) += lgdt330x.o
obj-$(CONFIG_DVB_CX24123) += cx24123.o
obj-$(CONFIG_DVB_LNBP21) += lnbp21.o
obj-$(CONFIG_DVB_ISL6421) += isl6421.o
+obj-$(CONFIG_DVB_TDA10086) += tda10086.o
+obj-$(CONFIG_DVB_TDA826X) += tda826x.o
+obj-$(CONFIG_DVB_TUNER_MT2060) += mt2060.o
+obj-$(CONFIG_DVB_TUA6100) += tua6100.o
diff --git a/drivers/media/dvb/frontends/bcm3510.h b/drivers/media/dvb/frontends/bcm3510.h
index 80f5d0953d0..6dfa839a702 100644
--- a/drivers/media/dvb/frontends/bcm3510.h
+++ b/drivers/media/dvb/frontends/bcm3510.h
@@ -34,7 +34,16 @@ struct bcm3510_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_BCM3510) || defined(CONFIG_DVB_BCM3510_MODULE)
extern struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* bcm3510_attach(const struct bcm3510_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_BCM3510
#endif
diff --git a/drivers/media/dvb/frontends/cx22700.h b/drivers/media/dvb/frontends/cx22700.h
index dcd8979c1a1..10286cc29fb 100644
--- a/drivers/media/dvb/frontends/cx22700.h
+++ b/drivers/media/dvb/frontends/cx22700.h
@@ -31,7 +31,16 @@ struct cx22700_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_CX22700) || defined(CONFIG_DVB_CX22700_MODULE)
extern struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx22700_attach(const struct cx22700_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX22700
#endif // CX22700_H
diff --git a/drivers/media/dvb/frontends/cx22702.c b/drivers/media/dvb/frontends/cx22702.c
index 4106d46c957..335219ebce2 100644
--- a/drivers/media/dvb/frontends/cx22702.c
+++ b/drivers/media/dvb/frontends/cx22702.c
@@ -399,7 +399,9 @@ static int cx22702_read_signal_strength(struct dvb_frontend* fe, u16* signal_str
{
struct cx22702_state* state = fe->demodulator_priv;
- *signal_strength = cx22702_readreg (state, 0x23);
+ u16 rs_ber = 0;
+ rs_ber = cx22702_readreg (state, 0x23);
+ *signal_strength = (rs_ber << 8) | rs_ber;
return 0;
}
diff --git a/drivers/media/dvb/frontends/cx22702.h b/drivers/media/dvb/frontends/cx22702.h
index 7f2f241e5d4..bc217ddf02c 100644
--- a/drivers/media/dvb/frontends/cx22702.h
+++ b/drivers/media/dvb/frontends/cx22702.h
@@ -41,7 +41,16 @@ struct cx22702_config
u8 output_mode;
};
+#if defined(CONFIG_DVB_CX22702) || defined(CONFIG_DVB_CX22702_MODULE)
extern struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx22702_attach(const struct cx22702_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX22702
#endif // CX22702_H
diff --git a/drivers/media/dvb/frontends/cx24110.c b/drivers/media/dvb/frontends/cx24110.c
index ce3c7398bac..ae96395217a 100644
--- a/drivers/media/dvb/frontends/cx24110.c
+++ b/drivers/media/dvb/frontends/cx24110.c
@@ -1,4 +1,4 @@
-/*
+ /*
cx24110 - Single Chip Satellite Channel Receiver driver module
Copyright (C) 2002 Peter Hettkamp <peter.hettkamp@htp-tel.de> based on
@@ -311,16 +311,17 @@ static int cx24110_set_symbolrate (struct cx24110_state* state, u32 srate)
}
-int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
+static int _cx24110_pll_write (struct dvb_frontend* fe, u8 *buf, int len)
{
struct cx24110_state *state = fe->demodulator_priv;
+ if (len != 3)
+ return -EINVAL;
+
/* tuner data is 21 bits long, must be left-aligned in data */
/* tuner cx24108 is written through a dedicated 3wire interface on the demod chip */
/* FIXME (low): add error handling, avoid infinite loops if HW fails... */
- dprintk("cx24110 debug: cx24108_write(%8.8x)\n",data);
-
cx24110_writereg(state,0x6d,0x30); /* auto mode at 62kHz */
cx24110_writereg(state,0x70,0x15); /* auto mode 21 bits */
@@ -329,19 +330,19 @@ int cx24110_pll_write (struct dvb_frontend* fe, u32 data)
cx24110_writereg(state,0x72,0);
/* write the topmost 8 bits */
- cx24110_writereg(state,0x72,(data>>24)&0xff);
+ cx24110_writereg(state,0x72,buf[0]);
/* wait for the send to be completed */
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
/* send another 8 bytes */
- cx24110_writereg(state,0x72,(data>>16)&0xff);
+ cx24110_writereg(state,0x72,buf[1]);
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
/* and the topmost 5 bits of this byte */
- cx24110_writereg(state,0x72,(data>>8)&0xff);
+ cx24110_writereg(state,0x72,buf[2]);
while ((cx24110_readreg(state,0x6d)&0xc0)==0x80)
;
@@ -642,6 +643,7 @@ static struct dvb_frontend_ops cx24110_ops = {
.release = cx24110_release,
.init = cx24110_initfe,
+ .write = _cx24110_pll_write,
.set_frontend = cx24110_set_frontend,
.get_frontend = cx24110_get_frontend,
.read_status = cx24110_read_status,
@@ -664,4 +666,3 @@ MODULE_AUTHOR("Peter Hettkamp");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(cx24110_attach);
-EXPORT_SYMBOL(cx24110_pll_write);
diff --git a/drivers/media/dvb/frontends/cx24110.h b/drivers/media/dvb/frontends/cx24110.h
index b354a64e0e7..c9d5ae250eb 100644
--- a/drivers/media/dvb/frontends/cx24110.h
+++ b/drivers/media/dvb/frontends/cx24110.h
@@ -33,9 +33,24 @@ struct cx24110_config
u8 demod_address;
};
+static inline int cx24110_pll_write(struct dvb_frontend *fe, u32 val) {
+ int r = 0;
+ u8 buf[] = {(u8) (val>>24), (u8) (val>>16), (u8) (val>>8)};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 3);
+ return r;
+}
+
+#if defined(CONFIG_DVB_CX24110) || defined(CONFIG_DVB_CX24110_MODULE)
extern struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
struct i2c_adapter* i2c);
-
-extern int cx24110_pll_write(struct dvb_frontend* fe, u32 data);
+#else
+static inline struct dvb_frontend* cx24110_attach(const struct cx24110_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24110
#endif // CX24110_H
diff --git a/drivers/media/dvb/frontends/cx24123.c b/drivers/media/dvb/frontends/cx24123.c
index 274a87b7a5d..62d69a6ea69 100644
--- a/drivers/media/dvb/frontends/cx24123.c
+++ b/drivers/media/dvb/frontends/cx24123.c
@@ -45,9 +45,6 @@ struct cx24123_state
struct dvb_frontend frontend;
- u32 lastber;
- u16 snr;
-
/* Some PLL specifics for tuning */
u32 VCAarg;
u32 VGAarg;
@@ -194,7 +191,7 @@ static struct {
{0x06, 0x31}, /* MPEG (default) */
{0x0b, 0x00}, /* Freq search start point (default) */
{0x0c, 0x00}, /* Demodulator sample gain (default) */
- {0x0d, 0x02}, /* Frequency search range = Fsymbol / 4 (default) */
+ {0x0d, 0x7f}, /* Force driver to shift until the maximum (+-10 MHz) */
{0x0e, 0x03}, /* Default non-inverted, FEC 3/4 (default) */
{0x0f, 0xfe}, /* FEC search mask (all supported codes) */
{0x10, 0x01}, /* Default search inversion, no repeat (default) */
@@ -223,8 +220,9 @@ static struct {
{0x44, 0x00}, /* Constellation (default) */
{0x45, 0x00}, /* Symbol count (default) */
{0x46, 0x0d}, /* Symbol rate estimator on (default) */
- {0x56, 0x41}, /* Various (default) */
+ {0x56, 0xc1}, /* Error Counter = Viterbi BER */
{0x57, 0xff}, /* Error Counter Window (default) */
+ {0x5c, 0x20}, /* Acquisition AFC Expiration window (default is 0x10) */
{0x67, 0x83}, /* Non-DCII symbol clock */
};
@@ -321,6 +319,12 @@ static int cx24123_set_fec(struct cx24123_state* state, fe_code_rate_t fec)
if ( (fec < FEC_NONE) || (fec > FEC_AUTO) )
fec = FEC_AUTO;
+ /* Set the soft decision threshold */
+ if(fec == FEC_1_2)
+ cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) | 0x01);
+ else
+ cx24123_writereg(state, 0x43, cx24123_readreg(state, 0x43) & ~0x01);
+
switch (fec) {
case FEC_1_2:
dprintk("%s: set FEC to 1/2\n",__FUNCTION__);
@@ -657,6 +661,10 @@ static int cx24123_initfe(struct dvb_frontend* fe)
for (i = 0; i < sizeof(cx24123_regdata) / sizeof(cx24123_regdata[0]); i++)
cx24123_writereg(state, cx24123_regdata[i].reg, cx24123_regdata[i].data);
+ /* Set the LNB polarity */
+ if(state->config->lnb_polarity)
+ cx24123_writereg(state, 0x32, cx24123_readreg(state, 0x32) | 0x02);
+
return 0;
}
@@ -674,6 +682,9 @@ static int cx24123_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage
case SEC_VOLTAGE_18:
dprintk("%s: setting voltage 18V\n", __FUNCTION__);
return cx24123_writereg(state, 0x29, val | 0x80);
+ case SEC_VOLTAGE_OFF:
+ /* already handled in cx88-dvb */
+ return 0;
default:
return -EINVAL;
};
@@ -776,13 +787,15 @@ static int cx24123_read_status(struct dvb_frontend* fe, fe_status_t* status)
if (lock & 0x01)
*status |= FE_HAS_SIGNAL;
if (sync & 0x02)
- *status |= FE_HAS_CARRIER;
+ *status |= FE_HAS_CARRIER; /* Phase locked */
if (sync & 0x04)
*status |= FE_HAS_VITERBI;
+
+ /* Reed-Solomon Status */
if (sync & 0x08)
*status |= FE_HAS_SYNC;
if (sync & 0x80)
- *status |= FE_HAS_LOCK;
+ *status |= FE_HAS_LOCK; /*Full Sync */
return 0;
}
@@ -795,29 +808,13 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
{
struct cx24123_state *state = fe->demodulator_priv;
- state->lastber =
- ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
+ /* The true bit error rate is this value divided by
+ the window size (set as 256 * 255) */
+ *ber = ((cx24123_readreg(state, 0x1c) & 0x3f) << 16) |
(cx24123_readreg(state, 0x1d) << 8 |
- cx24123_readreg(state, 0x1e));
-
- /* Do the signal quality processing here, it's derived from the BER. */
- /* Scale the BER from a 24bit to a SNR 16 bit where higher = better */
- if (state->lastber < 5000)
- state->snr = 655*100;
- else if ( (state->lastber >= 5000) && (state->lastber < 55000) )
- state->snr = 655*90;
- else if ( (state->lastber >= 55000) && (state->lastber < 150000) )
- state->snr = 655*80;
- else if ( (state->lastber >= 150000) && (state->lastber < 250000) )
- state->snr = 655*70;
- else if ( (state->lastber >= 250000) && (state->lastber < 450000) )
- state->snr = 655*65;
- else
- state->snr = 0;
-
- dprintk("%s: BER = %d, S/N index = %d\n",__FUNCTION__,state->lastber, state->snr);
+ cx24123_readreg(state, 0x1e));
- *ber = state->lastber;
+ dprintk("%s: BER = %d\n",__FUNCTION__,*ber);
return 0;
}
@@ -825,6 +822,7 @@ static int cx24123_read_ber(struct dvb_frontend* fe, u32* ber)
static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_strength)
{
struct cx24123_state *state = fe->demodulator_priv;
+
*signal_strength = cx24123_readreg(state, 0x3b) << 8; /* larger = better */
dprintk("%s: Signal strength = %d\n",__FUNCTION__,*signal_strength);
@@ -835,19 +833,13 @@ static int cx24123_read_signal_strength(struct dvb_frontend* fe, u16* signal_str
static int cx24123_read_snr(struct dvb_frontend* fe, u16* snr)
{
struct cx24123_state *state = fe->demodulator_priv;
- *snr = state->snr;
-
- dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr);
- return 0;
-}
+ /* Inverted raw Es/N0 count, totally bogus but better than the
+ BER threshold. */
+ *snr = 65535 - (((u16)cx24123_readreg(state, 0x18) << 8) |
+ (u16)cx24123_readreg(state, 0x19));
-static int cx24123_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
-{
- struct cx24123_state *state = fe->demodulator_priv;
- *ucblocks = state->lastber;
-
- dprintk("%s: ucblocks (ber) = %d\n",__FUNCTION__,*ucblocks);
+ dprintk("%s: read S/N index = %d\n",__FUNCTION__,*snr);
return 0;
}
@@ -922,6 +914,29 @@ static int cx24123_set_tone(struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
return 0;
}
+static int cx24123_tune(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters* params,
+ unsigned int mode_flags,
+ int *delay,
+ fe_status_t *status)
+{
+ int retval = 0;
+
+ if (params != NULL)
+ retval = cx24123_set_frontend(fe, params);
+
+ if (!(mode_flags & FE_TUNE_MODE_ONESHOT))
+ cx24123_read_status(fe, status);
+ *delay = HZ/10;
+
+ return retval;
+}
+
+static int cx24123_get_algo(struct dvb_frontend *fe)
+{
+ return 1; //FE_ALGO_HW
+}
+
static void cx24123_release(struct dvb_frontend* fe)
{
struct cx24123_state* state = fe->demodulator_priv;
@@ -949,8 +964,6 @@ struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
/* setup the state */
state->config = config;
state->i2c = i2c;
- state->lastber = 0;
- state->snr = 0;
state->VCAarg = 0;
state->VGAarg = 0;
state->bandselectarg = 0;
@@ -1003,11 +1016,12 @@ static struct dvb_frontend_ops cx24123_ops = {
.read_ber = cx24123_read_ber,
.read_signal_strength = cx24123_read_signal_strength,
.read_snr = cx24123_read_snr,
- .read_ucblocks = cx24123_read_ucblocks,
.diseqc_send_master_cmd = cx24123_send_diseqc_msg,
.diseqc_send_burst = cx24123_diseqc_send_burst,
.set_tone = cx24123_set_tone,
.set_voltage = cx24123_set_voltage,
+ .tune = cx24123_tune,
+ .get_frontend_algo = cx24123_get_algo,
};
module_param(debug, int, 0644);
diff --git a/drivers/media/dvb/frontends/cx24123.h b/drivers/media/dvb/frontends/cx24123.h
index 9606f825935..57a1dae1dc4 100644
--- a/drivers/media/dvb/frontends/cx24123.h
+++ b/drivers/media/dvb/frontends/cx24123.h
@@ -30,9 +30,21 @@ struct cx24123_config
/* Need to set device param for start_dma */
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
+
+ /* 0 = LNB voltage normal, 1 = LNB voltage inverted */
+ int lnb_polarity;
};
+#if defined(CONFIG_DVB_CX24123) || defined(CONFIG_DVB_CX24123_MODULE)
extern struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* cx24123_attach(const struct cx24123_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_CX24123
#endif /* CX24123_H */
diff --git a/drivers/media/dvb/frontends/dib3000-common.c b/drivers/media/dvb/frontends/dib3000-common.c
deleted file mode 100644
index 1a4f1f7c228..00000000000
--- a/drivers/media/dvb/frontends/dib3000-common.c
+++ /dev/null
@@ -1,83 +0,0 @@
-#include "dib3000-common.h"
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
-static int debug;
-module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
-#endif
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_i2c(args...) dprintk(0x02,args)
-#define deb_srch(args...) dprintk(0x04,args)
-
-
-int dib3000_read_reg(struct dib3000_state *state, u16 reg)
-{
- u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
- u8 rb[2];
- struct i2c_msg msg[] = {
- { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
- { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
- };
-
- if (i2c_transfer(state->i2c, msg, 2) != 2)
- deb_i2c("i2c read error\n");
-
- deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
- (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
-
- return (rb[0] << 8) | rb[1];
-}
-
-int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
-{
- u8 b[] = {
- (reg >> 8) & 0xff, reg & 0xff,
- (val >> 8) & 0xff, val & 0xff,
- };
- struct i2c_msg msg[] = {
- { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
- };
- deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
-
- return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
-}
-
-int dib3000_search_status(u16 irq,u16 lock)
-{
- if (irq & 0x02) {
- if (lock & 0x01) {
- deb_srch("auto search succeeded\n");
- return 1; // auto search succeeded
- } else {
- deb_srch("auto search not successful\n");
- return 0; // auto search failed
- }
- } else if (irq & 0x01) {
- deb_srch("auto search failed\n");
- return 0; // auto search failed
- }
- return -1; // try again
-}
-
-/* for auto search */
-u16 dib3000_seq[2][2][2] = /* fft,gua, inv */
- { /* fft */
- { /* gua */
- { 0, 1 }, /* 0 0 { 0,1 } */
- { 3, 9 }, /* 0 1 { 0,1 } */
- },
- {
- { 2, 5 }, /* 1 0 { 0,1 } */
- { 6, 11 }, /* 1 1 { 0,1 } */
- }
- };
-
-MODULE_AUTHOR("Patrick Boettcher <patrick.boettcher@desy.de");
-MODULE_DESCRIPTION("Common functions for the dib3000mb/dib3000mc dvb frontend drivers");
-MODULE_LICENSE("GPL");
-
-EXPORT_SYMBOL(dib3000_seq);
-
-EXPORT_SYMBOL(dib3000_read_reg);
-EXPORT_SYMBOL(dib3000_write_reg);
-EXPORT_SYMBOL(dib3000_search_status);
diff --git a/drivers/media/dvb/frontends/dib3000-common.h b/drivers/media/dvb/frontends/dib3000-common.h
deleted file mode 100644
index be1c0d3e138..00000000000
--- a/drivers/media/dvb/frontends/dib3000-common.h
+++ /dev/null
@@ -1,135 +0,0 @@
-/*
- * .h-files for the common use of the frontend drivers made by DiBcom
- * DiBcom 3000M-B/C, 3000P
- *
- * DiBcom (http://www.dibcom.fr/)
- *
- * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- * based on GPL code from DibCom, which has
- *
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.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, version 2.
- *
- * Acknowledgements
- *
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
- * sources, on which this driver (and the dvb-dibusb) are based.
- *
- * see Documentation/dvb/README.dibusb for more information
- *
- */
-
-#ifndef DIB3000_COMMON_H
-#define DIB3000_COMMON_H
-
-#include "dvb_frontend.h"
-#include "dib3000.h"
-
-/* info and err, taken from usb.h, if there is anything available like by default. */
-#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg)
-#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg)
-#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
-
-/* frontend state */
-struct dib3000_state {
- struct i2c_adapter* i2c;
-
-/* configuration settings */
- struct dib3000_config config;
-
- struct dvb_frontend frontend;
- int timing_offset;
- int timing_offset_comp_done;
-
- fe_bandwidth_t last_tuned_bw;
- u32 last_tuned_freq;
-};
-
-/* commonly used methods by the dib3000mb/mc/p frontend */
-extern int dib3000_read_reg(struct dib3000_state *state, u16 reg);
-extern int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val);
-
-extern int dib3000_search_status(u16 irq,u16 lock);
-
-/* handy shortcuts */
-#define rd(reg) dib3000_read_reg(state,reg)
-
-#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
- { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
-
-#define wr_foreach(a,v) { int i; \
- if (sizeof(a) != sizeof(v)) \
- err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
- for (i=0; i < sizeof(a)/sizeof(u16); i++) \
- wr(a[i],v[i]); \
- }
-
-#define set_or(reg,val) wr(reg,rd(reg) | val)
-
-#define set_and(reg,val) wr(reg,rd(reg) & val)
-
-
-/* debug */
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
-#define dprintk(level,args...) \
- do { if ((debug & level)) { printk(args); } } while (0)
-#else
-#define dprintk(args...) do { } while (0)
-#endif
-
-/* mask for enabling a specific pid for the pid_filter */
-#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
-
-/* common values for tuning */
-#define DIB3000_ALPHA_0 ( 0)
-#define DIB3000_ALPHA_1 ( 1)
-#define DIB3000_ALPHA_2 ( 2)
-#define DIB3000_ALPHA_4 ( 4)
-
-#define DIB3000_CONSTELLATION_QPSK ( 0)
-#define DIB3000_CONSTELLATION_16QAM ( 1)
-#define DIB3000_CONSTELLATION_64QAM ( 2)
-
-#define DIB3000_GUARD_TIME_1_32 ( 0)
-#define DIB3000_GUARD_TIME_1_16 ( 1)
-#define DIB3000_GUARD_TIME_1_8 ( 2)
-#define DIB3000_GUARD_TIME_1_4 ( 3)
-
-#define DIB3000_TRANSMISSION_MODE_2K ( 0)
-#define DIB3000_TRANSMISSION_MODE_8K ( 1)
-
-#define DIB3000_SELECT_LP ( 0)
-#define DIB3000_SELECT_HP ( 1)
-
-#define DIB3000_FEC_1_2 ( 1)
-#define DIB3000_FEC_2_3 ( 2)
-#define DIB3000_FEC_3_4 ( 3)
-#define DIB3000_FEC_5_6 ( 5)
-#define DIB3000_FEC_7_8 ( 7)
-
-#define DIB3000_HRCH_OFF ( 0)
-#define DIB3000_HRCH_ON ( 1)
-
-#define DIB3000_DDS_INVERSION_OFF ( 0)
-#define DIB3000_DDS_INVERSION_ON ( 1)
-
-#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8))
-#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7)))
-
-/* for auto search */
-extern u16 dib3000_seq[2][2][2];
-
-#define DIB3000_REG_MANUFACTOR_ID ( 1025)
-#define DIB3000_I2C_ID_DIBCOM (0x01b3)
-
-#define DIB3000_REG_DEVICE_ID ( 1026)
-#define DIB3000MB_DEVICE_ID (0x3000)
-#define DIB3000MC_DEVICE_ID (0x3001)
-#define DIB3000P_DEVICE_ID (0x3002)
-
-#endif // DIB3000_COMMON_H
diff --git a/drivers/media/dvb/frontends/dib3000.h b/drivers/media/dvb/frontends/dib3000.h
index ec927628d27..0caac3f0f27 100644
--- a/drivers/media/dvb/frontends/dib3000.h
+++ b/drivers/media/dvb/frontends/dib3000.h
@@ -41,9 +41,16 @@ struct dib_fe_xfer_ops
int (*tuner_pass_ctrl)(struct dvb_frontend *fe, int onoff, u8 pll_ctrl);
};
+#if defined(CONFIG_DVB_DIB3000MB) || defined(CONFIG_DVB_DIB3000MB_MODULE)
extern struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
+#else
+static inline struct dvb_frontend* dib3000mb_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_DIB3000MB
-extern struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops);
#endif // DIB3000_H
diff --git a/drivers/media/dvb/frontends/dib3000mb.c b/drivers/media/dvb/frontends/dib3000mb.c
index 5302e11883a..adbabfdb04a 100644
--- a/drivers/media/dvb/frontends/dib3000mb.c
+++ b/drivers/media/dvb/frontends/dib3000mb.c
@@ -29,9 +29,10 @@
#include <linux/string.h>
#include <linux/slab.h>
-#include "dib3000-common.h"
-#include "dib3000mb_priv.h"
+#include "dvb_frontend.h"
+
#include "dib3000.h"
+#include "dib3000mb_priv.h"
/* Version information */
#define DRIVER_VERSION "0.1"
@@ -44,10 +45,81 @@ module_param(debug, int, 0644);
MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe (|-able)).");
#endif
#define deb_info(args...) dprintk(0x01,args)
+#define deb_i2c(args...) dprintk(0x02,args)
+#define deb_srch(args...) dprintk(0x04,args)
+#define deb_info(args...) dprintk(0x01,args)
#define deb_xfer(args...) dprintk(0x02,args)
#define deb_setf(args...) dprintk(0x04,args)
#define deb_getf(args...) dprintk(0x08,args)
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "set debugging level (1=info,2=i2c,4=srch (|-able)).");
+#endif
+
+static int dib3000_read_reg(struct dib3000_state *state, u16 reg)
+{
+ u8 wb[] = { ((reg >> 8) | 0x80) & 0xff, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[] = {
+ { .addr = state->config.demod_address, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->config.demod_address, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c, msg, 2) != 2)
+ deb_i2c("i2c read error\n");
+
+ deb_i2c("reading i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,
+ (rb[0] << 8) | rb[1],(rb[0] << 8) | rb[1]);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib3000_write_reg(struct dib3000_state *state, u16 reg, u16 val)
+{
+ u8 b[] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg[] = {
+ { .addr = state->config.demod_address, .flags = 0, .buf = b, .len = 4 }
+ };
+ deb_i2c("writing i2c bus (reg: %5d 0x%04x, val: %5d 0x%04x)\n",reg,reg,val,val);
+
+ return i2c_transfer(state->i2c,msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+static int dib3000_search_status(u16 irq,u16 lock)
+{
+ if (irq & 0x02) {
+ if (lock & 0x01) {
+ deb_srch("auto search succeeded\n");
+ return 1; // auto search succeeded
+ } else {
+ deb_srch("auto search not successful\n");
+ return 0; // auto search failed
+ }
+ } else if (irq & 0x01) {
+ deb_srch("auto search failed\n");
+ return 0; // auto search failed
+ }
+ return -1; // try again
+}
+
+/* for auto search */
+static u16 dib3000_seq[2][2][2] = /* fft,gua, inv */
+ { /* fft */
+ { /* gua */
+ { 0, 1 }, /* 0 0 { 0,1 } */
+ { 3, 9 }, /* 0 1 { 0,1 } */
+ },
+ {
+ { 2, 5 }, /* 1 0 { 0,1 } */
+ { 6, 11 }, /* 1 1 { 0,1 } */
+ }
+ };
+
static int dib3000mb_get_frontend(struct dvb_frontend* fe,
struct dvb_frontend_parameters *fep);
diff --git a/drivers/media/dvb/frontends/dib3000mb_priv.h b/drivers/media/dvb/frontends/dib3000mb_priv.h
index 999b1904781..1a12747fdc9 100644
--- a/drivers/media/dvb/frontends/dib3000mb_priv.h
+++ b/drivers/media/dvb/frontends/dib3000mb_priv.h
@@ -13,6 +13,99 @@
#ifndef __DIB3000MB_PRIV_H_INCLUDED__
#define __DIB3000MB_PRIV_H_INCLUDED__
+/* info and err, taken from usb.h, if there is anything available like by default. */
+#define err(format, arg...) printk(KERN_ERR "dib3000: " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO "dib3000: " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "dib3000: " format "\n" , ## arg)
+
+/* handy shortcuts */
+#define rd(reg) dib3000_read_reg(state,reg)
+
+#define wr(reg,val) if (dib3000_write_reg(state,reg,val)) \
+ { err("while sending 0x%04x to 0x%04x.",val,reg); return -EREMOTEIO; }
+
+#define wr_foreach(a,v) { int i; \
+ if (sizeof(a) != sizeof(v)) \
+ err("sizeof: %zu %zu is different",sizeof(a),sizeof(v));\
+ for (i=0; i < sizeof(a)/sizeof(u16); i++) \
+ wr(a[i],v[i]); \
+ }
+
+#define set_or(reg,val) wr(reg,rd(reg) | val)
+
+#define set_and(reg,val) wr(reg,rd(reg) & val)
+
+/* debug */
+
+#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#define dprintk(level,args...) \
+ do { if ((debug & level)) { printk(args); } } while (0)
+#else
+#define dprintk(args...) do { } while (0)
+#endif
+
+/* mask for enabling a specific pid for the pid_filter */
+#define DIB3000_ACTIVATE_PID_FILTERING (0x2000)
+
+/* common values for tuning */
+#define DIB3000_ALPHA_0 ( 0)
+#define DIB3000_ALPHA_1 ( 1)
+#define DIB3000_ALPHA_2 ( 2)
+#define DIB3000_ALPHA_4 ( 4)
+
+#define DIB3000_CONSTELLATION_QPSK ( 0)
+#define DIB3000_CONSTELLATION_16QAM ( 1)
+#define DIB3000_CONSTELLATION_64QAM ( 2)
+
+#define DIB3000_GUARD_TIME_1_32 ( 0)
+#define DIB3000_GUARD_TIME_1_16 ( 1)
+#define DIB3000_GUARD_TIME_1_8 ( 2)
+#define DIB3000_GUARD_TIME_1_4 ( 3)
+
+#define DIB3000_TRANSMISSION_MODE_2K ( 0)
+#define DIB3000_TRANSMISSION_MODE_8K ( 1)
+
+#define DIB3000_SELECT_LP ( 0)
+#define DIB3000_SELECT_HP ( 1)
+
+#define DIB3000_FEC_1_2 ( 1)
+#define DIB3000_FEC_2_3 ( 2)
+#define DIB3000_FEC_3_4 ( 3)
+#define DIB3000_FEC_5_6 ( 5)
+#define DIB3000_FEC_7_8 ( 7)
+
+#define DIB3000_HRCH_OFF ( 0)
+#define DIB3000_HRCH_ON ( 1)
+
+#define DIB3000_DDS_INVERSION_OFF ( 0)
+#define DIB3000_DDS_INVERSION_ON ( 1)
+
+#define DIB3000_TUNER_WRITE_ENABLE(a) (0xffff & (a << 8))
+#define DIB3000_TUNER_WRITE_DISABLE(a) (0xffff & ((a << 8) | (1 << 7)))
+
+#define DIB3000_REG_MANUFACTOR_ID ( 1025)
+#define DIB3000_I2C_ID_DIBCOM (0x01b3)
+
+#define DIB3000_REG_DEVICE_ID ( 1026)
+#define DIB3000MB_DEVICE_ID (0x3000)
+#define DIB3000MC_DEVICE_ID (0x3001)
+#define DIB3000P_DEVICE_ID (0x3002)
+
+/* frontend state */
+struct dib3000_state {
+ struct i2c_adapter* i2c;
+
+/* configuration settings */
+ struct dib3000_config config;
+
+ struct dvb_frontend frontend;
+ int timing_offset;
+ int timing_offset_comp_done;
+
+ fe_bandwidth_t last_tuned_bw;
+ u32 last_tuned_freq;
+};
+
/* register addresses and some of their default values */
/* restart subsystems */
diff --git a/drivers/media/dvb/frontends/dib3000mc.c b/drivers/media/dvb/frontends/dib3000mc.c
index 98673474a14..cc28417fa33 100644
--- a/drivers/media/dvb/frontends/dib3000mc.c
+++ b/drivers/media/dvb/frontends/dib3000mc.c
@@ -1,913 +1,921 @@
/*
- * Frontend driver for mobile DVB-T demodulator DiBcom 3000P/M-C
- * DiBcom (http://www.dibcom.fr/)
+ * Driver for DiBcom DiB3000MC/P-demodulator.
*
+ * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
* Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher@desy.de)
*
- * based on GPL code from DiBCom, which has
+ * This code is partially based on the previous dib3000mc.c .
*
- * Copyright (C) 2004 Amaury Demol for DiBcom (ademol@dibcom.fr)
- *
- * This program is free software; you can redistribute it and/or
+ * 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, version 2.
- *
- * Acknowledgements
- *
- * Amaury Demol (ademol@dibcom.fr) from DiBcom for providing specs and driver
- * sources, on which this driver (and the dvb-dibusb) are based.
- *
- * see Documentation/dvb/README.dibusb for more information
- *
*/
+
#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/string.h>
-#include <linux/slab.h>
-
-#include "dib3000-common.h"
-#include "dib3000mc_priv.h"
-#include "dib3000.h"
-
-/* Version information */
-#define DRIVER_VERSION "0.1"
-#define DRIVER_DESC "DiBcom 3000M-C DVB-T demodulator"
-#define DRIVER_AUTHOR "Patrick Boettcher, patrick.boettcher@desy.de"
-
-#ifdef CONFIG_DVB_DIBCOM_DEBUG
+#include <linux/i2c.h>
+//#include <linux/init.h>
+//#include <linux/delay.h>
+//#include <linux/string.h>
+//#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+
+#include "dib3000mc.h"
+
static int debug;
module_param(debug, int, 0644);
-MODULE_PARM_DESC(debug, "set debugging level (1=info,2=xfer,4=setfe,8=getfe,16=stat (|-able)).");
-#endif
-#define deb_info(args...) dprintk(0x01,args)
-#define deb_xfer(args...) dprintk(0x02,args)
-#define deb_setf(args...) dprintk(0x04,args)
-#define deb_getf(args...) dprintk(0x08,args)
-#define deb_stat(args...) dprintk(0x10,args)
-
-static int dib3000mc_set_impulse_noise(struct dib3000_state * state, int mode,
- fe_transmit_mode_t transmission_mode, fe_bandwidth_t bandwidth)
-{
- switch (transmission_mode) {
- case TRANSMISSION_MODE_2K:
- wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[0]);
- break;
- case TRANSMISSION_MODE_8K:
- wr_foreach(dib3000mc_reg_fft,dib3000mc_fft_modes[1]);
- break;
- default:
- break;
- }
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
- switch (bandwidth) {
-/* case BANDWIDTH_5_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[0]);
- break; */
- case BANDWIDTH_6_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[1]);
- break;
- case BANDWIDTH_7_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[2]);
- break;
- case BANDWIDTH_8_MHZ:
- wr_foreach(dib3000mc_reg_impulse_noise,dib3000mc_impluse_noise[3]);
- break;
- default:
- break;
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiB3000MC/P:"); printk(args); } } while (0)
+
+struct dib3000mc_state {
+ struct dvb_frontend demod;
+ struct dib3000mc_config *cfg;
+
+ u8 i2c_addr;
+ struct i2c_adapter *i2c_adap;
+
+ struct dibx000_i2c_master i2c_master;
+
+ fe_bandwidth_t current_bandwidth;
+
+ u16 dev_id;
+};
+
+static u16 dib3000mc_read_word(struct dib3000mc_state *state, u16 reg)
+{
+ u8 wb[2] = { (reg >> 8) | 0x80, reg & 0xff };
+ u8 rb[2];
+ struct i2c_msg msg[2] = {
+ { .addr = state->i2c_addr >> 1, .flags = 0, .buf = wb, .len = 2 },
+ { .addr = state->i2c_addr >> 1, .flags = I2C_M_RD, .buf = rb, .len = 2 },
+ };
+
+ if (i2c_transfer(state->i2c_adap, msg, 2) != 2)
+ dprintk("i2c read error on %d\n",reg);
+
+ return (rb[0] << 8) | rb[1];
+}
+
+static int dib3000mc_write_word(struct dib3000mc_state *state, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = state->i2c_addr >> 1, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(state->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+
+static int dib3000mc_identify(struct dib3000mc_state *state)
+{
+ u16 value;
+ if ((value = dib3000mc_read_word(state, 1025)) != 0x01b3) {
+ dprintk("-E- DiB3000MC/P: wrong Vendor ID (read=0x%x)\n",value);
+ return -EREMOTEIO;
}
- switch (mode) {
- case 0: /* no impulse */ /* fall through */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[0]);
- break;
- case 1: /* new algo */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[1]);
- set_or(DIB3000MC_REG_IMP_NOISE_55,DIB3000MC_IMP_NEW_ALGO(0)); /* gives 1<<10 */
- break;
- default: /* old algo */
- wr_foreach(dib3000mc_reg_imp_noise_ctl,dib3000mc_imp_noise_ctl[3]);
- break;
+ value = dib3000mc_read_word(state, 1026);
+ if (value != 0x3001 && value != 0x3002) {
+ dprintk("-E- DiB3000MC/P: wrong Device ID (%x)\n",value);
+ return -EREMOTEIO;
}
+ state->dev_id = value;
+
+ dprintk("-I- found DiB3000MC/P: %x\n",state->dev_id);
+
return 0;
}
-static int dib3000mc_set_timing(struct dib3000_state *state, int upd_offset,
- fe_transmit_mode_t fft, fe_bandwidth_t bw)
+static int dib3000mc_set_timing(struct dib3000mc_state *state, s16 nfft, u8 bw, u8 update_offset)
{
- u16 timf_msb,timf_lsb;
- s32 tim_offset,tim_sgn;
- u64 comp1,comp2,comp=0;
-
- switch (bw) {
- case BANDWIDTH_8_MHZ: comp = DIB3000MC_CLOCK_REF*8; break;
- case BANDWIDTH_7_MHZ: comp = DIB3000MC_CLOCK_REF*7; break;
- case BANDWIDTH_6_MHZ: comp = DIB3000MC_CLOCK_REF*6; break;
- default: err("unknown bandwidth (%d)",bw); break;
- }
- timf_msb = (comp >> 16) & 0xff;
- timf_lsb = (comp & 0xffff);
+/*
+ u32 timf_msb, timf_lsb, i;
+ int tim_sgn ;
+ LUInt comp1, comp2, comp ;
+// u32 tim_offset ;
+ comp = 27700 * BW_INDEX_TO_KHZ(bw) / 1000;
+ timf_msb = (comp >> 16) & 0x00FF;
+ timf_lsb = comp & 0xFFFF;
// Update the timing offset ;
- if (upd_offset > 0) {
- if (!state->timing_offset_comp_done) {
- msleep(200);
+ if (update_offset) {
+ if (state->timing_offset_comp_done == 0) {
+ usleep(200000);
state->timing_offset_comp_done = 1;
}
- tim_offset = rd(DIB3000MC_REG_TIMING_OFFS_MSB);
+ tim_offset = dib3000mc_read_word(state, 416);
if ((tim_offset & 0x2000) == 0x2000)
- tim_offset |= 0xC000;
- if (fft == TRANSMISSION_MODE_2K)
- tim_offset <<= 2;
+ tim_offset |= 0xC000; // PB: This only works if tim_offset is s16 - weird
+
+ if (nfft == 0)
+ tim_offset = tim_offset << 2; // PB: Do not store the offset for different things in one variable
state->timing_offset += tim_offset;
}
-
tim_offset = state->timing_offset;
+
if (tim_offset < 0) {
tim_sgn = 1;
tim_offset = -tim_offset;
} else
tim_sgn = 0;
- comp1 = (u32)tim_offset * (u32)timf_lsb ;
- comp2 = (u32)tim_offset * (u32)timf_msb ;
+ comp1 = tim_offset * timf_lsb;
+ comp2 = tim_offset * timf_msb;
comp = ((comp1 >> 16) + comp2) >> 7;
if (tim_sgn == 0)
- comp = (u32)(timf_msb << 16) + (u32) timf_lsb + comp;
+ comp = timf_msb * (1<<16) + timf_lsb + comp;
else
- comp = (u32)(timf_msb << 16) + (u32) timf_lsb - comp ;
+ comp = timf_msb * (1<<16) + timf_lsb - comp;
+
+ timf_msb = (comp>>16)&0xFF ;
+ timf_lsb = comp&0xFFFF;
+*/
+ u32 timf = 1384402 * (BW_INDEX_TO_KHZ(bw) / 1000);
- timf_msb = (comp >> 16) & 0xff;
- timf_lsb = comp & 0xffff;
+ dib3000mc_write_word(state, 23, timf >> 16);
+ dib3000mc_write_word(state, 24, timf & 0xffff);
- wr(DIB3000MC_REG_TIMING_FREQ_MSB,timf_msb);
- wr(DIB3000MC_REG_TIMING_FREQ_LSB,timf_lsb);
return 0;
}
-static int dib3000mc_init_auto_scan(struct dib3000_state *state, fe_bandwidth_t bw, int boost)
+static int dib3000mc_setup_pwm3_state(struct dib3000mc_state *state)
{
- if (boost) {
- wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_ON);
+ if (state->cfg->pwm3_inversion) {
+ dib3000mc_write_word(state, 51, (2 << 14) | (0 << 10) | (7 << 6) | (2 << 2) | (2 << 0));
+ dib3000mc_write_word(state, 52, (0 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (1 << 2) | (2 << 0));
} else {
- wr(DIB3000MC_REG_SCAN_BOOST,DIB3000MC_SCAN_BOOST_OFF);
- }
- switch (bw) {
- case BANDWIDTH_8_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
- break;
- case BANDWIDTH_7_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_7mhz);
- break;
- case BANDWIDTH_6_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_6mhz);
- break;
-/* case BANDWIDTH_5_MHZ:
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_5mhz);
- break;*/
- case BANDWIDTH_AUTO:
- return -EOPNOTSUPP;
- default:
- err("unknown bandwidth value (%d).",bw);
- return -EINVAL;
- }
- if (boost) {
- u32 timeout = (rd(DIB3000MC_REG_BW_TIMOUT_MSB) << 16) +
- rd(DIB3000MC_REG_BW_TIMOUT_LSB);
- timeout *= 85; timeout >>= 7;
- wr(DIB3000MC_REG_BW_TIMOUT_MSB,(timeout >> 16) & 0xffff);
- wr(DIB3000MC_REG_BW_TIMOUT_LSB,timeout & 0xffff);
+ dib3000mc_write_word(state, 51, (2 << 14) | (4 << 10) | (7 << 6) | (2 << 2) | (2 << 0));
+ dib3000mc_write_word(state, 52, (1 << 8) | (5 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (2 << 0));
}
+
+ if (state->cfg->use_pwm3)
+ dib3000mc_write_word(state, 245, (1 << 3) | (1 << 0));
+ else
+ dib3000mc_write_word(state, 245, 0);
+
+ dib3000mc_write_word(state, 1040, 0x3);
return 0;
}
-static int dib3000mc_set_adp_cfg(struct dib3000_state *state, fe_modulation_t con)
+static int dib3000mc_set_output_mode(struct dib3000mc_state *state, int mode)
{
- switch (con) {
- case QAM_64:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[2]);
- break;
- case QAM_16:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[1]);
- break;
- case QPSK:
- wr_foreach(dib3000mc_reg_adp_cfg,dib3000mc_adp_cfg[0]);
- break;
- case QAM_AUTO:
+ int ret = 0;
+ u16 fifo_threshold = 1792;
+ u16 outreg = 0;
+ u16 outmode = 0;
+ u16 elecout = 1;
+ u16 smo_reg = dib3000mc_read_word(state, 206) & 0x0010; /* keep the pid_parse bit */
+
+ dprintk("-I- Setting output mode for demod %p to %d\n",
+ &state->demod, mode);
+
+ switch (mode) {
+ case OUTMODE_HIGH_Z: // disable
+ elecout = 0;
+ break;
+ case OUTMODE_MPEG2_PAR_GATED_CLK: // STBs with parallel gated clock
+ outmode = 0;
+ break;
+ case OUTMODE_MPEG2_PAR_CONT_CLK: // STBs with parallel continues clock
+ outmode = 1;
+ break;
+ case OUTMODE_MPEG2_SERIAL: // STBs with serial input
+ outmode = 2;
+ break;
+ case OUTMODE_MPEG2_FIFO: // e.g. USB feeding
+ elecout = 3;
+ /*ADDR @ 206 :
+ P_smo_error_discard [1;6:6] = 0
+ P_smo_rs_discard [1;5:5] = 0
+ P_smo_pid_parse [1;4:4] = 0
+ P_smo_fifo_flush [1;3:3] = 0
+ P_smo_mode [2;2:1] = 11
+ P_smo_ovf_prot [1;0:0] = 0
+ */
+ smo_reg |= 3 << 1;
+ fifo_threshold = 512;
+ outmode = 5;
+ break;
+ case OUTMODE_DIVERSITY:
+ outmode = 4;
+ elecout = 1;
break;
default:
- warn("unkown constellation.");
+ dprintk("Unhandled output_mode passed to be set for demod %p\n",&state->demod);
+ outmode = 0;
break;
}
- return 0;
+
+ if ((state->cfg->output_mpeg2_in_188_bytes))
+ smo_reg |= (1 << 5); // P_smo_rs_discard [1;5:5] = 1
+
+ outreg = dib3000mc_read_word(state, 244) & 0x07FF;
+ outreg |= (outmode << 11);
+ ret |= dib3000mc_write_word(state, 244, outreg);
+ ret |= dib3000mc_write_word(state, 206, smo_reg); /*smo_ mode*/
+ ret |= dib3000mc_write_word(state, 207, fifo_threshold); /* synchronous fread */
+ ret |= dib3000mc_write_word(state, 1040, elecout); /* P_out_cfg */
+ return ret;
}
-static int dib3000mc_set_general_cfg(struct dib3000_state *state, struct dvb_frontend_parameters *fep, int *auto_val)
+static int dib3000mc_set_bandwidth(struct dvb_frontend *demod, u8 bw)
{
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- fe_code_rate_t fe_cr = FEC_NONE;
- u8 fft=0, guard=0, qam=0, alpha=0, sel_hp=0, cr=0, hrch=0;
- int seq;
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 bw_cfg[6] = { 0 };
+ u16 imp_bw_cfg[3] = { 0 };
+ u16 reg;
- switch (ofdm->transmission_mode) {
- case TRANSMISSION_MODE_2K: fft = DIB3000_TRANSMISSION_MODE_2K; break;
- case TRANSMISSION_MODE_8K: fft = DIB3000_TRANSMISSION_MODE_8K; break;
- case TRANSMISSION_MODE_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->guard_interval) {
- case GUARD_INTERVAL_1_32: guard = DIB3000_GUARD_TIME_1_32; break;
- case GUARD_INTERVAL_1_16: guard = DIB3000_GUARD_TIME_1_16; break;
- case GUARD_INTERVAL_1_8: guard = DIB3000_GUARD_TIME_1_8; break;
- case GUARD_INTERVAL_1_4: guard = DIB3000_GUARD_TIME_1_4; break;
- case GUARD_INTERVAL_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->constellation) {
- case QPSK: qam = DIB3000_CONSTELLATION_QPSK; break;
- case QAM_16: qam = DIB3000_CONSTELLATION_16QAM; break;
- case QAM_64: qam = DIB3000_CONSTELLATION_64QAM; break;
- case QAM_AUTO: break;
- default: return -EINVAL;
- }
- switch (ofdm->hierarchy_information) {
- case HIERARCHY_NONE: /* fall through */
- case HIERARCHY_1: alpha = DIB3000_ALPHA_1; break;
- case HIERARCHY_2: alpha = DIB3000_ALPHA_2; break;
- case HIERARCHY_4: alpha = DIB3000_ALPHA_4; break;
- case HIERARCHY_AUTO: break;
- default: return -EINVAL;
- }
- if (ofdm->hierarchy_information == HIERARCHY_NONE) {
- hrch = DIB3000_HRCH_OFF;
- sel_hp = DIB3000_SELECT_HP;
- fe_cr = ofdm->code_rate_HP;
- } else if (ofdm->hierarchy_information != HIERARCHY_AUTO) {
- hrch = DIB3000_HRCH_ON;
- sel_hp = DIB3000_SELECT_LP;
- fe_cr = ofdm->code_rate_LP;
- }
- switch (fe_cr) {
- case FEC_1_2: cr = DIB3000_FEC_1_2; break;
- case FEC_2_3: cr = DIB3000_FEC_2_3; break;
- case FEC_3_4: cr = DIB3000_FEC_3_4; break;
- case FEC_5_6: cr = DIB3000_FEC_5_6; break;
- case FEC_7_8: cr = DIB3000_FEC_7_8; break;
- case FEC_NONE: break;
- case FEC_AUTO: break;
- default: return -EINVAL;
- }
+/* settings here are for 27.7MHz */
+ switch (bw) {
+ case BANDWIDTH_8_MHZ:
+ bw_cfg[0] = 0x0019; bw_cfg[1] = 0x5c30; bw_cfg[2] = 0x0054; bw_cfg[3] = 0x88a0; bw_cfg[4] = 0x01a6; bw_cfg[5] = 0xab20;
+ imp_bw_cfg[0] = 0x04db; imp_bw_cfg[1] = 0x00db; imp_bw_cfg[2] = 0x00b7;
+ break;
- wr(DIB3000MC_REG_DEMOD_PARM,DIB3000MC_DEMOD_PARM(alpha,qam,guard,fft));
- wr(DIB3000MC_REG_HRCH_PARM,DIB3000MC_HRCH_PARM(sel_hp,cr,hrch));
+ case BANDWIDTH_7_MHZ:
+ bw_cfg[0] = 0x001c; bw_cfg[1] = 0xfba5; bw_cfg[2] = 0x0060; bw_cfg[3] = 0x9c25; bw_cfg[4] = 0x01e3; bw_cfg[5] = 0x0cb7;
+ imp_bw_cfg[0] = 0x04c0; imp_bw_cfg[1] = 0x00c0; imp_bw_cfg[2] = 0x00a0;
+ break;
- switch (fep->inversion) {
- case INVERSION_OFF:
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
+ case BANDWIDTH_6_MHZ:
+ bw_cfg[0] = 0x0021; bw_cfg[1] = 0xd040; bw_cfg[2] = 0x0070; bw_cfg[3] = 0xb62b; bw_cfg[4] = 0x0233; bw_cfg[5] = 0x8ed5;
+ imp_bw_cfg[0] = 0x04a5; imp_bw_cfg[1] = 0x00a5; imp_bw_cfg[2] = 0x0089;
break;
- case INVERSION_AUTO: /* fall through */
- case INVERSION_ON:
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_ON);
+
+ case 255 /* BANDWIDTH_5_MHZ */:
+ bw_cfg[0] = 0x0028; bw_cfg[1] = 0x9380; bw_cfg[2] = 0x0087; bw_cfg[3] = 0x4100; bw_cfg[4] = 0x02a4; bw_cfg[5] = 0x4500;
+ imp_bw_cfg[0] = 0x0489; imp_bw_cfg[1] = 0x0089; imp_bw_cfg[2] = 0x0072;
break;
- default:
- return -EINVAL;
+
+ default: return -EINVAL;
}
- seq = dib3000_seq
- [ofdm->transmission_mode == TRANSMISSION_MODE_AUTO]
- [ofdm->guard_interval == GUARD_INTERVAL_AUTO]
- [fep->inversion == INVERSION_AUTO];
-
- deb_setf("seq? %d\n", seq);
- wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS(seq,1));
- *auto_val = ofdm->constellation == QAM_AUTO ||
- ofdm->hierarchy_information == HIERARCHY_AUTO ||
- ofdm->guard_interval == GUARD_INTERVAL_AUTO ||
- ofdm->transmission_mode == TRANSMISSION_MODE_AUTO ||
- fe_cr == FEC_AUTO ||
- fep->inversion == INVERSION_AUTO;
- return 0;
-}
+ for (reg = 6; reg < 12; reg++)
+ dib3000mc_write_word(state, reg, bw_cfg[reg - 6]);
+ dib3000mc_write_word(state, 12, 0x0000);
+ dib3000mc_write_word(state, 13, 0x03e8);
+ dib3000mc_write_word(state, 14, 0x0000);
+ dib3000mc_write_word(state, 15, 0x03f2);
+ dib3000mc_write_word(state, 16, 0x0001);
+ dib3000mc_write_word(state, 17, 0xb0d0);
+ // P_sec_len
+ dib3000mc_write_word(state, 18, 0x0393);
+ dib3000mc_write_word(state, 19, 0x8700);
-static int dib3000mc_get_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep)
-{
- struct dib3000_state* state = fe->demodulator_priv;
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- fe_code_rate_t *cr;
- u16 tps_val,cr_val;
- int inv_test1,inv_test2;
- u32 dds_val, threshold = 0x1000000;
-
- if (!(rd(DIB3000MC_REG_LOCK_507) & DIB3000MC_LOCK_507))
- return 0;
-
- dds_val = (rd(DIB3000MC_REG_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_DDS_FREQ_LSB);
- deb_getf("DDS_FREQ: %6x\n",dds_val);
- if (dds_val < threshold)
- inv_test1 = 0;
- else if (dds_val == threshold)
- inv_test1 = 1;
- else
- inv_test1 = 2;
-
- dds_val = (rd(DIB3000MC_REG_SET_DDS_FREQ_MSB) << 16) + rd(DIB3000MC_REG_SET_DDS_FREQ_LSB);
- deb_getf("DDS_SET_FREQ: %6x\n",dds_val);
- if (dds_val < threshold)
- inv_test2 = 0;
- else if (dds_val == threshold)
- inv_test2 = 1;
- else
- inv_test2 = 2;
+ for (reg = 55; reg < 58; reg++)
+ dib3000mc_write_word(state, reg, imp_bw_cfg[reg - 55]);
- fep->inversion =
- ((inv_test2 == 2) && (inv_test1==1 || inv_test1==0)) ||
- ((inv_test2 == 0) && (inv_test1==1 || inv_test1==2)) ?
- INVERSION_ON : INVERSION_OFF;
+ // Timing configuration
+ dib3000mc_set_timing(state, 0, bw, 0);
- deb_getf("inversion %d %d, %d\n", inv_test2, inv_test1, fep->inversion);
+ return 0;
+}
- fep->frequency = state->last_tuned_freq;
- fep->u.ofdm.bandwidth= state->last_tuned_bw;
+static u16 impulse_noise_val[29] =
- tps_val = rd(DIB3000MC_REG_TUNING_PARM);
+{
+ 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c, 0x3ffe, 0x7f3,
+ 0x2d94, 0x76, 0x53d, 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3, 0x3feb, 0x7d2,
+ 0x365e, 0x76, 0x48c, 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0000, 0xd
+};
- switch (DIB3000MC_TP_QAM(tps_val)) {
- case DIB3000_CONSTELLATION_QPSK:
- deb_getf("QPSK ");
- ofdm->constellation = QPSK;
- break;
- case DIB3000_CONSTELLATION_16QAM:
- deb_getf("QAM16 ");
- ofdm->constellation = QAM_16;
- break;
- case DIB3000_CONSTELLATION_64QAM:
- deb_getf("QAM64 ");
- ofdm->constellation = QAM_64;
- break;
- default:
- err("Unexpected constellation returned by TPS (%d)", tps_val);
- break;
+static void dib3000mc_set_impulse_noise(struct dib3000mc_state *state, u8 mode, s16 nfft)
+{
+ u16 i;
+ for (i = 58; i < 87; i++)
+ dib3000mc_write_word(state, i, impulse_noise_val[i-58]);
+
+ if (nfft == 1) {
+ dib3000mc_write_word(state, 58, 0x3b);
+ dib3000mc_write_word(state, 84, 0x00);
+ dib3000mc_write_word(state, 85, 0x8200);
}
- if (DIB3000MC_TP_HRCH(tps_val)) {
- deb_getf("HRCH ON ");
- cr = &ofdm->code_rate_LP;
- ofdm->code_rate_HP = FEC_NONE;
- switch (DIB3000MC_TP_ALPHA(tps_val)) {
- case DIB3000_ALPHA_0:
- deb_getf("HIERARCHY_NONE ");
- ofdm->hierarchy_information = HIERARCHY_NONE;
- break;
- case DIB3000_ALPHA_1:
- deb_getf("HIERARCHY_1 ");
- ofdm->hierarchy_information = HIERARCHY_1;
- break;
- case DIB3000_ALPHA_2:
- deb_getf("HIERARCHY_2 ");
- ofdm->hierarchy_information = HIERARCHY_2;
- break;
- case DIB3000_ALPHA_4:
- deb_getf("HIERARCHY_4 ");
- ofdm->hierarchy_information = HIERARCHY_4;
- break;
- default:
- err("Unexpected ALPHA value returned by TPS (%d)", tps_val);
- break;
- }
- cr_val = DIB3000MC_TP_FEC_CR_LP(tps_val);
+ dib3000mc_write_word(state, 34, 0x1294);
+ dib3000mc_write_word(state, 35, 0x1ff8);
+ if (mode == 1)
+ dib3000mc_write_word(state, 55, dib3000mc_read_word(state, 55) | (1 << 10));
+}
+
+static int dib3000mc_init(struct dvb_frontend *demod)
+{
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ struct dibx000_agc_config *agc = state->cfg->agc;
+
+ // Restart Configuration
+ dib3000mc_write_word(state, 1027, 0x8000);
+ dib3000mc_write_word(state, 1027, 0x0000);
+
+ // power up the demod + mobility configuration
+ dib3000mc_write_word(state, 140, 0x0000);
+ dib3000mc_write_word(state, 1031, 0);
+
+ if (state->cfg->mobile_mode) {
+ dib3000mc_write_word(state, 139, 0x0000);
+ dib3000mc_write_word(state, 141, 0x0000);
+ dib3000mc_write_word(state, 175, 0x0002);
+ dib3000mc_write_word(state, 1032, 0x0000);
} else {
- deb_getf("HRCH OFF ");
- cr = &ofdm->code_rate_HP;
- ofdm->code_rate_LP = FEC_NONE;
- ofdm->hierarchy_information = HIERARCHY_NONE;
- cr_val = DIB3000MC_TP_FEC_CR_HP(tps_val);
+ dib3000mc_write_word(state, 139, 0x0001);
+ dib3000mc_write_word(state, 141, 0x0000);
+ dib3000mc_write_word(state, 175, 0x0000);
+ dib3000mc_write_word(state, 1032, 0x012C);
}
+ dib3000mc_write_word(state, 1033, 0);
- switch (cr_val) {
- case DIB3000_FEC_1_2:
- deb_getf("FEC_1_2 ");
- *cr = FEC_1_2;
- break;
- case DIB3000_FEC_2_3:
- deb_getf("FEC_2_3 ");
- *cr = FEC_2_3;
- break;
- case DIB3000_FEC_3_4:
- deb_getf("FEC_3_4 ");
- *cr = FEC_3_4;
- break;
- case DIB3000_FEC_5_6:
- deb_getf("FEC_5_6 ");
- *cr = FEC_4_5;
- break;
- case DIB3000_FEC_7_8:
- deb_getf("FEC_7_8 ");
- *cr = FEC_7_8;
- break;
- default:
- err("Unexpected FEC returned by TPS (%d)", tps_val);
- break;
- }
+ // P_clk_cfg
+ dib3000mc_write_word(state, 1037, 12592);
- switch (DIB3000MC_TP_GUARD(tps_val)) {
- case DIB3000_GUARD_TIME_1_32:
- deb_getf("GUARD_INTERVAL_1_32 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_32;
- break;
- case DIB3000_GUARD_TIME_1_16:
- deb_getf("GUARD_INTERVAL_1_16 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_16;
- break;
- case DIB3000_GUARD_TIME_1_8:
- deb_getf("GUARD_INTERVAL_1_8 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_8;
- break;
- case DIB3000_GUARD_TIME_1_4:
- deb_getf("GUARD_INTERVAL_1_4 ");
- ofdm->guard_interval = GUARD_INTERVAL_1_4;
- break;
- default:
- err("Unexpected Guard Time returned by TPS (%d)", tps_val);
- break;
- }
+ // other configurations
- switch (DIB3000MC_TP_FFT(tps_val)) {
- case DIB3000_TRANSMISSION_MODE_2K:
- deb_getf("TRANSMISSION_MODE_2K ");
- ofdm->transmission_mode = TRANSMISSION_MODE_2K;
- break;
- case DIB3000_TRANSMISSION_MODE_8K:
- deb_getf("TRANSMISSION_MODE_8K ");
- ofdm->transmission_mode = TRANSMISSION_MODE_8K;
- break;
- default:
- err("unexpected transmission mode return by TPS (%d)", tps_val);
- break;
- }
- deb_getf("\n");
+ // P_ctrl_sfreq
+ dib3000mc_write_word(state, 33, (5 << 0));
+ dib3000mc_write_word(state, 88, (1 << 10) | (0x10 << 0));
+
+ // Phase noise control
+ // P_fft_phacor_inh, P_fft_phacor_cpe, P_fft_powrange
+ dib3000mc_write_word(state, 99, (1 << 9) | (0x20 << 0));
+
+ if (state->cfg->phase_noise_mode == 0)
+ dib3000mc_write_word(state, 111, 0x00);
+ else
+ dib3000mc_write_word(state, 111, 0x02);
+
+ // P_agc_global
+ dib3000mc_write_word(state, 50, 0x8000);
+
+ // agc setup misc
+ dib3000mc_setup_pwm3_state(state);
+
+ // P_agc_counter_lock
+ dib3000mc_write_word(state, 53, 0x87);
+ // P_agc_counter_unlock
+ dib3000mc_write_word(state, 54, 0x87);
+
+ /* agc */
+ dib3000mc_write_word(state, 36, state->cfg->max_time);
+ dib3000mc_write_word(state, 37, agc->setup);
+ dib3000mc_write_word(state, 38, state->cfg->pwm3_value);
+ dib3000mc_write_word(state, 39, state->cfg->ln_adc_level);
+
+ // set_agc_loop_Bw
+ dib3000mc_write_word(state, 40, 0x0179);
+ dib3000mc_write_word(state, 41, 0x03f0);
+
+ dib3000mc_write_word(state, 42, agc->agc1_max);
+ dib3000mc_write_word(state, 43, agc->agc1_min);
+ dib3000mc_write_word(state, 44, agc->agc2_max);
+ dib3000mc_write_word(state, 45, agc->agc2_min);
+ dib3000mc_write_word(state, 46, (agc->agc1_pt1 << 8) | agc->agc1_pt2);
+ dib3000mc_write_word(state, 47, (agc->agc1_slope1 << 8) | agc->agc1_slope2);
+ dib3000mc_write_word(state, 48, (agc->agc2_pt1 << 8) | agc->agc2_pt2);
+ dib3000mc_write_word(state, 49, (agc->agc2_slope1 << 8) | agc->agc2_slope2);
+
+// Begin: TimeOut registers
+ // P_pha3_thres
+ dib3000mc_write_word(state, 110, 3277);
+ // P_timf_alpha = 6, P_corm_alpha = 6, P_corm_thres = 0x80
+ dib3000mc_write_word(state, 26, 0x6680);
+ // lock_mask0
+ dib3000mc_write_word(state, 1, 4);
+ // lock_mask1
+ dib3000mc_write_word(state, 2, 4);
+ // lock_mask2
+ dib3000mc_write_word(state, 3, 0x1000);
+ // P_search_maxtrial=1
+ dib3000mc_write_word(state, 5, 1);
+
+ dib3000mc_set_bandwidth(&state->demod, BANDWIDTH_8_MHZ);
+
+ // div_lock_mask
+ dib3000mc_write_word(state, 4, 0x814);
+
+ dib3000mc_write_word(state, 21, (1 << 9) | 0x164);
+ dib3000mc_write_word(state, 22, 0x463d);
+
+ // Spurious rm cfg
+ // P_cspu_regul, P_cspu_win_cut
+ dib3000mc_write_word(state, 120, 0x200f);
+ // P_adp_selec_monit
+ dib3000mc_write_word(state, 134, 0);
+
+ // Fec cfg
+ dib3000mc_write_word(state, 195, 0x10);
+
+ // diversity register: P_dvsy_sync_wait..
+ dib3000mc_write_word(state, 180, 0x2FF0);
+
+ // Impulse noise configuration
+ dib3000mc_set_impulse_noise(state, 0, 1);
+
+ // output mode set-up
+ dib3000mc_set_output_mode(state, OUTMODE_HIGH_Z);
+
+ /* close the i2c-gate */
+ dib3000mc_write_word(state, 769, (1 << 7) );
return 0;
}
-static int dib3000mc_set_frontend(struct dvb_frontend* fe,
- struct dvb_frontend_parameters *fep, int tuner)
+static int dib3000mc_sleep(struct dvb_frontend *demod)
{
- struct dib3000_state* state = fe->demodulator_priv;
- struct dvb_ofdm_parameters *ofdm = &fep->u.ofdm;
- int search_state,auto_val;
- u16 val;
+ struct dib3000mc_state *state = demod->demodulator_priv;
- if (tuner && fe->ops.tuner_ops.set_params) { /* initial call from dvb */
- fe->ops.tuner_ops.set_params(fe, fep);
- if (fe->ops.i2c_gate_ctrl) fe->ops.i2c_gate_ctrl(fe, 0);
-
- state->last_tuned_freq = fep->frequency;
- // if (!scanboost) {
- dib3000mc_set_timing(state,0,ofdm->transmission_mode,ofdm->bandwidth);
- dib3000mc_init_auto_scan(state, ofdm->bandwidth, 0);
- state->last_tuned_bw = ofdm->bandwidth;
-
- wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_AGC);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
-
- /* Default cfg isi offset adp */
- wr_foreach(dib3000mc_reg_offset,dib3000mc_offset[0]);
-
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT | DIB3000MC_ISI_INHIBIT);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
- wr(DIB3000MC_REG_UNK_133,DIB3000MC_UNK_133);
-
- wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
- /* power smoothing */
- if (ofdm->bandwidth != BANDWIDTH_8_MHZ) {
- wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[0]);
- } else {
- wr_foreach(dib3000mc_reg_bw,dib3000mc_bw[3]);
- }
- auto_val = 0;
- dib3000mc_set_general_cfg(state,fep,&auto_val);
- dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
-
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val|DIB3000MC_DEMOD_RST_DEMOD_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
- // }
- msleep(70);
-
- /* something has to be auto searched */
- if (auto_val) {
- int as_count=0;
-
- deb_setf("autosearch enabled.\n");
-
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
-
- while ((search_state = dib3000_search_status(
- rd(DIB3000MC_REG_AS_IRQ),1)) < 0 && as_count++ < 100)
- msleep(10);
-
- deb_info("search_state after autosearch %d after %d checks\n",search_state,as_count);
-
- if (search_state == 1) {
- struct dvb_frontend_parameters feps;
- if (dib3000mc_get_frontend(fe, &feps) == 0) {
- deb_setf("reading tuning data from frontend succeeded.\n");
- return dib3000mc_set_frontend(fe, &feps, 0);
- }
- }
- } else {
- dib3000mc_set_impulse_noise(state,0,ofdm->transmission_mode,ofdm->bandwidth);
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
-
- /* set_offset_cfg */
- wr_foreach(dib3000mc_reg_offset,
- dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
- }
- } else { /* second call, after autosearch (fka: set_WithKnownParams) */
-// dib3000mc_set_timing(state,1,ofdm->transmission_mode,ofdm->bandwidth);
-
- auto_val = 0;
- dib3000mc_set_general_cfg(state,fep,&auto_val);
- if (auto_val)
- deb_info("auto_val is true, even though an auto search was already performed.\n");
-
- dib3000mc_set_impulse_noise(state,0,ofdm->constellation,ofdm->bandwidth);
-
- val = rd(DIB3000MC_REG_DEMOD_PARM);
- wr(DIB3000MC_REG_DEMOD_PARM,val | DIB3000MC_DEMOD_RST_AUTO_SRCH_ON);
- wr(DIB3000MC_REG_DEMOD_PARM,val);
+ dib3000mc_write_word(state, 1037, dib3000mc_read_word(state, 1037) | 0x0003);
+ dib3000mc_write_word(state, 1031, 0xFFFF);
+ dib3000mc_write_word(state, 1032, 0xFFFF);
+ dib3000mc_write_word(state, 1033, 0xFFF4); // **** Bin2
- msleep(30);
+ return 0;
+}
- wr(DIB3000MC_REG_ISI,DIB3000MC_ISI_DEFAULT|DIB3000MC_ISI_ACTIVATE);
- dib3000mc_set_adp_cfg(state,ofdm->constellation);
- wr_foreach(dib3000mc_reg_offset,
- dib3000mc_offset[(ofdm->transmission_mode == TRANSMISSION_MODE_8K)+1]);
+static void dib3000mc_set_adp_cfg(struct dib3000mc_state *state, s16 qam)
+{
+ u16 cfg[4] = { 0 },reg;
+ switch (qam) {
+ case 0:
+ cfg[0] = 0x099a; cfg[1] = 0x7fae; cfg[2] = 0x0333; cfg[3] = 0x7ff0;
+ break;
+ case 1:
+ cfg[0] = 0x023d; cfg[1] = 0x7fdf; cfg[2] = 0x00a4; cfg[3] = 0x7ff0;
+ break;
+ case 2:
+ cfg[0] = 0x0148; cfg[1] = 0x7ff0; cfg[2] = 0x00a4; cfg[3] = 0x7ff8;
+ break;
}
- return 0;
+ for (reg = 129; reg < 133; reg++)
+ dib3000mc_write_word(state, reg, cfg[reg - 129]);
}
-static int dib3000mc_fe_init(struct dvb_frontend* fe, int mobile_mode)
+static void dib3000mc_set_channel_cfg(struct dib3000mc_state *state, struct dibx000_ofdm_channel *chan, u16 seq)
{
- struct dib3000_state *state = fe->demodulator_priv;
- deb_info("init start\n");
-
- state->timing_offset = 0;
- state->timing_offset_comp_done = 0;
+ u16 tmp;
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_CONFIG);
- wr(DIB3000MC_REG_RESTART,DIB3000MC_RESTART_OFF);
- wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_UP);
- wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_PUP_MOBILE);
- wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_UP);
- wr(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_INIT);
+ dib3000mc_set_timing(state, chan->nfft, chan->Bw, 0);
- wr(DIB3000MC_REG_RST_UNC,DIB3000MC_RST_UNC_OFF);
- wr(DIB3000MC_REG_UNK_19,DIB3000MC_UNK_19);
+// if (boost)
+// dib3000mc_write_word(state, 100, (11 << 6) + 6);
+// else
+ dib3000mc_write_word(state, 100, (16 << 6) + 9);
- wr(33,5);
- wr(36,81);
- wr(DIB3000MC_REG_UNK_88,DIB3000MC_UNK_88);
+ dib3000mc_write_word(state, 1027, 0x0800);
+ dib3000mc_write_word(state, 1027, 0x0000);
- wr(DIB3000MC_REG_UNK_99,DIB3000MC_UNK_99);
- wr(DIB3000MC_REG_UNK_111,DIB3000MC_UNK_111_PH_N_MODE_0); /* phase noise algo off */
+ //Default cfg isi offset adp
+ dib3000mc_write_word(state, 26, 0x6680);
+ dib3000mc_write_word(state, 29, 0x1273);
+ dib3000mc_write_word(state, 33, 5);
+ dib3000mc_set_adp_cfg(state, 1);
+ dib3000mc_write_word(state, 133, 15564);
- /* mobile mode - portable reception */
- wr_foreach(dib3000mc_reg_mobile_mode,dib3000mc_mobile_mode[1]);
+ dib3000mc_write_word(state, 12 , 0x0);
+ dib3000mc_write_word(state, 13 , 0x3e8);
+ dib3000mc_write_word(state, 14 , 0x0);
+ dib3000mc_write_word(state, 15 , 0x3f2);
-/* TUNER_PANASONIC_ENV57H12D5: */
- wr_foreach(dib3000mc_reg_agc_bandwidth,dib3000mc_agc_bandwidth);
- wr_foreach(dib3000mc_reg_agc_bandwidth_general,dib3000mc_agc_bandwidth_general);
- wr_foreach(dib3000mc_reg_agc,dib3000mc_agc_tuner[1]);
+ dib3000mc_write_word(state, 93,0);
+ dib3000mc_write_word(state, 94,0);
+ dib3000mc_write_word(state, 95,0);
+ dib3000mc_write_word(state, 96,0);
+ dib3000mc_write_word(state, 97,0);
+ dib3000mc_write_word(state, 98,0);
- wr(DIB3000MC_REG_UNK_110,DIB3000MC_UNK_110);
- wr(26,0x6680);
- wr(DIB3000MC_REG_UNK_1,DIB3000MC_UNK_1);
- wr(DIB3000MC_REG_UNK_2,DIB3000MC_UNK_2);
- wr(DIB3000MC_REG_UNK_3,DIB3000MC_UNK_3);
- wr(DIB3000MC_REG_SEQ_TPS,DIB3000MC_SEQ_TPS_DEFAULT);
+ dib3000mc_set_impulse_noise(state, 0, chan->nfft);
- wr_foreach(dib3000mc_reg_bandwidth,dib3000mc_bandwidth_8mhz);
- wr_foreach(dib3000mc_reg_bandwidth_general,dib3000mc_bandwidth_general);
+ tmp = ((chan->nfft & 0x1) << 7) | (chan->guard << 5) | (chan->nqam << 3) | chan->vit_alpha;
+ dib3000mc_write_word(state, 0, tmp);
- wr(DIB3000MC_REG_UNK_4,DIB3000MC_UNK_4);
+ dib3000mc_write_word(state, 5, seq);
- wr(DIB3000MC_REG_SET_DDS_FREQ_MSB,DIB3000MC_DDS_FREQ_MSB_INV_OFF);
- wr(DIB3000MC_REG_SET_DDS_FREQ_LSB,DIB3000MC_DDS_FREQ_LSB);
+ tmp = (chan->vit_hrch << 4) | (chan->vit_select_hp);
+ if (!chan->vit_hrch || (chan->vit_hrch && chan->vit_select_hp))
+ tmp |= chan->vit_code_rate_hp << 1;
+ else
+ tmp |= chan->vit_code_rate_lp << 1;
+ dib3000mc_write_word(state, 181, tmp);
- dib3000mc_set_timing(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
-// wr_foreach(dib3000mc_reg_timing_freq,dib3000mc_timing_freq[3]);
+ // diversity synchro delay
+ tmp = dib3000mc_read_word(state, 180) & 0x000f;
+ tmp |= ((chan->nfft == 0) ? 64 : 256) * ((1 << (chan->guard)) * 3 / 2) << 4; // add 50% SFN margin
+ dib3000mc_write_word(state, 180, tmp);
- wr(DIB3000MC_REG_UNK_120,DIB3000MC_UNK_120);
- wr(DIB3000MC_REG_UNK_134,DIB3000MC_UNK_134);
- wr(DIB3000MC_REG_FEC_CFG,DIB3000MC_FEC_CFG);
+ // restart demod
+ tmp = dib3000mc_read_word(state, 0);
+ dib3000mc_write_word(state, 0, tmp | (1 << 9));
+ dib3000mc_write_word(state, 0, tmp);
- wr(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+ msleep(30);
- dib3000mc_set_impulse_noise(state,0,TRANSMISSION_MODE_8K,BANDWIDTH_8_MHZ);
+ dib3000mc_set_impulse_noise(state, state->cfg->impulse_noise_mode, chan->nfft);
+}
-/* output mode control, just the MPEG2_SLAVE */
-// set_or(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
- wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_SLAVE);
- wr(DIB3000MC_REG_SMO_MODE,DIB3000MC_SMO_MODE_SLAVE);
- wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_SLAVE);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_SLAVE);
+static int dib3000mc_autosearch_start(struct dvb_frontend *demod, struct dibx000_ofdm_channel *chan)
+{
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 reg;
+// u32 val;
+ struct dibx000_ofdm_channel fchan;
-/* MPEG2_PARALLEL_CONTINUOUS_CLOCK
- wr(DIB3000MC_REG_OUTMODE,
- DIB3000MC_SET_OUTMODE(DIB3000MC_OM_PAR_CONT_CLK,
- rd(DIB3000MC_REG_OUTMODE)));
+ INIT_OFDM_CHANNEL(&fchan);
+ fchan = *chan;
- wr(DIB3000MC_REG_SMO_MODE,
- DIB3000MC_SMO_MODE_DEFAULT |
- DIB3000MC_SMO_MODE_188);
- wr(DIB3000MC_REG_FIFO_THRESHOLD,DIB3000MC_FIFO_THRESHOLD_DEFAULT);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
-*/
+ /* a channel for autosearch */
+ reg = 0;
+ if (chan->nfft == -1 && chan->guard == -1) reg = 7;
+ if (chan->nfft == -1 && chan->guard != -1) reg = 2;
+ if (chan->nfft != -1 && chan->guard == -1) reg = 3;
-/* diversity */
- wr(DIB3000MC_REG_DIVERSITY1,DIB3000MC_DIVERSITY1_DEFAULT);
- wr(DIB3000MC_REG_DIVERSITY2,DIB3000MC_DIVERSITY2_DEFAULT);
+ fchan.nfft = 1; fchan.guard = 0; fchan.nqam = 2;
+ fchan.vit_alpha = 1; fchan.vit_code_rate_hp = 2; fchan.vit_code_rate_lp = 2;
+ fchan.vit_hrch = 0; fchan.vit_select_hp = 1;
- set_and(DIB3000MC_REG_DIVERSITY3,DIB3000MC_DIVERSITY3_IN_OFF);
+ dib3000mc_set_channel_cfg(state, &fchan, reg);
- set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_DIV_IN_OFF);
+ reg = dib3000mc_read_word(state, 0);
+ dib3000mc_write_word(state, 0, reg | (1 << 8));
+ dib3000mc_write_word(state, 0, reg);
- deb_info("init end\n");
return 0;
}
-static int dib3000mc_read_status(struct dvb_frontend* fe, fe_status_t *stat)
+
+static int dib3000mc_autosearch_is_irq(struct dvb_frontend *demod)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 lock = rd(DIB3000MC_REG_LOCKING);
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ u16 irq_pending = dib3000mc_read_word(state, 511);
- *stat = 0;
- if (DIB3000MC_AGC_LOCK(lock))
- *stat |= FE_HAS_SIGNAL;
- if (DIB3000MC_CARRIER_LOCK(lock))
- *stat |= FE_HAS_CARRIER;
- if (DIB3000MC_TPS_LOCK(lock))
- *stat |= FE_HAS_VITERBI;
- if (DIB3000MC_MPEG_SYNC_LOCK(lock))
- *stat |= (FE_HAS_SYNC | FE_HAS_LOCK);
+ if (irq_pending & 0x1) // failed
+ return 1;
- deb_stat("actual status is %2x fifo_level: %x,244: %x, 206: %x, 207: %x, 1040: %x\n",*stat,rd(510),rd(244),rd(206),rd(207),rd(1040));
+ if (irq_pending & 0x2) // succeeded
+ return 2;
- return 0;
+ return 0; // still pending
}
-static int dib3000mc_read_ber(struct dvb_frontend* fe, u32 *ber)
+static int dib3000mc_tune(struct dvb_frontend *demod, struct dibx000_ofdm_channel *ch)
{
- struct dib3000_state* state = fe->demodulator_priv;
- *ber = ((rd(DIB3000MC_REG_BER_MSB) << 16) | rd(DIB3000MC_REG_BER_LSB));
+ struct dib3000mc_state *state = demod->demodulator_priv;
+
+ // ** configure demod **
+ dib3000mc_set_channel_cfg(state, ch, 0);
+
+ // activates isi
+ dib3000mc_write_word(state, 29, 0x1073);
+
+ dib3000mc_set_adp_cfg(state, (u8)ch->nqam);
+
+ if (ch->nfft == 1) {
+ dib3000mc_write_word(state, 26, 38528);
+ dib3000mc_write_word(state, 33, 8);
+ } else {
+ dib3000mc_write_word(state, 26, 30336);
+ dib3000mc_write_word(state, 33, 6);
+ }
+
+ // if (lock)
+ // dib3000mc_set_timing(state, ch->nfft, ch->Bw, 1);
+
return 0;
}
-static int dib3000mc_read_unc_blocks(struct dvb_frontend* fe, u32 *unc)
+static int dib3000mc_demod_output_mode(struct dvb_frontend *demod, int mode)
{
- struct dib3000_state* state = fe->demodulator_priv;
-
- *unc = rd(DIB3000MC_REG_PACKET_ERRORS);
- return 0;
+ struct dib3000mc_state *state = demod->demodulator_priv;
+ return dib3000mc_set_output_mode(state, mode);
}
-/* see dib3000mb.c for calculation comments */
-static int dib3000mc_read_signal_strength(struct dvb_frontend* fe, u16 *strength)
+static int dib3000mc_i2c_enumeration(struct dvb_frontend *demod[], int no_of_demods, u8 default_addr)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB);
- *strength = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
+ struct dib3000mc_state *st;
+ int k,ret=0;
+ u8 new_addr;
+
+ static u8 DIB3000MC_I2C_ADDRESS[] = {20,22,24,26};
+
+ for (k = no_of_demods-1; k >= 0; k--) {
+ st = demod[k]->demodulator_priv;
+
+ /* designated i2c address */
+ new_addr = DIB3000MC_I2C_ADDRESS[k];
+
+ st->i2c_addr = new_addr;
+ if (dib3000mc_identify(st) != 0) {
+ st->i2c_addr = default_addr;
+ if (dib3000mc_identify(st) != 0) {
+ dprintk("-E- DiB3000P/MC #%d: not identified\n", k);
+ return -EINVAL;
+ }
+ }
+
+ /* turn on div_out */
+ dib3000mc_demod_output_mode(demod[k], OUTMODE_MPEG2_PAR_CONT_CLK);
+
+ // set new i2c address and force divstr (Bit 1) to value 0 (Bit 0)
+ ret |= dib3000mc_write_word(st, 1024, (new_addr << 3) | 0x1);
+ st->i2c_addr = new_addr;
+ }
+
+ for (k = 0; k < no_of_demods; k++) {
+ st = demod[k]->demodulator_priv;
+
+ ret |= dib3000mc_write_word(st, 1024, st->i2c_addr << 3);
- deb_stat("signal: mantisse = %d, exponent = %d\n",(*strength >> 8) & 0xff, *strength & 0xff);
+ /* turn off data output */
+ dib3000mc_demod_output_mode(demod[k],OUTMODE_HIGH_Z);
+ dib3000mc_write_word(st, 769, (1 << 7) );
+
+ }
return 0;
}
-/* see dib3000mb.c for calculation comments */
-static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
+struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating)
{
- struct dib3000_state* state = fe->demodulator_priv;
- u16 val = rd(DIB3000MC_REG_SIGNAL_NOISE_LSB),
- val2 = rd(DIB3000MC_REG_SIGNAL_NOISE_MSB);
- u16 sig,noise;
+ struct dib3000mc_state *st = demod->demodulator_priv;
+ return dibx000_get_i2c_adapter(&st->i2c_master, DIBX000_I2C_INTERFACE_TUNER, gating);
+}
- sig = (((val >> 6) & 0xff) << 8) + (val & 0x3f);
- noise = (((val >> 4) & 0xff) << 8) + ((val & 0xf) << 2) + ((val2 >> 14) & 0x3);
- if (noise == 0)
- *snr = 0xffff;
- else
- *snr = (u16) sig/noise;
+EXPORT_SYMBOL(dib3000mc_get_tuner_i2c_master);
+
+static int dib3000mc_get_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
+{
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 tps = dib3000mc_read_word(state,458);
+
+ fep->inversion = INVERSION_AUTO;
+
+ fep->u.ofdm.bandwidth = state->current_bandwidth;
+
+ switch ((tps >> 8) & 0x1) {
+ case 0: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_2K; break;
+ case 1: fep->u.ofdm.transmission_mode = TRANSMISSION_MODE_8K; break;
+ }
+
+ switch (tps & 0x3) {
+ case 0: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_32; break;
+ case 1: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_16; break;
+ case 2: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_8; break;
+ case 3: fep->u.ofdm.guard_interval = GUARD_INTERVAL_1_4; break;
+ }
+
+ switch ((tps >> 13) & 0x3) {
+ case 0: fep->u.ofdm.constellation = QPSK; break;
+ case 1: fep->u.ofdm.constellation = QAM_16; break;
+ case 2:
+ default: fep->u.ofdm.constellation = QAM_64; break;
+ }
+
+ /* as long as the frontend_param structure is fixed for hierarchical transmission I refuse to use it */
+ /* (tps >> 12) & 0x1 == hrch is used, (tps >> 9) & 0x7 == alpha */
+
+ fep->u.ofdm.hierarchy_information = HIERARCHY_NONE;
+ switch ((tps >> 5) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_HP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_HP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_HP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_HP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_HP = FEC_7_8; break;
+
+ }
+
+ switch ((tps >> 2) & 0x7) {
+ case 1: fep->u.ofdm.code_rate_LP = FEC_1_2; break;
+ case 2: fep->u.ofdm.code_rate_LP = FEC_2_3; break;
+ case 3: fep->u.ofdm.code_rate_LP = FEC_3_4; break;
+ case 5: fep->u.ofdm.code_rate_LP = FEC_5_6; break;
+ case 7:
+ default: fep->u.ofdm.code_rate_LP = FEC_7_8; break;
+ }
- deb_stat("signal: mantisse = %d, exponent = %d\n",(sig >> 8) & 0xff, sig & 0xff);
- deb_stat("noise: mantisse = %d, exponent = %d\n",(noise >> 8) & 0xff, noise & 0xff);
- deb_stat("snr: %d\n",*snr);
return 0;
}
-static int dib3000mc_sleep(struct dvb_frontend* fe)
+static int dib3000mc_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fep)
{
- struct dib3000_state* state = fe->demodulator_priv;
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ struct dibx000_ofdm_channel ch;
- set_or(DIB3000MC_REG_CLK_CFG_7,DIB3000MC_CLK_CFG_7_PWR_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_1,DIB3000MC_CLK_CFG_1_POWER_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_2,DIB3000MC_CLK_CFG_2_POWER_DOWN);
- wr(DIB3000MC_REG_CLK_CFG_3,DIB3000MC_CLK_CFG_3_POWER_DOWN);
- return 0;
+ INIT_OFDM_CHANNEL(&ch);
+ FEP2DIB(fep,&ch);
+
+ state->current_bandwidth = fep->u.ofdm.bandwidth;
+ dib3000mc_set_bandwidth(fe, fep->u.ofdm.bandwidth);
+
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, fep);
+ msleep(100);
+ }
+
+ if (fep->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ||
+ fep->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ||
+ fep->u.ofdm.constellation == QAM_AUTO ||
+ fep->u.ofdm.code_rate_HP == FEC_AUTO) {
+ int i = 100, found;
+
+ dib3000mc_autosearch_start(fe, &ch);
+ do {
+ msleep(1);
+ found = dib3000mc_autosearch_is_irq(fe);
+ } while (found == 0 && i--);
+
+ dprintk("autosearch returns: %d\n",found);
+ if (found == 0 || found == 1)
+ return 0; // no channel found
+
+ dib3000mc_get_frontend(fe, fep);
+ FEP2DIB(fep,&ch);
+ }
+
+ /* make this a config parameter */
+ dib3000mc_set_output_mode(state, OUTMODE_MPEG2_FIFO);
+
+ return dib3000mc_tune(fe, &ch);
}
-static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
+static int dib3000mc_read_status(struct dvb_frontend *fe, fe_status_t *stat)
{
- tune->min_delay_ms = 1000;
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 lock = dib3000mc_read_word(state, 509);
+
+ *stat = 0;
+
+ if (lock & 0x8000)
+ *stat |= FE_HAS_SIGNAL;
+ if (lock & 0x3000)
+ *stat |= FE_HAS_CARRIER;
+ if (lock & 0x0100)
+ *stat |= FE_HAS_VITERBI;
+ if (lock & 0x0010)
+ *stat |= FE_HAS_SYNC;
+ if (lock & 0x0008)
+ *stat |= FE_HAS_LOCK;
+
return 0;
}
-static int dib3000mc_fe_init_nonmobile(struct dvb_frontend* fe)
+static int dib3000mc_read_ber(struct dvb_frontend *fe, u32 *ber)
{
- return dib3000mc_fe_init(fe, 0);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ *ber = (dib3000mc_read_word(state, 500) << 16) | dib3000mc_read_word(state, 501);
+ return 0;
}
-static int dib3000mc_set_frontend_and_tuner(struct dvb_frontend* fe, struct dvb_frontend_parameters *fep)
+static int dib3000mc_read_unc_blocks(struct dvb_frontend *fe, u32 *unc)
{
- return dib3000mc_set_frontend(fe, fep, 1);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ *unc = dib3000mc_read_word(state, 508);
+ return 0;
}
-static void dib3000mc_release(struct dvb_frontend* fe)
+static int dib3000mc_read_signal_strength(struct dvb_frontend *fe, u16 *strength)
{
- struct dib3000_state *state = fe->demodulator_priv;
- kfree(state);
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 val = dib3000mc_read_word(state, 392);
+ *strength = 65535 - val;
+ return 0;
}
-/* pid filter and transfer stuff */
-static int dib3000mc_pid_control(struct dvb_frontend *fe,int index, int pid,int onoff)
+static int dib3000mc_read_snr(struct dvb_frontend* fe, u16 *snr)
{
- struct dib3000_state *state = fe->demodulator_priv;
- pid = (onoff ? pid | DIB3000_ACTIVATE_PID_FILTERING : 0);
- wr(index+DIB3000MC_REG_FIRST_PID,pid);
+ *snr = 0x0000;
return 0;
}
-static int dib3000mc_fifo_control(struct dvb_frontend *fe, int onoff)
+static int dib3000mc_fe_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings *tune)
{
- struct dib3000_state *state = fe->demodulator_priv;
- u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
-
- deb_xfer("%s fifo\n",onoff ? "enabling" : "disabling");
-
- if (onoff) {
- deb_xfer("%d %x\n",tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
- wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_FIFO_UNFLUSH);
- } else {
- deb_xfer("%d %x\n",tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
- wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_FIFO_FLUSH);
- }
+ tune->min_delay_ms = 1000;
return 0;
}
-static int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
+static void dib3000mc_release(struct dvb_frontend *fe)
{
- struct dib3000_state *state = fe->demodulator_priv;
- u16 tmp = rd(DIB3000MC_REG_SMO_MODE);
-
- deb_xfer("%s pid parsing\n",onoff ? "enabling" : "disabling");
-
- if (onoff) {
- wr(DIB3000MC_REG_SMO_MODE,tmp | DIB3000MC_SMO_MODE_PID_PARSE);
- } else {
- wr(DIB3000MC_REG_SMO_MODE,tmp & DIB3000MC_SMO_MODE_NO_PID_PARSE);
- }
- return 0;
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ dibx000_exit_i2c_master(&state->i2c_master);
+ kfree(state);
}
-static int dib3000mc_tuner_pass_ctrl(struct dvb_frontend *fe, int onoff, u8 pll_addr)
+int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff)
{
- struct dib3000_state *state = fe->demodulator_priv;
- if (onoff) {
- wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_ENABLE(pll_addr));
- } else {
- wr(DIB3000MC_REG_TUNER, DIB3000_TUNER_WRITE_DISABLE(pll_addr));
- }
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ dib3000mc_write_word(state, 212 + index, onoff ? (1 << 13) | pid : 0);
return 0;
}
+EXPORT_SYMBOL(dib3000mc_pid_control);
-static int dib3000mc_demod_init(struct dib3000_state *state)
+int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff)
{
- u16 default_addr = 0x0a;
- /* first init */
- if (state->config.demod_address != default_addr) {
- deb_info("initializing the demod the first time. Setting demod addr to 0x%x\n",default_addr);
- wr(DIB3000MC_REG_ELEC_OUT,DIB3000MC_ELEC_OUT_DIV_OUT_ON);
- wr(DIB3000MC_REG_OUTMODE,DIB3000MC_OM_PAR_CONT_CLK);
-
- wr(DIB3000MC_REG_RST_I2C_ADDR,
- DIB3000MC_DEMOD_ADDR(default_addr) |
- DIB3000MC_DEMOD_ADDR_ON);
-
- state->config.demod_address = default_addr;
-
- wr(DIB3000MC_REG_RST_I2C_ADDR,
- DIB3000MC_DEMOD_ADDR(default_addr));
- } else
- deb_info("demod is already initialized. Demod addr: 0x%x\n",state->config.demod_address);
- return 0;
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ u16 tmp = dib3000mc_read_word(state, 206) & ~(1 << 4);
+ tmp |= (onoff << 4);
+ return dib3000mc_write_word(state, 206, tmp);
}
+EXPORT_SYMBOL(dib3000mc_pid_parse);
+void dib3000mc_set_config(struct dvb_frontend *fe, struct dib3000mc_config *cfg)
+{
+ struct dib3000mc_state *state = fe->demodulator_priv;
+ state->cfg = cfg;
+}
+EXPORT_SYMBOL(dib3000mc_set_config);
static struct dvb_frontend_ops dib3000mc_ops;
-struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
- struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+int dib3000mc_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr, u8 do_i2c_enum, struct dib3000mc_config cfg[], struct dvb_frontend *demod[])
{
- struct dib3000_state* state = NULL;
- u16 devid;
+ struct dib3000mc_state *st;
+ int k, num=0;
- /* allocate memory for the internal state */
- state = kzalloc(sizeof(struct dib3000_state), GFP_KERNEL);
- if (state == NULL)
- goto error;
+ if (no_of_demods < 1)
+ return -EINVAL;
- /* setup the state */
- state->i2c = i2c;
- memcpy(&state->config,config,sizeof(struct dib3000_config));
+ for (k = 0; k < no_of_demods; k++) {
+ st = kzalloc(sizeof(struct dib3000mc_state), GFP_KERNEL);
+ if (st == NULL)
+ goto error;
- /* check for the correct demod */
- if (rd(DIB3000_REG_MANUFACTOR_ID) != DIB3000_I2C_ID_DIBCOM)
- goto error;
+ num++;
- devid = rd(DIB3000_REG_DEVICE_ID);
- if (devid != DIB3000MC_DEVICE_ID && devid != DIB3000P_DEVICE_ID)
- goto error;
+ st->cfg = &cfg[k];
+ // st->gpio_val = cfg[k].gpio_val;
+ // st->gpio_dir = cfg[k].gpio_dir;
+ st->i2c_adap = i2c_adap;
- switch (devid) {
- case DIB3000MC_DEVICE_ID:
- info("Found a DiBcom 3000M-C, interesting...");
- break;
- case DIB3000P_DEVICE_ID:
- info("Found a DiBcom 3000P.");
- break;
- }
+ demod[k] = &st->demod;
+ demod[k]->demodulator_priv = st;
+ memcpy(&st->demod.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
- /* create dvb_frontend */
- memcpy(&state->frontend.ops, &dib3000mc_ops, sizeof(struct dvb_frontend_ops));
- state->frontend.demodulator_priv = state;
+// INIT_COMPONENT_REGISTER_ACCESS(&st->register_access, 12, 16, dib7000p_register_read, dib7000p_register_write, st);
+// demod[k]->register_access = &st->register_access;
+ }
- /* set the xfer operations */
- xfer_ops->pid_parse = dib3000mc_pid_parse;
- xfer_ops->fifo_ctrl = dib3000mc_fifo_control;
- xfer_ops->pid_ctrl = dib3000mc_pid_control;
- xfer_ops->tuner_pass_ctrl = dib3000mc_tuner_pass_ctrl;
+ if (do_i2c_enum) {
+ if (dib3000mc_i2c_enumeration(demod,no_of_demods,default_addr) != 0)
+ goto error;
+ } else {
+ st = demod[0]->demodulator_priv;
+ st->i2c_addr = default_addr;
+ if (dib3000mc_identify(st) != 0)
+ goto error;
+ }
- dib3000mc_demod_init(state);
+ for (k = 0; k < num; k++) {
+ st = demod[k]->demodulator_priv;
+ dibx000_init_i2c_master(&st->i2c_master, DIB3000MC, st->i2c_adap, st->i2c_addr);
+ }
- return &state->frontend;
+ return 0;
error:
- kfree(state);
- return NULL;
+ for (k = 0; k < num; k++) {
+ kfree(demod[k]->demodulator_priv);
+ demod[k] = NULL;
+ }
+ return -EINVAL;
}
+
EXPORT_SYMBOL(dib3000mc_attach);
static struct dvb_frontend_ops dib3000mc_ops = {
-
.info = {
- .name = "DiBcom 3000P/M-C DVB-T",
- .type = FE_OFDM,
- .frequency_min = 44250000,
- .frequency_max = 867250000,
- .frequency_stepsize = 62500,
+ .name = "DiBcom 3000MC/P",
+ .type = FE_OFDM,
+ .frequency_min = 44250000,
+ .frequency_max = 867250000,
+ .frequency_stepsize = 62500,
.caps = FE_CAN_INVERSION_AUTO |
- FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
- FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
- FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
- FE_CAN_TRANSMISSION_MODE_AUTO |
- FE_CAN_GUARD_INTERVAL_AUTO |
- FE_CAN_RECOVER |
- FE_CAN_HIERARCHY_AUTO,
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK | FE_CAN_QAM_16 | FE_CAN_QAM_64 | FE_CAN_QAM_AUTO |
+ FE_CAN_TRANSMISSION_MODE_AUTO |
+ FE_CAN_GUARD_INTERVAL_AUTO |
+ FE_CAN_RECOVER |
+ FE_CAN_HIERARCHY_AUTO,
},
- .release = dib3000mc_release,
+ .release = dib3000mc_release,
- .init = dib3000mc_fe_init_nonmobile,
- .sleep = dib3000mc_sleep,
+ .init = dib3000mc_init,
+ .sleep = dib3000mc_sleep,
- .set_frontend = dib3000mc_set_frontend_and_tuner,
- .get_frontend = dib3000mc_get_frontend,
- .get_tune_settings = dib3000mc_fe_get_tune_settings,
+ .set_frontend = dib3000mc_set_frontend,
+ .get_tune_settings = dib3000mc_fe_get_tune_settings,
+ .get_frontend = dib3000mc_get_frontend,
- .read_status = dib3000mc_read_status,
- .read_ber = dib3000mc_read_ber,
+ .read_status = dib3000mc_read_status,
+ .read_ber = dib3000mc_read_ber,
.read_signal_strength = dib3000mc_read_signal_strength,
- .read_snr = dib3000mc_read_snr,
- .read_ucblocks = dib3000mc_read_unc_blocks,
+ .read_snr = dib3000mc_read_snr,
+ .read_ucblocks = dib3000mc_read_unc_blocks,
};
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Driver for the DiBcom 3000MC/P COFDM demodulator");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dib3000mc.h b/drivers/media/dvb/frontends/dib3000mc.h
new file mode 100644
index 00000000000..fd0b2e75599
--- /dev/null
+++ b/drivers/media/dvb/frontends/dib3000mc.h
@@ -0,0 +1,58 @@
+/*
+ * Driver for DiBcom DiB3000MC/P-demodulator.
+ *
+ * Copyright (C) 2004-6 DiBcom (http://www.dibcom.fr/)
+ * Copyright (C) 2004-5 Patrick Boettcher (patrick.boettcher\@desy.de)
+ *
+ * This code is partially based on the previous dib3000mc.c .
+ *
+ * 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, version 2.
+ */
+#ifndef DIB3000MC_H
+#define DIB3000MC_H
+
+#include "dibx000_common.h"
+
+struct dib3000mc_config {
+ struct dibx000_agc_config *agc;
+
+ u8 phase_noise_mode;
+ u8 impulse_noise_mode;
+
+ u8 pwm3_inversion;
+ u8 use_pwm3;
+ u16 pwm3_value;
+
+ u16 max_time;
+ u16 ln_adc_level;
+
+ u8 mobile_mode;
+
+ u8 output_mpeg2_in_188_bytes;
+};
+
+#define DEFAULT_DIB3000MC_I2C_ADDRESS 16
+#define DEFAULT_DIB3000P_I2C_ADDRESS 24
+
+#if defined(CONFIG_DVB_DIB3000MC) || defined(CONFIG_DVB_DIB3000MC_MODULE)
+extern int dib3000mc_attach(struct i2c_adapter *i2c_adap, int no_of_demods, u8 default_addr,
+ u8 do_i2c_enum, struct dib3000mc_config cfg[], struct dvb_frontend *demod[]);
+#else
+static inline struct dvb_frontend* dib3000mc_attach(const struct dib3000_config* config,
+ struct i2c_adapter* i2c, struct dib_fe_xfer_ops *xfer_ops)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_DIB3000MC
+
+extern struct i2c_adapter * dib3000mc_get_tuner_i2c_master(struct dvb_frontend *demod, int gating);
+
+extern int dib3000mc_pid_control(struct dvb_frontend *fe, int index, int pid,int onoff);
+extern int dib3000mc_pid_parse(struct dvb_frontend *fe, int onoff);
+
+extern void dib3000mc_set_config(struct dvb_frontend *, struct dib3000mc_config *);
+
+#endif
diff --git a/drivers/media/dvb/frontends/dib3000mc_priv.h b/drivers/media/dvb/frontends/dib3000mc_priv.h
deleted file mode 100644
index 2930aac7591..00000000000
--- a/drivers/media/dvb/frontends/dib3000mc_priv.h
+++ /dev/null
@@ -1,428 +0,0 @@
-/*
- * dib3000mc_priv.h
- *
- * Copyright (C) 2004 Patrick Boettcher (patrick.boettcher@desy.de)
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation, version 2.
- *
- * for more information see dib3000mc.c .
- */
-
-#ifndef __DIB3000MC_PRIV_H__
-#define __DIB3000MC_PRIV_H__
-
-/*
- * Demodulator parameters
- * reg: 0 1 1 1 11 11 111
- * | | | | | |
- * | | | | | +-- alpha (000=0, 001=1, 010=2, 100=4)
- * | | | | +----- constellation (00=QPSK, 01=16QAM, 10=64QAM)
- * | | | +-------- guard (00=1/32, 01=1/16, 10=1/8, 11=1/4)
- * | | +----------- transmission mode (0=2k, 1=8k)
- * | |
- * | +-------------- restart autosearch for parameters
- * +---------------- restart the demodulator
- * reg: 181 1 111 1
- * | | |
- * | | +- FEC applies for HP or LP (0=LP, 1=HP)
- * | +---- FEC rate (001=1/2, 010=2/3, 011=3/4, 101=5/6, 111=7/8)
- * +------- hierarchy on (0=no, 1=yes)
- */
-
-/* demodulator tuning parameter and restart options */
-#define DIB3000MC_REG_DEMOD_PARM ( 0)
-#define DIB3000MC_DEMOD_PARM(a,c,g,t) ( \
- (0x7 & a) | \
- ((0x3 & c) << 3) | \
- ((0x3 & g) << 5) | \
- ((0x1 & t) << 7) )
-#define DIB3000MC_DEMOD_RST_AUTO_SRCH_ON (1 << 8)
-#define DIB3000MC_DEMOD_RST_AUTO_SRCH_OFF (0 << 8)
-#define DIB3000MC_DEMOD_RST_DEMOD_ON (1 << 9)
-#define DIB3000MC_DEMOD_RST_DEMOD_OFF (0 << 9)
-
-/* register for hierarchy parameters */
-#define DIB3000MC_REG_HRCH_PARM ( 181)
-#define DIB3000MC_HRCH_PARM(s,f,h) ( \
- (0x1 & s) | \
- ((0x7 & f) << 1) | \
- ((0x1 & h) << 4) )
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_1 ( 1)
-#define DIB3000MC_UNK_1 ( 0x04)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_2 ( 2)
-#define DIB3000MC_UNK_2 ( 0x04)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_3 ( 3)
-#define DIB3000MC_UNK_3 (0x1000)
-
-#define DIB3000MC_REG_UNK_4 ( 4)
-#define DIB3000MC_UNK_4 (0x0814)
-
-/* timeout ??? */
-#define DIB3000MC_REG_SEQ_TPS ( 5)
-#define DIB3000MC_SEQ_TPS_DEFAULT ( 1)
-#define DIB3000MC_SEQ_TPS(s,t) ( \
- ((s & 0x0f) << 4) | \
- ((t & 0x01) << 8) )
-#define DIB3000MC_IS_TPS(v) ((v << 8) & 0x1)
-#define DIB3000MC_IS_AS(v) ((v >> 4) & 0xf)
-
-/* parameters for the bandwidth */
-#define DIB3000MC_REG_BW_TIMOUT_MSB ( 6)
-#define DIB3000MC_REG_BW_TIMOUT_LSB ( 7)
-
-static u16 dib3000mc_reg_bandwidth[] = { 6,7,8,9,10,11,16,17 };
-
-/*static u16 dib3000mc_bandwidth_5mhz[] =
- { 0x28, 0x9380, 0x87, 0x4100, 0x2a4, 0x4500, 0x1, 0xb0d0 };*/
-
-static u16 dib3000mc_bandwidth_6mhz[] =
- { 0x21, 0xd040, 0x70, 0xb62b, 0x233, 0x8ed5, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_bandwidth_7mhz[] =
- { 0x1c, 0xfba5, 0x60, 0x9c25, 0x1e3, 0x0cb7, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_bandwidth_8mhz[] =
- { 0x19, 0x5c30, 0x54, 0x88a0, 0x1a6, 0xab20, 0x1, 0xb0d0 };
-
-static u16 dib3000mc_reg_bandwidth_general[] = { 12,13,14,15 };
-static u16 dib3000mc_bandwidth_general[] = { 0x0000, 0x03e8, 0x0000, 0x03f2 };
-
-/* lock mask */
-#define DIB3000MC_REG_LOCK_MASK ( 15)
-#define DIB3000MC_ACTIVATE_LOCK_MASK (0x0800)
-
-/* reset the uncorrected packet count (??? do it 5 times) */
-#define DIB3000MC_REG_RST_UNC ( 18)
-#define DIB3000MC_RST_UNC_ON ( 1)
-#define DIB3000MC_RST_UNC_OFF ( 0)
-
-#define DIB3000MC_REG_UNK_19 ( 19)
-#define DIB3000MC_UNK_19 ( 0)
-
-/* DDS frequency value (IF position) and inversion bit */
-#define DIB3000MC_REG_INVERSION ( 21)
-#define DIB3000MC_REG_SET_DDS_FREQ_MSB ( 21)
-#define DIB3000MC_DDS_FREQ_MSB_INV_OFF (0x0164)
-#define DIB3000MC_DDS_FREQ_MSB_INV_ON (0x0364)
-
-#define DIB3000MC_REG_SET_DDS_FREQ_LSB ( 22)
-#define DIB3000MC_DDS_FREQ_LSB (0x463d)
-
-/* timing frequencies setting */
-#define DIB3000MC_REG_TIMING_FREQ_MSB ( 23)
-#define DIB3000MC_REG_TIMING_FREQ_LSB ( 24)
-#define DIB3000MC_CLOCK_REF (0x151fd1)
-
-//static u16 dib3000mc_reg_timing_freq[] = { 23,24 };
-
-//static u16 dib3000mc_timing_freq[][2] = {
-// { 0x69, 0x9f18 }, /* 5 MHz */
-// { 0x7e ,0xbee9 }, /* 6 MHz */
-// { 0x93 ,0xdebb }, /* 7 MHz */
-// { 0xa8 ,0xfe8c }, /* 8 MHz */
-//};
-
-/* timeout ??? */
-static u16 dib3000mc_reg_offset[] = { 26,33 };
-
-static u16 dib3000mc_offset[][2] = {
- { 26240, 5 }, /* default */
- { 30336, 6 }, /* 8K */
- { 38528, 8 }, /* 2K */
-};
-
-#define DIB3000MC_REG_ISI ( 29)
-#define DIB3000MC_ISI_DEFAULT (0x1073)
-#define DIB3000MC_ISI_ACTIVATE (0x0000)
-#define DIB3000MC_ISI_INHIBIT (0x0200)
-
-/* impulse noise control */
-static u16 dib3000mc_reg_imp_noise_ctl[] = { 34,35 };
-
-static u16 dib3000mc_imp_noise_ctl[][2] = {
- { 0x1294, 0x1ff8 }, /* mode 0 */
- { 0x1294, 0x1ff8 }, /* mode 1 */
- { 0x1294, 0x1ff8 }, /* mode 2 */
- { 0x1294, 0x1ff8 }, /* mode 3 */
- { 0x1294, 0x1ff8 }, /* mode 4 */
-};
-
-/* AGC registers */
-static u16 dib3000mc_reg_agc[] = {
- 36,37,38,39,42,43,44,45,46,47,48,49
-};
-
-static u16 dib3000mc_agc_tuner[][12] = {
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xcf5c, 0x6666,
- 0xbae1, 0xa148, 0x3b5e, 0x3c1c, 0x001a, 0x2019
- }, /* TUNER_PANASONIC_ENV77H04D5, */
-
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xdc29, 0x570a,
- 0xbae1, 0x8ccd, 0x3b6d, 0x551d, 0x000a, 0x951e
- }, /* TUNER_PANASONIC_ENV57H13D5, TUNER_PANASONIC_ENV57H12D5 */
-
- { 0x0051, 0x301d, 0x0000, 0x1cc7, 0xffff, 0xffff,
- 0xffff, 0x0000, 0xfdfd, 0x4040, 0x00fd, 0x4040
- }, /* TUNER_SAMSUNG_DTOS333IH102, TUNER_RFAGCIN_UNKNOWN */
-
- { 0x0196, 0x301d, 0x0000, 0x1cc7, 0xbd71, 0x5c29,
- 0xb5c3, 0x6148, 0x6569, 0x5127, 0x0033, 0x3537
- }, /* TUNER_PROVIDER_X */
- /* TODO TUNER_PANASONIC_ENV57H10D8, TUNER_PANASONIC_ENV57H11D8 */
-};
-
-/* AGC loop bandwidth */
-static u16 dib3000mc_reg_agc_bandwidth[] = { 40,41 };
-static u16 dib3000mc_agc_bandwidth[] = { 0x119,0x330 };
-
-static u16 dib3000mc_reg_agc_bandwidth_general[] = { 50,51,52,53,54 };
-static u16 dib3000mc_agc_bandwidth_general[] =
- { 0x8000, 0x91ca, 0x01ba, 0x0087, 0x0087 };
-
-#define DIB3000MC_REG_IMP_NOISE_55 ( 55)
-#define DIB3000MC_IMP_NEW_ALGO(w) (w | (1<<10))
-
-/* Impulse noise params */
-static u16 dib3000mc_reg_impulse_noise[] = { 55,56,57 };
-static u16 dib3000mc_impluse_noise[][3] = {
- { 0x489, 0x89, 0x72 }, /* 5 MHz */
- { 0x4a5, 0xa5, 0x89 }, /* 6 MHz */
- { 0x4c0, 0xc0, 0xa0 }, /* 7 MHz */
- { 0x4db, 0xdb, 0xb7 }, /* 8 Mhz */
-};
-
-static u16 dib3000mc_reg_fft[] = {
- 58,59,60,61,62,63,64,65,66,67,68,69,
- 70,71,72,73,74,75,76,77,78,79,80,81,
- 82,83,84,85,86
-};
-
-static u16 dib3000mc_fft_modes[][29] = {
- { 0x38, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
- 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
- 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
- 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
- 0x3ffe, 0x5b3, 0x3feb, 0x76, 0x0, 0xd
- }, /* fft mode 0 */
- { 0x3b, 0x6d9, 0x3f28, 0x7a7, 0x3a74, 0x196, 0x32a, 0x48c,
- 0x3ffe, 0x7f3, 0x2d94, 0x76, 0x53d,
- 0x3ff8, 0x7e3, 0x3320, 0x76, 0x5b3,
- 0x3feb, 0x7d2, 0x365e, 0x76, 0x48c,
- 0x3ffe, 0x5b3, 0x3feb, 0x0, 0x8200, 0xd
- }, /* fft mode 1 */
-};
-
-#define DIB3000MC_REG_UNK_88 ( 88)
-#define DIB3000MC_UNK_88 (0x0410)
-
-static u16 dib3000mc_reg_bw[] = { 93,94,95,96,97,98 };
-static u16 dib3000mc_bw[][6] = {
- { 0,0,0,0,0,0 }, /* 5 MHz */
- { 0,0,0,0,0,0 }, /* 6 MHz */
- { 0,0,0,0,0,0 }, /* 7 MHz */
- { 0x20, 0x21, 0x20, 0x23, 0x20, 0x27 }, /* 8 MHz */
-};
-
-
-/* phase noise control */
-#define DIB3000MC_REG_UNK_99 ( 99)
-#define DIB3000MC_UNK_99 (0x0220)
-
-#define DIB3000MC_REG_SCAN_BOOST ( 100)
-#define DIB3000MC_SCAN_BOOST_ON ((11 << 6) + 6)
-#define DIB3000MC_SCAN_BOOST_OFF ((16 << 6) + 9)
-
-/* timeout ??? */
-#define DIB3000MC_REG_UNK_110 ( 110)
-#define DIB3000MC_UNK_110 ( 3277)
-
-#define DIB3000MC_REG_UNK_111 ( 111)
-#define DIB3000MC_UNK_111_PH_N_MODE_0 ( 0)
-#define DIB3000MC_UNK_111_PH_N_MODE_1 (1 << 1)
-
-/* superious rm config */
-#define DIB3000MC_REG_UNK_120 ( 120)
-#define DIB3000MC_UNK_120 ( 8207)
-
-#define DIB3000MC_REG_UNK_133 ( 133)
-#define DIB3000MC_UNK_133 ( 15564)
-
-#define DIB3000MC_REG_UNK_134 ( 134)
-#define DIB3000MC_UNK_134 ( 0)
-
-/* adapter config for constellation */
-static u16 dib3000mc_reg_adp_cfg[] = { 129, 130, 131, 132 };
-
-static u16 dib3000mc_adp_cfg[][4] = {
- { 0x99a, 0x7fae, 0x333, 0x7ff0 }, /* QPSK */
- { 0x23d, 0x7fdf, 0x0a4, 0x7ff0 }, /* 16-QAM */
- { 0x148, 0x7ff0, 0x0a4, 0x7ff8 }, /* 64-QAM */
-};
-
-static u16 dib3000mc_reg_mobile_mode[] = { 139, 140, 141, 175, 1032 };
-
-static u16 dib3000mc_mobile_mode[][5] = {
- { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* fixed */
- { 0x01, 0x0, 0x0, 0x00, 0x12c }, /* portable */
- { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* mobile */
- { 0x00, 0x0, 0x0, 0x02, 0x000 }, /* auto */
-};
-
-#define DIB3000MC_REG_DIVERSITY1 ( 177)
-#define DIB3000MC_DIVERSITY1_DEFAULT ( 1)
-
-#define DIB3000MC_REG_DIVERSITY2 ( 178)
-#define DIB3000MC_DIVERSITY2_DEFAULT ( 1)
-
-#define DIB3000MC_REG_DIVERSITY3 ( 180)
-#define DIB3000MC_DIVERSITY3_IN_OFF (0xfff0)
-#define DIB3000MC_DIVERSITY3_IN_ON (0xfff6)
-
-#define DIB3000MC_REG_FEC_CFG ( 195)
-#define DIB3000MC_FEC_CFG ( 0x10)
-
-/*
- * reg 206, output mode
- * 1111 1111
- * |||| ||||
- * |||| |||+- unk
- * |||| ||+-- unk
- * |||| |+--- unk (on by default)
- * |||| +---- fifo_ctrl (1 = inhibit (flushed), 0 = active (unflushed))
- * |||+------ pid_parse (1 = enabled, 0 = disabled)
- * ||+------- outp_188 (1 = TS packet size 188, 0 = packet size 204)
- * |+-------- unk
- * +--------- unk
- */
-
-#define DIB3000MC_REG_SMO_MODE ( 206)
-#define DIB3000MC_SMO_MODE_DEFAULT (1 << 2)
-#define DIB3000MC_SMO_MODE_FIFO_FLUSH (1 << 3)
-#define DIB3000MC_SMO_MODE_FIFO_UNFLUSH (0xfff7)
-#define DIB3000MC_SMO_MODE_PID_PARSE (1 << 4)
-#define DIB3000MC_SMO_MODE_NO_PID_PARSE (0xffef)
-#define DIB3000MC_SMO_MODE_188 (1 << 5)
-#define DIB3000MC_SMO_MODE_SLAVE (DIB3000MC_SMO_MODE_DEFAULT | \
- DIB3000MC_SMO_MODE_188 | DIB3000MC_SMO_MODE_PID_PARSE | (1<<1))
-
-#define DIB3000MC_REG_FIFO_THRESHOLD ( 207)
-#define DIB3000MC_FIFO_THRESHOLD_DEFAULT ( 1792)
-#define DIB3000MC_FIFO_THRESHOLD_SLAVE ( 512)
-/*
- * pidfilter
- * it is not a hardware pidfilter but a filter which drops all pids
- * except the ones set. When connected to USB1.1 bandwidth this is important.
- * DiB3000P/M-C can filter up to 32 PIDs
- */
-#define DIB3000MC_REG_FIRST_PID ( 212)
-#define DIB3000MC_NUM_PIDS ( 32)
-
-#define DIB3000MC_REG_OUTMODE ( 244)
-#define DIB3000MC_OM_PARALLEL_GATED_CLK ( 0)
-#define DIB3000MC_OM_PAR_CONT_CLK (1 << 11)
-#define DIB3000MC_OM_SERIAL (2 << 11)
-#define DIB3000MC_OM_DIVOUT_ON (4 << 11)
-#define DIB3000MC_OM_SLAVE (DIB3000MC_OM_DIVOUT_ON | DIB3000MC_OM_PAR_CONT_CLK)
-
-#define DIB3000MC_REG_RF_POWER ( 392)
-
-#define DIB3000MC_REG_FFT_POSITION ( 407)
-
-#define DIB3000MC_REG_DDS_FREQ_MSB ( 414)
-#define DIB3000MC_REG_DDS_FREQ_LSB ( 415)
-
-#define DIB3000MC_REG_TIMING_OFFS_MSB ( 416)
-#define DIB3000MC_REG_TIMING_OFFS_LSB ( 417)
-
-#define DIB3000MC_REG_TUNING_PARM ( 458)
-#define DIB3000MC_TP_QAM(v) ((v >> 13) & 0x03)
-#define DIB3000MC_TP_HRCH(v) ((v >> 12) & 0x01)
-#define DIB3000MC_TP_ALPHA(v) ((v >> 9) & 0x07)
-#define DIB3000MC_TP_FFT(v) ((v >> 8) & 0x01)
-#define DIB3000MC_TP_FEC_CR_HP(v) ((v >> 5) & 0x07)
-#define DIB3000MC_TP_FEC_CR_LP(v) ((v >> 2) & 0x07)
-#define DIB3000MC_TP_GUARD(v) (v & 0x03)
-
-#define DIB3000MC_REG_SIGNAL_NOISE_MSB ( 483)
-#define DIB3000MC_REG_SIGNAL_NOISE_LSB ( 484)
-
-#define DIB3000MC_REG_MER ( 485)
-
-#define DIB3000MC_REG_BER_MSB ( 500)
-#define DIB3000MC_REG_BER_LSB ( 501)
-
-#define DIB3000MC_REG_PACKET_ERRORS ( 503)
-
-#define DIB3000MC_REG_PACKET_ERROR_COUNT ( 506)
-
-#define DIB3000MC_REG_LOCK_507 ( 507)
-#define DIB3000MC_LOCK_507 (0x0002) // ? name correct ?
-
-#define DIB3000MC_REG_LOCKING ( 509)
-#define DIB3000MC_AGC_LOCK(v) (v & 0x8000)
-#define DIB3000MC_CARRIER_LOCK(v) (v & 0x2000)
-#define DIB3000MC_MPEG_SYNC_LOCK(v) (v & 0x0080)
-#define DIB3000MC_MPEG_DATA_LOCK(v) (v & 0x0040)
-#define DIB3000MC_TPS_LOCK(v) (v & 0x0004)
-
-#define DIB3000MC_REG_AS_IRQ ( 511)
-#define DIB3000MC_AS_IRQ_SUCCESS (1 << 1)
-#define DIB3000MC_AS_IRQ_FAIL ( 1)
-
-#define DIB3000MC_REG_TUNER ( 769)
-
-#define DIB3000MC_REG_RST_I2C_ADDR ( 1024)
-#define DIB3000MC_DEMOD_ADDR_ON ( 1)
-#define DIB3000MC_DEMOD_ADDR(a) ((a << 4) & 0x03F0)
-
-#define DIB3000MC_REG_RESTART ( 1027)
-#define DIB3000MC_RESTART_OFF (0x0000)
-#define DIB3000MC_RESTART_AGC (0x0800)
-#define DIB3000MC_RESTART_CONFIG (0x8000)
-
-#define DIB3000MC_REG_RESTART_VIT ( 1028)
-#define DIB3000MC_RESTART_VIT_OFF ( 0)
-#define DIB3000MC_RESTART_VIT_ON ( 1)
-
-#define DIB3000MC_REG_CLK_CFG_1 ( 1031)
-#define DIB3000MC_CLK_CFG_1_POWER_UP ( 0)
-#define DIB3000MC_CLK_CFG_1_POWER_DOWN (0xffff)
-
-#define DIB3000MC_REG_CLK_CFG_2 ( 1032)
-#define DIB3000MC_CLK_CFG_2_PUP_FIXED (0x012c)
-#define DIB3000MC_CLK_CFG_2_PUP_PORT (0x0104)
-#define DIB3000MC_CLK_CFG_2_PUP_MOBILE (0x0000)
-#define DIB3000MC_CLK_CFG_2_POWER_DOWN (0xffff)
-
-#define DIB3000MC_REG_CLK_CFG_3 ( 1033)
-#define DIB3000MC_CLK_CFG_3_POWER_UP ( 0)
-#define DIB3000MC_CLK_CFG_3_POWER_DOWN (0xfff5)
-
-#define DIB3000MC_REG_CLK_CFG_7 ( 1037)
-#define DIB3000MC_CLK_CFG_7_INIT ( 12592)
-#define DIB3000MC_CLK_CFG_7_POWER_UP (~0x0003)
-#define DIB3000MC_CLK_CFG_7_PWR_DOWN (0x0003)
-#define DIB3000MC_CLK_CFG_7_DIV_IN_OFF (1 << 8)
-
-/* was commented out ??? */
-#define DIB3000MC_REG_CLK_CFG_8 ( 1038)
-#define DIB3000MC_CLK_CFG_8_POWER_UP (0x160c)
-
-#define DIB3000MC_REG_CLK_CFG_9 ( 1039)
-#define DIB3000MC_CLK_CFG_9_POWER_UP ( 0)
-
-/* also clock ??? */
-#define DIB3000MC_REG_ELEC_OUT ( 1040)
-#define DIB3000MC_ELEC_OUT_HIGH_Z ( 0)
-#define DIB3000MC_ELEC_OUT_DIV_OUT_ON ( 1)
-#define DIB3000MC_ELEC_OUT_SLAVE ( 3)
-
-#endif
diff --git a/drivers/media/dvb/frontends/dibx000_common.c b/drivers/media/dvb/frontends/dibx000_common.c
new file mode 100644
index 00000000000..a18c8f45a2e
--- /dev/null
+++ b/drivers/media/dvb/frontends/dibx000_common.c
@@ -0,0 +1,152 @@
+#include <linux/i2c.h>
+
+#include "dibx000_common.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "turn on debugging (default: 0)");
+
+#define dprintk(args...) do { if (debug) { printk(KERN_DEBUG "DiBX000: "); printk(args); } } while (0)
+
+static int dibx000_write_word(struct dibx000_i2c_master *mst, u16 reg, u16 val)
+{
+ u8 b[4] = {
+ (reg >> 8) & 0xff, reg & 0xff,
+ (val >> 8) & 0xff, val & 0xff,
+ };
+ struct i2c_msg msg = {
+ .addr = mst->i2c_addr, .flags = 0, .buf = b, .len = 4
+ };
+ return i2c_transfer(mst->i2c_adap, &msg, 1) != 1 ? -EREMOTEIO : 0;
+}
+
+
+static int dibx000_i2c_select_interface(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf)
+{
+ if (mst->device_rev > DIB3000MC && mst->selected_interface != intf) {
+ dprintk("selecting interface: %d\n",intf);
+ mst->selected_interface = intf;
+ return dibx000_write_word(mst, mst->base_reg + 4, intf);
+ }
+ return 0;
+}
+
+static int dibx000_i2c_gate_ctrl(struct dibx000_i2c_master *mst, u8 tx[4], u8 addr, int onoff)
+{
+ u16 val;
+
+
+ if (onoff)
+ val = addr << 8; // bit 7 = use master or not, if 0, the gate is open
+ else
+ val = 1 << 7;
+
+ if (mst->device_rev > DIB7000)
+ val <<= 1;
+
+ tx[0] = (((mst->base_reg + 1) >> 8) & 0xff);
+ tx[1] = ( (mst->base_reg + 1) & 0xff);
+ tx[2] = val >> 8;
+ tx[3] = val & 0xff;
+
+ return 0;
+}
+
+static u32 dibx000_i2c_func(struct i2c_adapter *adapter)
+{
+ return I2C_FUNC_I2C;
+}
+
+static int dibx000_i2c_gated_tuner_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msg[], int num)
+{
+ struct dibx000_i2c_master *mst = i2c_get_adapdata(i2c_adap);
+ struct i2c_msg m[2 + num];
+ u8 tx_open[4], tx_close[4];
+
+ memset(m,0, sizeof(struct i2c_msg) * (2 + num));
+
+ dibx000_i2c_select_interface(mst, DIBX000_I2C_INTERFACE_TUNER);
+
+ dibx000_i2c_gate_ctrl(mst, tx_open, msg[0].addr, 1);
+ m[0].addr = mst->i2c_addr;
+ m[0].buf = tx_open;
+ m[0].len = 4;
+
+ memcpy(&m[1], msg, sizeof(struct i2c_msg) * num);
+
+ dibx000_i2c_gate_ctrl(mst, tx_close, 0, 0);
+ m[num+1].addr = mst->i2c_addr;
+ m[num+1].buf = tx_close;
+ m[num+1].len = 4;
+
+ return i2c_transfer(mst->i2c_adap, m, 2+num) == 2 + num ? num : -EIO;
+}
+
+static struct i2c_algorithm dibx000_i2c_gated_tuner_algo = {
+ .master_xfer = dibx000_i2c_gated_tuner_xfer,
+ .functionality = dibx000_i2c_func,
+};
+
+struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating)
+{
+ struct i2c_adapter *i2c = NULL;
+
+ switch (intf) {
+ case DIBX000_I2C_INTERFACE_TUNER:
+ if (gating)
+ i2c = &mst->gated_tuner_i2c_adap;
+ break;
+ default:
+ printk(KERN_ERR "DiBX000: incorrect I2C interface selected\n");
+ break;
+ }
+
+ return i2c;
+}
+EXPORT_SYMBOL(dibx000_get_i2c_adapter);
+
+static int i2c_adapter_init(struct i2c_adapter *i2c_adap, struct i2c_algorithm *algo, const char name[I2C_NAME_SIZE], struct dibx000_i2c_master *mst)
+{
+ strncpy(i2c_adap->name, name, I2C_NAME_SIZE);
+ i2c_adap->class = I2C_CLASS_TV_DIGITAL,
+ i2c_adap->algo = algo;
+ i2c_adap->algo_data = NULL;
+ i2c_set_adapdata(i2c_adap, mst);
+ if (i2c_add_adapter(i2c_adap) < 0)
+ return -ENODEV;
+ return 0;
+}
+
+int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr)
+{
+ u8 tx[4];
+ struct i2c_msg m = { .addr = i2c_addr >> 1, .buf = tx, .len = 4 };
+
+ mst->device_rev = device_rev;
+ mst->i2c_adap = i2c_adap;
+ mst->i2c_addr = i2c_addr >> 1;
+
+ if (device_rev == DIB7000P)
+ mst->base_reg = 1024;
+ else
+ mst->base_reg = 768;
+
+ if (i2c_adapter_init(&mst->gated_tuner_i2c_adap, &dibx000_i2c_gated_tuner_algo, "DiBX000 tuner I2C bus", mst) != 0)
+ printk(KERN_ERR "DiBX000: could not initialize the tuner i2c_adapter\n");
+
+ /* initialize the i2c-master by closing the gate */
+ dibx000_i2c_gate_ctrl(mst, tx, 0, 0);
+
+ return i2c_transfer(i2c_adap, &m, 1) == 1;
+}
+EXPORT_SYMBOL(dibx000_init_i2c_master);
+
+void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst)
+{
+ i2c_del_adapter(&mst->gated_tuner_i2c_adap);
+}
+EXPORT_SYMBOL(dibx000_exit_i2c_master);
+
+MODULE_AUTHOR("Patrick Boettcher <pboettcher@dibcom.fr>");
+MODULE_DESCRIPTION("Common function the DiBcom demodulator family");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/dibx000_common.h b/drivers/media/dvb/frontends/dibx000_common.h
new file mode 100644
index 00000000000..bb0c65f8aee
--- /dev/null
+++ b/drivers/media/dvb/frontends/dibx000_common.h
@@ -0,0 +1,166 @@
+#ifndef DIBX000_COMMON_H
+#define DIBX000_COMMON_H
+
+enum dibx000_i2c_interface {
+ DIBX000_I2C_INTERFACE_TUNER = 0,
+ DIBX000_I2C_INTERFACE_GPIO_1_2 = 1,
+ DIBX000_I2C_INTERFACE_GPIO_3_4 = 2
+};
+
+struct dibx000_i2c_master {
+#define DIB3000MC 1
+#define DIB7000 2
+#define DIB7000P 11
+#define DIB7000MC 12
+ u16 device_rev;
+
+ enum dibx000_i2c_interface selected_interface;
+
+// struct i2c_adapter tuner_i2c_adap;
+ struct i2c_adapter gated_tuner_i2c_adap;
+
+ struct i2c_adapter *i2c_adap;
+ u8 i2c_addr;
+
+ u16 base_reg;
+};
+
+extern int dibx000_init_i2c_master(struct dibx000_i2c_master *mst, u16 device_rev, struct i2c_adapter *i2c_adap, u8 i2c_addr);
+extern struct i2c_adapter * dibx000_get_i2c_adapter(struct dibx000_i2c_master *mst, enum dibx000_i2c_interface intf, int gating);
+extern void dibx000_exit_i2c_master(struct dibx000_i2c_master *mst);
+
+#define BAND_LBAND 0x01
+#define BAND_UHF 0x02
+#define BAND_VHF 0x04
+
+struct dibx000_agc_config {
+ /* defines the capabilities of this AGC-setting - using the BAND_-defines*/
+ u8 band_caps;
+
+ u16 setup;
+
+ u16 inv_gain;
+ u16 time_stabiliz;
+
+ u8 alpha_level;
+ u16 thlock;
+
+ u8 wbd_inv;
+ u16 wbd_ref;
+ u8 wbd_sel;
+ u8 wbd_alpha;
+
+ u16 agc1_max;
+ u16 agc1_min;
+ u16 agc2_max;
+ u16 agc2_min;
+
+ u8 agc1_pt1;
+ u8 agc1_pt2;
+ u8 agc1_pt3;
+
+ u8 agc1_slope1;
+ u8 agc1_slope2;
+
+ u8 agc2_pt1;
+ u8 agc2_pt2;
+
+ u8 agc2_slope1;
+ u8 agc2_slope2;
+
+ u8 alpha_mant;
+ u8 alpha_exp;
+
+ u8 beta_mant;
+ u8 beta_exp;
+
+ u8 perform_agc_softsplit;
+
+ struct {
+ u16 min;
+ u16 max;
+ u16 min_thres;
+ u16 max_thres;
+ } split;
+};
+
+struct dibx000_bandwidth_config {
+ u32 internal;
+ u32 sampling;
+
+ u8 pll_prediv;
+ u8 pll_ratio;
+ u8 pll_range;
+ u8 pll_reset;
+ u8 pll_bypass;
+
+ u8 enable_refdiv;
+ u8 bypclk_div;
+ u8 IO_CLK_en_core;
+ u8 ADClkSrc;
+ u8 modulo;
+
+ u16 sad_cfg;
+
+ u32 ifreq;
+ u32 timf;
+};
+
+enum dibx000_adc_states {
+ DIBX000_SLOW_ADC_ON = 0,
+ DIBX000_SLOW_ADC_OFF,
+ DIBX000_ADC_ON,
+ DIBX000_ADC_OFF,
+ DIBX000_VBG_ENABLE,
+ DIBX000_VBG_DISABLE,
+};
+
+#define BW_INDEX_TO_KHZ(v) ( (v) == BANDWIDTH_8_MHZ ? 8000 : \
+ (v) == BANDWIDTH_7_MHZ ? 7000 : \
+ (v) == BANDWIDTH_6_MHZ ? 6000 : 8000 )
+
+/* Chip output mode. */
+#define OUTMODE_HIGH_Z 0
+#define OUTMODE_MPEG2_PAR_GATED_CLK 1
+#define OUTMODE_MPEG2_PAR_CONT_CLK 2
+#define OUTMODE_MPEG2_SERIAL 7
+#define OUTMODE_DIVERSITY 4
+#define OUTMODE_MPEG2_FIFO 5
+
+/* I hope I can get rid of the following kludge in the near future */
+struct dibx000_ofdm_channel {
+ u8 Bw;
+ s16 nfft;
+ s16 guard;
+ s16 nqam;
+ s16 vit_hrch;
+ s16 vit_select_hp;
+ s16 vit_alpha;
+ s16 vit_code_rate_hp;
+ s16 vit_code_rate_lp;
+};
+
+#define FEP2DIB(fep,ch) \
+ (ch)->Bw = (fep)->u.ofdm.bandwidth; \
+ (ch)->nfft = (fep)->u.ofdm.transmission_mode == TRANSMISSION_MODE_AUTO ? -1 : (fep)->u.ofdm.transmission_mode; \
+ (ch)->guard = (fep)->u.ofdm.guard_interval == GUARD_INTERVAL_AUTO ? -1 : (fep)->u.ofdm.guard_interval; \
+ (ch)->nqam = (fep)->u.ofdm.constellation == QAM_AUTO ? -1 : (fep)->u.ofdm.constellation == QAM_64 ? 2 : (fep)->u.ofdm.constellation; \
+ (ch)->vit_hrch = 0; /* linux-dvb is not prepared for HIERARCHICAL TRANSMISSION */ \
+ (ch)->vit_select_hp = 1; \
+ (ch)->vit_alpha = 1; \
+ (ch)->vit_code_rate_hp = (fep)->u.ofdm.code_rate_HP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_HP; \
+ (ch)->vit_code_rate_lp = (fep)->u.ofdm.code_rate_LP == FEC_AUTO ? -1 : (fep)->u.ofdm.code_rate_LP;
+
+#define INIT_OFDM_CHANNEL(ch) do {\
+ (ch)->Bw = 0; \
+ (ch)->nfft = -1; \
+ (ch)->guard = -1; \
+ (ch)->nqam = -1; \
+ (ch)->vit_hrch = -1; \
+ (ch)->vit_select_hp = -1; \
+ (ch)->vit_alpha = -1; \
+ (ch)->vit_code_rate_hp = -1; \
+ (ch)->vit_code_rate_lp = -1; \
+} while (0)
+
+#endif
diff --git a/drivers/media/dvb/frontends/dvb-pll.c b/drivers/media/dvb/frontends/dvb-pll.c
index 2be33f27c69..b7e7108ee5b 100644
--- a/drivers/media/dvb/frontends/dvb-pll.c
+++ b/drivers/media/dvb/frontends/dvb-pll.c
@@ -493,6 +493,9 @@ static int dvb_pll_sleep(struct dvb_frontend *fe)
int i;
int result;
+ if (priv->i2c == NULL)
+ return -EINVAL;
+
for (i = 0; i < priv->pll_desc->count; i++) {
if (priv->pll_desc->entries[i].limit == 0)
break;
@@ -611,7 +614,7 @@ static struct dvb_tuner_ops dvb_pll_tuner_ops = {
.get_bandwidth = dvb_pll_get_bandwidth,
};
-int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc)
+struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc)
{
u8 b1 [] = { 0 };
struct i2c_msg msg = { .addr = pll_addr, .flags = I2C_M_RD, .buf = b1, .len = 1 };
@@ -624,14 +627,14 @@ int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2
ret = i2c_transfer (i2c, &msg, 1);
if (ret != 1)
- return -1;
+ return NULL;
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 0);
}
priv = kzalloc(sizeof(struct dvb_pll_priv), GFP_KERNEL);
if (priv == NULL)
- return -ENOMEM;
+ return NULL;
priv->pll_i2c_address = pll_addr;
priv->i2c = i2c;
@@ -643,7 +646,7 @@ int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2
fe->ops.tuner_ops.info.frequency_min = desc->max;
fe->tuner_priv = priv;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(dvb_pll_attach);
diff --git a/drivers/media/dvb/frontends/dvb-pll.h b/drivers/media/dvb/frontends/dvb-pll.h
index 66361cd1880..ed5ac5a361a 100644
--- a/drivers/media/dvb/frontends/dvb-pll.h
+++ b/drivers/media/dvb/frontends/dvb-pll.h
@@ -57,8 +57,8 @@ extern int dvb_pll_configure(struct dvb_pll_desc *desc, u8 *buf,
* @param pll_addr i2c address of the PLL (if used).
* @param i2c i2c adapter to use (set to NULL if not used).
* @param desc dvb_pll_desc to use.
- * @return 0 on success, nonzero on failure.
+ * @return Frontend pointer on success, NULL on failure
*/
-extern int dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc);
+extern struct dvb_frontend *dvb_pll_attach(struct dvb_frontend *fe, int pll_addr, struct i2c_adapter *i2c, struct dvb_pll_desc *desc);
#endif
diff --git a/drivers/media/dvb/frontends/isl6421.c b/drivers/media/dvb/frontends/isl6421.c
index 58c34db3107..ef319369ec2 100644
--- a/drivers/media/dvb/frontends/isl6421.c
+++ b/drivers/media/dvb/frontends/isl6421.c
@@ -42,12 +42,11 @@ struct isl6421 {
u8 override_and;
struct i2c_adapter *i2c;
u8 i2c_addr;
- void (*release_chain)(struct dvb_frontend* fe);
};
static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
.buf = &isl6421->config,
.len = sizeof(isl6421->config) };
@@ -75,7 +74,7 @@ static int isl6421_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage
static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
+ struct isl6421 *isl6421 = (struct isl6421 *) fe->sec_priv;
struct i2c_msg msg = { .addr = isl6421->i2c_addr, .flags = 0,
.buf = &isl6421->config,
.len = sizeof(isl6421->config) };
@@ -93,31 +92,26 @@ static int isl6421_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
static void isl6421_release(struct dvb_frontend *fe)
{
- struct isl6421 *isl6421 = (struct isl6421 *) fe->misc_priv;
-
/* power off */
isl6421_set_voltage(fe, SEC_VOLTAGE_OFF);
- /* free data & call next release routine */
- fe->ops.release = isl6421->release_chain;
- kfree(fe->misc_priv);
- fe->misc_priv = NULL;
- if (fe->ops.release)
- fe->ops.release(fe);
+ /* free */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
}
-int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
u8 override_set, u8 override_clear)
{
struct isl6421 *isl6421 = kmalloc(sizeof(struct isl6421), GFP_KERNEL);
if (!isl6421)
- return -ENOMEM;
+ return NULL;
/* default configuration */
isl6421->config = ISL6421_ISEL1;
isl6421->i2c = i2c;
isl6421->i2c_addr = i2c_addr;
- fe->misc_priv = isl6421;
+ fe->sec_priv = isl6421;
/* bits which should be forced to '1' */
isl6421->override_or = override_set;
@@ -128,19 +122,17 @@ int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr
/* detect if it is present or not */
if (isl6421_set_voltage(fe, SEC_VOLTAGE_OFF)) {
kfree(isl6421);
- fe->misc_priv = NULL;
- return -EIO;
+ return NULL;
}
/* install release callback */
- isl6421->release_chain = fe->ops.release;
- fe->ops.release = isl6421_release;
+ fe->ops.release_sec = isl6421_release;
/* override frontend ops */
fe->ops.set_voltage = isl6421_set_voltage;
fe->ops.enable_high_lnb_voltage = isl6421_enable_high_lnb_voltage;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(isl6421_attach);
diff --git a/drivers/media/dvb/frontends/isl6421.h b/drivers/media/dvb/frontends/isl6421.h
index 675f80a19b9..1916e3eb2df 100644
--- a/drivers/media/dvb/frontends/isl6421.h
+++ b/drivers/media/dvb/frontends/isl6421.h
@@ -39,8 +39,17 @@
#define ISL6421_ISEL1 0x20
#define ISL6421_DCL 0x40
+#if defined(CONFIG_DVB_ISL6421) || defined(CONFIG_DVB_ISL6421_MODULE)
/* override_set and override_clear control which system register bits (above) to always set & clear */
-extern int isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+extern struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
u8 override_set, u8 override_clear);
+#else
+static inline struct dvb_frontend *isl6421_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 i2c_addr,
+ u8 override_set, u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_ISL6421
#endif
diff --git a/drivers/media/dvb/frontends/l64781.h b/drivers/media/dvb/frontends/l64781.h
index 83b8bc21027..21ba4a23076 100644
--- a/drivers/media/dvb/frontends/l64781.h
+++ b/drivers/media/dvb/frontends/l64781.h
@@ -31,8 +31,16 @@ struct l64781_config
u8 demod_address;
};
-
+#if defined(CONFIG_DVB_L64781) || defined(CONFIG_DVB_L64781_MODULE)
extern struct dvb_frontend* l64781_attach(const struct l64781_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* l64781_attach(const struct l64781_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_L64781
#endif // L64781_H
diff --git a/drivers/media/dvb/frontends/lgdt330x.h b/drivers/media/dvb/frontends/lgdt330x.h
index bad903c6f0f..3f96b485584 100644
--- a/drivers/media/dvb/frontends/lgdt330x.h
+++ b/drivers/media/dvb/frontends/lgdt330x.h
@@ -52,8 +52,17 @@ struct lgdt330x_config
int clock_polarity_flip;
};
+#if defined(CONFIG_DVB_LGDT330X) || defined(CONFIG_DVB_LGDT330X_MODULE)
extern struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* lgdt330x_attach(const struct lgdt330x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_LGDT330X
#endif /* LGDT330X_H */
diff --git a/drivers/media/dvb/frontends/lnbp21.c b/drivers/media/dvb/frontends/lnbp21.c
index e933edc8dd2..2d2f58c2622 100644
--- a/drivers/media/dvb/frontends/lnbp21.c
+++ b/drivers/media/dvb/frontends/lnbp21.c
@@ -40,12 +40,11 @@ struct lnbp21 {
u8 override_or;
u8 override_and;
struct i2c_adapter *i2c;
- void (*release_chain)(struct dvb_frontend* fe);
};
static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = 0x08, .flags = 0,
.buf = &lnbp21->config,
.len = sizeof(lnbp21->config) };
@@ -73,7 +72,7 @@ static int lnbp21_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
+ struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->sec_priv;
struct i2c_msg msg = { .addr = 0x08, .flags = 0,
.buf = &lnbp21->config,
.len = sizeof(lnbp21->config) };
@@ -91,29 +90,24 @@ static int lnbp21_enable_high_lnb_voltage(struct dvb_frontend *fe, long arg)
static void lnbp21_release(struct dvb_frontend *fe)
{
- struct lnbp21 *lnbp21 = (struct lnbp21 *) fe->misc_priv;
-
/* LNBP power off */
lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF);
- /* free data & call next release routine */
- fe->ops.release = lnbp21->release_chain;
- kfree(fe->misc_priv);
- fe->misc_priv = NULL;
- if (fe->ops.release)
- fe->ops.release(fe);
+ /* free data */
+ kfree(fe->sec_priv);
+ fe->sec_priv = NULL;
}
-int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
+struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
{
struct lnbp21 *lnbp21 = kmalloc(sizeof(struct lnbp21), GFP_KERNEL);
if (!lnbp21)
- return -ENOMEM;
+ return NULL;
/* default configuration */
lnbp21->config = LNBP21_ISEL;
lnbp21->i2c = i2c;
- fe->misc_priv = lnbp21;
+ fe->sec_priv = lnbp21;
/* bits which should be forced to '1' */
lnbp21->override_or = override_set;
@@ -124,19 +118,17 @@ int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_
/* detect if it is present or not */
if (lnbp21_set_voltage(fe, SEC_VOLTAGE_OFF)) {
kfree(lnbp21);
- fe->misc_priv = NULL;
- return -EIO;
+ return NULL;
}
/* install release callback */
- lnbp21->release_chain = fe->ops.release;
- fe->ops.release = lnbp21_release;
+ fe->ops.release_sec = lnbp21_release;
/* override frontend ops */
fe->ops.set_voltage = lnbp21_set_voltage;
fe->ops.enable_high_lnb_voltage = lnbp21_enable_high_lnb_voltage;
- return 0;
+ return fe;
}
EXPORT_SYMBOL(lnbp21_attach);
diff --git a/drivers/media/dvb/frontends/lnbp21.h b/drivers/media/dvb/frontends/lnbp21.h
index 047a4ab68c0..1fe1dd17931 100644
--- a/drivers/media/dvb/frontends/lnbp21.h
+++ b/drivers/media/dvb/frontends/lnbp21.h
@@ -39,7 +39,15 @@
#include <linux/dvb/frontend.h>
+#if defined(CONFIG_DVB_LNBP21) || defined(CONFIG_DVB_LNBP21_MODULE)
/* override_set and override_clear control which system register bits (above) to always set & clear */
-extern int lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear);
+extern struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear);
+#else
+static inline struct dvb_frontend *lnbp21_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, u8 override_set, u8 override_clear)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_LNBP21
-#endif
+#endif // _LNBP21_H
diff --git a/drivers/media/dvb/frontends/mt2060.c b/drivers/media/dvb/frontends/mt2060.c
new file mode 100644
index 00000000000..508ec1b6d1f
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt2060.c
@@ -0,0 +1,367 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+/* In that file, frequencies are expressed in kiloHertz to avoid 32 bits overflows */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/dvb/frontend.h>
+#include <linux/i2c.h>
+
+#include "dvb_frontend.h"
+
+#include "mt2060.h"
+#include "mt2060_priv.h"
+
+static int debug;
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off debugging (default:off).");
+
+#define dprintk(args...) do { if (debug) {printk(KERN_DEBUG "MT2060: " args); printk("\n"); }} while (0)
+
+// Reads a single register
+static int mt2060_readreg(struct mt2060_priv *priv, u8 reg, u8 *val)
+{
+ struct i2c_msg msg[2] = {
+ { .addr = priv->cfg->i2c_address, .flags = 0, .buf = &reg, .len = 1 },
+ { .addr = priv->cfg->i2c_address, .flags = I2C_M_RD, .buf = val, .len = 1 },
+ };
+
+ if (i2c_transfer(priv->i2c, msg, 2) != 2) {
+ printk(KERN_WARNING "mt2060 I2C read failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a single register
+static int mt2060_writereg(struct mt2060_priv *priv, u8 reg, u8 val)
+{
+ u8 buf[2] = { reg, val };
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = 2
+ };
+
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2060 I2C write failed\n");
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Writes a set of consecutive registers
+static int mt2060_writeregs(struct mt2060_priv *priv,u8 *buf, u8 len)
+{
+ struct i2c_msg msg = {
+ .addr = priv->cfg->i2c_address, .flags = 0, .buf = buf, .len = len
+ };
+ if (i2c_transfer(priv->i2c, &msg, 1) != 1) {
+ printk(KERN_WARNING "mt2060 I2C write failed (len=%i)\n",(int)len);
+ return -EREMOTEIO;
+ }
+ return 0;
+}
+
+// Initialisation sequences
+// LNABAND=3, NUM1=0x3C, DIV1=0x74, NUM2=0x1080, DIV2=0x49
+static u8 mt2060_config1[] = {
+ REG_LO1C1,
+ 0x3F, 0x74, 0x00, 0x08, 0x93
+};
+
+// FMCG=2, GP2=0, GP1=0
+static u8 mt2060_config2[] = {
+ REG_MISC_CTRL,
+ 0x20, 0x1E, 0x30, 0xff, 0x80, 0xff, 0x00, 0x2c, 0x42
+};
+
+// VGAG=3, V1CSE=1
+
+#ifdef MT2060_SPURCHECK
+/* The function below calculates the frequency offset between the output frequency if2
+ and the closer cross modulation subcarrier between lo1 and lo2 up to the tenth harmonic */
+static int mt2060_spurcalc(u32 lo1,u32 lo2,u32 if2)
+{
+ int I,J;
+ int dia,diamin,diff;
+ diamin=1000000;
+ for (I = 1; I < 10; I++) {
+ J = ((2*I*lo1)/lo2+1)/2;
+ diff = I*(int)lo1-J*(int)lo2;
+ if (diff < 0) diff=-diff;
+ dia = (diff-(int)if2);
+ if (dia < 0) dia=-dia;
+ if (diamin > dia) diamin=dia;
+ }
+ return diamin;
+}
+
+#define BANDWIDTH 4000 // kHz
+
+/* Calculates the frequency offset to add to avoid spurs. Returns 0 if no offset is needed */
+static int mt2060_spurcheck(u32 lo1,u32 lo2,u32 if2)
+{
+ u32 Spur,Sp1,Sp2;
+ int I,J;
+ I=0;
+ J=1000;
+
+ Spur=mt2060_spurcalc(lo1,lo2,if2);
+ if (Spur < BANDWIDTH) {
+ /* Potential spurs detected */
+ dprintk("Spurs before : f_lo1: %d f_lo2: %d (kHz)",
+ (int)lo1,(int)lo2);
+ I=1000;
+ Sp1 = mt2060_spurcalc(lo1+I,lo2+I,if2);
+ Sp2 = mt2060_spurcalc(lo1-I,lo2-I,if2);
+
+ if (Sp1 < Sp2) {
+ J=-J; I=-I; Spur=Sp2;
+ } else
+ Spur=Sp1;
+
+ while (Spur < BANDWIDTH) {
+ I += J;
+ Spur = mt2060_spurcalc(lo1+I,lo2+I,if2);
+ }
+ dprintk("Spurs after : f_lo1: %d f_lo2: %d (kHz)",
+ (int)(lo1+I),(int)(lo2+I));
+ }
+ return I;
+}
+#endif
+
+#define IF2 36150 // IF2 frequency = 36.150 MHz
+#define FREF 16000 // Quartz oscillator 16 MHz
+
+static int mt2060_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct mt2060_priv *priv;
+ int ret=0;
+ int i=0;
+ u32 freq;
+ u8 lnaband;
+ u32 f_lo1,f_lo2;
+ u32 div1,num1,div2,num2;
+ u8 b[8];
+ u32 if1;
+
+ priv = fe->tuner_priv;
+
+ if1 = priv->if1_freq;
+ b[0] = REG_LO1B1;
+ b[1] = 0xFF;
+
+ mt2060_writeregs(priv,b,2);
+
+ freq = params->frequency / 1000; // Hz -> kHz
+ priv->bandwidth = (fe->ops.info.type == FE_OFDM) ? params->u.ofdm.bandwidth : 0;
+
+ f_lo1 = freq + if1 * 1000;
+ f_lo1 = (f_lo1 / 250) * 250;
+ f_lo2 = f_lo1 - freq - IF2;
+ // From the Comtech datasheet, the step used is 50kHz. The tuner chip could be more precise
+ f_lo2 = ((f_lo2 + 25) / 50) * 50;
+ priv->frequency = (f_lo1 - f_lo2 - IF2) * 1000,
+
+#ifdef MT2060_SPURCHECK
+ // LO-related spurs detection and correction
+ num1 = mt2060_spurcheck(f_lo1,f_lo2,IF2);
+ f_lo1 += num1;
+ f_lo2 += num1;
+#endif
+ //Frequency LO1 = 16MHz * (DIV1 + NUM1/64 )
+ num1 = f_lo1 / (FREF / 64);
+ div1 = num1 / 64;
+ num1 &= 0x3f;
+
+ // Frequency LO2 = 16MHz * (DIV2 + NUM2/8192 )
+ num2 = f_lo2 * 64 / (FREF / 128);
+ div2 = num2 / 8192;
+ num2 &= 0x1fff;
+
+ if (freq <= 95000) lnaband = 0xB0; else
+ if (freq <= 180000) lnaband = 0xA0; else
+ if (freq <= 260000) lnaband = 0x90; else
+ if (freq <= 335000) lnaband = 0x80; else
+ if (freq <= 425000) lnaband = 0x70; else
+ if (freq <= 480000) lnaband = 0x60; else
+ if (freq <= 570000) lnaband = 0x50; else
+ if (freq <= 645000) lnaband = 0x40; else
+ if (freq <= 730000) lnaband = 0x30; else
+ if (freq <= 810000) lnaband = 0x20; else lnaband = 0x10;
+
+ b[0] = REG_LO1C1;
+ b[1] = lnaband | ((num1 >>2) & 0x0F);
+ b[2] = div1;
+ b[3] = (num2 & 0x0F) | ((num1 & 3) << 4);
+ b[4] = num2 >> 4;
+ b[5] = ((num2 >>12) & 1) | (div2 << 1);
+
+ dprintk("IF1: %dMHz",(int)if1);
+ dprintk("PLL freq=%dkHz f_lo1=%dkHz f_lo2=%dkHz",(int)freq,(int)f_lo1,(int)f_lo2);
+ dprintk("PLL div1=%d num1=%d div2=%d num2=%d",(int)div1,(int)num1,(int)div2,(int)num2);
+ dprintk("PLL [1..5]: %2x %2x %2x %2x %2x",(int)b[1],(int)b[2],(int)b[3],(int)b[4],(int)b[5]);
+
+ mt2060_writeregs(priv,b,6);
+
+ //Waits for pll lock or timeout
+ i = 0;
+ do {
+ mt2060_readreg(priv,REG_LO_STATUS,b);
+ if ((b[0] & 0x88)==0x88)
+ break;
+ msleep(4);
+ i++;
+ } while (i<10);
+
+ return ret;
+}
+
+static void mt2060_calibrate(struct mt2060_priv *priv)
+{
+ u8 b = 0;
+ int i = 0;
+
+ if (mt2060_writeregs(priv,mt2060_config1,sizeof(mt2060_config1)))
+ return;
+ if (mt2060_writeregs(priv,mt2060_config2,sizeof(mt2060_config2)))
+ return;
+
+ do {
+ b |= (1 << 6); // FM1SS;
+ mt2060_writereg(priv, REG_LO2C1,b);
+ msleep(20);
+
+ if (i == 0) {
+ b |= (1 << 7); // FM1CA;
+ mt2060_writereg(priv, REG_LO2C1,b);
+ b &= ~(1 << 7); // FM1CA;
+ msleep(20);
+ }
+
+ b &= ~(1 << 6); // FM1SS
+ mt2060_writereg(priv, REG_LO2C1,b);
+
+ msleep(20);
+ i++;
+ } while (i < 9);
+
+ i = 0;
+ while (i++ < 10 && mt2060_readreg(priv, REG_MISC_STAT, &b) == 0 && (b & (1 << 6)) == 0)
+ msleep(20);
+
+ if (i < 10) {
+ mt2060_readreg(priv, REG_FM_FREQ, &priv->fmfreq); // now find out, what is fmreq used for :)
+ dprintk("calibration was successful: %d", (int)priv->fmfreq);
+ } else
+ dprintk("FMCAL timed out");
+}
+
+static int mt2060_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static int mt2060_get_bandwidth(struct dvb_frontend *fe, u32 *bandwidth)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ *bandwidth = priv->bandwidth;
+ return 0;
+}
+
+static int mt2060_init(struct dvb_frontend *fe)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ return mt2060_writereg(priv, REG_VGAG,0x33);
+}
+
+static int mt2060_sleep(struct dvb_frontend *fe)
+{
+ struct mt2060_priv *priv = fe->tuner_priv;
+ return mt2060_writereg(priv, REG_VGAG,0x30);
+}
+
+static int mt2060_release(struct dvb_frontend *fe)
+{
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static const struct dvb_tuner_ops mt2060_tuner_ops = {
+ .info = {
+ .name = "Microtune MT2060",
+ .frequency_min = 48000000,
+ .frequency_max = 860000000,
+ .frequency_step = 50000,
+ },
+
+ .release = mt2060_release,
+
+ .init = mt2060_init,
+ .sleep = mt2060_sleep,
+
+ .set_params = mt2060_set_params,
+ .get_frequency = mt2060_get_frequency,
+ .get_bandwidth = mt2060_get_bandwidth
+};
+
+/* This functions tries to identify a MT2060 tuner by reading the PART/REV register. This is hasty. */
+int mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1)
+{
+ struct mt2060_priv *priv = NULL;
+ u8 id = 0;
+
+ priv = kzalloc(sizeof(struct mt2060_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+
+ priv->cfg = cfg;
+ priv->i2c = i2c;
+ priv->if1_freq = if1;
+
+ if (mt2060_readreg(priv,REG_PART_REV,&id) != 0) {
+ kfree(priv);
+ return -ENODEV;
+ }
+
+ if (id != PART_REV) {
+ kfree(priv);
+ return -ENODEV;
+ }
+ printk(KERN_INFO "MT2060: successfully identified (IF1 = %d)\n", if1);
+ memcpy(&fe->ops.tuner_ops, &mt2060_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ mt2060_calibrate(priv);
+
+ return 0;
+}
+EXPORT_SYMBOL(mt2060_attach);
+
+MODULE_AUTHOR("Olivier DANET");
+MODULE_DESCRIPTION("Microtune MT2060 silicon tuner driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/mt2060.h b/drivers/media/dvb/frontends/mt2060.h
new file mode 100644
index 00000000000..c58b03e8234
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt2060.h
@@ -0,0 +1,35 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef MT2060_H
+#define MT2060_H
+
+struct dvb_frontend;
+struct i2c_adapter;
+
+struct mt2060_config {
+ u8 i2c_address;
+ /* Shall we add settings for the discrete outputs ? */
+};
+
+extern int mt2060_attach(struct dvb_frontend *fe, struct i2c_adapter *i2c, struct mt2060_config *cfg, u16 if1);
+
+#endif
diff --git a/drivers/media/dvb/frontends/mt2060_priv.h b/drivers/media/dvb/frontends/mt2060_priv.h
new file mode 100644
index 00000000000..5eaccdefd0b
--- /dev/null
+++ b/drivers/media/dvb/frontends/mt2060_priv.h
@@ -0,0 +1,105 @@
+/*
+ * Driver for Microtune MT2060 "Single chip dual conversion broadband tuner"
+ *
+ * Copyright (c) 2006 Olivier DANET <odanet@caramail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ *
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.=
+ */
+
+#ifndef MT2060_PRIV_H
+#define MT2060_PRIV_H
+
+// Uncomment the #define below to enable spurs checking. The results where quite unconvincing.
+// #define MT2060_SPURCHECK
+
+/* This driver is based on the information available in the datasheet of the
+ "Comtech SDVBT-3K6M" tuner ( K1000737843.pdf ) which features the MT2060 register map :
+
+ I2C Address : 0x60
+
+ Reg.No | B7 | B6 | B5 | B4 | B3 | B2 | B1 | B0 | ( defaults )
+ --------------------------------------------------------------------------------
+ 00 | [ PART ] | [ REV ] | R = 0x63
+ 01 | [ LNABAND ] | [ NUM1(5:2) ] | RW = 0x3F
+ 02 | [ DIV1 ] | RW = 0x74
+ 03 | FM1CA | FM1SS | [ NUM1(1:0) ] | [ NUM2(3:0) ] | RW = 0x00
+ 04 | NUM2(11:4) ] | RW = 0x08
+ 05 | [ DIV2 ] |NUM2(12)| RW = 0x93
+ 06 | L1LK | [ TAD1 ] | L2LK | [ TAD2 ] | R
+ 07 | [ FMF ] | R
+ 08 | ? | FMCAL | ? | ? | ? | ? | ? | TEMP | R
+ 09 | 0 | 0 | [ FMGC ] | 0 | GP02 | GP01 | 0 | RW = 0x20
+ 0A | ??
+ 0B | 0 | 0 | 1 | 1 | 0 | 0 | [ VGAG ] | RW = 0x30
+ 0C | V1CSE | 1 | 1 | 1 | 1 | 1 | 1 | 1 | RW = 0xFF
+ 0D | 1 | 0 | [ V1CS ] | RW = 0xB0
+ 0E | ??
+ 0F | ??
+ 10 | ??
+ 11 | [ LOTO ] | 0 | 0 | 1 | 0 | RW = 0x42
+
+ PART : Part code : 6 for MT2060
+ REV : Revision code : 3 for current revision
+ LNABAND : Input frequency range : ( See code for details )
+ NUM1 / DIV1 / NUM2 / DIV2 : Frequencies programming ( See code for details )
+ FM1CA : Calibration Start Bit
+ FM1SS : Calibration Single Step bit
+ L1LK : LO1 Lock Detect
+ TAD1 : Tune Line ADC ( ? )
+ L2LK : LO2 Lock Detect
+ TAD2 : Tune Line ADC ( ? )
+ FMF : Estimated first IF Center frequency Offset ( ? )
+ FM1CAL : Calibration done bit
+ TEMP : On chip temperature sensor
+ FMCG : Mixer 1 Cap Gain ( ? )
+ GP01 / GP02 : Programmable digital outputs. Unconnected pins ?
+ V1CSE : LO1 VCO Automatic Capacitor Select Enable ( ? )
+ V1CS : LO1 Capacitor Selection Value ( ? )
+ LOTO : LO Timeout ( ? )
+ VGAG : Tuner Output gain
+*/
+
+#define I2C_ADDRESS 0x60
+
+#define REG_PART_REV 0
+#define REG_LO1C1 1
+#define REG_LO1C2 2
+#define REG_LO2C1 3
+#define REG_LO2C2 4
+#define REG_LO2C3 5
+#define REG_LO_STATUS 6
+#define REG_FM_FREQ 7
+#define REG_MISC_STAT 8
+#define REG_MISC_CTRL 9
+#define REG_RESERVED_A 0x0A
+#define REG_VGAG 0x0B
+#define REG_LO1B1 0x0C
+#define REG_LO1B2 0x0D
+#define REG_LOTO 0x11
+
+#define PART_REV 0x63 // The current driver works only with PART=6 and REV=3 chips
+
+struct mt2060_priv {
+ struct mt2060_config *cfg;
+ struct i2c_adapter *i2c;
+
+ u32 frequency;
+ u32 bandwidth;
+ u16 if1_freq;
+ u8 fmfreq;
+};
+
+#endif
diff --git a/drivers/media/dvb/frontends/mt312.h b/drivers/media/dvb/frontends/mt312.h
index 666a1bd1c24..7112fb4d58a 100644
--- a/drivers/media/dvb/frontends/mt312.h
+++ b/drivers/media/dvb/frontends/mt312.h
@@ -34,8 +34,16 @@ struct mt312_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_MT312) || defined(CONFIG_DVB_MT312_MODULE)
struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
struct i2c_adapter* i2c);
-
+#else
+static inline struct dvb_frontend* vp310_mt312_attach(const struct mt312_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_MT312
#endif // MT312_H
diff --git a/drivers/media/dvb/frontends/mt352.c b/drivers/media/dvb/frontends/mt352.c
index 5de7376c94c..87e31ca7e10 100644
--- a/drivers/media/dvb/frontends/mt352.c
+++ b/drivers/media/dvb/frontends/mt352.c
@@ -70,7 +70,7 @@ static int mt352_single_write(struct dvb_frontend *fe, u8 reg, u8 val)
return 0;
}
-int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
+static int _mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen)
{
int err,i;
for (i=0; i < ilen-1; i++)
@@ -107,7 +107,7 @@ static int mt352_sleep(struct dvb_frontend* fe)
{
static u8 mt352_softdown[] = { CLOCK_CTL, 0x20, 0x08 };
- mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
+ _mt352_write(fe, mt352_softdown, sizeof(mt352_softdown));
return 0;
}
@@ -293,14 +293,14 @@ static int mt352_set_parameters(struct dvb_frontend* fe,
fe->ops.i2c_gate_ctrl(fe, 0);
}
- mt352_write(fe, buf, 8);
- mt352_write(fe, fsm_go, 2);
+ _mt352_write(fe, buf, 8);
+ _mt352_write(fe, fsm_go, 2);
} else {
if (fe->ops.tuner_ops.calc_regs) {
fe->ops.tuner_ops.calc_regs(fe, param, buf+8, 5);
buf[8] <<= 1;
- mt352_write(fe, buf, sizeof(buf));
- mt352_write(fe, tuner_go, 2);
+ _mt352_write(fe, buf, sizeof(buf));
+ _mt352_write(fe, tuner_go, 2);
}
}
@@ -522,7 +522,7 @@ static int mt352_init(struct dvb_frontend* fe)
(mt352_read_register(state, CONFIG) & 0x20) == 0) {
/* Do a "hard" reset */
- mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
+ _mt352_write(fe, mt352_reset_attach, sizeof(mt352_reset_attach));
return state->config.demod_init(fe);
}
@@ -585,6 +585,7 @@ static struct dvb_frontend_ops mt352_ops = {
.init = mt352_init,
.sleep = mt352_sleep,
+ .write = _mt352_write,
.set_frontend = mt352_set_parameters,
.get_frontend = mt352_get_parameters,
@@ -605,4 +606,3 @@ MODULE_AUTHOR("Holger Waechtler, Daniel Mack, Antonio Mancuso");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(mt352_attach);
-EXPORT_SYMBOL(mt352_write);
diff --git a/drivers/media/dvb/frontends/mt352.h b/drivers/media/dvb/frontends/mt352.h
index 9e7ff4b8fe5..0035c2e2d7c 100644
--- a/drivers/media/dvb/frontends/mt352.h
+++ b/drivers/media/dvb/frontends/mt352.h
@@ -51,9 +51,23 @@ struct mt352_config
int (*demod_init)(struct dvb_frontend* fe);
};
+#if defined(CONFIG_DVB_MT352) || defined(CONFIG_DVB_MT352_MODULE)
extern struct dvb_frontend* mt352_attach(const struct mt352_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* mt352_attach(const struct mt352_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_MT352
-extern int mt352_write(struct dvb_frontend* fe, u8* ibuf, int ilen);
+static inline int mt352_write(struct dvb_frontend *fe, u8 *buf, int len) {
+ int r = 0;
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, len);
+ return r;
+}
#endif // MT352_H
diff --git a/drivers/media/dvb/frontends/nxt200x.h b/drivers/media/dvb/frontends/nxt200x.h
index 34d61735845..2eb220e9806 100644
--- a/drivers/media/dvb/frontends/nxt200x.h
+++ b/drivers/media/dvb/frontends/nxt200x.h
@@ -45,8 +45,17 @@ struct nxt200x_config
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
};
+#if defined(CONFIG_DVB_NXT200X) || defined(CONFIG_DVB_NXT200X_MODULE)
extern struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt200x_attach(const struct nxt200x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT200X
#endif /* NXT200X_H */
diff --git a/drivers/media/dvb/frontends/nxt6000.h b/drivers/media/dvb/frontends/nxt6000.h
index 117031d1170..9397393a6bd 100644
--- a/drivers/media/dvb/frontends/nxt6000.h
+++ b/drivers/media/dvb/frontends/nxt6000.h
@@ -33,7 +33,16 @@ struct nxt6000_config
u8 clock_inversion:1;
};
+#if defined(CONFIG_DVB_NXT6000) || defined(CONFIG_DVB_NXT6000_MODULE)
extern struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* nxt6000_attach(const struct nxt6000_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_NXT6000
#endif // NXT6000_H
diff --git a/drivers/media/dvb/frontends/or51132.h b/drivers/media/dvb/frontends/or51132.h
index 89658883abf..9718be4fb83 100644
--- a/drivers/media/dvb/frontends/or51132.h
+++ b/drivers/media/dvb/frontends/or51132.h
@@ -34,8 +34,17 @@ struct or51132_config
int (*set_ts_params)(struct dvb_frontend* fe, int is_punctured);
};
+#if defined(CONFIG_DVB_OR51132) || defined(CONFIG_DVB_OR51132_MODULE)
extern struct dvb_frontend* or51132_attach(const struct or51132_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51132_attach(const struct or51132_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51132
#endif // OR51132_H
diff --git a/drivers/media/dvb/frontends/or51211.h b/drivers/media/dvb/frontends/or51211.h
index 13a5a3afbf8..10a5419f9e0 100644
--- a/drivers/media/dvb/frontends/or51211.h
+++ b/drivers/media/dvb/frontends/or51211.h
@@ -37,8 +37,17 @@ struct or51211_config
void (*sleep)(struct dvb_frontend * fe);
};
+#if defined(CONFIG_DVB_OR51211) || defined(CONFIG_DVB_OR51211_MODULE)
extern struct dvb_frontend* or51211_attach(const struct or51211_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* or51211_attach(const struct or51211_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_OR51211
#endif // OR51211_H
diff --git a/drivers/media/dvb/frontends/s5h1420.h b/drivers/media/dvb/frontends/s5h1420.h
index 4e39015fa67..efc54d7f3c5 100644
--- a/drivers/media/dvb/frontends/s5h1420.h
+++ b/drivers/media/dvb/frontends/s5h1420.h
@@ -34,7 +34,16 @@ struct s5h1420_config
u8 invert:1;
};
+#if defined(CONFIG_DVB_S5H1420) || defined(CONFIG_DVB_S5H1420_MODULE)
extern struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* s5h1420_attach(const struct s5h1420_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_S5H1420
#endif // S5H1420_H
diff --git a/drivers/media/dvb/frontends/sp8870.h b/drivers/media/dvb/frontends/sp8870.h
index 93afbb969d6..4cf27d3b10f 100644
--- a/drivers/media/dvb/frontends/sp8870.h
+++ b/drivers/media/dvb/frontends/sp8870.h
@@ -35,7 +35,16 @@ struct sp8870_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE)
extern struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* sp8870_attach(const struct sp8870_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_SP8870
#endif // SP8870_H
diff --git a/drivers/media/dvb/frontends/sp887x.h b/drivers/media/dvb/frontends/sp887x.h
index c44b0ebdf1e..cab7ea644df 100644
--- a/drivers/media/dvb/frontends/sp887x.h
+++ b/drivers/media/dvb/frontends/sp887x.h
@@ -17,7 +17,16 @@ struct sp887x_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_SP887X) || defined(CONFIG_DVB_SP887X_MODULE)
extern struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* sp887x_attach(const struct sp887x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_SP887X
#endif // SP887X_H
diff --git a/drivers/media/dvb/frontends/stv0297.h b/drivers/media/dvb/frontends/stv0297.h
index 1da5384fb98..760b80db43a 100644
--- a/drivers/media/dvb/frontends/stv0297.h
+++ b/drivers/media/dvb/frontends/stv0297.h
@@ -42,7 +42,16 @@ struct stv0297_config
u8 stop_during_read:1;
};
+#if defined(CONFIG_DVB_STV0297) || defined(CONFIG_DVB_STV0297_MODULE)
extern struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* stv0297_attach(const struct stv0297_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_STV0297
#endif // STV0297_H
diff --git a/drivers/media/dvb/frontends/stv0299.c b/drivers/media/dvb/frontends/stv0299.c
index 96648a75440..93483769eca 100644
--- a/drivers/media/dvb/frontends/stv0299.c
+++ b/drivers/media/dvb/frontends/stv0299.c
@@ -92,11 +92,14 @@ static int stv0299_writeregI (struct stv0299_state* state, u8 reg, u8 data)
return (ret != 1) ? -EREMOTEIO : 0;
}
-int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data)
+int stv0299_write(struct dvb_frontend* fe, u8 *buf, int len)
{
struct stv0299_state* state = fe->demodulator_priv;
- return stv0299_writeregI(state, reg, data);
+ if (len != 2)
+ return -EINVAL;
+
+ return stv0299_writeregI(state, buf[0], buf[1]);
}
static u8 stv0299_readreg (struct stv0299_state* state, u8 reg)
@@ -694,6 +697,7 @@ static struct dvb_frontend_ops stv0299_ops = {
.init = stv0299_init,
.sleep = stv0299_sleep,
+ .write = stv0299_write,
.i2c_gate_ctrl = stv0299_i2c_gate_ctrl,
.set_frontend = stv0299_set_frontend,
@@ -724,5 +728,4 @@ MODULE_AUTHOR("Ralph Metzler, Holger Waechtler, Peter Schildmann, Felix Domke, "
"Andreas Oberritter, Andrew de Quincey, Kenneth Aafly");
MODULE_LICENSE("GPL");
-EXPORT_SYMBOL(stv0299_writereg);
EXPORT_SYMBOL(stv0299_attach);
diff --git a/drivers/media/dvb/frontends/stv0299.h b/drivers/media/dvb/frontends/stv0299.h
index 1504828e423..7ef25207081 100644
--- a/drivers/media/dvb/frontends/stv0299.h
+++ b/drivers/media/dvb/frontends/stv0299.h
@@ -89,9 +89,24 @@ struct stv0299_config
int (*set_symbol_rate)(struct dvb_frontend* fe, u32 srate, u32 ratio);
};
-extern int stv0299_writereg (struct dvb_frontend* fe, u8 reg, u8 data);
-
+#if defined(CONFIG_DVB_STV0299) || defined(CONFIG_DVB_STV0299_MODULE)
extern struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* stv0299_attach(const struct stv0299_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_STV0299
+
+static inline int stv0299_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // STV0299_H
diff --git a/drivers/media/dvb/frontends/tda10021.c b/drivers/media/dvb/frontends/tda10021.c
index 9cbd164aa28..dca89171be1 100644
--- a/drivers/media/dvb/frontends/tda10021.c
+++ b/drivers/media/dvb/frontends/tda10021.c
@@ -72,7 +72,7 @@ static u8 tda10021_inittab[0x40]=
0x04, 0x2d, 0x2f, 0xff, 0x00, 0x00, 0x00, 0x00,
};
-static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
+static int _tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
{
u8 buf[] = { reg, data };
struct i2c_msg msg = { .addr = state->config->demod_address, .flags = 0, .buf = buf, .len = 2 };
@@ -88,14 +88,6 @@ static int tda10021_writereg (struct tda10021_state* state, u8 reg, u8 data)
return (ret != 1) ? -EREMOTEIO : 0;
}
-int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data)
-{
- struct tda10021_state* state = fe->demodulator_priv;
-
- return tda10021_writereg(state, reg, data);
-}
-EXPORT_SYMBOL(tda10021_write_byte);
-
static u8 tda10021_readreg (struct tda10021_state* state, u8 reg)
{
u8 b0 [] = { reg };
@@ -149,8 +141,8 @@ static int tda10021_setup_reg0 (struct tda10021_state* state, u8 reg0,
else if (INVERSION_OFF == inversion)
DISABLE_INVERSION(reg0);
- tda10021_writereg (state, 0x00, reg0 & 0xfe);
- tda10021_writereg (state, 0x00, reg0 | 0x01);
+ _tda10021_writereg (state, 0x00, reg0 & 0xfe);
+ _tda10021_writereg (state, 0x00, reg0 | 0x01);
state->reg0 = reg0;
return 0;
@@ -198,17 +190,27 @@ static int tda10021_set_symbolrate (struct tda10021_state* state, u32 symbolrate
NDEC = (NDEC << 6) | tda10021_inittab[0x03];
- tda10021_writereg (state, 0x03, NDEC);
- tda10021_writereg (state, 0x0a, BDR&0xff);
- tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
- tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
+ _tda10021_writereg (state, 0x03, NDEC);
+ _tda10021_writereg (state, 0x0a, BDR&0xff);
+ _tda10021_writereg (state, 0x0b, (BDR>> 8)&0xff);
+ _tda10021_writereg (state, 0x0c, (BDR>>16)&0x3f);
- tda10021_writereg (state, 0x0d, BDRI);
- tda10021_writereg (state, 0x0e, SFIL);
+ _tda10021_writereg (state, 0x0d, BDRI);
+ _tda10021_writereg (state, 0x0e, SFIL);
return 0;
}
+int tda10021_write(struct dvb_frontend* fe, u8 *buf, int len)
+{
+ struct tda10021_state* state = fe->demodulator_priv;
+
+ if (len != 2)
+ return -EINVAL;
+
+ return _tda10021_writereg(state, buf[0], buf[1]);
+}
+
static int tda10021_init (struct dvb_frontend *fe)
{
struct tda10021_state* state = fe->demodulator_priv;
@@ -216,12 +218,12 @@ static int tda10021_init (struct dvb_frontend *fe)
dprintk("DVB: TDA10021(%d): init chip\n", fe->adapter->num);
- //tda10021_writereg (fe, 0, 0);
+ //_tda10021_writereg (fe, 0, 0);
for (i=0; i<tda10021_inittab_size; i++)
- tda10021_writereg (state, i, tda10021_inittab[i]);
+ _tda10021_writereg (state, i, tda10021_inittab[i]);
- tda10021_writereg (state, 0x34, state->pwm);
+ _tda10021_writereg (state, 0x34, state->pwm);
//Comment by markus
//0x2A[3-0] == PDIV -> P multiplaying factor (P=PDIV+1)(default 0)
@@ -230,7 +232,7 @@ static int tda10021_init (struct dvb_frontend *fe)
//0x2A[6] == POLAXIN -> Polarity of the input reference clock (default 0)
//Activate PLL
- tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
+ _tda10021_writereg(state, 0x2a, tda10021_inittab[0x2a] & 0xef);
return 0;
}
@@ -264,12 +266,12 @@ static int tda10021_set_parameters (struct dvb_frontend *fe,
}
tda10021_set_symbolrate (state, p->u.qam.symbol_rate);
- tda10021_writereg (state, 0x34, state->pwm);
+ _tda10021_writereg (state, 0x34, state->pwm);
- tda10021_writereg (state, 0x01, reg0x01[qam]);
- tda10021_writereg (state, 0x05, reg0x05[qam]);
- tda10021_writereg (state, 0x08, reg0x08[qam]);
- tda10021_writereg (state, 0x09, reg0x09[qam]);
+ _tda10021_writereg (state, 0x01, reg0x01[qam]);
+ _tda10021_writereg (state, 0x05, reg0x05[qam]);
+ _tda10021_writereg (state, 0x08, reg0x08[qam]);
+ _tda10021_writereg (state, 0x09, reg0x09[qam]);
tda10021_setup_reg0 (state, reg0x00[qam], p->inversion);
@@ -342,8 +344,8 @@ static int tda10021_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
*ucblocks = 0xffffffff;
/* reset uncorrected block counter */
- tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
- tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
+ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10] & 0xdf);
+ _tda10021_writereg (state, 0x10, tda10021_inittab[0x10]);
return 0;
}
@@ -392,8 +394,8 @@ static int tda10021_sleep(struct dvb_frontend* fe)
{
struct tda10021_state* state = fe->demodulator_priv;
- tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
- tda10021_writereg (state, 0x00, 0x80); /* standby */
+ _tda10021_writereg (state, 0x1b, 0x02); /* pdown ADC */
+ _tda10021_writereg (state, 0x00, 0x80); /* standby */
return 0;
}
@@ -459,6 +461,7 @@ static struct dvb_frontend_ops tda10021_ops = {
.init = tda10021_init,
.sleep = tda10021_sleep,
+ .write = tda10021_write,
.i2c_gate_ctrl = tda10021_i2c_gate_ctrl,
.set_frontend = tda10021_set_parameters,
diff --git a/drivers/media/dvb/frontends/tda10021.h b/drivers/media/dvb/frontends/tda10021.h
index b1df4259bee..d68ae20c841 100644
--- a/drivers/media/dvb/frontends/tda10021.h
+++ b/drivers/media/dvb/frontends/tda10021.h
@@ -32,9 +32,24 @@ struct tda10021_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_TDA10021) || defined(CONFIG_DVB_TDA10021_MODULE)
extern struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
struct i2c_adapter* i2c, u8 pwm);
-
-extern int tda10021_write_byte(struct dvb_frontend* fe, int reg, int data);
+#else
+static inline struct dvb_frontend* tda10021_attach(const struct tda10021_config* config,
+ struct i2c_adapter* i2c, u8 pwm)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA10021
+
+static inline int tda10021_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // TDA10021_H
diff --git a/drivers/media/dvb/frontends/tda1004x.c b/drivers/media/dvb/frontends/tda1004x.c
index 59a2ed614fc..11e0dca9a2d 100644
--- a/drivers/media/dvb/frontends/tda1004x.c
+++ b/drivers/media/dvb/frontends/tda1004x.c
@@ -579,11 +579,14 @@ static int tda1004x_decode_fec(int tdafec)
return -1;
}
-int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data)
+int tda1004x_write(struct dvb_frontend* fe, u8 *buf, int len)
{
struct tda1004x_state* state = fe->demodulator_priv;
- return tda1004x_write_byteI(state, reg, data);
+ if (len != 2)
+ return -EINVAL;
+
+ return tda1004x_write_byteI(state, buf[0], buf[1]);
}
static int tda10045_init(struct dvb_frontend* fe)
@@ -1216,6 +1219,7 @@ static struct dvb_frontend_ops tda10045_ops = {
.init = tda10045_init,
.sleep = tda1004x_sleep,
+ .write = tda1004x_write,
.i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
.set_frontend = tda1004x_set_fe,
@@ -1274,6 +1278,7 @@ static struct dvb_frontend_ops tda10046_ops = {
.init = tda10046_init,
.sleep = tda1004x_sleep,
+ .write = tda1004x_write,
.i2c_gate_ctrl = tda1004x_i2c_gate_ctrl,
.set_frontend = tda1004x_set_fe,
@@ -1323,4 +1328,3 @@ MODULE_LICENSE("GPL");
EXPORT_SYMBOL(tda10045_attach);
EXPORT_SYMBOL(tda10046_attach);
-EXPORT_SYMBOL(tda1004x_write_byte);
diff --git a/drivers/media/dvb/frontends/tda1004x.h b/drivers/media/dvb/frontends/tda1004x.h
index b877b23ed73..e28fca05734 100644
--- a/drivers/media/dvb/frontends/tda1004x.h
+++ b/drivers/media/dvb/frontends/tda1004x.h
@@ -71,12 +71,33 @@ struct tda1004x_config
int (*request_firmware)(struct dvb_frontend* fe, const struct firmware **fw, char* name);
};
+#if defined(CONFIG_DVB_TDA1004X) || defined(CONFIG_DVB_TDA1004X_MODULE)
extern struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
struct i2c_adapter* i2c);
extern struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
struct i2c_adapter* i2c);
-
-extern int tda1004x_write_byte(struct dvb_frontend* fe, int reg, int data);
+#else
+static inline struct dvb_frontend* tda10045_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+static inline struct dvb_frontend* tda10046_attach(const struct tda1004x_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA1004X
+
+static inline int tda1004x_writereg(struct dvb_frontend *fe, u8 reg, u8 val) {
+ int r = 0;
+ u8 buf[] = {reg, val};
+ if (fe->ops.write)
+ r = fe->ops.write(fe, buf, 2);
+ return r;
+}
#endif // TDA1004X_H
diff --git a/drivers/media/dvb/frontends/tda10086.c b/drivers/media/dvb/frontends/tda10086.c
new file mode 100644
index 00000000000..7456b0b9976
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10086.c
@@ -0,0 +1,740 @@
+ /*
+ Driver for Philips tda10086 DVBS Demodulator
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/jiffies.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+
+#include "dvb_frontend.h"
+#include "tda10086.h"
+
+#define SACLK 96000000
+
+struct tda10086_state {
+ struct i2c_adapter* i2c;
+ const struct tda10086_config* config;
+ struct dvb_frontend frontend;
+
+ /* private demod data */
+ u32 frequency;
+ u32 symbol_rate;
+};
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda10086: " args); \
+ } while (0)
+
+static int tda10086_write_byte(struct tda10086_state *state, int reg, int data)
+{
+ int ret;
+ u8 b0[] = { reg, data };
+ struct i2c_msg msg = { .flags = 0, .buf = b0, .len = 2 };
+
+ msg.addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, &msg, 1);
+
+ if (ret != 1)
+ dprintk("%s: error reg=0x%x, data=0x%x, ret=%i\n",
+ __FUNCTION__, reg, data, ret);
+
+ return (ret != 1) ? ret : 0;
+}
+
+static int tda10086_read_byte(struct tda10086_state *state, int reg)
+{
+ int ret;
+ u8 b0[] = { reg };
+ u8 b1[] = { 0 };
+ struct i2c_msg msg[] = {{ .flags = 0, .buf = b0, .len = 1 },
+ { .flags = I2C_M_RD, .buf = b1, .len = 1 }};
+
+ msg[0].addr = state->config->demod_address;
+ msg[1].addr = state->config->demod_address;
+ ret = i2c_transfer(state->i2c, msg, 2);
+
+ if (ret != 2) {
+ dprintk("%s: error reg=0x%x, ret=%i\n", __FUNCTION__, reg,
+ ret);
+ return ret;
+ }
+
+ return b1[0];
+}
+
+static int tda10086_write_mask(struct tda10086_state *state, int reg, int mask, int data)
+{
+ int val;
+
+ // read a byte and check
+ val = tda10086_read_byte(state, reg);
+ if (val < 0)
+ return val;
+
+ // mask if off
+ val = val & ~mask;
+ val |= data & 0xff;
+
+ // write it out again
+ return tda10086_write_byte(state, reg, val);
+}
+
+static int tda10086_init(struct dvb_frontend* fe)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // reset
+ tda10086_write_byte(state, 0x00, 0x00);
+ msleep(10);
+
+ // misc setup
+ tda10086_write_byte(state, 0x01, 0x94);
+ tda10086_write_byte(state, 0x02, 0x35); // NOTE: TT drivers appear to disable CSWP
+ tda10086_write_byte(state, 0x03, 0x64);
+ tda10086_write_byte(state, 0x04, 0x43);
+ tda10086_write_byte(state, 0x0c, 0x0c);
+ tda10086_write_byte(state, 0x1b, 0xb0); // noise threshold
+ tda10086_write_byte(state, 0x20, 0x89); // misc
+ tda10086_write_byte(state, 0x30, 0x04); // acquisition period length
+ tda10086_write_byte(state, 0x32, 0x00); // irq off
+ tda10086_write_byte(state, 0x31, 0x56); // setup AFC
+
+ // setup PLL (assumes 16Mhz XIN)
+ tda10086_write_byte(state, 0x55, 0x2c); // misc PLL setup
+ tda10086_write_byte(state, 0x3a, 0x0b); // M=12
+ tda10086_write_byte(state, 0x3b, 0x01); // P=2
+ tda10086_write_mask(state, 0x55, 0x20, 0x00); // powerup PLL
+
+ // setup TS interface
+ tda10086_write_byte(state, 0x11, 0x81);
+ tda10086_write_byte(state, 0x12, 0x81);
+ tda10086_write_byte(state, 0x19, 0x40); // parallel mode A + MSBFIRST
+ tda10086_write_byte(state, 0x56, 0x80); // powerdown WPLL - unused in the mode we use
+ tda10086_write_byte(state, 0x57, 0x08); // bypass WPLL - unused in the mode we use
+ tda10086_write_byte(state, 0x10, 0x2a);
+
+ // setup ADC
+ tda10086_write_byte(state, 0x58, 0x61); // ADC setup
+ tda10086_write_mask(state, 0x58, 0x01, 0x00); // powerup ADC
+
+ // setup AGC
+ tda10086_write_byte(state, 0x05, 0x0B);
+ tda10086_write_byte(state, 0x37, 0x63);
+ tda10086_write_byte(state, 0x3f, 0x03); // NOTE: flydvb uses 0x0a and varies it
+ tda10086_write_byte(state, 0x40, 0x64);
+ tda10086_write_byte(state, 0x41, 0x4f);
+ tda10086_write_byte(state, 0x42, 0x43);
+
+ // setup viterbi
+ tda10086_write_byte(state, 0x1a, 0x11); // VBER 10^6, DVB, QPSK
+
+ // setup carrier recovery
+ tda10086_write_byte(state, 0x3d, 0x80);
+
+ // setup SEC
+ tda10086_write_byte(state, 0x36, 0x00); // all SEC off
+ tda10086_write_byte(state, 0x34, (((1<<19) * (22000/1000)) / (SACLK/1000))); // } tone frequency
+ tda10086_write_byte(state, 0x35, (((1<<19) * (22000/1000)) / (SACLK/1000)) >> 8); // }
+
+ return 0;
+}
+
+static void tda10086_diseqc_wait(struct tda10086_state *state)
+{
+ unsigned long timeout = jiffies + msecs_to_jiffies(200);
+ while (!(tda10086_read_byte(state, 0x50) & 0x01)) {
+ if(time_after(jiffies, timeout)) {
+ printk("%s: diseqc queue not ready, command may be lost.\n", __FUNCTION__);
+ break;
+ }
+ msleep(10);
+ }
+}
+
+static int tda10086_set_tone (struct dvb_frontend* fe, fe_sec_tone_mode_t tone)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch(tone) {
+ case SEC_TONE_OFF:
+ tda10086_write_byte(state, 0x36, 0x00);
+ break;
+
+ case SEC_TONE_ON:
+ tda10086_write_byte(state, 0x36, 0x01);
+ break;
+ }
+
+ return 0;
+}
+
+static int tda10086_send_master_cmd (struct dvb_frontend* fe,
+ struct dvb_diseqc_master_cmd* cmd)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ int i;
+ u8 oldval;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (cmd->msg_len > 6)
+ return -EINVAL;
+ oldval = tda10086_read_byte(state, 0x36);
+
+ for(i=0; i< cmd->msg_len; i++) {
+ tda10086_write_byte(state, 0x48+i, cmd->msg[i]);
+ }
+ tda10086_write_byte(state, 0x36, 0x08 | ((cmd->msg_len + 1) << 4));
+
+ tda10086_diseqc_wait(state);
+
+ tda10086_write_byte(state, 0x36, oldval);
+
+ return 0;
+}
+
+static int tda10086_send_burst (struct dvb_frontend* fe, fe_sec_mini_cmd_t minicmd)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 oldval = tda10086_read_byte(state, 0x36);
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ switch(minicmd) {
+ case SEC_MINI_A:
+ tda10086_write_byte(state, 0x36, 0x04);
+ break;
+
+ case SEC_MINI_B:
+ tda10086_write_byte(state, 0x36, 0x06);
+ break;
+ }
+
+ tda10086_diseqc_wait(state);
+
+ tda10086_write_byte(state, 0x36, oldval);
+
+ return 0;
+}
+
+static int tda10086_set_inversion(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 invval = 0x80;
+
+ dprintk ("%s %i %i\n", __FUNCTION__, fe_params->inversion, state->config->invert);
+
+ switch(fe_params->inversion) {
+ case INVERSION_OFF:
+ if (state->config->invert)
+ invval = 0x40;
+ break;
+ case INVERSION_ON:
+ if (!state->config->invert)
+ invval = 0x40;
+ break;
+ case INVERSION_AUTO:
+ invval = 0x00;
+ break;
+ }
+ tda10086_write_mask(state, 0x0c, 0xc0, invval);
+
+ return 0;
+}
+
+static int tda10086_set_symbol_rate(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 dfn = 0;
+ u8 afs = 0;
+ u8 byp = 0;
+ u8 reg37 = 0x43;
+ u8 reg42 = 0x43;
+ u64 big;
+ u32 tmp;
+ u32 bdr;
+ u32 bdri;
+ u32 symbol_rate = fe_params->u.qpsk.symbol_rate;
+
+ dprintk ("%s %i\n", __FUNCTION__, symbol_rate);
+
+ // setup the decimation and anti-aliasing filters..
+ if (symbol_rate < (u32) (SACLK * 0.0137)) {
+ dfn=4;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0208)) {
+ dfn=4;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.0270)) {
+ dfn=3;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0416)) {
+ dfn=3;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.0550)) {
+ dfn=2;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.0833)) {
+ dfn=2;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.1100)) {
+ dfn=1;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.1666)) {
+ dfn=1;
+ afs=0;
+ } else if (symbol_rate < (u32) (SACLK * 0.2200)) {
+ dfn=0;
+ afs=1;
+ } else if (symbol_rate < (u32) (SACLK * 0.3333)) {
+ dfn=0;
+ afs=0;
+ } else {
+ reg37 = 0x63;
+ reg42 = 0x4f;
+ byp=1;
+ }
+
+ // calculate BDR
+ big = (1ULL<<21) * ((u64) symbol_rate/1000ULL) * (1ULL<<dfn);
+ big += ((SACLK/1000ULL)-1ULL);
+ do_div(big, (SACLK/1000ULL));
+ bdr = big & 0xfffff;
+
+ // calculate BDRI
+ tmp = (1<<dfn)*(symbol_rate/1000);
+ bdri = ((32 * (SACLK/1000)) + (tmp-1)) / tmp;
+
+ tda10086_write_byte(state, 0x21, (afs << 7) | dfn);
+ tda10086_write_mask(state, 0x20, 0x08, byp << 3);
+ tda10086_write_byte(state, 0x06, bdr);
+ tda10086_write_byte(state, 0x07, bdr >> 8);
+ tda10086_write_byte(state, 0x08, bdr >> 16);
+ tda10086_write_byte(state, 0x09, bdri);
+ tda10086_write_byte(state, 0x37, reg37);
+ tda10086_write_byte(state, 0x42, reg42);
+
+ return 0;
+}
+
+static int tda10086_set_fec(struct tda10086_state *state,
+ struct dvb_frontend_parameters *fe_params)
+{
+ u8 fecval;
+
+ dprintk ("%s %i\n", __FUNCTION__, fe_params->u.qpsk.fec_inner);
+
+ switch(fe_params->u.qpsk.fec_inner) {
+ case FEC_1_2:
+ fecval = 0x00;
+ break;
+ case FEC_2_3:
+ fecval = 0x01;
+ break;
+ case FEC_3_4:
+ fecval = 0x02;
+ break;
+ case FEC_4_5:
+ fecval = 0x03;
+ break;
+ case FEC_5_6:
+ fecval = 0x04;
+ break;
+ case FEC_6_7:
+ fecval = 0x05;
+ break;
+ case FEC_7_8:
+ fecval = 0x06;
+ break;
+ case FEC_8_9:
+ fecval = 0x07;
+ break;
+ case FEC_AUTO:
+ fecval = 0x08;
+ break;
+ default:
+ return -1;
+ }
+ tda10086_write_byte(state, 0x0d, fecval);
+
+ return 0;
+}
+
+static int tda10086_set_frontend(struct dvb_frontend* fe,
+ struct dvb_frontend_parameters *fe_params)
+{
+ struct tda10086_state *state = fe->demodulator_priv;
+ int ret;
+ u32 freq = 0;
+ int freqoff;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // set params
+ if (fe->ops.tuner_ops.set_params) {
+ fe->ops.tuner_ops.set_params(fe, fe_params);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (fe->ops.tuner_ops.get_frequency)
+ fe->ops.tuner_ops.get_frequency(fe, &freq);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+ }
+
+ // calcluate the frequency offset (in *Hz* not kHz)
+ freqoff = fe_params->frequency - freq;
+ freqoff = ((1<<16) * freqoff) / (SACLK/1000);
+ tda10086_write_byte(state, 0x3d, 0x80 | ((freqoff >> 8) & 0x7f));
+ tda10086_write_byte(state, 0x3e, freqoff);
+
+ if ((ret = tda10086_set_inversion(state, fe_params)) < 0)
+ return ret;
+ if ((ret = tda10086_set_symbol_rate(state, fe_params)) < 0)
+ return ret;
+ if ((ret = tda10086_set_fec(state, fe_params)) < 0)
+ return ret;
+
+ // soft reset + disable TS output until lock
+ tda10086_write_mask(state, 0x10, 0x40, 0x40);
+ tda10086_write_mask(state, 0x00, 0x01, 0x00);
+
+ state->symbol_rate = fe_params->u.qpsk.symbol_rate;
+ state->frequency = fe_params->frequency;
+ return 0;
+}
+
+static int tda10086_get_frontend(struct dvb_frontend* fe, struct dvb_frontend_parameters *fe_params)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 val;
+ int tmp;
+ u64 tmp64;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // calculate the updated frequency (note: we convert from Hz->kHz)
+ tmp64 = tda10086_read_byte(state, 0x52);
+ tmp64 |= (tda10086_read_byte(state, 0x51) << 8);
+ if (tmp64 & 0x8000)
+ tmp64 |= 0xffffffffffff0000ULL;
+ tmp64 = (tmp64 * (SACLK/1000ULL));
+ do_div(tmp64, (1ULL<<15) * (1ULL<<1));
+ fe_params->frequency = (int) state->frequency + (int) tmp64;
+
+ // the inversion
+ val = tda10086_read_byte(state, 0x0c);
+ if (val & 0x80) {
+ switch(val & 0x40) {
+ case 0x00:
+ fe_params->inversion = INVERSION_OFF;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_ON;
+ break;
+ default:
+ fe_params->inversion = INVERSION_ON;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_OFF;
+ break;
+ }
+ } else {
+ tda10086_read_byte(state, 0x0f);
+ switch(val & 0x02) {
+ case 0x00:
+ fe_params->inversion = INVERSION_OFF;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_ON;
+ break;
+ default:
+ fe_params->inversion = INVERSION_ON;
+ if (state->config->invert)
+ fe_params->inversion = INVERSION_OFF;
+ break;
+ }
+ }
+
+ // calculate the updated symbol rate
+ tmp = tda10086_read_byte(state, 0x1d);
+ if (tmp & 0x80)
+ tmp |= 0xffffff00;
+ tmp = (tmp * 480 * (1<<1)) / 128;
+ tmp = ((state->symbol_rate/1000) * tmp) / (1000000/1000);
+ fe_params->u.qpsk.symbol_rate = state->symbol_rate + tmp;
+
+ // the FEC
+ val = (tda10086_read_byte(state, 0x0d) & 0x70) >> 4;
+ switch(val) {
+ case 0x00:
+ fe_params->u.qpsk.fec_inner = FEC_1_2;
+ break;
+ case 0x01:
+ fe_params->u.qpsk.fec_inner = FEC_2_3;
+ break;
+ case 0x02:
+ fe_params->u.qpsk.fec_inner = FEC_3_4;
+ break;
+ case 0x03:
+ fe_params->u.qpsk.fec_inner = FEC_4_5;
+ break;
+ case 0x04:
+ fe_params->u.qpsk.fec_inner = FEC_5_6;
+ break;
+ case 0x05:
+ fe_params->u.qpsk.fec_inner = FEC_6_7;
+ break;
+ case 0x06:
+ fe_params->u.qpsk.fec_inner = FEC_7_8;
+ break;
+ case 0x07:
+ fe_params->u.qpsk.fec_inner = FEC_8_9;
+ break;
+ }
+
+ return 0;
+}
+
+static int tda10086_read_status(struct dvb_frontend* fe, fe_status_t *fe_status)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 val;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ val = tda10086_read_byte(state, 0x0e);
+ *fe_status = 0;
+ if (val & 0x01)
+ *fe_status |= FE_HAS_SIGNAL;
+ if (val & 0x02)
+ *fe_status |= FE_HAS_CARRIER;
+ if (val & 0x04)
+ *fe_status |= FE_HAS_VITERBI;
+ if (val & 0x08)
+ *fe_status |= FE_HAS_SYNC;
+ if (val & 0x10)
+ *fe_status |= FE_HAS_LOCK;
+
+ return 0;
+}
+
+static int tda10086_read_signal_strength(struct dvb_frontend* fe, u16 * signal)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 _str;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ _str = tda10086_read_byte(state, 0x43);
+ *signal = (_str << 8) | _str;
+
+ return 0;
+}
+
+static int tda10086_read_snr(struct dvb_frontend* fe, u16 * snr)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+ u8 _snr;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ _snr = tda10086_read_byte(state, 0x1c);
+ *snr = (_snr << 8) | _snr;
+
+ return 0;
+}
+
+static int tda10086_read_ucblocks(struct dvb_frontend* fe, u32* ucblocks)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // read it
+ *ucblocks = tda10086_read_byte(state, 0x18) & 0x7f;
+
+ // reset counter
+ tda10086_write_byte(state, 0x18, 0x00);
+ tda10086_write_byte(state, 0x18, 0x80);
+
+ return 0;
+}
+
+static int tda10086_read_ber(struct dvb_frontend* fe, u32* ber)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ // read it
+ *ber = 0;
+ *ber |= tda10086_read_byte(state, 0x15);
+ *ber |= tda10086_read_byte(state, 0x16) << 8;
+ *ber |= (tda10086_read_byte(state, 0x17) & 0xf) << 16;
+
+ return 0;
+}
+
+static int tda10086_sleep(struct dvb_frontend* fe)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ tda10086_write_mask(state, 0x00, 0x08, 0x08);
+
+ return 0;
+}
+
+static int tda10086_i2c_gate_ctrl(struct dvb_frontend* fe, int enable)
+{
+ struct tda10086_state* state = fe->demodulator_priv;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ if (enable) {
+ tda10086_write_mask(state, 0x00, 0x10, 0x10);
+ } else {
+ tda10086_write_mask(state, 0x00, 0x10, 0x00);
+ }
+
+ return 0;
+}
+
+static int tda10086_get_tune_settings(struct dvb_frontend* fe, struct dvb_frontend_tune_settings* fesettings)
+{
+ if (fesettings->parameters.u.qpsk.symbol_rate > 20000000) {
+ fesettings->min_delay_ms = 50;
+ fesettings->step_size = 2000;
+ fesettings->max_drift = 8000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 12000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 1500;
+ fesettings->max_drift = 9000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 8000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 1000;
+ fesettings->max_drift = 8000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 4000000) {
+ fesettings->min_delay_ms = 100;
+ fesettings->step_size = 500;
+ fesettings->max_drift = 7000;
+ } else if (fesettings->parameters.u.qpsk.symbol_rate > 2000000) {
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
+ fesettings->max_drift = 14 * fesettings->step_size;
+ } else {
+ fesettings->min_delay_ms = 200;
+ fesettings->step_size = (fesettings->parameters.u.qpsk.symbol_rate / 8000);
+ fesettings->max_drift = 18 * fesettings->step_size;
+ }
+
+ return 0;
+}
+
+static void tda10086_release(struct dvb_frontend* fe)
+{
+ struct tda10086_state *state = fe->demodulator_priv;
+ tda10086_sleep(fe);
+ kfree(state);
+}
+
+static struct dvb_frontend_ops tda10086_ops = {
+
+ .info = {
+ .name = "Philips TDA10086 DVB-S",
+ .type = FE_QPSK,
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_stepsize = 125, /* kHz for QPSK frontends */
+ .symbol_rate_min = 1000000,
+ .symbol_rate_max = 45000000,
+ .caps = FE_CAN_INVERSION_AUTO |
+ FE_CAN_FEC_1_2 | FE_CAN_FEC_2_3 | FE_CAN_FEC_3_4 |
+ FE_CAN_FEC_5_6 | FE_CAN_FEC_6_7 | FE_CAN_FEC_7_8 | FE_CAN_FEC_AUTO |
+ FE_CAN_QPSK
+ },
+
+ .release = tda10086_release,
+
+ .init = tda10086_init,
+ .sleep = tda10086_sleep,
+ .i2c_gate_ctrl = tda10086_i2c_gate_ctrl,
+
+ .set_frontend = tda10086_set_frontend,
+ .get_frontend = tda10086_get_frontend,
+ .get_tune_settings = tda10086_get_tune_settings,
+
+ .read_status = tda10086_read_status,
+ .read_ber = tda10086_read_ber,
+ .read_signal_strength = tda10086_read_signal_strength,
+ .read_snr = tda10086_read_snr,
+ .read_ucblocks = tda10086_read_ucblocks,
+
+ .diseqc_send_master_cmd = tda10086_send_master_cmd,
+ .diseqc_send_burst = tda10086_send_burst,
+ .set_tone = tda10086_set_tone,
+};
+
+struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
+ struct i2c_adapter* i2c)
+{
+ struct tda10086_state *state;
+
+ dprintk ("%s\n", __FUNCTION__);
+
+ /* allocate memory for the internal state */
+ state = kmalloc(sizeof(struct tda10086_state), GFP_KERNEL);
+ if (!state)
+ return NULL;
+
+ /* setup the state */
+ state->config = config;
+ state->i2c = i2c;
+
+ /* check if the demod is there */
+ if (tda10086_read_byte(state, 0x1e) != 0xe1) {
+ kfree(state);
+ return NULL;
+ }
+
+ /* create dvb_frontend */
+ memcpy(&state->frontend.ops, &tda10086_ops, sizeof(struct dvb_frontend_ops));
+ state->frontend.demodulator_priv = state;
+ return &state->frontend;
+}
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("Philips TDA10086 DVB-S Demodulator");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
+
+EXPORT_SYMBOL(tda10086_attach);
diff --git a/drivers/media/dvb/frontends/tda10086.h b/drivers/media/dvb/frontends/tda10086.h
new file mode 100644
index 00000000000..e8061db1112
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda10086.h
@@ -0,0 +1,41 @@
+ /*
+ Driver for Philips tda10086 DVBS Frontend
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef TDA10086_H
+#define TDA10086_H
+
+#include <linux/dvb/frontend.h>
+#include <linux/firmware.h>
+
+struct tda10086_config
+{
+ /* the demodulator's i2c address */
+ u8 demod_address;
+
+ /* does the "inversion" need inverted? */
+ u8 invert;
+};
+
+extern struct dvb_frontend* tda10086_attach(const struct tda10086_config* config,
+ struct i2c_adapter* i2c);
+
+#endif // TDA10086_H
diff --git a/drivers/media/dvb/frontends/tda8083.h b/drivers/media/dvb/frontends/tda8083.h
index e7a48f61ea2..aae15bdce6e 100644
--- a/drivers/media/dvb/frontends/tda8083.h
+++ b/drivers/media/dvb/frontends/tda8083.h
@@ -35,7 +35,16 @@ struct tda8083_config
u8 demod_address;
};
+#if defined(CONFIG_DVB_TDA8083) || defined(CONFIG_DVB_TDA8083_MODULE)
extern struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* tda8083_attach(const struct tda8083_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TDA8083
#endif // TDA8083_H
diff --git a/drivers/media/dvb/frontends/tda826x.c b/drivers/media/dvb/frontends/tda826x.c
new file mode 100644
index 00000000000..eeab26bd36e
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda826x.c
@@ -0,0 +1,173 @@
+ /*
+ Driver for Philips tda8262/tda8263 DVBS Silicon tuners
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "tda826x.h"
+
+static int debug = 0;
+#define dprintk(args...) \
+ do { \
+ if (debug) printk(KERN_DEBUG "tda826x: " args); \
+ } while (0)
+
+struct tda826x_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u8 has_loopthrough:1;
+ u32 frequency;
+};
+
+static int tda826x_release(struct dvb_frontend *fe)
+{
+ if (fe->tuner_priv)
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int tda826x_sleep(struct dvb_frontend *fe)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 buf [] = { 0x00, 0x8d };
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 2 };
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ if (!priv->has_loopthrough)
+ buf[1] = 0xad;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ dprintk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tda826x_set_params(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ int ret;
+ u32 div;
+ u8 buf [11];
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = buf, .len = 11 };
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ div = (params->frequency + (1000-1)) / 1000;
+
+ buf[0] = 0x00; // subaddress
+ buf[1] = 0x09; // powerdown RSSI + the magic value 1
+ if (!priv->has_loopthrough)
+ buf[1] |= 0x20; // power down loopthrough if not needed
+ buf[2] = (1<<5) | 0x0b; // 1Mhz + 0.45 VCO
+ buf[3] = div >> 7;
+ buf[4] = div << 1;
+ buf[5] = 0xff; // basedband filter to max
+ buf[6] = 0xfe; // gains at max + no RF attenuation
+ buf[7] = 0x83; // charge pumps at high, tests off
+ buf[8] = 0x80; // recommended value 4 for AMPVCO + disable ports.
+ buf[9] = 0x1a; // normal caltime + recommended values for SELTH + SELVTL
+ buf[10] = 0xd4; // recommended value 13 for BBIAS + unknown bit set on
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ dprintk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ priv->frequency = div * 1000;
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tda826x_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tda826x_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops tda826x_tuner_ops = {
+ .info = {
+ .name = "Philips TDA826X",
+ .frequency_min = 950000,
+ .frequency_min = 2175000
+ },
+ .release = tda826x_release,
+ .sleep = tda826x_sleep,
+ .set_params = tda826x_set_params,
+ .get_frequency = tda826x_get_frequency,
+};
+
+struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough)
+{
+ struct tda826x_priv *priv = NULL;
+ u8 b1 [] = { 0, 0 };
+ struct i2c_msg msg = { .addr = addr, .flags = I2C_M_RD, .buf = b1, .len = 2 };
+ int ret;
+
+ dprintk("%s:\n", __FUNCTION__);
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = i2c_transfer (i2c, &msg, 1);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 1)
+ return NULL;
+ if (!(b1[1] & 0x80))
+ return NULL;
+
+ priv = kzalloc(sizeof(struct tda826x_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+ priv->has_loopthrough = has_loopthrough;
+
+ memcpy(&fe->ops.tuner_ops, &tda826x_tuner_ops, sizeof(struct dvb_tuner_ops));
+
+ fe->tuner_priv = priv;
+
+ return fe;
+}
+EXPORT_SYMBOL(tda826x_attach);
+
+module_param(debug, int, 0644);
+MODULE_PARM_DESC(debug, "Turn on/off frontend debugging (default:off).");
+
+MODULE_DESCRIPTION("DVB TDA826x driver");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tda826x.h b/drivers/media/dvb/frontends/tda826x.h
new file mode 100644
index 00000000000..3307607632b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tda826x.h
@@ -0,0 +1,40 @@
+ /*
+ Driver for Philips tda8262/tda8263 DVBS Silicon tuners
+
+ (c) 2006 Andrew de Quincey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef __DVB_TDA826X_H__
+#define __DVB_TDA826X_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+/**
+ * Attach a tda826x tuner to the supplied frontend structure.
+ *
+ * @param fe Frontend to attach to.
+ * @param addr i2c address of the tuner.
+ * @param i2c i2c adapter to use.
+ * @param has_loopthrough Set to 1 if the card has a loopthrough RF connector.
+ * @return FE pointer on success, NULL on failure.
+ */
+extern struct dvb_frontend *tda826x_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c, int has_loopthrough);
+
+#endif
diff --git a/drivers/media/dvb/frontends/tua6100.c b/drivers/media/dvb/frontends/tua6100.c
new file mode 100644
index 00000000000..88554393a9b
--- /dev/null
+++ b/drivers/media/dvb/frontends/tua6100.c
@@ -0,0 +1,205 @@
+/**
+ * Driver for Infineon tua6100 pll.
+ *
+ * (c) 2006 Andrew de Quincey
+ *
+ * Based on code found in budget-av.c, which has the following:
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ * Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/dvb/frontend.h>
+#include <asm/types.h>
+
+#include "tua6100.h"
+
+struct tua6100_priv {
+ /* i2c details */
+ int i2c_address;
+ struct i2c_adapter *i2c;
+ u32 frequency;
+};
+
+static int tua6100_release(struct dvb_frontend *fe)
+{
+ if (fe->tuner_priv)
+ kfree(fe->tuner_priv);
+ fe->tuner_priv = NULL;
+ return 0;
+}
+
+static int tua6100_sleep(struct dvb_frontend *fe)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ int ret;
+ u8 reg0[] = { 0x00, 0x00 };
+ struct i2c_msg msg = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if ((ret = i2c_transfer (priv->i2c, &msg, 1)) != 1) {
+ printk("%s: i2c error\n", __FUNCTION__);
+ }
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return (ret == 1) ? 0 : ret;
+}
+
+static int tua6100_set_params(struct dvb_frontend *fe,
+ struct dvb_frontend_parameters *params)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ u32 div;
+ u32 prediv;
+ u8 reg0[] = { 0x00, 0x00 };
+ u8 reg1[] = { 0x01, 0x00, 0x00, 0x00 };
+ u8 reg2[] = { 0x02, 0x00, 0x00 };
+ struct i2c_msg msg0 = { .addr = priv->i2c_address, .flags = 0, .buf = reg0, .len = 2 };
+ struct i2c_msg msg1 = { .addr = priv->i2c_address, .flags = 0, .buf = reg1, .len = 4 };
+ struct i2c_msg msg2 = { .addr = priv->i2c_address, .flags = 0, .buf = reg2, .len = 3 };
+
+#define _R 4
+#define _P 32
+#define _ri 4000000
+
+ // setup register 0
+ if (params->frequency < 2000000) {
+ reg0[1] = 0x03;
+ } else {
+ reg0[1] = 0x07;
+ }
+
+ // setup register 1
+ if (params->frequency < 1630000) {
+ reg1[1] = 0x2c;
+ } else {
+ reg1[1] = 0x0c;
+ }
+ if (_P == 64)
+ reg1[1] |= 0x40;
+ if (params->frequency >= 1525000)
+ reg1[1] |= 0x80;
+
+ // register 2
+ reg2[1] = (_R >> 8) & 0x03;
+ reg2[2] = _R;
+ if (params->frequency < 1455000) {
+ reg2[1] |= 0x1c;
+ } else if (params->frequency < 1630000) {
+ reg2[1] |= 0x0c;
+ } else {
+ reg2[1] |= 0x1c;
+ }
+
+ // The N divisor ratio (note: params->frequency is in kHz, but we need it in Hz)
+ prediv = (params->frequency * _R) / (_ri / 1000);
+ div = prediv / _P;
+ reg1[1] |= (div >> 9) & 0x03;
+ reg1[2] = div >> 1;
+ reg1[3] = (div << 7);
+ priv->frequency = ((div * _P) * (_ri / 1000)) / _R;
+
+ // Finally, calculate and store the value for A
+ reg1[3] |= (prediv - (div*_P)) & 0x7f;
+
+#undef _R
+#undef _P
+#undef _ri
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg0, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg2, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ if (i2c_transfer(priv->i2c, &msg1, 1) != 1)
+ return -EIO;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ return 0;
+}
+
+static int tua6100_get_frequency(struct dvb_frontend *fe, u32 *frequency)
+{
+ struct tua6100_priv *priv = fe->tuner_priv;
+ *frequency = priv->frequency;
+ return 0;
+}
+
+static struct dvb_tuner_ops tua6100_tuner_ops = {
+ .info = {
+ .name = "Infineon TUA6100",
+ .frequency_min = 950000,
+ .frequency_max = 2150000,
+ .frequency_step = 1000,
+ },
+ .release = tua6100_release,
+ .sleep = tua6100_sleep,
+ .set_params = tua6100_set_params,
+ .get_frequency = tua6100_get_frequency,
+};
+
+struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
+{
+ struct tua6100_priv *priv = NULL;
+ u8 b1 [] = { 0x80 };
+ u8 b2 [] = { 0x00 };
+ struct i2c_msg msg [] = { { .addr = addr, .flags = 0, .buf = b1, .len = 1 },
+ { .addr = addr, .flags = I2C_M_RD, .buf = b2, .len = 1 } };
+ int ret;
+
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 1);
+ ret = i2c_transfer (i2c, msg, 2);
+ if (fe->ops.i2c_gate_ctrl)
+ fe->ops.i2c_gate_ctrl(fe, 0);
+
+ if (ret != 2)
+ return NULL;
+
+ priv = kzalloc(sizeof(struct tua6100_priv), GFP_KERNEL);
+ if (priv == NULL)
+ return NULL;
+
+ priv->i2c_address = addr;
+ priv->i2c = i2c;
+
+ memcpy(&fe->ops.tuner_ops, &tua6100_tuner_ops, sizeof(struct dvb_tuner_ops));
+ fe->tuner_priv = priv;
+ return fe;
+}
+EXPORT_SYMBOL(tua6100_attach);
+
+MODULE_DESCRIPTION("DVB tua6100 driver");
+MODULE_AUTHOR("Andrew de Quincey");
+MODULE_LICENSE("GPL");
diff --git a/drivers/media/dvb/frontends/tua6100.h b/drivers/media/dvb/frontends/tua6100.h
new file mode 100644
index 00000000000..8f98033ffa7
--- /dev/null
+++ b/drivers/media/dvb/frontends/tua6100.h
@@ -0,0 +1,47 @@
+/**
+ * Driver for Infineon tua6100 PLL.
+ *
+ * (c) 2006 Andrew de Quincey
+ *
+ * Based on code found in budget-av.c, which has the following:
+ * Compiled from various sources by Michael Hunold <michael@mihu.de>
+ *
+ * CI interface support (c) 2004 Olivier Gournet <ogournet@anevia.com> &
+ * Andrew de Quincey <adq_dvb@lidskialf.net>
+ *
+ * Copyright (C) 2002 Ralph Metzler <rjkm@metzlerbros.de>
+ *
+ * Copyright (C) 1999-2002 Ralph Metzler
+ * & Marcus Metzler for convergence integrated media GmbH
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __DVB_TUA6100_H__
+#define __DVB_TUA6100_H__
+
+#include <linux/i2c.h>
+#include "dvb_frontend.h"
+
+#if defined(CONFIG_DVB_TUA6100) || defined(CONFIG_DVB_TUA6100_MODULE)
+extern struct dvb_frontend *tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c);
+#else
+static inline struct dvb_frontend* tua6100_attach(struct dvb_frontend *fe, int addr, struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_TUA6100
+
+#endif
diff --git a/drivers/media/dvb/frontends/ves1820.h b/drivers/media/dvb/frontends/ves1820.h
index 520f09522fb..f0c9dded39d 100644
--- a/drivers/media/dvb/frontends/ves1820.h
+++ b/drivers/media/dvb/frontends/ves1820.h
@@ -41,7 +41,16 @@ struct ves1820_config
u8 selagc:1;
};
+#if defined(CONFIG_DVB_VES1820) || defined(CONFIG_DVB_VES1820_MODULE)
extern struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
struct i2c_adapter* i2c, u8 pwm);
+#else
+static inline struct dvb_frontend* ves1820_attach(const struct ves1820_config* config,
+ struct i2c_adapter* i2c, u8 pwm)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_VES1820
#endif // VES1820_H
diff --git a/drivers/media/dvb/frontends/ves1x93.h b/drivers/media/dvb/frontends/ves1x93.h
index ba88ae0855c..395fed39b28 100644
--- a/drivers/media/dvb/frontends/ves1x93.h
+++ b/drivers/media/dvb/frontends/ves1x93.h
@@ -40,7 +40,16 @@ struct ves1x93_config
u8 invert_pwm:1;
};
+#if defined(CONFIG_DVB_VES1X93) || defined(CONFIG_DVB_VES1X93_MODULE)
extern struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
struct i2c_adapter* i2c);
+#else
+static inline struct dvb_frontend* ves1x93_attach(const struct ves1x93_config* config,
+ struct i2c_adapter* i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_VES1X93
#endif // VES1X93_H
diff --git a/drivers/media/dvb/frontends/zl10353.c b/drivers/media/dvb/frontends/zl10353.c
index 2b95e8b6cd3..0e9b59af271 100644
--- a/drivers/media/dvb/frontends/zl10353.c
+++ b/drivers/media/dvb/frontends/zl10353.c
@@ -140,6 +140,8 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
zl10353_single_write(fe, 0x5E, 0x00);
zl10353_single_write(fe, 0x65, 0x5A);
zl10353_single_write(fe, 0x66, 0xE9);
+ zl10353_single_write(fe, 0x6C, 0xCD);
+ zl10353_single_write(fe, 0x6D, 0x7E);
zl10353_single_write(fe, 0x62, 0x0A);
// if there is no attached secondary tuner, we call set_params to program
@@ -168,6 +170,7 @@ static int zl10353_set_parameters(struct dvb_frontend *fe,
// even if there isn't a PLL attached to the secondary bus
zl10353_write(fe, pllbuf, sizeof(pllbuf));
+ zl10353_single_write(fe, 0x5F, 0x13);
zl10353_single_write(fe, 0x70, 0x01);
udelay(250);
zl10353_single_write(fe, 0xE4, 0x00);
@@ -243,9 +246,12 @@ static int zl10353_init(struct dvb_frontend *fe)
if (debug_regs)
zl10353_dump_regs(fe);
+ if (state->config.parallel_ts)
+ zl10353_reset_attach[2] &= ~0x20;
/* Do a "hard" reset if not already done */
- if (zl10353_read_register(state, 0x50) != 0x03) {
+ if (zl10353_read_register(state, 0x50) != zl10353_reset_attach[1] ||
+ zl10353_read_register(state, 0x51) != zl10353_reset_attach[2]) {
rc = zl10353_write(fe, zl10353_reset_attach,
sizeof(zl10353_reset_attach));
if (debug_regs)
@@ -258,7 +264,6 @@ static int zl10353_init(struct dvb_frontend *fe)
static void zl10353_release(struct dvb_frontend *fe)
{
struct zl10353_state *state = fe->demodulator_priv;
-
kfree(state);
}
@@ -314,6 +319,7 @@ static struct dvb_frontend_ops zl10353_ops = {
.init = zl10353_init,
.sleep = zl10353_sleep,
+ .write = zl10353_write,
.set_frontend = zl10353_set_parameters,
.get_tune_settings = zl10353_get_tune_settings,
@@ -330,4 +336,3 @@ MODULE_AUTHOR("Chris Pascoe");
MODULE_LICENSE("GPL");
EXPORT_SYMBOL(zl10353_attach);
-EXPORT_SYMBOL(zl10353_write);
diff --git a/drivers/media/dvb/frontends/zl10353.h b/drivers/media/dvb/frontends/zl10353.h
index 9770cb840cf..79a947215c4 100644
--- a/drivers/media/dvb/frontends/zl10353.h
+++ b/drivers/media/dvb/frontends/zl10353.h
@@ -31,11 +31,21 @@ struct zl10353_config
/* set if no pll is connected to the secondary i2c bus */
int no_tuner;
+
+ /* set if parallel ts output is required */
+ int parallel_ts;
};
+#if defined(CONFIG_DVB_ZL10353) || defined(CONFIG_DVB_ZL10353_MODULE)
extern struct dvb_frontend* zl10353_attach(const struct zl10353_config *config,
struct i2c_adapter *i2c);
-
-extern int zl10353_write(struct dvb_frontend *fe, u8 *ibuf, int ilen);
+#else
+static inline struct dvb_frontend* zl10353_attach(const struct zl10353_config *config,
+ struct i2c_adapter *i2c)
+{
+ printk(KERN_WARNING "%s: driver disabled by Kconfig\n", __FUNCTION__);
+ return NULL;
+}
+#endif // CONFIG_DVB_ZL10353
#endif /* ZL10353_H */
diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig
index 5fb097595cf..95531a62499 100644
--- a/drivers/media/dvb/ttpci/Kconfig
+++ b/drivers/media/dvb/ttpci/Kconfig
@@ -1,17 +1,17 @@
config DVB_AV7110
tristate "AV7110 cards"
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
- select FW_LOADER
+ select FW_LOADER if !DVB_AV7110_FIRMWARE
select VIDEO_SAA7146_VV
select DVB_PLL
- select DVB_VES1820
- select DVB_VES1X93
- select DVB_STV0299
- select DVB_TDA8083
- select DVB_SP8870
- select DVB_STV0297
- select DVB_L64781
- select DVB_LNBP21
+ select DVB_VES1820 if !DVB_FE_CUSTOMISE
+ select DVB_VES1X93 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8083 if !DVB_FE_CUSTOMISE
+ select DVB_SP8870 if !DVB_FE_CUSTOMISE
+ select DVB_STV0297 if !DVB_FE_CUSTOMISE
+ select DVB_L64781 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
Support for SAA7146 and AV7110 based DVB cards as produced
by Fujitsu-Siemens, Technotrend, Hauppauge and others.
@@ -63,14 +63,16 @@ config DVB_BUDGET
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select VIDEO_SAA7146
select DVB_PLL
- select DVB_STV0299
- select DVB_VES1X93
- select DVB_VES1820
- select DVB_L64781
- select DVB_TDA8083
- select DVB_TDA10021
- select DVB_S5H1420
- select DVB_LNBP21
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_VES1X93 if !DVB_FE_CUSTOMISE
+ select DVB_VES1820 if !DVB_FE_CUSTOMISE
+ select DVB_L64781 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8083 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+ select DVB_S5H1420 if !DVB_FE_CUSTOMISE
+ select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+ select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
Support for simple SAA7146 based DVB cards
(so called Budget- or Nova-PCI cards) without onboard
@@ -86,10 +88,10 @@ config DVB_BUDGET_CI
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select VIDEO_SAA7146
select DVB_PLL
- select DVB_STV0297
- select DVB_STV0299
- select DVB_TDA1004X
- select DVB_LNBP21
+ select DVB_STV0297 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
Support for simple SAA7146 based DVB cards
(so called Budget- or Nova-PCI cards) without onboard
@@ -108,9 +110,10 @@ config DVB_BUDGET_AV
depends on DVB_CORE && PCI && I2C && VIDEO_V4L1
select VIDEO_SAA7146_VV
select DVB_PLL
- select DVB_STV0299
- select DVB_TDA1004X
- select DVB_TDA10021
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_TDA10021 if !DVB_FE_CUSTOMISE
+ select DVB_TUA6100 if !DVB_FE_CUSTOMISE
select FW_LOADER
help
Support for simple SAA7146 based DVB cards
@@ -127,9 +130,9 @@ config DVB_BUDGET_PATCH
depends on DVB_CORE && DVB_BUDGET && VIDEO_V4L1
select DVB_AV7110
select DVB_PLL
- select DVB_STV0299
- select DVB_VES1X93
- select DVB_TDA8083
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_VES1X93 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8083 if !DVB_FE_CUSTOMISE
help
Support for Budget Patch (full TS) modification on
SAA7146+AV7110 based cards (DVB-S cards). This
diff --git a/drivers/media/dvb/ttpci/av7110.c b/drivers/media/dvb/ttpci/av7110.c
index 4506165c5de..bba23bcd1b1 100644
--- a/drivers/media/dvb/ttpci/av7110.c
+++ b/drivers/media/dvb/ttpci/av7110.c
@@ -1383,8 +1383,10 @@ static void dvb_unregister(struct av7110 *av7110)
dvb_dmxdev_release(&av7110->dmxdev);
dvb_dmx_release(&av7110->demux);
- if (av7110->fe != NULL)
+ if (av7110->fe != NULL) {
dvb_unregister_frontend(av7110->fe);
+ dvb_frontend_detach(av7110->fe);
+ }
dvb_unregister_device(av7110->osd_dev);
av7110_av_unregister(av7110);
av7110_ca_unregister(av7110);
@@ -1699,9 +1701,13 @@ static int alps_tdlb7_tuner_set_params(struct dvb_frontend* fe, struct dvb_front
static int alps_tdlb7_request_firmware(struct dvb_frontend* fe, const struct firmware **fw, char* name)
{
+#if defined(CONFIG_DVB_SP8870) || defined(CONFIG_DVB_SP8870_MODULE)
struct av7110* av7110 = (struct av7110*) fe->dvb->priv;
return request_firmware(fw, name, &av7110->dev->pci->dev);
+#else
+ return -EINVAL;
+#endif
}
static struct sp8870_config alps_tdlb7_config = {
@@ -2077,7 +2083,7 @@ static int frontend_init(struct av7110 *av7110)
if (av7110->dev->pci->subsystem_vendor == 0x110a) {
switch(av7110->dev->pci->subsystem_device) {
case 0x0000: // Fujitsu/Siemens DVB-Cable (ves1820/Philips CD1516(??))
- av7110->fe = ves1820_attach(&philips_cd1516_config,
+ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config,
&av7110->i2c_adap, read_pwm(av7110));
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params;
@@ -2092,7 +2098,7 @@ static int frontend_init(struct av7110 *av7110)
case 0x1002: // Hauppauge/TT WinTV DVB-S rev1.3SE
// try the ALPS BSRV2 first of all
- av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
@@ -2103,7 +2109,7 @@ static int frontend_init(struct av7110 *av7110)
}
// try the ALPS BSRU6 now
- av7110->fe = stv0299_attach(&alps_bsru6_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(stv0299_attach, &alps_bsru6_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
av7110->fe->tuner_priv = &av7110->i2c_adap;
@@ -2116,7 +2122,7 @@ static int frontend_init(struct av7110 *av7110)
}
// Try the grundig 29504-451
- av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
@@ -2130,7 +2136,7 @@ static int frontend_init(struct av7110 *av7110)
switch(av7110->dev->pci->subsystem_device) {
case 0x0000:
/* Siemens DVB-C (full-length card) VES1820/Philips CD1516 */
- av7110->fe = ves1820_attach(&philips_cd1516_config, &av7110->i2c_adap,
+ av7110->fe = dvb_attach(ves1820_attach, &philips_cd1516_config, &av7110->i2c_adap,
read_pwm(av7110));
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = philips_cd1516_tuner_set_params;
@@ -2138,7 +2144,7 @@ static int frontend_init(struct av7110 *av7110)
break;
case 0x0003:
/* Hauppauge DVB-C 2.1 VES1820/ALPS TDBE2 */
- av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap,
+ av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap,
read_pwm(av7110));
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
@@ -2148,17 +2154,24 @@ static int frontend_init(struct av7110 *av7110)
break;
case 0x0001: // Hauppauge/TT Nexus-T premium rev1.X
-
- // ALPS TDLB7
- av7110->fe = sp8870_attach(&alps_tdlb7_config, &av7110->i2c_adap);
+ // try ALPS TDLB7 first, then Grundig 29504-401
+ av7110->fe = dvb_attach(sp8870_attach, &alps_tdlb7_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_tdlb7_tuner_set_params;
+ break;
}
+ /* fall-thru */
+
+ case 0x0008: // Hauppauge/TT DVB-T
+ // Grundig 29504-401
+ av7110->fe = dvb_attach(l64781_attach, &grundig_29504_401_config, &av7110->i2c_adap);
+ if (av7110->fe)
+ av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
break;
case 0x0002: // Hauppauge/TT DVB-C premium rev2.X
- av7110->fe = ves1820_attach(&alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110));
+ av7110->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &av7110->i2c_adap, read_pwm(av7110));
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
}
@@ -2166,7 +2179,7 @@ static int frontend_init(struct av7110 *av7110)
case 0x0004: // Galaxis DVB-S rev1.3
/* ALPS BSRV2 */
- av7110->fe = ves1x93_attach(&alps_bsrv2_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
@@ -2178,7 +2191,7 @@ static int frontend_init(struct av7110 *av7110)
case 0x0006: /* Fujitsu-Siemens DVB-S rev 1.6 */
/* Grundig 29504-451 */
- av7110->fe = tda8083_attach(&grundig_29504_451_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(tda8083_attach, &grundig_29504_451_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
av7110->fe->ops.diseqc_send_master_cmd = av7110_diseqc_send_master_cmd;
@@ -2188,17 +2201,9 @@ static int frontend_init(struct av7110 *av7110)
}
break;
- case 0x0008: // Hauppauge/TT DVB-T
-
- av7110->fe = l64781_attach(&grundig_29504_401_config, &av7110->i2c_adap);
- if (av7110->fe) {
- av7110->fe->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
- }
- break;
-
case 0x000A: // Hauppauge/TT Nexus-CA rev1.X
- av7110->fe = stv0297_attach(&nexusca_stv0297_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(stv0297_attach, &nexusca_stv0297_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = nexusca_stv0297_tuner_set_params;
@@ -2214,12 +2219,12 @@ static int frontend_init(struct av7110 *av7110)
case 0x000E: /* Hauppauge/TT Nexus-S rev 2.3 */
/* ALPS BSBE1 */
- av7110->fe = stv0299_attach(&alps_bsbe1_config, &av7110->i2c_adap);
+ av7110->fe = dvb_attach(stv0299_attach, &alps_bsbe1_config, &av7110->i2c_adap);
if (av7110->fe) {
av7110->fe->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
av7110->fe->tuner_priv = &av7110->i2c_adap;
- if (lnbp21_attach(av7110->fe, &av7110->i2c_adap, 0, 0)) {
+ if (dvb_attach(lnbp21_attach, av7110->fe, &av7110->i2c_adap, 0, 0) == NULL) {
printk("dvb-ttpci: LNBP21 not found!\n");
if (av7110->fe->ops.release)
av7110->fe->ops.release(av7110->fe);
@@ -2255,8 +2260,7 @@ static int frontend_init(struct av7110 *av7110)
ret = dvb_register_frontend(&av7110->dvb_adapter, av7110->fe);
if (ret < 0) {
printk("av7110: Frontend registration failed!\n");
- if (av7110->fe->ops.release)
- av7110->fe->ops.release(av7110->fe);
+ dvb_frontend_detach(av7110->fe);
av7110->fe = NULL;
}
}
@@ -2823,7 +2827,7 @@ MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension av7110_extension = {
- .name = "dvb\0",
+ .name = "dvb",
.flags = SAA7146_I2C_SHORT_DELAY,
.module = THIS_MODULE,
diff --git a/drivers/media/dvb/ttpci/av7110_av.c b/drivers/media/dvb/ttpci/av7110_av.c
index 0f3a044aeb1..8c577cf30fb 100644
--- a/drivers/media/dvb/ttpci/av7110_av.c
+++ b/drivers/media/dvb/ttpci/av7110_av.c
@@ -33,7 +33,6 @@
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/byteorder/swabb.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
diff --git a/drivers/media/dvb/ttpci/av7110_ca.c b/drivers/media/dvb/ttpci/av7110_ca.c
index 6079e8865d5..dd9aee314e0 100644
--- a/drivers/media/dvb/ttpci/av7110_ca.c
+++ b/drivers/media/dvb/ttpci/av7110_ca.c
@@ -35,7 +35,6 @@
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/poll.h>
-#include <linux/byteorder/swabb.h>
#include <linux/smp_lock.h>
#include "av7110.h"
diff --git a/drivers/media/dvb/ttpci/av7110_hw.c b/drivers/media/dvb/ttpci/av7110_hw.c
index 75736f2fe83..37de2e88a27 100644
--- a/drivers/media/dvb/ttpci/av7110_hw.c
+++ b/drivers/media/dvb/ttpci/av7110_hw.c
@@ -34,7 +34,6 @@
#include <linux/string.h>
#include <linux/sched.h>
#include <linux/delay.h>
-#include <linux/byteorder/swabb.h>
#include <linux/smp_lock.h>
#include <linux/fs.h>
diff --git a/drivers/media/dvb/ttpci/av7110_v4l.c b/drivers/media/dvb/ttpci/av7110_v4l.c
index 6ffe53fdcf5..10cfe3131e7 100644
--- a/drivers/media/dvb/ttpci/av7110_v4l.c
+++ b/drivers/media/dvb/ttpci/av7110_v4l.c
@@ -32,7 +32,6 @@
#include <linux/fs.h>
#include <linux/timer.h>
#include <linux/poll.h>
-#include <linux/byteorder/swabb.h>
#include <linux/smp_lock.h>
#include "av7110.h"
@@ -922,7 +921,7 @@ static struct saa7146_ext_vv av7110_vv_data_st = {
static struct saa7146_ext_vv av7110_vv_data_c = {
.inputs = 1,
.audios = 1,
- .capabilities = V4L2_CAP_TUNER | V4L2_CAP_VBI_CAPTURE | V4L2_CAP_SLICED_VBI_OUTPUT,
+ .capabilities = V4L2_CAP_TUNER | V4L2_CAP_SLICED_VBI_OUTPUT,
.flags = SAA7146_USE_PORT_B_FOR_VBI,
.stds = &standard[0],
diff --git a/drivers/media/dvb/ttpci/budget-av.c b/drivers/media/dvb/ttpci/budget-av.c
index 2d21fec23b4..2235ff8b8a1 100644
--- a/drivers/media/dvb/ttpci/budget-av.c
+++ b/drivers/media/dvb/ttpci/budget-av.c
@@ -37,6 +37,7 @@
#include "stv0299.h"
#include "tda10021.h"
#include "tda1004x.h"
+#include "tua6100.h"
#include "dvb-pll.h"
#include <media/saa7146_vv.h>
#include <linux/module.h>
@@ -235,7 +236,7 @@ static int ciintf_slot_reset(struct dvb_ca_en50221 *ca, int slot)
/* set tda10021 back to original clock configuration on reset */
if (budget_av->tda10021_poclkp) {
- tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
+ tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
budget_av->tda10021_ts_enabled = 0;
}
@@ -257,7 +258,7 @@ static int ciintf_slot_shutdown(struct dvb_ca_en50221 *ca, int slot)
/* set tda10021 back to original clock configuration when cam removed */
if (budget_av->tda10021_poclkp) {
- tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
+ tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
budget_av->tda10021_ts_enabled = 0;
}
return 0;
@@ -277,7 +278,7 @@ static int ciintf_slot_ts_enable(struct dvb_ca_en50221 *ca, int slot)
/* tda10021 seems to need a different TS clock config when data is routed to the CAM */
if (budget_av->tda10021_poclkp) {
- tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1);
+ tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
budget_av->tda10021_ts_enabled = 1;
}
@@ -548,144 +549,6 @@ static int philips_su1278_ty_ci_tuner_set_params(struct dvb_frontend *fe,
return 0;
}
-#define MIN2(a,b) ((a) < (b) ? (a) : (b))
-#define MIN3(a,b,c) MIN2(MIN2(a,b),c)
-
-static int philips_su1278sh2_tua6100_tuner_set_params(struct dvb_frontend *fe,
- struct dvb_frontend_parameters *params)
-{
- u8 reg0 [2] = { 0x00, 0x00 };
- u8 reg1 [4] = { 0x01, 0x00, 0x00, 0x00 };
- u8 reg2 [3] = { 0x02, 0x00, 0x00 };
- int _fband;
- int first_ZF;
- int R, A, N, P, M;
- struct i2c_msg msg = {.addr = 0x60,.flags = 0,.buf = NULL,.len = 0 };
- int freq = params->frequency;
- struct budget *budget = (struct budget *) fe->dvb->priv;
-
- first_ZF = (freq) / 1000;
-
- if (abs(MIN2(abs(first_ZF-1190),abs(first_ZF-1790))) <
- abs(MIN3(abs(first_ZF-1202),abs(first_ZF-1542),abs(first_ZF-1890))))
- _fband = 2;
- else
- _fband = 3;
-
- if (_fband == 2) {
- if (((first_ZF >= 950) && (first_ZF < 1350)) ||
- ((first_ZF >= 1430) && (first_ZF < 1950)))
- reg0[1] = 0x07;
- else if (((first_ZF >= 1350) && (first_ZF < 1430)) ||
- ((first_ZF >= 1950) && (first_ZF < 2150)))
- reg0[1] = 0x0B;
- }
-
- if(_fband == 3) {
- if (((first_ZF >= 950) && (first_ZF < 1350)) ||
- ((first_ZF >= 1455) && (first_ZF < 1950)))
- reg0[1] = 0x07;
- else if (((first_ZF >= 1350) && (first_ZF < 1420)) ||
- ((first_ZF >= 1950) && (first_ZF < 2150)))
- reg0[1] = 0x0B;
- else if ((first_ZF >= 1420) && (first_ZF < 1455))
- reg0[1] = 0x0F;
- }
-
- if (first_ZF > 1525)
- reg1[1] |= 0x80;
- else
- reg1[1] &= 0x7F;
-
- if (_fband == 2) {
- if (first_ZF > 1430) { /* 1430MHZ */
- reg1[1] &= 0xCF; /* N2 */
- reg2[1] &= 0xCF; /* R2 */
- reg2[1] |= 0x10;
- } else {
- reg1[1] &= 0xCF; /* N2 */
- reg1[1] |= 0x20;
- reg2[1] &= 0xCF; /* R2 */
- reg2[1] |= 0x10;
- }
- }
-
- if (_fband == 3) {
- if ((first_ZF >= 1455) &&
- (first_ZF < 1630)) {
- reg1[1] &= 0xCF; /* N2 */
- reg1[1] |= 0x20;
- reg2[1] &= 0xCF; /* R2 */
- } else {
- if (first_ZF < 1455) {
- reg1[1] &= 0xCF; /* N2 */
- reg1[1] |= 0x20;
- reg2[1] &= 0xCF; /* R2 */
- reg2[1] |= 0x10;
- } else {
- if (first_ZF >= 1630) {
- reg1[1] &= 0xCF; /* N2 */
- reg2[1] &= 0xCF; /* R2 */
- reg2[1] |= 0x10;
- }
- }
- }
- }
-
- /* set ports, enable P0 for symbol rates > 4Ms/s */
- if (params->u.qpsk.symbol_rate >= 4000000)
- reg1[1] |= 0x0c;
- else
- reg1[1] |= 0x04;
-
- reg2[1] |= 0x0c;
-
- R = 64;
- A = 64;
- P = 64; //32
-
- M = (freq * R) / 4; /* in Mhz */
- N = (M - A * 1000) / (P * 1000);
-
- reg1[1] |= (N >> 9) & 0x03;
- reg1[2] = (N >> 1) & 0xff;
- reg1[3] = (N << 7) & 0x80;
-
- reg2[1] |= (R >> 8) & 0x03;
- reg2[2] = R & 0xFF; /* R */
-
- reg1[3] |= A & 0x7f; /* A */
-
- if (P == 64)
- reg1[1] |= 0x40; /* Prescaler 64/65 */
-
- reg0[1] |= 0x03;
-
- /* already enabled - do not reenable i2c repeater or TX fails */
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- msg.buf = reg0;
- msg.len = sizeof(reg0);
- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
- return -EIO;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- msg.buf = reg1;
- msg.len = sizeof(reg1);
- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
- return -EIO;
-
- if (fe->ops.i2c_gate_ctrl)
- fe->ops.i2c_gate_ctrl(fe, 1);
- msg.buf = reg2;
- msg.len = sizeof(reg2);
- if (i2c_transfer(&budget->i2c_adap, &msg, 1) != 1)
- return -EIO;
-
- return 0;
-}
-
static u8 typhoon_cinergy1200s_inittab[] = {
0x01, 0x15,
0x02, 0x30,
@@ -1068,9 +931,9 @@ static int tda10021_set_frontend(struct dvb_frontend *fe,
result = budget_av->tda10021_set_frontend(fe, p);
if (budget_av->tda10021_ts_enabled) {
- tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa1);
+ tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa1);
} else {
- tda10021_write_byte(budget_av->budget.dvb_frontend, 0x12, 0xa0);
+ tda10021_writereg(budget_av->budget.dvb_frontend, 0x12, 0xa0);
}
return result;
@@ -1096,15 +959,16 @@ static void frontend_init(struct budget_av *budget_av)
switch (saa->pci->subsystem_device) {
case SUBID_DVBS_KNC1:
+ case SUBID_DVBS_KNC1_PLUS:
case SUBID_DVBS_EASYWATCH_1:
if (saa->pci->subsystem_vendor == 0x1894) {
- fe = stv0299_attach(&cinergy_1200s_1894_0010_config,
+ fe = dvb_attach(stv0299_attach, &cinergy_1200s_1894_0010_config,
&budget_av->budget.i2c_adap);
if (fe) {
- fe->ops.tuner_ops.set_params = philips_su1278sh2_tua6100_tuner_set_params;
+ dvb_attach(tua6100_attach, fe, 0x60, &budget_av->budget.i2c_adap);
}
} else {
- fe = stv0299_attach(&typhoon_config,
+ fe = dvb_attach(stv0299_attach, &typhoon_config,
&budget_av->budget.i2c_adap);
if (fe) {
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
@@ -1116,16 +980,15 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBS_TV_STAR_CI:
case SUBID_DVBS_CYNERGY1200N:
case SUBID_DVBS_EASYWATCH:
- fe = stv0299_attach(&philips_sd1878_config,
+ fe = dvb_attach(stv0299_attach, &philips_sd1878_config,
&budget_av->budget.i2c_adap);
if (fe) {
fe->ops.tuner_ops.set_params = philips_sd1878_tda8261_tuner_set_params;
}
break;
- case SUBID_DVBS_KNC1_PLUS:
case SUBID_DVBS_TYPHOON:
- fe = stv0299_attach(&typhoon_config,
+ fe = dvb_attach(stv0299_attach, &typhoon_config,
&budget_av->budget.i2c_adap);
if (fe) {
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
@@ -1133,7 +996,7 @@ static void frontend_init(struct budget_av *budget_av)
break;
case SUBID_DVBS_CINERGY1200:
- fe = stv0299_attach(&cinergy_1200s_config,
+ fe = dvb_attach(stv0299_attach, &cinergy_1200s_config,
&budget_av->budget.i2c_adap);
if (fe) {
fe->ops.tuner_ops.set_params = philips_su1278_ty_ci_tuner_set_params;
@@ -1141,19 +1004,10 @@ static void frontend_init(struct budget_av *budget_av)
break;
case SUBID_DVBC_KNC1:
- budget_av->reinitialise_demod = 1;
- fe = tda10021_attach(&philips_cu1216_config,
- &budget_av->budget.i2c_adap,
- read_pwm(budget_av));
- if (fe) {
- fe->ops.tuner_ops.set_params = philips_cu1216_tuner_set_params;
- }
- break;
-
case SUBID_DVBC_KNC1_PLUS:
case SUBID_DVBC_CINERGY1200:
budget_av->reinitialise_demod = 1;
- fe = tda10021_attach(&philips_cu1216_config,
+ fe = dvb_attach(tda10021_attach, &philips_cu1216_config,
&budget_av->budget.i2c_adap,
read_pwm(budget_av));
if (fe) {
@@ -1168,7 +1022,7 @@ static void frontend_init(struct budget_av *budget_av)
case SUBID_DVBT_KNC1_PLUS:
case SUBID_DVBT_CINERGY1200:
budget_av->reinitialise_demod = 1;
- fe = tda10046_attach(&philips_tu1216_config,
+ fe = dvb_attach(tda10046_attach, &philips_tu1216_config,
&budget_av->budget.i2c_adap);
if (fe) {
fe->ops.tuner_ops.init = philips_tu1216_tuner_init;
@@ -1192,8 +1046,7 @@ static void frontend_init(struct budget_av *budget_av)
if (dvb_register_frontend(&budget_av->budget.dvb_adapter,
budget_av->budget.dvb_frontend)) {
printk(KERN_ERR "budget-av: Frontend registration failed!\n");
- if (budget_av->budget.dvb_frontend->ops.release)
- budget_av->budget.dvb_frontend->ops.release(budget_av->budget.dvb_frontend);
+ dvb_frontend_detach(budget_av->budget.dvb_frontend);
budget_av->budget.dvb_frontend = NULL;
}
}
@@ -1227,8 +1080,10 @@ static int budget_av_detach(struct saa7146_dev *dev)
if (budget_av->budget.ci_present)
ciintf_deinit(budget_av);
- if (budget_av->budget.dvb_frontend != NULL)
+ if (budget_av->budget.dvb_frontend != NULL) {
dvb_unregister_frontend(budget_av->budget.dvb_frontend);
+ dvb_frontend_detach(budget_av->budget.dvb_frontend);
+ }
err = ttpci_budget_deinit(&budget_av->budget);
kfree(budget_av);
@@ -1400,6 +1255,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(knc1s, 0x1131, 0x0010),
MAKE_EXTENSION_PCI(knc1s, 0x1894, 0x0010),
MAKE_EXTENSION_PCI(knc1sp, 0x1131, 0x0011),
+ MAKE_EXTENSION_PCI(knc1sp, 0x1894, 0x0011),
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0014),
MAKE_EXTENSION_PCI(kncxs, 0x1894, 0x0016),
MAKE_EXTENSION_PCI(satewpls, 0x1894, 0x001e),
diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c
index ffbbb3e34be..2a2e9b40061 100644
--- a/drivers/media/dvb/ttpci/budget-ci.c
+++ b/drivers/media/dvb/ttpci/budget-ci.c
@@ -749,17 +749,17 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend *fe, struct dvb
// setup PLL filter and TDA9889
switch (params->u.ofdm.bandwidth) {
case BANDWIDTH_6_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0x14);
+ tda1004x_writereg(fe, 0x0C, 0x14);
filter = 0;
break;
case BANDWIDTH_7_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0x80);
+ tda1004x_writereg(fe, 0x0C, 0x80);
filter = 0;
break;
case BANDWIDTH_8_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0x14);
+ tda1004x_writereg(fe, 0x0C, 0x14);
filter = 1;
break;
@@ -988,7 +988,7 @@ static void frontend_init(struct budget_ci *budget_ci)
switch (budget_ci->budget.dev->pci->subsystem_device) {
case 0x100c: // Hauppauge/TT Nova-CI budget (stv0299/ALPS BSRU6(tsa5059))
budget_ci->budget.dvb_frontend =
- stv0299_attach(&alps_bsru6_config, &budget_ci->budget.i2c_adap);
+ dvb_attach(stv0299_attach, &alps_bsru6_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
@@ -998,7 +998,7 @@ static void frontend_init(struct budget_ci *budget_ci)
case 0x100f: // Hauppauge/TT Nova-CI budget (stv0299b/Philips su1278(tsa5059))
budget_ci->budget.dvb_frontend =
- stv0299_attach(&philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
+ dvb_attach(stv0299_attach, &philips_su1278_tt_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_su1278_tt_tuner_set_params;
break;
@@ -1008,7 +1008,7 @@ static void frontend_init(struct budget_ci *budget_ci)
case 0x1010: // TT DVB-C CI budget (stv0297/Philips tdm1316l(tda6651tt))
budget_ci->tuner_pll_address = 0x61;
budget_ci->budget.dvb_frontend =
- stv0297_attach(&dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+ dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params;
break;
@@ -1018,7 +1018,7 @@ static void frontend_init(struct budget_ci *budget_ci)
case 0x1011: // Hauppauge/TT Nova-T budget (tda10045/Philips tdm1316l(tda6651tt) + TDA9889)
budget_ci->tuner_pll_address = 0x63;
budget_ci->budget.dvb_frontend =
- tda10045_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+ dvb_attach(tda10045_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
@@ -1029,7 +1029,7 @@ static void frontend_init(struct budget_ci *budget_ci)
case 0x1012: // TT DVB-T CI budget (tda10046/Philips tdm1316l(tda6651tt))
budget_ci->tuner_pll_address = 0x60;
budget_ci->budget.dvb_frontend =
- tda10046_attach(&philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
+ dvb_attach(tda10046_attach, &philips_tdm1316l_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
@@ -1038,16 +1038,15 @@ static void frontend_init(struct budget_ci *budget_ci)
break;
case 0x1017: // TT S-1500 PCI
- budget_ci->budget.dvb_frontend = stv0299_attach(&alps_bsbe1_config, &budget_ci->budget.i2c_adap);
+ budget_ci->budget.dvb_frontend = dvb_attach(stv0299_attach, &alps_bsbe1_config, &budget_ci->budget.i2c_adap);
if (budget_ci->budget.dvb_frontend) {
budget_ci->budget.dvb_frontend->ops.tuner_ops.set_params = alps_bsbe1_tuner_set_params;
budget_ci->budget.dvb_frontend->tuner_priv = &budget_ci->budget.i2c_adap;
budget_ci->budget.dvb_frontend->ops.dishnetwork_send_legacy_command = NULL;
- if (lnbp21_attach(budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0)) {
+ if (dvb_attach(lnbp21_attach, budget_ci->budget.dvb_frontend, &budget_ci->budget.i2c_adap, LNBP21_LLC, 0) == NULL) {
printk("%s: No LNBP21 found!\n", __FUNCTION__);
- if (budget_ci->budget.dvb_frontend->ops.release)
- budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
@@ -1065,8 +1064,7 @@ static void frontend_init(struct budget_ci *budget_ci)
if (dvb_register_frontend
(&budget_ci->budget.dvb_adapter, budget_ci->budget.dvb_frontend)) {
printk("budget-ci: Frontend registration failed!\n");
- if (budget_ci->budget.dvb_frontend->ops.release)
- budget_ci->budget.dvb_frontend->ops.release(budget_ci->budget.dvb_frontend);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
budget_ci->budget.dvb_frontend = NULL;
}
}
@@ -1114,8 +1112,10 @@ static int budget_ci_detach(struct saa7146_dev *dev)
if (budget_ci->budget.ci_present)
ciintf_deinit(budget_ci);
- if (budget_ci->budget.dvb_frontend)
+ if (budget_ci->budget.dvb_frontend) {
dvb_unregister_frontend(budget_ci->budget.dvb_frontend);
+ dvb_frontend_detach(budget_ci->budget.dvb_frontend);
+ }
err = ttpci_budget_deinit(&budget_ci->budget);
tasklet_kill(&budget_ci->msp430_irq_tasklet);
@@ -1153,7 +1153,7 @@ static struct pci_device_id pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
- .name = "budget_ci dvb\0",
+ .name = "budget_ci dvb",
.flags = SAA7146_I2C_SHORT_DELAY,
.module = THIS_MODULE,
diff --git a/drivers/media/dvb/ttpci/budget-patch.c b/drivers/media/dvb/ttpci/budget-patch.c
index 57227441891..fc1267b8c89 100644
--- a/drivers/media/dvb/ttpci/budget-patch.c
+++ b/drivers/media/dvb/ttpci/budget-patch.c
@@ -325,7 +325,7 @@ static void frontend_init(struct budget_patch* budget)
case 0x1013: // SATELCO Multimedia PCI
// try the ALPS BSRV2 first of all
- budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_patch_diseqc_send_master_cmd;
@@ -335,7 +335,7 @@ static void frontend_init(struct budget_patch* budget)
}
// try the ALPS BSRU6 now
- budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
@@ -347,7 +347,7 @@ static void frontend_init(struct budget_patch* budget)
}
// Try the grundig 29504-451
- budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
@@ -367,8 +367,7 @@ static void frontend_init(struct budget_patch* budget)
} else {
if (dvb_register_frontend(&budget->dvb_adapter, budget->dvb_frontend)) {
printk("budget-av: Frontend registration failed!\n");
- if (budget->dvb_frontend->ops.release)
- budget->dvb_frontend->ops.release(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
budget->dvb_frontend = NULL;
}
}
@@ -627,8 +626,10 @@ static int budget_patch_detach (struct saa7146_dev* dev)
struct budget_patch *budget = (struct budget_patch*) dev->ext_priv;
int err;
- if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend);
-
+ if (budget->dvb_frontend) {
+ dvb_unregister_frontend(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
+ }
err = ttpci_budget_deinit (budget);
kfree (budget);
@@ -647,7 +648,7 @@ static void __exit budget_patch_exit(void)
}
static struct saa7146_extension budget_extension = {
- .name = "budget_patch dvb\0",
+ .name = "budget_patch dvb",
.flags = 0,
.module = THIS_MODULE,
diff --git a/drivers/media/dvb/ttpci/budget.c b/drivers/media/dvb/ttpci/budget.c
index 863dffb4ed8..e58f0391e9d 100644
--- a/drivers/media/dvb/ttpci/budget.c
+++ b/drivers/media/dvb/ttpci/budget.c
@@ -41,6 +41,8 @@
#include "l64781.h"
#include "tda8083.h"
#include "s5h1420.h"
+#include "tda10086.h"
+#include "tda826x.h"
#include "lnbp21.h"
#include "bsru6.h"
@@ -342,6 +344,11 @@ static struct s5h1420_config s5h1420_config = {
.invert = 1,
};
+static struct tda10086_config tda10086_config = {
+ .demod_address = 0x0e,
+ .invert = 0,
+};
+
static u8 read_pwm(struct budget* budget)
{
u8 b = 0xff;
@@ -361,7 +368,7 @@ static void frontend_init(struct budget *budget)
case 0x1003: // Hauppauge/TT Nova budget (stv0299/ALPS BSRU6(tsa5059) OR ves1893/ALPS BSRV2(sp5659))
case 0x1013:
// try the ALPS BSRV2 first of all
- budget->dvb_frontend = ves1x93_attach(&alps_bsrv2_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(ves1x93_attach, &alps_bsrv2_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsrv2_tuner_set_params;
budget->dvb_frontend->ops.diseqc_send_master_cmd = budget_diseqc_send_master_cmd;
@@ -371,7 +378,7 @@ static void frontend_init(struct budget *budget)
}
// try the ALPS BSRU6 now
- budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
@@ -381,7 +388,7 @@ static void frontend_init(struct budget *budget)
case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
- budget->dvb_frontend = ves1820_attach(&alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
+ budget->dvb_frontend = dvb_attach(ves1820_attach, &alps_tdbe2_config, &budget->i2c_adap, read_pwm(budget));
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
break;
@@ -390,7 +397,7 @@ static void frontend_init(struct budget *budget)
case 0x1005: // Hauppauge/TT Nova-T budget (L64781/Grundig 29504-401(tsa5060))
- budget->dvb_frontend = l64781_attach(&grundig_29504_401_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(l64781_attach, &grundig_29504_401_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_401_tuner_set_params;
break;
@@ -398,7 +405,7 @@ static void frontend_init(struct budget *budget)
break;
case 0x4f60: // Fujitsu Siemens Activy Budget-S PCI rev AL (stv0299/ALPS BSRU6(tsa5059))
- budget->dvb_frontend = stv0299_attach(&alps_bsru6_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(stv0299_attach, &alps_bsru6_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = alps_bsru6_tuner_set_params;
budget->dvb_frontend->tuner_priv = &budget->i2c_adap;
@@ -408,7 +415,7 @@ static void frontend_init(struct budget *budget)
break;
case 0x4f61: // Fujitsu Siemens Activy Budget-S PCI rev GR (tda8083/Grundig 29504-451(tsa5522))
- budget->dvb_frontend = tda8083_attach(&grundig_29504_451_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(tda8083_attach, &grundig_29504_451_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = grundig_29504_451_tuner_set_params;
budget->dvb_frontend->ops.set_voltage = siemens_budget_set_voltage;
@@ -417,10 +424,28 @@ static void frontend_init(struct budget *budget)
break;
case 0x1016: // Hauppauge/TT Nova-S SE (samsung s5h1420/????(tda8260))
- budget->dvb_frontend = s5h1420_attach(&s5h1420_config, &budget->i2c_adap);
+ budget->dvb_frontend = dvb_attach(s5h1420_attach, &s5h1420_config, &budget->i2c_adap);
if (budget->dvb_frontend) {
budget->dvb_frontend->ops.tuner_ops.set_params = s5h1420_tuner_set_params;
- if (lnbp21_attach(budget->dvb_frontend, &budget->i2c_adap, 0, 0)) {
+ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
+ printk("%s: No LNBP21 found!\n", __FUNCTION__);
+ goto error_out;
+ }
+ break;
+ }
+
+ case 0x1018: // TT Budget-S-1401 (philips tda10086/philips tda8262)
+ // gpio2 is connected to CLB - reset it + leave it high
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTLO);
+ msleep(1);
+ saa7146_setgpio(budget->dev, 2, SAA7146_GPIO_OUTHI);
+ msleep(1);
+
+ budget->dvb_frontend = dvb_attach(tda10086_attach, &tda10086_config, &budget->i2c_adap);
+ if (budget->dvb_frontend) {
+ if (dvb_attach(tda826x_attach, budget->dvb_frontend, 0x60, &budget->i2c_adap, 0) == NULL)
+ printk("%s: No tda826x found!\n", __FUNCTION__);
+ if (dvb_attach(lnbp21_attach, budget->dvb_frontend, &budget->i2c_adap, 0, 0) == NULL) {
printk("%s: No LNBP21 found!\n", __FUNCTION__);
goto error_out;
}
@@ -442,8 +467,7 @@ static void frontend_init(struct budget *budget)
error_out:
printk("budget: Frontend registration failed!\n");
- if (budget->dvb_frontend->ops.release)
- budget->dvb_frontend->ops.release(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
budget->dvb_frontend = NULL;
return;
}
@@ -481,7 +505,10 @@ static int budget_detach (struct saa7146_dev* dev)
struct budget *budget = (struct budget*) dev->ext_priv;
int err;
- if (budget->dvb_frontend) dvb_unregister_frontend(budget->dvb_frontend);
+ if (budget->dvb_frontend) {
+ dvb_unregister_frontend(budget->dvb_frontend);
+ dvb_frontend_detach(budget->dvb_frontend);
+ }
err = ttpci_budget_deinit (budget);
@@ -497,6 +524,7 @@ MAKE_BUDGET_INFO(ttbs, "TT-Budget/WinTV-NOVA-S PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbc, "TT-Budget/WinTV-NOVA-C PCI", BUDGET_TT);
MAKE_BUDGET_INFO(ttbt, "TT-Budget/WinTV-NOVA-T PCI", BUDGET_TT);
MAKE_BUDGET_INFO(satel, "SATELCO Multimedia PCI", BUDGET_TT_HW_DISEQC);
+MAKE_BUDGET_INFO(ttbs1401, "TT-Budget-S-1401 PCI", BUDGET_TT);
MAKE_BUDGET_INFO(fsacs0, "Fujitsu Siemens Activy Budget-S PCI (rev GR/grundig frontend)", BUDGET_FS_ACTIVY);
MAKE_BUDGET_INFO(fsacs1, "Fujitsu Siemens Activy Budget-S PCI (rev AL/alps frontend)", BUDGET_FS_ACTIVY);
@@ -506,6 +534,7 @@ static struct pci_device_id pci_tbl[] = {
MAKE_EXTENSION_PCI(ttbt, 0x13c2, 0x1005),
MAKE_EXTENSION_PCI(satel, 0x13c2, 0x1013),
MAKE_EXTENSION_PCI(ttbs, 0x13c2, 0x1016),
+ MAKE_EXTENSION_PCI(ttbs1401, 0x13c2, 0x1018),
MAKE_EXTENSION_PCI(fsacs1,0x1131, 0x4f60),
MAKE_EXTENSION_PCI(fsacs0,0x1131, 0x4f61),
{
@@ -516,7 +545,7 @@ static struct pci_device_id pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, pci_tbl);
static struct saa7146_extension budget_extension = {
- .name = "budget dvb\0",
+ .name = "budget dvb",
.flags = SAA7146_I2C_SHORT_DELAY,
.module = THIS_MODULE,
diff --git a/drivers/media/dvb/ttusb-budget/Kconfig b/drivers/media/dvb/ttusb-budget/Kconfig
index 46a6a60d2ab..e78ea9227b0 100644
--- a/drivers/media/dvb/ttusb-budget/Kconfig
+++ b/drivers/media/dvb/ttusb-budget/Kconfig
@@ -2,13 +2,13 @@ config DVB_TTUSB_BUDGET
tristate "Technotrend/Hauppauge Nova-USB devices"
depends on DVB_CORE && USB && I2C
select DVB_PLL
- select DVB_CX22700
- select DVB_TDA1004X
- select DVB_VES1820
- select DVB_TDA8083
- select DVB_STV0299
- select DVB_STV0297
- select DVB_LNBP21
+ select DVB_CX22700 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_VES1820 if !DVB_FE_CUSTOMISE
+ select DVB_TDA8083 if !DVB_FE_CUSTOMISE
+ select DVB_STV0299 if !DVB_FE_CUSTOMISE
+ select DVB_STV0297 if !DVB_FE_CUSTOMISE
+ select DVB_LNBP21 if !DVB_FE_CUSTOMISE
help
Support for external USB adapters designed by Technotrend and
produced by Hauppauge, shipped under the brand name 'Nova-USB'.
diff --git a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
index 04cef302345..234199875f5 100644
--- a/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
+++ b/drivers/media/dvb/ttusb-budget/dvb-ttusb-budget.c
@@ -1107,17 +1107,17 @@ static int philips_tdm1316l_tuner_set_params(struct dvb_frontend* fe, struct dvb
// setup PLL filter
switch (params->u.ofdm.bandwidth) {
case BANDWIDTH_6_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0);
+ tda1004x_writereg(fe, 0x0C, 0);
filter = 0;
break;
case BANDWIDTH_7_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0);
+ tda1004x_writereg(fe, 0x0C, 0);
filter = 0;
break;
case BANDWIDTH_8_MHZ:
- tda1004x_write_byte(fe, 0x0C, 0xFF);
+ tda1004x_writereg(fe, 0x0C, 0xFF);
filter = 1;
break;
@@ -1564,13 +1564,13 @@ static void frontend_init(struct ttusb* ttusb)
switch(le16_to_cpu(ttusb->dev->descriptor.idProduct)) {
case 0x1003: // Hauppauge/TT Nova-USB-S budget (stv0299/ALPS BSRU6|BSBE1(tsa5059))
// try the stv0299 based first
- ttusb->fe = stv0299_attach(&alps_stv0299_config, &ttusb->i2c_adap);
+ ttusb->fe = dvb_attach(stv0299_attach, &alps_stv0299_config, &ttusb->i2c_adap);
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.set_params = philips_tsa5059_tuner_set_params;
if(ttusb->revision == TTUSB_REV_2_2) { // ALPS BSBE1
alps_stv0299_config.inittab = alps_bsbe1_inittab;
- lnbp21_attach(ttusb->fe, &ttusb->i2c_adap, 0, 0);
+ dvb_attach(lnbp21_attach, ttusb->fe, &ttusb->i2c_adap, 0, 0);
} else { // ALPS BSRU6
ttusb->fe->ops.set_voltage = ttusb_set_voltage;
}
@@ -1578,7 +1578,7 @@ static void frontend_init(struct ttusb* ttusb)
}
// Grundig 29504-491
- ttusb->fe = tda8083_attach(&ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap);
+ ttusb->fe = dvb_attach(tda8083_attach, &ttusb_novas_grundig_29504_491_config, &ttusb->i2c_adap);
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.set_params = ttusb_novas_grundig_29504_491_tuner_set_params;
ttusb->fe->ops.set_voltage = ttusb_set_voltage;
@@ -1587,13 +1587,13 @@ static void frontend_init(struct ttusb* ttusb)
break;
case 0x1004: // Hauppauge/TT DVB-C budget (ves1820/ALPS TDBE2(sp5659))
- ttusb->fe = ves1820_attach(&alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb));
+ ttusb->fe = dvb_attach(ves1820_attach, &alps_tdbe2_config, &ttusb->i2c_adap, read_pwm(ttusb));
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.set_params = alps_tdbe2_tuner_set_params;
break;
}
- ttusb->fe = stv0297_attach(&dvbc_philips_tdm1316l_config, &ttusb->i2c_adap);
+ ttusb->fe = dvb_attach(stv0297_attach, &dvbc_philips_tdm1316l_config, &ttusb->i2c_adap);
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.set_params = dvbc_philips_tdm1316l_tuner_set_params;
break;
@@ -1602,14 +1602,14 @@ static void frontend_init(struct ttusb* ttusb)
case 0x1005: // Hauppauge/TT Nova-USB-t budget (tda10046/Philips td1316(tda6651tt) OR cx22700/ALPS TDMB7(??))
// try the ALPS TDMB7 first
- ttusb->fe = cx22700_attach(&alps_tdmb7_config, &ttusb->i2c_adap);
+ ttusb->fe = dvb_attach(cx22700_attach, &alps_tdmb7_config, &ttusb->i2c_adap);
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.set_params = alps_tdmb7_tuner_set_params;
break;
}
// Philips td1316
- ttusb->fe = tda10046_attach(&philips_tdm1316l_config, &ttusb->i2c_adap);
+ ttusb->fe = dvb_attach(tda10046_attach, &philips_tdm1316l_config, &ttusb->i2c_adap);
if (ttusb->fe != NULL) {
ttusb->fe->ops.tuner_ops.init = philips_tdm1316l_tuner_init;
ttusb->fe->ops.tuner_ops.set_params = philips_tdm1316l_tuner_set_params;
@@ -1625,8 +1625,7 @@ static void frontend_init(struct ttusb* ttusb)
} else {
if (dvb_register_frontend(&ttusb->adapter, ttusb->fe)) {
printk("dvb-ttusb-budget: Frontend registration failed!\n");
- if (ttusb->fe->ops.release)
- ttusb->fe->ops.release(ttusb->fe);
+ dvb_frontend_detach(ttusb->fe);
ttusb->fe = NULL;
}
}
@@ -1763,7 +1762,10 @@ static void ttusb_disconnect(struct usb_interface *intf)
dvb_net_release(&ttusb->dvbnet);
dvb_dmxdev_release(&ttusb->dmxdev);
dvb_dmx_release(&ttusb->dvb_demux);
- if (ttusb->fe != NULL) dvb_unregister_frontend(ttusb->fe);
+ if (ttusb->fe != NULL) {
+ dvb_unregister_frontend(ttusb->fe);
+ dvb_frontend_detach(ttusb->fe);
+ }
i2c_del_adapter(&ttusb->i2c_adap);
dvb_unregister_adapter(&ttusb->adapter);
diff --git a/drivers/media/dvb/ttusb-dec/ttusb_dec.c b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
index 6c1cb770bcf..de077a75719 100644
--- a/drivers/media/dvb/ttusb-dec/ttusb_dec.c
+++ b/drivers/media/dvb/ttusb-dec/ttusb_dec.c
@@ -215,7 +215,7 @@ static void ttusb_dec_handle_irq( struct urb *urb, struct pt_regs *regs)
case -ECONNRESET:
case -ENOENT:
case -ESHUTDOWN:
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is dead, cleanup */
dprintk("%s:urb shutting down with status: %d\n",
__FUNCTION__, urb->status);
@@ -1512,7 +1512,11 @@ static void ttusb_dec_exit_dvb(struct ttusb_dec *dec)
dec->demux.dmx.remove_frontend(&dec->demux.dmx, &dec->frontend);
dvb_dmxdev_release(&dec->dmxdev);
dvb_dmx_release(&dec->demux);
- if (dec->fe) dvb_unregister_frontend(dec->fe);
+ if (dec->fe) {
+ dvb_unregister_frontend(dec->fe);
+ if (dec->fe->ops.release)
+ dec->fe->ops.release(dec->fe);
+ }
dvb_unregister_adapter(&dec->adapter);
}
diff --git a/drivers/media/radio/Kconfig b/drivers/media/radio/Kconfig
index 220076b1b95..7015517e2c1 100644
--- a/drivers/media/radio/Kconfig
+++ b/drivers/media/radio/Kconfig
@@ -7,7 +7,7 @@ menu "Radio Adapters"
config RADIO_CADET
tristate "ADS Cadet AM/FM Tuner"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these AM/FM radio cards, and then
fill in the port address below.
@@ -25,7 +25,7 @@ config RADIO_CADET
config RADIO_RTRACK
tristate "AIMSlab RadioTrack (aka RadioReveal) support"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
@@ -59,7 +59,7 @@ config RADIO_RTRACK_PORT
config RADIO_RTRACK2
tristate "AIMSlab RadioTrack II support"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have this FM radio card, and then fill in the
port address below.
@@ -82,7 +82,7 @@ config RADIO_RTRACK2_PORT
config RADIO_AZTECH
tristate "Aztech/Packard Bell Radio"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
@@ -106,7 +106,7 @@ config RADIO_AZTECH_PORT
config RADIO_GEMTEK
tristate "GemTek Radio Card support"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have this FM radio card, and then fill in the
port address below.
@@ -131,7 +131,7 @@ config RADIO_GEMTEK_PORT
config RADIO_GEMTEK_PCI
tristate "GemTek PCI Radio Card support"
- depends on VIDEO_V4L1 && PCI
+ depends on VIDEO_V4L2 && PCI
---help---
Choose Y here if you have this PCI FM radio card.
@@ -145,7 +145,7 @@ config RADIO_GEMTEK_PCI
config RADIO_MAXIRADIO
tristate "Guillemot MAXI Radio FM 2000 radio"
- depends on VIDEO_V4L1 && PCI
+ depends on VIDEO_V4L2 && PCI
---help---
Choose Y here if you have this radio card. This card may also be
found as Gemtek PCI FM.
@@ -160,7 +160,7 @@ config RADIO_MAXIRADIO
config RADIO_MAESTRO
tristate "Maestro on board radio"
- depends on VIDEO_V4L1
+ depends on VIDEO_V4L2 && PCI
---help---
Say Y here to directly support the on-board radio tuner on the
Maestro 2 or 2E sound card.
@@ -208,7 +208,7 @@ config RADIO_MIROPCM20_RDS
config RADIO_SF16FMI
tristate "SF16FMI Radio"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards. If you
compile the driver into the kernel and your card is not PnP one, you
@@ -225,7 +225,7 @@ config RADIO_SF16FMI
config RADIO_SF16FMR2
tristate "SF16FMR2 Radio"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards.
@@ -239,7 +239,7 @@ config RADIO_SF16FMR2
config RADIO_TERRATEC
tristate "TerraTec ActiveRadio ISA Standalone"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have this FM radio card, and then fill in the
port address below. (TODO)
@@ -268,7 +268,7 @@ config RADIO_TERRATEC_PORT
config RADIO_TRUST
tristate "Trust FM radio card"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
help
This is a driver for the Trust FM radio cards. Say Y if you have
such a card and want to use it under Linux.
@@ -286,7 +286,7 @@ config RADIO_TRUST_PORT
config RADIO_TYPHOON
tristate "Typhoon Radio (a.k.a. EcoRadio)"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address and the frequency used for muting below.
@@ -330,7 +330,7 @@ config RADIO_TYPHOON_MUTEFREQ
config RADIO_ZOLTRIX
tristate "Zoltrix Radio"
- depends on ISA && VIDEO_V4L1
+ depends on ISA && VIDEO_V4L2
---help---
Choose Y here if you have one of these FM radio cards, and then fill
in the port address below.
@@ -352,7 +352,7 @@ config RADIO_ZOLTRIX_PORT
config USB_DSBR
tristate "D-Link USB FM radio support (EXPERIMENTAL)"
- depends on USB && VIDEO_V4L1 && EXPERIMENTAL
+ depends on USB && VIDEO_V4L2 && EXPERIMENTAL
---help---
Say Y here if you want to connect this type of radio to your
computer's USB port. Note that the audio is not digital, and
diff --git a/drivers/media/radio/dsbr100.c b/drivers/media/radio/dsbr100.c
index f7e33f9ee8e..db865a0667e 100644
--- a/drivers/media/radio/dsbr100.c
+++ b/drivers/media/radio/dsbr100.c
@@ -33,8 +33,14 @@
History:
+ Version 0.41-ac1:
+ Alan Cox: Some cleanups and fixes
+
+ Version 0.41:
+ Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+
Version 0.40:
- Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
+ Markus: Updates for 2.6.x kernels, code layout changes, name sanitizing
Version 0.30:
Markus: Updates for 2.5.x kernel and more ISO compliant source
@@ -65,13 +71,12 @@
*/
-
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/input.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/usb.h>
#include <linux/smp_lock.h>
@@ -79,7 +84,22 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.40"
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+
+#define DRIVER_VERSION "v0.41"
+#define RADIO_VERSION KERNEL_VERSION(0,4,1)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
+
#define DRIVER_AUTHOR "Markus Demleitner <msdemlei@tucana.harvard.edu>"
#define DRIVER_DESC "D-Link DSB-R100 USB FM radio driver"
@@ -111,7 +131,7 @@ static int radio_nr = -1;
module_param(radio_nr, int, 0);
/* Data for one (physical) device */
-typedef struct {
+struct dsbr100_device {
struct usb_device *usbdev;
struct video_device *videodev;
unsigned char transfer_buffer[TB_LEN];
@@ -119,7 +139,8 @@ typedef struct {
int stereo;
int users;
int removed;
-} dsbr100_device;
+ int muted;
+};
/* File system interface */
@@ -138,7 +159,6 @@ static struct video_device dsbr100_videodev_template=
.owner = THIS_MODULE,
.name = "D-Link DSB-R 100",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_AZTECH,
.fops = &usb_dsbr100_fops,
.release = video_device_release,
};
@@ -161,7 +181,7 @@ static struct usb_driver usb_dsbr100_driver = {
/* Low-level device interface begins here */
/* switch on radio */
-static int dsbr100_start(dsbr100_device *radio)
+static int dsbr100_start(struct dsbr100_device *radio)
{
if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
@@ -172,12 +192,13 @@ static int dsbr100_start(dsbr100_device *radio)
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x01, 0x00, radio->transfer_buffer, 8, 300)<0)
return -1;
+ radio->muted=0;
return (radio->transfer_buffer)[0];
}
/* switch off radio */
-static int dsbr100_stop(dsbr100_device *radio)
+static int dsbr100_stop(struct dsbr100_device *radio)
{
if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
@@ -188,11 +209,12 @@ static int dsbr100_stop(dsbr100_device *radio)
USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
0x00, 0x00, radio->transfer_buffer, 8, 300)<0)
return -1;
+ radio->muted=1;
return (radio->transfer_buffer)[0];
}
/* set a frequency, freq is defined by v4l's TUNER_LOW, i.e. 1/16th kHz */
-static int dsbr100_setfreq(dsbr100_device *radio, int freq)
+static int dsbr100_setfreq(struct dsbr100_device *radio, int freq)
{
freq = (freq/16*80)/1000+856;
if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
@@ -217,7 +239,7 @@ static int dsbr100_setfreq(dsbr100_device *radio, int freq)
/* return the device status. This is, in effect, just whether it
sees a stereo signal or not. Pity. */
-static void dsbr100_getstat(dsbr100_device *radio)
+static void dsbr100_getstat(struct dsbr100_device *radio)
{
if (usb_control_msg(radio->usbdev, usb_rcvctrlpipe(radio->usbdev, 0),
USB_REQ_GET_STATUS,
@@ -236,9 +258,9 @@ usb if it is */
static int usb_dsbr100_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
- dsbr100_device *radio;
+ struct dsbr100_device *radio;
- if (!(radio = kmalloc(sizeof(dsbr100_device), GFP_KERNEL)))
+ if (!(radio = kmalloc(sizeof(struct dsbr100_device), GFP_KERNEL)))
return -ENOMEM;
if (!(radio->videodev = video_device_alloc())) {
kfree(radio);
@@ -271,7 +293,7 @@ code I'd expect I better did that, but if there's a memory
leak here it's tiny (~50 bytes per disconnect) */
static void usb_dsbr100_disconnect(struct usb_interface *intf)
{
- dsbr100_device *radio = usb_get_intfdata(intf);
+ struct dsbr100_device *radio = usb_get_intfdata(intf);
usb_set_intfdata (intf, NULL);
if (radio) {
@@ -291,89 +313,121 @@ static void usb_dsbr100_disconnect(struct usb_interface *intf)
static int usb_dsbr100_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
- dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
if (!radio)
return -EIO;
switch(cmd) {
- case VIDIOCGCAP: {
- struct video_capability *v = arg;
-
- memset(v, 0, sizeof(*v));
- v->type = VID_TYPE_TUNER;
- v->channels = 1;
- v->audios = 1;
- strcpy(v->name, "D-Link R-100 USB FM Radio");
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ strlcpy(v->driver, "dsbr100", sizeof (v->driver));
+ strlcpy(v->card, "D-Link R-100 USB FM Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER: {
- struct video_tuner *v = arg;
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
- dsbr100_getstat(radio);
- if(v->tuner) /* Only 1 tuner */
+ if (v->index > 0)
return -EINVAL;
+
+ dsbr100_getstat(radio);
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
v->rangelow = FREQ_MIN*FREQ_MUL;
v->rangehigh = FREQ_MAX*FREQ_MUL;
- v->flags = VIDEO_TUNER_LOW;
- v->mode = VIDEO_MODE_AUTO;
- v->signal = radio->stereo*0x7000;
- /* Don't know how to get signal strength */
- v->flags |= VIDEO_TUNER_STEREO_ON*radio->stereo;
- strcpy(v->name, "DSB R-100");
- return 0;
- }
- case VIDIOCSTUNER: {
- struct video_tuner *v = arg;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ if(radio->stereo)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal = 0xFFFF; /* We can't get the signal strength */
- if(v->tuner!=0)
- return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
return 0;
}
- case VIDIOCGFREQ: {
- int *freq = arg;
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
- if (radio->curfreq==-1)
+ if (v->index > 0)
return -EINVAL;
- *freq = radio->curfreq;
+
return 0;
}
- case VIDIOCSFREQ: {
- int *freq = arg;
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
- radio->curfreq = *freq;
+ radio->curfreq = f->frequency;
if (dsbr100_setfreq(radio, radio->curfreq)==-1)
warn("Set frequency failed");
return 0;
}
- case VIDIOCGAUDIO: {
- struct video_audio *v = arg;
-
- memset(v, 0, sizeof(*v));
- v->flags |= VIDEO_AUDIO_MUTABLE;
- v->mode = VIDEO_SOUND_STEREO;
- v->volume = 1;
- v->step = 1;
- strcpy(v->name, "Radio");
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = radio->curfreq;
+
return 0;
}
- case VIDIOCSAUDIO: {
- struct video_audio *v = arg;
-
- if (v->audio)
- return -EINVAL;
- if (v->flags&VIDEO_AUDIO_MUTE) {
- if (dsbr100_stop(radio)==-1)
- warn("Radio did not respond properly");
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return 0;
+ }
}
- else
- if (dsbr100_start(radio)==-1)
- warn("Radio did not respond properly");
- return 0;
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=radio->muted;
+ return 0;
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ if (dsbr100_stop(radio)==-1)
+ warn("Radio did not respond properly");
+ } else {
+ if (dsbr100_start(radio)==-1)
+ warn("Radio did not respond properly");
+ }
+ return 0;
+ }
+ return -EINVAL;
}
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ usb_dsbr100_do_ioctl);
}
}
@@ -385,9 +439,11 @@ static int usb_dsbr100_ioctl(struct inode *inode, struct file *file,
static int usb_dsbr100_open(struct inode *inode, struct file *file)
{
- dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
radio->users = 1;
+ radio->muted = 1;
+
if (dsbr100_start(radio)<0) {
warn("Radio did not start up properly");
radio->users = 0;
@@ -399,7 +455,7 @@ static int usb_dsbr100_open(struct inode *inode, struct file *file)
static int usb_dsbr100_close(struct inode *inode, struct file *file)
{
- dsbr100_device *radio=video_get_drvdata(video_devdata(file));
+ struct dsbr100_device *radio=video_get_drvdata(video_devdata(file));
if (!radio)
return -ENODEV;
diff --git a/drivers/media/radio/radio-aimslab.c b/drivers/media/radio/radio-aimslab.c
index df22a582e7a..3368a89bfad 100644
--- a/drivers/media/radio/radio-aimslab.c
+++ b/drivers/media/radio/radio-aimslab.c
@@ -1,5 +1,6 @@
/* radiotrack (radioreveal) driver for Linux radio support
* (c) 1997 M. Kirkwood
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
* Converted to new API by Alan Cox <Alan.Cox@linux.org>
* Various bugfixes and enhancements by Russell Kroll <rkroll@exploits.org>
*
@@ -33,11 +34,13 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_RTRACK_PORT */
#include <asm/semaphore.h> /* Lock for the I/O */
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
#ifndef CONFIG_RADIO_RTRACK_PORT
#define CONFIG_RADIO_RTRACK_PORT -1
#endif
@@ -209,6 +212,25 @@ static int rt_getsigstr(struct rt_device *dev)
return 1; /* signal present */
}
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
static int rt_do_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, void *arg)
{
@@ -217,73 +239,114 @@ static int rt_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "RadioTrack");
+ strlcpy(v->driver, "radio-aimslab", sizeof (v->driver));
+ strlcpy(v->card, "RadioTrack", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
v->rangelow=(87*16000);
v->rangehigh=(108*16000);
- v->flags=VIDEO_TUNER_LOW;
- v->mode=VIDEO_MODE_AUTO;
- strcpy(v->name, "FM");
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
v->signal=0xFFFF*rt_getsigstr(rt);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
+
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = rt->curfreq;
+ struct v4l2_frequency *f = arg;
+
+ rt->curfreq = f->frequency;
+ rt_setfreq(rt, rt->curfreq);
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- rt->curfreq = *freq;
- rt_setfreq(rt, rt->curfreq);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
- v->volume=rt->curvol * 6554;
- v->step=6554;
- strcpy(v->name, "Radio");
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- if(v->flags&VIDEO_AUDIO_MUTE)
- rt_mute(rt);
- else
- rt_setvol(rt,v->volume/6554);
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=rt->muted;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=rt->curvol * 6554;
+ return (0);
+ }
+ return -EINVAL;
}
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ rt_mute(rt);
+ } else {
+ rt_setvol(rt,rt->curvol);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ rt_setvol(rt,ctrl->value);
+ return (0);
+ }
+ return -EINVAL;
+ }
+
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ rt_do_ioctl);
}
}
@@ -309,7 +372,7 @@ static struct video_device rtrack_radio=
.owner = THIS_MODULE,
.name = "RadioTrack radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_RTRACK,
+ .hardware = 0,
.fops = &rtrack_fops,
};
diff --git a/drivers/media/radio/radio-aztech.c b/drivers/media/radio/radio-aztech.c
index 95e6322133e..3ba5fa8cf7e 100644
--- a/drivers/media/radio/radio-aztech.c
+++ b/drivers/media/radio/radio-aztech.c
@@ -1,5 +1,6 @@
/* radio-aztech.c - Aztech radio card driver for Linux 2.2
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
* Adapted to support the Video for Linux API by
* Russell Kroll <rkroll@exploits.org>. Based on original tuner code by:
*
@@ -30,9 +31,30 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_AZTECH_PORT */
+
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -166,81 +188,121 @@ static int az_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "Aztech Radio");
+ strlcpy(v->driver, "radio-aztech", sizeof (v->driver));
+ strlcpy(v->card, "Aztech Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
v->rangelow=(87*16000);
v->rangehigh=(108*16000);
- v->flags=VIDEO_TUNER_LOW;
- v->mode=VIDEO_MODE_AUTO;
- v->signal=0xFFFF*az_getsigstr(az);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
if(az_getstereo(az))
- v->flags|=VIDEO_TUNER_STEREO_ON;
- strcpy(v->name, "FM");
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xFFFF*az_getsigstr(az);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = az->curfreq;
+ struct v4l2_frequency *f = arg;
+
+ az->curfreq = f->frequency;
+ az_setfreq(az, az->curfreq);
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- az->curfreq = *freq;
- az_setfreq(az, az->curfreq);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = az->curfreq;
+
return 0;
}
- case VIDIOCGAUDIO:
+
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
- if(az->stereo)
- v->mode=VIDEO_SOUND_STEREO;
- else
- v->mode=VIDEO_SOUND_MONO;
- v->volume=az->curvol;
- v->step=16384;
- strcpy(v->name, "Radio");
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- az->curvol=v->volume;
-
- az->stereo=(v->mode&VIDEO_SOUND_STEREO)?1:0;
- if(v->flags&VIDEO_AUDIO_MUTE)
- az_setvol(az,0);
- else
- az_setvol(az,az->curvol);
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (az->curvol==0)
+ ctrl->value=1;
+ else
+ ctrl->value=0;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=az->curvol * 6554;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ az_setvol(az,0);
+ } else {
+ az_setvol(az,az->curvol);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ az_setvol(az,ctrl->value);
+ return (0);
+ }
+ return -EINVAL;
}
+
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ az_do_ioctl);
}
}
@@ -266,7 +328,7 @@ static struct video_device aztech_radio=
.owner = THIS_MODULE,
.name = "Aztech radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_AZTECH,
+ .hardware = 0,
.fops = &aztech_fops,
};
diff --git a/drivers/media/radio/radio-cadet.c b/drivers/media/radio/radio-cadet.c
index 8641aec7baf..69d4b7919c5 100644
--- a/drivers/media/radio/radio-cadet.c
+++ b/drivers/media/radio/radio-cadet.c
@@ -25,20 +25,28 @@
*
* 2003-01-31 Alan Cox <alan@redhat.com>
* Cleaned up locking, delay code, general odds and ends
+ *
+ * 2006-07-30 Hans J. Koch <koch@hjk-az.de>
+ * Changed API to V4L2
*/
+#include <linux/version.h>
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
#include <linux/ioport.h> /* request_region */
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* V4L2 API defs */
#include <media/v4l2-common.h>
#include <linux/param.h>
#include <linux/pnp.h>
#define RDS_BUFFER 256
+#define RDS_RX_FLAG 1
+#define MBS_RX_FLAG 2
+
+#define CADET_VERSION KERNEL_VERSION(0,3,3)
static int io=-1; /* default to isapnp activation */
static int radio_nr = -1;
@@ -61,44 +69,24 @@ static int cadet_probe(void);
*/
static __u16 sigtable[2][4]={{5,10,30,150},{28,40,63,1000}};
-static int cadet_getrds(void)
-{
- int rdsstat=0;
-
- spin_lock(&cadet_io_lock);
- outb(3,io); /* Select Decoder Control/Status */
- outb(inb(io+1)&0x7f,io+1); /* Reset RDS detection */
- spin_unlock(&cadet_io_lock);
-
- msleep(100);
-
- spin_lock(&cadet_io_lock);
- outb(3,io); /* Select Decoder Control/Status */
- if((inb(io+1)&0x80)!=0) {
- rdsstat|=VIDEO_TUNER_RDS_ON;
- }
- if((inb(io+1)&0x10)!=0) {
- rdsstat|=VIDEO_TUNER_MBS_ON;
- }
- spin_unlock(&cadet_io_lock);
- return rdsstat;
-}
-static int cadet_getstereo(void)
+static int
+cadet_getstereo(void)
{
- int ret = 0;
+ int ret = V4L2_TUNER_SUB_MONO;
if(curtuner != 0) /* Only FM has stereo capability! */
- return 0;
+ return V4L2_TUNER_SUB_MONO;
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */
if( (inb(io+1) & 0x40) == 0)
- ret = 1;
+ ret = V4L2_TUNER_SUB_STEREO;
spin_unlock(&cadet_io_lock);
return ret;
}
-static unsigned cadet_gettune(void)
+static unsigned
+cadet_gettune(void)
{
int curvol,i;
unsigned fifo=0;
@@ -135,7 +123,8 @@ static unsigned cadet_gettune(void)
return fifo;
}
-static unsigned cadet_getfreq(void)
+static unsigned
+cadet_getfreq(void)
{
int i;
unsigned freq=0,test,fifo=0;
@@ -167,7 +156,8 @@ static unsigned cadet_getfreq(void)
return freq;
}
-static void cadet_settune(unsigned fifo)
+static void
+cadet_settune(unsigned fifo)
{
int i;
unsigned test;
@@ -195,7 +185,8 @@ static void cadet_settune(unsigned fifo)
spin_unlock(&cadet_io_lock);
}
-static void cadet_setfreq(unsigned freq)
+static void
+cadet_setfreq(unsigned freq)
{
unsigned fifo;
int i,j,test;
@@ -255,7 +246,8 @@ static void cadet_setfreq(unsigned freq)
}
-static int cadet_getvol(void)
+static int
+cadet_getvol(void)
{
int ret = 0;
@@ -270,7 +262,8 @@ static int cadet_getvol(void)
}
-static void cadet_setvol(int vol)
+static void
+cadet_setvol(int vol)
{
spin_lock(&cadet_io_lock);
outb(7,io); /* Select tuner control */
@@ -281,7 +274,8 @@ static void cadet_setvol(int vol)
spin_unlock(&cadet_io_lock);
}
-static void cadet_handler(unsigned long data)
+static void
+cadet_handler(unsigned long data)
{
/*
* Service the RDS fifo
@@ -322,8 +316,8 @@ static void cadet_handler(unsigned long data)
-static ssize_t cadet_read(struct file *file, char __user *data,
- size_t count, loff_t *ppos)
+static ssize_t
+cadet_read(struct file *file, char __user *data, size_t count, loff_t *ppos)
{
int i=0;
unsigned char readbuf[RDS_BUFFER];
@@ -359,128 +353,156 @@ static int cadet_do_ioctl(struct inode *inode, struct file *file,
{
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
- memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=2;
- v->audios=1;
- strcpy(v->name, "ADS Cadet");
+ struct v4l2_capability *cap = arg;
+ memset(cap,0,sizeof(*cap));
+ cap->capabilities =
+ V4L2_CAP_TUNER |
+ V4L2_CAP_READWRITE;
+ cap->version = CADET_VERSION;
+ strcpy(cap->driver, "ADS Cadet");
+ strcpy(cap->card, "ADS Cadet");
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if((v->tuner<0)||(v->tuner>1)) {
- return -EINVAL;
- }
- switch(v->tuner) {
- case 0:
- strcpy(v->name,"FM");
- v->rangelow=1400; /* 87.5 MHz */
- v->rangehigh=1728; /* 108.0 MHz */
- v->flags=0;
- v->mode=0;
- v->mode|=VIDEO_MODE_AUTO;
- v->signal=sigstrength;
- if(cadet_getstereo()==1) {
- v->flags|=VIDEO_TUNER_STEREO_ON;
- }
- v->flags|=cadet_getrds();
- break;
- case 1:
- strcpy(v->name,"AM");
- v->rangelow=8320; /* 520 kHz */
- v->rangehigh=26400; /* 1650 kHz */
- v->flags=0;
- v->flags|=VIDEO_TUNER_LOW;
- v->mode=0;
- v->mode|=VIDEO_MODE_AUTO;
- v->signal=sigstrength;
- break;
+ struct v4l2_tuner *t = arg;
+ memset(t,0,sizeof(*t));
+ t->type = V4L2_TUNER_RADIO;
+ switch (t->index)
+ {
+ case 0: strcpy(t->name, "FM");
+ t->capability = V4L2_TUNER_CAP_STEREO;
+ t->rangelow = 1400; /* 87.5 MHz */
+ t->rangehigh = 1728; /* 108.0 MHz */
+ t->rxsubchans=cadet_getstereo();
+ switch (t->rxsubchans){
+ case V4L2_TUNER_SUB_MONO:
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ case V4L2_TUNER_SUB_STEREO:
+ t->audmode = V4L2_TUNER_MODE_STEREO;
+ break;
+ default: ;
+ }
+ break;
+ case 1: strcpy(t->name, "AM");
+ t->capability = V4L2_TUNER_CAP_LOW;
+ t->rangelow = 8320; /* 520 kHz */
+ t->rangehigh = 26400; /* 1650 kHz */
+ t->rxsubchans = V4L2_TUNER_SUB_MONO;
+ t->audmode = V4L2_TUNER_MODE_MONO;
+ break;
+ default:
+ return -EINVAL;
}
+
+ t->signal = sigstrength; /* We might need to modify scaling of this */
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if((v->tuner<0)||(v->tuner>1)) {
+ struct v4l2_tuner *t = arg;
+ if((t->index != 0)&&(t->index != 1))
return -EINVAL;
- }
- curtuner=v->tuner;
+
+ curtuner = t->index;
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = cadet_getfreq();
+ struct v4l2_frequency *f = arg;
+ memset(f,0,sizeof(*f));
+ f->tuner = curtuner;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = cadet_getfreq();
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- if((curtuner==0)&&((*freq<1400)||(*freq>1728))) {
+ struct v4l2_frequency *f = arg;
+ if (f->type != V4L2_TUNER_RADIO){
+ return -EINVAL;
+ }
+ if((curtuner==0)&&((f->frequency<1400)||(f->frequency>1728))) {
return -EINVAL;
}
- if((curtuner==1)&&((*freq<8320)||(*freq>26400))) {
+ if((curtuner==1)&&((f->frequency<8320)||(f->frequency>26400))) {
return -EINVAL;
}
- cadet_setfreq(*freq);
+ cadet_setfreq(f->frequency);
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
- if(cadet_getstereo()==0) {
- v->mode=VIDEO_SOUND_MONO;
- } else {
- v->mode=VIDEO_SOUND_STEREO;
+ struct v4l2_control *c = arg;
+ switch (c->id){
+ case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
+ c->value = (cadet_getvol() == 0);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ c->value = cadet_getvol();
+ break;
+ default:
+ return -EINVAL;
}
- v->volume=cadet_getvol();
- v->step=0xffff;
- strcpy(v->name, "Radio");
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_S_CTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- cadet_setvol(v->volume);
- if(v->flags&VIDEO_AUDIO_MUTE)
- cadet_setvol(0);
- else
- cadet_setvol(0xffff);
+ struct v4l2_control *c = arg;
+ switch (c->id){
+ case V4L2_CID_AUDIO_MUTE: /* TODO: Handle this correctly */
+ if (c->value) cadet_setvol(0);
+ else cadet_setvol(0xffff);
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ cadet_setvol(c->value);
+ break;
+ default:
+ return -EINVAL;
+ }
return 0;
}
+
default:
return -ENOIOCTLCMD;
}
}
-static int cadet_ioctl(struct inode *inode, struct file *file,
+static int
+cadet_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
return video_usercopy(inode, file, cmd, arg, cadet_do_ioctl);
}
-static int cadet_open(struct inode *inode, struct file *file)
+static int
+cadet_open(struct inode *inode, struct file *file)
{
- if(users)
- return -EBUSY;
users++;
- init_waitqueue_head(&read_queue);
+ if (1 == users) init_waitqueue_head(&read_queue);
return 0;
}
-static int cadet_release(struct inode *inode, struct file *file)
+static int
+cadet_release(struct inode *inode, struct file *file)
{
- del_timer_sync(&readtimer);
- rdsstat=0;
users--;
+ if (0 == users){
+ del_timer_sync(&readtimer);
+ rdsstat=0;
+ }
+ return 0;
+}
+
+static unsigned int
+cadet_poll(struct file *file, struct poll_table_struct *wait)
+{
+ poll_wait(file,&read_queue,wait);
+ if(rdsin != rdsout)
+ return POLLIN | POLLRDNORM;
return 0;
}
@@ -491,6 +513,7 @@ static struct file_operations cadet_fops = {
.release = cadet_release,
.read = cadet_read,
.ioctl = cadet_ioctl,
+ .poll = cadet_poll,
.compat_ioctl = v4l_compat_ioctl32,
.llseek = no_llseek,
};
@@ -500,7 +523,6 @@ static struct video_device cadet_radio=
.owner = THIS_MODULE,
.name = "Cadet radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_CADET,
.fops = &cadet_fops,
};
diff --git a/drivers/media/radio/radio-gemtek-pci.c b/drivers/media/radio/radio-gemtek-pci.c
index 4c82956390c..cfab57d6bc4 100644
--- a/drivers/media/radio/radio-gemtek-pci.c
+++ b/drivers/media/radio/radio-gemtek-pci.c
@@ -34,6 +34,8 @@
*
* TODO: multiple device support and portability were not tested
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
***************************************************************************
*/
@@ -42,10 +44,32 @@
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
#include <linux/errno.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#include <asm/io.h>
#include <asm/uaccess.h>
@@ -183,91 +207,117 @@ static int gemtek_pci_do_ioctl(struct inode *inode, struct file *file,
struct gemtek_pci_card *card = dev->priv;
switch ( cmd ) {
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *c = arg;
+ struct v4l2_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ strlcpy(v->driver, "radio-gemtek-pci", sizeof (v->driver));
+ strlcpy(v->card, "GemTek PCI Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
- memset(c,0,sizeof(*c));
- c->type = VID_TYPE_TUNER;
- c->channels = 1;
- c->audios = 1;
- strcpy( c->name, "Gemtek PCI Radio" );
return 0;
}
-
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *t = arg;
+ struct v4l2_tuner *v = arg;
- if ( t->tuner )
+ if (v->index > 0)
return -EINVAL;
- t->rangelow = GEMTEK_PCI_RANGE_LOW;
- t->rangehigh = GEMTEK_PCI_RANGE_HIGH;
- t->flags = VIDEO_TUNER_LOW;
- t->mode = VIDEO_MODE_AUTO;
- t->signal = 0xFFFF * gemtek_pci_getsignal( card );
- strcpy( t->name, "FM" );
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow = GEMTEK_PCI_RANGE_LOW;
+ v->rangehigh = GEMTEK_PCI_RANGE_HIGH;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xFFFF*gemtek_pci_getsignal( card );
+
return 0;
}
-
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *t = arg;
- if ( t->tuner )
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- return 0;
- }
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = card->current_frequency;
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
+ struct v4l2_frequency *f = arg;
- if ( (*freq < GEMTEK_PCI_RANGE_LOW) ||
- (*freq > GEMTEK_PCI_RANGE_HIGH) )
+ if ( (f->frequency < GEMTEK_PCI_RANGE_LOW) ||
+ (f->frequency > GEMTEK_PCI_RANGE_HIGH) )
return -EINVAL;
- gemtek_pci_setfrequency( card, *freq );
- card->current_frequency = *freq;
- card->mute = FALSE;
+ gemtek_pci_setfrequency( card, f->frequency );
+ card->current_frequency = f->frequency;
+ card->mute = FALSE;
return 0;
}
-
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *a = arg;
-
- memset( a, 0, sizeof( *a ) );
- a->flags |= VIDEO_AUDIO_MUTABLE;
- a->volume = 1;
- a->step = 65535;
- strcpy( a->name, "Radio" );
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
-
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *a = arg;
-
- if ( a->audio )
- return -EINVAL;
-
- if ( a->flags & VIDEO_AUDIO_MUTE )
- gemtek_pci_mute( card );
- else
- gemtek_pci_unmute( card );
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=card->mute;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (card->mute)
+ ctrl->value=0;
+ else
+ ctrl->value=65535;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ gemtek_pci_mute(card);
+ } else {
+ gemtek_pci_unmute(card);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (ctrl->value) {
+ gemtek_pci_unmute(card);
+ } else {
+ gemtek_pci_mute(card);
+ }
+ return (0);
+ }
+ return -EINVAL;
}
-
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ gemtek_pci_do_ioctl);
}
}
@@ -309,7 +359,7 @@ static struct video_device vdev_template = {
.owner = THIS_MODULE,
.name = "Gemtek PCI Radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_GEMTEK,
+ .hardware = 0,
.fops = &gemtek_pci_fops,
};
diff --git a/drivers/media/radio/radio-gemtek.c b/drivers/media/radio/radio-gemtek.c
index 162f37d8bf9..730fe16126c 100644
--- a/drivers/media/radio/radio-gemtek.c
+++ b/drivers/media/radio/radio-gemtek.c
@@ -13,6 +13,7 @@
*
* TODO: Allow for more than one of these foolish entities :-)
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -21,11 +22,32 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_GEMTEK_PORT */
#include <linux/spinlock.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#ifndef CONFIG_RADIO_GEMTEK_PORT
#define CONFIG_RADIO_GEMTEK_PORT -1
#endif
@@ -147,77 +169,122 @@ static int gemtek_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "GemTek");
+ strlcpy(v->driver, "radio-gemtek", sizeof (v->driver));
+ strlcpy(v->card, "GemTek", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- v->rangelow=87*16000;
- v->rangehigh=108*16000;
- v->flags=VIDEO_TUNER_LOW;
- v->mode=VIDEO_MODE_AUTO;
- v->signal=0xFFFF*gemtek_getsigstr(rt);
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow=(87*16000);
+ v->rangehigh=(108*16000);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xFFFF*gemtek_getsigstr(rt);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
- return 0;
- }
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = rt->curfreq;
+
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- rt->curfreq = *freq;
+ struct v4l2_frequency *f = arg;
+
+ rt->curfreq = f->frequency;
/* needs to be called twice in order for getsigstr to work */
gemtek_setfreq(rt, rt->curfreq);
gemtek_setfreq(rt, rt->curfreq);
return 0;
}
- case VIDIOCGAUDIO:
- {
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags|=VIDEO_AUDIO_MUTABLE;
- v->volume=1;
- v->step=65535;
- strcpy(v->name, "Radio");
- return 0;
- }
- case VIDIOCSAUDIO:
+ case VIDIOC_G_FREQUENCY:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
+ struct v4l2_frequency *f = arg;
- if(v->flags&VIDEO_AUDIO_MUTE)
- gemtek_mute(rt);
- else
- gemtek_unmute(rt);
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
return 0;
}
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=rt->muted;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (rt->muted)
+ ctrl->value=0;
+ else
+ ctrl->value=65535;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ gemtek_mute(rt);
+ } else {
+ gemtek_unmute(rt);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (ctrl->value) {
+ gemtek_unmute(rt);
+ } else {
+ gemtek_mute(rt);
+ }
+ return (0);
+ }
+ return -EINVAL;
+ }
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ gemtek_do_ioctl);
}
}
@@ -243,7 +310,7 @@ static struct video_device gemtek_radio=
.owner = THIS_MODULE,
.name = "GemTek radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_GEMTEK,
+ .hardware = 0,
.fops = &gemtek_fops,
};
diff --git a/drivers/media/radio/radio-maestro.c b/drivers/media/radio/radio-maestro.c
index fcfa6c9fe22..e8ce5f75cf1 100644
--- a/drivers/media/radio/radio-maestro.c
+++ b/drivers/media/radio/radio-maestro.c
@@ -14,6 +14,8 @@
* version 0.04
* + code improvements
* + VIDEO_TUNER_LOW is permanent
+ *
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h>
@@ -25,10 +27,23 @@
#include <asm/uaccess.h>
#include <linux/mutex.h>
#include <linux/pci.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
-#define DRIVER_VERSION "0.05"
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,6)
+#define DRIVER_VERSION "0.06"
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
#define GPIO_DATA 0x60 /* port offset from ESS_IO_BASE */
@@ -96,7 +111,7 @@ static struct file_operations maestro_fops = {
static struct video_device maestro_radio = {
.name = "Maestro radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_SF16MI,
+ .hardware = 0,
.fops = &maestro_fops,
};
@@ -130,7 +145,7 @@ static u32 radio_bits_get(struct radio_device *dev)
rdata = inw(io);
if(!l)
dev->stereo = rdata & STR_MOST ?
- 0 : VIDEO_TUNER_STEREO_ON;
+ 0 : 1;
else
if(rdata & STR_DATA)
data++;
@@ -183,72 +198,120 @@ static inline int radio_function(struct inode *inode, struct file *file,
struct radio_device *card = video_get_drvdata(dev);
switch (cmd) {
- case VIDIOCGCAP: {
- struct video_capability *v = arg;
- memset(v, 0, sizeof(*v));
- strcpy(v->name, "Maestro radio");
- v->type = VID_TYPE_TUNER;
- v->channels = v->audios = 1;
- return 0;
- } case VIDIOCGTUNER: {
- struct video_tuner *v = arg;
- if (v->tuner)
- return -EINVAL;
- (void)radio_bits_get(card);
- v->flags = VIDEO_TUNER_LOW | card->stereo;
- v->signal = card->tuned;
- strcpy(v->name, "FM");
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->mode = VIDEO_MODE_AUTO;
- return 0;
- } case VIDIOCSTUNER: {
- struct video_tuner *v = arg;
- if (v->tuner != 0)
- return -EINVAL;
- return 0;
- } case VIDIOCGFREQ: {
- unsigned long *freq = arg;
- *freq = BITS2FREQ(radio_bits_get(card));
- return 0;
- } case VIDIOCSFREQ: {
- unsigned long *freq = arg;
- if (*freq < FREQ_LO || *freq > FREQ_HI)
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *v = arg;
+ memset(v,0,sizeof(*v));
+ strlcpy(v->driver, "radio-maestro", sizeof (v->driver));
+ strlcpy(v->card, "Maestro Radio", sizeof (v->card));
+ sprintf(v->bus_info,"PCI");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
+ return 0;
+ }
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ (void)radio_bits_get(card);
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow = FREQ_LO;
+ v->rangehigh = FREQ_HI;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ if(card->stereo)
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=card->tuned;
+
+ return 0;
+ }
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
+ return -EINVAL;
+
+ return 0;
+ }
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
+ return -EINVAL;
+ radio_bits_set(card, FREQ2BITS(f->frequency));
+
+ return 0;
+ }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = BITS2FREQ(radio_bits_get(card));
+
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
return -EINVAL;
- radio_bits_set(card, FREQ2BITS(*freq));
- return 0;
- } case VIDIOCGAUDIO: {
- struct video_audio *v = arg;
- memset(v, 0, sizeof(*v));
- strcpy(v->name, "Radio");
- v->flags = VIDEO_AUDIO_MUTABLE | card->muted;
- v->mode = VIDEO_SOUND_STEREO;
- return 0;
- } case VIDIOCSAUDIO: {
- struct video_audio *v = arg;
- if (v->audio)
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=card->muted;
+ return (0);
+ }
return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
{
- register u16 io = card->io;
- register u16 omask = inw(io + IO_MASK);
- outw(~STR_WREN, io + IO_MASK);
- outw((card->muted = v->flags & VIDEO_AUDIO_MUTE) ?
- STR_WREN : 0, io);
- udelay(4);
- outw(omask, io + IO_MASK);
- msleep(125);
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ {
+ register u16 io = card->io;
+ register u16 omask = inw(io + IO_MASK);
+ outw(~STR_WREN, io + IO_MASK);
+ outw((card->muted = ctrl->value ) ?
+ STR_WREN : 0, io);
+ udelay(4);
+ outw(omask, io + IO_MASK);
+ msleep(125);
+
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- } case VIDIOCGUNIT: {
- struct video_unit *v = arg;
- v->video = VIDEO_NO_UNIT;
- v->vbi = VIDEO_NO_UNIT;
- v->radio = dev->minor;
- v->audio = 0;
- v->teletext = VIDEO_NO_UNIT;
- return 0;
- } default:
- return -ENOIOCTLCMD;
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ radio_function);
}
}
@@ -275,7 +338,7 @@ static u16 __devinit radio_power_on(struct radio_device *dev)
omask = inw(io + IO_MASK);
odir = (inw(io + IO_DIR) & ~STR_DATA) | (STR_CLK | STR_WREN);
outw(odir & ~STR_WREN, io + IO_DIR);
- dev->muted = inw(io) & STR_WREN ? 0 : VIDEO_AUDIO_MUTE;
+ dev->muted = inw(io) & STR_WREN ? 0 : 1;
outw(odir, io + IO_DIR);
outw(~(STR_WREN | STR_CLK), io + IO_MASK);
outw(dev->muted ? 0 : STR_WREN, io);
diff --git a/drivers/media/radio/radio-maxiradio.c b/drivers/media/radio/radio-maxiradio.c
index f93d7afe730..c2eeae7a10d 100644
--- a/drivers/media/radio/radio-maxiradio.c
+++ b/drivers/media/radio/radio-maxiradio.c
@@ -20,13 +20,14 @@
* 0.75b
* - better pci interface thanks to Francois Romieu <romieu@cogenit.fr>
*
- * 0.75
+ * 0.75 Sun Feb 4 22:51:27 EET 2001
* - tiding up
* - removed support for multiple devices as it didn't work anyway
*
* BUGS:
* - card unmutes if you change frequency
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
@@ -40,11 +41,24 @@
#include <linux/mutex.h>
#include <linux/pci.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
-/* version 0.75 Sun Feb 4 22:51:27 EET 2001 */
-#define DRIVER_VERSION "0.75"
+#define DRIVER_VERSION "0.76"
+
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,7,6)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
#ifndef PCI_VENDOR_ID_GUILLEMOT
#define PCI_VENDOR_ID_GUILLEMOT 0x5046
@@ -90,7 +104,6 @@ static struct video_device maxiradio_radio =
.owner = THIS_MODULE,
.name = "Maxi Radio FM2000 radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_SF16MI,
.fops = &maxiradio_fops,
};
@@ -176,89 +189,116 @@ static inline int radio_function(struct inode *inode, struct file *file,
struct radio_device *card=dev->priv;
switch(cmd) {
- case VIDIOCGCAP: {
- struct video_capability *v = arg;
-
+ case VIDIOC_QUERYCAP:
+ {
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- strcpy(v->name, "Maxi Radio FM2000 radio");
- v->type=VID_TYPE_TUNER;
- v->channels=v->audios=1;
+ strlcpy(v->driver, "radio-maxiradio", sizeof (v->driver));
+ strlcpy(v->card, "Maxi Radio FM2000 radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER: {
- struct video_tuner *v = arg;
+ case VIDIOC_G_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
- if(v->tuner)
+ if (v->index > 0)
return -EINVAL;
- card->stereo = 0xffff * get_stereo(card->io);
- card->tuned = 0xffff * get_tune(card->io);
-
- v->flags = VIDEO_TUNER_LOW | card->stereo;
- v->signal = card->tuned;
-
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
-
- v->rangelow = FREQ_LO;
- v->rangehigh = FREQ_HI;
- v->mode = VIDEO_MODE_AUTO;
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow=FREQ_LO;
+ v->rangehigh=FREQ_HI;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ if(get_stereo(card->io))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xffff*get_tune(card->io);
return 0;
}
- case VIDIOCSTUNER: {
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ case VIDIOC_S_TUNER:
+ {
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- return 0;
- }
- case VIDIOCGFREQ: {
- unsigned long *freq = arg;
- *freq = card->freq;
return 0;
}
- case VIDIOCSFREQ: {
- unsigned long *freq = arg;
+ case VIDIOC_S_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
- if (*freq < FREQ_LO || *freq > FREQ_HI)
+ if (f->frequency < FREQ_LO || f->frequency > FREQ_HI)
return -EINVAL;
- card->freq = *freq;
+
+ card->freq = f->frequency;
set_freq(card->io, FREQ2BITS(card->freq));
msleep(125);
return 0;
}
- case VIDIOCGAUDIO: {
- struct video_audio *v = arg;
- memset(v,0,sizeof(*v));
- strcpy(v->name, "Radio");
- v->flags=VIDEO_AUDIO_MUTABLE | card->muted;
- v->mode=VIDEO_SOUND_STEREO;
- return 0;
- }
+ case VIDIOC_G_FREQUENCY:
+ {
+ struct v4l2_frequency *f = arg;
- case VIDIOCSAUDIO: {
- struct video_audio *v = arg;
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = card->freq;
- if(v->audio)
- return -EINVAL;
- card->muted = v->flags & VIDEO_AUDIO_MUTE;
- if(card->muted)
- turn_power(card->io, 0);
- else
- set_freq(card->io, FREQ2BITS(card->freq));
return 0;
}
- case VIDIOCGUNIT: {
- struct video_unit *v = arg;
-
- v->video=VIDEO_NO_UNIT;
- v->vbi=VIDEO_NO_UNIT;
- v->radio=dev->minor;
- v->audio=0;
- v->teletext=VIDEO_NO_UNIT;
- return 0;
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=card->muted;
+ return (0);
+ }
+ return -EINVAL;
}
- default: return -ENOIOCTLCMD;
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ card->muted = ctrl->value;
+ if(card->muted)
+ turn_power(card->io, 0);
+ else
+ set_freq(card->io, FREQ2BITS(card->freq));
+ return 0;
+ }
+ return -EINVAL;
+ }
+
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ radio_function);
+
}
}
diff --git a/drivers/media/radio/radio-rtrack2.c b/drivers/media/radio/radio-rtrack2.c
index 5b68ac4c732..b9e98483e58 100644
--- a/drivers/media/radio/radio-rtrack2.c
+++ b/drivers/media/radio/radio-rtrack2.c
@@ -6,6 +6,7 @@
*
* TODO: Allow for more than one of these foolish entities :-)
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -14,11 +15,32 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_RTRACK2_PORT */
#include <linux/spinlock.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 65535,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#ifndef CONFIG_RADIO_RTRACK2_PORT
#define CONFIG_RADIO_RTRACK2_PORT -1
#endif
@@ -115,75 +137,120 @@ static int rt_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "RadioTrack II");
+ strlcpy(v->driver, "radio-rtrack2", sizeof (v->driver));
+ strlcpy(v->card, "RadioTrack II", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- v->rangelow=88*16000;
- v->rangehigh=108*16000;
- v->flags=VIDEO_TUNER_LOW;
- v->mode=VIDEO_MODE_AUTO;
- v->signal=0xFFFF*rt_getsigstr(rt);
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow=(88*16000);
+ v->rangehigh=(108*16000);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xFFFF*rt_getsigstr(rt);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
+
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = rt->curfreq;
+ struct v4l2_frequency *f = arg;
+
+ rt->curfreq = f->frequency;
+ rt_setfreq(rt, rt->curfreq);
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- rt->curfreq = *freq;
- rt_setfreq(rt, rt->curfreq);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = rt->curfreq;
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags|=VIDEO_AUDIO_MUTABLE;
- v->volume=1;
- v->step=65535;
- strcpy(v->name, "Radio");
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
-
- if(v->flags&VIDEO_AUDIO_MUTE)
- rt_mute(rt);
- else
- rt_unmute(rt);
-
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=rt->muted;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (rt->muted)
+ ctrl->value=0;
+ else
+ ctrl->value=65535;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ rt_mute(rt);
+ } else {
+ rt_unmute(rt);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ if (ctrl->value) {
+ rt_unmute(rt);
+ } else {
+ rt_mute(rt);
+ }
+ return (0);
+ }
+ return -EINVAL;
}
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ rt_do_ioctl);
}
}
@@ -209,7 +276,7 @@ static struct video_device rtrack2_radio=
.owner = THIS_MODULE,
.name = "RadioTrack II radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_RTRACK2,
+ .hardware = 0,
.fops = &rtrack2_fops,
};
diff --git a/drivers/media/radio/radio-sf16fmi.c b/drivers/media/radio/radio-sf16fmi.c
index efee6e339d1..ecc854b4ba3 100644
--- a/drivers/media/radio/radio-sf16fmi.c
+++ b/drivers/media/radio/radio-sf16fmi.c
@@ -13,20 +13,35 @@
* No volume control - only mute/unmute - you have to use line volume
* control on SB-part of SF16FMI
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
+#include <linux/version.h>
#include <linux/kernel.h> /* __setup */
#include <linux/module.h> /* Modules */
#include <linux/init.h> /* Initdata */
#include <linux/ioport.h> /* request_region */
#include <linux/delay.h> /* udelay */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
#include <linux/isapnp.h>
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
#include <linux/mutex.h>
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ }
+};
+
struct fmi_device
{
int port;
@@ -123,93 +138,122 @@ static int fmi_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- strcpy(v->name, "SF16-FMx radio");
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
+ strlcpy(v->driver, "radio-sf16fmi", sizeof (v->driver));
+ strlcpy(v->card, "SF16-FMx radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
+ struct v4l2_tuner *v = arg;
int mult;
- if(v->tuner) /* Only 1 tuner */
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
- mult = (fmi->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+ v->type = V4L2_TUNER_RADIO;
+
+ mult = (fmi->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
v->rangelow = RSF16_MINFREQ/mult;
v->rangehigh = RSF16_MAXFREQ/mult;
- v->flags=fmi->flags;
- v->mode=VIDEO_MODE_AUTO;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+ v->capability=fmi->flags&V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_STEREO;
v->signal = fmi_getsigstr(fmi);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- fmi->flags = v->flags & VIDEO_TUNER_LOW;
- /* Only 1 tuner so no setting needed ! */
- return 0;
- }
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = fmi->curfreq;
- if (!(fmi->flags & VIDEO_TUNER_LOW))
- *freq /= 1000;
+
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- if (!(fmi->flags & VIDEO_TUNER_LOW))
- *freq *= 1000;
- if (*freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+ struct v4l2_frequency *f = arg;
+
+ if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency *= 1000;
+ if (f->frequency < RSF16_MINFREQ ||
+ f->frequency > RSF16_MAXFREQ )
return -EINVAL;
/*rounding in steps of 800 to match th freq
that will be used */
- fmi->curfreq = (*freq/800)*800;
+ fmi->curfreq = (f->frequency/800)*800;
fmi_setfreq(fmi);
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_G_FREQUENCY:
{
- struct video_audio *v = arg;
- memset(v,0,sizeof(*v));
- v->flags=( (!fmi->curvol)*VIDEO_AUDIO_MUTE | VIDEO_AUDIO_MUTABLE);
- strcpy(v->name, "Radio");
- v->mode=VIDEO_SOUND_STEREO;
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmi->curfreq;
+ if (!(fmi->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency /= 1000;
+
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- fmi->curvol= v->flags&VIDEO_AUDIO_MUTE ? 0 : 1;
- fmi->curvol ?
- fmi_unmute(fmi->port) : fmi_mute(fmi->port);
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCGUNIT:
+ case VIDIOC_G_CTRL:
{
- struct video_unit *v = arg;
- v->video=VIDEO_NO_UNIT;
- v->vbi=VIDEO_NO_UNIT;
- v->radio=dev->minor;
- v->audio=0; /* How do we find out this??? */
- v->teletext=VIDEO_NO_UNIT;
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=fmi->curvol;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ {
+ if (ctrl->value)
+ fmi_mute(fmi->port);
+ else
+ fmi_unmute(fmi->port);
+
+ fmi->curvol=ctrl->value;
+ return (0);
+ }
+ }
+ return -EINVAL;
}
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ fmi_do_ioctl);
}
}
@@ -235,7 +279,7 @@ static struct video_device fmi_radio=
.owner = THIS_MODULE,
.name = "SF16FMx radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_SF16MI,
+ .hardware = 0,
.fops = &fmi_fops,
};
@@ -294,7 +338,7 @@ static int __init fmi_init(void)
fmi_unit.port = io;
fmi_unit.curvol = 0;
fmi_unit.curfreq = 0;
- fmi_unit.flags = VIDEO_TUNER_LOW;
+ fmi_unit.flags = V4L2_TUNER_CAP_LOW;
fmi_radio.priv = &fmi_unit;
mutex_init(&lock);
diff --git a/drivers/media/radio/radio-sf16fmr2.c b/drivers/media/radio/radio-sf16fmr2.c
index 3483b2c7bc9..4444dce864a 100644
--- a/drivers/media/radio/radio-sf16fmr2.c
+++ b/drivers/media/radio/radio-sf16fmr2.c
@@ -10,6 +10,8 @@
* For read stereo/mono you must wait 0.1 sec after set frequency and
* card unmuted so I set frequency on unmute
* Signal handling seem to work only on autoscanning (not implemented)
+ *
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -18,12 +20,34 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
#include <linux/mutex.h>
static struct mutex lock;
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 1<<12,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#undef DEBUG
//#define DEBUG 1
@@ -214,63 +238,65 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- strcpy(v->name, "SF16-FMR2 radio");
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
+ strlcpy(v->driver, "radio-sf16fmr2", sizeof (v->driver));
+ strlcpy(v->card, "SF16-FMR2 radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
+ struct v4l2_tuner *v = arg;
int mult;
- if(v->tuner) /* Only 1 tuner */
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
- mult = (fmr2->flags & VIDEO_TUNER_LOW) ? 1 : 1000;
+ v->type = V4L2_TUNER_RADIO;
+
+ mult = (fmr2->flags & V4L2_TUNER_CAP_LOW) ? 1 : 1000;
v->rangelow = RSF16_MINFREQ/mult;
v->rangehigh = RSF16_MAXFREQ/mult;
- v->flags = fmr2->flags | VIDEO_AUDIO_MUTABLE;
- if (fmr2->mute)
- v->flags |= VIDEO_AUDIO_MUTE;
- v->mode=VIDEO_MODE_AUTO;
+ v->rxsubchans =V4L2_TUNER_SUB_MONO | V4L2_TUNER_MODE_STEREO;
+ v->capability=fmr2->flags&V4L2_TUNER_CAP_LOW;
+
+ v->audmode = fmr2->stereo ? V4L2_TUNER_MODE_STEREO:
+ V4L2_TUNER_MODE_MONO;
mutex_lock(&lock);
v->signal = fmr2_getsigstr(fmr2);
mutex_unlock(&lock);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if (v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- fmr2->flags = v->flags & VIDEO_TUNER_LOW;
- return 0;
- }
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = fmr2->curfreq;
- if (!(fmr2->flags & VIDEO_TUNER_LOW))
- *freq /= 1000;
+
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- if (!(fmr2->flags & VIDEO_TUNER_LOW))
- *freq *= 1000;
- if ( *freq < RSF16_MINFREQ || *freq > RSF16_MAXFREQ )
+ struct v4l2_frequency *f = arg;
+
+ if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency *= 1000;
+ if (f->frequency < RSF16_MINFREQ ||
+ f->frequency > RSF16_MAXFREQ )
return -EINVAL;
- /* rounding in steps of 200 to match th freq
- * that will be used
- */
- fmr2->curfreq = (*freq/200)*200;
+ /*rounding in steps of 200 to match th freq
+ that will be used */
+ fmr2->curfreq = (f->frequency/200)*200;
/* set card freq (if not muted) */
if (fmr2->curvol && !fmr2->mute)
@@ -279,40 +305,81 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file,
fmr2_setfreq(fmr2);
mutex_unlock(&lock);
}
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_G_FREQUENCY:
{
- struct video_audio *v = arg;
- memset(v,0,sizeof(*v));
- /* !!! do not return VIDEO_AUDIO_MUTE */
- v->flags = VIDEO_AUDIO_MUTABLE;
- strcpy(v->name, "Radio");
- /* get current stereo mode */
- v->mode = fmr2->stereo ? VIDEO_SOUND_STEREO: VIDEO_SOUND_MONO;
- /* volume supported ? */
- if (fmr2->card_type == 11)
- {
- v->flags |= VIDEO_AUDIO_VOLUME;
- v->step = 1 << 12;
- v->volume = fmr2->curvol;
- }
- debug_print((KERN_DEBUG "Get flags %d vol %d\n", v->flags, v->volume));
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = fmr2->curfreq;
+ if (!(fmr2->flags & V4L2_TUNER_CAP_LOW))
+ f->frequency /= 1000;
+
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- debug_print((KERN_DEBUG "Set flags %d vol %d\n", v->flags, v->volume));
- /* set volume */
- if (v->flags & VIDEO_AUDIO_VOLUME)
- fmr2->curvol = v->volume; /* !!! set with precision */
- if (fmr2->card_type != 11) fmr2->curvol = 65535;
- fmr2->mute = 0;
- if (v->flags & VIDEO_AUDIO_MUTE)
- fmr2->mute = 1;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if ((fmr2->card_type != 11)
+ && V4L2_CID_AUDIO_VOLUME)
+ radio_qctrl[i].step=65535;
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=fmr2->mute;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=fmr2->curvol;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ fmr2->mute=ctrl->value;
+ if (fmr2->card_type != 11) {
+ if (!fmr2->mute) {
+ fmr2->curvol = 65535;
+ } else {
+ fmr2->curvol = 0;
+ }
+ }
+ break;
+ case V4L2_CID_AUDIO_VOLUME:
+ fmr2->curvol = ctrl->value;
+ if (fmr2->card_type != 11) {
+ if (fmr2->curvol) {
+ fmr2->curvol = 65535;
+ fmr2->mute = 0;
+ } else {
+ fmr2->curvol = 0;
+ fmr2->mute = 1;
+ }
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
#ifdef DEBUG
if (fmr2->curvol && !fmr2->mute)
printk(KERN_DEBUG "unmute\n");
@@ -320,27 +387,18 @@ static int fmr2_do_ioctl(struct inode *inode, struct file *file,
printk(KERN_DEBUG "mute\n");
#endif
mutex_lock(&lock);
- if (fmr2->curvol && !fmr2->mute)
- {
+ if (fmr2->curvol && !fmr2->mute) {
fmr2_setvolume(fmr2);
fmr2_setfreq(fmr2);
- }
- else fmr2_mute(fmr2->port);
+ } else
+ fmr2_mute(fmr2->port);
mutex_unlock(&lock);
- return 0;
- }
- case VIDIOCGUNIT:
- {
- struct video_unit *v = arg;
- v->video=VIDEO_NO_UNIT;
- v->vbi=VIDEO_NO_UNIT;
- v->radio=dev->minor;
- v->audio=0; /* How do we find out this??? */
- v->teletext=VIDEO_NO_UNIT;
- return 0;
+ return (0);
}
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ fmr2_do_ioctl);
+
}
}
@@ -366,7 +424,7 @@ static struct video_device fmr2_radio=
.owner = THIS_MODULE,
.name = "SF16FMR2 radio",
. type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_SF16FMR2,
+ .hardware = 0,
.fops = &fmr2_fops,
};
@@ -377,7 +435,7 @@ static int __init fmr2_init(void)
fmr2_unit.mute = 0;
fmr2_unit.curfreq = 0;
fmr2_unit.stereo = 1;
- fmr2_unit.flags = VIDEO_TUNER_LOW;
+ fmr2_unit.flags = V4L2_TUNER_CAP_LOW;
fmr2_unit.card_type = 0;
fmr2_radio.priv = &fmr2_unit;
@@ -396,7 +454,6 @@ static int __init fmr2_init(void)
}
printk(KERN_INFO "SF16FMR2 radio card driver at 0x%x.\n", io);
- debug_print((KERN_DEBUG "Mute %d Low %d\n",VIDEO_AUDIO_MUTE,VIDEO_TUNER_LOW));
/* mute card - prevents noisy bootups */
mutex_lock(&lock);
fmr2_mute(io);
diff --git a/drivers/media/radio/radio-terratec.c b/drivers/media/radio/radio-terratec.c
index dfba4ae596c..f539491a0d7 100644
--- a/drivers/media/radio/radio-terratec.c
+++ b/drivers/media/radio/radio-terratec.c
@@ -21,6 +21,7 @@
* If you can help me out with that, please contact me!!
*
*
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -29,11 +30,32 @@
#include <linux/delay.h> /* udelay */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_TERRATEC_PORT */
#include <linux/spinlock.h>
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 0xff,
+ .step = 1,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#ifndef CONFIG_RADIO_TERRATEC_PORT
#define CONFIG_RADIO_TERRATEC_PORT 0x590
#endif
@@ -194,73 +216,117 @@ static int tt_do_ioctl(struct inode *inode, struct file *file,
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "ActiveRadio");
+ strlcpy(v->driver, "radio-terratec", sizeof (v->driver));
+ strlcpy(v->card, "ActiveRadio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
v->rangelow=(87*16000);
v->rangehigh=(108*16000);
- v->flags=VIDEO_TUNER_LOW;
- v->mode=VIDEO_MODE_AUTO;
- strcpy(v->name, "FM");
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
v->signal=0xFFFF*tt_getsigstr(tt);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner!=0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
+
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = tt->curfreq;
+ struct v4l2_frequency *f = arg;
+
+ tt->curfreq = f->frequency;
+ tt_setfreq(tt, tt->curfreq);
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- tt->curfreq = *freq;
- tt_setfreq(tt, tt->curfreq);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = tt->curfreq;
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
- memset(v,0, sizeof(*v));
- v->flags|=VIDEO_AUDIO_MUTABLE|VIDEO_AUDIO_VOLUME;
- v->volume=tt->curvol * 6554;
- v->step=6554;
- strcpy(v->name, "Radio");
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
- if(v->audio)
- return -EINVAL;
- if(v->flags&VIDEO_AUDIO_MUTE)
- tt_mute(tt);
- else
- tt_setvol(tt,v->volume/6554);
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (tt->muted)
+ ctrl->value=1;
+ else
+ ctrl->value=0;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=tt->curvol * 6554;
+ return (0);
+ }
+ return -EINVAL;
}
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ tt_mute(tt);
+ } else {
+ tt_setvol(tt,tt->curvol);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ tt_setvol(tt,ctrl->value);
+ return (0);
+ }
+ return -EINVAL;
+ }
+
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ tt_do_ioctl);
}
}
@@ -286,7 +352,7 @@ static struct video_device terratec_radio=
.owner = THIS_MODULE,
.name = "TerraTec ActiveRadio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_TERRATEC,
+ .hardware = 0,
.fops = &terratec_fops,
};
diff --git a/drivers/media/radio/radio-trust.c b/drivers/media/radio/radio-trust.c
index 8da4badc22b..bb03ad5a203 100644
--- a/drivers/media/radio/radio-trust.c
+++ b/drivers/media/radio/radio-trust.c
@@ -12,7 +12,7 @@
* Scott McGrath (smcgrath@twilight.vtc.vsc.edu)
* William McGrath (wmcgrath@twilight.vtc.vsc.edu)
*
- * The basis for this code may be found at http://bigbang.vtc.vsc.edu/fmradio/
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <stdarg.h>
@@ -21,9 +21,46 @@
#include <linux/ioport.h>
#include <asm/io.h>
#include <asm/uaccess.h>
-#include <linux/videodev.h>
+#include <linux/videodev2.h>
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_TRUST_PORT */
+
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 2048,
+ .default_value = 65535,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_BASS,
+ .name = "Bass",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 4370,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },{
+ .id = V4L2_CID_AUDIO_TREBLE,
+ .name = "Treble",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 4370,
+ .default_value = 32768,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ },
+};
/* acceptable ports: 0x350 (JP3 shorted), 0x358 (JP3 open) */
@@ -160,88 +197,125 @@ static int tr_do_ioctl(struct inode *inode, struct file *file,
{
switch(cmd)
{
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
-
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type=VID_TYPE_TUNER;
- v->channels=1;
- v->audios=1;
- strcpy(v->name, "Trust FM Radio");
+ strlcpy(v->driver, "radio-trust", sizeof (v->driver));
+ strlcpy(v->card, "Trust FM Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
+ struct v4l2_tuner *v = arg;
- if(v->tuner) /* Only 1 tuner */
+ if (v->index > 0)
return -EINVAL;
- v->rangelow = 87500 * 16;
- v->rangehigh = 108000 * 16;
- v->flags = VIDEO_TUNER_LOW;
- v->mode = VIDEO_MODE_AUTO;
+ memset(v,0,sizeof(*v));
+ strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
- v->signal = tr_getsigstr();
+ v->rangelow=(87.5*16000);
+ v->rangehigh=(108*16000);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
if(tr_getstereo())
- v->flags |= VIDEO_TUNER_STEREO_ON;
-
- strcpy(v->name, "FM");
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=tr_getsigstr();
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if(v->tuner != 0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
return 0;
}
- case VIDIOCGFREQ:
+ case VIDIOC_S_FREQUENCY:
{
- unsigned long *freq = arg;
- *freq = curfreq;
+ struct v4l2_frequency *f = arg;
+
+ curfreq = f->frequency;
+ tr_setfreq(curfreq);
return 0;
}
- case VIDIOCSFREQ:
+ case VIDIOC_G_FREQUENCY:
{
- unsigned long *freq = arg;
- tr_setfreq(*freq);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = curfreq;
+
return 0;
}
- case VIDIOCGAUDIO:
+ case VIDIOC_QUERYCTRL:
{
- struct video_audio *v = arg;
-
- memset(v,0, sizeof(*v));
- v->flags = VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME |
- VIDEO_AUDIO_BASS | VIDEO_AUDIO_TREBLE;
- v->mode = curstereo? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
- v->volume = curvol * 2048;
- v->step = 2048;
- v->bass = curbass * 4370;
- v->treble = curtreble * 4370;
-
- strcpy(v->name, "Trust FM Radio");
- return 0;
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_CTRL:
{
- struct video_audio *v = arg;
-
- if(v->audio)
- return -EINVAL;
- tr_setvol(v->volume);
- tr_setbass(v->bass);
- tr_settreble(v->treble);
- tr_setstereo(v->mode & VIDEO_SOUND_STEREO);
- tr_setmute(v->flags & VIDEO_AUDIO_MUTE);
- return 0;
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=curmute;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value= curvol * 2048;
+ return (0);
+ case V4L2_CID_AUDIO_BASS:
+ ctrl->value= curbass * 4370;
+ return (0);
+ case V4L2_CID_AUDIO_TREBLE:
+ ctrl->value= curtreble * 4370;
+ return (0);
+ }
+ return -EINVAL;
}
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ tr_setmute(ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_VOLUME:
+ tr_setvol(ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_BASS:
+ tr_setbass(ctrl->value);
+ return 0;
+ case V4L2_CID_AUDIO_TREBLE:
+ tr_settreble(ctrl->value);
+ return (0);
+ }
+ return -EINVAL;
+ }
+
default:
- return -ENOIOCTLCMD;
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ tr_do_ioctl);
}
}
@@ -265,7 +339,7 @@ static struct video_device trust_radio=
.owner = THIS_MODULE,
.name = "Trust FM Radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_TRUST,
+ .hardware = 0,
.fops = &trust_fops,
};
diff --git a/drivers/media/radio/radio-typhoon.c b/drivers/media/radio/radio-typhoon.c
index edd01228866..4a72b4d4e62 100644
--- a/drivers/media/radio/radio-typhoon.c
+++ b/drivers/media/radio/radio-typhoon.c
@@ -27,6 +27,8 @@
* value where I do expect just noise and turn the speaker volume down.
* The frequency change is necessary since the card never seems to be
* completely silent.
+ *
+ * Converted to V4L2 API by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -35,11 +37,32 @@
#include <linux/proc_fs.h> /* radio card status report */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_TYPHOON_* */
-#define BANNER "Typhoon Radio Card driver v0.1\n"
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,1,1)
+#define BANNER "Typhoon Radio Card driver v0.1.1\n"
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 1<<14,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
+
#ifndef CONFIG_RADIO_TYPHOON_PORT
#define CONFIG_RADIO_TYPHOON_PORT -1
@@ -171,76 +194,114 @@ static int typhoon_do_ioctl(struct inode *inode, struct file *file,
struct typhoon_device *typhoon = dev->priv;
switch (cmd) {
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type = VID_TYPE_TUNER;
- v->channels = 1;
- v->audios = 1;
- strcpy(v->name, "Typhoon Radio");
+ strlcpy(v->driver, "radio-typhoon", sizeof (v->driver));
+ strlcpy(v->card, "Typhoon Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if (v->tuner) /* Only 1 tuner */
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- v->rangelow = 875 * 1600;
- v->rangehigh = 1080 * 1600;
- v->flags = VIDEO_TUNER_LOW;
- v->mode = VIDEO_MODE_AUTO;
- v->signal = 0xFFFF; /* We can't get the signal strength */
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow=(87.5*16000);
+ v->rangehigh=(108*16000);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal = 0xFFFF; /* We can't get the signal strength */
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if (v->tuner != 0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
+
return 0;
}
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = typhoon->curfreq;
- return 0;
- }
- case VIDIOCSFREQ:
- {
- unsigned long *freq = arg;
- typhoon->curfreq = *freq;
- typhoon_setfreq(typhoon, typhoon->curfreq);
- return 0;
- }
- case VIDIOCGAUDIO:
+ case VIDIOC_S_FREQUENCY:
{
- struct video_audio *v = arg;
- memset(v, 0, sizeof(*v));
- v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
- v->mode |= VIDEO_SOUND_MONO;
- v->volume = typhoon->curvol;
- v->step = 1 << 14;
- strcpy(v->name, "Typhoon Radio");
+ struct v4l2_frequency *f = arg;
+
+ typhoon->curfreq = f->frequency;
+ typhoon_setfreq(typhoon, typhoon->curfreq);
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_FREQUENCY:
{
- struct video_audio *v = arg;
- if (v->audio)
- return -EINVAL;
- if (v->flags & VIDEO_AUDIO_MUTE)
- typhoon_mute(typhoon);
- else
- typhoon_unmute(typhoon);
- if (v->flags & VIDEO_AUDIO_VOLUME)
- typhoon_setvol(typhoon, v->volume);
+ struct v4l2_frequency *f = arg;
+
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = typhoon->curfreq;
+
return 0;
}
- default:
- return -ENOIOCTLCMD;
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=typhoon->muted;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=typhoon->curvol;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ typhoon_mute(typhoon);
+ } else {
+ typhoon_unmute(typhoon);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ typhoon_setvol(typhoon, ctrl->value);
+ return (0);
+ }
+ return -EINVAL;
+ }
+
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ typhoon_do_ioctl);
}
}
@@ -271,7 +332,7 @@ static struct video_device typhoon_radio =
.owner = THIS_MODULE,
.name = "Typhoon Radio",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_TYPHOON,
+ .hardware = 0,
.fops = &typhoon_fops,
};
diff --git a/drivers/media/radio/radio-zoltrix.c b/drivers/media/radio/radio-zoltrix.c
index 59b86a6b4b0..671fe1b1e5b 100644
--- a/drivers/media/radio/radio-zoltrix.c
+++ b/drivers/media/radio/radio-zoltrix.c
@@ -24,6 +24,9 @@
* - Added unmute function
* - Reworked ioctl functions
* 2002-07-15 - Fix Stereo typo
+ *
+ * 2006-07-24 - Converted to V4L2 API
+ * by Mauro Carvalho Chehab <mchehab@infradead.org>
*/
#include <linux/module.h> /* Modules */
@@ -32,9 +35,30 @@
#include <linux/delay.h> /* udelay, msleep */
#include <asm/io.h> /* outb, outb_p */
#include <asm/uaccess.h> /* copy to/from user */
-#include <linux/videodev.h> /* kernel radio structs */
+#include <linux/videodev2.h> /* kernel radio structs */
#include <media/v4l2-common.h>
-#include <linux/config.h> /* CONFIG_RADIO_ZOLTRIX_PORT */
+
+#include <linux/version.h> /* for KERNEL_VERSION MACRO */
+#define RADIO_VERSION KERNEL_VERSION(0,0,2)
+
+static struct v4l2_queryctrl radio_qctrl[] = {
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .name = "Mute",
+ .minimum = 0,
+ .maximum = 1,
+ .default_value = 1,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ },{
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 65535,
+ .step = 4096,
+ .default_value = 0xff,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ }
+};
#ifndef CONFIG_RADIO_ZOLTRIX_PORT
#define CONFIG_RADIO_ZOLTRIX_PORT -1
@@ -213,78 +237,116 @@ static int zol_do_ioctl(struct inode *inode, struct file *file,
struct zol_device *zol = dev->priv;
switch (cmd) {
- case VIDIOCGCAP:
+ case VIDIOC_QUERYCAP:
{
- struct video_capability *v = arg;
-
+ struct v4l2_capability *v = arg;
memset(v,0,sizeof(*v));
- v->type = VID_TYPE_TUNER;
- v->channels = 1 + zol->stereo;
- v->audios = 1;
- strcpy(v->name, "Zoltrix Radio");
+ strlcpy(v->driver, "radio-zoltrix", sizeof (v->driver));
+ strlcpy(v->card, "Zoltrix Radio", sizeof (v->card));
+ sprintf(v->bus_info,"ISA");
+ v->version = RADIO_VERSION;
+ v->capabilities = V4L2_CAP_TUNER;
+
return 0;
}
- case VIDIOCGTUNER:
+ case VIDIOC_G_TUNER:
{
- struct video_tuner *v = arg;
- if (v->tuner)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
+
+ memset(v,0,sizeof(*v));
strcpy(v->name, "FM");
- v->rangelow = (int) (88.0 * 16000);
- v->rangehigh = (int) (108.0 * 16000);
- v->flags = zol_is_stereo(zol)
- ? VIDEO_TUNER_STEREO_ON : 0;
- v->flags |= VIDEO_TUNER_LOW;
- v->mode = VIDEO_MODE_AUTO;
- v->signal = 0xFFFF * zol_getsigstr(zol);
+ v->type = V4L2_TUNER_RADIO;
+
+ v->rangelow=(88*16000);
+ v->rangehigh=(108*16000);
+ v->rxsubchans =V4L2_TUNER_SUB_MONO|V4L2_TUNER_SUB_STEREO;
+ v->capability=V4L2_TUNER_CAP_LOW;
+ if(zol_is_stereo(zol))
+ v->audmode = V4L2_TUNER_MODE_STEREO;
+ else
+ v->audmode = V4L2_TUNER_MODE_MONO;
+ v->signal=0xFFFF*zol_getsigstr(zol);
+
return 0;
}
- case VIDIOCSTUNER:
+ case VIDIOC_S_TUNER:
{
- struct video_tuner *v = arg;
- if (v->tuner != 0)
+ struct v4l2_tuner *v = arg;
+
+ if (v->index > 0)
return -EINVAL;
- /* Only 1 tuner so no setting needed ! */
+
return 0;
}
- case VIDIOCGFREQ:
- {
- unsigned long *freq = arg;
- *freq = zol->curfreq;
- return 0;
- }
- case VIDIOCSFREQ:
- {
- unsigned long *freq = arg;
- zol->curfreq = *freq;
- zol_setfreq(zol, zol->curfreq);
- return 0;
- }
- case VIDIOCGAUDIO:
+ case VIDIOC_S_FREQUENCY:
{
- struct video_audio *v = arg;
- memset(v, 0, sizeof(*v));
- v->flags |= VIDEO_AUDIO_MUTABLE | VIDEO_AUDIO_VOLUME;
- v->mode |= zol_is_stereo(zol)
- ? VIDEO_SOUND_STEREO : VIDEO_SOUND_MONO;
- v->volume = zol->curvol * 4096;
- v->step = 4096;
- strcpy(v->name, "Zoltrix Radio");
+ struct v4l2_frequency *f = arg;
+
+ zol->curfreq = f->frequency;
+ zol_setfreq(zol, zol->curfreq);
return 0;
}
- case VIDIOCSAUDIO:
+ case VIDIOC_G_FREQUENCY:
{
- struct video_audio *v = arg;
- if (v->audio)
- return -EINVAL;
+ struct v4l2_frequency *f = arg;
- if (v->flags & VIDEO_AUDIO_MUTE)
- zol_mute(zol);
- else {
- zol_unmute(zol);
- zol_setvol(zol, v->volume / 4096);
- }
+ f->type = V4L2_TUNER_RADIO;
+ f->frequency = zol->curfreq;
+ return 0;
+ }
+ case VIDIOC_QUERYCTRL:
+ {
+ struct v4l2_queryctrl *qc = arg;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(radio_qctrl); i++) {
+ if (qc->id && qc->id == radio_qctrl[i].id) {
+ memcpy(qc, &(radio_qctrl[i]),
+ sizeof(*qc));
+ return (0);
+ }
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_G_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ ctrl->value=zol->muted;
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ ctrl->value=zol->curvol * 4096;
+ return (0);
+ }
+ return -EINVAL;
+ }
+ case VIDIOC_S_CTRL:
+ {
+ struct v4l2_control *ctrl= arg;
+
+ switch (ctrl->id) {
+ case V4L2_CID_AUDIO_MUTE:
+ if (ctrl->value) {
+ zol_mute(zol);
+ } else {
+ zol_unmute(zol);
+ zol_setvol(zol,zol->curvol);
+ }
+ return (0);
+ case V4L2_CID_AUDIO_VOLUME:
+ zol_setvol(zol,ctrl->value/4096);
+ return (0);
+ }
+ zol->stereo = 1;
+ zol_setfreq(zol, zol->curfreq);
+#if 0
+/* FIXME: Implement stereo/mono switch on V4L2 */
if (v->mode & VIDEO_SOUND_STEREO) {
zol->stereo = 1;
zol_setfreq(zol, zol->curfreq);
@@ -293,10 +355,13 @@ static int zol_do_ioctl(struct inode *inode, struct file *file,
zol->stereo = 0;
zol_setfreq(zol, zol->curfreq);
}
- return 0;
+#endif
+ return -EINVAL;
}
- default:
- return -ENOIOCTLCMD;
+
+ default:
+ return v4l_compat_translate_ioctl(inode,file,cmd,arg,
+ zol_do_ioctl);
}
}
@@ -323,7 +388,7 @@ static struct video_device zoltrix_radio =
.owner = THIS_MODULE,
.name = "Zoltrix Radio Plus",
.type = VID_TYPE_TUNER,
- .hardware = VID_HARDWARE_ZOLTRIX,
+ .hardware = 0,
.fops = &zoltrix_fops,
};
diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig
index 94d078b77ba..d1183c93922 100644
--- a/drivers/media/video/Kconfig
+++ b/drivers/media/video/Kconfig
@@ -16,6 +16,320 @@ config VIDEO_ADV_DEBUG
V4L devices.
In doubt, say N.
+config VIDEO_HELPER_CHIPS_AUTO
+ bool "Autoselect pertinent encoders/decoders and other helper chips"
+ default y
+ ---help---
+ Most video cards may require additional modules to encode or
+ decode audio/video standards. This option will autoselect
+ all pertinent modules to each selected video module.
+
+ Unselect this only if you know exaclty what you are doing, since
+ it may break support on some boards.
+
+ In doubt, say Y.
+
+#
+# Encoder / Decoder module configuration
+#
+
+menu "Encoders/decoders and other helper chips"
+ depends on VIDEO_DEV && !VIDEO_HELPER_CHIPS_AUTO
+
+comment "Audio Decoders"
+
+config VIDEO_TVAUDIO
+ tristate "Simple audio decoder chips"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for several audio decoder chips found on some bt8xx boards:
+ Philips: tda9840, tda9873h, tda9874h/a, tda9850, tda985x, tea6300,
+ tea6320, tea6420, tda8425, ta8874z.
+ Microchip: pic16c54 based design on ProVideo PV951 board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvaudio.
+
+config VIDEO_TDA7432
+ tristate "Philips TDA7432 audio processor chip"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for tda7432 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda7432.
+
+config VIDEO_TDA9840
+ tristate "Philips TDA9840 audio processor chip"
+ depends on VIDEO_DEV && I2C
+ ---help---
+ Support for tda9840 audio decoder chip found on some Zoran boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda9840.
+
+config VIDEO_TDA9875
+ tristate "Philips TDA9875 audio processor chip"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for tda9875 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tda9875.
+
+config VIDEO_TEA6415C
+ tristate "Philips TEA6415C audio processor chip"
+ depends on VIDEO_DEV && I2C
+ ---help---
+ Support for tea6415c audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6415c.
+
+config VIDEO_TEA6420
+ tristate "Philips TEA6420 audio processor chip"
+ depends on VIDEO_DEV && I2C
+ ---help---
+ Support for tea6420 audio decoder chip found on some bt8xx boards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tea6420.
+
+config VIDEO_MSP3400
+ tristate "Micronas MSP34xx audio decoders"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Micronas MSP34xx series of audio decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called msp3400.
+
+config VIDEO_CS53L32A
+ tristate "Cirrus Logic CS53L32A audio ADC"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Cirrus Logic CS53L32A low voltage
+ stereo A/D converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called cs53l32a.
+
+config VIDEO_TLV320AIC23B
+ tristate "Texas Instruments TLV320AIC23B audio codec"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Texas Instruments TLV320AIC23B audio codec.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tlv320aic23b.
+
+config VIDEO_WM8775
+ tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Wolfson Microelectronics WM8775 high
+ performance stereo A/D Converter with a 4 channel input mixer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8775.
+
+config VIDEO_WM8739
+ tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Wolfson Microelectronics WM8739
+ stereo A/D Converter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called wm8739.
+
+comment "MPEG video encoders"
+
+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"
+
+comment "Video encoders"
+
+config VIDEO_SAA7127
+ tristate "Philips SAA7127/9 digital video encoders"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Philips SAA7127/9 digital video encoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7127.
+
+config VIDEO_SAA7185
+ tristate "Philips SAA7185 video encoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Philips SAA7185 video encoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7185.
+
+config VIDEO_ADV7170
+ tristate "Analog Devices ADV7170 video encoder driver"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Analog Devices ADV7170 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7170.
+
+config VIDEO_ADV7175
+ tristate "Analog Devices ADV7175 video encoder driver"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Analog Devices ADV7175 video encoder driver
+
+ To compile this driver as a module, choose M here: the
+ module will be called adv7175.
+
+comment "Video decoders"
+
+config VIDEO_BT819
+ tristate "BT819A VideoStream Decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for BT819A video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt819.
+
+config VIDEO_BT856
+ tristate "BT856 VideoStream Decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for BT856 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt856.
+
+config VIDEO_BT866
+ tristate "BT866 VideoStream Decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for BT866 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bt866.
+
+config VIDEO_KS0127
+ tristate "KS0127 video decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for KS0127 video decoder.
+
+ This chip is used on AverMedia AVS6EYES Zoran-based MJPEG
+ cards.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ks0127.
+
+config VIDEO_SAA7110
+ tristate "Philips SAA7110 video decoder"
+ depends on VIDEO_V4L1
+ ---help---
+ Support for the Philips SAA7110 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7110.
+
+config VIDEO_SAA7111
+ tristate "Philips SAA7111 video decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Philips SAA711 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7111.
+
+config VIDEO_SAA7114
+ tristate "Philips SAA7114 video decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Philips SAA7114 video decoder. This driver
+ is used only on Zoran driver and should be moved soon to
+ SAA711x module.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7114.
+
+config VIDEO_SAA711X
+ tristate "Philips SAA7113/4/5 video decoders"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the Philips SAA7113/4/5 video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7115.
+
+config VIDEO_SAA7191
+ tristate "Philips SAA7191 video decoder"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for the Philips SAA7191 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called saa7191.
+
+config VIDEO_TVP5150
+ tristate "Texas Instruments TVP5150 video decoder"
+ depends on VIDEO_V4L2 && I2C
+ ---help---
+ Support for the Texas Instruments TVP5150 video decoder.
+
+ To compile this driver as a module, choose M here: the
+ module will be called tvp5150.
+
+config VIDEO_VPX3220
+ tristate "vpx3220a, vpx3216b & vpx3214c video decoder driver"
+ depends on VIDEO_V4L1 && I2C
+ ---help---
+ Support for VPX322x video decoders.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vpx3220.
+
+comment "Video improvement chips"
+
+config VIDEO_UPD64031A
+ tristate "NEC Electronics uPD64031A Ghost Reduction"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the NEC Electronics uPD64031A Ghost Reduction
+ video chip. It is most often found in NTSC TV cards made for
+ Japan and is used to reduce the 'ghosting' effect that can
+ be present in analog TV broadcasts.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64031a.
+
+config VIDEO_UPD64083
+ tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
+ ---help---
+ Support for the NEC Electronics uPD64083 3-Dimensional Y/C
+ separation video chip. It is used to improve the quality of
+ the colors of a composite signal.
+
+ To compile this driver as a module, choose M here: the
+ module will be called upd64083.
+
+endmenu # encoder / decoder chips
+
config VIDEO_VIVI
tristate "Virtual Video Driver"
depends on VIDEO_V4L2 && !SPARC32 && !SPARC64
@@ -135,7 +449,7 @@ source "drivers/media/video/cpia2/Kconfig"
config VIDEO_SAA5246A
tristate "SAA5246A, SAA5281 Teletext processor"
- depends on I2C && VIDEO_V4L1
+ depends on I2C && VIDEO_V4L2
help
Support for I2C bus based teletext using the SAA5246A or SAA5281
chip. Useful only if you live in Europe.
@@ -145,7 +459,7 @@ config VIDEO_SAA5246A
config VIDEO_SAA5249
tristate "SAA5249 Teletext processor"
- depends on VIDEO_DEV && I2C && VIDEO_V4L1
+ depends on VIDEO_DEV && I2C && VIDEO_V4L2
help
Support for I2C bus based teletext using the SAA5249 chip. At the
moment this is only useful on some European WinTV cards.
@@ -162,8 +476,9 @@ config TUNER_3036
config VIDEO_VINO
tristate "SGI Vino Video For Linux (EXPERIMENTAL)"
- depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L1
+ depends on I2C && SGI_IP22 && EXPERIMENTAL && VIDEO_V4L2
select I2C_ALGO_SGI
+ select VIDEO_SAA7191 if VIDEO_HELPER_CHIPS_AUTO
help
Say Y here to build in support for the Vino video input system found
on SGI Indy machines.
@@ -176,6 +491,9 @@ config VIDEO_STRADIS
driver for PCI. There is a product page at
<http://www.stradis.com/>.
+config VIDEO_ZORAN_ZR36060
+ tristate
+
config VIDEO_ZORAN
tristate "Zoran ZR36057/36067 Video For Linux"
depends on PCI && I2C_ALGOBIT && VIDEO_V4L1 && !PPC64
@@ -192,12 +510,18 @@ config VIDEO_ZORAN
config VIDEO_ZORAN_BUZ
tristate "Iomega Buz support"
depends on VIDEO_ZORAN
+ select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_SAA7185 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ZORAN_ZR36060
help
Support for the Iomega Buz MJPEG capture/playback card.
config VIDEO_ZORAN_DC10
tristate "Pinnacle/Miro DC10(+) support"
depends on VIDEO_ZORAN
+ select VIDEO_SAA7110
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ZORAN_ZR36060
help
Support for the Pinnacle/Miro DC10(+) MJPEG capture/playback
card.
@@ -205,6 +529,8 @@ config VIDEO_ZORAN_DC10
config VIDEO_ZORAN_DC30
tristate "Pinnacle/Miro DC30(+) support"
depends on VIDEO_ZORAN
+ select VIDEO_ADV7175 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_VPX3220 if VIDEO_HELPER_CHIPS_AUTO
help
Support for the Pinnacle/Miro DC30(+) MJPEG capture/playback
card. This also supports really old DC10 cards based on the
@@ -213,6 +539,9 @@ config VIDEO_ZORAN_DC30
config VIDEO_ZORAN_LML33
tristate "Linux Media Labs LML33 support"
depends on VIDEO_ZORAN
+ select VIDEO_BT819 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ZORAN_ZR36060
help
Support for the Linux Media Labs LML33 MJPEG capture/playback
card.
@@ -220,6 +549,9 @@ config VIDEO_ZORAN_LML33
config VIDEO_ZORAN_LML33R10
tristate "Linux Media Labs LML33R10 support"
depends on VIDEO_ZORAN
+ select VIDEO_SAA7114 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ADV7170 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ZORAN_ZR36060
help
support for the Linux Media Labs LML33R10 MJPEG capture/playback
card.
@@ -227,6 +559,9 @@ config VIDEO_ZORAN_LML33R10
config VIDEO_ZORAN_AVS6EYES
tristate "AverMedia 6 Eyes support (EXPERIMENTAL)"
depends on VIDEO_ZORAN && EXPERIMENTAL && VIDEO_V4L1
+ select VIDEO_BT856 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_KS0127 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_ZORAN_ZR36060
help
Support for the AverMedia 6 Eyes video surveillance card.
@@ -263,6 +598,10 @@ config VIDEO_MXB
depends on PCI && VIDEO_V4L1 && I2C
select VIDEO_SAA7146_VV
select VIDEO_TUNER
+ select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA9840 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6415C if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TEA6420 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for the 'Multimedia eXtension Board'
TV card by Siemens-Nixdorf.
@@ -274,7 +613,7 @@ config VIDEO_DPC
tristate "Philips-Semiconductors 'dpc7146 demonstration board'"
depends on PCI && VIDEO_V4L1 && I2C
select VIDEO_SAA7146_VV
- select VIDEO_V4L2
+ select VIDEO_SAA7111 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for the 'dpc7146 demonstration
board' by Philips-Semiconductors. It's the reference design
@@ -287,9 +626,8 @@ config VIDEO_DPC
config VIDEO_HEXIUM_ORION
tristate "Hexium HV-PCI6 and Orion frame grabber"
- depends on PCI && VIDEO_V4L1 && I2C
+ depends on PCI && VIDEO_V4L2 && I2C
select VIDEO_SAA7146_VV
- select VIDEO_V4L2
---help---
This is a video4linux driver for the Hexium HV-PCI6 and
Orion frame grabber cards by Hexium.
@@ -299,9 +637,8 @@ config VIDEO_HEXIUM_ORION
config VIDEO_HEXIUM_GEMINI
tristate "Hexium Gemini frame grabber"
- depends on PCI && VIDEO_V4L1 && I2C
+ depends on PCI && VIDEO_V4L2 && I2C
select VIDEO_SAA7146_VV
- select VIDEO_V4L2
---help---
This is a video4linux driver for the Hexium Gemini frame
grabber card by Hexium. Please note that the Gemini Dual
@@ -320,123 +657,16 @@ config VIDEO_M32R_AR
camera module.
config VIDEO_M32R_AR_M64278
- tristate "Use Colour AR module M64278(VGA)"
- depends on VIDEO_M32R_AR && PLAT_M32700UT
- ---help---
- Say Y here to use the Renesas M64278E-800 camera module,
- which supports VGA(640x480 pixcels) size of images.
-
-#
-# Encoder / Decoder module configuration
-#
-
-menu "Encoders and Decoders"
- depends on VIDEO_DEV
-
-config VIDEO_MSP3400
- tristate "Micronas MSP34xx audio decoders"
- depends on VIDEO_DEV && I2C
- ---help---
- Support for the Micronas MSP34xx series of audio decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called msp3400.
-
-config VIDEO_CS53L32A
- tristate "Cirrus Logic CS53L32A audio ADC"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
- ---help---
- Support for the Cirrus Logic CS53L32A low voltage
- stereo A/D converter.
-
- To compile this driver as a module, choose M here: the
- module will be called cs53l32a.
-
-config VIDEO_TLV320AIC23B
- tristate "Texas Instruments TLV320AIC23B audio codec"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
+ tristate "AR device with color module M64278(VGA)"
+ depends on PLAT_M32700UT
+ select VIDEO_M32R_AR
---help---
- Support for the Texas Instruments TLV320AIC23B audio codec.
-
- To compile this driver as a module, choose M here: the
- module will be called tlv320aic23b.
-
-config VIDEO_WM8775
- tristate "Wolfson Microelectronics WM8775 audio ADC with input mixer"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
- ---help---
- Support for the Wolfson Microelectronics WM8775 high
- performance stereo A/D Converter with a 4 channel input mixer.
+ This is a video4linux driver for the Renesas AR (Artificial
+ Retina) with M64278E-800 camera module.
+ This module supports VGA(640x480 pixels) resolutions.
To compile this driver as a module, choose M here: the
- module will be called wm8775.
-
-config VIDEO_WM8739
- tristate "Wolfson Microelectronics WM8739 stereo audio ADC"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
- ---help---
- Support for the Wolfson Microelectronics WM8739
- stereo A/D Converter.
-
- 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
- tristate "Philips SAA7113/4/5 video decoders"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
- ---help---
- Support for the Philips SAA7113/4/5 video decoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7115.
-
-config VIDEO_SAA7127
- tristate "Philips SAA7127/9 digital video encoders"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
- ---help---
- Support for the Philips SAA7127/9 digital video encoders.
-
- To compile this driver as a module, choose M here: the
- module will be called saa7127.
-
-config VIDEO_UPD64031A
- tristate "NEC Electronics uPD64031A Ghost Reduction"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
- ---help---
- Support for the NEC Electronics uPD64031A Ghost Reduction
- video chip. It is most often found in NTSC TV cards made for
- Japan and is used to reduce the 'ghosting' effect that can
- be present in analog TV broadcasts.
-
- To compile this driver as a module, choose M here: the
- module will be called upd64031a.
-
-config VIDEO_UPD64083
- tristate "NEC Electronics uPD64083 3-Dimensional Y/C separation"
- depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
- ---help---
- Support for the NEC Electronics uPD64083 3-Dimensional Y/C
- separation video chip. It is used to improve the quality of
- the colors of a composite signal.
-
- To compile this driver as a module, choose M here: the
- module will be called upd64083.
-
-endmenu # encoder / decoder chips
+ module will be called arv.
#
# USB Multimedia device configuration
@@ -445,8 +675,6 @@ 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"
source "drivers/media/video/usbvideo/Kconfig"
diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile
index e82e511f2a7..af57abce8a6 100644
--- a/drivers/media/video/Makefile
+++ b/drivers/media/video/Makefile
@@ -17,7 +17,10 @@ ifeq ($(CONFIG_VIDEO_V4L1_COMPAT),y)
endif
obj-$(CONFIG_VIDEO_BT848) += bt8xx/
-obj-$(CONFIG_VIDEO_BT848) += tvaudio.o tda7432.o tda9875.o ir-kbd-i2c.o
+obj-$(CONFIG_VIDEO_BT848) += ir-kbd-i2c.o
+obj-$(CONFIG_VIDEO_TVAUDIO) += tvaudio.o
+obj-$(CONFIG_VIDEO_TDA7432) += tda7432.o
+obj-$(CONFIG_VIDEO_TDA9875) += tda9875.o
obj-$(CONFIG_SOUND_TVMIXER) += tvmixer.o
obj-$(CONFIG_VIDEO_ZR36120) += zoran.o
@@ -27,17 +30,32 @@ obj-$(CONFIG_VIDEO_SAA5249) += saa5249.o
obj-$(CONFIG_VIDEO_CQCAM) += c-qcam.o
obj-$(CONFIG_VIDEO_BWQCAM) += bw-qcam.o
obj-$(CONFIG_VIDEO_W9966) += w9966.o
-obj-$(CONFIG_VIDEO_ZORAN_BUZ) += saa7111.o saa7185.o zr36060.o
-obj-$(CONFIG_VIDEO_ZORAN_DC10) += saa7110.o adv7175.o zr36060.o
-obj-$(CONFIG_VIDEO_ZORAN_DC30) += adv7175.o vpx3220.o zr36050.o \
- zr36016.o
-obj-$(CONFIG_VIDEO_ZORAN_LML33) += bt819.o bt856.o zr36060.o
-obj-$(CONFIG_VIDEO_ZORAN_LML33R10) += saa7114.o adv7170.o zr36060.o
-obj-$(CONFIG_VIDEO_ZORAN_AVS6EYES) += bt866.o ks0127.o zr36060.o
+
+obj-$(CONFIG_VIDEO_TDA9840) += tda9840.o
+obj-$(CONFIG_VIDEO_TEA6415C) += tea6415c.o
+obj-$(CONFIG_VIDEO_TEA6420) += tea6420.o
+obj-$(CONFIG_VIDEO_SAA7110) += saa7110.o
+obj-$(CONFIG_VIDEO_SAA7111) += saa7111.o
+obj-$(CONFIG_VIDEO_SAA7114) += saa7114.o
+obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
+obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
+obj-$(CONFIG_VIDEO_SAA7185) += saa7185.o
+obj-$(CONFIG_VIDEO_SAA7191) += saa7191.o
+obj-$(CONFIG_VIDEO_ADV7170) += adv7170.o
+obj-$(CONFIG_VIDEO_ADV7175) += adv7175.o
+obj-$(CONFIG_VIDEO_VPX3220) += vpx3220.o
+obj-$(CONFIG_VIDEO_BT819) += bt819.o
+obj-$(CONFIG_VIDEO_BT856) += bt856.o
+obj-$(CONFIG_VIDEO_BT866) += bt866.o
+obj-$(CONFIG_VIDEO_KS0127) += ks0127.o
+
obj-$(CONFIG_VIDEO_ZORAN) += zr36067.o videocodec.o
+obj-$(CONFIG_VIDEO_ZORAN_DC30) += zr36050.o zr36016.o
+obj-$(CONFIG_VIDEO_ZORAN_ZR36060) += zr36060.o
+
obj-$(CONFIG_VIDEO_PMS) += pms.o
obj-$(CONFIG_VIDEO_PLANB) += planb.o
-obj-$(CONFIG_VIDEO_VINO) += vino.o saa7191.o indycam.o
+obj-$(CONFIG_VIDEO_VINO) += vino.o indycam.o
obj-$(CONFIG_VIDEO_STRADIS) += stradis.o
obj-$(CONFIG_VIDEO_CPIA) += cpia.o
obj-$(CONFIG_VIDEO_CPIA_PP) += cpia_pp.o
@@ -46,7 +64,7 @@ obj-$(CONFIG_VIDEO_MEYE) += meye.o
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_TVP5150) += tvp5150.o
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2/
obj-$(CONFIG_VIDEO_MSP3400) += msp3400.o
obj-$(CONFIG_VIDEO_CS53L32A) += cs53l32a.o
@@ -55,10 +73,10 @@ obj-$(CONFIG_VIDEO_WM8775) += wm8775.o
obj-$(CONFIG_VIDEO_WM8739) += wm8739.o
obj-$(CONFIG_VIDEO_OVCAMCHIP) += ovcamchip/
obj-$(CONFIG_VIDEO_CPIA2) += cpia2/
-obj-$(CONFIG_VIDEO_MXB) += saa7111.o tda9840.o tea6415c.o tea6420.o mxb.o
+obj-$(CONFIG_VIDEO_MXB) += mxb.o
obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o
obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o
-obj-$(CONFIG_VIDEO_DPC) += saa7111.o dpc7146.o
+obj-$(CONFIG_VIDEO_DPC) += dpc7146.o
obj-$(CONFIG_TUNER_3036) += tuner-3036.o
obj-$(CONFIG_VIDEO_TUNER) += tuner.o
@@ -70,8 +88,6 @@ obj-$(CONFIG_VIDEO_TVEEPROM) += tveeprom.o
obj-$(CONFIG_VIDEO_M32R_AR_M64278) += arv.o
obj-$(CONFIG_VIDEO_CX25840) += cx25840/
-obj-$(CONFIG_VIDEO_SAA711X) += saa7115.o
-obj-$(CONFIG_VIDEO_SAA7127) += saa7127.o
obj-$(CONFIG_VIDEO_UPD64031A) += upd64031a.o
obj-$(CONFIG_VIDEO_UPD64083) += upd64083.o
obj-$(CONFIG_VIDEO_CX2341X) += cx2341x.o
@@ -96,4 +112,3 @@ obj-$(CONFIG_VIDEO_VIVI) += vivi.o
EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
extra-cflags-$(CONFIG_VIDEO_V4L1_COMPAT) += -DCONFIG_VIDEO_V4L1_COMPAT
-
diff --git a/drivers/media/video/bt866.c b/drivers/media/video/bt866.c
index 05e42bbcfc3..772fd52d551 100644
--- a/drivers/media/video/bt866.c
+++ b/drivers/media/video/bt866.c
@@ -65,7 +65,7 @@ MODULE_LICENSE("GPL");
struct bt866 {
struct i2c_client *i2c;
int addr;
- unsigned char reg[128];
+ unsigned char reg[256];
int norm;
int enable;
diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig
index cdcf5565071..58eae887a62 100644
--- a/drivers/media/video/bt8xx/Kconfig
+++ b/drivers/media/video/bt8xx/Kconfig
@@ -8,7 +8,10 @@ config VIDEO_BT848
select VIDEO_IR
select VIDEO_TUNER
select VIDEO_TVEEPROM
- select VIDEO_MSP3400
+ select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TVAUDIO if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA7432 if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TDA9875 if VIDEO_HELPER_CHIPS_AUTO
---help---
Support for BT848 based frame grabber/overlay boards. This includes
the Miro, Hauppauge and STB boards. Please read the material in
diff --git a/drivers/media/video/bt8xx/bttv-cards.c b/drivers/media/video/bt8xx/bttv-cards.c
index de14818d5cc..d23a42b1504 100644
--- a/drivers/media/video/bt8xx/bttv-cards.c
+++ b/drivers/media/video/bt8xx/bttv-cards.c
@@ -4991,7 +4991,7 @@ void __devinit bttv_check_chipset(void)
int pcipci_fail = 0;
struct pci_dev *dev = NULL;
- if (pci_pci_problems & PCIPCI_FAIL)
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) /* should check if target is AGP */
pcipci_fail = 1;
if (pci_pci_problems & (PCIPCI_TRITON|PCIPCI_NATOMA|PCIPCI_VIAETBF))
triton1 = 1;
diff --git a/drivers/media/video/bt8xx/bttv-driver.c b/drivers/media/video/bt8xx/bttv-driver.c
index 20dff7c316e..50dde82844e 100644
--- a/drivers/media/video/bt8xx/bttv-driver.c
+++ b/drivers/media/video/bt8xx/bttv-driver.c
@@ -2431,6 +2431,14 @@ static int bttv_do_ioctl(struct inode *inode, struct file *file,
fbuf->bytesperline = btv->fbuf.fmt.bytesperline;
if (fh->ovfmt)
fbuf->depth = fh->ovfmt->depth;
+ else {
+ if (fbuf->width)
+ fbuf->depth = ((fbuf->bytesperline<<3)
+ + (fbuf->width-1) )
+ /fbuf->width;
+ else
+ fbuf->depth = 0;
+ }
return 0;
}
case VIDIOCSFBUF:
@@ -4186,6 +4194,7 @@ static void __devexit bttv_remove(struct pci_dev *pci_dev)
return;
}
+#ifdef CONFIG_PM
static int bttv_suspend(struct pci_dev *pci_dev, pm_message_t state)
{
struct bttv *btv = pci_get_drvdata(pci_dev);
@@ -4266,6 +4275,7 @@ static int bttv_resume(struct pci_dev *pci_dev)
spin_unlock_irqrestore(&btv->s_lock,flags);
return 0;
}
+#endif
static struct pci_device_id bttv_pci_tbl[] = {
{PCI_VENDOR_ID_BROOKTREE, PCI_DEVICE_ID_BT848,
@@ -4286,8 +4296,10 @@ static struct pci_driver bttv_pci_driver = {
.id_table = bttv_pci_tbl,
.probe = bttv_probe,
.remove = __devexit_p(bttv_remove),
+#ifdef CONFIG_PM
.suspend = bttv_suspend,
.resume = bttv_resume,
+#endif
};
static int bttv_init_module(void)
diff --git a/drivers/media/video/bt8xx/bttv-i2c.c b/drivers/media/video/bt8xx/bttv-i2c.c
index 4b562b386fc..70de6c96e20 100644
--- a/drivers/media/video/bt8xx/bttv-i2c.c
+++ b/drivers/media/video/bt8xx/bttv-i2c.c
@@ -8,6 +8,9 @@
& Marcus Metzler (mocm@thp.uni-koeln.de)
(c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+ (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ - Multituner support and i2c address binding
+
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
@@ -45,10 +48,18 @@ static int i2c_debug;
static int i2c_hw;
static int i2c_scan;
module_param(i2c_debug, int, 0644);
+MODULE_PARM_DESC(i2c_hw,"configure i2c debug level");
module_param(i2c_hw, int, 0444);
+MODULE_PARM_DESC(i2c_hw,"force use of hardware i2c support, "
+ "instead of software bitbang");
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0444);
+MODULE_PARM_DESC(i2c_udelay,"soft i2c delay at insmod time, in usecs "
+ "(should be 5 or higher). Lower value means higher bus speed.");
+
/* ----------------------------------------------------------------------- */
/* I2C functions - bitbanging adapter (software i2c) */
@@ -100,7 +111,6 @@ static struct i2c_algo_bit_data bttv_i2c_algo_bit_template = {
.getsda = bttv_bit_getsda,
.getscl = bttv_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
@@ -426,6 +436,11 @@ int __devinit init_bttv_i2c(struct bttv *btv)
sizeof(bttv_i2c_adap_hw_template));
} else {
/* bt848 */
+ /* Prevents usage of invalid delay values */
+ if (i2c_udelay<5)
+ i2c_udelay=5;
+ bttv_i2c_algo_bit_template.udelay=i2c_udelay;
+
memcpy(&btv->c.i2c_adap, &bttv_i2c_adap_sw_template,
sizeof(bttv_i2c_adap_sw_template));
memcpy(&btv->i2c_algo, &bttv_i2c_algo_bit_template,
diff --git a/drivers/media/video/compat_ioctl32.c b/drivers/media/video/compat_ioctl32.c
index b69ee119481..d82a488f12a 100644
--- a/drivers/media/video/compat_ioctl32.c
+++ b/drivers/media/video/compat_ioctl32.c
@@ -58,6 +58,7 @@ static int put_video_tuner32(struct video_tuner *kp, struct video_tuner32 __user
return 0;
}
+
struct video_buffer32 {
compat_caddr_t base;
compat_int_t height, width, depth, bytesperline;
@@ -618,6 +619,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
struct video_buffer vb;
struct video_window vw;
struct video_code vc;
+ struct video_audio va;
#endif
struct v4l2_format v2f;
struct v4l2_buffer v2b;
@@ -635,31 +637,31 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
/* First, convert the command. */
switch(cmd) {
#ifdef CONFIG_VIDEO_V4L1_COMPAT
- case VIDIOCGTUNER32: cmd = VIDIOCGTUNER; break;
- case VIDIOCSTUNER32: cmd = VIDIOCSTUNER; break;
- case VIDIOCGWIN32: cmd = VIDIOCGWIN; break;
- case VIDIOCGFBUF32: cmd = VIDIOCGFBUF; break;
- case VIDIOCSFBUF32: cmd = VIDIOCSFBUF; break;
- case VIDIOCGFREQ32: cmd = VIDIOCGFREQ; break;
- case VIDIOCSFREQ32: cmd = VIDIOCSFREQ; break;
- case VIDIOCSMICROCODE32: cmd = VIDIOCSMICROCODE; break;
+ case VIDIOCGTUNER32: realcmd = cmd = VIDIOCGTUNER; break;
+ case VIDIOCSTUNER32: realcmd = cmd = VIDIOCSTUNER; break;
+ case VIDIOCGWIN32: realcmd = cmd = VIDIOCGWIN; break;
+ case VIDIOCGFBUF32: realcmd = cmd = VIDIOCGFBUF; break;
+ case VIDIOCSFBUF32: realcmd = cmd = VIDIOCSFBUF; break;
+ case VIDIOCGFREQ32: realcmd = cmd = VIDIOCGFREQ; break;
+ case VIDIOCSFREQ32: realcmd = cmd = VIDIOCSFREQ; break;
+ case VIDIOCSMICROCODE32: realcmd = cmd = VIDIOCSMICROCODE; break;
#endif
- case VIDIOC_G_FMT32: cmd = VIDIOC_G_FMT; break;
- case VIDIOC_S_FMT32: cmd = VIDIOC_S_FMT; break;
- case VIDIOC_QUERYBUF32: cmd = VIDIOC_QUERYBUF; break;
- case VIDIOC_QBUF32: cmd = VIDIOC_QBUF; break;
- case VIDIOC_DQBUF32: cmd = VIDIOC_DQBUF; break;
- case VIDIOC_STREAMON32: cmd = VIDIOC_STREAMON; break;
- case VIDIOC_STREAMOFF32: cmd = VIDIOC_STREAMOFF; break;
- case VIDIOC_G_FBUF32: cmd = VIDIOC_G_FBUF; break;
- case VIDIOC_S_FBUF32: cmd = VIDIOC_S_FBUF; break;
- case VIDIOC_OVERLAY32: cmd = VIDIOC_OVERLAY; break;
+ case VIDIOC_G_FMT32: realcmd = cmd = VIDIOC_G_FMT; break;
+ case VIDIOC_S_FMT32: realcmd = cmd = VIDIOC_S_FMT; break;
+ case VIDIOC_QUERYBUF32: realcmd = cmd = VIDIOC_QUERYBUF; break;
+ case VIDIOC_QBUF32: realcmd = cmd = VIDIOC_QBUF; break;
+ case VIDIOC_DQBUF32: realcmd = cmd = VIDIOC_DQBUF; break;
+ case VIDIOC_STREAMON32: realcmd = cmd = VIDIOC_STREAMON; break;
+ case VIDIOC_STREAMOFF32: realcmd = cmd = VIDIOC_STREAMOFF; break;
+ case VIDIOC_G_FBUF32: realcmd = cmd = VIDIOC_G_FBUF; break;
+ case VIDIOC_S_FBUF32: realcmd = cmd = VIDIOC_S_FBUF; break;
+ case VIDIOC_OVERLAY32: realcmd = cmd = VIDIOC_OVERLAY; break;
case VIDIOC_ENUMSTD32: realcmd = VIDIOC_ENUMSTD; break;
case VIDIOC_ENUMINPUT32: realcmd = VIDIOC_ENUMINPUT; break;
- case VIDIOC_S_CTRL32: cmd = VIDIOC_S_CTRL; break;
- case VIDIOC_G_INPUT32: cmd = VIDIOC_G_INPUT; break;
- case VIDIOC_S_INPUT32: cmd = VIDIOC_S_INPUT; break;
- case VIDIOC_TRY_FMT32: cmd = VIDIOC_TRY_FMT; break;
+ case VIDIOC_S_CTRL32: realcmd = cmd = VIDIOC_S_CTRL; break;
+ case VIDIOC_G_INPUT32: realcmd = cmd = VIDIOC_G_INPUT; break;
+ case VIDIOC_S_INPUT32: realcmd = cmd = VIDIOC_S_INPUT; break;
+ case VIDIOC_TRY_FMT32: realcmd = cmd = VIDIOC_TRY_FMT; break;
};
switch(cmd) {
@@ -676,6 +678,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
compatible_arg = 0;
break;
+
case VIDIOCSFREQ:
#endif
case VIDIOC_S_INPUT:
@@ -683,7 +686,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case VIDIOC_STREAMON:
case VIDIOC_STREAMOFF:
err = get_user(karg.vx, (u32 __user *)up);
- compatible_arg = 0;
+ compatible_arg = 1;
break;
case VIDIOC_S_FBUF:
@@ -739,6 +742,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case VIDIOC_G_FBUF:
case VIDIOC_G_INPUT:
compatible_arg = 0;
+ break;
#ifdef CONFIG_VIDEO_V4L1_COMPAT
case VIDIOCSMICROCODE:
err = microcode32(&karg.vc, up);
@@ -755,7 +759,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
mm_segment_t old_fs = get_fs();
set_fs(KERNEL_DS);
- err = native_ioctl(file, realcmd, (unsigned long)&karg);
+ err = native_ioctl(file, realcmd, (unsigned long) &karg);
set_fs(old_fs);
}
if(err == 0) {
@@ -772,6 +776,7 @@ static int do_video_ioctl(struct file *file, unsigned int cmd, unsigned long arg
case VIDIOCGFBUF:
err = put_video_buffer32(&karg.vb, up);
break;
+
#endif
case VIDIOC_G_FBUF:
err = put_v4l2_framebuffer32(&karg.v2fb, up);
@@ -841,10 +846,14 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOCSFBUF32:
case VIDIOCGFREQ32:
case VIDIOCSFREQ32:
+ case VIDIOCGAUDIO:
+ case VIDIOCSAUDIO:
#endif
case VIDIOC_QUERYCAP:
case VIDIOC_ENUM_FMT:
case VIDIOC_G_FMT32:
+ case VIDIOC_CROPCAP:
+ case VIDIOC_S_CROP:
case VIDIOC_S_FMT32:
case VIDIOC_REQBUFS:
case VIDIOC_QUERYBUF32:
@@ -882,8 +891,6 @@ long v4l_compat_ioctl32(struct file *file, unsigned int cmd, unsigned long arg)
case VIDIOCSPICT:
case VIDIOCCAPTURE:
case VIDIOCKEY:
- case VIDIOCGAUDIO:
- case VIDIOCSAUDIO:
case VIDIOCSYNC:
case VIDIOCMCAPTURE:
case VIDIOCGMBUF:
diff --git a/drivers/media/video/cpia2/cpia2.h b/drivers/media/video/cpia2/cpia2.h
index c5ecb2be5f9..8d2dfc12882 100644
--- a/drivers/media/video/cpia2/cpia2.h
+++ b/drivers/media/video/cpia2/cpia2.h
@@ -50,10 +50,6 @@
/***
* Image defines
***/
-#ifndef true
-#define true 1
-#define false 0
-#endif
/* Misc constants */
#define ALLOW_CORRUPT 0 /* Causes collater to discard checksum */
diff --git a/drivers/media/video/cx2341x.c b/drivers/media/video/cx2341x.c
index 65f00fc08fa..657e0b96914 100644
--- a/drivers/media/video/cx2341x.c
+++ b/drivers/media/video/cx2341x.c
@@ -691,7 +691,7 @@ void cx2341x_fill_defaults(struct cx2341x_mpeg_params *p)
.video_luma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_LUMA_SPATIAL_FILTER_TYPE_1D_HOR,
.video_chroma_spatial_filter_type = V4L2_MPEG_CX2341X_VIDEO_CHROMA_SPATIAL_FILTER_TYPE_1D_HOR,
.video_temporal_filter_mode = V4L2_MPEG_CX2341X_VIDEO_TEMPORAL_FILTER_MODE_MANUAL,
- .video_temporal_filter = 0,
+ .video_temporal_filter = 8,
.video_median_filter_type = V4L2_MPEG_CX2341X_VIDEO_MEDIAN_FILTER_TYPE_OFF,
.video_luma_median_filter_top = 255,
.video_luma_median_filter_bottom = 0,
@@ -731,6 +731,7 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
};
int err = 0;
+ u16 temporal = new->video_temporal_filter;
cx2341x_api(priv, func, CX2341X_ENC_SET_OUTPUT_PORT, 2, new->port, 0);
@@ -741,6 +742,7 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
if (old == NULL || old->width != new->width || old->height != new->height ||
old->video_encoding != new->video_encoding) {
+ int is_scaling;
u16 w = new->width;
u16 h = new->height;
@@ -750,6 +752,20 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
}
err = cx2341x_api(priv, func, CX2341X_ENC_SET_FRAME_SIZE, 2, h, w);
if (err) return err;
+
+ /* Adjust temporal filter if necessary. The problem with the temporal
+ filter is that it works well with full resolution capturing, but
+ not when the capture window is scaled (the filter introduces
+ a ghosting effect). So if the capture window changed, and there is
+ no updated filter value, then the filter is set depending on whether
+ the new window is full resolution or not.
+
+ For full resolution a setting of 8 really improves the video
+ quality, especially if the original video quality is suboptimal. */
+ is_scaling = new->width != 720 || new->height != (new->is_50hz ? 576 : 480);
+ if (old && old->video_temporal_filter == temporal) {
+ temporal = is_scaling ? 0 : 8;
+ }
}
if (old == NULL || old->stream_type != new->stream_type) {
@@ -815,9 +831,9 @@ int cx2341x_update(void *priv, cx2341x_mbox_func func,
}
if (old == NULL ||
old->video_spatial_filter != new->video_spatial_filter ||
- old->video_temporal_filter != new->video_temporal_filter) {
+ old->video_temporal_filter != temporal) {
err = cx2341x_api(priv, func, CX2341X_ENC_SET_DNR_FILTER_PROPS, 2,
- new->video_spatial_filter, new->video_temporal_filter);
+ new->video_spatial_filter, temporal);
if (err) return err;
}
if (old == NULL || old->video_temporal_decimation != new->video_temporal_decimation) {
@@ -855,6 +871,9 @@ void cx2341x_log_status(struct cx2341x_mpeg_params *p, const char *prefix)
printk(KERN_INFO "%s: Stream: %s\n",
prefix,
cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_TYPE));
+ printk(KERN_INFO "%s: VBI Format: %s\n",
+ prefix,
+ cx2341x_menu_item(p, V4L2_CID_MPEG_STREAM_VBI_FMT));
/* Video */
printk(KERN_INFO "%s: Video: %dx%d, %d fps\n",
diff --git a/drivers/media/video/cx25840/Kconfig b/drivers/media/video/cx25840/Kconfig
index 854264e42ec..7cf29a03ed6 100644
--- a/drivers/media/video/cx25840/Kconfig
+++ b/drivers/media/video/cx25840/Kconfig
@@ -1,6 +1,6 @@
config VIDEO_CX25840
tristate "Conexant CX2584x audio/video decoders"
- depends on VIDEO_DEV && I2C && EXPERIMENTAL
+ depends on VIDEO_V4L2 && I2C && EXPERIMENTAL
select FW_LOADER
---help---
Support for the Conexant CX2584x audio/video decoders.
diff --git a/drivers/media/video/cx25840/cx25840-vbi.c b/drivers/media/video/cx25840/cx25840-vbi.c
index 6cc8bf215e8..48014a254e1 100644
--- a/drivers/media/video/cx25840/cx25840-vbi.c
+++ b/drivers/media/video/cx25840/cx25840-vbi.c
@@ -111,6 +111,10 @@ void cx25840_vbi_setup(struct i2c_client *client)
uv_lpf=0;
comb=0;
sc=0x0a425f;
+ } else if (std == V4L2_STD_PAL_Nc) {
+ uv_lpf=1;
+ comb=0x20;
+ sc=556453;
} else {
uv_lpf=1;
comb=0x20;
diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig
index 7a94e6a1192..51d68f32aa0 100644
--- a/drivers/media/video/cx88/Kconfig
+++ b/drivers/media/video/cx88/Kconfig
@@ -1,7 +1,3 @@
-config VIDEO_CX88_VP3054
- tristate
- depends on VIDEO_CX88_DVB && DVB_MT352
-
config VIDEO_CX88
tristate "Conexant 2388x (bt878 successor) support"
depends on VIDEO_DEV && PCI && I2C
@@ -52,6 +48,14 @@ config VIDEO_CX88_DVB
depends on VIDEO_CX88 && DVB_CORE
select VIDEO_BUF_DVB
select DVB_PLL
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_ZL10353 if !DVB_FE_CUSTOMISE
+ select DVB_OR51132 if !DVB_FE_CUSTOMISE
+ select DVB_CX22702 if !DVB_FE_CUSTOMISE
+ select DVB_LGDT330X if !DVB_FE_CUSTOMISE
+ select DVB_NXT200X if !DVB_FE_CUSTOMISE
+ select DVB_CX24123 if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB/ATSC cards based on the
Conexant 2388x chip.
@@ -59,101 +63,12 @@ config VIDEO_CX88_DVB
To compile this driver as a module, choose M here: the
module will be called cx88-dvb.
- You must also select one or more DVB/ATSC demodulators.
- If you are unsure which you need, choose all of them.
-
-config VIDEO_CX88_DVB_ALL_FRONTENDS
- bool "Build all supported frontends for cx2388x based TV cards"
- default y
- depends on VIDEO_CX88_DVB
- select DVB_MT352
- select VIDEO_CX88_VP3054
- select DVB_ZL10353
- select DVB_OR51132
- select DVB_CX22702
- select DVB_LGDT330X
- select DVB_NXT200X
- select DVB_CX24123
- select DVB_ISL6421
- ---help---
- This builds cx88-dvb with all currently supported frontend
- demodulators. If you wish to tweak your configuration, and
- only include support for the hardware that you need, choose N here.
-
- If you are unsure, choose Y.
-
-config VIDEO_CX88_DVB_MT352
- bool "Zarlink MT352 DVB-T Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_MT352
- ---help---
- This adds DVB-T support for cards based on the
- Connexant 2388x chip and the MT352 demodulator.
-
-config VIDEO_CX88_DVB_VP3054
- bool "VP-3054 Secondary I2C Bus Support"
- default y
- depends on VIDEO_CX88_DVB_MT352
- select VIDEO_CX88_VP3054
+config VIDEO_CX88_VP3054
+ tristate "VP-3054 Secondary I2C Bus Support"
+ default m
+ depends on VIDEO_CX88_DVB && DVB_MT352
---help---
This adds DVB-T support for cards based on the
Connexant 2388x chip and the MT352 demodulator,
which also require support for the VP-3054
Secondary I2C bus, such at DNTV Live! DVB-T Pro.
-
-config VIDEO_CX88_DVB_ZL10353
- bool "Zarlink ZL10353 DVB-T Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_ZL10353
- ---help---
- This adds DVB-T support for cards based on the
- Connexant 2388x chip and the ZL10353 demodulator,
- successor to the Zarlink MT352.
-
-config VIDEO_CX88_DVB_OR51132
- bool "OR51132 ATSC Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_OR51132
- ---help---
- This adds ATSC 8VSB and QAM64/256 support for cards based on the
- Connexant 2388x chip and the OR51132 demodulator.
-
-config VIDEO_CX88_DVB_CX22702
- bool "Conexant CX22702 DVB-T Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_CX22702
- ---help---
- This adds DVB-T support for cards based on the
- Connexant 2388x chip and the CX22702 demodulator.
-
-config VIDEO_CX88_DVB_LGDT330X
- bool "LG Electronics DT3302/DT3303 ATSC Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_LGDT330X
- ---help---
- This adds ATSC 8VSB and QAM64/256 support for cards based on the
- Connexant 2388x chip and the LGDT3302/LGDT3303 demodulator.
-
-config VIDEO_CX88_DVB_NXT200X
- bool "NXT2002/NXT2004 ATSC Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_NXT200X
- ---help---
- This adds ATSC 8VSB and QAM64/256 support for cards based on the
- Connexant 2388x chip and the NXT2002/NXT2004 demodulator.
-
-config VIDEO_CX88_DVB_CX24123
- bool "Conexant CX24123 DVB-S Support"
- default y
- depends on VIDEO_CX88_DVB && !VIDEO_CX88_DVB_ALL_FRONTENDS
- select DVB_CX24123
- select DVB_ISL6421
- ---help---
- This adds DVB-S support for cards based on the
- Connexant 2388x chip and the CX24123 demodulator.
diff --git a/drivers/media/video/cx88/Makefile b/drivers/media/video/cx88/Makefile
index 352b919f30c..639c3b659d0 100644
--- a/drivers/media/video/cx88/Makefile
+++ b/drivers/media/video/cx88/Makefile
@@ -14,13 +14,6 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1
-extra-cflags-$(CONFIG_DVB_CX22702) += -DHAVE_CX22702=1
-extra-cflags-$(CONFIG_DVB_OR51132) += -DHAVE_OR51132=1
-extra-cflags-$(CONFIG_DVB_LGDT330X) += -DHAVE_LGDT330X=1
-extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1
-extra-cflags-$(CONFIG_DVB_ZL10353) += -DHAVE_ZL10353=1
-extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1
-extra-cflags-$(CONFIG_DVB_CX24123) += -DHAVE_CX24123=1
extra-cflags-$(CONFIG_VIDEO_CX88_VP3054)+= -DHAVE_VP3054_I2C=1
EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/cx88/cx88-blackbird.c b/drivers/media/video/cx88/cx88-blackbird.c
index b60177f173c..a7921f9d45d 100644
--- a/drivers/media/video/cx88/cx88-blackbird.c
+++ b/drivers/media/video/cx88/cx88-blackbird.c
@@ -1160,8 +1160,10 @@ static struct pci_driver blackbird_pci_driver = {
.id_table = cx8802_pci_tbl,
.probe = blackbird_probe,
.remove = __devexit_p(blackbird_remove),
+#ifdef CONFIG_PM
.suspend = cx8802_suspend_common,
.resume = cx8802_resume_common,
+#endif
};
static int blackbird_init(void)
diff --git a/drivers/media/video/cx88/cx88-cards.c b/drivers/media/video/cx88/cx88-cards.c
index 14bd4863d15..6214eb823b2 100644
--- a/drivers/media/video/cx88/cx88-cards.c
+++ b/drivers/media/video/cx88/cx88-cards.c
@@ -1041,11 +1041,11 @@ struct cx88_board cx88_boards[] = {
.input = {{
.type = CX88_VMUX_COMPOSITE1,
.vmux = 1,
- .gpio0 = 0x000027df,
+ .gpio0 = 0x000067df,
},{
.type = CX88_VMUX_SVIDEO,
.vmux = 2,
- .gpio0 = 0x000027df,
+ .gpio0 = 0x000067df,
}},
.dvb = 1,
},
@@ -1209,6 +1209,100 @@ struct cx88_board cx88_boards[] = {
}},
.dvb = 1,
},
+ [CX88_BOARD_HAUPPAUGE_HVR3000] = {
+ /* FIXME: Add dvb & radio support */
+ .name = "Hauppauge WinTV-HVR3000 TriMode Analog/DVB-S/DVB-T",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x84bf,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x84bf,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x84bf,
+ }},
+ },
+ [CX88_BOARD_NORWOOD_MICRO] = {
+ .name = "Norwood Micro TV Tuner",
+ .tuner_type = TUNER_TNF_5335MF,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x0709,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x070b,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x070b,
+ }},
+ },
+ [CX88_BOARD_TE_DTV_250_OEM_SWANN] = {
+ .name = "Shenzhen Tungsten Ages Tech TE-DTV-250 / Swann OEM",
+ .tuner_type = TUNER_LG_PAL_NEW_TAPC,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0x003fffff,
+ .gpio1 = 0x00e00000,
+ .gpio2 = 0x003fffff,
+ .gpio3 = 0x02000000,
+ }},
+ },
+ [CX88_BOARD_HAUPPAUGE_HVR1300] = {
+ .name = "Hauppauge WinTV-HVR1300 DVB-T/Hybrid MPEG Encoder",
+ .tuner_type = TUNER_PHILIPS_FMD1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .input = {{
+ .type = CX88_VMUX_TELEVISION,
+ .vmux = 0,
+ .gpio0 = 0xe780,
+ },{
+ .type = CX88_VMUX_COMPOSITE1,
+ .vmux = 1,
+ .gpio0 = 0xe780,
+ },{
+ .type = CX88_VMUX_SVIDEO,
+ .vmux = 2,
+ .gpio0 = 0xe780,
+ }},
+ /* fixme: Add radio support */
+ .dvb = 1,
+ },
};
const unsigned int cx88_bcount = ARRAY_SIZE(cx88_boards);
@@ -1254,7 +1348,7 @@ struct cx88_subid cx88_subids[] = {
.card = CX88_BOARD_LEADTEK_PVR2000,
},{
.subvendor = 0x107d,
- .subdevice = 0x663C,
+ .subdevice = 0x663c,
.card = CX88_BOARD_LEADTEK_PVR2000,
},{
.subvendor = 0x1461,
@@ -1458,6 +1552,35 @@ struct cx88_subid cx88_subids[] = {
.subvendor = 0x14f1,
.subdevice = 0x0084,
.card = CX88_BOARD_GENIATECH_DVBS,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x1404,
+ .card = CX88_BOARD_HAUPPAUGE_HVR3000,
+ },{
+ .subvendor = 0x1461,
+ .subdevice = 0xc111, /* AverMedia M150-D */
+ /* This board is known to work with the ASUS PVR416 config */
+ .card = CX88_BOARD_ASUS_PVR_416,
+ },{
+ .subvendor = 0xc180,
+ .subdevice = 0xc980,
+ .card = CX88_BOARD_TE_DTV_250_OEM_SWANN,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9600,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9601,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x0070,
+ .subdevice = 0x9602,
+ .card = CX88_BOARD_HAUPPAUGE_HVR1300,
+ },{
+ .subvendor = 0x107d,
+ .subdevice = 0x6632,
+ .card = CX88_BOARD_LEADTEK_PVR2000,
},
};
const unsigned int cx88_idcount = ARRAY_SIZE(cx88_subids);
@@ -1501,6 +1624,7 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
/* Make sure we support the board model */
switch (tv.model)
{
+ case 14569: /* WinTV-HVR3000 (OEM, no IR, no back panel video) */
case 28552: /* WinTV-PVR 'Roslyn' (No IR) */
case 34519: /* WinTV-PCI-FM */
case 90002: /* Nova-T-PCI (9002) */
@@ -1512,6 +1636,11 @@ static void hauppauge_eeprom(struct cx88_core *core, u8 *eeprom_data)
case 92000: /* Nova-SE2 (OEM, No Video or IR) */
case 94009: /* WinTV-HVR1100 (Video and IR Retail) */
case 94501: /* WinTV-HVR1100 (Video and IR OEM) */
+ case 96009: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX) */
+ case 96019: /* WinTV-HVR1300 (PAL Video, MPEG Video and IR RX/TX) */
+ case 96559: /* WinTV-HVR1300 (PAL Video, MPEG Video no IR) */
+ case 96569: /* WinTV-HVR1300 () */
+ case 96659: /* WinTV-HVR1300 () */
case 98559: /* WinTV-HVR1100LP (Video no IR, Retail - Low Profile) */
/* known */
break;
@@ -1638,6 +1767,22 @@ void cx88_card_list(struct cx88_core *core, struct pci_dev *pci)
core->name, i, cx88_boards[i].name);
}
+void cx88_card_setup_pre_i2c(struct cx88_core *core)
+{
+ switch (core->board) {
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ /* Bring the 702 demod up before i2c scanning/attach or devices are hidden */
+ /* We leave here with the 702 on the bus */
+ cx_write(MO_GP0_IO, 0x0000e780);
+ udelay(1000);
+ cx_clear(MO_GP0_IO, 0x00000080);
+ udelay(50);
+ cx_set(MO_GP0_IO, 0x00000080); /* 702 out of reset */
+ udelay(1000);
+ break;
+ }
+}
+
void cx88_card_setup(struct cx88_core *core)
{
static u8 eeprom[256];
@@ -1666,6 +1811,8 @@ void cx88_card_setup(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_DVB_T1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
+ case CX88_BOARD_HAUPPAUGE_HVR3000:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
if (0 == core->i2c_rc)
hauppauge_eeprom(core,eeprom);
break;
@@ -1673,9 +1820,15 @@ void cx88_card_setup(struct cx88_core *core)
cx_write(MO_GP0_IO, 0x000007f8);
cx_write(MO_GP1_IO, 0x00000001);
break;
+ case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
+ /* GPIO0:6 is hooked to FX2 reset pin */
+ cx_set(MO_GP0_IO, 0x00004040);
+ cx_clear(MO_GP0_IO, 0x00000040);
+ msleep(1000);
+ cx_set(MO_GP0_IO, 0x00004040);
+ /* FALLTHROUGH */
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
- case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
/* GPIO0:0 is hooked to mt352 reset pin */
cx_set(MO_GP0_IO, 0x00000101);
diff --git a/drivers/media/video/cx88/cx88-core.c b/drivers/media/video/cx88/cx88-core.c
index 973d3f39b2d..f379ede3049 100644
--- a/drivers/media/video/cx88/cx88-core.c
+++ b/drivers/media/video/cx88/cx88-core.c
@@ -105,7 +105,7 @@ static u32* cx88_risc_field(u32 *rp, struct scatterlist *sglist,
*(rp++)=cpu_to_le32(sg_dma_address(sg)+offset);
offset+=bpl;
} else {
- /* scanline needs to be splitted */
+ /* scanline needs to be split */
todo = bpl;
*(rp++)=cpu_to_le32(RISC_WRITE|RISC_SOL|
(sg_dma_len(sg)-offset));
@@ -792,6 +792,11 @@ int cx88_start_audio_dma(struct cx88_core *core)
{
/* constant 128 made buzz in analog Nicam-stereo for bigger fifo_size */
int bpl = cx88_sram_channels[SRAM_CH25].fifo_size/4;
+
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
/* setup fifo + format */
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH25], bpl, 0);
cx88_sram_channel_setup(core, &cx88_sram_channels[SRAM_CH26], bpl, 0);
@@ -801,11 +806,16 @@ int cx88_start_audio_dma(struct cx88_core *core)
/* start dma */
cx_write(MO_AUD_DMACNTRL, 0x0003); /* Up and Down fifo enable */
+
return 0;
}
int cx88_stop_audio_dma(struct cx88_core *core)
{
+ /* If downstream RISC is enabled, bail out; ALSA is managing DMA */
+ if (cx_read(MO_AUD_DMACNTRL) & 0x10)
+ return 0;
+
/* stop dma */
cx_write(MO_AUD_DMACNTRL, 0x0000);
@@ -1123,6 +1133,7 @@ struct cx88_core* cx88_core_get(struct pci_dev *pci)
/* init hardware */
cx88_reset(core);
+ cx88_card_setup_pre_i2c(core);
cx88_i2c_init(core,pci);
cx88_call_i2c_clients (core, TUNER_SET_STANDBY, NULL);
cx88_card_setup(core);
diff --git a/drivers/media/video/cx88/cx88-dvb.c b/drivers/media/video/cx88/cx88-dvb.c
index afde3789d70..c87041dee21 100644
--- a/drivers/media/video/cx88/cx88-dvb.c
+++ b/drivers/media/video/cx88/cx88-dvb.c
@@ -33,32 +33,18 @@
#include "dvb-pll.h"
#include <media/v4l2-common.h>
-#ifdef HAVE_MT352
-# include "mt352.h"
-# include "mt352_priv.h"
-# ifdef HAVE_VP3054_I2C
-# include "cx88-vp3054-i2c.h"
-# endif
-#endif
-#ifdef HAVE_ZL10353
-# include "zl10353.h"
-#endif
-#ifdef HAVE_CX22702
-# include "cx22702.h"
-#endif
-#ifdef HAVE_OR51132
-# include "or51132.h"
-#endif
-#ifdef HAVE_LGDT330X
-# include "lgdt330x.h"
-# include "lg_h06xf.h"
-#endif
-#ifdef HAVE_NXT200X
-# include "nxt200x.h"
-#endif
-#ifdef HAVE_CX24123
-# include "cx24123.h"
+#include "mt352.h"
+#include "mt352_priv.h"
+#ifdef HAVE_VP3054_I2C
+# include "cx88-vp3054-i2c.h"
#endif
+#include "zl10353.h"
+#include "cx22702.h"
+#include "or51132.h"
+#include "lgdt330x.h"
+#include "lg_h06xf.h"
+#include "nxt200x.h"
+#include "cx24123.h"
#include "isl6421.h"
MODULE_DESCRIPTION("driver for cx2388x based DVB cards");
@@ -114,8 +100,6 @@ static struct videobuf_queue_ops dvb_qops = {
};
/* ------------------------------------------------------------------ */
-
-#ifdef HAVE_MT352
static int dvico_fusionhdtv_demod_init(struct dvb_frontend* fe)
{
static u8 clock_config [] = { CLOCK_CTL, 0x38, 0x39 };
@@ -181,7 +165,7 @@ static int dntv_live_dvbt_demod_init(struct dvb_frontend* fe)
}
static struct mt352_config dvico_fusionhdtv = {
- .demod_address = 0x0F,
+ .demod_address = 0x0f,
.demod_init = dvico_fusionhdtv_demod_init,
};
@@ -191,7 +175,7 @@ static struct mt352_config dntv_live_dvbt_config = {
};
static struct mt352_config dvico_fusionhdtv_dual = {
- .demod_address = 0x0F,
+ .demod_address = 0x0f,
.demod_init = dvico_dual_demod_init,
};
@@ -266,8 +250,8 @@ static int dntv_live_dvbt_pro_tuner_set_params(struct dvb_frontend* fe,
if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) {
printk(KERN_WARNING "cx88-dvb: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, dev->core->pll_addr, buf[0], err);
+ "(addr %02x <- %02x, err = %i)\n",
+ __FUNCTION__, dev->core->pll_addr, buf[0], err);
if (err < 0)
return err;
else
@@ -283,9 +267,7 @@ static struct mt352_config dntv_live_dvbt_pro_config = {
.demod_init = dntv_live_dvbt_pro_demod_init,
};
#endif
-#endif
-#ifdef HAVE_ZL10353
static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
struct dvb_frontend_parameters *params)
{
@@ -304,8 +286,8 @@ static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
fe->ops.i2c_gate_ctrl(fe, 1);
if ((err = i2c_transfer(&dev->core->i2c_adap, &msg, 1)) != 1) {
printk(KERN_WARNING "cx88-dvb: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, pllbuf[0], pllbuf[1], err);
+ "(addr %02x <- %02x, err = %i)\n",
+ __FUNCTION__, pllbuf[0], pllbuf[1], err);
if (err < 0)
return err;
else
@@ -316,16 +298,14 @@ static int dvico_hybrid_tuner_set_params(struct dvb_frontend *fe,
}
static struct zl10353_config dvico_fusionhdtv_hybrid = {
- .demod_address = 0x0F,
+ .demod_address = 0x0f,
.no_tuner = 1,
};
static struct zl10353_config dvico_fusionhdtv_plus_v1_1 = {
- .demod_address = 0x0F,
+ .demod_address = 0x0f,
};
-#endif
-#ifdef HAVE_CX22702
static struct cx22702_config connexant_refboard_config = {
.demod_address = 0x43,
.output_mode = CX22702_SERIAL_OUTPUT,
@@ -339,9 +319,11 @@ static struct cx22702_config hauppauge_hvr1100_config = {
.demod_address = 0x63,
.output_mode = CX22702_SERIAL_OUTPUT,
};
-#endif
+static struct cx22702_config hauppauge_hvr1300_config = {
+ .demod_address = 0x63,
+ .output_mode = CX22702_SERIAL_OUTPUT,
+};
-#ifdef HAVE_OR51132
static int or51132_set_ts_param(struct dvb_frontend* fe,
int is_punctured)
{
@@ -351,12 +333,10 @@ static int or51132_set_ts_param(struct dvb_frontend* fe,
}
static struct or51132_config pchdtv_hd3000 = {
- .demod_address = 0x15,
- .set_ts_params = or51132_set_ts_param,
+ .demod_address = 0x15,
+ .set_ts_params = or51132_set_ts_param,
};
-#endif
-#ifdef HAVE_LGDT330X
static int lgdt3302_tuner_set_params(struct dvb_frontend* fe,
struct dvb_frontend_parameters* params)
{
@@ -373,14 +353,14 @@ static int lgdt3302_tuner_set_params(struct dvb_frontend* fe,
dvb_pll_configure(core->pll_desc, buf, params->frequency, 0);
dprintk(1, "%s: tuner at 0x%02x bytes: 0x%02x 0x%02x 0x%02x 0x%02x\n",
- __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]);
+ __FUNCTION__, msg.addr, buf[0],buf[1],buf[2],buf[3]);
if (fe->ops.i2c_gate_ctrl)
fe->ops.i2c_gate_ctrl(fe, 1);
if ((err = i2c_transfer(&core->i2c_adap, &msg, 1)) != 1) {
printk(KERN_WARNING "cx88-dvb: %s error "
- "(addr %02x <- %02x, err = %i)\n",
- __FUNCTION__, buf[0], buf[1], err);
+ "(addr %02x <- %02x, err = %i)\n",
+ __FUNCTION__, buf[0], buf[1], err);
if (err < 0)
return err;
else
@@ -425,28 +405,26 @@ static int lgdt330x_set_ts_param(struct dvb_frontend* fe, int is_punctured)
}
static struct lgdt330x_config fusionhdtv_3_gold = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3302,
- .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3302,
+ .serial_mpeg = 0x04, /* TPSERIAL for 3302 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
};
static struct lgdt330x_config fusionhdtv_5_gold = {
- .demod_address = 0x0e,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
+ .demod_address = 0x0e,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
};
static struct lgdt330x_config pchdtv_hd5500 = {
- .demod_address = 0x59,
- .demod_chip = LGDT3303,
- .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
- .set_ts_params = lgdt330x_set_ts_param,
+ .demod_address = 0x59,
+ .demod_chip = LGDT3303,
+ .serial_mpeg = 0x40, /* TPSERIAL for 3303 in TOP_CONTROL */
+ .set_ts_params = lgdt330x_set_ts_param,
};
-#endif
-#ifdef HAVE_NXT200X
static int nxt200x_set_ts_param(struct dvb_frontend* fe,
int is_punctured)
{
@@ -465,28 +443,27 @@ static int nxt200x_set_pll_input(u8* buf, int input)
}
static struct nxt200x_config ati_hdtvwonder = {
- .demod_address = 0x0a,
- .set_pll_input = nxt200x_set_pll_input,
- .set_ts_params = nxt200x_set_ts_param,
+ .demod_address = 0x0a,
+ .set_pll_input = nxt200x_set_pll_input,
+ .set_ts_params = nxt200x_set_ts_param,
};
-#endif
-#ifdef HAVE_CX24123
static int cx24123_set_ts_param(struct dvb_frontend* fe,
int is_punctured)
{
struct cx8802_dev *dev= fe->dvb->priv;
- dev->ts_gen_cntrl = 0x2;
+ dev->ts_gen_cntrl = 0x02;
return 0;
}
-static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t voltage)
+static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe,
+ fe_sec_voltage_t voltage)
{
struct cx8802_dev *dev= fe->dvb->priv;
struct cx88_core *core = dev->core;
if (voltage == SEC_VOLTAGE_OFF) {
- cx_write(MO_GP0_IO, 0x000006fB);
+ cx_write(MO_GP0_IO, 0x000006fb);
} else {
cx_write(MO_GP0_IO, 0x000006f9);
}
@@ -496,7 +473,8 @@ static int kworld_dvbs_100_set_voltage(struct dvb_frontend* fe, fe_sec_voltage_t
return 0;
}
-static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t voltage)
+static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe,
+ fe_sec_voltage_t voltage)
{
struct cx8802_dev *dev= fe->dvb->priv;
struct cx88_core *core = dev->core;
@@ -512,20 +490,20 @@ static int geniatech_dvbs_set_voltage(struct dvb_frontend *fe, fe_sec_voltage_t
}
static struct cx24123_config geniatech_dvbs_config = {
- .demod_address = 0x55,
- .set_ts_params = cx24123_set_ts_param,
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
};
static struct cx24123_config hauppauge_novas_config = {
- .demod_address = 0x55,
- .set_ts_params = cx24123_set_ts_param,
+ .demod_address = 0x55,
+ .set_ts_params = cx24123_set_ts_param,
};
static struct cx24123_config kworld_dvbs_100_config = {
- .demod_address = 0x15,
- .set_ts_params = cx24123_set_ts_param,
+ .demod_address = 0x15,
+ .set_ts_params = cx24123_set_ts_param,
+ .lnb_polarity = 1,
};
-#endif
static int dvb_register(struct cx8802_dev *dev)
{
@@ -535,114 +513,114 @@ static int dvb_register(struct cx8802_dev *dev)
/* init frontend */
switch (dev->core->board) {
-#ifdef HAVE_CX22702
case CX88_BOARD_HAUPPAUGE_DVB_T1:
- dev->dvb.frontend = cx22702_attach(&hauppauge_novat_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_novat_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt759x);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_thomson_dtt759x);
}
break;
case CX88_BOARD_TERRATEC_CINERGY_1400_DVB_T1:
case CX88_BOARD_CONEXANT_DVB_T1:
case CX88_BOARD_KWORLD_DVB_T_CX22702:
case CX88_BOARD_WINFAST_DTV1000:
- dev->dvb.frontend = cx22702_attach(&connexant_refboard_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &connexant_refboard_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x60,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt7579);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60,
+ &dev->core->i2c_adap,
+ &dvb_pll_thomson_dtt7579);
}
break;
case CX88_BOARD_WINFAST_DTV2000H:
case CX88_BOARD_HAUPPAUGE_HVR1100:
case CX88_BOARD_HAUPPAUGE_HVR1100LP:
- dev->dvb.frontend = cx22702_attach(&hauppauge_hvr1100_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr1100_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_fmd1216me);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_fmd1216me);
+ }
+ break;
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
+ dev->dvb.frontend = dvb_attach(cx22702_attach,
+ &hauppauge_hvr1300_config,
+ &dev->core->i2c_adap);
+ if (dev->dvb.frontend != NULL) {
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_fmd1216me);
}
break;
-#endif
-#if defined(HAVE_MT352) || defined(HAVE_ZL10353)
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_PLUS:
-#ifdef HAVE_MT352
- dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x60,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt7579);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60,
+ NULL, &dvb_pll_thomson_dtt7579);
break;
}
-#endif
-#ifdef HAVE_ZL10353
/* ZL10353 replaces MT352 on later cards */
- dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x60,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt7579);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x60,
+ NULL, &dvb_pll_thomson_dtt7579);
}
-#endif
break;
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_DUAL:
-#ifdef HAVE_MT352
/* The tin box says DEE1601, but it seems to be DTT7579
* compatible, with a slightly different MT352 AGC gain. */
- dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv_dual,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv_dual,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt7579);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_thomson_dtt7579);
break;
}
-#endif
-#ifdef HAVE_ZL10353
/* ZL10353 replaces MT352 on later cards */
- dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_plus_v1_1,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_plus_v1_1,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt7579);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_thomson_dtt7579);
}
-#endif
break;
-#endif /* HAVE_MT352 || HAVE_ZL10353 */
-#ifdef HAVE_MT352
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T1:
- dev->dvb.frontend = mt352_attach(&dvico_fusionhdtv,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach,
+ &dvico_fusionhdtv,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_lg_z201);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_lg_z201);
}
break;
case CX88_BOARD_KWORLD_DVB_T:
case CX88_BOARD_DNTV_LIVE_DVB_T:
case CX88_BOARD_ADSTECH_DVB_T_PCI:
- dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach,
+ &dntv_live_dvbt_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_unknown_1);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_unknown_1);
}
break;
case CX88_BOARD_DNTV_LIVE_DVB_T_PRO:
#ifdef HAVE_VP3054_I2C
dev->core->pll_addr = 0x61;
dev->core->pll_desc = &dvb_pll_fmd1216me;
- dev->dvb.frontend = mt352_attach(&dntv_live_dvbt_pro_config,
+ dev->dvb.frontend = dvb_attach(mt352_attach, &dntv_live_dvbt_pro_config,
&((struct vp3054_i2c_state *)dev->card_priv)->adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = dntv_live_dvbt_pro_tuner_set_params;
@@ -651,30 +629,26 @@ static int dvb_register(struct cx8802_dev *dev)
printk("%s: built without vp3054 support\n", dev->core->name);
#endif
break;
-#endif
-#ifdef HAVE_ZL10353
case CX88_BOARD_DVICO_FUSIONHDTV_DVB_T_HYBRID:
dev->core->pll_addr = 0x61;
dev->core->pll_desc = &dvb_pll_thomson_fe6600;
- dev->dvb.frontend = zl10353_attach(&dvico_fusionhdtv_hybrid,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(zl10353_attach,
+ &dvico_fusionhdtv_hybrid,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = dvico_hybrid_tuner_set_params;
}
break;
-#endif
-#ifdef HAVE_OR51132
case CX88_BOARD_PCHDTV_HD3000:
- dev->dvb.frontend = or51132_attach(&pchdtv_hd3000,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(or51132_attach,
+ &pchdtv_hd3000,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_thomson_dtt761x);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ &dev->core->i2c_adap,
+ &dvb_pll_thomson_dtt761x);
}
break;
-#endif
-#ifdef HAVE_LGDT330X
case CX88_BOARD_DVICO_FUSIONHDTV_3_GOLD_Q:
dev->ts_gen_cntrl = 0x08;
{
@@ -690,8 +664,9 @@ static int dvb_register(struct cx8802_dev *dev)
fusionhdtv_3_gold.pll_rf_set = lgdt330x_pll_rf_set;
dev->core->pll_addr = 0x61;
dev->core->pll_desc = &dvb_pll_microtune_4042;
- dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
}
@@ -709,8 +684,9 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(200);
dev->core->pll_addr = 0x61;
dev->core->pll_desc = &dvb_pll_thomson_dtt761x;
- dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_3_gold,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_3_gold,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3302_tuner_set_params;
}
@@ -726,8 +702,9 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(100);
cx_set(MO_GP0_IO, 1);
mdelay(200);
- dev->dvb.frontend = lgdt330x_attach(&fusionhdtv_5_gold,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &fusionhdtv_5_gold,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
}
@@ -743,52 +720,51 @@ static int dvb_register(struct cx8802_dev *dev)
mdelay(100);
cx_set(MO_GP0_IO, 1);
mdelay(200);
- dev->dvb.frontend = lgdt330x_attach(&pchdtv_hd5500,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(lgdt330x_attach,
+ &pchdtv_hd5500,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
dev->dvb.frontend->ops.tuner_ops.set_params = lgdt3303_tuner_set_params;
}
}
break;
-#endif
-#ifdef HAVE_NXT200X
case CX88_BOARD_ATI_HDTVWONDER:
- dev->dvb.frontend = nxt200x_attach(&ati_hdtvwonder,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(nxt200x_attach,
+ &ati_hdtvwonder,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend != NULL) {
- dvb_pll_attach(dev->dvb.frontend, 0x61,
- &dev->core->i2c_adap,
- &dvb_pll_tuv1236d);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_tuv1236d);
}
break;
-#endif
-#ifdef HAVE_CX24123
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
- dev->dvb.frontend = cx24123_attach(&hauppauge_novas_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx24123_attach,
+ &hauppauge_novas_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend) {
- isl6421_attach(dev->dvb.frontend, &dev->core->i2c_adap,
- 0x08, 0x00, 0x00);
+ dvb_attach(isl6421_attach, dev->dvb.frontend,
+ &dev->core->i2c_adap, 0x08, 0x00, 0x00);
}
break;
case CX88_BOARD_KWORLD_DVBS_100:
- dev->dvb.frontend = cx24123_attach(&kworld_dvbs_100_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx24123_attach,
+ &kworld_dvbs_100_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend) {
dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
dev->dvb.frontend->ops.set_voltage = kworld_dvbs_100_set_voltage;
}
break;
case CX88_BOARD_GENIATECH_DVBS:
- dev->dvb.frontend = cx24123_attach(&geniatech_dvbs_config,
- &dev->core->i2c_adap);
+ dev->dvb.frontend = dvb_attach(cx24123_attach,
+ &geniatech_dvbs_config,
+ &dev->core->i2c_adap);
if (dev->dvb.frontend) {
dev->core->prev_set_voltage = dev->dvb.frontend->ops.set_voltage;
dev->dvb.frontend->ops.set_voltage = geniatech_dvbs_set_voltage;
}
break;
-#endif
default:
printk("%s: The frontend of your DVB/ATSC card isn't supported yet\n",
dev->core->name);
@@ -908,8 +884,10 @@ static struct pci_driver dvb_pci_driver = {
.id_table = cx8802_pci_tbl,
.probe = dvb_probe,
.remove = __devexit_p(dvb_remove),
+#ifdef CONFIG_PM
.suspend = cx8802_suspend_common,
.resume = cx8802_resume_common,
+#endif
};
static int dvb_init(void)
diff --git a/drivers/media/video/cx88/cx88-i2c.c b/drivers/media/video/cx88/cx88-i2c.c
index 70663805cc3..27b5dbb2ca1 100644
--- a/drivers/media/video/cx88/cx88-i2c.c
+++ b/drivers/media/video/cx88/cx88-i2c.c
@@ -7,6 +7,9 @@
(c) 2002 Yurij Sysoev <yurij@naturesoft.net>
(c) 1999-2003 Gerd Knorr <kraxel@bytesex.org>
+ (c) 2005 Mauro Carvalho Chehab <mchehab@infradead.org>
+ - Multituner support and i2c address binding
+
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
@@ -40,6 +43,11 @@ static unsigned int i2c_scan = 0;
module_param(i2c_scan, int, 0444);
MODULE_PARM_DESC(i2c_scan,"scan i2c bus at insmod time");
+static unsigned int i2c_udelay = 5;
+module_param(i2c_udelay, int, 0644);
+MODULE_PARM_DESC(i2c_udelay,"i2c delay at insmod time, in usecs "
+ "(should be 5 or higher). Lower value means higher bus speed.");
+
#define dprintk(level,fmt, arg...) if (i2c_debug >= level) \
printk(KERN_DEBUG "%s: " fmt, core->name , ## arg)
@@ -155,7 +163,6 @@ static struct i2c_algo_bit_data cx8800_i2c_algo_template = {
.getsda = cx8800_bit_getsda,
.getscl = cx8800_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
@@ -199,6 +206,11 @@ static void do_i2c_scan(char *name, struct i2c_client *c)
/* init + register i2c algo-bit adapter */
int cx88_i2c_init(struct cx88_core *core, struct pci_dev *pci)
{
+ /* Prevents usage of invalid delay values */
+ if (i2c_udelay<5)
+ i2c_udelay=5;
+ cx8800_i2c_algo_template.udelay=i2c_udelay;
+
memcpy(&core->i2c_adap, &cx8800_i2c_adap_template,
sizeof(core->i2c_adap));
memcpy(&core->i2c_algo, &cx8800_i2c_algo_template,
diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c
index c2556464899..83ebf7a3c05 100644
--- a/drivers/media/video/cx88/cx88-input.c
+++ b/drivers/media/video/cx88/cx88-input.c
@@ -107,7 +107,15 @@ static void cx88_ir_handle_key(struct cx88_IR *ir)
(gpio & ir->mask_keydown) ? " down" : "",
(gpio & ir->mask_keyup) ? " up" : "");
- if (ir->mask_keydown) {
+ if (ir->core->board == CX88_BOARD_NORWOOD_MICRO) {
+ u32 gpio_key = cx_read(MO_GP0_IO);
+
+ data = (data << 4) | ((gpio_key & 0xf0) >> 4);
+
+ ir_input_keydown(ir->input, &ir->ir, data, data);
+ ir_input_nokey(ir->input, &ir->ir);
+
+ } else if (ir->mask_keydown) {
/* bit set on keydown */
if (gpio & ir->mask_keydown) {
ir_input_keydown(ir->input, &ir->ir, data, data);
@@ -187,6 +195,7 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
ir_codes = ir_codes_hauppauge_new;
ir_type = IR_TYPE_RC5;
ir->sampling = 1;
@@ -248,6 +257,13 @@ int cx88_ir_init(struct cx88_core *core, struct pci_dev *pci)
ir_type = IR_TYPE_PD;
ir->sampling = 0xff00; /* address */
break;
+ case CX88_BOARD_NORWOOD_MICRO:
+ ir_codes = ir_codes_norwood;
+ ir->gpio_addr = MO_GP1_IO;
+ ir->mask_keycode = 0x0e;
+ ir->mask_keyup = 0x80;
+ ir->polling = 50; /* ms */
+ break;
case CX88_BOARD_NPGTECH_REALTV_TOP10FM:
ir_codes = ir_codes_npgtech;
ir->gpio_addr = MO_GP0_IO;
@@ -402,6 +418,7 @@ void cx88_ir_irq(struct cx88_core *core)
case CX88_BOARD_HAUPPAUGE_NOVASE2_S1:
case CX88_BOARD_HAUPPAUGE_NOVASPLUS_S1:
case CX88_BOARD_HAUPPAUGE_HVR1100:
+ case CX88_BOARD_HAUPPAUGE_HVR1300:
ircode = ir_decode_biphase(ir->samples, ir->scount, 5, 7);
ir_dprintk("biphase decoded: %x\n", ircode);
if ((ircode & 0xfffff000) != 0x3000)
diff --git a/drivers/media/video/cx88/cx88-tvaudio.c b/drivers/media/video/cx88/cx88-tvaudio.c
index 5785c348157..741e7c5e69e 100644
--- a/drivers/media/video/cx88/cx88-tvaudio.c
+++ b/drivers/media/video/cx88/cx88-tvaudio.c
@@ -52,7 +52,6 @@
#include <linux/init.h>
#include <linux/smp_lock.h>
#include <linux/delay.h>
-#include <linux/config.h>
#include <linux/kthread.h>
#include "cx88.h"
@@ -138,14 +137,10 @@ static void set_audio_finish(struct cx88_core *core, u32 ctl)
{
u32 volume;
-#ifndef CONFIG_VIDEO_CX88_ALSA
/* restart dma; This avoids buzz in NICAM and is good in others */
cx88_stop_audio_dma(core);
-#endif
cx_write(AUD_RATE_THRES_DMD, 0x000000C0);
-#ifndef CONFIG_VIDEO_CX88_ALSA
cx88_start_audio_dma(core);
-#endif
if (cx88_boards[core->board].blackbird) {
/* sets sound input from external adc */
diff --git a/drivers/media/video/cx88/cx88-video.c b/drivers/media/video/cx88/cx88-video.c
index 94c92bacc34..fbc79e9842a 100644
--- a/drivers/media/video/cx88/cx88-video.c
+++ b/drivers/media/video/cx88/cx88-video.c
@@ -497,6 +497,7 @@ static int start_video_dma(struct cx8800_dev *dev,
return 0;
}
+#ifdef CONFIG_PM
static int stop_video_dma(struct cx8800_dev *dev)
{
struct cx88_core *core = dev->core;
@@ -512,6 +513,7 @@ static int stop_video_dma(struct cx8800_dev *dev)
cx_clear(MO_VID_INTMSK, 0x0f0011);
return 0;
}
+#endif
static int restart_video_queue(struct cx8800_dev *dev,
struct cx88_dmaqueue *q)
@@ -2017,6 +2019,7 @@ static void __devexit cx8800_finidev(struct pci_dev *pci_dev)
kfree(dev);
}
+#ifdef CONFIG_PM
static int cx8800_suspend(struct pci_dev *pci_dev, pm_message_t state)
{
struct cx8800_dev *dev = pci_get_drvdata(pci_dev);
@@ -2092,6 +2095,7 @@ static int cx8800_resume(struct pci_dev *pci_dev)
return 0;
}
+#endif
/* ----------------------------------------------------------- */
@@ -2112,9 +2116,10 @@ static struct pci_driver cx8800_pci_driver = {
.id_table = cx8800_pci_tbl,
.probe = cx8800_initdev,
.remove = __devexit_p(cx8800_finidev),
-
+#ifdef CONFIG_PM
.suspend = cx8800_suspend,
.resume = cx8800_resume,
+#endif
};
static int cx8800_init(void)
diff --git a/drivers/media/video/cx88/cx88-vp3054-i2c.c b/drivers/media/video/cx88/cx88-vp3054-i2c.c
index 751a754a45e..2b4f1970c7d 100644
--- a/drivers/media/video/cx88/cx88-vp3054-i2c.c
+++ b/drivers/media/video/cx88/cx88-vp3054-i2c.c
@@ -100,7 +100,6 @@ static struct i2c_algo_bit_data vp3054_i2c_algo_template = {
.getsda = vp3054_bit_getsda,
.getscl = vp3054_bit_getscl,
.udelay = 16,
- .mdelay = 10,
.timeout = 200,
};
diff --git a/drivers/media/video/cx88/cx88.h b/drivers/media/video/cx88/cx88.h
index e7810955dd4..89f12e273b7 100644
--- a/drivers/media/video/cx88/cx88.h
+++ b/drivers/media/video/cx88/cx88.h
@@ -197,6 +197,10 @@ extern struct sram_channel cx88_sram_channels[];
#define CX88_BOARD_NPGTECH_REALTV_TOP10FM 50
#define CX88_BOARD_WINFAST_DTV2000H 51
#define CX88_BOARD_GENIATECH_DVBS 52
+#define CX88_BOARD_HAUPPAUGE_HVR3000 53
+#define CX88_BOARD_NORWOOD_MICRO 54
+#define CX88_BOARD_TE_DTV_250_OEM_SWANN 55
+#define CX88_BOARD_HAUPPAUGE_HVR1300 56
enum cx88_itype {
CX88_VMUX_COMPOSITE1 = 1,
@@ -545,6 +549,7 @@ extern const unsigned int cx88_idcount;
extern void cx88_card_list(struct cx88_core *core, struct pci_dev *pci);
extern void cx88_card_setup(struct cx88_core *core);
+extern void cx88_card_setup_pre_i2c(struct cx88_core *core);
/* ----------------------------------------------------------- */
/* cx88-tvaudio.c */
diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig
index dfb15bfb83d..9285a58e47a 100644
--- a/drivers/media/video/em28xx/Kconfig
+++ b/drivers/media/video/em28xx/Kconfig
@@ -5,7 +5,8 @@ config VIDEO_EM28XX
select VIDEO_TUNER
select VIDEO_TVEEPROM
select VIDEO_IR
- select VIDEO_SAA711X
+ select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO
+ select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO
---help---
This is a video4linux driver for Empia 28xx based TV cards.
diff --git a/drivers/media/video/em28xx/em28xx-video.c b/drivers/media/video/em28xx/em28xx-video.c
index 2a461dde480..20df657b70c 100644
--- a/drivers/media/video/em28xx/em28xx-video.c
+++ b/drivers/media/video/em28xx/em28xx-video.c
@@ -174,7 +174,7 @@ static void em28xx_config_i2c(struct em28xx *dev)
route.input = INPUT(dev->ctl_input)->vmux;
route.output = 0;
- em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, NULL);
+ em28xx_i2c_call_clients(dev, VIDIOC_INT_RESET, 0);
em28xx_i2c_call_clients(dev, VIDIOC_INT_S_VIDEO_ROUTING, &route);
em28xx_i2c_call_clients(dev, VIDIOC_STREAMON, NULL);
diff --git a/drivers/media/video/ks0127.c b/drivers/media/video/ks0127.c
index 3bf7ac4f528..c1a377f797d 100644
--- a/drivers/media/video/ks0127.c
+++ b/drivers/media/video/ks0127.c
@@ -832,8 +832,7 @@ static int ks0127_detach(struct i2c_client *client)
static int __devinit ks0127_init_module(void)
{
init_reg_defaults();
- i2c_add_driver(&i2c_driver_ks0127);
- return 0;
+ return i2c_add_driver(&i2c_driver_ks0127);
}
static void __devexit ks0127_cleanup_module(void)
diff --git a/drivers/media/video/ov511.c b/drivers/media/video/ov511.c
index 1b07a61c2eb..5d8cd283fcd 100644
--- a/drivers/media/video/ov511.c
+++ b/drivers/media/video/ov511.c
@@ -301,10 +301,11 @@ static struct symbolic_list senlist[] = {
static struct symbolic_list urb_errlist[] = {
{ -ENOSR, "Buffer error (overrun)" },
{ -EPIPE, "Stalled (device not responding)" },
- { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EOVERFLOW, "Babble (device sends too much data)" },
{ -EPROTO, "Bit-stuff error (bad cable?)" },
- { -EILSEQ, "CRC/Timeout" },
- { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -EILSEQ, "CRC/Timeout (bad cable?)" },
+ { -ETIME, "Device does not respond to token" },
+ { -ETIMEDOUT, "Device does not respond to command" },
{ -1, NULL }
};
diff --git a/drivers/media/video/pvrusb2/Kconfig b/drivers/media/video/pvrusb2/Kconfig
index 7e727fe14b3..a52171ef613 100644
--- a/drivers/media/video/pvrusb2/Kconfig
+++ b/drivers/media/video/pvrusb2/Kconfig
@@ -5,8 +5,6 @@ config VIDEO_PVRUSB2
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.
@@ -14,6 +12,20 @@ config VIDEO_PVRUSB2
To compile this driver as a module, choose M here: the
module will be called pvrusb2
+config VIDEO_PVRUSB2_29XXX
+ bool "Hauppauge WinTV-PVR USB2 support for 29xxx model series"
+ depends on VIDEO_PVRUSB2 && EXPERIMENTAL
+ select VIDEO_SAA711X
+ select VIDEO_MSP3400
+ ---help---
+ This option enables support for WinTV-PVR USB2 devices whose
+ model number is of the form "29xxx" (leading prefix of "29"
+ followed by 3 digits).
+ To see if you may need this option, examine the white
+ sticker on the underside of your device.
+
+ If you are in doubt, say Y.
+
config VIDEO_PVRUSB2_24XXX
bool "Hauppauge WinTV-PVR USB2 support for 24xxx model series"
depends on VIDEO_PVRUSB2 && EXPERIMENTAL
@@ -60,3 +72,5 @@ config VIDEO_PVRUSB2_DEBUGIFC
You do not need to select this option unless you plan
on debugging the driver or performing a manual firmware
extraction.
+
+ If you are in doubt, say N.
diff --git a/drivers/media/video/pvrusb2/Makefile b/drivers/media/video/pvrusb2/Makefile
index 02e414210da..69b3e43cd0e 100644
--- a/drivers/media/video/pvrusb2/Makefile
+++ b/drivers/media/video/pvrusb2/Makefile
@@ -1,10 +1,6 @@
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 \
@@ -12,7 +8,7 @@ pvrusb2-objs := pvrusb2-i2c-core.o pvrusb2-i2c-cmd-v4l2.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) \
+ pvrusb2-cx2584x-v4l.o pvrusb2-wm8775.o \
$(obj-pvrusb2-sysfs-y) $(obj-pvrusb2-debugifc-y)
obj-$(CONFIG_VIDEO_PVRUSB2) += pvrusb2.o
diff --git a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
index fb6198f1df9..c77de859cc8 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-ctrl.c
@@ -43,12 +43,17 @@ int pvr2_ctrl_set_mask_value(struct pvr2_ctrl *cptr,int mask,int val)
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;
+ int lim;
+ lim = cptr->info->def.type_int.min_value;
+ if (cptr->info->get_min_value) {
+ cptr->info->get_min_value(cptr,&lim);
}
- if (val > cptr->info->def.type_int.max_value) {
- break;
+ if (val < lim) break;
+ lim = cptr->info->def.type_int.max_value;
+ if (cptr->info->get_max_value) {
+ cptr->info->get_max_value(cptr,&lim);
}
+ if (val > lim) break;
} else if (cptr->info->type == pvr2_ctl_enum) {
if (val >= cptr->info->def.type_enum.count) {
break;
@@ -91,7 +96,9 @@ 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) {
+ if (cptr->info->get_max_value) {
+ cptr->info->get_max_value(cptr,&ret);
+ } else if (cptr->info->type == pvr2_ctl_int) {
ret = cptr->info->def.type_int.max_value;
}
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
@@ -105,7 +112,9 @@ 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) {
+ if (cptr->info->get_min_value) {
+ cptr->info->get_min_value(cptr,&ret);
+ } else if (cptr->info->type == pvr2_ctl_int) {
ret = cptr->info->def.type_int.min_value;
}
} while(0); LOCK_GIVE(cptr->hdw->big_lock);
diff --git a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
index c80c26be6e4..df8feac16ae 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-cx2584x-v4l.c
@@ -221,7 +221,7 @@ static unsigned int decoder_describe(struct pvr2_v4l_cx2584x *ctxt,
static void decoder_reset(struct pvr2_v4l_cx2584x *ctxt)
{
int ret;
- ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,NULL);
+ ret = pvr2_i2c_client_cmd(ctxt->client,VIDIOC_INT_RESET,0);
pvr2_trace(PVR2_TRACE_CHIPS,"i2c cx25840 decoder_reset (ret=%d)",ret);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-encoder.c b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
index 18a7073501c..c94f97b7939 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-encoder.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-encoder.c
@@ -317,7 +317,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Failed to configure cx32416");
+ "Failed to configure cx23416");
return ret;
}
@@ -337,7 +337,7 @@ int pvr2_encoder_configure(struct pvr2_hdw *hdw)
if (ret) {
pvr2_trace(PVR2_TRACE_ERROR_LEGS,
- "Failed to initialize cx32416 video input");
+ "Failed to initialize cx23416 video input");
return ret;
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
index 0d6dc33ca32..34b08fbcc6e 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw-internal.h
@@ -33,7 +33,6 @@
*/
-#include <linux/config.h>
#include <linux/videodev2.h>
#include <linux/i2c.h>
#include <linux/mutex.h>
@@ -41,32 +40,6 @@
#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
@@ -107,6 +80,8 @@ struct pvr2_ctl_info {
/* Control's implementation */
pvr2_ctlf_get_value get_value; /* Get its value */
+ pvr2_ctlf_get_value get_min_value; /* Get minimum allowed value */
+ pvr2_ctlf_get_value get_max_value; /* Get maximum allowed 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 */
@@ -193,9 +168,7 @@ struct pvr2_decoder_ctrl {
/* 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
diff --git a/drivers/media/video/pvrusb2/pvrusb2-hdw.c b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
index be1e5cc7808..88604365777 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-hdw.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-hdw.c
@@ -38,9 +38,7 @@
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
{ }
};
@@ -48,9 +46,7 @@ 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 {
@@ -58,14 +54,12 @@ struct pvr2_string_table {
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",
"wm8775",
};
-#endif
// Names of other client modules to request for 29xxx model hardware
static const char *pvr2_client_29xxx[] = {
@@ -79,12 +73,10 @@ static struct pvr2_string_table pvr2_client_lists[] = {
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 ] = NULL};
@@ -221,14 +213,15 @@ static const struct pvr2_mpeg_ids mpeg_ids[] = {
};
#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",
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_44100] = "44.1 kHz",
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000] = "48 kHz",
+ [V4L2_MPEG_AUDIO_SAMPLING_FREQ_32000] = "32 kHz",
};
-
static const char *control_values_input[] = {
[PVR2_CVAL_INPUT_TV] = "television", /*xawtv needs this name*/
[PVR2_CVAL_INPUT_RADIO] = "radio",
@@ -362,6 +355,50 @@ static int ctrl_freq_set(struct pvr2_ctrl *cptr,int m,int v)
return 0;
}
+static int ctrl_hres_max_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* If we're dealing with a 24xxx device, force the horizontal
+ maximum to be 720 no matter what, since we can't get the device
+ to work properly with any other value. Otherwise just return
+ the normal value. */
+ *vp = cptr->info->def.type_int.max_value;
+ if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
+ return 0;
+}
+
+static int ctrl_hres_min_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* If we're dealing with a 24xxx device, force the horizontal
+ minimum to be 720 no matter what, since we can't get the device
+ to work properly with any other value. Otherwise just return
+ the normal value. */
+ *vp = cptr->info->def.type_int.min_value;
+ if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) *vp = 720;
+ return 0;
+}
+
+static int ctrl_vres_max_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* Actual maximum depends on the video standard in effect. */
+ if (cptr->hdw->std_mask_cur & V4L2_STD_525_60) {
+ *vp = 480;
+ } else {
+ *vp = 576;
+ }
+ return 0;
+}
+
+static int ctrl_vres_min_get(struct pvr2_ctrl *cptr,int *vp)
+{
+ /* Actual minimum depends on device type. */
+ if (cptr->hdw->hdw_type == PVR2_HDW_TYPE_24XXX) {
+ *vp = 75;
+ } else {
+ *vp = 17;
+ }
+ return 0;
+}
+
static int ctrl_cx2341x_is_dirty(struct pvr2_ctrl *cptr)
{
return cptr->hdw->enc_stale != 0;
@@ -719,19 +756,27 @@ static const struct pvr2_ctl_info control_defs[] = {
.internal_id = PVR2_CID_HRES,
.default_value = 720,
DEFREF(res_hor),
- DEFINT(320,720),
+ DEFINT(19,720),
+ /* Hook in check for clamp on horizontal resolution in
+ order to avoid unsolved problem involving cx25840. */
+ .get_max_value = ctrl_hres_max_get,
+ .get_min_value = ctrl_hres_min_get,
},{
.desc = "Vertical capture resolution",
.name = "resolution_ver",
.internal_id = PVR2_CID_VRES,
.default_value = 480,
DEFREF(res_ver),
- DEFINT(200,625),
+ DEFINT(17,576),
+ /* Hook in check for video standard and adjust maximum
+ depending on the standard. */
+ .get_max_value = ctrl_vres_max_get,
+ .get_min_value = ctrl_vres_min_get,
},{
.v4l_id = V4L2_CID_MPEG_AUDIO_SAMPLING_FREQ,
- .desc = "Sample rate",
+ .default_value = V4L2_MPEG_AUDIO_SAMPLING_FREQ_48000,
+ .desc = "Audio Sampling Frequency",
.name = "srate",
- .default_value = PVR2_CVAL_SRATE_48,
DEFREF(srate),
DEFENUM(control_values_srate),
},{
@@ -935,22 +980,18 @@ static int pvr2_upload_firmware1(struct pvr2_hdw *hdw)
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
@@ -2237,11 +2278,14 @@ static int pvr2_hdw_commit_ctl_internal(struct pvr2_hdw *hdw)
}
if (hdw->std_dirty ||
+ hdw->enc_stale ||
+ hdw->srate_dirty ||
+ hdw->res_ver_dirty ||
+ hdw->res_hor_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) {
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
index fbe6039aeb6..ed3e8105292 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-chips-v4l2.c
@@ -26,10 +26,8 @@
#include "pvrusb2-audio.h"
#include "pvrusb2-tuner.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__)
@@ -71,7 +69,6 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
return;
}
}
-#ifdef CONFIG_VIDEO_PVRUSB2_24XXX
if (id == I2C_DRIVERID_CX25840) {
if (pvr2_i2c_cx2584x_v4l_setup(hdw,cp)) {
return;
@@ -82,7 +79,6 @@ void pvr2_i2c_probe(struct pvr2_hdw *hdw,struct pvr2_i2c_client *cp)
return;
}
}
-#endif
if (id == I2C_DRIVERID_SAA711X) {
if (pvr2_i2c_decoder_v4l_setup(hdw,cp)) {
return;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
index 8a9933dec91..05ea17afe90 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-cmd-v4l2.c
@@ -31,7 +31,7 @@ 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);
+ "i2c v4l2 set_standard(0x%llx)",(long long unsigned)vs);
pvr2_i2c_core_cmd(hdw,VIDIOC_S_STD,&vs);
}
diff --git a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
index 7fca4798227..3b9012f8e38 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-i2c-core.c
@@ -185,8 +185,6 @@ static int pvr2_i2c_basic_op(struct pvr2_hdw *hdw,
}
}
-#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
@@ -289,8 +287,6 @@ static int i2c_hack_cx25840(struct pvr2_hdw *hdw,
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,
@@ -897,14 +893,12 @@ void pvr2_i2c_core_init(struct pvr2_hdw *hdw)
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));
diff --git a/drivers/media/video/pvrusb2/pvrusb2-main.c b/drivers/media/video/pvrusb2/pvrusb2-main.c
index 8f1a5afdd34..e976c484c05 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-main.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-main.c
@@ -20,7 +20,6 @@
*
*/
-#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/slab.h>
diff --git a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
index d1dda5caf40..c294f46db9b 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-sysfs.c
@@ -19,7 +19,6 @@
*
*/
-#include <linux/config.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <asm/semaphore.h>
@@ -40,8 +39,6 @@ struct pvr2_sysfs {
#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;
int v4l_minor_number_created_ok;
diff --git a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
index 0caf70b8c0d..3608c2f81df 100644
--- a/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
+++ b/drivers/media/video/pvrusb2/pvrusb2-v4l2.c
@@ -459,18 +459,26 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
ret = 0;
switch(vf->type) {
case V4L2_BUF_TYPE_VIDEO_CAPTURE: {
+ int lmin,lmax;
+ struct pvr2_ctrl *hcp,*vcp;
int h = vf->fmt.pix.height;
int w = vf->fmt.pix.width;
-
- if (h < 200) {
- h = 200;
- } else if (h > 625) {
- h = 625;
+ hcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_HRES);
+ vcp = pvr2_hdw_get_ctrl_by_id(hdw,PVR2_CID_VRES);
+
+ lmin = pvr2_ctrl_get_min(hcp);
+ lmax = pvr2_ctrl_get_max(hcp);
+ if (w < lmin) {
+ w = lmin;
+ } else if (w > lmax) {
+ w = lmax;
}
- if (w < 320) {
- w = 320;
- } else if (w > 720) {
- w = 720;
+ lmin = pvr2_ctrl_get_min(vcp);
+ lmax = pvr2_ctrl_get_max(vcp);
+ if (h < lmin) {
+ h = lmin;
+ } else if (h > lmax) {
+ h = lmax;
}
memcpy(vf, &pvr_format[PVR_FORMAT_PIX],
@@ -479,14 +487,8 @@ static int pvr2_v4l2_do_ioctl(struct inode *inode, struct file *file,
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);
+ pvr2_ctrl_set_value(hcp,vf->fmt.pix.width);
+ pvr2_ctrl_set_value(vcp,vf->fmt.pix.height);
}
} break;
case V4L2_BUF_TYPE_VBI_CAPTURE:
diff --git a/drivers/media/video/pwc/pwc-if.c b/drivers/media/video/pwc/pwc-if.c
index d4703944df9..53c4b5790d5 100644
--- a/drivers/media/video/pwc/pwc-if.c
+++ b/drivers/media/video/pwc/pwc-if.c
@@ -711,7 +711,7 @@ static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)
case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break;
case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break;
case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break;
- case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break;
+ case -ETIME: errmsg = "Device does not respond"; break;
}
PWC_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg);
/* Give up after a number of contiguous errors on the USB bus.
diff --git a/drivers/media/video/saa5246a.c b/drivers/media/video/saa5246a.c
index 59a187272c8..77bb940a1a4 100644
--- a/drivers/media/video/saa5246a.c
+++ b/drivers/media/video/saa5246a.c
@@ -830,7 +830,6 @@ static struct video_device saa_template =
.owner = THIS_MODULE,
.name = IF_NAME,
.type = VID_TYPE_TELETEXT,
- .hardware = VID_HARDWARE_SAA5249,
.fops = &saa_fops,
.release = video_device_release,
.minor = -1,
diff --git a/drivers/media/video/saa5249.c b/drivers/media/video/saa5249.c
index 19a8d65699f..bb3fb4387f6 100644
--- a/drivers/media/video/saa5249.c
+++ b/drivers/media/video/saa5249.c
@@ -713,7 +713,6 @@ static struct video_device saa_template =
.owner = THIS_MODULE,
.name = IF_NAME,
.type = VID_TYPE_TELETEXT, /*| VID_TYPE_TUNER ?? */
- .hardware = VID_HARDWARE_SAA5249,
.fops = &saa_fops,
};
diff --git a/drivers/media/video/saa7115.c b/drivers/media/video/saa7115.c
index b59c1171727..974179d4d38 100644
--- a/drivers/media/video/saa7115.c
+++ b/drivers/media/video/saa7115.c
@@ -1,4 +1,6 @@
-/* saa7115 - Philips SAA7113/SAA7114/SAA7115 video decoder driver
+/* saa711x - Philips SAA711x video decoder driver
+ * This driver can work with saa7111, saa7111a, saa7113, saa7114,
+ * saa7115 and saa7118.
*
* Based on saa7114 driver by Maxim Yevtyushkin, which is based on
* the saa7111 driver by Dave Perks.
@@ -16,7 +18,9 @@
* (2/17/2003)
*
* VBI support (2004) and cleanups (2005) by Hans Verkuil <hverkuil@xs4all.nl>
- * SAA7113 support by Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * Copyright (c) 2005-2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ * SAA7111, SAA7113 and SAA7118 support
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
@@ -33,6 +37,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
+#include "saa711x_regs.h"
#include <linux/kernel.h>
#include <linux/module.h>
@@ -43,7 +48,9 @@
#include <media/saa7115.h>
#include <asm/div64.h>
-MODULE_DESCRIPTION("Philips SAA7113/SAA7114/SAA7115 video decoder driver");
+#define VRES_60HZ (480+16)
+
+MODULE_DESCRIPTION("Philips SAA7111/SAA7113/SAA7114/SAA7115/SAA7118 video decoder driver");
MODULE_AUTHOR( "Maxim Yevtyushkin, Kevin Thayer, Chris Kennedy, "
"Hans Verkuil, Mauro Carvalho Chehab");
MODULE_LICENSE("GPL");
@@ -54,14 +61,14 @@ module_param(debug, bool, 0644);
MODULE_PARM_DESC(debug, "Debug level (0-1)");
static unsigned short normal_i2c[] = {
- 0x4a >> 1, 0x48 >> 1, /* SAA7113 */
- 0x42 >> 1, 0x40 >> 1, /* SAA7114 and SAA7115 */
+ 0x4a >> 1, 0x48 >> 1, /* SAA7111, SAA7111A and SAA7113 */
+ 0x42 >> 1, 0x40 >> 1, /* SAA7114, SAA7115 and SAA7118 */
I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
-struct saa7115_state {
+struct saa711x_state {
v4l2_std_id std;
int input;
int enable;
@@ -70,6 +77,8 @@ struct saa7115_state {
int contrast;
int hue;
int sat;
+ int width;
+ int height;
enum v4l2_chip_ident ident;
u32 audclk_freq;
u32 crystal_freq;
@@ -80,420 +89,508 @@ struct saa7115_state {
/* ----------------------------------------------------------------------- */
-static inline int saa7115_write(struct i2c_client *client, u8 reg, u8 value)
+static inline int saa711x_write(struct i2c_client *client, u8 reg, u8 value)
{
return i2c_smbus_write_byte_data(client, reg, value);
}
-static int saa7115_writeregs(struct i2c_client *client, const unsigned char *regs)
+/* Sanity routine to check if a register is present */
+static int saa711x_has_reg(const int id, const u8 reg)
{
+ if (id == V4L2_IDENT_SAA7111)
+ return reg < 0x20 && reg != 0x01 && reg != 0x0f &&
+ (reg < 0x13 || reg > 0x19) && reg != 0x1d && reg != 0x1e;
+
+ /* common for saa7113/4/5/8 */
+ if (unlikely((reg >= 0x3b && reg <= 0x3f) || reg == 0x5c || reg == 0x5f ||
+ reg == 0xa3 || reg == 0xa7 || reg == 0xab || reg == 0xaf || (reg >= 0xb5 && reg <= 0xb7) ||
+ reg == 0xd3 || reg == 0xd7 || reg == 0xdb || reg == 0xdf || (reg >= 0xe5 && reg <= 0xe7) ||
+ reg == 0x82 || (reg >= 0x89 && reg <= 0x8e)))
+ return 0;
+
+ switch (id) {
+ case V4L2_IDENT_SAA7113:
+ return reg != 0x14 && (reg < 0x18 || reg > 0x1e) && (reg < 0x20 || reg > 0x3f) &&
+ reg != 0x5d && reg < 0x63;
+ case V4L2_IDENT_SAA7114:
+ return (reg < 0x1a || reg > 0x1e) && (reg < 0x20 || reg > 0x2f) &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x33 && reg != 0x37 &&
+ reg != 0x81 && reg < 0xf0;
+ case V4L2_IDENT_SAA7115:
+ return (reg < 0x20 || reg > 0x2f) && reg != 0x65 && (reg < 0xfc || reg > 0xfe);
+ case V4L2_IDENT_SAA7118:
+ return (reg < 0x1a || reg > 0x1d) && (reg < 0x20 || reg > 0x22) &&
+ (reg < 0x26 || reg > 0x28) && reg != 0x33 && reg != 0x37 &&
+ (reg < 0x63 || reg > 0x7f) && reg != 0x81 && reg < 0xf0;
+ }
+ return 1;
+}
+
+static int saa711x_writeregs(struct i2c_client *client, const unsigned char *regs)
+{
+ struct saa711x_state *state = i2c_get_clientdata(client);
unsigned char reg, data;
while (*regs != 0x00) {
reg = *(regs++);
data = *(regs++);
- if (saa7115_write(client, reg, data) < 0)
- return -1;
+
+ /* According with datasheets, reserved regs should be
+ filled with 0 - seems better not to touch on they */
+ if (saa711x_has_reg(state->ident,reg)) {
+ if (saa711x_write(client, reg, data) < 0)
+ return -1;
+ } else {
+ v4l_dbg(1, debug, client, "tried to access reserved reg 0x%02x\n", reg);
+ }
}
return 0;
}
-static inline int saa7115_read(struct i2c_client *client, u8 reg)
+static inline int saa711x_read(struct i2c_client *client, u8 reg)
{
return i2c_smbus_read_byte_data(client, reg);
}
/* ----------------------------------------------------------------------- */
+/* SAA7111 initialization table */
+static const unsigned char saa7111_init[] = {
+ R_01_INC_DELAY, 0x00, /* reserved */
+
+ /*front end */
+ R_02_INPUT_CNTL_1, 0xd0, /* FUSE=3, GUDL=2, MODE=0 */
+ R_03_INPUT_CNTL_2, 0x23, /* HLNRS=0, VBSL=1, WPOFF=0, HOLDG=0,
+ * GAFIX=0, GAI1=256, GAI2=256 */
+ R_04_INPUT_CNTL_3, 0x00, /* GAI1=256 */
+ R_05_INPUT_CNTL_4, 0x00, /* GAI2=256 */
+
+ /* decoder */
+ R_06_H_SYNC_START, 0xf3, /* HSB at 13(50Hz) / 17(60Hz)
+ * pixels after end of last line */
+ R_07_H_SYNC_STOP, 0xe8, /* HSS seems to be needed to
+ * work with NTSC, too */
+ R_08_SYNC_CNTL, 0xc8, /* AUFD=1, FSEL=1, EXFIL=0,
+ * VTRC=1, HPLL=0, VNOI=0 */
+ R_09_LUMA_CNTL, 0x01, /* BYPS=0, PREF=0, BPSS=0,
+ * VBLB=0, UPTCV=0, APER=1 */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47, /* 0b - CONT=1.109 */
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01, /* 0e - CDTO=0, CSTD=0, DCCF=0,
+ * FCTC=0, CHBW=1 */
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* reserved */
+ R_10_CHROMA_CNTL_2, 0x48, /* 10 - OFTS=1, HDEL=0, VRLN=1, YDEL=0 */
+ R_11_MODE_DELAY_CNTL, 0x1c, /* 11 - GPSW=0, CM99=0, FECO=0, COMPO=1,
+ * OEYC=1, OEHV=1, VIPB=0, COLO=0 */
+ R_12_RT_SIGNAL_CNTL, 0x00, /* 12 - output control 2 */
+ R_13_RT_X_PORT_OUT_CNTL, 0x00, /* 13 - output control 3 */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
+/* SAA7113 init codes */
+static const unsigned char saa7113_init[] = {
+ R_01_INC_DELAY, 0x08,
+ R_02_INPUT_CNTL_1, 0xc2,
+ R_03_INPUT_CNTL_2, 0x30,
+ R_04_INPUT_CNTL_3, 0x00,
+ R_05_INPUT_CNTL_4, 0x00,
+ R_06_H_SYNC_START, 0x89,
+ R_07_H_SYNC_STOP, 0x0d,
+ R_08_SYNC_CNTL, 0x88,
+ R_09_LUMA_CNTL, 0x01,
+ R_0A_LUMA_BRIGHT_CNTL, 0x80,
+ R_0B_LUMA_CONTRAST_CNTL, 0x47,
+ R_0C_CHROMA_SAT_CNTL, 0x40,
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0E_CHROMA_CNTL_1, 0x01,
+ R_0F_CHROMA_GAIN_CNTL, 0x2a,
+ R_10_CHROMA_CNTL_2, 0x08,
+ R_11_MODE_DELAY_CNTL, 0x0c,
+ R_12_RT_SIGNAL_CNTL, 0x07,
+ R_13_RT_X_PORT_OUT_CNTL, 0x00,
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_15_VGATE_START_FID_CHG, 0x00,
+ R_16_VGATE_STOP, 0x00,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x00,
+
+ 0x00, 0x00
+};
+
/* If a value differs from the Hauppauge driver values, then the comment starts with
'was 0xXX' to denote the Hauppauge value. Otherwise the value is identical to what the
Hauppauge driver sets. */
+/* SAA7114 and SAA7115 initialization table */
static const unsigned char saa7115_init_auto_input[] = {
/* Front-End Part */
- 0x01, 0x48, /* white peak control disabled */
- 0x03, 0x20, /* was 0x30. 0x20: long vertical blanking */
- 0x04, 0x90, /* analog gain set to 0 */
- 0x05, 0x90, /* analog gain set to 0 */
+ R_01_INC_DELAY, 0x48, /* white peak control disabled */
+ R_03_INPUT_CNTL_2, 0x20, /* was 0x30. 0x20: long vertical blanking */
+ R_04_INPUT_CNTL_3, 0x90, /* analog gain set to 0 */
+ R_05_INPUT_CNTL_4, 0x90, /* analog gain set to 0 */
/* Decoder Part */
- 0x06, 0xeb, /* horiz sync begin = -21 */
- 0x07, 0xe0, /* horiz sync stop = -17 */
- 0x0a, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
- 0x0b, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
- 0x0c, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
- 0x0d, 0x00, /* chrominance hue control */
- 0x0f, 0x00, /* chrominance gain control: use automicatic mode */
- 0x10, 0x06, /* chrominance/luminance control: active adaptive combfilter */
- 0x11, 0x00, /* delay control */
- 0x12, 0x9d, /* RTS0 output control: VGATE */
- 0x13, 0x80, /* X-port output control: ITU656 standard mode, RTCO output enable RTCE */
- 0x14, 0x00, /* analog/ADC/auto compatibility control */
- 0x18, 0x40, /* raw data gain 0x00 = nominal */
- 0x19, 0x80, /* raw data offset 0x80 = 0 LSB */
- 0x1a, 0x77, /* color killer level control 0x77 = recommended */
- 0x1b, 0x42, /* misc chroma control 0x42 = recommended */
- 0x1c, 0xa9, /* combfilter control 0xA9 = recommended */
- 0x1d, 0x01, /* combfilter control 0x01 = recommended */
+ R_06_H_SYNC_START, 0xeb, /* horiz sync begin = -21 */
+ R_07_H_SYNC_STOP, 0xe0, /* horiz sync stop = -17 */
+ R_09_LUMA_CNTL, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
+ R_0A_LUMA_BRIGHT_CNTL, 0x80, /* was 0x88. decoder brightness, 0x80 is itu standard */
+ R_0B_LUMA_CONTRAST_CNTL, 0x44, /* was 0x48. decoder contrast, 0x44 is itu standard */
+ R_0C_CHROMA_SAT_CNTL, 0x40, /* was 0x47. decoder saturation, 0x40 is itu standard */
+ R_0D_CHROMA_HUE_CNTL, 0x00,
+ R_0F_CHROMA_GAIN_CNTL, 0x00, /* use automatic gain */
+ R_10_CHROMA_CNTL_2, 0x06, /* chroma: active adaptive combfilter */
+ R_11_MODE_DELAY_CNTL, 0x00,
+ R_12_RT_SIGNAL_CNTL, 0x9d, /* RTS0 output control: VGATE */
+ R_13_RT_X_PORT_OUT_CNTL, 0x80, /* ITU656 standard mode, RTCO output enable RTCE */
+ R_14_ANAL_ADC_COMPAT_CNTL, 0x00,
+ R_18_RAW_DATA_GAIN_CNTL, 0x40, /* gain 0x00 = nominal */
+ R_19_RAW_DATA_OFF_CNTL, 0x80,
+ R_1A_COLOR_KILL_LVL_CNTL, 0x77, /* recommended value */
+ R_1B_MISC_TVVCRDET, 0x42, /* recommended value */
+ R_1C_ENHAN_COMB_CTRL1, 0xa9, /* recommended value */
+ R_1D_ENHAN_COMB_CTRL2, 0x01, /* recommended value */
+
+
+ R_80_GLOBAL_CNTL_1, 0x0, /* No tasks enabled at init */
/* Power Device Control */
- 0x88, 0xd0, /* reset device */
- 0x88, 0xf0, /* set device programmed, all in operational mode */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset device */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* set device programmed, all in operational mode */
0x00, 0x00
};
+/* Used to reset saa7113, saa7114 and saa7115 */
static const unsigned char saa7115_cfg_reset_scaler[] = {
- 0x87, 0x00, /* disable I-port output */
- 0x88, 0xd0, /* reset scaler */
- 0x88, 0xf0, /* activate scaler */
- 0x87, 0x01, /* enable I-port output */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x00, /* disable I-port output */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* enable I-port output */
0x00, 0x00
};
/* ============== SAA7715 VIDEO templates ============= */
-static const unsigned char saa7115_cfg_60hz_fullres_x[] = {
- 0xcc, 0xd0, /* hsize low (output), hor. output window size = 0x2d0 = 720 */
- 0xcd, 0x02, /* hsize hi (output) */
+static const unsigned char saa7115_cfg_60hz_video[] = {
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
- /* Why not in 60hz-Land, too? */
- 0xd0, 0x01, /* downscale = 1 */
- 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
- 0xd9, 0x04,
- 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
- 0xdd, 0x02, /* H-scaling incr chroma */
+ R_15_VGATE_START_FID_CHG, 0x03,
+ R_16_VGATE_STOP, 0x11,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x9c,
- 0x00, 0x00
-};
-static const unsigned char saa7115_cfg_60hz_fullres_y[] = {
- 0xce, 0xf8, /* vsize low (output), ver. output window size = 248 (but 60hz is 240?) */
- 0xcf, 0x00, /* vsize hi (output) */
+ R_08_SYNC_CNTL, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
+ R_0E_CHROMA_CNTL_1, 0x07, /* video autodetection is on */
- /* Why not in 60hz-Land, too? */
- 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
- 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
+ R_5A_V_OFF_FOR_SLICER, 0x06, /* standard 60hz value for ITU656 line counting */
- 0xe0, 0x00, /* V-scaling incr luma low */
- 0xe1, 0x04, /* " hi */
- 0xe2, 0x00, /* V-scaling incr chroma low */
- 0xe3, 0x04, /* " hi */
+ /* Task A */
+ R_90_A_TASK_HANDLING_CNTL, 0x80,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
- 0x00, 0x00
-};
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x01,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
-static const unsigned char saa7115_cfg_60hz_video[] = {
- 0x80, 0x00, /* reset tasks */
- 0x88, 0xd0, /* reset scaler */
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
- 0x15, 0x03, /* VGATE pulse start */
- 0x16, 0x11, /* VGATE pulse stop */
- 0x17, 0x9c, /* VGATE MSB and other values */
+ R_98_A_VERT_INPUT_WINDOW_START, 0x05,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
- 0x08, 0x68, /* 0xBO: auto detection, 0x68 = NTSC */
- 0x0e, 0x07, /* lots of different stuff... video autodetection is on */
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x0c,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
- 0x5a, 0x06, /* Vertical offset, standard 60hz value for ITU656 line counting */
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05,
- /* Task A */
- 0x90, 0x80, /* Task Handling Control */
- 0x91, 0x48, /* X-port formats/config */
- 0x92, 0x40, /* Input Ref. signal Def. */
- 0x93, 0x84, /* I-port config */
- 0x94, 0x01, /* hoffset low (input), 0x0002 is minimum */
- 0x95, 0x00, /* hoffset hi (input) */
- 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */
- 0x97, 0x02, /* hsize hi (input) */
- 0x98, 0x05, /* voffset low (input) */
- 0x99, 0x00, /* voffset hi (input) */
- 0x9a, 0x0c, /* vsize low (input), 0x0c = 12 */
- 0x9b, 0x00, /* vsize hi (input) */
- 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */
- 0x9d, 0x05, /* hsize hi (output) */
- 0x9e, 0x0c, /* vsize low (output), 0x0c = 12 */
- 0x9f, 0x00, /* vsize hi (output) */
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x0c,
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00,
/* Task B */
- 0xc0, 0x00, /* Task Handling Control */
- 0xc1, 0x08, /* X-port formats/config */
- 0xc2, 0x00, /* Input Ref. signal Def. */
- 0xc3, 0x80, /* I-port config */
- 0xc4, 0x02, /* hoffset low (input), 0x0002 is minimum */
- 0xc5, 0x00, /* hoffset hi (input) */
- 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */
- 0xc7, 0x02, /* hsize hi (input) */
- 0xc8, 0x12, /* voffset low (input), 0x12 = 18 */
- 0xc9, 0x00, /* voffset hi (input) */
- 0xca, 0xf8, /* vsize low (input), 0xf8 = 248 */
- 0xcb, 0x00, /* vsize hi (input) */
- 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */
- 0xcd, 0x02, /* hsize hi (output) */
-
- 0xf0, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
- 0xf1, 0x05, /* low bit with 0xF0 */
- 0xf5, 0xad, /* Set pulse generator register */
- 0xf6, 0x01,
-
- 0x87, 0x00, /* Disable I-port output */
- 0x88, 0xd0, /* reset scaler */
- 0x80, 0x20, /* Activate only task "B", continuous mode (was 0xA0) */
- 0x88, 0xf0, /* activate scaler */
- 0x87, 0x01, /* Enable I-port output */
- 0x00, 0x00
-};
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
-static const unsigned char saa7115_cfg_50hz_fullres_x[] = {
- 0xcc, 0xd0, /* hsize low (output), 720 same as 60hz */
- 0xcd, 0x02, /* hsize hi (output) */
+ /* 0x0002 is minimum */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x02,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
- 0xd0, 0x01, /* down scale = 1 */
- 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
- 0xd9, 0x04,
- 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
- 0xdd, 0x02, /* H-scaling incr chroma */
+ /* 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
- 0x00, 0x00
-};
-static const unsigned char saa7115_cfg_50hz_fullres_y[] = {
- 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */
- 0xcf, 0x01, /* vsize hi (output) */
+ /* vwindow start 0x12 = 18 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x12,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vwindow length 0xf8 = 248 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, VRES_60HZ>>1,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, VRES_60HZ>>9,
- 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
- 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
+ /* hwindow 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
- 0xe0, 0x00, /* V-scaling incr luma low */
- 0xe1, 0x04, /* " hi */
- 0xe2, 0x00, /* V-scaling incr chroma low */
- 0xe3, 0x04, /* " hi */
+ R_F0_LFCO_PER_LINE, 0xad, /* Set PLL Register. 60hz 525 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0 */
+ R_F5_PULSGEN_LINE_LENGTH, 0xad,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
0x00, 0x00
};
static const unsigned char saa7115_cfg_50hz_video[] = {
- 0x80, 0x00, /* reset tasks */
- 0x88, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x00,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
- 0x15, 0x37, /* VGATE start */
- 0x16, 0x16, /* VGATE stop */
- 0x17, 0x99, /* VGATE MSB and other values */
+ R_15_VGATE_START_FID_CHG, 0x37, /* VGATE start */
+ R_16_VGATE_STOP, 0x16,
+ R_17_MISC_VGATE_CONF_AND_MSB, 0x99,
- 0x08, 0x28, /* 0x28 = PAL */
- 0x0e, 0x07, /* chrominance control 1 */
+ R_08_SYNC_CNTL, 0x28, /* 0x28 = PAL */
+ R_0E_CHROMA_CNTL_1, 0x07,
- 0x5a, 0x03, /* Vertical offset, standard 50hz value */
+ R_5A_V_OFF_FOR_SLICER, 0x03, /* standard 50hz value */
/* Task A */
- 0x90, 0x81, /* Task Handling Control */
- 0x91, 0x48, /* X-port formats/config */
- 0x92, 0x40, /* Input Ref. signal Def. */
- 0x93, 0x84, /* I-port config */
+ R_90_A_TASK_HANDLING_CNTL, 0x81,
+ R_91_A_X_PORT_FORMATS_AND_CONF, 0x48,
+ R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL, 0x40,
+ R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF, 0x84,
+
/* This is weird: the datasheet says that you should use 2 as the minimum value, */
/* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
- 0x94, 0x00, /* hoffset low (input), 0x0002 is minimum */
- 0x95, 0x00, /* hoffset hi (input) */
- 0x96, 0xd0, /* hsize low (input), 0x02d0 = 720 */
- 0x97, 0x02, /* hsize hi (input) */
- 0x98, 0x03, /* voffset low (input) */
- 0x99, 0x00, /* voffset hi (input) */
- 0x9a, 0x12, /* vsize low (input), 0x12 = 18 */
- 0x9b, 0x00, /* vsize hi (input) */
- 0x9c, 0xa0, /* hsize low (output), 0x05a0 = 1440 */
- 0x9d, 0x05, /* hsize hi (output) */
- 0x9e, 0x12, /* vsize low (output), 0x12 = 18 */
- 0x9f, 0x00, /* vsize hi (output) */
+ /* hoffset low (input), 0x0002 is minimum */
+ R_94_A_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_95_A_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize low (input), 0x02d0 = 720 */
+ R_96_A_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_98_A_VERT_INPUT_WINDOW_START, 0x03,
+ R_99_A_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x12 = 18 */
+ R_9A_A_VERT_INPUT_WINDOW_LENGTH, 0x12,
+ R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB, 0x00,
+
+ /* hsize 0x05a0 = 1440 */
+ R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH, 0xa0,
+ R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x05, /* hsize hi (output) */
+ R_9E_A_VERT_OUTPUT_WINDOW_LENGTH, 0x12, /* vsize low (output), 0x12 = 18 */
+ R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB, 0x00, /* vsize hi (output) */
/* Task B */
- 0xc0, 0x00, /* Task Handling Control */
- 0xc1, 0x08, /* X-port formats/config */
- 0xc2, 0x00, /* Input Ref. signal Def. */
- 0xc3, 0x80, /* I-port config */
- 0xc4, 0x00, /* hoffset low (input), 0x0002 is minimum. See comment at 0x94 above. */
- 0xc5, 0x00, /* hoffset hi (input) */
- 0xc6, 0xd0, /* hsize low (input), 0x02d0 = 720 */
- 0xc7, 0x02, /* hsize hi (input) */
- 0xc8, 0x16, /* voffset low (input), 0x16 = 22 */
- 0xc9, 0x00, /* voffset hi (input) */
- 0xca, 0x20, /* vsize low (input), 0x0120 = 288 */
- 0xcb, 0x01, /* vsize hi (input) */
- 0xcc, 0xd0, /* hsize low (output), 0x02d0 = 720 */
- 0xcd, 0x02, /* hsize hi (output) */
- 0xce, 0x20, /* vsize low (output), 0x0120 = 288 */
- 0xcf, 0x01, /* vsize hi (output) */
-
- 0xf0, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
- 0xf1, 0x05, /* low bit with 0xF0, (was 0x05) */
- 0xf5, 0xb0, /* Set pulse generator register */
- 0xf6, 0x01,
-
- 0x87, 0x00, /* Disable I-port output */
- 0x88, 0xd0, /* reset scaler (was 0xD0) */
- 0x80, 0x20, /* Activate only task "B" */
- 0x88, 0xf0, /* activate scaler */
- 0x87, 0x01, /* Enable I-port output */
+ R_C0_B_TASK_HANDLING_CNTL, 0x00,
+ R_C1_B_X_PORT_FORMATS_AND_CONF, 0x08,
+ R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION, 0x00,
+ R_C3_B_I_PORT_FORMATS_AND_CONF, 0x80,
+
+ /* This is weird: the datasheet says that you should use 2 as the minimum value, */
+ /* but Hauppauge uses 0, and changing that to 2 causes indeed problems (for 50hz) */
+ /* hoffset low (input), 0x0002 is minimum. See comment above. */
+ R_C4_B_HORIZ_INPUT_WINDOW_START, 0x00,
+ R_C5_B_HORIZ_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* hsize 0x02d0 = 720 */
+ R_C6_B_HORIZ_INPUT_WINDOW_LENGTH, 0xd0,
+ R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ /* voffset 0x16 = 22 */
+ R_C8_B_VERT_INPUT_WINDOW_START, 0x16,
+ R_C9_B_VERT_INPUT_WINDOW_START_MSB, 0x00,
+
+ /* vsize 0x0120 = 288 */
+ R_CA_B_VERT_INPUT_WINDOW_LENGTH, 0x20,
+ R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB, 0x01,
+
+ /* hsize 0x02d0 = 720 */
+ R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH, 0xd0,
+ R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB, 0x02,
+
+ R_F0_LFCO_PER_LINE, 0xb0, /* Set PLL Register. 50hz 625 lines per frame, 27 MHz */
+ R_F1_P_I_PARAM_SELECT, 0x05, /* low bit with 0xF0, (was 0x05) */
+ R_F5_PULSGEN_LINE_LENGTH, 0xb0,
+ R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG, 0x01,
+
0x00, 0x00
};
/* ============== SAA7715 VIDEO templates (end) ======= */
static const unsigned char saa7115_cfg_vbi_on[] = {
- 0x80, 0x00, /* reset tasks */
- 0x88, 0xd0, /* reset scaler */
- 0x80, 0x30, /* Activate both tasks */
- 0x88, 0xf0, /* activate scaler */
- 0x87, 0x01, /* Enable I-port output */
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x30, /* Activate both tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
+
0x00, 0x00
};
static const unsigned char saa7115_cfg_vbi_off[] = {
- 0x80, 0x00, /* reset tasks */
- 0x88, 0xd0, /* reset scaler */
- 0x80, 0x20, /* Activate only task "B" */
- 0x88, 0xf0, /* activate scaler */
- 0x87, 0x01, /* Enable I-port output */
- 0x00, 0x00
-};
+ R_80_GLOBAL_CNTL_1, 0x00, /* reset tasks */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0, /* reset scaler */
+ R_80_GLOBAL_CNTL_1, 0x20, /* Activate only task "B" */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0, /* activate scaler */
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01, /* Enable I-port output */
-static const unsigned char saa7113_init_auto_input[] = {
- 0x01, 0x08, /* PH7113_INCREMENT_DELAY - (1) (1) (1) (1) IDEL3 IDEL2 IDELL1 IDEL0 */
- 0x02, 0xc2, /* PH7113_ANALOG_INPUT_CONTR_1 - FUSE1 FUSE0 GUDL1 GUDL0 MODE3 MODE2 MODE1 MODE0 */
- 0x03, 0x30, /* PH7113_ANALOG_INPUT_CONTR_2 - (1) HLNRS VBSL WPOFF HOLDG GAFIX GAI28 GAI18 */
- 0x04, 0x00, /* PH7113_ANALOG_INPUT_CONTR_3 - GAI17 GAI16 GAI15 GAI14 GAI13 GAI12 GAI11 GAI10 */
- 0x05, 0x00, /* PH7113_ANALOG_INPUT_CONTR_4 - GAI27 GAI26 GAI25 GAI24 GAI23 GAI22 GAI21 GAI20 */
- 0x06, 0x89, /* PH7113_HORIZONTAL_SYNC_START - HSB7 HSB6 HSB5 HSB4 HSB3 HSB2 HSB1 HSB0 */
- 0x07, 0x0d, /* PH7113_HORIZONTAL_SYNC_STOP - HSS7 HSS6 HSS5 HSS4 HSS3 HSS2 HSS1 HSS0 */
- 0x08, 0x88, /* PH7113_SYNC_CONTROL - AUFD FSEL FOET HTC1 HTC0 HPLL VNOI1 VNOI0 */
- 0x09, 0x01, /* PH7113_LUMINANCE_CONTROL - BYPS PREF BPSS1 BPSS0 VBLB UPTCV APER1 APER0 */
- 0x0a, 0x80, /* PH7113_LUMINANCE_BRIGHTNESS - BRIG7 BRIG6 BRIG5 BRIG4 BRIG3 BRIG2 BRIG1 BRIG0 */
- 0x0b, 0x47, /* PH7113_LUMINANCE_CONTRAST - CONT7 CONT6 CONT5 CONT4 CONT3 CONT2 CONT1 CONT0 */
- 0x0c, 0x40, /* PH7113_CHROMA_SATURATION - SATN7 SATN6 SATN5 SATN4 SATN3 SATN2 SATN1 SATN0 */
- 0x0d, 0x00, /* PH7113_CHROMA_HUE_CONTROL - HUEC7 HUEC6 HUEC5 HUEC4 HUEC3 HUEC2 HUEC1 HUEC0 */
- 0x0e, 0x01, /* PH7113_CHROMA_CONTROL - CDTO CSTD2 CSTD1 CSTD0 DCCF FCTC CHBW1 CHBW0 */
- 0x0f, 0x2a, /* PH7113_CHROMA_GAIN_CONTROL - ACGC CGAIN6 CGAIN5 CGAIN4 CGAIN3 CGAIN2 CGAIN1 CGAIN0 */
- 0x10, 0x08, /* PH7113_FORMAT_DELAY_CONTROL - OFTS1 OFTS0 HDEL1 HDEL0 VRLN YDEL2 YDEL1 YDEL0 */
- 0x11, 0x0c, /* PH7113_OUTPUT_CONTROL_1 - GPSW1 CM99 GPSW0 HLSEL OEYC OERT VIPB COLO */
- 0x12, 0x07, /* PH7113_OUTPUT_CONTROL_2 - RTSE13 RTSE12 RTSE11 RTSE10 RTSE03 RTSE02 RTSE01 RTSE00 */
- 0x13, 0x00, /* PH7113_OUTPUT_CONTROL_3 - ADLSB (1) (1) OLDSB FIDP (1) AOSL1 AOSL0 */
- 0x14, 0x00, /* RESERVED 14 - (1) (1) (1) (1) (1) (1) (1) (1) */
- 0x15, 0x00, /* PH7113_V_GATE1_START - VSTA7 VSTA6 VSTA5 VSTA4 VSTA3 VSTA2 VSTA1 VSTA0 */
- 0x16, 0x00, /* PH7113_V_GATE1_STOP - VSTO7 VSTO6 VSTO5 VSTO4 VSTO3 VSTO2 VSTO1 VSTO0 */
- 0x17, 0x00, /* PH7113_V_GATE1_MSB - (1) (1) (1) (1) (1) (1) VSTO8 VSTA8 */
0x00, 0x00
};
+
static const unsigned char saa7115_init_misc[] = {
- 0x81, 0x01, /* reg 0x15,0x16 define blanking window */
- 0x82, 0x00,
- 0x83, 0x01, /* I port settings */
- 0x84, 0x20,
- 0x85, 0x21,
- 0x86, 0xc5,
- 0x87, 0x01,
+ R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F, 0x01,
+ R_83_X_PORT_I_O_ENA_AND_OUT_CLK, 0x01,
+ R_84_I_PORT_SIGNAL_DEF, 0x20,
+ R_85_I_PORT_SIGNAL_POLAR, 0x21,
+ R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT, 0xc5,
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 0x01,
/* Task A */
- 0xa0, 0x01, /* down scale = 1 */
- 0xa1, 0x00, /* prescale accumulation length = 1 */
- 0xa2, 0x00, /* dc gain and fir prefilter control */
- 0xa4, 0x80, /* Lum Brightness, nominal value = 0x80 */
- 0xa5, 0x40, /* Lum contrast, nominal value = 0x40 */
- 0xa6, 0x40, /* Chroma satur. nominal value = 0x80 */
- 0xa8, 0x00, /* hor lum scaling 0x0200 = 2 zoom */
- 0xa9, 0x02, /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
- 0xaa, 0x00, /* H-phase offset Luma = 0 */
- 0xac, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
- 0xad, 0x01, /* H-scaling incr chroma */
- 0xae, 0x00, /* H-phase offset chroma. must be offset luma / 2 */
-
- 0xb0, 0x00, /* V-scaling incr luma low */
- 0xb1, 0x04, /* " hi */
- 0xb2, 0x00, /* V-scaling incr chroma low */
- 0xb3, 0x04, /* " hi */
- 0xb4, 0x01, /* V-scaling mode control */
- 0xb8, 0x00, /* V-phase offset chroma 00 */
- 0xb9, 0x00, /* V-phase offset chroma 01 */
- 0xba, 0x00, /* V-phase offset chroma 10 */
- 0xbb, 0x00, /* V-phase offset chroma 11 */
- 0xbc, 0x00, /* V-phase offset luma 00 */
- 0xbd, 0x00, /* V-phase offset luma 01 */
- 0xbe, 0x00, /* V-phase offset luma 10 */
- 0xbf, 0x00, /* V-phase offset luma 11 */
+ R_A0_A_HORIZ_PRESCALING, 0x01,
+ R_A1_A_ACCUMULATION_LENGTH, 0x00,
+ R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_A4_A_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_A5_A_LUMA_CONTRAST_CNTL, 0x40,
+ R_A6_A_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* note: 2 x zoom ensures that VBI lines have same length as video lines. */
+ R_A8_A_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_A9_A_HORIZ_LUMA_SCALING_INC_MSB, 0x02,
+
+ R_AA_A_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be horiz lum scaling / 2 */
+ R_AC_A_HORIZ_CHROMA_SCALING_INC, 0x00,
+ R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB, 0x01,
+
+ /* must be offset luma / 2 */
+ R_AE_A_HORIZ_CHROMA_PHASE_OFF, 0x00,
+
+ R_B0_A_VERT_LUMA_SCALING_INC, 0x00,
+ R_B1_A_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_B2_A_VERT_CHROMA_SCALING_INC, 0x00,
+ R_B3_A_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_B4_A_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_B8_A_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_B9_A_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_BA_A_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_BB_A_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_BC_A_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_BD_A_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_BE_A_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_BF_A_VERT_LUMA_PHASE_OFF_11, 0x00,
/* Task B */
- 0xd0, 0x01, /* down scale = 1 */
- 0xd1, 0x00, /* prescale accumulation length = 1 */
- 0xd2, 0x00, /* dc gain and fir prefilter control */
- 0xd4, 0x80, /* Lum Brightness, nominal value = 0x80 */
- 0xd5, 0x40, /* Lum contrast, nominal value = 0x40 */
- 0xd6, 0x40, /* Chroma satur. nominal value = 0x80 */
- 0xd8, 0x00, /* hor lum scaling 0x0400 = 1 */
- 0xd9, 0x04,
- 0xda, 0x00, /* H-phase offset Luma = 0 */
- 0xdc, 0x00, /* hor chrom scaling 0x0200. must be hor lum scaling / 2 */
- 0xdd, 0x02, /* H-scaling incr chroma */
- 0xde, 0x00, /* H-phase offset chroma. must be offset luma / 2 */
-
- 0xe0, 0x00, /* V-scaling incr luma low */
- 0xe1, 0x04, /* " hi */
- 0xe2, 0x00, /* V-scaling incr chroma low */
- 0xe3, 0x04, /* " hi */
- 0xe4, 0x01, /* V-scaling mode control */
- 0xe8, 0x00, /* V-phase offset chroma 00 */
- 0xe9, 0x00, /* V-phase offset chroma 01 */
- 0xea, 0x00, /* V-phase offset chroma 10 */
- 0xeb, 0x00, /* V-phase offset chroma 11 */
- 0xec, 0x00, /* V-phase offset luma 00 */
- 0xed, 0x00, /* V-phase offset luma 01 */
- 0xee, 0x00, /* V-phase offset luma 10 */
- 0xef, 0x00, /* V-phase offset luma 11 */
-
- 0xf2, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
- 0xf3, 0x46,
- 0xf4, 0x00,
- 0xf7, 0x4b, /* not the recommended settings! */
- 0xf8, 0x00,
- 0xf9, 0x4b,
- 0xfa, 0x00,
- 0xfb, 0x4b,
- 0xff, 0x88, /* PLL2 lock detection settings: 71 lines 50% phase error */
+ R_D0_B_HORIZ_PRESCALING, 0x01,
+ R_D1_B_ACCUMULATION_LENGTH, 0x00,
+ R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER, 0x00,
+
+ /* Configure controls at nominal value*/
+ R_D4_B_LUMA_BRIGHTNESS_CNTL, 0x80,
+ R_D5_B_LUMA_CONTRAST_CNTL, 0x40,
+ R_D6_B_CHROMA_SATURATION_CNTL, 0x40,
+
+ /* hor lum scaling 0x0400 = 1 */
+ R_D8_B_HORIZ_LUMA_SCALING_INC, 0x00,
+ R_D9_B_HORIZ_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_DA_B_HORIZ_LUMA_PHASE_OFF, 0x00,
+
+ /* must be hor lum scaling / 2 */
+ R_DC_B_HORIZ_CHROMA_SCALING, 0x00,
+ R_DD_B_HORIZ_CHROMA_SCALING_MSB, 0x02,
+
+ /* must be offset luma / 2 */
+ R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA, 0x00,
+
+ R_E0_B_VERT_LUMA_SCALING_INC, 0x00,
+ R_E1_B_VERT_LUMA_SCALING_INC_MSB, 0x04,
+
+ R_E2_B_VERT_CHROMA_SCALING_INC, 0x00,
+ R_E3_B_VERT_CHROMA_SCALING_INC_MSB, 0x04,
+
+ R_E4_B_VERT_SCALING_MODE_CNTL, 0x01,
+
+ R_E8_B_VERT_CHROMA_PHASE_OFF_00, 0x00,
+ R_E9_B_VERT_CHROMA_PHASE_OFF_01, 0x00,
+ R_EA_B_VERT_CHROMA_PHASE_OFF_10, 0x00,
+ R_EB_B_VERT_CHROMA_PHASE_OFF_11, 0x00,
+
+ R_EC_B_VERT_LUMA_PHASE_OFF_00, 0x00,
+ R_ED_B_VERT_LUMA_PHASE_OFF_01, 0x00,
+ R_EE_B_VERT_LUMA_PHASE_OFF_10, 0x00,
+ R_EF_B_VERT_LUMA_PHASE_OFF_11, 0x00,
+
+ R_F2_NOMINAL_PLL2_DTO, 0x50, /* crystal clock = 24.576 MHz, target = 27MHz */
+ R_F3_PLL_INCREMENT, 0x46,
+ R_F4_PLL2_STATUS, 0x00,
+ R_F7_PULSE_A_POS_MSB, 0x4b, /* not the recommended settings! */
+ R_F8_PULSE_B_POS, 0x00,
+ R_F9_PULSE_B_POS_MSB, 0x4b,
+ R_FA_PULSE_C_POS, 0x00,
+ R_FB_PULSE_C_POS_MSB, 0x4b,
+
+ /* PLL2 lock detection settings: 71 lines 50% phase error */
+ R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES, 0x88,
/* Turn off VBI */
- 0x40, 0x20, /* No framing code errors allowed. */
- 0x41, 0xff,
- 0x42, 0xff,
- 0x43, 0xff,
- 0x44, 0xff,
- 0x45, 0xff,
- 0x46, 0xff,
- 0x47, 0xff,
- 0x48, 0xff,
- 0x49, 0xff,
- 0x4a, 0xff,
- 0x4b, 0xff,
- 0x4c, 0xff,
- 0x4d, 0xff,
- 0x4e, 0xff,
- 0x4f, 0xff,
- 0x50, 0xff,
- 0x51, 0xff,
- 0x52, 0xff,
- 0x53, 0xff,
- 0x54, 0xff,
- 0x55, 0xff,
- 0x56, 0xff,
- 0x57, 0xff,
- 0x58, 0x40,
- 0x59, 0x47,
- 0x5b, 0x83,
- 0x5d, 0xbd,
- 0x5e, 0x35,
-
- 0x02, 0x84, /* input tuner -> input 4, amplifier active */
- 0x09, 0x53, /* 0x53, was 0x56 for 60hz. luminance control */
-
- 0x80, 0x20, /* enable task B */
- 0x88, 0xd0,
- 0x88, 0xf0,
+ R_40_SLICER_CNTL_1, 0x20, /* No framing code errors allowed. */
+ R_41_LCR_BASE, 0xff,
+ R_41_LCR_BASE+1, 0xff,
+ R_41_LCR_BASE+2, 0xff,
+ R_41_LCR_BASE+3, 0xff,
+ R_41_LCR_BASE+4, 0xff,
+ R_41_LCR_BASE+5, 0xff,
+ R_41_LCR_BASE+6, 0xff,
+ R_41_LCR_BASE+7, 0xff,
+ R_41_LCR_BASE+8, 0xff,
+ R_41_LCR_BASE+9, 0xff,
+ R_41_LCR_BASE+10, 0xff,
+ R_41_LCR_BASE+11, 0xff,
+ R_41_LCR_BASE+12, 0xff,
+ R_41_LCR_BASE+13, 0xff,
+ R_41_LCR_BASE+14, 0xff,
+ R_41_LCR_BASE+15, 0xff,
+ R_41_LCR_BASE+16, 0xff,
+ R_41_LCR_BASE+17, 0xff,
+ R_41_LCR_BASE+18, 0xff,
+ R_41_LCR_BASE+19, 0xff,
+ R_41_LCR_BASE+20, 0xff,
+ R_41_LCR_BASE+21, 0xff,
+ R_41_LCR_BASE+22, 0xff,
+ R_58_PROGRAM_FRAMING_CODE, 0x40,
+ R_59_H_OFF_FOR_SLICER, 0x47,
+ R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF, 0x83,
+ R_5D_DID, 0xbd,
+ R_5E_SDID, 0x35,
+
+ R_02_INPUT_CNTL_1, 0x84, /* input tuner -> input 4, amplifier active */
+
+ R_80_GLOBAL_CNTL_1, 0x20, /* enable task B */
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xd0,
+ R_88_POWER_SAVE_ADC_PORT_CNTL, 0xf0,
0x00, 0x00
};
-static int saa7115_odd_parity(u8 c)
+static int saa711x_odd_parity(u8 c)
{
c ^= (c >> 4);
c ^= (c >> 2);
@@ -502,7 +599,7 @@ static int saa7115_odd_parity(u8 c)
return c & 1;
}
-static int saa7115_decode_vps(u8 * dst, u8 * p)
+static int saa711x_decode_vps(u8 * dst, u8 * p)
{
static const u8 biphase_tbl[] = {
0xf0, 0x78, 0x70, 0xf0, 0xb4, 0x3c, 0x34, 0xb4,
@@ -549,7 +646,7 @@ static int saa7115_decode_vps(u8 * dst, u8 * p)
return err & 0xf0;
}
-static int saa7115_decode_wss(u8 * p)
+static int saa711x_decode_wss(u8 * p)
{
static const int wss_bits[8] = {
0, 0, 0, 1, 0, 1, 1, 1
@@ -576,26 +673,25 @@ static int saa7115_decode_wss(u8 * p)
return wss;
}
-
-static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq)
+static int saa711x_set_audio_clock_freq(struct i2c_client *client, u32 freq)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
u32 acpf;
u32 acni;
u32 hz;
u64 f;
u8 acc = 0; /* reg 0x3a, audio clock control */
+ /* Checks for chips that don't have audio clock (saa7111, saa7113) */
+ if (!saa711x_has_reg(state->ident,R_30_AUD_MAST_CLK_CYCLES_PER_FIELD))
+ return 0;
+
v4l_dbg(1, debug, client, "set audio clock freq: %d\n", freq);
/* sanity check */
if (freq < 32000 || freq > 48000)
return -EINVAL;
- /* The saa7113 has no audio clock */
- if (state->ident == V4L2_IDENT_SAA7113)
- return 0;
-
/* hz is the refresh rate times 100 */
hz = (state->std & V4L2_STD_525_60) ? 5994 : 5000;
/* acpf = (256 * freq) / field_frequency == (256 * 100 * freq) / hz */
@@ -617,22 +713,26 @@ static int saa7115_set_audio_clock_freq(struct i2c_client *client, u32 freq)
if (state->apll)
acc |= 0x08;
- saa7115_write(client, 0x38, 0x03);
- saa7115_write(client, 0x39, 0x10);
- saa7115_write(client, 0x3a, acc);
- saa7115_write(client, 0x30, acpf & 0xff);
- saa7115_write(client, 0x31, (acpf >> 8) & 0xff);
- saa7115_write(client, 0x32, (acpf >> 16) & 0x03);
- saa7115_write(client, 0x34, acni & 0xff);
- saa7115_write(client, 0x35, (acni >> 8) & 0xff);
- saa7115_write(client, 0x36, (acni >> 16) & 0x3f);
+ saa711x_write(client, R_38_CLK_RATIO_AMXCLK_TO_ASCLK, 0x03);
+ saa711x_write(client, R_39_CLK_RATIO_ASCLK_TO_ALRCLK, 0x10);
+ saa711x_write(client, R_3A_AUD_CLK_GEN_BASIC_SETUP, acc);
+
+ saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD, acpf & 0xff);
+ saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+1,
+ (acpf >> 8) & 0xff);
+ saa711x_write(client, R_30_AUD_MAST_CLK_CYCLES_PER_FIELD+2,
+ (acpf >> 16) & 0x03);
+
+ saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC, acni & 0xff);
+ saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+1, (acni >> 8) & 0xff);
+ saa711x_write(client, R_34_AUD_MAST_CLK_NOMINAL_INC+2, (acni >> 16) & 0x3f);
state->audclk_freq = freq;
return 0;
}
-static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_set_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -642,7 +742,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
}
state->bright = ctrl->value;
- saa7115_write(client, 0x0a, state->bright);
+ saa711x_write(client, R_0A_LUMA_BRIGHT_CNTL, state->bright);
break;
case V4L2_CID_CONTRAST:
@@ -652,7 +752,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
}
state->contrast = ctrl->value;
- saa7115_write(client, 0x0b, state->contrast);
+ saa711x_write(client, R_0B_LUMA_CONTRAST_CNTL, state->contrast);
break;
case V4L2_CID_SATURATION:
@@ -662,7 +762,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
}
state->sat = ctrl->value;
- saa7115_write(client, 0x0c, state->sat);
+ saa711x_write(client, R_0C_CHROMA_SAT_CNTL, state->sat);
break;
case V4L2_CID_HUE:
@@ -672,7 +772,7 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
}
state->hue = ctrl->value;
- saa7115_write(client, 0x0d, state->hue);
+ saa711x_write(client, R_0D_CHROMA_HUE_CNTL, state->hue);
break;
default:
@@ -682,9 +782,9 @@ static int saa7115_set_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
+static int saa711x_get_v4lctrl(struct i2c_client *client, struct v4l2_control *ctrl)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
switch (ctrl->id) {
case V4L2_CID_BRIGHTNESS:
@@ -706,10 +806,115 @@ static int saa7115_get_v4lctrl(struct i2c_client *client, struct v4l2_control *c
return 0;
}
-static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
+static int saa711x_set_size(struct i2c_client *client, int width, int height)
+{
+ struct saa711x_state *state = i2c_get_clientdata(client);
+ int HPSC, HFSC;
+ int VSCY;
+ int res;
+ int is_50hz = state->std & V4L2_STD_625_50;
+ int Vsrc = is_50hz ? 576 : 480;
+
+ v4l_dbg(1, debug, client, "decoder set size to %ix%i\n",width,height);
+
+ /* FIXME need better bounds checking here */
+ if ((width < 1) || (width > 1440))
+ return -EINVAL;
+ if ((height < 1) || (height > Vsrc))
+ return -EINVAL;
+
+ if (!saa711x_has_reg(state->ident,R_D0_B_HORIZ_PRESCALING)) {
+ /* Decoder only supports 720 columns and 480 or 576 lines */
+ if (width != 720)
+ return -EINVAL;
+ if (height != Vsrc)
+ return -EINVAL;
+ }
+
+ state->width = width;
+ state->height = height;
+
+ if (!saa711x_has_reg(state->ident, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH))
+ return 0;
+
+ /* probably have a valid size, let's set it */
+ /* Set output width/height */
+ /* width */
+
+ saa711x_write(client, R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,
+ (u8) (width & 0xff));
+ saa711x_write(client, R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((width >> 8) & 0xff));
+
+ /* Vertical Scaling uses height/2 */
+ res=height/2;
+
+ /* On 60Hz, it is using a higher Vertical Output Size */
+ if (!is_50hz)
+ res+=(VRES_60HZ-480)>>1;
+
+ /* height */
+ saa711x_write(client, R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,
+ (u8) (res & 0xff));
+ saa711x_write(client, R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB,
+ (u8) ((res >> 8) & 0xff));
+
+ /* Scaling settings */
+ /* Hprescaler is floor(inres/outres) */
+ HPSC = (int)(720 / width);
+ /* 0 is not allowed (div. by zero) */
+ HPSC = HPSC ? HPSC : 1;
+ HFSC = (int)((1024 * 720) / (HPSC * width));
+ /* FIXME hardcodes to "Task B"
+ * write H prescaler integer */
+ saa711x_write(client, R_D0_B_HORIZ_PRESCALING,
+ (u8) (HPSC & 0x3f));
+
+ v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
+ /* write H fine-scaling (luminance) */
+ saa711x_write(client, R_D8_B_HORIZ_LUMA_SCALING_INC,
+ (u8) (HFSC & 0xff));
+ saa711x_write(client, R_D9_B_HORIZ_LUMA_SCALING_INC_MSB,
+ (u8) ((HFSC >> 8) & 0xff));
+ /* write H fine-scaling (chrominance)
+ * must be lum/2, so i'll just bitshift :) */
+ saa711x_write(client, R_DC_B_HORIZ_CHROMA_SCALING,
+ (u8) ((HFSC >> 1) & 0xff));
+ saa711x_write(client, R_DD_B_HORIZ_CHROMA_SCALING_MSB,
+ (u8) ((HFSC >> 9) & 0xff));
+
+ VSCY = (int)((1024 * Vsrc) / height);
+ v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
+
+ /* Correct Contrast and Luminance */
+ saa711x_write(client, R_D5_B_LUMA_CONTRAST_CNTL,
+ (u8) (64 * 1024 / VSCY));
+ saa711x_write(client, R_D6_B_CHROMA_SATURATION_CNTL,
+ (u8) (64 * 1024 / VSCY));
+
+ /* write V fine-scaling (luminance) */
+ saa711x_write(client, R_E0_B_VERT_LUMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(client, R_E1_B_VERT_LUMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+ /* write V fine-scaling (chrominance) */
+ saa711x_write(client, R_E2_B_VERT_CHROMA_SCALING_INC,
+ (u8) (VSCY & 0xff));
+ saa711x_write(client, R_E3_B_VERT_CHROMA_SCALING_INC_MSB,
+ (u8) ((VSCY >> 8) & 0xff));
+
+ saa711x_writeregs(client, saa7115_cfg_reset_scaler);
+
+ /* Activates task "B" */
+ saa711x_write(client, R_80_GLOBAL_CNTL_1,
+ saa711x_read(client,R_80_GLOBAL_CNTL_1)|0x20);
+
+ return 0;
+}
+
+static void saa711x_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
- int taskb = saa7115_read(client, 0x80) & 0x10;
+ struct saa711x_state *state = i2c_get_clientdata(client);
/* Prevent unnecessary standard changes. During a standard
change the I-Port is temporarily disabled. Any devices
@@ -721,17 +926,21 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
if (std == state->std)
return;
+ state->std = std;
+
// This works for NTSC-M, SECAM-L and the 50Hz PAL variants.
if (std & V4L2_STD_525_60) {
v4l_dbg(1, debug, client, "decoder set standard 60 Hz\n");
- saa7115_writeregs(client, saa7115_cfg_60hz_video);
+ saa711x_writeregs(client, saa7115_cfg_60hz_video);
+ saa711x_set_size(client,720,480);
} else {
v4l_dbg(1, debug, client, "decoder set standard 50 Hz\n");
- saa7115_writeregs(client, saa7115_cfg_50hz_video);
+ saa711x_writeregs(client, saa7115_cfg_50hz_video);
+ saa711x_set_size(client,720,576);
}
/* Register 0E - Bits D6-D4 on NO-AUTO mode
- (SAA7113 doesn't have auto mode)
+ (SAA7111 and SAA7113 doesn't have auto mode)
50 Hz / 625 lines 60 Hz / 525 lines
000 PAL BGDHI (4.43Mhz) NTSC M (3.58MHz)
001 NTSC 4.43 (50 Hz) PAL 4.43 (60 Hz)
@@ -739,8 +948,9 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
011 NTSC N (3.58MHz) PAL M (3.58MHz)
100 reserved NTSC-Japan (3.58MHz)
*/
- if (state->ident == V4L2_IDENT_SAA7113) {
- u8 reg = saa7115_read(client, 0x0e) & 0x8f;
+ if (state->ident == V4L2_IDENT_SAA7111 ||
+ state->ident == V4L2_IDENT_SAA7113) {
+ u8 reg = saa711x_read(client, R_0E_CHROMA_CNTL_1) & 0x8f;
if (std == V4L2_STD_PAL_M) {
reg |= 0x30;
@@ -751,31 +961,30 @@ static void saa7115_set_v4lstd(struct i2c_client *client, v4l2_std_id std)
} else if (std == V4L2_STD_NTSC_M_JP) {
reg |= 0x40;
}
- saa7115_write(client, 0x0e, reg);
- }
-
+ saa711x_write(client, R_0E_CHROMA_CNTL_1, reg);
+ } else {
+ /* restart task B if needed */
+ int taskb = saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10;
- state->std = std;
+ if (taskb && state->ident == V4L2_IDENT_SAA7114) {
+ saa711x_writeregs(client, saa7115_cfg_vbi_on);
+ }
- /* restart task B if needed */
- if (taskb && state->ident != V4L2_IDENT_SAA7115) {
- saa7115_writeregs(client, saa7115_cfg_vbi_on);
+ /* switch audio mode too! */
+ saa711x_set_audio_clock_freq(client, state->audclk_freq);
}
-
- /* switch audio mode too! */
- saa7115_set_audio_clock_freq(client, state->audclk_freq);
}
-static v4l2_std_id saa7115_get_v4lstd(struct i2c_client *client)
+static v4l2_std_id saa711x_get_v4lstd(struct i2c_client *client)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
return state->std;
}
-static void saa7115_log_status(struct i2c_client *client)
+static void saa711x_log_status(struct i2c_client *client)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
int reg1e, reg1f;
int signalOk;
int vcr;
@@ -783,7 +992,7 @@ static void saa7115_log_status(struct i2c_client *client)
v4l_info(client, "Audio frequency: %d Hz\n", state->audclk_freq);
if (state->ident != V4L2_IDENT_SAA7115) {
/* status for the saa7114 */
- reg1f = saa7115_read(client, 0x1f);
+ reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
signalOk = (reg1f & 0xc1) == 0x81;
v4l_info(client, "Video signal: %s\n", signalOk ? "ok" : "bad");
v4l_info(client, "Frequency: %s\n", (reg1f & 0x20) ? "60 Hz" : "50 Hz");
@@ -791,8 +1000,8 @@ static void saa7115_log_status(struct i2c_client *client)
}
/* status for the saa7115 */
- reg1e = saa7115_read(client, 0x1e);
- reg1f = saa7115_read(client, 0x1f);
+ reg1e = saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC);
+ reg1f = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
signalOk = (reg1f & 0xc1) == 0x81 && (reg1e & 0xc0) == 0x80;
vcr = !(reg1f & 0x10);
@@ -819,19 +1028,27 @@ static void saa7115_log_status(struct i2c_client *client)
v4l_info(client, "Detected format: BW/No color\n");
break;
}
+ v4l_info(client, "Width, Height: %d, %d\n", state->width, state->height);
}
/* setup the sliced VBI lcr registers according to the sliced VBI format */
-static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
+static void saa711x_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_format *fmt)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
int is_50hz = (state->std & V4L2_STD_625_50);
u8 lcr[24];
int i, x;
- /* saa7113/7114 doesn't yet support VBI */
+#if 1
+ /* saa7113/7114/7118 VBI support are experimental */
+ if (!saa711x_has_reg(state->ident,R_41_LCR_BASE))
+ return;
+
+#else
+ /* SAA7113 and SAA7118 also should support VBI - Need testing */
if (state->ident != V4L2_IDENT_SAA7115)
return;
+#endif
for (i = 0; i <= 23; i++)
lcr[i] = 0xff;
@@ -888,14 +1105,16 @@ static void saa7115_set_lcr(struct i2c_client *client, struct v4l2_sliced_vbi_fo
/* write the lcr registers */
for (i = 2; i <= 23; i++) {
- saa7115_write(client, i - 2 + 0x41, lcr[i]);
+ saa711x_write(client, i - 2 + R_41_LCR_BASE, lcr[i]);
}
/* enable/disable raw VBI capturing */
- saa7115_writeregs(client, fmt->service_set == 0 ? saa7115_cfg_vbi_on : saa7115_cfg_vbi_off);
+ saa711x_writeregs(client, fmt->service_set == 0 ?
+ saa7115_cfg_vbi_on :
+ saa7115_cfg_vbi_off);
}
-static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
{
static u16 lcr2vbi[] = {
0, V4L2_SLICED_TELETEXT_B, 0, /* 1 */
@@ -911,10 +1130,10 @@ static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return -EINVAL;
memset(sliced, 0, sizeof(*sliced));
/* done if using raw VBI */
- if (saa7115_read(client, 0x80) & 0x10)
+ if (saa711x_read(client, R_80_GLOBAL_CNTL_1) & 0x10)
return 0;
for (i = 2; i <= 23; i++) {
- u8 v = saa7115_read(client, i - 2 + 0x41);
+ u8 v = saa711x_read(client, i - 2 + R_41_LCR_BASE);
sliced->service_lines[0][i] = lcr2vbi[v >> 4];
sliced->service_lines[1][i] = lcr2vbi[v & 0xf];
@@ -924,114 +1143,31 @@ static int saa7115_get_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt
return 0;
}
-static int saa7115_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
+static int saa711x_set_v4lfmt(struct i2c_client *client, struct v4l2_format *fmt)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
- struct v4l2_pix_format *pix;
- int HPSC, HFSC;
- int VSCY, Vsrc;
- int is_50hz = state->std & V4L2_STD_625_50;
-
if (fmt->type == V4L2_BUF_TYPE_SLICED_VBI_CAPTURE) {
- saa7115_set_lcr(client, &fmt->fmt.sliced);
+ saa711x_set_lcr(client, &fmt->fmt.sliced);
return 0;
}
if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
- pix = &(fmt->fmt.pix);
-
- v4l_dbg(1, debug, client, "decoder set size\n");
-
- /* FIXME need better bounds checking here */
- if ((pix->width < 1) || (pix->width > 1440))
- return -EINVAL;
- if ((pix->height < 1) || (pix->height > 960))
- return -EINVAL;
-
- /* probably have a valid size, let's set it */
- /* Set output width/height */
- /* width */
- saa7115_write(client, 0xcc, (u8) (pix->width & 0xff));
- saa7115_write(client, 0xcd, (u8) ((pix->width >> 8) & 0xff));
- /* height */
- saa7115_write(client, 0xce, (u8) (pix->height & 0xff));
- saa7115_write(client, 0xcf, (u8) ((pix->height >> 8) & 0xff));
-
- /* Scaling settings */
- /* Hprescaler is floor(inres/outres) */
- /* FIXME hardcoding input res */
- if (pix->width != 720) {
- HPSC = (int)(720 / pix->width);
- /* 0 is not allowed (div. by zero) */
- HPSC = HPSC ? HPSC : 1;
- HFSC = (int)((1024 * 720) / (HPSC * pix->width));
-
- v4l_dbg(1, debug, client, "Hpsc: 0x%05x, Hfsc: 0x%05x\n", HPSC, HFSC);
- /* FIXME hardcodes to "Task B"
- * write H prescaler integer */
- saa7115_write(client, 0xd0, (u8) (HPSC & 0x3f));
-
- /* write H fine-scaling (luminance) */
- saa7115_write(client, 0xd8, (u8) (HFSC & 0xff));
- saa7115_write(client, 0xd9, (u8) ((HFSC >> 8) & 0xff));
- /* write H fine-scaling (chrominance)
- * must be lum/2, so i'll just bitshift :) */
- saa7115_write(client, 0xDC, (u8) ((HFSC >> 1) & 0xff));
- saa7115_write(client, 0xDD, (u8) ((HFSC >> 9) & 0xff));
- } else {
- if (is_50hz) {
- v4l_dbg(1, debug, client, "Setting full 50hz width\n");
- saa7115_writeregs(client, saa7115_cfg_50hz_fullres_x);
- } else {
- v4l_dbg(1, debug, client, "Setting full 60hz width\n");
- saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
- }
- }
-
- Vsrc = is_50hz ? 576 : 480;
-
- if (pix->height != Vsrc) {
- VSCY = (int)((1024 * Vsrc) / pix->height);
- v4l_dbg(1, debug, client, "Vsrc: %d, Vscy: 0x%05x\n", Vsrc, VSCY);
-
- /* Correct Contrast and Luminance */
- saa7115_write(client, 0xd5, (u8) (64 * 1024 / VSCY));
- saa7115_write(client, 0xd6, (u8) (64 * 1024 / VSCY));
-
- /* write V fine-scaling (luminance) */
- saa7115_write(client, 0xe0, (u8) (VSCY & 0xff));
- saa7115_write(client, 0xe1, (u8) ((VSCY >> 8) & 0xff));
- /* write V fine-scaling (chrominance) */
- saa7115_write(client, 0xe2, (u8) (VSCY & 0xff));
- saa7115_write(client, 0xe3, (u8) ((VSCY >> 8) & 0xff));
- } else {
- if (is_50hz) {
- v4l_dbg(1, debug, client, "Setting full 50Hz height\n");
- saa7115_writeregs(client, saa7115_cfg_50hz_fullres_y);
- } else {
- v4l_dbg(1, debug, client, "Setting full 60hz height\n");
- saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
- }
- }
-
- saa7115_writeregs(client, saa7115_cfg_reset_scaler);
- return 0;
+ return saa711x_set_size(client,fmt->fmt.pix.width,fmt->fmt.pix.height);
}
/* Decode the sliced VBI data stream as created by the saa7115.
The format is described in the saa7115 datasheet in Tables 25 and 26
and in Figure 33.
The current implementation uses SAV/EAV codes and not the ancillary data
- headers. The vbi->p pointer points to the SDID byte right after the SAV
+ headers. The vbi->p pointer points to the R_5E_SDID byte right after the SAV
code. */
-static void saa7115_decode_vbi_line(struct i2c_client *client,
+static void saa711x_decode_vbi_line(struct i2c_client *client,
struct v4l2_decode_vbi_line *vbi)
{
static const char vbi_no_data_pattern[] = {
0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0, 0xa0
};
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
u8 *p = vbi->p;
u32 wss;
int id1, id2; /* the ID1 and ID2 bytes from the internal header */
@@ -1066,12 +1202,12 @@ static void saa7115_decode_vbi_line(struct i2c_client *client,
vbi->type = V4L2_SLICED_TELETEXT_B;
break;
case 4:
- if (!saa7115_odd_parity(p[0]) || !saa7115_odd_parity(p[1]))
+ if (!saa711x_odd_parity(p[0]) || !saa711x_odd_parity(p[1]))
return;
vbi->type = V4L2_SLICED_CAPTION_525;
break;
case 5:
- wss = saa7115_decode_wss(p);
+ wss = saa711x_decode_wss(p);
if (wss == -1)
return;
p[0] = wss & 0xff;
@@ -1079,7 +1215,7 @@ static void saa7115_decode_vbi_line(struct i2c_client *client,
vbi->type = V4L2_SLICED_WSS_625;
break;
case 7:
- if (saa7115_decode_vps(p, p) != 0)
+ if (saa711x_decode_vps(p, p) != 0)
return;
vbi->type = V4L2_SLICED_VPS;
break;
@@ -1090,21 +1226,21 @@ static void saa7115_decode_vbi_line(struct i2c_client *client,
/* ============ SAA7115 AUDIO settings (end) ============= */
-static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *arg)
+static int saa711x_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
int *iarg = arg;
/* ioctls to allow direct access to the saa7115 registers for testing */
switch (cmd) {
case VIDIOC_S_FMT:
- return saa7115_set_v4lfmt(client, (struct v4l2_format *)arg);
+ return saa711x_set_v4lfmt(client, (struct v4l2_format *)arg);
case VIDIOC_G_FMT:
- return saa7115_get_v4lfmt(client, (struct v4l2_format *)arg);
+ return saa711x_get_v4lfmt(client, (struct v4l2_format *)arg);
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
- return saa7115_set_audio_clock_freq(client, *(u32 *)arg);
+ return saa711x_set_audio_clock_freq(client, *(u32 *)arg);
case VIDIOC_G_TUNER:
{
@@ -1113,7 +1249,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
if (state->radio)
break;
- status = saa7115_read(client, 0x1f);
+ status = saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC);
v4l_dbg(1, debug, client, "status: 0x%02x\n", status);
vt->signal = ((status & (1 << 6)) == 0) ? 0xffff : 0x0;
@@ -1121,14 +1257,14 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
}
case VIDIOC_LOG_STATUS:
- saa7115_log_status(client);
+ saa711x_log_status(client);
break;
case VIDIOC_G_CTRL:
- return saa7115_get_v4lctrl(client, (struct v4l2_control *)arg);
+ return saa711x_get_v4lctrl(client, (struct v4l2_control *)arg);
case VIDIOC_S_CTRL:
- return saa7115_set_v4lctrl(client, (struct v4l2_control *)arg);
+ return saa711x_set_v4lctrl(client, (struct v4l2_control *)arg);
case VIDIOC_QUERYCTRL:
{
@@ -1146,12 +1282,12 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
}
case VIDIOC_G_STD:
- *(v4l2_std_id *)arg = saa7115_get_v4lstd(client);
+ *(v4l2_std_id *)arg = saa711x_get_v4lstd(client);
break;
case VIDIOC_S_STD:
state->radio = 0;
- saa7115_set_v4lstd(client, *(v4l2_std_id *)arg);
+ saa711x_set_v4lstd(client, *(v4l2_std_id *)arg);
break;
case AUDC_SET_RADIO:
@@ -1187,13 +1323,13 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
state->input = route->input;
/* select mode */
- saa7115_write(client, 0x02,
- (saa7115_read(client, 0x02) & 0xf0) |
+ saa711x_write(client, R_02_INPUT_CNTL_1,
+ (saa711x_read(client, R_02_INPUT_CNTL_1) & 0xf0) |
state->input);
/* bypass chrominance trap for S-Video modes */
- saa7115_write(client, 0x09,
- (saa7115_read(client, 0x09) & 0x7f) |
+ saa711x_write(client, R_09_LUMA_CNTL,
+ (saa711x_read(client, R_09_LUMA_CNTL) & 0x7f) |
(state->input >= SAA7115_SVIDEO0 ? 0x80 : 0x0));
break;
}
@@ -1205,7 +1341,9 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
if (state->enable != (cmd == VIDIOC_STREAMON)) {
state->enable = (cmd == VIDIOC_STREAMON);
- saa7115_write(client, 0x87, state->enable);
+ saa711x_write(client,
+ R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED,
+ state->enable);
}
break;
@@ -1220,17 +1358,17 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
state->cgcdiv = (freq->flags & SAA7115_FREQ_FL_CGCDIV) ? 3 : 4;
state->ucgc = (freq->flags & SAA7115_FREQ_FL_UCGC) ? 1 : 0;
state->apll = (freq->flags & SAA7115_FREQ_FL_APLL) ? 1 : 0;
- saa7115_set_audio_clock_freq(client, state->audclk_freq);
+ saa711x_set_audio_clock_freq(client, state->audclk_freq);
break;
}
case VIDIOC_INT_DECODE_VBI_LINE:
- saa7115_decode_vbi_line(client, arg);
+ saa711x_decode_vbi_line(client, arg);
break;
case VIDIOC_INT_RESET:
v4l_dbg(1, debug, client, "decoder RESET\n");
- saa7115_writeregs(client, saa7115_cfg_reset_scaler);
+ saa711x_writeregs(client, saa7115_cfg_reset_scaler);
break;
case VIDIOC_INT_G_VBI_DATA:
@@ -1239,25 +1377,25 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
switch (data->id) {
case V4L2_SLICED_WSS_625:
- if (saa7115_read(client, 0x6b) & 0xc0)
+ if (saa711x_read(client, 0x6b) & 0xc0)
return -EIO;
- data->data[0] = saa7115_read(client, 0x6c);
- data->data[1] = saa7115_read(client, 0x6d);
+ data->data[0] = saa711x_read(client, 0x6c);
+ data->data[1] = saa711x_read(client, 0x6d);
return 0;
case V4L2_SLICED_CAPTION_525:
if (data->field == 0) {
/* CC */
- if (saa7115_read(client, 0x66) & 0xc0)
+ if (saa711x_read(client, 0x66) & 0xc0)
return -EIO;
- data->data[0] = saa7115_read(client, 0x67);
- data->data[1] = saa7115_read(client, 0x68);
+ data->data[0] = saa711x_read(client, 0x67);
+ data->data[1] = saa711x_read(client, 0x68);
return 0;
}
/* XDS */
- if (saa7115_read(client, 0x66) & 0x30)
+ if (saa711x_read(client, 0x66) & 0x30)
return -EIO;
- data->data[0] = saa7115_read(client, 0x69);
- data->data[1] = saa7115_read(client, 0x6a);
+ data->data[0] = saa711x_read(client, 0x69);
+ data->data[1] = saa711x_read(client, 0x6a);
return 0;
default:
return -EINVAL;
@@ -1272,7 +1410,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
if (reg->i2c_id != I2C_DRIVERID_SAA711X)
return -EINVAL;
- reg->val = saa7115_read(client, reg->reg & 0xff);
+ reg->val = saa711x_read(client, reg->reg & 0xff);
break;
}
@@ -1284,7 +1422,7 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
return -EINVAL;
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
- saa7115_write(client, reg->reg & 0xff, reg->val & 0xff);
+ saa711x_write(client, reg->reg & 0xff, reg->val & 0xff);
break;
}
#endif
@@ -1302,12 +1440,14 @@ static int saa7115_command(struct i2c_client *client, unsigned int cmd, void *ar
/* ----------------------------------------------------------------------- */
-static struct i2c_driver i2c_driver_saa7115;
+static struct i2c_driver i2c_driver_saa711x;
-static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
+static int saa711x_attach(struct i2c_adapter *adapter, int address, int kind)
{
struct i2c_client *client;
- struct saa7115_state *state;
+ struct saa711x_state *state;
+ int i;
+ char name[17];
u8 chip_id;
/* Check if the adapter supports the needed features */
@@ -1319,28 +1459,31 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
return -ENOMEM;
client->addr = address;
client->adapter = adapter;
- client->driver = &i2c_driver_saa7115;
+ client->driver = &i2c_driver_saa711x;
snprintf(client->name, sizeof(client->name) - 1, "saa7115");
v4l_dbg(1, debug, client, "detecting saa7115 client on address 0x%x\n", address << 1);
- saa7115_write(client, 0, 5);
- chip_id = saa7115_read(client, 0) & 0x0f;
- if (chip_id < 3 && chip_id > 5) {
- v4l_dbg(1, debug, client, "saa7115 not found\n");
- kfree(client);
- return 0;
+ for (i=0;i<0x0f;i++) {
+ saa711x_write(client, 0, i);
+ name[i] = (saa711x_read(client, 0) &0x0f) +'0';
+ if (name[i]>'9')
+ name[i]+='a'-'9'-1;
}
+ name[i]='\0';
+
+ saa711x_write(client, 0, 5);
+ chip_id = saa711x_read(client, 0) & 0x0f;
+
snprintf(client->name, sizeof(client->name) - 1, "saa711%d",chip_id);
- v4l_info(client, "saa711%d found @ 0x%x (%s)\n", chip_id, address << 1, adapter->name);
+ v4l_info(client, "saa711%d found (%s) @ 0x%x (%s)\n", chip_id, name, address << 1, adapter->name);
- state = kzalloc(sizeof(struct saa7115_state), GFP_KERNEL);
+ state = kzalloc(sizeof(struct saa711x_state), GFP_KERNEL);
i2c_set_clientdata(client, state);
if (state == NULL) {
kfree(client);
return -ENOMEM;
}
- state->std = V4L2_STD_NTSC;
state->input = -1;
state->enable = 1;
state->radio = 0;
@@ -1349,15 +1492,25 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
state->hue = 0;
state->sat = 64;
switch (chip_id) {
+ case 1:
+ state->ident = V4L2_IDENT_SAA7111;
+ break;
case 3:
state->ident = V4L2_IDENT_SAA7113;
break;
case 4:
state->ident = V4L2_IDENT_SAA7114;
break;
- default:
+ case 5:
state->ident = V4L2_IDENT_SAA7115;
break;
+ case 8:
+ state->ident = V4L2_IDENT_SAA7118;
+ break;
+ default:
+ state->ident = V4L2_IDENT_SAA7111;
+ v4l_info(client, "WARNING: Chip is not known - Falling back to saa7111\n");
+
}
state->audclk_freq = 48000;
@@ -1365,38 +1518,39 @@ static int saa7115_attach(struct i2c_adapter *adapter, int address, int kind)
v4l_dbg(1, debug, client, "writing init values\n");
/* init to 60hz/48khz */
- if (state->ident == V4L2_IDENT_SAA7113) {
- state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
- saa7115_writeregs(client, saa7113_init_auto_input);
- } else {
+ state->crystal_freq = SAA7115_FREQ_24_576_MHZ;
+ switch (state->ident) {
+ case V4L2_IDENT_SAA7111:
+ saa711x_writeregs(client, saa7111_init);
+ break;
+ case V4L2_IDENT_SAA7113:
+ saa711x_writeregs(client, saa7113_init);
+ break;
+ default:
state->crystal_freq = SAA7115_FREQ_32_11_MHZ;
- saa7115_writeregs(client, saa7115_init_auto_input);
+ saa711x_writeregs(client, saa7115_init_auto_input);
}
- saa7115_writeregs(client, saa7115_init_misc);
- saa7115_writeregs(client, saa7115_cfg_60hz_fullres_x);
- saa7115_writeregs(client, saa7115_cfg_60hz_fullres_y);
- saa7115_writeregs(client, saa7115_cfg_60hz_video);
- saa7115_set_audio_clock_freq(client, state->audclk_freq);
- saa7115_writeregs(client, saa7115_cfg_reset_scaler);
+ saa711x_writeregs(client, saa7115_init_misc);
+ saa711x_set_v4lstd(client, V4L2_STD_NTSC);
i2c_attach_client(client);
v4l_dbg(1, debug, client, "status: (1E) 0x%02x, (1F) 0x%02x\n",
- saa7115_read(client, 0x1e), saa7115_read(client, 0x1f));
+ saa711x_read(client, R_1E_STATUS_BYTE_1_VD_DEC), saa711x_read(client, R_1F_STATUS_BYTE_2_VD_DEC));
return 0;
}
-static int saa7115_probe(struct i2c_adapter *adapter)
+static int saa711x_probe(struct i2c_adapter *adapter)
{
if (adapter->class & I2C_CLASS_TV_ANALOG)
- return i2c_probe(adapter, &addr_data, &saa7115_attach);
+ return i2c_probe(adapter, &addr_data, &saa711x_attach);
return 0;
}
-static int saa7115_detach(struct i2c_client *client)
+static int saa711x_detach(struct i2c_client *client)
{
- struct saa7115_state *state = i2c_get_clientdata(client);
+ struct saa711x_state *state = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
@@ -1412,26 +1566,26 @@ static int saa7115_detach(struct i2c_client *client)
/* ----------------------------------------------------------------------- */
/* i2c implementation */
-static struct i2c_driver i2c_driver_saa7115 = {
+static struct i2c_driver i2c_driver_saa711x = {
.driver = {
.name = "saa7115",
},
.id = I2C_DRIVERID_SAA711X,
- .attach_adapter = saa7115_probe,
- .detach_client = saa7115_detach,
- .command = saa7115_command,
+ .attach_adapter = saa711x_probe,
+ .detach_client = saa711x_detach,
+ .command = saa711x_command,
};
-static int __init saa7115_init_module(void)
+static int __init saa711x_init_module(void)
{
- return i2c_add_driver(&i2c_driver_saa7115);
+ return i2c_add_driver(&i2c_driver_saa711x);
}
-static void __exit saa7115_cleanup_module(void)
+static void __exit saa711x_cleanup_module(void)
{
- i2c_del_driver(&i2c_driver_saa7115);
+ i2c_del_driver(&i2c_driver_saa711x);
}
-module_init(saa7115_init_module);
-module_exit(saa7115_cleanup_module);
+module_init(saa711x_init_module);
+module_exit(saa711x_cleanup_module);
diff --git a/drivers/media/video/saa711x_regs.h b/drivers/media/video/saa711x_regs.h
new file mode 100644
index 00000000000..4e5f2eb0a2c
--- /dev/null
+++ b/drivers/media/video/saa711x_regs.h
@@ -0,0 +1,549 @@
+/* saa711x - Philips SAA711x video decoder register specifications
+ *
+ * Copyright (c) 2006 Mauro Carvalho Chehab <mchehab@infradead.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define R_00_CHIP_VERSION 0x00
+/* Video Decoder */
+ /* Video Decoder - Frontend part */
+#define R_01_INC_DELAY 0x01
+#define R_02_INPUT_CNTL_1 0x02
+#define R_03_INPUT_CNTL_2 0x03
+#define R_04_INPUT_CNTL_3 0x04
+#define R_05_INPUT_CNTL_4 0x05
+ /* Video Decoder - Decoder part */
+#define R_06_H_SYNC_START 0x06
+#define R_07_H_SYNC_STOP 0x07
+#define R_08_SYNC_CNTL 0x08
+#define R_09_LUMA_CNTL 0x09
+#define R_0A_LUMA_BRIGHT_CNTL 0x0a
+#define R_0B_LUMA_CONTRAST_CNTL 0x0b
+#define R_0C_CHROMA_SAT_CNTL 0x0c
+#define R_0D_CHROMA_HUE_CNTL 0x0d
+#define R_0E_CHROMA_CNTL_1 0x0e
+#define R_0F_CHROMA_GAIN_CNTL 0x0f
+#define R_10_CHROMA_CNTL_2 0x10
+#define R_11_MODE_DELAY_CNTL 0x11
+#define R_12_RT_SIGNAL_CNTL 0x12
+#define R_13_RT_X_PORT_OUT_CNTL 0x13
+#define R_14_ANAL_ADC_COMPAT_CNTL 0x14
+#define R_15_VGATE_START_FID_CHG 0x15
+#define R_16_VGATE_STOP 0x16
+#define R_17_MISC_VGATE_CONF_AND_MSB 0x17
+#define R_18_RAW_DATA_GAIN_CNTL 0x18
+#define R_19_RAW_DATA_OFF_CNTL 0x19
+#define R_1A_COLOR_KILL_LVL_CNTL 0x1a
+#define R_1B_MISC_TVVCRDET 0x1b
+#define R_1C_ENHAN_COMB_CTRL1 0x1c
+#define R_1D_ENHAN_COMB_CTRL2 0x1d
+#define R_1E_STATUS_BYTE_1_VD_DEC 0x1e
+#define R_1F_STATUS_BYTE_2_VD_DEC 0x1f
+
+/* Component processing and interrupt masking part */
+#define R_23_INPUT_CNTL_5 0x23
+#define R_24_INPUT_CNTL_6 0x24
+#define R_25_INPUT_CNTL_7 0x25
+#define R_29_COMP_DELAY 0x29
+#define R_2A_COMP_BRIGHT_CNTL 0x2a
+#define R_2B_COMP_CONTRAST_CNTL 0x2b
+#define R_2C_COMP_SAT_CNTL 0x2c
+#define R_2D_INTERRUPT_MASK_1 0x2d
+#define R_2E_INTERRUPT_MASK_2 0x2e
+#define R_2F_INTERRUPT_MASK_3 0x2f
+
+/* Audio clock generator part */
+#define R_30_AUD_MAST_CLK_CYCLES_PER_FIELD 0x30
+#define R_34_AUD_MAST_CLK_NOMINAL_INC 0x34
+#define R_38_CLK_RATIO_AMXCLK_TO_ASCLK 0x38
+#define R_39_CLK_RATIO_ASCLK_TO_ALRCLK 0x39
+#define R_3A_AUD_CLK_GEN_BASIC_SETUP 0x3a
+
+/* General purpose VBI data slicer part */
+#define R_40_SLICER_CNTL_1 0x40
+#define R_41_LCR_BASE 0x41
+#define R_58_PROGRAM_FRAMING_CODE 0x58
+#define R_59_H_OFF_FOR_SLICER 0x59
+#define R_5A_V_OFF_FOR_SLICER 0x5a
+#define R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF 0x5b
+#define R_5D_DID 0x5d
+#define R_5E_SDID 0x5e
+#define R_60_SLICER_STATUS_BYTE_0 0x60
+#define R_61_SLICER_STATUS_BYTE_1 0x61
+#define R_62_SLICER_STATUS_BYTE_2 0x62
+
+/* X port, I port and the scaler part */
+ /* Task independent global settings */
+#define R_80_GLOBAL_CNTL_1 0x80
+#define R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F 0x81
+#define R_83_X_PORT_I_O_ENA_AND_OUT_CLK 0x83
+#define R_84_I_PORT_SIGNAL_DEF 0x84
+#define R_85_I_PORT_SIGNAL_POLAR 0x85
+#define R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT 0x86
+#define R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED 0x87
+#define R_88_POWER_SAVE_ADC_PORT_CNTL 0x88
+#define R_8F_STATUS_INFO_SCALER 0x8f
+ /* Task A definition */
+ /* Basic settings and acquisition window definition */
+#define R_90_A_TASK_HANDLING_CNTL 0x90
+#define R_91_A_X_PORT_FORMATS_AND_CONF 0x91
+#define R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL 0x92
+#define R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF 0x93
+#define R_94_A_HORIZ_INPUT_WINDOW_START 0x94
+#define R_95_A_HORIZ_INPUT_WINDOW_START_MSB 0x95
+#define R_96_A_HORIZ_INPUT_WINDOW_LENGTH 0x96
+#define R_97_A_HORIZ_INPUT_WINDOW_LENGTH_MSB 0x97
+#define R_98_A_VERT_INPUT_WINDOW_START 0x98
+#define R_99_A_VERT_INPUT_WINDOW_START_MSB 0x99
+#define R_9A_A_VERT_INPUT_WINDOW_LENGTH 0x9a
+#define R_9B_A_VERT_INPUT_WINDOW_LENGTH_MSB 0x9b
+#define R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH 0x9c
+#define R_9D_A_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0x9d
+#define R_9E_A_VERT_OUTPUT_WINDOW_LENGTH 0x9e
+#define R_9F_A_VERT_OUTPUT_WINDOW_LENGTH_MSB 0x9f
+ /* FIR filtering and prescaling */
+#define R_A0_A_HORIZ_PRESCALING 0xa0
+#define R_A1_A_ACCUMULATION_LENGTH 0xa1
+#define R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xa2
+#define R_A4_A_LUMA_BRIGHTNESS_CNTL 0xa4
+#define R_A5_A_LUMA_CONTRAST_CNTL 0xa5
+#define R_A6_A_CHROMA_SATURATION_CNTL 0xa6
+ /* Horizontal phase scaling */
+#define R_A8_A_HORIZ_LUMA_SCALING_INC 0xa8
+#define R_A9_A_HORIZ_LUMA_SCALING_INC_MSB 0xa9
+#define R_AA_A_HORIZ_LUMA_PHASE_OFF 0xaa
+#define R_AC_A_HORIZ_CHROMA_SCALING_INC 0xac
+#define R_AD_A_HORIZ_CHROMA_SCALING_INC_MSB 0xad
+#define R_AE_A_HORIZ_CHROMA_PHASE_OFF 0xae
+#define R_AF_A_HORIZ_CHROMA_PHASE_OFF_MSB 0xaf
+ /* Vertical scaling */
+#define R_B0_A_VERT_LUMA_SCALING_INC 0xb0
+#define R_B1_A_VERT_LUMA_SCALING_INC_MSB 0xb1
+#define R_B2_A_VERT_CHROMA_SCALING_INC 0xb2
+#define R_B3_A_VERT_CHROMA_SCALING_INC_MSB 0xb3
+#define R_B4_A_VERT_SCALING_MODE_CNTL 0xb4
+#define R_B8_A_VERT_CHROMA_PHASE_OFF_00 0xb8
+#define R_B9_A_VERT_CHROMA_PHASE_OFF_01 0xb9
+#define R_BA_A_VERT_CHROMA_PHASE_OFF_10 0xba
+#define R_BB_A_VERT_CHROMA_PHASE_OFF_11 0xbb
+#define R_BC_A_VERT_LUMA_PHASE_OFF_00 0xbc
+#define R_BD_A_VERT_LUMA_PHASE_OFF_01 0xbd
+#define R_BE_A_VERT_LUMA_PHASE_OFF_10 0xbe
+#define R_BF_A_VERT_LUMA_PHASE_OFF_11 0xbf
+ /* Task B definition */
+ /* Basic settings and acquisition window definition */
+#define R_C0_B_TASK_HANDLING_CNTL 0xc0
+#define R_C1_B_X_PORT_FORMATS_AND_CONF 0xc1
+#define R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION 0xc2
+#define R_C3_B_I_PORT_FORMATS_AND_CONF 0xc3
+#define R_C4_B_HORIZ_INPUT_WINDOW_START 0xc4
+#define R_C5_B_HORIZ_INPUT_WINDOW_START_MSB 0xc5
+#define R_C6_B_HORIZ_INPUT_WINDOW_LENGTH 0xc6
+#define R_C7_B_HORIZ_INPUT_WINDOW_LENGTH_MSB 0xc7
+#define R_C8_B_VERT_INPUT_WINDOW_START 0xc8
+#define R_C9_B_VERT_INPUT_WINDOW_START_MSB 0xc9
+#define R_CA_B_VERT_INPUT_WINDOW_LENGTH 0xca
+#define R_CB_B_VERT_INPUT_WINDOW_LENGTH_MSB 0xcb
+#define R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH 0xcc
+#define R_CD_B_HORIZ_OUTPUT_WINDOW_LENGTH_MSB 0xcd
+#define R_CE_B_VERT_OUTPUT_WINDOW_LENGTH 0xce
+#define R_CF_B_VERT_OUTPUT_WINDOW_LENGTH_MSB 0xcf
+ /* FIR filtering and prescaling */
+#define R_D0_B_HORIZ_PRESCALING 0xd0
+#define R_D1_B_ACCUMULATION_LENGTH 0xd1
+#define R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER 0xd2
+#define R_D4_B_LUMA_BRIGHTNESS_CNTL 0xd4
+#define R_D5_B_LUMA_CONTRAST_CNTL 0xd5
+#define R_D6_B_CHROMA_SATURATION_CNTL 0xd6
+ /* Horizontal phase scaling */
+#define R_D8_B_HORIZ_LUMA_SCALING_INC 0xd8
+#define R_D9_B_HORIZ_LUMA_SCALING_INC_MSB 0xd9
+#define R_DA_B_HORIZ_LUMA_PHASE_OFF 0xda
+#define R_DC_B_HORIZ_CHROMA_SCALING 0xdc
+#define R_DD_B_HORIZ_CHROMA_SCALING_MSB 0xdd
+#define R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA 0xde
+ /* Vertical scaling */
+#define R_E0_B_VERT_LUMA_SCALING_INC 0xe0
+#define R_E1_B_VERT_LUMA_SCALING_INC_MSB 0xe1
+#define R_E2_B_VERT_CHROMA_SCALING_INC 0xe2
+#define R_E3_B_VERT_CHROMA_SCALING_INC_MSB 0xe3
+#define R_E4_B_VERT_SCALING_MODE_CNTL 0xe4
+#define R_E8_B_VERT_CHROMA_PHASE_OFF_00 0xe8
+#define R_E9_B_VERT_CHROMA_PHASE_OFF_01 0xe9
+#define R_EA_B_VERT_CHROMA_PHASE_OFF_10 0xea
+#define R_EB_B_VERT_CHROMA_PHASE_OFF_11 0xeb
+#define R_EC_B_VERT_LUMA_PHASE_OFF_00 0xec
+#define R_ED_B_VERT_LUMA_PHASE_OFF_01 0xed
+#define R_EE_B_VERT_LUMA_PHASE_OFF_10 0xee
+#define R_EF_B_VERT_LUMA_PHASE_OFF_11 0xef
+
+/* second PLL (PLL2) and Pulsegenerator Programming */
+#define R_F0_LFCO_PER_LINE 0xf0
+#define R_F1_P_I_PARAM_SELECT 0xf1
+#define R_F2_NOMINAL_PLL2_DTO 0xf2
+#define R_F3_PLL_INCREMENT 0xf3
+#define R_F4_PLL2_STATUS 0xf4
+#define R_F5_PULSGEN_LINE_LENGTH 0xf5
+#define R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG 0xf6
+#define R_F7_PULSE_A_POS_MSB 0xf7
+#define R_F8_PULSE_B_POS 0xf8
+#define R_F9_PULSE_B_POS_MSB 0xf9
+#define R_FA_PULSE_C_POS 0xfa
+#define R_FB_PULSE_C_POS_MSB 0xfb
+#define R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES 0xff
+
+#if 0
+/* Those structs will be used in the future for debug purposes */
+struct saa711x_reg_descr {
+ u8 reg;
+ int count;
+ char *name;
+};
+
+struct saa711x_reg_descr saa711x_regs[] = {
+ /* REG COUNT NAME */
+ {R_00_CHIP_VERSION,1,
+ "Chip version"},
+
+ /* Video Decoder: R_01_INC_DELAY to R_1F_STATUS_BYTE_2_VD_DEC */
+
+ /* Video Decoder - Frontend part: R_01_INC_DELAY to R_05_INPUT_CNTL_4 */
+ {R_01_INC_DELAY,1,
+ "Increment delay"},
+ {R_02_INPUT_CNTL_1,1,
+ "Analog input control 1"},
+ {R_03_INPUT_CNTL_2,1,
+ "Analog input control 2"},
+ {R_04_INPUT_CNTL_3,1,
+ "Analog input control 3"},
+ {R_05_INPUT_CNTL_4,1,
+ "Analog input control 4"},
+
+ /* Video Decoder - Decoder part: R_06_H_SYNC_START to R_1F_STATUS_BYTE_2_VD_DEC */
+ {R_06_H_SYNC_START,1,
+ "Horizontal sync start"},
+ {R_07_H_SYNC_STOP,1,
+ "Horizontal sync stop"},
+ {R_08_SYNC_CNTL,1,
+ "Sync control"},
+ {R_09_LUMA_CNTL,1,
+ "Luminance control"},
+ {R_0A_LUMA_BRIGHT_CNTL,1,
+ "Luminance brightness control"},
+ {R_0B_LUMA_CONTRAST_CNTL,1,
+ "Luminance contrast control"},
+ {R_0C_CHROMA_SAT_CNTL,1,
+ "Chrominance saturation control"},
+ {R_0D_CHROMA_HUE_CNTL,1,
+ "Chrominance hue control"},
+ {R_0E_CHROMA_CNTL_1,1,
+ "Chrominance control 1"},
+ {R_0F_CHROMA_GAIN_CNTL,1,
+ "Chrominance gain control"},
+ {R_10_CHROMA_CNTL_2,1,
+ "Chrominance control 2"},
+ {R_11_MODE_DELAY_CNTL,1,
+ "Mode/delay control"},
+ {R_12_RT_SIGNAL_CNTL,1,
+ "RT signal control"},
+ {R_13_RT_X_PORT_OUT_CNTL,1,
+ "RT/X port output control"},
+ {R_14_ANAL_ADC_COMPAT_CNTL,1,
+ "Analog/ADC/compatibility control"},
+ {R_15_VGATE_START_FID_CHG, 1,
+ "VGATE start FID change"},
+ {R_16_VGATE_STOP,1,
+ "VGATE stop"},
+ {R_17_MISC_VGATE_CONF_AND_MSB, 1,
+ "Miscellaneous VGATE configuration and MSBs"},
+ {R_18_RAW_DATA_GAIN_CNTL,1,
+ "Raw data gain control",},
+ {R_19_RAW_DATA_OFF_CNTL,1,
+ "Raw data offset control",},
+ {R_1A_COLOR_KILL_LVL_CNTL,1,
+ "Color Killer Level Control"},
+ { R_1B_MISC_TVVCRDET, 1,
+ "MISC /TVVCRDET"},
+ { R_1C_ENHAN_COMB_CTRL1, 1,
+ "Enhanced comb ctrl1"},
+ { R_1D_ENHAN_COMB_CTRL2, 1,
+ "Enhanced comb ctrl1"},
+ {R_1E_STATUS_BYTE_1_VD_DEC,1,
+ "Status byte 1 video decoder"},
+ {R_1F_STATUS_BYTE_2_VD_DEC,1,
+ "Status byte 2 video decoder"},
+
+ /* Component processing and interrupt masking part: 0x20h to R_2F_INTERRUPT_MASK_3 */
+ /* 0x20 to 0x22 - Reserved */
+ {R_23_INPUT_CNTL_5,1,
+ "Analog input control 5"},
+ {R_24_INPUT_CNTL_6,1,
+ "Analog input control 6"},
+ {R_25_INPUT_CNTL_7,1,
+ "Analog input control 7"},
+ /* 0x26 to 0x28 - Reserved */
+ {R_29_COMP_DELAY,1,
+ "Component delay"},
+ {R_2A_COMP_BRIGHT_CNTL,1,
+ "Component brightness control"},
+ {R_2B_COMP_CONTRAST_CNTL,1,
+ "Component contrast control"},
+ {R_2C_COMP_SAT_CNTL,1,
+ "Component saturation control"},
+ {R_2D_INTERRUPT_MASK_1,1,
+ "Interrupt mask 1"},
+ {R_2E_INTERRUPT_MASK_2,1,
+ "Interrupt mask 2"},
+ {R_2F_INTERRUPT_MASK_3,1,
+ "Interrupt mask 3"},
+
+ /* Audio clock generator part: R_30_AUD_MAST_CLK_CYCLES_PER_FIELD to 0x3f */
+ {R_30_AUD_MAST_CLK_CYCLES_PER_FIELD,3,
+ "Audio master clock cycles per field"},
+ /* 0x33 - Reserved */
+ {R_34_AUD_MAST_CLK_NOMINAL_INC,3,
+ "Audio master clock nominal increment"},
+ /* 0x37 - Reserved */
+ {R_38_CLK_RATIO_AMXCLK_TO_ASCLK,1,
+ "Clock ratio AMXCLK to ASCLK"},
+ {R_39_CLK_RATIO_ASCLK_TO_ALRCLK,1,
+ "Clock ratio ASCLK to ALRCLK"},
+ {R_3A_AUD_CLK_GEN_BASIC_SETUP,1,
+ "Audio clock generator basic setup"},
+ /* 0x3b-0x3f - Reserved */
+
+ /* General purpose VBI data slicer part: R_40_SLICER_CNTL_1 to 0x7f */
+ {R_40_SLICER_CNTL_1,1,
+ "Slicer control 1"},
+ {R_41_LCR,23,
+ "R_41_LCR"},
+ {R_58_PROGRAM_FRAMING_CODE,1,
+ "Programmable framing code"},
+ {R_59_H_OFF_FOR_SLICER,1,
+ "Horizontal offset for slicer"},
+ {R_5A_V_OFF_FOR_SLICER,1,
+ "Vertical offset for slicer"},
+ {R_5B_FLD_OFF_AND_MSB_FOR_H_AND_V_OFF,1,
+ "Field offset and MSBs for horizontal and vertical offset"},
+ {R_5D_DID,1,
+ "Header and data identification (R_5D_DID)"},
+ {R_5E_SDID,1,
+ "Sliced data identification (R_5E_SDID) code"},
+ {R_60_SLICER_STATUS_BYTE_0,1,
+ "Slicer status byte 0"},
+ {R_61_SLICER_STATUS_BYTE_1,1,
+ "Slicer status byte 1"},
+ {R_62_SLICER_STATUS_BYTE_2,1,
+ "Slicer status byte 2"},
+ /* 0x63-0x7f - Reserved */
+
+ /* X port, I port and the scaler part: R_80_GLOBAL_CNTL_1 to R_EF_B_VERT_LUMA_PHASE_OFF_11 */
+ /* Task independent global settings: R_80_GLOBAL_CNTL_1 to R_8F_STATUS_INFO_SCALER */
+ {R_80_GLOBAL_CNTL_1,1,
+ "Global control 1"},
+ {R_81_V_SYNC_FLD_ID_SRC_SEL_AND_RETIMED_V_F,1,
+ "Vertical sync and Field ID source selection, retimed V and F signals"},
+ /* 0x82 - Reserved */
+ {R_83_X_PORT_I_O_ENA_AND_OUT_CLK,1,
+ "X port I/O enable and output clock"},
+ {R_84_I_PORT_SIGNAL_DEF,1,
+ "I port signal definitions"},
+ {R_85_I_PORT_SIGNAL_POLAR,1,
+ "I port signal polarities"},
+ {R_86_I_PORT_FIFO_FLAG_CNTL_AND_ARBIT,1,
+ "I port FIFO flag control and arbitration"},
+ {R_87_I_PORT_I_O_ENA_OUT_CLK_AND_GATED, 1,
+ "I port I/O enable output clock and gated"},
+ {R_88_POWER_SAVE_ADC_PORT_CNTL,1,
+ "Power save/ADC port control"},
+ /* 089-0x8e - Reserved */
+ {R_8F_STATUS_INFO_SCALER,1,
+ "Status information scaler part"},
+
+ /* Task A definition: R_90_A_TASK_HANDLING_CNTL to R_BF_A_VERT_LUMA_PHASE_OFF_11 */
+ /* Task A: Basic settings and acquisition window definition */
+ {R_90_A_TASK_HANDLING_CNTL,1,
+ "Task A: Task handling control"},
+ {R_91_A_X_PORT_FORMATS_AND_CONF,1,
+ "Task A: X port formats and configuration"},
+ {R_92_A_X_PORT_INPUT_REFERENCE_SIGNAL,1,
+ "Task A: X port input reference signal definition"},
+ {R_93_A_I_PORT_OUTPUT_FORMATS_AND_CONF,1,
+ "Task A: I port output formats and configuration"},
+ {R_94_A_HORIZ_INPUT_WINDOW_START,2,
+ "Task A: Horizontal input window start"},
+ {R_96_A_HORIZ_INPUT_WINDOW_LENGTH,2,
+ "Task A: Horizontal input window length"},
+ {R_98_A_VERT_INPUT_WINDOW_START,2,
+ "Task A: Vertical input window start"},
+ {R_9A_A_VERT_INPUT_WINDOW_LENGTH,2,
+ "Task A: Vertical input window length"},
+ {R_9C_A_HORIZ_OUTPUT_WINDOW_LENGTH,2,
+ "Task A: Horizontal output window length"},
+ {R_9E_A_VERT_OUTPUT_WINDOW_LENGTH,2,
+ "Task A: Vertical output window length"},
+
+ /* Task A: FIR filtering and prescaling */
+ {R_A0_A_HORIZ_PRESCALING,1,
+ "Task A: Horizontal prescaling"},
+ {R_A1_A_ACCUMULATION_LENGTH,1,
+ "Task A: Accumulation length"},
+ {R_A2_A_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1,
+ "Task A: Prescaler DC gain and FIR prefilter"},
+ /* 0xa3 - Reserved */
+ {R_A4_A_LUMA_BRIGHTNESS_CNTL,1,
+ "Task A: Luminance brightness control"},
+ {R_A5_A_LUMA_CONTRAST_CNTL,1,
+ "Task A: Luminance contrast control"},
+ {R_A6_A_CHROMA_SATURATION_CNTL,1,
+ "Task A: Chrominance saturation control"},
+ /* 0xa7 - Reserved */
+
+ /* Task A: Horizontal phase scaling */
+ {R_A8_A_HORIZ_LUMA_SCALING_INC,2,
+ "Task A: Horizontal luminance scaling increment"},
+ {R_AA_A_HORIZ_LUMA_PHASE_OFF,1,
+ "Task A: Horizontal luminance phase offset"},
+ /* 0xab - Reserved */
+ {R_AC_A_HORIZ_CHROMA_SCALING_INC,2,
+ "Task A: Horizontal chrominance scaling increment"},
+ {R_AE_A_HORIZ_CHROMA_PHASE_OFF,1,
+ "Task A: Horizontal chrominance phase offset"},
+ /* 0xaf - Reserved */
+
+ /* Task A: Vertical scaling */
+ {R_B0_A_VERT_LUMA_SCALING_INC,2,
+ "Task A: Vertical luminance scaling increment"},
+ {R_B2_A_VERT_CHROMA_SCALING_INC,2,
+ "Task A: Vertical chrominance scaling increment"},
+ {R_B4_A_VERT_SCALING_MODE_CNTL,1,
+ "Task A: Vertical scaling mode control"},
+ /* 0xb5-0xb7 - Reserved */
+ {R_B8_A_VERT_CHROMA_PHASE_OFF_00,1,
+ "Task A: Vertical chrominance phase offset '00'"},
+ {R_B9_A_VERT_CHROMA_PHASE_OFF_01,1,
+ "Task A: Vertical chrominance phase offset '01'"},
+ {R_BA_A_VERT_CHROMA_PHASE_OFF_10,1,
+ "Task A: Vertical chrominance phase offset '10'"},
+ {R_BB_A_VERT_CHROMA_PHASE_OFF_11,1,
+ "Task A: Vertical chrominance phase offset '11'"},
+ {R_BC_A_VERT_LUMA_PHASE_OFF_00,1,
+ "Task A: Vertical luminance phase offset '00'"},
+ {R_BD_A_VERT_LUMA_PHASE_OFF_01,1,
+ "Task A: Vertical luminance phase offset '01'"},
+ {R_BE_A_VERT_LUMA_PHASE_OFF_10,1,
+ "Task A: Vertical luminance phase offset '10'"},
+ {R_BF_A_VERT_LUMA_PHASE_OFF_11,1,
+ "Task A: Vertical luminance phase offset '11'"},
+
+ /* Task B definition: R_C0_B_TASK_HANDLING_CNTL to R_EF_B_VERT_LUMA_PHASE_OFF_11 */
+ /* Task B: Basic settings and acquisition window definition */
+ {R_C0_B_TASK_HANDLING_CNTL,1,
+ "Task B: Task handling control"},
+ {R_C1_B_X_PORT_FORMATS_AND_CONF,1,
+ "Task B: X port formats and configuration"},
+ {R_C2_B_INPUT_REFERENCE_SIGNAL_DEFINITION,1,
+ "Task B: Input reference signal definition"},
+ {R_C3_B_I_PORT_FORMATS_AND_CONF,1,
+ "Task B: I port formats and configuration"},
+ {R_C4_B_HORIZ_INPUT_WINDOW_START,2,
+ "Task B: Horizontal input window start"},
+ {R_C6_B_HORIZ_INPUT_WINDOW_LENGTH,2,
+ "Task B: Horizontal input window length"},
+ {R_C8_B_VERT_INPUT_WINDOW_START,2,
+ "Task B: Vertical input window start"},
+ {R_CA_B_VERT_INPUT_WINDOW_LENGTH,2,
+ "Task B: Vertical input window length"},
+ {R_CC_B_HORIZ_OUTPUT_WINDOW_LENGTH,2,
+ "Task B: Horizontal output window length"},
+ {R_CE_B_VERT_OUTPUT_WINDOW_LENGTH,2,
+ "Task B: Vertical output window length"},
+
+ /* Task B: FIR filtering and prescaling */
+ {R_D0_B_HORIZ_PRESCALING,1,
+ "Task B: Horizontal prescaling"},
+ {R_D1_B_ACCUMULATION_LENGTH,1,
+ "Task B: Accumulation length"},
+ {R_D2_B_PRESCALER_DC_GAIN_AND_FIR_PREFILTER,1,
+ "Task B: Prescaler DC gain and FIR prefilter"},
+ /* 0xd3 - Reserved */
+ {R_D4_B_LUMA_BRIGHTNESS_CNTL,1,
+ "Task B: Luminance brightness control"},
+ {R_D5_B_LUMA_CONTRAST_CNTL,1,
+ "Task B: Luminance contrast control"},
+ {R_D6_B_CHROMA_SATURATION_CNTL,1,
+ "Task B: Chrominance saturation control"},
+ /* 0xd7 - Reserved */
+
+ /* Task B: Horizontal phase scaling */
+ {R_D8_B_HORIZ_LUMA_SCALING_INC,2,
+ "Task B: Horizontal luminance scaling increment"},
+ {R_DA_B_HORIZ_LUMA_PHASE_OFF,1,
+ "Task B: Horizontal luminance phase offset"},
+ /* 0xdb - Reserved */
+ {R_DC_B_HORIZ_CHROMA_SCALING,2,
+ "Task B: Horizontal chrominance scaling"},
+ {R_DE_B_HORIZ_PHASE_OFFSET_CRHOMA,1,
+ "Task B: Horizontal Phase Offset Chroma"},
+ /* 0xdf - Reserved */
+
+ /* Task B: Vertical scaling */
+ {R_E0_B_VERT_LUMA_SCALING_INC,2,
+ "Task B: Vertical luminance scaling increment"},
+ {R_E2_B_VERT_CHROMA_SCALING_INC,2,
+ "Task B: Vertical chrominance scaling increment"},
+ {R_E4_B_VERT_SCALING_MODE_CNTL,1,
+ "Task B: Vertical scaling mode control"},
+ /* 0xe5-0xe7 - Reserved */
+ {R_E8_B_VERT_CHROMA_PHASE_OFF_00,1,
+ "Task B: Vertical chrominance phase offset '00'"},
+ {R_E9_B_VERT_CHROMA_PHASE_OFF_01,1,
+ "Task B: Vertical chrominance phase offset '01'"},
+ {R_EA_B_VERT_CHROMA_PHASE_OFF_10,1,
+ "Task B: Vertical chrominance phase offset '10'"},
+ {R_EB_B_VERT_CHROMA_PHASE_OFF_11,1,
+ "Task B: Vertical chrominance phase offset '11'"},
+ {R_EC_B_VERT_LUMA_PHASE_OFF_00,1,
+ "Task B: Vertical luminance phase offset '00'"},
+ {R_ED_B_VERT_LUMA_PHASE_OFF_01,1,
+ "Task B: Vertical luminance phase offset '01'"},
+ {R_EE_B_VERT_LUMA_PHASE_OFF_10,1,
+ "Task B: Vertical luminance phase offset '10'"},
+ {R_EF_B_VERT_LUMA_PHASE_OFF_11,1,
+ "Task B: Vertical luminance phase offset '11'"},
+
+ /* second PLL (PLL2) and Pulsegenerator Programming */
+ { R_F0_LFCO_PER_LINE, 1,
+ "LFCO's per line"},
+ { R_F1_P_I_PARAM_SELECT,1,
+ "P-/I- Param. Select., PLL Mode, PLL H-Src., LFCO's per line"},
+ { R_F2_NOMINAL_PLL2_DTO,1,
+ "Nominal PLL2 DTO"},
+ {R_F3_PLL_INCREMENT,1,
+ "PLL2 Increment"},
+ {R_F4_PLL2_STATUS,1,
+ "PLL2 Status"},
+ {R_F5_PULSGEN_LINE_LENGTH,1,
+ "Pulsgen. line length"},
+ {R_F6_PULSE_A_POS_LSB_AND_PULSEGEN_CONFIG,1,
+ "Pulse A Position, Pulsgen Resync., Pulsgen. H-Src., Pulsgen. line length"},
+ {R_F7_PULSE_A_POS_MSB,1,
+ "Pulse A Position"},
+ {R_F8_PULSE_B_POS,2,
+ "Pulse B Position"},
+ {R_FA_PULSE_C_POS,2,
+ "Pulse C Position"},
+ /* 0xfc to 0xfe - Reserved */
+ {R_FF_S_PLL_MAX_PHASE_ERR_THRESH_NUM_LINES,1,
+ "S_PLL max. phase, error threshold, PLL2 no. of lines, threshold"},
+};
+#endif
diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig
index f5543166d19..59da79ce2ef 100644
--- a/drivers/media/video/saa7134/Kconfig
+++ b/drivers/media/video/saa7134/Kconfig
@@ -41,53 +41,15 @@ config VIDEO_SAA7134_DVB
select VIDEO_BUF_DVB
select FW_LOADER
select DVB_PLL
+ select DVB_MT352 if !DVB_FE_CUSTOMISE
+ select DVB_TDA1004X if !DVB_FE_CUSTOMISE
+ select DVB_NXT200X if !DVB_FE_CUSTOMISE
+ select DVB_TDA10086 if !DVB_FE_CUSTOMISE
+ select DVB_TDA826X if !DVB_FE_CUSTOMISE
+ select DVB_ISL6421 if !DVB_FE_CUSTOMISE
---help---
This adds support for DVB cards based on the
Philips saa7134 chip.
To compile this driver as a module, choose M here: the
module will be called saa7134-dvb.
-
- You must also select one or more DVB demodulators.
- If you are unsure which you need, choose all of them.
-
-config VIDEO_SAA7134_DVB_ALL_FRONTENDS
- bool "Build all supported frontends for saa7134 based TV cards"
- default y
- depends on VIDEO_SAA7134_DVB
- select DVB_MT352
- select DVB_TDA1004X
- select DVB_NXT200X
- ---help---
- This builds saa7134-dvb with all currently supported frontend
- demodulators. If you wish to tweak your configuration, and
- only include support for the hardware that you need, choose N here.
-
- If you are unsure, choose Y.
-
-config VIDEO_SAA7134_DVB_MT352
- bool "Zarlink MT352 DVB-T Support"
- default y
- depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS
- select DVB_MT352
- ---help---
- This adds DVB-T support for cards based on the
- Philips saa7134 chip and the MT352 demodulator.
-
-config VIDEO_SAA7134_DVB_TDA1004X
- bool "Phillips TDA10045H/TDA10046H DVB-T Support"
- default y
- depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS
- select DVB_TDA1004X
- ---help---
- This adds DVB-T support for cards based on the
- Philips saa7134 chip and the TDA10045H/TDA10046H demodulator.
-
-config VIDEO_SAA7134_DVB_NXT200X
- bool "NXT2002/NXT2004 ATSC Support"
- default y
- depends on VIDEO_SAA7134_DVB && !VIDEO_SAA7134_DVB_ALL_FRONTENDS
- select DVB_NXT200X
- ---help---
- This adds ATSC 8VSB and QAM64/256 support for cards based on the
- Philips saa7134 chip and the NXT2002/NXT2004 demodulator.
diff --git a/drivers/media/video/saa7134/Makefile b/drivers/media/video/saa7134/Makefile
index be7b9ee697d..89a1565b425 100644
--- a/drivers/media/video/saa7134/Makefile
+++ b/drivers/media/video/saa7134/Makefile
@@ -16,8 +16,5 @@ EXTRA_CFLAGS += -Idrivers/media/dvb/dvb-core
EXTRA_CFLAGS += -Idrivers/media/dvb/frontends
extra-cflags-$(CONFIG_VIDEO_BUF_DVB) += -DHAVE_VIDEO_BUF_DVB=1
-extra-cflags-$(CONFIG_DVB_MT352) += -DHAVE_MT352=1
-extra-cflags-$(CONFIG_DVB_TDA1004X) += -DHAVE_TDA1004X=1
-extra-cflags-$(CONFIG_DVB_NXT200X) += -DHAVE_NXT200X=1
EXTRA_CFLAGS += $(extra-cflags-y) $(extra-cflags-m)
diff --git a/drivers/media/video/saa7134/saa7134-alsa.c b/drivers/media/video/saa7134/saa7134-alsa.c
index d73cff1970a..a39e0136ce3 100644
--- a/drivers/media/video/saa7134/saa7134-alsa.c
+++ b/drivers/media/video/saa7134/saa7134-alsa.c
@@ -590,6 +590,11 @@ static int snd_card_saa7134_hw_free(struct snd_pcm_substream * substream)
static int snd_card_saa7134_capture_close(struct snd_pcm_substream * substream)
{
+ snd_card_saa7134_t *saa7134 = snd_pcm_substream_chip(substream);
+ struct saa7134_dev *dev = saa7134->dev;
+
+ dev->ctl_mute = 1;
+ saa7134_tvaudio_setmute(dev);
return 0;
}
@@ -631,6 +636,9 @@ static int snd_card_saa7134_capture_open(struct snd_pcm_substream * substream)
runtime->private_free = snd_card_saa7134_runtime_free;
runtime->hw = snd_card_saa7134_capture;
+ dev->ctl_mute = 0;
+ saa7134_tvaudio_setmute(dev);
+
if ((err = snd_pcm_hw_constraint_integer(runtime, SNDRV_PCM_HW_PARAM_PERIODS)) < 0)
return err;
diff --git a/drivers/media/video/saa7134/saa7134-cards.c b/drivers/media/video/saa7134/saa7134-cards.c
index 927413aded1..aa1db509f3d 100644
--- a/drivers/media/video/saa7134/saa7134-cards.c
+++ b/drivers/media/video/saa7134/saa7134-cards.c
@@ -1911,7 +1911,7 @@ struct saa7134_board saa7134_boards[] = {
},
},
[SAA7134_BOARD_FLYDVBT_DUO_CARDBUS] = {
- .name = "LifeView/Typhoon FlyDVB-T Duo Cardbus",
+ .name = "LifeView/Typhoon/Genius FlyDVB-T Duo Cardbus",
.audio_clock = 0x00200000,
.tuner_type = TUNER_PHILIPS_TDA8290,
.radio_type = UNSET,
@@ -2891,6 +2891,80 @@ struct saa7134_board saa7134_boards[] = {
.gpio = 0x8000,
},
},
+ [SAA7134_BOARD_MEDION_MD8800_QUADRO] = {
+ .name = "Medion Md8800 Quadro",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_TDA8290,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = TV,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ },
+ [SAA7134_BOARD_FLYDVBS_LR300] = {
+ /* LifeView FlyDVB-s */
+ /* Igor M. Liplianin <liplianin@tut.by> */
+ .name = "LifeView FlyDVB-S /Acorp TV134DS",
+ .audio_clock = 0x00200000,
+ .tuner_type = TUNER_ABSENT,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .mpeg = SAA7134_MPEG_DVB,
+ .inputs = {{
+ .name = name_comp1, /* Composite input */
+ .vmux = 3,
+ .amux = LINE1,
+ },{
+ .name = name_svideo, /* S-Video signal on S-Video input */
+ .vmux = 8,
+ .amux = LINE1,
+ }},
+ },
+ [SAA7134_BOARD_PROTEUS_2309] = {
+ .name = "Proteus Pro 2309",
+ .audio_clock = 0x00187de7,
+ .tuner_type = TUNER_PHILIPS_FM1216ME_MK3,
+ .radio_type = UNSET,
+ .tuner_addr = ADDR_UNSET,
+ .radio_addr = ADDR_UNSET,
+ .tda9887_conf = TDA9887_PRESENT,
+ .inputs = {{
+ .name = name_tv,
+ .vmux = 1,
+ .amux = LINE2,
+ .tv = 1,
+ },{
+ .name = name_comp1,
+ .vmux = 0,
+ .amux = LINE2,
+ },{
+ .name = name_comp2,
+ .vmux = 3,
+ .amux = LINE2,
+ },{
+ .name = name_svideo,
+ .vmux = 8,
+ .amux = LINE2,
+ }},
+ .mute = {
+ .name = name_mute,
+ .amux = LINE1,
+ },
+ },
};
const unsigned int saa7134_bcount = ARRAY_SIZE(saa7134_boards);
@@ -3375,7 +3449,7 @@ struct pci_device_id saa7134_pci_tbl[] = {
.driver_data = SAA7134_BOARD_FLYDVB_TRIO,
},{
.vendor = PCI_VENDOR_ID_PHILIPS,
- .device = PCI_DEVICE_ID_PHILIPS_SAA7134, /* SAA 7131E */
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
.subvendor = 0x1461,
.subdevice = 0x2c05,
.driver_data = SAA7134_BOARD_AVERMEDIA_777,
@@ -3422,6 +3496,18 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x0005,
.driver_data = SAA7134_BOARD_MD7134_BRIDGE_2,
},{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x5168,
+ .subdevice = 0x0300,
+ .driver_data = SAA7134_BOARD_FLYDVBS_LR300,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7134,
+ .subvendor = 0x4e42,
+ .subdevice = 0x0300,/* LR300 */
+ .driver_data = SAA7134_BOARD_FLYDVBS_LR300,
+ },{
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
.subvendor = 0x1489,
@@ -3446,6 +3532,36 @@ struct pci_device_id saa7134_pci_tbl[] = {
.subdevice = 0x3502, /* whats the difference to 0x3306 ?*/
.driver_data = SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS,
},{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x0007,
+ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x16be,
+ .subdevice = 0x0008,
+ .driver_data = SAA7134_BOARD_MEDION_MD8800_QUADRO,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1461,
+ .subdevice = 0x2c05,
+ .driver_data = SAA7134_BOARD_AVERMEDIA_777,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7133,
+ .subvendor = 0x1489,
+ .subdevice = 0x0502, /* Cardbus version */
+ .driver_data = SAA7134_BOARD_FLYDVBT_DUO_CARDBUS,
+ },{
+ .vendor = PCI_VENDOR_ID_PHILIPS,
+ .device = PCI_DEVICE_ID_PHILIPS_SAA7130,
+ .subvendor = 0x0919, /* Philips Proteus PRO 2309 */
+ .subdevice = 0x2003,
+ .driver_data = SAA7134_BOARD_PROTEUS_2309,
+ },{
/* --- boards without eeprom + subsystem ID --- */
.vendor = PCI_VENDOR_ID_PHILIPS,
.device = PCI_DEVICE_ID_PHILIPS_SAA7134,
@@ -3548,6 +3664,12 @@ int saa7134_board_init1(struct saa7134_dev *dev)
case SAA7134_BOARD_SEDNA_PC_TV_CARDBUS:
case SAA7134_BOARD_FLYDVBT_LR301:
case SAA7134_BOARD_FLYDVBTDUO:
+ case SAA7134_BOARD_PROTEUS_2309:
+ dev->has_remote = SAA7134_REMOTE_GPIO;
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ saa_writeb(SAA7134_GPIO_GPMODE3, 0x80);
+ saa_writeb(SAA7134_GPIO_GPSTATUS2, 0x40);
dev->has_remote = SAA7134_REMOTE_GPIO;
break;
case SAA7134_BOARD_MD5044:
@@ -3732,6 +3854,7 @@ int saa7134_board_init2(struct saa7134_dev *dev)
case SAA7134_BOARD_PHILIPS_TIGER:
case SAA7134_BOARD_TEVION_DVBT_220RF:
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
+ case SAA7134_BOARD_MEDION_MD8800_QUADRO:
/* this is a hybrid board, initialize to analog mode
* and configure firmware eeprom address
*/
diff --git a/drivers/media/video/saa7134/saa7134-core.c b/drivers/media/video/saa7134/saa7134-core.c
index be3a81fc90a..09aa62f61af 100644
--- a/drivers/media/video/saa7134/saa7134-core.c
+++ b/drivers/media/video/saa7134/saa7134-core.c
@@ -843,7 +843,7 @@ static int __devinit saa7134_initdev(struct pci_dev *pci_dev,
latency = 0x0A;
}
#endif
- if (pci_pci_problems & PCIPCI_FAIL) {
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL)) {
printk(KERN_INFO "%s: quirk: this driver and your "
"chipset may not work together"
" in overlay mode.\n",dev->name);
diff --git a/drivers/media/video/saa7134/saa7134-dvb.c b/drivers/media/video/saa7134/saa7134-dvb.c
index 279828b8f29..b6881541e70 100644
--- a/drivers/media/video/saa7134/saa7134-dvb.c
+++ b/drivers/media/video/saa7134/saa7134-dvb.c
@@ -34,17 +34,14 @@
#include <media/v4l2-common.h>
#include "dvb-pll.h"
-#ifdef HAVE_MT352
-# include "mt352.h"
-# include "mt352_priv.h" /* FIXME */
-#endif
-#ifdef HAVE_TDA1004X
-# include "tda1004x.h"
-#endif
-#ifdef HAVE_NXT200X
-# include "nxt200x.h"
-#endif
-
+#include "mt352.h"
+#include "mt352_priv.h" /* FIXME */
+#include "tda1004x.h"
+#include "nxt200x.h"
+
+#include "tda10086.h"
+#include "tda826x.h"
+#include "isl6421.h"
MODULE_AUTHOR("Gerd Knorr <kraxel@bytesex.org> [SuSE Labs]");
MODULE_LICENSE("GPL");
@@ -54,8 +51,6 @@ module_param(antenna_pwr, int, 0444);
MODULE_PARM_DESC(antenna_pwr,"enable antenna power (Pinnacle 300i)");
/* ------------------------------------------------------------------ */
-
-#ifdef HAVE_MT352
static int pinnacle_antenna_pwr(struct saa7134_dev *dev, int on)
{
u32 ok;
@@ -185,12 +180,8 @@ static struct mt352_config avermedia_777 = {
.demod_address = 0xf,
.demod_init = mt352_aver777_init,
};
-#endif
/* ------------------------------------------------------------------ */
-
-#ifdef HAVE_TDA1004X
-
static int philips_tda6651_pll_set(u8 addr, struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
{
struct saa7134_dev *dev = fe->dvb->priv;
@@ -969,11 +960,58 @@ static struct tda1004x_config tevion_dvbt220rf_config = {
.request_firmware = NULL,
};
-#endif
+/* ------------------------------------------------------------------ */
+
+static int md8800_dvbt_analog_mode(struct dvb_frontend *fe)
+{
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 data[] = { 0x3c, 0x33, 0x68};
+ struct i2c_msg msg = {.addr=0x08, .flags=0, .buf=data, .len = sizeof(data)};
+
+ i2c_transfer(&dev->i2c_adap, &msg, 1);
+ philips_tda827xa_tuner_sleep( 0x61, fe);
+ return 0;
+}
+
+static int md8800_dvbt_pll_set(struct dvb_frontend *fe, struct dvb_frontend_parameters *params)
+{
+ int ret;
+ struct saa7134_dev *dev = fe->dvb->priv;
+ static u8 tda8290_close[] = { 0x21, 0xc0};
+ static u8 tda8290_open[] = { 0x21, 0x80};
+ struct i2c_msg tda8290_msg = {.addr = 0x4b,.flags = 0, .len = 2};
+ /* close tda8290 i2c bridge */
+ tda8290_msg.buf = tda8290_close;
+ ret = i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
+ if (ret != 1)
+ return -EIO;
+ msleep(20);
+ ret = philips_tda827xa_pll_set(0x60, fe, params);
+ if (ret != 0)
+ return ret;
+ /* open tda8290 i2c bridge */
+ tda8290_msg.buf = tda8290_open;
+ i2c_transfer(&dev->i2c_adap, &tda8290_msg, 1);
+ return ret;
+}
+
+static struct tda1004x_config md8800_dvbt_config = {
+ .demod_address = 0x08,
+ .invert = 1,
+ .invert_oclk = 0,
+ .xtal_freq = TDA10046_XTAL_16M,
+ .agc_config = TDA10046_AGC_TDA827X,
+ .if_freq = TDA10046_FREQ_045,
+ .request_firmware = NULL,
+};
+
+static struct tda10086_config flydvbs = {
+ .demod_address = 0x0e,
+ .invert = 0,
+};
/* ------------------------------------------------------------------ */
-#ifdef HAVE_NXT200X
static struct nxt200x_config avertvhda180 = {
.demod_address = 0x0a,
};
@@ -991,7 +1029,6 @@ static struct nxt200x_config kworldatsc110 = {
.demod_address = 0x0a,
.set_pll_input = nxt200x_set_pll_input,
};
-#endif
/* ------------------------------------------------------------------ */
@@ -1009,29 +1046,26 @@ static int dvb_init(struct saa7134_dev *dev)
dev);
switch (dev->board) {
-#ifdef HAVE_MT352
case SAA7134_BOARD_PINNACLE_300I_DVBT_PAL:
printk("%s: pinnacle 300i dvb setup\n",dev->name);
- dev->dvb.frontend = mt352_attach(&pinnacle_300i,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach, &pinnacle_300i,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.set_params = mt352_pinnacle_tuner_set_params;
}
break;
-
case SAA7134_BOARD_AVERMEDIA_777:
printk("%s: avertv 777 dvb setup\n",dev->name);
- dev->dvb.frontend = mt352_attach(&avermedia_777,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(mt352_attach, &avermedia_777,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.calc_regs = mt352_aver777_tuner_calc_regs;
}
break;
-#endif
-#ifdef HAVE_TDA1004X
case SAA7134_BOARD_MD7134:
- dev->dvb.frontend = tda10046_attach(&medion_cardbus,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &medion_cardbus,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_fmd1216_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_fmd1216_tuner_sleep;
@@ -1039,16 +1073,18 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_PHILIPS_TOUGH:
- dev->dvb.frontend = tda10046_attach(&philips_tu1216_60_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_60_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_60_init;
dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_60_set_params;
}
break;
case SAA7134_BOARD_FLYDVBTDUO:
- dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &tda827x_lifeview_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
@@ -1056,8 +1092,9 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_FLYDVBT_DUO_CARDBUS:
- dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &tda827x_lifeview_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
@@ -1065,8 +1102,9 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_PHILIPS_EUROPA:
- dev->dvb.frontend = tda10046_attach(&philips_europa_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->original_demod_sleep = dev->dvb.frontend->ops.sleep;
dev->dvb.frontend->ops.sleep = philips_europa_demod_sleep;
@@ -1076,8 +1114,9 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
- dev->dvb.frontend = tda10046_attach(&philips_europa_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_europa_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_europa_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_europa_tuner_sleep;
@@ -1085,16 +1124,18 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_VIDEOMATE_DVBT_200:
- dev->dvb.frontend = tda10046_attach(&philips_tu1216_61_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tu1216_61_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tu1216_tuner_61_init;
dev->dvb.frontend->ops.tuner_ops.set_params = philips_tu1216_tuner_61_set_params;
}
break;
case SAA7134_BOARD_PHILIPS_TIGER:
- dev->dvb.frontend = tda10046_attach(&philips_tiger_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tiger_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
@@ -1102,8 +1143,9 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_ASUSTeK_P7131_DUAL:
- dev->dvb.frontend = tda10046_attach(&philips_tiger_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &philips_tiger_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tiger_tuner_sleep;
@@ -1111,8 +1153,9 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_FLYDVBT_LR301:
- dev->dvb.frontend = tda10046_attach(&tda827x_lifeview_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &tda827x_lifeview_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = philips_tda827x_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = philips_tda827x_tuner_sleep;
@@ -1120,16 +1163,18 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_FLYDVB_TRIO:
- dev->dvb.frontend = tda10046_attach(&lifeview_trio_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &lifeview_trio_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.sleep = lifeview_trio_tuner_sleep;
dev->dvb.frontend->ops.tuner_ops.set_params = lifeview_trio_tuner_set_params;
}
break;
case SAA7134_BOARD_ADS_DUO_CARDBUS_PTV331:
- dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &ads_tech_duo_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep;
@@ -1137,37 +1182,63 @@ static int dvb_init(struct saa7134_dev *dev)
}
break;
case SAA7134_BOARD_TEVION_DVBT_220RF:
- dev->dvb.frontend = tda10046_attach(&tevion_dvbt220rf_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &tevion_dvbt220rf_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.sleep = tevion_dvb220rf_tuner_sleep;
dev->dvb.frontend->ops.tuner_ops.set_params = tevion_dvb220rf_tuner_set_params;
}
break;
case SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS:
- dev->dvb.frontend = tda10046_attach(&ads_tech_duo_config,
- &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(tda10046_attach,
+ &ads_tech_duo_config,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
dev->dvb.frontend->ops.tuner_ops.init = ads_duo_tuner_init;
dev->dvb.frontend->ops.tuner_ops.sleep = ads_duo_tuner_sleep;
dev->dvb.frontend->ops.tuner_ops.set_params = ads_duo_tuner_set_params;
}
break;
-#endif
-#ifdef HAVE_NXT200X
+ case SAA7134_BOARD_MEDION_MD8800_QUADRO:
+ dev->dvb.frontend = tda10046_attach(&md8800_dvbt_config,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dev->dvb.frontend->ops.tuner_ops.init = philips_tiger_tuner_init;
+ dev->dvb.frontend->ops.tuner_ops.sleep = md8800_dvbt_analog_mode;
+ dev->dvb.frontend->ops.tuner_ops.set_params = md8800_dvbt_pll_set;
+ }
+ break;
case SAA7134_BOARD_AVERMEDIA_AVERTVHD_A180:
- dev->dvb.frontend = nxt200x_attach(&avertvhda180, &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(nxt200x_attach, &avertvhda180,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
- dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tdhu2);
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_tdhu2);
}
break;
case SAA7134_BOARD_KWORLD_ATSC110:
- dev->dvb.frontend = nxt200x_attach(&kworldatsc110, &dev->i2c_adap);
+ dev->dvb.frontend = dvb_attach(nxt200x_attach, &kworldatsc110,
+ &dev->i2c_adap);
+ if (dev->dvb.frontend) {
+ dvb_attach(dvb_pll_attach, dev->dvb.frontend, 0x61,
+ NULL, &dvb_pll_tuv1236d);
+ }
+ break;
+ case SAA7134_BOARD_FLYDVBS_LR300:
+ dev->dvb.frontend = dvb_attach(tda10086_attach, &flydvbs,
+ &dev->i2c_adap);
if (dev->dvb.frontend) {
- dvb_pll_attach(dev->dvb.frontend, 0x61, &dev->i2c_adap, &dvb_pll_tuv1236d);
+ if (dvb_attach(tda826x_attach, dev->dvb.frontend, 0x60,
+ &dev->i2c_adap, 0) == NULL) {
+ printk("%s: No tda826x found!\n", __FUNCTION__);
+ }
+ if (dvb_attach(isl6421_attach, dev->dvb.frontend,
+ &dev->i2c_adap, 0x08, 0, 0) == NULL) {
+ printk("%s: No ISL6421 found!\n", __FUNCTION__);
+ }
}
break;
-#endif
default:
printk("%s: Huh? unknown DVB card?\n",dev->name);
break;
diff --git a/drivers/media/video/saa7134/saa7134-input.c b/drivers/media/video/saa7134/saa7134-input.c
index 7c595492c56..f7ea857d5d7 100644
--- a/drivers/media/video/saa7134/saa7134-input.c
+++ b/drivers/media/video/saa7134/saa7134-input.c
@@ -228,6 +228,12 @@ int saa7134_input_init1(struct saa7134_dev *dev)
mask_keyup = 0x400000;
polling = 50; // ms
break;
+ case SAA7134_BOARD_PROTEUS_2309:
+ ir_codes = ir_codes_proteus_2309;
+ mask_keycode = 0x00007F;
+ mask_keyup = 0x000080;
+ polling = 50; // ms
+ break;
case SAA7134_BOARD_VIDEOMATE_DVBT_300:
case SAA7134_BOARD_VIDEOMATE_DVBT_200:
ir_codes = ir_codes_videomate_tv_pvr;
diff --git a/drivers/media/video/saa7134/saa7134-tvaudio.c b/drivers/media/video/saa7134/saa7134-tvaudio.c
index 0db53d192b2..d31220d2049 100644
--- a/drivers/media/video/saa7134/saa7134-tvaudio.c
+++ b/drivers/media/video/saa7134/saa7134-tvaudio.c
@@ -1046,6 +1046,7 @@ int saa7134_tvaudio_do_scan(struct saa7134_dev *dev)
}
EXPORT_SYMBOL(saa_dsp_writel);
+EXPORT_SYMBOL(saa7134_tvaudio_setmute);
/* ----------------------------------------------------------- */
/*
diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h
index c04ce6152fd..7db7b970595 100644
--- a/drivers/media/video/saa7134/saa7134.h
+++ b/drivers/media/video/saa7134/saa7134.h
@@ -223,6 +223,9 @@ struct saa7134_format {
#define SAA7134_BOARD_MD7134_BRIDGE_2 93
#define SAA7134_BOARD_FLYDVBT_HYBRID_CARDBUS 94
#define SAA7134_BOARD_FLYVIDEO3000_NTSC 95
+#define SAA7134_BOARD_MEDION_MD8800_QUADRO 96
+#define SAA7134_BOARD_FLYDVBS_LR300 97
+#define SAA7134_BOARD_PROTEUS_2309 98
#define SAA7134_MAXBOARDS 8
#define SAA7134_INPUT_MAX 8
diff --git a/drivers/media/video/tda9887.c b/drivers/media/video/tda9887.c
index 8dab481d384..87ffb0e84a7 100644
--- a/drivers/media/video/tda9887.c
+++ b/drivers/media/video/tda9887.c
@@ -480,6 +480,8 @@ static int tda9887_set_config(struct tuner *t, char *buf)
}
if ((t->tda9887_config & TDA9887_INTERCARRIER_NTSC) && (t->std & V4L2_STD_NTSC))
buf[1] &= ~cQSS;
+ if (t->tda9887_config & TDA9887_GATING_18)
+ buf[3] &= ~cGating_36;
return 0;
}
diff --git a/drivers/media/video/tuner-simple.c b/drivers/media/video/tuner-simple.c
index abe37cf632c..63db4e97ae6 100644
--- a/drivers/media/video/tuner-simple.c
+++ b/drivers/media/video/tuner-simple.c
@@ -10,7 +10,7 @@
#include <media/v4l2-common.h>
static int offset = 0;
-module_param(offset, int, 0666);
+module_param(offset, int, 0664);
MODULE_PARM_DESC(offset,"Allows to specify an offset for tuner");
/* ---------------------------------------------------------------------- */
@@ -331,6 +331,8 @@ static void default_set_tv_freq(struct i2c_client *c, unsigned int freq)
else if (params->default_top_high)
config |= TDA9887_TOP(params->default_top_high);
}
+ if (params->default_pll_gating_18)
+ config |= TDA9887_GATING_18;
i2c_clients_command(c->adapter, TDA9887_SET_CONFIG, &config);
}
tuner_dbg("tv 0x%02x 0x%02x 0x%02x 0x%02x\n",
@@ -439,8 +441,6 @@ static void default_set_radio_freq(struct i2c_client *c, unsigned int freq)
buffer[3] = 0xa4;
break;
}
- buffer[0] = (div>>8) & 0x7f;
- buffer[1] = div & 0xff;
if (params->cb_first_if_lower_freq && div < t->last_div) {
buffer[0] = buffer[2];
buffer[1] = buffer[3];
diff --git a/drivers/media/video/tuner-types.c b/drivers/media/video/tuner-types.c
index 8b542599ed4..8fff642fad5 100644
--- a/drivers/media/video/tuner-types.c
+++ b/drivers/media/video/tuner-types.c
@@ -650,6 +650,7 @@ static struct tuner_params tuner_microtune_4049_fm5_params[] = {
.count = ARRAY_SIZE(tuner_temic_4009f_5_pal_ranges),
.has_tda9887 = 1,
.port1_invert_for_secam_lc = 1,
+ .default_pll_gating_18 = 1,
},
};
diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c
index 936e3f746fb..fcaef4bf828 100644
--- a/drivers/media/video/tvaudio.c
+++ b/drivers/media/video/tvaudio.c
@@ -28,6 +28,7 @@
#include <linux/i2c-algo-bit.h>
#include <linux/init.h>
#include <linux/smp_lock.h>
+#include <linux/kthread.h>
#include <media/tvaudio.h>
#include <media/v4l2-common.h>
@@ -124,11 +125,8 @@ struct CHIPSTATE {
int input;
/* thread */
- pid_t tpid;
- struct completion texit;
- wait_queue_head_t wq;
+ struct task_struct *thread;
struct timer_list wt;
- int done;
int watch_stereo;
int audmode;
};
@@ -264,28 +262,23 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd)
static void chip_thread_wake(unsigned long data)
{
struct CHIPSTATE *chip = (struct CHIPSTATE*)data;
- wake_up_interruptible(&chip->wq);
+ wake_up_process(chip->thread);
}
static int chip_thread(void *data)
{
- DECLARE_WAITQUEUE(wait, current);
struct CHIPSTATE *chip = data;
struct CHIPDESC *desc = chiplist + chip->type;
- daemonize("%s", chip->c.name);
- allow_signal(SIGTERM);
v4l_dbg(1, debug, &chip->c, "%s: thread started\n", chip->c.name);
for (;;) {
- add_wait_queue(&chip->wq, &wait);
- if (!chip->done) {
- set_current_state(TASK_INTERRUPTIBLE);
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (!kthread_should_stop())
schedule();
- }
- remove_wait_queue(&chip->wq, &wait);
+ set_current_state(TASK_RUNNING);
try_to_freeze();
- if (chip->done || signal_pending(current))
+ if (kthread_should_stop())
break;
v4l_dbg(1, debug, &chip->c, "%s: thread wakeup\n", chip->c.name);
@@ -301,7 +294,6 @@ static int chip_thread(void *data)
}
v4l_dbg(1, debug, &chip->c, "%s: thread exiting\n", chip->c.name);
- complete_and_exit(&chip->texit, 0);
return 0;
}
@@ -1536,19 +1528,18 @@ static int chip_attach(struct i2c_adapter *adap, int addr, int kind)
chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble));
}
- chip->tpid = -1;
+ chip->thread = NULL;
if (desc->checkmode) {
/* start async thread */
init_timer(&chip->wt);
chip->wt.function = chip_thread_wake;
chip->wt.data = (unsigned long)chip;
- init_waitqueue_head(&chip->wq);
- init_completion(&chip->texit);
- chip->tpid = kernel_thread(chip_thread,(void *)chip,0);
- if (chip->tpid < 0)
- v4l_warn(&chip->c, "%s: kernel_thread() failed\n",
+ chip->thread = kthread_run(chip_thread, chip, chip->c.name);
+ if (IS_ERR(chip->thread)) {
+ v4l_warn(&chip->c, "%s: failed to create kthread\n",
chip->c.name);
- wake_up_interruptible(&chip->wq);
+ chip->thread = NULL;
+ }
}
return 0;
}
@@ -1569,11 +1560,10 @@ static int chip_detach(struct i2c_client *client)
struct CHIPSTATE *chip = i2c_get_clientdata(client);
del_timer_sync(&chip->wt);
- if (chip->tpid >= 0) {
+ if (chip->thread) {
/* shutdown async thread */
- chip->done = 1;
- wake_up_interruptible(&chip->wq);
- wait_for_completion(&chip->texit);
+ kthread_stop(chip->thread);
+ chip->thread = NULL;
}
i2c_detach_client(&chip->c);
diff --git a/drivers/media/video/tveeprom.c b/drivers/media/video/tveeprom.c
index d95529e8e51..cd1502ac956 100644
--- a/drivers/media/video/tveeprom.c
+++ b/drivers/media/video/tveeprom.c
@@ -605,6 +605,8 @@ void tveeprom_hauppauge_analog(struct i2c_client *c, struct tveeprom *tvee,
tvee->tuner_formats |= hauppauge_tuner_fmt[i].id;
t_fmt_name1[j++] = hauppauge_tuner_fmt[i].name;
}
+ }
+ for (i = j = 0; i < 8; i++) {
if (t_format2 & (1 << i)) {
tvee->tuner2_formats |= hauppauge_tuner_fmt[i].id;
t_fmt_name2[j++] = hauppauge_tuner_fmt[i].name;
diff --git a/drivers/media/video/tvp5150.c b/drivers/media/video/tvp5150.c
index b167ffab252..bc0a4fc27b2 100644
--- a/drivers/media/video/tvp5150.c
+++ b/drivers/media/video/tvp5150.c
@@ -294,7 +294,7 @@ static inline void tvp5150_selmux(struct i2c_client *c)
if ((decoder->route.output & TVP5150_BLACK_SCREEN) || !decoder->enable)
input = 8;
- switch (input) {
+ switch (decoder->route.input) {
case TVP5150_COMPOSITE1:
input |= 2;
/* fall through */
@@ -308,6 +308,11 @@ static inline void tvp5150_selmux(struct i2c_client *c)
break;
}
+ tvp5150_dbg( 1, "Selecting video route: route input=%i, output=%i "
+ "=> tvp5150 input=%i, opmode=%i\n",
+ decoder->route.input,decoder->route.output,
+ input, opmode );
+
tvp5150_write(c, TVP5150_OP_MODE_CTL, opmode);
tvp5150_write(c, TVP5150_VD_IN_SRC_SEL_1, input);
};
diff --git a/drivers/media/video/usbvideo/konicawc.c b/drivers/media/video/usbvideo/konicawc.c
index 6f31ecc8884..4eee8be8831 100644
--- a/drivers/media/video/usbvideo/konicawc.c
+++ b/drivers/media/video/usbvideo/konicawc.c
@@ -222,6 +222,7 @@ static void konicawc_adjust_picture(struct uvd *uvd)
static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev)
{
struct input_dev *input_dev;
+ int error;
usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
@@ -242,7 +243,13 @@ static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev
input_dev->private = cam;
- input_register_device(cam->input);
+ error = input_register_device(cam->input);
+ if (error) {
+ warn("Failed to register camera's input device, err: %d\n",
+ error);
+ input_free_device(cam->input);
+ cam->input = NULL;
+ }
}
static void konicawc_unregister_input(struct konicawc *cam)
diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c
index 90d48e8510b..08f9559a6bf 100644
--- a/drivers/media/video/usbvideo/vicam.c
+++ b/drivers/media/video/usbvideo/vicam.c
@@ -7,6 +7,7 @@
* Monroe Williams (monroe@pobox.com)
*
* Supports 3COM HomeConnect PC Digital WebCam
+ * Supports Compro PS39U WebCam
*
* 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
@@ -60,6 +61,8 @@
/* Define these values to match your device */
#define USB_VICAM_VENDOR_ID 0x04c1
#define USB_VICAM_PRODUCT_ID 0x009d
+#define USB_COMPRO_VENDOR_ID 0x0602
+#define USB_COMPRO_PRODUCT_ID 0x1001
#define VICAM_BYTES_PER_PIXEL 3
#define VICAM_MAX_READ_SIZE (512*242+128)
@@ -1254,6 +1257,7 @@ static struct video_device vicam_template = {
/* table of devices that work with this driver */
static struct usb_device_id vicam_table[] = {
{USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)},
+ {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)},
{} /* Terminating entry */
};
diff --git a/drivers/media/video/v4l1-compat.c b/drivers/media/video/v4l1-compat.c
index d7c3fcbc80f..1d899e2db39 100644
--- a/drivers/media/video/v4l1-compat.c
+++ b/drivers/media/video/v4l1-compat.c
@@ -349,6 +349,8 @@ v4l_compat_translate_ioctl(struct inode *inode,
{
struct video_buffer *buffer = arg;
+ memset(buffer, 0, sizeof(*buffer));
+
err = drv(inode, file, VIDIOC_G_FBUF, &fbuf2);
if (err < 0) {
dprintk("VIDIOCGFBUF / VIDIOC_G_FBUF: %d\n",err);
@@ -361,7 +363,7 @@ v4l_compat_translate_ioctl(struct inode *inode,
switch (fbuf2.fmt.pixelformat) {
case V4L2_PIX_FMT_RGB332:
buffer->depth = 8;
- break;
+ break;
case V4L2_PIX_FMT_RGB555:
buffer->depth = 15;
break;
@@ -377,9 +379,13 @@ v4l_compat_translate_ioctl(struct inode *inode,
default:
buffer->depth = 0;
}
- if (0 != fbuf2.fmt.bytesperline)
+ if (fbuf2.fmt.bytesperline) {
buffer->bytesperline = fbuf2.fmt.bytesperline;
- else {
+ if (!buffer->depth && buffer->width)
+ buffer->depth = ((fbuf2.fmt.bytesperline<<3)
+ + (buffer->width-1) )
+ /buffer->width;
+ } else {
buffer->bytesperline =
(buffer->width * buffer->depth + 7) & 7;
buffer->bytesperline >>= 3;
diff --git a/drivers/media/video/v4l2-common.c b/drivers/media/video/v4l2-common.c
index 8d972ffdaf9..78d28b03ec9 100644
--- a/drivers/media/video/v4l2-common.c
+++ b/drivers/media/video/v4l2-common.c
@@ -938,6 +938,7 @@ void v4l_printk_ioctl_arg(char *s,unsigned int cmd, void *arg)
case VIDIOC_INT_AUDIO_CLOCK_FREQ:
case VIDIOC_INT_I2S_CLOCK_FREQ:
case VIDIOC_INT_S_STANDBY:
+ case VIDIOC_INT_RESET:
{
u32 *p=arg;
diff --git a/drivers/media/video/video-buf-dvb.c b/drivers/media/video/video-buf-dvb.c
index 7ee8a53cd33..f53edf1923b 100644
--- a/drivers/media/video/video-buf-dvb.c
+++ b/drivers/media/video/video-buf-dvb.c
@@ -223,6 +223,7 @@ fail_dmxdev:
fail_dmx:
dvb_unregister_frontend(dvb->frontend);
fail_frontend:
+ dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
fail_adapter:
return result;
@@ -236,6 +237,7 @@ void videobuf_dvb_unregister(struct videobuf_dvb *dvb)
dvb_dmxdev_release(&dvb->dmxdev);
dvb_dmx_release(&dvb->demux);
dvb_unregister_frontend(dvb->frontend);
+ dvb_frontend_detach(dvb->frontend);
dvb_unregister_adapter(&dvb->adapter);
}
diff --git a/drivers/media/video/videodev.c b/drivers/media/video/videodev.c
index edd7b83c346..479a0675cf6 100644
--- a/drivers/media/video/videodev.c
+++ b/drivers/media/video/videodev.c
@@ -739,13 +739,13 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_DQBUF:
{
struct v4l2_buffer *p=arg;
- if (!vfd->vidioc_qbuf)
+ if (!vfd->vidioc_dqbuf)
break;
ret = check_fmt (vfd, p->type);
if (ret)
break;
- ret=vfd->vidioc_qbuf(file, fh, p);
+ ret=vfd->vidioc_dqbuf(file, fh, p);
if (!ret)
dbgbuf(cmd,vfd,p);
break;
@@ -836,7 +836,7 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
break;
}
- if (index < 0 || index >= vfd->tvnormsize) {
+ if (index<0 || index >= vfd->tvnormsize) {
ret=-EINVAL;
break;
}
@@ -1283,9 +1283,29 @@ static int __video_do_ioctl(struct inode *inode, struct file *file,
case VIDIOC_G_PARM:
{
struct v4l2_streamparm *p=arg;
- if (!vfd->vidioc_g_parm)
- break;
- ret=vfd->vidioc_g_parm(file, fh, p);
+ if (vfd->vidioc_g_parm) {
+ ret=vfd->vidioc_g_parm(file, fh, p);
+ } else {
+ struct v4l2_standard s;
+
+ if (!vfd->tvnormsize) {
+ printk (KERN_WARNING "%s: no TV norms defined!\n",
+ vfd->name);
+ break;
+ }
+
+ if (p->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ return -EINVAL;
+
+ v4l2_video_std_construct(&s, vfd->tvnorms[vfd->current_norm].id,
+ vfd->tvnorms[vfd->current_norm].name);
+
+ memset(p,0,sizeof(*p));
+
+ p->parm.capture.timeperframe = s.frameperiod;
+ ret=0;
+ }
+
dbgarg (cmd, "type=%d\n", p->type);
break;
}
diff --git a/drivers/media/video/vino.c b/drivers/media/video/vino.c
index 268e69fdefc..d1e04f7c530 100644
--- a/drivers/media/video/vino.c
+++ b/drivers/media/video/vino.c
@@ -4404,7 +4404,6 @@ static struct video_device v4l_device_template = {
.name = "NOT SET",
//.type = VID_TYPE_CAPTURE | VID_TYPE_SUBCAPTURE |
// VID_TYPE_CLIPPING | VID_TYPE_SCALES, VID_TYPE_OVERLAY
- .hardware = VID_HARDWARE_VINO,
.fops = &vino_fops,
.minor = -1,
};
diff --git a/drivers/media/video/vivi.c b/drivers/media/video/vivi.c
index 841884af0cc..e7c01d560b6 100644
--- a/drivers/media/video/vivi.c
+++ b/drivers/media/video/vivi.c
@@ -992,7 +992,8 @@ static int vidiocgmbuf (struct file *file, void *priv, struct video_mbuf *mbuf)
struct vivi_fh *fh=priv;
struct videobuf_queue *q=&fh->vb_vidq;
struct v4l2_requestbuffers req;
- unsigned int i, ret;
+ unsigned int i;
+ int ret;
req.type = q->type;
req.count = 8;
@@ -1359,6 +1360,8 @@ static int __init vivi_init(void)
dev->vidq.timeout.data = (unsigned long)dev;
init_timer(&dev->vidq.timeout);
+ vivi.current_norm = tvnorms[0].id;
+
ret = video_register_device(&vivi, VFL_TYPE_GRABBER, video_nr);
printk(KERN_INFO "Video Technology Magazine Virtual Video Capture Board (Load status: %d)\n", ret);
return ret;
diff --git a/drivers/media/video/vpx3220.c b/drivers/media/video/vpx3220.c
index 1eca7e65d23..8ef31ed7d3f 100644
--- a/drivers/media/video/vpx3220.c
+++ b/drivers/media/video/vpx3220.c
@@ -744,6 +744,6 @@ vpx3220_cleanup (void)
module_init(vpx3220_init);
module_exit(vpx3220_cleanup);
-MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video encoder driver");
+MODULE_DESCRIPTION("vpx3220a/vpx3216b/vpx3214c video decoder driver");
MODULE_AUTHOR("Laurent Pinchart");
MODULE_LICENSE("GPL");
diff --git a/drivers/media/video/w9968cf.c b/drivers/media/video/w9968cf.c
index 20f211b55ad..2912326a5ae 100644
--- a/drivers/media/video/w9968cf.c
+++ b/drivers/media/video/w9968cf.c
@@ -586,15 +586,14 @@ static struct w9968cf_symbolic_list urb_errlist[] = {
{ -EFBIG, "Too much ISO frames requested" },
{ -ENOSR, "Buffer error (overrun)" },
{ -EPIPE, "Specified endpoint is stalled (device not responding)"},
- { -EOVERFLOW, "Babble (bad cable?)" },
+ { -EOVERFLOW, "Babble (too much data)" },
{ -EPROTO, "Bit-stuff error (bad cable?)" },
{ -EILSEQ, "CRC/Timeout" },
- { -ETIMEDOUT, "NAK (device does not respond)" },
+ { -ETIME, "Device does not respond to token" },
+ { -ETIMEDOUT, "Device does not respond to command" },
{ -1, NULL }
};
-
-
/****************************************************************************
* Memory management functions *
****************************************************************************/
diff --git a/drivers/media/video/zoran_card.c b/drivers/media/video/zoran_card.c
index f2249ed2527..9f21d0ba0f0 100644
--- a/drivers/media/video/zoran_card.c
+++ b/drivers/media/video/zoran_card.c
@@ -820,7 +820,6 @@ static struct i2c_algo_bit_data zoran_i2c_bit_data_template = {
.getsda = zoran_i2c_getsda,
.getscl = zoran_i2c_getscl,
.udelay = 10,
- .mdelay = 0,
.timeout = 100,
};
@@ -1621,10 +1620,10 @@ init_dc10_cards (void)
dprintk(5, KERN_DEBUG "Jotti is een held!\n");
/* some mainboards might not do PCI-PCI data transfer well */
- if (pci_pci_problems & PCIPCI_FAIL) {
+ if (pci_pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL|PCIPCI_ALIMAGIK)) {
dprintk(1,
KERN_WARNING
- "%s: chipset may not support reliable PCI-PCI DMA\n",
+ "%s: chipset does not support reliable PCI-PCI DMA\n",
ZORAN_NAME);
}
@@ -1632,7 +1631,7 @@ init_dc10_cards (void)
for (i = 0; i < zoran_num; i++) {
struct zoran *zr = &zoran[i];
- if (pci_pci_problems & PCIPCI_NATOMA && zr->revision <= 1) {
+ if ((pci_pci_problems & PCIPCI_NATOMA) && zr->revision <= 1) {
zr->jpg_buffers.need_contiguous = 1;
dprintk(1,
KERN_INFO
diff --git a/drivers/media/video/zoran_driver.c b/drivers/media/video/zoran_driver.c
index 5f90db27892..862a984c215 100644
--- a/drivers/media/video/zoran_driver.c
+++ b/drivers/media/video/zoran_driver.c
@@ -1512,6 +1512,13 @@ setup_fbuffer (struct file *file,
if (!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO))
return -EPERM;
+ /* Don't allow frame buffer overlay if PCI or AGP is buggy, or on
+ ALi Magik (that needs very low latency while the card needs a
+ higher value always) */
+
+ if (pci_pci_problems & (PCIPCI_FAIL | PCIAGP_FAIL | PCIPCI_ALIMAGIK))
+ return -ENXIO;
+
/* we need a bytesperline value, even if not given */
if (!bytesperline)
bytesperline = width * ((fmt->depth + 7) & ~7) / 8;
diff --git a/drivers/media/video/zr36120.c b/drivers/media/video/zr36120.c
index 50437383ed6..9240638a013 100644
--- a/drivers/media/video/zr36120.c
+++ b/drivers/media/video/zr36120.c
@@ -987,6 +987,8 @@ int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg)
VID_TYPE_SCALES;
if (ztv->have_tuner)
c.type |= VID_TYPE_TUNER;
+ if (pci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
+ c.type &= ~VID_TYPE_OVERLAY;
if (ztv->have_decoder) {
c.channels = ztv->card->video_inputs;
c.audios = ztv->card->audio_inputs;
@@ -1284,6 +1286,8 @@ int zoran_ioctl(struct video_device* dev, unsigned int cmd, void *arg)
struct video_buffer v;
if(!capable(CAP_SYS_ADMIN))
return -EPERM;
+ if (pcipci_problems & (PCIPCI_FAIL|PCIAGP_FAIL))
+ return -ENXIO;
if (copy_from_user(&v, arg,sizeof(v)))
return -EFAULT;
DEBUG(printk(CARD_DEBUG "VIDIOCSFBUF(%p,%d,%d,%d,%d)\n",CARD,v.base, v.width,v.height,v.depth,v.bytesperline));
@@ -2030,7 +2034,7 @@ void release_zoran(int max)
/* free it */
free_irq(ztv->dev->irq,ztv);
- /* unregister i2c_bus */
+ /* unregister i2c_bus */
i2c_unregister_bus((&ztv->i2c));
/* unmap and free memory */
diff --git a/drivers/message/i2o/Kconfig b/drivers/message/i2o/Kconfig
index fef67710388..6443392bfff 100644
--- a/drivers/message/i2o/Kconfig
+++ b/drivers/message/i2o/Kconfig
@@ -88,7 +88,7 @@ config I2O_BUS
config I2O_BLOCK
tristate "I2O Block OSM"
- depends on I2O
+ depends on I2O && BLOCK
---help---
Include support for the I2O Block OSM. The Block OSM presents disk
and other structured block devices to the operating system. If you
diff --git a/drivers/message/i2o/i2o_block.c b/drivers/message/i2o/i2o_block.c
index 1ddc2fb429d..eaba81bf2ec 100644
--- a/drivers/message/i2o/i2o_block.c
+++ b/drivers/message/i2o/i2o_block.c
@@ -390,9 +390,9 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req)
}
/* request is already processed by us, so return */
- if (req->flags & REQ_SPECIAL) {
+ if (blk_special_request(req)) {
osm_debug("REQ_SPECIAL already set!\n");
- req->flags |= REQ_DONTPREP;
+ req->cmd_flags |= REQ_DONTPREP;
return BLKPREP_OK;
}
@@ -411,7 +411,8 @@ static int i2o_block_prep_req_fn(struct request_queue *q, struct request *req)
ireq = req->special;
/* do not come back here */
- req->flags |= REQ_DONTPREP | REQ_SPECIAL;
+ req->cmd_type = REQ_TYPE_SPECIAL;
+ req->cmd_flags |= REQ_DONTPREP;
return BLKPREP_OK;
};
diff --git a/drivers/message/i2o/pci.c b/drivers/message/i2o/pci.c
index 1b58444d5aa..dec41cc8993 100644
--- a/drivers/message/i2o/pci.c
+++ b/drivers/message/i2o/pci.c
@@ -372,12 +372,13 @@ static int __devinit i2o_pci_probe(struct pci_dev *pdev,
* Expose the ship behind i960 for initialization, or it will
* failed
*/
- i960 =
- pci_find_slot(c->pdev->bus->number,
+ i960 = pci_get_slot(c->pdev->bus,
PCI_DEVFN(PCI_SLOT(c->pdev->devfn), 0));
- if (i960)
+ if (i960) {
pci_write_config_word(i960, 0x42, 0);
+ pci_dev_put(i960);
+ }
c->promise = 1;
c->limit_sectors = 1;
diff --git a/drivers/mfd/ucb1x00-ts.c b/drivers/mfd/ucb1x00-ts.c
index 02776814443..82938ad6ddb 100644
--- a/drivers/mfd/ucb1x00-ts.c
+++ b/drivers/mfd/ucb1x00-ts.c
@@ -58,6 +58,7 @@ static int adcsync;
static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x, u16 y)
{
struct input_dev *idev = ts->idev;
+
input_report_abs(idev, ABS_X, x);
input_report_abs(idev, ABS_Y, y);
input_report_abs(idev, ABS_PRESSURE, pressure);
@@ -67,6 +68,7 @@ static inline void ucb1x00_ts_evt_add(struct ucb1x00_ts *ts, u16 pressure, u16 x
static inline void ucb1x00_ts_event_release(struct ucb1x00_ts *ts)
{
struct input_dev *idev = ts->idev;
+
input_report_abs(idev, ABS_PRESSURE, 0);
input_sync(idev);
}
@@ -189,6 +191,7 @@ static inline unsigned int ucb1x00_ts_read_yres(struct ucb1x00_ts *ts)
static inline int ucb1x00_ts_pen_down(struct ucb1x00_ts *ts)
{
unsigned int val = ucb1x00_reg_read(ts->ucb, UCB_TS_CR);
+
if (machine_is_collie())
return (!(val & (UCB_TS_CR_TSPX_LOW)));
else
@@ -291,6 +294,7 @@ static int ucb1x00_thread(void *_ts)
static void ucb1x00_ts_irq(int idx, void *id)
{
struct ucb1x00_ts *ts = id;
+
ucb1x00_disable_irq(ts->ucb, UCB_IRQ_TSPX, UCB_FALLING);
wake_up(&ts->irq_wait);
}
@@ -372,36 +376,43 @@ static int ucb1x00_ts_resume(struct ucb1x00_dev *dev)
static int ucb1x00_ts_add(struct ucb1x00_dev *dev)
{
struct ucb1x00_ts *ts;
+ struct input_dev *idev;
+ int err;
ts = kzalloc(sizeof(struct ucb1x00_ts), GFP_KERNEL);
- if (!ts)
- return -ENOMEM;
-
- ts->idev = input_allocate_device();
- if (!ts->idev) {
- kfree(ts);
- return -ENOMEM;
+ idev = input_allocate_device();
+ if (!ts || !idev) {
+ err = -ENOMEM;
+ goto fail;
}
ts->ucb = dev->ucb;
+ ts->idev = idev;
ts->adcsync = adcsync ? UCB_SYNC : UCB_NOSYNC;
- ts->idev->private = ts;
- ts->idev->name = "Touchscreen panel";
- ts->idev->id.product = ts->ucb->id;
- ts->idev->open = ucb1x00_ts_open;
- ts->idev->close = ucb1x00_ts_close;
+ idev->private = ts;
+ idev->name = "Touchscreen panel";
+ idev->id.product = ts->ucb->id;
+ idev->open = ucb1x00_ts_open;
+ idev->close = ucb1x00_ts_close;
- __set_bit(EV_ABS, ts->idev->evbit);
- __set_bit(ABS_X, ts->idev->absbit);
- __set_bit(ABS_Y, ts->idev->absbit);
- __set_bit(ABS_PRESSURE, ts->idev->absbit);
+ __set_bit(EV_ABS, idev->evbit);
+ __set_bit(ABS_X, idev->absbit);
+ __set_bit(ABS_Y, idev->absbit);
+ __set_bit(ABS_PRESSURE, idev->absbit);
- input_register_device(ts->idev);
+ err = input_register_device(idev);
+ if (err)
+ goto fail;
dev->priv = ts;
return 0;
+
+ fail:
+ input_free_device(idev);
+ kfree(ts);
+ return err;
}
static void ucb1x00_ts_remove(struct ucb1x00_dev *dev)
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index 19c2b85249c..c1bf1fb04c5 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -3,5 +3,6 @@
#
obj- := misc.o # Dummy rule to force built-in.o to be made
-obj-$(CONFIG_IBM_ASM) += ibmasm/
+obj-$(CONFIG_IBM_ASM) += ibmasm/
obj-$(CONFIG_HDPU_FEATURES) += hdpuftrs/
+obj-$(CONFIG_LKDTM) += lkdtm.o
diff --git a/drivers/misc/ibmasm/ibmasmfs.c b/drivers/misc/ibmasm/ibmasmfs.c
index 4a35caff5d0..b99dc507de2 100644
--- a/drivers/misc/ibmasm/ibmasmfs.c
+++ b/drivers/misc/ibmasm/ibmasmfs.c
@@ -147,7 +147,6 @@ static struct inode *ibmasmfs_make_inode(struct super_block *sb, int mode)
if (ret) {
ret->i_mode = mode;
ret->i_uid = ret->i_gid = 0;
- ret->i_blksize = PAGE_CACHE_SIZE;
ret->i_blocks = 0;
ret->i_atime = ret->i_mtime = ret->i_ctime = CURRENT_TIME;
}
@@ -175,7 +174,7 @@ static struct dentry *ibmasmfs_create_file (struct super_block *sb,
}
inode->i_fop = fops;
- inode->u.generic_ip = data;
+ inode->i_private = data;
d_add(dentry, inode);
return dentry;
@@ -244,7 +243,7 @@ static int command_file_open(struct inode *inode, struct file *file)
{
struct ibmasmfs_command_data *command_data;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
command_data = kmalloc(sizeof(struct ibmasmfs_command_data), GFP_KERNEL);
@@ -252,7 +251,7 @@ static int command_file_open(struct inode *inode, struct file *file)
return -ENOMEM;
command_data->command = NULL;
- command_data->sp = inode->u.generic_ip;
+ command_data->sp = inode->i_private;
file->private_data = command_data;
return 0;
}
@@ -351,10 +350,10 @@ static int event_file_open(struct inode *inode, struct file *file)
struct ibmasmfs_event_data *event_data;
struct service_processor *sp;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
- sp = inode->u.generic_ip;
+ sp = inode->i_private;
event_data = kmalloc(sizeof(struct ibmasmfs_event_data), GFP_KERNEL);
if (!event_data)
@@ -439,14 +438,14 @@ static int r_heartbeat_file_open(struct inode *inode, struct file *file)
{
struct ibmasmfs_heartbeat_data *rhbeat;
- if (!inode->u.generic_ip)
+ if (!inode->i_private)
return -ENODEV;
rhbeat = kmalloc(sizeof(struct ibmasmfs_heartbeat_data), GFP_KERNEL);
if (!rhbeat)
return -ENOMEM;
- rhbeat->sp = (struct service_processor *)inode->u.generic_ip;
+ rhbeat->sp = inode->i_private;
rhbeat->active = 0;
ibmasm_init_reverse_heartbeat(rhbeat->sp, &rhbeat->heartbeat);
file->private_data = rhbeat;
@@ -508,7 +507,7 @@ static ssize_t r_heartbeat_file_write(struct file *file, const char __user *buf,
static int remote_settings_file_open(struct inode *inode, struct file *file)
{
- file->private_data = inode->u.generic_ip;
+ file->private_data = inode->i_private;
return 0;
}
diff --git a/drivers/misc/lkdtm.c b/drivers/misc/lkdtm.c
new file mode 100644
index 00000000000..e689ee94ac3
--- /dev/null
+++ b/drivers/misc/lkdtm.c
@@ -0,0 +1,342 @@
+/*
+ * Kprobe module for testing crash dumps
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Copyright (C) IBM Corporation, 2006
+ *
+ * Author: Ankita Garg <ankita@in.ibm.com>
+ *
+ * This module induces system failures at predefined crashpoints to
+ * evaluate the reliability of crash dumps obtained using different dumping
+ * solutions.
+ *
+ * It is adapted from the Linux Kernel Dump Test Tool by
+ * Fernando Luis Vazquez Cao <http://lkdtt.sourceforge.net>
+ *
+ * Usage : insmod lkdtm.ko [recur_count={>0}] cpoint_name=<> cpoint_type=<>
+ * [cpoint_count={>0}]
+ *
+ * recur_count : Recursion level for the stack overflow test. Default is 10.
+ *
+ * cpoint_name : Crash point where the kernel is to be crashed. It can be
+ * one of INT_HARDWARE_ENTRY, INT_HW_IRQ_EN, INT_TASKLET_ENTRY,
+ * FS_DEVRW, MEM_SWAPOUT, TIMERADD, SCSI_DISPATCH_CMD,
+ * IDE_CORE_CP
+ *
+ * cpoint_type : Indicates the action to be taken on hitting the crash point.
+ * It can be one of PANIC, BUG, EXCEPTION, LOOP, OVERFLOW
+ *
+ * cpoint_count : Indicates the number of times the crash point is to be hit
+ * to trigger an action. The default is 10.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/kprobes.h>
+#include <linux/kallsyms.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <scsi/scsi_cmnd.h>
+
+#ifdef CONFIG_IDE
+#include <linux/ide.h>
+#endif
+
+#define NUM_CPOINTS 8
+#define NUM_CPOINT_TYPES 5
+#define DEFAULT_COUNT 10
+#define REC_NUM_DEFAULT 10
+
+enum cname {
+ INVALID,
+ INT_HARDWARE_ENTRY,
+ INT_HW_IRQ_EN,
+ INT_TASKLET_ENTRY,
+ FS_DEVRW,
+ MEM_SWAPOUT,
+ TIMERADD,
+ SCSI_DISPATCH_CMD,
+ IDE_CORE_CP
+};
+
+enum ctype {
+ NONE,
+ PANIC,
+ BUG,
+ EXCEPTION,
+ LOOP,
+ OVERFLOW
+};
+
+static char* cp_name[] = {
+ "INT_HARDWARE_ENTRY",
+ "INT_HW_IRQ_EN",
+ "INT_TASKLET_ENTRY",
+ "FS_DEVRW",
+ "MEM_SWAPOUT",
+ "TIMERADD",
+ "SCSI_DISPATCH_CMD",
+ "IDE_CORE_CP"
+};
+
+static char* cp_type[] = {
+ "PANIC",
+ "BUG",
+ "EXCEPTION",
+ "LOOP",
+ "OVERFLOW"
+};
+
+static struct jprobe lkdtm;
+
+static int lkdtm_parse_commandline(void);
+static void lkdtm_handler(void);
+
+static char* cpoint_name = INVALID;
+static char* cpoint_type = NONE;
+static int cpoint_count = DEFAULT_COUNT;
+static int recur_count = REC_NUM_DEFAULT;
+
+static enum cname cpoint = INVALID;
+static enum ctype cptype = NONE;
+static int count = DEFAULT_COUNT;
+
+module_param(recur_count, int, 0644);
+MODULE_PARM_DESC(recur_count, "Recurcion level for the stack overflow test,\
+ default is 10");
+module_param(cpoint_name, charp, 0644);
+MODULE_PARM_DESC(cpoint_name, "Crash Point, where kernel is to be crashed");
+module_param(cpoint_type, charp, 06444);
+MODULE_PARM_DESC(cpoint_type, "Crash Point Type, action to be taken on\
+ hitting the crash point");
+module_param(cpoint_count, int, 06444);
+MODULE_PARM_DESC(cpoint_count, "Crash Point Count, number of times the \
+ crash point is to be hit to trigger action");
+
+unsigned int jp_do_irq(unsigned int irq, struct pt_regs *regs)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+
+irqreturn_t jp_handle_irq_event(unsigned int irq, struct pt_regs *regs,
+ struct irqaction *action)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+
+void jp_tasklet_action(struct softirq_action *a)
+{
+ lkdtm_handler();
+ jprobe_return();
+}
+
+void jp_ll_rw_block(int rw, int nr, struct buffer_head *bhs[])
+{
+ lkdtm_handler();
+ jprobe_return();
+}
+
+struct scan_control;
+
+unsigned long jp_shrink_page_list(struct list_head *page_list,
+ struct scan_control *sc)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+
+int jp_hrtimer_start(struct hrtimer *timer, ktime_t tim,
+ const enum hrtimer_mode mode)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+
+int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+
+#ifdef CONFIG_IDE
+int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
+ struct block_device *bdev, unsigned int cmd,
+ unsigned long arg)
+{
+ lkdtm_handler();
+ jprobe_return();
+ return 0;
+}
+#endif
+
+static int lkdtm_parse_commandline(void)
+{
+ int i;
+
+ if (cpoint_name == INVALID || cpoint_type == NONE ||
+ cpoint_count < 1 || recur_count < 1)
+ return -EINVAL;
+
+ for (i = 0; i < NUM_CPOINTS; ++i) {
+ if (!strcmp(cpoint_name, cp_name[i])) {
+ cpoint = i + 1;
+ break;
+ }
+ }
+
+ for (i = 0; i < NUM_CPOINT_TYPES; ++i) {
+ if (!strcmp(cpoint_type, cp_type[i])) {
+ cptype = i + 1;
+ break;
+ }
+ }
+
+ if (cpoint == INVALID || cptype == NONE)
+ return -EINVAL;
+
+ count = cpoint_count;
+
+ return 0;
+}
+
+static int recursive_loop(int a)
+{
+ char buf[1024];
+
+ memset(buf,0xFF,1024);
+ recur_count--;
+ if (!recur_count)
+ return 0;
+ else
+ return recursive_loop(a);
+}
+
+void lkdtm_handler(void)
+{
+ printk(KERN_INFO "lkdtm : Crash point %s of type %s hit\n",
+ cpoint_name, cpoint_type);
+ --count;
+
+ if (count == 0) {
+ switch (cptype) {
+ case NONE:
+ break;
+ case PANIC:
+ printk(KERN_INFO "lkdtm : PANIC\n");
+ panic("dumptest");
+ break;
+ case BUG:
+ printk(KERN_INFO "lkdtm : BUG\n");
+ BUG();
+ break;
+ case EXCEPTION:
+ printk(KERN_INFO "lkdtm : EXCEPTION\n");
+ *((int *) 0) = 0;
+ break;
+ case LOOP:
+ printk(KERN_INFO "lkdtm : LOOP\n");
+ for (;;);
+ break;
+ case OVERFLOW:
+ printk(KERN_INFO "lkdtm : OVERFLOW\n");
+ (void) recursive_loop(0);
+ break;
+ default:
+ break;
+ }
+ count = cpoint_count;
+ }
+}
+
+int lkdtm_module_init(void)
+{
+ int ret;
+
+ if (lkdtm_parse_commandline() == -EINVAL) {
+ printk(KERN_INFO "lkdtm : Invalid command\n");
+ return -EINVAL;
+ }
+
+ switch (cpoint) {
+ case INT_HARDWARE_ENTRY:
+ lkdtm.kp.symbol_name = "__do_IRQ";
+ lkdtm.entry = (kprobe_opcode_t*) jp_do_irq;
+ break;
+ case INT_HW_IRQ_EN:
+ lkdtm.kp.symbol_name = "handle_IRQ_event";
+ lkdtm.entry = (kprobe_opcode_t*) jp_handle_irq_event;
+ break;
+ case INT_TASKLET_ENTRY:
+ lkdtm.kp.symbol_name = "tasklet_action";
+ lkdtm.entry = (kprobe_opcode_t*) jp_tasklet_action;
+ break;
+ case FS_DEVRW:
+ lkdtm.kp.symbol_name = "ll_rw_block";
+ lkdtm.entry = (kprobe_opcode_t*) jp_ll_rw_block;
+ break;
+ case MEM_SWAPOUT:
+ lkdtm.kp.symbol_name = "shrink_page_list";
+ lkdtm.entry = (kprobe_opcode_t*) jp_shrink_page_list;
+ break;
+ case TIMERADD:
+ lkdtm.kp.symbol_name = "hrtimer_start";
+ lkdtm.entry = (kprobe_opcode_t*) jp_hrtimer_start;
+ break;
+ case SCSI_DISPATCH_CMD:
+ lkdtm.kp.symbol_name = "scsi_dispatch_cmd";
+ lkdtm.entry = (kprobe_opcode_t*) jp_scsi_dispatch_cmd;
+ break;
+ case IDE_CORE_CP:
+#ifdef CONFIG_IDE
+ lkdtm.kp.symbol_name = "generic_ide_ioctl";
+ lkdtm.entry = (kprobe_opcode_t*) jp_generic_ide_ioctl;
+#else
+ printk(KERN_INFO "lkdtm : Crash point not available\n");
+#endif
+ break;
+ default:
+ printk(KERN_INFO "lkdtm : Invalid Crash Point\n");
+ break;
+ }
+
+ if ((ret = register_jprobe(&lkdtm)) < 0) {
+ printk(KERN_INFO "lkdtm : Couldn't register jprobe\n");
+ return ret;
+ }
+
+ printk(KERN_INFO "lkdtm : Crash point %s of type %s registered\n",
+ cpoint_name, cpoint_type);
+ return 0;
+}
+
+void lkdtm_module_exit(void)
+{
+ unregister_jprobe(&lkdtm);
+ printk(KERN_INFO "lkdtm : Crash point unregistered\n");
+}
+
+module_init(lkdtm_module_init);
+module_exit(lkdtm_module_exit);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 45bcf098e76..f540bd88dc5 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -21,7 +21,7 @@ config MMC_DEBUG
config MMC_BLOCK
tristate "MMC block device driver"
- depends on MMC
+ depends on MMC && BLOCK
default y
help
Say Y here to enable the MMC block device driver support.
diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile
index d2957e35cc6..b1f6e03e7aa 100644
--- a/drivers/mmc/Makefile
+++ b/drivers/mmc/Makefile
@@ -24,7 +24,8 @@ obj-$(CONFIG_MMC_AU1X) += au1xmmc.o
obj-$(CONFIG_MMC_OMAP) += omap.o
obj-$(CONFIG_MMC_AT91RM9200) += at91_mci.o
-mmc_core-y := mmc.o mmc_queue.o mmc_sysfs.o
+mmc_core-y := mmc.o mmc_sysfs.o
+mmc_core-$(CONFIG_BLOCK) += mmc_queue.o
ifeq ($(CONFIG_MMC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/mmc/at91_mci.c b/drivers/mmc/at91_mci.c
index 6b7638b8429..cb142a66098 100644
--- a/drivers/mmc/at91_mci.c
+++ b/drivers/mmc/at91_mci.c
@@ -822,6 +822,7 @@ static int at91_mci_probe(struct platform_device *pdev)
mmc->f_min = 375000;
mmc->f_max = 25000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_BYTEBLOCK;
host = mmc_priv(mmc);
host->mmc = mmc;
@@ -850,7 +851,7 @@ static int at91_mci_probe(struct platform_device *pdev)
/*
* Allocate the MCI interrupt
*/
- ret = request_irq(AT91_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
+ ret = request_irq(AT91RM9200_ID_MCI, at91_mci_irq, IRQF_SHARED, DRIVER_NAME, host);
if (ret) {
printk(KERN_ERR "Failed to request MCI interrupt\n");
clk_disable(mci_clk);
@@ -906,7 +907,7 @@ static int at91_mci_remove(struct platform_device *pdev)
mmc_remove_host(mmc);
at91_mci_disable();
- free_irq(AT91_ID_MCI, host);
+ free_irq(AT91RM9200_ID_MCI, host);
mmc_free_host(mmc);
clk_disable(mci_clk); /* Disable the peripheral clock */
diff --git a/drivers/mmc/au1xmmc.c b/drivers/mmc/au1xmmc.c
index fb606165af3..61268da1395 100644
--- a/drivers/mmc/au1xmmc.c
+++ b/drivers/mmc/au1xmmc.c
@@ -731,7 +731,7 @@ static void au1xmmc_set_ios(struct mmc_host* mmc, struct mmc_ios* ios)
}
}
-static void au1xmmc_dma_callback(int irq, void *dev_id, struct pt_regs *regs)
+static void au1xmmc_dma_callback(int irq, void *dev_id)
{
struct au1xmmc_host *host = (struct au1xmmc_host *) dev_id;
diff --git a/drivers/mmc/imxmmc.c b/drivers/mmc/imxmmc.c
index fb6565b98f3..1b79dd271aa 100644
--- a/drivers/mmc/imxmmc.c
+++ b/drivers/mmc/imxmmc.c
@@ -956,7 +956,7 @@ static int imxmci_probe(struct platform_device *pdev)
mmc->f_min = 150000;
mmc->f_max = CLK_RATE/2;
mmc->ocr_avail = MMC_VDD_32_33;
- mmc->caps |= MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_BYTEBLOCK;
/* MMC core transfer sizes tunable parameters */
mmc->max_hw_segs = 64;
diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c
index 74eaaee66de..5b9caa7978d 100644
--- a/drivers/mmc/mmc.c
+++ b/drivers/mmc/mmc.c
@@ -996,7 +996,6 @@ static void mmc_read_scrs(struct mmc_host *host)
mmc_set_data_timeout(&data, card, 0);
- data.blksz_bits = 3;
data.blksz = 1 << 3;
data.blocks = 1;
data.flags = MMC_DATA_READ;
diff --git a/drivers/mmc/mmc_block.c b/drivers/mmc/mmc_block.c
index a0e0dad1b41..db0e8ad439a 100644
--- a/drivers/mmc/mmc_block.c
+++ b/drivers/mmc/mmc_block.c
@@ -32,6 +32,7 @@
#include <linux/mmc/card.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
+#include <linux/mmc/host.h>
#include <asm/system.h>
#include <asm/uaccess.h>
@@ -165,6 +166,7 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
do {
struct mmc_blk_request brq;
struct mmc_command cmd;
+ u32 readcmd, writecmd;
memset(&brq, 0, sizeof(struct mmc_blk_request));
brq.mrq.cmd = &brq.cmd;
@@ -172,7 +174,6 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
brq.cmd.arg = req->sector << 9;
brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
- brq.data.blksz_bits = md->block_bits;
brq.data.blksz = 1 << md->block_bits;
brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
brq.stop.opcode = MMC_STOP_TRANSMISSION;
@@ -181,20 +182,31 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
mmc_set_data_timeout(&brq.data, card, rq_data_dir(req) != READ);
- if (rq_data_dir(req) == READ) {
- brq.cmd.opcode = brq.data.blocks > 1 ? MMC_READ_MULTIPLE_BLOCK : MMC_READ_SINGLE_BLOCK;
- brq.data.flags |= MMC_DATA_READ;
- } else {
- brq.cmd.opcode = MMC_WRITE_BLOCK;
- brq.data.flags |= MMC_DATA_WRITE;
+ /*
+ * If the host doesn't support multiple block writes, force
+ * block writes to single block.
+ */
+ if (rq_data_dir(req) != READ &&
+ !(card->host->caps & MMC_CAP_MULTIWRITE))
brq.data.blocks = 1;
- }
if (brq.data.blocks > 1) {
brq.data.flags |= MMC_DATA_MULTI;
brq.mrq.stop = &brq.stop;
+ readcmd = MMC_READ_MULTIPLE_BLOCK;
+ writecmd = MMC_WRITE_MULTIPLE_BLOCK;
} else {
brq.mrq.stop = NULL;
+ readcmd = MMC_READ_SINGLE_BLOCK;
+ writecmd = MMC_WRITE_BLOCK;
+ }
+
+ if (rq_data_dir(req) == READ) {
+ brq.cmd.opcode = readcmd;
+ brq.data.flags |= MMC_DATA_READ;
+ } else {
+ brq.cmd.opcode = writecmd;
+ brq.data.flags |= MMC_DATA_WRITE;
}
brq.data.sg = mq->sg;
@@ -219,27 +231,29 @@ static int mmc_blk_issue_rq(struct mmc_queue *mq, struct request *req)
goto cmd_err;
}
- do {
- int err;
-
- cmd.opcode = MMC_SEND_STATUS;
- cmd.arg = card->rca << 16;
- cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
- err = mmc_wait_for_cmd(card->host, &cmd, 5);
- if (err) {
- printk(KERN_ERR "%s: error %d requesting status\n",
- req->rq_disk->disk_name, err);
- goto cmd_err;
- }
- } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
+ if (rq_data_dir(req) != READ) {
+ do {
+ int err;
+
+ cmd.opcode = MMC_SEND_STATUS;
+ cmd.arg = card->rca << 16;
+ cmd.flags = MMC_RSP_R1 | MMC_CMD_AC;
+ err = mmc_wait_for_cmd(card->host, &cmd, 5);
+ if (err) {
+ printk(KERN_ERR "%s: error %d requesting status\n",
+ req->rq_disk->disk_name, err);
+ goto cmd_err;
+ }
+ } while (!(cmd.resp[0] & R1_READY_FOR_DATA));
#if 0
- if (cmd.resp[0] & ~0x00000900)
- printk(KERN_ERR "%s: status = %08x\n",
- req->rq_disk->disk_name, cmd.resp[0]);
- if (mmc_decode_status(cmd.resp))
- goto cmd_err;
+ if (cmd.resp[0] & ~0x00000900)
+ printk(KERN_ERR "%s: status = %08x\n",
+ req->rq_disk->disk_name, cmd.resp[0]);
+ if (mmc_decode_status(cmd.resp))
+ goto cmd_err;
#endif
+ }
/*
* A block was successfully transferred.
diff --git a/drivers/mmc/mmc_queue.c b/drivers/mmc/mmc_queue.c
index 74f8cdeeff0..4ccdd82b680 100644
--- a/drivers/mmc/mmc_queue.c
+++ b/drivers/mmc/mmc_queue.c
@@ -28,7 +28,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
struct mmc_queue *mq = q->queuedata;
int ret = BLKPREP_KILL;
- if (req->flags & REQ_SPECIAL) {
+ if (blk_special_request(req)) {
/*
* Special commands already have the command
* blocks already setup in req->special.
@@ -36,7 +36,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
BUG_ON(!req->special);
ret = BLKPREP_OK;
- } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+ } else if (blk_fs_request(req) || blk_pc_request(req)) {
/*
* Block I/O requests need translating according
* to the protocol.
@@ -50,7 +50,7 @@ static int mmc_prep_request(struct request_queue *q, struct request *req)
}
if (ret == BLKPREP_OK)
- req->flags |= REQ_DONTPREP;
+ req->cmd_flags |= REQ_DONTPREP;
return ret;
}
diff --git a/drivers/mmc/mmci.c b/drivers/mmc/mmci.c
index 1886562abdd..2b5a0cc9ea5 100644
--- a/drivers/mmc/mmci.c
+++ b/drivers/mmc/mmci.c
@@ -69,12 +69,13 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
unsigned int datactrl, timeout, irqmask;
unsigned long long clks;
void __iomem *base;
+ int blksz_bits;
DBG(host, "blksz %04x blks %04x flags %08x\n",
- 1 << data->blksz_bits, data->blocks, data->flags);
+ data->blksz, data->blocks, data->flags);
host->data = data;
- host->size = data->blocks << data->blksz_bits;
+ host->size = data->blksz;
host->data_xfered = 0;
mmci_init_sg(host, data);
@@ -88,7 +89,10 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
writel(timeout, base + MMCIDATATIMER);
writel(host->size, base + MMCIDATALENGTH);
- datactrl = MCI_DPSM_ENABLE | data->blksz_bits << 4;
+ blksz_bits = ffs(data->blksz) - 1;
+ BUG_ON(1 << blksz_bits != data->blksz);
+
+ datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
if (data->flags & MMC_DATA_READ) {
datactrl |= MCI_DPSM_DIRECTION;
irqmask = MCI_RXFIFOHALFFULLMASK;
@@ -145,7 +149,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
unsigned int status)
{
if (status & MCI_DATABLOCKEND) {
- host->data_xfered += 1 << data->blksz_bits;
+ host->data_xfered += data->blksz;
}
if (status & (MCI_DATACRCFAIL|MCI_DATATIMEOUT|MCI_TXUNDERRUN|MCI_RXOVERRUN)) {
if (status & MCI_DATACRCFAIL)
@@ -505,6 +509,7 @@ static int mmci_probe(struct amba_device *dev, void *id)
mmc->f_min = (host->mclk + 511) / 512;
mmc->f_max = min(host->mclk, fmax);
mmc->ocr_avail = plat->ocr_mask;
+ mmc->caps = MMC_CAP_MULTIWRITE;
/*
* We can do SGIO
diff --git a/drivers/mmc/omap.c b/drivers/mmc/omap.c
index ddf06b32c15..52c9e52e6b7 100644
--- a/drivers/mmc/omap.c
+++ b/drivers/mmc/omap.c
@@ -1034,13 +1034,14 @@ static int __init mmc_omap_probe(struct platform_device *pdev)
host->irq = pdev->resource[1].start;
host->base = (void __iomem*)IO_ADDRESS(r->start);
- if (minfo->wire4)
- mmc->caps |= MMC_CAP_4_BIT_DATA;
-
mmc->ops = &mmc_omap_ops;
mmc->f_min = 400000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
+ mmc->caps = MMC_CAP_BYTEBLOCK;
+
+ if (minfo->wire4)
+ mmc->caps |= MMC_CAP_4_BIT_DATA;
/* Use scatterlist DMA to reduce per-transfer costs.
* NOTE max_seg_size assumption that small blocks aren't
diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c
index 4e21b3b9d33..4dab5ec392e 100644
--- a/drivers/mmc/sdhci.c
+++ b/drivers/mmc/sdhci.c
@@ -4,8 +4,9 @@
* Copyright (C) 2005-2006 Pierre Ossman, All Rights Reserved.
*
* 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.
+ * 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/delay.h>
@@ -1262,7 +1263,7 @@ static int __devinit sdhci_probe_slot(struct pci_dev *pdev, int slot)
mmc->ops = &sdhci_ops;
mmc->f_min = host->max_clk / 256;
mmc->f_max = host->max_clk;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
mmc->ocr_avail = 0;
if (caps & SDHCI_CAN_VDD_330)
diff --git a/drivers/mmc/sdhci.h b/drivers/mmc/sdhci.h
index f2453343f78..72a67937afe 100644
--- a/drivers/mmc/sdhci.h
+++ b/drivers/mmc/sdhci.h
@@ -4,8 +4,9 @@
* Copyright (C) 2005 Pierre Ossman, All Rights Reserved.
*
* 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.
+ * 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.
*/
/*
diff --git a/drivers/mmc/wbsd.c b/drivers/mmc/wbsd.c
index c351c6d1a18..88c6f0b129f 100644
--- a/drivers/mmc/wbsd.c
+++ b/drivers/mmc/wbsd.c
@@ -4,8 +4,9 @@
* Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
*
* 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.
+ * 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.
*
*
* Warning!
@@ -1323,7 +1324,7 @@ static int __devinit wbsd_alloc_mmc(struct device *dev)
mmc->f_min = 375000;
mmc->f_max = 24000000;
mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34;
- mmc->caps = MMC_CAP_4_BIT_DATA;
+ mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE | MMC_CAP_BYTEBLOCK;
spin_lock_init(&host->lock);
diff --git a/drivers/mmc/wbsd.h b/drivers/mmc/wbsd.h
index 249baa701cb..6072993f01e 100644
--- a/drivers/mmc/wbsd.h
+++ b/drivers/mmc/wbsd.h
@@ -4,8 +4,9 @@
* Copyright (C) 2004-2005 Pierre Ossman, All Rights Reserved.
*
* 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.
+ * 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.
*/
#define LOCK_CODE 0xAA
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig
index a03e862851d..a304b34c263 100644
--- a/drivers/mtd/Kconfig
+++ b/drivers/mtd/Kconfig
@@ -166,7 +166,7 @@ config MTD_CHAR
config MTD_BLOCK
tristate "Caching block device access to MTD devices"
- depends on MTD
+ depends on MTD && BLOCK
---help---
Although most flash chips have an erase size too large to be useful
as block devices, it is possible to use MTD devices which are based
@@ -188,7 +188,7 @@ config MTD_BLOCK
config MTD_BLOCK_RO
tristate "Readonly block device access to MTD devices"
- depends on MTD_BLOCK!=y && MTD
+ depends on MTD_BLOCK!=y && MTD && BLOCK
help
This allows you to mount read-only file systems (such as cramfs)
from an MTD device, without the overhead (and danger) of the caching
@@ -199,7 +199,7 @@ config MTD_BLOCK_RO
config FTL
tristate "FTL (Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the original Flash Translation Layer which
is part of the PCMCIA specification. It uses a kind of pseudo-
@@ -215,7 +215,7 @@ config FTL
config NFTL
tristate "NFTL (NAND Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the NAND Flash Translation Layer which is
used on M-Systems' DiskOnChip devices. It uses a kind of pseudo-
@@ -238,7 +238,7 @@ config NFTL_RW
config INFTL
tristate "INFTL (Inverse NAND Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the Inverse NAND Flash Translation
Layer which is used on M-Systems' newer DiskOnChip devices. It
@@ -255,7 +255,7 @@ config INFTL
config RFD_FTL
tristate "Resident Flash Disk (Flash Translation Layer) support"
- depends on MTD
+ depends on MTD && BLOCK
---help---
This provides support for the flash translation layer known
as the Resident Flash Disk (RFD), as used by the Embedded BIOS
diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig
index 16c02b5ccf7..440f6851da6 100644
--- a/drivers/mtd/devices/Kconfig
+++ b/drivers/mtd/devices/Kconfig
@@ -136,7 +136,7 @@ config MTDRAM_ABS_POS
config MTD_BLOCK2MTD
tristate "MTD using block device"
- depends on MTD
+ depends on MTD && BLOCK
help
This driver allows a block device to appear as an MTD. It would
generally be used in the following cases:
diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c
index 642d96bc891..2cc90243627 100644
--- a/drivers/mtd/maps/arctic-mtd.c
+++ b/drivers/mtd/maps/arctic-mtd.c
@@ -96,7 +96,7 @@ static struct mtd_partition arctic_partitions[PARTITIONS] = {
static int __init
init_arctic_mtd(void)
{
- int err = 0;
+ int err;
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
@@ -112,7 +112,7 @@ init_arctic_mtd(void)
arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
if (!arctic_mtd) {
- iounmap((void *) arctic_mtd_map.virt);
+ iounmap(arctic_mtd_map.virt);
return -ENXIO;
}
@@ -121,7 +121,7 @@ init_arctic_mtd(void)
err = add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
if (err) {
printk("%s: add_mtd_partitions failed\n", NAME);
- iounmap((void *) arctic_mtd_map.virt);
+ iounmap(arctic_mtd_map.virt);
}
return err;
diff --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c
index a64b1a5ab31..d76d5981b86 100644
--- a/drivers/mtd/maps/beech-mtd.c
+++ b/drivers/mtd/maps/beech-mtd.c
@@ -72,7 +72,7 @@ static struct mtd_partition beech_partitions[2] = {
static int __init
init_beech_mtd(void)
{
- int err = 0;
+ int err;
printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
@@ -89,7 +89,7 @@ init_beech_mtd(void)
beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
if (!beech_mtd) {
- iounmap((void *) beech_mtd_map.virt);
+ iounmap(beech_mtd_map.virt);
return -ENXIO;
}
@@ -98,7 +98,7 @@ init_beech_mtd(void)
err = add_mtd_partitions(beech_mtd, beech_partitions, 2);
if (err) {
printk("%s: add_mtd_partitions failed\n", NAME);
- iounmap((void *) beech_mtd_map.virt);
+ iounmap(beech_mtd_map.virt);
}
return err;
diff --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c
index d6bef100d69..df2c38ef105 100644
--- a/drivers/mtd/maps/cstm_mips_ixx.c
+++ b/drivers/mtd/maps/cstm_mips_ixx.c
@@ -175,8 +175,8 @@ int __init init_cstm_mips_ixx(void)
printk(KERN_WARNING "Failed to ioremap\n");
for (j = 0; j < i; j++) {
if (cstm_mips_ixx_map[j].virt) {
- iounmap((void *)cstm_mips_ixx_map[j].virt);
- cstm_mips_ixx_map[j].virt = 0;
+ iounmap(cstm_mips_ixx_map[j].virt);
+ cstm_mips_ixx_map[j].virt = NULL;
}
}
return -EIO;
@@ -214,8 +214,8 @@ int __init init_cstm_mips_ixx(void)
else {
for (i = 0; i < PHYSMAP_NUMBER; i++) {
if (cstm_mips_ixx_map[i].virt) {
- iounmap((void *)cstm_mips_ixx_map[i].virt);
- cstm_mips_ixx_map[i].virt = 0;
+ iounmap(cstm_mips_ixx_map[i].virt);
+ cstm_mips_ixx_map[i].virt = NULL;
}
}
return -ENXIO;
diff --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c
index 198e840ff6d..f9e8e5bcbc3 100644
--- a/drivers/mtd/maps/nettel.c
+++ b/drivers/mtd/maps/nettel.c
@@ -463,7 +463,7 @@ int __init nettel_init(void)
#ifdef CONFIG_MTD_CFI_INTELEXT
out_unmap1:
- iounmap((void *) nettel_intel_map.virt);
+ iounmap(nettel_intel_map.virt);
#endif
out_unmap2:
diff --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c
index 2257d2b500c..4d858b3d5f8 100644
--- a/drivers/mtd/maps/redwood.c
+++ b/drivers/mtd/maps/redwood.c
@@ -126,7 +126,7 @@ static struct mtd_info *redwood_mtd;
int __init init_redwood_flash(void)
{
- int err = 0;
+ int err;
printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
WINDOW_SIZE, WINDOW_ADDR);
diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c
index 458d3c8ae1e..178b53b56be 100644
--- a/drivers/mtd/mtd_blkdevs.c
+++ b/drivers/mtd/mtd_blkdevs.c
@@ -46,7 +46,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
nsect = req->current_nr_sectors;
buf = req->buffer;
- if (!(req->flags & REQ_CMD))
+ if (!blk_fs_request(req))
return 0;
if (block + nsect > get_capacity(req->rq_disk))
@@ -69,7 +69,7 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr,
return 1;
default:
- printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
+ printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req));
return 0;
}
}
diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c
index 12017f3c6bd..1daf8231aae 100644
--- a/drivers/mtd/nand/edb7312.c
+++ b/drivers/mtd/nand/edb7312.c
@@ -199,7 +199,7 @@ static void __exit ep7312_cleanup(void)
nand_release(ap7312_mtd);
/* Release io resource */
- iounmap((void *)this->IO_ADDR_R);
+ iounmap(this->IO_ADDR_R);
/* Free the MTD device structure */
kfree(ep7312_mtd);
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 975b2ef6112..baece61169f 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -415,7 +415,7 @@ static int nand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip,
* Wait for the ready pin, after a command
* The timeout is catched later.
*/
-static void nand_wait_ready(struct mtd_info *mtd)
+void nand_wait_ready(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
unsigned long timeo = jiffies + 2;
@@ -429,6 +429,7 @@ static void nand_wait_ready(struct mtd_info *mtd)
} while (time_before(jiffies, timeo));
led_trigger_event(nand_led_trigger, LED_OFF);
}
+EXPORT_SYMBOL_GPL(nand_wait_ready);
/**
* nand_command - [DEFAULT] Send command to NAND device
@@ -766,8 +767,8 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
- uint8_t *ecc_code = chip->buffers.ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
nand_read_page_raw(mtd, chip, buf);
@@ -808,8 +809,8 @@ static int nand_read_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
uint8_t *p = buf;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
- uint8_t *ecc_code = chip->buffers.ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ uint8_t *ecc_code = chip->buffers->ecccode;
int *eccpos = chip->ecc.layout->eccpos;
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
@@ -970,7 +971,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
page = realpage & chip->pagemask;
col = (int)(from & (mtd->writesize - 1));
- chip->oob_poi = chip->buffers.oobrbuf;
+ chip->oob_poi = chip->buffers->oobrbuf;
buf = ops->datbuf;
oob = ops->oobbuf;
@@ -981,7 +982,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
/* Is the current page in the buffer ? */
if (realpage != chip->pagebuf || oob) {
- bufpoi = aligned ? buf : chip->buffers.databuf;
+ bufpoi = aligned ? buf : chip->buffers->databuf;
if (likely(sndcmd)) {
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
@@ -989,14 +990,17 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
}
/* Now read the page into the buffer */
- ret = chip->ecc.read_page(mtd, chip, bufpoi);
+ if (unlikely(ops->mode == MTD_OOB_RAW))
+ ret = chip->ecc.read_page_raw(mtd, chip, bufpoi);
+ else
+ ret = chip->ecc.read_page(mtd, chip, bufpoi);
if (ret < 0)
break;
/* Transfer not aligned data */
if (!aligned) {
chip->pagebuf = realpage;
- memcpy(buf, chip->buffers.databuf + col, bytes);
+ memcpy(buf, chip->buffers->databuf + col, bytes);
}
buf += bytes;
@@ -1023,7 +1027,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
nand_wait_ready(mtd);
}
} else {
- memcpy(buf, chip->buffers.databuf + col, bytes);
+ memcpy(buf, chip->buffers->databuf + col, bytes);
buf += bytes;
}
@@ -1266,7 +1270,7 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
realpage = (int)(from >> chip->page_shift);
page = realpage & chip->pagemask;
- chip->oob_poi = chip->buffers.oobrbuf;
+ chip->oob_poi = chip->buffers->oobrbuf;
while(1) {
sndcmd = chip->ecc.read_oob(mtd, chip, page, sndcmd);
@@ -1322,8 +1326,6 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from,
static int nand_read_oob(struct mtd_info *mtd, loff_t from,
struct mtd_oob_ops *ops)
{
- int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
- uint8_t *buf) = NULL;
struct nand_chip *chip = mtd->priv;
int ret = -ENOTSUPP;
@@ -1341,12 +1343,7 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
- break;
-
case MTD_OOB_RAW:
- /* Replace the read_page algorithm temporary */
- read_page = chip->ecc.read_page;
- chip->ecc.read_page = nand_read_page_raw;
break;
default:
@@ -1358,8 +1355,6 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
else
ret = nand_do_read_ops(mtd, from, ops);
- if (unlikely(ops->mode == MTD_OOB_RAW))
- chip->ecc.read_page = read_page;
out:
nand_release_device(mtd);
return ret;
@@ -1391,7 +1386,7 @@ static void nand_write_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
int *eccpos = chip->ecc.layout->eccpos;
@@ -1417,7 +1412,7 @@ static void nand_write_page_hwecc(struct mtd_info *mtd, struct nand_chip *chip,
int i, eccsize = chip->ecc.size;
int eccbytes = chip->ecc.bytes;
int eccsteps = chip->ecc.steps;
- uint8_t *ecc_calc = chip->buffers.ecccalc;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
const uint8_t *p = buf;
int *eccpos = chip->ecc.layout->eccpos;
@@ -1478,7 +1473,7 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
}
/**
- * nand_write_page - [INTERNAL] write one page
+ * nand_write_page - [REPLACEABLE] write one page
* @mtd: MTD device structure
* @chip: NAND chip descriptor
* @buf: the data to write
@@ -1486,13 +1481,16 @@ static void nand_write_page_syndrome(struct mtd_info *mtd,
* @cached: cached programming
*/
static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf, int page, int cached)
+ const uint8_t *buf, int page, int cached, int raw)
{
int status;
chip->cmdfunc(mtd, NAND_CMD_SEQIN, 0x00, page);
- chip->ecc.write_page(mtd, chip, buf);
+ if (unlikely(raw))
+ chip->ecc.write_page_raw(mtd, chip, buf);
+ else
+ chip->ecc.write_page(mtd, chip, buf);
/*
* Cached progamming disabled for now, Not sure if its worth the
@@ -1627,7 +1625,7 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers.oobwbuf;
+ chip->oob_poi = chip->buffers->oobwbuf;
while(1) {
int cached = writelen > bytes && page != blockmask;
@@ -1635,7 +1633,8 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to,
if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops);
- ret = nand_write_page(mtd, chip, buf, page, cached);
+ ret = chip->write_page(mtd, chip, buf, page, cached,
+ (ops->mode == MTD_OOB_RAW));
if (ret)
break;
@@ -1745,7 +1744,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
if (page == chip->pagebuf)
chip->pagebuf = -1;
- chip->oob_poi = chip->buffers.oobwbuf;
+ chip->oob_poi = chip->buffers->oobwbuf;
memset(chip->oob_poi, 0xff, mtd->oobsize);
nand_fill_oob(chip, ops->oobbuf, ops);
status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask);
@@ -1768,8 +1767,6 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
static int nand_write_oob(struct mtd_info *mtd, loff_t to,
struct mtd_oob_ops *ops)
{
- void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
- const uint8_t *buf) = NULL;
struct nand_chip *chip = mtd->priv;
int ret = -ENOTSUPP;
@@ -1787,12 +1784,7 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_AUTO:
- break;
-
case MTD_OOB_RAW:
- /* Replace the write_page algorithm temporary */
- write_page = chip->ecc.write_page;
- chip->ecc.write_page = nand_write_page_raw;
break;
default:
@@ -1804,8 +1796,6 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
else
ret = nand_do_write_ops(mtd, to, ops);
- if (unlikely(ops->mode == MTD_OOB_RAW))
- chip->ecc.write_page = write_page;
out:
nand_release_device(mtd);
return ret;
@@ -2288,40 +2278,22 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd,
return type;
}
-/* module_text_address() isn't exported, and it's mostly a pointless
- test if this is a module _anyway_ -- they'd have to try _really_ hard
- to call us from in-kernel code if the core NAND support is modular. */
-#ifdef MODULE
-#define caller_is_module() (1)
-#else
-#define caller_is_module() \
- module_text_address((unsigned long)__builtin_return_address(0))
-#endif
-
/**
- * nand_scan - [NAND Interface] Scan for the NAND device
- * @mtd: MTD device structure
- * @maxchips: Number of chips to scan for
+ * nand_scan_ident - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
*
- * This fills out all the uninitialized function pointers
- * with the defaults.
- * The flash ID is read and the mtd/chip structures are
- * filled with the appropriate values.
- * The mtd->owner field must be set to the module of the caller
+ * This is the first phase of the normal nand_scan() function. It
+ * reads the flash ID and sets up MTD fields accordingly.
*
+ * The mtd->owner field must be set to the module of the caller.
*/
-int nand_scan(struct mtd_info *mtd, int maxchips)
+int nand_scan_ident(struct mtd_info *mtd, int maxchips)
{
int i, busw, nand_maf_id;
struct nand_chip *chip = mtd->priv;
struct nand_flash_dev *type;
- /* Many callers got this wrong, so check for it for a while... */
- if (!mtd->owner && caller_is_module()) {
- printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
- BUG();
- }
-
/* Get buswidth to select the correct functions */
busw = chip->options & NAND_BUSWIDTH_16;
/* Set the default functions */
@@ -2353,8 +2325,31 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
chip->numchips = i;
mtd->size = i * chip->chipsize;
+ return 0;
+}
+
+
+/**
+ * nand_scan_tail - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
+ *
+ * This is the second phase of the normal nand_scan() function. It
+ * fills out all the uninitialized function pointers with the defaults
+ * and scans for a bad block table if appropriate.
+ */
+int nand_scan_tail(struct mtd_info *mtd)
+{
+ int i;
+ struct nand_chip *chip = mtd->priv;
+
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ chip->buffers = kmalloc(sizeof(*chip->buffers), GFP_KERNEL);
+ if (!chip->buffers)
+ return -ENOMEM;
+
/* Preset the internal oob write buffer */
- memset(chip->buffers.oobwbuf, 0xff, mtd->oobsize);
+ memset(chip->buffers->oobwbuf, 0xff, mtd->oobsize);
/*
* If no default placement scheme is given, select an appropriate one
@@ -2377,10 +2372,18 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
}
}
+ if (!chip->write_page)
+ chip->write_page = nand_write_page;
+
/*
* check ECC mode, default to software if 3byte/512byte hardware ECC is
* selected and we have 256 byte pagesize fallback to software ECC
*/
+ if (!chip->ecc.read_page_raw)
+ chip->ecc.read_page_raw = nand_read_page_raw;
+ if (!chip->ecc.write_page_raw)
+ chip->ecc.write_page_raw = nand_write_page_raw;
+
switch (chip->ecc.mode) {
case NAND_ECC_HW:
/* Use standard hwecc read page function ? */
@@ -2438,6 +2441,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
chip->ecc.size = mtd->writesize;
chip->ecc.bytes = 0;
break;
+
default:
printk(KERN_WARNING "Invalid NAND_ECC_MODE %d\n",
chip->ecc.mode);
@@ -2503,6 +2507,44 @@ int nand_scan(struct mtd_info *mtd, int maxchips)
return chip->scan_bbt(mtd);
}
+/* module_text_address() isn't exported, and it's mostly a pointless
+ test if this is a module _anyway_ -- they'd have to try _really_ hard
+ to call us from in-kernel code if the core NAND support is modular. */
+#ifdef MODULE
+#define caller_is_module() (1)
+#else
+#define caller_is_module() \
+ module_text_address((unsigned long)__builtin_return_address(0))
+#endif
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd: MTD device structure
+ * @maxchips: Number of chips to scan for
+ *
+ * This fills out all the uninitialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values.
+ * The mtd->owner field must be set to the module of the caller
+ *
+ */
+int nand_scan(struct mtd_info *mtd, int maxchips)
+{
+ int ret;
+
+ /* Many callers got this wrong, so check for it for a while... */
+ if (!mtd->owner && caller_is_module()) {
+ printk(KERN_CRIT "nand_scan() called with NULL mtd->owner!\n");
+ BUG();
+ }
+
+ ret = nand_scan_ident(mtd, maxchips);
+ if (!ret)
+ ret = nand_scan_tail(mtd);
+ return ret;
+}
+
/**
* nand_release - [NAND Interface] Free resources held by the NAND device
* @mtd: MTD device structure
@@ -2520,9 +2562,13 @@ void nand_release(struct mtd_info *mtd)
/* Free bad block table memory */
kfree(chip->bbt);
+ if (!(chip->options & NAND_OWN_BUFFERS))
+ kfree(chip->buffers);
}
EXPORT_SYMBOL_GPL(nand_scan);
+EXPORT_SYMBOL_GPL(nand_scan_ident);
+EXPORT_SYMBOL_GPL(nand_scan_tail);
EXPORT_SYMBOL_GPL(nand_release);
static int __init nand_base_init(void)
diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c
index a612c4ea819..9402653eb09 100644
--- a/drivers/mtd/nand/nand_bbt.c
+++ b/drivers/mtd/nand/nand_bbt.c
@@ -759,7 +759,7 @@ static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *b
struct nand_chip *this = mtd->priv;
bd->options &= ~NAND_BBT_SCANEMPTY;
- return create_bbt(mtd, this->buffers.databuf, bd, -1);
+ return create_bbt(mtd, this->buffers->databuf, bd, -1);
}
/**
diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c
index dd5cea8b4a7..b5a5f8da472 100644
--- a/drivers/mtd/nftlcore.c
+++ b/drivers/mtd/nftlcore.c
@@ -175,6 +175,8 @@ int nftl_write_oob(struct mtd_info *mtd, loff_t offs, size_t len,
return res;
}
+#ifdef CONFIG_NFTL_RW
+
/*
* Write data and oob to flash
*/
@@ -196,8 +198,6 @@ static int nftl_write(struct mtd_info *mtd, loff_t offs, size_t len,
return res;
}
-#ifdef CONFIG_NFTL_RW
-
/* Actual NFTL access routines */
/* NFTL_findfreeblock: Find a free Erase Unit on the NFTL partition. This function is used
* when the give Virtual Unit Chain
diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
index 5930a03736d..465961b8bcd 100644
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -43,10 +43,4 @@ config MTD_ONENAND_OTP
OTP block is fully-guaranteed to be a valid block.
-config MTD_ONENAND_SYNC_READ
- bool "OneNAND Sync. Burst Read Support"
- depends on ARCH_OMAP
- help
- This enables support for Sync. Burst Read.
-
endmenu
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
index 84ec40d2543..8ed68b28afe 100644
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -1,7 +1,7 @@
/*
* linux/drivers/mtd/onenand/onenand_base.c
*
- * Copyright (C) 2005 Samsung Electronics
+ * Copyright (C) 2005-2006 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -199,6 +199,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le
case ONENAND_CMD_UNLOCK:
case ONENAND_CMD_LOCK:
case ONENAND_CMD_LOCK_TIGHT:
+ case ONENAND_CMD_UNLOCK_ALL:
block = -1;
page = -1;
break;
@@ -1211,11 +1212,11 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
end = len >> this->erase_shift;
/* Continuous lock scheme */
- if (this->options & ONENAND_CONT_LOCK) {
+ if (this->options & ONENAND_HAS_CONT_LOCK) {
/* Set start block address */
this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
/* Set end block address */
- this->write_word(end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
+ this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS);
/* Write unlock command */
this->command(mtd, ONENAND_CMD_UNLOCK, 0, 0);
@@ -1236,7 +1237,7 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
}
/* Block lock scheme */
- for (block = start; block < end; block++) {
+ for (block = start; block < start + end; block++) {
/* Set block address */
value = onenand_block_address(this, block);
this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
@@ -1265,6 +1266,79 @@ static int onenand_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
return 0;
}
+/**
+ * onenand_check_lock_status - [OneNAND Interface] Check lock status
+ * @param this onenand chip data structure
+ *
+ * Check lock status
+ */
+static void onenand_check_lock_status(struct onenand_chip *this)
+{
+ unsigned int value, block, status;
+ unsigned int end;
+
+ end = this->chipsize >> this->erase_shift;
+ for (block = 0; block < end; block++) {
+ /* Set block address */
+ value = onenand_block_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1);
+ /* Select DataRAM for DDP */
+ value = onenand_bufferram_address(this, block);
+ this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2);
+ /* Set start block address */
+ this->write_word(block, this->base + ONENAND_REG_START_BLOCK_ADDRESS);
+
+ /* Check lock status */
+ status = this->read_word(this->base + ONENAND_REG_WP_STATUS);
+ if (!(status & ONENAND_WP_US))
+ printk(KERN_ERR "block = %d, wp status = 0x%x\n", block, status);
+ }
+}
+
+/**
+ * onenand_unlock_all - [OneNAND Interface] unlock all blocks
+ * @param mtd MTD device structure
+ *
+ * Unlock all blocks
+ */
+static int onenand_unlock_all(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+
+ if (this->options & ONENAND_HAS_UNLOCK_ALL) {
+ /* Write unlock command */
+ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0);
+
+ /* There's no return value */
+ this->wait(mtd, FL_UNLOCKING);
+
+ /* Sanity check */
+ while (this->read_word(this->base + ONENAND_REG_CTRL_STATUS)
+ & ONENAND_CTRL_ONGO)
+ continue;
+
+ /* Workaround for all block unlock in DDP */
+ if (this->device_id & ONENAND_DEVICE_IS_DDP) {
+ loff_t ofs;
+ size_t len;
+
+ /* 1st block on another chip */
+ ofs = this->chipsize >> 1;
+ len = 1 << this->erase_shift;
+
+ onenand_unlock(mtd, ofs, len);
+ }
+
+ onenand_check_lock_status(this);
+
+ return 0;
+ }
+
+ mtd->unlock(mtd, 0x0, this->chipsize);
+
+ return 0;
+}
+
#ifdef CONFIG_MTD_ONENAND_OTP
/* Interal OTP operation */
@@ -1564,12 +1638,43 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from,
#endif /* CONFIG_MTD_ONENAND_OTP */
/**
+ * onenand_lock_scheme - Check and set OneNAND lock scheme
+ * @param mtd MTD data structure
+ *
+ * Check and set OneNAND lock scheme
+ */
+static void onenand_lock_scheme(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ unsigned int density, process;
+
+ /* Lock scheme depends on density and process */
+ density = this->device_id >> ONENAND_DEVICE_DENSITY_SHIFT;
+ process = this->version_id >> ONENAND_VERSION_PROCESS_SHIFT;
+
+ /* Lock scheme */
+ if (density >= ONENAND_DEVICE_DENSITY_1Gb) {
+ /* A-Die has all block unlock */
+ if (process) {
+ printk(KERN_DEBUG "Chip support all block unlock\n");
+ this->options |= ONENAND_HAS_UNLOCK_ALL;
+ }
+ } else {
+ /* Some OneNAND has continues lock scheme */
+ if (!process) {
+ printk(KERN_DEBUG "Lock scheme is Continues Lock\n");
+ this->options |= ONENAND_HAS_CONT_LOCK;
+ }
+ }
+}
+
+/**
* onenand_print_device_info - Print device ID
* @param device device ID
*
* Print device ID
*/
-static void onenand_print_device_info(int device)
+static void onenand_print_device_info(int device, int version)
{
int vcc, demuxed, ddp, density;
@@ -1583,6 +1688,7 @@ static void onenand_print_device_info(int device)
(16 << density),
vcc ? "2.65/3.3" : "1.8",
device);
+ printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version);
}
static const struct onenand_manufacturers onenand_manuf_ids[] = {
@@ -1625,9 +1731,14 @@ static int onenand_check_maf(int manuf)
static int onenand_probe(struct mtd_info *mtd)
{
struct onenand_chip *this = mtd->priv;
- int bram_maf_id, bram_dev_id, maf_id, dev_id;
- int version_id;
+ int bram_maf_id, bram_dev_id, maf_id, dev_id, ver_id;
int density;
+ int syscfg;
+
+ /* Save system configuration 1 */
+ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1);
+ /* Clear Sync. Burst Read mode to read BootRAM */
+ this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1);
/* Send the command for reading device ID from BootRAM */
this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM);
@@ -1636,24 +1747,31 @@ static int onenand_probe(struct mtd_info *mtd)
bram_maf_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x0);
bram_dev_id = this->read_word(this->base + ONENAND_BOOTRAM + 0x2);
+ /* Reset OneNAND to read default register values */
+ this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
+ /* Wait reset */
+ this->wait(mtd, FL_RESETING);
+
+ /* Restore system configuration 1 */
+ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1);
+
/* Check manufacturer ID */
if (onenand_check_maf(bram_maf_id))
return -ENXIO;
- /* Reset OneNAND to read default register values */
- this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_BOOTRAM);
-
/* Read manufacturer and device IDs from Register */
maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID);
dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID);
+ ver_id= this->read_word(this->base + ONENAND_REG_VERSION_ID);
/* Check OneNAND device */
if (maf_id != bram_maf_id || dev_id != bram_dev_id)
return -ENXIO;
/* Flash device information */
- onenand_print_device_info(dev_id);
+ onenand_print_device_info(dev_id, ver_id);
this->device_id = dev_id;
+ this->version_id = ver_id;
density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT;
this->chipsize = (16 << density) << 20;
@@ -1676,16 +1794,8 @@ static int onenand_probe(struct mtd_info *mtd)
mtd->size = this->chipsize;
- /* Version ID */
- version_id = this->read_word(this->base + ONENAND_REG_VERSION_ID);
- printk(KERN_DEBUG "OneNAND version = 0x%04x\n", version_id);
-
- /* Lock scheme */
- if (density <= ONENAND_DEVICE_DENSITY_512Mb &&
- !(version_id >> ONENAND_VERSION_PROCESS_SHIFT)) {
- printk(KERN_INFO "Lock scheme is Continues Lock\n");
- this->options |= ONENAND_CONT_LOCK;
- }
+ /* Check OneNAND lock scheme */
+ onenand_lock_scheme(mtd);
return 0;
}
@@ -1821,7 +1931,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips)
mtd->owner = THIS_MODULE;
/* Unlock whole block */
- mtd->unlock(mtd, 0x0, this->chipsize);
+ onenand_unlock_all(mtd);
return this->scan_bbt(mtd);
}
diff --git a/drivers/net/3c509.c b/drivers/net/3c509.c
index 59c33925be6..b936373ab2a 100644
--- a/drivers/net/3c509.c
+++ b/drivers/net/3c509.c
@@ -225,6 +225,7 @@ static struct eisa_device_id el3_eisa_ids[] = {
{ "TCM5095" },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, el3_eisa_ids);
static int el3_eisa_probe (struct device *device);
diff --git a/drivers/net/3c59x.c b/drivers/net/3c59x.c
index af301f09d67..df42e28cc80 100644
--- a/drivers/net/3c59x.c
+++ b/drivers/net/3c59x.c
@@ -851,6 +851,7 @@ static struct eisa_device_id vortex_eisa_ids[] = {
{ "TCM5970", CH_3C597 },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, vortex_eisa_ids);
static int vortex_eisa_probe(struct device *device);
static int vortex_eisa_remove(struct device *device);
diff --git a/drivers/net/8390.c b/drivers/net/8390.c
index 5b6b05ed8f3..9d34056147a 100644
--- a/drivers/net/8390.c
+++ b/drivers/net/8390.c
@@ -299,7 +299,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
* Slow phase with lock held.
*/
- disable_irq_nosync_lockdep(dev->irq);
+ disable_irq_nosync_lockdep_irqsave(dev->irq, &flags);
spin_lock(&ei_local->page_lock);
@@ -338,7 +338,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
netif_stop_queue(dev);
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
spin_unlock(&ei_local->page_lock);
- enable_irq_lockdep(dev->irq);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
ei_local->stat.tx_errors++;
return 1;
}
@@ -379,7 +379,7 @@ static int ei_start_xmit(struct sk_buff *skb, struct net_device *dev)
outb_p(ENISR_ALL, e8390_base + EN0_IMR);
spin_unlock(&ei_local->page_lock);
- enable_irq_lockdep(dev->irq);
+ enable_irq_lockdep_irqrestore(dev->irq, &flags);
dev_kfree_skb (skb);
ei_local->stat.tx_bytes += send_length;
diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig
index 63154774c25..ff8a8c0a26d 100644
--- a/drivers/net/Kconfig
+++ b/drivers/net/Kconfig
@@ -24,6 +24,9 @@ config NETDEVICES
If unsure, say Y.
+# All the following symbols are dependent on NETDEVICES - do not repeat
+# that for each of the symbols.
+if NETDEVICES
config IFB
tristate "Intermediate Functional Block support"
@@ -2852,6 +2855,8 @@ config NETCONSOLE
If you want to log kernel messages over the network, enable this.
See <file:Documentation/networking/netconsole.txt> for details.
+endif #NETDEVICES
+
config NETPOLL
def_bool NETCONSOLE
diff --git a/drivers/net/Space.c b/drivers/net/Space.c
index 9953201c670..a67f5efc983 100644
--- a/drivers/net/Space.c
+++ b/drivers/net/Space.c
@@ -165,7 +165,7 @@ static struct devprobe2 mca_probes[] __initdata = {
* look for EISA/PCI/MCA cards in addition to ISA cards).
*/
static struct devprobe2 isa_probes[] __initdata = {
-#ifdef CONFIG_HP100 /* ISA, EISA & PCI */
+#if defined(CONFIG_HP100) && defined(CONFIG_ISA) /* ISA, EISA */
{hp100_probe, 0},
#endif
#ifdef CONFIG_3C515
diff --git a/drivers/net/acenic.c b/drivers/net/acenic.c
index a075246f6f4..71a4f60f732 100644
--- a/drivers/net/acenic.c
+++ b/drivers/net/acenic.c
@@ -163,11 +163,7 @@ MODULE_DEVICE_TABLE(pci, acenic_pci_tbl);
#define SET_NETDEV_DEV(net, pdev) do{} while(0)
#endif
-#if LINUX_VERSION_CODE >= 0x2051c
#define ace_sync_irq(irq) synchronize_irq(irq)
-#else
-#define ace_sync_irq(irq) synchronize_irq()
-#endif
#ifndef offset_in_page
#define offset_in_page(ptr) ((unsigned long)(ptr) & ~PAGE_MASK)
diff --git a/drivers/net/appletalk/ipddp.c b/drivers/net/appletalk/ipddp.c
index 7f7dd450226..b98592a8bac 100644
--- a/drivers/net/appletalk/ipddp.c
+++ b/drivers/net/appletalk/ipddp.c
@@ -145,9 +145,7 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
/* Create the Extended DDP header */
ddp = (struct ddpehdr *)skb->data;
- ddp->deh_len = skb->len;
- ddp->deh_hops = 1;
- ddp->deh_pad = 0;
+ ddp->deh_len_hops = htons(skb->len + (1<<10));
ddp->deh_sum = 0;
/*
@@ -170,7 +168,6 @@ static int ipddp_xmit(struct sk_buff *skb, struct net_device *dev)
ddp->deh_sport = 72;
*((__u8 *)(ddp+1)) = 22; /* ddp type = IP */
- *((__u16 *)ddp)=ntohs(*((__u16 *)ddp)); /* fix up length field */
skb->protocol = htons(ETH_P_ATALK); /* Protocol has changed */
diff --git a/drivers/net/arm/at91_ether.c b/drivers/net/arm/at91_ether.c
index 95b28aa01f4..3ecf2cc53a7 100644
--- a/drivers/net/arm/at91_ether.c
+++ b/drivers/net/arm/at91_ether.c
@@ -947,7 +947,7 @@ static int __init at91ether_setup(unsigned long phy_type, unsigned short phy_add
return -ENOMEM;
dev->base_addr = AT91_VA_BASE_EMAC;
- dev->irq = AT91_ID_EMAC;
+ dev->irq = AT91RM9200_ID_EMAC;
SET_MODULE_OWNER(dev);
/* Install the interrupt handler */
diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c
index 7fcf015021e..6b4edb63c4c 100644
--- a/drivers/net/bnx2.c
+++ b/drivers/net/bnx2.c
@@ -56,8 +56,8 @@
#define DRV_MODULE_NAME "bnx2"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "1.4.44"
-#define DRV_MODULE_RELDATE "August 10, 2006"
+#define DRV_MODULE_VERSION "1.4.45"
+#define DRV_MODULE_RELDATE "September 29, 2006"
#define RUN_AT(x) (jiffies + (x))
@@ -5805,6 +5805,34 @@ bnx2_init_board(struct pci_dev *pdev, struct net_device *dev)
bp->cmd_ticks_int = bp->cmd_ticks;
}
+ /* Disable MSI on 5706 if AMD 8132 bridge is found.
+ *
+ * MSI is defined to be 32-bit write. The 5706 does 64-bit MSI writes
+ * with byte enables disabled on the unused 32-bit word. This is legal
+ * but causes problems on the AMD 8132 which will eventually stop
+ * responding after a while.
+ *
+ * AMD believes this incompatibility is unique to the 5706, and
+ * prefers to locally disable MSI rather than globally disabling it
+ * using pci_msi_quirk.
+ */
+ if (CHIP_NUM(bp) == CHIP_NUM_5706 && disable_msi == 0) {
+ struct pci_dev *amd_8132 = NULL;
+
+ while ((amd_8132 = pci_get_device(PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_8132_BRIDGE,
+ amd_8132))) {
+ u8 rev;
+
+ pci_read_config_byte(amd_8132, PCI_REVISION_ID, &rev);
+ if (rev >= 0x10 && rev <= 0x13) {
+ disable_msi = 1;
+ pci_dev_put(amd_8132);
+ break;
+ }
+ }
+ }
+
bp->autoneg = AUTONEG_SPEED | AUTONEG_FLOW_CTRL;
bp->req_line_speed = 0;
if (bp->phy_flags & PHY_SERDES_FLAG) {
diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c
index 6a407070c2e..3fb354d9c51 100644
--- a/drivers/net/bonding/bond_3ad.c
+++ b/drivers/net/bonding/bond_3ad.c
@@ -85,6 +85,7 @@
#define AD_LINK_SPEED_BITMASK_10MBPS 0x2
#define AD_LINK_SPEED_BITMASK_100MBPS 0x4
#define AD_LINK_SPEED_BITMASK_1000MBPS 0x8
+#define AD_LINK_SPEED_BITMASK_10000MBPS 0x10
//endalloun
// compare MAC addresses
@@ -99,7 +100,7 @@ static u16 __get_link_speed(struct port *port);
static u8 __get_duplex(struct port *port);
static inline void __initialize_port_locks(struct port *port);
//conversions
-static void __ntohs_lacpdu(struct lacpdu *lacpdu);
+static void __htons_lacpdu(struct lacpdu *lacpdu);
static u16 __ad_timer_to_ticks(u16 timer_type, u16 Par);
@@ -330,7 +331,8 @@ static inline void __release_rx_machine_lock(struct port *port)
* 0,
* %AD_LINK_SPEED_BITMASK_10MBPS,
* %AD_LINK_SPEED_BITMASK_100MBPS,
- * %AD_LINK_SPEED_BITMASK_1000MBPS
+ * %AD_LINK_SPEED_BITMASK_1000MBPS,
+ * %AD_LINK_SPEED_BITMASK_10000MBPS
*/
static u16 __get_link_speed(struct port *port)
{
@@ -357,6 +359,10 @@ static u16 __get_link_speed(struct port *port)
speed = AD_LINK_SPEED_BITMASK_1000MBPS;
break;
+ case SPEED_10000:
+ speed = AD_LINK_SPEED_BITMASK_10000MBPS;
+ break;
+
default:
speed = 0; // unknown speed value from ethtool. shouldn't happen
break;
@@ -414,23 +420,23 @@ static inline void __initialize_port_locks(struct port *port)
//conversions
/**
- * __ntohs_lacpdu - convert the contents of a LACPDU to host byte order
+ * __htons_lacpdu - convert the contents of a LACPDU to network byte order
* @lacpdu: the speicifed lacpdu
*
* For each multi-byte field in the lacpdu, convert its content
*/
-static void __ntohs_lacpdu(struct lacpdu *lacpdu)
+static void __htons_lacpdu(struct lacpdu *lacpdu)
{
if (lacpdu) {
- lacpdu->actor_system_priority = ntohs(lacpdu->actor_system_priority);
- lacpdu->actor_key = ntohs(lacpdu->actor_key);
- lacpdu->actor_port_priority = ntohs(lacpdu->actor_port_priority);
- lacpdu->actor_port = ntohs(lacpdu->actor_port);
- lacpdu->partner_system_priority = ntohs(lacpdu->partner_system_priority);
- lacpdu->partner_key = ntohs(lacpdu->partner_key);
- lacpdu->partner_port_priority = ntohs(lacpdu->partner_port_priority);
- lacpdu->partner_port = ntohs(lacpdu->partner_port);
- lacpdu->collector_max_delay = ntohs(lacpdu->collector_max_delay);
+ lacpdu->actor_system_priority = htons(lacpdu->actor_system_priority);
+ lacpdu->actor_key = htons(lacpdu->actor_key);
+ lacpdu->actor_port_priority = htons(lacpdu->actor_port_priority);
+ lacpdu->actor_port = htons(lacpdu->actor_port);
+ lacpdu->partner_system_priority = htons(lacpdu->partner_system_priority);
+ lacpdu->partner_key = htons(lacpdu->partner_key);
+ lacpdu->partner_port_priority = htons(lacpdu->partner_port_priority);
+ lacpdu->partner_port = htons(lacpdu->partner_port);
+ lacpdu->collector_max_delay = htons(lacpdu->collector_max_delay);
}
}
@@ -490,11 +496,11 @@ static void __record_pdu(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// record the new parameter values for the partner operational
- port->partner_oper_port_number = lacpdu->actor_port;
- port->partner_oper_port_priority = lacpdu->actor_port_priority;
+ port->partner_oper_port_number = ntohs(lacpdu->actor_port);
+ port->partner_oper_port_priority = ntohs(lacpdu->actor_port_priority);
port->partner_oper_system = lacpdu->actor_system;
- port->partner_oper_system_priority = lacpdu->actor_system_priority;
- port->partner_oper_key = lacpdu->actor_key;
+ port->partner_oper_system_priority = ntohs(lacpdu->actor_system_priority);
+ port->partner_oper_key = ntohs(lacpdu->actor_key);
// zero partener's lase states
port->partner_oper_port_state = 0;
port->partner_oper_port_state |= (lacpdu->actor_state & AD_STATE_LACP_ACTIVITY);
@@ -561,11 +567,11 @@ static void __update_selected(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if any parameter is different
- if ((lacpdu->actor_port != port->partner_oper_port_number) ||
- (lacpdu->actor_port_priority != port->partner_oper_port_priority) ||
+ if ((ntohs(lacpdu->actor_port) != port->partner_oper_port_number) ||
+ (ntohs(lacpdu->actor_port_priority) != port->partner_oper_port_priority) ||
MAC_ADDRESS_COMPARE(&(lacpdu->actor_system), &(port->partner_oper_system)) ||
- (lacpdu->actor_system_priority != port->partner_oper_system_priority) ||
- (lacpdu->actor_key != port->partner_oper_key) ||
+ (ntohs(lacpdu->actor_system_priority) != port->partner_oper_system_priority) ||
+ (ntohs(lacpdu->actor_key) != port->partner_oper_key) ||
((lacpdu->actor_state & AD_STATE_AGGREGATION) != (port->partner_oper_port_state & AD_STATE_AGGREGATION))
) {
// update the state machine Selected variable
@@ -628,11 +634,11 @@ static void __choose_matched(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if all parameters are alike
- if (((lacpdu->partner_port == port->actor_port_number) &&
- (lacpdu->partner_port_priority == port->actor_port_priority) &&
+ if (((ntohs(lacpdu->partner_port) == port->actor_port_number) &&
+ (ntohs(lacpdu->partner_port_priority) == port->actor_port_priority) &&
!MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) &&
- (lacpdu->partner_system_priority == port->actor_system_priority) &&
- (lacpdu->partner_key == port->actor_oper_port_key) &&
+ (ntohs(lacpdu->partner_system_priority) == port->actor_system_priority) &&
+ (ntohs(lacpdu->partner_key) == port->actor_oper_port_key) &&
((lacpdu->partner_state & AD_STATE_AGGREGATION) == (port->actor_oper_port_state & AD_STATE_AGGREGATION))) ||
// or this is individual link(aggregation == FALSE)
((lacpdu->actor_state & AD_STATE_AGGREGATION) == 0)
@@ -662,11 +668,11 @@ static void __update_ntt(struct lacpdu *lacpdu, struct port *port)
// validate lacpdu and port
if (lacpdu && port) {
// check if any parameter is different
- if ((lacpdu->partner_port != port->actor_port_number) ||
- (lacpdu->partner_port_priority != port->actor_port_priority) ||
+ if ((ntohs(lacpdu->partner_port) != port->actor_port_number) ||
+ (ntohs(lacpdu->partner_port_priority) != port->actor_port_priority) ||
MAC_ADDRESS_COMPARE(&(lacpdu->partner_system), &(port->actor_system)) ||
- (lacpdu->partner_system_priority != port->actor_system_priority) ||
- (lacpdu->partner_key != port->actor_oper_port_key) ||
+ (ntohs(lacpdu->partner_system_priority) != port->actor_system_priority) ||
+ (ntohs(lacpdu->partner_key) != port->actor_oper_port_key) ||
((lacpdu->partner_state & AD_STATE_LACP_ACTIVITY) != (port->actor_oper_port_state & AD_STATE_LACP_ACTIVITY)) ||
((lacpdu->partner_state & AD_STATE_LACP_TIMEOUT) != (port->actor_oper_port_state & AD_STATE_LACP_TIMEOUT)) ||
((lacpdu->partner_state & AD_STATE_SYNCHRONIZATION) != (port->actor_oper_port_state & AD_STATE_SYNCHRONIZATION)) ||
@@ -775,6 +781,9 @@ static u32 __get_agg_bandwidth(struct aggregator *aggregator)
case AD_LINK_SPEED_BITMASK_1000MBPS:
bandwidth = aggregator->num_of_ports * 1000;
break;
+ case AD_LINK_SPEED_BITMASK_10000MBPS:
+ bandwidth = aggregator->num_of_ports * 10000;
+ break;
default:
bandwidth=0; // to silent the compilor ....
}
@@ -847,7 +856,7 @@ static inline void __update_lacpdu_from_port(struct port *port)
*/
/* Convert all non u8 parameters to Big Endian for transmit */
- __ntohs_lacpdu(lacpdu);
+ __htons_lacpdu(lacpdu);
}
//////////////////////////////////////////////////////////////////////////////////////
@@ -2171,7 +2180,6 @@ static void bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u
switch (lacpdu->subtype) {
case AD_TYPE_LACPDU:
- __ntohs_lacpdu(lacpdu);
dprintk("Received LACPDU on port %d\n", port->actor_port_number);
ad_rx_machine(lacpdu, port);
break;
diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c
index 850aae21a2f..c0bbddae4ec 100644
--- a/drivers/net/bonding/bond_main.c
+++ b/drivers/net/bonding/bond_main.c
@@ -96,6 +96,7 @@ static char *lacp_rate = NULL;
static char *xmit_hash_policy = NULL;
static int arp_interval = BOND_LINK_ARP_INTERV;
static char *arp_ip_target[BOND_MAX_ARP_TARGETS] = { NULL, };
+static char *arp_validate = NULL;
struct bond_params bonding_defaults;
module_param(max_bonds, int, 0);
@@ -127,6 +128,8 @@ module_param(arp_interval, int, 0);
MODULE_PARM_DESC(arp_interval, "arp interval in milliseconds");
module_param_array(arp_ip_target, charp, NULL, 0);
MODULE_PARM_DESC(arp_ip_target, "arp targets in n.n.n.n form");
+module_param(arp_validate, charp, 0);
+MODULE_PARM_DESC(arp_validate, "validate src/dst of ARP probes: none (default), active, backup or all");
/*----------------------------- Global variables ----------------------------*/
@@ -170,6 +173,14 @@ struct bond_parm_tbl xmit_hashtype_tbl[] = {
{ NULL, -1},
};
+struct bond_parm_tbl arp_validate_tbl[] = {
+{ "none", BOND_ARP_VALIDATE_NONE},
+{ "active", BOND_ARP_VALIDATE_ACTIVE},
+{ "backup", BOND_ARP_VALIDATE_BACKUP},
+{ "all", BOND_ARP_VALIDATE_ALL},
+{ NULL, -1},
+};
+
/*-------------------------- Forward declarations ---------------------------*/
static void bond_send_gratuitous_arp(struct bonding *bond);
@@ -638,6 +649,7 @@ verify:
case SPEED_10:
case SPEED_100:
case SPEED_1000:
+ case SPEED_10000:
break;
default:
return -1;
@@ -1210,10 +1222,14 @@ static int bond_compute_features(struct bonding *bond)
unsigned long features = BOND_INTERSECT_FEATURES;
struct slave *slave;
struct net_device *bond_dev = bond->dev;
+ unsigned short max_hard_header_len = ETH_HLEN;
int i;
- bond_for_each_slave(bond, slave, i)
+ bond_for_each_slave(bond, slave, i) {
features &= (slave->dev->features & BOND_INTERSECT_FEATURES);
+ if (slave->dev->hard_header_len > max_hard_header_len)
+ max_hard_header_len = slave->dev->hard_header_len;
+ }
if ((features & NETIF_F_SG) &&
!(features & NETIF_F_ALL_CSUM))
@@ -1231,6 +1247,7 @@ static int bond_compute_features(struct bonding *bond)
features |= (bond_dev->features & ~BOND_INTERSECT_FEATURES);
bond_dev->features = features;
+ bond_dev->hard_header_len = max_hard_header_len;
return 0;
}
@@ -1365,6 +1382,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
}
new_slave->dev = slave_dev;
+ slave_dev->priv_flags |= IFF_BONDING;
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
@@ -1417,6 +1435,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
bond_compute_features(bond);
+ new_slave->last_arp_rx = jiffies;
+
if (bond->params.miimon && !bond->params.use_carrier) {
link_reporting = bond_check_dev_link(bond, slave_dev, 1);
@@ -1493,29 +1513,8 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
switch (bond->params.mode) {
case BOND_MODE_ACTIVEBACKUP:
- /* if we're in active-backup mode, we need one and
- * only one active interface. The backup interfaces
- * will have their SLAVE_INACTIVE flag set because we
- * need them to be drop all packets. Thus, since we
- * guarantee that curr_active_slave always point to
- * the last usable interface, we just have to verify
- * this interface's flag.
- */
- if (((!bond->curr_active_slave) ||
- (bond->curr_active_slave->dev->priv_flags & IFF_SLAVE_INACTIVE)) &&
- (new_slave->link != BOND_LINK_DOWN)) {
- /* first slave or no active slave yet, and this link
- is OK, so make this interface the active one */
- bond_change_active_slave(bond, new_slave);
- printk(KERN_INFO DRV_NAME
- ": %s: first active interface up!\n",
- bond->dev->name);
- netif_carrier_on(bond->dev);
-
- } else {
- dprintk("This is just a backup slave\n");
- bond_set_slave_inactive_flags(new_slave);
- }
+ bond_set_slave_inactive_flags(new_slave);
+ bond_select_active_slave(bond);
break;
case BOND_MODE_8023AD:
/* in 802.3ad mode, the internal mechanism
@@ -1778,7 +1777,8 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
dev_set_mac_address(slave_dev, &addr);
slave_dev->priv_flags &= ~(IFF_MASTER_8023AD | IFF_MASTER_ALB |
- IFF_SLAVE_INACTIVE);
+ IFF_SLAVE_INACTIVE | IFF_BONDING |
+ IFF_SLAVE_NEEDARP);
kfree(slave);
@@ -2252,7 +2252,7 @@ static u32 bond_glean_dev_ip(struct net_device *dev)
{
struct in_device *idev;
struct in_ifaddr *ifa;
- u32 addr = 0;
+ __be32 addr = 0;
if (!dev)
return 0;
@@ -2291,6 +2291,25 @@ static int bond_has_ip(struct bonding *bond)
return 0;
}
+static int bond_has_this_ip(struct bonding *bond, u32 ip)
+{
+ struct vlan_entry *vlan, *vlan_next;
+
+ if (ip == bond->master_ip)
+ return 1;
+
+ if (list_empty(&bond->vlan_list))
+ return 0;
+
+ list_for_each_entry_safe(vlan, vlan_next, &bond->vlan_list,
+ vlan_list) {
+ if (ip == vlan->vlan_ip)
+ return 1;
+ }
+
+ return 0;
+}
+
/*
* We go to the (large) trouble of VLAN tagging ARP frames because
* switches in VLAN mode (especially if ports are configured as
@@ -2429,6 +2448,93 @@ static void bond_send_gratuitous_arp(struct bonding *bond)
}
}
+static void bond_validate_arp(struct bonding *bond, struct slave *slave, u32 sip, u32 tip)
+{
+ int i;
+ u32 *targets = bond->params.arp_targets;
+
+ targets = bond->params.arp_targets;
+ for (i = 0; (i < BOND_MAX_ARP_TARGETS) && targets[i]; i++) {
+ dprintk("bva: sip %u.%u.%u.%u tip %u.%u.%u.%u t[%d] "
+ "%u.%u.%u.%u bhti(tip) %d\n",
+ NIPQUAD(sip), NIPQUAD(tip), i, NIPQUAD(targets[i]),
+ bond_has_this_ip(bond, tip));
+ if (sip == targets[i]) {
+ if (bond_has_this_ip(bond, tip))
+ slave->last_arp_rx = jiffies;
+ return;
+ }
+ }
+}
+
+static int bond_arp_rcv(struct sk_buff *skb, struct net_device *dev, struct packet_type *pt, struct net_device *orig_dev)
+{
+ struct arphdr *arp;
+ struct slave *slave;
+ struct bonding *bond;
+ unsigned char *arp_ptr;
+ u32 sip, tip;
+
+ if (!(dev->priv_flags & IFF_BONDING) || !(dev->flags & IFF_MASTER))
+ goto out;
+
+ bond = dev->priv;
+ read_lock(&bond->lock);
+
+ dprintk("bond_arp_rcv: bond %s skb->dev %s orig_dev %s\n",
+ bond->dev->name, skb->dev ? skb->dev->name : "NULL",
+ orig_dev ? orig_dev->name : "NULL");
+
+ slave = bond_get_slave_by_dev(bond, orig_dev);
+ if (!slave || !slave_do_arp_validate(bond, slave))
+ goto out_unlock;
+
+ /* ARP header, plus 2 device addresses, plus 2 IP addresses. */
+ if (!pskb_may_pull(skb, (sizeof(struct arphdr) +
+ (2 * dev->addr_len) +
+ (2 * sizeof(u32)))))
+ goto out_unlock;
+
+ arp = skb->nh.arph;
+ if (arp->ar_hln != dev->addr_len ||
+ skb->pkt_type == PACKET_OTHERHOST ||
+ skb->pkt_type == PACKET_LOOPBACK ||
+ arp->ar_hrd != htons(ARPHRD_ETHER) ||
+ arp->ar_pro != htons(ETH_P_IP) ||
+ arp->ar_pln != 4)
+ goto out_unlock;
+
+ arp_ptr = (unsigned char *)(arp + 1);
+ arp_ptr += dev->addr_len;
+ memcpy(&sip, arp_ptr, 4);
+ arp_ptr += 4 + dev->addr_len;
+ memcpy(&tip, arp_ptr, 4);
+
+ dprintk("bond_arp_rcv: %s %s/%d av %d sv %d sip %u.%u.%u.%u"
+ " tip %u.%u.%u.%u\n", bond->dev->name, slave->dev->name,
+ slave->state, bond->params.arp_validate,
+ slave_do_arp_validate(bond, slave), NIPQUAD(sip), NIPQUAD(tip));
+
+ /*
+ * Backup slaves won't see the ARP reply, but do come through
+ * here for each ARP probe (so we swap the sip/tip to validate
+ * the probe). In a "redundant switch, common router" type of
+ * configuration, the ARP probe will (hopefully) travel from
+ * the active, through one switch, the router, then the other
+ * switch before reaching the backup.
+ */
+ if (slave->state == BOND_STATE_ACTIVE)
+ bond_validate_arp(bond, slave, sip, tip);
+ else
+ bond_validate_arp(bond, slave, tip, sip);
+
+out_unlock:
+ read_unlock(&bond->lock);
+out:
+ dev_kfree_skb(skb);
+ return NET_RX_SUCCESS;
+}
+
/*
* this function is called regularly to monitor each slave's link
* ensuring that traffic is being sent and received when arp monitoring
@@ -2593,7 +2699,8 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
*/
bond_for_each_slave(bond, slave, i) {
if (slave->link != BOND_LINK_UP) {
- if ((jiffies - slave->dev->last_rx) <= delta_in_ticks) {
+ if ((jiffies - slave_last_rx(bond, slave)) <=
+ delta_in_ticks) {
slave->link = BOND_LINK_UP;
@@ -2638,7 +2745,7 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
if ((slave != bond->curr_active_slave) &&
(!bond->current_arp_slave) &&
- (((jiffies - slave->dev->last_rx) >= 3*delta_in_ticks) &&
+ (((jiffies - slave_last_rx(bond, slave)) >= 3*delta_in_ticks) &&
bond_has_ip(bond))) {
/* a backup slave has gone down; three times
* the delta allows the current slave to be
@@ -2685,7 +2792,7 @@ void bond_activebackup_arp_mon(struct net_device *bond_dev)
* if it is up and needs to take over as the curr_active_slave
*/
if ((((jiffies - slave->dev->trans_start) >= (2*delta_in_ticks)) ||
- (((jiffies - slave->dev->last_rx) >= (2*delta_in_ticks)) &&
+ (((jiffies - slave_last_rx(bond, slave)) >= (2*delta_in_ticks)) &&
bond_has_ip(bond))) &&
((jiffies - slave->jiffies) >= 2*delta_in_ticks)) {
@@ -2950,7 +3057,7 @@ static void bond_info_show_slave(struct seq_file *seq, const struct slave *slave
seq_printf(seq, "\nSlave Interface: %s\n", slave->dev->name);
seq_printf(seq, "MII Status: %s\n",
(slave->link == BOND_LINK_UP) ? "up" : "down");
- seq_printf(seq, "Link Failure Count: %d\n",
+ seq_printf(seq, "Link Failure Count: %u\n",
slave->link_failure_count);
seq_printf(seq,
@@ -3210,6 +3317,9 @@ static int bond_netdev_event(struct notifier_block *this, unsigned long event, v
(event_dev ? event_dev->name : "None"),
event);
+ if (!(event_dev->priv_flags & IFF_BONDING))
+ return NOTIFY_DONE;
+
if (event_dev->flags & IFF_MASTER) {
dprintk("IFF_MASTER\n");
return bond_master_netdev_event(event, event_dev);
@@ -3305,6 +3415,21 @@ static void bond_unregister_lacpdu(struct bonding *bond)
dev_remove_pack(&(BOND_AD_INFO(bond).ad_pkt_type));
}
+void bond_register_arp(struct bonding *bond)
+{
+ struct packet_type *pt = &bond->arp_mon_pt;
+
+ pt->type = htons(ETH_P_ARP);
+ pt->dev = NULL; /*bond->dev;XXX*/
+ pt->func = bond_arp_rcv;
+ dev_add_pack(pt);
+}
+
+void bond_unregister_arp(struct bonding *bond)
+{
+ dev_remove_pack(&bond->arp_mon_pt);
+}
+
/*---------------------------- Hashing Policies -----------------------------*/
/*
@@ -3391,6 +3516,9 @@ static int bond_open(struct net_device *bond_dev)
} else {
arp_timer->function = (void *)&bond_loadbalance_arp_mon;
}
+ if (bond->params.arp_validate)
+ bond_register_arp(bond);
+
add_timer(arp_timer);
}
@@ -3418,9 +3546,11 @@ static int bond_close(struct net_device *bond_dev)
bond_unregister_lacpdu(bond);
}
+ if (bond->params.arp_validate)
+ bond_unregister_arp(bond);
+
write_lock_bh(&bond->lock);
- bond_mc_list_destroy(bond);
/* signal timers not to re-arm */
bond->kill_timers = 1;
@@ -3451,8 +3581,6 @@ static int bond_close(struct net_device *bond_dev)
break;
}
- /* Release the bonded slaves */
- bond_release_all(bond_dev);
if ((bond->params.mode == BOND_MODE_TLB) ||
(bond->params.mode == BOND_MODE_ALB)) {
@@ -4179,6 +4307,7 @@ static int bond_init(struct net_device *bond_dev, struct bond_params *params)
/* Initialize the device options */
bond_dev->tx_queue_len = 0;
bond_dev->flags |= IFF_MASTER|IFF_MULTICAST;
+ bond_dev->priv_flags |= IFF_BONDING;
/* At first, we block adding VLANs. That's the only way to
* prevent problems that occur when adding VLANs over an
@@ -4237,6 +4366,9 @@ static void bond_free_all(void)
list_for_each_entry_safe(bond, nxt, &bond_dev_list, bond_list) {
struct net_device *bond_dev = bond->dev;
+ bond_mc_list_destroy(bond);
+ /* Release the bonded slaves */
+ bond_release_all(bond_dev);
unregister_netdevice(bond_dev);
bond_deinit(bond_dev);
}
@@ -4270,6 +4402,8 @@ int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl)
static int bond_check_params(struct bond_params *params)
{
+ int arp_validate_value;
+
/*
* Convert string parameters.
*/
@@ -4473,6 +4607,29 @@ static int bond_check_params(struct bond_params *params)
arp_interval = 0;
}
+ if (arp_validate) {
+ if (bond_mode != BOND_MODE_ACTIVEBACKUP) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate only supported in active-backup mode\n");
+ return -EINVAL;
+ }
+ if (!arp_interval) {
+ printk(KERN_ERR DRV_NAME
+ ": arp_validate requires arp_interval\n");
+ return -EINVAL;
+ }
+
+ arp_validate_value = bond_parse_parm(arp_validate,
+ arp_validate_tbl);
+ if (arp_validate_value == -1) {
+ printk(KERN_ERR DRV_NAME
+ ": Error: invalid arp_validate \"%s\"\n",
+ arp_validate == NULL ? "NULL" : arp_validate);
+ return -EINVAL;
+ }
+ } else
+ arp_validate_value = 0;
+
if (miimon) {
printk(KERN_INFO DRV_NAME
": MII link monitoring set to %d ms\n",
@@ -4481,8 +4638,10 @@ static int bond_check_params(struct bond_params *params)
int i;
printk(KERN_INFO DRV_NAME
- ": ARP monitoring set to %d ms with %d target(s):",
- arp_interval, arp_ip_count);
+ ": ARP monitoring set to %d ms, validate %s, with %d target(s):",
+ arp_interval,
+ arp_validate_tbl[arp_validate_value].modename,
+ arp_ip_count);
for (i = 0; i < arp_ip_count; i++)
printk (" %s", arp_ip_target[i]);
@@ -4516,6 +4675,7 @@ static int bond_check_params(struct bond_params *params)
params->xmit_policy = xmit_hashtype;
params->miimon = miimon;
params->arp_interval = arp_interval;
+ params->arp_validate = arp_validate_value;
params->updelay = updelay;
params->downdelay = downdelay;
params->use_carrier = use_carrier;
diff --git a/drivers/net/bonding/bond_sysfs.c b/drivers/net/bonding/bond_sysfs.c
index cfe4dc3a93a..ced9ed8f995 100644
--- a/drivers/net/bonding/bond_sysfs.c
+++ b/drivers/net/bonding/bond_sysfs.c
@@ -51,6 +51,7 @@ extern struct bond_params bonding_defaults;
extern struct bond_parm_tbl bond_mode_tbl[];
extern struct bond_parm_tbl bond_lacp_tbl[];
extern struct bond_parm_tbl xmit_hashtype_tbl[];
+extern struct bond_parm_tbl arp_validate_tbl[];
static int expected_refcount = -1;
static struct class *netdev_class;
@@ -503,6 +504,53 @@ out:
static CLASS_DEVICE_ATTR(xmit_hash_policy, S_IRUGO | S_IWUSR, bonding_show_xmit_hash, bonding_store_xmit_hash);
/*
+ * Show and set arp_validate.
+ */
+static ssize_t bonding_show_arp_validate(struct class_device *cd, char *buf)
+{
+ struct bonding *bond = to_bond(cd);
+
+ return sprintf(buf, "%s %d\n",
+ arp_validate_tbl[bond->params.arp_validate].modename,
+ bond->params.arp_validate) + 1;
+}
+
+static ssize_t bonding_store_arp_validate(struct class_device *cd, const char *buf, size_t count)
+{
+ int new_value;
+ struct bonding *bond = to_bond(cd);
+
+ new_value = bond_parse_parm((char *)buf, arp_validate_tbl);
+ if (new_value < 0) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: Ignoring invalid arp_validate value %s\n",
+ bond->dev->name, buf);
+ return -EINVAL;
+ }
+ if (new_value && (bond->params.mode != BOND_MODE_ACTIVEBACKUP)) {
+ printk(KERN_ERR DRV_NAME
+ ": %s: arp_validate only supported in active-backup mode.\n",
+ bond->dev->name);
+ return -EINVAL;
+ }
+ printk(KERN_INFO DRV_NAME ": %s: setting arp_validate to %s (%d).\n",
+ bond->dev->name, arp_validate_tbl[new_value].modename,
+ new_value);
+
+ if (!bond->params.arp_validate && new_value) {
+ bond_register_arp(bond);
+ } else if (bond->params.arp_validate && !new_value) {
+ bond_unregister_arp(bond);
+ }
+
+ bond->params.arp_validate = new_value;
+
+ return count;
+}
+
+static CLASS_DEVICE_ATTR(arp_validate, S_IRUGO | S_IWUSR, bonding_show_arp_validate, bonding_store_arp_validate);
+
+/*
* Show and set the arp timer interval. There are two tricky bits
* here. First, if ARP monitoring is activated, then we must disable
* MII monitoring. Second, if the ARP timer isn't running, we must
@@ -914,6 +962,11 @@ static ssize_t bonding_store_miimon(struct class_device *cd, const char *buf, si
"ARP monitoring. Disabling ARP monitoring...\n",
bond->dev->name);
bond->params.arp_interval = 0;
+ if (bond->params.arp_validate) {
+ bond_unregister_arp(bond);
+ bond->params.arp_validate =
+ BOND_ARP_VALIDATE_NONE;
+ }
/* Kill ARP timer, else it brings bond's link down */
if (bond->mii_timer.function) {
printk(KERN_INFO DRV_NAME
@@ -1093,7 +1146,7 @@ static ssize_t bonding_store_active_slave(struct class_device *cd, const char *b
strlen(slave->dev->name)) == 0) {
old_active = bond->curr_active_slave;
new_active = slave;
- if (new_active && (new_active == old_active)) {
+ if (new_active == old_active) {
/* do nothing */
printk(KERN_INFO DRV_NAME
": %s: %s is already the current active slave.\n",
@@ -1273,6 +1326,7 @@ static CLASS_DEVICE_ATTR(ad_partner_mac, S_IRUGO, bonding_show_ad_partner_mac, N
static struct attribute *per_bond_attrs[] = {
&class_device_attr_slaves.attr,
&class_device_attr_mode.attr,
+ &class_device_attr_arp_validate.attr,
&class_device_attr_arp_interval.attr,
&class_device_attr_arp_ip_target.attr,
&class_device_attr_downdelay.attr,
diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h
index 0bdfe2c7145..dc434fb6da8 100644
--- a/drivers/net/bonding/bonding.h
+++ b/drivers/net/bonding/bonding.h
@@ -22,8 +22,8 @@
#include "bond_3ad.h"
#include "bond_alb.h"
-#define DRV_VERSION "3.0.3"
-#define DRV_RELDATE "March 23, 2006"
+#define DRV_VERSION "3.1.1"
+#define DRV_RELDATE "September 26, 2006"
#define DRV_NAME "bonding"
#define DRV_DESCRIPTION "Ethernet Channel Bonding Driver"
@@ -126,6 +126,7 @@ struct bond_params {
int xmit_policy;
int miimon;
int arp_interval;
+ int arp_validate;
int use_carrier;
int updelay;
int downdelay;
@@ -149,8 +150,9 @@ struct slave {
struct net_device *dev; /* first - useful for panic debug */
struct slave *next;
struct slave *prev;
- s16 delay;
+ int delay;
u32 jiffies;
+ u32 last_arp_rx;
s8 link; /* one of BOND_LINK_XXXX */
s8 state; /* one of BOND_STATE_XXXX */
u32 original_flags;
@@ -198,6 +200,7 @@ struct bonding {
struct bond_params params;
struct list_head vlan_list;
struct vlan_group *vlgrp;
+ struct packet_type arp_mon_pt;
};
/**
@@ -228,6 +231,25 @@ static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
return (struct bonding *)slave->dev->master->priv;
}
+#define BOND_ARP_VALIDATE_NONE 0
+#define BOND_ARP_VALIDATE_ACTIVE (1 << BOND_STATE_ACTIVE)
+#define BOND_ARP_VALIDATE_BACKUP (1 << BOND_STATE_BACKUP)
+#define BOND_ARP_VALIDATE_ALL (BOND_ARP_VALIDATE_ACTIVE | \
+ BOND_ARP_VALIDATE_BACKUP)
+
+extern inline int slave_do_arp_validate(struct bonding *bond, struct slave *slave)
+{
+ return bond->params.arp_validate & (1 << slave->state);
+}
+
+extern inline u32 slave_last_rx(struct bonding *bond, struct slave *slave)
+{
+ if (slave_do_arp_validate(bond, slave))
+ return slave->last_arp_rx;
+
+ return slave->dev->last_rx;
+}
+
static inline void bond_set_slave_inactive_flags(struct slave *slave)
{
struct bonding *bond = slave->dev->master->priv;
@@ -235,12 +257,14 @@ static inline void bond_set_slave_inactive_flags(struct slave *slave)
bond->params.mode != BOND_MODE_ALB)
slave->state = BOND_STATE_BACKUP;
slave->dev->priv_flags |= IFF_SLAVE_INACTIVE;
+ if (slave_do_arp_validate(bond, slave))
+ slave->dev->priv_flags |= IFF_SLAVE_NEEDARP;
}
static inline void bond_set_slave_active_flags(struct slave *slave)
{
slave->state = BOND_STATE_ACTIVE;
- slave->dev->priv_flags &= ~IFF_SLAVE_INACTIVE;
+ slave->dev->priv_flags &= ~(IFF_SLAVE_INACTIVE | IFF_SLAVE_NEEDARP);
}
static inline void bond_set_master_3ad_flags(struct bonding *bond)
@@ -284,6 +308,8 @@ int bond_parse_parm(char *mode_arg, struct bond_parm_tbl *tbl);
const char *bond_mode_name(int mode);
void bond_select_active_slave(struct bonding *bond);
void bond_change_active_slave(struct bonding *bond, struct slave *new_active);
+void bond_register_arp(struct bonding *);
+void bond_unregister_arp(struct bonding *);
#endif /* _LINUX_BONDING_H */
diff --git a/drivers/net/dgrs.c b/drivers/net/dgrs.c
index e9361cb5f4c..d0842527b36 100644
--- a/drivers/net/dgrs.c
+++ b/drivers/net/dgrs.c
@@ -110,7 +110,6 @@ static char version[] __initdata =
* DGRS include files
*/
typedef unsigned char uchar;
-typedef unsigned int bool;
#define vol volatile
#include "dgrs.h"
diff --git a/drivers/net/e100.c b/drivers/net/e100.c
index dc5e38aefca..26073c34521 100644
--- a/drivers/net/e100.c
+++ b/drivers/net/e100.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
+ Intel PRO/100 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
- This program is distributed in the hope that it will be useful, but WITHOUT
+ This program is distributed in the hope 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 program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
@@ -159,7 +159,7 @@
#define DRV_NAME "e100"
#define DRV_EXT "-NAPI"
-#define DRV_VERSION "3.5.16-k2"DRV_EXT
+#define DRV_VERSION "3.5.17-k2"DRV_EXT
#define DRV_DESCRIPTION "Intel(R) PRO/100 Network Driver"
#define DRV_COPYRIGHT "Copyright(c) 1999-2006 Intel Corporation"
#define PFX DRV_NAME ": "
@@ -1657,13 +1657,14 @@ static int e100_tx_clean(struct nic *nic)
spin_lock(&nic->cb_lock);
- DPRINTK(TX_DONE, DEBUG, "cb->status = 0x%04X\n",
- nic->cb_to_clean->status);
-
/* Clean CBs marked complete */
for(cb = nic->cb_to_clean;
cb->status & cpu_to_le16(cb_complete);
cb = nic->cb_to_clean = cb->next) {
+ DPRINTK(TX_DONE, DEBUG, "cb[%d]->status = 0x%04X\n",
+ (int)(((void*)cb - (void*)nic->cbs)/sizeof(struct cb)),
+ cb->status);
+
if(likely(cb->skb != NULL)) {
nic->net_stats.tx_packets++;
nic->net_stats.tx_bytes += cb->skb->len;
@@ -2572,7 +2573,7 @@ static int __devinit e100_probe(struct pci_dev *pdev,
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = e100_netpoll;
#endif
- strcpy(netdev->name, pci_name(pdev));
+ strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
nic = netdev_priv(netdev);
nic->netdev = netdev;
@@ -2714,68 +2715,56 @@ static void __devexit e100_remove(struct pci_dev *pdev)
}
}
-#ifdef CONFIG_PM
static int e100_suspend(struct pci_dev *pdev, pm_message_t state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct nic *nic = netdev_priv(netdev);
- int retval;
- if(netif_running(netdev))
+ if (netif_running(netdev))
e100_down(nic);
e100_hw_reset(nic);
netif_device_detach(netdev);
+#ifdef CONFIG_PM
pci_save_state(pdev);
- retval = pci_enable_wake(pdev, pci_choose_state(pdev, state),
- nic->flags & (wol_magic | e100_asf(nic)));
- if (retval)
- DPRINTK(PROBE,ERR, "Error enabling wake\n");
+ if (nic->flags & (wol_magic | e100_asf(nic)))
+#else
+ if (nic->flags & (wol_magic))
+#endif
+ pci_enable_wake(pdev, pci_choose_state(pdev, state), 1);
+ else
+ /* disable PME */
+ pci_enable_wake(pdev, 0, 0);
+
pci_disable_device(pdev);
- retval = pci_set_power_state(pdev, pci_choose_state(pdev, state));
- if (retval)
- DPRINTK(PROBE,ERR, "Error %d setting power state\n", retval);
+ pci_set_power_state(pdev, pci_choose_state(pdev, state));
return 0;
}
+#ifdef CONFIG_PM
static int e100_resume(struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
struct nic *nic = netdev_priv(netdev);
- int retval;
- retval = pci_set_power_state(pdev, PCI_D0);
- if (retval)
- DPRINTK(PROBE,ERR, "Error waking adapter\n");
+ pci_set_power_state(pdev, PCI_D0);
pci_restore_state(pdev);
/* ack any pending wake events, disable PME */
- retval = pci_enable_wake(pdev, 0, 0);
- if (retval)
- DPRINTK(PROBE,ERR, "Error clearing wake events\n");
+ pci_enable_wake(pdev, 0, 0);
netif_device_attach(netdev);
- if(netif_running(netdev))
+ if (netif_running(netdev))
e100_up(nic);
return 0;
}
-#endif
+#endif /* CONFIG_PM */
static void e100_shutdown(struct pci_dev *pdev)
{
- struct net_device *netdev = pci_get_drvdata(pdev);
- struct nic *nic = netdev_priv(netdev);
- int retval;
-
-#ifdef CONFIG_PM
- retval = pci_enable_wake(pdev, 0, nic->flags & (wol_magic | e100_asf(nic)));
-#else
- retval = pci_enable_wake(pdev, 0, nic->flags & (wol_magic));
-#endif
- if (retval)
- DPRINTK(PROBE,ERR, "Error enabling wake\n");
+ e100_suspend(pdev, PMSG_SUSPEND);
}
/* ------------------ PCI Error Recovery infrastructure -------------- */
@@ -2859,8 +2848,9 @@ static struct pci_driver e100_driver = {
.id_table = e100_id_table,
.probe = e100_probe,
.remove = __devexit_p(e100_remove),
-#ifdef CONFIG_PM
+ /* Power Management hooks */
.suspend = e100_suspend,
+#ifdef CONFIG_PM
.resume = e100_resume,
#endif
.shutdown = e100_shutdown,
diff --git a/drivers/net/e1000/LICENSE b/drivers/net/e1000/LICENSE
deleted file mode 100644
index 5f297e5bb46..00000000000
--- a/drivers/net/e1000/LICENSE
+++ /dev/null
@@ -1,339 +0,0 @@
-
-"This software program is licensed subject to the GNU General Public License
-(GPL). Version 2, June 1991, available at
-<http://www.fsf.org/copyleft/gpl.html>"
-
-GNU General Public License
-
-Version 2, June 1991
-
-Copyright (C) 1989, 1991 Free Software Foundation, Inc.
-59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
-
-Everyone is permitted to copy and distribute verbatim copies of this license
-document, but changing it is not allowed.
-
-Preamble
-
-The licenses for most software are designed to take away your freedom to
-share and change it. By contrast, the GNU General Public License is intended
-to guarantee your freedom to share and change free software--to make sure
-the software is free for all its users. This General Public License applies
-to most of the Free Software Foundation's software and to any other program
-whose authors commit to using it. (Some other Free Software Foundation
-software is covered by the GNU Library General Public License instead.) You
-can apply it to your programs, too.
-
-When we speak of free software, we are referring to freedom, not price. Our
-General Public Licenses are designed to make sure that you have the freedom
-to distribute copies of free software (and charge for this service if you
-wish), that you receive source code or can get it if you want it, that you
-can change the software or use pieces of it in new free programs; and that
-you know you can do these things.
-
-To protect your rights, we need to make restrictions that forbid anyone to
-deny you these rights or to ask you to surrender the rights. These
-restrictions translate to certain responsibilities for you if you distribute
-copies of the software, or if you modify it.
-
-For example, if you distribute copies of such a program, whether gratis or
-for a fee, you must give the recipients all the rights that you have. You
-must make sure that they, too, receive or can get the source code. And you
-must show them these terms so they know their rights.
-
-We protect your rights with two steps: (1) copyright the software, and (2)
-offer you this license which gives you legal permission to copy, distribute
-and/or modify the software.
-
-Also, for each author's protection and ours, we want to make certain that
-everyone understands that there is no warranty for this free software. If
-the software is modified by someone else and passed on, we want its
-recipients to know that what they have is not the original, so that any
-problems introduced by others will not reflect on the original authors'
-reputations.
-
-Finally, any free program is threatened constantly by software patents. We
-wish to avoid the danger that redistributors of a free program will
-individually obtain patent licenses, in effect making the program
-proprietary. To prevent this, we have made it clear that any patent must be
-licensed for everyone's free use or not licensed at all.
-
-The precise terms and conditions for copying, distribution and modification
-follow.
-
-TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
-0. This License applies to any program or other work which contains a notice
- placed by the copyright holder saying it may be distributed under the
- terms of this General Public License. The "Program", below, refers to any
- such program or work, and a "work based on the Program" means either the
- Program or any derivative work under copyright law: that is to say, a
- work containing the Program or a portion of it, either verbatim or with
- modifications and/or translated into another language. (Hereinafter,
- translation is included without limitation in the term "modification".)
- Each licensee is addressed as "you".
-
- Activities other than copying, distribution and modification are not
- covered by this License; they are outside its scope. The act of running
- the Program is not restricted, and the output from the Program is covered
- only if its contents constitute a work based on the Program (independent
- of having been made by running the Program). Whether that is true depends
- on what the Program does.
-
-1. You may copy and distribute verbatim copies of the Program's source code
- as you receive it, in any medium, provided that you conspicuously and
- appropriately publish on each copy an appropriate copyright notice and
- disclaimer of warranty; keep intact all the notices that refer to this
- License and to the absence of any warranty; and give any other recipients
- of the Program a copy of this License along with the Program.
-
- You may charge a fee for the physical act of transferring a copy, and you
- may at your option offer warranty protection in exchange for a fee.
-
-2. You may modify your copy or copies of the Program or any portion of it,
- thus forming a work based on the Program, and copy and distribute such
- modifications or work under the terms of Section 1 above, provided that
- you also meet all of these conditions:
-
- * a) You must cause the modified files to carry prominent notices stating
- that you changed the files and the date of any change.
-
- * b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any part
- thereof, to be licensed as a whole at no charge to all third parties
- under the terms of this License.
-
- * c) If the modified program normally reads commands interactively when
- run, you must cause it, when started running for such interactive
- use in the most ordinary way, to print or display an announcement
- including an appropriate copyright notice and a notice that there is
- no warranty (or else, saying that you provide a warranty) and that
- users may redistribute the program under these conditions, and
- telling the user how to view a copy of this License. (Exception: if
- the Program itself is interactive but does not normally print such
- an announcement, your work based on the Program is not required to
- print an announcement.)
-
- These requirements apply to the modified work as a whole. If identifiable
- sections of that work are not derived from the Program, and can be
- reasonably considered independent and separate works in themselves, then
- this License, and its terms, do not apply to those sections when you
- distribute them as separate works. But when you distribute the same
- sections as part of a whole which is a work based on the Program, the
- distribution of the whole must be on the terms of this License, whose
- permissions for other licensees extend to the entire whole, and thus to
- each and every part regardless of who wrote it.
-
- Thus, it is not the intent of this section to claim rights or contest
- your rights to work written entirely by you; rather, the intent is to
- exercise the right to control the distribution of derivative or
- collective works based on the Program.
-
- In addition, mere aggregation of another work not based on the Program
- with the Program (or with a work based on the Program) on a volume of a
- storage or distribution medium does not bring the other work under the
- scope of this License.
-
-3. You may copy and distribute the Program (or a work based on it, under
- Section 2) in object code or executable form under the terms of Sections
- 1 and 2 above provided that you also do one of the following:
-
- * a) Accompany it with the complete corresponding machine-readable source
- code, which must be distributed under the terms of Sections 1 and 2
- above on a medium customarily used for software interchange; or,
-
- * b) Accompany it with a written offer, valid for at least three years,
- to give any third party, for a charge no more than your cost of
- physically performing source distribution, a complete machine-
- readable copy of the corresponding source code, to be distributed
- under the terms of Sections 1 and 2 above on a medium customarily
- used for software interchange; or,
-
- * c) Accompany it with the information you received as to the offer to
- distribute corresponding source code. (This alternative is allowed
- only for noncommercial distribution and only if you received the
- program in object code or executable form with such an offer, in
- accord with Subsection b above.)
-
- The source code for a work means the preferred form of the work for
- making modifications to it. For an executable work, complete source code
- means all the source code for all modules it contains, plus any
- associated interface definition files, plus the scripts used to control
- compilation and installation of the executable. However, as a special
- exception, the source code distributed need not include anything that is
- normally distributed (in either source or binary form) with the major
- components (compiler, kernel, and so on) of the operating system on which
- the executable runs, unless that component itself accompanies the
- executable.
-
- If distribution of executable or object code is made by offering access
- to copy from a designated place, then offering equivalent access to copy
- the source code from the same place counts as distribution of the source
- code, even though third parties are not compelled to copy the source
- along with the object code.
-
-4. You may not copy, modify, sublicense, or distribute the Program except as
- expressly provided under this License. Any attempt otherwise to copy,
- modify, sublicense or distribute the Program is void, and will
- automatically terminate your rights under this License. However, parties
- who have received copies, or rights, from you under this License will not
- have their licenses terminated so long as such parties remain in full
- compliance.
-
-5. You are not required to accept this License, since you have not signed
- it. However, nothing else grants you permission to modify or distribute
- the Program or its derivative works. These actions are prohibited by law
- if you do not accept this License. Therefore, by modifying or
- distributing the Program (or any work based on the Program), you
- indicate your acceptance of this License to do so, and all its terms and
- conditions for copying, distributing or modifying the Program or works
- based on it.
-
-6. Each time you redistribute the Program (or any work based on the
- Program), the recipient automatically receives a license from the
- original licensor to copy, distribute or modify the Program subject to
- these terms and conditions. You may not impose any further restrictions
- on the recipients' exercise of the rights granted herein. You are not
- responsible for enforcing compliance by third parties to this License.
-
-7. If, as a consequence of a court judgment or allegation of patent
- infringement or for any other reason (not limited to patent issues),
- conditions are imposed on you (whether by court order, agreement or
- otherwise) that contradict the conditions of this License, they do not
- excuse you from the conditions of this License. If you cannot distribute
- so as to satisfy simultaneously your obligations under this License and
- any other pertinent obligations, then as a consequence you may not
- distribute the Program at all. For example, if a patent license would
- not permit royalty-free redistribution of the Program by all those who
- receive copies directly or indirectly through you, then the only way you
- could satisfy both it and this License would be to refrain entirely from
- distribution of the Program.
-
- If any portion of this section is held invalid or unenforceable under any
- particular circumstance, the balance of the section is intended to apply
- and the section as a whole is intended to apply in other circumstances.
-
- It is not the purpose of this section to induce you to infringe any
- patents or other property right claims or to contest validity of any
- such claims; this section has the sole purpose of protecting the
- integrity of the free software distribution system, which is implemented
- by public license practices. Many people have made generous contributions
- to the wide range of software distributed through that system in
- reliance on consistent application of that system; it is up to the
- author/donor to decide if he or she is willing to distribute software
- through any other system and a licensee cannot impose that choice.
-
- This section is intended to make thoroughly clear what is believed to be
- a consequence of the rest of this License.
-
-8. If the distribution and/or use of the Program is restricted in certain
- countries either by patents or by copyrighted interfaces, the original
- copyright holder who places the Program under this License may add an
- explicit geographical distribution limitation excluding those countries,
- so that distribution is permitted only in or among countries not thus
- excluded. In such case, this License incorporates the limitation as if
- written in the body of this License.
-
-9. The Free Software Foundation may publish revised and/or new versions of
- the General Public License from time to time. Such new versions will be
- similar in spirit to the present version, but may differ in detail to
- address new problems or concerns.
-
- Each version is given a distinguishing version number. If the Program
- specifies a version number of this License which applies to it and "any
- later version", you have the option of following the terms and
- conditions either of that version or of any later version published by
- the Free Software Foundation. If the Program does not specify a version
- number of this License, you may choose any version ever published by the
- Free Software Foundation.
-
-10. If you wish to incorporate parts of the Program into other free programs
- whose distribution conditions are different, write to the author to ask
- for permission. For software which is copyrighted by the Free Software
- Foundation, write to the Free Software Foundation; we sometimes make
- exceptions for this. Our decision will be guided by the two goals of
- preserving the free status of all derivatives of our free software and
- of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
-11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
- FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
- OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
- PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
- EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
- ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH
- YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL
- NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
- WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
- REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR
- DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL
- DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
- (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED
- INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF
- THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR
- OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
-
-END OF TERMS AND CONDITIONS
-
-How to Apply These Terms to Your New Programs
-
-If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it free
-software which everyone can redistribute and change under these terms.
-
-To do so, attach the following notices to the program. It is safest to
-attach them to the start of each source file to most effectively convey the
-exclusion of warranty; and each file should have at least the "copyright"
-line and a pointer to where the full notice is found.
-
-one line to give the program's name and an idea of what it does.
-Copyright (C) yyyy name of author
-
-This program is free software; you can redistribute it and/or modify it
-under the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2 of the License, or (at your option)
-any later version.
-
-This program is distributed in the hope that it will be useful, but WITHOUT
-ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
-more details.
-
-You should have received a copy of the GNU General Public License along with
-this program; if not, write to the Free Software Foundation, Inc., 59
-Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this when
-it starts in an interactive mode:
-
-Gnomovision version 69, Copyright (C) year name of author Gnomovision comes
-with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free
-software, and you are welcome to redistribute it under certain conditions;
-type 'show c' for details.
-
-The hypothetical commands 'show w' and 'show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may be
-called something other than 'show w' and 'show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
-Yoyodyne, Inc., hereby disclaims all copyright interest in the program
-'Gnomovision' (which makes passes at compilers) written by James Hacker.
-
-signature of Ty Coon, 1 April 1989
-Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General Public
-License instead of this License.
diff --git a/drivers/net/e1000/Makefile b/drivers/net/e1000/Makefile
index 5dea2b7dea4..4a6ab152245 100644
--- a/drivers/net/e1000/Makefile
+++ b/drivers/net/e1000/Makefile
@@ -1,25 +1,24 @@
################################################################################
#
-#
-# Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# Intel PRO/1000 Linux driver
+# Copyright(c) 1999 - 2006 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
-#
-# The full GNU General Public License is included in this distribution in the
-# file called LICENSE.
-#
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
# Contact Information:
# Linux NICS <linux.nics@intel.com>
# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
diff --git a/drivers/net/e1000/e1000.h b/drivers/net/e1000/e1000.h
index 98afa9c2057..7ecce438d25 100644
--- a/drivers/net/e1000/e1000.h
+++ b/drivers/net/e1000/e1000.h
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -346,29 +345,9 @@ struct e1000_adapter {
};
enum e1000_state_t {
- __E1000_DRIVER_TESTING,
+ __E1000_TESTING,
__E1000_RESETTING,
+ __E1000_DOWN
};
-/* e1000_main.c */
-extern char e1000_driver_name[];
-extern char e1000_driver_version[];
-int e1000_up(struct e1000_adapter *adapter);
-void e1000_down(struct e1000_adapter *adapter);
-void e1000_reset(struct e1000_adapter *adapter);
-void e1000_reinit_locked(struct e1000_adapter *adapter);
-int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
-void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
-int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
-void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
-void e1000_update_stats(struct e1000_adapter *adapter);
-int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
-
-/* e1000_ethtool.c */
-void e1000_set_ethtool_ops(struct net_device *netdev);
-
-/* e1000_param.c */
-void e1000_check_options(struct e1000_adapter *adapter);
-
-
#endif /* _E1000_H_ */
diff --git a/drivers/net/e1000/e1000_ethtool.c b/drivers/net/e1000/e1000_ethtool.c
index e39aa1fc4d1..778ede3c021 100644
--- a/drivers/net/e1000/e1000_ethtool.c
+++ b/drivers/net/e1000/e1000_ethtool.c
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -33,6 +32,21 @@
#include <asm/uaccess.h>
+extern char e1000_driver_name[];
+extern char e1000_driver_version[];
+
+extern int e1000_up(struct e1000_adapter *adapter);
+extern void e1000_down(struct e1000_adapter *adapter);
+extern void e1000_reinit_locked(struct e1000_adapter *adapter);
+extern void e1000_reset(struct e1000_adapter *adapter);
+extern int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
+extern int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
+extern int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
+extern void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
+extern void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
+extern void e1000_update_stats(struct e1000_adapter *adapter);
+
+
struct e1000_stats {
char stat_string[ETH_GSTRING_LEN];
int sizeof_stat;
@@ -42,26 +56,30 @@ struct e1000_stats {
#define E1000_STAT(m) sizeof(((struct e1000_adapter *)0)->m), \
offsetof(struct e1000_adapter, m)
static const struct e1000_stats e1000_gstrings_stats[] = {
- { "rx_packets", E1000_STAT(net_stats.rx_packets) },
- { "tx_packets", E1000_STAT(net_stats.tx_packets) },
- { "rx_bytes", E1000_STAT(net_stats.rx_bytes) },
- { "tx_bytes", E1000_STAT(net_stats.tx_bytes) },
- { "rx_errors", E1000_STAT(net_stats.rx_errors) },
- { "tx_errors", E1000_STAT(net_stats.tx_errors) },
+ { "rx_packets", E1000_STAT(stats.gprc) },
+ { "tx_packets", E1000_STAT(stats.gptc) },
+ { "rx_bytes", E1000_STAT(stats.gorcl) },
+ { "tx_bytes", E1000_STAT(stats.gotcl) },
+ { "rx_broadcast", E1000_STAT(stats.bprc) },
+ { "tx_broadcast", E1000_STAT(stats.bptc) },
+ { "rx_multicast", E1000_STAT(stats.mprc) },
+ { "tx_multicast", E1000_STAT(stats.mptc) },
+ { "rx_errors", E1000_STAT(stats.rxerrc) },
+ { "tx_errors", E1000_STAT(stats.txerrc) },
{ "tx_dropped", E1000_STAT(net_stats.tx_dropped) },
- { "multicast", E1000_STAT(net_stats.multicast) },
- { "collisions", E1000_STAT(net_stats.collisions) },
- { "rx_length_errors", E1000_STAT(net_stats.rx_length_errors) },
+ { "multicast", E1000_STAT(stats.mprc) },
+ { "collisions", E1000_STAT(stats.colc) },
+ { "rx_length_errors", E1000_STAT(stats.rlerrc) },
{ "rx_over_errors", E1000_STAT(net_stats.rx_over_errors) },
- { "rx_crc_errors", E1000_STAT(net_stats.rx_crc_errors) },
+ { "rx_crc_errors", E1000_STAT(stats.crcerrs) },
{ "rx_frame_errors", E1000_STAT(net_stats.rx_frame_errors) },
{ "rx_no_buffer_count", E1000_STAT(stats.rnbc) },
- { "rx_missed_errors", E1000_STAT(net_stats.rx_missed_errors) },
- { "tx_aborted_errors", E1000_STAT(net_stats.tx_aborted_errors) },
- { "tx_carrier_errors", E1000_STAT(net_stats.tx_carrier_errors) },
+ { "rx_missed_errors", E1000_STAT(stats.mpc) },
+ { "tx_aborted_errors", E1000_STAT(stats.ecol) },
+ { "tx_carrier_errors", E1000_STAT(stats.tncrs) },
{ "tx_fifo_errors", E1000_STAT(net_stats.tx_fifo_errors) },
{ "tx_heartbeat_errors", E1000_STAT(net_stats.tx_heartbeat_errors) },
- { "tx_window_errors", E1000_STAT(net_stats.tx_window_errors) },
+ { "tx_window_errors", E1000_STAT(stats.latecol) },
{ "tx_abort_late_coll", E1000_STAT(stats.latecol) },
{ "tx_deferred_ok", E1000_STAT(stats.dc) },
{ "tx_single_coll_ok", E1000_STAT(stats.scc) },
@@ -193,13 +211,9 @@ e1000_set_settings(struct net_device *netdev, struct ethtool_cmd *ecmd)
ADVERTISED_FIBRE |
ADVERTISED_Autoneg;
else
- hw->autoneg_advertised = ADVERTISED_10baseT_Half |
- ADVERTISED_10baseT_Full |
- ADVERTISED_100baseT_Half |
- ADVERTISED_100baseT_Full |
- ADVERTISED_1000baseT_Full|
- ADVERTISED_Autoneg |
- ADVERTISED_TP;
+ hw->autoneg_advertised = ecmd->advertising |
+ ADVERTISED_TP |
+ ADVERTISED_Autoneg;
ecmd->advertising = hw->autoneg_advertised;
} else
if (e1000_set_spd_dplx(adapter, ecmd->speed + ecmd->duplex)) {
@@ -229,11 +243,11 @@ e1000_get_pauseparam(struct net_device *netdev,
pause->autoneg =
(adapter->fc_autoneg ? AUTONEG_ENABLE : AUTONEG_DISABLE);
- if (hw->fc == e1000_fc_rx_pause)
+ if (hw->fc == E1000_FC_RX_PAUSE)
pause->rx_pause = 1;
- else if (hw->fc == e1000_fc_tx_pause)
+ else if (hw->fc == E1000_FC_TX_PAUSE)
pause->tx_pause = 1;
- else if (hw->fc == e1000_fc_full) {
+ else if (hw->fc == E1000_FC_FULL) {
pause->rx_pause = 1;
pause->tx_pause = 1;
}
@@ -253,13 +267,13 @@ e1000_set_pauseparam(struct net_device *netdev,
msleep(1);
if (pause->rx_pause && pause->tx_pause)
- hw->fc = e1000_fc_full;
+ hw->fc = E1000_FC_FULL;
else if (pause->rx_pause && !pause->tx_pause)
- hw->fc = e1000_fc_rx_pause;
+ hw->fc = E1000_FC_RX_PAUSE;
else if (!pause->rx_pause && pause->tx_pause)
- hw->fc = e1000_fc_tx_pause;
+ hw->fc = E1000_FC_TX_PAUSE;
else if (!pause->rx_pause && !pause->tx_pause)
- hw->fc = e1000_fc_none;
+ hw->fc = E1000_FC_NONE;
hw->original_fc = hw->fc;
@@ -632,8 +646,8 @@ e1000_set_ringparam(struct net_device *netdev,
{
struct e1000_adapter *adapter = netdev_priv(netdev);
e1000_mac_type mac_type = adapter->hw.mac_type;
- struct e1000_tx_ring *txdr, *tx_old, *tx_new;
- struct e1000_rx_ring *rxdr, *rx_old, *rx_new;
+ struct e1000_tx_ring *txdr, *tx_old;
+ struct e1000_rx_ring *rxdr, *rx_old;
int i, err, tx_ring_size, rx_ring_size;
if ((ring->rx_mini_pending) || (ring->rx_jumbo_pending))
@@ -651,23 +665,17 @@ e1000_set_ringparam(struct net_device *netdev,
tx_old = adapter->tx_ring;
rx_old = adapter->rx_ring;
- adapter->tx_ring = kmalloc(tx_ring_size, GFP_KERNEL);
- if (!adapter->tx_ring) {
- err = -ENOMEM;
- goto err_setup_rx;
- }
- memset(adapter->tx_ring, 0, tx_ring_size);
+ err = -ENOMEM;
+ txdr = kzalloc(tx_ring_size, GFP_KERNEL);
+ if (!txdr)
+ goto err_alloc_tx;
- adapter->rx_ring = kmalloc(rx_ring_size, GFP_KERNEL);
- if (!adapter->rx_ring) {
- kfree(adapter->tx_ring);
- err = -ENOMEM;
- goto err_setup_rx;
- }
- memset(adapter->rx_ring, 0, rx_ring_size);
+ rxdr = kzalloc(rx_ring_size, GFP_KERNEL);
+ if (!rxdr)
+ goto err_alloc_rx;
- txdr = adapter->tx_ring;
- rxdr = adapter->rx_ring;
+ adapter->tx_ring = txdr;
+ adapter->rx_ring = rxdr;
rxdr->count = max(ring->rx_pending,(uint32_t)E1000_MIN_RXD);
rxdr->count = min(rxdr->count,(uint32_t)(mac_type < e1000_82544 ?
@@ -694,16 +702,14 @@ e1000_set_ringparam(struct net_device *netdev,
/* save the new, restore the old in order to free it,
* then restore the new back again */
- rx_new = adapter->rx_ring;
- tx_new = adapter->tx_ring;
adapter->rx_ring = rx_old;
adapter->tx_ring = tx_old;
e1000_free_all_rx_resources(adapter);
e1000_free_all_tx_resources(adapter);
kfree(tx_old);
kfree(rx_old);
- adapter->rx_ring = rx_new;
- adapter->tx_ring = tx_new;
+ adapter->rx_ring = rxdr;
+ adapter->tx_ring = txdr;
if ((err = e1000_up(adapter)))
goto err_setup;
}
@@ -715,6 +721,10 @@ err_setup_tx:
err_setup_rx:
adapter->rx_ring = rx_old;
adapter->tx_ring = tx_old;
+ kfree(rxdr);
+err_alloc_rx:
+ kfree(txdr);
+err_alloc_tx:
e1000_up(adapter);
err_setup:
clear_bit(__E1000_RESETTING, &adapter->flags);
@@ -1610,7 +1620,7 @@ e1000_diag_test(struct net_device *netdev,
struct e1000_adapter *adapter = netdev_priv(netdev);
boolean_t if_running = netif_running(netdev);
- set_bit(__E1000_DRIVER_TESTING, &adapter->flags);
+ set_bit(__E1000_TESTING, &adapter->flags);
if (eth_test->flags == ETH_TEST_FL_OFFLINE) {
/* Offline tests */
@@ -1655,7 +1665,7 @@ e1000_diag_test(struct net_device *netdev,
adapter->hw.autoneg = autoneg;
e1000_reset(adapter);
- clear_bit(__E1000_DRIVER_TESTING, &adapter->flags);
+ clear_bit(__E1000_TESTING, &adapter->flags);
if (if_running)
dev_open(netdev);
} else {
@@ -1670,7 +1680,7 @@ e1000_diag_test(struct net_device *netdev,
data[2] = 0;
data[3] = 0;
- clear_bit(__E1000_DRIVER_TESTING, &adapter->flags);
+ clear_bit(__E1000_TESTING, &adapter->flags);
}
msleep_interruptible(4 * 1000);
}
diff --git a/drivers/net/e1000/e1000_hw.c b/drivers/net/e1000/e1000_hw.c
index 10b8c8c2532..65077f39da6 100644
--- a/drivers/net/e1000/e1000_hw.c
+++ b/drivers/net/e1000/e1000_hw.c
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -34,6 +33,63 @@
#include "e1000_hw.h"
+static int32_t e1000_swfw_sync_acquire(struct e1000_hw *hw, uint16_t mask);
+static void e1000_swfw_sync_release(struct e1000_hw *hw, uint16_t mask);
+static int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *data);
+static int32_t e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data);
+static int32_t e1000_get_software_semaphore(struct e1000_hw *hw);
+static void e1000_release_software_semaphore(struct e1000_hw *hw);
+
+static uint8_t e1000_arc_subsystem_valid(struct e1000_hw *hw);
+static int32_t e1000_check_downshift(struct e1000_hw *hw);
+static int32_t e1000_check_polarity(struct e1000_hw *hw, e1000_rev_polarity *polarity);
+static void e1000_clear_hw_cntrs(struct e1000_hw *hw);
+static void e1000_clear_vfta(struct e1000_hw *hw);
+static int32_t e1000_commit_shadow_ram(struct e1000_hw *hw);
+static int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw, boolean_t link_up);
+static int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
+static int32_t e1000_detect_gig_phy(struct e1000_hw *hw);
+static int32_t e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank);
+static int32_t e1000_get_auto_rd_done(struct e1000_hw *hw);
+static int32_t e1000_get_cable_length(struct e1000_hw *hw, uint16_t *min_length, uint16_t *max_length);
+static int32_t e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw);
+static int32_t e1000_get_phy_cfg_done(struct e1000_hw *hw);
+static int32_t e1000_get_software_flag(struct e1000_hw *hw);
+static int32_t e1000_ich8_cycle_init(struct e1000_hw *hw);
+static int32_t e1000_ich8_flash_cycle(struct e1000_hw *hw, uint32_t timeout);
+static int32_t e1000_id_led_init(struct e1000_hw *hw);
+static int32_t e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw, uint32_t cnf_base_addr, uint32_t cnf_size);
+static int32_t e1000_init_lcd_from_nvm(struct e1000_hw *hw);
+static void e1000_init_rx_addrs(struct e1000_hw *hw);
+static void e1000_initialize_hardware_bits(struct e1000_hw *hw);
+static boolean_t e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw);
+static int32_t e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw);
+static int32_t e1000_mng_enable_host_if(struct e1000_hw *hw);
+static int32_t e1000_mng_host_if_write(struct e1000_hw *hw, uint8_t *buffer, uint16_t length, uint16_t offset, uint8_t *sum);
+static int32_t e1000_mng_write_cmd_header(struct e1000_hw* hw, struct e1000_host_mng_command_header* hdr);
+static int32_t e1000_mng_write_commit(struct e1000_hw *hw);
+static int32_t e1000_phy_ife_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static int32_t e1000_phy_igp_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static int32_t e1000_read_eeprom_eerd(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_eewr(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd);
+static int32_t e1000_phy_m88_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
+static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw);
+static int32_t e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t *data);
+static int32_t e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte);
+static int32_t e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte);
+static int32_t e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data);
+static int32_t e1000_read_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t *data);
+static int32_t e1000_write_ich8_data(struct e1000_hw *hw, uint32_t index, uint32_t size, uint16_t data);
+static int32_t e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static int32_t e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset, uint16_t words, uint16_t *data);
+static void e1000_release_software_flag(struct e1000_hw *hw);
+static int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active);
+static int32_t e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active);
+static int32_t e1000_set_pci_ex_no_snoop(struct e1000_hw *hw, uint32_t no_snoop);
+static void e1000_set_pci_express_master_disable(struct e1000_hw *hw);
+static int32_t e1000_wait_autoneg(struct e1000_hw *hw);
+static void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset, uint32_t value);
static int32_t e1000_set_phy_type(struct e1000_hw *hw);
static void e1000_phy_init_script(struct e1000_hw *hw);
static int32_t e1000_setup_copper_link(struct e1000_hw *hw);
@@ -70,69 +126,10 @@ static int32_t e1000_polarity_reversal_workaround(struct e1000_hw *hw);
static int32_t e1000_set_phy_mode(struct e1000_hw *hw);
static int32_t e1000_host_if_read_cookie(struct e1000_hw *hw, uint8_t *buffer);
static uint8_t e1000_calculate_mng_checksum(char *buffer, uint32_t length);
-static uint8_t e1000_arc_subsystem_valid(struct e1000_hw *hw);
-static int32_t e1000_check_downshift(struct e1000_hw *hw);
-static int32_t e1000_check_polarity(struct e1000_hw *hw, uint16_t *polarity);
-static void e1000_clear_hw_cntrs(struct e1000_hw *hw);
-static void e1000_clear_vfta(struct e1000_hw *hw);
-static int32_t e1000_commit_shadow_ram(struct e1000_hw *hw);
-static int32_t e1000_config_dsp_after_link_change(struct e1000_hw *hw,
- boolean_t link_up);
-static int32_t e1000_config_fc_after_link_up(struct e1000_hw *hw);
-static int32_t e1000_detect_gig_phy(struct e1000_hw *hw);
-static int32_t e1000_get_auto_rd_done(struct e1000_hw *hw);
-static int32_t e1000_get_cable_length(struct e1000_hw *hw,
- uint16_t *min_length,
- uint16_t *max_length);
-static int32_t e1000_get_hw_eeprom_semaphore(struct e1000_hw *hw);
-static int32_t e1000_get_phy_cfg_done(struct e1000_hw *hw);
-static int32_t e1000_id_led_init(struct e1000_hw * hw);
-static void e1000_init_rx_addrs(struct e1000_hw *hw);
-static boolean_t e1000_is_onboard_nvm_eeprom(struct e1000_hw *hw);
-static int32_t e1000_poll_eerd_eewr_done(struct e1000_hw *hw, int eerd);
-static void e1000_put_hw_eeprom_semaphore(struct e1000_hw *hw);
-static int32_t e1000_read_eeprom_eerd(struct e1000_hw *hw, uint16_t offset,
- uint16_t words, uint16_t *data);
-static int32_t e1000_set_d0_lplu_state(struct e1000_hw *hw, boolean_t active);
-static int32_t e1000_set_d3_lplu_state(struct e1000_hw *hw, boolean_t active);
-static int32_t e1000_wait_autoneg(struct e1000_hw *hw);
-
-static void e1000_write_reg_io(struct e1000_hw *hw, uint32_t offset,
- uint32_t value);
-
-#define E1000_WRITE_REG_IO(a, reg, val) \
- e1000_write_reg_io((a), E1000_##reg, val)
static int32_t e1000_configure_kmrn_for_10_100(struct e1000_hw *hw,
uint16_t duplex);
static int32_t e1000_configure_kmrn_for_1000(struct e1000_hw *hw);
-static int32_t e1000_erase_ich8_4k_segment(struct e1000_hw *hw,
- uint32_t segment);
-static int32_t e1000_get_software_flag(struct e1000_hw *hw);
-static int32_t e1000_get_software_semaphore(struct e1000_hw *hw);
-static int32_t e1000_init_lcd_from_nvm(struct e1000_hw *hw);
-static int32_t e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw);
-static int32_t e1000_read_eeprom_ich8(struct e1000_hw *hw, uint16_t offset,
- uint16_t words, uint16_t *data);
-static int32_t e1000_read_ich8_byte(struct e1000_hw *hw, uint32_t index,
- uint8_t* data);
-static int32_t e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index,
- uint16_t *data);
-static int32_t e1000_read_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr,
- uint16_t *data);
-static void e1000_release_software_flag(struct e1000_hw *hw);
-static void e1000_release_software_semaphore(struct e1000_hw *hw);
-static int32_t e1000_set_pci_ex_no_snoop(struct e1000_hw *hw,
- uint32_t no_snoop);
-static int32_t e1000_verify_write_ich8_byte(struct e1000_hw *hw,
- uint32_t index, uint8_t byte);
-static int32_t e1000_write_eeprom_ich8(struct e1000_hw *hw, uint16_t offset,
- uint16_t words, uint16_t *data);
-static int32_t e1000_write_ich8_byte(struct e1000_hw *hw, uint32_t index,
- uint8_t data);
-static int32_t e1000_write_kmrn_reg(struct e1000_hw *hw, uint32_t reg_addr,
- uint16_t data);
-
/* IGP cable length table */
static const
uint16_t e1000_igp_cable_length_table[IGP01E1000_AGC_LENGTH_TABLE_SIZE] =
@@ -156,13 +153,12 @@ uint16_t e1000_igp_2_cable_length_table[IGP02E1000_AGC_LENGTH_TABLE_SIZE] =
83, 89, 95, 100, 105, 109, 113, 116, 119, 122, 124,
104, 109, 114, 118, 121, 124};
-
/******************************************************************************
* Set the phy type member in the hw struct.
*
* hw - Struct containing variables accessed by shared code
*****************************************************************************/
-int32_t
+static int32_t
e1000_set_phy_type(struct e1000_hw *hw)
{
DEBUGFUNC("e1000_set_phy_type");
@@ -208,7 +204,6 @@ e1000_set_phy_type(struct e1000_hw *hw)
return E1000_SUCCESS;
}
-
/******************************************************************************
* IGP phy init script - initializes the GbE PHY
*
@@ -667,19 +662,12 @@ e1000_reset_hw(struct e1000_hw *hw)
E1000_WRITE_FLUSH(hw);
}
/* fall through */
- case e1000_82571:
- case e1000_82572:
- case e1000_ich8lan:
- case e1000_80003es2lan:
+ default:
+ /* Auto read done will delay 5ms or poll based on mac type */
ret_val = e1000_get_auto_rd_done(hw);
if (ret_val)
- /* We don't want to continue accessing MAC registers. */
return ret_val;
break;
- default:
- /* Wait for EEPROM reload (it happens automatically) */
- msleep(5);
- break;
}
/* Disable HW ARPs on ASF enabled adapters */
@@ -722,6 +710,123 @@ e1000_reset_hw(struct e1000_hw *hw)
}
/******************************************************************************
+ *
+ * Initialize a number of hardware-dependent bits
+ *
+ * hw: Struct containing variables accessed by shared code
+ *
+ * This function contains hardware limitation workarounds for PCI-E adapters
+ *
+ *****************************************************************************/
+static void
+e1000_initialize_hardware_bits(struct e1000_hw *hw)
+{
+ if ((hw->mac_type >= e1000_82571) && (!hw->initialize_hw_bits_disable)) {
+ /* Settings common to all PCI-express silicon */
+ uint32_t reg_ctrl, reg_ctrl_ext;
+ uint32_t reg_tarc0, reg_tarc1;
+ uint32_t reg_tctl;
+ uint32_t reg_txdctl, reg_txdctl1;
+
+ /* link autonegotiation/sync workarounds */
+ reg_tarc0 = E1000_READ_REG(hw, TARC0);
+ reg_tarc0 &= ~((1 << 30)|(1 << 29)|(1 << 28)|(1 << 27));
+
+ /* Enable not-done TX descriptor counting */
+ reg_txdctl = E1000_READ_REG(hw, TXDCTL);
+ reg_txdctl |= E1000_TXDCTL_COUNT_DESC;
+ E1000_WRITE_REG(hw, TXDCTL, reg_txdctl);
+ reg_txdctl1 = E1000_READ_REG(hw, TXDCTL1);
+ reg_txdctl1 |= E1000_TXDCTL_COUNT_DESC;
+ E1000_WRITE_REG(hw, TXDCTL1, reg_txdctl1);
+
+ switch (hw->mac_type) {
+ case e1000_82571:
+ case e1000_82572:
+ /* Clear PHY TX compatible mode bits */
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ reg_tarc1 &= ~((1 << 30)|(1 << 29));
+
+ /* link autonegotiation/sync workarounds */
+ reg_tarc0 |= ((1 << 26)|(1 << 25)|(1 << 24)|(1 << 23));
+
+ /* TX ring control fixes */
+ reg_tarc1 |= ((1 << 26)|(1 << 25)|(1 << 24));
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ case e1000_82573:
+ reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ reg_ctrl_ext &= ~(1 << 23);
+ reg_ctrl_ext |= (1 << 22);
+
+ /* TX byte count fix */
+ reg_ctrl = E1000_READ_REG(hw, CTRL);
+ reg_ctrl &= ~(1 << 29);
+
+ E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext);
+ E1000_WRITE_REG(hw, CTRL, reg_ctrl);
+ break;
+ case e1000_80003es2lan:
+ /* improve small packet performace for fiber/serdes */
+ if ((hw->media_type == e1000_media_type_fiber) ||
+ (hw->media_type == e1000_media_type_internal_serdes)) {
+ reg_tarc0 &= ~(1 << 20);
+ }
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ case e1000_ich8lan:
+ /* Reduce concurrent DMA requests to 3 from 4 */
+ if ((hw->revision_id < 3) ||
+ ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) &&
+ (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))
+ reg_tarc0 |= ((1 << 29)|(1 << 28));
+
+ reg_ctrl_ext = E1000_READ_REG(hw, CTRL_EXT);
+ reg_ctrl_ext |= (1 << 22);
+ E1000_WRITE_REG(hw, CTRL_EXT, reg_ctrl_ext);
+
+ /* workaround TX hang with TSO=on */
+ reg_tarc0 |= ((1 << 27)|(1 << 26)|(1 << 24)|(1 << 23));
+
+ /* Multiple read bit is reversed polarity */
+ reg_tctl = E1000_READ_REG(hw, TCTL);
+ reg_tarc1 = E1000_READ_REG(hw, TARC1);
+ if (reg_tctl & E1000_TCTL_MULR)
+ reg_tarc1 &= ~(1 << 28);
+ else
+ reg_tarc1 |= (1 << 28);
+
+ /* workaround TX hang with TSO=on */
+ reg_tarc1 |= ((1 << 30)|(1 << 26)|(1 << 24));
+
+ E1000_WRITE_REG(hw, TARC1, reg_tarc1);
+ break;
+ default:
+ break;
+ }
+
+ E1000_WRITE_REG(hw, TARC0, reg_tarc0);
+ }
+}
+
+/******************************************************************************
* Performs basic configuration of the adapter.
*
* hw - Struct containing variables accessed by shared code
@@ -749,14 +854,13 @@ e1000_init_hw(struct e1000_hw *hw)
DEBUGFUNC("e1000_init_hw");
/* force full DMA clock frequency for 10/100 on ICH8 A0-B0 */
- if (hw->mac_type == e1000_ich8lan) {
- reg_data = E1000_READ_REG(hw, TARC0);
- reg_data |= 0x30000000;
- E1000_WRITE_REG(hw, TARC0, reg_data);
-
- reg_data = E1000_READ_REG(hw, STATUS);
- reg_data &= ~0x80000000;
- E1000_WRITE_REG(hw, STATUS, reg_data);
+ if ((hw->mac_type == e1000_ich8lan) &&
+ ((hw->revision_id < 3) ||
+ ((hw->device_id != E1000_DEV_ID_ICH8_IGP_M_AMT) &&
+ (hw->device_id != E1000_DEV_ID_ICH8_IGP_M)))) {
+ reg_data = E1000_READ_REG(hw, STATUS);
+ reg_data &= ~0x80000000;
+ E1000_WRITE_REG(hw, STATUS, reg_data);
}
/* Initialize Identification LED */
@@ -769,6 +873,9 @@ e1000_init_hw(struct e1000_hw *hw)
/* Set the media type and TBI compatibility */
e1000_set_media_type(hw);
+ /* Must be called after e1000_set_media_type because media_type is used */
+ e1000_initialize_hardware_bits(hw);
+
/* Disabling VLAN filtering. */
DEBUGOUT("Initializing the IEEE VLAN\n");
/* VET hardcoded to standard value and VFTA removed in ICH8 LAN */
@@ -860,17 +967,6 @@ e1000_init_hw(struct e1000_hw *hw)
if (hw->mac_type > e1000_82544) {
ctrl = E1000_READ_REG(hw, TXDCTL);
ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB;
- switch (hw->mac_type) {
- default:
- break;
- case e1000_82571:
- case e1000_82572:
- case e1000_82573:
- case e1000_ich8lan:
- case e1000_80003es2lan:
- ctrl |= E1000_TXDCTL_COUNT_DESC;
- break;
- }
E1000_WRITE_REG(hw, TXDCTL, ctrl);
}
@@ -908,8 +1004,6 @@ e1000_init_hw(struct e1000_hw *hw)
case e1000_ich8lan:
ctrl = E1000_READ_REG(hw, TXDCTL1);
ctrl = (ctrl & ~E1000_TXDCTL_WTHRESH) | E1000_TXDCTL_FULL_TX_DESC_WB;
- if (hw->mac_type >= e1000_82571)
- ctrl |= E1000_TXDCTL_COUNT_DESC;
E1000_WRITE_REG(hw, TXDCTL1, ctrl);
break;
}
@@ -1018,11 +1112,11 @@ e1000_setup_link(struct e1000_hw *hw)
* control setting, then the variable hw->fc will
* be initialized based on a value in the EEPROM.
*/
- if (hw->fc == e1000_fc_default) {
+ if (hw->fc == E1000_FC_DEFAULT) {
switch (hw->mac_type) {
case e1000_ich8lan:
case e1000_82573:
- hw->fc = e1000_fc_full;
+ hw->fc = E1000_FC_FULL;
break;
default:
ret_val = e1000_read_eeprom(hw, EEPROM_INIT_CONTROL2_REG,
@@ -1032,12 +1126,12 @@ e1000_setup_link(struct e1000_hw *hw)
return -E1000_ERR_EEPROM;
}
if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) == 0)
- hw->fc = e1000_fc_none;
+ hw->fc = E1000_FC_NONE;
else if ((eeprom_data & EEPROM_WORD0F_PAUSE_MASK) ==
EEPROM_WORD0F_ASM_DIR)
- hw->fc = e1000_fc_tx_pause;
+ hw->fc = E1000_FC_TX_PAUSE;
else
- hw->fc = e1000_fc_full;
+ hw->fc = E1000_FC_FULL;
break;
}
}
@@ -1047,10 +1141,10 @@ e1000_setup_link(struct e1000_hw *hw)
* hub or switch with different Flow Control capabilities.
*/
if (hw->mac_type == e1000_82542_rev2_0)
- hw->fc &= (~e1000_fc_tx_pause);
+ hw->fc &= (~E1000_FC_TX_PAUSE);
if ((hw->mac_type < e1000_82543) && (hw->report_tx_early == 1))
- hw->fc &= (~e1000_fc_rx_pause);
+ hw->fc &= (~E1000_FC_RX_PAUSE);
hw->original_fc = hw->fc;
@@ -1102,7 +1196,7 @@ e1000_setup_link(struct e1000_hw *hw)
* ability to transmit pause frames in not enabled, then these
* registers will be set to 0.
*/
- if (!(hw->fc & e1000_fc_tx_pause)) {
+ if (!(hw->fc & E1000_FC_TX_PAUSE)) {
E1000_WRITE_REG(hw, FCRTL, 0);
E1000_WRITE_REG(hw, FCRTH, 0);
} else {
@@ -1149,11 +1243,11 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572)
E1000_WRITE_REG(hw, SCTL, E1000_DISABLE_SERDES_LOOPBACK);
- /* On adapters with a MAC newer than 82544, SW Defineable pin 1 will be
+ /* On adapters with a MAC newer than 82544, SWDP 1 will be
* set when the optics detect a signal. On older adapters, it will be
* cleared when there is a signal. This applies to fiber media only.
- * If we're on serdes media, adjust the output amplitude to value set in
- * the EEPROM.
+ * If we're on serdes media, adjust the output amplitude to value
+ * set in the EEPROM.
*/
ctrl = E1000_READ_REG(hw, CTRL);
if (hw->media_type == e1000_media_type_fiber)
@@ -1189,11 +1283,11 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
* 3: Both Rx and TX flow control (symmetric) are enabled.
*/
switch (hw->fc) {
- case e1000_fc_none:
+ case E1000_FC_NONE:
/* Flow control is completely disabled by a software over-ride. */
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD);
break;
- case e1000_fc_rx_pause:
+ case E1000_FC_RX_PAUSE:
/* RX Flow control is enabled and TX Flow control is disabled by a
* software over-ride. Since there really isn't a way to advertise
* that we are capable of RX Pause ONLY, we will advertise that we
@@ -1202,13 +1296,13 @@ e1000_setup_fiber_serdes_link(struct e1000_hw *hw)
*/
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
break;
- case e1000_fc_tx_pause:
+ case E1000_FC_TX_PAUSE:
/* TX Flow control is enabled, and RX Flow control is disabled, by a
* software over-ride.
*/
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_ASM_DIR);
break;
- case e1000_fc_full:
+ case E1000_FC_FULL:
/* Flow control (both RX and TX) is enabled by a software over-ride. */
txcw = (E1000_TXCW_ANE | E1000_TXCW_FD | E1000_TXCW_PAUSE_MASK);
break;
@@ -2124,13 +2218,13 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw)
* in the EEPROM is used.
*/
switch (hw->fc) {
- case e1000_fc_none: /* 0 */
+ case E1000_FC_NONE: /* 0 */
/* Flow control (RX & TX) is completely disabled by a
* software over-ride.
*/
mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
- case e1000_fc_rx_pause: /* 1 */
+ case E1000_FC_RX_PAUSE: /* 1 */
/* RX Flow control is enabled, and TX Flow control is
* disabled, by a software over-ride.
*/
@@ -2142,14 +2236,14 @@ e1000_phy_setup_autoneg(struct e1000_hw *hw)
*/
mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
break;
- case e1000_fc_tx_pause: /* 2 */
+ case E1000_FC_TX_PAUSE: /* 2 */
/* TX Flow control is enabled, and RX Flow control is
* disabled, by a software over-ride.
*/
mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
break;
- case e1000_fc_full: /* 3 */
+ case E1000_FC_FULL: /* 3 */
/* Flow control (both RX and TX) is enabled by a software
* over-ride.
*/
@@ -2193,7 +2287,7 @@ e1000_phy_force_speed_duplex(struct e1000_hw *hw)
DEBUGFUNC("e1000_phy_force_speed_duplex");
/* Turn off Flow control if we are forcing speed and duplex. */
- hw->fc = e1000_fc_none;
+ hw->fc = E1000_FC_NONE;
DEBUGOUT1("hw->fc = %d\n", hw->fc);
@@ -2547,18 +2641,18 @@ e1000_force_mac_fc(struct e1000_hw *hw)
*/
switch (hw->fc) {
- case e1000_fc_none:
+ case E1000_FC_NONE:
ctrl &= (~(E1000_CTRL_TFCE | E1000_CTRL_RFCE));
break;
- case e1000_fc_rx_pause:
+ case E1000_FC_RX_PAUSE:
ctrl &= (~E1000_CTRL_TFCE);
ctrl |= E1000_CTRL_RFCE;
break;
- case e1000_fc_tx_pause:
+ case E1000_FC_TX_PAUSE:
ctrl &= (~E1000_CTRL_RFCE);
ctrl |= E1000_CTRL_TFCE;
break;
- case e1000_fc_full:
+ case E1000_FC_FULL:
ctrl |= (E1000_CTRL_TFCE | E1000_CTRL_RFCE);
break;
default:
@@ -2657,14 +2751,14 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* LOCAL DEVICE | LINK PARTNER
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
*-------|---------|-------|---------|--------------------
- * 0 | 0 | DC | DC | e1000_fc_none
- * 0 | 1 | 0 | DC | e1000_fc_none
- * 0 | 1 | 1 | 0 | e1000_fc_none
- * 0 | 1 | 1 | 1 | e1000_fc_tx_pause
- * 1 | 0 | 0 | DC | e1000_fc_none
- * 1 | DC | 1 | DC | e1000_fc_full
- * 1 | 1 | 0 | 0 | e1000_fc_none
- * 1 | 1 | 0 | 1 | e1000_fc_rx_pause
+ * 0 | 0 | DC | DC | E1000_FC_NONE
+ * 0 | 1 | 0 | DC | E1000_FC_NONE
+ * 0 | 1 | 1 | 0 | E1000_FC_NONE
+ * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE
+ * 1 | 0 | 0 | DC | E1000_FC_NONE
+ * 1 | DC | 1 | DC | E1000_FC_FULL
+ * 1 | 1 | 0 | 0 | E1000_FC_NONE
+ * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE
*
*/
/* Are both PAUSE bits set to 1? If so, this implies
@@ -2676,7 +2770,7 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* LOCAL DEVICE | LINK PARTNER
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
*-------|---------|-------|---------|--------------------
- * 1 | DC | 1 | DC | e1000_fc_full
+ * 1 | DC | 1 | DC | E1000_FC_FULL
*
*/
if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
@@ -2687,11 +2781,11 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* ONLY. Hence, we must now check to see if we need to
* turn OFF the TRANSMISSION of PAUSE frames.
*/
- if (hw->original_fc == e1000_fc_full) {
- hw->fc = e1000_fc_full;
+ if (hw->original_fc == E1000_FC_FULL) {
+ hw->fc = E1000_FC_FULL;
DEBUGOUT("Flow Control = FULL.\n");
} else {
- hw->fc = e1000_fc_rx_pause;
+ hw->fc = E1000_FC_RX_PAUSE;
DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
}
}
@@ -2700,14 +2794,14 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* LOCAL DEVICE | LINK PARTNER
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
*-------|---------|-------|---------|--------------------
- * 0 | 1 | 1 | 1 | e1000_fc_tx_pause
+ * 0 | 1 | 1 | 1 | E1000_FC_TX_PAUSE
*
*/
else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
- hw->fc = e1000_fc_tx_pause;
+ hw->fc = E1000_FC_TX_PAUSE;
DEBUGOUT("Flow Control = TX PAUSE frames only.\n");
}
/* For transmitting PAUSE frames ONLY.
@@ -2715,14 +2809,14 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* LOCAL DEVICE | LINK PARTNER
* PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
*-------|---------|-------|---------|--------------------
- * 1 | 1 | 0 | 1 | e1000_fc_rx_pause
+ * 1 | 1 | 0 | 1 | E1000_FC_RX_PAUSE
*
*/
else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
(mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
!(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
(mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
- hw->fc = e1000_fc_rx_pause;
+ hw->fc = E1000_FC_RX_PAUSE;
DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
}
/* Per the IEEE spec, at this point flow control should be
@@ -2745,13 +2839,13 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
* be asked to delay transmission of packets than asking
* our link partner to pause transmission of frames.
*/
- else if ((hw->original_fc == e1000_fc_none ||
- hw->original_fc == e1000_fc_tx_pause) ||
+ else if ((hw->original_fc == E1000_FC_NONE ||
+ hw->original_fc == E1000_FC_TX_PAUSE) ||
hw->fc_strict_ieee) {
- hw->fc = e1000_fc_none;
+ hw->fc = E1000_FC_NONE;
DEBUGOUT("Flow Control = NONE.\n");
} else {
- hw->fc = e1000_fc_rx_pause;
+ hw->fc = E1000_FC_RX_PAUSE;
DEBUGOUT("Flow Control = RX PAUSE frames only.\n");
}
@@ -2766,7 +2860,7 @@ e1000_config_fc_after_link_up(struct e1000_hw *hw)
}
if (duplex == HALF_DUPLEX)
- hw->fc = e1000_fc_none;
+ hw->fc = E1000_FC_NONE;
/* Now we call a subroutine to actually force the MAC
* controller to use the correct flow control settings.
@@ -3417,9 +3511,8 @@ e1000_read_phy_reg(struct e1000_hw *hw,
return ret_val;
}
-int32_t
-e1000_read_phy_reg_ex(struct e1000_hw *hw,
- uint32_t reg_addr,
+static int32_t
+e1000_read_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
uint16_t *phy_data)
{
uint32_t i;
@@ -3499,8 +3592,7 @@ e1000_read_phy_reg_ex(struct e1000_hw *hw,
* data - data to write to the PHY
******************************************************************************/
int32_t
-e1000_write_phy_reg(struct e1000_hw *hw,
- uint32_t reg_addr,
+e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr,
uint16_t phy_data)
{
uint32_t ret_val;
@@ -3557,10 +3649,9 @@ e1000_write_phy_reg(struct e1000_hw *hw,
return ret_val;
}
-int32_t
-e1000_write_phy_reg_ex(struct e1000_hw *hw,
- uint32_t reg_addr,
- uint16_t phy_data)
+static int32_t
+e1000_write_phy_reg_ex(struct e1000_hw *hw, uint32_t reg_addr,
+ uint16_t phy_data)
{
uint32_t i;
uint32_t mdic = 0;
@@ -3711,7 +3802,7 @@ e1000_phy_hw_reset(struct e1000_hw *hw)
swfw = E1000_SWFW_PHY0_SM;
}
if (e1000_swfw_sync_acquire(hw, swfw)) {
- e1000_release_software_semaphore(hw);
+ DEBUGOUT("Unable to acquire swfw sync\n");
return -E1000_ERR_SWFW_SYNC;
}
/* Read the device control register and assert the E1000_CTRL_PHY_RST
@@ -3734,6 +3825,7 @@ e1000_phy_hw_reset(struct e1000_hw *hw)
if (hw->mac_type >= e1000_82571)
mdelay(10);
+
e1000_swfw_sync_release(hw, swfw);
} else {
/* Read the Extended Device Control Register, assert the PHY_RESET_DIR
@@ -3792,15 +3884,14 @@ e1000_phy_reset(struct e1000_hw *hw)
if (ret_val)
return E1000_SUCCESS;
- switch (hw->mac_type) {
- case e1000_82541_rev_2:
- case e1000_82571:
- case e1000_82572:
- case e1000_ich8lan:
+ switch (hw->phy_type) {
+ case e1000_phy_igp:
+ case e1000_phy_igp_2:
+ case e1000_phy_igp_3:
+ case e1000_phy_ife:
ret_val = e1000_phy_hw_reset(hw);
if (ret_val)
return ret_val;
-
break;
default:
ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data);
@@ -3936,7 +4027,7 @@ e1000_kumeran_lock_loss_workaround(struct e1000_hw *hw)
*
* hw - Struct containing variables accessed by shared code
******************************************************************************/
-int32_t
+static int32_t
e1000_detect_gig_phy(struct e1000_hw *hw)
{
int32_t phy_init_status, ret_val;
@@ -3945,6 +4036,9 @@ e1000_detect_gig_phy(struct e1000_hw *hw)
DEBUGFUNC("e1000_detect_gig_phy");
+ if (hw->phy_id != 0)
+ return E1000_SUCCESS;
+
/* The 82571 firmware may still be configuring the PHY. In this
* case, we cannot access the PHY until the configuration is done. So
* we explicitly set the PHY values. */
@@ -4061,7 +4155,8 @@ e1000_phy_igp_get_info(struct e1000_hw *hw,
struct e1000_phy_info *phy_info)
{
int32_t ret_val;
- uint16_t phy_data, polarity, min_length, max_length, average;
+ uint16_t phy_data, min_length, max_length, average;
+ e1000_rev_polarity polarity;
DEBUGFUNC("e1000_phy_igp_get_info");
@@ -4086,8 +4181,8 @@ e1000_phy_igp_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
- phy_info->mdix_mode = (phy_data & IGP01E1000_PSSR_MDIX) >>
- IGP01E1000_PSSR_MDIX_SHIFT;
+ phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & IGP01E1000_PSSR_MDIX) >>
+ IGP01E1000_PSSR_MDIX_SHIFT);
if ((phy_data & IGP01E1000_PSSR_SPEED_MASK) ==
IGP01E1000_PSSR_SPEED_1000MBPS) {
@@ -4096,10 +4191,12 @@ e1000_phy_igp_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
- phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
- SR_1000T_LOCAL_RX_STATUS_SHIFT;
- phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >>
- SR_1000T_REMOTE_RX_STATUS_SHIFT;
+ phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+ SR_1000T_LOCAL_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+ phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+ SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
/* Get cable length */
ret_val = e1000_get_cable_length(hw, &min_length, &max_length);
@@ -4135,7 +4232,8 @@ e1000_phy_ife_get_info(struct e1000_hw *hw,
struct e1000_phy_info *phy_info)
{
int32_t ret_val;
- uint16_t phy_data, polarity;
+ uint16_t phy_data;
+ e1000_rev_polarity polarity;
DEBUGFUNC("e1000_phy_ife_get_info");
@@ -4146,8 +4244,9 @@ e1000_phy_ife_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
phy_info->polarity_correction =
- (phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >>
- IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT;
+ ((phy_data & IFE_PSC_AUTO_POLARITY_DISABLE) >>
+ IFE_PSC_AUTO_POLARITY_DISABLE_SHIFT) ?
+ e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled;
if (phy_info->polarity_correction == e1000_polarity_reversal_enabled) {
ret_val = e1000_check_polarity(hw, &polarity);
@@ -4155,8 +4254,9 @@ e1000_phy_ife_get_info(struct e1000_hw *hw,
return ret_val;
} else {
/* Polarity is forced. */
- polarity = (phy_data & IFE_PSC_FORCE_POLARITY) >>
- IFE_PSC_FORCE_POLARITY_SHIFT;
+ polarity = ((phy_data & IFE_PSC_FORCE_POLARITY) >>
+ IFE_PSC_FORCE_POLARITY_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
}
phy_info->cable_polarity = polarity;
@@ -4164,9 +4264,9 @@ e1000_phy_ife_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
- phy_info->mdix_mode =
- (phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >>
- IFE_PMC_MDIX_MODE_SHIFT;
+ phy_info->mdix_mode = (e1000_auto_x_mode)
+ ((phy_data & (IFE_PMC_AUTO_MDIX | IFE_PMC_FORCE_MDIX)) >>
+ IFE_PMC_MDIX_MODE_SHIFT);
return E1000_SUCCESS;
}
@@ -4182,7 +4282,8 @@ e1000_phy_m88_get_info(struct e1000_hw *hw,
struct e1000_phy_info *phy_info)
{
int32_t ret_val;
- uint16_t phy_data, polarity;
+ uint16_t phy_data;
+ e1000_rev_polarity polarity;
DEBUGFUNC("e1000_phy_m88_get_info");
@@ -4195,11 +4296,14 @@ e1000_phy_m88_get_info(struct e1000_hw *hw,
return ret_val;
phy_info->extended_10bt_distance =
- (phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
- M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT;
+ ((phy_data & M88E1000_PSCR_10BT_EXT_DIST_ENABLE) >>
+ M88E1000_PSCR_10BT_EXT_DIST_ENABLE_SHIFT) ?
+ e1000_10bt_ext_dist_enable_lower : e1000_10bt_ext_dist_enable_normal;
+
phy_info->polarity_correction =
- (phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >>
- M88E1000_PSCR_POLARITY_REVERSAL_SHIFT;
+ ((phy_data & M88E1000_PSCR_POLARITY_REVERSAL) >>
+ M88E1000_PSCR_POLARITY_REVERSAL_SHIFT) ?
+ e1000_polarity_reversal_disabled : e1000_polarity_reversal_enabled;
/* Check polarity status */
ret_val = e1000_check_polarity(hw, &polarity);
@@ -4211,15 +4315,15 @@ e1000_phy_m88_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
- phy_info->mdix_mode = (phy_data & M88E1000_PSSR_MDIX) >>
- M88E1000_PSSR_MDIX_SHIFT;
+ phy_info->mdix_mode = (e1000_auto_x_mode)((phy_data & M88E1000_PSSR_MDIX) >>
+ M88E1000_PSSR_MDIX_SHIFT);
if ((phy_data & M88E1000_PSSR_SPEED) == M88E1000_PSSR_1000MBS) {
/* Cable Length Estimation and Local/Remote Receiver Information
* are only valid at 1000 Mbps.
*/
if (hw->phy_type != e1000_phy_gg82563) {
- phy_info->cable_length = ((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
+ phy_info->cable_length = (e1000_cable_length)((phy_data & M88E1000_PSSR_CABLE_LENGTH) >>
M88E1000_PSSR_CABLE_LENGTH_SHIFT);
} else {
ret_val = e1000_read_phy_reg(hw, GG82563_PHY_DSP_DISTANCE,
@@ -4227,18 +4331,20 @@ e1000_phy_m88_get_info(struct e1000_hw *hw,
if (ret_val)
return ret_val;
- phy_info->cable_length = phy_data & GG82563_DSPD_CABLE_LENGTH;
+ phy_info->cable_length = (e1000_cable_length)(phy_data & GG82563_DSPD_CABLE_LENGTH);
}
ret_val = e1000_read_phy_reg(hw, PHY_1000T_STATUS, &phy_data);
if (ret_val)
return ret_val;
- phy_info->local_rx = (phy_data & SR_1000T_LOCAL_RX_STATUS) >>
- SR_1000T_LOCAL_RX_STATUS_SHIFT;
+ phy_info->local_rx = ((phy_data & SR_1000T_LOCAL_RX_STATUS) >>
+ SR_1000T_LOCAL_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
+ phy_info->remote_rx = ((phy_data & SR_1000T_REMOTE_RX_STATUS) >>
+ SR_1000T_REMOTE_RX_STATUS_SHIFT) ?
+ e1000_1000t_rx_status_ok : e1000_1000t_rx_status_not_ok;
- phy_info->remote_rx = (phy_data & SR_1000T_REMOTE_RX_STATUS) >>
- SR_1000T_REMOTE_RX_STATUS_SHIFT;
}
return E1000_SUCCESS;
@@ -4441,7 +4547,7 @@ e1000_init_eeprom_params(struct e1000_hw *hw)
eeprom->use_eewr = FALSE;
break;
case e1000_ich8lan:
- {
+ {
int32_t i = 0;
uint32_t flash_size = E1000_READ_ICH8_REG(hw, ICH8_FLASH_GFPREG);
@@ -4468,7 +4574,7 @@ e1000_init_eeprom_params(struct e1000_hw *hw)
hw->flash_bank_size /= 2 * sizeof(uint16_t);
break;
- }
+ }
default:
break;
}
@@ -4800,7 +4906,7 @@ e1000_release_eeprom(struct e1000_hw *hw)
*
* hw - Struct containing variables accessed by shared code
*****************************************************************************/
-int32_t
+static int32_t
e1000_spi_eeprom_ready(struct e1000_hw *hw)
{
uint16_t retry_count = 0;
@@ -4854,44 +4960,43 @@ e1000_read_eeprom(struct e1000_hw *hw,
{
struct e1000_eeprom_info *eeprom = &hw->eeprom;
uint32_t i = 0;
- int32_t ret_val;
DEBUGFUNC("e1000_read_eeprom");
+ /* If eeprom is not yet detected, do so now */
+ if (eeprom->word_size == 0)
+ e1000_init_eeprom_params(hw);
+
/* A check for invalid values: offset too large, too many words, and not
* enough words.
*/
if ((offset >= eeprom->word_size) || (words > eeprom->word_size - offset) ||
(words == 0)) {
- DEBUGOUT("\"words\" parameter out of bounds\n");
+ DEBUGOUT2("\"words\" parameter out of bounds. Words = %d, size = %d\n", offset, eeprom->word_size);
return -E1000_ERR_EEPROM;
}
- /* FLASH reads without acquiring the semaphore are safe */
+ /* EEPROM's that don't use EERD to read require us to bit-bang the SPI
+ * directly. In this case, we need to acquire the EEPROM so that
+ * FW or other port software does not interrupt.
+ */
if (e1000_is_onboard_nvm_eeprom(hw) == TRUE &&
hw->eeprom.use_eerd == FALSE) {
- switch (hw->mac_type) {
- case e1000_80003es2lan:
- break;
- default:
- /* Prepare the EEPROM for reading */
- if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
- return -E1000_ERR_EEPROM;
- break;
- }
+ /* Prepare the EEPROM for bit-bang reading */
+ if (e1000_acquire_eeprom(hw) != E1000_SUCCESS)
+ return -E1000_ERR_EEPROM;
}
- if (eeprom->use_eerd == TRUE) {
- ret_val = e1000_read_eeprom_eerd(hw, offset, words, data);
- if ((e1000_is_onboard_nvm_eeprom(hw) == TRUE) ||
- (hw->mac_type != e1000_82573))
- e1000_release_eeprom(hw);
- return ret_val;
- }
+ /* Eerd register EEPROM access requires no eeprom aquire/release */
+ if (eeprom->use_eerd == TRUE)
+ return e1000_read_eeprom_eerd(hw, offset, words, data);
+ /* ICH EEPROM access is done via the ICH flash controller */
if (eeprom->type == e1000_eeprom_ich8)
return e1000_read_eeprom_ich8(hw, offset, words, data);
+ /* Set up the SPI or Microwire EEPROM for bit-bang reading. We have
+ * acquired the EEPROM at this point, so any returns should relase it */
if (eeprom->type == e1000_eeprom_spi) {
uint16_t word_in;
uint8_t read_opcode = EEPROM_READ_OPCODE_SPI;
@@ -5206,6 +5311,10 @@ e1000_write_eeprom(struct e1000_hw *hw,
DEBUGFUNC("e1000_write_eeprom");
+ /* If eeprom is not yet detected, do so now */
+ if (eeprom->word_size == 0)
+ e1000_init_eeprom_params(hw);
+
/* A check for invalid values: offset too large, too many words, and not
* enough words.
*/
@@ -5248,7 +5357,7 @@ e1000_write_eeprom(struct e1000_hw *hw,
* data - pointer to array of 8 bit words to be written to the EEPROM
*
*****************************************************************************/
-int32_t
+static int32_t
e1000_write_eeprom_spi(struct e1000_hw *hw,
uint16_t offset,
uint16_t words,
@@ -5314,7 +5423,7 @@ e1000_write_eeprom_spi(struct e1000_hw *hw,
* data - pointer to array of 16 bit words to be written to the EEPROM
*
*****************************************************************************/
-int32_t
+static int32_t
e1000_write_eeprom_microwire(struct e1000_hw *hw,
uint16_t offset,
uint16_t words,
@@ -5411,10 +5520,8 @@ e1000_commit_shadow_ram(struct e1000_hw *hw)
int32_t error = E1000_SUCCESS;
uint32_t old_bank_offset = 0;
uint32_t new_bank_offset = 0;
- uint32_t sector_retries = 0;
uint8_t low_byte = 0;
uint8_t high_byte = 0;
- uint8_t temp_byte = 0;
boolean_t sector_write_failed = FALSE;
if (hw->mac_type == e1000_82573) {
@@ -5467,41 +5574,46 @@ e1000_commit_shadow_ram(struct e1000_hw *hw)
e1000_erase_ich8_4k_segment(hw, 0);
}
- do {
- sector_write_failed = FALSE;
- /* Loop for every byte in the shadow RAM,
- * which is in units of words. */
- for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
- /* Determine whether to write the value stored
- * in the other NVM bank or a modified value stored
- * in the shadow RAM */
- if (hw->eeprom_shadow_ram[i].modified == TRUE) {
- low_byte = (uint8_t)hw->eeprom_shadow_ram[i].eeprom_word;
- e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset,
- &temp_byte);
- udelay(100);
- error = e1000_verify_write_ich8_byte(hw,
- (i << 1) + new_bank_offset,
- low_byte);
- if (error != E1000_SUCCESS)
- sector_write_failed = TRUE;
+ sector_write_failed = FALSE;
+ /* Loop for every byte in the shadow RAM,
+ * which is in units of words. */
+ for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+ /* Determine whether to write the value stored
+ * in the other NVM bank or a modified value stored
+ * in the shadow RAM */
+ if (hw->eeprom_shadow_ram[i].modified == TRUE) {
+ low_byte = (uint8_t)hw->eeprom_shadow_ram[i].eeprom_word;
+ udelay(100);
+ error = e1000_verify_write_ich8_byte(hw,
+ (i << 1) + new_bank_offset, low_byte);
+
+ if (error != E1000_SUCCESS)
+ sector_write_failed = TRUE;
+ else {
high_byte =
(uint8_t)(hw->eeprom_shadow_ram[i].eeprom_word >> 8);
- e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1,
- &temp_byte);
- udelay(100);
- } else {
- e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset,
- &low_byte);
udelay(100);
- error = e1000_verify_write_ich8_byte(hw,
- (i << 1) + new_bank_offset, low_byte);
- if (error != E1000_SUCCESS)
- sector_write_failed = TRUE;
+ }
+ } else {
+ e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset,
+ &low_byte);
+ udelay(100);
+ error = e1000_verify_write_ich8_byte(hw,
+ (i << 1) + new_bank_offset, low_byte);
+
+ if (error != E1000_SUCCESS)
+ sector_write_failed = TRUE;
+ else {
e1000_read_ich8_byte(hw, (i << 1) + old_bank_offset + 1,
&high_byte);
+ udelay(100);
}
+ }
+ /* If the write of the low byte was successful, go ahread and
+ * write the high byte while checking to make sure that if it
+ * is the signature byte, then it is handled properly */
+ if (sector_write_failed == FALSE) {
/* If the word is 0x13, then make sure the signature bits
* (15:14) are 11b until the commit has completed.
* This will allow us to write 10b which indicates the
@@ -5512,45 +5624,45 @@ e1000_commit_shadow_ram(struct e1000_hw *hw)
high_byte = E1000_ICH8_NVM_SIG_MASK | high_byte;
error = e1000_verify_write_ich8_byte(hw,
- (i << 1) + new_bank_offset + 1, high_byte);
+ (i << 1) + new_bank_offset + 1, high_byte);
if (error != E1000_SUCCESS)
sector_write_failed = TRUE;
- if (sector_write_failed == FALSE) {
- /* Clear the now not used entry in the cache */
- hw->eeprom_shadow_ram[i].modified = FALSE;
- hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF;
- }
+ } else {
+ /* If the write failed then break from the loop and
+ * return an error */
+ break;
}
+ }
- /* Don't bother writing the segment valid bits if sector
- * programming failed. */
- if (sector_write_failed == FALSE) {
- /* Finally validate the new segment by setting bit 15:14
- * to 10b in word 0x13 , this can be done without an
- * erase as well since these bits are 11 to start with
- * and we need to change bit 14 to 0b */
- e1000_read_ich8_byte(hw,
- E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset,
- &high_byte);
- high_byte &= 0xBF;
+ /* Don't bother writing the segment valid bits if sector
+ * programming failed. */
+ if (sector_write_failed == FALSE) {
+ /* Finally validate the new segment by setting bit 15:14
+ * to 10b in word 0x13 , this can be done without an
+ * erase as well since these bits are 11 to start with
+ * and we need to change bit 14 to 0b */
+ e1000_read_ich8_byte(hw,
+ E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset,
+ &high_byte);
+ high_byte &= 0xBF;
+ error = e1000_verify_write_ich8_byte(hw,
+ E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset, high_byte);
+ /* And invalidate the previously valid segment by setting
+ * its signature word (0x13) high_byte to 0b. This can be
+ * done without an erase because flash erase sets all bits
+ * to 1's. We can write 1's to 0's without an erase */
+ if (error == E1000_SUCCESS) {
error = e1000_verify_write_ich8_byte(hw,
- E1000_ICH8_NVM_SIG_WORD * 2 + 1 + new_bank_offset,
- high_byte);
- if (error != E1000_SUCCESS)
- sector_write_failed = TRUE;
+ E1000_ICH8_NVM_SIG_WORD * 2 + 1 + old_bank_offset, 0);
+ }
- /* And invalidate the previously valid segment by setting
- * its signature word (0x13) high_byte to 0b. This can be
- * done without an erase because flash erase sets all bits
- * to 1's. We can write 1's to 0's without an erase */
- error = e1000_verify_write_ich8_byte(hw,
- E1000_ICH8_NVM_SIG_WORD * 2 + 1 + old_bank_offset,
- 0);
- if (error != E1000_SUCCESS)
- sector_write_failed = TRUE;
+ /* Clear the now not used entry in the cache */
+ for (i = 0; i < E1000_SHADOW_RAM_WORDS; i++) {
+ hw->eeprom_shadow_ram[i].modified = FALSE;
+ hw->eeprom_shadow_ram[i].eeprom_word = 0xFFFF;
}
- } while (++sector_retries < 10 && sector_write_failed == TRUE);
+ }
}
return error;
@@ -5640,99 +5752,6 @@ e1000_init_rx_addrs(struct e1000_hw *hw)
}
/******************************************************************************
- * Updates the MAC's list of multicast addresses.
- *
- * hw - Struct containing variables accessed by shared code
- * mc_addr_list - the list of new multicast addresses
- * mc_addr_count - number of addresses
- * pad - number of bytes between addresses in the list
- * rar_used_count - offset where to start adding mc addresses into the RAR's
- *
- * The given list replaces any existing list. Clears the last 15 receive
- * address registers and the multicast table. Uses receive address registers
- * for the first 15 multicast addresses, and hashes the rest into the
- * multicast table.
- *****************************************************************************/
-#if 0
-void
-e1000_mc_addr_list_update(struct e1000_hw *hw,
- uint8_t *mc_addr_list,
- uint32_t mc_addr_count,
- uint32_t pad,
- uint32_t rar_used_count)
-{
- uint32_t hash_value;
- uint32_t i;
- uint32_t num_rar_entry;
- uint32_t num_mta_entry;
-
- DEBUGFUNC("e1000_mc_addr_list_update");
-
- /* Set the new number of MC addresses that we are being requested to use. */
- hw->num_mc_addrs = mc_addr_count;
-
- /* Clear RAR[1-15] */
- DEBUGOUT(" Clearing RAR[1-15]\n");
- num_rar_entry = E1000_RAR_ENTRIES;
- if (hw->mac_type == e1000_ich8lan)
- num_rar_entry = E1000_RAR_ENTRIES_ICH8LAN;
- /* Reserve a spot for the Locally Administered Address to work around
- * an 82571 issue in which a reset on one port will reload the MAC on
- * the other port. */
- if ((hw->mac_type == e1000_82571) && (hw->laa_is_present == TRUE))
- num_rar_entry -= 1;
-
- for (i = rar_used_count; i < num_rar_entry; i++) {
- E1000_WRITE_REG_ARRAY(hw, RA, (i << 1), 0);
- E1000_WRITE_FLUSH(hw);
- E1000_WRITE_REG_ARRAY(hw, RA, ((i << 1) + 1), 0);
- E1000_WRITE_FLUSH(hw);
- }
-
- /* Clear the MTA */
- DEBUGOUT(" Clearing MTA\n");
- num_mta_entry = E1000_NUM_MTA_REGISTERS;
- if (hw->mac_type == e1000_ich8lan)
- num_mta_entry = E1000_NUM_MTA_REGISTERS_ICH8LAN;
- for (i = 0; i < num_mta_entry; i++) {
- E1000_WRITE_REG_ARRAY(hw, MTA, i, 0);
- E1000_WRITE_FLUSH(hw);
- }
-
- /* Add the new addresses */
- for (i = 0; i < mc_addr_count; i++) {
- DEBUGOUT(" Adding the multicast addresses:\n");
- DEBUGOUT7(" MC Addr #%d =%.2X %.2X %.2X %.2X %.2X %.2X\n", i,
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad)],
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 1],
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 2],
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 3],
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 4],
- mc_addr_list[i * (ETH_LENGTH_OF_ADDRESS + pad) + 5]);
-
- hash_value = e1000_hash_mc_addr(hw,
- mc_addr_list +
- (i * (ETH_LENGTH_OF_ADDRESS + pad)));
-
- DEBUGOUT1(" Hash value = 0x%03X\n", hash_value);
-
- /* Place this multicast address in the RAR if there is room, *
- * else put it in the MTA
- */
- if (rar_used_count < num_rar_entry) {
- e1000_rar_set(hw,
- mc_addr_list + (i * (ETH_LENGTH_OF_ADDRESS + pad)),
- rar_used_count);
- rar_used_count++;
- } else {
- e1000_mta_set(hw, hash_value);
- }
- }
- DEBUGOUT("MC Update Complete\n");
-}
-#endif /* 0 */
-
-/******************************************************************************
* Hashes an address to determine its location in the multicast table
*
* hw - Struct containing variables accessed by shared code
@@ -6290,7 +6309,7 @@ e1000_led_off(struct e1000_hw *hw)
*
* hw - Struct containing variables accessed by shared code
*****************************************************************************/
-void
+static void
e1000_clear_hw_cntrs(struct e1000_hw *hw)
{
volatile uint32_t temp;
@@ -6539,6 +6558,8 @@ e1000_tbi_adjust_stats(struct e1000_hw *hw,
void
e1000_get_bus_info(struct e1000_hw *hw)
{
+ int32_t ret_val;
+ uint16_t pci_ex_link_status;
uint32_t status;
switch (hw->mac_type) {
@@ -6548,18 +6569,25 @@ e1000_get_bus_info(struct e1000_hw *hw)
hw->bus_speed = e1000_bus_speed_unknown;
hw->bus_width = e1000_bus_width_unknown;
break;
+ case e1000_82571:
case e1000_82572:
case e1000_82573:
+ case e1000_80003es2lan:
hw->bus_type = e1000_bus_type_pci_express;
hw->bus_speed = e1000_bus_speed_2500;
- hw->bus_width = e1000_bus_width_pciex_1;
+ ret_val = e1000_read_pcie_cap_reg(hw,
+ PCI_EX_LINK_STATUS,
+ &pci_ex_link_status);
+ if (ret_val)
+ hw->bus_width = e1000_bus_width_unknown;
+ else
+ hw->bus_width = (pci_ex_link_status & PCI_EX_LINK_WIDTH_MASK) >>
+ PCI_EX_LINK_WIDTH_SHIFT;
break;
- case e1000_82571:
case e1000_ich8lan:
- case e1000_80003es2lan:
hw->bus_type = e1000_bus_type_pci_express;
hw->bus_speed = e1000_bus_speed_2500;
- hw->bus_width = e1000_bus_width_pciex_4;
+ hw->bus_width = e1000_bus_width_pciex_1;
break;
default:
status = E1000_READ_REG(hw, STATUS);
@@ -6593,25 +6621,6 @@ e1000_get_bus_info(struct e1000_hw *hw)
break;
}
}
-/******************************************************************************
- * Reads a value from one of the devices registers using port I/O (as opposed
- * memory mapped I/O). Only 82544 and newer devices support port I/O.
- *
- * hw - Struct containing variables accessed by shared code
- * offset - offset to read from
- *****************************************************************************/
-#if 0
-uint32_t
-e1000_read_reg_io(struct e1000_hw *hw,
- uint32_t offset)
-{
- unsigned long io_addr = hw->io_base;
- unsigned long io_data = hw->io_base + 4;
-
- e1000_io_write(hw, io_addr, offset);
- return e1000_io_read(hw, io_data);
-}
-#endif /* 0 */
/******************************************************************************
* Writes a value to one of the devices registers using port I/O (as opposed to
@@ -6633,7 +6642,6 @@ e1000_write_reg_io(struct e1000_hw *hw,
e1000_io_write(hw, io_data, value);
}
-
/******************************************************************************
* Estimates the cable length.
*
@@ -6842,7 +6850,7 @@ e1000_get_cable_length(struct e1000_hw *hw,
*****************************************************************************/
static int32_t
e1000_check_polarity(struct e1000_hw *hw,
- uint16_t *polarity)
+ e1000_rev_polarity *polarity)
{
int32_t ret_val;
uint16_t phy_data;
@@ -6856,8 +6864,10 @@ e1000_check_polarity(struct e1000_hw *hw,
&phy_data);
if (ret_val)
return ret_val;
- *polarity = (phy_data & M88E1000_PSSR_REV_POLARITY) >>
- M88E1000_PSSR_REV_POLARITY_SHIFT;
+ *polarity = ((phy_data & M88E1000_PSSR_REV_POLARITY) >>
+ M88E1000_PSSR_REV_POLARITY_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
+
} else if (hw->phy_type == e1000_phy_igp ||
hw->phy_type == e1000_phy_igp_3 ||
hw->phy_type == e1000_phy_igp_2) {
@@ -6879,19 +6889,22 @@ e1000_check_polarity(struct e1000_hw *hw,
return ret_val;
/* Check the polarity bits */
- *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ? 1 : 0;
+ *polarity = (phy_data & IGP01E1000_PHY_POLARITY_MASK) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
} else {
/* For 10 Mbps, read the polarity bit in the status register. (for
* 100 Mbps this bit is always 0) */
- *polarity = phy_data & IGP01E1000_PSSR_POLARITY_REVERSED;
+ *polarity = (phy_data & IGP01E1000_PSSR_POLARITY_REVERSED) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
}
} else if (hw->phy_type == e1000_phy_ife) {
ret_val = e1000_read_phy_reg(hw, IFE_PHY_EXTENDED_STATUS_CONTROL,
&phy_data);
if (ret_val)
return ret_val;
- *polarity = (phy_data & IFE_PESC_POLARITY_REVERSED) >>
- IFE_PESC_POLARITY_REVERSED_SHIFT;
+ *polarity = ((phy_data & IFE_PESC_POLARITY_REVERSED) >>
+ IFE_PESC_POLARITY_REVERSED_SHIFT) ?
+ e1000_rev_polarity_reversed : e1000_rev_polarity_normal;
}
return E1000_SUCCESS;
}
@@ -7259,7 +7272,7 @@ e1000_set_d3_lplu_state(struct e1000_hw *hw,
} else if (hw->smart_speed == e1000_smart_speed_off) {
ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
&phy_data);
- if (ret_val)
+ if (ret_val)
return ret_val;
phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
@@ -7369,7 +7382,7 @@ e1000_set_d0_lplu_state(struct e1000_hw *hw,
} else if (hw->smart_speed == e1000_smart_speed_off) {
ret_val = e1000_read_phy_reg(hw, IGP01E1000_PHY_PORT_CONFIG,
&phy_data);
- if (ret_val)
+ if (ret_val)
return ret_val;
phy_data &= ~IGP01E1000_PSCFR_SMART_SPEED;
@@ -7475,7 +7488,7 @@ e1000_set_vco_speed(struct e1000_hw *hw)
*
* returns: - E1000_SUCCESS .
****************************************************************************/
-int32_t
+static int32_t
e1000_host_if_read_cookie(struct e1000_hw * hw, uint8_t *buffer)
{
uint8_t i;
@@ -7686,7 +7699,7 @@ e1000_check_mng_mode(struct e1000_hw *hw)
****************************************************************************/
int32_t
e1000_mng_write_dhcp_info(struct e1000_hw * hw, uint8_t *buffer,
- uint16_t length)
+ uint16_t length)
{
int32_t ret_val;
struct e1000_host_mng_command_header hdr;
@@ -7716,7 +7729,7 @@ e1000_mng_write_dhcp_info(struct e1000_hw * hw, uint8_t *buffer,
*
* returns - checksum of buffer contents.
****************************************************************************/
-uint8_t
+static uint8_t
e1000_calculate_mng_checksum(char *buffer, uint32_t length)
{
uint8_t sum = 0;
@@ -7914,32 +7927,6 @@ e1000_set_pci_express_master_disable(struct e1000_hw *hw)
E1000_WRITE_REG(hw, CTRL, ctrl);
}
-/***************************************************************************
- *
- * Enables PCI-Express master access.
- *
- * hw: Struct containing variables accessed by shared code
- *
- * returns: - none.
- *
- ***************************************************************************/
-#if 0
-void
-e1000_enable_pciex_master(struct e1000_hw *hw)
-{
- uint32_t ctrl;
-
- DEBUGFUNC("e1000_enable_pciex_master");
-
- if (hw->bus_type != e1000_bus_type_pci_express)
- return;
-
- ctrl = E1000_READ_REG(hw, CTRL);
- ctrl &= ~E1000_CTRL_GIO_MASTER_DISABLE;
- E1000_WRITE_REG(hw, CTRL, ctrl);
-}
-#endif /* 0 */
-
/*******************************************************************************
*
* Disables PCI-Express master access and verifies there are no pending requests
@@ -8063,7 +8050,6 @@ e1000_get_phy_cfg_done(struct e1000_hw *hw)
msleep(1);
timeout--;
}
-
if (!timeout) {
DEBUGOUT("MNG configuration cycle has not completed.\n");
return -E1000_ERR_RESET;
@@ -8172,8 +8158,9 @@ e1000_get_software_semaphore(struct e1000_hw *hw)
DEBUGFUNC("e1000_get_software_semaphore");
- if (hw->mac_type != e1000_80003es2lan)
+ if (hw->mac_type != e1000_80003es2lan) {
return E1000_SUCCESS;
+ }
while (timeout) {
swsm = E1000_READ_REG(hw, SWSM);
@@ -8206,8 +8193,9 @@ e1000_release_software_semaphore(struct e1000_hw *hw)
DEBUGFUNC("e1000_release_software_semaphore");
- if (hw->mac_type != e1000_80003es2lan)
+ if (hw->mac_type != e1000_80003es2lan) {
return;
+ }
swsm = E1000_READ_REG(hw, SWSM);
/* Release the SW semaphores.*/
@@ -8241,7 +8229,7 @@ e1000_check_phy_reset_block(struct e1000_hw *hw)
if (hw->mac_type > e1000_82547_rev_2)
manc = E1000_READ_REG(hw, MANC);
return (manc & E1000_MANC_BLK_PHY_RST_ON_IDE) ?
- E1000_BLK_PHY_RESET : E1000_SUCCESS;
+ E1000_BLK_PHY_RESET : E1000_SUCCESS;
}
static uint8_t
@@ -8377,66 +8365,6 @@ e1000_release_software_flag(struct e1000_hw *hw)
return;
}
-/***************************************************************************
- *
- * Disable dynamic power down mode in ife PHY.
- * It can be used to workaround band-gap problem.
- *
- * hw: Struct containing variables accessed by shared code
- *
- ***************************************************************************/
-#if 0
-int32_t
-e1000_ife_disable_dynamic_power_down(struct e1000_hw *hw)
-{
- uint16_t phy_data;
- int32_t ret_val = E1000_SUCCESS;
-
- DEBUGFUNC("e1000_ife_disable_dynamic_power_down");
-
- if (hw->phy_type == e1000_phy_ife) {
- ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data);
- if (ret_val)
- return ret_val;
-
- phy_data |= IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN;
- ret_val = e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, phy_data);
- }
-
- return ret_val;
-}
-#endif /* 0 */
-
-/***************************************************************************
- *
- * Enable dynamic power down mode in ife PHY.
- * It can be used to workaround band-gap problem.
- *
- * hw: Struct containing variables accessed by shared code
- *
- ***************************************************************************/
-#if 0
-int32_t
-e1000_ife_enable_dynamic_power_down(struct e1000_hw *hw)
-{
- uint16_t phy_data;
- int32_t ret_val = E1000_SUCCESS;
-
- DEBUGFUNC("e1000_ife_enable_dynamic_power_down");
-
- if (hw->phy_type == e1000_phy_ife) {
- ret_val = e1000_read_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, &phy_data);
- if (ret_val)
- return ret_val;
-
- phy_data &= ~IFE_PSC_DISABLE_DYNAMIC_POWER_DOWN;
- ret_val = e1000_write_phy_reg(hw, IFE_PHY_SPECIAL_CONTROL, phy_data);
- }
-
- return ret_val;
-}
-#endif /* 0 */
-
/******************************************************************************
* Reads a 16 bit word or words from the EEPROM using the ICH8's flash access
* register.
@@ -8832,20 +8760,22 @@ static int32_t
e1000_verify_write_ich8_byte(struct e1000_hw *hw, uint32_t index, uint8_t byte)
{
int32_t error = E1000_SUCCESS;
- int32_t program_retries;
- uint8_t temp_byte;
+ int32_t program_retries = 0;
- e1000_write_ich8_byte(hw, index, byte);
- udelay(100);
+ DEBUGOUT2("Byte := %2.2X Offset := %d\n", byte, index);
- for (program_retries = 0; program_retries < 100; program_retries++) {
- e1000_read_ich8_byte(hw, index, &temp_byte);
- if (temp_byte == byte)
- break;
- udelay(10);
- e1000_write_ich8_byte(hw, index, byte);
- udelay(100);
+ error = e1000_write_ich8_byte(hw, index, byte);
+
+ if (error != E1000_SUCCESS) {
+ for (program_retries = 0; program_retries < 100; program_retries++) {
+ DEBUGOUT2("Retrying \t Byte := %2.2X Offset := %d\n", byte, index);
+ error = e1000_write_ich8_byte(hw, index, byte);
+ udelay(100);
+ if (error == E1000_SUCCESS)
+ break;
+ }
}
+
if (program_retries == 100)
error = E1000_ERR_EEPROM;
@@ -8886,39 +8816,27 @@ e1000_read_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t *data)
}
/******************************************************************************
- * Writes a word to the NVM using the ICH8 flash access registers.
+ * Erases the bank specified. Each bank may be a 4, 8 or 64k block. Banks are 0
+ * based.
*
* hw - pointer to e1000_hw structure
- * index - The starting byte index of the word to read.
- * data - The word to write to the NVM.
- *****************************************************************************/
-#if 0
-int32_t
-e1000_write_ich8_word(struct e1000_hw *hw, uint32_t index, uint16_t data)
-{
- int32_t status = E1000_SUCCESS;
- status = e1000_write_ich8_data(hw, index, 2, data);
- return status;
-}
-#endif /* 0 */
-
-/******************************************************************************
- * Erases the bank specified. Each bank is a 4k block. Segments are 0 based.
- * segment N is 4096 * N + flash_reg_addr.
+ * bank - 0 for first bank, 1 for second bank
*
- * hw - pointer to e1000_hw structure
- * segment - 0 for first segment, 1 for second segment, etc.
+ * Note that this function may actually erase as much as 8 or 64 KBytes. The
+ * amount of NVM used in each bank is a *minimum* of 4 KBytes, but in fact the
+ * bank size may be 4, 8 or 64 KBytes
*****************************************************************************/
-static int32_t
-e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment)
+int32_t
+e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t bank)
{
union ich8_hws_flash_status hsfsts;
union ich8_hws_flash_ctrl hsflctl;
uint32_t flash_linear_address;
int32_t count = 0;
int32_t error = E1000_ERR_EEPROM;
- int32_t iteration, seg_size;
- int32_t sector_size;
+ int32_t iteration;
+ int32_t sub_sector_size = 0;
+ int32_t bank_size;
int32_t j = 0;
int32_t error_flag = 0;
@@ -8927,22 +8845,27 @@ e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment)
/* Determine HW Sector size: Read BERASE bits of Hw flash Status register */
/* 00: The Hw sector is 256 bytes, hence we need to erase 16
* consecutive sectors. The start index for the nth Hw sector can be
- * calculated as = segment * 4096 + n * 256
+ * calculated as bank * 4096 + n * 256
* 01: The Hw sector is 4K bytes, hence we need to erase 1 sector.
* The start index for the nth Hw sector can be calculated
- * as = segment * 4096
- * 10: Error condition
- * 11: The Hw sector size is much bigger than the size asked to
- * erase...error condition */
+ * as bank * 4096
+ * 10: The HW sector is 8K bytes
+ * 11: The Hw sector size is 64K bytes */
if (hsfsts.hsf_status.berasesz == 0x0) {
/* Hw sector size 256 */
- sector_size = seg_size = ICH8_FLASH_SEG_SIZE_256;
+ sub_sector_size = ICH8_FLASH_SEG_SIZE_256;
+ bank_size = ICH8_FLASH_SECTOR_SIZE;
iteration = ICH8_FLASH_SECTOR_SIZE / ICH8_FLASH_SEG_SIZE_256;
} else if (hsfsts.hsf_status.berasesz == 0x1) {
- sector_size = seg_size = ICH8_FLASH_SEG_SIZE_4K;
+ bank_size = ICH8_FLASH_SEG_SIZE_4K;
+ iteration = 1;
+ } else if (hw->mac_type != e1000_ich8lan &&
+ hsfsts.hsf_status.berasesz == 0x2) {
+ /* 8K erase size invalid for ICH8 - added in for ICH9 */
+ bank_size = ICH9_FLASH_SEG_SIZE_8K;
iteration = 1;
} else if (hsfsts.hsf_status.berasesz == 0x3) {
- sector_size = seg_size = ICH8_FLASH_SEG_SIZE_64K;
+ bank_size = ICH8_FLASH_SEG_SIZE_64K;
iteration = 1;
} else {
return error;
@@ -8966,16 +8889,15 @@ e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment)
/* Write the last 24 bits of an index within the block into Flash
* Linear address field in Flash Address. This probably needs to
- * be calculated here based off the on-chip segment size and the
- * software segment size assumed (4K) */
- /* TBD */
- flash_linear_address = segment * sector_size + j * seg_size;
- flash_linear_address &= ICH8_FLASH_LINEAR_ADDR_MASK;
+ * be calculated here based off the on-chip erase sector size and
+ * the software bank size (4, 8 or 64 KBytes) */
+ flash_linear_address = bank * bank_size + j * sub_sector_size;
flash_linear_address += hw->flash_base_addr;
+ flash_linear_address &= ICH8_FLASH_LINEAR_ADDR_MASK;
E1000_WRITE_ICH8_REG(hw, ICH8_FLASH_FADDR, flash_linear_address);
- error = e1000_ich8_flash_cycle(hw, 1000000);
+ error = e1000_ich8_flash_cycle(hw, ICH8_FLASH_ERASE_TIMEOUT);
/* Check if FCERR is set to 1. If 1, clear it and try the whole
* sequence a few more times else Done */
if (error == E1000_SUCCESS) {
@@ -8999,44 +8921,6 @@ e1000_erase_ich8_4k_segment(struct e1000_hw *hw, uint32_t segment)
return error;
}
-/******************************************************************************
- *
- * Reverse duplex setting without breaking the link.
- *
- * hw: Struct containing variables accessed by shared code
- *
- *****************************************************************************/
-#if 0
-int32_t
-e1000_duplex_reversal(struct e1000_hw *hw)
-{
- int32_t ret_val;
- uint16_t phy_data;
-
- if (hw->phy_type != e1000_phy_igp_3)
- return E1000_SUCCESS;
-
- ret_val = e1000_read_phy_reg(hw, PHY_CTRL, &phy_data);
- if (ret_val)
- return ret_val;
-
- phy_data ^= MII_CR_FULL_DUPLEX;
-
- ret_val = e1000_write_phy_reg(hw, PHY_CTRL, phy_data);
- if (ret_val)
- return ret_val;
-
- ret_val = e1000_read_phy_reg(hw, IGP3E1000_PHY_MISC_CTRL, &phy_data);
- if (ret_val)
- return ret_val;
-
- phy_data |= IGP3_PHY_MISC_DUPLEX_MANUAL_SET;
- ret_val = e1000_write_phy_reg(hw, IGP3E1000_PHY_MISC_CTRL, phy_data);
-
- return ret_val;
-}
-#endif /* 0 */
-
static int32_t
e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw,
uint32_t cnf_base_addr, uint32_t cnf_size)
@@ -9071,6 +8955,14 @@ e1000_init_lcd_from_nvm_config_region(struct e1000_hw *hw,
}
+/******************************************************************************
+ * This function initializes the PHY from the NVM on ICH8 platforms. This
+ * is needed due to an issue where the NVM configuration is not properly
+ * autoloaded after power transitions. Therefore, after each PHY reset, we
+ * will load the configuration data out of the NVM manually.
+ *
+ * hw: Struct containing variables accessed by shared code
+ *****************************************************************************/
static int32_t
e1000_init_lcd_from_nvm(struct e1000_hw *hw)
{
diff --git a/drivers/net/e1000/e1000_hw.h b/drivers/net/e1000/e1000_hw.h
index a170e96251f..112447fd8bf 100644
--- a/drivers/net/e1000/e1000_hw.h
+++ b/drivers/net/e1000/e1000_hw.h
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -93,11 +92,11 @@ typedef enum {
/* Flow Control Settings */
typedef enum {
- e1000_fc_none = 0,
- e1000_fc_rx_pause = 1,
- e1000_fc_tx_pause = 2,
- e1000_fc_full = 3,
- e1000_fc_default = 0xFF
+ E1000_FC_NONE = 0,
+ E1000_FC_RX_PAUSE = 1,
+ E1000_FC_TX_PAUSE = 2,
+ E1000_FC_FULL = 3,
+ E1000_FC_DEFAULT = 0xFF
} e1000_fc_type;
struct e1000_shadow_ram {
@@ -302,6 +301,9 @@ typedef enum {
#define E1000_BLK_PHY_RESET 12
#define E1000_ERR_SWFW_SYNC 13
+#define E1000_BYTE_SWAP_WORD(_value) ((((_value) & 0x00ff) << 8) | \
+ (((_value) & 0xff00) >> 8))
+
/* Function prototypes */
/* Initialization */
int32_t e1000_reset_hw(struct e1000_hw *hw);
@@ -314,7 +316,7 @@ int32_t e1000_setup_link(struct e1000_hw *hw);
int32_t e1000_phy_setup_autoneg(struct e1000_hw *hw);
void e1000_config_collision_dist(struct e1000_hw *hw);
int32_t e1000_check_for_link(struct e1000_hw *hw);
-int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t * speed, uint16_t * duplex);
+int32_t e1000_get_speed_and_duplex(struct e1000_hw *hw, uint16_t *speed, uint16_t *duplex);
int32_t e1000_force_mac_fc(struct e1000_hw *hw);
/* PHY */
@@ -322,9 +324,9 @@ int32_t e1000_read_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t *phy
int32_t e1000_write_phy_reg(struct e1000_hw *hw, uint32_t reg_addr, uint16_t data);
int32_t e1000_phy_hw_reset(struct e1000_hw *hw);
int32_t e1000_phy_reset(struct e1000_hw *hw);
-void e1000_phy_powerdown_workaround(struct e1000_hw *hw);
int32_t e1000_phy_get_info(struct e1000_hw *hw, struct e1000_phy_info *phy_info);
int32_t e1000_validate_mdi_setting(struct e1000_hw *hw);
+void e1000_phy_powerdown_workaround(struct e1000_hw *hw);
/* EEPROM Functions */
int32_t e1000_init_eeprom_params(struct e1000_hw *hw);
@@ -393,7 +395,6 @@ int32_t e1000_read_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uin
int32_t e1000_validate_eeprom_checksum(struct e1000_hw *hw);
int32_t e1000_update_eeprom_checksum(struct e1000_hw *hw);
int32_t e1000_write_eeprom(struct e1000_hw *hw, uint16_t reg, uint16_t words, uint16_t *data);
-int32_t e1000_read_part_num(struct e1000_hw *hw, uint32_t * part_num);
int32_t e1000_read_mac_addr(struct e1000_hw * hw);
/* Filters (multicast, vlan, receive) */
@@ -420,6 +421,7 @@ void e1000_pci_set_mwi(struct e1000_hw *hw);
void e1000_pci_clear_mwi(struct e1000_hw *hw);
void e1000_read_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
void e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t * value);
+int32_t e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value);
/* Port I/O is only supported on 82544 and newer */
void e1000_io_write(struct e1000_hw *hw, unsigned long port, uint32_t value);
int32_t e1000_disable_pciex_master(struct e1000_hw *hw);
@@ -574,10 +576,10 @@ int32_t e1000_check_phy_reset_block(struct e1000_hw *hw);
* E1000_RAR_ENTRIES - 1 multicast addresses.
*/
#define E1000_RAR_ENTRIES 15
-#define E1000_RAR_ENTRIES_ICH8LAN 7
+#define E1000_RAR_ENTRIES_ICH8LAN 6
-#define MIN_NUMBER_OF_DESCRIPTORS 8
-#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8
+#define MIN_NUMBER_OF_DESCRIPTORS 8
+#define MAX_NUMBER_OF_DESCRIPTORS 0xFFF8
/* Receive Descriptor */
struct e1000_rx_desc {
@@ -1300,6 +1302,7 @@ struct e1000_hw_stats {
uint64_t algnerrc;
uint64_t symerrs;
uint64_t rxerrc;
+ uint64_t txerrc;
uint64_t mpc;
uint64_t scc;
uint64_t ecol;
@@ -1332,8 +1335,9 @@ struct e1000_hw_stats {
uint64_t gotch;
uint64_t rnbc;
uint64_t ruc;
- uint64_t rfc;
uint64_t roc;
+ uint64_t rlerrc;
+ uint64_t rfc;
uint64_t rjc;
uint64_t mgprc;
uint64_t mgpdc;
@@ -1367,8 +1371,8 @@ struct e1000_hw_stats {
/* Structure containing variables used by the shared code (e1000_hw.c) */
struct e1000_hw {
- uint8_t *hw_addr;
- uint8_t *flash_address;
+ uint8_t __iomem *hw_addr;
+ uint8_t __iomem *flash_address;
e1000_mac_type mac_type;
e1000_phy_type phy_type;
uint32_t phy_init_script;
@@ -1440,6 +1444,7 @@ struct e1000_hw {
boolean_t tbi_compatibility_on;
boolean_t laa_is_present;
boolean_t phy_reset_disable;
+ boolean_t initialize_hw_bits_disable;
boolean_t fc_send_xon;
boolean_t fc_strict_ieee;
boolean_t report_tx_early;
@@ -1613,16 +1618,17 @@ struct e1000_hw {
#define E1000_CTRL_EXT_LINK_MODE_MASK 0x00C00000
#define E1000_CTRL_EXT_LINK_MODE_GMII 0x00000000
#define E1000_CTRL_EXT_LINK_MODE_TBI 0x00C00000
-#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000
+#define E1000_CTRL_EXT_LINK_MODE_KMRN 0x00000000
#define E1000_CTRL_EXT_LINK_MODE_SERDES 0x00C00000
+#define E1000_CTRL_EXT_LINK_MODE_SGMII 0x00800000
#define E1000_CTRL_EXT_WR_WMARK_MASK 0x03000000
#define E1000_CTRL_EXT_WR_WMARK_256 0x00000000
#define E1000_CTRL_EXT_WR_WMARK_320 0x01000000
#define E1000_CTRL_EXT_WR_WMARK_384 0x02000000
#define E1000_CTRL_EXT_WR_WMARK_448 0x03000000
-#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */
-#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */
-#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */
+#define E1000_CTRL_EXT_DRV_LOAD 0x10000000 /* Driver loaded bit for FW */
+#define E1000_CTRL_EXT_IAME 0x08000000 /* Interrupt acknowledge Auto-mask */
+#define E1000_CTRL_EXT_INT_TIMER_CLR 0x20000000 /* Clear Interrupt timers after IMS clear */
#define E1000_CRTL_EXT_PB_PAREN 0x01000000 /* packet buffer parity error detection enabled */
#define E1000_CTRL_EXT_DF_PAREN 0x02000000 /* descriptor FIFO parity error detection enable */
#define E1000_CTRL_EXT_GHOST_PAREN 0x40000000
@@ -2218,6 +2224,11 @@ struct e1000_host_command_info {
#define E1000_FACTPS_LAN_FUNC_SEL 0x40000000
#define E1000_FACTPS_PM_STATE_CHANGED 0x80000000
+/* PCI-Ex Config Space */
+#define PCI_EX_LINK_STATUS 0x12
+#define PCI_EX_LINK_WIDTH_MASK 0x3F0
+#define PCI_EX_LINK_WIDTH_SHIFT 4
+
/* EEPROM Commands - Microwire */
#define EEPROM_READ_OPCODE_MICROWIRE 0x6 /* EEPROM read opcode */
#define EEPROM_WRITE_OPCODE_MICROWIRE 0x5 /* EEPROM write opcode */
@@ -3120,6 +3131,7 @@ struct e1000_host_command_info {
/* I = Integrated
* E = External
*/
+#define M88_VENDOR 0x0141
#define M88E1000_E_PHY_ID 0x01410C50
#define M88E1000_I_PHY_ID 0x01410C30
#define M88E1011_I_PHY_ID 0x01410C20
@@ -3244,10 +3256,12 @@ struct e1000_host_command_info {
#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */
#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */
-#define ICH8_FLASH_COMMAND_TIMEOUT 500 /* 500 ms , should be adjusted */
-#define ICH8_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles , should be adjusted */
+#define ICH8_FLASH_COMMAND_TIMEOUT 5000 /* 5000 uSecs - adjusted */
+#define ICH8_FLASH_ERASE_TIMEOUT 3000000 /* Up to 3 seconds - worst case */
+#define ICH8_FLASH_CYCLE_REPEAT_COUNT 10 /* 10 cycles */
#define ICH8_FLASH_SEG_SIZE_256 256
#define ICH8_FLASH_SEG_SIZE_4K 4096
+#define ICH9_FLASH_SEG_SIZE_8K 8192
#define ICH8_FLASH_SEG_SIZE_64K 65536
#define ICH8_CYCLE_READ 0x0
diff --git a/drivers/net/e1000/e1000_main.c b/drivers/net/e1000/e1000_main.c
index 3f6a752700a..7dca38fba6a 100644
--- a/drivers/net/e1000/e1000_main.c
+++ b/drivers/net/e1000/e1000_main.c
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -36,7 +35,7 @@ static char e1000_driver_string[] = "Intel(R) PRO/1000 Network Driver";
#else
#define DRIVERNAPI "-NAPI"
#endif
-#define DRV_VERSION "7.2.7-k2"DRIVERNAPI
+#define DRV_VERSION "7.2.9-k2"DRIVERNAPI
char e1000_driver_version[] = DRV_VERSION;
static char e1000_copyright[] = "Copyright (c) 1999-2006 Intel Corporation.";
@@ -110,16 +109,24 @@ static struct pci_device_id e1000_pci_tbl[] = {
MODULE_DEVICE_TABLE(pci, e1000_pci_tbl);
+int e1000_up(struct e1000_adapter *adapter);
+void e1000_down(struct e1000_adapter *adapter);
+void e1000_reinit_locked(struct e1000_adapter *adapter);
+void e1000_reset(struct e1000_adapter *adapter);
+int e1000_set_spd_dplx(struct e1000_adapter *adapter, uint16_t spddplx);
+int e1000_setup_all_tx_resources(struct e1000_adapter *adapter);
+int e1000_setup_all_rx_resources(struct e1000_adapter *adapter);
+void e1000_free_all_tx_resources(struct e1000_adapter *adapter);
+void e1000_free_all_rx_resources(struct e1000_adapter *adapter);
static int e1000_setup_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *txdr);
+ struct e1000_tx_ring *txdr);
static int e1000_setup_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rxdr);
+ struct e1000_rx_ring *rxdr);
static void e1000_free_tx_resources(struct e1000_adapter *adapter,
- struct e1000_tx_ring *tx_ring);
+ struct e1000_tx_ring *tx_ring);
static void e1000_free_rx_resources(struct e1000_adapter *adapter,
- struct e1000_rx_ring *rx_ring);
-
-/* Local Function Prototypes */
+ struct e1000_rx_ring *rx_ring);
+void e1000_update_stats(struct e1000_adapter *adapter);
static int e1000_init_module(void);
static void e1000_exit_module(void);
@@ -172,6 +179,7 @@ static void e1000_alloc_rx_buffers_ps(struct e1000_adapter *adapter,
static int e1000_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd);
static int e1000_mii_ioctl(struct net_device *netdev, struct ifreq *ifr,
int cmd);
+void e1000_set_ethtool_ops(struct net_device *netdev);
static void e1000_enter_82542_rst(struct e1000_adapter *adapter);
static void e1000_leave_82542_rst(struct e1000_adapter *adapter);
static void e1000_tx_timeout(struct net_device *dev);
@@ -196,6 +204,8 @@ static void e1000_shutdown(struct pci_dev *pdev);
static void e1000_netpoll (struct net_device *netdev);
#endif
+extern void e1000_check_options(struct e1000_adapter *adapter);
+
static pci_ers_result_t e1000_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state);
static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev);
@@ -212,9 +222,9 @@ static struct pci_driver e1000_driver = {
.id_table = e1000_pci_tbl,
.probe = e1000_probe,
.remove = __devexit_p(e1000_remove),
+#ifdef CONFIG_PM
/* Power Managment Hooks */
.suspend = e1000_suspend,
-#ifdef CONFIG_PM
.resume = e1000_resume,
#endif
.shutdown = e1000_shutdown,
@@ -466,13 +476,14 @@ e1000_up(struct e1000_adapter *adapter)
adapter->tx_queue_len = netdev->tx_queue_len;
- mod_timer(&adapter->watchdog_timer, jiffies);
-
#ifdef CONFIG_E1000_NAPI
netif_poll_enable(netdev);
#endif
e1000_irq_enable(adapter);
+ clear_bit(__E1000_DOWN, &adapter->flags);
+
+ mod_timer(&adapter->watchdog_timer, jiffies + 2 * HZ);
return 0;
}
@@ -502,25 +513,48 @@ void e1000_power_up_phy(struct e1000_adapter *adapter)
static void e1000_power_down_phy(struct e1000_adapter *adapter)
{
- boolean_t mng_mode_enabled = (adapter->hw.mac_type >= e1000_82571) &&
- e1000_check_mng_mode(&adapter->hw);
- /* Power down the PHY so no link is implied when interface is down
- * The PHY cannot be powered down if any of the following is TRUE
+ /* Power down the PHY so no link is implied when interface is down *
+ * The PHY cannot be powered down if any of the following is TRUE *
* (a) WoL is enabled
* (b) AMT is active
* (c) SoL/IDER session is active */
if (!adapter->wol && adapter->hw.mac_type >= e1000_82540 &&
- adapter->hw.mac_type != e1000_ich8lan &&
- adapter->hw.media_type == e1000_media_type_copper &&
- !(E1000_READ_REG(&adapter->hw, MANC) & E1000_MANC_SMBUS_EN) &&
- !mng_mode_enabled &&
- !e1000_check_phy_reset_block(&adapter->hw)) {
+ adapter->hw.media_type == e1000_media_type_copper) {
uint16_t mii_reg = 0;
+
+ switch (adapter->hw.mac_type) {
+ case e1000_82540:
+ case e1000_82545:
+ case e1000_82545_rev_3:
+ case e1000_82546:
+ case e1000_82546_rev_3:
+ case e1000_82541:
+ case e1000_82541_rev_2:
+ case e1000_82547:
+ case e1000_82547_rev_2:
+ if (E1000_READ_REG(&adapter->hw, MANC) &
+ E1000_MANC_SMBUS_EN)
+ goto out;
+ break;
+ case e1000_82571:
+ case e1000_82572:
+ case e1000_82573:
+ case e1000_80003es2lan:
+ case e1000_ich8lan:
+ if (e1000_check_mng_mode(&adapter->hw) ||
+ e1000_check_phy_reset_block(&adapter->hw))
+ goto out;
+ break;
+ default:
+ goto out;
+ }
e1000_read_phy_reg(&adapter->hw, PHY_CTRL, &mii_reg);
mii_reg |= MII_CR_POWER_DOWN;
e1000_write_phy_reg(&adapter->hw, PHY_CTRL, mii_reg);
mdelay(1);
}
+out:
+ return;
}
void
@@ -528,6 +562,10 @@ e1000_down(struct e1000_adapter *adapter)
{
struct net_device *netdev = adapter->netdev;
+ /* signal that we're down so the interrupt handler does not
+ * reschedule our watchdog timer */
+ set_bit(__E1000_DOWN, &adapter->flags);
+
e1000_irq_disable(adapter);
del_timer_sync(&adapter->tx_fifo_stall_timer);
@@ -563,6 +601,9 @@ void
e1000_reset(struct e1000_adapter *adapter)
{
uint32_t pba, manc;
+#ifdef DISABLE_MULR
+ uint32_t tctl;
+#endif
uint16_t fc_high_water_mark = E1000_FC_HIGH_DIFF;
/* Repartition Pba for greater than 9k mtu
@@ -629,6 +670,12 @@ e1000_reset(struct e1000_adapter *adapter)
e1000_reset_hw(&adapter->hw);
if (adapter->hw.mac_type >= e1000_82544)
E1000_WRITE_REG(&adapter->hw, WUC, 0);
+#ifdef DISABLE_MULR
+ /* disable Multiple Reads in Transmit Control Register for debugging */
+ tctl = E1000_READ_REG(hw, TCTL);
+ E1000_WRITE_REG(hw, TCTL, tctl & ~E1000_TCTL_MULR);
+
+#endif
if (e1000_init_hw(&adapter->hw))
DPRINTK(PROBE, ERR, "Hardware Error\n");
e1000_update_mng_vlan(adapter);
@@ -652,9 +699,7 @@ e1000_reset(struct e1000_adapter *adapter)
phy_data);
}
- if (adapter->hw.mac_type < e1000_ich8lan)
- /* FIXME: this code is duplicate and wrong for PCI Express */
- if (adapter->en_mng_pt) {
+ if ((adapter->en_mng_pt) && (adapter->hw.mac_type < e1000_82571)) {
manc = E1000_READ_REG(&adapter->hw, MANC);
manc |= (E1000_MANC_ARP_EN | E1000_MANC_EN_MNG2HOST);
E1000_WRITE_REG(&adapter->hw, MANC, manc);
@@ -760,7 +805,7 @@ e1000_probe(struct pci_dev *pdev,
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = e1000_netpoll;
#endif
- strcpy(netdev->name, pci_name(pdev));
+ strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len;
@@ -863,11 +908,6 @@ e1000_probe(struct pci_dev *pdev,
INIT_WORK(&adapter->reset_task,
(void (*)(void *))e1000_reset_task, netdev);
- /* we're going to reset, so assume we have no link for now */
-
- netif_carrier_off(netdev);
- netif_stop_queue(netdev);
-
e1000_check_options(adapter);
/* Initial Wake on LAN setting
@@ -974,6 +1014,10 @@ e1000_probe(struct pci_dev *pdev,
if ((err = register_netdev(netdev)))
goto err_register;
+ /* tell the stack to leave us alone until e1000_open() is called */
+ netif_carrier_off(netdev);
+ netif_stop_queue(netdev);
+
DPRINTK(PROBE, INFO, "Intel(R) PRO/1000 Network Connection\n");
cards_found++;
@@ -1032,8 +1076,7 @@ e1000_remove(struct pci_dev *pdev)
flush_scheduled_work();
- if (adapter->hw.mac_type >= e1000_82540 &&
- adapter->hw.mac_type != e1000_ich8lan &&
+ if (adapter->hw.mac_type < e1000_82571 &&
adapter->hw.media_type == e1000_media_type_copper) {
manc = E1000_READ_REG(&adapter->hw, MANC);
if (manc & E1000_MANC_SMBUS_EN) {
@@ -1161,6 +1204,8 @@ e1000_sw_init(struct e1000_adapter *adapter)
atomic_set(&adapter->irq_sem, 1);
spin_lock_init(&adapter->stats_lock);
+ set_bit(__E1000_DOWN, &adapter->flags);
+
return 0;
}
@@ -1226,7 +1271,7 @@ e1000_open(struct net_device *netdev)
int err;
/* disallow open during test */
- if (test_bit(__E1000_DRIVER_TESTING, &adapter->flags))
+ if (test_bit(__E1000_TESTING, &adapter->flags))
return -EBUSY;
/* allocate transmit descriptors */
@@ -1299,8 +1344,12 @@ e1000_close(struct net_device *netdev)
e1000_free_all_tx_resources(adapter);
e1000_free_all_rx_resources(adapter);
+ /* kill manageability vlan ID if supported, but not if a vlan with
+ * the same ID is registered on the host OS (let 8021q kill it) */
if ((adapter->hw.mng_cookie.status &
- E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT)) {
+ E1000_MNG_DHCP_COOKIE_STATUS_VLAN_SUPPORT) &&
+ !(adapter->vlgrp &&
+ adapter->vlgrp->vlan_devices[adapter->mng_vlan_id])) {
e1000_vlan_rx_kill_vid(netdev, adapter->mng_vlan_id);
}
@@ -1510,27 +1559,14 @@ e1000_configure_tx(struct e1000_adapter *adapter)
/* Program the Transmit Control Register */
tctl = E1000_READ_REG(hw, TCTL);
-
tctl &= ~E1000_TCTL_CT;
tctl |= E1000_TCTL_PSP | E1000_TCTL_RTLC |
(E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT);
-#ifdef DISABLE_MULR
- /* disable Multiple Reads for debugging */
- tctl &= ~E1000_TCTL_MULR;
-#endif
-
if (hw->mac_type == e1000_82571 || hw->mac_type == e1000_82572) {
tarc = E1000_READ_REG(hw, TARC0);
- tarc |= ((1 << 25) | (1 << 21));
+ tarc |= (1 << 21);
E1000_WRITE_REG(hw, TARC0, tarc);
- tarc = E1000_READ_REG(hw, TARC1);
- tarc |= (1 << 25);
- if (tctl & E1000_TCTL_MULR)
- tarc &= ~(1 << 28);
- else
- tarc |= (1 << 28);
- E1000_WRITE_REG(hw, TARC1, tarc);
} else if (hw->mac_type == e1000_80003es2lan) {
tarc = E1000_READ_REG(hw, TARC0);
tarc |= 1;
@@ -2892,6 +2928,35 @@ e1000_transfer_dhcp_info(struct e1000_adapter *adapter, struct sk_buff *skb)
return 0;
}
+static int __e1000_maybe_stop_tx(struct net_device *netdev, int size)
+{
+ struct e1000_adapter *adapter = netdev_priv(netdev);
+ struct e1000_tx_ring *tx_ring = adapter->tx_ring;
+
+ netif_stop_queue(netdev);
+ /* Herbert's original patch had:
+ * smp_mb__after_netif_stop_queue();
+ * but since that doesn't exist yet, just open code it. */
+ smp_mb();
+
+ /* We need to check again in a case another CPU has just
+ * made room available. */
+ if (likely(E1000_DESC_UNUSED(tx_ring) < size))
+ return -EBUSY;
+
+ /* A reprieve! */
+ netif_start_queue(netdev);
+ return 0;
+}
+
+static int e1000_maybe_stop_tx(struct net_device *netdev,
+ struct e1000_tx_ring *tx_ring, int size)
+{
+ if (likely(E1000_DESC_UNUSED(tx_ring) >= size))
+ return 0;
+ return __e1000_maybe_stop_tx(netdev, size);
+}
+
#define TXD_USE_COUNT(S, X) (((S) >> (X)) + 1 )
static int
e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
@@ -2910,6 +2975,10 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
unsigned int f;
len -= skb->data_len;
+ /* This goes back to the question of how to logically map a tx queue
+ * to a flow. Right now, performance is impacted slightly negatively
+ * if using multiple tx queues. If the stack breaks away from a
+ * single qdisc implementation, we can look at this again. */
tx_ring = adapter->tx_ring;
if (unlikely(skb->len <= 0)) {
@@ -3005,8 +3074,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
/* need: count + 2 desc gap to keep tail from touching
* head, otherwise try next time */
- if (unlikely(E1000_DESC_UNUSED(tx_ring) < count + 2)) {
- netif_stop_queue(netdev);
+ if (unlikely(e1000_maybe_stop_tx(netdev, tx_ring, count + 2))) {
spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
return NETDEV_TX_BUSY;
}
@@ -3014,7 +3082,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
if (unlikely(adapter->hw.mac_type == e1000_82547)) {
if (unlikely(e1000_82547_fifo_workaround(adapter, skb))) {
netif_stop_queue(netdev);
- mod_timer(&adapter->tx_fifo_stall_timer, jiffies);
+ mod_timer(&adapter->tx_fifo_stall_timer, jiffies + 1);
spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
return NETDEV_TX_BUSY;
}
@@ -3053,8 +3121,7 @@ e1000_xmit_frame(struct sk_buff *skb, struct net_device *netdev)
netdev->trans_start = jiffies;
/* Make sure there is space in the ring for the next send. */
- if (unlikely(E1000_DESC_UNUSED(tx_ring) < MAX_SKB_FRAGS + 2))
- netif_stop_queue(netdev);
+ e1000_maybe_stop_tx(netdev, tx_ring, MAX_SKB_FRAGS + 2);
spin_unlock_irqrestore(&tx_ring->tx_lock, flags);
return NETDEV_TX_OK;
@@ -3131,11 +3198,13 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu)
}
break;
case e1000_82573:
- /* only enable jumbo frames if ASPM is disabled completely
- * this means both bits must be zero in 0x1A bits 3:2 */
+ /* Jumbo Frames not supported if:
+ * - this is not an 82573L device
+ * - ASPM is enabled in any way (0x1A bits 3:2) */
e1000_read_eeprom(&adapter->hw, EEPROM_INIT_3GIO_3, 1,
&eeprom_data);
- if (eeprom_data & EEPROM_WORD1A_ASPM_MASK) {
+ if ((adapter->hw.device_id != E1000_DEV_ID_82573L) ||
+ (eeprom_data & EEPROM_WORD1A_ASPM_MASK)) {
if (max_frame > MAXIMUM_ETHERNET_FRAME_SIZE) {
DPRINTK(PROBE, ERR,
"Jumbo Frames not supported.\n");
@@ -3143,6 +3212,8 @@ e1000_change_mtu(struct net_device *netdev, int new_mtu)
}
break;
}
+ /* ERT will be enabled later to enable wire speed receives */
+
/* fall through to get support */
case e1000_82571:
case e1000_82572:
@@ -3328,16 +3399,15 @@ e1000_update_stats(struct e1000_adapter *adapter)
adapter->stats.crcerrs + adapter->stats.algnerrc +
adapter->stats.ruc + adapter->stats.roc +
adapter->stats.cexterr;
- adapter->net_stats.rx_length_errors = adapter->stats.ruc +
- adapter->stats.roc;
+ adapter->stats.rlerrc = adapter->stats.ruc + adapter->stats.roc;
+ adapter->net_stats.rx_length_errors = adapter->stats.rlerrc;
adapter->net_stats.rx_crc_errors = adapter->stats.crcerrs;
adapter->net_stats.rx_frame_errors = adapter->stats.algnerrc;
adapter->net_stats.rx_missed_errors = adapter->stats.mpc;
/* Tx Errors */
-
- adapter->net_stats.tx_errors = adapter->stats.ecol +
- adapter->stats.latecol;
+ adapter->stats.txerrc = adapter->stats.ecol + adapter->stats.latecol;
+ adapter->net_stats.tx_errors = adapter->stats.txerrc;
adapter->net_stats.tx_aborted_errors = adapter->stats.ecol;
adapter->net_stats.tx_window_errors = adapter->stats.latecol;
adapter->net_stats.tx_carrier_errors = adapter->stats.tncrs;
@@ -3408,7 +3478,9 @@ e1000_intr(int irq, void *data, struct pt_regs *regs)
rctl = E1000_READ_REG(hw, RCTL);
E1000_WRITE_REG(hw, RCTL, rctl & ~E1000_RCTL_EN);
}
- mod_timer(&adapter->watchdog_timer, jiffies);
+ /* guard against interrupt when we're going down */
+ if (!test_bit(__E1000_DOWN, &adapter->flags))
+ mod_timer(&adapter->watchdog_timer, jiffies + 1);
}
#ifdef CONFIG_E1000_NAPI
@@ -3546,13 +3618,14 @@ e1000_clean_tx_irq(struct e1000_adapter *adapter,
tx_ring->next_to_clean = i;
#define TX_WAKE_THRESHOLD 32
- if (unlikely(cleaned && netif_queue_stopped(netdev) &&
- netif_carrier_ok(netdev))) {
- spin_lock(&tx_ring->tx_lock);
- if (netif_queue_stopped(netdev) &&
- (E1000_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD))
+ if (unlikely(cleaned && netif_carrier_ok(netdev) &&
+ E1000_DESC_UNUSED(tx_ring) >= TX_WAKE_THRESHOLD)) {
+ /* Make sure that anybody stopping the queue after this
+ * sees the new next_to_clean.
+ */
+ smp_mb();
+ if (netif_queue_stopped(netdev))
netif_wake_queue(netdev);
- spin_unlock(&tx_ring->tx_lock);
}
if (adapter->detect_tx_hung) {
@@ -4412,13 +4485,21 @@ e1000_write_pci_cfg(struct e1000_hw *hw, uint32_t reg, uint16_t *value)
pci_write_config_word(adapter->pdev, reg, *value);
}
-#if 0
-uint32_t
-e1000_io_read(struct e1000_hw *hw, unsigned long port)
+int32_t
+e1000_read_pcie_cap_reg(struct e1000_hw *hw, uint32_t reg, uint16_t *value)
{
- return inl(port);
+ struct e1000_adapter *adapter = hw->back;
+ uint16_t cap_offset;
+
+ cap_offset = pci_find_capability(adapter->pdev, PCI_CAP_ID_EXP);
+ if (!cap_offset)
+ return -E1000_ERR_CONFIG;
+
+ pci_read_config_word(adapter->pdev, cap_offset + reg, value);
+
+ return E1000_SUCCESS;
}
-#endif /* 0 */
+
void
e1000_io_write(struct e1000_hw *hw, unsigned long port, uint32_t value)
@@ -4693,9 +4774,7 @@ e1000_suspend(struct pci_dev *pdev, pm_message_t state)
pci_enable_wake(pdev, PCI_D3cold, 0);
}
- /* FIXME: this code is incorrect for PCI Express */
- if (adapter->hw.mac_type >= e1000_82540 &&
- adapter->hw.mac_type != e1000_ich8lan &&
+ if (adapter->hw.mac_type < e1000_82571 &&
adapter->hw.media_type == e1000_media_type_copper) {
manc = E1000_READ_REG(&adapter->hw, MANC);
if (manc & E1000_MANC_SMBUS_EN) {
@@ -4747,9 +4826,7 @@ e1000_resume(struct pci_dev *pdev)
netif_device_attach(netdev);
- /* FIXME: this code is incorrect for PCI Express */
- if (adapter->hw.mac_type >= e1000_82540 &&
- adapter->hw.mac_type != e1000_ich8lan &&
+ if (adapter->hw.mac_type < e1000_82571 &&
adapter->hw.media_type == e1000_media_type_copper) {
manc = E1000_READ_REG(&adapter->hw, MANC);
manc &= ~(E1000_MANC_ARP_EN);
@@ -4835,8 +4912,8 @@ static pci_ers_result_t e1000_io_slot_reset(struct pci_dev *pdev)
}
pci_set_master(pdev);
- pci_enable_wake(pdev, 3, 0);
- pci_enable_wake(pdev, 4, 0); /* 4 == D3 cold */
+ pci_enable_wake(pdev, PCI_D3hot, 0);
+ pci_enable_wake(pdev, PCI_D3cold, 0);
/* Perform card reset only on one instance of the card */
if (PCI_FUNC (pdev->devfn) != 0)
diff --git a/drivers/net/e1000/e1000_osdep.h b/drivers/net/e1000/e1000_osdep.h
index 46bc49df15e..a464cb29062 100644
--- a/drivers/net/e1000/e1000_osdep.h
+++ b/drivers/net/e1000/e1000_osdep.h
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
diff --git a/drivers/net/e1000/e1000_param.c b/drivers/net/e1000/e1000_param.c
index 21284273897..9c3c1acefcc 100644
--- a/drivers/net/e1000/e1000_param.c
+++ b/drivers/net/e1000/e1000_param.c
@@ -1,25 +1,24 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/1000 Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
@@ -397,17 +396,17 @@ e1000_check_options(struct e1000_adapter *adapter)
{ /* Flow Control */
struct e1000_opt_list fc_list[] =
- {{ e1000_fc_none, "Flow Control Disabled" },
- { e1000_fc_rx_pause,"Flow Control Receive Only" },
- { e1000_fc_tx_pause,"Flow Control Transmit Only" },
- { e1000_fc_full, "Flow Control Enabled" },
- { e1000_fc_default, "Flow Control Hardware Default" }};
+ {{ E1000_FC_NONE, "Flow Control Disabled" },
+ { E1000_FC_RX_PAUSE,"Flow Control Receive Only" },
+ { E1000_FC_TX_PAUSE,"Flow Control Transmit Only" },
+ { E1000_FC_FULL, "Flow Control Enabled" },
+ { E1000_FC_DEFAULT, "Flow Control Hardware Default" }};
struct e1000_option opt = {
.type = list_option,
.name = "Flow Control",
.err = "reading default settings from EEPROM",
- .def = e1000_fc_default,
+ .def = E1000_FC_DEFAULT,
.arg = { .l = { .nr = ARRAY_SIZE(fc_list),
.p = fc_list }}
};
diff --git a/drivers/net/fec_8xx/fec_main.c b/drivers/net/fec_8xx/fec_main.c
index 22ac2df1aeb..e17a1449ee1 100644
--- a/drivers/net/fec_8xx/fec_main.c
+++ b/drivers/net/fec_8xx/fec_main.c
@@ -30,6 +30,7 @@
#include <linux/mii.h>
#include <linux/ethtool.h>
#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
#include <asm/8xx_immap.h>
#include <asm/pgtable.h>
@@ -37,7 +38,6 @@
#include <asm/irq.h>
#include <asm/uaccess.h>
#include <asm/commproc.h>
-#include <asm/dma-mapping.h>
#include "fec_8xx.h"
diff --git a/drivers/net/forcedeth.c b/drivers/net/forcedeth.c
index 97db910fbc8..eea1d66c530 100644
--- a/drivers/net/forcedeth.c
+++ b/drivers/net/forcedeth.c
@@ -3789,6 +3789,12 @@ static int nv_loopback_test(struct net_device *dev)
/* setup packet for tx */
pkt_len = ETH_DATA_LEN;
tx_skb = dev_alloc_skb(pkt_len);
+ if (!tx_skb) {
+ printk(KERN_ERR "dev_alloc_skb() failed during loopback test"
+ " of %s\n", dev->name);
+ ret = 0;
+ goto out;
+ }
pkt_data = skb_put(tx_skb, pkt_len);
for (i = 0; i < pkt_len; i++)
pkt_data[i] = (u8)(i & 0xff);
@@ -3853,7 +3859,7 @@ static int nv_loopback_test(struct net_device *dev)
tx_skb->end-tx_skb->data,
PCI_DMA_TODEVICE);
dev_kfree_skb_any(tx_skb);
-
+ out:
/* stop engines */
nv_stop_rx(dev);
nv_stop_tx(dev);
diff --git a/drivers/net/fs_enet/fs_enet.h b/drivers/net/fs_enet/fs_enet.h
index 95022c005f7..92590d8fc24 100644
--- a/drivers/net/fs_enet/fs_enet.h
+++ b/drivers/net/fs_enet/fs_enet.h
@@ -6,11 +6,10 @@
#include <linux/types.h>
#include <linux/list.h>
#include <linux/phy.h>
+#include <linux/dma-mapping.h>
#include <linux/fs_enet_pd.h>
-#include <asm/dma-mapping.h>
-
#ifdef CONFIG_CPM1
#include <asm/commproc.h>
diff --git a/drivers/net/gt64240eth.h b/drivers/net/gt64240eth.h
deleted file mode 100644
index 0d6f486e457..00000000000
--- a/drivers/net/gt64240eth.h
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * This file is subject to the terms and conditions of the GNU General Public
- * License. See the file "COPYING" in the main directory of this archive
- * for more details.
- *
- * Copyright (C) 2001 Patton Electronics Company
- * Copyright (C) 2002 Momentum Computer
- *
- * Copyright 2000 MontaVista Software Inc.
- * Author: MontaVista Software, Inc.
- * stevel@mvista.com or support@mvista.com
- *
- * This program is free software; you can distribute it and/or modify it
- * under the terms of the GNU General Public License (Version 2) as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * Ethernet driver definitions for the MIPS GT96100 Advanced
- * Communication Controller.
- *
- * Modified for the Marvellous GT64240 Retarded Communication Controller.
- */
-#ifndef _GT64240ETH_H
-#define _GT64240ETH_H
-
-#include <asm/gt64240.h>
-
-#define ETHERNET_PORTS_DIFFERENCE_OFFSETS 0x400
-
-/* Translate those weanie names from Galileo/VxWorks header files: */
-
-#define GT64240_MRR MAIN_ROUTING_REGISTER
-#define GT64240_CIU_ARBITER_CONFIG COMM_UNIT_ARBITER_CONFIGURATION_REGISTER
-#define GT64240_CIU_ARBITER_CONTROL COMM_UNIT_ARBITER_CONTROL
-#define GT64240_MAIN_LOW_CAUSE LOW_INTERRUPT_CAUSE_REGISTER
-#define GT64240_MAIN_HIGH_CAUSE HIGH_INTERRUPT_CAUSE_REGISTER
-#define GT64240_CPU_LOW_MASK CPU_INTERRUPT_MASK_REGISTER_LOW
-#define GT64240_CPU_HIGH_MASK CPU_INTERRUPT_MASK_REGISTER_HIGH
-#define GT64240_CPU_SELECT_CAUSE CPU_SELECT_CAUSE_REGISTER
-
-#define GT64240_ETH_PHY_ADDR_REG ETHERNET_PHY_ADDRESS_REGISTER
-#define GT64240_ETH_PORT_CONFIG ETHERNET0_PORT_CONFIGURATION_REGISTER
-#define GT64240_ETH_PORT_CONFIG_EXT ETHERNET0_PORT_CONFIGURATION_EXTEND_REGISTER
-#define GT64240_ETH_PORT_COMMAND ETHERNET0_PORT_COMMAND_REGISTER
-#define GT64240_ETH_PORT_STATUS ETHERNET0_PORT_STATUS_REGISTER
-#define GT64240_ETH_IO_SIZE ETHERNET_PORTS_DIFFERENCE_OFFSETS
-#define GT64240_ETH_SMI_REG ETHERNET_SMI_REGISTER
-#define GT64240_ETH_MIB_COUNT_BASE ETHERNET0_MIB_COUNTER_BASE
-#define GT64240_ETH_SDMA_CONFIG ETHERNET0_SDMA_CONFIGURATION_REGISTER
-#define GT64240_ETH_SDMA_COMM ETHERNET0_SDMA_COMMAND_REGISTER
-#define GT64240_ETH_INT_MASK ETHERNET0_INTERRUPT_MASK_REGISTER
-#define GT64240_ETH_INT_CAUSE ETHERNET0_INTERRUPT_CAUSE_REGISTER
-#define GT64240_ETH_CURR_TX_DESC_PTR0 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_CURR_TX_DESC_PTR1 ETHERNET0_CURRENT_TX_DESCRIPTOR_POINTER1
-#define GT64240_ETH_1ST_RX_DESC_PTR0 ETHERNET0_FIRST_RX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_CURR_RX_DESC_PTR0 ETHERNET0_CURRENT_RX_DESCRIPTOR_POINTER0
-#define GT64240_ETH_HASH_TBL_PTR ETHERNET0_HASH_TABLE_POINTER_REGISTER
-
-/* Turn on NAPI by default */
-
-#define GT64240_NAPI 1
-
-/* Some 64240 settings that SHOULD eventually be setup in PROM monitor: */
-/* (Board-specific to the DSL3224 Rev A board ONLY!) */
-#define D3224_MPP_CTRL0_SETTING 0x66669900
-#define D3224_MPP_CTRL1_SETTING 0x00000000
-#define D3224_MPP_CTRL2_SETTING 0x00887700
-#define D3224_MPP_CTRL3_SETTING 0x00000044
-#define D3224_GPP_IO_CTRL_SETTING 0x0000e800
-#define D3224_GPP_LEVEL_CTRL_SETTING 0xf001f703
-#define D3224_GPP_VALUE_SETTING 0x00000000
-
-/* Keep the ring sizes a power of two for efficiency. */
-//-#define TX_RING_SIZE 16
-#define TX_RING_SIZE 64 /* TESTING !!! */
-#define RX_RING_SIZE 32
-#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer. */
-
-#define RX_HASH_TABLE_SIZE 16384
-#define HASH_HOP_NUMBER 12
-
-#define NUM_INTERFACES 3
-
-#define GT64240ETH_TX_TIMEOUT HZ/4
-
-#define MIPS_GT64240_BASE 0xf4000000
-#define GT64240_ETH0_BASE (MIPS_GT64240_BASE + GT64240_ETH_PORT_CONFIG)
-#define GT64240_ETH1_BASE (GT64240_ETH0_BASE + GT64240_ETH_IO_SIZE)
-#define GT64240_ETH2_BASE (GT64240_ETH1_BASE + GT64240_ETH_IO_SIZE)
-
-#if defined(CONFIG_MIPS_DSL3224)
-#define GT64240_ETHER0_IRQ 4
-#define GT64240_ETHER1_IRQ 4
-#else
-#define GT64240_ETHER0_IRQ -1
-#define GT64240_ETHER1_IRQ -1
-#endif
-
-#define REV_GT64240 0x1
-#define REV_GT64240A 0x10
-
-#define GT64240ETH_READ(gp, offset) \
- GT_READ((gp)->port_offset + (offset))
-
-#define GT64240ETH_WRITE(gp, offset, data) \
- GT_WRITE((gp)->port_offset + (offset), (data))
-
-#define GT64240ETH_SETBIT(gp, offset, bits) \
- GT64240ETH_WRITE((gp), (offset), \
- GT64240ETH_READ((gp), (offset)) | (bits))
-
-#define GT64240ETH_CLRBIT(gp, offset, bits) \
- GT64240ETH_WRITE((gp), (offset), \
- GT64240ETH_READ((gp), (offset)) & ~(bits))
-
-#define GT64240_READ(ofs) GT_READ(ofs)
-#define GT64240_WRITE(ofs, data) GT_WRITE((ofs), (data))
-
-/* Bit definitions of the SMI Reg */
-enum {
- smirDataMask = 0xffff,
- smirPhyAdMask = 0x1f << 16,
- smirPhyAdBit = 16,
- smirRegAdMask = 0x1f << 21,
- smirRegAdBit = 21,
- smirOpCode = 1 << 26,
- smirReadValid = 1 << 27,
- smirBusy = 1 << 28
-};
-
-/* Bit definitions of the Port Config Reg */
-enum pcr_bits {
- pcrPM = 1 << 0,
- pcrRBM = 1 << 1,
- pcrPBF = 1 << 2,
- pcrEN = 1 << 7,
- pcrLPBKMask = 0x3 << 8,
- pcrLPBKBit = 1 << 8,
- pcrFC = 1 << 10,
- pcrHS = 1 << 12,
- pcrHM = 1 << 13,
- pcrHDM = 1 << 14,
- pcrHD = 1 << 15,
- pcrISLMask = 0x7 << 28,
- pcrISLBit = 28,
- pcrACCS = 1 << 31
-};
-
-/* Bit definitions of the Port Config Extend Reg */
-enum pcxr_bits {
- pcxrIGMP = 1,
- pcxrSPAN = 2,
- pcxrPAR = 4,
- pcxrPRIOtxMask = 0x7 << 3,
- pcxrPRIOtxBit = 3,
- pcxrPRIOrxMask = 0x3 << 6,
- pcxrPRIOrxBit = 6,
- pcxrPRIOrxOverride = 1 << 8,
- pcxrDPLXen = 1 << 9,
- pcxrFCTLen = 1 << 10,
- pcxrFLP = 1 << 11,
- pcxrFCTL = 1 << 12,
- pcxrMFLMask = 0x3 << 14,
- pcxrMFLBit = 14,
- pcxrMIBclrMode = 1 << 16,
- pcxrSpeed = 1 << 18,
- pcxrSpeeden = 1 << 19,
- pcxrRMIIen = 1 << 20,
- pcxrDSCPen = 1 << 21
-};
-
-/* Bit definitions of the Port Command Reg */
-enum pcmr_bits {
- pcmrFJ = 1 << 15
-};
-
-
-/* Bit definitions of the Port Status Reg */
-enum psr_bits {
- psrSpeed = 1,
- psrDuplex = 2,
- psrFctl = 4,
- psrLink = 8,
- psrPause = 1 << 4,
- psrTxLow = 1 << 5,
- psrTxHigh = 1 << 6,
- psrTxInProg = 1 << 7
-};
-
-/* Bit definitions of the SDMA Config Reg */
-enum sdcr_bits {
- sdcrRCMask = 0xf << 2,
- sdcrRCBit = 2,
- sdcrBLMR = 1 << 6,
- sdcrBLMT = 1 << 7,
- sdcrPOVR = 1 << 8,
- sdcrRIFB = 1 << 9,
- sdcrBSZMask = 0x3 << 12,
- sdcrBSZBit = 12
-};
-
-/* Bit definitions of the SDMA Command Reg */
-enum sdcmr_bits {
- sdcmrERD = 1 << 7,
- sdcmrAR = 1 << 15,
- sdcmrSTDH = 1 << 16,
- sdcmrSTDL = 1 << 17,
- sdcmrTXDH = 1 << 23,
- sdcmrTXDL = 1 << 24,
- sdcmrAT = 1 << 31
-};
-
-/* Bit definitions of the Interrupt Cause Reg */
-enum icr_bits {
- icrRxBuffer = 1,
- icrTxBufferHigh = 1 << 2,
- icrTxBufferLow = 1 << 3,
- icrTxEndHigh = 1 << 6,
- icrTxEndLow = 1 << 7,
- icrRxError = 1 << 8,
- icrTxErrorHigh = 1 << 10,
- icrTxErrorLow = 1 << 11,
- icrRxOVR = 1 << 12,
- icrTxUdr = 1 << 13,
- icrRxBufferQ0 = 1 << 16,
- icrRxBufferQ1 = 1 << 17,
- icrRxBufferQ2 = 1 << 18,
- icrRxBufferQ3 = 1 << 19,
- icrRxErrorQ0 = 1 << 20,
- icrRxErrorQ1 = 1 << 21,
- icrRxErrorQ2 = 1 << 22,
- icrRxErrorQ3 = 1 << 23,
- icrMIIPhySTC = 1 << 28,
- icrSMIdone = 1 << 29,
- icrEtherIntSum = 1 << 31
-};
-
-
-/* The Rx and Tx descriptor lists. */
-#ifdef __LITTLE_ENDIAN
-typedef struct {
- u32 cmdstat;
- u16 reserved; //-prk21aug01 u32 reserved:16;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u32 buff_ptr;
- u32 next;
-} gt64240_td_t;
-
-typedef struct {
- u32 cmdstat;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u16 buff_sz; //-prk21aug01 u32 buff_sz:16;
- u32 buff_ptr;
- u32 next;
-} gt64240_rd_t;
-#elif defined(__BIG_ENDIAN)
-typedef struct {
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u16 reserved; //-prk21aug01 u32 reserved:16;
- u32 cmdstat;
- u32 next;
- u32 buff_ptr;
-} gt64240_td_t;
-
-typedef struct {
- u16 buff_sz; //-prk21aug01 u32 buff_sz:16;
- u16 byte_cnt; //-prk21aug01 u32 byte_cnt:16;
- u32 cmdstat;
- u32 next;
- u32 buff_ptr;
-} gt64240_rd_t;
-#else
-#error Either __BIG_ENDIAN or __LITTLE_ENDIAN must be defined!
-#endif
-
-
-/* Values for the Tx command-status descriptor entry. */
-enum td_cmdstat {
- txOwn = 1 << 31,
- txAutoMode = 1 << 30,
- txEI = 1 << 23,
- txGenCRC = 1 << 22,
- txPad = 1 << 18,
- txFirst = 1 << 17,
- txLast = 1 << 16,
- txErrorSummary = 1 << 15,
- txReTxCntMask = 0x0f << 10,
- txReTxCntBit = 10,
- txCollision = 1 << 9,
- txReTxLimit = 1 << 8,
- txUnderrun = 1 << 6,
- txLateCollision = 1 << 5
-};
-
-
-/* Values for the Rx command-status descriptor entry. */
-enum rd_cmdstat {
- rxOwn = 1 << 31,
- rxAutoMode = 1 << 30,
- rxEI = 1 << 23,
- rxFirst = 1 << 17,
- rxLast = 1 << 16,
- rxErrorSummary = 1 << 15,
- rxIGMP = 1 << 14,
- rxHashExpired = 1 << 13,
- rxMissedFrame = 1 << 12,
- rxFrameType = 1 << 11,
- rxShortFrame = 1 << 8,
- rxMaxFrameLen = 1 << 7,
- rxOverrun = 1 << 6,
- rxCollision = 1 << 4,
- rxCRCError = 1
-};
-
-/* Bit fields of a Hash Table Entry */
-enum hash_table_entry {
- hteValid = 1,
- hteSkip = 2,
- hteRD = 4
-};
-
-// The MIB counters
-typedef struct {
- u32 byteReceived;
- u32 byteSent;
- u32 framesReceived;
- u32 framesSent;
- u32 totalByteReceived;
- u32 totalFramesReceived;
- u32 broadcastFramesReceived;
- u32 multicastFramesReceived;
- u32 cRCError;
- u32 oversizeFrames;
- u32 fragments;
- u32 jabber;
- u32 collision;
- u32 lateCollision;
- u32 frames64;
- u32 frames65_127;
- u32 frames128_255;
- u32 frames256_511;
- u32 frames512_1023;
- u32 frames1024_MaxSize;
- u32 macRxError;
- u32 droppedFrames;
- u32 outMulticastFrames;
- u32 outBroadcastFrames;
- u32 undersizeFrames;
-} mib_counters_t;
-
-
-struct gt64240_private {
- gt64240_rd_t *rx_ring;
- gt64240_td_t *tx_ring;
- // The Rx and Tx rings must be 16-byte aligned
- dma_addr_t rx_ring_dma;
- dma_addr_t tx_ring_dma;
- char *hash_table;
- // The Hash Table must be 8-byte aligned
- dma_addr_t hash_table_dma;
- int hash_mode;
-
- // The Rx buffers must be 8-byte aligned
- char *rx_buff;
- dma_addr_t rx_buff_dma;
- // Tx buffers (tx_skbuff[i]->data) with less than 8 bytes
- // of payload must be 8-byte aligned
- struct sk_buff *tx_skbuff[TX_RING_SIZE];
- int rx_next_out; /* The next free ring entry to receive */
- int tx_next_in; /* The next free ring entry to send */
- int tx_next_out; /* The last ring entry the ISR processed */
- int tx_count; /* current # of pkts waiting to be sent in Tx ring */
- int intr_work_done; /* number of Rx and Tx pkts processed in the isr */
- int tx_full; /* Tx ring is full */
-
- mib_counters_t mib;
- struct net_device_stats stats;
-
- int io_size;
- int port_num; // 0 or 1
- u32 port_offset;
-
- int phy_addr; // PHY address
- u32 last_psr; // last value of the port status register
-
- int options; /* User-settable misc. driver options. */
- int drv_flags;
- spinlock_t lock; /* Serialise access to device */
- struct mii_if_info mii_if;
-
- u32 msg_enable;
-};
-
-#endif /* _GT64240ETH_H */
diff --git a/drivers/net/hp100.c b/drivers/net/hp100.c
index 03b3df33d81..ae8ad4f763b 100644
--- a/drivers/net/hp100.c
+++ b/drivers/net/hp100.c
@@ -188,10 +188,12 @@ struct hp100_private {
/*
* variables
*/
+#ifdef CONFIG_ISA
static const char *hp100_isa_tbl[] = {
"HWPF150", /* HP J2573 rev A */
"HWP1950", /* HP J2573 */
};
+#endif
#ifdef CONFIG_EISA
static struct eisa_device_id hp100_eisa_tbl[] = {
@@ -333,6 +335,7 @@ static __devinit const char *hp100_read_id(int ioaddr)
return str;
}
+#ifdef CONFIG_ISA
static __init int hp100_isa_probe1(struct net_device *dev, int ioaddr)
{
const char *sig;
@@ -390,9 +393,9 @@ static int __init hp100_isa_probe(struct net_device *dev, int addr)
}
return err;
}
+#endif /* CONFIG_ISA */
-
-#ifndef MODULE
+#if !defined(MODULE) && defined(CONFIG_ISA)
struct net_device * __init hp100_probe(int unit)
{
struct net_device *dev = alloc_etherdev(sizeof(struct hp100_private));
@@ -422,7 +425,7 @@ struct net_device * __init hp100_probe(int unit)
free_netdev(dev);
return ERR_PTR(err);
}
-#endif
+#endif /* !MODULE && CONFIG_ISA */
static int __devinit hp100_probe1(struct net_device *dev, int ioaddr,
u_char bus, struct pci_dev *pci_dev)
diff --git a/drivers/net/ifb.c b/drivers/net/ifb.c
index 6469130c141..c26a4b8e552 100644
--- a/drivers/net/ifb.c
+++ b/drivers/net/ifb.c
@@ -200,8 +200,8 @@ static struct net_device_stats *ifb_get_stats(struct net_device *dev)
pr_debug("tasklets stats %ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld:%ld \n",
dp->st_task_enter, dp->st_txq_refl_try, dp->st_rxq_enter,
- dp->st_rx2tx_tran dp->st_rxq_notenter, dp->st_rx_frm_egr,
- dp->st_rx_frm_ing, dp->st_rxq_check, dp->st_rxq_rsch );
+ dp->st_rx2tx_tran, dp->st_rxq_notenter, dp->st_rx_frm_egr,
+ dp->st_rx_frm_ing, dp->st_rxq_check, dp->st_rxq_rsch);
return stats;
}
diff --git a/drivers/net/irda/Kconfig b/drivers/net/irda/Kconfig
index e9e6d99a9ad..7c8ccc09b60 100644
--- a/drivers/net/irda/Kconfig
+++ b/drivers/net/irda/Kconfig
@@ -287,6 +287,7 @@ comment "FIR device drivers"
config USB_IRDA
tristate "IrDA USB dongles"
depends on IRDA && USB
+ select FW_LOADER
---help---
Say Y here if you want to build support for the USB IrDA FIR Dongle
device driver. To compile it as a module, choose M here: the module
diff --git a/drivers/net/irda/irda-usb.c b/drivers/net/irda/irda-usb.c
index 2a0d538b387..383cef1f599 100644
--- a/drivers/net/irda/irda-usb.c
+++ b/drivers/net/irda/irda-usb.c
@@ -671,10 +671,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
* Jean II */
done = 1;
break;
- case -ECONNABORTED: /* -103 */
- case -ECONNRESET: /* -104 */
- case -ETIMEDOUT: /* -110 */
- case -ENOENT: /* -2 (urb unlinked by us) */
+ case -ECONNRESET:
+ case -ENOENT: /* urb unlinked by us */
default: /* ??? - Play safe */
urb->status = 0;
netif_wake_queue(self->netdev);
@@ -712,10 +710,8 @@ static void irda_usb_net_timeout(struct net_device *netdev)
* Jean II */
done = 1;
break;
- case -ECONNABORTED: /* -103 */
- case -ECONNRESET: /* -104 */
- case -ETIMEDOUT: /* -110 */
- case -ENOENT: /* -2 (urb unlinked by us) */
+ case -ECONNRESET:
+ case -ENOENT: /* urb unlinked by us */
default: /* ??? - Play safe */
if(skb != NULL) {
dev_kfree_skb_any(skb);
@@ -845,14 +841,14 @@ static void irda_usb_receive(struct urb *urb, struct pt_regs *regs)
self->stats.rx_crc_errors++;
/* Also precursor to a hot-unplug on UHCI. */
/* Fallthrough... */
- case -ECONNRESET: /* -104 */
+ case -ECONNRESET:
/* Random error, if I remember correctly */
/* uhci_cleanup_unlink() is going to kill the Rx
* URB just after we return. No problem, at this
* point the URB will be idle ;-) - Jean II */
- case -ESHUTDOWN: /* -108 */
+ case -ESHUTDOWN:
/* That's usually a hot-unplug. Submit will fail... */
- case -ETIMEDOUT: /* -110 */
+ case -ETIME:
/* Usually precursor to a hot-unplug on OHCI. */
default:
self->stats.rx_errors++;
diff --git a/drivers/net/irda/nsc-ircc.c b/drivers/net/irda/nsc-ircc.c
index cb62f2a9676..7185a4ee3c1 100644
--- a/drivers/net/irda/nsc-ircc.c
+++ b/drivers/net/irda/nsc-ircc.c
@@ -110,7 +110,7 @@ static nsc_chip_t chips[] = {
{ "PC87338", { 0x398, 0x15c, 0x2e }, 0x08, 0xb0, 0xf8,
nsc_ircc_probe_338, nsc_ircc_init_338 },
/* Contributed by Steffen Pingel - IBM X40 */
- { "PC8738x", { 0x164e, 0x4e, 0x0 }, 0x20, 0xf4, 0xff,
+ { "PC8738x", { 0x164e, 0x4e, 0x2e }, 0x20, 0xf4, 0xff,
nsc_ircc_probe_39x, nsc_ircc_init_39x },
/* Contributed by Jan Frey - IBM A30/A31 */
{ "PC8739x", { 0x2e, 0x4e, 0x0 }, 0x20, 0xea, 0xff,
diff --git a/drivers/net/irda/smsc-ircc2.c b/drivers/net/irda/smsc-ircc2.c
index 2eff45bedc7..22358ff68c4 100644
--- a/drivers/net/irda/smsc-ircc2.c
+++ b/drivers/net/irda/smsc-ircc2.c
@@ -2354,6 +2354,26 @@ static int __init smsc_superio_lpc(unsigned short cfg_base)
#define PCIID_VENDOR_INTEL 0x8086
#define PCIID_VENDOR_ALI 0x10b9
static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __initdata = {
+ /*
+ * Subsystems needing entries:
+ * 0x10b9:0x1533 0x103c:0x0850 HP nx9010 family
+ * 0x10b9:0x1533 0x0e11:0x005a Compaq nc4000 family
+ * 0x8086:0x24cc 0x0e11:0x002a HP nx9000 family
+ */
+ {
+ /* Guessed entry */
+ .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .device = 0x24cc,
+ .subvendor = 0x103c,
+ .subdevice = 0x08bc,
+ .sir_io = 0x02f8,
+ .fir_io = 0x0130,
+ .fir_irq = 0x05,
+ .fir_dma = 0x03,
+ .cfg_base = 0x004e,
+ .preconfigure = preconfigure_through_82801,
+ .name = "HP nx5000 family",
+ },
{
.vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
.device = 0x24cc,
@@ -2366,7 +2386,7 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.fir_dma = 0x03,
.cfg_base = 0x004e,
.preconfigure = preconfigure_through_82801,
- .name = "HP nc8000",
+ .name = "HP nc8000 family",
},
{
.vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
@@ -2379,7 +2399,21 @@ static struct smsc_ircc_subsystem_configuration subsystem_configurations[] __ini
.fir_dma = 0x03,
.cfg_base = 0x004e,
.preconfigure = preconfigure_through_82801,
- .name = "HP nc6000",
+ .name = "HP nc6000 family",
+ },
+ {
+ .vendor = PCIID_VENDOR_INTEL, /* Intel 82801DBM LPC bridge */
+ .device = 0x24cc,
+ .subvendor = 0x0e11,
+ .subdevice = 0x0860,
+ /* I assume these are the same for x1000 as for the others */
+ .sir_io = 0x02e8,
+ .fir_io = 0x02f8,
+ .fir_irq = 0x07,
+ .fir_dma = 0x03,
+ .cfg_base = 0x002e,
+ .preconfigure = preconfigure_through_82801,
+ .name = "Compaq x1000 family",
},
{
/* Intel 82801DB/DBL (ICH4/ICH4-L) LPC Interface Bridge */
diff --git a/drivers/net/irda/stir4200.c b/drivers/net/irda/stir4200.c
index d61b208b52a..12103c93f7e 100644
--- a/drivers/net/irda/stir4200.c
+++ b/drivers/net/irda/stir4200.c
@@ -149,8 +149,6 @@ enum StirFifoCtlMask {
FIFOCTL_DIR = 0x10,
FIFOCTL_CLR = 0x08,
FIFOCTL_EMPTY = 0x04,
- FIFOCTL_RXERR = 0x02,
- FIFOCTL_TXERR = 0x01,
};
enum StirDiagMask {
@@ -615,19 +613,6 @@ static int fifo_txwait(struct stir_cb *stir, int space)
pr_debug("fifo status 0x%lx count %lu\n", status, count);
- /* error when receive/transmit fifo gets confused */
- if (status & FIFOCTL_RXERR) {
- stir->stats.rx_fifo_errors++;
- stir->stats.rx_errors++;
- break;
- }
-
- if (status & FIFOCTL_TXERR) {
- stir->stats.tx_fifo_errors++;
- stir->stats.tx_errors++;
- break;
- }
-
/* is fifo receiving already, or empty */
if (!(status & FIFOCTL_DIR)
|| (status & FIFOCTL_EMPTY))
diff --git a/drivers/net/irda/via-ircc.c b/drivers/net/irda/via-ircc.c
index 79b85f32750..d916e1257c4 100644
--- a/drivers/net/irda/via-ircc.c
+++ b/drivers/net/irda/via-ircc.c
@@ -1223,8 +1223,13 @@ static int upload_rxdata(struct via_ircc_cb *self, int iobase)
IRDA_DEBUG(2, "%s(): len=%x\n", __FUNCTION__, len);
+ if ((len - 4) < 2) {
+ self->stats.rx_dropped++;
+ return FALSE;
+ }
+
skb = dev_alloc_skb(len + 1);
- if ((skb == NULL) || ((len - 4) < 2)) {
+ if (skb == NULL) {
self->stats.rx_dropped++;
return FALSE;
}
diff --git a/drivers/net/irda/vlsi_ir.h b/drivers/net/irda/vlsi_ir.h
index a82a4ba8de4..c37f0bc4c7f 100644
--- a/drivers/net/irda/vlsi_ir.h
+++ b/drivers/net/irda/vlsi_ir.h
@@ -58,7 +58,7 @@ typedef void irqreturn_t;
/* PDE() introduced in 2.5.4 */
#ifdef CONFIG_PROC_FS
-#define PDE(inode) ((inode)->u.generic_ip)
+#define PDE(inode) ((inode)->i_private)
#endif
/* irda crc16 calculation exported in 2.5.42 */
diff --git a/drivers/net/ixgb/Makefile b/drivers/net/ixgb/Makefile
index a8a2d3d0356..838a5084fa0 100644
--- a/drivers/net/ixgb/Makefile
+++ b/drivers/net/ixgb/Makefile
@@ -1,33 +1,33 @@
################################################################################
#
-#
-# Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-#
-# This program is free software; you can redistribute it and/or modify it
-# under the terms of the GNU General Public License as published by the Free
-# Software Foundation; either version 2 of the License, or (at your option)
-# any later version.
-#
-# This program is distributed in the hope that it will be useful, but WITHOUT
-# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
-# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+# Intel PRO/10GbE Linux driver
+# Copyright(c) 1999 - 2006 Intel Corporation.
+#
+# This program is free software; you can redistribute it and/or modify it
+# under the terms and conditions of the GNU General Public License,
+# version 2, as published by the Free Software Foundation.
+#
+# This program is distributed in the hope 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.
-#
-# The full GNU General Public License is included in this distribution in the
-# file called LICENSE.
-#
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+#
+# The full GNU General Public License is included in this distribution in
+# the file called "COPYING".
+#
# Contact Information:
# Linux NICS <linux.nics@intel.com>
+# e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
# Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
#
################################################################################
#
-# Makefile for the Intel(R) PRO/10GbE driver
+# Makefile for the Intel(R) PRO/10GbE ethernet driver
#
obj-$(CONFIG_IXGB) += ixgb.o
diff --git a/drivers/net/ixgb/ixgb.h b/drivers/net/ixgb/ixgb.h
index a51604b3651..50ffe90488f 100644
--- a/drivers/net/ixgb/ixgb.h
+++ b/drivers/net/ixgb/ixgb.h
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
@@ -111,7 +111,7 @@ struct ixgb_adapter;
#define IXGB_RXBUFFER_16384 16384
/* How many Rx Buffers do we bundle into one write to the hardware ? */
-#define IXGB_RX_BUFFER_WRITE 4 /* Must be power of 2 */
+#define IXGB_RX_BUFFER_WRITE 8 /* Must be power of 2 */
/* only works for sizes that are powers of 2 */
#define IXGB_ROUNDUP(i, size) ((i) = (((i) + (size) - 1) & ~((size) - 1)))
diff --git a/drivers/net/ixgb/ixgb_ee.c b/drivers/net/ixgb/ixgb_ee.c
index 8357c5590bf..f15aebde7b9 100644
--- a/drivers/net/ixgb/ixgb_ee.c
+++ b/drivers/net/ixgb/ixgb_ee.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_ee.h b/drivers/net/ixgb/ixgb_ee.h
index bf6fa220f38..ef236b935c1 100644
--- a/drivers/net/ixgb/ixgb_ee.h
+++ b/drivers/net/ixgb/ixgb_ee.h
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_ethtool.c b/drivers/net/ixgb/ixgb_ethtool.c
index 64a383e4e89..cd22523fb03 100644
--- a/drivers/net/ixgb/ixgb_ethtool.c
+++ b/drivers/net/ixgb/ixgb_ethtool.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_hw.c b/drivers/net/ixgb/ixgb_hw.c
index acc6df7a6b3..02089b64e42 100644
--- a/drivers/net/ixgb/ixgb_hw.c
+++ b/drivers/net/ixgb/ixgb_hw.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_hw.h b/drivers/net/ixgb/ixgb_hw.h
index cb4568915ad..40ef5ca8871 100644
--- a/drivers/net/ixgb/ixgb_hw.h
+++ b/drivers/net/ixgb/ixgb_hw.h
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_ids.h b/drivers/net/ixgb/ixgb_ids.h
index 9fd61189b4b..4376e7e8fbe 100644
--- a/drivers/net/ixgb/ixgb_ids.h
+++ b/drivers/net/ixgb/ixgb_ids.h
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_main.c b/drivers/net/ixgb/ixgb_main.c
index 2e0f4b950a9..cfde7c2569b 100644
--- a/drivers/net/ixgb/ixgb_main.c
+++ b/drivers/net/ixgb/ixgb_main.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
@@ -36,7 +36,7 @@ static char ixgb_driver_string[] = "Intel(R) PRO/10GbE Network Driver";
#else
#define DRIVERNAPI "-NAPI"
#endif
-#define DRV_VERSION "1.0.112-k2"DRIVERNAPI
+#define DRV_VERSION "1.0.117-k2"DRIVERNAPI
char ixgb_driver_version[] = DRV_VERSION;
static char ixgb_copyright[] = "Copyright (c) 1999-2006 Intel Corporation.";
@@ -437,7 +437,7 @@ ixgb_probe(struct pci_dev *pdev,
netdev->poll_controller = ixgb_netpoll;
#endif
- strcpy(netdev->name, pci_name(pdev));
+ strncpy(netdev->name, pci_name(pdev), sizeof(netdev->name) - 1);
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len;
netdev->base_addr = adapter->hw.io_base;
@@ -2230,7 +2230,7 @@ static pci_ers_result_t ixgb_io_error_detected (struct pci_dev *pdev,
enum pci_channel_state state)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgb_adapter *adapter = netdev->priv;
+ struct ixgb_adapter *adapter = netdev_priv(netdev);
if(netif_running(netdev))
ixgb_down(adapter, TRUE);
@@ -2253,7 +2253,7 @@ static pci_ers_result_t ixgb_io_error_detected (struct pci_dev *pdev,
static pci_ers_result_t ixgb_io_slot_reset (struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgb_adapter *adapter = netdev->priv;
+ struct ixgb_adapter *adapter = netdev_priv(netdev);
if(pci_enable_device(pdev)) {
DPRINTK(PROBE, ERR, "Cannot re-enable PCI device after reset.\n");
@@ -2297,7 +2297,7 @@ static pci_ers_result_t ixgb_io_slot_reset (struct pci_dev *pdev)
static void ixgb_io_resume (struct pci_dev *pdev)
{
struct net_device *netdev = pci_get_drvdata(pdev);
- struct ixgb_adapter *adapter = netdev->priv;
+ struct ixgb_adapter *adapter = netdev_priv(netdev);
pci_set_master(pdev);
diff --git a/drivers/net/ixgb/ixgb_osdep.h b/drivers/net/ixgb/ixgb_osdep.h
index 19cb1d586de..8434d752fd8 100644
--- a/drivers/net/ixgb/ixgb_osdep.h
+++ b/drivers/net/ixgb/ixgb_osdep.h
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/ixgb/ixgb_param.c b/drivers/net/ixgb/ixgb_param.c
index 39fbed29a3d..b27442a121f 100644
--- a/drivers/net/ixgb/ixgb_param.c
+++ b/drivers/net/ixgb/ixgb_param.c
@@ -1,27 +1,27 @@
/*******************************************************************************
-
- Copyright(c) 1999 - 2006 Intel Corporation. All rights reserved.
-
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU General Public License as published by the Free
- Software Foundation; either version 2 of the License, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful, but WITHOUT
- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ Intel PRO/10GbE Linux driver
+ Copyright(c) 1999 - 2006 Intel Corporation.
+
+ This program is free software; you can redistribute it and/or modify it
+ under the terms and conditions of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ This program is distributed in the hope 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.
-
- The full GNU General Public License is included in this distribution in the
- file called LICENSE.
-
+ this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+
+ The full GNU General Public License is included in this distribution in
+ the file called "COPYING".
+
Contact Information:
Linux NICS <linux.nics@intel.com>
+ e1000-devel Mailing List <e1000-devel@lists.sourceforge.net>
Intel Corporation, 5200 N.E. Elam Young Parkway, Hillsboro, OR 97124-6497
*******************************************************************************/
diff --git a/drivers/net/loopback.c b/drivers/net/loopback.c
index f429b19bf62..4178b4b1d2d 100644
--- a/drivers/net/loopback.c
+++ b/drivers/net/loopback.c
@@ -161,15 +161,13 @@ static int loopback_xmit(struct sk_buff *skb, struct net_device *dev)
return(0);
}
+static struct net_device_stats loopback_stats;
+
static struct net_device_stats *get_stats(struct net_device *dev)
{
- struct net_device_stats *stats = dev->priv;
+ struct net_device_stats *stats = &loopback_stats;
int i;
- if (!stats) {
- return NULL;
- }
-
memset(stats, 0, sizeof(struct net_device_stats));
for_each_possible_cpu(i) {
@@ -185,19 +183,28 @@ static struct net_device_stats *get_stats(struct net_device *dev)
return stats;
}
-static u32 loopback_get_link(struct net_device *dev)
+static u32 always_on(struct net_device *dev)
{
return 1;
}
static const struct ethtool_ops loopback_ethtool_ops = {
- .get_link = loopback_get_link,
+ .get_link = always_on,
.get_tso = ethtool_op_get_tso,
.set_tso = ethtool_op_set_tso,
+ .get_tx_csum = always_on,
+ .get_sg = always_on,
+ .get_rx_csum = always_on,
};
+/*
+ * The loopback device is special. There is only one instance and
+ * it is statically allocated. Don't do this for other devices.
+ */
struct net_device loopback_dev = {
.name = "lo",
+ .get_stats = &get_stats,
+ .priv = &loopback_stats,
.mtu = (16 * 1024) + 20 + 20 + 12,
.hard_start_xmit = loopback_xmit,
.hard_header = eth_header,
@@ -221,16 +228,6 @@ struct net_device loopback_dev = {
/* Setup and register the loopback device. */
int __init loopback_init(void)
{
- struct net_device_stats *stats;
-
- /* Can survive without statistics */
- stats = kmalloc(sizeof(struct net_device_stats), GFP_KERNEL);
- if (stats) {
- memset(stats, 0, sizeof(struct net_device_stats));
- loopback_dev.priv = stats;
- loopback_dev.get_stats = &get_stats;
- }
-
return register_netdev(&loopback_dev);
};
diff --git a/drivers/net/ne3210.c b/drivers/net/ne3210.c
index 0fa8e4d2276..d6632897542 100644
--- a/drivers/net/ne3210.c
+++ b/drivers/net/ne3210.c
@@ -343,6 +343,7 @@ static struct eisa_device_id ne3210_ids[] = {
{ "NVL1801" },
{ "" },
};
+MODULE_DEVICE_TABLE(eisa, ne3210_ids);
static struct eisa_driver ne3210_eisa_driver = {
.id_table = ne3210_ids,
diff --git a/drivers/net/phy/fixed.c b/drivers/net/phy/fixed.c
index 19f7ee63276..94b47c8d0ab 100644
--- a/drivers/net/phy/fixed.c
+++ b/drivers/net/phy/fixed.c
@@ -289,9 +289,13 @@ static int fixed_mdio_register_device(int number, int speed, int duplex)
goto probe_fail;
}
- device_bind_driver(&phydev->dev);
+ err = device_bind_driver(&phydev->dev);
+
up_write(&phydev->dev.bus->subsys.rwsem);
+ if (err)
+ goto probe_fail;
+
return 0;
probe_fail:
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 2d1ecfdc80d..3bbd5e70c20 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -212,11 +212,13 @@ struct phy_device *phy_attach(struct net_device *dev,
err = d->driver->probe(d);
- if (err < 0)
- return ERR_PTR(err);
+ if (err >= 0)
+ err = device_bind_driver(d);
- device_bind_driver(d);
up_write(&d->bus->subsys.rwsem);
+
+ if (err)
+ return ERR_PTR(err);
}
if (phydev->attached_dev) {
@@ -522,7 +524,7 @@ EXPORT_SYMBOL(genphy_read_status);
static int genphy_config_init(struct phy_device *phydev)
{
- u32 val;
+ int val;
u32 features;
/* For now, I'll claim that the generic driver supports
diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index 5666ed99814..0adee733b76 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -600,6 +600,7 @@ static int pppoe_connect(struct socket *sock, struct sockaddr *uservaddr,
po->chan.hdrlen = (sizeof(struct pppoe_hdr) +
dev->hard_header_len);
+ po->chan.mtu = dev->mtu - sizeof(struct pppoe_hdr);
po->chan.private = sk;
po->chan.ops = &pppoe_chan_ops;
diff --git a/drivers/net/s2io.c b/drivers/net/s2io.c
index f5dbeb27b6f..1bf23e41f58 100644
--- a/drivers/net/s2io.c
+++ b/drivers/net/s2io.c
@@ -2904,7 +2904,7 @@ static void s2io_mdio_write(u32 mmd_type, u64 addr, u16 value, struct net_device
{
u64 val64 = 0x0;
nic_t *sp = dev->priv;
- XENA_dev_config_t *bar0 = (XENA_dev_config_t *)sp->bar0;
+ XENA_dev_config_t __iomem *bar0 = sp->bar0;
//address transaction
val64 = val64 | MDIO_MMD_INDX_ADDR(addr)
@@ -2953,7 +2953,7 @@ static u64 s2io_mdio_read(u32 mmd_type, u64 addr, struct net_device *dev)
u64 val64 = 0x0;
u64 rval64 = 0x0;
nic_t *sp = dev->priv;
- XENA_dev_config_t *bar0 = (XENA_dev_config_t *)sp->bar0;
+ XENA_dev_config_t __iomem *bar0 = sp->bar0;
/* address transaction */
val64 = val64 | MDIO_MMD_INDX_ADDR(addr)
@@ -3276,7 +3276,7 @@ static void alarm_intr_handler(struct s2io_nic *nic)
* SUCCESS on success and FAILURE on failure.
*/
-static int wait_for_cmd_complete(void *addr, u64 busy_bit)
+static int wait_for_cmd_complete(void __iomem *addr, u64 busy_bit)
{
int ret = FAILURE, cnt = 0;
u64 val64;
@@ -4303,11 +4303,11 @@ static struct net_device_stats *s2io_get_stats(struct net_device *dev)
sp->stats.tx_errors =
le32_to_cpu(mac_control->stats_info->tmac_any_err_frms);
sp->stats.rx_errors =
- le32_to_cpu(mac_control->stats_info->rmac_drop_frms);
+ le64_to_cpu(mac_control->stats_info->rmac_drop_frms);
sp->stats.multicast =
le32_to_cpu(mac_control->stats_info->rmac_vld_mcst_frms);
sp->stats.rx_length_errors =
- le32_to_cpu(mac_control->stats_info->rmac_long_frms);
+ le64_to_cpu(mac_control->stats_info->rmac_long_frms);
return (&sp->stats);
}
diff --git a/drivers/net/skge.c b/drivers/net/skge.c
index 9142d91355b..705e9a8fa30 100644
--- a/drivers/net/skge.c
+++ b/drivers/net/skge.c
@@ -58,6 +58,7 @@
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
#define BLINK_MS 250
+#define LINK_HZ (HZ/2)
MODULE_DESCRIPTION("SysKonnect Gigabit Ethernet driver");
MODULE_AUTHOR("Stephen Hemminger <shemminger@osdl.org>");
@@ -605,7 +606,12 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
if (hw->chip_id == CHIP_ID_GENESIS) {
switch (mode) {
case LED_MODE_OFF:
- xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_OFF);
+ else {
+ skge_write32(hw, SK_REG(port, TX_LED_VAL), 0);
+ skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_T_OFF);
+ }
skge_write8(hw, SK_REG(port, LNK_LED_REG), LINKLED_OFF);
skge_write32(hw, SK_REG(port, RX_LED_VAL), 0);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_T_OFF);
@@ -625,8 +631,14 @@ static void skge_led(struct skge_port *skge, enum led_mode mode)
skge_write32(hw, SK_REG(port, RX_LED_VAL), 100);
skge_write8(hw, SK_REG(port, RX_LED_CTRL), LED_START);
- xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
- break;
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_phy_write(hw, port, PHY_BCOM_P_EXT_CTRL, PHY_B_PEC_LED_ON);
+ else {
+ skge_write8(hw, SK_REG(port, TX_LED_TST), LED_T_ON);
+ skge_write32(hw, SK_REG(port, TX_LED_VAL), 100);
+ skge_write8(hw, SK_REG(port, TX_LED_CTRL), LED_START);
+ }
+
}
} else {
switch (mode) {
@@ -879,6 +891,9 @@ static int __xm_phy_read(struct skge_hw *hw, int port, u16 reg, u16 *val)
xm_write16(hw, port, XM_PHY_ADDR, reg | hw->phy_addr);
*val = xm_read16(hw, port, XM_PHY_DATA);
+ if (hw->phy_type == SK_PHY_XMAC)
+ goto ready;
+
for (i = 0; i < PHY_RETRIES; i++) {
if (xm_read16(hw, port, XM_MMU_CMD) & XM_MMU_PHY_RDY)
goto ready;
@@ -965,7 +980,8 @@ static void genesis_reset(struct skge_hw *hw, int port)
xm_write16(hw, port, XM_RX_CMD, 0); /* reset RX CMD Reg */
/* disable Broadcom PHY IRQ */
- xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
+ if (hw->phy_type == SK_PHY_BCOM)
+ xm_write16(hw, port, PHY_BCOM_INT_MASK, 0xffff);
xm_outhash(hw, port, XM_HSM, zero);
}
@@ -1000,60 +1016,64 @@ static void bcom_check_link(struct skge_hw *hw, int port)
if (netif_carrier_ok(dev))
skge_link_down(skge);
- } else {
- if (skge->autoneg == AUTONEG_ENABLE &&
- (status & PHY_ST_AN_OVER)) {
- u16 lpa = xm_phy_read(hw, port, PHY_BCOM_AUNE_LP);
- u16 aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
-
- if (lpa & PHY_B_AN_RF) {
- printk(KERN_NOTICE PFX "%s: remote fault\n",
- dev->name);
- return;
- }
+ return;
+ }
- /* Check Duplex mismatch */
- switch (aux & PHY_B_AS_AN_RES_MSK) {
- case PHY_B_RES_1000FD:
- skge->duplex = DUPLEX_FULL;
- break;
- case PHY_B_RES_1000HD:
- skge->duplex = DUPLEX_HALF;
- break;
- default:
- printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
- dev->name);
- return;
- }
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ u16 lpa, aux;
+ if (!(status & PHY_ST_AN_OVER))
+ return;
- /* We are using IEEE 802.3z/D5.0 Table 37-4 */
- switch (aux & PHY_B_AS_PAUSE_MSK) {
- case PHY_B_AS_PAUSE_MSK:
- skge->flow_control = FLOW_MODE_SYMMETRIC;
- break;
- case PHY_B_AS_PRR:
- skge->flow_control = FLOW_MODE_REM_SEND;
- break;
- case PHY_B_AS_PRT:
- skge->flow_control = FLOW_MODE_LOC_SEND;
- break;
- default:
- skge->flow_control = FLOW_MODE_NONE;
- }
+ lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+ if (lpa & PHY_B_AN_RF) {
+ printk(KERN_NOTICE PFX "%s: remote fault\n",
+ dev->name);
+ return;
+ }
- skge->speed = SPEED_1000;
+ aux = xm_phy_read(hw, port, PHY_BCOM_AUX_STAT);
+
+ /* Check Duplex mismatch */
+ switch (aux & PHY_B_AS_AN_RES_MSK) {
+ case PHY_B_RES_1000FD:
+ skge->duplex = DUPLEX_FULL;
+ break;
+ case PHY_B_RES_1000HD:
+ skge->duplex = DUPLEX_HALF;
+ break;
+ default:
+ printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
+ dev->name);
+ return;
}
- if (!netif_carrier_ok(dev))
- genesis_link_up(skge);
+
+ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
+ switch (aux & PHY_B_AS_PAUSE_MSK) {
+ case PHY_B_AS_PAUSE_MSK:
+ skge->flow_control = FLOW_MODE_SYMMETRIC;
+ break;
+ case PHY_B_AS_PRR:
+ skge->flow_control = FLOW_MODE_REM_SEND;
+ break;
+ case PHY_B_AS_PRT:
+ skge->flow_control = FLOW_MODE_LOC_SEND;
+ break;
+ default:
+ skge->flow_control = FLOW_MODE_NONE;
+ }
+ skge->speed = SPEED_1000;
}
+
+ if (!netif_carrier_ok(dev))
+ genesis_link_up(skge);
}
/* Broadcom 5400 only supports giagabit! SysKonnect did not put an additional
* Phy on for 100 or 10Mbit operation
*/
-static void bcom_phy_init(struct skge_port *skge, int jumbo)
+static void bcom_phy_init(struct skge_port *skge)
{
struct skge_hw *hw = skge->hw;
int port = skge->port;
@@ -1144,7 +1164,7 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
phy_pause_map[skge->flow_control] | PHY_AN_CSMA);
/* Handle Jumbo frames */
- if (jumbo) {
+ if (hw->dev[port]->mtu > ETH_DATA_LEN) {
xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
PHY_B_AC_TX_TST | PHY_B_AC_LONG_PACK);
@@ -1157,8 +1177,154 @@ static void bcom_phy_init(struct skge_port *skge, int jumbo)
/* Use link status change interrupt */
xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+}
- bcom_check_link(hw, port);
+static void xm_phy_init(struct skge_port *skge)
+{
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+ u16 ctrl = 0;
+
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ if (skge->advertising & ADVERTISED_1000baseT_Half)
+ ctrl |= PHY_X_AN_HD;
+ if (skge->advertising & ADVERTISED_1000baseT_Full)
+ ctrl |= PHY_X_AN_FD;
+
+ switch(skge->flow_control) {
+ case FLOW_MODE_NONE:
+ ctrl |= PHY_X_P_NO_PAUSE;
+ break;
+ case FLOW_MODE_LOC_SEND:
+ ctrl |= PHY_X_P_ASYM_MD;
+ break;
+ case FLOW_MODE_SYMMETRIC:
+ ctrl |= PHY_X_P_BOTH_MD;
+ break;
+ }
+
+ xm_phy_write(hw, port, PHY_XMAC_AUNE_ADV, ctrl);
+
+ /* Restart Auto-negotiation */
+ ctrl = PHY_CT_ANE | PHY_CT_RE_CFG;
+ } else {
+ /* Set DuplexMode in Config register */
+ if (skge->duplex == DUPLEX_FULL)
+ ctrl |= PHY_CT_DUP_MD;
+ /*
+ * Do NOT enable Auto-negotiation here. This would hold
+ * the link down because no IDLEs are transmitted
+ */
+ }
+
+ xm_phy_write(hw, port, PHY_XMAC_CTRL, ctrl);
+
+ /* Poll PHY for status changes */
+ schedule_delayed_work(&skge->link_thread, LINK_HZ);
+}
+
+static void xm_check_link(struct net_device *dev)
+{
+ struct skge_port *skge = netdev_priv(dev);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+ u16 status;
+
+ /* read twice because of latch */
+ (void) xm_phy_read(hw, port, PHY_XMAC_STAT);
+ status = xm_phy_read(hw, port, PHY_XMAC_STAT);
+
+ if ((status & PHY_ST_LSYNC) == 0) {
+ u16 cmd = xm_read16(hw, port, XM_MMU_CMD);
+ cmd &= ~(XM_MMU_ENA_RX | XM_MMU_ENA_TX);
+ xm_write16(hw, port, XM_MMU_CMD, cmd);
+ /* dummy read to ensure writing */
+ (void) xm_read16(hw, port, XM_MMU_CMD);
+
+ if (netif_carrier_ok(dev))
+ skge_link_down(skge);
+ return;
+ }
+
+ if (skge->autoneg == AUTONEG_ENABLE) {
+ u16 lpa, res;
+
+ if (!(status & PHY_ST_AN_OVER))
+ return;
+
+ lpa = xm_phy_read(hw, port, PHY_XMAC_AUNE_LP);
+ if (lpa & PHY_B_AN_RF) {
+ printk(KERN_NOTICE PFX "%s: remote fault\n",
+ dev->name);
+ return;
+ }
+
+ res = xm_phy_read(hw, port, PHY_XMAC_RES_ABI);
+
+ /* Check Duplex mismatch */
+ switch (res & (PHY_X_RS_HD | PHY_X_RS_FD)) {
+ case PHY_X_RS_FD:
+ skge->duplex = DUPLEX_FULL;
+ break;
+ case PHY_X_RS_HD:
+ skge->duplex = DUPLEX_HALF;
+ break;
+ default:
+ printk(KERN_NOTICE PFX "%s: duplex mismatch\n",
+ dev->name);
+ return;
+ }
+
+ /* We are using IEEE 802.3z/D5.0 Table 37-4 */
+ if (lpa & PHY_X_P_SYM_MD)
+ skge->flow_control = FLOW_MODE_SYMMETRIC;
+ else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_ASYM_MD)
+ skge->flow_control = FLOW_MODE_REM_SEND;
+ else if ((lpa & PHY_X_RS_PAUSE) == PHY_X_P_BOTH_MD)
+ skge->flow_control = FLOW_MODE_LOC_SEND;
+ else
+ skge->flow_control = FLOW_MODE_NONE;
+
+
+ skge->speed = SPEED_1000;
+ }
+
+ if (!netif_carrier_ok(dev))
+ genesis_link_up(skge);
+}
+
+/* Poll to check for link coming up.
+ * Since internal PHY is wired to a level triggered pin, can't
+ * get an interrupt when carrier is detected.
+ */
+static void xm_link_timer(void *arg)
+{
+ struct net_device *dev = arg;
+ struct skge_port *skge = netdev_priv(arg);
+ struct skge_hw *hw = skge->hw;
+ int port = skge->port;
+
+ if (!netif_running(dev))
+ return;
+
+ if (netif_carrier_ok(dev)) {
+ xm_read16(hw, port, XM_ISRC);
+ if (!(xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS))
+ goto nochange;
+ } else {
+ if (xm_read32(hw, port, XM_GP_PORT) & XM_GP_INP_ASS)
+ goto nochange;
+ xm_read16(hw, port, XM_ISRC);
+ if (xm_read16(hw, port, XM_ISRC) & XM_IS_INP_ASS)
+ goto nochange;
+ }
+
+ mutex_lock(&hw->phy_mutex);
+ xm_check_link(dev);
+ mutex_unlock(&hw->phy_mutex);
+
+nochange:
+ schedule_delayed_work(&skge->link_thread, LINK_HZ);
}
static void genesis_mac_init(struct skge_hw *hw, int port)
@@ -1189,20 +1355,29 @@ static void genesis_mac_init(struct skge_hw *hw, int port)
* namely for the 1000baseTX cards that use the XMAC's
* GMII mode.
*/
- /* Take external Phy out of reset */
- r = skge_read32(hw, B2_GP_IO);
- if (port == 0)
- r |= GP_DIR_0|GP_IO_0;
- else
- r |= GP_DIR_2|GP_IO_2;
+ if (hw->phy_type != SK_PHY_XMAC) {
+ /* Take external Phy out of reset */
+ r = skge_read32(hw, B2_GP_IO);
+ if (port == 0)
+ r |= GP_DIR_0|GP_IO_0;
+ else
+ r |= GP_DIR_2|GP_IO_2;
- skge_write32(hw, B2_GP_IO, r);
+ skge_write32(hw, B2_GP_IO, r);
+ /* Enable GMII interface */
+ xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
+ }
- /* Enable GMII interface */
- xm_write16(hw, port, XM_HW_CFG, XM_HW_GMII_MD);
- bcom_phy_init(skge, jumbo);
+ switch(hw->phy_type) {
+ case SK_PHY_XMAC:
+ xm_phy_init(skge);
+ break;
+ case SK_PHY_BCOM:
+ bcom_phy_init(skge);
+ bcom_check_link(hw, port);
+ }
/* Set Station Address */
xm_outaddr(hw, port, XM_SA, dev->dev_addr);
@@ -1335,16 +1510,18 @@ static void genesis_stop(struct skge_port *skge)
skge_write16(hw, SK_REG(port, TX_MFF_CTRL1), MFF_SET_MAC_RST);
/* For external PHYs there must be special handling */
- reg = skge_read32(hw, B2_GP_IO);
- if (port == 0) {
- reg |= GP_DIR_0;
- reg &= ~GP_IO_0;
- } else {
- reg |= GP_DIR_2;
- reg &= ~GP_IO_2;
+ if (hw->phy_type != SK_PHY_XMAC) {
+ reg = skge_read32(hw, B2_GP_IO);
+ if (port == 0) {
+ reg |= GP_DIR_0;
+ reg &= ~GP_IO_0;
+ } else {
+ reg |= GP_DIR_2;
+ reg &= ~GP_IO_2;
+ }
+ skge_write32(hw, B2_GP_IO, reg);
+ skge_read32(hw, B2_GP_IO);
}
- skge_write32(hw, B2_GP_IO, reg);
- skge_read32(hw, B2_GP_IO);
xm_write16(hw, port, XM_MMU_CMD,
xm_read16(hw, port, XM_MMU_CMD)
@@ -1406,7 +1583,7 @@ static void genesis_link_up(struct skge_port *skge)
struct skge_hw *hw = skge->hw;
int port = skge->port;
u16 cmd;
- u32 mode, msk;
+ u32 mode;
cmd = xm_read16(hw, port, XM_MMU_CMD);
@@ -1454,27 +1631,24 @@ static void genesis_link_up(struct skge_port *skge)
}
xm_write32(hw, port, XM_MODE, mode);
-
- msk = XM_DEF_MSK;
- /* disable GP0 interrupt bit for external Phy */
- msk |= XM_IS_INP_ASS;
-
- xm_write16(hw, port, XM_IMSK, msk);
+ xm_write16(hw, port, XM_IMSK, XM_DEF_MSK);
xm_read16(hw, port, XM_ISRC);
/* get MMU Command Reg. */
cmd = xm_read16(hw, port, XM_MMU_CMD);
- if (skge->duplex == DUPLEX_FULL)
+ if (hw->phy_type != SK_PHY_XMAC && skge->duplex == DUPLEX_FULL)
cmd |= XM_MMU_GMII_FD;
/*
* Workaround BCOM Errata (#10523) for all BCom Phys
* Enable Power Management after link up
*/
- xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
- xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
- & ~PHY_B_AC_DIS_PM);
- xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+ if (hw->phy_type == SK_PHY_BCOM) {
+ xm_phy_write(hw, port, PHY_BCOM_AUX_CTRL,
+ xm_phy_read(hw, port, PHY_BCOM_AUX_CTRL)
+ & ~PHY_B_AC_DIS_PM);
+ xm_phy_write(hw, port, PHY_BCOM_INT_MASK, PHY_B_DEF_MSK);
+ }
/* enable Rx/Tx */
xm_write16(hw, port, XM_MMU_CMD,
@@ -2240,6 +2414,8 @@ static int skge_down(struct net_device *dev)
printk(KERN_INFO PFX "%s: disabling interface\n", dev->name);
netif_stop_queue(dev);
+ if (hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC)
+ cancel_rearming_delayed_work(&skge->link_thread);
skge_write8(skge->hw, SK_REG(skge->port, LNK_LED_REG), LED_OFF);
if (hw->chip_id == CHIP_ID_GENESIS)
@@ -2862,7 +3038,7 @@ static void skge_extirq(void *arg)
if (netif_running(dev)) {
if (hw->chip_id != CHIP_ID_GENESIS)
yukon_phy_intr(skge);
- else
+ else if (hw->phy_type == SK_PHY_BCOM)
bcom_phy_intr(skge);
}
}
@@ -3014,7 +3190,7 @@ static int skge_reset(struct skge_hw *hw)
{
u32 reg;
u16 ctst, pci_status;
- u8 t8, mac_cfg, pmd_type, phy_type;
+ u8 t8, mac_cfg, pmd_type;
int i;
ctst = skge_read16(hw, B0_CTST);
@@ -3038,19 +3214,22 @@ static int skge_reset(struct skge_hw *hw)
ctst & (CS_CLK_RUN_HOT|CS_CLK_RUN_RST|CS_CLK_RUN_ENA));
hw->chip_id = skge_read8(hw, B2_CHIP_ID);
- phy_type = skge_read8(hw, B2_E_1) & 0xf;
+ hw->phy_type = skge_read8(hw, B2_E_1) & 0xf;
pmd_type = skge_read8(hw, B2_PMD_TYP);
hw->copper = (pmd_type == 'T' || pmd_type == '1');
switch (hw->chip_id) {
case CHIP_ID_GENESIS:
- switch (phy_type) {
+ switch (hw->phy_type) {
+ case SK_PHY_XMAC:
+ hw->phy_addr = PHY_ADDR_XMAC;
+ break;
case SK_PHY_BCOM:
hw->phy_addr = PHY_ADDR_BCOM;
break;
default:
printk(KERN_ERR PFX "%s: unsupported phy type 0x%x\n",
- pci_name(hw->pdev), phy_type);
+ pci_name(hw->pdev), hw->phy_type);
return -EOPNOTSUPP;
}
break;
@@ -3058,7 +3237,7 @@ static int skge_reset(struct skge_hw *hw)
case CHIP_ID_YUKON:
case CHIP_ID_YUKON_LITE:
case CHIP_ID_YUKON_LP:
- if (phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
+ if (hw->phy_type < SK_PHY_MARV_COPPER && pmd_type != 'S')
hw->copper = 1;
hw->phy_addr = PHY_ADDR_MARV;
@@ -3089,10 +3268,13 @@ static int skge_reset(struct skge_hw *hw)
else
hw->ram_size = t8 * 4096;
- hw->intr_mask = IS_HW_ERR | IS_EXT_REG | IS_PORT_1;
+ hw->intr_mask = IS_HW_ERR | IS_PORT_1;
if (hw->ports > 1)
hw->intr_mask |= IS_PORT_2;
+ if (!(hw->chip_id == CHIP_ID_GENESIS && hw->phy_type == SK_PHY_XMAC))
+ hw->intr_mask |= IS_EXT_REG;
+
if (hw->chip_id == CHIP_ID_GENESIS)
genesis_init(hw);
else {
@@ -3226,6 +3408,9 @@ static struct net_device *skge_devinit(struct skge_hw *hw, int port,
skge->port = port;
+ /* Only used for Genesis XMAC */
+ INIT_WORK(&skge->link_thread, xm_link_timer, dev);
+
if (hw->chip_id != CHIP_ID_GENESIS) {
dev->features |= NETIF_F_IP_CSUM | NETIF_F_SG;
skge->rx_csum = 1;
diff --git a/drivers/net/skge.h b/drivers/net/skge.h
index 79e09271bcf..d0b47d46cf9 100644
--- a/drivers/net/skge.h
+++ b/drivers/net/skge.h
@@ -934,7 +934,7 @@ enum {
PHY_XMAC_AUNE_ADV = 0x04,/* 16 bit r/w Auto-Neg. Advertisement */
PHY_XMAC_AUNE_LP = 0x05,/* 16 bit r/o Link Partner Abi Reg */
PHY_XMAC_AUNE_EXP = 0x06,/* 16 bit r/o Auto-Neg. Expansion Reg */
- PHY_XMAC_NEPG = 0x07,/* 16 bit r/w Next Page Register */
+ PHY_XMAC_NEPG = 0x07,/* 16 bit r/w Next Page Register */
PHY_XMAC_NEPG_LP = 0x08,/* 16 bit r/o Next Page Link Partner */
PHY_XMAC_EXT_STAT = 0x0f,/* 16 bit r/o Ext Status Register */
@@ -1097,13 +1097,36 @@ enum {
/* Pause Bits (PHY_X_AN_PAUSE and PHY_X_RS_PAUSE) encoding */
enum {
- PHY_X_P_NO_PAUSE = 0<<7,/* Bit 8..7: no Pause Mode */
+ PHY_X_P_NO_PAUSE= 0<<7,/* Bit 8..7: no Pause Mode */
PHY_X_P_SYM_MD = 1<<7, /* Bit 8..7: symmetric Pause Mode */
PHY_X_P_ASYM_MD = 2<<7,/* Bit 8..7: asymmetric Pause Mode */
PHY_X_P_BOTH_MD = 3<<7,/* Bit 8..7: both Pause Mode */
};
+/***** PHY_XMAC_EXT_STAT 16 bit r/w Extended Status Register *****/
+enum {
+ PHY_X_EX_FD = 1<<15, /* Bit 15: Device Supports Full Duplex */
+ PHY_X_EX_HD = 1<<14, /* Bit 14: Device Supports Half Duplex */
+};
+
+/***** PHY_XMAC_RES_ABI 16 bit r/o PHY Resolved Ability *****/
+enum {
+ PHY_X_RS_PAUSE = 3<<7, /* Bit 8..7: selected Pause Mode */
+ PHY_X_RS_HD = 1<<6, /* Bit 6: Half Duplex Mode selected */
+ PHY_X_RS_FD = 1<<5, /* Bit 5: Full Duplex Mode selected */
+ PHY_X_RS_ABLMIS = 1<<4, /* Bit 4: duplex or pause cap mismatch */
+ PHY_X_RS_PAUMIS = 1<<3, /* Bit 3: pause capability mismatch */
+};
+
+/* Remote Fault Bits (PHY_X_AN_RFB) encoding */
+enum {
+ X_RFB_OK = 0<<12,/* Bit 13..12 No errors, Link OK */
+ X_RFB_LF = 1<<12,/* Bit 13..12 Link Failure */
+ X_RFB_OFF = 2<<12,/* Bit 13..12 Offline */
+ X_RFB_AN_ERR = 3<<12,/* Bit 13..12 Auto-Negotiation Error */
+};
+
/* Broadcom-Specific */
/***** PHY_BCOM_1000T_CTRL 16 bit r/w 1000Base-T Control Reg *****/
enum {
@@ -2158,8 +2181,8 @@ enum {
XM_IS_LNK_AE = 1<<14, /* Bit 14: Link Asynchronous Event */
XM_IS_TX_ABORT = 1<<13, /* Bit 13: Transmit Abort, late Col. etc */
XM_IS_FRC_INT = 1<<12, /* Bit 12: Force INT bit set in GP */
- XM_IS_INP_ASS = 1<<11, /* Bit 11: Input Asserted, GP bit 0 set */
- XM_IS_LIPA_RC = 1<<10, /* Bit 10: Link Partner requests config */
+ XM_IS_INP_ASS = 1<<11, /* Bit 11: Input Asserted, GP bit 0 set */
+ XM_IS_LIPA_RC = 1<<10, /* Bit 10: Link Partner requests config */
XM_IS_RX_PAGE = 1<<9, /* Bit 9: Page Received */
XM_IS_TX_PAGE = 1<<8, /* Bit 8: Next Page Loaded for Transmit */
XM_IS_AND = 1<<7, /* Bit 7: Auto-Negotiation Done */
@@ -2172,9 +2195,7 @@ enum {
XM_IS_RX_COMP = 1<<0, /* Bit 0: Frame Rx Complete */
};
-#define XM_DEF_MSK (~(XM_IS_INP_ASS | XM_IS_LIPA_RC | XM_IS_RX_PAGE | \
- XM_IS_AND | XM_IS_RXC_OV | XM_IS_TXC_OV | \
- XM_IS_RXF_OV | XM_IS_TXF_UR))
+#define XM_DEF_MSK (~(XM_IS_RXC_OV | XM_IS_TXC_OV | XM_IS_RXF_OV | XM_IS_TXF_UR))
/* XM_HW_CFG 16 bit r/w Hardware Config Register */
@@ -2396,6 +2417,7 @@ struct skge_hw {
u8 chip_rev;
u8 copper;
u8 ports;
+ u8 phy_type;
u32 ram_size;
u32 ram_offset;
@@ -2422,6 +2444,7 @@ struct skge_port {
struct net_device_stats net_stats;
+ struct work_struct link_thread;
u8 rx_csum;
u8 blink_on;
u8 flow_control;
diff --git a/drivers/net/sky2.c b/drivers/net/sky2.c
index 7eeefa2d6c8..396e7df3c61 100644
--- a/drivers/net/sky2.c
+++ b/drivers/net/sky2.c
@@ -50,19 +50,18 @@
#include "sky2.h"
#define DRV_NAME "sky2"
-#define DRV_VERSION "1.7"
+#define DRV_VERSION "1.9"
#define PFX DRV_NAME " "
/*
* The Yukon II chipset takes 64 bit command blocks (called list elements)
* that are organized into three (receive, transmit, status) different rings
- * similar to Tigon3. A transmit can require several elements;
- * a receive requires one (or two if using 64 bit dma).
+ * similar to Tigon3.
*/
-#define RX_LE_SIZE 512
+#define RX_LE_SIZE 1024
#define RX_LE_BYTES (RX_LE_SIZE*sizeof(struct sky2_rx_le))
-#define RX_MAX_PENDING (RX_LE_SIZE/2 - 2)
+#define RX_MAX_PENDING (RX_LE_SIZE/6 - 2)
#define RX_DEF_PENDING RX_MAX_PENDING
#define RX_SKB_ALIGN 8
#define RX_BUF_WRITE 16
@@ -74,7 +73,6 @@
#define STATUS_RING_SIZE 2048 /* 2 ports * (TX + 2*RX) */
#define STATUS_LE_BYTES (STATUS_RING_SIZE*sizeof(struct sky2_status_le))
-#define ETH_JUMBO_MTU 9000
#define TX_WATCHDOG (5 * HZ)
#define NAPI_WEIGHT 64
#define PHY_RETRIES 1000
@@ -90,7 +88,7 @@ static int debug = -1; /* defaults above */
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,...,16=all)");
-static int copybreak __read_mostly = 256;
+static int copybreak __read_mostly = 128;
module_param(copybreak, int, 0);
MODULE_PARM_DESC(copybreak, "Receive copy threshold");
@@ -769,9 +767,16 @@ static inline struct sky2_tx_le *get_tx_le(struct sky2_port *sky2)
struct sky2_tx_le *le = sky2->tx_le + sky2->tx_prod;
sky2->tx_prod = RING_NEXT(sky2->tx_prod, TX_RING_SIZE);
+ le->ctrl = 0;
return le;
}
+static inline struct tx_ring_info *tx_le_re(struct sky2_port *sky2,
+ struct sky2_tx_le *le)
+{
+ return sky2->tx_ring + (le - sky2->tx_le);
+}
+
/* Update chip's next pointer */
static inline void sky2_put_idx(struct sky2_hw *hw, unsigned q, u16 idx)
{
@@ -786,6 +791,7 @@ static inline struct sky2_rx_le *sky2_next_rx(struct sky2_port *sky2)
{
struct sky2_rx_le *le = sky2->rx_le + sky2->rx_put;
sky2->rx_put = RING_NEXT(sky2->rx_put, RX_LE_SIZE);
+ le->ctrl = 0;
return le;
}
@@ -795,17 +801,16 @@ static inline u32 high32(dma_addr_t a)
return sizeof(a) > sizeof(u32) ? (a >> 16) >> 16 : 0;
}
-/* Build description to hardware about buffer */
-static void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map)
+/* Build description to hardware for one receive segment */
+static void sky2_rx_add(struct sky2_port *sky2, u8 op,
+ dma_addr_t map, unsigned len)
{
struct sky2_rx_le *le;
u32 hi = high32(map);
- u16 len = sky2->rx_bufsize;
if (sky2->rx_addr64 != hi) {
le = sky2_next_rx(sky2);
le->addr = cpu_to_le32(hi);
- le->ctrl = 0;
le->opcode = OP_ADDR64 | HW_OWNER;
sky2->rx_addr64 = high32(map + len);
}
@@ -813,11 +818,53 @@ static void sky2_rx_add(struct sky2_port *sky2, dma_addr_t map)
le = sky2_next_rx(sky2);
le->addr = cpu_to_le32((u32) map);
le->length = cpu_to_le16(len);
- le->ctrl = 0;
- le->opcode = OP_PACKET | HW_OWNER;
+ le->opcode = op | HW_OWNER;
+}
+
+/* Build description to hardware for one possibly fragmented skb */
+static void sky2_rx_submit(struct sky2_port *sky2,
+ const struct rx_ring_info *re)
+{
+ int i;
+
+ sky2_rx_add(sky2, OP_PACKET, re->data_addr, sky2->rx_data_size);
+
+ for (i = 0; i < skb_shinfo(re->skb)->nr_frags; i++)
+ sky2_rx_add(sky2, OP_BUFFER, re->frag_addr[i], PAGE_SIZE);
}
+static void sky2_rx_map_skb(struct pci_dev *pdev, struct rx_ring_info *re,
+ unsigned size)
+{
+ struct sk_buff *skb = re->skb;
+ int i;
+
+ re->data_addr = pci_map_single(pdev, skb->data, size, PCI_DMA_FROMDEVICE);
+ pci_unmap_len_set(re, data_size, size);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+ re->frag_addr[i] = pci_map_page(pdev,
+ skb_shinfo(skb)->frags[i].page,
+ skb_shinfo(skb)->frags[i].page_offset,
+ skb_shinfo(skb)->frags[i].size,
+ PCI_DMA_FROMDEVICE);
+}
+
+static void sky2_rx_unmap_skb(struct pci_dev *pdev, struct rx_ring_info *re)
+{
+ struct sk_buff *skb = re->skb;
+ int i;
+
+ pci_unmap_single(pdev, re->data_addr, pci_unmap_len(re, data_size),
+ PCI_DMA_FROMDEVICE);
+
+ for (i = 0; i < skb_shinfo(skb)->nr_frags; i++)
+ pci_unmap_page(pdev, re->frag_addr[i],
+ skb_shinfo(skb)->frags[i].size,
+ PCI_DMA_FROMDEVICE);
+}
+
/* Tell chip where to start receive checksum.
* Actually has two checksums, but set both same to avoid possible byte
* order problems.
@@ -877,12 +924,10 @@ static void sky2_rx_clean(struct sky2_port *sky2)
memset(sky2->rx_le, 0, RX_LE_BYTES);
for (i = 0; i < sky2->rx_pending; i++) {
- struct ring_info *re = sky2->rx_ring + i;
+ struct rx_ring_info *re = sky2->rx_ring + i;
if (re->skb) {
- pci_unmap_single(sky2->hw->pdev,
- re->mapaddr, sky2->rx_bufsize,
- PCI_DMA_FROMDEVICE);
+ sky2_rx_unmap_skb(sky2->hw->pdev, re);
kfree_skb(re->skb);
re->skb = NULL;
}
@@ -936,13 +981,13 @@ static void sky2_vlan_rx_register(struct net_device *dev, struct vlan_group *grp
struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port;
- spin_lock_bh(&sky2->tx_lock);
+ netif_tx_lock_bh(dev);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_ON);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_ON);
sky2->vlgrp = grp;
- spin_unlock_bh(&sky2->tx_lock);
+ netif_tx_unlock_bh(dev);
}
static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
@@ -951,50 +996,69 @@ static void sky2_vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
struct sky2_hw *hw = sky2->hw;
u16 port = sky2->port;
- spin_lock_bh(&sky2->tx_lock);
+ netif_tx_lock_bh(dev);
sky2_write32(hw, SK_REG(port, RX_GMF_CTRL_T), RX_VLAN_STRIP_OFF);
sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T), TX_VLAN_TAG_OFF);
if (sky2->vlgrp)
sky2->vlgrp->vlan_devices[vid] = NULL;
- spin_unlock_bh(&sky2->tx_lock);
+ netif_tx_unlock_bh(dev);
}
#endif
/*
+ * Allocate an skb for receiving. If the MTU is large enough
+ * make the skb non-linear with a fragment list of pages.
+ *
* It appears the hardware has a bug in the FIFO logic that
* cause it to hang if the FIFO gets overrun and the receive buffer
* is not 64 byte aligned. The buffer returned from netdev_alloc_skb is
* aligned except if slab debugging is enabled.
*/
-static inline struct sk_buff *sky2_alloc_skb(struct net_device *dev,
- unsigned int length,
- gfp_t gfp_mask)
+static struct sk_buff *sky2_rx_alloc(struct sky2_port *sky2)
{
struct sk_buff *skb;
+ unsigned long p;
+ int i;
- skb = __netdev_alloc_skb(dev, length + RX_SKB_ALIGN, gfp_mask);
- if (likely(skb)) {
- unsigned long p = (unsigned long) skb->data;
- skb_reserve(skb, ALIGN(p, RX_SKB_ALIGN) - p);
+ skb = netdev_alloc_skb(sky2->netdev, sky2->rx_data_size + RX_SKB_ALIGN);
+ if (!skb)
+ goto nomem;
+
+ p = (unsigned long) skb->data;
+ skb_reserve(skb, ALIGN(p, RX_SKB_ALIGN) - p);
+
+ for (i = 0; i < sky2->rx_nfrags; i++) {
+ struct page *page = alloc_page(GFP_ATOMIC);
+
+ if (!page)
+ goto free_partial;
+ skb_fill_page_desc(skb, i, page, 0, PAGE_SIZE);
}
return skb;
+free_partial:
+ kfree_skb(skb);
+nomem:
+ return NULL;
}
/*
* Allocate and setup receiver buffer pool.
- * In case of 64 bit dma, there are 2X as many list elements
- * available as ring entries
- * and need to reserve one list element so we don't wrap around.
+ * Normal case this ends up creating one list element for skb
+ * in the receive ring. Worst case if using large MTU and each
+ * allocation falls on a different 64 bit region, that results
+ * in 6 list elements per ring entry.
+ * One element is used for checksum enable/disable, and one
+ * extra to avoid wrap.
*/
static int sky2_rx_start(struct sky2_port *sky2)
{
struct sky2_hw *hw = sky2->hw;
+ struct rx_ring_info *re;
unsigned rxq = rxqaddr[sky2->port];
- int i;
- unsigned thresh;
+ unsigned i, size, space, thresh;
sky2->rx_put = sky2->rx_next = 0;
sky2_qset(hw, rxq);
@@ -1007,27 +1071,56 @@ static int sky2_rx_start(struct sky2_port *sky2)
sky2_prefetch_init(hw, rxq, sky2->rx_le_map, RX_LE_SIZE - 1);
rx_set_checksum(sky2);
+
+ /* Space needed for frame data + headers rounded up */
+ size = ALIGN(sky2->netdev->mtu + ETH_HLEN + VLAN_HLEN, 8)
+ + 8;
+
+ /* Stopping point for hardware truncation */
+ thresh = (size - 8) / sizeof(u32);
+
+ /* Account for overhead of skb - to avoid order > 0 allocation */
+ space = SKB_DATA_ALIGN(size) + NET_SKB_PAD
+ + sizeof(struct skb_shared_info);
+
+ sky2->rx_nfrags = space >> PAGE_SHIFT;
+ BUG_ON(sky2->rx_nfrags > ARRAY_SIZE(re->frag_addr));
+
+ if (sky2->rx_nfrags != 0) {
+ /* Compute residue after pages */
+ space = sky2->rx_nfrags << PAGE_SHIFT;
+
+ if (space < size)
+ size -= space;
+ else
+ size = 0;
+
+ /* Optimize to handle small packets and headers */
+ if (size < copybreak)
+ size = copybreak;
+ if (size < ETH_HLEN)
+ size = ETH_HLEN;
+ }
+ sky2->rx_data_size = size;
+
+ /* Fill Rx ring */
for (i = 0; i < sky2->rx_pending; i++) {
- struct ring_info *re = sky2->rx_ring + i;
+ re = sky2->rx_ring + i;
- re->skb = sky2_alloc_skb(sky2->netdev, sky2->rx_bufsize,
- GFP_KERNEL);
+ re->skb = sky2_rx_alloc(sky2);
if (!re->skb)
goto nomem;
- re->mapaddr = pci_map_single(hw->pdev, re->skb->data,
- sky2->rx_bufsize, PCI_DMA_FROMDEVICE);
- sky2_rx_add(sky2, re->mapaddr);
+ sky2_rx_map_skb(hw->pdev, re, sky2->rx_data_size);
+ sky2_rx_submit(sky2, re);
}
-
/*
* The receiver hangs if it receives frames larger than the
* packet buffer. As a workaround, truncate oversize frames, but
* the register is limited to 9 bits, so if you do frames > 2052
* you better get the MTU right!
*/
- thresh = (sky2->rx_bufsize - 8) / sizeof(u32);
if (thresh > 0x1ff)
sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_OFF);
else {
@@ -1035,7 +1128,6 @@ static int sky2_rx_start(struct sky2_port *sky2)
sky2_write32(hw, SK_REG(sky2->port, RX_GMF_CTRL_T), RX_TRUNC_ON);
}
-
/* Tell chip about available buffers */
sky2_write16(hw, Y2_QADDR(rxq, PREF_UNIT_PUT_IDX), sky2->rx_put);
return 0;
@@ -1094,7 +1186,7 @@ static int sky2_up(struct net_device *dev)
goto err_out;
memset(sky2->rx_le, 0, RX_LE_BYTES);
- sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct ring_info),
+ sky2->rx_ring = kcalloc(sky2->rx_pending, sizeof(struct rx_ring_info),
GFP_KERNEL);
if (!sky2->rx_ring)
goto err_out;
@@ -1124,7 +1216,8 @@ static int sky2_up(struct net_device *dev)
sky2_qset(hw, txqaddr[port]);
/* Set almost empty threshold */
- if (hw->chip_id == CHIP_ID_YUKON_EC_U && hw->chip_rev == 1)
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U
+ && hw->chip_rev == CHIP_REV_YU_EC_U_A0)
sky2_write16(hw, Q_ADDR(txqaddr[port], Q_AL), 0x1a0);
sky2_prefetch_init(hw, txqaddr[port], sky2->tx_le_map,
@@ -1195,8 +1288,6 @@ static unsigned tx_le_req(const struct sk_buff *skb)
* A single packet can generate multiple list elements, and
* the number of ring elements will probably be less than the number
* of list elements used.
- *
- * No BH disabling for tx_lock here (like tg3)
*/
static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
{
@@ -1210,27 +1301,8 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
u16 mss;
u8 ctrl;
- /* No BH disabling for tx_lock here. We are running in BH disabled
- * context and TX reclaim runs via poll inside of a software
- * interrupt, and no related locks in IRQ processing.
- */
- if (!spin_trylock(&sky2->tx_lock))
- return NETDEV_TX_LOCKED;
-
- if (unlikely(tx_avail(sky2) < tx_le_req(skb))) {
- /* There is a known but harmless race with lockless tx
- * and netif_stop_queue.
- */
- if (!netif_queue_stopped(dev)) {
- netif_stop_queue(dev);
- if (net_ratelimit())
- printk(KERN_WARNING PFX "%s: ring full when queue awake!\n",
- dev->name);
- }
- spin_unlock(&sky2->tx_lock);
-
- return NETDEV_TX_BUSY;
- }
+ if (unlikely(tx_avail(sky2) < tx_le_req(skb)))
+ return NETDEV_TX_BUSY;
if (unlikely(netif_msg_tx_queued(sky2)))
printk(KERN_DEBUG "%s: tx queued, slot %u, len %d\n",
@@ -1240,13 +1312,10 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
mapping = pci_map_single(hw->pdev, skb->data, len, PCI_DMA_TODEVICE);
addr64 = high32(mapping);
- re = sky2->tx_ring + sky2->tx_prod;
-
/* Send high bits if changed or crosses boundary */
if (addr64 != sky2->tx_addr64 || high32(mapping + len) != sky2->tx_addr64) {
le = get_tx_le(sky2);
le->addr = cpu_to_le32(addr64);
- le->ctrl = 0;
le->opcode = OP_ADDR64 | HW_OWNER;
sky2->tx_addr64 = high32(mapping + len);
}
@@ -1262,7 +1331,6 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
le = get_tx_le(sky2);
le->addr = cpu_to_le32(mss);
le->opcode = OP_LRGLEN | HW_OWNER;
- le->ctrl = 0;
sky2->tx_last_mss = mss;
}
}
@@ -1275,7 +1343,6 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
le = get_tx_le(sky2);
le->addr = 0;
le->opcode = OP_VLAN|HW_OWNER;
- le->ctrl = 0;
} else
le->opcode |= OP_VLAN;
le->length = cpu_to_be16(vlan_tx_tag_get(skb));
@@ -1312,13 +1379,13 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
le->ctrl = ctrl;
le->opcode = mss ? (OP_LARGESEND | HW_OWNER) : (OP_PACKET | HW_OWNER);
- /* Record the transmit mapping info */
+ re = tx_le_re(sky2, le);
re->skb = skb;
pci_unmap_addr_set(re, mapaddr, mapping);
+ pci_unmap_len_set(re, maplen, len);
for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
- struct tx_ring_info *fre;
+ const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
mapping = pci_map_page(hw->pdev, frag->page, frag->page_offset,
frag->size, PCI_DMA_TODEVICE);
@@ -1337,12 +1404,12 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
le->ctrl = ctrl;
le->opcode = OP_BUFFER | HW_OWNER;
- fre = sky2->tx_ring
- + RING_NEXT((re - sky2->tx_ring) + i, TX_RING_SIZE);
- pci_unmap_addr_set(fre, mapaddr, mapping);
+ re = tx_le_re(sky2, le);
+ re->skb = skb;
+ pci_unmap_addr_set(re, mapaddr, mapping);
+ pci_unmap_len_set(re, maplen, frag->size);
}
- re->idx = sky2->tx_prod;
le->ctrl |= EOP;
if (tx_avail(sky2) <= MAX_SKB_TX_LE)
@@ -1350,8 +1417,6 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
sky2_put_idx(hw, txqaddr[sky2->port], sky2->tx_prod);
- spin_unlock(&sky2->tx_lock);
-
dev->trans_start = jiffies;
return NETDEV_TX_OK;
}
@@ -1360,59 +1425,59 @@ static int sky2_xmit_frame(struct sk_buff *skb, struct net_device *dev)
* Free ring elements from starting at tx_cons until "done"
*
* NB: the hardware will tell us about partial completion of multi-part
- * buffers; these are deferred until completion.
+ * buffers so make sure not to free skb to early.
*/
static void sky2_tx_complete(struct sky2_port *sky2, u16 done)
{
struct net_device *dev = sky2->netdev;
struct pci_dev *pdev = sky2->hw->pdev;
- u16 nxt, put;
- unsigned i;
+ unsigned idx;
BUG_ON(done >= TX_RING_SIZE);
- if (unlikely(netif_msg_tx_done(sky2)))
- printk(KERN_DEBUG "%s: tx done, up to %u\n",
- dev->name, done);
-
- for (put = sky2->tx_cons; put != done; put = nxt) {
- struct tx_ring_info *re = sky2->tx_ring + put;
- struct sk_buff *skb = re->skb;
-
- nxt = re->idx;
- BUG_ON(nxt >= TX_RING_SIZE);
- prefetch(sky2->tx_ring + nxt);
-
- /* Check for partial status */
- if (tx_dist(put, done) < tx_dist(put, nxt))
+ for (idx = sky2->tx_cons; idx != done;
+ idx = RING_NEXT(idx, TX_RING_SIZE)) {
+ struct sky2_tx_le *le = sky2->tx_le + idx;
+ struct tx_ring_info *re = sky2->tx_ring + idx;
+
+ switch(le->opcode & ~HW_OWNER) {
+ case OP_LARGESEND:
+ case OP_PACKET:
+ pci_unmap_single(pdev,
+ pci_unmap_addr(re, mapaddr),
+ pci_unmap_len(re, maplen),
+ PCI_DMA_TODEVICE);
break;
-
- skb = re->skb;
- pci_unmap_single(pdev, pci_unmap_addr(re, mapaddr),
- skb_headlen(skb), PCI_DMA_TODEVICE);
-
- for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
- struct tx_ring_info *fre;
- fre = sky2->tx_ring + RING_NEXT(put + i, TX_RING_SIZE);
- pci_unmap_page(pdev, pci_unmap_addr(fre, mapaddr),
- skb_shinfo(skb)->frags[i].size,
+ case OP_BUFFER:
+ pci_unmap_page(pdev, pci_unmap_addr(re, mapaddr),
+ pci_unmap_len(re, maplen),
PCI_DMA_TODEVICE);
+ break;
}
- dev_kfree_skb(skb);
+ if (le->ctrl & EOP) {
+ if (unlikely(netif_msg_tx_done(sky2)))
+ printk(KERN_DEBUG "%s: tx done %u\n",
+ dev->name, idx);
+ dev_kfree_skb(re->skb);
+ }
+
+ le->opcode = 0; /* paranoia */
}
- sky2->tx_cons = put;
+ sky2->tx_cons = idx;
if (tx_avail(sky2) > MAX_SKB_TX_LE + 4)
netif_wake_queue(dev);
}
/* Cleanup all untransmitted buffers, assume transmitter not running */
-static void sky2_tx_clean(struct sky2_port *sky2)
+static void sky2_tx_clean(struct net_device *dev)
{
- spin_lock_bh(&sky2->tx_lock);
+ struct sky2_port *sky2 = netdev_priv(dev);
+
+ netif_tx_lock_bh(dev);
sky2_tx_complete(sky2, sky2->tx_prod);
- spin_unlock_bh(&sky2->tx_lock);
+ netif_tx_unlock_bh(dev);
}
/* Network shutdown */
@@ -1443,6 +1508,13 @@ static int sky2_down(struct net_device *dev)
sky2_write32(hw, RB_ADDR(txqaddr[port], RB_CTRL),
RB_RST_SET | RB_DIS_OP_MD);
+ /* WA for dev. #4.209 */
+ if (hw->chip_id == CHIP_ID_YUKON_EC_U
+ && hw->chip_rev == CHIP_REV_YU_EC_U_A1)
+ sky2_write32(hw, SK_REG(port, TX_GMF_CTRL_T),
+ sky2->speed != SPEED_1000 ?
+ TX_STFW_ENA : TX_STFW_DIS);
+
ctrl = gma_read16(hw, port, GM_GP_CTRL);
ctrl &= ~(GM_GPCR_TX_ENA | GM_GPCR_RX_ENA);
gma_write16(hw, port, GM_GP_CTRL, ctrl);
@@ -1489,7 +1561,7 @@ static int sky2_down(struct net_device *dev)
synchronize_irq(hw->pdev->irq);
- sky2_tx_clean(sky2);
+ sky2_tx_clean(dev);
sky2_rx_clean(sky2);
pci_free_consistent(hw->pdev, RX_LE_BYTES,
@@ -1624,22 +1696,33 @@ static int sky2_autoneg_done(struct sky2_port *sky2, u16 aux)
return -1;
}
- if (hw->chip_id != CHIP_ID_YUKON_FE &&
- gm_phy_read(hw, port, PHY_MARV_1000T_STAT) & PHY_B_1000S_MSF) {
- printk(KERN_ERR PFX "%s: master/slave fault",
- sky2->netdev->name);
- return -1;
- }
-
if (!(aux & PHY_M_PS_SPDUP_RES)) {
printk(KERN_ERR PFX "%s: speed/duplex mismatch",
sky2->netdev->name);
return -1;
}
- sky2->duplex = (aux & PHY_M_PS_FULL_DUP) ? DUPLEX_FULL : DUPLEX_HALF;
-
sky2->speed = sky2_phy_speed(hw, aux);
+ if (sky2->speed == SPEED_1000) {
+ u16 ctl2 = gm_phy_read(hw, port, PHY_MARV_1000T_CTRL);
+ u16 lpa2 = gm_phy_read(hw, port, PHY_MARV_1000T_STAT);
+ if (lpa2 & PHY_B_1000S_MSF) {
+ printk(KERN_ERR PFX "%s: master/slave fault",
+ sky2->netdev->name);
+ return -1;
+ }
+
+ if ((ctl2 & PHY_M_1000C_AFD) && (lpa2 & PHY_B_1000S_LP_FD))
+ sky2->duplex = DUPLEX_FULL;
+ else
+ sky2->duplex = DUPLEX_HALF;
+ } else {
+ u16 adv = gm_phy_read(hw, port, PHY_MARV_AUNE_ADV);
+ if ((aux & adv) & PHY_AN_FULL)
+ sky2->duplex = DUPLEX_FULL;
+ else
+ sky2->duplex = DUPLEX_HALF;
+ }
/* Pause bits are offset (9..8) */
if (hw->chip_id == CHIP_ID_YUKON_XL || hw->chip_id == CHIP_ID_YUKON_EC_U)
@@ -1730,31 +1813,22 @@ static void sky2_tx_timeout(struct net_device *dev)
} else if (report != sky2->tx_cons) {
printk(KERN_INFO PFX "status report lost?\n");
- spin_lock_bh(&sky2->tx_lock);
+ netif_tx_lock_bh(dev);
sky2_tx_complete(sky2, report);
- spin_unlock_bh(&sky2->tx_lock);
+ netif_tx_unlock_bh(dev);
} else {
printk(KERN_INFO PFX "hardware hung? flushing\n");
sky2_write32(hw, Q_ADDR(txq, Q_CSR), BMU_STOP);
sky2_write32(hw, Y2_QADDR(txq, PREF_UNIT_CTRL), PREF_UNIT_RST_SET);
- sky2_tx_clean(sky2);
+ sky2_tx_clean(dev);
sky2_qset(hw, txq);
sky2_prefetch_init(hw, txq, sky2->tx_le_map, TX_RING_SIZE - 1);
}
}
-
-/* Want receive buffer size to be multiple of 64 bits
- * and incl room for vlan and truncation
- */
-static inline unsigned sky2_buf_size(int mtu)
-{
- return ALIGN(mtu + ETH_HLEN + VLAN_HLEN, 8) + 8;
-}
-
static int sky2_change_mtu(struct net_device *dev, int new_mtu)
{
struct sky2_port *sky2 = netdev_priv(dev);
@@ -1789,7 +1863,7 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
sky2_rx_clean(sky2);
dev->mtu = new_mtu;
- sky2->rx_bufsize = sky2_buf_size(new_mtu);
+
mode = DATA_BLIND_VAL(DATA_BLIND_DEF) |
GM_SMOD_VLAN_ENA | IPG_DATA_VAL(IPG_DATA_DEF);
@@ -1815,16 +1889,100 @@ static int sky2_change_mtu(struct net_device *dev, int new_mtu)
return err;
}
+/* For small just reuse existing skb for next receive */
+static struct sk_buff *receive_copy(struct sky2_port *sky2,
+ const struct rx_ring_info *re,
+ unsigned length)
+{
+ struct sk_buff *skb;
+
+ skb = netdev_alloc_skb(sky2->netdev, length + 2);
+ if (likely(skb)) {
+ skb_reserve(skb, 2);
+ pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->data_addr,
+ length, PCI_DMA_FROMDEVICE);
+ memcpy(skb->data, re->skb->data, length);
+ skb->ip_summed = re->skb->ip_summed;
+ skb->csum = re->skb->csum;
+ pci_dma_sync_single_for_device(sky2->hw->pdev, re->data_addr,
+ length, PCI_DMA_FROMDEVICE);
+ re->skb->ip_summed = CHECKSUM_NONE;
+ __skb_put(skb, length);
+ }
+ return skb;
+}
+
+/* Adjust length of skb with fragments to match received data */
+static void skb_put_frags(struct sk_buff *skb, unsigned int hdr_space,
+ unsigned int length)
+{
+ int i, num_frags;
+ unsigned int size;
+
+ /* put header into skb */
+ size = min(length, hdr_space);
+ skb->tail += size;
+ skb->len += size;
+ length -= size;
+
+ num_frags = skb_shinfo(skb)->nr_frags;
+ for (i = 0; i < num_frags; i++) {
+ skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
+
+ if (length == 0) {
+ /* don't need this page */
+ __free_page(frag->page);
+ --skb_shinfo(skb)->nr_frags;
+ } else {
+ size = min(length, (unsigned) PAGE_SIZE);
+
+ frag->size = size;
+ skb->data_len += size;
+ skb->truesize += size;
+ skb->len += size;
+ length -= size;
+ }
+ }
+}
+
+/* Normal packet - take skb from ring element and put in a new one */
+static struct sk_buff *receive_new(struct sky2_port *sky2,
+ struct rx_ring_info *re,
+ unsigned int length)
+{
+ struct sk_buff *skb, *nskb;
+ unsigned hdr_space = sky2->rx_data_size;
+
+ pr_debug(PFX "receive new length=%d\n", length);
+
+ /* Don't be tricky about reusing pages (yet) */
+ nskb = sky2_rx_alloc(sky2);
+ if (unlikely(!nskb))
+ return NULL;
+
+ skb = re->skb;
+ sky2_rx_unmap_skb(sky2->hw->pdev, re);
+
+ prefetch(skb->data);
+ re->skb = nskb;
+ sky2_rx_map_skb(sky2->hw->pdev, re, hdr_space);
+
+ if (skb_shinfo(skb)->nr_frags)
+ skb_put_frags(skb, hdr_space, length);
+ else
+ skb_put(skb, hdr_space);
+ return skb;
+}
+
/*
* Receive one packet.
- * For small packets or errors, just reuse existing skb.
* For larger packets, get new buffer.
*/
static struct sk_buff *sky2_receive(struct net_device *dev,
u16 length, u32 status)
{
struct sky2_port *sky2 = netdev_priv(dev);
- struct ring_info *re = sky2->rx_ring + sky2->rx_next;
+ struct rx_ring_info *re = sky2->rx_ring + sky2->rx_next;
struct sk_buff *skb = NULL;
if (unlikely(netif_msg_rx_status(sky2)))
@@ -1843,40 +2001,12 @@ static struct sk_buff *sky2_receive(struct net_device *dev,
if (length > dev->mtu + ETH_HLEN)
goto oversize;
- if (length < copybreak) {
- skb = netdev_alloc_skb(dev, length + 2);
- if (!skb)
- goto resubmit;
-
- skb_reserve(skb, 2);
- pci_dma_sync_single_for_cpu(sky2->hw->pdev, re->mapaddr,
- length, PCI_DMA_FROMDEVICE);
- memcpy(skb->data, re->skb->data, length);
- skb->ip_summed = re->skb->ip_summed;
- skb->csum = re->skb->csum;
- pci_dma_sync_single_for_device(sky2->hw->pdev, re->mapaddr,
- length, PCI_DMA_FROMDEVICE);
- } else {
- struct sk_buff *nskb;
-
- nskb = sky2_alloc_skb(dev, sky2->rx_bufsize, GFP_ATOMIC);
- if (!nskb)
- goto resubmit;
-
- skb = re->skb;
- re->skb = nskb;
- pci_unmap_single(sky2->hw->pdev, re->mapaddr,
- sky2->rx_bufsize, PCI_DMA_FROMDEVICE);
- prefetch(skb->data);
-
- re->mapaddr = pci_map_single(sky2->hw->pdev, nskb->data,
- sky2->rx_bufsize, PCI_DMA_FROMDEVICE);
- }
-
- skb_put(skb, length);
+ if (length < copybreak)
+ skb = receive_copy(sky2, re, length);
+ else
+ skb = receive_new(sky2, re, length);
resubmit:
- re->skb->ip_summed = CHECKSUM_NONE;
- sky2_rx_add(sky2, re->mapaddr);
+ sky2_rx_submit(sky2, re);
return skb;
@@ -1909,9 +2039,9 @@ static inline void sky2_tx_done(struct net_device *dev, u16 last)
struct sky2_port *sky2 = netdev_priv(dev);
if (netif_running(dev)) {
- spin_lock(&sky2->tx_lock);
+ netif_tx_lock(dev);
sky2_tx_complete(sky2, last);
- spin_unlock(&sky2->tx_lock);
+ netif_tx_unlock(dev);
}
}
@@ -2082,7 +2212,7 @@ static void sky2_hw_intr(struct sky2_hw *hw)
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
sky2_pci_write16(hw, PCI_STATUS,
- pci_err | PCI_STATUS_ERROR_BITS);
+ pci_err | PCI_STATUS_ERROR_BITS);
sky2_write8(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
}
@@ -2090,7 +2220,8 @@ static void sky2_hw_intr(struct sky2_hw *hw)
/* PCI-Express uncorrectable Error occurred */
u32 pex_err;
- pex_err = sky2_pci_read32(hw, PEX_UNC_ERR_STAT);
+ pex_err = sky2_pci_read32(hw,
+ hw->err_cap + PCI_ERR_UNCOR_STATUS);
if (net_ratelimit())
printk(KERN_ERR PFX "%s: pci express error (0x%x)\n",
@@ -2098,15 +2229,20 @@ static void sky2_hw_intr(struct sky2_hw *hw)
/* clear the interrupt */
sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_ON);
- sky2_pci_write32(hw, PEX_UNC_ERR_STAT,
- 0xffffffffUL);
+ sky2_pci_write32(hw,
+ hw->err_cap + PCI_ERR_UNCOR_STATUS,
+ 0xffffffffUL);
sky2_write32(hw, B2_TST_CTRL1, TST_CFG_WRITE_OFF);
- if (pex_err & PEX_FATAL_ERRORS) {
+
+ /* In case of fatal error mask off to keep from getting stuck */
+ if (pex_err & (PCI_ERR_UNC_POISON_TLP | PCI_ERR_UNC_FCP
+ | PCI_ERR_UNC_DLP)) {
u32 hwmsk = sky2_read32(hw, B0_HWE_IMSK);
hwmsk &= ~Y2_IS_PCI_EXP;
sky2_write32(hw, B0_HWE_IMSK, hwmsk);
}
+
}
if (status & Y2_HWE_L1_MASK)
@@ -2287,6 +2423,7 @@ static int sky2_reset(struct sky2_hw *hw)
u16 status;
u8 t8;
int i;
+ u32 msk;
sky2_write8(hw, B0_CTST, CS_RST_CLR);
@@ -2327,9 +2464,13 @@ static int sky2_reset(struct sky2_hw *hw)
sky2_write8(hw, B0_CTST, CS_MRST_CLR);
/* clear any PEX errors */
- if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP))
- sky2_pci_write32(hw, PEX_UNC_ERR_STAT, 0xffffffffUL);
-
+ if (pci_find_capability(hw->pdev, PCI_CAP_ID_EXP)) {
+ hw->err_cap = pci_find_ext_capability(hw->pdev, PCI_EXT_CAP_ID_ERR);
+ if (hw->err_cap)
+ sky2_pci_write32(hw,
+ hw->err_cap + PCI_ERR_UNCOR_STATUS,
+ 0xffffffffUL);
+ }
hw->pmd_type = sky2_read8(hw, B2_PMD_TYP);
hw->ports = 1;
@@ -2386,7 +2527,10 @@ static int sky2_reset(struct sky2_hw *hw)
sky2_write8(hw, RAM_BUFFER(i, B3_RI_RTO_XS2), SK_RI_TO_53);
}
- sky2_write32(hw, B0_HWE_IMSK, Y2_HWE_ALL_MASK);
+ msk = Y2_HWE_ALL_MASK;
+ if (!hw->err_cap)
+ msk &= ~Y2_IS_PCI_EXP;
+ sky2_write32(hw, B0_HWE_IMSK, msk);
for (i = 0; i < hw->ports; i++)
sky2_gmac_reset(hw, i);
@@ -3102,7 +3246,6 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
sky2->hw = hw;
sky2->msg_enable = netif_msg_init(debug, default_msg);
- spin_lock_init(&sky2->tx_lock);
/* Auto speed and flow control */
sky2->autoneg = AUTONEG_ENABLE;
sky2->tx_pause = 1;
@@ -3115,13 +3258,11 @@ static __devinit struct net_device *sky2_init_netdev(struct sky2_hw *hw,
spin_lock_init(&sky2->phy_lock);
sky2->tx_pending = TX_DEF_PENDING;
sky2->rx_pending = RX_DEF_PENDING;
- sky2->rx_bufsize = sky2_buf_size(ETH_DATA_LEN);
hw->dev[port] = dev;
sky2->port = port;
- dev->features |= NETIF_F_LLTX;
if (hw->chip_id != CHIP_ID_YUKON_EC_U)
dev->features |= NETIF_F_TSO;
if (highmem)
@@ -3316,6 +3457,14 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
if (!dev)
goto err_out_free_pci;
+ if (!disable_msi && pci_enable_msi(pdev) == 0) {
+ err = sky2_test_msi(hw);
+ if (err == -EOPNOTSUPP)
+ pci_disable_msi(pdev);
+ else if (err)
+ goto err_out_free_netdev;
+ }
+
err = register_netdev(dev);
if (err) {
printk(KERN_ERR PFX "%s: cannot register net device\n",
@@ -3323,6 +3472,14 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
goto err_out_free_netdev;
}
+ err = request_irq(pdev->irq, sky2_intr, IRQF_SHARED, dev->name, hw);
+ if (err) {
+ printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
+ pci_name(pdev), pdev->irq);
+ goto err_out_unregister;
+ }
+ sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
+
sky2_show_addr(dev);
if (hw->ports > 1 && (dev1 = sky2_init_netdev(hw, 1, using_dac))) {
@@ -3337,23 +3494,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
}
}
- if (!disable_msi && pci_enable_msi(pdev) == 0) {
- err = sky2_test_msi(hw);
- if (err == -EOPNOTSUPP)
- pci_disable_msi(pdev);
- else if (err)
- goto err_out_unregister;
- }
-
- err = request_irq(pdev->irq, sky2_intr, IRQF_SHARED, DRV_NAME, hw);
- if (err) {
- printk(KERN_ERR PFX "%s: cannot assign irq %d\n",
- pci_name(pdev), pdev->irq);
- goto err_out_unregister;
- }
-
- sky2_write32(hw, B0_IMSK, Y2_IS_BASE);
-
setup_timer(&hw->idle_timer, sky2_idle, (unsigned long) hw);
sky2_idle_start(hw);
@@ -3363,10 +3503,6 @@ static int __devinit sky2_probe(struct pci_dev *pdev,
err_out_unregister:
pci_disable_msi(pdev);
- if (dev1) {
- unregister_netdev(dev1);
- free_netdev(dev1);
- }
unregister_netdev(dev);
err_out_free_netdev:
free_netdev(dev);
diff --git a/drivers/net/sky2.h b/drivers/net/sky2.h
index 4c13c371bc2..f66109a96d9 100644
--- a/drivers/net/sky2.h
+++ b/drivers/net/sky2.h
@@ -4,24 +4,17 @@
#ifndef _SKY2_H
#define _SKY2_H
-/* PCI config registers */
+#define ETH_JUMBO_MTU 9000 /* Maximum MTU supported */
+
+/* PCI device specific config registers */
enum {
PCI_DEV_REG1 = 0x40,
PCI_DEV_REG2 = 0x44,
- PCI_DEV_STATUS = 0x7c,
PCI_DEV_REG3 = 0x80,
PCI_DEV_REG4 = 0x84,
PCI_DEV_REG5 = 0x88,
};
-enum {
- PEX_DEV_CAP = 0xe4,
- PEX_DEV_CTRL = 0xe8,
- PEX_DEV_STA = 0xea,
- PEX_LNK_STAT = 0xf2,
- PEX_UNC_ERR_STAT= 0x104,
-};
-
/* Yukon-2 */
enum pci_dev_reg_1 {
PCI_Y2_PIG_ENA = 1<<31, /* Enable Plug-in-Go (YUKON-2) */
@@ -70,39 +63,6 @@ enum pci_dev_reg_4 {
PCI_STATUS_REC_MASTER_ABORT | \
PCI_STATUS_REC_TARGET_ABORT | \
PCI_STATUS_PARITY)
-
-enum pex_dev_ctrl {
- PEX_DC_MAX_RRS_MSK = 7<<12, /* Bit 14..12: Max. Read Request Size */
- PEX_DC_EN_NO_SNOOP = 1<<11,/* Enable No Snoop */
- PEX_DC_EN_AUX_POW = 1<<10,/* Enable AUX Power */
- PEX_DC_EN_PHANTOM = 1<<9, /* Enable Phantom Functions */
- PEX_DC_EN_EXT_TAG = 1<<8, /* Enable Extended Tag Field */
- PEX_DC_MAX_PLS_MSK = 7<<5, /* Bit 7.. 5: Max. Payload Size Mask */
- PEX_DC_EN_REL_ORD = 1<<4, /* Enable Relaxed Ordering */
- PEX_DC_EN_UNS_RQ_RP = 1<<3, /* Enable Unsupported Request Reporting */
- PEX_DC_EN_FAT_ER_RP = 1<<2, /* Enable Fatal Error Reporting */
- PEX_DC_EN_NFA_ER_RP = 1<<1, /* Enable Non-Fatal Error Reporting */
- PEX_DC_EN_COR_ER_RP = 1<<0, /* Enable Correctable Error Reporting */
-};
-#define PEX_DC_MAX_RD_RQ_SIZE(x) (((x)<<12) & PEX_DC_MAX_RRS_MSK)
-
-/* PEX_UNC_ERR_STAT PEX Uncorrectable Errors Status Register (Yukon-2) */
-enum pex_err {
- PEX_UNSUP_REQ = 1<<20, /* Unsupported Request Error */
-
- PEX_MALFOR_TLP = 1<<18, /* Malformed TLP */
-
- PEX_UNEXP_COMP = 1<<16, /* Unexpected Completion */
-
- PEX_COMP_TO = 1<<14, /* Completion Timeout */
- PEX_FLOW_CTRL_P = 1<<13, /* Flow Control Protocol Error */
- PEX_POIS_TLP = 1<<12, /* Poisoned TLP */
-
- PEX_DATA_LINK_P = 1<<4, /* Data Link Protocol Error */
- PEX_FATAL_ERRORS= (PEX_MALFOR_TLP | PEX_FLOW_CTRL_P | PEX_DATA_LINK_P),
-};
-
-
enum csr_regs {
B0_RAP = 0x0000,
B0_CTST = 0x0004,
@@ -1816,12 +1776,14 @@ struct sky2_status_le {
struct tx_ring_info {
struct sk_buff *skb;
DECLARE_PCI_UNMAP_ADDR(mapaddr);
- u16 idx;
+ DECLARE_PCI_UNMAP_ADDR(maplen);
};
-struct ring_info {
+struct rx_ring_info {
struct sk_buff *skb;
- dma_addr_t mapaddr;
+ dma_addr_t data_addr;
+ DECLARE_PCI_UNMAP_ADDR(data_size);
+ dma_addr_t frag_addr[ETH_JUMBO_MTU >> PAGE_SHIFT];
};
struct sky2_port {
@@ -1831,7 +1793,6 @@ struct sky2_port {
u32 msg_enable;
spinlock_t phy_lock;
- spinlock_t tx_lock ____cacheline_aligned_in_smp;
struct tx_ring_info *tx_ring;
struct sky2_tx_le *tx_le;
u16 tx_cons; /* next le to check */
@@ -1841,13 +1802,15 @@ struct sky2_port {
u16 tx_last_mss;
u32 tx_tcpsum;
- struct ring_info *rx_ring ____cacheline_aligned_in_smp;
+ struct rx_ring_info *rx_ring ____cacheline_aligned_in_smp;
struct sky2_rx_le *rx_le;
u32 rx_addr64;
u16 rx_next; /* next re to check */
u16 rx_put; /* next le index to use */
u16 rx_pending;
- u16 rx_bufsize;
+ u16 rx_data_size;
+ u16 rx_nfrags;
+
#ifdef SKY2_VLAN_TAG_USED
u16 rx_tag;
struct vlan_group *vlgrp;
@@ -1873,6 +1836,7 @@ struct sky2_hw {
struct net_device *dev[2];
int pm_cap;
+ int err_cap;
u8 chip_id;
u8 chip_rev;
u8 pmd_type;
diff --git a/drivers/net/smc91x.h b/drivers/net/smc91x.h
index 7aa7fbac822..fedd1a37bc3 100644
--- a/drivers/net/smc91x.h
+++ b/drivers/net/smc91x.h
@@ -195,6 +195,7 @@ SMC_outw(u16 val, void __iomem *ioaddr, int reg)
#define SMC_IRQ_FLAGS (( \
machine_is_omap_h2() \
|| machine_is_omap_h3() \
+ || machine_is_omap_h4() \
|| (machine_is_omap_innovator() && !cpu_is_omap1510()) \
) ? IRQF_TRIGGER_FALLING : IRQF_TRIGGER_RISING)
@@ -379,6 +380,24 @@ static inline void LPD7_SMC_outsw (unsigned char* a, int r,
#define SMC_IRQ_FLAGS (0)
+#elif defined(CONFIG_ARCH_VERSATILE)
+
+#define SMC_CAN_USE_8BIT 1
+#define SMC_CAN_USE_16BIT 1
+#define SMC_CAN_USE_32BIT 1
+#define SMC_NOWAIT 1
+
+#define SMC_inb(a, r) readb((a) + (r))
+#define SMC_inw(a, r) readw((a) + (r))
+#define SMC_inl(a, r) readl((a) + (r))
+#define SMC_outb(v, a, r) writeb(v, (a) + (r))
+#define SMC_outw(v, a, r) writew(v, (a) + (r))
+#define SMC_outl(v, a, r) writel(v, (a) + (r))
+#define SMC_insl(a, r, p, l) readsl((a) + (r), p, l)
+#define SMC_outsl(a, r, p, l) writesl((a) + (r), p, l)
+
+#define SMC_IRQ_FLAGS (0)
+
#else
#define SMC_CAN_USE_8BIT 1
diff --git a/drivers/net/spider_net.c b/drivers/net/spider_net.c
index cc240adb726..1397fc55cf6 100644
--- a/drivers/net/spider_net.c
+++ b/drivers/net/spider_net.c
@@ -317,7 +317,7 @@ spider_net_init_chain(struct spider_net_card *card,
SPIDER_NET_DESCR_SIZE,
direction);
- if (buf == DMA_ERROR_CODE)
+ if (pci_dma_mapping_error(buf))
goto iommu_error;
descr->bus_addr = buf;
@@ -420,7 +420,7 @@ spider_net_prepare_rx_descr(struct spider_net_card *card,
buf = pci_map_single(card->pdev, descr->skb->data,
SPIDER_NET_MAX_FRAME, PCI_DMA_FROMDEVICE);
descr->buf_addr = buf;
- if (buf == DMA_ERROR_CODE) {
+ if (pci_dma_mapping_error(buf)) {
dev_kfree_skb_any(descr->skb);
if (netif_msg_rx_err(card) && net_ratelimit())
pr_err("Could not iommu-map rx buffer\n");
@@ -649,7 +649,7 @@ spider_net_prepare_tx_descr(struct spider_net_card *card,
dma_addr_t buf;
buf = pci_map_single(card->pdev, skb->data, skb->len, PCI_DMA_TODEVICE);
- if (buf == DMA_ERROR_CODE) {
+ if (pci_dma_mapping_error(buf)) {
if (netif_msg_tx_err(card) && net_ratelimit())
pr_err("could not iommu-map packet (%p, %i). "
"Dropping packet\n", skb->data, skb->len);
diff --git a/drivers/net/stnic.c b/drivers/net/stnic.c
index 3fd7a4fee66..e6f90427160 100644
--- a/drivers/net/stnic.c
+++ b/drivers/net/stnic.c
@@ -19,7 +19,7 @@
#include <asm/system.h>
#include <asm/io.h>
-#include <asm/se/se.h>
+#include <asm/se.h>
#include <asm/machvec.h>
#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
diff --git a/drivers/net/tg3.c b/drivers/net/tg3.c
index aaf45b907a7..c25ba273b74 100644
--- a/drivers/net/tg3.c
+++ b/drivers/net/tg3.c
@@ -68,8 +68,8 @@
#define DRV_MODULE_NAME "tg3"
#define PFX DRV_MODULE_NAME ": "
-#define DRV_MODULE_VERSION "3.65"
-#define DRV_MODULE_RELDATE "August 07, 2006"
+#define DRV_MODULE_VERSION "3.66"
+#define DRV_MODULE_RELDATE "September 23, 2006"
#define TG3_DEF_MAC_MODE 0
#define TG3_DEF_RX_MODE 0
@@ -173,6 +173,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5705F)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5720)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5721)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5722)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5751)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5750M)},
@@ -187,6 +188,7 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5754M)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5755M)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5756)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5786)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5787M)},
@@ -197,6 +199,8 @@ static struct pci_device_id tg3_pci_tbl[] = {
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5780S)},
{PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5781)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906)},
+ {PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, PCI_DEVICE_ID_TIGON3_5906M)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9DXX)},
{PCI_DEVICE(PCI_VENDOR_ID_SYSKONNECT, PCI_DEVICE_ID_SYSKONNECT_9MXX)},
{PCI_DEVICE(PCI_VENDOR_ID_ALTIMA, PCI_DEVICE_ID_ALTIMA_AC1000)},
@@ -424,6 +428,16 @@ static void tg3_write32_tx_mbox(struct tg3 *tp, u32 off, u32 val)
readl(mbox);
}
+static u32 tg3_read32_mbox_5906(struct tg3 *tp, u32 off)
+{
+ return (readl(tp->regs + off + GRCMBOX_BASE));
+}
+
+static void tg3_write32_mbox_5906(struct tg3 *tp, u32 off, u32 val)
+{
+ writel(val, tp->regs + off + GRCMBOX_BASE);
+}
+
#define tw32_mailbox(reg, val) tp->write32_mbox(tp, reg, val)
#define tw32_mailbox_f(reg, val) tw32_mailbox_flush(tp, (reg), (val))
#define tw32_rx_mbox(reg, val) tp->write32_rx_mbox(tp, reg, val)
@@ -439,6 +453,10 @@ static void tg3_write_mem(struct tg3 *tp, u32 off, u32 val)
{
unsigned long flags;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) &&
+ (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC))
+ return;
+
spin_lock_irqsave(&tp->indirect_lock, flags);
if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) {
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
@@ -460,6 +478,12 @@ static void tg3_read_mem(struct tg3 *tp, u32 off, u32 *val)
{
unsigned long flags;
+ if ((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) &&
+ (off >= NIC_SRAM_STATS_BLK) && (off < NIC_SRAM_TX_BUFFER_DESC)) {
+ *val = 0;
+ return;
+ }
+
spin_lock_irqsave(&tp->indirect_lock, flags);
if (tp->tg3_flags & TG3_FLAG_SRAM_USE_CONFIG) {
pci_write_config_dword(tp->pdev, TG3PCI_MEM_WIN_BASE_ADDR, off);
@@ -489,6 +513,9 @@ static inline void tg3_cond_int(struct tg3 *tp)
if (!(tp->tg3_flags & TG3_FLAG_TAGGED_STATUS) &&
(tp->hw_status->status & SD_STATUS_UPDATED))
tw32(GRC_LOCAL_CTRL, tp->grc_local_ctrl | GRC_LCLCTRL_SETINT);
+ else
+ tw32(HOSTCC_MODE, tp->coalesce_mode |
+ (HOSTCC_MODE_ENABLE | HOSTCC_MODE_NOW));
}
static void tg3_enable_ints(struct tg3 *tp)
@@ -654,6 +681,10 @@ static int tg3_writephy(struct tg3 *tp, int reg, u32 val)
unsigned int loops;
int ret;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 &&
+ (reg == MII_TG3_CTRL || reg == MII_TG3_AUX_CTRL))
+ return 0;
+
if ((tp->mi_mode & MAC_MI_MODE_AUTO_POLL) != 0) {
tw32_f(MAC_MI_MODE,
(tp->mi_mode & ~MAC_MI_MODE_AUTO_POLL));
@@ -1004,6 +1035,24 @@ out:
phy_reg | MII_TG3_EXT_CTRL_FIFO_ELASTIC);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 phy_reg;
+
+ /* adjust output voltage */
+ tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x12);
+
+ if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phy_reg)) {
+ u32 phy_reg2;
+
+ tg3_writephy(tp, MII_TG3_EPHY_TEST,
+ phy_reg | MII_TG3_EPHY_SHADOW_EN);
+ /* Enable auto-MDIX */
+ if (!tg3_readphy(tp, 0x10, &phy_reg2))
+ tg3_writephy(tp, 0x10, phy_reg2 | 0x4000);
+ tg3_writephy(tp, MII_TG3_EPHY_TEST, phy_reg);
+ }
+ }
+
tg3_phy_set_wirespeed(tp);
return 0;
}
@@ -1117,6 +1166,15 @@ static void tg3_nvram_unlock(struct tg3 *);
static void tg3_power_down_phy(struct tg3 *tp)
{
+ if (tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)
+ return;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) {
+ tg3_writephy(tp, MII_TG3_EXT_CTRL,
+ MII_TG3_EXT_CTRL_FORCE_LED_OFF);
+ tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
+ }
+
/* The PHY should not be powered down on some chips because
* of bugs.
*/
@@ -1199,7 +1257,12 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tg3_setup_phy(tp, 0);
}
- if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val;
+
+ val = tr32(GRC_VCPU_EXT_CTRL);
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_DISABLE_WOL);
+ } else if (!(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
int i;
u32 val;
@@ -1223,7 +1286,10 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x5a);
udelay(40);
- mac_mode = MAC_MODE_PORT_MODE_MII;
+ if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
+ mac_mode = MAC_MODE_PORT_MODE_GMII;
+ else
+ mac_mode = MAC_MODE_PORT_MODE_MII;
if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5700 ||
!(tp->tg3_flags & TG3_FLAG_WOL_SPEED_100MB))
@@ -1301,15 +1367,8 @@ static int tg3_set_power_state(struct tg3 *tp, pci_power_t state)
}
if (!(tp->tg3_flags & TG3_FLAG_WOL_ENABLE) &&
- !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF)) {
- /* Turn off the PHY */
- if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
- tg3_writephy(tp, MII_TG3_EXT_CTRL,
- MII_TG3_EXT_CTRL_FORCE_LED_OFF);
- tg3_writephy(tp, MII_TG3_AUX_CTRL, 0x01b2);
- tg3_power_down_phy(tp);
- }
- }
+ !(tp->tg3_flags & TG3_FLAG_ENABLE_ASF))
+ tg3_power_down_phy(tp);
tg3_frob_aux_power(tp);
@@ -1467,6 +1526,13 @@ static void tg3_aux_stat_to_speed_duplex(struct tg3 *tp, u32 val, u16 *speed, u8
break;
default:
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ *speed = (val & MII_TG3_AUX_STAT_100) ? SPEED_100 :
+ SPEED_10;
+ *duplex = (val & MII_TG3_AUX_STAT_FULL) ? DUPLEX_FULL :
+ DUPLEX_HALF;
+ break;
+ }
*speed = SPEED_INVALID;
*duplex = DUPLEX_INVALID;
break;
@@ -1749,7 +1815,7 @@ static int tg3_setup_copper_phy(struct tg3 *tp, int force_reset)
if (tp->tg3_flags & TG3_FLAG_USE_MI_INTERRUPT)
tg3_writephy(tp, MII_TG3_IMASK, ~MII_TG3_INT_LINKCHG);
- else
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tg3_writephy(tp, MII_TG3_IMASK, ~0);
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5700 ||
@@ -2406,24 +2472,27 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
expected_sg_dig_ctrl |= (1 << 12);
if (sg_dig_ctrl != expected_sg_dig_ctrl) {
+ if ((tp->tg3_flags2 & TG3_FLG2_PARALLEL_DETECT) &&
+ tp->serdes_counter &&
+ ((mac_status & (MAC_STATUS_PCS_SYNCED |
+ MAC_STATUS_RCVD_CFG)) ==
+ MAC_STATUS_PCS_SYNCED)) {
+ tp->serdes_counter--;
+ current_link_up = 1;
+ goto out;
+ }
+restart_autoneg:
if (workaround)
tw32_f(MAC_SERDES_CFG, serdes_cfg | 0xc011000);
tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl | (1 << 30));
udelay(5);
tw32_f(SG_DIG_CTRL, expected_sg_dig_ctrl);
- tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
} else if (mac_status & (MAC_STATUS_PCS_SYNCED |
MAC_STATUS_SIGNAL_DET)) {
- int i;
-
- /* Giver time to negotiate (~200ms) */
- for (i = 0; i < 40000; i++) {
- sg_dig_status = tr32(SG_DIG_STATUS);
- if (sg_dig_status & (0x3))
- break;
- udelay(5);
- }
+ sg_dig_status = tr32(SG_DIG_STATUS);
mac_status = tr32(MAC_STATUS);
if ((sg_dig_status & (1 << 1)) &&
@@ -2439,10 +2508,11 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
tg3_setup_flow_control(tp, local_adv, remote_adv);
current_link_up = 1;
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = 0;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
} else if (!(sg_dig_status & (1 << 1))) {
- if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED)
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ if (tp->serdes_counter)
+ tp->serdes_counter--;
else {
if (workaround) {
u32 val = serdes_cfg;
@@ -2466,9 +2536,17 @@ static int tg3_setup_fiber_hw_autoneg(struct tg3 *tp, u32 mac_status)
!(mac_status & MAC_STATUS_RCVD_CFG)) {
tg3_setup_flow_control(tp, 0, 0);
current_link_up = 1;
- }
+ tp->tg3_flags2 |=
+ TG3_FLG2_PARALLEL_DETECT;
+ tp->serdes_counter =
+ SERDES_PARALLEL_DET_TIMEOUT;
+ } else
+ goto restart_autoneg;
}
}
+ } else {
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5704S;
+ tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
}
out:
@@ -2599,14 +2677,16 @@ static int tg3_setup_fiber_phy(struct tg3 *tp, int force_reset)
MAC_STATUS_CFG_CHANGED));
udelay(5);
if ((tr32(MAC_STATUS) & (MAC_STATUS_SYNC_CHANGED |
- MAC_STATUS_CFG_CHANGED)) == 0)
+ MAC_STATUS_CFG_CHANGED |
+ MAC_STATUS_LNKSTATE_CHANGED)) == 0)
break;
}
mac_status = tr32(MAC_STATUS);
if ((mac_status & MAC_STATUS_PCS_SYNCED) == 0) {
current_link_up = 0;
- if (tp->link_config.autoneg == AUTONEG_ENABLE) {
+ if (tp->link_config.autoneg == AUTONEG_ENABLE &&
+ tp->serdes_counter == 0) {
tw32_f(MAC_MODE, (tp->mac_mode |
MAC_MODE_SEND_CONFIGS));
udelay(1);
@@ -2711,7 +2791,7 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
tg3_writephy(tp, MII_BMCR, bmcr);
tw32_f(MAC_EVENT, MAC_EVENT_LNKSTATE_CHANGED);
- tp->tg3_flags2 |= TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter = SERDES_AN_TIMEOUT_5714S;
tp->tg3_flags2 &= ~TG3_FLG2_PARALLEL_DETECT;
return err;
@@ -2816,9 +2896,9 @@ static int tg3_setup_fiber_mii_phy(struct tg3 *tp, int force_reset)
static void tg3_serdes_parallel_detect(struct tg3 *tp)
{
- if (tp->tg3_flags2 & TG3_FLG2_PHY_JUST_INITTED) {
+ if (tp->serdes_counter) {
/* Give autoneg time to complete. */
- tp->tg3_flags2 &= ~TG3_FLG2_PHY_JUST_INITTED;
+ tp->serdes_counter--;
return;
}
if (!netif_carrier_ok(tp->dev) &&
@@ -3535,8 +3615,7 @@ static irqreturn_t tg3_test_isr(int irq, void *dev_id,
if ((sblk->status & SD_STATUS_UPDATED) ||
!(tr32(TG3PCI_PCISTATE) & PCISTATE_INT_NOT_ACTIVE)) {
- tw32_mailbox(MAILBOX_INTERRUPT_0 + TG3_64BIT_REG_LOW,
- 0x00000001);
+ tg3_disable_ints(tp);
return IRQ_RETVAL(1);
}
return IRQ_RETVAL(0);
@@ -4644,6 +4723,44 @@ static void tg3_write_sig_legacy(struct tg3 *tp, int kind)
}
}
+static int tg3_poll_fw(struct tg3 *tp)
+{
+ int i;
+ u32 val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ for (i = 0; i < 400; i++) {
+ if (tr32(VCPU_STATUS) & VCPU_STATUS_INIT_DONE)
+ return 0;
+ udelay(10);
+ }
+ return -ENODEV;
+ }
+
+ /* Wait for firmware initialization to complete. */
+ for (i = 0; i < 100000; i++) {
+ tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
+ if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
+ break;
+ udelay(10);
+ }
+
+ /* Chip might not be fitted with firmware. Some Sun onboard
+ * parts are configured like that. So don't signal the timeout
+ * of the above loop as an error, but do report the lack of
+ * running firmware once.
+ */
+ if (i >= 100000 &&
+ !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
+ tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;
+
+ printk(KERN_INFO PFX "%s: No firmware running.\n",
+ tp->dev->name);
+ }
+
+ return 0;
+}
+
static void tg3_stop_fw(struct tg3 *);
/* tp->lock is held. */
@@ -4651,7 +4768,7 @@ static int tg3_chip_reset(struct tg3 *tp)
{
u32 val;
void (*write_op)(struct tg3 *, u32, u32);
- int i;
+ int err;
tg3_nvram_lock(tp);
@@ -4688,6 +4805,12 @@ static int tg3_chip_reset(struct tg3 *tp)
}
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tw32(VCPU_STATUS, tr32(VCPU_STATUS) | VCPU_STATUS_DRV_RESET);
+ tw32(GRC_VCPU_EXT_CTRL,
+ tr32(GRC_VCPU_EXT_CTRL) & ~GRC_VCPU_EXT_CTRL_HALT_CPU);
+ }
+
if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)
val |= GRC_MISC_CFG_KEEP_GPHY_POWER;
tw32(GRC_MISC_CFG, val);
@@ -4811,26 +4934,9 @@ static int tg3_chip_reset(struct tg3 *tp)
tw32_f(MAC_MODE, 0);
udelay(40);
- /* Wait for firmware initialization to complete. */
- for (i = 0; i < 100000; i++) {
- tg3_read_mem(tp, NIC_SRAM_FIRMWARE_MBOX, &val);
- if (val == ~NIC_SRAM_FIRMWARE_MBOX_MAGIC1)
- break;
- udelay(10);
- }
-
- /* Chip might not be fitted with firmare. Some Sun onboard
- * parts are configured like that. So don't signal the timeout
- * of the above loop as an error, but do report the lack of
- * running firmware once.
- */
- if (i >= 100000 &&
- !(tp->tg3_flags2 & TG3_FLG2_NO_FWARE_REPORTED)) {
- tp->tg3_flags2 |= TG3_FLG2_NO_FWARE_REPORTED;
-
- printk(KERN_INFO PFX "%s: No firmware running.\n",
- tp->dev->name);
- }
+ err = tg3_poll_fw(tp);
+ if (err)
+ return err;
if ((tp->tg3_flags2 & TG3_FLG2_PCI_EXPRESS) &&
tp->pci_chip_rev_id != CHIPREV_ID_5750_A0) {
@@ -5036,6 +5142,12 @@ static int tg3_halt_cpu(struct tg3 *tp, u32 offset)
BUG_ON(offset == TX_CPU_BASE &&
(tp->tg3_flags2 & TG3_FLG2_5705_PLUS));
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 val = tr32(GRC_VCPU_EXT_CTRL);
+
+ tw32(GRC_VCPU_EXT_CTRL, val | GRC_VCPU_EXT_CTRL_HALT_CPU);
+ return 0;
+ }
if (offset == RX_CPU_BASE) {
for (i = 0; i < 10000; i++) {
tw32(offset + CPU_STATE, 0xffffffff);
@@ -6040,6 +6152,13 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
val = 1;
else if (val > tp->rx_std_max_post)
val = tp->rx_std_max_post;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ if (tp->pci_chip_rev_id == CHIPREV_ID_5906_A1)
+ tw32(ISO_PKT_TX, (tr32(ISO_PKT_TX) & ~0x3) | 0x2);
+
+ if (val > (TG3_RX_INTERNAL_RING_SZ_5906 / 2))
+ val = TG3_RX_INTERNAL_RING_SZ_5906 / 2;
+ }
tw32(RCVBDI_STD_THRESH, val);
@@ -6460,7 +6579,8 @@ static int tg3_reset_hw(struct tg3 *tp, int reset_phy)
if (err)
return err;
- if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES)) {
+ if (!(tp->tg3_flags2 & TG3_FLG2_PHY_SERDES) &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906) {
u32 tmp;
/* Clear CRC stats. */
@@ -6660,12 +6780,14 @@ static void tg3_timer(unsigned long __opaque)
need_setup = 1;
}
if (need_setup) {
- tw32_f(MAC_MODE,
- (tp->mac_mode &
- ~MAC_MODE_PORT_MODE_MASK));
- udelay(40);
- tw32_f(MAC_MODE, tp->mac_mode);
- udelay(40);
+ if (!tp->serdes_counter) {
+ tw32_f(MAC_MODE,
+ (tp->mac_mode &
+ ~MAC_MODE_PORT_MODE_MASK));
+ udelay(40);
+ tw32_f(MAC_MODE, tp->mac_mode);
+ udelay(40);
+ }
tg3_setup_phy(tp, 0);
}
} else if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES)
@@ -6674,13 +6796,29 @@ static void tg3_timer(unsigned long __opaque)
tp->timer_counter = tp->timer_multiplier;
}
- /* Heartbeat is only sent once every 2 seconds. */
+ /* Heartbeat is only sent once every 2 seconds.
+ *
+ * The heartbeat is to tell the ASF firmware that the host
+ * driver is still alive. In the event that the OS crashes,
+ * ASF needs to reset the hardware to free up the FIFO space
+ * that may be filled with rx packets destined for the host.
+ * If the FIFO is full, ASF will no longer function properly.
+ *
+ * Unintended resets have been reported on real time kernels
+ * where the timer doesn't run on time. Netpoll will also have
+ * same problem.
+ *
+ * The new FWCMD_NICDRV_ALIVE3 command tells the ASF firmware
+ * to check the ring condition when the heartbeat is expiring
+ * before doing the reset. This will prevent most unintended
+ * resets.
+ */
if (!--tp->asf_counter) {
if (tp->tg3_flags & TG3_FLAG_ENABLE_ASF) {
u32 val;
tg3_write_mem(tp, NIC_SRAM_FW_CMD_MBOX,
- FWCMD_NICDRV_ALIVE2);
+ FWCMD_NICDRV_ALIVE3);
tg3_write_mem(tp, NIC_SRAM_FW_CMD_LEN_MBOX, 4);
/* 5 seconds timeout */
tg3_write_mem(tp, NIC_SRAM_FW_CMD_DATA_MBOX, 5);
@@ -6721,8 +6859,7 @@ static int tg3_request_irq(struct tg3 *tp)
static int tg3_test_interrupt(struct tg3 *tp)
{
struct net_device *dev = tp->dev;
- int err, i;
- u32 int_mbox = 0;
+ int err, i, intr_ok = 0;
if (!netif_running(dev))
return -ENODEV;
@@ -6743,10 +6880,18 @@ static int tg3_test_interrupt(struct tg3 *tp)
HOSTCC_MODE_NOW);
for (i = 0; i < 5; i++) {
+ u32 int_mbox, misc_host_ctrl;
+
int_mbox = tr32_mailbox(MAILBOX_INTERRUPT_0 +
TG3_64BIT_REG_LOW);
- if (int_mbox != 0)
+ misc_host_ctrl = tr32(TG3PCI_MISC_HOST_CTRL);
+
+ if ((int_mbox != 0) ||
+ (misc_host_ctrl & MISC_HOST_CTRL_MASK_PCI_INT)) {
+ intr_ok = 1;
break;
+ }
+
msleep(10);
}
@@ -6759,7 +6904,7 @@ static int tg3_test_interrupt(struct tg3 *tp)
if (err)
return err;
- if (int_mbox != 0)
+ if (intr_ok)
return 0;
return -EIO;
@@ -6936,9 +7081,10 @@ static int tg3_open(struct net_device *dev)
if (tp->tg3_flags2 & TG3_FLG2_USING_MSI) {
if (tp->tg3_flags2 & TG3_FLG2_1SHOT_MSI) {
- u32 val = tr32(0x7c04);
+ u32 val = tr32(PCIE_TRANSACTION_CFG);
- tw32(0x7c04, val | (1 << 29));
+ tw32(PCIE_TRANSACTION_CFG,
+ val | PCIE_TRANS_CFG_1SHOT_MSI);
}
}
}
@@ -7857,7 +8003,7 @@ static int tg3_set_wol(struct net_device *dev, struct ethtool_wolinfo *wol)
if (wol->wolopts & ~WAKE_MAGIC)
return -EINVAL;
if ((wol->wolopts & WAKE_MAGIC) &&
- tp->tg3_flags2 & TG3_FLG2_PHY_SERDES &&
+ tp->tg3_flags2 & TG3_FLG2_ANY_SERDES &&
!(tp->tg3_flags & TG3_FLAG_SERDES_WOL_CAP))
return -EINVAL;
@@ -7893,7 +8039,8 @@ static int tg3_set_tso(struct net_device *dev, u32 value)
return -EINVAL;
return 0;
}
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) {
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)) {
if (value)
dev->features |= NETIF_F_TSO6;
else
@@ -8147,6 +8294,8 @@ static void tg3_get_ethtool_stats (struct net_device *dev,
#define NVRAM_TEST_SIZE 0x100
#define NVRAM_SELFBOOT_FORMAT1_SIZE 0x14
+#define NVRAM_SELFBOOT_HW_SIZE 0x20
+#define NVRAM_SELFBOOT_DATA_SIZE 0x1c
static int tg3_test_nvram(struct tg3 *tp)
{
@@ -8158,12 +8307,14 @@ static int tg3_test_nvram(struct tg3 *tp)
if (magic == TG3_EEPROM_MAGIC)
size = NVRAM_TEST_SIZE;
- else if ((magic & 0xff000000) == 0xa5000000) {
+ else if ((magic & TG3_EEPROM_MAGIC_FW_MSK) == TG3_EEPROM_MAGIC_FW) {
if ((magic & 0xe00000) == 0x200000)
size = NVRAM_SELFBOOT_FORMAT1_SIZE;
else
return 0;
- } else
+ } else if ((magic & TG3_EEPROM_MAGIC_HW_MSK) == TG3_EEPROM_MAGIC_HW)
+ size = NVRAM_SELFBOOT_HW_SIZE;
+ else
return -EIO;
buf = kmalloc(size, GFP_KERNEL);
@@ -8182,7 +8333,8 @@ static int tg3_test_nvram(struct tg3 *tp)
goto out;
/* Selfboot format */
- if (cpu_to_be32(buf[0]) != TG3_EEPROM_MAGIC) {
+ if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_FW_MSK) ==
+ TG3_EEPROM_MAGIC_FW) {
u8 *buf8 = (u8 *) buf, csum8 = 0;
for (i = 0; i < size; i++)
@@ -8197,6 +8349,51 @@ static int tg3_test_nvram(struct tg3 *tp)
goto out;
}
+ if ((cpu_to_be32(buf[0]) & TG3_EEPROM_MAGIC_HW_MSK) ==
+ TG3_EEPROM_MAGIC_HW) {
+ u8 data[NVRAM_SELFBOOT_DATA_SIZE];
+ u8 parity[NVRAM_SELFBOOT_DATA_SIZE];
+ u8 *buf8 = (u8 *) buf;
+ int j, k;
+
+ /* Separate the parity bits and the data bytes. */
+ for (i = 0, j = 0, k = 0; i < NVRAM_SELFBOOT_HW_SIZE; i++) {
+ if ((i == 0) || (i == 8)) {
+ int l;
+ u8 msk;
+
+ for (l = 0, msk = 0x80; l < 7; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+ }
+ else if (i == 16) {
+ int l;
+ u8 msk;
+
+ for (l = 0, msk = 0x20; l < 6; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+
+ for (l = 0, msk = 0x80; l < 8; l++, msk >>= 1)
+ parity[k++] = buf8[i] & msk;
+ i++;
+ }
+ data[j++] = buf8[i];
+ }
+
+ err = -EIO;
+ for (i = 0; i < NVRAM_SELFBOOT_DATA_SIZE; i++) {
+ u8 hw8 = hweight8(data[i]);
+
+ if ((hw8 & 0x1) && parity[i])
+ goto out;
+ else if (!(hw8 & 0x1) && !parity[i])
+ goto out;
+ }
+ err = 0;
+ goto out;
+ }
+
/* Bootstrap checksum at offset 0x10 */
csum = calc_crc((unsigned char *) buf, 0x10);
if(csum != cpu_to_le32(buf[0x10/4]))
@@ -8243,7 +8440,7 @@ static int tg3_test_link(struct tg3 *tp)
/* Only test the commonly used registers */
static int tg3_test_registers(struct tg3 *tp)
{
- int i, is_5705;
+ int i, is_5705, is_5750;
u32 offset, read_mask, write_mask, val, save_val, read_val;
static struct {
u16 offset;
@@ -8251,6 +8448,7 @@ static int tg3_test_registers(struct tg3 *tp)
#define TG3_FL_5705 0x1
#define TG3_FL_NOT_5705 0x2
#define TG3_FL_NOT_5788 0x4
+#define TG3_FL_NOT_5750 0x8
u32 read_mask;
u32 write_mask;
} reg_tbl[] = {
@@ -8361,9 +8559,9 @@ static int tg3_test_registers(struct tg3 *tp)
0xffffffff, 0x00000000 },
/* Buffer Manager Control Registers. */
- { BUFMGR_MB_POOL_ADDR, 0x0000,
+ { BUFMGR_MB_POOL_ADDR, TG3_FL_NOT_5750,
0x00000000, 0x007fff80 },
- { BUFMGR_MB_POOL_SIZE, 0x0000,
+ { BUFMGR_MB_POOL_SIZE, TG3_FL_NOT_5750,
0x00000000, 0x007fffff },
{ BUFMGR_MB_RDMA_LOW_WATER, 0x0000,
0x00000000, 0x0000003f },
@@ -8389,10 +8587,12 @@ static int tg3_test_registers(struct tg3 *tp)
{ 0xffff, 0x0000, 0x00000000, 0x00000000 },
};
- if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS)
+ is_5705 = is_5750 = 0;
+ if (tp->tg3_flags2 & TG3_FLG2_5705_PLUS) {
is_5705 = 1;
- else
- is_5705 = 0;
+ if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS)
+ is_5750 = 1;
+ }
for (i = 0; reg_tbl[i].offset != 0xffff; i++) {
if (is_5705 && (reg_tbl[i].flags & TG3_FL_NOT_5705))
@@ -8405,6 +8605,9 @@ static int tg3_test_registers(struct tg3 *tp)
(reg_tbl[i].flags & TG3_FL_NOT_5788))
continue;
+ if (is_5750 && (reg_tbl[i].flags & TG3_FL_NOT_5750))
+ continue;
+
offset = (u32) reg_tbl[i].offset;
read_mask = reg_tbl[i].read_mask;
write_mask = reg_tbl[i].write_mask;
@@ -8496,6 +8699,13 @@ static int tg3_test_memory(struct tg3 *tp)
{ 0x00008000, 0x02000},
{ 0x00010000, 0x0c000},
{ 0xffffffff, 0x00000}
+ }, mem_tbl_5906[] = {
+ { 0x00000200, 0x00008},
+ { 0x00004000, 0x00400},
+ { 0x00006000, 0x00400},
+ { 0x00008000, 0x01000},
+ { 0x00010000, 0x01000},
+ { 0xffffffff, 0x00000}
};
struct mem_entry *mem_tbl;
int err = 0;
@@ -8505,6 +8715,8 @@ static int tg3_test_memory(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
mem_tbl = mem_tbl_5755;
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ mem_tbl = mem_tbl_5906;
else
mem_tbl = mem_tbl_5705;
} else
@@ -8541,13 +8753,41 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
return 0;
mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
- MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY |
- MAC_MODE_PORT_MODE_GMII;
+ MAC_MODE_PORT_INT_LPBACK | MAC_MODE_LINK_POLARITY;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_GMII;
tw32(MAC_MODE, mac_mode);
} else if (loopback_mode == TG3_PHY_LOOPBACK) {
- tg3_writephy(tp, MII_BMCR, BMCR_LOOPBACK | BMCR_FULLDPLX |
- BMCR_SPEED1000);
+ u32 val;
+
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ u32 phytest;
+
+ if (!tg3_readphy(tp, MII_TG3_EPHY_TEST, &phytest)) {
+ u32 phy;
+
+ tg3_writephy(tp, MII_TG3_EPHY_TEST,
+ phytest | MII_TG3_EPHY_SHADOW_EN);
+ if (!tg3_readphy(tp, 0x1b, &phy))
+ tg3_writephy(tp, 0x1b, phy & ~0x20);
+ if (!tg3_readphy(tp, 0x10, &phy))
+ tg3_writephy(tp, 0x10, phy & ~0x4000);
+ tg3_writephy(tp, MII_TG3_EPHY_TEST, phytest);
+ }
+ }
+ val = BMCR_LOOPBACK | BMCR_FULLDPLX;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ val |= BMCR_SPEED100;
+ else
+ val |= BMCR_SPEED1000;
+
+ tg3_writephy(tp, MII_BMCR, val);
udelay(40);
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tg3_writephy(tp, MII_TG3_EPHY_PTEST, 0x1800);
+
/* reset to prevent losing 1st rx packet intermittently */
if (tp->tg3_flags2 & TG3_FLG2_MII_SERDES) {
tw32_f(MAC_RX_MODE, RX_MODE_RESET);
@@ -8555,7 +8795,11 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
tw32_f(MAC_RX_MODE, tp->rx_mode);
}
mac_mode = (tp->mac_mode & ~MAC_MODE_PORT_MODE_MASK) |
- MAC_MODE_LINK_POLARITY | MAC_MODE_PORT_MODE_GMII;
+ MAC_MODE_LINK_POLARITY;
+ if (tp->tg3_flags & TG3_FLAG_10_100_ONLY)
+ mac_mode |= MAC_MODE_PORT_MODE_MII;
+ else
+ mac_mode |= MAC_MODE_PORT_MODE_GMII;
if ((tp->phy_id & PHY_ID_MASK) == PHY_ID_BCM5401) {
mac_mode &= ~MAC_MODE_LINK_POLARITY;
tg3_writephy(tp, MII_TG3_EXT_CTRL,
@@ -8604,7 +8848,8 @@ static int tg3_run_loopback(struct tg3 *tp, int loopback_mode)
udelay(10);
- for (i = 0; i < 10; i++) {
+ /* 250 usec to allow enough time on some 10/100 Mbps devices. */
+ for (i = 0; i < 25; i++) {
tw32_f(HOSTCC_MODE, tp->coalesce_mode | HOSTCC_MODE_ENABLE |
HOSTCC_MODE_NOW);
@@ -8956,7 +9201,9 @@ static void __devinit tg3_get_eeprom_size(struct tg3 *tp)
if (tg3_nvram_read_swab(tp, 0, &magic) != 0)
return;
- if ((magic != TG3_EEPROM_MAGIC) && ((magic & 0xff000000) != 0xa5000000))
+ if ((magic != TG3_EEPROM_MAGIC) &&
+ ((magic & TG3_EEPROM_MAGIC_FW_MSK) != TG3_EEPROM_MAGIC_FW) &&
+ ((magic & TG3_EEPROM_MAGIC_HW_MSK) != TG3_EEPROM_MAGIC_HW))
return;
/*
@@ -9194,6 +9441,13 @@ static void __devinit tg3_get_5787_nvram_info(struct tg3 *tp)
}
}
+static void __devinit tg3_get_5906_nvram_info(struct tg3 *tp)
+{
+ tp->nvram_jedecnum = JEDEC_ATMEL;
+ tp->tg3_flags |= TG3_FLAG_NVRAM_BUFFERED;
+ tp->nvram_pagesize = ATMEL_AT24C512_CHIP_SIZE;
+}
+
/* Chips other than 5700/5701 use the NVRAM for fetching info. */
static void __devinit tg3_nvram_init(struct tg3 *tp)
{
@@ -9230,6 +9484,8 @@ static void __devinit tg3_nvram_init(struct tg3 *tp)
tg3_get_5755_nvram_info(tp);
else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
tg3_get_5787_nvram_info(tp);
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ tg3_get_5906_nvram_info(tp);
else
tg3_get_nvram_info(tp);
@@ -9703,6 +9959,12 @@ static void __devinit tg3_get_eeprom_hw_cfg(struct tg3 *tp)
/* Assume an onboard device by default. */
tp->tg3_flags |= TG3_FLAG_EEPROM_WRITE_PROT;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ if (!(tr32(PCIE_TRANSACTION_CFG) & PCIE_TRANS_CFG_LOM))
+ tp->tg3_flags &= ~TG3_FLAG_EEPROM_WRITE_PROT;
+ return;
+ }
+
tg3_read_mem(tp, NIC_SRAM_DATA_SIG, &val);
if (val == NIC_SRAM_DATA_SIG_MAGIC) {
u32 nic_cfg, led_cfg;
@@ -10034,7 +10296,10 @@ static void __devinit tg3_read_partno(struct tg3 *tp)
}
out_not_found:
- strcpy(tp->board_part_number, "none");
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ strcpy(tp->board_part_number, "BCM95906");
+ else
+ strcpy(tp->board_part_number, "none");
}
static void __devinit tg3_read_fw_ver(struct tg3 *tp)
@@ -10236,6 +10501,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5752 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906 ||
(tp->tg3_flags2 & TG3_FLG2_5780_CLASS))
tp->tg3_flags2 |= TG3_FLG2_5750_PLUS;
@@ -10245,7 +10511,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (tp->tg3_flags2 & TG3_FLG2_5750_PLUS) {
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787) {
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
tp->tg3_flags2 |= TG3_FLG2_HW_TSO_2;
tp->tg3_flags2 |= TG3_FLG2_1SHOT_MSI;
} else {
@@ -10262,7 +10529,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5750 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5752 &&
GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5755 &&
- GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787)
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5787 &&
+ GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tp->tg3_flags2 |= TG3_FLG2_JUMBO_CAPABLE;
if (pci_find_capability(tp->pdev, PCI_CAP_ID_EXP) != 0)
@@ -10392,6 +10660,12 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
pci_cmd &= ~PCI_COMMAND_MEMORY;
pci_write_config_word(tp->pdev, PCI_COMMAND, pci_cmd);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tp->read32_mbox = tg3_read32_mbox_5906;
+ tp->write32_mbox = tg3_write32_mbox_5906;
+ tp->write32_tx_mbox = tg3_write32_mbox_5906;
+ tp->write32_rx_mbox = tg3_write32_mbox_5906;
+ }
if (tp->write32 == tg3_write_indirect_reg32 ||
((tp->tg3_flags & TG3_FLAG_PCIX_MODE) &&
@@ -10463,6 +10737,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
((GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5705) &&
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A0) &&
(tp->pci_chip_rev_id != CHIPREV_ID_5705_A1)) ||
+ (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) ||
(tp->tg3_flags2 & TG3_FLG2_ANY_SERDES))
tp->tg3_flags2 |= TG3_FLG2_NO_ETH_WIRE_SPEED;
@@ -10476,7 +10751,7 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
tp->tg3_flags2 |= TG3_FLG2_PHY_JITTER_BUG;
- else
+ else if (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906)
tp->tg3_flags2 |= TG3_FLG2_PHY_BER_BUG;
}
@@ -10566,7 +10841,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
tp->pdev->device == PCI_DEVICE_ID_TIGON3_5705F)) ||
(tp->pdev->vendor == PCI_VENDOR_ID_BROADCOM &&
(tp->pdev->device == PCI_DEVICE_ID_TIGON3_5751F ||
- tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F)))
+ tp->pdev->device == PCI_DEVICE_ID_TIGON3_5753F)) ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->tg3_flags |= TG3_FLAG_10_100_ONLY;
err = tg3_phy_probe(tp);
@@ -10617,7 +10893,8 @@ static int __devinit tg3_get_invariants(struct tg3 *tp)
* straddle the 4GB address boundary in some cases.
*/
if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5755 ||
- GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787)
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5787 ||
+ GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
tp->dev->hard_start_xmit = tg3_start_xmit;
else
tp->dev->hard_start_xmit = tg3_start_xmit_dma_bug;
@@ -10698,6 +10975,8 @@ static int __devinit tg3_get_device_address(struct tg3 *tp)
else
tg3_nvram_unlock(tp);
}
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906)
+ mac_offset = 0x10;
/* First try to get it from MAC address mailbox. */
tg3_read_mem(tp, NIC_SRAM_MAC_ADDR_HIGH_MBOX, &hi);
@@ -11181,6 +11460,12 @@ static void __devinit tg3_init_bufmgr_config(struct tg3 *tp)
DEFAULT_MB_MACRX_LOW_WATER_5705;
tp->bufmgr_config.mbuf_high_water =
DEFAULT_MB_HIGH_WATER_5705;
+ if (GET_ASIC_REV(tp->pci_chip_rev_id) == ASIC_REV_5906) {
+ tp->bufmgr_config.mbuf_mac_rx_low_water =
+ DEFAULT_MB_MACRX_LOW_WATER_5906;
+ tp->bufmgr_config.mbuf_high_water =
+ DEFAULT_MB_HIGH_WATER_5906;
+ }
tp->bufmgr_config.mbuf_read_dma_low_water_jumbo =
DEFAULT_MB_RDMA_LOW_WATER_JUMBO_5780;
@@ -11224,6 +11509,8 @@ static char * __devinit tg3_phy_string(struct tg3 *tp)
case PHY_ID_BCM5780: return "5780";
case PHY_ID_BCM5755: return "5755";
case PHY_ID_BCM5787: return "5787";
+ case PHY_ID_BCM5756: return "5722/5756";
+ case PHY_ID_BCM5906: return "5906";
case PHY_ID_BCM8002: return "8002/serdes";
case 0: return "serdes";
default: return "unknown";
@@ -11526,7 +11813,8 @@ static int __devinit tg3_init_one(struct pci_dev *pdev,
*/
if (tp->tg3_flags2 & TG3_FLG2_HW_TSO) {
dev->features |= NETIF_F_TSO;
- if (tp->tg3_flags2 & TG3_FLG2_HW_TSO_2)
+ if ((tp->tg3_flags2 & TG3_FLG2_HW_TSO_2) &&
+ (GET_ASIC_REV(tp->pci_chip_rev_id) != ASIC_REV_5906))
dev->features |= NETIF_F_TSO6;
}
diff --git a/drivers/net/tg3.h b/drivers/net/tg3.h
index 3ecf356cfb0..92f53000bce 100644
--- a/drivers/net/tg3.h
+++ b/drivers/net/tg3.h
@@ -24,6 +24,8 @@
#define RX_COPY_THRESHOLD 256
+#define TG3_RX_INTERNAL_RING_SZ_5906 32
+
#define RX_STD_MAX_SIZE 1536
#define RX_STD_MAX_SIZE_5705 512
#define RX_JUMBO_MAX_SIZE 0xdeadbeef /* XXX */
@@ -129,6 +131,7 @@
#define CHIPREV_ID_5752_A0_HW 0x5000
#define CHIPREV_ID_5752_A0 0x6000
#define CHIPREV_ID_5752_A1 0x6001
+#define CHIPREV_ID_5906_A1 0xc001
#define GET_ASIC_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 12)
#define ASIC_REV_5700 0x07
#define ASIC_REV_5701 0x00
@@ -141,6 +144,7 @@
#define ASIC_REV_5714 0x09
#define ASIC_REV_5755 0x0a
#define ASIC_REV_5787 0x0b
+#define ASIC_REV_5906 0x0c
#define GET_CHIP_REV(CHIP_REV_ID) ((CHIP_REV_ID) >> 8)
#define CHIPREV_5700_AX 0x70
#define CHIPREV_5700_BX 0x71
@@ -646,7 +650,8 @@
#define SNDDATAI_SCTRL_FORCE_ZERO 0x00000010
#define SNDDATAI_STATSENAB 0x00000c0c
#define SNDDATAI_STATSINCMASK 0x00000c10
-/* 0xc14 --> 0xc80 unused */
+#define ISO_PKT_TX 0x00000c20
+/* 0xc24 --> 0xc80 unused */
#define SNDDATAI_COS_CNT_0 0x00000c80
#define SNDDATAI_COS_CNT_1 0x00000c84
#define SNDDATAI_COS_CNT_2 0x00000c88
@@ -997,11 +1002,13 @@
#define BUFMGR_MB_MACRX_LOW_WATER 0x00004414
#define DEFAULT_MB_MACRX_LOW_WATER 0x00000020
#define DEFAULT_MB_MACRX_LOW_WATER_5705 0x00000010
+#define DEFAULT_MB_MACRX_LOW_WATER_5906 0x00000004
#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO 0x00000098
#define DEFAULT_MB_MACRX_LOW_WATER_JUMBO_5780 0x0000004b
#define BUFMGR_MB_HIGH_WATER 0x00004418
#define DEFAULT_MB_HIGH_WATER 0x00000060
#define DEFAULT_MB_HIGH_WATER_5705 0x00000060
+#define DEFAULT_MB_HIGH_WATER_5906 0x00000010
#define DEFAULT_MB_HIGH_WATER_JUMBO 0x0000017c
#define DEFAULT_MB_HIGH_WATER_JUMBO_5780 0x00000096
#define BUFMGR_RX_MB_ALLOC_REQ 0x0000441c
@@ -1138,7 +1145,12 @@
#define TX_CPU_STATE 0x00005404
#define TX_CPU_PGMCTR 0x0000541c
+#define VCPU_STATUS 0x00005100
+#define VCPU_STATUS_INIT_DONE 0x04000000
+#define VCPU_STATUS_DRV_RESET 0x08000000
+
/* Mailboxes */
+#define GRCMBOX_BASE 0x00005600
#define GRCMBOX_INTERRUPT_0 0x00005800 /* 64-bit */
#define GRCMBOX_INTERRUPT_1 0x00005808 /* 64-bit */
#define GRCMBOX_INTERRUPT_2 0x00005810 /* 64-bit */
@@ -1398,7 +1410,10 @@
#define GRC_EEPROM_CTRL 0x00006840
#define GRC_MDI_CTRL 0x00006844
#define GRC_SEEPROM_DELAY 0x00006848
-/* 0x684c --> 0x6c00 unused */
+/* 0x684c --> 0x6890 unused */
+#define GRC_VCPU_EXT_CTRL 0x00006890
+#define GRC_VCPU_EXT_CTRL_HALT_CPU 0x00400000
+#define GRC_VCPU_EXT_CTRL_DISABLE_WOL 0x20000000
#define GRC_FASTBOOT_PC 0x00006894 /* 5752, 5755, 5787 */
/* 0x6c00 --> 0x7000 unused */
@@ -1485,9 +1500,17 @@
#define NVRAM_WRITE1 0x00007028
/* 0x702c --> 0x7400 unused */
-/* 0x7400 --> 0x8000 unused */
+/* 0x7400 --> 0x7c00 unused */
+#define PCIE_TRANSACTION_CFG 0x00007c04
+#define PCIE_TRANS_CFG_1SHOT_MSI 0x20000000
+#define PCIE_TRANS_CFG_LOM 0x00000020
+
#define TG3_EEPROM_MAGIC 0x669955aa
+#define TG3_EEPROM_MAGIC_FW 0xa5000000
+#define TG3_EEPROM_MAGIC_FW_MSK 0xff000000
+#define TG3_EEPROM_MAGIC_HW 0xabcd
+#define TG3_EEPROM_MAGIC_HW_MSK 0xffff
/* 32K Window into NIC internal memory */
#define NIC_SRAM_WIN_BASE 0x00008000
@@ -1537,6 +1560,7 @@
#define FWCMD_NICDRV_FIX_DMAR 0x00000005
#define FWCMD_NICDRV_FIX_DMAW 0x00000006
#define FWCMD_NICDRV_ALIVE2 0x0000000d
+#define FWCMD_NICDRV_ALIVE3 0x0000000e
#define NIC_SRAM_FW_CMD_LEN_MBOX 0x00000b7c
#define NIC_SRAM_FW_CMD_DATA_MBOX 0x00000b80
#define NIC_SRAM_FW_ASF_STATUS_MBOX 0x00000c00
@@ -1604,6 +1628,7 @@
#define MII_TG3_DSP_RW_PORT 0x15 /* DSP coefficient read/write port */
#define MII_TG3_DSP_ADDRESS 0x17 /* DSP address register */
+#define MII_TG3_EPHY_PTEST 0x17 /* 5906 PHY register */
#define MII_TG3_AUX_CTRL 0x18 /* auxilliary control register */
@@ -1617,6 +1642,8 @@
#define MII_TG3_AUX_STAT_100FULL 0x0500
#define MII_TG3_AUX_STAT_1000HALF 0x0600
#define MII_TG3_AUX_STAT_1000FULL 0x0700
+#define MII_TG3_AUX_STAT_100 0x0008
+#define MII_TG3_AUX_STAT_FULL 0x0001
#define MII_TG3_ISTAT 0x1a /* IRQ status register */
#define MII_TG3_IMASK 0x1b /* IRQ mask register */
@@ -1627,6 +1654,9 @@
#define MII_TG3_INT_DUPLEXCHG 0x0008
#define MII_TG3_INT_ANEG_PAGE_RX 0x0400
+#define MII_TG3_EPHY_TEST 0x1f /* 5906 PHY register */
+#define MII_TG3_EPHY_SHADOW_EN 0x80
+
/* There are two ways to manage the TX descriptors on the tigon3.
* Either the descriptors are in host DMA'able memory, or they
* exist only in the cards on-chip SRAM. All 16 send bds are under
@@ -2203,7 +2233,6 @@ struct tg3 {
#define TG3_FLG2_PCI_EXPRESS 0x00000200
#define TG3_FLG2_ASF_NEW_HANDSHAKE 0x00000400
#define TG3_FLG2_HW_AUTONEG 0x00000800
-#define TG3_FLG2_PHY_JUST_INITTED 0x00001000
#define TG3_FLG2_PHY_SERDES 0x00002000
#define TG3_FLG2_CAPACITIVE_COUPLING 0x00004000
#define TG3_FLG2_FLASH 0x00008000
@@ -2236,6 +2265,12 @@ struct tg3 {
u16 asf_counter;
u16 asf_multiplier;
+ /* 1 second counter for transient serdes link events */
+ u32 serdes_counter;
+#define SERDES_AN_TIMEOUT_5704S 2
+#define SERDES_PARALLEL_DET_TIMEOUT 1
+#define SERDES_AN_TIMEOUT_5714S 1
+
struct tg3_link_config link_config;
struct tg3_bufmgr_config bufmgr_config;
@@ -2276,6 +2311,8 @@ struct tg3 {
#define PHY_ID_BCM5780 0x60008350
#define PHY_ID_BCM5755 0xbc050cc0
#define PHY_ID_BCM5787 0xbc050ce0
+#define PHY_ID_BCM5756 0xbc050ed0
+#define PHY_ID_BCM5906 0xdc00ac40
#define PHY_ID_BCM8002 0x60010140
#define PHY_ID_INVALID 0xffffffff
#define PHY_ID_REV_MASK 0x0000000f
@@ -2302,7 +2339,8 @@ struct tg3 {
(X) == PHY_ID_BCM5705 || (X) == PHY_ID_BCM5750 || \
(X) == PHY_ID_BCM5752 || (X) == PHY_ID_BCM5714 || \
(X) == PHY_ID_BCM5780 || (X) == PHY_ID_BCM5787 || \
- (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM8002)
+ (X) == PHY_ID_BCM5755 || (X) == PHY_ID_BCM5756 || \
+ (X) == PHY_ID_BCM5906 || (X) == PHY_ID_BCM8002)
struct tg3_hw_stats *hw_stats;
dma_addr_t stats_mapping;
diff --git a/drivers/net/tokenring/lanstreamer.c b/drivers/net/tokenring/lanstreamer.c
index 0d66700c6ce..bfc8c3eae9a 100644
--- a/drivers/net/tokenring/lanstreamer.c
+++ b/drivers/net/tokenring/lanstreamer.c
@@ -1876,7 +1876,6 @@ static int sprintf_info(char *buffer, struct net_device *dev)
datap[size+1]=io_word & 0xff;
}
-
size = sprintf(buffer, "\n%6s: Adapter Address : Node Address : Functional Addr\n", dev->name);
size += sprintf(buffer + size,
@@ -1932,64 +1931,6 @@ static int sprintf_info(char *buffer, struct net_device *dev)
#endif
#endif
-#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-static int streamer_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
-{
- int i;
- struct streamer_private *streamer_priv = (struct streamer_private *) dev->priv;
- u8 __iomem *streamer_mmio = streamer_priv->streamer_mmio;
-
- switch(cmd) {
- case IOCTL_SISR_MASK:
- writew(SISR_MI, streamer_mmio + SISR_MASK_SUM);
- break;
- case IOCTL_SPIN_LOCK_TEST:
- printk(KERN_INFO "spin_lock() called.\n");
- spin_lock(&streamer_priv->streamer_lock);
- spin_unlock(&streamer_priv->streamer_lock);
- printk(KERN_INFO "spin_unlock() finished.\n");
- break;
- case IOCTL_PRINT_BDAS:
- printk(KERN_INFO "bdas: RXBDA: %x RXLBDA: %x TX2FDA: %x TX2LFDA: %x\n",
- readw(streamer_mmio + RXBDA),
- readw(streamer_mmio + RXLBDA),
- readw(streamer_mmio + TX2FDA),
- readw(streamer_mmio + TX2LFDA));
- break;
- case IOCTL_PRINT_REGISTERS:
- printk(KERN_INFO "registers:\n");
- printk(KERN_INFO "SISR: %04x MISR: %04x LISR: %04x BCTL: %04x BMCTL: %04x\nmask %04x mask %04x\n",
- readw(streamer_mmio + SISR),
- readw(streamer_mmio + MISR_RUM),
- readw(streamer_mmio + LISR),
- readw(streamer_mmio + BCTL),
- readw(streamer_mmio + BMCTL_SUM),
- readw(streamer_mmio + SISR_MASK),
- readw(streamer_mmio + MISR_MASK));
- break;
- case IOCTL_PRINT_RX_BUFS:
- printk(KERN_INFO "Print rx bufs:\n");
- for(i=0; i<STREAMER_RX_RING_SIZE; i++)
- printk(KERN_INFO "rx_ring %d status: 0x%x\n", i,
- streamer_priv->streamer_rx_ring[i].status);
- break;
- case IOCTL_PRINT_TX_BUFS:
- printk(KERN_INFO "Print tx bufs:\n");
- for(i=0; i<STREAMER_TX_RING_SIZE; i++)
- printk(KERN_INFO "tx_ring %d status: 0x%x\n", i,
- streamer_priv->streamer_tx_ring[i].status);
- break;
- case IOCTL_RX_CMD:
- streamer_rx(dev);
- printk(KERN_INFO "Sent rx command.\n");
- break;
- default:
- printk(KERN_INFO "Bad ioctl!\n");
- }
- return 0;
-}
-#endif
-
static struct pci_driver streamer_pci_driver = {
.name = "lanstreamer",
.id_table = streamer_pci_tbl,
diff --git a/drivers/net/tokenring/lanstreamer.h b/drivers/net/tokenring/lanstreamer.h
index 5557d8e1e22..e7bb3494afc 100644
--- a/drivers/net/tokenring/lanstreamer.h
+++ b/drivers/net/tokenring/lanstreamer.h
@@ -62,18 +62,6 @@
#include <linux/version.h>
-#if STREAMER_IOCTL && (LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0))
-#include <asm/ioctl.h>
-#define IOCTL_PRINT_RX_BUFS SIOCDEVPRIVATE
-#define IOCTL_PRINT_TX_BUFS SIOCDEVPRIVATE+1
-#define IOCTL_RX_CMD SIOCDEVPRIVATE+2
-#define IOCTL_TX_CMD SIOCDEVPRIVATE+3
-#define IOCTL_PRINT_REGISTERS SIOCDEVPRIVATE+4
-#define IOCTL_PRINT_BDAS SIOCDEVPRIVATE+5
-#define IOCTL_SPIN_LOCK_TEST SIOCDEVPRIVATE+6
-#define IOCTL_SISR_MASK SIOCDEVPRIVATE+7
-#endif
-
/* MAX_INTR - the maximum number of times we can loop
* inside the interrupt function before returning
* control to the OS (maximum value is 256)
diff --git a/drivers/net/tulip/de4x5.c b/drivers/net/tulip/de4x5.c
index e661d0a9cc6..fb5fa7d6888 100644
--- a/drivers/net/tulip/de4x5.c
+++ b/drivers/net/tulip/de4x5.c
@@ -2114,6 +2114,7 @@ static struct eisa_device_id de4x5_eisa_ids[] = {
{ "DEC4250", 0 }, /* 0 is the board name index... */
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, de4x5_eisa_ids);
static struct eisa_driver de4x5_eisa_driver = {
.id_table = de4x5_eisa_ids,
diff --git a/drivers/net/tun.c b/drivers/net/tun.c
index de8da6dee1b..151a2e10e4f 100644
--- a/drivers/net/tun.c
+++ b/drivers/net/tun.c
@@ -288,11 +288,10 @@ static inline size_t iov_total(const struct iovec *iv, unsigned long count)
return len;
}
-/* Writev */
-static ssize_t tun_chr_writev(struct file * file, const struct iovec *iv,
- unsigned long count, loff_t *pos)
+static ssize_t tun_chr_aio_write(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
{
- struct tun_struct *tun = file->private_data;
+ struct tun_struct *tun = iocb->ki_filp->private_data;
if (!tun)
return -EBADFD;
@@ -302,14 +301,6 @@ static ssize_t tun_chr_writev(struct file * file, const struct iovec *iv,
return tun_get_user(tun, (struct iovec *) iv, iov_total(iv, count));
}
-/* Write */
-static ssize_t tun_chr_write(struct file * file, const char __user * buf,
- size_t count, loff_t *pos)
-{
- struct iovec iv = { (void __user *) buf, count };
- return tun_chr_writev(file, &iv, 1, pos);
-}
-
/* Put packet to the user space buffer */
static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
struct sk_buff *skb,
@@ -343,10 +334,10 @@ static __inline__ ssize_t tun_put_user(struct tun_struct *tun,
return total;
}
-/* Readv */
-static ssize_t tun_chr_readv(struct file *file, const struct iovec *iv,
- unsigned long count, loff_t *pos)
+static ssize_t tun_chr_aio_read(struct kiocb *iocb, const struct iovec *iv,
+ unsigned long count, loff_t pos)
{
+ struct file *file = iocb->ki_filp;
struct tun_struct *tun = file->private_data;
DECLARE_WAITQUEUE(wait, current);
struct sk_buff *skb;
@@ -426,14 +417,6 @@ static ssize_t tun_chr_readv(struct file *file, const struct iovec *iv,
return ret;
}
-/* Read */
-static ssize_t tun_chr_read(struct file * file, char __user * buf,
- size_t count, loff_t *pos)
-{
- struct iovec iv = { buf, count };
- return tun_chr_readv(file, &iv, 1, pos);
-}
-
static void tun_setup(struct net_device *dev)
{
struct tun_struct *tun = netdev_priv(dev);
@@ -714,7 +697,7 @@ static int tun_chr_fasync(int fd, struct file *file, int on)
return ret;
if (on) {
- ret = f_setown(file, current->pid, 0);
+ ret = __f_setown(file, task_pid(current), PIDTYPE_PID, 0);
if (ret)
return ret;
tun->flags |= TUN_FASYNC;
@@ -764,10 +747,10 @@ static int tun_chr_close(struct inode *inode, struct file *file)
static struct file_operations tun_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
- .read = tun_chr_read,
- .readv = tun_chr_readv,
- .write = tun_chr_write,
- .writev = tun_chr_writev,
+ .read = do_sync_read,
+ .aio_read = tun_chr_aio_read,
+ .write = do_sync_write,
+ .aio_write = tun_chr_aio_write,
.poll = tun_chr_poll,
.ioctl = tun_chr_ioctl,
.open = tun_chr_open,
diff --git a/drivers/net/typhoon.c b/drivers/net/typhoon.c
index 8f6f6fd8b87..d5c32e9caa9 100644
--- a/drivers/net/typhoon.c
+++ b/drivers/net/typhoon.c
@@ -333,11 +333,7 @@ enum state_values {
#define TYPHOON_RESET_TIMEOUT_NOSLEEP ((6 * 1000000) / TYPHOON_UDELAY)
#define TYPHOON_WAIT_TIMEOUT ((1000000 / 2) / TYPHOON_UDELAY)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 5, 28)
-#define typhoon_synchronize_irq(x) synchronize_irq()
-#else
#define typhoon_synchronize_irq(x) synchronize_irq(x)
-#endif
#if defined(NETIF_F_TSO)
#define skb_tso_size(x) (skb_shinfo(x)->gso_size)
diff --git a/drivers/net/wan/Kconfig b/drivers/net/wan/Kconfig
index 54b8e492ef9..58b7efbb075 100644
--- a/drivers/net/wan/Kconfig
+++ b/drivers/net/wan/Kconfig
@@ -154,7 +154,7 @@ config HDLC
If unsure, say N.
config HDLC_RAW
- bool "Raw HDLC support"
+ tristate "Raw HDLC support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC over WAN connections.
@@ -162,7 +162,7 @@ config HDLC_RAW
If unsure, say N.
config HDLC_RAW_ETH
- bool "Raw HDLC Ethernet device support"
+ tristate "Raw HDLC Ethernet device support"
depends on HDLC
help
Generic HDLC driver supporting raw HDLC Ethernet device emulation
@@ -173,7 +173,7 @@ config HDLC_RAW_ETH
If unsure, say N.
config HDLC_CISCO
- bool "Cisco HDLC support"
+ tristate "Cisco HDLC support"
depends on HDLC
help
Generic HDLC driver supporting Cisco HDLC over WAN connections.
@@ -181,7 +181,7 @@ config HDLC_CISCO
If unsure, say N.
config HDLC_FR
- bool "Frame Relay support"
+ tristate "Frame Relay support"
depends on HDLC
help
Generic HDLC driver supporting Frame Relay over WAN connections.
@@ -189,7 +189,7 @@ config HDLC_FR
If unsure, say N.
config HDLC_PPP
- bool "Synchronous Point-to-Point Protocol (PPP) support"
+ tristate "Synchronous Point-to-Point Protocol (PPP) support"
depends on HDLC
help
Generic HDLC driver supporting PPP over WAN connections.
@@ -197,7 +197,7 @@ config HDLC_PPP
If unsure, say N.
config HDLC_X25
- bool "X.25 protocol support"
+ tristate "X.25 protocol support"
depends on HDLC && (LAPB=m && HDLC=m || LAPB=y)
help
Generic HDLC driver supporting X.25 over WAN connections.
diff --git a/drivers/net/wan/Makefile b/drivers/net/wan/Makefile
index 316ca6869d5..83ec2c87ba3 100644
--- a/drivers/net/wan/Makefile
+++ b/drivers/net/wan/Makefile
@@ -9,14 +9,13 @@ cyclomx-y := cycx_main.o
cyclomx-$(CONFIG_CYCLOMX_X25) += cycx_x25.o
cyclomx-objs := $(cyclomx-y)
-hdlc-y := hdlc_generic.o
-hdlc-$(CONFIG_HDLC_RAW) += hdlc_raw.o
-hdlc-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
-hdlc-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
-hdlc-$(CONFIG_HDLC_FR) += hdlc_fr.o
-hdlc-$(CONFIG_HDLC_PPP) += hdlc_ppp.o
-hdlc-$(CONFIG_HDLC_X25) += hdlc_x25.o
-hdlc-objs := $(hdlc-y)
+obj-$(CONFIG_HDLC) += hdlc.o
+obj-$(CONFIG_HDLC_RAW) += hdlc_raw.o
+obj-$(CONFIG_HDLC_RAW_ETH) += hdlc_raw_eth.o
+obj-$(CONFIG_HDLC_CISCO) += hdlc_cisco.o
+obj-$(CONFIG_HDLC_FR) += hdlc_fr.o
+obj-$(CONFIG_HDLC_PPP) += hdlc_ppp.o syncppp.o
+obj-$(CONFIG_HDLC_X25) += hdlc_x25.o
pc300-y := pc300_drv.o
pc300-$(CONFIG_PC300_MLPPP) += pc300_tty.o
@@ -38,10 +37,6 @@ obj-$(CONFIG_CYCLADES_SYNC) += cycx_drv.o cyclomx.o
obj-$(CONFIG_LAPBETHER) += lapbether.o
obj-$(CONFIG_SBNI) += sbni.o
obj-$(CONFIG_PC300) += pc300.o
-obj-$(CONFIG_HDLC) += hdlc.o
-ifeq ($(CONFIG_HDLC_PPP),y)
- obj-$(CONFIG_HDLC) += syncppp.o
-endif
obj-$(CONFIG_N2) += n2.o
obj-$(CONFIG_C101) += c101.o
obj-$(CONFIG_WANXL) += wanxl.o
diff --git a/drivers/net/wan/hdlc_generic.c b/drivers/net/wan/hdlc.c
index 04ca1f7b642..db354e0edbe 100644
--- a/drivers/net/wan/hdlc_generic.c
+++ b/drivers/net/wan/hdlc.c
@@ -1,7 +1,7 @@
/*
* Generic HDLC support routines for Linux
*
- * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -17,9 +17,9 @@
* Use sethdlc utility to set line parameters, protocol and PVCs
*
* How does it work:
- * - proto.open(), close(), start(), stop() calls are serialized.
+ * - proto->open(), close(), start(), stop() calls are serialized.
* The order is: open, [ start, stop ... ] close ...
- * - proto.start() and stop() are called with spin_lock_irq held.
+ * - proto->start() and stop() are called with spin_lock_irq held.
*/
#include <linux/module.h>
@@ -38,10 +38,12 @@
#include <linux/hdlc.h>
-static const char* version = "HDLC support module revision 1.19";
+static const char* version = "HDLC support module revision 1.20";
#undef DEBUG_LINK
+static struct hdlc_proto *first_proto = NULL;
+
static int hdlc_change_mtu(struct net_device *dev, int new_mtu)
{
@@ -63,11 +65,11 @@ static struct net_device_stats *hdlc_get_stats(struct net_device *dev)
static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
struct packet_type *p, struct net_device *orig_dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.netif_rx)
- return hdlc->proto.netif_rx(skb);
+ struct hdlc_device_desc *desc = dev_to_desc(dev);
+ if (desc->netif_rx)
+ return desc->netif_rx(skb);
- hdlc->stats.rx_dropped++; /* Shouldn't happen */
+ desc->stats.rx_dropped++; /* Shouldn't happen */
dev_kfree_skb(skb);
return NET_RX_DROP;
}
@@ -77,8 +79,8 @@ static int hdlc_rcv(struct sk_buff *skb, struct net_device *dev,
static inline void hdlc_proto_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.start)
- return hdlc->proto.start(dev);
+ if (hdlc->proto->start)
+ return hdlc->proto->start(dev);
}
@@ -86,8 +88,8 @@ static inline void hdlc_proto_start(struct net_device *dev)
static inline void hdlc_proto_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->proto.stop)
- return hdlc->proto.stop(dev);
+ if (hdlc->proto->stop)
+ return hdlc->proto->stop(dev);
}
@@ -144,15 +146,15 @@ int hdlc_open(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
- printk(KERN_DEBUG "hdlc_open() carrier %i open %i\n",
+ printk(KERN_DEBUG "%s: hdlc_open() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
- if (hdlc->proto.id == -1)
+ if (hdlc->proto == NULL)
return -ENOSYS; /* no protocol attached */
- if (hdlc->proto.open) {
- int result = hdlc->proto.open(dev);
+ if (hdlc->proto->open) {
+ int result = hdlc->proto->open(dev);
if (result)
return result;
}
@@ -178,7 +180,7 @@ void hdlc_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
- printk(KERN_DEBUG "hdlc_close() carrier %i open %i\n",
+ printk(KERN_DEBUG "%s: hdlc_close() carrier %i open %i\n", dev->name,
hdlc->carrier, hdlc->open);
#endif
@@ -190,68 +192,34 @@ void hdlc_close(struct net_device *dev)
spin_unlock_irq(&hdlc->state_lock);
- if (hdlc->proto.close)
- hdlc->proto.close(dev);
+ if (hdlc->proto->close)
+ hdlc->proto->close(dev);
}
-#ifndef CONFIG_HDLC_RAW
-#define hdlc_raw_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_RAW_ETH
-#define hdlc_raw_eth_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_PPP
-#define hdlc_ppp_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_CISCO
-#define hdlc_cisco_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_FR
-#define hdlc_fr_ioctl(dev, ifr) -ENOSYS
-#endif
-
-#ifndef CONFIG_HDLC_X25
-#define hdlc_x25_ioctl(dev, ifr) -ENOSYS
-#endif
-
-
int hdlc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
- unsigned int proto;
+ struct hdlc_proto *proto = first_proto;
+ int result;
if (cmd != SIOCWANDEV)
return -EINVAL;
- switch(ifr->ifr_settings.type) {
- case IF_PROTO_HDLC:
- case IF_PROTO_HDLC_ETH:
- case IF_PROTO_PPP:
- case IF_PROTO_CISCO:
- case IF_PROTO_FR:
- case IF_PROTO_X25:
- proto = ifr->ifr_settings.type;
- break;
-
- default:
- proto = hdlc->proto.id;
+ if (dev_to_hdlc(dev)->proto) {
+ result = dev_to_hdlc(dev)->proto->ioctl(dev, ifr);
+ if (result != -EINVAL)
+ return result;
}
- switch(proto) {
- case IF_PROTO_HDLC: return hdlc_raw_ioctl(dev, ifr);
- case IF_PROTO_HDLC_ETH: return hdlc_raw_eth_ioctl(dev, ifr);
- case IF_PROTO_PPP: return hdlc_ppp_ioctl(dev, ifr);
- case IF_PROTO_CISCO: return hdlc_cisco_ioctl(dev, ifr);
- case IF_PROTO_FR: return hdlc_fr_ioctl(dev, ifr);
- case IF_PROTO_X25: return hdlc_x25_ioctl(dev, ifr);
- default: return -EINVAL;
+ /* Not handled by currently attached protocol (if any) */
+
+ while (proto) {
+ if ((result = proto->ioctl(dev, ifr)) != -EINVAL)
+ return result;
+ proto = proto->next;
}
+ return -EINVAL;
}
void hdlc_setup(struct net_device *dev)
@@ -267,8 +235,6 @@ void hdlc_setup(struct net_device *dev)
dev->flags = IFF_POINTOPOINT | IFF_NOARP;
- hdlc->proto.id = -1;
- hdlc->proto.detach = NULL;
hdlc->carrier = 1;
hdlc->open = 0;
spin_lock_init(&hdlc->state_lock);
@@ -277,7 +243,8 @@ void hdlc_setup(struct net_device *dev)
struct net_device *alloc_hdlcdev(void *priv)
{
struct net_device *dev;
- dev = alloc_netdev(sizeof(hdlc_device), "hdlc%d", hdlc_setup);
+ dev = alloc_netdev(sizeof(struct hdlc_device_desc) +
+ sizeof(hdlc_device), "hdlc%d", hdlc_setup);
if (dev)
dev_to_hdlc(dev)->priv = priv;
return dev;
@@ -286,13 +253,71 @@ struct net_device *alloc_hdlcdev(void *priv)
void unregister_hdlc_device(struct net_device *dev)
{
rtnl_lock();
- hdlc_proto_detach(dev_to_hdlc(dev));
unregister_netdevice(dev);
+ detach_hdlc_protocol(dev);
rtnl_unlock();
}
+int attach_hdlc_protocol(struct net_device *dev, struct hdlc_proto *proto,
+ int (*rx)(struct sk_buff *skb), size_t size)
+{
+ detach_hdlc_protocol(dev);
+
+ if (!try_module_get(proto->module))
+ return -ENOSYS;
+
+ if (size)
+ if ((dev_to_hdlc(dev)->state = kmalloc(size,
+ GFP_KERNEL)) == NULL) {
+ printk(KERN_WARNING "Memory squeeze on"
+ " hdlc_proto_attach()\n");
+ module_put(proto->module);
+ return -ENOBUFS;
+ }
+ dev_to_hdlc(dev)->proto = proto;
+ dev_to_desc(dev)->netif_rx = rx;
+ return 0;
+}
+
+
+void detach_hdlc_protocol(struct net_device *dev)
+{
+ hdlc_device *hdlc = dev_to_hdlc(dev);
+
+ if (hdlc->proto) {
+ if (hdlc->proto->detach)
+ hdlc->proto->detach(dev);
+ module_put(hdlc->proto->module);
+ hdlc->proto = NULL;
+ }
+ kfree(hdlc->state);
+ hdlc->state = NULL;
+}
+
+
+void register_hdlc_protocol(struct hdlc_proto *proto)
+{
+ proto->next = first_proto;
+ first_proto = proto;
+}
+
+
+void unregister_hdlc_protocol(struct hdlc_proto *proto)
+{
+ struct hdlc_proto **p = &first_proto;
+ while (*p) {
+ if (*p == proto) {
+ *p = proto->next;
+ return;
+ }
+ p = &((*p)->next);
+ }
+}
+
+
+
MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
MODULE_DESCRIPTION("HDLC support module");
MODULE_LICENSE("GPL v2");
@@ -303,6 +328,10 @@ EXPORT_SYMBOL(hdlc_ioctl);
EXPORT_SYMBOL(hdlc_setup);
EXPORT_SYMBOL(alloc_hdlcdev);
EXPORT_SYMBOL(unregister_hdlc_device);
+EXPORT_SYMBOL(register_hdlc_protocol);
+EXPORT_SYMBOL(unregister_hdlc_protocol);
+EXPORT_SYMBOL(attach_hdlc_protocol);
+EXPORT_SYMBOL(detach_hdlc_protocol);
static struct packet_type hdlc_packet_type = {
.type = __constant_htons(ETH_P_HDLC),
diff --git a/drivers/net/wan/hdlc_cisco.c b/drivers/net/wan/hdlc_cisco.c
index f289daba0c7..b0bc5ddcf1b 100644
--- a/drivers/net/wan/hdlc_cisco.c
+++ b/drivers/net/wan/hdlc_cisco.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Cisco HDLC support
*
- * Copyright (C) 2000 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2000 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -34,17 +34,56 @@
#define CISCO_KEEPALIVE_REQ 2 /* Cisco keepalive request */
+struct hdlc_header {
+ u8 address;
+ u8 control;
+ u16 protocol;
+}__attribute__ ((packed));
+
+
+struct cisco_packet {
+ u32 type; /* code */
+ u32 par1;
+ u32 par2;
+ u16 rel; /* reliability */
+ u32 time;
+}__attribute__ ((packed));
+#define CISCO_PACKET_LEN 18
+#define CISCO_BIG_PACKET_LEN 20
+
+
+struct cisco_state {
+ cisco_proto settings;
+
+ struct timer_list timer;
+ unsigned long last_poll;
+ int up;
+ int request_sent;
+ u32 txseq; /* TX sequence number */
+ u32 rxseq; /* RX sequence number */
+};
+
+
+static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
+static inline struct cisco_state * state(hdlc_device *hdlc)
+{
+ return(struct cisco_state *)(hdlc->state);
+}
+
+
static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
u16 type, void *daddr, void *saddr,
unsigned int len)
{
- hdlc_header *data;
+ struct hdlc_header *data;
#ifdef DEBUG_HARD_HEADER
printk(KERN_DEBUG "%s: cisco_hard_header called\n", dev->name);
#endif
- skb_push(skb, sizeof(hdlc_header));
- data = (hdlc_header*)skb->data;
+ skb_push(skb, sizeof(struct hdlc_header));
+ data = (struct hdlc_header*)skb->data;
if (type == CISCO_KEEPALIVE)
data->address = CISCO_MULTICAST;
else
@@ -52,7 +91,7 @@ static int cisco_hard_header(struct sk_buff *skb, struct net_device *dev,
data->control = 0;
data->protocol = htons(type);
- return sizeof(hdlc_header);
+ return sizeof(struct hdlc_header);
}
@@ -61,9 +100,10 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
u32 par1, u32 par2)
{
struct sk_buff *skb;
- cisco_packet *data;
+ struct cisco_packet *data;
- skb = dev_alloc_skb(sizeof(hdlc_header) + sizeof(cisco_packet));
+ skb = dev_alloc_skb(sizeof(struct hdlc_header) +
+ sizeof(struct cisco_packet));
if (!skb) {
printk(KERN_WARNING
"%s: Memory squeeze on cisco_keepalive_send()\n",
@@ -72,7 +112,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
}
skb_reserve(skb, 4);
cisco_hard_header(skb, dev, CISCO_KEEPALIVE, NULL, NULL, 0);
- data = (cisco_packet*)(skb->data + 4);
+ data = (struct cisco_packet*)(skb->data + 4);
data->type = htonl(type);
data->par1 = htonl(par1);
@@ -81,7 +121,7 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
/* we will need do_div here if 1000 % HZ != 0 */
data->time = htonl((jiffies - INITIAL_JIFFIES) * (1000 / HZ));
- skb_put(skb, sizeof(cisco_packet));
+ skb_put(skb, sizeof(struct cisco_packet));
skb->priority = TC_PRIO_CONTROL;
skb->dev = dev;
skb->nh.raw = skb->data;
@@ -93,9 +133,9 @@ static void cisco_keepalive_send(struct net_device *dev, u32 type,
static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
{
- hdlc_header *data = (hdlc_header*)skb->data;
+ struct hdlc_header *data = (struct hdlc_header*)skb->data;
- if (skb->len < sizeof(hdlc_header))
+ if (skb->len < sizeof(struct hdlc_header))
return __constant_htons(ETH_P_HDLC);
if (data->address != CISCO_MULTICAST &&
@@ -106,7 +146,7 @@ static __be16 cisco_type_trans(struct sk_buff *skb, struct net_device *dev)
case __constant_htons(ETH_P_IP):
case __constant_htons(ETH_P_IPX):
case __constant_htons(ETH_P_IPV6):
- skb_pull(skb, sizeof(hdlc_header));
+ skb_pull(skb, sizeof(struct hdlc_header));
return data->protocol;
default:
return __constant_htons(ETH_P_HDLC);
@@ -118,12 +158,12 @@ static int cisco_rx(struct sk_buff *skb)
{
struct net_device *dev = skb->dev;
hdlc_device *hdlc = dev_to_hdlc(dev);
- hdlc_header *data = (hdlc_header*)skb->data;
- cisco_packet *cisco_data;
+ struct hdlc_header *data = (struct hdlc_header*)skb->data;
+ struct cisco_packet *cisco_data;
struct in_device *in_dev;
- u32 addr, mask;
+ __be32 addr, mask;
- if (skb->len < sizeof(hdlc_header))
+ if (skb->len < sizeof(struct hdlc_header))
goto rx_error;
if (data->address != CISCO_MULTICAST &&
@@ -137,15 +177,17 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_SUCCESS;
case CISCO_KEEPALIVE:
- if (skb->len != sizeof(hdlc_header) + CISCO_PACKET_LEN &&
- skb->len != sizeof(hdlc_header) + CISCO_BIG_PACKET_LEN) {
- printk(KERN_INFO "%s: Invalid length of Cisco "
- "control packet (%d bytes)\n",
- dev->name, skb->len);
+ if ((skb->len != sizeof(struct hdlc_header) +
+ CISCO_PACKET_LEN) &&
+ (skb->len != sizeof(struct hdlc_header) +
+ CISCO_BIG_PACKET_LEN)) {
+ printk(KERN_INFO "%s: Invalid length of Cisco control"
+ " packet (%d bytes)\n", dev->name, skb->len);
goto rx_error;
}
- cisco_data = (cisco_packet*)(skb->data + sizeof(hdlc_header));
+ cisco_data = (struct cisco_packet*)(skb->data + sizeof
+ (struct hdlc_header));
switch(ntohl (cisco_data->type)) {
case CISCO_ADDR_REQ: /* Stolen from syncppp.c :-) */
@@ -178,11 +220,11 @@ static int cisco_rx(struct sk_buff *skb)
goto rx_error;
case CISCO_KEEPALIVE_REQ:
- hdlc->state.cisco.rxseq = ntohl(cisco_data->par1);
- if (hdlc->state.cisco.request_sent &&
- ntohl(cisco_data->par2)==hdlc->state.cisco.txseq) {
- hdlc->state.cisco.last_poll = jiffies;
- if (!hdlc->state.cisco.up) {
+ state(hdlc)->rxseq = ntohl(cisco_data->par1);
+ if (state(hdlc)->request_sent &&
+ ntohl(cisco_data->par2) == state(hdlc)->txseq) {
+ state(hdlc)->last_poll = jiffies;
+ if (!state(hdlc)->up) {
u32 sec, min, hrs, days;
sec = ntohl(cisco_data->time) / 1000;
min = sec / 60; sec -= min * 60;
@@ -193,7 +235,7 @@ static int cisco_rx(struct sk_buff *skb)
dev->name, days, hrs,
min, sec);
netif_dormant_off(dev);
- hdlc->state.cisco.up = 1;
+ state(hdlc)->up = 1;
}
}
@@ -208,7 +250,7 @@ static int cisco_rx(struct sk_buff *skb)
return NET_RX_DROP;
rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
+ dev_to_desc(dev)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -220,23 +262,22 @@ static void cisco_timer(unsigned long arg)
struct net_device *dev = (struct net_device *)arg;
hdlc_device *hdlc = dev_to_hdlc(dev);
- if (hdlc->state.cisco.up &&
- time_after(jiffies, hdlc->state.cisco.last_poll +
- hdlc->state.cisco.settings.timeout * HZ)) {
- hdlc->state.cisco.up = 0;
+ if (state(hdlc)->up &&
+ time_after(jiffies, state(hdlc)->last_poll +
+ state(hdlc)->settings.timeout * HZ)) {
+ state(hdlc)->up = 0;
printk(KERN_INFO "%s: Link down\n", dev->name);
netif_dormant_on(dev);
}
- cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ,
- ++hdlc->state.cisco.txseq,
- hdlc->state.cisco.rxseq);
- hdlc->state.cisco.request_sent = 1;
- hdlc->state.cisco.timer.expires = jiffies +
- hdlc->state.cisco.settings.interval * HZ;
- hdlc->state.cisco.timer.function = cisco_timer;
- hdlc->state.cisco.timer.data = arg;
- add_timer(&hdlc->state.cisco.timer);
+ cisco_keepalive_send(dev, CISCO_KEEPALIVE_REQ, ++state(hdlc)->txseq,
+ state(hdlc)->rxseq);
+ state(hdlc)->request_sent = 1;
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.interval * HZ;
+ state(hdlc)->timer.function = cisco_timer;
+ state(hdlc)->timer.data = arg;
+ add_timer(&state(hdlc)->timer);
}
@@ -244,15 +285,15 @@ static void cisco_timer(unsigned long arg)
static void cisco_start(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- hdlc->state.cisco.up = 0;
- hdlc->state.cisco.request_sent = 0;
- hdlc->state.cisco.txseq = hdlc->state.cisco.rxseq = 0;
-
- init_timer(&hdlc->state.cisco.timer);
- hdlc->state.cisco.timer.expires = jiffies + HZ; /*First poll after 1s*/
- hdlc->state.cisco.timer.function = cisco_timer;
- hdlc->state.cisco.timer.data = (unsigned long)dev;
- add_timer(&hdlc->state.cisco.timer);
+ state(hdlc)->up = 0;
+ state(hdlc)->request_sent = 0;
+ state(hdlc)->txseq = state(hdlc)->rxseq = 0;
+
+ init_timer(&state(hdlc)->timer);
+ state(hdlc)->timer.expires = jiffies + HZ; /*First poll after 1s*/
+ state(hdlc)->timer.function = cisco_timer;
+ state(hdlc)->timer.data = (unsigned long)dev;
+ add_timer(&state(hdlc)->timer);
}
@@ -260,15 +301,24 @@ static void cisco_start(struct net_device *dev)
static void cisco_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- del_timer_sync(&hdlc->state.cisco.timer);
+ del_timer_sync(&state(hdlc)->timer);
netif_dormant_on(dev);
- hdlc->state.cisco.up = 0;
- hdlc->state.cisco.request_sent = 0;
+ state(hdlc)->up = 0;
+ state(hdlc)->request_sent = 0;
}
-int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .start = cisco_start,
+ .stop = cisco_stop,
+ .type_trans = cisco_type_trans,
+ .ioctl = cisco_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
{
cisco_proto __user *cisco_s = ifr->ifr_settings.ifs_ifsu.cisco;
const size_t size = sizeof(cisco_proto);
@@ -278,12 +328,14 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_CISCO;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(cisco_s, &hdlc->state.cisco.settings, size))
+ if (copy_to_user(cisco_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@@ -302,19 +354,15 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
result=hdlc->attach(dev, ENCODING_NRZ,PARITY_CRC16_PR1_CCITT);
-
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.cisco.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
+ result = attach_hdlc_protocol(dev, &proto, cisco_rx,
+ sizeof(struct cisco_state));
+ if (result)
+ return result;
- hdlc->proto.start = cisco_start;
- hdlc->proto.stop = cisco_stop;
- hdlc->proto.netif_rx = cisco_rx;
- hdlc->proto.type_trans = cisco_type_trans;
- hdlc->proto.id = IF_PROTO_CISCO;
+ memcpy(&state(hdlc)->settings, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = cisco_hard_header;
dev->hard_header_cache = NULL;
@@ -327,3 +375,25 @@ int hdlc_cisco_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Cisco HDLC protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_fr.c b/drivers/net/wan/hdlc_fr.c
index 7bb737bbdeb..b45ab680d2d 100644
--- a/drivers/net/wan/hdlc_fr.c
+++ b/drivers/net/wan/hdlc_fr.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Frame Relay support
*
- * Copyright (C) 1999 - 2005 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -52,6 +52,8 @@
#undef DEBUG_PKT
#undef DEBUG_ECN
#undef DEBUG_LINK
+#undef DEBUG_PROTO
+#undef DEBUG_PVC
#define FR_UI 0x03
#define FR_PAD 0x00
@@ -115,13 +117,53 @@ typedef struct {
}__attribute__ ((packed)) fr_hdr;
+typedef struct pvc_device_struct {
+ struct net_device *frad;
+ struct net_device *main;
+ struct net_device *ether; /* bridged Ethernet interface */
+ struct pvc_device_struct *next; /* Sorted in ascending DLCI order */
+ int dlci;
+ int open_count;
+
+ struct {
+ unsigned int new: 1;
+ unsigned int active: 1;
+ unsigned int exist: 1;
+ unsigned int deleted: 1;
+ unsigned int fecn: 1;
+ unsigned int becn: 1;
+ unsigned int bandwidth; /* Cisco LMI reporting only */
+ }state;
+}pvc_device;
+
+
+struct frad_state {
+ fr_proto settings;
+ pvc_device *first_pvc;
+ int dce_pvc_count;
+
+ struct timer_list timer;
+ unsigned long last_poll;
+ int reliable;
+ int dce_changed;
+ int request;
+ int fullrep_sent;
+ u32 last_errors; /* last errors bit list */
+ u8 n391cnt;
+ u8 txseq; /* TX sequence number */
+ u8 rxseq; /* RX sequence number */
+};
+
+
+static int fr_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
static inline u16 q922_to_dlci(u8 *hdr)
{
return ((hdr[0] & 0xFC) << 2) | ((hdr[1] & 0xF0) >> 4);
}
-
static inline void dlci_to_q922(u8 *hdr, u16 dlci)
{
hdr[0] = (dlci >> 2) & 0xFC;
@@ -129,10 +171,21 @@ static inline void dlci_to_q922(u8 *hdr, u16 dlci)
}
+static inline struct frad_state * state(hdlc_device *hdlc)
+{
+ return(struct frad_state *)(hdlc->state);
+}
+
+
+static __inline__ pvc_device* dev_to_pvc(struct net_device *dev)
+{
+ return dev->priv;
+}
+
static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
{
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->dlci == dlci)
@@ -146,10 +199,10 @@ static inline pvc_device* find_pvc(hdlc_device *hdlc, u16 dlci)
}
-static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
+static pvc_device* add_pvc(struct net_device *dev, u16 dlci)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc, **pvc_p = &hdlc->state.fr.first_pvc;
+ pvc_device *pvc, **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if ((*pvc_p)->dlci == dlci)
@@ -160,12 +213,15 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
}
pvc = kmalloc(sizeof(pvc_device), GFP_ATOMIC);
+#ifdef DEBUG_PVC
+ printk(KERN_DEBUG "add_pvc: allocated pvc %p, frad %p\n", pvc, dev);
+#endif
if (!pvc)
return NULL;
memset(pvc, 0, sizeof(pvc_device));
pvc->dlci = dlci;
- pvc->master = dev;
+ pvc->frad = dev;
pvc->next = *pvc_p; /* Put it in the chain */
*pvc_p = pvc;
return pvc;
@@ -174,7 +230,7 @@ static inline pvc_device* add_pvc(struct net_device *dev, u16 dlci)
static inline int pvc_is_used(pvc_device *pvc)
{
- return pvc->main != NULL || pvc->ether != NULL;
+ return pvc->main || pvc->ether;
}
@@ -200,11 +256,14 @@ static inline void pvc_carrier(int on, pvc_device *pvc)
static inline void delete_unused_pvcs(hdlc_device *hdlc)
{
- pvc_device **pvc_p = &hdlc->state.fr.first_pvc;
+ pvc_device **pvc_p = &state(hdlc)->first_pvc;
while (*pvc_p) {
if (!pvc_is_used(*pvc_p)) {
pvc_device *pvc = *pvc_p;
+#ifdef DEBUG_PVC
+ printk(KERN_DEBUG "freeing unused pvc: %p\n", pvc);
+#endif
*pvc_p = pvc->next;
kfree(pvc);
continue;
@@ -295,16 +354,16 @@ static int pvc_open(struct net_device *dev)
{
pvc_device *pvc = dev_to_pvc(dev);
- if ((pvc->master->flags & IFF_UP) == 0)
- return -EIO; /* Master must be UP in order to activate PVC */
+ if ((pvc->frad->flags & IFF_UP) == 0)
+ return -EIO; /* Frad must be UP in order to activate PVC */
if (pvc->open_count++ == 0) {
- hdlc_device *hdlc = dev_to_hdlc(pvc->master);
- if (hdlc->state.fr.settings.lmi == LMI_NONE)
- pvc->state.active = netif_carrier_ok(pvc->master);
+ hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
+ if (state(hdlc)->settings.lmi == LMI_NONE)
+ pvc->state.active = netif_carrier_ok(pvc->frad);
pvc_carrier(pvc->state.active, pvc);
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_changed = 1;
}
return 0;
}
@@ -316,12 +375,12 @@ static int pvc_close(struct net_device *dev)
pvc_device *pvc = dev_to_pvc(dev);
if (--pvc->open_count == 0) {
- hdlc_device *hdlc = dev_to_hdlc(pvc->master);
- if (hdlc->state.fr.settings.lmi == LMI_NONE)
+ hdlc_device *hdlc = dev_to_hdlc(pvc->frad);
+ if (state(hdlc)->settings.lmi == LMI_NONE)
pvc->state.active = 0;
- if (hdlc->state.fr.settings.dce) {
- hdlc->state.fr.dce_changed = 1;
+ if (state(hdlc)->settings.dce) {
+ state(hdlc)->dce_changed = 1;
pvc->state.active = 0;
}
}
@@ -348,7 +407,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
info.dlci = pvc->dlci;
- memcpy(info.master, pvc->master->name, IFNAMSIZ);
+ memcpy(info.master, pvc->frad->name, IFNAMSIZ);
if (copy_to_user(ifr->ifr_settings.ifs_ifsu.fr_pvc_info,
&info, sizeof(info)))
return -EFAULT;
@@ -361,7 +420,7 @@ static int pvc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
static inline struct net_device_stats *pvc_get_stats(struct net_device *dev)
{
- return netdev_priv(dev);
+ return &dev_to_desc(dev)->stats;
}
@@ -393,7 +452,7 @@ static int pvc_xmit(struct sk_buff *skb, struct net_device *dev)
stats->tx_packets++;
if (pvc->state.fecn) /* TX Congestion counter */
stats->tx_compressed++;
- skb->dev = pvc->master;
+ skb->dev = pvc->frad;
dev_queue_xmit(skb);
return 0;
}
@@ -419,7 +478,7 @@ static int pvc_change_mtu(struct net_device *dev, int new_mtu)
static inline void fr_log_dlci_active(pvc_device *pvc)
{
printk(KERN_INFO "%s: DLCI %d [%s%s%s]%s %s\n",
- pvc->master->name,
+ pvc->frad->name,
pvc->dlci,
pvc->main ? pvc->main->name : "",
pvc->main && pvc->ether ? " " : "",
@@ -438,21 +497,20 @@ static inline u8 fr_lmi_nextseq(u8 x)
}
-
static void fr_lmi_send(struct net_device *dev, int fullrep)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
struct sk_buff *skb;
- pvc_device *pvc = hdlc->state.fr.first_pvc;
- int lmi = hdlc->state.fr.settings.lmi;
- int dce = hdlc->state.fr.settings.dce;
+ pvc_device *pvc = state(hdlc)->first_pvc;
+ int lmi = state(hdlc)->settings.lmi;
+ int dce = state(hdlc)->settings.dce;
int len = lmi == LMI_ANSI ? LMI_ANSI_LENGTH : LMI_CCITT_CISCO_LENGTH;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3;
u8 *data;
int i = 0;
if (dce && fullrep) {
- len += hdlc->state.fr.dce_pvc_count * (2 + stat_len);
+ len += state(hdlc)->dce_pvc_count * (2 + stat_len);
if (len > HDLC_MAX_MRU) {
printk(KERN_WARNING "%s: Too many PVCs while sending "
"LMI full report\n", dev->name);
@@ -486,8 +544,9 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = fullrep ? LMI_FULLREP : LMI_INTEGRITY;
data[i++] = lmi == LMI_CCITT ? LMI_CCITT_ALIVE : LMI_ANSI_CISCO_ALIVE;
data[i++] = LMI_INTEG_LEN;
- data[i++] = hdlc->state.fr.txseq =fr_lmi_nextseq(hdlc->state.fr.txseq);
- data[i++] = hdlc->state.fr.rxseq;
+ data[i++] = state(hdlc)->txseq =
+ fr_lmi_nextseq(state(hdlc)->txseq);
+ data[i++] = state(hdlc)->rxseq;
if (dce && fullrep) {
while (pvc) {
@@ -496,7 +555,7 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
data[i++] = stat_len;
/* LMI start/restart */
- if (hdlc->state.fr.reliable && !pvc->state.exist) {
+ if (state(hdlc)->reliable && !pvc->state.exist) {
pvc->state.exist = pvc->state.new = 1;
fr_log_dlci_active(pvc);
}
@@ -541,15 +600,15 @@ static void fr_lmi_send(struct net_device *dev, int fullrep)
static void fr_set_link_state(int reliable, struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
- hdlc->state.fr.reliable = reliable;
+ state(hdlc)->reliable = reliable;
if (reliable) {
netif_dormant_off(dev);
- hdlc->state.fr.n391cnt = 0; /* Request full status */
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->n391cnt = 0; /* Request full status */
+ state(hdlc)->dce_changed = 1;
- if (hdlc->state.fr.settings.lmi == LMI_NONE) {
+ if (state(hdlc)->settings.lmi == LMI_NONE) {
while (pvc) { /* Activate all PVCs */
pvc_carrier(1, pvc);
pvc->state.exist = pvc->state.active = 1;
@@ -563,7 +622,7 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
pvc_carrier(0, pvc);
pvc->state.exist = pvc->state.active = 0;
pvc->state.new = 0;
- if (!hdlc->state.fr.settings.dce)
+ if (!state(hdlc)->settings.dce)
pvc->state.bandwidth = 0;
pvc = pvc->next;
}
@@ -571,7 +630,6 @@ static void fr_set_link_state(int reliable, struct net_device *dev)
}
-
static void fr_timer(unsigned long arg)
{
struct net_device *dev = (struct net_device *)arg;
@@ -579,62 +637,61 @@ static void fr_timer(unsigned long arg)
int i, cnt = 0, reliable;
u32 list;
- if (hdlc->state.fr.settings.dce) {
- reliable = hdlc->state.fr.request &&
- time_before(jiffies, hdlc->state.fr.last_poll +
- hdlc->state.fr.settings.t392 * HZ);
- hdlc->state.fr.request = 0;
+ if (state(hdlc)->settings.dce) {
+ reliable = state(hdlc)->request &&
+ time_before(jiffies, state(hdlc)->last_poll +
+ state(hdlc)->settings.t392 * HZ);
+ state(hdlc)->request = 0;
} else {
- hdlc->state.fr.last_errors <<= 1; /* Shift the list */
- if (hdlc->state.fr.request) {
- if (hdlc->state.fr.reliable)
+ state(hdlc)->last_errors <<= 1; /* Shift the list */
+ if (state(hdlc)->request) {
+ if (state(hdlc)->reliable)
printk(KERN_INFO "%s: No LMI status reply "
"received\n", dev->name);
- hdlc->state.fr.last_errors |= 1;
+ state(hdlc)->last_errors |= 1;
}
- list = hdlc->state.fr.last_errors;
- for (i = 0; i < hdlc->state.fr.settings.n393; i++, list >>= 1)
+ list = state(hdlc)->last_errors;
+ for (i = 0; i < state(hdlc)->settings.n393; i++, list >>= 1)
cnt += (list & 1); /* errors count */
- reliable = (cnt < hdlc->state.fr.settings.n392);
+ reliable = (cnt < state(hdlc)->settings.n392);
}
- if (hdlc->state.fr.reliable != reliable) {
+ if (state(hdlc)->reliable != reliable) {
printk(KERN_INFO "%s: Link %sreliable\n", dev->name,
reliable ? "" : "un");
fr_set_link_state(reliable, dev);
}
- if (hdlc->state.fr.settings.dce)
- hdlc->state.fr.timer.expires = jiffies +
- hdlc->state.fr.settings.t392 * HZ;
+ if (state(hdlc)->settings.dce)
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.t392 * HZ;
else {
- if (hdlc->state.fr.n391cnt)
- hdlc->state.fr.n391cnt--;
+ if (state(hdlc)->n391cnt)
+ state(hdlc)->n391cnt--;
- fr_lmi_send(dev, hdlc->state.fr.n391cnt == 0);
+ fr_lmi_send(dev, state(hdlc)->n391cnt == 0);
- hdlc->state.fr.last_poll = jiffies;
- hdlc->state.fr.request = 1;
- hdlc->state.fr.timer.expires = jiffies +
- hdlc->state.fr.settings.t391 * HZ;
+ state(hdlc)->last_poll = jiffies;
+ state(hdlc)->request = 1;
+ state(hdlc)->timer.expires = jiffies +
+ state(hdlc)->settings.t391 * HZ;
}
- hdlc->state.fr.timer.function = fr_timer;
- hdlc->state.fr.timer.data = arg;
- add_timer(&hdlc->state.fr.timer);
+ state(hdlc)->timer.function = fr_timer;
+ state(hdlc)->timer.data = arg;
+ add_timer(&state(hdlc)->timer);
}
-
static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
pvc_device *pvc;
u8 rxseq, txseq;
- int lmi = hdlc->state.fr.settings.lmi;
- int dce = hdlc->state.fr.settings.dce;
+ int lmi = state(hdlc)->settings.lmi;
+ int dce = state(hdlc)->settings.dce;
int stat_len = (lmi == LMI_CISCO) ? 6 : 3, reptype, error, no_ram, i;
if (skb->len < (lmi == LMI_ANSI ? LMI_ANSI_LENGTH :
@@ -645,8 +702,8 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (skb->data[3] != (lmi == LMI_CISCO ? NLPID_CISCO_LMI :
NLPID_CCITT_ANSI_LMI)) {
- printk(KERN_INFO "%s: Received non-LMI frame with LMI"
- " DLCI\n", dev->name);
+ printk(KERN_INFO "%s: Received non-LMI frame with LMI DLCI\n",
+ dev->name);
return 1;
}
@@ -706,53 +763,53 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
i++;
- hdlc->state.fr.rxseq = skb->data[i++]; /* TX sequence from peer */
+ state(hdlc)->rxseq = skb->data[i++]; /* TX sequence from peer */
rxseq = skb->data[i++]; /* Should confirm our sequence */
- txseq = hdlc->state.fr.txseq;
+ txseq = state(hdlc)->txseq;
if (dce)
- hdlc->state.fr.last_poll = jiffies;
+ state(hdlc)->last_poll = jiffies;
error = 0;
- if (!hdlc->state.fr.reliable)
+ if (!state(hdlc)->reliable)
error = 1;
- if (rxseq == 0 || rxseq != txseq) {
- hdlc->state.fr.n391cnt = 0; /* Ask for full report next time */
+ if (rxseq == 0 || rxseq != txseq) { /* Ask for full report next time */
+ state(hdlc)->n391cnt = 0;
error = 1;
}
if (dce) {
- if (hdlc->state.fr.fullrep_sent && !error) {
+ if (state(hdlc)->fullrep_sent && !error) {
/* Stop sending full report - the last one has been confirmed by DTE */
- hdlc->state.fr.fullrep_sent = 0;
- pvc = hdlc->state.fr.first_pvc;
+ state(hdlc)->fullrep_sent = 0;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.new) {
pvc->state.new = 0;
/* Tell DTE that new PVC is now active */
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_changed = 1;
}
pvc = pvc->next;
}
}
- if (hdlc->state.fr.dce_changed) {
+ if (state(hdlc)->dce_changed) {
reptype = LMI_FULLREP;
- hdlc->state.fr.fullrep_sent = 1;
- hdlc->state.fr.dce_changed = 0;
+ state(hdlc)->fullrep_sent = 1;
+ state(hdlc)->dce_changed = 0;
}
- hdlc->state.fr.request = 1; /* got request */
+ state(hdlc)->request = 1; /* got request */
fr_lmi_send(dev, reptype == LMI_FULLREP ? 1 : 0);
return 0;
}
/* DTE */
- hdlc->state.fr.request = 0; /* got response, no request pending */
+ state(hdlc)->request = 0; /* got response, no request pending */
if (error)
return 0;
@@ -760,7 +817,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
if (reptype != LMI_FULLREP)
return 0;
- pvc = hdlc->state.fr.first_pvc;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
pvc->state.deleted = 1;
@@ -827,7 +884,7 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
i += stat_len;
}
- pvc = hdlc->state.fr.first_pvc;
+ pvc = state(hdlc)->first_pvc;
while (pvc) {
if (pvc->state.deleted && pvc->state.exist) {
@@ -841,17 +898,16 @@ static int fr_lmi_recv(struct net_device *dev, struct sk_buff *skb)
}
/* Next full report after N391 polls */
- hdlc->state.fr.n391cnt = hdlc->state.fr.settings.n391;
+ state(hdlc)->n391cnt = state(hdlc)->settings.n391;
return 0;
}
-
static int fr_rx(struct sk_buff *skb)
{
- struct net_device *ndev = skb->dev;
- hdlc_device *hdlc = dev_to_hdlc(ndev);
+ struct net_device *frad = skb->dev;
+ hdlc_device *hdlc = dev_to_hdlc(frad);
fr_hdr *fh = (fr_hdr*)skb->data;
u8 *data = skb->data;
u16 dlci;
@@ -864,11 +920,11 @@ static int fr_rx(struct sk_buff *skb)
dlci = q922_to_dlci(skb->data);
if ((dlci == LMI_CCITT_ANSI_DLCI &&
- (hdlc->state.fr.settings.lmi == LMI_ANSI ||
- hdlc->state.fr.settings.lmi == LMI_CCITT)) ||
+ (state(hdlc)->settings.lmi == LMI_ANSI ||
+ state(hdlc)->settings.lmi == LMI_CCITT)) ||
(dlci == LMI_CISCO_DLCI &&
- hdlc->state.fr.settings.lmi == LMI_CISCO)) {
- if (fr_lmi_recv(ndev, skb))
+ state(hdlc)->settings.lmi == LMI_CISCO)) {
+ if (fr_lmi_recv(frad, skb))
goto rx_error;
dev_kfree_skb_any(skb);
return NET_RX_SUCCESS;
@@ -878,7 +934,7 @@ static int fr_rx(struct sk_buff *skb)
if (!pvc) {
#ifdef DEBUG_PKT
printk(KERN_INFO "%s: No PVC for received frame's DLCI %d\n",
- ndev->name, dlci);
+ frad->name, dlci);
#endif
dev_kfree_skb_any(skb);
return NET_RX_DROP;
@@ -886,7 +942,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.fecn != fh->fecn) {
#ifdef DEBUG_ECN
- printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", ndev->name,
+ printk(KERN_DEBUG "%s: DLCI %d FECN O%s\n", frad->name,
dlci, fh->fecn ? "N" : "FF");
#endif
pvc->state.fecn ^= 1;
@@ -894,7 +950,7 @@ static int fr_rx(struct sk_buff *skb)
if (pvc->state.becn != fh->becn) {
#ifdef DEBUG_ECN
- printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", ndev->name,
+ printk(KERN_DEBUG "%s: DLCI %d BECN O%s\n", frad->name,
dlci, fh->becn ? "N" : "FF");
#endif
pvc->state.becn ^= 1;
@@ -902,7 +958,7 @@ static int fr_rx(struct sk_buff *skb)
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- hdlc->stats.rx_dropped++;
+ dev_to_desc(frad)->stats.rx_dropped++;
return NET_RX_DROP;
}
@@ -938,13 +994,13 @@ static int fr_rx(struct sk_buff *skb)
default:
printk(KERN_INFO "%s: Unsupported protocol, OUI=%x "
- "PID=%x\n", ndev->name, oui, pid);
+ "PID=%x\n", frad->name, oui, pid);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
} else {
printk(KERN_INFO "%s: Unsupported protocol, NLPID=%x "
- "length = %i\n", ndev->name, data[3], skb->len);
+ "length = %i\n", frad->name, data[3], skb->len);
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -964,7 +1020,7 @@ static int fr_rx(struct sk_buff *skb)
}
rx_error:
- hdlc->stats.rx_errors++; /* Mark error */
+ dev_to_desc(frad)->stats.rx_errors++; /* Mark error */
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
@@ -977,44 +1033,42 @@ static void fr_start(struct net_device *dev)
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_start\n");
#endif
- if (hdlc->state.fr.settings.lmi != LMI_NONE) {
- hdlc->state.fr.reliable = 0;
- hdlc->state.fr.dce_changed = 1;
- hdlc->state.fr.request = 0;
- hdlc->state.fr.fullrep_sent = 0;
- hdlc->state.fr.last_errors = 0xFFFFFFFF;
- hdlc->state.fr.n391cnt = 0;
- hdlc->state.fr.txseq = hdlc->state.fr.rxseq = 0;
-
- init_timer(&hdlc->state.fr.timer);
+ if (state(hdlc)->settings.lmi != LMI_NONE) {
+ state(hdlc)->reliable = 0;
+ state(hdlc)->dce_changed = 1;
+ state(hdlc)->request = 0;
+ state(hdlc)->fullrep_sent = 0;
+ state(hdlc)->last_errors = 0xFFFFFFFF;
+ state(hdlc)->n391cnt = 0;
+ state(hdlc)->txseq = state(hdlc)->rxseq = 0;
+
+ init_timer(&state(hdlc)->timer);
/* First poll after 1 s */
- hdlc->state.fr.timer.expires = jiffies + HZ;
- hdlc->state.fr.timer.function = fr_timer;
- hdlc->state.fr.timer.data = (unsigned long)dev;
- add_timer(&hdlc->state.fr.timer);
+ state(hdlc)->timer.expires = jiffies + HZ;
+ state(hdlc)->timer.function = fr_timer;
+ state(hdlc)->timer.data = (unsigned long)dev;
+ add_timer(&state(hdlc)->timer);
} else
fr_set_link_state(1, dev);
}
-
static void fr_stop(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
#ifdef DEBUG_LINK
printk(KERN_DEBUG "fr_stop\n");
#endif
- if (hdlc->state.fr.settings.lmi != LMI_NONE)
- del_timer_sync(&hdlc->state.fr.timer);
+ if (state(hdlc)->settings.lmi != LMI_NONE)
+ del_timer_sync(&state(hdlc)->timer);
fr_set_link_state(0, dev);
}
-
static void fr_close(struct net_device *dev)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
- pvc_device *pvc = hdlc->state.fr.first_pvc;
+ pvc_device *pvc = state(hdlc)->first_pvc;
while (pvc) { /* Shutdown all PVCs for this FRAD */
if (pvc->main)
@@ -1025,7 +1079,8 @@ static void fr_close(struct net_device *dev)
}
}
-static void dlci_setup(struct net_device *dev)
+
+static void pvc_setup(struct net_device *dev)
{
dev->type = ARPHRD_DLCI;
dev->flags = IFF_POINTOPOINT;
@@ -1033,9 +1088,9 @@ static void dlci_setup(struct net_device *dev)
dev->addr_len = 2;
}
-static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
+static int fr_add_pvc(struct net_device *frad, unsigned int dlci, int type)
{
- hdlc_device *hdlc = dev_to_hdlc(master);
+ hdlc_device *hdlc = dev_to_hdlc(frad);
pvc_device *pvc = NULL;
struct net_device *dev;
int result, used;
@@ -1044,9 +1099,9 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
if (type == ARPHRD_ETHER)
prefix = "pvceth%d";
- if ((pvc = add_pvc(master, dlci)) == NULL) {
+ if ((pvc = add_pvc(frad, dlci)) == NULL) {
printk(KERN_WARNING "%s: Memory squeeze on fr_add_pvc()\n",
- master->name);
+ frad->name);
return -ENOBUFS;
}
@@ -1060,11 +1115,11 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
"pvceth%d", ether_setup);
else
dev = alloc_netdev(sizeof(struct net_device_stats),
- "pvc%d", dlci_setup);
+ "pvc%d", pvc_setup);
if (!dev) {
printk(KERN_WARNING "%s: Memory squeeze on fr_pvc()\n",
- master->name);
+ frad->name);
delete_unused_pvcs(hdlc);
return -ENOBUFS;
}
@@ -1102,8 +1157,8 @@ static int fr_add_pvc(struct net_device *master, unsigned int dlci, int type)
dev->destructor = free_netdev;
*get_dev_p(pvc, type) = dev;
if (!used) {
- hdlc->state.fr.dce_changed = 1;
- hdlc->state.fr.dce_pvc_count++;
+ state(hdlc)->dce_changed = 1;
+ state(hdlc)->dce_pvc_count++;
}
return 0;
}
@@ -1128,8 +1183,8 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
*get_dev_p(pvc, type) = NULL;
if (!pvc_is_used(pvc)) {
- hdlc->state.fr.dce_pvc_count--;
- hdlc->state.fr.dce_changed = 1;
+ state(hdlc)->dce_pvc_count--;
+ state(hdlc)->dce_changed = 1;
}
delete_unused_pvcs(hdlc);
return 0;
@@ -1137,14 +1192,13 @@ static int fr_del_pvc(hdlc_device *hdlc, unsigned int dlci, int type)
-static void fr_destroy(hdlc_device *hdlc)
+static void fr_destroy(struct net_device *frad)
{
- pvc_device *pvc;
-
- pvc = hdlc->state.fr.first_pvc;
- hdlc->state.fr.first_pvc = NULL; /* All PVCs destroyed */
- hdlc->state.fr.dce_pvc_count = 0;
- hdlc->state.fr.dce_changed = 1;
+ hdlc_device *hdlc = dev_to_hdlc(frad);
+ pvc_device *pvc = state(hdlc)->first_pvc;
+ state(hdlc)->first_pvc = NULL; /* All PVCs destroyed */
+ state(hdlc)->dce_pvc_count = 0;
+ state(hdlc)->dce_changed = 1;
while (pvc) {
pvc_device *next = pvc->next;
@@ -1161,8 +1215,17 @@ static void fr_destroy(hdlc_device *hdlc)
}
+static struct hdlc_proto proto = {
+ .close = fr_close,
+ .start = fr_start,
+ .stop = fr_stop,
+ .detach = fr_destroy,
+ .ioctl = fr_ioctl,
+ .module = THIS_MODULE,
+};
+
-int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
+static int fr_ioctl(struct net_device *dev, struct ifreq *ifr)
{
fr_proto __user *fr_s = ifr->ifr_settings.ifs_ifsu.fr;
const size_t size = sizeof(fr_proto);
@@ -1173,12 +1236,14 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_FR;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(fr_s, &hdlc->state.fr.settings, size))
+ if (copy_to_user(fr_s, &state(hdlc)->settings, size))
return -EFAULT;
return 0;
@@ -1213,20 +1278,16 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- if (hdlc->proto.id != IF_PROTO_FR) {
- hdlc_proto_detach(hdlc);
- hdlc->state.fr.first_pvc = NULL;
- hdlc->state.fr.dce_pvc_count = 0;
+ if (dev_to_hdlc(dev)->proto != &proto) { /* Different proto */
+ result = attach_hdlc_protocol(dev, &proto, fr_rx,
+ sizeof(struct frad_state));
+ if (result)
+ return result;
+ state(hdlc)->first_pvc = NULL;
+ state(hdlc)->dce_pvc_count = 0;
}
- memcpy(&hdlc->state.fr.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.close = fr_close;
- hdlc->proto.start = fr_start;
- hdlc->proto.stop = fr_stop;
- hdlc->proto.detach = fr_destroy;
- hdlc->proto.netif_rx = fr_rx;
- hdlc->proto.id = IF_PROTO_FR;
+ memcpy(&state(hdlc)->settings, &new_settings, size);
+
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_FRAD;
@@ -1238,6 +1299,9 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
case IF_PROTO_FR_DEL_PVC:
case IF_PROTO_FR_ADD_ETH_PVC:
case IF_PROTO_FR_DEL_ETH_PVC:
+ if (dev_to_hdlc(dev)->proto != &proto) /* Different proto */
+ return -EINVAL;
+
if(!capable(CAP_NET_ADMIN))
return -EPERM;
@@ -1263,3 +1327,24 @@ int hdlc_fr_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Frame-Relay protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_ppp.c b/drivers/net/wan/hdlc_ppp.c
index fbaab5bf71e..e9f717070fd 100644
--- a/drivers/net/wan/hdlc_ppp.c
+++ b/drivers/net/wan/hdlc_ppp.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* Point-to-point protocol support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -22,6 +22,21 @@
#include <linux/lapb.h>
#include <linux/rtnetlink.h>
#include <linux/hdlc.h>
+#include <net/syncppp.h>
+
+struct ppp_state {
+ struct ppp_device pppdev;
+ struct ppp_device *syncppp_ptr;
+ int (*old_change_mtu)(struct net_device *dev, int new_mtu);
+};
+
+static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr);
+
+
+static inline struct ppp_state* state(hdlc_device *hdlc)
+{
+ return(struct ppp_state *)(hdlc->state);
+}
static int ppp_open(struct net_device *dev)
@@ -30,16 +45,16 @@ static int ppp_open(struct net_device *dev)
void *old_ioctl;
int result;
- dev->priv = &hdlc->state.ppp.syncppp_ptr;
- hdlc->state.ppp.syncppp_ptr = &hdlc->state.ppp.pppdev;
- hdlc->state.ppp.pppdev.dev = dev;
+ dev->priv = &state(hdlc)->syncppp_ptr;
+ state(hdlc)->syncppp_ptr = &state(hdlc)->pppdev;
+ state(hdlc)->pppdev.dev = dev;
old_ioctl = dev->do_ioctl;
- hdlc->state.ppp.old_change_mtu = dev->change_mtu;
- sppp_attach(&hdlc->state.ppp.pppdev);
+ state(hdlc)->old_change_mtu = dev->change_mtu;
+ sppp_attach(&state(hdlc)->pppdev);
/* sppp_attach nukes them. We don't need syncppp's ioctl */
dev->do_ioctl = old_ioctl;
- hdlc->state.ppp.pppdev.sppp.pp_flags &= ~PP_CISCO;
+ state(hdlc)->pppdev.sppp.pp_flags &= ~PP_CISCO;
dev->type = ARPHRD_PPP;
result = sppp_open(dev);
if (result) {
@@ -59,7 +74,7 @@ static void ppp_close(struct net_device *dev)
sppp_close(dev);
sppp_detach(dev);
dev->rebuild_header = NULL;
- dev->change_mtu = hdlc->state.ppp.old_change_mtu;
+ dev->change_mtu = state(hdlc)->old_change_mtu;
dev->mtu = HDLC_MAX_MTU;
dev->hard_header_len = 16;
}
@@ -73,13 +88,24 @@ static __be16 ppp_type_trans(struct sk_buff *skb, struct net_device *dev)
-int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .open = ppp_open,
+ .close = ppp_close,
+ .type_trans = ppp_type_trans,
+ .ioctl = ppp_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_PPP;
return 0; /* return protocol only, no settable parameters */
@@ -96,13 +122,10 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.open = ppp_open;
- hdlc->proto.close = ppp_close;
- hdlc->proto.type_trans = ppp_type_trans;
- hdlc->proto.id = IF_PROTO_PPP;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(struct ppp_state));
+ if (result)
+ return result;
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_PPP;
@@ -113,3 +136,25 @@ int hdlc_ppp_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("PPP protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_raw.c b/drivers/net/wan/hdlc_raw.c
index f15aa6ba77f..fe3cae5c6b9 100644
--- a/drivers/net/wan/hdlc_raw.c
+++ b/drivers/net/wan/hdlc_raw.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -24,6 +24,8 @@
#include <linux/hdlc.h>
+static int raw_ioctl(struct net_device *dev, struct ifreq *ifr);
+
static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
{
return __constant_htons(ETH_P_IP);
@@ -31,7 +33,14 @@ static __be16 raw_type_trans(struct sk_buff *skb, struct net_device *dev)
-int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .type_trans = raw_type_trans,
+ .ioctl = raw_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int raw_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@@ -41,12 +50,14 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_HDLC;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+ if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@@ -71,12 +82,11 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.type_trans = raw_type_trans;
- hdlc->proto.id = IF_PROTO_HDLC;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(raw_hdlc_proto));
+ if (result)
+ return result;
+ memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = hdlc->xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_RAWHDLC;
@@ -88,3 +98,25 @@ int hdlc_raw_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Raw HDLC protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_raw_eth.c b/drivers/net/wan/hdlc_raw_eth.c
index d1884987f94..1a69a9aaa9b 100644
--- a/drivers/net/wan/hdlc_raw_eth.c
+++ b/drivers/net/wan/hdlc_raw_eth.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* HDLC Ethernet emulation support
*
- * Copyright (C) 2002-2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 2002-2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -25,6 +25,7 @@
#include <linux/etherdevice.h>
#include <linux/hdlc.h>
+static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr);
static int eth_tx(struct sk_buff *skb, struct net_device *dev)
{
@@ -44,7 +45,14 @@ static int eth_tx(struct sk_buff *skb, struct net_device *dev)
}
-int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
+static struct hdlc_proto proto = {
+ .type_trans = eth_type_trans,
+ .ioctl = raw_eth_ioctl,
+ .module = THIS_MODULE,
+};
+
+
+static int raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
{
raw_hdlc_proto __user *raw_s = ifr->ifr_settings.ifs_ifsu.raw_hdlc;
const size_t size = sizeof(raw_hdlc_proto);
@@ -56,12 +64,14 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_HDLC_ETH;
if (ifr->ifr_settings.size < size) {
ifr->ifr_settings.size = size; /* data size wanted */
return -ENOBUFS;
}
- if (copy_to_user(raw_s, &hdlc->state.raw_hdlc.settings, size))
+ if (copy_to_user(raw_s, hdlc->state, size))
return -EFAULT;
return 0;
@@ -86,12 +96,11 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memcpy(&hdlc->state.raw_hdlc.settings, &new_settings, size);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.type_trans = eth_type_trans;
- hdlc->proto.id = IF_PROTO_HDLC_ETH;
+ result = attach_hdlc_protocol(dev, &proto, NULL,
+ sizeof(raw_hdlc_proto));
+ if (result)
+ return result;
+ memcpy(hdlc->state, &new_settings, size);
dev->hard_start_xmit = eth_tx;
old_ch_mtu = dev->change_mtu;
old_qlen = dev->tx_queue_len;
@@ -106,3 +115,25 @@ int hdlc_raw_eth_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("Ethernet encapsulation support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/hdlc_x25.c b/drivers/net/wan/hdlc_x25.c
index a867fb411f8..e4bb9f8ad43 100644
--- a/drivers/net/wan/hdlc_x25.c
+++ b/drivers/net/wan/hdlc_x25.c
@@ -2,7 +2,7 @@
* Generic HDLC support routines for Linux
* X.25 support
*
- * Copyright (C) 1999 - 2003 Krzysztof Halasa <khc@pm.waw.pl>
+ * Copyright (C) 1999 - 2006 Krzysztof Halasa <khc@pm.waw.pl>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License
@@ -25,6 +25,8 @@
#include <net/x25device.h>
+static int x25_ioctl(struct net_device *dev, struct ifreq *ifr);
+
/* These functions are callbacks called by LAPB layer */
static void x25_connect_disconnect(struct net_device *dev, int reason, int code)
@@ -162,30 +164,39 @@ static void x25_close(struct net_device *dev)
static int x25_rx(struct sk_buff *skb)
{
- hdlc_device *hdlc = dev_to_hdlc(skb->dev);
+ struct hdlc_device_desc *desc = dev_to_desc(skb->dev);
if ((skb = skb_share_check(skb, GFP_ATOMIC)) == NULL) {
- hdlc->stats.rx_dropped++;
+ desc->stats.rx_dropped++;
return NET_RX_DROP;
}
if (lapb_data_received(skb->dev, skb) == LAPB_OK)
return NET_RX_SUCCESS;
- hdlc->stats.rx_errors++;
+ desc->stats.rx_errors++;
dev_kfree_skb_any(skb);
return NET_RX_DROP;
}
+static struct hdlc_proto proto = {
+ .open = x25_open,
+ .close = x25_close,
+ .ioctl = x25_ioctl,
+ .module = THIS_MODULE,
+};
+
-int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
+static int x25_ioctl(struct net_device *dev, struct ifreq *ifr)
{
hdlc_device *hdlc = dev_to_hdlc(dev);
int result;
switch (ifr->ifr_settings.type) {
case IF_GET_PROTO:
+ if (dev_to_hdlc(dev)->proto != &proto)
+ return -EINVAL;
ifr->ifr_settings.type = IF_PROTO_X25;
return 0; /* return protocol only, no settable parameters */
@@ -200,14 +211,9 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
if (result)
return result;
- hdlc_proto_detach(hdlc);
- memset(&hdlc->proto, 0, sizeof(hdlc->proto));
-
- hdlc->proto.open = x25_open;
- hdlc->proto.close = x25_close;
- hdlc->proto.netif_rx = x25_rx;
- hdlc->proto.type_trans = NULL;
- hdlc->proto.id = IF_PROTO_X25;
+ if ((result = attach_hdlc_protocol(dev, &proto,
+ x25_rx, 0)) != 0)
+ return result;
dev->hard_start_xmit = x25_xmit;
dev->hard_header = NULL;
dev->type = ARPHRD_X25;
@@ -218,3 +224,25 @@ int hdlc_x25_ioctl(struct net_device *dev, struct ifreq *ifr)
return -EINVAL;
}
+
+
+static int __init mod_init(void)
+{
+ register_hdlc_protocol(&proto);
+ return 0;
+}
+
+
+
+static void __exit mod_exit(void)
+{
+ unregister_hdlc_protocol(&proto);
+}
+
+
+module_init(mod_init);
+module_exit(mod_exit);
+
+MODULE_AUTHOR("Krzysztof Halasa <khc@pm.waw.pl>");
+MODULE_DESCRIPTION("X.25 protocol support for generic HDLC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/net/wan/pc300.h b/drivers/net/wan/pc300.h
index 2024b26b99e..63e9fcf31fb 100644
--- a/drivers/net/wan/pc300.h
+++ b/drivers/net/wan/pc300.h
@@ -100,6 +100,7 @@
#define _PC300_H
#include <linux/hdlc.h>
+#include <net/syncppp.h>
#include "hd64572.h"
#include "pc300-falc-lh.h"
diff --git a/drivers/net/wan/pc300_drv.c b/drivers/net/wan/pc300_drv.c
index 56e69403d17..8d9b959bf15 100644
--- a/drivers/net/wan/pc300_drv.c
+++ b/drivers/net/wan/pc300_drv.c
@@ -2016,7 +2016,6 @@ static void sca_intr(pc300_t * card)
pc300ch_t *chan = &card->chan[ch];
pc300dev_t *d = &chan->d;
struct net_device *dev = d->dev;
- hdlc_device *hdlc = dev_to_hdlc(dev);
spin_lock(&card->card_lock);
@@ -2049,8 +2048,8 @@ static void sca_intr(pc300_t * card)
}
cpc_net_rx(dev);
/* Discard invalid frames */
- hdlc->stats.rx_errors++;
- hdlc->stats.rx_over_errors++;
+ hdlc_stats(dev)->rx_errors++;
+ hdlc_stats(dev)->rx_over_errors++;
chan->rx_first_bd = 0;
chan->rx_last_bd = N_DMA_RX_BUF - 1;
rx_dma_start(card, ch);
@@ -2116,8 +2115,8 @@ static void sca_intr(pc300_t * card)
card->hw.cpld_reg2) &
~ (CPLD_REG2_FALC_LED1 << (2 * ch)));
}
- hdlc->stats.tx_errors++;
- hdlc->stats.tx_fifo_errors++;
+ hdlc_stats(dev)->tx_errors++;
+ hdlc_stats(dev)->tx_fifo_errors++;
sca_tx_intr(d);
}
}
@@ -2534,7 +2533,6 @@ static int cpc_change_mtu(struct net_device *dev, int new_mtu)
static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
@@ -2552,10 +2550,10 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGPC300CONF:
#ifdef CONFIG_PC300_MLPPP
if (conf->proto != PC300_PROTO_MLPPP) {
- conf->proto = hdlc->proto.id;
+ conf->proto = /* FIXME hdlc->proto.id */ 0;
}
#else
- conf->proto = hdlc->proto.id;
+ conf->proto = /* FIXME hdlc->proto.id */ 0;
#endif
memcpy(&conf_aux.conf, conf, sizeof(pc300chconf_t));
memcpy(&conf_aux.hw, &card->hw, sizeof(pc300hw_t));
@@ -2588,12 +2586,12 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
}
} else {
memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
- hdlc->proto.id = conf->proto;
+ /* FIXME hdlc->proto.id = conf->proto; */
}
}
#else
memcpy(conf, &conf_aux.conf, sizeof(pc300chconf_t));
- hdlc->proto.id = conf->proto;
+ /* FIXME hdlc->proto.id = conf->proto; */
#endif
return 0;
case SIOCGPC300STATUS:
@@ -2606,7 +2604,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
case SIOCGPC300UTILSTATS:
{
if (!arg) { /* clear statistics */
- memset(&hdlc->stats, 0, sizeof(struct net_device_stats));
+ memset(hdlc_stats(dev), 0, sizeof(struct net_device_stats));
if (card->hw.type == PC300_TE) {
memset(&chan->falc, 0, sizeof(falc_t));
}
@@ -2617,7 +2615,7 @@ static int cpc_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
pc300stats.hw_type = card->hw.type;
pc300stats.line_on = card->chan[ch].d.line_on;
pc300stats.line_off = card->chan[ch].d.line_off;
- memcpy(&pc300stats.gen_stats, &hdlc->stats,
+ memcpy(&pc300stats.gen_stats, hdlc_stats(dev),
sizeof(struct net_device_stats));
if (card->hw.type == PC300_TE)
memcpy(&pc300stats.te_stats,&chan->falc,sizeof(falc_t));
@@ -3147,7 +3145,6 @@ static void cpc_closech(pc300dev_t * d)
int cpc_open(struct net_device *dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
struct ifreq ifr;
int result;
@@ -3156,12 +3153,14 @@ int cpc_open(struct net_device *dev)
printk("pc300: cpc_open");
#endif
+#ifdef FIXME
if (hdlc->proto.id == IF_PROTO_PPP) {
d->if_ptr = &hdlc->state.ppp.pppdev;
}
+#endif
result = hdlc_open(dev);
- if (hdlc->proto.id == IF_PROTO_PPP) {
+ if (/* FIXME hdlc->proto.id == IF_PROTO_PPP*/ 0) {
dev->priv = d;
}
if (result) {
@@ -3176,7 +3175,6 @@ int cpc_open(struct net_device *dev)
static int cpc_close(struct net_device *dev)
{
- hdlc_device *hdlc = dev_to_hdlc(dev);
pc300dev_t *d = (pc300dev_t *) dev->priv;
pc300ch_t *chan = (pc300ch_t *) d->chan;
pc300_t *card = (pc300_t *) chan->card;
@@ -3193,7 +3191,7 @@ static int cpc_close(struct net_device *dev)
CPC_UNLOCK(card, flags);
hdlc_close(dev);
- if (hdlc->proto.id == IF_PROTO_PPP) {
+ if (/* FIXME hdlc->proto.id == IF_PROTO_PPP*/ 0) {
d->if_ptr = NULL;
}
#ifdef CONFIG_PC300_MLPPP
diff --git a/drivers/net/wan/syncppp.c b/drivers/net/wan/syncppp.c
index c13b459a013..218f7b574ab 100644
--- a/drivers/net/wan/syncppp.c
+++ b/drivers/net/wan/syncppp.c
@@ -469,7 +469,7 @@ static void sppp_lcp_input (struct sppp *sp, struct sk_buff *skb)
struct net_device *dev = sp->pp_if;
int len = skb->len;
u8 *p, opt[6];
- u32 rmagic;
+ u32 rmagic = 0;
if (!pskb_may_pull(skb, sizeof(struct lcp_header))) {
if (sp->pp_flags & PP_DEBUG)
@@ -763,7 +763,7 @@ static void sppp_cisco_input (struct sppp *sp, struct sk_buff *skb)
{
struct in_device *in_dev;
struct in_ifaddr *ifa;
- u32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
+ __be32 addr = 0, mask = ~0; /* FIXME: is the mask correct? */
#ifdef CONFIG_INET
rcu_read_lock();
if ((in_dev = __in_dev_get_rcu(dev)) != NULL)
diff --git a/drivers/net/wireless/airo.c b/drivers/net/wireless/airo.c
index bff04cba3fe..39d09345027 100644
--- a/drivers/net/wireless/airo.c
+++ b/drivers/net/wireless/airo.c
@@ -5659,25 +5659,40 @@ static int airo_pci_resume(struct pci_dev *pdev)
static int __init airo_init_module( void )
{
- int i, have_isa_dev = 0;
+ int i;
+#if 0
+ int have_isa_dev = 0;
+#endif
airo_entry = create_proc_entry("aironet",
S_IFDIR | airo_perm,
proc_root_driver);
- airo_entry->uid = proc_uid;
- airo_entry->gid = proc_gid;
+
+ if (airo_entry) {
+ airo_entry->uid = proc_uid;
+ airo_entry->gid = proc_gid;
+ }
for( i = 0; i < 4 && io[i] && irq[i]; i++ ) {
airo_print_info("", "Trying to configure ISA adapter at irq=%d "
"io=0x%x", irq[i], io[i] );
if (init_airo_card( irq[i], io[i], 0, NULL ))
+#if 0
have_isa_dev = 1;
+#else
+ /* do nothing */ ;
+#endif
}
#ifdef CONFIG_PCI
airo_print_info("", "Probing for PCI adapters");
- pci_register_driver(&airo_driver);
+ i = pci_register_driver(&airo_driver);
airo_print_info("", "Finished probing for PCI adapters");
+
+ if (i) {
+ remove_proc_entry("aironet", proc_root_driver);
+ return i;
+ }
#endif
/* Always exit with success, as we are a library module
@@ -5868,7 +5883,7 @@ static int airo_set_essid(struct net_device *dev,
int index = (dwrq->flags & IW_ENCODE_INDEX) - 1;
/* Check the size of the string */
- if(dwrq->length > IW_ESSID_MAX_SIZE+1) {
+ if(dwrq->length > IW_ESSID_MAX_SIZE) {
return -E2BIG ;
}
/* Check if index is valid */
@@ -5880,7 +5895,7 @@ static int airo_set_essid(struct net_device *dev,
memset(SSID_rid.ssids[index].ssid, 0,
sizeof(SSID_rid.ssids[index].ssid));
memcpy(SSID_rid.ssids[index].ssid, extra, dwrq->length);
- SSID_rid.ssids[index].len = dwrq->length - 1;
+ SSID_rid.ssids[index].len = dwrq->length;
}
SSID_rid.len = sizeof(SSID_rid);
/* Write it to the card */
@@ -5990,7 +6005,7 @@ static int airo_set_nick(struct net_device *dev,
struct airo_info *local = dev->priv;
/* Check the size of the string */
- if(dwrq->length > 16 + 1) {
+ if(dwrq->length > 16) {
return -E2BIG;
}
readConfigRid(local, 1);
@@ -6015,7 +6030,7 @@ static int airo_get_nick(struct net_device *dev,
readConfigRid(local, 1);
strncpy(extra, local->config.nodeName, 16);
extra[16] = '\0';
- dwrq->length = strlen(extra) + 1;
+ dwrq->length = strlen(extra);
return 0;
}
@@ -6767,9 +6782,9 @@ static int airo_set_retry(struct net_device *dev,
}
readConfigRid(local, 1);
if(vwrq->flags & IW_RETRY_LIMIT) {
- if(vwrq->flags & IW_RETRY_MAX)
+ if(vwrq->flags & IW_RETRY_LONG)
local->config.longRetryLimit = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MIN)
+ else if (vwrq->flags & IW_RETRY_SHORT)
local->config.shortRetryLimit = vwrq->value;
else {
/* No modifier : set both */
@@ -6805,14 +6820,14 @@ static int airo_get_retry(struct net_device *dev,
if((vwrq->flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME) {
vwrq->flags = IW_RETRY_LIFETIME;
vwrq->value = (int)local->config.txLifetime * 1024;
- } else if((vwrq->flags & IW_RETRY_MAX)) {
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ } else if((vwrq->flags & IW_RETRY_LONG)) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
vwrq->value = (int)local->config.longRetryLimit;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = (int)local->config.shortRetryLimit;
if((int)local->config.shortRetryLimit != (int)local->config.longRetryLimit)
- vwrq->flags |= IW_RETRY_MIN;
+ vwrq->flags |= IW_RETRY_SHORT;
}
return 0;
@@ -6990,6 +7005,7 @@ static int airo_set_power(struct net_device *dev,
local->config.rmode |= RXMODE_BC_MC_ADDR;
set_bit (FLAG_COMMIT, &local->flags);
case IW_POWER_ON:
+ /* This is broken, fixme ;-) */
break;
default:
return -EINVAL;
diff --git a/drivers/net/wireless/atmel.c b/drivers/net/wireless/atmel.c
index 995c7bea589..0fc267d626d 100644
--- a/drivers/net/wireless/atmel.c
+++ b/drivers/net/wireless/atmel.c
@@ -1656,13 +1656,13 @@ static int atmel_set_essid(struct net_device *dev,
priv->connect_to_any_BSS = 0;
/* Check the size of the string */
- if (dwrq->length > MAX_SSID_LENGTH + 1)
+ if (dwrq->length > MAX_SSID_LENGTH)
return -E2BIG;
if (index != 0)
return -EINVAL;
- memcpy(priv->new_SSID, extra, dwrq->length - 1);
- priv->new_SSID_size = dwrq->length - 1;
+ memcpy(priv->new_SSID, extra, dwrq->length);
+ priv->new_SSID_size = dwrq->length;
}
return -EINPROGRESS;
@@ -2120,9 +2120,9 @@ static int atmel_set_retry(struct net_device *dev,
struct atmel_private *priv = netdev_priv(dev);
if (!vwrq->disabled && (vwrq->flags & IW_RETRY_LIMIT)) {
- if (vwrq->flags & IW_RETRY_MAX)
+ if (vwrq->flags & IW_RETRY_LONG)
priv->long_retry = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MIN)
+ else if (vwrq->flags & IW_RETRY_SHORT)
priv->short_retry = vwrq->value;
else {
/* No modifier : set both */
@@ -2144,15 +2144,15 @@ static int atmel_get_retry(struct net_device *dev,
vwrq->disabled = 0; /* Can't be disabled */
- /* Note : by default, display the min retry number */
- if (vwrq->flags & IW_RETRY_MAX) {
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ /* Note : by default, display the short retry number */
+ if (vwrq->flags & IW_RETRY_LONG) {
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
vwrq->value = priv->long_retry;
} else {
vwrq->flags = IW_RETRY_LIMIT;
vwrq->value = priv->short_retry;
if (priv->long_retry != priv->short_retry)
- vwrq->flags |= IW_RETRY_MIN;
+ vwrq->flags |= IW_RETRY_SHORT;
}
return 0;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx.h b/drivers/net/wireless/bcm43xx/bcm43xx.h
index 6d4ea36bc56..d6a8bf09878 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx.h
+++ b/drivers/net/wireless/bcm43xx/bcm43xx.h
@@ -666,7 +666,6 @@ struct bcm43xx_noise_calculation {
};
struct bcm43xx_stats {
- u8 link_quality;
u8 noise;
struct iw_statistics wstats;
/* Store the last TX/RX times here for updating the leds. */
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
index 923275ea078..b9df06a06ea 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_debugfs.c
@@ -54,7 +54,7 @@ static ssize_t write_file_dummy(struct file *file, const char __user *buf,
static int open_file_generic(struct inode *inode, struct file *file)
{
- file->private_data = inode->u.generic_ip;
+ file->private_data = inode->i_private;
return 0;
}
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_main.c b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
index cb9a3ae8463..eb65db7393b 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_main.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_main.c
@@ -2405,9 +2405,10 @@ static int bcm43xx_chip_init(struct bcm43xx_private *bcm)
BCM43xx_UCODE_TIME) & 0x1f);
if ( value16 > 0x128 ) {
- dprintk(KERN_ERR PFX
- "Firmware: no support for microcode rev > 0x128\n");
- err = -1;
+ printk(KERN_ERR PFX
+ "Firmware: no support for microcode extracted "
+ "from version 4.x binary drivers.\n");
+ err = -EOPNOTSUPP;
goto err_release_fw;
}
@@ -3169,8 +3170,7 @@ static void bcm43xx_periodic_work_handler(void *d)
* be preemtible.
*/
mutex_lock(&bcm->mutex);
- netif_stop_queue(bcm->net_dev);
- synchronize_net();
+ netif_tx_disable(bcm->net_dev);
spin_lock_irqsave(&bcm->irq_lock, flags);
bcm43xx_mac_suspend(bcm);
if (bcm43xx_using_pio(bcm))
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
index eafd0f66268..52ce2a9334f 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_phy.c
@@ -361,7 +361,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
if (phy->rev <= 2)
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg1[i]);
- else if ((phy->rev == 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
+ else if ((phy->rev >= 7) && (bcm43xx_phy_read(bcm, 0x0449) & 0x0200))
for (i = 0; i < BCM43xx_ILT_NOISESCALEG_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x1400 + i, bcm43xx_ilt_noisescaleg3[i]);
else
@@ -371,7 +371,7 @@ static void bcm43xx_phy_setupg(struct bcm43xx_private *bcm)
if (phy->rev == 2)
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr1[i]);
- else if ((phy->rev > 2) && (phy->rev <= 7))
+ else if ((phy->rev > 2) && (phy->rev <= 8))
for (i = 0; i < BCM43xx_ILT_SIGMASQR_SIZE; i++)
bcm43xx_ilt_write(bcm, 0x5000 + i, bcm43xx_ilt_sigmasqr2[i]);
@@ -1197,7 +1197,7 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
if (phy->rev == 1)
bcm43xx_phy_initb5(bcm);
- else if (phy->rev >= 2 && phy->rev <= 7)
+ else
bcm43xx_phy_initb6(bcm);
if (phy->rev >= 2 || phy->connected)
bcm43xx_phy_inita(bcm);
@@ -1241,23 +1241,22 @@ static void bcm43xx_phy_initg(struct bcm43xx_private *bcm)
bcm43xx_phy_lo_g_measure(bcm);
} else {
if (radio->version == 0x2050 && radio->revision == 8) {
- //FIXME
+ bcm43xx_radio_write16(bcm, 0x0052,
+ (radio->txctl1 << 4) | radio->txctl2);
} else {
bcm43xx_radio_write16(bcm, 0x0052,
(bcm43xx_radio_read16(bcm, 0x0052)
& 0xFFF0) | radio->txctl1);
}
if (phy->rev >= 6) {
- /*
bcm43xx_phy_write(bcm, 0x0036,
(bcm43xx_phy_read(bcm, 0x0036)
- & 0xF000) | (FIXME << 12));
- */
+ & 0xF000) | (radio->txctl2 << 12));
}
if (bcm->sprom.boardflags & BCM43xx_BFL_PACTRL)
bcm43xx_phy_write(bcm, 0x002E, 0x8075);
else
- bcm43xx_phy_write(bcm, 0x003E, 0x807F);
+ bcm43xx_phy_write(bcm, 0x002E, 0x807F);
if (phy->rev < 2)
bcm43xx_phy_write(bcm, 0x002F, 0x0101);
else
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
index 888077fc14c..9b7b15cf656 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_wx.c
@@ -334,7 +334,7 @@ static int bcm43xx_wx_get_nick(struct net_device *net_dev,
size_t len;
mutex_lock(&bcm->mutex);
- len = strlen(bcm->nick) + 1;
+ len = strlen(bcm->nick);
memcpy(extra, bcm->nick, len);
data->data.length = (__u16)len;
data->data.flags = 1;
diff --git a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
index c0efbfe605a..0159e4e9320 100644
--- a/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
+++ b/drivers/net/wireless/bcm43xx/bcm43xx_xmit.c
@@ -496,15 +496,14 @@ int bcm43xx_rx(struct bcm43xx_private *bcm,
stats.signal = bcm43xx_rssi_postprocess(bcm, rxhdr->rssi, is_ofdm,
!!(rxflags1 & BCM43xx_RXHDR_FLAGS1_2053RSSIADJ),
!!(rxflags3 & BCM43xx_RXHDR_FLAGS3_2050RSSIADJ));
-//TODO stats.noise =
+ stats.noise = bcm->stats.noise;
if (is_ofdm)
stats.rate = bcm43xx_plcp_get_bitrate_ofdm(plcp);
else
stats.rate = bcm43xx_plcp_get_bitrate_cck(plcp);
stats.received_channel = radio->channel;
-//TODO stats.control =
stats.mask = IEEE80211_STATMASK_SIGNAL |
-//TODO IEEE80211_STATMASK_NOISE |
+ IEEE80211_STATMASK_NOISE |
IEEE80211_STATMASK_RATE |
IEEE80211_STATMASK_RSSI;
if (phy->type == BCM43xx_PHYTYPE_A)
diff --git a/drivers/net/wireless/hostap/hostap_ioctl.c b/drivers/net/wireless/hostap/hostap_ioctl.c
index 7a4978516ea..d061fb3443f 100644
--- a/drivers/net/wireless/hostap/hostap_ioctl.c
+++ b/drivers/net/wireless/hostap/hostap_ioctl.c
@@ -1412,9 +1412,9 @@ static int prism2_ioctl_siwretry(struct net_device *dev,
/* what could be done, if firmware would support this.. */
if (rrq->flags & IW_RETRY_LIMIT) {
- if (rrq->flags & IW_RETRY_MAX)
+ if (rrq->flags & IW_RETRY_LONG)
HFA384X_RID_LONGRETRYLIMIT = rrq->value;
- else if (rrq->flags & IW_RETRY_MIN)
+ else if (rrq->flags & IW_RETRY_SHORT)
HFA384X_RID_SHORTRETRYLIMIT = rrq->value;
else {
HFA384X_RID_LONGRETRYLIMIT = rrq->value;
@@ -1468,14 +1468,14 @@ static int prism2_ioctl_giwretry(struct net_device *dev,
rrq->value = le16_to_cpu(altretry);
else
rrq->value = local->manual_retry_count;
- } else if ((rrq->flags & IW_RETRY_MAX)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ } else if ((rrq->flags & IW_RETRY_LONG)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
rrq->value = longretry;
} else {
rrq->flags = IW_RETRY_LIMIT;
rrq->value = shortretry;
if (shortretry != longretry)
- rrq->flags |= IW_RETRY_MIN;
+ rrq->flags |= IW_RETRY_SHORT;
}
}
return 0;
diff --git a/drivers/net/wireless/ipw2100.c b/drivers/net/wireless/ipw2100.c
index b4d81a04c89..599e2fe7618 100644
--- a/drivers/net/wireless/ipw2100.c
+++ b/drivers/net/wireless/ipw2100.c
@@ -150,7 +150,6 @@ that only one external action is invoked at a time.
#include <linux/skbuff.h>
#include <asm/uaccess.h>
#include <asm/io.h>
-#define __KERNEL_SYSCALLS__
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/slab.h>
@@ -163,6 +162,7 @@ that only one external action is invoked at a time.
#include <linux/firmware.h>
#include <linux/acpi.h>
#include <linux/ctype.h>
+#include <linux/latency.h>
#include "ipw2100.h"
@@ -1697,6 +1697,11 @@ static int ipw2100_up(struct ipw2100_priv *priv, int deferred)
return 0;
}
+ /* the ipw2100 hardware really doesn't want power management delays
+ * longer than 175usec
+ */
+ modify_acceptable_latency("ipw2100", 175);
+
/* If the interrupt is enabled, turn it off... */
spin_lock_irqsave(&priv->low_lock, flags);
ipw2100_disable_interrupts(priv);
@@ -1849,6 +1854,8 @@ static void ipw2100_down(struct ipw2100_priv *priv)
ipw2100_disable_interrupts(priv);
spin_unlock_irqrestore(&priv->low_lock, flags);
+ modify_acceptable_latency("ipw2100", INFINITE_LATENCY);
+
#ifdef ACPI_CSTATE_LIMIT_DEFINED
if (priv->config & CFG_C3_DISABLED) {
IPW_DEBUG_INFO(": Resetting C3 transitions.\n");
@@ -6267,7 +6274,9 @@ static int ipw2100_pci_init_one(struct pci_dev *pci_dev,
IPW_DEBUG_INFO("%s: Bound to %s\n", dev->name, pci_name(pci_dev));
/* perform this after register_netdev so that dev->name is set */
- sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+ err = sysfs_create_group(&pci_dev->dev.kobj, &ipw2100_attribute_group);
+ if (err)
+ goto fail_unlock;
/* If the RF Kill switch is disabled, go ahead and complete the
* startup sequence */
@@ -6533,13 +6542,17 @@ static int __init ipw2100_init(void)
printk(KERN_INFO DRV_NAME ": %s\n", DRV_COPYRIGHT);
ret = pci_register_driver(&ipw2100_pci_driver);
+ if (ret)
+ goto out;
+ set_acceptable_latency("ipw2100", INFINITE_LATENCY);
#ifdef CONFIG_IPW2100_DEBUG
ipw2100_debug_level = debug;
- driver_create_file(&ipw2100_pci_driver.driver,
- &driver_attr_debug_level);
+ ret = driver_create_file(&ipw2100_pci_driver.driver,
+ &driver_attr_debug_level);
#endif
+out:
return ret;
}
@@ -6554,6 +6567,7 @@ static void __exit ipw2100_exit(void)
&driver_attr_debug_level);
#endif
pci_unregister_driver(&ipw2100_pci_driver);
+ remove_acceptable_latency("ipw2100");
}
module_init(ipw2100_init);
@@ -6958,7 +6972,7 @@ static int ipw2100_wx_set_essid(struct net_device *dev,
}
if (wrqu->essid.flags && wrqu->essid.length) {
- length = wrqu->essid.length - 1;
+ length = wrqu->essid.length;
essid = extra;
}
@@ -7051,7 +7065,7 @@ static int ipw2100_wx_get_nick(struct net_device *dev,
struct ipw2100_priv *priv = ieee80211_priv(dev);
- wrqu->data.length = strlen(priv->nick) + 1;
+ wrqu->data.length = strlen(priv->nick);
memcpy(extra, priv->nick, wrqu->data.length);
wrqu->data.flags = 1; /* active */
@@ -7343,14 +7357,14 @@ static int ipw2100_wx_set_retry(struct net_device *dev,
goto done;
}
- if (wrqu->retry.flags & IW_RETRY_MIN) {
+ if (wrqu->retry.flags & IW_RETRY_SHORT) {
err = ipw2100_set_short_retry(priv, wrqu->retry.value);
IPW_DEBUG_WX("SET Short Retry Limit -> %d \n",
wrqu->retry.value);
goto done;
}
- if (wrqu->retry.flags & IW_RETRY_MAX) {
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
err = ipw2100_set_long_retry(priv, wrqu->retry.value);
IPW_DEBUG_WX("SET Long Retry Limit -> %d \n",
wrqu->retry.value);
@@ -7383,14 +7397,14 @@ static int ipw2100_wx_get_retry(struct net_device *dev,
if ((wrqu->retry.flags & IW_RETRY_TYPE) == IW_RETRY_LIFETIME)
return -EINVAL;
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
wrqu->retry.value = priv->long_retry_limit;
} else {
wrqu->retry.flags =
(priv->short_retry_limit !=
priv->long_retry_limit) ?
- IW_RETRY_LIMIT | IW_RETRY_MIN : IW_RETRY_LIMIT;
+ IW_RETRY_LIMIT | IW_RETRY_SHORT : IW_RETRY_LIMIT;
wrqu->retry.value = priv->short_retry_limit;
}
diff --git a/drivers/net/wireless/ipw2200.c b/drivers/net/wireless/ipw2200.c
index 7358664e090..5685d7ba55b 100644
--- a/drivers/net/wireless/ipw2200.c
+++ b/drivers/net/wireless/ipw2200.c
@@ -8875,8 +8875,6 @@ static int ipw_wx_set_essid(struct net_device *dev,
}
length = min((int)wrqu->essid.length, IW_ESSID_MAX_SIZE);
- if (!extra[length - 1])
- length--;
priv->config |= CFG_STATIC_ESSID;
@@ -8953,7 +8951,7 @@ static int ipw_wx_get_nick(struct net_device *dev,
struct ipw_priv *priv = ieee80211_priv(dev);
IPW_DEBUG_WX("Getting nick\n");
mutex_lock(&priv->mutex);
- wrqu->data.length = strlen(priv->nick) + 1;
+ wrqu->data.length = strlen(priv->nick);
memcpy(extra, priv->nick, wrqu->data.length);
wrqu->data.flags = 1; /* active */
mutex_unlock(&priv->mutex);
@@ -9276,9 +9274,9 @@ static int ipw_wx_set_retry(struct net_device *dev,
return -EINVAL;
mutex_lock(&priv->mutex);
- if (wrqu->retry.flags & IW_RETRY_MIN)
+ if (wrqu->retry.flags & IW_RETRY_SHORT)
priv->short_retry_limit = (u8) wrqu->retry.value;
- else if (wrqu->retry.flags & IW_RETRY_MAX)
+ else if (wrqu->retry.flags & IW_RETRY_LONG)
priv->long_retry_limit = (u8) wrqu->retry.value;
else {
priv->short_retry_limit = (u8) wrqu->retry.value;
@@ -9307,11 +9305,11 @@ static int ipw_wx_get_retry(struct net_device *dev,
return -EINVAL;
}
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
wrqu->retry.value = priv->long_retry_limit;
- } else if (wrqu->retry.flags & IW_RETRY_MIN) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ } else if (wrqu->retry.flags & IW_RETRY_SHORT) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
wrqu->retry.value = priv->short_retry_limit;
} else {
wrqu->retry.flags = IW_RETRY_LIMIT;
diff --git a/drivers/net/wireless/orinoco.c b/drivers/net/wireless/orinoco.c
index 1840b69e3cb..9e19a963feb 100644
--- a/drivers/net/wireless/orinoco.c
+++ b/drivers/net/wireless/orinoco.c
@@ -3037,7 +3037,7 @@ static int orinoco_ioctl_getessid(struct net_device *dev,
}
erq->flags = 1;
- erq->length = strlen(essidbuf) + 1;
+ erq->length = strlen(essidbuf);
return 0;
}
@@ -3078,7 +3078,7 @@ static int orinoco_ioctl_getnick(struct net_device *dev,
memcpy(nickbuf, priv->nick, IW_ESSID_MAX_SIZE+1);
orinoco_unlock(priv, &flags);
- nrq->length = strlen(nickbuf)+1;
+ nrq->length = strlen(nickbuf);
return 0;
}
@@ -3575,14 +3575,14 @@ static int orinoco_ioctl_getretry(struct net_device *dev,
rrq->value = lifetime * 1000; /* ??? */
} else {
/* By default, display the min number */
- if ((rrq->flags & IW_RETRY_MAX)) {
- rrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if ((rrq->flags & IW_RETRY_LONG)) {
+ rrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
rrq->value = long_limit;
} else {
rrq->flags = IW_RETRY_LIMIT;
rrq->value = short_limit;
if(short_limit != long_limit)
- rrq->flags |= IW_RETRY_MIN;
+ rrq->flags |= IW_RETRY_SHORT;
}
}
diff --git a/drivers/net/wireless/prism54/isl_ioctl.c b/drivers/net/wireless/prism54/isl_ioctl.c
index c09fbf733b3..286325ca329 100644
--- a/drivers/net/wireless/prism54/isl_ioctl.c
+++ b/drivers/net/wireless/prism54/isl_ioctl.c
@@ -742,9 +742,9 @@ prism54_set_essid(struct net_device *ndev, struct iw_request_info *info,
/* Check if we were asked for `any' */
if (dwrq->flags && dwrq->length) {
- if (dwrq->length > min(33, IW_ESSID_MAX_SIZE + 1))
+ if (dwrq->length > 32)
return -E2BIG;
- essid.length = dwrq->length - 1;
+ essid.length = dwrq->length;
memcpy(essid.octets, extra, dwrq->length);
} else
essid.length = 0;
@@ -814,7 +814,7 @@ prism54_get_nick(struct net_device *ndev, struct iw_request_info *info,
dwrq->length = 0;
down_read(&priv->mib_sem);
- dwrq->length = strlen(priv->nickname) + 1;
+ dwrq->length = strlen(priv->nickname);
memcpy(extra, priv->nickname, dwrq->length);
up_read(&priv->mib_sem);
@@ -992,9 +992,9 @@ prism54_set_retry(struct net_device *ndev, struct iw_request_info *info,
return -EINVAL;
if (vwrq->flags & IW_RETRY_LIMIT) {
- if (vwrq->flags & IW_RETRY_MIN)
+ if (vwrq->flags & IW_RETRY_SHORT)
slimit = vwrq->value;
- else if (vwrq->flags & IW_RETRY_MAX)
+ else if (vwrq->flags & IW_RETRY_LONG)
llimit = vwrq->value;
else {
/* we are asked to set both */
@@ -1035,18 +1035,18 @@ prism54_get_retry(struct net_device *ndev, struct iw_request_info *info,
mgt_get_request(priv, DOT11_OID_MAXTXLIFETIME, 0, NULL, &r);
vwrq->value = r.u * 1024;
vwrq->flags = IW_RETRY_LIFETIME;
- } else if ((vwrq->flags & IW_RETRY_MAX)) {
+ } else if ((vwrq->flags & IW_RETRY_LONG)) {
/* we are asked for the long retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_LONGRETRIES, 0, NULL, &r);
vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
} else {
/* default. get the short retry limit */
rvalue |=
mgt_get_request(priv, DOT11_OID_SHORTRETRIES, 0, NULL, &r);
vwrq->value = r.u;
- vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
}
return rvalue;
diff --git a/drivers/net/wireless/ray_cs.c b/drivers/net/wireless/ray_cs.c
index 4574290f971..e82548ea609 100644
--- a/drivers/net/wireless/ray_cs.c
+++ b/drivers/net/wireless/ray_cs.c
@@ -1173,7 +1173,7 @@ static int ray_set_essid(struct net_device *dev,
return -EOPNOTSUPP;
} else {
/* Check the size of the string */
- if(dwrq->length > IW_ESSID_MAX_SIZE + 1) {
+ if(dwrq->length > IW_ESSID_MAX_SIZE) {
return -E2BIG;
}
diff --git a/drivers/net/wireless/strip.c b/drivers/net/wireless/strip.c
index ccaf28e8db0..337c692f6fd 100644
--- a/drivers/net/wireless/strip.c
+++ b/drivers/net/wireless/strip.c
@@ -1342,7 +1342,7 @@ static unsigned char *strip_make_packet(unsigned char *buffer,
* 'broadcast hub' radio (First byte of address being 0xFF means broadcast)
*/
if (haddr.c[0] == 0xFF) {
- u32 brd = 0;
+ __be32 brd = 0;
struct in_device *in_dev;
rcu_read_lock();
@@ -1406,7 +1406,7 @@ static void strip_send(struct strip *strip_info, struct sk_buff *skb)
int doreset = (long) jiffies - strip_info->watchdog_doreset >= 0;
int doprobe = (long) jiffies - strip_info->watchdog_doprobe >= 0
&& !doreset;
- u32 addr, brd;
+ __be32 addr, brd;
/*
* 1. If we have a packet, encapsulate it and put it in the buffer
diff --git a/drivers/net/wireless/wl3501_cs.c b/drivers/net/wireless/wl3501_cs.c
index e0d294c1297..e3ae5f60d5b 100644
--- a/drivers/net/wireless/wl3501_cs.c
+++ b/drivers/net/wireless/wl3501_cs.c
@@ -1802,15 +1802,15 @@ static int wl3501_get_retry(struct net_device *dev,
&retry, sizeof(retry));
if (rc)
goto out;
- if (wrqu->retry.flags & IW_RETRY_MAX) {
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MAX;
+ if (wrqu->retry.flags & IW_RETRY_LONG) {
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_LONG;
goto set_value;
}
rc = wl3501_get_mib_value(this, WL3501_MIB_ATTR_SHORT_RETRY_LIMIT,
&retry, sizeof(retry));
if (rc)
goto out;
- wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_MIN;
+ wrqu->retry.flags = IW_RETRY_LIMIT | IW_RETRY_SHORT;
set_value:
wrqu->retry.value = retry;
wrqu->retry.disabled = 0;
diff --git a/drivers/net/wireless/zd1201.c b/drivers/net/wireless/zd1201.c
index c52e9bcf8d0..80af9a9fcbb 100644
--- a/drivers/net/wireless/zd1201.c
+++ b/drivers/net/wireless/zd1201.c
@@ -119,7 +119,7 @@ static void zd1201_usbfree(struct urb *urb, struct pt_regs *regs)
switch(urb->status) {
case -EILSEQ:
case -ENODEV:
- case -ETIMEDOUT:
+ case -ETIME:
case -ENOENT:
case -EPIPE:
case -EOVERFLOW:
@@ -201,7 +201,7 @@ static void zd1201_usbrx(struct urb *urb, struct pt_regs *regs)
switch(urb->status) {
case -EILSEQ:
case -ENODEV:
- case -ETIMEDOUT:
+ case -ETIME:
case -ENOENT:
case -EPIPE:
case -EOVERFLOW:
@@ -1218,7 +1218,7 @@ static int zd1201_set_essid(struct net_device *dev,
return -EINVAL;
if (data->length < 1)
data->length = 1;
- zd->essidlen = data->length-1;
+ zd->essidlen = data->length;
memset(zd->essid, 0, IW_ESSID_MAX_SIZE+1);
memcpy(zd->essid, essid, data->length);
return zd1201_join(zd, zd->essid, zd->essidlen);
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.c b/drivers/net/wireless/zd1211rw/zd_chip.c
index 7c4e32cf0d4..aa661b2b76c 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.c
+++ b/drivers/net/wireless/zd1211rw/zd_chip.c
@@ -249,7 +249,6 @@ int zd_ioread16(struct zd_chip *chip, zd_addr_t addr, u16 *value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread16_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -260,7 +259,6 @@ int zd_ioread32(struct zd_chip *chip, zd_addr_t addr, u32 *value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread32_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -271,7 +269,6 @@ int zd_iowrite16(struct zd_chip *chip, zd_addr_t addr, u16 value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite16_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -282,7 +279,6 @@ int zd_iowrite32(struct zd_chip *chip, zd_addr_t addr, u32 value)
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite32_locked(chip, value, addr);
mutex_unlock(&chip->mutex);
@@ -294,7 +290,6 @@ int zd_ioread32v(struct zd_chip *chip, const zd_addr_t *addresses,
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_ioread32v_locked(chip, values, addresses, count);
mutex_unlock(&chip->mutex);
@@ -306,7 +301,6 @@ int zd_iowrite32a(struct zd_chip *chip, const struct zd_ioreq32 *ioreqs,
{
int r;
- ZD_ASSERT(!mutex_is_locked(&chip->mutex));
mutex_lock(&chip->mutex);
r = zd_iowrite32a_locked(chip, ioreqs, count);
mutex_unlock(&chip->mutex);
@@ -331,13 +325,22 @@ static int read_pod(struct zd_chip *chip, u8 *rf_type)
chip->patch_cr157 = (value >> 13) & 0x1;
chip->patch_6m_band_edge = (value >> 21) & 0x1;
chip->new_phy_layout = (value >> 31) & 0x1;
+ chip->link_led = ((value >> 4) & 1) ? LED1 : LED2;
+ chip->supports_tx_led = 1;
+ if (value & (1 << 24)) { /* LED scenario */
+ if (value & (1 << 29))
+ chip->supports_tx_led = 0;
+ }
dev_dbg_f(zd_chip_dev(chip),
"RF %s %#01x PA type %#01x patch CCK %d patch CR157 %d "
- "patch 6M %d new PHY %d\n",
+ "patch 6M %d new PHY %d link LED%d tx led %d\n",
zd_rf_name(*rf_type), *rf_type,
chip->pa_type, chip->patch_cck_gain,
- chip->patch_cr157, chip->patch_6m_band_edge, chip->new_phy_layout);
+ chip->patch_cr157, chip->patch_6m_band_edge,
+ chip->new_phy_layout,
+ chip->link_led == LED1 ? 1 : 2,
+ chip->supports_tx_led);
return 0;
error:
*rf_type = 0;
@@ -1181,7 +1184,7 @@ static int update_pwr_int(struct zd_chip *chip, u8 channel)
u8 value = chip->pwr_int_values[channel - 1];
dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_int %#04x\n",
channel, value);
- return zd_iowrite32_locked(chip, value, CR31);
+ return zd_iowrite16_locked(chip, value, CR31);
}
static int update_pwr_cal(struct zd_chip *chip, u8 channel)
@@ -1189,12 +1192,12 @@ static int update_pwr_cal(struct zd_chip *chip, u8 channel)
u8 value = chip->pwr_cal_values[channel-1];
dev_dbg_f(zd_chip_dev(chip), "channel %d pwr_cal %#04x\n",
channel, value);
- return zd_iowrite32_locked(chip, value, CR68);
+ return zd_iowrite16_locked(chip, value, CR68);
}
static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
{
- struct zd_ioreq32 ioreqs[3];
+ struct zd_ioreq16 ioreqs[3];
ioreqs[0].addr = CR67;
ioreqs[0].value = chip->ofdm_cal_values[OFDM_36M_INDEX][channel-1];
@@ -1206,7 +1209,7 @@ static int update_ofdm_cal(struct zd_chip *chip, u8 channel)
dev_dbg_f(zd_chip_dev(chip),
"channel %d ofdm_cal 36M %#04x 48M %#04x 54M %#04x\n",
channel, ioreqs[0].value, ioreqs[1].value, ioreqs[2].value);
- return zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ return zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
}
static int update_channel_integration_and_calibration(struct zd_chip *chip,
@@ -1218,7 +1221,7 @@ static int update_channel_integration_and_calibration(struct zd_chip *chip,
if (r)
return r;
if (chip->is_zd1211b) {
- static const struct zd_ioreq32 ioreqs[] = {
+ static const struct zd_ioreq16 ioreqs[] = {
{ CR69, 0x28 },
{},
{ CR69, 0x2a },
@@ -1230,7 +1233,7 @@ static int update_channel_integration_and_calibration(struct zd_chip *chip,
r = update_pwr_cal(chip, channel);
if (r)
return r;
- r = zd_iowrite32a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
if (r)
return r;
}
@@ -1252,7 +1255,7 @@ static int patch_cck_gain(struct zd_chip *chip)
if (r)
return r;
dev_dbg_f(zd_chip_dev(chip), "patching value %x\n", value & 0xff);
- return zd_iowrite32_locked(chip, value & 0xff, CR47);
+ return zd_iowrite16_locked(chip, value & 0xff, CR47);
}
int zd_chip_set_channel(struct zd_chip *chip, u8 channel)
@@ -1295,89 +1298,60 @@ u8 zd_chip_get_channel(struct zd_chip *chip)
return channel;
}
-static u16 led_mask(int led)
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status)
{
- switch (led) {
- case 1:
- return LED1;
- case 2:
- return LED2;
- default:
- return 0;
- }
-}
-
-static int read_led_reg(struct zd_chip *chip, u16 *status)
-{
- ZD_ASSERT(mutex_is_locked(&chip->mutex));
- return zd_ioread16_locked(chip, status, CR_LED);
-}
-
-static int write_led_reg(struct zd_chip *chip, u16 status)
-{
- ZD_ASSERT(mutex_is_locked(&chip->mutex));
- return zd_iowrite16_locked(chip, status, CR_LED);
-}
+ static const zd_addr_t a[] = {
+ FW_LINK_STATUS,
+ CR_LED,
+ };
-int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status)
-{
- int r, ret;
- u16 mask = led_mask(led);
- u16 reg;
+ int r;
+ u16 v[ARRAY_SIZE(a)];
+ struct zd_ioreq16 ioreqs[ARRAY_SIZE(a)] = {
+ [0] = { FW_LINK_STATUS },
+ [1] = { CR_LED },
+ };
+ u16 other_led;
- if (!mask)
- return -EINVAL;
mutex_lock(&chip->mutex);
- r = read_led_reg(chip, &reg);
+ r = zd_ioread16v_locked(chip, v, (const zd_addr_t *)a, ARRAY_SIZE(a));
if (r)
- return r;
+ goto out;
+
+ other_led = chip->link_led == LED1 ? LED2 : LED1;
+
switch (status) {
- case LED_STATUS:
- return (reg & mask) ? LED_ON : LED_OFF;
case LED_OFF:
- reg &= ~mask;
- ret = LED_OFF;
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~(LED1|LED2);
break;
- case LED_FLIP:
- reg ^= mask;
- ret = (reg&mask) ? LED_ON : LED_OFF;
+ case LED_SCANNING:
+ ioreqs[0].value = FW_LINK_OFF;
+ ioreqs[1].value = v[1] & ~other_led;
+ if (get_seconds() % 3 == 0) {
+ ioreqs[1].value &= ~chip->link_led;
+ } else {
+ ioreqs[1].value |= chip->link_led;
+ }
break;
- case LED_ON:
- reg |= mask;
- ret = LED_ON;
+ case LED_ASSOCIATED:
+ ioreqs[0].value = FW_LINK_TX;
+ ioreqs[1].value = v[1] & ~other_led;
+ ioreqs[1].value |= chip->link_led;
break;
default:
- return -EINVAL;
- }
- r = write_led_reg(chip, reg);
- if (r) {
- ret = r;
+ r = -EINVAL;
goto out;
}
-out:
- mutex_unlock(&chip->mutex);
- return r;
-}
-
-int zd_chip_led_flip(struct zd_chip *chip, int led,
- const unsigned int *phases_msecs, unsigned int count)
-{
- int i, r;
- enum led_status status;
- r = zd_chip_led_status(chip, led, LED_STATUS);
- if (r)
- return r;
- status = r;
- for (i = 0; i < count; i++) {
- r = zd_chip_led_status(chip, led, LED_FLIP);
- if (r < 0)
+ if (v[0] != ioreqs[0].value || v[1] != ioreqs[1].value) {
+ r = zd_iowrite16a_locked(chip, ioreqs, ARRAY_SIZE(ioreqs));
+ if (r)
goto out;
- msleep(phases_msecs[i]);
}
-
+ r = 0;
out:
- zd_chip_led_status(chip, led, status);
+ mutex_unlock(&chip->mutex);
return r;
}
@@ -1679,4 +1653,3 @@ int zd_rfwritev_cr_locked(struct zd_chip *chip,
return 0;
}
-
diff --git a/drivers/net/wireless/zd1211rw/zd_chip.h b/drivers/net/wireless/zd1211rw/zd_chip.h
index 4b125085989..ae59597ce4e 100644
--- a/drivers/net/wireless/zd1211rw/zd_chip.h
+++ b/drivers/net/wireless/zd1211rw/zd_chip.h
@@ -428,6 +428,7 @@
/* masks for controlling LEDs */
#define LED1 0x0100
#define LED2 0x0200
+#define LED_SW 0x0400
/* Seems to indicate that the configuration is over.
*/
@@ -629,6 +630,10 @@
#define FW_SOFT_RESET FW_REG(4)
#define FW_FLASH_CHK FW_REG(5)
+#define FW_LINK_OFF 0x0
+#define FW_LINK_TX 0x1
+/* 0x2 - link led on? */
+
enum {
CR_BASE_OFFSET = 0x9000,
FW_START_OFFSET = 0xee00,
@@ -663,8 +668,11 @@ struct zd_chip {
u8 pwr_int_values[E2P_CHANNEL_COUNT];
/* SetPointOFDM in the vendor driver */
u8 ofdm_cal_values[3][E2P_CHANNEL_COUNT];
- u8 pa_type:4, patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
- new_phy_layout:1, is_zd1211b:1;
+ u16 link_led;
+ unsigned int pa_type:4,
+ patch_cck_gain:1, patch_cr157:1, patch_6m_band_edge:1,
+ new_phy_layout:1,
+ is_zd1211b:1, supports_tx_led:1;
};
static inline struct zd_chip *zd_usb_to_chip(struct zd_usb *usb)
@@ -812,15 +820,12 @@ int zd_chip_lock_phy_regs(struct zd_chip *chip);
int zd_chip_unlock_phy_regs(struct zd_chip *chip);
enum led_status {
- LED_OFF = 0,
- LED_ON = 1,
- LED_FLIP = 2,
- LED_STATUS = 3,
+ LED_OFF = 0,
+ LED_SCANNING = 1,
+ LED_ASSOCIATED = 2,
};
-int zd_chip_led_status(struct zd_chip *chip, int led, enum led_status status);
-int zd_chip_led_flip(struct zd_chip *chip, int led,
- const unsigned int *phases_msecs, unsigned int count);
+int zd_chip_control_leds(struct zd_chip *chip, enum led_status status);
int zd_set_beacon_interval(struct zd_chip *chip, u32 interval);
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.c b/drivers/net/wireless/zd1211rw/zd_mac.c
index 1989f1c05fb..2d12837052b 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.c
+++ b/drivers/net/wireless/zd1211rw/zd_mac.c
@@ -33,6 +33,10 @@
static void ieee_init(struct ieee80211_device *ieee);
static void softmac_init(struct ieee80211softmac_device *sm);
+static void housekeeping_init(struct zd_mac *mac);
+static void housekeeping_enable(struct zd_mac *mac);
+static void housekeeping_disable(struct zd_mac *mac);
+
int zd_mac_init(struct zd_mac *mac,
struct net_device *netdev,
struct usb_interface *intf)
@@ -46,6 +50,7 @@ int zd_mac_init(struct zd_mac *mac,
ieee_init(ieee);
softmac_init(ieee80211_priv(netdev));
zd_chip_init(&mac->chip, netdev, intf);
+ housekeeping_init(mac);
return 0;
}
@@ -178,6 +183,7 @@ int zd_mac_open(struct net_device *netdev)
if (r < 0)
goto disable_rx;
+ housekeeping_enable(mac);
ieee80211softmac_start(netdev);
return 0;
disable_rx:
@@ -204,6 +210,7 @@ int zd_mac_stop(struct net_device *netdev)
*/
zd_chip_disable_rx(chip);
+ housekeeping_disable(mac);
ieee80211softmac_stop(netdev);
zd_chip_disable_hwint(chip);
@@ -1080,3 +1087,46 @@ void zd_dump_rx_status(const struct rx_status *status)
}
}
#endif /* DEBUG */
+
+#define LINK_LED_WORK_DELAY HZ
+
+static void link_led_handler(void *p)
+{
+ struct zd_mac *mac = p;
+ struct zd_chip *chip = &mac->chip;
+ struct ieee80211softmac_device *sm = ieee80211_priv(mac->netdev);
+ int is_associated;
+ int r;
+
+ spin_lock_irq(&mac->lock);
+ is_associated = sm->associated != 0;
+ spin_unlock_irq(&mac->lock);
+
+ r = zd_chip_control_leds(chip,
+ is_associated ? LED_ASSOCIATED : LED_SCANNING);
+ if (r)
+ dev_err(zd_mac_dev(mac), "zd_chip_control_leds error %d\n", r);
+
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ LINK_LED_WORK_DELAY);
+}
+
+static void housekeeping_init(struct zd_mac *mac)
+{
+ INIT_WORK(&mac->housekeeping.link_led_work, link_led_handler, mac);
+}
+
+static void housekeeping_enable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ queue_delayed_work(zd_workqueue, &mac->housekeeping.link_led_work,
+ 0);
+}
+
+static void housekeeping_disable(struct zd_mac *mac)
+{
+ dev_dbg_f(zd_mac_dev(mac), "\n");
+ cancel_rearming_delayed_workqueue(zd_workqueue,
+ &mac->housekeeping.link_led_work);
+ zd_chip_control_leds(&mac->chip, LED_OFF);
+}
diff --git a/drivers/net/wireless/zd1211rw/zd_mac.h b/drivers/net/wireless/zd1211rw/zd_mac.h
index 29b51fd7d4e..b8ea3de7924 100644
--- a/drivers/net/wireless/zd1211rw/zd_mac.h
+++ b/drivers/net/wireless/zd1211rw/zd_mac.h
@@ -120,6 +120,10 @@ enum mac_flags {
MAC_FIXED_CHANNEL = 0x01,
};
+struct housekeeping {
+ struct work_struct link_led_work;
+};
+
#define ZD_MAC_STATS_BUFFER_SIZE 16
struct zd_mac {
@@ -128,6 +132,7 @@ struct zd_mac {
struct net_device *netdev;
/* Unlocked reading possible */
struct iw_statistics iw_stats;
+ struct housekeeping housekeeping;
unsigned int stats_count;
u8 qual_buffer[ZD_MAC_STATS_BUFFER_SIZE];
u8 rssi_buffer[ZD_MAC_STATS_BUFFER_SIZE];
diff --git a/drivers/net/wireless/zd1211rw/zd_netdev.c b/drivers/net/wireless/zd1211rw/zd_netdev.c
index 440ef24b5fd..af3a7b36d07 100644
--- a/drivers/net/wireless/zd1211rw/zd_netdev.c
+++ b/drivers/net/wireless/zd1211rw/zd_netdev.c
@@ -82,7 +82,7 @@ static int iw_get_nick(struct net_device *netdev,
union iwreq_data *req, char *extra)
{
strcpy(extra, "zd1211");
- req->data.length = strlen(extra) + 1;
+ req->data.length = strlen(extra);
req->data.flags = 1;
return 0;
}
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.c b/drivers/net/wireless/zd1211rw/zd_usb.c
index 31027e52b04..5c265ad0485 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.c
+++ b/drivers/net/wireless/zd1211rw/zd_usb.c
@@ -24,6 +24,7 @@
#include <linux/errno.h>
#include <linux/skbuff.h>
#include <linux/usb.h>
+#include <linux/workqueue.h>
#include <net/ieee80211.h>
#include "zd_def.h"
@@ -1112,12 +1113,20 @@ static struct usb_driver driver = {
.disconnect = disconnect,
};
+struct workqueue_struct *zd_workqueue;
+
static int __init usb_init(void)
{
int r;
pr_debug("usb_init()\n");
+ zd_workqueue = create_singlethread_workqueue(driver.name);
+ if (zd_workqueue == NULL) {
+ printk(KERN_ERR "%s: couldn't create workqueue\n", driver.name);
+ return -ENOMEM;
+ }
+
r = usb_register(&driver);
if (r) {
printk(KERN_ERR "usb_register() failed. Error number %d\n", r);
@@ -1132,6 +1141,7 @@ static void __exit usb_exit(void)
{
pr_debug("usb_exit()\n");
usb_deregister(&driver);
+ destroy_workqueue(zd_workqueue);
}
module_init(usb_init);
diff --git a/drivers/net/wireless/zd1211rw/zd_usb.h b/drivers/net/wireless/zd1211rw/zd_usb.h
index ded39de5f72..e81a2d3cfff 100644
--- a/drivers/net/wireless/zd1211rw/zd_usb.h
+++ b/drivers/net/wireless/zd1211rw/zd_usb.h
@@ -238,4 +238,6 @@ int zd_usb_iowrite16v(struct zd_usb *usb, const struct zd_ioreq16 *ioreqs,
int zd_usb_rfwrite(struct zd_usb *usb, u32 value, u8 bits);
+extern struct workqueue_struct *zd_workqueue;
+
#endif /* _ZD_USB_H */
diff --git a/drivers/oprofile/oprofilefs.c b/drivers/oprofile/oprofilefs.c
index 71c2da277d6..5756401fb15 100644
--- a/drivers/oprofile/oprofilefs.c
+++ b/drivers/oprofile/oprofilefs.c
@@ -31,7 +31,6 @@ static struct inode * oprofilefs_get_inode(struct super_block * sb, int mode)
inode->i_mode = mode;
inode->i_uid = 0;
inode->i_gid = 0;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
}
@@ -110,8 +109,8 @@ static ssize_t ulong_write_file(struct file * file, char const __user * buf, siz
static int default_open(struct inode * inode, struct file * filp)
{
- if (inode->u.generic_ip)
- filp->private_data = inode->u.generic_ip;
+ if (inode->i_private)
+ filp->private_data = inode->i_private;
return 0;
}
@@ -158,7 +157,7 @@ int oprofilefs_create_ulong(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
@@ -171,7 +170,7 @@ int oprofilefs_create_ro_ulong(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
@@ -197,7 +196,7 @@ int oprofilefs_create_ro_atomic(struct super_block * sb, struct dentry * root,
if (!d)
return -EFAULT;
- d->d_inode->u.generic_ip = val;
+ d->d_inode->i_private = val;
return 0;
}
diff --git a/drivers/parisc/led.c b/drivers/parisc/led.c
index bf00fa2537b..8dac2ba82bb 100644
--- a/drivers/parisc/led.c
+++ b/drivers/parisc/led.c
@@ -684,7 +684,7 @@ int __init led_init(void)
int ret;
snprintf(lcd_text_default, sizeof(lcd_text_default),
- "Linux %s", system_utsname.release);
+ "Linux %s", init_utsname()->release);
/* Work around the buggy PDC of KittyHawk-machines */
switch (CPU_HVERSION) {
diff --git a/drivers/parisc/power.c b/drivers/parisc/power.c
index fad5a33bf0f..4a9f025a6b5 100644
--- a/drivers/parisc/power.c
+++ b/drivers/parisc/power.c
@@ -84,8 +84,7 @@
static void deferred_poweroff(void *dummy)
{
- extern int cad_pid; /* from kernel/sys.c */
- if (kill_proc(cad_pid, SIGINT, 1)) {
+ if (kill_cad_pid(SIGINT, 1)) {
/* just in case killing init process failed */
machine_power_off();
}
diff --git a/drivers/parport/parport_serial.c b/drivers/parport/parport_serial.c
index 98b83a85c60..78c0a269a2b 100644
--- a/drivers/parport/parport_serial.c
+++ b/drivers/parport/parport_serial.c
@@ -374,6 +374,7 @@ static void __devexit parport_serial_pci_remove (struct pci_dev *dev)
return;
}
+#ifdef CONFIG_PM
static int parport_serial_pci_suspend(struct pci_dev *dev, pm_message_t state)
{
struct parport_serial_private *priv = pci_get_drvdata(dev);
@@ -407,14 +408,17 @@ static int parport_serial_pci_resume(struct pci_dev *dev)
return 0;
}
+#endif
static struct pci_driver parport_serial_pci_driver = {
.name = "parport_serial",
.id_table = parport_serial_pci_tbl,
.probe = parport_serial_pci_probe,
.remove = __devexit_p(parport_serial_pci_remove),
+#ifdef CONFIG_PM
.suspend = parport_serial_pci_suspend,
.resume = parport_serial_pci_resume,
+#endif
};
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 4d762fc4878..c27e782e6df 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -17,6 +17,31 @@ config PCI_MSI
If you don't know what to do here, say N.
+config PCI_MULTITHREAD_PROBE
+ bool "PCI Multi-threaded probe (EXPERIMENTAL)"
+ depends on PCI && EXPERIMENTAL
+ help
+ Say Y here if you want the PCI core to spawn a new thread for
+ every PCI device that is probed. This can cause a huge
+ speedup in boot times on multiprocessor machines, and even a
+ smaller speedup on single processor machines.
+
+ But it can also cause lots of bad things to happen. A number
+ of PCI drivers can not properly handle running in this way,
+ some will just not work properly at all, while others might
+ decide to blow up power supplies with a huge load all at once,
+ so use this option at your own risk.
+
+ It is very unwise to use this option if you are not using a
+ boot process that can handle devices being created in any
+ order. A program that can create persistant block and network
+ device names (like udev) is a good idea if you wish to use
+ this option.
+
+ Again, use this option at your own risk, you have been warned!
+
+ When in doubt, say N.
+
config PCI_DEBUG
bool "PCI Debugging"
depends on PCI && DEBUG_KERNEL
diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c
index 5f7db9d2436..aadaa3c8096 100644
--- a/drivers/pci/bus.c
+++ b/drivers/pci/bus.c
@@ -77,9 +77,12 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res,
* This adds a single pci device to the global
* device list and adds sysfs and procfs entries
*/
-void __devinit pci_bus_add_device(struct pci_dev *dev)
+int __devinit pci_bus_add_device(struct pci_dev *dev)
{
- device_add(&dev->dev);
+ int retval;
+ retval = device_add(&dev->dev);
+ if (retval)
+ return retval;
down_write(&pci_bus_sem);
list_add_tail(&dev->global_list, &pci_devices);
@@ -87,6 +90,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
pci_proc_attach_device(dev);
pci_create_sysfs_dev_files(dev);
+ return 0;
}
/**
@@ -104,6 +108,7 @@ void __devinit pci_bus_add_device(struct pci_dev *dev)
void __devinit pci_bus_add_devices(struct pci_bus *bus)
{
struct pci_dev *dev;
+ int retval;
list_for_each_entry(dev, &bus->devices, bus_list) {
/*
@@ -112,7 +117,9 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
*/
if (!list_empty(&dev->global_list))
continue;
- pci_bus_add_device(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "Error adding device, continuing\n");
}
list_for_each_entry(dev, &bus->devices, bus_list) {
@@ -129,10 +136,13 @@ void __devinit pci_bus_add_devices(struct pci_bus *bus)
list_add_tail(&dev->subordinate->node,
&dev->bus->children);
up_write(&pci_bus_sem);
- }
+ }
pci_bus_add_devices(dev->subordinate);
-
- sysfs_create_link(&dev->subordinate->class_dev.kobj, &dev->dev.kobj, "bridge");
+ retval = sysfs_create_link(&dev->subordinate->class_dev.kobj,
+ &dev->dev.kobj, "bridge");
+ if (retval)
+ dev_err(&dev->dev, "Error creating sysfs "
+ "bridge symlink, continuing...\n");
}
}
}
diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h
index be104eced34..7fff07e877c 100644
--- a/drivers/pci/hotplug/acpiphp.h
+++ b/drivers/pci/hotplug/acpiphp.h
@@ -150,6 +150,11 @@ struct acpiphp_attention_info
struct module *owner;
};
+struct acpiphp_ioapic {
+ struct pci_dev *dev;
+ u32 gsi_base;
+ struct list_head list;
+};
/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index ae67a8f55ba..83e8e4412de 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -53,6 +53,8 @@
#include "acpiphp.h"
static LIST_HEAD(bridge_list);
+static LIST_HEAD(ioapic_list);
+static DEFINE_SPINLOCK(ioapic_list_lock);
#define MY_NAME "acpiphp_glue"
@@ -797,6 +799,7 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
struct pci_dev *pdev;
u32 gsi_base;
u64 phys_addr;
+ struct acpiphp_ioapic *ioapic;
/* Evaluate _STA if present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
@@ -811,41 +814,107 @@ ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+ ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic)
+ return AE_NO_MEMORY;
+
pdev = get_apic_pci_info(handle);
if (!pdev)
- return AE_OK;
+ goto exit_kfree;
- if (pci_enable_device(pdev)) {
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_enable_device(pdev))
+ goto exit_pci_dev_put;
pci_set_master(pdev);
- if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)")) {
- pci_disable_device(pdev);
- pci_dev_put(pdev);
- return AE_OK;
- }
+ if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
+ goto exit_pci_disable_device;
phys_addr = pci_resource_start(pdev, 0);
- if (acpi_register_ioapic(handle, phys_addr, gsi_base)) {
- pci_release_region(pdev, 0);
- pci_disable_device(pdev);
- pci_dev_put(pdev);
+ if (acpi_register_ioapic(handle, phys_addr, gsi_base))
+ goto exit_pci_release_region;
+
+ ioapic->gsi_base = gsi_base;
+ ioapic->dev = pdev;
+ spin_lock(&ioapic_list_lock);
+ list_add_tail(&ioapic->list, &ioapic_list);
+ spin_unlock(&ioapic_list_lock);
+
+ return AE_OK;
+
+ exit_pci_release_region:
+ pci_release_region(pdev, 0);
+ exit_pci_disable_device:
+ pci_disable_device(pdev);
+ exit_pci_dev_put:
+ pci_dev_put(pdev);
+ exit_kfree:
+ kfree(ioapic);
+
+ return AE_OK;
+}
+
+static acpi_status
+ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+ acpi_status status;
+ unsigned long sta;
+ acpi_handle tmp;
+ u32 gsi_base;
+ struct acpiphp_ioapic *pos, *n, *ioapic = NULL;
+
+ /* Evaluate _STA if present */
+ status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+ if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
+ return AE_CTRL_DEPTH;
+
+ /* Scan only PCI bus scope */
+ status = acpi_get_handle(handle, "_HID", &tmp);
+ if (ACPI_SUCCESS(status))
+ return AE_CTRL_DEPTH;
+
+ if (get_gsi_base(handle, &gsi_base))
return AE_OK;
+
+ acpi_unregister_ioapic(handle, gsi_base);
+
+ spin_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(pos, n, &ioapic_list, list) {
+ if (pos->gsi_base != gsi_base)
+ continue;
+ ioapic = pos;
+ list_del(&ioapic->list);
+ break;
}
+ spin_unlock(&ioapic_list_lock);
+
+ if (!ioapic)
+ return AE_OK;
+
+ pci_release_region(ioapic->dev, 0);
+ pci_disable_device(ioapic->dev);
+ pci_dev_put(ioapic->dev);
+ kfree(ioapic);
return AE_OK;
}
static int acpiphp_configure_ioapics(acpi_handle handle)
{
+ ioapic_add(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
return 0;
}
+static int acpiphp_unconfigure_ioapics(acpi_handle handle)
+{
+ ioapic_remove(handle, 0, NULL, NULL);
+ acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+ ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
+ return 0;
+}
+
static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
@@ -997,7 +1066,7 @@ acpiphp_bus_add_out:
* @handle: handle to acpi namespace
*
*/
-int acpiphp_bus_trim(acpi_handle handle)
+static int acpiphp_bus_trim(acpi_handle handle)
{
struct acpi_device *device;
int retval;
@@ -1074,10 +1143,11 @@ static int enable_device(struct acpiphp_slot *slot)
pci_bus_assign_resources(bus);
acpiphp_sanitize_bus(bus);
+ acpiphp_set_hpp_values(slot->bridge->handle, bus);
+ list_for_each_entry(func, &slot->funcs, sibling)
+ acpiphp_configure_ioapics(func->handle);
pci_enable_bridges(bus);
pci_bus_add_devices(bus);
- acpiphp_set_hpp_values(slot->bridge->handle, bus);
- acpiphp_configure_ioapics(slot->bridge->handle);
/* associate pci_dev to our representation */
list_for_each (l, &slot->funcs) {
@@ -1103,6 +1173,16 @@ static int enable_device(struct acpiphp_slot *slot)
return retval;
}
+static void disable_bridges(struct pci_bus *bus)
+{
+ struct pci_dev *dev;
+ list_for_each_entry(dev, &bus->devices, bus_list) {
+ if (dev->subordinate) {
+ disable_bridges(dev->subordinate);
+ pci_disable_device(dev);
+ }
+ }
+}
/**
* disable_device - disable a slot
@@ -1127,6 +1207,19 @@ static int disable_device(struct acpiphp_slot *slot)
func->bridge = NULL;
}
+ if (func->pci_dev) {
+ pci_stop_bus_device(func->pci_dev);
+ if (func->pci_dev->subordinate) {
+ disable_bridges(func->pci_dev->subordinate);
+ pci_disable_device(func->pci_dev);
+ }
+ }
+ }
+
+ list_for_each (l, &slot->funcs) {
+ func = list_entry(l, struct acpiphp_func, sibling);
+
+ acpiphp_unconfigure_ioapics(func->handle);
acpiphp_bus_trim(func->handle);
/* try to remove anyway.
* acpiphp_bus_add might have been failed */
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index 317457dd401..d0a07d9ab30 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -487,9 +487,7 @@ static void __exit ibm_acpiphp_exit(void)
if (ACPI_FAILURE(status))
err("%s: Notification handler removal failed\n", __FUNCTION__);
/* remove the /sys entries */
- if (sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr))
- err("%s: removal of sysfs file apci_table failed\n",
- __FUNCTION__);
+ sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
}
module_init(ibm_acpiphp_init);
diff --git a/drivers/pci/hotplug/cpqphp_sysfs.c b/drivers/pci/hotplug/cpqphp_sysfs.c
index 8b3da007e85..5bab666cd67 100644
--- a/drivers/pci/hotplug/cpqphp_sysfs.c
+++ b/drivers/pci/hotplug/cpqphp_sysfs.c
@@ -140,7 +140,7 @@ struct ctrl_dbg {
static int open(struct inode *inode, struct file *file)
{
- struct controller *ctrl = inode->u.generic_ip;
+ struct controller *ctrl = inode->i_private;
struct ctrl_dbg *dbg;
int retval = -ENOMEM;
diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c
index dd2b762777c..05a4f0f9018 100644
--- a/drivers/pci/hotplug/fakephp.c
+++ b/drivers/pci/hotplug/fakephp.c
@@ -176,7 +176,9 @@ static void pci_rescan_slot(struct pci_dev *temp)
struct pci_bus *bus = temp->bus;
struct pci_dev *dev;
int func;
+ int retval;
u8 hdr_type;
+
if (!pci_read_config_byte(temp, PCI_HEADER_TYPE, &hdr_type)) {
temp->hdr_type = hdr_type & 0x7f;
if (!pci_find_slot(bus->number, temp->devfn)) {
@@ -185,8 +187,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
/* multifunction device? */
@@ -205,8 +211,12 @@ static void pci_rescan_slot(struct pci_dev *temp)
dbg("New device on %s function %x:%x\n",
bus->name, temp->devfn >> 3,
temp->devfn & 7);
- pci_bus_add_device(dev);
- add_slot(dev);
+ retval = pci_bus_add_device(dev);
+ if (retval)
+ dev_err(&dev->dev, "error adding "
+ "device, continuing.\n");
+ else
+ add_slot(dev);
}
}
}
diff --git a/drivers/pci/hotplug/pci_hotplug.h b/drivers/pci/hotplug/pci_hotplug.h
index e929b7c1142..772523dc386 100644
--- a/drivers/pci/hotplug/pci_hotplug.h
+++ b/drivers/pci/hotplug/pci_hotplug.h
@@ -172,8 +172,8 @@ struct hotplug_slot {
extern int pci_hp_register (struct hotplug_slot *slot);
extern int pci_hp_deregister (struct hotplug_slot *slot);
-extern int pci_hp_change_slot_info (struct hotplug_slot *slot,
- struct hotplug_slot_info *info);
+extern int __must_check pci_hp_change_slot_info (struct hotplug_slot *slot,
+ struct hotplug_slot_info *info);
extern struct subsystem pci_hotplug_slots_subsys;
/* PCI Setting Record (Type 0) */
diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c
index b7b378df89e..e2823ea9c4e 100644
--- a/drivers/pci/hotplug/pci_hotplug_core.c
+++ b/drivers/pci/hotplug/pci_hotplug_core.c
@@ -482,31 +482,95 @@ static int has_test_file (struct hotplug_slot *slot)
static int fs_add_slot (struct hotplug_slot *slot)
{
- if (has_power_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ int retval = 0;
- if (has_attention_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ if (has_power_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ if (retval)
+ goto exit_power;
+ }
- if (has_latch_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ if (has_attention_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ goto exit_attention;
+ }
- if (has_adapter_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ if (has_latch_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ goto exit_latch;
+ }
- if (has_address_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ if (has_adapter_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ goto exit_adapter;
+ }
- if (has_max_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ if (has_address_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ goto exit_address;
+ }
+ if (has_max_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ goto exit_max_speed;
+ }
+
+ if (has_cur_bus_speed_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ goto exit_cur_speed;
+ }
+
+ if (has_test_file(slot) == 0) {
+ retval = sysfs_create_file(&slot->kobj,
+ &hotplug_slot_attr_test.attr);
+ if (retval)
+ goto exit_test;
+ }
+
+ goto exit;
+
+exit_test:
if (has_cur_bus_speed_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
- if (has_test_file(slot) == 0)
- sysfs_create_file(&slot->kobj, &hotplug_slot_attr_test.attr);
+exit_cur_speed:
+ if (has_max_bus_speed_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
- return 0;
+exit_max_speed:
+ if (has_address_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+
+exit_address:
+ if (has_adapter_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+
+exit_adapter:
+ if (has_latch_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+
+exit_latch:
+ if (has_attention_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+
+exit_attention:
+ if (has_power_file(slot) == 0)
+ sysfs_remove_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+exit_power:
+exit:
+ return retval;
}
static void fs_remove_slot (struct hotplug_slot *slot)
@@ -626,8 +690,11 @@ int pci_hp_deregister (struct hotplug_slot *slot)
*
* Returns 0 if successful, anything else for an error.
*/
-int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info *info)
+int __must_check pci_hp_change_slot_info(struct hotplug_slot *slot,
+ struct hotplug_slot_info *info)
{
+ int retval;
+
if ((slot == NULL) || (info == NULL))
return -ENODEV;
@@ -636,32 +703,60 @@ int pci_hp_change_slot_info (struct hotplug_slot *slot, struct hotplug_slot_info
* for the files referring to the fields that have now changed.
*/
if ((has_power_file(slot) == 0) &&
- (slot->info->power_status != info->power_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_power.attr);
+ (slot->info->power_status != info->power_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_power.attr);
+ if (retval)
+ return retval;
+ }
if ((has_attention_file(slot) == 0) &&
- (slot->info->attention_status != info->attention_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_attention.attr);
+ (slot->info->attention_status != info->attention_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_attention.attr);
+ if (retval)
+ return retval;
+ }
if ((has_latch_file(slot) == 0) &&
- (slot->info->latch_status != info->latch_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_latch.attr);
+ (slot->info->latch_status != info->latch_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_latch.attr);
+ if (retval)
+ return retval;
+ }
if ((has_adapter_file(slot) == 0) &&
- (slot->info->adapter_status != info->adapter_status))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_presence.attr);
+ (slot->info->adapter_status != info->adapter_status)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_presence.attr);
+ if (retval)
+ return retval;
+ }
if ((has_address_file(slot) == 0) &&
- (slot->info->address != info->address))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_address.attr);
+ (slot->info->address != info->address)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_address.attr);
+ if (retval)
+ return retval;
+ }
if ((has_max_bus_speed_file(slot) == 0) &&
- (slot->info->max_bus_speed != info->max_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_max_bus_speed.attr);
+ (slot->info->max_bus_speed != info->max_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_max_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
if ((has_cur_bus_speed_file(slot) == 0) &&
- (slot->info->cur_bus_speed != info->cur_bus_speed))
- sysfs_update_file(&slot->kobj, &hotplug_slot_attr_cur_bus_speed.attr);
+ (slot->info->cur_bus_speed != info->cur_bus_speed)) {
+ retval = sysfs_update_file(&slot->kobj,
+ &hotplug_slot_attr_cur_bus_speed.attr);
+ if (retval)
+ return retval;
+ }
memcpy (slot->info, info, sizeof (struct hotplug_slot_info));
diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c
index 33d19876835..41290a106bd 100644
--- a/drivers/pci/hotplug/pciehp_ctrl.c
+++ b/drivers/pci/hotplug/pciehp_ctrl.c
@@ -762,14 +762,14 @@ int pciehp_enable_slot(struct slot *p_slot)
if (rc || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
if (MRL_SENS(p_slot->ctrl->ctrlcap)) {
rc = p_slot->hpc_ops->get_latch_status(p_slot, &getstatus);
if (rc || getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -778,7 +778,7 @@ int pciehp_enable_slot(struct slot *p_slot)
if (rc || getstatus) {
info("%s: already enabled on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
mutex_unlock(&p_slot->ctrl->crit_sect);
@@ -813,7 +813,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || !getstatus) {
info("%s: no adapter on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -822,7 +822,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || getstatus) {
info("%s: latch open on slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -ENODEV;
}
}
@@ -831,7 +831,7 @@ int pciehp_disable_slot(struct slot *p_slot)
if (ret || !getstatus) {
info("%s: already disabled slot(%x)\n", __FUNCTION__, p_slot->number);
mutex_unlock(&p_slot->ctrl->crit_sect);
- return 1;
+ return -EINVAL;
}
}
diff --git a/drivers/pci/hotplug/pcihp_skeleton.c b/drivers/pci/hotplug/pcihp_skeleton.c
index 8ad446605f7..2b9e10e3861 100644
--- a/drivers/pci/hotplug/pcihp_skeleton.c
+++ b/drivers/pci/hotplug/pcihp_skeleton.c
@@ -1,5 +1,5 @@
/*
- * PCI Hot Plug Controller Skeleton Driver - 0.2
+ * PCI Hot Plug Controller Skeleton Driver - 0.3
*
* Copyright (C) 2001,2003 Greg Kroah-Hartman (greg@kroah.com)
* Copyright (C) 2001,2003 IBM Corp.
@@ -21,7 +21,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
- * This driver is to be used as a skeleton driver to be show how to interface
+ * This driver is to be used as a skeleton driver to show how to interface
* with the pci hotplug core easily.
*
* Send feedback to <greg@kroah.com>
@@ -58,8 +58,6 @@ static LIST_HEAD(slot_list);
#define info(format, arg...) printk(KERN_INFO "%s: " format "\n", MY_NAME , ## arg)
#define warn(format, arg...) printk(KERN_WARNING "%s: " format "\n", MY_NAME , ## arg)
-
-
/* local variables */
static int debug;
static int num_slots;
@@ -109,7 +107,6 @@ static int enable_slot(struct hotplug_slot *hotplug_slot)
return retval;
}
-
static int disable_slot(struct hotplug_slot *hotplug_slot)
{
struct slot *slot = hotplug_slot->private;
@@ -342,7 +339,7 @@ static int __init pcihp_skel_init(void)
info(DRIVER_DESC " version: " DRIVER_VERSION "\n");
/*
* Do specific initialization stuff for your driver here
- * Like initializing your controller hardware (if any) and
+ * like initializing your controller hardware (if any) and
* determining the number of slots you have in the system
* right now.
*/
diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h
index 7208b95c6ee..c7103ac5cd0 100644
--- a/drivers/pci/hotplug/shpchp.h
+++ b/drivers/pci/hotplug/shpchp.h
@@ -173,7 +173,7 @@ struct controller {
#define msg_button_cancel "PCI slot #%s - action canceled due to button press.\n"
/* sysfs functions for the hotplug controller info */
-extern void shpchp_create_ctrl_files (struct controller *ctrl);
+extern int __must_check shpchp_create_ctrl_files(struct controller *ctrl);
extern int shpchp_sysfs_enable_slot(struct slot *slot);
extern int shpchp_sysfs_disable_slot(struct slot *slot);
diff --git a/drivers/pci/hotplug/shpchp_core.c b/drivers/pci/hotplug/shpchp_core.c
index a14e7de1984..235c18a2239 100644
--- a/drivers/pci/hotplug/shpchp_core.c
+++ b/drivers/pci/hotplug/shpchp_core.c
@@ -449,10 +449,14 @@ static int shpc_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ctrl->speed = PCI_SPEED_33MHz;
}
- shpchp_create_ctrl_files(ctrl);
+ rc = shpchp_create_ctrl_files(ctrl);
+ if (rc)
+ goto err_cleanup_slots;
return 0;
+err_cleanup_slots:
+ cleanup_slots(ctrl);
err_out_release_ctlr:
ctrl->hpc_ops->release_ctlr(ctrl);
err_out_free_ctrl:
diff --git a/drivers/pci/hotplug/shpchp_sysfs.c b/drivers/pci/hotplug/shpchp_sysfs.c
index 620e1139e60..29fa9d26ada 100644
--- a/drivers/pci/hotplug/shpchp_sysfs.c
+++ b/drivers/pci/hotplug/shpchp_sysfs.c
@@ -91,9 +91,9 @@ static ssize_t show_ctrl (struct device *dev, struct device_attribute *attr, cha
}
static DEVICE_ATTR (ctrl, S_IRUGO, show_ctrl, NULL);
-void shpchp_create_ctrl_files (struct controller *ctrl)
+int __must_check shpchp_create_ctrl_files (struct controller *ctrl)
{
- device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
+ return device_create_file (&ctrl->pci_dev->dev, &dev_attr_ctrl);
}
void shpchp_remove_ctrl_files(struct controller *ctrl)
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c
index a83c1f5735d..27a057409ec 100644
--- a/drivers/pci/msi.c
+++ b/drivers/pci/msi.c
@@ -45,16 +45,10 @@ msi_register(struct msi_ops *ops)
return 0;
}
-static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags)
-{
- memset(p, 0, sizeof(struct msi_desc));
-}
-
static int msi_cache_init(void)
{
- msi_cachep = kmem_cache_create("msi_cache",
- sizeof(struct msi_desc),
- 0, SLAB_HWCACHE_ALIGN, msi_cache_ctor, NULL);
+ msi_cachep = kmem_cache_create("msi_cache", sizeof(struct msi_desc),
+ 0, SLAB_HWCACHE_ALIGN, NULL, NULL);
if (!msi_cachep)
return -ENOMEM;
@@ -402,11 +396,10 @@ static struct msi_desc* alloc_msi_entry(void)
{
struct msi_desc *entry;
- entry = kmem_cache_alloc(msi_cachep, SLAB_KERNEL);
+ entry = kmem_cache_zalloc(msi_cachep, GFP_KERNEL);
if (!entry)
return NULL;
- memset(entry, 0, sizeof(struct msi_desc));
entry->link.tail = entry->link.head = 0; /* single message */
entry->dev = NULL;
@@ -901,6 +894,33 @@ static int msix_capability_init(struct pci_dev *dev,
}
/**
+ * pci_msi_supported - check whether MSI may be enabled on device
+ * @dev: pointer to the pci_dev data structure of MSI device function
+ *
+ * MSI must be globally enabled and supported by the device and its root
+ * bus. But, the root bus is not easy to find since some architectures
+ * have virtual busses on top of the PCI hierarchy (for instance the
+ * hypertransport bus), while the actual bus where MSI must be supported
+ * is below. So we test the MSI flag on all parent busses and assume
+ * that no quirk will ever set the NO_MSI flag on a non-root bus.
+ **/
+static
+int pci_msi_supported(struct pci_dev * dev)
+{
+ struct pci_bus *bus;
+
+ if (!pci_msi_enable || !dev || dev->no_msi)
+ return -EINVAL;
+
+ /* check MSI flags of all parent busses */
+ for (bus = dev->bus; bus; bus = bus->parent)
+ if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
+ return -EINVAL;
+
+ return 0;
+}
+
+/**
* pci_enable_msi - configure device's MSI capability structure
* @dev: pointer to the pci_dev data structure of MSI device function
*
@@ -912,19 +932,11 @@ static int msix_capability_init(struct pci_dev *dev,
**/
int pci_enable_msi(struct pci_dev* dev)
{
- struct pci_bus *bus;
- int pos, temp, status = -EINVAL;
+ int pos, temp, status;
u16 control;
- if (!pci_msi_enable || !dev)
- return status;
-
- if (dev->no_msi)
- return status;
-
- for (bus = dev->bus; bus; bus = bus->parent)
- if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
- return -EINVAL;
+ if (pci_msi_supported(dev) < 0)
+ return -EINVAL;
temp = dev->irq;
@@ -1134,22 +1146,14 @@ static int reroute_msix_table(int head, struct msix_entry *entries, int *nvec)
**/
int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec)
{
- struct pci_bus *bus;
int status, pos, nr_entries, free_vectors;
int i, j, temp;
u16 control;
unsigned long flags;
- if (!pci_msi_enable || !dev || !entries)
+ if (!entries || pci_msi_supported(dev) < 0)
return -EINVAL;
- if (dev->no_msi)
- return -EINVAL;
-
- for (bus = dev->bus; bus; bus = bus->parent)
- if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI)
- return -EINVAL;
-
status = msi_init();
if (status < 0)
return status;
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c
index 474e9cd0e9e..b1c0c707d96 100644
--- a/drivers/pci/pci-driver.c
+++ b/drivers/pci/pci-driver.c
@@ -17,6 +17,16 @@
* Registration of PCI drivers and handling of hot-pluggable devices.
*/
+/* multithreaded probe logic */
+static int pci_multithread_probe =
+#ifdef CONFIG_PCI_MULTITHREAD_PROBE
+ 1;
+#else
+ 0;
+#endif
+__module_param_call("", pci_multithread_probe, param_set_bool, param_get_bool, &pci_multithread_probe, 0644);
+
+
/*
* Dynamic device IDs are disabled for !CONFIG_HOTPLUG
*/
@@ -46,6 +56,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
subdevice=PCI_ANY_ID, class=0, class_mask=0;
unsigned long driver_data=0;
int fields=0;
+ int retval = 0;
fields = sscanf(buf, "%x %x %x %x %x %x %lux",
&vendor, &device, &subvendor, &subdevice,
@@ -72,10 +83,12 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count)
spin_unlock(&pdrv->dynids.lock);
if (get_driver(&pdrv->driver)) {
- driver_attach(&pdrv->driver);
+ retval = driver_attach(&pdrv->driver);
put_driver(&pdrv->driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -279,6 +292,18 @@ static int pci_device_suspend(struct device * dev, pm_message_t state)
return i;
}
+static int pci_device_suspend_late(struct device * dev, pm_message_t state)
+{
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+ int i = 0;
+
+ if (drv && drv->suspend_late) {
+ i = drv->suspend_late(pci_dev, state);
+ suspend_report_result(drv->suspend_late, i);
+ }
+ return i;
+}
/*
* Default resume method for devices that have no driver provided resume,
@@ -313,6 +338,17 @@ static int pci_device_resume(struct device * dev)
return error;
}
+static int pci_device_resume_early(struct device * dev)
+{
+ int error = 0;
+ struct pci_dev * pci_dev = to_pci_dev(dev);
+ struct pci_driver * drv = pci_dev->driver;
+
+ if (drv && drv->resume_early)
+ error = drv->resume_early(pci_dev);
+ return error;
+}
+
static void pci_device_shutdown(struct device *dev)
{
struct pci_dev *pci_dev = to_pci_dev(dev);
@@ -386,6 +422,11 @@ int __pci_register_driver(struct pci_driver *drv, struct module *owner)
drv->driver.owner = owner;
drv->driver.kobj.ktype = &pci_driver_kobj_type;
+ if (pci_multithread_probe)
+ drv->driver.multithread_probe = pci_multithread_probe;
+ else
+ drv->driver.multithread_probe = drv->multithread_probe;
+
spin_lock_init(&drv->dynids.lock);
INIT_LIST_HEAD(&drv->dynids.list);
@@ -509,8 +550,10 @@ struct bus_type pci_bus_type = {
.probe = pci_device_probe,
.remove = pci_device_remove,
.suspend = pci_device_suspend,
- .shutdown = pci_device_shutdown,
+ .suspend_late = pci_device_suspend_late,
+ .resume_early = pci_device_resume_early,
.resume = pci_device_resume,
+ .shutdown = pci_device_shutdown,
.dev_attrs = pci_dev_attrs,
};
diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c
index fdefa7dcd15..a1d2e979b17 100644
--- a/drivers/pci/pci-sysfs.c
+++ b/drivers/pci/pci-sysfs.c
@@ -117,6 +117,7 @@ is_enabled_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct pci_dev *pdev = to_pci_dev(dev);
+ int retval = 0;
/* this can crash the machine when done on the "wrong" device */
if (!capable(CAP_SYS_ADMIN))
@@ -126,11 +127,53 @@ is_enabled_store(struct device *dev, struct device_attribute *attr,
pci_disable_device(pdev);
if (*buf == '1')
- pci_enable_device(pdev);
+ retval = pci_enable_device(pdev);
+ if (retval)
+ return retval;
return count;
}
+static ssize_t
+msi_bus_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ if (!pdev->subordinate)
+ return 0;
+
+ return sprintf (buf, "%u\n",
+ !(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI));
+}
+
+static ssize_t
+msi_bus_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct pci_dev *pdev = to_pci_dev(dev);
+
+ /* bad things may happen if the no_msi flag is changed
+ * while some drivers are loaded */
+ if (!capable(CAP_SYS_ADMIN))
+ return count;
+
+ if (!pdev->subordinate)
+ return count;
+
+ if (*buf == '0') {
+ pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to not support MSI,"
+ " bad things could happen.\n");
+ }
+
+ if (*buf == '1') {
+ pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI;
+ dev_warn(&pdev->dev, "forced subordinate bus to support MSI,"
+ " bad things could happen.\n");
+ }
+
+ return count;
+}
struct device_attribute pci_dev_attrs[] = {
__ATTR_RO(resource),
@@ -145,6 +188,7 @@ struct device_attribute pci_dev_attrs[] = {
__ATTR(enable, 0600, is_enabled_show, is_enabled_store),
__ATTR(broken_parity_status,(S_IRUGO|S_IWUSR),
broken_parity_status_show,broken_parity_status_store),
+ __ATTR(msi_bus, 0644, msi_bus_show, msi_bus_store),
__ATTR_NULL,
};
@@ -385,15 +429,38 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr,
}
/**
+ * pci_remove_resource_files - cleanup resource files
+ * @dev: dev to cleanup
+ *
+ * If we created resource files for @dev, remove them from sysfs and
+ * free their resources.
+ */
+static void
+pci_remove_resource_files(struct pci_dev *pdev)
+{
+ int i;
+
+ for (i = 0; i < PCI_ROM_RESOURCE; i++) {
+ struct bin_attribute *res_attr;
+
+ res_attr = pdev->res_attr[i];
+ if (res_attr) {
+ sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
+ kfree(res_attr);
+ }
+ }
+}
+
+/**
* pci_create_resource_files - create resource files in sysfs for @dev
* @dev: dev in question
*
* Walk the resources in @dev creating files for each resource available.
*/
-static void
-pci_create_resource_files(struct pci_dev *pdev)
+static int pci_create_resource_files(struct pci_dev *pdev)
{
int i;
+ int retval;
/* Expose the PCI resources from this device as files */
for (i = 0; i < PCI_ROM_RESOURCE; i++) {
@@ -416,35 +483,19 @@ pci_create_resource_files(struct pci_dev *pdev)
res_attr->size = pci_resource_len(pdev, i);
res_attr->mmap = pci_mmap_resource;
res_attr->private = &pdev->resource[i];
- sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
- }
- }
-}
-
-/**
- * pci_remove_resource_files - cleanup resource files
- * @dev: dev to cleanup
- *
- * If we created resource files for @dev, remove them from sysfs and
- * free their resources.
- */
-static void
-pci_remove_resource_files(struct pci_dev *pdev)
-{
- int i;
-
- for (i = 0; i < PCI_ROM_RESOURCE; i++) {
- struct bin_attribute *res_attr;
-
- res_attr = pdev->res_attr[i];
- if (res_attr) {
- sysfs_remove_bin_file(&pdev->dev.kobj, res_attr);
- kfree(res_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, res_attr);
+ if (retval) {
+ pci_remove_resource_files(pdev);
+ return retval;
+ }
+ } else {
+ return -ENOMEM;
}
}
+ return 0;
}
#else /* !HAVE_PCI_MMAP */
-static inline void pci_create_resource_files(struct pci_dev *dev) { return; }
+static inline int pci_create_resource_files(struct pci_dev *dev) { return 0; }
static inline void pci_remove_resource_files(struct pci_dev *dev) { return; }
#endif /* HAVE_PCI_MMAP */
@@ -529,22 +580,27 @@ static struct bin_attribute pcie_config_attr = {
.write = pci_write_config,
};
-int pci_create_sysfs_dev_files (struct pci_dev *pdev)
+int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev)
{
+ struct bin_attribute *rom_attr = NULL;
+ int retval;
+
if (!sysfs_initialized)
return -EACCES;
if (pdev->cfg_size < 4096)
- sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pci_config_attr);
else
- sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+ if (retval)
+ goto err;
- pci_create_resource_files(pdev);
+ retval = pci_create_resource_files(pdev);
+ if (retval)
+ goto err_bin_file;
/* If the device has a ROM, try to expose it in sysfs. */
if (pci_resource_len(pdev, PCI_ROM_RESOURCE)) {
- struct bin_attribute *rom_attr;
-
rom_attr = kzalloc(sizeof(*rom_attr), GFP_ATOMIC);
if (rom_attr) {
pdev->rom_attr = rom_attr;
@@ -554,13 +610,28 @@ int pci_create_sysfs_dev_files (struct pci_dev *pdev)
rom_attr->attr.owner = THIS_MODULE;
rom_attr->read = pci_read_rom;
rom_attr->write = pci_write_rom;
- sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ retval = sysfs_create_bin_file(&pdev->dev.kobj, rom_attr);
+ if (retval)
+ goto err_rom;
+ } else {
+ retval = -ENOMEM;
+ goto err_bin_file;
}
}
/* add platform-specific attributes */
pcibios_add_platform_entries(pdev);
-
+
return 0;
+
+err_rom:
+ kfree(rom_attr);
+err_bin_file:
+ if (pdev->cfg_size < 4096)
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pci_config_attr);
+ else
+ sysfs_remove_bin_file(&pdev->dev.kobj, &pcie_config_attr);
+err:
+ return retval;
}
/**
@@ -589,10 +660,14 @@ void pci_remove_sysfs_dev_files(struct pci_dev *pdev)
static int __init pci_sysfs_init(void)
{
struct pci_dev *pdev = NULL;
-
+ int retval;
+
sysfs_initialized = 1;
- for_each_pci_dev(pdev)
- pci_create_sysfs_dev_files(pdev);
+ for_each_pci_dev(pdev) {
+ retval = pci_create_sysfs_dev_files(pdev);
+ if (retval)
+ return retval;
+ }
return 0;
}
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 9f79dd6d51a..a544997399b 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -432,10 +432,12 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
case PM_EVENT_ON:
return PCI_D0;
case PM_EVENT_FREEZE:
+ case PM_EVENT_PRETHAW:
+ /* REVISIT both freeze and pre-thaw "should" use D0 */
case PM_EVENT_SUSPEND:
return PCI_D3hot;
default:
- printk("They asked me for state %d\n", state.event);
+ printk("Unrecognized suspend event %d\n", state.event);
BUG();
}
return PCI_D0;
@@ -443,6 +445,51 @@ pci_power_t pci_choose_state(struct pci_dev *dev, pm_message_t state)
EXPORT_SYMBOL(pci_choose_state);
+static int pci_save_pcie_state(struct pci_dev *dev)
+{
+ int pos, i = 0;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (pos <= 0)
+ return 0;
+
+ save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL);
+ if (!save_state) {
+ dev_err(&dev->dev, "Out of memory in pci_save_pcie_state\n");
+ return -ENOMEM;
+ }
+ cap = (u16 *)&save_state->data[0];
+
+ pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]);
+ pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]);
+ pci_add_saved_cap(dev, save_state);
+ return 0;
+}
+
+static void pci_restore_pcie_state(struct pci_dev *dev)
+{
+ int i = 0, pos;
+ struct pci_cap_saved_state *save_state;
+ u16 *cap;
+
+ save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP);
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!save_state || pos <= 0)
+ return;
+ cap = (u16 *)&save_state->data[0];
+
+ pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_SLTCTL, cap[i++]);
+ pci_write_config_word(dev, pos + PCI_EXP_RTCTL, cap[i++]);
+ pci_remove_saved_cap(save_state);
+ kfree(save_state);
+}
+
/**
* pci_save_state - save the PCI configuration space of a device before suspending
* @dev: - PCI device that we're dealing with
@@ -458,6 +505,8 @@ pci_save_state(struct pci_dev *dev)
return i;
if ((i = pci_save_msix_state(dev)) != 0)
return i;
+ if ((i = pci_save_pcie_state(dev)) != 0)
+ return i;
return 0;
}
@@ -471,6 +520,9 @@ pci_restore_state(struct pci_dev *dev)
int i;
int val;
+ /* PCI Express register must be restored first */
+ pci_restore_pcie_state(dev);
+
/*
* The Base Address register should be programmed before the command
* register(s)
@@ -953,13 +1005,12 @@ static int __devinit pci_setup(char *str)
}
str = k;
}
- return 1;
+ return 0;
}
+early_param("pci", pci_setup);
device_initcall(pci_init);
-__setup("pci=", pci_setup);
-
#if defined(CONFIG_ISA) || defined(CONFIG_EISA)
/* FIXME: Some boxes have multiple ISA bridges! */
struct pci_dev *isa_bridge;
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index 08d58fc78ee..6bf327db5c5 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -42,7 +42,7 @@ extern void pci_remove_legacy_files(struct pci_bus *bus);
/* Lock for read/write access to pci device and bus lists */
extern struct rw_semaphore pci_bus_sem;
-#ifdef CONFIG_X86_IO_APIC
+#ifdef CONFIG_PCI_MSI
extern int pci_msi_quirk;
#else
#define pci_msi_quirk 0
diff --git a/drivers/pci/pcie/Kconfig b/drivers/pci/pcie/Kconfig
index 1012db8b8b2..0ad92a8ad8b 100644
--- a/drivers/pci/pcie/Kconfig
+++ b/drivers/pci/pcie/Kconfig
@@ -34,3 +34,4 @@ config HOTPLUG_PCI_PCIE_POLL_EVENT_MODE
When in doubt, say N.
+source "drivers/pci/pcie/aer/Kconfig"
diff --git a/drivers/pci/pcie/Makefile b/drivers/pci/pcie/Makefile
index 984fa87283e..e00fb99acf4 100644
--- a/drivers/pci/pcie/Makefile
+++ b/drivers/pci/pcie/Makefile
@@ -5,3 +5,6 @@
pcieportdrv-y := portdrv_core.o portdrv_pci.o portdrv_bus.o
obj-$(CONFIG_PCIEPORTBUS) += pcieportdrv.o
+
+# Build PCI Express AER if needed
+obj-$(CONFIG_PCIEAER) += aer/
diff --git a/drivers/pci/pcie/aer/Kconfig b/drivers/pci/pcie/aer/Kconfig
new file mode 100644
index 00000000000..3f37a60a643
--- /dev/null
+++ b/drivers/pci/pcie/aer/Kconfig
@@ -0,0 +1,12 @@
+#
+# PCI Express Root Port Device AER Configuration
+#
+
+config PCIEAER
+ boolean "Root Port Advanced Error Reporting support"
+ depends on PCIEPORTBUS && ACPI
+ default y
+ help
+ This enables PCI Express Root Port Advanced Error Reporting
+ (AER) driver support. Error reporting messages sent to Root
+ Port will be handled by PCI Express AER driver.
diff --git a/drivers/pci/pcie/aer/Makefile b/drivers/pci/pcie/aer/Makefile
new file mode 100644
index 00000000000..15a4f40d520
--- /dev/null
+++ b/drivers/pci/pcie/aer/Makefile
@@ -0,0 +1,8 @@
+#
+# Makefile for PCI-Express Root Port Advanced Error Reporting Driver
+#
+
+obj-$(CONFIG_PCIEAER) += aerdriver.o
+
+aerdriver-objs := aerdrv_errprint.o aerdrv_core.o aerdrv.o aerdrv_acpi.o
+
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
new file mode 100644
index 00000000000..0d4ac027d53
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -0,0 +1,346 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * This file implements the AER root port service driver. The driver will
+ * register an irq handler. When root port triggers an AER interrupt, the irq
+ * handler will collect root port status and schedule a work.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/pcieport_if.h>
+
+#include "aerdrv.h"
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0"
+#define DRIVER_AUTHOR "tom.l.nguyen@intel.com"
+#define DRIVER_DESC "Root Port Advanced Error Reporting Driver"
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id );
+static void aer_remove(struct pcie_device *dev);
+static int aer_suspend(struct pcie_device *dev, pm_message_t state)
+{return 0;}
+static int aer_resume(struct pcie_device *dev) {return 0;}
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error);
+static void aer_error_resume(struct pci_dev *dev);
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev);
+
+/*
+ * PCI Express bus's AER Root service driver data structure
+ */
+static struct pcie_port_service_id aer_id[] = {
+ {
+ .vendor = PCI_ANY_ID,
+ .device = PCI_ANY_ID,
+ .port_type = PCIE_RC_PORT,
+ .service_type = PCIE_PORT_SERVICE_AER,
+ },
+ { /* end: all zeroes */ }
+};
+
+static struct pci_error_handlers aer_error_handlers = {
+ .error_detected = aer_error_detected,
+ .resume = aer_error_resume,
+};
+
+static struct pcie_port_service_driver aerdrv = {
+ .name = "aer",
+ .id_table = &aer_id[0],
+
+ .probe = aer_probe,
+ .remove = aer_remove,
+
+ .suspend = aer_suspend,
+ .resume = aer_resume,
+
+ .err_handler = &aer_error_handlers,
+
+ .reset_link = aer_root_reset,
+};
+
+/**
+ * aer_irq - Root Port's ISR
+ * @irq: IRQ assigned to Root Port
+ * @context: pointer to Root Port data structure
+ * @r: pointer struct pt_regs
+ *
+ * Invoked when Root Port detects AER messages.
+ **/
+static irqreturn_t aer_irq(int irq, void *context, struct pt_regs * r)
+{
+ unsigned int status, id;
+ struct pcie_device *pdev = (struct pcie_device *)context;
+ struct aer_rpc *rpc = get_service_data(pdev);
+ int next_prod_idx;
+ unsigned long flags;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev->port);
+ /*
+ * Must lock access to Root Error Status Reg, Root Error ID Reg,
+ * and Root error producer/consumer index
+ */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+
+ /* Read error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
+ if (!(status & ROOT_ERR_STATUS_MASKS)) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_NONE;
+ }
+
+ /* Read error source and clear error status */
+ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
+ pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
+
+ /* Store error source for later DPC handler */
+ next_prod_idx = rpc->prod_idx + 1;
+ if (next_prod_idx == AER_ERROR_SOURCES_MAX)
+ next_prod_idx = 0;
+ if (next_prod_idx == rpc->cons_idx) {
+ /*
+ * Error Storm Condition - possibly the same error occurred.
+ * Drop the error.
+ */
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return IRQ_HANDLED;
+ }
+ rpc->e_sources[rpc->prod_idx].status = status;
+ rpc->e_sources[rpc->prod_idx].id = id;
+ rpc->prod_idx = next_prod_idx;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ /* Invoke DPC handler */
+ schedule_work(&rpc->dpc_handler);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * aer_alloc_rpc - allocate Root Port data structure
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when Root Port's AER service is loaded.
+ **/
+static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc;
+
+ if (!(rpc = (struct aer_rpc *)kmalloc(sizeof(struct aer_rpc),
+ GFP_KERNEL)))
+ return NULL;
+
+ memset(rpc, 0, sizeof(struct aer_rpc));
+ /*
+ * Initialize Root lock access, e_lock, to Root Error Status Reg,
+ * Root Error ID Reg, and Root error producer/consumer index.
+ */
+ rpc->e_lock = SPIN_LOCK_UNLOCKED;
+
+ rpc->rpd = dev;
+ INIT_WORK(&rpc->dpc_handler, aer_isr, (void *)dev);
+ rpc->prod_idx = rpc->cons_idx = 0;
+ mutex_init(&rpc->rpc_mutex);
+ init_waitqueue_head(&rpc->wait_release);
+
+ /* Use PCIE bus function to store rpc into PCIE device */
+ set_service_data(dev, rpc);
+
+ return rpc;
+}
+
+/**
+ * aer_remove - clean up resources
+ * @dev: pointer to the pcie_dev data structure
+ *
+ * Invoked when PCI Express bus unloads or AER probe fails.
+ **/
+static void aer_remove(struct pcie_device *dev)
+{
+ struct aer_rpc *rpc = get_service_data(dev);
+
+ if (rpc) {
+ /* If register interrupt service, it must be free. */
+ if (rpc->isr)
+ free_irq(dev->irq, dev);
+
+ wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
+
+ aer_delete_rootport(rpc);
+ set_service_data(dev, NULL);
+ }
+}
+
+/**
+ * aer_probe - initialize resources
+ * @dev: pointer to the pcie_dev data structure
+ * @id: pointer to the service id data structure
+ *
+ * Invoked when PCI Express bus loads AER service driver.
+ **/
+static int __devinit aer_probe (struct pcie_device *dev,
+ const struct pcie_port_service_id *id )
+{
+ int status;
+ struct aer_rpc *rpc;
+ struct device *device = &dev->device;
+
+ /* Init */
+ if ((status = aer_init(dev)))
+ return status;
+
+ /* Alloc rpc data structure */
+ if (!(rpc = aer_alloc_rpc(dev))) {
+ printk(KERN_DEBUG "%s: Alloc rpc fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return -ENOMEM;
+ }
+
+ /* Request IRQ ISR */
+ if ((status = request_irq(dev->irq, aer_irq, SA_SHIRQ, "aerdrv",
+ dev))) {
+ printk(KERN_DEBUG "%s: Request ISR fails on PCIE device[%s]\n",
+ __FUNCTION__, device->bus_id);
+ aer_remove(dev);
+ return status;
+ }
+
+ rpc->isr = 1;
+
+ aer_enable_rootport(rpc);
+
+ return status;
+}
+
+/**
+ * aer_root_reset - reset link on Root Port
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver when performing link reset at Root Port.
+ **/
+static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
+{
+ u16 p2p_ctrl;
+ u32 status;
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Assert Secondary Bus Reset */
+ pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
+ p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /* De-assert Secondary Bus Reset */
+ p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
+ pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+
+ /*
+ * System software must wait for at least 100ms from the end
+ * of a reset of one or more device before it is permitted
+ * to issue Configuration Requests to those devices.
+ */
+ msleep(200);
+ printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
+ pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
+ pci_write_config_dword(dev,
+ pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+/**
+ * aer_error_detected - update severity status
+ * @dev: pointer to Root Port's pci_dev data structure
+ * @error: error severity being notified by port bus
+ *
+ * Invoked by Port Bus driver during error recovery.
+ **/
+static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
+{
+ /* Root Port has no impact. Always recovers. */
+ return PCI_ERS_RESULT_CAN_RECOVER;
+}
+
+/**
+ * aer_error_resume - clean up corresponding error status bits
+ * @dev: pointer to Root Port's pci_dev data structure
+ *
+ * Invoked by Port Bus driver during nonfatal recovery.
+ **/
+static void aer_error_resume(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+ u16 reg16;
+
+ /* Clean up Root device status */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16);
+
+ /* Clean AER Root Error Status */
+ pos = pci_find_aer_capability(dev);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+}
+
+/**
+ * aer_service_init - register AER root service driver
+ *
+ * Invoked when AER root service driver is loaded.
+ **/
+static int __init aer_service_init(void)
+{
+ return pcie_port_service_register(&aerdrv);
+}
+
+/**
+ * aer_service_exit - unregister AER root service driver
+ *
+ * Invoked when AER root service driver is unloaded.
+ **/
+static void __exit aer_service_exit(void)
+{
+ pcie_port_service_unregister(&aerdrv);
+}
+
+module_init(aer_service_init);
+module_exit(aer_service_exit);
diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h
new file mode 100644
index 00000000000..daf0cad88fc
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv.h
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#ifndef _AERDRV_H_
+#define _AERDRV_H_
+
+#include <linux/pcieport_if.h>
+#include <linux/aer.h>
+
+#define AER_NONFATAL 0
+#define AER_FATAL 1
+#define AER_CORRECTABLE 2
+#define AER_UNCORRECTABLE 4
+#define AER_ERROR_MASK 0x001fffff
+#define AER_ERROR(d) (d & AER_ERROR_MASK)
+
+#define OSC_METHOD_RUN_SUCCESS 0
+#define OSC_METHOD_NOT_SUPPORTED 1
+#define OSC_METHOD_RUN_FAILURE 2
+
+/* Root Error Status Register Bits */
+#define ROOT_ERR_STATUS_MASKS 0x0f
+
+#define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \
+ PCI_EXP_RTCTL_SENFEE| \
+ PCI_EXP_RTCTL_SEFEE)
+#define ROOT_PORT_INTR_ON_MESG_MASK (PCI_ERR_ROOT_CMD_COR_EN| \
+ PCI_ERR_ROOT_CMD_NONFATAL_EN| \
+ PCI_ERR_ROOT_CMD_FATAL_EN)
+#define ERR_COR_ID(d) (d & 0xffff)
+#define ERR_UNCOR_ID(d) (d >> 16)
+
+#define AER_SUCCESS 0
+#define AER_UNSUCCESS 1
+#define AER_ERROR_SOURCES_MAX 100
+
+#define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \
+ PCI_ERR_UNC_ECRC| \
+ PCI_ERR_UNC_UNSUP| \
+ PCI_ERR_UNC_COMP_ABORT| \
+ PCI_ERR_UNC_UNX_COMP| \
+ PCI_ERR_UNC_MALF_TLP)
+
+/* AER Error Info Flags */
+#define AER_TLP_HEADER_VALID_FLAG 0x00000001
+#define AER_MULTI_ERROR_VALID_FLAG 0x00000002
+
+#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1
+#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010
+
+struct header_log_regs {
+ unsigned int dw0;
+ unsigned int dw1;
+ unsigned int dw2;
+ unsigned int dw3;
+};
+
+struct aer_err_info {
+ int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */
+ int flags;
+ unsigned int status; /* COR/UNCOR Error Status */
+ struct header_log_regs tlp; /* TLP Header */
+};
+
+struct aer_err_source {
+ unsigned int status;
+ unsigned int id;
+};
+
+struct aer_rpc {
+ struct pcie_device *rpd; /* Root Port device */
+ struct work_struct dpc_handler;
+ struct aer_err_source e_sources[AER_ERROR_SOURCES_MAX];
+ unsigned short prod_idx; /* Error Producer Index */
+ unsigned short cons_idx; /* Error Consumer Index */
+ int isr;
+ spinlock_t e_lock; /*
+ * Lock access to Error Status/ID Regs
+ * and error producer/consumer index
+ */
+ struct mutex rpc_mutex; /*
+ * only one thread could do
+ * recovery on the same
+ * root port hierachy
+ */
+ wait_queue_head_t wait_release;
+};
+
+struct aer_broadcast_data {
+ enum pci_channel_state state;
+ enum pci_ers_result result;
+};
+
+static inline pci_ers_result_t merge_result(enum pci_ers_result orig,
+ enum pci_ers_result new)
+{
+ switch (orig) {
+ case PCI_ERS_RESULT_CAN_RECOVER:
+ case PCI_ERS_RESULT_RECOVERED:
+ orig = new;
+ break;
+ case PCI_ERS_RESULT_DISCONNECT:
+ if (new == PCI_ERS_RESULT_NEED_RESET)
+ orig = new;
+ break;
+ default:
+ break;
+ }
+
+ return orig;
+}
+
+extern struct bus_type pcie_port_bus_type;
+extern void aer_enable_rootport(struct aer_rpc *rpc);
+extern void aer_delete_rootport(struct aer_rpc *rpc);
+extern int aer_init(struct pcie_device *dev);
+extern void aer_isr(void *context);
+extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info);
+extern int aer_osc_setup(struct pci_dev *dev);
+
+#endif //_AERDRV_H_
diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c
new file mode 100644
index 00000000000..fa68e89ebec
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_acpi.c
@@ -0,0 +1,68 @@
+/*
+ * Access ACPI _OSC method
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+/**
+ * aer_osc_setup - run ACPI _OSC method
+ *
+ * Return:
+ * Zero if success. Nonzero for otherwise.
+ *
+ * Invoked when PCIE bus loads AER service driver. To avoid conflict with
+ * BIOS AER support requires BIOS to yield AER control to OS native driver.
+ **/
+int aer_osc_setup(struct pci_dev *dev)
+{
+ int retval = OSC_METHOD_RUN_SUCCESS;
+ acpi_status status;
+ acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
+ struct pci_dev *pdev = dev;
+ struct pci_bus *parent;
+
+ while (!handle) {
+ if (!pdev || !pdev->bus->parent)
+ break;
+ parent = pdev->bus->parent;
+ if (!parent->self)
+ /* Parent must be a host bridge */
+ handle = acpi_get_pci_rootbridge_handle(
+ pci_domain_nr(parent),
+ parent->number);
+ else
+ handle = DEVICE_ACPI_HANDLE(
+ &(parent->self->dev));
+ pdev = parent->self;
+ }
+
+ if (!handle)
+ return OSC_METHOD_NOT_SUPPORTED;
+
+ pci_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT);
+ status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL |
+ OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL);
+ if (ACPI_FAILURE(status)) {
+ if (status == AE_SUPPORT)
+ retval = OSC_METHOD_NOT_SUPPORTED;
+ else
+ retval = OSC_METHOD_RUN_FAILURE;
+ }
+
+ return retval;
+}
+
diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c
new file mode 100644
index 00000000000..1c7e660d653
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_core.c
@@ -0,0 +1,758 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_core.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * This file implements the core part of PCI-Express AER. When an pci-express
+ * error is delivered, an error message will be collected and printed to
+ * console, then, an error recovery procedure will be executed by following
+ * the pci error recovery rules.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+#include <linux/acpi.h>
+#include <linux/pci-acpi.h>
+#include <linux/delay.h>
+#include "aerdrv.h"
+
+static int forceload;
+module_param(forceload, bool, 0);
+
+#define PCI_CFG_SPACE_SIZE (0x100)
+int pci_find_aer_capability(struct pci_dev *dev)
+{
+ int pos;
+ u32 reg32 = 0;
+
+ /* Check if it's a pci-express device */
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return 0;
+
+ /* Check if it supports pci-express AER */
+ pos = PCI_CFG_SPACE_SIZE;
+ while (pos) {
+ if (pci_read_config_dword(dev, pos, &reg32))
+ return 0;
+
+ /* some broken boards return ~0 */
+ if (reg32 == 0xffffffff)
+ return 0;
+
+ if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
+ break;
+
+ pos = reg32 >> 20;
+ }
+
+ return pos;
+}
+
+int pci_enable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_disable_pcie_error_reporting(struct pci_dev *dev)
+{
+ u16 reg16 = 0;
+ int pos;
+
+ pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE);
+ pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
+ reg16);
+ return 0;
+}
+
+int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
+{
+ int pos;
+ u32 status, mask;
+
+ pos = pci_find_aer_capability(dev);
+ if (!pos)
+ return -EIO;
+
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
+ if (dev->error_state == pci_channel_io_normal)
+ status &= ~mask; /* Clear corresponding nonfatal bits */
+ else
+ status &= mask; /* Clear corresponding fatal bits */
+ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
+
+ return 0;
+}
+
+static int find_device_iter(struct device *device, void *data)
+{
+ struct pci_dev *dev;
+ u16 id = *(unsigned long *)data;
+ u8 secondary, subordinate, d_bus = id >> 8;
+
+ if (device->bus == &pci_bus_type) {
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn)) {
+ /*
+ * Device ID match
+ */
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+
+ /*
+ * If device is P2P, check if it is an upstream?
+ */
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ pci_read_config_byte(dev, PCI_SECONDARY_BUS,
+ &secondary);
+ pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
+ &subordinate);
+ if (d_bus >= secondary && d_bus <= subordinate) {
+ *(unsigned long*)data = (unsigned long)device;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * find_source_device - search through device hierarchy for source device
+ * @p_dev: pointer to Root Port pci_dev data structure
+ * @id: device ID of agent who sends an error message to this Root Port
+ *
+ * Invoked when error is detected at the Root Port.
+ **/
+static struct device* find_source_device(struct pci_dev *parent, u16 id)
+{
+ struct pci_dev *dev = parent;
+ struct device *device;
+ unsigned long device_addr;
+ int status;
+
+ /* Is Root Port an agent that sends error message? */
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return &dev->dev;
+
+ do {
+ device_addr = id;
+ if ((status = device_for_each_child(&dev->dev,
+ &device_addr, find_device_iter))) {
+ device = (struct device*)device_addr;
+ dev = to_pci_dev(device);
+ if (id == ((dev->bus->number << 8) | dev->devfn))
+ return device;
+ }
+ }while (status);
+
+ return NULL;
+}
+
+static void report_error_detected(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ dev->error_state = result_data->state;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->error_detected) {
+ if (result_data->state == pci_channel_io_frozen &&
+ !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
+ /*
+ * In case of fatal recovery, if one of down-
+ * stream device has no driver. We might be
+ * unable to recover because a later insmod
+ * of a driver for this device is unaware of
+ * its hw state.
+ */
+ printk(KERN_DEBUG "Device ID[%s] has %s\n",
+ dev->dev.bus_id, (dev->driver) ?
+ "no AER-aware driver" : "no driver");
+ }
+ return;
+ }
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->error_detected(dev, result_data->state);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_mmio_enabled(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->mmio_enabled)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->mmio_enabled(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_slot_reset(struct pci_dev *dev, void *data)
+{
+ pci_ers_result_t vote;
+ struct pci_error_handlers *err_handler;
+ struct aer_broadcast_data *result_data;
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ vote = err_handler->slot_reset(dev);
+ result_data->result = merge_result(result_data->result, vote);
+ return;
+}
+
+static void report_resume(struct pci_dev *dev, void *data)
+{
+ struct pci_error_handlers *err_handler;
+
+ dev->error_state = pci_channel_io_normal;
+
+ if (!dev->driver ||
+ !dev->driver->err_handler ||
+ !dev->driver->err_handler->slot_reset)
+ return;
+
+ err_handler = dev->driver->err_handler;
+ err_handler->resume(dev);
+ return;
+}
+
+/**
+ * broadcast_error_message - handle message broadcast to downstream drivers
+ * @device: pointer to from where in a hierarchy message is broadcasted down
+ * @api: callback to be broadcasted
+ * @state: error state
+ *
+ * Invoked during error recovery process. Once being invoked, the content
+ * of error severity will be broadcasted to all downstream drivers in a
+ * hierarchy in question.
+ **/
+static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
+ enum pci_channel_state state,
+ char *error_mesg,
+ void (*cb)(struct pci_dev *, void *))
+{
+ struct aer_broadcast_data result_data;
+
+ printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+ result_data.state = state;
+ if (cb == report_error_detected)
+ result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
+ else
+ result_data.result = PCI_ERS_RESULT_RECOVERED;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
+ /*
+ * If the error is reported by a bridge, we think this error
+ * is related to the downstream link of the bridge, so we
+ * do error recovery on all subordinates of the bridge instead
+ * of the bridge and clear the error status of the bridge.
+ */
+ if (cb == report_error_detected)
+ dev->error_state = state;
+ pci_walk_bus(dev->subordinate, cb, &result_data);
+ if (cb == report_resume) {
+ pci_cleanup_aer_uncorrect_error_status(dev);
+ dev->error_state = pci_channel_io_normal;
+ }
+ }
+ else {
+ /*
+ * If the error is reported by an end point, we think this
+ * error is related to the upstream link of the end point.
+ */
+ pci_walk_bus(dev->bus, cb, &result_data);
+ }
+
+ return result_data.result;
+}
+
+struct find_aer_service_data {
+ struct pcie_port_service_driver *aer_driver;
+ int is_downstream;
+};
+
+static int find_aer_service_iter(struct device *device, void *data)
+{
+ struct device_driver *driver;
+ struct pcie_port_service_driver *service_driver;
+ struct pcie_device *pcie_dev;
+ struct find_aer_service_data *result;
+
+ result = (struct find_aer_service_data *) data;
+
+ if (device->bus == &pcie_port_bus_type) {
+ pcie_dev = to_pcie_device(device);
+ if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT)
+ result->is_downstream = 1;
+
+ driver = device->driver;
+ if (driver) {
+ service_driver = to_service_driver(driver);
+ if (service_driver->id_table->service_type ==
+ PCIE_PORT_SERVICE_AER) {
+ result->aer_driver = service_driver;
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static void find_aer_service(struct pci_dev *dev,
+ struct find_aer_service_data *data)
+{
+ int retval;
+ retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
+}
+
+static pci_ers_result_t reset_link(struct pcie_device *aerdev,
+ struct pci_dev *dev)
+{
+ struct pci_dev *udev;
+ pci_ers_result_t status;
+ struct find_aer_service_data data;
+
+ if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
+ udev = dev;
+ else
+ udev= dev->bus->self;
+
+ data.is_downstream = 0;
+ data.aer_driver = NULL;
+ find_aer_service(udev, &data);
+
+ /*
+ * Use the aer driver of the error agent firstly.
+ * If it hasn't the aer driver, use the root port's
+ */
+ if (!data.aer_driver || !data.aer_driver->reset_link) {
+ if (data.is_downstream &&
+ aerdev->device.driver &&
+ to_service_driver(aerdev->device.driver)->reset_link) {
+ data.aer_driver =
+ to_service_driver(aerdev->device.driver);
+ } else {
+ printk(KERN_DEBUG "No link-reset support to Device ID"
+ "[%s]\n",
+ dev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ }
+
+ status = data.aer_driver->reset_link(udev);
+ if (status != PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "Link reset at upstream Device ID"
+ "[%s] failed\n",
+ udev->dev.bus_id);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+
+ return status;
+}
+
+/**
+ * do_recovery - handle nonfatal/fatal error recovery process
+ * @aerdev: pointer to a pcie_device data structure of root port
+ * @dev: pointer to a pci_dev data structure of agent detecting an error
+ * @severity: error severity type
+ *
+ * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
+ * error detected message to all downstream drivers within a hierarchy in
+ * question and return the returned code.
+ **/
+static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
+ struct pci_dev *dev,
+ int severity)
+{
+ pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
+ enum pci_channel_state state;
+
+ if (severity == AER_FATAL)
+ state = pci_channel_io_frozen;
+ else
+ state = pci_channel_io_normal;
+
+ status = broadcast_error_message(dev,
+ state,
+ "error_detected",
+ report_error_detected);
+
+ if (severity == AER_FATAL) {
+ result = reset_link(aerdev, dev);
+ if (result != PCI_ERS_RESULT_RECOVERED) {
+ /* TODO: Should panic here? */
+ return result;
+ }
+ }
+
+ if (status == PCI_ERS_RESULT_CAN_RECOVER)
+ status = broadcast_error_message(dev,
+ state,
+ "mmio_enabled",
+ report_mmio_enabled);
+
+ if (status == PCI_ERS_RESULT_NEED_RESET) {
+ /*
+ * TODO: Should call platform-specific
+ * functions to reset slot before calling
+ * drivers' slot_reset callbacks?
+ */
+ status = broadcast_error_message(dev,
+ state,
+ "slot_reset",
+ report_slot_reset);
+ }
+
+ if (status == PCI_ERS_RESULT_RECOVERED)
+ broadcast_error_message(dev,
+ state,
+ "resume",
+ report_resume);
+
+ return status;
+}
+
+/**
+ * handle_error_source - handle logging error into an event log
+ * @aerdev: pointer to pcie_device data structure of the root port
+ * @dev: pointer to pci_dev data structure of error source device
+ * @info: comprehensive error information
+ *
+ * Invoked when an error being detected by Root Port.
+ **/
+static void handle_error_source(struct pcie_device * aerdev,
+ struct pci_dev *dev,
+ struct aer_err_info info)
+{
+ pci_ers_result_t status = 0;
+ int pos;
+
+ if (info.severity == AER_CORRECTABLE) {
+ /*
+ * Correctable error does not need software intevention.
+ * No need to go through error recovery process.
+ */
+ pos = pci_find_aer_capability(dev);
+ if (pos)
+ pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ info.status);
+ } else {
+ status = do_recovery(aerdev, dev, info.severity);
+ if (status == PCI_ERS_RESULT_RECOVERED) {
+ printk(KERN_DEBUG "AER driver successfully recovered\n");
+ } else {
+ /* TODO: Should kernel panic here? */
+ printk(KERN_DEBUG "AER driver didn't recover\n");
+ }
+ }
+}
+
+/**
+ * aer_enable_rootport - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus loads AER service driver.
+ **/
+void aer_enable_rootport(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ int pos, aer_pos;
+ u16 reg16;
+ u32 reg32;
+
+ pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
+ /* Clear PCIE Capability's Device Status */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+
+ /* Disable system error generation in response to error messages */
+ pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
+ reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
+ pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+
+ aer_pos = pci_find_aer_capability(pdev);
+ /* Clear error status */
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
+ pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
+ pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
+
+ /* Enable Root Port device reporting error itself */
+ pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
+ reg16 = reg16 |
+ PCI_EXP_DEVCTL_CERE |
+ PCI_EXP_DEVCTL_NFERE |
+ PCI_EXP_DEVCTL_FERE |
+ PCI_EXP_DEVCTL_URRE;
+ pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
+ reg16);
+
+ /* Enable Root Port's interrupt in response to error messages */
+ pci_write_config_dword(pdev,
+ aer_pos + PCI_ERR_ROOT_COMMAND,
+ ROOT_PORT_INTR_ON_MESG_MASK);
+}
+
+/**
+ * disable_root_aer - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIE bus unloads AER service driver.
+ **/
+static void disable_root_aer(struct aer_rpc *rpc)
+{
+ struct pci_dev *pdev = rpc->rpd->port;
+ u32 reg32;
+ int pos;
+
+ pos = pci_find_aer_capability(pdev);
+ /* Disable Root's interrupt in response to error messages */
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
+
+ /* Clear Root's error status reg */
+ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
+}
+
+/**
+ * get_e_source - retrieve an error source
+ * @rpc: pointer to the root port which holds an error
+ *
+ * Invoked by DPC handler to consume an error.
+ **/
+static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
+{
+ struct aer_err_source *e_source;
+ unsigned long flags;
+
+ /* Lock access to Root error producer/consumer index */
+ spin_lock_irqsave(&rpc->e_lock, flags);
+ if (rpc->prod_idx == rpc->cons_idx) {
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+ return NULL;
+ }
+ e_source = &rpc->e_sources[rpc->cons_idx];
+ rpc->cons_idx++;
+ if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
+ rpc->cons_idx = 0;
+ spin_unlock_irqrestore(&rpc->e_lock, flags);
+
+ return e_source;
+}
+
+static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
+{
+ int pos;
+
+ pos = pci_find_aer_capability(dev);
+
+ /* The device might not support AER */
+ if (!pos)
+ return AER_SUCCESS;
+
+ if (info->severity == AER_CORRECTABLE) {
+ pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+ } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
+ info->severity == AER_NONFATAL) {
+
+ /* Link is still healthy for IO reads */
+ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
+ &info->status);
+ if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
+ return AER_UNSUCCESS;
+
+ if (info->status & AER_LOG_TLP_MASKS) {
+ info->flags |= AER_TLP_HEADER_VALID_FLAG;
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
+ pci_read_config_dword(dev,
+ pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
+ }
+ }
+
+ return AER_SUCCESS;
+}
+
+/**
+ * aer_isr_one_error - consume an error detected by root port
+ * @p_device: pointer to error root port service device
+ * @e_src: pointer to an error source
+ **/
+static void aer_isr_one_error(struct pcie_device *p_device,
+ struct aer_err_source *e_src)
+{
+ struct device *s_device;
+ struct aer_err_info e_info = {0, 0, 0,};
+ int i;
+ u16 id;
+
+ /*
+ * There is a possibility that both correctable error and
+ * uncorrectable error being logged. Report correctable error first.
+ */
+ for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
+ if (i > 4)
+ break;
+ if (!(e_src->status & i))
+ continue;
+
+ /* Init comprehensive error information */
+ if (i & PCI_ERR_ROOT_COR_RCV) {
+ id = ERR_COR_ID(e_src->id);
+ e_info.severity = AER_CORRECTABLE;
+ } else {
+ id = ERR_UNCOR_ID(e_src->id);
+ e_info.severity = ((e_src->status >> 6) & 1);
+ }
+ if (e_src->status &
+ (PCI_ERR_ROOT_MULTI_COR_RCV |
+ PCI_ERR_ROOT_MULTI_UNCOR_RCV))
+ e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
+ if (!(s_device = find_source_device(p_device->port, id))) {
+ printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
+ __FUNCTION__, id);
+ continue;
+ }
+ if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
+ AER_SUCCESS) {
+ aer_print_error(to_pci_dev(s_device), &e_info);
+ handle_error_source(p_device,
+ to_pci_dev(s_device),
+ e_info);
+ }
+ }
+}
+
+/**
+ * aer_isr - consume errors detected by root port
+ * @context: pointer to a private data of pcie device
+ *
+ * Invoked, as DPC, when root port records new detected error
+ **/
+void aer_isr(void *context)
+{
+ struct pcie_device *p_device = (struct pcie_device *) context;
+ struct aer_rpc *rpc = get_service_data(p_device);
+ struct aer_err_source *e_src;
+
+ mutex_lock(&rpc->rpc_mutex);
+ e_src = get_e_source(rpc);
+ while (e_src) {
+ aer_isr_one_error(p_device, e_src);
+ e_src = get_e_source(rpc);
+ }
+ mutex_unlock(&rpc->rpc_mutex);
+
+ wake_up(&rpc->wait_release);
+}
+
+/**
+ * aer_delete_rootport - disable root port aer and delete service data
+ * @rpc: pointer to a root port device being deleted
+ *
+ * Invoked when AER service unloaded on a specific Root Port
+ **/
+void aer_delete_rootport(struct aer_rpc *rpc)
+{
+ /* Disable root port AER itself */
+ disable_root_aer(rpc);
+
+ kfree(rpc);
+}
+
+/**
+ * aer_init - provide AER initialization
+ * @dev: pointer to AER pcie device
+ *
+ * Invoked when AER service driver is loaded.
+ **/
+int aer_init(struct pcie_device *dev)
+{
+ int status;
+
+ /* Run _OSC Method */
+ status = aer_osc_setup(dev->port);
+
+ if(status != OSC_METHOD_RUN_SUCCESS) {
+ printk(KERN_DEBUG "%s: AER service init fails - %s\n",
+ __FUNCTION__,
+ (status == OSC_METHOD_NOT_SUPPORTED) ?
+ "No ACPI _OSC support" : "Run ACPI _OSC fails");
+
+ if (!forceload)
+ return status;
+ }
+
+ return AER_SUCCESS;
+}
+
+EXPORT_SYMBOL_GPL(pci_find_aer_capability);
+EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
+EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
+
diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c
new file mode 100644
index 00000000000..3933d4f30e8
--- /dev/null
+++ b/drivers/pci/pcie/aer/aerdrv_errprint.c
@@ -0,0 +1,248 @@
+/*
+ * drivers/pci/pcie/aer/aerdrv_errprint.c
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Format error messages and print them to console.
+ *
+ * Copyright (C) 2006 Intel Corp.
+ * Tom Long Nguyen (tom.l.nguyen@intel.com)
+ * Zhang Yanmin (yanmin.zhang@intel.com)
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/pm.h>
+#include <linux/suspend.h>
+
+#include "aerdrv.h"
+
+#define AER_AGENT_RECEIVER 0
+#define AER_AGENT_REQUESTER 1
+#define AER_AGENT_COMPLETER 2
+#define AER_AGENT_TRANSMITTER 3
+
+#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \
+ PCI_ERR_UNC_UNSUP)
+
+#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT
+
+#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0)))
+
+#define AER_GET_AGENT(t, e) \
+ ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \
+ (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \
+ (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \
+ AER_AGENT_RECEIVER)
+
+#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR
+#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \
+ (PCI_ERR_UNC_DLP| \
+ PCI_ERR_COR_BAD_TLP| \
+ PCI_ERR_COR_BAD_DLLP| \
+ PCI_ERR_COR_REP_ROLL| \
+ ((t == AER_CORRECTABLE) ? \
+ PCI_ERR_COR_REP_TIMER: 0))
+
+#define AER_PHYSICAL_LAYER_ERROR 0
+#define AER_DATA_LINK_LAYER_ERROR 1
+#define AER_TRANSACTION_LAYER_ERROR 2
+
+#define AER_GET_LAYER_ERROR(t, e) \
+ ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \
+ AER_PHYSICAL_LAYER_ERROR : \
+ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \
+ AER_DATA_LINK_LAYER_ERROR : \
+ AER_TRANSACTION_LAYER_ERROR)
+
+/*
+ * AER error strings
+ */
+static char* aer_error_severity_string[] = {
+ "Uncorrected (Non-Fatal)",
+ "Uncorrected (Fatal)",
+ "Corrected"
+};
+
+static char* aer_error_layer[] = {
+ "Physical Layer",
+ "Data Link Layer",
+ "Transaction Layer"
+};
+static char* aer_correctable_error_string[] = {
+ "Receiver Error ", /* Bit Position 0 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Bad TLP ", /* Bit Position 6 */
+ "Bad DLLP ", /* Bit Position 7 */
+ "RELAY_NUM Rollover ", /* Bit Position 8 */
+ NULL,
+ NULL,
+ NULL,
+ "Replay Timer Timeout ", /* Bit Position 12 */
+ "Advisory Non-Fatal ", /* Bit Position 13 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_uncorrectable_error_string[] = {
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Data Link Protocol ", /* Bit Position 4 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ "Poisoned TLP ", /* Bit Position 12 */
+ "Flow Control Protocol ", /* Bit Position 13 */
+ "Completion Timeout ", /* Bit Position 14 */
+ "Completer Abort ", /* Bit Position 15 */
+ "Unexpected Completion ", /* Bit Position 16 */
+ "Receiver Overflow ", /* Bit Position 17 */
+ "Malformed TLP ", /* Bit Position 18 */
+ "ECRC ", /* Bit Position 19 */
+ "Unsupported Request ", /* Bit Position 20 */
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+};
+
+static char* aer_agent_string[] = {
+ "Receiver ID",
+ "Requester ID",
+ "Completer ID",
+ "Transmitter ID"
+};
+
+static char * aer_get_error_source_name(int severity,
+ unsigned int status,
+ char errmsg_buff[])
+{
+ int i;
+ char * errmsg = NULL;
+
+ for (i = 0; i < 32; i++) {
+ if (!(status & (1 << i)))
+ continue;
+
+ if (severity == AER_CORRECTABLE)
+ errmsg = aer_correctable_error_string[i];
+ else
+ errmsg = aer_uncorrectable_error_string[i];
+
+ if (!errmsg) {
+ sprintf(errmsg_buff, "Unknown Error Bit %2d ", i);
+ errmsg = errmsg_buff;
+ }
+
+ break;
+ }
+
+ return errmsg;
+}
+
+static DEFINE_SPINLOCK(logbuf_lock);
+static char errmsg_buff[100];
+void aer_print_error(struct pci_dev *dev, struct aer_err_info *info)
+{
+ char * errmsg;
+ int err_layer, agent;
+ char * loglevel;
+
+ if (info->severity == AER_CORRECTABLE)
+ loglevel = KERN_WARNING;
+ else
+ loglevel = KERN_ERR;
+
+ printk("%s+------ PCI-Express Device Error ------+\n", loglevel);
+ printk("%sError Severity\t\t: %s\n", loglevel,
+ aer_error_severity_string[info->severity]);
+
+ if ( info->status == 0) {
+ printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel);
+ printk("%sUnaccessible Received\t: %s\n", loglevel,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ printk("%sUnregistered Agent ID\t: %04x\n", loglevel,
+ (dev->bus->number << 8) | dev->devfn);
+ } else {
+ err_layer = AER_GET_LAYER_ERROR(info->severity, info->status);
+ printk("%sPCIE Bus Error type\t: %s\n", loglevel,
+ aer_error_layer[err_layer]);
+
+ spin_lock(&logbuf_lock);
+ errmsg = aer_get_error_source_name(info->severity,
+ info->status,
+ errmsg_buff);
+ printk("%s%s\t: %s\n", loglevel, errmsg,
+ info->flags & AER_MULTI_ERROR_VALID_FLAG ?
+ "Multiple" : "First");
+ spin_unlock(&logbuf_lock);
+
+ agent = AER_GET_AGENT(info->severity, info->status);
+ printk("%s%s\t\t: %04x\n", loglevel,
+ aer_agent_string[agent],
+ (dev->bus->number << 8) | dev->devfn);
+
+ printk("%sVendorID=%04xh, DeviceID=%04xh,"
+ " Bus=%02xh, Device=%02xh, Function=%02xh\n",
+ loglevel,
+ dev->vendor,
+ dev->device,
+ dev->bus->number,
+ PCI_SLOT(dev->devfn),
+ PCI_FUNC(dev->devfn));
+
+ if (info->flags & AER_TLP_HEADER_VALID_FLAG) {
+ unsigned char *tlp = (unsigned char *) &info->tlp;
+ printk("%sTLB Header:\n", loglevel);
+ printk("%s%02x%02x%02x%02x %02x%02x%02x%02x"
+ " %02x%02x%02x%02x %02x%02x%02x%02x\n",
+ loglevel,
+ *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp,
+ *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4),
+ *(tlp + 11), *(tlp + 10), *(tlp + 9),
+ *(tlp + 8), *(tlp + 15), *(tlp + 14),
+ *(tlp + 13), *(tlp + 12));
+ }
+ }
+}
+
diff --git a/drivers/pci/pcie/portdrv.h b/drivers/pci/pcie/portdrv.h
index 1d317d22ee8..67fcd176bab 100644
--- a/drivers/pci/pcie/portdrv.h
+++ b/drivers/pci/pcie/portdrv.h
@@ -39,7 +39,7 @@ extern int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state);
extern int pcie_port_device_resume(struct pci_dev *dev);
#endif
extern void pcie_port_device_remove(struct pci_dev *dev);
-extern void pcie_port_bus_register(void);
+extern int pcie_port_bus_register(void);
extern void pcie_port_bus_unregister(void);
#endif /* _PORTDRV_H_ */
diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c
index 3e84b501e6a..3f0976868ed 100644
--- a/drivers/pci/pcie/portdrv_bus.c
+++ b/drivers/pci/pcie/portdrv_bus.c
@@ -24,6 +24,7 @@ struct bus_type pcie_port_bus_type = {
.suspend = pcie_port_bus_suspend,
.resume = pcie_port_bus_resume,
};
+EXPORT_SYMBOL_GPL(pcie_port_bus_type);
static int pcie_port_bus_match(struct device *dev, struct device_driver *drv)
{
diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c
index 55c66226786..bd6615b4d40 100644
--- a/drivers/pci/pcie/portdrv_core.c
+++ b/drivers/pci/pcie/portdrv_core.c
@@ -6,6 +6,7 @@
* Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com)
*/
+#include <linux/compiler.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
@@ -339,8 +340,7 @@ static int suspend_iter(struct device *dev, void *data)
int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state)
{
- device_for_each_child(&dev->dev, &state, suspend_iter);
- return 0;
+ return device_for_each_child(&dev->dev, &state, suspend_iter);
}
static int resume_iter(struct device *dev, void *data)
@@ -358,8 +358,7 @@ static int resume_iter(struct device *dev, void *data)
int pcie_port_device_resume(struct pci_dev *dev)
{
- device_for_each_child(&dev->dev, NULL, resume_iter);
- return 0;
+ return device_for_each_child(&dev->dev, NULL, resume_iter);
}
#endif
@@ -402,9 +401,9 @@ void pcie_port_device_remove(struct pci_dev *dev)
pci_disable_msi(dev);
}
-void pcie_port_bus_register(void)
+int __must_check pcie_port_bus_register(void)
{
- bus_register(&pcie_port_bus_type);
+ return bus_register(&pcie_port_bus_type);
}
void pcie_port_bus_unregister(void)
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index 478d0d28f7a..037690e08f5 100644
--- a/drivers/pci/pcie/portdrv_pci.c
+++ b/drivers/pci/pcie/portdrv_pci.c
@@ -14,8 +14,10 @@
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/pcieport_if.h>
+#include <linux/aer.h>
#include "portdrv.h"
+#include "aer/aerdrv.h"
/*
* Version Information
@@ -30,6 +32,43 @@ MODULE_LICENSE("GPL");
/* global data */
static const char device_name[] = "pcieport-driver";
+static int pcie_portdrv_save_config(struct pci_dev *dev)
+{
+ return pci_save_state(dev);
+}
+
+#ifdef CONFIG_PM
+static int pcie_portdrv_restore_config(struct pci_dev *dev)
+{
+ int retval;
+
+ pci_restore_state(dev);
+ retval = pci_enable_device(dev);
+ if (retval)
+ return retval;
+ pci_set_master(dev);
+ return 0;
+}
+
+static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state)
+{
+ int ret = pcie_port_device_suspend(dev, state);
+
+ if (!ret)
+ ret = pcie_portdrv_save_config(dev);
+ return ret;
+}
+
+static int pcie_portdrv_resume(struct pci_dev *dev)
+{
+ pcie_portdrv_restore_config(dev);
+ return pcie_port_device_resume(dev);
+}
+#else
+#define pcie_portdrv_suspend NULL
+#define pcie_portdrv_resume NULL
+#endif
+
/*
* pcie_portdrv_probe - Probe PCI-Express port devices
* @dev: PCI-Express port device being probed
@@ -61,6 +100,10 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev,
return -ENOMEM;
}
+ pcie_portdrv_save_config(dev);
+
+ pci_enable_pcie_error_reporting(dev);
+
return 0;
}
@@ -70,39 +113,151 @@ static void pcie_portdrv_remove (struct pci_dev *dev)
kfree(pci_get_drvdata(dev));
}
-#ifdef CONFIG_PM
-static int pcie_portdrv_save_config(struct pci_dev *dev)
+static int error_detected_iter(struct device *device, void *data)
{
- return pci_save_state(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ struct aer_broadcast_data *result_data;
+ pci_ers_result_t status;
+
+ result_data = (struct aer_broadcast_data *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (!driver ||
+ !driver->err_handler ||
+ !driver->err_handler->error_detected)
+ return 0;
+
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error detected message to service drivers */
+ status = driver->err_handler->error_detected(
+ pcie_device->port,
+ result_data->state);
+ result_data->result =
+ merge_result(result_data->result, status);
+ }
+
+ return 0;
}
-static int pcie_portdrv_restore_config(struct pci_dev *dev)
+static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev,
+ enum pci_channel_state error)
{
+ struct aer_broadcast_data result_data =
+ {error, PCI_ERS_RESULT_CAN_RECOVER};
int retval;
- pci_restore_state(dev);
- retval = pci_enable_device(dev);
- if (retval)
- return retval;
- pci_set_master(dev);
+ /* can not fail */
+ retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter);
+
+ return result_data.result;
+}
+
+static int mmio_enabled_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->mmio_enabled) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->mmio_enabled(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
return 0;
}
-static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state)
+static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev)
{
- int ret = pcie_port_device_suspend(dev, state);
+ pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED;
+ int retval;
- if (!ret)
- ret = pcie_portdrv_save_config(dev);
- return ret;
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter);
+ return status;
}
-static int pcie_portdrv_resume (struct pci_dev *dev)
+static int slot_reset_iter(struct device *device, void *data)
{
- pcie_portdrv_restore_config(dev);
- return pcie_port_device_resume(dev);
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+ pci_ers_result_t status, *result;
+
+ result = (pci_ers_result_t *) data;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->slot_reset) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ status = driver->err_handler->slot_reset(
+ pcie_device->port);
+ *result = merge_result(*result, status);
+ }
+ }
+
+ return 0;
+}
+
+static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev)
+{
+ pci_ers_result_t status;
+ int retval;
+
+ /* If fatal, restore cfg space for possible link reset at upstream */
+ if (dev->error_state == pci_channel_io_frozen) {
+ pcie_portdrv_restore_config(dev);
+ pci_enable_pcie_error_reporting(dev);
+ }
+
+ /* get true return value from &status */
+ retval = device_for_each_child(&dev->dev, &status, slot_reset_iter);
+
+ return status;
+}
+
+static int resume_iter(struct device *device, void *data)
+{
+ struct pcie_device *pcie_device;
+ struct pcie_port_service_driver *driver;
+
+ if (device->bus == &pcie_port_bus_type && device->driver) {
+ driver = to_service_driver(device->driver);
+ if (driver &&
+ driver->err_handler &&
+ driver->err_handler->resume) {
+ pcie_device = to_pcie_device(device);
+
+ /* Forward error message to service drivers */
+ driver->err_handler->resume(pcie_device->port);
+ }
+ }
+
+ return 0;
+}
+
+static void pcie_portdrv_err_resume(struct pci_dev *dev)
+{
+ int retval;
+ /* nothing to do with error value, if it ever happens */
+ retval = device_for_each_child(&dev->dev, NULL, resume_iter);
}
-#endif
/*
* LINUX Device Driver Model
@@ -114,6 +269,13 @@ static const struct pci_device_id port_pci_ids[] = { {
};
MODULE_DEVICE_TABLE(pci, port_pci_ids);
+static struct pci_error_handlers pcie_portdrv_err_handler = {
+ .error_detected = pcie_portdrv_error_detected,
+ .mmio_enabled = pcie_portdrv_mmio_enabled,
+ .slot_reset = pcie_portdrv_slot_reset,
+ .resume = pcie_portdrv_err_resume,
+};
+
static struct pci_driver pcie_portdrv = {
.name = (char *)device_name,
.id_table = &port_pci_ids[0],
@@ -121,20 +283,25 @@ static struct pci_driver pcie_portdrv = {
.probe = pcie_portdrv_probe,
.remove = pcie_portdrv_remove,
-#ifdef CONFIG_PM
.suspend = pcie_portdrv_suspend,
.resume = pcie_portdrv_resume,
-#endif /* PM */
+
+ .err_handler = &pcie_portdrv_err_handler,
};
static int __init pcie_portdrv_init(void)
{
- int retval = 0;
+ int retval;
- pcie_port_bus_register();
+ retval = pcie_port_bus_register();
+ if (retval) {
+ printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval);
+ goto out;
+ }
retval = pci_register_driver(&pcie_portdrv);
if (retval)
pcie_port_bus_unregister();
+ out:
return retval;
}
diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c
index c5a58d1c6c1..a3b0a5eb505 100644
--- a/drivers/pci/probe.c
+++ b/drivers/pci/probe.c
@@ -339,6 +339,7 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
{
struct pci_bus *child;
int i;
+ int retval;
/*
* Allocate a new bus, and inherit stuff from the parent..
@@ -356,8 +357,13 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
child->class_dev.class = &pcibus_class;
sprintf(child->class_dev.class_id, "%04x:%02x", pci_domain_nr(child), busnr);
- class_device_register(&child->class_dev);
- class_device_create_file(&child->class_dev, &class_device_attr_cpuaffinity);
+ retval = class_device_register(&child->class_dev);
+ if (retval)
+ goto error_register;
+ retval = class_device_create_file(&child->class_dev,
+ &class_device_attr_cpuaffinity);
+ if (retval)
+ goto error_file_create;
/*
* Set up the primary, secondary and subordinate
@@ -375,6 +381,12 @@ pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr)
bridge->subordinate = child;
return child;
+
+error_file_create:
+ class_device_unregister(&child->class_dev);
+error_register:
+ kfree(child);
+ return NULL;
}
struct pci_bus * __devinit pci_add_new_bus(struct pci_bus *parent, struct pci_dev *dev, int busnr)
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index def78a2a7c1..23b599d6a9d 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -93,8 +93,21 @@ static void __devinit quirk_nopcipci(struct pci_dev *dev)
pci_pci_problems |= PCIPCI_FAIL;
}
}
+
+static void __devinit quirk_nopciamd(struct pci_dev *dev)
+{
+ u8 rev;
+ pci_read_config_byte(dev, 0x08, &rev);
+ if (rev == 0x13) {
+ /* Erratum 24 */
+ printk(KERN_INFO "Chipset erratum: Disabling direct PCI/AGP transfers.\n");
+ pci_pci_problems |= PCIAGP_FAIL;
+ }
+}
+
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_5597, quirk_nopcipci );
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_DEVICE_ID_SI_496, quirk_nopcipci );
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8151_0, quirk_nopciamd );
/*
* Triton requires workarounds to be used by the drivers
@@ -555,7 +568,7 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, PCI_DEVICE_ID_VIA_8237, quirk_via_vt
* is currently marked NoFix
*
* We have multiple reports of hangs with this chipset that went away with
- * noapic specified. For the moment we assume its the errata. We may be wrong
+ * noapic specified. For the moment we assume it's the erratum. We may be wrong
* of course. However the advice is demonstrably good even if so..
*/
static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
@@ -564,7 +577,7 @@ static void __devinit quirk_amd_ioapic(struct pci_dev *dev)
pci_read_config_byte(dev, PCI_REVISION_ID, &rev);
if (rev >= 0x02) {
- printk(KERN_WARNING "I/O APIC: AMD Errata #22 may be present. In the event of instability try\n");
+ printk(KERN_WARNING "I/O APIC: AMD Erratum #22 may be present. In the event of instability try\n");
printk(KERN_WARNING " : booting with the \"noapic\" option.\n");
}
}
@@ -577,8 +590,6 @@ static void __init quirk_ioapic_rmw(struct pci_dev *dev)
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SI, PCI_ANY_ID, quirk_ioapic_rmw );
-int pci_msi_quirk;
-
#define AMD8131_revA0 0x01
#define AMD8131_revB0 0x11
#define AMD8131_MISC 0x40
@@ -587,12 +598,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
{
unsigned char revid, tmp;
- if (dev->subordinate) {
- printk(KERN_WARNING "PCI: MSI quirk detected. "
- "PCI_BUS_FLAGS_NO_MSI set for subordinate bus.\n");
- dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
- }
-
if (nr_ioapics == 0)
return;
@@ -605,13 +610,6 @@ static void __init quirk_amd_8131_ioapic(struct pci_dev *dev)
}
}
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_amd_8131_ioapic);
-
-static void __init quirk_svw_msi(struct pci_dev *dev)
-{
- pci_msi_quirk = 1;
- printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
-}
-DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi );
#endif /* CONFIG_X86_IO_APIC */
@@ -1690,6 +1688,95 @@ static void __devinit quirk_nvidia_ck804_pcie_aer_ext_cap(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
quirk_nvidia_ck804_pcie_aer_ext_cap);
+#ifdef CONFIG_PCI_MSI
+/* To disable MSI globally */
+int pci_msi_quirk;
+
+/* The Serverworks PCI-X chipset does not support MSI. We cannot easily rely
+ * on setting PCI_BUS_FLAGS_NO_MSI in its bus flags because there are actually
+ * some other busses controlled by the chipset even if Linux is not aware of it.
+ * Instead of setting the flag on all busses in the machine, simply disable MSI
+ * globally.
+ */
+static void __init quirk_svw_msi(struct pci_dev *dev)
+{
+ pci_msi_quirk = 1;
+ printk(KERN_WARNING "PCI: MSI quirk detected. pci_msi_quirk set.\n");
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_GCNB_LE, quirk_svw_msi);
+
+/* Disable MSI on chipsets that are known to not support it */
+static void __devinit quirk_disable_msi(struct pci_dev *dev)
+{
+ if (dev->subordinate) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "PCI_BUS_FLAGS_NO_MSI set for %s subordinate bus.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi);
+
+/* Go through the list of Hypertransport capabilities and
+ * return 1 if a HT MSI capability is found and enabled */
+static int __devinit msi_ht_cap_enabled(struct pci_dev *dev)
+{
+ u8 pos;
+ int ttl;
+ for (pos = pci_find_capability(dev, PCI_CAP_ID_HT), ttl = 48;
+ pos && ttl;
+ pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT), ttl--) {
+ u32 cap_hdr;
+ /* MSI mapping section according to Hypertransport spec */
+ if (pci_read_config_dword(dev, pos, &cap_hdr) == 0
+ && (cap_hdr & 0xf8000000) == 0xa8000000 /* MSI mapping */) {
+ printk(KERN_INFO "PCI: Found HT MSI mapping on %s with capability %s\n",
+ pci_name(dev), cap_hdr & 0x10000 ? "enabled" : "disabled");
+ return (cap_hdr & 0x10000) != 0; /* MSI mapping cap enabled */
+ }
+ }
+ return 0;
+}
+
+/* Check the hypertransport MSI mapping to know whether MSI is enabled or not */
+static void __devinit quirk_msi_ht_cap(struct pci_dev *dev)
+{
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT2000_PCIE,
+ quirk_msi_ht_cap);
+
+/* The nVidia CK804 chipset may have 2 HT MSI mappings.
+ * MSI are supported if the MSI capability set in any of these mappings.
+ */
+static void __devinit quirk_nvidia_ck804_msi_ht_cap(struct pci_dev *dev)
+{
+ struct pci_dev *pdev;
+
+ if (!dev->subordinate)
+ return;
+
+ /* check HT MSI cap on this chipset and the root one.
+ * a single one having MSI is enough to be sure that MSI are supported.
+ */
+ pdev = pci_find_slot(dev->bus->number, 0);
+ if (dev->subordinate && !msi_ht_cap_enabled(dev)
+ && !msi_ht_cap_enabled(pdev)) {
+ printk(KERN_WARNING "PCI: MSI quirk detected. "
+ "MSI disabled on chipset %s.\n",
+ pci_name(dev));
+ dev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI;
+ }
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_CK804_PCIE,
+ quirk_nvidia_ck804_msi_ht_cap);
+#endif /* CONFIG_PCI_MSI */
+
EXPORT_SYMBOL(pcie_mch_quirk);
#ifdef CONFIG_HOTPLUG
EXPORT_SYMBOL(pci_fixup_device);
diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c
index 99ffbd478b2..430281b2e92 100644
--- a/drivers/pci/remove.c
+++ b/drivers/pci/remove.c
@@ -16,8 +16,11 @@ static void pci_free_resources(struct pci_dev *dev)
}
}
-static void pci_destroy_dev(struct pci_dev *dev)
+static void pci_stop_dev(struct pci_dev *dev)
{
+ if (!dev->global_list.next)
+ return;
+
if (!list_empty(&dev->global_list)) {
pci_proc_detach_device(dev);
pci_remove_sysfs_dev_files(dev);
@@ -27,6 +30,11 @@ static void pci_destroy_dev(struct pci_dev *dev)
dev->global_list.next = dev->global_list.prev = NULL;
up_write(&pci_bus_sem);
}
+}
+
+static void pci_destroy_dev(struct pci_dev *dev)
+{
+ pci_stop_dev(dev);
/* Remove the device from the device lists, and prevent any further
* list accesses from this device */
@@ -119,5 +127,32 @@ void pci_remove_behind_bridge(struct pci_dev *dev)
}
}
+static void pci_stop_bus_devices(struct pci_bus *bus)
+{
+ struct list_head *l, *n;
+
+ list_for_each_safe(l, n, &bus->devices) {
+ struct pci_dev *dev = pci_dev_b(l);
+ pci_stop_bus_device(dev);
+ }
+}
+
+/**
+ * pci_stop_bus_device - stop a PCI device and any children
+ * @dev: the device to stop
+ *
+ * Stop a PCI device (detach the driver, remove from the global list
+ * and so on). This also stop any subordinate buses and children in a
+ * depth-first manner.
+ */
+void pci_stop_bus_device(struct pci_dev *dev)
+{
+ if (dev->subordinate)
+ pci_stop_bus_devices(dev->subordinate);
+
+ pci_stop_dev(dev);
+}
+
EXPORT_SYMBOL(pci_remove_bus_device);
EXPORT_SYMBOL(pci_remove_behind_bridge);
+EXPORT_SYMBOL_GPL(pci_stop_bus_device);
diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c
index 47c1071ad84..54404917be9 100644
--- a/drivers/pci/setup-bus.c
+++ b/drivers/pci/setup-bus.c
@@ -55,12 +55,19 @@ pbus_assign_resources_sorted(struct pci_bus *bus)
list_for_each_entry(dev, &bus->devices, bus_list) {
u16 class = dev->class >> 8;
- /* Don't touch classless devices or host bridges or ioapics. */
+ /* Don't touch classless devices or host bridges. */
if (class == PCI_CLASS_NOT_DEFINED ||
- class == PCI_CLASS_BRIDGE_HOST ||
- class == PCI_CLASS_SYSTEM_PIC)
+ class == PCI_CLASS_BRIDGE_HOST)
continue;
+ /* Don't touch ioapics if it has the assigned resources. */
+ if (class == PCI_CLASS_SYSTEM_PIC) {
+ res = &dev->resource[0];
+ if (res[0].start || res[1].start || res[2].start ||
+ res[3].start || res[4].start || res[5].start)
+ continue;
+ }
+
pdev_sort_resources(dev, &head);
}
diff --git a/drivers/pcmcia/cardbus.c b/drivers/pcmcia/cardbus.c
index 3f6d51d1137..2d7effe7990 100644
--- a/drivers/pcmcia/cardbus.c
+++ b/drivers/pcmcia/cardbus.c
@@ -138,7 +138,7 @@ int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void
cs_dbg(s, 3, "read_cb_mem(%d, %#x, %u)\n", space, addr, len);
- dev = pci_find_slot(s->cb_dev->subordinate->number, 0);
+ dev = pci_get_slot(s->cb_dev->subordinate, 0);
if (!dev)
goto fail;
@@ -152,6 +152,9 @@ int read_cb_mem(struct pcmcia_socket * s, int space, u_int addr, u_int len, void
}
res = dev->resource + space - 1;
+
+ pci_dev_put(dev);
+
if (!res->flags)
goto fail;
diff --git a/drivers/pcmcia/omap_cf.c b/drivers/pcmcia/omap_cf.c
index 420e10aec0a..01be47e7273 100644
--- a/drivers/pcmcia/omap_cf.c
+++ b/drivers/pcmcia/omap_cf.c
@@ -67,6 +67,7 @@ struct omap_cf_socket {
struct platform_device *pdev;
unsigned long phys_cf;
u_int irq;
+ struct resource iomem;
};
#define POLL_INTERVAL (2 * HZ)
@@ -112,16 +113,14 @@ static int omap_cf_get_status(struct pcmcia_socket *s, u_int *sp)
if (!sp)
return -EINVAL;
- /* FIXME power management should probably be board-specific:
- * - 3VCARD vs XVCARD (OSK only handles 3VCARD)
- * - POWERON (switched on/off by set_socket)
- */
+ /* NOTE CF is always 3VCARD */
if (omap_cf_present()) {
struct omap_cf_socket *cf;
*sp = SS_READY | SS_DETECT | SS_POWERON | SS_3VCARD;
cf = container_of(s, struct omap_cf_socket, socket);
- s->irq.AssignedIRQ = cf->irq;
+ s->irq.AssignedIRQ = 0;
+ s->pci_irq = cf->irq;
} else
*sp = 0;
return 0;
@@ -132,7 +131,7 @@ omap_cf_set_socket(struct pcmcia_socket *sock, struct socket_state_t *s)
{
u16 control;
- /* FIXME some non-OSK boards will support power switching */
+ /* REVISIT some non-OSK boards may support power switching */
switch (s->Vcc) {
case 0:
case 33:
@@ -204,7 +203,7 @@ static struct pccard_operations omap_cf_ops = {
* "what chipselect is used". Boards could want more.
*/
-static int __init omap_cf_probe(struct device *dev)
+static int __devinit omap_cf_probe(struct device *dev)
{
unsigned seg;
struct omap_cf_socket *cf;
@@ -253,6 +252,9 @@ static int __init omap_cf_probe(struct device *dev)
default:
goto fail1;
}
+ cf->iomem.start = cf->phys_cf;
+ cf->iomem.end = cf->iomem.end + SZ_8K - 1;
+ cf->iomem.flags = IORESOURCE_MEM;
/* pcmcia layer only remaps "real" memory */
cf->socket.io_offset = (unsigned long)
@@ -296,6 +298,7 @@ static int __init omap_cf_probe(struct device *dev)
cf->socket.features = SS_CAP_PCCARD | SS_CAP_STATIC_MAP
| SS_CAP_MEM_ALIGN;
cf->socket.map_size = SZ_2K;
+ cf->socket.io[0].res = &cf->iomem;
status = pcmcia_register_socket(&cf->socket);
if (status < 0)
@@ -334,15 +337,15 @@ static struct device_driver omap_cf_driver = {
.bus = &platform_bus_type,
.probe = omap_cf_probe,
.remove = __devexit_p(omap_cf_remove),
- .suspend = pcmcia_socket_dev_suspend,
- .resume = pcmcia_socket_dev_resume,
+ .suspend = pcmcia_socket_dev_suspend,
+ .resume = pcmcia_socket_dev_resume,
};
static int __init omap_cf_init(void)
{
if (cpu_is_omap16xx())
- driver_register(&omap_cf_driver);
- return 0;
+ return driver_register(&omap_cf_driver);
+ return -ENODEV;
}
static void __exit omap_cf_exit(void)
diff --git a/drivers/pcmcia/socket_sysfs.c b/drivers/pcmcia/socket_sysfs.c
index c5d7476da47..933cd864a5c 100644
--- a/drivers/pcmcia/socket_sysfs.c
+++ b/drivers/pcmcia/socket_sysfs.c
@@ -298,7 +298,7 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz
{
struct pcmcia_socket *s = to_socket(container_of(kobj, struct class_device, kobj));
cisdump_t *cis;
- ssize_t ret = count;
+ int error;
if (off)
return -EINVAL;
@@ -316,25 +316,22 @@ static ssize_t pccard_store_cis(struct kobject *kobj, char *buf, loff_t off, siz
cis->Length = count + 1;
memcpy(cis->Data, buf, count);
- if (pcmcia_replace_cis(s, cis))
- ret = -EIO;
-
+ error = pcmcia_replace_cis(s, cis);
kfree(cis);
+ if (error)
+ return -EIO;
- if (!ret) {
- mutex_lock(&s->skt_mutex);
- if ((s->callback) && (s->state & SOCKET_PRESENT) &&
- !(s->state & SOCKET_CARDBUS)) {
- if (try_module_get(s->callback->owner)) {
- s->callback->requery(s);
- module_put(s->callback->owner);
- }
+ mutex_lock(&s->skt_mutex);
+ if ((s->callback) && (s->state & SOCKET_PRESENT) &&
+ !(s->state & SOCKET_CARDBUS)) {
+ if (try_module_get(s->callback->owner)) {
+ s->callback->requery(s);
+ module_put(s->callback->owner);
}
- mutex_unlock(&s->skt_mutex);
}
+ mutex_unlock(&s->skt_mutex);
-
- return (ret);
+ return count;
}
diff --git a/drivers/pnp/pnpbios/core.c b/drivers/pnp/pnpbios/core.c
index 551f58e2981..81a6c83d89a 100644
--- a/drivers/pnp/pnpbios/core.c
+++ b/drivers/pnp/pnpbios/core.c
@@ -526,6 +526,10 @@ static int __init pnpbios_init(void)
{
int ret;
+#if defined(CONFIG_PPC_MERGE)
+ if (check_legacy_ioport(PNPBIOS_BASE))
+ return -ENODEV;
+#endif
if (pnpbios_disabled || dmi_check_system(pnpbios_dmi_table)) {
printk(KERN_INFO "PnPBIOS: Disabled\n");
return -ENODEV;
@@ -575,6 +579,10 @@ subsys_initcall(pnpbios_init);
static int __init pnpbios_thread_init(void)
{
+#if defined(CONFIG_PPC_MERGE)
+ if (check_legacy_ioport(PNPBIOS_BASE))
+ return 0;
+#endif
if (pnpbios_disabled)
return 0;
#ifdef CONFIG_HOTPLUG
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 7ff1d88094b..fc766a7a611 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -27,7 +27,7 @@ config RTC_HCTOSYS
help
If you say yes here, the system time will be set using
the value read from the specified RTC device. This is useful
- in order to avoid unnecessary fschk runs.
+ in order to avoid unnecessary fsck runs.
config RTC_HCTOSYS_DEVICE
string "The RTC to read the time from"
@@ -37,6 +37,13 @@ config RTC_HCTOSYS_DEVICE
The RTC device that will be used as the source for
the system time, usually rtc0.
+config RTC_DEBUG
+ bool "RTC debug support"
+ depends on RTC_CLASS = y
+ help
+ Say yes here to enable debugging support in the RTC framework
+ and individual RTC drivers.
+
comment "RTC interfaces"
depends on RTC_CLASS
@@ -45,8 +52,8 @@ config RTC_INTF_SYSFS
depends on RTC_CLASS && SYSFS
default RTC_CLASS
help
- Say yes here if you want to use your RTC using the sysfs
- interface, /sys/class/rtc/rtcX .
+ Say yes here if you want to use your RTCs using sysfs interfaces,
+ /sys/class/rtc/rtc0 through /sys/.../rtcN.
This driver can also be built as a module. If so, the module
will be called rtc-sysfs.
@@ -56,8 +63,9 @@ config RTC_INTF_PROC
depends on RTC_CLASS && PROC_FS
default RTC_CLASS
help
- Say yes here if you want to use your RTC using the proc
- interface, /proc/driver/rtc .
+ Say yes here if you want to use your first RTC through the proc
+ interface, /proc/driver/rtc. Other RTCs will not be available
+ through that API.
This driver can also be built as a module. If so, the module
will be called rtc-proc.
@@ -67,8 +75,11 @@ config RTC_INTF_DEV
depends on RTC_CLASS
default RTC_CLASS
help
- Say yes here if you want to use your RTC using the dev
- interface, /dev/rtc .
+ Say yes here if you want to use your RTCs using the /dev
+ interfaces, which "udev" sets up as /dev/rtc0 through
+ /dev/rtcN. You may want to set up a symbolic link so one
+ of these can be accessed as /dev/rtc, which is a name
+ expected by "hwclock" and some other programs.
This driver can also be built as a module. If so, the module
will be called rtc-dev.
@@ -78,7 +89,8 @@ config RTC_INTF_DEV_UIE_EMUL
depends on RTC_INTF_DEV
help
Provides an emulation for RTC_UIE if the underlaying rtc chip
- driver did not provide RTC_UIE ioctls.
+ driver does not expose RTC_UIE ioctls. Those requests generate
+ once-per-second update interrupts, used for synchronization.
comment "RTC drivers"
depends on RTC_CLASS
@@ -238,6 +250,16 @@ config RTC_DRV_SA1100
To compile this driver as a module, choose M here: the
module will be called rtc-sa1100.
+config RTC_DRV_SH
+ tristate "SuperH On-Chip RTC"
+ depends on RTC_CLASS && SUPERH
+ help
+ Say Y here to enable support for the on-chip RTC found in
+ most SuperH processors.
+
+ To compile this driver as a module, choose M here: the
+ module will be called rtc-sh.
+
config RTC_DRV_VR41XX
tristate "NEC VR41XX"
depends on RTC_CLASS && CPU_VR41XX
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index bbcfb09d81d..3ba5ff6e680 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -2,6 +2,10 @@
# Makefile for RTC class/drivers.
#
+ifeq ($(CONFIG_RTC_DEBUG),y)
+ EXTRA_CFLAGS += -DDEBUG
+endif
+
obj-$(CONFIG_RTC_LIB) += rtc-lib.o
obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
@@ -31,3 +35,4 @@ obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_AT91) += rtc-at91.o
+obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c
index 1cb61a761cb..7a0d8ee2de9 100644
--- a/drivers/rtc/class.c
+++ b/drivers/rtc/class.c
@@ -39,7 +39,7 @@ static void rtc_device_release(struct class_device *class_dev)
* Returns the pointer to the new struct class device.
*/
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
- struct rtc_class_ops *ops,
+ const struct rtc_class_ops *ops,
struct module *owner)
{
struct rtc_device *rtc;
@@ -142,9 +142,9 @@ static void __exit rtc_exit(void)
class_destroy(rtc_class);
}
-module_init(rtc_init);
+subsys_initcall(rtc_init);
module_exit(rtc_exit);
-MODULE_AUTHOR("Alessandro Zummo <a.zummo@towerteh.it>");
+MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC class support");
MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-at91.c b/drivers/rtc/rtc-at91.c
index dfd0ce86f6a..c0714da4492 100644
--- a/drivers/rtc/rtc-at91.c
+++ b/drivers/rtc/rtc-at91.c
@@ -267,7 +267,7 @@ static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id,
return IRQ_NONE; /* not handled */
}
-static struct rtc_class_ops at91_rtc_ops = {
+static const struct rtc_class_ops at91_rtc_ops = {
.ioctl = at91_rtc_ioctl,
.read_time = at91_rtc_readtime,
.set_time = at91_rtc_settime,
@@ -307,6 +307,7 @@ static int __init at91_rtc_probe(struct platform_device *pdev)
return PTR_ERR(rtc);
}
platform_set_drvdata(pdev, rtc);
+ device_init_wakeup(&pdev->dev, 1);
printk(KERN_INFO "AT91 Real Time Clock driver.\n");
return 0;
@@ -327,6 +328,7 @@ static int __devexit at91_rtc_remove(struct platform_device *pdev)
rtc_device_unregister(rtc);
platform_set_drvdata(pdev, NULL);
+ device_init_wakeup(&pdev->dev, 0);
return 0;
}
@@ -336,6 +338,7 @@ static int __devexit at91_rtc_remove(struct platform_device *pdev)
/* AT91RM9200 RTC Power management control */
static struct timespec at91_rtc_delta;
+static u32 at91_rtc_imr;
static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
@@ -349,6 +352,18 @@ static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
rtc_tm_to_time(&tm, &time.tv_sec);
save_time_delta(&at91_rtc_delta, &time);
+ /* this IRQ is shared with DBGU and other hardware which isn't
+ * necessarily doing PM like we are...
+ */
+ at91_rtc_imr = at91_sys_read(AT91_RTC_IMR)
+ & (AT91_RTC_ALARM|AT91_RTC_SECEV);
+ if (at91_rtc_imr) {
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(AT91_ID_SYS);
+ else
+ at91_sys_write(AT91_RTC_IDR, at91_rtc_imr);
+ }
+
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
@@ -367,6 +382,13 @@ static int at91_rtc_resume(struct platform_device *pdev)
rtc_tm_to_time(&tm, &time.tv_sec);
restore_time_delta(&at91_rtc_delta, &time);
+ if (at91_rtc_imr) {
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(AT91_ID_SYS);
+ else
+ at91_sys_write(AT91_RTC_IER, at91_rtc_imr);
+ }
+
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
diff --git a/drivers/rtc/rtc-dev.c b/drivers/rtc/rtc-dev.c
index 61a58259c93..583789c66cd 100644
--- a/drivers/rtc/rtc-dev.c
+++ b/drivers/rtc/rtc-dev.c
@@ -24,7 +24,7 @@ static int rtc_dev_open(struct inode *inode, struct file *file)
int err;
struct rtc_device *rtc = container_of(inode->i_cdev,
struct rtc_device, char_dev);
- struct rtc_class_ops *ops = rtc->ops;
+ const struct rtc_class_ops *ops = rtc->ops;
/* We keep the lock as long as the device is in use
* and return immediately if busy
@@ -209,7 +209,7 @@ static int rtc_dev_ioctl(struct inode *inode, struct file *file,
int err = 0;
struct class_device *class_dev = file->private_data;
struct rtc_device *rtc = to_rtc_device(class_dev);
- struct rtc_class_ops *ops = rtc->ops;
+ const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg;
@@ -406,7 +406,6 @@ static int rtc_dev_add_device(struct class_device *class_dev,
rtc->char_dev.owner = rtc->owner;
if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) {
- cdev_del(&rtc->char_dev);
dev_err(class_dev->dev,
"failed to add char device %d:%d\n",
MAJOR(rtc_devt), rtc->id);
@@ -496,7 +495,7 @@ static void __exit rtc_dev_exit(void)
unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
}
-module_init(rtc_dev_init);
+subsys_initcall(rtc_dev_init);
module_exit(rtc_dev_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c
index e8afb938478..cc5032b6f42 100644
--- a/drivers/rtc/rtc-ds1307.c
+++ b/drivers/rtc/rtc-ds1307.c
@@ -178,7 +178,7 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
return 0;
}
-static struct rtc_class_ops ds13xx_rtc_ops = {
+static const struct rtc_class_ops ds13xx_rtc_ops = {
.read_time = ds1307_get_time,
.set_time = ds1307_set_time,
};
diff --git a/drivers/rtc/rtc-ds1553.c b/drivers/rtc/rtc-ds1553.c
index 20900149547..9647188fee2 100644
--- a/drivers/rtc/rtc-ds1553.c
+++ b/drivers/rtc/rtc-ds1553.c
@@ -18,7 +18,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define RTC_REG_SIZE 0x2000
#define RTC_OFFSET 0x1ff0
@@ -250,7 +250,7 @@ static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
return 0;
}
-static struct rtc_class_ops ds1553_rtc_ops = {
+static const struct rtc_class_ops ds1553_rtc_ops = {
.read_time = ds1553_rtc_read_time,
.set_time = ds1553_rtc_set_time,
.read_alarm = ds1553_rtc_read_alarm,
@@ -357,9 +357,13 @@ static int __init ds1553_rtc_probe(struct platform_device *pdev)
pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
- sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
+ if (ret)
+ goto out;
return 0;
out:
+ if (pdata->rtc)
+ rtc_device_unregister(pdata->rtc);
if (pdata->irq >= 0)
free_irq(pdata->irq, pdev);
if (ioaddr)
diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c
index 9be81fd4737..9c68ec99afa 100644
--- a/drivers/rtc/rtc-ds1672.c
+++ b/drivers/rtc/rtc-ds1672.c
@@ -156,7 +156,7 @@ static ssize_t show_control(struct device *dev, struct device_attribute *attr, c
}
static DEVICE_ATTR(control, S_IRUGO, show_control, NULL);
-static struct rtc_class_ops ds1672_rtc_ops = {
+static const struct rtc_class_ops ds1672_rtc_ops = {
.read_time = ds1672_rtc_read_time,
.set_time = ds1672_rtc_set_time,
.set_mmss = ds1672_rtc_set_mmss,
diff --git a/drivers/rtc/rtc-ds1742.c b/drivers/rtc/rtc-ds1742.c
index 8e47e5a06d2..6273a3d240a 100644
--- a/drivers/rtc/rtc-ds1742.c
+++ b/drivers/rtc/rtc-ds1742.c
@@ -17,7 +17,7 @@
#include <linux/platform_device.h>
#include <linux/io.h>
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define RTC_REG_SIZE 0x800
#define RTC_OFFSET 0x7f8
@@ -116,7 +116,7 @@ static int ds1742_rtc_read_time(struct device *dev, struct rtc_time *tm)
return 0;
}
-static struct rtc_class_ops ds1742_rtc_ops = {
+static const struct rtc_class_ops ds1742_rtc_ops = {
.read_time = ds1742_rtc_read_time,
.set_time = ds1742_rtc_set_time,
};
@@ -196,7 +196,7 @@ static int __init ds1742_rtc_probe(struct platform_device *pdev)
writeb(sec, ioaddr + RTC_SECONDS);
writeb(cen & RTC_CENTURY_MASK, ioaddr + RTC_CONTROL);
}
- if (readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG)
+ if (!(readb(ioaddr + RTC_DAY) & RTC_BATT_FLAG))
dev_warn(&pdev->dev, "voltage-low detected.\n");
rtc = rtc_device_register(pdev->name, &pdev->dev,
@@ -208,9 +208,13 @@ static int __init ds1742_rtc_probe(struct platform_device *pdev)
pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
- sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
+ ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
+ if (ret)
+ goto out;
return 0;
out:
+ if (pdata->rtc)
+ rtc_device_unregister(pdata->rtc);
if (ioaddr)
iounmap(ioaddr);
if (pdata->baseaddr)
diff --git a/drivers/rtc/rtc-ep93xx.c b/drivers/rtc/rtc-ep93xx.c
index e1a1169e466..ef4f147f3c0 100644
--- a/drivers/rtc/rtc-ep93xx.c
+++ b/drivers/rtc/rtc-ep93xx.c
@@ -73,7 +73,7 @@ static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static struct rtc_class_ops ep93xx_rtc_ops = {
+static const struct rtc_class_ops ep93xx_rtc_ops = {
.read_time = ep93xx_rtc_read_time,
.set_time = ep93xx_rtc_set_time,
.set_mmss = ep93xx_rtc_set_mmss,
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index f324d0a635d..1c743641b73 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -390,7 +390,7 @@ static int isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
}
-static struct rtc_class_ops isl1208_rtc_ops = {
+static const struct rtc_class_ops isl1208_rtc_ops = {
.proc = isl1208_rtc_proc,
.read_time = isl1208_rtc_read_time,
.set_time = isl1208_rtc_set_time,
diff --git a/drivers/rtc/rtc-lib.c b/drivers/rtc/rtc-lib.c
index 9812120f3a7..ba795a4db1e 100644
--- a/drivers/rtc/rtc-lib.c
+++ b/drivers/rtc/rtc-lib.c
@@ -94,12 +94,12 @@ EXPORT_SYMBOL(rtc_time_to_tm);
int rtc_valid_tm(struct rtc_time *tm)
{
if (tm->tm_year < 70
- || tm->tm_mon >= 12
+ || ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
- || tm->tm_hour >= 24
- || tm->tm_min >= 60
- || tm->tm_sec >= 60)
+ || ((unsigned)tm->tm_hour) >= 24
+ || ((unsigned)tm->tm_min) >= 60
+ || ((unsigned)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
diff --git a/drivers/rtc/rtc-m48t86.c b/drivers/rtc/rtc-m48t86.c
index 8c0d1a6739a..8ff4a1221f5 100644
--- a/drivers/rtc/rtc-m48t86.c
+++ b/drivers/rtc/rtc-m48t86.c
@@ -138,7 +138,7 @@ static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static struct rtc_class_ops m48t86_rtc_ops = {
+static const struct rtc_class_ops m48t86_rtc_ops = {
.read_time = m48t86_rtc_read_time,
.set_time = m48t86_rtc_set_time,
.proc = m48t86_rtc_proc,
diff --git a/drivers/rtc/rtc-max6902.c b/drivers/rtc/rtc-max6902.c
index 2c9739562b5..9eeef964663 100644
--- a/drivers/rtc/rtc-max6902.c
+++ b/drivers/rtc/rtc-max6902.c
@@ -207,7 +207,7 @@ static int max6902_set_time(struct device *dev, struct rtc_time *tm)
return max6902_set_datetime(dev, tm);
}
-static struct rtc_class_ops max6902_rtc_ops = {
+static const struct rtc_class_ops max6902_rtc_ops = {
.read_time = max6902_read_time,
.set_time = max6902_set_time,
};
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c
index ba9a583b7b6..a760cf69af9 100644
--- a/drivers/rtc/rtc-pcf8563.c
+++ b/drivers/rtc/rtc-pcf8563.c
@@ -95,7 +95,7 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR])
- + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 100 : 0);
+ + (buf[PCF8563_REG_MO] & PCF8563_MO_C ? 0 : 100);
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
@@ -135,7 +135,7 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
/* year and century */
buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
- if (tm->tm_year / 100)
+ if (tm->tm_year < 100)
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
@@ -227,7 +227,7 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
-static struct rtc_class_ops pcf8563_rtc_ops = {
+static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
diff --git a/drivers/rtc/rtc-pcf8583.c b/drivers/rtc/rtc-pcf8583.c
index b235a30cb66..5875ebb8c79 100644
--- a/drivers/rtc/rtc-pcf8583.c
+++ b/drivers/rtc/rtc-pcf8583.c
@@ -273,7 +273,7 @@ static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm)
return ret;
}
-static struct rtc_class_ops pcf8583_rtc_ops = {
+static const struct rtc_class_ops pcf8583_rtc_ops = {
.read_time = pcf8583_rtc_read_time,
.set_time = pcf8583_rtc_set_time,
};
diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c
index d6d1c5726b0..739d1a6e14e 100644
--- a/drivers/rtc/rtc-pl031.c
+++ b/drivers/rtc/rtc-pl031.c
@@ -128,7 +128,7 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
return 0;
}
-static struct rtc_class_ops pl031_ops = {
+static const struct rtc_class_ops pl031_ops = {
.open = pl031_open,
.release = pl031_release,
.ioctl = pl031_ioctl,
diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c
index cef5f5a3bbf..d51d8f20e63 100644
--- a/drivers/rtc/rtc-proc.c
+++ b/drivers/rtc/rtc-proc.c
@@ -23,7 +23,7 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)
{
int err;
struct class_device *class_dev = seq->private;
- struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
+ const struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
struct rtc_wkalrm alrm;
struct rtc_time tm;
@@ -61,7 +61,7 @@ static int rtc_proc_show(struct seq_file *seq, void *offset)
seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
else
seq_printf(seq, "**-");
- if ((unsigned int)alrm.time.tm_mday <= 31)
+ if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
seq_printf(seq, "%02d\n", alrm.time.tm_mday);
else
seq_printf(seq, "**\n");
@@ -156,7 +156,7 @@ static void __exit rtc_proc_exit(void)
class_interface_unregister(&rtc_proc_interface);
}
-module_init(rtc_proc_init);
+subsys_initcall(rtc_proc_init);
module_exit(rtc_proc_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
diff --git a/drivers/rtc/rtc-rs5c348.c b/drivers/rtc/rtc-rs5c348.c
index 0964d1dba92..f50f3fc353c 100644
--- a/drivers/rtc/rtc-rs5c348.c
+++ b/drivers/rtc/rtc-rs5c348.c
@@ -23,7 +23,7 @@
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
-#define DRV_VERSION "0.1"
+#define DRV_VERSION "0.2"
#define RS5C348_REG_SECS 0
#define RS5C348_REG_MINS 1
@@ -140,7 +140,7 @@ rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
return 0;
}
-static struct rtc_class_ops rs5c348_rtc_ops = {
+static const struct rtc_class_ops rs5c348_rtc_ops = {
.read_time = rs5c348_rtc_read_time,
.set_time = rs5c348_rtc_set_time,
};
@@ -175,8 +175,15 @@ static int __devinit rs5c348_probe(struct spi_device *spi)
goto kfree_exit;
if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
u8 buf[2];
+ struct rtc_time tm;
if (ret & RS5C348_BIT_VDET)
dev_warn(&spi->dev, "voltage-low detected.\n");
+ if (ret & RS5C348_BIT_XSTP)
+ dev_warn(&spi->dev, "oscillator-stop detected.\n");
+ rtc_time_to_tm(0, &tm); /* 1970/1/1 */
+ ret = rs5c348_rtc_set_time(&spi->dev, &tm);
+ if (ret < 0)
+ goto kfree_exit;
buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
buf[1] = 0;
ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c
index 7553d797603..bbdad099471 100644
--- a/drivers/rtc/rtc-rs5c372.c
+++ b/drivers/rtc/rtc-rs5c372.c
@@ -160,7 +160,7 @@ static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static struct rtc_class_ops rs5c372_rtc_ops = {
+static const struct rtc_class_ops rs5c372_rtc_ops = {
.proc = rs5c372_rtc_proc,
.read_time = rs5c372_rtc_read_time,
.set_time = rs5c372_rtc_set_time,
diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c
index 2c7de79c83b..625dad2eeb4 100644
--- a/drivers/rtc/rtc-s3c.c
+++ b/drivers/rtc/rtc-s3c.c
@@ -386,7 +386,7 @@ static void s3c_rtc_release(struct device *dev)
free_irq(s3c_rtc_tickno, rtc_dev);
}
-static struct rtc_class_ops s3c_rtcops = {
+static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.ioctl = s3c_rtc_ioctl,
diff --git a/drivers/rtc/rtc-sa1100.c b/drivers/rtc/rtc-sa1100.c
index ee4b61ee67b..439c41aea31 100644
--- a/drivers/rtc/rtc-sa1100.c
+++ b/drivers/rtc/rtc-sa1100.c
@@ -303,7 +303,7 @@ static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static struct rtc_class_ops sa1100_rtc_ops = {
+static const struct rtc_class_ops sa1100_rtc_ops = {
.open = sa1100_rtc_open,
.read_callback = sa1100_rtc_read_callback,
.release = sa1100_rtc_release,
diff --git a/drivers/rtc/rtc-sh.c b/drivers/rtc/rtc-sh.c
new file mode 100644
index 00000000000..d2ce0c8bb8f
--- /dev/null
+++ b/drivers/rtc/rtc-sh.c
@@ -0,0 +1,467 @@
+/*
+ * SuperH On-Chip RTC Support
+ *
+ * Copyright (C) 2006 Paul Mundt
+ *
+ * Based on the old arch/sh/kernel/cpu/rtc.c by:
+ *
+ * Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
+ * Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/bcd.h>
+#include <linux/rtc.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/seq_file.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_CPU_SH3
+#define rtc_reg_size sizeof(u16)
+#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
+#elif defined(CONFIG_CPU_SH4)
+#define rtc_reg_size sizeof(u32)
+#define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */
+#endif
+
+#define RTC_REG(r) ((r) * rtc_reg_size)
+
+#define R64CNT RTC_REG(0)
+#define RSECCNT RTC_REG(1)
+#define RMINCNT RTC_REG(2)
+#define RHRCNT RTC_REG(3)
+#define RWKCNT RTC_REG(4)
+#define RDAYCNT RTC_REG(5)
+#define RMONCNT RTC_REG(6)
+#define RYRCNT RTC_REG(7)
+#define RSECAR RTC_REG(8)
+#define RMINAR RTC_REG(9)
+#define RHRAR RTC_REG(10)
+#define RWKAR RTC_REG(11)
+#define RDAYAR RTC_REG(12)
+#define RMONAR RTC_REG(13)
+#define RCR1 RTC_REG(14)
+#define RCR2 RTC_REG(15)
+
+/* RCR1 Bits */
+#define RCR1_CF 0x80 /* Carry Flag */
+#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
+#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
+#define RCR1_AF 0x01 /* Alarm Flag */
+
+/* RCR2 Bits */
+#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
+#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
+#define RCR2_RTCEN 0x08 /* ENable RTC */
+#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
+#define RCR2_RESET 0x02 /* Reset bit */
+#define RCR2_START 0x01 /* Start bit */
+
+struct sh_rtc {
+ void __iomem *regbase;
+ unsigned long regsize;
+ struct resource *res;
+ unsigned int alarm_irq, periodic_irq, carry_irq;
+ struct rtc_device *rtc_dev;
+ spinlock_t lock;
+};
+
+static irqreturn_t sh_rtc_interrupt(int irq, void *id, struct pt_regs *regs)
+{
+ struct platform_device *pdev = id;
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp, events = 0;
+
+ spin_lock(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (tmp & RCR1_AF)
+ events |= RTC_AF | RTC_IRQF;
+
+ tmp &= ~(RCR1_CF | RCR1_AF);
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
+
+ spin_unlock(&rtc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t sh_rtc_periodic(int irq, void *id, struct pt_regs *regs)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(id);
+
+ spin_lock(&rtc->lock);
+
+ rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF);
+
+ spin_unlock(&rtc->lock);
+
+ return IRQ_HANDLED;
+}
+
+static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR2);
+
+ if (enable) {
+ tmp &= ~RCR2_PESMASK;
+ tmp |= RCR2_PEF | (2 << 4);
+ } else
+ tmp &= ~(RCR2_PESMASK | RCR2_PEF);
+
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+
+ if (enable)
+ tmp |= RCR1_AIE;
+ else
+ tmp &= ~RCR1_AIE;
+
+ writeb(tmp, rtc->regbase + RCR1);
+
+ spin_unlock_irq(&rtc->lock);
+}
+
+static int sh_rtc_open(struct device *dev)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+ int ret;
+
+ tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF;
+ tmp |= RCR1_CIE;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, SA_INTERRUPT,
+ "sh-rtc period", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request period IRQ failed with %d, IRQ %d\n",
+ ret, rtc->periodic_irq);
+ return ret;
+ }
+
+ ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, SA_INTERRUPT,
+ "sh-rtc carry", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n",
+ ret, rtc->carry_irq);
+ free_irq(rtc->periodic_irq, dev);
+ goto err_bad_carry;
+ }
+
+ ret = request_irq(rtc->alarm_irq, sh_rtc_interrupt, SA_INTERRUPT,
+ "sh-rtc alarm", dev);
+ if (unlikely(ret)) {
+ dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n",
+ ret, rtc->alarm_irq);
+ goto err_bad_alarm;
+ }
+
+ return 0;
+
+err_bad_alarm:
+ free_irq(rtc->carry_irq, dev);
+err_bad_carry:
+ free_irq(rtc->periodic_irq, dev);
+
+ return ret;
+}
+
+static void sh_rtc_release(struct device *dev)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+
+ sh_rtc_setpie(dev, 0);
+
+ free_irq(rtc->periodic_irq, dev);
+ free_irq(rtc->carry_irq, dev);
+ free_irq(rtc->alarm_irq, dev);
+}
+
+static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
+{
+ struct sh_rtc *rtc = dev_get_drvdata(dev);
+ unsigned int tmp;
+
+ tmp = readb(rtc->regbase + RCR1);
+ seq_printf(seq, "alarm_IRQ\t: %s\n",
+ (tmp & RCR1_AIE) ? "yes" : "no");
+ seq_printf(seq, "carry_IRQ\t: %s\n",
+ (tmp & RCR1_CIE) ? "yes" : "no");
+
+ tmp = readb(rtc->regbase + RCR2);
+ seq_printf(seq, "periodic_IRQ\t: %s\n",
+ (tmp & RCR2_PEF) ? "yes" : "no");
+
+ return 0;
+}
+
+static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
+{
+ unsigned int ret = -ENOIOCTLCMD;
+
+ switch (cmd) {
+ case RTC_PIE_OFF:
+ case RTC_PIE_ON:
+ sh_rtc_setpie(dev, cmd == RTC_PIE_ON);
+ ret = 0;
+ break;
+ case RTC_AIE_OFF:
+ case RTC_AIE_ON:
+ sh_rtc_setaie(dev, cmd == RTC_AIE_ON);
+ ret = 0;
+ break;
+ }
+
+ return ret;
+}
+
+static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int sec128, sec2, yr, yr100, cf_bit;
+
+ do {
+ unsigned int tmp;
+
+ spin_lock_irq(&rtc->lock);
+
+ tmp = readb(rtc->regbase + RCR1);
+ tmp &= ~RCR1_CF; /* Clear CF-bit */
+ tmp |= RCR1_CIE;
+ writeb(tmp, rtc->regbase + RCR1);
+
+ sec128 = readb(rtc->regbase + R64CNT);
+
+ tm->tm_sec = BCD2BIN(readb(rtc->regbase + RSECCNT));
+ tm->tm_min = BCD2BIN(readb(rtc->regbase + RMINCNT));
+ tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT));
+ tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT));
+ tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT));
+ tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT));
+
+#if defined(CONFIG_CPU_SH4)
+ yr = readw(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN(yr >> 8);
+ yr &= 0xff;
+#else
+ yr = readb(rtc->regbase + RYRCNT);
+ yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20);
+#endif
+
+ tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900;
+
+ sec2 = readb(rtc->regbase + R64CNT);
+ cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF;
+
+ spin_unlock_irq(&rtc->lock);
+ } while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
+
+#if RTC_BIT_INVERTED != 0
+ if ((sec128 & RTC_BIT_INVERTED))
+ tm->tm_sec--;
+#endif
+
+ dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
+ "mday=%d, mon=%d, year=%d, wday=%d\n",
+ __FUNCTION__,
+ tm->tm_sec, tm->tm_min, tm->tm_hour,
+ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
+
+ if (rtc_valid_tm(tm) < 0)
+ dev_err(dev, "invalid date\n");
+
+ return 0;
+}
+
+static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+ unsigned int tmp;
+ int year;
+
+ spin_lock_irq(&rtc->lock);
+
+ /* Reset pre-scaler & stop RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp |= RCR2_RESET;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ writeb(BIN2BCD(tm->tm_sec), rtc->regbase + RSECCNT);
+ writeb(BIN2BCD(tm->tm_min), rtc->regbase + RMINCNT);
+ writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT);
+ writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT);
+ writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT);
+ writeb(BIN2BCD(tm->tm_mon), rtc->regbase + RMONCNT);
+
+#ifdef CONFIG_CPU_SH3
+ year = tm->tm_year % 100;
+ writeb(BIN2BCD(year), rtc->regbase + RYRCNT);
+#else
+ year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) |
+ BIN2BCD(tm->tm_year % 100);
+ writew(year, rtc->regbase + RYRCNT);
+#endif
+
+ /* Start RTC */
+ tmp = readb(rtc->regbase + RCR2);
+ tmp &= ~RCR2_RESET;
+ tmp |= RCR2_RTCEN | RCR2_START;
+ writeb(tmp, rtc->regbase + RCR2);
+
+ spin_unlock_irq(&rtc->lock);
+
+ return 0;
+}
+
+static struct rtc_class_ops sh_rtc_ops = {
+ .open = sh_rtc_open,
+ .release = sh_rtc_release,
+ .ioctl = sh_rtc_ioctl,
+ .read_time = sh_rtc_read_time,
+ .set_time = sh_rtc_set_time,
+ .proc = sh_rtc_proc,
+};
+
+static int __devinit sh_rtc_probe(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc;
+ struct resource *res;
+ int ret = -ENOENT;
+
+ rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
+ if (unlikely(!rtc))
+ return -ENOMEM;
+
+ spin_lock_init(&rtc->lock);
+
+ rtc->periodic_irq = platform_get_irq(pdev, 0);
+ if (unlikely(rtc->periodic_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for period\n");
+ goto err_badres;
+ }
+
+ rtc->carry_irq = platform_get_irq(pdev, 1);
+ if (unlikely(rtc->carry_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for carry\n");
+ goto err_badres;
+ }
+
+ rtc->alarm_irq = platform_get_irq(pdev, 2);
+ if (unlikely(rtc->alarm_irq < 0)) {
+ dev_err(&pdev->dev, "No IRQ for alarm\n");
+ goto err_badres;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_IO, 0);
+ if (unlikely(res == NULL)) {
+ dev_err(&pdev->dev, "No IO resource\n");
+ goto err_badres;
+ }
+
+ rtc->regsize = res->end - res->start + 1;
+
+ rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name);
+ if (unlikely(!rtc->res)) {
+ ret = -EBUSY;
+ goto err_badres;
+ }
+
+ rtc->regbase = (void __iomem *)rtc->res->start;
+ if (unlikely(!rtc->regbase)) {
+ ret = -EINVAL;
+ goto err_badmap;
+ }
+
+ rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
+ &sh_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc)) {
+ ret = PTR_ERR(rtc->rtc_dev);
+ goto err_badmap;
+ }
+
+ platform_set_drvdata(pdev, rtc);
+
+ return 0;
+
+err_badmap:
+ release_resource(rtc->res);
+err_badres:
+ kfree(rtc);
+
+ return ret;
+}
+
+static int __devexit sh_rtc_remove(struct platform_device *pdev)
+{
+ struct sh_rtc *rtc = platform_get_drvdata(pdev);
+
+ if (likely(rtc->rtc_dev))
+ rtc_device_unregister(rtc->rtc_dev);
+
+ sh_rtc_setpie(&pdev->dev, 0);
+ sh_rtc_setaie(&pdev->dev, 0);
+
+ release_resource(rtc->res);
+
+ platform_set_drvdata(pdev, NULL);
+
+ kfree(rtc);
+
+ return 0;
+}
+static struct platform_driver sh_rtc_platform_driver = {
+ .driver = {
+ .name = "sh-rtc",
+ .owner = THIS_MODULE,
+ },
+ .probe = sh_rtc_probe,
+ .remove = __devexit_p(sh_rtc_remove),
+};
+
+static int __init sh_rtc_init(void)
+{
+ return platform_driver_register(&sh_rtc_platform_driver);
+}
+
+static void __exit sh_rtc_exit(void)
+{
+ platform_driver_unregister(&sh_rtc_platform_driver);
+}
+
+module_init(sh_rtc_init);
+module_exit(sh_rtc_exit);
+
+MODULE_DESCRIPTION("SuperH on-chip RTC driver");
+MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c
index 7c1f3d2e53c..625637b84d3 100644
--- a/drivers/rtc/rtc-sysfs.c
+++ b/drivers/rtc/rtc-sysfs.c
@@ -116,7 +116,7 @@ static void __exit rtc_sysfs_exit(void)
class_interface_unregister(&rtc_sysfs_interface);
}
-module_init(rtc_sysfs_init);
+subsys_initcall(rtc_sysfs_init);
module_exit(rtc_sysfs_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
diff --git a/drivers/rtc/rtc-test.c b/drivers/rtc/rtc-test.c
index e1fa5fe7901..bc4bd24508a 100644
--- a/drivers/rtc/rtc-test.c
+++ b/drivers/rtc/rtc-test.c
@@ -75,7 +75,7 @@ static int test_rtc_ioctl(struct device *dev, unsigned int cmd,
}
}
-static struct rtc_class_ops test_rtc_ops = {
+static const struct rtc_class_ops test_rtc_ops = {
.proc = test_rtc_proc,
.read_time = test_rtc_read_time,
.set_time = test_rtc_set_time,
diff --git a/drivers/rtc/rtc-v3020.c b/drivers/rtc/rtc-v3020.c
index a40f400acff..09b714f1cdc 100644
--- a/drivers/rtc/rtc-v3020.c
+++ b/drivers/rtc/rtc-v3020.c
@@ -149,7 +149,7 @@ static int v3020_set_time(struct device *dev, struct rtc_time *dt)
return 0;
}
-static struct rtc_class_ops v3020_rtc_ops = {
+static const struct rtc_class_ops v3020_rtc_ops = {
.read_time = v3020_read_time,
.set_time = v3020_set_time,
};
@@ -169,9 +169,6 @@ static int rtc_probe(struct platform_device *pdev)
if (pdev->resource[0].flags != IORESOURCE_MEM)
return -EBUSY;
- if (pdev == NULL)
- return -EBUSY;
-
chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;
diff --git a/drivers/rtc/rtc-vr41xx.c b/drivers/rtc/rtc-vr41xx.c
index 596764fd29f..58e5ed0aa12 100644
--- a/drivers/rtc/rtc-vr41xx.c
+++ b/drivers/rtc/rtc-vr41xx.c
@@ -296,7 +296,7 @@ static irqreturn_t rtclong1_interrupt(int irq, void *dev_id, struct pt_regs *reg
return IRQ_HANDLED;
}
-static struct rtc_class_ops vr41xx_rtc_ops = {
+static const struct rtc_class_ops vr41xx_rtc_ops = {
.release = vr41xx_rtc_release,
.ioctl = vr41xx_rtc_ioctl,
.read_time = vr41xx_rtc_read_time,
diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c
index 788b6d1f8f2..522c69753bb 100644
--- a/drivers/rtc/rtc-x1205.c
+++ b/drivers/rtc/rtc-x1205.c
@@ -460,7 +460,7 @@ static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
return 0;
}
-static struct rtc_class_ops x1205_rtc_ops = {
+static const struct rtc_class_ops x1205_rtc_ops = {
.proc = x1205_rtc_proc,
.read_time = x1205_rtc_read_time,
.set_time = x1205_rtc_set_time,
diff --git a/drivers/s390/block/Kconfig b/drivers/s390/block/Kconfig
index 929d6fff615..b250c535450 100644
--- a/drivers/s390/block/Kconfig
+++ b/drivers/s390/block/Kconfig
@@ -1,4 +1,4 @@
-if S390
+if S390 && BLOCK
comment "S/390 block device drivers"
depends on S390
diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c
index 23fa0b28917..222a8a71a5e 100644
--- a/drivers/s390/block/dasd_diag.c
+++ b/drivers/s390/block/dasd_diag.c
@@ -63,44 +63,26 @@ static const u8 DASD_DIAG_CMS1[] = { 0xc3, 0xd4, 0xe2, 0xf1 };/* EBCDIC CMS1 */
* and function code cmd.
* In case of an exception return 3. Otherwise return result of bitwise OR of
* resulting condition code and DIAG return code. */
-static __inline__ int
-dia250(void *iob, int cmd)
+static inline int dia250(void *iob, int cmd)
{
+ register unsigned long reg0 asm ("0") = (unsigned long) iob;
typedef union {
struct dasd_diag_init_io init_io;
struct dasd_diag_rw_io rw_io;
} addr_type;
int rc;
- __asm__ __volatile__(
-#ifdef CONFIG_64BIT
- " lghi %0,3\n"
- " lgr 0,%3\n"
- " diag 0,%2,0x250\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- " or %0,1\n"
- "1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous\n"
-#else
- " lhi %0,3\n"
- " lr 0,%3\n"
+ rc = 3;
+ asm volatile(
" diag 0,%2,0x250\n"
"0: ipm %0\n"
" srl %0,28\n"
" or %0,1\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous\n"
-#endif
- : "=&d" (rc), "=m" (*(addr_type *) iob)
- : "d" (cmd), "d" (iob), "m" (*(addr_type *) iob)
- : "0", "1", "cc");
+ EX_TABLE(0b,1b)
+ : "+d" (rc), "=m" (*(addr_type *) iob)
+ : "d" (cmd), "d" (reg0), "m" (*(addr_type *) iob)
+ : "1", "cc");
return rc;
}
@@ -547,7 +529,7 @@ dasd_diag_build_cp(struct dasd_device * device, struct request *req)
}
cqr->retries = DIAG_MAX_RETRIES;
cqr->buildclk = get_clock();
- if (req->flags & REQ_FAILFAST)
+ if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->device = device;
cqr->expires = DIAG_TIMEOUT;
diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c
index b7a7fac3f7c..5ecea3e4fde 100644
--- a/drivers/s390/block/dasd_eckd.c
+++ b/drivers/s390/block/dasd_eckd.c
@@ -1266,7 +1266,7 @@ dasd_eckd_build_cp(struct dasd_device * device, struct request *req)
recid++;
}
}
- if (req->flags & REQ_FAILFAST)
+ if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->device = device;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c
index e85015be109..80926c54822 100644
--- a/drivers/s390/block/dasd_fba.c
+++ b/drivers/s390/block/dasd_fba.c
@@ -344,7 +344,7 @@ dasd_fba_build_cp(struct dasd_device * device, struct request *req)
recid++;
}
}
- if (req->flags & REQ_FAILFAST)
+ if (req->cmd_flags & REQ_FAILFAST)
set_bit(DASD_CQR_FLAGS_FAILFAST, &cqr->flags);
cqr->device = device;
cqr->expires = 5 * 60 * HZ; /* 5 minutes */
diff --git a/drivers/s390/block/xpram.c b/drivers/s390/block/xpram.c
index cab2c736683..a04d9120cef 100644
--- a/drivers/s390/block/xpram.c
+++ b/drivers/s390/block/xpram.c
@@ -89,28 +89,15 @@ MODULE_LICENSE("GPL");
*/
static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgin traps */
- " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22e0000,%1,%2\n" /* pgin %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
@@ -137,28 +124,15 @@ static int xpram_page_in (unsigned long page_addr, unsigned int xpage_index)
*/
static long xpram_page_out (unsigned long page_addr, unsigned int xpage_index)
{
- int cc;
+ int cc = 2; /* return unused cc 2 if pgin traps */
- __asm__ __volatile__ (
- " lhi %0,2\n" /* return unused cc 2 if pgout traps */
- " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " .insn rre,0xb22f0000,%1,%2\n" /* pgout %1,%2 */
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifndef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (cc)
- : "a" (__pa(page_addr)), "a" (xpage_index)
- : "cc" );
+ EX_TABLE(0b,1b)
+ : "+d" (cc) : "a" (__pa(page_addr)), "d" (xpage_index) : "cc");
if (cc == 3)
return -ENXIO;
if (cc == 2) {
diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c
index 2fa566fa6da..d7de175d53f 100644
--- a/drivers/s390/char/con3215.c
+++ b/drivers/s390/char/con3215.c
@@ -1103,7 +1103,7 @@ tty3215_start(struct tty_struct *tty)
}
}
-static struct tty_operations tty3215_ops = {
+static const struct tty_operations tty3215_ops = {
.open = tty3215_open,
.close = tty3215_close,
.write = tty3215_write,
diff --git a/drivers/s390/char/fs3270.c b/drivers/s390/char/fs3270.c
index ef004d08971..78f8bda81da 100644
--- a/drivers/s390/char/fs3270.c
+++ b/drivers/s390/char/fs3270.c
@@ -17,7 +17,6 @@
#include <asm/ccwdev.h>
#include <asm/cio.h>
-#include <asm/cpcmd.h>
#include <asm/ebcdic.h>
#include <asm/idals.h>
@@ -28,7 +27,7 @@ struct raw3270_fn fs3270_fn;
struct fs3270 {
struct raw3270_view view;
- pid_t fs_pid; /* Pid of controlling program. */
+ struct pid *fs_pid; /* Pid of controlling program. */
int read_command; /* ccw command to use for reads. */
int write_command; /* ccw command to use for writes. */
int attention; /* Got attention. */
@@ -103,7 +102,7 @@ fs3270_restore_callback(struct raw3270_request *rq, void *data)
fp = (struct fs3270 *) rq->view;
if (rq->rc != 0 || rq->rescnt != 0) {
if (fp->fs_pid)
- kill_proc(fp->fs_pid, SIGHUP, 1);
+ kill_pid(fp->fs_pid, SIGHUP, 1);
}
fp->rdbuf_size = 0;
raw3270_request_reset(rq);
@@ -174,7 +173,7 @@ fs3270_save_callback(struct raw3270_request *rq, void *data)
*/
if (rq->rc != 0 || rq->rescnt == 0) {
if (fp->fs_pid)
- kill_proc(fp->fs_pid, SIGHUP, 1);
+ kill_pid(fp->fs_pid, SIGHUP, 1);
fp->rdbuf_size = 0;
} else
fp->rdbuf_size = fp->rdbuf->size - rq->rescnt;
@@ -443,7 +442,7 @@ fs3270_open(struct inode *inode, struct file *filp)
return PTR_ERR(fp);
init_waitqueue_head(&fp->wait);
- fp->fs_pid = current->pid;
+ fp->fs_pid = get_pid(task_pid(current));
rc = raw3270_add_view(&fp->view, &fs3270_fn, minor);
if (rc) {
fs3270_free_view(&fp->view);
@@ -481,7 +480,8 @@ fs3270_close(struct inode *inode, struct file *filp)
fp = filp->private_data;
filp->private_data = NULL;
if (fp) {
- fp->fs_pid = 0;
+ put_pid(fp->fs_pid);
+ fp->fs_pid = NULL;
raw3270_reset(&fp->view);
raw3270_put_view(&fp->view);
raw3270_del_view(&fp->view);
diff --git a/drivers/s390/char/sclp.c b/drivers/s390/char/sclp.c
index 985d1613baa..31e335751d6 100644
--- a/drivers/s390/char/sclp.c
+++ b/drivers/s390/char/sclp.c
@@ -100,13 +100,12 @@ service_call(sclp_cmdw_t command, void *sccb)
{
int cc;
- __asm__ __volatile__(
- " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
- " ipm %0\n"
- " srl %0,28"
- : "=&d" (cc)
- : "d" (command), "a" (__pa(sccb))
- : "cc", "memory" );
+ asm volatile(
+ " .insn rre,0xb2200000,%1,%2\n" /* servc %1,%2 */
+ " ipm %0\n"
+ " srl %0,28"
+ : "=&d" (cc) : "d" (command), "a" (__pa(sccb))
+ : "cc", "memory");
if (cc == 3)
return -EIO;
if (cc == 2)
@@ -360,16 +359,6 @@ sclp_interrupt_handler(struct pt_regs *regs, __u16 code)
sclp_process_queue();
}
-/* Return current Time-Of-Day clock. */
-static inline u64
-sclp_get_clock(void)
-{
- u64 result;
-
- asm volatile ("STCK 0(%1)" : "=m" (result) : "a" (&(result)) : "cc");
- return result;
-}
-
/* Convert interval in jiffies to TOD ticks. */
static inline u64
sclp_tod_from_jiffies(unsigned long jiffies)
@@ -382,7 +371,6 @@ sclp_tod_from_jiffies(unsigned long jiffies)
void
sclp_sync_wait(void)
{
- unsigned long psw_mask;
unsigned long flags;
unsigned long cr0, cr0_sync;
u64 timeout;
@@ -392,7 +380,7 @@ sclp_sync_wait(void)
timeout = 0;
if (timer_pending(&sclp_request_timer)) {
/* Get timeout TOD value */
- timeout = sclp_get_clock() +
+ timeout = get_clock() +
sclp_tod_from_jiffies(sclp_request_timer.expires -
jiffies);
}
@@ -406,13 +394,12 @@ sclp_sync_wait(void)
cr0_sync |= 0x00000200;
cr0_sync &= 0xFFFFF3AC;
__ctl_load(cr0_sync, 0, 0);
- asm volatile ("STOSM 0(%1),0x01"
- : "=m" (psw_mask) : "a" (&psw_mask) : "memory");
+ __raw_local_irq_stosm(0x01);
/* Loop until driver state indicates finished request */
while (sclp_running_state != sclp_running_state_idle) {
/* Check for expired request timer */
if (timer_pending(&sclp_request_timer) &&
- sclp_get_clock() > timeout &&
+ get_clock() > timeout &&
del_timer(&sclp_request_timer))
sclp_request_timer.function(sclp_request_timer.data);
barrier();
diff --git a/drivers/s390/char/sclp_tty.c b/drivers/s390/char/sclp_tty.c
index f6cf9023039..6f43e04dbef 100644
--- a/drivers/s390/char/sclp_tty.c
+++ b/drivers/s390/char/sclp_tty.c
@@ -711,7 +711,7 @@ static struct sclp_register sclp_input_event =
.receiver_fn = sclp_tty_receiver
};
-static struct tty_operations sclp_ops = {
+static const struct tty_operations sclp_ops = {
.open = sclp_tty_open,
.close = sclp_tty_close,
.write = sclp_tty_write,
diff --git a/drivers/s390/char/sclp_vt220.c b/drivers/s390/char/sclp_vt220.c
index 54fba6f1718..723bf4191bf 100644
--- a/drivers/s390/char/sclp_vt220.c
+++ b/drivers/s390/char/sclp_vt220.c
@@ -655,7 +655,7 @@ __sclp_vt220_init(int early)
return 0;
}
-static struct tty_operations sclp_vt220_ops = {
+static const struct tty_operations sclp_vt220_ops = {
.open = sclp_vt220_open,
.close = sclp_vt220_close,
.write = sclp_vt220_write,
diff --git a/drivers/s390/char/tty3270.c b/drivers/s390/char/tty3270.c
index 29718042c6c..4717c361160 100644
--- a/drivers/s390/char/tty3270.c
+++ b/drivers/s390/char/tty3270.c
@@ -698,7 +698,6 @@ tty3270_alloc_view(void)
if (!tp->freemem_pages)
goto out_tp;
INIT_LIST_HEAD(&tp->freemem);
- init_timer(&tp->timer);
for (pages = 0; pages < TTY3270_STRING_PAGES; pages++) {
tp->freemem_pages[pages] = (void *)
__get_free_pages(GFP_KERNEL|GFP_DMA, 0);
@@ -1738,7 +1737,7 @@ tty3270_ioctl(struct tty_struct *tty, struct file *file,
return kbd_ioctl(tp->kbd, file, cmd, arg);
}
-static struct tty_operations tty3270_ops = {
+static const struct tty_operations tty3270_ops = {
.open = tty3270_open,
.close = tty3270_close,
.write = tty3270_write,
diff --git a/drivers/s390/char/vmwatchdog.c b/drivers/s390/char/vmwatchdog.c
index 807320a41fa..4b868f72fe8 100644
--- a/drivers/s390/char/vmwatchdog.c
+++ b/drivers/s390/char/vmwatchdog.c
@@ -54,48 +54,20 @@ enum vmwdt_func {
static int __diag288(enum vmwdt_func func, unsigned int timeout,
char *cmd, size_t len)
{
- register unsigned long __func asm("2");
- register unsigned long __timeout asm("3");
- register unsigned long __cmdp asm("4");
- register unsigned long __cmdl asm("5");
+ register unsigned long __func asm("2") = func;
+ register unsigned long __timeout asm("3") = timeout;
+ register unsigned long __cmdp asm("4") = virt_to_phys(cmd);
+ register unsigned long __cmdl asm("5") = len;
int err;
- __func = func;
- __timeout = timeout;
- __cmdp = virt_to_phys(cmd);
- __cmdl = len;
- err = 0;
- asm volatile (
-#ifdef CONFIG_64BIT
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lghi %0,%1\n"
- " jg 1b\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 1b,2b\n"
- ".previous\n"
-#else
- "diag %2,%4,0x288\n"
- "1: \n"
- ".section .fixup,\"ax\"\n"
- "2: lhi %0,%1\n"
- " bras 1,3f\n"
- " .long 1b\n"
- "3: l 1,0(1)\n"
- " br 1\n"
- ".previous\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 1b,2b\n"
- ".previous\n"
-#endif
- : "+&d"(err)
- : "i"(-EINVAL), "d"(__func), "d"(__timeout),
- "d"(__cmdp), "d"(__cmdl)
- : "1", "cc");
+ err = -EINVAL;
+ asm volatile(
+ " diag %1,%3,0x288\n"
+ "0: la %0,0\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (err) : "d"(__func), "d"(__timeout),
+ "d"(__cmdp), "d"(__cmdl), "0" (-EINVAL) : "1", "cc");
return err;
}
diff --git a/drivers/s390/cio/device_id.c b/drivers/s390/cio/device_id.c
index 438db483035..1398367b5f6 100644
--- a/drivers/s390/cio/device_id.c
+++ b/drivers/s390/cio/device_id.c
@@ -42,18 +42,15 @@ diag210(struct diag210 * addr)
spin_lock_irqsave(&diag210_lock, flags);
diag210_tmp = *addr;
- asm volatile (
- " lhi %0,-1\n"
- " sam31\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
- "1: sam64\n"
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory" );
+ asm volatile(
+ " lhi %0,-1\n"
+ " sam31\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1: sam64\n"
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(&diag210_tmp)) : "cc", "memory");
*addr = diag210_tmp;
spin_unlock_irqrestore(&diag210_lock, flags);
@@ -66,17 +63,14 @@ diag210(struct diag210 * addr)
{
int ccode;
- asm volatile (
- " lhi %0,-1\n"
- " diag %1,0,0x210\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " lhi %0,-1\n"
+ " diag %1,0,0x210\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
- : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory" );
+ EX_TABLE(0b,1b)
+ : "=&d" (ccode) : "a" (__pa(addr)) : "cc", "memory");
return ccode;
}
diff --git a/drivers/s390/cio/ioasm.h b/drivers/s390/cio/ioasm.h
index 95a9462f9a9..ad6d8294006 100644
--- a/drivers/s390/cio/ioasm.h
+++ b/drivers/s390/cio/ioasm.h
@@ -25,106 +25,74 @@ struct tpi_info {
static inline int stsch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " stsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int stsch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " stsch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " stsch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch(struct subchannel_id schid,
volatile struct schib *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " msch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " msch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int msch_err(struct subchannel_id schid,
volatile struct schib *addr)
{
- int ccode;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ int ccode = -EIO;
- __asm__ __volatile__(
- " lhi %0,%3\n"
- " lr 1,%1\n"
- " msch 0(%2)\n"
- "0: ipm %0\n"
- " srl %0,28\n"
+ asm volatile(
+ " msch 0(%2)\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
"1:\n"
-#ifdef CONFIG_64BIT
- ".section __ex_table,\"a\"\n"
- " .align 8\n"
- " .quad 0b,1b\n"
- ".previous"
-#else
- ".section __ex_table,\"a\"\n"
- " .align 4\n"
- " .long 0b,1b\n"
- ".previous"
-#endif
- : "=&d" (ccode)
- : "d" (schid), "a" (addr), "K" (-EIO), "m" (*addr)
- : "cc", "1" );
+ EX_TABLE(0b,1b)
+ : "+d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int tsch(struct subchannel_id schid,
volatile struct irb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " tsch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tsch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
@@ -132,89 +100,77 @@ static inline int tpi( volatile struct tpi_info *addr)
{
int ccode;
- __asm__ __volatile__(
- " tpi 0(%1)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " tpi 0(%1)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int ssch(struct subchannel_id schid,
volatile struct orb *addr)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " ssch 0(%2)\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid), "a" (addr), "m" (*addr)
- : "cc", "1" );
+ asm volatile(
+ " ssch 0(%2)\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1), "a" (addr), "m" (*addr) : "cc");
return ccode;
}
static inline int rsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " rsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int csch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " csch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " csch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int hsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " hsch\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " hsch\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
static inline int xsch(struct subchannel_id schid)
{
+ register struct subchannel_id reg1 asm ("1") = schid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " .insn rre,0xb2760000,%1,0\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (schid)
- : "cc", "1" );
+ asm volatile(
+ " .insn rre,0xb2760000,%1,0\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
@@ -223,41 +179,27 @@ static inline int chsc(void *chsc_area)
typedef struct { char _[4096]; } addr_type;
int cc;
- __asm__ __volatile__ (
- ".insn rre,0xb25f0000,%2,0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " .insn rre,0xb25f0000,%2,0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc), "=m" (*(addr_type *) chsc_area)
: "d" (chsc_area), "m" (*(addr_type *) chsc_area)
- : "cc" );
-
+ : "cc");
return cc;
}
-static inline int iac( void)
-{
- int ccode;
-
- __asm__ __volatile__(
- " iac 1\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode) : : "cc", "1" );
- return ccode;
-}
-
static inline int rchp(int chpid)
{
+ register unsigned int reg1 asm ("1") = chpid;
int ccode;
- __asm__ __volatile__(
- " lr 1,%1\n"
- " rchp\n"
- " ipm %0\n"
- " srl %0,28"
- : "=d" (ccode)
- : "d" (chpid)
- : "cc", "1" );
+ asm volatile(
+ " lr 1,%1\n"
+ " rchp\n"
+ " ipm %0\n"
+ " srl %0,28"
+ : "=d" (ccode) : "d" (reg1) : "cc");
return ccode;
}
diff --git a/drivers/s390/cio/qdio.h b/drivers/s390/cio/qdio.h
index 124569362f0..49bb9e371c3 100644
--- a/drivers/s390/cio/qdio.h
+++ b/drivers/s390/cio/qdio.h
@@ -274,12 +274,11 @@ do_sqbs(unsigned long sch, unsigned char state, int queue,
register unsigned long _sch asm ("1") = sch;
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
- asm volatile (
- " .insn rsy,0xeb000000008A,%1,0,0(%2)\n\t"
- : "+d" (_ccq), "+d" (_queuestart)
- : "d" ((unsigned long)state), "d" (_sch)
- : "memory", "cc"
- );
+ asm volatile(
+ " .insn rsy,0xeb000000008A,%1,0,0(%2)"
+ : "+d" (_ccq), "+d" (_queuestart)
+ : "d" ((unsigned long)state), "d" (_sch)
+ : "memory", "cc");
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
@@ -299,12 +298,11 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
unsigned long _queuestart = ((unsigned long)queue << 32) | *start;
unsigned long _state = 0;
- asm volatile (
- " .insn rrf,0xB99c0000,%1,%2,0,0 \n\t"
- : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
- : "d" (_sch)
- : "memory", "cc"
- );
+ asm volatile(
+ " .insn rrf,0xB99c0000,%1,%2,0,0"
+ : "+d" (_ccq), "+d" (_queuestart), "+d" (_state)
+ : "d" (_sch)
+ : "memory", "cc" );
*count = _ccq & 0xff;
*start = _queuestart & 0xff;
*state = _state & 0xff;
@@ -319,69 +317,35 @@ do_eqbs(unsigned long sch, unsigned char *state, int queue,
static inline int
do_siga_sync(struct subchannel_id schid, unsigned int mask1, unsigned int mask2)
{
+ register unsigned long reg0 asm ("0") = 2;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ register unsigned long reg2 asm ("2") = mask1;
+ register unsigned long reg3 asm ("3") = mask2;
int cc;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,2 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "lr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,2 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "llgfr 3,%3 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask1), "d" (mask2)
- : "cc", "0", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
+ : "d" (reg0), "d" (reg1), "d" (reg2), "d" (reg3) : "cc");
return cc;
}
static inline int
do_siga_input(struct subchannel_id schid, unsigned int mask)
{
+ register unsigned long reg0 asm ("0") = 1;
+ register struct subchannel_id reg1 asm ("1") = schid;
+ register unsigned long reg2 asm ("2") = mask;
int cc;
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,1 \n\t"
- "lr 1,%1 \n\t"
- "lr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- : "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 0,1 \n\t"
- "llgfr 1,%1 \n\t"
- "llgfr 2,%2 \n\t"
- "siga 0 \n\t"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
+ asm volatile(
+ " siga 0\n"
+ " ipm %0\n"
+ " srl %0,28\n"
: "=d" (cc)
- : "d" (schid), "d" (mask)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
+ : "d" (reg0), "d" (reg1), "d" (reg2) : "cc", "memory");
return cc;
}
@@ -389,93 +353,35 @@ static inline int
do_siga_output(unsigned long schid, unsigned long mask, __u32 *bb,
unsigned int fc)
{
+ register unsigned long __fc asm("0") = fc;
+ register unsigned long __schid asm("1") = schid;
+ register unsigned long __mask asm("2") = mask;
int cc;
- __u32 busy_bit;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 0,0 \n\t"
- "lr 1,%2 \n\t"
- "lr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "lr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "2: \n\t"
- "lhi %0,%4 \n\t"
- "bras 1,3f \n\t"
- ".long 1b \n\t"
- "3: \n\t"
- "l 1,0(1) \n\t"
- "br 1 \n\t"
- ".previous \n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 4 \n\t"
- ".long 0b,2b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
- : "cc", "0", "1", "2", "memory"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "llgfr 0,%5 \n\t"
- "lgr 1,%2 \n\t"
- "llgfr 2,%3 \n\t"
- "siga 0 \n\t"
- "0:"
- "ipm %0 \n\t"
- "srl %0,28 \n\t"
- "srl 0,31 \n\t"
- "llgfr %1,0 \n\t"
- "1: \n\t"
- ".section .fixup,\"ax\"\n\t"
- "lghi %0,%4 \n\t"
- "jg 1b \n\t"
- ".previous\n\t"
- ".section __ex_table,\"a\"\n\t"
- ".align 8 \n\t"
- ".quad 0b,1b \n\t"
- ".previous \n\t"
- : "=d" (cc), "=d" (busy_bit)
- : "d" (schid), "d" (mask),
- "i" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION), "d" (fc)
- : "cc", "0", "1", "2", "memory"
- );
-#endif /* CONFIG_64BIT */
-
- (*bb) = busy_bit;
+
+ asm volatile(
+ " siga 0\n"
+ "0: ipm %0\n"
+ " srl %0,28\n"
+ "1:\n"
+ EX_TABLE(0b,1b)
+ : "=d" (cc), "+d" (__fc), "+d" (__schid), "+d" (__mask)
+ : "0" (QDIO_SIGA_ERROR_ACCESS_EXCEPTION)
+ : "cc", "memory");
+ (*bb) = ((unsigned int) __fc) >> 31;
return cc;
}
static inline unsigned long
do_clear_global_summary(void)
{
-
- unsigned long time;
-
-#ifndef CONFIG_64BIT
- asm volatile (
- "lhi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#else /* CONFIG_64BIT */
- asm volatile (
- "lghi 1,3 \n\t"
- ".insn rre,0xb2650000,2,0 \n\t"
- "lgr %0,3 \n\t"
- : "=d" (time) : : "cc", "1", "2", "3"
- );
-#endif /* CONFIG_64BIT */
-
- return time;
+ register unsigned long __fn asm("1") = 3;
+ register unsigned long __tmp asm("2");
+ register unsigned long __time asm("3");
+
+ asm volatile(
+ " .insn rre,0xb2650000,2,0"
+ : "+d" (__fn), "=d" (__tmp), "=d" (__time));
+ return __time;
}
/*
diff --git a/drivers/s390/net/iucv.c b/drivers/s390/net/iucv.c
index 821dde86e24..809dd8d7f47 100644
--- a/drivers/s390/net/iucv.c
+++ b/drivers/s390/net/iucv.c
@@ -534,19 +534,15 @@ iucv_add_handler (handler *new)
*
* Returns: return code from CP's IUCV call
*/
-static __inline__ ulong
-b2f0(__u32 code, void *parm)
+static inline ulong b2f0(__u32 code, void *parm)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iucv_dumpit("iparml before b2f0 call:", parm, sizeof(iucv_param));
- asm volatile (
- "LRA 1,0(%1)\n\t"
- "LR 0,%0\n\t"
- ".long 0xb2f01000"
- :
- : "d" (code), "a" (parm)
- : "0", "1"
- );
+ reg0 = code;
+ reg1 = virt_to_phys(parm);
+ asm volatile(".long 0xb2f01000" : : "d" (reg0), "a" (reg1));
iucv_dumpit("iparml after b2f0 call:", parm, sizeof(iucv_param));
@@ -1248,6 +1244,8 @@ iucv_purge (__u16 pathid, __u32 msgid, __u32 srccls, __u32 *audit)
static int
iucv_query_generic(int want_maxconn)
{
+ register unsigned long reg0 asm ("0");
+ register unsigned long reg1 asm ("1");
iparml_purge *parm = (iparml_purge *)grab_param();
int bufsize, maxconn;
int ccode;
@@ -1256,18 +1254,15 @@ iucv_query_generic(int want_maxconn)
* Call b2f0 and store R0 (max buffer size),
* R1 (max connections) and CC.
*/
- asm volatile (
- "LRA 1,0(%4)\n\t"
- "LR 0,%3\n\t"
- ".long 0xb2f01000\n\t"
- "IPM %0\n\t"
- "SRL %0,28\n\t"
- "ST 0,%1\n\t"
- "ST 1,%2\n\t"
- : "=d" (ccode), "=m" (bufsize), "=m" (maxconn)
- : "d" (QUERY), "a" (parm)
- : "0", "1", "cc"
- );
+ reg0 = QUERY;
+ reg1 = virt_to_phys(parm);
+ asm volatile(
+ " .long 0xb2f01000\n"
+ " ipm %0\n"
+ " srl %0,28\n"
+ : "=d" (ccode), "+d" (reg0), "+d" (reg1) : : "cc");
+ bufsize = reg0;
+ maxconn = reg1;
release_param(parm);
if (ccode)
diff --git a/drivers/s390/net/qeth_main.c b/drivers/s390/net/qeth_main.c
index 5613b4564fa..8364d5475ac 100644
--- a/drivers/s390/net/qeth_main.c
+++ b/drivers/s390/net/qeth_main.c
@@ -8067,7 +8067,7 @@ qeth_arp_constructor(struct neighbour *neigh)
neigh->parms = neigh_parms_clone(parms);
rcu_read_unlock();
- neigh->type = inet_addr_type(*(u32 *) neigh->primary_key);
+ neigh->type = inet_addr_type(*(__be32 *) neigh->primary_key);
neigh->nud_state = NUD_NOARP;
neigh->ops = arp_direct_ops;
neigh->output = neigh->ops->queue_xmit;
diff --git a/drivers/s390/s390mach.c b/drivers/s390/s390mach.c
index a914129a4da..e088b5e2871 100644
--- a/drivers/s390/s390mach.c
+++ b/drivers/s390/s390mach.c
@@ -208,7 +208,7 @@ s390_handle_mcck(void)
*/
__ctl_clear_bit(14, 24); /* Disable WARNING MCH */
if (xchg(&mchchk_wng_posted, 1) == 0)
- kill_proc(1, SIGPWR, 1);
+ kill_cad_pid(SIGPWR, 1);
}
#endif
@@ -253,11 +253,12 @@ s390_revalidate_registers(struct mci *mci)
kill_task = 1;
#ifndef CONFIG_64BIT
- asm volatile("ld 0,0(%0)\n"
- "ld 2,8(%0)\n"
- "ld 4,16(%0)\n"
- "ld 6,24(%0)"
- : : "a" (&S390_lowcore.floating_pt_save_area));
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 2,8(%0)\n"
+ " ld 4,16(%0)\n"
+ " ld 6,24(%0)"
+ : : "a" (&S390_lowcore.floating_pt_save_area));
#endif
if (MACHINE_HAS_IEEE) {
@@ -274,37 +275,36 @@ s390_revalidate_registers(struct mci *mci)
* Floating point control register can't be restored.
* Task will be terminated.
*/
- asm volatile ("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
+ asm volatile("lfpc 0(%0)" : : "a" (&zero), "m" (zero));
kill_task = 1;
- }
- else
- asm volatile (
- "lfpc 0(%0)"
- : : "a" (fpt_creg_save_area));
-
- asm volatile("ld 0,0(%0)\n"
- "ld 1,8(%0)\n"
- "ld 2,16(%0)\n"
- "ld 3,24(%0)\n"
- "ld 4,32(%0)\n"
- "ld 5,40(%0)\n"
- "ld 6,48(%0)\n"
- "ld 7,56(%0)\n"
- "ld 8,64(%0)\n"
- "ld 9,72(%0)\n"
- "ld 10,80(%0)\n"
- "ld 11,88(%0)\n"
- "ld 12,96(%0)\n"
- "ld 13,104(%0)\n"
- "ld 14,112(%0)\n"
- "ld 15,120(%0)\n"
- : : "a" (fpt_save_area));
+ } else
+ asm volatile("lfpc 0(%0)" : : "a" (fpt_creg_save_area));
+
+ asm volatile(
+ " ld 0,0(%0)\n"
+ " ld 1,8(%0)\n"
+ " ld 2,16(%0)\n"
+ " ld 3,24(%0)\n"
+ " ld 4,32(%0)\n"
+ " ld 5,40(%0)\n"
+ " ld 6,48(%0)\n"
+ " ld 7,56(%0)\n"
+ " ld 8,64(%0)\n"
+ " ld 9,72(%0)\n"
+ " ld 10,80(%0)\n"
+ " ld 11,88(%0)\n"
+ " ld 12,96(%0)\n"
+ " ld 13,104(%0)\n"
+ " ld 14,112(%0)\n"
+ " ld 15,120(%0)\n"
+ : : "a" (fpt_save_area));
}
/* Revalidate access registers */
- asm volatile("lam 0,15,0(%0)"
- : : "a" (&S390_lowcore.access_regs_save_area));
+ asm volatile(
+ " lam 0,15,0(%0)"
+ : : "a" (&S390_lowcore.access_regs_save_area));
if (!mci->ar)
/*
* Access registers have unknown contents.
@@ -321,11 +321,13 @@ s390_revalidate_registers(struct mci *mci)
s390_handle_damage("invalid control registers.");
else
#ifdef CONFIG_64BIT
- asm volatile("lctlg 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctlg 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#else
- asm volatile("lctl 0,15,0(%0)"
- : : "a" (&S390_lowcore.cregs_save_area));
+ asm volatile(
+ " lctl 0,15,0(%0)"
+ : : "a" (&S390_lowcore.cregs_save_area));
#endif
/*
@@ -339,20 +341,23 @@ s390_revalidate_registers(struct mci *mci)
* old contents (should be zero) otherwise set it to zero.
*/
if (!mci->pr)
- asm volatile("sr 0,0\n"
- "sckpf"
- : : : "0", "cc");
+ asm volatile(
+ " sr 0,0\n"
+ " sckpf"
+ : : : "0", "cc");
else
asm volatile(
- "l 0,0(%0)\n"
- "sckpf"
- : : "a" (&S390_lowcore.tod_progreg_save_area) : "0", "cc");
+ " l 0,0(%0)\n"
+ " sckpf"
+ : : "a" (&S390_lowcore.tod_progreg_save_area)
+ : "0", "cc");
#endif
/* Revalidate clock comparator register */
- asm volatile ("stck 0(%1)\n"
- "sckc 0(%1)"
- : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
+ asm volatile(
+ " stck 0(%1)\n"
+ " sckc 0(%1)"
+ : "=m" (tmpclock) : "a" (&(tmpclock)) : "cc", "memory");
/* Check if old PSW is valid */
if (!mci->wp)
diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c
index 7cafa34e4c7..4d2bc798132 100644
--- a/drivers/s390/scsi/zfcp_scsi.c
+++ b/drivers/s390/scsi/zfcp_scsi.c
@@ -301,7 +301,7 @@ zfcp_scsi_command_sync(struct zfcp_unit *unit, struct scsi_cmnd *scpnt,
int use_timer)
{
int ret;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
scpnt->SCp.ptr = (void *) &wait; /* silent re-use */
scpnt->scsi_done = zfcp_scsi_command_sync_handler;
diff --git a/drivers/sbus/char/aurora.c b/drivers/sbus/char/aurora.c
index 4fdb2c93221..a305d409154 100644
--- a/drivers/sbus/char/aurora.c
+++ b/drivers/sbus/char/aurora.c
@@ -2187,7 +2187,7 @@ static void do_softint(void *private_)
#endif
}
-static struct tty_operations aurora_ops = {
+static const struct tty_operations aurora_ops = {
.open = aurora_open,
.close = aurora_close,
.write = aurora_write,
diff --git a/drivers/sbus/char/bbc_envctrl.c b/drivers/sbus/char/bbc_envctrl.c
index 1cc706e1111..d27e4f6d704 100644
--- a/drivers/sbus/char/bbc_envctrl.c
+++ b/drivers/sbus/char/bbc_envctrl.c
@@ -4,9 +4,6 @@
* Copyright (C) 2001 David S. Miller (davem@redhat.com)
*/
-#define __KERNEL_SYSCALLS__
-static int errno;
-
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/sched.h>
@@ -200,7 +197,7 @@ static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp)
printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n");
shutting_down = 1;
- if (execve("/sbin/shutdown", argv, envp) < 0)
+ if (kernel_execve("/sbin/shutdown", argv, envp) < 0)
printk(KERN_CRIT "envctrl: shutdown execution failed\n");
}
diff --git a/drivers/sbus/char/envctrl.c b/drivers/sbus/char/envctrl.c
index 063e676a3ac..728a133d0fc 100644
--- a/drivers/sbus/char/envctrl.c
+++ b/drivers/sbus/char/envctrl.c
@@ -19,9 +19,6 @@
* Daniele Bellucci <bellucda@tiscali.it>
*/
-#define __KERNEL_SYSCALLS__
-static int errno;
-
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
@@ -976,13 +973,15 @@ static void envctrl_do_shutdown(void)
"HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
char *argv[] = {
"/sbin/shutdown", "-h", "now", NULL };
+ int ret;
if (inprog != 0)
return;
inprog = 1;
printk(KERN_CRIT "kenvctrld: WARNING: Shutting down the system now.\n");
- if (0 > execve("/sbin/shutdown", argv, envp)) {
+ ret = kernel_execve("/sbin/shutdown", argv, envp);
+ if (ret < 0) {
printk(KERN_CRIT "kenvctrld: WARNING: system shutdown failed!\n");
inprog = 0; /* unlikely to succeed, but we could try again */
}
diff --git a/drivers/scsi/53c700.c b/drivers/scsi/53c700.c
index 657a3ab7539..15ce40a7053 100644
--- a/drivers/scsi/53c700.c
+++ b/drivers/scsi/53c700.c
@@ -1939,7 +1939,7 @@ NCR_700_abort(struct scsi_cmnd * SCp)
STATIC int
NCR_700_bus_reset(struct scsi_cmnd * SCp)
{
- DECLARE_COMPLETION(complete);
+ DECLARE_COMPLETION_ONSTACK(complete);
struct NCR_700_Host_Parameters *hostdata =
(struct NCR_700_Host_Parameters *)SCp->device->host->hostdata[0];
diff --git a/drivers/scsi/BusLogic.h b/drivers/scsi/BusLogic.h
index 9792e5af525..d6d1d5613c8 100644
--- a/drivers/scsi/BusLogic.h
+++ b/drivers/scsi/BusLogic.h
@@ -237,10 +237,7 @@ enum BusLogic_BIOS_DiskGeometryTranslation {
Define a Boolean data type.
*/
-typedef enum {
- false,
- true
-} PACKED boolean;
+typedef bool boolean;
/*
Define a 10^18 Statistics Byte Counter data type.
diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index c4dfcc91ddd..dab082002e6 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -3,11 +3,13 @@ menu "SCSI device support"
config RAID_ATTRS
tristate "RAID Transport Class"
default n
+ depends on BLOCK
---help---
Provides RAID
config SCSI
tristate "SCSI device support"
+ depends on BLOCK
---help---
If you want to use a SCSI hard disk, SCSI tape drive, SCSI CD-ROM or
any other SCSI device under Linux, say Y and make sure that you know
diff --git a/drivers/scsi/aha1740.c b/drivers/scsi/aha1740.c
index 0e4a7ebe300..6b35ed8301e 100644
--- a/drivers/scsi/aha1740.c
+++ b/drivers/scsi/aha1740.c
@@ -681,6 +681,7 @@ static struct eisa_device_id aha1740_ids[] = {
{ "ADP0400" }, /* 1744 */
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, aha1740_ids);
static struct eisa_driver aha1740_driver = {
.id_table = aha1740_ids,
diff --git a/drivers/scsi/aic7xxx/aic7770_osm.c b/drivers/scsi/aic7xxx/aic7770_osm.c
index 867cbe23579..1ac119733ba 100644
--- a/drivers/scsi/aic7xxx/aic7770_osm.c
+++ b/drivers/scsi/aic7xxx/aic7770_osm.c
@@ -132,7 +132,8 @@ static struct eisa_device_id aic7770_ids[] = {
{ "ADP7770", 5 }, /* AIC7770 generic */
{ "" }
};
-
+MODULE_DEVICE_TABLE(eisa, aic7770_ids);
+
static struct eisa_driver aic7770_driver = {
.id_table = aic7770_ids,
.driver = {
diff --git a/drivers/scsi/aic7xxx/aic79xx_osm.c b/drivers/scsi/aic7xxx/aic79xx_osm.c
index c7eeaced324..1faa008b5b8 100644
--- a/drivers/scsi/aic7xxx/aic79xx_osm.c
+++ b/drivers/scsi/aic7xxx/aic79xx_osm.c
@@ -646,7 +646,7 @@ ahd_linux_dev_reset(struct scsi_cmnd *cmd)
struct ahd_initiator_tinfo *tinfo;
struct ahd_tmode_tstate *tstate;
unsigned long flags;
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
reset_scb = NULL;
paused = FALSE;
@@ -2251,7 +2251,7 @@ done:
if (paused)
ahd_unpause(ahd);
if (wait) {
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
ahd->platform_data->eh_done = &done;
ahd_unlock(ahd, &flags);
diff --git a/drivers/scsi/aic7xxx/aic7xxx_osm.c b/drivers/scsi/aic7xxx/aic7xxx_osm.c
index 64c8b88a429..339b85cb61c 100644
--- a/drivers/scsi/aic7xxx/aic7xxx_osm.c
+++ b/drivers/scsi/aic7xxx/aic7xxx_osm.c
@@ -2335,7 +2335,7 @@ done:
if (paused)
ahc_unpause(ahc);
if (wait) {
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
ahc->platform_data->eh_done = &done;
ahc_unlock(ahc, &flags);
diff --git a/drivers/scsi/aic7xxx_old.c b/drivers/scsi/aic7xxx_old.c
index 5dcef48d414..10353379a07 100644
--- a/drivers/scsi/aic7xxx_old.c
+++ b/drivers/scsi/aic7xxx_old.c
@@ -2862,7 +2862,7 @@ aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
aic_dev->r_total++;
ptr = aic_dev->r_bins;
}
- if(cmd->device->simple_tags && cmd->request->flags & REQ_HARDBARRIER)
+ if(cmd->device->simple_tags && cmd->request->cmd_flags & REQ_HARDBARRIER)
{
aic_dev->barrier_total++;
if(scb->tag_action == MSG_ORDERED_Q_TAG)
@@ -10158,7 +10158,7 @@ aic7xxx_buildscb(struct aic7xxx_host *p, Scsi_Cmnd *cmd,
/* We always force TEST_UNIT_READY to untagged */
if (cmd->cmnd[0] != TEST_UNIT_READY && sdptr->simple_tags)
{
- if (req->flags & REQ_HARDBARRIER)
+ if (req->cmd_flags & REQ_HARDBARRIER)
{
if(sdptr->ordered_tags)
{
diff --git a/drivers/scsi/gdth.c b/drivers/scsi/gdth.c
index 43afd476e60..0f3eb22b979 100644
--- a/drivers/scsi/gdth.c
+++ b/drivers/scsi/gdth.c
@@ -724,7 +724,7 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd,
int timeout, u32 *info)
{
Scsi_Cmnd *scp;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
int rval;
scp = kmalloc(sizeof(*scp), GFP_KERNEL);
@@ -764,7 +764,7 @@ int __gdth_execute(struct scsi_device *sdev, gdth_cmd_str *gdtcmd, char *cmnd,
{
Scsi_Cmnd *scp = scsi_allocate_device(sdev, 1, FALSE);
unsigned bufflen = gdtcmd ? sizeof(gdth_cmd_str) : 0;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
int rval;
if (!scp)
diff --git a/drivers/scsi/ide-scsi.c b/drivers/scsi/ide-scsi.c
index 94d1de55607..1427a41e844 100644
--- a/drivers/scsi/ide-scsi.c
+++ b/drivers/scsi/ide-scsi.c
@@ -344,7 +344,7 @@ static int idescsi_check_condition(ide_drive_t *drive, struct request *failed_co
pc->buffer = buf;
pc->c[0] = REQUEST_SENSE;
pc->c[4] = pc->request_transfer = pc->buffer_size = SCSI_SENSE_BUFFERSIZE;
- rq->flags = REQ_SENSE;
+ rq->cmd_type = REQ_TYPE_SENSE;
pc->timeout = jiffies + WAIT_READY;
/* NOTE! Save the failed packet command in "rq->buffer" */
rq->buffer = (void *) failed_command->special;
@@ -398,12 +398,12 @@ static int idescsi_end_request (ide_drive_t *drive, int uptodate, int nrsecs)
int errors = rq->errors;
unsigned long flags;
- if (!(rq->flags & (REQ_SPECIAL|REQ_SENSE))) {
+ if (!blk_special_request(rq) && !blk_sense_request(rq)) {
ide_end_request(drive, uptodate, nrsecs);
return 0;
}
ide_end_drive_cmd (drive, 0, 0);
- if (rq->flags & REQ_SENSE) {
+ if (blk_sense_request(rq)) {
idescsi_pc_t *opc = (idescsi_pc_t *) rq->buffer;
if (log) {
printk ("ide-scsi: %s: wrap up check %lu, rst = ", drive->name, opc->scsi_cmd->serial_number);
@@ -708,11 +708,11 @@ static ide_startstop_t idescsi_issue_pc (ide_drive_t *drive, idescsi_pc_t *pc)
static ide_startstop_t idescsi_do_request (ide_drive_t *drive, struct request *rq, sector_t block)
{
#if IDESCSI_DEBUG_LOG
- printk (KERN_INFO "rq_status: %d, dev: %s, cmd: %x, errors: %d\n",rq->rq_status, rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
+ printk (KERN_INFO "dev: %s, cmd: %x, errors: %d\n", rq->rq_disk->disk_name,rq->cmd[0],rq->errors);
printk (KERN_INFO "sector: %ld, nr_sectors: %ld, current_nr_sectors: %d\n",rq->sector,rq->nr_sectors,rq->current_nr_sectors);
#endif /* IDESCSI_DEBUG_LOG */
- if (rq->flags & (REQ_SPECIAL|REQ_SENSE)) {
+ if (blk_sense_request(rq) || blk_special_request(rq)) {
return idescsi_issue_pc (drive, (idescsi_pc_t *) rq->special);
}
blk_dump_rq_flags(rq, "ide-scsi: unsup command");
@@ -938,7 +938,7 @@ static int idescsi_queue (struct scsi_cmnd *cmd,
ide_init_drive_cmd (rq);
rq->special = (char *) pc;
- rq->flags = REQ_SPECIAL;
+ rq->cmd_type = REQ_TYPE_SPECIAL;
spin_unlock_irq(host->host_lock);
rq->rq_disk = scsi->disk;
(void) ide_do_drive_cmd (drive, rq, ide_end);
@@ -992,7 +992,7 @@ static int idescsi_eh_abort (struct scsi_cmnd *cmd)
*/
printk (KERN_ERR "ide-scsi: cmd aborted!\n");
- if (scsi->pc->rq->flags & REQ_SENSE)
+ if (blk_sense_request(scsi->pc->rq))
kfree(scsi->pc->buffer);
kfree(scsi->pc->rq);
kfree(scsi->pc);
@@ -1042,7 +1042,7 @@ static int idescsi_eh_reset (struct scsi_cmnd *cmd)
/* kill current request */
blkdev_dequeue_request(req);
end_that_request_last(req, 0);
- if (req->flags & REQ_SENSE)
+ if (blk_sense_request(req))
kfree(scsi->pc->buffer);
kfree(scsi->pc);
scsi->pc = NULL;
diff --git a/drivers/scsi/libsas/sas_scsi_host.c b/drivers/scsi/libsas/sas_scsi_host.c
index 7f9e89bcac7..e46e79355b7 100644
--- a/drivers/scsi/libsas/sas_scsi_host.c
+++ b/drivers/scsi/libsas/sas_scsi_host.c
@@ -126,7 +126,7 @@ static enum task_attribute sas_scsi_get_task_attr(struct scsi_cmnd *cmd)
enum task_attribute ta = TASK_ATTR_SIMPLE;
if (cmd->request && blk_rq_tagged(cmd->request)) {
if (cmd->device->ordered_tags &&
- (cmd->request->flags & REQ_HARDBARRIER))
+ (cmd->request->cmd_flags & REQ_HARDBARRIER))
ta = TASK_ATTR_HOQ;
}
return ta;
diff --git a/drivers/scsi/lpfc/lpfc_ct.c b/drivers/scsi/lpfc/lpfc_ct.c
index ae410645899..1b53afb1cb5 100644
--- a/drivers/scsi/lpfc/lpfc_ct.c
+++ b/drivers/scsi/lpfc/lpfc_ct.c
@@ -933,8 +933,8 @@ lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
ae = (ATTRIBUTE_ENTRY *) ((uint8_t *) rh + size);
ae->ad.bits.AttrType = be16_to_cpu(OS_NAME_VERSION);
sprintf(ae->un.OsNameVersion, "%s %s %s",
- system_utsname.sysname, system_utsname.release,
- system_utsname.version);
+ init_utsname()->sysname, init_utsname()->release,
+ init_utsname()->version);
len = strlen(ae->un.OsNameVersion);
len += (len & 3) ? (4 - (len & 3)) : 4;
ae->ad.bits.AttrLen = be16_to_cpu(FOURBYTES + len);
@@ -1052,7 +1052,7 @@ lpfc_fdmi_cmd(struct lpfc_hba * phba, struct lpfc_nodelist * ndlp, int cmdcode)
size);
ae->ad.bits.AttrType = be16_to_cpu(HOST_NAME);
sprintf(ae->un.HostName, "%s",
- system_utsname.nodename);
+ init_utsname()->nodename);
len = strlen(ae->un.HostName);
len += (len & 3) ? (4 - (len & 3)) : 4;
ae->ad.bits.AttrLen =
@@ -1140,7 +1140,7 @@ lpfc_fdmi_tmo_handler(struct lpfc_hba *phba)
ndlp = lpfc_findnode_did(phba, NLP_SEARCH_ALL, FDMI_DID);
if (ndlp) {
- if (system_utsname.nodename[0] != '\0') {
+ if (init_utsname()->nodename[0] != '\0') {
lpfc_fdmi_cmd(phba, ndlp, SLI_MGMT_DHBA);
} else {
mod_timer(&phba->fc_fdmitmo, jiffies + HZ * 60);
diff --git a/drivers/scsi/mesh.c b/drivers/scsi/mesh.c
index 592b52afe65..683fc7ae4b8 100644
--- a/drivers/scsi/mesh.c
+++ b/drivers/scsi/mesh.c
@@ -1756,16 +1756,23 @@ static void set_mesh_power(struct mesh_state *ms, int state)
pmac_call_feature(PMAC_FTR_MESH_ENABLE, macio_get_of_node(ms->mdev), 0, 0);
msleep(10);
}
-}
+}
#ifdef CONFIG_PM
-static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
+static int mesh_suspend(struct macio_dev *mdev, pm_message_t mesg)
{
struct mesh_state *ms = (struct mesh_state *)macio_get_drvdata(mdev);
unsigned long flags;
- if (state.event == mdev->ofdev.dev.power.power_state.event || state.event < 2)
+ switch (mesg.event) {
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_FREEZE:
+ break;
+ default:
+ return 0;
+ }
+ if (mesg.event == mdev->ofdev.dev.power.power_state.event)
return 0;
scsi_block_requests(ms->host);
@@ -1780,7 +1787,7 @@ static int mesh_suspend(struct macio_dev *mdev, pm_message_t state)
disable_irq(ms->meshintr);
set_mesh_power(ms, 0);
- mdev->ofdev.dev.power.power_state = state;
+ mdev->ofdev.dev.power.power_state = mesg;
return 0;
}
diff --git a/drivers/scsi/pluto.c b/drivers/scsi/pluto.c
index 0bd9c60e645..aa60a5f1fbc 100644
--- a/drivers/scsi/pluto.c
+++ b/drivers/scsi/pluto.c
@@ -67,7 +67,6 @@ static void __init pluto_detect_done(Scsi_Cmnd *SCpnt)
static void __init pluto_detect_scsi_done(Scsi_Cmnd *SCpnt)
{
- SCpnt->request->rq_status = RQ_SCSI_DONE;
PLND(("Detect done %08lx\n", (long)SCpnt))
if (atomic_dec_and_test (&fcss))
up(&fc_sem);
@@ -166,7 +165,7 @@ int __init pluto_detect(struct scsi_host_template *tpnt)
SCpnt->cmd_len = COMMAND_SIZE(INQUIRY);
- SCpnt->request->rq_status = RQ_SCSI_BUSY;
+ SCpnt->request->cmd_flags &= ~REQ_STARTED;
SCpnt->done = pluto_detect_done;
SCpnt->request_bufflen = 256;
@@ -178,7 +177,8 @@ int __init pluto_detect(struct scsi_host_template *tpnt)
for (retry = 0; retry < 5; retry++) {
for (i = 0; i < fcscount; i++) {
if (!fcs[i].fc) break;
- if (fcs[i].cmd.request->rq_status != RQ_SCSI_DONE) {
+ if (!(fcs[i].cmd.request->cmd_flags & REQ_STARTED)) {
+ fcs[i].cmd.request->cmd_flags |= REQ_STARTED;
disable_irq(fcs[i].fc->irq);
PLND(("queuecommand %d %d\n", retry, i))
fcp_scsi_queuecommand (&(fcs[i].cmd),
diff --git a/drivers/scsi/qla1280.c b/drivers/scsi/qla1280.c
index 8953991462d..332151e2a01 100644
--- a/drivers/scsi/qla1280.c
+++ b/drivers/scsi/qla1280.c
@@ -813,7 +813,7 @@ qla1280_error_action(struct scsi_cmnd *cmd, enum action action)
uint16_t data;
unsigned char *handle;
int result, i;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
struct timer_list timer;
ha = (struct scsi_qla_host *)(CMD_HOST(cmd)->hostdata);
@@ -2406,7 +2406,7 @@ qla1280_mailbox_command(struct scsi_qla_host *ha, uint8_t mr, uint16_t *mb)
uint16_t *optr, *iptr;
uint16_t __iomem *mptr;
uint16_t data;
- DECLARE_COMPLETION(wait);
+ DECLARE_COMPLETION_ONSTACK(wait);
struct timer_list timer;
ENTER("qla1280_mailbox_command");
diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c
index 7a054f9d1ee..da95bce907d 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -592,12 +592,6 @@ int scsi_dispatch_cmd(struct scsi_cmnd *cmd)
return rtn;
}
-
-/*
- * Per-CPU I/O completion queue.
- */
-static DEFINE_PER_CPU(struct list_head, scsi_done_q);
-
/**
* scsi_req_abort_cmd -- Request command recovery for the specified command
* cmd: pointer to the SCSI command of interest
@@ -1065,7 +1059,7 @@ int scsi_device_cancel(struct scsi_device *sdev, int recovery)
spin_lock_irqsave(&sdev->list_lock, flags);
list_for_each_entry(scmd, &sdev->cmd_list, list) {
- if (scmd->request && scmd->request->rq_status != RQ_INACTIVE) {
+ if (scmd->request) {
/*
* If we are unable to remove the timer, it means
* that the command has already timed out or
@@ -1102,7 +1096,7 @@ MODULE_PARM_DESC(scsi_logging_level, "a bit mask of logging levels");
static int __init init_scsi(void)
{
- int error, i;
+ int error;
error = scsi_init_queue();
if (error)
@@ -1123,9 +1117,6 @@ static int __init init_scsi(void)
if (error)
goto cleanup_sysctl;
- for_each_possible_cpu(i)
- INIT_LIST_HEAD(&per_cpu(scsi_done_q, i));
-
scsi_netlink_init();
printk(KERN_NOTICE "SCSI subsystem initialized\n");
diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index d6743b959a7..71084728eb4 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -82,7 +82,7 @@ static void scsi_unprep_request(struct request *req)
{
struct scsi_cmnd *cmd = req->special;
- req->flags &= ~REQ_DONTPREP;
+ req->cmd_flags &= ~REQ_DONTPREP;
req->special = NULL;
scsi_put_command(cmd);
@@ -196,7 +196,8 @@ int scsi_execute(struct scsi_device *sdev, const unsigned char *cmd,
req->sense_len = 0;
req->retries = retries;
req->timeout = timeout;
- req->flags |= flags | REQ_BLOCK_PC | REQ_SPECIAL | REQ_QUIET;
+ req->cmd_type = REQ_TYPE_BLOCK_PC;
+ req->cmd_flags |= flags | REQ_QUIET | REQ_PREEMPT;
/*
* head injection *required* here otherwise quiesce won't work
@@ -397,7 +398,8 @@ int scsi_execute_async(struct scsi_device *sdev, const unsigned char *cmd,
req = blk_get_request(sdev->request_queue, write, gfp);
if (!req)
goto free_sense;
- req->flags |= REQ_BLOCK_PC | REQ_QUIET;
+ req->cmd_type = REQ_TYPE_BLOCK_PC;
+ req->cmd_flags |= REQ_QUIET;
if (use_sg)
err = scsi_req_map_sg(req, buffer, use_sg, bufflen, gfp);
@@ -933,7 +935,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
break;
}
}
- if (!(req->flags & REQ_QUIET)) {
+ if (!(req->cmd_flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
"Device not ready: ");
scsi_print_sense_hdr("", &sshdr);
@@ -941,7 +943,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
scsi_end_request(cmd, 0, this_count, 1);
return;
case VOLUME_OVERFLOW:
- if (!(req->flags & REQ_QUIET)) {
+ if (!(req->cmd_flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
"Volume overflow, CDB: ");
__scsi_print_command(cmd->cmnd);
@@ -963,7 +965,7 @@ void scsi_io_completion(struct scsi_cmnd *cmd, unsigned int good_bytes)
return;
}
if (result) {
- if (!(req->flags & REQ_QUIET)) {
+ if (!(req->cmd_flags & REQ_QUIET)) {
scmd_printk(KERN_INFO, cmd,
"SCSI error: return code = 0x%08x\n",
result);
@@ -995,7 +997,7 @@ static int scsi_init_io(struct scsi_cmnd *cmd)
/*
* if this is a rq->data based REQ_BLOCK_PC, setup for a non-sg xfer
*/
- if ((req->flags & REQ_BLOCK_PC) && !req->bio) {
+ if (blk_pc_request(req) && !req->bio) {
cmd->request_bufflen = req->data_len;
cmd->request_buffer = req->data;
req->buffer = req->data;
@@ -1139,13 +1141,12 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req)
* these two cases differently. We differentiate by looking
* at request->cmd, as this tells us the real story.
*/
- if (req->flags & REQ_SPECIAL && req->special) {
+ if (blk_special_request(req) && req->special)
cmd = req->special;
- } else if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
-
- if(unlikely(specials_only) && !(req->flags & REQ_SPECIAL)) {
- if(specials_only == SDEV_QUIESCE ||
- specials_only == SDEV_BLOCK)
+ else if (blk_pc_request(req) || blk_fs_request(req)) {
+ if (unlikely(specials_only) && !(req->cmd_flags & REQ_PREEMPT)){
+ if (specials_only == SDEV_QUIESCE ||
+ specials_only == SDEV_BLOCK)
goto defer;
sdev_printk(KERN_ERR, sdev,
@@ -1153,7 +1154,6 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req)
goto kill;
}
-
/*
* Now try and find a command block that we can use.
*/
@@ -1184,7 +1184,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req)
* lock. We hope REQ_STARTED prevents anything untoward from
* happening now.
*/
- if (req->flags & (REQ_CMD | REQ_BLOCK_PC)) {
+ if (blk_fs_request(req) || blk_pc_request(req)) {
int ret;
/*
@@ -1216,7 +1216,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req)
/*
* Initialize the actual SCSI command for this request.
*/
- if (req->flags & REQ_BLOCK_PC) {
+ if (blk_pc_request(req)) {
scsi_setup_blk_pc_cmnd(cmd);
} else if (req->rq_disk) {
struct scsi_driver *drv;
@@ -1233,7 +1233,7 @@ static int scsi_prep_fn(struct request_queue *q, struct request *req)
/*
* The request is now prepped, no need to come back here
*/
- req->flags |= REQ_DONTPREP;
+ req->cmd_flags |= REQ_DONTPREP;
return BLKPREP_OK;
defer:
@@ -1454,8 +1454,9 @@ static void scsi_request_fn(struct request_queue *q)
if (unlikely(cmd == NULL)) {
printk(KERN_CRIT "impossible request in %s.\n"
"please mail a stack trace to "
- "linux-scsi@vger.kernel.org",
+ "linux-scsi@vger.kernel.org\n",
__FUNCTION__);
+ blk_dump_rq_flags(req, "foo");
BUG();
}
spin_lock(shost->host_lock);
diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 638cff41d43..10bc99c911f 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -443,8 +443,7 @@ static int sd_init_command(struct scsi_cmnd * SCpnt)
SCpnt->cmnd[0] = READ_6;
SCpnt->sc_data_direction = DMA_FROM_DEVICE;
} else {
- printk(KERN_ERR "sd: Unknown command %lx\n", rq->flags);
-/* overkill panic("Unknown sd command %lx\n", rq->flags); */
+ printk(KERN_ERR "sd: Unknown command %x\n", rq->cmd_flags);
return 0;
}
@@ -840,7 +839,7 @@ static int sd_issue_flush(struct device *dev, sector_t *error_sector)
static void sd_prepare_flush(request_queue_t *q, struct request *rq)
{
memset(rq->cmd, 0, sizeof(rq->cmd));
- rq->flags |= REQ_BLOCK_PC;
+ rq->cmd_type = REQ_TYPE_BLOCK_PC;
rq->timeout = SD_TIMEOUT;
rq->cmd[0] = SYNCHRONIZE_CACHE;
rq->cmd_len = 10;
diff --git a/drivers/scsi/sim710.c b/drivers/scsi/sim710.c
index b27e85428da..551baccec52 100644
--- a/drivers/scsi/sim710.c
+++ b/drivers/scsi/sim710.c
@@ -282,6 +282,7 @@ static struct eisa_device_id sim710_eisa_ids[] = {
{ "HWP0C80" },
{ "" }
};
+MODULE_DEVICE_TABLE(eisa, sim710_eisa_ids);
static __init int
sim710_eisa_probe(struct device *dev)
diff --git a/drivers/scsi/sun3_NCR5380.c b/drivers/scsi/sun3_NCR5380.c
index 2f8073b73bf..7f9bcef6adf 100644
--- a/drivers/scsi/sun3_NCR5380.c
+++ b/drivers/scsi/sun3_NCR5380.c
@@ -2017,7 +2017,7 @@ static void NCR5380_information_transfer (struct Scsi_Host *instance)
if((count > SUN3_DMA_MINSIZE) && (sun3_dma_setup_done
!= cmd))
{
- if(cmd->request->flags & REQ_CMD) {
+ if(blk_fs_request(cmd->request)) {
sun3scsi_dma_setup(d, count,
rq_data_dir(cmd->request));
sun3_dma_setup_done = cmd;
diff --git a/drivers/scsi/sun3_scsi.c b/drivers/scsi/sun3_scsi.c
index 837173415d4..44a99aeb818 100644
--- a/drivers/scsi/sun3_scsi.c
+++ b/drivers/scsi/sun3_scsi.c
@@ -524,7 +524,7 @@ static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
int write_flag)
{
- if(cmd->request->flags & REQ_CMD)
+ if(blk_fs_request(cmd->request))
return wanted;
else
return 0;
diff --git a/drivers/scsi/sun3_scsi_vme.c b/drivers/scsi/sun3_scsi_vme.c
index 008a82ab852..f5742b84b27 100644
--- a/drivers/scsi/sun3_scsi_vme.c
+++ b/drivers/scsi/sun3_scsi_vme.c
@@ -458,7 +458,7 @@ static inline unsigned long sun3scsi_dma_residual(struct Scsi_Host *instance)
static inline unsigned long sun3scsi_dma_xfer_len(unsigned long wanted, Scsi_Cmnd *cmd,
int write_flag)
{
- if(cmd->request->flags & REQ_CMD)
+ if(blk_fs_request(cmd->request))
return wanted;
else
return 0;
diff --git a/drivers/serial/68328serial.c b/drivers/serial/68328serial.c
index 993a702422e..bac853c5abb 100644
--- a/drivers/serial/68328serial.c
+++ b/drivers/serial/68328serial.c
@@ -1378,7 +1378,7 @@ void startup_console(void)
#endif /* CONFIG_PM_LEGACY */
-static struct tty_operations rs_ops = {
+static const struct tty_operations rs_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/serial/68360serial.c b/drivers/serial/68360serial.c
index e80e70e9b12..1b299e8c57c 100644
--- a/drivers/serial/68360serial.c
+++ b/drivers/serial/68360serial.c
@@ -2424,7 +2424,7 @@ long console_360_init(long kmem_start, long kmem_end)
*/
static int baud_idx;
-static struct tty_operations rs_360_ops = {
+static const struct tty_operations rs_360_ops = {
.owner = THIS_MODULE,
.open = rs_360_open,
.close = rs_360_close,
diff --git a/drivers/serial/8250_acorn.c b/drivers/serial/8250_acorn.c
index 32af3650e8b..ef8cc8a70c6 100644
--- a/drivers/serial/8250_acorn.c
+++ b/drivers/serial/8250_acorn.c
@@ -35,6 +35,7 @@ struct serial_card_type {
struct serial_card_info {
unsigned int num_ports;
int ports[MAX_PORTS];
+ void __iomem *vaddr;
};
static int __devinit
@@ -44,7 +45,6 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
struct serial_card_type *type = id->data;
struct uart_port port;
unsigned long bus_addr;
- unsigned char __iomem *virt_addr;
unsigned int i;
info = kmalloc(sizeof(struct serial_card_info), GFP_KERNEL);
@@ -55,8 +55,8 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
info->num_ports = type->num_ports;
bus_addr = ecard_resource_start(ec, type->type);
- virt_addr = ioremap(bus_addr, ecard_resource_len(ec, type->type));
- if (!virt_addr) {
+ info->vaddr = ioremap(bus_addr, ecard_resource_len(ec, type->type));
+ if (!info->vaddr) {
kfree(info);
return -ENOMEM;
}
@@ -72,7 +72,7 @@ serial_card_probe(struct expansion_card *ec, const struct ecard_id *id)
port.dev = &ec->dev;
for (i = 0; i < info->num_ports; i ++) {
- port.membase = virt_addr + type->offset[i];
+ port.membase = info->vaddr + type->offset[i];
port.mapbase = bus_addr + type->offset[i];
info->ports[i] = serial8250_register_port(&port);
@@ -92,6 +92,7 @@ static void __devexit serial_card_remove(struct expansion_card *ec)
if (info->ports[i] > 0)
serial8250_unregister_port(info->ports[i]);
+ iounmap(info->vaddr);
kfree(info);
}
diff --git a/drivers/serial/8250_gsc.c b/drivers/serial/8250_gsc.c
index 913c71cc056..1ebe6b585d2 100644
--- a/drivers/serial/8250_gsc.c
+++ b/drivers/serial/8250_gsc.c
@@ -64,6 +64,7 @@ serial_init_chip(struct parisc_device *dev)
err = serial8250_register_port(&port);
if (err < 0) {
printk(KERN_WARNING "serial8250_register_port returned error %d\n", err);
+ iounmap(port.membase);
return err;
}
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig
index 5b48ac22c9c..d926272a40d 100644
--- a/drivers/serial/Kconfig
+++ b/drivers/serial/Kconfig
@@ -295,7 +295,7 @@ config SERIAL_AMBA_PL011_CONSOLE
Even if you say Y here, the currently visible framebuffer console
(/dev/tty0) will still be used as the system console by default, but
you can alter that using a kernel command line option such as
- "console=ttyAM0". (Try "man bootparam" or see the documentation of
+ "console=ttyAMA0". (Try "man bootparam" or see the documentation of
your boot loader (lilo or loadlin) about how to pass options to the
kernel at boot time.)
@@ -642,12 +642,17 @@ config V850E_UART_CONSOLE
select SERIAL_CORE_CONSOLE
config SERIAL_SH_SCI
- tristate "SH SCI(F) serial port support"
+ tristate "SuperH SCI(F) serial port support"
depends on SUPERH || H8300
select SERIAL_CORE
+config SERIAL_SH_SCI_NR_UARTS
+ int "Maximum number of SCI(F) serial ports"
+ depends on SERIAL_SH_SCI
+ default "2"
+
config SERIAL_SH_SCI_CONSOLE
- bool "Support for console on SH SCI(F)"
+ bool "Support for console on SuperH SCI(F)"
depends on SERIAL_SH_SCI=y
select SERIAL_CORE_CONSOLE
diff --git a/drivers/serial/at91_serial.c b/drivers/serial/at91_serial.c
index 54c6b2adf7b..bf4bf103e5a 100644
--- a/drivers/serial/at91_serial.c
+++ b/drivers/serial/at91_serial.c
@@ -139,7 +139,7 @@ static void at91_set_mctrl(struct uart_port *port, u_int mctrl)
* AT91RM9200 Errata #39: RTS0 is not internally connected to PA21.
* We need to drive the pin manually.
*/
- if (port->mapbase == AT91_BASE_US0) {
+ if (port->mapbase == AT91RM9200_BASE_US0) {
if (mctrl & TIOCM_RTS)
at91_set_gpio_value(AT91_PIN_PA21, 0);
else
diff --git a/drivers/serial/crisv10.c b/drivers/serial/crisv10.c
index cabd048c863..9851d9eff02 100644
--- a/drivers/serial/crisv10.c
+++ b/drivers/serial/crisv10.c
@@ -4825,7 +4825,7 @@ show_serial_version(void)
/* rs_init inits the driver at boot (using the module_init chain) */
-static struct tty_operations rs_ops = {
+static const struct tty_operations rs_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/serial/ioc4_serial.c b/drivers/serial/ioc4_serial.c
index 576ca1eaa2b..5ec4716c99b 100644
--- a/drivers/serial/ioc4_serial.c
+++ b/drivers/serial/ioc4_serial.c
@@ -2685,6 +2685,7 @@ static int ioc4_serial_remove_one(struct ioc4_driver_data *idd)
if (soft) {
free_irq(control->ic_irq, soft);
if (soft->is_ioc4_serial_addr) {
+ iounmap(soft->is_ioc4_serial_addr);
release_region((unsigned long)
soft->is_ioc4_serial_addr,
sizeof(struct ioc4_serial));
@@ -2887,6 +2888,8 @@ out4:
out3:
kfree(control);
out2:
+ if (serial)
+ iounmap(serial);
release_region(tmp_addr1, sizeof(struct ioc4_serial));
out1:
diff --git a/drivers/serial/ip22zilog.c b/drivers/serial/ip22zilog.c
index 5ff269fb604..dbf13c03a1b 100644
--- a/drivers/serial/ip22zilog.c
+++ b/drivers/serial/ip22zilog.c
@@ -1229,13 +1229,27 @@ static int __init ip22zilog_init(void)
static void __exit ip22zilog_exit(void)
{
int i;
+ struct uart_ip22zilog_port *up;
for (i = 0; i < NUM_CHANNELS; i++) {
- struct uart_ip22zilog_port *up = &ip22zilog_port_table[i];
+ up = &ip22zilog_port_table[i];
uart_remove_one_port(&ip22zilog_reg, &up->port);
}
+ /* Free IO mem */
+ up = &ip22zilog_port_table[0];
+ for (i = 0; i < NUM_IP22ZILOG; i++) {
+ if (up[(i * 2) + 0].port.mapbase) {
+ iounmap((void*)up[(i * 2) + 0].port.mapbase);
+ up[(i * 2) + 0].port.mapbase = 0;
+ }
+ if (up[(i * 2) + 1].port.mapbase) {
+ iounmap((void*)up[(i * 2) + 1].port.mapbase);
+ up[(i * 2) + 1].port.mapbase = 0;
+ }
+ }
+
uart_unregister_driver(&ip22zilog_reg);
}
diff --git a/drivers/serial/mcfserial.c b/drivers/serial/mcfserial.c
index 832abd3c470..00d7859c167 100644
--- a/drivers/serial/mcfserial.c
+++ b/drivers/serial/mcfserial.c
@@ -1666,7 +1666,7 @@ static void show_serial_version(void)
printk(mcfrs_drivername);
}
-static struct tty_operations mcfrs_ops = {
+static const struct tty_operations mcfrs_ops = {
.open = mcfrs_open,
.close = mcfrs_close,
.write = mcfrs_write,
diff --git a/drivers/serial/mpc52xx_uart.c b/drivers/serial/mpc52xx_uart.c
index 7708e5dd365..dbad0e31e00 100644
--- a/drivers/serial/mpc52xx_uart.c
+++ b/drivers/serial/mpc52xx_uart.c
@@ -338,14 +338,23 @@ mpc52xx_uart_release_port(struct uart_port *port)
static int
mpc52xx_uart_request_port(struct uart_port *port)
{
+ int err;
+
if (port->flags & UPF_IOREMAP) /* Need to remap ? */
port->membase = ioremap(port->mapbase, MPC52xx_PSC_SIZE);
if (!port->membase)
return -EINVAL;
- return request_mem_region(port->mapbase, MPC52xx_PSC_SIZE,
+ err = request_mem_region(port->mapbase, MPC52xx_PSC_SIZE,
"mpc52xx_psc_uart") != NULL ? 0 : -EBUSY;
+
+ if (err && (port->flags & UPF_IOREMAP)) {
+ iounmap(port->membase);
+ port->membase = NULL;
+ }
+
+ return err;
}
static void
diff --git a/drivers/serial/mpsc.c b/drivers/serial/mpsc.c
index 63d2a66e563..704243c9f78 100644
--- a/drivers/serial/mpsc.c
+++ b/drivers/serial/mpsc.c
@@ -1893,6 +1893,10 @@ mpsc_drv_map_regs(struct mpsc_port_info *pi, struct platform_device *pd)
}
else {
mpsc_resource_err("SDMA base");
+ if (pi->mpsc_base) {
+ iounmap(pi->mpsc_base);
+ pi->mpsc_base = NULL;
+ }
return -ENOMEM;
}
@@ -1905,6 +1909,14 @@ mpsc_drv_map_regs(struct mpsc_port_info *pi, struct platform_device *pd)
}
else {
mpsc_resource_err("BRG base");
+ if (pi->mpsc_base) {
+ iounmap(pi->mpsc_base);
+ pi->mpsc_base = NULL;
+ }
+ if (pi->sdma_base) {
+ iounmap(pi->sdma_base);
+ pi->sdma_base = NULL;
+ }
return -ENOMEM;
}
diff --git a/drivers/serial/mux.c b/drivers/serial/mux.c
index 4a1c9983f38..aa819d3f8ee 100644
--- a/drivers/serial/mux.c
+++ b/drivers/serial/mux.c
@@ -521,6 +521,8 @@ static void __exit mux_exit(void)
for (i = 0; i < port_cnt; i++) {
uart_remove_one_port(&mux_driver, &mux_ports[i]);
+ if (mux_ports[i].membase)
+ iounmap(mux_ports[i].membase);
}
uart_unregister_driver(&mux_driver);
diff --git a/drivers/serial/serial_core.c b/drivers/serial/serial_core.c
index 372e47f7d59..de5e8930a6f 100644
--- a/drivers/serial/serial_core.c
+++ b/drivers/serial/serial_core.c
@@ -1929,6 +1929,13 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
mutex_lock(&state->mutex);
+#ifdef CONFIG_DISABLE_CONSOLE_SUSPEND
+ if (uart_console(port)) {
+ mutex_unlock(&state->mutex);
+ return 0;
+ }
+#endif
+
if (state->info && state->info->flags & UIF_INITIALIZED) {
const struct uart_ops *ops = port->ops;
@@ -1967,6 +1974,13 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
mutex_lock(&state->mutex);
+#ifdef CONFIG_DISABLE_CONSOLE_SUSPEND
+ if (uart_console(port)) {
+ mutex_unlock(&state->mutex);
+ return 0;
+ }
+#endif
+
uart_change_pm(state, 0);
/*
@@ -2097,7 +2111,7 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
}
}
-static struct tty_operations uart_ops = {
+static const struct tty_operations uart_ops = {
.open = uart_open,
.close = uart_close,
.write = uart_write,
diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c
index cbede06cac2..f336ba6778d 100644
--- a/drivers/serial/sh-sci.c
+++ b/drivers/serial/sh-sci.c
@@ -3,7 +3,7 @@
*
* SuperH on-chip serial module support. (SCI with no FIFO / with FIFO)
*
- * Copyright (C) 2002, 2003, 2004 Paul Mundt
+ * Copyright (C) 2002 - 2006 Paul Mundt
*
* based off of the old drivers/char/sh-sci.c by:
*
@@ -20,10 +20,9 @@
#undef DEBUG
+#include <linux/config.h>
#include <linux/module.h>
#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
@@ -32,71 +31,77 @@
#include <linux/major.h>
#include <linux/string.h>
#include <linux/sysrq.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
-#include <linux/slab.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/console.h>
-#include <linux/bitops.h>
-#include <linux/generic_serial.h>
+#include <linux/platform_device.h>
#ifdef CONFIG_CPU_FREQ
#include <linux/notifier.h>
#include <linux/cpufreq.h>
#endif
-#include <asm/system.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/uaccess.h>
-
#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
#include <asm/clock.h>
-#endif
-
-#ifdef CONFIG_SH_STANDARD_BIOS
#include <asm/sh_bios.h>
+#include <asm/kgdb.h>
#endif
+#include <asm/sci.h>
+
#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
#define SUPPORT_SYSRQ
#endif
#include "sh-sci.h"
-#ifdef CONFIG_SH_KGDB
-#include <asm/kgdb.h>
+struct sci_port {
+ struct uart_port port;
+
+ /* Port type */
+ unsigned int type;
+
+ /* Port IRQs: ERI, RXI, TXI, BRI (optional) */
+ unsigned int irqs[SCIx_NR_IRQS];
+
+ /* Port pin configuration */
+ void (*init_pins)(struct uart_port *port,
+ unsigned int cflag);
-static int kgdb_get_char(struct sci_port *port);
-static void kgdb_put_char(struct sci_port *port, char c);
-static void kgdb_handle_error(struct sci_port *port);
+ /* Port enable callback */
+ void (*enable)(struct uart_port *port);
+
+ /* Port disable callback */
+ void (*disable)(struct uart_port *port);
+
+ /* Break timer */
+ struct timer_list break_timer;
+ int break_flag;
+};
+
+#ifdef CONFIG_SH_KGDB
static struct sci_port *kgdb_sci_port;
-#endif /* CONFIG_SH_KGDB */
+#endif
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
-static struct sci_port *serial_console_port = 0;
-#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
+static struct sci_port *serial_console_port;
+#endif
/* Function prototypes */
static void sci_stop_tx(struct uart_port *port);
-static void sci_start_tx(struct uart_port *port);
-static void sci_start_rx(struct uart_port *port, unsigned int tty_start);
-static void sci_stop_rx(struct uart_port *port);
-static int sci_request_irq(struct sci_port *port);
-static void sci_free_irq(struct sci_port *port);
-
-static struct sci_port sci_ports[];
-static struct uart_driver sci_uart_driver;
-#define SCI_NPORTS sci_uart_driver.nr
+#define SCI_NPORTS CONFIG_SERIAL_SH_SCI_NR_UARTS
-#if defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static struct sci_port sci_ports[SCI_NPORTS];
+static struct uart_driver sci_uart_driver;
-static void handle_error(struct uart_port *port)
-{ /* Clear error flags */
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) && \
+ defined(CONFIG_SH_STANDARD_BIOS) || defined(CONFIG_SH_KGDB)
+static inline void handle_error(struct uart_port *port)
+{
+ /* Clear error flags */
sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port));
}
@@ -106,8 +111,8 @@ static int get_char(struct uart_port *port)
unsigned short status;
int c;
- local_irq_save(flags);
- do {
+ spin_lock_irqsave(&port->lock, flags);
+ do {
status = sci_in(port, SCxSR);
if (status & SCxSR_ERRORS(port)) {
handle_error(port);
@@ -117,38 +122,19 @@ static int get_char(struct uart_port *port)
c = sci_in(port, SCxRDR);
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
return c;
}
-
-/* Taken from sh-stub.c of GDB 4.18 */
-static const char hexchars[] = "0123456789abcdef";
-
-static __inline__ char highhex(int x)
-{
- return hexchars[(x >> 4) & 0xf];
-}
-
-static __inline__ char lowhex(int x)
-{
- return hexchars[x & 0xf];
-}
-
#endif /* CONFIG_SH_STANDARD_BIOS || CONFIG_SH_KGDB */
-/*
- * Send the packet in buffer. The host gets one chance to read it.
- * This routine does not wait for a positive acknowledge.
- */
-
-#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
+#if defined(CONFIG_SERIAL_SH_SCI_CONSOLE) || defined(CONFIG_SH_KGDB)
static void put_char(struct uart_port *port, char c)
{
unsigned long flags;
unsigned short status;
- local_irq_save(flags);
+ spin_lock_irqsave(&port->lock, flags);
do {
status = sci_in(port, SCxSR);
@@ -158,9 +144,11 @@ static void put_char(struct uart_port *port, char c)
sci_in(port, SCxSR); /* Dummy read */
sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
- local_irq_restore(flags);
+ spin_unlock_irqrestore(&port->lock, flags);
}
+#endif
+#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
static void put_string(struct sci_port *sci_port, const char *buffer, int count)
{
struct uart_port *port = &sci_port->port;
@@ -213,96 +201,28 @@ static void put_string(struct sci_port *sci_port, const char *buffer, int count)
}
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
-
#ifdef CONFIG_SH_KGDB
-
-/* Is the SCI ready, ie is there a char waiting? */
-static int kgdb_is_char_ready(struct sci_port *port)
-{
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & (SCxSR_ERRORS(port) | SCxSR_BRK(port)))
- kgdb_handle_error(port);
-
- return (status & SCxSR_RDxF(port));
-}
-
-/* Write a char */
-static void kgdb_put_char(struct sci_port *port, char c)
-{
- unsigned short status;
-
- do
- status = sci_in(port, SCxSR);
- while (!(status & SCxSR_TDxE(port)));
-
- sci_out(port, SCxTDR, c);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port));
-}
-
-/* Get a char if there is one, else ret -1 */
-static int kgdb_get_char(struct sci_port *port)
-{
- int c;
-
- if (kgdb_is_char_ready(port) == 0)
- c = -1;
- else {
- c = sci_in(port, SCxRDR);
- sci_in(port, SCxSR); /* Dummy read */
- sci_out(port, SCxSR, SCxSR_RDxF_CLEAR(port));
- }
-
- return c;
-}
-
-/* Called from kgdbstub.c to get a character, i.e. is blocking */
static int kgdb_sci_getchar(void)
{
- volatile int c;
+ int c;
/* Keep trying to read a character, this could be neater */
- while ((c = kgdb_get_char(kgdb_sci_port)) < 0);
+ while ((c = get_char(kgdb_sci_port)) < 0)
+ cpu_relax();
return c;
}
-/* Called from kgdbstub.c to put a character, just a wrapper */
-static void kgdb_sci_putchar(int c)
-{
-
- kgdb_put_char(kgdb_sci_port, c);
-}
-
-/* Clear any errors on the SCI */
-static void kgdb_handle_error(struct sci_port *port)
+static inline void kgdb_sci_putchar(int c)
{
- sci_out(port, SCxSR, SCxSR_ERROR_CLEAR(port)); /* Clear error flags */
+ put_char(kgdb_sci_port, c);
}
-
-/* Breakpoint if there's a break sent on the serial port */
-static void kgdb_break_interrupt(int irq, void *ptr, struct pt_regs *regs)
-{
- struct sci_port *port = ptr;
- unsigned short status = sci_in(port, SCxSR);
-
- if (status & SCxSR_BRK(port)) {
-
- /* Break into the debugger if a break is detected */
- BREAKPOINT();
-
- /* Clear */
- sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
- }
-}
-
#endif /* CONFIG_SH_KGDB */
#if defined(__H8300S__)
enum { sci_disable, sci_enable };
-static void h8300_sci_enable(struct uart_port* port, unsigned int ctrl)
+static void h8300_sci_config(struct uart_port* port, unsigned int ctrl)
{
volatile unsigned char *mstpcrl=(volatile unsigned char *)MSTPCRL;
int ch = (port->mapbase - SMR0) >> 3;
@@ -314,32 +234,66 @@ static void h8300_sci_enable(struct uart_port* port, unsigned int ctrl)
*mstpcrl &= ~mask;
}
}
+
+static inline void h8300_sci_enable(struct uart_port *port)
+{
+ h8300_sci_config(port, sci_enable);
+}
+
+static inline void h8300_sci_disable(struct uart_port *port)
+{
+ h8300_sci_config(port, sci_disable);
+}
#endif
-#if defined(SCI_ONLY) || defined(SCI_AND_SCIF)
-#if defined(__H8300H__) || defined(__H8300S__)
+#if defined(SCI_ONLY) || defined(SCI_AND_SCIF) && \
+ defined(__H8300H__) || defined(__H8300S__)
static void sci_init_pins_sci(struct uart_port* port, unsigned int cflag)
{
int ch = (port->mapbase - SMR0) >> 3;
/* set DDR regs */
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].rx,H8300_GPIO_INPUT);
- H8300_GPIO_DDR(h8300_sci_pins[ch].port,h8300_sci_pins[ch].tx,H8300_GPIO_OUTPUT);
+ H8300_GPIO_DDR(h8300_sci_pins[ch].port,
+ h8300_sci_pins[ch].rx,
+ H8300_GPIO_INPUT);
+ H8300_GPIO_DDR(h8300_sci_pins[ch].port,
+ h8300_sci_pins[ch].tx,
+ H8300_GPIO_OUTPUT);
+
/* tx mark output*/
H8300_SCI_DR(ch) |= h8300_sci_pins[ch].tx;
}
+#else
+#define sci_init_pins_sci NULL
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
+{
+ unsigned int fcr_val = 0;
+
+ if (cflag & CRTSCTS)
+ fcr_val |= SCFCR_MCE;
+
+ sci_out(port, SCFCR, fcr_val);
+}
+#else
+#define sci_init_pins_irda NULL
#endif
+
+#ifdef SCI_ONLY
+#define sci_init_pins_scif NULL
#endif
#if defined(SCIF_ONLY) || defined(SCI_AND_SCIF)
-#if defined(CONFIG_CPU_SUBTYPE_SH7300)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7710)
/* SH7300 doesn't use RTS/CTS */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
sci_out(port, SCFCR, 0);
}
#elif defined(CONFIG_CPU_SH3)
-/* For SH7705, SH7707, SH7709, SH7709A, SH7729 */
+/* For SH7705, SH7706, SH7707, SH7709, SH7709A, SH7729 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
unsigned int fcr_val = 0;
@@ -366,20 +320,7 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
sci_out(port, SCFCR, fcr_val);
}
-
-#if defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
-static void sci_init_pins_irda(struct uart_port *port, unsigned int cflag)
-{
- unsigned int fcr_val = 0;
-
- if (cflag & CRTSCTS)
- fcr_val |= SCFCR_MCE;
-
- sci_out(port, SCFCR, fcr_val);
-}
-#endif
#else
-
/* For SH7750 */
static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
{
@@ -388,7 +329,9 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
if (cflag & CRTSCTS) {
fcr_val |= SCFCR_MCE;
} else {
-#ifdef CONFIG_CPU_SUBTYPE_SH7780
+#ifdef CONFIG_CPU_SUBTYPE_SH7343
+ /* Nothing */
+#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
ctrl_outw(0x0080, SCSPTR0); /* Set RTS = 1 */
#else
ctrl_outw(0x0080, SCSPTR2); /* Set RTS = 1 */
@@ -396,10 +339,41 @@ static void sci_init_pins_scif(struct uart_port *port, unsigned int cflag)
}
sci_out(port, SCFCR, fcr_val);
}
+#endif
+
+#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
+static inline int scif_txroom(struct uart_port *port)
+{
+ return SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0x7f);
+}
+
+static inline int scif_rxroom(struct uart_port *port)
+{
+ return sci_in(port, SCRFDR) & 0x7f;
+}
+#else
+static inline int scif_txroom(struct uart_port *port)
+{
+ return SCIF_TXROOM_MAX - (sci_in(port, SCFDR) >> 8);
+}
+static inline int scif_rxroom(struct uart_port *port)
+{
+ return sci_in(port, SCFDR) & SCIF_RFDC_MASK;
+}
#endif
#endif /* SCIF_ONLY || SCI_AND_SCIF */
+static inline int sci_txroom(struct uart_port *port)
+{
+ return ((sci_in(port, SCxSR) & SCI_TDRE) != 0);
+}
+
+static inline int sci_rxroom(struct uart_port *port)
+{
+ return ((sci_in(port, SCxSR) & SCxSR_RDxF(port)) != 0);
+}
+
/* ********************************************************************** *
* the interrupt related routines *
* ********************************************************************** */
@@ -408,14 +382,12 @@ static void sci_transmit_chars(struct uart_port *port)
{
struct circ_buf *xmit = &port->info->xmit;
unsigned int stopped = uart_tx_stopped(port);
- unsigned long flags;
unsigned short status;
unsigned short ctrl;
- int count, txroom;
+ int count;
status = sci_in(port, SCxSR);
if (!(status & SCxSR_TDxE(port))) {
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
if (uart_circ_empty(xmit)) {
ctrl &= ~SCI_CTRL_FLAGS_TIE;
@@ -423,25 +395,15 @@ static void sci_transmit_chars(struct uart_port *port)
ctrl |= SCI_CTRL_FLAGS_TIE;
}
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
return;
}
-#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
- txroom = SCIF_TXROOM_MAX - (sci_in(port, SCTFDR) & 0x7f);
-#else
- txroom = SCIF_TXROOM_MAX - (sci_in(port, SCFDR)>>8);
-#endif
- } else {
- txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0;
- }
-#else
- txroom = (sci_in(port, SCxSR) & SCI_TDRE)?1:0;
+#ifndef SCI_ONLY
+ if (port->type == PORT_SCIF)
+ count = scif_txroom(port);
+ else
#endif
-
- count = txroom;
+ count = sci_txroom(port);
do {
unsigned char c;
@@ -468,7 +430,6 @@ static void sci_transmit_chars(struct uart_port *port)
if (uart_circ_empty(xmit)) {
sci_stop_tx(port);
} else {
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
#if !defined(SCI_ONLY)
@@ -480,7 +441,6 @@ static void sci_transmit_chars(struct uart_port *port)
ctrl |= SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
}
@@ -490,6 +450,7 @@ static void sci_transmit_chars(struct uart_port *port)
static inline void sci_receive_chars(struct uart_port *port,
struct pt_regs *regs)
{
+ struct sci_port *sci_port = (struct sci_port *)port;
struct tty_struct *tty = port->info->tty;
int i, count, copied = 0;
unsigned short status;
@@ -501,18 +462,11 @@ static inline void sci_receive_chars(struct uart_port *port,
while (1) {
#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
-#if defined(CONFIG_CPU_SUBTYPE_SH7760) || defined(CONFIG_CPU_SUBTYPE_SH7780)
- count = sci_in(port, SCRFDR) & 0x7f;
-#else
- count = sci_in(port, SCFDR)&SCIF_RFDC_MASK ;
-#endif
- } else {
- count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0;
- }
-#else
- count = (sci_in(port, SCxSR)&SCxSR_RDxF(port))?1:0;
+ if (port->type == PORT_SCIF)
+ count = scif_rxroom(port);
+ else
#endif
+ count = sci_rxroom(port);
/* Don't copy more bytes than there is room for in the buffer */
count = tty_buffer_request_room(tty, count);
@@ -523,11 +477,10 @@ static inline void sci_receive_chars(struct uart_port *port,
if (port->type == PORT_SCI) {
char c = sci_in(port, SCxRDR);
- if(((struct sci_port *)port)->break_flag
- || uart_handle_sysrq_char(port, c, regs)) {
+ if (uart_handle_sysrq_char(port, c, regs) || sci_port->break_flag)
count = 0;
- } else {
- tty_insert_flip_char(tty, c, TTY_NORMAL);
+ else {
+ tty_insert_flip_char(tty, c, TTY_NORMAL);
}
} else {
for (i=0; i<count; i++) {
@@ -535,15 +488,17 @@ static inline void sci_receive_chars(struct uart_port *port,
status = sci_in(port, SCxSR);
#if defined(CONFIG_CPU_SH3)
/* Skip "chars" during break */
- if (((struct sci_port *)port)->break_flag) {
+ if (sci_port->break_flag) {
if ((c == 0) &&
(status & SCxSR_FER(port))) {
count--; i--;
continue;
}
+
/* Nonzero => end-of-break */
pr_debug("scif: debounce<%02x>\n", c);
- ((struct sci_port *)port)->break_flag = 0;
+ sci_port->break_flag = 0;
+
if (STEPFN(c)) {
count--; i--;
continue;
@@ -600,15 +555,17 @@ static void sci_schedule_break_timer(struct sci_port *port)
/* Ensure that two consecutive samples find the break over. */
static void sci_break_timer(unsigned long data)
{
- struct sci_port * port = (struct sci_port *)data;
- if(sci_rxd_in(&port->port) == 0) {
+ struct sci_port *port = (struct sci_port *)data;
+
+ if (sci_rxd_in(&port->port) == 0) {
port->break_flag = 1;
- sci_schedule_break_timer(port);
- } else if(port->break_flag == 1){
+ sci_schedule_break_timer(port);
+ } else if (port->break_flag == 1) {
/* break is over. */
port->break_flag = 2;
- sci_schedule_break_timer(port);
- } else port->break_flag = 0;
+ sci_schedule_break_timer(port);
+ } else
+ port->break_flag = 0;
}
static inline int sci_handle_errors(struct uart_port *port)
@@ -617,40 +574,41 @@ static inline int sci_handle_errors(struct uart_port *port)
unsigned short status = sci_in(port, SCxSR);
struct tty_struct *tty = port->info->tty;
- if (status&SCxSR_ORER(port)) {
+ if (status & SCxSR_ORER(port)) {
/* overrun error */
- if(tty_insert_flip_char(tty, 0, TTY_OVERRUN))
+ if (tty_insert_flip_char(tty, 0, TTY_OVERRUN))
copied++;
pr_debug("sci: overrun error\n");
}
- if (status&SCxSR_FER(port)) {
+ if (status & SCxSR_FER(port)) {
if (sci_rxd_in(port) == 0) {
/* Notify of BREAK */
- struct sci_port * sci_port = (struct sci_port *)port;
- if(!sci_port->break_flag) {
- sci_port->break_flag = 1;
- sci_schedule_break_timer((struct sci_port *)port);
+ struct sci_port *sci_port = (struct sci_port *)port;
+
+ if (!sci_port->break_flag) {
+ sci_port->break_flag = 1;
+ sci_schedule_break_timer(sci_port);
+
/* Do sysrq handling. */
- if(uart_handle_break(port))
+ if (uart_handle_break(port))
return 0;
pr_debug("sci: BREAK detected\n");
- if(tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tty, 0, TTY_BREAK))
copied++;
}
- }
- else {
+ } else {
/* frame error */
- if(tty_insert_flip_char(tty, 0, TTY_FRAME))
+ if (tty_insert_flip_char(tty, 0, TTY_FRAME))
copied++;
pr_debug("sci: frame error\n");
}
}
- if (status&SCxSR_PER(port)) {
- if(tty_insert_flip_char(tty, 0, TTY_PARITY))
- copied++;
+ if (status & SCxSR_PER(port)) {
/* parity error */
+ if (tty_insert_flip_char(tty, 0, TTY_PARITY))
+ copied++;
pr_debug("sci: parity error\n");
}
@@ -673,7 +631,7 @@ static inline int sci_handle_breaks(struct uart_port *port)
s->break_flag = 1;
#endif
/* Notify of BREAK */
- if(tty_insert_flip_char(tty, 0, TTY_BREAK))
+ if (tty_insert_flip_char(tty, 0, TTY_BREAK))
copied++;
pr_debug("sci: BREAK detected\n");
}
@@ -682,7 +640,7 @@ static inline int sci_handle_breaks(struct uart_port *port)
/* XXX: Handle SCIF overrun error */
if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) {
sci_out(port, SCLSR, 0);
- if(tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
+ if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) {
copied++;
pr_debug("sci: overrun error\n");
}
@@ -691,13 +649,12 @@ static inline int sci_handle_breaks(struct uart_port *port)
if (copied)
tty_flip_buffer_push(tty);
+
return copied;
}
-static irqreturn_t sci_rx_interrupt(int irq, void *ptr, struct pt_regs *regs)
+static irqreturn_t sci_rx_interrupt(int irq, void *port, struct pt_regs *regs)
{
- struct uart_port *port = ptr;
-
/* I think sci_receive_chars has to be called irrespective
* of whether the I_IXOFF is set, otherwise, how is the interrupt
* to be disabled?
@@ -711,7 +668,9 @@ static irqreturn_t sci_tx_interrupt(int irq, void *ptr, struct pt_regs *regs)
{
struct uart_port *port = ptr;
+ spin_lock_irq(&port->lock);
sci_transmit_chars(port);
+ spin_unlock_irq(&port->lock);
return IRQ_HANDLED;
}
@@ -755,6 +714,12 @@ static irqreturn_t sci_br_interrupt(int irq, void *ptr, struct pt_regs *regs)
/* Handle BREAKs */
sci_handle_breaks(port);
+
+#ifdef CONFIG_SH_KGDB
+ /* Break into the debugger if a break is detected */
+ BREAKPOINT();
+#endif
+
sci_out(port, SCxSR, SCxSR_BREAK_CLEAR(port));
return IRQ_HANDLED;
@@ -769,16 +734,16 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr, struct pt_regs *regs)
scr_status = sci_in(port,SCSCR);
/* Tx Interrupt */
- if ((ssr_status&0x0020) && (scr_status&0x0080))
+ if ((ssr_status & 0x0020) && (scr_status & 0x0080))
sci_tx_interrupt(irq, ptr, regs);
/* Rx Interrupt */
- if ((ssr_status&0x0002) && (scr_status&0x0040))
+ if ((ssr_status & 0x0002) && (scr_status & 0x0040))
sci_rx_interrupt(irq, ptr, regs);
/* Error Interrupt */
- if ((ssr_status&0x0080) && (scr_status&0x0400))
+ if ((ssr_status & 0x0080) && (scr_status & 0x0400))
sci_er_interrupt(irq, ptr, regs);
/* Break Interrupt */
- if ((ssr_status&0x0010) && (scr_status&0x0200))
+ if ((ssr_status & 0x0010) && (scr_status & 0x0200))
sci_br_interrupt(irq, ptr, regs);
return IRQ_HANDLED;
@@ -789,7 +754,8 @@ static irqreturn_t sci_mpxed_interrupt(int irq, void *ptr, struct pt_regs *regs)
* Here we define a transistion notifier so that we can update all of our
* ports' baud rate when the peripheral clock changes.
*/
-static int sci_notifier(struct notifier_block *self, unsigned long phase, void *p)
+static int sci_notifier(struct notifier_block *self,
+ unsigned long phase, void *p)
{
struct cpufreq_freqs *freqs = p;
int i;
@@ -816,8 +782,9 @@ static int sci_notifier(struct notifier_block *self, unsigned long phase, void *
clk_put(clk);
}
- printk("%s: got a postchange notification for cpu %d (old %d, new %d)\n",
- __FUNCTION__, freqs->cpu, freqs->old, freqs->new);
+ printk(KERN_INFO "%s: got a postchange notification "
+ "for cpu %d (old %d, new %d)\n",
+ __FUNCTION__, freqs->cpu, freqs->old, freqs->new);
}
return NOTIFY_OK;
@@ -841,8 +808,9 @@ static int sci_request_irq(struct sci_port *port)
printk(KERN_ERR "sci: Cannot allocate irq.(IRQ=0)\n");
return -ENODEV;
}
- if (request_irq(port->irqs[0], sci_mpxed_interrupt, IRQF_DISABLED,
- "sci", port)) {
+
+ if (request_irq(port->irqs[0], sci_mpxed_interrupt,
+ SA_INTERRUPT, "sci", port)) {
printk(KERN_ERR "sci: Cannot allocate irq.\n");
return -ENODEV;
}
@@ -850,8 +818,8 @@ static int sci_request_irq(struct sci_port *port)
for (i = 0; i < ARRAY_SIZE(handlers); i++) {
if (!port->irqs[i])
continue;
- if (request_irq(port->irqs[i], handlers[i], IRQF_DISABLED,
- desc[i], port)) {
+ if (request_irq(port->irqs[i], handlers[i],
+ SA_INTERRUPT, desc[i], port)) {
printk(KERN_ERR "sci: Cannot allocate irq.\n");
return -ENODEV;
}
@@ -903,50 +871,42 @@ static unsigned int sci_get_mctrl(struct uart_port *port)
static void sci_start_tx(struct uart_port *port)
{
- struct sci_port *s = &sci_ports[port->line];
+ unsigned short ctrl;
- disable_irq(s->irqs[SCIx_TXI_IRQ]);
- sci_transmit_chars(port);
- enable_irq(s->irqs[SCIx_TXI_IRQ]);
+ /* Set TIE (Transmit Interrupt Enable) bit in SCSCR */
+ ctrl = sci_in(port, SCSCR);
+ ctrl |= SCI_CTRL_FLAGS_TIE;
+ sci_out(port, SCSCR, ctrl);
}
static void sci_stop_tx(struct uart_port *port)
{
- unsigned long flags;
unsigned short ctrl;
/* Clear TIE (Transmit Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl &= ~SCI_CTRL_FLAGS_TIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_start_rx(struct uart_port *port, unsigned int tty_start)
{
- unsigned long flags;
unsigned short ctrl;
/* Set RIE (Receive Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl |= SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE;
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_stop_rx(struct uart_port *port)
{
- unsigned long flags;
unsigned short ctrl;
/* Clear RIE (Receive Interrupt Enable) bit in SCSCR */
- local_irq_save(flags);
ctrl = sci_in(port, SCSCR);
ctrl &= ~(SCI_CTRL_FLAGS_RIE | SCI_CTRL_FLAGS_REIE);
sci_out(port, SCSCR, ctrl);
- local_irq_restore(flags);
}
static void sci_enable_ms(struct uart_port *port)
@@ -963,9 +923,8 @@ static int sci_startup(struct uart_port *port)
{
struct sci_port *s = &sci_ports[port->line];
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_enable);
-#endif
+ if (s->enable)
+ s->enable(port);
sci_request_irq(s);
sci_start_tx(port);
@@ -982,9 +941,8 @@ static void sci_shutdown(struct uart_port *port)
sci_stop_tx(port);
sci_free_irq(s);
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_disable);
-#endif
+ if (s->disable)
+ s->disable(port);
}
static void sci_set_termios(struct uart_port *port, struct termios *termios,
@@ -997,6 +955,23 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+ switch (baud) {
+ case 0:
+ t = -1;
+ break;
+ default:
+ {
+#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
+ struct clk *clk = clk_get("module_clk");
+ t = SCBRR_VALUE(baud, clk_get_rate(clk));
+ clk_put(clk);
+#else
+ t = SCBRR_VALUE(baud);
+#endif
+ }
+ break;
+ }
+
spin_lock_irqsave(&port->lock, flags);
do {
@@ -1006,9 +981,8 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */
#if !defined(SCI_ONLY)
- if (port->type == PORT_SCIF) {
+ if (port->type == PORT_SCIF)
sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST);
- }
#endif
smr_val = sci_in(port, SCSMR) & 3;
@@ -1025,23 +999,6 @@ static void sci_set_termios(struct uart_port *port, struct termios *termios,
sci_out(port, SCSMR, smr_val);
- switch (baud) {
- case 0:
- t = -1;
- break;
- default:
- {
-#if defined(CONFIG_SUPERH) && !defined(CONFIG_SUPERH64)
- struct clk *clk = clk_get("module_clk");
- t = SCBRR_VALUE(baud, clk_get_rate(clk));
- clk_put(clk);
-#else
- t = SCBRR_VALUE(baud);
-#endif
- }
- break;
- }
-
if (t > 0) {
if(t >= 256) {
sci_out(port, SCSMR, (sci_in(port, SCSMR) & ~3) | 1);
@@ -1092,11 +1049,23 @@ static void sci_config_port(struct uart_port *port, int flags)
port->type = s->type;
+ switch (port->type) {
+ case PORT_SCI:
+ s->init_pins = sci_init_pins_sci;
+ break;
+ case PORT_SCIF:
+ s->init_pins = sci_init_pins_scif;
+ break;
+ case PORT_IRDA:
+ s->init_pins = sci_init_pins_irda;
+ break;
+ }
+
#if defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
if (port->mapbase == 0)
port->mapbase = onchip_remap(SCIF_ADDR_SH5, 1024, "SCIF");
- port->membase = (void *)port->mapbase;
+ port->membase = (void __iomem *)port->mapbase;
#endif
}
@@ -1132,412 +1101,61 @@ static struct uart_ops sci_uart_ops = {
.verify_port = sci_verify_port,
};
-static struct sci_port sci_ports[] = {
-#if defined(CONFIG_CPU_SUBTYPE_SH7708)
- {
- .port = {
- .membase = (void *)0xfffffe80,
- .mapbase = 0xfffffe80,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7705)
- {
- .port = {
- .membase = (void *)SCIF0,
- .mapbase = SCIF0,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH3_IRDA_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)SCIF2,
- .mapbase = SCIF2,
- .iotype = UPIO_MEM,
- .irq = 59,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH3_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
- {
- .port = {
- .membase = (void *)0xfffffe80,
- .mapbase = 0xfffffe80,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
- {
- .port = {
- .membase = (void *)0xa4000150,
- .mapbase = 0xa4000150,
- .iotype = UPIO_MEM,
- .irq = 59,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH3_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xa4000140,
- .mapbase = 0xa4000140,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_IRDA,
- .irqs = SH3_IRDA_IRQS,
- .init_pins = sci_init_pins_irda,
- }
-#elif defined(CONFIG_CPU_SUBTYPE_SH7300)
- {
- .port = {
- .membase = (void *)0xA4430000,
- .mapbase = 0xA4430000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7300_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH73180_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || defined(CONFIG_CPU_SUBTYPE_SH7751)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 25,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = SCI_IRQS,
- },
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7760)
- {
- .port = {
- .membase = (void *)0xfe600000,
- .mapbase = 0xfe600000,
- .iotype = UPIO_MEM,
- .irq = 55,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xfe610000,
- .mapbase = 0xfe610000,
- .iotype = UPIO_MEM,
- .irq = 75,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xfe620000,
- .mapbase = 0xfe620000,
- .iotype = UPIO_MEM,
- .irq = 79,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCIF,
- .irqs = SH7760_SCIF2_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 26,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = STB1_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xffe80000,
- .mapbase = 0xffe80000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH4_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH5_101) || defined(CONFIG_CPU_SUBTYPE_SH5_103)
- {
- .port = {
- .iotype = UPIO_MEM,
- .irq = 42,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH5_SCIF_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_H83007) || defined(CONFIG_H83068)
- {
- .port = {
- .membase = (void *)0x00ffffb0,
- .mapbase = 0x00ffffb0,
- .iotype = UPIO_MEM,
- .irq = 54,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS0,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffffb8,
- .mapbase = 0x00ffffb8,
- .iotype = UPIO_MEM,
- .irq = 58,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS1,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffffc0,
- .mapbase = 0x00ffffc0,
- .iotype = UPIO_MEM,
- .irq = 62,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCI,
- .irqs = H8300H_SCI_IRQS2,
- .init_pins = sci_init_pins_sci,
- },
-#elif defined(CONFIG_H8S2678)
- {
- .port = {
- .membase = (void *)0x00ffff78,
- .mapbase = 0x00ffff78,
- .iotype = UPIO_MEM,
- .irq = 90,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS0,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffff80,
- .mapbase = 0x00ffff80,
- .iotype = UPIO_MEM,
- .irq = 94,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS1,
- .init_pins = sci_init_pins_sci,
- },
- {
- .port = {
- .membase = (void *)0x00ffff88,
- .mapbase = 0x00ffff88,
- .iotype = UPIO_MEM,
- .irq = 98,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCI,
- .irqs = H8S_SCI_IRQS2,
- .init_pins = sci_init_pins_sci,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7770)
- {
- .port = {
- .membase = (void *)0xff923000,
- .mapbase = 0xff923000,
- .iotype = UPIO_MEM,
- .irq = 61,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xff924000,
- .mapbase = 0xff924000,
- .iotype = UPIO_MEM,
- .irq = 62,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xff925000,
- .mapbase = 0xff925000,
- .iotype = UPIO_MEM,
- .irq = 63,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 2,
- },
- .type = PORT_SCIF,
- .irqs = SH7770_SCIF2_IRQS,
- .init_pins = sci_init_pins_scif,
- },
-#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
- {
- .port = {
- .membase = (void *)0xffe00000,
- .mapbase = 0xffe00000,
- .iotype = UPIO_MEM,
- .irq = 43,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 0,
- },
- .type = PORT_SCIF,
- .irqs = SH7780_SCIF0_IRQS,
- .init_pins = sci_init_pins_scif,
- },
- {
- .port = {
- .membase = (void *)0xffe10000,
- .mapbase = 0xffe10000,
- .iotype = UPIO_MEM,
- .irq = 79,
- .ops = &sci_uart_ops,
- .flags = UPF_BOOT_AUTOCONF,
- .line = 1,
- },
- .type = PORT_SCIF,
- .irqs = SH7780_SCIF1_IRQS,
- .init_pins = sci_init_pins_scif,
- },
+static void __init sci_init_ports(void)
+{
+ static int first = 1;
+ int i;
+
+ if (!first)
+ return;
+
+ first = 0;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ sci_ports[i].port.ops = &sci_uart_ops;
+ sci_ports[i].port.iotype = UPIO_MEM;
+ sci_ports[i].port.line = i;
+ sci_ports[i].port.fifosize = 1;
+
+#if defined(__H8300H__) || defined(__H8300S__)
+#ifdef __H8300S__
+ sci_ports[i].enable = h8300_sci_enable;
+ sci_ports[i].disable = h8300_sci_disable;
+#endif
+ sci_ports[i].port.uartclk = CONFIG_CPU_CLOCK;
+#elif defined(CONFIG_SUPERH64)
+ sci_ports[i].port.uartclk = current_cpu_data.module_clock * 16;
#else
-#error "CPU subtype not defined"
+ /*
+ * XXX: We should use a proper SCI/SCIF clock
+ */
+ {
+ struct clk *clk = clk_get("module_clk");
+ sci_ports[i].port.uartclk = clk_get_rate(clk) * 16;
+ clk_put(clk);
+ }
#endif
-};
+
+ sci_ports[i].break_timer.data = (unsigned long)&sci_ports[i];
+ sci_ports[i].break_timer.function = sci_break_timer;
+
+ init_timer(&sci_ports[i].break_timer);
+ }
+}
+
+int __init early_sci_setup(struct uart_port *port)
+{
+ if (unlikely(port->line > SCI_NPORTS))
+ return -ENODEV;
+
+ sci_init_ports();
+
+ sci_ports[port->line].port.membase = port->membase;
+ sci_ports[port->line].port.mapbase = port->mapbase;
+ sci_ports[port->line].port.type = port->type;
+
+ return 0;
+}
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
/*
@@ -1559,34 +1177,38 @@ static int __init serial_console_setup(struct console *co, char *options)
int flow = 'n';
int ret;
+ /*
+ * Check whether an invalid uart number has been specified, and
+ * if so, search for the first available port that does have
+ * console support.
+ */
+ if (co->index >= SCI_NPORTS)
+ co->index = 0;
+
serial_console_port = &sci_ports[co->index];
port = &serial_console_port->port;
- port->type = serial_console_port->type;
-
-#ifdef CONFIG_SUPERH64
- /* This is especially needed on sh64 to remap the SCIF */
- sci_config_port(port, 0);
-#endif
/*
- * We need to set the initial uartclk here, since otherwise it will
- * only ever be setup at sci_init() time.
+ * Also need to check port->type, we don't actually have any
+ * UPIO_PORT ports, but uart_report_port() handily misreports
+ * it anyways if we don't have a port available by the time this is
+ * called.
*/
-#if defined(__H8300H__) || defined(__H8300S__)
- port->uartclk = CONFIG_CPU_CLOCK;
+ if (!port->type)
+ return -ENODEV;
+ if (!port->membase || !port->mapbase)
+ return -ENODEV;
+
+ spin_lock_init(&port->lock);
+
+ port->type = serial_console_port->type;
+
+ if (port->flags & UPF_IOREMAP)
+ sci_config_port(port, 0);
+
+ if (serial_console_port->enable)
+ serial_console_port->enable(port);
-#if defined(__H8300S__)
- h8300_sci_enable(port, sci_enable);
-#endif
-#elif defined(CONFIG_SUPERH64)
- port->uartclk = current_cpu_data.module_clock * 16;
-#else
- {
- struct clk *clk = clk_get("module_clk");
- port->uartclk = clk_get_rate(clk) * 16;
- clk_put(clk);
- }
-#endif
if (options)
uart_parse_options(options, &baud, &parity, &bits, &flow);
@@ -1604,17 +1226,17 @@ static struct console serial_console = {
.device = uart_console_device,
.write = serial_console_write,
.setup = serial_console_setup,
- .flags = CON_PRINTBUFFER,
+ .flags = CON_PRINTBUFFER,
.index = -1,
.data = &sci_uart_driver,
};
static int __init sci_console_init(void)
{
+ sci_init_ports();
register_console(&serial_console);
return 0;
}
-
console_initcall(sci_console_init);
#endif /* CONFIG_SERIAL_SH_SCI_CONSOLE */
@@ -1649,6 +1271,8 @@ int __init kgdb_console_setup(struct console *co, char *options)
int parity = 'n';
int flow = 'n';
+ spin_lock_init(&port->lock);
+
if (co->index != kgdb_portnum)
co->index = kgdb_portnum;
@@ -1677,10 +1301,10 @@ static struct console kgdb_console = {
/* Register the KGDB console so we get messages (d'oh!) */
static int __init kgdb_console_init(void)
{
+ sci_init_ports();
register_console(&kgdb_console);
return 0;
}
-
console_initcall(kgdb_console_init);
#endif /* CONFIG_SH_KGDB_CONSOLE */
@@ -1701,60 +1325,132 @@ static struct uart_driver sci_uart_driver = {
.dev_name = "ttySC",
.major = SCI_MAJOR,
.minor = SCI_MINOR_START,
+ .nr = SCI_NPORTS,
.cons = SCI_CONSOLE,
};
-static int __init sci_init(void)
+/*
+ * Register a set of serial devices attached to a platform device. The
+ * list is terminated with a zero flags entry, which means we expect
+ * all entries to have at least UPF_BOOT_AUTOCONF set. Platforms that need
+ * remapping (such as sh64) should also set UPF_IOREMAP.
+ */
+static int __devinit sci_probe(struct platform_device *dev)
{
- int chan, ret;
+ struct plat_sci_port *p = dev->dev.platform_data;
+ int i;
- printk("%s", banner);
+ for (i = 0; p && p->flags != 0 && i < SCI_NPORTS; p++, i++) {
+ struct sci_port *sciport = &sci_ports[i];
- sci_uart_driver.nr = ARRAY_SIZE(sci_ports);
+ sciport->port.mapbase = p->mapbase;
- ret = uart_register_driver(&sci_uart_driver);
- if (ret == 0) {
- for (chan = 0; chan < SCI_NPORTS; chan++) {
- struct sci_port *sciport = &sci_ports[chan];
+ /*
+ * For the simple (and majority of) cases where we don't need
+ * to do any remapping, just cast the cookie directly.
+ */
+ if (p->mapbase && !p->membase && !(p->flags & UPF_IOREMAP))
+ p->membase = (void __iomem *)p->mapbase;
-#if defined(__H8300H__) || defined(__H8300S__)
- sciport->port.uartclk = CONFIG_CPU_CLOCK;
-#elif defined(CONFIG_SUPERH64)
- sciport->port.uartclk = current_cpu_data.module_clock * 16;
-#else
- struct clk *clk = clk_get("module_clk");
- sciport->port.uartclk = clk_get_rate(clk) * 16;
- clk_put(clk);
-#endif
- uart_add_one_port(&sci_uart_driver, &sciport->port);
- sciport->break_timer.data = (unsigned long)sciport;
- sciport->break_timer.function = sci_break_timer;
- init_timer(&sciport->break_timer);
- }
+ sciport->port.membase = p->membase;
+
+ sciport->port.irq = p->irqs[SCIx_TXI_IRQ];
+ sciport->port.flags = p->flags;
+ sciport->port.dev = &dev->dev;
+
+ sciport->type = sciport->port.type = p->type;
+
+ memcpy(&sciport->irqs, &p->irqs, sizeof(p->irqs));
+
+ uart_add_one_port(&sci_uart_driver, &sciport->port);
}
#ifdef CONFIG_CPU_FREQ
cpufreq_register_notifier(&sci_nb, CPUFREQ_TRANSITION_NOTIFIER);
- printk("sci: CPU frequency notifier registered\n");
+ dev_info(&dev->dev, "sci: CPU frequency notifier registered\n");
#endif
#ifdef CONFIG_SH_STANDARD_BIOS
sh_bios_gdb_detach();
#endif
- return ret;
+ return 0;
}
-static void __exit sci_exit(void)
+static int __devexit sci_remove(struct platform_device *dev)
+{
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++)
+ uart_remove_one_port(&sci_uart_driver, &sci_ports[i].port);
+
+ return 0;
+}
+
+static int sci_suspend(struct platform_device *dev, pm_message_t state)
{
- int chan;
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ struct sci_port *p = &sci_ports[i];
+
+ if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
+ uart_suspend_port(&sci_uart_driver, &p->port);
+ }
- for (chan = 0; chan < SCI_NPORTS; chan++)
- uart_remove_one_port(&sci_uart_driver, &sci_ports[chan].port);
+ return 0;
+}
+static int sci_resume(struct platform_device *dev)
+{
+ int i;
+
+ for (i = 0; i < SCI_NPORTS; i++) {
+ struct sci_port *p = &sci_ports[i];
+
+ if (p->type != PORT_UNKNOWN && p->port.dev == &dev->dev)
+ uart_resume_port(&sci_uart_driver, &p->port);
+ }
+
+ return 0;
+}
+
+static struct platform_driver sci_driver = {
+ .probe = sci_probe,
+ .remove = __devexit_p(sci_remove),
+ .suspend = sci_suspend,
+ .resume = sci_resume,
+ .driver = {
+ .name = "sh-sci",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init sci_init(void)
+{
+ int ret;
+
+ printk(banner);
+
+ sci_init_ports();
+
+ ret = uart_register_driver(&sci_uart_driver);
+ if (likely(ret == 0)) {
+ ret = platform_driver_register(&sci_driver);
+ if (unlikely(ret))
+ uart_unregister_driver(&sci_uart_driver);
+ }
+
+ return ret;
+}
+
+static void __exit sci_exit(void)
+{
+ platform_driver_unregister(&sci_driver);
uart_unregister_driver(&sci_uart_driver);
}
module_init(sci_init);
module_exit(sci_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h
index ab320fa3237..28643c4dc85 100644
--- a/drivers/serial/sh-sci.h
+++ b/drivers/serial/sh-sci.h
@@ -10,7 +10,9 @@
* Modified to support SH7300(SH-Mobile) SCIF. Takashi Kusuda (Jun 2003).
* Modified to support H8/300 Series Yoshinori Sato (Feb 2004).
*/
+#include <linux/config.h>
#include <linux/serial_core.h>
+#include <asm/io.h>
#if defined(__H8300H__) || defined(__H8300S__)
#include <asm/gpio.h>
@@ -22,40 +24,13 @@
#endif
#endif
-/* Offsets into the sci_port->irqs array */
-#define SCIx_ERI_IRQ 0
-#define SCIx_RXI_IRQ 1
-#define SCIx_TXI_IRQ 2
-
-/* ERI, RXI, TXI, BRI */
-#define SCI_IRQS { 23, 24, 25, 0 }
-#define SH3_SCIF_IRQS { 56, 57, 59, 58 }
-#define SH3_IRDA_IRQS { 52, 53, 55, 54 }
-#define SH4_SCIF_IRQS { 40, 41, 43, 42 }
-#define STB1_SCIF1_IRQS {23, 24, 26, 25 }
-#define SH7760_SCIF0_IRQS { 52, 53, 55, 54 }
-#define SH7760_SCIF1_IRQS { 72, 73, 75, 74 }
-#define SH7760_SCIF2_IRQS { 76, 77, 79, 78 }
-#define SH7300_SCIF0_IRQS {80, 80, 80, 80 }
-#define SH73180_SCIF_IRQS {80, 81, 83, 82 }
-#define H8300H_SCI_IRQS0 {52, 53, 54, 0 }
-#define H8300H_SCI_IRQS1 {56, 57, 58, 0 }
-#define H8300H_SCI_IRQS2 {60, 61, 62, 0 }
-#define H8S_SCI_IRQS0 {88, 89, 90, 0 }
-#define H8S_SCI_IRQS1 {92, 93, 94, 0 }
-#define H8S_SCI_IRQS2 {96, 97, 98, 0 }
-#define SH5_SCIF_IRQS {39, 40, 42, 0 }
-#define SH7770_SCIF0_IRQS {61, 61, 61, 61 }
-#define SH7770_SCIF1_IRQS {62, 62, 62, 62 }
-#define SH7770_SCIF2_IRQS {63, 63, 63, 63 }
-#define SH7780_SCIF0_IRQS {40, 41, 43, 42 }
-#define SH7780_SCIF1_IRQS {76, 77, 79, 78 }
-
#if defined(CONFIG_CPU_SUBTYPE_SH7708)
# define SCSPTR 0xffffff7c /* 8 bit */
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCI_ONLY
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7706)
# define SCPCR 0xA4000116 /* 16 bit SCI and SCIF */
# define SCPDR 0xA4000136 /* 8 bit SCI and SCIF */
# define SCSCR_INIT(port) 0x30 /* TIE=0,RIE=0,TE=1,RE=1 */
@@ -99,12 +74,23 @@
# define SCPDR 0xA4050136 /* 16 bit SCIF */
# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCIF_ONLY
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
+# define SCSPTR0 0xA4400000 /* 16 bit SCIF */
+# define SCSCR_INIT(port) 0x0030 /* TIE=0,RIE=0,TE=1,RE=1 */
+# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH73180)
# define SCPDR 0xA4050138 /* 16 bit SCIF */
# define SCSPTR2 SCPDR
# define SCIF_ORER 0x0001 /* overrun error bit */
# define SCSCR_INIT(port) 0x0038 /* TIE=0,RIE=0,TE=1,RE=1 */
# define SCIF_ONLY
+#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
+# define SCSPTR0 0xffe00010 /* 16 bit SCIF */
+# define SCSPTR1 0xffe10010 /* 16 bit SCIF */
+# define SCSPTR2 0xffe20010 /* 16 bit SCIF */
+# define SCSPTR3 0xffe30010 /* 16 bit SCIF */
+# define SCSCR_INIT(port) 0x32 /* TIE=0,RIE=0,TE=1,RE=1,REIE=0,CKE=1 */
+# define SCIF_ONLY
#elif defined(CONFIG_CPU_SUBTYPE_SH4_202)
# define SCSPTR2 0xffe80020 /* 16 bit SCIF */
# define SCIF_ORER 0x0001 /* overrun error bit */
@@ -145,7 +131,7 @@
#elif defined(CONFIG_CPU_SUBTYPE_SH7780)
# define SCSPTR0 0xffe00024 /* 16 bit SCIF */
# define SCSPTR1 0xffe10024 /* 16 bit SCIF */
-# define SCIF_OPER 0x0001 /* Overrun error bit */
+# define SCIF_ORER 0x0001 /* Overrun error bit */
# define SCSCR_INIT(port) 0x3a /* TIE=0,RIE=0,TE=1,RE=1,REIE=1 */
# define SCIF_ONLY
#else
@@ -273,15 +259,6 @@
*/
#define SCI_EVENT_WRITE_WAKEUP 0
-struct sci_port {
- struct uart_port port;
- int type;
- unsigned char irqs[4]; /* ERI, RXI, TXI, BRI */
- void (*init_pins)(struct uart_port *port, unsigned int cflag);
- int break_flag;
- struct timer_list break_timer;
-};
-
#define SCI_IN(size, offset) \
unsigned int addr = port->mapbase + (offset); \
if ((size) == 8) { \
@@ -336,7 +313,9 @@ struct sci_port {
}
#ifdef CONFIG_CPU_SH3
-#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7710)
#define SCIF_FNS(name, scif_offset, scif_size) \
CPU_SCIF_FNS(name, scif_offset, scif_size)
#else
@@ -362,7 +341,9 @@ struct sci_port {
CPU_SCIF_FNS(name, sh4_scif_offset, sh4_scif_size)
#endif
-#if defined(CONFIG_CPU_SUBTYPE_SH7300) || defined(CONFIG_CPU_SUBTYPE_SH7705)
+#if defined(CONFIG_CPU_SUBTYPE_SH7300) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7705) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7710)
SCIF_FNS(SCSMR, 0x00, 16)
SCIF_FNS(SCBRR, 0x04, 8)
SCIF_FNS(SCSCR, 0x08, 16)
@@ -447,7 +428,9 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inb(SCSPTR)&0x01 ? 1 : 0; /* SCI */
return 1;
}
-#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || defined(CONFIG_CPU_SUBTYPE_SH7709)
+#elif defined(CONFIG_CPU_SUBTYPE_SH7707) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7709) || \
+ defined(CONFIG_CPU_SUBTYPE_SH7706)
static inline int sci_rxd_in(struct uart_port *port)
{
if (port->mapbase == 0xfffffe80)
@@ -467,6 +450,13 @@ static inline int sci_rxd_in(struct uart_port *port)
return ctrl_inb(SCPDR)&0x10 ? 1 : 0; /* SCIF */
return 1;
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7710)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == SCSPTR0)
+ return ctrl_inw(SCSPTR0 + 0x10) & 0x01 ? 1 : 0;
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_SH7750) || \
defined(CONFIG_CPU_SUBTYPE_SH7751) || \
defined(CONFIG_CPU_SUBTYPE_SH4_202)
@@ -504,6 +494,19 @@ static inline int sci_rxd_in(struct uart_port *port)
{
return ctrl_inb(SCPDR)&0x01 ? 1 : 0; /* SCIF0 */
}
+#elif defined(CONFIG_CPU_SUBTYPE_SH7343)
+static inline int sci_rxd_in(struct uart_port *port)
+{
+ if (port->mapbase == 0xffe00000)
+ return ctrl_inw(SCSPTR0) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe10000)
+ return ctrl_inw(SCSPTR1) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe20000)
+ return ctrl_inw(SCSPTR2) & 0x0001 ? 1 : 0; /* SCIF */
+ if (port->mapbase == 0xffe30000)
+ return ctrl_inw(SCSPTR3) & 0x0001 ? 1 : 0; /* SCIF */
+ return 1;
+}
#elif defined(CONFIG_CPU_SUBTYPE_ST40STB1)
static inline int sci_rxd_in(struct uart_port *port)
{
@@ -587,4 +590,3 @@ static inline int sci_rxd_in(struct uart_port *port)
#else /* Generic SH */
#define SCBRR_VALUE(bps, clk) ((clk+16*bps)/(32*bps)-1)
#endif
-
diff --git a/drivers/serial/sunsu.c b/drivers/serial/sunsu.c
index d3a5aeee73a..9b3b9aaa6b9 100644
--- a/drivers/serial/sunsu.c
+++ b/drivers/serial/sunsu.c
@@ -1499,6 +1499,9 @@ static int __devexit su_remove(struct of_device *dev)
uart_remove_one_port(&sunsu_reg, &up->port);
}
+ if (up->port.membase)
+ of_iounmap(up->port.membase, up->reg_size);
+
dev_set_drvdata(&dev->dev, NULL);
return 0;
diff --git a/drivers/serial/sunzilog.c b/drivers/serial/sunzilog.c
index d34f336d53d..0da3ebfff82 100644
--- a/drivers/serial/sunzilog.c
+++ b/drivers/serial/sunzilog.c
@@ -1270,7 +1270,7 @@ static void __init sunzilog_register_serio(struct uart_sunzilog_port *up)
}
#endif
-static void __init sunzilog_init_hw(struct uart_sunzilog_port *up)
+static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
{
struct zilog_channel __iomem *channel;
unsigned long flags;
diff --git a/drivers/tc/zs.c b/drivers/tc/zs.c
index 5e8a27620f6..622881f2676 100644
--- a/drivers/tc/zs.c
+++ b/drivers/tc/zs.c
@@ -1701,7 +1701,7 @@ static void __init probe_sccs(void)
spin_unlock_irqrestore(&zs_lock, flags);
}
-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = rs_open,
.close = rs_close,
.write = rs_write,
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 00504319752..f9b1719b9a3 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -25,6 +25,7 @@ config USB_ARCH_HAS_OHCI
default y if PXA27x
default y if ARCH_EP93XX
default y if (ARCH_AT91RM9200 || ARCH_AT91SAM9261)
+ default y if ARCH_PNX4008
# PPC:
default y if STB03xxx
default y if PPC_MPC52xx
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 4710eb02ed6..97d57cfc343 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_USB_ISP116X_HCD) += host/
obj-$(CONFIG_USB_OHCI_HCD) += host/
obj-$(CONFIG_USB_UHCI_HCD) += host/
obj-$(CONFIG_USB_SL811_HCD) += host/
+obj-$(CONFIG_USB_U132_HCD) += host/
obj-$(CONFIG_ETRAX_USB_HOST) += host/
obj-$(CONFIG_USB_OHCI_AT91) += host/
@@ -23,6 +24,7 @@ obj-$(CONFIG_USB_PRINTER) += class/
obj-$(CONFIG_USB_STORAGE) += storage/
obj-$(CONFIG_USB) += storage/
+obj-$(CONFIG_USB_ACECAD) += input/
obj-$(CONFIG_USB_AIPTEK) += input/
obj-$(CONFIG_USB_ATI_REMOTE) += input/
obj-$(CONFIG_USB_HID) += input/
@@ -31,8 +33,8 @@ obj-$(CONFIG_USB_KBTAB) += input/
obj-$(CONFIG_USB_MOUSE) += input/
obj-$(CONFIG_USB_MTOUCH) += input/
obj-$(CONFIG_USB_POWERMATE) += input/
+obj-$(CONFIG_USB_TRANCEVIBRATOR)+= input/
obj-$(CONFIG_USB_WACOM) += input/
-obj-$(CONFIG_USB_ACECAD) += input/
obj-$(CONFIG_USB_XPAD) += input/
obj-$(CONFIG_USB_CATC) += net/
@@ -47,22 +49,24 @@ obj-$(CONFIG_USB_MICROTEK) += image/
obj-$(CONFIG_USB_SERIAL) += serial/
+obj-$(CONFIG_USB_ADUTUX) += misc/
+obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_AUERSWALD) += misc/
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= misc/
obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_EMI62) += misc/
+obj-$(CONFIG_USB_FTDI_ELAN) += misc/
obj-$(CONFIG_USB_IDMOUSE) += misc/
obj-$(CONFIG_USB_LCD) += misc/
obj-$(CONFIG_USB_LD) += misc/
obj-$(CONFIG_USB_LED) += misc/
obj-$(CONFIG_USB_LEGOTOWER) += misc/
+obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
obj-$(CONFIG_USB_RIO500) += misc/
+obj-$(CONFIG_USB_SISUSBVGA) += misc/
obj-$(CONFIG_USB_TEST) += misc/
obj-$(CONFIG_USB_USS720) += misc/
-obj-$(CONFIG_USB_PHIDGETSERVO) += misc/
-obj-$(CONFIG_USB_SISUSBVGA) += misc/
-obj-$(CONFIG_USB_APPLEDISPLAY) += misc/
obj-$(CONFIG_USB_ATM) += atm/
obj-$(CONFIG_USB_SPEEDTOUCH) += atm/
diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c
index b38990adf1c..465961a26e4 100644
--- a/drivers/usb/atm/ueagle-atm.c
+++ b/drivers/usb/atm/ueagle-atm.c
@@ -1621,26 +1621,32 @@ static int claim_interface(struct usb_device *usb_dev,
return ret;
}
-static void create_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static struct attribute *attrs[] = {
+ &dev_attr_stat_status.attr,
+ &dev_attr_stat_mflags.attr,
+ &dev_attr_stat_human_status.attr,
+ &dev_attr_stat_delin.attr,
+ &dev_attr_stat_vidcpe.attr,
+ &dev_attr_stat_usrate.attr,
+ &dev_attr_stat_dsrate.attr,
+ &dev_attr_stat_usattenuation.attr,
+ &dev_attr_stat_dsattenuation.attr,
+ &dev_attr_stat_usmargin.attr,
+ &dev_attr_stat_dsmargin.attr,
+ &dev_attr_stat_txflow.attr,
+ &dev_attr_stat_rxflow.attr,
+ &dev_attr_stat_uscorr.attr,
+ &dev_attr_stat_dscorr.attr,
+ &dev_attr_stat_usunc.attr,
+ &dev_attr_stat_dsunc.attr,
+};
+static struct attribute_group attr_grp = {
+ .attrs = attrs,
+};
+
+static int create_fs_entries(struct usb_interface *intf)
{
- /* sysfs interface */
- device_create_file(&intf->dev, &dev_attr_stat_status);
- device_create_file(&intf->dev, &dev_attr_stat_mflags);
- device_create_file(&intf->dev, &dev_attr_stat_human_status);
- device_create_file(&intf->dev, &dev_attr_stat_delin);
- device_create_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_create_file(&intf->dev, &dev_attr_stat_usrate);
- device_create_file(&intf->dev, &dev_attr_stat_dsrate);
- device_create_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_create_file(&intf->dev, &dev_attr_stat_usmargin);
- device_create_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_create_file(&intf->dev, &dev_attr_stat_txflow);
- device_create_file(&intf->dev, &dev_attr_stat_rxflow);
- device_create_file(&intf->dev, &dev_attr_stat_uscorr);
- device_create_file(&intf->dev, &dev_attr_stat_dscorr);
- device_create_file(&intf->dev, &dev_attr_stat_usunc);
- device_create_file(&intf->dev, &dev_attr_stat_dsunc);
+ return sysfs_create_group(&intf->dev.kobj, &attr_grp);
}
static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
@@ -1708,37 +1714,25 @@ static int uea_bind(struct usbatm_data *usbatm, struct usb_interface *intf,
return ret;
}
- create_fs_entries(sc, intf);
+ ret = create_fs_entries(intf);
+ if (ret) {
+ uea_stop(sc);
+ kfree(sc);
+ return ret;
+ }
return 0;
}
-static void destroy_fs_entries(struct uea_softc *sc, struct usb_interface *intf)
+static void destroy_fs_entries(struct usb_interface *intf)
{
- /* sysfs interface */
- device_remove_file(&intf->dev, &dev_attr_stat_status);
- device_remove_file(&intf->dev, &dev_attr_stat_mflags);
- device_remove_file(&intf->dev, &dev_attr_stat_human_status);
- device_remove_file(&intf->dev, &dev_attr_stat_delin);
- device_remove_file(&intf->dev, &dev_attr_stat_vidcpe);
- device_remove_file(&intf->dev, &dev_attr_stat_usrate);
- device_remove_file(&intf->dev, &dev_attr_stat_dsrate);
- device_remove_file(&intf->dev, &dev_attr_stat_usattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_dsattenuation);
- device_remove_file(&intf->dev, &dev_attr_stat_usmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_dsmargin);
- device_remove_file(&intf->dev, &dev_attr_stat_txflow);
- device_remove_file(&intf->dev, &dev_attr_stat_rxflow);
- device_remove_file(&intf->dev, &dev_attr_stat_uscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_dscorr);
- device_remove_file(&intf->dev, &dev_attr_stat_usunc);
- device_remove_file(&intf->dev, &dev_attr_stat_dsunc);
+ sysfs_remove_group(&intf->dev.kobj, &attr_grp);
}
static void uea_unbind(struct usbatm_data *usbatm, struct usb_interface *intf)
{
struct uea_softc *sc = usbatm->driver_data;
- destroy_fs_entries(sc, intf);
+ destroy_fs_entries(intf);
uea_stop(sc);
kfree(sc);
}
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index ca90326f2f5..71288295df2 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -1120,7 +1120,7 @@ static struct usb_driver acm_driver = {
* TTY driver structures.
*/
-static struct tty_operations acm_ops = {
+static const struct tty_operations acm_ops = {
.open = acm_tty_open,
.close = acm_tty_close,
.write = acm_tty_write,
diff --git a/drivers/usb/class/usblp.c b/drivers/usb/class/usblp.c
index 48dee4b8d8e..9cac11ca1bb 100644
--- a/drivers/usb/class/usblp.c
+++ b/drivers/usb/class/usblp.c
@@ -813,7 +813,7 @@ static unsigned int usblp_quirks (__u16 vendor, __u16 product)
return 0;
}
-static struct file_operations usblp_fops = {
+static const struct file_operations usblp_fops = {
.owner = THIS_MODULE,
.read = usblp_read,
.write = usblp_write,
@@ -927,7 +927,9 @@ static int usblp_probe(struct usb_interface *intf,
/* Retrieve and store the device ID string. */
usblp_cache_device_id_string(usblp);
- device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+ retval = device_create_file(&intf->dev, &dev_attr_ieee1284_id);
+ if (retval)
+ goto abort_intfdata;
#ifdef DEBUG
usblp_check_status(usblp, 0);
@@ -1021,18 +1023,13 @@ static int usblp_select_alts(struct usblp *usblp)
for (e = 0; e < ifd->desc.bNumEndpoints; e++) {
epd = &ifd->endpoint[e].desc;
- if ((epd->bmAttributes&USB_ENDPOINT_XFERTYPE_MASK)!=
- USB_ENDPOINT_XFER_BULK)
- continue;
-
- if (!(epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK)) {
+ if (usb_endpoint_is_bulk_out(epd))
if (!epwrite)
epwrite = epd;
- } else {
+ if (usb_endpoint_is_bulk_in(epd))
if (!epread)
epread = epd;
- }
}
/* Ignore buggy hardware without the right endpoints. */
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ec510922af6..34e9bac319b 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -4,7 +4,7 @@
usbcore-objs := usb.o hub.o hcd.o urb.o message.o driver.o \
config.o file.o buffer.o sysfs.o endpoint.o \
- devio.o notify.o
+ devio.o notify.o generic.o
ifeq ($(CONFIG_PCI),y)
usbcore-objs += hcd-pci.o
diff --git a/drivers/usb/core/buffer.c b/drivers/usb/core/buffer.c
index f4f4ef0f377..840442a25b6 100644
--- a/drivers/usb/core/buffer.c
+++ b/drivers/usb/core/buffer.c
@@ -104,7 +104,7 @@ void *hcd_buffer_alloc (
dma_addr_t *dma
)
{
- struct usb_hcd *hcd = bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
/* some USB hosts just use PIO */
@@ -127,7 +127,7 @@ void hcd_buffer_free (
dma_addr_t dma
)
{
- struct usb_hcd *hcd = bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(bus);
int i;
if (!addr)
diff --git a/drivers/usb/core/config.c b/drivers/usb/core/config.c
index 4c9e63e665b..bfb3731d42d 100644
--- a/drivers/usb/core/config.c
+++ b/drivers/usb/core/config.c
@@ -475,7 +475,9 @@ int usb_get_configuration(struct usb_device *dev)
if (result < 0) {
dev_err(ddev, "unable to read config index %d "
"descriptor/%s\n", cfgno, "start");
- goto err;
+ dev_err(ddev, "chopping to %d config(s)\n", cfgno);
+ dev->descriptor.bNumConfigurations = cfgno;
+ break;
} else if (result < 4) {
dev_err(ddev, "config index %d descriptor too short "
"(expected %i, got %i)\n", cfgno,
diff --git a/drivers/usb/core/devices.c b/drivers/usb/core/devices.c
index c0f37343a27..3538c2fdadf 100644
--- a/drivers/usb/core/devices.c
+++ b/drivers/usb/core/devices.c
@@ -593,7 +593,7 @@ static ssize_t usb_device_read(struct file *file, char __user *buf, size_t nbyte
/* Kernel lock for "lastev" protection */
static unsigned int usb_device_poll(struct file *file, struct poll_table_struct *wait)
{
- struct usb_device_status *st = (struct usb_device_status *)file->private_data;
+ struct usb_device_status *st = file->private_data;
unsigned int mask = 0;
lock_kernel();
@@ -603,7 +603,7 @@ static unsigned int usb_device_poll(struct file *file, struct poll_table_struct
unlock_kernel();
return POLLIN;
}
-
+
/* we may have dropped BKL - need to check for having lost the race */
if (file->private_data) {
kfree(st);
@@ -667,7 +667,7 @@ static loff_t usb_device_lseek(struct file * file, loff_t offset, int orig)
return ret;
}
-struct file_operations usbfs_devices_fops = {
+const struct file_operations usbfs_devices_fops = {
.llseek = usb_device_lseek,
.read = usb_device_read,
.poll = usb_device_poll,
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c
index 218621b9958..3f509beb88e 100644
--- a/drivers/usb/core/devio.c
+++ b/drivers/usb/core/devio.c
@@ -59,10 +59,13 @@
#define USB_DEVICE_MAX USB_MAXBUS * 128
static struct class *usb_device_class;
+/* Mutual exclusion for removal, open, and release */
+DEFINE_MUTEX(usbfs_mutex);
+
struct async {
struct list_head asynclist;
struct dev_state *ps;
- pid_t pid;
+ struct pid *pid;
uid_t uid, euid;
unsigned int signr;
unsigned int ifnum;
@@ -87,9 +90,10 @@ MODULE_PARM_DESC (usbfs_snoop, "true to log all usbfs traffic");
#define MAX_USBFS_BUFFER_SIZE 16384
-static inline int connected (struct usb_device *dev)
+static inline int connected (struct dev_state *ps)
{
- return dev->state != USB_STATE_NOTATTACHED;
+ return (!list_empty(&ps->list) &&
+ ps->dev->state != USB_STATE_NOTATTACHED);
}
static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
@@ -118,7 +122,7 @@ static loff_t usbdev_lseek(struct file *file, loff_t offset, int orig)
static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, loff_t *ppos)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
ssize_t ret = 0;
unsigned len;
@@ -127,7 +131,7 @@ static ssize_t usbdev_read(struct file *file, char __user *buf, size_t nbytes, l
pos = *ppos;
usb_lock_device(dev);
- if (!connected(dev)) {
+ if (!connected(ps)) {
ret = -ENODEV;
goto err;
} else if (pos < 0) {
@@ -221,6 +225,7 @@ static struct async *alloc_async(unsigned int numisoframes)
static void free_async(struct async *as)
{
+ put_pid(as->pid);
kfree(as->urb->transfer_buffer);
kfree(as->urb->setup_packet);
usb_free_urb(as->urb);
@@ -301,7 +306,7 @@ static void snoop_urb(struct urb *urb, void __user *userurb)
static void async_completed(struct urb *urb, struct pt_regs *regs)
{
- struct async *as = (struct async *)urb->context;
+ struct async *as = urb->context;
struct dev_state *ps = as->ps;
struct siginfo sinfo;
@@ -313,7 +318,7 @@ static void async_completed(struct urb *urb, struct pt_regs *regs)
sinfo.si_errno = as->urb->status;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = as->userurb;
- kill_proc_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
+ kill_pid_info_as_uid(as->signr, &sinfo, as->pid, as->uid,
as->euid, as->secid);
}
snoop(&urb->dev->dev, "urb complete\n");
@@ -541,25 +546,25 @@ static int usbdev_open(struct inode *inode, struct file *file)
struct dev_state *ps;
int ret;
- /*
- * no locking necessary here, as chrdev_open has the kernel lock
- * (still acquire the kernel lock for safety)
- */
+ /* Protect against simultaneous removal or release */
+ mutex_lock(&usbfs_mutex);
+
ret = -ENOMEM;
if (!(ps = kmalloc(sizeof(struct dev_state), GFP_KERNEL)))
- goto out_nolock;
+ goto out;
- lock_kernel();
ret = -ENOENT;
/* check if we are called from a real node or usbfs */
if (imajor(inode) == USB_DEVICE_MAJOR)
dev = usbdev_lookup_minor(iminor(inode));
if (!dev)
- dev = inode->u.generic_ip;
- if (!dev) {
- kfree(ps);
+ dev = inode->i_private;
+ if (!dev)
goto out;
- }
+ ret = usb_autoresume_device(dev, 1);
+ if (ret)
+ goto out;
+
usb_get_dev(dev);
ret = 0;
ps->dev = dev;
@@ -569,7 +574,7 @@ static int usbdev_open(struct inode *inode, struct file *file)
INIT_LIST_HEAD(&ps->async_completed);
init_waitqueue_head(&ps->wait);
ps->discsignr = 0;
- ps->disc_pid = current->pid;
+ ps->disc_pid = get_pid(task_pid(current));
ps->disc_uid = current->uid;
ps->disc_euid = current->euid;
ps->disccontext = NULL;
@@ -579,30 +584,37 @@ static int usbdev_open(struct inode *inode, struct file *file)
list_add_tail(&ps->list, &dev->filelist);
file->private_data = ps;
out:
- unlock_kernel();
- out_nolock:
- return ret;
+ if (ret)
+ kfree(ps);
+ mutex_unlock(&usbfs_mutex);
+ return ret;
}
static int usbdev_release(struct inode *inode, struct file *file)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
unsigned int ifnum;
usb_lock_device(dev);
+
+ /* Protect against simultaneous open */
+ mutex_lock(&usbfs_mutex);
list_del_init(&ps->list);
+ mutex_unlock(&usbfs_mutex);
+
for (ifnum = 0; ps->ifclaimed && ifnum < 8*sizeof(ps->ifclaimed);
ifnum++) {
if (test_bit(ifnum, &ps->ifclaimed))
releaseintf(ps, ifnum);
}
destroy_all_async(ps);
+ usb_autosuspend_device(dev, 1);
usb_unlock_device(dev);
usb_put_dev(dev);
- ps->dev = NULL;
+ put_pid(ps->disc_pid);
kfree(ps);
- return 0;
+ return 0;
}
static int proc_control(struct dev_state *ps, void __user *arg)
@@ -1053,7 +1065,7 @@ static int proc_do_submiturb(struct dev_state *ps, struct usbdevfs_urb *uurb,
as->userbuffer = NULL;
as->signr = uurb->signr;
as->ifnum = ifnum;
- as->pid = current->pid;
+ as->pid = get_pid(task_pid(current));
as->uid = current->uid;
as->euid = current->euid;
security_task_getsecid(current, &as->secid);
@@ -1322,7 +1334,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
}
}
- if (!connected(ps->dev)) {
+ if (!connected(ps)) {
kfree(buf);
return -ENODEV;
}
@@ -1349,7 +1361,7 @@ static int proc_ioctl(struct dev_state *ps, struct usbdevfs_ioctl *ctl)
/* let kernel drivers try to (re)bind to the interface */
case USBDEVFS_CONNECT:
usb_unlock_device(ps->dev);
- bus_rescan_devices(intf->dev.bus);
+ retval = bus_rescan_devices(intf->dev.bus);
usb_lock_device(ps->dev);
break;
@@ -1413,7 +1425,7 @@ static int proc_ioctl_compat(struct dev_state *ps, compat_uptr_t arg)
*/
static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
+ struct dev_state *ps = file->private_data;
struct usb_device *dev = ps->dev;
void __user *p = (void __user *)arg;
int ret = -ENOTTY;
@@ -1421,7 +1433,7 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
if (!(file->f_mode & FMODE_WRITE))
return -EPERM;
usb_lock_device(dev);
- if (!connected(dev)) {
+ if (!connected(ps)) {
usb_unlock_device(dev);
return -ENODEV;
}
@@ -1556,18 +1568,18 @@ static int usbdev_ioctl(struct inode *inode, struct file *file, unsigned int cmd
/* No kernel lock - fine */
static unsigned int usbdev_poll(struct file *file, struct poll_table_struct *wait)
{
- struct dev_state *ps = (struct dev_state *)file->private_data;
- unsigned int mask = 0;
+ struct dev_state *ps = file->private_data;
+ unsigned int mask = 0;
poll_wait(file, &ps->wait, wait);
if (file->f_mode & FMODE_WRITE && !list_empty(&ps->async_completed))
mask |= POLLOUT | POLLWRNORM;
- if (!connected(ps->dev))
+ if (!connected(ps))
mask |= POLLERR | POLLHUP;
return mask;
}
-struct file_operations usbfs_device_file_operations = {
+const struct file_operations usbfs_device_file_operations = {
.llseek = usbdev_lseek,
.read = usbdev_read,
.poll = usbdev_poll,
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index ec890650141..113e484c763 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -17,12 +17,14 @@
*
* NOTE! This is not actually a driver at all, rather this is
* just a collection of helper routines that implement the
- * generic USB things that the real drivers can use..
+ * matching, probing, releasing, suspending and resuming for
+ * real drivers.
*
*/
#include <linux/device.h>
#include <linux/usb.h>
+#include <linux/workqueue.h>
#include "hcd.h"
#include "usb.h"
@@ -34,38 +36,6 @@ struct usb_dynid {
struct usb_device_id id;
};
-
-static int generic_probe(struct device *dev)
-{
- return 0;
-}
-static int generic_remove(struct device *dev)
-{
- struct usb_device *udev = to_usb_device(dev);
-
- /* if this is only an unbind, not a physical disconnect, then
- * unconfigure the device */
- if (udev->state == USB_STATE_CONFIGURED)
- usb_set_configuration(udev, 0);
-
- /* in case the call failed or the device was suspended */
- if (udev->state >= USB_STATE_CONFIGURED)
- usb_disable_device(udev, 0);
- return 0;
-}
-
-struct device_driver usb_generic_driver = {
- .owner = THIS_MODULE,
- .name = "usb",
- .bus = &usb_bus_type,
- .probe = generic_probe,
- .remove = generic_remove,
-};
-
-/* Fun hack to determine if the struct device is a
- * usb device or a usb interface. */
-int usb_generic_driver_data;
-
#ifdef CONFIG_HOTPLUG
/*
@@ -80,6 +50,7 @@ static ssize_t store_new_id(struct device_driver *driver,
u32 idVendor = 0;
u32 idProduct = 0;
int fields = 0;
+ int retval = 0;
fields = sscanf(buf, "%x %x", &idVendor, &idProduct);
if (fields < 2)
@@ -99,10 +70,12 @@ static ssize_t store_new_id(struct device_driver *driver,
spin_unlock(&usb_drv->dynids.lock);
if (get_driver(driver)) {
- driver_attach(driver);
+ retval = driver_attach(driver);
put_driver(driver);
}
+ if (retval)
+ return retval;
return count;
}
static DRIVER_ATTR(new_id, S_IWUSR, NULL, store_new_id);
@@ -115,7 +88,7 @@ static int usb_create_newid_file(struct usb_driver *usb_drv)
goto exit;
if (usb_drv->probe != NULL)
- error = sysfs_create_file(&usb_drv->driver.kobj,
+ error = sysfs_create_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
exit:
return error;
@@ -127,7 +100,7 @@ static void usb_remove_newid_file(struct usb_driver *usb_drv)
return;
if (usb_drv->probe != NULL)
- sysfs_remove_file(&usb_drv->driver.kobj,
+ sysfs_remove_file(&usb_drv->drvwrap.driver.kobj,
&driver_attr_new_id.attr);
}
@@ -174,21 +147,57 @@ static const struct usb_device_id *usb_match_dynamic_id(struct usb_interface *in
}
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
+static int usb_probe_device(struct device *dev)
+{
+ struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+ struct usb_device *udev;
+ int error = -ENODEV;
+
+ dev_dbg(dev, "%s\n", __FUNCTION__);
+
+ if (!is_usb_device(dev)) /* Sanity check */
+ return error;
+
+ udev = to_usb_device(dev);
+
+ /* TODO: Add real matching code */
+
+ /* The device should always appear to be in use
+ * unless the driver suports autosuspend.
+ */
+ udev->pm_usage_cnt = !(udriver->supports_autosuspend);
+
+ error = udriver->probe(udev);
+ return error;
+}
+
+/* called from driver core with dev locked */
+static int usb_unbind_device(struct device *dev)
+{
+ struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
+
+ udriver->disconnect(to_usb_device(dev));
+ return 0;
+}
+
+
+/* called from driver core with dev locked */
static int usb_probe_interface(struct device *dev)
{
- struct usb_interface * intf = to_usb_interface(dev);
- struct usb_driver * driver = to_usb_driver(dev->driver);
+ struct usb_driver *driver = to_usb_driver(dev->driver);
+ struct usb_interface *intf;
+ struct usb_device *udev;
const struct usb_device_id *id;
int error = -ENODEV;
dev_dbg(dev, "%s\n", __FUNCTION__);
- if (!driver->probe)
+ if (is_usb_device(dev)) /* Sanity check */
return error;
- /* FIXME we'd much prefer to just resume it ... */
- if (interface_to_usbdev(intf)->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
+
+ intf = to_usb_interface(dev);
+ udev = interface_to_usbdev(intf);
id = usb_match_id(intf, driver->id_table);
if (!id)
@@ -196,48 +205,165 @@ static int usb_probe_interface(struct device *dev)
if (id) {
dev_dbg(dev, "%s - got id\n", __FUNCTION__);
+ error = usb_autoresume_device(udev, 1);
+ if (error)
+ return error;
+
/* Interface "power state" doesn't correspond to any hardware
* state whatsoever. We use it to record when it's bound to
* a driver that may start I/0: it's not frozen/quiesced.
*/
mark_active(intf);
intf->condition = USB_INTERFACE_BINDING;
+
+ /* The interface should always appear to be in use
+ * unless the driver suports autosuspend.
+ */
+ intf->pm_usage_cnt = !(driver->supports_autosuspend);
+
error = driver->probe(intf, id);
if (error) {
mark_quiesced(intf);
+ intf->needs_remote_wakeup = 0;
intf->condition = USB_INTERFACE_UNBOUND;
} else
intf->condition = USB_INTERFACE_BOUND;
+
+ usb_autosuspend_device(udev, 1);
}
return error;
}
-/* called from driver core with usb_bus_type.subsys writelock */
+/* called from driver core with dev locked */
static int usb_unbind_interface(struct device *dev)
{
+ struct usb_driver *driver = to_usb_driver(dev->driver);
struct usb_interface *intf = to_usb_interface(dev);
- struct usb_driver *driver = to_usb_driver(intf->dev.driver);
+ struct usb_device *udev;
+ int error;
intf->condition = USB_INTERFACE_UNBINDING;
+ /* Autoresume for set_interface call below */
+ udev = interface_to_usbdev(intf);
+ error = usb_autoresume_device(udev, 1);
+
/* release all urbs for this interface */
usb_disable_interface(interface_to_usbdev(intf), intf);
- if (driver && driver->disconnect)
- driver->disconnect(intf);
+ driver->disconnect(intf);
/* reset other interface state */
usb_set_interface(interface_to_usbdev(intf),
intf->altsetting[0].desc.bInterfaceNumber,
0);
usb_set_intfdata(intf, NULL);
+
intf->condition = USB_INTERFACE_UNBOUND;
mark_quiesced(intf);
+ intf->needs_remote_wakeup = 0;
+
+ if (!error)
+ usb_autosuspend_device(udev, 1);
return 0;
}
+/**
+ * usb_driver_claim_interface - bind a driver to an interface
+ * @driver: the driver to be bound
+ * @iface: the interface to which it will be bound; must be in the
+ * usb device's active configuration
+ * @priv: driver data associated with that interface
+ *
+ * This is used by usb device drivers that need to claim more than one
+ * interface on a device when probing (audio and acm are current examples).
+ * No device driver should directly modify internal usb_interface or
+ * usb_device structure members.
+ *
+ * Few drivers should need to use this routine, since the most natural
+ * way to bind to an interface is to return the private data from
+ * the driver's probe() method.
+ *
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver probe() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+int usb_driver_claim_interface(struct usb_driver *driver,
+ struct usb_interface *iface, void* priv)
+{
+ struct device *dev = &iface->dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+ int retval = 0;
+
+ if (dev->driver)
+ return -EBUSY;
+
+ dev->driver = &driver->drvwrap.driver;
+ usb_set_intfdata(iface, priv);
+
+ usb_pm_lock(udev);
+ iface->condition = USB_INTERFACE_BOUND;
+ mark_active(iface);
+ iface->pm_usage_cnt = !(driver->supports_autosuspend);
+ usb_pm_unlock(udev);
+
+ /* if interface was already added, bind now; else let
+ * the future device_add() bind it, bypassing probe()
+ */
+ if (device_is_registered(dev))
+ retval = device_bind_driver(dev);
+
+ return retval;
+}
+EXPORT_SYMBOL(usb_driver_claim_interface);
+
+/**
+ * usb_driver_release_interface - unbind a driver from an interface
+ * @driver: the driver to be unbound
+ * @iface: the interface from which it will be unbound
+ *
+ * This can be used by drivers to release an interface without waiting
+ * for their disconnect() methods to be called. In typical cases this
+ * also causes the driver disconnect() method to be called.
+ *
+ * This call is synchronous, and may not be used in an interrupt context.
+ * Callers must own the device lock and the driver model's usb_bus_type.subsys
+ * writelock. So driver disconnect() entries don't need extra locking,
+ * but other call contexts may need to explicitly claim those locks.
+ */
+void usb_driver_release_interface(struct usb_driver *driver,
+ struct usb_interface *iface)
+{
+ struct device *dev = &iface->dev;
+ struct usb_device *udev = interface_to_usbdev(iface);
+
+ /* this should never happen, don't release something that's not ours */
+ if (!dev->driver || dev->driver != &driver->drvwrap.driver)
+ return;
+
+ /* don't release from within disconnect() */
+ if (iface->condition != USB_INTERFACE_BOUND)
+ return;
+
+ /* don't release if the interface hasn't been added yet */
+ if (device_is_registered(dev)) {
+ iface->condition = USB_INTERFACE_UNBINDING;
+ device_release_driver(dev);
+ }
+
+ dev->driver = NULL;
+ usb_set_intfdata(iface, NULL);
+
+ usb_pm_lock(udev);
+ iface->condition = USB_INTERFACE_UNBOUND;
+ mark_quiesced(iface);
+ iface->needs_remote_wakeup = 0;
+ usb_pm_unlock(udev);
+}
+EXPORT_SYMBOL(usb_driver_release_interface);
+
/* returns 0 if no match, 1 if match */
static int usb_match_one_id(struct usb_interface *interface,
const struct usb_device_id *id)
@@ -381,35 +507,223 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_match_id);
int usb_device_match(struct device *dev, struct device_driver *drv)
{
+ /* devices and interfaces are handled separately */
+ if (is_usb_device(dev)) {
+
+ /* interface drivers never match devices */
+ if (!is_usb_device_driver(drv))
+ return 0;
+
+ /* TODO: Add real matching code */
+ return 1;
+
+ } else {
+ struct usb_interface *intf;
+ struct usb_driver *usb_drv;
+ const struct usb_device_id *id;
+
+ /* device drivers never match interfaces */
+ if (is_usb_device_driver(drv))
+ return 0;
+
+ intf = to_usb_interface(dev);
+ usb_drv = to_usb_driver(drv);
+
+ id = usb_match_id(intf, usb_drv->id_table);
+ if (id)
+ return 1;
+
+ id = usb_match_dynamic_id(intf, usb_drv);
+ if (id)
+ return 1;
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_HOTPLUG
+
+/*
+ * This sends an uevent to userspace, typically helping to load driver
+ * or other modules, configure the device, and more. Drivers can provide
+ * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+ *
+ * We're called either from khubd (the typical case) or from root hub
+ * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
+ * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
+ * device (and this configuration!) are still present.
+ */
+static int usb_uevent(struct device *dev, char **envp, int num_envp,
+ char *buffer, int buffer_size)
+{
struct usb_interface *intf;
- struct usb_driver *usb_drv;
- const struct usb_device_id *id;
+ struct usb_device *usb_dev;
+ struct usb_host_interface *alt;
+ int i = 0;
+ int length = 0;
- /* check for generic driver, which we don't match any device with */
- if (drv == &usb_generic_driver)
- return 0;
+ if (!dev)
+ return -ENODEV;
- intf = to_usb_interface(dev);
- usb_drv = to_usb_driver(drv);
+ /* driver is often null here; dev_dbg() would oops */
+ pr_debug ("usb %s: uevent\n", dev->bus_id);
- id = usb_match_id(intf, usb_drv->id_table);
- if (id)
- return 1;
+ if (is_usb_device(dev)) {
+ usb_dev = to_usb_device(dev);
+ alt = NULL;
+ } else {
+ intf = to_usb_interface(dev);
+ usb_dev = interface_to_usbdev(intf);
+ alt = intf->cur_altsetting;
+ }
+
+ if (usb_dev->devnum < 0) {
+ pr_debug ("usb %s: already deleted?\n", dev->bus_id);
+ return -ENODEV;
+ }
+ if (!usb_dev->bus) {
+ pr_debug ("usb %s: bus removed?\n", dev->bus_id);
+ return -ENODEV;
+ }
+
+#ifdef CONFIG_USB_DEVICEFS
+ /* If this is available, userspace programs can directly read
+ * all the device descriptors we don't tell them about. Or
+ * even act as usermode drivers.
+ *
+ * FIXME reduce hardwired intelligence here
+ */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "DEVICE=/proc/bus/usb/%03d/%03d",
+ usb_dev->bus->busnum, usb_dev->devnum))
+ return -ENOMEM;
+#endif
+
+ /* per-device configurations are common */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "PRODUCT=%x/%x/%x",
+ le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct),
+ le16_to_cpu(usb_dev->descriptor.bcdDevice)))
+ return -ENOMEM;
+
+ /* class-based driver binding models */
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "TYPE=%d/%d/%d",
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol))
+ return -ENOMEM;
+
+ if (!is_usb_device(dev)) {
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "INTERFACE=%d/%d/%d",
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+
+ if (add_uevent_var(envp, num_envp, &i,
+ buffer, buffer_size, &length,
+ "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
+ le16_to_cpu(usb_dev->descriptor.idVendor),
+ le16_to_cpu(usb_dev->descriptor.idProduct),
+ le16_to_cpu(usb_dev->descriptor.bcdDevice),
+ usb_dev->descriptor.bDeviceClass,
+ usb_dev->descriptor.bDeviceSubClass,
+ usb_dev->descriptor.bDeviceProtocol,
+ alt->desc.bInterfaceClass,
+ alt->desc.bInterfaceSubClass,
+ alt->desc.bInterfaceProtocol))
+ return -ENOMEM;
+ }
+
+ envp[i] = NULL;
- id = usb_match_dynamic_id(intf, usb_drv);
- if (id)
- return 1;
return 0;
}
+#else
+
+static int usb_uevent(struct device *dev, char **envp,
+ int num_envp, char *buffer, int buffer_size)
+{
+ return -ENODEV;
+}
+
+#endif /* CONFIG_HOTPLUG */
+
+/**
+ * usb_register_device_driver - register a USB device (not interface) driver
+ * @new_udriver: USB operations for the device driver
+ * @owner: module owner of this driver.
+ *
+ * Registers a USB device driver with the USB core. The list of
+ * unattached devices will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized devices.
+ * Returns a negative error code on failure and 0 on success.
+ */
+int usb_register_device_driver(struct usb_device_driver *new_udriver,
+ struct module *owner)
+{
+ int retval = 0;
+
+ if (usb_disabled())
+ return -ENODEV;
+
+ new_udriver->drvwrap.for_devices = 1;
+ new_udriver->drvwrap.driver.name = (char *) new_udriver->name;
+ new_udriver->drvwrap.driver.bus = &usb_bus_type;
+ new_udriver->drvwrap.driver.probe = usb_probe_device;
+ new_udriver->drvwrap.driver.remove = usb_unbind_device;
+ new_udriver->drvwrap.driver.owner = owner;
+
+ retval = driver_register(&new_udriver->drvwrap.driver);
+
+ if (!retval) {
+ pr_info("%s: registered new device driver %s\n",
+ usbcore_name, new_udriver->name);
+ usbfs_update_special();
+ } else {
+ printk(KERN_ERR "%s: error %d registering device "
+ " driver %s\n",
+ usbcore_name, retval, new_udriver->name);
+ }
+
+ return retval;
+}
+EXPORT_SYMBOL_GPL(usb_register_device_driver);
+
+/**
+ * usb_deregister_device_driver - unregister a USB device (not interface) driver
+ * @udriver: USB operations of the device driver to unregister
+ * Context: must be able to sleep
+ *
+ * Unlinks the specified driver from the internal USB driver list.
+ */
+void usb_deregister_device_driver(struct usb_device_driver *udriver)
+{
+ pr_info("%s: deregistering device driver %s\n",
+ usbcore_name, udriver->name);
+
+ driver_unregister(&udriver->drvwrap.driver);
+ usbfs_update_special();
+}
+EXPORT_SYMBOL_GPL(usb_deregister_device_driver);
+
/**
- * usb_register_driver - register a USB driver
- * @new_driver: USB operations for the driver
+ * usb_register_driver - register a USB interface driver
+ * @new_driver: USB operations for the interface driver
* @owner: module owner of this driver.
*
- * Registers a USB driver with the USB core. The list of unattached
- * interfaces will be rescanned whenever a new driver is added, allowing
- * the new driver to attach to any recognized devices.
+ * Registers a USB interface driver with the USB core. The list of
+ * unattached interfaces will be rescanned whenever a new driver is
+ * added, allowing the new driver to attach to any recognized interfaces.
* Returns a negative error code on failure and 0 on success.
*
* NOTE: if you want your driver to use the USB major number, you must call
@@ -423,23 +737,25 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
if (usb_disabled())
return -ENODEV;
- new_driver->driver.name = (char *)new_driver->name;
- new_driver->driver.bus = &usb_bus_type;
- new_driver->driver.probe = usb_probe_interface;
- new_driver->driver.remove = usb_unbind_interface;
- new_driver->driver.owner = owner;
+ new_driver->drvwrap.for_devices = 0;
+ new_driver->drvwrap.driver.name = (char *) new_driver->name;
+ new_driver->drvwrap.driver.bus = &usb_bus_type;
+ new_driver->drvwrap.driver.probe = usb_probe_interface;
+ new_driver->drvwrap.driver.remove = usb_unbind_interface;
+ new_driver->drvwrap.driver.owner = owner;
spin_lock_init(&new_driver->dynids.lock);
INIT_LIST_HEAD(&new_driver->dynids.list);
- retval = driver_register(&new_driver->driver);
+ retval = driver_register(&new_driver->drvwrap.driver);
if (!retval) {
- pr_info("%s: registered new driver %s\n",
+ pr_info("%s: registered new interface driver %s\n",
usbcore_name, new_driver->name);
usbfs_update_special();
usb_create_newid_file(new_driver);
} else {
- printk(KERN_ERR "%s: error %d registering driver %s\n",
+ printk(KERN_ERR "%s: error %d registering interface "
+ " driver %s\n",
usbcore_name, retval, new_driver->name);
}
@@ -448,8 +764,8 @@ int usb_register_driver(struct usb_driver *new_driver, struct module *owner)
EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
/**
- * usb_deregister - unregister a USB driver
- * @driver: USB operations of the driver to unregister
+ * usb_deregister - unregister a USB interface driver
+ * @driver: USB operations of the interface driver to unregister
* Context: must be able to sleep
*
* Unlinks the specified driver from the internal USB driver list.
@@ -460,12 +776,554 @@ EXPORT_SYMBOL_GPL_FUTURE(usb_register_driver);
*/
void usb_deregister(struct usb_driver *driver)
{
- pr_info("%s: deregistering driver %s\n", usbcore_name, driver->name);
+ pr_info("%s: deregistering interface driver %s\n",
+ usbcore_name, driver->name);
usb_remove_newid_file(driver);
usb_free_dynids(driver);
- driver_unregister(&driver->driver);
+ driver_unregister(&driver->drvwrap.driver);
usbfs_update_special();
}
EXPORT_SYMBOL_GPL_FUTURE(usb_deregister);
+
+#ifdef CONFIG_PM
+
+/* Caller has locked udev's pm_mutex */
+static int suspend_device(struct usb_device *udev, pm_message_t msg)
+{
+ struct usb_device_driver *udriver;
+ int status = 0;
+
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state == USB_STATE_SUSPENDED)
+ goto done;
+
+ /* For devices that don't have a driver, we do a standard suspend. */
+ if (udev->dev.driver == NULL) {
+ udev->do_remote_wakeup = 0;
+ status = usb_port_suspend(udev);
+ goto done;
+ }
+
+ udriver = to_usb_device_driver(udev->dev.driver);
+ status = udriver->suspend(udev, msg);
+
+done:
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ udev->dev.power.power_state.event = msg.event;
+ return status;
+}
+
+/* Caller has locked udev's pm_mutex */
+static int resume_device(struct usb_device *udev)
+{
+ struct usb_device_driver *udriver;
+ int status = 0;
+
+ if (udev->state == USB_STATE_NOTATTACHED ||
+ udev->state != USB_STATE_SUSPENDED)
+ goto done;
+
+ /* Can't resume it if it doesn't have a driver. */
+ if (udev->dev.driver == NULL) {
+ status = -ENOTCONN;
+ goto done;
+ }
+
+ udriver = to_usb_device_driver(udev->dev.driver);
+ status = udriver->resume(udev);
+
+done:
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ udev->dev.power.power_state.event = PM_EVENT_ON;
+ return status;
+}
+
+/* Caller has locked intf's usb_device's pm mutex */
+static int suspend_interface(struct usb_interface *intf, pm_message_t msg)
+{
+ struct usb_driver *driver;
+ int status = 0;
+
+ /* with no hardware, USB interfaces only use FREEZE and ON states */
+ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+ !is_active(intf))
+ goto done;
+
+ if (intf->condition == USB_INTERFACE_UNBOUND) /* This can't happen */
+ goto done;
+ driver = to_usb_driver(intf->dev.driver);
+
+ if (driver->suspend && driver->resume) {
+ status = driver->suspend(intf, msg);
+ if (status == 0)
+ mark_quiesced(intf);
+ else if (!interface_to_usbdev(intf)->auto_pm)
+ dev_err(&intf->dev, "%s error %d\n",
+ "suspend", status);
+ } else {
+ // FIXME else if there's no suspend method, disconnect...
+ // Not possible if auto_pm is set...
+ dev_warn(&intf->dev, "no suspend for driver %s?\n",
+ driver->name);
+ mark_quiesced(intf);
+ }
+
+done:
+ // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ intf->dev.power.power_state.event = msg.event;
+ return status;
+}
+
+/* Caller has locked intf's usb_device's pm_mutex */
+static int resume_interface(struct usb_interface *intf)
+{
+ struct usb_driver *driver;
+ int status = 0;
+
+ if (interface_to_usbdev(intf)->state == USB_STATE_NOTATTACHED ||
+ is_active(intf))
+ goto done;
+
+ /* Don't let autoresume interfere with unbinding */
+ if (intf->condition == USB_INTERFACE_UNBINDING)
+ goto done;
+
+ /* Can't resume it if it doesn't have a driver. */
+ if (intf->condition == USB_INTERFACE_UNBOUND) {
+ status = -ENOTCONN;
+ goto done;
+ }
+ driver = to_usb_driver(intf->dev.driver);
+
+ if (driver->resume) {
+ status = driver->resume(intf);
+ if (status)
+ dev_err(&intf->dev, "%s error %d\n",
+ "resume", status);
+ else
+ mark_active(intf);
+ } else {
+ dev_warn(&intf->dev, "no resume for driver %s?\n",
+ driver->name);
+ mark_active(intf);
+ }
+
+done:
+ // dev_dbg(&intf->dev, "%s: status %d\n", __FUNCTION__, status);
+ if (status == 0)
+ intf->dev.power.power_state.event = PM_EVENT_ON;
+ return status;
+}
+
+/**
+ * usb_suspend_both - suspend a USB device and its interfaces
+ * @udev: the usb_device to suspend
+ * @msg: Power Management message describing this state transition
+ *
+ * This is the central routine for suspending USB devices. It calls the
+ * suspend methods for all the interface drivers in @udev and then calls
+ * the suspend method for @udev itself. If an error occurs at any stage,
+ * all the interfaces which were suspended are resumed so that they remain
+ * in the same state as the device.
+ *
+ * If an autosuspend is in progress (@udev->auto_pm is set), the routine
+ * checks first to make sure that neither the device itself or any of its
+ * active interfaces is in use (pm_usage_cnt is greater than 0). If they
+ * are, the autosuspend fails.
+ *
+ * If the suspend succeeds, the routine recursively queues an autosuspend
+ * request for @udev's parent device, thereby propagating the change up
+ * the device tree. If all of the parent's children are now suspended,
+ * the parent will autosuspend in turn.
+ *
+ * The suspend method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex. Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autosuspend requests generated by a child device or
+ * interface driver may not be. Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations. However, drivers
+ * must be prepared to handle suspend calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * autosuspends) while holding @udev's device lock (preventing outside
+ * suspends).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
+{
+ int status = 0;
+ int i = 0;
+ struct usb_interface *intf;
+ struct usb_device *parent = udev->parent;
+
+ cancel_delayed_work(&udev->autosuspend);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return 0;
+ if (udev->state == USB_STATE_SUSPENDED)
+ return 0;
+
+ udev->do_remote_wakeup = device_may_wakeup(&udev->dev);
+
+ /* For autosuspend, fail fast if anything is in use.
+ * Also fail if any interfaces require remote wakeup but it
+ * isn't available. */
+ if (udev->auto_pm) {
+ if (udev->pm_usage_cnt > 0)
+ return -EBUSY;
+ if (udev->actconfig) {
+ for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ if (!is_active(intf))
+ continue;
+ if (intf->pm_usage_cnt > 0)
+ return -EBUSY;
+ if (intf->needs_remote_wakeup &&
+ !udev->do_remote_wakeup) {
+ dev_dbg(&udev->dev,
+ "remote wakeup needed for autosuspend\n");
+ return -EOPNOTSUPP;
+ }
+ }
+ i = 0;
+ }
+ }
+
+ /* Suspend all the interfaces and then udev itself */
+ if (udev->actconfig) {
+ for (; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ status = suspend_interface(intf, msg);
+ if (status != 0)
+ break;
+ }
+ }
+ if (status == 0)
+ status = suspend_device(udev, msg);
+
+ /* If the suspend failed, resume interfaces that did get suspended */
+ if (status != 0) {
+ while (--i >= 0) {
+ intf = udev->actconfig->interface[i];
+ resume_interface(intf);
+ }
+
+ /* If the suspend succeeded, propagate it up the tree */
+ } else if (parent)
+ usb_autosuspend_device(parent, 0);
+
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ return status;
+}
+
+/**
+ * usb_resume_both - resume a USB device and its interfaces
+ * @udev: the usb_device to resume
+ *
+ * This is the central routine for resuming USB devices. It calls the
+ * the resume method for @udev and then calls the resume methods for all
+ * the interface drivers in @udev.
+ *
+ * Before starting the resume, the routine calls itself recursively for
+ * the parent device of @udev, thereby propagating the change up the device
+ * tree and assuring that @udev will be able to resume. If the parent is
+ * unable to resume successfully, the routine fails.
+ *
+ * The resume method calls are subject to mutual exclusion under control
+ * of @udev's pm_mutex. Many of these calls are also under the protection
+ * of @udev's device lock (including all requests originating outside the
+ * USB subsystem), but autoresume requests generated by a child device or
+ * interface driver may not be. Usbcore will insure that the method calls
+ * do not arrive during bind, unbind, or reset operations. However, drivers
+ * must be prepared to handle resume calls arriving at unpredictable times.
+ * The only way to block such calls is to do an autoresume (preventing
+ * other autoresumes) while holding @udev's device lock (preventing outside
+ * resumes).
+ *
+ * The caller must hold @udev->pm_mutex.
+ *
+ * This routine can run only in process context.
+ */
+int usb_resume_both(struct usb_device *udev)
+{
+ int status = 0;
+ int i;
+ struct usb_interface *intf;
+ struct usb_device *parent = udev->parent;
+
+ cancel_delayed_work(&udev->autosuspend);
+ if (udev->state == USB_STATE_NOTATTACHED)
+ return -ENODEV;
+
+ /* Propagate the resume up the tree, if necessary */
+ if (udev->state == USB_STATE_SUSPENDED) {
+ if (parent) {
+ usb_pm_lock(parent);
+ parent->auto_pm = 1;
+ status = usb_resume_both(parent);
+ } else {
+
+ /* We can't progagate beyond the USB subsystem,
+ * so if a root hub's controller is suspended
+ * then we're stuck. */
+ if (udev->dev.parent->power.power_state.event !=
+ PM_EVENT_ON)
+ status = -EHOSTUNREACH;
+ }
+ if (status == 0)
+ status = resume_device(udev);
+ if (parent)
+ usb_pm_unlock(parent);
+ } else {
+
+ /* Needed only for setting udev->dev.power.power_state.event
+ * and for possible debugging message. */
+ status = resume_device(udev);
+ }
+
+ /* Now the parent won't suspend until we are finished */
+
+ if (status == 0 && udev->actconfig) {
+ for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
+ intf = udev->actconfig->interface[i];
+ resume_interface(intf);
+ }
+ }
+
+ // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
+ return status;
+}
+
+#ifdef CONFIG_USB_SUSPEND
+
+/**
+ * usb_autosuspend_device - delayed autosuspend of a USB device and its interfaces
+ * @udev: the usb_device to autosuspend
+ * @dec_usage_cnt: flag to decrement @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem is finished using
+ * @udev and wants to allow it to autosuspend. Examples would be when
+ * @udev's device file in usbfs is closed or after a configuration change.
+ *
+ * @dec_usage_cnt should be 1 if the subsystem previously incremented
+ * @udev's usage counter (such as by passing 1 to usb_autoresume_device);
+ * otherwise it should be 0.
+ *
+ * If the usage counter for @udev or any of its active interfaces is greater
+ * than 0, the autosuspend request will not be queued. (If an interface
+ * driver does not support autosuspend then its usage counter is permanently
+ * positive.) Likewise, if an interface driver requires remote-wakeup
+ * capability during autosuspend but remote wakeup is disabled, the
+ * autosuspend will fail.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt)
+{
+ usb_pm_lock(udev);
+ udev->pm_usage_cnt -= dec_usage_cnt;
+ if (udev->pm_usage_cnt <= 0)
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ USB_AUTOSUSPEND_DELAY);
+ usb_pm_unlock(udev);
+ // dev_dbg(&udev->dev, "%s: cnt %d\n",
+ // __FUNCTION__, udev->pm_usage_cnt);
+}
+
+/**
+ * usb_autoresume_device - immediately autoresume a USB device and its interfaces
+ * @udev: the usb_device to autoresume
+ * @inc_usage_cnt: flag to increment @udev's PM-usage counter
+ *
+ * This routine should be called when a core subsystem wants to use @udev
+ * and needs to guarantee that it is not suspended. In addition, the
+ * caller can prevent @udev from being autosuspended subsequently. (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * Examples would be when @udev's device file in usbfs is opened (autosuspend
+ * should be prevented until the file is closed) or when a remote-wakeup
+ * request is received (later autosuspends should not be prevented).
+ *
+ * @inc_usage_cnt should be 1 to increment @udev's usage counter and prevent
+ * autosuspends. This prevention will persist until the usage counter is
+ * decremented again (such as by passing 1 to usb_autosuspend_device).
+ * Otherwise @inc_usage_cnt should be 0 to leave the usage counter unchanged.
+ * Regardless, if the autoresume fails then the usage counter is not
+ * incremented.
+ *
+ * Often the caller will hold @udev's device lock, but this is not
+ * necessary (and attempting it might cause deadlock).
+ *
+ * This routine can run only in process context.
+ */
+int usb_autoresume_device(struct usb_device *udev, int inc_usage_cnt)
+{
+ int status;
+
+ usb_pm_lock(udev);
+ udev->pm_usage_cnt += inc_usage_cnt;
+ udev->auto_pm = 1;
+ status = usb_resume_both(udev);
+ if (status != 0)
+ udev->pm_usage_cnt -= inc_usage_cnt;
+ usb_pm_unlock(udev);
+ // dev_dbg(&udev->dev, "%s: status %d cnt %d\n",
+ // __FUNCTION__, status, udev->pm_usage_cnt);
+ return status;
+}
+
+/**
+ * usb_autopm_put_interface - decrement a USB interface's PM-usage counter
+ * @intf: the usb_interface whose counter should be decremented
+ *
+ * This routine should be called by an interface driver when it is
+ * finished using @intf and wants to allow it to autosuspend. A typical
+ * example would be a character-device driver when its device file is
+ * closed.
+ *
+ * The routine decrements @intf's usage counter. When the counter reaches
+ * 0, a delayed autosuspend request for @intf's device is queued. When
+ * the delay expires, if @intf->pm_usage_cnt is still <= 0 along with all
+ * the other usage counters for the sibling interfaces and @intf's
+ * usb_device, the device and all its interfaces will be autosuspended.
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver. The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface. The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * If the driver has set @intf->needs_remote_wakeup then autosuspend will
+ * take place only if the device's remote-wakeup facility is enabled.
+ *
+ * Suspend method calls queued by this routine can arrive at any time
+ * while @intf is resumed and its usage counter is equal to 0. They are
+ * not protected by the usb_device's lock but only by its pm_mutex.
+ * Drivers must provide their own synchronization.
+ *
+ * This routine can run only in process context.
+ */
+void usb_autopm_put_interface(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+
+ usb_pm_lock(udev);
+ if (intf->condition != USB_INTERFACE_UNBOUND &&
+ --intf->pm_usage_cnt <= 0) {
+ queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend,
+ USB_AUTOSUSPEND_DELAY);
+ }
+ usb_pm_unlock(udev);
+ // dev_dbg(&intf->dev, "%s: cnt %d\n",
+ // __FUNCTION__, intf->pm_usage_cnt);
+}
+EXPORT_SYMBOL_GPL(usb_autopm_put_interface);
+
+/**
+ * usb_autopm_get_interface - increment a USB interface's PM-usage counter
+ * @intf: the usb_interface whose counter should be incremented
+ *
+ * This routine should be called by an interface driver when it wants to
+ * use @intf and needs to guarantee that it is not suspended. In addition,
+ * the routine prevents @intf from being autosuspended subsequently. (Note
+ * that this will not prevent suspend events originating in the PM core.)
+ * This prevention will persist until usb_autopm_put_interface() is called
+ * or @intf is unbound. A typical example would be a character-device
+ * driver when its device file is opened.
+ *
+ * The routine increments @intf's usage counter. So long as the counter
+ * is greater than 0, autosuspend will not be allowed for @intf or its
+ * usb_device. When the driver is finished using @intf it should call
+ * usb_autopm_put_interface() to decrement the usage counter and queue
+ * a delayed autosuspend request (if the counter is <= 0).
+ *
+ * Note that @intf->pm_usage_cnt is owned by the interface driver. The
+ * core will not change its value other than the increment and decrement
+ * in usb_autopm_get_interface and usb_autopm_put_interface. The driver
+ * may use this simple counter-oriented discipline or may set the value
+ * any way it likes.
+ *
+ * Resume method calls generated by this routine can arrive at any time
+ * while @intf is suspended. They are not protected by the usb_device's
+ * lock but only by its pm_mutex. Drivers must provide their own
+ * synchronization.
+ *
+ * This routine can run only in process context.
+ */
+int usb_autopm_get_interface(struct usb_interface *intf)
+{
+ struct usb_device *udev = interface_to_usbdev(intf);
+ int status;
+
+ usb_pm_lock(udev);
+ if (intf->condition == USB_INTERFACE_UNBOUND)
+ status = -ENODEV;
+ else {
+ ++intf->pm_usage_cnt;
+ udev->auto_pm = 1;
+ status = usb_resume_both(udev);
+ if (status != 0)
+ --intf->pm_usage_cnt;
+ }
+ usb_pm_unlock(udev);
+ // dev_dbg(&intf->dev, "%s: status %d cnt %d\n",
+ // __FUNCTION__, status, intf->pm_usage_cnt);
+ return status;
+}
+EXPORT_SYMBOL_GPL(usb_autopm_get_interface);
+
+#endif /* CONFIG_USB_SUSPEND */
+
+static int usb_suspend(struct device *dev, pm_message_t message)
+{
+ int status;
+
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
+
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_suspend_both(udev, message);
+ usb_pm_unlock(udev);
+ } else
+ status = 0;
+ return status;
+}
+
+static int usb_resume(struct device *dev)
+{
+ int status;
+
+ if (is_usb_device(dev)) {
+ struct usb_device *udev = to_usb_device(dev);
+
+ usb_pm_lock(udev);
+ udev->auto_pm = 0;
+ status = usb_resume_both(udev);
+ usb_pm_unlock(udev);
+
+ /* Rebind drivers that had no suspend method? */
+ } else
+ status = 0;
+ return status;
+}
+
+#endif /* CONFIG_PM */
+
+struct bus_type usb_bus_type = {
+ .name = "usb",
+ .match = usb_device_match,
+ .uevent = usb_uevent,
+#ifdef CONFIG_PM
+ .suspend = usb_suspend,
+ .resume = usb_resume,
+#endif
+};
diff --git a/drivers/usb/core/endpoint.c b/drivers/usb/core/endpoint.c
index 247b5a4913a..3ebb90149e9 100644
--- a/drivers/usb/core/endpoint.c
+++ b/drivers/usb/core/endpoint.c
@@ -207,9 +207,9 @@ static void ep_device_release(struct device *dev)
kfree(ep_dev);
}
-void usb_create_ep_files(struct device *parent,
- struct usb_host_endpoint *endpoint,
- struct usb_device *udev)
+int usb_create_ep_files(struct device *parent,
+ struct usb_host_endpoint *endpoint,
+ struct usb_device *udev)
{
char name[8];
struct ep_device *ep_dev;
@@ -242,19 +242,33 @@ void usb_create_ep_files(struct device *parent,
retval = device_register(&ep_dev->dev);
if (retval)
goto error;
- sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+ retval = sysfs_create_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+ if (retval)
+ goto error_group;
endpoint->ep_dev = ep_dev;
/* create the symlink to the old-style "ep_XX" directory */
sprintf(name, "ep_%02x", endpoint->desc.bEndpointAddress);
- sysfs_create_link(&parent->kobj, &endpoint->ep_dev->dev.kobj, name);
-
+ retval = sysfs_create_link(&parent->kobj,
+ &endpoint->ep_dev->dev.kobj, name);
+ if (retval)
+ goto error_link;
exit:
- return;
+ return retval;
+
+error_link:
+ sysfs_remove_group(&ep_dev->dev.kobj, &ep_dev_attr_grp);
+
+error_group:
+ device_unregister(&ep_dev->dev);
+ endpoint->ep_dev = NULL;
+ destroy_endpoint_class();
+ return retval;
error:
kfree(ep_dev);
- return;
+ destroy_endpoint_class();
+ return retval;
}
void usb_remove_ep_files(struct usb_host_endpoint *endpoint)
diff --git a/drivers/usb/core/file.c b/drivers/usb/core/file.c
index 8de4f8c99d6..c376c655c5d 100644
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -55,7 +55,7 @@ static int usb_open(struct inode * inode, struct file * file)
return err;
}
-static struct file_operations usb_fops = {
+static const struct file_operations usb_fops = {
.owner = THIS_MODULE,
.open = usb_open,
};
diff --git a/drivers/usb/core/generic.c b/drivers/usb/core/generic.c
new file mode 100644
index 00000000000..16332cc5794
--- /dev/null
+++ b/drivers/usb/core/generic.c
@@ -0,0 +1,208 @@
+/*
+ * drivers/usb/generic.c - generic driver for USB devices (not interfaces)
+ *
+ * (C) Copyright 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ *
+ * based on drivers/usb/usb.c which had the following copyrights:
+ * (C) Copyright Linus Torvalds 1999
+ * (C) Copyright Johannes Erdfelt 1999-2001
+ * (C) Copyright Andreas Gal 1999
+ * (C) Copyright Gregory P. Smith 1999
+ * (C) Copyright Deti Fliegl 1999 (new USB architecture)
+ * (C) Copyright Randy Dunlap 2000
+ * (C) Copyright David Brownell 2000-2004
+ * (C) Copyright Yggdrasil Computing, Inc. 2000
+ * (usb_device_id matching changes by Adam J. Richter)
+ * (C) Copyright Greg Kroah-Hartman 2002-2003
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/usb.h>
+#include "usb.h"
+
+static inline const char *plural(int n)
+{
+ return (n == 1 ? "" : "s");
+}
+
+static int choose_configuration(struct usb_device *udev)
+{
+ int i;
+ int num_configs;
+ int insufficient_power = 0;
+ struct usb_host_config *c, *best;
+
+ best = NULL;
+ c = udev->config;
+ num_configs = udev->descriptor.bNumConfigurations;
+ for (i = 0; i < num_configs; (i++, c++)) {
+ struct usb_interface_descriptor *desc = NULL;
+
+ /* It's possible that a config has no interfaces! */
+ if (c->desc.bNumInterfaces > 0)
+ desc = &c->intf_cache[0]->altsetting->desc;
+
+ /*
+ * HP's USB bus-powered keyboard has only one configuration
+ * and it claims to be self-powered; other devices may have
+ * similar errors in their descriptors. If the next test
+ * were allowed to execute, such configurations would always
+ * be rejected and the devices would not work as expected.
+ * In the meantime, we run the risk of selecting a config
+ * that requires external power at a time when that power
+ * isn't available. It seems to be the lesser of two evils.
+ *
+ * Bugzilla #6448 reports a device that appears to crash
+ * when it receives a GET_DEVICE_STATUS request! We don't
+ * have any other way to tell whether a device is self-powered,
+ * but since we don't use that information anywhere but here,
+ * the call has been removed.
+ *
+ * Maybe the GET_DEVICE_STATUS call and the test below can
+ * be reinstated when device firmwares become more reliable.
+ * Don't hold your breath.
+ */
+#if 0
+ /* Rule out self-powered configs for a bus-powered device */
+ if (bus_powered && (c->desc.bmAttributes &
+ USB_CONFIG_ATT_SELFPOWER))
+ continue;
+#endif
+
+ /*
+ * The next test may not be as effective as it should be.
+ * Some hubs have errors in their descriptor, claiming
+ * to be self-powered when they are really bus-powered.
+ * We will overestimate the amount of current such hubs
+ * make available for each port.
+ *
+ * This is a fairly benign sort of failure. It won't
+ * cause us to reject configurations that we should have
+ * accepted.
+ */
+
+ /* Rule out configs that draw too much bus current */
+ if (c->desc.bMaxPower * 2 > udev->bus_mA) {
+ insufficient_power++;
+ continue;
+ }
+
+ /* If the first config's first interface is COMM/2/0xff
+ * (MSFT RNDIS), rule it out unless Linux has host-side
+ * RNDIS support. */
+ if (i == 0 && desc
+ && desc->bInterfaceClass == USB_CLASS_COMM
+ && desc->bInterfaceSubClass == 2
+ && desc->bInterfaceProtocol == 0xff) {
+#ifndef CONFIG_USB_NET_RNDIS_HOST
+ continue;
+#else
+ best = c;
+#endif
+ }
+
+ /* From the remaining configs, choose the first one whose
+ * first interface is for a non-vendor-specific class.
+ * Reason: Linux is more likely to have a class driver
+ * than a vendor-specific driver. */
+ else if (udev->descriptor.bDeviceClass !=
+ USB_CLASS_VENDOR_SPEC &&
+ (!desc || desc->bInterfaceClass !=
+ USB_CLASS_VENDOR_SPEC)) {
+ best = c;
+ break;
+ }
+
+ /* If all the remaining configs are vendor-specific,
+ * choose the first one. */
+ else if (!best)
+ best = c;
+ }
+
+ if (insufficient_power > 0)
+ dev_info(&udev->dev, "rejected %d configuration%s "
+ "due to insufficient available bus power\n",
+ insufficient_power, plural(insufficient_power));
+
+ if (best) {
+ i = best->desc.bConfigurationValue;
+ dev_info(&udev->dev,
+ "configuration #%d chosen from %d choice%s\n",
+ i, num_configs, plural(num_configs));
+ } else {
+ i = -1;
+ dev_warn(&udev->dev,
+ "no configuration chosen from %d choice%s\n",
+ num_configs, plural(num_configs));
+ }
+ return i;
+}
+
+static int generic_probe(struct usb_device *udev)
+{
+ int err, c;
+
+ /* put device-specific files into sysfs */
+ usb_create_sysfs_dev_files(udev);
+
+ /* Choose and set the configuration. This registers the interfaces
+ * with the driver core and lets interface drivers bind to them.
+ */
+ c = choose_configuration(udev);
+ if (c >= 0) {
+ err = usb_set_configuration(udev, c);
+ if (err) {
+ dev_err(&udev->dev, "can't set config #%d, error %d\n",
+ c, err);
+ /* This need not be fatal. The user can try to
+ * set other configurations. */
+ }
+ }
+
+ /* USB device state == configured ... usable */
+ usb_notify_add_device(udev);
+
+ return 0;
+}
+
+static void generic_disconnect(struct usb_device *udev)
+{
+ usb_notify_remove_device(udev);
+
+ /* if this is only an unbind, not a physical disconnect, then
+ * unconfigure the device */
+ if (udev->actconfig)
+ usb_set_configuration(udev, 0);
+
+ usb_remove_sysfs_dev_files(udev);
+}
+
+#ifdef CONFIG_PM
+
+static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+{
+ /* USB devices enter SUSPEND state through their hubs, but can be
+ * marked for FREEZE as soon as their children are already idled.
+ * But those semantics are useless, so we equate the two (sigh).
+ */
+ return usb_port_suspend(udev);
+}
+
+static int generic_resume(struct usb_device *udev)
+{
+ return usb_port_resume(udev);
+}
+
+#endif /* CONFIG_PM */
+
+struct usb_device_driver usb_generic_driver = {
+ .name = "usb",
+ .probe = generic_probe,
+ .disconnect = generic_disconnect,
+#ifdef CONFIG_PM
+ .suspend = generic_suspend,
+ .resume = generic_resume,
+#endif
+ .supports_autosuspend = 1,
+};
diff --git a/drivers/usb/core/hcd-pci.c b/drivers/usb/core/hcd-pci.c
index 5078fb3375e..edf4300a3f7 100644
--- a/drivers/usb/core/hcd-pci.c
+++ b/drivers/usb/core/hcd-pci.c
@@ -281,7 +281,7 @@ int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t message)
(void) usb_hcd_pci_resume (dev);
}
- } else {
+ } else if (hcd->state != HC_STATE_HALT) {
dev_dbg (hcd->self.controller, "hcd state %d; not suspended\n",
hcd->state);
WARN_ON(1);
@@ -413,4 +413,20 @@ EXPORT_SYMBOL (usb_hcd_pci_resume);
#endif /* CONFIG_PM */
+/**
+ * usb_hcd_pci_shutdown - shutdown host controller
+ * @dev: USB Host Controller being shutdown
+ */
+void usb_hcd_pci_shutdown (struct pci_dev *dev)
+{
+ struct usb_hcd *hcd;
+
+ hcd = pci_get_drvdata(dev);
+ if (!hcd)
+ return;
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_pci_shutdown);
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index fb4d058bbde..e658089f7b5 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -36,6 +36,7 @@
#include <linux/mutex.h>
#include <asm/irq.h>
#include <asm/byteorder.h>
+#include <linux/platform_device.h>
#include <linux/usb.h>
@@ -317,8 +318,8 @@ static int rh_string (
// id 3 == vendor description
} else if (id == 3) {
- snprintf (buf, sizeof buf, "%s %s %s", system_utsname.sysname,
- system_utsname.release, hcd->driver->description);
+ snprintf (buf, sizeof buf, "%s %s %s", init_utsname()->sysname,
+ init_utsname()->release, hcd->driver->description);
// unsupported IDs --> "protocol stall"
} else
@@ -344,7 +345,8 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
struct usb_ctrlrequest *cmd;
u16 typeReq, wValue, wIndex, wLength;
u8 *ubuf = urb->transfer_buffer;
- u8 tbuf [sizeof (struct usb_hub_descriptor)];
+ u8 tbuf [sizeof (struct usb_hub_descriptor)]
+ __attribute__((aligned(4)));
const u8 *bufp = tbuf;
int len = 0;
int patch_wakeup = 0;
@@ -632,31 +634,20 @@ static int rh_urb_enqueue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/
-/* Asynchronous unlinks of root-hub control URBs are legal, but they
- * don't do anything. Status URB unlinks must be made in process context
- * with interrupts enabled.
+/* Unlinks of root-hub control URBs are legal, but they don't do anything
+ * since these URBs always execute synchronously.
*/
static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
{
- if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
- if (in_interrupt())
- return 0; /* nothing to do */
-
- spin_lock_irq(&urb->lock); /* from usb_kill_urb */
- ++urb->reject;
- spin_unlock_irq(&urb->lock);
-
- wait_event(usb_kill_urb_queue,
- atomic_read(&urb->use_count) == 0);
+ unsigned long flags;
- spin_lock_irq(&urb->lock);
- --urb->reject;
- spin_unlock_irq(&urb->lock);
+ if (usb_pipeendpoint(urb->pipe) == 0) { /* Control URB */
+ ; /* Do nothing */
} else { /* Status URB */
if (!hcd->uses_new_polling)
- del_timer_sync (&hcd->rh_timer);
- local_irq_disable ();
+ del_timer (&hcd->rh_timer);
+ local_irq_save (flags);
spin_lock (&hcd_root_hub_lock);
if (urb == hcd->status_urb) {
hcd->status_urb = NULL;
@@ -666,7 +657,7 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
spin_unlock (&hcd_root_hub_lock);
if (urb)
usb_hcd_giveback_urb (hcd, urb, NULL);
- local_irq_enable ();
+ local_irq_restore (flags);
}
return 0;
@@ -674,31 +665,6 @@ static int usb_rh_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
/*-------------------------------------------------------------------------*/
-/* exported only within usbcore */
-struct usb_bus *usb_bus_get(struct usb_bus *bus)
-{
- if (bus)
- kref_get(&bus->kref);
- return bus;
-}
-
-static void usb_host_release(struct kref *kref)
-{
- struct usb_bus *bus = container_of(kref, struct usb_bus, kref);
-
- if (bus->release)
- bus->release(bus);
-}
-
-/* exported only within usbcore */
-void usb_bus_put(struct usb_bus *bus)
-{
- if (bus)
- kref_put(&bus->kref, usb_host_release);
-}
-
-/*-------------------------------------------------------------------------*/
-
static struct class *usb_host_class;
int usb_host_init(void)
@@ -730,39 +696,12 @@ static void usb_bus_init (struct usb_bus *bus)
bus->devnum_next = 1;
bus->root_hub = NULL;
- bus->hcpriv = NULL;
bus->busnum = -1;
bus->bandwidth_allocated = 0;
bus->bandwidth_int_reqs = 0;
bus->bandwidth_isoc_reqs = 0;
INIT_LIST_HEAD (&bus->bus_list);
-
- kref_init(&bus->kref);
-}
-
-/**
- * usb_alloc_bus - creates a new USB host controller structure
- * @op: pointer to a struct usb_operations that this bus structure should use
- * Context: !in_interrupt()
- *
- * Creates a USB host controller bus structure with the specified
- * usb_operations and initializes all the necessary internal objects.
- *
- * If no memory is available, NULL is returned.
- *
- * The caller should call usb_put_bus() when it is finished with the structure.
- */
-struct usb_bus *usb_alloc_bus (struct usb_operations *op)
-{
- struct usb_bus *bus;
-
- bus = kzalloc (sizeof *bus, GFP_KERNEL);
- if (!bus)
- return NULL;
- usb_bus_init (bus);
- bus->op = op;
- return bus;
}
/*-------------------------------------------------------------------------*/
@@ -897,8 +836,7 @@ void usb_enable_root_hub_irq (struct usb_bus *bus)
struct usb_hcd *hcd;
hcd = container_of (bus, struct usb_hcd, self);
- if (hcd->driver->hub_irq_enable && !hcd->poll_rh &&
- hcd->state != HC_STATE_HALT)
+ if (hcd->driver->hub_irq_enable && hcd->state != HC_STATE_HALT)
hcd->driver->hub_irq_enable (hcd);
}
@@ -1112,10 +1050,10 @@ static void urb_unlink (struct urb *urb)
* expects usb_submit_urb() to have sanity checked and conditioned all
* inputs in the urb
*/
-static int hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
+int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags)
{
int status;
- struct usb_hcd *hcd = urb->dev->bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
struct usb_host_endpoint *ep;
unsigned long flags;
@@ -1186,7 +1124,7 @@ doit:
/* lower level hcd code should use *_dma exclusively,
* unless it uses pio or talks to another transport.
*/
- if (hcd->self.controller->dma_mask) {
+ if (hcd->self.uses_dma) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
urb->setup_dma = dma_map_single (
@@ -1221,9 +1159,10 @@ done:
/*-------------------------------------------------------------------------*/
/* called in any context */
-static int hcd_get_frame_number (struct usb_device *udev)
+int usb_hcd_get_frame_number (struct usb_device *udev)
{
- struct usb_hcd *hcd = (struct usb_hcd *)udev->bus->hcpriv;
+ struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
if (!HC_IS_RUNNING (hcd->state))
return -ESHUTDOWN;
return hcd->driver->get_frame_number (hcd);
@@ -1263,7 +1202,7 @@ unlink1 (struct usb_hcd *hcd, struct urb *urb)
* caller guarantees urb won't be recycled till both unlink()
* and the urb's completion function return
*/
-static int hcd_unlink_urb (struct urb *urb, int status)
+int usb_hcd_unlink_urb (struct urb *urb, int status)
{
struct usb_host_endpoint *ep;
struct usb_hcd *hcd = NULL;
@@ -1296,7 +1235,7 @@ static int hcd_unlink_urb (struct urb *urb, int status)
spin_lock (&hcd_data_lock);
sys = &urb->dev->dev;
- hcd = urb->dev->bus->hcpriv;
+ hcd = bus_to_hcd(urb->dev->bus);
if (hcd == NULL) {
retval = -ENODEV;
goto done;
@@ -1354,41 +1293,33 @@ done:
/*-------------------------------------------------------------------------*/
/* disables the endpoint: cancels any pending urbs, then synchronizes with
- * the hcd to make sure all endpoint state is gone from hardware. use for
+ * the hcd to make sure all endpoint state is gone from hardware, and then
+ * waits until the endpoint's queue is completely drained. use for
* set_configuration, set_interface, driver removal, physical disconnect.
*
* example: a qh stored in ep->hcpriv, holding state related to endpoint
* type, maxpacket size, toggle, halt status, and scheduling.
*/
-static void
-hcd_endpoint_disable (struct usb_device *udev, struct usb_host_endpoint *ep)
+void usb_hcd_endpoint_disable (struct usb_device *udev,
+ struct usb_host_endpoint *ep)
{
struct usb_hcd *hcd;
struct urb *urb;
- hcd = udev->bus->hcpriv;
+ hcd = bus_to_hcd(udev->bus);
WARN_ON (!HC_IS_RUNNING (hcd->state) && hcd->state != HC_STATE_HALT &&
udev->state != USB_STATE_NOTATTACHED);
local_irq_disable ();
- /* FIXME move most of this into message.c as part of its
- * endpoint disable logic
- */
-
/* ep is already gone from udev->ep_{in,out}[]; no more submits */
rescan:
spin_lock (&hcd_data_lock);
list_for_each_entry (urb, &ep->urb_list, urb_list) {
int tmp;
- /* another cpu may be in hcd, spinning on hcd_data_lock
- * to giveback() this urb. the races here should be
- * small, but a full fix needs a new "can't submit"
- * urb state.
- * FIXME urb->reject should allow that...
- */
+ /* the urb may already have been unlinked */
if (urb->status != -EINPROGRESS)
continue;
usb_get_urb (urb);
@@ -1430,6 +1361,30 @@ rescan:
might_sleep ();
if (hcd->driver->endpoint_disable)
hcd->driver->endpoint_disable (hcd, ep);
+
+ /* Wait until the endpoint queue is completely empty. Most HCDs
+ * will have done this already in their endpoint_disable method,
+ * but some might not. And there could be root-hub control URBs
+ * still pending since they aren't affected by the HCDs'
+ * endpoint_disable methods.
+ */
+ while (!list_empty (&ep->urb_list)) {
+ spin_lock_irq (&hcd_data_lock);
+
+ /* The list may have changed while we acquired the spinlock */
+ urb = NULL;
+ if (!list_empty (&ep->urb_list)) {
+ urb = list_entry (ep->urb_list.prev, struct urb,
+ urb_list);
+ usb_get_urb (urb);
+ }
+ spin_unlock_irq (&hcd_data_lock);
+
+ if (urb) {
+ usb_kill_urb (urb);
+ usb_put_urb (urb);
+ }
+ }
}
/*-------------------------------------------------------------------------*/
@@ -1476,50 +1431,6 @@ int hcd_bus_resume (struct usb_bus *bus)
return status;
}
-/*
- * usb_hcd_suspend_root_hub - HCD autosuspends downstream ports
- * @hcd: host controller for this root hub
- *
- * This call arranges that usb_hcd_resume_root_hub() is safe to call later;
- * that the HCD's root hub polling is deactivated; and that the root's hub
- * driver is suspended. HCDs may call this to autosuspend when their root
- * hub's downstream ports are all inactive: unpowered, disconnected,
- * disabled, or suspended.
- *
- * The HCD will autoresume on device connect change detection (using SRP
- * or a D+/D- pullup). The HCD also autoresumes on remote wakeup signaling
- * from any ports that are suspended (if that is enabled). In most cases,
- * overcurrent signaling (on powered ports) will also start autoresume.
- *
- * Always called with IRQs blocked.
- */
-void usb_hcd_suspend_root_hub (struct usb_hcd *hcd)
-{
- struct urb *urb;
-
- spin_lock (&hcd_root_hub_lock);
- usb_suspend_root_hub (hcd->self.root_hub);
-
- /* force status urb to complete/unlink while suspended */
- if (hcd->status_urb) {
- urb = hcd->status_urb;
- urb->status = -ECONNRESET;
- urb->hcpriv = NULL;
- urb->actual_length = 0;
-
- del_timer (&hcd->rh_timer);
- hcd->poll_pending = 0;
- hcd->status_urb = NULL;
- } else
- urb = NULL;
- spin_unlock (&hcd_root_hub_lock);
- hcd->state = HC_STATE_SUSPENDED;
-
- if (urb)
- usb_hcd_giveback_urb (hcd, urb, NULL);
-}
-EXPORT_SYMBOL_GPL(usb_hcd_suspend_root_hub);
-
/**
* usb_hcd_resume_root_hub - called by HCD to resume its root hub
* @hcd: host controller for this root hub
@@ -1583,20 +1494,6 @@ EXPORT_SYMBOL (usb_bus_start_enum);
/*-------------------------------------------------------------------------*/
-/*
- * usb_hcd_operations - adapts usb_bus framework to HCD framework (bus glue)
- */
-static struct usb_operations usb_hcd_operations = {
- .get_frame_number = hcd_get_frame_number,
- .submit_urb = hcd_submit_urb,
- .unlink_urb = hcd_unlink_urb,
- .buffer_alloc = hcd_buffer_alloc,
- .buffer_free = hcd_buffer_free,
- .disable = hcd_endpoint_disable,
-};
-
-/*-------------------------------------------------------------------------*/
-
/**
* usb_hcd_giveback_urb - return URB from HCD to device driver
* @hcd: host controller returning the URB
@@ -1617,8 +1514,9 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs
at_root_hub = (urb->dev == hcd->self.root_hub);
urb_unlink (urb);
- /* lower level hcd code should use *_dma exclusively */
- if (hcd->self.controller->dma_mask && !at_root_hub) {
+ /* lower level hcd code should use *_dma exclusively if the
+ * host controller does DMA */
+ if (hcd->self.uses_dma && !at_root_hub) {
if (usb_pipecontrol (urb->pipe)
&& !(urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
dma_unmap_single (hcd->self.controller, urb->setup_dma,
@@ -1704,14 +1602,6 @@ EXPORT_SYMBOL_GPL (usb_hc_died);
/*-------------------------------------------------------------------------*/
-static void hcd_release (struct usb_bus *bus)
-{
- struct usb_hcd *hcd;
-
- hcd = container_of(bus, struct usb_hcd, self);
- kfree(hcd);
-}
-
/**
* usb_create_hcd - create and initialize an HCD structure
* @driver: HC driver that will use this hcd
@@ -1736,13 +1626,12 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
return NULL;
}
dev_set_drvdata(dev, hcd);
+ kref_init(&hcd->kref);
usb_bus_init(&hcd->self);
- hcd->self.op = &usb_hcd_operations;
- hcd->self.hcpriv = hcd;
- hcd->self.release = &hcd_release;
hcd->self.controller = dev;
hcd->self.bus_name = bus_name;
+ hcd->self.uses_dma = (dev->dma_mask != NULL);
init_timer(&hcd->rh_timer);
hcd->rh_timer.function = rh_timer_func;
@@ -1756,10 +1645,25 @@ struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
}
EXPORT_SYMBOL (usb_create_hcd);
+static void hcd_release (struct kref *kref)
+{
+ struct usb_hcd *hcd = container_of (kref, struct usb_hcd, kref);
+
+ kfree(hcd);
+}
+
+struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd)
+{
+ if (hcd)
+ kref_get (&hcd->kref);
+ return hcd;
+}
+EXPORT_SYMBOL (usb_get_hcd);
+
void usb_put_hcd (struct usb_hcd *hcd)
{
- dev_set_drvdata(hcd->self.controller, NULL);
- usb_bus_put(&hcd->self);
+ if (hcd)
+ kref_put (&hcd->kref, hcd_release);
}
EXPORT_SYMBOL (usb_put_hcd);
@@ -1915,6 +1819,16 @@ void usb_remove_hcd(struct usb_hcd *hcd)
}
EXPORT_SYMBOL (usb_remove_hcd);
+void
+usb_hcd_platform_shutdown(struct platform_device* dev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(dev);
+
+ if (hcd->driver->shutdown)
+ hcd->driver->shutdown(hcd);
+}
+EXPORT_SYMBOL (usb_hcd_platform_shutdown);
+
/*-------------------------------------------------------------------------*/
#if defined(CONFIG_USB_MON)
diff --git a/drivers/usb/core/hcd.h b/drivers/usb/core/hcd.h
index 7022aafb2ae..676877c15f8 100644
--- a/drivers/usb/core/hcd.h
+++ b/drivers/usb/core/hcd.h
@@ -55,12 +55,13 @@
/*-------------------------------------------------------------------------*/
-struct usb_hcd { /* usb_bus.hcpriv points to this */
+struct usb_hcd {
/*
* housekeeping
*/
struct usb_bus self; /* hcd is-a bus */
+ struct kref kref; /* reference counter */
const char *product_desc; /* product/vendor string */
char irq_descr[24]; /* driver + bus # */
@@ -85,6 +86,7 @@ struct usb_hcd { /* usb_bus.hcpriv points to this */
unsigned uses_new_polling:1;
unsigned poll_rh:1; /* poll for rh status? */
unsigned poll_pending:1; /* status has changed? */
+ unsigned wireless:1; /* Wireless USB HCD */
int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
@@ -128,8 +130,10 @@ static inline struct usb_bus *hcd_to_bus (struct usb_hcd *hcd)
return &hcd->self;
}
-
-// urb.hcpriv is really hardware-specific
+static inline struct usb_hcd *bus_to_hcd (struct usb_bus *bus)
+{
+ return container_of(bus, struct usb_hcd, self);
+}
struct hcd_timeout { /* timeouts we allocate */
struct list_head timeout_list;
@@ -138,28 +142,6 @@ struct hcd_timeout { /* timeouts we allocate */
/*-------------------------------------------------------------------------*/
-/*
- * FIXME usb_operations should vanish or become hc_driver,
- * when usb_bus and usb_hcd become the same thing.
- */
-
-struct usb_operations {
- int (*get_frame_number) (struct usb_device *usb_dev);
- int (*submit_urb) (struct urb *urb, gfp_t mem_flags);
- int (*unlink_urb) (struct urb *urb, int status);
-
- /* allocate dma-consistent buffer for URB_DMA_NOMAPPING */
- void *(*buffer_alloc)(struct usb_bus *bus, size_t size,
- gfp_t mem_flags,
- dma_addr_t *dma);
- void (*buffer_free)(struct usb_bus *bus, size_t size,
- void *addr, dma_addr_t dma);
-
- void (*disable)(struct usb_device *udev,
- struct usb_host_endpoint *ep);
-};
-
-/* each driver provides one of these, and hardware init support */
struct pt_regs;
@@ -192,6 +174,9 @@ struct hc_driver {
/* cleanly make HCD stop writing memory and doing I/O */
void (*stop) (struct usb_hcd *hcd);
+ /* shutdown HCD */
+ void (*shutdown) (struct usb_hcd *hcd);
+
/* return current frame number */
int (*get_frame_number) (struct usb_hcd *hcd);
@@ -218,15 +203,25 @@ struct hc_driver {
/* Needed only if port-change IRQs are level-triggered */
};
-extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb, struct pt_regs *regs);
+extern int usb_hcd_submit_urb (struct urb *urb, gfp_t mem_flags);
+extern int usb_hcd_unlink_urb (struct urb *urb, int status);
+extern void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb,
+ struct pt_regs *regs);
+extern void usb_hcd_endpoint_disable (struct usb_device *udev,
+ struct usb_host_endpoint *ep);
+extern int usb_hcd_get_frame_number (struct usb_device *udev);
extern struct usb_hcd *usb_create_hcd (const struct hc_driver *driver,
struct device *dev, char *bus_name);
+extern struct usb_hcd *usb_get_hcd (struct usb_hcd *hcd);
extern void usb_put_hcd (struct usb_hcd *hcd);
extern int usb_add_hcd(struct usb_hcd *hcd,
unsigned int irqnum, unsigned long irqflags);
extern void usb_remove_hcd(struct usb_hcd *hcd);
+struct platform_device;
+extern void usb_hcd_platform_shutdown(struct platform_device* dev);
+
#ifdef CONFIG_PCI
struct pci_dev;
struct pci_device_id;
@@ -239,6 +234,8 @@ extern int usb_hcd_pci_suspend (struct pci_dev *dev, pm_message_t state);
extern int usb_hcd_pci_resume (struct pci_dev *dev);
#endif /* CONFIG_PM */
+extern void usb_hcd_pci_shutdown (struct pci_dev *dev);
+
#endif /* CONFIG_PCI */
/* pci-ish (pdev null is ok) buffer alloc/mapping support */
@@ -352,8 +349,6 @@ extern long usb_calc_bus_time (int speed, int is_input,
/*-------------------------------------------------------------------------*/
-extern struct usb_bus *usb_alloc_bus (struct usb_operations *);
-
extern void usb_set_device_state(struct usb_device *udev,
enum usb_device_state new_state);
@@ -365,9 +360,6 @@ extern struct list_head usb_bus_list;
extern struct mutex usb_bus_list_lock;
extern wait_queue_head_t usb_kill_urb_queue;
-extern struct usb_bus *usb_bus_get (struct usb_bus *bus);
-extern void usb_bus_put (struct usb_bus *bus);
-
extern void usb_enable_root_hub_irq (struct usb_bus *bus);
extern int usb_find_interface_driver (struct usb_device *dev,
@@ -376,17 +368,11 @@ extern int usb_find_interface_driver (struct usb_device *dev,
#define usb_endpoint_out(ep_dir) (!((ep_dir) & USB_DIR_IN))
#ifdef CONFIG_PM
-extern void usb_hcd_suspend_root_hub (struct usb_hcd *hcd);
extern void usb_hcd_resume_root_hub (struct usb_hcd *hcd);
extern void usb_root_hub_lost_power (struct usb_device *rhdev);
extern int hcd_bus_suspend (struct usb_bus *bus);
extern int hcd_bus_resume (struct usb_bus *bus);
#else
-static inline void usb_hcd_suspend_root_hub(struct usb_hcd *hcd)
-{
- return;
-}
-
static inline void usb_hcd_resume_root_hub(struct usb_hcd *hcd)
{
return;
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 26c8cb5f3e6..7676690a038 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -293,7 +293,7 @@ void usb_kick_khubd(struct usb_device *hdev)
/* completion function, fires on port status changes and various faults */
static void hub_irq(struct urb *urb, struct pt_regs *regs)
{
- struct usb_hub *hub = (struct usb_hub *)urb->context;
+ struct usb_hub *hub = urb->context;
int status;
int i;
unsigned long bits;
@@ -311,7 +311,7 @@ static void hub_irq(struct urb *urb, struct pt_regs *regs)
goto resubmit;
hub->error = urb->status;
/* FALL THROUGH */
-
+
/* let khubd handle things */
case 0: /* we got data: port status changed */
bits = 0;
@@ -452,18 +452,14 @@ static void hub_power_on(struct usb_hub *hub)
msleep(max(pgood_delay, (unsigned) 100));
}
-static inline void __hub_quiesce(struct usb_hub *hub)
+static void hub_quiesce(struct usb_hub *hub)
{
/* (nonblocking) khubd and related activity won't re-trigger */
hub->quiescing = 1;
hub->activating = 0;
hub->resume_root_hub = 0;
-}
-static void hub_quiesce(struct usb_hub *hub)
-{
/* (blocking) stop khubd and related activity */
- __hub_quiesce(hub);
usb_kill_urb(hub->urb);
if (hub->has_indicators)
cancel_delayed_work(&hub->leds);
@@ -868,13 +864,8 @@ descriptor_error:
endpoint = &desc->endpoint[0].desc;
- /* Output endpoint? Curiouser and curiouser.. */
- if (!(endpoint->bEndpointAddress & USB_DIR_IN))
- goto descriptor_error;
-
- /* If it's not an interrupt endpoint, we'd better punt! */
- if ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- != USB_ENDPOINT_XFER_INT)
+ /* If it's not an interrupt in endpoint, we'd better punt! */
+ if (!usb_endpoint_is_int_in(endpoint))
goto descriptor_error;
/* We found a hub */
@@ -1022,26 +1013,29 @@ void usb_set_device_state(struct usb_device *udev,
if (udev->state == USB_STATE_NOTATTACHED)
; /* do nothing */
else if (new_state != USB_STATE_NOTATTACHED) {
- udev->state = new_state;
/* root hub wakeup capabilities are managed out-of-band
* and may involve silicon errata ... ignore them here.
*/
if (udev->parent) {
- if (new_state == USB_STATE_CONFIGURED)
+ if (udev->state == USB_STATE_SUSPENDED
+ || new_state == USB_STATE_SUSPENDED)
+ ; /* No change to wakeup settings */
+ else if (new_state == USB_STATE_CONFIGURED)
device_init_wakeup(&udev->dev,
(udev->actconfig->desc.bmAttributes
& USB_CONFIG_ATT_WAKEUP));
- else if (new_state != USB_STATE_SUSPENDED)
+ else
device_init_wakeup(&udev->dev, 0);
}
+ udev->state = new_state;
} else
recursively_mark_NOTATTACHED(udev);
spin_unlock_irqrestore(&device_state_lock, flags);
}
-#ifdef CONFIG_PM
+#ifdef CONFIG_PM
/**
* usb_root_hub_lost_power - called by HCD if the root hub lost Vbus power
@@ -1059,6 +1053,12 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
unsigned long flags;
dev_warn(&rhdev->dev, "root hub lost power or was reset\n");
+
+ /* Make sure no potential wakeup events get lost,
+ * by forcing the root hub to be resumed.
+ */
+ rhdev->dev.power.prev_state.event = PM_EVENT_ON;
+
spin_lock_irqsave(&device_state_lock, flags);
hub = hdev_to_hub(rhdev);
for (port1 = 1; port1 <= rhdev->maxchild; ++port1) {
@@ -1072,7 +1072,7 @@ void usb_root_hub_lost_power(struct usb_device *rhdev)
}
EXPORT_SYMBOL_GPL(usb_root_hub_lost_power);
-#endif
+#endif /* CONFIG_PM */
static void choose_address(struct usb_device *udev)
{
@@ -1148,144 +1148,28 @@ void usb_disconnect(struct usb_device **pdev)
* cleaning up all state associated with the current configuration
* so that the hardware is now fully quiesced.
*/
+ dev_dbg (&udev->dev, "unregistering device\n");
usb_disable_device(udev, 0);
- usb_notify_remove_device(udev);
+ usb_unlock_device(udev);
- /* Free the device number, remove the /proc/bus/usb entry and
- * the sysfs attributes, and delete the parent's children[]
+ /* Unregister the device. The device driver is responsible
+ * for removing the device files from usbfs and sysfs and for
+ * de-configuring the device.
+ */
+ device_del(&udev->dev);
+
+ /* Free the device number and delete the parent's children[]
* (or root_hub) pointer.
*/
- dev_dbg (&udev->dev, "unregistering device\n");
release_address(udev);
- usb_remove_sysfs_dev_files(udev);
/* Avoid races with recursively_mark_NOTATTACHED() */
spin_lock_irq(&device_state_lock);
*pdev = NULL;
spin_unlock_irq(&device_state_lock);
- usb_unlock_device(udev);
-
- device_unregister(&udev->dev);
-}
-
-static inline const char *plural(int n)
-{
- return (n == 1 ? "" : "s");
-}
-
-static int choose_configuration(struct usb_device *udev)
-{
- int i;
- int num_configs;
- int insufficient_power = 0;
- struct usb_host_config *c, *best;
-
- best = NULL;
- c = udev->config;
- num_configs = udev->descriptor.bNumConfigurations;
- for (i = 0; i < num_configs; (i++, c++)) {
- struct usb_interface_descriptor *desc = NULL;
-
- /* It's possible that a config has no interfaces! */
- if (c->desc.bNumInterfaces > 0)
- desc = &c->intf_cache[0]->altsetting->desc;
-
- /*
- * HP's USB bus-powered keyboard has only one configuration
- * and it claims to be self-powered; other devices may have
- * similar errors in their descriptors. If the next test
- * were allowed to execute, such configurations would always
- * be rejected and the devices would not work as expected.
- * In the meantime, we run the risk of selecting a config
- * that requires external power at a time when that power
- * isn't available. It seems to be the lesser of two evils.
- *
- * Bugzilla #6448 reports a device that appears to crash
- * when it receives a GET_DEVICE_STATUS request! We don't
- * have any other way to tell whether a device is self-powered,
- * but since we don't use that information anywhere but here,
- * the call has been removed.
- *
- * Maybe the GET_DEVICE_STATUS call and the test below can
- * be reinstated when device firmwares become more reliable.
- * Don't hold your breath.
- */
-#if 0
- /* Rule out self-powered configs for a bus-powered device */
- if (bus_powered && (c->desc.bmAttributes &
- USB_CONFIG_ATT_SELFPOWER))
- continue;
-#endif
-
- /*
- * The next test may not be as effective as it should be.
- * Some hubs have errors in their descriptor, claiming
- * to be self-powered when they are really bus-powered.
- * We will overestimate the amount of current such hubs
- * make available for each port.
- *
- * This is a fairly benign sort of failure. It won't
- * cause us to reject configurations that we should have
- * accepted.
- */
-
- /* Rule out configs that draw too much bus current */
- if (c->desc.bMaxPower * 2 > udev->bus_mA) {
- insufficient_power++;
- continue;
- }
-
- /* If the first config's first interface is COMM/2/0xff
- * (MSFT RNDIS), rule it out unless Linux has host-side
- * RNDIS support. */
- if (i == 0 && desc
- && desc->bInterfaceClass == USB_CLASS_COMM
- && desc->bInterfaceSubClass == 2
- && desc->bInterfaceProtocol == 0xff) {
-#ifndef CONFIG_USB_NET_RNDIS_HOST
- continue;
-#else
- best = c;
-#endif
- }
-
- /* From the remaining configs, choose the first one whose
- * first interface is for a non-vendor-specific class.
- * Reason: Linux is more likely to have a class driver
- * than a vendor-specific driver. */
- else if (udev->descriptor.bDeviceClass !=
- USB_CLASS_VENDOR_SPEC &&
- (!desc || desc->bInterfaceClass !=
- USB_CLASS_VENDOR_SPEC)) {
- best = c;
- break;
- }
-
- /* If all the remaining configs are vendor-specific,
- * choose the first one. */
- else if (!best)
- best = c;
- }
-
- if (insufficient_power > 0)
- dev_info(&udev->dev, "rejected %d configuration%s "
- "due to insufficient available bus power\n",
- insufficient_power, plural(insufficient_power));
-
- if (best) {
- i = best->desc.bConfigurationValue;
- dev_info(&udev->dev,
- "configuration #%d chosen from %d choice%s\n",
- i, num_configs, plural(num_configs));
- } else {
- i = -1;
- dev_warn(&udev->dev,
- "no configuration chosen from %d choice%s\n",
- num_configs, plural(num_configs));
- }
- return i;
+ put_device(&udev->dev);
}
#ifdef DEBUG
@@ -1328,7 +1212,6 @@ static inline void show_string(struct usb_device *udev, char *id, char *string)
int usb_new_device(struct usb_device *udev)
{
int err;
- int c;
err = usb_get_configuration(udev);
if (err < 0) {
@@ -1371,8 +1254,7 @@ int usb_new_device(struct usb_device *udev)
USB_DT_OTG, (void **) &desc) == 0) {
if (desc->bmAttributes & USB_OTG_HNP) {
unsigned port1 = udev->portnum;
- struct usb_device *root = udev->parent;
-
+
dev_info(&udev->dev,
"Dual-Role OTG device on %sHNP port\n",
(port1 == bus->otg_port)
@@ -1407,9 +1289,9 @@ int usb_new_device(struct usb_device *udev)
* (Includes HNP test device.)
*/
if (udev->bus->b_hnp_enable || udev->bus->is_b_host) {
- static int __usb_suspend_device(struct usb_device *,
+ static int __usb_port_suspend(struct usb_device *,
int port1);
- err = __usb_suspend_device(udev, udev->bus->otg_port);
+ err = __usb_port_suspend(udev, udev->bus->otg_port);
if (err < 0)
dev_dbg(&udev->dev, "HNP fail, %d\n", err);
}
@@ -1418,34 +1300,15 @@ int usb_new_device(struct usb_device *udev)
}
#endif
- /* put device-specific files into sysfs */
+ /* Register the device. The device driver is responsible
+ * for adding the device files to usbfs and sysfs and for
+ * configuring the device.
+ */
err = device_add (&udev->dev);
if (err) {
dev_err(&udev->dev, "can't device_add, error %d\n", err);
goto fail;
}
- usb_create_sysfs_dev_files (udev);
-
- usb_lock_device(udev);
-
- /* choose and set the configuration. that registers the interfaces
- * with the driver core, and lets usb device drivers bind to them.
- */
- c = choose_configuration(udev);
- if (c >= 0) {
- err = usb_set_configuration(udev, c);
- if (err) {
- dev_err(&udev->dev, "can't set config #%d, error %d\n",
- c, err);
- /* This need not be fatal. The user can try to
- * set other configurations. */
- }
- }
-
- /* USB device state == configured ... usable */
- usb_notify_add_device(udev);
-
- usb_unlock_device(udev);
return 0;
@@ -1472,6 +1335,18 @@ static int hub_port_status(struct usb_hub *hub, int port1,
return ret;
}
+
+/* Returns 1 if @hub is a WUSB root hub, 0 otherwise */
+static unsigned hub_is_wusb(struct usb_hub *hub)
+{
+ struct usb_hcd *hcd;
+ if (hub->hdev->parent != NULL) /* not a root hub? */
+ return 0;
+ hcd = container_of(hub->hdev->bus, struct usb_hcd, self);
+ return hcd->wireless;
+}
+
+
#define PORT_RESET_TRIES 5
#define SET_ADDRESS_TRIES 2
#define GET_DESCRIPTOR_TRIES 2
@@ -1512,7 +1387,9 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
/* if we`ve finished resetting, then break out of the loop */
if (!(portstatus & USB_PORT_STAT_RESET) &&
(portstatus & USB_PORT_STAT_ENABLE)) {
- if (portstatus & USB_PORT_STAT_HIGH_SPEED)
+ if (hub_is_wusb(hub))
+ udev->speed = USB_SPEED_VARIABLE;
+ else if (portstatus & USB_PORT_STAT_HIGH_SPEED)
udev->speed = USB_SPEED_HIGH;
else if (portstatus & USB_PORT_STAT_LOW_SPEED)
udev->speed = USB_SPEED_LOW;
@@ -1607,6 +1484,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
kick_khubd(hub);
}
+#ifdef CONFIG_PM
#ifdef CONFIG_USB_SUSPEND
@@ -1633,7 +1511,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
* NOTE: OTG devices may issue remote wakeup (or SRP) even when
* we don't explicitly enable it here.
*/
- if (device_may_wakeup(&udev->dev)) {
+ if (udev->do_remote_wakeup) {
status = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
USB_REQ_SET_FEATURE, USB_RECIP_DEVICE,
USB_DEVICE_REMOTE_WAKEUP, 0,
@@ -1659,7 +1537,8 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
USB_CTRL_SET_TIMEOUT);
} else {
/* device has up to 10 msec to fully suspend */
- dev_dbg(&udev->dev, "usb suspend\n");
+ dev_dbg(&udev->dev, "usb %ssuspend\n",
+ udev->auto_pm ? "auto-" : "");
usb_set_device_state(udev, USB_STATE_SUSPENDED);
msleep(10);
}
@@ -1684,7 +1563,7 @@ static int hub_port_suspend(struct usb_hub *hub, int port1,
* the root hub for their bus goes into global suspend ... so we don't
* (falsely) update the device power state to say it suspended.
*/
-static int __usb_suspend_device (struct usb_device *udev, int port1)
+static int __usb_port_suspend (struct usb_device *udev, int port1)
{
int status = 0;
@@ -1692,49 +1571,29 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
if (port1 < 0)
return port1;
- if (udev->state == USB_STATE_SUSPENDED
- || udev->state == USB_STATE_NOTATTACHED) {
- return 0;
- }
-
- /* all interfaces must already be suspended */
- if (udev->actconfig) {
- int i;
-
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct usb_interface *intf;
-
- intf = udev->actconfig->interface[i];
- if (is_active(intf)) {
- dev_dbg(&intf->dev, "nyet suspended\n");
- return -EBUSY;
- }
- }
- }
-
- /* we only change a device's upstream USB link.
- * root hubs have no upstream USB link.
+ /* we change the device's upstream USB link,
+ * but root hubs have no upstream USB link.
*/
if (udev->parent)
status = hub_port_suspend(hdev_to_hub(udev->parent), port1,
udev);
-
- if (status == 0)
- udev->dev.power.power_state = PMSG_SUSPEND;
+ else {
+ dev_dbg(&udev->dev, "usb %ssuspend\n",
+ udev->auto_pm ? "auto-" : "");
+ usb_set_device_state(udev, USB_STATE_SUSPENDED);
+ }
return status;
}
-#endif
-
/*
- * usb_suspend_device - suspend a usb device
+ * usb_port_suspend - suspend a usb device's upstream port
* @udev: device that's no longer in active use
* Context: must be able to sleep; device not locked; pm locks held
*
* Suspends a USB device that isn't in active use, conserving power.
* Devices may wake out of a suspend, if anything important happens,
* using the remote wakeup mechanism. They may also be taken out of
- * suspend by the host, using usb_resume_device(). It's also routine
+ * suspend by the host, using usb_port_resume(). It's also routine
* to disconnect devices while they are suspended.
*
* This only affects the USB hardware for a device; its interfaces
@@ -1746,17 +1605,9 @@ static int __usb_suspend_device (struct usb_device *udev, int port1)
*
* Returns 0 on success, else negative errno.
*/
-int usb_suspend_device(struct usb_device *udev)
+int usb_port_suspend(struct usb_device *udev)
{
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
- return __usb_suspend_device(udev, udev->portnum);
-#else
- /* NOTE: udev->state unchanged, it's not lying ... */
- udev->dev.power.power_state = PMSG_SUSPEND;
- return 0;
-#endif
+ return __usb_port_suspend(udev, udev->portnum);
}
/*
@@ -1767,7 +1618,7 @@ int usb_suspend_device(struct usb_device *udev)
* resume (by host) or remote wakeup (by device) ... now see what changed
* in the tree that's rooted at this device.
*/
-static int finish_device_resume(struct usb_device *udev)
+static int finish_port_resume(struct usb_device *udev)
{
int status;
u16 devstatus;
@@ -1783,7 +1634,6 @@ static int finish_device_resume(struct usb_device *udev)
usb_set_device_state(udev, udev->actconfig
? USB_STATE_CONFIGURED
: USB_STATE_ADDRESS);
- udev->dev.power.power_state = PMSG_ON;
/* 10.5.4.5 says be sure devices in the tree are still there.
* For now let's assume the device didn't go crazy on resume,
@@ -1798,9 +1648,6 @@ static int finish_device_resume(struct usb_device *udev)
"gone after usb resume? status %d\n",
status);
else if (udev->actconfig) {
- unsigned i;
- int (*resume)(struct device *);
-
le16_to_cpus(&devstatus);
if ((devstatus & (1 << USB_DEVICE_REMOTE_WAKEUP))
&& udev->parent) {
@@ -1811,24 +1658,9 @@ static int finish_device_resume(struct usb_device *udev)
USB_DEVICE_REMOTE_WAKEUP, 0,
NULL, 0,
USB_CTRL_SET_TIMEOUT);
- if (status) {
+ if (status)
dev_dbg(&udev->dev, "disable remote "
"wakeup, status %d\n", status);
- status = 0;
- }
- }
-
- /* resume interface drivers; if this is a hub, it
- * may have a child resume event to deal with soon
- */
- resume = udev->dev.bus->resume;
- for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
- struct device *dev =
- &udev->actconfig->interface[i]->dev;
-
- down(&dev->sem);
- (void) resume(dev);
- up(&dev->sem);
}
status = 0;
@@ -1839,8 +1671,6 @@ static int finish_device_resume(struct usb_device *udev)
return status;
}
-#ifdef CONFIG_USB_SUSPEND
-
static int
hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
{
@@ -1848,6 +1678,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
// dev_dbg(hub->intfdev, "resume port %d\n", port1);
+ set_bit(port1, hub->busy_bits);
+
/* see 7.1.7.7; affects power usage, but not budgeting */
status = clear_port_feature(hub->hdev,
port1, USB_PORT_FEAT_SUSPEND);
@@ -1861,7 +1693,8 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
/* drive resume for at least 20 msec */
if (udev)
- dev_dbg(&udev->dev, "RESUME\n");
+ dev_dbg(&udev->dev, "usb %sresume\n",
+ udev->auto_pm ? "auto-" : "");
msleep(25);
#define LIVE_FLAGS ( USB_PORT_STAT_POWER \
@@ -1891,19 +1724,21 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
/* TRSMRCY = 10 msec */
msleep(10);
if (udev)
- status = finish_device_resume(udev);
+ status = finish_port_resume(udev);
}
}
if (status < 0)
hub_port_logical_disconnect(hub, port1);
+ clear_bit(port1, hub->busy_bits);
+ if (!hub->hdev->parent && !hub->busy_bits[0])
+ usb_enable_root_hub_irq(hub->hdev->bus);
+
return status;
}
-#endif
-
/*
- * usb_resume_device - re-activate a suspended usb device
+ * usb_port_resume - re-activate a suspended usb device's upstream port
* @udev: device to re-activate
* Context: must be able to sleep; device not locked; pm locks held
*
@@ -1915,36 +1750,24 @@ hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
*
* Returns 0 on success, else negative errno.
*/
-int usb_resume_device(struct usb_device *udev)
+int usb_port_resume(struct usb_device *udev)
{
int status;
- if (udev->state == USB_STATE_NOTATTACHED)
- return -ENODEV;
-
- /* selective resume of one downstream hub-to-device port */
+ /* we change the device's upstream USB link,
+ * but root hubs have no upstream USB link.
+ */
if (udev->parent) {
-#ifdef CONFIG_USB_SUSPEND
- if (udev->state == USB_STATE_SUSPENDED) {
- // NOTE swsusp may bork us, device state being wrong...
- // NOTE this fails if parent is also suspended...
- status = hub_port_resume(hdev_to_hub(udev->parent),
- udev->portnum, udev);
- } else
-#endif
- status = 0;
- } else
- status = finish_device_resume(udev);
- if (status < 0)
- dev_dbg(&udev->dev, "can't resume, status %d\n",
- status);
-
- /* rebind drivers that had no suspend() */
- if (status == 0) {
- usb_unlock_device(udev);
- bus_rescan_devices(&usb_bus_type);
- usb_lock_device(udev);
+ // NOTE this fails if parent is also suspended...
+ status = hub_port_resume(hdev_to_hub(udev->parent),
+ udev->portnum, udev);
+ } else {
+ dev_dbg(&udev->dev, "usb %sresume\n",
+ udev->auto_pm ? "auto-" : "");
+ status = finish_port_resume(udev);
}
+ if (status < 0)
+ dev_dbg(&udev->dev, "can't resume, status %d\n", status);
return status;
}
@@ -1952,23 +1775,60 @@ static int remote_wakeup(struct usb_device *udev)
{
int status = 0;
-#ifdef CONFIG_USB_SUSPEND
+ /* All this just to avoid sending a port-resume message
+ * to the parent hub! */
- /* don't repeat RESUME sequence if this device
- * was already woken up by some other task
- */
usb_lock_device(udev);
+ usb_pm_lock(udev);
if (udev->state == USB_STATE_SUSPENDED) {
- dev_dbg(&udev->dev, "RESUME (wakeup)\n");
+ dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
/* TRSMRCY = 10 msec */
msleep(10);
- status = finish_device_resume(udev);
+ status = finish_port_resume(udev);
+ if (status == 0)
+ udev->dev.power.power_state.event = PM_EVENT_ON;
}
+ usb_pm_unlock(udev);
+
+ if (status == 0)
+ usb_autoresume_device(udev, 0);
usb_unlock_device(udev);
-#endif
return status;
}
+#else /* CONFIG_USB_SUSPEND */
+
+/* When CONFIG_USB_SUSPEND isn't set, we never suspend or resume any ports. */
+
+int usb_port_suspend(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int
+finish_port_resume(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int
+hub_port_resume(struct usb_hub *hub, int port1, struct usb_device *udev)
+{
+ return 0;
+}
+
+int usb_port_resume(struct usb_device *udev)
+{
+ return 0;
+}
+
+static inline int remote_wakeup(struct usb_device *udev)
+{
+ return 0;
+}
+
+#endif
+
static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
{
struct usb_hub *hub = usb_get_intfdata (intf);
@@ -1980,13 +1840,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_device *udev;
udev = hdev->children [port1-1];
- if (udev && (udev->dev.power.power_state.event
- == PM_EVENT_ON
+ if (udev && msg.event == PM_EVENT_SUSPEND &&
#ifdef CONFIG_USB_SUSPEND
- || udev->state != USB_STATE_SUSPENDED
+ udev->state != USB_STATE_SUSPENDED
+#else
+ udev->dev.power.power_state.event
+ == PM_EVENT_ON
#endif
- )) {
- dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
+ ) {
+ if (!hdev->auto_pm)
+ dev_dbg(&intf->dev, "port %d nyet suspended\n",
+ port1);
return -EBUSY;
}
}
@@ -2035,66 +1899,22 @@ static int hub_resume(struct usb_interface *intf)
}
}
+ /* tell khubd to look for changes on this hub */
hub_activate(hub);
-
- /* REVISIT: this recursion probably shouldn't exist. Remove
- * this code sometime, after retesting with different root and
- * external hubs.
- */
-#ifdef CONFIG_USB_SUSPEND
- {
- unsigned port1;
-
- for (port1 = 1; port1 <= hdev->maxchild; port1++) {
- struct usb_device *udev;
- u16 portstat, portchange;
-
- udev = hdev->children [port1-1];
- status = hub_port_status(hub, port1, &portstat, &portchange);
- if (status == 0) {
- if (portchange & USB_PORT_STAT_C_SUSPEND) {
- clear_port_feature(hdev, port1,
- USB_PORT_FEAT_C_SUSPEND);
- portchange &= ~USB_PORT_STAT_C_SUSPEND;
- }
-
- /* let khubd handle disconnects etc */
- if (portchange)
- continue;
- }
-
- if (!udev || status < 0)
- continue;
- usb_lock_device(udev);
- if (portstat & USB_PORT_STAT_SUSPEND)
- status = hub_port_resume(hub, port1, udev);
- else {
- status = finish_device_resume(udev);
- if (status < 0) {
- dev_dbg(&intf->dev, "resume port %d --> %d\n",
- port1, status);
- hub_port_logical_disconnect(hub, port1);
- }
- }
- usb_unlock_device(udev);
- }
- }
-#endif
return 0;
}
-void usb_suspend_root_hub(struct usb_device *hdev)
-{
- struct usb_hub *hub = hdev_to_hub(hdev);
+#else /* CONFIG_PM */
- /* This also makes any led blinker stop retriggering. We're called
- * from irq, so the blinker might still be scheduled. Caller promises
- * that the root hub status URB will be canceled.
- */
- __hub_quiesce(hub);
- mark_quiesced(to_usb_interface(hub->intfdev));
+static inline int remote_wakeup(struct usb_device *udev)
+{
+ return 0;
}
+#define hub_suspend NULL
+#define hub_resume NULL
+#endif
+
void usb_resume_root_hub(struct usb_device *hdev)
{
struct usb_hub *hub = hdev_to_hub(hdev);
@@ -2214,6 +2034,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
int i, j, retval;
unsigned delay = HUB_SHORT_RESET_TIME;
enum usb_device_speed oldspeed = udev->speed;
+ char *speed, *type;
/* root hub ports have a slightly longer reset period
* (from USB 2.0 spec, section 7.1.7.5)
@@ -2246,8 +2067,13 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
/* USB 2.0 section 5.5.3 talks about ep0 maxpacket ...
* it's fixed size except for full speed devices.
+ * For Wireless USB devices, ep0 max packet is always 512 (tho
+ * reported as 0xff in the device descriptor). WUSB1.0[4.8.1].
*/
switch (udev->speed) {
+ case USB_SPEED_VARIABLE: /* fixed at 512 */
+ udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(512);
+ break;
case USB_SPEED_HIGH: /* fixed at 64 */
udev->ep0.desc.wMaxPacketSize = __constant_cpu_to_le16(64);
break;
@@ -2265,17 +2091,21 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
goto fail;
}
+ type = "";
+ switch (udev->speed) {
+ case USB_SPEED_LOW: speed = "low"; break;
+ case USB_SPEED_FULL: speed = "full"; break;
+ case USB_SPEED_HIGH: speed = "high"; break;
+ case USB_SPEED_VARIABLE:
+ speed = "variable";
+ type = "Wireless ";
+ break;
+ default: speed = "?"; break;
+ }
dev_info (&udev->dev,
- "%s %s speed USB device using %s and address %d\n",
- (udev->config) ? "reset" : "new",
- ({ char *speed; switch (udev->speed) {
- case USB_SPEED_LOW: speed = "low"; break;
- case USB_SPEED_FULL: speed = "full"; break;
- case USB_SPEED_HIGH: speed = "high"; break;
- default: speed = "?"; break;
- }; speed;}),
- udev->bus->controller->driver->name,
- udev->devnum);
+ "%s %s speed %sUSB device using %s and address %d\n",
+ (udev->config) ? "reset" : "new", speed, type,
+ udev->bus->controller->driver->name, udev->devnum);
/* Set up TT records, if needed */
if (hdev->tt) {
@@ -2317,6 +2147,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
* down tremendously by NAKing the unexpectedly
* early status stage. Also, retry on all errors;
* some devices are flakey.
+ * 255 is for WUSB devices, we actually need to use 512.
+ * WUSB1.0[4.8.1].
*/
for (j = 0; j < 3; ++j) {
buf->bMaxPacketSize0 = 0;
@@ -2326,7 +2158,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
buf, GET_DESCRIPTOR_BUFSIZE,
(i ? USB_CTRL_GET_TIMEOUT : 1000));
switch (buf->bMaxPacketSize0) {
- case 8: case 16: case 32: case 64:
+ case 8: case 16: case 32: case 64: case 255:
if (buf->bDescriptorType ==
USB_DT_DEVICE) {
r = 0;
@@ -2400,7 +2232,8 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
if (retval)
goto fail;
- i = udev->descriptor.bMaxPacketSize0;
+ i = udev->descriptor.bMaxPacketSize0 == 0xff?
+ 512 : udev->descriptor.bMaxPacketSize0;
if (le16_to_cpu(udev->ep0.desc.wMaxPacketSize) != i) {
if (udev->speed != USB_SPEED_FULL ||
!(i == 8 || i == 16 || i == 32 || i == 64)) {
@@ -2585,6 +2418,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
usb_set_device_state(udev, USB_STATE_POWERED);
udev->speed = USB_SPEED_UNKNOWN;
udev->bus_mA = hub->mA_per_port;
+ udev->level = hdev->level + 1;
/* set the address */
choose_address(udev);
@@ -2736,17 +2570,6 @@ static void hub_events(void)
usb_get_intf(intf);
spin_unlock_irq(&hub_event_lock);
- /* Is this is a root hub wanting to reactivate the downstream
- * ports? If so, be sure the interface resumes even if its
- * stub "device" node was never suspended.
- */
- if (i) {
- dpm_runtime_resume(&hdev->dev);
- dpm_runtime_resume(&intf->dev);
- usb_put_intf(intf);
- continue;
- }
-
/* Lock the device, then check to see if we were
* disconnected while waiting for the lock to succeed. */
if (locktree(hdev) < 0) {
@@ -2763,6 +2586,13 @@ static void hub_events(void)
goto loop;
}
+ /* Is this is a root hub wanting to reactivate the downstream
+ * ports? If so, be sure the interface resumes even if its
+ * stub "device" node was never suspended.
+ */
+ if (i)
+ usb_autoresume_device(hdev, 0);
+
/* If this is an inactive or suspended hub, do nothing */
if (hub->quiescing)
goto loop;
@@ -2900,7 +2730,7 @@ static void hub_events(void)
/* If this is a root hub, tell the HCD it's okay to
* re-enable port-change interrupts now. */
- if (!hdev->parent)
+ if (!hdev->parent && !hub->busy_bits[0])
usb_enable_root_hub_irq(hdev->bus);
loop:
@@ -3075,6 +2905,9 @@ int usb_reset_device(struct usb_device *udev)
break;
}
clear_bit(port1, parent_hub->busy_bits);
+ if (!parent_hdev->parent && !parent_hub->busy_bits[0])
+ usb_enable_root_hub_irq(parent_hdev->bus);
+
if (ret < 0)
goto re_enumerate;
@@ -3128,6 +2961,7 @@ re_enumerate:
hub_port_logical_disconnect(parent_hub, port1);
return -ENODEV;
}
+EXPORT_SYMBOL(usb_reset_device);
/**
* usb_reset_composite_device - warn interface drivers and perform a USB port reset
@@ -3163,6 +2997,9 @@ int usb_reset_composite_device(struct usb_device *udev,
return -EINVAL;
}
+ /* Prevent autosuspend during the reset */
+ usb_autoresume_device(udev, 1);
+
if (iface && iface->condition != USB_INTERFACE_BINDING)
iface = NULL;
@@ -3204,5 +3041,7 @@ int usb_reset_composite_device(struct usb_device *udev,
}
}
+ usb_autosuspend_device(udev, 1);
return ret;
}
+EXPORT_SYMBOL(usb_reset_composite_device);
diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h
index 29d5f45a845..0f8e82a4d48 100644
--- a/drivers/usb/core/hub.h
+++ b/drivers/usb/core/hub.h
@@ -212,7 +212,8 @@ struct usb_hub {
unsigned long event_bits[1]; /* status change bitmask */
unsigned long change_bits[1]; /* ports with logical connect
status change */
- unsigned long busy_bits[1]; /* ports being reset */
+ unsigned long busy_bits[1]; /* ports being reset or
+ resumed */
#if USB_MAXCHILDREN > 31 /* 8*sizeof(unsigned long) - 1 */
#error event_bits[] is too short!
#endif
diff --git a/drivers/usb/core/inode.c b/drivers/usb/core/inode.c
index 3182c2224ba..b5d6a79af0b 100644
--- a/drivers/usb/core/inode.c
+++ b/drivers/usb/core/inode.c
@@ -44,7 +44,7 @@
#include "hcd.h"
static struct super_operations usbfs_ops;
-static struct file_operations default_file_operations;
+static const struct file_operations default_file_operations;
static struct vfsmount *usbfs_mount;
static int usbfs_mount_count; /* = 0 */
static int ignore_mount = 0;
@@ -249,7 +249,6 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
inode->i_mode = mode;
inode->i_uid = current->fsuid;
inode->i_gid = current->fsgid;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;
switch (mode & S_IFMT) {
@@ -264,7 +263,7 @@ static struct inode *usbfs_get_inode (struct super_block *sb, int mode, dev_t de
inode->i_fop = &simple_dir_operations;
/* directory inodes start off with i_nlink == 2 (for "." entry) */
- inode->i_nlink++;
+ inc_nlink(inode);
break;
}
}
@@ -296,7 +295,7 @@ static int usbfs_mkdir (struct inode *dir, struct dentry *dentry, int mode)
mode = (mode & (S_IRWXUGO | S_ISVTX)) | S_IFDIR;
res = usbfs_mknod (dir, dentry, mode, 0);
if (!res)
- dir->i_nlink++;
+ inc_nlink(dir);
return res;
}
@@ -333,7 +332,7 @@ static int usbfs_unlink (struct inode *dir, struct dentry *dentry)
{
struct inode *inode = dentry->d_inode;
mutex_lock(&inode->i_mutex);
- dentry->d_inode->i_nlink--;
+ drop_nlink(dentry->d_inode);
dput(dentry);
mutex_unlock(&inode->i_mutex);
d_delete(dentry);
@@ -348,10 +347,11 @@ static int usbfs_rmdir(struct inode *dir, struct dentry *dentry)
mutex_lock(&inode->i_mutex);
dentry_unhash(dentry);
if (usbfs_empty(dentry)) {
- dentry->d_inode->i_nlink -= 2;
+ drop_nlink(dentry->d_inode);
+ drop_nlink(dentry->d_inode);
dput(dentry);
inode->i_flags |= S_DEAD;
- dir->i_nlink--;
+ drop_nlink(dir);
error = 0;
}
mutex_unlock(&inode->i_mutex);
@@ -402,13 +402,13 @@ static loff_t default_file_lseek (struct file *file, loff_t offset, int orig)
static int default_open (struct inode *inode, struct file *file)
{
- if (inode->u.generic_ip)
- file->private_data = inode->u.generic_ip;
+ if (inode->i_private)
+ file->private_data = inode->i_private;
return 0;
}
-static struct file_operations default_file_operations = {
+static const struct file_operations default_file_operations = {
.read = default_read_file,
.write = default_write_file,
.open = default_open,
@@ -495,7 +495,7 @@ static int fs_create_by_name (const char *name, mode_t mode,
static struct dentry *fs_create_file (const char *name, mode_t mode,
struct dentry *parent, void *data,
- struct file_operations *fops,
+ const struct file_operations *fops,
uid_t uid, gid_t gid)
{
struct dentry *dentry;
@@ -509,7 +509,7 @@ static struct dentry *fs_create_file (const char *name, mode_t mode,
} else {
if (dentry->d_inode) {
if (data)
- dentry->d_inode->u.generic_ip = data;
+ dentry->d_inode->i_private = data;
if (fops)
dentry->d_inode->i_fop = fops;
dentry->d_inode->i_uid = uid;
@@ -699,7 +699,7 @@ static void usbfs_remove_device(struct usb_device *dev)
sinfo.si_errno = EPIPE;
sinfo.si_code = SI_ASYNCIO;
sinfo.si_addr = ds->disccontext;
- kill_proc_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
+ kill_pid_info_as_uid(ds->discsignr, &sinfo, ds->disc_pid, ds->disc_uid, ds->disc_euid, ds->secid);
}
}
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
index 4cc8d3e67db..85b1cd18336 100644
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -23,59 +23,44 @@ static void usb_api_blocking_completion(struct urb *urb, struct pt_regs *regs)
}
-static void timeout_kill(unsigned long data)
-{
- struct urb *urb = (struct urb *) data;
-
- usb_unlink_urb(urb);
-}
-
-// Starts urb and waits for completion or timeout
-// note that this call is NOT interruptible, while
-// many device driver i/o requests should be interruptible
-static int usb_start_wait_urb(struct urb *urb, int timeout, int* actual_length)
+/*
+ * Starts urb and waits for completion or timeout. Note that this call
+ * is NOT interruptible. Many device driver i/o requests should be
+ * interruptible and therefore these drivers should implement their
+ * own interruptible routines.
+ */
+static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
{
- struct completion done;
- struct timer_list timer;
- int status;
+ struct completion done;
+ unsigned long expire;
+ int status;
init_completion(&done);
urb->context = &done;
urb->actual_length = 0;
status = usb_submit_urb(urb, GFP_NOIO);
-
- if (status == 0) {
- if (timeout > 0) {
- init_timer(&timer);
- timer.expires = jiffies + msecs_to_jiffies(timeout);
- timer.data = (unsigned long)urb;
- timer.function = timeout_kill;
- /* grr. timeout _should_ include submit delays. */
- add_timer(&timer);
- }
- wait_for_completion(&done);
+ if (unlikely(status))
+ goto out;
+
+ expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
+ if (!wait_for_completion_timeout(&done, expire)) {
+
+ dev_dbg(&urb->dev->dev,
+ "%s timed out on ep%d%s len=%d/%d\n",
+ current->comm,
+ usb_pipeendpoint(urb->pipe),
+ usb_pipein(urb->pipe) ? "in" : "out",
+ urb->actual_length,
+ urb->transfer_buffer_length);
+
+ usb_kill_urb(urb);
+ status = urb->status == -ENOENT ? -ETIMEDOUT : urb->status;
+ } else
status = urb->status;
- /* note: HCDs return ETIMEDOUT for other reasons too */
- if (status == -ECONNRESET) {
- dev_dbg(&urb->dev->dev,
- "%s timed out on ep%d%s len=%d/%d\n",
- current->comm,
- usb_pipeendpoint(urb->pipe),
- usb_pipein(urb->pipe) ? "in" : "out",
- urb->actual_length,
- urb->transfer_buffer_length
- );
- if (urb->actual_length > 0)
- status = 0;
- else
- status = -ETIMEDOUT;
- }
- if (timeout > 0)
- del_timer_sync(&timer);
- }
-
+out:
if (actual_length)
*actual_length = urb->actual_length;
+
usb_free_urb(urb);
return status;
}
@@ -263,7 +248,7 @@ static void sg_clean (struct usb_sg_request *io)
static void sg_complete (struct urb *urb, struct pt_regs *regs)
{
- struct usb_sg_request *io = (struct usb_sg_request *) urb->context;
+ struct usb_sg_request *io = urb->context;
spin_lock (&io->lock);
@@ -999,8 +984,8 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr)
ep = dev->ep_in[epnum];
dev->ep_in[epnum] = NULL;
}
- if (ep && dev->bus && dev->bus->op && dev->bus->op->disable)
- dev->bus->op->disable(dev, ep);
+ if (ep && dev->bus)
+ usb_hcd_endpoint_disable(dev, ep);
}
/**
@@ -1381,9 +1366,6 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
if (cp && configuration == 0)
dev_warn(&dev->dev, "config 0 descriptor??\n");
- if (dev->state == USB_STATE_SUSPENDED)
- return -EHOSTUNREACH;
-
/* Allocate memory for new interfaces before doing anything else,
* so that if we run out then nothing will have changed. */
n = nintf = 0;
@@ -1418,6 +1400,11 @@ free_interfaces:
configuration, -i);
}
+ /* Wake up the device so we can send it the Set-Config request */
+ ret = usb_autoresume_device(dev, 1);
+ if (ret)
+ goto free_interfaces;
+
/* if it's already configured, clear out old state first.
* getting rid of old interfaces means unbinding their drivers.
*/
@@ -1437,6 +1424,7 @@ free_interfaces:
dev->actconfig = cp;
if (!cp) {
usb_set_device_state(dev, USB_STATE_ADDRESS);
+ usb_autosuspend_device(dev, 1);
goto free_interfaces;
}
usb_set_device_state(dev, USB_STATE_CONFIGURED);
@@ -1505,8 +1493,68 @@ free_interfaces:
usb_create_sysfs_intf_files (intf);
}
+ usb_autosuspend_device(dev, 1);
+ return 0;
+}
+
+struct set_config_request {
+ struct usb_device *udev;
+ int config;
+ struct work_struct work;
+};
+
+/* Worker routine for usb_driver_set_configuration() */
+static void driver_set_config_work(void *_req)
+{
+ struct set_config_request *req = _req;
+
+ usb_lock_device(req->udev);
+ usb_set_configuration(req->udev, req->config);
+ usb_unlock_device(req->udev);
+ usb_put_dev(req->udev);
+ kfree(req);
+}
+
+/**
+ * usb_driver_set_configuration - Provide a way for drivers to change device configurations
+ * @udev: the device whose configuration is being updated
+ * @config: the configuration being chosen.
+ * Context: In process context, must be able to sleep
+ *
+ * Device interface drivers are not allowed to change device configurations.
+ * This is because changing configurations will destroy the interface the
+ * driver is bound to and create new ones; it would be like a floppy-disk
+ * driver telling the computer to replace the floppy-disk drive with a
+ * tape drive!
+ *
+ * Still, in certain specialized circumstances the need may arise. This
+ * routine gets around the normal restrictions by using a work thread to
+ * submit the change-config request.
+ *
+ * Returns 0 if the request was succesfully queued, error code otherwise.
+ * The caller has no way to know whether the queued request will eventually
+ * succeed.
+ */
+int usb_driver_set_configuration(struct usb_device *udev, int config)
+{
+ struct set_config_request *req;
+
+ req = kmalloc(sizeof(*req), GFP_KERNEL);
+ if (!req)
+ return -ENOMEM;
+ req->udev = udev;
+ req->config = config;
+ INIT_WORK(&req->work, driver_set_config_work, req);
+
+ usb_get_dev(udev);
+ if (!schedule_work(&req->work)) {
+ usb_put_dev(udev);
+ kfree(req);
+ return -EINVAL;
+ }
return 0;
}
+EXPORT_SYMBOL_GPL(usb_driver_set_configuration);
// synchronous request completion model
EXPORT_SYMBOL(usb_control_msg);
diff --git a/drivers/usb/core/notify.c b/drivers/usb/core/notify.c
index b042676af0a..6b36897ca15 100644
--- a/drivers/usb/core/notify.c
+++ b/drivers/usb/core/notify.c
@@ -50,8 +50,11 @@ void usb_notify_add_device(struct usb_device *udev)
void usb_notify_remove_device(struct usb_device *udev)
{
+ /* Protect against simultaneous usbfs open */
+ mutex_lock(&usbfs_mutex);
blocking_notifier_call_chain(&usb_notifier_list,
USB_DEVICE_REMOVE, udev);
+ mutex_unlock(&usbfs_mutex);
}
void usb_notify_add_bus(struct usb_bus *ubus)
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index dec973affb0..55d8f575206 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -60,7 +60,7 @@ static ssize_t
set_bConfigurationValue (struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
- struct usb_device *udev = udev = to_usb_device (dev);
+ struct usb_device *udev = to_usb_device (dev);
int config, value;
if (sscanf (buf, "%u", &config) != 1 || config > 255)
@@ -186,6 +186,7 @@ usb_descriptor_attr (bMaxPacketSize0, "%d\n")
static struct attribute *dev_attrs[] = {
/* current configuration's attributes */
+ &dev_attr_configuration.attr,
&dev_attr_bNumInterfaces.attr,
&dev_attr_bConfigurationValue.attr,
&dev_attr_bmAttributes.attr,
@@ -209,20 +210,40 @@ static struct attribute_group dev_attr_grp = {
.attrs = dev_attrs,
};
-void usb_create_sysfs_dev_files (struct usb_device *udev)
+int usb_create_sysfs_dev_files(struct usb_device *udev)
{
struct device *dev = &udev->dev;
+ int retval;
- sysfs_create_group(&dev->kobj, &dev_attr_grp);
+ retval = sysfs_create_group(&dev->kobj, &dev_attr_grp);
+ if (retval)
+ return retval;
- if (udev->manufacturer)
- device_create_file (dev, &dev_attr_manufacturer);
- if (udev->product)
- device_create_file (dev, &dev_attr_product);
- if (udev->serial)
- device_create_file (dev, &dev_attr_serial);
- device_create_file (dev, &dev_attr_configuration);
- usb_create_ep_files(dev, &udev->ep0, udev);
+ if (udev->manufacturer) {
+ retval = device_create_file (dev, &dev_attr_manufacturer);
+ if (retval)
+ goto error;
+ }
+ if (udev->product) {
+ retval = device_create_file (dev, &dev_attr_product);
+ if (retval)
+ goto error;
+ }
+ if (udev->serial) {
+ retval = device_create_file (dev, &dev_attr_serial);
+ if (retval)
+ goto error;
+ }
+ retval = usb_create_ep_files(dev, &udev->ep0, udev);
+ if (retval)
+ goto error;
+ return 0;
+error:
+ usb_remove_ep_files(&udev->ep0);
+ device_remove_file(dev, &dev_attr_manufacturer);
+ device_remove_file(dev, &dev_attr_product);
+ device_remove_file(dev, &dev_attr_serial);
+ return retval;
}
void usb_remove_sysfs_dev_files (struct usb_device *udev)
@@ -238,7 +259,6 @@ void usb_remove_sysfs_dev_files (struct usb_device *udev)
device_remove_file(dev, &dev_attr_product);
if (udev->serial)
device_remove_file(dev, &dev_attr_serial);
- device_remove_file (dev, &dev_attr_configuration);
}
/* Interface fields */
@@ -340,18 +360,28 @@ static inline void usb_remove_intf_ep_files(struct usb_interface *intf)
usb_remove_ep_files(&iface_desc->endpoint[i]);
}
-void usb_create_sysfs_intf_files (struct usb_interface *intf)
+int usb_create_sysfs_intf_files(struct usb_interface *intf)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_host_interface *alt = intf->cur_altsetting;
+ int retval;
- sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+ retval = sysfs_create_group(&intf->dev.kobj, &intf_attr_grp);
+ if (retval)
+ goto error;
if (alt->string == NULL)
alt->string = usb_cache_string(udev, alt->desc.iInterface);
if (alt->string)
- device_create_file(&intf->dev, &dev_attr_interface);
+ retval = device_create_file(&intf->dev, &dev_attr_interface);
usb_create_intf_ep_files(intf, udev);
+ return 0;
+error:
+ if (alt->string)
+ device_remove_file(&intf->dev, &dev_attr_interface);
+ sysfs_remove_group(&intf->dev.kobj, &intf_attr_grp);
+ usb_remove_intf_ep_files(intf);
+ return retval;
}
void usb_remove_sysfs_intf_files (struct usb_interface *intf)
diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c
index 9864988377c..9801d08edac 100644
--- a/drivers/usb/core/urb.c
+++ b/drivers/usb/core/urb.c
@@ -57,7 +57,7 @@ struct urb *usb_alloc_urb(int iso_packets, gfp_t mem_flags)
{
struct urb *urb;
- urb = (struct urb *)kmalloc(sizeof(struct urb) +
+ urb = kmalloc(sizeof(struct urb) +
iso_packets * sizeof(struct usb_iso_packet_descriptor),
mem_flags);
if (!urb) {
@@ -221,7 +221,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
{
int pipe, temp, max;
struct usb_device *dev;
- struct usb_operations *op;
int is_out;
if (!urb || urb->hcpriv || !urb->complete)
@@ -233,8 +232,6 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
if (dev->bus->controller->power.power_state.event != PM_EVENT_ON
|| dev->state == USB_STATE_SUSPENDED)
return -EHOSTUNREACH;
- if (!(op = dev->bus->op) || !op->submit_urb)
- return -ENODEV;
urb->status = -EINPROGRESS;
urb->actual_length = 0;
@@ -376,7 +373,7 @@ int usb_submit_urb(struct urb *urb, gfp_t mem_flags)
urb->interval = temp;
}
- return op->submit_urb (urb, mem_flags);
+ return usb_hcd_submit_urb (urb, mem_flags);
}
/*-------------------------------------------------------------------*/
@@ -440,9 +437,9 @@ int usb_unlink_urb(struct urb *urb)
{
if (!urb)
return -EINVAL;
- if (!(urb->dev && urb->dev->bus && urb->dev->bus->op))
+ if (!(urb->dev && urb->dev->bus))
return -ENODEV;
- return urb->dev->bus->op->unlink_urb(urb, -ECONNRESET);
+ return usb_hcd_unlink_urb(urb, -ECONNRESET);
}
/**
@@ -468,13 +465,13 @@ int usb_unlink_urb(struct urb *urb)
void usb_kill_urb(struct urb *urb)
{
might_sleep();
- if (!(urb && urb->dev && urb->dev->bus && urb->dev->bus->op))
+ if (!(urb && urb->dev && urb->dev->bus))
return;
spin_lock_irq(&urb->lock);
++urb->reject;
spin_unlock_irq(&urb->lock);
- urb->dev->bus->op->unlink_urb(urb, -ENOENT);
+ usb_hcd_unlink_urb(urb, -ENOENT);
wait_event(usb_kill_urb_queue, atomic_read(&urb->use_count) == 0);
spin_lock_irq(&urb->lock);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 184c24660a4..e4df9edf1bc 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -33,6 +33,7 @@
#include <linux/smp_lock.h>
#include <linux/usb.h>
#include <linux/mutex.h>
+#include <linux/workqueue.h>
#include <asm/io.h>
#include <asm/scatterlist.h>
@@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore";
static int nousb; /* Disable USB when built into kernel image */
+struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */
+
/**
* usb_ifnum_to_if - get the interface object with a given interface number
@@ -67,7 +70,8 @@ static int nousb; /* Disable USB when built into kernel image */
* Don't call this function unless you are bound to one of the interfaces
* on this device or you have locked the device!
*/
-struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
+struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
+ unsigned ifnum)
{
struct usb_host_config *config = dev->actconfig;
int i;
@@ -100,8 +104,8 @@ struct usb_interface *usb_ifnum_to_if(struct usb_device *dev, unsigned ifnum)
* Don't call this function unless you are bound to the intf interface
* or you have locked the device!
*/
-struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
- unsigned int altnum)
+struct usb_host_interface *usb_altnum_to_altsetting(const struct usb_interface *intf,
+ unsigned int altnum)
{
int i;
@@ -112,87 +116,6 @@ struct usb_host_interface *usb_altnum_to_altsetting(struct usb_interface *intf,
return NULL;
}
-/**
- * usb_driver_claim_interface - bind a driver to an interface
- * @driver: the driver to be bound
- * @iface: the interface to which it will be bound; must be in the
- * usb device's active configuration
- * @priv: driver data associated with that interface
- *
- * This is used by usb device drivers that need to claim more than one
- * interface on a device when probing (audio and acm are current examples).
- * No device driver should directly modify internal usb_interface or
- * usb_device structure members.
- *
- * Few drivers should need to use this routine, since the most natural
- * way to bind to an interface is to return the private data from
- * the driver's probe() method.
- *
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock. So driver probe() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-int usb_driver_claim_interface(struct usb_driver *driver,
- struct usb_interface *iface, void* priv)
-{
- struct device *dev = &iface->dev;
-
- if (dev->driver)
- return -EBUSY;
-
- dev->driver = &driver->driver;
- usb_set_intfdata(iface, priv);
- iface->condition = USB_INTERFACE_BOUND;
- mark_active(iface);
-
- /* if interface was already added, bind now; else let
- * the future device_add() bind it, bypassing probe()
- */
- if (device_is_registered(dev))
- device_bind_driver(dev);
-
- return 0;
-}
-
-/**
- * usb_driver_release_interface - unbind a driver from an interface
- * @driver: the driver to be unbound
- * @iface: the interface from which it will be unbound
- *
- * This can be used by drivers to release an interface without waiting
- * for their disconnect() methods to be called. In typical cases this
- * also causes the driver disconnect() method to be called.
- *
- * This call is synchronous, and may not be used in an interrupt context.
- * Callers must own the device lock and the driver model's usb_bus_type.subsys
- * writelock. So driver disconnect() entries don't need extra locking,
- * but other call contexts may need to explicitly claim those locks.
- */
-void usb_driver_release_interface(struct usb_driver *driver,
- struct usb_interface *iface)
-{
- struct device *dev = &iface->dev;
-
- /* this should never happen, don't release something that's not ours */
- if (!dev->driver || dev->driver != &driver->driver)
- return;
-
- /* don't release from within disconnect() */
- if (iface->condition != USB_INTERFACE_BOUND)
- return;
-
- /* don't release if the interface hasn't been added yet */
- if (device_is_registered(dev)) {
- iface->condition = USB_INTERFACE_UNBINDING;
- device_release_driver(dev);
- }
-
- dev->driver = NULL;
- usb_set_intfdata(iface, NULL);
- iface->condition = USB_INTERFACE_UNBOUND;
- mark_quiesced(iface);
-}
-
struct find_interface_arg {
int minor;
struct usb_interface *interface;
@@ -204,7 +127,7 @@ static int __find_interface(struct device * dev, void * data)
struct usb_interface *intf;
/* can't look at usb devices, only interfaces */
- if (dev->driver == &usb_generic_driver)
+ if (is_usb_device(dev))
return 0;
intf = to_usb_interface(dev);
@@ -227,147 +150,82 @@ static int __find_interface(struct device * dev, void * data)
struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor)
{
struct find_interface_arg argb;
+ int retval;
argb.minor = minor;
argb.interface = NULL;
- driver_for_each_device(&drv->driver, NULL, &argb, __find_interface);
+ /* eat the error, it will be in argb.interface */
+ retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb,
+ __find_interface);
return argb.interface;
}
-#ifdef CONFIG_HOTPLUG
-
-/*
- * This sends an uevent to userspace, typically helping to load driver
- * or other modules, configure the device, and more. Drivers can provide
- * a MODULE_DEVICE_TABLE to help with module loading subtasks.
+/**
+ * usb_release_dev - free a usb device structure when all users of it are finished.
+ * @dev: device that's been disconnected
*
- * We're called either from khubd (the typical case) or from root hub
- * (init, kapmd, modprobe, rmmod, etc), but the agents need to handle
- * delays in event delivery. Use sysfs (and DEVPATH) to make sure the
- * device (and this configuration!) are still present.
+ * Will be called only by the device core when all users of this usb device are
+ * done.
*/
-static int usb_uevent(struct device *dev, char **envp, int num_envp,
- char *buffer, int buffer_size)
+static void usb_release_dev(struct device *dev)
{
- struct usb_interface *intf;
- struct usb_device *usb_dev;
- struct usb_host_interface *alt;
- int i = 0;
- int length = 0;
-
- if (!dev)
- return -ENODEV;
-
- /* driver is often null here; dev_dbg() would oops */
- pr_debug ("usb %s: uevent\n", dev->bus_id);
-
- /* Must check driver_data here, as on remove driver is always NULL */
- if ((dev->driver == &usb_generic_driver) ||
- (dev->driver_data == &usb_generic_driver_data))
- return 0;
-
- intf = to_usb_interface(dev);
- usb_dev = interface_to_usbdev (intf);
- alt = intf->cur_altsetting;
+ struct usb_device *udev;
- if (usb_dev->devnum < 0) {
- pr_debug ("usb %s: already deleted?\n", dev->bus_id);
- return -ENODEV;
- }
- if (!usb_dev->bus) {
- pr_debug ("usb %s: bus removed?\n", dev->bus_id);
- return -ENODEV;
- }
+ udev = to_usb_device(dev);
-#ifdef CONFIG_USB_DEVICEFS
- /* If this is available, userspace programs can directly read
- * all the device descriptors we don't tell them about. Or
- * even act as usermode drivers.
- *
- * FIXME reduce hardwired intelligence here
- */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "DEVICE=/proc/bus/usb/%03d/%03d",
- usb_dev->bus->busnum, usb_dev->devnum))
- return -ENOMEM;
+#ifdef CONFIG_USB_SUSPEND
+ cancel_delayed_work(&udev->autosuspend);
+ flush_workqueue(ksuspend_usb_wq);
#endif
+ usb_destroy_configuration(udev);
+ usb_put_hcd(bus_to_hcd(udev->bus));
+ kfree(udev->product);
+ kfree(udev->manufacturer);
+ kfree(udev->serial);
+ kfree(udev);
+}
- /* per-device configurations are common */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "PRODUCT=%x/%x/%x",
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- le16_to_cpu(usb_dev->descriptor.bcdDevice)))
- return -ENOMEM;
+#ifdef CONFIG_PM
- /* class-based driver binding models */
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "TYPE=%d/%d/%d",
- usb_dev->descriptor.bDeviceClass,
- usb_dev->descriptor.bDeviceSubClass,
- usb_dev->descriptor.bDeviceProtocol))
+static int ksuspend_usb_init(void)
+{
+ ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd");
+ if (!ksuspend_usb_wq)
return -ENOMEM;
+ return 0;
+}
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "INTERFACE=%d/%d/%d",
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
+static void ksuspend_usb_cleanup(void)
+{
+ destroy_workqueue(ksuspend_usb_wq);
+}
- if (add_uevent_var(envp, num_envp, &i,
- buffer, buffer_size, &length,
- "MODALIAS=usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02Xic%02Xisc%02Xip%02X",
- le16_to_cpu(usb_dev->descriptor.idVendor),
- le16_to_cpu(usb_dev->descriptor.idProduct),
- le16_to_cpu(usb_dev->descriptor.bcdDevice),
- usb_dev->descriptor.bDeviceClass,
- usb_dev->descriptor.bDeviceSubClass,
- usb_dev->descriptor.bDeviceProtocol,
- alt->desc.bInterfaceClass,
- alt->desc.bInterfaceSubClass,
- alt->desc.bInterfaceProtocol))
- return -ENOMEM;
+#else
- envp[i] = NULL;
+#define ksuspend_usb_init() 0
+#define ksuspend_usb_cleanup() do {} while (0)
- return 0;
-}
+#endif
-#else
+#ifdef CONFIG_USB_SUSPEND
-static int usb_uevent(struct device *dev, char **envp,
- int num_envp, char *buffer, int buffer_size)
+/* usb_autosuspend_work - callback routine to autosuspend a USB device */
+static void usb_autosuspend_work(void *_udev)
{
- return -ENODEV;
-}
+ struct usb_device *udev = _udev;
-#endif /* CONFIG_HOTPLUG */
+ usb_pm_lock(udev);
+ udev->auto_pm = 1;
+ usb_suspend_both(udev, PMSG_SUSPEND);
+ usb_pm_unlock(udev);
+}
-/**
- * usb_release_dev - free a usb device structure when all users of it are finished.
- * @dev: device that's been disconnected
- *
- * Will be called only by the device core when all users of this usb device are
- * done.
- */
-static void usb_release_dev(struct device *dev)
-{
- struct usb_device *udev;
+#else
- udev = to_usb_device(dev);
+static void usb_autosuspend_work(void *_udev)
+{}
- usb_destroy_configuration(udev);
- usb_bus_put(udev->bus);
- kfree(udev->product);
- kfree(udev->manufacturer);
- kfree(udev->serial);
- kfree(udev);
-}
+#endif
/**
* usb_alloc_dev - usb device constructor (usbcore-internal)
@@ -390,8 +248,7 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
if (!dev)
return NULL;
- bus = usb_bus_get(bus);
- if (!bus) {
+ if (!usb_get_hcd(bus_to_hcd(bus))) {
kfree(dev);
return NULL;
}
@@ -399,11 +256,12 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
device_initialize(&dev->dev);
dev->dev.bus = &usb_bus_type;
dev->dev.dma_mask = bus->controller->dma_mask;
- dev->dev.driver_data = &usb_generic_driver_data;
- dev->dev.driver = &usb_generic_driver;
dev->dev.release = usb_release_dev;
dev->state = USB_STATE_ATTACHED;
+ /* This magic assignment distinguishes devices from interfaces */
+ dev->dev.platform_data = &usb_generic_driver;
+
INIT_LIST_HEAD(&dev->ep0.urb_list);
dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;
@@ -444,6 +302,10 @@ usb_alloc_dev(struct usb_device *parent, struct usb_bus *bus, unsigned port1)
dev->parent = parent;
INIT_LIST_HEAD(&dev->filelist);
+#ifdef CONFIG_PM
+ mutex_init(&dev->pm_mutex);
+ INIT_WORK(&dev->autosuspend, usb_autosuspend_work, dev);
+#endif
return dev;
}
@@ -549,7 +411,7 @@ void usb_put_intf(struct usb_interface *intf)
* case the driver already owns the device lock.)
*/
int usb_lock_device_for_reset(struct usb_device *udev,
- struct usb_interface *iface)
+ const struct usb_interface *iface)
{
unsigned long jiffies_expire = jiffies + HZ;
@@ -672,7 +534,139 @@ exit:
*/
int usb_get_current_frame_number(struct usb_device *dev)
{
- return dev->bus->op->get_frame_number (dev);
+ return usb_hcd_get_frame_number (dev);
+}
+
+/**
+ * usb_endpoint_dir_in - check if the endpoint has IN direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type IN, otherwise it returns false.
+ */
+int usb_endpoint_dir_in(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN);
+}
+
+/**
+ * usb_endpoint_dir_out - check if the endpoint has OUT direction
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type OUT, otherwise it returns false.
+ */
+int usb_endpoint_dir_out(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT);
+}
+
+/**
+ * usb_endpoint_xfer_bulk - check if the endpoint has bulk transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type bulk, otherwise it returns false.
+ */
+int usb_endpoint_xfer_bulk(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_BULK);
+}
+
+/**
+ * usb_endpoint_xfer_int - check if the endpoint has interrupt transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type interrupt, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_int(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_INT);
+}
+
+/**
+ * usb_endpoint_xfer_isoc - check if the endpoint has isochronous transfer type
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint is of type isochronous, otherwise it returns
+ * false.
+ */
+int usb_endpoint_xfer_isoc(const struct usb_endpoint_descriptor *epd)
+{
+ return ((epd->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
+ USB_ENDPOINT_XFER_ISOC);
+}
+
+/**
+ * usb_endpoint_is_bulk_in - check if the endpoint is bulk IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_bulk_out - check if the endpoint is bulk OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has bulk transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_bulk_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_bulk(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_int_in - check if the endpoint is interrupt IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_int_out - check if the endpoint is interrupt OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has interrupt transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_int_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_int(epd) && usb_endpoint_dir_out(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_in - check if the endpoint is isochronous IN
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and IN direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_in(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_in(epd));
+}
+
+/**
+ * usb_endpoint_is_isoc_out - check if the endpoint is isochronous OUT
+ * @epd: endpoint to be checked
+ *
+ * Returns true if the endpoint has isochronous transfer type and OUT direction,
+ * otherwise it returns false.
+ */
+int usb_endpoint_is_isoc_out(const struct usb_endpoint_descriptor *epd)
+{
+ return (usb_endpoint_xfer_isoc(epd) && usb_endpoint_dir_out(epd));
}
/*-------------------------------------------------------------------*/
@@ -737,9 +731,9 @@ void *usb_buffer_alloc (
dma_addr_t *dma
)
{
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_alloc)
+ if (!dev || !dev->bus)
return NULL;
- return dev->bus->op->buffer_alloc (dev->bus, size, mem_flags, dma);
+ return hcd_buffer_alloc (dev->bus, size, mem_flags, dma);
}
/**
@@ -760,9 +754,11 @@ void usb_buffer_free (
dma_addr_t dma
)
{
- if (!dev || !dev->bus || !dev->bus->op || !dev->bus->op->buffer_free)
- return;
- dev->bus->op->buffer_free (dev->bus, size, addr, dma);
+ if (!dev || !dev->bus)
+ return;
+ if (!addr)
+ return;
+ hcd_buffer_free (dev->bus, size, addr, dma);
}
/**
@@ -911,8 +907,8 @@ void usb_buffer_unmap (struct urb *urb)
*
* Reverse the effect of this call with usb_buffer_unmap_sg().
*/
-int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int nents)
+int usb_buffer_map_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int nents)
{
struct usb_bus *bus;
struct device *controller;
@@ -946,8 +942,8 @@ int usb_buffer_map_sg (struct usb_device *dev, unsigned pipe,
* Use this when you are re-using a scatterlist's data buffers for
* another USB request.
*/
-void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_dmasync_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
@@ -972,8 +968,8 @@ void usb_buffer_dmasync_sg (struct usb_device *dev, unsigned pipe,
*
* Reverses the effect of usb_buffer_map_sg().
*/
-void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
- struct scatterlist *sg, int n_hw_ents)
+void usb_buffer_unmap_sg(const struct usb_device *dev, unsigned pipe,
+ struct scatterlist *sg, int n_hw_ents)
{
struct usb_bus *bus;
struct device *controller;
@@ -988,116 +984,6 @@ void usb_buffer_unmap_sg (struct usb_device *dev, unsigned pipe,
usb_pipein (pipe) ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
}
-static int verify_suspended(struct device *dev, void *unused)
-{
- if (dev->driver == NULL)
- return 0;
- return (dev->power.power_state.event == PM_EVENT_ON) ? -EBUSY : 0;
-}
-
-static int usb_generic_suspend(struct device *dev, pm_message_t message)
-{
- struct usb_interface *intf;
- struct usb_driver *driver;
- int status;
-
- /* USB devices enter SUSPEND state through their hubs, but can be
- * marked for FREEZE as soon as their children are already idled.
- * But those semantics are useless, so we equate the two (sigh).
- */
- if (dev->driver == &usb_generic_driver) {
- if (dev->power.power_state.event == message.event)
- return 0;
- /* we need to rule out bogus requests through sysfs */
- status = device_for_each_child(dev, NULL, verify_suspended);
- if (status)
- return status;
- return usb_suspend_device (to_usb_device(dev));
- }
-
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data))
- return 0;
-
- intf = to_usb_interface(dev);
- driver = to_usb_driver(dev->driver);
-
- /* with no hardware, USB interfaces only use FREEZE and ON states */
- if (!is_active(intf))
- return 0;
-
- if (driver->suspend && driver->resume) {
- status = driver->suspend(intf, message);
- if (status)
- dev_err(dev, "%s error %d\n", "suspend", status);
- else
- mark_quiesced(intf);
- } else {
- // FIXME else if there's no suspend method, disconnect...
- dev_warn(dev, "no suspend for driver %s?\n", driver->name);
- mark_quiesced(intf);
- status = 0;
- }
- return status;
-}
-
-static int usb_generic_resume(struct device *dev)
-{
- struct usb_interface *intf;
- struct usb_driver *driver;
- struct usb_device *udev;
- int status;
-
- if (dev->power.power_state.event == PM_EVENT_ON)
- return 0;
-
- /* mark things as "on" immediately, no matter what errors crop up */
- dev->power.power_state.event = PM_EVENT_ON;
-
- /* devices resume through their hubs */
- if (dev->driver == &usb_generic_driver) {
- udev = to_usb_device(dev);
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
- return usb_resume_device (to_usb_device(dev));
- }
-
- if ((dev->driver == NULL) ||
- (dev->driver_data == &usb_generic_driver_data)) {
- dev->power.power_state.event = PM_EVENT_FREEZE;
- return 0;
- }
-
- intf = to_usb_interface(dev);
- driver = to_usb_driver(dev->driver);
-
- udev = interface_to_usbdev(intf);
- if (udev->state == USB_STATE_NOTATTACHED)
- return 0;
-
- /* if driver was suspended, it has a resume method;
- * however, sysfs can wrongly mark things as suspended
- * (on the "no suspend method" FIXME path above)
- */
- if (driver->resume) {
- status = driver->resume(intf);
- if (status) {
- dev_err(dev, "%s error %d\n", "resume", status);
- mark_quiesced(intf);
- }
- } else
- dev_warn(dev, "no resume for driver %s?\n", driver->name);
- return 0;
-}
-
-struct bus_type usb_bus_type = {
- .name = "usb",
- .match = usb_device_match,
- .uevent = usb_uevent,
- .suspend = usb_generic_suspend,
- .resume = usb_generic_resume,
-};
-
/* format to disable USB on kernel command line is: nousb */
__module_param_call("", nousb, param_set_bool, param_get_bool, &nousb, 0444);
@@ -1120,9 +1006,12 @@ static int __init usb_init(void)
return 0;
}
+ retval = ksuspend_usb_init();
+ if (retval)
+ goto out;
retval = bus_register(&usb_bus_type);
if (retval)
- goto out;
+ goto bus_register_failed;
retval = usb_host_init();
if (retval)
goto host_init_failed;
@@ -1141,7 +1030,7 @@ static int __init usb_init(void)
retval = usb_hub_init();
if (retval)
goto hub_init_failed;
- retval = driver_register(&usb_generic_driver);
+ retval = usb_register_device_driver(&usb_generic_driver, THIS_MODULE);
if (!retval)
goto out;
@@ -1158,6 +1047,8 @@ major_init_failed:
usb_host_cleanup();
host_init_failed:
bus_unregister(&usb_bus_type);
+bus_register_failed:
+ ksuspend_usb_cleanup();
out:
return retval;
}
@@ -1171,7 +1062,7 @@ static void __exit usb_exit(void)
if (nousb)
return;
- driver_unregister(&usb_generic_driver);
+ usb_deregister_device_driver(&usb_generic_driver);
usb_major_cleanup();
usbfs_cleanup();
usb_deregister(&usbfs_driver);
@@ -1179,6 +1070,7 @@ static void __exit usb_exit(void)
usb_hub_cleanup();
usb_host_cleanup();
bus_unregister(&usb_bus_type);
+ ksuspend_usb_cleanup();
}
subsys_initcall(usb_init);
@@ -1201,20 +1093,27 @@ EXPORT_SYMBOL(usb_hub_tt_clear_buffer);
EXPORT_SYMBOL(usb_lock_device_for_reset);
-EXPORT_SYMBOL(usb_driver_claim_interface);
-EXPORT_SYMBOL(usb_driver_release_interface);
EXPORT_SYMBOL(usb_find_interface);
EXPORT_SYMBOL(usb_ifnum_to_if);
EXPORT_SYMBOL(usb_altnum_to_altsetting);
-EXPORT_SYMBOL(usb_reset_device);
-EXPORT_SYMBOL(usb_reset_composite_device);
-
EXPORT_SYMBOL(__usb_get_extra_descriptor);
EXPORT_SYMBOL(usb_find_device);
EXPORT_SYMBOL(usb_get_current_frame_number);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_dir_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_bulk);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_int);
+EXPORT_SYMBOL_GPL(usb_endpoint_xfer_isoc);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_bulk_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_int_out);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_in);
+EXPORT_SYMBOL_GPL(usb_endpoint_is_isoc_out);
+
EXPORT_SYMBOL (usb_buffer_alloc);
EXPORT_SYMBOL (usb_buffer_free);
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h
index 49f69236b42..13322e33f91 100644
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -1,10 +1,10 @@
/* Functions local to drivers/usb/core/ */
-extern void usb_create_sysfs_dev_files (struct usb_device *dev);
+extern int usb_create_sysfs_dev_files (struct usb_device *dev);
extern void usb_remove_sysfs_dev_files (struct usb_device *dev);
-extern void usb_create_sysfs_intf_files (struct usb_interface *intf);
+extern int usb_create_sysfs_intf_files (struct usb_interface *intf);
extern void usb_remove_sysfs_intf_files (struct usb_interface *intf);
-extern void usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
+extern int usb_create_ep_files(struct device *parent, struct usb_host_endpoint *endpoint,
struct usb_device *udev);
extern void usb_remove_ep_files(struct usb_host_endpoint *endpoint);
@@ -20,7 +20,6 @@ extern char *usb_cache_string(struct usb_device *udev, int index);
extern int usb_set_configuration(struct usb_device *dev, int configuration);
extern void usb_kick_khubd(struct usb_device *dev);
-extern void usb_suspend_root_hub(struct usb_device *hdev);
extern void usb_resume_root_hub(struct usb_device *dev);
extern int usb_hub_init(void);
@@ -30,28 +29,91 @@ extern void usb_major_cleanup(void);
extern int usb_host_init(void);
extern void usb_host_cleanup(void);
-extern int usb_suspend_device(struct usb_device *dev);
-extern int usb_resume_device(struct usb_device *dev);
+#ifdef CONFIG_PM
-extern struct device_driver usb_generic_driver;
-extern int usb_generic_driver_data;
-extern int usb_device_match(struct device *dev, struct device_driver *drv);
+extern int usb_suspend_both(struct usb_device *udev, pm_message_t msg);
+extern int usb_resume_both(struct usb_device *udev);
+extern int usb_port_suspend(struct usb_device *dev);
+extern int usb_port_resume(struct usb_device *dev);
+
+static inline void usb_pm_lock(struct usb_device *udev)
+{
+ mutex_lock_nested(&udev->pm_mutex, udev->level);
+}
+
+static inline void usb_pm_unlock(struct usb_device *udev)
+{
+ mutex_unlock(&udev->pm_mutex);
+}
+
+#else
+
+#define usb_suspend_both(udev, msg) 0
+static inline int usb_resume_both(struct usb_device *udev)
+{
+ return 0;
+}
+#define usb_port_suspend(dev) 0
+#define usb_port_resume(dev) 0
+static inline void usb_pm_lock(struct usb_device *udev) {}
+static inline void usb_pm_unlock(struct usb_device *udev) {}
+
+#endif
+
+#ifdef CONFIG_USB_SUSPEND
+
+#define USB_AUTOSUSPEND_DELAY (HZ*2)
+
+extern void usb_autosuspend_device(struct usb_device *udev, int dec_busy_cnt);
+extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt);
+
+#else
+
+#define usb_autosuspend_device(udev, dec_busy_cnt) do {} while (0)
+static inline int usb_autoresume_device(struct usb_device *udev,
+ int inc_busy_cnt)
+{
+ return 0;
+}
+
+#endif
+
+extern struct workqueue_struct *ksuspend_usb_wq;
+extern struct bus_type usb_bus_type;
+extern struct usb_device_driver usb_generic_driver;
+
+/* Here's how we tell apart devices and interfaces. Luckily there's
+ * no such thing as a platform USB device, so we can steal the use
+ * of the platform_data field. */
+
+static inline int is_usb_device(const struct device *dev)
+{
+ return dev->platform_data == &usb_generic_driver;
+}
+
+/* Do the same for device drivers and interface drivers. */
+
+static inline int is_usb_device_driver(struct device_driver *drv)
+{
+ return container_of(drv, struct usbdrv_wrap, driver)->
+ for_devices;
+}
/* Interfaces and their "power state" are owned by usbcore */
static inline void mark_active(struct usb_interface *f)
{
- f->dev.power.power_state.event = PM_EVENT_ON;
+ f->is_active = 1;
}
static inline void mark_quiesced(struct usb_interface *f)
{
- f->dev.power.power_state.event = PM_EVENT_FREEZE;
+ f->is_active = 0;
}
-static inline int is_active(struct usb_interface *f)
+static inline int is_active(const struct usb_interface *f)
{
- return f->dev.power.power_state.event == PM_EVENT_ON;
+ return f->is_active;
}
@@ -59,9 +121,10 @@ static inline int is_active(struct usb_interface *f)
extern const char *usbcore_name;
/* usbfs stuff */
+extern struct mutex usbfs_mutex;
extern struct usb_driver usbfs_driver;
-extern struct file_operations usbfs_devices_fops;
-extern struct file_operations usbfs_device_file_operations;
+extern const struct file_operations usbfs_devices_fops;
+extern const struct file_operations usbfs_device_file_operations;
extern void usbfs_conn_disc_event(void);
extern int usbdev_init(void);
@@ -76,7 +139,7 @@ struct dev_state {
struct list_head async_completed;
wait_queue_head_t wait; /* wake up if a request completed */
unsigned int discsignr;
- pid_t disc_pid;
+ struct pid *disc_pid;
uid_t disc_uid, disc_euid;
void __user *disccontext;
unsigned long ifclaimed;
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 1a32d96774b..8e5dd6f29d0 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -26,7 +26,7 @@ config USB_GADGET
you need a low level bus controller driver, and some software
talking to it. Peripheral controllers are often discrete silicon,
or are integrated with the CPU in a microcontroller. The more
- familiar host side controllers have names like like "EHCI", "OHCI",
+ familiar host side controllers have names like "EHCI", "OHCI",
or "UHCI", and are usually integrated into southbridges on PC
motherboards.
@@ -404,6 +404,20 @@ config USB_G_SERIAL
which includes instructions and a "driver info file" needed to
make MS-Windows work with this driver.
+config USB_MIDI_GADGET
+ tristate "MIDI Gadget (EXPERIMENTAL)"
+ depends on SND && EXPERIMENTAL
+ select SND_RAWMIDI
+ help
+ The MIDI Gadget acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_midi".
+
# put drivers that need isochronous transfer support (for audio
# or video class gadget drivers), or specific hardware, here.
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5a28e61392e..e71e086a1cf 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_USB_AT91) += at91_udc.o
g_zero-objs := zero.o usbstring.o config.o epautoconf.o
g_ether-objs := ether.o usbstring.o config.o epautoconf.o
g_serial-objs := serial.o usbstring.o config.o epautoconf.o
+g_midi-objs := gmidi.o usbstring.o config.o epautoconf.o
gadgetfs-objs := inode.o
g_file_storage-objs := file_storage.o usbstring.o config.o \
epautoconf.o
@@ -28,4 +29,5 @@ obj-$(CONFIG_USB_ETH) += g_ether.o
obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o
obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index cfebca05ead..77beba485a8 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -247,7 +247,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
return single_open(file, proc_udc_show, PDE(inode)->data);
}
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
.open = proc_udc_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -1658,7 +1658,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (!request_mem_region(AT91_BASE_UDP, SZ_16K, driver_name)) {
+ if (!request_mem_region(AT91RM9200_BASE_UDP, SZ_16K, driver_name)) {
DBG("someone's using UDC memory\n");
return -EBUSY;
}
@@ -1720,7 +1720,7 @@ static int __devinit at91udc_probe(struct platform_device *pdev)
fail1:
device_unregister(&udc->gadget.dev);
fail0:
- release_mem_region(AT91_BASE_UDP, SZ_16K);
+ release_mem_region(AT91RM9200_BASE_UDP, SZ_16K);
DBG("%s probe failed, %d\n", driver_name, retval);
return retval;
}
@@ -1742,7 +1742,7 @@ static int __devexit at91udc_remove(struct platform_device *pdev)
free_irq(udc->board.vbus_pin, udc);
free_irq(udc->udp_irq, udc);
device_unregister(&udc->gadget.dev);
- release_mem_region(AT91_BASE_UDP, SZ_16K);
+ release_mem_region(AT91RM9200_BASE_UDP, SZ_16K);
clk_put(udc->iclk);
clk_put(udc->fclk);
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 7d1c22c3495..4d2946e540c 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -816,15 +816,14 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
dum->gadget.dev.driver = &driver->driver;
dev_dbg (udc_dev(dum), "binding gadget driver '%s'\n",
driver->driver.name);
- if ((retval = driver->bind (&dum->gadget)) != 0) {
- dum->driver = NULL;
- dum->gadget.dev.driver = NULL;
- return retval;
- }
+ if ((retval = driver->bind (&dum->gadget)) != 0)
+ goto err_bind_gadget;
driver->driver.bus = dum->gadget.dev.parent->bus;
- driver_register (&driver->driver);
- device_bind_driver (&dum->gadget.dev);
+ if ((retval = driver_register (&driver->driver)) != 0)
+ goto err_register;
+ if ((retval = device_bind_driver (&dum->gadget.dev)) != 0)
+ goto err_bind_driver;
/* khubd will enumerate this in a while */
spin_lock_irq (&dum->lock);
@@ -834,6 +833,19 @@ usb_gadget_register_driver (struct usb_gadget_driver *driver)
usb_hcd_poll_rh_status (dummy_to_hcd (dum));
return 0;
+
+err_bind_driver:
+ driver_unregister (&driver->driver);
+err_register:
+ driver->unbind (&dum->gadget);
+ spin_lock_irq (&dum->lock);
+ dum->pullup = 0;
+ set_link_state (dum);
+ spin_unlock_irq (&dum->lock);
+err_bind_gadget:
+ dum->driver = NULL;
+ dum->gadget.dev.driver = NULL;
+ return retval;
}
EXPORT_SYMBOL (usb_gadget_register_driver);
@@ -889,11 +901,9 @@ EXPORT_SYMBOL (net2280_set_fifo_mode);
static void
dummy_gadget_release (struct device *dev)
{
-#if 0 /* usb_bus_put isn't EXPORTed! */
struct dummy *dum = gadget_dev_to_dummy (dev);
- usb_bus_put (&dummy_to_hcd (dum)->self);
-#endif
+ usb_put_hcd (dummy_to_hcd (dum));
}
static int dummy_udc_probe (struct platform_device *pdev)
@@ -915,12 +925,12 @@ static int dummy_udc_probe (struct platform_device *pdev)
if (rc < 0)
return rc;
-#if 0 /* usb_bus_get isn't EXPORTed! */
- usb_bus_get (&dummy_to_hcd (dum)->self);
-#endif
+ usb_get_hcd (dummy_to_hcd (dum));
platform_set_drvdata (pdev, dum);
- device_create_file (&dum->gadget.dev, &dev_attr_function);
+ rc = device_create_file (&dum->gadget.dev, &dev_attr_function);
+ if (rc < 0)
+ device_unregister (&dum->gadget.dev);
return rc;
}
@@ -1868,8 +1878,7 @@ static int dummy_start (struct usb_hcd *hcd)
#endif
/* FIXME 'urbs' should be a per-device thing, maybe in usbcore */
- device_create_file (dummy_dev(dum), &dev_attr_urbs);
- return 0;
+ return device_create_file (dummy_dev(dum), &dev_attr_urbs);
}
static void dummy_stop (struct usb_hcd *hcd)
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 30299c620d9..1c17d26d03b 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -262,7 +262,7 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
#define DEV_CONFIG_CDC
#endif
-#ifdef CONFIG_USB_GADGET_MUSBHDRC
+#ifdef CONFIG_USB_GADGET_MUSB_HDRC
#define DEV_CONFIG_CDC
#endif
@@ -2014,7 +2014,7 @@ rndis_control_ack_complete (struct usb_ep *ep, struct usb_request *req)
static int rndis_control_ack (struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- u32 length;
+ int length;
struct usb_request *resp = dev->stat_req;
/* in case RNDIS calls this after disconnect */
@@ -2230,6 +2230,9 @@ eth_bind (struct usb_gadget *gadget)
if (gadget_is_pxa (gadget)) {
/* pxa doesn't support altsettings */
cdc = 0;
+ } else if (gadget_is_musbhdrc(gadget)) {
+ /* reduce tx dma overhead by avoiding special cases */
+ zlp = 0;
} else if (gadget_is_sh(gadget)) {
/* sh doesn't support multiple interfaces or configs */
cdc = 0;
@@ -2257,7 +2260,7 @@ eth_bind (struct usb_gadget *gadget)
return -ENODEV;
}
snprintf (manufacturer, sizeof manufacturer, "%s %s/%s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
/* If there's an RNDIS configuration, that's what Windows wants to
@@ -2564,7 +2567,7 @@ static struct usb_gadget_driver eth_driver = {
.function = (char *) driver_desc,
.bind = eth_bind,
- .unbind = __exit_p(eth_unbind),
+ .unbind = eth_unbind,
.setup = eth_setup,
.disconnect = eth_disconnect,
diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c
index 8d7f1e84cd7..8b975d15538 100644
--- a/drivers/usb/gadget/file_storage.c
+++ b/drivers/usb/gadget/file_storage.c
@@ -567,6 +567,7 @@ struct lun {
unsigned int ro : 1;
unsigned int prevent_medium_removal : 1;
unsigned int registered : 1;
+ unsigned int info_valid : 1;
u32 sense_data;
u32 sense_data_info;
@@ -1656,6 +1657,7 @@ static int do_read(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
bh->inreq->length = 0;
bh->state = BUF_STATE_FULL;
break;
@@ -1691,6 +1693,7 @@ static int do_read(struct fsg_dev *fsg)
if (nread < amount) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -1785,6 +1788,7 @@ static int do_write(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = usb_offset >> 9;
+ curlun->info_valid = 1;
continue;
}
amount -= (amount & 511);
@@ -1827,6 +1831,7 @@ static int do_write(struct fsg_dev *fsg)
if (bh->outreq->status != 0) {
curlun->sense_data = SS_COMMUNICATION_FAILURE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -1868,6 +1873,7 @@ static int do_write(struct fsg_dev *fsg)
if (nwritten < amount) {
curlun->sense_data = SS_WRITE_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -2010,6 +2016,7 @@ static int do_verify(struct fsg_dev *fsg)
curlun->sense_data =
SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
@@ -2036,6 +2043,7 @@ static int do_verify(struct fsg_dev *fsg)
if (nread == 0) {
curlun->sense_data = SS_UNRECOVERED_READ_ERROR;
curlun->sense_data_info = file_offset >> 9;
+ curlun->info_valid = 1;
break;
}
file_offset += nread;
@@ -2079,6 +2087,7 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
struct lun *curlun = fsg->curlun;
u8 *buf = (u8 *) bh->buf;
u32 sd, sdinfo;
+ int valid;
/*
* From the SCSI-2 spec., section 7.9 (Unit attention condition):
@@ -2106,15 +2115,18 @@ static int do_request_sense(struct fsg_dev *fsg, struct fsg_buffhd *bh)
fsg->bad_lun_okay = 1;
sd = SS_LOGICAL_UNIT_NOT_SUPPORTED;
sdinfo = 0;
+ valid = 0;
} else {
sd = curlun->sense_data;
sdinfo = curlun->sense_data_info;
+ valid = curlun->info_valid << 7;
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
memset(buf, 0, 18);
- buf[0] = 0x80 | 0x70; // Valid, current error
+ buf[0] = valid | 0x70; // Valid, current error
buf[2] = SK(sd);
put_be32(&buf[3], sdinfo); // Sense information
buf[7] = 18 - 8; // Additional sense length
@@ -2703,6 +2715,7 @@ static int check_command(struct fsg_dev *fsg, int cmnd_size,
if (fsg->cmnd[0] != SC_REQUEST_SENSE) {
curlun->sense_data = SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
} else {
fsg->curlun = curlun = NULL;
@@ -3332,6 +3345,7 @@ static void handle_exception(struct fsg_dev *fsg)
curlun->sense_data = curlun->unit_attention_data =
SS_NO_SENSE;
curlun->sense_data_info = 0;
+ curlun->info_valid = 0;
}
fsg->state = FSG_STATE_IDLE;
}
@@ -3873,21 +3887,26 @@ static int __init fsg_bind(struct usb_gadget *gadget)
for (i = 0; i < fsg->nluns; ++i) {
curlun = &fsg->luns[i];
curlun->ro = mod_data.ro[i];
+ curlun->dev.release = lun_release;
curlun->dev.parent = &gadget->dev;
curlun->dev.driver = &fsg_driver.driver;
dev_set_drvdata(&curlun->dev, fsg);
snprintf(curlun->dev.bus_id, BUS_ID_SIZE,
"%s-lun%d", gadget->dev.bus_id, i);
- if ((rc = device_register(&curlun->dev)) != 0)
+ if ((rc = device_register(&curlun->dev)) != 0) {
INFO(fsg, "failed to register LUN%d: %d\n", i, rc);
- else {
- curlun->registered = 1;
- curlun->dev.release = lun_release;
- device_create_file(&curlun->dev, &dev_attr_ro);
- device_create_file(&curlun->dev, &dev_attr_file);
- kref_get(&fsg->ref);
+ goto out;
+ }
+ if ((rc = device_create_file(&curlun->dev,
+ &dev_attr_ro)) != 0 ||
+ (rc = device_create_file(&curlun->dev,
+ &dev_attr_file)) != 0) {
+ device_unregister(&curlun->dev);
+ goto out;
}
+ curlun->registered = 1;
+ kref_get(&fsg->ref);
if (mod_data.file[i] && *mod_data.file[i]) {
if ((rc = open_backing_file(curlun,
@@ -3982,7 +4001,7 @@ static int __init fsg_bind(struct usb_gadget *gadget)
usb_gadget_set_selfpowered(gadget);
snprintf(manufacturer, sizeof manufacturer, "%s %s with %s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
/* On a real device, serial[] would be loaded from permanent
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/gmidi.c
new file mode 100644
index 00000000000..83601d4009e
--- /dev/null
+++ b/drivers/usb/gadget/gmidi.c
@@ -0,0 +1,1337 @@
+/*
+ * gmidi.c -- USB MIDI Gadget Driver
+ *
+ * Copyright (C) 2006 Thumtronics Pty Ltd.
+ * Developed for Thumtronics by Grey Innovation
+ * Ben Williamson <ben.williamson@greyinnovation.com>
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * License ("GPL") version 2, as published by the Free Software Foundation.
+ *
+ * This code is based in part on:
+ *
+ * Gadget Zero driver, Copyright (C) 2003-2004 David Brownell.
+ * USB Audio driver, Copyright (C) 2002 by Takashi Iwai.
+ * USB MIDI driver, Copyright (C) 2002-2005 Clemens Ladisch.
+ *
+ * Refer to the USB Device Class Definition for MIDI Devices:
+ * http://www.usb.org/developers/devclass_docs/midi10.pdf
+ */
+
+#define DEBUG 1
+// #define VERBOSE
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/utsname.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+
+#include <sound/driver.h>
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/rawmidi.h>
+
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi.h>
+
+#include "gadget_chips.h"
+
+MODULE_AUTHOR("Ben Williamson");
+MODULE_LICENSE("GPL v2");
+
+#define DRIVER_VERSION "25 Jul 2006"
+
+static const char shortname[] = "g_midi";
+static const char longname[] = "MIDI Gadget";
+
+static int index = SNDRV_DEFAULT_IDX1;
+static char *id = SNDRV_DEFAULT_STR1;
+
+module_param(index, int, 0444);
+MODULE_PARM_DESC(index, "Index value for the USB MIDI Gadget adapter.");
+module_param(id, charp, 0444);
+MODULE_PARM_DESC(id, "ID string for the USB MIDI Gadget adapter.");
+
+/* Some systems will want different product identifers published in the
+ * device descriptor, either numbers or strings or both. These string
+ * parameters are in UTF-8 (superset of ASCII's 7 bit characters).
+ */
+
+static ushort idVendor;
+module_param(idVendor, ushort, S_IRUGO);
+MODULE_PARM_DESC(idVendor, "USB Vendor ID");
+
+static ushort idProduct;
+module_param(idProduct, ushort, S_IRUGO);
+MODULE_PARM_DESC(idProduct, "USB Product ID");
+
+static ushort bcdDevice;
+module_param(bcdDevice, ushort, S_IRUGO);
+MODULE_PARM_DESC(bcdDevice, "USB Device version (BCD)");
+
+static char *iManufacturer;
+module_param(iManufacturer, charp, S_IRUGO);
+MODULE_PARM_DESC(iManufacturer, "USB Manufacturer string");
+
+static char *iProduct;
+module_param(iProduct, charp, S_IRUGO);
+MODULE_PARM_DESC(iProduct, "USB Product string");
+
+static char *iSerialNumber;
+module_param(iSerialNumber, charp, S_IRUGO);
+MODULE_PARM_DESC(iSerialNumber, "SerialNumber");
+
+/*
+ * this version autoconfigures as much as possible,
+ * which is reasonable for most "bulk-only" drivers.
+ */
+static const char *EP_IN_NAME;
+static const char *EP_OUT_NAME;
+
+
+/* big enough to hold our biggest descriptor */
+#define USB_BUFSIZ 256
+
+
+/* This is a gadget, and the IN/OUT naming is from the host's perspective.
+ USB -> OUT endpoint -> rawmidi
+ USB <- IN endpoint <- rawmidi */
+struct gmidi_in_port {
+ struct gmidi_device* dev;
+ int active;
+ uint8_t cable; /* cable number << 4 */
+ uint8_t state;
+#define STATE_UNKNOWN 0
+#define STATE_1PARAM 1
+#define STATE_2PARAM_1 2
+#define STATE_2PARAM_2 3
+#define STATE_SYSEX_0 4
+#define STATE_SYSEX_1 5
+#define STATE_SYSEX_2 6
+ uint8_t data[2];
+};
+
+struct gmidi_device {
+ spinlock_t lock;
+ struct usb_gadget *gadget;
+ struct usb_request *req; /* for control responses */
+ u8 config;
+ struct usb_ep *in_ep, *out_ep;
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_substream *in_substream;
+ struct snd_rawmidi_substream *out_substream;
+
+ /* For the moment we only support one port in
+ each direction, but in_port is kept as a
+ separate struct so we can have more later. */
+ struct gmidi_in_port in_port;
+ unsigned long out_triggered;
+ struct tasklet_struct tasklet;
+};
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req);
+
+
+#define xprintk(d,level,fmt,args...) \
+ dev_printk(level , &(d)->gadget->dev , fmt , ## args)
+
+#ifdef DEBUG
+#define DBG(dev,fmt,args...) \
+ xprintk(dev , KERN_DEBUG , fmt , ## args)
+#else
+#define DBG(dev,fmt,args...) \
+ do { } while (0)
+#endif /* DEBUG */
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(dev,fmt,args...) \
+ do { } while (0)
+#endif /* VERBOSE */
+
+#define ERROR(dev,fmt,args...) \
+ xprintk(dev , KERN_ERR , fmt , ## args)
+#define WARN(dev,fmt,args...) \
+ xprintk(dev , KERN_WARNING , fmt , ## args)
+#define INFO(dev,fmt,args...) \
+ xprintk(dev , KERN_INFO , fmt , ## args)
+
+
+static unsigned buflen = 256;
+static unsigned qlen = 32;
+
+module_param(buflen, uint, S_IRUGO);
+module_param(qlen, uint, S_IRUGO);
+
+
+/* Thanks to Grey Innovation for donating this product ID.
+ *
+ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!!
+ * Instead: allocate your own, using normal USB-IF procedures.
+ */
+#define DRIVER_VENDOR_NUM 0x17b3 /* Grey Innovation */
+#define DRIVER_PRODUCT_NUM 0x0004 /* Linux-USB "MIDI Gadget" */
+
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full)
+ * configuration descriptors are built on demand.
+ */
+
+#define STRING_MANUFACTURER 25
+#define STRING_PRODUCT 42
+#define STRING_SERIAL 101
+#define STRING_MIDI_GADGET 250
+
+/* We only have the one configuration, it's number 1. */
+#define GMIDI_CONFIG 1
+
+/* We have two interfaces- AudioControl and MIDIStreaming */
+#define GMIDI_AC_INTERFACE 0
+#define GMIDI_MS_INTERFACE 1
+#define GMIDI_NUM_INTERFACES 2
+
+DECLARE_USB_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(1);
+
+/* B.1 Device Descriptor */
+static struct usb_device_descriptor device_desc = {
+ .bLength = USB_DT_DEVICE_SIZE,
+ .bDescriptorType = USB_DT_DEVICE,
+ .bcdUSB = __constant_cpu_to_le16(0x0200),
+ .bDeviceClass = USB_CLASS_PER_INTERFACE,
+ .idVendor = __constant_cpu_to_le16(DRIVER_VENDOR_NUM),
+ .idProduct = __constant_cpu_to_le16(DRIVER_PRODUCT_NUM),
+ .iManufacturer = STRING_MANUFACTURER,
+ .iProduct = STRING_PRODUCT,
+ .bNumConfigurations = 1,
+};
+
+/* B.2 Configuration Descriptor */
+static struct usb_config_descriptor config_desc = {
+ .bLength = USB_DT_CONFIG_SIZE,
+ .bDescriptorType = USB_DT_CONFIG,
+ /* compute wTotalLength on the fly */
+ .bNumInterfaces = GMIDI_NUM_INTERFACES,
+ .bConfigurationValue = GMIDI_CONFIG,
+ .iConfiguration = STRING_MIDI_GADGET,
+ /*
+ * FIXME: When embedding this driver in a device,
+ * these need to be set to reflect the actual
+ * power properties of the device. Is it selfpowered?
+ */
+ .bmAttributes = USB_CONFIG_ATT_ONE,
+ .bMaxPower = 1,
+};
+
+/* B.3.1 Standard AC Interface Descriptor */
+static const struct usb_interface_descriptor ac_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = GMIDI_AC_INTERFACE,
+ .bNumEndpoints = 0,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL,
+ .iInterface = STRING_MIDI_GADGET,
+};
+
+/* B.3.2 Class-Specific AC Interface Descriptor */
+static const struct usb_ac_header_descriptor_1 ac_header_desc = {
+ .bLength = USB_DT_AC_HEADER_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdADC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = USB_DT_AC_HEADER_SIZE(1),
+ .bInCollection = 1,
+ .baInterfaceNr = {
+ [0] = GMIDI_MS_INTERFACE,
+ }
+};
+
+/* B.4.1 Standard MS Interface Descriptor */
+static const struct usb_interface_descriptor ms_interface_desc = {
+ .bLength = USB_DT_INTERFACE_SIZE,
+ .bDescriptorType = USB_DT_INTERFACE,
+ .bInterfaceNumber = GMIDI_MS_INTERFACE,
+ .bNumEndpoints = 2,
+ .bInterfaceClass = USB_CLASS_AUDIO,
+ .bInterfaceSubClass = USB_SUBCLASS_MIDISTREAMING,
+ .iInterface = STRING_MIDI_GADGET,
+};
+
+/* B.4.2 Class-Specific MS Interface Descriptor */
+static const struct usb_ms_header_descriptor ms_header_desc = {
+ .bLength = USB_DT_MS_HEADER_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_HEADER,
+ .bcdMSC = __constant_cpu_to_le16(0x0100),
+ .wTotalLength = USB_DT_MS_HEADER_SIZE
+ + 2*USB_DT_MIDI_IN_SIZE
+ + 2*USB_DT_MIDI_OUT_SIZE(1),
+};
+
+#define JACK_IN_EMB 1
+#define JACK_IN_EXT 2
+#define JACK_OUT_EMB 3
+#define JACK_OUT_EXT 4
+
+/* B.4.3 MIDI IN Jack Descriptors */
+static const struct usb_midi_in_jack_descriptor jack_in_emb_desc = {
+ .bLength = USB_DT_MIDI_IN_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = JACK_IN_EMB,
+};
+
+static const struct usb_midi_in_jack_descriptor jack_in_ext_desc = {
+ .bLength = USB_DT_MIDI_IN_SIZE,
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_IN_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = JACK_IN_EXT,
+};
+
+/* B.4.4 MIDI OUT Jack Descriptors */
+static const struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc = {
+ .bLength = USB_DT_MIDI_OUT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EMBEDDED,
+ .bJackID = JACK_OUT_EMB,
+ .bNrInputPins = 1,
+ .pins = {
+ [0] = {
+ .baSourceID = JACK_IN_EXT,
+ .baSourcePin = 1,
+ }
+ }
+};
+
+static const struct usb_midi_out_jack_descriptor_1 jack_out_ext_desc = {
+ .bLength = USB_DT_MIDI_OUT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_INTERFACE,
+ .bDescriptorSubtype = USB_MS_MIDI_OUT_JACK,
+ .bJackType = USB_MS_EXTERNAL,
+ .bJackID = JACK_OUT_EXT,
+ .bNrInputPins = 1,
+ .pins = {
+ [0] = {
+ .baSourceID = JACK_IN_EMB,
+ .baSourcePin = 1,
+ }
+ }
+};
+
+/* B.5.1 Standard Bulk OUT Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_out_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_OUT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.5.2 Class-specific MS Bulk OUT Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_out_desc = {
+ .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = {
+ [0] = JACK_IN_EMB,
+ }
+};
+
+/* B.6.1 Standard Bulk IN Endpoint Descriptor */
+static struct usb_endpoint_descriptor bulk_in_desc = {
+ .bLength = USB_DT_ENDPOINT_AUDIO_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bEndpointAddress = USB_DIR_IN,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+};
+
+/* B.6.2 Class-specific MS Bulk IN Endpoint Descriptor */
+static const struct usb_ms_endpoint_descriptor_1 ms_in_desc = {
+ .bLength = USB_DT_MS_ENDPOINT_SIZE(1),
+ .bDescriptorType = USB_DT_CS_ENDPOINT,
+ .bDescriptorSubtype = USB_MS_GENERAL,
+ .bNumEmbMIDIJack = 1,
+ .baAssocJackID = {
+ [0] = JACK_OUT_EMB,
+ }
+};
+
+static const struct usb_descriptor_header *gmidi_function [] = {
+ (struct usb_descriptor_header *)&ac_interface_desc,
+ (struct usb_descriptor_header *)&ac_header_desc,
+ (struct usb_descriptor_header *)&ms_interface_desc,
+
+ (struct usb_descriptor_header *)&ms_header_desc,
+ (struct usb_descriptor_header *)&jack_in_emb_desc,
+ (struct usb_descriptor_header *)&jack_in_ext_desc,
+ (struct usb_descriptor_header *)&jack_out_emb_desc,
+ (struct usb_descriptor_header *)&jack_out_ext_desc,
+ /* If you add more jacks, update ms_header_desc.wTotalLength */
+
+ (struct usb_descriptor_header *)&bulk_out_desc,
+ (struct usb_descriptor_header *)&ms_out_desc,
+ (struct usb_descriptor_header *)&bulk_in_desc,
+ (struct usb_descriptor_header *)&ms_in_desc,
+ NULL,
+};
+
+static char manufacturer[50];
+static char product_desc[40] = "MIDI Gadget";
+static char serial_number[20];
+
+/* static strings, in UTF-8 */
+static struct usb_string strings [] = {
+ { STRING_MANUFACTURER, manufacturer, },
+ { STRING_PRODUCT, product_desc, },
+ { STRING_SERIAL, serial_number, },
+ { STRING_MIDI_GADGET, longname, },
+ { } /* end of list */
+};
+
+static struct usb_gadget_strings stringtab = {
+ .language = 0x0409, /* en-us */
+ .strings = strings,
+};
+
+static int config_buf(struct usb_gadget *gadget,
+ u8 *buf, u8 type, unsigned index)
+{
+ int len;
+
+ /* only one configuration */
+ if (index != 0) {
+ return -EINVAL;
+ }
+ len = usb_gadget_config_buf(&config_desc,
+ buf, USB_BUFSIZ, gmidi_function);
+ if (len < 0) {
+ return len;
+ }
+ ((struct usb_config_descriptor *)buf)->bDescriptorType = type;
+ return len;
+}
+
+static struct usb_request* alloc_ep_req(struct usb_ep *ep, unsigned length)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (req) {
+ req->length = length;
+ req->buf = kmalloc(length, GFP_ATOMIC);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+
+static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
+{
+ kfree(req->buf);
+ usb_ep_free_request(ep, req);
+}
+
+static const uint8_t gmidi_cin_length[] = {
+ 0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+};
+
+/*
+ * Receives a chunk of MIDI data.
+ */
+static void gmidi_read_data(struct usb_ep *ep, int cable,
+ uint8_t* data, int length)
+{
+ struct gmidi_device *dev = ep->driver_data;
+ /* cable is ignored, because for now we only have one. */
+
+ if (!dev->out_substream) {
+ /* Nobody is listening - throw it on the floor. */
+ return;
+ }
+ if (!test_bit(dev->out_substream->number, &dev->out_triggered)) {
+ return;
+ }
+ snd_rawmidi_receive(dev->out_substream, data, length);
+}
+
+static void gmidi_handle_out_data(struct usb_ep *ep, struct usb_request *req)
+{
+ unsigned i;
+ u8 *buf = req->buf;
+
+ for (i = 0; i + 3 < req->actual; i += 4) {
+ if (buf[i] != 0) {
+ int cable = buf[i] >> 4;
+ int length = gmidi_cin_length[buf[i] & 0x0f];
+ gmidi_read_data(ep, cable, &buf[i + 1], length);
+ }
+ }
+}
+
+static void gmidi_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ struct gmidi_device *dev = ep->driver_data;
+ int status = req->status;
+
+ switch (status) {
+ case 0: /* normal completion */
+ if (ep == dev->out_ep) {
+ /* we received stuff.
+ req is queued again, below */
+ gmidi_handle_out_data(ep, req);
+ } else if (ep == dev->in_ep) {
+ /* our transmit completed.
+ see if there's more to go.
+ gmidi_transmit eats req, don't queue it again. */
+ gmidi_transmit(dev, req);
+ return;
+ }
+ break;
+
+ /* this endpoint is normally active while we're configured */
+ case -ECONNABORTED: /* hardware forced ep reset */
+ case -ECONNRESET: /* request dequeued */
+ case -ESHUTDOWN: /* disconnect from host */
+ VDBG(dev, "%s gone (%d), %d/%d\n", ep->name, status,
+ req->actual, req->length);
+ if (ep == dev->out_ep) {
+ gmidi_handle_out_data(ep, req);
+ }
+ free_ep_req(ep, req);
+ return;
+
+ case -EOVERFLOW: /* buffer overrun on read means that
+ * we didn't provide a big enough
+ * buffer.
+ */
+ default:
+ DBG(dev, "%s complete --> %d, %d/%d\n", ep->name,
+ status, req->actual, req->length);
+ break;
+ case -EREMOTEIO: /* short read */
+ break;
+ }
+
+ status = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (status) {
+ ERROR(dev, "kill %s: resubmit %d bytes --> %d\n",
+ ep->name, req->length, status);
+ usb_ep_set_halt(ep);
+ /* FIXME recover later ... somehow */
+ }
+}
+
+static int set_gmidi_config(struct gmidi_device *dev, gfp_t gfp_flags)
+{
+ int err = 0;
+ struct usb_request *req;
+ struct usb_ep* ep;
+ unsigned i;
+
+ err = usb_ep_enable(dev->in_ep, &bulk_in_desc);
+ if (err) {
+ ERROR(dev, "can't start %s: %d\n", dev->in_ep->name, err);
+ goto fail;
+ }
+ dev->in_ep->driver_data = dev;
+
+ err = usb_ep_enable(dev->out_ep, &bulk_out_desc);
+ if (err) {
+ ERROR(dev, "can't start %s: %d\n", dev->out_ep->name, err);
+ goto fail;
+ }
+ dev->out_ep->driver_data = dev;
+
+ /* allocate a bunch of read buffers and queue them all at once. */
+ ep = dev->out_ep;
+ for (i = 0; i < qlen && err == 0; i++) {
+ req = alloc_ep_req(ep, buflen);
+ if (req) {
+ req->complete = gmidi_complete;
+ err = usb_ep_queue(ep, req, GFP_ATOMIC);
+ if (err) {
+ DBG(dev, "%s queue req: %d\n", ep->name, err);
+ }
+ } else {
+ err = -ENOMEM;
+ }
+ }
+fail:
+ /* caller is responsible for cleanup on error */
+ return err;
+}
+
+
+static void gmidi_reset_config(struct gmidi_device *dev)
+{
+ if (dev->config == 0) {
+ return;
+ }
+
+ DBG(dev, "reset config\n");
+
+ /* just disable endpoints, forcing completion of pending i/o.
+ * all our completion handlers free their requests in this case.
+ */
+ usb_ep_disable(dev->in_ep);
+ usb_ep_disable(dev->out_ep);
+ dev->config = 0;
+}
+
+/* change our operational config. this code must agree with the code
+ * that returns config descriptors, and altsetting code.
+ *
+ * it's also responsible for power management interactions. some
+ * configurations might not work with our current power sources.
+ *
+ * note that some device controller hardware will constrain what this
+ * code can do, perhaps by disallowing more than one configuration or
+ * by limiting configuration choices (like the pxa2xx).
+ */
+static int
+gmidi_set_config(struct gmidi_device *dev, unsigned number, gfp_t gfp_flags)
+{
+ int result = 0;
+ struct usb_gadget *gadget = dev->gadget;
+
+#if 0
+ /* FIXME */
+ /* Hacking this bit out fixes a bug where on receipt of two
+ USB_REQ_SET_CONFIGURATION messages, we end up with no
+ buffered OUT requests waiting for data. This is clearly
+ hiding a bug elsewhere, because if the config didn't
+ change then we really shouldn't do anything. */
+ /* Having said that, when we do "change" from config 1
+ to config 1, we at least gmidi_reset_config() which
+ clears out any requests on endpoints, so it's not like
+ we leak or anything. */
+ if (number == dev->config) {
+ return 0;
+ }
+#endif
+
+ if (gadget_is_sa1100(gadget) && dev->config) {
+ /* tx fifo is full, but we can't clear it...*/
+ INFO(dev, "can't change configurations\n");
+ return -ESPIPE;
+ }
+ gmidi_reset_config(dev);
+
+ switch (number) {
+ case GMIDI_CONFIG:
+ result = set_gmidi_config(dev, gfp_flags);
+ break;
+ default:
+ result = -EINVAL;
+ /* FALL THROUGH */
+ case 0:
+ return result;
+ }
+
+ if (!result && (!dev->in_ep || !dev->out_ep)) {
+ result = -ENODEV;
+ }
+ if (result) {
+ gmidi_reset_config(dev);
+ } else {
+ char *speed;
+
+ switch (gadget->speed) {
+ case USB_SPEED_LOW: speed = "low"; break;
+ case USB_SPEED_FULL: speed = "full"; break;
+ case USB_SPEED_HIGH: speed = "high"; break;
+ default: speed = "?"; break;
+ }
+
+ dev->config = number;
+ INFO(dev, "%s speed\n", speed);
+ }
+ return result;
+}
+
+
+static void gmidi_setup_complete(struct usb_ep *ep, struct usb_request *req)
+{
+ if (req->status || req->actual != req->length) {
+ DBG((struct gmidi_device *) ep->driver_data,
+ "setup complete --> %d, %d/%d\n",
+ req->status, req->actual, req->length);
+ }
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's
+ * not handled lower down, in hardware or the hardware driver (like
+ * device and endpoint feature flags, and their status). It's all
+ * housekeeping for the gadget function we're implementing. Most of
+ * the work is in config-specific setup.
+ */
+static int gmidi_setup(struct usb_gadget *gadget,
+ const struct usb_ctrlrequest *ctrl)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ struct usb_request *req = dev->req;
+ int value = -EOPNOTSUPP;
+ u16 w_index = le16_to_cpu(ctrl->wIndex);
+ u16 w_value = le16_to_cpu(ctrl->wValue);
+ u16 w_length = le16_to_cpu(ctrl->wLength);
+
+ /* usually this stores reply data in the pre-allocated ep0 buffer,
+ * but config change events will reconfigure hardware.
+ */
+ req->zero = 0;
+ switch (ctrl->bRequest) {
+
+ case USB_REQ_GET_DESCRIPTOR:
+ if (ctrl->bRequestType != USB_DIR_IN) {
+ goto unknown;
+ }
+ switch (w_value >> 8) {
+
+ case USB_DT_DEVICE:
+ value = min(w_length, (u16) sizeof(device_desc));
+ memcpy(req->buf, &device_desc, value);
+ break;
+ case USB_DT_CONFIG:
+ value = config_buf(gadget, req->buf,
+ w_value >> 8,
+ w_value & 0xff);
+ if (value >= 0) {
+ value = min(w_length, (u16)value);
+ }
+ break;
+
+ case USB_DT_STRING:
+ /* wIndex == language code.
+ * this driver only handles one language, you can
+ * add string tables for other languages, using
+ * any UTF-8 characters
+ */
+ value = usb_gadget_get_string(&stringtab,
+ w_value & 0xff, req->buf);
+ if (value >= 0) {
+ value = min(w_length, (u16)value);
+ }
+ break;
+ }
+ break;
+
+ /* currently two configs, two speeds */
+ case USB_REQ_SET_CONFIGURATION:
+ if (ctrl->bRequestType != 0) {
+ goto unknown;
+ }
+ if (gadget->a_hnp_support) {
+ DBG(dev, "HNP available\n");
+ } else if (gadget->a_alt_hnp_support) {
+ DBG(dev, "HNP needs a different root port\n");
+ } else {
+ VDBG(dev, "HNP inactive\n");
+ }
+ spin_lock(&dev->lock);
+ value = gmidi_set_config(dev, w_value, GFP_ATOMIC);
+ spin_unlock(&dev->lock);
+ break;
+ case USB_REQ_GET_CONFIGURATION:
+ if (ctrl->bRequestType != USB_DIR_IN) {
+ goto unknown;
+ }
+ *(u8 *)req->buf = dev->config;
+ value = min(w_length, (u16)1);
+ break;
+
+ /* until we add altsetting support, or other interfaces,
+ * only 0/0 are possible. pxa2xx only supports 0/0 (poorly)
+ * and already killed pending endpoint I/O.
+ */
+ case USB_REQ_SET_INTERFACE:
+ if (ctrl->bRequestType != USB_RECIP_INTERFACE) {
+ goto unknown;
+ }
+ spin_lock(&dev->lock);
+ if (dev->config && w_index < GMIDI_NUM_INTERFACES
+ && w_value == 0)
+ {
+ u8 config = dev->config;
+
+ /* resets interface configuration, forgets about
+ * previous transaction state (queued bufs, etc)
+ * and re-inits endpoint state (toggle etc)
+ * no response queued, just zero status == success.
+ * if we had more than one interface we couldn't
+ * use this "reset the config" shortcut.
+ */
+ gmidi_reset_config(dev);
+ gmidi_set_config(dev, config, GFP_ATOMIC);
+ value = 0;
+ }
+ spin_unlock(&dev->lock);
+ break;
+ case USB_REQ_GET_INTERFACE:
+ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) {
+ goto unknown;
+ }
+ if (!dev->config) {
+ break;
+ }
+ if (w_index >= GMIDI_NUM_INTERFACES) {
+ value = -EDOM;
+ break;
+ }
+ *(u8 *)req->buf = 0;
+ value = min(w_length, (u16)1);
+ break;
+
+ default:
+unknown:
+ VDBG(dev, "unknown control req%02x.%02x v%04x i%04x l%d\n",
+ ctrl->bRequestType, ctrl->bRequest,
+ w_value, w_index, w_length);
+ }
+
+ /* respond with data transfer before status phase? */
+ if (value >= 0) {
+ req->length = value;
+ req->zero = value < w_length;
+ value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ if (value < 0) {
+ DBG(dev, "ep_queue --> %d\n", value);
+ req->status = 0;
+ gmidi_setup_complete(gadget->ep0, req);
+ }
+ }
+
+ /* device either stalls (value < 0) or reports success */
+ return value;
+}
+
+static void gmidi_disconnect(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ unsigned long flags;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ gmidi_reset_config(dev);
+
+ /* a more significant application might have some non-usb
+ * activities to quiesce here, saving resources like power
+ * or pushing the notification up a network stack.
+ */
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* next we may get setup() calls to enumerate new connections;
+ * or an unbind() during shutdown (including removing module).
+ */
+}
+
+static void /* __init_or_exit */ gmidi_unbind(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+ struct snd_card* card;
+
+ DBG(dev, "unbind\n");
+
+ card = dev->card;
+ dev->card = NULL;
+ if (card) {
+ snd_card_free(card);
+ }
+
+ /* we've already been disconnected ... no i/o is active */
+ if (dev->req) {
+ dev->req->length = USB_BUFSIZ;
+ free_ep_req(gadget->ep0, dev->req);
+ }
+ kfree(dev);
+ set_gadget_data(gadget, NULL);
+}
+
+static int gmidi_snd_free(struct snd_device *device)
+{
+ return 0;
+}
+
+static void gmidi_transmit_packet(struct usb_request* req, uint8_t p0,
+ uint8_t p1, uint8_t p2, uint8_t p3)
+{
+ unsigned length = req->length;
+
+ uint8_t* buf = (uint8_t*)req->buf + length;
+ buf[0] = p0;
+ buf[1] = p1;
+ buf[2] = p2;
+ buf[3] = p3;
+ req->length = length + 4;
+}
+
+/*
+ * Converts MIDI commands to USB MIDI packets.
+ */
+static void gmidi_transmit_byte(struct usb_request* req,
+ struct gmidi_in_port* port, uint8_t b)
+{
+ uint8_t p0 = port->cable;
+
+ if (b >= 0xf8) {
+ gmidi_transmit_packet(req, p0 | 0x0f, b, 0, 0);
+ } else if (b >= 0xf0) {
+ switch (b) {
+ case 0xf0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case 0xf1:
+ case 0xf3:
+ port->data[0] = b;
+ port->state = STATE_1PARAM;
+ break;
+ case 0xf2:
+ port->data[0] = b;
+ port->state = STATE_2PARAM_1;
+ break;
+ case 0xf4:
+ case 0xf5:
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf6:
+ gmidi_transmit_packet(req, p0 | 0x05, 0xf6, 0, 0);
+ port->state = STATE_UNKNOWN;
+ break;
+ case 0xf7:
+ switch (port->state) {
+ case STATE_SYSEX_0:
+ gmidi_transmit_packet(req,
+ p0 | 0x05, 0xf7, 0, 0);
+ break;
+ case STATE_SYSEX_1:
+ gmidi_transmit_packet(req,
+ p0 | 0x06, port->data[0], 0xf7, 0);
+ break;
+ case STATE_SYSEX_2:
+ gmidi_transmit_packet(req,
+ p0 | 0x07, port->data[0],
+ port->data[1], 0xf7);
+ break;
+ }
+ port->state = STATE_UNKNOWN;
+ break;
+ }
+ } else if (b >= 0x80) {
+ port->data[0] = b;
+ if (b >= 0xc0 && b <= 0xdf)
+ port->state = STATE_1PARAM;
+ else
+ port->state = STATE_2PARAM_1;
+ } else { /* b < 0x80 */
+ switch (port->state) {
+ case STATE_1PARAM:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ } else {
+ p0 |= 0x02;
+ port->state = STATE_UNKNOWN;
+ }
+ gmidi_transmit_packet(req, p0, port->data[0], b, 0);
+ break;
+ case STATE_2PARAM_1:
+ port->data[1] = b;
+ port->state = STATE_2PARAM_2;
+ break;
+ case STATE_2PARAM_2:
+ if (port->data[0] < 0xf0) {
+ p0 |= port->data[0] >> 4;
+ port->state = STATE_2PARAM_1;
+ } else {
+ p0 |= 0x03;
+ port->state = STATE_UNKNOWN;
+ }
+ gmidi_transmit_packet(req,
+ p0, port->data[0], port->data[1], b);
+ break;
+ case STATE_SYSEX_0:
+ port->data[0] = b;
+ port->state = STATE_SYSEX_1;
+ break;
+ case STATE_SYSEX_1:
+ port->data[1] = b;
+ port->state = STATE_SYSEX_2;
+ break;
+ case STATE_SYSEX_2:
+ gmidi_transmit_packet(req,
+ p0 | 0x04, port->data[0], port->data[1], b);
+ port->state = STATE_SYSEX_0;
+ break;
+ }
+ }
+}
+
+static void gmidi_transmit(struct gmidi_device* dev, struct usb_request* req)
+{
+ struct usb_ep* ep = dev->in_ep;
+ struct gmidi_in_port* port = &dev->in_port;
+
+ if (!ep) {
+ return;
+ }
+ if (!req) {
+ req = alloc_ep_req(ep, buflen);
+ }
+ if (!req) {
+ ERROR(dev, "gmidi_transmit: alloc_ep_request failed\n");
+ return;
+ }
+ req->length = 0;
+ req->complete = gmidi_complete;
+
+ if (port->active) {
+ while (req->length + 3 < buflen) {
+ uint8_t b;
+ if (snd_rawmidi_transmit(dev->in_substream, &b, 1)
+ != 1)
+ {
+ port->active = 0;
+ break;
+ }
+ gmidi_transmit_byte(req, port, b);
+ }
+ }
+ if (req->length > 0) {
+ usb_ep_queue(ep, req, GFP_ATOMIC);
+ } else {
+ free_ep_req(ep, req);
+ }
+}
+
+static void gmidi_in_tasklet(unsigned long data)
+{
+ struct gmidi_device* dev = (struct gmidi_device*)data;
+
+ gmidi_transmit(dev, NULL);
+}
+
+static int gmidi_in_open(struct snd_rawmidi_substream *substream)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_in_open\n");
+ dev->in_substream = substream;
+ dev->in_port.state = STATE_UNKNOWN;
+ return 0;
+}
+
+static int gmidi_in_close(struct snd_rawmidi_substream *substream)
+{
+ VDBG(dev, "gmidi_in_close\n");
+ return 0;
+}
+
+static void gmidi_in_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_in_trigger %d\n", up);
+ dev->in_port.active = up;
+ if (up) {
+ tasklet_hi_schedule(&dev->tasklet);
+ }
+}
+
+static int gmidi_out_open(struct snd_rawmidi_substream *substream)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_out_open\n");
+ dev->out_substream = substream;
+ return 0;
+}
+
+static int gmidi_out_close(struct snd_rawmidi_substream *substream)
+{
+ VDBG(dev, "gmidi_out_close\n");
+ return 0;
+}
+
+static void gmidi_out_trigger(struct snd_rawmidi_substream *substream, int up)
+{
+ struct gmidi_device* dev = substream->rmidi->private_data;
+
+ VDBG(dev, "gmidi_out_trigger %d\n", up);
+ if (up) {
+ set_bit(substream->number, &dev->out_triggered);
+ } else {
+ clear_bit(substream->number, &dev->out_triggered);
+ }
+}
+
+static struct snd_rawmidi_ops gmidi_in_ops = {
+ .open = gmidi_in_open,
+ .close = gmidi_in_close,
+ .trigger = gmidi_in_trigger,
+};
+
+static struct snd_rawmidi_ops gmidi_out_ops = {
+ .open = gmidi_out_open,
+ .close = gmidi_out_close,
+ .trigger = gmidi_out_trigger
+};
+
+/* register as a sound "card" */
+static int gmidi_register_card(struct gmidi_device *dev)
+{
+ struct snd_card *card;
+ struct snd_rawmidi *rmidi;
+ int err;
+ int out_ports = 1;
+ int in_ports = 1;
+ static struct snd_device_ops ops = {
+ .dev_free = gmidi_snd_free,
+ };
+
+ card = snd_card_new(index, id, THIS_MODULE, 0);
+ if (!card) {
+ ERROR(dev, "snd_card_new failed\n");
+ err = -ENOMEM;
+ goto fail;
+ }
+ dev->card = card;
+
+ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, dev, &ops);
+ if (err < 0) {
+ ERROR(dev, "snd_device_new failed: error %d\n", err);
+ goto fail;
+ }
+
+ strcpy(card->driver, longname);
+ strcpy(card->longname, longname);
+ strcpy(card->shortname, shortname);
+
+ /* Set up rawmidi */
+ dev->in_port.dev = dev;
+ dev->in_port.active = 0;
+ snd_component_add(card, "MIDI");
+ err = snd_rawmidi_new(card, "USB MIDI Gadget", 0,
+ out_ports, in_ports, &rmidi);
+ if (err < 0) {
+ ERROR(dev, "snd_rawmidi_new failed: error %d\n", err);
+ goto fail;
+ }
+ dev->rmidi = rmidi;
+ strcpy(rmidi->name, card->shortname);
+ rmidi->info_flags = SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+ rmidi->private_data = dev;
+
+ /* Yes, rawmidi OUTPUT = USB IN, and rawmidi INPUT = USB OUT.
+ It's an upside-down world being a gadget. */
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &gmidi_in_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &gmidi_out_ops);
+
+ snd_card_set_dev(card, &dev->gadget->dev);
+
+ /* register it - we're ready to go */
+ err = snd_card_register(card);
+ if (err < 0) {
+ ERROR(dev, "snd_card_register failed\n");
+ goto fail;
+ }
+
+ VDBG(dev, "gmidi_register_card finished ok\n");
+ return 0;
+
+fail:
+ if (dev->card) {
+ snd_card_free(dev->card);
+ dev->card = NULL;
+ }
+ return err;
+}
+
+/*
+ * Creates an output endpoint, and initializes output ports.
+ */
+static int __devinit gmidi_bind(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev;
+ struct usb_ep *in_ep, *out_ep;
+ int gcnum, err = 0;
+
+ /* support optional vendor/distro customization */
+ if (idVendor) {
+ if (!idProduct) {
+ printk(KERN_ERR "idVendor needs idProduct!\n");
+ return -ENODEV;
+ }
+ device_desc.idVendor = cpu_to_le16(idVendor);
+ device_desc.idProduct = cpu_to_le16(idProduct);
+ if (bcdDevice) {
+ device_desc.bcdDevice = cpu_to_le16(bcdDevice);
+ }
+ }
+ if (iManufacturer) {
+ strlcpy(manufacturer, iManufacturer, sizeof(manufacturer));
+ } else {
+ snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
+ init_utsname()->sysname, init_utsname()->release,
+ gadget->name);
+ }
+ if (iProduct) {
+ strlcpy(product_desc, iProduct, sizeof(product_desc));
+ }
+ if (iSerialNumber) {
+ device_desc.iSerialNumber = STRING_SERIAL,
+ strlcpy(serial_number, iSerialNumber, sizeof(serial_number));
+ }
+
+ /* Bulk-only drivers like this one SHOULD be able to
+ * autoconfigure on any sane usb controller driver,
+ * but there may also be important quirks to address.
+ */
+ usb_ep_autoconfig_reset(gadget);
+ in_ep = usb_ep_autoconfig(gadget, &bulk_in_desc);
+ if (!in_ep) {
+autoconf_fail:
+ printk(KERN_ERR "%s: can't autoconfigure on %s\n",
+ shortname, gadget->name);
+ return -ENODEV;
+ }
+ EP_IN_NAME = in_ep->name;
+ in_ep->driver_data = in_ep; /* claim */
+
+ out_ep = usb_ep_autoconfig(gadget, &bulk_out_desc);
+ if (!out_ep) {
+ goto autoconf_fail;
+ }
+ EP_OUT_NAME = out_ep->name;
+ out_ep->driver_data = out_ep; /* claim */
+
+ gcnum = usb_gadget_controller_number(gadget);
+ if (gcnum >= 0) {
+ device_desc.bcdDevice = cpu_to_le16(0x0200 + gcnum);
+ } else {
+ /* gmidi is so simple (no altsettings) that
+ * it SHOULD NOT have problems with bulk-capable hardware.
+ * so warn about unrecognized controllers, don't panic.
+ */
+ printk(KERN_WARNING "%s: controller '%s' not recognized\n",
+ shortname, gadget->name);
+ device_desc.bcdDevice = __constant_cpu_to_le16(0x9999);
+ }
+
+
+ /* ok, we made sense of the hardware ... */
+ dev = kzalloc(sizeof(*dev), SLAB_KERNEL);
+ if (!dev) {
+ return -ENOMEM;
+ }
+ spin_lock_init(&dev->lock);
+ dev->gadget = gadget;
+ dev->in_ep = in_ep;
+ dev->out_ep = out_ep;
+ set_gadget_data(gadget, dev);
+ tasklet_init(&dev->tasklet, gmidi_in_tasklet, (unsigned long)dev);
+
+ /* preallocate control response and buffer */
+ dev->req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+ if (!dev->req) {
+ err = -ENOMEM;
+ goto fail;
+ }
+ dev->req->buf = usb_ep_alloc_buffer(gadget->ep0, USB_BUFSIZ,
+ &dev->req->dma, GFP_KERNEL);
+ if (!dev->req->buf) {
+ err = -ENOMEM;
+ goto fail;
+ }
+
+ dev->req->complete = gmidi_setup_complete;
+
+ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket;
+
+ gadget->ep0->driver_data = dev;
+
+ INFO(dev, "%s, version: " DRIVER_VERSION "\n", longname);
+ INFO(dev, "using %s, OUT %s IN %s\n", gadget->name,
+ EP_OUT_NAME, EP_IN_NAME);
+
+ /* register as an ALSA sound card */
+ err = gmidi_register_card(dev);
+ if (err < 0) {
+ goto fail;
+ }
+
+ VDBG(dev, "gmidi_bind finished ok\n");
+ return 0;
+
+fail:
+ gmidi_unbind(gadget);
+ return err;
+}
+
+
+static void gmidi_suspend(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+
+ if (gadget->speed == USB_SPEED_UNKNOWN) {
+ return;
+ }
+
+ DBG(dev, "suspend\n");
+}
+
+static void gmidi_resume(struct usb_gadget *gadget)
+{
+ struct gmidi_device *dev = get_gadget_data(gadget);
+
+ DBG(dev, "resume\n");
+}
+
+
+static struct usb_gadget_driver gmidi_driver = {
+ .speed = USB_SPEED_FULL,
+ .function = (char *)longname,
+ .bind = gmidi_bind,
+ .unbind = __exit_p(gmidi_unbind),
+
+ .setup = gmidi_setup,
+ .disconnect = gmidi_disconnect,
+
+ .suspend = gmidi_suspend,
+ .resume = gmidi_resume,
+
+ .driver = {
+ .name = (char *)shortname,
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init gmidi_init(void)
+{
+ return usb_gadget_register_driver(&gmidi_driver);
+}
+module_init(gmidi_init);
+
+static void __exit gmidi_cleanup(void)
+{
+ usb_gadget_unregister_driver(&gmidi_driver);
+}
+module_exit(gmidi_cleanup);
+
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/inode.c
index 3bdc5e3ba23..86924f9cdd7 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/inode.c
@@ -32,6 +32,7 @@
#include <linux/compiler.h>
#include <asm/uaccess.h>
#include <linux/slab.h>
+#include <linux/poll.h>
#include <linux/device.h>
#include <linux/moduleparam.h>
@@ -222,7 +223,6 @@ static void put_ep (struct ep_data *data)
/* needs no more cleanup */
BUG_ON (!list_empty (&data->epfiles));
BUG_ON (waitqueue_active (&data->wait));
- BUG_ON (down_trylock (&data->lock) != 0);
kfree (data);
}
@@ -342,7 +342,7 @@ fail:
static ssize_t
ep_io (struct ep_data *epdata, void *buf, unsigned len)
{
- DECLARE_COMPLETION (done);
+ DECLARE_COMPLETION_ONSTACK (done);
int value;
spin_lock_irq (&epdata->dev->lock);
@@ -477,6 +477,10 @@ static int
ep_release (struct inode *inode, struct file *fd)
{
struct ep_data *data = fd->private_data;
+ int value;
+
+ if ((value = down_interruptible(&data->lock)) < 0)
+ return value;
/* clean up if this can be reopened */
if (data->state != STATE_EP_UNBOUND) {
@@ -485,6 +489,7 @@ ep_release (struct inode *inode, struct file *fd)
data->hs_desc.bDescriptorType = 0;
usb_ep_disable(data->ep);
}
+ up (&data->lock);
put_ep (data);
return 0;
}
@@ -528,7 +533,8 @@ struct kiocb_priv {
struct usb_request *req;
struct ep_data *epdata;
void *buf;
- char __user *ubuf; /* NULL for writes */
+ const struct iovec *iv;
+ unsigned long nr_segs;
unsigned actual;
};
@@ -556,17 +562,32 @@ static int ep_aio_cancel(struct kiocb *iocb, struct io_event *e)
static ssize_t ep_aio_read_retry(struct kiocb *iocb)
{
struct kiocb_priv *priv = iocb->private;
- ssize_t status = priv->actual;
-
- /* we "retry" to get the right mm context for this: */
- status = copy_to_user(priv->ubuf, priv->buf, priv->actual);
- if (unlikely(0 != status))
- status = -EFAULT;
- else
- status = priv->actual;
- kfree(priv->buf);
- kfree(priv);
- return status;
+ ssize_t len, total;
+ int i;
+
+ /* we "retry" to get the right mm context for this: */
+
+ /* copy stuff into user buffers */
+ total = priv->actual;
+ len = 0;
+ for (i=0; i < priv->nr_segs; i++) {
+ ssize_t this = min((ssize_t)(priv->iv[i].iov_len), total);
+
+ if (copy_to_user(priv->iv[i].iov_base, priv->buf, this)) {
+ if (len == 0)
+ len = -EFAULT;
+ break;
+ }
+
+ total -= this;
+ len += this;
+ if (total == 0)
+ break;
+ }
+ kfree(priv->buf);
+ kfree(priv);
+ aio_put_req(iocb);
+ return len;
}
static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
@@ -579,7 +600,7 @@ static void ep_aio_complete(struct usb_ep *ep, struct usb_request *req)
spin_lock(&epdata->dev->lock);
priv->req = NULL;
priv->epdata = NULL;
- if (priv->ubuf == NULL
+ if (priv->iv == NULL
|| unlikely(req->actual == 0)
|| unlikely(kiocbIsCancelled(iocb))) {
kfree(req->buf);
@@ -614,7 +635,8 @@ ep_aio_rwtail(
char *buf,
size_t len,
struct ep_data *epdata,
- char __user *ubuf
+ const struct iovec *iv,
+ unsigned long nr_segs
)
{
struct kiocb_priv *priv;
@@ -629,7 +651,8 @@ fail:
return value;
}
iocb->private = priv;
- priv->ubuf = ubuf;
+ priv->iv = iv;
+ priv->nr_segs = nr_segs;
value = get_ready_ep(iocb->ki_filp->f_flags, epdata);
if (unlikely(value < 0)) {
@@ -669,47 +692,59 @@ fail:
kfree(priv);
put_ep(epdata);
} else
- value = (ubuf ? -EIOCBRETRY : -EIOCBQUEUED);
+ value = (iv ? -EIOCBRETRY : -EIOCBQUEUED);
return value;
}
static ssize_t
-ep_aio_read(struct kiocb *iocb, char __user *ubuf, size_t len, loff_t o)
+ep_aio_read(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
if (unlikely(epdata->desc.bEndpointAddress & USB_DIR_IN))
return -EINVAL;
- buf = kmalloc(len, GFP_KERNEL);
+
+ buf = kmalloc(iocb->ki_left, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
+
iocb->ki_retry = ep_aio_read_retry;
- return ep_aio_rwtail(iocb, buf, len, epdata, ubuf);
+ return ep_aio_rwtail(iocb, buf, iocb->ki_left, epdata, iov, nr_segs);
}
static ssize_t
-ep_aio_write(struct kiocb *iocb, const char __user *ubuf, size_t len, loff_t o)
+ep_aio_write(struct kiocb *iocb, const struct iovec *iov,
+ unsigned long nr_segs, loff_t o)
{
struct ep_data *epdata = iocb->ki_filp->private_data;
char *buf;
+ size_t len = 0;
+ int i = 0;
if (unlikely(!(epdata->desc.bEndpointAddress & USB_DIR_IN)))
return -EINVAL;
- buf = kmalloc(len, GFP_KERNEL);
+
+ buf = kmalloc(iocb->ki_left, GFP_KERNEL);
if (unlikely(!buf))
return -ENOMEM;
- if (unlikely(copy_from_user(buf, ubuf, len) != 0)) {
- kfree(buf);
- return -EFAULT;
+
+ for (i=0; i < nr_segs; i++) {
+ if (unlikely(copy_from_user(&buf[len], iov[i].iov_base,
+ iov[i].iov_len) != 0)) {
+ kfree(buf);
+ return -EFAULT;
+ }
+ len += iov[i].iov_len;
}
- return ep_aio_rwtail(iocb, buf, len, epdata, NULL);
+ return ep_aio_rwtail(iocb, buf, len, epdata, NULL, 0);
}
/*----------------------------------------------------------------------*/
/* used after endpoint configuration */
-static struct file_operations ep_io_operations = {
+static const struct file_operations ep_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -741,7 +776,7 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
struct ep_data *data = fd->private_data;
struct usb_ep *ep;
u32 tag;
- int value;
+ int value, length = len;
if ((value = down_interruptible (&data->lock)) < 0)
return value;
@@ -792,7 +827,6 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
goto fail0;
}
}
- value = len;
spin_lock_irq (&data->dev->lock);
if (data->dev->state == STATE_DEV_UNBOUND) {
@@ -822,8 +856,10 @@ ep_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
data->name);
data->state = STATE_EP_DEFER_ENABLE;
}
- if (value == 0)
+ if (value == 0) {
fd->f_op = &ep_io_operations;
+ value = length;
+ }
gone:
spin_unlock_irq (&data->dev->lock);
if (value < 0) {
@@ -844,7 +880,7 @@ fail1:
static int
ep_open (struct inode *inode, struct file *fd)
{
- struct ep_data *data = inode->u.generic_ip;
+ struct ep_data *data = inode->i_private;
int value = -EBUSY;
if (down_interruptible (&data->lock) != 0)
@@ -867,7 +903,7 @@ ep_open (struct inode *inode, struct file *fd)
}
/* used before endpoint configuration */
-static struct file_operations ep_config_operations = {
+static const struct file_operations ep_config_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -1009,7 +1045,7 @@ ep0_read (struct file *fd, char __user *buf, size_t len, loff_t *ptr)
else {
len = min (len, (size_t)dev->req->actual);
// FIXME don't call this with the spinlock held ...
- if (copy_to_user (buf, &dev->req->buf, len))
+ if (copy_to_user (buf, dev->req->buf, len))
retval = -EFAULT;
clean_req (dev->gadget->ep0, dev->req);
/* NOTE userspace can't yet choose to stall */
@@ -1229,6 +1265,35 @@ dev_release (struct inode *inode, struct file *fd)
return 0;
}
+static unsigned int
+ep0_poll (struct file *fd, poll_table *wait)
+{
+ struct dev_data *dev = fd->private_data;
+ int mask = 0;
+
+ poll_wait(fd, &dev->wait, wait);
+
+ spin_lock_irq (&dev->lock);
+
+ /* report fd mode change before acting on it */
+ if (dev->setup_abort) {
+ dev->setup_abort = 0;
+ mask = POLLHUP;
+ goto out;
+ }
+
+ if (dev->state == STATE_SETUP) {
+ if (dev->setup_in || dev->setup_can_stall)
+ mask = POLLOUT;
+ } else {
+ if (dev->ev_next != 0)
+ mask = POLLIN;
+ }
+out:
+ spin_unlock_irq(&dev->lock);
+ return mask;
+}
+
static int dev_ioctl (struct inode *inode, struct file *fd,
unsigned code, unsigned long value)
{
@@ -1241,14 +1306,14 @@ static int dev_ioctl (struct inode *inode, struct file *fd,
}
/* used after device configuration */
-static struct file_operations ep0_io_operations = {
+static const struct file_operations ep0_io_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = ep0_read,
.write = ep0_write,
.fasync = ep0_fasync,
- // .poll = ep0_poll,
+ .poll = ep0_poll,
.ioctl = dev_ioctl,
.release = dev_release,
};
@@ -1696,16 +1761,17 @@ gadgetfs_disconnect (struct usb_gadget *gadget)
{
struct dev_data *dev = get_gadget_data (gadget);
+ spin_lock (&dev->lock);
if (dev->state == STATE_UNCONNECTED) {
DBG (dev, "already unconnected\n");
- return;
+ goto exit;
}
dev->state = STATE_UNCONNECTED;
INFO (dev, "disconnected\n");
- spin_lock (&dev->lock);
next_event (dev, GADGETFS_DISCONNECT);
ep0_readable (dev);
+exit:
spin_unlock (&dev->lock);
}
@@ -1909,7 +1975,7 @@ fail:
static int
dev_open (struct inode *inode, struct file *fd)
{
- struct dev_data *dev = inode->u.generic_ip;
+ struct dev_data *dev = inode->i_private;
int value = -EBUSY;
if (dev->state == STATE_DEV_DISABLED) {
@@ -1922,7 +1988,7 @@ dev_open (struct inode *inode, struct file *fd)
return value;
}
-static struct file_operations dev_init_operations = {
+static const struct file_operations dev_init_operations = {
.owner = THIS_MODULE,
.llseek = no_llseek,
@@ -1966,11 +2032,10 @@ gadgetfs_make_inode (struct super_block *sb,
inode->i_mode = mode;
inode->i_uid = default_uid;
inode->i_gid = default_gid;
- inode->i_blksize = PAGE_CACHE_SIZE;
inode->i_blocks = 0;
inode->i_atime = inode->i_mtime = inode->i_ctime
= CURRENT_TIME;
- inode->u.generic_ip = data;
+ inode->i_private = data;
inode->i_fop = fops;
}
return inode;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index 09243239d94..3bda37f9a35 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -2,7 +2,7 @@
* Driver for the PLX NET2280 USB device controller.
* Specs and errata are available from <http://www.plxtech.com>.
*
- * PLX Technology Inc. (formerly NetChip Technology) supported the
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
* development of this driver.
*
*
@@ -26,7 +26,8 @@
* Copyright (C) 2003 David Brownell
* Copyright (C) 2003-2005 PLX Technology, Inc.
*
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility with 2282 chip
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ * with 2282 chip
*
* 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
@@ -85,7 +86,7 @@ static const char driver_name [] = "net2280";
static const char driver_desc [] = DRIVER_DESC;
static const char ep0name [] = "ep0";
-static const char *ep_name [] = {
+static const char *const ep_name [] = {
ep0name,
"ep-a", "ep-b", "ep-c", "ep-d",
"ep-e", "ep-f",
@@ -225,7 +226,9 @@ net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
if (!ep->is_in)
writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
else if (dev->pdev->device != 0x2280) {
- /* Added for 2282, Don't use nak packets on an in endpoint, this was ignored on 2280 */
+ /* Added for 2282, Don't use nak packets on an in endpoint,
+ * this was ignored on 2280
+ */
writel ((1 << CLEAR_NAK_OUT_PACKETS)
| (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
}
@@ -288,7 +291,7 @@ static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
return -ETIMEDOUT;
}
-static struct usb_ep_ops net2280_ep_ops;
+static const struct usb_ep_ops net2280_ep_ops;
static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
{
@@ -449,34 +452,15 @@ net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
/*-------------------------------------------------------------------------*/
-#undef USE_KMALLOC
-
-/* many common platforms have dma-coherent caches, which means that it's
- * safe to use kmalloc() memory for all i/o buffers without using any
- * cache flushing calls. (unless you're trying to share cache lines
- * between dma and non-dma activities, which is a slow idea in any case.)
+/*
+ * dma-coherent memory allocation (for dma-capable endpoints)
*
- * other platforms need more care, with 2.5 having a moderately general
- * solution (which falls down for allocations smaller than one page)
- * that improves significantly on the 2.4 PCI allocators by removing
- * the restriction that memory never be freed in_interrupt().
+ * NOTE: the dma_*_coherent() API calls suck. Most implementations are
+ * (a) page-oriented, so small buffers lose big; and (b) asymmetric with
+ * respect to calls with irqs disabled: alloc is safe, free is not.
+ * We currently work around (b), but not (a).
*/
-#if defined(CONFIG_X86)
-#define USE_KMALLOC
-
-#elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
-#define USE_KMALLOC
-#elif defined(CONFIG_MIPS) && !defined(CONFIG_DMA_NONCOHERENT)
-#define USE_KMALLOC
-
-/* FIXME there are other cases, including an x86-64 one ... */
-#endif
-
-/* allocating buffers this way eliminates dma mapping overhead, which
- * on some platforms will mean eliminating a per-io buffer copy. with
- * some kinds of system caches, further tweaks may still be needed.
- */
static void *
net2280_alloc_buffer (
struct usb_ep *_ep,
@@ -493,43 +477,71 @@ net2280_alloc_buffer (
return NULL;
*dma = DMA_ADDR_INVALID;
-#if defined(USE_KMALLOC)
- retval = kmalloc(bytes, gfp_flags);
- if (retval)
- *dma = virt_to_phys(retval);
-#else
- if (ep->dma) {
- /* the main problem with this call is that it wastes memory
- * on typical 1/N page allocations: it allocates 1-N pages.
- */
-#warning Using dma_alloc_coherent even with buffers smaller than a page.
+ if (ep->dma)
retval = dma_alloc_coherent(&ep->dev->pdev->dev,
bytes, dma, gfp_flags);
- } else
+ else
retval = kmalloc(bytes, gfp_flags);
-#endif
return retval;
}
+static DEFINE_SPINLOCK(buflock);
+static LIST_HEAD(buffers);
+
+struct free_record {
+ struct list_head list;
+ struct device *dev;
+ unsigned bytes;
+ dma_addr_t dma;
+};
+
+static void do_free(unsigned long ignored)
+{
+ spin_lock_irq(&buflock);
+ while (!list_empty(&buffers)) {
+ struct free_record *buf;
+
+ buf = list_entry(buffers.next, struct free_record, list);
+ list_del(&buf->list);
+ spin_unlock_irq(&buflock);
+
+ dma_free_coherent(buf->dev, buf->bytes, buf, buf->dma);
+
+ spin_lock_irq(&buflock);
+ }
+ spin_unlock_irq(&buflock);
+}
+
+static DECLARE_TASKLET(deferred_free, do_free, 0);
+
static void
net2280_free_buffer (
struct usb_ep *_ep,
- void *buf,
+ void *address,
dma_addr_t dma,
unsigned bytes
) {
/* free memory into the right allocator */
-#ifndef USE_KMALLOC
if (dma != DMA_ADDR_INVALID) {
struct net2280_ep *ep;
+ struct free_record *buf = address;
+ unsigned long flags;
ep = container_of(_ep, struct net2280_ep, ep);
if (!_ep)
return;
- dma_free_coherent(&ep->dev->pdev->dev, bytes, buf, dma);
+
+ ep = container_of (_ep, struct net2280_ep, ep);
+ buf->dev = &ep->dev->pdev->dev;
+ buf->bytes = bytes;
+ buf->dma = dma;
+
+ spin_lock_irqsave(&buflock, flags);
+ list_add_tail(&buf->list, &buffers);
+ tasklet_schedule(&deferred_free);
+ spin_unlock_irqrestore(&buflock, flags);
} else
-#endif
- kfree (buf);
+ kfree (address);
}
/*-------------------------------------------------------------------------*/
@@ -737,7 +749,8 @@ fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
*/
if (ep->is_in)
dmacount |= (1 << DMA_DIRECTION);
- if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) || ep->dev->pdev->device != 0x2280)
+ if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
+ || ep->dev->pdev->device != 0x2280)
dmacount |= (1 << END_OF_CHAIN);
req->valid = valid;
@@ -812,7 +825,7 @@ static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
/* previous OUT packet might have been short */
if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
- & (1 << NAK_OUT_PACKETS)) != 0) {
+ & (1 << NAK_OUT_PACKETS)) != 0) {
writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
&ep->regs->ep_stat);
@@ -1373,7 +1386,7 @@ net2280_fifo_flush (struct usb_ep *_ep)
(void) readl (&ep->regs->ep_rsp);
}
-static struct usb_ep_ops net2280_ep_ops = {
+static const struct usb_ep_ops net2280_ep_ops = {
.enable = net2280_enable,
.disable = net2280_disable,
@@ -1631,7 +1644,7 @@ show_registers (struct device *_dev, struct device_attribute *attr, char *buf)
}
/* Indexed Registers */
- // none yet
+ // none yet
/* Statistics */
t = scnprintf (next, size, "\nirqs: ");
@@ -1691,11 +1704,11 @@ show_queues (struct device *_dev, struct device_attribute *attr, char *buf)
({ char *val;
switch (d->bmAttributes & 0x03) {
case USB_ENDPOINT_XFER_BULK:
- val = "bulk"; break;
+ val = "bulk"; break;
case USB_ENDPOINT_XFER_INT:
- val = "intr"; break;
+ val = "intr"; break;
default:
- val = "iso"; break;
+ val = "iso"; break;
}; val; }),
le16_to_cpu (d->wMaxPacketSize) & 0x1fff,
ep->dma ? "dma" : "pio", ep->fifo_size
@@ -1808,8 +1821,8 @@ extern int net2280_set_fifo_mode (struct usb_gadget *gadget, int mode);
* net2280_set_fifo_mode - change allocation of fifo buffers
* @gadget: access to the net2280 device that will be updated
* @mode: 0 for default, four 1kB buffers (ep-a through ep-d);
- * 1 for two 2kB buffers (ep-a and ep-b only);
- * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
+ * 1 for two 2kB buffers (ep-a and ep-b only);
+ * 2 for one 2kB buffer (ep-a) and two 1kB ones (ep-b, ep-c).
*
* returns zero on success, else negative errno. when this succeeds,
* the contents of gadget->ep_list may have changed.
@@ -2241,7 +2254,8 @@ static void handle_ep_small (struct net2280_ep *ep)
req->td->dmacount = 0;
t = readl (&ep->regs->ep_avail);
dma_done (ep, req, count,
- (ep->out_overflow || t) ? -EOVERFLOW : 0);
+ (ep->out_overflow || t)
+ ? -EOVERFLOW : 0);
}
/* also flush to prevent erratum 0106 trouble */
@@ -2411,7 +2425,7 @@ static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
, &ep->regs->ep_stat);
u.raw [0] = readl (&dev->usb->setup0123);
u.raw [1] = readl (&dev->usb->setup4567);
-
+
cpu_to_le32s (&u.raw [0]);
cpu_to_le32s (&u.raw [1]);
@@ -2578,14 +2592,16 @@ static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
/* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
* Root Port Reset is indicated by ROOT_PORT_RESET_INTERRRUPT set and
- * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
+ * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
* only indicates a change in the reset state).
*/
if (stat & tmp) {
writel (tmp, &dev->regs->irqstat1);
- if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
- ((readl (&dev->usb->usbstat) & mask) == 0))
- || ((readl (&dev->usb->usbctl) & (1 << VBUS_PIN)) == 0)
+ if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
+ && ((readl (&dev->usb->usbstat) & mask)
+ == 0))
+ || ((readl (&dev->usb->usbctl)
+ & (1 << VBUS_PIN)) == 0)
) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
DEBUG (dev, "disconnect %s\n",
dev->driver->driver.name);
@@ -2852,7 +2868,7 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
/* now all the pci goodies ... */
if (pci_enable_device (pdev) < 0) {
- retval = -ENODEV;
+ retval = -ENODEV;
goto done;
}
dev->enabled = 1;
@@ -2870,6 +2886,10 @@ static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
}
dev->region = 1;
+ /* FIXME provide firmware download interface to put
+ * 8051 code into the chip, e.g. to turn on PCI PM.
+ */
+
base = ioremap_nocache (resource, len);
if (base == NULL) {
DEBUG (dev, "can't map memory\n");
@@ -2984,16 +3004,16 @@ static void net2280_shutdown (struct pci_dev *pdev)
/*-------------------------------------------------------------------------*/
-static struct pci_device_id pci_ids [] = { {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
+static const struct pci_device_id pci_ids [] = { {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
.vendor = 0x17cc,
.device = 0x2280,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
}, {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
.vendor = 0x17cc,
.device = 0x2282,
.subvendor = PCI_ANY_ID,
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 2de9748ee67..8c18df86983 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -40,7 +40,7 @@
#include <linux/platform_device.h>
#include <linux/usb_ch9.h>
#include <linux/usb_gadget.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <asm/byteorder.h>
@@ -2437,7 +2437,7 @@ static int proc_udc_open(struct inode *inode, struct file *file)
return single_open(file, proc_udc_show, NULL);
}
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
.open = proc_udc_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -2869,7 +2869,7 @@ cleanup0:
static int __exit omap_udc_remove(struct platform_device *pdev)
{
- DECLARE_COMPLETION(done);
+ DECLARE_COMPLETION_ONSTACK(done);
if (!udc)
return -ENODEV;
diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c
index fff027d30a0..f1adcf8b202 100644
--- a/drivers/usb/gadget/pxa2xx_udc.c
+++ b/drivers/usb/gadget/pxa2xx_udc.c
@@ -150,6 +150,39 @@ MODULE_PARM_DESC (fifo_mode, "pxa2xx udc fifo mode");
static void pxa2xx_ep_fifo_flush (struct usb_ep *ep);
static void nuke (struct pxa2xx_ep *, int status);
+/* one GPIO should be used to detect VBUS from the host */
+static int is_vbus_present(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_vbus)
+ return pxa_gpio_get(mach->gpio_vbus);
+ if (mach->udc_is_connected)
+ return mach->udc_is_connected();
+ return 1;
+}
+
+/* one GPIO should control a D+ pullup, so host sees this device (or not) */
+static void pullup_off(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_pullup)
+ pxa_gpio_set(mach->gpio_pullup, 0);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+}
+
+static void pullup_on(void)
+{
+ struct pxa2xx_udc_mach_info *mach = the_controller->mach;
+
+ if (mach->gpio_pullup)
+ pxa_gpio_set(mach->gpio_pullup, 1);
+ else if (mach->udc_command)
+ mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+}
+
static void pio_irq_enable(int bEndpointAddress)
{
bEndpointAddress &= 0xf;
@@ -1721,6 +1754,16 @@ lubbock_vbus_irq(int irq, void *_dev, struct pt_regs *r)
#endif
+static irqreturn_t
+udc_vbus_irq(int irq, void *_dev, struct pt_regs *r)
+{
+ struct pxa2xx_udc *dev = _dev;
+ int vbus = pxa_gpio_get(dev->mach->gpio_vbus);
+
+ pxa2xx_udc_vbus_session(&dev->gadget, vbus);
+ return IRQ_HANDLED;
+}
+
/*-------------------------------------------------------------------------*/
@@ -2438,7 +2481,7 @@ static struct pxa2xx_udc memory = {
static int __init pxa2xx_udc_probe(struct platform_device *pdev)
{
struct pxa2xx_udc *dev = &memory;
- int retval, out_dma = 1;
+ int retval, out_dma = 1, vbus_irq;
u32 chiprev;
/* insist on Intel/ARM/XScale */
@@ -2502,6 +2545,16 @@ static int __init pxa2xx_udc_probe(struct platform_device *pdev)
/* other non-static parts of init */
dev->dev = &pdev->dev;
dev->mach = pdev->dev.platform_data;
+ if (dev->mach->gpio_vbus) {
+ vbus_irq = IRQ_GPIO(dev->mach->gpio_vbus & GPIO_MD_MASK_NR);
+ pxa_gpio_mode((dev->mach->gpio_vbus & GPIO_MD_MASK_NR)
+ | GPIO_IN);
+ set_irq_type(vbus_irq, IRQT_BOTHEDGE);
+ } else
+ vbus_irq = 0;
+ if (dev->mach->gpio_pullup)
+ pxa_gpio_mode((dev->mach->gpio_pullup & GPIO_MD_MASK_NR)
+ | GPIO_OUT | GPIO_DFLT_LOW);
init_timer(&dev->timer);
dev->timer.function = udc_watchdog;
@@ -2557,8 +2610,19 @@ lubbock_fail0:
HEX_DISPLAY(dev->stats.irqs);
LUB_DISC_BLNK_LED &= 0xff;
#endif
- }
+ } else
#endif
+ if (vbus_irq) {
+ retval = request_irq(vbus_irq, udc_vbus_irq,
+ SA_INTERRUPT | SA_SAMPLE_RANDOM,
+ driver_name, dev);
+ if (retval != 0) {
+ printk(KERN_ERR "%s: can't get irq %i, err %d\n",
+ driver_name, vbus_irq, retval);
+ free_irq(IRQ_USB, dev);
+ return -EBUSY;
+ }
+ }
create_proc_files();
return 0;
@@ -2587,6 +2651,8 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev)
free_irq(LUBBOCK_USB_IRQ, dev);
}
#endif
+ if (dev->mach->gpio_vbus)
+ free_irq(IRQ_GPIO(dev->mach->gpio_vbus), dev);
platform_set_drvdata(pdev, NULL);
the_controller = NULL;
return 0;
diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h
index 19a883f7d1b..8e598c8bf4e 100644
--- a/drivers/usb/gadget/pxa2xx_udc.h
+++ b/drivers/usb/gadget/pxa2xx_udc.h
@@ -177,27 +177,19 @@ struct pxa2xx_udc {
static struct pxa2xx_udc *the_controller;
-/* one GPIO should be used to detect VBUS from the host */
-static inline int is_vbus_present(void)
+static inline int pxa_gpio_get(unsigned gpio)
{
- if (!the_controller->mach->udc_is_connected)
- return 1;
- return the_controller->mach->udc_is_connected();
+ return (GPLR(gpio) & GPIO_bit(gpio)) != 0;
}
-/* one GPIO should control a D+ pullup, so host sees this device (or not) */
-static inline void pullup_off(void)
+static inline void pxa_gpio_set(unsigned gpio, int is_on)
{
- if (!the_controller->mach->udc_command)
- return;
- the_controller->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
-}
+ int mask = GPIO_bit(gpio);
-static inline void pullup_on(void)
-{
- if (!the_controller->mach->udc_command)
- return;
- the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
+ if (is_on)
+ GPSR(gpio) = mask;
+ else
+ GPCR(gpio) = mask;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c
index e762aa19ab0..208e55a667a 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/serial.c
@@ -271,7 +271,7 @@ static unsigned int use_acm = GS_DEFAULT_USE_ACM;
/* tty driver struct */
-static struct tty_operations gs_tty_ops = {
+static const struct tty_operations gs_tty_ops = {
.open = gs_open,
.close = gs_close,
.write = gs_write,
@@ -1120,12 +1120,15 @@ static int gs_send(struct gs_dev *dev)
gs_debug_level(3, "gs_send: len=%d, 0x%2.2x 0x%2.2x 0x%2.2x ...\n", len, *((unsigned char *)req->buf), *((unsigned char *)req->buf+1), *((unsigned char *)req->buf+2));
list_del(&req_entry->re_entry);
req->length = len;
+ spin_unlock_irqrestore(&dev->dev_lock, flags);
if ((ret=usb_ep_queue(ep, req, GFP_ATOMIC))) {
printk(KERN_ERR
"gs_send: cannot queue read request, ret=%d\n",
ret);
+ spin_lock_irqsave(&dev->dev_lock, flags);
break;
}
+ spin_lock_irqsave(&dev->dev_lock, flags);
} else {
break;
}
@@ -1431,7 +1434,7 @@ static int __init gs_bind(struct usb_gadget *gadget)
return -ENOMEM;
snprintf(manufacturer, sizeof(manufacturer), "%s %s with %s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
memset(dev, 0, sizeof(struct gs_dev));
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index b7018ee487e..0f809dd6849 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -1242,7 +1242,7 @@ autoconf_fail:
EP_OUT_NAME, EP_IN_NAME);
snprintf (manufacturer, sizeof manufacturer, "%s %s with %s",
- system_utsname.sysname, system_utsname.release,
+ init_utsname()->sysname, init_utsname()->release,
gadget->name);
return 0;
diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
index b93d71d28db..cf10cbc98f8 100644
--- a/drivers/usb/host/Kconfig
+++ b/drivers/usb/host/Kconfig
@@ -83,6 +83,7 @@ config USB_OHCI_HCD
tristate "OHCI HCD support"
depends on USB && USB_ARCH_HAS_OHCI
select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
+ select I2C if ARCH_PNX4008
---help---
The Open Host Controller Interface (OHCI) is a standard for accessing
USB 1.1 host controller hardware. It does more in hardware than Intel's
@@ -141,6 +142,34 @@ config USB_UHCI_HCD
To compile this driver as a module, choose M here: the
module will be called uhci-hcd.
+config USB_U132_HCD
+ tristate "Elan U132 Adapter Host Controller"
+ depends on USB && USB_FTDI_ELAN
+ default M
+ help
+ The U132 adapter is a USB to CardBus adapter specifically designed
+ for PC cards that contain an OHCI host controller. Typical PC cards
+ are the Orange Mobile 3G Option GlobeTrotter Fusion card. The U132
+ adapter will *NOT* work with PC cards that do not contain an OHCI
+ controller.
+
+ For those PC cards that contain multiple OHCI controllers only ther
+ first one is used.
+
+ The driver consists of two modules, the "ftdi-elan" module is a
+ USB client driver that interfaces to the FTDI chip within ELAN's
+ USB-to-PCMCIA adapter, and this "u132-hcd" module is a USB host
+ controller driver that talks to the OHCI controller within the
+ CardBus cards that are inserted in the U132 adapter.
+
+ This driver has been tested with a CardBus OHCI USB adapter, and
+ worked with a USB PEN Drive inserted into the first USB port of
+ the PCCARD. A rather pointless thing to do, but useful for testing.
+
+ It is safe to say M here.
+
+ See also <http://www.elandigitalsystems.com/support/ufaq/u132linux.php>
+
config USB_SL811_HCD
tristate "SL811HS HCD support"
depends on USB
diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile
index e3020f4b17b..a2e58c86849 100644
--- a/drivers/usb/host/Makefile
+++ b/drivers/usb/host/Makefile
@@ -14,4 +14,5 @@ obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o
obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
+obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
diff --git a/drivers/usb/host/ehci-au1xxx.c b/drivers/usb/host/ehci-au1xxx.c
index 26ed757d22a..5d1b12aad77 100644
--- a/drivers/usb/host/ehci-au1xxx.c
+++ b/drivers/usb/host/ehci-au1xxx.c
@@ -200,6 +200,7 @@ static const struct hc_driver ehci_au1xxx_hc_driver = {
.reset = ehci_init,
.start = ehci_run,
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -268,6 +269,7 @@ MODULE_ALIAS("au1xxx-ehci");
static struct platform_driver ehci_hcd_au1xxx_driver = {
.probe = ehci_hcd_au1xxx_drv_probe,
.remove = ehci_hcd_au1xxx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ehci_hcd_au1xxx_drv_suspend, */
/*.resume = ehci_hcd_au1xxx_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ehci-dbg.c b/drivers/usb/host/ehci-dbg.c
index 65ac9fef3a7..23b95b2bfe1 100644
--- a/drivers/usb/host/ehci-dbg.c
+++ b/drivers/usb/host/ehci-dbg.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001-2002 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -65,7 +65,7 @@ static void dbg_hcs_params (struct ehci_hcd *ehci, char *label)
for (i = 0; i < HCS_N_PORTS (params); i++) {
// FIXME MIPS won't readb() ...
byte = readb (&ehci->caps->portroute[(i>>1)]);
- sprintf(tmp, "%d ",
+ sprintf(tmp, "%d ",
((i & 0x1) ? ((byte)&0xf) : ((byte>>4)&0xf)));
strcat(buf, tmp);
}
@@ -141,12 +141,12 @@ dbg_qh (const char *label, struct ehci_hcd *ehci, struct ehci_qh *qh)
}
static void __attribute__((__unused__))
-dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
+dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
{
ehci_dbg (ehci, "%s [%d] itd %p, next %08x, urb %p\n",
label, itd->frame, itd, le32_to_cpu(itd->hw_next), itd->urb);
ehci_dbg (ehci,
- " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
+ " trans: %08x %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_transaction[0]),
le32_to_cpu(itd->hw_transaction[1]),
le32_to_cpu(itd->hw_transaction[2]),
@@ -156,7 +156,7 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
le32_to_cpu(itd->hw_transaction[6]),
le32_to_cpu(itd->hw_transaction[7]));
ehci_dbg (ehci,
- " buf: %08x %08x %08x %08x %08x %08x %08x\n",
+ " buf: %08x %08x %08x %08x %08x %08x %08x\n",
le32_to_cpu(itd->hw_bufp[0]),
le32_to_cpu(itd->hw_bufp[1]),
le32_to_cpu(itd->hw_bufp[2]),
@@ -171,12 +171,12 @@ dbg_itd (const char *label, struct ehci_hcd *ehci, struct ehci_itd *itd)
}
static void __attribute__((__unused__))
-dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
+dbg_sitd (const char *label, struct ehci_hcd *ehci, struct ehci_sitd *sitd)
{
ehci_dbg (ehci, "%s [%d] sitd %p, next %08x, urb %p\n",
label, sitd->frame, sitd, le32_to_cpu(sitd->hw_next), sitd->urb);
ehci_dbg (ehci,
- " addr %08x sched %04x result %08x buf %08x %08x\n",
+ " addr %08x sched %04x result %08x buf %08x %08x\n",
le32_to_cpu(sitd->hw_fullspeed_ep),
le32_to_cpu(sitd->hw_uframe),
le32_to_cpu(sitd->hw_results),
@@ -451,7 +451,7 @@ show_async (struct class_device *class_dev, char *buf)
*buf = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -497,7 +497,7 @@ show_periodic (struct class_device *class_dev, char *buf)
seen_count = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -634,7 +634,7 @@ show_registers (struct class_device *class_dev, char *buf)
static char label [] = "";
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ehci = hcd_to_ehci (hcd);
next = buf;
size = PAGE_SIZE;
@@ -754,9 +754,7 @@ show_registers (struct class_device *class_dev, char *buf)
}
if (ehci->reclaim) {
- temp = scnprintf (next, size, "reclaim qh %p%s\n",
- ehci->reclaim,
- ehci->reclaim_ready ? " ready" : "");
+ temp = scnprintf (next, size, "reclaim qh %p\n", ehci->reclaim);
size -= temp;
next += temp;
}
@@ -785,10 +783,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ehci_hcd *ehci)
{
struct class_device *cldev = ehci_to_hcd(ehci)->self.class_dev;
+ int retval;
- class_device_create_file(cldev, &class_device_attr_async);
- class_device_create_file(cldev, &class_device_attr_periodic);
- class_device_create_file(cldev, &class_device_attr_registers);
+ retval = class_device_create_file(cldev, &class_device_attr_async);
+ retval = class_device_create_file(cldev, &class_device_attr_periodic);
+ retval = class_device_create_file(cldev, &class_device_attr_registers);
}
static inline void remove_debug_files (struct ehci_hcd *ehci)
diff --git a/drivers/usb/host/ehci-fsl.c b/drivers/usb/host/ehci-fsl.c
index d030516edfb..1a915e982c1 100644
--- a/drivers/usb/host/ehci-fsl.c
+++ b/drivers/usb/host/ehci-fsl.c
@@ -285,6 +285,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
.resume = ehci_bus_resume,
#endif
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -329,6 +330,7 @@ MODULE_ALIAS("fsl-ehci");
static struct platform_driver ehci_fsl_driver = {
.probe = ehci_fsl_drv_probe,
.remove = ehci_fsl_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
.driver = {
.name = "fsl-ehci",
},
diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c
index d63177a8eae..5ac91859113 100644
--- a/drivers/usb/host/ehci-hcd.c
+++ b/drivers/usb/host/ehci-hcd.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2000-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -70,7 +70,7 @@
* 2002-08-06 Handling for bulk and interrupt transfers is mostly shared;
* only scheduling is different, no arbitrary limitations.
* 2002-07-25 Sanity check PCI reads, mostly for better cardbus support,
- * clean up HC run state handshaking.
+ * clean up HC run state handshaking.
* 2002-05-24 Preliminary FS/LS interrupts, using scheduling shortcuts
* 2002-05-11 Clear TT errors for FS/LS ctrl/bulk. Fill in some other
* missing pieces: enabling 64bit dma, handoff from BIOS/SMM.
@@ -111,7 +111,7 @@ static const char hcd_name [] = "ehci_hcd";
#define EHCI_TUNE_MULT_TT 1
#define EHCI_TUNE_FLS 2 /* (small) 256 frame schedule */
-#define EHCI_IAA_JIFFIES (HZ/100) /* arbitrary; ~10 msec */
+#define EHCI_IAA_MSECS 10 /* arbitrary */
#define EHCI_IO_JIFFIES (HZ/10) /* io watchdog > irq_thresh */
#define EHCI_ASYNC_JIFFIES (HZ/20) /* async idle timeout */
#define EHCI_SHRINK_JIFFIES (HZ/200) /* async qh unlink delay */
@@ -254,6 +254,7 @@ static void ehci_quiesce (struct ehci_hcd *ehci)
/*-------------------------------------------------------------------------*/
+static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs);
static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
#include "ehci-hub.c"
@@ -263,28 +264,39 @@ static void ehci_work(struct ehci_hcd *ehci, struct pt_regs *regs);
/*-------------------------------------------------------------------------*/
-static void ehci_watchdog (unsigned long param)
+static void ehci_iaa_watchdog (unsigned long param)
{
struct ehci_hcd *ehci = (struct ehci_hcd *) param;
unsigned long flags;
+ u32 status;
spin_lock_irqsave (&ehci->lock, flags);
+ WARN_ON(!ehci->reclaim);
- /* lost IAA irqs wedge things badly; seen with a vt8235 */
+ /* lost IAA irqs wedge things badly; seen first with a vt8235 */
if (ehci->reclaim) {
- u32 status = readl (&ehci->regs->status);
-
+ status = readl (&ehci->regs->status);
if (status & STS_IAA) {
ehci_vdbg (ehci, "lost IAA\n");
COUNT (ehci->stats.lost_iaa);
writel (STS_IAA, &ehci->regs->status);
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
}
}
- /* stop async processing after it's idled a bit */
+ spin_unlock_irqrestore (&ehci->lock, flags);
+}
+
+static void ehci_watchdog (unsigned long param)
+{
+ struct ehci_hcd *ehci = (struct ehci_hcd *) param;
+ unsigned long flags;
+
+ spin_lock_irqsave (&ehci->lock, flags);
+
+ /* stop async processing after it's idled a bit */
if (test_bit (TIMER_ASYNC_OFF, &ehci->actions))
- start_unlink_async (ehci, ehci->async);
+ start_unlink_async (ehci, ehci->async);
/* ehci could run by timer, without IRQs ... */
ehci_work (ehci, NULL);
@@ -292,21 +304,20 @@ static void ehci_watchdog (unsigned long param)
spin_unlock_irqrestore (&ehci->lock, flags);
}
-/* Reboot notifiers kick in for silicon on any bus (not just pci, etc).
+/* ehci_shutdown kick in for silicon on any bus (not just pci, etc).
* This forcibly disables dma and IRQs, helping kexec and other cases
* where the next system software may expect clean state.
*/
-static int
-ehci_reboot (struct notifier_block *self, unsigned long code, void *null)
+static void
+ehci_shutdown (struct usb_hcd *hcd)
{
- struct ehci_hcd *ehci;
+ struct ehci_hcd *ehci;
- ehci = container_of (self, struct ehci_hcd, reboot_notifier);
+ ehci = hcd_to_ehci (hcd);
(void) ehci_halt (ehci);
/* make BIOS/etc use companion controller during reboot */
writel (0, &ehci->regs->configured_flag);
- return 0;
}
static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
@@ -334,8 +345,6 @@ static void ehci_port_power (struct ehci_hcd *ehci, int is_on)
static void ehci_work (struct ehci_hcd *ehci, struct pt_regs *regs)
{
timer_action_done (ehci, TIMER_IO_WATCHDOG);
- if (ehci->reclaim_ready)
- end_unlink_async (ehci, regs);
/* another CPU may drop ehci->lock during a schedule scan while
* it reports urb completions. this flag guards against bogus
@@ -370,6 +379,7 @@ static void ehci_stop (struct usb_hcd *hcd)
/* no more interrupts ... */
del_timer_sync (&ehci->watchdog);
+ del_timer_sync (&ehci->iaa_watchdog);
spin_lock_irq(&ehci->lock);
if (HC_IS_RUNNING (hcd->state))
@@ -381,7 +391,6 @@ static void ehci_stop (struct usb_hcd *hcd)
/* let companion controllers work when we aren't */
writel (0, &ehci->regs->configured_flag);
- unregister_reboot_notifier (&ehci->reboot_notifier);
remove_debug_files (ehci);
@@ -417,6 +426,10 @@ static int ehci_init(struct usb_hcd *hcd)
ehci->watchdog.function = ehci_watchdog;
ehci->watchdog.data = (unsigned long) ehci;
+ init_timer(&ehci->iaa_watchdog);
+ ehci->iaa_watchdog.function = ehci_iaa_watchdog;
+ ehci->iaa_watchdog.data = (unsigned long) ehci;
+
/*
* hw default: 1K periodic list heads, one per frame.
* periodic_size can shrink by USBCMD update if hcc_params allows.
@@ -427,13 +440,12 @@ static int ehci_init(struct usb_hcd *hcd)
/* controllers may cache some of the periodic schedule ... */
hcc_params = readl(&ehci->caps->hcc_params);
- if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
+ if (HCC_ISOC_CACHE(hcc_params)) // full frame cache
ehci->i_thresh = 8;
else // N microframes cached
ehci->i_thresh = 2 + HCC_ISOC_THRES(hcc_params);
ehci->reclaim = NULL;
- ehci->reclaim_ready = 0;
ehci->next_uframe = -1;
/*
@@ -483,9 +495,6 @@ static int ehci_init(struct usb_hcd *hcd)
}
ehci->command = temp;
- ehci->reboot_notifier.notifier_call = ehci_reboot;
- register_reboot_notifier(&ehci->reboot_notifier);
-
return 0;
}
@@ -499,7 +508,6 @@ static int ehci_run (struct usb_hcd *hcd)
/* EHCI spec section 4.1 */
if ((retval = ehci_reset(ehci)) != 0) {
- unregister_reboot_notifier(&ehci->reboot_notifier);
ehci_mem_cleanup(ehci);
return retval;
}
@@ -611,7 +619,7 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd, struct pt_regs *regs)
/* complete the unlinking of some qh [4.15.2.3] */
if (status & STS_IAA) {
COUNT (ehci->stats.reclaim);
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, regs);
bh = 1;
}
@@ -715,10 +723,14 @@ static int ehci_urb_enqueue (
static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- /* if we need to use IAA and it's busy, defer */
- if (qh->qh_state == QH_STATE_LINKED
- && ehci->reclaim
- && HC_IS_RUNNING (ehci_to_hcd(ehci)->state)) {
+ // BUG_ON(qh->qh_state != QH_STATE_LINKED);
+
+ /* failfast */
+ if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state))
+ end_unlink_async (ehci, NULL);
+
+ /* defer till later if busy */
+ else if (ehci->reclaim) {
struct ehci_qh *last;
for (last = ehci->reclaim;
@@ -728,12 +740,8 @@ static void unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
qh->qh_state = QH_STATE_UNLINK_WAIT;
last->reclaim = qh;
- /* bypass IAA if the hc can't care */
- } else if (!HC_IS_RUNNING (ehci_to_hcd(ehci)->state) && ehci->reclaim)
- end_unlink_async (ehci, NULL);
-
- /* something else might have unlinked the qh by now */
- if (qh->qh_state == QH_STATE_LINKED)
+ /* start IAA cycle */
+ } else
start_unlink_async (ehci, qh);
}
@@ -755,7 +763,19 @@ static int ehci_urb_dequeue (struct usb_hcd *hcd, struct urb *urb)
qh = (struct ehci_qh *) urb->hcpriv;
if (!qh)
break;
- unlink_async (ehci, qh);
+ switch (qh->qh_state) {
+ case QH_STATE_LINKED:
+ case QH_STATE_COMPLETING:
+ unlink_async (ehci, qh);
+ break;
+ case QH_STATE_UNLINK:
+ case QH_STATE_UNLINK_WAIT:
+ /* already started */
+ break;
+ case QH_STATE_IDLE:
+ WARN_ON(1);
+ break;
+ }
break;
case PIPE_INTERRUPT:
@@ -847,6 +867,7 @@ rescan:
unlink_async (ehci, qh);
/* FALL THROUGH */
case QH_STATE_UNLINK: /* wait for hw to finish? */
+ case QH_STATE_UNLINK_WAIT:
idle_timeout:
spin_unlock_irqrestore (&ehci->lock, flags);
schedule_timeout_uninterruptible(1);
diff --git a/drivers/usb/host/ehci-hub.c b/drivers/usb/host/ehci-hub.c
index d03e3cad5ca..b2ee13c5851 100644
--- a/drivers/usb/host/ehci-hub.c
+++ b/drivers/usb/host/ehci-hub.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -48,7 +48,7 @@ static int ehci_bus_suspend (struct usb_hcd *hcd)
}
ehci->command = readl (&ehci->regs->command);
if (ehci->reclaim)
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);
/* suspend any active/unsuspended ports, maybe allow wakeup */
@@ -103,10 +103,10 @@ static int ehci_bus_resume (struct usb_hcd *hcd)
/* re-init operational registers in case we lost power */
if (readl (&ehci->regs->intr_enable) == 0) {
- /* at least some APM implementations will try to deliver
+ /* at least some APM implementations will try to deliver
* IRQs right away, so delay them until we're ready.
- */
- intr_enable = 1;
+ */
+ intr_enable = 1;
writel (0, &ehci->regs->segment);
writel (ehci->periodic_dma, &ehci->regs->frame_list);
writel ((u32)ehci->async->qh_dma, &ehci->regs->async_next);
@@ -232,7 +232,7 @@ ehci_hub_status_data (struct usb_hcd *hcd, char *buf)
buf [1] = 0;
retval++;
}
-
+
/* no hub change reports (bit 0) for now (power, ...) */
/* port N changes (bit N)? */
@@ -304,7 +304,7 @@ ehci_hub_descriptor (
/*-------------------------------------------------------------------------*/
-#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
+#define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
static int ehci_hub_control (
struct usb_hcd *hcd,
diff --git a/drivers/usb/host/ehci-mem.c b/drivers/usb/host/ehci-mem.c
index 766061e0260..a8ba2e1497a 100644
--- a/drivers/usb/host/ehci-mem.c
+++ b/drivers/usb/host/ehci-mem.c
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -25,7 +25,7 @@
* - data used only by the HCD ... kmalloc is fine
* - async and periodic schedules, shared by HC and HCD ... these
* need to use dma_pool or dma_alloc_coherent
- * - driver buffers, read/written by HC ... single shot DMA mapped
+ * - driver buffers, read/written by HC ... single shot DMA mapped
*
* There's also PCI "register" data, which is memory mapped.
* No memory seen by this driver is pageable.
@@ -119,7 +119,7 @@ static inline void qh_put (struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
-/* The queue heads and transfer descriptors are managed from pools tied
+/* The queue heads and transfer descriptors are managed from pools tied
* to each of the "per device" structures.
* This is the initialisation and cleanup code.
*/
@@ -165,7 +165,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
int i;
/* QTDs for control/bulk/intr transfers */
- ehci->qtd_pool = dma_pool_create ("ehci_qtd",
+ ehci->qtd_pool = dma_pool_create ("ehci_qtd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qtd),
32 /* byte alignment (for hw parts) */,
@@ -175,7 +175,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* QHs for control/bulk/intr transfers */
- ehci->qh_pool = dma_pool_create ("ehci_qh",
+ ehci->qh_pool = dma_pool_create ("ehci_qh",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_qh),
32 /* byte alignment (for hw parts) */,
@@ -189,7 +189,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* ITD for high speed ISO transfers */
- ehci->itd_pool = dma_pool_create ("ehci_itd",
+ ehci->itd_pool = dma_pool_create ("ehci_itd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_itd),
32 /* byte alignment (for hw parts) */,
@@ -199,7 +199,7 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
}
/* SITD for full/low speed split ISO transfers */
- ehci->sitd_pool = dma_pool_create ("ehci_sitd",
+ ehci->sitd_pool = dma_pool_create ("ehci_sitd",
ehci_to_hcd(ehci)->self.controller,
sizeof (struct ehci_sitd),
32 /* byte alignment (for hw parts) */,
diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c
index cadffacd945..08d0472d4f5 100644
--- a/drivers/usb/host/ehci-pci.c
+++ b/drivers/usb/host/ehci-pci.c
@@ -238,6 +238,12 @@ static int ehci_pci_suspend(struct usb_hcd *hcd, pm_message_t message)
writel (0, &ehci->regs->intr_enable);
(void)readl(&ehci->regs->intr_enable);
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW) {
+ ehci_halt(ehci);
+ ehci_reset(ehci);
+ }
+
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -297,7 +303,7 @@ restart:
/* emptying the schedule aborts any urbs */
spin_lock_irq(&ehci->lock);
if (ehci->reclaim)
- ehci->reclaim_ready = 1;
+ end_unlink_async (ehci, NULL);
ehci_work(ehci, NULL);
spin_unlock_irq(&ehci->lock);
@@ -332,6 +338,7 @@ static const struct hc_driver ehci_pci_hc_driver = {
.resume = ehci_pci_resume,
#endif
.stop = ehci_stop,
+ .shutdown = ehci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -378,4 +385,5 @@ static struct pci_driver ehci_pci_driver = {
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
+ .shutdown = usb_hcd_pci_shutdown,
};
diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c
index e469221e7ec..7fc25b6bd7d 100644
--- a/drivers/usb/host/ehci-q.c
+++ b/drivers/usb/host/ehci-q.c
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2001-2004 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -31,7 +31,7 @@
* ISO traffic uses "ISO TD" (itd, and sitd) records, and (along with
* interrupts) needs careful scheduling. Performance improvements can be
* an ongoing challenge. That's in "ehci-sched.c".
- *
+ *
* USB 1.1 devices are handled (a) by "companion" OHCI or UHCI root hubs,
* or otherwise through transaction translators (TTs) in USB 2.0 hubs using
* (b) special fields in qh entries or (c) split iso entries. TTs will
@@ -199,7 +199,7 @@ static void qtd_copy_status (
&& ((token & QTD_STS_MMF) != 0
|| QTD_CERR(token) == 0)
&& (!ehci_is_TDI(ehci)
- || urb->dev->tt->hub !=
+ || urb->dev->tt->hub !=
ehci_to_hcd(ehci)->self.root_hub)) {
#ifdef DEBUG
struct usb_device *tt = urb->dev->tt->hub;
@@ -364,7 +364,7 @@ qh_completions (struct ehci_hcd *ehci, struct ehci_qh *qh, struct pt_regs *regs)
*/
if (likely (urb->status == -EINPROGRESS))
continue;
-
+
/* issue status after short control reads */
if (unlikely (do_status != 0)
&& QTD_PID (token) == 0 /* OUT */) {
@@ -388,7 +388,7 @@ halt:
wmb ();
}
}
-
+
/* remove it from the queue */
spin_lock (&urb->lock);
qtd_copy_status (ehci, urb, qtd->length, token);
@@ -518,7 +518,7 @@ qh_urb_transaction (
/* for zero length DATA stages, STATUS is always IN */
if (len == 0)
token |= (1 /* "in" */ << 8);
- }
+ }
/*
* data transfer stage: buffer setup
@@ -759,7 +759,7 @@ qh_make (
}
break;
default:
- dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
+ dbg ("bogus dev %p speed %d", urb->dev, urb->dev->speed);
done:
qh_put (qh);
return NULL;
@@ -967,17 +967,16 @@ static void end_unlink_async (struct ehci_hcd *ehci, struct pt_regs *regs)
struct ehci_qh *qh = ehci->reclaim;
struct ehci_qh *next;
- timer_action_done (ehci, TIMER_IAA_WATCHDOG);
+ iaa_watchdog_done (ehci);
// qh->hw_next = cpu_to_le32 (qh->qh_dma);
qh->qh_state = QH_STATE_IDLE;
qh->qh_next.qh = NULL;
- qh_put (qh); // refcount from reclaim
+ qh_put (qh); // refcount from reclaim
/* other unlink(s) may be pending (in QH_STATE_UNLINK_WAIT) */
next = qh->reclaim;
ehci->reclaim = next;
- ehci->reclaim_ready = 0;
qh->reclaim = NULL;
qh_completions (ehci, qh, regs);
@@ -1031,7 +1030,7 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
timer_action_done (ehci, TIMER_ASYNC_OFF);
}
return;
- }
+ }
qh->qh_state = QH_STATE_UNLINK;
ehci->reclaim = qh = qh_get (qh);
@@ -1046,17 +1045,16 @@ static void start_unlink_async (struct ehci_hcd *ehci, struct ehci_qh *qh)
if (unlikely (ehci_to_hcd(ehci)->state == HC_STATE_HALT)) {
/* if (unlikely (qh->reclaim != 0))
- * this will recurse, probably not much
+ * this will recurse, probably not much
*/
end_unlink_async (ehci, NULL);
return;
}
- ehci->reclaim_ready = 0;
cmd |= CMD_IAAD;
writel (cmd, &ehci->regs->command);
(void) readl (&ehci->regs->command);
- timer_action (ehci, TIMER_IAA_WATCHDOG);
+ iaa_watchdog_start (ehci);
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c
index 4859900bd13..e5e9c653c90 100644
--- a/drivers/usb/host/ehci-sched.c
+++ b/drivers/usb/host/ehci-sched.c
@@ -1,7 +1,7 @@
/*
* Copyright (c) 2001-2004 by David Brownell
* Copyright (c) 2003 Michal Sojka, for high-speed iso transfers
- *
+ *
* 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
@@ -613,7 +613,7 @@ static void intr_deschedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
/*-------------------------------------------------------------------------*/
static int check_period (
- struct ehci_hcd *ehci,
+ struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
unsigned period,
@@ -629,7 +629,7 @@ static int check_period (
/*
* 80% periodic == 100 usec/uframe available
- * convert "usecs we need" to "max already claimed"
+ * convert "usecs we need" to "max already claimed"
*/
usecs = 100 - usecs;
@@ -659,14 +659,14 @@ static int check_period (
}
static int check_intr_schedule (
- struct ehci_hcd *ehci,
+ struct ehci_hcd *ehci,
unsigned frame,
unsigned uframe,
const struct ehci_qh *qh,
__le32 *c_maskp
)
{
- int retval = -ENOSPC;
+ int retval = -ENOSPC;
u8 mask = 0;
if (qh->c_usecs && uframe >= 6) /* FSTN territory? */
@@ -701,7 +701,7 @@ static int check_intr_schedule (
/* Make sure this tt's buffer is also available for CSPLITs.
* We pessimize a bit; probably the typical full speed case
* doesn't need the second CSPLIT.
- *
+ *
* NOTE: both SPLIT and CSPLIT could be checked in just
* one smart pass...
*/
@@ -728,7 +728,7 @@ done:
*/
static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
{
- int status;
+ int status;
unsigned uframe;
__le32 c_mask;
unsigned frame; /* 0..(qh->period - 1), or NO_FRAME */
@@ -784,7 +784,7 @@ static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh)
ehci_dbg (ehci, "reused qh %p schedule\n", qh);
/* stuff into the periodic schedule */
- status = qh_link_periodic (ehci, qh);
+ status = qh_link_periodic (ehci, qh);
done:
return status;
}
@@ -1681,7 +1681,7 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
status = -ESHUTDOWN;
else
status = iso_stream_schedule (ehci, urb, stream);
- if (likely (status == 0))
+ if (likely (status == 0))
itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -1738,7 +1738,7 @@ sitd_sched_init (
if (packet->buf1 != (buf & ~(u64)0x0fff))
packet->cross = 1;
- /* OUT uses multiple start-splits */
+ /* OUT uses multiple start-splits */
if (stream->bEndpointAddress & USB_DIR_IN)
continue;
length = (length + 187) / 188;
@@ -1925,7 +1925,7 @@ sitd_link_urb (
/*-------------------------------------------------------------------------*/
#define SITD_ERRS (SITD_STS_ERR | SITD_STS_DBE | SITD_STS_BABBLE \
- | SITD_STS_XACT | SITD_STS_MMF)
+ | SITD_STS_XACT | SITD_STS_MMF)
static unsigned
sitd_complete (
@@ -2043,7 +2043,7 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
status = -ESHUTDOWN;
else
status = iso_stream_schedule (ehci, urb, stream);
- if (status == 0)
+ if (status == 0)
sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
spin_unlock_irqrestore (&ehci->lock, flags);
@@ -2226,5 +2226,5 @@ restart:
now_uframe++;
now_uframe %= mod;
}
- }
+ }
}
diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h
index 679c1cdcc91..6aac39f50e0 100644
--- a/drivers/usb/host/ehci.h
+++ b/drivers/usb/host/ehci.h
@@ -1,6 +1,6 @@
/*
* Copyright (c) 2001-2002 by David Brownell
- *
+ *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -58,7 +58,6 @@ struct ehci_hcd { /* one per controller */
/* async schedule support */
struct ehci_qh *async;
struct ehci_qh *reclaim;
- unsigned reclaim_ready : 1;
unsigned scanning : 1;
/* periodic schedule support */
@@ -81,8 +80,8 @@ struct ehci_hcd { /* one per controller */
struct dma_pool *itd_pool; /* itd per iso urb */
struct dma_pool *sitd_pool; /* sitd per split iso urb */
+ struct timer_list iaa_watchdog;
struct timer_list watchdog;
- struct notifier_block reboot_notifier;
unsigned long actions;
unsigned stamp;
unsigned long next_statechange;
@@ -104,7 +103,7 @@ struct ehci_hcd { /* one per controller */
#endif
};
-/* convert between an HCD pointer and the corresponding EHCI_HCD */
+/* convert between an HCD pointer and the corresponding EHCI_HCD */
static inline struct ehci_hcd *hcd_to_ehci (struct usb_hcd *hcd)
{
return (struct ehci_hcd *) (hcd->hcd_priv);
@@ -115,9 +114,21 @@ static inline struct usb_hcd *ehci_to_hcd (struct ehci_hcd *ehci)
}
+static inline void
+iaa_watchdog_start (struct ehci_hcd *ehci)
+{
+ WARN_ON(timer_pending(&ehci->iaa_watchdog));
+ mod_timer (&ehci->iaa_watchdog,
+ jiffies + msecs_to_jiffies(EHCI_IAA_MSECS));
+}
+
+static inline void iaa_watchdog_done (struct ehci_hcd *ehci)
+{
+ del_timer (&ehci->iaa_watchdog);
+}
+
enum ehci_timer_action {
TIMER_IO_WATCHDOG,
- TIMER_IAA_WATCHDOG,
TIMER_ASYNC_SHRINK,
TIMER_ASYNC_OFF,
};
@@ -135,9 +146,6 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
unsigned long t;
switch (action) {
- case TIMER_IAA_WATCHDOG:
- t = EHCI_IAA_JIFFIES;
- break;
case TIMER_IO_WATCHDOG:
t = EHCI_IO_JIFFIES;
break;
@@ -154,8 +162,7 @@ timer_action (struct ehci_hcd *ehci, enum ehci_timer_action action)
// async queue SHRINK often precedes IAA. while it's ready
// to go OFF neither can matter, and afterwards the IO
// watchdog stops unless there's still periodic traffic.
- if (action != TIMER_IAA_WATCHDOG
- && t > ehci->watchdog.expires
+ if (time_before_eq(t, ehci->watchdog.expires)
&& timer_pending (&ehci->watchdog))
return;
mod_timer (&ehci->watchdog, t);
@@ -179,8 +186,8 @@ struct ehci_caps {
#define HCS_INDICATOR(p) ((p)&(1 << 16)) /* true: has port indicators */
#define HCS_N_CC(p) (((p)>>12)&0xf) /* bits 15:12, #companion HCs */
#define HCS_N_PCC(p) (((p)>>8)&0xf) /* bits 11:8, ports per CC */
-#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
-#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
+#define HCS_PORTROUTED(p) ((p)&(1 << 7)) /* true: port routing */
+#define HCS_PPC(p) ((p)&(1 << 4)) /* true: port power control */
#define HCS_N_PORTS(p) (((p)>>0)&0xf) /* bits 3:0, ports on HC */
u32 hcc_params; /* HCCPARAMS - offset 0x8 */
@@ -205,7 +212,7 @@ struct ehci_regs {
#define CMD_LRESET (1<<7) /* partial reset (no ports, etc) */
#define CMD_IAAD (1<<6) /* "doorbell" interrupt async advance */
#define CMD_ASE (1<<5) /* async schedule enable */
-#define CMD_PSE (1<<4) /* periodic schedule enable */
+#define CMD_PSE (1<<4) /* periodic schedule enable */
/* 3:2 is periodic frame list size */
#define CMD_RESET (1<<1) /* reset HC not bus */
#define CMD_RUN (1<<0) /* start/stop HC */
@@ -231,9 +238,9 @@ struct ehci_regs {
/* FRINDEX: offset 0x0C */
u32 frame_index; /* current microframe number */
/* CTRLDSSEGMENT: offset 0x10 */
- u32 segment; /* address bits 63:32 if needed */
+ u32 segment; /* address bits 63:32 if needed */
/* PERIODICLISTBASE: offset 0x14 */
- u32 frame_list; /* points to periodic list */
+ u32 frame_list; /* points to periodic list */
/* ASYNCLISTADDR: offset 0x18 */
u32 async_next; /* address of next async queue head */
@@ -302,7 +309,7 @@ struct ehci_dbg_port {
/*
* EHCI Specification 0.95 Section 3.5
- * QTD: describe data transfer components (buffer, direction, ...)
+ * QTD: describe data transfer components (buffer, direction, ...)
* See Fig 3-6 "Queue Element Transfer Descriptor Block Diagram".
*
* These are associated only with "QH" (Queue Head) structures,
@@ -312,7 +319,7 @@ struct ehci_qtd {
/* first part defined by EHCI spec */
__le32 hw_next; /* see EHCI 3.5.1 */
__le32 hw_alt_next; /* see EHCI 3.5.2 */
- __le32 hw_token; /* see EHCI 3.5.3 */
+ __le32 hw_token; /* see EHCI 3.5.3 */
#define QTD_TOGGLE (1 << 31) /* data toggle */
#define QTD_LENGTH(tok) (((tok)>>16) & 0x7fff)
#define QTD_IOC (1 << 15) /* interrupt on complete */
@@ -349,8 +356,8 @@ struct ehci_qtd {
/* values for that type tag */
#define Q_TYPE_ITD __constant_cpu_to_le32 (0 << 1)
#define Q_TYPE_QH __constant_cpu_to_le32 (1 << 1)
-#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
-#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
+#define Q_TYPE_SITD __constant_cpu_to_le32 (2 << 1)
+#define Q_TYPE_FSTN __constant_cpu_to_le32 (3 << 1)
/* next async queue entry, or pointer to interrupt/periodic QH */
#define QH_NEXT(dma) (cpu_to_le32(((u32)dma)&~0x01f)|Q_TYPE_QH)
@@ -367,7 +374,7 @@ struct ehci_qtd {
* For entries in the async schedule, the type tag always says "qh".
*/
union ehci_shadow {
- struct ehci_qh *qh; /* Q_TYPE_QH */
+ struct ehci_qh *qh; /* Q_TYPE_QH */
struct ehci_itd *itd; /* Q_TYPE_ITD */
struct ehci_sitd *sitd; /* Q_TYPE_SITD */
struct ehci_fstn *fstn; /* Q_TYPE_FSTN */
@@ -397,7 +404,7 @@ struct ehci_qh {
#define QH_HUBPORT 0x3f800000
#define QH_MULT 0xc0000000
__le32 hw_current; /* qtd list - see EHCI 3.6.4 */
-
+
/* qtd overlay (hardware parts of a struct ehci_qtd) */
__le32 hw_qtd_next;
__le32 hw_alt_next;
@@ -472,7 +479,7 @@ struct ehci_iso_stream {
struct list_head td_list; /* queued itds/sitds */
struct list_head free_list; /* list of unused itds/sitds */
struct usb_device *udev;
- struct usb_host_endpoint *ep;
+ struct usb_host_endpoint *ep;
/* output of (re)scheduling */
unsigned long start; /* jiffies */
@@ -492,8 +499,8 @@ struct ehci_iso_stream {
unsigned bandwidth;
/* This is used to initialize iTD's hw_bufp fields */
- __le32 buf0;
- __le32 buf1;
+ __le32 buf0;
+ __le32 buf1;
__le32 buf2;
/* this is used to initialize sITD's tt info */
@@ -521,7 +528,7 @@ struct ehci_itd {
#define ITD_ACTIVE __constant_cpu_to_le32(EHCI_ISOC_ACTIVE)
- __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
+ __le32 hw_bufp [7]; /* see EHCI 3.3.3 */
__le32 hw_bufp_hi [7]; /* Appendix B */
/* the rest is HCD-private */
@@ -542,7 +549,7 @@ struct ehci_itd {
/*-------------------------------------------------------------------------*/
/*
- * EHCI Specification 0.95 Section 3.4
+ * EHCI Specification 0.95 Section 3.4
* siTD, aka split-transaction isochronous Transfer Descriptor
* ... describe full speed iso xfers through TT in hubs
* see Figure 3-5 "Split-transaction Isochronous Transaction Descriptor (siTD)
diff --git a/drivers/usb/host/isp116x-hcd.c b/drivers/usb/host/isp116x-hcd.c
index 5147ed4a666..a72e041df8e 100644
--- a/drivers/usb/host/isp116x-hcd.c
+++ b/drivers/usb/host/isp116x-hcd.c
@@ -1204,10 +1204,10 @@ static int isp116x_show_dbg(struct seq_file *s, void *unused)
static int isp116x_open_seq(struct inode *inode, struct file *file)
{
- return single_open(file, isp116x_show_dbg, inode->u.generic_ip);
+ return single_open(file, isp116x_show_dbg, inode->i_private);
}
-static struct file_operations isp116x_debug_fops = {
+static const struct file_operations isp116x_debug_fops = {
.open = isp116x_open_seq,
.read = seq_read,
.llseek = seq_lseek,
diff --git a/drivers/usb/host/isp116x.h b/drivers/usb/host/isp116x.h
index a1b7c3813d3..b91e2edd9c5 100644
--- a/drivers/usb/host/isp116x.h
+++ b/drivers/usb/host/isp116x.h
@@ -233,7 +233,7 @@ static const int cc_to_error[16] = {
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
+ /* DevNotResp */ -ETIME,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c
index 85cc059705a..b466581beb4 100644
--- a/drivers/usb/host/ohci-at91.c
+++ b/drivers/usb/host/ohci-at91.c
@@ -193,7 +193,7 @@ ohci_at91_start (struct usb_hcd *hcd)
if ((ret = ohci_init(ohci)) < 0)
return ret;
- root->maxchild = board->ports;
+ ohci->num_ports = board->ports;
if ((ret = ohci_run(ohci)) < 0) {
err("can't start %s", hcd->self.bus_name);
@@ -221,6 +221,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.start = ohci_at91_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -239,7 +240,7 @@ static const struct hc_driver ohci_at91_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
-
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -296,6 +297,7 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
if (!clocked) {
clk_enable(iclk);
clk_enable(fclk);
+ clocked = 1;
}
return 0;
@@ -310,6 +312,7 @@ MODULE_ALIAS("at91_ohci");
static struct platform_driver ohci_hcd_at91_driver = {
.probe = ohci_hcd_at91_drv_probe,
.remove = ohci_hcd_at91_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
.suspend = ohci_hcd_at91_drv_suspend,
.resume = ohci_hcd_at91_drv_resume,
.driver = {
diff --git a/drivers/usb/host/ohci-au1xxx.c b/drivers/usb/host/ohci-au1xxx.c
index f7a975d5db0..24e23c5783d 100644
--- a/drivers/usb/host/ohci-au1xxx.c
+++ b/drivers/usb/host/ohci-au1xxx.c
@@ -268,11 +268,8 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_au1xxx_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_au1xxx_suspend, -- tbd */
- /* resume: ohci_au1xxx_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -291,6 +288,7 @@ static const struct hc_driver ohci_au1xxx_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -338,6 +336,7 @@ static int ohci_hcd_au1xxx_drv_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_au1xxx_driver = {
.probe = ohci_hcd_au1xxx_drv_probe,
.remove = ohci_hcd_au1xxx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_au1xxx_drv_suspend, */
/*.resume = ohci_hcd_au1xxx_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-dbg.c b/drivers/usb/host/ohci-dbg.c
index 7bfffcbbd22..8293c1d4be3 100644
--- a/drivers/usb/host/ohci-dbg.c
+++ b/drivers/usb/host/ohci-dbg.c
@@ -477,7 +477,7 @@ show_async (struct class_device *class_dev, char *buf)
unsigned long flags;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
/* display control and bulk lists together, for simplicity */
@@ -510,7 +510,7 @@ show_periodic (struct class_device *class_dev, char *buf)
seen_count = 0;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
next = buf;
size = PAGE_SIZE;
@@ -607,7 +607,7 @@ show_registers (struct class_device *class_dev, char *buf)
u32 rdata;
bus = class_get_devdata(class_dev);
- hcd = bus->hcpriv;
+ hcd = bus_to_hcd(bus);
ohci = hcd_to_ohci(hcd);
regs = ohci->regs;
next = buf;
@@ -667,6 +667,11 @@ show_registers (struct class_device *class_dev, char *buf)
size -= temp;
next += temp;
+ temp = scnprintf (next, size, "hub poll timer %s\n",
+ ohci_to_hcd(ohci)->poll_rh ? "ON" : "off");
+ size -= temp;
+ next += temp;
+
/* roothub */
ohci_dump_roothub (ohci, 1, &next, &size);
@@ -680,10 +685,11 @@ static CLASS_DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
static inline void create_debug_files (struct ohci_hcd *ohci)
{
struct class_device *cldev = ohci_to_hcd(ohci)->self.class_dev;
+ int retval;
- class_device_create_file(cldev, &class_device_attr_async);
- class_device_create_file(cldev, &class_device_attr_periodic);
- class_device_create_file(cldev, &class_device_attr_registers);
+ retval = class_device_create_file(cldev, &class_device_attr_async);
+ retval = class_device_create_file(cldev, &class_device_attr_periodic);
+ retval = class_device_create_file(cldev, &class_device_attr_registers);
ohci_dbg (ohci, "created debug files\n");
}
diff --git a/drivers/usb/host/ohci-ep93xx.c b/drivers/usb/host/ohci-ep93xx.c
index 6531c4d2652..1bf5e7a4e73 100644
--- a/drivers/usb/host/ohci-ep93xx.c
+++ b/drivers/usb/host/ohci-ep93xx.c
@@ -128,12 +128,14 @@ static struct hc_driver ohci_ep93xx_hc_driver = {
.flags = HCD_USB11 | HCD_MEMORY,
.start = ohci_ep93xx_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
.urb_enqueue = ohci_urb_enqueue,
.urb_dequeue = ohci_urb_dequeue,
.endpoint_disable = ohci_endpoint_disable,
.get_frame_number = ohci_get_frame,
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -202,6 +204,7 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
static struct platform_driver ohci_hcd_ep93xx_driver = {
.probe = ohci_hcd_ep93xx_drv_probe,
.remove = ohci_hcd_ep93xx_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_hcd_ep93xx_drv_suspend,
.resume = ohci_hcd_ep93xx_drv_resume,
diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c
index 94d8cf4b36c..d1d68c40225 100644
--- a/drivers/usb/host/ohci-hcd.c
+++ b/drivers/usb/host/ohci-hcd.c
@@ -88,7 +88,7 @@
#include <linux/timer.h>
#include <linux/list.h>
#include <linux/usb.h>
-#include <linux/usb_otg.h>
+#include <linux/usb/otg.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
#include <linux/reboot.h>
@@ -101,7 +101,7 @@
#include "../core/hcd.h"
-#define DRIVER_VERSION "2005 April 22"
+#define DRIVER_VERSION "2006 August 04"
#define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell"
#define DRIVER_DESC "USB 1.1 'Open' Host Controller (OHCI) Driver"
@@ -110,9 +110,10 @@
#undef OHCI_VERBOSE_DEBUG /* not always helpful */
/* For initializing controller (mask in an HCFS mode too) */
-#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
#define OHCI_INTR_INIT \
- (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_WDH)
+ (OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE \
+ | OHCI_INTR_RD | OHCI_INTR_WDH)
#ifdef __hppa__
/* On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */
@@ -128,12 +129,13 @@
static const char hcd_name [] = "ohci_hcd";
+#define STATECHANGE_DELAY msecs_to_jiffies(300)
+
#include "ohci.h"
static void ohci_dump (struct ohci_hcd *ohci, int verbose);
static int ohci_init (struct ohci_hcd *ohci);
static void ohci_stop (struct usb_hcd *hcd);
-static int ohci_reboot (struct notifier_block *, unsigned long , void *);
#include "ohci-hub.c"
#include "ohci-dbg.c"
@@ -416,21 +418,20 @@ static void ohci_usb_reset (struct ohci_hcd *ohci)
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
}
-/* reboot notifier forcibly disables IRQs and DMA, helping kexec and
+/* ohci_shutdown forcibly disables IRQs and DMA, helping kexec and
* other cases where the next software may expect clean state from the
* "firmware". this is bus-neutral, unlike shutdown() methods.
*/
-static int
-ohci_reboot (struct notifier_block *block, unsigned long code, void *null)
+static void
+ohci_shutdown (struct usb_hcd *hcd)
{
struct ohci_hcd *ohci;
- ohci = container_of (block, struct ohci_hcd, reboot_notifier);
+ ohci = hcd_to_ohci (hcd);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
ohci_usb_reset (ohci);
/* flush the writes */
(void) ohci_readl (ohci, &ohci->regs->control);
- return 0;
}
/*-------------------------------------------------------------------------*
@@ -446,7 +447,6 @@ static int ohci_init (struct ohci_hcd *ohci)
disable (ohci);
ohci->regs = hcd->regs;
- ohci->next_statechange = jiffies;
/* REVISIT this BIOS handshake is now moved into PCI "quirks", and
* was never needed for most non-PCI systems ... remove the code?
@@ -502,7 +502,6 @@ static int ohci_init (struct ohci_hcd *ohci)
if ((ret = ohci_mem_init (ohci)) < 0)
ohci_stop (hcd);
else {
- register_reboot_notifier (&ohci->reboot_notifier);
create_debug_files (ohci);
}
@@ -637,10 +636,14 @@ retry:
return -EOVERFLOW;
}
- /* start controller operations */
+ /* use rhsc irqs after khubd is fully initialized */
+ hcd->poll_rh = 1;
+ hcd->uses_new_polling = 1;
+
+ /* start controller operations */
ohci->hc_control &= OHCI_CTRL_RWC;
- ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
- ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
+ ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER;
+ ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
hcd->state = HC_STATE_RUNNING;
/* wake on ConnectStatusChange, matching external hubs */
@@ -648,7 +651,7 @@ retry:
/* Choose the interrupts we care about now, others later on demand */
mask = OHCI_INTR_INIT;
- ohci_writel (ohci, mask, &ohci->regs->intrstatus);
+ ohci_writel (ohci, ~0, &ohci->regs->intrstatus);
ohci_writel (ohci, mask, &ohci->regs->intrenable);
/* handle root hub init quirks ... */
@@ -672,6 +675,7 @@ retry:
// flush those writes
(void) ohci_readl (ohci, &ohci->regs->control);
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
spin_unlock_irq (&ohci->lock);
// POTPGT delay is bits 24-31, in 2 ms units.
@@ -709,7 +713,14 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
/* interrupt for some other device? */
} else if ((ints &= ohci_readl (ohci, &regs->intrenable)) == 0) {
return IRQ_NOTMINE;
- }
+ }
+
+ if (ints & OHCI_INTR_RHSC) {
+ ohci_vdbg (ohci, "rhsc\n");
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
+ ohci_writel (ohci, OHCI_INTR_RHSC, &regs->intrstatus);
+ usb_hcd_poll_rh_status(hcd);
+ }
if (ints & OHCI_INTR_UE) {
disable (ohci);
@@ -723,13 +734,18 @@ static irqreturn_t ohci_irq (struct usb_hcd *hcd, struct pt_regs *ptregs)
if (ints & OHCI_INTR_RD) {
ohci_vdbg (ohci, "resume detect\n");
ohci_writel (ohci, OHCI_INTR_RD, &regs->intrstatus);
- if (hcd->state != HC_STATE_QUIESCING)
+ hcd->poll_rh = 1;
+ if (ohci->autostop) {
+ spin_lock (&ohci->lock);
+ ohci_rh_resume (ohci);
+ spin_unlock (&ohci->lock);
+ } else
usb_hcd_resume_root_hub(hcd);
}
if (ints & OHCI_INTR_WDH) {
if (HC_IS_RUNNING(hcd->state))
- ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
+ ohci_writel (ohci, OHCI_INTR_WDH, &regs->intrdisable);
spin_lock (&ohci->lock);
dl_done_list (ohci, ptregs);
spin_unlock (&ohci->lock);
@@ -775,9 +791,10 @@ static void ohci_stop (struct usb_hcd *hcd)
ohci_usb_reset (ohci);
ohci_writel (ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
-
+ free_irq(hcd->irq, hcd);
+ hcd->irq = -1;
+
remove_debug_files (ohci);
- unregister_reboot_notifier (&ohci->reboot_notifier);
ohci_mem_cleanup (ohci);
if (ohci->hcca) {
dma_free_coherent (hcd->self.controller,
@@ -917,6 +934,10 @@ MODULE_LICENSE ("GPL");
#include "ohci-at91.c"
#endif
+#ifdef CONFIG_ARCH_PNX4008
+#include "ohci-pnx4008.c"
+#endif
+
#if !(defined(CONFIG_PCI) \
|| defined(CONFIG_SA1111) \
|| defined(CONFIG_ARCH_S3C2410) \
@@ -928,6 +949,7 @@ MODULE_LICENSE ("GPL");
|| defined (CONFIG_USB_OHCI_HCD_PPC_SOC) \
|| defined (CONFIG_ARCH_AT91RM9200) \
|| defined (CONFIG_ARCH_AT91SAM9261) \
+ || defined (CONFIG_ARCH_PNX4008) \
)
#error "missing bus glue for ohci-hcd"
#endif
diff --git a/drivers/usb/host/ohci-hub.c b/drivers/usb/host/ohci-hub.c
index 5b0a23fd798..ec75774abea 100644
--- a/drivers/usb/host/ohci-hub.c
+++ b/drivers/usb/host/ohci-hub.c
@@ -36,27 +36,25 @@
/*-------------------------------------------------------------------------*/
-#ifdef CONFIG_PM
+/* hcd->hub_irq_enable() */
+static void ohci_rhsc_enable (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrenable);
+}
#define OHCI_SCHED_ENABLES \
(OHCI_CTRL_CLE|OHCI_CTRL_BLE|OHCI_CTRL_PLE|OHCI_CTRL_IE)
static void dl_done_list (struct ohci_hcd *, struct pt_regs *);
static void finish_unlinks (struct ohci_hcd *, u16 , struct pt_regs *);
-static int ohci_restart (struct ohci_hcd *ohci);
-static int ohci_bus_suspend (struct usb_hcd *hcd)
+static int ohci_rh_suspend (struct ohci_hcd *ohci, int autostop)
+__releases(ohci->lock)
+__acquires(ohci->lock)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int status = 0;
- unsigned long flags;
-
- spin_lock_irqsave (&ohci->lock, flags);
-
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
- spin_unlock_irqrestore (&ohci->lock, flags);
- return -ESHUTDOWN;
- }
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
switch (ohci->hc_control & OHCI_CTRL_HCFS) {
@@ -72,15 +70,16 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
ohci_dbg (ohci, "needs reinit!\n");
goto done;
case OHCI_USB_SUSPEND:
- ohci_dbg (ohci, "already suspended\n");
- goto done;
+ if (!ohci->autostop) {
+ ohci_dbg (ohci, "already suspended\n");
+ goto done;
+ }
}
- ohci_dbg (ohci, "suspend root hub\n");
+ ohci_dbg (ohci, "%s root hub\n",
+ autostop ? "auto-stop" : "suspend");
/* First stop any processing */
- if (ohci->hc_control & OHCI_SCHED_ENABLES) {
- int limit;
-
+ if (!autostop && (ohci->hc_control & OHCI_SCHED_ENABLES)) {
ohci->hc_control &= ~OHCI_SCHED_ENABLES;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
@@ -90,27 +89,22 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
* then the last WDH could take 6+ msec
*/
ohci_dbg (ohci, "stopping schedules ...\n");
- limit = 2000;
- while (limit > 0) {
- udelay (250);
- limit =- 250;
- if (ohci_readl (ohci, &ohci->regs->intrstatus)
- & OHCI_INTR_SF)
- break;
- }
- dl_done_list (ohci, NULL);
- mdelay (7);
+ ohci->autostop = 0;
+ spin_unlock_irq (&ohci->lock);
+ msleep (8);
+ spin_lock_irq (&ohci->lock);
}
dl_done_list (ohci, NULL);
finish_unlinks (ohci, ohci_frame_no(ohci), NULL);
- ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
- &ohci->regs->intrstatus);
/* maybe resume can wake root hub */
- if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev))
+ if (device_may_wakeup(&ohci_to_hcd(ohci)->self.root_hub->dev) ||
+ autostop)
ohci->hc_control |= OHCI_CTRL_RWE;
- else
+ else {
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
ohci->hc_control &= ~OHCI_CTRL_RWE;
+ }
/* Suspend hub ... this is the "global (to this bus) suspend" mode,
* which doesn't imply ports will first be individually suspended.
@@ -121,13 +115,12 @@ static int ohci_bus_suspend (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control);
/* no resumes until devices finish suspending */
- ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ if (!autostop) {
+ ohci->next_statechange = jiffies + msecs_to_jiffies (5);
+ ohci->autostop = 0;
+ }
done:
- /* external suspend vs self autosuspend ... same effect */
- if (status == 0)
- usb_hcd_suspend_root_hub(hcd);
- spin_unlock_irqrestore (&ohci->lock, flags);
return status;
}
@@ -139,25 +132,19 @@ static inline struct ed *find_head (struct ed *ed)
return ed;
}
+static int ohci_restart (struct ohci_hcd *ohci);
+
/* caller has locked the root hub */
-static int ohci_bus_resume (struct usb_hcd *hcd)
+static int ohci_rh_resume (struct ohci_hcd *ohci)
+__releases(ohci->lock)
+__acquires(ohci->lock)
{
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ struct usb_hcd *hcd = ohci_to_hcd (ohci);
u32 temp, enables;
int status = -EINPROGRESS;
- unsigned long flags;
-
- if (time_before (jiffies, ohci->next_statechange))
- msleep(5);
-
- spin_lock_irqsave (&ohci->lock, flags);
-
- if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags))) {
- spin_unlock_irqrestore (&ohci->lock, flags);
- return -ESHUTDOWN;
- }
-
+ int autostopped = ohci->autostop;
+ ohci->autostop = 0;
ohci->hc_control = ohci_readl (ohci, &ohci->regs->control);
if (ohci->hc_control & (OHCI_CTRL_IR | OHCI_SCHED_ENABLES)) {
@@ -177,7 +164,8 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
ohci->hc_control |= OHCI_USB_RESUME;
ohci_writel (ohci, ohci->hc_control, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- ohci_dbg (ohci, "resume root hub\n");
+ ohci_dbg (ohci, "%s root hub\n",
+ autostopped ? "auto-start" : "resume");
break;
case OHCI_USB_RESUME:
/* HCFS changes sometime after INTR_RD */
@@ -192,16 +180,24 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
ohci_dbg (ohci, "lost power\n");
status = -EBUSY;
}
- spin_unlock_irqrestore (&ohci->lock, flags);
+#ifdef CONFIG_PM
if (status == -EBUSY) {
- (void) ohci_init (ohci);
- return ohci_restart (ohci);
+ if (!autostopped) {
+ spin_unlock_irq (&ohci->lock);
+ (void) ohci_init (ohci);
+ status = ohci_restart (ohci);
+ spin_lock_irq (&ohci->lock);
+ }
+ return status;
}
+#endif
if (status != -EINPROGRESS)
return status;
+ if (autostopped)
+ goto skip_resume;
+ spin_unlock_irq (&ohci->lock);
temp = ohci->num_ports;
- enables = 0;
while (temp--) {
u32 stat = ohci_readl (ohci,
&ohci->regs->roothub.portstatus [temp]);
@@ -234,17 +230,21 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
/* Sometimes PCI D3 suspend trashes frame timings ... */
periodic_reinit (ohci);
+ /* the following code is executed with ohci->lock held and
+ * irqs disabled if and only if autostopped is true
+ */
+
+skip_resume:
/* interrupts might have been disabled */
ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
if (ohci->ed_rm_list)
ohci_writel (ohci, OHCI_INTR_SF, &ohci->regs->intrenable);
- ohci_writel (ohci, ohci_readl (ohci, &ohci->regs->intrstatus),
- &ohci->regs->intrstatus);
/* Then re-enable operations */
ohci_writel (ohci, OHCI_USB_OPER, &ohci->regs->control);
(void) ohci_readl (ohci, &ohci->regs->control);
- msleep (3);
+ if (!autostopped)
+ msleep (3);
temp = ohci->hc_control;
temp &= OHCI_CTRL_RWC;
@@ -254,10 +254,14 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
(void) ohci_readl (ohci, &ohci->regs->control);
/* TRSMRCY */
- msleep (10);
+ if (!autostopped) {
+ msleep (10);
+ spin_lock_irq (&ohci->lock);
+ }
+ /* now ohci->lock is always held and irqs are always disabled */
- /* keep it alive for ~5x suspend + resume costs */
- ohci->next_statechange = jiffies + msecs_to_jiffies (250);
+ /* keep it alive for more than ~5x suspend + resume costs */
+ ohci->next_statechange = jiffies + STATECHANGE_DELAY;
/* maybe turn schedules back on */
enables = 0;
@@ -291,6 +295,45 @@ static int ohci_bus_resume (struct usb_hcd *hcd)
return 0;
}
+#ifdef CONFIG_PM
+
+static int ohci_bus_suspend (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_suspend (ohci, 0);
+ spin_unlock_irq (&ohci->lock);
+ return rc;
+}
+
+static int ohci_bus_resume (struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+ int rc;
+
+ if (time_before (jiffies, ohci->next_statechange))
+ msleep(5);
+
+ spin_lock_irq (&ohci->lock);
+
+ if (unlikely(!test_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags)))
+ rc = -ESHUTDOWN;
+ else
+ rc = ohci_rh_resume (ohci);
+ spin_unlock_irq (&ohci->lock);
+
+ /* poll until we know a device is connected or we autostop */
+ if (rc == 0)
+ usb_hcd_poll_rh_status(hcd);
+ return rc;
+}
+
#endif /* CONFIG_PM */
/*-------------------------------------------------------------------------*/
@@ -302,20 +345,11 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
{
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int i, changed = 0, length = 1;
- int can_suspend = device_may_wakeup(&hcd->self.root_hub->dev);
+ int any_connected = 0, rhsc_enabled = 1;
unsigned long flags;
spin_lock_irqsave (&ohci->lock, flags);
- /* handle autosuspended root: finish resuming before
- * letting khubd or root hub timer see state changes.
- */
- if (unlikely((ohci->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_OPER
- || !HC_IS_RUNNING(hcd->state))) {
- can_suspend = 0;
- goto done;
- }
-
/* undocumented erratum seen on at least rev D */
if ((ohci->flags & OHCI_QUIRK_AMD756)
&& (roothub_a (ohci) & RH_A_NDP) > MAX_ROOT_PORTS) {
@@ -339,6 +373,9 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
for (i = 0; i < ohci->num_ports; i++) {
u32 status = roothub_portstatus (ohci, i);
+ /* can't autostop if ports are connected */
+ any_connected |= (status & RH_PS_CCS);
+
if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC
| RH_PS_OCIC | RH_PS_PRSC)) {
changed = 1;
@@ -346,39 +383,72 @@ ohci_hub_status_data (struct usb_hcd *hcd, char *buf)
buf [0] |= 1 << (i + 1);
else
buf [1] |= 1 << (i - 7);
- continue;
}
-
- /* can suspend if no ports are enabled; or if all all
- * enabled ports are suspended AND remote wakeup is on.
- */
- if (!(status & RH_PS_CCS))
- continue;
- if ((status & RH_PS_PSS) && can_suspend)
- continue;
- can_suspend = 0;
}
-done:
- spin_unlock_irqrestore (&ohci->lock, flags);
-#ifdef CONFIG_PM
- /* save power by suspending idle root hubs;
- * INTR_RD wakes us when there's work
+ /* NOTE: vendors didn't always make the same implementation
+ * choices for RHSC. Sometimes it triggers on an edge (like
+ * setting and maybe clearing a port status change bit); and
+ * it's level-triggered on other silicon, active until khubd
+ * clears all active port status change bits. If it's still
+ * set (level-triggered) we must disable it and rely on
+ * polling until khubd re-enables it.
*/
- if (can_suspend
- && !changed
- && !ohci->ed_rm_list
- && ((OHCI_CTRL_HCFS | OHCI_SCHED_ENABLES)
- & ohci->hc_control)
- == OHCI_USB_OPER
- && time_after (jiffies, ohci->next_statechange)
- && usb_trylock_device (hcd->self.root_hub) == 0
- ) {
- ohci_vdbg (ohci, "autosuspend\n");
- (void) ohci_bus_suspend (hcd);
- usb_unlock_device (hcd->self.root_hub);
+ if (ohci_readl (ohci, &ohci->regs->intrstatus) & OHCI_INTR_RHSC) {
+ ohci_writel (ohci, OHCI_INTR_RHSC, &ohci->regs->intrdisable);
+ (void) ohci_readl (ohci, &ohci->regs->intrdisable);
+ rhsc_enabled = 0;
}
-#endif
+ hcd->poll_rh = 1;
+
+ /* carry out appropriate state changes */
+ switch (ohci->hc_control & OHCI_CTRL_HCFS) {
+
+ case OHCI_USB_OPER:
+ /* keep on polling until we know a device is connected
+ * and RHSC is enabled */
+ if (!ohci->autostop) {
+ if (any_connected) {
+ if (rhsc_enabled)
+ hcd->poll_rh = 0;
+ } else {
+ ohci->autostop = 1;
+ ohci->next_statechange = jiffies + HZ;
+ }
+
+ /* if no devices have been attached for one second, autostop */
+ } else {
+ if (changed || any_connected) {
+ ohci->autostop = 0;
+ ohci->next_statechange = jiffies +
+ STATECHANGE_DELAY;
+ } else if (time_after_eq (jiffies,
+ ohci->next_statechange)
+ && !ohci->ed_rm_list
+ && !(ohci->hc_control &
+ OHCI_SCHED_ENABLES)) {
+ ohci_rh_suspend (ohci, 1);
+ }
+ }
+ break;
+
+ /* if there is a port change, autostart or ask to be resumed */
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ if (changed) {
+ if (ohci->autostop)
+ ohci_rh_resume (ohci);
+ else
+ usb_hcd_resume_root_hub (hcd);
+ } else {
+ /* everything is idle, no need for polling */
+ hcd->poll_rh = 0;
+ }
+ break;
+ }
+
+done:
+ spin_unlock_irqrestore (&ohci->lock, flags);
return changed ? length : 0;
}
@@ -550,9 +620,6 @@ static int ohci_hub_control (
break;
case USB_PORT_FEAT_SUSPEND:
temp = RH_PS_POCI;
- if ((ohci->hc_control & OHCI_CTRL_HCFS)
- != OHCI_USB_OPER)
- usb_hcd_resume_root_hub(hcd);
break;
case USB_PORT_FEAT_C_SUSPEND:
temp = RH_PS_PSSC;
diff --git a/drivers/usb/host/ohci-lh7a404.c b/drivers/usb/host/ohci-lh7a404.c
index 5602da9bd52..e121d97ed91 100644
--- a/drivers/usb/host/ohci-lh7a404.c
+++ b/drivers/usb/host/ohci-lh7a404.c
@@ -173,11 +173,8 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_lh7a404_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_lh7a404_suspend, -- tbd */
- /* resume: ohci_lh7a404_resume, -- tbd */
-#endif /*CONFIG_PM*/
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -196,6 +193,7 @@ static const struct hc_driver ohci_lh7a404_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -244,6 +242,7 @@ static int ohci_hcd_lh7a404_drv_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_lh7a404_driver = {
.probe = ohci_hcd_lh7a404_drv_probe,
.remove = ohci_hcd_lh7a404_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_lh7a404_drv_suspend, */
/*.resume = ohci_hcd_lh7a404_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-mem.c b/drivers/usb/host/ohci-mem.c
index bfbe328a478..d976614eebd 100644
--- a/drivers/usb/host/ohci-mem.c
+++ b/drivers/usb/host/ohci-mem.c
@@ -28,7 +28,6 @@ static void ohci_hcd_init (struct ohci_hcd *ohci)
ohci->next_statechange = jiffies;
spin_lock_init (&ohci->lock);
INIT_LIST_HEAD (&ohci->pending);
- ohci->reboot_notifier.notifier_call = ohci_reboot;
}
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index c4c4babd476..9c02177de50 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -4,7 +4,7 @@
* (C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at>
* (C) Copyright 2000-2005 David Brownell
* (C) Copyright 2002 Hewlett-Packard Company
- *
+ *
* OMAP Bus Glue
*
* Modified for OMAP by Tony Lindgren <tony@atomide.com>
@@ -66,15 +66,20 @@ extern int usb_disabled(void);
extern int ocpi_enable(void);
static struct clk *usb_host_ck;
+static struct clk *usb_dc_ck;
+static int host_enabled;
+static int host_initialized;
static void omap_ohci_clock_power(int on)
{
if (on) {
+ clk_enable(usb_dc_ck);
clk_enable(usb_host_ck);
/* guesstimate for T5 == 1x 32K clock + APLL lock time */
udelay(100);
} else {
clk_disable(usb_host_ck);
+ clk_disable(usb_dc_ck);
}
}
@@ -87,14 +92,14 @@ static int omap_ohci_transceiver_power(int on)
if (on) {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
- | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ | ((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, LOW);
} else {
if (machine_is_omap_innovator() && cpu_is_omap1510())
fpga_write(fpga_read(INNOVATOR_FPGA_CAM_USB_CONTROL)
- & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
+ & ~((1 << 5/*usb1*/) | (1 << 3/*usb2*/)),
INNOVATOR_FPGA_CAM_USB_CONTROL);
else if (machine_is_omap_osk())
tps65010_set_gpio_out_value(GPIO1, HIGH);
@@ -103,6 +108,7 @@ static int omap_ohci_transceiver_power(int on)
return 0;
}
+#ifdef CONFIG_ARCH_OMAP15XX
/*
* OMAP-1510 specific Local Bus clock on/off
*/
@@ -121,8 +127,8 @@ static int omap_1510_local_bus_power(int on)
/*
* OMAP-1510 specific Local Bus initialization
* NOTE: This assumes 32MB memory size in OMAP1510LB_MEMSIZE.
- * See also arch/mach-omap/memory.h for __virt_to_dma() and
- * __dma_to_virt() which need to match with the physical
+ * See also arch/mach-omap/memory.h for __virt_to_dma() and
+ * __dma_to_virt() which need to match with the physical
* Local Bus address below.
*/
static int omap_1510_local_bus_init(void)
@@ -130,7 +136,7 @@ static int omap_1510_local_bus_init(void)
unsigned int tlb;
unsigned long lbaddr, physaddr;
- omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
+ omap_writel((omap_readl(OMAP1510_LB_CLOCK_DIV) & 0xfffffff8) | 0x4,
OMAP1510_LB_CLOCK_DIV);
/* Configure the Local Bus MMU table */
@@ -138,7 +144,7 @@ static int omap_1510_local_bus_init(void)
lbaddr = tlb * 0x00100000 + OMAP1510_LB_OFFSET;
physaddr = tlb * 0x00100000 + PHYS_OFFSET;
omap_writel((lbaddr & 0x0fffffff) >> 22, OMAP1510_LB_MMU_CAM_H);
- omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
+ omap_writel(((lbaddr & 0x003ffc00) >> 6) | 0xc,
OMAP1510_LB_MMU_CAM_L);
omap_writel(physaddr >> 16, OMAP1510_LB_MMU_RAM_H);
omap_writel((physaddr & 0x0000fc00) | 0x300, OMAP1510_LB_MMU_RAM_L);
@@ -152,6 +158,10 @@ static int omap_1510_local_bus_init(void)
return 0;
}
+#else
+#define omap_1510_local_bus_power(x) {}
+#define omap_1510_local_bus_init() {}
+#endif
#ifdef CONFIG_USB_OTG
@@ -173,13 +183,14 @@ static void start_hnp(struct ohci_hcd *ohci)
/*-------------------------------------------------------------------------*/
-static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
+static int ohci_omap_init(struct usb_hcd *hcd)
{
- struct omap_usb_config *config = pdev->dev.platform_data;
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ struct omap_usb_config *config = hcd->self.controller->platform_data;
int need_transceiver = (config->otg != 0);
int ret;
- dev_dbg(&pdev->dev, "starting USB Controller\n");
+ dev_dbg(hcd->self.controller, "starting USB Controller\n");
if (config->otg) {
ohci_to_hcd(ohci)->self.otg_port = config->otg;
@@ -200,7 +211,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
if (ohci->transceiver) {
int status = otg_set_host(ohci->transceiver,
&ohci_to_hcd(ohci)->self);
- dev_dbg(&pdev->dev, "init %s transceiver, status %d\n",
+ dev_dbg(hcd->self.controller, "init %s transceiver, status %d\n",
ohci->transceiver->label, status);
if (status) {
if (ohci->transceiver)
@@ -208,7 +219,7 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
return status;
}
} else {
- dev_err(&pdev->dev, "can't find transceiver\n");
+ dev_err(hcd->self.controller, "can't find transceiver\n");
return -ENODEV;
}
}
@@ -247,6 +258,10 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
}
ohci_writel(ohci, rh, &ohci->regs->roothub.a);
distrust_firmware = 0;
+ } else if (machine_is_nokia770()) {
+ /* We require a self-powered hub, which should have
+ * plenty of power. */
+ ohci_to_hcd(ohci)->power_budget = 0;
}
/* FIXME khubd hub requests should manage power switching */
@@ -260,21 +275,15 @@ static int omap_start_hc(struct ohci_hcd *ohci, struct platform_device *pdev)
return 0;
}
-static void omap_stop_hc(struct platform_device *pdev)
+static void ohci_omap_stop(struct usb_hcd *hcd)
{
- dev_dbg(&pdev->dev, "stopping USB Controller\n");
+ dev_dbg(hcd->self.controller, "stopping USB Controller\n");
omap_ohci_clock_power(0);
}
/*-------------------------------------------------------------------------*/
-void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
-
-/* configure so an HC device and id are always provided */
-/* always called with process context; sleeping is OK */
-
-
/**
* usb_hcd_omap_probe - initialize OMAP-based HCDs
* Context: !in_interrupt()
@@ -283,7 +292,7 @@ void usb_hcd_omap_remove (struct usb_hcd *, struct platform_device *);
* then invokes the start() method for the HCD associated with it
* through the hotplug entry's driver_data.
*/
-int usb_hcd_omap_probe (const struct hc_driver *driver,
+static int usb_hcd_omap_probe (const struct hc_driver *driver,
struct platform_device *pdev)
{
int retval, irq;
@@ -291,12 +300,12 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
struct ohci_hcd *ohci;
if (pdev->num_resources != 2) {
- printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
+ printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
pdev->num_resources);
return -ENODEV;
}
- if (pdev->resource[0].flags != IORESOURCE_MEM
+ if (pdev->resource[0].flags != IORESOURCE_MEM
|| pdev->resource[1].flags != IORESOURCE_IRQ) {
printk(KERN_ERR "hcd probe: invalid resource type\n");
return -ENODEV;
@@ -306,6 +315,17 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
if (IS_ERR(usb_host_ck))
return PTR_ERR(usb_host_ck);
+ if (!cpu_is_omap1510())
+ usb_dc_ck = clk_get(0, "usb_dc_ck");
+ else
+ usb_dc_ck = clk_get(0, "lb_ck");
+
+ if (IS_ERR(usb_dc_ck)) {
+ clk_put(usb_host_ck);
+ return PTR_ERR(usb_dc_ck);
+ }
+
+
hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
if (!hcd) {
retval = -ENOMEM;
@@ -325,9 +345,8 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
ohci = hcd_to_ohci(hcd);
ohci_hcd_init(ohci);
- retval = omap_start_hc(ohci, pdev);
- if (retval < 0)
- goto err2;
+ host_initialized = 0;
+ host_enabled = 1;
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
@@ -335,15 +354,21 @@ int usb_hcd_omap_probe (const struct hc_driver *driver,
goto err2;
}
retval = usb_add_hcd(hcd, irq, IRQF_DISABLED);
- if (retval == 0)
- return retval;
+ if (retval)
+ goto err2;
+
+ host_initialized = 1;
+
+ if (!host_enabled)
+ omap_ohci_clock_power(0);
- omap_stop_hc(pdev);
+ return 0;
err2:
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
err1:
usb_put_hcd(hcd);
err0:
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
return retval;
}
@@ -359,31 +384,41 @@ err0:
* Reverses the effect of usb_hcd_omap_probe(), first invoking
* the HCD's stop() method. It is always called from a thread
* context, normally "rmmod", "apmd", or something similar.
- *
*/
-void usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
+static inline void
+usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
{
+ struct ohci_hcd *ohci = hcd_to_ohci (hcd);
+
usb_remove_hcd(hcd);
+ if (ohci->transceiver) {
+ (void) otg_set_host(ohci->transceiver, 0);
+ put_device(ohci->transceiver->dev);
+ }
if (machine_is_omap_osk())
omap_free_gpio(9);
- omap_stop_hc(pdev);
release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
usb_put_hcd(hcd);
+ clk_put(usb_dc_ck);
clk_put(usb_host_ck);
}
/*-------------------------------------------------------------------------*/
-static int __devinit
+static int
ohci_omap_start (struct usb_hcd *hcd)
{
struct omap_usb_config *config;
struct ohci_hcd *ohci = hcd_to_ohci (hcd);
int ret;
+ if (!host_enabled)
+ return 0;
config = hcd->self.controller->platform_data;
- if (config->otg || config->rwc)
+ if (config->otg || config->rwc) {
+ ohci->hc_control = OHCI_CTRL_RWC;
writel(OHCI_CTRL_RWC, &ohci->regs->control);
+ }
if ((ret = ohci_run (ohci)) < 0) {
dev_err(hcd->self.controller, "can't start\n");
@@ -409,8 +444,10 @@ static const struct hc_driver ohci_omap_hc_driver = {
/*
* basic lifecycle operations
*/
+ .reset = ohci_omap_init,
.start = ohci_omap_start,
- .stop = ohci_stop,
+ .stop = ohci_omap_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -429,6 +466,7 @@ static const struct hc_driver ohci_omap_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -446,13 +484,8 @@ static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
{
struct usb_hcd *hcd = platform_get_drvdata(dev);
- struct ohci_hcd *ohci = hcd_to_ohci (hcd);
usb_hcd_omap_remove(hcd, dev);
- if (ohci->transceiver) {
- (void) otg_set_host(ohci->transceiver, 0);
- put_device(ohci->transceiver->dev);
- }
platform_set_drvdata(dev, NULL);
return 0;
@@ -472,7 +505,7 @@ static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
omap_ohci_clock_power(0);
ohci_to_hcd(ohci)->state = HC_STATE_SUSPENDED;
- dev->power.power_state = PMSG_SUSPEND;
+ dev->dev.power.power_state = PMSG_SUSPEND;
return 0;
}
@@ -485,8 +518,8 @@ static int ohci_omap_resume(struct platform_device *dev)
ohci->next_statechange = jiffies;
omap_ohci_clock_power(1);
- dev->power.power_state = PMSG_ON;
- usb_hcd_resume_root_hub(dev_get_drvdata(dev));
+ dev->dev.power.power_state = PMSG_ON;
+ usb_hcd_resume_root_hub(platform_get_drvdata(dev));
return 0;
}
@@ -500,6 +533,7 @@ static int ohci_omap_resume(struct platform_device *dev)
static struct platform_driver ohci_hcd_omap_driver = {
.probe = ohci_hcd_omap_drv_probe,
.remove = ohci_hcd_omap_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_omap_suspend,
.resume = ohci_omap_resume,
diff --git a/drivers/usb/host/ohci-pci.c b/drivers/usb/host/ohci-pci.c
index b268537e389..87441855278 100644
--- a/drivers/usb/host/ohci-pci.c
+++ b/drivers/usb/host/ohci-pci.c
@@ -73,13 +73,14 @@ ohci_pci_start (struct usb_hcd *hcd)
else if (pdev->vendor == PCI_VENDOR_ID_NS) {
struct pci_dev *b;
- b = pci_find_slot (pdev->bus->number,
+ b = pci_get_slot (pdev->bus,
PCI_DEVFN (PCI_SLOT (pdev->devfn), 1));
if (b && b->device == PCI_DEVICE_ID_NS_87560_LIO
&& b->vendor == PCI_VENDOR_ID_NS) {
ohci->flags |= OHCI_QUIRK_SUPERIO;
ohci_dbg (ohci, "Using NSC SuperIO setup\n");
}
+ pci_dev_put(b);
}
/* Check for Compaq's ZFMicro chipset, which needs short
@@ -135,6 +136,11 @@ static int ohci_pci_suspend (struct usb_hcd *hcd, pm_message_t message)
}
ohci_writel(ohci, OHCI_INTR_MIE, &ohci->regs->intrdisable);
(void)ohci_readl(ohci, &ohci->regs->intrdisable);
+
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW)
+ ohci_usb_reset(ohci);
+
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
bail:
spin_unlock_irqrestore (&ohci->lock, flags);
@@ -171,11 +177,14 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.reset = ohci_pci_reset,
.start = ohci_pci_start,
+ .stop = ohci_stop,
+ .shutdown = ohci_shutdown,
+
#ifdef CONFIG_PM
+ /* these suspend/resume entries are for upstream PCI glue ONLY */
.suspend = ohci_pci_suspend,
.resume = ohci_pci_resume,
#endif
- .stop = ohci_stop,
/*
* managing i/o requests and associated device resources
@@ -194,6 +203,7 @@ static const struct hc_driver ohci_pci_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -224,6 +234,8 @@ static struct pci_driver ohci_pci_driver = {
.suspend = usb_hcd_pci_suspend,
.resume = usb_hcd_pci_resume,
#endif
+
+ .shutdown = usb_hcd_pci_shutdown,
};
diff --git a/drivers/usb/host/ohci-pnx4008.c b/drivers/usb/host/ohci-pnx4008.c
new file mode 100644
index 00000000000..82cb22f002e
--- /dev/null
+++ b/drivers/usb/host/ohci-pnx4008.c
@@ -0,0 +1,476 @@
+/*
+ * drivers/usb/host/ohci-pnx4008.c
+ *
+ * driver for Philips PNX4008 USB Host
+ *
+ * Authors: Dmitry Chigirev <source@mvista.com>
+ * Vitaly Wool <vitalywool@gmail.com>
+ *
+ * register initialization is based on code examples provided by Philips
+ * Copyright (c) 2005 Koninklijke Philips Electronics N.V.
+ *
+ * NOTE: This driver does not have suspend/resume functionality
+ * This driver is intended for engineering development purposes only
+ *
+ * 2005-2006 (c) MontaVista Software, Inc. 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/clk.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <asm/arch/platform.h>
+#include <asm/arch/irqs.h>
+#include <asm/arch/gpio.h>
+
+#define USB_CTRL IO_ADDRESS(PNX4008_PWRMAN_BASE + 0x64)
+
+/* USB_CTRL bit defines */
+#define USB_SLAVE_HCLK_EN (1 << 24)
+#define USB_HOST_NEED_CLK_EN (1 << 21)
+
+#define USB_OTG_CLK_CTRL IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF4)
+#define USB_OTG_CLK_STAT IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0xFF8)
+
+/* USB_OTG_CLK_CTRL bit defines */
+#define AHB_M_CLOCK_ON (1 << 4)
+#define OTG_CLOCK_ON (1 << 3)
+#define I2C_CLOCK_ON (1 << 2)
+#define DEV_CLOCK_ON (1 << 1)
+#define HOST_CLOCK_ON (1 << 0)
+
+#define USB_OTG_STAT_CONTROL IO_ADDRESS(PNX4008_USB_CONFIG_BASE + 0x110)
+
+/* USB_OTG_STAT_CONTROL bit defines */
+#define TRANSPARENT_I2C_EN (1 << 7)
+#define HOST_EN (1 << 0)
+
+/* ISP1301 USB transceiver I2C registers */
+#define ISP1301_MODE_CONTROL_1 0x04 /* u8 read, set, +1 clear */
+
+#define MC1_SPEED_REG (1 << 0)
+#define MC1_SUSPEND_REG (1 << 1)
+#define MC1_DAT_SE0 (1 << 2)
+#define MC1_TRANSPARENT (1 << 3)
+#define MC1_BDIS_ACON_EN (1 << 4)
+#define MC1_OE_INT_EN (1 << 5)
+#define MC1_UART_EN (1 << 6)
+#define MC1_MASK 0x7f
+
+#define ISP1301_MODE_CONTROL_2 0x12 /* u8 read, set, +1 clear */
+
+#define MC2_GLOBAL_PWR_DN (1 << 0)
+#define MC2_SPD_SUSP_CTRL (1 << 1)
+#define MC2_BI_DI (1 << 2)
+#define MC2_TRANSP_BDIR0 (1 << 3)
+#define MC2_TRANSP_BDIR1 (1 << 4)
+#define MC2_AUDIO_EN (1 << 5)
+#define MC2_PSW_EN (1 << 6)
+#define MC2_EN2V7 (1 << 7)
+
+#define ISP1301_OTG_CONTROL_1 0x06 /* u8 read, set, +1 clear */
+# define OTG1_DP_PULLUP (1 << 0)
+# define OTG1_DM_PULLUP (1 << 1)
+# define OTG1_DP_PULLDOWN (1 << 2)
+# define OTG1_DM_PULLDOWN (1 << 3)
+# define OTG1_ID_PULLDOWN (1 << 4)
+# define OTG1_VBUS_DRV (1 << 5)
+# define OTG1_VBUS_DISCHRG (1 << 6)
+# define OTG1_VBUS_CHRG (1 << 7)
+#define ISP1301_OTG_STATUS 0x10 /* u8 readonly */
+# define OTG_B_SESS_END (1 << 6)
+# define OTG_B_SESS_VLD (1 << 7)
+
+#define ISP1301_I2C_ADDR 0x2C
+
+#define ISP1301_I2C_MODE_CONTROL_1 0x4
+#define ISP1301_I2C_MODE_CONTROL_2 0x12
+#define ISP1301_I2C_OTG_CONTROL_1 0x6
+#define ISP1301_I2C_OTG_CONTROL_2 0x10
+#define ISP1301_I2C_INTERRUPT_SOURCE 0x8
+#define ISP1301_I2C_INTERRUPT_LATCH 0xA
+#define ISP1301_I2C_INTERRUPT_FALLING 0xC
+#define ISP1301_I2C_INTERRUPT_RISING 0xE
+#define ISP1301_I2C_REG_CLEAR_ADDR 1
+
+struct i2c_driver isp1301_driver;
+struct i2c_client *isp1301_i2c_client;
+
+extern int usb_disabled(void);
+extern int ocpi_enable(void);
+
+static struct clk *usb_clk;
+
+static int isp1301_probe(struct i2c_adapter *adap);
+static int isp1301_detach(struct i2c_client *client);
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+ void *arg);
+
+static unsigned short normal_i2c[] =
+ { ISP1301_I2C_ADDR, ISP1301_I2C_ADDR + 1, I2C_CLIENT_END };
+static unsigned short dummy_i2c_addrlist[] = { I2C_CLIENT_END };
+
+static struct i2c_client_address_data addr_data = {
+ .normal_i2c = normal_i2c,
+ .probe = dummy_i2c_addrlist,
+ .ignore = dummy_i2c_addrlist,
+};
+
+struct i2c_driver isp1301_driver = {
+ .id = I2C_DRIVERID_I2CDEV, /* Fake Id */
+ .class = I2C_CLASS_HWMON,
+ .attach_adapter = isp1301_probe,
+ .detach_client = isp1301_detach,
+ .command = isp1301_command
+};
+
+static int isp1301_attach(struct i2c_adapter *adap, int addr, int kind)
+{
+ struct i2c_client *c;
+
+ c = (struct i2c_client *)kzalloc(sizeof(*c), SLAB_KERNEL);
+
+ if (!c)
+ return -ENOMEM;
+
+ strcpy(c->name, "isp1301");
+ c->flags = 0;
+ c->addr = addr;
+ c->adapter = adap;
+ c->driver = &isp1301_driver;
+
+ isp1301_i2c_client = c;
+
+ return i2c_attach_client(c);
+}
+
+static int isp1301_probe(struct i2c_adapter *adap)
+{
+ return i2c_probe(adap, &addr_data, isp1301_attach);
+}
+
+static int isp1301_detach(struct i2c_client *client)
+{
+ i2c_detach_client(client);
+ kfree(isp1301_i2c_client);
+ return 0;
+}
+
+/* No commands defined */
+static int isp1301_command(struct i2c_client *client, unsigned int cmd,
+ void *arg)
+{
+ return 0;
+}
+
+static void i2c_write(u8 buf, u8 subaddr)
+{
+ char tmpbuf[2];
+
+ tmpbuf[0] = subaddr; /*register number */
+ tmpbuf[1] = buf; /*register data */
+ i2c_master_send(isp1301_i2c_client, &tmpbuf[0], 2);
+}
+
+static void isp1301_configure(void)
+{
+ /* PNX4008 only supports DAT_SE0 USB mode */
+ /* PNX4008 R2A requires setting the MAX603 to output 3.6V */
+ /* Power up externel charge-pump */
+
+ i2c_write(MC1_DAT_SE0 | MC1_SPEED_REG, ISP1301_I2C_MODE_CONTROL_1);
+ i2c_write(~(MC1_DAT_SE0 | MC1_SPEED_REG),
+ ISP1301_I2C_MODE_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL,
+ ISP1301_I2C_MODE_CONTROL_2);
+ i2c_write(~(MC2_BI_DI | MC2_PSW_EN | MC2_SPD_SUSP_CTRL),
+ ISP1301_I2C_MODE_CONTROL_2 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN,
+ ISP1301_I2C_OTG_CONTROL_1);
+ i2c_write(~(OTG1_DM_PULLDOWN | OTG1_DP_PULLDOWN),
+ ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_LATCH | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_FALLING | ISP1301_I2C_REG_CLEAR_ADDR);
+ i2c_write(0xFF,
+ ISP1301_I2C_INTERRUPT_RISING | ISP1301_I2C_REG_CLEAR_ADDR);
+
+}
+
+static inline void isp1301_vbus_on(void)
+{
+ i2c_write(OTG1_VBUS_DRV, ISP1301_I2C_OTG_CONTROL_1);
+}
+
+static inline void isp1301_vbus_off(void)
+{
+ i2c_write(OTG1_VBUS_DRV,
+ ISP1301_I2C_OTG_CONTROL_1 | ISP1301_I2C_REG_CLEAR_ADDR);
+}
+
+static void pnx4008_start_hc(void)
+{
+ unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+ isp1301_vbus_on();
+}
+
+static void pnx4008_stop_hc(void)
+{
+ unsigned long tmp;
+ isp1301_vbus_off();
+ tmp = __raw_readl(USB_OTG_STAT_CONTROL) & ~HOST_EN;
+ __raw_writel(tmp, USB_OTG_STAT_CONTROL);
+}
+
+static int __devinit ohci_pnx4008_start(struct usb_hcd *hcd)
+{
+ struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+ int ret;
+
+ if ((ret = ohci_init(ohci)) < 0)
+ return ret;
+
+ if ((ret = ohci_run(ohci)) < 0) {
+ dev_err(hcd->self.controller, "can't start\n");
+ ohci_stop(hcd);
+ return ret;
+ }
+ return 0;
+}
+
+static const struct hc_driver ohci_pnx4008_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "pnx4008 OHCI",
+
+ /*
+ * generic hardware linkage
+ */
+ .irq = ohci_irq,
+ .flags = HCD_USB11 | HCD_MEMORY,
+
+ .hcd_priv_size = sizeof(struct ohci_hcd),
+ /*
+ * basic lifecycle operations
+ */
+ .start = ohci_pnx4008_start,
+ .stop = ohci_stop,
+
+ /*
+ * managing i/o requests and associated device resources
+ */
+ .urb_enqueue = ohci_urb_enqueue,
+ .urb_dequeue = ohci_urb_dequeue,
+ .endpoint_disable = ohci_endpoint_disable,
+
+ /*
+ * scheduling support
+ */
+ .get_frame_number = ohci_get_frame,
+
+ /*
+ * root hub support
+ */
+ .hub_status_data = ohci_hub_status_data,
+ .hub_control = ohci_hub_control,
+
+ .start_port_reset = ohci_start_port_reset,
+};
+
+#define USB_CLOCK_MASK (AHB_M_CLOCK_ON| OTG_CLOCK_ON | HOST_CLOCK_ON | I2C_CLOCK_ON)
+
+static void pnx4008_set_usb_bits(void)
+{
+ start_int_set_falling_edge(SE_USB_OTG_ATX_INT_N);
+ start_int_ack(SE_USB_OTG_ATX_INT_N);
+ start_int_umask(SE_USB_OTG_ATX_INT_N);
+
+ start_int_set_rising_edge(SE_USB_OTG_TIMER_INT);
+ start_int_ack(SE_USB_OTG_TIMER_INT);
+ start_int_umask(SE_USB_OTG_TIMER_INT);
+
+ start_int_set_rising_edge(SE_USB_I2C_INT);
+ start_int_ack(SE_USB_I2C_INT);
+ start_int_umask(SE_USB_I2C_INT);
+
+ start_int_set_rising_edge(SE_USB_INT);
+ start_int_ack(SE_USB_INT);
+ start_int_umask(SE_USB_INT);
+
+ start_int_set_rising_edge(SE_USB_NEED_CLK_INT);
+ start_int_ack(SE_USB_NEED_CLK_INT);
+ start_int_umask(SE_USB_NEED_CLK_INT);
+
+ start_int_set_rising_edge(SE_USB_AHB_NEED_CLK_INT);
+ start_int_ack(SE_USB_AHB_NEED_CLK_INT);
+ start_int_umask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static void pnx4008_unset_usb_bits(void)
+{
+ start_int_mask(SE_USB_OTG_ATX_INT_N);
+ start_int_mask(SE_USB_OTG_TIMER_INT);
+ start_int_mask(SE_USB_I2C_INT);
+ start_int_mask(SE_USB_INT);
+ start_int_mask(SE_USB_NEED_CLK_INT);
+ start_int_mask(SE_USB_AHB_NEED_CLK_INT);
+}
+
+static int __devinit usb_hcd_pnx4008_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = 0;
+ struct ohci_hcd *ohci;
+ const struct hc_driver *driver = &ohci_pnx4008_hc_driver;
+
+ int ret = 0, irq;
+
+ dev_dbg(&pdev->dev, "%s: " DRIVER_INFO " (pnx4008)\n", hcd_name);
+ if (usb_disabled()) {
+ err("USB is disabled");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ if (pdev->num_resources != 2
+ || pdev->resource[0].flags != IORESOURCE_MEM
+ || pdev->resource[1].flags != IORESOURCE_IRQ) {
+ err("Invalid resource configuration");
+ ret = -ENODEV;
+ goto out;
+ }
+
+ /* Enable AHB slave USB clock, needed for further USB clock control */
+ __raw_writel(USB_SLAVE_HCLK_EN | (1 << 19), USB_CTRL);
+
+ ret = i2c_add_driver(&isp1301_driver);
+ if (ret < 0) {
+ err("failed to connect I2C to ISP1301 USB Transceiver");
+ goto out;
+ }
+
+ isp1301_configure();
+
+ /* Enable USB PLL */
+ usb_clk = clk_get(&pdev->dev, "ck_pll5");
+ if (IS_ERR(usb_clk)) {
+ err("failed to acquire USB PLL");
+ ret = PTR_ERR(usb_clk);
+ goto out1;
+ }
+
+ ret = clk_enable(usb_clk);
+ if (ret < 0) {
+ err("failed to start USB PLL");
+ goto out2;
+ }
+
+ ret = clk_set_rate(usb_clk, 48000);
+ if (ret < 0) {
+ err("failed to set USB clock rate");
+ goto out3;
+ }
+
+ __raw_writel(__raw_readl(USB_CTRL) | USB_HOST_NEED_CLK_EN, USB_CTRL);
+
+ /* Set to enable all needed USB clocks */
+ __raw_writel(USB_CLOCK_MASK, USB_OTG_CLK_CTRL);
+
+ while ((__raw_readl(USB_OTG_CLK_STAT) & USB_CLOCK_MASK) !=
+ USB_CLOCK_MASK) ;
+
+ hcd = usb_create_hcd (driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ err("Failed to allocate HC buffer");
+ ret = -ENOMEM;
+ goto out3;
+ }
+
+ /* Set all USB bits in the Start Enable register */
+ pnx4008_set_usb_bits();
+
+ hcd->rsrc_start = pdev->resource[0].start;
+ hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ dev_dbg(&pdev->dev, "request_mem_region failed\n");
+ ret = -ENOMEM;
+ goto out4;
+ }
+ hcd->regs = (void __iomem *)pdev->resource[0].start;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ ret = -ENXIO;
+ goto out4;
+ }
+
+ hcd->self.hcpriv = (void *)hcd;
+
+ pnx4008_start_hc();
+ platform_set_drvdata(pdev, hcd);
+ ohci = hcd_to_ohci(hcd);
+ ohci_hcd_init(ohci);
+
+ dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
+ ret = usb_add_hcd(hcd, irq, SA_INTERRUPT);
+ if (ret == 0)
+ return ret;
+
+ pnx4008_stop_hc();
+out4:
+ pnx4008_unset_usb_bits();
+ usb_put_hcd(hcd);
+out3:
+ clk_disable(usb_clk);
+out2:
+ clk_put(usb_clk);
+out1:
+ i2c_del_driver(&isp1301_driver);
+out:
+ return ret;
+}
+
+static int usb_hcd_pnx4008_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+
+ usb_remove_hcd(hcd);
+ pnx4008_stop_hc();
+ release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+ usb_put_hcd(hcd);
+ pnx4008_unset_usb_bits();
+ clk_disable(usb_clk);
+ clk_put(usb_clk);
+ i2c_del_driver(&isp1301_driver);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver usb_hcd_pnx4008_driver = {
+ .driver = {
+ .name = "usb-ohci",
+ },
+ .probe = usb_hcd_pnx4008_probe,
+ .remove = usb_hcd_pnx4008_remove,
+};
+
+static int __init usb_hcd_pnx4008_init(void)
+{
+ return platform_driver_register(&usb_hcd_pnx4008_driver);
+}
+
+static void __exit usb_hcd_pnx4008_cleanup(void)
+{
+ return platform_driver_unregister(&usb_hcd_pnx4008_driver);
+}
+
+module_init(usb_hcd_pnx4008_init);
+module_exit(usb_hcd_pnx4008_cleanup);
diff --git a/drivers/usb/host/ohci-ppc-soc.c b/drivers/usb/host/ohci-ppc-soc.c
index 9fe56ff1615..d9d1ae236bd 100644
--- a/drivers/usb/host/ohci-ppc-soc.c
+++ b/drivers/usb/host/ohci-ppc-soc.c
@@ -148,6 +148,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.start = ohci_ppc_soc_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -166,6 +167,7 @@ static const struct hc_driver ohci_ppc_soc_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -195,6 +197,7 @@ static int ohci_hcd_ppc_soc_drv_remove(struct platform_device *pdev)
static struct platform_driver ohci_hcd_ppc_soc_driver = {
.probe = ohci_hcd_ppc_soc_drv_probe,
.remove = ohci_hcd_ppc_soc_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
/*.suspend = ohci_hcd_ppc_soc_drv_suspend,*/
/*.resume = ohci_hcd_ppc_soc_drv_resume,*/
diff --git a/drivers/usb/host/ohci-pxa27x.c b/drivers/usb/host/ohci-pxa27x.c
index 6f559e10278..e176b04d7ae 100644
--- a/drivers/usb/host/ohci-pxa27x.c
+++ b/drivers/usb/host/ohci-pxa27x.c
@@ -270,6 +270,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.start = ohci_pxa27x_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -288,6 +289,7 @@ static const struct hc_driver ohci_pxa27x_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -357,6 +359,7 @@ static int ohci_hcd_pxa27x_drv_resume(struct platform_device *pdev)
static struct platform_driver ohci_hcd_pxa27x_driver = {
.probe = ohci_hcd_pxa27x_drv_probe,
.remove = ohci_hcd_pxa27x_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
#ifdef CONFIG_PM
.suspend = ohci_hcd_pxa27x_drv_suspend,
.resume = ohci_hcd_pxa27x_drv_resume,
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
index d2fc6969a9f..59e436424d4 100644
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -370,7 +370,7 @@ static int usb_hcd_s3c2410_probe (const struct hc_driver *driver,
goto err_mem;
}
- usb_clk = clk_get(&dev->dev, "upll");
+ usb_clk = clk_get(&dev->dev, "usb-bus-host");
if (IS_ERR(usb_clk)) {
dev_err(&dev->dev, "cannot get usb-host clock\n");
retval = -ENOENT;
@@ -447,6 +447,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.start = ohci_s3c2410_start,
.stop = ohci_stop,
+ .shutdown = ohci_shutdown,
/*
* managing i/o requests and associated device resources
@@ -465,6 +466,7 @@ static const struct hc_driver ohci_s3c2410_hc_driver = {
*/
.hub_status_data = ohci_s3c2410_hub_status_data,
.hub_control = ohci_s3c2410_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
@@ -490,6 +492,7 @@ static int ohci_hcd_s3c2410_drv_remove(struct platform_device *pdev)
static struct platform_driver ohci_hcd_s3c2410_driver = {
.probe = ohci_hcd_s3c2410_drv_probe,
.remove = ohci_hcd_s3c2410_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
/*.suspend = ohci_hcd_s3c2410_drv_suspend, */
/*.resume = ohci_hcd_s3c2410_drv_resume, */
.driver = {
diff --git a/drivers/usb/host/ohci-sa1111.c b/drivers/usb/host/ohci-sa1111.c
index ce3de106cad..71371de32ad 100644
--- a/drivers/usb/host/ohci-sa1111.c
+++ b/drivers/usb/host/ohci-sa1111.c
@@ -212,10 +212,6 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
* basic lifecycle operations
*/
.start = ohci_sa1111_start,
-#ifdef CONFIG_PM
- /* suspend: ohci_sa1111_suspend, -- tbd */
- /* resume: ohci_sa1111_resume, -- tbd */
-#endif
.stop = ohci_stop,
/*
@@ -235,6 +231,7 @@ static const struct hc_driver ohci_sa1111_hc_driver = {
*/
.hub_status_data = ohci_hub_status_data,
.hub_control = ohci_hub_control,
+ .hub_irq_enable = ohci_rhsc_enable,
#ifdef CONFIG_PM
.bus_suspend = ohci_bus_suspend,
.bus_resume = ohci_bus_resume,
diff --git a/drivers/usb/host/ohci.h b/drivers/usb/host/ohci.h
index caacf14371f..a2f42a2f47c 100644
--- a/drivers/usb/host/ohci.h
+++ b/drivers/usb/host/ohci.h
@@ -159,7 +159,7 @@ static const int cc_to_error [16] = {
/* Bit Stuff */ -EPROTO,
/* Data Togg */ -EILSEQ,
/* Stall */ -EPIPE,
- /* DevNotResp */ -ETIMEDOUT,
+ /* DevNotResp */ -ETIME,
/* PIDCheck */ -EPROTO,
/* UnExpPID */ -EPROTO,
/* DataOver */ -EOVERFLOW,
@@ -388,8 +388,7 @@ struct ohci_hcd {
u32 hc_control; /* copy of hc control reg */
unsigned long next_statechange; /* suspend/resume */
u32 fminterval; /* saved register */
-
- struct notifier_block reboot_notifier;
+ unsigned autostop:1; /* rh auto stopping/stopped */
unsigned long flags; /* for HC bugs */
#define OHCI_QUIRK_AMD756 0x01 /* erratum #4 */
diff --git a/drivers/usb/host/sl811-hcd.c b/drivers/usb/host/sl811-hcd.c
index fa34092bbcd..3a586aab393 100644
--- a/drivers/usb/host/sl811-hcd.c
+++ b/drivers/usb/host/sl811-hcd.c
@@ -597,7 +597,7 @@ done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs)
/* error? retry, until "3 strikes" */
} else if (++ep->error_count >= 3) {
if (status & SL11H_STATMASK_TMOUT)
- urbstat = -ETIMEDOUT;
+ urbstat = -ETIME;
else if (status & SL11H_STATMASK_OVF)
urbstat = -EOVERFLOW;
else
@@ -1517,7 +1517,7 @@ static int proc_sl811h_open(struct inode *inode, struct file *file)
return single_open(file, proc_sl811h_show, PDE(inode)->data);
}
-static struct file_operations proc_ops = {
+static const struct file_operations proc_ops = {
.open = proc_sl811h_open,
.read = seq_read,
.llseek = seq_lseek,
@@ -1783,10 +1783,15 @@ sl811h_suspend(struct platform_device *dev, pm_message_t state)
struct sl811 *sl811 = hcd_to_sl811(hcd);
int retval = 0;
- if (state.event == PM_EVENT_FREEZE)
+ switch (state.event) {
+ case PM_EVENT_FREEZE:
retval = sl811h_bus_suspend(hcd);
- else if (state.event == PM_EVENT_SUSPEND)
+ break;
+ case PM_EVENT_SUSPEND:
+ case PM_EVENT_PRETHAW: /* explicitly discard hw state */
port_power(sl811, 0);
+ break;
+ }
if (retval == 0)
dev->dev.power.power_state = state;
return retval;
diff --git a/drivers/usb/host/u132-hcd.c b/drivers/usb/host/u132-hcd.c
new file mode 100644
index 00000000000..cb2e2a604d1
--- /dev/null
+++ b/drivers/usb/host/u132-hcd.c
@@ -0,0 +1,3295 @@
+/*
+* Host Controller Driver for the Elan Digital Systems U132 adapter
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.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, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB host drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+#include <linux/pci_ids.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/byteorder.h>
+#include "../core/hcd.h"
+#include "ohci.h"
+#define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+#define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+ OHCI_INTR_WDH)
+MODULE_AUTHOR("Tony Olech - Elan Digital Systems Limited");
+MODULE_DESCRIPTION("U132 USB Host Controller Driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+INT_MODULE_PARM(testing, 0);
+/* Some boards misreport power switching/overcurrent*/
+static int distrust_firmware = 1;
+module_param(distrust_firmware, bool, 0);
+MODULE_PARM_DESC(distrust_firmware, "true to distrust firmware power/overcurren"
+ "t setup");
+DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
+/*
+* u132_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore u132_module_lock;
+static int u132_exiting = 0;
+static int u132_instances = 0;
+static struct list_head u132_static_list;
+/*
+* end of the global variables protected by u132_module_lock
+*/
+static struct workqueue_struct *workqueue;
+#define MAX_U132_PORTS 7
+#define MAX_U132_ADDRS 128
+#define MAX_U132_UDEVS 4
+#define MAX_U132_ENDPS 100
+#define MAX_U132_RINGS 4
+static const char *cc_to_text[16] = {
+ "No Error ",
+ "CRC Error ",
+ "Bit Stuff ",
+ "Data Togg ",
+ "Stall ",
+ "DevNotResp ",
+ "PIDCheck ",
+ "UnExpPID ",
+ "DataOver ",
+ "DataUnder ",
+ "(for hw) ",
+ "(for hw) ",
+ "BufferOver ",
+ "BuffUnder ",
+ "(for HCD) ",
+ "(for HCD) "
+};
+struct u132_port {
+ struct u132 *u132;
+ int reset;
+ int enable;
+ int power;
+ int Status;
+};
+struct u132_addr {
+ u8 address;
+};
+struct u132_udev {
+ struct kref kref;
+ struct usb_device *usb_device;
+ u8 enumeration;
+ u8 udev_number;
+ u8 usb_addr;
+ u8 portnumber;
+ u8 endp_number_in[16];
+ u8 endp_number_out[16];
+};
+#define ENDP_QUEUE_SHIFT 3
+#define ENDP_QUEUE_SIZE (1<<ENDP_QUEUE_SHIFT)
+#define ENDP_QUEUE_MASK (ENDP_QUEUE_SIZE-1)
+struct u132_urbq {
+ struct list_head urb_more;
+ struct urb *urb;
+};
+struct u132_spin {
+ spinlock_t slock;
+};
+struct u132_endp {
+ struct kref kref;
+ u8 udev_number;
+ u8 endp_number;
+ u8 usb_addr;
+ u8 usb_endp;
+ struct u132 *u132;
+ struct list_head endp_ring;
+ struct u132_ring *ring;
+ unsigned toggle_bits:2;
+ unsigned active:1;
+ unsigned delayed:1;
+ unsigned input:1;
+ unsigned output:1;
+ unsigned pipetype:2;
+ unsigned dequeueing:1;
+ unsigned edset_flush:1;
+ unsigned spare_bits:14;
+ unsigned long jiffies;
+ struct usb_host_endpoint *hep;
+ struct u132_spin queue_lock;
+ u16 queue_size;
+ u16 queue_last;
+ u16 queue_next;
+ struct urb *urb_list[ENDP_QUEUE_SIZE];
+ struct list_head urb_more;
+ struct work_struct scheduler;
+};
+struct u132_ring {
+ unsigned in_use:1;
+ unsigned length:7;
+ u8 number;
+ struct u132 *u132;
+ struct u132_endp *curr_endp;
+ struct work_struct scheduler;
+};
+#define OHCI_QUIRK_AMD756 0x01
+#define OHCI_QUIRK_SUPERIO 0x02
+#define OHCI_QUIRK_INITRESET 0x04
+#define OHCI_BIG_ENDIAN 0x08
+#define OHCI_QUIRK_ZFMICRO 0x10
+struct u132 {
+ struct kref kref;
+ struct list_head u132_list;
+ struct semaphore sw_lock;
+ struct semaphore scheduler_lock;
+ struct u132_platform_data *board;
+ struct platform_device *platform_dev;
+ struct u132_ring ring[MAX_U132_RINGS];
+ int sequence_num;
+ int going;
+ int power;
+ int reset;
+ int num_ports;
+ u32 hc_control;
+ u32 hc_fminterval;
+ u32 hc_roothub_status;
+ u32 hc_roothub_a;
+ u32 hc_roothub_portstatus[MAX_ROOT_PORTS];
+ int flags;
+ unsigned long next_statechange;
+ struct work_struct monitor;
+ int num_endpoints;
+ struct u132_addr addr[MAX_U132_ADDRS];
+ struct u132_udev udev[MAX_U132_UDEVS];
+ struct u132_port port[MAX_U132_PORTS];
+ struct u132_endp *endp[MAX_U132_ENDPS];
+};
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data);
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, u8 addressofs,
+ u8 width, u32 *data);
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, u8 addressofs,
+ u8 width, u32 data);
+/*
+* these can not be inlines because we need the structure offset!!
+* Does anyone have a better way?????
+*/
+#define u132_read_pcimem(u132, member, data) \
+ usb_ftdi_elan_read_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+#define u132_write_pcimem(u132, member, data) \
+ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0, data);
+#define u132_write_pcimem_byte(u132, member, data) \
+ usb_ftdi_elan_write_pcimem(u132->platform_dev, offsetof(struct \
+ ohci_regs, member), 0x0e, data);
+static inline struct u132 *udev_to_u132(struct u132_udev *udev)
+{
+ u8 udev_number = udev->udev_number;
+ return container_of(udev, struct u132, udev[udev_number]);
+}
+
+static inline struct u132 *hcd_to_u132(struct usb_hcd *hcd)
+{
+ return (struct u132 *)(hcd->hcd_priv);
+}
+
+static inline struct usb_hcd *u132_to_hcd(struct u132 *u132)
+{
+ return container_of((void *)u132, struct usb_hcd, hcd_priv);
+}
+
+static inline void u132_disable(struct u132 *u132)
+{
+ u132_to_hcd(u132)->state = HC_STATE_HALT;
+}
+
+
+#define kref_to_u132(d) container_of(d, struct u132, kref)
+#define kref_to_u132_endp(d) container_of(d, struct u132_endp, kref)
+#define kref_to_u132_udev(d) container_of(d, struct u132_udev, kref)
+#include "../misc/usb_u132.h"
+static const char hcd_name[] = "u132_hcd";
+#define PORT_C_MASK ((USB_PORT_STAT_C_CONNECTION | USB_PORT_STAT_C_ENABLE | \
+ USB_PORT_STAT_C_SUSPEND | USB_PORT_STAT_C_OVERCURRENT | \
+ USB_PORT_STAT_C_RESET) << 16)
+static void u132_hcd_delete(struct kref *kref)
+{
+ struct u132 *u132 = kref_to_u132(kref);
+ struct platform_device *pdev = u132->platform_dev;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132->going += 1;
+ down(&u132_module_lock);
+ list_del_init(&u132->u132_list);
+ u132_instances -= 1;
+ up(&u132_module_lock);
+ dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
+ "2=%p going=%d pdev=%p\n", hcd, u132, u132->going, pdev);
+ usb_put_hcd(hcd);
+}
+
+static inline void u132_u132_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static inline void u132_u132_init_kref(struct u132 *u132)
+{
+ kref_init(&u132->kref);
+}
+
+static void u132_udev_delete(struct kref *kref)
+{
+ struct u132_udev *udev = kref_to_u132_udev(kref);
+ udev->udev_number = 0;
+ udev->usb_device = NULL;
+ udev->usb_addr = 0;
+ udev->enumeration = 0;
+}
+
+static inline void u132_udev_put_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_put(&udev->kref, u132_udev_delete);
+}
+
+static inline void u132_udev_get_kref(struct u132 *u132, struct u132_udev *udev)
+{
+ kref_get(&udev->kref);
+}
+
+static inline void u132_udev_init_kref(struct u132 *u132,
+ struct u132_udev *udev)
+{
+ kref_init(&udev->kref);
+}
+
+static inline void u132_ring_put_kref(struct u132 *u132, struct u132_ring *ring)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_ring_requeue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &ring->scheduler, delta))
+ return;
+ } else if (queue_work(workqueue, &ring->scheduler))
+ return;
+ kref_put(&u132->kref, u132_hcd_delete);
+ return;
+}
+
+static void u132_ring_queue_work(struct u132 *u132, struct u132_ring *ring,
+ unsigned int delta)
+{
+ kref_get(&u132->kref);
+ u132_ring_requeue_work(u132, ring, delta);
+ return;
+}
+
+static void u132_ring_cancel_work(struct u132 *u132, struct u132_ring *ring)
+{
+ if (cancel_delayed_work(&ring->scheduler)) {
+ kref_put(&u132->kref, u132_hcd_delete);
+ }
+}
+
+static void u132_endp_delete(struct kref *kref)
+{
+ struct u132_endp *endp = kref_to_u132_endp(kref);
+ struct u132 *u132 = endp->u132;
+ u8 usb_addr = endp->usb_addr;
+ u8 usb_endp = endp->usb_endp;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ u8 endp_number = endp->endp_number;
+ struct usb_host_endpoint *hep = endp->hep;
+ struct u132_ring *ring = endp->ring;
+ struct list_head *head = &endp->endp_ring;
+ ring->length -= 1;
+ if (endp == ring->curr_endp) {
+ if (list_empty(head)) {
+ ring->curr_endp = NULL;
+ list_del(head);
+ } else {
+ struct u132_endp *next_endp = list_entry(head->next,
+ struct u132_endp, endp_ring);
+ ring->curr_endp = next_endp;
+ list_del(head);
+ }} else
+ list_del(head);
+ if (endp->input) {
+ udev->endp_number_in[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ if (endp->output) {
+ udev->endp_number_out[usb_endp] = 0;
+ u132_udev_put_kref(u132, udev);
+ }
+ u132->endp[endp_number - 1] = NULL;
+ hep->hcpriv = NULL;
+ kfree(endp);
+ u132_u132_put_kref(u132);
+}
+
+static inline void u132_endp_put_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_endp_get_kref(struct u132 *u132, struct u132_endp *endp)
+{
+ kref_get(&endp->kref);
+}
+
+static inline void u132_endp_init_kref(struct u132 *u132,
+ struct u132_endp *endp)
+{
+ kref_init(&endp->kref);
+ kref_get(&u132->kref);
+}
+
+static void u132_endp_queue_work(struct u132 *u132, struct u132_endp *endp,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &endp->scheduler, delta))
+ kref_get(&endp->kref);
+ } else if (queue_work(workqueue, &endp->scheduler))
+ kref_get(&endp->kref);
+ return;
+}
+
+static void u132_endp_cancel_work(struct u132 *u132, struct u132_endp *endp)
+{
+ if (cancel_delayed_work(&endp->scheduler))
+ kref_put(&endp->kref, u132_endp_delete);
+}
+
+static inline void u132_monitor_put_kref(struct u132 *u132)
+{
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static void u132_monitor_queue_work(struct u132 *u132, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &u132->monitor, delta)) {
+ kref_get(&u132->kref);
+ }
+ } else if (queue_work(workqueue, &u132->monitor))
+ kref_get(&u132->kref);
+ return;
+}
+
+static void u132_monitor_requeue_work(struct u132 *u132, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(workqueue, &u132->monitor, delta))
+ return;
+ } else if (queue_work(workqueue, &u132->monitor))
+ return;
+ kref_put(&u132->kref, u132_hcd_delete);
+ return;
+}
+
+static void u132_monitor_cancel_work(struct u132 *u132)
+{
+ if (cancel_delayed_work(&u132->monitor))
+ kref_put(&u132->kref, u132_hcd_delete);
+}
+
+static int read_roothub_info(struct u132 *u132)
+{
+ u32 revision;
+ int retval;
+ retval = u132_read_pcimem(u132, revision, &revision);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ } else if ((revision & 0xFF) == 0x10) {
+ } else if ((revision & 0xFF) == 0x11) {
+ } else {
+ dev_err(&u132->platform_dev->dev, "device revision is not valid"
+ " %08X\n", revision);
+ return -ENODEV;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device co"
+ "ntrol\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.status,
+ &u132->hc_roothub_status);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.status\n", retval);
+ return retval;
+ }
+ retval = u132_read_pcimem(u132, roothub.a, &u132->hc_roothub_a);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d accessing device re"
+ "g roothub.a\n", retval);
+ return retval;
+ }
+ {
+ int I = u132->num_ports;
+ int i = 0;
+ while (I-- > 0) {
+ retval = u132_read_pcimem(u132, roothub.portstatus[i],
+ &u132->hc_roothub_portstatus[i]);
+ if (retval) {
+ dev_err(&u132->platform_dev->dev, "error %d acc"
+ "essing device roothub.portstatus[%d]\n"
+ , retval, i);
+ return retval;
+ } else
+ i += 1;
+ }
+ }
+ return 0;
+}
+
+static void u132_hcd_monitor_work(void *data)
+{
+ struct u132 *u132 = data;
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ u132_monitor_put_kref(u132);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ int retval;
+ down(&u132->sw_lock);
+ retval = read_roothub_info(u132);
+ if (retval) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ u132_disable(u132);
+ u132->going = 1;
+ up(&u132->sw_lock);
+ usb_hc_died(hcd);
+ ftdi_elan_gone_away(u132->platform_dev);
+ u132_monitor_put_kref(u132);
+ return;
+ } else {
+ u132_monitor_requeue_work(u132, 500);
+ up(&u132->sw_lock);
+ return;
+ }
+ }
+}
+
+static void u132_hcd_giveback_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ urb->status = status;
+ urb->hcpriv = NULL;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ } down(&u132->scheduler_lock);
+ ring = endp->ring;
+ ring->in_use = 0;
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return;
+}
+
+static void u132_hcd_forget_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ u132_endp_put_kref(u132, endp);
+}
+
+static void u132_hcd_abandon_urb(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb, int status)
+{
+ unsigned long irqs;
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ urb->error_count = 0;
+ urb->status = status;
+ urb->hcpriv = NULL;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_next += 1;
+ if (ENDP_QUEUE_SIZE > --endp->queue_size) {
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next, struct u132_urbq,
+ urb_more);
+ list_del(next);
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urbq->urb;
+ endp->active = 0;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ kfree(urbq);
+ } usb_hcd_giveback_urb(hcd, urb, NULL);
+ return;
+}
+
+static inline int edset_input(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_input(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_setup(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_setup(u132->platform_dev, ring->number, endp,
+ urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_single(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_single(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+static inline int edset_output(struct u132 *u132, struct u132_ring *ring,
+ struct u132_endp *endp, struct urb *urb, u8 address, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ return usb_ftdi_elan_edset_output(u132->platform_dev, ring->number,
+ endp, urb, address, endp->usb_endp, toggle_bits, callback);
+}
+
+
+/*
+* must not LOCK sw_lock
+*
+*/
+static void u132_hcd_interrupt_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ if (urb->actual_length > 0) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_interrupt_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ } else {
+ ring->in_use = 0;
+ endp->active = 0;
+ endp->jiffies = jiffies +
+ msecs_to_jiffies(urb->interval);
+ u132_ring_cancel_work(u132, ring);
+ u132_ring_queue_work(u132, ring, 0);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ }
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 1 & toggle_bits);
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp,
+ 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p givin"
+ "g back INTERRUPT %s\n", urb,
+ cc_to_text[condition_code]);
+ }
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_output_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ urb->actual_length += len;
+ endp->toggle_bits = toggle_bits;
+ if (urb->transfer_buffer_length > urb->actual_length) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_bulk_output_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_bulk_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer + urb->actual_length;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length += len;
+ if ((condition_code == TD_CC_NOERROR) &&
+ (urb->transfer_buffer_length > urb->actual_length)) {
+ int retval;
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_NOERROR) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else if ((condition_code == TD_DATAUNDERRUN) &&
+ ((urb->transfer_flags & URB_SHORT_NOT_OK) == 0)) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_DATAUNDERRUN) {
+ endp->toggle_bits = toggle_bits;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0,
+ 1 & toggle_bits);
+ dev_warn(&u132->platform_dev->dev, "urb=%p(SHORT NOT OK"
+ ") giving back BULK IN %s\n", urb,
+ cc_to_text[condition_code]);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, endp->usb_endp, 0, 0);
+ dev_err(&u132->platform_dev->dev, "urb=%p giving back B"
+ "ULK IN code=%d %s\n", urb, condition_code,
+ cc_to_text[condition_code]);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length = len;
+ if ((condition_code == TD_CC_NOERROR) || ((condition_code ==
+ TD_DATAUNDERRUN) && ((urb->transfer_flags &
+ URB_SHORT_NOT_OK) == 0))) {
+ int retval;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0x3,
+ u132_hcd_configure_empty_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (condition_code == TD_CC_STALL) {
+ up(&u132->scheduler_lock);
+ dev_warn(&u132->platform_dev->dev, "giving back SETUP I"
+ "NPUT STALL urb %p\n", urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ dev_err(&u132->platform_dev->dev, "giving back SETUP IN"
+ "PUT %s urb %p\n", cc_to_text[condition_code],
+ urb);
+ u132_hcd_giveback_urb(u132, endp, urb,
+ cc_to_error[condition_code]);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_empty_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_configure_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ if (usb_pipein(urb->pipe)) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address,
+ endp->usb_endp, 0,
+ u132_hcd_configure_empty_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_empty_recv(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ u132->addr[0].address = 0;
+ endp->usb_addr = udev->usb_addr;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_enumeration_address_sent(void *data, struct urb *urb,
+ u8 *buf, int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, 0, endp->usb_endp, 0,
+ u132_hcd_enumeration_empty_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_empty_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, 0);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_input_recv(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ u8 *u = urb->transfer_buffer;
+ u8 *b = buf;
+ int L = len;
+ while (L-- > 0) {
+ *u++ = *b++;
+ }
+ urb->actual_length = len;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_empty(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0x3,
+ u132_hcd_initial_empty_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_initial_setup_sent(void *data, struct urb *urb, u8 *buf,
+ int len, int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual, int non_null)
+{
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ u8 address = u132->addr[endp->usb_addr].address;
+ down(&u132->scheduler_lock);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ up(&u132->scheduler_lock);
+ u132_hcd_forget_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (endp->dequeueing) {
+ endp->dequeueing = 0;
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -EINTR);
+ return;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, -ENODEV);
+ return;
+ } else if (urb->status == -EINPROGRESS) {
+ int retval;
+ struct u132_ring *ring = endp->ring;
+ up(&u132->scheduler_lock);
+ retval = usb_ftdi_elan_edset_input(u132->platform_dev,
+ ring->number, endp, urb, address, endp->usb_endp, 0,
+ u132_hcd_initial_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ dev_err(&u132->platform_dev->dev, "CALLBACK called urb=%p statu"
+ "s=%d\n", urb, urb->status);
+ up(&u132->scheduler_lock);
+ u132_hcd_giveback_urb(u132, endp, urb, urb->status);
+ return;
+ }
+}
+
+static void u132_hcd_ring_work_scheduler(void *data);
+static void u132_hcd_endp_work_scheduler(void *data);
+/*
+* this work function is only executed from the work queue
+*
+*/
+static void u132_hcd_ring_work_scheduler(void *data)
+{
+ struct u132_ring *ring = data;
+ struct u132 *u132 = ring->u132;
+ down(&u132->scheduler_lock);
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else if (ring->curr_endp) {
+ struct u132_endp *last_endp = ring->curr_endp;
+ struct list_head *scan;
+ struct list_head *head = &last_endp->endp_ring;
+ unsigned long wakeup = 0;
+ list_for_each(scan, head) {
+ struct u132_endp *endp = list_entry(scan,
+ struct u132_endp, endp_ring);
+ if (endp->queue_next == endp->queue_last) {
+ } else if ((endp->delayed == 0)
+ || time_after_eq(jiffies, endp->jiffies)) {
+ ring->curr_endp = endp;
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ }
+ if (last_endp->queue_next == last_endp->queue_last) {
+ } else if ((last_endp->delayed == 0) || time_after_eq(jiffies,
+ last_endp->jiffies)) {
+ u132_endp_cancel_work(u132, last_endp);
+ u132_endp_queue_work(u132, last_endp, 0);
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ } else {
+ unsigned long delta = last_endp->jiffies - jiffies;
+ if (delta > wakeup)
+ wakeup = delta;
+ }
+ if (wakeup > 0) {
+ u132_ring_requeue_work(u132, ring, wakeup);
+ up(&u132->scheduler_lock);
+ return;
+ } else {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+ } else {
+ up(&u132->scheduler_lock);
+ u132_ring_put_kref(u132, ring);
+ return;
+ }
+}
+
+static void u132_hcd_endp_work_scheduler(void *data)
+{
+ struct u132_ring *ring;
+ struct u132_endp *endp = data;
+ struct u132 *u132 = endp->u132;
+ down(&u132->scheduler_lock);
+ ring = endp->ring;
+ if (endp->edset_flush) {
+ endp->edset_flush = 0;
+ if (endp->dequeueing)
+ usb_ftdi_elan_edset_flush(u132->platform_dev,
+ ring->number, endp);
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->active) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->queue_next == endp->queue_last) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (endp->pipetype == PIPE_INTERRUPT) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_single(u132, ring, endp, urb, address,
+ endp->toggle_bits, u132_hcd_interrupt_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else if (endp->pipetype == PIPE_CONTROL) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else if (address == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_initial_setup_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else if (endp->usb_addr == 0) {
+ int retval;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, 0, 0x2,
+ u132_hcd_enumeration_address_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ } else {
+ int retval;
+ u8 address = u132->addr[endp->usb_addr].address;
+ struct urb *urb = endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_setup(u132, ring, endp, urb, address,
+ 0x2, u132_hcd_configure_setup_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb, retval);
+ return;
+ }
+ } else {
+ if (endp->input) {
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_input(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_input_recv);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ } else { /* output pipe */
+ u8 address = u132->addr[endp->usb_addr].address;
+ if (ring->in_use) {
+ up(&u132->scheduler_lock);
+ u132_endp_put_kref(u132, endp);
+ return;
+ } else {
+ int retval;
+ struct urb *urb = endp->urb_list[
+ ENDP_QUEUE_MASK & endp->queue_next];
+ endp->active = 1;
+ ring->curr_endp = endp;
+ ring->in_use = 1;
+ up(&u132->scheduler_lock);
+ retval = edset_output(u132, ring, endp, urb,
+ address, endp->toggle_bits,
+ u132_hcd_bulk_output_sent);
+ if (retval == 0) {
+ } else
+ u132_hcd_giveback_urb(u132, endp, urb,
+ retval);
+ return;
+ }
+ }
+ }
+}
+
+static void port_power(struct u132 *u132, int pn, int is_on)
+{
+ u132->port[pn].power = is_on;
+}
+
+static void u132_power(struct u132 *u132, int is_on)
+{
+ struct usb_hcd *hcd = u132_to_hcd(u132)
+ ; /* hub is inactive unless the port is powered */
+ if (is_on) {
+ if (u132->power)
+ return;
+ u132->power = 1;
+ hcd->self.controller->power.power_state = PMSG_ON;
+ } else {
+ u132->power = 0;
+ hcd->state = HC_STATE_HALT;
+ hcd->self.controller->power.power_state = PMSG_SUSPEND;
+ }
+}
+
+static int u132_periodic_reinit(struct u132 *u132)
+{
+ int retval;
+ u32 fi = u132->hc_fminterval & 0x03fff;
+ u32 fit;
+ u32 fminterval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ fit = fminterval & FIT;
+ retval = u132_write_pcimem(u132, fminterval,
+ (fit ^ FIT) | u132->hc_fminterval);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, periodicstart,
+ ((9 *fi) / 10) & 0x3fff);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static char *hcfs2string(int state)
+{
+ switch (state) {
+ case OHCI_USB_RESET:
+ return "reset";
+ case OHCI_USB_RESUME:
+ return "resume";
+ case OHCI_USB_OPER:
+ return "operational";
+ case OHCI_USB_SUSPEND:
+ return "suspend";
+ }
+ return "?";
+}
+
+static int u132_usb_reset(struct u132 *u132)
+{
+ int retval;
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval)
+ return retval;
+ u132->hc_control &= OHCI_CTRL_RWC;
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ return 0;
+}
+
+static int u132_init(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u132_disable(u132);
+ u132->next_statechange =
+ jiffies; /* SMM owns the HC? not for long! */ {
+ u32 control;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ if (control & OHCI_CTRL_IR) {
+ u32 temp = 50;
+ retval = u132_write_pcimem(u132, intrenable,
+ OHCI_INTR_OC);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus,
+ OHCI_OCR);
+ if (retval)
+ return retval;
+ check:{
+ retval = u132_read_pcimem(u132, control,
+ &control);
+ if (retval)
+ return retval;
+ }
+ if (control & OHCI_CTRL_IR) {
+ msleep(10);
+ if (--temp == 0) {
+ dev_err(&u132->platform_dev->dev, "USB "
+ "HC takeover failed!(BIOS/SMM b"
+ "ug) control=%08X\n", control);
+ return -EBUSY;
+ }
+ goto check;
+ }
+ u132_usb_reset(u132);
+ }
+ }
+ retval = u132_write_pcimem(u132, intrdisable, OHCI_INTR_MIE);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ if (u132->num_ports == 0) {
+ u32 rh_a = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ u132->num_ports = rh_a & RH_A_NDP;
+ retval = read_roothub_info(u132);
+ if (retval)
+ return retval;
+ }
+ if (u132->num_ports > MAX_U132_PORTS) {
+ return -EINVAL;
+ }
+ return 0;
+}
+
+
+/* Start an OHCI controller, set the BUS operational
+* resets USB and controller
+* enable interrupts
+*/
+static int u132_run(struct u132 *u132)
+{
+ int retval;
+ u32 control;
+ u32 status;
+ u32 fminterval;
+ u32 periodicstart;
+ u32 cmdstatus;
+ u32 roothub_a;
+ int mask = OHCI_INTR_INIT;
+ int first = u132->hc_fminterval == 0;
+ int sleep_time = 0;
+ int reset_timeout = 30; /* ... allow extra time */
+ u132_disable(u132);
+ if (first) {
+ u32 temp;
+ retval = u132_read_pcimem(u132, fminterval, &temp);
+ if (retval)
+ return retval;
+ u132->hc_fminterval = temp & 0x3fff;
+ if (u132->hc_fminterval != FI) {
+ }
+ u132->hc_fminterval |= FSMP(u132->hc_fminterval) << 16;
+ }
+ retval = u132_read_pcimem(u132, control, &u132->hc_control);
+ if (retval)
+ return retval;
+ dev_info(&u132->platform_dev->dev, "resetting from state '%s', control "
+ "= %08X\n", hcfs2string(u132->hc_control & OHCI_CTRL_HCFS),
+ u132->hc_control);
+ switch (u132->hc_control & OHCI_CTRL_HCFS) {
+ case OHCI_USB_OPER:
+ sleep_time = 0;
+ break;
+ case OHCI_USB_SUSPEND:
+ case OHCI_USB_RESUME:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESUME;
+ sleep_time = 10;
+ break;
+ default:
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_USB_RESET;
+ sleep_time = 50;
+ break;
+ }
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ msleep(sleep_time);
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ if (!(roothub_a & RH_A_NPS)) {
+ int temp; /* power down each port */
+ for (temp = 0; temp < u132->num_ports; temp++) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[temp], RH_PS_LSDA);
+ if (retval)
+ return retval;
+ }
+ }
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ retry:retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_HCR);
+ if (retval)
+ return retval;
+ extra:{
+ retval = u132_read_pcimem(u132, cmdstatus, &status);
+ if (retval)
+ return retval;
+ if (0 != (status & OHCI_HCR)) {
+ if (--reset_timeout == 0) {
+ dev_err(&u132->platform_dev->dev, "USB HC reset"
+ " timed out!\n");
+ return -ENODEV;
+ } else {
+ msleep(5);
+ goto extra;
+ }
+ }
+ }
+ if (u132->flags & OHCI_QUIRK_INITRESET) {
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, ed_controlhead, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, ed_bulkhead, 0x11000000);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, hcca, 0x00000000);
+ if (retval)
+ return retval;
+ retval = u132_periodic_reinit(u132);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, fminterval, &fminterval);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, periodicstart, &periodicstart);
+ if (retval)
+ return retval;
+ if (0 == (fminterval & 0x3fff0000) || 0 == periodicstart) {
+ if (!(u132->flags & OHCI_QUIRK_INITRESET)) {
+ u132->flags |= OHCI_QUIRK_INITRESET;
+ goto retry;
+ } else
+ dev_err(&u132->platform_dev->dev, "init err(%08x %04x)"
+ "\n", fminterval, periodicstart);
+ } /* start controller operations */
+ u132->hc_control &= OHCI_CTRL_RWC;
+ u132->hc_control |= OHCI_CONTROL_INIT | OHCI_CTRL_BLE | OHCI_USB_OPER;
+ retval = u132_write_pcimem(u132, control, u132->hc_control);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem_byte(u132, cmdstatus, OHCI_BLF);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, cmdstatus, &cmdstatus);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_DRWE);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrstatus, mask);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, intrdisable,
+ OHCI_INTR_MIE | OHCI_INTR_OC | OHCI_INTR_RHSC | OHCI_INTR_FNO |
+ OHCI_INTR_UE | OHCI_INTR_RD | OHCI_INTR_SF | OHCI_INTR_WDH |
+ OHCI_INTR_SO);
+ if (retval)
+ return retval; /* handle root hub init quirks ... */
+ retval = u132_read_pcimem(u132, roothub.a, &roothub_a);
+ if (retval)
+ return retval;
+ roothub_a &= ~(RH_A_PSM | RH_A_OCPM);
+ if (u132->flags & OHCI_QUIRK_SUPERIO) {
+ roothub_a |= RH_A_NOCP;
+ roothub_a &= ~(RH_A_POTPGT | RH_A_NPS);
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ } else if ((u132->flags & OHCI_QUIRK_AMD756) || distrust_firmware) {
+ roothub_a |= RH_A_NPS;
+ retval = u132_write_pcimem(u132, roothub.a, roothub_a);
+ if (retval)
+ return retval;
+ }
+ retval = u132_write_pcimem(u132, roothub.status, RH_HS_LPSC);
+ if (retval)
+ return retval;
+ retval = u132_write_pcimem(u132, roothub.b,
+ (roothub_a & RH_A_NPS) ? 0 : RH_B_PPCM);
+ if (retval)
+ return retval;
+ retval = u132_read_pcimem(u132, control, &control);
+ if (retval)
+ return retval;
+ mdelay((roothub_a >> 23) & 0x1fe);
+ u132_to_hcd(u132)->state = HC_STATE_RUNNING;
+ return 0;
+}
+
+static void u132_hcd_stop(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ } else {
+ down(&u132->sw_lock);
+ msleep(100);
+ u132_power(u132, 0);
+ up(&u132->sw_lock);
+ }
+}
+
+static int u132_hcd_start(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else if (hcd->self.controller) {
+ int retval;
+ struct platform_device *pdev =
+ to_platform_device(hcd->self.controller);
+ u16 vendor = ((struct u132_platform_data *)
+ (pdev->dev.platform_data))->vendor;
+ u16 device = ((struct u132_platform_data *)
+ (pdev->dev.platform_data))->device;
+ down(&u132->sw_lock);
+ msleep(10);
+ if (vendor == PCI_VENDOR_ID_AMD && device == 0x740c) {
+ u132->flags = OHCI_QUIRK_AMD756;
+ } else if (vendor == PCI_VENDOR_ID_OPTI && device == 0xc861) {
+ dev_err(&u132->platform_dev->dev, "WARNING: OPTi workar"
+ "ounds unavailable\n");
+ } else if (vendor == PCI_VENDOR_ID_COMPAQ && device == 0xa0f8)
+ u132->flags |= OHCI_QUIRK_ZFMICRO;
+ retval = u132_run(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ msleep(100);
+ up(&u132->sw_lock);
+ return retval;
+ } else {
+ dev_err(&u132->platform_dev->dev, "platform_device missing\n");
+ return -ENODEV;
+ }
+}
+
+static int u132_hcd_reset(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval;
+ down(&u132->sw_lock);
+ retval = u132_init(u132);
+ if (retval) {
+ u132_disable(u132);
+ u132->going = 1;
+ }
+ up(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int create_endpoint_and_queue_int(struct u132 *u132,
+ struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ unsigned long irqs;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, msecs_to_jiffies(urb->interval));
+ return 0;
+}
+
+static int queue_int_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ endp->delayed = 1;
+ endp->jiffies = jiffies + msecs_to_jiffies(urb->interval);
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_bulk(struct u132 *u132,
+ struct u132_udev *udev, struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp, u8 address,
+ gfp_t mem_flags)
+{
+ int ring_number;
+ struct u132_ring *ring;
+ unsigned long irqs;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_endp_init_kref(u132, endp);
+ if (usb_pipein(urb->pipe)) {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 0, 0);
+ ring_number = 3;
+ endp->input = 1;
+ endp->output = 0;
+ udev->endp_number_in[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ } else {
+ endp->toggle_bits = 0x2;
+ usb_settoggle(udev->usb_device, usb_endp, 1, 0);
+ ring_number = 2;
+ endp->input = 0;
+ endp->output = 1;
+ udev->endp_number_out[usb_endp] = endp_number;
+ u132_udev_get_kref(u132, udev);
+ }
+ ring = endp->ring = &u132->ring[ring_number - 1];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+}
+
+static int queue_bulk_on_old_endpoint(struct u132 *u132, struct u132_udev *udev,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp, u8 address)
+{
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq = kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+}
+
+static int create_endpoint_and_queue_control(struct u132 *u132,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, u8 usb_addr, u8 usb_endp,
+ gfp_t mem_flags)
+{
+ struct u132_ring *ring;
+ u8 endp_number = ++u132->num_endpoints;
+ struct u132_endp *endp = hep->hcpriv = u132->endp[endp_number - 1] =
+ kmalloc(sizeof(struct u132_endp), mem_flags);
+ if (!endp) {
+ return -ENOMEM;
+ }
+ INIT_WORK(&endp->scheduler, u132_hcd_endp_work_scheduler, (void *)endp);
+ spin_lock_init(&endp->queue_lock.slock);
+ INIT_LIST_HEAD(&endp->urb_more);
+ ring = endp->ring = &u132->ring[0];
+ if (ring->curr_endp) {
+ list_add_tail(&endp->endp_ring, &ring->curr_endp->endp_ring);
+ } else {
+ INIT_LIST_HEAD(&endp->endp_ring);
+ ring->curr_endp = endp;
+ }
+ ring->length += 1;
+ endp->dequeueing = 0;
+ endp->edset_flush = 0;
+ endp->active = 0;
+ endp->delayed = 0;
+ endp->endp_number = endp_number;
+ endp->u132 = u132;
+ endp->hep = hep;
+ u132_endp_init_kref(u132, endp);
+ u132_endp_get_kref(u132, endp);
+ if (usb_addr == 0) {
+ unsigned long irqs;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_init_kref(u132, udev);
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ } else { /*(usb_addr > 0) */
+ unsigned long irqs;
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ endp->udev_number = address;
+ endp->usb_addr = usb_addr;
+ endp->usb_endp = usb_endp;
+ endp->input = 1;
+ endp->output = 1;
+ endp->pipetype = usb_pipetype(urb->pipe);
+ u132_udev_get_kref(u132, udev);
+ udev->enumeration = 2;
+ udev->endp_number_in[usb_endp] = endp_number;
+ udev->endp_number_out[usb_endp] = endp_number;
+ urb->hcpriv = u132;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ endp->queue_size = 1;
+ endp->queue_last = 0;
+ endp->queue_next = 0;
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] = urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+}
+
+static int queue_control_on_old_endpoint(struct u132 *u132,
+ struct usb_host_endpoint *hep, struct urb *urb,
+ struct usb_device *usb_dev, struct u132_endp *endp, u8 usb_addr,
+ u8 usb_endp)
+{
+ if (usb_addr == 0) {
+ if (usb_pipein(urb->pipe)) {
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ } else { /* usb_pipeout(urb->pipe) */
+ struct u132_addr *addr = &u132->addr[usb_dev->devnum];
+ int I = MAX_U132_UDEVS;
+ int i = 0;
+ while (--I > 0) {
+ struct u132_udev *udev = &u132->udev[++i];
+ if (udev->usb_device) {
+ continue;
+ } else {
+ udev->enumeration = 1;
+ u132->addr[0].address = i;
+ endp->udev_number = i;
+ udev->udev_number = i;
+ udev->usb_addr = usb_dev->devnum;
+ u132_udev_init_kref(u132, udev);
+ udev->endp_number_in[usb_endp] =
+ endp->endp_number;
+ u132_udev_get_kref(u132, udev);
+ udev->endp_number_out[usb_endp] =
+ endp->endp_number;
+ udev->usb_device = usb_dev;
+ ((u8 *) (urb->setup_packet))[2] =
+ addr->address = i;
+ u132_udev_get_kref(u132, udev);
+ break;
+ }
+ }
+ if (I == 0) {
+ dev_err(&u132->platform_dev->dev, "run out of d"
+ "evice space\n");
+ return -EINVAL;
+ }
+ urb->hcpriv = u132;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK &
+ endp->queue_last++] = urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq),
+ GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more,
+ &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+ } else { /*(usb_addr > 0) */
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ urb->hcpriv = u132;
+ if (udev->enumeration == 2) {
+ } else
+ udev->enumeration = 2;
+ if (endp->queue_size++ < ENDP_QUEUE_SIZE) {
+ endp->urb_list[ENDP_QUEUE_MASK & endp->queue_last++] =
+ urb;
+ } else {
+ struct u132_urbq *urbq =
+ kmalloc(sizeof(struct u132_urbq), GFP_ATOMIC);
+ if (urbq == NULL) {
+ endp->queue_size -= 1;
+ return -ENOMEM;
+ } else {
+ list_add_tail(&urbq->urb_more, &endp->urb_more);
+ urbq->urb = urb;
+ }
+ }
+ return 0;
+ }
+}
+
+static int u132_urb_enqueue(struct usb_hcd *hcd, struct usb_host_endpoint *hep,
+ struct urb *urb, gfp_t mem_flags)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (irqs_disabled()) {
+ if (__GFP_WAIT & mem_flags) {
+ printk(KERN_ERR "invalid context for function that migh"
+ "t sleep\n");
+ return -EINVAL;
+ }
+ }
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed urb="
+ "%p status=%d\n", urb, urb->status);
+ return -ESHUTDOWN;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ struct usb_device *usb_dev = urb->dev;
+ if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = hep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_int_on_old_endpoint(u132, udev,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp, address);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp,
+ msecs_to_jiffies(urb->interval))
+ ;
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else { /*(endp == NULL) */
+ return create_endpoint_and_queue_int(u132, udev,
+ hep, urb, usb_dev, usb_addr, usb_endp,
+ address, mem_flags);
+ }
+ } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+ dev_err(&u132->platform_dev->dev, "the hardware does no"
+ "t support PIPE_ISOCHRONOUS\n");
+ return -EINVAL;
+ } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ struct u132_endp *endp = hep->hcpriv;
+ urb->actual_length = 0;
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_bulk_on_old_endpoint(u132, udev,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp, address);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_bulk(u132,
+ udev, hep, urb, usb_dev, usb_addr,
+ usb_endp, address, mem_flags);
+ } else {
+ struct u132_endp *endp = hep->hcpriv;
+ u16 urb_size = 8;
+ u8 *b = urb->setup_packet;
+ int i = 0;
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int l = 0;
+ data[0] = 0;
+ while (urb_size-- > 0) {
+ if (i > m) {
+ } else if (i++ < m) {
+ int w = sprintf(d, " %02X", *b++);
+ d += w;
+ l += w;
+ } else
+ d += sprintf(d, " ..");
+ }
+ if (endp) {
+ unsigned long irqs;
+ int retval;
+ spin_lock_irqsave(&endp->queue_lock.slock,
+ irqs);
+ retval = queue_control_on_old_endpoint(u132,
+ hep, urb, usb_dev, endp, usb_addr,
+ usb_endp);
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ if (retval) {
+ return retval;
+ } else {
+ u132_endp_queue_work(u132, endp, 0);
+ return 0;
+ }
+ } else if (u132->num_endpoints == MAX_U132_ENDPS) {
+ return -EINVAL;
+ } else
+ return create_endpoint_and_queue_control(u132,
+ hep, urb, usb_dev, usb_addr, usb_endp,
+ mem_flags);
+ }
+ }
+}
+
+static int dequeue_from_overflow_chain(struct u132 *u132,
+ struct u132_endp *endp, struct urb *urb)
+{
+ struct list_head *scan;
+ struct list_head *head = &endp->urb_more;
+ list_for_each(scan, head) {
+ struct u132_urbq *urbq = list_entry(scan, struct u132_urbq,
+ urb_more);
+ if (urbq->urb == urb) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ list_del(scan);
+ endp->queue_size -= 1;
+ urb->error_count = 0;
+ urb->hcpriv = NULL;
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return 0;
+ } else
+ continue;
+ }
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]=%p ring"
+ "[%d] %c%c usb_endp=%d usb_addr=%d size=%d next=%04X last=%04X"
+ "\n", urb, endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ return -EINVAL;
+}
+
+static int u132_endp_urb_dequeue(struct u132 *u132, struct u132_endp *endp,
+ struct urb *urb)
+{
+ unsigned long irqs;
+ spin_lock_irqsave(&endp->queue_lock.slock, irqs);
+ if (endp->queue_size == 0) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in endp[%d]"
+ "=%p ring[%d] %c%c usb_endp=%d usb_addr=%d\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ', endp->output ? 'O' : ' ',
+ endp->usb_endp, endp->usb_addr);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ }
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK & endp->queue_next]) {
+ if (endp->active) {
+ endp->dequeueing = 1;
+ endp->edset_flush = 1;
+ u132_endp_queue_work(u132, endp, 0);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ urb->hcpriv = NULL;
+ return 0;
+ } else {
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ u132_hcd_abandon_urb(u132, endp, urb, urb->status);
+ return 0;
+ }
+ } else {
+ u16 queue_list = 0;
+ u16 queue_size = endp->queue_size;
+ u16 queue_scan = endp->queue_next;
+ struct urb **urb_slot = NULL;
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ if (urb == endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan]) {
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ break;
+ } else
+ continue;
+ }
+ while (++queue_list < ENDP_QUEUE_SIZE && --queue_size > 0) {
+ *urb_slot = endp->urb_list[ENDP_QUEUE_MASK &
+ ++queue_scan];
+ urb_slot = &endp->urb_list[ENDP_QUEUE_MASK &
+ queue_scan];
+ }
+ if (urb_slot) {
+ struct usb_hcd *hcd = u132_to_hcd(u132);
+ endp->queue_size -= 1;
+ if (list_empty(&endp->urb_more)) {
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ } else {
+ struct list_head *next = endp->urb_more.next;
+ struct u132_urbq *urbq = list_entry(next,
+ struct u132_urbq, urb_more);
+ list_del(next);
+ *urb_slot = urbq->urb;
+ spin_unlock_irqrestore(&endp->queue_lock.slock,
+ irqs);
+ kfree(urbq);
+ } urb->error_count = 0;
+ urb->hcpriv = NULL;
+ usb_hcd_giveback_urb(hcd, urb, NULL);
+ return 0;
+ } else if (list_empty(&endp->urb_more)) {
+ dev_err(&u132->platform_dev->dev, "urb=%p not found in "
+ "endp[%d]=%p ring[%d] %c%c usb_endp=%d usb_addr"
+ "=%d size=%d next=%04X last=%04X\n", urb,
+ endp->endp_number, endp, endp->ring->number,
+ endp->input ? 'I' : ' ',
+ endp->output ? 'O' : ' ', endp->usb_endp,
+ endp->usb_addr, endp->queue_size,
+ endp->queue_next, endp->queue_last);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return -EINVAL;
+ } else {
+ int retval = dequeue_from_overflow_chain(u132, endp,
+ urb);
+ spin_unlock_irqrestore(&endp->queue_lock.slock, irqs);
+ return retval;
+ }
+ }
+}
+
+static int u132_urb_dequeue(struct usb_hcd *hcd, struct urb *urb)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else {
+ u8 usb_addr = usb_pipedevice(urb->pipe);
+ u8 usb_endp = usb_pipeendpoint(urb->pipe);
+ u8 address = u132->addr[usb_addr].address;
+ struct u132_udev *udev = &u132->udev[address];
+ if (usb_pipein(urb->pipe)) {
+ u8 endp_number = udev->endp_number_in[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb);
+ } else {
+ u8 endp_number = udev->endp_number_out[usb_endp];
+ struct u132_endp *endp = u132->endp[endp_number - 1];
+ return u132_endp_urb_dequeue(u132, endp, urb);
+ }
+ }
+}
+
+static void u132_endpoint_disable(struct usb_hcd *hcd,
+ struct usb_host_endpoint *hep)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 2) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else {
+ struct u132_endp *endp = hep->hcpriv;
+ if (endp)
+ u132_endp_put_kref(u132, endp);
+ }
+}
+
+static int u132_get_frame(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int frame = 0;
+ dev_err(&u132->platform_dev->dev, "TODO: u132_get_frame\n");
+ msleep(100);
+ return frame;
+ }
+}
+
+static int u132_roothub_descriptor(struct u132 *u132,
+ struct usb_hub_descriptor *desc)
+{
+ int retval;
+ u16 temp;
+ u32 rh_a = -1;
+ u32 rh_b = -1;
+ retval = u132_read_pcimem(u132, roothub.a, &rh_a);
+ if (retval)
+ return retval;
+ desc->bDescriptorType = 0x29;
+ desc->bPwrOn2PwrGood = (rh_a & RH_A_POTPGT) >> 24;
+ desc->bHubContrCurrent = 0;
+ desc->bNbrPorts = u132->num_ports;
+ temp = 1 + (u132->num_ports / 8);
+ desc->bDescLength = 7 + 2 *temp;
+ temp = 0;
+ if (rh_a & RH_A_NPS)
+ temp |= 0x0002;
+ if (rh_a & RH_A_PSM)
+ temp |= 0x0001;
+ if (rh_a & RH_A_NOCP) {
+ temp |= 0x0010;
+ } else if (rh_a & RH_A_OCPM)
+ temp |= 0x0008;
+ desc->wHubCharacteristics = cpu_to_le16(temp);
+ retval = u132_read_pcimem(u132, roothub.b, &rh_b);
+ if (retval)
+ return retval;
+ memset(desc->bitmap, 0xff, sizeof(desc->bitmap));
+ desc->bitmap[0] = rh_b & RH_B_DR;
+ if (u132->num_ports > 7) {
+ desc->bitmap[1] = (rh_b & RH_B_DR) >> 8;
+ desc->bitmap[2] = 0xff;
+ } else
+ desc->bitmap[1] = 0xff;
+ return 0;
+}
+
+static int u132_roothub_status(struct u132 *u132, __le32 *desc)
+{
+ u32 rh_status = -1;
+ int ret_status = u132_read_pcimem(u132, roothub.status, &rh_status);
+ *desc = cpu_to_le32(rh_status);
+ return ret_status;
+}
+
+static int u132_roothub_portstatus(struct u132 *u132, __le32 *desc, u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port = wIndex - 1;
+ u32 rh_portstatus = -1;
+ int ret_portstatus = u132_read_pcimem(u132,
+ roothub.portstatus[port], &rh_portstatus);
+ *desc = cpu_to_le32(rh_portstatus);
+ if (*(u16 *) (desc + 2)) {
+ dev_info(&u132->platform_dev->dev, "Port %d Status Chan"
+ "ge = %08X\n", port, *desc);
+ }
+ return ret_portstatus;
+ }
+}
+
+
+/* this timer value might be vendor-specific ... */
+#define PORT_RESET_HW_MSEC 10
+#define PORT_RESET_MSEC 10
+/* wrap-aware logic morphed from <linux/jiffies.h> */
+#define tick_before(t1, t2) ((s16)(((s16)(t1))-((s16)(t2))) < 0)
+static int u132_roothub_portreset(struct u132 *u132, int port_index)
+{
+ int retval;
+ u32 fmnumber;
+ u16 now;
+ u16 reset_done;
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ reset_done = now + PORT_RESET_MSEC;
+ do {
+ u32 portstat;
+ do {
+ retval = u132_read_pcimem(u132,
+ roothub.portstatus[port_index], &portstat);
+ if (retval)
+ return retval;
+ if (RH_PS_PRS & portstat) {
+ continue;
+ } else
+ break;
+ } while (tick_before(now, reset_done));
+ if (RH_PS_PRS & portstat)
+ return -ENODEV;
+ if (RH_PS_CCS & portstat) {
+ if (RH_PS_PRSC & portstat) {
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index],
+ RH_PS_PRSC);
+ if (retval)
+ return retval;
+ }
+ } else
+ break; /* start the next reset,
+ sleep till it's probably done */
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ RH_PS_PRS);
+ if (retval)
+ return retval;
+ msleep(PORT_RESET_HW_MSEC);
+ retval = u132_read_pcimem(u132, fmnumber, &fmnumber);
+ if (retval)
+ return retval;
+ now = fmnumber;
+ } while (tick_before(now, reset_done));
+ return 0;
+}
+
+static int u132_roothub_setportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int retval;
+ int port_index = wIndex - 1;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_SUSPEND:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PSS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_POWER:
+ retval = u132_write_pcimem(u132,
+ roothub.portstatus[port_index], RH_PS_PPS);
+ if (retval)
+ return retval;
+ return 0;
+ case USB_PORT_FEAT_RESET:
+ retval = u132_roothub_portreset(u132, port_index);
+ if (retval)
+ return retval;
+ return 0;
+ default:
+ return -EPIPE;
+ }
+ }
+}
+
+static int u132_roothub_clearportfeature(struct u132 *u132, u16 wValue,
+ u16 wIndex)
+{
+ if (wIndex == 0 || wIndex > u132->num_ports) {
+ return -EINVAL;
+ } else {
+ int port_index = wIndex - 1;
+ u32 temp;
+ int retval;
+ struct u132_port *port = &u132->port[port_index];
+ port->Status &= ~(1 << wValue);
+ switch (wValue) {
+ case USB_PORT_FEAT_ENABLE:
+ temp = RH_PS_CCS;
+ break;
+ case USB_PORT_FEAT_C_ENABLE:
+ temp = RH_PS_PESC;
+ break;
+ case USB_PORT_FEAT_SUSPEND:
+ temp = RH_PS_POCI;
+ if ((u132->hc_control & OHCI_CTRL_HCFS)
+ != OHCI_USB_OPER) {
+ dev_err(&u132->platform_dev->dev, "TODO resume_"
+ "root_hub\n");
+ }
+ break;
+ case USB_PORT_FEAT_C_SUSPEND:
+ temp = RH_PS_PSSC;
+ break;
+ case USB_PORT_FEAT_POWER:
+ temp = RH_PS_LSDA;
+ break;
+ case USB_PORT_FEAT_C_CONNECTION:
+ temp = RH_PS_CSC;
+ break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+ temp = RH_PS_OCIC;
+ break;
+ case USB_PORT_FEAT_C_RESET:
+ temp = RH_PS_PRSC;
+ break;
+ default:
+ return -EPIPE;
+ }
+ retval = u132_write_pcimem(u132, roothub.portstatus[port_index],
+ temp);
+ if (retval)
+ return retval;
+ return 0;
+ }
+}
+
+
+/* the virtual root hub timer IRQ checks for hub status*/
+static int u132_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p has been remov"
+ "ed %d\n", hcd, u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device hcd=%p is being remov"
+ "ed\n", hcd);
+ dump_stack();
+ return -ESHUTDOWN;
+ } else {
+ int i, changed = 0, length = 1;
+ if (u132->flags & OHCI_QUIRK_AMD756) {
+ if ((u132->hc_roothub_a & RH_A_NDP) > MAX_ROOT_PORTS) {
+ dev_err(&u132->platform_dev->dev, "bogus NDP, r"
+ "ereads as NDP=%d\n",
+ u132->hc_roothub_a & RH_A_NDP);
+ goto done;
+ }
+ }
+ if (u132->hc_roothub_status & (RH_HS_LPSC | RH_HS_OCIC)) {
+ buf[0] = changed = 1;
+ } else
+ buf[0] = 0;
+ if (u132->num_ports > 7) {
+ buf[1] = 0;
+ length++;
+ }
+ for (i = 0; i < u132->num_ports; i++) {
+ if (u132->hc_roothub_portstatus[i] & (RH_PS_CSC |
+ RH_PS_PESC | RH_PS_PSSC | RH_PS_OCIC |
+ RH_PS_PRSC)) {
+ changed = 1;
+ if (i < 7) {
+ buf[0] |= 1 << (i + 1);
+ } else
+ buf[1] |= 1 << (i - 7);
+ continue;
+ }
+ if (!(u132->hc_roothub_portstatus[i] & RH_PS_CCS)) {
+ continue;
+ }
+ if ((u132->hc_roothub_portstatus[i] & RH_PS_PSS)) {
+ continue;
+ }
+ }
+ done:return changed ? length : 0;
+ }
+}
+
+static int u132_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ down(&u132->sw_lock);
+ switch (typeReq) {
+ case ClearHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case SetHubFeature:
+ switch (wValue) {
+ case C_HUB_OVER_CURRENT:
+ case C_HUB_LOCAL_POWER:
+ break;
+ default:
+ goto stall;
+ }
+ break;
+ case ClearPortFeature:{
+ retval = u132_roothub_clearportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubDescriptor:{
+ retval = u132_roothub_descriptor(u132,
+ (struct usb_hub_descriptor *)buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetHubStatus:{
+ retval = u132_roothub_status(u132,
+ (__le32 *) buf);
+ if (retval)
+ goto error;
+ break;
+ }
+ case GetPortStatus:{
+ retval = u132_roothub_portstatus(u132,
+ (__le32 *) buf, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ case SetPortFeature:{
+ retval = u132_roothub_setportfeature(u132,
+ wValue, wIndex);
+ if (retval)
+ goto error;
+ break;
+ }
+ default:
+ goto stall;
+ error:u132_disable(u132);
+ u132->going = 1;
+ break;
+ stall:retval = -EPIPE;
+ break;
+ }
+ up(&u132->sw_lock);
+ return retval;
+ }
+}
+
+static int u132_start_port_reset(struct usb_hcd *hcd, unsigned port_num)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static void u132_hub_irq_enable(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ } else if (u132->going > 0)
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+}
+
+
+#ifdef CONFIG_PM
+static int u132_hcd_suspend(struct usb_hcd *hcd, pm_message_t message)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_hcd_resume(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_bus_suspend(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+static int u132_bus_resume(struct usb_hcd *hcd)
+{
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else
+ return 0;
+}
+
+#else
+#define u132_hcd_suspend NULL
+#define u132_hcd_resume NULL
+#define u132_bus_suspend NULL
+#define u132_bus_resume NULL
+#endif
+static struct hc_driver u132_hc_driver = {
+ .description = hcd_name,
+ .hcd_priv_size = sizeof(struct u132),
+ .irq = NULL,
+ .flags = HCD_USB11 | HCD_MEMORY,
+ .reset = u132_hcd_reset,
+ .start = u132_hcd_start,
+ .suspend = u132_hcd_suspend,
+ .resume = u132_hcd_resume,
+ .stop = u132_hcd_stop,
+ .urb_enqueue = u132_urb_enqueue,
+ .urb_dequeue = u132_urb_dequeue,
+ .endpoint_disable = u132_endpoint_disable,
+ .get_frame_number = u132_get_frame,
+ .hub_status_data = u132_hub_status_data,
+ .hub_control = u132_hub_control,
+ .bus_suspend = u132_bus_suspend,
+ .bus_resume = u132_bus_resume,
+ .start_port_reset = u132_start_port_reset,
+ .hub_irq_enable = u132_hub_irq_enable,
+};
+
+/*
+* This function may be called by the USB core whilst the "usb_all_devices_rwsem"
+* is held for writing, thus this module must not call usb_remove_hcd()
+* synchronously - but instead should immediately stop activity to the
+* device and ansynchronously call usb_remove_hcd()
+*/
+static int __devexit u132_remove(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ if (hcd) {
+ struct u132 *u132 = hcd_to_u132(hcd);
+ dump_stack();
+ if (u132->going++ > 1) {
+ return -ENODEV;
+ } else {
+ int rings = MAX_U132_RINGS;
+ int endps = MAX_U132_ENDPS;
+ msleep(100);
+ down(&u132->sw_lock);
+ u132_monitor_cancel_work(u132);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ u132_ring_cancel_work(u132, ring);
+ } while (endps-- > 0) {
+ struct u132_endp *endp = u132->endp[endps];
+ if (endp)
+ u132_endp_cancel_work(u132, endp);
+ }
+ u132->going += 1;
+ printk(KERN_INFO "removing device u132.%d\n",
+ u132->sequence_num);
+ up(&u132->sw_lock);
+ usb_remove_hcd(hcd);
+ u132_u132_put_kref(u132);
+ return 0;
+ }
+ } else
+ return 0;
+}
+
+static void u132_initialise(struct u132 *u132, struct platform_device *pdev)
+{
+ int rings = MAX_U132_RINGS;
+ int ports = MAX_U132_PORTS;
+ int addrs = MAX_U132_ADDRS;
+ int udevs = MAX_U132_UDEVS;
+ int endps = MAX_U132_ENDPS;
+ u132->board = pdev->dev.platform_data;
+ u132->platform_dev = pdev;
+ u132->power = 0;
+ u132->reset = 0;
+ init_MUTEX(&u132->sw_lock);
+ init_MUTEX(&u132->scheduler_lock);
+ while (rings-- > 0) {
+ struct u132_ring *ring = &u132->ring[rings];
+ ring->u132 = u132;
+ ring->number = rings + 1;
+ ring->length = 0;
+ ring->curr_endp = NULL;
+ INIT_WORK(&ring->scheduler, u132_hcd_ring_work_scheduler,
+ (void *)ring);
+ } down(&u132->sw_lock);
+ INIT_WORK(&u132->monitor, u132_hcd_monitor_work, (void *)u132);
+ while (ports-- > 0) {
+ struct u132_port *port = &u132->port[ports];
+ port->u132 = u132;
+ port->reset = 0;
+ port->enable = 0;
+ port->power = 0;
+ port->Status = 0;
+ } while (addrs-- > 0) {
+ struct u132_addr *addr = &u132->addr[addrs];
+ addr->address = 0;
+ } while (udevs-- > 0) {
+ struct u132_udev *udev = &u132->udev[udevs];
+ int i = ARRAY_SIZE(udev->endp_number_in);
+ int o = ARRAY_SIZE(udev->endp_number_out);
+ udev->usb_device = NULL;
+ udev->udev_number = 0;
+ udev->usb_addr = 0;
+ udev->portnumber = 0;
+ while (i-- > 0) {
+ udev->endp_number_in[i] = 0;
+ }
+ while (o-- > 0) {
+ udev->endp_number_out[o] = 0;
+ }
+ }
+ while (endps-- > 0) {
+ u132->endp[endps] = NULL;
+ }
+ up(&u132->sw_lock);
+ return;
+}
+
+static int __devinit u132_probe(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd;
+ msleep(100);
+ if (u132_exiting > 0) {
+ return -ENODEV;
+ } /* refuse to confuse usbcore */
+ if (pdev->dev.dma_mask) {
+ return -EINVAL;
+ }
+ hcd = usb_create_hcd(&u132_hc_driver, &pdev->dev, pdev->dev.bus_id);
+ if (!hcd) {
+ printk(KERN_ERR "failed to create the usb hcd struct for U132\n"
+ );
+ ftdi_elan_gone_away(pdev);
+ return -ENOMEM;
+ } else {
+ int retval = 0;
+ struct u132 *u132 = hcd_to_u132(hcd);
+ hcd->rsrc_start = 0;
+ down(&u132_module_lock);
+ list_add_tail(&u132->u132_list, &u132_static_list);
+ u132->sequence_num = ++u132_instances;
+ up(&u132_module_lock);
+ u132_u132_init_kref(u132);
+ u132_initialise(u132, pdev);
+ hcd->product_desc = "ELAN U132 Host Controller";
+ retval = usb_add_hcd(hcd, 0, 0);
+ if (retval != 0) {
+ dev_err(&u132->platform_dev->dev, "init error %d\n",
+ retval);
+ u132_u132_put_kref(u132);
+ return retval;
+ } else {
+ u132_monitor_queue_work(u132, 100);
+ return 0;
+ }
+ }
+}
+
+
+#ifdef CONFIG_PM
+/* for this device there's no useful distinction between the controller
+* and its root hub, except that the root hub only gets direct PM calls
+* when CONFIG_USB_SUSPEND is enabled.
+*/
+static int u132_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ if (state.event == PM_EVENT_FREEZE) {
+ retval = u132_bus_suspend(hcd);
+ } else if (state.event == PM_EVENT_SUSPEND) {
+ int ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 0);
+ }
+ }
+ if (retval == 0)
+ pdev->dev.power.power_state = state;
+ return retval;
+ }
+}
+
+static int u132_resume(struct platform_device *pdev)
+{
+ struct usb_hcd *hcd = platform_get_drvdata(pdev);
+ struct u132 *u132 = hcd_to_u132(hcd);
+ if (u132->going > 1) {
+ dev_err(&u132->platform_dev->dev, "device has been removed %d\n"
+ , u132->going);
+ return -ENODEV;
+ } else if (u132->going > 0) {
+ dev_err(&u132->platform_dev->dev, "device is being removed\n");
+ return -ESHUTDOWN;
+ } else {
+ int retval = 0;
+ if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) {
+ int ports = MAX_U132_PORTS;
+ while (ports-- > 0) {
+ port_power(u132, ports, 1);
+ }
+ retval = 0;
+ } else {
+ pdev->dev.power.power_state = PMSG_ON;
+ retval = u132_bus_resume(hcd);
+ }
+ return retval;
+ }
+}
+
+#else
+#define u132_suspend NULL
+#define u132_resume NULL
+#endif
+/*
+* this driver is loaded explicitely by ftdi_u132
+*
+* the platform_driver struct is static because it is per type of module
+*/
+static struct platform_driver u132_platform_driver = {
+ .probe = u132_probe,
+ .remove = __devexit_p(u132_remove),
+ .suspend = u132_suspend,
+ .resume = u132_resume,
+ .driver = {
+ .name = (char *)hcd_name,
+ .owner = THIS_MODULE,
+ },
+};
+static int __init u132_hcd_init(void)
+{
+ int retval;
+ INIT_LIST_HEAD(&u132_static_list);
+ u132_instances = 0;
+ u132_exiting = 0;
+ init_MUTEX(&u132_module_lock);
+ if (usb_disabled())
+ return -ENODEV;
+ printk(KERN_INFO "driver %s built at %s on %s\n", hcd_name, __TIME__,
+ __DATE__);
+ workqueue = create_singlethread_workqueue("u132");
+ retval = platform_driver_register(&u132_platform_driver);
+ return retval;
+}
+
+
+module_init(u132_hcd_init);
+static void __exit u132_hcd_exit(void)
+{
+ struct u132 *u132;
+ struct u132 *temp;
+ down(&u132_module_lock);
+ u132_exiting += 1;
+ up(&u132_module_lock);
+ list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
+ platform_device_unregister(u132->platform_dev);
+ } platform_driver_unregister(&u132_platform_driver);
+ printk(KERN_INFO "u132-hcd driver deregistered\n");
+ wait_event(u132_hcd_wait, u132_instances == 0);
+ flush_workqueue(workqueue);
+ destroy_workqueue(workqueue);
+}
+
+
+module_exit(u132_hcd_exit);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/host/uhci-debug.c b/drivers/usb/host/uhci-debug.c
index dc286a48caf..e345f15b7d8 100644
--- a/drivers/usb/host/uhci-debug.c
+++ b/drivers/usb/host/uhci-debug.c
@@ -16,7 +16,7 @@
#include "uhci-hcd.h"
-#define uhci_debug_operations (* (struct file_operations *) NULL)
+#define uhci_debug_operations (* (const struct file_operations *) NULL)
static struct dentry *uhci_debugfs_root;
#ifdef DEBUG
@@ -428,7 +428,7 @@ struct uhci_debug {
static int uhci_debug_open(struct inode *inode, struct file *file)
{
- struct uhci_hcd *uhci = inode->u.generic_ip;
+ struct uhci_hcd *uhci = inode->i_private;
struct uhci_debug *up;
int ret = -ENOMEM;
unsigned long flags;
@@ -500,7 +500,7 @@ static int uhci_debug_release(struct inode *inode, struct file *file)
}
#undef uhci_debug_operations
-static struct file_operations uhci_debug_operations = {
+static const struct file_operations uhci_debug_operations = {
.owner = THIS_MODULE,
.open = uhci_debug_open,
.llseek = uhci_debug_lseek,
diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c
index 4151f618602..eb4eab98e8b 100644
--- a/drivers/usb/host/uhci-hcd.c
+++ b/drivers/usb/host/uhci-hcd.c
@@ -734,6 +734,10 @@ static int uhci_suspend(struct usb_hcd *hcd, pm_message_t message)
/* FIXME: Enable non-PME# remote wakeup? */
+ /* make sure snapshot being resumed re-enumerates everything */
+ if (message.event == PM_EVENT_PRETHAW)
+ uhci_hc_died(uhci);
+
done_okay:
clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
done:
@@ -909,8 +913,7 @@ static int __init uhci_hcd_init(void)
return 0;
init_failed:
- if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_privs were freed!");
+ kmem_cache_destroy(uhci_up_cachep);
up_failed:
debugfs_remove(uhci_debugfs_root);
@@ -926,10 +929,7 @@ errbuf_failed:
static void __exit uhci_hcd_cleanup(void)
{
pci_unregister_driver(&uhci_pci_driver);
-
- if (kmem_cache_destroy(uhci_up_cachep))
- warn("not all urb_privs were freed!");
-
+ kmem_cache_destroy(uhci_up_cachep);
debugfs_remove(uhci_debugfs_root);
kfree(errbuf);
}
diff --git a/drivers/usb/host/uhci-hub.c b/drivers/usb/host/uhci-hub.c
index c545ef92fe2..16fb72eb6fc 100644
--- a/drivers/usb/host/uhci-hub.c
+++ b/drivers/usb/host/uhci-hub.c
@@ -84,6 +84,7 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
unsigned long port_addr)
{
int status;
+ int i;
if (inw(port_addr) & (USBPORTSC_SUSP | USBPORTSC_RD)) {
CLR_RH_PORTSTAT(USBPORTSC_SUSP | USBPORTSC_RD);
@@ -92,9 +93,14 @@ static void uhci_finish_suspend(struct uhci_hcd *uhci, int port,
/* The controller won't actually turn off the RD bit until
* it has had a chance to send a low-speed EOP sequence,
- * which takes 3 bit times (= 2 microseconds). We'll delay
- * slightly longer for good luck. */
- udelay(4);
+ * which is supposed to take 3 bit times (= 2 microseconds).
+ * Experiments show that some controllers take longer, so
+ * we'll poll for completion. */
+ for (i = 0; i < 10; ++i) {
+ if (!(inw(port_addr) & USBPORTSC_RD))
+ break;
+ udelay(1);
+ }
}
clear_bit(port, &uhci->resuming_ports);
}
diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c
index 08daf400f98..ca6305c1d64 100644
--- a/drivers/usb/image/mdc800.c
+++ b/drivers/usb/image/mdc800.c
@@ -424,7 +424,7 @@ static void mdc800_usb_download_notify (struct urb *urb, struct pt_regs *res)
***************************************************************************/
static struct usb_driver mdc800_usb_driver;
-static struct file_operations mdc800_device_ops;
+static const struct file_operations mdc800_device_ops;
static struct usb_class_driver mdc800_class = {
.name = "mdc800%d",
.fops = &mdc800_device_ops,
@@ -941,7 +941,7 @@ static ssize_t mdc800_device_write (struct file *file, const char __user *buf, s
****************************************************************************/
/* File Operations of this drivers */
-static struct file_operations mdc800_device_ops =
+static const struct file_operations mdc800_device_ops =
{
.owner = THIS_MODULE,
.read = mdc800_device_read,
diff --git a/drivers/usb/image/microtek.c b/drivers/usb/image/microtek.c
index b2bafc37c41..5f861331932 100644
--- a/drivers/usb/image/microtek.c
+++ b/drivers/usb/image/microtek.c
@@ -225,7 +225,7 @@ static inline void mts_debug_dump(struct mts_desc* desc) {
}
-static inline void mts_show_command(Scsi_Cmnd *srb)
+static inline void mts_show_command(struct scsi_cmnd *srb)
{
char *what = NULL;
@@ -309,7 +309,7 @@ static inline void mts_show_command(Scsi_Cmnd *srb)
#else
-static inline void mts_show_command(Scsi_Cmnd * dummy)
+static inline void mts_show_command(struct scsi_cmnd * dummy)
{
}
@@ -338,7 +338,7 @@ static int mts_slave_configure (struct scsi_device *s)
return 0;
}
-static int mts_scsi_abort (Scsi_Cmnd *srb)
+static int mts_scsi_abort(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
@@ -349,7 +349,7 @@ static int mts_scsi_abort (Scsi_Cmnd *srb)
return FAILED;
}
-static int mts_scsi_host_reset (Scsi_Cmnd *srb)
+static int mts_scsi_host_reset(struct scsi_cmnd *srb)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int result, rc;
@@ -366,8 +366,8 @@ static int mts_scsi_host_reset (Scsi_Cmnd *srb)
return result ? FAILED : SUCCESS;
}
-static
-int mts_scsi_queuecommand (Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback );
+static int
+mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback);
static void mts_transfer_cleanup( struct urb *transfer );
static void mts_do_sg(struct urb * transfer, struct pt_regs *regs);
@@ -537,7 +537,7 @@ static const unsigned char mts_direction[256/8] = {
#define MTS_DIRECTION_IS_IN(x) ((mts_direction[x>>3] >> (x & 7)) & 1)
static void
-mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
+mts_build_transfer_context(struct scsi_cmnd *srb, struct mts_desc* desc)
{
int pipe;
struct scatterlist * sg;
@@ -588,8 +588,8 @@ mts_build_transfer_context( Scsi_Cmnd *srb, struct mts_desc* desc )
}
-static
-int mts_scsi_queuecommand( Scsi_Cmnd *srb, mts_scsi_cmnd_callback callback )
+static int
+mts_scsi_queuecommand(struct scsi_cmnd *srb, mts_scsi_cmnd_callback callback)
{
struct mts_desc* desc = (struct mts_desc*)(srb->device->host->hostdata[0]);
int err = 0;
diff --git a/drivers/usb/image/microtek.h b/drivers/usb/image/microtek.h
index 926d4bdc674..d5d62a93905 100644
--- a/drivers/usb/image/microtek.h
+++ b/drivers/usb/image/microtek.h
@@ -8,14 +8,14 @@
*
*/
-typedef void (*mts_scsi_cmnd_callback)(Scsi_Cmnd *);
+typedef void (*mts_scsi_cmnd_callback)(struct scsi_cmnd *);
struct mts_transfer_context
{
struct mts_desc* instance;
mts_scsi_cmnd_callback final_callback;
- Scsi_Cmnd *srb;
+ struct scsi_cmnd *srb;
void* data;
unsigned data_length;
diff --git a/drivers/usb/input/Kconfig b/drivers/usb/input/Kconfig
index 650103bc961..21cd2264008 100644
--- a/drivers/usb/input/Kconfig
+++ b/drivers/usb/input/Kconfig
@@ -60,16 +60,17 @@ config HID_FF
If unsure, say N.
config HID_PID
- bool "PID Devices (Microsoft Sidewinder Force Feedback 2)"
+ bool "PID device support"
depends on HID_FF
help
- Say Y here if you have a PID-compliant joystick and wish to enable force
- feedback for it. The Microsoft Sidewinder Force Feedback 2 is one such
- device.
+ Say Y here if you have a PID-compliant device and wish to enable force
+ feedback for it. Microsoft Sidewinder Force Feedback 2 is one of such
+ devices.
config LOGITECH_FF
bool "Logitech WingMan *3D support"
depends on HID_FF
+ select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have one of these devices:
- Logitech WingMan Cordless RumblePad
@@ -81,12 +82,21 @@ config LOGITECH_FF
config THRUSTMASTER_FF
bool "ThrustMaster FireStorm Dual Power 2 support (EXPERIMENTAL)"
depends on HID_FF && EXPERIMENTAL
+ select INPUT_FF_MEMLESS if USB_HID
help
Say Y here if you have a THRUSTMASTER FireStore Dual Power 2,
and want to enable force feedback support for it.
Note: if you say N here, this device will still be supported, but without
force feedback.
+config ZEROPLUS_FF
+ bool "Zeroplus based game controller support"
+ depends on HID_FF
+ select INPUT_FF_MEMLESS if USB_HID
+ help
+ Say Y here if you have a Zeroplus based game controller and want to
+ enable force feedback for it.
+
config USB_HIDDEV
bool "/dev/hiddev raw HID device support"
depends on USB_HID
@@ -205,10 +215,12 @@ config USB_TOUCHSCREEN
depends on USB && INPUT
---help---
USB Touchscreen driver for:
- - eGalax Touchkit USB
+ - eGalax Touchkit USB (also includes eTurboTouch CT-410/510/700)
- PanJit TouchSet USB
- - 3M MicroTouch USB
+ - 3M MicroTouch USB (EX II series)
- ITM
+ - some other eTurboTouch
+ - Gunze AHL61
Have a look at <http://linux.chapter7.ch/touchkit/> for
a usage description and the required user-space stuff.
@@ -218,7 +230,7 @@ config USB_TOUCHSCREEN
config USB_TOUCHSCREEN_EGALAX
default y
- bool "eGalax device support" if EMBEDDED
+ bool "eGalax, eTurboTouch CT-410/510/700 device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_PANJIT
@@ -228,7 +240,7 @@ config USB_TOUCHSCREEN_PANJIT
config USB_TOUCHSCREEN_3M
default y
- bool "3M/Microtouch device support" if EMBEDDED
+ bool "3M/Microtouch EX II series device support" if EMBEDDED
depends on USB_TOUCHSCREEN
config USB_TOUCHSCREEN_ITM
@@ -236,6 +248,16 @@ config USB_TOUCHSCREEN_ITM
bool "ITM device support" if EMBEDDED
depends on USB_TOUCHSCREEN
+config USB_TOUCHSCREEN_ETURBO
+ default y
+ bool "eTurboTouch (non-eGalax compatible) device support" if EMBEDDED
+ depends on USB_TOUCHSCREEN
+
+config USB_TOUCHSCREEN_GUNZE
+ default y
+ bool "Gunze AHL61 device support" if EMBEDDED
+ depends on USB_TOUCHSCREEN
+
config USB_YEALINK
tristate "Yealink usb-p1k voip phone"
depends on USB && INPUT && EXPERIMENTAL
@@ -326,3 +348,13 @@ config USB_APPLETOUCH
To compile this driver as a module, choose M here: the
module will be called appletouch.
+
+config USB_TRANCEVIBRATOR
+ tristate "PlayStation 2 Trance Vibrator driver support"
+ depends on USB
+ help
+ Say Y here if you want to connect a PlayStation 2 Trance Vibrator
+ device to your computer's USB port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called trancevibrator.
diff --git a/drivers/usb/input/Makefile b/drivers/usb/input/Makefile
index 764114529c5..295f459d107 100644
--- a/drivers/usb/input/Makefile
+++ b/drivers/usb/input/Makefile
@@ -3,6 +3,7 @@
#
# Multipart objects.
+wacom-objs := wacom_sys.o wacom_wac.o
usbhid-objs := hid-core.o
# Optional parts of multipart objects.
@@ -14,7 +15,7 @@ ifeq ($(CONFIG_USB_HIDINPUT),y)
usbhid-objs += hid-input.o
endif
ifeq ($(CONFIG_HID_PID),y)
- usbhid-objs += pid.o
+ usbhid-objs += hid-pidff.o
endif
ifeq ($(CONFIG_LOGITECH_FF),y)
usbhid-objs += hid-lgff.o
@@ -22,6 +23,9 @@ endif
ifeq ($(CONFIG_THRUSTMASTER_FF),y)
usbhid-objs += hid-tmff.o
endif
+ifeq ($(CONFIG_ZEROPLUS_FF),y)
+ usbhid-objs += hid-zpff.o
+endif
ifeq ($(CONFIG_HID_FF),y)
usbhid-objs += hid-ff.o
endif
@@ -44,6 +48,7 @@ obj-$(CONFIG_USB_ACECAD) += acecad.o
obj-$(CONFIG_USB_YEALINK) += yealink.o
obj-$(CONFIG_USB_XPAD) += xpad.o
obj-$(CONFIG_USB_APPLETOUCH) += appletouch.o
+obj-$(CONFIG_USB_TRANCEVIBRATOR) += trancevibrator.o
ifeq ($(CONFIG_USB_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
diff --git a/drivers/usb/input/acecad.c b/drivers/usb/input/acecad.c
index 18c10e150ef..d83603ba40a 100644
--- a/drivers/usb/input/acecad.c
+++ b/drivers/usb/input/acecad.c
@@ -141,10 +141,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
endpoint = &interface->endpoint[0].desc;
- if (!(endpoint->bEndpointAddress & 0x80))
- return -ENODEV;
-
- if ((endpoint->bmAttributes & 3) != 3)
+ if (!usb_endpoint_is_int_in(endpoint))
return -ENODEV;
pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
diff --git a/drivers/usb/input/appletouch.c b/drivers/usb/input/appletouch.c
index 044faa07e29..0aa9cc2bfd6 100644
--- a/drivers/usb/input/appletouch.c
+++ b/drivers/usb/input/appletouch.c
@@ -436,10 +436,7 @@ static int atp_probe(struct usb_interface *iface, const struct usb_device_id *id
iface_desc = iface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
endpoint = &iface_desc->endpoint[i].desc;
- if (!int_in_endpointAddr &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_INT)) {
+ if (!int_in_endpointAddr && usb_endpoint_is_int_in(endpoint)) {
/* we found an interrupt in endpoint */
int_in_endpointAddr = endpoint->bEndpointAddress;
break;
diff --git a/drivers/usb/input/ati_remote.c b/drivers/usb/input/ati_remote.c
index 3719fcb04b8..3558d7ed99b 100644
--- a/drivers/usb/input/ati_remote.c
+++ b/drivers/usb/input/ati_remote.c
@@ -732,12 +732,8 @@ static int ati_remote_probe(struct usb_interface *interface, const struct usb_de
endpoint_in = &iface_host->endpoint[0].desc;
endpoint_out = &iface_host->endpoint[1].desc;
- if (!(endpoint_in->bEndpointAddress & USB_DIR_IN)) {
- err("%s: Unexpected endpoint_in->bEndpointAddress\n", __FUNCTION__);
- return -ENODEV;
- }
- if ((endpoint_in->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT) {
- err("%s: Unexpected endpoint_in->bmAttributes\n", __FUNCTION__);
+ if (!usb_endpoint_is_int_in(endpoint_in)) {
+ err("%s: Unexpected endpoint_in\n", __FUNCTION__);
return -ENODEV;
}
if (le16_to_cpu(endpoint_in->wMaxPacketSize) == 0) {
diff --git a/drivers/usb/input/hid-core.c b/drivers/usb/input/hid-core.c
index 3305fb6079e..e0fd11605b4 100644
--- a/drivers/usb/input/hid-core.c
+++ b/drivers/usb/input/hid-core.c
@@ -543,8 +543,6 @@ static void hid_free_device(struct hid_device *device)
{
unsigned i,j;
- hid_ff_exit(device);
-
for (i = 0; i < HID_REPORT_TYPES; i++) {
struct hid_report_enum *report_enum = device->report_enum + i;
@@ -1023,7 +1021,8 @@ static void hid_irq_in(struct urb *urb, struct pt_regs *regs)
return;
case -EILSEQ: /* protocol error or unplug */
case -EPROTO: /* protocol error or unplug */
- case -ETIMEDOUT: /* NAK */
+ case -ETIME: /* protocol error or unplug */
+ case -ETIMEDOUT: /* Should never happen, but... */
clear_bit(HID_IN_RUNNING, &hid->iofl);
hid_io_error(hid);
return;
@@ -1108,7 +1107,7 @@ int hid_set_field(struct hid_field *field, unsigned offset, __s32 value)
/*
* Find a report field with a specified HID usage.
*/
-
+#if 0
struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type)
{
struct hid_report *report;
@@ -1120,6 +1119,7 @@ struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_u
return report->field[i];
return NULL;
}
+#endif /* 0 */
static int hid_submit_out(struct hid_device *hid)
{
@@ -1535,13 +1535,17 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_GLAB 0x06c2
#define USB_DEVICE_ID_4_PHIDGETSERVO_30 0x0038
#define USB_DEVICE_ID_1_PHIDGETSERVO_30 0x0039
-#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
#define USB_DEVICE_ID_0_0_4_IF_KIT 0x0040
+#define USB_DEVICE_ID_0_16_16_IF_KIT 0x0044
+#define USB_DEVICE_ID_8_8_8_IF_KIT 0x0045
+#define USB_DEVICE_ID_0_8_7_IF_KIT 0x0051
#define USB_DEVICE_ID_0_8_8_IF_KIT 0x0053
+#define USB_DEVICE_ID_PHIDGET_MOTORCONTROL 0x0058
#define USB_VENDOR_ID_WISEGROUP 0x0925
#define USB_DEVICE_ID_1_PHIDGETSERVO_20 0x8101
#define USB_DEVICE_ID_4_PHIDGETSERVO_20 0x8104
+#define USB_DEVICE_ID_8_8_4_IF_KIT 0x8201
#define USB_DEVICE_ID_DUAL_USB_JOYPAD 0x8866
#define USB_VENDOR_ID_WISEGROUP_LTD 0x6677
@@ -1591,6 +1595,13 @@ void hid_init_reports(struct hid_device *hid)
#define USB_VENDOR_ID_YEALINK 0x6993
#define USB_DEVICE_ID_YEALINK_P1K_P4K_B2K 0xb001
+
+#define USB_VENDOR_ID_ALCOR 0x058f
+#define USB_DEVICE_ID_ALCOR_USBRS232 0x9720
+
+#define USB_VENDOR_ID_SUN 0x0430
+#define USB_DEVICE_ID_RARITAN_KVM_DONGLE 0xcdab
+
/*
* Alphabetically sorted blacklist by quirk type.
*/
@@ -1608,6 +1619,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_22, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_23, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_AIPTEK, USB_DEVICE_ID_AIPTEK_24, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ALCOR, USB_DEVICE_ID_ALCOR_USBRS232, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_BERKSHIRE, USB_DEVICE_ID_BERKSHIRE_PCWD, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW40, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_CODEMERCS, USB_DEVICE_ID_CODEMERCS_IOW24, HID_QUIRK_IGNORE },
@@ -1620,9 +1632,12 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ESSENTIAL_REALITY, USB_DEVICE_ID_ESSENTIAL_REALITY_P5, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_4_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_1_PHIDGETSERVO_30, HID_QUIRK_IGNORE },
- { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_0_4_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_16_16_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_8_8_8_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_7_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GLAB, USB_DEVICE_ID_0_8_8_IF_KIT, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_GLAB, USB_DEVICE_ID_PHIDGET_MOTORCONTROL, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_POWERMATE, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GRIFFIN, USB_DEVICE_ID_SOUNDKNOB, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_GTCO, USB_DEVICE_ID_GTCO_90, HID_QUIRK_IGNORE },
@@ -1690,7 +1705,11 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_MGE, USB_DEVICE_ID_MGE_UPS1, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 30, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 100, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 108, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 118, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 200, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 300, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ONTRAK, USB_DEVICE_ID_ONTRAK_ADU100 + 400, HID_QUIRK_IGNORE },
@@ -1701,6 +1720,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_4_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20, HID_QUIRK_IGNORE },
+ { USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K, HID_QUIRK_IGNORE },
{ USB_VENDOR_ID_ACECAD, USB_DEVICE_ID_ACECAD_FLAIR, HID_QUIRK_IGNORE },
@@ -1711,6 +1731,7 @@ static const struct hid_blacklist {
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_2PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVM, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_ATEN, USB_DEVICE_ID_ATEN_4PORTKVMC, HID_QUIRK_NOGET },
+ { USB_VENDOR_ID_SUN, USB_DEVICE_ID_RARITAN_KVM_DONGLE, HID_QUIRK_NOGET },
{ USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_DUAL_USB_JOYPAD, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
{ USB_VENDOR_ID_WISEGROUP_LTD, USB_DEVICE_ID_SMARTJOY_DUAL_PLUS, HID_QUIRK_NOGET | HID_QUIRK_MULTI_INPUT },
diff --git a/drivers/usb/input/hid-ff.c b/drivers/usb/input/hid-ff.c
index d5c91ee6799..a8fc46c721c 100644
--- a/drivers/usb/input/hid-ff.c
+++ b/drivers/usb/input/hid-ff.c
@@ -44,45 +44,38 @@ struct hid_ff_initializer {
int (*init)(struct hid_device*);
};
+/*
+ * We try pidff when no other driver is found because PID is the
+ * standards compliant way of implementing force feedback in HID.
+ * pidff_init() will quickly abort if the device doesn't appear to
+ * be a PID device
+ */
static struct hid_ff_initializer inits[] = {
#ifdef CONFIG_LOGITECH_FF
- {0x46d, 0xc211, hid_lgff_init}, // Logitech Cordless rumble pad
- {0x46d, 0xc283, hid_lgff_init}, // Logitech Wingman Force 3d
- {0x46d, 0xc295, hid_lgff_init}, // Logitech MOMO force wheel
- {0x46d, 0xc219, hid_lgff_init}, // Logitech Cordless rumble pad 2
-#endif
-#ifdef CONFIG_HID_PID
- {0x45e, 0x001b, hid_pid_init},
+ { 0x46d, 0xc211, hid_lgff_init }, /* Logitech Cordless rumble pad */
+ { 0x46d, 0xc283, hid_lgff_init }, /* Logitech Wingman Force 3d */
+ { 0x46d, 0xc295, hid_lgff_init }, /* Logitech MOMO force wheel */
+ { 0x46d, 0xc219, hid_lgff_init }, /* Logitech Cordless rumble pad 2 */
#endif
#ifdef CONFIG_THRUSTMASTER_FF
- {0x44f, 0xb304, hid_tmff_init},
+ { 0x44f, 0xb304, hid_tmff_init },
#endif
- {0, 0, NULL} /* Terminating entry */
+#ifdef CONFIG_ZEROPLUS_FF
+ { 0xc12, 0x0005, hid_zpff_init },
+ { 0xc12, 0x0030, hid_zpff_init },
+#endif
+ { 0, 0, hid_pidff_init} /* Matches anything */
};
-static struct hid_ff_initializer *hid_get_ff_init(__u16 idVendor,
- __u16 idProduct)
-{
- struct hid_ff_initializer *init;
- for (init = inits;
- init->idVendor
- && !(init->idVendor == idVendor
- && init->idProduct == idProduct);
- init++);
-
- return init->idVendor? init : NULL;
-}
-
int hid_ff_init(struct hid_device* hid)
{
struct hid_ff_initializer *init;
+ int vendor = le16_to_cpu(hid->dev->descriptor.idVendor);
+ int product = le16_to_cpu(hid->dev->descriptor.idProduct);
- init = hid_get_ff_init(le16_to_cpu(hid->dev->descriptor.idVendor),
- le16_to_cpu(hid->dev->descriptor.idProduct));
+ for (init = inits; init->idVendor; init++)
+ if (init->idVendor == vendor && init->idProduct == product)
+ break;
- if (!init) {
- dbg("hid_ff_init could not find initializer");
- return -ENOSYS;
- }
return init->init(hid);
}
diff --git a/drivers/usb/input/hid-input.c b/drivers/usb/input/hid-input.c
index 7208839f2db..4c62afbeb43 100644
--- a/drivers/usb/input/hid-input.c
+++ b/drivers/usb/input/hid-input.c
@@ -65,11 +65,9 @@ static const struct {
#define map_rel(c) do { usage->code = c; usage->type = EV_REL; bit = input->relbit; max = REL_MAX; } while (0)
#define map_key(c) do { usage->code = c; usage->type = EV_KEY; bit = input->keybit; max = KEY_MAX; } while (0)
#define map_led(c) do { usage->code = c; usage->type = EV_LED; bit = input->ledbit; max = LED_MAX; } while (0)
-#define map_ff(c) do { usage->code = c; usage->type = EV_FF; bit = input->ffbit; max = FF_MAX; } while (0)
#define map_abs_clear(c) do { map_abs(c); clear_bit(c, bit); } while (0)
#define map_key_clear(c) do { map_key(c); clear_bit(c, bit); } while (0)
-#define map_ff_effect(c) do { set_bit(c, input->ffbit); } while (0)
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
@@ -525,23 +523,7 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel
case HID_UP_PID:
- set_bit(EV_FF, input->evbit);
switch(usage->hid & HID_USAGE) {
- case 0x26: map_ff_effect(FF_CONSTANT); goto ignore;
- case 0x27: map_ff_effect(FF_RAMP); goto ignore;
- case 0x28: map_ff_effect(FF_CUSTOM); goto ignore;
- case 0x30: map_ff_effect(FF_SQUARE); map_ff_effect(FF_PERIODIC); goto ignore;
- case 0x31: map_ff_effect(FF_SINE); map_ff_effect(FF_PERIODIC); goto ignore;
- case 0x32: map_ff_effect(FF_TRIANGLE); map_ff_effect(FF_PERIODIC); goto ignore;
- case 0x33: map_ff_effect(FF_SAW_UP); map_ff_effect(FF_PERIODIC); goto ignore;
- case 0x34: map_ff_effect(FF_SAW_DOWN); map_ff_effect(FF_PERIODIC); goto ignore;
- case 0x40: map_ff_effect(FF_SPRING); goto ignore;
- case 0x41: map_ff_effect(FF_DAMPER); goto ignore;
- case 0x42: map_ff_effect(FF_INERTIA); goto ignore;
- case 0x43: map_ff_effect(FF_FRICTION); goto ignore;
- case 0x7e: map_ff(FF_GAIN); break;
- case 0x83: input->ff_effects_max = field->value[0]; goto ignore;
- case 0x98: map_ff(FF_AUTOCENTER); break;
case 0xa4: map_key_clear(BTN_DEAD); break;
default: goto ignore;
}
@@ -698,8 +680,7 @@ void hidinput_hid_event(struct hid_device *hid, struct hid_field *field, struct
}
if (usage->hid == (HID_UP_PID | 0x83UL)) { /* Simultaneous Effects Max */
- input->ff_effects_max = value;
- dbg("Maximum Effects - %d",input->ff_effects_max);
+ dbg("Maximum Effects - %d",value);
return;
}
@@ -748,7 +729,7 @@ static int hidinput_input_event(struct input_dev *dev, unsigned int type, unsign
int offset;
if (type == EV_FF)
- return hid_ff_event(hid, dev, type, code, value);
+ return input_ff_event(dev, type, code, value);
if (type != EV_LED)
return -1;
diff --git a/drivers/usb/input/hid-lgff.c b/drivers/usb/input/hid-lgff.c
index f07d44357ff..93da222b6da 100644
--- a/drivers/usb/input/hid-lgff.c
+++ b/drivers/usb/input/hid-lgff.c
@@ -1,12 +1,11 @@
/*
- * $$
- *
* Force feedback support for hid-compliant for some of the devices from
* Logitech, namely:
* - WingMan Cordless RumblePad
* - WingMan Force 3D
*
* Copyright (c) 2002-2004 Johann Deneux
+ * Copyright (c) 2006 Anssi Hannula <anssi.hannula@gmail.com>
*/
/*
@@ -29,495 +28,117 @@
*/
#include <linux/input.h>
-#include <linux/sched.h>
-
-//#define DEBUG
#include <linux/usb.h>
-
-#include <linux/circ_buf.h>
-
#include "hid.h"
-#include "fixp-arith.h"
-
-
-/* Periodicity of the update */
-#define PERIOD (HZ/10)
-
-#define RUN_AT(t) (jiffies + (t))
-
-/* Effect status */
-#define EFFECT_STARTED 0 /* Effect is going to play after some time
- (ff_replay.delay) */
-#define EFFECT_PLAYING 1 /* Effect is being played */
-#define EFFECT_USED 2
-
-// For lgff_device::flags
-#define DEVICE_CLOSING 0 /* The driver is being unitialised */
-
-/* Check that the current process can access an effect */
-#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
- || effect.owner == current->pid)
-
-#define LGFF_CHECK_OWNERSHIP(i, l) \
- (i>=0 && i<LGFF_EFFECTS \
- && test_bit(EFFECT_USED, l->effects[i].flags) \
- && CHECK_OWNERSHIP(l->effects[i]))
-
-#define LGFF_EFFECTS 8
struct device_type {
u16 idVendor;
u16 idProduct;
- signed short *ff;
-};
-
-struct lgff_effect {
- pid_t owner;
-
- struct ff_effect effect;
-
- unsigned long flags[1];
- unsigned int count; /* Number of times left to play */
- unsigned long started_at; /* When the effect started to play */
-};
-
-struct lgff_device {
- struct hid_device* hid;
-
- struct hid_report* constant;
- struct hid_report* rumble;
- struct hid_report* condition;
-
- struct lgff_effect effects[LGFF_EFFECTS];
- spinlock_t lock; /* device-level lock. Having locks on
- a per-effect basis could be nice, but
- isn't really necessary */
-
- unsigned long flags[1]; /* Contains various information about the
- state of the driver for this device */
-
- struct timer_list timer;
+ const signed short *ff;
};
-/* Callbacks */
-static void hid_lgff_exit(struct hid_device* hid);
-static int hid_lgff_event(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value);
-static int hid_lgff_flush(struct input_dev *input, struct file *file);
-static int hid_lgff_upload_effect(struct input_dev *input,
- struct ff_effect *effect);
-static int hid_lgff_erase(struct input_dev *input, int id);
-
-/* Local functions */
-static void hid_lgff_input_init(struct hid_device* hid);
-static void hid_lgff_timer(unsigned long timer_data);
-static struct hid_report* hid_lgff_duplicate_report(struct hid_report*);
-static void hid_lgff_delete_report(struct hid_report*);
-
-static signed short ff_rumble[] = {
+static const signed short ff_rumble[] = {
FF_RUMBLE,
-1
};
-static signed short ff_joystick[] = {
+static const signed short ff_joystick[] = {
FF_CONSTANT,
-1
};
-static struct device_type devices[] = {
- {0x046d, 0xc211, ff_rumble},
- {0x046d, 0xc219, ff_rumble},
- {0x046d, 0xc283, ff_joystick},
- {0x0000, 0x0000, ff_joystick}
+static const struct device_type devices[] = {
+ { 0x046d, 0xc211, ff_rumble },
+ { 0x046d, 0xc219, ff_rumble },
+ { 0x046d, 0xc283, ff_joystick },
+ { 0x0000, 0x0000, ff_joystick }
};
+static int hid_lgff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+ struct hid_device *hid = dev->private;
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct hid_report *report = list_entry(report_list->next, struct hid_report, list);
+ int x, y;
+ unsigned int left, right;
+
+#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+ x = effect->u.ramp.start_level + 0x7f; /* 0x7f is center */
+ y = effect->u.ramp.end_level + 0x7f;
+ CLAMP(x);
+ CLAMP(y);
+ report->field[0]->value[0] = 0x51;
+ report->field[0]->value[1] = 0x08;
+ report->field[0]->value[2] = x;
+ report->field[0]->value[3] = y;
+ dbg("(x, y)=(%04x, %04x)", x, y);
+ hid_submit_report(hid, report, USB_DIR_OUT);
+ break;
+
+ case FF_RUMBLE:
+ right = effect->u.rumble.strong_magnitude;
+ left = effect->u.rumble.weak_magnitude;
+ right = right * 0xff / 0xffff;
+ left = left * 0xff / 0xffff;
+ CLAMP(left);
+ CLAMP(right);
+ report->field[0]->value[0] = 0x42;
+ report->field[0]->value[1] = 0x00;
+ report->field[0]->value[2] = left;
+ report->field[0]->value[3] = right;
+ dbg("(left, right)=(%04x, %04x)", left, right);
+ hid_submit_report(hid, report, USB_DIR_OUT);
+ break;
+ }
+ return 0;
+}
+
int hid_lgff_init(struct hid_device* hid)
{
- struct lgff_device *private;
- struct hid_report* report;
- struct hid_field* field;
+ struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
+ struct list_head *report_list = &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct input_dev *dev = hidinput->input;
+ struct hid_report *report;
+ struct hid_field *field;
+ int error;
+ int i, j;
/* Find the report to use */
- if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
+ if (list_empty(report_list)) {
err("No output report found");
return -1;
}
+
/* Check that the report looks ok */
- report = (struct hid_report*)hid->report_enum[HID_OUTPUT_REPORT].report_list.next;
+ report = list_entry(report_list->next, struct hid_report, list);
if (!report) {
err("NULL output report");
return -1;
}
+
field = report->field[0];
if (!field) {
err("NULL field");
return -1;
}
- private = kzalloc(sizeof(struct lgff_device), GFP_KERNEL);
- if (!private)
- return -1;
- hid->ff_private = private;
-
- /* Input init */
- hid_lgff_input_init(hid);
-
-
- private->constant = hid_lgff_duplicate_report(report);
- if (!private->constant) {
- kfree(private);
- return -1;
- }
- private->constant->field[0]->value[0] = 0x51;
- private->constant->field[0]->value[1] = 0x08;
- private->constant->field[0]->value[2] = 0x7f;
- private->constant->field[0]->value[3] = 0x7f;
-
- private->rumble = hid_lgff_duplicate_report(report);
- if (!private->rumble) {
- hid_lgff_delete_report(private->constant);
- kfree(private);
- return -1;
- }
- private->rumble->field[0]->value[0] = 0x42;
-
-
- private->condition = hid_lgff_duplicate_report(report);
- if (!private->condition) {
- hid_lgff_delete_report(private->rumble);
- hid_lgff_delete_report(private->constant);
- kfree(private);
- return -1;
- }
-
- private->hid = hid;
-
- spin_lock_init(&private->lock);
- init_timer(&private->timer);
- private->timer.data = (unsigned long)private;
- private->timer.function = hid_lgff_timer;
-
- /* Event and exit callbacks */
- hid->ff_exit = hid_lgff_exit;
- hid->ff_event = hid_lgff_event;
-
- /* Start the update task */
- private->timer.expires = RUN_AT(PERIOD);
- add_timer(&private->timer); /*TODO: only run the timer when at least
- one effect is playing */
-
- printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
-
- return 0;
-}
-
-static struct hid_report* hid_lgff_duplicate_report(struct hid_report* report)
-{
- struct hid_report* ret;
-
- ret = kmalloc(sizeof(struct lgff_device), GFP_KERNEL);
- if (!ret)
- return NULL;
- *ret = *report;
-
- ret->field[0] = kmalloc(sizeof(struct hid_field), GFP_KERNEL);
- if (!ret->field[0]) {
- kfree(ret);
- return NULL;
- }
- *ret->field[0] = *report->field[0];
-
- ret->field[0]->value = kzalloc(sizeof(s32[8]), GFP_KERNEL);
- if (!ret->field[0]->value) {
- kfree(ret->field[0]);
- kfree(ret);
- return NULL;
- }
-
- return ret;
-}
-
-static void hid_lgff_delete_report(struct hid_report* report)
-{
- if (report) {
- kfree(report->field[0]->value);
- kfree(report->field[0]);
- kfree(report);
- }
-}
-
-static void hid_lgff_input_init(struct hid_device* hid)
-{
- struct device_type* dev = devices;
- signed short* ff;
- u16 idVendor = le16_to_cpu(hid->dev->descriptor.idVendor);
- u16 idProduct = le16_to_cpu(hid->dev->descriptor.idProduct);
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
-
- while (dev->idVendor && (idVendor != dev->idVendor || idProduct != dev->idProduct))
- dev++;
-
- for (ff = dev->ff; *ff >= 0; ff++)
- set_bit(*ff, input_dev->ffbit);
-
- input_dev->upload_effect = hid_lgff_upload_effect;
- input_dev->flush = hid_lgff_flush;
-
- set_bit(EV_FF, input_dev->evbit);
- input_dev->ff_effects_max = LGFF_EFFECTS;
-}
-
-static void hid_lgff_exit(struct hid_device* hid)
-{
- struct lgff_device *lgff = hid->ff_private;
-
- set_bit(DEVICE_CLOSING, lgff->flags);
- del_timer_sync(&lgff->timer);
-
- hid_lgff_delete_report(lgff->condition);
- hid_lgff_delete_report(lgff->rumble);
- hid_lgff_delete_report(lgff->constant);
-
- kfree(lgff);
-}
-
-static int hid_lgff_event(struct hid_device *hid, struct input_dev* input,
- unsigned int type, unsigned int code, int value)
-{
- struct lgff_device *lgff = hid->ff_private;
- struct lgff_effect *effect = lgff->effects + code;
- unsigned long flags;
-
- if (type != EV_FF) return -EINVAL;
- if (!LGFF_CHECK_OWNERSHIP(code, lgff)) return -EACCES;
- if (value < 0) return -EINVAL;
-
- spin_lock_irqsave(&lgff->lock, flags);
-
- if (value > 0) {
- if (test_bit(EFFECT_STARTED, effect->flags)) {
- spin_unlock_irqrestore(&lgff->lock, flags);
- return -EBUSY;
- }
- if (test_bit(EFFECT_PLAYING, effect->flags)) {
- spin_unlock_irqrestore(&lgff->lock, flags);
- return -EBUSY;
- }
-
- effect->count = value;
-
- if (effect->effect.replay.delay) {
- set_bit(EFFECT_STARTED, effect->flags);
- } else {
- set_bit(EFFECT_PLAYING, effect->flags);
- }
- effect->started_at = jiffies;
- }
- else { /* value == 0 */
- clear_bit(EFFECT_STARTED, effect->flags);
- clear_bit(EFFECT_PLAYING, effect->flags);
- }
-
- spin_unlock_irqrestore(&lgff->lock, flags);
-
- return 0;
-
-}
-
-/* Erase all effects this process owns */
-static int hid_lgff_flush(struct input_dev *dev, struct file *file)
-{
- struct hid_device *hid = dev->private;
- struct lgff_device *lgff = hid->ff_private;
- int i;
-
- for (i=0; i<dev->ff_effects_max; ++i) {
-
- /*NOTE: no need to lock here. The only times EFFECT_USED is
- modified is when effects are uploaded or when an effect is
- erased. But a process cannot close its dev/input/eventX fd
- and perform ioctls on the same fd all at the same time */
- if ( current->pid == lgff->effects[i].owner
- && test_bit(EFFECT_USED, lgff->effects[i].flags)) {
-
- if (hid_lgff_erase(dev, i))
- warn("erase effect %d failed", i);
- }
-
- }
-
- return 0;
-}
-
-static int hid_lgff_erase(struct input_dev *dev, int id)
-{
- struct hid_device *hid = dev->private;
- struct lgff_device *lgff = hid->ff_private;
- unsigned long flags;
-
- if (!LGFF_CHECK_OWNERSHIP(id, lgff)) return -EACCES;
-
- spin_lock_irqsave(&lgff->lock, flags);
- lgff->effects[id].flags[0] = 0;
- spin_unlock_irqrestore(&lgff->lock, flags);
-
- return 0;
-}
-
-static int hid_lgff_upload_effect(struct input_dev* input,
- struct ff_effect* effect)
-{
- struct hid_device *hid = input->private;
- struct lgff_device *lgff = hid->ff_private;
- struct lgff_effect new;
- int id;
- unsigned long flags;
-
- dbg("ioctl rumble");
-
- if (!test_bit(effect->type, input->ffbit)) return -EINVAL;
-
- spin_lock_irqsave(&lgff->lock, flags);
-
- if (effect->id == -1) {
- int i;
-
- for (i=0; i<LGFF_EFFECTS && test_bit(EFFECT_USED, lgff->effects[i].flags); ++i);
- if (i >= LGFF_EFFECTS) {
- spin_unlock_irqrestore(&lgff->lock, flags);
- return -ENOSPC;
+ for (i = 0; i < ARRAY_SIZE(devices); i++) {
+ if (dev->id.vendor == devices[i].idVendor &&
+ dev->id.product == devices[i].idProduct) {
+ for (j = 0; devices[i].ff[j] >= 0; j++)
+ set_bit(devices[i].ff[j], dev->ffbit);
+ break;
}
-
- effect->id = i;
- lgff->effects[i].owner = current->pid;
- lgff->effects[i].flags[0] = 0;
- set_bit(EFFECT_USED, lgff->effects[i].flags);
}
- else if (!LGFF_CHECK_OWNERSHIP(effect->id, lgff)) {
- spin_unlock_irqrestore(&lgff->lock, flags);
- return -EACCES;
- }
-
- id = effect->id;
- new = lgff->effects[id];
-
- new.effect = *effect;
-
- if (test_bit(EFFECT_STARTED, lgff->effects[id].flags)
- || test_bit(EFFECT_STARTED, lgff->effects[id].flags)) {
-
- /* Changing replay parameters is not allowed (for the time
- being) */
- if (new.effect.replay.delay != lgff->effects[id].effect.replay.delay
- || new.effect.replay.length != lgff->effects[id].effect.replay.length) {
- spin_unlock_irqrestore(&lgff->lock, flags);
- return -ENOSYS;
- }
- lgff->effects[id] = new;
+ error = input_ff_create_memless(dev, NULL, hid_lgff_play);
+ if (error)
+ return error;
- } else {
- lgff->effects[id] = new;
- }
+ printk(KERN_INFO "Force feedback for Logitech force feedback devices by Johann Deneux <johann.deneux@it.uu.se>\n");
- spin_unlock_irqrestore(&lgff->lock, flags);
return 0;
}
-
-static void hid_lgff_timer(unsigned long timer_data)
-{
- struct lgff_device *lgff = (struct lgff_device*)timer_data;
- struct hid_device *hid = lgff->hid;
- unsigned long flags;
- int x = 0x7f, y = 0x7f; // Coordinates of constant effects
- unsigned int left = 0, right = 0; // Rumbling
- int i;
-
- spin_lock_irqsave(&lgff->lock, flags);
-
- for (i=0; i<LGFF_EFFECTS; ++i) {
- struct lgff_effect* effect = lgff->effects +i;
-
- if (test_bit(EFFECT_PLAYING, effect->flags)) {
-
- switch (effect->effect.type) {
- case FF_CONSTANT: {
- //TODO: handle envelopes
- int degrees = effect->effect.direction * 360 >> 16;
- x += fixp_mult(fixp_sin(degrees),
- fixp_new16(effect->effect.u.constant.level));
- y += fixp_mult(-fixp_cos(degrees),
- fixp_new16(effect->effect.u.constant.level));
- } break;
- case FF_RUMBLE:
- right += effect->effect.u.rumble.strong_magnitude;
- left += effect->effect.u.rumble.weak_magnitude;
- break;
- };
-
- /* One run of the effect is finished playing */
- if (time_after(jiffies,
- effect->started_at
- + effect->effect.replay.delay*HZ/1000
- + effect->effect.replay.length*HZ/1000)) {
- dbg("Finished playing once %d", i);
- if (--effect->count <= 0) {
- dbg("Stopped %d", i);
- clear_bit(EFFECT_PLAYING, effect->flags);
- }
- else {
- dbg("Start again %d", i);
- if (effect->effect.replay.length != 0) {
- clear_bit(EFFECT_PLAYING, effect->flags);
- set_bit(EFFECT_STARTED, effect->flags);
- }
- effect->started_at = jiffies;
- }
- }
-
- } else if (test_bit(EFFECT_STARTED, lgff->effects[i].flags)) {
- /* Check if we should start playing the effect */
- if (time_after(jiffies,
- lgff->effects[i].started_at
- + lgff->effects[i].effect.replay.delay*HZ/1000)) {
- dbg("Now playing %d", i);
- clear_bit(EFFECT_STARTED, lgff->effects[i].flags);
- set_bit(EFFECT_PLAYING, lgff->effects[i].flags);
- }
- }
- }
-
-#define CLAMP(x) if (x < 0) x = 0; if (x > 0xff) x = 0xff
-
- // Clamp values
- CLAMP(x);
- CLAMP(y);
- CLAMP(left);
- CLAMP(right);
-
-#undef CLAMP
-
- if (x != lgff->constant->field[0]->value[2]
- || y != lgff->constant->field[0]->value[3]) {
- lgff->constant->field[0]->value[2] = x;
- lgff->constant->field[0]->value[3] = y;
- dbg("(x,y)=(%04x, %04x)", x, y);
- hid_submit_report(hid, lgff->constant, USB_DIR_OUT);
- }
-
- if (left != lgff->rumble->field[0]->value[2]
- || right != lgff->rumble->field[0]->value[3]) {
- lgff->rumble->field[0]->value[2] = left;
- lgff->rumble->field[0]->value[3] = right;
- dbg("(left,right)=(%04x, %04x)", left, right);
- hid_submit_report(hid, lgff->rumble, USB_DIR_OUT);
- }
-
- if (!test_bit(DEVICE_CLOSING, lgff->flags)) {
- lgff->timer.expires = RUN_AT(PERIOD);
- add_timer(&lgff->timer);
- }
-
- spin_unlock_irqrestore(&lgff->lock, flags);
-}
diff --git a/drivers/usb/input/hid-pidff.c b/drivers/usb/input/hid-pidff.c
new file mode 100644
index 00000000000..5420c13eb8e
--- /dev/null
+++ b/drivers/usb/input/hid-pidff.c
@@ -0,0 +1,1330 @@
+/*
+ * Force feedback driver for USB HID PID compliant devices
+ *
+ * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("hid-pidff: " format "\n" , ## arg)
+
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/usb.h>
+
+#include "hid.h"
+
+#define PID_EFFECTS_MAX 64
+
+/* Report usage table used to put reports into an array */
+
+#define PID_SET_EFFECT 0
+#define PID_EFFECT_OPERATION 1
+#define PID_DEVICE_GAIN 2
+#define PID_POOL 3
+#define PID_BLOCK_LOAD 4
+#define PID_BLOCK_FREE 5
+#define PID_DEVICE_CONTROL 6
+#define PID_CREATE_NEW_EFFECT 7
+
+#define PID_REQUIRED_REPORTS 7
+
+#define PID_SET_ENVELOPE 8
+#define PID_SET_CONDITION 9
+#define PID_SET_PERIODIC 10
+#define PID_SET_CONSTANT 11
+#define PID_SET_RAMP 12
+static const u8 pidff_reports[] = {
+ 0x21, 0x77, 0x7d, 0x7f, 0x89, 0x90, 0x96, 0xab,
+ 0x5a, 0x5f, 0x6e, 0x73, 0x74
+};
+
+/* device_control is really 0x95, but 0x96 specified as it is the usage of
+the only field in that report */
+
+/* Value usage tables used to put fields and values into arrays */
+
+#define PID_EFFECT_BLOCK_INDEX 0
+
+#define PID_DURATION 1
+#define PID_GAIN 2
+#define PID_TRIGGER_BUTTON 3
+#define PID_TRIGGER_REPEAT_INT 4
+#define PID_DIRECTION_ENABLE 5
+#define PID_START_DELAY 6
+static const u8 pidff_set_effect[] = {
+ 0x22, 0x50, 0x52, 0x53, 0x54, 0x56, 0xa7
+};
+
+#define PID_ATTACK_LEVEL 1
+#define PID_ATTACK_TIME 2
+#define PID_FADE_LEVEL 3
+#define PID_FADE_TIME 4
+static const u8 pidff_set_envelope[] = { 0x22, 0x5b, 0x5c, 0x5d, 0x5e };
+
+#define PID_PARAM_BLOCK_OFFSET 1
+#define PID_CP_OFFSET 2
+#define PID_POS_COEFFICIENT 3
+#define PID_NEG_COEFFICIENT 4
+#define PID_POS_SATURATION 5
+#define PID_NEG_SATURATION 6
+#define PID_DEAD_BAND 7
+static const u8 pidff_set_condition[] = {
+ 0x22, 0x23, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65
+};
+
+#define PID_MAGNITUDE 1
+#define PID_OFFSET 2
+#define PID_PHASE 3
+#define PID_PERIOD 4
+static const u8 pidff_set_periodic[] = { 0x22, 0x70, 0x6f, 0x71, 0x72 };
+static const u8 pidff_set_constant[] = { 0x22, 0x70 };
+
+#define PID_RAMP_START 1
+#define PID_RAMP_END 2
+static const u8 pidff_set_ramp[] = { 0x22, 0x75, 0x76 };
+
+#define PID_RAM_POOL_AVAILABLE 1
+static const u8 pidff_block_load[] = { 0x22, 0xac };
+
+#define PID_LOOP_COUNT 1
+static const u8 pidff_effect_operation[] = { 0x22, 0x7c };
+
+static const u8 pidff_block_free[] = { 0x22 };
+
+#define PID_DEVICE_GAIN_FIELD 0
+static const u8 pidff_device_gain[] = { 0x7e };
+
+#define PID_RAM_POOL_SIZE 0
+#define PID_SIMULTANEOUS_MAX 1
+#define PID_DEVICE_MANAGED_POOL 2
+static const u8 pidff_pool[] = { 0x80, 0x83, 0xa9 };
+
+/* Special field key tables used to put special field keys into arrays */
+
+#define PID_ENABLE_ACTUATORS 0
+#define PID_RESET 1
+static const u8 pidff_device_control[] = { 0x97, 0x9a };
+
+#define PID_CONSTANT 0
+#define PID_RAMP 1
+#define PID_SQUARE 2
+#define PID_SINE 3
+#define PID_TRIANGLE 4
+#define PID_SAW_UP 5
+#define PID_SAW_DOWN 6
+#define PID_SPRING 7
+#define PID_DAMPER 8
+#define PID_INERTIA 9
+#define PID_FRICTION 10
+static const u8 pidff_effect_types[] = {
+ 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34,
+ 0x40, 0x41, 0x42, 0x43
+};
+
+#define PID_BLOCK_LOAD_SUCCESS 0
+#define PID_BLOCK_LOAD_FULL 1
+static const u8 pidff_block_load_status[] = { 0x8c, 0x8d };
+
+#define PID_EFFECT_START 0
+#define PID_EFFECT_STOP 1
+static const u8 pidff_effect_operation_status[] = { 0x79, 0x7b };
+
+struct pidff_usage {
+ struct hid_field *field;
+ s32 *value;
+};
+
+struct pidff_device {
+ struct hid_device *hid;
+
+ struct hid_report *reports[sizeof(pidff_reports)];
+
+ struct pidff_usage set_effect[sizeof(pidff_set_effect)];
+ struct pidff_usage set_envelope[sizeof(pidff_set_envelope)];
+ struct pidff_usage set_condition[sizeof(pidff_set_condition)];
+ struct pidff_usage set_periodic[sizeof(pidff_set_periodic)];
+ struct pidff_usage set_constant[sizeof(pidff_set_constant)];
+ struct pidff_usage set_ramp[sizeof(pidff_set_ramp)];
+
+ struct pidff_usage device_gain[sizeof(pidff_device_gain)];
+ struct pidff_usage block_load[sizeof(pidff_block_load)];
+ struct pidff_usage pool[sizeof(pidff_pool)];
+ struct pidff_usage effect_operation[sizeof(pidff_effect_operation)];
+ struct pidff_usage block_free[sizeof(pidff_block_free)];
+
+ /* Special field is a field that is not composed of
+ usage<->value pairs that pidff_usage values are */
+
+ /* Special field in create_new_effect */
+ struct hid_field *create_new_effect_type;
+
+ /* Special fields in set_effect */
+ struct hid_field *set_effect_type;
+ struct hid_field *effect_direction;
+
+ /* Special field in device_control */
+ struct hid_field *device_control;
+
+ /* Special field in block_load */
+ struct hid_field *block_load_status;
+
+ /* Special field in effect_operation */
+ struct hid_field *effect_operation_status;
+
+ int control_id[sizeof(pidff_device_control)];
+ int type_id[sizeof(pidff_effect_types)];
+ int status_id[sizeof(pidff_block_load_status)];
+ int operation_id[sizeof(pidff_effect_operation_status)];
+
+ int pid_id[PID_EFFECTS_MAX];
+};
+
+/*
+ * Scale an unsigned value with range 0..max for the given field
+ */
+static int pidff_rescale(int i, int max, struct hid_field *field)
+{
+ return i * (field->logical_maximum - field->logical_minimum) / max +
+ field->logical_minimum;
+}
+
+/*
+ * Scale a signed value in range -0x8000..0x7fff for the given field
+ */
+static int pidff_rescale_signed(int i, struct hid_field *field)
+{
+ return i == 0 ? 0 : i >
+ 0 ? i * field->logical_maximum / 0x7fff : i *
+ field->logical_minimum / -0x8000;
+}
+
+static void pidff_set(struct pidff_usage *usage, u16 value)
+{
+ usage->value[0] = pidff_rescale(value, 0xffff, usage->field);
+ debug("calculated from %d to %d", value, usage->value[0]);
+}
+
+static void pidff_set_signed(struct pidff_usage *usage, s16 value)
+{
+ if (usage->field->logical_minimum < 0)
+ usage->value[0] = pidff_rescale_signed(value, usage->field);
+ else {
+ if (value < 0)
+ usage->value[0] =
+ pidff_rescale(-value, 0x8000, usage->field);
+ else
+ usage->value[0] =
+ pidff_rescale(value, 0x7fff, usage->field);
+ }
+ debug("calculated from %d to %d", value, usage->value[0]);
+}
+
+/*
+ * Send envelope report to the device
+ */
+static void pidff_set_envelope_report(struct pidff_device *pidff,
+ struct ff_envelope *envelope)
+{
+ pidff->set_envelope[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+ pidff->set_envelope[PID_ATTACK_LEVEL].value[0] =
+ pidff_rescale(envelope->attack_level >
+ 0x7fff ? 0x7fff : envelope->attack_level, 0x7fff,
+ pidff->set_envelope[PID_ATTACK_LEVEL].field);
+ pidff->set_envelope[PID_FADE_LEVEL].value[0] =
+ pidff_rescale(envelope->fade_level >
+ 0x7fff ? 0x7fff : envelope->fade_level, 0x7fff,
+ pidff->set_envelope[PID_FADE_LEVEL].field);
+
+ pidff->set_envelope[PID_ATTACK_TIME].value[0] = envelope->attack_length;
+ pidff->set_envelope[PID_FADE_TIME].value[0] = envelope->fade_length;
+
+ debug("attack %u => %d", envelope->attack_level,
+ pidff->set_envelope[PID_ATTACK_LEVEL].value[0]);
+
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_ENVELOPE],
+ USB_DIR_OUT);
+}
+
+/*
+ * Test if the new envelope differs from old one
+ */
+static int pidff_needs_set_envelope(struct ff_envelope *envelope,
+ struct ff_envelope *old)
+{
+ return envelope->attack_level != old->attack_level ||
+ envelope->fade_level != old->fade_level ||
+ envelope->attack_length != old->attack_length ||
+ envelope->fade_length != old->fade_length;
+}
+
+/*
+ * Send constant force report to the device
+ */
+static void pidff_set_constant_force_report(struct pidff_device *pidff,
+ struct ff_effect *effect)
+{
+ pidff->set_constant[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+ pidff_set_signed(&pidff->set_constant[PID_MAGNITUDE],
+ effect->u.constant.level);
+
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONSTANT],
+ USB_DIR_OUT);
+}
+
+/*
+ * Test if the constant parameters have changed between effects
+ */
+static int pidff_needs_set_constant(struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ return effect->u.constant.level != old->u.constant.level;
+}
+
+/*
+ * Send set effect report to the device
+ */
+static void pidff_set_effect_report(struct pidff_device *pidff,
+ struct ff_effect *effect)
+{
+ pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+ pidff->set_effect_type->value[0] =
+ pidff->create_new_effect_type->value[0];
+ pidff->set_effect[PID_DURATION].value[0] = effect->replay.length;
+ pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = effect->trigger.button;
+ pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] =
+ effect->trigger.interval;
+ pidff->set_effect[PID_GAIN].value[0] =
+ pidff->set_effect[PID_GAIN].field->logical_maximum;
+ pidff->set_effect[PID_DIRECTION_ENABLE].value[0] = 1;
+ pidff->effect_direction->value[0] =
+ pidff_rescale(effect->direction, 0xffff,
+ pidff->effect_direction);
+ pidff->set_effect[PID_START_DELAY].value[0] = effect->replay.delay;
+
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
+ USB_DIR_OUT);
+}
+
+/*
+ * Test if the values used in set_effect have changed
+ */
+static int pidff_needs_set_effect(struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ return effect->replay.length != old->replay.length ||
+ effect->trigger.interval != old->trigger.interval ||
+ effect->trigger.button != old->trigger.button ||
+ effect->direction != old->direction ||
+ effect->replay.delay != old->replay.delay;
+}
+
+/*
+ * Send periodic effect report to the device
+ */
+static void pidff_set_periodic_report(struct pidff_device *pidff,
+ struct ff_effect *effect)
+{
+ pidff->set_periodic[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+ pidff_set_signed(&pidff->set_periodic[PID_MAGNITUDE],
+ effect->u.periodic.magnitude);
+ pidff_set_signed(&pidff->set_periodic[PID_OFFSET],
+ effect->u.periodic.offset);
+ pidff_set(&pidff->set_periodic[PID_PHASE], effect->u.periodic.phase);
+ pidff->set_periodic[PID_PERIOD].value[0] = effect->u.periodic.period;
+
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_PERIODIC],
+ USB_DIR_OUT);
+
+}
+
+/*
+ * Test if periodic effect parameters have changed
+ */
+static int pidff_needs_set_periodic(struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ return effect->u.periodic.magnitude != old->u.periodic.magnitude ||
+ effect->u.periodic.offset != old->u.periodic.offset ||
+ effect->u.periodic.phase != old->u.periodic.phase ||
+ effect->u.periodic.period != old->u.periodic.period;
+}
+
+/*
+ * Send condition effect reports to the device
+ */
+static void pidff_set_condition_report(struct pidff_device *pidff,
+ struct ff_effect *effect)
+{
+ int i;
+
+ pidff->set_condition[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+ for (i = 0; i < 2; i++) {
+ pidff->set_condition[PID_PARAM_BLOCK_OFFSET].value[0] = i;
+ pidff_set_signed(&pidff->set_condition[PID_CP_OFFSET],
+ effect->u.condition[i].center);
+ pidff_set_signed(&pidff->set_condition[PID_POS_COEFFICIENT],
+ effect->u.condition[i].right_coeff);
+ pidff_set_signed(&pidff->set_condition[PID_NEG_COEFFICIENT],
+ effect->u.condition[i].left_coeff);
+ pidff_set(&pidff->set_condition[PID_POS_SATURATION],
+ effect->u.condition[i].right_saturation);
+ pidff_set(&pidff->set_condition[PID_NEG_SATURATION],
+ effect->u.condition[i].left_saturation);
+ pidff_set(&pidff->set_condition[PID_DEAD_BAND],
+ effect->u.condition[i].deadband);
+ hid_wait_io(pidff->hid);
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_CONDITION],
+ USB_DIR_OUT);
+ }
+}
+
+/*
+ * Test if condition effect parameters have changed
+ */
+static int pidff_needs_set_condition(struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ int i;
+ int ret = 0;
+
+ for (i = 0; i < 2; i++) {
+ struct ff_condition_effect *cond = &effect->u.condition[i];
+ struct ff_condition_effect *old_cond = &old->u.condition[i];
+
+ ret |= cond->center != old_cond->center ||
+ cond->right_coeff != old_cond->right_coeff ||
+ cond->left_coeff != old_cond->left_coeff ||
+ cond->right_saturation != old_cond->right_saturation ||
+ cond->left_saturation != old_cond->left_saturation ||
+ cond->deadband != old_cond->deadband;
+ }
+
+ return ret;
+}
+
+/*
+ * Send ramp force report to the device
+ */
+static void pidff_set_ramp_force_report(struct pidff_device *pidff,
+ struct ff_effect *effect)
+{
+ pidff->set_ramp[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+ pidff_set_signed(&pidff->set_ramp[PID_RAMP_START],
+ effect->u.ramp.start_level);
+ pidff_set_signed(&pidff->set_ramp[PID_RAMP_END],
+ effect->u.ramp.end_level);
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_RAMP],
+ USB_DIR_OUT);
+}
+
+/*
+ * Test if ramp force parameters have changed
+ */
+static int pidff_needs_set_ramp(struct ff_effect *effect, struct ff_effect *old)
+{
+ return effect->u.ramp.start_level != old->u.ramp.start_level ||
+ effect->u.ramp.end_level != old->u.ramp.end_level;
+}
+
+/*
+ * Send a request for effect upload to the device
+ *
+ * Returns 0 if device reported success, -ENOSPC if the device reported memory
+ * is full. Upon unknown response the function will retry for 60 times, if
+ * still unsuccessful -EIO is returned.
+ */
+static int pidff_request_effect_upload(struct pidff_device *pidff, int efnum)
+{
+ int j;
+
+ pidff->create_new_effect_type->value[0] = efnum;
+ hid_submit_report(pidff->hid, pidff->reports[PID_CREATE_NEW_EFFECT],
+ USB_DIR_OUT);
+ debug("create_new_effect sent, type: %d", efnum);
+
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] = 0;
+ pidff->block_load_status->value[0] = 0;
+ hid_wait_io(pidff->hid);
+
+ for (j = 0; j < 60; j++) {
+ debug("pid_block_load requested");
+ hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_LOAD],
+ USB_DIR_IN);
+ hid_wait_io(pidff->hid);
+ if (pidff->block_load_status->value[0] ==
+ pidff->status_id[PID_BLOCK_LOAD_SUCCESS]) {
+ debug("device reported free memory: %d bytes",
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
+ return 0;
+ }
+ if (pidff->block_load_status->value[0] ==
+ pidff->status_id[PID_BLOCK_LOAD_FULL]) {
+ debug("not enough memory free: %d bytes",
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value ?
+ pidff->block_load[PID_RAM_POOL_AVAILABLE].value[0] : -1);
+ return -ENOSPC;
+ }
+ }
+ printk(KERN_ERR "hid-pidff: pid_block_load failed 60 times\n");
+ return -EIO;
+}
+
+/*
+ * Play the effect with PID id n times
+ */
+static void pidff_playback_pid(struct pidff_device *pidff, int pid_id, int n)
+{
+ pidff->effect_operation[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
+
+ if (n == 0) {
+ pidff->effect_operation_status->value[0] =
+ pidff->operation_id[PID_EFFECT_STOP];
+ } else {
+ pidff->effect_operation_status->value[0] =
+ pidff->operation_id[PID_EFFECT_START];
+ pidff->effect_operation[PID_LOOP_COUNT].value[0] = n;
+ }
+
+ hid_wait_io(pidff->hid);
+ hid_submit_report(pidff->hid, pidff->reports[PID_EFFECT_OPERATION],
+ USB_DIR_OUT);
+}
+
+/**
+ * Play the effect with effect id @effect_id for @value times
+ */
+static int pidff_playback(struct input_dev *dev, int effect_id, int value)
+{
+ struct pidff_device *pidff = dev->ff->private;
+
+ pidff_playback_pid(pidff, pidff->pid_id[effect_id], value);
+
+ return 0;
+}
+
+/*
+ * Erase effect with PID id
+ */
+static void pidff_erase_pid(struct pidff_device *pidff, int pid_id)
+{
+ pidff->block_free[PID_EFFECT_BLOCK_INDEX].value[0] = pid_id;
+ hid_submit_report(pidff->hid, pidff->reports[PID_BLOCK_FREE],
+ USB_DIR_OUT);
+}
+
+/*
+ * Stop and erase effect with effect_id
+ */
+static int pidff_erase_effect(struct input_dev *dev, int effect_id)
+{
+ struct pidff_device *pidff = dev->ff->private;
+ int pid_id = pidff->pid_id[effect_id];
+
+ debug("starting to erase %d/%d", effect_id, pidff->pid_id[effect_id]);
+ pidff_playback_pid(pidff, pid_id, 0);
+ pidff_erase_pid(pidff, pid_id);
+
+ return 0;
+}
+
+/*
+ * Effect upload handler
+ */
+static int pidff_upload_effect(struct input_dev *dev, struct ff_effect *effect,
+ struct ff_effect *old)
+{
+ struct pidff_device *pidff = dev->ff->private;
+ int type_id;
+ int error;
+
+ switch (effect->type) {
+ case FF_CONSTANT:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_CONSTANT]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_constant(effect, old))
+ pidff_set_constant_force_report(pidff, effect);
+ if (!old ||
+ pidff_needs_set_envelope(&effect->u.constant.envelope,
+ &old->u.constant.envelope))
+ pidff_set_envelope_report(pidff,
+ &effect->u.constant.envelope);
+ break;
+
+ case FF_PERIODIC:
+ if (!old) {
+ switch (effect->u.periodic.waveform) {
+ case FF_SQUARE:
+ type_id = PID_SQUARE;
+ break;
+ case FF_TRIANGLE:
+ type_id = PID_TRIANGLE;
+ break;
+ case FF_SINE:
+ type_id = PID_SINE;
+ break;
+ case FF_SAW_UP:
+ type_id = PID_SAW_UP;
+ break;
+ case FF_SAW_DOWN:
+ type_id = PID_SAW_DOWN;
+ break;
+ default:
+ printk(KERN_ERR
+ "hid-pidff: invalid waveform\n");
+ return -EINVAL;
+ }
+
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[type_id]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_periodic(effect, old))
+ pidff_set_periodic_report(pidff, effect);
+ if (!old ||
+ pidff_needs_set_envelope(&effect->u.periodic.envelope,
+ &old->u.periodic.envelope))
+ pidff_set_envelope_report(pidff,
+ &effect->u.periodic.envelope);
+ break;
+
+ case FF_RAMP:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_RAMP]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_ramp(effect, old))
+ pidff_set_ramp_force_report(pidff, effect);
+ if (!old ||
+ pidff_needs_set_envelope(&effect->u.ramp.envelope,
+ &old->u.ramp.envelope))
+ pidff_set_envelope_report(pidff,
+ &effect->u.ramp.envelope);
+ break;
+
+ case FF_SPRING:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_SPRING]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_condition(effect, old))
+ pidff_set_condition_report(pidff, effect);
+ break;
+
+ case FF_FRICTION:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_FRICTION]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_condition(effect, old))
+ pidff_set_condition_report(pidff, effect);
+ break;
+
+ case FF_DAMPER:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_DAMPER]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_condition(effect, old))
+ pidff_set_condition_report(pidff, effect);
+ break;
+
+ case FF_INERTIA:
+ if (!old) {
+ error = pidff_request_effect_upload(pidff,
+ pidff->type_id[PID_INERTIA]);
+ if (error)
+ return error;
+ }
+ if (!old || pidff_needs_set_effect(effect, old))
+ pidff_set_effect_report(pidff, effect);
+ if (!old || pidff_needs_set_condition(effect, old))
+ pidff_set_condition_report(pidff, effect);
+ break;
+
+ default:
+ printk(KERN_ERR "hid-pidff: invalid type\n");
+ return -EINVAL;
+ }
+
+ if (!old)
+ pidff->pid_id[effect->id] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0];
+
+ debug("uploaded");
+
+ return 0;
+}
+
+/*
+ * set_gain() handler
+ */
+static void pidff_set_gain(struct input_dev *dev, u16 gain)
+{
+ struct pidff_device *pidff = dev->ff->private;
+
+ pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], gain);
+ hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
+ USB_DIR_OUT);
+}
+
+static void pidff_autocenter(struct pidff_device *pidff, u16 magnitude)
+{
+ struct hid_field *field =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field;
+
+ if (!magnitude) {
+ pidff_playback_pid(pidff, field->logical_minimum, 0);
+ return;
+ }
+
+ pidff_playback_pid(pidff, field->logical_minimum, 1);
+
+ pidff->set_effect[PID_EFFECT_BLOCK_INDEX].value[0] =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum;
+ pidff->set_effect_type->value[0] = pidff->type_id[PID_SPRING];
+ pidff->set_effect[PID_DURATION].value[0] = 0;
+ pidff->set_effect[PID_TRIGGER_BUTTON].value[0] = 0;
+ pidff->set_effect[PID_TRIGGER_REPEAT_INT].value[0] = 0;
+ pidff_set(&pidff->set_effect[PID_GAIN], magnitude);
+ pidff->set_effect[PID_START_DELAY].value[0] = 0;
+
+ hid_submit_report(pidff->hid, pidff->reports[PID_SET_EFFECT],
+ USB_DIR_OUT);
+}
+
+/*
+ * pidff_set_autocenter() handler
+ */
+static void pidff_set_autocenter(struct input_dev *dev, u16 magnitude)
+{
+ struct pidff_device *pidff = dev->ff->private;
+
+ pidff_autocenter(pidff, magnitude);
+}
+
+/*
+ * Find fields from a report and fill a pidff_usage
+ */
+static int pidff_find_fields(struct pidff_usage *usage, const u8 *table,
+ struct hid_report *report, int count, int strict)
+{
+ int i, j, k, found;
+
+ for (k = 0; k < count; k++) {
+ found = 0;
+ for (i = 0; i < report->maxfield; i++) {
+ if (report->field[i]->maxusage !=
+ report->field[i]->report_count) {
+ debug("maxusage and report_count do not match, "
+ "skipping");
+ continue;
+ }
+ for (j = 0; j < report->field[i]->maxusage; j++) {
+ if (report->field[i]->usage[j].hid ==
+ (HID_UP_PID | table[k])) {
+ debug("found %d at %d->%d", k, i, j);
+ usage[k].field = report->field[i];
+ usage[k].value =
+ &report->field[i]->value[j];
+ found = 1;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ if (!found && strict) {
+ debug("failed to locate %d", k);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+/*
+ * Return index into pidff_reports for the given usage
+ */
+static int pidff_check_usage(int usage)
+{
+ int i;
+
+ for (i = 0; i < sizeof(pidff_reports); i++)
+ if (usage == (HID_UP_PID | pidff_reports[i]))
+ return i;
+
+ return -1;
+}
+
+/*
+ * Find the reports and fill pidff->reports[]
+ * report_type specifies either OUTPUT or FEATURE reports
+ */
+static void pidff_find_reports(struct hid_device *hid, int report_type,
+ struct pidff_device *pidff)
+{
+ struct hid_report *report;
+ int i, ret;
+
+ list_for_each_entry(report,
+ &hid->report_enum[report_type].report_list, list) {
+ if (report->maxfield < 1)
+ continue;
+ ret = pidff_check_usage(report->field[0]->logical);
+ if (ret != -1) {
+ debug("found usage 0x%02x from field->logical",
+ pidff_reports[ret]);
+ pidff->reports[ret] = report;
+ continue;
+ }
+
+ /*
+ * Sometimes logical collections are stacked to indicate
+ * different usages for the report and the field, in which
+ * case we want the usage of the parent. However, Linux HID
+ * implementation hides this fact, so we have to dig it up
+ * ourselves
+ */
+ i = report->field[0]->usage[0].collection_index;
+ if (i <= 0 ||
+ hid->collection[i - 1].type != HID_COLLECTION_LOGICAL)
+ continue;
+ ret = pidff_check_usage(hid->collection[i - 1].usage);
+ if (ret != -1 && !pidff->reports[ret]) {
+ debug("found usage 0x%02x from collection array",
+ pidff_reports[ret]);
+ pidff->reports[ret] = report;
+ }
+ }
+}
+
+/*
+ * Test if the required reports have been found
+ */
+static int pidff_reports_ok(struct pidff_device *pidff)
+{
+ int i;
+
+ for (i = 0; i <= PID_REQUIRED_REPORTS; i++) {
+ if (!pidff->reports[i]) {
+ debug("%d missing", i);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Find a field with a specific usage within a report
+ */
+static struct hid_field *pidff_find_special_field(struct hid_report *report,
+ int usage, int enforce_min)
+{
+ int i;
+
+ for (i = 0; i < report->maxfield; i++) {
+ if (report->field[i]->logical == (HID_UP_PID | usage) &&
+ report->field[i]->report_count > 0) {
+ if (!enforce_min ||
+ report->field[i]->logical_minimum == 1)
+ return report->field[i];
+ else {
+ printk(KERN_ERR "hid-pidff: logical_minimum "
+ "is not 1 as it should be\n");
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/*
+ * Fill a pidff->*_id struct table
+ */
+static int pidff_find_special_keys(int *keys, struct hid_field *fld,
+ const u8 *usagetable, int count)
+{
+
+ int i, j;
+ int found = 0;
+
+ for (i = 0; i < count; i++) {
+ for (j = 0; j < fld->maxusage; j++) {
+ if (fld->usage[j].hid == (HID_UP_PID | usagetable[i])) {
+ keys[i] = j + 1;
+ found++;
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+#define PIDFF_FIND_SPECIAL_KEYS(keys, field, name) \
+ pidff_find_special_keys(pidff->keys, pidff->field, pidff_ ## name, \
+ sizeof(pidff_ ## name))
+
+/*
+ * Find and check the special fields
+ */
+static int pidff_find_special_fields(struct pidff_device *pidff)
+{
+ debug("finding special fields");
+
+ pidff->create_new_effect_type =
+ pidff_find_special_field(pidff->reports[PID_CREATE_NEW_EFFECT],
+ 0x25, 1);
+ pidff->set_effect_type =
+ pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+ 0x25, 1);
+ pidff->effect_direction =
+ pidff_find_special_field(pidff->reports[PID_SET_EFFECT],
+ 0x57, 0);
+ pidff->device_control =
+ pidff_find_special_field(pidff->reports[PID_DEVICE_CONTROL],
+ 0x96, 1);
+ pidff->block_load_status =
+ pidff_find_special_field(pidff->reports[PID_BLOCK_LOAD],
+ 0x8b, 1);
+ pidff->effect_operation_status =
+ pidff_find_special_field(pidff->reports[PID_EFFECT_OPERATION],
+ 0x78, 1);
+
+ debug("search done");
+
+ if (!pidff->create_new_effect_type || !pidff->set_effect_type) {
+ printk(KERN_ERR "hid-pidff: effect lists not found\n");
+ return -1;
+ }
+
+ if (!pidff->effect_direction) {
+ printk(KERN_ERR "hid-pidff: direction field not found\n");
+ return -1;
+ }
+
+ if (!pidff->device_control) {
+ printk(KERN_ERR "hid-pidff: device control field not found\n");
+ return -1;
+ }
+
+ if (!pidff->block_load_status) {
+ printk(KERN_ERR
+ "hid-pidff: block load status field not found\n");
+ return -1;
+ }
+
+ if (!pidff->effect_operation_status) {
+ printk(KERN_ERR
+ "hid-pidff: effect operation field not found\n");
+ return -1;
+ }
+
+ pidff_find_special_keys(pidff->control_id, pidff->device_control,
+ pidff_device_control,
+ sizeof(pidff_device_control));
+
+ PIDFF_FIND_SPECIAL_KEYS(control_id, device_control, device_control);
+
+ if (!PIDFF_FIND_SPECIAL_KEYS(type_id, create_new_effect_type,
+ effect_types)) {
+ printk(KERN_ERR "hid-pidff: no effect types found\n");
+ return -1;
+ }
+
+ if (PIDFF_FIND_SPECIAL_KEYS(status_id, block_load_status,
+ block_load_status) !=
+ sizeof(pidff_block_load_status)) {
+ printk(KERN_ERR
+ "hidpidff: block load status identifiers not found\n");
+ return -1;
+ }
+
+ if (PIDFF_FIND_SPECIAL_KEYS(operation_id, effect_operation_status,
+ effect_operation_status) !=
+ sizeof(pidff_effect_operation_status)) {
+ printk(KERN_ERR
+ "hidpidff: effect operation identifiers not found\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/**
+ * Find the implemented effect types
+ */
+static int pidff_find_effects(struct pidff_device *pidff,
+ struct input_dev *dev)
+{
+ int i;
+
+ for (i = 0; i < sizeof(pidff_effect_types); i++) {
+ int pidff_type = pidff->type_id[i];
+ if (pidff->set_effect_type->usage[pidff_type].hid !=
+ pidff->create_new_effect_type->usage[pidff_type].hid) {
+ printk(KERN_ERR "hid-pidff: "
+ "effect type number %d is invalid\n", i);
+ return -1;
+ }
+ }
+
+ if (pidff->type_id[PID_CONSTANT])
+ set_bit(FF_CONSTANT, dev->ffbit);
+ if (pidff->type_id[PID_RAMP])
+ set_bit(FF_RAMP, dev->ffbit);
+ if (pidff->type_id[PID_SQUARE]) {
+ set_bit(FF_SQUARE, dev->ffbit);
+ set_bit(FF_PERIODIC, dev->ffbit);
+ }
+ if (pidff->type_id[PID_SINE]) {
+ set_bit(FF_SINE, dev->ffbit);
+ set_bit(FF_PERIODIC, dev->ffbit);
+ }
+ if (pidff->type_id[PID_TRIANGLE]) {
+ set_bit(FF_TRIANGLE, dev->ffbit);
+ set_bit(FF_PERIODIC, dev->ffbit);
+ }
+ if (pidff->type_id[PID_SAW_UP]) {
+ set_bit(FF_SAW_UP, dev->ffbit);
+ set_bit(FF_PERIODIC, dev->ffbit);
+ }
+ if (pidff->type_id[PID_SAW_DOWN]) {
+ set_bit(FF_SAW_DOWN, dev->ffbit);
+ set_bit(FF_PERIODIC, dev->ffbit);
+ }
+ if (pidff->type_id[PID_SPRING])
+ set_bit(FF_SPRING, dev->ffbit);
+ if (pidff->type_id[PID_DAMPER])
+ set_bit(FF_DAMPER, dev->ffbit);
+ if (pidff->type_id[PID_INERTIA])
+ set_bit(FF_INERTIA, dev->ffbit);
+ if (pidff->type_id[PID_FRICTION])
+ set_bit(FF_FRICTION, dev->ffbit);
+
+ return 0;
+
+}
+
+#define PIDFF_FIND_FIELDS(name, report, strict) \
+ pidff_find_fields(pidff->name, pidff_ ## name, \
+ pidff->reports[report], \
+ sizeof(pidff_ ## name), strict)
+
+/*
+ * Fill and check the pidff_usages
+ */
+static int pidff_init_fields(struct pidff_device *pidff, struct input_dev *dev)
+{
+ int envelope_ok = 0;
+
+ if (PIDFF_FIND_FIELDS(set_effect, PID_SET_EFFECT, 1)) {
+ printk(KERN_ERR
+ "hid-pidff: unknown set_effect report layout\n");
+ return -ENODEV;
+ }
+
+ PIDFF_FIND_FIELDS(block_load, PID_BLOCK_LOAD, 0);
+ if (!pidff->block_load[PID_EFFECT_BLOCK_INDEX].value) {
+ printk(KERN_ERR
+ "hid-pidff: unknown pid_block_load report layout\n");
+ return -ENODEV;
+ }
+
+ if (PIDFF_FIND_FIELDS(effect_operation, PID_EFFECT_OPERATION, 1)) {
+ printk(KERN_ERR
+ "hid-pidff: unknown effect_operation report layout\n");
+ return -ENODEV;
+ }
+
+ if (PIDFF_FIND_FIELDS(block_free, PID_BLOCK_FREE, 1)) {
+ printk(KERN_ERR
+ "hid-pidff: unknown pid_block_free report layout\n");
+ return -ENODEV;
+ }
+
+ if (!PIDFF_FIND_FIELDS(set_envelope, PID_SET_ENVELOPE, 1))
+ envelope_ok = 1;
+
+ if (pidff_find_special_fields(pidff) || pidff_find_effects(pidff, dev))
+ return -ENODEV;
+
+ if (!envelope_ok) {
+ if (test_and_clear_bit(FF_CONSTANT, dev->ffbit))
+ printk(KERN_WARNING "hid-pidff: "
+ "has constant effect but no envelope\n");
+ if (test_and_clear_bit(FF_RAMP, dev->ffbit))
+ printk(KERN_WARNING "hid-pidff: "
+ "has ramp effect but no envelope\n");
+
+ if (test_and_clear_bit(FF_PERIODIC, dev->ffbit))
+ printk(KERN_WARNING "hid-pidff: "
+ "has periodic effect but no envelope\n");
+ }
+
+ if (test_bit(FF_CONSTANT, dev->ffbit) &&
+ PIDFF_FIND_FIELDS(set_constant, PID_SET_CONSTANT, 1)) {
+ printk(KERN_WARNING
+ "hid-pidff: unknown constant effect layout\n");
+ clear_bit(FF_CONSTANT, dev->ffbit);
+ }
+
+ if (test_bit(FF_RAMP, dev->ffbit) &&
+ PIDFF_FIND_FIELDS(set_ramp, PID_SET_RAMP, 1)) {
+ printk(KERN_WARNING "hid-pidff: unknown ramp effect layout\n");
+ clear_bit(FF_RAMP, dev->ffbit);
+ }
+
+ if ((test_bit(FF_SPRING, dev->ffbit) ||
+ test_bit(FF_DAMPER, dev->ffbit) ||
+ test_bit(FF_FRICTION, dev->ffbit) ||
+ test_bit(FF_INERTIA, dev->ffbit)) &&
+ PIDFF_FIND_FIELDS(set_condition, PID_SET_CONDITION, 1)) {
+ printk(KERN_WARNING
+ "hid-pidff: unknown condition effect layout\n");
+ clear_bit(FF_SPRING, dev->ffbit);
+ clear_bit(FF_DAMPER, dev->ffbit);
+ clear_bit(FF_FRICTION, dev->ffbit);
+ clear_bit(FF_INERTIA, dev->ffbit);
+ }
+
+ if (test_bit(FF_PERIODIC, dev->ffbit) &&
+ PIDFF_FIND_FIELDS(set_periodic, PID_SET_PERIODIC, 1)) {
+ printk(KERN_WARNING
+ "hid-pidff: unknown periodic effect layout\n");
+ clear_bit(FF_PERIODIC, dev->ffbit);
+ }
+
+ PIDFF_FIND_FIELDS(pool, PID_POOL, 0);
+
+ if (!PIDFF_FIND_FIELDS(device_gain, PID_DEVICE_GAIN, 1))
+ set_bit(FF_GAIN, dev->ffbit);
+
+ return 0;
+}
+
+/*
+ * Reset the device
+ */
+static void pidff_reset(struct pidff_device *pidff)
+{
+ struct hid_device *hid = pidff->hid;
+ int i = 0;
+
+ pidff->device_control->value[0] = pidff->control_id[PID_RESET];
+ /* We reset twice as sometimes hid_wait_io isn't waiting long enough */
+ hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+ hid_wait_io(hid);
+ hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+ hid_wait_io(hid);
+
+ pidff->device_control->value[0] =
+ pidff->control_id[PID_ENABLE_ACTUATORS];
+ hid_submit_report(hid, pidff->reports[PID_DEVICE_CONTROL], USB_DIR_OUT);
+ hid_wait_io(hid);
+
+ /* pool report is sometimes messed up, refetch it */
+ hid_submit_report(hid, pidff->reports[PID_POOL], USB_DIR_IN);
+ hid_wait_io(hid);
+
+ if (pidff->pool[PID_SIMULTANEOUS_MAX].value) {
+ int sim_effects = pidff->pool[PID_SIMULTANEOUS_MAX].value[0];
+ while (sim_effects < 2) {
+ if (i++ > 20) {
+ printk(KERN_WARNING "hid-pidff: device reports "
+ "%d simultaneous effects\n",
+ sim_effects);
+ break;
+ }
+ debug("pid_pool requested again");
+ hid_submit_report(hid, pidff->reports[PID_POOL],
+ USB_DIR_IN);
+ hid_wait_io(hid);
+ }
+ }
+}
+
+/*
+ * Test if autocenter modification is using the supported method
+ */
+static int pidff_check_autocenter(struct pidff_device *pidff,
+ struct input_dev *dev)
+{
+ int error;
+
+ /*
+ * Let's find out if autocenter modification is supported
+ * Specification doesn't specify anything, so we request an
+ * effect upload and cancel it immediately. If the approved
+ * effect id was one above the minimum, then we assume the first
+ * effect id is a built-in spring type effect used for autocenter
+ */
+
+ error = pidff_request_effect_upload(pidff, 1);
+ if (error) {
+ printk(KERN_ERR "hid-pidff: upload request failed\n");
+ return error;
+ }
+
+ if (pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0] ==
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum + 1) {
+ pidff_autocenter(pidff, 0xffff);
+ set_bit(FF_AUTOCENTER, dev->ffbit);
+ } else {
+ printk(KERN_NOTICE "hid-pidff: "
+ "device has unknown autocenter control method\n");
+ }
+
+ pidff_erase_pid(pidff,
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].value[0]);
+
+ return 0;
+
+}
+
+/*
+ * Check if the device is PID and initialize it
+ */
+int hid_pidff_init(struct hid_device *hid)
+{
+ struct pidff_device *pidff;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+ struct hid_input, list);
+ struct input_dev *dev = hidinput->input;
+ struct ff_device *ff;
+ int max_effects;
+ int error;
+
+ debug("starting pid init");
+
+ if (list_empty(&hid->report_enum[HID_OUTPUT_REPORT].report_list)) {
+ debug("not a PID device, no output report");
+ return -ENODEV;
+ }
+
+ pidff = kzalloc(sizeof(*pidff), GFP_KERNEL);
+ if (!pidff)
+ return -ENOMEM;
+
+ pidff->hid = hid;
+
+ pidff_find_reports(hid, HID_OUTPUT_REPORT, pidff);
+ pidff_find_reports(hid, HID_FEATURE_REPORT, pidff);
+
+ if (!pidff_reports_ok(pidff)) {
+ debug("reports not ok, aborting");
+ error = -ENODEV;
+ goto fail;
+ }
+
+ error = pidff_init_fields(pidff, dev);
+ if (error)
+ goto fail;
+
+ pidff_reset(pidff);
+
+ if (test_bit(FF_GAIN, dev->ffbit)) {
+ pidff_set(&pidff->device_gain[PID_DEVICE_GAIN_FIELD], 0xffff);
+ hid_submit_report(pidff->hid, pidff->reports[PID_DEVICE_GAIN],
+ USB_DIR_OUT);
+ }
+
+ error = pidff_check_autocenter(pidff, dev);
+ if (error)
+ goto fail;
+
+ max_effects =
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_maximum -
+ pidff->block_load[PID_EFFECT_BLOCK_INDEX].field->logical_minimum +
+ 1;
+ debug("max effects is %d", max_effects);
+
+ if (max_effects > PID_EFFECTS_MAX)
+ max_effects = PID_EFFECTS_MAX;
+
+ if (pidff->pool[PID_SIMULTANEOUS_MAX].value)
+ debug("max simultaneous effects is %d",
+ pidff->pool[PID_SIMULTANEOUS_MAX].value[0]);
+
+ if (pidff->pool[PID_RAM_POOL_SIZE].value)
+ debug("device memory size is %d bytes",
+ pidff->pool[PID_RAM_POOL_SIZE].value[0]);
+
+ if (pidff->pool[PID_DEVICE_MANAGED_POOL].value &&
+ pidff->pool[PID_DEVICE_MANAGED_POOL].value[0] == 0) {
+ printk(KERN_NOTICE "hid-pidff: "
+ "device does not support device managed pool\n");
+ goto fail;
+ }
+
+ error = input_ff_create(dev, max_effects);
+ if (error)
+ goto fail;
+
+ ff = dev->ff;
+ ff->private = pidff;
+ ff->upload = pidff_upload_effect;
+ ff->erase = pidff_erase_effect;
+ ff->set_gain = pidff_set_gain;
+ ff->set_autocenter = pidff_set_autocenter;
+ ff->playback = pidff_playback;
+
+ printk(KERN_INFO "Force feedback for USB HID PID devices by "
+ "Anssi Hannula <anssi.hannula@gmail.com>\n");
+
+ return 0;
+
+ fail:
+ kfree(pidff);
+ return error;
+}
diff --git a/drivers/usb/input/hid-tmff.c b/drivers/usb/input/hid-tmff.c
index 534425c69c0..2d5be4c318a 100644
--- a/drivers/usb/input/hid-tmff.c
+++ b/drivers/usb/input/hid-tmff.c
@@ -28,97 +28,65 @@
*/
#include <linux/input.h>
-#include <linux/sched.h>
#undef DEBUG
#include <linux/usb.h>
-#include <linux/circ_buf.h>
-
#include "hid.h"
-#include "fixp-arith.h"
/* Usages for thrustmaster devices I know about */
#define THRUSTMASTER_USAGE_RUMBLE_LR (HID_UP_GENDESK | 0xbb)
-#define DELAY_CALC(t,delay) ((t) + (delay)*HZ/1000)
-
-/* Effect status */
-#define EFFECT_STARTED 0 /* Effect is going to play after some time */
-#define EFFECT_PLAYING 1 /* Effect is playing */
-#define EFFECT_USED 2
-
-/* For tmff_device::flags */
-#define DEVICE_CLOSING 0 /* The driver is being unitialised */
-
-/* Check that the current process can access an effect */
-#define CHECK_OWNERSHIP(effect) (current->pid == 0 \
- || effect.owner == current->pid)
-
-#define TMFF_CHECK_ID(id) ((id) >= 0 && (id) < TMFF_EFFECTS)
-#define TMFF_CHECK_OWNERSHIP(i, l) \
- (test_bit(EFFECT_USED, l->effects[i].flags) \
- && CHECK_OWNERSHIP(l->effects[i]))
-
-#define TMFF_EFFECTS 8
-
-struct tmff_effect {
- pid_t owner;
-
- struct ff_effect effect;
-
- unsigned long flags[1];
- unsigned int count; /* Number of times left to play */
-
- unsigned long play_at; /* When the effect starts to play */
- unsigned long stop_at; /* When the effect ends */
-};
struct tmff_device {
- struct hid_device *hid;
-
struct hid_report *report;
-
struct hid_field *rumble;
+};
- unsigned int effects_playing;
- struct tmff_effect effects[TMFF_EFFECTS];
- spinlock_t lock; /* device-level lock. Having locks on
- a per-effect basis could be nice, but
- isn't really necessary */
+/* Changes values from 0 to 0xffff into values from minimum to maximum */
+static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
+{
+ int ret;
- unsigned long flags[1]; /* Contains various information about the
- state of the driver for this device */
+ ret = (in * (maximum - minimum) / 0xffff) + minimum;
+ if (ret < minimum)
+ return minimum;
+ if (ret > maximum)
+ return maximum;
+ return ret;
+}
- struct timer_list timer;
-};
+static int hid_tmff_play(struct input_dev *dev, void *data, struct ff_effect *effect)
+{
+ struct hid_device *hid = dev->private;
+ struct tmff_device *tmff = data;
+ int left, right; /* Rumbling */
-/* Callbacks */
-static void hid_tmff_exit(struct hid_device *hid);
-static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value);
-static int hid_tmff_flush(struct input_dev *input, struct file *file);
-static int hid_tmff_upload_effect(struct input_dev *input,
- struct ff_effect *effect);
-static int hid_tmff_erase(struct input_dev *input, int id);
+ left = hid_tmff_scale(effect->u.rumble.weak_magnitude,
+ tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
+ right = hid_tmff_scale(effect->u.rumble.strong_magnitude,
+ tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
-/* Local functions */
-static void hid_tmff_recalculate_timer(struct tmff_device *tmff);
-static void hid_tmff_timer(unsigned long timer_data);
+ tmff->rumble->value[0] = left;
+ tmff->rumble->value[1] = right;
+ dbg("(left,right)=(%08x, %08x)", left, right);
+ hid_submit_report(hid, tmff->report, USB_DIR_OUT);
+
+ return 0;
+}
int hid_tmff_init(struct hid_device *hid)
{
- struct tmff_device *private;
+ struct tmff_device *tmff;
struct list_head *pos;
struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
struct input_dev *input_dev = hidinput->input;
+ int error;
- private = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
- if (!private)
+ tmff = kzalloc(sizeof(struct tmff_device), GFP_KERNEL);
+ if (!tmff)
return -ENOMEM;
- hid->ff_private = private;
-
/* Find the report to use */
__list_for_each(pos, &hid->report_enum[HID_OUTPUT_REPORT].report_list) {
struct hid_report *report = (struct hid_report *)pos;
@@ -142,18 +110,18 @@ int hid_tmff_init(struct hid_device *hid)
continue;
}
- if (private->report && private->report != report) {
+ if (tmff->report && tmff->report != report) {
warn("ignoring THRUSTMASTER_USAGE_RUMBLE_LR in other report");
continue;
}
- if (private->rumble && private->rumble != field) {
+ if (tmff->rumble && tmff->rumble != field) {
warn("ignoring duplicate THRUSTMASTER_USAGE_RUMBLE_LR");
continue;
}
- private->report = report;
- private->rumble = field;
+ tmff->report = report;
+ tmff->rumble = field;
set_bit(FF_RUMBLE, input_dev->ffbit);
break;
@@ -162,302 +130,17 @@ int hid_tmff_init(struct hid_device *hid)
warn("ignoring unknown output usage %08x", field->usage[0].hid);
continue;
}
-
- /* Fallthrough to here only when a valid usage is found */
- input_dev->upload_effect = hid_tmff_upload_effect;
- input_dev->flush = hid_tmff_flush;
-
- set_bit(EV_FF, input_dev->evbit);
- input_dev->ff_effects_max = TMFF_EFFECTS;
}
}
- private->hid = hid;
-
- spin_lock_init(&private->lock);
- init_timer(&private->timer);
- private->timer.data = (unsigned long)private;
- private->timer.function = hid_tmff_timer;
-
- /* Event and exit callbacks */
- hid->ff_exit = hid_tmff_exit;
- hid->ff_event = hid_tmff_event;
-
- info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
-
- return 0;
-}
-
-static void hid_tmff_exit(struct hid_device *hid)
-{
- struct tmff_device *tmff = hid->ff_private;
- unsigned long flags;
-
- spin_lock_irqsave(&tmff->lock, flags);
-
- set_bit(DEVICE_CLOSING, tmff->flags);
- del_timer_sync(&tmff->timer);
-
- spin_unlock_irqrestore(&tmff->lock, flags);
-
- kfree(tmff);
-}
-
-static int hid_tmff_event(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value)
-{
- struct tmff_device *tmff = hid->ff_private;
- struct tmff_effect *effect = &tmff->effects[code];
- unsigned long flags;
-
- if (type != EV_FF)
- return -EINVAL;
- if (!TMFF_CHECK_ID(code))
- return -EINVAL;
- if (!TMFF_CHECK_OWNERSHIP(code, tmff))
- return -EACCES;
- if (value < 0)
- return -EINVAL;
-
- spin_lock_irqsave(&tmff->lock, flags);
-
- if (value > 0) {
- set_bit(EFFECT_STARTED, effect->flags);
- clear_bit(EFFECT_PLAYING, effect->flags);
- effect->count = value;
- effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
- } else {
- clear_bit(EFFECT_STARTED, effect->flags);
- clear_bit(EFFECT_PLAYING, effect->flags);
- }
-
- hid_tmff_recalculate_timer(tmff);
-
- spin_unlock_irqrestore(&tmff->lock, flags);
-
- return 0;
-
-}
-
-/* Erase all effects this process owns */
-
-static int hid_tmff_flush(struct input_dev *dev, struct file *file)
-{
- struct hid_device *hid = dev->private;
- struct tmff_device *tmff = hid->ff_private;
- int i;
-
- for (i=0; i<dev->ff_effects_max; ++i)
-
- /* NOTE: no need to lock here. The only times EFFECT_USED is
- modified is when effects are uploaded or when an effect is
- erased. But a process cannot close its dev/input/eventX fd
- and perform ioctls on the same fd all at the same time */
-
- if (current->pid == tmff->effects[i].owner
- && test_bit(EFFECT_USED, tmff->effects[i].flags))
- if (hid_tmff_erase(dev, i))
- warn("erase effect %d failed", i);
-
-
- return 0;
-}
-
-static int hid_tmff_erase(struct input_dev *dev, int id)
-{
- struct hid_device *hid = dev->private;
- struct tmff_device *tmff = hid->ff_private;
- unsigned long flags;
-
- if (!TMFF_CHECK_ID(id))
- return -EINVAL;
- if (!TMFF_CHECK_OWNERSHIP(id, tmff))
- return -EACCES;
-
- spin_lock_irqsave(&tmff->lock, flags);
-
- tmff->effects[id].flags[0] = 0;
- hid_tmff_recalculate_timer(tmff);
-
- spin_unlock_irqrestore(&tmff->lock, flags);
-
- return 0;
-}
-
-static int hid_tmff_upload_effect(struct input_dev *input,
- struct ff_effect *effect)
-{
- struct hid_device *hid = input->private;
- struct tmff_device *tmff = hid->ff_private;
- int id;
- unsigned long flags;
-
- if (!test_bit(effect->type, input->ffbit))
- return -EINVAL;
- if (effect->id != -1 && !TMFF_CHECK_ID(effect->id))
- return -EINVAL;
-
- spin_lock_irqsave(&tmff->lock, flags);
-
- if (effect->id == -1) {
- /* Find a free effect */
- for (id = 0; id < TMFF_EFFECTS && test_bit(EFFECT_USED, tmff->effects[id].flags); ++id);
-
- if (id >= TMFF_EFFECTS) {
- spin_unlock_irqrestore(&tmff->lock, flags);
- return -ENOSPC;
- }
-
- effect->id = id;
- tmff->effects[id].owner = current->pid;
- tmff->effects[id].flags[0] = 0;
- set_bit(EFFECT_USED, tmff->effects[id].flags);
-
- } else {
- /* Re-uploading an owned effect, to change parameters */
- id = effect->id;
- clear_bit(EFFECT_PLAYING, tmff->effects[id].flags);
+ error = input_ff_create_memless(input_dev, tmff, hid_tmff_play);
+ if (error) {
+ kfree(tmff);
+ return error;
}
- tmff->effects[id].effect = *effect;
-
- hid_tmff_recalculate_timer(tmff);
+ info("Force feedback for ThrustMaster rumble pad devices by Zinx Verituse <zinx@epicsol.org>");
- spin_unlock_irqrestore(&tmff->lock, flags);
return 0;
}
-/* Start the timer for the next start/stop/delay */
-/* Always call this while tmff->lock is locked */
-
-static void hid_tmff_recalculate_timer(struct tmff_device *tmff)
-{
- int i;
- int events = 0;
- unsigned long next_time;
-
- next_time = 0; /* Shut up compiler's incorrect warning */
-
- /* Find the next change in an effect's status */
- for (i = 0; i < TMFF_EFFECTS; ++i) {
- struct tmff_effect *effect = &tmff->effects[i];
- unsigned long play_time;
-
- if (!test_bit(EFFECT_STARTED, effect->flags))
- continue;
-
- effect->stop_at = DELAY_CALC(effect->play_at, effect->effect.replay.length);
-
- if (!test_bit(EFFECT_PLAYING, effect->flags))
- play_time = effect->play_at;
- else
- play_time = effect->stop_at;
-
- events++;
-
- if (time_after(jiffies, play_time))
- play_time = jiffies;
-
- if (events == 1)
- next_time = play_time;
- else {
- if (time_after(next_time, play_time))
- next_time = play_time;
- }
- }
-
- if (!events && tmff->effects_playing) {
- /* Treat all effects turning off as an event */
- events = 1;
- next_time = jiffies;
- }
-
- if (!events) {
- /* No events, no time, no need for a timer. */
- del_timer_sync(&tmff->timer);
- return;
- }
-
- mod_timer(&tmff->timer, next_time);
-}
-
-/* Changes values from 0 to 0xffff into values from minimum to maximum */
-static inline int hid_tmff_scale(unsigned int in, int minimum, int maximum)
-{
- int ret;
-
- ret = (in * (maximum - minimum) / 0xffff) + minimum;
- if (ret < minimum)
- return minimum;
- if (ret > maximum)
- return maximum;
- return ret;
-}
-
-static void hid_tmff_timer(unsigned long timer_data)
-{
- struct tmff_device *tmff = (struct tmff_device *) timer_data;
- struct hid_device *hid = tmff->hid;
- unsigned long flags;
- int left = 0, right = 0; /* Rumbling */
- int i;
-
- spin_lock_irqsave(&tmff->lock, flags);
-
- tmff->effects_playing = 0;
-
- for (i = 0; i < TMFF_EFFECTS; ++i) {
- struct tmff_effect *effect = &tmff->effects[i];
-
- if (!test_bit(EFFECT_STARTED, effect->flags))
- continue;
-
- if (!time_after(jiffies, effect->play_at))
- continue;
-
- if (time_after(jiffies, effect->stop_at)) {
-
- dbg("Finished playing once %d", i);
- clear_bit(EFFECT_PLAYING, effect->flags);
-
- if (--effect->count <= 0) {
- dbg("Stopped %d", i);
- clear_bit(EFFECT_STARTED, effect->flags);
- continue;
- } else {
- dbg("Start again %d", i);
- effect->play_at = DELAY_CALC(jiffies, effect->effect.replay.delay);
- continue;
- }
- }
-
- ++tmff->effects_playing;
-
- set_bit(EFFECT_PLAYING, effect->flags);
-
- switch (effect->effect.type) {
- case FF_RUMBLE:
- right += effect->effect.u.rumble.strong_magnitude;
- left += effect->effect.u.rumble.weak_magnitude;
- break;
- default:
- BUG();
- break;
- }
- }
-
- left = hid_tmff_scale(left, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
- right = hid_tmff_scale(right, tmff->rumble->logical_minimum, tmff->rumble->logical_maximum);
-
- if (left != tmff->rumble->value[0] || right != tmff->rumble->value[1]) {
- tmff->rumble->value[0] = left;
- tmff->rumble->value[1] = right;
- dbg("(left,right)=(%08x, %08x)", left, right);
- hid_submit_report(hid, tmff->report, USB_DIR_OUT);
- }
-
- if (!test_bit(DEVICE_CLOSING, tmff->flags))
- hid_tmff_recalculate_timer(tmff);
-
- spin_unlock_irqrestore(&tmff->lock, flags);
-}
diff --git a/drivers/usb/input/hid-zpff.c b/drivers/usb/input/hid-zpff.c
new file mode 100644
index 00000000000..d2ce3214572
--- /dev/null
+++ b/drivers/usb/input/hid-zpff.c
@@ -0,0 +1,110 @@
+/*
+ * Force feedback support for Zeroplus based devices
+ *
+ * Copyright (c) 2005, 2006 Anssi Hannula <anssi.hannula@gmail.com>
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+
+/* #define DEBUG */
+
+#define debug(format, arg...) pr_debug("hid-zpff: " format "\n" , ## arg)
+
+#include <linux/input.h>
+#include <linux/usb.h>
+#include "hid.h"
+
+struct zpff_device {
+ struct hid_report *report;
+};
+
+static int hid_zpff_play(struct input_dev *dev, void *data,
+ struct ff_effect *effect)
+{
+ struct hid_device *hid = dev->private;
+ struct zpff_device *zpff = data;
+ int left, right;
+
+ /*
+ * The following is specified the other way around in the Zeroplus
+ * datasheet but the order below is correct for the XFX Executioner;
+ * however it is possible that the XFX Executioner is an exception
+ */
+
+ left = effect->u.rumble.strong_magnitude;
+ right = effect->u.rumble.weak_magnitude;
+ debug("called with 0x%04x 0x%04x", left, right);
+
+ left = left * 0x7f / 0xffff;
+ right = right * 0x7f / 0xffff;
+
+ zpff->report->field[2]->value[0] = left;
+ zpff->report->field[3]->value[0] = right;
+ debug("running with 0x%02x 0x%02x", left, right);
+ hid_submit_report(hid, zpff->report, USB_DIR_OUT);
+
+ return 0;
+}
+
+int hid_zpff_init(struct hid_device *hid)
+{
+ struct zpff_device *zpff;
+ struct hid_report *report;
+ struct hid_input *hidinput = list_entry(hid->inputs.next,
+ struct hid_input, list);
+ struct list_head *report_list =
+ &hid->report_enum[HID_OUTPUT_REPORT].report_list;
+ struct input_dev *dev = hidinput->input;
+ int error;
+
+ if (list_empty(report_list)) {
+ printk(KERN_ERR "hid-zpff: no output report found\n");
+ return -ENODEV;
+ }
+
+ report = list_entry(report_list->next, struct hid_report, list);
+
+ if (report->maxfield < 4) {
+ printk(KERN_ERR "hid-zpff: not enough fields in report\n");
+ return -ENODEV;
+ }
+
+ zpff = kzalloc(sizeof(struct zpff_device), GFP_KERNEL);
+ if (!zpff)
+ return -ENOMEM;
+
+ set_bit(FF_RUMBLE, dev->ffbit);
+
+ error = input_ff_create_memless(dev, zpff, hid_zpff_play);
+ if (error) {
+ kfree(zpff);
+ return error;
+ }
+
+ zpff->report = report;
+ zpff->report->field[0]->value[0] = 0x00;
+ zpff->report->field[1]->value[0] = 0x02;
+ zpff->report->field[2]->value[0] = 0x00;
+ zpff->report->field[3]->value[0] = 0x00;
+ hid_submit_report(hid, zpff->report, USB_DIR_OUT);
+
+ printk(KERN_INFO "Force feedback for Zeroplus based devices by "
+ "Anssi Hannula <anssi.hannula@gmail.com>\n");
+
+ return 0;
+}
diff --git a/drivers/usb/input/hid.h b/drivers/usb/input/hid.h
index 778e575de35..b03fd9b075d 100644
--- a/drivers/usb/input/hid.h
+++ b/drivers/usb/input/hid.h
@@ -449,11 +449,6 @@ struct hid_device { /* device report descriptor */
char phys[64]; /* Device physical location */
char uniq[64]; /* Device unique identifier (serial #) */
- void *ff_private; /* Private data for the force-feedback driver */
- void (*ff_exit)(struct hid_device*); /* Called by hid_exit_ff(hid) */
- int (*ff_event)(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value);
-
#ifdef CONFIG_USB_HIDINPUT_POWERBOOK
unsigned long pb_pressed_fn[NBITS(KEY_MAX)];
unsigned long pb_pressed_numlock[NBITS(KEY_MAX)];
@@ -521,29 +516,22 @@ void hid_close(struct hid_device *);
int hid_set_field(struct hid_field *, unsigned, __s32);
void hid_submit_report(struct hid_device *, struct hid_report *, unsigned char dir);
void hid_init_reports(struct hid_device *hid);
-struct hid_field *hid_find_field_by_usage(struct hid_device *hid, __u32 wanted_usage, int type);
int hid_wait_io(struct hid_device* hid);
#ifdef CONFIG_HID_FF
int hid_ff_init(struct hid_device *hid);
+
+int hid_lgff_init(struct hid_device *hid);
+int hid_tmff_init(struct hid_device *hid);
+int hid_zpff_init(struct hid_device *hid);
+#ifdef CONFIG_HID_PID
+int hid_pidff_init(struct hid_device *hid);
+#else
+static inline int hid_pidff_init(struct hid_device *hid) { return -ENODEV; }
+#endif
+
#else
static inline int hid_ff_init(struct hid_device *hid) { return -1; }
#endif
-static inline void hid_ff_exit(struct hid_device *hid)
-{
- if (hid->ff_exit)
- hid->ff_exit(hid);
-}
-static inline int hid_ff_event(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value)
-{
- if (hid->ff_event)
- return hid->ff_event(hid, input, type, code, value);
- return -ENOSYS;
-}
-
-int hid_lgff_init(struct hid_device* hid);
-int hid_tmff_init(struct hid_device* hid);
-int hid_pid_init(struct hid_device* hid);
diff --git a/drivers/usb/input/hiddev.c b/drivers/usb/input/hiddev.c
index f6b839c257a..a2b419d1374 100644
--- a/drivers/usb/input/hiddev.c
+++ b/drivers/usb/input/hiddev.c
@@ -722,7 +722,7 @@ inval:
return -EINVAL;
}
-static struct file_operations hiddev_fops = {
+static const struct file_operations hiddev_fops = {
.owner = THIS_MODULE,
.read = hiddev_read,
.write = hiddev_write,
diff --git a/drivers/usb/input/itmtouch.c b/drivers/usb/input/itmtouch.c
index 86acb5f1907..61966d719ca 100644
--- a/drivers/usb/input/itmtouch.c
+++ b/drivers/usb/input/itmtouch.c
@@ -87,7 +87,7 @@ static void itmtouch_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/keyspan_remote.c b/drivers/usb/input/keyspan_remote.c
index 4723b310f27..a9035955157 100644
--- a/drivers/usb/input/keyspan_remote.c
+++ b/drivers/usb/input/keyspan_remote.c
@@ -420,8 +420,7 @@ static struct usb_endpoint_descriptor *keyspan_get_in_endpoint(struct usb_host_i
for (i = 0; i < iface->desc.bNumEndpoints; ++i) {
endpoint = &iface->endpoint[i].desc;
- if ((endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found our interrupt in endpoint */
return endpoint;
}
diff --git a/drivers/usb/input/mtouchusb.c b/drivers/usb/input/mtouchusb.c
index a9ccda8810e..5dce951f275 100644
--- a/drivers/usb/input/mtouchusb.c
+++ b/drivers/usb/input/mtouchusb.c
@@ -107,7 +107,7 @@ static void mtouchusb_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/pid.c b/drivers/usb/input/pid.c
deleted file mode 100644
index d9d9f656b8c..00000000000
--- a/drivers/usb/input/pid.c
+++ /dev/null
@@ -1,295 +0,0 @@
-/*
- * PID Force feedback support for hid devices.
- *
- * Copyright (c) 2002 Rodrigo Damazio.
- * Portions by Johann Deneux and Bjorn Augustson
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <rdamazio@lsi.usp.br>
- */
-
-#include <linux/config.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/mm.h>
-#include <linux/smp_lock.h>
-#include <linux/spinlock.h>
-#include <linux/input.h>
-#include <linux/usb.h>
-#include "hid.h"
-#include "pid.h"
-
-#define CHECK_OWNERSHIP(i, hid_pid) \
- ((i) < FF_EFFECTS_MAX && i >= 0 && \
- test_bit(FF_PID_FLAGS_USED, &hid_pid->effects[(i)].flags) && \
- (current->pid == 0 || \
- (hid_pid)->effects[(i)].owner == current->pid))
-
-/* Called when a transfer is completed */
-static void hid_pid_ctrl_out(struct urb *u, struct pt_regs *regs)
-{
- dev_dbg(&u->dev->dev, "hid_pid_ctrl_out - Transfer Completed\n");
-}
-
-static void hid_pid_exit(struct hid_device *hid)
-{
- struct hid_ff_pid *private = hid->ff_private;
-
- if (private->urbffout) {
- usb_kill_urb(private->urbffout);
- usb_free_urb(private->urbffout);
- }
-}
-
-static int pid_upload_periodic(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
-{
- dev_info(&pid->hid->dev->dev, "requested periodic force upload\n");
- return 0;
-}
-
-static int pid_upload_constant(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
-{
- dev_info(&pid->hid->dev->dev, "requested constant force upload\n");
- return 0;
-}
-
-static int pid_upload_condition(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
-{
- dev_info(&pid->hid->dev->dev, "requested Condition force upload\n");
- return 0;
-}
-
-static int pid_upload_ramp(struct hid_ff_pid *pid, struct ff_effect *effect, int is_update)
-{
- dev_info(&pid->hid->dev->dev, "request ramp force upload\n");
- return 0;
-}
-
-static int hid_pid_event(struct hid_device *hid, struct input_dev *input,
- unsigned int type, unsigned int code, int value)
-{
- dev_dbg(&hid->dev->dev, "PID event received: type=%d,code=%d,value=%d.\n", type, code, value);
-
- if (type != EV_FF)
- return -1;
-
- return 0;
-}
-
-/* Lock must be held by caller */
-static void hid_pid_ctrl_playback(struct hid_device *hid, struct hid_pid_effect *effect, int play)
-{
- if (play)
- set_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
- else
- clear_bit(FF_PID_FLAGS_PLAYING, &effect->flags);
-}
-
-static int hid_pid_erase(struct input_dev *dev, int id)
-{
- struct hid_device *hid = dev->private;
- struct hid_ff_pid *pid = hid->ff_private;
- struct hid_field *field;
- unsigned long flags;
- int ret;
-
- if (!CHECK_OWNERSHIP(id, pid))
- return -EACCES;
-
- /* Find report */
- field = hid_find_field_by_usage(hid, HID_UP_PID | FF_PID_USAGE_BLOCK_FREE,
- HID_OUTPUT_REPORT);
- if (!field) {
- dev_err(&hid->dev->dev, "couldn't find report\n");
- return -EIO;
- }
-
- ret = hid_set_field(field, 0, pid->effects[id].device_id);
- if (ret) {
- dev_err(&hid->dev->dev, "couldn't set field\n");
- return ret;
- }
-
- hid_submit_report(hid, field->report, USB_DIR_OUT);
-
- spin_lock_irqsave(&pid->lock, flags);
- hid_pid_ctrl_playback(hid, pid->effects + id, 0);
- pid->effects[id].flags = 0;
- spin_unlock_irqrestore(&pid->lock, flags);
-
- return 0;
-}
-
-/* Erase all effects this process owns */
-static int hid_pid_flush(struct input_dev *dev, struct file *file)
-{
- struct hid_device *hid = dev->private;
- struct hid_ff_pid *pid = hid->ff_private;
- int i;
-
- /*NOTE: no need to lock here. The only times EFFECT_USED is
- modified is when effects are uploaded or when an effect is
- erased. But a process cannot close its dev/input/eventX fd
- and perform ioctls on the same fd all at the same time */
- /*FIXME: multiple threads, anyone? */
- for (i = 0; i < dev->ff_effects_max; ++i)
- if (current->pid == pid->effects[i].owner
- && test_bit(FF_PID_FLAGS_USED, &pid->effects[i].flags))
- if (hid_pid_erase(dev, i))
- dev_warn(&hid->dev->dev, "erase effect %d failed", i);
-
- return 0;
-}
-
-static int hid_pid_upload_effect(struct input_dev *dev,
- struct ff_effect *effect)
-{
- struct hid_ff_pid *pid_private = (struct hid_ff_pid *)(dev->private);
- int ret;
- int is_update;
- unsigned long flags;
-
- dev_dbg(&pid_private->hid->dev->dev, "upload effect called: effect_type=%x\n", effect->type);
- /* Check this effect type is supported by this device */
- if (!test_bit(effect->type, dev->ffbit)) {
- dev_dbg(&pid_private->hid->dev->dev,
- "invalid kind of effect requested.\n");
- return -EINVAL;
- }
-
- /*
- * If we want to create a new effect, get a free id
- */
- if (effect->id == -1) {
- int id = 0;
-
- // Spinlock so we don`t get a race condition when choosing IDs
- spin_lock_irqsave(&pid_private->lock, flags);
-
- while (id < FF_EFFECTS_MAX)
- if (!test_and_set_bit(FF_PID_FLAGS_USED, &pid_private->effects[id++].flags))
- break;
-
- if (id == FF_EFFECTS_MAX) {
- spin_unlock_irqrestore(&pid_private->lock, flags);
-// TEMP - We need to get ff_effects_max correctly first: || id >= dev->ff_effects_max) {
- dev_dbg(&pid_private->hid->dev->dev, "Not enough device memory\n");
- return -ENOMEM;
- }
-
- effect->id = id;
- dev_dbg(&pid_private->hid->dev->dev, "effect ID is %d.\n", id);
- pid_private->effects[id].owner = current->pid;
- pid_private->effects[id].flags = (1 << FF_PID_FLAGS_USED);
- spin_unlock_irqrestore(&pid_private->lock, flags);
-
- is_update = FF_PID_FALSE;
- } else {
- /* We want to update an effect */
- if (!CHECK_OWNERSHIP(effect->id, pid_private))
- return -EACCES;
-
- /* Parameter type cannot be updated */
- if (effect->type != pid_private->effects[effect->id].effect.type)
- return -EINVAL;
-
- /* Check the effect is not already being updated */
- if (test_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags))
- return -EAGAIN;
-
- is_update = FF_PID_TRUE;
- }
-
- /*
- * Upload the effect
- */
- switch (effect->type) {
- case FF_PERIODIC:
- ret = pid_upload_periodic(pid_private, effect, is_update);
- break;
-
- case FF_CONSTANT:
- ret = pid_upload_constant(pid_private, effect, is_update);
- break;
-
- case FF_SPRING:
- case FF_FRICTION:
- case FF_DAMPER:
- case FF_INERTIA:
- ret = pid_upload_condition(pid_private, effect, is_update);
- break;
-
- case FF_RAMP:
- ret = pid_upload_ramp(pid_private, effect, is_update);
- break;
-
- default:
- dev_dbg(&pid_private->hid->dev->dev,
- "invalid type of effect requested - %x.\n",
- effect->type);
- return -EINVAL;
- }
- /* If a packet was sent, forbid new updates until we are notified
- * that the packet was updated
- */
- if (ret == 0)
- set_bit(FF_PID_FLAGS_UPDATING, &pid_private->effects[effect->id].flags);
- pid_private->effects[effect->id].effect = *effect;
- return ret;
-}
-
-int hid_pid_init(struct hid_device *hid)
-{
- struct hid_ff_pid *private;
- struct hid_input *hidinput = list_entry(hid->inputs.next, struct hid_input, list);
- struct input_dev *input_dev = hidinput->input;
-
- private = hid->ff_private = kzalloc(sizeof(struct hid_ff_pid), GFP_KERNEL);
- if (!private)
- return -ENOMEM;
-
- private->hid = hid;
-
- hid->ff_exit = hid_pid_exit;
- hid->ff_event = hid_pid_event;
-
- /* Open output URB */
- if (!(private->urbffout = usb_alloc_urb(0, GFP_KERNEL))) {
- kfree(private);
- return -1;
- }
-
- usb_fill_control_urb(private->urbffout, hid->dev, 0,
- (void *)&private->ffcr, private->ctrl_buffer, 8,
- hid_pid_ctrl_out, hid);
-
- input_dev->upload_effect = hid_pid_upload_effect;
- input_dev->flush = hid_pid_flush;
- input_dev->ff_effects_max = 8; // A random default
- set_bit(EV_FF, input_dev->evbit);
- set_bit(EV_FF_STATUS, input_dev->evbit);
-
- spin_lock_init(&private->lock);
-
- printk(KERN_INFO "Force feedback driver for PID devices by Rodrigo Damazio <rdamazio@lsi.usp.br>.\n");
-
- return 0;
-}
diff --git a/drivers/usb/input/pid.h b/drivers/usb/input/pid.h
deleted file mode 100644
index a2cb9627ed0..00000000000
--- a/drivers/usb/input/pid.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * PID Force feedback support for hid devices.
- *
- * Copyright (c) 2002 Rodrigo Damazio.
- */
-
-/*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * Should you need to contact me, the author, you can do so by
- * e-mail - mail your message to <rdamazio@lsi.usp.br>
- */
-
-#define FF_EFFECTS_MAX 64
-
-#define FF_PID_FLAGS_USED 1 /* If the effect exists */
-#define FF_PID_FLAGS_UPDATING 2 /* If the effect is being updated */
-#define FF_PID_FLAGS_PLAYING 3 /* If the effect is currently being played */
-
-#define FF_PID_FALSE 0
-#define FF_PID_TRUE 1
-
-struct hid_pid_effect {
- unsigned long flags;
- pid_t owner;
- unsigned int device_id; /* The device-assigned ID */
- struct ff_effect effect;
-};
-
-struct hid_ff_pid {
- struct hid_device *hid;
- unsigned long gain;
-
- struct urb *urbffout;
- struct usb_ctrlrequest ffcr;
- spinlock_t lock;
-
- unsigned char ctrl_buffer[8];
-
- struct hid_pid_effect effects[FF_EFFECTS_MAX];
-};
-
-/*
- * Constants from the PID usage table (still far from complete)
- */
-
-#define FF_PID_USAGE_BLOCK_LOAD 0x89UL
-#define FF_PID_USAGE_BLOCK_FREE 0x90UL
-#define FF_PID_USAGE_NEW_EFFECT 0xABUL
-#define FF_PID_USAGE_POOL_REPORT 0x7FUL
diff --git a/drivers/usb/input/powermate.c b/drivers/usb/input/powermate.c
index b3c0d0c3eae..f0f8db6810a 100644
--- a/drivers/usb/input/powermate.c
+++ b/drivers/usb/input/powermate.c
@@ -313,9 +313,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
- if (!(endpoint->bEndpointAddress & 0x80))
- return -EIO;
- if ((endpoint->bmAttributes & 3) != 3)
+ if (!usb_endpoint_is_int_in(endpoint))
return -EIO;
usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
diff --git a/drivers/usb/input/touchkitusb.c b/drivers/usb/input/touchkitusb.c
index 0149043ffb9..30b9f820e7a 100644
--- a/drivers/usb/input/touchkitusb.c
+++ b/drivers/usb/input/touchkitusb.c
@@ -201,7 +201,7 @@ static void touchkit_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
diff --git a/drivers/usb/input/trancevibrator.c b/drivers/usb/input/trancevibrator.c
new file mode 100644
index 00000000000..33cd91d11ec
--- /dev/null
+++ b/drivers/usb/input/trancevibrator.c
@@ -0,0 +1,159 @@
+/*
+ * PlayStation 2 Trance Vibrator driver
+ *
+ * Copyright (C) 2006 Sam Hocevar <sam@zoy.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* Standard include files */
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+/* Version Information */
+#define DRIVER_VERSION "v1.1"
+#define DRIVER_AUTHOR "Sam Hocevar, sam@zoy.org"
+#define DRIVER_DESC "PlayStation 2 Trance Vibrator driver"
+
+#define TRANCEVIBRATOR_VENDOR_ID 0x0b49 /* ASCII Corporation */
+#define TRANCEVIBRATOR_PRODUCT_ID 0x064f /* Trance Vibrator */
+
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(TRANCEVIBRATOR_VENDOR_ID, TRANCEVIBRATOR_PRODUCT_ID) },
+ { },
+};
+MODULE_DEVICE_TABLE (usb, id_table);
+
+/* Driver-local specific stuff */
+struct trancevibrator {
+ struct usb_device *udev;
+ unsigned int speed;
+};
+
+static ssize_t show_speed(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct trancevibrator *tv = usb_get_intfdata(intf);
+
+ return sprintf(buf, "%d\n", tv->speed);
+}
+
+static ssize_t set_speed(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct usb_interface *intf = to_usb_interface(dev);
+ struct trancevibrator *tv = usb_get_intfdata(intf);
+ int temp, retval;
+
+ temp = simple_strtoul(buf, NULL, 10);
+ if (temp > 255)
+ temp = 255;
+ else if (temp < 0)
+ temp = 0;
+ tv->speed = temp;
+
+ dev_dbg(&tv->udev->dev, "speed = %d\n", tv->speed);
+
+ /* Set speed */
+ retval = usb_control_msg(tv->udev, usb_sndctrlpipe(tv->udev, 0),
+ 0x01, /* vendor request: set speed */
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_OTHER,
+ tv->speed, /* speed value */
+ 0, NULL, 0, USB_CTRL_GET_TIMEOUT);
+ if (retval) {
+ dev_dbg(&tv->udev->dev, "retval = %d\n", retval);
+ return retval;
+ }
+ return count;
+}
+
+static DEVICE_ATTR(speed, S_IWUGO | S_IRUGO, show_speed, set_speed);
+
+static int tv_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct trancevibrator *dev;
+ int retval;
+
+ dev = kzalloc(sizeof(struct trancevibrator), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ dev->udev = usb_get_dev(udev);
+ usb_set_intfdata(interface, dev);
+ retval = device_create_file(&interface->dev, &dev_attr_speed);
+ if (retval)
+ goto error_create_file;
+
+ return 0;
+
+error_create_file:
+ usb_put_dev(udev);
+ usb_set_intfdata(interface, NULL);
+error:
+ kfree(dev);
+ return retval;
+}
+
+static void tv_disconnect(struct usb_interface *interface)
+{
+ struct trancevibrator *dev;
+
+ dev = usb_get_intfdata (interface);
+ usb_set_intfdata(interface, NULL);
+ device_remove_file(&interface->dev, &dev_attr_speed);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+}
+
+/* USB subsystem object */
+static struct usb_driver tv_driver = {
+ .name = "trancevibrator",
+ .probe = tv_probe,
+ .disconnect = tv_disconnect,
+ .id_table = id_table,
+};
+
+static int __init tv_init(void)
+{
+ int retval = usb_register(&tv_driver);
+ if (retval) {
+ err("usb_register failed. Error number %d", retval);
+ return retval;
+ }
+
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return 0;
+}
+
+static void __exit tv_exit(void)
+{
+ usb_deregister(&tv_driver);
+}
+
+module_init (tv_init);
+module_exit (tv_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/input/usbmouse.c b/drivers/usb/input/usbmouse.c
index 446935b671d..0fb792be95e 100644
--- a/drivers/usb/input/usbmouse.c
+++ b/drivers/usb/input/usbmouse.c
@@ -218,7 +218,7 @@ static void usb_mouse_disconnect(struct usb_interface *intf)
static struct usb_device_id usb_mouse_id_table [] = {
{ USB_INTERFACE_INFO(3, 1, 2) },
- { } /* Terminating entry */
+ { } /* Terminating entry */
};
MODULE_DEVICE_TABLE (usb, usb_mouse_id_table);
diff --git a/drivers/usb/input/usbtouchscreen.c b/drivers/usb/input/usbtouchscreen.c
index a338bf4c2d7..4640d1000d8 100644
--- a/drivers/usb/input/usbtouchscreen.c
+++ b/drivers/usb/input/usbtouchscreen.c
@@ -2,9 +2,12 @@
* usbtouchscreen.c
* Driver for USB Touchscreens, supporting those devices:
* - eGalax Touchkit
- * - 3M/Microtouch
+ * includes eTurboTouch CT-410/510/700
+ * - 3M/Microtouch EX II series
* - ITM
* - PanJit TouchSet
+ * - eTurboTouch
+ * - Gunze AHL61
*
* Copyright (C) 2004-2006 by Daniel Ritz <daniel.ritz@gmx.ch>
* Copyright (C) by Todd E. Johnson (mtouchusb.c)
@@ -42,7 +45,7 @@
#include <linux/usb/input.h>
-#define DRIVER_VERSION "v0.3"
+#define DRIVER_VERSION "v0.4"
#define DRIVER_AUTHOR "Daniel Ritz <daniel.ritz@gmx.ch>"
#define DRIVER_DESC "USB Touchscreen Driver"
@@ -60,6 +63,7 @@ struct usbtouch_device_info {
int flags;
void (*process_pkt) (struct usbtouch_usb *usbtouch, struct pt_regs *regs, unsigned char *pkt, int len);
+ int (*get_pkt_len) (unsigned char *pkt, int len);
int (*read_data) (unsigned char *pkt, int *x, int *y, int *touch, int *press);
int (*init) (struct usbtouch_usb *usbtouch);
};
@@ -81,8 +85,16 @@ struct usbtouch_usb {
char phys[64];
};
-static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
- struct pt_regs *regs, unsigned char *pkt, int len);
+
+#if defined(CONFIG_USB_TOUCHSCREEN_EGALAX) || defined(CONFIG_USB_TOUCHSCREEN_ETURBO)
+#define MULTI_PACKET
+#endif
+
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ struct pt_regs *regs,
+ unsigned char *pkt, int len);
+#endif
/* device types */
enum {
@@ -91,14 +103,19 @@ enum {
DEVTYPE_PANJIT,
DEVTYPE_3M,
DEVTYPE_ITM,
+ DEVTYPE_ETURBO,
+ DEVTYPE_GUNZE,
};
static struct usb_device_id usbtouch_devices[] = {
#ifdef CONFIG_USB_TOUCHSCREEN_EGALAX
{USB_DEVICE(0x3823, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x3823, 0x0002), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0123, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0001), .driver_info = DEVTYPE_EGALAX},
{USB_DEVICE(0x0eef, 0x0002), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0001), .driver_info = DEVTYPE_EGALAX},
+ {USB_DEVICE(0x1234, 0x0002), .driver_info = DEVTYPE_EGALAX},
#endif
#ifdef CONFIG_USB_TOUCHSCREEN_PANJIT
@@ -116,6 +133,14 @@ static struct usb_device_id usbtouch_devices[] = {
{USB_DEVICE(0x0403, 0xf9e9), .driver_info = DEVTYPE_ITM},
#endif
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ {USB_DEVICE(0x1234, 0x5678), .driver_info = DEVTYPE_ETURBO},
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ {USB_DEVICE(0x0637, 0x0001), .driver_info = DEVTYPE_GUNZE},
+#endif
+
{}
};
@@ -140,82 +165,23 @@ static int egalax_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
*touch = pkt[0] & 0x01;
return 1;
-
}
-static int egalax_get_pkt_len(unsigned char *buf)
+static int egalax_get_pkt_len(unsigned char *buf, int len)
{
switch (buf[0] & EGALAX_PKT_TYPE_MASK) {
case EGALAX_PKT_TYPE_REPT:
return 5;
case EGALAX_PKT_TYPE_DIAG:
+ if (len < 2)
+ return -1;
+
return buf[1] + 2;
}
return 0;
}
-
-static void egalax_process(struct usbtouch_usb *usbtouch, struct pt_regs *regs,
- unsigned char *pkt, int len)
-{
- unsigned char *buffer;
- int pkt_len, buf_len, pos;
-
- /* if the buffer contains data, append */
- if (unlikely(usbtouch->buf_len)) {
- int tmp;
-
- /* if only 1 byte in buffer, add another one to get length */
- if (usbtouch->buf_len == 1)
- usbtouch->buffer[1] = pkt[0];
-
- pkt_len = egalax_get_pkt_len(usbtouch->buffer);
-
- /* unknown packet: drop everything */
- if (!pkt_len)
- return;
-
- /* append, process */
- tmp = pkt_len - usbtouch->buf_len;
- memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
- usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
-
- buffer = pkt + tmp;
- buf_len = len - tmp;
- } else {
- buffer = pkt;
- buf_len = len;
- }
-
- /* only one byte left in buffer */
- if (unlikely(buf_len == 1)) {
- usbtouch->buffer[0] = buffer[0];
- usbtouch->buf_len = 1;
- return;
- }
-
- /* loop over the buffer */
- pos = 0;
- while (pos < buf_len) {
- /* get packet len */
- pkt_len = egalax_get_pkt_len(buffer + pos);
-
- /* unknown packet: drop everything */
- if (unlikely(!pkt_len))
- return;
-
- /* full packet: process */
- if (likely(pkt_len <= buf_len)) {
- usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
- } else {
- /* incomplete packet: save in buffer */
- memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
- usbtouch->buf_len = buf_len - pos;
- }
- pos += pkt_len;
- }
-}
#endif
@@ -254,7 +220,7 @@ static int mtouch_read_data(unsigned char *pkt, int *x, int *y, int *touch, int
static int mtouch_init(struct usbtouch_usb *usbtouch)
{
- int ret;
+ int ret, i;
ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
MTOUCHUSB_RESET,
@@ -264,15 +230,20 @@ static int mtouch_init(struct usbtouch_usb *usbtouch)
__FUNCTION__, ret);
if (ret < 0)
return ret;
-
- ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
- MTOUCHUSB_ASYNC_REPORT,
- USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
- 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
- dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
- __FUNCTION__, ret);
- if (ret < 0)
- return ret;
+ msleep(150);
+
+ for (i = 0; i < 3; i++) {
+ ret = usb_control_msg(usbtouch->udev, usb_rcvctrlpipe(usbtouch->udev, 0),
+ MTOUCHUSB_ASYNC_REPORT,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ 1, 1, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ dbg("%s - usb_control_msg - MTOUCHUSB_ASYNC_REPORT - bytes|err: %d",
+ __FUNCTION__, ret);
+ if (ret >= 0)
+ break;
+ if (ret != -EPIPE)
+ return ret;
+ }
return 0;
}
@@ -296,6 +267,54 @@ static int itm_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *pr
/*****************************************************************************
+ * eTurboTouch part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+static int eturbo_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+ unsigned int shift;
+
+ /* packets should start with sync */
+ if (!(pkt[0] & 0x80))
+ return 0;
+
+ shift = (6 - (pkt[0] & 0x03));
+ *x = ((pkt[3] << 7) | pkt[4]) >> shift;
+ *y = ((pkt[1] << 7) | pkt[2]) >> shift;
+ *touch = (pkt[0] & 0x10) ? 1 : 0;
+
+ return 1;
+}
+
+static int eturbo_get_pkt_len(unsigned char *buf, int len)
+{
+ if (buf[0] & 0x80)
+ return 5;
+ if (buf[0] == 0x01)
+ return 3;
+ return 0;
+}
+#endif
+
+
+/*****************************************************************************
+ * Gunze part
+ */
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+static int gunze_read_data(unsigned char *pkt, int *x, int *y, int *touch, int *press)
+{
+ if (!(pkt[0] & 0x80) || ((pkt[1] | pkt[2] | pkt[3]) & 0x80))
+ return 0;
+
+ *x = ((pkt[0] & 0x1F) << 7) | (pkt[2] & 0x7F);
+ *y = ((pkt[1] & 0x1F) << 7) | (pkt[3] & 0x7F);
+ *touch = pkt[0] & 0x20;
+
+ return 1;
+}
+#endif
+
+/*****************************************************************************
* the different device descriptors
*/
static struct usbtouch_device_info usbtouch_dev_info[] = {
@@ -307,7 +326,8 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.max_yc = 0x07ff,
.rept_size = 16,
.flags = USBTOUCH_FLG_BUFFER,
- .process_pkt = egalax_process,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = egalax_get_pkt_len,
.read_data = egalax_read_data,
},
#endif
@@ -346,6 +366,31 @@ static struct usbtouch_device_info usbtouch_dev_info[] = {
.read_data = itm_read_data,
},
#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_ETURBO
+ [DEVTYPE_ETURBO] = {
+ .min_xc = 0x0,
+ .max_xc = 0x07ff,
+ .min_yc = 0x0,
+ .max_yc = 0x07ff,
+ .rept_size = 8,
+ .flags = USBTOUCH_FLG_BUFFER,
+ .process_pkt = usbtouch_process_multi,
+ .get_pkt_len = eturbo_get_pkt_len,
+ .read_data = eturbo_read_data,
+ },
+#endif
+
+#ifdef CONFIG_USB_TOUCHSCREEN_GUNZE
+ [DEVTYPE_GUNZE] = {
+ .min_xc = 0x0,
+ .max_xc = 0x0fff,
+ .min_yc = 0x0,
+ .max_yc = 0x0fff,
+ .rept_size = 4,
+ .read_data = gunze_read_data,
+ },
+#endif
};
@@ -377,6 +422,83 @@ static void usbtouch_process_pkt(struct usbtouch_usb *usbtouch,
}
+#ifdef MULTI_PACKET
+static void usbtouch_process_multi(struct usbtouch_usb *usbtouch,
+ struct pt_regs *regs,
+ unsigned char *pkt, int len)
+{
+ unsigned char *buffer;
+ int pkt_len, pos, buf_len, tmp;
+
+ /* process buffer */
+ if (unlikely(usbtouch->buf_len)) {
+ /* try to get size */
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+
+ /* drop? */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* need to append -pkt_len bytes before able to get size */
+ if (unlikely(pkt_len < 0)) {
+ int append = -pkt_len;
+ if (unlikely(append > len))
+ append = len;
+ if (usbtouch->buf_len + append >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, append);
+ usbtouch->buf_len += append;
+
+ pkt_len = usbtouch->type->get_pkt_len(
+ usbtouch->buffer, usbtouch->buf_len);
+ if (pkt_len < 0)
+ return;
+ }
+
+ /* append */
+ tmp = pkt_len - usbtouch->buf_len;
+ if (usbtouch->buf_len + tmp >= usbtouch->type->rept_size)
+ goto out_flush_buf;
+ memcpy(usbtouch->buffer + usbtouch->buf_len, pkt, tmp);
+ usbtouch_process_pkt(usbtouch, regs, usbtouch->buffer, pkt_len);
+
+ buffer = pkt + tmp;
+ buf_len = len - tmp;
+ } else {
+ buffer = pkt;
+ buf_len = len;
+ }
+
+ /* loop over the received packet, process */
+ pos = 0;
+ while (pos < buf_len) {
+ /* get packet len */
+ pkt_len = usbtouch->type->get_pkt_len(buffer + pos, len);
+
+ /* unknown packet: drop everything */
+ if (unlikely(!pkt_len))
+ goto out_flush_buf;
+
+ /* full packet: process */
+ if (likely((pkt_len > 0) && (pkt_len <= buf_len - pos))) {
+ usbtouch_process_pkt(usbtouch, regs, buffer + pos, pkt_len);
+ } else {
+ /* incomplete packet: save in buffer */
+ memcpy(usbtouch->buffer, buffer + pos, buf_len - pos);
+ usbtouch->buf_len = buf_len - pos;
+ return;
+ }
+ pos += pkt_len;
+ }
+
+out_flush_buf:
+ usbtouch->buf_len = 0;
+ return;
+}
+#endif
+
+
static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
{
struct usbtouch_usb *usbtouch = urb->context;
@@ -386,7 +508,7 @@ static void usbtouch_irq(struct urb *urb, struct pt_regs *regs)
case 0:
/* success */
break;
- case -ETIMEDOUT:
+ case -ETIME:
/* this urb is timing out */
dbg("%s - urb timed out - was the device unplugged?",
__FUNCTION__);
@@ -452,7 +574,7 @@ static int usbtouch_probe(struct usb_interface *intf,
struct usb_endpoint_descriptor *endpoint;
struct usb_device *udev = interface_to_usbdev(intf);
struct usbtouch_device_info *type;
- int err;
+ int err = -ENOMEM;
interface = intf->cur_altsetting;
endpoint = &interface->endpoint[0].desc;
@@ -526,6 +648,7 @@ static int usbtouch_probe(struct usb_interface *intf,
usbtouch->data, type->rept_size,
usbtouch_irq, usbtouch, endpoint->bInterval);
+ usbtouch->irq->dev = usbtouch->udev;
usbtouch->irq->transfer_dma = usbtouch->data_dma;
usbtouch->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
@@ -553,7 +676,7 @@ out_free_buffers:
out_free:
input_free_device(input_dev);
kfree(usbtouch);
- return -ENOMEM;
+ return err;
}
static void usbtouch_disconnect(struct usb_interface *intf)
diff --git a/drivers/usb/input/wacom.c b/drivers/usb/input/wacom.c
deleted file mode 100644
index 369461a70b7..00000000000
--- a/drivers/usb/input/wacom.c
+++ /dev/null
@@ -1,1003 +0,0 @@
-/*
- * USB Wacom Graphire and Wacom Intuos tablet support
- *
- * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
- * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
- * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
- * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
- * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
- * Copyright (c) 2000 Daniel Egger <egger@suse.de>
- * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
- * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
- * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
- *
- * ChangeLog:
- * v0.1 (vp) - Initial release
- * v0.2 (aba) - Support for all buttons / combinations
- * v0.3 (vp) - Support for Intuos added
- * v0.4 (sm) - Support for more Intuos models, menustrip
- * relative mode, proximity.
- * v0.5 (vp) - Big cleanup, nifty features removed,
- * they belong in userspace
- * v1.8 (vp) - Submit URB only when operating, moved to CVS,
- * use input_report_key instead of report_btn and
- * other cleanups
- * v1.11 (vp) - Add URB ->dev setting for new kernels
- * v1.11 (jb) - Add support for the 4D Mouse & Lens
- * v1.12 (de) - Add support for two more inking pen IDs
- * v1.14 (vp) - Use new USB device id probing scheme.
- * Fix Wacom Graphire mouse wheel
- * v1.18 (vp) - Fix mouse wheel direction
- * Make mouse relative
- * v1.20 (fl) - Report tool id for Intuos devices
- * - Multi tools support
- * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
- * - Add PL models support
- * - Fix Wacom Graphire mouse wheel again
- * v1.21 (vp) - Removed protocol descriptions
- * - Added MISC_SERIAL for tool serial numbers
- * (gb) - Identify version on module load.
- * v1.21.1 (fl) - added Graphire2 support
- * v1.21.2 (fl) - added Intuos2 support
- * - added all the PL ids
- * v1.21.3 (fl) - added another eraser id from Neil Okamoto
- * - added smooth filter for Graphire from Peri Hankey
- * - added PenPartner support from Olaf van Es
- * - new tool ids from Ole Martin Bjoerndalen
- * v1.29 (pc) - Add support for more tablets
- * - Fix pressure reporting
- * v1.30 (vp) - Merge 2.4 and 2.5 drivers
- * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
- * - Cleanups here and there
- * v1.30.1 (pi) - Added Graphire3 support
- * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
- * v1.43 (pc) - Added support for Cintiq 21UX
- * - Fixed a Graphire bug
- * - Merged wacom_intuos3_irq into wacom_intuos_irq
- * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
- * - Report Device IDs
- * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
- * - Minor data report fix
- */
-
-/*
- * 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/kernel.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/usb/input.h>
-#include <asm/unaligned.h>
-
-/*
- * Version Information
- */
-#define DRIVER_VERSION "v1.45"
-#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
-#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
-#define DRIVER_LICENSE "GPL"
-
-MODULE_AUTHOR(DRIVER_AUTHOR);
-MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_LICENSE(DRIVER_LICENSE);
-
-#define USB_VENDOR_ID_WACOM 0x056a
-#define STYLUS_DEVICE_ID 0x02
-#define CURSOR_DEVICE_ID 0x06
-#define ERASER_DEVICE_ID 0x0A
-
-enum {
- PENPARTNER = 0,
- GRAPHIRE,
- WACOM_G4,
- PL,
- INTUOS,
- INTUOS3,
- INTUOS312,
- INTUOS319,
- CINTIQ,
- MAX_TYPE
-};
-
-struct wacom_features {
- char *name;
- int pktlen;
- int x_max;
- int y_max;
- int pressure_max;
- int distance_max;
- int type;
- usb_complete_t irq;
-};
-
-struct wacom {
- signed char *data;
- dma_addr_t data_dma;
- struct input_dev *dev;
- struct usb_device *usbdev;
- struct urb *irq;
- struct wacom_features *features;
- int tool[2];
- int id[2];
- __u32 serial[2];
- char phys[32];
-};
-
-#define USB_REQ_GET_REPORT 0x01
-#define USB_REQ_SET_REPORT 0x09
-
-static int usb_get_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 100);
-}
-
-static int usb_set_report(struct usb_interface *intf, unsigned char type,
- unsigned char id, void *buf, int size)
-{
- return usb_control_msg(interface_to_usbdev(intf),
- usb_sndctrlpipe(interface_to_usbdev(intf), 0),
- USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
- (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
- buf, size, 1000);
-}
-
-static void wacom_pl_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int prox, pressure, id;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- dbg("wacom_pl_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- prox = data[1] & 0x40;
-
- input_regs(dev, regs);
-
- id = ERASER_DEVICE_ID;
- if (prox) {
-
- pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
- if (wacom->features->pressure_max > 255)
- pressure = (pressure << 1) | ((data[4] >> 6) & 1);
- pressure += (wacom->features->pressure_max + 1) / 2;
-
- /*
- * if going from out of proximity into proximity select between the eraser
- * and the pen based on the state of the stylus2 button, choose eraser if
- * pressed else choose pen. if not a proximity change from out to in, send
- * an out of proximity for previous tool then a in for new tool.
- */
- if (!wacom->tool[0]) {
- /* Eraser bit set for DTF */
- if (data[1] & 0x10)
- wacom->tool[1] = BTN_TOOL_RUBBER;
- else
- /* Going into proximity select tool */
- wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
- } else {
- /* was entered with stylus2 pressed */
- if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
- /* report out proximity for previous tool */
- input_report_key(dev, wacom->tool[1], 0);
- input_sync(dev);
- wacom->tool[1] = BTN_TOOL_PEN;
- goto exit;
- }
- }
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
- id = STYLUS_DEVICE_ID;
- }
- input_report_key(dev, wacom->tool[1], prox); /* report in proximity for tool */
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- input_report_abs(dev, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
- input_report_abs(dev, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
- input_report_abs(dev, ABS_PRESSURE, pressure);
-
- input_report_key(dev, BTN_TOUCH, data[4] & 0x08);
- input_report_key(dev, BTN_STYLUS, data[4] & 0x10);
- /* Only allow the stylus2 button to be reported for the pen tool. */
- input_report_key(dev, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
- } else {
- /* report proximity-out of a (valid) tool */
- if (wacom->tool[1] != BTN_TOOL_RUBBER) {
- /* Unknown tool selected default to pen tool */
- wacom->tool[1] = BTN_TOOL_PEN;
- }
- input_report_key(dev, wacom->tool[1], prox);
- }
-
- wacom->tool[0] = prox; /* Save proximity state */
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_ptu_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int retval, id;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
- if (data[1] & 0x04) {
- input_report_key(dev, BTN_TOOL_RUBBER, data[1] & 0x20);
- input_report_key(dev, BTN_TOUCH, data[1] & 0x08);
- id = ERASER_DEVICE_ID;
- } else {
- input_report_key(dev, BTN_TOOL_PEN, data[1] & 0x20);
- input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
- id = STYLUS_DEVICE_ID;
- }
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[2]));
- input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[4]));
- input_report_abs(dev, ABS_PRESSURE, le16_to_cpu(*(__le16 *) &data[6]));
- input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_key(dev, BTN_STYLUS2, data[1] & 0x10);
-
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_penpartner_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2) {
- printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
- input_report_key(dev, BTN_TOOL_PEN, 1);
- input_report_abs(dev, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
- input_report_abs(dev, ABS_X, le16_to_cpu(*(__le16 *) &data[1]));
- input_report_abs(dev, ABS_Y, le16_to_cpu(*(__le16 *) &data[3]));
- input_report_abs(dev, ABS_PRESSURE, (signed char)data[6] + 127);
- input_report_key(dev, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
- input_report_key(dev, BTN_STYLUS, (data[5] & 0x40));
- input_sync(dev);
-
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static void wacom_graphire_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int x, y, id, rw;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] == 99) return; /* for Volito tablets */
-
- if (data[0] != 2) {
- dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
-
- id = STYLUS_DEVICE_ID;
- if (data[1] & 0x10) { /* in prox */
-
- switch ((data[1] >> 5) & 3) {
-
- case 0: /* Pen */
- wacom->tool[0] = BTN_TOOL_PEN;
- break;
-
- case 1: /* Rubber */
- wacom->tool[0] = BTN_TOOL_RUBBER;
- id = ERASER_DEVICE_ID;
- break;
-
- case 2: /* Mouse with wheel */
- input_report_key(dev, BTN_MIDDLE, data[1] & 0x04);
- if (wacom->features->type == WACOM_G4) {
- rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
- input_report_rel(dev, REL_WHEEL, -rw);
- } else
- input_report_rel(dev, REL_WHEEL, -(signed char) data[6]);
- /* fall through */
-
- case 3: /* Mouse without wheel */
- wacom->tool[0] = BTN_TOOL_MOUSE;
- id = CURSOR_DEVICE_ID;
- input_report_key(dev, BTN_LEFT, data[1] & 0x01);
- input_report_key(dev, BTN_RIGHT, data[1] & 0x02);
- if (wacom->features->type == WACOM_G4)
- input_report_abs(dev, ABS_DISTANCE, data[6]);
- else
- input_report_abs(dev, ABS_DISTANCE, data[7]);
- break;
- }
- }
-
- if (data[1] & 0x90) {
- x = le16_to_cpu(*(__le16 *) &data[2]);
- y = le16_to_cpu(*(__le16 *) &data[4]);
- input_report_abs(dev, ABS_X, x);
- input_report_abs(dev, ABS_Y, y);
- if (wacom->tool[0] != BTN_TOOL_MOUSE) {
- input_report_abs(dev, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
- input_report_key(dev, BTN_TOUCH, data[1] & 0x01);
- input_report_key(dev, BTN_STYLUS, data[1] & 0x02);
- input_report_key(dev, BTN_STYLUS2, data[1] & 0x04);
- }
- }
-
- if (data[1] & 0x10)
- input_report_abs(dev, ABS_MISC, id); /* report tool id */
- else
- input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
- input_report_key(dev, wacom->tool[0], data[1] & 0x10);
- input_sync(dev);
-
- /* send pad data */
- if (wacom->features->type == WACOM_G4) {
- if ((wacom->serial[1] & 0xc0) != (data[7] & 0xf8)) {
- wacom->id[1] = 1;
- wacom->serial[1] = (data[7] & 0xf8);
- input_report_key(dev, BTN_0, (data[7] & 0x40));
- input_report_key(dev, BTN_4, (data[7] & 0x80));
- rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
- input_report_rel(dev, REL_WHEEL, rw);
- input_report_key(dev, BTN_TOOL_FINGER, 0xf0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
- } else if (wacom->id[1]) {
- wacom->id[1] = 0;
- input_report_key(dev, BTN_TOOL_FINGER, 0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xf0);
- }
- input_sync(dev);
- }
- exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static int wacom_intuos_inout(struct urb *urb)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- int idx;
-
- /* tool number */
- idx = data[1] & 0x01;
-
- /* Enter report */
- if ((data[1] & 0xfc) == 0xc0) {
- /* serial number of the tool */
- wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
- (data[4] << 20) + (data[5] << 12) +
- (data[6] << 4) + (data[7] >> 4);
-
- wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
- switch (wacom->id[idx]) {
- case 0x812: /* Inking pen */
- case 0x801: /* Intuos3 Inking pen */
- case 0x012:
- wacom->tool[idx] = BTN_TOOL_PENCIL;
- break;
- case 0x822: /* Pen */
- case 0x842:
- case 0x852:
- case 0x823: /* Intuos3 Grip Pen */
- case 0x813: /* Intuos3 Classic Pen */
- case 0x885: /* Intuos3 Marker Pen */
- case 0x022:
- wacom->tool[idx] = BTN_TOOL_PEN;
- break;
- case 0x832: /* Stroke pen */
- case 0x032:
- wacom->tool[idx] = BTN_TOOL_BRUSH;
- break;
- case 0x007: /* Mouse 4D and 2D */
- case 0x09c:
- case 0x094:
- case 0x017: /* Intuos3 2D Mouse */
- wacom->tool[idx] = BTN_TOOL_MOUSE;
- break;
- case 0x096: /* Lens cursor */
- case 0x097: /* Intuos3 Lens cursor */
- wacom->tool[idx] = BTN_TOOL_LENS;
- break;
- case 0x82a: /* Eraser */
- case 0x85a:
- case 0x91a:
- case 0xd1a:
- case 0x0fa:
- case 0x82b: /* Intuos3 Grip Pen Eraser */
- case 0x81b: /* Intuos3 Classic Pen Eraser */
- case 0x91b: /* Intuos3 Airbrush Eraser */
- wacom->tool[idx] = BTN_TOOL_RUBBER;
- break;
- case 0xd12:
- case 0x912:
- case 0x112:
- case 0x913: /* Intuos3 Airbrush */
- wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
- break;
- default: /* Unknown tool */
- wacom->tool[idx] = BTN_TOOL_PEN;
- }
- if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
- ((wacom->features->type == INTUOS312)
- || (wacom->features->type == INTUOS319)))) {
- input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
- input_report_key(dev, wacom->tool[idx], 1);
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
- }
- return 1;
- }
-
- /* Exit report */
- if ((data[1] & 0xfe) == 0x80) {
- input_report_key(dev, wacom->tool[idx], 0);
- input_report_abs(dev, ABS_MISC, 0); /* reset tool id */
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
- return 1;
- }
-
- if((wacom->tool[idx] == BTN_TOOL_LENS) && ((wacom->features->type == INTUOS312)
- || (wacom->features->type == INTUOS319)))
- return 1;
- else
- return 0;
-}
-
-static void wacom_intuos_general(struct urb *urb)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- unsigned int t;
-
- /* general pen packet */
- if ((data[1] & 0xb8) == 0xa0) {
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- input_report_abs(dev, ABS_PRESSURE, t);
- input_report_abs(dev, ABS_TILT_X,
- ((data[7] << 1) & 0x7e) | (data[8] >> 7));
- input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
- input_report_key(dev, BTN_STYLUS, data[1] & 2);
- input_report_key(dev, BTN_STYLUS2, data[1] & 4);
- input_report_key(dev, BTN_TOUCH, t > 10);
- }
-
- /* airbrush second packet */
- if ((data[1] & 0xbc) == 0xb4) {
- input_report_abs(dev, ABS_WHEEL,
- (data[6] << 2) | ((data[7] >> 6) & 3));
- input_report_abs(dev, ABS_TILT_X,
- ((data[7] << 1) & 0x7e) | (data[8] >> 7));
- input_report_abs(dev, ABS_TILT_Y, data[8] & 0x7f);
- }
- return;
-}
-
-static void wacom_intuos_irq(struct urb *urb, struct pt_regs *regs)
-{
- struct wacom *wacom = urb->context;
- unsigned char *data = wacom->data;
- struct input_dev *dev = wacom->dev;
- unsigned int t;
- int idx;
- int retval;
-
- switch (urb->status) {
- case 0:
- /* success */
- break;
- case -ECONNRESET:
- case -ENOENT:
- case -ESHUTDOWN:
- /* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
- return;
- default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
- goto exit;
- }
-
- if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
- dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
- goto exit;
- }
-
- input_regs(dev, regs);
-
- /* tool number */
- idx = data[1] & 0x01;
-
- /* pad packets. Works as a second tool and is always in prox */
- if (data[0] == 12) {
- /* initiate the pad as a device */
- if (wacom->tool[1] != BTN_TOOL_FINGER)
- wacom->tool[1] = BTN_TOOL_FINGER;
-
- input_report_key(dev, BTN_0, (data[5] & 0x01));
- input_report_key(dev, BTN_1, (data[5] & 0x02));
- input_report_key(dev, BTN_2, (data[5] & 0x04));
- input_report_key(dev, BTN_3, (data[5] & 0x08));
- input_report_key(dev, BTN_4, (data[6] & 0x01));
- input_report_key(dev, BTN_5, (data[6] & 0x02));
- input_report_key(dev, BTN_6, (data[6] & 0x04));
- input_report_key(dev, BTN_7, (data[6] & 0x08));
- input_report_abs(dev, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
- input_report_abs(dev, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
-
- if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
- input_report_key(dev, wacom->tool[1], 1);
- else
- input_report_key(dev, wacom->tool[1], 0);
- input_event(dev, EV_MSC, MSC_SERIAL, 0xffffffff);
- input_sync(dev);
- goto exit;
- }
-
- /* process in/out prox events */
- if (wacom_intuos_inout(urb))
- goto exit;
-
- /* Cintiq doesn't send data when RDY bit isn't set */
- if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
- goto exit;
-
- if (wacom->features->type >= INTUOS3) {
- input_report_abs(dev, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
- input_report_abs(dev, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
- input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
- } else {
- input_report_abs(dev, ABS_X, be16_to_cpu(*(__be16 *) &data[2]));
- input_report_abs(dev, ABS_Y, be16_to_cpu(*(__be16 *) &data[4]));
- input_report_abs(dev, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
- }
-
- /* process general packets */
- wacom_intuos_general(urb);
-
- /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
- if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
-
- if (data[1] & 0x02) {
- /* Rotation packet */
- if (wacom->features->type >= INTUOS3) {
- /* I3 marker pen rotation reported as wheel
- * due to valuator limitation
- */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
- ((t-1) / 2 + 450)) : (450 - t / 2) ;
- input_report_abs(dev, ABS_WHEEL, t);
- } else {
- /* 4D mouse rotation packet */
- t = (data[6] << 3) | ((data[7] >> 5) & 7);
- input_report_abs(dev, ABS_RZ, (data[7] & 0x20) ?
- ((t - 1) / 2) : -t / 2);
- }
-
- } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
- /* 4D mouse packet */
- input_report_key(dev, BTN_LEFT, data[8] & 0x01);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
-
- input_report_key(dev, BTN_SIDE, data[8] & 0x20);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x10);
- t = (data[6] << 2) | ((data[7] >> 6) & 3);
- input_report_abs(dev, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
-
- } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
- /* 2D mouse packet */
- input_report_key(dev, BTN_LEFT, data[8] & 0x04);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x08);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x10);
- input_report_rel(dev, REL_WHEEL, (data[8] & 0x01)
- - ((data[8] & 0x02) >> 1));
-
- /* I3 2D mouse side buttons */
- if (wacom->features->type == INTUOS3) {
- input_report_key(dev, BTN_SIDE, data[8] & 0x40);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x20);
- }
-
- } else if (wacom->features->type < INTUOS3) {
- /* Lens cursor packets */
- input_report_key(dev, BTN_LEFT, data[8] & 0x01);
- input_report_key(dev, BTN_MIDDLE, data[8] & 0x02);
- input_report_key(dev, BTN_RIGHT, data[8] & 0x04);
- input_report_key(dev, BTN_SIDE, data[8] & 0x10);
- input_report_key(dev, BTN_EXTRA, data[8] & 0x08);
- }
- }
-
- input_report_abs(dev, ABS_MISC, wacom->id[idx]); /* report tool id */
- input_report_key(dev, wacom->tool[idx], 1);
- input_event(dev, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
- input_sync(dev);
-
-exit:
- retval = usb_submit_urb (urb, GFP_ATOMIC);
- if (retval)
- err ("%s - usb_submit_urb failed with result %d",
- __FUNCTION__, retval);
-}
-
-static struct wacom_features wacom_features[] = {
- { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_penpartner_irq },
- { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_graphire_irq },
- { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_graphire_irq },
- { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom PenPartner2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_graphire_irq },
- { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_pl_irq },
- { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL700", 8, 6758, 5406, 511, 32, PL, wacom_pl_irq },
- { "Wacom PL510", 8, 6282, 4762, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTU710", 8, 34080, 27660, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTF521", 8, 6282, 4762, 511, 32, PL, wacom_pl_irq },
- { "Wacom DTF720", 8, 6858, 5506, 511, 32, PL, wacom_pl_irq },
- { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PL, wacom_ptu_irq },
- { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_intuos_irq },
- { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS312, wacom_intuos_irq },
- { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS319, wacom_intuos_irq },
- { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 15, INTUOS3, wacom_intuos_irq },
- { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_intuos_irq },
- { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_intuos_irq },
- { }
-};
-
-static struct usb_device_id wacom_ids[] = {
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC3) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
- { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
- { }
-};
-
-MODULE_DEVICE_TABLE(usb, wacom_ids);
-
-static int wacom_open(struct input_dev *dev)
-{
- struct wacom *wacom = dev->private;
-
- wacom->irq->dev = wacom->usbdev;
- if (usb_submit_urb(wacom->irq, GFP_KERNEL))
- return -EIO;
-
- return 0;
-}
-
-static void wacom_close(struct input_dev *dev)
-{
- struct wacom *wacom = dev->private;
-
- usb_kill_urb(wacom->irq);
-}
-
-static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
-{
- struct usb_device *dev = interface_to_usbdev(intf);
- struct usb_endpoint_descriptor *endpoint;
- struct wacom *wacom;
- struct input_dev *input_dev;
- char rep_data[2], limit = 0;
-
- wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
- input_dev = input_allocate_device();
- if (!wacom || !input_dev)
- goto fail1;
-
- wacom->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
- if (!wacom->data)
- goto fail1;
-
- wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
- if (!wacom->irq)
- goto fail2;
-
- wacom->usbdev = dev;
- wacom->dev = input_dev;
- usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
- strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
-
- wacom->features = wacom_features + (id - wacom_ids);
- if (wacom->features->pktlen > 10)
- BUG();
-
- input_dev->name = wacom->features->name;
- usb_to_input_id(dev, &input_dev->id);
-
- input_dev->cdev.dev = &intf->dev;
- input_dev->private = wacom;
- input_dev->open = wacom_open;
- input_dev->close = wacom_close;
-
- input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
- input_set_abs_params(input_dev, ABS_X, 0, wacom->features->x_max, 4, 0);
- input_set_abs_params(input_dev, ABS_Y, 0, wacom->features->y_max, 4, 0);
- input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom->features->pressure_max, 0, 0);
- input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
-
- switch (wacom->features->type) {
- case WACOM_G4:
- input_dev->evbit[0] |= BIT(EV_MSC);
- input_dev->mscbit[0] |= BIT(MSC_SERIAL);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
- /* fall through */
-
- case GRAPHIRE:
- input_dev->evbit[0] |= BIT(EV_REL);
- input_dev->relbit[0] |= BIT(REL_WHEEL);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
- break;
-
- case INTUOS3:
- case INTUOS312:
- case INTUOS319:
- case CINTIQ:
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
- input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
- input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
- /* fall through */
-
- case INTUOS:
- input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
- input_dev->mscbit[0] |= BIT(MSC_SERIAL);
- input_dev->relbit[0] |= BIT(REL_WHEEL);
- input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
- | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
- input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom->features->distance_max, 0, 0);
- input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
- input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
- input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
- input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
- input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
- break;
-
- case PL:
- input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
- break;
- }
-
- endpoint = &intf->cur_altsetting->endpoint[0].desc;
-
- if (wacom->features->pktlen > 10)
- BUG();
-
- usb_fill_int_urb(wacom->irq, dev,
- usb_rcvintpipe(dev, endpoint->bEndpointAddress),
- wacom->data, wacom->features->pktlen,
- wacom->features->irq, wacom, endpoint->bInterval);
- wacom->irq->transfer_dma = wacom->data_dma;
- wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-
- input_register_device(wacom->dev);
-
- /* Ask the tablet to report tablet data. Repeat until it succeeds */
- do {
- rep_data[0] = 2;
- rep_data[1] = 2;
- usb_set_report(intf, 3, 2, rep_data, 2);
- usb_get_report(intf, 3, 2, rep_data, 2);
- } while (rep_data[1] != 2 && limit++ < 5);
-
- usb_set_intfdata(intf, wacom);
- return 0;
-
-fail2: usb_buffer_free(dev, 10, wacom->data, wacom->data_dma);
-fail1: input_free_device(input_dev);
- kfree(wacom);
- return -ENOMEM;
-}
-
-static void wacom_disconnect(struct usb_interface *intf)
-{
- struct wacom *wacom = usb_get_intfdata (intf);
-
- usb_set_intfdata(intf, NULL);
- if (wacom) {
- usb_kill_urb(wacom->irq);
- input_unregister_device(wacom->dev);
- usb_free_urb(wacom->irq);
- usb_buffer_free(interface_to_usbdev(intf), 10, wacom->data, wacom->data_dma);
- kfree(wacom);
- }
-}
-
-static struct usb_driver wacom_driver = {
- .name = "wacom",
- .probe = wacom_probe,
- .disconnect = wacom_disconnect,
- .id_table = wacom_ids,
-};
-
-static int __init wacom_init(void)
-{
- int result = usb_register(&wacom_driver);
- if (result == 0)
- info(DRIVER_VERSION ":" DRIVER_DESC);
- return result;
-}
-
-static void __exit wacom_exit(void)
-{
- usb_deregister(&wacom_driver);
-}
-
-module_init(wacom_init);
-module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom.h b/drivers/usb/input/wacom.h
new file mode 100644
index 00000000000..832737b658c
--- /dev/null
+++ b/drivers/usb/input/wacom.h
@@ -0,0 +1,132 @@
+/*
+ * drivers/usb/input/wacom.h
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support
+ *
+ * Copyright (c) 2000-2004 Vojtech Pavlik <vojtech@ucw.cz>
+ * Copyright (c) 2000 Andreas Bach Aaen <abach@stofanet.dk>
+ * Copyright (c) 2000 Clifford Wolf <clifford@clifford.at>
+ * Copyright (c) 2000 Sam Mosel <sam.mosel@computer.org>
+ * Copyright (c) 2000 James E. Blair <corvus@gnu.org>
+ * Copyright (c) 2000 Daniel Egger <egger@suse.de>
+ * Copyright (c) 2001 Frederic Lepied <flepied@mandrakesoft.com>
+ * Copyright (c) 2004 Panagiotis Issaris <panagiotis.issaris@mech.kuleuven.ac.be>
+ * Copyright (c) 2002-2006 Ping Cheng <pingc@wacom.com>
+ *
+ * ChangeLog:
+ * v0.1 (vp) - Initial release
+ * v0.2 (aba) - Support for all buttons / combinations
+ * v0.3 (vp) - Support for Intuos added
+ * v0.4 (sm) - Support for more Intuos models, menustrip
+ * relative mode, proximity.
+ * v0.5 (vp) - Big cleanup, nifty features removed,
+ * they belong in userspace
+ * v1.8 (vp) - Submit URB only when operating, moved to CVS,
+ * use input_report_key instead of report_btn and
+ * other cleanups
+ * v1.11 (vp) - Add URB ->dev setting for new kernels
+ * v1.11 (jb) - Add support for the 4D Mouse & Lens
+ * v1.12 (de) - Add support for two more inking pen IDs
+ * v1.14 (vp) - Use new USB device id probing scheme.
+ * Fix Wacom Graphire mouse wheel
+ * v1.18 (vp) - Fix mouse wheel direction
+ * Make mouse relative
+ * v1.20 (fl) - Report tool id for Intuos devices
+ * - Multi tools support
+ * - Corrected Intuos protocol decoding (airbrush, 4D mouse, lens cursor...)
+ * - Add PL models support
+ * - Fix Wacom Graphire mouse wheel again
+ * v1.21 (vp) - Removed protocol descriptions
+ * - Added MISC_SERIAL for tool serial numbers
+ * (gb) - Identify version on module load.
+ * v1.21.1 (fl) - added Graphire2 support
+ * v1.21.2 (fl) - added Intuos2 support
+ * - added all the PL ids
+ * v1.21.3 (fl) - added another eraser id from Neil Okamoto
+ * - added smooth filter for Graphire from Peri Hankey
+ * - added PenPartner support from Olaf van Es
+ * - new tool ids from Ole Martin Bjoerndalen
+ * v1.29 (pc) - Add support for more tablets
+ * - Fix pressure reporting
+ * v1.30 (vp) - Merge 2.4 and 2.5 drivers
+ * - Since 2.5 now has input_sync(), remove MSC_SERIAL abuse
+ * - Cleanups here and there
+ * v1.30.1 (pi) - Added Graphire3 support
+ * v1.40 (pc) - Add support for several new devices, fix eraser reporting, ...
+ * v1.43 (pc) - Added support for Cintiq 21UX
+ * - Fixed a Graphire bug
+ * - Merged wacom_intuos3_irq into wacom_intuos_irq
+ * v1.44 (pc) - Added support for Graphire4, Cintiq 710, Intuos3 6x11, etc.
+ * - Report Device IDs
+ * v1.45 (pc) - Added support for DTF 521, Intuos3 12x12 and 12x19
+ * - Minor data report fix
+ * v1.46 (pc) - Split wacom.c into wacom_sys.c and wacom_wac.c,
+ * - where wacom_sys.c deals with system specific code,
+ * - and wacom_wac.c deals with Wacom specific code
+ */
+
+/*
+ * 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.
+ */
+#ifndef WACOM_H
+#define WACOM_H
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/usb/input.h>
+#include <asm/unaligned.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.46"
+#define DRIVER_AUTHOR "Vojtech Pavlik <vojtech@ucw.cz>"
+#define DRIVER_DESC "USB Wacom Graphire and Wacom Intuos tablet driver"
+#define DRIVER_LICENSE "GPL"
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE(DRIVER_LICENSE);
+
+#define USB_VENDOR_ID_WACOM 0x056a
+
+struct wacom {
+ dma_addr_t data_dma;
+ struct input_dev *dev;
+ struct usb_device *usbdev;
+ struct urb *irq;
+ struct wacom_wac * wacom_wac;
+ char phys[32];
+};
+
+struct wacom_combo {
+ struct wacom * wacom;
+ struct urb * urb;
+ struct pt_regs *regs;
+};
+
+extern int wacom_wac_irq(struct wacom_wac * wacom_wac, void * wcombo);
+extern void wacom_sys_irq(struct urb *urb, struct pt_regs *regs);
+extern void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data);
+extern void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data);
+extern void wacom_report_key(void *wcombo, unsigned int key_type, int key_data);
+extern void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value);
+extern void wacom_input_regs(void *wcombo);
+extern void wacom_input_sync(void *wcombo);
+extern void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac);
+extern __u16 wacom_le16_to_cpu(unsigned char *data);
+extern __u16 wacom_be16_to_cpu(unsigned char *data);
+extern struct wacom_features * get_wacom_feature(const struct usb_device_id *id);
+extern const struct usb_device_id * get_device_table(void);
+
+#endif
diff --git a/drivers/usb/input/wacom_sys.c b/drivers/usb/input/wacom_sys.c
new file mode 100644
index 00000000000..7c3b52bdd9d
--- /dev/null
+++ b/drivers/usb/input/wacom_sys.c
@@ -0,0 +1,315 @@
+/*
+ * drivers/usb/input/wacom_sys.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - system specific code
+ */
+
+/*
+ * 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 "wacom.h"
+#include "wacom_wac.h"
+
+#define USB_REQ_GET_REPORT 0x01
+#define USB_REQ_SET_REPORT 0x09
+
+static int usb_get_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_rcvctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_GET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 100);
+}
+
+static int usb_set_report(struct usb_interface *intf, unsigned char type,
+ unsigned char id, void *buf, int size)
+{
+ return usb_control_msg(interface_to_usbdev(intf),
+ usb_sndctrlpipe(interface_to_usbdev(intf), 0),
+ USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id, intf->altsetting[0].desc.bInterfaceNumber,
+ buf, size, 1000);
+}
+
+static struct input_dev * get_input_dev(struct wacom_combo *wcombo)
+{
+ return wcombo->wacom->dev;
+}
+
+void wacom_sys_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct wacom *wacom = urb->context;
+ struct wacom_combo wcombo;
+ int retval;
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ goto exit;
+ }
+
+ wcombo.wacom = wacom;
+ wcombo.urb = urb;
+ wcombo.regs = regs;
+
+ if (wacom_wac_irq(wacom->wacom_wac, (void *)&wcombo))
+ input_sync(get_input_dev(&wcombo));
+
+ exit:
+ retval = usb_submit_urb (urb, GFP_ATOMIC);
+ if (retval)
+ err ("%s - usb_submit_urb failed with result %d",
+ __FUNCTION__, retval);
+}
+
+void wacom_report_key(void *wcombo, unsigned int key_type, int key_data)
+{
+ input_report_key(get_input_dev((struct wacom_combo *)wcombo), key_type, key_data);
+ return;
+}
+
+void wacom_report_abs(void *wcombo, unsigned int abs_type, int abs_data)
+{
+ input_report_abs(get_input_dev((struct wacom_combo *)wcombo), abs_type, abs_data);
+ return;
+}
+
+void wacom_report_rel(void *wcombo, unsigned int rel_type, int rel_data)
+{
+ input_report_rel(get_input_dev((struct wacom_combo *)wcombo), rel_type, rel_data);
+ return;
+}
+
+void wacom_input_event(void *wcombo, unsigned int type, unsigned int code, int value)
+{
+ input_event(get_input_dev((struct wacom_combo *)wcombo), type, code, value);
+ return;
+}
+
+__u16 wacom_be16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = be16_to_cpu(*(__be16 *) data);
+ return value;
+}
+
+__u16 wacom_le16_to_cpu(unsigned char *data)
+{
+ __u16 value;
+ value = be16_to_cpu(*(__be16 *) data);
+ return value;
+}
+
+void wacom_input_regs(void *wcombo)
+{
+ input_regs(get_input_dev((struct wacom_combo *)wcombo), ((struct wacom_combo *)wcombo)->regs);
+ return;
+}
+
+void wacom_input_sync(void *wcombo)
+{
+ input_sync(get_input_dev((struct wacom_combo *)wcombo));
+ return;
+}
+
+static int wacom_open(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ wacom->irq->dev = wacom->usbdev;
+ if (usb_submit_urb(wacom->irq, GFP_KERNEL))
+ return -EIO;
+
+ return 0;
+}
+
+static void wacom_close(struct input_dev *dev)
+{
+ struct wacom *wacom = dev->private;
+
+ usb_kill_urb(wacom->irq);
+}
+
+void input_dev_g4(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+}
+
+void input_dev_g(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_REL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+}
+
+void input_dev_i3(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_FINGER);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_0) | BIT(BTN_1) | BIT(BTN_2) | BIT(BTN_3) | BIT(BTN_4) | BIT(BTN_5) | BIT(BTN_6) | BIT(BTN_7);
+ input_set_abs_params(input_dev, ABS_RX, 0, 4097, 0, 0);
+ input_set_abs_params(input_dev, ABS_RY, 0, 4097, 0, 0);
+}
+
+void input_dev_i(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->evbit[0] |= BIT(EV_MSC) | BIT(EV_REL);
+ input_dev->mscbit[0] |= BIT(MSC_SERIAL);
+ input_dev->relbit[0] |= BIT(REL_WHEEL);
+ input_dev->keybit[LONG(BTN_LEFT)] |= BIT(BTN_LEFT) | BIT(BTN_RIGHT) | BIT(BTN_MIDDLE) | BIT(BTN_SIDE) | BIT(BTN_EXTRA);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER) | BIT(BTN_TOOL_MOUSE) | BIT(BTN_TOOL_BRUSH)
+ | BIT(BTN_TOOL_PENCIL) | BIT(BTN_TOOL_AIRBRUSH) | BIT(BTN_TOOL_LENS) | BIT(BTN_STYLUS2);
+ input_set_abs_params(input_dev, ABS_DISTANCE, 0, wacom_wac->features->distance_max, 0, 0);
+ input_set_abs_params(input_dev, ABS_WHEEL, 0, 1023, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_X, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_TILT_Y, 0, 127, 0, 0);
+ input_set_abs_params(input_dev, ABS_RZ, -900, 899, 0, 0);
+ input_set_abs_params(input_dev, ABS_THROTTLE, -1023, 1023, 0, 0);
+}
+
+void input_dev_pl(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_STYLUS2) | BIT(BTN_TOOL_RUBBER);
+}
+
+void input_dev_pt(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_RUBBER);
+}
+
+static int wacom_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_endpoint_descriptor *endpoint;
+ struct wacom *wacom;
+ struct wacom_wac *wacom_wac;
+ struct input_dev *input_dev;
+ char rep_data[2], limit = 0;
+
+ wacom = kzalloc(sizeof(struct wacom), GFP_KERNEL);
+ wacom_wac = kzalloc(sizeof(struct wacom_wac), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!wacom || !input_dev || !wacom_wac)
+ goto fail1;
+
+ wacom_wac->data = usb_buffer_alloc(dev, 10, GFP_KERNEL, &wacom->data_dma);
+ if (!wacom_wac->data)
+ goto fail1;
+
+ wacom->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!wacom->irq)
+ goto fail2;
+
+ wacom->usbdev = dev;
+ wacom->dev = input_dev;
+ usb_make_path(dev, wacom->phys, sizeof(wacom->phys));
+ strlcat(wacom->phys, "/input0", sizeof(wacom->phys));
+
+ wacom_wac->features = get_wacom_feature(id);
+ if (wacom_wac->features->pktlen > 10)
+ BUG();
+
+ input_dev->name = wacom_wac->features->name;
+ wacom->wacom_wac = wacom_wac;
+ usb_to_input_id(dev, &input_dev->id);
+
+ input_dev->cdev.dev = &intf->dev;
+ input_dev->private = wacom;
+ input_dev->open = wacom_open;
+ input_dev->close = wacom_close;
+
+ input_dev->evbit[0] |= BIT(EV_KEY) | BIT(EV_ABS);
+ input_dev->keybit[LONG(BTN_DIGI)] |= BIT(BTN_TOOL_PEN) | BIT(BTN_TOUCH) | BIT(BTN_STYLUS);
+ input_set_abs_params(input_dev, ABS_X, 0, wacom_wac->features->x_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_Y, 0, wacom_wac->features->y_max, 4, 0);
+ input_set_abs_params(input_dev, ABS_PRESSURE, 0, wacom_wac->features->pressure_max, 0, 0);
+ input_dev->absbit[LONG(ABS_MISC)] |= BIT(ABS_MISC);
+
+ wacom_init_input_dev(input_dev, wacom_wac);
+
+ endpoint = &intf->cur_altsetting->endpoint[0].desc;
+
+ usb_fill_int_urb(wacom->irq, dev,
+ usb_rcvintpipe(dev, endpoint->bEndpointAddress),
+ wacom_wac->data, wacom_wac->features->pktlen,
+ wacom_wac->features->irq, wacom, endpoint->bInterval);
+ wacom->irq->transfer_dma = wacom->data_dma;
+ wacom->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ input_register_device(wacom->dev);
+
+ /* Ask the tablet to report tablet data. Repeat until it succeeds */
+ do {
+ rep_data[0] = 2;
+ rep_data[1] = 2;
+ usb_set_report(intf, 3, 2, rep_data, 2);
+ usb_get_report(intf, 3, 2, rep_data, 2);
+ } while (rep_data[1] != 2 && limit++ < 5);
+
+ usb_set_intfdata(intf, wacom);
+ return 0;
+
+fail2: usb_buffer_free(dev, 10, wacom_wac->data, wacom->data_dma);
+fail1: input_free_device(input_dev);
+ kfree(wacom);
+ kfree(wacom_wac);
+ return -ENOMEM;
+}
+
+static void wacom_disconnect(struct usb_interface *intf)
+{
+ struct wacom *wacom = usb_get_intfdata (intf);
+
+ usb_set_intfdata(intf, NULL);
+ if (wacom) {
+ usb_kill_urb(wacom->irq);
+ input_unregister_device(wacom->dev);
+ usb_free_urb(wacom->irq);
+ usb_buffer_free(interface_to_usbdev(intf), 10, wacom->wacom_wac->data, wacom->data_dma);
+ kfree(wacom);
+ kfree(wacom->wacom_wac);
+ }
+}
+
+static struct usb_driver wacom_driver = {
+ .name = "wacom",
+ .probe = wacom_probe,
+ .disconnect = wacom_disconnect,
+};
+
+static int __init wacom_init(void)
+{
+ int result;
+ wacom_driver.id_table = get_device_table();
+ result = usb_register(&wacom_driver);
+ if (result == 0)
+ info(DRIVER_VERSION ":" DRIVER_DESC);
+ return result;
+}
+
+static void __exit wacom_exit(void)
+{
+ usb_deregister(&wacom_driver);
+}
+
+module_init(wacom_init);
+module_exit(wacom_exit);
diff --git a/drivers/usb/input/wacom_wac.c b/drivers/usb/input/wacom_wac.c
new file mode 100644
index 00000000000..85d458c98b6
--- /dev/null
+++ b/drivers/usb/input/wacom_wac.c
@@ -0,0 +1,646 @@
+/*
+ * drivers/usb/input/wacom_wac.c
+ *
+ * USB Wacom Graphire and Wacom Intuos tablet support - Wacom specific code
+ *
+ */
+
+/*
+ * 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 "wacom.h"
+#include "wacom_wac.h"
+
+static int wacom_penpartner_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+
+ switch (data[0]) {
+ case 1:
+ wacom_input_regs(wcombo);
+ if (data[5] & 0x80) {
+ wacom->tool[0] = (data[5] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ wacom->id[0] = (data[5] & 0x20) ? ERASER_DEVICE_ID : STYLUS_DEVICE_ID;
+ wacom_report_key(wcombo, wacom->tool[0], 1);
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[0]); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -127));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ } else {
+ wacom_report_key(wcombo, wacom->tool[0], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* report tool id */
+ wacom_report_abs(wcombo, ABS_PRESSURE, -1);
+ wacom_report_key(wcombo, BTN_TOUCH, 0);
+ }
+ break;
+ case 2:
+ wacom_input_regs(wcombo);
+ wacom_report_key(wcombo, BTN_TOOL_PEN, 1);
+ wacom_report_abs(wcombo, ABS_MISC, STYLUS_DEVICE_ID); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[1]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[3]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, (signed char)data[6] + 127);
+ wacom_report_key(wcombo, BTN_TOUCH, ((signed char)data[6] > -80) && !(data[5] & 0x20));
+ wacom_report_key(wcombo, BTN_STYLUS, (data[5] & 0x40));
+ break;
+ default:
+ printk(KERN_INFO "wacom_penpartner_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+ return 1;
+}
+
+static int wacom_pl_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int prox, id, pressure;
+
+ if (data[0] != 2) {
+ dbg("wacom_pl_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ prox = data[1] & 0x40;
+
+ wacom_input_regs(wcombo);
+
+ id = ERASER_DEVICE_ID;
+ if (prox) {
+
+ pressure = (signed char)((data[7] << 1) | ((data[4] >> 2) & 1));
+ if (wacom->features->pressure_max > 255)
+ pressure = (pressure << 1) | ((data[4] >> 6) & 1);
+ pressure += (wacom->features->pressure_max + 1) / 2;
+
+ /*
+ * if going from out of proximity into proximity select between the eraser
+ * and the pen based on the state of the stylus2 button, choose eraser if
+ * pressed else choose pen. if not a proximity change from out to in, send
+ * an out of proximity for previous tool then a in for new tool.
+ */
+ if (!wacom->tool[0]) {
+ /* Eraser bit set for DTF */
+ if (data[1] & 0x10)
+ wacom->tool[1] = BTN_TOOL_RUBBER;
+ else
+ /* Going into proximity select tool */
+ wacom->tool[1] = (data[4] & 0x20) ? BTN_TOOL_RUBBER : BTN_TOOL_PEN;
+ } else {
+ /* was entered with stylus2 pressed */
+ if (wacom->tool[1] == BTN_TOOL_RUBBER && !(data[4] & 0x20)) {
+ /* report out proximity for previous tool */
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_input_sync(wcombo);
+ wacom->tool[1] = BTN_TOOL_PEN;
+ return 0;
+ }
+ }
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox); /* report in proximity for tool */
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, data[3] | (data[2] << 7) | ((data[1] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_Y, data[6] | (data[5] << 7) | ((data[4] & 0x03) << 14));
+ wacom_report_abs(wcombo, ABS_PRESSURE, pressure);
+
+ wacom_report_key(wcombo, BTN_TOUCH, data[4] & 0x08);
+ wacom_report_key(wcombo, BTN_STYLUS, data[4] & 0x10);
+ /* Only allow the stylus2 button to be reported for the pen tool. */
+ wacom_report_key(wcombo, BTN_STYLUS2, (wacom->tool[1] == BTN_TOOL_PEN) && (data[4] & 0x20));
+ } else {
+ /* report proximity-out of a (valid) tool */
+ if (wacom->tool[1] != BTN_TOOL_RUBBER) {
+ /* Unknown tool selected default to pen tool */
+ wacom->tool[1] = BTN_TOOL_PEN;
+ }
+ wacom_report_key(wcombo, wacom->tool[1], prox);
+ }
+
+ wacom->tool[0] = prox; /* Save proximity state */
+ return 1;
+}
+
+static int wacom_ptu_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int id;
+
+ if (data[0] != 2) {
+ printk(KERN_INFO "wacom_ptu_irq: received unknown report #%d\n", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+ if (data[1] & 0x04) {
+ wacom_report_key(wcombo, BTN_TOOL_RUBBER, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x08);
+ id = ERASER_DEVICE_ID;
+ } else {
+ wacom_report_key(wcombo, BTN_TOOL_PEN, data[1] & 0x20);
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ id = STYLUS_DEVICE_ID;
+ }
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ wacom_report_abs(wcombo, ABS_X, wacom_le16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_le16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_PRESSURE, wacom_le16_to_cpu(&data[6]));
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x10);
+ return 1;
+}
+
+static int wacom_graphire_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int x, y, id, rw;
+
+ if (data[0] != 2) {
+ dbg("wacom_graphire_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+
+ id = STYLUS_DEVICE_ID;
+ if (data[1] & 0x10) { /* in prox */
+
+ switch ((data[1] >> 5) & 3) {
+
+ case 0: /* Pen */
+ wacom->tool[0] = BTN_TOOL_PEN;
+ break;
+
+ case 1: /* Rubber */
+ wacom->tool[0] = BTN_TOOL_RUBBER;
+ id = ERASER_DEVICE_ID;
+ break;
+
+ case 2: /* Mouse with wheel */
+ wacom_report_key(wcombo, BTN_MIDDLE, data[1] & 0x04);
+ if (wacom->features->type == WACOM_G4) {
+ rw = data[7] & 0x04 ? (data[7] & 0x03)-4 : (data[7] & 0x03);
+ wacom_report_rel(wcombo, REL_WHEEL, -rw);
+ } else
+ wacom_report_rel(wcombo, REL_WHEEL, -(signed char) data[6]);
+ /* fall through */
+
+ case 3: /* Mouse without wheel */
+ wacom->tool[0] = BTN_TOOL_MOUSE;
+ id = CURSOR_DEVICE_ID;
+ wacom_report_key(wcombo, BTN_LEFT, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_RIGHT, data[1] & 0x02);
+ if (wacom->features->type == WACOM_G4)
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[6]);
+ else
+ wacom_report_abs(wcombo, ABS_DISTANCE, data[7]);
+ break;
+ }
+ }
+
+ if (data[1] & 0x90) {
+ x = wacom_le16_to_cpu(&data[2]);
+ y = wacom_le16_to_cpu(&data[4]);
+ wacom_report_abs(wcombo, ABS_X, x);
+ wacom_report_abs(wcombo, ABS_Y, y);
+ if (wacom->tool[0] != BTN_TOOL_MOUSE) {
+ wacom_report_abs(wcombo, ABS_PRESSURE, data[6] | ((data[7] & 0x01) << 8));
+ wacom_report_key(wcombo, BTN_TOUCH, data[1] & 0x01);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 0x02);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 0x04);
+ }
+ }
+
+ if (data[1] & 0x10)
+ wacom_report_abs(wcombo, ABS_MISC, id); /* report tool id */
+ else
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_report_key(wcombo, wacom->tool[0], data[1] & 0x10);
+ wacom_input_sync(wcombo);
+
+ /* send pad data */
+ if (wacom->features->type == WACOM_G4) {
+ if ( (wacom->serial[1] & 0xc0) != (data[7] & 0xf8) ) {
+ wacom->id[1] = 1;
+ wacom->serial[1] = (data[7] & 0xf8);
+ wacom_report_key(wcombo, BTN_0, (data[7] & 0x40));
+ wacom_report_key(wcombo, BTN_4, (data[7] & 0x80));
+ rw = ((data[7] & 0x18) >> 3) - ((data[7] & 0x20) >> 3);
+ wacom_report_rel(wcombo, REL_WHEEL, rw);
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0xf0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ } else if (wacom->id[1]) {
+ wacom->id[1] = 0;
+ wacom_report_key(wcombo, BTN_TOOL_FINGER, 0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xf0);
+ }
+ }
+ return 1;
+}
+
+static int wacom_intuos_inout(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ int idx;
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* Enter report */
+ if ((data[1] & 0xfc) == 0xc0) {
+ /* serial number of the tool */
+ wacom->serial[idx] = ((data[3] & 0x0f) << 28) +
+ (data[4] << 20) + (data[5] << 12) +
+ (data[6] << 4) + (data[7] >> 4);
+
+ wacom->id[idx] = (data[2] << 4) | (data[3] >> 4);
+ switch (wacom->id[idx]) {
+ case 0x812: /* Inking pen */
+ case 0x801: /* Intuos3 Inking pen */
+ case 0x012:
+ wacom->tool[idx] = BTN_TOOL_PENCIL;
+ break;
+ case 0x822: /* Pen */
+ case 0x842:
+ case 0x852:
+ case 0x823: /* Intuos3 Grip Pen */
+ case 0x813: /* Intuos3 Classic Pen */
+ case 0x885: /* Intuos3 Marker Pen */
+ case 0x022:
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ break;
+ case 0x832: /* Stroke pen */
+ case 0x032:
+ wacom->tool[idx] = BTN_TOOL_BRUSH;
+ break;
+ case 0x007: /* Mouse 4D and 2D */
+ case 0x09c:
+ case 0x094:
+ case 0x017: /* Intuos3 2D Mouse */
+ wacom->tool[idx] = BTN_TOOL_MOUSE;
+ break;
+ case 0x096: /* Lens cursor */
+ case 0x097: /* Intuos3 Lens cursor */
+ wacom->tool[idx] = BTN_TOOL_LENS;
+ break;
+ case 0x82a: /* Eraser */
+ case 0x85a:
+ case 0x91a:
+ case 0xd1a:
+ case 0x0fa:
+ case 0x82b: /* Intuos3 Grip Pen Eraser */
+ case 0x81b: /* Intuos3 Classic Pen Eraser */
+ case 0x91b: /* Intuos3 Airbrush Eraser */
+ wacom->tool[idx] = BTN_TOOL_RUBBER;
+ break;
+ case 0xd12:
+ case 0x912:
+ case 0x112:
+ case 0x913: /* Intuos3 Airbrush */
+ wacom->tool[idx] = BTN_TOOL_AIRBRUSH;
+ break;
+ default: /* Unknown tool */
+ wacom->tool[idx] = BTN_TOOL_PEN;
+ }
+ /* only large I3 support Lens Cursor */
+ if(!((wacom->tool[idx] == BTN_TOOL_LENS) &&
+ (wacom->features->type == INTUOS3))) {
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[idx], 1);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
+ }
+ return 1;
+ }
+
+ /* Exit report */
+ if ((data[1] & 0xfe) == 0x80) {
+ wacom_report_key(wcombo, wacom->tool[idx], 0);
+ wacom_report_abs(wcombo, ABS_MISC, 0); /* reset tool id */
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 2;
+ }
+ return 0;
+}
+
+static void wacom_intuos_general(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+
+ /* general pen packet */
+ if ((data[1] & 0xb8) == 0xa0) {
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_PRESSURE, t);
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ wacom_report_key(wcombo, BTN_STYLUS, data[1] & 2);
+ wacom_report_key(wcombo, BTN_STYLUS2, data[1] & 4);
+ wacom_report_key(wcombo, BTN_TOUCH, t > 10);
+ }
+
+ /* airbrush second packet */
+ if ((data[1] & 0xbc) == 0xb4) {
+ wacom_report_abs(wcombo, ABS_WHEEL,
+ (data[6] << 2) | ((data[7] >> 6) & 3));
+ wacom_report_abs(wcombo, ABS_TILT_X,
+ ((data[7] << 1) & 0x7e) | (data[8] >> 7));
+ wacom_report_abs(wcombo, ABS_TILT_Y, data[8] & 0x7f);
+ }
+ return;
+}
+
+static int wacom_intuos_irq(struct wacom_wac *wacom, void *wcombo)
+{
+ unsigned char *data = wacom->data;
+ unsigned int t;
+ int idx, result;
+
+ if (data[0] != 2 && data[0] != 5 && data[0] != 6 && data[0] != 12) {
+ dbg("wacom_intuos_irq: received unknown report #%d", data[0]);
+ return 0;
+ }
+
+ wacom_input_regs(wcombo);
+
+ /* tool number */
+ idx = data[1] & 0x01;
+
+ /* pad packets. Works as a second tool and is always in prox */
+ if (data[0] == 12) {
+ /* initiate the pad as a device */
+ if (wacom->tool[1] != BTN_TOOL_FINGER)
+ wacom->tool[1] = BTN_TOOL_FINGER;
+
+ wacom_report_key(wcombo, BTN_0, (data[5] & 0x01));
+ wacom_report_key(wcombo, BTN_1, (data[5] & 0x02));
+ wacom_report_key(wcombo, BTN_2, (data[5] & 0x04));
+ wacom_report_key(wcombo, BTN_3, (data[5] & 0x08));
+ wacom_report_key(wcombo, BTN_4, (data[6] & 0x01));
+ wacom_report_key(wcombo, BTN_5, (data[6] & 0x02));
+ wacom_report_key(wcombo, BTN_6, (data[6] & 0x04));
+ wacom_report_key(wcombo, BTN_7, (data[6] & 0x08));
+ wacom_report_abs(wcombo, ABS_RX, ((data[1] & 0x1f) << 8) | data[2]);
+ wacom_report_abs(wcombo, ABS_RY, ((data[3] & 0x1f) << 8) | data[4]);
+
+ if((data[5] & 0x0f) | (data[6] & 0x0f) | (data[1] & 0x1f) | data[2])
+ wacom_report_key(wcombo, wacom->tool[1], 1);
+ else
+ wacom_report_key(wcombo, wacom->tool[1], 0);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, 0xffffffff);
+ return 1;
+ }
+
+ /* process in/out prox events */
+ result = wacom_intuos_inout(wacom, wcombo);
+ if (result)
+ return result-1;
+
+ /* Cintiq doesn't send data when RDY bit isn't set */
+ if ((wacom->features->type == CINTIQ) && !(data[1] & 0x40))
+ return 0;
+
+ if (wacom->features->type >= INTUOS3) {
+ wacom_report_abs(wcombo, ABS_X, (data[2] << 9) | (data[3] << 1) | ((data[9] >> 1) & 1));
+ wacom_report_abs(wcombo, ABS_Y, (data[4] << 9) | (data[5] << 1) | (data[9] & 1));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 2) & 0x3f));
+ } else {
+ wacom_report_abs(wcombo, ABS_X, wacom_be16_to_cpu(&data[2]));
+ wacom_report_abs(wcombo, ABS_Y, wacom_be16_to_cpu(&data[4]));
+ wacom_report_abs(wcombo, ABS_DISTANCE, ((data[9] >> 3) & 0x1f));
+ }
+
+ /* process general packets */
+ wacom_intuos_general(wacom, wcombo);
+
+ /* 4D mouse, 2D mouse, marker pen rotation, or Lens cursor packets */
+ if ((data[1] & 0xbc) == 0xa8 || (data[1] & 0xbe) == 0xb0) {
+
+ if (data[1] & 0x02) {
+ /* Rotation packet */
+ if (wacom->features->type >= INTUOS3) {
+ /* I3 marker pen rotation reported as wheel
+ * due to valuator limitation
+ */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ t = (data[7] & 0x20) ? ((t > 900) ? ((t-1) / 2 - 1350) :
+ ((t-1) / 2 + 450)) : (450 - t / 2) ;
+ wacom_report_abs(wcombo, ABS_WHEEL, t);
+ } else {
+ /* 4D mouse rotation packet */
+ t = (data[6] << 3) | ((data[7] >> 5) & 7);
+ wacom_report_abs(wcombo, ABS_RZ, (data[7] & 0x20) ?
+ ((t - 1) / 2) : -t / 2);
+ }
+
+ } else if (!(data[1] & 0x10) && wacom->features->type < INTUOS3) {
+ /* 4D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x20);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x10);
+ t = (data[6] << 2) | ((data[7] >> 6) & 3);
+ wacom_report_abs(wcombo, ABS_THROTTLE, (data[8] & 0x08) ? -t : t);
+
+ } else if (wacom->tool[idx] == BTN_TOOL_MOUSE) {
+ /* 2D mouse packet */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x08);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x10);
+ wacom_report_rel(wcombo, REL_WHEEL, (data[8] & 0x01)
+ - ((data[8] & 0x02) >> 1));
+
+ /* I3 2D mouse side buttons */
+ if (wacom->features->type == INTUOS3) {
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x40);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x20);
+ }
+
+ } else if (wacom->features->type < INTUOS3) {
+ /* Lens cursor packets */
+ wacom_report_key(wcombo, BTN_LEFT, data[8] & 0x01);
+ wacom_report_key(wcombo, BTN_MIDDLE, data[8] & 0x02);
+ wacom_report_key(wcombo, BTN_RIGHT, data[8] & 0x04);
+ wacom_report_key(wcombo, BTN_SIDE, data[8] & 0x10);
+ wacom_report_key(wcombo, BTN_EXTRA, data[8] & 0x08);
+ }
+ }
+
+ wacom_report_abs(wcombo, ABS_MISC, wacom->id[idx]); /* report tool id */
+ wacom_report_key(wcombo, wacom->tool[idx], 1);
+ wacom_input_event(wcombo, EV_MSC, MSC_SERIAL, wacom->serial[idx]);
+ return 1;
+}
+
+int wacom_wac_irq(struct wacom_wac *wacom_wac, void *wcombo)
+{
+ switch (wacom_wac->features->type) {
+ case PENPARTNER:
+ return (wacom_penpartner_irq(wacom_wac, wcombo));
+ break;
+ case PL:
+ return (wacom_pl_irq(wacom_wac, wcombo));
+ break;
+ case WACOM_G4:
+ case GRAPHIRE:
+ return (wacom_graphire_irq(wacom_wac, wcombo));
+ break;
+ case PTU:
+ return (wacom_ptu_irq(wacom_wac, wcombo));
+ break;
+ case INTUOS:
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ return (wacom_intuos_irq(wacom_wac, wcombo));
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+void wacom_init_input_dev(struct input_dev *input_dev, struct wacom_wac *wacom_wac)
+{
+ switch (wacom_wac->features->type) {
+ case WACOM_G4:
+ input_dev_g4(input_dev, wacom_wac);
+ /* fall through */
+ case GRAPHIRE:
+ input_dev_g(input_dev, wacom_wac);
+ break;
+ case INTUOS3:
+ case INTUOS3L:
+ case CINTIQ:
+ input_dev_i3(input_dev, wacom_wac);
+ /* fall through */
+ case INTUOS:
+ input_dev_i(input_dev, wacom_wac);
+ break;
+ case PL:
+ case PTU:
+ input_dev_pl(input_dev, wacom_wac);
+ break;
+ case PENPARTNER:
+ input_dev_pt(input_dev, wacom_wac);
+ break;
+ }
+ return;
+}
+
+static struct wacom_features wacom_features[] = {
+ { "Wacom Penpartner", 7, 5040, 3780, 255, 32, PENPARTNER, wacom_sys_irq },
+ { "Wacom Graphire", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire2 4x5", 8, 10206, 7422, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire2 5x7", 8, 13918, 10206, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire3", 8, 10208, 7424, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire3 6x8", 8, 16704, 12064, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Graphire4 4x5", 8, 10208, 7424, 511, 32, WACOM_G4, wacom_sys_irq },
+ { "Wacom Graphire4 6x8", 8, 16704, 12064, 511, 32, WACOM_G4, wacom_sys_irq },
+ { "Wacom Volito", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom PenStation2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Volito2 4x5", 8, 5104, 3712, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Volito2 2x3", 8, 3248, 2320, 511, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom PenPartner2", 8, 3250, 2320, 255, 32, GRAPHIRE, wacom_sys_irq },
+ { "Wacom Intuos 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq},
+ { "Wacom Intuos 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq},
+ { "Wacom PL400", 8, 5408, 4056, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL500", 8, 6144, 4608, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL600", 8, 6126, 4604, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL600SX", 8, 6260, 5016, 255, 32, PL, wacom_sys_irq },
+ { "Wacom PL550", 8, 6144, 4608, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL800", 8, 7220, 5780, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL700", 8, 6758, 5406, 511, 32, PL, wacom_sys_irq },
+ { "Wacom PL510", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTU710", 8, 34080, 27660, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTF521", 8, 6282, 4762, 511, 32, PL, wacom_sys_irq },
+ { "Wacom DTF720", 8, 6858, 5506, 511, 32, PL, wacom_sys_irq },
+ { "Wacom Cintiq Partner",8, 20480, 15360, 511, 32, PTU, wacom_sys_irq },
+ { "Wacom Intuos2 4x5", 10, 12700, 10600, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 9x12", 10, 30480, 24060, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 12x12", 10, 30480, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos2 12x18", 10, 45720, 31680, 1023, 15, INTUOS, wacom_sys_irq },
+ { "Wacom Intuos3 4x5", 10, 25400, 20320, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 6x8", 10, 40640, 30480, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 9x12", 10, 60960, 45720, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Intuos3 12x12", 10, 60960, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
+ { "Wacom Intuos3 12x19", 10, 97536, 60960, 1023, 15, INTUOS3L, wacom_sys_irq },
+ { "Wacom Intuos3 6x11", 10, 54204, 31750, 1023, 15, INTUOS3, wacom_sys_irq },
+ { "Wacom Cintiq 21UX", 10, 87200, 65600, 1023, 15, CINTIQ, wacom_sys_irq },
+ { "Wacom Intuos2 6x8", 10, 20320, 16240, 1023, 15, INTUOS, wacom_sys_irq },
+ { }
+};
+
+static struct usb_device_id wacom_ids[] = {
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x00) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x10) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x11) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x12) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x13) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x14) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x15) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x16) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x60) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x61) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x62) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x63) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x64) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x20) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x21) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x22) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x23) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x24) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x30) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x31) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x32) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x33) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x34) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x35) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x37) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x38) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x39) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xC4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x03) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x41) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x42) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x43) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x44) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x45) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB0) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB1) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB2) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB3) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB4) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0xB5) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x3F) },
+ { USB_DEVICE(USB_VENDOR_ID_WACOM, 0x47) },
+ { }
+};
+
+const struct usb_device_id * get_device_table(void) {
+ const struct usb_device_id * id_table = wacom_ids;
+ return id_table;
+}
+
+struct wacom_features * get_wacom_feature(const struct usb_device_id * id) {
+ int index = id - wacom_ids;
+ struct wacom_features *wf = &wacom_features[index];
+ return wf;
+}
+
+MODULE_DEVICE_TABLE(usb, wacom_ids);
diff --git a/drivers/usb/input/wacom_wac.h b/drivers/usb/input/wacom_wac.h
new file mode 100644
index 00000000000..ceae7bf59d9
--- /dev/null
+++ b/drivers/usb/input/wacom_wac.h
@@ -0,0 +1,48 @@
+/*
+ * drivers/usb/input/wacom_wac.h
+ *
+ * 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.
+ */
+#ifndef WACOM_WAC_H
+#define WACOM_WAC_H
+
+#define STYLUS_DEVICE_ID 0x02
+#define CURSOR_DEVICE_ID 0x06
+#define ERASER_DEVICE_ID 0x0A
+
+enum {
+ PENPARTNER = 0,
+ GRAPHIRE,
+ WACOM_G4,
+ PTU,
+ PL,
+ INTUOS,
+ INTUOS3,
+ INTUOS3L,
+ CINTIQ,
+ MAX_TYPE
+};
+
+struct wacom_features {
+ char *name;
+ int pktlen;
+ int x_max;
+ int y_max;
+ int pressure_max;
+ int distance_max;
+ int type;
+ usb_complete_t irq;
+};
+
+struct wacom_wac {
+ signed char *data;
+ int tool[2];
+ int id[2];
+ __u32 serial[2];
+ struct wacom_features *features;
+};
+
+#endif
diff --git a/drivers/usb/input/yealink.c b/drivers/usb/input/yealink.c
index 7b45fd3de91..7291e7a2717 100644
--- a/drivers/usb/input/yealink.c
+++ b/drivers/usb/input/yealink.c
@@ -971,7 +971,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
DRIVER_VERSION, sizeof(DRIVER_VERSION));
/* Register sysfs hooks (don't care about failure) */
- sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
+ ret = sysfs_create_group(&intf->dev.kobj, &yld_attr_group);
return 0;
}
diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig
index 88928a4be80..c29658f69e2 100644
--- a/drivers/usb/misc/Kconfig
+++ b/drivers/usb/misc/Kconfig
@@ -32,6 +32,16 @@ config USB_EMI26
To compile this driver as a module, choose M here: the
module will be called emi26.
+config USB_ADUTUX
+ tristate "ADU devices from Ontrak Control Systems (EXPERIMENTAL)"
+ depends on USB && EXPERIMENTAL
+ help
+ Say Y if you want to use an ADU device from Ontrak Control
+ Systems.
+
+ To compile this driver as a module, choose M here. The module
+ will be called adutux.
+
config USB_AUERSWALD
tristate "USB Auerswald ISDN support (EXPERIMENTAL)"
depends on USB && EXPERIMENTAL
@@ -115,19 +125,36 @@ config USB_CYTHERM
To compile this driver as a module, choose M here: the
module will be called cytherm.
-config USB_PHIDGETKIT
- tristate "USB PhidgetKit support"
+config USB_PHIDGET
+ tristate "USB Phidgets drivers"
depends on USB
help
- Say Y here if you want to connect a PhidgetKit USB device from
- Phidgets Inc.
+ Say Y here to enable the various drivers for devices from
+ Phidgets inc.
+
+config USB_PHIDGETKIT
+ tristate "USB PhidgetInterfaceKit support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetInterfaceKit USB device
+ from Phidgets Inc.
To compile this driver as a module, choose M here: the
module will be called phidgetkit.
+config USB_PHIDGETMOTORCONTROL
+ tristate "USB PhidgetMotorControl support"
+ depends on USB_PHIDGET
+ help
+ Say Y here if you want to connect a PhidgetMotorControl USB device
+ from Phidgets Inc.
+
+ To compile this driver as a module, choose M here: the
+ module will be called phidgetmotorcontrol.
+
config USB_PHIDGETSERVO
tristate "USB PhidgetServo support"
- depends on USB
+ depends on USB_PHIDGET
help
Say Y here if you want to connect an 1 or 4 Motor PhidgetServo
servo controller version 2.0 or 3.0.
@@ -151,6 +178,30 @@ config USB_IDMOUSE
See also <http://www.fs.tum.de/~echtler/idmouse/>.
+config USB_FTDI_ELAN
+ tristate "Elan PCMCIA CardBus Adapter USB Client"
+ depends on USB
+ default M
+ help
+ ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
+ Currently only the U132 adapter is available.
+
+ The U132 is specifically designed for CardBus PC cards that contain
+ an OHCI host controller. Typical PC cards are the Orange Mobile 3G
+ Option GlobeTrotter Fusion card. The U132 adapter will *NOT* work
+ with PC cards that do not contain an OHCI controller. To use a U132
+ adapter you will need this "ftdi-elan" module as well as the "u132-hcd"
+ module which is a USB host controller driver that talks to the OHCI
+ controller within CardBus card that are inserted in the U132 adapter.
+
+ This driver has been tested with a CardBus OHCI USB adapter, and
+ worked with a USB PEN Drive inserted into the first USB port of
+ the PCCARD. A rather pointless thing to do, but useful for testing.
+
+ See also the USB_U132_HCD entry "Elan U132 Adapter Host Controller"
+
+ It is safe to say M here.
+
config USB_APPLEDISPLAY
tristate "Apple Cinema Display support"
depends on USB
diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile
index 2927260c581..2be70fa259b 100644
--- a/drivers/usb/misc/Makefile
+++ b/drivers/usb/misc/Makefile
@@ -3,22 +3,25 @@
# (the ones that don't fit into any other categories)
#
+obj-$(CONFIG_USB_ADUTUX) += adutux.o
obj-$(CONFIG_USB_AUERSWALD) += auerswald.o
obj-$(CONFIG_USB_CYPRESS_CY7C63)+= cypress_cy7c63.o
obj-$(CONFIG_USB_CYTHERM) += cytherm.o
obj-$(CONFIG_USB_EMI26) += emi26.o
obj-$(CONFIG_USB_EMI62) += emi62.o
+obj-$(CONFIG_USB_FTDI_ELAN) += ftdi-elan.o
obj-$(CONFIG_USB_IDMOUSE) += idmouse.o
obj-$(CONFIG_USB_LCD) += usblcd.o
obj-$(CONFIG_USB_LD) += ldusb.o
obj-$(CONFIG_USB_LED) += usbled.o
obj-$(CONFIG_USB_LEGOTOWER) += legousbtower.o
+obj-$(CONFIG_USB_PHIDGET) += phidget.o
obj-$(CONFIG_USB_PHIDGETKIT) += phidgetkit.o
+obj-$(CONFIG_USB_PHIDGETMOTORCONTROL) += phidgetmotorcontrol.o
obj-$(CONFIG_USB_PHIDGETSERVO) += phidgetservo.o
obj-$(CONFIG_USB_RIO500) += rio500.o
obj-$(CONFIG_USB_TEST) += usbtest.o
obj-$(CONFIG_USB_USS720) += uss720.o
-obj-$(CONFIG_USB_APPLEDISPLAY) += appledisplay.o
obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/
diff --git a/drivers/usb/misc/adutux.c b/drivers/usb/misc/adutux.c
new file mode 100644
index 00000000000..d3963199b6e
--- /dev/null
+++ b/drivers/usb/misc/adutux.c
@@ -0,0 +1,900 @@
+/*
+ * adutux - driver for ADU devices from Ontrak Control Systems
+ * This is an experimental driver. Use at your own risk.
+ * This driver is not supported by Ontrak Control Systems.
+ *
+ * Copyright (c) 2003 John Homppi (SCO, leave this notice here)
+ *
+ * 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.
+ *
+ * derived from the Lego USB Tower driver 0.56:
+ * Copyright (c) 2003 David Glance <davidgsf@sourceforge.net>
+ * 2001 Juergen Stuber <stuber@loria.fr>
+ * that was derived from USB Skeleton driver - 0.5
+ * Copyright (c) 2001 Greg Kroah-Hartman (greg@kroah.com)
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <asm/uaccess.h>
+
+#ifdef CONFIG_USB_DEBUG
+static int debug = 5;
+#else
+static int debug = 1;
+#endif
+
+/* Use our own dbg macro */
+#undef dbg
+#define dbg(lvl, format, arg...) \
+do { \
+ if (debug >= lvl) \
+ printk(KERN_DEBUG __FILE__ " : " format " \n", ## arg); \
+} while (0)
+
+
+/* Version Information */
+#define DRIVER_VERSION "v0.0.13"
+#define DRIVER_AUTHOR "John Homppi"
+#define DRIVER_DESC "adutux (see www.ontrak.net)"
+
+/* Module parameters */
+module_param(debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
+
+/* Define these values to match your device */
+#define ADU_VENDOR_ID 0x0a07
+#define ADU_PRODUCT_ID 0x0064
+
+/* table of devices that work with this driver */
+static struct usb_device_id device_table [] = {
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID) }, /* ADU100 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+20) }, /* ADU120 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+30) }, /* ADU130 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+100) }, /* ADU200 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+108) }, /* ADU208 */
+ { USB_DEVICE(ADU_VENDOR_ID, ADU_PRODUCT_ID+118) }, /* ADU218 */
+ { }/* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, device_table);
+
+#ifdef CONFIG_USB_DYNAMIC_MINORS
+#define ADU_MINOR_BASE 0
+#else
+#define ADU_MINOR_BASE 67
+#endif
+
+/* we can have up to this number of device plugged in at once */
+#define MAX_DEVICES 16
+
+#define COMMAND_TIMEOUT (2*HZ) /* 60 second timeout for a command */
+
+/* Structure to hold all of our device specific stuff */
+struct adu_device {
+ struct semaphore sem; /* locks this structure */
+ struct usb_device* udev; /* save off the usb device pointer */
+ struct usb_interface* interface;
+ unsigned char minor; /* the starting minor number for this device */
+ char serial_number[8];
+
+ int open_count; /* number of times this port has been opened */
+
+ char* read_buffer_primary;
+ int read_buffer_length;
+ char* read_buffer_secondary;
+ int secondary_head;
+ int secondary_tail;
+ spinlock_t buflock;
+
+ wait_queue_head_t read_wait;
+ wait_queue_head_t write_wait;
+
+ char* interrupt_in_buffer;
+ struct usb_endpoint_descriptor* interrupt_in_endpoint;
+ struct urb* interrupt_in_urb;
+ int read_urb_finished;
+
+ char* interrupt_out_buffer;
+ struct usb_endpoint_descriptor* interrupt_out_endpoint;
+ struct urb* interrupt_out_urb;
+};
+
+/* prevent races between open() and disconnect */
+static DEFINE_MUTEX(disconnect_mutex);
+static struct usb_driver adu_driver;
+
+static void adu_debug_data(int level, const char *function, int size,
+ const unsigned char *data)
+{
+ int i;
+
+ if (debug < level)
+ return;
+
+ printk(KERN_DEBUG __FILE__": %s - length = %d, data = ",
+ function, size);
+ for (i = 0; i < size; ++i)
+ printk("%.2x ", data[i]);
+ printk("\n");
+}
+
+/**
+ * adu_abort_transfers
+ * aborts transfers and frees associated data structures
+ */
+static void adu_abort_transfers(struct adu_device *dev)
+{
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (dev == NULL) {
+ dbg(1," %s : dev is null", __FUNCTION__);
+ goto exit;
+ }
+
+ if (dev->udev == NULL) {
+ dbg(1," %s : udev is null", __FUNCTION__);
+ goto exit;
+ }
+
+ dbg(2," %s : udev state %d", __FUNCTION__, dev->udev->state);
+ if (dev->udev->state == USB_STATE_NOTATTACHED) {
+ dbg(1," %s : udev is not attached", __FUNCTION__);
+ goto exit;
+ }
+
+ /* shutdown transfer */
+ usb_unlink_urb(dev->interrupt_in_urb);
+ usb_unlink_urb(dev->interrupt_out_urb);
+
+exit:
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+static void adu_delete(struct adu_device *dev)
+{
+ dbg(2, "%s enter", __FUNCTION__);
+
+ adu_abort_transfers(dev);
+
+ /* free data structures */
+ usb_free_urb(dev->interrupt_in_urb);
+ usb_free_urb(dev->interrupt_out_urb);
+ kfree(dev->read_buffer_primary);
+ kfree(dev->read_buffer_secondary);
+ kfree(dev->interrupt_in_buffer);
+ kfree(dev->interrupt_out_buffer);
+ kfree(dev);
+
+ dbg(2, "%s : leave", __FUNCTION__);
+}
+
+static void adu_interrupt_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct adu_device *dev = urb->context;
+
+ dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+
+ spin_lock(&dev->buflock);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) && (urb->status != -ECONNRESET)) {
+ dbg(1," %s : nonzero status received: %d",
+ __FUNCTION__, urb->status);
+ }
+ goto exit;
+ }
+
+ if (urb->actual_length > 0 && dev->interrupt_in_buffer[0] != 0x00) {
+ if (dev->read_buffer_length <
+ (4 * le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize)) -
+ (urb->actual_length)) {
+ memcpy (dev->read_buffer_primary +
+ dev->read_buffer_length,
+ dev->interrupt_in_buffer, urb->actual_length);
+
+ dev->read_buffer_length += urb->actual_length;
+ dbg(2," %s reading %d ", __FUNCTION__,
+ urb->actual_length);
+ } else {
+ dbg(1," %s : read_buffer overflow", __FUNCTION__);
+ }
+ }
+
+exit:
+ dev->read_urb_finished = 1;
+ spin_unlock(&dev->buflock);
+ /* always wake up so we recover from errors */
+ wake_up_interruptible(&dev->read_wait);
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static void adu_interrupt_out_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct adu_device *dev = urb->context;
+
+ dbg(4," %s : enter, status %d", __FUNCTION__, urb->status);
+ adu_debug_data(5,__FUNCTION__, urb->actual_length, urb->transfer_buffer);
+
+ if (urb->status != 0) {
+ if ((urb->status != -ENOENT) &&
+ (urb->status != -ECONNRESET)) {
+ dbg(1, " %s :nonzero status received: %d",
+ __FUNCTION__, urb->status);
+ }
+ goto exit;
+ }
+
+ wake_up_interruptible(&dev->write_wait);
+exit:
+
+ adu_debug_data(5, __FUNCTION__, urb->actual_length,
+ urb->transfer_buffer);
+ dbg(4," %s : leave, status %d", __FUNCTION__, urb->status);
+}
+
+static int adu_open(struct inode *inode, struct file *file)
+{
+ struct adu_device *dev = NULL;
+ struct usb_interface *interface;
+ int subminor;
+ int retval = 0;
+
+ dbg(2,"%s : enter", __FUNCTION__);
+
+ subminor = iminor(inode);
+
+ mutex_lock(&disconnect_mutex);
+
+ interface = usb_find_interface(&adu_driver, subminor);
+ if (!interface) {
+ err("%s - error, can't find device for minor %d",
+ __FUNCTION__, subminor);
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ dev = usb_get_intfdata(interface);
+ if (!dev) {
+ retval = -ENODEV;
+ goto exit_no_device;
+ }
+
+ /* lock this device */
+ if ((retval = down_interruptible(&dev->sem))) {
+ dbg(2, "%s : sem down failed", __FUNCTION__);
+ goto exit_no_device;
+ }
+
+ /* increment our usage count for the device */
+ ++dev->open_count;
+ dbg(2,"%s : open count %d", __FUNCTION__, dev->open_count);
+
+ /* save device in the file's private structure */
+ file->private_data = dev;
+
+ /* initialize in direction */
+ dev->read_buffer_length = 0;
+
+ /* fixup first read by having urb waiting for it */
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback, dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->read_urb_finished = 0;
+ usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ /* we ignore failure */
+ /* end of fixup for first read */
+
+ up(&dev->sem);
+
+exit_no_device:
+ mutex_unlock(&disconnect_mutex);
+ dbg(2,"%s : leave, return value %d ", __FUNCTION__, retval);
+
+ return retval;
+}
+
+static int adu_release_internal(struct adu_device *dev)
+{
+ int retval = 0;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (dev->udev == NULL) {
+ /* the device was unplugged before the file was released */
+ adu_delete(dev);
+ goto exit;
+ }
+
+ /* decrement our usage count for the device */
+ --dev->open_count;
+ dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+ if (dev->open_count <= 0) {
+ adu_abort_transfers(dev);
+ dev->open_count = 0;
+ }
+
+exit:
+ dbg(2," %s : leave", __FUNCTION__);
+ return retval;
+}
+
+static int adu_release(struct inode *inode, struct file *file)
+{
+ struct adu_device *dev = NULL;
+ int retval = 0;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (file == NULL) {
+ dbg(1," %s : file is NULL", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ dev = file->private_data;
+
+ if (dev == NULL) {
+ dbg(1," %s : object is NULL", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* lock our device */
+ down(&dev->sem); /* not interruptible */
+
+ if (dev->open_count <= 0) {
+ dbg(1," %s : device not opened", __FUNCTION__);
+ retval = -ENODEV;
+ goto exit;
+ }
+
+ /* do the work */
+ retval = adu_release_internal(dev);
+
+exit:
+ up(&dev->sem);
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+ return retval;
+}
+
+static ssize_t adu_read(struct file *file, __user char *buffer, size_t count,
+ loff_t *ppos)
+{
+ struct adu_device *dev;
+ size_t bytes_read = 0;
+ size_t bytes_to_read = count;
+ int i;
+ int retval = 0;
+ int timeout = 0;
+ int should_submit = 0;
+ unsigned long flags;
+ DECLARE_WAITQUEUE(wait, current);
+
+ dbg(2," %s : enter, count = %Zd, file=%p", __FUNCTION__, count, file);
+
+ dev = file->private_data;
+ dbg(2," %s : dev=%p", __FUNCTION__, dev);
+ /* lock this object */
+ if (down_interruptible(&dev->sem))
+ return -ERESTARTSYS;
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL || dev->minor == 0) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that some data was requested */
+ if (count == 0) {
+ dbg(1," %s : read request of 0 bytes", __FUNCTION__);
+ goto exit;
+ }
+
+ timeout = COMMAND_TIMEOUT;
+ dbg(2," %s : about to start looping", __FUNCTION__);
+ while (bytes_to_read) {
+ int data_in_secondary = dev->secondary_tail - dev->secondary_head;
+ dbg(2," %s : while, data_in_secondary=%d, status=%d",
+ __FUNCTION__, data_in_secondary,
+ dev->interrupt_in_urb->status);
+
+ if (data_in_secondary) {
+ /* drain secondary buffer */
+ int amount = bytes_to_read < data_in_secondary ? bytes_to_read : data_in_secondary;
+ i = copy_to_user(buffer, dev->read_buffer_secondary+dev->secondary_head, amount);
+ if (i < 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+ dev->secondary_head += (amount - i);
+ bytes_read += (amount - i);
+ bytes_to_read -= (amount - i);
+ if (i) {
+ retval = bytes_read ? bytes_read : -EFAULT;
+ goto exit;
+ }
+ } else {
+ /* we check the primary buffer */
+ spin_lock_irqsave (&dev->buflock, flags);
+ if (dev->read_buffer_length) {
+ /* we secure access to the primary */
+ char *tmp;
+ dbg(2," %s : swap, read_buffer_length = %d",
+ __FUNCTION__, dev->read_buffer_length);
+ tmp = dev->read_buffer_secondary;
+ dev->read_buffer_secondary = dev->read_buffer_primary;
+ dev->read_buffer_primary = tmp;
+ dev->secondary_head = 0;
+ dev->secondary_tail = dev->read_buffer_length;
+ dev->read_buffer_length = 0;
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ /* we have a free buffer so use it */
+ should_submit = 1;
+ } else {
+ /* even the primary was empty - we may need to do IO */
+ if (dev->interrupt_in_urb->status == -EINPROGRESS) {
+ /* somebody is doing IO */
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submitted already", __FUNCTION__);
+ } else {
+ /* we must initiate input */
+ dbg(2," %s : initiate input", __FUNCTION__);
+ dev->read_urb_finished = 0;
+
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ retval = usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ if (!retval) {
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submitted OK", __FUNCTION__);
+ } else {
+ if (retval == -ENOMEM) {
+ retval = bytes_read ? bytes_read : -ENOMEM;
+ }
+ spin_unlock_irqrestore(&dev->buflock, flags);
+ dbg(2," %s : submit failed", __FUNCTION__);
+ goto exit;
+ }
+ }
+
+ /* we wait for I/O to complete */
+ set_current_state(TASK_INTERRUPTIBLE);
+ add_wait_queue(&dev->read_wait, &wait);
+ if (!dev->read_urb_finished)
+ timeout = schedule_timeout(COMMAND_TIMEOUT);
+ else
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&dev->read_wait, &wait);
+
+ if (timeout <= 0) {
+ dbg(2," %s : timeout", __FUNCTION__);
+ retval = bytes_read ? bytes_read : -ETIMEDOUT;
+ goto exit;
+ }
+
+ if (signal_pending(current)) {
+ dbg(2," %s : signal pending", __FUNCTION__);
+ retval = bytes_read ? bytes_read : -EINTR;
+ goto exit;
+ }
+ }
+ }
+ }
+
+ retval = bytes_read;
+ /* if the primary buffer is empty then use it */
+ if (should_submit && !dev->interrupt_in_urb->status==-EINPROGRESS) {
+ usb_fill_int_urb(dev->interrupt_in_urb,dev->udev,
+ usb_rcvintpipe(dev->udev,
+ dev->interrupt_in_endpoint->bEndpointAddress),
+ dev->interrupt_in_buffer,
+ le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize),
+ adu_interrupt_in_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->read_urb_finished = 0;
+ usb_submit_urb(dev->interrupt_in_urb, GFP_KERNEL);
+ /* we ignore failure */
+ }
+
+exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+ return retval;
+}
+
+static ssize_t adu_write(struct file *file, const __user char *buffer,
+ size_t count, loff_t *ppos)
+{
+ struct adu_device *dev;
+ size_t bytes_written = 0;
+ size_t bytes_to_write;
+ size_t buffer_size;
+ int retval = 0;
+ int timeout = 0;
+
+ dbg(2," %s : enter, count = %Zd", __FUNCTION__, count);
+
+ dev = file->private_data;
+
+ /* lock this object */
+ down_interruptible(&dev->sem);
+
+ /* verify that the device wasn't unplugged */
+ if (dev->udev == NULL || dev->minor == 0) {
+ retval = -ENODEV;
+ err("No device or device unplugged %d", retval);
+ goto exit;
+ }
+
+ /* verify that we actually have some data to write */
+ if (count == 0) {
+ dbg(1," %s : write request of 0 bytes", __FUNCTION__);
+ goto exit;
+ }
+
+
+ while (count > 0) {
+ if (dev->interrupt_out_urb->status == -EINPROGRESS) {
+ timeout = COMMAND_TIMEOUT;
+
+ while (timeout > 0) {
+ if (signal_pending(current)) {
+ dbg(1," %s : interrupted", __FUNCTION__);
+ retval = -EINTR;
+ goto exit;
+ }
+ up(&dev->sem);
+ timeout = interruptible_sleep_on_timeout(&dev->write_wait, timeout);
+ down_interruptible(&dev->sem);
+ if (timeout > 0) {
+ break;
+ }
+ dbg(1," %s : interrupted timeout: %d", __FUNCTION__, timeout);
+ }
+
+
+ dbg(1," %s : final timeout: %d", __FUNCTION__, timeout);
+
+ if (timeout == 0) {
+ dbg(1, "%s - command timed out.", __FUNCTION__);
+ retval = -ETIMEDOUT;
+ goto exit;
+ }
+
+ dbg(4," %s : in progress, count = %Zd", __FUNCTION__, count);
+
+ } else {
+ dbg(4," %s : sending, count = %Zd", __FUNCTION__, count);
+
+ /* write the data into interrupt_out_buffer from userspace */
+ buffer_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+ bytes_to_write = count > buffer_size ? buffer_size : count;
+ dbg(4," %s : buffer_size = %Zd, count = %Zd, bytes_to_write = %Zd",
+ __FUNCTION__, buffer_size, count, bytes_to_write);
+
+ if (copy_from_user(dev->interrupt_out_buffer, buffer, bytes_to_write) != 0) {
+ retval = -EFAULT;
+ goto exit;
+ }
+
+ /* send off the urb */
+ usb_fill_int_urb(
+ dev->interrupt_out_urb,
+ dev->udev,
+ usb_sndintpipe(dev->udev, dev->interrupt_out_endpoint->bEndpointAddress),
+ dev->interrupt_out_buffer,
+ bytes_to_write,
+ adu_interrupt_out_callback,
+ dev,
+ dev->interrupt_in_endpoint->bInterval);
+ /* dev->interrupt_in_urb->transfer_flags |= URB_ASYNC_UNLINK; */
+ dev->interrupt_out_urb->actual_length = bytes_to_write;
+ retval = usb_submit_urb(dev->interrupt_out_urb, GFP_KERNEL);
+ if (retval < 0) {
+ err("Couldn't submit interrupt_out_urb %d", retval);
+ goto exit;
+ }
+
+ buffer += bytes_to_write;
+ count -= bytes_to_write;
+
+ bytes_written += bytes_to_write;
+ }
+ }
+
+ retval = bytes_written;
+
+exit:
+ /* unlock the device */
+ up(&dev->sem);
+
+ dbg(2," %s : leave, return value %d", __FUNCTION__, retval);
+
+ return retval;
+}
+
+/* file operations needed when we register this driver */
+static struct file_operations adu_fops = {
+ .owner = THIS_MODULE,
+ .read = adu_read,
+ .write = adu_write,
+ .open = adu_open,
+ .release = adu_release,
+};
+
+/*
+ * usb class driver info in order to get a minor number from the usb core,
+ * and to have the device registered with devfs and the driver core
+ */
+static struct usb_class_driver adu_class = {
+ .name = "usb/adutux%d",
+ .fops = &adu_fops,
+ .minor_base = ADU_MINOR_BASE,
+};
+
+/**
+ * adu_probe
+ *
+ * Called by the usb core when a new device is connected that it thinks
+ * this driver might be interested in.
+ */
+static int adu_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_device *udev = interface_to_usbdev(interface);
+ struct adu_device *dev = NULL;
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ int retval = -ENODEV;
+ int in_end_size;
+ int out_end_size;
+ int i;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ if (udev == NULL) {
+ dev_err(&interface->dev, "udev is NULL.\n");
+ goto exit;
+ }
+
+ /* allocate memory for our device state and intialize it */
+ dev = kzalloc(sizeof(struct adu_device), GFP_KERNEL);
+ if (dev == NULL) {
+ dev_err(&interface->dev, "Out of memory\n");
+ retval = -ENOMEM;
+ goto exit;
+ }
+
+ init_MUTEX(&dev->sem);
+ spin_lock_init(&dev->buflock);
+ dev->udev = udev;
+ init_waitqueue_head(&dev->read_wait);
+ init_waitqueue_head(&dev->write_wait);
+
+ iface_desc = &interface->altsetting[0];
+
+ /* set up the endpoint information */
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+
+ if (usb_endpoint_is_int_in(endpoint))
+ dev->interrupt_in_endpoint = endpoint;
+
+ if (usb_endpoint_is_int_out(endpoint))
+ dev->interrupt_out_endpoint = endpoint;
+ }
+ if (dev->interrupt_in_endpoint == NULL) {
+ dev_err(&interface->dev, "interrupt in endpoint not found\n");
+ goto error;
+ }
+ if (dev->interrupt_out_endpoint == NULL) {
+ dev_err(&interface->dev, "interrupt out endpoint not found\n");
+ goto error;
+ }
+
+ in_end_size = le16_to_cpu(dev->interrupt_in_endpoint->wMaxPacketSize);
+ out_end_size = le16_to_cpu(dev->interrupt_out_endpoint->wMaxPacketSize);
+
+ dev->read_buffer_primary = kmalloc((4 * in_end_size), GFP_KERNEL);
+ if (!dev->read_buffer_primary) {
+ dev_err(&interface->dev, "Couldn't allocate read_buffer_primary\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->read_buffer_primary, 'a', in_end_size);
+ memset(dev->read_buffer_primary + in_end_size, 'b', in_end_size);
+ memset(dev->read_buffer_primary + (2 * in_end_size), 'c', in_end_size);
+ memset(dev->read_buffer_primary + (3 * in_end_size), 'd', in_end_size);
+
+ dev->read_buffer_secondary = kmalloc((4 * in_end_size), GFP_KERNEL);
+ if (!dev->read_buffer_secondary) {
+ dev_err(&interface->dev, "Couldn't allocate read_buffer_secondary\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->read_buffer_secondary, 'e', in_end_size);
+ memset(dev->read_buffer_secondary + in_end_size, 'f', in_end_size);
+ memset(dev->read_buffer_secondary + (2 * in_end_size), 'g', in_end_size);
+ memset(dev->read_buffer_secondary + (3 * in_end_size), 'h', in_end_size);
+
+ dev->interrupt_in_buffer = kmalloc(in_end_size, GFP_KERNEL);
+ if (!dev->interrupt_in_buffer) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_in_buffer\n");
+ goto error;
+ }
+
+ /* debug code prime the buffer */
+ memset(dev->interrupt_in_buffer, 'i', in_end_size);
+
+ dev->interrupt_in_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_in_urb) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_in_urb\n");
+ goto error;
+ }
+ dev->interrupt_out_buffer = kmalloc(out_end_size, GFP_KERNEL);
+ if (!dev->interrupt_out_buffer) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_out_buffer\n");
+ goto error;
+ }
+ dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!dev->interrupt_out_urb) {
+ dev_err(&interface->dev, "Couldn't allocate interrupt_out_urb\n");
+ goto error;
+ }
+
+ if (!usb_string(udev, udev->descriptor.iSerialNumber, dev->serial_number,
+ sizeof(dev->serial_number))) {
+ dev_err(&interface->dev, "Could not retrieve serial number\n");
+ goto error;
+ }
+ dbg(2," %s : serial_number=%s", __FUNCTION__, dev->serial_number);
+
+ /* we can register the device now, as it is ready */
+ usb_set_intfdata(interface, dev);
+
+ retval = usb_register_dev(interface, &adu_class);
+
+ if (retval) {
+ /* something prevented us from registering this driver */
+ dev_err(&interface->dev, "Not able to get a minor for this device.\n");
+ usb_set_intfdata(interface, NULL);
+ goto error;
+ }
+
+ dev->minor = interface->minor;
+
+ /* let the user know what node this device is now attached to */
+ dev_info(&interface->dev, "ADU%d %s now attached to /dev/usb/adutux%d",
+ udev->descriptor.idProduct, dev->serial_number,
+ (dev->minor - ADU_MINOR_BASE));
+exit:
+ dbg(2," %s : leave, return value %p (dev)", __FUNCTION__, dev);
+
+ return retval;
+
+error:
+ adu_delete(dev);
+ return retval;
+}
+
+/**
+ * adu_disconnect
+ *
+ * Called by the usb core when the device is removed from the system.
+ */
+static void adu_disconnect(struct usb_interface *interface)
+{
+ struct adu_device *dev;
+ int minor;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ mutex_lock(&disconnect_mutex); /* not interruptible */
+
+ dev = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+
+ down(&dev->sem); /* not interruptible */
+
+ minor = dev->minor;
+
+ /* give back our minor */
+ usb_deregister_dev(interface, &adu_class);
+ dev->minor = 0;
+
+ /* if the device is not opened, then we clean up right now */
+ dbg(2," %s : open count %d", __FUNCTION__, dev->open_count);
+ if (!dev->open_count) {
+ up(&dev->sem);
+ adu_delete(dev);
+ } else {
+ dev->udev = NULL;
+ up(&dev->sem);
+ }
+
+ mutex_unlock(&disconnect_mutex);
+
+ dev_info(&interface->dev, "ADU device adutux%d now disconnected",
+ (minor - ADU_MINOR_BASE));
+
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+/* usb specific object needed to register this driver with the usb subsystem */
+static struct usb_driver adu_driver = {
+ .name = "adutux",
+ .probe = adu_probe,
+ .disconnect = adu_disconnect,
+ .id_table = device_table,
+};
+
+static int __init adu_init(void)
+{
+ int result;
+
+ dbg(2," %s : enter", __FUNCTION__);
+
+ /* register this driver with the USB subsystem */
+ result = usb_register(&adu_driver);
+ if (result < 0) {
+ err("usb_register failed for the "__FILE__" driver. "
+ "Error number %d", result);
+ goto exit;
+ }
+
+ info("adutux " DRIVER_DESC " " DRIVER_VERSION);
+ info("adutux is an experimental driver. Use at your own risk");
+
+exit:
+ dbg(2," %s : leave, return value %d", __FUNCTION__, result);
+
+ return result;
+}
+
+static void __exit adu_exit(void)
+{
+ dbg(2," %s : enter", __FUNCTION__);
+ /* deregister this driver with the USB subsystem */
+ usb_deregister(&adu_driver);
+ dbg(2," %s : leave", __FUNCTION__);
+}
+
+module_init(adu_init);
+module_exit(adu_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/auerswald.c b/drivers/usb/misc/auerswald.c
index 1fef36e71c5..4fd2110b341 100644
--- a/drivers/usb/misc/auerswald.c
+++ b/drivers/usb/misc/auerswald.c
@@ -806,7 +806,7 @@ static void auerbuf_releasebuf( pauerbuf_t bp)
0 Initial, OK
-EINPROGRESS during submission until end
-ENOENT if urb is unlinked
--ETIMEDOUT Transfer timed out, NAK
+-ETIME Device did not respond
-ENOMEM Memory Overflow
-ENODEV Specified USB-device or bus doesn't exist
-ENXIO URB already queued
@@ -832,7 +832,7 @@ static int auerswald_status_retry (int status)
{
switch (status) {
case 0:
- case -ETIMEDOUT:
+ case -ETIME:
case -EOVERFLOW:
case -EAGAIN:
case -EPIPE:
@@ -1858,7 +1858,7 @@ static int auerchar_release (struct inode *inode, struct file *file)
/*----------------------------------------------------------------------*/
/* File operation structure */
-static struct file_operations auerswald_fops =
+static const struct file_operations auerswald_fops =
{
.owner = THIS_MODULE,
.llseek = no_llseek,
diff --git a/drivers/usb/misc/cypress_cy7c63.c b/drivers/usb/misc/cypress_cy7c63.c
index 9c46746d5d0..b63b5f34b2a 100644
--- a/drivers/usb/misc/cypress_cy7c63.c
+++ b/drivers/usb/misc/cypress_cy7c63.c
@@ -209,7 +209,7 @@ static int cypress_probe(struct usb_interface *interface,
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory!\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(interface_to_usbdev(interface));
@@ -218,15 +218,26 @@ static int cypress_probe(struct usb_interface *interface,
usb_set_intfdata(interface, dev);
/* create device attribute files */
- device_create_file(&interface->dev, &dev_attr_port0);
- device_create_file(&interface->dev, &dev_attr_port1);
+ retval = device_create_file(&interface->dev, &dev_attr_port0);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port1);
+ if (retval)
+ goto error;
/* let the user know that the device is now attached */
dev_info(&interface->dev,
"Cypress CY7C63xxx device now attached\n");
+ return 0;
- retval = 0;
error:
+ device_remove_file(&interface->dev, &dev_attr_port0);
+ device_remove_file(&interface->dev, &dev_attr_port1);
+ usb_set_intfdata(interface, NULL);
+ usb_put_dev(dev->udev);
+ kfree(dev);
+
+error_mem:
return retval;
}
diff --git a/drivers/usb/misc/cytherm.c b/drivers/usb/misc/cytherm.c
index b20bec44555..04e87acd6e4 100644
--- a/drivers/usb/misc/cytherm.c
+++ b/drivers/usb/misc/cytherm.c
@@ -353,7 +353,7 @@ static int cytherm_probe(struct usb_interface *interface,
dev = kzalloc (sizeof(struct usb_cytherm), GFP_KERNEL);
if (dev == NULL) {
dev_err (&interface->dev, "Out of memory\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(udev);
@@ -362,18 +362,35 @@ static int cytherm_probe(struct usb_interface *interface,
dev->brightness = 0xFF;
- device_create_file(&interface->dev, &dev_attr_brightness);
- device_create_file(&interface->dev, &dev_attr_temp);
- device_create_file(&interface->dev, &dev_attr_button);
- device_create_file(&interface->dev, &dev_attr_port0);
- device_create_file(&interface->dev, &dev_attr_port1);
+ retval = device_create_file(&interface->dev, &dev_attr_brightness);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_temp);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_button);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port0);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_port1);
+ if (retval)
+ goto error;
- dev_info (&interface->dev,
+ dev_info (&interface->dev,
"Cypress thermometer device now attached\n");
return 0;
-
- error:
+error:
+ device_remove_file(&interface->dev, &dev_attr_brightness);
+ device_remove_file(&interface->dev, &dev_attr_temp);
+ device_remove_file(&interface->dev, &dev_attr_button);
+ device_remove_file(&interface->dev, &dev_attr_port0);
+ device_remove_file(&interface->dev, &dev_attr_port1);
+ usb_set_intfdata (interface, NULL);
+ usb_put_dev(dev->udev);
kfree(dev);
+error_mem:
return retval;
}
diff --git a/drivers/usb/misc/ftdi-elan.c b/drivers/usb/misc/ftdi-elan.c
new file mode 100644
index 00000000000..b88a09497c2
--- /dev/null
+++ b/drivers/usb/misc/ftdi-elan.c
@@ -0,0 +1,2809 @@
+/*
+* USB FTDI client driver for Elan Digital Systems's Uxxx adapters
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+* http://www.elandigitalsystems.com
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+* tony.olech@elandigitalsystems.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, version 2.
+*
+*
+* This driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+*/
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/ioctl.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <asm/uaccess.h>
+#include <linux/usb.h>
+#include <linux/workqueue.h>
+#include <linux/platform_device.h>
+MODULE_AUTHOR("Tony Olech");
+MODULE_DESCRIPTION("FTDI ELAN driver");
+MODULE_LICENSE("GPL");
+#define INT_MODULE_PARM(n, v) static int n = v;module_param(n, int, 0444)
+extern struct platform_driver u132_platform_driver;
+static struct workqueue_struct *status_queue;
+static struct workqueue_struct *command_queue;
+static struct workqueue_struct *respond_queue;
+/*
+* ftdi_module_lock exists to protect access to global variables
+*
+*/
+static struct semaphore ftdi_module_lock;
+static int ftdi_instances = 0;
+static struct list_head ftdi_static_list;
+/*
+* end of the global variables protected by ftdi_module_lock
+*/
+#include "usb_u132.h"
+#define TD_DEVNOTRESP 5
+/* Define these values to match your devices*/
+#define USB_FTDI_ELAN_VENDOR_ID 0x0403
+#define USB_FTDI_ELAN_PRODUCT_ID 0xd6ea
+/* table of devices that work with this driver*/
+static struct usb_device_id ftdi_elan_table[] = {
+ {USB_DEVICE(USB_FTDI_ELAN_VENDOR_ID, USB_FTDI_ELAN_PRODUCT_ID)},
+ { /* Terminating entry */ }
+};
+
+MODULE_DEVICE_TABLE(usb, ftdi_elan_table);
+/* only the jtag(firmware upgrade device) interface requires
+* a device file and corresponding minor number, but the
+* interface is created unconditionally - I suppose it could
+* be configured or not according to a module parameter.
+* But since we(now) require one interface per device,
+* and since it unlikely that a normal installation would
+* require more than a couple of elan-ftdi devices, 8 seems
+* like a reasonable limit to have here, and if someone
+* really requires more than 8 devices, then they can frig the
+* code and recompile
+*/
+#define USB_FTDI_ELAN_MINOR_BASE 192
+#define COMMAND_BITS 5
+#define COMMAND_SIZE (1<<COMMAND_BITS)
+#define COMMAND_MASK (COMMAND_SIZE-1)
+struct u132_command {
+ u8 header;
+ u16 length;
+ u8 address;
+ u8 width;
+ u32 value;
+ int follows;
+ void *buffer;
+};
+#define RESPOND_BITS 5
+#define RESPOND_SIZE (1<<RESPOND_BITS)
+#define RESPOND_MASK (RESPOND_SIZE-1)
+struct u132_respond {
+ u8 header;
+ u8 address;
+ u32 *value;
+ int *result;
+ struct completion wait_completion;
+};
+struct u132_target {
+ void *endp;
+ struct urb *urb;
+ int toggle_bits;
+ int error_count;
+ int condition_code;
+ int repeat_number;
+ int halted;
+ int skipped;
+ int actual;
+ int non_null;
+ int active;
+ int abandoning;
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code,
+ int repeat_number, int halted, int skipped, int actual,
+ int non_null);
+};
+/* Structure to hold all of our device specific stuff*/
+struct usb_ftdi {
+ struct list_head ftdi_list;
+ struct semaphore u132_lock;
+ int command_next;
+ int command_head;
+ struct u132_command command[COMMAND_SIZE];
+ int respond_next;
+ int respond_head;
+ struct u132_respond respond[RESPOND_SIZE];
+ struct u132_target target[4];
+ char device_name[16];
+ unsigned synchronized:1;
+ unsigned enumerated:1;
+ unsigned registered:1;
+ unsigned initialized:1;
+ unsigned card_ejected:1;
+ int function;
+ int sequence_num;
+ int disconnected;
+ int gone_away;
+ int stuck_status;
+ int status_queue_delay;
+ struct semaphore sw_lock;
+ struct usb_device *udev;
+ struct usb_interface *interface;
+ struct usb_class_driver *class;
+ struct work_struct status_work;
+ struct work_struct command_work;
+ struct work_struct respond_work;
+ struct u132_platform_data platform_data;
+ struct resource resources[0];
+ struct platform_device platform_dev;
+ unsigned char *bulk_in_buffer;
+ size_t bulk_in_size;
+ size_t bulk_in_last;
+ size_t bulk_in_left;
+ __u8 bulk_in_endpointAddr;
+ __u8 bulk_out_endpointAddr;
+ struct kref kref;
+ u32 controlreg;
+ u8 response[4 + 1024];
+ int expected;
+ int recieved;
+ int ed_found;
+};
+#define kref_to_usb_ftdi(d) container_of(d, struct usb_ftdi, kref)
+#define platform_device_to_usb_ftdi(d) container_of(d, struct usb_ftdi, \
+ platform_dev)
+static struct usb_driver ftdi_elan_driver;
+static void ftdi_elan_delete(struct kref *kref)
+{
+ struct usb_ftdi *ftdi = kref_to_usb_ftdi(kref);
+ dev_warn(&ftdi->udev->dev, "FREEING ftdi=%p\n", ftdi);
+ usb_put_dev(ftdi->udev);
+ ftdi->disconnected += 1;
+ down(&ftdi_module_lock);
+ list_del_init(&ftdi->ftdi_list);
+ ftdi_instances -= 1;
+ up(&ftdi_module_lock);
+ kfree(ftdi->bulk_in_buffer);
+ ftdi->bulk_in_buffer = NULL;
+}
+
+static void ftdi_elan_put_kref(struct usb_ftdi *ftdi)
+{
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_elan_get_kref(struct usb_ftdi *ftdi)
+{
+ kref_get(&ftdi->kref);
+}
+
+static void ftdi_elan_init_kref(struct usb_ftdi *ftdi)
+{
+ kref_init(&ftdi->kref);
+}
+
+static void ftdi_status_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ return;
+ } else if (queue_work(status_queue, &ftdi->status_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_status_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(status_queue, &ftdi->status_work, delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(status_queue, &ftdi->status_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_status_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->status_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_command_requeue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(command_queue, &ftdi->command_work,
+ delta))
+ return;
+ } else if (queue_work(command_queue, &ftdi->command_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_command_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(command_queue, &ftdi->command_work,
+ delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(command_queue, &ftdi->command_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_command_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->command_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+static void ftdi_response_requeue_work(struct usb_ftdi *ftdi,
+ unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+ delta))
+ return;
+ } else if (queue_work(respond_queue, &ftdi->respond_work))
+ return;
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+ return;
+}
+
+static void ftdi_respond_queue_work(struct usb_ftdi *ftdi, unsigned int delta)
+{
+ if (delta > 0) {
+ if (queue_delayed_work(respond_queue, &ftdi->respond_work,
+ delta))
+ kref_get(&ftdi->kref);
+ } else if (queue_work(respond_queue, &ftdi->respond_work))
+ kref_get(&ftdi->kref);
+ return;
+}
+
+static void ftdi_response_cancel_work(struct usb_ftdi *ftdi)
+{
+ if (cancel_delayed_work(&ftdi->respond_work))
+ kref_put(&ftdi->kref, ftdi_elan_delete);
+}
+
+void ftdi_elan_gone_away(struct platform_device *pdev)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ ftdi->gone_away += 1;
+ ftdi_elan_put_kref(ftdi);
+}
+
+
+EXPORT_SYMBOL_GPL(ftdi_elan_gone_away);
+void ftdi_release_platform_dev(struct device *dev)
+{
+ dev->parent = NULL;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+ struct u132_target *target, u8 *buffer, int length);
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi);
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi);
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi);
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi);
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi);
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi);
+static int ftdi_elan_hcd_init(struct usb_ftdi *ftdi)
+{
+ int result;
+ if (ftdi->platform_dev.dev.parent)
+ return -EBUSY;
+ ftdi_elan_get_kref(ftdi);
+ ftdi->platform_data.potpg = 100;
+ ftdi->platform_data.reset = NULL;
+ ftdi->platform_dev.id = ftdi->sequence_num;
+ ftdi->platform_dev.resource = ftdi->resources;
+ ftdi->platform_dev.num_resources = ARRAY_SIZE(ftdi->resources);
+ ftdi->platform_dev.dev.platform_data = &ftdi->platform_data;
+ ftdi->platform_dev.dev.parent = NULL;
+ ftdi->platform_dev.dev.release = ftdi_release_platform_dev;
+ ftdi->platform_dev.dev.dma_mask = NULL;
+ snprintf(ftdi->device_name, sizeof(ftdi->device_name), "u132_hcd");
+ ftdi->platform_dev.name = ftdi->device_name;
+ dev_info(&ftdi->udev->dev, "requesting module '%s'\n", "u132_hcd");
+ request_module("u132_hcd");
+ dev_info(&ftdi->udev->dev, "registering '%s'\n",
+ ftdi->platform_dev.name);
+ result = platform_device_register(&ftdi->platform_dev);
+ return result;
+}
+
+static void ftdi_elan_abandon_completions(struct usb_ftdi *ftdi)
+{
+ down(&ftdi->u132_lock);
+ while (ftdi->respond_next > ftdi->respond_head) {
+ struct u132_respond *respond = &ftdi->respond[RESPOND_MASK &
+ ftdi->respond_head++];
+ *respond->result = -ESHUTDOWN;
+ *respond->value = 0;
+ complete(&respond->wait_completion);
+ } up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_abandon_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ if (target->active == 1) {
+ target->condition_code = TD_DEVNOTRESP;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, NULL, 0);
+ down(&ftdi->u132_lock);
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_flush_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ target->abandoning = 1;
+ wait_1:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed_number << 5) | 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_1;
+ }
+ }
+ wait_2:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x90 | (ed_number << 5);
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_2;
+ }
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_cancel_targets(struct usb_ftdi *ftdi)
+{
+ int ed_number = 4;
+ down(&ftdi->u132_lock);
+ while (ed_number-- > 0) {
+ struct u132_target *target = &ftdi->target[ed_number];
+ target->abandoning = 1;
+ wait:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed_number << 5) | 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait;
+ }
+ }
+ }
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ up(&ftdi->u132_lock);
+}
+
+static void ftdi_elan_kick_command_queue(struct usb_ftdi *ftdi)
+{
+ ftdi_command_queue_work(ftdi, 0);
+ return;
+}
+
+static void ftdi_elan_command_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ int retval = ftdi_elan_command_engine(ftdi);
+ if (retval == -ESHUTDOWN) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval)
+ dev_err(&ftdi->udev->dev, "command error %d\n", retval);
+ ftdi_command_requeue_work(ftdi, msecs_to_jiffies(10));
+ return;
+ }
+}
+
+static void ftdi_elan_kick_respond_queue(struct usb_ftdi *ftdi)
+{
+ ftdi_respond_queue_work(ftdi, 0);
+ return;
+}
+
+static void ftdi_elan_respond_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ int retval = ftdi_elan_respond_engine(ftdi);
+ if (retval == 0) {
+ } else if (retval == -ESHUTDOWN) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval == -ENODEV) {
+ ftdi->disconnected += 1;
+ } else if (retval == -EILSEQ) {
+ ftdi->disconnected += 1;
+ } else {
+ ftdi->disconnected += 1;
+ dev_err(&ftdi->udev->dev, "respond error %d\n", retval);
+ }
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_abandon_completions(ftdi);
+ ftdi_elan_abandon_targets(ftdi);
+ }
+ ftdi_response_requeue_work(ftdi, msecs_to_jiffies(10));
+ return;
+ }
+}
+
+
+/*
+* the sw_lock is initially held and will be freed
+* after the FTDI has been synchronized
+*
+*/
+static void ftdi_elan_status_work(void *data)
+{
+ struct usb_ftdi *ftdi = data;
+ int work_delay_in_msec = 0;
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else if (ftdi->synchronized == 0) {
+ down(&ftdi->sw_lock);
+ if (ftdi_elan_synchronize(ftdi) == 0) {
+ ftdi->synchronized = 1;
+ ftdi_command_queue_work(ftdi, 1);
+ ftdi_respond_queue_work(ftdi, 1);
+ up(&ftdi->sw_lock);
+ work_delay_in_msec = 100;
+ } else {
+ dev_err(&ftdi->udev->dev, "synchronize failed\n");
+ up(&ftdi->sw_lock);
+ work_delay_in_msec = 10 *1000;
+ }
+ } else if (ftdi->stuck_status > 0) {
+ if (ftdi_elan_stuck_waiting(ftdi) == 0) {
+ ftdi->stuck_status = 0;
+ ftdi->synchronized = 0;
+ } else if ((ftdi->stuck_status++ % 60) == 1) {
+ dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+ "- please remove\n");
+ } else
+ dev_err(&ftdi->udev->dev, "WRONG type of card inserted "
+ "- checked %d times\n", ftdi->stuck_status);
+ work_delay_in_msec = 100;
+ } else if (ftdi->enumerated == 0) {
+ if (ftdi_elan_enumeratePCI(ftdi) == 0) {
+ ftdi->enumerated = 1;
+ work_delay_in_msec = 250;
+ } else
+ work_delay_in_msec = 1000;
+ } else if (ftdi->initialized == 0) {
+ if (ftdi_elan_setupOHCI(ftdi) == 0) {
+ ftdi->initialized = 1;
+ work_delay_in_msec = 500;
+ } else {
+ dev_err(&ftdi->udev->dev, "initialized failed - trying "
+ "again in 10 seconds\n");
+ work_delay_in_msec = 10 *1000;
+ }
+ } else if (ftdi->registered == 0) {
+ work_delay_in_msec = 10;
+ if (ftdi_elan_hcd_init(ftdi) == 0) {
+ ftdi->registered = 1;
+ } else
+ dev_err(&ftdi->udev->dev, "register failed\n");
+ work_delay_in_msec = 250;
+ } else {
+ if (ftdi_elan_checkingPCI(ftdi) == 0) {
+ work_delay_in_msec = 250;
+ } else if (ftdi->controlreg & 0x00400000) {
+ if (ftdi->gone_away > 0) {
+ dev_err(&ftdi->udev->dev, "PCI device eject con"
+ "firmed platform_dev.dev.parent=%p plat"
+ "form_dev.dev=%p\n",
+ ftdi->platform_dev.dev.parent,
+ &ftdi->platform_dev.dev);
+ platform_device_unregister(&ftdi->platform_dev);
+ ftdi->platform_dev.dev.parent = NULL;
+ ftdi->registered = 0;
+ ftdi->enumerated = 0;
+ ftdi->card_ejected = 0;
+ ftdi->initialized = 0;
+ ftdi->gone_away = 0;
+ } else
+ ftdi_elan_flush_targets(ftdi);
+ work_delay_in_msec = 250;
+ } else {
+ dev_err(&ftdi->udev->dev, "PCI device has disappeared\n"
+ );
+ ftdi_elan_cancel_targets(ftdi);
+ work_delay_in_msec = 500;
+ ftdi->enumerated = 0;
+ ftdi->initialized = 0;
+ }
+ }
+ if (ftdi->disconnected > 0) {
+ ftdi_elan_put_kref(ftdi);
+ return;
+ } else {
+ ftdi_status_requeue_work(ftdi,
+ msecs_to_jiffies(work_delay_in_msec));
+ return;
+ }
+}
+
+
+/*
+* file_operations for the jtag interface
+*
+* the usage count for the device is incremented on open()
+* and decremented on release()
+*/
+static int ftdi_elan_open(struct inode *inode, struct file *file)
+{
+ int subminor = iminor(inode);
+ struct usb_interface *interface = usb_find_interface(&ftdi_elan_driver,
+ subminor);
+ if (!interface) {
+ printk(KERN_ERR "can't find device for minor %d\n", subminor);
+ return -ENODEV;
+ } else {
+ struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+ if (!ftdi) {
+ return -ENODEV;
+ } else {
+ if (down_interruptible(&ftdi->sw_lock)) {
+ return -EINTR;
+ } else {
+ ftdi_elan_get_kref(ftdi);
+ file->private_data = ftdi;
+ return 0;
+ }
+ }
+ }
+}
+
+static int ftdi_elan_release(struct inode *inode, struct file *file)
+{
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi == NULL)
+ return -ENODEV;
+ up(&ftdi->sw_lock); /* decrement the count on our device */
+ ftdi_elan_put_kref(ftdi);
+ return 0;
+}
+
+
+#define FTDI_ELAN_IOC_MAGIC 0xA1
+#define FTDI_ELAN_IOCDEBUG _IOC(_IOC_WRITE, FTDI_ELAN_IOC_MAGIC, 1, 132)
+static int ftdi_elan_ioctl(struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ switch (cmd) {
+ case FTDI_ELAN_IOCDEBUG:{
+ char line[132];
+ int size = strncpy_from_user(line,
+ (const char __user *)arg, sizeof(line));
+ if (size < 0) {
+ return -EINVAL;
+ } else {
+ printk(KERN_ERR "TODO: ioctl %s\n", line);
+ return 0;
+ }
+ }
+ default:
+ return -EFAULT;
+ }
+}
+
+
+/*
+*
+* blocking bulk reads are used to get data from the device
+*
+*/
+static ssize_t ftdi_elan_read(struct file *file, char __user *buffer,
+ size_t count, loff_t *ppos)
+{
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int bytes_read = 0;
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ }
+ data[0] = 0;
+ have:if (ftdi->bulk_in_left > 0) {
+ if (count-- > 0) {
+ char *p = ++ftdi->bulk_in_last + ftdi->bulk_in_buffer;
+ ftdi->bulk_in_left -= 1;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X", 0x000000FF & *p);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ if (copy_to_user(buffer++, p, 1)) {
+ return -EFAULT;
+ } else {
+ bytes_read += 1;
+ goto have;
+ }
+ } else
+ return bytes_read;
+ }
+ more:if (count > 0) {
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(50));
+ if (packet_bytes > 2) {
+ ftdi->bulk_in_left = packet_bytes - 2;
+ ftdi->bulk_in_last = 1;
+ goto have;
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else if (bytes_read > 0) {
+ return bytes_read;
+ } else
+ return retval;
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return bytes_read;
+ } else
+ return retval;
+ } else
+ return bytes_read;
+}
+
+static void ftdi_elan_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)urb->context;
+ if (urb->status && !(urb->status == -ENOENT || urb->status ==
+ -ECONNRESET || urb->status == -ESHUTDOWN)) {
+ dev_err(&ftdi->udev->dev, "urb=%p write bulk status received: %"
+ "d\n", urb, urb->status);
+ }
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ urb->transfer_buffer, urb->transfer_dma);
+}
+
+static int fill_buffer_with_all_queued_commands(struct usb_ftdi *ftdi,
+ char *buf, int command_size, int total_size)
+{
+ int ed_commands = 0;
+ int b = 0;
+ int I = command_size;
+ int i = ftdi->command_head;
+ while (I-- > 0) {
+ struct u132_command *command = &ftdi->command[COMMAND_MASK &
+ i++];
+ int F = command->follows;
+ u8 *f = command->buffer;
+ if (command->header & 0x80) {
+ ed_commands |= 1 << (0x3 & (command->header >> 5));
+ }
+ buf[b++] = command->header;
+ buf[b++] = (command->length >> 0) & 0x00FF;
+ buf[b++] = (command->length >> 8) & 0x00FF;
+ buf[b++] = command->address;
+ buf[b++] = command->width;
+ while (F-- > 0) {
+ buf[b++] = *f++;
+ }
+ }
+ return ed_commands;
+}
+
+static int ftdi_elan_total_command_size(struct usb_ftdi *ftdi, int command_size)
+{
+ int total_size = 0;
+ int I = command_size;
+ int i = ftdi->command_head;
+ while (I-- > 0) {
+ struct u132_command *command = &ftdi->command[COMMAND_MASK &
+ i++];
+ total_size += 5 + command->follows;
+ } return total_size;
+}
+
+static int ftdi_elan_command_engine(struct usb_ftdi *ftdi)
+{
+ int retval;
+ char *buf;
+ int ed_commands;
+ int total_size;
+ struct urb *urb;
+ int command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size == 0)
+ return 0;
+ total_size = ftdi_elan_total_command_size(ftdi, command_size);
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not get a urb to write %d comm"
+ "ands totaling %d bytes to the Uxxx\n", command_size,
+ total_size);
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, total_size, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer to write %d c"
+ "ommands totaling %d bytes to the Uxxx\n", command_size,
+ total_size);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ ed_commands = fill_buffer_with_all_queued_commands(ftdi, buf,
+ command_size, total_size);
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, total_size,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ if (ed_commands) {
+ char diag[40 *3 + 4];
+ char *d = diag;
+ int m = total_size;
+ u8 *c = buf;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ }
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed %d to submit urb %p to write "
+ "%d commands totaling %d bytes to the Uxxx\n", retval,
+ urb, command_size, total_size);
+ usb_buffer_free(ftdi->udev, total_size, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return retval;
+ }
+ usb_free_urb(urb); /* release our reference to this urb,
+ the USB core will eventually free it entirely */
+ ftdi->command_head += command_size;
+ ftdi_elan_kick_respond_queue(ftdi);
+ return 0;
+}
+
+static void ftdi_elan_do_callback(struct usb_ftdi *ftdi,
+ struct u132_target *target, u8 *buffer, int length)
+{
+ struct urb *urb = target->urb;
+ int halted = target->halted;
+ int skipped = target->skipped;
+ int actual = target->actual;
+ int non_null = target->non_null;
+ int toggle_bits = target->toggle_bits;
+ int error_count = target->error_count;
+ int condition_code = target->condition_code;
+ int repeat_number = target->repeat_number;
+ void (*callback) (void *, struct urb *, u8 *, int, int, int, int, int,
+ int, int, int, int) = target->callback;
+ target->active -= 1;
+ target->callback = NULL;
+ (*callback) (target->endp, urb, buffer, length, toggle_bits,
+ error_count, condition_code, repeat_number, halted, skipped,
+ actual, non_null);
+}
+
+static char *have_ed_set_response(struct usb_ftdi *ftdi,
+ struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+ char *b)
+{
+ int payload = (ed_length >> 0) & 0x07FF;
+ down(&ftdi->u132_lock);
+ target->actual = 0;
+ target->non_null = (ed_length >> 15) & 0x0001;
+ target->repeat_number = (ed_length >> 11) & 0x000F;
+ if (ed_type == 0x02) {
+ if (payload == 0 || target->abandoning > 0) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ ftdi->expected = 4 + payload;
+ ftdi->ed_found = 1;
+ up(&ftdi->u132_lock);
+ return b;
+ }
+ } else if (ed_type == 0x03) {
+ if (payload == 0 || target->abandoning > 0) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ ftdi->expected = 4 + payload;
+ ftdi->ed_found = 1;
+ up(&ftdi->u132_lock);
+ return b;
+ }
+ } else if (ed_type == 0x01) {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ } else {
+ target->abandoning = 0;
+ up(&ftdi->u132_lock);
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+ }
+}
+
+static char *have_ed_get_response(struct usb_ftdi *ftdi,
+ struct u132_target *target, u16 ed_length, int ed_number, int ed_type,
+ char *b)
+{
+ down(&ftdi->u132_lock);
+ target->condition_code = TD_DEVNOTRESP;
+ target->actual = (ed_length >> 0) & 0x01FF;
+ target->non_null = (ed_length >> 15) & 0x0001;
+ target->repeat_number = (ed_length >> 11) & 0x000F;
+ up(&ftdi->u132_lock);
+ if (target->active)
+ ftdi_elan_do_callback(ftdi, target, NULL, 0);
+ target->abandoning = 0;
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ return ftdi->response;
+}
+
+
+/*
+* The engine tries to empty the FTDI fifo
+*
+* all responses found in the fifo data are dispatched thus
+* the response buffer can only ever hold a maximum sized
+* response from the Uxxx.
+*
+*/
+static int ftdi_elan_respond_engine(struct usb_ftdi *ftdi)
+{
+ u8 *b = ftdi->response + ftdi->recieved;
+ int bytes_read = 0;
+ int retry_on_empty = 1;
+ int retry_on_timeout = 3;
+ int empty_packets = 0;
+ read:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(500));
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = packet_bytes;
+ u8 *c = ftdi->bulk_in_buffer;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ if (packet_bytes > 2) {
+ ftdi->bulk_in_left = packet_bytes - 2;
+ ftdi->bulk_in_last = 1;
+ goto have;
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+ "t_bytes = %d with total %d bytes%s\n",
+ packet_bytes, bytes_read, diag);
+ goto more;
+ } else if (bytes_read > 0) {
+ dev_err(&ftdi->udev->dev, "ONLY %d bytes%s\n",
+ bytes_read, diag);
+ return -ENOMEM;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT with packe"
+ "t_bytes = %d with total %d bytes%s\n",
+ packet_bytes, bytes_read, diag);
+ return -ENOMEM;
+ }
+ } else if (retval == -EILSEQ) {
+ dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+ " = %d with total %d bytes%s\n", retval,
+ packet_bytes, bytes_read, diag);
+ return retval;
+ } else if (retval) {
+ dev_err(&ftdi->udev->dev, "error = %d with packet_bytes"
+ " = %d with total %d bytes%s\n", retval,
+ packet_bytes, bytes_read, diag);
+ return retval;
+ } else if (packet_bytes == 2) {
+ unsigned char s0 = ftdi->bulk_in_buffer[0];
+ unsigned char s1 = ftdi->bulk_in_buffer[1];
+ empty_packets += 1;
+ if (s0 == 0x31 && s1 == 0x60) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else if (s0 == 0x31 && s1 == 0x00) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ }
+ } else if (packet_bytes == 1) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ } else {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else
+ return 0;
+ }
+ }
+ more:{
+ goto read;
+ }
+ have:if (ftdi->bulk_in_left > 0) {
+ u8 c = ftdi->bulk_in_buffer[++ftdi->bulk_in_last];
+ bytes_read += 1;
+ ftdi->bulk_in_left -= 1;
+ if (ftdi->recieved == 0 && c == 0xFF) {
+ goto have;
+ } else
+ *b++ = c;
+ if (++ftdi->recieved < ftdi->expected) {
+ goto have;
+ } else if (ftdi->ed_found) {
+ int ed_number = (ftdi->response[0] >> 5) & 0x03;
+ u16 ed_length = (ftdi->response[2] << 8) |
+ ftdi->response[1];
+ struct u132_target *target = &ftdi->target[ed_number];
+ int payload = (ed_length >> 0) & 0x07FF;
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = payload;
+ u8 *c = 4 + ftdi->response;
+ int s = (sizeof(diag) - 1) / 3;
+ diag[0] = 0;
+ while (s-- > 0 && m-- > 0) {
+ if (s > 0 || m == 0) {
+ d += sprintf(d, " %02X", *c++);
+ } else
+ d += sprintf(d, " ..");
+ }
+ ftdi_elan_do_callback(ftdi, target, 4 + ftdi->response,
+ payload);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ b = ftdi->response;
+ goto have;
+ } else if (ftdi->expected == 8) {
+ u8 buscmd;
+ int respond_head = ftdi->respond_head++;
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & respond_head];
+ u32 data = ftdi->response[7];
+ data <<= 8;
+ data |= ftdi->response[6];
+ data <<= 8;
+ data |= ftdi->response[5];
+ data <<= 8;
+ data |= ftdi->response[4];
+ *respond->value = data;
+ *respond->result = 0;
+ complete(&respond->wait_completion);
+ ftdi->recieved = 0;
+ ftdi->expected = 4;
+ ftdi->ed_found = 0;
+ b = ftdi->response;
+ buscmd = (ftdi->response[0] >> 0) & 0x0F;
+ if (buscmd == 0x00) {
+ } else if (buscmd == 0x02) {
+ } else if (buscmd == 0x06) {
+ } else if (buscmd == 0x0A) {
+ } else
+ dev_err(&ftdi->udev->dev, "Uxxx unknown(%0X) va"
+ "lue = %08X\n", buscmd, data);
+ goto have;
+ } else {
+ if ((ftdi->response[0] & 0x80) == 0x00) {
+ ftdi->expected = 8;
+ goto have;
+ } else {
+ int ed_number = (ftdi->response[0] >> 5) & 0x03;
+ int ed_type = (ftdi->response[0] >> 0) & 0x03;
+ u16 ed_length = (ftdi->response[2] << 8) |
+ ftdi->response[1];
+ struct u132_target *target = &ftdi->target[
+ ed_number];
+ target->halted = (ftdi->response[0] >> 3) &
+ 0x01;
+ target->skipped = (ftdi->response[0] >> 2) &
+ 0x01;
+ target->toggle_bits = (ftdi->response[3] >> 6)
+ & 0x03;
+ target->error_count = (ftdi->response[3] >> 4)
+ & 0x03;
+ target->condition_code = (ftdi->response[
+ 3] >> 0) & 0x0F;
+ if ((ftdi->response[0] & 0x10) == 0x00) {
+ b = have_ed_set_response(ftdi, target,
+ ed_length, ed_number, ed_type,
+ b);
+ goto have;
+ } else {
+ b = have_ed_get_response(ftdi, target,
+ ed_length, ed_number, ed_type,
+ b);
+ goto have;
+ }
+ }
+ }
+ } else
+ goto more;
+}
+
+
+/*
+* create a urb, and a buffer for it, and copy the data to the urb
+*
+*/
+static ssize_t ftdi_elan_write(struct file *file,
+ const char __user *user_buffer, size_t count,
+ loff_t *ppos)
+{
+ int retval = 0;
+ struct urb *urb;
+ char *buf;
+ char data[30 *3 + 4];
+ char *d = data;
+ const char __user *s = user_buffer;
+ int m = (sizeof(data) - 1) / 3;
+ struct usb_ftdi *ftdi = (struct usb_ftdi *)file->private_data;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ }
+ if (count == 0) {
+ goto exit;
+ }
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ retval = -ENOMEM;
+ goto error_1;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, count, GFP_KERNEL,
+ &urb->transfer_dma);
+ if (!buf) {
+ retval = -ENOMEM;
+ goto error_2;
+ }
+ if (copy_from_user(buf, user_buffer, count)) {
+ retval = -EFAULT;
+ goto error_3;
+ }
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, count,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed submitting write urb, error %"
+ "d\n", retval);
+ goto error_4;
+ }
+ usb_free_urb(urb);
+ exit:;
+ if (count > m) {
+ int I = m - 1;
+ while (I-- > 0) {
+ d += sprintf(d, " %02X", 0x000000FF & *s++);
+ }
+ d += sprintf(d, " ..");
+ } else {
+ int I = count;
+ while (I-- > 0) {
+ d += sprintf(d, " %02X", 0x000000FF & *s++);
+ }
+ }
+ return count;
+ error_4: error_3:usb_buffer_free(ftdi->udev, count, buf,
+ urb->transfer_dma);
+ error_2:usb_free_urb(urb);
+ error_1:return retval;
+}
+
+static struct file_operations ftdi_elan_fops = {
+ .owner = THIS_MODULE,
+ .llseek = no_llseek,
+ .ioctl = ftdi_elan_ioctl,
+ .read = ftdi_elan_read,
+ .write = ftdi_elan_write,
+ .open = ftdi_elan_open,
+ .release = ftdi_elan_release,
+};
+
+/*
+* usb class driver info in order to get a minor number from the usb core,
+* and to have the device registered with the driver core
+*/
+static struct usb_class_driver ftdi_elan_jtag_class = {
+ .name = "ftdi-%d-jtag",
+ .fops = &ftdi_elan_fops,
+ .minor_base = USB_FTDI_ELAN_MINOR_BASE,
+};
+
+/*
+* the following definitions are for the
+* ELAN FPGA state machgine processor that
+* lies on the other side of the FTDI chip
+*/
+#define cPCIu132rd 0x0
+#define cPCIu132wr 0x1
+#define cPCIiord 0x2
+#define cPCIiowr 0x3
+#define cPCImemrd 0x6
+#define cPCImemwr 0x7
+#define cPCIcfgrd 0xA
+#define cPCIcfgwr 0xB
+#define cPCInull 0xF
+#define cU132cmd_status 0x0
+#define cU132flash 0x1
+#define cPIDsetup 0x0
+#define cPIDout 0x1
+#define cPIDin 0x2
+#define cPIDinonce 0x3
+#define cCCnoerror 0x0
+#define cCCcrc 0x1
+#define cCCbitstuff 0x2
+#define cCCtoggle 0x3
+#define cCCstall 0x4
+#define cCCnoresp 0x5
+#define cCCbadpid1 0x6
+#define cCCbadpid2 0x7
+#define cCCdataoverrun 0x8
+#define cCCdataunderrun 0x9
+#define cCCbuffoverrun 0xC
+#define cCCbuffunderrun 0xD
+#define cCCnotaccessed 0xF
+static int ftdi_elan_write_reg(struct usb_ftdi *ftdi, u32 data)
+{
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | cPCIu132wr;
+ command->length = 0x04;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_write_config(struct usb_ftdi *ftdi, int config_offset,
+ u8 width, u32 data)
+{
+ u8 addressofs = config_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | (cPCIcfgwr & 0x0F);
+ command->length = 0x04;
+ command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_write_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+ u8 width, u32 data)
+{
+ u8 addressofs = mem_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x00 | (cPCImemwr & 0x0F);
+ command->length = 0x04;
+ command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 4;
+ command->value = data;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_write_pcimem(struct platform_device *pdev, int mem_offset,
+ u8 width, u32 data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_write_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_write_pcimem);
+static int ftdi_elan_read_reg(struct usb_ftdi *ftdi, u32 *data)
+{
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | cPCIu132rd;
+ command->length = 0x04;
+ respond->address = command->address = cU132cmd_status;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_read_reg(struct platform_device *pdev, u32 *data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_read_reg(ftdi, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_reg);
+static int ftdi_elan_read_config(struct usb_ftdi *ftdi, int config_offset,
+ u8 width, u32 *data)
+{
+ u8 addressofs = config_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | (cPCIcfgrd &
+ 0x0F);
+ command->length = 0x04;
+ respond->address = command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+static int ftdi_elan_read_pcimem(struct usb_ftdi *ftdi, int mem_offset,
+ u8 width, u32 *data)
+{
+ u8 addressofs = mem_offset / 4;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ int respond_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ respond_size = ftdi->respond_next - ftdi->respond_head;
+ if (command_size < COMMAND_SIZE && respond_size < RESPOND_SIZE)
+ {
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ struct u132_respond *respond = &ftdi->respond[
+ RESPOND_MASK & ftdi->respond_next];
+ int result = -ENODEV;
+ respond->result = &result;
+ respond->header = command->header = 0x00 | (cPCImemrd &
+ 0x0F);
+ command->length = 0x04;
+ respond->address = command->address = addressofs;
+ command->width = 0x00 | (width & 0x0F);
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ respond->value = data;
+ init_completion(&respond->wait_completion);
+ ftdi->command_next += 1;
+ ftdi->respond_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ wait_for_completion(&respond->wait_completion);
+ return result;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_read_pcimem(struct platform_device *pdev, int mem_offset,
+ u8 width, u32 *data)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else
+ return ftdi_elan_read_pcimem(ftdi, mem_offset, width, data);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_read_pcimem);
+static int ftdi_elan_edset_setup(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x80 | (ed << 5);
+ command->length = 0x8007;
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 8;
+ command->value = 0;
+ command->buffer = urb->setup_packet;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_setup(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_setup);
+static int ftdi_elan_edset_input(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ int remaining_length = urb->transfer_buffer_length -
+ urb->actual_length;
+ command->header = 0x82 | (ed << 5);
+ if (remaining_length == 0) {
+ command->length = 0x0000;
+ } else if (remaining_length > 1024) {
+ command->length = 0x8000 | 1023;
+ } else
+ command->length = 0x8000 | (remaining_length -
+ 1);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_input(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_input);
+static int ftdi_elan_edset_empty(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x81 | (ed << 5);
+ command->length = 0x0000;
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_empty(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_empty);
+static int ftdi_elan_edset_output(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ u8 *b;
+ u16 urb_size;
+ int i = 0;
+ char data[30 *3 + 4];
+ char *d = data;
+ int m = (sizeof(data) - 1) / 3;
+ int l = 0;
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x81 | (ed << 5);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = min(1024,
+ urb->transfer_buffer_length -
+ urb->actual_length);
+ command->value = 0;
+ command->buffer = urb->transfer_buffer +
+ urb->actual_length;
+ command->length = 0x8000 | (command->follows - 1);
+ b = command->buffer;
+ urb_size = command->follows;
+ data[0] = 0;
+ while (urb_size-- > 0) {
+ if (i > m) {
+ } else if (i++ < m) {
+ int w = sprintf(d, " %02X", *b++);
+ d += w;
+ l += w;
+ } else
+ d += sprintf(d, " ..");
+ }
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_output(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_output);
+static int ftdi_elan_edset_single(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ u8 ed = ed_number - 1;
+ wait:if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ int command_size;
+ down(&ftdi->u132_lock);
+ command_size = ftdi->command_next - ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ int remaining_length = urb->transfer_buffer_length -
+ urb->actual_length;
+ struct u132_target *target = &ftdi->target[ed];
+ struct u132_command *command = &ftdi->command[
+ COMMAND_MASK & ftdi->command_next];
+ command->header = 0x83 | (ed << 5);
+ if (remaining_length == 0) {
+ command->length = 0x0000;
+ } else if (remaining_length > 1024) {
+ command->length = 0x8000 | 1023;
+ } else
+ command->length = 0x8000 | (remaining_length -
+ 1);
+ command->address = (toggle_bits << 6) | (ep_number << 2)
+ | (address << 0);
+ command->width = usb_maxpacket(urb->dev, urb->pipe,
+ usb_pipeout(urb->pipe));
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = NULL;
+ target->callback = callback;
+ target->endp = endp;
+ target->urb = urb;
+ target->active = 1;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ goto wait;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null))
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_single(ftdi, ed_number, endp, urb, address,
+ ep_number, toggle_bits, callback);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_single);
+static int ftdi_elan_edset_flush(struct usb_ftdi *ftdi, u8 ed_number,
+ void *endp)
+{
+ u8 ed = ed_number - 1;
+ if (ftdi->disconnected > 0) {
+ return -ENODEV;
+ } else if (ftdi->initialized == 0) {
+ return -ENODEV;
+ } else {
+ struct u132_target *target = &ftdi->target[ed];
+ down(&ftdi->u132_lock);
+ if (target->abandoning > 0) {
+ up(&ftdi->u132_lock);
+ return 0;
+ } else {
+ target->abandoning = 1;
+ wait_1:if (target->active == 1) {
+ int command_size = ftdi->command_next -
+ ftdi->command_head;
+ if (command_size < COMMAND_SIZE) {
+ struct u132_command *command =
+ &ftdi->command[COMMAND_MASK &
+ ftdi->command_next];
+ command->header = 0x80 | (ed << 5) |
+ 0x4;
+ command->length = 0x00;
+ command->address = 0x00;
+ command->width = 0x00;
+ command->follows = 0;
+ command->value = 0;
+ command->buffer = &command->value;
+ ftdi->command_next += 1;
+ ftdi_elan_kick_command_queue(ftdi);
+ } else {
+ up(&ftdi->u132_lock);
+ msleep(100);
+ down(&ftdi->u132_lock);
+ goto wait_1;
+ }
+ }
+ up(&ftdi->u132_lock);
+ return 0;
+ }
+ }
+}
+
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+ void *endp)
+{
+ struct usb_ftdi *ftdi = platform_device_to_usb_ftdi(pdev);
+ return ftdi_elan_edset_flush(ftdi, ed_number, endp);
+}
+
+
+EXPORT_SYMBOL_GPL(usb_ftdi_elan_edset_flush);
+static int ftdi_elan_flush_input_fifo(struct usb_ftdi *ftdi)
+{
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ int retry_on_status = 20;
+ more:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(100));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ char c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X",
+ 0x000000FF & c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ goto more;
+ } else if (packet_bytes > 1) {
+ char s1 = ftdi->bulk_in_buffer[0];
+ char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x60) {
+ return 0;
+ } else if (retry_on_status-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (packet_bytes > 0) {
+ char b1 = ftdi->bulk_in_buffer[0];
+ dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+ "TDI = %02X\n", b1);
+ if (retry_on_status-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+ "t reached\n");
+ return -ENOMEM;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet retry l"
+ "imit reached\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+ return retval;
+ }
+ }
+ return -1;
+}
+
+
+/*
+* send the long flush sequence
+*
+*/
+static int ftdi_elan_synchronize_flush(struct usb_ftdi *ftdi)
+{
+ int retval;
+ struct urb *urb;
+ char *buf;
+ int I = 257;
+ int i = 0;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not alloc a urb for flush sequ"
+ "ence\n");
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer for flush seq"
+ "uence\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ while (I-- > 0)
+ buf[i++] = 0x55;
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, i,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+ "flush sequence\n");
+ usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ usb_free_urb(urb);
+ return 0;
+}
+
+
+/*
+* send the reset sequence
+*
+*/
+static int ftdi_elan_synchronize_reset(struct usb_ftdi *ftdi)
+{
+ int retval;
+ struct urb *urb;
+ char *buf;
+ int I = 4;
+ int i = 0;
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&ftdi->udev->dev, "could not get a urb for the reset se"
+ "quence\n");
+ return -ENOMEM;
+ }
+ buf = usb_buffer_alloc(ftdi->udev, I, GFP_KERNEL, &urb->transfer_dma);
+ if (!buf) {
+ dev_err(&ftdi->udev->dev, "could not get a buffer for the reset"
+ " sequence\n");
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ buf[i++] = 0x55;
+ buf[i++] = 0xAA;
+ buf[i++] = 0x5A;
+ buf[i++] = 0xA5;
+ usb_fill_bulk_urb(urb, ftdi->udev, usb_sndbulkpipe(ftdi->udev,
+ ftdi->bulk_out_endpointAddr), buf, i,
+ ftdi_elan_write_bulk_callback, ftdi);
+ urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+ retval = usb_submit_urb(urb, GFP_KERNEL);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "failed to submit urb containing the "
+ "reset sequence\n");
+ usb_buffer_free(ftdi->udev, i, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ return -ENOMEM;
+ }
+ usb_free_urb(urb);
+ return 0;
+}
+
+static int ftdi_elan_synchronize(struct usb_ftdi *ftdi)
+{
+ int retval;
+ int long_stop = 10;
+ int retry_on_timeout = 5;
+ int retry_on_empty = 10;
+ int err_count = 0;
+ retval = ftdi_elan_flush_input_fifo(ftdi);
+ if (retval)
+ return retval;
+ ftdi->bulk_in_left = 0;
+ ftdi->bulk_in_last = -1;
+ while (long_stop-- > 0) {
+ int read_stop;
+ int read_stuck;
+ retval = ftdi_elan_synchronize_flush(ftdi);
+ if (retval)
+ return retval;
+ retval = ftdi_elan_flush_input_fifo(ftdi);
+ if (retval)
+ return retval;
+ reset:retval = ftdi_elan_synchronize_reset(ftdi);
+ if (retval)
+ return retval;
+ read_stop = 100;
+ read_stuck = 10;
+ read:{
+ int packet_bytes = 0;
+ retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev,
+ ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(500));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ unsigned char c = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X", c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ if (c == 0x7E) {
+ return 0;
+ } else {
+ if (c == 0x55) {
+ goto read;
+ } else if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ }
+ } else if (packet_bytes > 1) {
+ unsigned char s1 = ftdi->bulk_in_buffer[0];
+ unsigned char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x00) {
+ if (read_stuck-- > 0) {
+ goto read;
+ } else
+ goto reset;
+ } else if (s1 == 0x31 && s2 == 0x60) {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ } else {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retr"
+ "y limit reached\n");
+ continue;
+ }
+ }
+ } else if (packet_bytes > 0) {
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retry limit "
+ "reached\n");
+ continue;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT re"
+ "try limit reached\n");
+ continue;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet"
+ " retry limit reached\n");
+ continue;
+ }
+ } else {
+ err_count += 1;
+ dev_err(&ftdi->udev->dev, "error = %d\n",
+ retval);
+ if (read_stop-- > 0) {
+ goto read;
+ } else {
+ dev_err(&ftdi->udev->dev, "retry limit "
+ "reached\n");
+ continue;
+ }
+ }
+ }
+ }
+ dev_err(&ftdi->udev->dev, "failed to synchronize\n");
+ return -EFAULT;
+}
+
+static int ftdi_elan_stuck_waiting(struct usb_ftdi *ftdi)
+{
+ int retry_on_empty = 10;
+ int retry_on_timeout = 5;
+ int retry_on_status = 50;
+ more:{
+ int packet_bytes = 0;
+ int retval = usb_bulk_msg(ftdi->udev,
+ usb_rcvbulkpipe(ftdi->udev, ftdi->bulk_in_endpointAddr),
+ ftdi->bulk_in_buffer, ftdi->bulk_in_size,
+ &packet_bytes, msecs_to_jiffies(1000));
+ if (packet_bytes > 2) {
+ char diag[30 *3 + 4];
+ char *d = diag;
+ int m = (sizeof(diag) - 1) / 3;
+ char *b = ftdi->bulk_in_buffer;
+ int bytes_read = 0;
+ diag[0] = 0;
+ while (packet_bytes-- > 0) {
+ char c = *b++;
+ if (bytes_read < m) {
+ d += sprintf(d, " %02X",
+ 0x000000FF & c);
+ } else if (bytes_read > m) {
+ } else
+ d += sprintf(d, " ..");
+ bytes_read += 1;
+ continue;
+ }
+ goto more;
+ } else if (packet_bytes > 1) {
+ char s1 = ftdi->bulk_in_buffer[0];
+ char s2 = ftdi->bulk_in_buffer[1];
+ if (s1 == 0x31 && s2 == 0x60) {
+ return 0;
+ } else if (retry_on_status-- > 0) {
+ msleep(5);
+ goto more;
+ } else
+ return -EFAULT;
+ } else if (packet_bytes > 0) {
+ char b1 = ftdi->bulk_in_buffer[0];
+ dev_err(&ftdi->udev->dev, "only one byte flushed from F"
+ "TDI = %02X\n", b1);
+ if (retry_on_status-- > 0) {
+ msleep(5);
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "STATUS ERROR retry l"
+ "imit reached\n");
+ return -EFAULT;
+ }
+ } else if (retval == -ETIMEDOUT) {
+ if (retry_on_timeout-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "TIMED OUT retry limi"
+ "t reached\n");
+ return -ENOMEM;
+ }
+ } else if (retval == 0) {
+ if (retry_on_empty-- > 0) {
+ goto more;
+ } else {
+ dev_err(&ftdi->udev->dev, "empty packet retry l"
+ "imit reached\n");
+ return -ENOMEM;
+ }
+ } else {
+ dev_err(&ftdi->udev->dev, "error = %d\n", retval);
+ return -ENOMEM;
+ }
+ }
+ return -1;
+}
+
+static int ftdi_elan_checkingPCI(struct usb_ftdi *ftdi)
+{
+ int UxxxStatus = ftdi_elan_read_reg(ftdi, &ftdi->controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ if (ftdi->controlreg & 0x00400000) {
+ if (ftdi->card_ejected) {
+ } else {
+ ftdi->card_ejected = 1;
+ dev_err(&ftdi->udev->dev, "CARD EJECTED - controlreg = "
+ "%08X\n", ftdi->controlreg);
+ }
+ return -ENODEV;
+ } else {
+ u8 fn = ftdi->function - 1;
+ int activePCIfn = fn << 8;
+ u32 pcidata;
+ u32 pciVID;
+ u32 pciPID;
+ int reg = 0;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ pciVID = pcidata & 0xFFFF;
+ pciPID = (pcidata >> 16) & 0xFFFF;
+ if (pciVID == ftdi->platform_data.vendor && pciPID ==
+ ftdi->platform_data.device) {
+ return 0;
+ } else {
+ dev_err(&ftdi->udev->dev, "vendor=%04X pciVID=%04X devi"
+ "ce=%04X pciPID=%04X\n",
+ ftdi->platform_data.vendor, pciVID,
+ ftdi->platform_data.device, pciPID);
+ return -ENODEV;
+ }
+ }
+}
+
+static int ftdi_elan_enumeratePCI(struct usb_ftdi *ftdi)
+{
+ u32 latence_timer;
+ u32 controlreg;
+ int UxxxStatus;
+ u32 pcidata;
+ int reg = 0;
+ int foundOHCI = 0;
+ u8 fn;
+ int activePCIfn = 0;
+ u32 pciVID = 0;
+ u32 pciPID = 0;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000000L);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(750);
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x100);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x00000200L | 0x500);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020CL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020DL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(250);
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000020FL | 0x000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x800);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_reg(ftdi, &controlreg);
+ if (UxxxStatus)
+ return UxxxStatus;
+ msleep(1000);
+ for (fn = 0; (fn < 4) && (!foundOHCI); fn++) {
+ activePCIfn = fn << 8;
+ ftdi->function = fn + 1;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ pciVID = pcidata & 0xFFFF;
+ pciPID = (pcidata >> 16) & 0xFFFF;
+ if ((pciVID == 0x1045) && (pciPID == 0xc861)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x1033) && (pciPID == 0x0035)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x10b9) && (pciPID == 0x5237)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x11c1) && (pciPID == 0x5802)) {
+ foundOHCI = 1;
+ } else if ((pciVID == 0x11AB) && (pciPID == 0x1FA6)) {
+ }
+ }
+ if (foundOHCI == 0) {
+ return -ENXIO;
+ }
+ ftdi->platform_data.vendor = pciVID;
+ ftdi->platform_data.device = pciPID;
+ UxxxStatus = ftdi_elan_write_reg(ftdi, 0x0000025FL | 0x2800);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 16;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+ 0xFFFFFFFF);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0,
+ 0xF0000000);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 12;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &latence_timer);
+ if (UxxxStatus)
+ return UxxxStatus;
+ latence_timer &= 0xFFFF00FF;
+ latence_timer |= 0x00001600;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+ latence_timer);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ reg = 4;
+ UxxxStatus = ftdi_elan_write_config(ftdi, activePCIfn | reg, 0x00,
+ 0x06);
+ if (UxxxStatus)
+ return UxxxStatus;
+ UxxxStatus = ftdi_elan_read_config(ftdi, activePCIfn | reg, 0,
+ &pcidata);
+ if (UxxxStatus)
+ return UxxxStatus;
+ return 0;
+}
+
+static int ftdi_elan_setupOHCI(struct usb_ftdi *ftdi)
+{
+ u32 pcidata;
+ int U132Status;
+ int reg;
+ int reset_repeat = 0;
+ do_reset:reg = 8;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x01);
+ if (U132Status)
+ return U132Status;
+ reset_check:{
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ if (pcidata & 1) {
+ msleep(500);
+ if (reset_repeat++ > 100) {
+ reset_repeat = 0;
+ goto do_reset;
+ } else
+ goto reset_check;
+ }
+ }
+ goto dump_regs;
+ msleep(500);
+ reg = 0x28;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x11000000);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x40;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x34;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x2edf2edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 4;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0xA0);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(250);
+ reg = 8;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x0e, 0x04);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x28;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 8;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x48;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x00001200);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x34;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x28002edf);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(100);
+ reg = 0x50;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10000);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ power_check:U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ if (!(pcidata & 1)) {
+ msleep(500);
+ goto power_check;
+ }
+ msleep(3000);
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x54;
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x10);
+ if (U132Status)
+ return U132Status;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ msleep(750);
+ reg = 0x54;
+ if (0) {
+ U132Status = ftdi_elan_write_pcimem(ftdi, reg, 0x00, 0x02);
+ if (U132Status)
+ return U132Status;
+ }
+ if (0) {
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ }
+ reg = 0x54;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ reg = 0x58;
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ dump_regs:for (reg = 0; reg <= 0x54; reg += 4) {
+ U132Status = ftdi_elan_read_pcimem(ftdi, reg, 0, &pcidata);
+ if (U132Status)
+ return U132Status;
+ }
+ return 0;
+}
+
+
+/*
+* we use only the first bulk-in and bulk-out endpoints
+*/
+static int ftdi_elan_probe(struct usb_interface *interface,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *iface_desc;
+ struct usb_endpoint_descriptor *endpoint;
+ size_t buffer_size;
+ int i;
+ int retval = -ENOMEM;
+ struct usb_ftdi *ftdi = kmalloc(sizeof(struct usb_ftdi), GFP_KERNEL);
+ if (ftdi == NULL) {
+ printk(KERN_ERR "Out of memory\n");
+ return -ENOMEM;
+ }
+ memset(ftdi, 0x00, sizeof(struct usb_ftdi));
+ down(&ftdi_module_lock);
+ list_add_tail(&ftdi->ftdi_list, &ftdi_static_list);
+ ftdi->sequence_num = ++ftdi_instances;
+ up(&ftdi_module_lock);
+ ftdi_elan_init_kref(ftdi);
+ init_MUTEX(&ftdi->sw_lock);
+ ftdi->udev = usb_get_dev(interface_to_usbdev(interface));
+ ftdi->interface = interface;
+ init_MUTEX(&ftdi->u132_lock);
+ ftdi->expected = 4;
+ iface_desc = interface->cur_altsetting;
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (!ftdi->bulk_in_endpointAddr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_IN) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+ {
+ buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
+ ftdi->bulk_in_size = buffer_size;
+ ftdi->bulk_in_endpointAddr = endpoint->bEndpointAddress;
+ ftdi->bulk_in_buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!ftdi->bulk_in_buffer) {
+ dev_err(&ftdi->udev->dev, "Could not allocate b"
+ "ulk_in_buffer\n");
+ retval = -ENOMEM;
+ goto error;
+ }
+ }
+ if (!ftdi->bulk_out_endpointAddr &&
+ ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+ == USB_DIR_OUT) && ((endpoint->bmAttributes &
+ USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_BULK))
+ {
+ ftdi->bulk_out_endpointAddr =
+ endpoint->bEndpointAddress;
+ }
+ }
+ if (!(ftdi->bulk_in_endpointAddr && ftdi->bulk_out_endpointAddr)) {
+ dev_err(&ftdi->udev->dev, "Could not find both bulk-in and bulk"
+ "-out endpoints\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ dev_info(&ftdi->udev->dev, "interface %d has I=%02X O=%02X\n",
+ iface_desc->desc.bInterfaceNumber, ftdi->bulk_in_endpointAddr,
+ ftdi->bulk_out_endpointAddr);
+ usb_set_intfdata(interface, ftdi);
+ if (iface_desc->desc.bInterfaceNumber == 0 &&
+ ftdi->bulk_in_endpointAddr == 0x81 &&
+ ftdi->bulk_out_endpointAddr == 0x02) {
+ retval = usb_register_dev(interface, &ftdi_elan_jtag_class);
+ if (retval) {
+ dev_err(&ftdi->udev->dev, "Not able to get a minor for "
+ "this device.\n");
+ usb_set_intfdata(interface, NULL);
+ retval = -ENOMEM;
+ goto error;
+ } else {
+ ftdi->class = &ftdi_elan_jtag_class;
+ dev_info(&ftdi->udev->dev, "USB FDTI=%p JTAG interface "
+ "%d now attached to ftdi%d\n", ftdi,
+ iface_desc->desc.bInterfaceNumber,
+ interface->minor);
+ return 0;
+ }
+ } else if (iface_desc->desc.bInterfaceNumber == 1 &&
+ ftdi->bulk_in_endpointAddr == 0x83 &&
+ ftdi->bulk_out_endpointAddr == 0x04) {
+ ftdi->class = NULL;
+ dev_info(&ftdi->udev->dev, "USB FDTI=%p ELAN interface %d now a"
+ "ctivated\n", ftdi, iface_desc->desc.bInterfaceNumber);
+ INIT_WORK(&ftdi->status_work, ftdi_elan_status_work,
+ (void *)ftdi);
+ INIT_WORK(&ftdi->command_work, ftdi_elan_command_work,
+ (void *)ftdi);
+ INIT_WORK(&ftdi->respond_work, ftdi_elan_respond_work,
+ (void *)ftdi);
+ ftdi_status_queue_work(ftdi, msecs_to_jiffies(3 *1000));
+ return 0;
+ } else {
+ dev_err(&ftdi->udev->dev,
+ "Could not find ELAN's U132 device\n");
+ retval = -ENODEV;
+ goto error;
+ }
+ error:if (ftdi) {
+ ftdi_elan_put_kref(ftdi);
+ }
+ return retval;
+}
+
+static void ftdi_elan_disconnect(struct usb_interface *interface)
+{
+ struct usb_ftdi *ftdi = usb_get_intfdata(interface);
+ ftdi->disconnected += 1;
+ if (ftdi->class) {
+ int minor = interface->minor;
+ struct usb_class_driver *class = ftdi->class;
+ usb_set_intfdata(interface, NULL);
+ usb_deregister_dev(interface, class);
+ dev_info(&ftdi->udev->dev, "USB FTDI U132 jtag interface on min"
+ "or %d now disconnected\n", minor);
+ } else {
+ ftdi_status_cancel_work(ftdi);
+ ftdi_command_cancel_work(ftdi);
+ ftdi_response_cancel_work(ftdi);
+ ftdi_elan_abandon_completions(ftdi);
+ ftdi_elan_abandon_targets(ftdi);
+ if (ftdi->registered) {
+ platform_device_unregister(&ftdi->platform_dev);
+ ftdi->synchronized = 0;
+ ftdi->enumerated = 0;
+ ftdi->registered = 0;
+ }
+ flush_workqueue(status_queue);
+ flush_workqueue(command_queue);
+ flush_workqueue(respond_queue);
+ ftdi->disconnected += 1;
+ usb_set_intfdata(interface, NULL);
+ dev_info(&ftdi->udev->dev, "USB FTDI U132 host controller inter"
+ "face now disconnected\n");
+ }
+ ftdi_elan_put_kref(ftdi);
+}
+
+static struct usb_driver ftdi_elan_driver = {
+ .name = "ftdi-elan",
+ .probe = ftdi_elan_probe,
+ .disconnect = ftdi_elan_disconnect,
+ .id_table = ftdi_elan_table,
+};
+static int __init ftdi_elan_init(void)
+{
+ int result;
+ printk(KERN_INFO "driver %s built at %s on %s\n", ftdi_elan_driver.name,
+ __TIME__, __DATE__);
+ init_MUTEX(&ftdi_module_lock);
+ INIT_LIST_HEAD(&ftdi_static_list);
+ status_queue = create_singlethread_workqueue("ftdi-status-control");
+ command_queue = create_singlethread_workqueue("ftdi-command-engine");
+ respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+ result = usb_register(&ftdi_elan_driver);
+ if (result)
+ printk(KERN_ERR "usb_register failed. Error number %d\n",
+ result);
+ return result;
+}
+
+static void __exit ftdi_elan_exit(void)
+{
+ struct usb_ftdi *ftdi;
+ struct usb_ftdi *temp;
+ usb_deregister(&ftdi_elan_driver);
+ printk(KERN_INFO "ftdi_u132 driver deregistered\n");
+ list_for_each_entry_safe(ftdi, temp, &ftdi_static_list, ftdi_list) {
+ ftdi_status_cancel_work(ftdi);
+ ftdi_command_cancel_work(ftdi);
+ ftdi_response_cancel_work(ftdi);
+ } flush_workqueue(status_queue);
+ destroy_workqueue(status_queue);
+ status_queue = NULL;
+ flush_workqueue(command_queue);
+ destroy_workqueue(command_queue);
+ command_queue = NULL;
+ flush_workqueue(respond_queue);
+ destroy_workqueue(respond_queue);
+ respond_queue = NULL;
+}
+
+
+module_init(ftdi_elan_init);
+module_exit(ftdi_elan_exit);
diff --git a/drivers/usb/misc/idmouse.c b/drivers/usb/misc/idmouse.c
index fcd69c52aea..8e6e195a22b 100644
--- a/drivers/usb/misc/idmouse.c
+++ b/drivers/usb/misc/idmouse.c
@@ -98,7 +98,7 @@ static int idmouse_probe(struct usb_interface *interface,
static void idmouse_disconnect(struct usb_interface *interface);
/* file operation pointers */
-static struct file_operations idmouse_fops = {
+static const struct file_operations idmouse_fops = {
.owner = THIS_MODULE,
.read = idmouse_read,
.open = idmouse_open,
diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c
index f30ab1fbb3c..10b640339d8 100644
--- a/drivers/usb/misc/ldusb.c
+++ b/drivers/usb/misc/ldusb.c
@@ -589,7 +589,7 @@ exit:
}
/* file operations needed when we register this driver */
-static struct file_operations ld_usb_fops = {
+static const struct file_operations ld_usb_fops = {
.owner = THIS_MODULE,
.read = ld_usb_read,
.write = ld_usb_write,
@@ -657,15 +657,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id *
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_in(endpoint))
dev->interrupt_in_endpoint = endpoint;
- }
- if (((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT)) {
+ if (usb_endpoint_is_int_out(endpoint))
dev->interrupt_out_endpoint = endpoint;
- }
}
if (dev->interrupt_in_endpoint == NULL) {
dev_err(&intf->dev, "Interrupt in endpoint not found\n");
diff --git a/drivers/usb/misc/legousbtower.c b/drivers/usb/misc/legousbtower.c
index 7699d970e68..77c36e63c7b 100644
--- a/drivers/usb/misc/legousbtower.c
+++ b/drivers/usb/misc/legousbtower.c
@@ -259,7 +259,7 @@ static void tower_disconnect (struct usb_interface *interface);
static DEFINE_MUTEX (disconnect_mutex);
/* file operations needed when we register this driver */
-static struct file_operations tower_fops = {
+static const struct file_operations tower_fops = {
.owner = THIS_MODULE,
.read = tower_read,
.write = tower_write,
diff --git a/drivers/usb/misc/phidget.c b/drivers/usb/misc/phidget.c
new file mode 100644
index 00000000000..735ed33f4f7
--- /dev/null
+++ b/drivers/usb/misc/phidget.c
@@ -0,0 +1,43 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * 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/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/device.h>
+
+struct class *phidget_class;
+
+static int __init init_phidget(void)
+{
+ phidget_class = class_create(THIS_MODULE, "phidget");
+
+ if (IS_ERR(phidget_class))
+ return PTR_ERR(phidget_class);
+
+ return 0;
+}
+
+static void __exit cleanup_phidget(void)
+{
+ class_destroy(phidget_class);
+}
+
+EXPORT_SYMBOL_GPL(phidget_class);
+
+module_init(init_phidget);
+module_exit(cleanup_phidget);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("Container module for phidget class");
+
diff --git a/drivers/usb/misc/phidget.h b/drivers/usb/misc/phidget.h
new file mode 100644
index 00000000000..c4011907d43
--- /dev/null
+++ b/drivers/usb/misc/phidget.h
@@ -0,0 +1,12 @@
+/*
+ * USB Phidgets class
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * 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.
+ */
+
+extern struct class *phidget_class;
diff --git a/drivers/usb/misc/phidgetkit.c b/drivers/usb/misc/phidgetkit.c
index bfbbbfbb92b..78e419904ab 100644
--- a/drivers/usb/misc/phidgetkit.c
+++ b/drivers/usb/misc/phidgetkit.c
@@ -20,6 +20,8 @@
#include <linux/module.h>
#include <linux/usb.h>
+#include "phidget.h"
+
#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
#define DRIVER_DESC "USB PhidgetInterfaceKit Driver"
@@ -42,26 +44,35 @@ struct driver_interfacekit {
int inputs;
int outputs;
int has_lcd;
+ int amnesiac;
};
-#define ifkit(_sensors, _inputs, _outputs, _lcd) \
-static struct driver_interfacekit ph_##_sensors##_inputs##_outputs = { \
+
+#define ifkit(_sensors, _inputs, _outputs, _lcd, _amnesiac) \
+{ \
.sensors = _sensors, \
.inputs = _inputs, \
.outputs = _outputs, \
.has_lcd = _lcd, \
+ .amnesiac = _amnesiac \
};
-ifkit(0, 0, 4, 0);
-ifkit(8, 8, 8, 0);
-ifkit(0, 4, 7, 1);
-ifkit(8, 8, 4, 0);
-ifkit(0, 8, 8, 1);
-ifkit(0, 16, 16, 0);
+
+static const struct driver_interfacekit ph_004 = ifkit(0, 0, 4, 0, 0);
+static const struct driver_interfacekit ph_888n = ifkit(8, 8, 8, 0, 1);
+static const struct driver_interfacekit ph_888o = ifkit(8, 8, 8, 0, 0);
+static const struct driver_interfacekit ph_047 = ifkit(0, 4, 7, 1, 0);
+static const struct driver_interfacekit ph_884 = ifkit(8, 8, 4, 0, 0);
+static const struct driver_interfacekit ph_088 = ifkit(0, 8, 8, 1, 0);
+static const struct driver_interfacekit ph_01616 = ifkit(0, 16, 16, 0, 0);
+
+static unsigned long device_no;
struct interfacekit {
struct usb_device *udev;
struct usb_interface *intf;
struct driver_interfacekit *ifkit;
+ struct device *dev;
unsigned long outputs;
+ int dev_no;
u8 inputs[MAX_INTERFACES];
u16 sensors[MAX_INTERFACES];
u8 lcd_files_on;
@@ -71,6 +82,7 @@ struct interfacekit {
dma_addr_t data_dma;
struct work_struct do_notify;
+ struct work_struct do_resubmit;
unsigned long input_events;
unsigned long sensor_events;
};
@@ -78,8 +90,10 @@ struct interfacekit {
static struct usb_device_id id_table[] = {
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT004),
.driver_info = (kernel_ulong_t)&ph_004},
- {USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888),
- .driver_info = (kernel_ulong_t)&ph_888},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0, 0x814),
+ .driver_info = (kernel_ulong_t)&ph_888o},
+ {USB_DEVICE_VER(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT888, 0x0815, 0xffff),
+ .driver_info = (kernel_ulong_t)&ph_888n},
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT047),
.driver_info = (kernel_ulong_t)&ph_047},
{USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_INTERFACEKIT088),
@@ -92,16 +106,11 @@ static struct usb_device_id id_table[] = {
};
MODULE_DEVICE_TABLE(usb, id_table);
-static int change_outputs(struct interfacekit *kit, int output_num, int enable)
+static int set_outputs(struct interfacekit *kit)
{
u8 *buffer;
int retval;
- if (enable)
- set_bit(output_num, &kit->outputs);
- else
- clear_bit(output_num, &kit->outputs);
-
buffer = kzalloc(4, GFP_KERNEL);
if (!buffer) {
dev_err(&kit->udev->dev, "%s - out of memory\n", __FUNCTION__);
@@ -121,6 +130,9 @@ static int change_outputs(struct interfacekit *kit, int output_num, int enable)
retval);
kfree(buffer);
+ if (kit->ifkit->amnesiac)
+ schedule_delayed_work(&kit->do_resubmit, HZ / 2);
+
return retval < 0 ? retval : 0;
}
@@ -180,21 +192,24 @@ exit:
}
#define set_lcd_line(number) \
-static ssize_t lcd_line_##number(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) \
-{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
- change_string(kit, buf, number - 1); \
- return count; \
-} \
-static DEVICE_ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number);
+static ssize_t lcd_line_##number(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ change_string(kit, buf, number - 1); \
+ return count; \
+}
+
+#define lcd_line_attr(number) \
+ __ATTR(lcd_line_##number, S_IWUGO, NULL, lcd_line_##number)
+
set_lcd_line(1);
set_lcd_line(2);
static ssize_t set_backlight(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
- struct usb_interface *intf = to_usb_interface(dev);
- struct interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = dev_get_drvdata(dev);
int enabled;
unsigned char *buffer;
int retval = -ENOMEM;
@@ -226,23 +241,30 @@ exit:
kfree(buffer);
return retval;
}
-static DEVICE_ATTR(backlight, S_IWUGO, NULL, set_backlight);
+
+static struct device_attribute dev_lcd_line_attrs[] = {
+ lcd_line_attr(1),
+ lcd_line_attr(2),
+ __ATTR(backlight, S_IWUGO, NULL, set_backlight)
+};
static void remove_lcd_files(struct interfacekit *kit)
{
+ int i;
+
if (kit->lcd_files_on) {
dev_dbg(&kit->udev->dev, "Removing lcd files\n");
- device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_1);
- device_remove_file(&kit->intf->dev, &dev_attr_lcd_line_2);
- device_remove_file(&kit->intf->dev, &dev_attr_backlight);
+
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
}
}
static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
- struct usb_interface *intf = to_usb_interface(dev);
- struct interfacekit *kit = usb_get_intfdata(intf);
+ struct interfacekit *kit = dev_get_drvdata(dev);
int enable;
+ int i, rc;
if (kit->ifkit->has_lcd == 0)
return -ENODEV;
@@ -253,9 +275,12 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
if (enable) {
if (!kit->lcd_files_on) {
dev_dbg(&kit->udev->dev, "Adding lcd files\n");
- device_create_file(&kit->intf->dev, &dev_attr_lcd_line_1);
- device_create_file(&kit->intf->dev, &dev_attr_lcd_line_2);
- device_create_file(&kit->intf->dev, &dev_attr_backlight);
+ for (i=0; i<ARRAY_SIZE(dev_lcd_line_attrs); i++) {
+ rc = device_create_file(kit->dev,
+ &dev_lcd_line_attrs[i]);
+ if (rc)
+ goto out;
+ }
kit->lcd_files_on = 1;
}
} else {
@@ -266,7 +291,13 @@ static ssize_t enable_lcd_files(struct device *dev, struct device_attribute *att
}
return count;
+out:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_lcd_line_attrs[i]);
+
+ return rc;
}
+
static DEVICE_ATTR(lcd, S_IWUGO, NULL, enable_lcd_files);
static void interfacekit_irq(struct urb *urb, struct pt_regs *regs)
@@ -362,44 +393,58 @@ static void do_notify(void *data)
for (i=0; i<kit->ifkit->inputs; i++) {
if (test_and_clear_bit(i, &kit->input_events)) {
sprintf(sysfs_file, "input%d", i + 1);
- sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
}
}
for (i=0; i<kit->ifkit->sensors; i++) {
if (test_and_clear_bit(i, &kit->sensor_events)) {
sprintf(sysfs_file, "sensor%d", i + 1);
- sysfs_notify(&kit->intf->dev.kobj, NULL, sysfs_file);
+ sysfs_notify(&kit->dev->kobj, NULL, sysfs_file);
}
}
}
+static void do_resubmit(void *data)
+{
+ set_outputs(data);
+}
+
#define show_set_output(value) \
-static ssize_t set_output##value(struct device *dev, struct device_attribute *attr, const char *buf, \
- size_t count) \
+static ssize_t set_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
- int enabled; \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
+ int enable; \
int retval; \
\
- if (sscanf(buf, "%d", &enabled) < 1) \
+ if (sscanf(buf, "%d", &enable) < 1) \
return -EINVAL; \
\
- retval = change_outputs(kit, value - 1, enabled); \
+ if (enable) \
+ set_bit(value - 1, &kit->outputs); \
+ else \
+ clear_bit(value - 1, &kit->outputs); \
+ \
+ retval = set_outputs(kit); \
\
return retval ? retval : count; \
} \
\
-static ssize_t show_output##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_output##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", !!test_bit(value - 1, &kit->outputs));\
-} \
-static DEVICE_ATTR(output##value, S_IWUGO | S_IRUGO, \
- show_output##value, set_output##value);
+}
+
+#define output_attr(value) \
+ __ATTR(output##value, S_IWUGO | S_IRUGO, \
+ show_output##value, set_output##value)
+
show_set_output(1);
show_set_output(2);
show_set_output(3);
@@ -417,15 +462,24 @@ show_set_output(14);
show_set_output(15);
show_set_output(16);
+static struct device_attribute dev_output_attrs[] = {
+ output_attr(1), output_attr(2), output_attr(3), output_attr(4),
+ output_attr(5), output_attr(6), output_attr(7), output_attr(8),
+ output_attr(9), output_attr(10), output_attr(11), output_attr(12),
+ output_attr(13), output_attr(14), output_attr(15), output_attr(16)
+};
+
#define show_input(value) \
-static ssize_t show_input##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", (int)kit->inputs[value - 1]); \
-} \
-static DEVICE_ATTR(input##value, S_IRUGO, show_input##value, NULL);
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
show_input(1);
show_input(2);
@@ -444,15 +498,25 @@ show_input(14);
show_input(15);
show_input(16);
+static struct device_attribute dev_input_attrs[] = {
+ input_attr(1), input_attr(2), input_attr(3), input_attr(4),
+ input_attr(5), input_attr(6), input_attr(7), input_attr(8),
+ input_attr(9), input_attr(10), input_attr(11), input_attr(12),
+ input_attr(13), input_attr(14), input_attr(15), input_attr(16)
+};
+
#define show_sensor(value) \
-static ssize_t show_sensor##value(struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_sensor##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface(dev); \
- struct interfacekit *kit = usb_get_intfdata(intf); \
+ struct interfacekit *kit = dev_get_drvdata(dev); \
\
return sprintf(buf, "%d\n", (int)kit->sensors[value - 1]); \
-} \
-static DEVICE_ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL);
+}
+
+#define sensor_attr(value) \
+ __ATTR(sensor##value, S_IRUGO, show_sensor##value, NULL)
show_sensor(1);
show_sensor(2);
@@ -463,6 +527,11 @@ show_sensor(6);
show_sensor(7);
show_sensor(8);
+static struct device_attribute dev_sensor_attrs[] = {
+ sensor_attr(1), sensor_attr(2), sensor_attr(3), sensor_attr(4),
+ sensor_attr(5), sensor_attr(6), sensor_attr(7), sensor_attr(8)
+};
+
static int interfacekit_probe(struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
@@ -471,6 +540,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
struct interfacekit *kit;
struct driver_interfacekit *ifkit;
int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
ifkit = (struct driver_interfacekit *)id->driver_info;
if (!ifkit)
@@ -493,6 +563,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
if (!kit)
goto out;
+ kit->dev_no = -1;
kit->ifkit = ifkit;
kit->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &kit->data_dma);
if (!kit->data)
@@ -505,6 +576,7 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
kit->udev = usb_get_dev(dev);
kit->intf = intf;
INIT_WORK(&kit->do_notify, do_notify, kit);
+ INIT_WORK(&kit->do_resubmit, do_resubmit, kit);
usb_fill_int_urb(kit->irq, kit->udev, pipe, kit->data,
maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
interfacekit_irq, kit, endpoint->bInterval);
@@ -513,85 +585,80 @@ static int interfacekit_probe(struct usb_interface *intf, const struct usb_devic
usb_set_intfdata(intf, kit);
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ kit->dev_no = bit;
+
+ kit->dev = device_create(phidget_class, &kit->udev->dev, 0,
+ "interfacekit%d", kit->dev_no);
+ if (IS_ERR(kit->dev)) {
+ rc = PTR_ERR(kit->dev);
+ kit->dev = NULL;
+ goto out;
+ }
+ dev_set_drvdata(kit->dev, kit);
+
if (usb_submit_urb(kit->irq, GFP_KERNEL)) {
rc = -EIO;
goto out;
}
- if (ifkit->outputs >= 4) {
- device_create_file(&intf->dev, &dev_attr_output1);
- device_create_file(&intf->dev, &dev_attr_output2);
- device_create_file(&intf->dev, &dev_attr_output3);
- device_create_file(&intf->dev, &dev_attr_output4);
- }
- if (ifkit->outputs >= 8) {
- device_create_file(&intf->dev, &dev_attr_output5);
- device_create_file(&intf->dev, &dev_attr_output6);
- device_create_file(&intf->dev, &dev_attr_output7);
- device_create_file(&intf->dev, &dev_attr_output8);
- }
- if (ifkit->outputs == 16) {
- device_create_file(&intf->dev, &dev_attr_output9);
- device_create_file(&intf->dev, &dev_attr_output10);
- device_create_file(&intf->dev, &dev_attr_output11);
- device_create_file(&intf->dev, &dev_attr_output12);
- device_create_file(&intf->dev, &dev_attr_output13);
- device_create_file(&intf->dev, &dev_attr_output14);
- device_create_file(&intf->dev, &dev_attr_output15);
- device_create_file(&intf->dev, &dev_attr_output16);
+ for (i=0; i<ifkit->outputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_output_attrs[i]);
+ if (rc)
+ goto out2;
}
- if (ifkit->inputs >= 4) {
- device_create_file(&intf->dev, &dev_attr_input1);
- device_create_file(&intf->dev, &dev_attr_input2);
- device_create_file(&intf->dev, &dev_attr_input3);
- device_create_file(&intf->dev, &dev_attr_input4);
- }
- if (ifkit->inputs >= 8) {
- device_create_file(&intf->dev, &dev_attr_input5);
- device_create_file(&intf->dev, &dev_attr_input6);
- device_create_file(&intf->dev, &dev_attr_input7);
- device_create_file(&intf->dev, &dev_attr_input8);
- }
- if (ifkit->inputs == 16) {
- device_create_file(&intf->dev, &dev_attr_input9);
- device_create_file(&intf->dev, &dev_attr_input10);
- device_create_file(&intf->dev, &dev_attr_input11);
- device_create_file(&intf->dev, &dev_attr_input12);
- device_create_file(&intf->dev, &dev_attr_input13);
- device_create_file(&intf->dev, &dev_attr_input14);
- device_create_file(&intf->dev, &dev_attr_input15);
- device_create_file(&intf->dev, &dev_attr_input16);
+ for (i=0; i<ifkit->inputs; i++ ) {
+ rc = device_create_file(kit->dev, &dev_input_attrs[i]);
+ if (rc)
+ goto out3;
}
- if (ifkit->sensors >= 4) {
- device_create_file(&intf->dev, &dev_attr_sensor1);
- device_create_file(&intf->dev, &dev_attr_sensor2);
- device_create_file(&intf->dev, &dev_attr_sensor3);
- device_create_file(&intf->dev, &dev_attr_sensor4);
+ for (i=0; i<ifkit->sensors; i++ ) {
+ rc = device_create_file(kit->dev, &dev_sensor_attrs[i]);
+ if (rc)
+ goto out4;
}
- if (ifkit->sensors >= 7) {
- device_create_file(&intf->dev, &dev_attr_sensor5);
- device_create_file(&intf->dev, &dev_attr_sensor6);
- device_create_file(&intf->dev, &dev_attr_sensor7);
- }
- if (ifkit->sensors == 8)
- device_create_file(&intf->dev, &dev_attr_sensor8);
- if (ifkit->has_lcd)
- device_create_file(&intf->dev, &dev_attr_lcd);
+ if (ifkit->has_lcd) {
+ rc = device_create_file(kit->dev, &dev_attr_lcd);
+ if (rc)
+ goto out4;
+
+ }
dev_info(&intf->dev, "USB PhidgetInterfaceKit %d/%d/%d attached\n",
ifkit->sensors, ifkit->inputs, ifkit->outputs);
return 0;
+out4:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ i = ifkit->inputs;
+out3:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
+
+ i = ifkit->outputs;
+out2:
+ while (i-- > 0)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
out:
if (kit) {
if (kit->irq)
usb_free_urb(kit->irq);
if (kit->data)
usb_buffer_free(dev, URB_INT_SIZE, kit->data, kit->data_dma);
+ if (kit->dev)
+ device_unregister(kit->dev);
+ if (kit->dev_no >= 0)
+ clear_bit(kit->dev_no, &device_no);
+
kfree(kit);
}
@@ -601,6 +668,7 @@ out:
static void interfacekit_disconnect(struct usb_interface *interface)
{
struct interfacekit *kit;
+ int i;
kit = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
@@ -612,74 +680,30 @@ static void interfacekit_disconnect(struct usb_interface *interface)
usb_buffer_free(kit->udev, URB_INT_SIZE, kit->data, kit->data_dma);
cancel_delayed_work(&kit->do_notify);
+ cancel_delayed_work(&kit->do_resubmit);
- if (kit->ifkit->outputs >= 4) {
- device_remove_file(&interface->dev, &dev_attr_output1);
- device_remove_file(&interface->dev, &dev_attr_output2);
- device_remove_file(&interface->dev, &dev_attr_output3);
- device_remove_file(&interface->dev, &dev_attr_output4);
- }
- if (kit->ifkit->outputs >= 8) {
- device_remove_file(&interface->dev, &dev_attr_output5);
- device_remove_file(&interface->dev, &dev_attr_output6);
- device_remove_file(&interface->dev, &dev_attr_output7);
- device_remove_file(&interface->dev, &dev_attr_output8);
- }
- if (kit->ifkit->outputs == 16) {
- device_remove_file(&interface->dev, &dev_attr_output9);
- device_remove_file(&interface->dev, &dev_attr_output10);
- device_remove_file(&interface->dev, &dev_attr_output11);
- device_remove_file(&interface->dev, &dev_attr_output12);
- device_remove_file(&interface->dev, &dev_attr_output13);
- device_remove_file(&interface->dev, &dev_attr_output14);
- device_remove_file(&interface->dev, &dev_attr_output15);
- device_remove_file(&interface->dev, &dev_attr_output16);
- }
+ for (i=0; i<kit->ifkit->outputs; i++)
+ device_remove_file(kit->dev, &dev_output_attrs[i]);
- if (kit->ifkit->inputs >= 4) {
- device_remove_file(&interface->dev, &dev_attr_input1);
- device_remove_file(&interface->dev, &dev_attr_input2);
- device_remove_file(&interface->dev, &dev_attr_input3);
- device_remove_file(&interface->dev, &dev_attr_input4);
- }
- if (kit->ifkit->inputs >= 8) {
- device_remove_file(&interface->dev, &dev_attr_input5);
- device_remove_file(&interface->dev, &dev_attr_input6);
- device_remove_file(&interface->dev, &dev_attr_input7);
- device_remove_file(&interface->dev, &dev_attr_input8);
- }
- if (kit->ifkit->inputs == 16) {
- device_remove_file(&interface->dev, &dev_attr_input9);
- device_remove_file(&interface->dev, &dev_attr_input10);
- device_remove_file(&interface->dev, &dev_attr_input11);
- device_remove_file(&interface->dev, &dev_attr_input12);
- device_remove_file(&interface->dev, &dev_attr_input13);
- device_remove_file(&interface->dev, &dev_attr_input14);
- device_remove_file(&interface->dev, &dev_attr_input15);
- device_remove_file(&interface->dev, &dev_attr_input16);
- }
+ for (i=0; i<kit->ifkit->inputs; i++)
+ device_remove_file(kit->dev, &dev_input_attrs[i]);
- if (kit->ifkit->sensors >= 4) {
- device_remove_file(&interface->dev, &dev_attr_sensor1);
- device_remove_file(&interface->dev, &dev_attr_sensor2);
- device_remove_file(&interface->dev, &dev_attr_sensor3);
- device_remove_file(&interface->dev, &dev_attr_sensor4);
- }
- if (kit->ifkit->sensors >= 7) {
- device_remove_file(&interface->dev, &dev_attr_sensor5);
- device_remove_file(&interface->dev, &dev_attr_sensor6);
- device_remove_file(&interface->dev, &dev_attr_sensor7);
+ for (i=0; i<kit->ifkit->sensors; i++)
+ device_remove_file(kit->dev, &dev_sensor_attrs[i]);
+
+ if (kit->ifkit->has_lcd) {
+ device_remove_file(kit->dev, &dev_attr_lcd);
+ remove_lcd_files(kit);
}
- if (kit->ifkit->sensors == 8)
- device_remove_file(&interface->dev, &dev_attr_sensor8);
- if (kit->ifkit->has_lcd)
- device_remove_file(&interface->dev, &dev_attr_lcd);
+ device_unregister(kit->dev);
dev_info(&interface->dev, "USB PhidgetInterfaceKit %d/%d/%d detached\n",
kit->ifkit->sensors, kit->ifkit->inputs, kit->ifkit->outputs);
usb_put_dev(kit->udev);
+ clear_bit(kit->dev_no, &device_no);
+
kfree(kit);
}
diff --git a/drivers/usb/misc/phidgetmotorcontrol.c b/drivers/usb/misc/phidgetmotorcontrol.c
new file mode 100644
index 00000000000..6b59b620d61
--- /dev/null
+++ b/drivers/usb/misc/phidgetmotorcontrol.c
@@ -0,0 +1,466 @@
+/*
+ * USB Phidget MotorControl driver
+ *
+ * Copyright (C) 2006 Sean Young <sean@mess.org>
+ *
+ * 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/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+
+#include "phidget.h"
+
+#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
+#define DRIVER_DESC "USB PhidgetMotorControl Driver"
+
+#define USB_VENDOR_ID_GLAB 0x06c2
+#define USB_DEVICE_ID_MOTORCONTROL 0x0058
+
+#define URB_INT_SIZE 8
+
+static unsigned long device_no;
+
+struct motorcontrol {
+ struct usb_device *udev;
+ struct usb_interface *intf;
+ struct device *dev;
+ int dev_no;
+ u8 inputs[4];
+ s8 desired_speed[2];
+ s8 speed[2];
+ s16 _current[2];
+ s8 acceleration[2];
+ struct urb *irq;
+ unsigned char *data;
+ dma_addr_t data_dma;
+
+ struct work_struct do_notify;
+ unsigned long input_events;
+ unsigned long speed_events;
+ unsigned long exceed_events;
+};
+
+static struct usb_device_id id_table[] = {
+ { USB_DEVICE(USB_VENDOR_ID_GLAB, USB_DEVICE_ID_MOTORCONTROL) },
+ {}
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+static int set_motor(struct motorcontrol *mc, int motor)
+{
+ u8 *buffer;
+ int speed, speed2, acceleration;
+ int retval;
+
+ buffer = kzalloc(8, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&mc->intf->dev, "%s - out of memory\n", __FUNCTION__);
+ return -ENOMEM;
+ }
+
+ acceleration = mc->acceleration[motor] * 10;
+ /* -127 <= speed <= 127 */
+ speed = (mc->desired_speed[motor] * 127) / 100;
+ /* -0x7300 <= speed2 <= 0x7300 */
+ speed2 = (mc->desired_speed[motor] * 230 * 128) / 100;
+
+ buffer[0] = motor;
+ buffer[1] = speed;
+ buffer[2] = acceleration >> 8;
+ buffer[3] = acceleration;
+ buffer[4] = speed2 >> 8;
+ buffer[5] = speed2;
+
+ retval = usb_control_msg(mc->udev,
+ usb_sndctrlpipe(mc->udev, 0),
+ 0x09, 0x21, 0x0200, 0x0000, buffer, 8, 2000);
+
+ if (retval != 8)
+ dev_err(&mc->intf->dev, "usb_control_msg returned %d\n",
+ retval);
+ kfree(buffer);
+
+ return retval < 0 ? retval : 0;
+}
+
+static void motorcontrol_irq(struct urb *urb, struct pt_regs *regs)
+{
+ struct motorcontrol *mc = urb->context;
+ unsigned char *buffer = mc->data;
+ int i, level;
+ int status;
+
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET: /* unlink */
+ case -ENOENT:
+ case -ESHUTDOWN:
+ return;
+ /* -EPIPE: should clear the halt */
+ default: /* error */
+ goto resubmit;
+ }
+
+ /* digital inputs */
+ for (i=0; i<4; i++) {
+ level = (buffer[0] >> i) & 1;
+ if (mc->inputs[i] != level) {
+ mc->inputs[i] = level;
+ set_bit(i, &mc->input_events);
+ }
+ }
+
+ /* motor speed */
+ if (buffer[2] == 0) {
+ for (i=0; i<2; i++) {
+ level = ((s8)buffer[4+i]) * 100 / 127;
+ if (mc->speed[i] != level) {
+ mc->speed[i] = level;
+ set_bit(i, &mc->speed_events);
+ }
+ }
+ } else {
+ int index = buffer[3] & 1;
+
+ level = ((s8)buffer[4] << 8) | buffer[5];
+ level = level * 100 / 29440;
+ if (mc->speed[index] != level) {
+ mc->speed[index] = level;
+ set_bit(index, &mc->speed_events);
+ }
+
+ level = ((s8)buffer[6] << 8) | buffer[7];
+ mc->_current[index] = level * 100 / 1572;
+ }
+
+ if (buffer[1] & 1)
+ set_bit(0, &mc->exceed_events);
+
+ if (buffer[1] & 2)
+ set_bit(1, &mc->exceed_events);
+
+ if (mc->input_events || mc->exceed_events || mc->speed_events)
+ schedule_work(&mc->do_notify);
+
+resubmit:
+ status = usb_submit_urb(urb, SLAB_ATOMIC);
+ if (status)
+ dev_err(&mc->intf->dev,
+ "can't resubmit intr, %s-%s/motorcontrol0, status %d",
+ mc->udev->bus->bus_name,
+ mc->udev->devpath, status);
+}
+
+static void do_notify(void *data)
+{
+ struct motorcontrol *mc = data;
+ int i;
+ char sysfs_file[8];
+
+ for (i=0; i<4; i++) {
+ if (test_and_clear_bit(i, &mc->input_events)) {
+ sprintf(sysfs_file, "input%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->speed_events)) {
+ sprintf(sysfs_file, "speed%d", i);
+ sysfs_notify(&mc->dev->kobj, NULL, sysfs_file);
+ }
+ }
+
+ for (i=0; i<2; i++) {
+ if (test_and_clear_bit(i, &mc->exceed_events))
+ dev_warn(&mc->intf->dev,
+ "motor #%d exceeds 1.5 Amp current limit\n", i);
+ }
+}
+
+#define show_set_speed(value) \
+static ssize_t set_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int speed; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &speed) < 1) \
+ return -EINVAL; \
+ \
+ if (speed < -100 || speed > 100) \
+ return -EINVAL; \
+ \
+ mc->desired_speed[value] = speed; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_speed##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->speed[value]); \
+}
+
+#define speed_attr(value) \
+ __ATTR(speed##value, S_IWUGO | S_IRUGO, \
+ show_speed##value, set_speed##value)
+
+show_set_speed(0);
+show_set_speed(1);
+
+#define show_set_acceleration(value) \
+static ssize_t set_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ const char *buf, size_t count) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ int acceleration; \
+ int retval; \
+ \
+ if (sscanf(buf, "%d", &acceleration) < 1) \
+ return -EINVAL; \
+ \
+ if (acceleration < 0 || acceleration > 100) \
+ return -EINVAL; \
+ \
+ mc->acceleration[value] = acceleration; \
+ \
+ retval = set_motor(mc, value); \
+ \
+ return retval ? retval : count; \
+} \
+ \
+static ssize_t show_acceleration##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", mc->acceleration[value]); \
+}
+
+#define acceleration_attr(value) \
+ __ATTR(acceleration##value, S_IWUGO | S_IRUGO, \
+ show_acceleration##value, set_acceleration##value)
+
+show_set_acceleration(0);
+show_set_acceleration(1);
+
+#define show_current(value) \
+static ssize_t show_current##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%dmA\n", (int)mc->_current[value]); \
+}
+
+#define current_attr(value) \
+ __ATTR(current##value, S_IRUGO, show_current##value, NULL)
+
+show_current(0);
+show_current(1);
+
+#define show_input(value) \
+static ssize_t show_input##value(struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
+{ \
+ struct motorcontrol *mc = dev_get_drvdata(dev); \
+ \
+ return sprintf(buf, "%d\n", (int)mc->inputs[value]); \
+}
+
+#define input_attr(value) \
+ __ATTR(input##value, S_IRUGO, show_input##value, NULL)
+
+show_input(0);
+show_input(1);
+show_input(2);
+show_input(3);
+
+static struct device_attribute dev_attrs[] = {
+ input_attr(0),
+ input_attr(1),
+ input_attr(2),
+ input_attr(3),
+ speed_attr(0),
+ speed_attr(1),
+ acceleration_attr(0),
+ acceleration_attr(1),
+ current_attr(0),
+ current_attr(1)
+};
+
+static int motorcontrol_probe(struct usb_interface *intf, const struct usb_device_id *id)
+{
+ struct usb_device *dev = interface_to_usbdev(intf);
+ struct usb_host_interface *interface;
+ struct usb_endpoint_descriptor *endpoint;
+ struct motorcontrol *mc;
+ int pipe, maxp, rc = -ENOMEM;
+ int bit, value, i;
+
+ interface = intf->cur_altsetting;
+ if (interface->desc.bNumEndpoints != 1)
+ return -ENODEV;
+
+ endpoint = &interface->endpoint[0].desc;
+ if (!(endpoint->bEndpointAddress & 0x80))
+ return -ENODEV;
+
+ /*
+ * bmAttributes
+ */
+ pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
+ maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+
+ mc = kzalloc(sizeof(*mc), GFP_KERNEL);
+ if (!mc)
+ goto out;
+
+ mc->dev_no = -1;
+ mc->data = usb_buffer_alloc(dev, URB_INT_SIZE, SLAB_ATOMIC, &mc->data_dma);
+ if (!mc->data)
+ goto out;
+
+ mc->irq = usb_alloc_urb(0, GFP_KERNEL);
+ if (!mc->irq)
+ goto out;
+
+ mc->udev = usb_get_dev(dev);
+ mc->intf = intf;
+ mc->acceleration[0] = mc->acceleration[1] = 10;
+ INIT_WORK(&mc->do_notify, do_notify, mc);
+ usb_fill_int_urb(mc->irq, mc->udev, pipe, mc->data,
+ maxp > URB_INT_SIZE ? URB_INT_SIZE : maxp,
+ motorcontrol_irq, mc, endpoint->bInterval);
+ mc->irq->transfer_dma = mc->data_dma;
+ mc->irq->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+
+ usb_set_intfdata(intf, mc);
+
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while(value);
+ mc->dev_no = bit;
+
+ mc->dev = device_create(phidget_class, &mc->udev->dev, 0,
+ "motorcontrol%d", mc->dev_no);
+ if (IS_ERR(mc->dev)) {
+ rc = PTR_ERR(mc->dev);
+ mc->dev = NULL;
+ goto out;
+ }
+
+ dev_set_drvdata(mc->dev, mc);
+
+ if (usb_submit_urb(mc->irq, GFP_KERNEL)) {
+ rc = -EIO;
+ goto out;
+ }
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++) {
+ rc = device_create_file(mc->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
+ }
+
+ dev_info(&intf->dev, "USB PhidgetMotorControl attached\n");
+
+ return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+out:
+ if (mc) {
+ if (mc->irq)
+ usb_free_urb(mc->irq);
+ if (mc->data)
+ usb_buffer_free(dev, URB_INT_SIZE, mc->data, mc->data_dma);
+ if (mc->dev)
+ device_unregister(mc->dev);
+ if (mc->dev_no >= 0)
+ clear_bit(mc->dev_no, &device_no);
+
+ kfree(mc);
+ }
+
+ return rc;
+}
+
+static void motorcontrol_disconnect(struct usb_interface *interface)
+{
+ struct motorcontrol *mc;
+ int i;
+
+ mc = usb_get_intfdata(interface);
+ usb_set_intfdata(interface, NULL);
+ if (!mc)
+ return;
+
+ usb_kill_urb(mc->irq);
+ usb_free_urb(mc->irq);
+ usb_buffer_free(mc->udev, URB_INT_SIZE, mc->data, mc->data_dma);
+
+ cancel_delayed_work(&mc->do_notify);
+
+ for (i=0; i<ARRAY_SIZE(dev_attrs); i++)
+ device_remove_file(mc->dev, &dev_attrs[i]);
+
+ device_unregister(mc->dev);
+
+ usb_put_dev(mc->udev);
+ clear_bit(mc->dev_no, &device_no);
+ kfree(mc);
+
+ dev_info(&interface->dev, "USB PhidgetMotorControl detached\n");
+}
+
+static struct usb_driver motorcontrol_driver = {
+ .name = "phidgetmotorcontrol",
+ .probe = motorcontrol_probe,
+ .disconnect = motorcontrol_disconnect,
+ .id_table = id_table
+};
+
+static int __init motorcontrol_init(void)
+{
+ int retval = 0;
+
+ retval = usb_register(&motorcontrol_driver);
+ if (retval)
+ err("usb_register failed. Error number %d", retval);
+
+ return retval;
+}
+
+static void __exit motorcontrol_exit(void)
+{
+ usb_deregister(&motorcontrol_driver);
+}
+
+module_init(motorcontrol_init);
+module_exit(motorcontrol_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/misc/phidgetservo.c b/drivers/usb/misc/phidgetservo.c
index c0df79c9653..7163f05c5b2 100644
--- a/drivers/usb/misc/phidgetservo.c
+++ b/drivers/usb/misc/phidgetservo.c
@@ -1,7 +1,7 @@
/*
* USB PhidgetServo driver 1.0
*
- * Copyright (C) 2004 Sean Young <sean@mess.org>
+ * Copyright (C) 2004, 2006 Sean Young <sean@mess.org>
*
* 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
@@ -15,14 +15,6 @@
*
* CAUTION: Generally you should use 0 < degrees < 180 as anything else
* is probably beyond the range of your servo and may damage it.
- *
- * Jun 16, 2004: Sean Young <sean@mess.org>
- * - cleanups
- * - was using memory after kfree()
- * Aug 8, 2004: Sean Young <sean@mess.org>
- * - set the highest angle as high as the hardware allows, there are
- * some odd servos out there
- *
*/
#include <linux/kernel.h>
@@ -32,6 +24,8 @@
#include <linux/module.h>
#include <linux/usb.h>
+#include "phidget.h"
+
#define DRIVER_AUTHOR "Sean Young <sean@mess.org>"
#define DRIVER_DESC "USB PhidgetServo Driver"
@@ -70,8 +64,12 @@ static struct usb_device_id id_table[] = {
MODULE_DEVICE_TABLE(usb, id_table);
+static int unsigned long device_no;
+
struct phidget_servo {
struct usb_device *udev;
+ struct device *dev;
+ int dev_no;
ulong type;
int pulse[4];
int degrees[4];
@@ -203,16 +201,16 @@ change_position_v20(struct phidget_servo *servo, int servo_no, int degrees,
}
#define show_set(value) \
-static ssize_t set_servo##value (struct device *dev, struct device_attribute *attr, \
+static ssize_t set_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
const char *buf, size_t count) \
{ \
int degrees, minutes, retval; \
- struct usb_interface *intf = to_usb_interface (dev); \
- struct phidget_servo *servo = usb_get_intfdata (intf); \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
\
minutes = 0; \
/* must at least convert degrees */ \
- if (sscanf (buf, "%d.%d", &degrees, &minutes) < 1) { \
+ if (sscanf(buf, "%d.%d", &degrees, &minutes) < 1) { \
return -EINVAL; \
} \
\
@@ -220,86 +218,127 @@ static ssize_t set_servo##value (struct device *dev, struct device_attribute *at
return -EINVAL; \
\
if (servo->type & SERVO_VERSION_30) \
- retval = change_position_v30 (servo, value, degrees, \
+ retval = change_position_v30(servo, value, degrees, \
minutes); \
else \
- retval = change_position_v20 (servo, value, degrees, \
+ retval = change_position_v20(servo, value, degrees, \
minutes); \
\
return retval < 0 ? retval : count; \
} \
\
-static ssize_t show_servo##value (struct device *dev, struct device_attribute *attr, char *buf) \
+static ssize_t show_servo##value (struct device *dev, \
+ struct device_attribute *attr, \
+ char *buf) \
{ \
- struct usb_interface *intf = to_usb_interface (dev); \
- struct phidget_servo *servo = usb_get_intfdata (intf); \
+ struct phidget_servo *servo = dev_get_drvdata(dev); \
\
- return sprintf (buf, "%d.%02d\n", servo->degrees[value], \
+ return sprintf(buf, "%d.%02d\n", servo->degrees[value], \
servo->minutes[value]); \
-} \
-static DEVICE_ATTR(servo##value, S_IWUGO | S_IRUGO, \
- show_servo##value, set_servo##value);
+}
+#define servo_attr(value) \
+ __ATTR(servo##value, S_IWUGO | S_IRUGO, \
+ show_servo##value, set_servo##value)
show_set(0);
show_set(1);
show_set(2);
show_set(3);
+static struct device_attribute dev_attrs[] = {
+ servo_attr(0), servo_attr(1), servo_attr(2), servo_attr(3)
+};
+
static int
servo_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
struct usb_device *udev = interface_to_usbdev(interface);
struct phidget_servo *dev;
+ int bit, value, rc;
+ int servo_count, i;
dev = kzalloc(sizeof (struct phidget_servo), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "%s - out of memory\n", __FUNCTION__);
- return -ENOMEM;
+ rc = -ENOMEM;
+ goto out;
}
dev->udev = usb_get_dev(udev);
dev->type = id->driver_info;
+ dev->dev_no = -1;
usb_set_intfdata(interface, dev);
- device_create_file(&interface->dev, &dev_attr_servo0);
- if (dev->type & SERVO_COUNT_QUAD) {
- device_create_file(&interface->dev, &dev_attr_servo1);
- device_create_file(&interface->dev, &dev_attr_servo2);
- device_create_file(&interface->dev, &dev_attr_servo3);
+ do {
+ bit = find_first_zero_bit(&device_no, sizeof(device_no));
+ value = test_and_set_bit(bit, &device_no);
+ } while (value);
+ dev->dev_no = bit;
+
+ dev->dev = device_create(phidget_class, &dev->udev->dev, 0,
+ "servo%d", dev->dev_no);
+ if (IS_ERR(dev->dev)) {
+ rc = PTR_ERR(dev->dev);
+ dev->dev = NULL;
+ goto out;
+ }
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++) {
+ rc = device_create_file(dev->dev, &dev_attrs[i]);
+ if (rc)
+ goto out2;
}
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 attached\n",
- dev->type & SERVO_COUNT_QUAD ? 4 : 1,
- dev->type & SERVO_VERSION_30 ? 3 : 2);
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
- if(!(dev->type & SERVO_VERSION_30))
+ if (!(dev->type & SERVO_VERSION_30))
dev_info(&interface->dev,
"WARNING: v2.0 not tested! Please report if it works.\n");
return 0;
+out2:
+ while (i-- > 0)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+out:
+ if (dev) {
+ if (dev->dev)
+ device_unregister(dev->dev);
+ if (dev->dev_no >= 0)
+ clear_bit(dev->dev_no, &device_no);
+
+ kfree(dev);
+ }
+
+ return rc;
}
static void
servo_disconnect(struct usb_interface *interface)
{
struct phidget_servo *dev;
+ int servo_count, i;
dev = usb_get_intfdata(interface);
usb_set_intfdata(interface, NULL);
- device_remove_file(&interface->dev, &dev_attr_servo0);
- if (dev->type & SERVO_COUNT_QUAD) {
- device_remove_file(&interface->dev, &dev_attr_servo1);
- device_remove_file(&interface->dev, &dev_attr_servo2);
- device_remove_file(&interface->dev, &dev_attr_servo3);
- }
+ if (!dev)
+ return;
+
+ servo_count = dev->type & SERVO_COUNT_QUAD ? 4 : 1;
+
+ for (i=0; i<servo_count; i++)
+ device_remove_file(dev->dev, &dev_attrs[i]);
+ device_unregister(dev->dev);
usb_put_dev(dev->udev);
dev_info(&interface->dev, "USB %d-Motor PhidgetServo v%d.0 detached\n",
- dev->type & SERVO_COUNT_QUAD ? 4 : 1,
- dev->type & SERVO_VERSION_30 ? 3 : 2);
+ servo_count, dev->type & SERVO_VERSION_30 ? 3 : 2);
+ clear_bit(dev->dev_no, &device_no);
kfree(dev);
}
diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c
index e16582f3733..a44124c7e85 100644
--- a/drivers/usb/misc/sisusbvga/sisusb.c
+++ b/drivers/usb/misc/sisusbvga/sisusb.c
@@ -3179,7 +3179,7 @@ sisusb_compat_ioctl(struct file *f, unsigned int cmd, unsigned long arg)
}
#endif
-static struct file_operations usb_sisusb_fops = {
+static const struct file_operations usb_sisusb_fops = {
.owner = THIS_MODULE,
.open = sisusb_open,
.release = sisusb_release,
diff --git a/drivers/usb/misc/usb_u132.h b/drivers/usb/misc/usb_u132.h
new file mode 100644
index 00000000000..551ba8906d6
--- /dev/null
+++ b/drivers/usb/misc/usb_u132.h
@@ -0,0 +1,97 @@
+/*
+* Common Header File for the Elan Digital Systems U132 adapter
+* this file should be included by both the "ftdi-u132" and
+* the "u132-hcd" modules.
+*
+* Copyright(C) 2006 Elan Digital Systems Limited
+*(http://www.elandigitalsystems.com)
+*
+* Author and Maintainer - Tony Olech - Elan Digital Systems
+*(tony.olech@elandigitalsystems.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, version 2.
+*
+*
+* The driver was written by Tony Olech(tony.olech@elandigitalsystems.com)
+* based on various USB client drivers in the 2.6.15 linux kernel
+* with constant reference to the 3rd Edition of Linux Device Drivers
+* published by O'Reilly
+*
+* The U132 adapter is a USB to CardBus adapter specifically designed
+* for PC cards that contain an OHCI host controller. Typical PC cards
+* are the Orange Mobile 3G Option GlobeTrotter Fusion card.
+*
+* The U132 adapter will *NOT *work with PC cards that do not contain
+* an OHCI controller. A simple way to test whether a PC card has an
+* OHCI controller as an interface is to insert the PC card directly
+* into a laptop(or desktop) with a CardBus slot and if "lspci" shows
+* a new USB controller and "lsusb -v" shows a new OHCI Host Controller
+* then there is a good chance that the U132 adapter will support the
+* PC card.(you also need the specific client driver for the PC card)
+*
+* Please inform the Author and Maintainer about any PC cards that
+* contain OHCI Host Controller and work when directly connected to
+* an embedded CardBus slot but do not work when they are connected
+* via an ELAN U132 adapter.
+*
+* The driver consists of two modules, the "ftdi-u132" module is
+* a USB client driver that interfaces to the FTDI chip within
+* the U132 adapter manufactured by Elan Digital Systems, and the
+* "u132-hcd" module is a USB host controller driver that talks
+* to the OHCI controller within CardBus card that are inserted
+* in the U132 adapter.
+*
+* The "ftdi-u132" module should be loaded automatically by the
+* hot plug system when the U132 adapter is plugged in. The module
+* initialises the adapter which mostly consists of synchronising
+* the FTDI chip, before continuously polling the adapter to detect
+* PC card insertions. As soon as a PC card containing a recognised
+* OHCI controller is seen the "ftdi-u132" module explicitly requests
+* the kernel to load the "u132-hcd" module.
+*
+* The "ftdi-u132" module provides the interface to the inserted
+* PC card and the "u132-hcd" module uses the API to send and recieve
+* data. The API features call-backs, so that part of the "u132-hcd"
+* module code will run in the context of one of the kernel threads
+* of the "ftdi-u132" module.
+*
+*/
+int ftdi_elan_switch_on_diagnostics(int number);
+void ftdi_elan_gone_away(struct platform_device *pdev);
+void start_usb_lock_device_tracing(void);
+struct u132_platform_data {
+ u16 vendor;
+ u16 device;
+ u8 potpg;
+ void (*port_power) (struct device *dev, int is_on);
+ void (*reset) (struct device *dev);
+};
+int usb_ftdi_elan_edset_single(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_output(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_empty(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_input(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_setup(struct platform_device *pdev, u8 ed_number,
+ void *endp, struct urb *urb, u8 address, u8 ep_number, u8 toggle_bits,
+ void (*callback) (void *endp, struct urb *urb, u8 *buf, int len,
+ int toggle_bits, int error_count, int condition_code, int repeat_number,
+ int halted, int skipped, int actual, int non_null));
+int usb_ftdi_elan_edset_flush(struct platform_device *pdev, u8 ed_number,
+ void *endp);
diff --git a/drivers/usb/misc/usblcd.c b/drivers/usb/misc/usblcd.c
index e095772dd8e..dbaca9f1efa 100644
--- a/drivers/usb/misc/usblcd.c
+++ b/drivers/usb/misc/usblcd.c
@@ -239,7 +239,7 @@ error:
return retval;
}
-static struct file_operations lcd_fops = {
+static const struct file_operations lcd_fops = {
.owner = THIS_MODULE,
.read = lcd_read,
.write = lcd_write,
@@ -290,9 +290,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
- (endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
@@ -305,9 +303,7 @@ static int lcd_probe(struct usb_interface *interface, const struct usb_device_id
}
if (!dev->bulk_out_endpointAddr &&
- !(endpoint->bEndpointAddress & USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
diff --git a/drivers/usb/misc/usbled.c b/drivers/usb/misc/usbled.c
index 0c5ee0ad6bb..49c5c5c4c43 100644
--- a/drivers/usb/misc/usbled.c
+++ b/drivers/usb/misc/usbled.c
@@ -108,22 +108,34 @@ static int led_probe(struct usb_interface *interface, const struct usb_device_id
dev = kzalloc(sizeof(struct usb_led), GFP_KERNEL);
if (dev == NULL) {
dev_err(&interface->dev, "Out of memory\n");
- goto error;
+ goto error_mem;
}
dev->udev = usb_get_dev(udev);
usb_set_intfdata (interface, dev);
- device_create_file(&interface->dev, &dev_attr_blue);
- device_create_file(&interface->dev, &dev_attr_red);
- device_create_file(&interface->dev, &dev_attr_green);
+ retval = device_create_file(&interface->dev, &dev_attr_blue);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_red);
+ if (retval)
+ goto error;
+ retval = device_create_file(&interface->dev, &dev_attr_green);
+ if (retval)
+ goto error;
dev_info(&interface->dev, "USB LED device now attached\n");
return 0;
error:
+ device_remove_file(&interface->dev, &dev_attr_blue);
+ device_remove_file(&interface->dev, &dev_attr_red);
+ device_remove_file(&interface->dev, &dev_attr_green);
+ usb_set_intfdata (interface, NULL);
+ usb_put_dev(dev->udev);
kfree(dev);
+error_mem:
return retval;
}
diff --git a/drivers/usb/mon/mon_main.c b/drivers/usb/mon/mon_main.c
index 275a66f8305..394bbf2f68d 100644
--- a/drivers/usb/mon/mon_main.c
+++ b/drivers/usb/mon/mon_main.c
@@ -265,7 +265,6 @@ static void mon_dissolve(struct mon_bus *mbus, struct usb_bus *ubus)
ubus->mon_bus = NULL;
mbus->u_bus = NULL;
mb();
- // usb_bus_put(ubus);
}
/*
@@ -297,12 +296,12 @@ static void mon_bus_init(struct dentry *mondir, struct usb_bus *ubus)
INIT_LIST_HEAD(&mbus->r_list);
/*
- * This usb_bus_get here is superfluous, because we receive
- * a notification if usb_bus is about to be removed.
+ * We don't need to take a reference to ubus, because we receive
+ * a notification if the bus is about to be removed.
*/
- // usb_bus_get(ubus);
mbus->u_bus = ubus;
ubus->mon_bus = mbus;
+ mbus->uses_dma = ubus->uses_dma;
rc = snprintf(name, NAMESZ, "%dt", ubus->busnum);
if (rc <= 0 || rc >= NAMESZ)
diff --git a/drivers/usb/mon/mon_stat.c b/drivers/usb/mon/mon_stat.c
index 1fe01d994a7..f6d1491256c 100644
--- a/drivers/usb/mon/mon_stat.c
+++ b/drivers/usb/mon/mon_stat.c
@@ -28,7 +28,7 @@ static int mon_stat_open(struct inode *inode, struct file *file)
if ((sp = kmalloc(sizeof(struct snap), GFP_KERNEL)) == NULL)
return -ENOMEM;
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
sp->slen = snprintf(sp->str, STAT_BUF_SIZE,
"nreaders %d events %u text_lost %u\n",
@@ -62,7 +62,7 @@ static int mon_stat_release(struct inode *inode, struct file *file)
return 0;
}
-struct file_operations mon_fops_stat = {
+const struct file_operations mon_fops_stat = {
.owner = THIS_MODULE,
.open = mon_stat_open,
.llseek = no_llseek,
diff --git a/drivers/usb/mon/mon_text.c b/drivers/usb/mon/mon_text.c
index f961a770cee..7a2346c5328 100644
--- a/drivers/usb/mon/mon_text.c
+++ b/drivers/usb/mon/mon_text.c
@@ -75,13 +75,13 @@ static void mon_text_ctor(void *, kmem_cache_t *, unsigned long);
*/
static inline char mon_text_get_setup(struct mon_event_text *ep,
- struct urb *urb, char ev_type)
+ struct urb *urb, char ev_type, struct mon_bus *mbus)
{
if (!usb_pipecontrol(urb->pipe) || ev_type != 'S')
return '-';
- if (urb->transfer_flags & URB_NO_SETUP_DMA_MAP)
+ if (mbus->uses_dma && (urb->transfer_flags & URB_NO_SETUP_DMA_MAP))
return mon_dmapeek(ep->setup, urb->setup_dma, SETUP_MAX);
if (urb->setup_packet == NULL)
return 'Z'; /* '0' would be not as pretty. */
@@ -91,7 +91,7 @@ static inline char mon_text_get_setup(struct mon_event_text *ep,
}
static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
- int len, char ev_type)
+ int len, char ev_type, struct mon_bus *mbus)
{
int pipe = urb->pipe;
@@ -117,7 +117,7 @@ static inline char mon_text_get_data(struct mon_event_text *ep, struct urb *urb,
* contain non-NULL garbage in case the upper level promised to
* set DMA for the HCD.
*/
- if (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP)
+ if (mbus->uses_dma && (urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP))
return mon_dmapeek(ep->data, urb->transfer_dma, len);
if (urb->transfer_buffer == NULL)
@@ -161,8 +161,9 @@ static void mon_text_event(struct mon_reader_text *rp, struct urb *urb,
/* Collecting status makes debugging sense for submits, too */
ep->status = urb->status;
- ep->setup_flag = mon_text_get_setup(ep, urb, ev_type);
- ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type);
+ ep->setup_flag = mon_text_get_setup(ep, urb, ev_type, rp->r.m_bus);
+ ep->data_flag = mon_text_get_data(ep, urb, ep->length, ev_type,
+ rp->r.m_bus);
rp->nevents++;
list_add_tail(&ep->e_link, &rp->e_list);
@@ -238,7 +239,7 @@ static int mon_text_open(struct inode *inode, struct file *file)
int rc;
mutex_lock(&mon_lock);
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
ubus = mbus->u_bus;
rp = kzalloc(sizeof(struct mon_reader_text), GFP_KERNEL);
@@ -401,7 +402,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
struct mon_event_text *ep;
mutex_lock(&mon_lock);
- mbus = inode->u.generic_ip;
+ mbus = inode->i_private;
if (mbus->nreaders <= 0) {
printk(KERN_ERR TAG ": consistency error on close\n");
@@ -435,7 +436,7 @@ static int mon_text_release(struct inode *inode, struct file *file)
return 0;
}
-struct file_operations mon_fops_text = {
+const struct file_operations mon_fops_text = {
.owner = THIS_MODULE,
.open = mon_text_open,
.llseek = no_llseek,
diff --git a/drivers/usb/mon/usb_mon.h b/drivers/usb/mon/usb_mon.h
index 33678c24ebe..ab9d02d5df7 100644
--- a/drivers/usb/mon/usb_mon.h
+++ b/drivers/usb/mon/usb_mon.h
@@ -20,6 +20,7 @@ struct mon_bus {
struct dentry *dent_s; /* Debugging file */
struct dentry *dent_t; /* Text interface file */
struct usb_bus *u_bus;
+ int uses_dma;
/* Ref */
int nreaders; /* Under mon_lock AND mbus->lock */
@@ -53,7 +54,7 @@ extern char mon_dmapeek(unsigned char *dst, dma_addr_t dma_addr, int len);
extern struct mutex mon_lock;
-extern struct file_operations mon_fops_text;
-extern struct file_operations mon_fops_stat;
+extern const struct file_operations mon_fops_text;
+extern const struct file_operations mon_fops_stat;
#endif /* __USB_MON_H */
diff --git a/drivers/usb/net/asix.c b/drivers/usb/net/asix.c
index 2e2bbc003e9..9c0eacf7055 100644
--- a/drivers/usb/net/asix.c
+++ b/drivers/usb/net/asix.c
@@ -1,7 +1,8 @@
/*
* ASIX AX8817X based USB 2.0 Ethernet Devices
- * Copyright (C) 2003-2005 David Hollis <dhollis@davehollis.com>
+ * Copyright (C) 2003-2006 David Hollis <dhollis@davehollis.com>
* Copyright (C) 2005 Phil Chang <pchang23@sbcglobal.net>
+ * Copyright (C) 2006 James Painter <jamie.painter@iname.com>
* Copyright (c) 2002-2003 TiVo Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -36,6 +37,9 @@
#include "usbnet.h"
+#define DRIVER_VERSION "14-Jun-2006"
+static const char driver_name [] = "asix";
+
/* ASIX AX8817X based USB 2.0 Ethernet Devices */
#define AX_CMD_SET_SW_MII 0x06
@@ -46,23 +50,25 @@
#define AX_CMD_WRITE_EEPROM 0x0c
#define AX_CMD_WRITE_ENABLE 0x0d
#define AX_CMD_WRITE_DISABLE 0x0e
+#define AX_CMD_READ_RX_CTL 0x0f
#define AX_CMD_WRITE_RX_CTL 0x10
#define AX_CMD_READ_IPG012 0x11
#define AX_CMD_WRITE_IPG0 0x12
#define AX_CMD_WRITE_IPG1 0x13
+#define AX_CMD_READ_NODE_ID 0x13
#define AX_CMD_WRITE_IPG2 0x14
#define AX_CMD_WRITE_MULTI_FILTER 0x16
-#define AX_CMD_READ_NODE_ID 0x17
+#define AX88172_CMD_READ_NODE_ID 0x17
#define AX_CMD_READ_PHY_ID 0x19
#define AX_CMD_READ_MEDIUM_STATUS 0x1a
#define AX_CMD_WRITE_MEDIUM_MODE 0x1b
#define AX_CMD_READ_MONITOR_MODE 0x1c
#define AX_CMD_WRITE_MONITOR_MODE 0x1d
+#define AX_CMD_READ_GPIOS 0x1e
#define AX_CMD_WRITE_GPIOS 0x1f
#define AX_CMD_SW_RESET 0x20
#define AX_CMD_SW_PHY_STATUS 0x21
#define AX_CMD_SW_PHY_SELECT 0x22
-#define AX88772_CMD_READ_NODE_ID 0x13
#define AX_MONITOR_MODE 0x01
#define AX_MONITOR_LINK 0x02
@@ -70,15 +76,15 @@
#define AX_MONITOR_HSFS 0x10
/* AX88172 Medium Status Register values */
-#define AX_MEDIUM_FULL_DUPLEX 0x02
-#define AX_MEDIUM_TX_ABORT_ALLOW 0x04
-#define AX_MEDIUM_FLOW_CONTROL_EN 0x10
+#define AX88172_MEDIUM_FD 0x02
+#define AX88172_MEDIUM_TX 0x04
+#define AX88172_MEDIUM_FC 0x10
+#define AX88172_MEDIUM_DEFAULT \
+ ( AX88172_MEDIUM_FD | AX88172_MEDIUM_TX | AX88172_MEDIUM_FC )
#define AX_MCAST_FILTER_SIZE 8
#define AX_MAX_MCAST 64
-#define AX_EEPROM_LEN 0x40
-
#define AX_SWRESET_CLEAR 0x00
#define AX_SWRESET_RR 0x01
#define AX_SWRESET_RT 0x02
@@ -92,23 +98,78 @@
#define AX88772_IPG1_DEFAULT 0x0c
#define AX88772_IPG2_DEFAULT 0x12
-#define AX88772_MEDIUM_FULL_DUPLEX 0x0002
-#define AX88772_MEDIUM_RESERVED 0x0004
-#define AX88772_MEDIUM_RX_FC_ENABLE 0x0010
-#define AX88772_MEDIUM_TX_FC_ENABLE 0x0020
-#define AX88772_MEDIUM_PAUSE_FORMAT 0x0080
-#define AX88772_MEDIUM_RX_ENABLE 0x0100
-#define AX88772_MEDIUM_100MB 0x0200
-#define AX88772_MEDIUM_DEFAULT \
- (AX88772_MEDIUM_FULL_DUPLEX | AX88772_MEDIUM_RX_FC_ENABLE | \
- AX88772_MEDIUM_TX_FC_ENABLE | AX88772_MEDIUM_100MB | \
- AX88772_MEDIUM_RESERVED | AX88772_MEDIUM_RX_ENABLE )
+/* AX88772 & AX88178 Medium Mode Register */
+#define AX_MEDIUM_PF 0x0080
+#define AX_MEDIUM_JFE 0x0040
+#define AX_MEDIUM_TFC 0x0020
+#define AX_MEDIUM_RFC 0x0010
+#define AX_MEDIUM_ENCK 0x0008
+#define AX_MEDIUM_AC 0x0004
+#define AX_MEDIUM_FD 0x0002
+#define AX_MEDIUM_GM 0x0001
+#define AX_MEDIUM_SM 0x1000
+#define AX_MEDIUM_SBP 0x0800
+#define AX_MEDIUM_PS 0x0200
+#define AX_MEDIUM_RE 0x0100
+
+#define AX88178_MEDIUM_DEFAULT \
+ (AX_MEDIUM_PS | AX_MEDIUM_FD | AX_MEDIUM_AC | \
+ AX_MEDIUM_RFC | AX_MEDIUM_TFC | AX_MEDIUM_JFE | \
+ AX_MEDIUM_RE )
-#define AX_EEPROM_MAGIC 0xdeadbeef
+#define AX88772_MEDIUM_DEFAULT \
+ (AX_MEDIUM_FD | AX_MEDIUM_RFC | \
+ AX_MEDIUM_TFC | AX_MEDIUM_PS | \
+ AX_MEDIUM_AC | AX_MEDIUM_RE )
+
+/* AX88772 & AX88178 RX_CTL values */
+#define AX_RX_CTL_SO 0x0080
+#define AX_RX_CTL_AP 0x0020
+#define AX_RX_CTL_AM 0x0010
+#define AX_RX_CTL_AB 0x0008
+#define AX_RX_CTL_SEP 0x0004
+#define AX_RX_CTL_AMALL 0x0002
+#define AX_RX_CTL_PRO 0x0001
+#define AX_RX_CTL_MFB_2048 0x0000
+#define AX_RX_CTL_MFB_4096 0x0100
+#define AX_RX_CTL_MFB_8192 0x0200
+#define AX_RX_CTL_MFB_16384 0x0300
+
+#define AX_DEFAULT_RX_CTL \
+ (AX_RX_CTL_SO | AX_RX_CTL_AB )
+
+/* GPIO 0 .. 2 toggles */
+#define AX_GPIO_GPO0EN 0x01 /* GPIO0 Output enable */
+#define AX_GPIO_GPO_0 0x02 /* GPIO0 Output value */
+#define AX_GPIO_GPO1EN 0x04 /* GPIO1 Output enable */
+#define AX_GPIO_GPO_1 0x08 /* GPIO1 Output value */
+#define AX_GPIO_GPO2EN 0x10 /* GPIO2 Output enable */
+#define AX_GPIO_GPO_2 0x20 /* GPIO2 Output value */
+#define AX_GPIO_RESERVED 0x40 /* Reserved */
+#define AX_GPIO_RSE 0x80 /* Reload serial EEPROM */
+
+#define AX_EEPROM_MAGIC 0xdeadbeef
+#define AX88172_EEPROM_LEN 0x40
+#define AX88772_EEPROM_LEN 0xff
+
+#define PHY_MODE_MARVELL 0x0000
+#define MII_MARVELL_LED_CTRL 0x0018
+#define MII_MARVELL_STATUS 0x001b
+#define MII_MARVELL_CTRL 0x0014
+
+#define MARVELL_LED_MANUAL 0x0019
+
+#define MARVELL_STATUS_HWCFG 0x0004
+
+#define MARVELL_CTRL_TXDELAY 0x0002
+#define MARVELL_CTRL_RXDELAY 0x0080
/* This structure cannot exceed sizeof(unsigned long [5]) AKA 20 bytes */
struct asix_data {
u8 multi_filter[AX_MCAST_FILTER_SIZE];
+ u8 phymode;
+ u8 ledmode;
+ u8 eeprom_len;
};
struct ax88172_int_data {
@@ -122,6 +183,8 @@ struct ax88172_int_data {
static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
+ devdbg(dev,"asix_read_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
return usb_control_msg(
dev->udev,
usb_rcvctrlpipe(dev->udev, 0),
@@ -137,6 +200,8 @@ static int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
static int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *data)
{
+ devdbg(dev,"asix_write_cmd() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
return usb_control_msg(
dev->udev,
usb_sndctrlpipe(dev->udev, 0),
@@ -161,12 +226,167 @@ static void asix_async_cmd_callback(struct urb *urb, struct pt_regs *regs)
usb_free_urb(urb);
}
+static void
+asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+ u16 size, void *data)
+{
+ struct usb_ctrlrequest *req;
+ int status;
+ struct urb *urb;
+
+ devdbg(dev,"asix_write_cmd_async() cmd=0x%02x value=0x%04x index=0x%04x size=%d",
+ cmd, value, index, size);
+ if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
+ deverr(dev, "Error allocating URB in write_cmd_async!");
+ return;
+ }
+
+ if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
+ deverr(dev, "Failed to allocate memory for control request");
+ usb_free_urb(urb);
+ return;
+ }
+
+ req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+ req->bRequest = cmd;
+ req->wValue = value;
+ req->wIndex = index;
+ req->wLength = size;
+
+ usb_fill_control_urb(urb, dev->udev,
+ usb_sndctrlpipe(dev->udev, 0),
+ (void *)req, data, size,
+ asix_async_cmd_callback, req);
+
+ if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
+ deverr(dev, "Error submitting the control message: status=%d",
+ status);
+ kfree(req);
+ usb_free_urb(urb);
+ }
+}
+
+static int asix_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+ u8 *head;
+ u32 header;
+ char *packet;
+ struct sk_buff *ax_skb;
+ u16 size;
+
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
+
+ skb_pull(skb, 4);
+
+ while (skb->len > 0) {
+ if ((short)(header & 0x0000ffff) !=
+ ~((short)((header & 0xffff0000) >> 16))) {
+ deverr(dev,"asix_rx_fixup() Bad Header Length");
+ }
+ /* get the packet length */
+ size = (u16) (header & 0x0000ffff);
+
+ if ((skb->len) - ((size + 1) & 0xfffe) == 0)
+ return 2;
+ if (size > ETH_FRAME_LEN) {
+ deverr(dev,"asix_rx_fixup() Bad RX Length %d", size);
+ return 0;
+ }
+ ax_skb = skb_clone(skb, GFP_ATOMIC);
+ if (ax_skb) {
+ ax_skb->len = size;
+ ax_skb->data = packet;
+ ax_skb->tail = packet + size;
+ usbnet_skb_return(dev, ax_skb);
+ } else {
+ return 0;
+ }
+
+ skb_pull(skb, (size + 1) & 0xfffe);
+
+ if (skb->len == 0)
+ break;
+
+ head = (u8 *) skb->data;
+ memcpy(&header, head, sizeof(header));
+ le32_to_cpus(&header);
+ packet = head + sizeof(header);
+ skb_pull(skb, 4);
+ }
+
+ if (skb->len < 0) {
+ deverr(dev,"asix_rx_fixup() Bad SKB Length %d", skb->len);
+ return 0;
+ }
+ return 1;
+}
+
+static struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+ gfp_t flags)
+{
+ int padlen;
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
+ u32 packet_len;
+ u32 padbytes = 0xffff0000;
+
+ padlen = ((skb->len + 4) % 512) ? 0 : 4;
+
+ if ((!skb_cloned(skb))
+ && ((headroom + tailroom) >= (4 + padlen))) {
+ if ((headroom < 4) || (tailroom < padlen)) {
+ skb->data = memmove(skb->head + 4, skb->data, skb->len);
+ skb->tail = skb->data + skb->len;
+ }
+ } else {
+ struct sk_buff *skb2;
+ skb2 = skb_copy_expand(skb, 4, padlen, flags);
+ dev_kfree_skb_any(skb);
+ skb = skb2;
+ if (!skb)
+ return NULL;
+ }
+
+ skb_push(skb, 4);
+ packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
+ memcpy(skb->data, &packet_len, sizeof(packet_len));
+
+ if ((skb->len % 512) == 0) {
+ memcpy( skb->tail, &padbytes, sizeof(padbytes));
+ skb_put(skb, sizeof(padbytes));
+ }
+ return skb;
+}
+
+static void asix_status(struct usbnet *dev, struct urb *urb)
+{
+ struct ax88172_int_data *event;
+ int link;
+
+ if (urb->actual_length < 8)
+ return;
+
+ event = urb->transfer_buffer;
+ link = event->link & 0x01;
+ if (netif_carrier_ok(dev->net) != link) {
+ if (link) {
+ netif_carrier_on(dev->net);
+ usbnet_defer_kevent (dev, EVENT_LINK_RESET );
+ } else
+ netif_carrier_off(dev->net);
+ devdbg(dev, "Link Status is: %d", link);
+ }
+}
+
static inline int asix_set_sw_mii(struct usbnet *dev)
{
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to enable software MII access");
+ deverr(dev, "Failed to enable software MII access");
return ret;
}
@@ -175,24 +395,27 @@ static inline int asix_set_hw_mii(struct usbnet *dev)
int ret;
ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to enable hardware MII access");
+ deverr(dev, "Failed to enable hardware MII access");
return ret;
}
-static inline int asix_get_phyid(struct usbnet *dev)
+static inline int asix_get_phy_addr(struct usbnet *dev)
{
int ret = 0;
void *buf;
+ devdbg(dev, "asix_get_phy_addr()");
+
buf = kmalloc(2, GFP_KERNEL);
if (!buf)
goto out1;
if ((ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID,
0, 0, 2, buf)) < 2) {
- devdbg(dev, "Error reading PHYID register: %02x", ret);
+ deverr(dev, "Error reading PHYID register: %02x", ret);
goto out2;
}
+ devdbg(dev, "asix_get_phy_addr() returning 0x%04x", *((u16 *)buf));
ret = *((u8 *)buf + 1);
out2:
kfree(buf);
@@ -206,91 +429,109 @@ static int asix_sw_reset(struct usbnet *dev, u8 flags)
ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
if (ret < 0)
- devdbg(dev,"Failed to send software reset: %02x", ret);
+ deverr(dev,"Failed to send software reset: %02x", ret);
return ret;
}
+static u16 asix_read_rx_ctl(struct usbnet *dev)
+{
+ u16 ret = 0;
+ void *buf;
+
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ goto out1;
+
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL,
+ 0, 0, 2, buf)) < 2) {
+ deverr(dev, "Error reading RX_CTL register: %02x", ret);
+ goto out2;
+ }
+ ret = le16_to_cpu(*((u16 *)buf));
+out2:
+ kfree(buf);
+out1:
+ return ret;
+}
+
static int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
{
int ret;
+ devdbg(dev,"asix_write_rx_ctl() - mode = 0x%04x", mode);
ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
if (ret < 0)
- devdbg(dev, "Failed to write RX_CTL mode: %02x", ret);
+ deverr(dev, "Failed to write RX_CTL mode to 0x%04x: %02x",
+ mode, ret);
return ret;
}
-static void asix_status(struct usbnet *dev, struct urb *urb)
+static u16 asix_read_medium_status(struct usbnet *dev)
{
- struct ax88172_int_data *event;
- int link;
+ u16 ret = 0;
+ void *buf;
- if (urb->actual_length < 8)
- return;
+ buf = kmalloc(2, GFP_KERNEL);
+ if (!buf)
+ goto out1;
- event = urb->transfer_buffer;
- link = event->link & 0x01;
- if (netif_carrier_ok(dev->net) != link) {
- if (link) {
- netif_carrier_on(dev->net);
- usbnet_defer_kevent (dev, EVENT_LINK_RESET );
- } else
- netif_carrier_off(dev->net);
- devdbg(dev, "Link Status is: %d", link);
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
+ 0, 0, 2, buf)) < 2) {
+ deverr(dev, "Error reading Medium Status register: %02x", ret);
+ goto out2;
}
+ ret = le16_to_cpu(*((u16 *)buf));
+out2:
+ kfree(buf);
+out1:
+ return ret;
}
-static void
-asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
- u16 size, void *data)
+static int asix_write_medium_mode(struct usbnet *dev, u16 mode)
{
- struct usb_ctrlrequest *req;
- int status;
- struct urb *urb;
+ int ret;
- if ((urb = usb_alloc_urb(0, GFP_ATOMIC)) == NULL) {
- devdbg(dev, "Error allocating URB in write_cmd_async!");
- return;
- }
+ devdbg(dev,"asix_write_medium_mode() - mode = 0x%04x", mode);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ if (ret < 0)
+ deverr(dev, "Failed to write Medium Mode mode to 0x%04x: %02x",
+ mode, ret);
- if ((req = kmalloc(sizeof(struct usb_ctrlrequest), GFP_ATOMIC)) == NULL) {
- deverr(dev, "Failed to allocate memory for control request");
- usb_free_urb(urb);
- return;
- }
+ return ret;
+}
- req->bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
- req->bRequest = cmd;
- req->wValue = cpu_to_le16(value);
- req->wIndex = cpu_to_le16(index);
- req->wLength = cpu_to_le16(size);
+static int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
+{
+ int ret;
- usb_fill_control_urb(urb, dev->udev,
- usb_sndctrlpipe(dev->udev, 0),
- (void *)req, data, size,
- asix_async_cmd_callback, req);
+ devdbg(dev,"asix_write_gpio() - value = 0x%04x", value);
+ ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
+ if (ret < 0)
+ deverr(dev, "Failed to write GPIO value 0x%04x: %02x",
+ value, ret);
- if((status = usb_submit_urb(urb, GFP_ATOMIC)) < 0) {
- deverr(dev, "Error submitting the control message: status=%d",
- status);
- kfree(req);
- usb_free_urb(urb);
- }
+ if (sleep)
+ msleep(sleep);
+
+ return ret;
}
+/*
+ * AX88772 & AX88178 have a 16-bit RX_CTL value
+ */
static void asix_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
struct asix_data *data = (struct asix_data *)&dev->data;
- u8 rx_ctl = 0x8c;
+ u16 rx_ctl = AX_DEFAULT_RX_CTL;
if (net->flags & IFF_PROMISC) {
- rx_ctl |= 0x01;
+ rx_ctl |= AX_RX_CTL_PRO;
} else if (net->flags & IFF_ALLMULTI
|| net->mc_count > AX_MAX_MCAST) {
- rx_ctl |= 0x02;
+ rx_ctl |= AX_RX_CTL_AMALL;
} else if (net->mc_count == 0) {
/* just broadcast and directed */
} else {
@@ -317,7 +558,7 @@ static void asix_set_multicast(struct net_device *net)
asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
AX_MCAST_FILTER_SIZE, data->multi_filter);
- rx_ctl |= 0x10;
+ rx_ctl |= AX_RX_CTL_AM;
}
asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
@@ -333,50 +574,43 @@ static int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
(__u16)loc, 2, (u16 *)&res);
asix_set_hw_mii(dev);
- return res & 0xffff;
-}
+ devdbg(dev, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x", phy_id, loc, le16_to_cpu(res & 0xffff));
-/* same as above, but converts resulting value to cpu byte order */
-static int asix_mdio_read_le(struct net_device *netdev, int phy_id, int loc)
-{
- return le16_to_cpu(asix_mdio_read(netdev,phy_id, loc));
+ return le16_to_cpu(res & 0xffff);
}
static void
asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
{
struct usbnet *dev = netdev_priv(netdev);
- u16 res = val;
+ u16 res = cpu_to_le16(val);
+ devdbg(dev, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x", phy_id, loc, val);
asix_set_sw_mii(dev);
asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
(__u16)loc, 2, (u16 *)&res);
asix_set_hw_mii(dev);
}
-/* same as above, but converts new value to le16 byte order before writing */
-static void
-asix_mdio_write_le(struct net_device *netdev, int phy_id, int loc, int val)
+/* Get the PHY Identifier from the PHYSID1 & PHYSID2 MII registers */
+static u32 asix_get_phyid(struct usbnet *dev)
{
- asix_mdio_write( netdev, phy_id, loc, cpu_to_le16(val) );
-}
+ int phy_reg;
+ u32 phy_id;
-static int ax88172_link_reset(struct usbnet *dev)
-{
- u16 lpa;
- u16 adv;
- u16 res;
- u8 mode;
+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
+ if (phy_reg < 0)
+ return 0;
- mode = AX_MEDIUM_TX_ABORT_ALLOW | AX_MEDIUM_FLOW_CONTROL_EN;
- lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
- adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
- res = mii_nway_result(lpa|adv);
- if (res & LPA_DUPLEX)
- mode |= AX_MEDIUM_FULL_DUPLEX;
- asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ phy_id = (phy_reg & 0xffff) << 16;
- return 0;
+ phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID2);
+ if (phy_reg < 0)
+ return 0;
+
+ phy_id |= (phy_reg & 0xffff);
+
+ return phy_id;
}
static void
@@ -423,7 +657,10 @@ asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
static int asix_get_eeprom_len(struct net_device *net)
{
- return AX_EEPROM_LEN;
+ struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ return data->eeprom_len;
}
static int asix_get_eeprom(struct net_device *net,
@@ -453,9 +690,14 @@ static int asix_get_eeprom(struct net_device *net,
static void asix_get_drvinfo (struct net_device *net,
struct ethtool_drvinfo *info)
{
+ struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
/* Inherit standard device info */
usbnet_get_drvinfo(net, info);
- info->eedump_len = 0x3e;
+ strncpy (info->driver, driver_name, sizeof info->driver);
+ strncpy (info->version, DRIVER_VERSION, sizeof info->version);
+ info->eedump_len = data->eeprom_len;
}
static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
@@ -468,8 +710,34 @@ static int asix_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
{
struct usbnet *dev = netdev_priv(net);
+ int res = mii_ethtool_sset(&dev->mii,cmd);
+
+ /* link speed/duplex might have changed */
+ if (dev->driver_info->link_reset)
+ dev->driver_info->link_reset(dev);
+
+ return res;
+}
+
+static int asix_nway_reset(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return mii_nway_restart(&dev->mii);
+}
+
+static u32 asix_get_link(struct net_device *net)
+{
+ struct usbnet *dev = netdev_priv(net);
- return mii_ethtool_sset(&dev->mii,cmd);
+ return mii_link_ok(&dev->mii);
+}
+
+static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+{
+ struct usbnet *dev = netdev_priv(net);
+
+ return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
}
/* We need to override some ethtool_ops so we require our
@@ -477,7 +745,8 @@ static int asix_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
devices that may be connected at the same time. */
static struct ethtool_ops ax88172_ethtool_ops = {
.get_drvinfo = asix_get_drvinfo,
- .get_link = ethtool_op_get_link,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_wol = asix_get_wol,
@@ -488,11 +757,66 @@ static struct ethtool_ops ax88172_ethtool_ops = {
.set_settings = asix_set_settings,
};
-static int asix_ioctl (struct net_device *net, struct ifreq *rq, int cmd)
+static void ax88172_set_multicast(struct net_device *net)
{
struct usbnet *dev = netdev_priv(net);
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u8 rx_ctl = 0x8c;
- return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+ if (net->flags & IFF_PROMISC) {
+ rx_ctl |= 0x01;
+ } else if (net->flags & IFF_ALLMULTI
+ || net->mc_count > AX_MAX_MCAST) {
+ rx_ctl |= 0x02;
+ } else if (net->mc_count == 0) {
+ /* just broadcast and directed */
+ } else {
+ /* We use the 20 byte dev->data
+ * for our 8 byte filter buffer
+ * to avoid allocating memory that
+ * is tricky to free later */
+ struct dev_mc_list *mc_list = net->mc_list;
+ u32 crc_bits;
+ int i;
+
+ memset(data->multi_filter, 0, AX_MCAST_FILTER_SIZE);
+
+ /* Build the multicast hash filter. */
+ for (i = 0; i < net->mc_count; i++) {
+ crc_bits =
+ ether_crc(ETH_ALEN,
+ mc_list->dmi_addr) >> 26;
+ data->multi_filter[crc_bits >> 3] |=
+ 1 << (crc_bits & 7);
+ mc_list = mc_list->next;
+ }
+
+ asix_write_cmd_async(dev, AX_CMD_WRITE_MULTI_FILTER, 0, 0,
+ AX_MCAST_FILTER_SIZE, data->multi_filter);
+
+ rx_ctl |= 0x10;
+ }
+
+ asix_write_cmd_async(dev, AX_CMD_WRITE_RX_CTL, rx_ctl, 0, 0, NULL);
+}
+
+static int ax88172_link_reset(struct usbnet *dev)
+{
+ u8 mode;
+ struct ethtool_cmd ecmd;
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88172_MEDIUM_DEFAULT;
+
+ if (ecmd.duplex != DUPLEX_FULL)
+ mode |= ~AX88172_MEDIUM_FD;
+
+ devdbg(dev, "ax88172_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ return 0;
}
static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
@@ -501,6 +825,9 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
void *buf;
int i;
unsigned long gpio_bits = dev->driver_info->data;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ data->eeprom_len = AX88172_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
@@ -519,12 +846,12 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
msleep(5);
}
- if ((ret = asix_write_rx_ctl(dev,0x80)) < 0)
+ if ((ret = asix_write_rx_ctl(dev, 0x80)) < 0)
goto out2;
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
- if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+ if ((ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
0, 0, 6, buf)) < 0) {
dbg("read AX_CMD_READ_NODE_ID failed: %d", ret);
goto out2;
@@ -537,14 +864,14 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
dev->mii.mdio_write = asix_mdio_write;
dev->mii.phy_id_mask = 0x3f;
dev->mii.reg_num_mask = 0x1f;
- dev->mii.phy_id = asix_get_phyid(dev);
+ dev->mii.phy_id = asix_get_phy_addr(dev);
dev->net->do_ioctl = asix_ioctl;
- dev->net->set_multicast_list = asix_set_multicast;
+ dev->net->set_multicast_list = ax88172_set_multicast;
dev->net->ethtool_ops = &ax88172_ethtool_ops;
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
mii_nway_restart(&dev->mii);
@@ -557,7 +884,8 @@ out1:
static struct ethtool_ops ax88772_ethtool_ops = {
.get_drvinfo = asix_get_drvinfo,
- .get_link = ethtool_op_get_link,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
.get_msglevel = usbnet_get_msglevel,
.set_msglevel = usbnet_set_msglevel,
.get_wol = asix_get_wol,
@@ -568,10 +896,37 @@ static struct ethtool_ops ax88772_ethtool_ops = {
.set_settings = asix_set_settings,
};
+static int ax88772_link_reset(struct usbnet *dev)
+{
+ u16 mode;
+ struct ethtool_cmd ecmd;
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88772_MEDIUM_DEFAULT;
+
+ if (ecmd.speed != SPEED_100)
+ mode &= ~AX_MEDIUM_PS;
+
+ if (ecmd.duplex != DUPLEX_FULL)
+ mode &= ~AX_MEDIUM_FD;
+
+ devdbg(dev, "ax88772_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ return 0;
+}
+
static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
{
int ret;
void *buf;
+ u16 rx_ctl;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u32 phyid;
+
+ data->eeprom_len = AX88772_EEPROM_LEN;
usbnet_get_endpoints(dev,intf);
@@ -582,13 +937,12 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
goto out1;
}
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
- 0x00B0, 0, 0, buf)) < 0)
+ if ((ret = asix_write_gpio(dev,
+ AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5)) < 0)
goto out2;
- msleep(5);
if ((ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT,
- 0x0001, 0, 0, buf)) < 0) {
+ 0x0000, 0, 0, buf)) < 0) {
dbg("Select PHY #1 failed: %d", ret);
goto out2;
}
@@ -605,36 +959,34 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
goto out2;
msleep(150);
- if ((ret = asix_write_rx_ctl(dev, 0x00)) < 0)
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x after software reset", rx_ctl);
+ if ((ret = asix_write_rx_ctl(dev, 0x0000)) < 0)
goto out2;
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x setting to 0x0000", rx_ctl);
+
/* Get the MAC address */
memset(buf, 0, ETH_ALEN);
- if ((ret = asix_read_cmd(dev, AX88772_CMD_READ_NODE_ID,
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
0, 0, ETH_ALEN, buf)) < 0) {
dbg("Failed to read MAC address: %d", ret);
goto out2;
}
memcpy(dev->net->dev_addr, buf, ETH_ALEN);
- if ((ret = asix_set_sw_mii(dev)) < 0)
- goto out2;
-
- if (((ret = asix_read_cmd(dev, AX_CMD_READ_MII_REG,
- 0x0010, 2, 2, buf)) < 0)
- || (*((u16 *)buf) != 0x003b)) {
- dbg("Read PHY register 2 must be 0x3b00: %d", ret);
- goto out2;
- }
-
/* Initialize MII structure */
dev->mii.dev = dev->net;
dev->mii.mdio_read = asix_mdio_read;
dev->mii.mdio_write = asix_mdio_write;
- dev->mii.phy_id_mask = 0xff;
- dev->mii.reg_num_mask = 0xff;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0x1f;
dev->net->do_ioctl = asix_ioctl;
- dev->mii.phy_id = asix_get_phyid(dev);
+ dev->mii.phy_id = asix_get_phy_addr(dev);
+
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
if ((ret = asix_sw_reset(dev, AX_SWRESET_PRL)) < 0)
goto out2;
@@ -649,16 +1001,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
dev->net->set_multicast_list = asix_set_multicast;
dev->net->ethtool_ops = &ax88772_ethtool_ops;
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
- asix_mdio_write_le(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_CSMA);
mii_nway_restart(&dev->mii);
- if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
- AX88772_MEDIUM_DEFAULT, 0, 0, buf)) < 0) {
- dbg("Write medium mode register: %d", ret);
+ if ((ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT)) < 0)
goto out2;
- }
if ((ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
@@ -666,13 +1015,17 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
dbg("Write IPG,IPG1,IPG2 failed: %d", ret);
goto out2;
}
- if ((ret = asix_set_hw_mii(dev)) < 0)
- goto out2;
/* Set RX_CTL to default values with 2k buffer, and enable cactus */
- if ((ret = asix_write_rx_ctl(dev, 0x0088)) < 0)
+ if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
goto out2;
+ rx_ctl = asix_read_rx_ctl(dev);
+ dbg("RX_CTL is 0x%04x after all initializations", rx_ctl);
+
+ rx_ctl = asix_read_medium_status(dev);
+ dbg("Medium Status is 0x%04x after all initializations", rx_ctl);
+
kfree(buf);
/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
@@ -690,120 +1043,285 @@ out1:
return ret;
}
-static int ax88772_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+static struct ethtool_ops ax88178_ethtool_ops = {
+ .get_drvinfo = asix_get_drvinfo,
+ .get_link = asix_get_link,
+ .nway_reset = asix_nway_reset,
+ .get_msglevel = usbnet_get_msglevel,
+ .set_msglevel = usbnet_set_msglevel,
+ .get_wol = asix_get_wol,
+ .set_wol = asix_set_wol,
+ .get_eeprom_len = asix_get_eeprom_len,
+ .get_eeprom = asix_get_eeprom,
+ .get_settings = asix_get_settings,
+ .set_settings = asix_set_settings,
+};
+
+static int marvell_phy_init(struct usbnet *dev)
{
- u8 *head;
- u32 header;
- char *packet;
- struct sk_buff *ax_skb;
- u16 size;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ u16 reg;
- head = (u8 *) skb->data;
- memcpy(&header, head, sizeof(header));
- le32_to_cpus(&header);
- packet = head + sizeof(header);
+ devdbg(dev,"marvell_phy_init()");
- skb_pull(skb, 4);
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_MARVELL_STATUS);
+ devdbg(dev,"MII_MARVELL_STATUS = 0x%04x", reg);
- while (skb->len > 0) {
- if ((short)(header & 0x0000ffff) !=
- ~((short)((header & 0xffff0000) >> 16))) {
- devdbg(dev,"header length data is error");
- }
- /* get the packet length */
- size = (u16) (header & 0x0000ffff);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_MARVELL_CTRL,
+ MARVELL_CTRL_RXDELAY | MARVELL_CTRL_TXDELAY);
- if ((skb->len) - ((size + 1) & 0xfffe) == 0)
- return 2;
- if (size > ETH_FRAME_LEN) {
- devdbg(dev,"invalid rx length %d", size);
- return 0;
- }
- ax_skb = skb_clone(skb, GFP_ATOMIC);
- if (ax_skb) {
- ax_skb->len = size;
- ax_skb->data = packet;
- ax_skb->tail = packet + size;
- usbnet_skb_return(dev, ax_skb);
- } else {
- return 0;
- }
+ if (data->ledmode) {
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL);
+ devdbg(dev,"MII_MARVELL_LED_CTRL (1) = 0x%04x", reg);
- skb_pull(skb, (size + 1) & 0xfffe);
+ reg &= 0xf8ff;
+ reg |= (1 + 0x0100);
+ asix_mdio_write(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL, reg);
- if (skb->len == 0)
- break;
+ reg = asix_mdio_read(dev->net, dev->mii.phy_id,
+ MII_MARVELL_LED_CTRL);
+ devdbg(dev,"MII_MARVELL_LED_CTRL (2) = 0x%04x", reg);
+ reg &= 0xfc0f;
+ }
- head = (u8 *) skb->data;
- memcpy(&header, head, sizeof(header));
- le32_to_cpus(&header);
- packet = head + sizeof(header);
- skb_pull(skb, 4);
+ return 0;
+}
+
+static int marvell_led_status(struct usbnet *dev, u16 speed)
+{
+ u16 reg = asix_mdio_read(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL);
+
+ devdbg(dev, "marvell_led_status() read 0x%04x", reg);
+
+ /* Clear out the center LED bits - 0x03F0 */
+ reg &= 0xfc0f;
+
+ switch (speed) {
+ case SPEED_1000:
+ reg |= 0x03e0;
+ break;
+ case SPEED_100:
+ reg |= 0x03b0;
+ break;
+ default:
+ reg |= 0x02f0;
}
- if (skb->len < 0) {
- devdbg(dev,"invalid rx length %d", skb->len);
- return 0;
+ devdbg(dev, "marvell_led_status() writing 0x%04x", reg);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MARVELL_LED_MANUAL, reg);
+
+ return 0;
+}
+
+static int ax88178_link_reset(struct usbnet *dev)
+{
+ u16 mode;
+ struct ethtool_cmd ecmd;
+ struct asix_data *data = (struct asix_data *)&dev->data;
+
+ devdbg(dev,"ax88178_link_reset()");
+
+ mii_check_media(&dev->mii, 1, 1);
+ mii_ethtool_gset(&dev->mii, &ecmd);
+ mode = AX88178_MEDIUM_DEFAULT;
+
+ if (ecmd.speed == SPEED_1000)
+ mode |= AX_MEDIUM_GM | AX_MEDIUM_ENCK;
+ else if (ecmd.speed == SPEED_100)
+ mode |= AX_MEDIUM_PS;
+ else
+ mode &= ~(AX_MEDIUM_PS | AX_MEDIUM_GM);
+
+ if (ecmd.duplex == DUPLEX_FULL)
+ mode |= AX_MEDIUM_FD;
+ else
+ mode &= ~AX_MEDIUM_FD;
+
+ devdbg(dev, "ax88178_link_reset() speed: %d duplex: %d setting mode to 0x%04x", ecmd.speed, ecmd.duplex, mode);
+
+ asix_write_medium_mode(dev, mode);
+
+ if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
+ marvell_led_status(dev, ecmd.speed);
+
+ return 0;
+}
+
+static void ax88178_set_mfb(struct usbnet *dev)
+{
+ u16 mfb = AX_RX_CTL_MFB_16384;
+ u16 rxctl;
+ u16 medium;
+ int old_rx_urb_size = dev->rx_urb_size;
+
+ if (dev->hard_mtu < 2048) {
+ dev->rx_urb_size = 2048;
+ mfb = AX_RX_CTL_MFB_2048;
+ } else if (dev->hard_mtu < 4096) {
+ dev->rx_urb_size = 4096;
+ mfb = AX_RX_CTL_MFB_4096;
+ } else if (dev->hard_mtu < 8192) {
+ dev->rx_urb_size = 8192;
+ mfb = AX_RX_CTL_MFB_8192;
+ } else if (dev->hard_mtu < 16384) {
+ dev->rx_urb_size = 16384;
+ mfb = AX_RX_CTL_MFB_16384;
}
- return 1;
+
+ rxctl = asix_read_rx_ctl(dev);
+ asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
+
+ medium = asix_read_medium_status(dev);
+ if (dev->net->mtu > 1500)
+ medium |= AX_MEDIUM_JFE;
+ else
+ medium &= ~AX_MEDIUM_JFE;
+ asix_write_medium_mode(dev, medium);
+
+ if (dev->rx_urb_size > old_rx_urb_size)
+ usbnet_unlink_rx_urbs(dev);
}
-static struct sk_buff *ax88772_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
- gfp_t flags)
+static int ax88178_change_mtu(struct net_device *net, int new_mtu)
{
- int padlen;
- int headroom = skb_headroom(skb);
- int tailroom = skb_tailroom(skb);
- u32 packet_len;
- u32 padbytes = 0xffff0000;
+ struct usbnet *dev = netdev_priv(net);
+ int ll_mtu = new_mtu + net->hard_header_len + 4;
- padlen = ((skb->len + 4) % 512) ? 0 : 4;
+ devdbg(dev, "ax88178_change_mtu() new_mtu=%d", new_mtu);
- if ((!skb_cloned(skb))
- && ((headroom + tailroom) >= (4 + padlen))) {
- if ((headroom < 4) || (tailroom < padlen)) {
- skb->data = memmove(skb->head + 4, skb->data, skb->len);
- skb->tail = skb->data + skb->len;
- }
+ if (new_mtu <= 0 || ll_mtu > 16384)
+ return -EINVAL;
+
+ if ((ll_mtu % dev->maxpacket) == 0)
+ return -EDOM;
+
+ net->mtu = new_mtu;
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+ ax88178_set_mfb(dev);
+
+ return 0;
+}
+
+static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+ struct asix_data *data = (struct asix_data *)&dev->data;
+ int ret;
+ void *buf;
+ u16 eeprom;
+ int gpio0 = 0;
+ u32 phyid;
+
+ usbnet_get_endpoints(dev,intf);
+
+ buf = kmalloc(6, GFP_KERNEL);
+ if(!buf) {
+ dbg ("Cannot allocate memory for buffer");
+ ret = -ENOMEM;
+ goto out1;
+ }
+
+ eeprom = 0;
+ asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &eeprom);
+ dbg("GPIO Status: 0x%04x", eeprom);
+
+ asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
+ asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
+ asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
+
+ dbg("EEPROM index 0x17 is 0x%04x", eeprom);
+
+ if (eeprom == 0xffff) {
+ data->phymode = PHY_MODE_MARVELL;
+ data->ledmode = 0;
+ gpio0 = 1;
} else {
- struct sk_buff *skb2;
- skb2 = skb_copy_expand(skb, 4, padlen, flags);
- dev_kfree_skb_any(skb);
- skb = skb2;
- if (!skb)
- return NULL;
+ data->phymode = eeprom & 7;
+ data->ledmode = eeprom >> 8;
+ gpio0 = (eeprom & 0x80) ? 0 : 1;
}
+ dbg("GPIO0: %d, PhyMode: %d", gpio0, data->phymode);
- skb_push(skb, 4);
- packet_len = (((skb->len - 4) ^ 0x0000ffff) << 16) + (skb->len - 4);
- memcpy(skb->data, &packet_len, sizeof(packet_len));
+ asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
+ if ((eeprom >> 8) != 1) {
+ asix_write_gpio(dev, 0x003c, 30);
+ asix_write_gpio(dev, 0x001c, 300);
+ asix_write_gpio(dev, 0x003c, 30);
+ } else {
+ dbg("gpio phymode == 1 path");
+ asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
+ asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
+ }
- if ((skb->len % 512) == 0) {
- memcpy( skb->tail, &padbytes, sizeof(padbytes));
- skb_put(skb, sizeof(padbytes));
+ asix_sw_reset(dev, 0);
+ msleep(150);
+
+ asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+ msleep(150);
+
+ asix_write_rx_ctl(dev, 0);
+
+ /* Get the MAC address */
+ memset(buf, 0, ETH_ALEN);
+ if ((ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
+ 0, 0, ETH_ALEN, buf)) < 0) {
+ dbg("Failed to read MAC address: %d", ret);
+ goto out2;
}
- return skb;
-}
+ memcpy(dev->net->dev_addr, buf, ETH_ALEN);
-static int ax88772_link_reset(struct usbnet *dev)
-{
- u16 lpa;
- u16 adv;
- u16 res;
- u16 mode;
+ /* Initialize MII structure */
+ dev->mii.dev = dev->net;
+ dev->mii.mdio_read = asix_mdio_read;
+ dev->mii.mdio_write = asix_mdio_write;
+ dev->mii.phy_id_mask = 0x1f;
+ dev->mii.reg_num_mask = 0xff;
+ dev->mii.supports_gmii = 1;
+ dev->net->do_ioctl = asix_ioctl;
+ dev->mii.phy_id = asix_get_phy_addr(dev);
+ dev->net->set_multicast_list = asix_set_multicast;
+ dev->net->ethtool_ops = &ax88178_ethtool_ops;
+ dev->net->change_mtu = &ax88178_change_mtu;
- mode = AX88772_MEDIUM_DEFAULT;
- lpa = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_LPA);
- adv = asix_mdio_read_le(dev->net, dev->mii.phy_id, MII_ADVERTISE);
- res = mii_nway_result(lpa|adv);
+ phyid = asix_get_phyid(dev);
+ dbg("PHYID=0x%08x", phyid);
+
+ if (data->phymode == PHY_MODE_MARVELL) {
+ marvell_phy_init(dev);
+ msleep(60);
+ }
+
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR,
+ BMCR_RESET | BMCR_ANENABLE);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+ ADVERTISE_ALL | ADVERTISE_CSMA | ADVERTISE_PAUSE_CAP);
+ asix_mdio_write(dev->net, dev->mii.phy_id, MII_CTRL1000,
+ ADVERTISE_1000FULL);
+
+ mii_nway_restart(&dev->mii);
+
+ if ((ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT)) < 0)
+ goto out2;
- if ((res & LPA_DUPLEX) == 0)
- mode &= ~AX88772_MEDIUM_FULL_DUPLEX;
- if ((res & LPA_100) == 0)
- mode &= ~AX88772_MEDIUM_100MB;
- asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+ if ((ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL)) < 0)
+ goto out2;
+
+ kfree(buf);
+
+ /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
+ if (dev->driver_info->flags & FLAG_FRAMING_AX) {
+ /* hard_mtu is still the default - the device does not support
+ jumbo eth frames */
+ dev->rx_urb_size = 2048;
+ }
return 0;
+
+out2:
+ kfree(buf);
+out1:
+ return ret;
}
static const struct driver_info ax8817x_info = {
@@ -853,8 +1371,19 @@ static const struct driver_info ax88772_info = {
.link_reset = ax88772_link_reset,
.reset = ax88772_link_reset,
.flags = FLAG_ETHER | FLAG_FRAMING_AX,
- .rx_fixup = ax88772_rx_fixup,
- .tx_fixup = ax88772_tx_fixup,
+ .rx_fixup = asix_rx_fixup,
+ .tx_fixup = asix_tx_fixup,
+};
+
+static const struct driver_info ax88178_info = {
+ .description = "ASIX AX88178 USB 2.0 Ethernet",
+ .bind = ax88178_bind,
+ .status = asix_status,
+ .link_reset = ax88178_link_reset,
+ .reset = ax88178_link_reset,
+ .flags = FLAG_ETHER | FLAG_FRAMING_AX,
+ .rx_fixup = asix_rx_fixup,
+ .tx_fixup = asix_tx_fixup,
};
static const struct usb_device_id products [] = {
@@ -913,7 +1442,7 @@ static const struct usb_device_id products [] = {
}, {
// ASIX AX88178 10/100/1000
USB_DEVICE (0x0b95, 0x1780),
- .driver_info = (unsigned long) &ax88772_info,
+ .driver_info = (unsigned long) &ax88178_info,
}, {
// Linksys USB200M Rev 2
USB_DEVICE (0x13b1, 0x0018),
@@ -922,6 +1451,18 @@ static const struct usb_device_id products [] = {
// 0Q0 cable ethernet
USB_DEVICE (0x1557, 0x7720),
.driver_info = (unsigned long) &ax88772_info,
+}, {
+ // DLink DUB-E100 H/W Ver B1
+ USB_DEVICE (0x07d1, 0x3c05),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
+ // DLink DUB-E100 H/W Ver B1 Alternate
+ USB_DEVICE (0x2001, 0x3c05),
+ .driver_info = (unsigned long) &ax88772_info,
+}, {
+ // Linksys USB1000
+ USB_DEVICE (0x1737, 0x0039),
+ .driver_info = (unsigned long) &ax88178_info,
},
{ }, // END
};
diff --git a/drivers/usb/net/kaweth.c b/drivers/usb/net/kaweth.c
index def3bb8e229..544d41fe9b9 100644
--- a/drivers/usb/net/kaweth.c
+++ b/drivers/usb/net/kaweth.c
@@ -165,6 +165,7 @@ static struct usb_device_id usb_klsi_table[] = {
{ USB_DEVICE(0x1645, 0x0005) }, /* Entrega E45 */
{ USB_DEVICE(0x1645, 0x0008) }, /* Entrega USB Ethernet Adapter */
{ USB_DEVICE(0x1645, 0x8005) }, /* PortGear Ethernet Adapter */
+ { USB_DEVICE(0x1668, 0x0323) }, /* Actiontec USB Ethernet */
{ USB_DEVICE(0x2001, 0x4000) }, /* D-link DSB-650C */
{} /* Null terminator */
};
diff --git a/drivers/usb/net/net1080.c b/drivers/usb/net/net1080.c
index a9b6eeac3e3..301baa72bac 100644
--- a/drivers/usb/net/net1080.c
+++ b/drivers/usb/net/net1080.c
@@ -498,25 +498,24 @@ static int net1080_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
static struct sk_buff *
net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
{
- int padlen;
struct sk_buff *skb2;
struct nc_header *header = NULL;
struct nc_trailer *trailer = NULL;
+ int padlen = sizeof (struct nc_trailer);
int len = skb->len;
- padlen = ((len + sizeof (struct nc_header)
- + sizeof (struct nc_trailer)) & 0x01) ? 0 : 1;
+ if (!((len + padlen + sizeof (struct nc_header)) & 0x01))
+ padlen++;
if (!skb_cloned(skb)) {
int headroom = skb_headroom(skb);
int tailroom = skb_tailroom(skb);
- if ((padlen + sizeof (struct nc_trailer)) <= tailroom
- && sizeof (struct nc_header) <= headroom)
+ if (padlen <= tailroom &&
+ sizeof(struct nc_header) <= headroom)
/* There's enough head and tail room */
goto encapsulate;
- if ((sizeof (struct nc_header) + padlen
- + sizeof (struct nc_trailer)) <
+ if ((sizeof (struct nc_header) + padlen) <
(headroom + tailroom)) {
/* There's enough total room, so just readjust */
skb->data = memmove(skb->head
@@ -530,7 +529,7 @@ net1080_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
/* Create a new skb to use with the correct size */
skb2 = skb_copy_expand(skb,
sizeof (struct nc_header),
- sizeof (struct nc_trailer) + padlen,
+ padlen,
flags);
dev_kfree_skb_any(skb);
if (!skb2)
diff --git a/drivers/usb/net/pegasus.c b/drivers/usb/net/pegasus.c
index ab21f960d25..918cf5a77c0 100644
--- a/drivers/usb/net/pegasus.c
+++ b/drivers/usb/net/pegasus.c
@@ -45,7 +45,7 @@
/*
* Version Information
*/
-#define DRIVER_VERSION "v0.6.13 (2005/11/13)"
+#define DRIVER_VERSION "v0.6.14 (2006/09/27)"
#define DRIVER_AUTHOR "Petko Manolov <petkan@users.sourceforge.net>"
#define DRIVER_DESC "Pegasus/Pegasus II USB Ethernet driver"
@@ -339,7 +339,7 @@ static int read_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 * regd)
}
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return ret;
}
@@ -376,7 +376,7 @@ static int write_mii_word(pegasus_t * pegasus, __u8 phy, __u8 indx, __u16 regd)
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
@@ -413,7 +413,7 @@ static int read_eprom_word(pegasus_t * pegasus, __u8 index, __u16 * retdata)
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
@@ -461,7 +461,7 @@ static int write_eprom_word(pegasus_t * pegasus, __u8 index, __u16 data)
return ret;
fail:
if (netif_msg_drv(pegasus))
- dev_warn(&pegasus->intf->dev, "fail %s\n", __FUNCTION__);
+ dev_warn(&pegasus->intf->dev, "%s failed\n", __FUNCTION__);
return -ETIMEDOUT;
}
#endif /* PEGASUS_WRITE_EEPROM */
@@ -481,8 +481,12 @@ static void set_ethernet_addr(pegasus_t * pegasus)
{
__u8 node_id[6];
- get_node_id(pegasus, node_id);
- set_registers(pegasus, EthID, sizeof (node_id), node_id);
+ if (pegasus->features & PEGASUS_II) {
+ get_registers(pegasus, 0x10, sizeof(node_id), node_id);
+ } else {
+ get_node_id(pegasus, node_id);
+ set_registers(pegasus, EthID, sizeof (node_id), node_id);
+ }
memcpy(pegasus->net->dev_addr, node_id, sizeof (node_id));
}
@@ -619,7 +623,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
switch (urb->status) {
case 0:
break;
- case -ETIMEDOUT:
+ case -ETIME:
if (netif_msg_rx_err(pegasus))
pr_debug("%s: reset MAC\n", net->name);
pegasus->flags &= ~PEGASUS_RX_BUSY;
diff --git a/drivers/usb/net/rtl8150.c b/drivers/usb/net/rtl8150.c
index a72685b9606..2364c209938 100644
--- a/drivers/usb/net/rtl8150.c
+++ b/drivers/usb/net/rtl8150.c
@@ -438,7 +438,7 @@ static void read_bulk_callback(struct urb *urb, struct pt_regs *regs)
break;
case -ENOENT:
return; /* the urb is in unlink state */
- case -ETIMEDOUT:
+ case -ETIME:
warn("may be reset is needed?..");
goto goon;
default:
diff --git a/drivers/usb/net/usbnet.c b/drivers/usb/net/usbnet.c
index 54183e173a6..98a522f1e26 100644
--- a/drivers/usb/net/usbnet.c
+++ b/drivers/usb/net/usbnet.c
@@ -61,8 +61,11 @@
* let the USB host controller be busy for 5msec or more before an irq
* is required, under load. Jumbograms change the equation.
*/
-#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
-#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? 60 : 4)
+#define RX_MAX_QUEUE_MEMORY (60 * 1518)
+#define RX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->rx_urb_size) : 4)
+#define TX_QLEN(dev) (((dev)->udev->speed == USB_SPEED_HIGH) ? \
+ (RX_MAX_QUEUE_MEMORY/(dev)->hard_mtu) : 4)
// reawaken network queue this soon after stopping; else watchdog barks
#define TX_TIMEOUT_JIFFIES (5*HZ)
@@ -227,13 +230,23 @@ static int usbnet_change_mtu (struct net_device *net, int new_mtu)
{
struct usbnet *dev = netdev_priv(net);
int ll_mtu = new_mtu + net->hard_header_len;
+ int old_hard_mtu = dev->hard_mtu;
+ int old_rx_urb_size = dev->rx_urb_size;
- if (new_mtu <= 0 || ll_mtu > dev->hard_mtu)
+ if (new_mtu <= 0)
return -EINVAL;
// no second zero-length packet read wanted after mtu-sized packets
if ((ll_mtu % dev->maxpacket) == 0)
return -EDOM;
net->mtu = new_mtu;
+
+ dev->hard_mtu = net->mtu + net->hard_header_len;
+ if (dev->rx_urb_size == old_hard_mtu) {
+ dev->rx_urb_size = dev->hard_mtu;
+ if (dev->rx_urb_size > old_rx_urb_size)
+ usbnet_unlink_rx_urbs(dev);
+ }
+
return 0;
}
@@ -412,9 +425,9 @@ static void rx_complete (struct urb *urb, struct pt_regs *regs)
// we get controller i/o faults during khubd disconnect() delays.
// throttle down resubmits, to avoid log floods; just temporarily,
// so we still recover when the fault isn't a khubd delay.
- case -EPROTO: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
dev->stats.rx_errors++;
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay, jiffies + THROTTLE_JIFFIES);
@@ -521,6 +534,17 @@ static int unlink_urbs (struct usbnet *dev, struct sk_buff_head *q)
return count;
}
+// Flush all pending rx urbs
+// minidrivers may need to do this when the MTU changes
+
+void usbnet_unlink_rx_urbs(struct usbnet *dev)
+{
+ if (netif_running(dev->net)) {
+ (void) unlink_urbs (dev, &dev->rxq);
+ tasklet_schedule(&dev->bh);
+ }
+}
+EXPORT_SYMBOL_GPL(usbnet_unlink_rx_urbs);
/*-------------------------------------------------------------------------*/
@@ -629,7 +653,7 @@ static int usbnet_open (struct net_device *net)
devinfo (dev, "open: enable queueing "
"(rx %d, tx %d) mtu %d %s framing",
- RX_QLEN (dev), TX_QLEN (dev), dev->net->mtu,
+ (int)RX_QLEN (dev), (int)TX_QLEN (dev), dev->net->mtu,
framing);
}
@@ -797,9 +821,9 @@ static void tx_complete (struct urb *urb, struct pt_regs *regs)
// like rx, tx gets controller i/o faults during khubd delays
// and so it uses the same throttling mechanism.
- case -EPROTO: // ehci
- case -ETIMEDOUT: // ohci
- case -EILSEQ: // uhci
+ case -EPROTO:
+ case -ETIME:
+ case -EILSEQ:
if (!timer_pending (&dev->delay)) {
mod_timer (&dev->delay,
jiffies + THROTTLE_JIFFIES);
diff --git a/drivers/usb/net/usbnet.h b/drivers/usb/net/usbnet.h
index 89fc4958eec..c0746f0454a 100644
--- a/drivers/usb/net/usbnet.h
+++ b/drivers/usb/net/usbnet.h
@@ -166,6 +166,7 @@ struct skb_data { /* skb->cb is one of these */
extern int usbnet_get_endpoints(struct usbnet *, struct usb_interface *);
extern void usbnet_defer_kevent (struct usbnet *, int);
extern void usbnet_skb_return (struct usbnet *, struct sk_buff *);
+extern void usbnet_unlink_rx_urbs(struct usbnet *);
extern u32 usbnet_get_msglevel (struct net_device *);
extern void usbnet_set_msglevel (struct net_device *, u32);
diff --git a/drivers/usb/serial/Kconfig b/drivers/usb/serial/Kconfig
index f5b9438c94f..5076b9d9705 100644
--- a/drivers/usb/serial/Kconfig
+++ b/drivers/usb/serial/Kconfig
@@ -53,6 +53,15 @@ config USB_SERIAL_GENERIC
support" be compiled as a module for this driver to be used
properly.
+config USB_SERIAL_AIRCABLE
+ tristate "AIRcable USB Bluetooth Dongle Driver (EXPERIMENTAL)"
+ depends on USB_SERIAL && EXPERIMENTAL
+ help
+ Say Y here if you want to use AIRcable USB Bluetoot Dongle.
+
+ To compile this driver as a module, choose M here: the module
+ will be called aircable.
+
config USB_SERIAL_AIRPRIME
tristate "USB AirPrime CDMA Wireless Driver"
depends on USB_SERIAL
@@ -413,6 +422,21 @@ config USB_SERIAL_MCT_U232
To compile this driver as a module, choose M here: the
module will be called mct_u232.
+config USB_SERIAL_MOS7840
+ tristate "USB Moschip 7840/7820 USB Serial Driver"
+ depends on USB_SERIAL
+ ---help---
+ Say Y here if you want to use a MCS7840 Quad-Serial or MCS7820
+ Dual-Serial port device from MosChip Semiconductor.
+
+ The MCS7840 and MCS7820 have been developed to connect a wide range
+ of standard serial devices to a USB host. The MCS7840 has a USB
+ device controller connected to four (4) individual UARTs while the
+ MCS7820 controller connects to two (2) individual UARTs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mos7840. If unsure, choose N.
+
config USB_SERIAL_NAVMAN
tristate "USB Navman GPS device"
depends on USB_SERIAL
@@ -526,5 +550,6 @@ config USB_EZUSB
depends on USB_SERIAL_KEYSPAN_PDA || USB_SERIAL_XIRCOM || USB_SERIAL_KEYSPAN || USB_SERIAL_WHITEHEAT
default y
+
endmenu
diff --git a/drivers/usb/serial/Makefile b/drivers/usb/serial/Makefile
index 8efed2ce1ba..8dce83340e3 100644
--- a/drivers/usb/serial/Makefile
+++ b/drivers/usb/serial/Makefile
@@ -11,6 +11,7 @@ usbserial-obj-$(CONFIG_USB_EZUSB) += ezusb.o
usbserial-objs := usb-serial.o generic.o bus.o $(usbserial-obj-y)
+obj-$(CONFIG_USB_SERIAL_AIRCABLE) += aircable.o
obj-$(CONFIG_USB_SERIAL_AIRPRIME) += airprime.o
obj-$(CONFIG_USB_SERIAL_ARK3116) += ark3116.o
obj-$(CONFIG_USB_SERIAL_BELKIN) += belkin_sa.o
@@ -33,6 +34,7 @@ obj-$(CONFIG_USB_SERIAL_KEYSPAN_PDA) += keyspan_pda.o
obj-$(CONFIG_USB_SERIAL_KLSI) += kl5kusb105.o
obj-$(CONFIG_USB_SERIAL_KOBIL_SCT) += kobil_sct.o
obj-$(CONFIG_USB_SERIAL_MCT_U232) += mct_u232.o
+obj-$(CONFIG_USB_SERIAL_MOS7840) += mos7840.o
obj-$(CONFIG_USB_SERIAL_NAVMAN) += navman.o
obj-$(CONFIG_USB_SERIAL_OMNINET) += omninet.o
obj-$(CONFIG_USB_SERIAL_OPTION) += option.o
diff --git a/drivers/usb/serial/aircable.c b/drivers/usb/serial/aircable.c
new file mode 100644
index 00000000000..2ccd9ded52a
--- /dev/null
+++ b/drivers/usb/serial/aircable.c
@@ -0,0 +1,625 @@
+/*
+ * AIRcable USB Bluetooth Dongle Driver.
+ *
+ * Copyright (C) 2006 Manuel Francisco Naranjo (naranjo.manuel@gmail.com)
+ * 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 device works as an standard CDC device, it has 2 interfaces, the first
+ * one is for firmware access and the second is the serial one.
+ * The protocol is very simply, there are two posibilities reading or writing.
+ * When writting the first urb must have a Header that starts with 0x20 0x29 the
+ * next two bytes must say how much data will be sended.
+ * When reading the process is almost equal except that the header starts with
+ * 0x00 0x20.
+ *
+ * The device simply need some stuff to understand data comming from the usb
+ * buffer: The First and Second byte is used for a Header, the Third and Fourth
+ * tells the device the amount of information the package holds.
+ * Packages are 60 bytes long Header Stuff.
+ * When writting to the device the first two bytes of the header are 0x20 0x29
+ * When reading the bytes are 0x00 0x20, or 0x00 0x10, there is an strange
+ * situation, when too much data arrives to the device because it sends the data
+ * but with out the header. I will use a simply hack to override this situation,
+ * if there is data coming that does not contain any header, then that is data
+ * that must go directly to the tty, as there is no documentation about if there
+ * is any other control code, I will simply check for the first
+ * one.
+ *
+ * The driver registers himself with the USB-serial core and the USB Core. I had
+ * to implement a probe function agains USB-serial, because other way, the
+ * driver was attaching himself to both interfaces. I have tryed with different
+ * configurations of usb_serial_driver with out exit, only the probe function
+ * could handle this correctly.
+ *
+ * I have taken some info from a Greg Kroah-Hartman article:
+ * http://www.linuxjournal.com/article/6573
+ * And from Linux Device Driver Kit CD, which is a great work, the authors taken
+ * the work to recompile lots of information an knowladge in drivers development
+ * and made it all avaible inside a cd.
+ * URL: http://kernel.org/pub/linux/kernel/people/gregkh/ddk/
+ *
+ */
+
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/circ_buf.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+
+static int debug;
+
+/* Vendor and Product ID */
+#define AIRCABLE_VID 0x16CA
+#define AIRCABLE_USB_PID 0x1502
+
+/* write buffer size defines */
+#define AIRCABLE_BUF_SIZE 2048
+
+/* Protocol Stuff */
+#define HCI_HEADER_LENGTH 0x4
+#define TX_HEADER_0 0x20
+#define TX_HEADER_1 0x29
+#define RX_HEADER_0 0x00
+#define RX_HEADER_1 0x20
+#define MAX_HCI_FRAMESIZE 60
+#define HCI_COMPLETE_FRAME 64
+
+/* rx_flags */
+#define THROTTLED 0x01
+#define ACTUALLY_THROTTLED 0x02
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "v1.0b2"
+#define DRIVER_AUTHOR "Naranjo, Manuel Francisco <naranjo.manuel@gmail.com>"
+#define DRIVER_DESC "AIRcable USB Driver"
+
+/* ID table that will be registered with USB core */
+static struct usb_device_id id_table [] = {
+ { USB_DEVICE(AIRCABLE_VID, AIRCABLE_USB_PID) },
+ { },
+};
+MODULE_DEVICE_TABLE(usb, id_table);
+
+
+/* Internal Structure */
+struct aircable_private {
+ spinlock_t rx_lock; /* spinlock for the receive lines */
+ struct circ_buf *tx_buf; /* write buffer */
+ struct circ_buf *rx_buf; /* read buffer */
+ int rx_flags; /* for throttilng */
+ struct work_struct rx_work; /* work cue for the receiving line */
+};
+
+/* Private methods */
+
+/* Circular Buffer Methods, code from ti_usb_3410_5052 used */
+/*
+ * serial_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void serial_buf_clear(struct circ_buf *cb)
+{
+ cb->head = cb->tail = 0;
+}
+
+/*
+ * serial_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct circ_buf *serial_buf_alloc(void)
+{
+ struct circ_buf *cb;
+ cb = kmalloc(sizeof(struct circ_buf), GFP_KERNEL);
+ if (cb == NULL)
+ return NULL;
+ cb->buf = kmalloc(AIRCABLE_BUF_SIZE, GFP_KERNEL);
+ if (cb->buf == NULL) {
+ kfree(cb);
+ return NULL;
+ }
+ serial_buf_clear(cb);
+ return cb;
+}
+
+/*
+ * serial_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void serial_buf_free(struct circ_buf *cb)
+{
+ kfree(cb->buf);
+ kfree(cb);
+}
+
+/*
+ * serial_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static int serial_buf_data_avail(struct circ_buf *cb)
+{
+ return CIRC_CNT(cb->head,cb->tail,AIRCABLE_BUF_SIZE);
+}
+
+/*
+ * serial_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_put(struct circ_buf *cb, const char *buf, int count)
+{
+ int c, ret = 0;
+ while (1) {
+ c = CIRC_SPACE_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(cb->buf + cb->head, buf, c);
+ cb->head = (cb->head + c) & (AIRCABLE_BUF_SIZE-1);
+ buf += c;
+ count -= c;
+ ret= c;
+ }
+ return ret;
+}
+
+/*
+ * serial_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static int serial_buf_get(struct circ_buf *cb, char *buf, int count)
+{
+ int c, ret = 0;
+ while (1) {
+ c = CIRC_CNT_TO_END(cb->head, cb->tail, AIRCABLE_BUF_SIZE);
+ if (count < c)
+ c = count;
+ if (c <= 0)
+ break;
+ memcpy(buf, cb->buf + cb->tail, c);
+ cb->tail = (cb->tail + c) & (AIRCABLE_BUF_SIZE-1);
+ buf += c;
+ count -= c;
+ ret= c;
+ }
+ return ret;
+}
+
+/* End of circula buffer methods */
+
+static void aircable_send(struct usb_serial_port *port)
+{
+ int count, result;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ unsigned char* buf;
+ dbg("%s - port %d", __FUNCTION__, port->number);
+ if (port->write_urb_busy)
+ return;
+
+ count = min(serial_buf_data_avail(priv->tx_buf), MAX_HCI_FRAMESIZE);
+ if (count == 0)
+ return;
+
+ buf = kzalloc(count + HCI_HEADER_LENGTH, GFP_ATOMIC);
+ if (!buf) {
+ err("%s- kzalloc(%d) failed.", __FUNCTION__,
+ count + HCI_HEADER_LENGTH);
+ return;
+ }
+
+ buf[0] = TX_HEADER_0;
+ buf[1] = TX_HEADER_1;
+ buf[2] = (unsigned char)count;
+ buf[3] = (unsigned char)(count >> 8);
+ serial_buf_get(priv->tx_buf,buf + HCI_HEADER_LENGTH, MAX_HCI_FRAMESIZE);
+
+ memcpy(port->write_urb->transfer_buffer, buf,
+ count + HCI_HEADER_LENGTH);
+
+ kfree(buf);
+ port->write_urb_busy = 1;
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ count + HCI_HEADER_LENGTH,
+ port->write_urb->transfer_buffer);
+ port->write_urb->transfer_buffer_length = count + HCI_HEADER_LENGTH;
+ port->write_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
+
+ if (result) {
+ dev_err(&port->dev,
+ "%s - failed submitting write urb, error %d\n",
+ __FUNCTION__, result);
+ port->write_urb_busy = 0;
+ }
+
+ schedule_work(&port->work);
+}
+
+static void aircable_read(void *params)
+{
+ struct usb_serial_port *port = params;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned char *data;
+ int count;
+ if (priv->rx_flags & THROTTLED){
+ if (priv->rx_flags & ACTUALLY_THROTTLED)
+ schedule_work(&priv->rx_work);
+ return;
+ }
+
+ /* By now I will flush data to the tty in packages of no more than
+ * 64 bytes, to ensure I do not get throttled.
+ * Ask USB mailing list for better aproach.
+ */
+ tty = port->tty;
+
+ if (!tty)
+ schedule_work(&priv->rx_work);
+
+ count = min(64, serial_buf_data_avail(priv->rx_buf));
+
+ if (count <= 0)
+ return; //We have finished sending everything.
+
+ tty_prepare_flip_string(tty, &data, count);
+ if (!data){
+ err("%s- kzalloc(%d) failed.", __FUNCTION__, count);
+ return;
+ }
+
+ serial_buf_get(priv->rx_buf, data, count);
+
+ tty_flip_buffer_push(tty);
+
+ if (serial_buf_data_avail(priv->rx_buf))
+ schedule_work(&priv->rx_work);
+
+ return;
+}
+/* End of private methods */
+
+static int aircable_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+ struct usb_host_interface *iface_desc = serial->interface->cur_altsetting;
+ struct usb_endpoint_descriptor *endpoint;
+ int num_bulk_out=0;
+ int i;
+
+ for (i = 0; i < iface_desc->desc.bNumEndpoints; i++) {
+ endpoint = &iface_desc->endpoint[i].desc;
+ if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
+ ((endpoint->bmAttributes & 3) == 0x02)) {
+ /* we found our bulk out endpoint */
+ dbg("found bulk out on endpoint %d", i);
+ ++num_bulk_out;
+ }
+ }
+
+ if (num_bulk_out == 0) {
+ dbg("Invalid interface, discarding");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int aircable_attach (struct usb_serial *serial)
+{
+ struct usb_serial_port *port = serial->port[0];
+ struct aircable_private *priv;
+
+ priv = kzalloc(sizeof(struct aircable_private), GFP_KERNEL);
+ if (!priv){
+ err("%s- kmalloc(%Zd) failed.", __FUNCTION__,
+ sizeof(struct aircable_private));
+ return -ENOMEM;
+ }
+
+ /* Allocation of Circular Buffers */
+ priv->tx_buf = serial_buf_alloc();
+ if (priv->tx_buf == NULL) {
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ priv->rx_buf = serial_buf_alloc();
+ if (priv->rx_buf == NULL) {
+ kfree(priv->tx_buf);
+ kfree(priv);
+ return -ENOMEM;
+ }
+
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ INIT_WORK(&priv->rx_work, aircable_read, port);
+
+ usb_set_serial_port_data(serial->port[0], priv);
+
+ return 0;
+}
+
+static void aircable_shutdown(struct usb_serial *serial)
+{
+
+ struct usb_serial_port *port = serial->port[0];
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+
+ dbg("%s", __FUNCTION__);
+
+ if (priv) {
+ serial_buf_free(priv->tx_buf);
+ serial_buf_free(priv->rx_buf);
+ usb_set_serial_port_data(port, NULL);
+ kfree(priv);
+ }
+}
+
+static int aircable_write_room(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ return serial_buf_data_avail(priv->tx_buf);
+}
+
+static int aircable_write(struct usb_serial_port *port,
+ const unsigned char *source, int count)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ int temp;
+
+ dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, source);
+
+ if (!count){
+ dbg("%s - write request of 0 bytes", __FUNCTION__);
+ return count;
+ }
+
+ temp = serial_buf_put(priv->tx_buf, source, count);
+
+ aircable_send(port);
+
+ if (count > AIRCABLE_BUF_SIZE)
+ count = AIRCABLE_BUF_SIZE;
+
+ return count;
+
+}
+
+static void aircable_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ int result;
+
+ dbg("%s - urb->status: %d", __FUNCTION__ , urb->status);
+
+ /* This has been taken from cypress_m8.c cypress_write_int_callback */
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d",
+ __FUNCTION__, urb->status);
+ port->write_urb_busy = 0;
+ return;
+ default:
+ /* error in the urb, so we have to resubmit it */
+ dbg("%s - Overflow in write", __FUNCTION__);
+ dbg("%s - nonzero write bulk status received: %d",
+ __FUNCTION__, urb->status);
+ port->write_urb->transfer_buffer_length = 1;
+ port->write_urb->dev = port->serial->dev;
+ result = usb_submit_urb(port->write_urb, GFP_KERNEL);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting write urb, error %d\n",
+ __FUNCTION__, result);
+ else
+ return;
+ }
+
+ port->write_urb_busy = 0;
+
+ aircable_send(port);
+}
+
+static void aircable_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ struct tty_struct *tty;
+ unsigned long no_packages, remaining, package_length, i;
+ int result, shift = 0;
+ unsigned char *temp;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - urb->status = %d", __FUNCTION__, urb->status);
+ if (!port->open_count) {
+ dbg("%s - port is closed, exiting.", __FUNCTION__);
+ return;
+ }
+ if (urb->status == -EPROTO) {
+ dbg("%s - caught -EPROTO, resubmitting the urb",
+ __FUNCTION__);
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ aircable_read_bulk_callback, port);
+
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ return;
+ }
+ dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
+ return;
+ }
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length,urb->transfer_buffer);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ if (urb->actual_length <= 2) {
+ /* This is an incomplete package */
+ serial_buf_put(priv->rx_buf, urb->transfer_buffer,
+ urb->actual_length);
+ } else {
+ temp = urb->transfer_buffer;
+ if (temp[0] == RX_HEADER_0)
+ shift = HCI_HEADER_LENGTH;
+
+ remaining = urb->actual_length;
+ no_packages = urb->actual_length / (HCI_COMPLETE_FRAME);
+
+ if (urb->actual_length % HCI_COMPLETE_FRAME != 0)
+ no_packages+=1;
+
+ for (i = 0; i < no_packages ;i++) {
+ if (remaining > (HCI_COMPLETE_FRAME))
+ package_length = HCI_COMPLETE_FRAME;
+ else
+ package_length = remaining;
+ remaining -= package_length;
+
+ serial_buf_put(priv->rx_buf,
+ urb->transfer_buffer + shift +
+ (HCI_COMPLETE_FRAME) * (i),
+ package_length - shift);
+ }
+ }
+ aircable_read(port);
+ }
+
+ /* Schedule the next read _if_ we are still open */
+ if (port->open_count) {
+ usb_fill_bulk_urb(port->read_urb, port->serial->dev,
+ usb_rcvbulkpipe(port->serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ aircable_read_bulk_callback, port);
+
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&urb->dev->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ }
+
+ return;
+}
+
+/* Based on ftdi_sio.c throttle */
+static void aircable_throttle(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ priv->rx_flags |= THROTTLED;
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+}
+
+/* Based on ftdi_sio.c unthrottle */
+static void aircable_unthrottle(struct usb_serial_port *port)
+{
+ struct aircable_private *priv = usb_get_serial_port_data(port);
+ int actually_throttled;
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->rx_lock, flags);
+ actually_throttled = priv->rx_flags & ACTUALLY_THROTTLED;
+ priv->rx_flags &= ~(THROTTLED | ACTUALLY_THROTTLED);
+ spin_unlock_irqrestore(&priv->rx_lock, flags);
+
+ if (actually_throttled)
+ schedule_work(&priv->rx_work);
+}
+
+static struct usb_serial_driver aircable_device = {
+ .description = "aircable",
+ .id_table = id_table,
+ .num_ports = 1,
+ .attach = aircable_attach,
+ .probe = aircable_probe,
+ .shutdown = aircable_shutdown,
+ .write = aircable_write,
+ .write_room = aircable_write_room,
+ .write_bulk_callback = aircable_write_bulk_callback,
+ .read_bulk_callback = aircable_read_bulk_callback,
+ .throttle = aircable_throttle,
+ .unthrottle = aircable_unthrottle,
+};
+
+static struct usb_driver aircable_driver = {
+ .name = "aircable",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = id_table,
+};
+
+static int __init aircable_init (void)
+{
+ int retval;
+ retval = usb_serial_register(&aircable_device);
+ if (retval)
+ goto failed_serial_register;
+ retval = usb_register(&aircable_driver);
+ if (retval)
+ goto failed_usb_register;
+ return 0;
+
+failed_serial_register:
+ usb_serial_deregister(&aircable_device);
+failed_usb_register:
+ return retval;
+}
+
+static void __exit aircable_exit (void)
+{
+ usb_deregister(&aircable_driver);
+ usb_serial_deregister(&aircable_device);
+}
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
+
+module_init(aircable_init);
+module_exit(aircable_exit);
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/airprime.c b/drivers/usb/serial/airprime.c
index 62082532a8b..6e1a84a858d 100644
--- a/drivers/usb/serial/airprime.c
+++ b/drivers/usb/serial/airprime.c
@@ -1,7 +1,7 @@
/*
* AirPrime CDMA Wireless Serial USB driver
*
- * Copyright (C) 2005 Greg Kroah-Hartman <gregkh@suse.de>
+ * Copyright (C) 2005-2006 Greg Kroah-Hartman <gregkh@suse.de>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
@@ -11,26 +11,264 @@
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/tty.h>
+#include <linux/tty_flip.h>
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
static struct usb_device_id id_table [] = {
{ USB_DEVICE(0x0c88, 0x17da) }, /* Kyocera Wireless KPC650/Passport */
- { USB_DEVICE(0xf3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
- { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
+ { USB_DEVICE(0x0f3d, 0x0112) }, /* AirPrime CDMA Wireless PC Card */
+ { USB_DEVICE(0x1199, 0x0017) }, /* Sierra Wireless EM5625 */
+ { USB_DEVICE(0x1199, 0x0018) }, /* Sierra Wireless MC5720 */
{ USB_DEVICE(0x1199, 0x0112) }, /* Sierra Wireless Aircard 580 */
{ USB_DEVICE(0x1199, 0x0218) }, /* Sierra Wireless MC5720 */
+ { USB_DEVICE(0x1410, 0x1110) }, /* Novatel Wireless Merlin CDMA */
{ },
};
MODULE_DEVICE_TABLE(usb, id_table);
+#define URB_TRANSFER_BUFFER_SIZE 4096
+#define NUM_READ_URBS 4
+#define NUM_WRITE_URBS 4
+#define NUM_BULK_EPS 3
+#define MAX_BULK_EPS 6
+
+/* if overridden by the user, then use their value for the size of the
+ * read and write urbs, and the number of endpoints */
+static int buffer_size = URB_TRANSFER_BUFFER_SIZE;
+static int endpoints = NUM_BULK_EPS;
+static int debug;
+struct airprime_private {
+ spinlock_t lock;
+ int outstanding_urbs;
+ int throttled;
+ struct urb *read_urbp[NUM_READ_URBS];
+};
+
+static void airprime_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ unsigned char *data = urb->transfer_buffer;
+ struct tty_struct *tty;
+ int result;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (urb->status) {
+ dbg("%s - nonzero read bulk status received: %d",
+ __FUNCTION__, urb->status);
+ /* something happened, so free up the memory for this urb */
+ if (urb->transfer_buffer) {
+ kfree (urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+ return;
+ }
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+
+ tty = port->tty;
+ if (tty && urb->actual_length) {
+ tty_insert_flip_string (tty, data, urb->actual_length);
+ tty_flip_buffer_push (tty);
+ }
+
+ result = usb_submit_urb (urb, GFP_ATOMIC);
+ if (result)
+ dev_err(&port->dev, "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
+ return;
+}
+
+static void airprime_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
+{
+ struct usb_serial_port *port = urb->context;
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* free up the transfer buffer, as usb_free_urb() does not do this */
+ kfree (urb->transfer_buffer);
+
+ if (urb->status)
+ dbg("%s - nonzero write bulk status received: %d",
+ __FUNCTION__, urb->status);
+ spin_lock_irqsave(&priv->lock, flags);
+ --priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ usb_serial_port_softint(port);
+}
+
+static int airprime_open(struct usb_serial_port *port, struct file *filp)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct urb *urb;
+ char *buffer = NULL;
+ int i;
+ int result = 0;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* initialize our private data structure if it isn't already created */
+ if (!priv) {
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ result = -ENOMEM;
+ goto out;
+ }
+ spin_lock_init(&priv->lock);
+ usb_set_serial_port_data(port, priv);
+ }
+
+ for (i = 0; i < NUM_READ_URBS; ++i) {
+ buffer = kmalloc(buffer_size, GFP_KERNEL);
+ if (!buffer) {
+ dev_err(&port->dev, "%s - out of memory.\n",
+ __FUNCTION__);
+ result = -ENOMEM;
+ goto errout;
+ }
+ urb = usb_alloc_urb(0, GFP_KERNEL);
+ if (!urb) {
+ dev_err(&port->dev, "%s - no more urbs?\n",
+ __FUNCTION__);
+ result = -ENOMEM;
+ goto errout;
+ }
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ buffer, buffer_size,
+ airprime_read_bulk_callback, port);
+ result = usb_submit_urb(urb, GFP_KERNEL);
+ if (result) {
+ dev_err(&port->dev,
+ "%s - failed submitting read urb %d for port %d, error %d\n",
+ __FUNCTION__, i, port->number, result);
+ goto errout;
+ }
+ /* remember this urb so we can kill it when the port is closed */
+ priv->read_urbp[i] = urb;
+ }
+ goto out;
+
+ errout:
+ /* some error happened, cancel any submitted urbs and clean up anything that
+ got allocated successfully */
+
+ for ( ; i >= 0; --i) {
+ urb = priv->read_urbp[i];
+ if (urb) {
+ /* This urb was submitted successfully. So we have to
+ cancel it.
+ Unlinking the urb will invoke read_bulk_callback()
+ with an error status, so its transfer buffer will
+ be freed there */
+ if (usb_unlink_urb (urb) != -EINPROGRESS) {
+ /* comments in drivers/usb/core/urb.c say this
+ can only happen if the urb was never submitted,
+ or has completed already.
+ Either way we may have to free the transfer
+ buffer here. */
+ if (urb->transfer_buffer) {
+ kfree (urb->transfer_buffer);
+ urb->transfer_buffer = NULL;
+ }
+ }
+ usb_free_urb (urb);
+ }
+ }
+
+ out:
+ return result;
+}
+
+static void airprime_close(struct usb_serial_port *port, struct file * filp)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ int i;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* killing the urb will invoke read_bulk_callback() with an error status,
+ so the transfer buffer will be freed there */
+ for (i = 0; i < NUM_READ_URBS; ++i) {
+ usb_kill_urb (priv->read_urbp[i]);
+ usb_free_urb (priv->read_urbp[i]);
+ }
+
+ /* free up private structure */
+ kfree (priv);
+ usb_set_serial_port_data(port, NULL);
+}
+
+static int airprime_write(struct usb_serial_port *port,
+ const unsigned char *buf, int count)
+{
+ struct airprime_private *priv = usb_get_serial_port_data(port);
+ struct usb_serial *serial = port->serial;
+ struct urb *urb;
+ unsigned char *buffer;
+ unsigned long flags;
+ int status;
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (priv->outstanding_urbs > NUM_WRITE_URBS) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ dbg("%s - write limit hit\n", __FUNCTION__);
+ return 0;
+ }
+ spin_unlock_irqrestore(&priv->lock, flags);
+ buffer = kmalloc(count, GFP_ATOMIC);
+ if (!buffer) {
+ dev_err(&port->dev, "out of memory\n");
+ return -ENOMEM;
+ }
+ urb = usb_alloc_urb(0, GFP_ATOMIC);
+ if (!urb) {
+ dev_err(&port->dev, "no more free urbs\n");
+ kfree (buffer);
+ return -ENOMEM;
+ }
+ memcpy (buffer, buf, count);
+
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, buffer);
+
+ usb_fill_bulk_urb(urb, serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ buffer, count,
+ airprime_write_bulk_callback, port);
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev,
+ "%s - usb_submit_urb(write bulk) failed with status = %d\n",
+ __FUNCTION__, status);
+ count = status;
+ kfree (buffer);
+ } else {
+ spin_lock_irqsave(&priv->lock, flags);
+ ++priv->outstanding_urbs;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ }
+ /* we are done with this urb, so let the host driver
+ * really free it when it is finished with it */
+ usb_free_urb (urb);
+ return count;
+}
+
static struct usb_driver airprime_driver = {
.name = "airprime",
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
- .no_dynamic_id = 1,
+ .no_dynamic_id = 1,
};
static struct usb_serial_driver airprime_device = {
@@ -42,13 +280,17 @@ static struct usb_serial_driver airprime_device = {
.num_interrupt_in = NUM_DONT_CARE,
.num_bulk_in = NUM_DONT_CARE,
.num_bulk_out = NUM_DONT_CARE,
- .num_ports = 1,
+ .open = airprime_open,
+ .close = airprime_close,
+ .write = airprime_write,
};
static int __init airprime_init(void)
{
int retval;
+ airprime_device.num_ports =
+ (endpoints > 0 && endpoints <= MAX_BULK_EPS) ? endpoints : NUM_BULK_EPS;
retval = usb_serial_register(&airprime_device);
if (retval)
return retval;
@@ -60,6 +302,8 @@ static int __init airprime_init(void)
static void __exit airprime_exit(void)
{
+ dbg("%s", __FUNCTION__);
+
usb_deregister(&airprime_driver);
usb_serial_deregister(&airprime_device);
}
@@ -67,3 +311,10 @@ static void __exit airprime_exit(void)
module_init(airprime_init);
module_exit(airprime_exit);
MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled");
+module_param(buffer_size, int, 0);
+MODULE_PARM_DESC(buffer_size, "Size of the transfer buffers in bytes (default 4096)");
+module_param(endpoints, int, 0);
+MODULE_PARM_DESC(endpoints, "Number of bulk EPs to configure (default 3)");
diff --git a/drivers/usb/serial/ark3116.c b/drivers/usb/serial/ark3116.c
index 970d9ef0a7a..ca52f12f0e2 100644
--- a/drivers/usb/serial/ark3116.c
+++ b/drivers/usb/serial/ark3116.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2006
+ * Simon Schulz (ark3116_driver <at> auctionant.de)
+ *
* ark3116
* - implements a driver for the arkmicro ark3116 chipset (vendor=0x6547,
* productid=0x0232) (used in a datacable called KQ-U8A)
@@ -8,8 +11,6 @@
*
* - based on logs created by usbsnoopy
*
- * Author : Simon Schulz [ark3116_driver<AT>auctionant.de]
- *
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
@@ -22,6 +23,8 @@
#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/serial.h>
+#include <asm/uaccess.h>
static int debug;
@@ -43,10 +46,10 @@ static inline void ARK3116_SND(struct usb_serial *serial, int seq,
{
int result;
result = usb_control_msg(serial->dev,
- usb_sndctrlpipe(serial->dev,0),
+ usb_sndctrlpipe(serial->dev, 0),
request, requesttype, value, index,
- NULL,0x00, 1000);
- dbg("%03d > ok",seq);
+ NULL, 0x00, 1000);
+ dbg("%03d > ok", seq);
}
static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
@@ -56,27 +59,25 @@ static inline void ARK3116_RCV(struct usb_serial *serial, int seq,
{
int result;
result = usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev,0),
- request, requesttype, value, index,
- buf, 0x0000001, 1000);
+ usb_rcvctrlpipe(serial->dev, 0),
+ request, requesttype, value, index,
+ buf, 0x0000001, 1000);
if (result)
- dbg("%03d < %d bytes [0x%02X]",seq, result, buf[0]);
+ dbg("%03d < %d bytes [0x%02X]", seq, result, buf[0]);
else
dbg("%03d < 0 bytes", seq);
}
-
static inline void ARK3116_RCV_QUIET(struct usb_serial *serial,
__u8 request, __u8 requesttype,
__u16 value, __u16 index, char *buf)
{
usb_control_msg(serial->dev,
- usb_rcvctrlpipe(serial->dev,0),
+ usb_rcvctrlpipe(serial->dev, 0),
request, requesttype, value, index,
buf, 0x0000001, 1000);
}
-
static int ark3116_attach(struct usb_serial *serial)
{
char *buf;
@@ -84,10 +85,10 @@ static int ark3116_attach(struct usb_serial *serial)
int i;
for (i = 0; i < serial->num_ports; ++i) {
- priv = kmalloc (sizeof (struct ark3116_private), GFP_KERNEL);
+ priv = kmalloc(sizeof (struct ark3116_private), GFP_KERNEL);
if (!priv)
goto cleanup;
- memset (priv, 0x00, sizeof (struct ark3116_private));
+ memset(priv, 0x00, sizeof (struct ark3116_private));
spin_lock_init(&priv->lock);
usb_set_serial_port_data(serial->port[i], priv);
@@ -95,63 +96,62 @@ static int ark3116_attach(struct usb_serial *serial)
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
- dbg("error kmalloc -> out of mem ?");
+ dbg("error kmalloc -> out of mem?");
goto cleanup;
}
/* 3 */
- ARK3116_SND(serial, 3,0xFE,0x40,0x0008,0x0002);
- ARK3116_SND(serial, 4,0xFE,0x40,0x0008,0x0001);
- ARK3116_SND(serial, 5,0xFE,0x40,0x0000,0x0008);
- ARK3116_SND(serial, 6,0xFE,0x40,0x0000,0x000B);
+ ARK3116_SND(serial, 3, 0xFE, 0x40, 0x0008, 0x0002);
+ ARK3116_SND(serial, 4, 0xFE, 0x40, 0x0008, 0x0001);
+ ARK3116_SND(serial, 5, 0xFE, 0x40, 0x0000, 0x0008);
+ ARK3116_SND(serial, 6, 0xFE, 0x40, 0x0000, 0x000B);
/* <-- seq7 */
- ARK3116_RCV(serial, 7,0xFE,0xC0,0x0000,0x0003, 0x00, buf);
- ARK3116_SND(serial, 8,0xFE,0x40,0x0080,0x0003);
- ARK3116_SND(serial, 9,0xFE,0x40,0x001A,0x0000);
- ARK3116_SND(serial,10,0xFE,0x40,0x0000,0x0001);
- ARK3116_SND(serial,11,0xFE,0x40,0x0000,0x0003);
+ ARK3116_RCV(serial, 7, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 8, 0xFE, 0x40, 0x0080, 0x0003);
+ ARK3116_SND(serial, 9, 0xFE, 0x40, 0x001A, 0x0000);
+ ARK3116_SND(serial, 10, 0xFE, 0x40, 0x0000, 0x0001);
+ ARK3116_SND(serial, 11, 0xFE, 0x40, 0x0000, 0x0003);
/* <-- seq12 */
- ARK3116_RCV(serial,12,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
- ARK3116_SND(serial,13,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 12, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+ ARK3116_SND(serial, 13, 0xFE, 0x40, 0x0000, 0x0004);
/* 14 */
- ARK3116_RCV(serial,14,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
- ARK3116_SND(serial,15,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 14, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
+ ARK3116_SND(serial, 15, 0xFE, 0x40, 0x0000, 0x0004);
/* 16 */
- ARK3116_RCV(serial,16,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+ ARK3116_RCV(serial, 16, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
/* --> seq17 */
- ARK3116_SND(serial,17,0xFE,0x40,0x0001,0x0004);
+ ARK3116_SND(serial, 17, 0xFE, 0x40, 0x0001, 0x0004);
/* <-- seq18 */
- ARK3116_RCV(serial,18,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+ ARK3116_RCV(serial, 18, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
/* --> seq19 */
- ARK3116_SND(serial,19,0xFE,0x40,0x0003,0x0004);
-
+ ARK3116_SND(serial, 19, 0xFE, 0x40, 0x0003, 0x0004);
/* <-- seq20 */
- /* seems like serial port status info (RTS, CTS,...) */
- /* returns modem control line status ?! */
- ARK3116_RCV(serial,20,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
-
- /* set 9600 baud & do some init ?! */
- ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
- ARK3116_SND(serial,148,0xFE,0x40,0x0038,0x0000);
- ARK3116_SND(serial,149,0xFE,0x40,0x0001,0x0001);
- ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
- ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
- ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
- ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
- ARK3116_SND(serial,154,0xFE,0x40,0x0003,0x0003);
+ /* seems like serial port status info (RTS, CTS, ...) */
+ /* returns modem control line status?! */
+ ARK3116_RCV(serial, 20, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
+
+ /* set 9600 baud & do some init?! */
+ ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+ ARK3116_SND(serial, 148, 0xFE, 0x40, 0x0038, 0x0000);
+ ARK3116_SND(serial, 149, 0xFE, 0x40, 0x0001, 0x0001);
+ ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
+ ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
+ ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 154, 0xFE, 0x40, 0x0003, 0x0003);
kfree(buf);
- return(0);
+ return 0;
cleanup:
- for (--i; i>=0; --i)
+ for (--i; i >= 0; --i)
usb_set_serial_port_data(serial->port[i], NULL);
return -ENOMEM;
}
@@ -180,7 +180,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
spin_lock_irqsave(&priv->lock, flags);
if (!priv->termios_initialized) {
*(port->tty->termios) = tty_std_termios;
- port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ port->tty->termios->c_cflag = B9600 | CS8
+ | CREAD | HUPCL | CLOCAL;
priv->termios_initialized = 1;
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -204,8 +205,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
}
/* set data bit count (8/7/6/5) */
- if (cflag & CSIZE){
- switch (cflag & CSIZE){
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
case CS5:
config |= 0x00;
dbg("setting CS5");
@@ -219,7 +220,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
dbg("setting CS7");
break;
default:
- err ("CSIZE was set but not CS5-CS8, using CS8!");
+ err("CSIZE was set but not CS5-CS8, using CS8!");
+ /* fall through */
case CS8:
config |= 0x03;
dbg("setting CS8");
@@ -227,8 +229,8 @@ static void ark3116_set_termios(struct usb_serial_port *port,
}
}
- /* set parity (NONE,EVEN,ODD) */
- if (cflag & PARENB){
+ /* set parity (NONE/EVEN/ODD) */
+ if (cflag & PARENB) {
if (cflag & PARODD) {
config |= 0x08;
dbg("setting parity to ODD");
@@ -240,20 +242,19 @@ static void ark3116_set_termios(struct usb_serial_port *port,
dbg("setting parity to NONE");
}
- /* SET STOPBIT (1/2) */
+ /* set stop bit (1/2) */
if (cflag & CSTOPB) {
config |= 0x04;
- dbg ("setting 2 stop bits");
+ dbg("setting 2 stop bits");
} else {
- dbg ("setting 1 stop bit");
+ dbg("setting 1 stop bit");
}
-
- /* set baudrate: */
+ /* set baudrate */
baud = 0;
- switch (cflag & CBAUD){
+ switch (cflag & CBAUD) {
case B0:
- err("can't set 0baud, using 9600 instead");
+ err("can't set 0 baud, using 9600 instead");
break;
case B75: baud = 75; break;
case B150: baud = 150; break;
@@ -285,38 +286,40 @@ static void ark3116_set_termios(struct usb_serial_port *port,
*/
if (baud == 460800)
/* strange, for 460800 the formula is wrong
- * (dont use round(), then 9600baud is wrong) */
+ * if using round() then 9600baud is wrong) */
ark3116_baud = 7;
else
ark3116_baud = 3000000 / baud;
/* ? */
- ARK3116_RCV(serial,0,0xFE,0xC0,0x0000,0x0003, 0x03, buf);
+ ARK3116_RCV(serial, 0, 0xFE, 0xC0, 0x0000, 0x0003, 0x03, buf);
+
/* offset = buf[0]; */
/* offset = 0x03; */
- /* dbg("using 0x%04X as target for 0x0003:",0x0080+offset); */
-
+ /* dbg("using 0x%04X as target for 0x0003:", 0x0080 + offset); */
/* set baudrate */
- dbg("setting baudrate to %d (->reg=%d)",baud,ark3116_baud);
- ARK3116_SND(serial,147,0xFE,0x40,0x0083,0x0003);
- ARK3116_SND(serial,148,0xFE,0x40,(ark3116_baud & 0x00FF) ,0x0000);
- ARK3116_SND(serial,149,0xFE,0x40,(ark3116_baud & 0xFF00)>>8,0x0001);
- ARK3116_SND(serial,150,0xFE,0x40,0x0003,0x0003);
+ dbg("setting baudrate to %d (->reg=%d)", baud, ark3116_baud);
+ ARK3116_SND(serial, 147, 0xFE, 0x40, 0x0083, 0x0003);
+ ARK3116_SND(serial, 148, 0xFE, 0x40,
+ (ark3116_baud & 0x00FF), 0x0000);
+ ARK3116_SND(serial, 149, 0xFE, 0x40,
+ (ark3116_baud & 0xFF00) >> 8, 0x0001);
+ ARK3116_SND(serial, 150, 0xFE, 0x40, 0x0003, 0x0003);
/* ? */
- ARK3116_RCV(serial,151,0xFE,0xC0,0x0000,0x0004,0x03, buf);
- ARK3116_SND(serial,152,0xFE,0x40,0x0000,0x0003);
+ ARK3116_RCV(serial, 151, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 152, 0xFE, 0x40, 0x0000, 0x0003);
/* set data bit count, stop bit count & parity: */
dbg("updating bit count, stop bit or parity (cfg=0x%02X)", config);
- ARK3116_RCV(serial,153,0xFE,0xC0,0x0000,0x0003,0x00, buf);
- ARK3116_SND(serial,154,0xFE,0x40,config,0x0003);
+ ARK3116_RCV(serial, 153, 0xFE, 0xC0, 0x0000, 0x0003, 0x00, buf);
+ ARK3116_SND(serial, 154, 0xFE, 0x40, config, 0x0003);
if (cflag & CRTSCTS)
- dbg("CRTSCTS not supported by chipset ?!");
+ dbg("CRTSCTS not supported by chipset?!");
- /* TEST ARK3116_SND(154,0xFE,0x40,0xFFFF, 0x0006); */
+ /* TEST ARK3116_SND(154, 0xFE, 0x40, 0xFFFF, 0x0006); */
kfree(buf);
return;
@@ -329,11 +332,11 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
char *buf;
int result = 0;
- dbg("%s - port %d", __FUNCTION__, port->number);
+ dbg("%s - port %d", __FUNCTION__, port->number);
buf = kmalloc(1, GFP_KERNEL);
if (!buf) {
- dbg("error kmalloc -> out of mem ?");
+ dbg("error kmalloc -> out of mem?");
return -ENOMEM;
}
@@ -342,44 +345,68 @@ static int ark3116_open(struct usb_serial_port *port, struct file *filp)
return result;
/* open */
- ARK3116_RCV(serial,111,0xFE,0xC0,0x0000,0x0003, 0x02, buf);
+ ARK3116_RCV(serial, 111, 0xFE, 0xC0, 0x0000, 0x0003, 0x02, buf);
- ARK3116_SND(serial,112,0xFE,0x40,0x0082,0x0003);
- ARK3116_SND(serial,113,0xFE,0x40,0x001A,0x0000);
- ARK3116_SND(serial,114,0xFE,0x40,0x0000,0x0001);
- ARK3116_SND(serial,115,0xFE,0x40,0x0002,0x0003);
+ ARK3116_SND(serial, 112, 0xFE, 0x40, 0x0082, 0x0003);
+ ARK3116_SND(serial, 113, 0xFE, 0x40, 0x001A, 0x0000);
+ ARK3116_SND(serial, 114, 0xFE, 0x40, 0x0000, 0x0001);
+ ARK3116_SND(serial, 115, 0xFE, 0x40, 0x0002, 0x0003);
- ARK3116_RCV(serial,116,0xFE,0xC0,0x0000,0x0004, 0x03, buf);
- ARK3116_SND(serial,117,0xFE,0x40,0x0002,0x0004);
+ ARK3116_RCV(serial, 116, 0xFE, 0xC0, 0x0000, 0x0004, 0x03, buf);
+ ARK3116_SND(serial, 117, 0xFE, 0x40, 0x0002, 0x0004);
- ARK3116_RCV(serial,118,0xFE,0xC0,0x0000,0x0004, 0x02, buf);
- ARK3116_SND(serial,119,0xFE,0x40,0x0000,0x0004);
+ ARK3116_RCV(serial, 118, 0xFE, 0xC0, 0x0000, 0x0004, 0x02, buf);
+ ARK3116_SND(serial, 119, 0xFE, 0x40, 0x0000, 0x0004);
- ARK3116_RCV(serial,120,0xFE,0xC0,0x0000,0x0004, 0x00, buf);
+ ARK3116_RCV(serial, 120, 0xFE, 0xC0, 0x0000, 0x0004, 0x00, buf);
- ARK3116_SND(serial,121,0xFE,0x40,0x0001,0x0004);
+ ARK3116_SND(serial, 121, 0xFE, 0x40, 0x0001, 0x0004);
- ARK3116_RCV(serial,122,0xFE,0xC0,0x0000,0x0004, 0x01, buf);
+ ARK3116_RCV(serial, 122, 0xFE, 0xC0, 0x0000, 0x0004, 0x01, buf);
- ARK3116_SND(serial,123,0xFE,0x40,0x0003,0x0004);
+ ARK3116_SND(serial, 123, 0xFE, 0x40, 0x0003, 0x0004);
- /* returns different values (control lines ?!) */
- ARK3116_RCV(serial,124,0xFE,0xC0,0x0000,0x0006, 0xFF, buf);
+ /* returns different values (control lines?!) */
+ ARK3116_RCV(serial, 124, 0xFE, 0xC0, 0x0000, 0x0006, 0xFF, buf);
- /* initialise termios: */
+ /* initialise termios */
if (port->tty)
ark3116_set_termios(port, &tmp_termios);
kfree(buf);
return result;
-
}
static int ark3116_ioctl(struct usb_serial_port *port, struct file *file,
unsigned int cmd, unsigned long arg)
{
- dbg("ioctl not supported yet...");
+ struct serial_struct serstruct;
+ void __user *user_arg = (void __user *)arg;
+
+ switch (cmd) {
+ case TIOCGSERIAL:
+ /* XXX: Some of these values are probably wrong. */
+ memset(&serstruct, 0, sizeof (serstruct));
+ serstruct.type = PORT_16654;
+ serstruct.line = port->serial->minor;
+ serstruct.port = port->number;
+ serstruct.custom_divisor = 0;
+ serstruct.baud_base = 460800;
+
+ if (copy_to_user(user_arg, &serstruct, sizeof (serstruct)))
+ return -EFAULT;
+
+ return 0;
+ case TIOCSSERIAL:
+ if (copy_from_user(&serstruct, user_arg, sizeof (serstruct)))
+ return -EFAULT;
+ return 0;
+ default:
+ dbg("%s cmd 0x%04x not supported", __FUNCTION__, cmd);
+ break;
+ }
+
return -ENOIOCTLCMD;
}
@@ -389,7 +416,7 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
char *buf;
char temp;
- /* seems like serial port status info (RTS, CTS,...) is stored
+ /* seems like serial port status info (RTS, CTS, ...) is stored
* in reg(?) 0x0006
* pcb connection point 11 = GND -> sets bit4 of response
* pcb connection point 7 = GND -> sets bit6 of response
@@ -401,16 +428,16 @@ static int ark3116_tiocmget(struct usb_serial_port *port, struct file *file)
return -ENOMEM;
}
- /* read register: */
- ARK3116_RCV_QUIET(serial,0xFE,0xC0,0x0000,0x0006,buf);
+ /* read register */
+ ARK3116_RCV_QUIET(serial, 0xFE, 0xC0, 0x0000, 0x0006, buf);
temp = buf[0];
kfree(buf);
- /* i do not really know if bit4=CTS and bit6=DSR... was just a
- * quick guess !!
+ /* i do not really know if bit4=CTS and bit6=DSR... just a
+ * quick guess!
*/
- return (temp & (1<<4) ? TIOCM_CTS : 0) |
- (temp & (1<<6) ? TIOCM_DSR : 0);
+ return (temp & (1<<4) ? TIOCM_CTS : 0)
+ | (temp & (1<<6) ? TIOCM_DSR : 0);
}
static struct usb_driver ark3116_driver = {
diff --git a/drivers/usb/serial/cyberjack.c b/drivers/usb/serial/cyberjack.c
index 6286aba86fa..d954ec34b01 100644
--- a/drivers/usb/serial/cyberjack.c
+++ b/drivers/usb/serial/cyberjack.c
@@ -214,14 +214,14 @@ static int cyberjack_write (struct usb_serial_port *port, const unsigned char *b
return (0);
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
spin_lock_irqsave(&priv->lock, flags);
diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c
index ee70fddcab6..e1173c1aee3 100644
--- a/drivers/usb/serial/cypress_m8.c
+++ b/drivers/usb/serial/cypress_m8.c
@@ -129,6 +129,9 @@ struct cypress_private {
int cmd_ctrl; /* always set this to 1 before issuing a command */
struct cypress_buf *buf; /* write buffer */
int write_urb_in_use; /* write urb in use indicator */
+ int write_urb_interval; /* interval to use for write urb */
+ int read_urb_interval; /* interval to use for read urb */
+ int comm_is_ok; /* true if communication is (still) ok */
int termios_initialized;
__u8 line_control; /* holds dtr / rts value */
__u8 current_status; /* received from last read - info on dsr,cts,cd,ri,etc */
@@ -168,6 +171,7 @@ static int cypress_tiocmset (struct usb_serial_port *port, struct file *file,
static int cypress_chars_in_buffer (struct usb_serial_port *port);
static void cypress_throttle (struct usb_serial_port *port);
static void cypress_unthrottle (struct usb_serial_port *port);
+static void cypress_set_dead (struct usb_serial_port *port);
static void cypress_read_int_callback (struct urb *urb, struct pt_regs *regs);
static void cypress_write_int_callback (struct urb *urb, struct pt_regs *regs);
/* baud helper functions */
@@ -288,6 +292,9 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
priv = usb_get_serial_port_data(port);
+ if (!priv->comm_is_ok)
+ return -ENODEV;
+
switch(cypress_request_type) {
case CYPRESS_SET_CONFIG:
@@ -365,13 +372,12 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
if (tries++ >= 3)
break;
- if (retval == EPIPE)
- usb_clear_halt(port->serial->dev, 0x00);
- } while (retval != 8 && retval != ENODEV);
+ } while (retval != 8 && retval != -ENODEV);
- if (retval != 8)
+ if (retval != 8) {
err("%s - failed sending serial line settings - %d", __FUNCTION__, retval);
- else {
+ cypress_set_dead(port);
+ } else {
spin_lock_irqsave(&priv->lock, flags);
priv->baud_rate = new_baudrate;
priv->cbr_mask = baud_mask;
@@ -392,12 +398,11 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
if (tries++ >= 3)
break;
- if (retval == EPIPE)
- usb_clear_halt(port->serial->dev, 0x00);
- } while (retval != 5 && retval != ENODEV);
+ } while (retval != 5 && retval != -ENODEV);
if (retval != 5) {
err("%s - failed to retrieve serial line settings - %d", __FUNCTION__, retval);
+ cypress_set_dead(port);
return retval;
} else {
spin_lock_irqsave(&priv->lock, flags);
@@ -419,6 +424,24 @@ static int cypress_serial_control (struct usb_serial_port *port, unsigned baud_m
} /* cypress_serial_control */
+static void cypress_set_dead(struct usb_serial_port *port)
+{
+ struct cypress_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ if (!priv->comm_is_ok) {
+ spin_unlock_irqrestore(&priv->lock, flags);
+ return;
+ }
+ priv->comm_is_ok = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ err("cypress_m8 suspending failing port %d - interval might be too short",
+ port->number);
+}
+
+
/* given a baud mask, it will return integer baud on success */
static int mask_to_rate (unsigned mask)
{
@@ -472,13 +495,15 @@ static unsigned rate_to_mask (int rate)
static int generic_startup (struct usb_serial *serial)
{
struct cypress_private *priv;
+ struct usb_serial_port *port = serial->port[0];
- dbg("%s - port %d", __FUNCTION__, serial->port[0]->number);
+ dbg("%s - port %d", __FUNCTION__, port->number);
priv = kzalloc(sizeof (struct cypress_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ priv->comm_is_ok = !0;
spin_lock_init(&priv->lock);
priv->buf = cypress_buf_alloc(CYPRESS_BUF_SIZE);
if (priv->buf == NULL) {
@@ -489,13 +514,24 @@ static int generic_startup (struct usb_serial *serial)
usb_reset_configuration (serial->dev);
- interval = 1;
priv->cmd_ctrl = 0;
priv->line_control = 0;
priv->termios_initialized = 0;
priv->rx_flags = 0;
priv->cbr_mask = B300;
- usb_set_serial_port_data(serial->port[0], priv);
+ if (interval > 0) {
+ priv->write_urb_interval = interval;
+ priv->read_urb_interval = interval;
+ dbg("%s - port %d read & write intervals forced to %d",
+ __FUNCTION__,port->number,interval);
+ } else {
+ priv->write_urb_interval = port->interrupt_out_urb->interval;
+ priv->read_urb_interval = port->interrupt_in_urb->interval;
+ dbg("%s - port %d intervals: read=%d write=%d",
+ __FUNCTION__,port->number,
+ priv->read_urb_interval,priv->write_urb_interval);
+ }
+ usb_set_serial_port_data(port, priv);
return 0;
}
@@ -585,6 +621,9 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
dbg("%s - port %d", __FUNCTION__, port->number);
+ if (!priv->comm_is_ok)
+ return -EIO;
+
/* clear halts before open */
usb_clear_halt(serial->dev, 0x81);
usb_clear_halt(serial->dev, 0x02);
@@ -624,11 +663,12 @@ static int cypress_open (struct usb_serial_port *port, struct file *filp)
usb_fill_int_urb(port->interrupt_in_urb, serial->dev,
usb_rcvintpipe(serial->dev, port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer, port->interrupt_in_urb->transfer_buffer_length,
- cypress_read_int_callback, port, interval);
+ cypress_read_int_callback, port, priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result){
dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
+ cypress_set_dead(port);
}
return result;
@@ -733,6 +773,9 @@ static void cypress_send(struct usb_serial_port *port)
struct cypress_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
+ if (!priv->comm_is_ok)
+ return;
+
dbg("%s - port %d", __FUNCTION__, port->number);
dbg("%s - interrupt out size is %d", __FUNCTION__, port->interrupt_out_size);
@@ -806,14 +849,16 @@ send:
usb_serial_debug_data(debug, &port->dev, __FUNCTION__, port->interrupt_out_size,
port->interrupt_out_urb->transfer_buffer);
- port->interrupt_out_urb->transfer_buffer_length = actual_size;
- port->interrupt_out_urb->dev = port->serial->dev;
- port->interrupt_out_urb->interval = interval;
+ usb_fill_int_urb(port->interrupt_out_urb, port->serial->dev,
+ usb_sndintpipe(port->serial->dev, port->interrupt_out_endpointAddress),
+ port->interrupt_out_buffer, port->interrupt_out_size,
+ cypress_write_int_callback, port, priv->write_urb_interval);
result = usb_submit_urb (port->interrupt_out_urb, GFP_ATOMIC);
if (result) {
dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__,
result);
priv->write_urb_in_use = 0;
+ cypress_set_dead(port);
}
spin_lock_irqsave(&priv->lock, flags);
@@ -1214,13 +1259,18 @@ static void cypress_unthrottle (struct usb_serial_port *port)
priv->rx_flags = 0;
spin_unlock_irqrestore(&priv->lock, flags);
+ if (!priv->comm_is_ok)
+ return;
+
if (actually_throttled) {
port->interrupt_in_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&port->dev, "%s - failed submitting read urb, "
"error %d\n", __FUNCTION__, result);
+ cypress_set_dead(port);
+ }
}
}
@@ -1240,9 +1290,22 @@ static void cypress_read_int_callback(struct urb *urb, struct pt_regs *regs)
dbg("%s - port %d", __FUNCTION__, port->number);
- if (urb->status) {
- dbg("%s - nonzero read status received: %d", __FUNCTION__,
- urb->status);
+ switch (urb->status) {
+ case 0: /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* precursor to disconnect so just go away */
+ return;
+ case -EPIPE:
+ usb_clear_halt(port->serial->dev,0x81);
+ break;
+ default:
+ /* something ugly is going on... */
+ dev_err(&urb->dev->dev,"%s - unexpected nonzero read status received: %d\n",
+ __FUNCTION__,urb->status);
+ cypress_set_dead(port);
return;
}
@@ -1343,18 +1406,20 @@ continue_read:
/* Continue trying to always read... unless the port has closed. */
- if (port->open_count > 0) {
+ if (port->open_count > 0 && priv->comm_is_ok) {
usb_fill_int_urb(port->interrupt_in_urb, port->serial->dev,
usb_rcvintpipe(port->serial->dev,
port->interrupt_in_endpointAddress),
port->interrupt_in_urb->transfer_buffer,
port->interrupt_in_urb->transfer_buffer_length,
- cypress_read_int_callback, port, interval);
+ cypress_read_int_callback, port, priv->read_urb_interval);
result = usb_submit_urb(port->interrupt_in_urb, GFP_ATOMIC);
- if (result)
+ if (result) {
dev_err(&urb->dev->dev, "%s - failed resubmitting "
"read urb, error %d\n", __FUNCTION__,
result);
+ cypress_set_dead(port);
+ }
}
return;
@@ -1380,20 +1445,26 @@ static void cypress_write_int_callback(struct urb *urb, struct pt_regs *regs)
dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
priv->write_urb_in_use = 0;
return;
- case -EPIPE: /* no break needed */
+ case -EPIPE: /* no break needed; clear halt and resubmit */
+ if (!priv->comm_is_ok)
+ break;
usb_clear_halt(port->serial->dev, 0x02);
- default:
/* error in the urb, so we have to resubmit it */
- dbg("%s - Overflow in write", __FUNCTION__);
dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
port->interrupt_out_urb->transfer_buffer_length = 1;
port->interrupt_out_urb->dev = port->serial->dev;
result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
- if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
- __FUNCTION__, result);
- else
+ if (!result)
return;
+ dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n",
+ __FUNCTION__, result);
+ cypress_set_dead(port);
+ break;
+ default:
+ dev_err(&urb->dev->dev,"%s - unexpected nonzero write status received: %d\n",
+ __FUNCTION__,urb->status);
+ cypress_set_dead(port);
+ break;
}
priv->write_urb_in_use = 0;
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index c6115aa1b44..e774a27c6c9 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -344,6 +344,7 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2102_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2103_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2104_PID) },
+ { USB_DEVICE(SEALEVEL_VID, SEALEVEL_2106_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_1_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2201_2_PID) },
{ USB_DEVICE(SEALEVEL_VID, SEALEVEL_2202_1_PID) },
@@ -507,6 +508,9 @@ static struct usb_device_id id_table_combined [] = {
{ USB_DEVICE(FTDI_VID, FTDI_THORLABS_PID) },
{ USB_DEVICE(TESTO_VID, TESTO_USB_INTERFACE_PID) },
{ USB_DEVICE(FTDI_VID, FTDI_GAMMA_SCOUT_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13M_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13S_PID) },
+ { USB_DEVICE(FTDI_VID, FTDI_TACTRIX_OPENPORT_13U_PID) },
{ }, /* Optional parameter entry */
{ } /* Terminating entry */
};
@@ -1101,25 +1105,29 @@ static ssize_t store_event_char(struct device *dev, struct device_attribute *att
static DEVICE_ATTR(latency_timer, S_IWUSR | S_IRUGO, show_latency_timer, store_latency_timer);
static DEVICE_ATTR(event_char, S_IWUSR, NULL, store_event_char);
-static void create_sysfs_attrs(struct usb_serial *serial)
-{
+static int create_sysfs_attrs(struct usb_serial *serial)
+{
struct ftdi_private *priv;
struct usb_device *udev;
+ int retval = 0;
dbg("%s",__FUNCTION__);
-
+
priv = usb_get_serial_port_data(serial->port[0]);
udev = serial->dev;
-
+
/* XXX I've no idea if the original SIO supports the event_char
* sysfs parameter, so I'm playing it safe. */
if (priv->chip_type != SIO) {
dbg("sysfs attributes for %s", ftdi_chip_name[priv->chip_type]);
- device_create_file(&udev->dev, &dev_attr_event_char);
- if (priv->chip_type == FT232BM || priv->chip_type == FT2232C) {
- device_create_file(&udev->dev, &dev_attr_latency_timer);
+ retval = device_create_file(&udev->dev, &dev_attr_event_char);
+ if ((!retval) &&
+ (priv->chip_type == FT232BM || priv->chip_type == FT2232C)) {
+ retval = device_create_file(&udev->dev,
+ &dev_attr_latency_timer);
}
}
+ return retval;
}
static void remove_sysfs_attrs(struct usb_serial *serial)
@@ -1162,7 +1170,8 @@ static int ftdi_sio_attach (struct usb_serial *serial)
struct usb_serial_port *port = serial->port[0];
struct ftdi_private *priv;
struct ftdi_sio_quirk *quirk;
-
+ int retval;
+
dbg("%s",__FUNCTION__);
priv = kzalloc(sizeof(struct ftdi_private), GFP_KERNEL);
@@ -1203,15 +1212,18 @@ static int ftdi_sio_attach (struct usb_serial *serial)
usb_set_serial_port_data(serial->port[0], priv);
ftdi_determine_type (serial->port[0]);
- create_sysfs_attrs(serial);
+ retval = create_sysfs_attrs(serial);
+ if (retval)
+ dev_err(&serial->dev->dev, "Error creating sysfs files, "
+ "continuing\n");
/* Check for device requiring special set up. */
quirk = (struct ftdi_sio_quirk *)usb_get_serial_data(serial);
if (quirk && quirk->setup) {
quirk->setup(serial);
}
-
- return (0);
+
+ return 0;
} /* ftdi_sio_attach */
diff --git a/drivers/usb/serial/ftdi_sio.h b/drivers/usb/serial/ftdi_sio.h
index 77299996f7e..f0edb87d2dd 100644
--- a/drivers/usb/serial/ftdi_sio.h
+++ b/drivers/usb/serial/ftdi_sio.h
@@ -111,6 +111,7 @@
#define SEALEVEL_2102_PID 0x2102 /* SeaLINK+485 (2102) */
#define SEALEVEL_2103_PID 0x2103 /* SeaLINK+232I (2103) */
#define SEALEVEL_2104_PID 0x2104 /* SeaLINK+485I (2104) */
+#define SEALEVEL_2106_PID 0x9020 /* SeaLINK+422 (2106) */
#define SEALEVEL_2201_1_PID 0x2211 /* SeaPORT+2/232 (2201) Port 1 */
#define SEALEVEL_2201_2_PID 0x2221 /* SeaPORT+2/232 (2201) Port 2 */
#define SEALEVEL_2202_1_PID 0x2212 /* SeaPORT+2/485 (2202) Port 1 */
@@ -472,6 +473,15 @@
*/
#define FTDI_GAMMA_SCOUT_PID 0xD678 /* Gamma Scout online */
+/*
+ * Tactrix OpenPort (ECU) devices.
+ * OpenPort 1.3M submitted by Donour Sizemore.
+ * OpenPort 1.3S and 1.3U submitted by Ian Abbott.
+ */
+#define FTDI_TACTRIX_OPENPORT_13M_PID 0xCC48 /* OpenPort 1.3 Mitsubishi */
+#define FTDI_TACTRIX_OPENPORT_13S_PID 0xCC49 /* OpenPort 1.3 Subaru */
+#define FTDI_TACTRIX_OPENPORT_13U_PID 0xCC4A /* OpenPort 1.3 Universal */
+
/* Commands */
#define FTDI_SIO_RESET 0 /* Reset the port */
#define FTDI_SIO_MODEM_CTRL 1 /* Set the modem control register */
diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c
index 727852634be..4b1196a8b09 100644
--- a/drivers/usb/serial/garmin_gps.c
+++ b/drivers/usb/serial/garmin_gps.c
@@ -1,7 +1,7 @@
/*
* Garmin GPS driver
*
- * Copyright (C) 2004 Hermann Kneissel herkne@users.sourceforge.net
+ * Copyright (C) 2006 Hermann Kneissel herkne@users.sourceforge.net
*
* The latest version of the driver can be found at
* http://sourceforge.net/projects/garmin-gps/
@@ -37,6 +37,8 @@
#include <linux/usb.h>
#include <linux/usb/serial.h>
+#include <linux/version.h>
+
/* the mode to be set when the port ist opened */
static int initial_mode = 1;
@@ -50,7 +52,7 @@ static int debug = 0;
*/
#define VERSION_MAJOR 0
-#define VERSION_MINOR 23
+#define VERSION_MINOR 28
#define _STR(s) #s
#define _DRIVER_VERSION(a,b) "v" _STR(a) "." _STR(b)
@@ -164,7 +166,8 @@ struct garmin_data {
#define FLAGS_SESSION_REPLY1_SEEN 0x0080
#define FLAGS_SESSION_REPLY2_SEEN 0x0040
#define FLAGS_BULK_IN_ACTIVE 0x0020
-#define FLAGS_THROTTLED 0x0010
+#define FLAGS_BULK_IN_RESTART 0x0010
+#define FLAGS_THROTTLED 0x0008
#define CLEAR_HALT_REQUIRED 0x0001
#define FLAGS_QUEUING 0x0100
@@ -224,7 +227,7 @@ static struct usb_driver garmin_driver = {
.probe = usb_serial_probe,
.disconnect = usb_serial_disconnect,
.id_table = id_table,
- .no_dynamic_id = 1,
+ .no_dynamic_id = 1,
};
@@ -270,7 +273,7 @@ static inline int isAbortTrfCmnd(const unsigned char *buf)
static void send_to_tty(struct usb_serial_port *port,
- char *data, unsigned int actual_length)
+ char *data, unsigned int actual_length)
{
struct tty_struct *tty = port->tty;
@@ -294,15 +297,15 @@ static void send_to_tty(struct usb_serial_port *port,
* queue a received (usb-)packet for later processing
*/
static int pkt_add(struct garmin_data * garmin_data_p,
- unsigned char *data, unsigned int data_length)
+ unsigned char *data, unsigned int data_length)
{
+ int state = 0;
int result = 0;
unsigned long flags;
struct garmin_packet *pkt;
/* process only packets containg data ... */
if (data_length) {
- garmin_data_p->flags |= FLAGS_QUEUING;
pkt = kmalloc(sizeof(struct garmin_packet)+data_length,
GFP_ATOMIC);
if (pkt == NULL) {
@@ -313,14 +316,16 @@ static int pkt_add(struct garmin_data * garmin_data_p,
memcpy(pkt->data, data, data_length);
spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_QUEUING;
result = list_empty(&garmin_data_p->pktlist);
pkt->seq = garmin_data_p->seq_counter++;
list_add_tail(&pkt->list, &garmin_data_p->pktlist);
+ state = garmin_data_p->state;
spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* in serial mode, if someone is waiting for data from
the device, iconvert and send the next packet to tty. */
- if (result && (garmin_data_p->state == STATE_GSP_WAIT_DATA)) {
+ if (result && (state == STATE_GSP_WAIT_DATA)) {
gsp_next_packet(garmin_data_p);
}
}
@@ -370,9 +375,9 @@ static void pkt_clear(struct garmin_data * garmin_data_p)
static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
{
__u8 pkt[10];
- __u8 cksum = 0;
- __u8 *ptr = pkt;
- unsigned l = 0;
+ __u8 cksum = 0;
+ __u8 *ptr = pkt;
+ unsigned l = 0;
dbg("%s - pkt-id: 0x%X.", __FUNCTION__, 0xFF & pkt_id);
@@ -416,7 +421,7 @@ static int gsp_send_ack(struct garmin_data * garmin_data_p, __u8 pkt_id)
static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
{
const __u8* recpkt = garmin_data_p->inbuffer+GSP_INITIAL_OFFSET;
- __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
+ __le32 *usbdata = (__le32 *) garmin_data_p->inbuffer;
int cksum = 0;
int n = 0;
@@ -447,11 +452,11 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
n++;
}
- if ((0xff & (cksum + *recpkt)) != 0) {
- dbg("%s - invalid checksum, expected %02x, got %02x",
- __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
- return -EINVPKT;
- }
+ if ((0xff & (cksum + *recpkt)) != 0) {
+ dbg("%s - invalid checksum, expected %02x, got %02x",
+ __FUNCTION__, 0xff & -cksum, 0xff & *recpkt);
+ return -EINVPKT;
+ }
usbdata[0] = __cpu_to_le32(GARMIN_LAYERID_APPL);
usbdata[1] = __cpu_to_le32(pktid);
@@ -491,20 +496,28 @@ static int gsp_rec_packet(struct garmin_data * garmin_data_p, int count)
*/
static int gsp_receive(struct garmin_data * garmin_data_p,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
int offs = 0;
int ack_or_nak_seen = 0;
int i = 0;
- __u8 *dest = garmin_data_p->inbuffer;
- int size = garmin_data_p->insize;
+ __u8 *dest;
+ int size;
// dleSeen: set if last byte read was a DLE
- int dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+ int dleSeen;
// skip: if set, skip incoming data until possible start of
// new packet
- int skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+ int skip;
__u8 data;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ dest = garmin_data_p->inbuffer;
+ size = garmin_data_p->insize;
+ dleSeen = garmin_data_p->flags & FLAGS_GSP_DLESEEN;
+ skip = garmin_data_p->flags & FLAGS_GSP_SKIP;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
dbg("%s - dle=%d skip=%d size=%d count=%d",
__FUNCTION__, dleSeen, skip, size, count);
@@ -572,6 +585,8 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
}
}
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+
garmin_data_p->insize = size;
// copy flags back to structure
@@ -587,6 +602,11 @@ static int gsp_receive(struct garmin_data * garmin_data_p,
if (ack_or_nak_seen) {
garmin_data_p->state = STATE_GSP_WAIT_DATA;
+ }
+
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+
+ if (ack_or_nak_seen) {
gsp_next_packet(garmin_data_p);
}
@@ -676,7 +696,7 @@ static int gsp_send(struct garmin_data * garmin_data_p,
src = garmin_data_p->outbuffer+GARMIN_PKTHDR_LENGTH;
if (k > (GARMIN_PKTHDR_LENGTH-2)) {
/* can't add stuffing DLEs in place, move data to end
- of buffer ... */
+ of buffer ... */
dst = garmin_data_p->outbuffer+GPS_OUT_BUFSIZ-datalen;
memcpy(dst, src, datalen);
src = dst;
@@ -755,8 +775,9 @@ static void gsp_next_packet(struct garmin_data * garmin_data_p)
* or even incomplete packets
*/
static int nat_receive(struct garmin_data * garmin_data_p,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
__u8 * dest;
int offs = 0;
int result = count;
@@ -803,7 +824,9 @@ static int nat_receive(struct garmin_data * garmin_data_p,
/* if this was an abort-transfer command,
flush all queued data. */
if (isAbortTrfCmnd(garmin_data_p->inbuffer)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_DROP_DATA;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
pkt_clear(garmin_data_p);
}
}
@@ -839,12 +862,15 @@ static void priv_status_resp(struct usb_serial_port *port)
static int process_resetdev_request(struct usb_serial_port *port)
{
+ unsigned long flags;
int status;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~(CLEAR_HALT_REQUIRED);
garmin_data_p->state = STATE_RESET;
garmin_data_p->serial_num = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
usb_kill_urb (port->interrupt_in_urb);
dbg("%s - usb_reset_device", __FUNCTION__ );
@@ -862,6 +888,7 @@ static int process_resetdev_request(struct usb_serial_port *port)
*/
static int garmin_clear(struct garmin_data * garmin_data_p)
{
+ unsigned long flags;
int status = 0;
struct usb_serial_port *port = garmin_data_p->port;
@@ -875,8 +902,10 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
/* flush all queued data */
pkt_clear(garmin_data_p);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->insize = 0;
garmin_data_p->outsize = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
return status;
}
@@ -888,6 +917,7 @@ static int garmin_clear(struct garmin_data * garmin_data_p)
static int garmin_init_session(struct usb_serial_port *port)
{
+ unsigned long flags;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
int status = 0;
@@ -913,7 +943,9 @@ static int garmin_init_session(struct usb_serial_port *port)
if (status >= 0) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts++;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* not needed, but the win32 driver does it too ... */
status = garmin_write_bulk(port,
@@ -921,7 +953,9 @@ static int garmin_init_session(struct usb_serial_port *port)
sizeof(GARMIN_START_SESSION_REQ2));
if (status >= 0) {
status = 0;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts++;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
}
}
@@ -935,6 +969,7 @@ static int garmin_init_session(struct usb_serial_port *port)
static int garmin_open (struct usb_serial_port *port, struct file *filp)
{
+ unsigned long flags;
int status = 0;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -948,9 +983,11 @@ static int garmin_open (struct usb_serial_port *port, struct file *filp)
if (port->tty)
port->tty->low_latency = 1;
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->mode = initial_mode;
garmin_data_p->count = 0;
garmin_data_p->flags = 0;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* shutdown any bulk reads that might be going on */
usb_kill_urb (port->write_urb);
@@ -996,6 +1033,7 @@ static void garmin_close (struct usb_serial_port *port, struct file * filp)
static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -1007,7 +1045,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
if (urb->status) {
dbg("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= CLEAR_HALT_REQUIRED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
usb_serial_port_softint(port);
@@ -1015,8 +1055,9 @@ static void garmin_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
static int garmin_write_bulk (struct usb_serial_port *port,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
struct urb *urb;
@@ -1026,7 +1067,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
dbg("%s - port %d, state %d", __FUNCTION__, port->number,
garmin_data_p->state);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_DROP_DATA;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
buffer = kmalloc (count, GFP_ATOMIC);
if (!buffer) {
@@ -1053,7 +1096,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
urb->transfer_flags |= URB_ZERO_PACKET;
if (GARMIN_LAYERID_APPL == getLayerId(buffer)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
pkt_clear(garmin_data_p);
garmin_data_p->state = STATE_GSP_WAIT_DATA;
@@ -1087,8 +1132,9 @@ static int garmin_write_bulk (struct usb_serial_port *port,
static int garmin_write (struct usb_serial_port *port,
- const unsigned char *buf, int count)
+ const unsigned char *buf, int count)
{
+ unsigned long flags;
int pktid, pktsiz, len;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
__le32 *privpkt = (__le32 *)garmin_data_p->privpkt;
@@ -1139,7 +1185,9 @@ static int garmin_write (struct usb_serial_port *port,
break;
case PRIV_PKTID_RESET_REQ:
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_REQ_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
break;
case PRIV_PKTID_SET_DEF_MODE:
@@ -1155,6 +1203,8 @@ static int garmin_write (struct usb_serial_port *port,
}
}
+ garmin_data_p->ignorePkts = 0;
+
if (garmin_data_p->mode == MODE_GARMIN_SERIAL) {
return gsp_receive(garmin_data_p, buf, count);
} else { /* MODE_NATIVE */
@@ -1177,10 +1227,10 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
{
/*
* Report back the number of bytes currently in our input buffer.
- * Will this lock up the driver - the buffer contains an incomplete
- * package which will not be written to the device until it
- * has been completed ?
- */
+ * Will this lock up the driver - the buffer contains an incomplete
+ * package which will not be written to the device until it
+ * has been completed ?
+ */
//struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
//return garmin_data_p->insize;
return 0;
@@ -1190,6 +1240,8 @@ static int garmin_chars_in_buffer (struct usb_serial_port *port)
static void garmin_read_process(struct garmin_data * garmin_data_p,
unsigned char *data, unsigned data_length)
{
+ unsigned long flags;
+
if (garmin_data_p->flags & FLAGS_DROP_DATA) {
/* abort-transfer cmd is actice */
dbg("%s - pkt dropped", __FUNCTION__);
@@ -1200,11 +1252,14 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
if a reset is required or not when closing
the device */
if (0 == memcmp(data, GARMIN_APP_LAYER_REPLY,
- sizeof(GARMIN_APP_LAYER_REPLY)))
+ sizeof(GARMIN_APP_LAYER_REPLY))) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_APP_RESP_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+ }
/* if throttling is active or postprecessing is required
- put the received data in th input queue, otherwise
+ put the received data in the input queue, otherwise
send it directly to the tty port */
if (garmin_data_p->flags & FLAGS_QUEUING) {
pkt_add(garmin_data_p, data, data_length);
@@ -1221,6 +1276,7 @@ static void garmin_read_process(struct garmin_data * garmin_data_p,
static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
@@ -1245,19 +1301,30 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
garmin_read_process(garmin_data_p, data, urb->actual_length);
- /* Continue trying to read until nothing more is received */
- if (urb->actual_length > 0) {
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe (serial->dev,
- port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- garmin_read_bulk_callback, port);
+ if (urb->actual_length == 0 &&
+ 0 != (garmin_data_p->flags & FLAGS_BULK_IN_RESTART)) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags &= ~FLAGS_BULK_IN_RESTART;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
if (status)
dev_err(&port->dev,
"%s - failed resubmitting read urb, error %d\n",
__FUNCTION__, status);
+ } else if (urb->actual_length > 0) {
+ /* Continue trying to read until nothing more is received */
+ if (0 == (garmin_data_p->flags & FLAGS_THROTTLED)) {
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, status);
+ }
+ } else {
+ dbg("%s - end of bulk data", __FUNCTION__);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags &= ~FLAGS_BULK_IN_ACTIVE;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
return;
}
@@ -1265,6 +1332,7 @@ static void garmin_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
{
+ unsigned long flags;
int status;
struct usb_serial_port *port = (struct usb_serial_port *)urb->context;
struct usb_serial *serial = port->serial;
@@ -1297,25 +1365,41 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
dbg("%s - bulk data available.", __FUNCTION__);
- /* bulk data available */
- usb_fill_bulk_urb (port->read_urb, serial->dev,
- usb_rcvbulkpipe (serial->dev,
- port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer,
- port->read_urb->transfer_buffer_length,
- garmin_read_bulk_callback, port);
- status = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (status) {
- dev_err(&port->dev,
- "%s - failed submitting read urb, error %d\n",
- __FUNCTION__, status);
+ if (0 == (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+
+ /* bulk data available */
+ usb_fill_bulk_urb (port->read_urb, serial->dev,
+ usb_rcvbulkpipe (serial->dev,
+ port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer,
+ port->read_urb->transfer_buffer_length,
+ garmin_read_bulk_callback, port);
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status) {
+ dev_err(&port->dev,
+ "%s - failed submitting read urb, error %d\n",
+ __FUNCTION__, status);
+ } else {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_BULK_IN_ACTIVE;
+ /* do not send this packet to the user */
+ garmin_data_p->ignorePkts = 1;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
+ }
+ } else {
+ /* bulk-in transfer still active */
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
+ garmin_data_p->flags |= FLAGS_BULK_IN_RESTART;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
} else if (urb->actual_length == (4+sizeof(GARMIN_START_SESSION_REPLY))
&& 0 == memcmp(data, GARMIN_START_SESSION_REPLY,
sizeof(GARMIN_START_SESSION_REPLY))) {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_SESSION_REPLY1_SEEN;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* save the serial number */
garmin_data_p->serial_num
@@ -1330,7 +1414,9 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
ignore it. */
dbg("%s - pkt ignored (%d)",
__FUNCTION__, garmin_data_p->ignorePkts);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->ignorePkts--;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
} else {
garmin_read_process(garmin_data_p, data, urb->actual_length);
}
@@ -1351,18 +1437,20 @@ static void garmin_read_int_callback (struct urb *urb, struct pt_regs *regs)
*/
static int garmin_flush_queue(struct garmin_data * garmin_data_p)
{
+ unsigned long flags;
struct garmin_packet *pkt;
if ((garmin_data_p->flags & FLAGS_THROTTLED) == 0) {
pkt = pkt_pop(garmin_data_p);
if (pkt != NULL) {
-
send_to_tty(garmin_data_p->port, pkt->data, pkt->size);
kfree(pkt);
mod_timer(&garmin_data_p->timer, (1)+jiffies);
} else {
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_QUEUING;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
}
return 0;
@@ -1371,26 +1459,41 @@ static int garmin_flush_queue(struct garmin_data * garmin_data_p)
static void garmin_throttle (struct usb_serial_port *port)
{
+ unsigned long flags;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
dbg("%s - port %d", __FUNCTION__, port->number);
/* set flag, data received will be put into a queue
for later processing */
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags |= FLAGS_QUEUING|FLAGS_THROTTLED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
}
static void garmin_unthrottle (struct usb_serial_port *port)
{
+ unsigned long flags;
struct garmin_data * garmin_data_p = usb_get_serial_port_data(port);
+ int status;
dbg("%s - port %d", __FUNCTION__, port->number);
+ spin_lock_irqsave(&garmin_data_p->lock, flags);
garmin_data_p->flags &= ~FLAGS_THROTTLED;
+ spin_unlock_irqrestore(&garmin_data_p->lock, flags);
/* in native mode send queued data to tty, in
serial mode nothing needs to be done here */
if (garmin_data_p->mode == MODE_NATIVE)
garmin_flush_queue(garmin_data_p);
+
+ if (0 != (garmin_data_p->flags & FLAGS_BULK_IN_ACTIVE)) {
+ status = usb_submit_urb(port->read_urb, GFP_ATOMIC);
+ if (status)
+ dev_err(&port->dev,
+ "%s - failed resubmitting read urb, error %d\n",
+ __FUNCTION__, status);
+ }
}
@@ -1420,11 +1523,12 @@ static int garmin_attach (struct usb_serial *serial)
dbg("%s", __FUNCTION__);
- garmin_data_p = kzalloc(sizeof(struct garmin_data), GFP_KERNEL);
+ garmin_data_p = kmalloc (sizeof(struct garmin_data), GFP_KERNEL);
if (garmin_data_p == NULL) {
dev_err(&port->dev, "%s - Out of memory\n", __FUNCTION__);
return -ENOMEM;
}
+ memset (garmin_data_p, 0, sizeof(struct garmin_data));
init_timer(&garmin_data_p->timer);
spin_lock_init(&garmin_data_p->lock);
INIT_LIST_HEAD(&garmin_data_p->pktlist);
@@ -1459,10 +1563,10 @@ static void garmin_shutdown (struct usb_serial *serial)
/* All of the device info needed */
static struct usb_serial_driver garmin_device = {
.driver = {
- .owner = THIS_MODULE,
- .name = "garmin_gps",
+ .owner = THIS_MODULE,
+ .name = "garmin_gps",
},
- .description = "Garmin GPS usb/tty",
+ .description = "Garmin GPS usb/tty",
.id_table = id_table,
.num_interrupt_in = 1,
.num_bulk_in = 1,
@@ -1483,6 +1587,7 @@ static struct usb_serial_driver garmin_device = {
};
+
static int __init garmin_init (void)
{
int retval;
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
index 17271355639..21cbaa0fb96 100644
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -175,14 +175,14 @@ int usb_serial_generic_write(struct usb_serial_port *port, const unsigned char *
/* only do something if we have a bulk out endpoint */
if (serial->num_bulk_out) {
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
count = (count > port->bulk_out_size) ? port->bulk_out_size : count;
diff --git a/drivers/usb/serial/ipaq.c b/drivers/usb/serial/ipaq.c
index 9840bade79f..cbc725a6c58 100644
--- a/drivers/usb/serial/ipaq.c
+++ b/drivers/usb/serial/ipaq.c
@@ -479,6 +479,7 @@ static struct usb_device_id ipaq_id_table [] = {
{ USB_DEVICE(0x0BB4, 0x0A9D) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9E) }, /* SmartPhone USB Sync */
{ USB_DEVICE(0x0BB4, 0x0A9F) }, /* SmartPhone USB Sync */
+ { USB_DEVICE(0x0BB4, 0x0BCE) }, /* "High Tech Computer Corp" */
{ USB_DEVICE(0x0BF8, 0x1001) }, /* Fujitsu Siemens Computers USB Sync */
{ USB_DEVICE(0x0C44, 0x03A2) }, /* Motorola iDEN Smartphone */
{ USB_DEVICE(0x0C8E, 0x6000) }, /* Cesscom Luxian Series */
@@ -652,11 +653,6 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
port->bulk_out_size = port->write_urb->transfer_buffer_length = URBDATA_SIZE;
msleep(1000*initial_wait);
- /* Start reading from the device */
- usb_fill_bulk_urb(port->read_urb, serial->dev,
- usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
- port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
- ipaq_read_bulk_callback, port);
/*
* Send out control message observed in win98 sniffs. Not sure what
@@ -670,18 +666,31 @@ static int ipaq_open(struct usb_serial_port *port, struct file *filp)
result = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0), 0x22, 0x21,
0x1, 0, NULL, 0, 100);
- if (result == 0) {
- result = usb_submit_urb(port->read_urb, GFP_KERNEL);
- if (result) {
- err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
- goto error;
- }
- return 0;
- }
+ if (!result)
+ break;
+
msleep(1000);
}
- err("%s - failed doing control urb, error %d", __FUNCTION__, result);
- goto error;
+
+ if (!retries && result) {
+ err("%s - failed doing control urb, error %d", __FUNCTION__,
+ result);
+ goto error;
+ }
+
+ /* Start reading from the device */
+ usb_fill_bulk_urb(port->read_urb, serial->dev,
+ usb_rcvbulkpipe(serial->dev, port->bulk_in_endpointAddress),
+ port->read_urb->transfer_buffer, port->read_urb->transfer_buffer_length,
+ ipaq_read_bulk_callback, port);
+
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
+ if (result) {
+ err("%s - failed submitting read urb, error %d", __FUNCTION__, result);
+ goto error;
+ }
+
+ return 0;
enomem:
result = -ENOMEM;
diff --git a/drivers/usb/serial/ipw.c b/drivers/usb/serial/ipw.c
index 87306cb6f9f..812bc213a96 100644
--- a/drivers/usb/serial/ipw.c
+++ b/drivers/usb/serial/ipw.c
@@ -394,14 +394,14 @@ static int ipw_write(struct usb_serial_port *port, const unsigned char *buf, int
return 0;
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
count = min(count, port->bulk_out_size);
memcpy(port->bulk_out_buffer, buf, count);
diff --git a/drivers/usb/serial/ir-usb.c b/drivers/usb/serial/ir-usb.c
index 1738b0b6a37..1b348df388e 100644
--- a/drivers/usb/serial/ir-usb.c
+++ b/drivers/usb/serial/ir-usb.c
@@ -342,14 +342,14 @@ static int ir_write (struct usb_serial_port *port, const unsigned char *buf, int
if (count == 0)
return 0;
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
transfer_buffer = port->write_urb->transfer_buffer;
transfer_size = min(count, port->bulk_out_size - 1);
diff --git a/drivers/usb/serial/keyspan_pda.c b/drivers/usb/serial/keyspan_pda.c
index 49b8dc039d1..59e777f1e8f 100644
--- a/drivers/usb/serial/keyspan_pda.c
+++ b/drivers/usb/serial/keyspan_pda.c
@@ -518,13 +518,13 @@ static int keyspan_pda_write(struct usb_serial_port *port,
the TX urb is in-flight (wait until it completes)
the device is full (wait until it says there is room)
*/
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy || priv->tx_throttled) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
/* At this point the URB is in our control, nobody else can submit it
again (the only sudden transition was the one from EINPROGRESS to
diff --git a/drivers/usb/serial/mos7840.c b/drivers/usb/serial/mos7840.c
new file mode 100644
index 00000000000..95bf57166c5
--- /dev/null
+++ b/drivers/usb/serial/mos7840.c
@@ -0,0 +1,2962 @@
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Clean ups from Moschip version and a few ioctl implementations by:
+ * Paul B Schroeder <pschroeder "at" uplogix "dot" com>
+ *
+ * Originally based on drivers/usb/serial/io_edgeport.c which is:
+ * Copyright (C) 2000 Inside Out Networks, All rights reserved.
+ * Copyright (C) 2001-2002 Greg Kroah-Hartman <greg@kroah.com>
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/tty.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_flip.h>
+#include <linux/module.h>
+#include <linux/serial.h>
+#include <linux/usb.h>
+#include <linux/usb/serial.h>
+#include <asm/uaccess.h>
+
+/*
+ * Version Information
+ */
+#define DRIVER_VERSION "1.3.1"
+#define DRIVER_DESC "Moschip 7840/7820 USB Serial Driver"
+
+/*
+ * 16C50 UART register defines
+ */
+
+#define LCR_BITS_5 0x00 /* 5 bits/char */
+#define LCR_BITS_6 0x01 /* 6 bits/char */
+#define LCR_BITS_7 0x02 /* 7 bits/char */
+#define LCR_BITS_8 0x03 /* 8 bits/char */
+#define LCR_BITS_MASK 0x03 /* Mask for bits/char field */
+
+#define LCR_STOP_1 0x00 /* 1 stop bit */
+#define LCR_STOP_1_5 0x04 /* 1.5 stop bits (if 5 bits/char) */
+#define LCR_STOP_2 0x04 /* 2 stop bits (if 6-8 bits/char) */
+#define LCR_STOP_MASK 0x04 /* Mask for stop bits field */
+
+#define LCR_PAR_NONE 0x00 /* No parity */
+#define LCR_PAR_ODD 0x08 /* Odd parity */
+#define LCR_PAR_EVEN 0x18 /* Even parity */
+#define LCR_PAR_MARK 0x28 /* Force parity bit to 1 */
+#define LCR_PAR_SPACE 0x38 /* Force parity bit to 0 */
+#define LCR_PAR_MASK 0x38 /* Mask for parity field */
+
+#define LCR_SET_BREAK 0x40 /* Set Break condition */
+#define LCR_DL_ENABLE 0x80 /* Enable access to divisor latch */
+
+#define MCR_DTR 0x01 /* Assert DTR */
+#define MCR_RTS 0x02 /* Assert RTS */
+#define MCR_OUT1 0x04 /* Loopback only: Sets state of RI */
+#define MCR_MASTER_IE 0x08 /* Enable interrupt outputs */
+#define MCR_LOOPBACK 0x10 /* Set internal (digital) loopback mode */
+#define MCR_XON_ANY 0x20 /* Enable any char to exit XOFF mode */
+
+#define MOS7840_MSR_CTS 0x10 /* Current state of CTS */
+#define MOS7840_MSR_DSR 0x20 /* Current state of DSR */
+#define MOS7840_MSR_RI 0x40 /* Current state of RI */
+#define MOS7840_MSR_CD 0x80 /* Current state of CD */
+
+/*
+ * Defines used for sending commands to port
+ */
+
+#define WAIT_FOR_EVER (HZ * 0 ) /* timeout urb is wait for ever */
+#define MOS_WDR_TIMEOUT (HZ * 5 ) /* default urb timeout */
+
+#define MOS_PORT1 0x0200
+#define MOS_PORT2 0x0300
+#define MOS_VENREG 0x0000
+#define MOS_MAX_PORT 0x02
+#define MOS_WRITE 0x0E
+#define MOS_READ 0x0D
+
+/* Requests */
+#define MCS_RD_RTYPE 0xC0
+#define MCS_WR_RTYPE 0x40
+#define MCS_RDREQ 0x0D
+#define MCS_WRREQ 0x0E
+#define MCS_CTRL_TIMEOUT 500
+#define VENDOR_READ_LENGTH (0x01)
+
+#define MAX_NAME_LEN 64
+
+#define ZLP_REG1 0x3A //Zero_Flag_Reg1 58
+#define ZLP_REG5 0x3E //Zero_Flag_Reg5 62
+
+/* For higher baud Rates use TIOCEXBAUD */
+#define TIOCEXBAUD 0x5462
+
+/* vendor id and device id defines */
+
+#define USB_VENDOR_ID_MOSCHIP 0x9710
+#define MOSCHIP_DEVICE_ID_7840 0x7840
+#define MOSCHIP_DEVICE_ID_7820 0x7820
+
+/* Interrupt Rotinue Defines */
+
+#define SERIAL_IIR_RLS 0x06
+#define SERIAL_IIR_MS 0x00
+
+/*
+ * Emulation of the bit mask on the LINE STATUS REGISTER.
+ */
+#define SERIAL_LSR_DR 0x0001
+#define SERIAL_LSR_OE 0x0002
+#define SERIAL_LSR_PE 0x0004
+#define SERIAL_LSR_FE 0x0008
+#define SERIAL_LSR_BI 0x0010
+
+#define MOS_MSR_DELTA_CTS 0x10
+#define MOS_MSR_DELTA_DSR 0x20
+#define MOS_MSR_DELTA_RI 0x40
+#define MOS_MSR_DELTA_CD 0x80
+
+// Serial Port register Address
+#define INTERRUPT_ENABLE_REGISTER ((__u16)(0x01))
+#define FIFO_CONTROL_REGISTER ((__u16)(0x02))
+#define LINE_CONTROL_REGISTER ((__u16)(0x03))
+#define MODEM_CONTROL_REGISTER ((__u16)(0x04))
+#define LINE_STATUS_REGISTER ((__u16)(0x05))
+#define MODEM_STATUS_REGISTER ((__u16)(0x06))
+#define SCRATCH_PAD_REGISTER ((__u16)(0x07))
+#define DIVISOR_LATCH_LSB ((__u16)(0x00))
+#define DIVISOR_LATCH_MSB ((__u16)(0x01))
+
+#define CLK_MULTI_REGISTER ((__u16)(0x02))
+#define CLK_START_VALUE_REGISTER ((__u16)(0x03))
+
+#define SERIAL_LCR_DLAB ((__u16)(0x0080))
+
+/*
+ * URB POOL related defines
+ */
+#define NUM_URBS 16 /* URB Count */
+#define URB_TRANSFER_BUFFER_SIZE 32 /* URB Size */
+
+
+static struct usb_device_id moschip_port_id_table[] = {
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+ {} /* terminating entry */
+};
+
+static __devinitdata struct usb_device_id moschip_id_table_combined[] = {
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7840)},
+ {USB_DEVICE(USB_VENDOR_ID_MOSCHIP, MOSCHIP_DEVICE_ID_7820)},
+ {} /* terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, moschip_id_table_combined);
+
+/* This structure holds all of the local port information */
+
+struct moschip_port {
+ int port_num; /*Actual port number in the device(1,2,etc) */
+ struct urb *write_urb; /* write URB for this port */
+ struct urb *read_urb; /* read URB for this port */
+ __u8 shadowLCR; /* last LCR value received */
+ __u8 shadowMCR; /* last MCR value received */
+ char open;
+ wait_queue_head_t wait_chase; /* for handling sleeping while waiting for chase to finish */
+ wait_queue_head_t delta_msr_wait; /* for handling sleeping while waiting for msr change to happen */
+ int delta_msr_cond;
+ struct async_icount icount;
+ struct usb_serial_port *port; /* loop back to the owner of this object */
+
+ /*Offsets */
+ __u8 SpRegOffset;
+ __u8 ControlRegOffset;
+ __u8 DcrRegOffset;
+ //for processing control URBS in interrupt context
+ struct urb *control_urb;
+ char *ctrl_buf;
+ int MsrLsr;
+
+ struct urb *write_urb_pool[NUM_URBS];
+};
+
+
+static int debug;
+static int mos7840_num_ports; //this says the number of ports in the device
+static int mos7840_num_open_ports;
+
+
+/*
+ * mos7840_set_reg_sync
+ * To set the Control register by calling usb_fill_control_urb function
+ * by passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_reg_sync(struct usb_serial_port *port, __u16 reg,
+ __u16 val)
+{
+ struct usb_device *dev = port->serial->dev;
+ val = val & 0x00ff;
+ dbg("mos7840_set_reg_sync offset is %x, value %x\n", reg, val);
+
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+ MCS_WR_RTYPE, val, reg, NULL, 0,
+ MOS_WDR_TIMEOUT);
+}
+
+/*
+ * mos7840_get_reg_sync
+ * To set the Uart register by calling usb_fill_control_urb function by
+ * passing usb_rcvctrlpipe function as parameter.
+ */
+
+static int mos7840_get_reg_sync(struct usb_serial_port *port, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret = 0;
+
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+ MCS_RD_RTYPE, 0, reg, val, VENDOR_READ_LENGTH,
+ MOS_WDR_TIMEOUT);
+ dbg("mos7840_get_reg_sync offset is %x, return val %x\n", reg, *val);
+ *val = (*val) & 0x00ff;
+ return ret;
+}
+
+/*
+ * mos7840_set_uart_reg
+ * To set the Uart register by calling usb_fill_control_urb function by
+ * passing usb_sndctrlpipe function as parameter.
+ */
+
+static int mos7840_set_uart_reg(struct usb_serial_port *port, __u16 reg,
+ __u16 val)
+{
+
+ struct usb_device *dev = port->serial->dev;
+ val = val & 0x00ff;
+ // For the UART control registers, the application number need to be Or'ed
+ if (mos7840_num_ports == 4) {
+ val |=
+ (((__u16) port->number - (__u16) (port->serial->minor)) +
+ 1) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n", val);
+ } else {
+ if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+ val |=
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 1) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n",
+ val);
+ } else {
+ val |=
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 2) << 8;
+ dbg("mos7840_set_uart_reg application number is %x\n",
+ val);
+ }
+ }
+ return usb_control_msg(dev, usb_sndctrlpipe(dev, 0), MCS_WRREQ,
+ MCS_WR_RTYPE, val, reg, NULL, 0,
+ MOS_WDR_TIMEOUT);
+
+}
+
+/*
+ * mos7840_get_uart_reg
+ * To set the Control register by calling usb_fill_control_urb function
+ * by passing usb_rcvctrlpipe function as parameter.
+ */
+static int mos7840_get_uart_reg(struct usb_serial_port *port, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = port->serial->dev;
+ int ret = 0;
+ __u16 Wval;
+
+ //dbg("application number is %4x \n",(((__u16)port->number - (__u16)(port->serial->minor))+1)<<8);
+ /*Wval is same as application number */
+ if (mos7840_num_ports == 4) {
+ Wval =
+ (((__u16) port->number - (__u16) (port->serial->minor)) +
+ 1) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n", Wval);
+ } else {
+ if (((__u16) port->number - (__u16) (port->serial->minor)) == 0) {
+ Wval =
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 1) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n",
+ Wval);
+ } else {
+ Wval =
+ (((__u16) port->number -
+ (__u16) (port->serial->minor)) + 2) << 8;
+ dbg("mos7840_get_uart_reg application number is %x\n",
+ Wval);
+ }
+ }
+ ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), MCS_RDREQ,
+ MCS_RD_RTYPE, Wval, reg, val, VENDOR_READ_LENGTH,
+ MOS_WDR_TIMEOUT);
+ *val = (*val) & 0x00ff;
+ return ret;
+}
+
+static void mos7840_dump_serial_port(struct moschip_port *mos7840_port)
+{
+
+ dbg("***************************************\n");
+ dbg("SpRegOffset is %2x\n", mos7840_port->SpRegOffset);
+ dbg("ControlRegOffset is %2x \n", mos7840_port->ControlRegOffset);
+ dbg("DCRRegOffset is %2x \n", mos7840_port->DcrRegOffset);
+ dbg("***************************************\n");
+
+}
+
+/************************************************************************/
+/************************************************************************/
+/* I N T E R F A C E F U N C T I O N S */
+/* I N T E R F A C E F U N C T I O N S */
+/************************************************************************/
+/************************************************************************/
+
+static inline void mos7840_set_port_private(struct usb_serial_port *port,
+ struct moschip_port *data)
+{
+ usb_set_serial_port_data(port, (void *)data);
+}
+
+static inline struct moschip_port *mos7840_get_port_private(struct
+ usb_serial_port
+ *port)
+{
+ return (struct moschip_port *)usb_get_serial_port_data(port);
+}
+
+static int mos7840_handle_new_msr(struct moschip_port *port, __u8 new_msr)
+{
+ struct moschip_port *mos7840_port;
+ struct async_icount *icount;
+ mos7840_port = port;
+ icount = &mos7840_port->icount;
+ if (new_msr &
+ (MOS_MSR_DELTA_CTS | MOS_MSR_DELTA_DSR | MOS_MSR_DELTA_RI |
+ MOS_MSR_DELTA_CD)) {
+ icount = &mos7840_port->icount;
+
+ /* update input line counters */
+ if (new_msr & MOS_MSR_DELTA_CTS) {
+ icount->cts++;
+ }
+ if (new_msr & MOS_MSR_DELTA_DSR) {
+ icount->dsr++;
+ }
+ if (new_msr & MOS_MSR_DELTA_CD) {
+ icount->dcd++;
+ }
+ if (new_msr & MOS_MSR_DELTA_RI) {
+ icount->rng++;
+ }
+ }
+
+ return 0;
+}
+
+static int mos7840_handle_new_lsr(struct moschip_port *port, __u8 new_lsr)
+{
+ struct async_icount *icount;
+
+ dbg("%s - %02x", __FUNCTION__, new_lsr);
+
+ if (new_lsr & SERIAL_LSR_BI) {
+ //
+ // Parity and Framing errors only count if they
+ // occur exclusive of a break being
+ // received.
+ //
+ new_lsr &= (__u8) (SERIAL_LSR_OE | SERIAL_LSR_BI);
+ }
+
+ /* update input line counters */
+ icount = &port->icount;
+ if (new_lsr & SERIAL_LSR_BI) {
+ icount->brk++;
+ }
+ if (new_lsr & SERIAL_LSR_OE) {
+ icount->overrun++;
+ }
+ if (new_lsr & SERIAL_LSR_PE) {
+ icount->parity++;
+ }
+ if (new_lsr & SERIAL_LSR_FE) {
+ icount->frame++;
+ }
+
+ return 0;
+}
+
+/************************************************************************/
+/************************************************************************/
+/* U S B C A L L B A C K F U N C T I O N S */
+/* U S B C A L L B A C K F U N C T I O N S */
+/************************************************************************/
+/************************************************************************/
+
+static void mos7840_control_callback(struct urb *urb, struct pt_regs *regs)
+{
+ unsigned char *data;
+ struct moschip_port *mos7840_port;
+ __u8 regval = 0x0;
+
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
+ goto exit;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+
+ dbg("%s urb buffer size is %d\n", __FUNCTION__, urb->actual_length);
+ dbg("%s mos7840_port->MsrLsr is %d port %d\n", __FUNCTION__,
+ mos7840_port->MsrLsr, mos7840_port->port_num);
+ data = urb->transfer_buffer;
+ regval = (__u8) data[0];
+ dbg("%s data is %x\n", __FUNCTION__, regval);
+ if (mos7840_port->MsrLsr == 0)
+ mos7840_handle_new_msr(mos7840_port, regval);
+ else if (mos7840_port->MsrLsr == 1)
+ mos7840_handle_new_lsr(mos7840_port, regval);
+
+ exit:
+ return;
+}
+
+static int mos7840_get_reg(struct moschip_port *mcs, __u16 Wval, __u16 reg,
+ __u16 * val)
+{
+ struct usb_device *dev = mcs->port->serial->dev;
+ struct usb_ctrlrequest *dr = NULL;
+ unsigned char *buffer = NULL;
+ int ret = 0;
+ buffer = (__u8 *) mcs->ctrl_buf;
+
+// dr=(struct usb_ctrlrequest *)(buffer);
+ dr = (void *)(buffer + 2);
+ dr->bRequestType = MCS_RD_RTYPE;
+ dr->bRequest = MCS_RDREQ;
+ dr->wValue = cpu_to_le16(Wval); //0;
+ dr->wIndex = cpu_to_le16(reg);
+ dr->wLength = cpu_to_le16(2);
+
+ usb_fill_control_urb(mcs->control_urb, dev, usb_rcvctrlpipe(dev, 0),
+ (unsigned char *)dr, buffer, 2,
+ mos7840_control_callback, mcs);
+ mcs->control_urb->transfer_buffer_length = 2;
+ ret = usb_submit_urb(mcs->control_urb, GFP_ATOMIC);
+ return ret;
+}
+
+/*****************************************************************************
+ * mos7840_interrupt_callback
+ * this is the callback function for when we have received data on the
+ * interrupt endpoint.
+ *****************************************************************************/
+
+static void mos7840_interrupt_callback(struct urb *urb, struct pt_regs *regs)
+{
+ int result;
+ int length;
+ struct moschip_port *mos7840_port;
+ struct usb_serial *serial;
+ __u16 Data;
+ unsigned char *data;
+ __u8 sp[5], st;
+ int i;
+ __u16 wval;
+
+ dbg("%s", " : Entering\n");
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ switch (urb->status) {
+ case 0:
+ /* success */
+ break;
+ case -ECONNRESET:
+ case -ENOENT:
+ case -ESHUTDOWN:
+ /* this urb is terminated, clean up */
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
+ return;
+ default:
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
+ goto exit;
+ }
+
+ length = urb->actual_length;
+ data = urb->transfer_buffer;
+
+ serial = (struct usb_serial *)urb->context;
+
+ /* Moschip get 5 bytes
+ * Byte 1 IIR Port 1 (port.number is 0)
+ * Byte 2 IIR Port 2 (port.number is 1)
+ * Byte 3 IIR Port 3 (port.number is 2)
+ * Byte 4 IIR Port 4 (port.number is 3)
+ * Byte 5 FIFO status for both */
+
+ if (length && length > 5) {
+ dbg("%s \n", "Wrong data !!!");
+ return;
+ }
+
+ sp[0] = (__u8) data[0];
+ sp[1] = (__u8) data[1];
+ sp[2] = (__u8) data[2];
+ sp[3] = (__u8) data[3];
+ st = (__u8) data[4];
+
+ for (i = 0; i < serial->num_ports; i++) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+ wval =
+ (((__u16) serial->port[i]->number -
+ (__u16) (serial->minor)) + 1) << 8;
+ if (mos7840_port->open) {
+ if (sp[i] & 0x01) {
+ dbg("SP%d No Interrupt !!!\n", i);
+ } else {
+ switch (sp[i] & 0x0f) {
+ case SERIAL_IIR_RLS:
+ dbg("Serial Port %d: Receiver status error or ", i);
+ dbg("address bit detected in 9-bit mode\n");
+ mos7840_port->MsrLsr = 1;
+ mos7840_get_reg(mos7840_port, wval,
+ LINE_STATUS_REGISTER,
+ &Data);
+ break;
+ case SERIAL_IIR_MS:
+ dbg("Serial Port %d: Modem status change\n", i);
+ mos7840_port->MsrLsr = 0;
+ mos7840_get_reg(mos7840_port, wval,
+ MODEM_STATUS_REGISTER,
+ &Data);
+ break;
+ }
+ }
+ }
+ }
+ exit:
+ result = usb_submit_urb(urb, GFP_ATOMIC);
+ if (result) {
+ dev_err(&urb->dev->dev,
+ "%s - Error %d submitting interrupt urb\n",
+ __FUNCTION__, result);
+ }
+
+ return;
+
+}
+
+static int mos7840_port_paranoia_check(struct usb_serial_port *port,
+ const char *function)
+{
+ if (!port) {
+ dbg("%s - port == NULL", function);
+ return -1;
+ }
+ if (!port->serial) {
+ dbg("%s - port->serial == NULL", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Inline functions to check the sanity of a pointer that is passed to us */
+static int mos7840_serial_paranoia_check(struct usb_serial *serial,
+ const char *function)
+{
+ if (!serial) {
+ dbg("%s - serial == NULL", function);
+ return -1;
+ }
+ if (!serial->type) {
+ dbg("%s - serial->type == NULL!", function);
+ return -1;
+ }
+
+ return 0;
+}
+
+static struct usb_serial *mos7840_get_usb_serial(struct usb_serial_port *port,
+ const char *function)
+{
+ /* if no port was specified, or it fails a paranoia check */
+ if (!port ||
+ mos7840_port_paranoia_check(port, function) ||
+ mos7840_serial_paranoia_check(port->serial, function)) {
+ /* then say that we don't have a valid usb_serial thing, which will * end up genrating -ENODEV return values */
+ return NULL;
+ }
+
+ return port->serial;
+}
+
+/*****************************************************************************
+ * mos7840_bulk_in_callback
+ * this is the callback function for when we have received data on the
+ * bulk in endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_in_callback(struct urb *urb, struct pt_regs *regs)
+{
+ int status;
+ unsigned char *data;
+ struct usb_serial *serial;
+ struct usb_serial_port *port;
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero read bulk status received: %d", urb->status);
+ return;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+ if (!mos7840_port) {
+ dbg("%s", "NULL mos7840_port pointer \n");
+ return;
+ }
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s\n", "Bad serial pointer ");
+ return;
+ }
+
+ dbg("%s\n", "Entering... \n");
+
+ data = urb->transfer_buffer;
+
+ dbg("%s", "Entering ........... \n");
+
+ if (urb->actual_length) {
+ tty = mos7840_port->port->tty;
+ if (tty) {
+ tty_buffer_request_room(tty, urb->actual_length);
+ tty_insert_flip_string(tty, data, urb->actual_length);
+ dbg(" %s \n", data);
+ tty_flip_buffer_push(tty);
+ }
+ mos7840_port->icount.rx += urb->actual_length;
+ dbg("mos7840_port->icount.rx is %d:\n",
+ mos7840_port->icount.rx);
+ }
+
+ if (!mos7840_port->read_urb) {
+ dbg("%s", "URB KILLED !!!\n");
+ return;
+ }
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+}
+
+/*****************************************************************************
+ * mos7840_bulk_out_data_callback
+ * this is the callback function for when we have finished sending serial data
+ * on the bulk out endpoint.
+ *****************************************************************************/
+
+static void mos7840_bulk_out_data_callback(struct urb *urb,
+ struct pt_regs *regs)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ if (!urb) {
+ dbg("%s", "Invalid Pointer !!!!:\n");
+ return;
+ }
+
+ if (urb->status) {
+ dbg("nonzero write bulk status received:%d\n", urb->status);
+ return;
+ }
+
+ mos7840_port = (struct moschip_port *)urb->context;
+ if (!mos7840_port) {
+ dbg("%s", "NULL mos7840_port pointer \n");
+ return;
+ }
+
+ if (mos7840_port_paranoia_check(mos7840_port->port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ dbg("%s \n", "Entering .........");
+
+ tty = mos7840_port->port->tty;
+
+ if (tty && mos7840_port->open) {
+ /* let the tty driver wakeup if it has a special *
+ * write_wakeup function */
+
+ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+ && tty->ldisc.write_wakeup) {
+ (tty->ldisc.write_wakeup) (tty);
+ }
+
+ /* tell the tty driver that something has changed */
+ wake_up_interruptible(&tty->write_wait);
+ }
+
+}
+
+/************************************************************************/
+/* D R I V E R T T Y I N T E R F A C E F U N C T I O N S */
+/************************************************************************/
+#ifdef MCSSerialProbe
+static int mos7840_serial_probe(struct usb_serial *serial,
+ const struct usb_device_id *id)
+{
+
+ /*need to implement the mode_reg reading and updating\
+ structures usb_serial_ device_type\
+ (i.e num_ports, num_bulkin,bulkout etc) */
+ /* Also we can update the changes attach */
+ return 1;
+}
+#endif
+
+/*****************************************************************************
+ * mos7840_open
+ * this function is called by the tty driver when a port is opened
+ * If successful, we return 0
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_open(struct usb_serial_port *port, struct file *filp)
+{
+ int response;
+ int j;
+ struct usb_serial *serial;
+ struct urb *urb;
+ __u16 Data;
+ int status;
+ struct moschip_port *mos7840_port;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return -ENODEV;
+ }
+
+ mos7840_num_open_ports++;
+ serial = port->serial;
+
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return -ENODEV;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ usb_clear_halt(serial->dev, port->write_urb->pipe);
+ usb_clear_halt(serial->dev, port->read_urb->pipe);
+
+ /* Initialising the write urb pool */
+ for (j = 0; j < NUM_URBS; ++j) {
+ urb = usb_alloc_urb(0, SLAB_ATOMIC);
+ mos7840_port->write_urb_pool[j] = urb;
+
+ if (urb == NULL) {
+ err("No more urbs???");
+ continue;
+ }
+
+ urb->transfer_buffer = NULL;
+ urb->transfer_buffer =
+ kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+ if (!urb->transfer_buffer) {
+ err("%s-out of memory for urb buffers.", __FUNCTION__);
+ continue;
+ }
+ }
+
+/*****************************************************************************
+ * Initialize MCS7840 -- Write Init values to corresponding Registers
+ *
+ * Register Index
+ * 1 : IER
+ * 2 : FCR
+ * 3 : LCR
+ * 4 : MCR
+ *
+ * 0x08 : SP1/2 Control Reg
+ *****************************************************************************/
+
+//NEED to check the following Block
+
+ status = 0;
+ Data = 0x0;
+ status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading Spreg failed\n");
+ return -1;
+ }
+ Data |= 0x80;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Spreg failed\n");
+ return -1;
+ }
+
+ Data &= ~0x80;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Spreg failed\n");
+ return -1;
+ }
+//End of block to be checked
+
+ status = 0;
+ Data = 0x0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading Controlreg failed\n");
+ return -1;
+ }
+ Data |= 0x08; //Driver done bit
+ Data |= 0x20; //rx_disable
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+ if (status < 0) {
+ dbg("writing Controlreg failed\n");
+ return -1;
+ }
+ //do register settings here
+ // Set all regs to the device default values.
+ ////////////////////////////////////
+ // First Disable all interrupts.
+ ////////////////////////////////////
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+ if (status < 0) {
+ dbg("disableing interrupts failed\n");
+ return -1;
+ }
+ // Set FIFO_CONTROL_REGISTER to the default value
+ Data = 0x00;
+ status = 0;
+ status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing FIFO_CONTROL_REGISTER failed\n");
+ return -1;
+ }
+
+ Data = 0xcf;
+ status = 0;
+ status = mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing FIFO_CONTROL_REGISTER failed\n");
+ return -1;
+ }
+
+ Data = 0x03;
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+ mos7840_port->shadowLCR = Data;
+
+ Data = 0x0b;
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ mos7840_port->shadowMCR = Data;
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+ mos7840_port->shadowLCR = Data;
+
+ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ Data = 0x0c;
+ status = 0;
+ status = mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+ Data = 0x0;
+ status = 0;
+ status = mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+
+ Data = Data & ~SERIAL_LCR_DLAB;
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+ mos7840_port->shadowLCR = Data;
+
+ //clearing Bulkin and Bulkout Fifo
+ Data = 0x0;
+ status = 0;
+ status = mos7840_get_reg_sync(port, mos7840_port->SpRegOffset, &Data);
+
+ Data = Data | 0x0c;
+ status = 0;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+
+ Data = Data & ~0x0c;
+ status = 0;
+ status = mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ //Finally enable all interrupts
+ Data = 0x0;
+ Data = 0x0c;
+ status = 0;
+ status = mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ //clearing rx_disable
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ Data = Data & ~0x20;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+ // rx_negate
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->ControlRegOffset, &Data);
+ Data = Data | 0x10;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->ControlRegOffset, Data);
+
+ /* force low_latency on so that our tty_push actually forces *
+ * the data through,otherwise it is scheduled, and with *
+ * high data rates (like with OHCI) data can get lost. */
+
+ if (port->tty)
+ port->tty->low_latency = 1;
+/* Check to see if we've set up our endpoint info yet *
+ * (can't set it up in mos7840_startup as the structures *
+ * were not set up at that time.) */
+ if (mos7840_num_open_ports == 1) {
+ if (serial->port[0]->interrupt_in_buffer == NULL) {
+
+ /* set up interrupt urb */
+
+ usb_fill_int_urb(serial->port[0]->interrupt_in_urb,
+ serial->dev,
+ usb_rcvintpipe(serial->dev,
+ serial->port[0]->
+ interrupt_in_endpointAddress),
+ serial->port[0]->interrupt_in_buffer,
+ serial->port[0]->interrupt_in_urb->
+ transfer_buffer_length,
+ mos7840_interrupt_callback,
+ serial,
+ serial->port[0]->interrupt_in_urb->
+ interval);
+
+ /* start interrupt read for mos7840 *
+ * will continue as long as mos7840 is connected */
+
+ response =
+ usb_submit_urb(serial->port[0]->interrupt_in_urb,
+ GFP_KERNEL);
+ if (response) {
+ err("%s - Error %d submitting interrupt urb",
+ __FUNCTION__, response);
+ }
+
+ }
+
+ }
+
+ /* see if we've set up our endpoint info yet *
+ * (can't set it up in mos7840_startup as the *
+ * structures were not set up at that time.) */
+
+ dbg("port number is %d \n", port->number);
+ dbg("serial number is %d \n", port->serial->minor);
+ dbg("Bulkin endpoint is %d \n", port->bulk_in_endpointAddress);
+ dbg("BulkOut endpoint is %d \n", port->bulk_out_endpointAddress);
+ dbg("Interrupt endpoint is %d \n", port->interrupt_in_endpointAddress);
+ dbg("port's number in the device is %d\n", mos7840_port->port_num);
+ mos7840_port->read_urb = port->read_urb;
+
+ /* set up our bulk in urb */
+
+ usb_fill_bulk_urb(mos7840_port->read_urb,
+ serial->dev,
+ usb_rcvbulkpipe(serial->dev,
+ port->bulk_in_endpointAddress),
+ port->bulk_in_buffer,
+ mos7840_port->read_urb->transfer_buffer_length,
+ mos7840_bulk_in_callback, mos7840_port);
+
+ dbg("mos7840_open: bulkin endpoint is %d\n",
+ port->bulk_in_endpointAddress);
+ response = usb_submit_urb(mos7840_port->read_urb, GFP_KERNEL);
+ if (response) {
+ err("%s - Error %d submitting control urb", __FUNCTION__,
+ response);
+ }
+
+ /* initialize our wait queues */
+ init_waitqueue_head(&mos7840_port->wait_chase);
+ init_waitqueue_head(&mos7840_port->delta_msr_wait);
+
+ /* initialize our icount structure */
+ memset(&(mos7840_port->icount), 0x00, sizeof(mos7840_port->icount));
+
+ /* initialize our port settings */
+ mos7840_port->shadowMCR = MCR_MASTER_IE; /* Must set to enable ints! */
+ /* send a open port command */
+ mos7840_port->open = 1;
+ //mos7840_change_port_settings(mos7840_port,old_termios);
+ mos7840_port->icount.tx = 0;
+ mos7840_port->icount.rx = 0;
+
+ dbg("\n\nusb_serial serial:%x mos7840_port:%x\n usb_serial_port port:%x\n\n", (unsigned int)serial, (unsigned int)mos7840_port, (unsigned int)port);
+
+ return 0;
+
+}
+
+/*****************************************************************************
+ * mos7840_chars_in_buffer
+ * this function is called by the tty driver when it wants to know how many
+ * bytes of data we currently have outstanding in the port (data that has
+ * been written, but hasn't made it out the port yet)
+ * If successful, we return the number of bytes left to be written in the
+ * system,
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_chars_in_buffer(struct usb_serial_port *port)
+{
+ int i;
+ int chars = 0;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", " mos7840_chars_in_buffer:entering ...........");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s \n", "mos7840_break:leaving ...........");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status == -EINPROGRESS) {
+ chars += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+ dbg("%s - returns %d", __FUNCTION__, chars);
+ return (chars);
+
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_tx_empty
+ *
+ * This function will block the close until one of the following:
+ * 1. TX count are 0
+ * 2. The mos7840 has stopped
+ * 3. A timout of 3 seconds without activity has expired
+ *
+ ************************************************************************/
+static void mos7840_block_until_tx_empty(struct moschip_port *mos7840_port)
+{
+ int timeout = HZ / 10;
+ int wait = 30;
+ int count;
+
+ while (1) {
+
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+
+ /* Check for Buffer status */
+ if (count <= 0) {
+ return;
+ }
+
+ /* Block the thread for a while */
+ interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+ timeout);
+
+ /* No activity.. count down section */
+ wait--;
+ if (wait == 0) {
+ dbg("%s - TIMEOUT", __FUNCTION__);
+ return;
+ } else {
+ /* Reset timout value back to seconds */
+ wait = 30;
+ }
+ }
+}
+
+/*****************************************************************************
+ * mos7840_close
+ * this function is called by the tty driver when a port is closed
+ *****************************************************************************/
+
+static void mos7840_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+ int j;
+ __u16 Data;
+
+ dbg("%s\n", "mos7840_close:entering...");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL) {
+ return;
+ }
+
+ for (j = 0; j < NUM_URBS; ++j)
+ usb_kill_urb(mos7840_port->write_urb_pool[j]);
+
+ /* Freeing Write URBs */
+ for (j = 0; j < NUM_URBS; ++j) {
+ if (mos7840_port->write_urb_pool[j]) {
+ if (mos7840_port->write_urb_pool[j]->transfer_buffer)
+ kfree(mos7840_port->write_urb_pool[j]->
+ transfer_buffer);
+
+ usb_free_urb(mos7840_port->write_urb_pool[j]);
+ }
+ }
+
+ if (serial->dev) {
+ /* flush and block until tx is empty */
+ mos7840_block_until_tx_empty(mos7840_port);
+ }
+
+ /* While closing port, shutdown all bulk read, write *
+ * and interrupt read if they exists */
+ if (serial->dev) {
+
+ if (mos7840_port->write_urb) {
+ dbg("%s", "Shutdown bulk write\n");
+ usb_kill_urb(mos7840_port->write_urb);
+ }
+
+ if (mos7840_port->read_urb) {
+ dbg("%s", "Shutdown bulk read\n");
+ usb_kill_urb(mos7840_port->read_urb);
+ }
+ if ((&mos7840_port->control_urb)) {
+ dbg("%s", "Shutdown control read\n");
+ // usb_kill_urb (mos7840_port->control_urb);
+
+ }
+ }
+// if(mos7840_port->ctrl_buf != NULL)
+// kfree(mos7840_port->ctrl_buf);
+ mos7840_num_open_ports--;
+ dbg("mos7840_num_open_ports in close%d:in port%d\n",
+ mos7840_num_open_ports, port->number);
+ if (mos7840_num_open_ports == 0) {
+ if (serial->port[0]->interrupt_in_urb) {
+ dbg("%s", "Shutdown interrupt_in_urb\n");
+ }
+ }
+
+ if (mos7840_port->write_urb) {
+ /* if this urb had a transfer buffer already (old tx) free it */
+
+ if (mos7840_port->write_urb->transfer_buffer != NULL) {
+ kfree(mos7840_port->write_urb->transfer_buffer);
+ }
+ usb_free_urb(mos7840_port->write_urb);
+ }
+
+ Data = 0x0;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ Data = 0x00;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ mos7840_port->open = 0;
+
+ dbg("%s \n", "Leaving ............");
+}
+
+/************************************************************************
+ *
+ * mos7840_block_until_chase_response
+ *
+ * This function will block the close until one of the following:
+ * 1. Response to our Chase comes from mos7840
+ * 2. A timout of 10 seconds without activity has expired
+ * (1K of mos7840 data @ 2400 baud ==> 4 sec to empty)
+ *
+ ************************************************************************/
+
+static void mos7840_block_until_chase_response(struct moschip_port
+ *mos7840_port)
+{
+ int timeout = 1 * HZ;
+ int wait = 10;
+ int count;
+
+ while (1) {
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+
+ /* Check for Buffer status */
+ if (count <= 0) {
+ return;
+ }
+
+ /* Block the thread for a while */
+ interruptible_sleep_on_timeout(&mos7840_port->wait_chase,
+ timeout);
+ /* No activity.. count down section */
+ wait--;
+ if (wait == 0) {
+ dbg("%s - TIMEOUT", __FUNCTION__);
+ return;
+ } else {
+ /* Reset timout value back to seconds */
+ wait = 10;
+ }
+ }
+
+}
+
+/*****************************************************************************
+ * mos7840_break
+ * this function sends a break to the port
+ *****************************************************************************/
+static void mos7840_break(struct usb_serial_port *port, int break_state)
+{
+ unsigned char data;
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", "Entering ...........");
+ dbg("mos7840_break: Start\n");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return;
+ }
+
+ serial = mos7840_get_usb_serial(port, __FUNCTION__);
+ if (!serial) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL) {
+ return;
+ }
+
+ if (serial->dev) {
+
+ /* flush and block until tx is empty */
+ mos7840_block_until_chase_response(mos7840_port);
+ }
+
+ if (break_state == -1) {
+ data = mos7840_port->shadowLCR | LCR_SET_BREAK;
+ } else {
+ data = mos7840_port->shadowLCR & ~LCR_SET_BREAK;
+ }
+
+ mos7840_port->shadowLCR = data;
+ dbg("mcs7840_break mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER,
+ mos7840_port->shadowLCR);
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_write_room
+ * this function is called by the tty driver when it wants to know how many
+ * bytes of data we can accept for a specific port.
+ * If successful, we return the amount of room that we have for this port
+ * Otherwise we return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write_room(struct usb_serial_port *port)
+{
+ int i;
+ int room = 0;
+ struct moschip_port *mos7840_port;
+
+ dbg("%s \n", " mos7840_write_room:entering ...........");
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ dbg("%s \n", " mos7840_write_room:leaving ...........");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s \n", "mos7840_break:leaving ...........");
+ return -1;
+ }
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ room += URB_TRANSFER_BUFFER_SIZE;
+ }
+ }
+
+ dbg("%s - returns %d", __FUNCTION__, room);
+ return (room);
+
+}
+
+/*****************************************************************************
+ * mos7840_write
+ * this function is called by the tty driver when data should be written to
+ * the port.
+ * If successful, we return the number of bytes written, otherwise we
+ * return a negative error number.
+ *****************************************************************************/
+
+static int mos7840_write(struct usb_serial_port *port,
+ const unsigned char *data, int count)
+{
+ int status;
+ int i;
+ int bytes_sent = 0;
+ int transfer_size;
+ int from_user = 0;
+
+ struct moschip_port *mos7840_port;
+ struct usb_serial *serial;
+ struct urb *urb;
+ //__u16 Data;
+ const unsigned char *current_position = data;
+ unsigned char *data1;
+ dbg("%s \n", "entering ...........");
+ //dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",mos7840_port->shadowLCR);
+
+#ifdef NOTMOS7840
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, LINE_CONTROL_REGISTER, &Data);
+ mos7840_port->shadowLCR = Data;
+ dbg("mos7840_write: LINE_CONTROL_REGISTER is %x\n", Data);
+ dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+
+ //Data = 0x03;
+ //status = mos7840_set_uart_reg(port,LINE_CONTROL_REGISTER,Data);
+ //mos7840_port->shadowLCR=Data;//Need to add later
+
+ Data |= SERIAL_LCR_DLAB; //data latch enable in LCR 0x80
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ //Data = 0x0c;
+ //status = mos7840_set_uart_reg(port,DIVISOR_LATCH_LSB,Data);
+ Data = 0x00;
+ status = 0;
+ status = mos7840_get_uart_reg(port, DIVISOR_LATCH_LSB, &Data);
+ dbg("mos7840_write:DLL value is %x\n", Data);
+
+ Data = 0x0;
+ status = 0;
+ status = mos7840_get_uart_reg(port, DIVISOR_LATCH_MSB, &Data);
+ dbg("mos7840_write:DLM value is %x\n", Data);
+
+ Data = Data & ~SERIAL_LCR_DLAB;
+ dbg("mos7840_write: mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ status = 0;
+ status = mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+#endif
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Port Paranoia failed \n");
+ return -1;
+ }
+
+ serial = port->serial;
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Serial Paranoia failed \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ if (mos7840_port == NULL) {
+ dbg("%s", "mos7840_port is NULL\n");
+ return -1;
+ }
+
+ /* try to find a free urb in the list */
+ urb = NULL;
+
+ for (i = 0; i < NUM_URBS; ++i) {
+ if (mos7840_port->write_urb_pool[i]->status != -EINPROGRESS) {
+ urb = mos7840_port->write_urb_pool[i];
+ dbg("\nURB:%d", i);
+ break;
+ }
+ }
+
+ if (urb == NULL) {
+ dbg("%s - no more free urbs", __FUNCTION__);
+ goto exit;
+ }
+
+ if (urb->transfer_buffer == NULL) {
+ urb->transfer_buffer =
+ kmalloc(URB_TRANSFER_BUFFER_SIZE, GFP_KERNEL);
+
+ if (urb->transfer_buffer == NULL) {
+ err("%s no more kernel memory...", __FUNCTION__);
+ goto exit;
+ }
+ }
+ transfer_size = min(count, URB_TRANSFER_BUFFER_SIZE);
+
+ if (from_user) {
+ if (copy_from_user
+ (urb->transfer_buffer, current_position, transfer_size)) {
+ bytes_sent = -EFAULT;
+ goto exit;
+ }
+ } else {
+ memcpy(urb->transfer_buffer, current_position, transfer_size);
+ }
+
+ /* fill urb with data and submit */
+ usb_fill_bulk_urb(urb,
+ serial->dev,
+ usb_sndbulkpipe(serial->dev,
+ port->bulk_out_endpointAddress),
+ urb->transfer_buffer,
+ transfer_size,
+ mos7840_bulk_out_data_callback, mos7840_port);
+
+ data1 = urb->transfer_buffer;
+ dbg("\nbulkout endpoint is %d", port->bulk_out_endpointAddress);
+
+ /* send it down the pipe */
+ status = usb_submit_urb(urb, GFP_ATOMIC);
+
+ if (status) {
+ err("%s - usb_submit_urb(write bulk) failed with status = %d",
+ __FUNCTION__, status);
+ bytes_sent = status;
+ goto exit;
+ }
+ bytes_sent = transfer_size;
+ mos7840_port->icount.tx += transfer_size;
+ dbg("mos7840_port->icount.tx is %d:\n", mos7840_port->icount.tx);
+ exit:
+
+ return bytes_sent;
+
+}
+
+/*****************************************************************************
+ * mos7840_throttle
+ * this function is called by the tty driver when it wants to stop the data
+ * being read from the port.
+ *****************************************************************************/
+
+static void mos7840_throttle(struct usb_serial_port *port)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ int status;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ dbg("- port %d\n", port->number);
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return;
+
+ if (!mos7840_port->open) {
+ dbg("%s\n", "port not opened");
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - no tty available", __FUNCTION__);
+ return;
+ }
+
+ /* if we are implementing XON/XOFF, send the stop character */
+ if (I_IXOFF(tty)) {
+ unsigned char stop_char = STOP_CHAR(tty);
+ status = mos7840_write(port, &stop_char, 1);
+ if (status <= 0) {
+ return;
+ }
+ }
+
+ /* if we are implementing RTS/CTS, toggle that line */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ mos7840_port->shadowMCR &= ~MCR_RTS;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+ mos7840_port->shadowMCR);
+
+ if (status < 0) {
+ return;
+ }
+ }
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_unthrottle
+ * this function is called by the tty driver when it wants to resume the data
+ * being read from the port (called after SerialThrottle is called)
+ *****************************************************************************/
+static void mos7840_unthrottle(struct usb_serial_port *port)
+{
+ struct tty_struct *tty;
+ int status;
+ struct moschip_port *mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ if (mos7840_port == NULL)
+ return;
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ tty = port->tty;
+ if (!tty) {
+ dbg("%s - no tty available", __FUNCTION__);
+ return;
+ }
+
+ /* if we are implementing XON/XOFF, send the start character */
+ if (I_IXOFF(tty)) {
+ unsigned char start_char = START_CHAR(tty);
+ status = mos7840_write(port, &start_char, 1);
+ if (status <= 0) {
+ return;
+ }
+ }
+
+ /* if we are implementing RTS/CTS, toggle that line */
+ if (tty->termios->c_cflag & CRTSCTS) {
+ mos7840_port->shadowMCR |= MCR_RTS;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER,
+ mos7840_port->shadowMCR);
+ if (status < 0) {
+ return;
+ }
+ }
+
+ return;
+}
+
+static int mos7840_tiocmget(struct usb_serial_port *port, struct file *file)
+{
+ struct moschip_port *mos7840_port;
+ unsigned int result;
+ __u16 msr;
+ __u16 mcr;
+ int status = 0;
+ mos7840_port = mos7840_get_port_private(port);
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+ status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+ result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
+ | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
+ | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
+ | ((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0)
+ | ((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0)
+ | ((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0)
+ | ((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0);
+
+ dbg("%s - 0x%04X", __FUNCTION__, result);
+
+ return result;
+}
+
+static int mos7840_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
+{
+ struct moschip_port *mos7840_port;
+ unsigned int mcr;
+ unsigned int status;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return -ENODEV;
+
+ mcr = mos7840_port->shadowMCR;
+ if (clear & TIOCM_RTS)
+ mcr &= ~MCR_RTS;
+ if (clear & TIOCM_DTR)
+ mcr &= ~MCR_DTR;
+ if (clear & TIOCM_LOOP)
+ mcr &= ~MCR_LOOPBACK;
+
+ if (set & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ if (set & TIOCM_DTR)
+ mcr |= MCR_DTR;
+ if (set & TIOCM_LOOP)
+ mcr |= MCR_LOOPBACK;
+
+ mos7840_port->shadowMCR = mcr;
+
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, mcr);
+ if (status < 0) {
+ dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_calc_baud_rate_divisor
+ * this function calculates the proper baud rate divisor for the specified
+ * baud rate.
+ *****************************************************************************/
+static int mos7840_calc_baud_rate_divisor(int baudRate, int *divisor,
+ __u16 * clk_sel_val)
+{
+
+ dbg("%s - %d", __FUNCTION__, baudRate);
+
+ if (baudRate <= 115200) {
+ *divisor = 115200 / baudRate;
+ *clk_sel_val = 0x0;
+ }
+ if ((baudRate > 115200) && (baudRate <= 230400)) {
+ *divisor = 230400 / baudRate;
+ *clk_sel_val = 0x10;
+ } else if ((baudRate > 230400) && (baudRate <= 403200)) {
+ *divisor = 403200 / baudRate;
+ *clk_sel_val = 0x20;
+ } else if ((baudRate > 403200) && (baudRate <= 460800)) {
+ *divisor = 460800 / baudRate;
+ *clk_sel_val = 0x30;
+ } else if ((baudRate > 460800) && (baudRate <= 806400)) {
+ *divisor = 806400 / baudRate;
+ *clk_sel_val = 0x40;
+ } else if ((baudRate > 806400) && (baudRate <= 921600)) {
+ *divisor = 921600 / baudRate;
+ *clk_sel_val = 0x50;
+ } else if ((baudRate > 921600) && (baudRate <= 1572864)) {
+ *divisor = 1572864 / baudRate;
+ *clk_sel_val = 0x60;
+ } else if ((baudRate > 1572864) && (baudRate <= 3145728)) {
+ *divisor = 3145728 / baudRate;
+ *clk_sel_val = 0x70;
+ }
+ return 0;
+
+#ifdef NOTMCS7840
+
+ for (i = 0; i < ARRAY_SIZE(mos7840_divisor_table); i++) {
+ if (mos7840_divisor_table[i].BaudRate == baudrate) {
+ *divisor = mos7840_divisor_table[i].Divisor;
+ return 0;
+ }
+ }
+
+ /* After trying for all the standard baud rates *
+ * Try calculating the divisor for this baud rate */
+
+ if (baudrate > 75 && baudrate < 230400) {
+ /* get the divisor */
+ custom = (__u16) (230400L / baudrate);
+
+ /* Check for round off */
+ round1 = (__u16) (2304000L / baudrate);
+ round = (__u16) (round1 - (custom * 10));
+ if (round > 4) {
+ custom++;
+ }
+ *divisor = custom;
+
+ dbg(" Baud %d = %d\n", baudrate, custom);
+ return 0;
+ }
+
+ dbg("%s\n", " Baud calculation Failed...");
+ return -1;
+#endif
+}
+
+/*****************************************************************************
+ * mos7840_send_cmd_write_baud_rate
+ * this function sends the proper command to change the baud rate of the
+ * specified port.
+ *****************************************************************************/
+
+static int mos7840_send_cmd_write_baud_rate(struct moschip_port *mos7840_port,
+ int baudRate)
+{
+ int divisor = 0;
+ int status;
+ __u16 Data;
+ unsigned char number;
+ __u16 clk_sel_val;
+ struct usb_serial_port *port;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return -1;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ number = mos7840_port->port->number - mos7840_port->port->serial->minor;
+
+ dbg("%s - port = %d, baud = %d", __FUNCTION__,
+ mos7840_port->port->number, baudRate);
+ //reset clk_uart_sel in spregOffset
+ if (baudRate > 115200) {
+#ifdef HW_flow_control
+ //NOTE: need to see the pther register to modify
+ //setting h/w flow control bit to 1;
+ status = 0;
+ Data = 0x2b;
+ mos7840_port->shadowMCR = Data;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+#endif
+
+ } else {
+#ifdef HW_flow_control
+ //setting h/w flow control bit to 0;
+ status = 0;
+ Data = 0xb;
+ mos7840_port->shadowMCR = Data;
+ status =
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+#endif
+
+ }
+
+ if (1) //baudRate <= 115200)
+ {
+ clk_sel_val = 0x0;
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_calc_baud_rate_divisor(baudRate, &divisor,
+ &clk_sel_val);
+ status =
+ mos7840_get_reg_sync(port, mos7840_port->SpRegOffset,
+ &Data);
+ if (status < 0) {
+ dbg("reading spreg failed in set_serial_baud\n");
+ return -1;
+ }
+ Data = (Data & 0x8f) | clk_sel_val;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(port, mos7840_port->SpRegOffset, Data);
+ if (status < 0) {
+ dbg("Writing spreg failed in set_serial_baud\n");
+ return -1;
+ }
+ /* Calculate the Divisor */
+
+ if (status) {
+ err("%s - bad baud rate", __FUNCTION__);
+ dbg("%s\n", "bad baud rate");
+ return status;
+ }
+ /* Enable access to divisor latch */
+ Data = mos7840_port->shadowLCR | SERIAL_LCR_DLAB;
+ mos7840_port->shadowLCR = Data;
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ /* Write the divisor */
+ Data = (unsigned char)(divisor & 0xff);
+ dbg("set_serial_baud Value to write DLL is %x\n", Data);
+ mos7840_set_uart_reg(port, DIVISOR_LATCH_LSB, Data);
+
+ Data = (unsigned char)((divisor & 0xff00) >> 8);
+ dbg("set_serial_baud Value to write DLM is %x\n", Data);
+ mos7840_set_uart_reg(port, DIVISOR_LATCH_MSB, Data);
+
+ /* Disable access to divisor latch */
+ Data = mos7840_port->shadowLCR & ~SERIAL_LCR_DLAB;
+ mos7840_port->shadowLCR = Data;
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ }
+
+ return status;
+}
+
+/*****************************************************************************
+ * mos7840_change_port_settings
+ * This routine is called to set the UART on the device to match
+ * the specified new settings.
+ *****************************************************************************/
+
+static void mos7840_change_port_settings(struct moschip_port *mos7840_port,
+ struct termios *old_termios)
+{
+ struct tty_struct *tty;
+ int baud;
+ unsigned cflag;
+ unsigned iflag;
+ __u8 lData;
+ __u8 lParity;
+ __u8 lStop;
+ int status;
+ __u16 Data;
+ struct usb_serial_port *port;
+ struct usb_serial *serial;
+
+ if (mos7840_port == NULL)
+ return;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ if (mos7840_serial_paranoia_check(port->serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return;
+ }
+
+ serial = port->serial;
+
+ dbg("%s - port %d", __FUNCTION__, mos7840_port->port->number);
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ tty = mos7840_port->port->tty;
+
+ if ((!tty) || (!tty->termios)) {
+ dbg("%s - no tty structures", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s", "Entering .......... \n");
+
+ lData = LCR_BITS_8;
+ lStop = LCR_STOP_1;
+ lParity = LCR_PAR_NONE;
+
+ cflag = tty->termios->c_cflag;
+ iflag = tty->termios->c_iflag;
+
+ /* Change the number of bits */
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
+ case CS5:
+ lData = LCR_BITS_5;
+ break;
+
+ case CS6:
+ lData = LCR_BITS_6;
+ break;
+
+ case CS7:
+ lData = LCR_BITS_7;
+ break;
+ default:
+ case CS8:
+ lData = LCR_BITS_8;
+ break;
+ }
+ }
+ /* Change the Parity bit */
+ if (cflag & PARENB) {
+ if (cflag & PARODD) {
+ lParity = LCR_PAR_ODD;
+ dbg("%s - parity = odd", __FUNCTION__);
+ } else {
+ lParity = LCR_PAR_EVEN;
+ dbg("%s - parity = even", __FUNCTION__);
+ }
+
+ } else {
+ dbg("%s - parity = none", __FUNCTION__);
+ }
+
+ if (cflag & CMSPAR) {
+ lParity = lParity | 0x20;
+ }
+
+ /* Change the Stop bit */
+ if (cflag & CSTOPB) {
+ lStop = LCR_STOP_2;
+ dbg("%s - stop bits = 2", __FUNCTION__);
+ } else {
+ lStop = LCR_STOP_1;
+ dbg("%s - stop bits = 1", __FUNCTION__);
+ }
+
+ /* Update the LCR with the correct value */
+ mos7840_port->shadowLCR &=
+ ~(LCR_BITS_MASK | LCR_STOP_MASK | LCR_PAR_MASK);
+ mos7840_port->shadowLCR |= (lData | lParity | lStop);
+
+ dbg("mos7840_change_port_settings mos7840_port->shadowLCR is %x\n",
+ mos7840_port->shadowLCR);
+ /* Disable Interrupts */
+ Data = 0x00;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ Data = 0x00;
+ mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+ Data = 0xcf;
+ mos7840_set_uart_reg(port, FIFO_CONTROL_REGISTER, Data);
+
+ /* Send the updated LCR value to the mos7840 */
+ Data = mos7840_port->shadowLCR;
+
+ mos7840_set_uart_reg(port, LINE_CONTROL_REGISTER, Data);
+
+ Data = 0x00b;
+ mos7840_port->shadowMCR = Data;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ Data = 0x00b;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ /* set up the MCR register and send it to the mos7840 */
+
+ mos7840_port->shadowMCR = MCR_MASTER_IE;
+ if (cflag & CBAUD) {
+ mos7840_port->shadowMCR |= (MCR_DTR | MCR_RTS);
+ }
+
+ if (cflag & CRTSCTS) {
+ mos7840_port->shadowMCR |= (MCR_XON_ANY);
+
+ } else {
+ mos7840_port->shadowMCR &= ~(MCR_XON_ANY);
+ }
+
+ Data = mos7840_port->shadowMCR;
+ mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+
+ /* Determine divisor based on baud rate */
+ baud = tty_get_baud_rate(tty);
+
+ if (!baud) {
+ /* pick a default, any default... */
+ dbg("%s\n", "Picked default baud...");
+ baud = 9600;
+ }
+
+ dbg("%s - baud rate = %d", __FUNCTION__, baud);
+ status = mos7840_send_cmd_write_baud_rate(mos7840_port, baud);
+
+ /* Enable Interrupts */
+ Data = 0x0c;
+ mos7840_set_uart_reg(port, INTERRUPT_ENABLE_REGISTER, Data);
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+ wake_up(&mos7840_port->delta_msr_wait);
+ mos7840_port->delta_msr_cond = 1;
+ dbg("mos7840_change_port_settings mos7840_port->shadowLCR is End %x\n",
+ mos7840_port->shadowLCR);
+
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_set_termios
+ * this function is called by the tty driver when it wants to change
+ * the termios structure
+ *****************************************************************************/
+
+static void mos7840_set_termios(struct usb_serial_port *port,
+ struct termios *old_termios)
+{
+ int status;
+ unsigned int cflag;
+ struct usb_serial *serial;
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+ dbg("mos7840_set_termios: START\n");
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return;
+ }
+
+ serial = port->serial;
+
+ if (mos7840_serial_paranoia_check(serial, __FUNCTION__)) {
+ dbg("%s", "Invalid Serial \n");
+ return;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+
+ if (mos7840_port == NULL)
+ return;
+
+ tty = port->tty;
+
+ if (!port->tty || !port->tty->termios) {
+ dbg("%s - no tty or termios", __FUNCTION__);
+ return;
+ }
+
+ if (!mos7840_port->open) {
+ dbg("%s - port not opened", __FUNCTION__);
+ return;
+ }
+
+ dbg("%s\n", "setting termios - ");
+
+ cflag = tty->termios->c_cflag;
+
+ if (!cflag) {
+ dbg("%s %s\n", __FUNCTION__, "cflag is NULL");
+ return;
+ }
+
+ /* check that they really want us to change something */
+ if (old_termios) {
+ if ((cflag == old_termios->c_cflag) &&
+ (RELEVANT_IFLAG(tty->termios->c_iflag) ==
+ RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("%s\n", "Nothing to change");
+ return;
+ }
+ }
+
+ dbg("%s - clfag %08x iflag %08x", __FUNCTION__,
+ tty->termios->c_cflag, RELEVANT_IFLAG(tty->termios->c_iflag));
+
+ if (old_termios) {
+ dbg("%s - old clfag %08x old iflag %08x", __FUNCTION__,
+ old_termios->c_cflag, RELEVANT_IFLAG(old_termios->c_iflag));
+ }
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* change the port settings to the new ones specified */
+
+ mos7840_change_port_settings(mos7840_port, old_termios);
+
+ if (!mos7840_port->read_urb) {
+ dbg("%s", "URB KILLED !!!!!\n");
+ return;
+ }
+
+ if (mos7840_port->read_urb->status != -EINPROGRESS) {
+ mos7840_port->read_urb->dev = serial->dev;
+ status = usb_submit_urb(mos7840_port->read_urb, GFP_ATOMIC);
+ if (status) {
+ dbg(" usb_submit_urb(read bulk) failed, status = %d",
+ status);
+ }
+ }
+ return;
+}
+
+/*****************************************************************************
+ * mos7840_get_lsr_info - get line status register info
+ *
+ * Purpose: Let user call ioctl() to get info when the UART physically
+ * is emptied. On bus types like RS485, the transmitter must
+ * release the bus after transmitting. This must be done when
+ * the transmit shift register is empty, not be done when the
+ * transmit holding register is empty. This functionality
+ * allows an RS485 driver to be written in user space.
+ *****************************************************************************/
+
+static int mos7840_get_lsr_info(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ int count;
+ unsigned int result = 0;
+
+ count = mos7840_chars_in_buffer(mos7840_port->port);
+ if (count == 0) {
+ dbg("%s -- Empty", __FUNCTION__);
+ result = TIOCSER_TEMT;
+ }
+
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_bytes_avail - get number of bytes available
+ *
+ * Purpose: Let user call ioctl to get the count of number of bytes available.
+ *****************************************************************************/
+
+static int mos7840_get_bytes_avail(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ unsigned int result = 0;
+ struct tty_struct *tty = mos7840_port->port->tty;
+
+ if (!tty)
+ return -ENOIOCTLCMD;
+
+ result = tty->read_cnt;
+
+ dbg("%s(%d) = %d", __FUNCTION__, mos7840_port->port->number, result);
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+
+ return -ENOIOCTLCMD;
+}
+
+/*****************************************************************************
+ * mos7840_set_modem_info
+ * function to set modem info
+ *****************************************************************************/
+
+static int mos7840_set_modem_info(struct moschip_port *mos7840_port,
+ unsigned int cmd, unsigned int *value)
+{
+ unsigned int mcr;
+ unsigned int arg;
+ __u16 Data;
+ int status;
+ struct usb_serial_port *port;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ port = (struct usb_serial_port *)mos7840_port->port;
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mcr = mos7840_port->shadowMCR;
+
+ if (copy_from_user(&arg, value, sizeof(int)))
+ return -EFAULT;
+
+ switch (cmd) {
+ case TIOCMBIS:
+ if (arg & TIOCM_RTS)
+ mcr |= MCR_RTS;
+ if (arg & TIOCM_DTR)
+ mcr |= MCR_RTS;
+ if (arg & TIOCM_LOOP)
+ mcr |= MCR_LOOPBACK;
+ break;
+
+ case TIOCMBIC:
+ if (arg & TIOCM_RTS)
+ mcr &= ~MCR_RTS;
+ if (arg & TIOCM_DTR)
+ mcr &= ~MCR_RTS;
+ if (arg & TIOCM_LOOP)
+ mcr &= ~MCR_LOOPBACK;
+ break;
+
+ case TIOCMSET:
+ /* turn off the RTS and DTR and LOOPBACK
+ * and then only turn on what was asked to */
+ mcr &= ~(MCR_RTS | MCR_DTR | MCR_LOOPBACK);
+ mcr |= ((arg & TIOCM_RTS) ? MCR_RTS : 0);
+ mcr |= ((arg & TIOCM_DTR) ? MCR_DTR : 0);
+ mcr |= ((arg & TIOCM_LOOP) ? MCR_LOOPBACK : 0);
+ break;
+ }
+
+ mos7840_port->shadowMCR = mcr;
+
+ Data = mos7840_port->shadowMCR;
+ status = 0;
+ status = mos7840_set_uart_reg(port, MODEM_CONTROL_REGISTER, Data);
+ if (status < 0) {
+ dbg("setting MODEM_CONTROL_REGISTER Failed\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_modem_info
+ * function to get modem info
+ *****************************************************************************/
+
+static int mos7840_get_modem_info(struct moschip_port *mos7840_port,
+ unsigned int *value)
+{
+ unsigned int result = 0;
+ __u16 msr;
+ unsigned int mcr = mos7840_port->shadowMCR;
+ int status = 0;
+ status =
+ mos7840_get_uart_reg(mos7840_port->port, MODEM_STATUS_REGISTER,
+ &msr);
+ result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0) /* 0x002 */
+ |((mcr & MCR_RTS) ? TIOCM_RTS : 0) /* 0x004 */
+ |((msr & MOS7840_MSR_CTS) ? TIOCM_CTS : 0) /* 0x020 */
+ |((msr & MOS7840_MSR_CD) ? TIOCM_CAR : 0) /* 0x040 */
+ |((msr & MOS7840_MSR_RI) ? TIOCM_RI : 0) /* 0x080 */
+ |((msr & MOS7840_MSR_DSR) ? TIOCM_DSR : 0); /* 0x100 */
+
+ dbg("%s -- %x", __FUNCTION__, result);
+
+ if (copy_to_user(value, &result, sizeof(int)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * mos7840_get_serial_info
+ * function to get information about serial port
+ *****************************************************************************/
+
+static int mos7840_get_serial_info(struct moschip_port *mos7840_port,
+ struct serial_struct *retinfo)
+{
+ struct serial_struct tmp;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ if (!retinfo)
+ return -EFAULT;
+
+ memset(&tmp, 0, sizeof(tmp));
+
+ tmp.type = PORT_16550A;
+ tmp.line = mos7840_port->port->serial->minor;
+ tmp.port = mos7840_port->port->number;
+ tmp.irq = 0;
+ tmp.flags = ASYNC_SKIP_TEST | ASYNC_AUTO_IRQ;
+ tmp.xmit_fifo_size = NUM_URBS * URB_TRANSFER_BUFFER_SIZE;
+ tmp.baud_base = 9600;
+ tmp.close_delay = 5 * HZ;
+ tmp.closing_wait = 30 * HZ;
+
+ if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
+ return -EFAULT;
+ return 0;
+}
+
+/*****************************************************************************
+ * SerialIoctl
+ * this function handles any ioctl calls to the driver
+ *****************************************************************************/
+
+static int mos7840_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct moschip_port *mos7840_port;
+ struct tty_struct *tty;
+
+ struct async_icount cnow;
+ struct async_icount cprev;
+ struct serial_icounter_struct icount;
+ int mosret = 0;
+ int retval;
+ struct tty_ldisc *ld;
+
+ if (mos7840_port_paranoia_check(port, __FUNCTION__)) {
+ dbg("%s", "Invalid port \n");
+ return -1;
+ }
+
+ mos7840_port = mos7840_get_port_private(port);
+ tty = mos7840_port->port->tty;
+
+ if (mos7840_port == NULL)
+ return -1;
+
+ dbg("%s - port %d, cmd = 0x%x", __FUNCTION__, port->number, cmd);
+
+ switch (cmd) {
+ /* return number of bytes available */
+
+ case TIOCINQ:
+ dbg("%s (%d) TIOCINQ", __FUNCTION__, port->number);
+ return mos7840_get_bytes_avail(mos7840_port,
+ (unsigned int *)arg);
+ break;
+
+ case TIOCOUTQ:
+ dbg("%s (%d) TIOCOUTQ", __FUNCTION__, port->number);
+ return put_user(tty->driver->chars_in_buffer ?
+ tty->driver->chars_in_buffer(tty) : 0,
+ (int __user *)arg);
+ break;
+
+ case TCFLSH:
+ retval = tty_check_change(tty);
+ if (retval)
+ return retval;
+
+ ld = tty_ldisc_ref(tty);
+ switch (arg) {
+ case TCIFLUSH:
+ if (ld && ld->flush_buffer)
+ ld->flush_buffer(tty);
+ break;
+ case TCIOFLUSH:
+ if (ld && ld->flush_buffer)
+ ld->flush_buffer(tty);
+ /* fall through */
+ case TCOFLUSH:
+ if (tty->driver->flush_buffer)
+ tty->driver->flush_buffer(tty);
+ break;
+ default:
+ tty_ldisc_deref(ld);
+ return -EINVAL;
+ }
+ tty_ldisc_deref(ld);
+ return 0;
+
+ case TCGETS:
+ if (kernel_termios_to_user_termios
+ ((struct termios __user *)arg, tty->termios))
+ return -EFAULT;
+ return 0;
+
+ case TIOCSERGETLSR:
+ dbg("%s (%d) TIOCSERGETLSR", __FUNCTION__, port->number);
+ return mos7840_get_lsr_info(mos7840_port, (unsigned int *)arg);
+ return 0;
+
+ case TIOCMBIS:
+ case TIOCMBIC:
+ case TIOCMSET:
+ dbg("%s (%d) TIOCMSET/TIOCMBIC/TIOCMSET", __FUNCTION__,
+ port->number);
+ mosret =
+ mos7840_set_modem_info(mos7840_port, cmd,
+ (unsigned int *)arg);
+ return mosret;
+
+ case TIOCMGET:
+ dbg("%s (%d) TIOCMGET", __FUNCTION__, port->number);
+ return mos7840_get_modem_info(mos7840_port,
+ (unsigned int *)arg);
+
+ case TIOCGSERIAL:
+ dbg("%s (%d) TIOCGSERIAL", __FUNCTION__, port->number);
+ return mos7840_get_serial_info(mos7840_port,
+ (struct serial_struct *)arg);
+
+ case TIOCSSERIAL:
+ dbg("%s (%d) TIOCSSERIAL", __FUNCTION__, port->number);
+ break;
+
+ case TIOCMIWAIT:
+ dbg("%s (%d) TIOCMIWAIT", __FUNCTION__, port->number);
+ cprev = mos7840_port->icount;
+ while (1) {
+ //interruptible_sleep_on(&mos7840_port->delta_msr_wait);
+ mos7840_port->delta_msr_cond = 0;
+ wait_event_interruptible(mos7840_port->delta_msr_wait,
+ (mos7840_port->
+ delta_msr_cond == 1));
+
+ /* see if a signal did it */
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ cnow = mos7840_port->icount;
+ if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
+ cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
+ return -EIO; /* no change => error */
+ if (((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
+ ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
+ ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
+ ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
+ return 0;
+ }
+ cprev = cnow;
+ }
+ /* NOTREACHED */
+ break;
+
+ case TIOCGICOUNT:
+ cnow = mos7840_port->icount;
+ icount.cts = cnow.cts;
+ icount.dsr = cnow.dsr;
+ icount.rng = cnow.rng;
+ icount.dcd = cnow.dcd;
+ icount.rx = cnow.rx;
+ icount.tx = cnow.tx;
+ icount.frame = cnow.frame;
+ icount.overrun = cnow.overrun;
+ icount.parity = cnow.parity;
+ icount.brk = cnow.brk;
+ icount.buf_overrun = cnow.buf_overrun;
+
+ dbg("%s (%d) TIOCGICOUNT RX=%d, TX=%d", __FUNCTION__,
+ port->number, icount.rx, icount.tx);
+ if (copy_to_user((void *)arg, &icount, sizeof(icount)))
+ return -EFAULT;
+ return 0;
+
+ case TIOCEXBAUD:
+ return 0;
+ default:
+ break;
+ }
+
+ return -ENOIOCTLCMD;
+}
+
+static int mos7840_calc_num_ports(struct usb_serial *serial)
+{
+
+ dbg("numberofendpoints: %d \n",
+ (int)serial->interface->cur_altsetting->desc.bNumEndpoints);
+ dbg("numberofendpoints: %d \n",
+ (int)serial->interface->altsetting->desc.bNumEndpoints);
+ if (serial->interface->cur_altsetting->desc.bNumEndpoints == 5) {
+ mos7840_num_ports = 2;
+ serial->type->num_ports = 2;
+ } else if (serial->interface->cur_altsetting->desc.bNumEndpoints == 9) {
+ mos7840_num_ports = 4;
+ serial->type->num_bulk_in = 4;
+ serial->type->num_bulk_out = 4;
+ serial->type->num_ports = 4;
+ }
+
+ return mos7840_num_ports;
+}
+
+/****************************************************************************
+ * mos7840_startup
+ ****************************************************************************/
+
+static int mos7840_startup(struct usb_serial *serial)
+{
+ struct moschip_port *mos7840_port;
+ struct usb_device *dev;
+ int i, status;
+
+ __u16 Data;
+ dbg("%s \n", " mos7840_startup :entering..........");
+
+ if (!serial) {
+ dbg("%s\n", "Invalid Handler");
+ return -1;
+ }
+
+ dev = serial->dev;
+
+ dbg("%s\n", "Entering...");
+
+ /* we set up the pointers to the endpoints in the mos7840_open *
+ * function, as the structures aren't created yet. */
+
+ /* set up port private structures */
+ for (i = 0; i < serial->num_ports; ++i) {
+ mos7840_port = kmalloc(sizeof(struct moschip_port), GFP_KERNEL);
+ if (mos7840_port == NULL) {
+ err("%s - Out of memory", __FUNCTION__);
+ return -ENOMEM;
+ }
+ memset(mos7840_port, 0, sizeof(struct moschip_port));
+
+ /* Initialize all port interrupt end point to port 0 int endpoint *
+ * Our device has only one interrupt end point comman to all port */
+
+ mos7840_port->port = serial->port[i];
+ mos7840_set_port_private(serial->port[i], mos7840_port);
+
+ mos7840_port->port_num = ((serial->port[i]->number -
+ (serial->port[i]->serial->minor)) +
+ 1);
+
+ if (mos7840_port->port_num == 1) {
+ mos7840_port->SpRegOffset = 0x0;
+ mos7840_port->ControlRegOffset = 0x1;
+ mos7840_port->DcrRegOffset = 0x4;
+ } else if ((mos7840_port->port_num == 2)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0x8;
+ mos7840_port->ControlRegOffset = 0x9;
+ mos7840_port->DcrRegOffset = 0x16;
+ } else if ((mos7840_port->port_num == 2)
+ && (mos7840_num_ports == 2)) {
+ mos7840_port->SpRegOffset = 0xa;
+ mos7840_port->ControlRegOffset = 0xb;
+ mos7840_port->DcrRegOffset = 0x19;
+ } else if ((mos7840_port->port_num == 3)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0xa;
+ mos7840_port->ControlRegOffset = 0xb;
+ mos7840_port->DcrRegOffset = 0x19;
+ } else if ((mos7840_port->port_num == 4)
+ && (mos7840_num_ports == 4)) {
+ mos7840_port->SpRegOffset = 0xc;
+ mos7840_port->ControlRegOffset = 0xd;
+ mos7840_port->DcrRegOffset = 0x1c;
+ }
+ mos7840_dump_serial_port(mos7840_port);
+
+ mos7840_set_port_private(serial->port[i], mos7840_port);
+
+ //enable rx_disable bit in control register
+
+ status =
+ mos7840_get_reg_sync(serial->port[i],
+ mos7840_port->ControlRegOffset, &Data);
+ if (status < 0) {
+ dbg("Reading ControlReg failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("ControlReg Reading success val is %x, status%d\n",
+ Data, status);
+ Data |= 0x08; //setting driver done bit
+ Data |= 0x04; //sp1_bit to have cts change reflect in modem status reg
+
+ //Data |= 0x20; //rx_disable bit
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ mos7840_port->ControlRegOffset, Data);
+ if (status < 0) {
+ dbg("Writing ControlReg failed(rx_disable) status-0x%x\n", status);
+ break;
+ } else
+ dbg("ControlReg Writing success(rx_disable) status%d\n",
+ status);
+
+ //Write default values in DCR (i.e 0x01 in DCR0, 0x05 in DCR2 and 0x24 in DCR3
+ Data = 0x01;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 0), Data);
+ if (status < 0) {
+ dbg("Writing DCR0 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR0 Writing success status%d\n", status);
+
+ Data = 0x05;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 1), Data);
+ if (status < 0) {
+ dbg("Writing DCR1 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR1 Writing success status%d\n", status);
+
+ Data = 0x24;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ (__u16) (mos7840_port->DcrRegOffset +
+ 2), Data);
+ if (status < 0) {
+ dbg("Writing DCR2 failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("DCR2 Writing success status%d\n", status);
+
+ // write values in clkstart0x0 and clkmulti 0x20
+ Data = 0x0;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i],
+ CLK_START_VALUE_REGISTER, Data);
+ if (status < 0) {
+ dbg("Writing CLK_START_VALUE_REGISTER failed status-0x%x\n", status);
+ break;
+ } else
+ dbg("CLK_START_VALUE_REGISTER Writing success status%d\n", status);
+
+ Data = 0x20;
+ status = 0;
+ status =
+ mos7840_set_reg_sync(serial->port[i], CLK_MULTI_REGISTER,
+ Data);
+ if (status < 0) {
+ dbg("Writing CLK_MULTI_REGISTER failed status-0x%x\n",
+ status);
+ break;
+ } else
+ dbg("CLK_MULTI_REGISTER Writing success status%d\n",
+ status);
+
+ //write value 0x0 to scratchpad register
+ Data = 0x00;
+ status = 0;
+ status =
+ mos7840_set_uart_reg(serial->port[i], SCRATCH_PAD_REGISTER,
+ Data);
+ if (status < 0) {
+ dbg("Writing SCRATCH_PAD_REGISTER failed status-0x%x\n",
+ status);
+ break;
+ } else
+ dbg("SCRATCH_PAD_REGISTER Writing success status%d\n",
+ status);
+
+ //Zero Length flag register
+ if ((mos7840_port->port_num != 1)
+ && (mos7840_num_ports == 2)) {
+
+ Data = 0xff;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[i],
+ (__u16) (ZLP_REG1 +
+ ((__u16)
+ mos7840_port->
+ port_num)),
+ Data);
+ dbg("ZLIP offset%x\n",
+ (__u16) (ZLP_REG1 +
+ ((__u16) mos7840_port->port_num)));
+ if (status < 0) {
+ dbg("Writing ZLP_REG%d failed status-0x%x\n",
+ i + 2, status);
+ break;
+ } else
+ dbg("ZLP_REG%d Writing success status%d\n",
+ i + 2, status);
+ } else {
+ Data = 0xff;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[i],
+ (__u16) (ZLP_REG1 +
+ ((__u16)
+ mos7840_port->
+ port_num) -
+ 0x1), Data);
+ dbg("ZLIP offset%x\n",
+ (__u16) (ZLP_REG1 +
+ ((__u16) mos7840_port->port_num) - 0x1));
+ if (status < 0) {
+ dbg("Writing ZLP_REG%d failed status-0x%x\n",
+ i + 1, status);
+ break;
+ } else
+ dbg("ZLP_REG%d Writing success status%d\n",
+ i + 1, status);
+
+ }
+ mos7840_port->control_urb = usb_alloc_urb(0, SLAB_ATOMIC);
+ mos7840_port->ctrl_buf = kmalloc(16, GFP_KERNEL);
+
+ }
+
+ //Zero Length flag enable
+ Data = 0x0f;
+ status = 0;
+ status = mos7840_set_reg_sync(serial->port[0], ZLP_REG5, Data);
+ if (status < 0) {
+ dbg("Writing ZLP_REG5 failed status-0x%x\n", status);
+ return -1;
+ } else
+ dbg("ZLP_REG5 Writing success status%d\n", status);
+
+ /* setting configuration feature to one */
+ usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ (__u8) 0x03, 0x00, 0x01, 0x00, 0x00, 0x00, 5 * HZ);
+ return 0;
+}
+
+/****************************************************************************
+ * mos7840_shutdown
+ * This function is called whenever the device is removed from the usb bus.
+ ****************************************************************************/
+
+static void mos7840_shutdown(struct usb_serial *serial)
+{
+ int i;
+ struct moschip_port *mos7840_port;
+ dbg("%s \n", " shutdown :entering..........");
+
+ if (!serial) {
+ dbg("%s", "Invalid Handler \n");
+ return;
+ }
+
+ /* check for the ports to be closed,close the ports and disconnect */
+
+ /* free private structure allocated for serial port *
+ * stop reads and writes on all ports */
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ mos7840_port = mos7840_get_port_private(serial->port[i]);
+ kfree(mos7840_port->ctrl_buf);
+ usb_kill_urb(mos7840_port->control_urb);
+ kfree(mos7840_port);
+ mos7840_set_port_private(serial->port[i], NULL);
+ }
+
+ dbg("%s\n", "Thank u :: ");
+
+}
+
+static struct usb_serial_driver moschip7840_4port_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "mos7840",
+ },
+ .description = DRIVER_DESC,
+ .id_table = moschip_port_id_table,
+ .num_interrupt_in = 1, //NUM_DONT_CARE,//1,
+#ifdef check
+ .num_bulk_in = 4,
+ .num_bulk_out = 4,
+ .num_ports = 4,
+#endif
+ .open = mos7840_open,
+ .close = mos7840_close,
+ .write = mos7840_write,
+ .write_room = mos7840_write_room,
+ .chars_in_buffer = mos7840_chars_in_buffer,
+ .throttle = mos7840_throttle,
+ .unthrottle = mos7840_unthrottle,
+ .calc_num_ports = mos7840_calc_num_ports,
+#ifdef MCSSerialProbe
+ .probe = mos7840_serial_probe,
+#endif
+ .ioctl = mos7840_ioctl,
+ .set_termios = mos7840_set_termios,
+ .break_ctl = mos7840_break,
+ .tiocmget = mos7840_tiocmget,
+ .tiocmset = mos7840_tiocmset,
+ .attach = mos7840_startup,
+ .shutdown = mos7840_shutdown,
+ .read_bulk_callback = mos7840_bulk_in_callback,
+ .read_int_callback = mos7840_interrupt_callback,
+};
+
+static struct usb_driver io_driver = {
+ .name = "mos7840",
+ .probe = usb_serial_probe,
+ .disconnect = usb_serial_disconnect,
+ .id_table = moschip_id_table_combined,
+};
+
+/****************************************************************************
+ * moschip7840_init
+ * This is called by the module subsystem, or on startup to initialize us
+ ****************************************************************************/
+static int __init moschip7840_init(void)
+{
+ int retval;
+
+ dbg("%s \n", " mos7840_init :entering..........");
+
+ /* Register with the usb serial */
+ retval = usb_serial_register(&moschip7840_4port_device);
+
+ if (retval)
+ goto failed_port_device_register;
+
+ dbg("%s\n", "Entring...");
+ info(DRIVER_DESC " " DRIVER_VERSION);
+
+ /* Register with the usb */
+ retval = usb_register(&io_driver);
+
+ if (retval)
+ goto failed_usb_register;
+
+ if (retval == 0) {
+ dbg("%s\n", "Leaving...");
+ return 0;
+ }
+
+ failed_usb_register:
+ usb_serial_deregister(&moschip7840_4port_device);
+
+ failed_port_device_register:
+
+ return retval;
+}
+
+/****************************************************************************
+ * moschip7840_exit
+ * Called when the driver is about to be unloaded.
+ ****************************************************************************/
+static void __exit moschip7840_exit(void)
+{
+
+ dbg("%s \n", " mos7840_exit :entering..........");
+
+ usb_deregister(&io_driver);
+
+ usb_serial_deregister(&moschip7840_4port_device);
+
+ dbg("%s\n", "Entring...");
+}
+
+module_init(moschip7840_init);
+module_exit(moschip7840_exit);
+
+/* Module information */
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+module_param(debug, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug enabled or not");
diff --git a/drivers/usb/serial/omninet.c b/drivers/usb/serial/omninet.c
index e49f40913c2..a764ff4e326 100644
--- a/drivers/usb/serial/omninet.c
+++ b/drivers/usb/serial/omninet.c
@@ -256,14 +256,14 @@ static int omninet_write (struct usb_serial_port *port, const unsigned char *buf
return (0);
}
- spin_lock(&wport->lock);
+ spin_lock_bh(&wport->lock);
if (wport->write_urb_busy) {
- spin_unlock(&wport->lock);
+ spin_unlock_bh(&wport->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
wport->write_urb_busy = 1;
- spin_unlock(&wport->lock);
+ spin_unlock_bh(&wport->lock);
count = (count > OMNINET_BULKOUTSIZE) ? OMNINET_BULKOUTSIZE : count;
diff --git a/drivers/usb/serial/pl2303.c b/drivers/usb/serial/pl2303.c
index 65e4d046951..9c18173e33f 100644
--- a/drivers/usb/serial/pl2303.c
+++ b/drivers/usb/serial/pl2303.c
@@ -81,10 +81,12 @@ static struct usb_device_id id_table [] = {
{ USB_DEVICE(SPEEDDRAGON_VENDOR_ID, SPEEDDRAGON_PRODUCT_ID) },
{ USB_DEVICE(DATAPILOT_U2_VENDOR_ID, DATAPILOT_U2_PRODUCT_ID) },
{ USB_DEVICE(BELKIN_VENDOR_ID, BELKIN_PRODUCT_ID) },
+ { USB_DEVICE(ALCOR_VENDOR_ID, ALCOR_PRODUCT_ID) },
+ { USB_DEVICE(HUAWEI_VENDOR_ID, HUAWEI_PRODUCT_ID) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, id_table);
+MODULE_DEVICE_TABLE(usb, id_table);
static struct usb_driver pl2303_driver = {
.name = "pl2303",
@@ -127,65 +129,6 @@ static struct usb_driver pl2303_driver = {
#define UART_OVERRUN_ERROR 0x40
#define UART_CTS 0x80
-/* function prototypes for a PL2303 serial converter */
-static int pl2303_open (struct usb_serial_port *port, struct file *filp);
-static void pl2303_close (struct usb_serial_port *port, struct file *filp);
-static void pl2303_set_termios (struct usb_serial_port *port,
- struct termios *old);
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file,
- unsigned int cmd, unsigned long arg);
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs);
-static int pl2303_write (struct usb_serial_port *port,
- const unsigned char *buf, int count);
-static void pl2303_send (struct usb_serial_port *port);
-static int pl2303_write_room(struct usb_serial_port *port);
-static int pl2303_chars_in_buffer(struct usb_serial_port *port);
-static void pl2303_break_ctl(struct usb_serial_port *port,int break_state);
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file);
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
- unsigned int set, unsigned int clear);
-static int pl2303_startup (struct usb_serial *serial);
-static void pl2303_shutdown (struct usb_serial *serial);
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size);
-static void pl2303_buf_free(struct pl2303_buf *pb);
-static void pl2303_buf_clear(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb);
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
- unsigned int count);
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
- unsigned int count);
-
-
-/* All of the device info needed for the PL2303 SIO serial converter */
-static struct usb_serial_driver pl2303_device = {
- .driver = {
- .owner = THIS_MODULE,
- .name = "pl2303",
- },
- .id_table = id_table,
- .num_interrupt_in = NUM_DONT_CARE,
- .num_bulk_in = 1,
- .num_bulk_out = 1,
- .num_ports = 1,
- .open = pl2303_open,
- .close = pl2303_close,
- .write = pl2303_write,
- .ioctl = pl2303_ioctl,
- .break_ctl = pl2303_break_ctl,
- .set_termios = pl2303_set_termios,
- .tiocmget = pl2303_tiocmget,
- .tiocmset = pl2303_tiocmset,
- .read_bulk_callback = pl2303_read_bulk_callback,
- .read_int_callback = pl2303_read_int_callback,
- .write_bulk_callback = pl2303_write_bulk_callback,
- .write_room = pl2303_write_room,
- .chars_in_buffer = pl2303_chars_in_buffer,
- .attach = pl2303_startup,
- .shutdown = pl2303_shutdown,
-};
enum pl2303_type {
type_0, /* don't know the difference between type 0 and */
@@ -204,8 +147,166 @@ struct pl2303_private {
enum pl2303_type type;
};
+/*
+ * pl2303_buf_alloc
+ *
+ * Allocate a circular buffer and all associated memory.
+ */
+static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
+{
+ struct pl2303_buf *pb;
+
+ if (size == 0)
+ return NULL;
+
+ pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
+ if (pb == NULL)
+ return NULL;
+
+ pb->buf_buf = kmalloc(size, GFP_KERNEL);
+ if (pb->buf_buf == NULL) {
+ kfree(pb);
+ return NULL;
+ }
+
+ pb->buf_size = size;
+ pb->buf_get = pb->buf_put = pb->buf_buf;
-static int pl2303_startup (struct usb_serial *serial)
+ return pb;
+}
+
+/*
+ * pl2303_buf_free
+ *
+ * Free the buffer and all associated memory.
+ */
+static void pl2303_buf_free(struct pl2303_buf *pb)
+{
+ if (pb) {
+ kfree(pb->buf_buf);
+ kfree(pb);
+ }
+}
+
+/*
+ * pl2303_buf_clear
+ *
+ * Clear out all data in the circular buffer.
+ */
+static void pl2303_buf_clear(struct pl2303_buf *pb)
+{
+ if (pb != NULL)
+ pb->buf_get = pb->buf_put;
+ /* equivalent to a get of all data available */
+}
+
+/*
+ * pl2303_buf_data_avail
+ *
+ * Return the number of bytes of data available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_space_avail
+ *
+ * Return the number of bytes of space available in the circular
+ * buffer.
+ */
+static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
+{
+ if (pb == NULL)
+ return 0;
+
+ return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
+}
+
+/*
+ * pl2303_buf_put
+ *
+ * Copy data data from a user buffer and put it into the circular buffer.
+ * Restrict to the amount of space available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
+ unsigned int count)
+{
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_space_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_put;
+ if (count > len) {
+ memcpy(pb->buf_put, buf, len);
+ memcpy(pb->buf_buf, buf+len, count - len);
+ pb->buf_put = pb->buf_buf + count - len;
+ } else {
+ memcpy(pb->buf_put, buf, count);
+ if (count < len)
+ pb->buf_put += count;
+ else /* count == len */
+ pb->buf_put = pb->buf_buf;
+ }
+
+ return count;
+}
+
+/*
+ * pl2303_buf_get
+ *
+ * Get data from the circular buffer and copy to the given buffer.
+ * Restrict to the amount of data available.
+ *
+ * Return the number of bytes copied.
+ */
+static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
+ unsigned int count)
+{
+ unsigned int len;
+
+ if (pb == NULL)
+ return 0;
+
+ len = pl2303_buf_data_avail(pb);
+ if (count > len)
+ count = len;
+
+ if (count == 0)
+ return 0;
+
+ len = pb->buf_buf + pb->buf_size - pb->buf_get;
+ if (count > len) {
+ memcpy(buf, pb->buf_get, len);
+ memcpy(buf+len, pb->buf_buf, count - len);
+ pb->buf_get = pb->buf_buf + count - len;
+ } else {
+ memcpy(buf, pb->buf_get, count);
+ if (count < len)
+ pb->buf_get += count;
+ else /* count == len */
+ pb->buf_get = pb->buf_buf;
+ }
+
+ return count;
+}
+
+static int pl2303_startup(struct usb_serial *serial)
{
struct pl2303_private *priv;
enum pl2303_type type = type_0;
@@ -247,36 +348,17 @@ cleanup:
return -ENOMEM;
}
-static int set_control_lines (struct usb_device *dev, u8 value)
+static int set_control_lines(struct usb_device *dev, u8 value)
{
int retval;
- retval = usb_control_msg (dev, usb_sndctrlpipe (dev, 0),
- SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
- value, 0, NULL, 0, 100);
+ retval = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
+ SET_CONTROL_REQUEST, SET_CONTROL_REQUEST_TYPE,
+ value, 0, NULL, 0, 100);
dbg("%s - value = %d, retval = %d", __FUNCTION__, value, retval);
return retval;
}
-static int pl2303_write (struct usb_serial_port *port, const unsigned char *buf, int count)
-{
- struct pl2303_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
-
- dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
-
- if (!count)
- return count;
-
- spin_lock_irqsave(&priv->lock, flags);
- count = pl2303_buf_put(priv->buf, buf, count);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- pl2303_send(port);
-
- return count;
-}
-
static void pl2303_send(struct usb_serial_port *port)
{
int count, result;
@@ -293,7 +375,7 @@ static void pl2303_send(struct usb_serial_port *port)
}
count = pl2303_buf_get(priv->buf, port->write_urb->transfer_buffer,
- port->bulk_out_size);
+ port->bulk_out_size);
if (count == 0) {
spin_unlock_irqrestore(&priv->lock, flags);
@@ -304,13 +386,15 @@ static void pl2303_send(struct usb_serial_port *port)
spin_unlock_irqrestore(&priv->lock, flags);
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count, port->write_urb->transfer_buffer);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__, count,
+ port->write_urb->transfer_buffer);
port->write_urb->transfer_buffer_length = count;
port->write_urb->dev = port->serial->dev;
- result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result) {
- dev_err(&port->dev, "%s - failed submitting write urb, error %d\n", __FUNCTION__, result);
+ dev_err(&port->dev, "%s - failed submitting write urb,"
+ " error %d\n", __FUNCTION__, result);
priv->write_urb_in_use = 0;
// TODO: reschedule pl2303_send
}
@@ -318,6 +402,26 @@ static void pl2303_send(struct usb_serial_port *port)
usb_serial_port_softint(port);
}
+static int pl2303_write(struct usb_serial_port *port, const unsigned char *buf,
+ int count)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+
+ dbg("%s - port %d, %d bytes", __FUNCTION__, port->number, count);
+
+ if (!count)
+ return count;
+
+ spin_lock_irqsave(&priv->lock, flags);
+ count = pl2303_buf_put(priv->buf, buf, count);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ pl2303_send(port);
+
+ return count;
+}
+
static int pl2303_write_room(struct usb_serial_port *port)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -350,7 +454,8 @@ static int pl2303_chars_in_buffer(struct usb_serial_port *port)
return chars;
}
-static void pl2303_set_termios (struct usb_serial_port *port, struct termios *old_termios)
+static void pl2303_set_termios(struct usb_serial_port *port,
+ struct termios *old_termios)
{
struct usb_serial *serial = port->serial;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -371,7 +476,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
spin_lock_irqsave(&priv->lock, flags);
if (!priv->termios_initialized) {
*(port->tty->termios) = tty_std_termios;
- port->tty->termios->c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+ port->tty->termios->c_cflag = B9600 | CS8 | CREAD |
+ HUPCL | CLOCAL;
priv->termios_initialized = 1;
}
spin_unlock_irqrestore(&priv->lock, flags);
@@ -380,24 +486,24 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
/* check that they really want us to change something */
if (old_termios) {
if ((cflag == old_termios->c_cflag) &&
- (RELEVANT_IFLAG(port->tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) {
- dbg("%s - nothing to change...", __FUNCTION__);
- return;
+ (RELEVANT_IFLAG(port->tty->termios->c_iflag) ==
+ RELEVANT_IFLAG(old_termios->c_iflag))) {
+ dbg("%s - nothing to change...", __FUNCTION__);
+ return;
}
}
- buf = kzalloc (7, GFP_KERNEL);
+ buf = kzalloc(7, GFP_KERNEL);
if (!buf) {
dev_err(&port->dev, "%s - out of memory.\n", __FUNCTION__);
return;
}
-
- i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
- GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
- buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
+ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
+ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if (cflag & CSIZE) {
switch (cflag & CSIZE) {
@@ -429,7 +535,8 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
case B230400: baud = 230400; break;
case B460800: baud = 460800; break;
default:
- dev_err(&port->dev, "pl2303 driver does not support the baudrate requested (fix it)\n");
+ dev_err(&port->dev, "pl2303 driver does not support"
+ " the baudrate requested (fix it)\n");
break;
}
dbg("%s - baud = %d", __FUNCTION__, baud);
@@ -469,10 +576,10 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
dbg("%s - parity = none", __FUNCTION__);
}
- i = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0x21:0x20:0:0 %d", i);
+ i = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ SET_LINE_REQUEST, SET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0x21:0x20:0:0 %d", i);
/* change control lines if we are switching to or from B0 */
spin_lock_irqsave(&priv->lock, flags);
@@ -488,13 +595,13 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
} else {
spin_unlock_irqrestore(&priv->lock, flags);
}
-
+
buf[0] = buf[1] = buf[2] = buf[3] = buf[4] = buf[5] = buf[6] = 0;
- i = usb_control_msg (serial->dev, usb_rcvctrlpipe (serial->dev, 0),
- GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
- 0, 0, buf, 7, 100);
- dbg ("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
+ i = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ GET_LINE_REQUEST, GET_LINE_REQUEST_TYPE,
+ 0, 0, buf, 7, 100);
+ dbg("0xa1:0x21:0:0 %d - %x %x %x %x %x %x %x", i,
buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6]);
if (cflag & CRTSCTS) {
@@ -503,18 +610,82 @@ static void pl2303_set_termios (struct usb_serial_port *port, struct termios *ol
index = 0x61;
else
index = 0x41;
- i = usb_control_msg(serial->dev,
+ i = usb_control_msg(serial->dev,
usb_sndctrlpipe(serial->dev, 0),
VENDOR_WRITE_REQUEST,
VENDOR_WRITE_REQUEST_TYPE,
0x0, index, NULL, 0, 100);
- dbg ("0x40:0x1:0x0:0x%x %d", index, i);
+ dbg("0x40:0x1:0x0:0x%x %d", index, i);
+ }
+
+ kfree(buf);
+}
+
+static void pl2303_close(struct usb_serial_port *port, struct file *filp)
+{
+ struct pl2303_private *priv = usb_get_serial_port_data(port);
+ unsigned long flags;
+ unsigned int c_cflag;
+ int bps;
+ long timeout;
+ wait_queue_t wait;
+
+ dbg("%s - port %d", __FUNCTION__, port->number);
+
+ /* wait for data to drain from the buffer */
+ spin_lock_irqsave(&priv->lock, flags);
+ timeout = PL2303_CLOSING_WAIT;
+ init_waitqueue_entry(&wait, current);
+ add_wait_queue(&port->tty->write_wait, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+ if (pl2303_buf_data_avail(priv->buf) == 0 ||
+ timeout == 0 || signal_pending(current) ||
+ !usb_get_intfdata(port->serial->interface)) /* disconnect */
+ break;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ timeout = schedule_timeout(timeout);
+ spin_lock_irqsave(&priv->lock, flags);
}
+ set_current_state(TASK_RUNNING);
+ remove_wait_queue(&port->tty->write_wait, &wait);
+ /* clear out any remaining data in the buffer */
+ pl2303_buf_clear(priv->buf);
+ spin_unlock_irqrestore(&priv->lock, flags);
+
+ /* wait for characters to drain from the device */
+ /* (this is long enough for the entire 256 byte */
+ /* pl2303 hardware buffer to drain with no flow */
+ /* control for data rates of 1200 bps or more, */
+ /* for lower rates we should really know how much */
+ /* data is in the buffer to compute a delay */
+ /* that is not unnecessarily long) */
+ bps = tty_get_baud_rate(port->tty);
+ if (bps > 1200)
+ timeout = max((HZ*2560)/bps,HZ/10);
+ else
+ timeout = 2*HZ;
+ schedule_timeout_interruptible(timeout);
- kfree (buf);
+ /* shutdown our urbs */
+ dbg("%s - shutting down urbs", __FUNCTION__);
+ usb_kill_urb(port->write_urb);
+ usb_kill_urb(port->read_urb);
+ usb_kill_urb(port->interrupt_in_urb);
+
+ if (port->tty) {
+ c_cflag = port->tty->termios->c_cflag;
+ if (c_cflag & HUPCL) {
+ /* drop DTR and RTS */
+ spin_lock_irqsave(&priv->lock, flags);
+ priv->line_control = 0;
+ spin_unlock_irqrestore(&priv->lock, flags);
+ set_control_lines(port->serial->dev, 0);
+ }
+ }
}
-static int pl2303_open (struct usb_serial_port *port, struct file *filp)
+static int pl2303_open(struct usb_serial_port *port, struct file *filp)
{
struct termios tmp_termios;
struct usb_serial *serial = port->serial;
@@ -568,98 +739,35 @@ static int pl2303_open (struct usb_serial_port *port, struct file *filp)
/* Setup termios */
if (port->tty) {
- pl2303_set_termios (port, &tmp_termios);
+ pl2303_set_termios(port, &tmp_termios);
}
//FIXME: need to assert RTS and DTR if CRTSCTS off
dbg("%s - submitting read urb", __FUNCTION__);
port->read_urb->dev = serial->dev;
- result = usb_submit_urb (port->read_urb, GFP_KERNEL);
+ result = usb_submit_urb(port->read_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting read urb, error %d\n", __FUNCTION__, result);
- pl2303_close (port, NULL);
+ dev_err(&port->dev, "%s - failed submitting read urb,"
+ " error %d\n", __FUNCTION__, result);
+ pl2303_close(port, NULL);
return -EPROTO;
}
dbg("%s - submitting interrupt urb", __FUNCTION__);
port->interrupt_in_urb->dev = serial->dev;
- result = usb_submit_urb (port->interrupt_in_urb, GFP_KERNEL);
+ result = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
if (result) {
- dev_err(&port->dev, "%s - failed submitting interrupt urb, error %d\n", __FUNCTION__, result);
- pl2303_close (port, NULL);
+ dev_err(&port->dev, "%s - failed submitting interrupt urb,"
+ " error %d\n", __FUNCTION__, result);
+ pl2303_close(port, NULL);
return -EPROTO;
}
return 0;
}
-
-static void pl2303_close (struct usb_serial_port *port, struct file *filp)
-{
- struct pl2303_private *priv = usb_get_serial_port_data(port);
- unsigned long flags;
- unsigned int c_cflag;
- int bps;
- long timeout;
- wait_queue_t wait;
-
- dbg("%s - port %d", __FUNCTION__, port->number);
-
- /* wait for data to drain from the buffer */
- spin_lock_irqsave(&priv->lock, flags);
- timeout = PL2303_CLOSING_WAIT;
- init_waitqueue_entry(&wait, current);
- add_wait_queue(&port->tty->write_wait, &wait);
- for (;;) {
- set_current_state(TASK_INTERRUPTIBLE);
- if (pl2303_buf_data_avail(priv->buf) == 0
- || timeout == 0 || signal_pending(current)
- || !usb_get_intfdata(port->serial->interface)) /* disconnect */
- break;
- spin_unlock_irqrestore(&priv->lock, flags);
- timeout = schedule_timeout(timeout);
- spin_lock_irqsave(&priv->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->tty->write_wait, &wait);
- /* clear out any remaining data in the buffer */
- pl2303_buf_clear(priv->buf);
- spin_unlock_irqrestore(&priv->lock, flags);
-
- /* wait for characters to drain from the device */
- /* (this is long enough for the entire 256 byte */
- /* pl2303 hardware buffer to drain with no flow */
- /* control for data rates of 1200 bps or more, */
- /* for lower rates we should really know how much */
- /* data is in the buffer to compute a delay */
- /* that is not unnecessarily long) */
- bps = tty_get_baud_rate(port->tty);
- if (bps > 1200)
- timeout = max((HZ*2560)/bps,HZ/10);
- else
- timeout = 2*HZ;
- schedule_timeout_interruptible(timeout);
-
- /* shutdown our urbs */
- dbg("%s - shutting down urbs", __FUNCTION__);
- usb_kill_urb(port->write_urb);
- usb_kill_urb(port->read_urb);
- usb_kill_urb(port->interrupt_in_urb);
-
- if (port->tty) {
- c_cflag = port->tty->termios->c_cflag;
- if (c_cflag & HUPCL) {
- /* drop DTR and RTS */
- spin_lock_irqsave(&priv->lock, flags);
- priv->line_control = 0;
- spin_unlock_irqrestore (&priv->lock, flags);
- set_control_lines (port->serial->dev, 0);
- }
- }
-}
-
-static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
- unsigned int set, unsigned int clear)
+static int pl2303_tiocmset(struct usb_serial_port *port, struct file *file,
+ unsigned int set, unsigned int clear)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
@@ -668,7 +776,7 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
if (!usb_get_intfdata(port->serial->interface))
return -ENODEV;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
if (set & TIOCM_RTS)
priv->line_control |= CONTROL_RTS;
if (set & TIOCM_DTR)
@@ -678,12 +786,12 @@ static int pl2303_tiocmset (struct usb_serial_port *port, struct file *file,
if (clear & TIOCM_DTR)
priv->line_control &= ~CONTROL_DTR;
control = priv->line_control;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
- return set_control_lines (port->serial->dev, control);
+ return set_control_lines(port->serial->dev, control);
}
-static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
+static int pl2303_tiocmget(struct usb_serial_port *port, struct file *file)
{
struct pl2303_private *priv = usb_get_serial_port_data(port);
unsigned long flags;
@@ -696,10 +804,10 @@ static int pl2303_tiocmget (struct usb_serial_port *port, struct file *file)
if (!usb_get_intfdata(port->serial->interface))
return -ENODEV;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
mcr = priv->line_control;
status = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
result = ((mcr & CONTROL_DTR) ? TIOCM_DTR : 0)
| ((mcr & CONTROL_RTS) ? TIOCM_RTS : 0)
@@ -721,22 +829,22 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
unsigned int status;
unsigned int changed;
- spin_lock_irqsave (&priv->lock, flags);
+ spin_lock_irqsave(&priv->lock, flags);
prevstatus = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
+ spin_unlock_irqrestore(&priv->lock, flags);
while (1) {
interruptible_sleep_on(&priv->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
-
- spin_lock_irqsave (&priv->lock, flags);
+
+ spin_lock_irqsave(&priv->lock, flags);
status = priv->line_status;
- spin_unlock_irqrestore (&priv->lock, flags);
-
+ spin_unlock_irqrestore(&priv->lock, flags);
+
changed=prevstatus^status;
-
+
if (((arg & TIOCM_RNG) && (changed & UART_RING)) ||
((arg & TIOCM_DSR) && (changed & UART_DSR)) ||
((arg & TIOCM_CD) && (changed & UART_DCD)) ||
@@ -749,7 +857,8 @@ static int wait_modem_info(struct usb_serial_port *port, unsigned int arg)
return 0;
}
-static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsigned int cmd, unsigned long arg)
+static int pl2303_ioctl(struct usb_serial_port *port, struct file *file,
+ unsigned int cmd, unsigned long arg)
{
dbg("%s (%d) cmd = 0x%04x", __FUNCTION__, port->number, cmd);
@@ -766,7 +875,7 @@ static int pl2303_ioctl (struct usb_serial_port *port, struct file *file, unsign
return -ENOIOCTLCMD;
}
-static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
+static void pl2303_break_ctl(struct usb_serial_port *port, int break_state)
{
struct usb_serial *serial = port->serial;
u16 state;
@@ -780,15 +889,14 @@ static void pl2303_break_ctl (struct usb_serial_port *port, int break_state)
state = BREAK_ON;
dbg("%s - turning break %s", __FUNCTION__, state==BREAK_OFF ? "off" : "on");
- result = usb_control_msg (serial->dev, usb_sndctrlpipe (serial->dev, 0),
- BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
- 0, NULL, 0, 100);
+ result = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ BREAK_REQUEST, BREAK_REQUEST_TYPE, state,
+ 0, NULL, 0, 100);
if (result)
dbg("%s - error sending break = %d", __FUNCTION__, result);
}
-
-static void pl2303_shutdown (struct usb_serial *serial)
+static void pl2303_shutdown(struct usb_serial *serial)
{
int i;
struct pl2303_private *priv;
@@ -802,7 +910,7 @@ static void pl2303_shutdown (struct usb_serial *serial)
kfree(priv);
usb_set_serial_port_data(serial->port[i], NULL);
}
- }
+ }
}
static void pl2303_update_line_status(struct usb_serial_port *port,
@@ -814,29 +922,33 @@ static void pl2303_update_line_status(struct usb_serial_port *port,
unsigned long flags;
u8 status_idx = UART_STATE;
u8 length = UART_STATE + 1;
+ u16 idv, idp;
+
+ idv = le16_to_cpu(port->serial->dev->descriptor.idVendor);
+ idp = le16_to_cpu(port->serial->dev->descriptor.idProduct);
- if ((le16_to_cpu(port->serial->dev->descriptor.idVendor) == SIEMENS_VENDOR_ID) &&
- (le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X65 ||
- le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_SX1 ||
- le16_to_cpu(port->serial->dev->descriptor.idProduct) == SIEMENS_PRODUCT_ID_X75)) {
- length = 1;
- status_idx = 0;
+
+ if (idv == SIEMENS_VENDOR_ID) {
+ if (idp == SIEMENS_PRODUCT_ID_X65 ||
+ idp == SIEMENS_PRODUCT_ID_SX1 ||
+ idp == SIEMENS_PRODUCT_ID_X75) {
+
+ length = 1;
+ status_idx = 0;
+ }
}
if (actual_length < length)
- goto exit;
+ return;
/* Save off the uart status for others to look at */
spin_lock_irqsave(&priv->lock, flags);
priv->line_status = data[status_idx];
spin_unlock_irqrestore(&priv->lock, flags);
- wake_up_interruptible (&priv->delta_msr_wait);
-
-exit:
- return;
+ wake_up_interruptible(&priv->delta_msr_wait);
}
-static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_int_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
unsigned char *data = urb->transfer_buffer;
@@ -853,25 +965,29 @@ static void pl2303_read_int_callback (struct urb *urb, struct pt_regs *regs)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
return;
default:
- dbg("%s - nonzero urb status received: %d", __FUNCTION__, urb->status);
+ dbg("%s - nonzero urb status received: %d", __FUNCTION__,
+ urb->status);
goto exit;
}
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, urb->transfer_buffer);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length, urb->transfer_buffer);
+
pl2303_update_line_status(port, data, actual_length);
exit:
- status = usb_submit_urb (urb, GFP_ATOMIC);
+ status = usb_submit_urb(urb, GFP_ATOMIC);
if (status)
- dev_err(&urb->dev->dev, "%s - usb_submit_urb failed with result %d\n",
+ dev_err(&urb->dev->dev,
+ "%s - usb_submit_urb failed with result %d\n",
__FUNCTION__, status);
}
-
-static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_read_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -892,20 +1008,25 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
return;
}
if (urb->status == -EPROTO) {
- /* PL2303 mysteriously fails with -EPROTO reschedule the read */
- dbg("%s - caught -EPROTO, resubmitting the urb", __FUNCTION__);
+ /* PL2303 mysteriously fails with -EPROTO reschedule
+ * the read */
+ dbg("%s - caught -EPROTO, resubmitting the urb",
+ __FUNCTION__);
urb->status = 0;
urb->dev = port->serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed"
+ " resubmitting read urb, error %d\n",
+ __FUNCTION__, result);
return;
}
dbg("%s - unable to handle the error, exiting.", __FUNCTION__);
return;
}
- usb_serial_debug_data(debug, &port->dev, __FUNCTION__, urb->actual_length, data);
+ usb_serial_debug_data(debug, &port->dev, __FUNCTION__,
+ urb->actual_length, data);
/* get tty_flag from status */
tty_flag = TTY_NORMAL;
@@ -914,7 +1035,7 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
status = priv->line_status;
priv->line_status &= ~UART_STATE_TRANSIENT_MASK;
spin_unlock_irqrestore(&priv->lock, flags);
- wake_up_interruptible (&priv->delta_msr_wait);
+ wake_up_interruptible(&priv->delta_msr_wait);
/* break takes precedence over parity, */
/* which takes precedence over framing errors */
@@ -933,8 +1054,8 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
if (status & UART_OVERRUN_ERROR)
tty_insert_flip_char(tty, 0, TTY_OVERRUN);
for (i = 0; i < urb->actual_length; ++i)
- tty_insert_flip_char (tty, data[i], tty_flag);
- tty_flip_buffer_push (tty);
+ tty_insert_flip_char(tty, data[i], tty_flag);
+ tty_flip_buffer_push(tty);
}
/* Schedule the next read _if_ we are still open */
@@ -942,15 +1063,14 @@ static void pl2303_read_bulk_callback (struct urb *urb, struct pt_regs *regs)
urb->dev = port->serial->dev;
result = usb_submit_urb(urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting read urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed resubmitting"
+ " read urb, error %d\n", __FUNCTION__, result);
}
return;
}
-
-
-static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
+static void pl2303_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
{
struct usb_serial_port *port = (struct usb_serial_port *) urb->context;
struct pl2303_private *priv = usb_get_serial_port_data(port);
@@ -966,18 +1086,21 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
case -ENOENT:
case -ESHUTDOWN:
/* this urb is terminated, clean up */
- dbg("%s - urb shutting down with status: %d", __FUNCTION__, urb->status);
+ dbg("%s - urb shutting down with status: %d", __FUNCTION__,
+ urb->status);
priv->write_urb_in_use = 0;
return;
default:
/* error in the urb, so we have to resubmit it */
dbg("%s - Overflow in write", __FUNCTION__);
- dbg("%s - nonzero write bulk status received: %d", __FUNCTION__, urb->status);
+ dbg("%s - nonzero write bulk status received: %d", __FUNCTION__,
+ urb->status);
port->write_urb->transfer_buffer_length = 1;
port->write_urb->dev = port->serial->dev;
- result = usb_submit_urb (port->write_urb, GFP_ATOMIC);
+ result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
if (result)
- dev_err(&urb->dev->dev, "%s - failed resubmitting write urb, error %d\n", __FUNCTION__, result);
+ dev_err(&urb->dev->dev, "%s - failed resubmitting write"
+ " urb, error %d\n", __FUNCTION__, result);
else
return;
}
@@ -988,191 +1111,38 @@ static void pl2303_write_bulk_callback (struct urb *urb, struct pt_regs *regs)
pl2303_send(port);
}
+/* All of the device info needed for the PL2303 SIO serial converter */
+static struct usb_serial_driver pl2303_device = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "pl2303",
+ },
+ .id_table = id_table,
+ .num_interrupt_in = NUM_DONT_CARE,
+ .num_bulk_in = 1,
+ .num_bulk_out = 1,
+ .num_ports = 1,
+ .open = pl2303_open,
+ .close = pl2303_close,
+ .write = pl2303_write,
+ .ioctl = pl2303_ioctl,
+ .break_ctl = pl2303_break_ctl,
+ .set_termios = pl2303_set_termios,
+ .tiocmget = pl2303_tiocmget,
+ .tiocmset = pl2303_tiocmset,
+ .read_bulk_callback = pl2303_read_bulk_callback,
+ .read_int_callback = pl2303_read_int_callback,
+ .write_bulk_callback = pl2303_write_bulk_callback,
+ .write_room = pl2303_write_room,
+ .chars_in_buffer = pl2303_chars_in_buffer,
+ .attach = pl2303_startup,
+ .shutdown = pl2303_shutdown,
+};
-/*
- * pl2303_buf_alloc
- *
- * Allocate a circular buffer and all associated memory.
- */
-
-static struct pl2303_buf *pl2303_buf_alloc(unsigned int size)
-{
-
- struct pl2303_buf *pb;
-
-
- if (size == 0)
- return NULL;
-
- pb = (struct pl2303_buf *)kmalloc(sizeof(struct pl2303_buf), GFP_KERNEL);
- if (pb == NULL)
- return NULL;
-
- pb->buf_buf = kmalloc(size, GFP_KERNEL);
- if (pb->buf_buf == NULL) {
- kfree(pb);
- return NULL;
- }
-
- pb->buf_size = size;
- pb->buf_get = pb->buf_put = pb->buf_buf;
-
- return pb;
-
-}
-
-
-/*
- * pl2303_buf_free
- *
- * Free the buffer and all associated memory.
- */
-
-static void pl2303_buf_free(struct pl2303_buf *pb)
-{
- if (pb) {
- kfree(pb->buf_buf);
- kfree(pb);
- }
-}
-
-
-/*
- * pl2303_buf_clear
- *
- * Clear out all data in the circular buffer.
- */
-
-static void pl2303_buf_clear(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- pb->buf_get = pb->buf_put;
- /* equivalent to a get of all data available */
-}
-
-
-/*
- * pl2303_buf_data_avail
- *
- * Return the number of bytes of data available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_data_avail(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- return ((pb->buf_size + pb->buf_put - pb->buf_get) % pb->buf_size);
- else
- return 0;
-}
-
-
-/*
- * pl2303_buf_space_avail
- *
- * Return the number of bytes of space available in the circular
- * buffer.
- */
-
-static unsigned int pl2303_buf_space_avail(struct pl2303_buf *pb)
-{
- if (pb != NULL)
- return ((pb->buf_size + pb->buf_get - pb->buf_put - 1) % pb->buf_size);
- else
- return 0;
-}
-
-
-/*
- * pl2303_buf_put
- *
- * Copy data data from a user buffer and put it into the circular buffer.
- * Restrict to the amount of space available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_put(struct pl2303_buf *pb, const char *buf,
- unsigned int count)
-{
-
- unsigned int len;
-
-
- if (pb == NULL)
- return 0;
-
- len = pl2303_buf_space_avail(pb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = pb->buf_buf + pb->buf_size - pb->buf_put;
- if (count > len) {
- memcpy(pb->buf_put, buf, len);
- memcpy(pb->buf_buf, buf+len, count - len);
- pb->buf_put = pb->buf_buf + count - len;
- } else {
- memcpy(pb->buf_put, buf, count);
- if (count < len)
- pb->buf_put += count;
- else /* count == len */
- pb->buf_put = pb->buf_buf;
- }
-
- return count;
-
-}
-
-
-/*
- * pl2303_buf_get
- *
- * Get data from the circular buffer and copy to the given buffer.
- * Restrict to the amount of data available.
- *
- * Return the number of bytes copied.
- */
-
-static unsigned int pl2303_buf_get(struct pl2303_buf *pb, char *buf,
- unsigned int count)
-{
-
- unsigned int len;
-
-
- if (pb == NULL)
- return 0;
-
- len = pl2303_buf_data_avail(pb);
- if (count > len)
- count = len;
-
- if (count == 0)
- return 0;
-
- len = pb->buf_buf + pb->buf_size - pb->buf_get;
- if (count > len) {
- memcpy(buf, pb->buf_get, len);
- memcpy(buf+len, pb->buf_buf, count - len);
- pb->buf_get = pb->buf_buf + count - len;
- } else {
- memcpy(buf, pb->buf_get, count);
- if (count < len)
- pb->buf_get += count;
- else /* count == len */
- pb->buf_get = pb->buf_buf;
- }
-
- return count;
-
-}
-
-static int __init pl2303_init (void)
+static int __init pl2303_init(void)
{
int retval;
+
retval = usb_serial_register(&pl2303_device);
if (retval)
goto failed_usb_serial_register;
@@ -1187,14 +1157,12 @@ failed_usb_serial_register:
return retval;
}
-
-static void __exit pl2303_exit (void)
+static void __exit pl2303_exit(void)
{
- usb_deregister (&pl2303_driver);
- usb_serial_deregister (&pl2303_device);
+ usb_deregister(&pl2303_driver);
+ usb_serial_deregister(&pl2303_device);
}
-
module_init(pl2303_init);
module_exit(pl2303_exit);
diff --git a/drivers/usb/serial/pl2303.h b/drivers/usb/serial/pl2303.h
index 55195e76eb6..65a5039665e 100644
--- a/drivers/usb/serial/pl2303.h
+++ b/drivers/usb/serial/pl2303.h
@@ -89,3 +89,11 @@
/* Belkin "F5U257" Serial Adapter */
#define BELKIN_VENDOR_ID 0x050d
#define BELKIN_PRODUCT_ID 0x0257
+
+/* Alcor Micro Corp. USB 2.0 TO RS-232 */
+#define ALCOR_VENDOR_ID 0x058F
+#define ALCOR_PRODUCT_ID 0x9720
+
+/* Huawei E620 UMTS/HSDPA card (ID: 12d1:1001) */
+#define HUAWEI_VENDOR_ID 0x12d1
+#define HUAWEI_PRODUCT_ID 0x1001
diff --git a/drivers/usb/serial/safe_serial.c b/drivers/usb/serial/safe_serial.c
index 789771ecdb1..1e07dfad685 100644
--- a/drivers/usb/serial/safe_serial.c
+++ b/drivers/usb/serial/safe_serial.c
@@ -298,14 +298,14 @@ static int safe_write (struct usb_serial_port *port, const unsigned char *buf, i
dbg ("%s - write request of 0 bytes", __FUNCTION__);
return (0);
}
- spin_lock(&port->lock);
+ spin_lock_bh(&port->lock);
if (port->write_urb_busy) {
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
dbg("%s - already writing", __FUNCTION__);
return 0;
}
port->write_urb_busy = 1;
- spin_unlock(&port->lock);
+ spin_unlock_bh(&port->lock);
packet_length = port->bulk_out_size; // get max packetsize
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
index e06a41bd0f3..8006e51c34b 100644
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -676,33 +676,29 @@ int usb_serial_probe(struct usb_interface *interface,
iface_desc = interface->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+
+ if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
dbg("found bulk in on endpoint %d", i);
bulk_in_endpoint[num_bulk_in] = endpoint;
++num_bulk_in;
}
- if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
- ((endpoint->bmAttributes & 3) == 0x02)) {
+ if (usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dbg("found bulk out on endpoint %d", i);
bulk_out_endpoint[num_bulk_out] = endpoint;
++num_bulk_out;
}
-
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in on endpoint %d", i);
interrupt_in_endpoint[num_interrupt_in] = endpoint;
++num_interrupt_in;
}
- if (((endpoint->bEndpointAddress & 0x80) == 0x00) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+ if (usb_endpoint_is_int_out(endpoint)) {
/* we found an interrupt out endpoint */
dbg("found interrupt out on endpoint %d", i);
interrupt_out_endpoint[num_interrupt_out] = endpoint;
@@ -716,14 +712,15 @@ int usb_serial_probe(struct usb_interface *interface,
if (((le16_to_cpu(dev->descriptor.idVendor) == PL2303_VENDOR_ID) &&
(le16_to_cpu(dev->descriptor.idProduct) == PL2303_PRODUCT_ID)) ||
((le16_to_cpu(dev->descriptor.idVendor) == ATEN_VENDOR_ID) &&
- (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID))) {
+ (le16_to_cpu(dev->descriptor.idProduct) == ATEN_PRODUCT_ID)) ||
+ ((le16_to_cpu(dev->descriptor.idVendor) == ALCOR_VENDOR_ID) &&
+ (le16_to_cpu(dev->descriptor.idProduct) == ALCOR_PRODUCT_ID))) {
if (interface != dev->actconfig->interface[0]) {
/* check out the endpoints of the other interface*/
iface_desc = dev->actconfig->interface[0]->cur_altsetting;
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
- if ((endpoint->bEndpointAddress & 0x80) &&
- ((endpoint->bmAttributes & 3) == 0x03)) {
+ if (usb_endpoint_is_int_in(endpoint)) {
/* we found a interrupt in endpoint */
dbg("found interrupt in for Prolific device on separate interface");
interrupt_in_endpoint[num_interrupt_in] = endpoint;
@@ -937,7 +934,10 @@ int usb_serial_probe(struct usb_interface *interface,
snprintf (&port->dev.bus_id[0], sizeof(port->dev.bus_id), "ttyUSB%d", port->number);
dbg ("%s - registering %s", __FUNCTION__, port->dev.bus_id);
- device_register (&port->dev);
+ retval = device_register(&port->dev);
+ if (retval)
+ dev_err(&port->dev, "Error registering port device, "
+ "continuing\n");
}
usb_serial_console_init (debug, minor);
@@ -1015,7 +1015,7 @@ void usb_serial_disconnect(struct usb_interface *interface)
dev_info(dev, "device disconnected\n");
}
-static struct tty_operations serial_ops = {
+static const struct tty_operations serial_ops = {
.open = serial_open,
.close = serial_close,
.write = serial_write,
diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig
index be9eec22574..422a4b288e3 100644
--- a/drivers/usb/storage/Kconfig
+++ b/drivers/usb/storage/Kconfig
@@ -8,8 +8,7 @@ comment "may also be needed; see USB_STORAGE Help for more information"
config USB_STORAGE
tristate "USB Mass Storage support"
- depends on USB
- select SCSI
+ depends on USB && SCSI
---help---
Say Y here if you want to connect USB mass storage devices to your
computer's USB port. This is the driver you need for USB
@@ -18,7 +17,7 @@ config USB_STORAGE
similar devices. This driver may also be used for some cameras
and card readers.
- This option 'selects' (turns on, enables) 'SCSI', but you
+ This option depends on 'SCSI' support being enabled, but you
probably also need 'SCSI device support: SCSI disk support'
(BLK_DEV_SD) for most USB storage devices.
@@ -135,6 +134,18 @@ config USB_STORAGE_ONETOUCH
this input in any keybinding software. (e.g. gnome's keyboard short-
cuts)
+config USB_STORAGE_KARMA
+ bool "Support for Rio Karma music player"
+ depends on USB_STORAGE
+ help
+ Say Y here to include additional code to support the Rio Karma
+ USB interface.
+
+ This code places the Rio Karma into mass storage mode, enabling
+ it to be mounted as an ordinary filesystem. Performing an eject
+ on the resulting scsi device node returns the Karma to normal
+ operation.
+
config USB_LIBUSUAL
bool "The shared table of common (or usual) storage devices"
depends on USB
diff --git a/drivers/usb/storage/Makefile b/drivers/usb/storage/Makefile
index 8cbba22508a..023969b4385 100644
--- a/drivers/usb/storage/Makefile
+++ b/drivers/usb/storage/Makefile
@@ -20,6 +20,7 @@ usb-storage-obj-$(CONFIG_USB_STORAGE_DATAFAB) += datafab.o
usb-storage-obj-$(CONFIG_USB_STORAGE_JUMPSHOT) += jumpshot.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ALAUDA) += alauda.o
usb-storage-obj-$(CONFIG_USB_STORAGE_ONETOUCH) += onetouch.o
+usb-storage-obj-$(CONFIG_USB_STORAGE_KARMA) += karma.o
usb-storage-objs := scsiglue.o protocol.o transport.o usb.o \
initializers.o $(usb-storage-obj-y)
diff --git a/drivers/usb/storage/initializers.c b/drivers/usb/storage/initializers.c
index ab173b30076..5b06f9240d0 100644
--- a/drivers/usb/storage/initializers.c
+++ b/drivers/usb/storage/initializers.c
@@ -45,12 +45,6 @@
#include "debug.h"
#include "transport.h"
-#define RIO_MSC 0x08
-#define RIOP_INIT "RIOP\x00\x01\x08"
-#define RIOP_INIT_LEN 7
-#define RIO_SEND_LEN 40
-#define RIO_RECV_LEN 0x200
-
/* This places the Shuttle/SCM USB<->SCSI bridge devices in multi-target
* mode */
int usb_stor_euscsi_init(struct us_data *us)
@@ -97,70 +91,3 @@ int usb_stor_ucr61s2b_init(struct us_data *us)
return (res ? -1 : 0);
}
-
-/* Place the Rio Karma into mass storage mode.
- *
- * The initialization begins by sending 40 bytes starting
- * RIOP\x00\x01\x08\x00, which the device will ack with a 512-byte
- * packet with the high four bits set and everything else null.
- *
- * Next, we send RIOP\x80\x00\x08\x00. Each time, a 512 byte response
- * must be read, but we must loop until byte 5 in the response is 0x08,
- * indicating success. */
-int rio_karma_init(struct us_data *us)
-{
- int result, partial;
- char *recv;
- unsigned long timeout;
-
- // us->iobuf is big enough to hold cmd but not receive
- if (!(recv = kmalloc(RIO_RECV_LEN, GFP_KERNEL)))
- goto die_nomem;
-
- US_DEBUGP("Initializing Karma...\n");
-
- memset(us->iobuf, 0, RIO_SEND_LEN);
- memcpy(us->iobuf, RIOP_INIT, RIOP_INIT_LEN);
-
- result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
- us->iobuf, RIO_SEND_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
- recv, RIO_RECV_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- us->iobuf[4] = 0x80;
- us->iobuf[5] = 0;
- timeout = jiffies + msecs_to_jiffies(3000);
- for (;;) {
- US_DEBUGP("Sending init command\n");
- result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
- us->iobuf, RIO_SEND_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
- recv, RIO_RECV_LEN, &partial);
- if (result != USB_STOR_XFER_GOOD)
- goto die;
-
- if (recv[5] == RIO_MSC)
- break;
- if (time_after(jiffies, timeout))
- goto die;
- msleep(10);
- }
- US_DEBUGP("Karma initialized.\n");
- kfree(recv);
- return 0;
-
-die:
- kfree(recv);
-die_nomem:
- US_DEBUGP("Could not initialize karma.\n");
- return USB_STOR_TRANSPORT_FAILED;
-}
-
diff --git a/drivers/usb/storage/initializers.h b/drivers/usb/storage/initializers.h
index 927f7781080..e2967a4d48a 100644
--- a/drivers/usb/storage/initializers.h
+++ b/drivers/usb/storage/initializers.h
@@ -47,4 +47,3 @@ int usb_stor_euscsi_init(struct us_data *us);
/* This function is required to activate all four slots on the UCR-61S2B
* flash reader */
int usb_stor_ucr61s2b_init(struct us_data *us);
-int rio_karma_init(struct us_data *us);
diff --git a/drivers/usb/storage/karma.c b/drivers/usb/storage/karma.c
new file mode 100644
index 00000000000..0d79ae5683f
--- /dev/null
+++ b/drivers/usb/storage/karma.c
@@ -0,0 +1,155 @@
+/* Driver for Rio Karma
+ *
+ * (c) 2006 Bob Copeland <me@bobcopeland.com>
+ * (c) 2006 Keith Bennett <keith@mcs.st-and.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_device.h>
+
+#include "usb.h"
+#include "transport.h"
+#include "debug.h"
+#include "karma.h"
+
+#define RIO_PREFIX "RIOP\x00"
+#define RIO_PREFIX_LEN 5
+#define RIO_SEND_LEN 40
+#define RIO_RECV_LEN 0x200
+
+#define RIO_ENTER_STORAGE 0x1
+#define RIO_LEAVE_STORAGE 0x2
+#define RIO_RESET 0xC
+
+extern int usb_stor_Bulk_transport(struct scsi_cmnd *, struct us_data *);
+
+struct karma_data {
+ int in_storage;
+ char *recv;
+};
+
+/*
+ * Send commands to Rio Karma.
+ *
+ * For each command we send 40 bytes starting 'RIOP\0' followed by
+ * the command number and a sequence number, which the device will ack
+ * with a 512-byte packet with the high four bits set and everything
+ * else null. Then we send 'RIOP\x80' followed by a zero and the
+ * sequence number, until byte 5 in the response repeats the sequence
+ * number.
+ */
+static int rio_karma_send_command(char cmd, struct us_data *us)
+{
+ int result, partial;
+ unsigned long timeout;
+ static unsigned char seq = 1;
+ struct karma_data *data = (struct karma_data *) us->extra;
+
+ US_DEBUGP("karma: sending command %04x\n", cmd);
+ memset(us->iobuf, 0, RIO_SEND_LEN);
+ memcpy(us->iobuf, RIO_PREFIX, RIO_PREFIX_LEN);
+ us->iobuf[5] = cmd;
+ us->iobuf[6] = seq;
+
+ timeout = jiffies + msecs_to_jiffies(6000);
+ for (;;) {
+ result = usb_stor_bulk_transfer_buf(us, us->send_bulk_pipe,
+ us->iobuf, RIO_SEND_LEN, &partial);
+ if (result != USB_STOR_XFER_GOOD)
+ goto err;
+
+ result = usb_stor_bulk_transfer_buf(us, us->recv_bulk_pipe,
+ data->recv, RIO_RECV_LEN, &partial);
+ if (result != USB_STOR_XFER_GOOD)
+ goto err;
+
+ if (data->recv[5] == seq)
+ break;
+
+ if (time_after(jiffies, timeout))
+ goto err;
+
+ us->iobuf[4] = 0x80;
+ us->iobuf[5] = 0;
+ msleep(50);
+ }
+
+ seq++;
+ if (seq == 0)
+ seq = 1;
+
+ US_DEBUGP("karma: sent command %04x\n", cmd);
+ return 0;
+err:
+ US_DEBUGP("karma: command %04x failed\n", cmd);
+ return USB_STOR_TRANSPORT_FAILED;
+}
+
+/*
+ * Trap START_STOP and READ_10 to leave/re-enter storage mode.
+ * Everything else is propagated to the normal bulk layer.
+ */
+int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us)
+{
+ int ret;
+ struct karma_data *data = (struct karma_data *) us->extra;
+
+ if (srb->cmnd[0] == READ_10 && !data->in_storage) {
+ ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+ if (ret)
+ return ret;
+
+ data->in_storage = 1;
+ return usb_stor_Bulk_transport(srb, us);
+ } else if (srb->cmnd[0] == START_STOP) {
+ ret = rio_karma_send_command(RIO_LEAVE_STORAGE, us);
+ if (ret)
+ return ret;
+
+ data->in_storage = 0;
+ return rio_karma_send_command(RIO_RESET, us);
+ }
+ return usb_stor_Bulk_transport(srb, us);
+}
+
+static void rio_karma_destructor(void *extra)
+{
+ struct karma_data *data = (struct karma_data *) extra;
+ kfree(data->recv);
+}
+
+int rio_karma_init(struct us_data *us)
+{
+ int ret = 0;
+ struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
+ if (!data)
+ goto out;
+
+ data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
+ if (!data->recv) {
+ kfree(data);
+ goto out;
+ }
+
+ us->extra = data;
+ us->extra_destructor = rio_karma_destructor;
+ ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
+ data->in_storage = (ret == 0);
+out:
+ return ret;
+}
diff --git a/drivers/usb/storage/karma.h b/drivers/usb/storage/karma.h
new file mode 100644
index 00000000000..8a60972af8c
--- /dev/null
+++ b/drivers/usb/storage/karma.h
@@ -0,0 +1,7 @@
+#ifndef _KARMA_USB_H
+#define _KARMA_USB_H
+
+extern int rio_karma_init(struct us_data *us);
+extern int rio_karma_transport(struct scsi_cmnd *srb, struct us_data *us);
+
+#endif
diff --git a/drivers/usb/storage/libusual.c b/drivers/usb/storage/libusual.c
index b1ec4a71854..599ad10a761 100644
--- a/drivers/usb/storage/libusual.c
+++ b/drivers/usb/storage/libusual.c
@@ -8,6 +8,7 @@
#include <linux/usb.h>
#include <linux/usb_usual.h>
#include <linux/vmalloc.h>
+#include <linux/kthread.h>
/*
*/
@@ -117,7 +118,7 @@ static int usu_probe(struct usb_interface *intf,
const struct usb_device_id *id)
{
unsigned long type;
- int rc;
+ struct task_struct* task;
unsigned long flags;
type = USB_US_TYPE(id->driver_info);
@@ -132,8 +133,9 @@ static int usu_probe(struct usb_interface *intf,
stat[type].fls |= USU_MOD_FL_THREAD;
spin_unlock_irqrestore(&usu_lock, flags);
- rc = kernel_thread(usu_probe_thread, (void*)type, CLONE_VM);
- if (rc < 0) {
+ task = kthread_run(usu_probe_thread, (void*)type, "libusual_%d", type);
+ if (IS_ERR(task)) {
+ int rc = PTR_ERR(task);
printk(KERN_WARNING "libusual: "
"Unable to start the thread for %s: %d\n",
bias_names[type], rc);
@@ -175,8 +177,6 @@ static int usu_probe_thread(void *arg)
int rc;
unsigned long flags;
- daemonize("libusual_%d", type); /* "usb-storage" is kinda too long */
-
/* A completion does not work here because it's counted. */
down(&usu_init_notify);
up(&usu_init_notify);
diff --git a/drivers/usb/storage/onetouch.c b/drivers/usb/storage/onetouch.c
index 313920d980c..f843a0bcf10 100644
--- a/drivers/usb/storage/onetouch.c
+++ b/drivers/usb/storage/onetouch.c
@@ -135,6 +135,7 @@ int onetouch_connect_input(struct us_data *ss)
struct usb_onetouch *onetouch;
struct input_dev *input_dev;
int pipe, maxp;
+ int error = -ENOMEM;
interface = ss->pusb_intf->cur_altsetting;
@@ -211,15 +212,18 @@ int onetouch_connect_input(struct us_data *ss)
ss->suspend_resume_hook = usb_onetouch_pm_hook;
#endif
- input_register_device(onetouch->dev);
+ error = input_register_device(onetouch->dev);
+ if (error)
+ goto fail3;
return 0;
+ fail3: usb_free_urb(onetouch->irq);
fail2: usb_buffer_free(udev, ONETOUCH_PKT_LEN,
onetouch->data, onetouch->data_dma);
fail1: kfree(onetouch);
input_free_device(input_dev);
- return -ENOMEM;
+ return error;
}
void onetouch_release_input(void *onetouch_)
diff --git a/drivers/usb/storage/scsiglue.c b/drivers/usb/storage/scsiglue.c
index a4b7df9ff8c..e1072d52d64 100644
--- a/drivers/usb/storage/scsiglue.c
+++ b/drivers/usb/storage/scsiglue.c
@@ -72,12 +72,27 @@ static const char* host_info(struct Scsi_Host *host)
static int slave_alloc (struct scsi_device *sdev)
{
+ struct us_data *us = host_to_us(sdev->host);
+
/*
* Set the INQUIRY transfer length to 36. We don't use any of
* the extra data and many devices choke if asked for more or
* less than 36 bytes.
*/
sdev->inquiry_len = 36;
+
+ /*
+ * The UFI spec treates the Peripheral Qualifier bits in an
+ * INQUIRY result as reserved and requires devices to set them
+ * to 0. However the SCSI spec requires these bits to be set
+ * to 3 to indicate when a LUN is not present.
+ *
+ * Let the scanning code know if this target merely sets
+ * Peripheral Device Type to 0x1f to indicate no LUN.
+ */
+ if (us->subclass == US_SC_UFI)
+ sdev->sdev_target->pdt_1f_for_no_lun = 1;
+
return 0;
}
diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c
index d6acc92a4ae..f23514c4e64 100644
--- a/drivers/usb/storage/transport.c
+++ b/drivers/usb/storage/transport.c
@@ -294,11 +294,6 @@ static int interpret_urb_result(struct us_data *us, unsigned int pipe,
return USB_STOR_XFER_ERROR;
return USB_STOR_XFER_STALLED;
- /* timeout or excessively long NAK */
- case -ETIMEDOUT:
- US_DEBUGP("-- timeout or NAK\n");
- return USB_STOR_XFER_ERROR;
-
/* babble - the device tried to send more than we wanted to read */
case -EOVERFLOW:
US_DEBUGP("-- babble\n");
diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h
index b130e170b4a..c9a8d50106d 100644
--- a/drivers/usb/storage/unusual_devs.h
+++ b/drivers/usb/storage/unusual_devs.h
@@ -152,6 +152,13 @@ UNUSUAL_DEV( 0x0421, 0x042e, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_IGNORE_RESIDUE | US_FL_FIX_CAPACITY ),
+/* Reported by Jon Hart <Jon.Hart@web.de> */
+UNUSUAL_DEV( 0x0421, 0x0434, 0x0100, 0x0100,
+ "Nokia",
+ "E60",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY | US_FL_IGNORE_RESIDUE ),
+
/* Reported by Sumedha Swamy <sumedhaswamy@gmail.com> and
* Einar Th. Einarsson <einarthered@gmail.com> */
UNUSUAL_DEV( 0x0421, 0x0444, 0x0100, 0x0100,
@@ -218,10 +225,12 @@ UNUSUAL_DEV( 0x0457, 0x0151, 0x0100, 0x0100,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NOT_LOCKABLE ),
+#ifdef CONFIG_USB_STORAGE_KARMA
UNUSUAL_DEV( 0x045a, 0x5210, 0x0101, 0x0101,
"Rio",
"Rio Karma",
- US_SC_SCSI, US_PR_BULK, rio_karma_init, 0),
+ US_SC_SCSI, US_PR_KARMA, rio_karma_init, 0),
+#endif
/* Patch submitted by Philipp Friedrich <philipp@void.at> */
UNUSUAL_DEV( 0x0482, 0x0100, 0x0100, 0x0100,
@@ -631,6 +640,13 @@ UNUSUAL_DEV( 0x0595, 0x4343, 0x0000, 0x2210,
"Digital Camera EX-20 DSC",
US_SC_8070, US_PR_DEVICE, NULL, 0 ),
+/* Reported by <Hendryk.Pfeiffer@gmx.de> */
+UNUSUAL_DEV( 0x059f, 0x0643, 0x0000, 0x0000,
+ "LaCie",
+ "DVD+-RW",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_GO_SLOW ),
+
/* Submitted by Joel Bourquard <numlock@freesurf.ch>
* Some versions of this device need the SubClass and Protocol overrides
* while others don't.
@@ -1254,6 +1270,13 @@ UNUSUAL_DEV( 0x0fce, 0xd008, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_NO_WP_DETECT ),
+/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
+ "Sony Ericsson",
+ "P990i",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Reported by Emmanuel Vasilakis <evas@forthnet.gr> */
UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000,
"Sony Ericsson",
@@ -1261,6 +1284,13 @@ UNUSUAL_DEV( 0x0fce, 0xe031, 0x0000, 0x0000,
US_SC_DEVICE, US_PR_DEVICE, NULL,
US_FL_FIX_CAPACITY ),
+/* Reported by Jan Mate <mate@fiit.stuba.sk> */
+UNUSUAL_DEV( 0x0fce, 0xe030, 0x0000, 0x0000,
+ "Sony Ericsson",
+ "P990i",
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_FIX_CAPACITY ),
+
/* Reported by Kevin Cernekee <kpc-usbdev@gelato.uiuc.edu>
* Tested on hardware version 1.10.
* Entry is needed only for the initializer function override.
diff --git a/drivers/usb/storage/usb.c b/drivers/usb/storage/usb.c
index 8d7bdcb5924..b8d6031b097 100644
--- a/drivers/usb/storage/usb.c
+++ b/drivers/usb/storage/usb.c
@@ -98,6 +98,9 @@
#ifdef CONFIG_USB_STORAGE_ALAUDA
#include "alauda.h"
#endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+#include "karma.h"
+#endif
/* Some informational data */
MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
@@ -646,6 +649,14 @@ static int get_transport(struct us_data *us)
break;
#endif
+#ifdef CONFIG_USB_STORAGE_KARMA
+ case US_PR_KARMA:
+ us->transport_name = "Rio Karma/Bulk";
+ us->transport = rio_karma_transport;
+ us->transport_reset = usb_stor_Bulk_reset;
+ break;
+#endif
+
default:
return -EIO;
}
diff --git a/drivers/usb/usb-skeleton.c b/drivers/usb/usb-skeleton.c
index b362039792b..1b51d3187a9 100644
--- a/drivers/usb/usb-skeleton.c
+++ b/drivers/usb/usb-skeleton.c
@@ -1,5 +1,5 @@
/*
- * USB Skeleton driver - 2.0
+ * USB Skeleton driver - 2.2
*
* Copyright (C) 2001-2004 Greg Kroah-Hartman (greg@kroah.com)
*
@@ -7,9 +7,8 @@
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation, version 2.
*
- * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
- * but has been rewritten to be easy to read and use, as no locks are now
- * needed anymore.
+ * This driver is based on the 2.6.3 version of drivers/usb/usb-skeleton.c
+ * but has been rewritten to be easier to read and use.
*
*/
@@ -21,6 +20,7 @@
#include <linux/kref.h>
#include <asm/uaccess.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
/* Define these values to match your devices */
@@ -32,38 +32,39 @@ static struct usb_device_id skel_table [] = {
{ USB_DEVICE(USB_SKEL_VENDOR_ID, USB_SKEL_PRODUCT_ID) },
{ } /* Terminating entry */
};
-MODULE_DEVICE_TABLE (usb, skel_table);
+MODULE_DEVICE_TABLE(usb, skel_table);
/* Get a minor range for your devices from the usb maintainer */
#define USB_SKEL_MINOR_BASE 192
/* our private defines. if this grows any larger, use your own .h file */
-#define MAX_TRANSFER ( PAGE_SIZE - 512 )
+#define MAX_TRANSFER (PAGE_SIZE - 512)
#define WRITES_IN_FLIGHT 8
/* Structure to hold all of our device specific stuff */
struct usb_skel {
- struct usb_device * udev; /* the usb device for this device */
- struct usb_interface * interface; /* the interface for this device */
+ struct usb_device *dev; /* the usb device for this device */
+ struct usb_interface *interface; /* the interface for this device */
struct semaphore limit_sem; /* limiting the number of writes in progress */
- unsigned char * bulk_in_buffer; /* the buffer to receive data */
+ unsigned char *bulk_in_buffer; /* the buffer to receive data */
size_t bulk_in_size; /* the size of the receive buffer */
__u8 bulk_in_endpointAddr; /* the address of the bulk in endpoint */
__u8 bulk_out_endpointAddr; /* the address of the bulk out endpoint */
struct kref kref;
+ struct mutex io_mutex; /* synchronize I/O with disconnect */
};
#define to_skel_dev(d) container_of(d, struct usb_skel, kref)
static struct usb_driver skel_driver;
static void skel_delete(struct kref *kref)
-{
+{
struct usb_skel *dev = to_skel_dev(kref);
usb_put_dev(dev->udev);
- kfree (dev->bulk_in_buffer);
- kfree (dev);
+ kfree(dev->bulk_in_buffer);
+ kfree(dev);
}
static int skel_open(struct inode *inode, struct file *file)
@@ -89,6 +90,11 @@ static int skel_open(struct inode *inode, struct file *file)
goto exit;
}
+ /* prevent the device from being autosuspended */
+ retval = usb_autopm_get_interface(interface);
+ if (retval)
+ goto exit;
+
/* increment our usage count for the device */
kref_get(&dev->kref);
@@ -107,6 +113,12 @@ static int skel_release(struct inode *inode, struct file *file)
if (dev == NULL)
return -ENODEV;
+ /* allow the device to be autosuspended */
+ mutex_lock(&dev->io_mutex);
+ if (dev->interface)
+ usb_autopm_put_interface(dev->interface);
+ mutex_unlock(&dev->io_mutex);
+
/* decrement the count on our device */
kref_put(&dev->kref, skel_delete);
return 0;
@@ -115,11 +127,17 @@ static int skel_release(struct inode *inode, struct file *file)
static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
{
struct usb_skel *dev;
- int retval = 0;
+ int retval;
int bytes_read;
dev = (struct usb_skel *)file->private_data;
-
+
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto exit;
+ }
+
/* do a blocking bulk read to get data from the device */
retval = usb_bulk_msg(dev->udev,
usb_rcvbulkpipe(dev->udev, dev->bulk_in_endpointAddr),
@@ -135,6 +153,8 @@ static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *
retval = bytes_read;
}
+exit:
+ mutex_unlock(&dev->io_mutex);
return retval;
}
@@ -145,16 +165,16 @@ static void skel_write_bulk_callback(struct urb *urb, struct pt_regs *regs)
dev = (struct usb_skel *)urb->context;
/* sync/async unlink faults aren't errors */
- if (urb->status &&
- !(urb->status == -ENOENT ||
+ if (urb->status &&
+ !(urb->status == -ENOENT ||
urb->status == -ECONNRESET ||
urb->status == -ESHUTDOWN)) {
- dbg("%s - nonzero write bulk status received: %d",
+ err("%s - nonzero write bulk status received: %d",
__FUNCTION__, urb->status);
}
/* free up our allocated buffer */
- usb_buffer_free(urb->dev, urb->transfer_buffer_length,
+ usb_buffer_free(urb->dev, urb->transfer_buffer_length,
urb->transfer_buffer, urb->transfer_dma);
up(&dev->limit_sem);
}
@@ -179,6 +199,12 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
goto exit;
}
+ mutex_lock(&dev->io_mutex);
+ if (!dev->interface) { /* disconnect() was called */
+ retval = -ENODEV;
+ goto error;
+ }
+
/* create a urb, and a buffer for it, and copy the data to the urb */
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb) {
@@ -213,17 +239,22 @@ static ssize_t skel_write(struct file *file, const char *user_buffer, size_t cou
/* release our reference to this urb, the USB core will eventually free it entirely */
usb_free_urb(urb);
-exit:
+ mutex_unlock(&dev->io_mutex);
return writesize;
error:
- usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
- usb_free_urb(urb);
+ if (urb) {
+ usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
+ usb_free_urb(urb);
+ }
+ mutex_unlock(&dev->io_mutex);
up(&dev->limit_sem);
+
+exit:
return retval;
}
-static struct file_operations skel_fops = {
+static const struct file_operations skel_fops = {
.owner = THIS_MODULE,
.read = skel_read,
.write = skel_write,
@@ -231,7 +262,7 @@ static struct file_operations skel_fops = {
.release = skel_release,
};
-/*
+/*
* usb class driver info in order to get a minor number from the usb core,
* and to have the device registered with the driver core
*/
@@ -243,7 +274,7 @@ static struct usb_class_driver skel_class = {
static int skel_probe(struct usb_interface *interface, const struct usb_device_id *id)
{
- struct usb_skel *dev = NULL;
+ struct usb_skel *dev;
struct usb_host_interface *iface_desc;
struct usb_endpoint_descriptor *endpoint;
size_t buffer_size;
@@ -252,12 +283,13 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
/* allocate memory for our device state and initialize it */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
- if (dev == NULL) {
+ if (!dev) {
err("Out of memory");
goto error;
}
kref_init(&dev->kref);
sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
+ mutex_init(&dev->io_mutex);
dev->udev = usb_get_dev(interface_to_usbdev(interface));
dev->interface = interface;
@@ -269,10 +301,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
endpoint = &iface_desc->endpoint[i].desc;
if (!dev->bulk_in_endpointAddr &&
- ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_IN) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
buffer_size = le16_to_cpu(endpoint->wMaxPacketSize);
dev->bulk_in_size = buffer_size;
@@ -285,10 +314,7 @@ static int skel_probe(struct usb_interface *interface, const struct usb_device_i
}
if (!dev->bulk_out_endpointAddr &&
- ((endpoint->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
- == USB_DIR_OUT) &&
- ((endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
- == USB_ENDPOINT_XFER_BULK)) {
+ usb_endpoint_is_bulk_out(endpoint)) {
/* we found a bulk out endpoint */
dev->bulk_out_endpointAddr = endpoint->bEndpointAddress;
}
@@ -334,6 +360,11 @@ static void skel_disconnect(struct usb_interface *interface)
/* give back our minor */
usb_deregister_dev(interface, &skel_class);
+ /* prevent more I/O from starting */
+ mutex_lock(&dev->io_mutex);
+ dev->interface = NULL;
+ mutex_unlock(&dev->io_mutex);
+
unlock_kernel();
/* decrement our usage count */
@@ -367,7 +398,7 @@ static void __exit usb_skel_exit(void)
usb_deregister(&skel_driver);
}
-module_init (usb_skel_init);
-module_exit (usb_skel_exit);
+module_init(usb_skel_init);
+module_exit(usb_skel_exit);
MODULE_LICENSE("GPL");
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig
index 702eb933cf8..a1c8923b0bf 100644
--- a/drivers/video/Kconfig
+++ b/drivers/video/Kconfig
@@ -53,6 +53,11 @@ config FB
(e.g. an accelerated X server) and that are not frame buffer
device-aware may cause unexpected results. If unsure, say N.
+config FB_DDC
+ tristate
+ depends on FB && I2C && I2C_ALGOBIT
+ default n
+
config FB_CFB_FILLRECT
tristate
depends on FB
@@ -696,6 +701,7 @@ config FB_NVIDIA
depends on FB && PCI
select I2C_ALGOBIT if FB_NVIDIA_I2C
select I2C if FB_NVIDIA_I2C
+ select FB_DDC if FB_NVIDIA_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -734,6 +740,7 @@ config FB_RIVA
depends on FB && PCI
select I2C_ALGOBIT if FB_RIVA_I2C
select I2C if FB_RIVA_I2C
+ select FB_DDC if FB_RIVA_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -822,33 +829,52 @@ config FB_I810_I2C
depends on FB_I810 && FB_I810_GTF
select I2C
select I2C_ALGOBIT
+ select FB_DDC
help
config FB_INTEL
- tristate "Intel 830M/845G/852GM/855GM/865G support (EXPERIMENTAL)"
+ tristate "Intel 830M/845G/852GM/855GM/865G/915G/945G support (EXPERIMENTAL)"
depends on FB && EXPERIMENTAL && PCI && X86
select AGP
select AGP_INTEL
+ select I2C_ALGOBIT if FB_INTEL_I2C
+ select I2C if FB_INTEL_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
select FB_CFB_IMAGEBLIT
help
This driver supports the on-board graphics built in to the Intel
- 830M/845G/852GM/855GM/865G chipsets.
+ 830M/845G/852GM/855GM/865G/915G/915GM/945G/945GM chipsets.
Say Y if you have and plan to use such a board.
- To compile this driver as a module, choose M here: the
+ If you say Y here and want DDC/I2C support you must first say Y to
+ "I2C support" and "I2C bit-banging support" in the character devices
+ section.
+
+ If you say M here then "I2C support" and "I2C bit-banging support"
+ can be build either as modules or built-in.
+
+ To compile this driver as a module, choose M here: the
module will be called intelfb.
+ For more information, please read <file:Documentation/fb/intelfb.txt>
+
config FB_INTEL_DEBUG
- bool "Intel driver Debug Messages"
+ bool "Intel driver Debug Messages"
depends on FB_INTEL
---help---
Say Y here if you want the Intel driver to output all sorts
of debugging informations to provide to the maintainer when
something goes wrong.
+config FB_INTEL_I2C
+ bool "DDC/I2C for Intel framebuffer support"
+ depends on FB_INTEL
+ default y
+ help
+ Say Y here if you want DDC/I2C support for your on-board Intel graphics.
+
config FB_MATROX
tristate "Matrox acceleration"
depends on FB && PCI
@@ -994,6 +1020,7 @@ config FB_RADEON
depends on FB && PCI
select I2C_ALGOBIT if FB_RADEON_I2C
select I2C if FB_RADEON_I2C
+ select FB_DDC if FB_RADEON_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -1122,6 +1149,7 @@ config FB_SAVAGE
depends on FB && PCI && EXPERIMENTAL
select I2C_ALGOBIT if FB_SAVAGE_I2C
select I2C if FB_SAVAGE_I2C
+ select FB_DDC if FB_SAVAGE_I2C
select FB_MODE_HELPERS
select FB_CFB_FILLRECT
select FB_CFB_COPYAREA
@@ -1601,7 +1629,8 @@ config FB_VIRTUAL
kernel option `video=vfb:'.
To compile this driver as a module, choose M here: the
- module will be called vfb.
+ module will be called vfb. In order to load it, you must use
+ the vfb_enable=1 option.
If unsure, say N.
if VT
diff --git a/drivers/video/Makefile b/drivers/video/Makefile
index 481c6c9695f..a6980e9a248 100644
--- a/drivers/video/Makefile
+++ b/drivers/video/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_FB_CFB_FILLRECT) += cfbfillrect.o
obj-$(CONFIG_FB_CFB_COPYAREA) += cfbcopyarea.o
obj-$(CONFIG_FB_CFB_IMAGEBLIT) += cfbimgblt.o
obj-$(CONFIG_FB_MACMODES) += macmodes.o
+obj-$(CONFIG_FB_DDC) += fb_ddc.o
# Hardware specific drivers go first
obj-$(CONFIG_FB_RETINAZ3) += retz3fb.o
diff --git a/drivers/video/aty/atyfb.h b/drivers/video/aty/atyfb.h
index 55fb8b04489..b04f49fb976 100644
--- a/drivers/video/aty/atyfb.h
+++ b/drivers/video/aty/atyfb.h
@@ -355,5 +355,9 @@ static inline void wait_for_idle(struct atyfb_par *par)
extern void aty_reset_engine(const struct atyfb_par *par);
extern void aty_init_engine(struct atyfb_par *par, struct fb_info *info);
-extern void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par);
extern u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par);
+
+void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
+void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
+void atyfb_imageblit(struct fb_info *info, const struct fb_image *image);
+
diff --git a/drivers/video/aty/atyfb_base.c b/drivers/video/aty/atyfb_base.c
index 19a71f04578..b45c9fd1b33 100644
--- a/drivers/video/aty/atyfb_base.c
+++ b/drivers/video/aty/atyfb_base.c
@@ -240,9 +240,6 @@ static int atyfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
static int atyfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info);
static int atyfb_blank(int blank, struct fb_info *info);
static int atyfb_ioctl(struct fb_info *info, u_int cmd, u_long arg);
-extern void atyfb_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
-extern void atyfb_copyarea(struct fb_info *info, const struct fb_copyarea *area);
-extern void atyfb_imageblit(struct fb_info *info, const struct fb_image *image);
#ifdef __sparc__
static int atyfb_mmap(struct fb_info *info, struct vm_area_struct *vma);
#endif
@@ -3863,6 +3860,7 @@ static int __devinit atyfb_setup(char *options)
static int __devinit atyfb_init(void)
{
+ int err1 = 1, err2 = 1;
#ifndef MODULE
char *option = NULL;
@@ -3872,12 +3870,13 @@ static int __devinit atyfb_init(void)
#endif
#ifdef CONFIG_PCI
- pci_register_driver(&atyfb_driver);
+ err1 = pci_register_driver(&atyfb_driver);
#endif
#ifdef CONFIG_ATARI
- atyfb_atari_probe();
+ err2 = atyfb_atari_probe();
#endif
- return 0;
+
+ return (err1 && err2) ? -ENODEV : 0;
}
static void __exit atyfb_exit(void)
diff --git a/drivers/video/aty/mach64_ct.c b/drivers/video/aty/mach64_ct.c
index e7056934c6a..5080816be65 100644
--- a/drivers/video/aty/mach64_ct.c
+++ b/drivers/video/aty/mach64_ct.c
@@ -27,7 +27,7 @@ u8 aty_ld_pll_ct(int offset, const struct atyfb_par *par)
return res;
}
-void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par)
+static void aty_st_pll_ct(int offset, u8 val, const struct atyfb_par *par)
{
/* write addr byte */
aty_st_8(CLOCK_CNTL_ADDR, ((offset << 2) & PLL_ADDR) | PLL_WR_EN, par);
diff --git a/drivers/video/aty/radeon_i2c.c b/drivers/video/aty/radeon_i2c.c
index 9aaca58c074..67675452009 100644
--- a/drivers/video/aty/radeon_i2c.c
+++ b/drivers/video/aty/radeon_i2c.c
@@ -16,8 +16,6 @@
#include "radeonfb.h"
#include "../edid.h"
-#define RADEON_DDC 0x50
-
static void radeon_gpio_setscl(void* data, int state)
{
struct radeon_i2c_chan *chan = data;
@@ -138,108 +136,10 @@ void radeon_delete_i2c_busses(struct radeonfb_info *rinfo)
rinfo->i2c[3].rinfo = NULL;
}
-
-static u8 *radeon_do_probe_i2c_edid(struct radeon_i2c_chan *chan)
-{
- u8 start = 0x0;
- struct i2c_msg msgs[] = {
- {
- .addr = RADEON_DDC,
- .len = 1,
- .buf = &start,
- }, {
- .addr = RADEON_DDC,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- },
- };
- u8 *buf;
-
- buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (!buf) {
- dev_warn(&chan->rinfo->pdev->dev, "Out of memory!\n");
- return NULL;
- }
- msgs[1].buf = buf;
-
- if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
- return buf;
- dev_dbg(&chan->rinfo->pdev->dev, "Unable to read EDID block.\n");
- kfree(buf);
- return NULL;
-}
-
-
-int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn, u8 **out_edid)
+int radeon_probe_i2c_connector(struct radeonfb_info *rinfo, int conn,
+ u8 **out_edid)
{
- u32 reg = rinfo->i2c[conn-1].ddc_reg;
- u8 *edid = NULL;
- int i, j;
-
- OUTREG(reg, INREG(reg) &
- ~(VGA_DDC_DATA_OUTPUT | VGA_DDC_CLK_OUTPUT));
-
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
- (void)INREG(reg);
-
- for (i = 0; i < 3; i++) {
- /* For some old monitors we need the
- * following process to initialize/stop DDC
- */
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
- (void)INREG(reg);
- msleep(13);
-
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
- (void)INREG(reg);
- for (j = 0; j < 5; j++) {
- msleep(10);
- if (INREG(reg) & VGA_DDC_CLK_INPUT)
- break;
- }
- if (j == 5)
- continue;
-
- OUTREG(reg, INREG(reg) | VGA_DDC_DATA_OUT_EN);
- (void)INREG(reg);
- msleep(15);
- OUTREG(reg, INREG(reg) | VGA_DDC_CLK_OUT_EN);
- (void)INREG(reg);
- msleep(15);
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
- (void)INREG(reg);
- msleep(15);
-
- /* Do the real work */
- edid = radeon_do_probe_i2c_edid(&rinfo->i2c[conn-1]);
-
- OUTREG(reg, INREG(reg) |
- (VGA_DDC_DATA_OUT_EN | VGA_DDC_CLK_OUT_EN));
- (void)INREG(reg);
- msleep(15);
-
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN));
- (void)INREG(reg);
- for (j = 0; j < 10; j++) {
- msleep(10);
- if (INREG(reg) & VGA_DDC_CLK_INPUT)
- break;
- }
-
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_DATA_OUT_EN));
- (void)INREG(reg);
- msleep(15);
- OUTREG(reg, INREG(reg) |
- (VGA_DDC_DATA_OUT_EN | VGA_DDC_CLK_OUT_EN));
- (void)INREG(reg);
- if (edid)
- break;
- }
- /* Release the DDC lines when done or the Apple Cinema HD display
- * will switch off
- */
- OUTREG(reg, INREG(reg) & ~(VGA_DDC_CLK_OUT_EN | VGA_DDC_DATA_OUT_EN));
- (void)INREG(reg);
+ u8 *edid = fb_ddc_read(&rinfo->i2c[conn-1].adapter);
if (out_edid)
*out_edid = edid;
diff --git a/drivers/video/aty/radeon_pm.c b/drivers/video/aty/radeon_pm.c
index e308ed2d249..9a2b0d69b0a 100644
--- a/drivers/video/aty/radeon_pm.c
+++ b/drivers/video/aty/radeon_pm.c
@@ -86,6 +86,9 @@ static struct radeon_device_id radeon_workaround_list[] = {
BUGFIX("Samsung P35",
PCI_VENDOR_ID_SAMSUNG, 0xc00c,
radeon_pm_off, radeon_reinitialize_M10),
+ BUGFIX("Acer Aspire 2010",
+ PCI_VENDOR_ID_AI, 0x0061,
+ radeon_pm_off, radeon_reinitialize_M10),
{ .ident = NULL }
};
@@ -2621,25 +2624,28 @@ static int radeon_restore_pci_cfg(struct radeonfb_info *rinfo)
}
-int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(pdev);
struct radeonfb_info *rinfo = info->par;
int i;
- if (state.event == pdev->dev.power.power_state.event)
+ if (mesg.event == pdev->dev.power.power_state.event)
return 0;
- printk(KERN_DEBUG "radeonfb (%s): suspending to state: %d...\n",
- pci_name(pdev), state.event);
+ printk(KERN_DEBUG "radeonfb (%s): suspending for event: %d...\n",
+ pci_name(pdev), mesg.event);
/* For suspend-to-disk, we cheat here. We don't suspend anything and
* let fbcon continue drawing until we are all set. That shouldn't
* really cause any problem at this point, provided that the wakeup
* code knows that any state in memory may not match the HW
*/
- if (state.event == PM_EVENT_FREEZE)
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE: /* about to take snapshot */
+ case PM_EVENT_PRETHAW: /* before restoring snapshot */
goto done;
+ }
acquire_console_sem();
@@ -2706,7 +2712,7 @@ int radeonfb_pci_suspend(struct pci_dev *pdev, pm_message_t state)
release_console_sem();
done:
- pdev->dev.power.power_state = state;
+ pdev->dev.power.power_state = mesg;
return 0;
}
diff --git a/drivers/video/au1100fb.c b/drivers/video/au1100fb.c
index f25d5d64833..ef5c16f7f5a 100644
--- a/drivers/video/au1100fb.c
+++ b/drivers/video/au1100fb.c
@@ -8,6 +8,7 @@
* <c.pellegrin@exadron.com>
*
* PM support added by Rodolfo Giometti <giometti@linux.it>
+ * Cursor enable/disable by Rodolfo Giometti <giometti@linux.it>
*
* Copyright 2002 MontaVista Software
* Author: MontaVista Software, Inc.
@@ -110,6 +111,10 @@ static struct fb_var_screeninfo au1100fb_var __initdata = {
static struct au1100fb_drv_info drv_info;
+static int nocursor = 0;
+module_param(nocursor, int, 0644);
+MODULE_PARM_DESC(nocursor, "cursor enable/disable");
+
/*
* Set hardware with var settings. This will enable the controller with a specific
* mode, normally validated with the fb_check_var method
@@ -422,6 +427,17 @@ int au1100fb_fb_mmap(struct fb_info *fbi, struct vm_area_struct *vma)
return 0;
}
+/* fb_cursor
+ * Used to disable cursor drawing...
+ */
+int au1100fb_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
+{
+ if (nocursor)
+ return 0;
+ else
+ return -EINVAL; /* just to force soft_cursor() call */
+}
+
static struct fb_ops au1100fb_ops =
{
.owner = THIS_MODULE,
@@ -433,6 +449,7 @@ static struct fb_ops au1100fb_ops =
.fb_imageblit = cfb_imageblit,
.fb_rotate = au1100fb_fb_rotate,
.fb_mmap = au1100fb_fb_mmap,
+ .fb_cursor = au1100fb_fb_cursor,
};
@@ -677,7 +694,7 @@ int au1100fb_setup(char *options)
if (options) {
while ((this_opt = strsep(&options,",")) != NULL) {
/* Panel option */
- if (!strncmp(this_opt, "panel:", 6)) {
+ if (!strncmp(this_opt, "panel:", 6)) {
int i;
this_opt += 6;
for (i = 0; i < num_panels; i++) {
@@ -685,13 +702,18 @@ int au1100fb_setup(char *options)
known_lcd_panels[i].name,
strlen(this_opt))) {
panel_idx = i;
- break;
+ break;
+ }
}
- }
if (i >= num_panels) {
print_warn("Panel %s not supported!", this_opt);
}
}
+ if (!strncmp(this_opt, "nocursor", 8)) {
+ this_opt += 8;
+ nocursor = 1;
+ print_info("Cursor disabled");
+ }
/* Mode option (only option that start with digit) */
else if (isdigit(this_opt[0])) {
mode = kmalloc(strlen(this_opt) + 1, GFP_KERNEL);
@@ -700,7 +722,7 @@ int au1100fb_setup(char *options)
/* Unsupported option */
else {
print_warn("Unsupported option \"%s\"", this_opt);
- }
+ }
}
}
diff --git a/drivers/video/backlight/hp680_bl.c b/drivers/video/backlight/hp680_bl.c
index ffc72ae3ada..fe1488374f6 100644
--- a/drivers/video/backlight/hp680_bl.c
+++ b/drivers/video/backlight/hp680_bl.c
@@ -20,7 +20,7 @@
#include <asm/cpu/dac.h>
#include <asm/hp6xx/hp6xx.h>
-#include <asm/hd64461/hd64461.h>
+#include <asm/hd64461.h>
#define HP680_MAX_INTENSITY 255
#define HP680_DEFAULT_INTENSITY 10
@@ -163,6 +163,6 @@ static void __exit hp680bl_exit(void)
module_init(hp680bl_init);
module_exit(hp680bl_exit);
-MODULE_AUTHOR("Andriy Skulysh <askulysh@image.kiev.ua>");
+MODULE_AUTHOR("Andriy Skulysh <askulysh@gmail.com>");
MODULE_DESCRIPTION("HP Jornada 680 Backlight Driver");
MODULE_LICENSE("GPL");
diff --git a/drivers/video/backlight/locomolcd.c b/drivers/video/backlight/locomolcd.c
index caf1eca199b..628571c63ba 100644
--- a/drivers/video/backlight/locomolcd.c
+++ b/drivers/video/backlight/locomolcd.c
@@ -33,19 +33,19 @@ static unsigned long locomolcd_flags;
static void locomolcd_on(int comadj)
{
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 1);
mdelay(2);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 1);
mdelay(2);
locomo_m62332_senddata(locomolcd_dev, comadj, 0);
mdelay(5);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 1);
mdelay(10);
/* TFTCRST | CPSOUT=0 | CPSEN */
@@ -58,8 +58,8 @@ static void locomolcd_on(int comadj)
locomo_writel((0x04 | 0x01), locomolcd_dev->mapbase + LOCOMO_TC);
mdelay(10);
- locomo_gpio_set_dir(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 1);
+ locomo_gpio_set_dir(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 1);
}
static void locomolcd_off(int comadj)
@@ -68,16 +68,16 @@ static void locomolcd_off(int comadj)
locomo_writel(0x06, locomolcd_dev->mapbase + LOCOMO_TC);
mdelay(1);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHA_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHA_ON, 0);
mdelay(110);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VEE_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VEE_ON, 0);
mdelay(700);
/* TFTCRST=0 | CPSOUT=0 | CPSEN = 0 */
locomo_writel(0, locomolcd_dev->mapbase + LOCOMO_TC);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_MOD, 0);
- locomo_gpio_write(locomolcd_dev, LOCOMO_GPIO_LCD_VSHD_ON, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_MOD, 0);
+ locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0);
}
void locomolcd_power(int on)
@@ -167,14 +167,14 @@ static int locomolcd_resume(struct locomo_dev *dev)
#define locomolcd_resume NULL
#endif
-static int locomolcd_probe(struct locomo_dev *dev)
+static int locomolcd_probe(struct locomo_dev *ldev)
{
unsigned long flags;
local_irq_save(flags);
- locomolcd_dev = dev;
+ locomolcd_dev = ldev;
- locomo_gpio_set_dir(dev, LOCOMO_GPIO_FL_VR, 0);
+ locomo_gpio_set_dir(ldev->dev.parent, 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.
diff --git a/drivers/video/console/Kconfig b/drivers/video/console/Kconfig
index 4444bef68fb..aa3935df852 100644
--- a/drivers/video/console/Kconfig
+++ b/drivers/video/console/Kconfig
@@ -6,7 +6,7 @@ menu "Console display driver support"
config VGA_CONSOLE
bool "VGA text console" if EMBEDDED || !X86
- depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE
+ depends on !ARCH_ACORN && !ARCH_EBSA110 && !4xx && !8xx && !SPARC && !M68K && !PARISC && !FRV && !ARCH_VERSATILE && !SUPERH
default y
help
Saying Y here will allow you to use Linux in text mode through a
diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c
index 1b4f75d1f8a..8c041daa3a1 100644
--- a/drivers/video/console/fbcon.c
+++ b/drivers/video/console/fbcon.c
@@ -133,6 +133,7 @@ static int info_idx = -1;
/* console rotation */
static int rotate;
+static int fbcon_has_sysfs;
static const struct consw fb_con;
@@ -396,9 +397,8 @@ static void fb_flashcursor(void *private)
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->vc_deccm != 1) {
release_console_sem();
return;
}
@@ -2166,7 +2166,12 @@ static int fbcon_switch(struct vc_data *vc)
fbcon_del_cursor_timer(old_info);
}
- fbcon_add_cursor_timer(info);
+ if (fbcon_is_inactive(vc, info) ||
+ ops->blank_state != FB_BLANK_UNBLANK)
+ fbcon_del_cursor_timer(info);
+ else
+ fbcon_add_cursor_timer(info);
+
set_blitting_type(vc, info);
ops->cursor_reset = 1;
@@ -2276,10 +2281,11 @@ static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch)
update_screen(vc);
}
- if (!blank)
- fbcon_add_cursor_timer(info);
- else
+ if (fbcon_is_inactive(vc, info) ||
+ ops->blank_state != FB_BLANK_UNBLANK)
fbcon_del_cursor_timer(info);
+ else
+ fbcon_add_cursor_timer(info);
return 0;
}
@@ -3161,11 +3167,26 @@ static struct class_device_attribute class_device_attrs[] = {
static int fbcon_init_class_device(void)
{
- int i;
+ int i, error = 0;
+
+ fbcon_has_sysfs = 1;
+
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) {
+ error = class_device_create_file(fbcon_class_device,
+ &class_device_attrs[i]);
+
+ if (error)
+ break;
+ }
+
+ if (error) {
+ while (--i >= 0)
+ class_device_remove_file(fbcon_class_device,
+ &class_device_attrs[i]);
+
+ fbcon_has_sysfs = 0;
+ }
- for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
- class_device_create_file(fbcon_class_device,
- &class_device_attrs[i]);
return 0;
}
@@ -3225,7 +3246,10 @@ static void fbcon_exit(void)
module_put(info->fbops->owner);
if (info->fbcon_par) {
+ struct fbcon_ops *ops = info->fbcon_par;
+
fbcon_del_cursor_timer(info);
+ kfree(ops->cursor_src);
kfree(info->fbcon_par);
info->fbcon_par = NULL;
}
@@ -3271,9 +3295,13 @@ 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]);
+ if (fbcon_has_sysfs) {
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_remove_file(fbcon_class_device,
+ &class_device_attrs[i]);
+
+ fbcon_has_sysfs = 0;
+ }
}
static void __exit fb_console_exit(void)
diff --git a/drivers/video/console/fbcon.h b/drivers/video/console/fbcon.h
index f244ad066d6..b9386d168c0 100644
--- a/drivers/video/console/fbcon.h
+++ b/drivers/video/console/fbcon.h
@@ -80,6 +80,8 @@ struct fbcon_ops {
char *cursor_data;
u8 *fontbuffer;
u8 *fontdata;
+ u8 *cursor_src;
+ u32 cursor_size;
u32 fd_size;
};
/*
diff --git a/drivers/video/console/fbcon_ccw.c b/drivers/video/console/fbcon_ccw.c
index 4481c80b8b2..825e6d6972a 100644
--- a/drivers/video/console/fbcon_ccw.c
+++ b/drivers/video/console/fbcon_ccw.c
@@ -391,7 +391,7 @@ static void ccw_cursor(struct vc_data *vc, struct fb_info *info, int mode,
ops->cursor_reset = 0;
}
-int ccw_update_start(struct fb_info *info)
+static int ccw_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
u32 yoffset;
diff --git a/drivers/video/console/fbcon_cw.c b/drivers/video/console/fbcon_cw.c
index 7f92c06afea..c637e631880 100644
--- a/drivers/video/console/fbcon_cw.c
+++ b/drivers/video/console/fbcon_cw.c
@@ -375,7 +375,7 @@ static void cw_cursor(struct vc_data *vc, struct fb_info *info, int mode,
ops->cursor_reset = 0;
}
-int cw_update_start(struct fb_info *info)
+static int cw_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
u32 vxres = GETVXRES(ops->p->scrollmode, info);
diff --git a/drivers/video/console/fbcon_ud.c b/drivers/video/console/fbcon_ud.c
index ab91005e64d..1473506df5d 100644
--- a/drivers/video/console/fbcon_ud.c
+++ b/drivers/video/console/fbcon_ud.c
@@ -415,7 +415,7 @@ static void ud_cursor(struct vc_data *vc, struct fb_info *info, int mode,
ops->cursor_reset = 0;
}
-int ud_update_start(struct fb_info *info)
+static int ud_update_start(struct fb_info *info)
{
struct fbcon_ops *ops = info->fbcon_par;
int xoffset, yoffset;
diff --git a/drivers/video/console/softcursor.c b/drivers/video/console/softcursor.c
index 557c563e4ae..7d07d838356 100644
--- a/drivers/video/console/softcursor.c
+++ b/drivers/video/console/softcursor.c
@@ -20,11 +20,12 @@
int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
{
+ struct fbcon_ops *ops = info->fbcon_par;
unsigned int scan_align = info->pixmap.scan_align - 1;
unsigned int buf_align = info->pixmap.buf_align - 1;
unsigned int i, size, dsize, s_pitch, d_pitch;
struct fb_image *image;
- u8 *dst, *src;
+ u8 *dst;
if (info->state != FBINFO_STATE_RUNNING)
return 0;
@@ -32,11 +33,19 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
s_pitch = (cursor->image.width + 7) >> 3;
dsize = s_pitch * cursor->image.height;
- src = kmalloc(dsize + sizeof(struct fb_image), GFP_ATOMIC);
- if (!src)
- return -ENOMEM;
+ if (dsize + sizeof(struct fb_image) != ops->cursor_size) {
+ if (ops->cursor_src != NULL)
+ kfree(ops->cursor_src);
+ ops->cursor_size = dsize + sizeof(struct fb_image);
- image = (struct fb_image *) (src + dsize);
+ ops->cursor_src = kmalloc(ops->cursor_size, GFP_ATOMIC);
+ if (!ops->cursor_src) {
+ ops->cursor_size = 0;
+ return -ENOMEM;
+ }
+ }
+
+ image = (struct fb_image *) (ops->cursor_src + dsize);
*image = cursor->image;
d_pitch = (s_pitch + scan_align) & ~scan_align;
@@ -48,21 +57,23 @@ int soft_cursor(struct fb_info *info, struct fb_cursor *cursor)
switch (cursor->rop) {
case ROP_XOR:
for (i = 0; i < dsize; i++)
- src[i] = image->data[i] ^ cursor->mask[i];
+ ops->cursor_src[i] = image->data[i] ^
+ cursor->mask[i];
break;
case ROP_COPY:
default:
for (i = 0; i < dsize; i++)
- src[i] = image->data[i] & cursor->mask[i];
+ ops->cursor_src[i] = image->data[i] &
+ cursor->mask[i];
break;
}
} else
- memcpy(src, image->data, dsize);
+ memcpy(ops->cursor_src, image->data, dsize);
- fb_pad_aligned_buffer(dst, d_pitch, src, s_pitch, image->height);
+ fb_pad_aligned_buffer(dst, d_pitch, ops->cursor_src, s_pitch,
+ image->height);
image->data = dst;
info->fbops->fb_imageblit(info, image);
- kfree(src);
return 0;
}
diff --git a/drivers/video/fb_ddc.c b/drivers/video/fb_ddc.c
new file mode 100644
index 00000000000..3aa6ebf68f1
--- /dev/null
+++ b/drivers/video/fb_ddc.c
@@ -0,0 +1,116 @@
+/*
+ * driver/vide/fb_ddc.c - DDC/EDID read support.
+ *
+ * Copyright (C) 2006 Dennis Munsie <dmunsie@cecropia.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file COPYING in the main directory of this archive
+ * for more details.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/fb.h>
+#include <linux/i2c-algo-bit.h>
+
+#include "edid.h"
+
+#define DDC_ADDR 0x50
+
+static unsigned char *fb_do_probe_ddc_edid(struct i2c_adapter *adapter)
+{
+ unsigned char start = 0x0;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = DDC_ADDR,
+ .len = 1,
+ .buf = &start,
+ }, {
+ .addr = DDC_ADDR,
+ .flags = I2C_M_RD,
+ .len = EDID_LENGTH,
+ }
+ };
+ unsigned char *buf;
+
+ buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
+ if (!buf) {
+ dev_warn(&adapter->dev, "unable to allocate memory for EDID "
+ "block.\n");
+ return NULL;
+ }
+ msgs[1].buf = buf;
+
+ if (i2c_transfer(adapter, msgs, 2) == 2)
+ return buf;
+
+ dev_warn(&adapter->dev, "unable to read EDID block.\n");
+ kfree(buf);
+ return NULL;
+}
+
+unsigned char *fb_ddc_read(struct i2c_adapter *adapter)
+{
+ struct i2c_algo_bit_data *algo_data = adapter->algo_data;
+ unsigned char *edid = NULL;
+ int i, j;
+
+ algo_data->setscl(algo_data->data, 1);
+ algo_data->setscl(algo_data->data, 0);
+
+ for (i = 0; i < 3; i++) {
+ /* For some old monitors we need the
+ * following process to initialize/stop DDC
+ */
+ algo_data->setsda(algo_data->data, 0);
+ msleep(13);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 5; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+ if (j == 5)
+ continue;
+
+ algo_data->setsda(algo_data->data, 0);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+
+ /* Do the real work */
+ edid = fb_do_probe_ddc_edid(adapter);
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+ msleep(15);
+
+ algo_data->setscl(algo_data->data, 1);
+ for (j = 0; j < 10; j++) {
+ msleep(10);
+ if (algo_data->getscl(algo_data->data))
+ break;
+ }
+
+ algo_data->setsda(algo_data->data, 1);
+ msleep(15);
+ algo_data->setscl(algo_data->data, 0);
+ if (edid)
+ break;
+ }
+ /* Release the DDC lines when done or the Apple Cinema HD display
+ * will switch off
+ */
+ algo_data->setsda(algo_data->data, 0);
+ algo_data->setscl(algo_data->data, 0);
+
+ return edid;
+}
+
+EXPORT_SYMBOL_GPL(fb_ddc_read);
+
+MODULE_AUTHOR("Dennis Munsie <dmunsie@cecropia.com>");
+MODULE_DESCRIPTION("DDC/EDID reading support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index 17961e3ecaa..93ffcdd95f5 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -554,7 +554,8 @@ static int fbmem_read_proc(char *buf, char **start, off_t offset,
int clen;
clen = 0;
- for (fi = registered_fb; fi < &registered_fb[FB_MAX] && len < 4000; fi++)
+ for (fi = registered_fb; fi < &registered_fb[FB_MAX] && clen < 4000;
+ fi++)
if (*fi)
clen += sprintf(buf + clen, "%d %s\n",
(*fi)->node,
diff --git a/drivers/video/fbsysfs.c b/drivers/video/fbsysfs.c
index c151dcf6878..d3a50417ed9 100644
--- a/drivers/video/fbsysfs.c
+++ b/drivers/video/fbsysfs.c
@@ -20,6 +20,8 @@
#include <linux/console.h>
#include <linux/module.h>
+#define FB_SYSFS_FLAG_ATTR 1
+
/**
* framebuffer_alloc - creates a new frame buffer info structure
*
@@ -483,12 +485,27 @@ static struct class_device_attribute class_device_attrs[] = {
int fb_init_class_device(struct fb_info *fb_info)
{
- unsigned int i;
+ int i, error = 0;
+
class_set_devdata(fb_info->class_device, fb_info);
- for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
- class_device_create_file(fb_info->class_device,
- &class_device_attrs[i]);
+ fb_info->class_flag |= FB_SYSFS_FLAG_ATTR;
+
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++) {
+ error = class_device_create_file(fb_info->class_device,
+ &class_device_attrs[i]);
+
+ if (error)
+ break;
+ }
+
+ if (error) {
+ while (--i >= 0)
+ class_device_remove_file(fb_info->class_device,
+ &class_device_attrs[i]);
+ fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
+ }
+
return 0;
}
@@ -496,9 +513,13 @@ void fb_cleanup_class_device(struct fb_info *fb_info)
{
unsigned int i;
- for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
- class_device_remove_file(fb_info->class_device,
- &class_device_attrs[i]);
+ if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) {
+ for (i = 0; i < ARRAY_SIZE(class_device_attrs); i++)
+ class_device_remove_file(fb_info->class_device,
+ &class_device_attrs[i]);
+
+ fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR;
+ }
}
#ifdef CONFIG_FB_BACKLIGHT
diff --git a/drivers/video/hitfb.c b/drivers/video/hitfb.c
index 4cc6b454265..3afb472763c 100644
--- a/drivers/video/hitfb.c
+++ b/drivers/video/hitfb.c
@@ -4,7 +4,7 @@
* (C) 1999 Mihai Spatar
* (C) 2000 YAEGASHI Takeshi
* (C) 2003, 2004 Paul Mundt
- * (C) 2003, 2004 Andriy Skulysh
+ * (C) 2003, 2004, 2006 Andriy Skulysh
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file COPYING in the main directory of this archive for
@@ -20,18 +20,16 @@
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/init.h>
+#include <linux/platform_device.h>
#include <linux/fb.h>
#include <asm/machvec.h>
#include <asm/uaccess.h>
#include <asm/pgtable.h>
#include <asm/io.h>
-#include <asm/hd64461/hd64461.h>
-
-#ifdef MACH_HP600
+#include <asm/hd64461.h>
#include <asm/cpu/dac.h>
#include <asm/hp6xx/hp6xx.h>
-#endif
#define WIDTH 640
@@ -45,7 +43,6 @@ static struct fb_var_screeninfo hitfb_var __initdata = {
static struct fb_fix_screeninfo hitfb_fix __initdata = {
.id = "Hitachi HD64461",
.type = FB_TYPE_PACKED_PIXELS,
- .ypanstep = 8,
.accel = FB_ACCEL_NONE,
};
@@ -73,26 +70,14 @@ static inline void hitfb_accel_set_dest(int truecolor, u16 dx, u16 dy,
if (truecolor)
saddr <<= 1;
- fb_writew(width, HD64461_BBTDWR);
- fb_writew(height, HD64461_BBTDHR);
+ fb_writew(width-1, HD64461_BBTDWR);
+ fb_writew(height-1, HD64461_BBTDHR);
fb_writew(saddr & 0xffff, HD64461_BBTDSARL);
fb_writew(saddr >> 16, HD64461_BBTDSARH);
}
-static inline void hitfb_accel_solidfill(int truecolor, u16 dx, u16 dy,
- u16 width, u16 height, u16 color)
-{
- hitfb_accel_set_dest(truecolor, dx, dy, width, height);
-
- fb_writew(0x00f0, HD64461_BBTROPR);
- fb_writew(16, HD64461_BBTMDR);
- fb_writew(color, HD64461_GRSCR);
-
- hitfb_accel_start(truecolor);
-}
-
static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,
u16 dy, u16 width, u16 height, u16 rop,
u32 mask_addr)
@@ -100,6 +85,8 @@ static inline void hitfb_accel_bitblt(int truecolor, u16 sx, u16 sy, u16 dx,
u32 saddr, daddr;
u32 maddr = 0;
+ height--;
+ width--;
fb_writew(rop, HD64461_BBTROPR);
if ((sy < dy) || ((sy == dy) && (sx <= dx))) {
saddr = WIDTH * (sy + height) + sx + width;
@@ -146,6 +133,7 @@ static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
if (rect->rop != ROP_COPY)
cfb_fillrect(p, rect);
else {
+ hitfb_accel_wait();
fb_writew(0x00f0, HD64461_BBTROPR);
fb_writew(16, HD64461_BBTMDR);
@@ -161,16 +149,15 @@ static void hitfb_fillrect(struct fb_info *p, const struct fb_fillrect *rect)
rect->height);
hitfb_accel_start(0);
}
- hitfb_accel_wait();
}
}
static void hitfb_copyarea(struct fb_info *p, const struct fb_copyarea *area)
{
+ hitfb_accel_wait();
hitfb_accel_bitblt(p->var.bits_per_pixel == 16, area->sx, area->sy,
area->dx, area->dy, area->width, area->height,
0x00cc, 0);
- hitfb_accel_wait();
}
static int hitfb_pan_display(struct fb_var_screeninfo *var,
@@ -182,7 +169,7 @@ static int hitfb_pan_display(struct fb_var_screeninfo *var,
if (xoffset != 0)
return -EINVAL;
- fb_writew(yoffset, HD64461_LCDCBAR);
+ fb_writew((yoffset*info->fix.line_length)>>10, HD64461_LCDCBAR);
return 0;
}
@@ -192,12 +179,6 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
unsigned short v;
if (blank_mode) {
-#ifdef MACH_HP600
- sh_dac_disable(DAC_LCD_BRIGHTNESS);
- v = fb_readw(HD64461_GPBDR);
- v |= HD64461_GPBDR_LCDOFF;
- fb_writew(v, HD64461_GPBDR);
-#endif
v = fb_readw(HD64461_LDR1);
v &= ~HD64461_LDR1_DON;
fb_writew(v, HD64461_LDR1);
@@ -213,19 +194,18 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
v = fb_readw(HD64461_STBCR);
v &= ~HD64461_STBCR_SLCDST;
fb_writew(v, HD64461_STBCR);
-#ifdef MACH_HP600
- sh_dac_enable(DAC_LCD_BRIGHTNESS);
- v = fb_readw(HD64461_GPBDR);
- v &= ~HD64461_GPBDR_LCDOFF;
- fb_writew(v, HD64461_GPBDR);
-#endif
- v = fb_readw(HD64461_LDR1);
- v |= HD64461_LDR1_DON;
- fb_writew(v, HD64461_LDR1);
v = fb_readw(HD64461_LCDCCR);
- v &= ~HD64461_LCDCCR_MOFF;
+ v &= ~(HD64461_LCDCCR_MOFF | HD64461_LCDCCR_STREQ);
fb_writew(v, HD64461_LCDCCR);
+
+ do {
+ v = fb_readw(HD64461_LCDCCR);
+ } while(v&HD64461_LCDCCR_STBACK);
+
+ v = fb_readw(HD64461_LDR1);
+ v |= HD64461_LDR1_DON;
+ fb_writew(v, HD64461_LDR1);
}
return 0;
}
@@ -233,7 +213,7 @@ int hitfb_blank(int blank_mode, struct fb_info *info)
static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
unsigned blue, unsigned transp, struct fb_info *info)
{
- if (regno >= info->cmap.len)
+ if (regno >= 256)
return 1;
switch (info->var.bits_per_pixel) {
@@ -244,6 +224,8 @@ static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
fb_writew(blue >> 10, HD64461_CPTWDR);
break;
case 16:
+ if (regno >= 16)
+ return 1;
((u32 *) (info->pseudo_palette))[regno] =
((red & 0xf800)) |
((green & 0xfc00) >> 5) | ((blue & 0xf800) >> 11);
@@ -252,26 +234,113 @@ static int hitfb_setcolreg(unsigned regno, unsigned red, unsigned green,
return 0;
}
+static int hitfb_sync(struct fb_info *info)
+{
+ hitfb_accel_wait();
+
+ return 0;
+}
+
+static int hitfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
+{
+ int maxy;
+
+ var->xres = info->var.xres;
+ var->xres_virtual = info->var.xres;
+ var->yres = info->var.yres;
+
+ if ((var->bits_per_pixel != 8) && (var->bits_per_pixel != 16))
+ var->bits_per_pixel = info->var.bits_per_pixel;
+
+ if (var->yres_virtual < var->yres)
+ var->yres_virtual = var->yres;
+
+ maxy = info->fix.smem_len / var->xres;
+
+ if (var->bits_per_pixel == 16)
+ maxy /= 2;
+
+ if (var->yres_virtual > maxy)
+ var->yres_virtual = maxy;
+
+ var->xoffset = 0;
+ var->yoffset = 0;
+
+ switch (var->bits_per_pixel) {
+ case 8:
+ var->red.offset = 0;
+ var->red.length = 8;
+ var->green.offset = 0;
+ var->green.length = 8;
+ var->blue.offset = 0;
+ var->blue.length = 8;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ case 16: /* RGB 565 */
+ var->red.offset = 11;
+ var->red.length = 5;
+ var->green.offset = 5;
+ var->green.length = 6;
+ var->blue.offset = 0;
+ var->blue.length = 5;
+ var->transp.offset = 0;
+ var->transp.length = 0;
+ break;
+ }
+
+ return 0;
+}
+
+static int hitfb_set_par(struct fb_info *info)
+{
+ unsigned short ldr3;
+
+ switch (info->var.bits_per_pixel) {
+ case 8:
+ info->fix.line_length = info->var.xres;
+ info->fix.visual = FB_VISUAL_PSEUDOCOLOR;
+ info->fix.ypanstep = 16;
+ break;
+ case 16:
+ info->fix.line_length = info->var.xres*2;
+ info->fix.visual = FB_VISUAL_TRUECOLOR;
+ info->fix.ypanstep = 8;
+ break;
+ }
+
+ fb_writew(info->fix.line_length, HD64461_LCDCLOR);
+ ldr3 = fb_readw(HD64461_LDR3);
+ ldr3 &= ~15;
+ ldr3 |= (info->var.bits_per_pixel == 8) ? 4 : 8;
+ fb_writew(ldr3, HD64461_LDR3);
+ return 0;
+}
+
static struct fb_ops hitfb_ops = {
.owner = THIS_MODULE,
+ .fb_check_var = hitfb_check_var,
+ .fb_set_par = hitfb_set_par,
.fb_setcolreg = hitfb_setcolreg,
.fb_blank = hitfb_blank,
+ .fb_sync = hitfb_sync,
.fb_pan_display = hitfb_pan_display,
.fb_fillrect = hitfb_fillrect,
.fb_copyarea = hitfb_copyarea,
.fb_imageblit = cfb_imageblit,
};
-int __init hitfb_init(void)
+static int __init hitfb_probe(struct platform_device *dev)
{
unsigned short lcdclor, ldr3, ldvndr;
- int size;
if (fb_get_options("hitfb", NULL))
return -ENODEV;
+ hitfb_fix.mmio_start = CONFIG_HD64461_IOBASE+0x1000;
+ hitfb_fix.mmio_len = 0x1000;
hitfb_fix.smem_start = CONFIG_HD64461_IOBASE + 0x02000000;
- hitfb_fix.smem_len = (MACH_HP690) ? 1024 * 1024 : 512 * 1024;
+ hitfb_fix.smem_len = 512 * 1024;
lcdclor = fb_readw(HD64461_LCDCLOR);
ldvndr = fb_readw(HD64461_LDVNDR);
@@ -321,12 +390,12 @@ int __init hitfb_init(void)
fb_info.var = hitfb_var;
fb_info.fix = hitfb_fix;
fb_info.pseudo_palette = pseudo_palette;
- fb_info.flags = FBINFO_DEFAULT;
+ fb_info.flags = FBINFO_DEFAULT | FBINFO_HWACCEL_YPAN |
+ FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_COPYAREA;
fb_info.screen_base = (void *)hitfb_fix.smem_start;
- size = (fb_info.var.bits_per_pixel == 8) ? 256 : 16;
- fb_alloc_cmap(&fb_info.cmap, size, 0);
+ fb_alloc_cmap(&fb_info.cmap, 256, 0);
if (register_framebuffer(&fb_info) < 0)
return -EINVAL;
@@ -336,9 +405,75 @@ int __init hitfb_init(void)
return 0;
}
+static int __devexit hitfb_remove(struct platform_device *dev)
+{
+ return unregister_framebuffer(&fb_info);
+}
+
+#ifdef CONFIG_PM
+static int hitfb_suspend(struct platform_device *dev, pm_message_t state)
+{
+ u16 v;
+
+ hitfb_blank(1,0);
+ v = fb_readw(HD64461_STBCR);
+ v |= HD64461_STBCR_SLCKE_IST;
+ fb_writew(v, HD64461_STBCR);
+
+ return 0;
+}
+
+static int hitfb_resume(struct platform_device *dev)
+{
+ u16 v;
+
+ v = fb_readw(HD64461_STBCR);
+ v &= ~HD64461_STBCR_SLCKE_OST;
+ msleep(100);
+ v = fb_readw(HD64461_STBCR);
+ v &= ~HD64461_STBCR_SLCKE_IST;
+ fb_writew(v, HD64461_STBCR);
+ hitfb_blank(0,0);
+
+ return 0;
+}
+#endif
+
+static struct platform_driver hitfb_driver = {
+ .probe = hitfb_probe,
+ .remove = __devexit_p(hitfb_remove),
+#ifdef CONFIG_PM
+ .suspend = hitfb_suspend,
+ .resume = hitfb_resume,
+#endif
+ .driver = {
+ .name = "hitfb",
+ },
+};
+
+static struct platform_device hitfb_device = {
+ .name = "hitfb",
+ .id = -1,
+};
+
+static int __init hitfb_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&hitfb_driver);
+ if (!ret) {
+ ret = platform_device_register(&hitfb_device);
+ if (ret)
+ platform_driver_unregister(&hitfb_driver);
+ }
+ return ret;
+}
+
+
static void __exit hitfb_exit(void)
{
- unregister_framebuffer(&fb_info);
+ platform_device_unregister(&hitfb_device);
+ platform_driver_unregister(&hitfb_driver);
}
module_init(hitfb_init);
diff --git a/drivers/video/i810/i810-i2c.c b/drivers/video/i810/i810-i2c.c
index c1f7b49975d..b38d805db31 100644
--- a/drivers/video/i810/i810-i2c.c
+++ b/drivers/video/i810/i810-i2c.c
@@ -19,7 +19,6 @@
#include "i810_main.h"
#include "../edid.h"
-#define I810_DDC 0x50
/* bit locations in the registers */
#define SCL_DIR_MASK 0x0001
#define SCL_DIR 0x0002
@@ -98,7 +97,6 @@ static int i810_setup_i2c_bus(struct i810fb_i2c_chan *chan, const char *name)
chan->algo.getsda = i810i2c_getsda;
chan->algo.getscl = i810i2c_getscl;
chan->algo.udelay = 10;
- chan->algo.mdelay = 10;
chan->algo.timeout = (HZ/2);
chan->algo.data = chan;
@@ -151,53 +149,14 @@ void i810_delete_i2c_busses(struct i810fb_par *par)
par->chan[2].par = NULL;
}
-static u8 *i810_do_probe_i2c_edid(struct i810fb_i2c_chan *chan)
-{
- u8 start = 0x0;
- struct i2c_msg msgs[] = {
- {
- .addr = I810_DDC,
- .len = 1,
- .buf = &start,
- }, {
- .addr = I810_DDC,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- },
- };
- u8 *buf;
-
- buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (!buf) {
- DPRINTK("i810-i2c: Failed to allocate memory\n");
- return NULL;
- }
- msgs[1].buf = buf;
-
- if (i2c_transfer(&chan->adapter, msgs, 2) == 2) {
- DPRINTK("i810-i2c: I2C Transfer successful\n");
- return buf;
- }
-
- DPRINTK("i810-i2c: Unable to read EDID block.\n");
- kfree(buf);
- return NULL;
-}
-
int i810_probe_i2c_connector(struct fb_info *info, u8 **out_edid, int conn)
{
struct i810fb_par *par = info->par;
u8 *edid = NULL;
- int i;
DPRINTK("i810-i2c: Probe DDC%i Bus\n", conn+1);
if (conn < par->ddc_num) {
- for (i = 0; i < 3; i++) {
- /* Do the real work */
- edid = i810_do_probe_i2c_edid(&par->chan[conn]);
- if (edid)
- break;
- }
+ edid = fb_ddc_read(&par->chan[conn].adapter);
} else {
const u8 *e = fb_firmware_edid(info->device);
diff --git a/drivers/video/i810/i810_main.c b/drivers/video/i810/i810_main.c
index a6ca02f2156..b55a12d95eb 100644
--- a/drivers/video/i810/i810_main.c
+++ b/drivers/video/i810/i810_main.c
@@ -1554,15 +1554,17 @@ static struct fb_ops i810fb_ops __devinitdata = {
/***********************************************************************
* Power Management *
***********************************************************************/
-static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
+static int i810fb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct i810fb_par *par = info->par;
- par->cur_state = state.event;
+ par->cur_state = mesg.event;
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
+ switch (mesg.event) {
+ case PM_EVENT_FREEZE:
+ case PM_EVENT_PRETHAW:
+ dev->dev.power.power_state = mesg;
return 0;
}
@@ -1578,7 +1580,7 @@ static int i810fb_suspend(struct pci_dev *dev, pm_message_t state)
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;
@@ -1600,7 +1602,10 @@ static int i810fb_resume(struct pci_dev *dev)
acquire_console_sem();
pci_set_power_state(dev, PCI_D0);
pci_restore_state(dev);
- pci_enable_device(dev);
+
+ if (pci_enable_device(dev))
+ goto fail;
+
pci_set_master(dev);
agp_bind_memory(par->i810_gtt.i810_fb_memory,
par->fb.offset);
@@ -1609,6 +1614,7 @@ static int i810fb_resume(struct pci_dev *dev)
i810fb_set_par(info);
fb_set_suspend (info, 0);
info->fbops->fb_blank(VESA_NO_BLANKING, info);
+fail:
release_console_sem();
return 0;
}
diff --git a/drivers/video/intelfb/Makefile b/drivers/video/intelfb/Makefile
index 722d21d6e5c..6c782d3ae1b 100644
--- a/drivers/video/intelfb/Makefile
+++ b/drivers/video/intelfb/Makefile
@@ -1,6 +1,8 @@
obj-$(CONFIG_FB_INTEL) += intelfb.o
-intelfb-objs := intelfbdrv.o intelfbhw.o
+intelfb-y := intelfbdrv.o intelfbhw.o
+intelfb-$(CONFIG_FB_INTEL_I2C) += intelfb_i2c.o
+intelfb-objs := $(intelfb-y)
ifdef CONFIG_FB_INTEL_DEBUG
#EXTRA_CFLAGS += -DDEBUG -DVERBOSE -DREGDUMP
diff --git a/drivers/video/intelfb/intelfb.h b/drivers/video/intelfb/intelfb.h
index e290d7460e1..80b94c19a9f 100644
--- a/drivers/video/intelfb/intelfb.h
+++ b/drivers/video/intelfb/intelfb.h
@@ -6,6 +6,10 @@
#include <linux/agp_backend.h>
#include <linux/fb.h>
+#ifdef CONFIG_FB_INTEL_I2C
+#include <linux/i2c.h>
+#include <linux/i2c-algo-bit.h>
+#endif
/*** Version/name ***/
#define INTELFB_VERSION "0.9.4"
@@ -115,6 +119,29 @@
/* Intel agpgart driver */
#define AGP_PHYSICAL_MEMORY 2
+/* store information about an Ixxx DVO */
+/* The i830->i865 use multiple DVOs with multiple i2cs */
+/* the i915, i945 have a single sDVO i2c bus - which is different */
+#define MAX_OUTPUTS 6
+
+/* these are outputs from the chip - integrated only
+ external chips are via DVO or SDVO output */
+#define INTELFB_OUTPUT_UNUSED 0
+#define INTELFB_OUTPUT_ANALOG 1
+#define INTELFB_OUTPUT_DVO 2
+#define INTELFB_OUTPUT_SDVO 3
+#define INTELFB_OUTPUT_LVDS 4
+#define INTELFB_OUTPUT_TVOUT 5
+
+#define INTELFB_DVO_CHIP_NONE 0
+#define INTELFB_DVO_CHIP_LVDS 1
+#define INTELFB_DVO_CHIP_TMDS 2
+#define INTELFB_DVO_CHIP_TVOUT 4
+
+#define INTELFB_OUTPUT_PIPE_NC 0
+#define INTELFB_OUTPUT_PIPE_A 1
+#define INTELFB_OUTPUT_PIPE_B 2
+
/*** Data Types ***/
/* supported chipsets */
@@ -195,6 +222,10 @@ struct intelfb_hwstate {
u32 mem_mode;
u32 fw_blc_0;
u32 fw_blc_1;
+ u16 hwstam;
+ u16 ier;
+ u16 iir;
+ u16 imr;
};
struct intelfb_heap_data {
@@ -204,6 +235,33 @@ struct intelfb_heap_data {
u32 size; // in bytes
};
+#ifdef CONFIG_FB_INTEL_I2C
+struct intelfb_i2c_chan {
+ struct intelfb_info *dinfo;
+ u32 reg;
+ struct i2c_adapter adapter;
+ struct i2c_algo_bit_data algo;
+};
+#endif
+
+struct intelfb_output_rec {
+ int type;
+ int pipe;
+ int flags;
+
+#ifdef CONFIG_FB_INTEL_I2C
+ struct intelfb_i2c_chan i2c_bus;
+ struct intelfb_i2c_chan ddc_bus;
+#endif
+};
+
+struct intelfb_vsync {
+ wait_queue_head_t wait;
+ unsigned int count;
+ int pan_display;
+ u32 pan_offset;
+};
+
struct intelfb_info {
struct fb_info *info;
struct fb_ops *fbops;
@@ -220,7 +278,7 @@ struct intelfb_info {
u8 fbmem_gart;
/* mtrr support */
- u32 mtrr_reg;
+ int mtrr_reg;
u32 has_mtrr;
/* heap data */
@@ -267,6 +325,12 @@ struct intelfb_info {
int fixed_mode;
int ring_active;
int flag;
+ unsigned long irq_flags;
+ int open;
+
+ /* vsync */
+ struct intelfb_vsync vsync;
+ spinlock_t int_lock;
/* hw cursor */
int cursor_on;
@@ -285,12 +349,25 @@ struct intelfb_info {
/* index into plls */
int pll_index;
+
+ /* outputs */
+ int num_outputs;
+ struct intelfb_output_rec output[MAX_OUTPUTS];
};
#define IS_I9XX(dinfo) (((dinfo)->chipset == INTEL_915G)||(dinfo->chipset == INTEL_915GM)||((dinfo)->chipset == INTEL_945G)||(dinfo->chipset==INTEL_945GM))
+#ifndef FBIO_WAITFORVSYNC
+#define FBIO_WAITFORVSYNC _IOW('F', 0x20, __u32)
+#endif
+
/*** function prototypes ***/
extern int intelfb_var_to_depth(const struct fb_var_screeninfo *var);
+#ifdef CONFIG_FB_INTEL_I2C
+extern void intelfb_create_i2c_busses(struct intelfb_info *dinfo);
+extern void intelfb_delete_i2c_busses(struct intelfb_info *dinfo);
+#endif
+
#endif /* _INTELFB_H */
diff --git a/drivers/video/intelfb/intelfb_i2c.c b/drivers/video/intelfb/intelfb_i2c.c
new file mode 100644
index 00000000000..c1113d6e941
--- /dev/null
+++ b/drivers/video/intelfb/intelfb_i2c.c
@@ -0,0 +1,200 @@
+/**************************************************************************
+
+ Copyright 2006 Dave Airlie <airlied@linux.ie>
+
+All Rights Reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the "Software"),
+to deal in the Software without restriction, including without limitation
+on the rights to use, copy, modify, merge, publish, distribute, sub
+license, and/or sell copies of the Software, and to permit persons to whom
+the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice (including the next
+paragraph) shall be included in all copies or substantial portions of the
+Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+THE COPYRIGHT HOLDERS AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+**************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fb.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-id.h>
+#include <linux/i2c-algo-bit.h>
+
+#include <asm/io.h>
+
+#include "intelfb.h"
+#include "intelfbhw.h"
+
+/* bit locations in the registers */
+#define SCL_DIR_MASK 0x0001
+#define SCL_DIR 0x0002
+#define SCL_VAL_MASK 0x0004
+#define SCL_VAL_OUT 0x0008
+#define SCL_VAL_IN 0x0010
+#define SDA_DIR_MASK 0x0100
+#define SDA_DIR 0x0200
+#define SDA_VAL_MASK 0x0400
+#define SDA_VAL_OUT 0x0800
+#define SDA_VAL_IN 0x1000
+
+static void intelfb_gpio_setscl(void *data, int state)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, (state ? SCL_VAL_OUT : 0) | SCL_DIR | SCL_DIR_MASK | SCL_VAL_MASK);
+ val = INREG(chan->reg);
+}
+
+static void intelfb_gpio_setsda(void *data, int state)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, (state ? SDA_VAL_OUT : 0) | SDA_DIR | SDA_DIR_MASK | SDA_VAL_MASK);
+ val = INREG(chan->reg);
+}
+
+static int intelfb_gpio_getscl(void *data)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, SCL_DIR_MASK);
+ OUTREG(chan->reg, 0);
+ val = INREG(chan->reg);
+ return ((val & SCL_VAL_IN) != 0);
+}
+
+static int intelfb_gpio_getsda(void *data)
+{
+ struct intelfb_i2c_chan *chan = data;
+ struct intelfb_info *dinfo = chan->dinfo;
+ u32 val;
+
+ OUTREG(chan->reg, SDA_DIR_MASK);
+ OUTREG(chan->reg, 0);
+ val = INREG(chan->reg);
+ return ((val & SDA_VAL_IN) != 0);
+}
+
+static int intelfb_setup_i2c_bus(struct intelfb_info *dinfo,
+ struct intelfb_i2c_chan *chan,
+ const u32 reg, const char *name)
+{
+ int rc;
+
+ chan->dinfo = dinfo;
+ chan->reg = reg;
+ snprintf(chan->adapter.name, I2C_NAME_SIZE, "intelfb %s", name);
+ chan->adapter.owner = THIS_MODULE;
+ chan->adapter.id = I2C_HW_B_INTELFB;
+ chan->adapter.algo_data = &chan->algo;
+ chan->adapter.dev.parent = &chan->dinfo->pdev->dev;
+ chan->algo.setsda = intelfb_gpio_setsda;
+ chan->algo.setscl = intelfb_gpio_setscl;
+ chan->algo.getsda = intelfb_gpio_getsda;
+ chan->algo.getscl = intelfb_gpio_getscl;
+ chan->algo.udelay = 40;
+ chan->algo.timeout = 20;
+ chan->algo.data = chan;
+
+ i2c_set_adapdata(&chan->adapter, chan);
+
+ /* Raise SCL and SDA */
+ intelfb_gpio_setsda(chan, 1);
+ intelfb_gpio_setscl(chan, 1);
+ udelay(20);
+
+ rc = i2c_bit_add_bus(&chan->adapter);
+ if (rc == 0)
+ DBG_MSG("I2C bus %s registered.\n", name);
+ else
+ WRN_MSG("Failed to register I2C bus %s.\n", name);
+ return rc;
+}
+
+void intelfb_create_i2c_busses(struct intelfb_info *dinfo)
+{
+ int i = 0;
+
+ /* everyone has at least a single analog output */
+ dinfo->num_outputs = 1;
+ dinfo->output[i].type = INTELFB_OUTPUT_ANALOG;
+
+ /* setup the DDC bus for analog output */
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOA, "CRTDDC_A");
+ i++;
+
+ /* need to add the output busses for each device
+ - this function is very incomplete
+ - i915GM has LVDS and TVOUT for example
+ */
+ switch(dinfo->chipset) {
+ case INTEL_830M:
+ case INTEL_845G:
+ case INTEL_855GM:
+ case INTEL_865G:
+ dinfo->output[i].type = INTELFB_OUTPUT_DVO;
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].ddc_bus, GPIOD, "DVODDC_D");
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "DVOI2C_E");
+ i++;
+ break;
+ case INTEL_915G:
+ case INTEL_915GM:
+ /* has some LVDS + tv-out */
+ case INTEL_945G:
+ case INTEL_945GM:
+ /* SDVO ports have a single control bus - 2 devices */
+ dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+ intelfb_setup_i2c_bus(dinfo, &dinfo->output[i].i2c_bus, GPIOE, "SDVOCTRL_E");
+ /* TODO: initialize the SDVO */
+// I830SDVOInit(pScrn, i, DVOB);
+ i++;
+
+ /* set up SDVOC */
+ dinfo->output[i].type = INTELFB_OUTPUT_SDVO;
+ dinfo->output[i].i2c_bus = dinfo->output[i - 1].i2c_bus;
+ /* TODO: initialize the SDVO */
+// I830SDVOInit(pScrn, i, DVOC);
+ i++;
+ break;
+ }
+ dinfo->num_outputs = i;
+}
+
+void intelfb_delete_i2c_busses(struct intelfb_info *dinfo)
+{
+ int i;
+
+ for (i = 0; i < MAX_OUTPUTS; i++) {
+ if (dinfo->output[i].i2c_bus.dinfo) {
+ i2c_bit_del_bus(&dinfo->output[i].i2c_bus.adapter);
+ dinfo->output[i].i2c_bus.dinfo = NULL;
+ }
+ if (dinfo->output[i].ddc_bus.dinfo) {
+ i2c_bit_del_bus(&dinfo->output[i].ddc_bus.adapter);
+ dinfo->output[i].ddc_bus.dinfo = NULL;
+ }
+ }
+}
diff --git a/drivers/video/intelfb/intelfbdrv.c b/drivers/video/intelfb/intelfbdrv.c
index 06af89d44a0..6f9de04193d 100644
--- a/drivers/video/intelfb/intelfbdrv.c
+++ b/drivers/video/intelfb/intelfbdrv.c
@@ -136,6 +136,8 @@
static void __devinit get_initial_mode(struct intelfb_info *dinfo);
static void update_dinfo(struct intelfb_info *dinfo,
struct fb_var_screeninfo *var);
+static int intelfb_open(struct fb_info *info, int user);
+static int intelfb_release(struct fb_info *info, int user);
static int intelfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info);
static int intelfb_set_par(struct fb_info *info);
@@ -194,6 +196,8 @@ static int num_registered = 0;
/* fb ops */
static struct fb_ops intel_fb_ops = {
.owner = THIS_MODULE,
+ .fb_open = intelfb_open,
+ .fb_release = intelfb_release,
.fb_check_var = intelfb_check_var,
.fb_set_par = intelfb_set_par,
.fb_setcolreg = intelfb_setcolreg,
@@ -446,6 +450,8 @@ cleanup(struct intelfb_info *dinfo)
if (!dinfo)
return;
+ intelfbhw_disable_irq(dinfo);
+
fb_dealloc_cmap(&dinfo->info->cmap);
kfree(dinfo->info->pixmap.addr);
@@ -467,6 +473,11 @@ cleanup(struct intelfb_info *dinfo)
agp_free_memory(dinfo->gtt_ring_mem);
}
+#ifdef CONFIG_FB_INTEL_I2C
+ /* un-register I2C bus */
+ intelfb_delete_i2c_busses(dinfo);
+#endif
+
if (dinfo->mmio_base)
iounmap((void __iomem *)dinfo->mmio_base);
if (dinfo->aperture.virtual)
@@ -844,6 +855,11 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
if (bailearly == 5)
bailout(dinfo);
+#ifdef CONFIG_FB_INTEL_I2C
+ /* register I2C bus */
+ intelfb_create_i2c_busses(dinfo);
+#endif
+
if (bailearly == 6)
bailout(dinfo);
@@ -888,6 +904,13 @@ intelfb_pci_register(struct pci_dev *pdev, const struct pci_device_id *ent)
}
dinfo->registered = 1;
+ dinfo->open = 0;
+
+ init_waitqueue_head(&dinfo->vsync.wait);
+ spin_lock_init(&dinfo->int_lock);
+ dinfo->irq_flags = 0;
+ dinfo->vsync.pan_display = 0;
+ dinfo->vsync.pan_offset = 0;
return 0;
@@ -1188,6 +1211,34 @@ update_dinfo(struct intelfb_info *dinfo, struct fb_var_screeninfo *var)
***************************************************************/
static int
+intelfb_open(struct fb_info *info, int user)
+{
+ struct intelfb_info *dinfo = GET_DINFO(info);
+
+ if (user) {
+ dinfo->open++;
+ }
+
+ return 0;
+}
+
+static int
+intelfb_release(struct fb_info *info, int user)
+{
+ struct intelfb_info *dinfo = GET_DINFO(info);
+
+ if (user) {
+ dinfo->open--;
+ msleep(1);
+ if (!dinfo->open) {
+ intelfbhw_disable_irq(dinfo);
+ }
+ }
+
+ return 0;
+}
+
+static int
intelfb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
{
int change_var = 0;
@@ -1433,6 +1484,19 @@ static int
intelfb_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg)
{
int retval = 0;
+ struct intelfb_info *dinfo = GET_DINFO(info);
+ u32 pipe = 0;
+
+ switch (cmd) {
+ case FBIO_WAITFORVSYNC:
+ if (get_user(pipe, (__u32 __user *)arg))
+ return -EFAULT;
+
+ retval = intelfbhw_wait_for_vsync(dinfo, pipe);
+ break;
+ default:
+ break;
+ }
return retval;
}
diff --git a/drivers/video/intelfb/intelfbhw.c b/drivers/video/intelfb/intelfbhw.c
index 2a9322f9cfd..f887f1efd3f 100644
--- a/drivers/video/intelfb/intelfbhw.c
+++ b/drivers/video/intelfb/intelfbhw.c
@@ -32,6 +32,7 @@
#include <linux/pci.h>
#include <linux/vmalloc.h>
#include <linux/pagemap.h>
+#include <linux/interrupt.h>
#include <asm/io.h>
@@ -368,7 +369,13 @@ intelfbhw_pan_display(struct fb_var_screeninfo *var, struct fb_info *info)
offset += dinfo->fb.offset << 12;
- OUTREG(DSPABASE, offset);
+ dinfo->vsync.pan_offset = offset;
+ if ((var->activate & FB_ACTIVATE_VBL) && !intelfbhw_enable_irq(dinfo, 0)) {
+ dinfo->vsync.pan_display = 1;
+ } else {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, offset);
+ }
return 0;
}
@@ -585,6 +592,11 @@ intelfbhw_read_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw,
hw->fw_blc_0 = INREG(FW_BLC_0);
hw->fw_blc_1 = INREG(FW_BLC_1);
+ hw->hwstam = INREG16(HWSTAM);
+ hw->ier = INREG16(IER);
+ hw->iir = INREG16(IIR);
+ hw->imr = INREG16(IMR);
+
return 0;
}
@@ -613,6 +625,7 @@ static int calc_vclock(int index, int m1, int m2, int n, int p1, int p2, int lvd
return vco / p;
}
+#if REGDUMP
static void
intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
{
@@ -638,6 +651,7 @@ intelfbhw_get_p1p2(struct intelfb_info *dinfo, int dpll, int *o_p1, int *o_p2)
*o_p1 = p1;
*o_p2 = p2;
}
+#endif
void
@@ -794,6 +808,10 @@ intelfbhw_print_hw_state(struct intelfb_info *dinfo, struct intelfb_hwstate *hw)
printk(" FW_BLC_0 0x%08x\n", hw->fw_blc_0);
printk(" FW_BLC_1 0x%08x\n", hw->fw_blc_1);
+ printk(" HWSTAM 0x%04x\n", hw->hwstam);
+ printk(" IER 0x%04x\n", hw->ier);
+ printk(" IIR 0x%04x\n", hw->iir);
+ printk(" IMR 0x%04x\n", hw->imr);
printk("hw state dump end\n");
#endif
}
@@ -1932,3 +1950,119 @@ intelfbhw_cursor_reset(struct intelfb_info *dinfo) {
addr += 16;
}
}
+
+static irqreturn_t
+intelfbhw_irq(int irq, void *dev_id, struct pt_regs *fp) {
+ int handled = 0;
+ u16 tmp;
+ struct intelfb_info *dinfo = (struct intelfb_info *)dev_id;
+
+ spin_lock(&dinfo->int_lock);
+
+ tmp = INREG16(IIR);
+ tmp &= VSYNC_PIPE_A_INTERRUPT;
+
+ if (tmp == 0) {
+ spin_unlock(&dinfo->int_lock);
+ return IRQ_RETVAL(handled);
+ }
+
+ OUTREG16(IIR, tmp);
+
+ if (tmp & VSYNC_PIPE_A_INTERRUPT) {
+ dinfo->vsync.count++;
+ if (dinfo->vsync.pan_display) {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+ }
+ wake_up_interruptible(&dinfo->vsync.wait);
+ handled = 1;
+ }
+
+ spin_unlock(&dinfo->int_lock);
+
+ return IRQ_RETVAL(handled);
+}
+
+int
+intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable) {
+
+ if (!test_and_set_bit(0, &dinfo->irq_flags)) {
+ if (request_irq(dinfo->pdev->irq, intelfbhw_irq, SA_SHIRQ, "intelfb", dinfo)) {
+ clear_bit(0, &dinfo->irq_flags);
+ return -EINVAL;
+ }
+
+ spin_lock_irq(&dinfo->int_lock);
+ OUTREG16(HWSTAM, 0xfffe);
+ OUTREG16(IMR, 0x0);
+ OUTREG16(IER, VSYNC_PIPE_A_INTERRUPT);
+ spin_unlock_irq(&dinfo->int_lock);
+ } else if (reenable) {
+ u16 ier;
+
+ spin_lock_irq(&dinfo->int_lock);
+ ier = INREG16(IER);
+ if ((ier & VSYNC_PIPE_A_INTERRUPT)) {
+ DBG_MSG("someone disabled the IRQ [%08X]\n", ier);
+ OUTREG(IER, VSYNC_PIPE_A_INTERRUPT);
+ }
+ spin_unlock_irq(&dinfo->int_lock);
+ }
+ return 0;
+}
+
+void
+intelfbhw_disable_irq(struct intelfb_info *dinfo) {
+ u16 tmp;
+
+ if (test_and_clear_bit(0, &dinfo->irq_flags)) {
+ if (dinfo->vsync.pan_display) {
+ dinfo->vsync.pan_display = 0;
+ OUTREG(DSPABASE, dinfo->vsync.pan_offset);
+ }
+ spin_lock_irq(&dinfo->int_lock);
+ OUTREG16(HWSTAM, 0xffff);
+ OUTREG16(IMR, 0xffff);
+ OUTREG16(IER, 0x0);
+
+ tmp = INREG16(IIR);
+ OUTREG16(IIR, tmp);
+ spin_unlock_irq(&dinfo->int_lock);
+
+ free_irq(dinfo->pdev->irq, dinfo);
+ }
+}
+
+int
+intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe) {
+ struct intelfb_vsync *vsync;
+ unsigned int count;
+ int ret;
+
+ switch (pipe) {
+ case 0:
+ vsync = &dinfo->vsync;
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ ret = intelfbhw_enable_irq(dinfo, 0);
+ if (ret) {
+ return ret;
+ }
+
+ count = vsync->count;
+ ret = wait_event_interruptible_timeout(vsync->wait, count != vsync->count, HZ/10);
+ if (ret < 0) {
+ return ret;
+ }
+ if (ret == 0) {
+ intelfbhw_enable_irq(dinfo, 1);
+ DBG_MSG("wait_for_vsync timed out!\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
diff --git a/drivers/video/intelfb/intelfbhw.h b/drivers/video/intelfb/intelfbhw.h
index 10acda098b7..8c54ba8fbdd 100644
--- a/drivers/video/intelfb/intelfbhw.h
+++ b/drivers/video/intelfb/intelfbhw.h
@@ -88,6 +88,19 @@
#define INSTDONE 0x2090
#define PRI_RING_EMPTY 1
+#define HWSTAM 0x2098
+#define IER 0x20A0
+#define IIR 0x20A4
+#define IMR 0x20A8
+#define VSYNC_PIPE_A_INTERRUPT (1 << 7)
+#define PIPE_A_EVENT_INTERRUPT (1 << 4)
+#define VSYNC_PIPE_B_INTERRUPT (1 << 5)
+#define PIPE_B_EVENT_INTERRUPT (1 << 4)
+#define HOST_PORT_EVENT_INTERRUPT (1 << 3)
+#define CAPTURE_EVENT_INTERRUPT (1 << 2)
+#define USER_DEFINED_INTERRUPT (1 << 1)
+#define BREAKPOINT_INTERRUPT 1
+
#define INSTPM 0x20c0
#define SYNC_FLUSH_ENABLE (1 << 5)
@@ -113,6 +126,12 @@
#define FW_DISPC_BL_SHIFT 8
#define FW_DISPC_BL_MASK 0x7
+#define GPIOA 0x5010
+#define GPIOB 0x5014
+#define GPIOC 0x5018 // this may be external DDC on i830
+#define GPIOD 0x501C // this is DVO DDC
+#define GPIOE 0x5020 // this is DVO i2C
+#define GPIOF 0x5024
/* PLL registers */
#define VGA0_DIVISOR 0x06000
@@ -468,9 +487,12 @@
/* I/O macros */
#define INREG8(addr) readb((u8 __iomem *)(dinfo->mmio_base + (addr)))
+#define INREG16(addr) readw((u16 __iomem *)(dinfo->mmio_base + (addr)))
#define INREG(addr) readl((u32 __iomem *)(dinfo->mmio_base + (addr)))
#define OUTREG8(addr, val) writeb((val),(u8 __iomem *)(dinfo->mmio_base + \
(addr)))
+#define OUTREG16(addr, val) writew((val),(u16 __iomem *)(dinfo->mmio_base + \
+ (addr)))
#define OUTREG(addr, val) writel((val),(u32 __iomem *)(dinfo->mmio_base + \
(addr)))
@@ -545,5 +567,8 @@ extern void intelfbhw_cursor_setcolor(struct intelfb_info *dinfo, u32 bg,
extern void intelfbhw_cursor_load(struct intelfb_info *dinfo, int width,
int height, u8 *data);
extern void intelfbhw_cursor_reset(struct intelfb_info *dinfo);
+extern int intelfbhw_enable_irq(struct intelfb_info *dinfo, int reenable);
+extern void intelfbhw_disable_irq(struct intelfb_info *dinfo);
+extern int intelfbhw_wait_for_vsync(struct intelfb_info *dinfo, u32 pipe);
#endif /* _INTELFBHW_H */
diff --git a/drivers/video/matrox/i2c-matroxfb.c b/drivers/video/matrox/i2c-matroxfb.c
index 57abbae5520..795c1a99a68 100644
--- a/drivers/video/matrox/i2c-matroxfb.c
+++ b/drivers/video/matrox/i2c-matroxfb.c
@@ -95,12 +95,12 @@ static struct i2c_adapter matrox_i2c_adapter_template =
static struct i2c_algo_bit_data matrox_i2c_algo_template =
{
- NULL,
- matroxfb_gpio_setsda,
- matroxfb_gpio_setscl,
- matroxfb_gpio_getsda,
- matroxfb_gpio_getscl,
- 10, 10, 100,
+ .setsda = matroxfb_gpio_setsda,
+ .setscl = matroxfb_gpio_setscl,
+ .getsda = matroxfb_gpio_getsda,
+ .getscl = matroxfb_gpio_getscl,
+ .udelay = 10,
+ .timeout = 100,
};
static int i2c_bus_reg(struct i2c_bit_adapter* b, struct matrox_fb_info* minfo,
diff --git a/drivers/video/matrox/matroxfb_base.c b/drivers/video/matrox/matroxfb_base.c
index 4a57dabb77d..7acf01c181e 100644
--- a/drivers/video/matrox/matroxfb_base.c
+++ b/drivers/video/matrox/matroxfb_base.c
@@ -2277,10 +2277,13 @@ static void __init matroxfb_init_params(void) {
}
}
-static void __init matrox_init(void) {
+static int __init matrox_init(void) {
+ int err;
+
matroxfb_init_params();
- pci_register_driver(&matroxfb_driver);
+ err = pci_register_driver(&matroxfb_driver);
dev = -1; /* accept all new devices... */
+ return err;
}
/* **************************** exit-time only **************************** */
@@ -2437,6 +2440,7 @@ static int __initdata initialized = 0;
static int __init matroxfb_init(void)
{
char *option = NULL;
+ int err = 0;
DBG(__FUNCTION__)
@@ -2448,11 +2452,11 @@ static int __init matroxfb_init(void)
return -ENXIO;
if (!initialized) {
initialized = 1;
- matrox_init();
+ err = matrox_init();
}
hotplug = 1;
/* never return failure, user can hotplug matrox later... */
- return 0;
+ return err;
}
module_init(matroxfb_init);
diff --git a/drivers/video/mbx/mbxfb.c b/drivers/video/mbx/mbxfb.c
index 6849ab75d40..a32d1af79e0 100644
--- a/drivers/video/mbx/mbxfb.c
+++ b/drivers/video/mbx/mbxfb.c
@@ -118,8 +118,19 @@ static unsigned int mbxfb_get_pixclock(unsigned int pixclock_ps,
/* convert pixclock to KHz */
pixclock = PICOS2KHZ(pixclock_ps);
+ /* PLL output freq = (ref_clk * M) / (N * 2^P)
+ *
+ * M: 1 to 63
+ * N: 1 to 7
+ * P: 0 to 7
+ */
+
+ /* RAPH: When N==1, the resulting pixel clock appears to
+ * get divided by 2. Preventing N=1 by starting the following
+ * loop at 2 prevents this. Is this a bug with my chip
+ * revision or something I dont understand? */
for (m = 1; m < 64; m++) {
- for (n = 1; n < 8; n++) {
+ for (n = 2; n < 8; n++) {
for (p = 0; p < 8; p++) {
clk = (ref_clk * m) / (n * (1 << p));
err = (clk > pixclock) ? (clk - pixclock) :
@@ -244,8 +255,8 @@ static int mbxfb_set_par(struct fb_info *info)
/* setup resolution */
gsctrl &= ~(FMsk(GSCTRL_GSWIDTH) | FMsk(GSCTRL_GSHEIGHT));
- gsctrl |= Gsctrl_Width(info->var.xres - 1) |
- Gsctrl_Height(info->var.yres - 1);
+ gsctrl |= Gsctrl_Width(info->var.xres) |
+ Gsctrl_Height(info->var.yres);
writel(gsctrl, GSCTRL);
udelay(1000);
@@ -402,8 +413,8 @@ static void __devinit setup_graphics(struct fb_info *fbi)
{
unsigned long gsctrl;
- gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres - 1) |
- Gsctrl_Height(fbi->var.yres - 1);
+ gsctrl = GSCTRL_GAMMA_EN | Gsctrl_Width(fbi->var.xres) |
+ Gsctrl_Height(fbi->var.yres);
switch (fbi->var.bits_per_pixel) {
case 16:
if (fbi->var.green.length == 5)
diff --git a/drivers/video/nvidia/nv_i2c.c b/drivers/video/nvidia/nv_i2c.c
index 19eef3a0902..e48de3c9fd1 100644
--- a/drivers/video/nvidia/nv_i2c.c
+++ b/drivers/video/nvidia/nv_i2c.c
@@ -160,51 +160,12 @@ void nvidia_delete_i2c_busses(struct nvidia_par *par)
}
-static u8 *nvidia_do_probe_i2c_edid(struct nvidia_i2c_chan *chan)
-{
- u8 start = 0x0;
- struct i2c_msg msgs[] = {
- {
- .addr = 0x50,
- .len = 1,
- .buf = &start,
- }, {
- .addr = 0x50,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- },
- };
- u8 *buf;
-
- if (!chan->par)
- return NULL;
-
- buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (!buf) {
- dev_warn(&chan->par->pci_dev->dev, "Out of memory!\n");
- return NULL;
- }
- msgs[1].buf = buf;
-
- if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
- return buf;
- dev_dbg(&chan->par->pci_dev->dev, "Unable to read EDID block.\n");
- kfree(buf);
- return NULL;
-}
-
int nvidia_probe_i2c_connector(struct fb_info *info, int conn, u8 **out_edid)
{
struct nvidia_par *par = info->par;
- u8 *edid = NULL;
- int i;
-
- for (i = 0; i < 3; i++) {
- /* Do the real work */
- edid = nvidia_do_probe_i2c_edid(&par->chan[conn - 1]);
- if (edid)
- break;
- }
+ u8 *edid;
+
+ edid = fb_ddc_read(&par->chan[conn - 1].adapter);
if (!edid && conn == 1) {
/* try to get from firmware */
diff --git a/drivers/video/nvidia/nvidia.c b/drivers/video/nvidia/nvidia.c
index d4f85011787..eb24107bcc8 100644
--- a/drivers/video/nvidia/nvidia.c
+++ b/drivers/video/nvidia/nvidia.c
@@ -28,6 +28,9 @@
#include <asm/prom.h>
#include <asm/pci-bridge.h>
#endif
+#ifdef CONFIG_BOOTX_TEXT
+#include <asm/btext.h>
+#endif
#include "nv_local.h"
#include "nv_type.h"
@@ -681,6 +684,13 @@ static int nvidiafb_set_par(struct fb_info *info)
nvidia_vga_protect(par, 0);
+#ifdef CONFIG_BOOTX_TEXT
+ /* Update debug text engine */
+ btext_update_display(info->fix.smem_start,
+ info->var.xres, info->var.yres,
+ info->var.bits_per_pixel, info->fix.line_length);
+#endif
+
NVTRACE_LEAVE();
return 0;
}
@@ -950,24 +960,25 @@ static struct fb_ops nvidia_fb_ops = {
};
#ifdef CONFIG_PM
-static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t state)
+static int nvidiafb_suspend(struct pci_dev *dev, pm_message_t mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct nvidia_par *par = info->par;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
acquire_console_sem();
- par->pm_state = state.event;
+ par->pm_state = mesg.event;
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
- } else {
+ if (mesg.event == PM_EVENT_SUSPEND) {
fb_set_suspend(info, 1);
nvidiafb_blank(FB_BLANK_POWERDOWN, info);
nvidia_write_regs(par, &par->SavedReg);
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
}
+ dev->dev.power.power_state = mesg;
release_console_sem();
return 0;
@@ -983,7 +994,10 @@ static int nvidiafb_resume(struct pci_dev *dev)
if (par->pm_state != PM_EVENT_FREEZE) {
pci_restore_state(dev);
- pci_enable_device(dev);
+
+ if (pci_enable_device(dev))
+ goto fail;
+
pci_set_master(dev);
}
@@ -992,6 +1006,7 @@ static int nvidiafb_resume(struct pci_dev *dev)
fb_set_suspend (info, 0);
nvidiafb_blank(FB_BLANK_UNBLANK, info);
+fail:
release_console_sem();
return 0;
}
diff --git a/drivers/video/pvr2fb.c b/drivers/video/pvr2fb.c
index 940ba2be55e..78dc59a1751 100644
--- a/drivers/video/pvr2fb.c
+++ b/drivers/video/pvr2fb.c
@@ -187,7 +187,7 @@ static short do_blank = 0; /* (Un)Blank the screen */
static unsigned int is_blanked = 0; /* Is the screen blanked? */
#ifdef CONFIG_SH_STORE_QUEUES
-static struct sq_mapping *pvr2fb_map;
+static unsigned long pvr2fb_map;
#endif
#ifdef CONFIG_SH_DMA
@@ -213,15 +213,17 @@ static irqreturn_t pvr2fb_interrupt(int irq, void *dev_id, struct pt_regs *fp);
static int pvr2_init_cable(void);
static int pvr2_get_param(const struct pvr2_params *p, const char *s,
int val, int size);
+#ifdef CONFIG_SH_DMA
static ssize_t pvr2fb_write(struct file *file, const char *buf,
size_t count, loff_t *ppos);
+#endif
static struct fb_ops pvr2fb_ops = {
- .owner = THIS_MODULE,
- .fb_setcolreg = pvr2fb_setcolreg,
- .fb_blank = pvr2fb_blank,
- .fb_check_var = pvr2fb_check_var,
- .fb_set_par = pvr2fb_set_par,
+ .owner = THIS_MODULE,
+ .fb_setcolreg = pvr2fb_setcolreg,
+ .fb_blank = pvr2fb_blank,
+ .fb_check_var = pvr2fb_check_var,
+ .fb_set_par = pvr2fb_set_par,
#ifdef CONFIG_SH_DMA
.fb_write = pvr2fb_write,
#endif
@@ -783,7 +785,7 @@ static int __init pvr2fb_common_init(void)
goto out_err;
}
- fb_memset((unsigned long)fb_info->screen_base, 0, pvr2_fix.smem_len);
+ fb_memset(fb_info->screen_base, 0, pvr2_fix.smem_len);
pvr2_fix.ypanstep = nopan ? 0 : 1;
pvr2_fix.ywrapstep = nowrap ? 0 : 1;
@@ -820,7 +822,7 @@ static int __init pvr2fb_common_init(void)
modememused >> 10, (unsigned long)(fb_info->fix.smem_len >> 10));
printk("fb%d: Mode %dx%d-%d pitch = %ld cable: %s video output: %s\n",
fb_info->node, fb_info->var.xres, fb_info->var.yres,
- fb_info->var.bits_per_pixel,
+ fb_info->var.bits_per_pixel,
get_line_length(fb_info->var.xres, fb_info->var.bits_per_pixel),
(char *)pvr2_get_param(cables, NULL, cable_type, 3),
(char *)pvr2_get_param(outputs, NULL, video_output, 3));
@@ -829,10 +831,10 @@ static int __init pvr2fb_common_init(void)
printk(KERN_NOTICE "fb%d: registering with SQ API\n", fb_info->node);
pvr2fb_map = sq_remap(fb_info->fix.smem_start, fb_info->fix.smem_len,
- fb_info->fix.id);
+ fb_info->fix.id, pgprot_val(PAGE_SHARED));
printk(KERN_NOTICE "fb%d: Mapped video memory to SQ addr 0x%lx\n",
- fb_info->node, pvr2fb_map->sq_addr);
+ fb_info->node, pvr2fb_map);
#endif
return 0;
diff --git a/drivers/video/riva/fbdev.c b/drivers/video/riva/fbdev.c
index 4acde4f7dbf..b120896c8ab 100644
--- a/drivers/video/riva/fbdev.c
+++ b/drivers/video/riva/fbdev.c
@@ -393,8 +393,8 @@ static void riva_bl_init(struct riva_par *par)
mutex_lock(&info->bl_mutex);
info->bl_dev = bd;
fb_bl_default_curve(info, 0,
- 0x158 * FB_BACKLIGHT_MAX / MAX_LEVEL,
- 0x534 * FB_BACKLIGHT_MAX / MAX_LEVEL);
+ MIN_LEVEL * FB_BACKLIGHT_MAX / MAX_LEVEL,
+ FB_BACKLIGHT_MAX);
mutex_unlock(&info->bl_mutex);
down(&bd->sem);
@@ -784,7 +784,7 @@ static void riva_load_video_mode(struct fb_info *info)
NVTRACE_ENTER();
/* time to calculate */
- rivafb_blank(1, info);
+ rivafb_blank(FB_BLANK_NORMAL, info);
bpp = info->var.bits_per_pixel;
if (bpp == 16 && info->var.green.length == 5)
@@ -917,7 +917,7 @@ static void riva_load_video_mode(struct fb_info *info)
par->current_state = newmode;
riva_load_state(par, &par->current_state);
par->riva.LockUnlock(&par->riva, 0); /* important for HW cursor */
- rivafb_blank(0, info);
+ rivafb_blank(FB_BLANK_UNBLANK, info);
NVTRACE_LEAVE();
}
diff --git a/drivers/video/riva/rivafb-i2c.c b/drivers/video/riva/rivafb-i2c.c
index 9751c37c0bf..c15b259af64 100644
--- a/drivers/video/riva/rivafb-i2c.c
+++ b/drivers/video/riva/rivafb-i2c.c
@@ -25,8 +25,6 @@
#include "rivafb.h"
#include "../edid.h"
-#define RIVA_DDC 0x50
-
static void riva_gpio_setscl(void* data, int state)
{
struct riva_i2c_chan *chan = data;
@@ -158,50 +156,12 @@ void riva_delete_i2c_busses(struct riva_par *par)
par->chan[2].par = NULL;
}
-static u8 *riva_do_probe_i2c_edid(struct riva_i2c_chan *chan)
-{
- u8 start = 0x0;
- struct i2c_msg msgs[] = {
- {
- .addr = RIVA_DDC,
- .len = 1,
- .buf = &start,
- }, {
- .addr = RIVA_DDC,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- },
- };
- u8 *buf;
-
- if (!chan->par)
- return NULL;
-
- buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
- if (!buf) {
- dev_warn(&chan->par->pdev->dev, "Out of memory!\n");
- return NULL;
- }
- msgs[1].buf = buf;
-
- if (i2c_transfer(&chan->adapter, msgs, 2) == 2)
- return buf;
- dev_dbg(&chan->par->pdev->dev, "Unable to read EDID block.\n");
- kfree(buf);
- return NULL;
-}
-
int riva_probe_i2c_connector(struct riva_par *par, int conn, u8 **out_edid)
{
u8 *edid = NULL;
- int i;
- for (i = 0; i < 3; i++) {
- /* Do the real work */
- edid = riva_do_probe_i2c_edid(&par->chan[conn-1]);
- if (edid)
- break;
- }
+ edid = fb_ddc_read(&par->chan[conn-1].adapter);
+
if (out_edid)
*out_edid = edid;
if (!edid)
diff --git a/drivers/video/savage/savagefb-i2c.c b/drivers/video/savage/savagefb-i2c.c
index e83befd16d6..3f94223b7f0 100644
--- a/drivers/video/savage/savagefb-i2c.c
+++ b/drivers/video/savage/savagefb-i2c.c
@@ -148,7 +148,6 @@ static int savage_setup_i2c_bus(struct savagefb_i2c_chan *chan,
chan->adapter.algo_data = &chan->algo;
chan->adapter.dev.parent = &chan->par->pcidev->dev;
chan->algo.udelay = 40;
- chan->algo.mdelay = 5;
chan->algo.timeout = 20;
chan->algo.data = chan;
@@ -214,52 +213,15 @@ void savagefb_delete_i2c_busses(struct fb_info *info)
par->chan.par = NULL;
}
-static u8 *savage_do_probe_i2c_edid(struct savagefb_i2c_chan *chan)
-{
- u8 start = 0x0;
- struct i2c_msg msgs[] = {
- {
- .addr = SAVAGE_DDC,
- .len = 1,
- .buf = &start,
- }, {
- .addr = SAVAGE_DDC,
- .flags = I2C_M_RD,
- .len = EDID_LENGTH,
- },
- };
- u8 *buf = NULL;
-
- if (chan->par) {
- buf = kmalloc(EDID_LENGTH, GFP_KERNEL);
-
- if (buf) {
- msgs[1].buf = buf;
-
- if (i2c_transfer(&chan->adapter, msgs, 2) != 2) {
- dev_dbg(&chan->par->pcidev->dev,
- "Unable to read EDID block.\n");
- kfree(buf);
- buf = NULL;
- }
- }
- }
-
- return buf;
-}
-
int savagefb_probe_i2c_connector(struct fb_info *info, u8 **out_edid)
{
struct savagefb_par *par = info->par;
- u8 *edid = NULL;
- int i;
-
- for (i = 0; i < 3; i++) {
- /* Do the real work */
- edid = savage_do_probe_i2c_edid(&par->chan);
- if (edid)
- break;
- }
+ u8 *edid;
+
+ if (par->chan.par)
+ edid = fb_ddc_read(&par->chan.adapter);
+ else
+ edid = NULL;
if (!edid) {
/* try to get from firmware */
diff --git a/drivers/video/savage/savagefb_driver.c b/drivers/video/savage/savagefb_driver.c
index 461e094e7b4..82b3deaae02 100644
--- a/drivers/video/savage/savagefb_driver.c
+++ b/drivers/video/savage/savagefb_driver.c
@@ -2323,24 +2323,24 @@ 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 mesg)
{
struct fb_info *info = pci_get_drvdata(dev);
struct savagefb_par *par = info->par;
DBG("savagefb_suspend");
-
- par->pm_state = state.event;
+ if (mesg.event == PM_EVENT_PRETHAW)
+ mesg.event = PM_EVENT_FREEZE;
+ par->pm_state = mesg.event;
+ dev->dev.power.power_state = mesg;
/*
* For PM_EVENT_FREEZE, do not power down so the console
* can remain active.
*/
- if (state.event == PM_EVENT_FREEZE) {
- dev->dev.power.power_state = state;
+ if (mesg.event == PM_EVENT_FREEZE)
return 0;
- }
acquire_console_sem();
fb_set_suspend(info, 1);
@@ -2353,7 +2353,7 @@ static int savagefb_suspend(struct pci_dev* dev, pm_message_t state)
savage_disable_mmio(par);
pci_save_state(dev);
pci_disable_device(dev);
- pci_set_power_state(dev, pci_choose_state(dev, state));
+ pci_set_power_state(dev, pci_choose_state(dev, mesg));
release_console_sem();
return 0;
diff --git a/drivers/video/sis/init.h b/drivers/video/sis/init.h
index 7ecab87cef0..59d12844b4d 100644
--- a/drivers/video/sis/init.h
+++ b/drivers/video/sis/init.h
@@ -77,16 +77,9 @@
#include <linux/types.h>
#include <asm/io.h>
#include <linux/fb.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <video/fbcon.h>
-#endif
#include "sis.h"
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <linux/sisfb.h>
-#else
#include <video/sisfb.h>
#endif
-#endif
/* Mode numbers */
static const unsigned short ModeIndex_320x200[] = {0x59, 0x41, 0x00, 0x4f};
diff --git a/drivers/video/sis/init301.h b/drivers/video/sis/init301.h
index bc321dc57e9..4f3a28699d3 100644
--- a/drivers/video/sis/init301.h
+++ b/drivers/video/sis/init301.h
@@ -71,16 +71,9 @@
#include <linux/types.h>
#include <asm/io.h>
#include <linux/fb.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <video/fbcon.h>
-#endif
#include "sis.h"
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <linux/sisfb.h>
-#else
#include <video/sisfb.h>
#endif
-#endif
static const unsigned char SiS_YPbPrTable[3][64] = {
{
diff --git a/drivers/video/sis/initextlfb.c b/drivers/video/sis/initextlfb.c
index 09f5d758b6c..c3884a29f4c 100644
--- a/drivers/video/sis/initextlfb.c
+++ b/drivers/video/sis/initextlfb.c
@@ -34,12 +34,10 @@
#include <linux/types.h>
#include <linux/fb.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
int sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr,
unsigned char modeno, unsigned char rateindex);
int sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno,
unsigned char rateindex, struct fb_var_screeninfo *var);
-#endif
BOOLEAN sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno,
int *htotal, int *vtotal, unsigned char rateindex);
@@ -49,7 +47,6 @@ extern BOOLEAN SiS_SearchModeID(struct SiS_Private *SiS_Pr, unsigned short *Mode
extern void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata,
int xres, int yres, struct fb_var_screeninfo *var, BOOLEAN writeres);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
int
sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr, unsigned char modeno,
unsigned char rateindex)
@@ -177,7 +174,6 @@ sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno,
return 1;
}
-#endif /* Linux >= 2.5 */
BOOLEAN
sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno, int *htotal,
diff --git a/drivers/video/sis/osdef.h b/drivers/video/sis/osdef.h
index f59568020eb..d048bd39961 100644
--- a/drivers/video/sis/osdef.h
+++ b/drivers/video/sis/osdef.h
@@ -100,11 +100,7 @@
#define SIS315H
#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
#define SIS_LINUX_KERNEL_26
-#else
-#define SIS_LINUX_KERNEL_24
-#endif
#if !defined(SIS300) && !defined(SIS315H)
#warning Neither CONFIG_FB_SIS_300 nor CONFIG_FB_SIS_315 is set
diff --git a/drivers/video/sis/sis_accel.c b/drivers/video/sis/sis_accel.c
index 3b7ce032e2e..7addf91d2fe 100644
--- a/drivers/video/sis/sis_accel.c
+++ b/drivers/video/sis/sis_accel.c
@@ -32,22 +32,10 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fb.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <linux/console.h>
-#endif
#include <linux/ioport.h>
#include <linux/types.h>
-
#include <asm/io.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <video/fbcon.h>
-#include <video/fbcon-cfb8.h>
-#include <video/fbcon-cfb16.h>
-#include <video/fbcon-cfb24.h>
-#include <video/fbcon-cfb32.h>
-#endif
-
#include "sis.h"
#include "sis_accel.h"
@@ -91,11 +79,9 @@ static const u8 sisPatALUConv[] =
0xFF, /* dest = 0xFF; 1, GXset, 0xF */
};
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34)
static const int myrops[] = {
3, 10, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3
};
-#endif
/* 300 series ----------------------------------------------------- */
#ifdef CONFIG_FB_SIS_300
@@ -315,8 +301,6 @@ void sisfb_syncaccel(struct sis_video_info *ivideo)
}
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0) /* --------------- 2.5 --------------- */
-
int fbcon_sis_sync(struct fb_info *info)
{
struct sis_video_info *ivideo = (struct sis_video_info *)info->par;
@@ -438,13 +422,3 @@ void fbcon_sis_copyarea(struct fb_info *info, const struct fb_copyarea *area)
sisfb_syncaccel(ivideo);
}
-
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) /* -------------- 2.4 --------------- */
-
-#include "sisfb_accel_2_4.h"
-
-#endif /* KERNEL VERSION */
-
-
diff --git a/drivers/video/sis/sis_accel.h b/drivers/video/sis/sis_accel.h
index 046e2c4a8e0..30e03cdf6b8 100644
--- a/drivers/video/sis/sis_accel.h
+++ b/drivers/video/sis/sis_accel.h
@@ -390,25 +390,11 @@
MMIO_OUT32(ivideo->mmio_vbase, FIRE_TRIGGER, 0); \
CmdQueLen -= 2;
-
int sisfb_initaccel(struct sis_video_info *ivideo);
void sisfb_syncaccel(struct sis_video_info *ivideo);
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,33)
-void fbcon_sis_bmove(struct display *p, int srcy, int srcx, int dsty,
- int dstx, int height, int width);
-void fbcon_sis_revc(struct display *p, int srcy, int srcx);
-void fbcon_sis_clear8(struct vc_data *conp, struct display *p, int srcy,
- int srcx, int height, int width);
-void fbcon_sis_clear16(struct vc_data *conp, struct display *p, int srcy,
- int srcx, int height, int width);
-void fbcon_sis_clear32(struct vc_data *conp, struct display *p, int srcy,
- int srcx, int height, int width);
-#endif
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,34)
int fbcon_sis_sync(struct fb_info *info);
void fbcon_sis_fillrect(struct fb_info *info, const struct fb_fillrect *rect);
void fbcon_sis_copyarea(struct fb_info *info, const struct fb_copyarea *area);
-#endif
#endif
diff --git a/drivers/video/sis/sis_main.c b/drivers/video/sis/sis_main.c
index 895ebda7d9e..baaf495a0a6 100644
--- a/drivers/video/sis/sis_main.c
+++ b/drivers/video/sis/sis_main.c
@@ -35,9 +35,7 @@
#include <linux/version.h>
#include <linux/module.h>
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
#include <linux/moduleparam.h>
-#endif
#include <linux/kernel.h>
#include <linux/smp_lock.h>
#include <linux/spinlock.h>
@@ -58,9 +56,6 @@
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/vmalloc.h>
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <linux/vt_kern.h>
-#endif
#include <linux/capability.h>
#include <linux/fs.h>
#include <linux/types.h>
@@ -70,35 +65,9 @@
#include <asm/mtrr.h>
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#include <video/fbcon.h>
-#include <video/fbcon-cfb8.h>
-#include <video/fbcon-cfb16.h>
-#include <video/fbcon-cfb24.h>
-#include <video/fbcon-cfb32.h>
-#endif
-
#include "sis.h"
#include "sis_main.h"
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,3)
-#error "This version of sisfb requires at least 2.6.3"
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#ifdef FBCON_HAS_CFB8
-extern struct display_switch fbcon_sis8;
-#endif
-#ifdef FBCON_HAS_CFB16
-extern struct display_switch fbcon_sis16;
-#endif
-#ifdef FBCON_HAS_CFB32
-extern struct display_switch fbcon_sis32;
-#endif
-#endif
-
static void sisfb_handle_command(struct sis_video_info *ivideo,
struct sisfb_cmd *sisfb_command);
@@ -114,17 +83,7 @@ sisfb_setdefaultparms(void)
sisfb_max = -1;
sisfb_userom = -1;
sisfb_useoem = -1;
-#ifdef MODULE
- /* Module: "None" for 2.4, default mode for 2.5+ */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
- sisfb_mode_idx = -1;
-#else
- sisfb_mode_idx = MODE_INDEX_NONE;
-#endif
-#else
- /* Static: Default mode */
sisfb_mode_idx = -1;
-#endif
sisfb_parm_rate = -1;
sisfb_crt1off = 0;
sisfb_forcecrt1 = -1;
@@ -142,10 +101,6 @@ sisfb_setdefaultparms(void)
sisfb_tvxposoffset = 0;
sisfb_tvyposoffset = 0;
sisfb_nocrt2rate = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- sisfb_inverse = 0;
- sisfb_fontname[0] = 0;
-#endif
#if !defined(__i386__) && !defined(__x86_64__)
sisfb_resetcard = 0;
sisfb_videoram = 0;
@@ -162,14 +117,11 @@ sisfb_search_vesamode(unsigned int vesamode, BOOLEAN quiet)
/* We don't know the hardware specs yet and there is no ivideo */
if(vesamode == 0) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- sisfb_mode_idx = MODE_INDEX_NONE;
-#else
if(!quiet)
printk(KERN_ERR "sisfb: Invalid mode. Using default.\n");
sisfb_mode_idx = DEFAULT_MODE;
-#endif
+
return;
}
@@ -215,7 +167,6 @@ sisfb_search_mode(char *name, BOOLEAN quiet)
return;
}
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
if(!strnicmp(name, sisbios_mode[MODE_INDEX_NONE].name, strlen(name))) {
if(!quiet)
printk(KERN_ERR "sisfb: Mode 'none' not supported anymore. Using default.\n");
@@ -223,7 +174,7 @@ sisfb_search_mode(char *name, BOOLEAN quiet)
sisfb_mode_idx = DEFAULT_MODE;
return;
}
-#endif
+
if(strlen(name) <= 19) {
strcpy(strbuf1, name);
for(i = 0; i < strlen(strbuf1); i++) {
@@ -1315,20 +1266,7 @@ sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *in
ivideo->refresh_rate = 60;
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- if(ivideo->sisfb_thismonitor.datavalid) {
- if(!sisfb_verify_rate(ivideo, &ivideo->sisfb_thismonitor, ivideo->sisfb_mode_idx,
- ivideo->rate_idx, ivideo->refresh_rate)) {
- printk(KERN_INFO "sisfb: WARNING: Refresh rate exceeds monitor specs!\n");
- }
- }
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- if(((var->activate & FB_ACTIVATE_MASK) == FB_ACTIVATE_NOW) && isactive) {
-#else
if(isactive) {
-#endif
/* If acceleration to be used? Need to know
* before pre/post_set_mode()
*/
@@ -1367,9 +1305,7 @@ sisfb_do_set_var(struct fb_var_screeninfo *var, int isactive, struct fb_info *in
ivideo->current_linelength = ivideo->video_linelength;
ivideo->current_pixclock = var->pixclock;
ivideo->current_refresh_rate = ivideo->refresh_rate;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
ivideo->sisfb_lastrates[ivideo->mode_no] = ivideo->refresh_rate;
-#endif
}
return 0;
@@ -1435,18 +1371,6 @@ sisfb_pan_var(struct sis_video_info *ivideo, struct fb_var_screeninfo *var)
return 0;
}
-/* ------------ FBDev related routines for 2.4 series ----------- */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-
-#include "sisfb_fbdev_2_4.h"
-
-#endif
-
-/* ------------ FBDev related routines for 2.6 series ----------- */
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-
static int
sisfb_open(struct fb_info *info, int user)
{
@@ -1744,8 +1668,6 @@ sisfb_blank(int blank, struct fb_info *info)
return sisfb_myblank(ivideo, blank);
}
-#endif
-
/* ----------- FBDev related routines for all series ---------- */
#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
@@ -1969,20 +1891,6 @@ sisfb_get_fix(struct fb_fix_screeninfo *fix, int con, struct fb_info *info)
/* ---------------- fb_ops structures ----------------- */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static struct fb_ops sisfb_ops = {
- .owner = THIS_MODULE,
- .fb_get_fix = sisfb_get_fix,
- .fb_get_var = sisfb_get_var,
- .fb_set_var = sisfb_set_var,
- .fb_get_cmap = sisfb_get_cmap,
- .fb_set_cmap = sisfb_set_cmap,
- .fb_pan_display = sisfb_pan_display,
- .fb_ioctl = sisfb_ioctl
-};
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
static struct fb_ops sisfb_ops = {
.owner = THIS_MODULE,
.fb_open = sisfb_open,
@@ -2004,7 +1912,6 @@ static struct fb_ops sisfb_ops = {
#endif
.fb_ioctl = sisfb_ioctl
};
-#endif
/* ---------------- Chip generation dependent routines ---------------- */
@@ -4100,16 +4007,6 @@ sisfb_setup(char *options)
sisfb_search_mode(this_opt + 5, FALSE);
} else if(!strnicmp(this_opt, "vesa:", 5)) {
sisfb_search_vesamode(simple_strtoul(this_opt + 5, NULL, 0), FALSE);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- } else if(!strnicmp(this_opt, "inverse", 7)) {
- sisfb_inverse = 1;
- /* fb_invert_cmaps(); */
- } else if(!strnicmp(this_opt, "font:", 5)) {
- if(strlen(this_opt + 5) < 40) {
- strncpy(sisfb_fontname, this_opt + 5, sizeof(sisfb_fontname) - 1);
- sisfb_fontname[sizeof(sisfb_fontname) - 1] = '\0';
- }
-#endif
} else if(!strnicmp(this_opt, "rate:", 5)) {
sisfb_parm_rate = simple_strtoul(this_opt + 5, NULL, 0);
} else if(!strnicmp(this_opt, "forcecrt1:", 10)) {
@@ -5870,17 +5767,9 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
if(sisfb_off)
return -ENXIO;
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,3))
sis_fb_info = framebuffer_alloc(sizeof(*ivideo), &pdev->dev);
if(!sis_fb_info)
return -ENOMEM;
-#else
- sis_fb_info = kmalloc(sizeof(*sis_fb_info) + sizeof(*ivideo), GFP_KERNEL);
- if(!sis_fb_info)
- return -ENOMEM;
- memset(sis_fb_info, 0, sizeof(*sis_fb_info) + sizeof(*ivideo));
- sis_fb_info->par = ((char *)sis_fb_info + sizeof(*sis_fb_info));
-#endif
ivideo = (struct sis_video_info *)sis_fb_info->par;
ivideo->memyselfandi = sis_fb_info;
@@ -5970,10 +5859,6 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
ivideo->tvxpos = sisfb_tvxposoffset;
ivideo->tvypos = sisfb_tvyposoffset;
ivideo->sisfb_nocrt2rate = sisfb_nocrt2rate;
-#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,5,0)
- ivideo->sisfb_inverse = sisfb_inverse;
-#endif
-
ivideo->refresh_rate = 0;
if(ivideo->sisfb_parm_rate != -1) {
ivideo->refresh_rate = ivideo->sisfb_parm_rate;
@@ -6049,10 +5934,6 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
}
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- strcpy(sis_fb_info->modename, ivideo->myid);
-#endif
-
ivideo->SiS_Pr.ChipType = ivideo->chip;
ivideo->SiS_Pr.ivideo = (void *)ivideo;
@@ -6134,20 +6015,6 @@ sisfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
#endif
}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-#ifdef MODULE
- if((reg & 0x80) && (reg != 0xff)) {
- if((sisbios_mode[ivideo->sisfb_mode_idx].mode_no[ivideo->mni])
- != 0xFF) {
- printk(KERN_INFO "sisfb: Cannot initialize display mode, "
- "X server is active\n");
- ret = -EBUSY;
- goto error_4;
- }
- }
-#endif
-#endif
-
/* Search and copy ROM image */
ivideo->bios_abase = NULL;
ivideo->SiS_Pr.VirtualRomBase = NULL;
@@ -6281,9 +6148,6 @@ error_0: iounmap(ivideo->video_vbase);
error_1: release_mem_region(ivideo->video_base, ivideo->video_size);
error_2: release_mem_region(ivideo->mmio_base, ivideo->mmio_size);
error_3: vfree(ivideo->bios_abase);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-error_4:
-#endif
if(ivideo->lpcdev)
SIS_PCI_PUT_DEVICE(ivideo->lpcdev);
if(ivideo->nbridge)
@@ -6586,7 +6450,6 @@ error_4:
sis_fb_info->fix = ivideo->sisfb_fix;
sis_fb_info->screen_base = ivideo->video_vbase + ivideo->video_offset;
sis_fb_info->fbops = &sisfb_ops;
-
sisfb_get_fix(&sis_fb_info->fix, -1, sis_fb_info);
sis_fb_info->pseudo_palette = ivideo->pseudo_palette;
@@ -6603,10 +6466,6 @@ error_4:
}
#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- vc_resize_con(1, 1, 0);
-#endif
-
if(register_framebuffer(sis_fb_info) < 0) {
printk(KERN_ERR "sisfb: Fatal error: Failed to register framebuffer\n");
ret = -EINVAL;
@@ -6653,12 +6512,7 @@ error_4:
printk(KERN_INFO "fb%d: %s frame buffer device version %d.%d.%d\n",
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- GET_FB_IDX(sis_fb_info->node),
-#else
- sis_fb_info->node,
-#endif
- ivideo->myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
+ sis_fb_info->node, ivideo->myid, VER_MAJOR, VER_MINOR, VER_LEVEL);
printk(KERN_INFO "sisfb: Copyright (C) 2001-2005 Thomas Winischhofer\n");
@@ -6732,11 +6586,7 @@ static void __devexit sisfb_remove(struct pci_dev *pdev)
/* Unregister the framebuffer */
if(ivideo->registered) {
unregister_framebuffer(sis_fb_info);
-#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,3))
framebuffer_release(sis_fb_info);
-#else
- kfree(sis_fb_info);
-#endif
}
/* OK, our ivideo is gone for good from here. */
@@ -6762,7 +6612,6 @@ static struct pci_driver sisfb_driver = {
SISINITSTATIC int __init sisfb_init(void)
{
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
#ifndef MODULE
char *options = NULL;
@@ -6771,15 +6620,12 @@ SISINITSTATIC int __init sisfb_init(void)
sisfb_setup(options);
#endif
-#endif
return pci_register_driver(&sisfb_driver);
}
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
#ifndef MODULE
module_init(sisfb_init);
#endif
-#endif
/*****************************************************/
/* MODULE */
@@ -6799,9 +6645,6 @@ static int pdc1 = -1;
static int noaccel = -1;
static int noypan = -1;
static int nomax = -1;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static int inverse = 0;
-#endif
static int userom = -1;
static int useoem = -1;
static char *tvstandard = NULL;
@@ -6861,10 +6704,6 @@ static int __init sisfb_init_module(void)
else if(nomax == 0)
sisfb_max = 1;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
- if(inverse) sisfb_inverse = 1;
-#endif
-
if(mem)
sisfb_parm_mem = mem;
@@ -6913,35 +6752,6 @@ MODULE_DESCRIPTION("SiS 300/540/630/730/315/55x/65x/661/74x/330/76x/34x, XGI V3X
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thomas Winischhofer <thomas@winischhofer.net>, Others");
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM(mem, "i");
-MODULE_PARM(noaccel, "i");
-MODULE_PARM(noypan, "i");
-MODULE_PARM(nomax, "i");
-MODULE_PARM(userom, "i");
-MODULE_PARM(useoem, "i");
-MODULE_PARM(mode, "s");
-MODULE_PARM(vesa, "i");
-MODULE_PARM(rate, "i");
-MODULE_PARM(forcecrt1, "i");
-MODULE_PARM(forcecrt2type, "s");
-MODULE_PARM(scalelcd, "i");
-MODULE_PARM(pdc, "i");
-MODULE_PARM(pdc1, "i");
-MODULE_PARM(specialtiming, "s");
-MODULE_PARM(lvdshl, "i");
-MODULE_PARM(tvstandard, "s");
-MODULE_PARM(tvxposoffset, "i");
-MODULE_PARM(tvyposoffset, "i");
-MODULE_PARM(nocrt2rate, "i");
-MODULE_PARM(inverse, "i");
-#if !defined(__i386__) && !defined(__x86_64__)
-MODULE_PARM(resetcard, "i");
-MODULE_PARM(videoram, "i");
-#endif
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
module_param(mem, int, 0);
module_param(noaccel, int, 0);
module_param(noypan, int, 0);
@@ -6966,18 +6776,7 @@ module_param(nocrt2rate, int, 0);
module_param(resetcard, int, 0);
module_param(videoram, int, 0);
#endif
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM_DESC(mem,
- "\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
- "for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
- "on the amount of video RAM available. If 8MB of video RAM or less is available,\n"
- "the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
- "otherwise at 12288KB. On 315/330/340 series, the heap size is 32KB by default.\n"
- "The value is to be specified without 'KB' and must match the MaxXFBMem setting\n"
- "for XFree86 4.x/X.org 6.7 and later.\n");
-#else
MODULE_PARM_DESC(mem,
"\nDetermines the beginning of the video memory heap in KB. This heap is used\n"
"for video RAM management for eg. DRM/DRI. On 300 series, the default depends\n"
@@ -6985,7 +6784,6 @@ MODULE_PARM_DESC(mem,
"the heap starts at 4096KB, if between 8 and 16MB are available at 8192KB,\n"
"otherwise at 12288KB. On 315/330/340 series, the heap size is 32KB by default.\n"
"The value is to be specified without 'KB'.\n");
-#endif
MODULE_PARM_DESC(noaccel,
"\nIf set to anything other than 0, 2D acceleration will be disabled.\n"
@@ -7002,23 +6800,6 @@ MODULE_PARM_DESC(nomax,
"enable the user to positively specify a virtual Y size of the screen using\n"
"fbset. (default: 0)\n");
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM_DESC(mode,
- "\nSelects the desired display mode in the format [X]x[Y]x[Depth], eg.\n"
- "1024x768x16. Other formats supported include XxY-Depth and\n"
- "XxY-Depth@Rate. If the parameter is only one (decimal or hexadecimal)\n"
- "number, it will be interpreted as a VESA mode number. (default: none if\n"
- "sisfb is a module; this leaves the console untouched and the driver will\n"
- "only do the video memory management for eg. DRM/DRI; 800x600x8 if sisfb\n"
- "is in the kernel)\n");
-MODULE_PARM_DESC(vesa,
- "\nSelects the desired display mode by VESA defined mode number, eg. 0x117\n"
- "(default: 0x0000 if sisfb is a module; this leaves the console untouched\n"
- "and the driver will only do the video memory management for eg. DRM/DRI;\n"
- "0x0103 if sisfb is in the kernel)\n");
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
MODULE_PARM_DESC(mode,
"\nSelects the desired default display mode in the format XxYxDepth,\n"
"eg. 1024x768x16. Other formats supported include XxY-Depth and\n"
@@ -7028,7 +6809,6 @@ MODULE_PARM_DESC(mode,
MODULE_PARM_DESC(vesa,
"\nSelects the desired default display mode by VESA defined mode number, eg.\n"
"0x117 (default: 0x0103)\n");
-#endif
MODULE_PARM_DESC(rate,
"\nSelects the desired vertical refresh rate for CRT1 (external VGA) in Hz.\n"
@@ -7094,12 +6874,6 @@ MODULE_PARM_DESC(nocrt2rate,
"\nSetting this to 1 will force the driver to use the default refresh rate for\n"
"CRT2 if CRT2 type is VGA. (default: 0, use same rate as CRT1)\n");
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-MODULE_PARM_DESC(inverse,
- "\nSetting this to anything but 0 should invert the display colors, but this\n"
- "does not seem to work. (default: 0)\n");
-#endif
-
#if !defined(__i386__) && !defined(__x86_64__)
#ifdef CONFIG_FB_SIS_300
MODULE_PARM_DESC(resetcard,
diff --git a/drivers/video/sis/sis_main.h b/drivers/video/sis/sis_main.h
index 70b6df371b8..88e4f1e4147 100644
--- a/drivers/video/sis/sis_main.h
+++ b/drivers/video/sis/sis_main.h
@@ -67,15 +67,7 @@ static int sisfb_ypan = -1;
static int sisfb_max = -1;
static int sisfb_userom = 1;
static int sisfb_useoem = -1;
-#ifdef MODULE
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-static int sisfb_mode_idx = -1;
-#else
-static int sisfb_mode_idx = MODE_INDEX_NONE; /* Don't use a mode by default if we are a module */
-#endif
-#else
static int sisfb_mode_idx = -1; /* Use a default mode if we are inside the kernel */
-#endif
static int sisfb_parm_rate = -1;
static int sisfb_crt1off = 0;
static int sisfb_forcecrt1 = -1;
@@ -93,10 +85,6 @@ static int sisfb_tvstd = -1;
static int sisfb_tvxposoffset = 0;
static int sisfb_tvyposoffset = 0;
static int sisfb_nocrt2rate = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static int sisfb_inverse = 0;
-static char sisfb_fontname[40];
-#endif
#if !defined(__i386__) && !defined(__x86_64__)
static int sisfb_resetcard = 0;
static int sisfb_videoram = 0;
@@ -687,54 +675,8 @@ SISINITSTATIC int sisfb_init(void);
static int sisfb_get_fix(struct fb_fix_screeninfo *fix, int con,
struct fb_info *info);
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-static int sisfb_get_fix(struct fb_fix_screeninfo *fix,
- int con,
- struct fb_info *info);
-static int sisfb_get_var(struct fb_var_screeninfo *var,
- int con,
- struct fb_info *info);
-static int sisfb_set_var(struct fb_var_screeninfo *var,
- int con,
- struct fb_info *info);
-static void sisfb_crtc_to_var(struct sis_video_info *ivideo,
- struct fb_var_screeninfo *var);
-static int sisfb_get_cmap(struct fb_cmap *cmap,
- int kspc,
- int con,
- struct fb_info *info);
-static int sisfb_set_cmap(struct fb_cmap *cmap,
- int kspc,
- int con,
- struct fb_info *info);
-static int sisfb_update_var(int con,
- struct fb_info *info);
-static int sisfb_switch(int con,
- struct fb_info *info);
-static void sisfb_blank(int blank,
- struct fb_info *info);
-static void sisfb_set_disp(int con,
- struct fb_var_screeninfo *var,
- struct fb_info *info);
-static int sis_getcolreg(unsigned regno, unsigned *red, unsigned *green,
- unsigned *blue, unsigned *transp,
- struct fb_info *fb_info);
-static void sisfb_do_install_cmap(int con,
- struct fb_info *info);
-static int sisfb_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg, int con,
- struct fb_info *info);
-#endif
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,15)
static int sisfb_ioctl(struct fb_info *info, unsigned int cmd,
unsigned long arg);
-#else
-static int sisfb_ioctl(struct inode *inode, struct file *file,
- unsigned int cmd, unsigned long arg,
- struct fb_info *info);
-#endif
static int sisfb_set_par(struct fb_info *info);
static int sisfb_blank(int blank,
struct fb_info *info);
@@ -743,7 +685,6 @@ extern void fbcon_sis_fillrect(struct fb_info *info,
extern void fbcon_sis_copyarea(struct fb_info *info,
const struct fb_copyarea *area);
extern int fbcon_sis_sync(struct fb_info *info);
-#endif
/* Internal 2D accelerator functions */
extern int sisfb_initaccel(struct sis_video_info *ivideo);
@@ -811,16 +752,10 @@ extern BOOLEAN SiSDetermineROMLayout661(struct SiS_Private *SiS_Pr);
extern BOOLEAN sisfb_gettotalfrommode(struct SiS_Private *SiS_Pr, unsigned char modeno,
int *htotal, int *vtotal, unsigned char rateindex);
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,0)
extern int sisfb_mode_rate_to_dclock(struct SiS_Private *SiS_Pr,
unsigned char modeno, unsigned char rateindex);
extern int sisfb_mode_rate_to_ddata(struct SiS_Private *SiS_Pr, unsigned char modeno,
unsigned char rateindex, struct fb_var_screeninfo *var);
-#endif
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-extern void SiS_Generic_ConvertCRData(struct SiS_Private *SiS_Pr, unsigned char *crdata, int xres,
- int yres, struct fb_var_screeninfo *var, BOOLEAN writeres);
-#endif
/* Chrontel TV, DDC and DPMS functions */
extern unsigned short SiS_GetCH700x(struct SiS_Private *SiS_Pr, unsigned short reg);
diff --git a/drivers/video/sis/vgatypes.h b/drivers/video/sis/vgatypes.h
index 831b9f42264..05d08b7889a 100644
--- a/drivers/video/sis/vgatypes.h
+++ b/drivers/video/sis/vgatypes.h
@@ -73,12 +73,10 @@ typedef unsigned int BOOLEAN;
#ifdef SIS_LINUX_KERNEL
typedef unsigned long SISIOADDRESS;
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,8)
#include <linux/types.h> /* Need __iomem */
#undef SISIOMEMTYPE
#define SISIOMEMTYPE __iomem
#endif
-#endif
#ifdef SIS_XORG_XF86
#if XF86_VERSION_CURRENT < XF86_VERSION_NUMERIC(4,2,0,0,0)
diff --git a/drivers/video/sstfb.c b/drivers/video/sstfb.c
index dad54e73147..711cb11d6eb 100644
--- a/drivers/video/sstfb.c
+++ b/drivers/video/sstfb.c
@@ -17,7 +17,10 @@
* (port driver to new frambuffer infrastructure)
* 01/2003 Helge Deller <deller@gmx.de>
* (initial work on fb hardware acceleration for voodoo2)
- *
+ * 08/2006 Alan Cox <alan@redhat.com>
+ * Remove never finished and bogus 24/32bit support
+ * Clean up macro abuse
+ * Minor tidying for format.
*/
/*
@@ -40,6 +43,7 @@
through the fifo. warning: issuing a nop command seems to need pci_fifo
-FIXME: in case of failure in the init sequence, be sure we return to a safe
state.
+- FIXME: Use accelerator for 2D scroll
-FIXME: 4MB boards have banked memory (FbiInit2 bits 1 & 20)
*/
@@ -67,9 +71,6 @@
#undef SST_DEBUG
-/* enable 24/32 bpp functions ? (completely untested!) */
-#undef EN_24_32_BPP
-
/*
Default video mode .
0 800x600@60 took from glide
@@ -377,7 +378,11 @@ static void sstfb_clear_screen(struct fb_info *info)
* sstfb_check_var - Optional function. Validates a var passed in.
* @var: frame buffer variable screen structure
* @info: frame buffer structure that represents a single frame buffer
+ *
+ * Limit to the abilities of a single chip as SLI is not supported
+ * by this driver.
*/
+
static int sstfb_check_var(struct fb_var_screeninfo *var,
struct fb_info *info)
{
@@ -390,7 +395,7 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
unsigned int freq;
if (sst_calc_pll(PICOS2KHZ(var->pixclock), &freq, &par->pll)) {
- eprintk("Pixclock at %ld KHZ out of range\n",
+ printk(KERN_ERR "sstfb: Pixclock at %ld KHZ out of range\n",
PICOS2KHZ(var->pixclock));
return -EINVAL;
}
@@ -409,27 +414,15 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
case 0 ... 16 :
var->bits_per_pixel = 16;
break;
-#ifdef EN_24_32_BPP
- case 17 ... 24 :
- var->bits_per_pixel = 24;
- break;
- case 25 ... 32 :
- var->bits_per_pixel = 32;
- break;
-#endif
default :
- eprintk("Unsupported bpp %d\n", var->bits_per_pixel);
+ printk(KERN_ERR "sstfb: Unsupported bpp %d\n", var->bits_per_pixel);
return -EINVAL;
}
/* validity tests */
- if ((var->xres <= 1) || (yDim <= 0 )
- || (var->hsync_len <= 1)
- || (hSyncOff <= 1)
- || (var->left_margin <= 2)
- || (vSyncOn <= 0)
- || (vSyncOff <= 0)
- || (vBackPorch <= 0)) {
+ if (var->xres <= 1 || yDim <= 0 || var->hsync_len <= 1 ||
+ hSyncOff <= 1 || var->left_margin <= 2 || vSyncOn <= 0 ||
+ vSyncOff <= 0 || vBackPorch <= 0) {
return -EINVAL;
}
@@ -437,21 +430,17 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
/* Voodoo 2 limits */
tiles_in_X = (var->xres + 63 ) / 64 * 2;
- if (((var->xres - 1) >= POW2(11)) || (yDim >= POW2(11))) {
- eprintk("Unsupported resolution %dx%d\n",
+ if (var->xres > POW2(11) || yDim >= POW2(11)) {
+ printk(KERN_ERR "sstfb: Unsupported resolution %dx%d\n",
var->xres, var->yres);
return -EINVAL;
}
- if (((var->hsync_len-1) >= POW2(9))
- || ((hSyncOff-1) >= POW2(11))
- || ((var->left_margin - 2) >= POW2(9))
- || (vSyncOn >= POW2(13))
- || (vSyncOff >= POW2(13))
- || (vBackPorch >= POW2(9))
- || (tiles_in_X >= POW2(6))
- || (tiles_in_X <= 0)) {
- eprintk("Unsupported Timings\n");
+ if (var->hsync_len > POW2(9) || hSyncOff > POW2(11) ||
+ var->left_margin - 2 >= POW2(9) || vSyncOn >= POW2(13) ||
+ vSyncOff >= POW2(13) || vBackPorch >= POW2(9) ||
+ tiles_in_X >= POW2(6) || tiles_in_X <= 0) {
+ printk(KERN_ERR "sstfb: Unsupported timings\n");
return -EINVAL;
}
} else {
@@ -459,24 +448,20 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
tiles_in_X = (var->xres + 63 ) / 64;
if (var->vmode) {
- eprintk("Interlace/Doublescan not supported %#x\n",
+ printk(KERN_ERR "sstfb: Interlace/doublescan not supported %#x\n",
var->vmode);
return -EINVAL;
}
- if (((var->xres - 1) >= POW2(10)) || (var->yres >= POW2(10))) {
- eprintk("Unsupported resolution %dx%d\n",
+ if (var->xres > POW2(10) || var->yres >= POW2(10)) {
+ printk(KERN_ERR "sstfb: Unsupported resolution %dx%d\n",
var->xres, var->yres);
return -EINVAL;
}
- if (((var->hsync_len - 1) >= POW2(8))
- || ((hSyncOff-1) >= POW2(10))
- || ((var->left_margin - 2) >= POW2(8))
- || (vSyncOn >= POW2(12))
- || (vSyncOff >= POW2(12))
- || (vBackPorch >= POW2(8))
- || (tiles_in_X >= POW2(4))
- || (tiles_in_X <= 0)) {
- eprintk("Unsupported Timings\n");
+ if (var->hsync_len > POW2(8) || hSyncOff - 1 > POW2(10) ||
+ var->left_margin - 2 >= POW2(8) || vSyncOn >= POW2(12) ||
+ vSyncOff >= POW2(12) || vBackPorch >= POW2(8) ||
+ tiles_in_X >= POW2(4) || tiles_in_X <= 0) {
+ printk(KERN_ERR "sstfb: Unsupported timings\n");
return -EINVAL;
}
}
@@ -486,8 +471,8 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
real_length = tiles_in_X * (IS_VOODOO2(par) ? 32 : 64 )
* ((var->bits_per_pixel == 16) ? 2 : 4);
- if ((real_length * yDim) > info->fix.smem_len) {
- eprintk("Not enough video memory\n");
+ if (real_length * yDim > info->fix.smem_len) {
+ printk(KERN_ERR "sstfb: Not enough video memory\n");
return -ENOMEM;
}
@@ -515,20 +500,6 @@ static int sstfb_check_var(struct fb_var_screeninfo *var,
var->blue.offset = 0;
var->transp.offset = 0;
break;
-#ifdef EN_24_32_BPP
- case 24: /* RGB 888 LfbMode 4 */
- case 32: /* ARGB 8888 LfbMode 5 */
- var->red.length = 8;
- var->green.length = 8;
- var->blue.length = 8;
- var->transp.length = 0;
-
- var->red.offset = 16;
- var->green.offset = 8;
- var->blue.offset = 0;
- var->transp.offset = 0; /* in 24bpp we fake a 32 bpp mode */
- break;
-#endif
default:
return -EINVAL;
}
@@ -653,13 +624,6 @@ static int sstfb_set_par(struct fb_info *info)
case 16:
fbiinit1 |= SEL_SOURCE_VCLK_2X_SEL;
break;
-#ifdef EN_24_32_BPP
- case 24:
- case 32:
- /* sst_set_bits(FBIINIT1, SEL_SOURCE_VCLK_2X_DIV2 | EN_24BPP);*/
- fbiinit1 |= SEL_SOURCE_VCLK_2X_SEL | EN_24BPP;
- break;
-#endif
default:
return -EINVAL;
}
@@ -690,14 +654,6 @@ static int sstfb_set_par(struct fb_info *info)
case 16:
lfbmode = LFB_565;
break;
-#ifdef EN_24_32_BPP
- case 24:
- lfbmode = LFB_888;
- break;
- case 32:
- lfbmode = LFB_8888;
- break;
-#endif
default:
return -EINVAL;
}
@@ -789,8 +745,7 @@ static int sstfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
return -EFAULT;
if (val > info->fix.smem_len)
val = info->fix.smem_len;
- printk("filling %#x \n", val);
- for (p=0 ; p<val; p+=2)
+ for (p = 0 ; p < val; p += 2)
writew(p >> 6, info->screen_base + p);
return 0;
@@ -802,13 +757,10 @@ static int sstfb_ioctl(struct fb_info *info, u_int cmd, u_long arg)
pci_write_config_dword(sst_dev, PCI_INIT_ENABLE,
tmp | PCI_EN_INIT_WR );
fbiinit0 = sst_read (FBIINIT0);
- if (val) {
+ if (val)
sst_write(FBIINIT0, fbiinit0 & ~EN_VGA_PASSTHROUGH);
- iprintk("Disabling VGA pass-through\n");
- } else {
+ else
sst_write(FBIINIT0, fbiinit0 | EN_VGA_PASSTHROUGH);
- iprintk("Enabling VGA pass-through\n");
- }
pci_write_config_dword(sst_dev, PCI_INIT_ENABLE, tmp);
return 0;
@@ -884,9 +836,9 @@ static int __devinit sst_get_memsize(struct fb_info *info, __u32 *memsize)
u8 __iomem *fbbase_virt = info->screen_base;
/* force memsize */
- if ((mem >= 1 ) && (mem <= 4)) {
+ if (mem >= 1 && mem <= 4) {
*memsize = (mem * 0x100000);
- iprintk("supplied memsize: %#x\n", *memsize);
+ printk(KERN_INFO "supplied memsize: %#x\n", *memsize);
return 1;
}
@@ -927,7 +879,7 @@ static int __devinit sst_detect_att(struct fb_info *info)
struct sstfb_par *par = info->par;
int i, mir, dir;
- for (i=0; i<3; i++) {
+ for (i = 0; i < 3; i++) {
sst_dac_write(DACREG_WMA, 0); /* backdoor */
sst_dac_read(DACREG_RMR); /* read 4 times RMR */
sst_dac_read(DACREG_RMR);
@@ -940,7 +892,7 @@ static int __devinit sst_detect_att(struct fb_info *info)
/*the 7th, device ID register */
dir = sst_dac_read(DACREG_RMR);
f_ddprintk("mir: %#x, dir: %#x\n", mir, dir);
- if ((mir == DACREG_MIR_ATT ) && (dir == DACREG_DIR_ATT)) {
+ if (mir == DACREG_MIR_ATT && dir == DACREG_DIR_ATT) {
return 1;
}
}
@@ -1134,12 +1086,6 @@ static void sst_set_vidmod_att_ti(struct fb_info *info, const int bpp)
case 16:
sst_dac_write(DACREG_RMR, (cr0 & 0x0f) | DACREG_CR0_16BPP);
break;
-#ifdef EN_24_32_BPP
- case 24:
- case 32:
- sst_dac_write(DACREG_RMR, (cr0 & 0x0f) | DACREG_CR0_24BPP);
- break;
-#endif
default:
dprintk("%s: bad depth '%u'\n", __FUNCTION__, bpp);
break;
@@ -1154,12 +1100,6 @@ static void sst_set_vidmod_ics(struct fb_info *info, const int bpp)
case 16:
sst_dac_write(DACREG_ICS_CMD, DACREG_ICS_CMD_16BPP);
break;
-#ifdef EN_24_32_BPP
- case 24:
- case 32:
- sst_dac_write(DACREG_ICS_CMD, DACREG_ICS_CMD_24BPP);
- break;
-#endif
default:
dprintk("%s: bad depth '%u'\n", __FUNCTION__, bpp);
break;
@@ -1250,7 +1190,7 @@ static int __devinit sst_init(struct fb_info *info, struct sstfb_par *par)
PCI_EN_INIT_WR | PCI_REMAP_DAC );
/* detect dac type */
if (!sst_detect_dactype(info, par)) {
- eprintk("Unknown dac type\n");
+ printk(KERN_ERR "sstfb: unknown dac type.\n");
//FIXME watch it: we are not in a safe state, bad bad bad.
return 0;
}
@@ -1258,10 +1198,10 @@ static int __devinit sst_init(struct fb_info *info, struct sstfb_par *par)
/* set graphic clock */
par->gfx_clock = spec->default_gfx_clock;
if ((gfxclk >10 ) && (gfxclk < spec->max_gfxclk)) {
- iprintk("Using supplied graphic freq : %dMHz\n", gfxclk);
+ printk(KERN_INFO "sstfb: Using supplied graphic freq : %dMHz\n", gfxclk);
par->gfx_clock = gfxclk *1000;
} else if (gfxclk) {
- wprintk ("%dMhz is way out of spec! Using default\n", gfxclk);
+ printk(KERN_WARNING "sstfb: %dMhz is way out of spec! Using default\n", gfxclk);
}
sst_calc_pll(par->gfx_clock, &Fout, &gfx_timings);
@@ -1396,7 +1336,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
/* Enable device in PCI config. */
if ((err=pci_enable_device(pdev))) {
- eprintk("cannot enable device\n");
+ printk(KERN_ERR "cannot enable device\n");
return err;
}
@@ -1422,39 +1362,39 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
fix->smem_start = fix->mmio_start + 0x400000;
if (!request_mem_region(fix->mmio_start, fix->mmio_len, "sstfb MMIO")) {
- eprintk("cannot reserve mmio memory\n");
+ printk(KERN_ERR "sstfb: cannot reserve mmio memory\n");
goto fail_mmio_mem;
}
if (!request_mem_region(fix->smem_start, 0x400000,"sstfb FB")) {
- eprintk("cannot reserve fb memory\n");
+ printk(KERN_ERR "sstfb: cannot reserve fb memory\n");
goto fail_fb_mem;
}
par->mmio_vbase = ioremap_nocache(fix->mmio_start,
fix->mmio_len);
if (!par->mmio_vbase) {
- eprintk("cannot remap register area %#lx\n",
+ printk(KERN_ERR "sstfb: cannot remap register area %#lx\n",
fix->mmio_start);
goto fail_mmio_remap;
}
info->screen_base = ioremap_nocache(fix->smem_start, 0x400000);
if (!info->screen_base) {
- eprintk("cannot remap framebuffer %#lx\n",
+ printk(KERN_ERR "sstfb: cannot remap framebuffer %#lx\n",
fix->smem_start);
goto fail_fb_remap;
}
if (!sst_init(info, par)) {
- eprintk("Init failed\n");
+ printk(KERN_ERR "sstfb: Init failed\n");
goto fail;
}
sst_get_memsize(info, &fix->smem_len);
strlcpy(fix->id, spec->name, sizeof(fix->id));
- iprintk("%s (revision %d) with %s dac\n",
+ printk(KERN_INFO "%s (revision %d) with %s dac\n",
fix->id, par->revision, par->dac_sw.name);
- iprintk("framebuffer at %#lx, mapped to 0x%p, size %dMB\n",
+ printk(KERN_INFO "framebuffer at %#lx, mapped to 0x%p, size %dMB\n",
fix->smem_start, info->screen_base,
fix->smem_len >> 20);
@@ -1471,24 +1411,25 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
fix->accel = FB_ACCEL_NONE; /* FIXME */
/*
* According to the specs, the linelength must be of 1024 *pixels*
- * and the 24bpp mode is in fact a 32 bpp mode.
+ * and the 24bpp mode is in fact a 32 bpp mode (and both are in
+ * fact dithered to 16bit).
*/
fix->line_length = 2048; /* default value, for 24 or 32bit: 4096 */
if ( mode_option &&
fb_find_mode(&info->var, info, mode_option, NULL, 0, NULL, 16)) {
- eprintk("can't set supplied video mode. Using default\n");
+ printk(KERN_ERR "sstfb: can't set supplied video mode. Using default\n");
info->var = sstfb_default;
} else
info->var = sstfb_default;
if (sstfb_check_var(&info->var, info)) {
- eprintk("invalid default video mode.\n");
+ printk(KERN_ERR "sstfb: invalid default video mode.\n");
goto fail;
}
if (sstfb_set_par(info)) {
- eprintk("can't set default video mode.\n");
+ printk(KERN_ERR "sstfb: can't set default video mode.\n");
goto fail;
}
@@ -1497,7 +1438,7 @@ static int __devinit sstfb_probe(struct pci_dev *pdev,
/* register fb */
info->device = &pdev->dev;
if (register_framebuffer(info) < 0) {
- eprintk("can't register framebuffer.\n");
+ printk(KERN_ERR "sstfb: can't register framebuffer.\n");
goto fail;
}
@@ -1711,4 +1652,3 @@ module_param(gfxclk, int, 0);
MODULE_PARM_DESC(gfxclk, "Force graphic chip frequency in MHz. DANGEROUS. (default=auto)");
module_param(slowpci, bool, 0);
MODULE_PARM_DESC(slowpci, "Uses slow PCI settings (0 or 1) (default=0)");
-